diff --git a/.downloads/ncpfs-2.0.7.tgz b/.downloads/ncpfs-2.0.7.tgz new file mode 100644 index 0000000..5ddb7a0 Binary files /dev/null and b/.downloads/ncpfs-2.0.7.tgz differ diff --git a/Changes b/Changes index 823a054..ac367b8 100644 --- a/Changes +++ b/Changes @@ -1,6 +1,14 @@ I only began this file with ncpfs-0.12. If you're interested in older versions, you can find them on ftp.gwdg.de:/pub/linux/misc/ncpfs/old. +ncpfs-2.0.6 -> ncpfs-2.0.7 +- Hopefully removed one security problem in ncpumount. +- Added command line flag to pserver.c +- Heavily reconstructed ncpfs utils. Created the lib/ dir. +- Separated the uid utils in sutil +- Add nwsfind to enable users to use ncpfs safely without setting + the utils setuid root. + ncpfs-2.0.5 -> ncpfs-2.0.6 - Added a short description of a problem that I need help with to the file BUGS. If you know a bit of the linux networking code, please diff --git a/FAQ b/FAQ index 904c800..8a3d947 100644 --- a/FAQ +++ b/FAQ @@ -1,5 +1,5 @@ There is certainly not enough material to call this an FAQ, but some -questions reach me regularly. Probably the documenation is not clear +questions reach me regularly. Probably the documentation is not clear enough. ------------------------------------------------------------------------------- diff --git a/Makefile b/Makefile index ab8fba4..3f6f699 100644 --- a/Makefile +++ b/Makefile @@ -2,7 +2,7 @@ # Makefile for the linux ncp-filesystem routines. # -VERSION = 2.0.6 +VERSION = 2.0.7 # If you are using kerneld to autoload ncp support, # uncomment this (kerneld is in linux since about 1.3.57): @@ -11,33 +11,39 @@ VERSION = 2.0.6 # If your system is ELF, either also do a 'make install', or append the util/ # directory where the dynamic library resides to the environment # variable LD_LIBRARY_PATH -HAVE_ELF=$(shell file `which gcc`|grep ELF >/dev/null && echo -n yes ) - +HAVE_ELF=$(shell file `whereis gcc|cut -d ' ' -f 2`| \ + grep ELF >/dev/null && echo -n yes ) TOPDIR = $(shell pwd) -BINDIR = /usr/local/bin +BINDIR = /usr/bin SBINDIR = /sbin -INTERM_BINDIR = $(TOPDIR)/bin -SUBDIRS = util ipx-1.0 man +SUBDIRS = lib sutil util ipx-1.0 man KVERSION=$(shell uname -r | cut -b1-3) +INCLUDES=-I$(TOPDIR)/include + ifeq ($(KVERSION),1.2) SUBDIRS += kernel-1.2/src -INCLUDES = -I$(TOPDIR)/kernel-1.2 +INCLUDES += -I$(TOPDIR)/kernel-1.2 endif -export INCLUDES BINDIR INTERM_BINDIR SBINDIR KERNELD VERSION HAVE_ELF +CFLAGS = -Wall $(INCLUDES) $(KERNELD) -DNCPFS_VERSION=\"$(VERSION)\" + +#CFLAGS += -g +CFLAGS += -O2 + +export INCLUDES BINDIR SBINDIR KERNELD VERSION HAVE_ELF CFLAGS all: for i in $(SUBDIRS); do make -C $$i all; done @if [ "$(HAVE_ELF)" = yes ] ;\ then \ echo ; echo ; echo ;\ - echo Please add \'`pwd`/util\' to the environment ; \ + echo Please add \'`pwd`/lib\' to the environment ; \ echo variable LD_LIBRARY_PATH by executing ; \ echo ;\ - echo export LD_LIBRARY_PATH=\"\$$LD_LIBRARY_PATH:`pwd`/util\" ; \ + echo export LD_LIBRARY_PATH=\"\$$LD_LIBRARY_PATH:`pwd`/lib\" ; \ echo ;\ echo or do a \'make install\'. ;\ echo ;\ @@ -49,17 +55,16 @@ dep: install: for i in $(SUBDIRS); do make -C $$i install; done -clean: - rm -f `find . -type f -name '*.o' -print` - rm -f `find . -type f -name '*~' -print` - rm -f `find . -type f -name '.depend' -print` - rm -f `find . -type f -name '*.out' -print` +clean_me: + rm -f `find -name '*.out'` + rm -f `find -name '*~'` + rm -f ncpfs.tgz + +clean: clean_me for i in $(SUBDIRS); do make -C $$i clean; done -mrproper: clean - rm -fr $(INTERM_BINDIR)/* ncpfs.tgz - make -C util mrproper - make -C ipxdump mrproper +mrproper: clean_me + for i in $(SUBDIRS) ipxdump; do make -C $$i mrproper; done modules: ncpfs.o diff --git a/TODO b/TODO index d3663bf..b5e4d5c 100644 --- a/TODO +++ b/TODO @@ -1,14 +1,11 @@ Here's a list of things I want to do. Feel free to send suggestions, or even help me ;-). -- Add flags to pserver's command line, so that the print command can - find out the name of user who printed the job. - - do rtt estimation, like tcp does. - Do better connection management. I imagine to create a ncpd. -- When ncp is done, one can think about mounting several volumes over +- When ncpd is done, one can think about mounting several volumes over a single NCP connection. This should make the trade-off mentioned in ncpmount.8 unnecessary. diff --git a/util/nwbpsecurity b/bin/nwbpsecurity similarity index 100% rename from util/nwbpsecurity rename to bin/nwbpsecurity diff --git a/util/start_ipx b/bin/start_ipx similarity index 100% rename from util/start_ipx rename to bin/start_ipx diff --git a/util/com_err/com_err.h b/include/com_err.h similarity index 100% rename from util/com_err/com_err.h rename to include/com_err.h diff --git a/util/ipxlib.h b/include/ipxlib.h similarity index 100% rename from util/ipxlib.h rename to include/ipxlib.h diff --git a/util/ncplib.h b/include/ncplib.h similarity index 99% rename from util/ncplib.h rename to include/ncplib.h index 0179b24..3fc078e 100644 --- a/util/ncplib.h +++ b/include/ncplib.h @@ -121,6 +121,10 @@ ncp_find_permanent(const struct ncp_conn_spec *spec); struct sockaddr_ipx * ncp_find_fileserver(const char *server_name, long *err); +/* Find the address of a server */ +struct sockaddr_ipx * +ncp_find_server(const char **server_name, int type, long *err); + /* Detach from a permanent connection or destroy a temporary connection */ long diff --git a/ipx-1.0/Gregs.Makefile b/ipx-1.0/Gregs.Makefile deleted file mode 100644 index ca4e086..0000000 --- a/ipx-1.0/Gregs.Makefile +++ /dev/null @@ -1,24 +0,0 @@ -CFLAGS = -O2 -Wall -UTILS = ipx_configure ipx_interface ipx_internal_net ipx_route -all: $(UTILS) - -clean: - rm -f $(UTILS) *.o rip sap ipxrcv ipxsend - -install: $(UTILS) - for i in $(UTILS); \ - do \ - install --strip $$i /sbin; \ - install $$i.8 /usr/man/man8; \ - done - install init.ipx /etc/rc.d/init.d/ipx - install -m 0644 config.ipx /etc/sysconfig/ipx - rm -f /etc/rc.d/rc2.d/S15ipx - ln -sf /etc/rc.d/init.d/ipx /etc/rc.d/rc2.d/S15ipx - rm -f /etc/rc.d/rc3.d/S15ipx - ln -sf /etc/rc.d/init.d/ipx /etc/rc.d/rc3.d/S15ipx - rm -f /etc/rc.d/rc5.d/S15ipx - ln -sf /etc/rc.d/init.d/ipx /etc/rc.d/rc5.d/S15ipx - rm -f /etc/rc.d/rc6.d/K55ipx - ln -sf /etc/rc.d/init.d/ipx /etc/rc.d/rc6.d/K55ipx - diff --git a/ipx-1.0/Makefile b/ipx-1.0/Makefile index 47b4a18..c12bf8d 100644 --- a/ipx-1.0/Makefile +++ b/ipx-1.0/Makefile @@ -1,30 +1,19 @@ CFLAGS = -O2 -Wall -UTILS = $(INTERM_BINDIR)/ipx_configure $(INTERM_BINDIR)/ipx_interface \ - $(INTERM_BINDIR)/ipx_internal_net $(INTERM_BINDIR)/ipx_route +UTILS = ipx_configure ipx_interface ipx_internal_net ipx_route all: $(UTILS) -$(INTERM_BINDIR)/ipx_configure: ipx_configure.o - $(CC) -o $(INTERM_BINDIR)/ipx_configure ipx_configure.o - -$(INTERM_BINDIR)/ipx_interface: ipx_interface.o - $(CC) -o $(INTERM_BINDIR)/ipx_interface ipx_interface.o - -$(INTERM_BINDIR)/ipx_internal_net: ipx_internal_net.o - $(CC) -o $(INTERM_BINDIR)/ipx_internal_net ipx_internal_net.o - -$(INTERM_BINDIR)/ipx_route: ipx_route.o - $(CC) -o $(INTERM_BINDIR)/ipx_route ipx_route.o - dep: $(CPP) -M $(INCLUDES) *.c > .depend clean: rm -f $(UTILS) *.o rip sap ipxrcv ipxsend +mrproper: clean + rm -f .depend + install: $(UTILS) for i in $(UTILS); \ do \ install $$i $(BINDIR); \ done - diff --git a/lib/Makefile b/lib/Makefile new file mode 100644 index 0000000..fa86697 --- /dev/null +++ b/lib/Makefile @@ -0,0 +1,76 @@ +# +# Makefile for the linux ncp-filesystem routines. +# + +CC = gcc + +ifeq ($(HAVE_ELF),yes) +PIC_FLAG = -fPIC +NCP_LIB = libncp.so.1.0 +LIB_LINK_COMMAND = gcc -shared -Wl,-soname,libncp.so.1 -o $(NCP_LIB) +INSTALL_LIB = install $(NCP_LIB) -m 755 /lib; \ + ln -sf $(NCP_LIB) /lib/libncp.so.1; \ + ldconfig +export PIC_FLAG +else +NCP_LIB = libncp.a +LIB_LINK_COMMAND = ar r libncp.a +endif + +CFLAGS += $(PIC_FLAG) + +default: + make -C .. + +all: libcom_err.a ncplib_err.o $(NCP_LIB) + +install: + $(INSTALL_LIB) + +ncplib.o: ncplib.c ncplib_err.h + $(CC) $(CFLAGS) -finline-functions -c ncplib.c + +COM_ERR_CFILES = com_err/com_err.c com_err/error_message.c com_err/et_name.c \ + com_err/init_et.c +COM_ERR_OFILES = com_err/com_err.o com_err/error_message.o com_err/et_name.o \ + com_err/init_et.o + +libcom_err.a: $(COM_ERR_CFILES) + make -C com_err + +$(NCP_LIB): ncplib.o ncplib_err.o libcom_err.a + $(LIB_LINK_COMMAND) ncplib.o ncplib_err.o $(COM_ERR_OFILES) + ln -sf libncp.so.1.0 libncp.so.1 + ln -sf libncp.so.1 libncp.so + export LD_LIBRARY_PATH=`pwd`:LD_LIBRARY_PATH + +ncplib_err.o: ncplib_err.h ncplib_err.c + $(CC) $(CFLAGS) -c ncplib_err.c + +ncplib_err.h: ncplib_err.et + com_err/compile_et ncplib_err + ln -sf ../lib/ncplib_err.h ../include/ncplib_err.h + +ncplib_err.c: ncplib_err.et + com_err/compile_et ncplib_err + +dep: ncplib_err.h + make -C com_err dep + $(CPP) -M $(INCLUDES) *.c > .depend + +clean: + make -C com_err clean + rm -f *.o *~ ncplib_err.[ch] ../include/ncplib_err.h + rm -f libncp.* + +mrproper: clean + make -C com_err mrproper + rm -f $(UTILS) .depend $(DISTFILE) + +# +# include a dependency file if one exists +# +ifeq (.depend,$(wildcard .depend)) +include .depend +endif + diff --git a/util/com_err/ChangeLog b/lib/com_err/ChangeLog similarity index 100% rename from util/com_err/ChangeLog rename to lib/com_err/ChangeLog diff --git a/util/com_err/Makefile b/lib/com_err/Makefile similarity index 74% rename from util/com_err/Makefile rename to lib/com_err/Makefile index 271ce59..eeafb15 100644 --- a/util/com_err/Makefile +++ b/lib/com_err/Makefile @@ -5,13 +5,16 @@ OBJECTS = com_err.o error_message.o et_name.o init_et.o CFLAGS = -Wall -O2 $(PIC_FLAG) -all: $(OBJECTS) +all: ../libcom_err.a + +../libcom_err.a: $(OBJECTS) + ar r ../libcom_err.a $(OBJECTS) dep: $(CPP) -M $(INCLUDES) *.c > .depend clean: - rm -f *.o + rm -f *.o ../libcom_err.a mrproper: clean rm -f .depend diff --git a/util/com_err/com_err.3 b/lib/com_err/com_err.3 similarity index 100% rename from util/com_err/com_err.3 rename to lib/com_err/com_err.3 diff --git a/util/com_err/com_err.c b/lib/com_err/com_err.c similarity index 100% rename from util/com_err/com_err.c rename to lib/com_err/com_err.c diff --git a/lib/com_err/com_err.h b/lib/com_err/com_err.h new file mode 100644 index 0000000..f28dce8 --- /dev/null +++ b/lib/com_err/com_err.h @@ -0,0 +1,40 @@ +/* + * Header file for common error description library. + * + * Copyright 1988, Student Information Processing Board of the + * Massachusetts Institute of Technology. + * + * For copyright and distribution info, see the documentation supplied + * with this package. + */ + +#ifndef __COM_ERR_H + +typedef long errcode_t; + +#ifdef __STDC__ +#include + +/* ANSI C -- use prototypes etc */ +void com_err (const char *, long, const char *, ...); +void com_err_va (const char *whoami, errcode_t code, const char *fmt, + va_list args); +char const *error_message (long); +extern void (*com_err_hook) (const char *, long, const char *, va_list); +void (*set_com_err_hook (void (*) (const char *, long, const char *, va_list))) + (const char *, long, const char *, va_list); +void (*reset_com_err_hook (void)) (const char *, long, const char *, va_list); +int init_error_table(const char * const *msgs, int base, int count); +#else +/* no prototypes */ +void com_err (); +void com_err_va (); +char *error_message (); +extern void (*com_err_hook) (); +void (*set_com_err_hook ()) (); +void (*reset_com_err_hook ()) (); +int init_error_table(); +#endif + +#define __COM_ERR_H +#endif /* ! defined(__COM_ERR_H) */ diff --git a/util/com_err/com_err.texinfo b/lib/com_err/com_err.texinfo similarity index 100% rename from util/com_err/com_err.texinfo rename to lib/com_err/com_err.texinfo diff --git a/util/com_err/compile_et b/lib/com_err/compile_et similarity index 100% rename from util/com_err/compile_et rename to lib/com_err/compile_et diff --git a/util/com_err/compile_et.1 b/lib/com_err/compile_et.1 similarity index 100% rename from util/com_err/compile_et.1 rename to lib/com_err/compile_et.1 diff --git a/util/com_err/error_message.c b/lib/com_err/error_message.c similarity index 100% rename from util/com_err/error_message.c rename to lib/com_err/error_message.c diff --git a/util/com_err/error_table.h b/lib/com_err/error_table.h similarity index 100% rename from util/com_err/error_table.h rename to lib/com_err/error_table.h diff --git a/util/com_err/et_c.awk b/lib/com_err/et_c.awk similarity index 100% rename from util/com_err/et_c.awk rename to lib/com_err/et_c.awk diff --git a/util/com_err/et_h.awk b/lib/com_err/et_h.awk similarity index 100% rename from util/com_err/et_h.awk rename to lib/com_err/et_h.awk diff --git a/util/com_err/et_name.c b/lib/com_err/et_name.c similarity index 100% rename from util/com_err/et_name.c rename to lib/com_err/et_name.c diff --git a/util/com_err/init_et.c b/lib/com_err/init_et.c similarity index 100% rename from util/com_err/init_et.c rename to lib/com_err/init_et.c diff --git a/util/com_err/internal.h b/lib/com_err/internal.h similarity index 100% rename from util/com_err/internal.h rename to lib/com_err/internal.h diff --git a/util/com_err/mit-sipb-copyright.h b/lib/com_err/mit-sipb-copyright.h similarity index 100% rename from util/com_err/mit-sipb-copyright.h rename to lib/com_err/mit-sipb-copyright.h diff --git a/util/ipx_sap_types b/lib/ipx_sap_types similarity index 100% rename from util/ipx_sap_types rename to lib/ipx_sap_types diff --git a/util/ncplib.c b/lib/ncplib.c similarity index 91% rename from util/ncplib.c rename to lib/ncplib.c index 10bc8ba..f152e20 100644 --- a/util/ncplib.c +++ b/lib/ncplib.c @@ -161,6 +161,43 @@ ipx_sscanf_node(char *buf, unsigned char node[6]) return 6; } +static int +ipx_sscanf_saddr(char *buf, struct sockaddr_ipx *target) +{ + char *p; + struct sockaddr_ipx addr; + + addr.sipx_family = AF_IPX; + addr.sipx_type = NCP_PTYPE; + + if (sscanf(buf, "%lx", &addr.sipx_network) != 1) + { + return 1; + } + addr.sipx_network = htonl(addr.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; +} + void ipx_assign_node(IPXNode dest, IPXNode src) { @@ -222,224 +259,6 @@ ipx_recv(int sock, void *buf, int len, unsigned int flags, int timeout, timeout, err); } -static long -ipx_sap_find_nearest(int server_type, struct sockaddr_ipx *result, - char server_name[NCP_BINDERY_NAME_LEN]) -{ - struct sockaddr_ipx addr; - char data[1024]; - int sock; - int opt; - int packets; - int len; - - struct sap_server_ident *ident; - - if ((sock = socket(AF_IPX,SOCK_DGRAM,PF_IPX)) < 0) - { - if (errno == EINVAL) - { - return NCPL_ET_NO_IPX; - } - return errno; - } - - opt=1; - /* Permit broadcast output */ - if(setsockopt(sock,SOL_SOCKET,SO_BROADCAST, &opt,sizeof(opt))==-1) - { - goto finished; - } - - memzero(addr); - addr.sipx_family = AF_IPX; - addr.sipx_network = htonl(0x0); - addr.sipx_port = htons(0x0); - addr.sipx_type = IPX_SAP_PTYPE; - - if(bind(sock,(struct sockaddr*)&addr,sizeof(addr))==-1) - { - if (errno == EADDRNOTAVAIL) - { - errno = NCPL_ET_NO_INTERFACE; - } - goto finished; - } - - *(unsigned short *)data = htons(IPX_SAP_NEAREST_QUERY); - *(unsigned short *)&(data[2]) = htons(server_type); - - memzero(addr); - addr.sipx_family = AF_IPX; - addr.sipx_port = htons(IPX_SAP_PORT); - addr.sipx_type = IPX_SAP_PTYPE; - addr.sipx_network = htonl(0x0); - ipx_assign_node(addr.sipx_node, IPX_BROADCAST_NODE); - - if (sendto(sock, data, 4, 0, - (struct sockaddr *)&addr, sizeof(addr)) < 0) - { - goto finished; - } - - packets = 5; - do - { - long err; - len = ipx_recv(sock, data, 1024, 0, 1, &err); - if (len < 66) - { - packets = packets - 1; - continue; - } - } - while ( (ntohs(*((__u16 *)data)) != IPX_SAP_NEAREST_RESPONSE) - && (packets > 0)); - - if (packets == 0) - { - close(sock); - return NCPL_ET_NO_SERVER; - } - - ident = (struct sap_server_ident *)(data+2); - - result->sipx_family = AF_IPX; - result->sipx_network = ident->server_network; - result->sipx_port = ident->server_port; - ipx_assign_node(result->sipx_node, ident->server_node); - - memcpy(server_name, ident->server_name, sizeof(ident->server_name)); - - errno = 0; - - finished: - close(sock); - return errno; -} - -static int -ipx_make_reachable(IPXNet 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; - int addrlen; - int sock; - int opt; - int res=-1; - int i; - int packets; - - if (geteuid() != 0) - { - errno = EPERM; - return -1; - } - - memzero(rip); - - sock = socket(AF_IPX, SOCK_DGRAM, PF_IPX); - - if (sock == -1) - { - if (errno == EINVAL) - { - return NCPL_ET_NO_IPX; - } - return errno; - } - - opt=1; - /* Permit broadcast output */ - if (setsockopt(sock, SOL_SOCKET, SO_BROADCAST, &opt, sizeof(opt)) != 0) - { - goto finished; - } - - memzero(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 = htonl(network); - - if (sendto(sock, &rip, sizeof(rip), 0, - (struct sockaddr *)&addr, sizeof(addr)) < 0) - { - goto finished; - } - - packets = 3; - do - { - long err; - int len; - - if (packets == 0) - { - goto finished; - } - - addrlen = sizeof(struct sockaddr_ipx); - - len = ipx_recvfrom(sock, &rip, sizeof(rip),0, sr, &addrlen, 1, - &err); - - if (len < sizeof(rip)) - { - packets = packets - 1; - continue; - } - } - while (ntohs(rip.operation) != IPX_RIP_RESPONSE); - - if (rip.rt[0].network != htonl(network)) - { - goto finished; - } - - rt_def.rt_flags = RTF_GATEWAY; - st->sipx_network = htonl(network); - 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 != 0) - { - errno = ENETUNREACH; - } - return res; -} - static int install_wdog(struct ncp_conn *conn) { @@ -780,13 +599,8 @@ ncp_connect_addr(struct ncp_conn *conn, const struct sockaddr_ipx *target, if ((err = do_ncp_call(conn, sizeof(*h))) != 0) { - if ( (err != ENETUNREACH) - || (ipx_make_reachable(htonl(target->sipx_network)) != 0) - || ((err = do_ncp_call(conn, sizeof(*h))) != 0)) - { - close(ncp_sock); close(wdog_sock); - return err; - } + close(ncp_sock); close(wdog_sock); + return err; } if (wdog_needed != 0) @@ -817,103 +631,82 @@ ncp_connect_addr(struct ncp_conn *conn, const struct sockaddr_ipx *target, static long ncp_connect_any(struct ncp_conn *conn, int wdog_needed) { - struct sockaddr_ipx addr; - char name[NCP_BINDERY_NAME_LEN]; + struct sockaddr_ipx *addr; long result; + const char *server = NULL; + long err; - if ((result = ipx_sap_find_nearest(IPX_SAP_FILE_SERVER, - &addr, name)) != 0) + if ((addr = ncp_find_server(&server,NCP_BINDERY_FSERVER,&err)) == NULL) + { + return err; + } + + if ((result = ncp_connect_addr(conn, addr, wdog_needed)) != 0) { return result; } - - if ((result = ncp_connect_addr(conn, &addr, wdog_needed)) != 0) - { - return result; - } - strcpy(conn->server, name); + strcpy(conn->server, server); return 0; } struct sockaddr_ipx * ncp_find_fileserver(const char *server_name, long *err) { - char server[NCP_BINDERY_NAME_LEN]; - char nearest[NCP_BINDERY_NAME_LEN]; - struct nw_property prop; - struct prop_net_address *n_addr = (struct prop_net_address *)∝ - struct ncp_conn conn; + return ncp_find_server(&server_name, NCP_BINDERY_FSERVER, err); +} +struct sockaddr_ipx * +ncp_find_server(const char **server_name, int type, long *err) +{ + char command[256]; + char buf[128]; + char server[NCP_BINDERY_NAME_LEN+1]; static struct sockaddr_ipx result; + FILE *p; + int res; + char *n; - initialize_NCPL_error_table(); - if (strlen(server_name) >= sizeof(server)) + memset(server, 0, sizeof(server)); + + if (*server_name != NULL) { - *err = NCPL_ET_NAMETOOLONG; + strncpy(server, *server_name, sizeof(server)-1); + str_upper(server); + } + + sprintf(command, "nwsfind -t %d %s", type, server); + p = popen(command, "r"); + + if (p == NULL) + { + *err = errno; return NULL; } - strcpy(server, server_name); - str_upper(server); + fgets(buf, sizeof(buf), p); - if ((*err = ipx_sap_find_nearest(IPX_SAP_FILE_SERVER, - &result, nearest)) != 0) + if (buf[strlen(buf)] - 1 == '\n'); { + buf[strlen(buf)] = '\0'; + } + + if (((res = pclose(p)) != 0) || (ipx_sscanf_saddr(buf, &result) != 0)) + { + *err = (*server_name != NULL) + ? NCPL_ET_HOST_UNKNOWN : NCPL_ET_NO_SERVER; return NULL; } - /* We have to ask the nearest server for our wanted server */ - - memzero(conn); - if ((*err = ncp_connect_addr(&conn, &result, 0)) != 0) + if (*server_name == NULL) { - return NULL; + if ((n = strchr(buf, ' ')) == NULL) + { + *err = NCPL_ET_HOST_UNKNOWN; + return NULL; + } + *server_name = n; } - /* The following optimization should have been done before - ncp_connect_addr. This would be convenient if there was a - simple way to find out whether there is a route to the - server. Parsing /proc/net/ipx_route is not too nice, so we - just connect to the server and immediately disconnect - again. This way we also find out if the server still has - free connection slots. */ - - if (strcmp(server, nearest) == 0) - { - /* Our wanted server answered the SAP GNS request, so - use it */ - ncp_do_close(&conn); - errno = 0; - return &result; - } - - if (ncp_read_property_value(&conn, NCP_BINDERY_FSERVER, server, 1, - "NET_ADDRESS", &prop) != 0) - { - ncp_do_close(&conn); - *err = NCPL_ET_HOST_UNKNOWN; - return NULL; - } - - if ((*err = ncp_do_close(&conn)) != 0) - { - return NULL; - } - - result.sipx_family = AF_IPX; - result.sipx_network = n_addr->network; - result.sipx_port = n_addr->port; - ipx_assign_node(result.sipx_node, n_addr->node); - - /* To make the final server reachable, we connect again. See - above. (When can we rely on all users running ipxd??? :-)) */ - memzero(conn); - if ( ((*err = ncp_connect_addr(&conn, &result, 0)) != 0) - || ((*err = ncp_do_close(&conn)) != 0)) - { - return NULL; - } - return &result; } diff --git a/util/ncplib_err.et b/lib/ncplib_err.et similarity index 100% rename from util/ncplib_err.et rename to lib/ncplib_err.et diff --git a/util/nwcrypt.c b/lib/nwcrypt.c similarity index 100% rename from util/nwcrypt.c rename to lib/nwcrypt.c diff --git a/man/ncopy.1 b/man/ncopy.1 index b1b99c6..bddb22b 100644 --- a/man/ncopy.1 +++ b/man/ncopy.1 @@ -90,7 +90,7 @@ option to .B ncpmount(8), ncpumount(8) .SH CREDITS -ncopy was written by Brian G. Reid (breid@tim.com) and -Tom C. Henderson (thenderson@tim.com). -Many thanks to Volker Lendecke (lendecke@namu01.gwdg.de) for the ncpfs -and ncplib which made ncopy possible. +ncopy was written by Brian G. Reid (breid@tim.com) and Tom +C. Henderson (thenderson@tim.com). Many thanks to Volker Lendecke +(lendecke@math.uni-goettingen.de) for the ncpfs and ncplib which made +ncopy possible. diff --git a/man/ncpmount.8 b/man/ncpmount.8 index 30b15c5..afa1d18 100644 --- a/man/ncpmount.8 +++ b/man/ncpmount.8 @@ -291,5 +291,5 @@ The encryption code was taken from Dr. Dobbs's Journal 11/93. There Pawel Szczerbina described it in an article on NCP. The ncpfs code was initially hacked from smbfs by Volker Lendecke -(lendecke@namu01.gwdg.de). smbfs was put together by Paal-Kr. Engstad +(lendecke@math.uni-goettingen.de). smbfs was put together by Paal-Kr. Engstad (pke@engstad.ingok.hitos.no) and later polished by Volker. diff --git a/man/nprint.1 b/man/nprint.1 index 850d37a..c583cf8 100644 --- a/man/nprint.1 +++ b/man/nprint.1 @@ -219,4 +219,4 @@ printed if a printer operator has put in the correct form. .B nwclient(5), slist(1), pqlist(1), ncpmount(8), ncpumount(8) .SH CREDITS -nprint was written by Volker Lendecke (lendecke@namu01.gwdg.de) +nprint was written by Volker Lendecke (lendecke@math.uni-goettingen.de) diff --git a/man/nwauth.1 b/man/nwauth.1 new file mode 100644 index 0000000..d1b5adb --- /dev/null +++ b/man/nwauth.1 @@ -0,0 +1,69 @@ +.TH NWAUTH 1 10/27/1996 nwauth nwauth +.SH NAME +nwauth \- Verify username/password +.SH SYNOPSIS +.B nwauth +[ +.B -h +] [ +.B -S +.I server +] [ +.B -U +.I user name +] [ +.B -P +.I password + | +.B -n +] +.SH DESCRIPTION +.B nwauth +does nothing but logging into a NetWare server. If the login was +successful, an error code of 0 is returned. If the login was not +successful, an error code of 1 is returned. It was designed for +use by other programs who want authenticate users via a NetWare +server. + +nwauth +.B always +expects a password on stdin. If the stdin is a tty, then the user is +prompted for a password. Otherwise nwauth simply reads stdin for a +password. + +.B nwauth +looks up the file +.I $HOME/.nwclient +to find a file server and a user name. See nwclient(5) for more +information. Please note that the access permissions of .nwclient MUST +be 600, for security reasons. + +.SH OPTIONS + +.B -h +.RS 3 +.B -h +is used to print a short help text. +.RE + +.B -S +.I server +.RS 3 +.B server +is the name of the server you want to use. +.RE + +.B -U +.I user name +.RS 3 +If the user name your NetWare administrator gave to you differs +from your unix user-id, you should use +.B -U +to tell the server about your NetWare user name. +.RE + +.SH SEE ALSO +.B nwclient(5) + +.SH CREDITS +nwauth was written by Volker Lendecke (lendecke@math.uni-goettingen.de) diff --git a/man/nwsfind.1 b/man/nwsfind.1 new file mode 100644 index 0000000..f559eec --- /dev/null +++ b/man/nwsfind.1 @@ -0,0 +1,36 @@ +.TH NWSFIND 1 10/27/1996 nwsfind nwsfind +.SH NAME +nwsfind \- Find a NetWare Server +.SH SYNOPSIS +.B nwsfind +[ +.B -t +.I type +] [ +.I name +] +.SH DESCRIPTION +.B nwsfind +searches for a NetWare server and finds a route to this +server. nwsfind was written to be setuid root. It is called from +within the ncp library, so that it is possible that normal users use +the utilities. + +.SH OPTIONS +.B name +.RS 3 +If you look for a specific server, you should give nwsfind this +argument. If you omit it, nwsfind looks for the server nearest to you. +.RE + +.B -t +.I type +.RS 3 +By default nwsfind looks for file servers. In case you want to look up +another server type, you have to specify its numerical type as +.B type. +.RE + + +.SH CREDITS +nwsfind was written by Volker Lendecke (lendecke@math.uni-goettingen.de) diff --git a/man/pqlist.1 b/man/pqlist.1 index 5779a8c..3334eb1 100644 --- a/man/pqlist.1 +++ b/man/pqlist.1 @@ -102,4 +102,4 @@ this conversion by .B nwclient(5), nprint(1), slist(1), ncpmount(8), ncpumount(8) .SH CREDITS -pqlist was written by Volker Lendecke (lendecke@namu01.gwdg.de) +pqlist was written by Volker Lendecke (lendecke@math.uni-goettingen.de) diff --git a/man/pserver.1 b/man/pserver.1 index de39c7b..0cb3dd2 100644 --- a/man/pserver.1 +++ b/man/pserver.1 @@ -1,4 +1,4 @@ -.TH PSERVER 1 02/10/1996 pserver pserver +.TH PSERVER 1 10/22/1996 pserver pserver .SH NAME pserver \- NetWare print server .SH SYNOPSIS @@ -101,6 +101,13 @@ process, and feeds the job file to stdin. .I command is the printing command that is executed for each job. The default command is 'lpr'. + +You can insert several flags into the command, preceded by %. These +are replaced with values retrieved from the queue structure for the +print job. + +%u: This field will be replaced by the name of the user who posted +this print job. .RE .B -j @@ -139,4 +146,4 @@ diagnostic messages that are printed when a error occurs. .B nwclient(5), slist(1), pqlist(1), ncpmount(8), ncpumount(8) .SH CREDITS -pserver was written by Volker Lendecke (lendecke@namu01.gwdg.de) +pserver was written by Volker Lendecke (lendecke@math.uni-goettingen.de) diff --git a/man/slist.1 b/man/slist.1 index 13065a6..0c3e28e 100644 --- a/man/slist.1 +++ b/man/slist.1 @@ -40,4 +40,4 @@ List all available Netware servers on your Network, that begin with an "I". .B ncpmount(8), ncpumount(8), pqlist(1), nprint(1) .SH CREDITS -slist was written by Volker Lendecke (lendecke@namu01.gwdg.de) +slist was written by Volker Lendecke (lendecke@math.uni-goettingen.de) diff --git a/ncpfs-2.0.6.lsm b/ncpfs-2.0.7.lsm similarity index 83% rename from ncpfs-2.0.6.lsm rename to ncpfs-2.0.7.lsm index d930a5a..2cd938c 100644 --- a/ncpfs-2.0.6.lsm +++ b/ncpfs-2.0.7.lsm @@ -1,7 +1,7 @@ Begin3 Title: ncpfs -Version: 2.0.6 -Entered-date: 08. August 1996 +Version: 2.0.7 +Entered-date: 27. October 1996 Description: With ncpfs you can mount volumes of your netware server under Linux. You can also print to netware print queues and spool netware print queues to the @@ -13,7 +13,7 @@ Author: lendecke@namu01.Num.Math.Uni-Goettingen.de (Volker Lendecke) Maintained-by: lendecke@namu01.Num.Math.Uni-Goettingen.de (Volker Lendecke) Primary-site: ftp.gwdg.de:/pub/linux/misc/ncpfs Alternate-site: sunsite.unc.edu:/pub/Linux/system/Filesystems/ncpfs - ~136k ncpfs-2.0.6.tgz - ~ 1k ncpfs-2.0.6.lsm + ~148k ncpfs-2.0.7.tgz + ~ 1k ncpfs-2.0.7.lsm Copying-policy: GPL End diff --git a/sutil/Makefile b/sutil/Makefile new file mode 100644 index 0000000..638f2de --- /dev/null +++ b/sutil/Makefile @@ -0,0 +1,42 @@ +# +# Makefile for the linux ncp-filesystem routines. +# + +UTILS = ncpmount ncpumount nwsfind + +CC = gcc + +default: + make -C .. + +all: $(UTILS) + +install: all + for i in $(UTILS); \ + do install $$i -m 4755 $(BINDIR); done + +$(UTILS): %: %.o libncp.a + $(CC) -o $@ $(addsuffix .o,$@) -L. -lncp -L../lib -lcom_err + +ncplib.o: ncplib.c ncplib.h + $(CC) $(CFLAGS) -finline-functions -c ncplib.c + +libncp.a: ncplib.o ../lib/ncplib_err.o + ar r libncp.a ncplib.o ../lib/ncplib_err.o + +dep: + $(CPP) -M $(INCLUDES) *.c > .depend + +clean: + rm -f *.o *~ libncp.a $(UTILS) + +mrproper: clean + rm -f .depend + +# +# include a dependency file if one exists +# +ifeq (.depend,$(wildcard .depend)) +include .depend +endif + diff --git a/sutil/ipxlib.h b/sutil/ipxlib.h new file mode 100644 index 0000000..ac243b0 --- /dev/null +++ b/sutil/ipxlib.h @@ -0,0 +1,93 @@ +/* + * ipxlib.h + * + * Copyright (C) 1995 by Volker Lendecke + * + */ + +#ifndef _IPXLIB_H +#define _IPXLIB_H + + +#include +#include +#include +#include +#include + +typedef unsigned long IPXNet; +typedef unsigned short IPXPort; +typedef unsigned char IPXNode[IPX_NODE_LEN]; + +#define IPX_USER_PTYPE (0x00) +#define IPX_RIP_PTYPE (0x01) +#define IPX_SAP_PTYPE (0x04) +#define IPX_AUTO_PORT (0x0000) +#define IPX_SAP_PORT (0x0452) +#define IPX_RIP_PORT (0x0453) + +#define IPX_SAP_GENERAL_QUERY (0x0001) +#define IPX_SAP_GENERAL_RESPONSE (0x0002) +#define IPX_SAP_NEAREST_QUERY (0x0003) +#define IPX_SAP_NEAREST_RESPONSE (0x0004) + +#define IPX_SAP_FILE_SERVER (0x0004) + +struct sap_query { + unsigned short query_type; /* net order */ + unsigned short server_type; /* net order */ +}; + +struct sap_server_ident { + unsigned short server_type __attribute__ ((packed)); + char server_name[48] __attribute__ ((packed)); + IPXNet server_network __attribute__ ((packed)); + IPXNode server_node __attribute__ ((packed)); + IPXPort server_port __attribute__ ((packed)); + unsigned short intermediate_network __attribute__ ((packed)); +}; + +#define IPX_RIP_REQUEST (0x1) +#define IPX_RIP_RESPONSE (0x2) + +struct ipx_rip_packet { + __u16 operation __attribute__ ((packed)); + struct ipx_rt_def { + __u32 network __attribute__ ((packed)); + __u16 hops __attribute__ ((packed)); + __u16 ticks __attribute__ ((packed)); + } rt[1] __attribute__ ((packed)); +}; + +#define IPX_BROADCAST_NODE ("\xff\xff\xff\xff\xff\xff") +#define IPX_THIS_NODE ("\0\0\0\0\0\0") +#define IPX_THIS_NET (0) + +#ifndef IPX_NODE_LEN +#define IPX_NODE_LEN (6) +#endif + +void +ipx_print_node(IPXNode node); +void +ipx_print_network(IPXNet net); +void +ipx_print_port(IPXPort port); +void +ipx_print_saddr(struct sockaddr_ipx* sipx); +void +ipx_fprint_node(FILE *file, IPXNode node); +void +ipx_fprint_network(FILE *file, IPXNet net); +void +ipx_fprint_port(FILE *file, IPXPort port); +void +ipx_fprint_saddr(FILE *file, struct sockaddr_ipx* sipx); +int +ipx_sscanf_node(char *buf, unsigned char node[IPX_NODE_LEN]); +void +ipx_assign_node(IPXNode dest, IPXNode src); +int +ipx_node_equal(IPXNode n1,IPXNode n2); + +#endif /* _IPXLIB_H */ diff --git a/sutil/ncplib.c b/sutil/ncplib.c new file mode 100644 index 0000000..7f09950 --- /dev/null +++ b/sutil/ncplib.c @@ -0,0 +1,1927 @@ +/* + * ncplib.c + * + * Copyright (C) 1995, 1996 by Volker Lendecke + * + */ + +#include "ncplib.h" +#include "ncplib_err.h" + +typedef __u8 byte; +typedef __u16 word; +typedef __u32 dword; + +#include +/* #include */ /* generates a warning here */ +extern pid_t wait(int *); +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static long +ncp_negotiate_buffersize(struct ncp_conn *conn, + int size, int *target); +static long +ncp_login_object(struct ncp_conn *conn, + const unsigned char *username, + int login_type, + const unsigned char *password); + +static long +ncp_do_close(struct ncp_conn *conn); + +void +str_upper(char *name) +{ + while (*name) { + *name = toupper(*name); + name = name + 1; + } +} + +/* 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,"%08lX",ntohl(net)); +} + +void +ipx_fprint_port(FILE* file,IPXPort port) +{ + fprintf(file,"%04X",ntohs(port)); +} + +void +ipx_fprint_saddr(FILE* file,struct sockaddr_ipx* sipx) +{ + ipx_fprint_network(file,sipx->sipx_network); + fprintf(file,":"); + ipx_fprint_node(file,sipx->sipx_node); + fprintf(file,":"); + ipx_fprint_port(file,sipx->sipx_port); +} + +void +ipx_print_node(IPXNode node) +{ + ipx_fprint_node(stdout,node); +} + +void +ipx_print_network(IPXNet net) +{ + ipx_fprint_network(stdout,net); +} + +void +ipx_print_port(IPXPort port) +{ + ipx_fprint_port(stdout,port); +} + +void +ipx_print_saddr(struct sockaddr_ipx* sipx) +{ + ipx_fprint_saddr(stdout,sipx); +} + +int +ipx_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; +} + +void +ipx_assign_node(IPXNode dest, IPXNode src) +{ + memcpy(dest, src, IPX_NODE_LEN); +} + +int +ipx_node_equal(IPXNode n1,IPXNode n2) +{ + return memcmp(n1,n2, IPX_NODE_LEN)==0; +} + +static int +ipx_recvfrom(int sock, void *buf, int len, unsigned int flags, + struct sockaddr_ipx *sender, int *addrlen, int timeout, + long *err) +{ + fd_set rd, wr, ex; + struct timeval tv; + int result; + + FD_ZERO(&rd); FD_ZERO(&wr); FD_ZERO(&ex); + FD_SET(sock, &rd); + + tv.tv_sec = timeout; + tv.tv_usec = 0; + + if ((result = select(sock+1, &rd, &wr, &ex, &tv)) == -1) + { + *err = errno; + return -1; + } + + if (FD_ISSET(sock, &rd)) + { + result = recvfrom(sock, buf, len, flags, + (struct sockaddr *)sender, addrlen); + } + else + { + result = -1; + errno = ETIMEDOUT; + } + if (result < 0) + { + *err = errno; + } + return result; +} + +static int +ipx_recv(int sock, void *buf, int len, unsigned int flags, int timeout, + long *err) +{ + struct sockaddr_ipx sender; + int addrlen = sizeof(sender); + + return ipx_recvfrom(sock, buf, len, flags, &sender, &addrlen, + timeout, err); +} + +static long +ipx_sap_find_nearest(int server_type, struct sockaddr_ipx *result, + char server_name[NCP_BINDERY_NAME_LEN]) +{ + struct sockaddr_ipx addr; + char data[1024]; + int sock; + int opt; + int packets; + int len; + + struct sap_server_ident *ident; + + if ((sock = socket(AF_IPX,SOCK_DGRAM,PF_IPX)) < 0) + { + if (errno == EINVAL) + { + return NCPL_ET_NO_IPX; + } + return errno; + } + + opt=1; + /* Permit broadcast output */ + if(setsockopt(sock,SOL_SOCKET,SO_BROADCAST, &opt,sizeof(opt))==-1) + { + goto finished; + } + + memzero(addr); + addr.sipx_family = AF_IPX; + addr.sipx_network = htonl(0x0); + addr.sipx_port = htons(0x0); + addr.sipx_type = IPX_SAP_PTYPE; + + if(bind(sock,(struct sockaddr*)&addr,sizeof(addr))==-1) + { + if (errno == EADDRNOTAVAIL) + { + errno = NCPL_ET_NO_INTERFACE; + } + goto finished; + } + + *(unsigned short *)data = htons(IPX_SAP_NEAREST_QUERY); + *(unsigned short *)&(data[2]) = htons(server_type); + + memzero(addr); + addr.sipx_family = AF_IPX; + addr.sipx_port = htons(IPX_SAP_PORT); + addr.sipx_type = IPX_SAP_PTYPE; + addr.sipx_network = htonl(0x0); + ipx_assign_node(addr.sipx_node, IPX_BROADCAST_NODE); + + if (sendto(sock, data, 4, 0, + (struct sockaddr *)&addr, sizeof(addr)) < 0) + { + goto finished; + } + + packets = 5; + do + { + long err; + len = ipx_recv(sock, data, 1024, 0, 1, &err); + if (len < 66) + { + packets = packets - 1; + continue; + } + } + while ( (ntohs(*((__u16 *)data)) != IPX_SAP_NEAREST_RESPONSE) + && (packets > 0)); + + if (packets == 0) + { + close(sock); + return NCPL_ET_NO_SERVER; + } + + ident = (struct sap_server_ident *)(data+2); + + result->sipx_family = AF_IPX; + result->sipx_network = ident->server_network; + result->sipx_port = ident->server_port; + ipx_assign_node(result->sipx_node, ident->server_node); + + memcpy(server_name, ident->server_name, sizeof(ident->server_name)); + + errno = 0; + + finished: + close(sock); + return errno; +} + +static int +ipx_make_reachable(IPXNet 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; + int addrlen; + int sock; + int opt; + int res=-1; + int i; + int packets; + + if (geteuid() != 0) + { + errno = EPERM; + return -1; + } + + memzero(rip); + + sock = socket(AF_IPX, SOCK_DGRAM, PF_IPX); + + if (sock == -1) + { + if (errno == EINVAL) + { + return NCPL_ET_NO_IPX; + } + return errno; + } + + opt=1; + /* Permit broadcast output */ + if (setsockopt(sock, SOL_SOCKET, SO_BROADCAST, &opt, sizeof(opt)) != 0) + { + goto finished; + } + + memzero(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 = htonl(network); + + if (sendto(sock, &rip, sizeof(rip), 0, + (struct sockaddr *)&addr, sizeof(addr)) < 0) + { + goto finished; + } + + packets = 3; + do + { + long err; + int len; + + if (packets == 0) + { + goto finished; + } + + addrlen = sizeof(struct sockaddr_ipx); + + len = ipx_recvfrom(sock, &rip, sizeof(rip),0, sr, &addrlen, 1, + &err); + + if (len < sizeof(rip)) + { + packets = packets - 1; + continue; + } + } + while (ntohs(rip.operation) != IPX_RIP_RESPONSE); + + if (rip.rt[0].network != htonl(network)) + { + goto finished; + } + + rt_def.rt_flags = RTF_GATEWAY; + st->sipx_network = htonl(network); + 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 != 0) + { + errno = ENETUNREACH; + } + return res; +} + +static int +install_wdog(struct ncp_conn *conn) +{ + int parent_pid = getpid(); + int pid; + int sock = conn->wdog_sock; + + char buf[1024]; + struct sockaddr_ipx sender; + int sizeofaddr = sizeof(struct sockaddr_ipx); + int pktsize; + + + if ((pid = fork()) < 0) + { + return -1; + } + + if (pid != 0) + { + /* Parent, should go on as usual */ + conn->wdog_pid = pid; + return 0; + } + + while (1) + { + long err; + /* every 120 seconds we look if our parent is + still alive */ + pktsize = ipx_recvfrom(sock, buf, sizeof(buf), 0, + &sender, &sizeofaddr, 120, &err); + + if (getppid() != parent_pid) + { + /* our parent has died, so nothing to do + anymore */ + exit(0); + } + + if ( (pktsize != 2) + || (buf[1] != '?')) + { + continue; + } + + buf[1] = 'Y'; + pktsize = sendto(sock, buf, 2, 0, + (struct sockaddr *)&sender, + sizeof(sender)); + } +} + +#define ncp_printf printf + +static void +assert_conn_locked(struct ncp_conn *conn); + +static void +assert_conn_not_locked(struct ncp_conn *conn) +{ + if (conn->lock != 0) + { + ncp_printf("ncpfs: conn already locked!\n"); + } +} + +static void +ncp_lock_conn(struct ncp_conn *conn) +{ + assert_conn_not_locked(conn); + conn->lock = 1; +} + +static void +ncp_unlock_conn(struct ncp_conn *conn) +{ + assert_conn_locked(conn); + conn->lock = 0; +} + +static long +do_ncp_call(struct ncp_conn *conn, int request_size) +{ + struct ncp_request_header request = + *((struct ncp_request_header *)(&(conn->packet))); + + int result; + int retries = 20; + int len; + long err; + + while (retries > 0) + { + struct ncp_reply_header reply; + struct sockaddr_ipx sender; + int sizeofaddr = sizeof(sender); + + retries -= 1; + + result = sendto(conn->ncp_sock, conn->packet, + request_size, + 0, (struct sockaddr *)&(conn->i.addr), + sizeof(conn->i.addr)); + + if (result < 0) + { + return errno; + } + + re_select: + len = ipx_recvfrom(conn->ncp_sock, + (char *)&reply, sizeof(reply), + MSG_PEEK, &sender, &sizeofaddr, 3, &err); + + if ((len < 0) && (err == ETIMEDOUT)) + { + continue; + } + if (len < 0) + { + return err; + } + + if ( /* Is the sender wrong? */ + (memcmp(&sender.sipx_node, + &(conn->i.addr.sipx_node), 6) != 0) + || (sender.sipx_port != conn->i.addr.sipx_port) + + /* Did the sender send a positive acknowledge? */ + || ( (len == sizeof(reply)) + && (reply.type == NCP_POSITIVE_ACK)) + + /* Did we get a bogus answer? */ + || ( (len < sizeof(reply)) + || (reply.type != NCP_REPLY) + || ( (request.type != NCP_ALLOC_SLOT_REQUEST) + && ( (reply.sequence != request.sequence) + || (reply.conn_low != request.conn_low) + || (reply.conn_high != request.conn_high))))) + { + /* Then throw away the packet */ + ipx_recv(conn->ncp_sock, (char *)&reply, sizeof(reply), + 0, 1, &err); + goto re_select; + } + + ipx_recv(conn->ncp_sock, conn->packet, NCP_PACKET_SIZE, + 0, 1, &err); + conn->reply_size = len; + return 0; + } + return ETIMEDOUT; +} + +static int +ncp_mount_request(struct ncp_conn *conn, int function) +{ + struct ncp_request_header *h + = (struct ncp_request_header *)(conn->packet); + struct ncp_reply_header *reply + = (struct ncp_reply_header *)(conn->packet); + struct ncp_ioctl_request request; + + int result; + + assert_conn_locked(conn); + + if (conn->has_subfunction != 0) + { + *(__u16 *)&(h->data[0]) + = htons(conn->current_size + - sizeof(struct ncp_request_header) - 2); + } + + request.function = function; + request.size = conn->current_size; + request.data = conn->packet; + + if ((result = ioctl(conn->mount_fid,NCP_IOC_NCPREQUEST, &request)) < 0) + { + return result; + } + + conn->completion = reply->completion_code; + conn->conn_status = reply->connection_state; + conn->ncp_reply_size = result - sizeof(struct ncp_reply_header); + + if ((reply->completion_code != 0) && (conn->verbose != 0)) + { + ncp_printf("ncp_request_error: %d\n", reply->completion_code); + } + return reply->completion_code == 0 ? 0 : NCPL_ET_REQUEST_ERROR; +} + +static long +ncp_temp_request(struct ncp_conn *conn, int function) +{ + struct ncp_request_header *h + = (struct ncp_request_header *)&(conn->packet); + struct ncp_reply_header *r + = (struct ncp_reply_header *)&(conn->packet); + + long err; + + assert_conn_locked(conn); + + if (conn->has_subfunction != 0) + { + *(__u16 *)&(h->data[0]) + = htons(conn->current_size + - sizeof(struct ncp_request_header) - 2); + } + + h->type = NCP_REQUEST; + + conn->sequence += 1; + h->sequence = conn->sequence; + h->conn_low = (conn->i.connection) & 0xff; + h->conn_high = ((conn->i.connection) & 0xff00) >> 8; + h->task = 1; + h->function = function; + + if ((err = do_ncp_call(conn, conn->current_size)) != 0) + { + return err; + } + + conn->completion = r->completion_code; + conn->conn_status = r->connection_state; + conn->ncp_reply_size = + conn->reply_size - sizeof(struct ncp_reply_header); + + if ((r->completion_code != 0) && (conn->verbose != 0)) + { + ncp_printf("ncp_completion_code: %d\n", r->completion_code); + } + return r->completion_code == 0 ? 0 : NCPL_ET_REQUEST_ERROR; +} + +static long +ncp_connect_addr(struct ncp_conn *conn, const struct sockaddr_ipx *target, + int wdog_needed) +{ + struct ncp_request_header *h = + (struct ncp_request_header *)&(conn->packet); + + struct sockaddr_ipx addr; + int addrlen; + + int ncp_sock, wdog_sock; + long err; + + conn->is_connected = NOT_CONNECTED; + conn->verbose = 0; + + if ((ncp_sock = socket(AF_IPX, SOCK_DGRAM, PF_IPX)) == -1) + { + return errno; + } + + if ((wdog_sock = socket(AF_IPX, SOCK_DGRAM, PF_IPX)) == -1) + { + 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; + + h->type = NCP_ALLOC_SLOT_REQUEST; + + conn->sequence = 0; + conn->i.addr = *target; + h->sequence = conn->sequence; + h->conn_low = 0xff; + h->conn_high = 0xff; + h->task = 1; + h->function = 0; + + if ((err = do_ncp_call(conn, sizeof(*h))) != 0) + { + if ( (err != ENETUNREACH) + || (ipx_make_reachable(htonl(target->sipx_network)) != 0) + || ((err = do_ncp_call(conn, sizeof(*h))) != 0)) + { + close(ncp_sock); close(wdog_sock); + return err; + } + } + + if (wdog_needed != 0) + { + install_wdog(conn); + } + else + { + conn->wdog_pid = 0; + } + + conn->sequence = 0; + conn->i.connection = h->conn_low + (h->conn_high * 256); + conn->is_connected = CONN_TEMPORARY; + + if ( (ncp_negotiate_buffersize(conn, 1024, + &(conn->i.buffer_size)) != 0) + || (conn->i.buffer_size < 512) + || (conn->i.buffer_size > 1024)) + { + ncp_do_close(conn); + return -1; + } + + return 0; +} + +static long +ncp_connect_any(struct ncp_conn *conn, int wdog_needed) +{ + struct sockaddr_ipx addr; + char name[NCP_BINDERY_NAME_LEN]; + long result; + + if ((result = ipx_sap_find_nearest(IPX_SAP_FILE_SERVER, + &addr, name)) != 0) + { + return result; + } + + if ((result = ncp_connect_addr(conn, &addr, wdog_needed)) != 0) + { + return result; + } + strcpy(conn->server, name); + return 0; +} + +struct sockaddr_ipx * +ncp_find_fileserver(char *server_name, long *err) +{ + return ncp_find_server(&server_name, NCP_BINDERY_FSERVER, err); +} + +struct sockaddr_ipx * +ncp_find_server(char **server_name, int type, long *err) +{ + char server[NCP_BINDERY_NAME_LEN+1]; + static char nearest[NCP_BINDERY_NAME_LEN+1]; + struct nw_property prop; + struct prop_net_address *n_addr = (struct prop_net_address *)∝ + struct ncp_conn conn; + + static struct sockaddr_ipx result; + + initialize_NCPL_error_table(); + + memset(server, 0, sizeof(server)); + memset(nearest, 0, sizeof(nearest)); + + if (*server_name != NULL) + { + if (strlen(*server_name) >= sizeof(server)) + { + *err = NCPL_ET_NAMETOOLONG; + return NULL; + } + + strcpy(server, *server_name); + str_upper(server); + } + + if ((*err = ipx_sap_find_nearest(type, &result, nearest)) != 0) + { + return NULL; + } + + /* We have to ask the nearest server for our wanted server */ + + memzero(conn); + if ((*err = ncp_connect_addr(&conn, &result, 0)) != 0) + { + return NULL; + } + + if (*server_name == NULL) + { + *server_name = nearest; + ncp_do_close(&conn); + errno = 0; + return &result; + } + + /* The following optimization should have been done before + ncp_connect_addr. This would be convenient if there was a + simple way to find out whether there is a route to the + server. Parsing /proc/net/ipx_route is not too nice, so we + just connect to the server and immediately disconnect + again. This way we also find out if the server still has + free connection slots. */ + + if (strcmp(server, nearest) == 0) + { + /* Our wanted server answered the SAP GNS request, so + use it */ + ncp_do_close(&conn); + errno = 0; + return &result; + } + + if (ncp_read_property_value(&conn, type, server, 1, + "NET_ADDRESS", &prop) != 0) + { + ncp_do_close(&conn); + *err = NCPL_ET_HOST_UNKNOWN; + return NULL; + } + + if ((*err = ncp_do_close(&conn)) != 0) + { + return NULL; + } + + result.sipx_family = AF_IPX; + result.sipx_network = n_addr->network; + result.sipx_port = n_addr->port; + ipx_assign_node(result.sipx_node, n_addr->node); + + /* To make the final server reachable, we connect again. See + above. (When can we rely on all users running ipxd??? :-)) */ + memzero(conn); + if ( ((*err = ncp_connect_addr(&conn, &result, 0)) != 0) + || ((*err = ncp_do_close(&conn)) != 0)) + { + return NULL; + } + + return &result; +} + +static long +ncp_open_temporary(struct ncp_conn *conn, + struct ncp_conn_spec *spec) +{ + struct sockaddr_ipx *addr; + long err; + + if (spec == NULL) + { + return ncp_connect_any(conn, 1); + } + + if ((addr = ncp_find_fileserver(spec->server, &err)) == NULL) + { + return err; + } + + if ((err = ncp_connect_addr(conn, addr, 1)) != 0) + { + return err; + } + + strcpy(conn->server, spec->server); + + if (strlen(spec->user) != 0) + { + if (ncp_login_object(conn, spec->user, spec->login_type, + spec->password) != 0) + { + ncp_do_close(conn); + return EACCES; + } + strcpy(conn->user, spec->user); + } + + return 0; +} + +char * +ncp_find_permanent(const struct ncp_conn_spec *spec) +{ + FILE *mtab; + struct ncp_conn_ent *conn_ent; + char *result = NULL; + int mount_fid; + struct ncp_fs_info i; + + initialize_NCPL_error_table(); + + 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) + || ( (strlen(spec->server) != 0) + && (strcasecmp(conn_ent->server, + spec->server) != 0)) + || ( (strlen(spec->user) != 0) + && (strcasecmp(conn_ent->user, + spec->user) != 0))) + { + continue; + } + } + + mount_fid = open(conn_ent->mount_point, O_RDONLY, 0); + if (mount_fid < 0) + { + continue; + } + + i.version = NCP_GET_FS_INFO_VERSION; + + if (ioctl(mount_fid, NCP_IOC_GET_FS_INFO, &i) < 0) + { + close(mount_fid); + continue; + } + + close(mount_fid); + result = conn_ent->mount_point; + break; + } + + fclose(mtab); + errno = (result == NULL) ? ENOENT : 0; + return result; +} + +static int +ncp_open_permanent(struct ncp_conn *conn, + const struct ncp_conn_spec *spec) +{ + char *mount_point; + + if (conn->is_connected != NOT_CONNECTED) + { + errno = EBUSY; + return -1; + } + + if ((mount_point = ncp_find_permanent(spec)) == NULL) + { + return -1; + } + + if (strlen(mount_point) >= sizeof(conn->mount_point)) + { + errno = ENAMETOOLONG; + return -1; + } + + /* The rest has already been done in ncp_find_permanent, so we + * do not check errors anymore */ + conn->mount_fid = open(mount_point, O_RDONLY, 0); + conn->i.version = NCP_GET_FS_INFO_VERSION; + ioctl(conn->mount_fid, NCP_IOC_GET_FS_INFO, &(conn->i)); + if (spec != NULL) + { + strncpy(conn->server, spec->server, sizeof(conn->server)); + strncpy(conn->user, spec->user, sizeof(conn->user)); + } + else + { + memset(conn->server, '\0', sizeof(conn->server)); + memset(conn->user, '\0', sizeof(conn->user)); + } + strcpy(conn->mount_point, mount_point); + conn->is_connected = CONN_PERMANENT; + return 0; +} + +struct ncp_conn * +ncp_open(struct ncp_conn_spec *spec, long *err) +{ + struct ncp_conn *result; + + initialize_NCPL_error_table(); + + result = malloc(sizeof(struct ncp_conn)); + + if (result == NULL) + { + *err = ENOMEM; + return NULL; + } + + memzero(*result); + + if (ncp_open_permanent(result, spec) == 0) + { + return result; + } + + if ((*err = ncp_open_temporary(result, spec)) != 0) + { + free(result); + return NULL; + } + return result; +} + + +struct ncp_conn * +ncp_open_mount(const char *mount_point, long *err) +{ + struct ncp_conn *result; + + initialize_NCPL_error_table(); + + if (strlen(mount_point) >= sizeof(result->mount_point)) + { + *err = ENAMETOOLONG; + return NULL; + } + + result = malloc(sizeof(struct ncp_conn)); + + if (result == NULL) + { + *err = ENOMEM; + return NULL; + } + + memzero(*result); + + result->is_connected = NOT_CONNECTED; + + result->mount_fid = open(mount_point, O_RDONLY, 0); + if (result->mount_fid < 0) + { + free(result); + *err = ENODEV; + return NULL; + } + strcpy(result->mount_point, mount_point); + result->is_connected = CONN_PERMANENT; + + result->i.version = NCP_GET_FS_INFO_VERSION; + + if (ioctl(result->mount_fid, NCP_IOC_GET_FS_INFO, &(result->i)) != 0) + { + free(result); + *err = NCPL_ET_NO_NCPFS_FILE; + return NULL; + } + return result; +} + +static long +ncp_user_disconnect(struct ncp_conn *conn) +{ + struct ncp_request_header *h + = (struct ncp_request_header *)(conn->packet); + long result; + + h->type = NCP_DEALLOC_SLOT_REQUEST; + + conn->sequence += 1; + h->sequence = conn->sequence; + h->conn_low = (conn->i.connection) & 0xff; + h->conn_high = ((conn->i.connection) & 0xff00) >> 8; + h->task = 1; + h->function = 0; + + if ((result = do_ncp_call(conn, sizeof(*h))) != 0) + { + return result; + } + + close(conn->ncp_sock); + close(conn->wdog_sock); + + if (conn->wdog_pid != 0) + { + kill(conn->wdog_pid, SIGTERM); + wait(NULL); + } + + return 0; +} + +static long +ncp_do_close(struct ncp_conn *conn) +{ + long result = -1; + + switch (conn->is_connected) + { + case CONN_PERMANENT: + result = close(conn->mount_fid); + break; + + case CONN_TEMPORARY: + result = ncp_user_disconnect(conn); + break; + + default: + break; + } + + conn->is_connected = NOT_CONNECTED; + + return result; +} + +long +ncp_close(struct ncp_conn *conn) +{ + long result; + if (conn == NULL) + { + return 0; + } + + if ((result = ncp_do_close(conn)) != 0) + { + return result; + } + free(conn); + return 0; +} + +struct ncp_conn_ent * +ncp_get_conn_ent(FILE *filep) +{ + static struct ncp_conn_ent entry; + char server[2*NCP_BINDERY_NAME_LEN]; + char *user; + struct mntent *mnt_ent; + int fid; + + memzero(server); + memzero(entry); + + while ((mnt_ent = getmntent(filep)) != NULL) + { + if (strcmp(mnt_ent->mnt_type, "ncpfs") != 0) + { + continue; + } + + if (strlen(mnt_ent->mnt_fsname) >= sizeof(server)) + { + continue; + } + + strcpy(server, mnt_ent->mnt_fsname); + user = strchr(server, '/'); + if (user != NULL) + { + *user = '\0'; + user += 1; + if (strlen(user) >= sizeof(entry.user)) + { + continue; + } + strcpy(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 (ioctl(fid, NCP_IOC_GETMOUNTUID, &entry.uid) != 0) { + close(fid); + continue; + } + + close(fid); + return &entry; + } + + 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; + + memzero(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; +} + +FILE * +ncp_fopen_nwc(const char *user, const char *mode, long *err) +{ + char path[MAXPATHLEN]; + char *home = NULL; + struct stat st; + + if (mode == NULL) + { + mode = "r"; + } + + if (user == NULL) + { + home = getenv("HOME"); + } + else + { + struct passwd *pwd; + + if ((pwd = getpwnam(user)) != NULL) + { + home = pwd->pw_dir; + } + } + + if ( (home == NULL) + || (strlen(home) + sizeof(NWCLIENT) + 2 > sizeof(path))) + { + *err = ENAMETOOLONG; + return NULL; + } + + strcpy(path, home); + strcat(path, "/"); + strcat(path, NWCLIENT); + + if (stat(path, &st) != 0) + { + *err = errno; + return NULL; + } + + if ((st.st_mode & (S_IRWXO | S_IRWXG)) != 0) + { + *err = NCPL_ET_INVALID_MODE; + return NULL; + } + + return fopen(path, mode); +} + +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) +{ + static struct ncp_conn_spec spec; + + struct ncp_conn conn; + + FILE *nwc; + struct ncp_conn_spec *nwc_ent; + + initialize_NCPL_error_table(); + + *err = 0; + memzero(spec); + spec.uid = getuid(); + + if (server != NULL) + { + if (strlen(server) >= sizeof(spec.server)) + { + *err = NCPL_ET_NAMETOOLONG; + return NULL; + } + strcpy(spec.server, server); + } + else + { + if ((nwc = ncp_fopen_nwc(NULL, NULL, err)) == NULL) + { + *err = NCPL_ET_NO_SERVER; + return NULL; + } + + nwc_ent = ncp_get_nwc_ent(nwc); + fclose(nwc); + + if (nwc_ent == NULL) + { + *err = NCPL_ET_NO_SPEC; + return NULL; + } + strcpy(spec.server, nwc_ent->server); + strcpy(spec.user, nwc_ent->user); + } + + str_upper(spec.server); + + if (login_necessary == 0) + { + memset(spec.user, 0, sizeof(spec.user)); + memset(spec.password, 0, sizeof(spec.password)); + return &spec; + } + + if (user != NULL) + { + if (strlen(user) >= sizeof(spec.user)) + { + *err = NCPL_ET_NAMETOOLONG; + return NULL; + } + strcpy(spec.user, user); + } + + str_upper(spec.user); + spec.login_type = NCP_BINDERY_USER; + + if (ncp_open_permanent(&conn, &spec) == 0) + { + ncp_do_close(&conn); + return &spec; + } + + if (password != NULL) + { + if (strlen(password) >= sizeof(spec.password)) + { + *err = NCPL_ET_NAMETOOLONG; + return NULL; + } + strcpy(spec.password, password); + } + else + { + if ((nwc = ncp_fopen_nwc(NULL, NULL, err)) != NULL) + { + while ((nwc_ent = ncp_get_nwc_ent(nwc)) != NULL) + { + if ( (strcasecmp(spec.server, + nwc_ent->server) != 0) + || ( (*spec.user != '\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 (strlen(spec.user) == 0) + { + *err = NCPL_ET_NO_USER; + return NULL; + } + + if ((strlen(spec.password) == 0) && (password == NULL)) + { + char *password; + if (!(isatty(0) && isatty(1))) + { + return NULL; + } + printf("Logging into %s as %s\n", + spec.server, spec.user); + + password = getpass("Password: "); + if (strlen(password) > sizeof(spec.password)) + { + return NULL; + } + strcpy(spec.password, password); + } + else + { + if (strcmp(spec.password, NWC_NOPASSWORD) == 0) + { + *spec.password = '\0'; + } + } + + str_upper(spec.server); + str_upper(spec.user); + str_upper(spec.password); + return &spec; +} + +struct ncp_conn * +ncp_initialize_as(int *argc, char **argv, + int login_necessary, int login_type, long *err) +{ + char *server = NULL; + char *user = NULL; + char *password = NULL; + struct ncp_conn_spec *spec; + int i = 1; + + int get_argument(int arg_no, 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; + } + + initialize_NCPL_error_table(); + + *err = EINVAL; + + while (i < *argc) + { + if ( (argv[i][0] != '-') + || (strlen(argv[i]) != 2)) + { + i += 1; + continue; + } + + switch (argv[i][1]) + { + case 'S': + if (get_argument(i, &server) != 0) + { + return NULL; + } + continue; + case 'U': + if (get_argument(i, &user) != 0) + { + return NULL; + } + continue; + case 'P': + if (get_argument(i, &password) != 0) + { + return NULL; + } + continue; + case 'n': + if (get_argument(i, 0) != 0) + { + return NULL; + } + password = NWC_NOPASSWORD; + continue; + } + i += 1; + } + + spec = ncp_find_conn_spec(server, user, password, login_necessary, + getuid(), err); + + if (spec == NULL) + { + if (login_necessary != 0) + { + return NULL; + } + else + { + return ncp_open(NULL, err); + } + } + + spec->login_type = login_type; + + if (login_necessary == 0) + { + spec->user[0] = '\0'; + } + + return ncp_open(spec, err); +} + +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); +} + +static long +ncp_request(struct ncp_conn *conn, int function) +{ + switch (conn->is_connected) { + case CONN_PERMANENT: + return ncp_mount_request(conn, function); + case CONN_TEMPORARY: + return ncp_temp_request(conn, function); + default: + } + return ENOTCONN; +} + +/****************************************************************************/ +/* */ +/* Helper functions */ +/* */ +/****************************************************************************/ + +static inline int +min(int a, int b) +{ + return (alock == 0) { + ncp_printf("ncpfs: conn not locked!\n"); + } +} + +static void +ncp_add_byte(struct ncp_conn *conn, byte x) +{ + assert_conn_locked(conn); + *(byte *)(&(conn->packet[conn->current_size])) = x; + conn->current_size += 1; + return; +} + +static void +ncp_add_word(struct ncp_conn *conn, word x) +{ + assert_conn_locked(conn); + *(word *)(&(conn->packet[conn->current_size])) = x; + conn->current_size += 2; + return; +} + +static void +ncp_add_mem(struct ncp_conn *conn, const void *source, int size) +{ + assert_conn_locked(conn); + memcpy(&(conn->packet[conn->current_size]), source, size); + conn->current_size += size; + return; +} + +static 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; +} + +static void +ncp_init_request(struct ncp_conn *conn) +{ + ncp_lock_conn(conn); + + conn->current_size = sizeof(struct ncp_request_header); + conn->has_subfunction = 0; +} + +static void +ncp_init_request_s(struct ncp_conn *conn, int subfunction) +{ + ncp_init_request(conn); + ncp_add_word(conn, 0); /* preliminary size */ + + ncp_add_byte(conn, subfunction); + + conn->has_subfunction = 1; +} + +static char * +ncp_reply_data(struct ncp_conn *conn, int offset) +{ + return &(conn->packet[sizeof(struct ncp_reply_header) + offset]); +} + +static byte +ncp_reply_byte(struct ncp_conn *conn, int offset) +{ + return *(byte *)(ncp_reply_data(conn, offset)); +} + +static word +ncp_reply_word(struct ncp_conn *conn, int offset) +{ + return *(word *)(ncp_reply_data(conn, offset)); +} + +static dword +ncp_reply_dword(struct ncp_conn *conn, int offset) +{ + return *(dword *)(ncp_reply_data(conn, offset)); +} + +/* Here the ncp calls begin + */ + +static long +ncp_negotiate_buffersize(struct ncp_conn *conn, + int size, int *target) +{ + long result; + + ncp_init_request(conn); + ncp_add_word(conn, htons(size)); + + if ((result = ncp_request(conn, 33)) != 0) { + ncp_unlock_conn(conn); + return result; + } + + *target =min(ntohs(ncp_reply_word(conn, 0)), size); + + ncp_unlock_conn(conn); + return 0; +} + +/* + * target is a 8-byte buffer + */ +long +ncp_get_encryption_key(struct ncp_conn *conn, + char *target) +{ + long result; + + ncp_init_request_s(conn, 23); + + if ((result = ncp_request(conn, 23)) != 0) { + ncp_unlock_conn(conn); + return result; + } + + if (conn->ncp_reply_size < 8) { + ncp_printf("ncp_reply_size %d < 8\n", + conn->ncp_reply_size); + ncp_unlock_conn(conn); + return result; + } + + memcpy(target, ncp_reply_data(conn, 0), 8); + ncp_unlock_conn(conn); + return 0; +} + +long +ncp_get_bindery_object_id(struct ncp_conn *conn, + __u16 object_type, + const char *object_name, + struct ncp_bindery_object *target) +{ + long result; + ncp_init_request_s(conn, 53); + ncp_add_word(conn, htons(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_printf("ncp_reply_size %d < 54\n", + conn->ncp_reply_size); + ncp_unlock_conn(conn); + return result; + } + + target->object_id = ntohl(ncp_reply_dword(conn, 0)); + target->object_type = ntohs(ncp_reply_word (conn, 4)); + memcpy(target->object_name, ncp_reply_data(conn, 6), 48); + ncp_unlock_conn(conn); + return 0; +} + +long +ncp_read_property_value(struct ncp_conn *conn, + int object_type, const char *object_name, + int segment, const char *prop_name, + struct nw_property *target) +{ + long result; + ncp_init_request_s(conn, 61); + ncp_add_word(conn, htons(object_type)); + ncp_add_pstring(conn, object_name); + ncp_add_byte(conn, segment); + ncp_add_pstring(conn, prop_name); + + if ((result = ncp_request(conn, 23)) != 0) { + ncp_unlock_conn(conn); + return result; + } + + memcpy(&(target->value), ncp_reply_data(conn, 0), 128); + target->more_flag = ncp_reply_byte(conn, 128); + target->property_flag = ncp_reply_byte(conn, 129); + ncp_unlock_conn(conn); + return 0; +} + +long +ncp_login_encrypted(struct ncp_conn *conn, + const struct ncp_bindery_object *object, + const unsigned char *key, + const unsigned char *passwd) +{ + dword tmpID = htonl(object->object_id); + unsigned char buf[128]; + unsigned char encrypted[8]; + long result; + + 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(conn, htons(object->object_type)); + ncp_add_pstring(conn, object->object_name); + + result = ncp_request(conn, 23); + ncp_unlock_conn(conn); + return result; +} + +long +ncp_login_unencrypted(struct ncp_conn *conn, + __u16 object_type, const char *object_name, + const unsigned char *passwd) +{ + long result; + ncp_init_request_s(conn, 20); + ncp_add_word(conn, htons(object_type)); + ncp_add_pstring(conn, object_name); + ncp_add_pstring(conn, passwd); + result = ncp_request(conn, 23); + ncp_unlock_conn(conn); + return result; +} + +long +ncp_login_user(struct ncp_conn *conn, + const unsigned char *username, + const unsigned char *password) +{ + return ncp_login_object(conn, username, NCP_BINDERY_USER, password); +} + +static long +ncp_login_object(struct ncp_conn *conn, + const unsigned char *username, + int login_type, + const unsigned 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 (conn->completion != NCP_GRACE_PERIOD) + { + 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; +} diff --git a/sutil/ncplib.h b/sutil/ncplib.h new file mode 100644 index 0000000..7aaaf44 --- /dev/null +++ b/sutil/ncplib.h @@ -0,0 +1,198 @@ +/* + * ncplib.h + * + * Copyright (C) 1995, 1996 by Volker Lendecke + * + */ + +#ifndef _NCPLIB_H +#define _NCPLIB_H + +#include +#include +#include +#include +#include +#include +#include + +#include "ipxlib.h" +#include "com_err.h" + +#ifndef memzero +#include +#define memzero(object) memset(&(object), 0, sizeof(object)) +#endif + +void +str_upper(char *name); + +enum connect_state { + NOT_CONNECTED = 0, + CONN_PERMANENT, + CONN_TEMPORARY +}; + +struct ncp_conn { + + enum connect_state is_connected; + + char server[NCP_BINDERY_NAME_LEN]; + char user [NCP_BINDERY_NAME_LEN]; + + struct ncp_fs_info i; + + /* Fields for use with permanent connections */ + int mount_fid; + char mount_point[MAXPATHLEN]; + + /* Fields for use with temporary connections */ + int ncp_sock; + int wdog_sock; + int wdog_pid; + __u8 sequence; + int completion; + int conn_status; + int reply_size; + + /* Fields used to setup ncp requests */ + int current_size; + int has_subfunction; + int verbose; + int ncp_reply_size; + + int lock; + + char packet[NCP_PACKET_SIZE]; +}; + +struct ncp_conn_spec { + char server[NCP_BINDERY_NAME_LEN]; + char user[NCP_BINDERY_NAME_LEN]; + uid_t uid; + int login_type; /* NCP_BINDERY_USER / NCP_BINDERY_PSERVER */ + char password[NCP_BINDERY_NAME_LEN]; +}; + +struct ncp_property_info { + __u8 property_name[16]; + __u8 property_flags; + __u8 property_security; + __u32 search_instance; + __u8 value_available_flag; + __u8 more_properties_flag; +}; + +/* ncp_initialize is the main entry point for user programs which want + to connect to a NetWare Server. It looks for -S, -U, -P and -n in + the argument list, opens the connection and removes the arguments + from the list. It was designed after the X Windows init + functions. */ +struct ncp_conn * +ncp_initialize(int *argc, char **argv, + int login_necessary, long *err); + +/* You can login as another object by this procedure. As a first use + pserver comes to mind. */ +struct ncp_conn * +ncp_initialize_as(int *argc, char **argv, + int login_necessary, int login_type, long *err); + + +/* Open a connection */ +struct ncp_conn * +ncp_open(struct ncp_conn_spec *spec, long *err); + +/* Open a connection on an existing mount point */ +struct ncp_conn * +ncp_open_mount(const char *mount_point, long *err); + +/* Find a permanent connection that fits the spec, return NULL if + * there is none. */ +char * +ncp_find_permanent(const struct ncp_conn_spec *spec); + +/* Find the address of a file server */ +struct sockaddr_ipx * +ncp_find_fileserver(char *server_name, long *err); + +/* Find the address of a server */ +struct sockaddr_ipx * +ncp_find_server(char **server_name, int type, long *err); + +/* Detach from a permanent connection or destroy a temporary + connection */ +long +ncp_close(struct ncp_conn *conn); + +/* like getmntent, get_ncp_conn_ent scans /etc/mtab for usable + connections */ + +struct ncp_conn_ent { + char server[NCP_BINDERY_NAME_LEN]; + char user[NCP_BINDERY_NAME_LEN]; + uid_t uid; + char mount_point[MAXPATHLEN]; +}; + +struct ncp_conn_ent * +ncp_get_conn_ent(FILE *filep); + +#define NWCLIENT (".nwclient") +#define NWC_NOPASSWORD ("-") + +/* find an appropriate connection */ + +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); + +long +ncp_get_encryption_key(struct ncp_conn *conn, + char *target); + +struct ncp_station_addr { + __u32 NetWork __attribute__ ((packed)); + __u8 Node[6] __attribute__ ((packed)); + __u16 Socket __attribute__ ((packed)); +}; + +struct ncp_prop_login_control { + __u8 AccountExpireDate[3] __attribute__ ((packed)); + __u8 Disabled __attribute__ ((packed)); + __u8 PasswordExpireDate[3] __attribute__ ((packed)); + __u8 GraceLogins __attribute__ ((packed)); + __u16 PasswordExpireInterval __attribute__ ((packed)); + __u8 MaxGraceLogins __attribute__ ((packed)); + __u8 MinPasswordLength __attribute__ ((packed)); + __u16 MaxConnections __attribute__ ((packed)); + __u8 ConnectionTimeMask[42] __attribute__ ((packed)); + __u8 LastLogin[6] __attribute__ ((packed)); + __u8 RestrictionMask __attribute__ ((packed)); + __u8 reserved __attribute__ ((packed)); + __u32 MaxDiskUsage __attribute__ ((packed)); + __u16 BadLoginCount __attribute__ ((packed)); + __u32 BadLoginCountDown __attribute__ ((packed)); + struct ncp_station_addr LastIntruder __attribute__ ((packed)); +}; + +long +ncp_read_property_value(struct ncp_conn *conn, + int object_type, const char *object_name, + int segment, const char *prop_name, + struct nw_property *target); + +#define NCP_GRACE_PERIOD (0xdf) + +long +ncp_get_bindery_object_id(struct ncp_conn *conn, + __u16 object_type, + const char *object_name, + struct ncp_bindery_object *target); + +long +ncp_login_user(struct ncp_conn *conn, + const unsigned char *username, + const unsigned char *password); + +#endif /* _NCPLIB_H */ diff --git a/util/ncpmount.c b/sutil/ncpmount.c similarity index 100% rename from util/ncpmount.c rename to sutil/ncpmount.c diff --git a/util/ncpumount.c b/sutil/ncpumount.c similarity index 95% rename from util/ncpumount.c rename to sutil/ncpumount.c index 2c72bf5..521776e 100644 --- a/util/ncpumount.c +++ b/sutil/ncpumount.c @@ -86,6 +86,11 @@ canonicalize (const char *path) if (realpath (path, canonical)) return canonical; + if (strlen(path) > PATH_MAX) + { + return NULL; + } + strcpy (canonical, path); return canonical; } @@ -116,7 +121,15 @@ main(int argc, char *argv[]) mount_point = canonicalize(argv[1]); + if (mount_point == NULL) + { + fprintf(stderr, "Invalid mount point: %s\n", argv[1]); + exit(1); + } + if (umount_ok(mount_point) != 0) { + fprintf(stderr, "You are not allowed to umount %s\n", + mount_point); exit(1); } diff --git a/sutil/nwcrypt.c b/sutil/nwcrypt.c new file mode 100644 index 0000000..4c028da --- /dev/null +++ b/sutil/nwcrypt.c @@ -0,0 +1,209 @@ +/*$********************************************************* +$* +$* This code has been taken from DDJ 11/93, from an +$* article by Pawel Szczerbina. +$* +$* Password encryption routines follow. +$* Converted to C from Barry Nance's Pascal +$* prog published in the March -93 issue of Byte. +$* +$* Adapted to be useable for ncpfs by +$* Volker Lendecke in +$* October 1995. +$* +$**********************************************************/ + +/**************************************************************************** + +I read that Novell is not very open when it comes to technical details +of the Netware Core Protocol. This might be especially true for the +encryption stuff. I took the necessary code from Dr. Dobb's Journal +11/93, Undocumented Corner. I asked Jon Erickson about +the legal status of this piece of code: + + +--- +Date: Thu, 12 Oct 1995 13:44:18 +0100 +From: Volker Lendecke +To: jon@ddj.com +Subject: legal status of your source code? + + +Hello! + +I hope that you're the right one to write to, you are the first on your WWW +server. If you are not, could you please forward this message to the right +person? Thanks. + +I'm currently exploring the possibility to write a free (in the GNU GPL +sense) NCP filesystem, which would allow me to access a novell server +transparently. For that I would like to use the encryption functions you +published in DDJ 11/93, Undocumented Corner. I would make some cosmetic +changes, such as other indentations, minor code changes and so on. But I do +not know if that allows me to publish this code under GPL. One alternative +would be to publish a diff against your listing, but that would probably +contain much of your code as well, and it would be very inconvenient for +the average user. + +I think that you have some kind of standard procedure for such a +case. Please tell me what I should do. + +Many thanks in advance, + + Volker + + +=================================================================+ + ! Volker Lendecke Internet: lendecke@namu01.gwdg.de ! + ! D-37081 Goettingen, Germany ! + +=================================================================+ + +-- + + +I got the following answer: + +--- +From: Jon Erickson +X-Mailer: SCO System V Mail (version 3.2) +To: lendecke@namu01.gwdg.de +Subject: Re: legal status of your source code? +Date: Thu, 12 Oct 95 5:42:56 PDT + +Volker, +Code from Dr. Dobb's Journal related articles is provided for +anyone to use. Clearly, the author of the article should be +given credit. +Jon Erickson + +--- + +With this answer in mind, I took the code and made it a bit more +C-like. The original seemed to be translated by a mechanical pascal->c +translator. Jon's answer encouraged me to publish nwcrypt.c under the +GPL. If anybody who knows more about copyright and sees any problems +with this, please tell me. +****************************************************************************/ + +/******************* Data types ***************************/ +typedef unsigned char buf32[32]; +typedef unsigned char buf16[16]; +typedef unsigned char buf8[8]; +typedef unsigned char buf4[4]; + +static unsigned char encrypttable[256] = +{0x7,0x8,0x0,0x8,0x6,0x4,0xE,0x4,0x5,0xC,0x1,0x7,0xB,0xF,0xA,0x8, + 0xF,0x8,0xC,0xC,0x9,0x4,0x1,0xE,0x4,0x6,0x2,0x4,0x0,0xA,0xB,0x9, + 0x2,0xF,0xB,0x1,0xD,0x2,0x1,0x9,0x5,0xE,0x7,0x0,0x0,0x2,0x6,0x6, + 0x0,0x7,0x3,0x8,0x2,0x9,0x3,0xF,0x7,0xF,0xC,0xF,0x6,0x4,0xA,0x0, + 0x2,0x3,0xA,0xB,0xD,0x8,0x3,0xA,0x1,0x7,0xC,0xF,0x1,0x8,0x9,0xD, + 0x9,0x1,0x9,0x4,0xE,0x4,0xC,0x5,0x5,0xC,0x8,0xB,0x2,0x3,0x9,0xE, + 0x7,0x7,0x6,0x9,0xE,0xF,0xC,0x8,0xD,0x1,0xA,0x6,0xE,0xD,0x0,0x7, + 0x7,0xA,0x0,0x1,0xF,0x5,0x4,0xB,0x7,0xB,0xE,0xC,0x9,0x5,0xD,0x1, + 0xB,0xD,0x1,0x3,0x5,0xD,0xE,0x6,0x3,0x0,0xB,0xB,0xF,0x3,0x6,0x4, + 0x9,0xD,0xA,0x3,0x1,0x4,0x9,0x4,0x8,0x3,0xB,0xE,0x5,0x0,0x5,0x2, + 0xC,0xB,0xD,0x5,0xD,0x5,0xD,0x2,0xD,0x9,0xA,0xC,0xA,0x0,0xB,0x3, + 0x5,0x3,0x6,0x9,0x5,0x1,0xE,0xE,0x0,0xE,0x8,0x2,0xD,0x2,0x2,0x0, + 0x4,0xF,0x8,0x5,0x9,0x6,0x8,0x6,0xB,0xA,0xB,0xF,0x0,0x7,0x2,0x8, + 0xC,0x7,0x3,0xA,0x1,0x4,0x2,0x5,0xF,0x7,0xA,0xC,0xE,0x5,0x9,0x3, + 0xE,0x7,0x1,0x2,0xE,0x1,0xF,0x4,0xA,0x6,0xC,0x6,0xF,0x4,0x3,0x0, + 0xC,0x0,0x3,0x6,0xF,0x8,0x7,0xB,0x2,0xD,0xC,0x6,0xA,0xA,0x8,0xD}; + +static buf32 encryptkeys = +{0x48,0x93,0x46,0x67,0x98,0x3D,0xE6,0x8D, + 0xB7,0x10,0x7A,0x26,0x5A,0xB9,0xB1,0x35, + 0x6B,0x0F,0xD5,0x70,0xAE,0xFB,0xAD,0x11, + 0xF4,0x47,0xDC,0xA7,0xEC,0xCF,0x50,0xC0}; + + +static void +shuffle1(buf32 temp, unsigned char *target) +{ + short b4; + unsigned char b3; + int s, b2, i; + + b4 = 0; + + for (b2 = 0; b2 <= 1; ++b2) + { + for (s = 0; s <= 31; ++s) + { + b3 = (temp[s]+b4) ^ (temp[(s+b4)&31] - encryptkeys[s]); + b4 = b4 + b3; + temp[s] = b3; + } + } + + for (i = 0; i <= 15; ++i) { + target[i] = encrypttable[temp[ 2*i ]] + | (encrypttable[temp[ 2*i + 1]] << 4); + } +} + + +static void +shuffle(const unsigned char *lon, const unsigned char *buf, int buflen, + unsigned char *target) +{ + int b2, d, s; + buf32 temp; + + while ( (buflen > 0) + && (buf[buflen - 1] == 0)) { + buflen = buflen - 1; + } + + for (s = 0; s < 32; s++) { + temp[s] = 0; + } + + d = 0; + while (buflen >= 32) + { + for (s = 0; s <= 31; ++s) + { + temp[s] = temp[s] ^ buf[d]; + d = d + 1; + } + buflen = buflen - 32; + } + b2 = d; + if (buflen > 0) + { + for (s = 0; s <= 31; ++s) + { + if (d + buflen == b2) + { + b2 = d; + temp[s] = temp[s] ^ encryptkeys[s]; + } else { + temp[s] = temp[s] ^ buf[b2]; + b2 = b2 + 1; + } + } + } + + for (s = 0; s <= 31; ++s) + temp[s] = temp[s] ^ lon[s & 3]; + + shuffle1(temp,target); +} + + +static void +nw_encrypt(const unsigned char *fra, + const unsigned char *buf, + unsigned char *til) +{ + buf32 k; + int s; + + shuffle(&(fra[0]), buf, 16, &(k[ 0])); + shuffle(&(fra[4]), buf, 16, &(k[16])); + + for (s = 0; s <= 15; ++s) + k[s] = k[s] ^ k[31 - s]; + + for (s = 0; s <= 7; ++s) + til[s] = k[s] ^ k[15 - s]; +} diff --git a/sutil/nwsfind.c b/sutil/nwsfind.c new file mode 100644 index 0000000..2220337 --- /dev/null +++ b/sutil/nwsfind.c @@ -0,0 +1,102 @@ +/* + * nwsfind.c + * + * Find a NetWare server and open a route to it. + * This tool can safely be setuid root, if normal users should be able to + * access any NetWare server. + * + * Copyright (C) 1996 by Volker Lendecke + * + */ + +#include "ncplib.h" +#include +#include +#include + +static char *progname; + +static void +usage(void) +{ + fprintf(stderr, "usage: %s [server]\n", progname); +} + +static void +help(void) +{ + printf("\n"); + printf("usage: %s [server]\n", progname); + printf("\n" + "-t Server type, default: File server\n" + "-h Print this help text\n" + "\n"); +} + +static void +swallow_error(const char * name, long code, const char *format, va_list arg) +{ + printf("%s ", error_message(code)); + vfprintf(stdout, format, arg); + putchar('\n'); + return; +} + +int +main(int argc, char *argv[]) +{ + char *server = NULL; + int object_type = NCP_BINDERY_FSERVER; + struct sockaddr_ipx *result; + long err; + + int opt; + + progname = argv[0]; + + set_com_err_hook(swallow_error); + + while ((opt = getopt(argc, argv, "t:")) != EOF) + { + switch(opt) { + case 't': + object_type = atoi(optarg); + break; + case 'h': + case '?': + help(); + exit(1); + default: + usage(); + exit(1); + } + } + + if (optind < argc-1) + { + usage(); + exit(1); + } + + if (optind == argc-1) + { + server = argv[optind]; + if (strlen(server) >= NCP_BINDERY_NAME_LEN) + { + com_err(argv[0], ENAMETOOLONG, "server name too long"); + exit(1); + } + } + + result = ncp_find_server(&server, object_type, &err); + + if (result == NULL) + { + com_err(argv[0], err, "when trying to find server"); + exit(1); + } + + ipx_print_saddr(result); + printf(" %s\n", server); + return 0; +} diff --git a/util/Makefile b/util/Makefile index 1b02dec..0a9e77b 100644 --- a/util/Makefile +++ b/util/Makefile @@ -5,30 +5,18 @@ USERUTILS = slist pqlist nwfsinfo pserver nprint nsend ncopy nwpasswd USERUTILS += nwbols nwbocreate nwborm nwboprops USERUTILS += nwbpcreate nwbprm nwbpvalues nwbpadd nwbpset -USERUTILS += nwgrant nwrevoke nwuserlist nwrights -UIDUTILS = ncpmount ncpumount +USERUTILS += nwgrant nwrevoke nwuserlist nwrights nwauth SBINUTILS = nwmsg -UTIL_EXECS = $(USERUTILS) $(UIDUTILS) $(SBINUTILS) -UTILS = $(addprefix $(INTERM_BINDIR)/,$(UTIL_EXECS)) +UTILS = $(USERUTILS) $(SBINUTILS) ncptest -CFLAGS = -Wall $(INCLUDES) $(KERNELD) -DNCPFS_VERSION=\"$(VERSION)\" CC = gcc -#CFLAGS += -g -CFLAGS += -O2 - ifeq ($(HAVE_ELF),yes) -PIC_FLAG = -fPIC NCP_LIB = libncp.so.1.0 -LIB_LINK_COMMAND = gcc -shared -Wl,-soname,libncp.so.1 -o $(NCP_LIB) -INSTALL_LIB = install $(NCP_LIB) -m 755 /lib; \ - ln -sf $(NCP_LIB) /lib/libncp.so.1; \ - ldconfig -export PIC_FLAG else NCP_LIB = libncp.a -LIB_LINK_COMMAND = ar r libncp.a +LIBDEP = ../lib/libncp.a endif default: @@ -37,59 +25,22 @@ default: all: $(UTILS) ncptest install: all - for i in $(UTIL_EXECS); \ - do install $(INTERM_BINDIR)/$$i -m 755 $(BINDIR); done - for i in $(UIDUTILS); \ - do install $(INTERM_BINDIR)/$$i -m 4755 $(BINDIR); done + for i in $(USERUTILS); \ + do install $$i -m 755 $(BINDIR); done for i in $(SBINUTILS); \ - do install $(INTERM_BINDIR)/$$i -m 755 $(SBINDIR); done - $(INSTALL_LIB) + do install $$i -m 755 $(SBINDIR); done -$(UTILS): $(addsuffix .o,$(UTIL_EXECS)) $(NCP_LIB) - $(CC) -o $@ $(addsuffix .o,$(notdir $@)) -L. -lncp +$(UTILS): %: %.o $(LIBDEP) + $(CC) -o $@ $(addsuffix .o,$@) -L../lib -lncp -ncplib.o: ncplib.c ncplib.h ncplib_err.h - $(CC) $(CFLAGS) $(PIC_FLAG) -finline-functions -c ncplib.c - -COM_ERR_CFILES = com_err/com_err.c com_err/error_message.c com_err/et_name.c \ - com_err/init_et.c - -$(NCP_LIB): ncplib.o ncplib_err.o $(COM_ERR_CFILES) - make -C com_err - $(LIB_LINK_COMMAND) ncplib.o ncplib_err.o \ - com_err/com_err.o com_err/error_message.o com_err/et_name.o \ - com_err/init_et.o - ln -sf libncp.so.1.0 libncp.so.1 - ln -sf libncp.so.1 libncp.so - export LD_LIBRARY_PATH=`pwd`:$LD_LIBRARY_PATH - -ncplib_err.o: ncplib_err.h ncplib_err.c - $(CC) $(CFLAGS) $(PIC_FLAG) -c ncplib_err.c - -ncplib_err.h: ncplib_err.et - com_err/compile_et ncplib_err - -ncplib_err.c: ncplib_err.et - com_err/compile_et ncplib_err - -test: test.o ncplib.o - $(CC) -o test test.o ncplib.o - -ncptest: ncptest.o $(NCP_LIB) - $(CC) -o ncptest ncptest.o -L. -lncp - -dep: ncplib_err.h - make -C com_err dep +dep: $(CPP) -M $(INCLUDES) *.c > .depend clean: - make -C com_err clean - rm -f *.o *~ slist test ncptest ncplib_err.[ch] - rm -f libncp.* + rm -f *.o *~ $(UTILS) mrproper: clean - make -C com_err mrproper - rm -f $(UTILS) .depend $(DISTFILE) + rm -f .depend # # include a dependency file if one exists diff --git a/util/com_err.h b/util/com_err.h deleted file mode 120000 index 2f20682..0000000 --- a/util/com_err.h +++ /dev/null @@ -1 +0,0 @@ -com_err/com_err.h \ No newline at end of file diff --git a/util/nwauth.c b/util/nwauth.c new file mode 100644 index 0000000..a4946ea --- /dev/null +++ b/util/nwauth.c @@ -0,0 +1,127 @@ +/* + * nwauth.c + * + * Check a user/passwd against a NetWare server + * + * Copyright (C) 1996 by Volker Lendecke + * + */ + +#include "ncplib.h" +#include +#include +#include + +static char *progname; + +static void +usage(void) +{ + fprintf(stderr, "usage: %s [options]\n", progname); +} + +static void +help(void) +{ + printf("\n"); + printf("usage: %s [options]\n", progname); + printf("\n" + "-h Print this help text\n" + "-S server Server name to be used\n" + "-U username Username sent to server\n" + "-t type Object type (decimal value)\n" + "\n"); +} + +static void +swallow_error(const char * s, long x, const char *t, va_list arg) +{ + return; +} + +int +main(int argc, char *argv[]) +{ + struct ncp_conn_spec *spec; + struct ncp_conn *conn; + char *server = NULL; + char *object_name = NULL; + int object_type = NCP_BINDERY_USER; + long err; + + char *str; + + int opt; + + progname = argv[0]; + + if (!isatty(0)) + { + set_com_err_hook(swallow_error); + } + + while ((opt = getopt(argc, argv, "h?S:U:t:")) != EOF) + { + switch(opt) { + case 'S': + server = optarg; + break; + case 'U': + object_name = optarg; + break; + case 't': + object_type = atoi(optarg); + break; + case 'h': + case '?': + help(); + exit(1); + default: + usage(); + exit(1); + } + } + + spec = ncp_find_conn_spec(server, object_name, "", + 1, getuid(), &err); + + if (spec == NULL) + { + com_err(argv[0], err, "when trying to find server"); + exit(1); + } + + if (ncp_find_fileserver(spec->server, &err) == NULL) + { + com_err(argv[0], err, "when trying to find server"); + exit(1); + } + + spec->login_type = object_type; + memset(spec->password, 0, sizeof(spec->password)); + + if (isatty(0)) + { + str = getpass("Enter password: "); + if (strlen(str) >= sizeof(spec->password)) + { + printf("Password too long\n"); + exit(1); + } + strcpy(spec->password, str); + } + else + { + fgets(spec->password, sizeof(spec->password), stdin); + } + + str_upper(spec->password); + + if ((conn = ncp_open(spec, &err)) == NULL) + { + com_err(argv[0], err, "when trying to open connection"); + exit(1); + } + ncp_close(conn); + return 0; +} diff --git a/util/pserver.c b/util/pserver.c index a80b389..203ff38 100644 --- a/util/pserver.c +++ b/util/pserver.c @@ -260,6 +260,70 @@ init_queue(struct ncp_conn *conn, char *queue_name, char *command, } +void +build_command(struct nw_queue *q, struct queue_job *j, + char *target, int target_size) +{ + char *s = q->command; + char *target_end = target+target_size; + + void add_string(char *s) + { + int len = strlen(s); + if (target + len + 1 > target_end) + { + len = target_end - target - 1; + } + strncpy(target, s, len); + target += len; + } + + + memset(target, 0, target_size); + + while ((*s != 0) && (target < target_end)) + { + if (*s != '%') + { + *target = *s; + target += 1; + s += 1; + continue; + } + + switch (*(s+1)) + { + case '%': + *target = '%'; + target += 1; + case 'u': + { + char *user; + struct ncp_bindery_object u; + if (ncp_get_bindery_object_name + (q->conn, j->j.ClientObjectID, &u) + == 0) + { + user = u.object_name; + } + else + { + user = "*UNKNOWN USER*"; + } + add_string(user); + } + default: + *target = '%'; + *(target+1) = *(s+1); + target += 2; + } + s += 2; + } +} + + + + static int poll_queue(struct nw_queue *q) { @@ -316,6 +380,8 @@ poll_queue(struct nw_queue *q) { /* child */ + char command[2048]; + close(fd[1]); /* close write end */ if (fd[0] != STDIN_FILENO) @@ -329,7 +395,9 @@ poll_queue(struct nw_queue *q) close(fd[0]); } - execl("/bin/sh", "sh", "-c", q->command, NULL); + build_command(q, &job, command, sizeof(command)); + + execl("/bin/sh", "sh", "-c", command, NULL); syslog(LOG_ERR, "exec error: %m\n"); close(fd[0]); exit(1);