/* ncplib.c Copyright (C) 1995, 1996 by Volker Lendecke Copyright (C) 1996, 1997, 1998, 1999, 2000 Petr Vandrovec This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Revision history: 0.00 1995 Volker Lendecke Initial release. 0.xx 1995-1999 Petr Vandrovec Paul Rensing Wolfram Pienkoss Eloy A. Paris <> David Woodhouse Christian Groessler Martin Stover <> Rik Faith Uwe Bones Tomasz Babczynski Guntram Blom <> Brian G. Reid Jeff Buhrt Neil Turton Tom C. Henderson Mathew Lim Steven M. Hirsch Volker Lendecke Different contributuions. See ../Changes for details. 1.00 1999, November 20 Petr Vandrovec Added license. 1.01 1999, December 5 Petr Vandrovec Added ERR_RDN_TOO_LONG error description. Removed select() call from recv path. 1.02 2000, January 13 Petr Vandrovec Use IPX_SAP_SPECIFIC_QUERY first. Helps on misconfigured networks when there is no server answering get nearest service requests. 1.03 2000, January 16 Petr Vandrovec Changes for 32bit uids. 1.04 2000, January 17 Petr Vandrovec Fix bugs in RIP route search. 1.05 2000, January 26 Petr Vandrovec Fixed ncp_get_fs_info for 32bit uids. 1.06 2000, May 14 Petr Vandrovec Added support for upcoming /dev/ncp device. 1.07 2000, May 24 Petr Vandrovec Added NWLogoutFromFileServer. 1.08 2000, May 27 Petr Vandrovec Modified ncp_open() to try server name as DNS too, so now you can use -S dns.name instead of -A dns.name. Fixes problem with pam_ncp_auth in pure-IP environment. 1.09 2000, Jun 20 Petr Vandrovec Added NCP/TCP userspace support. 1.10 2000, August 25 Petr Vandrovec Added some error descriptions. 1.11 2001, January 11 Patrick Pollet ncp_find_conn_spec2 should use passed 'uid' instead of doing 'getuid()' on its own. Could affect PHP ncp auth module and Apache ncp auth module. 1.12 2001, March 4 Petr Vandrovec Changed ncp_scan_bindery_object() prototype: search_filter is const. Added ncp_next_conn. 1.13 2001, May 31 Petr Vandrovec Include strings.h for AIX, use waitpid instead of wait4, call ioctl(,NCP_*,) only when compiling with kernel support. 1.14 2001, September 9 Petr Vandrovec Fix exec_nwsfind to open child's stdin/stderr with O_RDWR instead of O_RDONLY. 1.15 2001, September 23 Petr Vandrovec Always initialize unused fields of struct ncp_bindery_object to zero (in ncp_get_bindery_object_{id,name} and ncp_get_stations_logged_info). 1.16 2001, September 23 Petr Vandrovec Added ncp_change_object_security... How is it possible that nobody never evere noticed that this function occurs only in headers? 1.17 2001, December 4 Petr Vandrovec Call waitpid() without WNOHANG. Otherwise we can leave zombies on SMP machines, and maybe even on UP. 1.18 2002, January 6 Petr Vandrovec Added NULL pointer checks here and there. Added NCP reply size checks. 1.19 2003, March 19 Patrick Pollet Modified ncp_find_permanent & ncp_find_conn_spec3 to fix ncp_initialize not accepting -S /mount/point. */ /* #define CONFIG_NATIVE_UNIX */ #undef CONFIG_NATIVE_UNIX #define NCP_OBSOLETE #if 0 #define ncp_dprintf(X...) printf(X) #else #define ncp_dprintf(X...) #endif #include "config.h" #include "ncplib_i.h" /* due to NWVerifyObjectPassword... */ #include #include #include "ncpsign.h" #ifdef NDS_SUPPORT int bindery_only = 0; #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef NCP_KERNEL_NCPFS_AVAILABLE #include #endif #include #include #include #ifdef CONFIG_NATIVE_IP #include #endif #ifdef CONFIG_NATIVE_UNIX #include #endif #include #include "private/ncp-new.h" #include "ncpcode.h" #include "cfgfile.h" #include "private/libintl.h" #define _(X) dgettext(NCPFS_PACKAGE, (X)) #define N_(X) (X) #ifndef ENOPKG #define ENOPKG ENOSYS #endif #define NCP_DEFAULT_BUFSIZE 1024 #define NCP_MAX_BUFSIZE 1024 #ifdef SIGNATURES int in_options = 1; /* signatures supported - sign packets if NCP server requests it */ #else int in_options = 0; #endif static ncpt_mutex_t conn_lock = NCPT_MUTEX_INITIALIZER; static NCP_LIST_HEAD(conn_list); ncpt_mutex_t nds_ring_lock = NCPT_MUTEX_INITIALIZER; /* return number of bytes in packet */ static inline size_t ncp_packet_size(struct ncp_conn *_conn) { return _conn->current_point - _conn->packet - 6; } static long ncp_negotiate_buffersize(struct ncp_conn *conn, size_t size, size_t *target); #ifdef SIGNATURES static NWCCODE ncp_negotiate_size_and_options(struct ncp_conn *conn, size_t size, int options, size_t *ret_size, int *ret_options); #endif static long ncp_login_object(struct ncp_conn *conn, const char *username, int login_type, const char *password); static long ncp_do_close(struct ncp_conn *conn); void str_upper(char *name) { while (*name) { *name = toupper(*name); name = name + 1; } } #ifdef NCP_TRACE_ENABLE static FILE *logfile = 0; inline FILE * get_logfile() { if (!logfile) logfile = fopen("/var/log/ncplib.trc", "aw"); return logfile; } void __ncp_trace(char *module, int line, char *p,...) { FILE *log = get_logfile(); va_list ap; if (!log) return; fprintf(log, "%s(%d)->", module, line); va_start(ap, p); vfprintf(log, p, ap); va_end(ap); fprintf(log, "\n"); fflush(log); } void __dump_hex(const char *_msg, const unsigned char *_buf, size_t _len) { static char sym[] = "0123456789ABCDEF"; FILE *log = get_logfile(); if (!log) return; fprintf(log, "len = %d:msg->%s", _len, _msg); fflush(log); for (; _len > 0; _len--, _buf++) { putc(sym[(*_buf) >> 4], log); putc(sym[(*_buf) & 0x0F], log); } putc('\n', log); fflush(log); } #endif /*def NCP_TRACE_ENABLE */ #if 0 static int debug_level = 5; static FILE *logfile = stderr; static void dprintf(int level, char *p,...) { va_list ap; if (level > debug_level) { return; } va_start(ap, p); vfprintf(logfile, p, ap); va_end(ap); fprintf(logfile, "\n"); fflush(logfile); } #endif /* I know it's terrible to include a .c file here, but I want to keep the file nwcrypt.c intact and separate for copyright reasons */ #include "nwcrypt.c" 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, "%08X", (u_int32_t)ntohl(net)); } void ipx_fprint_port(FILE * file, IPXPort port) { fprintf(file, "%04X", ntohs(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); } int ipx_node_equal(CIPXNode n1, CIPXNode n2) { return memcmp(n1, n2, IPX_NODE_LEN) == 0; } 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; } #ifdef CONFIG_NATIVE_IPX 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_saddr(struct sockaddr_ipx *sipx) { ipx_fprint_saddr(stdout, sipx); } int ipx_sscanf_saddr(char *buf, struct sockaddr_ipx *target) { char *p; struct sockaddr_ipx addr; unsigned long sipx_network; addr.sipx_family = AF_IPX; addr.sipx_type = NCP_PTYPE; if (sscanf(buf, "%lx", &sipx_network) != 1) { return 1; } addr.sipx_network = htonl(sipx_network); if ((p = strchr(buf, ':')) == NULL) { return 1; } p += 1; if (ipx_sscanf_node(p, addr.sipx_node) != 6) { return 1; } if ((p = strchr(p, ':')) == NULL) { return 1; } p += 1; if (sscanf(p, "%hx", &addr.sipx_port) != 1) { return 1; } addr.sipx_port = htons(addr.sipx_port); *target = addr; return 0; } #endif /* CONFIG_NATIVE_IPX */ NWCCODE x_recvfrom(int sock, void *buf, int len, unsigned int flags, struct sockaddr *sender, socklen_t* addrlen, int timeout, size_t* rlen) { int result; restart: if (timeout >= 0) { struct pollfd pfd; pfd.fd = sock; pfd.events = POLLIN | POLLHUP; if ((result = poll(&pfd, 1, timeout)) == -1) { if (errno == EINTR) { timeout /= 2; goto restart; } return errno; } if (!(pfd.revents & (POLLIN | POLLHUP))) { return ETIMEDOUT; } } result = sender?recvfrom(sock, buf, len, flags, sender, addrlen): recv(sock, buf, len, flags); if (result < 0) return errno; *rlen = result; return 0; } int ncp_fd_nosigpipe(UNUSED(int fd)) { #ifdef HAVE_SO_NOSIGPIPE static const int one = 1; return setsockopt(fd, SOL_SOCKET, SO_NOSIGPIPE, &one, sizeof(one)); #else return 0; #endif } #ifdef CONFIG_NATIVE_IPX static int exec_nwsfind(const char* request[]) { int err; signal(SIGCHLD, SIG_DFL); err = fork(); if (err < 0) return errno; if (err == 0) { close(0); close(1); close(2); open("/dev/null", O_RDWR); /* fd 0 */ dup2(0, 1); dup2(0, 2); request[0] = NWSFIND; execv(request[0], (char**)request); /* OK, something bad happen... Drop it to /dev/null */ exit(127); } else { int rd; if (waitpid(err, &rd, 0) != err) { return -1; } if (!WIFEXITED(rd)) { return -1; } if (WEXITSTATUS(rd)) { return -1; } } return 0; } /* Used only by nwsfind */ int ipx_make_reachable_rip(const struct sockaddr_ipx* target); int ipx_make_reachable_rip(const struct sockaddr_ipx* target) { IPXNet network = target->sipx_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; struct ipx_rip_packet rip; struct sockaddr_ipx addr; socklen_t addrlen; int sock; int opt; int res = -1; int i; int packets; memset(&rip, 0, sizeof(rip)); sock = socket(PF_IPX, SOCK_DGRAM, IPXPROTO_IPX); if (sock == -1) { return errno; } if (ncp_fd_nosigpipe(sock)) { close(sock); return errno; } opt = 1; /* Permit broadcast output */ if (setsockopt(sock, SOL_SOCKET, SO_BROADCAST, &opt, sizeof(opt)) != 0) { 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)) != 0) { goto finished; } addr.sipx_family = AF_IPX; addr.sipx_port = htons(IPX_RIP_PORT); addr.sipx_type = IPX_RIP_PTYPE; addr.sipx_network = htonl(0x0); ipx_assign_node(addr.sipx_node, IPX_BROADCAST_NODE); rip.operation = htons(IPX_RIP_REQUEST); rip.rt[0].network = network; if (sendto(sock, &rip, sizeof(rip), 0, (struct sockaddr *) &addr, sizeof(addr)) < 0) { goto finished; } packets = 3; do { NWCCODE err; size_t len; if (packets == 0) { goto finished; } addrlen = sizeof(struct sockaddr_ipx); err = x_recvfrom(sock, &rip, sizeof(rip), 0, (struct sockaddr*)sr, &addrlen, 1000, &len); if (err || len < sizeof(rip)) { packets = packets - 1; continue; } } while (ntohs(rip.operation) != IPX_RIP_RESPONSE); if (rip.rt[0].network != network) { goto finished; } rt_def.rt_flags = RTF_GATEWAY; st->sipx_network = network; st->sipx_family = AF_IPX; sr->sipx_family = AF_IPX; i = 0; do { res = ioctl(sock, SIOCADDRT, &rt_def); i++; } while ((i < 5) && (res < 0) && (errno == EAGAIN)); finished: close(sock); if (res) { return ENETUNREACH; } return 0; } static int ipx_make_reachable_call(const struct sockaddr_ipx* target) { char buf[40]; const char *buf2[4]; buf2[1] = "-a"; buf2[2] = buf; buf2[3] = NULL; sprintf(buf, "%08x:%02x%02x%02x%02x%02x%02x:%04x", (u_int32_t)ntohl(target->sipx_network), ((const unsigned char*)target->sipx_node)[0], ((const unsigned char*)target->sipx_node)[1], ((const unsigned char*)target->sipx_node)[2], ((const unsigned char*)target->sipx_node)[3], ((const unsigned char*)target->sipx_node)[4], ((const unsigned char*)target->sipx_node)[5], ntohs(target->sipx_port)); return exec_nwsfind(buf2) ? ENETUNREACH : 0; } static int ipx_make_reachable(const struct sockaddr_ipx* target) { if (geteuid() == 0) return ipx_make_reachable_rip(target); else return ipx_make_reachable_call(target); } void ipx_assign_node(IPXNode dest, CIPXNode src) { memcpy(dest, src, IPX_NODE_LEN); } static void run_wdog(struct ncp_conn *conn, int fd) { struct pollfd pfd[2]; pfd[0].fd = conn->wdog_sock; pfd[0].events = POLLIN; pfd[1].fd = fd; pfd[1].events = POLLIN | POLLHUP; while (1) { switch (poll(pfd, 2, -1)) { case -1: if (errno != EINTR) { /* Commit suicide, error happened */ return; } break; case 0: break; default: if (pfd[0].revents & POLLIN) { struct sockaddr_ipx sender; socklen_t sizeofaddr = sizeof(struct sockaddr_ipx); unsigned char buf[1024]; size_t pktsize; NWCCODE err; err = x_recvfrom(pfd[0].fd, buf, sizeof(buf), 0, (struct sockaddr*)&sender, &sizeofaddr, 120000, &pktsize); if (!err && pktsize >= 2 && buf[1] == '?') { buf[1] = 'Y'; pktsize = sendto(pfd[0].fd, buf, 2, 0, (struct sockaddr *)&sender, sizeof(sender)); } } if (pfd[0].revents & (POLLHUP | POLLNVAL)) { /* HUP or invalid? Both unexpected => die */ return; } if (pfd[1].revents & POLLHUP) { /* Parent closed NCP connection => stop */ return; } if (pfd[1].revents & POLLIN) { int i; char cmd; /* Parent wrote something to us */ i = read(fd, &cmd, 1); if (i == 1) { switch (cmd) { case 'Q': /* quit */ return; } } /* Something wrong happened => die */ return; } if (pfd[1].revents & POLLNVAL) { /* Parent either send us some data, or invalid => die */ return; } break; } } } static int install_wdog(struct ncp_conn *conn) { int pid; int fildes[2]; int i; if (socketpair(PF_UNIX, SOCK_STREAM, 0, fildes)) { return -1; } fcntl(fildes[0], F_SETFD, FD_CLOEXEC); fcntl(fildes[1], F_SETFD, FD_CLOEXEC); if ((pid = fork()) < 0) { close(fildes[0]); close(fildes[1]); return -1; } if (pid != 0) { int status; pid_t p; close(fildes[0]); /* Parent: wait for child to exit */ p = waitpid(pid, &status, 0); if (p < 0) { close(fildes[1]); return -1; } if (!WIFEXITED(status) || WEXITSTATUS(status)) { close(fildes[1]); return -1; } conn->wdog_pipe = fildes[1]; return 0; } for (i = 0; i < 1024; i++) { if (i == fildes[0] || i == conn->wdog_sock) { continue; } close(i); } if ((pid = fork()) < 0) { close(fildes[0]); exit(1); } if (pid != 0) { close(fildes[0]); exit(0); } chdir("/"); run_wdog(conn, fildes[0]); /* wdog socket must be closed before wdog pipe */ close(conn->wdog_sock); close(fildes[0]); exit(0); } #endif /* CONFIG_NATIVE_IPX */ void ncp_lock_conn(struct ncp_conn *conn) { ncpt_mutex_lock(&conn->buffer_mutex); conn->lock++; } void ncp_unlock_conn(struct ncp_conn *conn) { assert_conn_locked(conn); conn->lock--; ncpt_mutex_unlock(&conn->buffer_mutex); } static struct ncp_conn *ncp_alloc_conn(void) { struct ncp_conn* conn; conn = (struct ncp_conn*)malloc(sizeof(*conn)); if (conn) { memset(conn, 0, sizeof(*conn)); ncpt_atomic_set(&conn->use_count, 1); ncpt_atomic_set(&conn->store_count, 0); NCP_INIT_LIST_HEAD(&conn->nds_ring); // conn->nds_conn = NULL; NCP_INIT_LIST_HEAD(&conn->conn_ring); ncpt_mutex_init(&conn->buffer_mutex); ncpt_mutex_init(&conn->serverInfo.mutex); // conn->serverInfo.valid = 0; // conn->connState = 0; conn->bcast_state = NWCC_BCAST_PERMIT_UNKNOWN; conn->global_fd = -1; conn->wdog_pipe = -1; ncpt_mutex_lock(&conn_lock); list_add(&conn->conn_ring, &conn_list); ncpt_mutex_unlock(&conn_lock); } return conn; } static inline unsigned int get_conn_from_reply(const void* hdr) { return BVAL(hdr, 3) | (BVAL(hdr, 5) << 8); } static NWCCODE do_ncp_call(struct ncp_conn *conn, unsigned int cmd, unsigned int task, const unsigned char* request, size_t request_size) { int result; int retries = 20; size_t len; NWCCODE err; unsigned char header[6]; unsigned char signature[8]; struct iovec io[3]; struct msghdr msg; conn->sequence++; WSET_HL(header, 0, cmd); BSET(header, 2, conn->sequence); BSET(header, 3, conn->i.connection); BSET(header, 4, task); BSET(header, 5, conn->i.connection >> 8); io[0].iov_base = header; io[0].iov_len = 6; io[1].iov_base = ncp_const_cast(request); io[1].iov_len = request_size; io[2].iov_base = signature; io[2].iov_len = sign_packet(conn, request, request_size, cpu_to_le32(request_size + 6), signature); msg.msg_namelen = 0; msg.msg_name = NULL; msg.msg_iov = io; msg.msg_iovlen = io[2].iov_len ? 3 : 2; msg.msg_control = NULL; msg.msg_controllen = 0; conn->ncp_reply = conn->ncp_reply_buffer; while (retries > 0) { retries -= 1; result = sendmsg(conn->ncp_sock, &msg, 0); if (result < 0) { return errno; } re_select: err = x_recv(conn->ncp_sock, conn->ncp_reply, conn->ncp_reply_alloc, 0, 3000, &len); if (err) { if (err == ETIMEDOUT) continue; return err; } if (len < sizeof(struct ncp_reply_header*) || len > conn->ncp_reply_alloc) { continue; } if (WVAL_HL(conn->ncp_reply, 0) != NCP_REPLY) { goto re_select; } if (cmd != NCP_ALLOC_SLOT_REQUEST && (BVAL(conn->ncp_reply, 2) != conn->sequence || get_conn_from_reply(conn->ncp_reply) != conn->i.connection)) { goto re_select; } if (ncp_get_sign_active(conn) && cmd != NCP_DEALLOC_SLOT_REQUEST) { unsigned int hdrl; len -= 8; hdrl = conn->nt == NT_UDP ? 8 : 6; if (len < hdrl) goto re_select; if (sign_verify_reply(conn, conn->ncp_reply + hdrl, len - hdrl, cpu_to_le32(len), conn->ncp_reply + len)) { goto re_select; } } conn->ncp_reply_size = len - sizeof(struct ncp_reply_header); return 0; } return ETIMEDOUT; } static int do_tcp_rcv(int fd, unsigned char* data, size_t len) { while (len) { int rcv; rcv = recv(fd, data, len, 0); if (rcv < 0) { if (errno == EINTR) continue; return errno; } if (rcv == 0) { return ECONNABORTED; } if ((size_t)rcv > len) { return ECONNABORTED; } len -= rcv; data += rcv; } return 0; } static int do_tcp_rcv_skip(int fd, size_t len) { while (len) { int rcv; unsigned char dummy[1000]; if (len > sizeof(dummy)) { rcv = recv(fd, dummy, sizeof(dummy), 0); } else { rcv = recv(fd, dummy, len, 0); } if (rcv < 0) { if (errno == EINTR) continue; return errno; } if (rcv == 0) { return ECONNABORTED; } if ((size_t)rcv > len) { return ECONNABORTED; } len -= rcv; } return 0; } static NWCCODE do_ncp_tcp_call(struct ncp_conn *conn, unsigned int cmd, unsigned int task, const unsigned char *request, size_t request_size) { unsigned char replyhdr[18]; int result; unsigned char rqhdr[30]; struct iovec io[2]; struct msghdr msg; size_t replyhdrlen; size_t ln; struct ncp_reply_header *reply; size_t siglen; conn->sequence++; siglen = sign_packet(conn, request, request_size, cpu_to_be32(request_size + 30), rqhdr + 16); DSET_HL(rqhdr, 0, 0x446D6454); DSET_HL(rqhdr, 4, request_size + 16 + siglen + 6); DSET_HL(rqhdr, 8, 1); DSET_HL(rqhdr, 12, sizeof(conn->packet)); WSET_HL(rqhdr, 16 + siglen + 0, cmd); BSET(rqhdr, 16 + siglen + 2, conn->sequence); BSET(rqhdr, 16 + siglen + 3, conn->i.connection); BSET(rqhdr, 16 + siglen + 4, task); BSET(rqhdr, 16 + siglen + 5, conn->i.connection >> 8); io[0].iov_base = rqhdr; io[0].iov_len = 16 + siglen + 6; io[1].iov_base = ncp_const_cast(request); io[1].iov_len = request_size; msg.msg_namelen = 0; msg.msg_name = NULL; msg.msg_iov = io; msg.msg_iovlen = 2; msg.msg_control = NULL; msg.msg_controllen = 0; result = sendmsg(conn->ncp_sock, &msg, MSG_NOSIGNAL); if (result < 0) { return errno; } if ((size_t)result != io[1].iov_len + io[0].iov_len) { return ECONNABORTED; } replyhdrlen = 8 + siglen + 2; while (1) { result = do_tcp_rcv(conn->ncp_sock, replyhdr, replyhdrlen); if (result) { return result; } if (DVAL_HL(replyhdr, 0) != 0x744E6350) { fprintf(stderr, "RecvUnk: %08X, %08X\n", DVAL_HL(replyhdr, 0), DVAL_HL(replyhdr, 4)); return ECONNABORTED; } ln = DVAL_HL(replyhdr, 4) & 0x0FFFFFFF; if (ln < replyhdrlen) { return ECONNABORTED; } ln -= replyhdrlen; if (WVAL_HL(replyhdr, replyhdrlen - 2) == NCP_REPLY) { break; } result = do_tcp_rcv_skip(conn->ncp_sock, ln); if (result) { return result; } }; if (ln < sizeof(*reply) - 2) { return ECONNABORTED; } if (ln > sizeof(conn->packet) - 2) { fprintf(stderr, "Too long reply: %zu\n", ln); return ECONNABORTED; } result = do_tcp_rcv(conn->ncp_sock, conn->packet + 2, ln); if (result) { return result; } reply = (struct ncp_reply_header*)conn->packet; reply->type = htons(NCP_REPLY); if (cmd != NCP_ALLOC_SLOT_REQUEST) { if (reply->sequence != conn->sequence || get_conn_from_reply(reply) != conn->i.connection) { /* Protocol violation... */ return ECONNABORTED; } } ln += 2; if (ncp_get_sign_active(conn) && cmd != NCP_DEALLOC_SLOT_REQUEST) { if (sign_verify_reply(conn, conn->packet + 6, ln - 6, cpu_to_be32(ln + 16), replyhdr + 8)) { return ERR_INVALID_SIGNATURE; } } conn->ncp_reply_size = ln - sizeof(struct ncp_reply_header); conn->ncp_reply = conn->packet; return 0; } static inline void fill_size(struct ncp_conn *_conn) { if (_conn->has_subfunction != 0) { size_t sz = ncp_packet_size(_conn) - 2 - 1; WSET_HL(_conn->packet, 7, sz); } } static NWCCODE ncp_mount_request(struct ncp_conn *conn, int function) { #ifdef NCP_KERNEL_NCPFS_AVAILABLE struct ncp_ioctl_request request; int result; assert_conn_locked(conn); fill_size(conn); request.function = function; request.size = ncp_packet_size(conn) + 6; request.data = conn->packet; if ((result = ioctl(conn->mount_fid, NCP_IOC_NCPREQUEST, &request)) < 0) { return errno; } conn->ncp_reply_size = result - sizeof(struct ncp_reply_header); conn->ncp_reply = conn->packet; result = BVAL(conn->packet, 6); conn->conn_status = BVAL(conn->packet, 7); if (result && (conn->verbose != 0)) { ncp_printf(_("ncp_request_error: %d\n"), result); } return (result == 0) ? 0 : (NWE_SERVER_ERROR | result); #else return ENOPKG; #endif /* NCP_KERNEL_NCPFS_AVAILABLE */ } static NWCCODE ncp_kernel_request(struct ncp_conn *conn, int function) { #ifdef NCP_KERNEL_NCPFS_AVAILABLE struct ncp_ioc_request_reply request; int result; assert_conn_locked(conn); fill_size(conn); request.function = function; request.request.size = ncp_packet_size(conn) - 1; request.request.addr = conn->packet + sizeof(struct ncp_request_header); request.reply.size = sizeof(conn->packet) - sizeof(struct ncp_reply_header); request.reply.addr = conn->packet + sizeof(struct ncp_reply_header); if ((result = ioctl(conn->mount_fid, NCP_IOC_REQUEST_REPLY, &request)) < 0) { return errno; } conn->ncp_reply_size = request.reply.size - sizeof(struct ncp_reply_header); conn->ncp_reply = conn->packet; if (result && (conn->verbose != 0)) { ncp_printf(_("ncp_request_error: %d\n"), result); } return (result == 0) ? 0 : (NWE_SERVER_ERROR | result); #else return ENOPKG; #endif /* NCP_KERNEL_NCPFS_AVAILABLE */ } static NWCCODE ncp_temp_request(struct ncp_conn *conn, int function) { NWCCODE err; assert_conn_locked(conn); BSET(conn->packet, 6, function); fill_size(conn); switch (conn->nt) { case NT_IPX: case NT_UDP: err = do_ncp_call(conn, NCP_REQUEST, 1, conn->packet + 6, ncp_packet_size(conn)); break; case NT_TCP: err = do_ncp_tcp_call(conn, NCP_REQUEST, 1, conn->packet + 6, ncp_packet_size(conn)); break; default: err = ECONNABORTED; break; } if (err) { return err; } err = BVAL(conn->ncp_reply, 6); conn->conn_status = BVAL(conn->ncp_reply, 7); if (err && (conn->verbose != 0)) { ncp_printf(_("ncp_request_error: %d\n"), err); } return (err == 0) ? 0 : (NWE_SERVER_ERROR | err); } NWCCODE ncp_renegotiate_siglevel(NWCONN_HANDLE conn, size_t buffsize, int siglevel) { size_t neg_buffsize; int err; int newoptions; int options; if (ncp_get_sign_active(conn)) siglevel = 3; if (siglevel < 2) newoptions = 0; else newoptions = 2; #ifdef SIGNATURES err = ncp_negotiate_size_and_options(conn, buffsize, newoptions, &neg_buffsize, &options); if (!err) { if ((options & 2) != newoptions) { if (siglevel == 3) { return NWE_SIGNATURE_LEVEL_CONFLICT; } if (siglevel != 0) { newoptions ^= 2; err = ncp_negotiate_size_and_options(conn, buffsize, newoptions, &neg_buffsize, &options); if (!err) { if ((options & 2) != newoptions) { return NWE_SIGNATURE_LEVEL_CONFLICT; } } } } } if (err) #endif { if (siglevel == 3) { return NWE_SIGNATURE_LEVEL_CONFLICT; } err = ncp_negotiate_buffersize(conn, buffsize, &neg_buffsize); if (err) { return err; } options = 0; } if ((neg_buffsize < 512) || (neg_buffsize > NCP_PACKET_SIZE - 40)) return NWE_REQUESTER_FAILURE; conn->i.buffer_size = neg_buffsize; #ifdef SIGNATURES conn->sign_wanted = (options & 2) ? 1 : 0; #ifdef NCP_KERNEL_NCPFS_AVAILABLE if (conn->is_connected == CONN_PERMANENT) { int cursign; if (ioctl(conn->mount_fid, NCP_IOC_SIGN_WANTED, &cursign)) { /* ncpfs does not support SIGN_WANTED -> current sign level = 0 */ cursign = 0; } if (cursign) cursign = 1; if (cursign != conn->sign_wanted) { int newsign = conn->sign_wanted ? -1 : 0; err = ioctl(conn->mount_fid, NCP_IOC_SET_SIGN_WANTED, &newsign); if (err) { return errno; } } } #endif /* NCP_KERNEL_NCPFS_AVAILABLE */ #endif return 0; } static inline NWCCODE ncp_negotiate_siglevel(struct ncp_conn *conn, size_t buffsize, int siglevel) { #ifdef SIGNATURES conn->sign_active = 0; conn->sign_wanted = 0; #endif return ncp_renegotiate_siglevel(conn, buffsize, siglevel); } long ncp_renegotiate_connparam(struct ncp_conn *conn, int buffsize, int newoptions) { if (newoptions & 2) newoptions = 2; else newoptions = 1; return ncp_renegotiate_siglevel(conn, buffsize, newoptions); } static void move_conn_to_kernel(UNUSED(struct ncp_conn *conn)) { #if 0 int f; int e; struct ncp_ioc_newconn nc; f = open("/dev/ncp", O_RDWR); if (f == -1) return; nc.fd = conn->ncp_sock; nc.flags = 0; if (ncp_get_sign_wanted(conn)) nc.flags |= NCP_FLAGS_SIGN_NEGOTIATED; nc.sequence = conn->sequence; nc.connection = conn->i.connection; e = ioctl(f, NCP_IOC_NEWCONN, &nc); if (e) { close(f); return; } close(conn->ncp_sock); conn->ncp_sock = -1; conn->is_connected = CONN_KERNELBASED; conn->mount_fid = f; #endif } static NWCCODE ncp_finish_connect(struct ncp_conn *conn) { NWCCODE err; conn->i.connection = get_conn_from_reply(conn->ncp_reply); conn->is_connected = CONN_TEMPORARY; err = ncp_negotiate_siglevel(conn, NCP_DEFAULT_BUFSIZE, in_options); if (err) { return err; } conn->bcast_state = NWCC_BCAST_PERMIT_ALL; return 0; } #ifdef CONFIG_NATIVE_IPX static long ncp_connect_ipx_addr(struct ncp_conn *conn, const struct sockaddr_ipx *target, int wdog_needed) { static const unsigned char zero = 0; struct sockaddr_ipx addr; socklen_t addrlen; int ncp_sock, wdog_sock; long err; conn->ncp_reply_buffer = malloc(NCP_PACKET_SIZE); if (!conn->ncp_reply_buffer) { return ENOMEM; } conn->ncp_reply_alloc = NCP_PACKET_SIZE; conn->is_connected = NOT_CONNECTED; conn->verbose = 0; if ((ncp_sock = socket(PF_IPX, SOCK_DGRAM, IPXPROTO_IPX)) == -1) { return errno; } if (ncp_fd_nosigpipe(ncp_sock)) { close(ncp_sock); return errno; } if ((wdog_sock = socket(PF_IPX, SOCK_DGRAM, IPXPROTO_IPX)) == -1) { close(ncp_sock); return errno; } if (ncp_fd_nosigpipe(wdog_sock)) { close(wdog_sock); close(ncp_sock); return errno; } addr.sipx_family = AF_IPX; addr.sipx_port = htons(0x0); addr.sipx_type = NCP_PTYPE; addr.sipx_network = IPX_THIS_NET; ipx_assign_node(addr.sipx_node, IPX_THIS_NODE); addrlen = sizeof(addr); if ((bind(ncp_sock, (struct sockaddr *) &addr, sizeof(addr)) == -1) || (getsockname(ncp_sock, (struct sockaddr *) &addr, &addrlen) == -1)) { int saved_errno = errno; close(ncp_sock); close(wdog_sock); return saved_errno; } addr.sipx_port = htons(ntohs(addr.sipx_port) + 1); if (bind(wdog_sock, (struct sockaddr *) &addr, sizeof(addr)) == -1) { int saved_errno = errno; close(ncp_sock); close(wdog_sock); return saved_errno; } conn->ncp_sock = ncp_sock; conn->wdog_sock = wdog_sock; conn->sequence = 255; conn->addr.ipx = *target; conn->nt = NT_IPX; if (connect(ncp_sock, (const struct sockaddr*)target, sizeof(*target)) == -1) { int saved_errno = errno; if ((saved_errno != ENETUNREACH) || ipx_make_reachable(target) || connect(ncp_sock, (const struct sockaddr*)target, sizeof(*target))) {; close(ncp_sock); close(wdog_sock); return saved_errno; } } conn->i.connection = ~0; if ((err = do_ncp_call(conn, NCP_ALLOC_SLOT_REQUEST, 1, &zero, 1)) != 0) { if ((err != ENETUNREACH) || (ipx_make_reachable(target) != 0) || ((err = do_ncp_call(conn, NCP_ALLOC_SLOT_REQUEST, 1, &zero, 1)) != 0)) { close(ncp_sock); close(wdog_sock); return err; } } if (wdog_needed != 0) { install_wdog(conn); } err = ncp_finish_connect(conn); if (err) { return err; } move_conn_to_kernel(conn); return 0; } #endif /* CONFIG_NATIVE_IPX */ #ifdef CONFIG_NATIVE_IP static long ncp_connect_in_addr(struct ncp_conn *conn, const struct sockaddr_in *target, UNUSED(int wdog_needed)) { static const unsigned char zero = 0; struct sockaddr_in addr; socklen_t addrlen; int ncp_sock; long err; conn->ncp_reply_buffer = malloc(NCP_PACKET_SIZE); if (!conn->ncp_reply_buffer) { return ENOMEM; } conn->ncp_reply_alloc = NCP_PACKET_SIZE; conn->is_connected = NOT_CONNECTED; conn->verbose = 0; if ((ncp_sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1) { return errno; } if (ncp_fd_nosigpipe(ncp_sock)) { close(ncp_sock); return errno; } addr.sin_family = AF_INET; addr.sin_port = htons(0x0); addr.sin_addr.s_addr = INADDR_ANY; addrlen = sizeof(addr); if (bind(ncp_sock, (struct sockaddr *) &addr, sizeof(addr)) == -1) { int saved_errno = errno; close(ncp_sock); return saved_errno; } conn->ncp_sock = ncp_sock; conn->wdog_sock = -1; conn->sequence = 255; memcpy(&conn->addr, target, sizeof(*target)); conn->nt = NT_UDP; if (connect(ncp_sock, (const struct sockaddr*)target, sizeof(*target)) == -1) { int saved_errno = errno; close(ncp_sock); return saved_errno; } conn->i.connection = ~0; if ((err = do_ncp_call(conn, NCP_ALLOC_SLOT_REQUEST, 1, &zero, 1)) != 0) { /* ? request route ? */ close(ncp_sock); return err; } err = ncp_finish_connect(conn); if (err) { return err; } move_conn_to_kernel(conn); return 0; } static long ncp_connect_tcp_addr(struct ncp_conn *conn, const struct sockaddr_in *target, UNUSED(int wdog_needed)) { static const unsigned char connreq[19] = { 1, 16, 0, 0 }; struct sockaddr_in addr; socklen_t addrlen; int ncp_sock; long err; conn->is_connected = NOT_CONNECTED; conn->verbose = 0; if ((ncp_sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1) { return errno; } if (ncp_fd_nosigpipe(ncp_sock)) { close(ncp_sock); return errno; } addr.sin_family = AF_INET; addr.sin_port = htons(0x0); addr.sin_addr.s_addr = INADDR_ANY; addrlen = sizeof(addr); if (bind(ncp_sock, (struct sockaddr *) &addr, sizeof(addr)) == -1) { int saved_errno = errno; close(ncp_sock); return saved_errno; } conn->ncp_sock = ncp_sock; conn->wdog_sock = -1; conn->sequence = 255; memcpy(&conn->addr, target, sizeof(*target)); conn->nt = NT_TCP; if (connect(ncp_sock, (const struct sockaddr*)target, sizeof(*target)) == -1) { int saved_errno = errno; close(ncp_sock); return saved_errno; } conn->i.connection = ~0; if ((err = do_ncp_tcp_call(conn, NCP_ALLOC_SLOT_REQUEST, 1, connreq, sizeof(connreq))) != 0) { /* ? request route ? */ close(ncp_sock); return err; } err = ncp_finish_connect(conn); if (err) { return err; } move_conn_to_kernel(conn); return 0; } #endif /* CONFIG_NATIVE_IP */ #ifdef CONFIG_NATIVE_UNIX static long ncp_connect_un_addr(struct ncp_conn *conn, const struct sockaddr_un *target, int wdog_needed) { static const unsigned char zero = 0; struct sockaddr_un addr; int fd; int ncp_sock; long err; conn->is_connected = NOT_CONNECTED; conn->verbose = 0; if ((fd = socket(PF_UNIX, SOCK_DGRAM, 0)) == -1) { return errno; } if (ncp_fd_nosigpipe(fd)) { close(fd); return errno; } addr.sun_family = AF_UNIX; if (bind(fd, (struct sockaddr *) &addr, 2) == -1) { int saved_errno = errno; close(fd); return saved_errno; } memcpy(addr.sun_path, "\000ncpfs", 6); if (connect(fd, (struct sockaddr *) &addr, 8) == -1) { int saved_errno = errno; close(fd); return saved_errno; } { unsigned char buffer = 'Q'; if (send(fd, &buffer, 1, MSG_DONTWAIT) != 1) { int saved_errno = errno; close(fd); return saved_errno; } } { unsigned char buffer[100]; struct msghdr msg; struct cmsghdr* cmsg; struct iovec io[1]; unsigned char ctrl[1024]; int ret; io[0].iov_base = buffer; io[0].iov_len = sizeof(buffer); msg.msg_namelen = 0; msg.msg_name = NULL; msg.msg_iov = io; msg.msg_iovlen = 1; msg.msg_control = ctrl; msg.msg_controllen = sizeof(ctrl); ret = recvmsg(fd, &msg, 0); if (ret < 0) { int saved_errno = errno; close(fd); return saved_errno; } close(fd); for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) { if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS && cmsg->cmsg_len >= CMSG_LEN(sizeof(int))) break; } if (!cmsg) return EACCES; ncp_sock = *(int*)(CMSG_DATA(cmsg)); if (ncp_sock == -1) return EINVAL; } #if 0 { int val = 1; if (setsockopt(ncp_sock, SOL_SOCKET, SO_PASSCRED, &val, sizeof(val)) == -1) { int saved_errno = errno; close(ncp_sock); return saved_errno; } } #endif conn->ncp_sock = ncp_sock; conn->wdog_sock = -1; conn->sequence = 255; memset(&conn->addr, 0, sizeof(conn->addr)); #if 0 memcpy(addr.sun_path, "\000ncpfs", 6); if (connect(ncp_sock, (const struct sockaddr*)&addr, 8) == -1) { int saved_errno = errno; close(ncp_sock); return saved_errno; } #endif conn->i.connection = ~0; if ((err = do_ncp_call(conn, NCP_ALLOC_SLOT_REQUEST, 1, &zero, 1)) != 0) { /* ? request route ? */ close(ncp_sock); return err; } err = ncp_finish_connect(conn); if (err) { return err; } return 0; } #endif /* CONFIG_NATIVE_UNIX */ static long ncp_connect_addr(struct ncp_conn *conn, const struct sockaddr *target, int wdog_needed, nuint transport) { switch (target->sa_family) { #ifdef CONFIG_NATIVE_IPX case AF_IPX: if (transport == NT_IPX) { return ncp_connect_ipx_addr(conn, (const struct sockaddr_ipx*)target, wdog_needed); } break; #endif /* CONFIG_NATIVE_IPX */ #ifdef CONFIG_NATIVE_IP case AF_INET: switch (transport) { case NT_TCP: return ncp_connect_tcp_addr(conn, (const struct sockaddr_in*)target, wdog_needed); case NT_UDP: return ncp_connect_in_addr(conn, (const struct sockaddr_in*)target, wdog_needed); } break; #endif /* CONFIG_NATIVE_IP */ #ifdef CONFIG_NATIVE_UNIX case AF_UNIX: return ncp_connect_un_addr(conn, (const struct sockaddr_un*)target, wdog_needed); #endif /* CONFIG_NATIVE_UNIX */ default: break; } return NWE_UNSUPPORTED_TRAN_TYPE; } static long ncp_connect_any(NWCONN_HANDLE *conn, UNUSED(int wdog_needed)) { return NWCCOpenConnByName(NULL, NULL, NWCC_NAME_FORMAT_BIND, 0, NWCC_RESERVED, conn); } long ncp_find_fileserver(const char *server_name, struct sockaddr* addr, socklen_t len) { return ncp_find_server(&server_name, OT_FILE_SERVER, addr, len); } #ifdef NDS_SUPPORT static NWCCODE ncp_login_nds(struct ncp_conn* conn, const char* object_name, const char* password) { NWCCODE err; err = NWE_NCP_NOT_SUPPORTED; if (NWIsDSServer(conn, NULL)) { err = nds_login_auth(conn, object_name, password); if (!err) return 0; if (err == NWE_PASSWORD_EXPIRED) { fprintf(stderr, _("Your password has expired\n")); return 0; } } return err; } #endif NWCCODE ncp_login_conn(struct ncp_conn* conn, const char* object_name, NWObjectType object_type, const char* password) { NWCCODE err; char* auth; auth = cfgGetItem("Requester", "NetWare Protocol"); if (auth) { char* ptr = auth; char* curr; err = NWE_UNSUPPORTED_AUTHENTICATOR; while ((curr = strsep(&ptr, " \t,")) != NULL) { if (!strcasecmp(curr, "BIND")) { err = ncp_login_object(conn, object_name, object_type, password); #ifdef NDS_SUPPORT } else if (!strcasecmp(curr, "NDS")) { err = ncp_login_nds(conn, object_name, password); #endif } else { /* Leave error code as is... */ } if (!err) { break; } } free(auth); } else { #ifdef NDS_SUPPORT err = ncp_login_nds(conn, object_name, password); if (!err) { return 0; } #endif err = ncp_login_object(conn, object_name, object_type, password); } return err; } static long ncp_open_temporary(NWCONN_HANDLE *conn, const char* server) { return NWCCOpenConnByName(NULL, server, NWCC_NAME_FORMAT_BIND, 0, NWCC_RESERVED, conn); } static long ncp_open_temporary2(struct ncp_conn **conn, const char* address) { /* Ask only for IPv4 name resolve... */ return NWCCOpenConnByName(NULL, address, NWCC_NAME_FORMAT_BIND, 0, NWCC_RESERVED, conn); } #ifdef NCP_KERNEL_NCPFS_AVAILABLE static int ncp_get_fs_info(int fd, struct ncp_fs_info_v2* info) { int err; struct ncp_fs_info i; info->version = NCP_GET_FS_INFO_VERSION_V2; err = ioctl(fd, NCP_IOC_GET_FS_INFO_V2, info); if (err != -1) { /* Kernel uses dirEntNum as seen by protocol (as an opaque 4 byte entity), while userspace uses host byteorder */ info->directory_id = DVAL_LH(&info->directory_id, 0); return err; } err = errno; if (err != EINVAL) return err; i.version = NCP_GET_FS_INFO_VERSION; err = ioctl(fd, NCP_IOC_GET_FS_INFO, &i); if (err != -1) { info->version = NCP_GET_FS_INFO_VERSION_V2; info->mounted_uid = i.mounted_uid; info->connection = i.connection; info->buffer_size = i.buffer_size; info->volume_number = i.volume_number; /* Kernel uses dirEntNum as seen by protocol (as an opaque 4 byte entity), while userspace uses host byteorder */ info->directory_id = DVAL_LH(&i.directory_id, 0); info->dummy1 = info->dummy2 = info->dummy3 = 0; return err; } return errno; } #endif char * ncp_find_permanent(const struct ncp_conn_spec *spec) { #ifdef NCP_KERNEL_NCPFS_AVAILABLE FILE *mtab; struct ncp_conn_ent *conn_ent; char *result = NULL; int mount_fid; struct ncp_fs_info_v2 i; if ((mtab = fopen(MOUNTED, "r")) == NULL) { return NULL; } while ((conn_ent = ncp_get_conn_ent(mtab)) != NULL) { if (spec != NULL) { if (conn_ent->uid != spec->uid) { continue; } if (spec->server[0] == '/') { if (strcmp(conn_ent->mount_point, spec->server)) { continue; } } else { if (spec->server[0] && strcasecmp(conn_ent->server, spec->server)) { continue; } if (spec->user[0] && strcasecmp(conn_ent->user, spec->user)) { continue; } } } mount_fid = open(conn_ent->mount_point, O_RDONLY, 0); if (mount_fid < 0) { continue; } if (ncp_get_fs_info(mount_fid, &i)) { close(mount_fid); continue; } close(mount_fid); result = conn_ent->mount_point; break; } fclose(mtab); errno = (result == NULL) ? ENOENT : 0; return result; #else return NULL; #endif /* NCP_KERNEL_NCPFS_AVAILABLE */ } #ifdef NCP_KERNEL_NCPFS_AVAILABLE #ifdef SIGNATURES static void ncp_sign_init_perm(struct ncp_conn *conn) { if (ioctl(conn->mount_fid, NCP_IOC_SIGN_WANTED, &conn->sign_wanted) != 0) conn->sign_wanted = 0; conn->sign_active = 0; } #endif #endif static int ncp_open_permanent(const struct ncp_conn_spec *spec, struct ncp_conn** conn) { #ifdef NCP_KERNEL_NCPFS_AVAILABLE char *mount_point; if ((mount_point = ncp_find_permanent(spec)) == NULL) { return -1; } return ncp_open_mount(mount_point, conn); #else return -1; #endif /* NCP_KERNEL_NCPFS_AVAILABLE */ } static NWCCODE ncp_open_2(struct ncp_conn **conn, const struct ncp_conn_spec *spec, const char* address) { struct ncp_conn *result; static int ncp_domain_bound = 0; NWCCODE err; if (!ncp_domain_bound) { bindtextdomain(NCPFS_PACKAGE, LOCALEDIR); ncp_domain_bound = 1; } if (ncp_open_permanent(spec, conn) == 0) { return 0; } if (spec) { err = NWE_REQUESTER_FAILURE; if (address) { err = ncp_open_temporary2(&result, address); } if (err) err = ncp_open_temporary(&result, spec->server); } else { err = ncp_connect_any(&result, 1); } if (err) { return err; } if (spec && (strlen(spec->user) != 0)) { err = ncp_login_conn(result, spec->user, spec->login_type, spec->password); if (err) { ncp_close(result); return err; } result->user = strdup(spec->user); } *conn = result; return 0; } struct ncp_conn * ncp_open(const struct ncp_conn_spec *spec, long *err) { NWCCODE nwerr; struct ncp_conn* conn; nwerr = ncp_open_2(&conn, spec, NULL); *err = nwerr; if (nwerr) return NULL; return conn; } static int ncp_do_open_fd(int fd, struct ncp_conn** conn) { #ifdef NCP_KERNEL_NCPFS_AVAILABLE struct ncp_conn *result; size_t klen; *conn = NULL; result = ncp_alloc_conn(); if (result == NULL) { return ENOMEM; } result->mount_fid = fd; result->is_connected = CONN_PERMANENT; /* on kernels <= 2.2.10 & 2.3.8 directory_id can be set incorrectly */ /* do stat(mount_point) before if you want to use it... */ if (ncp_get_fs_info(result->mount_fid, &(result->i))) { free(result); return errno; } #ifdef SIGNATURES ncp_sign_init_perm(result); #endif if (!ncp_get_private_key(result, NULL, &klen) && klen > 10) { result->connState |= CONNECTION_AUTHENTICATED; } else { nuint8 level; NWCCODE err = NWGetBinderyAccessLevel(result, &level, NULL); if (err == 0 && level != 0) { result->connState |= CONNECTION_AUTHENTICATED | CONNECTION_LICENSED; } } result->bcast_state = NWCC_BCAST_PERMIT_UNKNOWN; *conn = result; return 0; #else return ENOPKG; /* better error code, anyone? */ #endif /* NCP_KERNEL_NCPFS_AVAILABLE */ } int ncp_open_fd(int fd, struct ncp_conn** conn) { int fd2; int err; fd2 = dup(fd); if (fd2 == -1) return errno; err = ncp_do_open_fd(fd2, conn); if (err) close(fd2); return err; } int ncp_open_mount(const char *mount_point, struct ncp_conn** conn) { int fd; int err; fd = open(mount_point, O_RDONLY, 0); if (fd == -1) return errno; err = ncp_do_open_fd(fd, conn); if (err) { close(fd); return err; } /* returning NULL is not critical */ (*conn)->mount_point = strdup(mount_point); return 0; } static long ncp_user_disconnect(struct ncp_conn *conn) { static const unsigned char zero = 0; long result; switch (conn->nt) { case NT_IPX: case NT_UDP: result = do_ncp_call(conn, NCP_DEALLOC_SLOT_REQUEST, 1, &zero, 1); break; case NT_TCP: result = do_ncp_tcp_call(conn, NCP_DEALLOC_SLOT_REQUEST, 1, &zero, 1); break; default: result = ECONNABORTED; break; } if (result) { return result; } close(conn->ncp_sock); if (conn->wdog_sock != -1) { close(conn->wdog_sock); } if (conn->wdog_pipe != -1) { unsigned char dummy[1]; int res; res = send(conn->wdog_pipe, "Q", 1, MSG_NOSIGNAL); /* If write failed, we should not wait for child */ if (res == 1) { read(conn->wdog_pipe, dummy, 1); } close(conn->wdog_pipe); } return 0; } static long ncp_do_close(struct ncp_conn *conn) { long result; switch (conn->is_connected) { case CONN_PERMANENT: if (conn->global_fd != -1) close(conn->global_fd); result = close(conn->mount_fid); ncpt_mutex_lock(&nds_ring_lock); list_del(&conn->nds_ring); conn->nds_conn = NULL; ncpt_mutex_unlock(&nds_ring_lock); /* call NWDSReleaseDSConnection... */ conn->state++; break; case CONN_TEMPORARY: if (conn->global_fd != -1) close(conn->global_fd); result = ncp_user_disconnect(conn); ncpt_mutex_lock(&nds_ring_lock); list_del(&conn->nds_ring); conn->nds_conn = NULL; ncpt_mutex_unlock(&nds_ring_lock); /* call NWDSReleaseDSConnection... */ conn->state++; break; case CONN_KERNELBASED: if (conn->global_fd != -1) close(conn->global_fd); result = close(conn->mount_fid); ncpt_mutex_lock(&nds_ring_lock); list_del(&conn->nds_ring); conn->nds_conn = NULL; ncpt_mutex_unlock(&nds_ring_lock); /* call NWDSReleaseDSConnection... */ conn->state++; break; case NOT_CONNECTED: result = 0; break; default: result = -1; break; } conn->is_connected = NOT_CONNECTED; if (ncpt_atomic_read(&conn->store_count)) return 0; ncpt_mutex_lock(&conn_lock); list_del(&conn->conn_ring); ncpt_mutex_unlock(&conn_lock); if (conn->mount_point) { free(conn->mount_point); conn->mount_point = NULL; } if (conn->serverInfo.serverName) { free(conn->serverInfo.serverName); conn->serverInfo.serverName = NULL; } if (conn->user) { free(conn->user); conn->user = NULL; /* To be safe */ } if (conn->ncp_reply_buffer) { free(conn->ncp_reply_buffer); conn->ncp_reply_buffer = NULL; conn->ncp_reply_alloc = 0; } if (conn->private_key) { free(conn->private_key); conn->private_key = NULL; conn->private_key_len = 0; } ncpt_mutex_destroy(&conn->serverInfo.mutex); ncpt_mutex_destroy(&conn->buffer_mutex); free(conn); return result; } long ncp_conn_release(struct ncp_conn *conn) { if (!ncpt_atomic_dec_and_test(&conn->store_count)) return 0; if (ncpt_atomic_read(&conn->use_count)) return 0; return ncp_do_close(conn); } long ncp_close(struct ncp_conn *conn) { if (conn == NULL) { return 0; } if (ncpt_atomic_read(&conn->use_count)) { if (!ncpt_atomic_dec_and_test(&conn->use_count)) return 0; return ncp_do_close(conn); } else return NWE_REQUESTER_FAILURE; /* buggg ! */ } int ncp_get_mount_uid(int fid, uid_t* uid) { #ifdef NCP_KERNEL_NCPFS_AVAILABLE unsigned long k2_uid; __kernel_uid_t k_uid; int err; err = ioctl(fid, NCP_IOC_GETMOUNTUID2, &k2_uid); if (!err) { *uid = k2_uid; return 0; } if (errno != -EINVAL) return err; err = ioctl(fid, NCP_IOC_GETMOUNTUID, &k_uid); if (err) return err; *uid = k_uid; return 0; #else errno = ENOPKG; return -1; #endif /* NCP_KERNEL_NCPFS_AVAILABLE */ } struct ncp_conn_ent * ncp_get_conn_ent(FILE * filep) { #ifdef NCP_KERNEL_NCPFS_AVAILABLE static struct ncp_conn_ent entry; static char server[2 * NCPFS_MAX_CFG_USERNAME]; char *user; struct mntent *mnt_ent; int fid; memset(server, 0, sizeof(server)); memset(&entry, 0, sizeof(entry)); while ((mnt_ent = getmntent(filep)) != NULL) { if (strcmp(mnt_ent->mnt_type, "ncpfs") && strcmp(mnt_ent->mnt_type, "ncp")) { continue; } if (strlen(mnt_ent->mnt_fsname) >= sizeof(server)) { continue; } strcpy(server, mnt_ent->mnt_fsname); user = strchr(server, '/'); if (user == NULL) { continue; } *user = '\0'; user += 1; entry.user = user; if ((strlen(server) >= sizeof(entry.server)) || (strlen(mnt_ent->mnt_dir) >= sizeof(entry.mount_point))) { continue; } strcpy(entry.server, server); strcpy(entry.mount_point, mnt_ent->mnt_dir); fid = open(entry.mount_point, O_RDONLY, 0); if (fid == -1) { continue; } if (ncp_get_mount_uid(fid, &entry.uid) != 0) { close(fid); continue; } close(fid); return &entry; } #else errno = ENOPKG; #endif return NULL; } static struct ncp_conn_spec * ncp_get_nwc_ent(FILE * nwc) { static struct ncp_conn_spec spec; char line[512]; int line_len; char *user; char *password; memset(&spec, 0, sizeof(spec)); spec.uid = getuid(); while (fgets(line, sizeof(line), nwc) != NULL) { if ((line[0] == '\n') || (line[0] == '#')) { continue; } line_len = strlen(line); if (line[line_len - 1] == '\n') { line[line_len - 1] = '\0'; } user = strchr(line, '/'); password = strchr(user != NULL ? user : line, ' '); if (password != NULL) { *password = '\0'; password += 1; } if (user != NULL) { *user = '\0'; user += 1; if (strlen(user) >= sizeof(spec.user)) { continue; } strcpy(spec.user, user); } if (strlen(line) >= sizeof(spec.server)) { continue; } strcpy(spec.server, line); if (password != NULL) { while (*password == ' ') { password += 1; } if (strlen(password) >= sizeof(spec.password)) { continue; } strcpy(spec.password, password); } return &spec; } return NULL; } static NWCCODE ncp_fopen_nwc(FILE** nwc) { char path[MAXPATHLEN]; char *home = NULL; struct stat st; FILE* f; home = getenv("HOME"); if ((home == NULL) || (strlen(home) + sizeof(NWCLIENT) + 2 > sizeof(path))) { return ENAMETOOLONG; } strcpy(path, home); strcat(path, "/"); strcat(path, NWCLIENT); if (stat(path, &st) != 0) { return errno; } if (st.st_uid != getuid()) { return EACCES; } if ((st.st_mode & (S_IRWXO | S_IRWXG)) != 0) { return NCPLIB_INVALID_MODE; } f = fopen(path, "r"); if (!f) { return errno; } *nwc = f; return 0; } NWCCODE ncp_find_conn_spec3(const char *server, const char *user, const char *password, int login_necessary, uid_t uid, int allow_multiple_conns, struct ncp_conn_spec *spec) { FILE *nwc; struct ncp_conn_spec *nwc_ent; if (!spec) { return ERR_NULL_POINTER; } memset(spec, 0, sizeof(*spec)); spec->uid = uid; if (server != NULL) { if (strlen(server) >= sizeof(spec->server)) { return ENAMETOOLONG; } strcpy(spec->server, server); } else { if (ncp_fopen_nwc(&nwc)) { return NWE_SERVER_UNKNOWN; } nwc_ent = ncp_get_nwc_ent(nwc); fclose(nwc); if (nwc_ent == NULL) { return NWE_SERVER_NO_CONN; } strcpy(spec->server, nwc_ent->server); strcpy(spec->user, nwc_ent->user); } if (login_necessary == 0) { memset(spec->user, 0, sizeof(spec->user)); memset(spec->password, 0, sizeof(spec->password)); return 0; } if (user != NULL) { if (strlen(user) >= sizeof(spec->user)) { return ENAMETOOLONG; } strcpy(spec->user, user); } str_upper(spec->user); spec->login_type = NCP_BINDERY_USER; if (!allow_multiple_conns) { struct ncp_conn* conn; if (ncp_open_permanent(spec, &conn) == 0) { ncp_close(conn); if (login_necessary && !(conn->connState & CONNECTION_AUTHENTICATED)) { return NWE_USER_NO_NAME; } return 0; } } if (password != NULL) { if (strlen(password) >= sizeof(spec->password)) { return ENAMETOOLONG; } strcpy(spec->password, password); } else { if (!ncp_fopen_nwc(&nwc)) { while ((nwc_ent = ncp_get_nwc_ent(nwc)) != NULL) { if ((strcasecmp(spec->server, nwc_ent->server) != 0) || ((spec->user[0] != '\0') && (strcasecmp(spec->user, nwc_ent->user) != 0))) { continue; } strcpy(spec->user, nwc_ent->user); strcpy(spec->password, nwc_ent->password); break; } fclose(nwc); } } if (spec->user[0] == 0) { if (login_necessary == 1) return NWE_USER_NO_NAME; spec->password[0] = 0; return 0; } if ((spec->password[0] == 0) && (password == NULL)) { char *pwd; if (!(isatty(0) && isatty(1))) { return NCPLIB_PASSWORD_REQUIRED; } printf(_("Logging into %s as %s\n"), spec->server, spec->user); pwd = getpass(_("Password: ")); if (strlen(pwd) >= sizeof(spec->password)) { return ENAMETOOLONG; } strcpy(spec->password, pwd); } else { if (strcmp(spec->password, NWC_NOPASSWORD) == 0) { spec->password[0] = '\0'; } } str_upper(spec->server); str_upper(spec->user); str_upper(spec->password); return 0; } struct ncp_conn_spec * ncp_find_conn_spec2(const char *server, const char *user, const char *password, int login_necessary, uid_t uid, int allow_multiple_conns, long *err) { static struct ncp_conn_spec spec; NWCCODE nwerr; nwerr = ncp_find_conn_spec3(server, user, password, login_necessary, uid, allow_multiple_conns, &spec); *err = nwerr; if (!nwerr) return &spec; return NULL; } struct ncp_conn_spec * ncp_find_conn_spec(const char *server, const char *user, const char *password, int login_necessary, uid_t uid, long *err) { return ncp_find_conn_spec2(server, user, password, login_necessary, uid, 0, err); } static int ncp_init_get_argument(int *argc, char **argv, int arg_no, const char **target) { int count = 1; if (target != NULL) { if (arg_no + 1 >= *argc) { /* No argument to switch */ errno = EINVAL; return -1; } *target = argv[arg_no + 1]; count = 2; } /* Delete the consumed switch from the argument list and decrement the argument count */ while (count + arg_no < *argc) { argv[arg_no] = argv[arg_no + count]; arg_no += 1; } *argc -= count; return 0; } struct ncp_conn * ncp_initialize_2(int *argc, char **argv, int login_necessary, int login_type, long *err, int required) { const char *server = NULL; const char *user = NULL; const char *password = NULL; const char *address = NULL; struct ncp_conn_spec spec; struct ncp_conn *conn; int i = 1; NWCCODE nwerr; *err = EINVAL; while (i < *argc) { if ((argv[i][0] != '-') || (strlen(argv[i]) != 2)) { i += 1; continue; } switch (argv[i][1]) { case 'S': if (ncp_init_get_argument(argc, argv, i, &server) != 0) { return NULL; } continue; case 'U': if (ncp_init_get_argument(argc, argv, i, &user) != 0) { return NULL; } continue; case 'P': if (ncp_init_get_argument(argc, argv, i, &password) != 0) { return NULL; } if (password) { char* opw = (char*)password; password = strdup(password); memset(opw, 0, strlen(opw)); } continue; case 'n': if (ncp_init_get_argument(argc, argv, i, NULL) != 0) { return NULL; } password = NWC_NOPASSWORD; continue; #ifdef NDS_SUPPORT case 'b': if (ncp_init_get_argument(argc, argv, i, NULL) != 0) { return NULL; } bindery_only = 1; continue; #endif #ifdef CONFIG_NATIVE_IP case 'A': if (ncp_init_get_argument(argc, argv, i, &address) != 0) { return NULL; } continue; #endif /* CONFIG_NATIVE_IP */ } i += 1; } if ((!required) && !(server || user || password || address)) return NULL; nwerr = ncp_find_conn_spec3(server, user, password, login_necessary, getuid(), 0, &spec); if (nwerr) { *err = nwerr; if (login_necessary == 1) { return NULL; } return ncp_open(NULL, err); } spec.login_type = login_type; if (login_necessary == 0) { spec.user[0] = '\0'; } nwerr = ncp_open_2(&conn, &spec, address); *err = nwerr; if (nwerr) return NULL; return conn; } struct ncp_conn * ncp_initialize_as(int *argc, char **argv, int login_necessary, int login_type, long *err) { return ncp_initialize_2(argc, argv, login_necessary, login_type, err, 1); } struct ncp_conn * ncp_initialize(int *argc, char **argv, int login_necessary, long *err) { return ncp_initialize_as(argc, argv, login_necessary, NCP_BINDERY_USER, err); } long ncp_request(struct ncp_conn *conn, int function) { long result = ENOTCONN; #ifdef NCP_TRACE_ENABLE { char *ppacket = conn->packet + sizeof(struct ncp_request_header); if (conn->has_subfunction) { NCP_TRACE("ncp_request: fn=%d subfn=%d", function, (int) ppacket[2]); } else { NCP_TRACE("ncp_request: fn=%d subfn?%d", function, (int) *ppacket); } DUMP_HEX("ncp_request: packet=", ppacket, ncp_packet_size(conn)); } #endif /*def NCP_TRACE_ENABLE */ switch (conn->is_connected) { case CONN_PERMANENT: result = ncp_mount_request(conn, function); break; case CONN_TEMPORARY: result = ncp_temp_request(conn, function); break; case CONN_KERNELBASED: result = ncp_kernel_request(conn, function); break; default: break; } #ifdef NCP_TRACE_ENABLE NCP_TRACE("ncp_request: reply %ld", result); if (result == 0) { DUMP_HEX("ncp_request->reply=", conn->ncp_reply + sizeof(struct ncp_reply_header), conn->ncp_reply_size); } #endif /*def NCP_TRACE_ENABLE */ return result; } /****************************************************************************/ /* */ /* Helper functions */ /* */ /****************************************************************************/ struct nw_time_buffer { u_int8_t year; u_int8_t month; u_int8_t day; u_int8_t hour; u_int8_t minute; u_int8_t second; u_int8_t wday; } __attribute__((packed)); static time_t nw_to_ctime(struct nw_time_buffer *source) { struct tm u_time; memset(&u_time, 0, sizeof(u_time)); u_time.tm_sec = source->second; u_time.tm_min = source->minute; u_time.tm_hour = source->hour; u_time.tm_mday = source->day; u_time.tm_mon = source->month - 1; u_time.tm_year = source->year; u_time.tm_isdst = -1; if (u_time.tm_year < 80) { u_time.tm_year += 100; } return mktime(&u_time); } void ncp_add_pstring(struct ncp_conn *conn, const char *s) { int len = strlen(s); assert_conn_locked(conn); if (len > 255) { ncp_printf(_("ncpfs: string too long: %s\n"), s); len = 255; } ncp_add_byte(conn, len); ncp_add_mem(conn, s, len); return; } void ncp_init_request(struct ncp_conn *conn) { ncp_lock_conn(conn); conn->current_point = conn->packet + sizeof(struct ncp_request_header); conn->has_subfunction = 0; } void ncp_init_request_s(struct ncp_conn *conn, int subfunction) { ncp_init_request(conn); ncp_add_word_lh(conn, 0); /* preliminary size */ ncp_add_byte(conn, subfunction); conn->has_subfunction = 1; } /* Here the ncp calls begin */ static long ncp_negotiate_buffersize(struct ncp_conn *conn, size_t size, size_t *target) { NWCCODE err; nuint8 rq[2]; nuint8 buf[2]; NW_FRAGMENT rp; WSET_HL(rq, 0, size); rp.fragAddress = buf; rp.fragSize = 2; err = NWRequestSimple(conn, 33, rq, 2, &rp); if (err) return err; if (rp.fragSize < 2) return NWE_INVALID_NCP_PACKET_LENGTH; if (target) *target = min(WVAL_HL(buf, 0), size); return 0; } #ifdef SIGNATURES static NWCCODE ncp_negotiate_size_and_options(struct ncp_conn *conn, size_t size, int options, size_t *ret_size, int *ret_options) { long result; ncp_init_request(conn); if (options & 2) { ncp_add_word_hl(conn, size + 8); } else { ncp_add_word_hl(conn, size); } ncp_add_byte(conn, options); if ((result = ncp_request(conn, 0x61)) != 0) { ncp_unlock_conn(conn); return result; } if (conn->ncp_reply_size < 5) { ncp_unlock_conn(conn); return NWE_INVALID_NCP_PACKET_LENGTH; } if (ncp_reply_word_hl(conn, 0) == 0) *ret_size = size; else *ret_size = min(ncp_reply_word_hl(conn, 0), size); *ret_options = ncp_reply_byte(conn, 4); ncp_unlock_conn(conn); return 0; } #endif long ncp_get_file_server_description_strings(struct ncp_conn *conn, char target[512]) { NW_FRAGMENT rp; if (!target) return NWE_PARAM_INVALID; rp.fragAddress = target; rp.fragSize = 512; return NWRequestSimple(conn, NCPC_SFN(23,201), NULL, 0, &rp); } long ncp_get_file_server_time(struct ncp_conn *conn, time_t * target) { NWCCODE result; struct nw_time_buffer buf; NW_FRAGMENT rp; rp.fragAddress = &buf; rp.fragSize = sizeof(buf); result = NWRequestSimple(conn, 20, NULL, 0, &rp); if (result) return result; if (rp.fragSize < sizeof(buf)) return NWE_INVALID_NCP_PACKET_LENGTH; if (target) *target = nw_to_ctime(&buf); return 0; } long ncp_set_file_server_time(struct ncp_conn *conn, time_t * source) { int year; struct tm *utime = localtime(source); nuint8 srvtime[6]; year = utime->tm_year; if (year > 99) { year -= 100; } BSET(srvtime, 0, year); BSET(srvtime, 1, utime->tm_mon + 1); BSET(srvtime, 2, utime->tm_mday); BSET(srvtime, 3, utime->tm_hour); BSET(srvtime, 4, utime->tm_min); BSET(srvtime, 5, utime->tm_sec); return NWRequestSimple(conn, NCPC_SFN(23,202), srvtime, 6, NULL); } long ncp_get_file_server_information(struct ncp_conn *conn, struct ncp_file_server_info *target) { long result; ncp_init_request_s(conn, 17); if ((result = ncp_request(conn, 23)) != 0) { ncp_unlock_conn(conn); return result; } memcpy(target, ncp_reply_data(conn, 0), sizeof(*target)); ncp_unlock_conn(conn); target->MaximumServiceConnections = htons(target->MaximumServiceConnections); target->ConnectionsInUse = htons(target->ConnectionsInUse); target->MaxConnectionsEverUsed = htons(target->MaxConnectionsEverUsed); target->NumberMountedVolumes = htons(target->NumberMountedVolumes); return 0; } NWCCODE ncp_get_file_server_information_2(struct ncp_conn *conn, struct ncp_file_server_info_2 *target, size_t starget) { NWCCODE result; switch (starget) { case sizeof(struct ncp_file_server_info_2): break; default: return EINVAL; } ncp_init_request_s(conn, 17); if ((result = ncp_request(conn, 23)) != 0) { ncp_unlock_conn(conn); return result; } if (target) { memcpy(target->ServerName, ncp_reply_data(conn, 0), 48); target->ServerName[48] = 0; target->FileServiceVersion = ncp_reply_byte(conn, 48); target->FileServiceSubVersion = ncp_reply_byte(conn, 49); target->MaximumServiceConnections = ncp_reply_word_hl(conn, 50); target->ConnectionsInUse = ncp_reply_word_hl(conn, 52); target->NumberMountedVolumes = ncp_reply_word_hl(conn, 54); target->Revision = ncp_reply_byte(conn, 56); target->SFTLevel = ncp_reply_byte(conn, 57); target->TTSLevel = ncp_reply_byte(conn, 58); target->MaxConnectionsEverUsed = ncp_reply_word_hl(conn, 59); target->AccountVersion = ncp_reply_byte(conn, 61); target->VAPVersion = ncp_reply_byte(conn, 62); target->QueueVersion = ncp_reply_byte(conn, 63); target->PrintVersion = ncp_reply_byte(conn, 64); target->VirtualConsoleVersion = ncp_reply_byte(conn, 65); target->RestrictionLevel = ncp_reply_byte(conn, 66); target->InternetBridge = ncp_reply_byte(conn, 67); target->MixedModePathFlag = ncp_reply_byte(conn, 68); target->LocalLoginInfoCcode = ncp_reply_byte(conn, 69); target->ProductMajorVersion = ncp_reply_word_hl(conn, 70); target->ProductMinorVersion = ncp_reply_word_hl(conn, 72); target->ProductRevisionVersion = ncp_reply_word_hl(conn, 74); target->OSLanguageID = ncp_reply_byte(conn, 76); target->_64BitOffsetsSupportedFlag = ncp_reply_byte(conn, 77); } ncp_unlock_conn(conn); return 0; } long ncp_get_connlist(struct ncp_conn *conn, NWObjectType object_type, const char *object_name, int *returned_no, u_int8_t conn_numbers[256]) { long result; unsigned int cnlen; if (!object_name || !returned_no || !conn_numbers) { return ERR_NULL_POINTER; } ncp_init_request_s(conn, 21); ncp_add_word_hl(conn, object_type); ncp_add_pstring(conn, object_name); if ((result = ncp_request(conn, 23)) != 0) { ncp_unlock_conn(conn); return result; } if (conn->ncp_reply_size < 1) { ncp_unlock_conn(conn); return NWE_INVALID_NCP_PACKET_LENGTH; } cnlen = ncp_reply_byte(conn, 0); if (conn->ncp_reply_size < 1 + cnlen) { ncp_unlock_conn(conn); return NWE_INVALID_NCP_PACKET_LENGTH; } *returned_no = cnlen; memcpy(conn_numbers, ncp_reply_data(conn, 1), cnlen); ncp_unlock_conn(conn); return 0; } long ncp_get_stations_logged_info(struct ncp_conn *conn, u_int32_t connection, struct ncp_bindery_object *target, time_t * login_time) { long result; ncp_init_request_s(conn, 28); ncp_add_dword_lh(conn, connection); if ((result = ncp_request(conn, 23)) != 0) { ncp_unlock_conn(conn); return result; } if (conn->ncp_reply_size < 60) { ncp_unlock_conn(conn); return NWE_INVALID_NCP_PACKET_LENGTH; } if (target) { memset(target, 0, sizeof(*target)); target->object_id = ncp_reply_dword_hl(conn, 0); target->object_type = ncp_reply_word_hl(conn, 4); memcpy(target->object_name, ncp_reply_data(conn, 6), sizeof(target->object_name)); target->object_flags = 0; target->object_security = 0; target->object_has_prop = 0; } if (login_time) *login_time = nw_to_ctime((struct nw_time_buffer *) ncp_reply_data(conn, 54)); ncp_unlock_conn(conn); return 0; } long ncp_get_internet_address(struct ncp_conn *conn, u_int32_t connection, struct sockaddr *target, u_int8_t * conn_type) { long result; u_int8_t ct; union ncp_sockaddr* t2 = (union ncp_sockaddr*)target; if (!target) { return ERR_NULL_POINTER; } ncp_init_request_s(conn, 26); ncp_add_dword_lh(conn, connection); if ((result = ncp_request(conn, 23)) != 0) { ncp_unlock_conn(conn); return result; } memset(target, 0, sizeof(*target)); ct = ncp_reply_byte(conn, 12); if (conn_type) *conn_type = ct; if (ct == 11) { /* 11... why 11? Nobody knows... */ #ifdef NCP_IN_SUPPORT t2->inet.sin_family = AF_INET; memcpy(&(t2->inet.sin_addr.s_addr), ncp_reply_data(conn, 0), 4); /* I'm not sure about port... it is always 0 on Netwares I checked */ memcpy(&(t2->inet.sin_port), ncp_reply_data(conn, 4), 2); #endif } else { /* 2... IPX, 3... internal network (doc lies Appletalk DDP) */ #ifdef NCP_IPX_SUPPORT t2->ipx.sipx_family = AF_IPX; memcpy(&(t2->ipx.sipx_network), ncp_reply_data(conn, 0), 4); memcpy(&(t2->ipx.sipx_node), ncp_reply_data(conn, 4), 6); memcpy(&(t2->ipx.sipx_port), ncp_reply_data(conn, 10), 2); #endif } ncp_unlock_conn(conn); return 0; } long ncp_send_broadcast(struct ncp_conn *conn, u_int8_t no_conn, const u_int8_t * connections, const char *message) { long result; if (!message || (no_conn && !connections)) { return ERR_NULL_POINTER; } if (strlen(message) > 58) { return NWE_SERVER_FAILURE; } ncp_init_request_s(conn, 0); ncp_add_byte(conn, no_conn); ncp_add_mem(conn, connections, no_conn); ncp_add_pstring(conn, message); result = ncp_request(conn, 21); ncp_unlock_conn(conn); return result; } long ncp_send_broadcast2(struct ncp_conn *conn, unsigned int conns, const unsigned int* connlist, const char* message) { int i; long result; if (!message || (conns && !connlist)) { return ERR_NULL_POINTER; } i = strlen(message); if (i > 255) { return NWE_SERVER_FAILURE; } if (conns > 350) /* max pkt len ~ 1KB */ /* maybe do it by handshaked length ? */ return NWE_SERVER_FAILURE; ncp_init_request_s(conn, 0x0A); ncp_add_word_lh(conn, conns); for (;conns; --conns) ncp_add_dword_lh(conn, *connlist++); ncp_add_byte(conn, i); ncp_add_mem(conn, message, i); result = ncp_request(conn, 0x15); ncp_unlock_conn(conn); return result; } /* * target is a 8-byte buffer */ long ncp_get_encryption_key(struct ncp_conn *conn, unsigned char *target) { NW_FRAGMENT rp; NWCCODE err; if (!target) return NWE_PARAM_INVALID; rp.fragAddress = target; rp.fragSize = 8; err = NWRequestSimple(conn, NCPC_SFN(23,23), NULL, 0, &rp); if (err) return err; if (rp.fragSize < 8) return NWE_INVALID_NCP_PACKET_LENGTH; return 0; } long ncp_get_bindery_object_id(struct ncp_conn *conn, NWObjectType object_type, const char *object_name, struct ncp_bindery_object *target) { long result; if (!object_name || !target) { return ERR_NULL_POINTER; } ncp_init_request_s(conn, 53); ncp_add_word_hl(conn, object_type); ncp_add_pstring(conn, object_name); if ((result = ncp_request(conn, 23)) != 0) { ncp_unlock_conn(conn); return result; } if (conn->ncp_reply_size < 54) { ncp_unlock_conn(conn); return NWE_INVALID_NCP_PACKET_LENGTH; } target->object_id = ncp_reply_dword_hl(conn, 0); target->object_type = ncp_reply_word_hl(conn, 4); memcpy(target->object_name, ncp_reply_data(conn, 6), 48); target->object_flags = 0; target->object_security = 0; target->object_has_prop = 0; ncp_unlock_conn(conn); return 0; } long ncp_get_bindery_object_name(struct ncp_conn *conn, u_int32_t object_id, struct ncp_bindery_object *target) { long result; if (!target) { return ERR_NULL_POINTER; } ncp_init_request_s(conn, 54); ncp_add_dword_hl(conn, object_id); if ((result = ncp_request(conn, 23)) != 0) { ncp_unlock_conn(conn); return result; } target->object_id = ncp_reply_dword_hl(conn, 0); target->object_type = ncp_reply_word_hl(conn, 4); memcpy(target->object_name, ncp_reply_data(conn, 6), NCP_BINDERY_NAME_LEN); target->object_flags = 0; target->object_security = 0; target->object_has_prop = 0; ncp_unlock_conn(conn); return 0; } NWCCODE NWScanObject(NWCONN_HANDLE conn, const char *searchName, NWObjectType searchType, NWObjectID *objID, char objName[NCP_BINDERY_NAME_LEN + 1], NWObjectType *objType, nuint8 *hasPropertiesFlag, nuint8 *objFlags, nuint8 *objSecurity) { NWCCODE result; if (!searchName || !objID) { return ERR_NULL_POINTER; } ncp_init_request_s(conn, 55); ncp_add_dword_hl(conn, *objID); ncp_add_word_hl(conn, searchType); ncp_add_pstring(conn, searchName); /* fn: 23 , subfn: 55 */ if ((result = ncp_request(conn, 23)) != 0) { ncp_unlock_conn(conn); return result; } *objID = ncp_reply_dword_hl(conn, 0); if (objType) { *objType = ncp_reply_word_hl(conn, 4); } if (objName) { memcpy(objName, ncp_reply_data(conn, 6), NCP_BINDERY_NAME_LEN); objName[NCP_BINDERY_NAME_LEN] = 0; } if (hasPropertiesFlag) { *hasPropertiesFlag = ncp_reply_byte(conn, 56); } if (objFlags) { *objFlags = ncp_reply_byte(conn, 54); } if (objSecurity) { *objSecurity = ncp_reply_byte(conn, 55); } ncp_unlock_conn(conn); return 0; } long ncp_scan_bindery_object(struct ncp_conn *conn, NWObjectID last_id, NWObjectType object_type, const char *search_string, struct ncp_bindery_object *target) { NWCCODE result; nuint8 hasProperties, objFlags, objSecurity; if (!target) { return ERR_NULL_POINTER; } result = NWScanObject(conn, search_string, object_type, &last_id, target->object_name, &object_type, &hasProperties, &objFlags, &objSecurity); if (!result) { target->object_id = last_id; target->object_type = object_type; target->object_flags = objFlags; target->object_security = objSecurity; target->object_has_prop = hasProperties; } return result; } long ncp_change_object_security(struct ncp_conn *conn, NWObjectType object_type, const char *object_name, u_int8_t new_object_security) { long result; if (!object_name) { return ERR_NULL_POINTER; } ncp_init_request_s(conn, 56); ncp_add_byte(conn, new_object_security); ncp_add_word_hl(conn, object_type); ncp_add_pstring(conn, object_name); result = ncp_request(conn, 23); ncp_unlock_conn(conn); return result; } long ncp_create_bindery_object(struct ncp_conn *conn, NWObjectType object_type, const char *object_name, u_int8_t object_security, u_int8_t object_status) { long result; if (!object_name) { return ERR_NULL_POINTER; } ncp_init_request_s(conn, 50); ncp_add_byte(conn, object_status); ncp_add_byte(conn, object_security); ncp_add_word_hl(conn, object_type); ncp_add_pstring(conn, object_name); result = ncp_request(conn, 23); ncp_unlock_conn(conn); return result; } long ncp_delete_bindery_object(struct ncp_conn *conn, NWObjectType object_type, const char *object_name) { long result; if (!object_name) { return ERR_NULL_POINTER; } ncp_init_request_s(conn, 51); ncp_add_word_hl(conn, object_type); ncp_add_pstring(conn, object_name); result = ncp_request(conn, 23); ncp_unlock_conn(conn); return result; } NWCCODE NWReadPropertyValue(NWCONN_HANDLE conn, const char *objName, NWObjectType objType, const char *propertyName, unsigned int segmentNum, nuint8 *segmentData, nuint8 *moreSegments, nuint8 *flags) { NWCCODE result; if (!objName || !propertyName) { return ERR_NULL_POINTER; } if (segmentNum >= 256) { return NWE_PARAM_INVALID; } ncp_init_request_s(conn, 61); ncp_add_word_hl(conn, objType); ncp_add_pstring(conn, objName); ncp_add_byte(conn, segmentNum); ncp_add_pstring(conn, propertyName); if ((result = ncp_request(conn, 23)) != 0) { ncp_unlock_conn(conn); return result; } if (conn->ncp_reply_size < 130) { ncp_unlock_conn(conn); return NWE_INVALID_NCP_PACKET_LENGTH; } if (segmentData) { memcpy(segmentData, ncp_reply_data(conn, 0), 128); } if (moreSegments) { *moreSegments = ncp_reply_byte(conn, 128); } if (flags) { *flags = ncp_reply_byte(conn, 129); } ncp_unlock_conn(conn); return 0; } long ncp_read_property_value(struct ncp_conn *conn, NWObjectType object_type, const char *object_name, int segment, const char *prop_name, struct nw_property *target) { nuint8 moreSegments; nuint8 flags; NWCCODE result; if (!target) { return ERR_NULL_POINTER; } result = NWReadPropertyValue(conn, object_name, object_type, prop_name, segment, target->value, &moreSegments, &flags); if (!result) { target->more_flag = moreSegments; target->property_flag = flags; } return result; } long ncp_scan_property(struct ncp_conn *conn, NWObjectType object_type, const char *object_name, u_int32_t last_id, const char *search_string, struct ncp_property_info *property_info) { long result; if (!object_name || !search_string || !property_info) { return ERR_NULL_POINTER; } ncp_init_request_s(conn, 60); ncp_add_word_hl(conn, object_type); ncp_add_pstring(conn, object_name); ncp_add_dword_hl(conn, last_id); ncp_add_pstring(conn, search_string); if ((result = ncp_request(conn, 23)) != 0) { ncp_unlock_conn(conn); return result; } if (conn->ncp_reply_size < 24) { ncp_unlock_conn(conn); return NWE_INVALID_NCP_PACKET_LENGTH; } memcpy(property_info->property_name, ncp_reply_data(conn, 0), 16); property_info->property_flags = ncp_reply_byte(conn, 16); property_info->property_security = ncp_reply_byte(conn, 17); property_info->search_instance = ncp_reply_dword_hl(conn, 18); property_info->value_available_flag = ncp_reply_byte(conn, 22); property_info->more_properties_flag = ncp_reply_byte(conn, 23); ncp_unlock_conn(conn); return 0; } long ncp_add_object_to_set(struct ncp_conn *conn, NWObjectType object_type, const char *object_name, const char *property_name, NWObjectType member_type, const char *member_name) { long result; if (!object_name || !property_name || !member_name) { return ERR_NULL_POINTER; } ncp_init_request_s(conn, 65); ncp_add_word_hl(conn, object_type); ncp_add_pstring(conn, object_name); ncp_add_pstring(conn, property_name); ncp_add_word_hl(conn, member_type); ncp_add_pstring(conn, member_name); result = ncp_request(conn, 23); ncp_unlock_conn(conn); return result; } long ncp_change_property_security(struct ncp_conn *conn, NWObjectType object_type, const char *object_name, const char *property_name, u_int8_t property_security) { long result; if (!object_name || !property_name) { return ERR_NULL_POINTER; } ncp_init_request_s(conn, 59); ncp_add_word_hl(conn, object_type); ncp_add_pstring(conn, object_name); ncp_add_byte(conn, property_security); ncp_add_pstring(conn, property_name); result = ncp_request(conn, 23); ncp_unlock_conn(conn); return result; } long ncp_create_property(struct ncp_conn *conn, NWObjectType object_type, const char *object_name, const char *property_name, u_int8_t property_flags, u_int8_t property_security) { long result; if (!object_name || !property_name) { return ERR_NULL_POINTER; } ncp_init_request_s(conn, 57); ncp_add_word_hl(conn, object_type); ncp_add_pstring(conn, object_name); ncp_add_byte(conn, property_flags); ncp_add_byte(conn, property_security); ncp_add_pstring(conn, property_name); result = ncp_request(conn, 23); ncp_unlock_conn(conn); return result; } long ncp_delete_object_from_set(struct ncp_conn *conn, NWObjectType object_type, const char *object_name, const char *property_name, NWObjectType member_type, const char *member_name) { long result; if (!object_name || !property_name || !member_name) { return ERR_NULL_POINTER; } ncp_init_request_s(conn, 66); ncp_add_word_hl(conn, object_type); ncp_add_pstring(conn, object_name); ncp_add_pstring(conn, property_name); ncp_add_word_hl(conn, member_type); ncp_add_pstring(conn, member_name); result = ncp_request(conn, 23); ncp_unlock_conn(conn); return result; } long ncp_delete_property(struct ncp_conn *conn, NWObjectType object_type, const char *object_name, const char *property_name) { long result; if (!object_name || !property_name) { return ERR_NULL_POINTER; } ncp_init_request_s(conn, 58); ncp_add_word_hl(conn, object_type); ncp_add_pstring(conn, object_name); ncp_add_pstring(conn, property_name); result = ncp_request(conn, 23); ncp_unlock_conn(conn); return result; } long ncp_write_property_value(struct ncp_conn *conn, NWObjectType object_type, const char *object_name, const char *property_name, u_int8_t segment, const struct nw_property *property_value) { long result; if (!object_name || !property_name || !property_value) { return ERR_NULL_POINTER; } ncp_init_request_s(conn, 62); ncp_add_word_hl(conn, object_type); ncp_add_pstring(conn, object_name); ncp_add_byte(conn, segment); ncp_add_byte(conn, property_value->more_flag); ncp_add_pstring(conn, property_name); ncp_add_mem(conn, property_value->value, 128); result = ncp_request(conn, 23); ncp_unlock_conn(conn); return result; } long ncp_get_big_ncp_max_packet_size(struct ncp_conn *conn, u_int16_t proposed_max_size, u_int8_t proposed_security_flag, u_int16_t * accepted_max_size, u_int16_t * echo_socket, u_int8_t * accepted_security_flag) { long result; ncp_init_request(conn); ncp_add_word_hl(conn, proposed_max_size); ncp_add_byte(conn, proposed_security_flag); if ((result = ncp_request(conn, 97)) != 0) { ncp_unlock_conn(conn); return result; } if (conn->ncp_reply_size < 5) { ncp_unlock_conn(conn); return NWE_INVALID_NCP_PACKET_LENGTH; } if (accepted_max_size) *accepted_max_size = ncp_reply_word_hl(conn, 0); if (echo_socket) *echo_socket = ncp_reply_word_hl(conn, 2); if (accepted_security_flag) *accepted_security_flag = ncp_reply_byte(conn, 4); ncp_unlock_conn(conn); return 0; } #if 0 static long ncp_verify_object_password(struct ncp_conn *conn, const char *objName, NWObjectType objType, const char *objPassword) { long result; ncp_init_request_s(conn, 63); ncp_add_word_hl(conn, object_type); ncp_add_pstring(conn, object_name); ncp_add_pstring(conn, passwd); result = ncp_request(conn, 23); ncp_unlock_conn(conn); return result; } #endif static long ncp_keyed_verify_password(struct ncp_conn *conn, const struct ncp_bindery_object *object, const unsigned char *key, const char *passwd) { dword tmpID = htonl(object->object_id); unsigned char buf[128]; unsigned char encrypted[8]; long result; if (!passwd) { return ERR_NULL_POINTER; } shuffle((byte *) & tmpID, passwd, strlen(passwd), buf); nw_encrypt(key, buf, encrypted); ncp_init_request_s(conn, 74); ncp_add_mem(conn, encrypted, 8); ncp_add_word_hl(conn, object->object_type); ncp_add_pstring(conn, object->object_name); result = ncp_request(conn, 23); ncp_unlock_conn(conn); return result; } NWCCODE NWVerifyObjectPassword(NWCONN_HANDLE conn, const char* objName, NWObjectType objType, const char* objPassword) { NWCCODE result; u_int8_t ncp_key[8]; struct ncp_bindery_object user; if ((result = ncp_get_encryption_key(conn, ncp_key)) != 0) { #if 0 if (!ncp_conn_trusted(conn)) result = ncp_verify_object_password(conn, objName, objType, objPassword); #endif return result; } result = ncp_get_bindery_object_id(conn, objType, objName, &user); if (result) return result; return ncp_keyed_verify_password(conn, &user, ncp_key, objPassword); } static NWCCODE ncp_sign_stop(NWCONN_HANDLE conn); NWCCODE NWLogoutFromFileServer(NWCONN_HANDLE conn) { NWCCODE result; ncp_init_request(conn); result = ncp_request(conn, 25); if (result == 0) { conn->user_id = 0; conn->user_id_valid = 1; conn->state++; conn->connState &= ~(CONNECTION_AUTHENTICATED | CONNECTION_LICENSED); result = ncp_sign_stop(conn); } ncp_unlock_conn(conn); return result; } long ncp_login_encrypted(struct ncp_conn *conn, const struct ncp_bindery_object *object, const unsigned char *key, const char *passwd) { dword tmpID; unsigned char buf[128]; unsigned char encrypted[8]; long result; if (!passwd || !key || !object) { return ERR_NULL_POINTER; } tmpID = htonl(object->object_id); shuffle((byte *) & tmpID, passwd, strlen(passwd), buf); nw_encrypt(key, buf, encrypted); ncp_init_request_s(conn, 24); ncp_add_mem(conn, encrypted, 8); ncp_add_word_hl(conn, object->object_type); ncp_add_pstring(conn, object->object_name); result = ncp_request(conn, 23); if ((result == 0) || (result == NWE_PASSWORD_EXPIRED)) { long result2; /* we must not set user_id here, we do not know it. We know special `password ID' here only. */ conn->user_id_valid = 0; conn->state++; conn->connState |= CONNECTION_AUTHENTICATED | CONNECTION_LICENSED; memcpy(buf + 16, key, 8); sign_init(buf, buf); result2 = ncp_sign_start(conn, buf); if (result2) result = result2; } ncp_unlock_conn(conn); return result; } long ncp_login_unencrypted(struct ncp_conn *conn, NWObjectType object_type, const char *object_name, const char *passwd) { long result; if (!object_name || !passwd) { return ERR_NULL_POINTER; } ncp_init_request_s(conn, 20); ncp_add_word_hl(conn, object_type); ncp_add_pstring(conn, object_name); ncp_add_pstring(conn, passwd); result = ncp_request(conn, 23); if ((result == 0) || (result == NWE_PASSWORD_EXPIRED)) { /* we do not have user_id here... */ conn->user_id_valid = 0; conn->state++; conn->connState |= CONNECTION_AUTHENTICATED | CONNECTION_LICENSED; } ncp_unlock_conn(conn); return result; } long ncp_change_login_passwd(struct ncp_conn *conn, const struct ncp_bindery_object *object, const unsigned char *key, const char *oldpasswd, const char *newpasswd) { long id; unsigned char cryptkey[8]; unsigned char newpwd[16]; /* new passwd as stored by server */ unsigned char oldpwd[16]; /* old passwd as stored by server */ unsigned char len; long result; if (!object || !key || !oldpasswd || !newpasswd) { return ERR_NULL_POINTER; } id = htonl(object->object_id); memcpy(cryptkey, key, 8); shuffle((byte *) & id, oldpasswd, strlen(oldpasswd), oldpwd); shuffle((byte *) & id, newpasswd, strlen(newpasswd), newpwd); nw_encrypt(cryptkey, oldpwd, cryptkey); newpassencrypt(oldpwd, newpwd); newpassencrypt(oldpwd + 8, newpwd + 8); if ((len = strlen(newpasswd)) > 63) { len = 63; } len = ((len ^ oldpwd[0] ^ oldpwd[1]) & 0x7f) | 0x40; ncp_init_request_s(conn, 75); ncp_add_mem(conn, cryptkey, 8); ncp_add_word_hl(conn, object->object_type); ncp_add_pstring(conn, object->object_name); ncp_add_byte(conn, len); ncp_add_mem(conn, newpwd, 16); result = ncp_request(conn, 23); ncp_unlock_conn(conn); return result; } long ncp_login_user(struct ncp_conn *conn, const char *username, const char *password) { return ncp_login_object(conn, username, NCP_BINDERY_USER, password); } static long ncp_login_object(struct ncp_conn *conn, const char *username, int login_type, const char *password) { long result; unsigned char ncp_key[8]; struct ncp_bindery_object user; if ((result = ncp_get_encryption_key(conn, ncp_key)) != 0) { return ncp_login_unencrypted(conn, login_type, username, password); } if ((result = ncp_get_bindery_object_id(conn, login_type, username, &user)) != 0) { return result; } if ((result = ncp_login_encrypted(conn, &user, ncp_key, password)) != 0) { struct nw_property p; struct ncp_prop_login_control *l = (struct ncp_prop_login_control *) &p; if (result != NWE_PASSWORD_EXPIRED) { return result; } fprintf(stderr, _("Your password has expired\n")); if ((result = ncp_read_property_value(conn, NCP_BINDERY_USER, username, 1, "LOGIN_CONTROL", &p)) == 0) { fprintf(stderr, _("You have %d login attempts left\n"), l->GraceLogins); } } return 0; } static NWCCODE ncp_sign_stop(UNUSED(NWCONN_HANDLE conn)) { #ifdef SIGNATURES return 0; #if 0 NWCCODE result = 0; printf("Conn %p, %d\n", conn, conn->sign_active); if (conn->sign_active) { conn->sign_active = 0; switch (conn->is_connected) { case NOT_CONNECTED: break; case CONN_TEMPORARY: break; case CONN_PERMANENT: /* Oops. We cannot turn off active signatures */ if (ioctl(conn->mount_fid, NCP_IOC_SIGN_INIT, NULL)) result = EOPNOTSUPP; break; case CONN_KERNELBASED: /* Oops. We cannot turn off active signatures */ if (ioctl(conn->mount_fid, NCP_IOC_SIGN_INIT, NULL)) result = EOPNOTSUPP; break; } } return result; #endif #else return 0; #endif } long ncp_sign_start(struct ncp_conn *conn, const unsigned char *sign_root) { #ifdef SIGNATURES static const unsigned char init_last[16]= {0x01,0x23,0x45,0x67,0x89,0xab,0xcd,0xef, 0xfe,0xdc,0xba,0x98,0x76,0x54,0x32,0x10}; if (ncp_get_sign_wanted(conn)) { memcpy(conn->sign_data.sign_root, sign_root, 8); memcpy(conn->sign_data.sign_last, init_last, 16); conn->sign_active = 1; switch (conn->is_connected) { case NOT_CONNECTED: break; case CONN_TEMPORARY: break; case CONN_PERMANENT: #ifdef NCP_KERNEL_NCPFS_AVAILABLE if (ioctl(conn->mount_fid, NCP_IOC_SIGN_INIT, &conn->sign_data)) #endif return NWE_SIGNATURE_LEVEL_CONFLICT; break; case CONN_KERNELBASED: #ifdef NCP_KERNEL_NCPFS_AVAILABLE if (ioctl(conn->mount_fid, NCP_IOC_SIGN_INIT, &conn->sign_data)) #endif return NWE_SIGNATURE_LEVEL_CONFLICT; break; } } #endif return 0; } #ifdef NDS_SUPPORT long ncp_send_nds_frag(struct ncp_conn *conn, int ndsverb, const void *iinbuf, size_t inbuflen, void *ioutbuf, size_t outbufsize, size_t *outbuflen) { long result; size_t sizeleft, i; size_t maxdatasize = 514; int first = 1; int firstReply = 1; int fraghnd = -1; int32_t ndsCode = -399; size_t replyLen = 0; size_t fragLen; const nuint8* inbuf = iinbuf; nuint8* outbuf = ioutbuf; if (inbuflen && !inbuf) { return ERR_NULL_POINTER; } if (outbuflen) *outbuflen = 0; do { sizeleft = maxdatasize; ncp_init_request(conn); ncp_add_byte(conn, 2); ncp_add_dword_lh(conn, fraghnd); if (first) { ncp_add_dword_lh(conn, maxdatasize - 8); ncp_add_dword_lh(conn, inbuflen + 12); ncp_add_dword_lh(conn, 0); ncp_add_dword_lh(conn, ndsverb); ncp_add_dword_lh(conn, outbufsize); sizeleft -= 25; first = 0; } else sizeleft -= 5; i = (sizeleft > inbuflen) ? inbuflen : sizeleft; if (i) ncp_add_mem(conn, inbuf, i); inbuflen -= i; inbuf += i; if ((result = ncp_request(conn, 0x68)) != 0) { ncp_unlock_conn(conn); ncp_dprintf(_("Error in ncp_request\n")); return result; } fragLen = ncp_reply_dword_lh(conn, 0); if (fragLen < 4) { ncp_unlock_conn(conn); ncp_dprintf(_("Fragment too short\n")); return NWE_INVALID_NCP_PACKET_LENGTH; } fraghnd = ncp_reply_dword_lh(conn, 4); fragLen -= 4; if (fragLen) { int hdr; if (firstReply) { ndsCode = ncp_reply_dword_lh(conn, 8); hdr = 12; fragLen -= 4; firstReply = 0; } else { hdr = 8; } if (fragLen > outbufsize) { ncp_unlock_conn(conn); ncp_dprintf(_("Fragment too large, len=%d, max=%d\n"), fragLen, outbufsize); return NWE_BUFFER_OVERFLOW; } if (outbuf) { memcpy(outbuf, ncp_reply_data(conn, hdr), fragLen); outbuf += fragLen; } replyLen += fragLen; } else { /* if reply len == 0 then we must have something to transmit */ /* otherwise it can cause endless loop */ if ((fraghnd != -1) && (inbuflen == 0)) { ncp_unlock_conn(conn); ncp_dprintf(_("Why next fragment?\n")); return NWE_SERVER_FAILURE; } } ncp_unlock_conn(conn); if (fraghnd != -1) { ncp_dprintf(_("Fragmented\n")); } } while (fraghnd != -1); if (inbuflen || firstReply) { ncp_dprintf(_("InBufLen after request=%d, FirstReply=%d\n"), inbuflen, firstReply); return NWE_SERVER_FAILURE; } if (outbuflen) *outbuflen = replyLen; if (ndsCode != 0) { ncp_dprintf(_("NDS error %d\n"), ndsCode); if ((ndsCode < 0) && (ndsCode > -256)) return -ndsCode | NWE_SERVER_ERROR; else return ndsCode; } return 0; } long ncp_send_nds(struct ncp_conn *conn, int fn, const char *data_in, size_t data_in_len, char *data_out, size_t data_out_max, size_t *data_out_len) { size_t i; long err; ncp_init_request(conn); ncp_add_byte(conn, fn); if (data_in) ncp_add_mem(conn, data_in, data_in_len); if (!(err = ncp_request(conn, 0x68))) { i = conn->ncp_reply_size; if (i > data_out_max) i = data_out_max; if (data_out) memcpy(data_out, ncp_reply_data(conn, 0), i); if (data_out_len) *data_out_len = i; } else if (data_out_len) *data_out_len = 0; ncp_unlock_conn(conn); return err; } long ncp_change_conn_state(struct ncp_conn *conn, int new_state) { nuint8 ns[4]; DSET_LH(ns, 0, new_state); return NWRequestSimple(conn, NCPC_SFN(23,29), ns, 4, NULL); } #endif /* NDS_SUPPORT */ static int ncp_attach_by_addr_tran(const struct sockaddr *target, nuint transport, struct ncp_conn** result) { struct ncp_conn *conn; int err; if (!result) { return ERR_NULL_POINTER; } *result = NULL; conn = ncp_alloc_conn(); if (!conn) { return ENOMEM; } err = ncp_connect_addr(conn, target, 1, transport); if (err) { ncp_close(conn); return err; } *result = conn; return 0; } NWCCODE NWCCOpenConnBySockAddr(const struct sockaddr* tran, enum NET_ADDRESS_TYPE atran, nuint openState, nuint reserved, NWCONN_HANDLE* conn) { if (reserved != NWCC_RESERVED) return NWE_PARAM_INVALID; /* not yet supported */ if (openState & NWCC_OPEN_PUBLIC) return NWE_PARAM_INVALID; return ncp_attach_by_addr_tran(tran, atran, conn); } static int ncp_attach_by_addr(const struct sockaddr *target, struct ncp_conn** result) { nuint transport; if (!result || !target) { return ERR_NULL_POINTER; } switch (target->sa_family) { case AF_IPX: transport = NT_IPX; break; case AF_INET: if (getenv("NCP_OVER_TCP")) { transport = NT_TCP; } else { transport = NT_UDP; } break; case AF_UNIX: transport = NT_TCP; break; default: return EAFNOSUPPORT; } return ncp_attach_by_addr_tran(target, transport, result); } struct ncp_conn * ncp_open_addr(const struct sockaddr *target, long *err) { struct ncp_conn *conn; if (!err) { return NULL; } *err = ncp_attach_by_addr(target, &conn); return conn; } long ncp_get_broadcast_message(struct ncp_conn *conn, char message[256]) { long result; int length; if (!message) { return ERR_NULL_POINTER; } ncp_init_request_s(conn, 0x0B); if ((result = ncp_request(conn, 0x15)) != 0) { ncp_unlock_conn(conn); ncp_init_request_s(conn, 0x01); if ((result = ncp_request(conn, 0x15)) != 0) { ncp_unlock_conn(conn); return result; } } length = ncp_reply_byte(conn, 0); message[length] = 0; memcpy(message, ncp_reply_data(conn, 1), length); ncp_unlock_conn(conn); return 0; } int ncp_get_conn_type(struct ncp_conn* conn) { if (conn) { switch (conn->is_connected) { case CONN_PERMANENT: return NCP_CONN_PERMANENT; case CONN_TEMPORARY: return NCP_CONN_TEMPORARY; case CONN_KERNELBASED: return NCP_CONN_KERNELBASED; default:; } } return NCP_CONN_INVALID; } int ncp_get_conn_number(struct ncp_conn* conn) { int type; type = ncp_get_conn_type(conn); if (conn == NCP_CONN_INVALID) return -ENOTCONN; return conn->i.connection; } int ncp_get_fid(struct ncp_conn* conn) { if (ncp_get_conn_type(conn) != NCP_CONN_PERMANENT) return -1; return conn->mount_fid; } NWCCODE ncp_get_dentry_ttl(struct ncp_conn* conn, unsigned int* ttl) { #ifdef NCP_KERNEL_NCPFS_AVAILABLE int fd = ncp_get_fid(conn); u_int32_t kernel_ttl; if (fd == -1) return NWE_REQUESTER_FAILURE; if (ioctl(fd, NCP_IOC_GETDENTRYTTL, &kernel_ttl)) { if (errno != EINVAL) return errno; kernel_ttl = 0; /* old kernel... */ } if (ttl) *ttl = kernel_ttl; return 0; #else return NWE_REQUESTER_FAILURE; #endif } NWCCODE ncp_set_dentry_ttl(struct ncp_conn* conn, unsigned int ttl) { #ifdef NCP_KERNEL_NCPFS_AVAILABLE int fd = ncp_get_fid(conn); u_int32_t kernel_ttl; if (fd == -1) return NWE_REQUESTER_FAILURE; kernel_ttl = ttl; if (ioctl(fd, NCP_IOC_SETDENTRYTTL, &kernel_ttl)) return errno; return 0; #else return NWE_REQUESTER_FAILURE; #endif } static NWCCODE ncp_get_private_key_perm(struct ncp_conn* conn, void* pk, size_t* pk_len) { #ifdef NCP_KERNEL_NCPFS_AVAILABLE int fd = ncp_get_fid(conn); struct ncp_privatedata_ioctl npi; if (fd == -1) return NWE_REQUESTER_FAILURE; if (!pk_len) { return ERR_NULL_POINTER; } npi.data = pk; if (pk) npi.len = *pk_len; else npi.len = 0; if (ioctl(fd, NCP_IOC_GETPRIVATEDATA, &npi)) return errno; *pk_len = npi.len; return 0; #else return NWE_REQUESTER_FAILURE; #endif } static NWCCODE ncp_get_private_key_temp(struct ncp_conn* conn, void* pk, size_t* pk_len) { NWCCODE err = 0; ncp_lock_conn(conn); if (pk) { size_t maxln = *pk_len; if (maxln > conn->private_key_len) { maxln = conn->private_key_len; } memcpy(pk, conn->private_key, maxln); } *pk_len = conn->private_key_len; ncp_unlock_conn(conn); return err; } NWCCODE ncp_get_private_key(struct ncp_conn* conn, void* pk, size_t* pk_len) { switch (ncp_get_conn_type(conn)) { case NCP_CONN_TEMPORARY: return ncp_get_private_key_temp(conn, pk, pk_len); default: return ncp_get_private_key_perm(conn, pk, pk_len); } } static NWCCODE ncp_set_private_key_perm(struct ncp_conn* conn, const void* pk, size_t pk_len) { #ifdef NCP_KERNEL_NCPFS_AVAILABLE int fd = ncp_get_fid(conn); struct ncp_privatedata_ioctl npi; if (fd == -1) return NWE_REQUESTER_FAILURE; if (pk_len && !pk) return ERR_NULL_POINTER; npi.data = (void*)pk; npi.len = pk_len; if (ioctl(fd, NCP_IOC_SETPRIVATEDATA, &npi)) return errno; return 0; #else return NWE_REQUESTER_FAILURE; #endif } static NWCCODE ncp_set_private_key_temp(struct ncp_conn* conn, const void* pk, size_t pk_len) { void *keydata; void *oldkeydata; keydata = malloc(pk_len); if (!keydata) return ENOMEM; mlock(keydata, pk_len); memcpy(keydata, pk, pk_len); ncp_lock_conn(conn); oldkeydata = conn->private_key; conn->private_key = keydata; conn->private_key_len = pk_len; ncp_unlock_conn(conn); free(oldkeydata); return 0; } NWCCODE ncp_set_private_key(struct ncp_conn* conn, const void* pk, size_t pk_len) { switch (ncp_get_conn_type(conn)) { case NCP_CONN_TEMPORARY: return ncp_set_private_key_temp(conn, pk, pk_len); default: return ncp_set_private_key_perm(conn, pk, pk_len); } } NWCCODE ncp_next_conn(NWCONN_HANDLE conn, NWCONN_HANDLE* next_conn) { struct list_head* h; if (!next_conn) { return ERR_NULL_POINTER; } ncpt_mutex_lock(&conn_lock); for (h = conn ? conn->conn_ring.next : conn_list.next; h != &conn_list; h = h->next) { NWCONN_HANDLE c2 = list_entry(h, struct ncp_conn, conn_ring); if (c2->is_connected == NOT_CONNECTED) continue; if (ncpt_atomic_read(&c2->use_count)) { /* FIXME. use_count has to be redone - either with conditional store, or with mutex. Code never expected that library can return connection handle from internal list */ ncpt_atomic_inc(&c2->use_count); ncpt_mutex_unlock(&conn_lock); *next_conn = c2; return 0; } } ncpt_mutex_unlock(&conn_lock); return NWE_REQUESTER_FAILURE; } #ifdef NCP_DEBUG static FILE* ncp_do_FILE = NULL; static const char* ncp_do_filename = NULL; static size_t ncp_do_filename_len = 0; static pid_t ncp_do_lastpid = 0; static ncpt_mutex_t ncp_do_lock = NCPT_MUTEX_INITIALIZER; static void ncp_do_printf(const char* fmt, ...) { va_list ap; ncpt_mutex_lock(&ncp_do_lock); if (ncp_do_filename) { pid_t cpid; cpid = getpid(); if (cpid != ncp_do_lastpid) { char* nfn; if (ncp_do_FILE) { fclose(ncp_do_FILE); ncp_do_FILE = NULL; } nfn = alloca(ncp_do_filename_len + 20); memcpy(nfn, ncp_do_filename, ncp_do_filename_len); sprintf(nfn + ncp_do_filename_len, ".%u", cpid); ncp_do_FILE = fopen(nfn, "wt"); if (ncp_do_FILE) { ncp_do_lastpid = cpid; } } } if (ncp_do_FILE) { va_start(ap, fmt); vfprintf(ncp_do_FILE, fmt, ap); va_end(ap); } ncpt_mutex_unlock(&ncp_do_lock); } static void ncp_do_init(void) { ncp_do_FILE = stdout; if (getuid() == geteuid()) { const char* ncp_do; ncp_do = getenv("NCP_DEBUG_OUTPUT"); if (ncp_do) { ncp_do_filename_len = strlen(ncp_do); ncp_do_filename = ncp_do; } } } static void ncp_do_fini(void) { ncpt_mutex_lock(&ncp_do_lock); if (ncp_do_FILE && ncp_do_filename) { fclose(ncp_do_FILE); ncp_do_FILE = NULL; } ncpt_mutex_unlock(&ncp_do_lock); } static void ncp_debug_help(void) __attribute((noreturn)); static void ncp_debug_help(void) { fprintf(stderr, "Valid options for the NCP_DEBUG environment variable are:\n" "\n" " cleaup List NCP connections opened at exit\n" " all All options above\n" " help Display this help text\n" "\n" "To direct the debugging output into a file instead of standard output\n" "a filename can be specified using the NCP_DEBUG_OUTPUT environment variable.\n"); exit(248); } static void ncp_debug_usage(int len, const char* ncpdebug) { fprintf(stderr, "warning: NCP debug option `%*.*s\' unknown: try NCP_DEBUG=help\n", len, len, ncpdebug); } static int ncp_debug_show_cleanup = 0; static void ncp_init(void) __attribute__((constructor)); static void ncp_init(void) { const char* ncpdebug; size_t ln; ncpdebug = getenv("NCP_DEBUG"); if (!ncpdebug) { return; } while (*ncpdebug) { ln = strcspn(ncpdebug, ",:;"); if (ln) { if (ln == 4 && !memcmp(ncpdebug, "help", 4)) { ncp_debug_help(); } else if (ln == 7 && !memcmp(ncpdebug, "cleanup", 7)) { ncp_debug_show_cleanup = 1; } else if (ln == 3 && !memcmp(ncpdebug, "all", 3)) { ncp_debug_show_cleanup = 1; } else { ncp_debug_usage(ln, ncpdebug); } } ncpdebug += ln; ln = strspn(ncpdebug, ",:;"); ncpdebug += ln; } ncp_do_init(); } static void ncp_fini(void) __attribute__((destructor)); static void ncp_fini(void) { struct list_head* h; unsigned int cnt = 0; if (ncp_debug_show_cleanup) { ncpt_mutex_lock(&conn_lock); for (h = conn_list.next; h != &conn_list; h = h->next) { char fsname[NCP_BINDERY_NAME_LEN + 1]; NWCCODE err; NWCONN_HANDLE c2 = list_entry(h, struct ncp_conn, conn_ring); ncp_do_printf("Connection %p: store count=%u, use count=%u\n", c2, ncpt_atomic_read(&c2->store_count), ncpt_atomic_read(&c2->use_count)); ncp_do_printf(" NDS connection: %p\n", c2->nds_conn); ncp_do_printf(" User: %s\n", c2->user); err = NWGetFileServerName(c2, fsname); if (err) sprintf(fsname, "<%s>", strnwerror(err)); ncp_do_printf(" Server name: %s\n", fsname); cnt++; } ncpt_mutex_unlock(&conn_lock); ncp_do_printf("Found %u connections alive on exit!\n", cnt); } ncp_do_fini(); } #endif