/* * ncpmount.c * * Copyright (C) 1995 by Volker Lendecke * */ #include #include #include #include #include #include #include #include #include #include #include /* #include */ /* generates a warning here */ extern pid_t waitpid(pid_t, int *, int); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ipxutil.h" static char *progname; static void str_upper(char *name) { while (*name) { *name = toupper(*name); name = name + 1; } } static char * fullpath(const char *p) { char path[MAXPATHLEN]; if (realpath(p, path) == NULL) return strdup(p); else return strdup(path); } static void usage(void) { printf("usage: %s server mount-point [options]\n", progname); printf("Try `%s -h' for more information\n", progname); } static void help(void) { printf("\n"); printf("usage: %s server mount-point [options]\n", progname); printf("\n" "-U username Username sent to server\n" "-u uid uid the mounted files get\n" "-g gid gid the mounted files get\n" "-f mode permission the files get (octal notation)\n" "-d mode permission the dirs get (octal notation)\n" "-C Don't convert password to uppercase\n" "-P password Use this password\n" "-n Do not use any password\n" " If neither -P nor -n are given, you are\n" " asked for a password.\n" "-h print this help text\n" "\n"); } struct sap_query { unsigned short query_type; /* net order */ unsigned short server_type; /* net order */ }; struct sap_server_ident { unsigned short server_type __attribute__ ((packed)); char server_name[48] __attribute__ ((packed)); IPXNet server_network __attribute__ ((packed)); IPXNode server_node __attribute__ ((packed)); IPXPort server_port __attribute__ ((packed)); unsigned short intermediate_network __attribute__ ((packed)); }; void ipx_fprint_node(FILE* file,IPXNode node) { fprintf(file,"%02X%02X%02X%02X%02X%02X", (unsigned char)node[0], (unsigned char)node[1], (unsigned char)node[2], (unsigned char)node[3], (unsigned char)node[4], (unsigned char)node[5] ); } void ipx_fprint_network(FILE* file,IPXNet net) { fprintf(file,"%08lX",ntohl(net)); } void ipx_fprint_port(FILE* file,IPXPort port) { fprintf(file,"%04X",ntohs(port)); } void ipx_fprint_saddr(FILE* file,struct sockaddr_ipx* sipx) { ipx_fprint_network(file,sipx->sipx_network); fprintf(file,":"); ipx_fprint_node(file,sipx->sipx_node); fprintf(file,":"); ipx_fprint_port(file,sipx->sipx_port); } void ipx_print_node(IPXNode node) { ipx_fprint_node(stdout,node); } void ipx_print_network(IPXNet net) { ipx_fprint_network(stdout,net); } void ipx_print_port(IPXPort port) { ipx_fprint_port(stdout,port); } void ipx_print_saddr(struct sockaddr_ipx* sipx) { ipx_fprint_saddr(stdout,sipx); } int ipx_sap_find_server(char *name, int server_type, int timeout, struct sockaddr_ipx *result) { struct sockaddr_ipx ipxs; char data[1024]; int sock; int opt; int res = -1; int name_len = strlen(name); fd_set rd, wr, ex; struct timeval tv; if (name_len > 48) { return -1; } sock=socket(AF_IPX,SOCK_DGRAM,PF_IPX); if (sock==-1) { return -1; } opt=1; /* Permit broadcast output */ if(setsockopt(sock,SOL_SOCKET,SO_BROADCAST, &opt,sizeof(opt))==-1) { perror("setsockopt"); goto finished; } memset(&ipxs, 0, sizeof(ipxs)); ipxs.sipx_family=AF_IPX; ipxs.sipx_network=htonl(0x0); ipxs.sipx_port=htons(0x0); ipxs.sipx_type=IPX_SAP_PTYPE; if(bind(sock,(struct sockaddr*)&ipxs,sizeof(ipxs))==-1) { perror("bind"); goto finished; } *(unsigned short *)data = htons(0x0001); *(unsigned short *)&(data[2]) = htons(server_type); memset(&ipxs, 0, sizeof(ipxs)); ipxs.sipx_family=AF_IPX; ipxs.sipx_network=htonl(0x0); ipx_assign_node(ipxs.sipx_node, IPX_BROADCAST_NODE); ipxs.sipx_port=htons(IPX_SAP_PORT); ipxs.sipx_type=IPX_SAP_PTYPE; if (sendto(sock, data, 4, 0, (struct sockaddr *)&ipxs, sizeof(ipxs)) < 0) { perror("sendto"); goto finished; } FD_ZERO(&rd); FD_ZERO(&wr); FD_ZERO(&ex); FD_SET(sock, &rd); tv.tv_sec = timeout; tv.tv_usec = 0; if (select(sock+1, &rd, &wr, &ex, &tv) == -1) { perror("select"); goto finished; } if (FD_ISSET(sock, &rd)) { int len = recv(sock, data, 1024, 0); int i; struct sap_server_ident *ident; for (i = 2; i < len; i += 64) { ident = (struct sap_server_ident *)(data+i); if ( (strncmp(name,ident->server_name,name_len)==0) && (name_len < 48) && (ident->server_name[name_len] == '\0')) { result->sipx_family = AF_IPX; result->sipx_network = ident->server_network; result->sipx_port = ident->server_port; ipx_assign_node(result->sipx_node, ident->server_node); res = 0; goto finished; } } } finished: close(sock); return res; } int ipx_sscanf_node(char *buf, unsigned char node[6]) { int i; int n[6]; if ((i = sscanf(buf, "%2x%2x%2x%2x%2x%2x", &(n[0]), &(n[1]), &(n[2]), &(n[3]), &(n[4]), &(n[5]))) != 6) { return i; } for (i=0; i<6; i++) { node[i] = n[i]; } return 6; } static int parse_args(int argc, char *argv[], struct ncp_mount_data *data, int *got_password, int *upcase_password) { int opt; struct passwd *pwd; struct group *grp; *got_password = 0; *upcase_password = 1; while ((opt = getopt (argc, argv, "Cp:s:c:U:u:g:f:d:m:P:n")) != EOF) { switch (opt) { case 'C': *upcase_password = 0; break; case 'U': if (strlen(optarg) > 63) { fprintf(stderr, "Username too long: %s\n", optarg); return 1; } strcpy(data->username, optarg); break; case 'u': if (isdigit(optarg[0])) { data->uid = atoi(optarg); } else { pwd = getpwnam(optarg); if (pwd == NULL) { fprintf(stderr, "Unknown user: %s\n", optarg); return 1; } data->uid = pwd->pw_uid; } break; case 'g': if (isdigit(optarg[0])) { data->gid = atoi(optarg); } else { grp = getgrnam(optarg); if (grp == NULL) { fprintf(stderr, "Unknown group: %s\n", optarg); return 1; } data->gid = grp->gr_gid; } break; case 'f': data->file_mode = strtol(optarg, NULL, 8); break; case 'd': data->dir_mode = strtol(optarg, NULL, 8); break; case 'P': strcpy(data->password, optarg); *got_password = 1; break; case 'n': data->password[0] = '\0'; *got_password = 1; break; default: return -1; } } return 0; } /* Returns 0 if the filesystem is in the kernel after this routine completes */ static int load_ncpfs() { FILE *fver, *ffs; char s[1024]; char modname[1024]; char *p, *p1; pid_t pid; int status; /* Check if ncpfs is in the kernel */ ffs = fopen("/proc/filesystems", "r"); if (ffs == NULL) { perror("Error: \"/proc/filesystems\" could not be read:"); return -1; } p = NULL; while (! feof(ffs)) { p1 = fgets(s, sizeof(s), ffs); if (p1) { p = strstr(s, "ncpfs"); if (p) { break; } } } fclose(ffs); if (p) { return 0; } fver = fopen("/proc/version", "r"); if (fver == NULL) { perror("Error: \"/proc/version\" could not be read:"); return -1; } fgets(s, 1024, fver); fclose(fver); p = strstr(s, "version "); if (p == NULL) { version_error: fprintf(stderr, "Error: Unable to determine the Linux version" "from \"/proc/version\n"); return -1; } p = strchr(p, ' ') + 1; p1 = strchr(p, ' '); if (p1 == NULL) { goto version_error; } strcpy(modname, "/lib/modules/"); strncat(modname, p, p1 - p); strcat(modname, "/ncpfs.o"); /* system() function without signal handling, from Stevens */ if ((pid = fork()) < 0) { return 1; } else if (pid == 0) { /* child */ execl("/sbin/insmod", "insmod", modname, NULL); _exit(127); /* execl error */ } else { /* parent */ while (waitpid(pid, &status, 0) < 0) { if (errno != EINTR) { status = -1; break; } } } return status; } /* Check whether user is allowed to mount on the specified mount point */ static int mount_ok(struct stat *st) { if (!S_ISDIR(st->st_mode)) { errno = ENOTDIR; return -1; } if ( (getuid() != 0) && ( (getuid() != st->st_uid) || ((st->st_mode & S_IRWXU) != S_IRWXU))) { errno = EPERM; return -1; } return 0; } int main(int argc, char *argv[]) { struct ncp_mount_data data; struct sockaddr_ipx addr; struct stat st; int fd; int Got_Password; int Upcase_Password; int um; unsigned int flags; char hostname[MAXHOSTNAMELEN + 1]; char *server_name; char *mount_point; struct mntent ment; FILE *mtab; int ncp_sock, wdog_sock; progname = argv[0]; if (geteuid() != 0) { fprintf(stderr, "%s must be installed suid root\n", progname); exit(1); } memset(&data, 0, sizeof(struct ncp_mount_data)); memset(hostname, '\0', MAXHOSTNAMELEN+1); gethostname(hostname, MAXHOSTNAMELEN); if (argc < 3) { if ( (argc == 2) && (argv[1][0] == '-') && (argv[1][1] == 'h') && (argv[1][2] == '\0')) { help(); return 0; } else { usage(); return -1; } } server_name = argv[1]; mount_point = argv[2]; argv += 2; argc -= 2; if (stat(mount_point, &st) == -1) { fprintf(stderr, "could not find mount point %s: %s\n", mount_point, strerror(errno)); exit(1); } if (mount_ok(&st) != 0) { fprintf(stderr, "cannot to mount on %s: %s\n", mount_point, strerror(errno)); exit(1); } ncp_sock = socket(AF_IPX, SOCK_DGRAM, PF_IPX); if (ncp_sock == -1) { fprintf(stderr, "could not open ncp socket: %s\n", strerror(errno)); exit(1); } wdog_sock = socket(AF_IPX, SOCK_DGRAM, PF_IPX); if (wdog_sock == -1) { fprintf(stderr, "could not open wdog socket: %s\n", strerror(errno)); exit(1); } addr.sipx_type = NCP_PTYPE; if (bind(ncp_sock, (struct sockaddr *)&addr, sizeof(addr)) == -1) { fprintf(stderr, "bind(ncp_sock, ): %s\n", strerror(errno)); exit(1); } if (bind(wdog_sock, (struct sockaddr *)&addr, sizeof(addr)) == -1) { fprintf(stderr, "bind(wdog_sock, ): %s\n", strerror(errno)); exit(1); } if (ipx_sap_find_server(server_name, IPX_SAP_FILE_SERVER, 3, &addr) != 0) { fprintf(stderr, "could not find server %s\n", server_name); exit(1); } /* Check if the ncpfs filesystem is in the kernel. If not, attempt * to load the ncpfs module */ if (load_ncpfs() != 0) { fprintf(stderr, "Error: Unable to start ncpfs, exiting...\n"); exit(1); } data.version = NCP_MOUNT_VERSION; data.ncp_fd = ncp_sock; data.wdog_fd = wdog_sock; data.mounted_uid = getuid(); data.serv_addr = addr; strcpy(data.server_name, server_name); data.time_out = 20; /* 2 seconds */ data.retry_count = 2; /* getuid() gives us the real uid, who may umount the fs */ data.mounted_uid = getuid(); if (getenv("USER")) { strcpy(data.username, getenv("USER")); str_upper(data.username); } if (data.username[0] == 0 && getenv("LOGNAME")) { strcpy(data.username,getenv("LOGNAME")); str_upper(data.username); } data.uid = getuid(); data.gid = getgid(); um = umask(0); umask(um); data.file_mode = (S_IRWXU|S_IRWXG|S_IRWXO) & ~um; data.dir_mode = 0; if (parse_args(argc, argv, &data, &Got_Password, &Upcase_Password) != 0) { usage(); return -1; } if (data.dir_mode == 0) { data.dir_mode = data.file_mode; if ((data.dir_mode & S_IRUSR) != 0) data.dir_mode |= S_IXUSR; if ((data.dir_mode & S_IRGRP) != 0) data.dir_mode |= S_IXGRP; if ((data.dir_mode & S_IROTH) != 0) data.dir_mode |= S_IXOTH; } if (Got_Password == 0) { strcpy(data.password, getpass("Password: ")); } if (Upcase_Password == 1) { str_upper(data.password); } if (data.server_name[0] == '\0') { strcpy(data.server_name, server_name); str_upper(data.server_name); } flags = MS_MGC_VAL; if (mount(NULL, mount_point, "ncpfs", flags, (char *)&data) < 0) { perror("mount error"); close(wdog_sock); close(ncp_sock); printf("Maybe you should try to type the username and\n" "password in UPPERCASE.\n"); return -1; } close(ncp_sock); close(wdog_sock); ment.mnt_fsname = server_name; ment.mnt_dir = fullpath(mount_point); ment.mnt_type = "ncpfs"; ment.mnt_opts = "rw"; ment.mnt_freq = 0; ment.mnt_passno= 0; mount_point = ment.mnt_dir; if ((fd = open(MOUNTED"~", O_RDWR|O_CREAT|O_EXCL, 0600)) == -1) { fprintf(stderr, "Can't get "MOUNTED"~ lock file"); return 1; } close(fd); if ((mtab = setmntent(MOUNTED, "a+")) == NULL) { fprintf(stderr, "Can't open " MOUNTED); return 1; } if (addmntent(mtab, &ment) == 1) { fprintf(stderr, "Can't write mount entry"); return 1; } if (fchmod(fileno(mtab), 0644) == -1) { fprintf(stderr, "Can't set perms on "MOUNTED); return 1; } endmntent(mtab); if (unlink(MOUNTED"~") == -1) { fprintf(stderr, "Can't remove "MOUNTED"~"); return 1; } return 0; }