/* * 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 #include #include "ipxutil.h" #include "ncplib_user.h" static char *progname; static void str_upper(char *name) { while (*name) { *name = toupper(*name); name = name + 1; } } 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); } static int ipx_make_reachable(__u32 network) { struct rtentry rt_def; /* Router */ struct sockaddr_ipx *sr = (struct sockaddr_ipx *)&rt_def.rt_gateway; /* Target */ struct sockaddr_ipx *st = (struct sockaddr_ipx *)&rt_def.rt_dst; fd_set rd, wr, ex; struct timeval tv; struct ipx_rip_packet rip; struct sockaddr_ipx addr; int addrlen; int sock; int opt; int res=-1; int i; 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(&addr, 0, sizeof(addr)); addr.sipx_family=AF_IPX; addr.sipx_network=htonl(0x0); addr.sipx_port=htons(0x0); addr.sipx_type=IPX_RIP_PTYPE; if(bind(sock,(struct sockaddr*)&addr,sizeof(addr))==-1) { perror("bind"); goto finished; } addr.sipx_family = AF_IPX; addr.sipx_network = htonl(0x0); addr.sipx_port = htons(IPX_RIP_PORT); addr.sipx_type = IPX_RIP_PTYPE; ipx_assign_node(addr.sipx_node, IPX_BROADCAST_NODE); rip.operation = htons(IPX_RIP_REQUEST); rip.rt[0].network = htonl(network); if (sendto(sock, &rip, sizeof(rip), 0, (struct sockaddr *)&addr, sizeof(addr)) < 0) { perror("sendto"); goto finished; } do { FD_ZERO(&rd); FD_ZERO(&wr); FD_ZERO(&ex); FD_SET(sock, &rd); tv.tv_sec = 3; tv.tv_usec = 0; if (select(sock+1, &rd, &wr, &ex, &tv) == -1) { goto finished; } if (FD_ISSET(sock, &rd)) { int len; addrlen = sizeof(struct sockaddr_ipx); len = recvfrom(sock, &rip, sizeof(rip), 0, (struct sockaddr *)sr, &addrlen); if (len < sizeof(rip)) { continue; } } else { goto finished; } } while (ntohs(rip.operation) != IPX_RIP_RESPONSE); if (rip.rt[0].network != htonl(network)) { goto finished; } rt_def.rt_flags = RTF_GATEWAY; st->sipx_network = htonl(network); sr->sipx_family = st->sipx_family = AF_IPX; i = 0; do { res = ioctl(sock, SIOCADDRT, &rt_def); i++; } while ((i < 5) && (res < 0) && (errno == EAGAIN)); if (res != 0) { switch (errno) { case ENETUNREACH: fprintf(stderr, "%s: Router network (%08lX) not reachable.\n", progname, htonl(sr->sipx_network)); break; default: perror("ioctl"); break; } goto finished; } finished: close(sock); return res; } 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; struct sap_server_ident *ident; struct ncp_server server; struct nw_property prop; struct net_address { __u32 network __attribute__ ((packed)); __u8 node[6] __attribute__ ((packed)); __u16 port __attribute__ ((packed)); } *n_addr = (struct net_address *)∝ 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(IPX_SAP_NEAREST_QUERY); *(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; } do { 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) { goto finished; } if (FD_ISSET(sock, &rd)) { int len = recv(sock, data, 1024, 0); if (len < 96) { continue; } } else { goto finished; } } while (ntohs(*((__u16 *)data)) != IPX_SAP_NEAREST_RESPONSE); ident = (struct sap_server_ident *)(data+2); /* If the server we got back is the correct one, we normally would not need to ask for the NET_ADDRESS property. But we try to connect anyway to check whether there's a valid route to the server's internal network. Because this one request is not very expensive, we always do it. */ server.addr.sipx_family = AF_IPX; server.addr.sipx_network = ident->server_network; server.addr.sipx_port = ident->server_port; ipx_assign_node(server.addr.sipx_node, ident->server_node); if (ncp_connect(&server) != 0) { if ( (errno != ENETUNREACH) || (ipx_make_reachable(ntohl(server.addr.sipx_network))!=0) || (ncp_connect(&server) != 0)) { goto finished; } } if (ncp_read_property_value(&server, NCP_BINDERY_FSERVER, name, 1, "NET_ADDRESS", &prop) != 0) { ncp_disconnect(&server); goto finished; } if (ncp_disconnect(&server) != 0) { goto finished; } result->sipx_family = AF_IPX; result->sipx_network = n_addr->network; result->sipx_port = n_addr->port; ipx_assign_node(result->sipx_node, n_addr->node); res = 0; 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; struct ncp_server serv; struct ncp_server *server = &serv; char mount_name[256]; int fd; int Got_Password; int Upcase_Password; int um; unsigned int flags; char hostname[MAXHOSTNAMELEN + 1]; char *server_name; char mount_point[MAXPATHLEN]; 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]; str_upper(server_name); realpath(argv[2], mount_point); 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, "\nbind: %s\n", strerror(errno)); fprintf(stderr, "\nMaybe you want to use \n" "ipx_configure --auto_interface=on --auto_primary=on\n" "and try again after waiting a minute.\n\n"); 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) { printf("mount failed\n"); close(wdog_sock); close(ncp_sock); return -1; } close(ncp_sock); close(wdog_sock); if (ncp_connect_mount(server, mount_point) != 0) { fprintf(stderr, "Could not open %s: %s\n", mount_point, strerror(errno)); umount(mount_point); return -1; } server->silent = 1; if (ncp_login_user(server, data.username, data.password) != 0) { fprintf(stderr, "login failed\n"); fprintf(stderr, "should try to type the username and\n" "password in UPPERCASE.\n"); close(server->mount_fid); umount(mount_point); return -1; } strcpy(mount_name, server_name); strcat(mount_name, "/"); strcat(mount_name, data.username); ment.mnt_fsname = mount_name; ment.mnt_dir = mount_point; ment.mnt_type = "ncpfs"; ment.mnt_opts = "rw"; ment.mnt_freq = 0; ment.mnt_passno= 0; 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; }