From f813517d69f3af32e376c74372aeb779ae4d940e Mon Sep 17 00:00:00 2001 From: ncpfs archive import Date: Tue, 28 Apr 2026 20:39:59 +0200 Subject: [PATCH] Import ncpfs 2.0.12 --- .downloads/ncpfs-2.0.12.tgz | Bin 0 -> 195454 bytes Changes | 79 ++ FAQ | 19 +- Makefile | 32 +- README | 30 +- README.NDS | 18 + include/glibstub.h | 22 + include/ipxlib.h | 44 +- include/kernel/fs.h | 11 + include/kernel/if.h | 11 + include/kernel/ipx.h | 11 + include/kernel/ncp.h | 196 +++ include/kernel/ncp_fs.h | 88 ++ include/kernel/route.h | 12 + include/kernel/types.h | 40 + include/ncp.h | 122 ++ include/ncplib.h | 70 +- include/ncpsign.h | 18 + include/ndslib.h | 35 + ipx-1.0/Makefile | 2 +- ipx-1.0/ipx_configure.c | 2 +- ipx-1.0/ipx_interface.c | 12 +- ipx-1.0/ipx_internal_net.c | 8 +- ipx-1.0/ipx_route.c | 8 +- ipxdump/Makefile | 2 +- ipxdump/ipxdump.c | 13 +- ipxdump/ipxparse.c | 13 +- ipxdump/ipxutil.h | 2 +- kernel-1.2/linux/ncp.h | 317 ----- kernel-1.2/linux/ncp_fs.h | 166 --- kernel-1.2/linux/ncp_fs_i.h | 38 - kernel-1.2/linux/ncp_fs_sb.h | 76 -- kernel-1.2/linux/ncp_mount.h | 50 - kernel-1.2/src/Makefile | 65 - kernel-1.2/src/dir.c | 1245 ----------------- kernel-1.2/src/file.c | 273 ---- kernel-1.2/src/inode.c | 493 ------- kernel-1.2/src/ioctl.c | 159 --- kernel-1.2/src/mmap.c | 155 --- kernel-1.2/src/ncplib_kernel.c | 622 --------- kernel-1.2/src/ncplib_kernel.h | 163 --- kernel-1.2/src/sock.c | 555 -------- lib/Makefile | 25 +- lib/mpilib.c | 1881 ++++++++++++++++++++++++++ lib/mpilib.h | 468 +++++++ lib/ncplib.c | 502 ++++++- lib/ncplib_err.et | 14 +- lib/ncpsign.c | 103 ++ lib/ndscrypt.c | 295 ++++ lib/ndscrypt.h | 47 + lib/ndslib.c | 1235 +++++++++++++++++ lib/nwcrypt.c | 2 +- lib/platform.h | 218 +++ lib/usuals.h | 41 + man/pqrm.1 | 105 ++ man/pqstat.1 | 107 ++ man/pserver.1 | 3 + ncpfs-2.0.11.lsm => ncpfs-2.0.12.lsm | 11 +- ncpfs-nds-0.06.CHANGES | 96 ++ ncpfs-nds-0.06.README | 42 + patches/README | 6 +- patches/linux-2.1.26.diff | 35 - sutil/Makefile | 22 +- sutil/ipxlib.h | 98 -- sutil/ncplib.c | 462 ++++++- sutil/ncplib.h | 58 +- sutil/ncpmount.c | 772 +++++++++-- sutil/ncpmount.h | 76 ++ sutil/ncpumount.c | 10 +- sutil/nwcrypt.c | 2 +- sutil/nwsfind.c | 40 +- util/Makefile | 9 +- util/ipx_probe.c | 456 +++++++ util/ncopy.c | 2 +- util/ncptest.c | 9 +- util/nsend.c | 18 +- util/nwbpvalues.c | 10 +- util/nwmsg.c | 1 + util/pqrm.c | 71 + util/pqstat.c | 188 +++ util/pserver.c | 50 +- 81 files changed, 8104 insertions(+), 4783 deletions(-) create mode 100644 .downloads/ncpfs-2.0.12.tgz create mode 100644 README.NDS create mode 100644 include/glibstub.h create mode 100644 include/kernel/fs.h create mode 100644 include/kernel/if.h create mode 100644 include/kernel/ipx.h create mode 100644 include/kernel/ncp.h create mode 100644 include/kernel/ncp_fs.h create mode 100644 include/kernel/route.h create mode 100644 include/kernel/types.h create mode 100644 include/ncp.h create mode 100644 include/ncpsign.h create mode 100644 include/ndslib.h delete mode 100644 kernel-1.2/linux/ncp.h delete mode 100644 kernel-1.2/linux/ncp_fs.h delete mode 100644 kernel-1.2/linux/ncp_fs_i.h delete mode 100644 kernel-1.2/linux/ncp_fs_sb.h delete mode 100644 kernel-1.2/linux/ncp_mount.h delete mode 100644 kernel-1.2/src/Makefile delete mode 100644 kernel-1.2/src/dir.c delete mode 100644 kernel-1.2/src/file.c delete mode 100644 kernel-1.2/src/inode.c delete mode 100644 kernel-1.2/src/ioctl.c delete mode 100644 kernel-1.2/src/mmap.c delete mode 100644 kernel-1.2/src/ncplib_kernel.c delete mode 100644 kernel-1.2/src/ncplib_kernel.h delete mode 100644 kernel-1.2/src/sock.c create mode 100644 lib/mpilib.c create mode 100644 lib/mpilib.h create mode 100644 lib/ncpsign.c create mode 100644 lib/ndscrypt.c create mode 100644 lib/ndscrypt.h create mode 100644 lib/ndslib.c create mode 100644 lib/platform.h create mode 100644 lib/usuals.h create mode 100644 man/pqrm.1 create mode 100644 man/pqstat.1 rename ncpfs-2.0.11.lsm => ncpfs-2.0.12.lsm (75%) create mode 100644 ncpfs-nds-0.06.CHANGES create mode 100644 ncpfs-nds-0.06.README delete mode 100644 patches/linux-2.1.26.diff delete mode 100644 sutil/ipxlib.h create mode 100644 sutil/ncpmount.h create mode 100644 util/ipx_probe.c create mode 100644 util/pqrm.c create mode 100644 util/pqstat.c diff --git a/.downloads/ncpfs-2.0.12.tgz b/.downloads/ncpfs-2.0.12.tgz new file mode 100644 index 0000000000000000000000000000000000000000..87049eff9f1a94b69a8b5fe32c2d39c3f85d767d GIT binary patch literal 195454 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%0D*2POwKYlF()V{7qRn1Ewu&!)|;n> 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℘bn%I3y@4Ckzs*pRxTNfG7%|0bCAt=8sp@&X2$JJ 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%0ANrp_3H2xUjIG`VhT{g`hsi0 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 literal 0 HcmV?d00001 diff --git a/Changes b/Changes index 14ed13e..c87dfd0 100644 --- a/Changes +++ b/Changes @@ -1,6 +1,85 @@ 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. +[Versions ncpfs-2.0.11.x are available at ftp://platan.vc.cvut.cz/pub/linux/ncpfs] + +ncpfs-2.0.11.19 -> ncpfs-2.0.12 +- Polished for release 2.0.12 +- Dave: pserver fixes, including addition of %d flag. + +ncpfs-2.0.11.18 -> ncpfs-2.0.11.19 +- Dave, VANA: new userspace utilities - pqstat and pqrm + +ncpfs-2.0.11.17 -> ncpfs-2.0.11.18 (no userspace change) +- Dave: getwd() did not work on 2.1.x +- VANA: Volumes are now allways listed lowercased on 2.1.x + +ncpfs-2.0.11.16 -> ncpfs-2.0.11.17 +- Dave@imladris.demon.co.uk: Patch to pserver, ncp_get_broadcast_message +- VANA & Dave: Cleanup for glibc, I hope that complete (xcpt. few warnings + about long int vs. u_int32_t + +ncpfs-2.0.11.15 -> ncpfs-2.0.11.16 +- VANA: Removed symlink latest from archive :-) +- VANA: Added ncp_send_broadcast2 + +ncpfs-2.0.11.14 -> ncpfs-2.0.11.15 +- VANA: Fixed bug: wrong completion code returned when login to server without + r/w replica of logged-in user +- VANA: It is possible to disable NFS and/or OS2 namespace support in mount + +ncpfs-2.0.11.13 -> ncpfs-2.0.11.14 +- VANA: One source for 2.0 and 2.1 kernels +- Chirstian Groessler: Added strong mounts + +ncpfs-2.0.11.12 -> ncpfs-2.0.11.13 +- Arne: Signatures added to ncpfs-nds-0.06 +- VANA: Synchronized sources with Arne + +ncpfs-2.0.11.11 -> ncpfs-2.0.11.12 +- VANA: Fixed compilation error if compiled against kernel without signatures even + if SIGNATURES = 0 set + +ncpfs-2.0.11.10 -> ncpfs-2.0.11.11 +- VANA: Fixed segfault on invalid user name in NDS mode +- VANA: Added locking features (through ncpfs-specific ioctl(2)) + +ncpfs-2.0.11.9 -> ncpfs-2.0.11.10 +- VANA: Synchronized with nds-patches-0.05 from Arne@knoware.nl + +ncpfs-2.0.11.8 -> ncpfs-2.0.11.9 +- VANA: Added call to lock connection (dropped in 2.0.11.7, sorry) + +ncpfs-2.0.11.7 -> ncpfs-2.0.11.8 +- VANA: Can be correctly compiled without signatures support in kernel (I hope) +- VANA: Fix in kernel in setting task number + +ncpfs-2.0.11.6 -> ncpfs-2.0.11.7 +- VANA: Codebase synchronized with arne@knoware.nl +- ARNE: Gracelogins on NDS +- VANA: Removed some (one) compilation warnings + +ncpfs-2.0.11.5 -> ncpfs-2.0.11.6 +- VANA: Support for NDS login accross servers + +ncpfs-2.0.11.4 -> ncpfs-2.0.11.5 +- VANA: Cleanup in ndscrypt +- VANA: Bugfix: empty password for NDS login is now allowed & works + +ncpfs-2.0.11.3 -> ncpfs-2.0.11.4 +- Enabled some buffer cleaning +- Added parameter "-b" to ncpmount for bindery login to NDS server + +ncpfs-2.0.11.2 -> ncpfs-2.0.11.3 +- Added NDS support by Arne@knoware.nl; small fixes in code; moved sections + to do same things as DOS login does. + +ncpfs-2.0.11.1 -> ncpfs-2.0.11.2 +- VANA: Fixed that some error conditions in LOGIN should start packet signatures + +ncpfs-2.0.11 -> ncpfs-2.0.11.1 +- VANA: Added packet signatures by ... (Arne?) + ncpfs-2.0.10 -> ncpfs-2.0.11 - Added Martin's patch to Linux 2.0.30 to get rid of the lockups. MANY thanks to Martin Stover! diff --git a/FAQ b/FAQ index 8a3d947..f08ec41 100644 --- a/FAQ +++ b/FAQ @@ -35,6 +35,9 @@ doing this. A promising hint that has already helped some people is to switch off packet signatures on the 4.1 server, as ncpfs does not support them. +Note: ncpfs, as of 2.0.12, and kernel 2.1.89, does now support packet +signatures. + ------------------------------------------------------------------------------- Q: When I re-export ncpfs-mounted directories via nfs, I get messages like @@ -54,14 +57,10 @@ When you want to export a directory via NFS, you have to do two things: ------------------------------------------------------------------------------- -Q: When I compile ncpfs, I get a message like the following: +Q: I cannot login into server with these utilities. It was possible with an +older version. -make[1]: Entering directory `/home/me/netware/ncpfs/kernel-1.2/src' -gcc -D__KERNEL__ -I. -Wall -Wstrict-prototypes -O2 -DMODULE -fomit-frame-pointer -I/home/me/netware/ncpfs/kernel-1.2 -DNCPFS_VERSION=\"0.17\" -c dir.c -dir.c:36: warning: `struct dirent' declared inside parameter list -dir.c:36: warning: its scope is only this definition or declaration, -... - -You try to compile the part of ncpfs that is meant for kernel 1.2.13 under -kernel 1.3.x. Please look at the Makefile and comment out the -corresponding lines. +A: You are probably connecting into Netware 4.x or IntraNetware. If you want a +temporary workaround, add the option "-b" to the ncpmount commandline. +For the future you should determine your Directory Services user name and +use that instead of your bindery name. diff --git a/Makefile b/Makefile index 233f9d6..01c4dcf 100644 --- a/Makefile +++ b/Makefile @@ -2,7 +2,7 @@ # Makefile for the linux ncp-filesystem routines. # -VERSION = 2.0.11 +VERSION = 2.0.12 # If you are using kerneld to autoload ncp support, # uncomment this (kerneld is in linux since about 1.3.57): @@ -14,6 +14,24 @@ KERNELD = -DHAVE_KERNELD HAVE_ELF=$(shell file `whereis gcc|cut -d ' ' -f 2`| \ grep ELF >/dev/null && echo -n yes ) +# If you want to include NDS support for ncpmount uncomment this: +# WARNING! NDS support is very beta, uncomment only if you are testing +# because anything can happen (like crashing the linux box or nw server). +NDS_SUPPORT = 1 + +# If you want to include packet signature support uncomment this: +# WARNING! packet signature support is in beta stage, uncomment only when you +# know what you are doing, anything can happen (like crashing the linux box or +# netware server). +# When enabling, make sure you have applied the kernel patches too, +# otherwise the packet signatures won't work. +SIGNATURES = 1 + +# Include code for Linux2.0.x +MOUNT2 = 1 +# Include code for Linux2.1.x +MOUNT3 = 1 + TOPDIR = $(shell pwd) BINDIR = /usr/bin SBINDIR = /sbin @@ -23,19 +41,15 @@ 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 -endif - COPT = -O2 -COPT += -g +# COPT += -g CFLAGS = $(COPT) -Wall $(INCLUDES) $(KERNELD) -DNCPFS_VERSION=\"$(VERSION)\" -export INCLUDES BINDIR SBINDIR KERNELD VERSION HAVE_ELF CFLAGS +export INCLUDES BINDIR SBINDIR KERNELD VERSION HAVE_ELF CFLAGS NDS_SUPPORT \ + SIGNATURES MOUNT2 MOUNT3 all: - for i in $(SUBDIRS); do make -C $$i all; done + set -e; for i in $(SUBDIRS); do make -C $$i all; done @if [ "$(HAVE_ELF)" = yes ] ;\ then \ echo ; echo ; echo ;\ diff --git a/README b/README index 612fe00..20b41ba 100644 --- a/README +++ b/README @@ -32,28 +32,18 @@ The IPX protocol (CONFIG_IPX) [N/y/m/?] simply answer 'y'. Probably you do not need the full internal net that you are asked for next. -The installation of ncpfs depends on the kernel version you are -using. For kernel 1.2, you should simply type 'make' and look at -what's in the bin/ directory after that. Please be sure that your -kernel resides in /usr/src/linux, because the file -kernel-1.2/src/sock.c has to refer directly to it. - -If you use Kernel 1.3, please be sure that you use at least -1.3.71. ncpfs does NOT work with any earlier 1.3.x kernel. - -If you use Kernel 1.3.71 or later, you might have to recompile your -kernel. With these kernels, the kernel part of ncpfs is already -included in the main source tree. If you want to use ncpfs, you should -say 'y' to 'make config' when you are asked for IPX, and again when it -asks for ncpfs. After you have rebooted with the new kernel, 'cat -/proc/filesystems' should show you a line saying that the kernel knows -ncpfs. - -If you are running kerneld, please uncomment the corresponding line in +If you are not running kerneld, please comment the corresponding line in the Makefile to reflect this. -If your system is ELF, please enable the use of the shared ncp-library -in the Makefile. This will save at least 1MB of disk space. +If you are not using 2.0.x kernels, you can comment out MOUNT2=1 line +in the Makefile. +If you are not using 2.1.x kernels, you can comment out MOUNT3=1 line +in the Makefile. + +If you are not using NDS access, you can comment out NDS_SUPPORT=1 in +the Makefile. +If you are not using packet signatures, you can comment out SIGNATURES=1 +in the Makefile. After you adapted your Makefile, type 'make' and, as root, 'make install'. diff --git a/README.NDS b/README.NDS new file mode 100644 index 0000000..6ba5590 --- /dev/null +++ b/README.NDS @@ -0,0 +1,18 @@ +The NDS login code uses the RSA public key cryptosystem. Because of a patent +right on this algorithm (U.S. patent #4,405,829, issued 20 Sep 1983), you +are probably not allowed to use this code in the U.S.A. and Canada, and +possibly neither in other countries. Check this before you use NDS logins! + +The mpilib.c, mpilib.h, platform.h, and usuals.h in the lib/ directory are +taken from the PGP 2.3 source distribution (Copyright 1986-92 by Philip +Zimmermann), which is distributed under the GPL, as stated below. + +Excerpt from pgpdoc2.txt (contained in pgp23src.zip): +"All the source code for PGP is available for free under the "Copyleft" +General Public License from the Free Software Foundation (FSF)." + +For more details on the RSA patent see the pgp23src archive, or more recent +PGP packages. + +Arne de Bruijn +arne@knoware.nl diff --git a/include/glibstub.h b/include/glibstub.h new file mode 100644 index 0000000..89b0332 --- /dev/null +++ b/include/glibstub.h @@ -0,0 +1,22 @@ +#ifndef __GLIBSTUB_H__ +#define __GLIBSTUB_H__ + +#undef GLIBCHDR +#ifdef __GLIBC__ +#if __GLIBC__ >= 2 +#define GLIBCHDR +#endif +#endif +#ifdef GLIBCHDR +#define HAVE_NETIPX_IPX_H +#define HAVE_SYS_MOUNT_H +#define HAVE_NET_ROUTE_H +#define HAVE_NET_IF_H +#else +#undef HAVE_NETIPX_IPX_H +#undef HAVE_SYS_MOUNT_H +#undef HAVE_NET_ROUTE_H +#undef HAVE_NET_IF_H +#endif +#endif /* __GLIBSTUB_H__ */ + diff --git a/include/ipxlib.h b/include/ipxlib.h index bf16bf4..17b09ff 100644 --- a/include/ipxlib.h +++ b/include/ipxlib.h @@ -8,16 +8,13 @@ #ifndef _IPXLIB_H #define _IPXLIB_H +#include "kernel/types.h" +#include "ncp.h" +#include "kernel/ipx.h" -#include -#include -#include -#include -#include - -typedef unsigned long IPXNet; -typedef unsigned short IPXPort; -typedef unsigned char IPXNode[IPX_NODE_LEN]; +typedef u_int32_t IPXNet; +typedef u_int16_t IPXPort; +typedef u_int8_t IPXNode[IPX_NODE_LEN]; #define IPX_USER_PTYPE (0x00) #define IPX_RIP_PTYPE (0x01) @@ -35,18 +32,18 @@ typedef unsigned char IPXNode[IPX_NODE_LEN]; struct sap_query { - unsigned short query_type; /* net order */ - unsigned short server_type; /* net order */ + u_int16_t query_type; /* net order */ + u_int16_t 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)); + u_int16_t 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)); + u_int16_t intermediate_network __attribute__((packed)); }; #define IPX_RIP_REQUEST (0x1) @@ -54,12 +51,12 @@ struct sap_server_ident struct ipx_rip_packet { - __u16 operation __attribute__((packed)); + u_int16_t operation __attribute__((packed)); struct ipx_rt_def { - __u32 network __attribute__((packed)); - __u16 hops __attribute__((packed)); - __u16 ticks __attribute__((packed)); + u_int16_t network __attribute__((packed)); + u_int16_t hops __attribute__((packed)); + u_int16_t ticks __attribute__((packed)); } rt[1] __attribute__((packed)); }; @@ -95,4 +92,9 @@ void int ipx_node_equal(IPXNode n1, IPXNode n2); +#ifdef __MAKE_NCPMOUNT__ +int +ipx_sscanf_saddr(char* buf, struct sockaddr_ipx* sipx); +#endif + #endif /* _IPXLIB_H */ diff --git a/include/kernel/fs.h b/include/kernel/fs.h new file mode 100644 index 0000000..ae00f2f --- /dev/null +++ b/include/kernel/fs.h @@ -0,0 +1,11 @@ +#ifndef _KERNEL_FS_H +#define _KERNEL_FS_H + +#include "glibstub.h" +#ifdef HAVE_SYS_MOUNT_H +#include +#else +#include +#endif + +#endif diff --git a/include/kernel/if.h b/include/kernel/if.h new file mode 100644 index 0000000..14dee6d --- /dev/null +++ b/include/kernel/if.h @@ -0,0 +1,11 @@ +#ifndef _KERNEL_IF_H +#define _KERNEL_IF_H + +#include "glibstub.h" +#ifdef HAVE_NET_IF_H +#include +#else +#include +#endif + +#endif diff --git a/include/kernel/ipx.h b/include/kernel/ipx.h new file mode 100644 index 0000000..e6cff29 --- /dev/null +++ b/include/kernel/ipx.h @@ -0,0 +1,11 @@ +#ifndef _KERNEL_IPX_H +#define _KERNEL_IPX_H + +#include "glibstub.h" +#ifdef HAVE_NETIPX_IPX_H +#include +#else +#include +#endif + +#endif diff --git a/include/kernel/ncp.h b/include/kernel/ncp.h new file mode 100644 index 0000000..17518db --- /dev/null +++ b/include/kernel/ncp.h @@ -0,0 +1,196 @@ +/* + * ncp.h + * + * Copyright (C) 1995 by Volker Lendecke + * Modified for sparc by J.F. Chadima + * + */ + +#ifndef _LINUX_NCP_H +#define _LINUX_NCP_H + +#include "kernel/types.h" +#include "kernel/ipx.h" + +#define NCP_PTYPE (0x11) +#define NCP_PORT (0x0451) + +#define NCP_ALLOC_SLOT_REQUEST (0x1111) +#define NCP_REQUEST (0x2222) +#define NCP_DEALLOC_SLOT_REQUEST (0x5555) + +struct ncp_request_header { + u_int16_t type __attribute__((packed)); + u_int8_t sequence __attribute__((packed)); + u_int8_t conn_low __attribute__((packed)); + u_int8_t task __attribute__((packed)); + u_int8_t conn_high __attribute__((packed)); + u_int8_t function __attribute__((packed)); + u_int8_t data[0] __attribute__((packed)); +}; + +#define NCP_REPLY (0x3333) +#define NCP_POSITIVE_ACK (0x9999) + +struct ncp_reply_header { + __u16 type __attribute__((packed)); + __u8 sequence __attribute__((packed)); + __u8 conn_low __attribute__((packed)); + __u8 task __attribute__((packed)); + __u8 conn_high __attribute__((packed)); + __u8 completion_code __attribute__((packed)); + __u8 connection_state __attribute__((packed)); + __u8 data[0] __attribute__((packed)); +}; + +#define NCP_VOLNAME_LEN (16) +#define NCP_NUMBER_OF_VOLUMES (64) +struct ncp_volume_info { + __u32 total_blocks; + __u32 free_blocks; + __u32 purgeable_blocks; + __u32 not_yet_purgeable_blocks; + __u32 total_dir_entries; + __u32 available_dir_entries; + __u8 sectors_per_block; + char volume_name[NCP_VOLNAME_LEN + 1]; +}; + +/* these define the attribute byte as seen by NCP */ +#define aRONLY (ntohl(0x01000000)) +#define aHIDDEN (ntohl(0x02000000)) +#define aSYSTEM (ntohl(0x04000000)) +#define aEXECUTE (ntohl(0x08000000)) +#define aDIR (ntohl(0x10000000)) +#define aARCH (ntohl(0x20000000)) + +#define AR_READ (ntohs(0x0100)) +#define AR_WRITE (ntohs(0x0200)) +#define AR_EXCLUSIVE (ntohs(0x2000)) + +#define NCP_FILE_ID_LEN 6 + +/* Defines for Name Spaces */ +#define NW_NS_DOS 0 +#define NW_NS_MAC 1 +#define NW_NS_NFS 2 +#define NW_NS_FTAM 3 +#define NW_NS_OS2 4 + +/* Defines for ReturnInformationMask */ +#define RIM_NAME (ntohl(0x01000000L)) +#define RIM_SPACE_ALLOCATED (ntohl(0x02000000L)) +#define RIM_ATTRIBUTES (ntohl(0x04000000L)) +#define RIM_DATA_SIZE (ntohl(0x08000000L)) +#define RIM_TOTAL_SIZE (ntohl(0x10000000L)) +#define RIM_EXT_ATTR_INFO (ntohl(0x20000000L)) +#define RIM_ARCHIVE (ntohl(0x40000000L)) +#define RIM_MODIFY (ntohl(0x80000000L)) +#define RIM_CREATION (ntohl(0x00010000L)) +#define RIM_OWNING_NAMESPACE (ntohl(0x00020000L)) +#define RIM_DIRECTORY (ntohl(0x00040000L)) +#define RIM_RIGHTS (ntohl(0x00080000L)) +#define RIM_ALL (ntohl(0xFF0F0000L)) +#define RIM_COMPRESSED_INFO (ntohl(0x00000080L)) + +/* open/create modes */ +#define OC_MODE_OPEN 0x01 +#define OC_MODE_TRUNCATE 0x02 +#define OC_MODE_REPLACE 0x02 +#define OC_MODE_CREATE 0x08 + +/* open/create results */ +#define OC_ACTION_NONE 0x00 +#define OC_ACTION_OPEN 0x01 +#define OC_ACTION_CREATE 0x02 +#define OC_ACTION_TRUNCATE 0x04 +#define OC_ACTION_REPLACE 0x04 + +/* access rights attributes */ +#ifndef AR_READ_ONLY +#define AR_READ_ONLY 0x0001 +#define AR_WRITE_ONLY 0x0002 +#define AR_DENY_READ 0x0004 +#define AR_DENY_WRITE 0x0008 +#define AR_COMPATIBILITY 0x0010 +#define AR_WRITE_THROUGH 0x0040 +#define AR_OPEN_COMPRESSED 0x0100 +#endif + +struct nw_info_struct { + __u32 spaceAlloc __attribute__((packed)); + __u32 attributes __attribute__((packed)); + __u16 flags __attribute__((packed)); + __u32 dataStreamSize __attribute__((packed)); + __u32 totalStreamSize __attribute__((packed)); + __u16 numberOfStreams __attribute__((packed)); + __u16 creationTime __attribute__((packed)); + __u16 creationDate __attribute__((packed)); + __u32 creatorID __attribute__((packed)); + __u16 modifyTime __attribute__((packed)); + __u16 modifyDate __attribute__((packed)); + __u32 modifierID __attribute__((packed)); + __u16 lastAccessDate __attribute__((packed)); + __u16 archiveTime __attribute__((packed)); + __u16 archiveDate __attribute__((packed)); + __u32 archiverID __attribute__((packed)); + __u16 inheritedRightsMask __attribute__((packed)); + __u32 dirEntNum __attribute__((packed)); + __u32 DosDirNum __attribute__((packed)); + __u32 volNumber __attribute__((packed)); + __u32 EADataSize __attribute__((packed)); + __u32 EAKeyCount __attribute__((packed)); + __u32 EAKeySize __attribute__((packed)); + __u32 NSCreator __attribute__((packed)); + __u8 nameLen __attribute__((packed)); + __u8 entryName[256] __attribute__((packed)); +}; + +/* modify mask - use with MODIFY_DOS_INFO structure */ +#define DM_ATTRIBUTES (ntohl(0x02000000L)) +#define DM_CREATE_DATE (ntohl(0x04000000L)) +#define DM_CREATE_TIME (ntohl(0x08000000L)) +#define DM_CREATOR_ID (ntohl(0x10000000L)) +#define DM_ARCHIVE_DATE (ntohl(0x20000000L)) +#define DM_ARCHIVE_TIME (ntohl(0x40000000L)) +#define DM_ARCHIVER_ID (ntohl(0x80000000L)) +#define DM_MODIFY_DATE (ntohl(0x00010000L)) +#define DM_MODIFY_TIME (ntohl(0x00020000L)) +#define DM_MODIFIER_ID (ntohl(0x00040000L)) +#define DM_LAST_ACCESS_DATE (ntohl(0x00080000L)) +#define DM_INHERITED_RIGHTS_MASK (ntohl(0x00100000L)) +#define DM_MAXIMUM_SPACE (ntohl(0x00200000L)) + +struct nw_modify_dos_info { + __u32 attributes __attribute__((packed)); + __u16 creationDate __attribute__((packed)); + __u16 creationTime __attribute__((packed)); + __u32 creatorID __attribute__((packed)); + __u16 modifyDate __attribute__((packed)); + __u16 modifyTime __attribute__((packed)); + __u32 modifierID __attribute__((packed)); + __u16 archiveDate __attribute__((packed)); + __u16 archiveTime __attribute__((packed)); + __u32 archiverID __attribute__((packed)); + __u16 lastAccessDate __attribute__((packed)); + __u16 inheritanceGrantMask __attribute__((packed)); + __u16 inheritanceRevokeMask __attribute__((packed)); + __u32 maximumSpace __attribute__((packed)); +}; + +struct nw_file_info { + struct nw_info_struct i; + int opened; + int access; + __u32 server_file_handle __attribute__((packed)); + __u8 open_create_action __attribute__((packed)); + __u8 file_handle[6] __attribute__((packed)); +}; + +struct nw_search_sequence { + __u8 volNumber __attribute__((packed)); + __u32 dirBase __attribute__((packed)); + __u32 sequence __attribute__((packed)); +}; + +#endif /* _LINUX_NCP_H */ diff --git a/include/kernel/ncp_fs.h b/include/kernel/ncp_fs.h new file mode 100644 index 0000000..93becf0 --- /dev/null +++ b/include/kernel/ncp_fs.h @@ -0,0 +1,88 @@ +/* + * ncp_fs.h + * + * Copyright (C) 1995, 1996 by Volker Lendecke + * + */ + +#ifndef _KERNEL_NCP_FS_H +#define _KERNEL_NCP_FS_H + +#include "kernel/fs.h" +#include +#include + +/* + * ioctl commands + */ + +struct ncp_ioctl_request { + unsigned int function; + unsigned int size; + char *data; +}; + +struct ncp_fs_info { + int version; + struct sockaddr_ipx addr; + __kerXX_uid_t mounted_uid; + int connection; /* Connection number the server assigned us */ + int buffer_size; /* The negotiated buffer size, to be + used for read/write requests! */ + + int volume_number; + __u32 directory_id; +}; + +struct ncp_sign_init +{ + char sign_root[8]; + char sign_last[16]; +}; + +struct ncp_lock_ioctl +{ +#define NCP_LOCK_LOG 0 +#define NCP_LOCK_SH 1 +#define NCP_LOCK_EX 2 +#define NCP_LOCK_CLEAR 256 + int cmd; + int origin; + unsigned int offset; + unsigned int length; +#define NCP_LOCK_DEFAULT_TIMEOUT 18 +#define NCP_LOCK_MAX_TIMEOUT 180 + int timeout; +}; + +struct ncp_setroot_ioctl +{ + int volNumber; + int namespace; + __u32 dirEntNum; +}; + +#define NCP_IOC_NCPREQUEST _IOR('n', 1, struct ncp_ioctl_request) +#define NCP_IOC_GETMOUNTUID _IOW('n', 2, __kernel_uid_t) +#define NCP_IOC_CONN_LOGGED_IN _IO('n', 3) + +#define NCP_GET_FS_INFO_VERSION (1) +#define NCP_IOC_GET_FS_INFO _IOWR('n', 4, struct ncp_fs_info) + +#define NCP_IOC_SIGN_INIT _IOR('n', 5, struct ncp_sign_init) +#define NCP_IOC_SIGN_WANTED _IOR('n', 6, int) +#define NCP_IOC_SET_SIGN_WANTED _IOW('n', 6, int) + +#define NCP_IOC_LOCKUNLOCK _IOR('n', 7, struct ncp_lock_ioctl) + +#define NCP_IOC_GETROOT _IOW('n', 8, struct ncp_setroot_ioctl) +#define NCP_IOC_SETROOT _IOR('n', 8, struct ncp_setroot_ioctl) +/* + * The packet size to allocate. One page should be enough. + */ +#define NCP_PACKET_SIZE 4070 + +#define NCP_MAXPATHLEN 255 +#define NCP_MAXNAMELEN 14 + +#endif /* _LINUX_NCP_FS_H */ diff --git a/include/kernel/route.h b/include/kernel/route.h new file mode 100644 index 0000000..f5c0b83 --- /dev/null +++ b/include/kernel/route.h @@ -0,0 +1,12 @@ +#ifndef __KERNEL_ROUTE_H__ +#define __KERNEL_ROUTE_H__ + +#include "glibstub.h" +#ifdef HAVE_NET_ROUTE_H +#include +#else +#include +#endif + +#endif /* __KERNEL_ROUTE_H__ */ + diff --git a/include/kernel/types.h b/include/kernel/types.h new file mode 100644 index 0000000..fe37968 --- /dev/null +++ b/include/kernel/types.h @@ -0,0 +1,40 @@ +#ifndef __KERNEL_TYPES_H__ +#define __KERNEL_TYPES_H__ + +#include + +#undef __u8 +#undef __u16 +#undef __u32 +#define __u8 u_int8_t +#define __u16 u_int16_t +#define __u32 u_int32_t + +typedef u_int16_t __kerXX_uid_t; + +#include + +typedef __kernel_pid_t __ker20_pid_t; +typedef __kernel_uid_t __ker20_uid_t; +typedef __kernel_gid_t __ker20_gid_t; +typedef __kernel_mode_t __ker20_mode_t; + +typedef __kernel_pid_t __ker21_pid_t; +typedef __kernel_uid_t __ker21_uid_t; +typedef __kernel_gid_t __ker21_gid_t; +typedef __kernel_mode_t __ker21_mode_t; + +#ifdef __GLIBC__ +/* why is this defined in posix_types ???? dirty hack... */ +#undef __FD_CLR +#undef __FD_SET +#undef __FD_ISSET +#undef __FD_ZERO +#ifdef _SELECTBITS_H +#undef _SELECTBITS_H +#include +#endif +#endif + +#endif /* __KERNEL_TYPES_H__ */ + diff --git a/include/ncp.h b/include/ncp.h new file mode 100644 index 0000000..29361ba --- /dev/null +++ b/include/ncp.h @@ -0,0 +1,122 @@ +/* + * ncp.h + * + * Copyright (C) 1995 by Volker Lendecke + * Modified for sparc by J.F. Chadima + * + */ + +#ifndef _NCP_H +#define _NCP_H + +#include "kernel/types.h" +#include "kernel/ipx.h" +#include "kernel/ncp.h" +#include "kernel/ncp_fs.h" + +#define NCP_BINDERY_USER (0x0001) +#define NCP_BINDERY_UGROUP (0x0002) +#define NCP_BINDERY_PQUEUE (0x0003) +#define NCP_BINDERY_FSERVER (0x0004) +#define NCP_BINDERY_NAME_LEN (48) +struct ncp_bindery_object { + __u32 object_id; + __u16 object_type; + __u8 object_name[NCP_BINDERY_NAME_LEN]; + __u8 object_flags; + __u8 object_security; + __u8 object_has_prop; +}; + +struct nw_property { + __u8 value[128]; + __u8 more_flag; + __u8 property_flag; +}; + +struct prop_net_address { + __u32 network __attribute__((packed)); + __u8 node[IPX_NODE_LEN] __attribute__((packed)); + __u16 port __attribute__((packed)); +}; + +struct ncp_filesearch_info { + __u8 volume_number; + __u16 directory_id; + __u16 sequence_no; + __u8 access_rights; +}; + +#define NCP_MAX_FILENAME (14) +struct ncp_file_info { + __u8 file_id[NCP_FILE_ID_LEN]; + char file_name[NCP_MAX_FILENAME + 1]; + __u8 file_attributes; + __u8 file_mode; + __u32 file_length; + __u16 creation_date; + __u16 access_date; + __u16 update_date; + __u16 update_time; +}; + +struct nw_queue_job_entry { + __u16 InUse __attribute__((packed)); + __u32 prev __attribute__((packed)); + __u32 next __attribute__((packed)); + __u32 ClientStation __attribute__((packed)); + __u32 ClientTask __attribute__((packed)); + __u32 ClientObjectID __attribute__((packed)); + __u32 TargetServerID __attribute__((packed)); + __u8 TargetExecTime[6] __attribute__((packed)); + __u8 JobEntryTime[6] __attribute__((packed)); + __u32 JobNumber __attribute__((packed)); + __u16 JobType __attribute__((packed)); + __u16 JobPosition __attribute__((packed)); + __u16 JobControlFlags __attribute__((packed)); + __u8 FileNameLen __attribute__((packed)); + char JobFileName[13] __attribute__((packed)); + __u32 JobFileHandle __attribute__((packed)); + __u32 ServerStation __attribute__((packed)); + __u32 ServerTaskNumber __attribute__((packed)); + __u32 ServerObjectID __attribute__((packed)); + char JobTextDescription[50] __attribute__((packed)); + char ClientRecordArea[152] __attribute__((packed)); +}; + +struct queue_job { + struct nw_queue_job_entry j; + __u8 file_handle[6]; +}; + +#define QJE_OPER_HOLD 0x80 +#define QJE_USER_HOLD 0x40 +#define QJE_ENTRYOPEN 0x20 +#define QJE_SERV_RESTART 0x10 +#define QJE_SERV_AUTO 0x08 + +/* ClientRecordArea for print jobs */ + +#define KEEP_ON 0x0400 +#define NO_FORM_FEED 0x0800 +#define NOTIFICATION 0x1000 +#define DELETE_FILE 0x2000 +#define EXPAND_TABS 0x4000 +#define PRINT_BANNER 0x8000 + +struct print_job_record { + __u8 Version __attribute__((packed)); + __u8 TabSize __attribute__((packed)); + __u16 Copies __attribute__((packed)); + __u16 CtrlFlags __attribute__((packed)); + __u16 Lines __attribute__((packed)); + __u16 Rows __attribute__((packed)); + char FormName[16] __attribute__((packed)); + __u8 Reserved[6] __attribute__((packed)); + char BannerName[13] __attribute__((packed)); + char FnameBanner[13] __attribute__((packed)); + char FnameHeader[14] __attribute__((packed)); + char Path[80] __attribute__((packed)); +}; + +#endif /* _NCP_H */ diff --git a/include/ncplib.h b/include/ncplib.h index e0f2af7..d8d12a9 100644 --- a/include/ncplib.h +++ b/include/ncplib.h @@ -8,14 +8,17 @@ #ifndef _NCPLIB_H #define _NCPLIB_H -#include -#include -#include -#include +#include "ncp.h" #include #include #include +#ifdef SIGNATURES +#ifndef NCP_IOC_SIGN_INIT +#undef SIGNATURES +#endif /* NCP_IOC_SIGN_INIT */ +#endif /* SIGNATURES */ + #include "ipxlib.h" #include "com_err.h" @@ -153,6 +156,13 @@ struct ncp_conn int lock; char packet[NCP_PACKET_SIZE]; +#ifdef SIGNATURES + /* Fields used to make packet signatures */ + int sign_wanted; + int sign_active; + char sign_root[8]; + char sign_last[16]; +#endif }; struct ncp_conn_spec @@ -302,6 +312,11 @@ long __u8 no_conn, const __u8 * connections, const char *message); +long + ncp_send_broadcast2(struct ncp_conn *conn, + unsigned int conns, const unsigned int* connlist, + const char* message); + long ncp_get_encryption_key(struct ncp_conn *conn, char *target); @@ -438,6 +453,7 @@ long const unsigned char *oldpasswd, const unsigned char *newpasswd); +#define NWE_SIGNATURE_LEVEL_CONFLICT (0x8861) #define NCP_GRACE_PERIOD (0xdf) long @@ -614,6 +630,27 @@ long __u32 queue_id, struct queue_job *job); +long + ncp_get_queue_length(struct ncp_conn *conn, + __u32 queue_id, + __u32 *queue_length); + +long + ncp_get_queue_job_ids(struct ncp_conn *conn, + __u32 queue_id, + __u32 queue_section, + __u32 *length1, + __u32 *length2, + __u32 ids[]); +long + ncp_get_queue_job_info(struct ncp_conn *conn, + __u32 queue_id, + __u32 job_id, + struct nw_queue_job_entry *jobdata); + +long +NWRemoveJobFromQueue2(struct ncp_conn* conn, __u32 queue_id, __u32 job_id); + long ncp_close_file_and_start_job(struct ncp_conn *conn, __u32 queue_id, @@ -671,6 +708,31 @@ long __u8 volume_number, __u32 dir_entry, __u16 rights_mask, int object_count, struct ncp_trustee_struct *rights); +#ifdef SIGNATURES +long +ncp_sign_start(struct ncp_conn *conn, const char *sign_root); +#endif +#ifdef NDS_SUPPORT +long +ncp_send_nds_frag(struct ncp_conn *conn, + int ndsverb, + char *inbuf, int inbuflen, + char *outbuf, int outbufsize, int *outbuflen); +long +ncp_send_nds(struct ncp_conn *conn, int fn, + char *data_in, int data_in_len, + char *data_out, int data_out_max, int *data_out_len); + +long +ncp_change_conn_state(struct ncp_conn *conn, int new_state); + +struct ncp_conn * +ncp_open_addr(struct sockaddr_ipx *target, long *err); + +int +ncp_get_mount_uid(int fid, uid_t* uid); + +#endif #endif /* _NCPLIB_H */ diff --git a/include/ncpsign.h b/include/ncpsign.h new file mode 100644 index 0000000..72d8f8e --- /dev/null +++ b/include/ncpsign.h @@ -0,0 +1,18 @@ +#ifdef SIGNATURES +/* + * ncpsign.h + * + * Arne de Bruijn (arne@knoware.nl), 1997 + * + */ + +#ifndef _NCPSIGN_H +#define _NCPSIGN_H + +#include "ncplib.h" + +void sign_init(const char *logindata, char *sign_root); +void sign_packet(struct ncp_conn *conn, int *size); + +#endif +#endif diff --git a/include/ndslib.h b/include/ndslib.h new file mode 100644 index 0000000..89a4813 --- /dev/null +++ b/include/ndslib.h @@ -0,0 +1,35 @@ +/* + NDS client for ncpfs + Copyright (C) 1997 Arne de Bruijn + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifndef _NDSLIB_H_ +#define _NDSLIB_H_ + +#include "ncplib.h" +typedef unsigned short uni_char; + +#define NDS_GRACE_PERIOD -223 + +int strlen_u(const uni_char *s); +void strcpy_uc(char *d, const uni_char *s); +void strcpy_cu(uni_char *d, const char *s); +long nds_get_server_name(struct ncp_conn *conn, uni_char **server_name); +long nds_get_tree_name(struct ncp_conn *conn, char *name, int name_buf_len); +long nds_login_auth(struct ncp_conn *conn, const char *user, const char *pwd); + +#endif /* ifndef _NDSLIB_H_ */ diff --git a/ipx-1.0/Makefile b/ipx-1.0/Makefile index c12bf8d..2335a5d 100644 --- a/ipx-1.0/Makefile +++ b/ipx-1.0/Makefile @@ -1,4 +1,4 @@ -CFLAGS = -O2 -Wall +CFLAGS = -O2 -Wall -I../include UTILS = ipx_configure ipx_interface ipx_internal_net ipx_route all: $(UTILS) diff --git a/ipx-1.0/ipx_configure.c b/ipx-1.0/ipx_configure.c index 1f4d754..37ce88e 100644 --- a/ipx-1.0/ipx_configure.c +++ b/ipx-1.0/ipx_configure.c @@ -8,7 +8,7 @@ #include #include #include -#include +#include "kernel/ipx.h" #include #include #include diff --git a/ipx-1.0/ipx_interface.c b/ipx-1.0/ipx_interface.c index 105c529..1c59cfe 100644 --- a/ipx-1.0/ipx_interface.c +++ b/ipx-1.0/ipx_interface.c @@ -11,8 +11,8 @@ #include #include #include -#include -#include +#include "kernel/ipx.h" +#include "kernel/if.h" #include #include #include @@ -176,8 +176,8 @@ ipx_add_interface(int argc, char **argv) progname); break; case EADDRINUSE: - fprintf(stderr, "%s: Network number (%08lX) already in use.\n", - progname, htonl(sipx->sipx_network)); + fprintf(stderr, "%s: Network number (%08X) already in use.\n", + progname, (u_int32_t)htonl(sipx->sipx_network)); break; case EPROTONOSUPPORT: fprintf(stderr, "%s: Invalid frame type (%s).\n", @@ -372,9 +372,9 @@ ipx_check_interface(int argc, char **argv) if (result == 0) { printf( - "IPX Address for (%s, %s) is %08lX:%02X%02X%02X%02X%02X%02X.\n", + "IPX Address for (%s, %s) is %08X:%02X%02X%02X%02X%02X%02X.\n", argv[1], frame_types[fti].ft_name, - htonl(sipx->sipx_network), sipx->sipx_node[0], + (u_int32_t)htonl(sipx->sipx_network), sipx->sipx_node[0], sipx->sipx_node[1], sipx->sipx_node[2], sipx->sipx_node[3], sipx->sipx_node[4], sipx->sipx_node[5]); diff --git a/ipx-1.0/ipx_internal_net.c b/ipx-1.0/ipx_internal_net.c index 52e37ab..004a8c7 100644 --- a/ipx-1.0/ipx_internal_net.c +++ b/ipx-1.0/ipx_internal_net.c @@ -9,8 +9,8 @@ #include #include #include -#include -#include +#include "kernel/ipx.h" +#include "kernel/if.h" #include #include #include @@ -129,8 +129,8 @@ ipx_add_internal_net(int argc, char **argv) progname); break; case EADDRINUSE: - fprintf(stderr, "%s: Network number (%08lX) already in use.\n", - progname, htonl(sipx->sipx_network)); + fprintf(stderr, "%s: Network number (%08X) already in use.\n", + progname, (u_int32_t)htonl(sipx->sipx_network)); break; case EAGAIN: fprintf(stderr, diff --git a/ipx-1.0/ipx_route.c b/ipx-1.0/ipx_route.c index 8f0489d..47df63b 100644 --- a/ipx-1.0/ipx_route.c +++ b/ipx-1.0/ipx_route.c @@ -11,11 +11,11 @@ #include #include #include -#include +#include "kernel/ipx.h" #include #include #include -#include +#include "kernel/route.h" static struct rtentry rd; static char *progname; @@ -142,8 +142,8 @@ ipx_add_route(int argc, char **argv) switch (errno) { case ENETUNREACH: - fprintf(stderr, "%s: Router network (%08lX) not reachable.\n", - progname, htonl(sr->sipx_network)); + fprintf(stderr, "%s: Router network (%08X) not reachable.\n", + progname, (u_int32_t)htonl(sr->sipx_network)); break; default: sprintf(errmsg, "%s: ioctl", progname); diff --git a/ipxdump/Makefile b/ipxdump/Makefile index b67fab8..08372dd 100644 --- a/ipxdump/Makefile +++ b/ipxdump/Makefile @@ -1,6 +1,6 @@ EXEC= ipxdump ipxparse -CFLAGS= -Wall -O2 +CFLAGS= -Wall -O2 -I../include OBJECTS= ipxutil.o all: $(EXEC) diff --git a/ipxdump/ipxdump.c b/ipxdump/ipxdump.c index c85b024..164797b 100644 --- a/ipxdump/ipxdump.c +++ b/ipxdump/ipxdump.c @@ -21,14 +21,13 @@ #include #include #include -#include +#include #include #include -#include +#include #include -#include -#include -#include +#include +#include #include #include #include @@ -226,9 +225,9 @@ handle_frame(unsigned char *buf, int length, struct sockaddr *saddr) { /* Ethernet packet type ID field */ unsigned short packet_type = ((struct ethhdr *) buf)->h_proto; - switch (packet_type) + switch (htons(packet_type)) { - case __constant_ntohs(ETH_P_IPX): + case ETH_P_IPX: handle_ipx("EtherII", &(buf[sizeof(struct ethhdr)])); break; default: diff --git a/ipxdump/ipxparse.c b/ipxdump/ipxparse.c index b612e26..43991ff 100644 --- a/ipxdump/ipxparse.c +++ b/ipxdump/ipxparse.c @@ -21,20 +21,23 @@ #include #include #include -#include +#include #include #include -#include +#include #include -#include -#include -#include +#include +#include #include #include #include #include #include "ipxutil.h" +#define __u8 u_int8_t +#define __u16 u_int16_t +#define __u32 u_int32_t + #define DUMPALLSAPS /* #define if you want to dump all SAP's */ struct ipx_address diff --git a/ipxdump/ipxutil.h b/ipxdump/ipxutil.h index e259a58..3c7a78d 100644 --- a/ipxdump/ipxutil.h +++ b/ipxdump/ipxutil.h @@ -25,7 +25,7 @@ #define __IPXUTIL_H__ #include -#include +#include "kernel/ipx.h" #define IPX_MAX_ERROR (255) #define IPX_THIS_NET (0) diff --git a/kernel-1.2/linux/ncp.h b/kernel-1.2/linux/ncp.h deleted file mode 100644 index 88d9261..0000000 --- a/kernel-1.2/linux/ncp.h +++ /dev/null @@ -1,317 +0,0 @@ -/* - * ncp.h - * - * Copyright (C) 1995 by Volker Lendecke - * - */ - -#ifndef _LINUX_NCP_H -#define _LINUX_NCP_H - -#include -#include - -#define NCP_PTYPE (0x11) -#define NCP_PORT (0x0451) - -#define NCP_ALLOC_SLOT_REQUEST (0x1111) -#define NCP_REQUEST (0x2222) -#define NCP_DEALLOC_SLOT_REQUEST (0x5555) - -struct ncp_request_header -{ - __u16 type __attribute__((packed)); - __u8 sequence __attribute__((packed)); - __u8 conn_low __attribute__((packed)); - __u8 task __attribute__((packed)); - __u8 conn_high __attribute__((packed)); - __u8 function __attribute__((packed)); - __u8 data[0] __attribute__((packed)); -}; - -#define NCP_REPLY (0x3333) -#define NCP_POSITIVE_ACK (0x9999) - -struct ncp_reply_header -{ - __u16 type __attribute__((packed)); - __u8 sequence __attribute__((packed)); - __u8 conn_low __attribute__((packed)); - __u8 task __attribute__((packed)); - __u8 conn_high __attribute__((packed)); - __u8 completion_code __attribute__((packed)); - __u8 connection_state __attribute__((packed)); - __u8 data[0] __attribute__((packed)); -}; - - -#define NCP_BINDERY_USER (0x0001) -#define NCP_BINDERY_UGROUP (0x0002) -#define NCP_BINDERY_PQUEUE (0x0003) -#define NCP_BINDERY_FSERVER (0x0004) -#define NCP_BINDERY_NAME_LEN (48) -struct ncp_bindery_object -{ - __u32 object_id; - __u16 object_type; - __u8 object_name[NCP_BINDERY_NAME_LEN]; - __u8 object_flags; - __u8 object_security; - __u8 object_has_prop; -}; - -struct nw_property -{ - __u8 value[128]; - __u8 more_flag; - __u8 property_flag; -}; - -struct prop_net_address -{ - __u32 network __attribute__((packed)); - __u8 node[IPX_NODE_LEN] __attribute__((packed)); - __u16 port __attribute__((packed)); -}; - -#define NCP_VOLNAME_LEN (16) -#define NCP_NUMBER_OF_VOLUMES (64) -struct ncp_volume_info -{ - __u32 total_blocks; - __u32 free_blocks; - __u32 purgeable_blocks; - __u32 not_yet_purgeable_blocks; - __u32 total_dir_entries; - __u32 available_dir_entries; - __u8 sectors_per_block; - char volume_name[NCP_VOLNAME_LEN + 1]; -}; - -struct ncp_filesearch_info -{ - __u8 volume_number; - __u16 directory_id; - __u16 sequence_no; - __u8 access_rights; -}; - -#define NCP_MAX_FILENAME 14 - -/* these define the attribute byte as seen by NCP */ -#define aRONLY (1L<<0) -#define aHIDDEN (1L<<1) -#define aSYSTEM (1L<<2) -#define aEXECUTE (1L<<3) -#define aDIR (1L<<4) -#define aARCH (1L<<5) - -#define AR_READ (0x01) -#define AR_WRITE (0x02) -#define AR_EXCLUSIVE (0x20) - -#define NCP_FILE_ID_LEN 6 -struct ncp_file_info -{ - __u8 file_id[NCP_FILE_ID_LEN]; - char file_name[NCP_MAX_FILENAME + 1]; - __u8 file_attributes; - __u8 file_mode; - __u32 file_length; - __u16 creation_date; - __u16 access_date; - __u16 update_date; - __u16 update_time; -}; - -/* Defines for Name Spaces */ -#define NW_NS_DOS 0 -#define NW_NS_MAC 1 -#define NW_NS_NFS 2 -#define NW_NS_FTAM 3 -#define NW_NS_OS2 4 - -/* Defines for ReturnInformationMask */ -#define RIM_NAME (0x0001L) -#define RIM_SPACE_ALLOCATED (0x0002L) -#define RIM_ATTRIBUTES (0x0004L) -#define RIM_DATA_SIZE (0x0008L) -#define RIM_TOTAL_SIZE (0x0010L) -#define RIM_EXT_ATTR_INFO (0x0020L) -#define RIM_ARCHIVE (0x0040L) -#define RIM_MODIFY (0x0080L) -#define RIM_CREATION (0x0100L) -#define RIM_OWNING_NAMESPACE (0x0200L) -#define RIM_DIRECTORY (0x0400L) -#define RIM_RIGHTS (0x0800L) -#define RIM_ALL (0x0FFFL) -#define RIM_COMPRESSED_INFO (0x80000000L) - -/* open/create modes */ -#define OC_MODE_OPEN 0x01 -#define OC_MODE_TRUNCATE 0x02 -#define OC_MODE_REPLACE 0x02 -#define OC_MODE_CREATE 0x08 - -/* open/create results */ -#define OC_ACTION_NONE 0x00 -#define OC_ACTION_OPEN 0x01 -#define OC_ACTION_CREATE 0x02 -#define OC_ACTION_TRUNCATE 0x04 -#define OC_ACTION_REPLACE 0x04 - -/* access rights attributes */ -#ifndef AR_READ_ONLY -#define AR_READ_ONLY 0x0001 -#define AR_WRITE_ONLY 0x0002 -#define AR_DENY_READ 0x0004 -#define AR_DENY_WRITE 0x0008 -#define AR_COMPATIBILITY 0x0010 -#define AR_WRITE_THROUGH 0x0040 -#define AR_OPEN_COMPRESSED 0x0100 -#endif - -struct nw_info_struct -{ - __u32 spaceAlloc __attribute__((packed)); - __u32 attributes __attribute__((packed)); - __u16 flags __attribute__((packed)); - __u32 dataStreamSize __attribute__((packed)); - __u32 totalStreamSize __attribute__((packed)); - __u16 numberOfStreams __attribute__((packed)); - __u16 creationTime __attribute__((packed)); - __u16 creationDate __attribute__((packed)); - __u32 creatorID __attribute__((packed)); - __u16 modifyTime __attribute__((packed)); - __u16 modifyDate __attribute__((packed)); - __u32 modifierID __attribute__((packed)); - __u16 lastAccessDate __attribute__((packed)); - __u16 archiveTime __attribute__((packed)); - __u16 archiveDate __attribute__((packed)); - __u32 archiverID __attribute__((packed)); - __u16 inheritedRightsMask __attribute__((packed)); - __u32 dirEntNum __attribute__((packed)); - __u32 DosDirNum __attribute__((packed)); - __u32 volNumber __attribute__((packed)); - __u32 EADataSize __attribute__((packed)); - __u32 EAKeyCount __attribute__((packed)); - __u32 EAKeySize __attribute__((packed)); - __u32 NSCreator __attribute__((packed)); - __u8 nameLen __attribute__((packed)); - __u8 entryName[256] __attribute__((packed)); -}; - -/* modify mask - use with MODIFY_DOS_INFO structure */ -#define DM_ATTRIBUTES (0x0002L) -#define DM_CREATE_DATE (0x0004L) -#define DM_CREATE_TIME (0x0008L) -#define DM_CREATOR_ID (0x0010L) -#define DM_ARCHIVE_DATE (0x0020L) -#define DM_ARCHIVE_TIME (0x0040L) -#define DM_ARCHIVER_ID (0x0080L) -#define DM_MODIFY_DATE (0x0100L) -#define DM_MODIFY_TIME (0x0200L) -#define DM_MODIFIER_ID (0x0400L) -#define DM_LAST_ACCESS_DATE (0x0800L) -#define DM_INHERITED_RIGHTS_MASK (0x1000L) -#define DM_MAXIMUM_SPACE (0x2000L) - -struct nw_modify_dos_info -{ - __u32 attributes __attribute__((packed)); - __u16 creationDate __attribute__((packed)); - __u16 creationTime __attribute__((packed)); - __u32 creatorID __attribute__((packed)); - __u16 modifyDate __attribute__((packed)); - __u16 modifyTime __attribute__((packed)); - __u32 modifierID __attribute__((packed)); - __u16 archiveDate __attribute__((packed)); - __u16 archiveTime __attribute__((packed)); - __u32 archiverID __attribute__((packed)); - __u16 lastAccessDate __attribute__((packed)); - __u16 inheritanceGrantMask __attribute__((packed)); - __u16 inheritanceRevokeMask __attribute__((packed)); - __u32 maximumSpace __attribute__((packed)); -}; - -struct nw_file_info -{ - struct nw_info_struct i; - int opened; - int access; - __u32 server_file_handle __attribute__((packed)); - __u8 open_create_action __attribute__((packed)); - __u8 file_handle[6] __attribute__((packed)); -}; - -struct nw_search_sequence -{ - __u8 volNumber __attribute__((packed)); - __u32 dirBase __attribute__((packed)); - __u32 sequence __attribute__((packed)); -}; - -struct nw_queue_job_entry -{ - __u16 InUse __attribute__((packed)); - __u32 prev __attribute__((packed)); - __u32 next __attribute__((packed)); - __u32 ClientStation __attribute__((packed)); - __u32 ClientTask __attribute__((packed)); - __u32 ClientObjectID __attribute__((packed)); - __u32 TargetServerID __attribute__((packed)); - __u8 TargetExecTime[6] __attribute__((packed)); - __u8 JobEntryTime[6] __attribute__((packed)); - __u32 JobNumber __attribute__((packed)); - __u16 JobType __attribute__((packed)); - __u16 JobPosition __attribute__((packed)); - __u16 JobControlFlags __attribute__((packed)); - __u8 FileNameLen __attribute__((packed)); - char JobFileName[13] __attribute__((packed)); - __u32 JobFileHandle __attribute__((packed)); - __u32 ServerStation __attribute__((packed)); - __u32 ServerTaskNumber __attribute__((packed)); - __u32 ServerObjectID __attribute__((packed)); - char JobTextDescription[50] __attribute__((packed)); - char ClientRecordArea[152] __attribute__((packed)); -}; - -struct queue_job -{ - struct nw_queue_job_entry j; - __u8 file_handle[6]; -}; - -#define QJE_OPER_HOLD 0x80 -#define QJE_USER_HOLD 0x40 -#define QJE_ENTRYOPEN 0x20 -#define QJE_SERV_RESTART 0x10 -#define QJE_SERV_AUTO 0x08 - -/* ClientRecordArea for print jobs */ - -#define KEEP_ON 0x0400 -#define NO_FORM_FEED 0x0800 -#define NOTIFICATION 0x1000 -#define DELETE_FILE 0x2000 -#define EXPAND_TABS 0x4000 -#define PRINT_BANNER 0x8000 - -struct print_job_record -{ - __u8 Version __attribute__((packed)); - __u8 TabSize __attribute__((packed)); - __u16 Copies __attribute__((packed)); - __u16 CtrlFlags __attribute__((packed)); - __u16 Lines __attribute__((packed)); - __u16 Rows __attribute__((packed)); - char FormName[16] __attribute__((packed)); - __u8 Reserved[6] __attribute__((packed)); - char BannerName[13] __attribute__((packed)); - char FnameBanner[13] __attribute__((packed)); - char FnameHeader[14] __attribute__((packed)); - char Path[80] __attribute__((packed)); -}; - - -#endif /* _LINUX_NCP_H */ diff --git a/kernel-1.2/linux/ncp_fs.h b/kernel-1.2/linux/ncp_fs.h deleted file mode 100644 index 116b289..0000000 --- a/kernel-1.2/linux/ncp_fs.h +++ /dev/null @@ -1,166 +0,0 @@ -/* - * ncp_fs.h - * - * Copyright (C) 1995 by Volker Lendecke - * - */ - -#ifndef _LINUX_NCP_FS_H -#define _LINUX_NCP_FS_H - -#include -#include -#include - -#include -#include -#include - -/* - * ioctl commands - */ - -struct ncp_ioctl_request -{ - unsigned int function; - unsigned int size; - char *data; -}; - -struct ncp_fs_info -{ - int version; - struct sockaddr_ipx addr; - uid_t mounted_uid; - int connection; /* Connection number the server assigned us */ - int buffer_size; /* The negotiated buffer size, to be - used for read/write requests! */ - - int volume_number; - __u32 directory_id; -}; - -#define NCP_IOC_NCPREQUEST _IOR('n', 1, struct ncp_ioctl_request) -#define NCP_IOC_GETMOUNTUID _IOW('n', 2, uid_t) -#define NCP_IOC_CONN_LOGGED_IN _IO('n', 3) - -#define NCP_GET_FS_INFO_VERSION (1) -#define NCP_IOC_GET_FS_INFO _IOWR('n', 4, struct ncp_fs_info) - -/* - * The packet size to allocate. One page should be enough. - */ -#define NCP_PACKET_SIZE 4070 - -#define NCP_MAXPATHLEN 255 -#define NCP_MAXNAMELEN 14 - -#ifdef __KERNEL__ - -/* The readdir cache size controls how many directory entries are - * cached. - */ -#define NCP_READDIR_CACHE_SIZE 64 - - -#define NCP_MAX_RPC_TIMEOUT (6*HZ) - -/* Guess, what 0x564c is :-) */ -#define NCP_SUPER_MAGIC 0x564c - - -#define NCP_SBP(sb) ((struct ncp_server *)((sb)->u.generic_sbp)) -#define NCP_INOP(inode) ((struct ncp_inode_info *)((inode)->u.generic_ip)) - -#define NCP_SERVER(inode) NCP_SBP((inode)->i_sb) -#define NCP_FINFO(inode) (&(NCP_INOP(inode)->finfo)) -#define NCP_ISTRUCT(inode) (&(NCP_FINFO(inode)->i)) - -#ifdef DEBUG_NCP_MALLOC - -#include - -extern int ncp_malloced; -extern int ncp_current_malloced; - -static inline void * -ncp_kmalloc(unsigned int size, int priority) -{ - ncp_malloced += 1; - ncp_current_malloced += 1; - return kmalloc(size, priority); -} - -static inline void -ncp_kfree_s(void *obj, int size) -{ - ncp_current_malloced -= 1; - kfree_s(obj, size); -} - -#else /* DEBUG_NCP_MALLOC */ - -#define ncp_kmalloc(s,p) kmalloc(s,p) -#define ncp_kfree_s(o,s) kfree_s(o,s) - -#endif /* DEBUG_NCP_MALLOC */ - -#if DEBUG_NCP > 0 -#define DPRINTK(format, args...) printk(format , ## args) -#else -#define DPRINTK(format, args...) -#endif - -#if DEBUG_NCP > 1 -#define DDPRINTK(format, args...) printk(format , ## args) -#else -#define DDPRINTK(format, args...) -#endif - - -/* linux/fs/ncpfs/file.c */ -extern struct inode_operations ncp_file_inode_operations; -int ncp_make_open(struct inode *i, int right); - -/* linux/fs/ncpfs/dir.c */ -extern struct inode_operations ncp_dir_inode_operations; -void ncp_free_inode_info(struct ncp_inode_info *i); -void ncp_free_all_inodes(struct ncp_server *server); -void ncp_init_root(struct ncp_server *server); -int ncp_conn_logged_in(struct ncp_server *server); -int ncp_stat_root(struct ncp_server *server); -void ncp_init_dir_cache(void); -void ncp_invalid_dir_cache(struct inode *ino); -struct ncp_inode_info *ncp_find_inode(struct inode *inode); -ino_t ncp_info_ino(struct ncp_server *server, struct ncp_inode_info *info); -void ncp_invalidate_all_inodes(struct ncp_server *server); -void ncp_free_dir_cache(void); -int ncp_date_dos2unix(__u16 time, __u16 date); -void ncp_date_unix2dos(int unix_date, __u16 * time, __u16 * date); - - -/* linux/fs/ncpfs/ioctl.c */ -int ncp_ioctl(struct inode *inode, struct file *filp, - unsigned int cmd, unsigned long arg); - -/* linux/fs/ncpfs/inode.c */ -struct super_block *ncp_read_super(struct super_block *sb, - void *raw_data, int silent); -void ncp_invalidate_connection(struct ncp_server *server); -int ncp_conn_is_valid(struct ncp_server *server); - -/* linux/fs/ncpfs/sock.c */ -int ncp_request(struct ncp_server *server, int function); -int ncp_connect(struct ncp_server *server); -int ncp_disconnect(struct ncp_server *server); -int ncp_catch_watchdog(struct ncp_server *server); -int ncp_dont_catch_watchdog(struct ncp_server *server); -void ncp_lock_server(struct ncp_server *server); -void ncp_unlock_server(struct ncp_server *server); - -/* linux/fs/ncpfs/mmap.c */ -int ncp_mmap(struct inode *inode, struct file *file, struct vm_area_struct *vma); - -#endif /* __KERNEL__ */ - -#endif /* _LINUX_NCP_FS_H */ diff --git a/kernel-1.2/linux/ncp_fs_i.h b/kernel-1.2/linux/ncp_fs_i.h deleted file mode 100644 index b36821f..0000000 --- a/kernel-1.2/linux/ncp_fs_i.h +++ /dev/null @@ -1,38 +0,0 @@ -/* - * ncp_fs_i.h - * - * Copyright (C) 1995 by Paal-Kr. Engstad and Volker Lendecke - * - */ - -#ifndef _LINUX_NCP_FS_I -#define _LINUX_NCP_FS_I - -#include - -#ifdef __KERNEL__ - -enum ncp_inode_state -{ - NCP_INODE_VALID = 19, /* Inode currently in use */ - NCP_INODE_LOOKED_UP, /* directly before iget */ - NCP_INODE_CACHED, /* in a path to an inode which is in use */ - NCP_INODE_INVALID -}; - -/* - * ncp fs inode data (in memory only) - */ -struct ncp_inode_info -{ - enum ncp_inode_state state; - int nused; /* for directories: - number of references in memory */ - struct ncp_inode_info *dir; - struct ncp_inode_info *next, *prev; - struct inode *inode; - struct nw_file_info finfo; -}; - -#endif -#endif diff --git a/kernel-1.2/linux/ncp_fs_sb.h b/kernel-1.2/linux/ncp_fs_sb.h deleted file mode 100644 index ccdff2a..0000000 --- a/kernel-1.2/linux/ncp_fs_sb.h +++ /dev/null @@ -1,76 +0,0 @@ -/* - * ncp_fs_sb.h - * - * Copyright (C) 1995 by Volker Lendecke - * - */ - -#ifndef _NCP_FS_SB -#define _NCP_FS_SB - -#include -#include - -#ifdef __KERNEL__ - -#define NCP_DEFAULT_BUFSIZE 1024 - -struct ncp_server -{ - - struct ncp_mount_data m; /* Nearly all of the mount data is of - interest for us later, so we store - it completely. */ - - __u8 name_space[NCP_NUMBER_OF_VOLUMES]; - - struct file *ncp_filp; /* File pointer to ncp socket */ - struct file *wdog_filp; /* File pointer to wdog socket */ - void *data_ready; /* The wdog socket gets a new - data_ready callback. We store the - old one for checking purposes and - to reset it on unmounting. */ - - u8 sequence; - u8 task; - u16 connection; /* Remote connection number */ - - u8 completion; /* Status message from server */ - u8 conn_status; /* Bit 4 = 1 ==> Server going down, no - requests allowed anymore. - Bit 0 = 1 ==> Server is down. */ - - int buffer_size; /* Negotiated bufsize */ - - int reply_size; /* Size of last reply */ - - int packet_size; - unsigned char *packet; /* Here we prepare requests and - receive replies */ - - int lock; /* To prevent mismatch in protocols. */ - struct wait_queue *wait; - - int current_size; /* for packet preparation */ - int has_subfunction; - int ncp_reply_size; - - struct ncp_inode_info root; - char root_path; /* '\0' */ -}; - -static inline int -ncp_conn_valid(struct ncp_server *server) -{ - return ((server->conn_status & 0x11) == 0); -} - -static inline void -ncp_invalidate_conn(struct ncp_server *server) -{ - server->conn_status |= 0x01; -} - -#endif /* __KERNEL__ */ - -#endif diff --git a/kernel-1.2/linux/ncp_mount.h b/kernel-1.2/linux/ncp_mount.h deleted file mode 100644 index 9399a41..0000000 --- a/kernel-1.2/linux/ncp_mount.h +++ /dev/null @@ -1,50 +0,0 @@ -/* - * ncp_mount.h - * - * Copyright (C) 1995 by Volker Lendecke - * - */ - -#ifndef _LINUX_NCP_MOUNT_H -#define _LINUX_NCP_MOUNT_H - -#include -#include -#include -#include - -#define NCP_MOUNT_VERSION 2 - -#define NCP_USERNAME_LEN (NCP_BINDERY_NAME_LEN) -#define NCP_PASSWORD_LEN 20 - -/* Values for flags */ -#define NCP_MOUNT_SOFT 0x0001 -#define NCP_MOUNT_INTR 0x0002 - -struct ncp_mount_data -{ - int version; - unsigned int ncp_fd; /* The socket to the ncp port */ - unsigned int wdog_fd; /* Watchdog packets come here */ - unsigned int message_fd; /* Message notifications come here */ - uid_t mounted_uid; /* Who may umount() this filesystem? */ - - struct sockaddr_ipx serv_addr; - unsigned char server_name[NCP_BINDERY_NAME_LEN]; - - unsigned char mount_point[PATH_MAX + 1]; - unsigned char mounted_vol[NCP_VOLNAME_LEN + 1]; - - unsigned int time_out; /* How long should I wait after - sending a NCP request? */ - unsigned int retry_count; /* And how often should I retry? */ - unsigned int flags; - - uid_t uid; - gid_t gid; - mode_t file_mode; - mode_t dir_mode; -}; - -#endif diff --git a/kernel-1.2/src/Makefile b/kernel-1.2/src/Makefile deleted file mode 100644 index 2bfa208..0000000 --- a/kernel-1.2/src/Makefile +++ /dev/null @@ -1,65 +0,0 @@ -# -# Makefile for the linux ncp-filesystem routines. -# - -CFLAGS = -Wall -Wstrict-prototypes -O2 -DMODULE -fomit-frame-pointer \ - $(INCLUDES) -DNCPFS_VERSION=\"$(VERSION)\"\ -# -DDEBUG_NCP=1 -DDEBUG_NCP_MALLOC -# -DDEBUG_NCP_MALLOC - -CC = gcc -D__KERNEL__ -I. -AS = as -ARCH = i386 - -.c.s: - $(CC) $(CFLAGS) -S $< -.c.o: - $(CC) $(CFLAGS) -c $< -.s.o: - $(AS) -o $*.o $< - -OBJS= dir.o inode.o file.o sock.o ioctl.o ncplib_kernel.o mmap.o - -all: $(INTERM_BINDIR)/ncpfs.o - -$(INTERM_BINDIR)/ncpfs.o: $(OBJS) - $(LD) -r -o $@ $(OBJS) - -ncplib_kernel.o: ncplib_kernel.c ncplib_kernel.h - $(CC) $(CFLAGS) -finline-functions -c $< - -dep: - $(CPP) -M $(INCLUDES) *.c > .depend - -clean: - rm -f *.o *~ - -realclean: clean - rm -f ncpmount ncptest .depend $(DISTFILE) *.out - -modules: ncpfs.o - -SRCPATH=$(shell pwd) -SRCDIR=$(shell basename $(SRCPATH)) -DISTFILE=$(SRCDIR).tgz -BACKUPFILE=ncpfs01.tgz -HOME=/home/me - -backup: - (rm -f $(DISTFILE); cd ..; tar cvf - $(SRCDIR) | gzip -1 \ - > $(HOME)/tarz/backup/$(BACKUPFILE)) - (cd $(HOME)/tarz/backup; ls -l $(BACKUPFILE); mcopy $(BACKUPFILE) a:) - -dist: realclean - rm -fr mnt - (cd ..; \ - tar cvf - $(SRCDIR) | \ - gzip -9 > $(DISTFILE); \ - mv $(DISTFILE) $(SRCDIR)) - -# -# include a dependency file if one exists -# -ifeq (.depend,$(wildcard .depend)) -include .depend -endif diff --git a/kernel-1.2/src/dir.c b/kernel-1.2/src/dir.c deleted file mode 100644 index 781af52..0000000 --- a/kernel-1.2/src/dir.c +++ /dev/null @@ -1,1245 +0,0 @@ -/* - * dir.c - * - * Copyright (C) 1995 by Volker Lendecke - * - */ - -#include -#ifdef MODULE -#include -#include -#endif - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "ncplib_kernel.h" - -struct ncp_dirent -{ - struct nw_info_struct i; - struct nw_search_sequence s; /* given back for i */ - unsigned long f_pos; -}; - -static int - ncp_dir_read(struct inode *inode, struct file *filp, char *buf, int count); - -static int - ncp_readdir(struct inode *inode, struct file *filp, - struct dirent *dirent, int count); - -static int - ncp_read_volume_list(struct ncp_server *server, int start_with, - int cache_size); - -static int - ncp_do_readdir(struct ncp_server *server, struct inode *dir, int fpos, - int cache_size, struct ncp_dirent *entry); - -static struct inode * - ncp_iget(struct inode *dir, struct nw_file_info *finfo); - -static struct ncp_inode_info * - ncp_find_dir_inode(struct inode *dir, const char *name); - -static int - ncp_lookup(struct inode *dir, const char *__name, - int len, struct inode **result); - -static int - ncp_create(struct inode *dir, const char *name, int len, int mode, - struct inode **result); - -static int - ncp_mkdir(struct inode *dir, const char *name, int len, int mode); - -static int - ncp_rmdir(struct inode *dir, const char *name, int len); - -static int - ncp_unlink(struct inode *dir, const char *name, int len); - -static int - ncp_rename(struct inode *old_dir, const char *old_name, int old_len, - struct inode *new_dir, const char *new_name, int new_len); - -static inline void -str_upper(char *name) -{ - while (*name) - { - if (*name >= 'a' && *name <= 'z') - { - *name -= ('a' - 'A'); - } - name++; - } -} - -static inline void -str_lower(char *name) -{ - while (*name) - { - if (*name >= 'A' && *name <= 'Z') - { - *name += ('a' - 'A'); - } - name++; - } -} - -static inline int -ncp_namespace(struct inode *i) -{ - struct ncp_server *server = NCP_SERVER(i); - struct nw_info_struct *info = NCP_ISTRUCT(i); - return server->name_space[info->volNumber]; -} - -static inline int -ncp_preserve_case(struct inode *i) -{ - return (ncp_namespace(i) == NW_NS_OS2); -} - -static struct file_operations ncp_dir_operations = -{ - NULL, /* lseek - default */ - ncp_dir_read, /* read - bad */ - NULL, /* write - bad */ - ncp_readdir, /* readdir */ - NULL, /* select - default */ - ncp_ioctl, /* ioctl */ - NULL, /* mmap */ - NULL, /* no special open code */ - NULL, /* no special release code */ - NULL /* fsync */ -}; - -struct inode_operations ncp_dir_inode_operations = -{ - &ncp_dir_operations, /* default directory file ops */ - ncp_create, /* create */ - ncp_lookup, /* lookup */ - NULL, /* link */ - ncp_unlink, /* unlink */ - NULL, /* symlink */ - ncp_mkdir, /* mkdir */ - ncp_rmdir, /* rmdir */ - NULL, /* mknod */ - ncp_rename, /* rename */ - NULL, /* readlink */ - NULL, /* follow_link */ - NULL, /* bmap */ - NULL, /* truncate */ - NULL, /* permission */ - NULL /* smap */ -}; - - -/* Here we encapsulate the inode number handling that depends upon the - * mount mode: When we mount a complete server, the memory address of - * the npc_inode_info is used as an inode. When only a single volume - * is mounted, then the dirEntNum is used as the inode number. As this - * is unique for the complete volume, this should enable the NFS - * exportability of a ncpfs-mounted volume. - */ - -static inline int -ncp_single_volume(struct ncp_server *server) -{ - return (server->m.mounted_vol[0] != '\0'); -} - -inline ino_t -ncp_info_ino(struct ncp_server * server, struct ncp_inode_info * info) -{ - return ncp_single_volume(server) - ? info->finfo.i.dirEntNum : (ino_t) info; -} - -static inline int -ncp_is_server_root(struct inode *inode) -{ - struct ncp_server *s = NCP_SERVER(inode); - - return ((!ncp_single_volume(s)) - && (inode->i_ino == ncp_info_ino(s, &(s->root)))); -} - -struct ncp_inode_info * -ncp_find_inode(struct inode *inode) -{ - struct ncp_server *server = NCP_SERVER(inode); - struct ncp_inode_info *root = &(server->root); - struct ncp_inode_info *this = root; - - ino_t ino = inode->i_ino; - - do - { - if (ino == ncp_info_ino(server, this)) - { - return this; - } - this = this->next; - } - while (this != root); - - return NULL; -} - -static int -ncp_dir_read(struct inode *inode, struct file *filp, char *buf, int count) -{ - return -EISDIR; -} - -#define NAME_OFFSET(de) ((int) ((de)->d_name - (char *) (de))) -#define ROUND_UP(x) (((x)+3) & ~3) - -/* In ncpfs, we have unique inodes across all mounted filesystems, for - all inodes that are in memory. That's why it's enough to index the - directory cache by the inode number. */ - -static int c_dev = 0; -static unsigned long c_ino = 0; -static int c_size; -static int c_seen_eof; -static int c_last_returned_index; -static struct ncp_dirent *c_entry = NULL; -static int c_lock = 0; -static struct wait_queue *c_wait = NULL; - -static inline void -ncp_lock_dircache(void) -{ - while (c_lock) - sleep_on(&c_wait); - c_lock = 1; -} - -static inline void -ncp_unlock_dircache(void) -{ - c_lock = 0; - wake_up(&c_wait); -} - -static int -ncp_readdir(struct inode *inode, struct file *filp, - struct dirent *dirent, int count) -{ - int result = 0; - int i = 0; - int index = 0; - struct ncp_dirent *entry = NULL; - struct ncp_server *server = NCP_SERVER(inode); - struct ncp_inode_info *dir = NCP_INOP(inode); - - int filldir(struct dirent *dirent, - const char *name, int len, - int f_pos, ino_t ino) - { - memcpy_tofs(dirent->d_name, name, len); - put_fs_byte(0, &(dirent->d_name[len])); - put_fs_long(ino, &dirent->d_ino); - put_fs_word(len, &dirent->d_reclen); - put_fs_word(f_pos, &dirent->d_off); - return 1; - } - - DPRINTK("ncp_readdir: filp->f_pos = %d\n", (int) filp->f_pos); - DPRINTK("ncp_readdir: inode->i_ino = %ld, c_ino = %ld\n", - inode->i_ino, c_ino); - - if (!inode || !S_ISDIR(inode->i_mode)) - { - printk("ncp_readdir: inode is NULL or not a directory\n"); - return -EBADF; - } - if (!ncp_conn_valid(server)) - { - return -EIO; - } - ncp_lock_dircache(); - if (c_entry == NULL) - { - i = sizeof(struct ncp_dirent) * NCP_READDIR_CACHE_SIZE; - c_entry = (struct ncp_dirent *) ncp_kmalloc(i, GFP_KERNEL); - if (c_entry == NULL) - { - printk("ncp_readdir: no MEMORY for cache\n"); - result = -ENOMEM; - goto finished; - } - } - if (filp->f_pos == 0) - { - ncp_invalid_dir_cache(inode); - if (filldir(dirent, ".", 1, filp->f_pos, - ncp_info_ino(server, dir)) < 0) - { - goto finished; - } - filp->f_pos += 1; - result = ROUND_UP(NAME_OFFSET(dirent) + i + 1); - goto finished; - } - if (filp->f_pos == 1) - { - if (filldir(dirent, "..", 2, filp->f_pos, - ncp_info_ino(server, dir->dir)) < 0) - { - goto finished; - } - filp->f_pos += 1; - result = ROUND_UP(NAME_OFFSET(dirent) + i + 1); - goto finished; - } - if ((inode->i_dev == c_dev) && (inode->i_ino == c_ino)) - { - for (i = 0; i < c_size; i++) - { - if (filp->f_pos == c_entry[i].f_pos) - { - entry = &c_entry[i]; - c_last_returned_index = i; - index = i; - break; - } - } - if ((entry == NULL) && c_seen_eof) - { - goto finished; - } - } - if (entry == NULL) - { - int entries; - DDPRINTK("ncp_readdir: Not found in cache.\n"); - - if (ncp_is_server_root(inode)) - { - entries = ncp_read_volume_list(server, filp->f_pos, - NCP_READDIR_CACHE_SIZE); - DPRINTK("ncp_read_volume_list returned %d\n", entries); - - } else - { - entries = ncp_do_readdir(server, inode, filp->f_pos, - NCP_READDIR_CACHE_SIZE, - c_entry); - DPRINTK("ncp_readdir returned %d\n", entries); - } - - if (entries < 0) - { - c_dev = 0; - c_ino = 0; - result = entries; - goto finished; - } - if (entries > 0) - { - c_seen_eof = (entries < NCP_READDIR_CACHE_SIZE); - c_dev = inode->i_dev; - c_ino = inode->i_ino; - c_size = entries; - entry = c_entry; - c_last_returned_index = 0; - index = 0; - - if (!ncp_preserve_case(inode)) - { - for (i = 0; i < c_size; i++) - { - str_lower(c_entry[i].i.entryName); - } - } - } - } - if (entry == NULL) - { - /* Nothing found, even from a ncp call */ - goto finished; - } - if (index < c_size) - { - /* We found it. For getwd(), we have to return the - correct inode in d_ino if the inode is currently in - use. Otherwise the inode number does not - matter. (You can argue a lot about this..) */ - - ino_t ino; - - if (ncp_single_volume(server)) - { - ino = (ino_t) (entry->i.dirEntNum); - } else - { - struct ncp_inode_info *ino_info; - ino_info = ncp_find_dir_inode(inode, - entry->i.entryName); - - /* Some programs seem to be confused about a - * zero inode number, so we set it to one. - * Thanks to Gordon Chaffee for this one. */ - if (ino_info == NULL) - { - ino_info = (struct ncp_inode_info *) 1; - } - ino = (ino_t) (ino_info); - } - - DPRINTK("ncp_readdir: entry->path= %s\n", entry->i.entryName); - DPRINTK("ncp_readdir: entry->f_pos = %ld\n", entry->f_pos); - - if (filldir(dirent, entry->i.entryName, entry->i.nameLen, - entry->f_pos, ino) < 0) - { - goto finished; - } - filp->f_pos += 1; - index += 1; - entry += 1; - result = ROUND_UP(NAME_OFFSET(dirent) + i + 1); - goto finished; - } - finished: - ncp_unlock_dircache(); - return result; -} - -static int -ncp_read_volume_list(struct ncp_server *server, int fpos, int cache_size) -{ - struct ncp_dirent *entry = c_entry; - - int total_count = 2; - int i; - -#if 1 - if (fpos < 2) - { - printk("OOPS, we expect fpos >= 2"); - fpos = 2; - } -#endif - - for (i = 0; i < NCP_NUMBER_OF_VOLUMES; i++) - { - struct ncp_volume_info info; - - if (ncp_get_volume_info_with_number(server, i, &info) != 0) - { - return (total_count - fpos); - } - if (strlen(info.volume_name) > 0) - { - if (total_count < fpos) - { - DPRINTK("ncp_read_volumes: skipped vol: %s\n", - info.volume_name); - } else if (total_count >= fpos + cache_size) - { - return (total_count - fpos); - } else - { - DPRINTK("ncp_read_volumes: found vol: %s\n", - info.volume_name); - - if (ncp_lookup_volume(server, - info.volume_name, - &(entry->i)) != 0) - { - DPRINTK("ncpfs: could not lookup vol " - "%s\n", info.volume_name); - continue; - } - entry->f_pos = total_count; - entry += 1; - } - total_count += 1; - } - } - return (total_count - fpos); -} - -static int -ncp_do_readdir(struct ncp_server *server, struct inode *dir, int fpos, - int cache_size, struct ncp_dirent *entry) -{ - static struct nw_search_sequence seq; - static struct inode *last_dir; - static int total_count; - -#if 1 - if (fpos < 2) - { - printk("OOPS, we expect fpos >= 2"); - fpos = 2; - } -#endif - DPRINTK("ncp_do_readdir: fpos = %d\n", fpos); - - if (fpos == 2) - { - last_dir = NULL; - total_count = 2; - } - if ((fpos != total_count) || (dir != last_dir)) - { - total_count = 2; - last_dir = dir; - - DPRINTK("ncp_do_readdir: re-used seq for %s\n", - NCP_ISTRUCT(dir)->entryName); - - if (ncp_initialize_search(server, NCP_ISTRUCT(dir), &seq) != 0) - { - DPRINTK("ncp_init_search failed\n"); - return total_count - fpos; - } - } - while (total_count < fpos + cache_size) - { - if (ncp_search_for_file_or_subdir(server, &seq, - &(entry->i)) != 0) - { - return total_count - fpos; - } - if (total_count < fpos) - { - DPRINTK("ncp_do_readdir: skipped file: %s\n", - entry->i.entryName); - } else - { - DDPRINTK("ncp_do_r: file: %s, f_pos=%d,total_count=%d", - entry->i.entryName, fpos, total_count); - entry->s = seq; - entry->f_pos = total_count; - entry += 1; - } - total_count += 1; - } - return (total_count - fpos); -} - -void -ncp_init_dir_cache(void) -{ - c_dev = 0; - c_ino = 0; - c_entry = NULL; -} - -void -ncp_invalid_dir_cache(struct inode *inode) -{ - if ((inode->i_dev == c_dev) && (inode->i_ino == c_ino)) - { - c_dev = 0; - c_ino = 0; - c_seen_eof = 0; - } -} - -void -ncp_free_dir_cache(void) -{ - DPRINTK("ncp_free_dir_cache: enter\n"); - - if (c_entry == NULL) - { - return; - } - ncp_kfree_s(c_entry, - sizeof(struct ncp_dirent) * NCP_READDIR_CACHE_SIZE); - c_entry = NULL; - - DPRINTK("ncp_free_dir_cache: exit\n"); -} - - -static struct inode * -ncp_iget(struct inode *dir, struct nw_file_info *finfo) -{ - struct inode *inode; - struct ncp_inode_info *new_inode_info; - struct ncp_inode_info *root; - - if (dir == NULL) - { - printk("ncp_iget: dir is NULL\n"); - return NULL; - } - if (finfo == NULL) - { - printk("ncp_iget: finfo is NULL\n"); - return NULL; - } - new_inode_info = ncp_kmalloc(sizeof(struct ncp_inode_info), - GFP_KERNEL); - - if (new_inode_info == NULL) - { - printk("ncp_iget: could not alloc mem for %s\n", - finfo->i.entryName); - return NULL; - } - new_inode_info->state = NCP_INODE_LOOKED_UP; - new_inode_info->nused = 0; - new_inode_info->dir = NCP_INOP(dir); - new_inode_info->finfo = *finfo; - - NCP_INOP(dir)->nused += 1; - - /* We have to link the new inode_info into the doubly linked - list of inode_infos to make a complete linear search - possible. */ - - root = &(NCP_SERVER(dir)->root); - - new_inode_info->prev = root; - new_inode_info->next = root->next; - root->next->prev = new_inode_info; - root->next = new_inode_info; - - if (!(inode = iget(dir->i_sb, ncp_info_ino(NCP_SERVER(dir), - new_inode_info)))) - { - printk("ncp_iget: iget failed!"); - return NULL; - } - return inode; -} - -void -ncp_free_inode_info(struct ncp_inode_info *i) -{ - if (i == NULL) - { - printk("ncp_free_inode: i == NULL\n"); - return; - } - i->state = NCP_INODE_CACHED; - while ((i->nused == 0) && (i->state == NCP_INODE_CACHED)) - { - struct ncp_inode_info *dir = i->dir; - - i->next->prev = i->prev; - i->prev->next = i->next; - - DDPRINTK("ncp_free_inode_info: freeing %s\n", - i->finfo.i.entryName); - - ncp_kfree_s(i, sizeof(struct ncp_inode_info)); - - if (dir == i) - return; - - (dir->nused)--; - i = dir; - } -} - -void -ncp_init_root(struct ncp_server *server) -{ - struct ncp_inode_info *root = &(server->root); - struct nw_info_struct *i = &(root->finfo.i); - unsigned short dummy; - - DPRINTK("ncp_init_root: i = %x\n", (int) i); - - root->finfo.opened = 0; - i->attributes = aDIR; - i->dataStreamSize = 1024; - i->dirEntNum = i->DosDirNum = 0; - i->volNumber = NCP_NUMBER_OF_VOLUMES + 1; /* illegal volnum */ - ncp_date_unix2dos(0, &(i->creationTime), &(i->creationDate)); - ncp_date_unix2dos(0, &(i->modifyTime), &(i->modifyDate)); - ncp_date_unix2dos(0, &dummy, &(i->lastAccessDate)); - i->nameLen = 0; - i->entryName[0] = '\0'; - - root->state = NCP_INODE_LOOKED_UP; - root->nused = 1; - root->dir = root; - root->next = root->prev = root; - return; -} - -int -ncp_conn_logged_in(struct ncp_server *server) -{ - if (server->m.mounted_vol[0] == '\0') - { - return 0; - } - str_upper(server->m.mounted_vol); - if (ncp_lookup_volume(server, server->m.mounted_vol, - &(server->root.finfo.i)) != 0) - { - return -ENOENT; - } - str_lower(server->root.finfo.i.entryName); - - return 0; -} - -void -ncp_free_all_inodes(struct ncp_server *server) -{ - /* Here nothing should be to do. I do not know whether it's - better to leave some memory allocated or be stuck in an - endless loop */ -#if 1 - struct ncp_inode_info *root = &(server->root); - - if (root->next != root) - { - printk("ncp_free_all_inodes: INODES LEFT!!!\n"); - } - while (root->next != root) - { - printk("ncp_free_all_inodes: freeing inode\n"); - ncp_free_inode_info(root->next); - /* In case we have an endless loop.. */ - schedule(); - } -#endif - - return; -} - -/* We will search the inode that belongs to this name, currently by a - complete linear search through the inodes belonging to this - filesystem. This has to be fixed. */ -static struct ncp_inode_info * -ncp_find_dir_inode(struct inode *dir, const char *name) -{ - struct ncp_server *server = NCP_SERVER(dir); - struct nw_info_struct *dir_info = NCP_ISTRUCT(dir); - struct ncp_inode_info *result = &(server->root); - - if (name == NULL) - { - return NULL; - } - do - { - if ((result->dir->finfo.i.dirEntNum == dir_info->dirEntNum) - && (result->dir->finfo.i.volNumber == dir_info->volNumber) - && (strcmp(result->finfo.i.entryName, name) == 0) - /* The root dir is never looked up using this - * routine. Without the following test a root - * directory 'sys' in a volume named 'sys' could - * never be looked up, because - * server->root->dir==server->root. */ - && (result != &(server->root))) - { - return result; - } - result = result->next; - - } - while (result != &(server->root)); - - return NULL; -} - -static int -ncp_lookup(struct inode *dir, const char *__name, int len, - struct inode **result) -{ - struct nw_file_info finfo; - struct ncp_server *server; - struct ncp_inode_info *result_info; - int found_in_cache; - int down_case = 0; - char name[len + 1]; - - *result = NULL; - - if (!dir || !S_ISDIR(dir->i_mode)) - { - printk("ncp_lookup: inode is NULL or not a directory.\n"); - iput(dir); - return -ENOENT; - } - server = NCP_SERVER(dir); - if (!ncp_conn_valid(server)) - { - iput(dir); - return -EIO; - } - DDPRINTK("ncp_lookup: %s, len %d\n", __name, len); - - /* Fast cheat for . */ - if (len == 0 || (len == 1 && __name[0] == '.')) - { - *result = dir; - return 0; - } - /* ..and for .. */ - if (len == 2 && __name[0] == '.' && __name[1] == '.') - { - struct ncp_inode_info *parent = NCP_INOP(dir)->dir; - - if (parent->state == NCP_INODE_CACHED) - { - parent->state = NCP_INODE_LOOKED_UP; - } - *result = iget(dir->i_sb, ncp_info_ino(server, parent)); - iput(dir); - if (*result == 0) - { - return -EACCES; - } else - { - return 0; - } - } - memcpy(name, __name, len); - name[len] = 0; - lock_super(dir->i_sb); - result_info = ncp_find_dir_inode(dir, name); - - if (result_info != 0) - { - if (result_info->state == NCP_INODE_CACHED) - { - result_info->state = NCP_INODE_LOOKED_UP; - } - /* Here we convert the inode_info address into an - inode number */ - - *result = iget(dir->i_sb, ncp_info_ino(server, result_info)); - unlock_super(dir->i_sb); - iput(dir); - - if (*result == NULL) - { - return -EACCES; - } - return 0; - } - /* If the file is in the dir cache, we do not have to ask the - server. */ - - found_in_cache = 0; - - ncp_lock_dircache(); - - if ((dir->i_dev == c_dev) && (dir->i_ino == c_ino)) - { - int first = c_last_returned_index; - int i; - - i = first; - do - { - DDPRINTK("ncp_lookup: trying index: %d, name: %s\n", - i, c_entry[i].i.entryName); - - if (strcmp(c_entry[i].i.entryName, name) == 0) - { - DPRINTK("ncp_lookup: found in cache!\n"); - finfo.i = c_entry[i].i; - found_in_cache = 1; - break; - } - i = (i + 1) % c_size; - } - while (i != first); - } - ncp_unlock_dircache(); - - if (found_in_cache == 0) - { - int res; - - DDPRINTK("ncp_lookup: do_lookup on %s/%s\n", - NCP_ISTRUCT(dir)->entryName, name); - - if (ncp_is_server_root(dir)) - { - str_upper(name); - down_case = 1; - res = ncp_lookup_volume(server, name, &(finfo.i)); - } else - { - if (!ncp_preserve_case(dir)) - { - str_upper(name); - down_case = 1; - } - res = ncp_obtain_info(server, - NCP_ISTRUCT(dir)->volNumber, - NCP_ISTRUCT(dir)->dirEntNum, - name, &(finfo.i)); - } - if (res != 0) - { - unlock_super(dir->i_sb); - iput(dir); - return -ENOENT; - } - } - finfo.opened = 0; - - if (down_case != 0) - { - str_lower(finfo.i.entryName); - } - if (!(*result = ncp_iget(dir, &finfo))) - { - unlock_super(dir->i_sb); - iput(dir); - return -EACCES; - } - unlock_super(dir->i_sb); - iput(dir); - return 0; -} - -static int -ncp_create(struct inode *dir, const char *name, int len, int mode, - struct inode **result) -{ - struct nw_file_info finfo; - __u8 _name[len + 1]; - - *result = NULL; - - if (!dir || !S_ISDIR(dir->i_mode)) - { - printk("ncp_create: inode is NULL or not a directory\n"); - iput(dir); - return -ENOENT; - } - if (!ncp_conn_valid(NCP_SERVER(dir))) - { - iput(dir); - return -EIO; - } - strncpy(_name, name, len); - _name[len] = '\0'; - - if (!ncp_preserve_case(dir)) - { - str_upper(_name); - } - lock_super(dir->i_sb); - if (ncp_open_create_file_or_subdir(NCP_SERVER(dir), - NCP_ISTRUCT(dir), _name, - OC_MODE_CREATE | OC_MODE_OPEN | - OC_MODE_REPLACE, - 0, AR_READ | AR_WRITE, - &finfo) != 0) - { - unlock_super(dir->i_sb); - iput(dir); - return -EACCES; - } - ncp_invalid_dir_cache(dir); - - if (!ncp_preserve_case(dir)) - { - str_lower(finfo.i.entryName); - } - finfo.access = O_RDWR; - - if (!(*result = ncp_iget(dir, &finfo)) < 0) - { - ncp_close_file(NCP_SERVER(dir), finfo.file_handle); - unlock_super(dir->i_sb); - iput(dir); - return -EINVAL; - } - unlock_super(dir->i_sb); - iput(dir); - return 0; -} - -static int -ncp_mkdir(struct inode *dir, const char *name, int len, int mode) -{ - int error; - struct nw_file_info new_dir; - __u8 _name[len + 1]; - - if ((name[0] == '.') - && ((len == 1) - || ((len == 2) - && (name[1] == '.')))) - { - iput(dir); - return -EEXIST; - } - strncpy(_name, name, len); - _name[len] = '\0'; - - if (!ncp_preserve_case(dir)) - { - str_upper(_name); - } - if (!dir || !S_ISDIR(dir->i_mode)) - { - printk("ncp_mkdir: inode is NULL or not a directory\n"); - iput(dir); - return -ENOENT; - } - if (!ncp_conn_valid(NCP_SERVER(dir))) - { - iput(dir); - return -EIO; - } - if (ncp_open_create_file_or_subdir(NCP_SERVER(dir), - NCP_ISTRUCT(dir), _name, - OC_MODE_CREATE, aDIR, 0xffff, - &new_dir) != 0) - { - error = -EACCES; - } else - { - error = 0; - ncp_invalid_dir_cache(dir); - } - - iput(dir); - return error; -} - -static int -ncp_rmdir(struct inode *dir, const char *name, int len) -{ - int error; - __u8 _name[len + 1]; - - if (!dir || !S_ISDIR(dir->i_mode)) - { - printk("ncp_rmdir: inode is NULL or not a directory\n"); - iput(dir); - return -ENOENT; - } - if (!ncp_conn_valid(NCP_SERVER(dir))) - { - iput(dir); - return -EIO; - } - if (ncp_find_dir_inode(dir, name) != NULL) - { - iput(dir); - error = -EBUSY; - } else - { - - strncpy(_name, name, len); - _name[len] = '\0'; - - if (!ncp_preserve_case(dir)) - { - str_upper(_name); - } - if ((error = ncp_del_file_or_subdir(NCP_SERVER(dir), - NCP_ISTRUCT(dir), - _name)) == 0) - { - ncp_invalid_dir_cache(dir); - } else - { - error = -EACCES; - } - } - iput(dir); - return error; -} - -static int -ncp_unlink(struct inode *dir, const char *name, int len) -{ - int error; - __u8 _name[len + 1]; - - if (!dir || !S_ISDIR(dir->i_mode)) - { - printk("ncp_unlink: inode is NULL or not a directory\n"); - iput(dir); - return -ENOENT; - } - if (!ncp_conn_valid(NCP_SERVER(dir))) - { - iput(dir); - return -EIO; - } - if (ncp_find_dir_inode(dir, name) != NULL) - { - iput(dir); - error = -EBUSY; - } else - { - strncpy(_name, name, len); - _name[len] = '\0'; - - if (!ncp_preserve_case(dir)) - { - str_upper(_name); - } - if ((error = ncp_del_file_or_subdir(NCP_SERVER(dir), - NCP_ISTRUCT(dir), - _name)) == 0) - { - ncp_invalid_dir_cache(dir); - } else - { - error = -EACCES; - } - } - iput(dir); - return error; -} - -static int -ncp_rename(struct inode *old_dir, const char *old_name, int old_len, - struct inode *new_dir, const char *new_name, int new_len) -{ - int res; - char _old_name[old_len + 1]; - char _new_name[new_len + 1]; - - if (!old_dir || !S_ISDIR(old_dir->i_mode)) - { - printk("ncp_rename: old inode is NULL or not a directory\n"); - res = -ENOENT; - goto finished; - } - if (!ncp_conn_valid(NCP_SERVER(old_dir))) - { - res = -EIO; - goto finished; - } - if (!new_dir || !S_ISDIR(new_dir->i_mode)) - { - printk("ncp_rename: new inode is NULL or not a directory\n"); - res = -ENOENT; - goto finished; - } - if ((ncp_find_dir_inode(old_dir, old_name) != NULL) - || (ncp_find_dir_inode(new_dir, new_name) != NULL)) - { - res = -EBUSY; - goto finished; - } - strncpy(_old_name, old_name, old_len); - _old_name[old_len] = '\0'; - - if (!ncp_preserve_case(old_dir)) - { - str_upper(_old_name); - } - strncpy(_new_name, new_name, new_len); - _new_name[new_len] = '\0'; - - if (!ncp_preserve_case(new_dir)) - { - str_upper(_new_name); - } - res = ncp_ren_or_mov_file_or_subdir(NCP_SERVER(old_dir), - NCP_ISTRUCT(old_dir), _old_name, - NCP_ISTRUCT(new_dir), _new_name); - - if (res == 0) - { - ncp_invalid_dir_cache(old_dir); - ncp_invalid_dir_cache(new_dir); - } else - { - res = -EACCES; - } - - finished: - iput(old_dir); - iput(new_dir); - return res; -} - -/* The following routines are taken directly from msdos-fs */ - -/* Linear day numbers of the respective 1sts in non-leap years. */ - -static int day_n[] = -{0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 0, 0, 0, 0}; - /* JanFebMarApr May Jun Jul Aug Sep Oct Nov Dec */ - - -extern struct timezone sys_tz; - -static int -utc2local(int time) -{ - return time - sys_tz.tz_minuteswest * 60; -} - -static int -local2utc(int time) -{ - return time + sys_tz.tz_minuteswest * 60; -} - -/* Convert a MS-DOS time/date pair to a UNIX date (seconds since 1 1 70). */ - -int -ncp_date_dos2unix(unsigned short time, unsigned short date) -{ - int month, year, secs; - - month = ((date >> 5) & 15) - 1; - year = date >> 9; - secs = (time & 31) * 2 + 60 * ((time >> 5) & 63) + (time >> 11) * 3600 + 86400 * - ((date & 31) - 1 + day_n[month] + (year / 4) + year * 365 - ((year & 3) == 0 && - month < 2 ? 1 : 0) + 3653); - /* days since 1.1.70 plus 80's leap day */ - return local2utc(secs); -} - - -/* Convert linear UNIX date to a MS-DOS time/date pair. */ -void -ncp_date_unix2dos(int unix_date, unsigned short *time, unsigned short *date) -{ - int day, year, nl_day, month; - - unix_date = utc2local(unix_date); - *time = (unix_date % 60) / 2 + (((unix_date / 60) % 60) << 5) + - (((unix_date / 3600) % 24) << 11); - day = unix_date / 86400 - 3652; - year = day / 365; - if ((year + 3) / 4 + 365 * year > day) - year--; - day -= (year + 3) / 4 + 365 * year; - if (day == 59 && !(year & 3)) - { - nl_day = day; - month = 2; - } else - { - nl_day = (year & 3) || day <= 59 ? day : day - 1; - for (month = 0; month < 12; month++) - if (day_n[month] > nl_day) - break; - } - *date = nl_day - day_n[month - 1] + 1 + (month << 5) + (year << 9); -} diff --git a/kernel-1.2/src/file.c b/kernel-1.2/src/file.c deleted file mode 100644 index ab40d3c..0000000 --- a/kernel-1.2/src/file.c +++ /dev/null @@ -1,273 +0,0 @@ -/* - * file.c - * - * Copyright (C) 1995 by Paal-Kr. Engstad and Volker Lendecke - * - */ - -#include -#ifdef MODULE -#include -#include -#endif - -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include "ncplib_kernel.h" -#include - -static inline int -min(int a, int b) -{ - return a < b ? a : b; -} - -static int -ncp_fsync(struct inode *inode, struct file *file) -{ - return 0; -} - -int -ncp_make_open(struct inode *i, int right) -{ - struct nw_file_info *finfo; - - if (i == NULL) - { - printk("ncp_make_open: got NULL inode\n"); - return -EINVAL; - } - finfo = NCP_FINFO(i); - - DPRINTK("ncp_make_open: dirent->opened = %d\n", finfo->opened); - - lock_super(i->i_sb); - if (finfo->opened == 0) - { - finfo->access = -1; - /* tries max. rights */ - if (ncp_open_create_file_or_subdir(NCP_SERVER(i), - NULL, NULL, - OC_MODE_OPEN, 0, - AR_READ | AR_WRITE, - finfo) == 0) - { - finfo->access = O_RDWR; - } else if (ncp_open_create_file_or_subdir(NCP_SERVER(i), - NULL, NULL, - OC_MODE_OPEN, 0, - AR_READ, - finfo) == 0) - { - finfo->access = O_RDONLY; - } - } - unlock_super(i->i_sb); - - if (((right == O_RDONLY) && ((finfo->access == O_RDONLY) - || (finfo->access == O_RDWR))) - || ((right == O_WRONLY) && ((finfo->access == O_WRONLY) - || (finfo->access == O_RDWR))) - || ((right == O_RDWR) && (finfo->access == O_RDWR))) - return 0; - - return -EACCES; -} - -static int -ncp_file_read(struct inode *inode, struct file *file, char *buf, int count) -{ - int bufsize, already_read; - off_t pos; - int errno; - - DPRINTK("ncp_file_read: enter %s\n", NCP_ISTRUCT(inode)->entryName); - - if (inode == NULL) - { - DPRINTK("ncp_file_read: inode = NULL\n"); - return -EINVAL; - } - if (!ncp_conn_valid(NCP_SERVER(inode))) - { - return -EIO; - } - if (!S_ISREG(inode->i_mode)) - { - DPRINTK("ncp_file_read: read from non-file, mode %07o\n", - inode->i_mode); - return -EINVAL; - } - pos = file->f_pos; - - if (pos + count > inode->i_size) - { - count = inode->i_size - pos; - } - if (count <= 0) - { - return 0; - } - if ((errno = ncp_make_open(inode, O_RDONLY)) != 0) - { - return errno; - } - bufsize = NCP_SERVER(inode)->buffer_size; - - already_read = 0; - - /* First read in as much as possible for each bufsize. */ - while (already_read < count) - { - int read_this_time; - int to_read = min(bufsize - (pos % bufsize), - count - already_read); - - if (ncp_read(NCP_SERVER(inode), NCP_FINFO(inode)->file_handle, - pos, to_read, buf, &read_this_time) != 0) - { - return -EIO; /* This is not exact, i know.. */ - } - pos += read_this_time; - buf += read_this_time; - already_read += read_this_time; - - if (read_this_time < to_read) - { - break; - } - } - - file->f_pos = pos; - - if (!IS_RDONLY(inode)) - { - inode->i_atime = CURRENT_TIME; - } - inode->i_dirt = 1; - - DPRINTK("ncp_file_read: exit %s\n", NCP_ISTRUCT(inode)->entryName); - - return already_read; -} - -static int -ncp_file_write(struct inode *inode, struct file *file, char *buf, - int count) -{ - int bufsize, already_written; - off_t pos; - int errno; - - if (inode == NULL) - { - DPRINTK("ncp_file_write: inode = NULL\n"); - return -EINVAL; - } - if (!ncp_conn_valid(NCP_SERVER(inode))) - { - return -EIO; - } - if (!S_ISREG(inode->i_mode)) - { - DPRINTK("ncp_file_write: write to non-file, mode %07o\n", - inode->i_mode); - return -EINVAL; - } - DPRINTK("ncp_file_write: enter %s\n", NCP_ISTRUCT(inode)->entryName); - - if (count <= 0) - { - return 0; - } - if ((errno = ncp_make_open(inode, O_RDWR)) != 0) - { - return errno; - } - pos = file->f_pos; - - if (file->f_flags & O_APPEND) - { - pos = inode->i_size; - } - bufsize = NCP_SERVER(inode)->buffer_size; - - already_written = 0; - - while (already_written < count) - { - int written_this_time; - int to_write = min(bufsize - (pos % bufsize), - count - already_written); - - if (ncp_write(NCP_SERVER(inode), NCP_FINFO(inode)->file_handle, - pos, to_write, buf, &written_this_time) != 0) - { - return -EIO; - } - pos += written_this_time; - buf += written_this_time; - already_written += written_this_time; - - if (written_this_time < to_write) - { - break; - } - } - - inode->i_mtime = inode->i_ctime = CURRENT_TIME; - inode->i_dirt = 1; - - file->f_pos = pos; - - if (pos > inode->i_size) - { - inode->i_size = pos; - ncp_invalid_dir_cache(NCP_INOP(inode)->dir->inode); - } - DPRINTK("ncp_file_write: exit %s\n", NCP_ISTRUCT(inode)->entryName); - - return already_written; -} - -static struct file_operations ncp_file_operations = -{ - NULL, /* lseek - default */ - ncp_file_read, /* read */ - ncp_file_write, /* write */ - NULL, /* readdir - bad */ - NULL, /* select - default */ - ncp_ioctl, /* ioctl */ - ncp_mmap, /* mmap */ - NULL, /* open */ - NULL, /* release */ - ncp_fsync, /* fsync */ -}; - -struct inode_operations ncp_file_inode_operations = -{ - &ncp_file_operations, /* default file operations */ - NULL, /* create */ - NULL, /* lookup */ - NULL, /* link */ - NULL, /* unlink */ - NULL, /* symlink */ - NULL, /* mkdir */ - NULL, /* rmdir */ - NULL, /* mknod */ - NULL, /* rename */ - NULL, /* readlink */ - NULL, /* follow_link */ - NULL, /* bmap */ - NULL /* truncate */ -}; diff --git a/kernel-1.2/src/inode.c b/kernel-1.2/src/inode.c deleted file mode 100644 index 8b575db..0000000 --- a/kernel-1.2/src/inode.c +++ /dev/null @@ -1,493 +0,0 @@ -/* - * inode.c - * - * Copyright (C) 1995 by Volker Lendecke - * - */ - -#include -#ifdef MODULE -#include -#include -#else -#define MOD_INC_USE_COUNT -#define MOD_DEC_USE_COUNT -#endif - -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "ncplib_kernel.h" - -extern int close_fp(struct file *filp); - -static void ncp_put_inode(struct inode *); -static void ncp_read_inode(struct inode *); -static void ncp_put_super(struct super_block *); -static void ncp_statfs(struct super_block *sb, struct statfs *stat); -static int ncp_notify_change(struct inode *inode, struct iattr *attr); - -static struct super_operations ncp_sops = -{ - ncp_read_inode, /* read inode */ - ncp_notify_change, /* notify change */ - NULL, /* write inode */ - ncp_put_inode, /* put inode */ - ncp_put_super, /* put superblock */ - NULL, /* write superblock */ - ncp_statfs, /* stat filesystem */ - NULL -}; - -/* ncp_read_inode: Called from iget, it only traverses the allocated - ncp_inode_info's and initializes the inode from the data found - there. It does not allocate or deallocate anything. */ - -static void -ncp_read_inode(struct inode *inode) -{ - /* Our task should be extremely simple here. We only have to - look up the infomation somebody else (ncp_iget) put into - the inode tree. The address of this information is the - inode->i_ino. Just to make sure everything went well, we - check it's there. */ - - struct ncp_inode_info *inode_info = ncp_find_inode(inode); - - if (inode_info == NULL) - { - /* Ok, now we're in trouble. The inode info is not there. What - should we do now??? */ - printk("ncp_read_inode: inode info not found\n"); - return; - } - inode_info->state = NCP_INODE_VALID; - - NCP_INOP(inode) = inode_info; - inode_info->inode = inode; - - if (NCP_ISTRUCT(inode)->attributes & aDIR) - { - inode->i_mode = NCP_SERVER(inode)->m.dir_mode; - /* for directories dataStreamSize seems to be some - Object ID ??? */ - inode->i_size = 512; - } else - { - inode->i_mode = NCP_SERVER(inode)->m.file_mode; - inode->i_size = NCP_ISTRUCT(inode)->dataStreamSize; - } - - DDPRINTK("ncp_read_inode: inode->i_mode = %u\n", inode->i_mode); - - inode->i_nlink = 1; - inode->i_uid = NCP_SERVER(inode)->m.uid; - inode->i_gid = NCP_SERVER(inode)->m.gid; - inode->i_blksize = 512; - inode->i_rdev = 0; - - if ((inode->i_blksize != 0) && (inode->i_size != 0)) - { - inode->i_blocks = - (inode->i_size - 1) / inode->i_blksize + 1; - } else - { - inode->i_blocks = 0; - } - - inode->i_mtime = ncp_date_dos2unix(NCP_ISTRUCT(inode)->modifyTime, - NCP_ISTRUCT(inode)->modifyDate); - inode->i_ctime = ncp_date_dos2unix(NCP_ISTRUCT(inode)->creationTime, - NCP_ISTRUCT(inode)->creationDate); - inode->i_atime = ncp_date_dos2unix(0, - NCP_ISTRUCT(inode)->lastAccessDate); - - if (S_ISREG(inode->i_mode)) - { - inode->i_op = &ncp_file_inode_operations; - } else if (S_ISDIR(inode->i_mode)) - { - inode->i_op = &ncp_dir_inode_operations; - } else - { - inode->i_op = NULL; - } -} - -static void -ncp_put_inode(struct inode *inode) -{ - struct nw_file_info *finfo = NCP_FINFO(inode); - struct super_block *sb = inode->i_sb; - - lock_super(sb); - if (finfo->opened != 0) - { - if (ncp_close_file(NCP_SERVER(inode), finfo->file_handle) != 0) - { - /* We can't do anything but complain. */ - printk("ncp_put_inode: could not close\n"); - } - } - DDPRINTK("ncp_put_inode: put %s\n", - finfo->i.entryName); - - ncp_free_inode_info(NCP_INOP(inode)); - - if (S_ISDIR(inode->i_mode)) - { - DDPRINTK("ncp_put_inode: put directory %ld\n", - inode->i_ino); - ncp_invalid_dir_cache(inode); - } - clear_inode(inode); - unlock_super(sb); -} - -struct super_block * -ncp_read_super(struct super_block *sb, void *raw_data, int silent) -{ - struct ncp_mount_data *data = (struct ncp_mount_data *) raw_data; - struct ncp_server *server; - struct file *ncp_filp; - struct file *wdog_filp; - dev_t dev = sb->s_dev; - int error; - - if (data == NULL) - { - printk("ncp_read_super: missing data argument\n"); - sb->s_dev = 0; - return NULL; - } - if (data->version != NCP_MOUNT_VERSION) - { - printk("ncp warning: mount version %s than kernel\n", - (data->version < NCP_MOUNT_VERSION) ? - "older" : "newer"); - sb->s_dev = 0; - return NULL; - } - if ((data->ncp_fd >= NR_OPEN) - || ((ncp_filp = current->files->fd[data->ncp_fd]) == NULL) - || (!S_ISSOCK(ncp_filp->f_inode->i_mode))) - { - printk("ncp_read_super: invalid ncp socket\n"); - sb->s_dev = 0; - return NULL; - } - if ((data->wdog_fd >= NR_OPEN) - || ((wdog_filp = current->files->fd[data->wdog_fd]) == NULL) - || (!S_ISSOCK(wdog_filp->f_inode->i_mode))) - { - printk("ncp_read_super: invalid wdog socket\n"); - sb->s_dev = 0; - return NULL; - } - /* We must malloc our own super-block info */ - server = (struct ncp_server *) ncp_kmalloc(sizeof(struct ncp_server), - GFP_KERNEL); - - if (server == NULL) - { - printk("ncp_read_super: could not alloc ncp_server\n"); - return NULL; - } - ncp_filp->f_count += 1; - wdog_filp->f_count += 1; - - lock_super(sb); - - NCP_SBP(sb) = server; - - sb->s_blocksize = 1024; /* Eh... Is this correct? */ - sb->s_blocksize_bits = 10; - sb->s_magic = NCP_SUPER_MAGIC; - sb->s_dev = dev; - sb->s_op = &ncp_sops; - - server->ncp_filp = ncp_filp; - server->wdog_filp = wdog_filp; - server->lock = 0; - server->wait = NULL; - server->packet = NULL; - server->buffer_size = 0; - server->conn_status = 0; - - server->m = *data; - server->m.file_mode = (server->m.file_mode & - (S_IRWXU | S_IRWXG | S_IRWXO)) | S_IFREG; - server->m.dir_mode = (server->m.dir_mode & - (S_IRWXU | S_IRWXG | S_IRWXO)) | S_IFDIR; - - server->packet_size = NCP_PACKET_SIZE; - server->packet = ncp_kmalloc(NCP_PACKET_SIZE, GFP_KERNEL); - - if (server->packet == NULL) - { - printk("ncpfs: could not alloc packet\n"); - error = -ENOMEM; - unlock_super(sb); - goto fail; - } - ncp_init_root(server); - - /* - * Make the connection to the server - */ - - if (ncp_catch_watchdog(server) != 0) - { - printk("ncp_read_super: Could not catch watchdog\n"); - error = -EINVAL; - unlock_super(sb); - goto fail; - } - ncp_lock_server(server); - error = ncp_connect(server); - ncp_unlock_server(server); - unlock_super(sb); - - if (error < 0) - { - sb->s_dev = 0; - printk("ncp_read_super: Failed connection, bailing out " - "(error = %d).\n", -error); - ncp_kfree_s(server->packet, server->packet_size); - ncp_dont_catch_watchdog(server); - goto fail; - } - DPRINTK("ncp_read_super: NCP_SBP(sb) = %x\n", (int) NCP_SBP(sb)); - - if (!(sb->s_mounted = iget(sb, ncp_info_ino(server, &(server->root))))) - { - sb->s_dev = 0; - printk("ncp_read_super: get root inode failed\n"); - goto disconnect; - } - if (ncp_negotiate_buffersize(server, NCP_DEFAULT_BUFSIZE, - &(server->buffer_size)) != 0) - { - sb->s_dev = 0; - printk("ncp_read_super: could not get bufsize\n"); - goto disconnect; - } - DPRINTK("ncpfs: bufsize = %d\n", server->buffer_size); - - MOD_INC_USE_COUNT; - return sb; - - disconnect: - ncp_lock_server(server); - ncp_disconnect(server); - ncp_unlock_server(server); - ncp_kfree_s(server->packet, server->packet_size); - ncp_dont_catch_watchdog(server); - fail: - ncp_filp->f_count -= 1; - wdog_filp->f_count -= 1; - ncp_kfree_s(NCP_SBP(sb), sizeof(struct ncp_server)); - return NULL; -} - -static void -ncp_put_super(struct super_block *sb) -{ - struct ncp_server *server = NCP_SBP(sb); - - lock_super(sb); - - ncp_lock_server(server); - ncp_disconnect(server); - ncp_unlock_server(server); - - close_fp(server->ncp_filp); - - ncp_dont_catch_watchdog(server); - close_fp(server->wdog_filp); - - ncp_free_all_inodes(server); - - ncp_kfree_s(server->packet, server->packet_size); - - sb->s_dev = 0; - ncp_kfree_s(NCP_SBP(sb), sizeof(struct ncp_server)); - NCP_SBP(sb) = NULL; - - unlock_super(sb); - - MOD_DEC_USE_COUNT; -} - -static void -ncp_statfs(struct super_block *sb, struct statfs *stat) -{ - struct statfs tmp; - - /* We cannot say how much disk space is left on a mounted - NetWare Server, because free space is distributed over - volumes, and the current user might have disk quotas. So - free space is not that simple to determine. Our decision - here is to err conservatively. */ - - tmp.f_type = NCP_SUPER_MAGIC; - tmp.f_bsize = 512; - tmp.f_blocks = 0; - tmp.f_bfree = 0; - tmp.f_bavail = 0; - tmp.f_files = -1; - tmp.f_ffree = -1; - tmp.f_namelen = 12; - memcpy_tofs(stat, &tmp, sizeof(tmp)); -} - -static int -ncp_notify_change(struct inode *inode, struct iattr *attr) -{ - int result = 0; - int info_mask; - struct nw_modify_dos_info info; - - if (!ncp_conn_valid(NCP_SERVER(inode))) - { - return -EIO; - } - if ((result = inode_change_ok(inode, attr)) < 0) - return result; - - if (((attr->ia_valid & ATTR_UID) && - (attr->ia_uid != NCP_SERVER(inode)->m.uid))) - return -EPERM; - - if (((attr->ia_valid & ATTR_GID) && - (attr->ia_uid != NCP_SERVER(inode)->m.gid))) - return -EPERM; - - if (((attr->ia_valid & ATTR_MODE) && - (attr->ia_mode & - ~(S_IFREG | S_IFDIR | S_IRWXU | S_IRWXG | S_IRWXO)))) - return -EPERM; - - info_mask = 0; - memset(&info, 0, sizeof(info)); - - if ((attr->ia_valid & ATTR_CTIME) != 0) - { - info_mask |= (DM_CREATE_TIME | DM_CREATE_DATE); - ncp_date_unix2dos(attr->ia_ctime, - &(info.creationTime), &(info.creationDate)); - } - if ((attr->ia_valid & ATTR_MTIME) != 0) - { - info_mask |= (DM_MODIFY_TIME | DM_MODIFY_DATE); - ncp_date_unix2dos(attr->ia_mtime, - &(info.modifyTime), &(info.modifyDate)); - } - if ((attr->ia_valid & ATTR_ATIME) != 0) - { - __u16 dummy; - info_mask |= (DM_LAST_ACCESS_DATE); - ncp_date_unix2dos(attr->ia_ctime, - &(dummy), &(info.lastAccessDate)); - } - if (info_mask != 0) - { - if ((result = - ncp_modify_file_or_subdir_dos_info(NCP_SERVER(inode), - NCP_ISTRUCT(inode), - info_mask, - &info)) != 0) - { - result = -EACCES; - - if (info_mask == (DM_CREATE_TIME | DM_CREATE_DATE)) - { - /* NetWare seems not to allow this. I - do not know why. So, just tell the - user everything went fine. This is - a terrible hack, but I do not know - how to do this correctly. */ - result = 0; - } - } - } - if ((attr->ia_valid & ATTR_SIZE) != 0) - { - int written; - - DPRINTK("ncpfs: trying to change size of %s to %ld\n", - NCP_ISTRUCT(inode)->entryName, attr->ia_size); - - if ((result = ncp_make_open(inode, O_RDWR)) < 0) - { - return -EACCES; - } - ncp_write(NCP_SERVER(inode), NCP_FINFO(inode)->file_handle, - attr->ia_size, 0, "", &written); - - /* According to ndir, the changes only take effect after - closing the file */ - ncp_close_file(NCP_SERVER(inode), - NCP_FINFO(inode)->file_handle); - NCP_FINFO(inode)->opened = 0; - - result = 0; - } - ncp_invalid_dir_cache(NCP_INOP(inode)->dir->inode); - - return result; -} - -#ifdef DEBUG_NCP_MALLOC -int ncp_malloced; -int ncp_current_malloced; -#endif - -#ifdef MODULE - -char kernel_version[] = UTS_RELEASE; - -static struct file_system_type ncp_fs_type = -{ - ncp_read_super, "ncpfs", 0, NULL -}; - -int -init_module(void) -{ - DPRINTK("ncpfs: init_module called\n"); - -#ifdef DEBUG_NCP_MALLOC - ncp_malloced = 0; - ncp_current_malloced = 0; -#endif - - ncp_init_dir_cache(); - register_filesystem(&ncp_fs_type); - printk("ncpfs version %s loaded\n", NCPFS_VERSION); - return 0; -} - -void -cleanup_module(void) -{ - DPRINTK("ncpfs: cleanup_module called\n"); - ncp_free_dir_cache(); - unregister_filesystem(&ncp_fs_type); -#ifdef DEBUG_NCP_MALLOC - printk("ncp_malloced: %d\n", ncp_malloced); - printk("ncp_current_malloced: %d\n", ncp_current_malloced); -#endif -} - -#endif diff --git a/kernel-1.2/src/ioctl.c b/kernel-1.2/src/ioctl.c deleted file mode 100644 index 0791445..0000000 --- a/kernel-1.2/src/ioctl.c +++ /dev/null @@ -1,159 +0,0 @@ -/* - * ioctl.c - * - * Copyright (C) 1995 by Volker Lendecke - * - */ - -#include -#ifdef MODULE -#include -#include -#endif - -#include -#include -#include -#include -#include -#include -#include -#include - -int -ncp_ioctl(struct inode *inode, struct file *filp, - unsigned int cmd, unsigned long arg) -{ - int result; - struct ncp_ioctl_request request; - struct ncp_fs_info info; - struct ncp_server *server = NCP_SERVER(inode); - - /* - * Binary compatible with 1.3.XX releases. - * Take this out in 2.1.0 development series. - * 12 Mar 1996 - */ - switch (cmd) - { - case _IOR('n', 1, unsigned char *): - cmd = NCP_IOC_NCPREQUEST; - break; - case _IOR('u', 1, uid_t): - cmd = NCP_IOC_GETMOUNTUID; - break; - case _IO('l', 1): - cmd = NCP_IOC_CONN_LOGGED_IN; - break; - case _IOWR('i', 1, unsigned char *): - cmd = NCP_IOC_GET_FS_INFO; - break; - } - - switch (cmd) - { - case NCP_IOC_NCPREQUEST: - - if ((permission(inode, MAY_WRITE) != 0) - && (current->uid != server->m.mounted_uid)) - { - return -EACCES; - } - if ((result = verify_area(VERIFY_READ, (char *) arg, - sizeof(request))) != 0) - { - return result; - } - memcpy_fromfs(&request, (struct ncp_ioctl_request *) arg, - sizeof(request)); - - if ((request.function > 255) - || (request.size > - NCP_PACKET_SIZE - sizeof(struct ncp_request_header))) - { - return -EINVAL; - } - if ((result = verify_area(VERIFY_WRITE, (char *) request.data, - NCP_PACKET_SIZE)) != 0) - { - return result; - } - ncp_lock_server(server); - - /* FIXME: We hack around in the server's structures - here to be able to use ncp_request */ - - server->has_subfunction = 0; - server->current_size = request.size; - memcpy_fromfs(server->packet, request.data, request.size); - - ncp_request(server, request.function); - - DPRINTK("ncp_ioctl: copy %d bytes\n", - server->reply_size); - memcpy_tofs(request.data, server->packet, server->reply_size); - - ncp_unlock_server(server); - - return server->reply_size; - - case NCP_IOC_CONN_LOGGED_IN: - - if ((permission(inode, MAY_WRITE) != 0) - && (current->uid != server->m.mounted_uid)) - { - return -EACCES; - } - return ncp_conn_logged_in(server); - - case NCP_IOC_GET_FS_INFO: - - if ((permission(inode, MAY_WRITE) != 0) - && (current->uid != server->m.mounted_uid)) - { - return -EACCES; - } - if ((result = verify_area(VERIFY_WRITE, (char *) arg, - sizeof(info))) != 0) - { - return result; - } - memcpy_fromfs(&info, (struct ncp_fs_info *) arg, - sizeof(info)); - - if (info.version != NCP_GET_FS_INFO_VERSION) - { - DPRINTK("info.version invalid: %d\n", info.version); - return -EINVAL; - } - info.addr = server->m.serv_addr; - info.mounted_uid = server->m.mounted_uid; - info.connection = server->connection; - info.buffer_size = server->buffer_size; - info.volume_number = NCP_ISTRUCT(inode)->volNumber; - info.directory_id = NCP_ISTRUCT(inode)->DosDirNum; - - memcpy_tofs((struct ncp_fs_info *) arg, &info, sizeof(info)); - return 0; - - case NCP_IOC_GETMOUNTUID: - - if ((permission(inode, MAY_READ) != 0) - && (current->uid != server->m.mounted_uid)) - { - return -EACCES; - } - if ((result = verify_area(VERIFY_WRITE, (uid_t *) arg, - sizeof(uid_t))) != 0) - { - return result; - } - put_fs_word(server->m.mounted_uid, (uid_t *) arg); - return 0; - - default: - return -EINVAL; - } - - return -EINVAL; -} diff --git a/kernel-1.2/src/mmap.c b/kernel-1.2/src/mmap.c deleted file mode 100644 index ec7f794..0000000 --- a/kernel-1.2/src/mmap.c +++ /dev/null @@ -1,155 +0,0 @@ -/* - * mmap.c - * - * Copyright (C) 1995 by Volker Lendecke - * - */ - -#include -#ifdef MODULE -#include -#include -#endif - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "ncplib_kernel.h" -#include -#include - -static inline int -min(int a, int b) -{ - return a < b ? a : b; -} - -/* - * Fill in the supplied page for mmap - */ -static unsigned long -ncp_file_mmap_nopage(struct vm_area_struct *area, - unsigned long address, unsigned long page, int no_share) -{ - struct inode *inode = area->vm_inode; - unsigned int clear; - unsigned long tmp; - int bufsize; - int pos; - unsigned short fs; - - address &= PAGE_MASK; - pos = address - area->vm_start + area->vm_offset; - - clear = 0; - if (address + PAGE_SIZE > area->vm_end) - { - clear = address + PAGE_SIZE - area->vm_end; - } - /* what we can read in one go */ - bufsize = NCP_SERVER(inode)->buffer_size; - - fs = get_fs(); - set_fs(get_ds()); - - if (ncp_make_open(inode, O_RDONLY) < 0) - { - clear = PAGE_SIZE; - } else - { - int already_read = 0; - int count = PAGE_SIZE - clear; - int to_read; - - while (already_read < count) - { - int read_this_time; - - if ((pos % bufsize) != 0) - { - to_read = bufsize - (pos % bufsize); - } else - { - to_read = bufsize; - } - - to_read = min(to_read, count - already_read); - - if (ncp_read(NCP_SERVER(inode), - NCP_FINFO(inode)->file_handle, - pos, to_read, - (char *) (page + already_read), - &read_this_time) != 0) - { - read_this_time = 0; - } - pos += read_this_time; - already_read += read_this_time; - - if (read_this_time < to_read) - { - break; - } - } - - } - - set_fs(fs); - - tmp = page + PAGE_SIZE; - while (clear--) - { - *(char *) --tmp = 0; - } - return page; -} - -struct vm_operations_struct ncp_file_mmap = -{ - NULL, /* open */ - NULL, /* close */ - NULL, /* unmap */ - NULL, /* protect */ - NULL, /* sync */ - NULL, /* advise */ - ncp_file_mmap_nopage, /* nopage */ - NULL, /* wppage */ - NULL, /* swapout */ - NULL, /* swapin */ -}; - - -/* This is used for a general mmap of a ncp file */ -int -ncp_mmap(struct inode *inode, struct file *file, struct vm_area_struct *vma) -{ - DPRINTK("ncp_mmap: called\n"); - - if (!ncp_conn_valid(NCP_SERVER(inode))) - { - return -EIO; - } - /* only PAGE_COW or read-only supported now */ - if (vma->vm_flags & VM_SHARED) - return -EINVAL; - if (!inode->i_sb || !S_ISREG(inode->i_mode)) - return -EACCES; - if (!IS_RDONLY(inode)) - { - inode->i_atime = CURRENT_TIME; - inode->i_dirt = 1; - } - vma->vm_inode = inode; - inode->i_count++; - vma->vm_ops = &ncp_file_mmap; - return 0; -} diff --git a/kernel-1.2/src/ncplib_kernel.c b/kernel-1.2/src/ncplib_kernel.c deleted file mode 100644 index dd741ce..0000000 --- a/kernel-1.2/src/ncplib_kernel.c +++ /dev/null @@ -1,622 +0,0 @@ - -#include -#ifdef MODULE -#include -#include -#endif - -#include "ncplib_kernel.h" - -typedef __u8 byte; -typedef __u16 word; -typedef __u32 dword; - -static inline int -min(int a, int b) -{ - return a < b ? a : b; -} - -static void -assert_server_locked(struct ncp_server *server) -{ - if (server->lock == 0) - { - DPRINTK("ncpfs: server not locked!\n"); - } -} - -static void -ncp_add_byte(struct ncp_server *server, byte x) -{ - assert_server_locked(server); - *(byte *) (&(server->packet[server->current_size])) = x; - server->current_size += 1; - return; -} - -static void -ncp_add_word(struct ncp_server *server, word x) -{ - assert_server_locked(server); - *(word *) (&(server->packet[server->current_size])) = x; - server->current_size += 2; - return; -} - -static void -ncp_add_dword(struct ncp_server *server, dword x) -{ - assert_server_locked(server); - *(dword *) (&(server->packet[server->current_size])) = x; - server->current_size += 4; - return; -} - -static void -ncp_add_mem(struct ncp_server *server, const void *source, int size) -{ - assert_server_locked(server); - memcpy(&(server->packet[server->current_size]), source, size); - server->current_size += size; - return; -} - -static void -ncp_add_mem_fromfs(struct ncp_server *server, const char *source, int size) -{ - assert_server_locked(server); - memcpy_fromfs(&(server->packet[server->current_size]), source, size); - server->current_size += size; - return; -} - -static void -ncp_add_pstring(struct ncp_server *server, const char *s) -{ - int len = strlen(s); - assert_server_locked(server); - if (len > 255) - { - DPRINTK("ncpfs: string too long: %s\n", s); - len = 255; - } - ncp_add_byte(server, len); - ncp_add_mem(server, s, len); - return; -} - -static void -ncp_init_request(struct ncp_server *server) -{ - ncp_lock_server(server); - - server->current_size = sizeof(struct ncp_request_header); - server->has_subfunction = 0; -} - -static void -ncp_init_request_s(struct ncp_server *server, int subfunction) -{ - ncp_init_request(server); - ncp_add_word(server, 0); /* preliminary size */ - - ncp_add_byte(server, subfunction); - - server->has_subfunction = 1; -} - -static char * -ncp_reply_data(struct ncp_server *server, int offset) -{ - return &(server->packet[sizeof(struct ncp_reply_header) + offset]); -} - -static byte -ncp_reply_byte(struct ncp_server *server, int offset) -{ - return *(byte *) (ncp_reply_data(server, offset)); -} - -static word -ncp_reply_word(struct ncp_server *server, int offset) -{ - return *(word *) (ncp_reply_data(server, offset)); -} - -static dword -ncp_reply_dword(struct ncp_server *server, int offset) -{ - return *(dword *) (ncp_reply_data(server, offset)); -} - -int -ncp_negotiate_buffersize(struct ncp_server *server, - int size, int *target) -{ - int result; - - ncp_init_request(server); - ncp_add_word(server, htons(size)); - - if ((result = ncp_request(server, 33)) != 0) - { - ncp_unlock_server(server); - return result; - } - *target = min(ntohs(ncp_reply_word(server, 0)), size); - - ncp_unlock_server(server); - return 0; -} - -int -ncp_get_volume_info_with_number(struct ncp_server *server, int n, - struct ncp_volume_info *target) -{ - int result; - int len; - - ncp_init_request_s(server, 44); - ncp_add_byte(server, n); - - if ((result = ncp_request(server, 22)) != 0) - { - ncp_unlock_server(server); - return result; - } - target->total_blocks = ncp_reply_dword(server, 0); - target->free_blocks = ncp_reply_dword(server, 4); - target->purgeable_blocks = ncp_reply_dword(server, 8); - target->not_yet_purgeable_blocks = ncp_reply_dword(server, 12); - target->total_dir_entries = ncp_reply_dword(server, 16); - target->available_dir_entries = ncp_reply_dword(server, 20); - target->sectors_per_block = ncp_reply_byte(server, 28); - - memset(&(target->volume_name), 0, sizeof(target->volume_name)); - - len = ncp_reply_byte(server, 29); - if (len > NCP_VOLNAME_LEN) - { - DPRINTK("ncpfs: volume name too long: %d\n", len); - ncp_unlock_server(server); - return -EIO; - } - memcpy(&(target->volume_name), ncp_reply_data(server, 30), len); - ncp_unlock_server(server); - return 0; -} - -int -ncp_close_file(struct ncp_server *server, const char *file_id) -{ - int result; - - ncp_init_request(server); - ncp_add_byte(server, 0); - ncp_add_mem(server, file_id, 6); - - if ((result = ncp_request(server, 66)) != 0) - { - ncp_unlock_server(server); - return result; - } - ncp_unlock_server(server); - return 0; -} - -static void -ncp_add_handle_path(struct ncp_server *server, - __u8 vol_num, - __u32 dir_base, int have_dir_base, - char *path) -{ - ncp_add_byte(server, vol_num); - ncp_add_dword(server, dir_base); - if (have_dir_base != 0) - { - ncp_add_byte(server, 1); /* dir_base */ - } else - { - ncp_add_byte(server, 0xff); /* no handle */ - } - if (path != NULL) - { - ncp_add_byte(server, 1); /* 1 component */ - ncp_add_pstring(server, path); - } else - { - ncp_add_byte(server, 0); - } -} - -static void -ncp_extract_file_info(void *structure, struct nw_info_struct *target) -{ - __u8 *name_len; - const int info_struct_size = sizeof(struct nw_info_struct) - 257; - - memcpy(target, structure, info_struct_size); - name_len = structure + info_struct_size; - target->nameLen = *name_len; - strncpy(target->entryName, name_len + 1, *name_len); - target->entryName[*name_len] = '\0'; - return; -} - -int -ncp_obtain_info(struct ncp_server *server, - __u8 vol_num, __u32 dir_base, - char *path, /* At most 1 component */ - struct nw_info_struct *target) -{ - int result; - - if (target == NULL) - { - return -EINVAL; - } - ncp_init_request(server); - ncp_add_byte(server, 6); /* subfunction */ - ncp_add_byte(server, server->name_space[vol_num]); - ncp_add_byte(server, server->name_space[vol_num]); - ncp_add_word(server, 0xff); /* get all */ - ncp_add_dword(server, RIM_ALL); - ncp_add_handle_path(server, vol_num, dir_base, 1, path); - - if ((result = ncp_request(server, 87)) != 0) - { - ncp_unlock_server(server); - return result; - } - ncp_extract_file_info(ncp_reply_data(server, 0), target); - ncp_unlock_server(server); - return 0; -} - -static inline int -ncp_has_os2_namespace(struct ncp_server *server, __u8 volume) -{ - int result; - __u8 *namespace; - __u16 no_namespaces; - - ncp_init_request(server); - ncp_add_byte(server, 24); /* Subfunction: Get Name Spaces Loaded */ - ncp_add_word(server, 0); - ncp_add_byte(server, volume); - - if ((result = ncp_request(server, 87)) != 0) - { - ncp_unlock_server(server); - return 0; - } - no_namespaces = ncp_reply_word(server, 0); - namespace = ncp_reply_data(server, 2); - - while (no_namespaces > 0) - { - DPRINTK("get_namespaces: found %d on %d\n", *namespace, volume); - - if (*namespace == 4) - { - DPRINTK("get_namespaces: found OS2\n"); - ncp_unlock_server(server); - return 1; - } - namespace += 1; - no_namespaces -= 1; - } - ncp_unlock_server(server); - return 0; -} - -int -ncp_lookup_volume(struct ncp_server *server, - char *volname, - struct nw_info_struct *target) -{ - int result; - int volnum; - - DPRINTK("ncp_lookup_volume: looking up vol %s\n", volname); - - ncp_init_request(server); - ncp_add_byte(server, 22); /* Subfunction: Generate dir handle */ - ncp_add_byte(server, 0); /* DOS namespace */ - ncp_add_byte(server, 0); /* reserved */ - ncp_add_byte(server, 0); /* reserved */ - ncp_add_byte(server, 0); /* reserved */ - - ncp_add_byte(server, 0); /* faked volume number */ - ncp_add_dword(server, 0); /* faked dir_base */ - ncp_add_byte(server, 0xff); /* Don't have a dir_base */ - ncp_add_byte(server, 1); /* 1 path component */ - ncp_add_pstring(server, volname); - - if ((result = ncp_request(server, 87)) != 0) - { - ncp_unlock_server(server); - return result; - } - memset(target, 0, sizeof(*target)); - target->DosDirNum = target->dirEntNum = ncp_reply_dword(server, 4); - target->volNumber = volnum = ncp_reply_byte(server, 8); - ncp_unlock_server(server); - - server->name_space[volnum] = ncp_has_os2_namespace(server, volnum) ? 4 : 0; - - DPRINTK("lookup_vol: namespace[%d] = %d\n", - volnum, server->name_space[volnum]); - - target->nameLen = strlen(volname); - strcpy(target->entryName, volname); - target->attributes = aDIR; - return 0; -} - -int -ncp_modify_file_or_subdir_dos_info(struct ncp_server *server, - struct nw_info_struct *file, - __u32 info_mask, - struct nw_modify_dos_info *info) -{ - int result; - - ncp_init_request(server); - ncp_add_byte(server, 7); /* subfunction */ - ncp_add_byte(server, server->name_space[file->volNumber]); - ncp_add_byte(server, 0); /* reserved */ - ncp_add_word(server, 0x8006); /* search attribs: all */ - - ncp_add_dword(server, info_mask); - ncp_add_mem(server, info, sizeof(*info)); - ncp_add_handle_path(server, file->volNumber, - file->dirEntNum, 1, NULL); - - if ((result = ncp_request(server, 87)) != 0) - { - ncp_unlock_server(server); - return result; - } - ncp_unlock_server(server); - return 0; -} - -int -ncp_del_file_or_subdir(struct ncp_server *server, - struct nw_info_struct *dir, char *name) -{ - int result; - - ncp_init_request(server); - ncp_add_byte(server, 8); /* subfunction */ - ncp_add_byte(server, server->name_space[dir->volNumber]); - ncp_add_byte(server, 0); /* reserved */ - ncp_add_word(server, 0x8006); /* search attribs: all */ - ncp_add_handle_path(server, dir->volNumber, - dir->dirEntNum, 1, name); - - if ((result = ncp_request(server, 87)) != 0) - { - ncp_unlock_server(server); - return result; - } - ncp_unlock_server(server); - return 0; -} - -static inline void -ConvertToNWfromDWORD(__u32 sfd, __u8 ret[6]) -{ - __u16 *dest = (__u16 *) ret; - memcpy(&(dest[1]), &sfd, 4); - dest[0] = dest[1] + 1; - return; -} - -/* If both dir and name are NULL, then in target there's already a - looked-up entry that wants to be opened. */ -int -ncp_open_create_file_or_subdir(struct ncp_server *server, - struct nw_info_struct *dir, char *name, - int open_create_mode, - __u32 create_attributes, - int desired_acc_rights, - struct nw_file_info *target) -{ - int result; - __u16 search_attribs = 0x0006; - __u8 volume = (dir != NULL) ? dir->volNumber : target->i.volNumber; - - if ((create_attributes & aDIR) != 0) - { - search_attribs |= 0x8000; - } - ncp_init_request(server); - ncp_add_byte(server, 1); /* subfunction */ - ncp_add_byte(server, server->name_space[volume]); - ncp_add_byte(server, open_create_mode); - ncp_add_word(server, search_attribs); - ncp_add_dword(server, RIM_ALL); - ncp_add_dword(server, create_attributes); - /* The desired acc rights seem to be the inherited rights mask - for directories */ - ncp_add_word(server, desired_acc_rights); - - if (dir != NULL) - { - ncp_add_handle_path(server, volume, dir->dirEntNum, 1, name); - } else - { - ncp_add_handle_path(server, volume, target->i.dirEntNum, - 1, NULL); - } - - if ((result = ncp_request(server, 87)) != 0) - { - ncp_unlock_server(server); - return result; - } - target->opened = 1; - target->server_file_handle = ncp_reply_dword(server, 0); - target->open_create_action = ncp_reply_byte(server, 4); - - if (dir != NULL) - { - /* in target there's a new finfo to fill */ - ncp_extract_file_info(ncp_reply_data(server, 5), &(target->i)); - } - ConvertToNWfromDWORD(target->server_file_handle, target->file_handle); - - ncp_unlock_server(server); - return 0; -} - - -int -ncp_initialize_search(struct ncp_server *server, - struct nw_info_struct *dir, - struct nw_search_sequence *target) -{ - int result; - - ncp_init_request(server); - ncp_add_byte(server, 2); /* subfunction */ - ncp_add_byte(server, server->name_space[dir->volNumber]); - ncp_add_byte(server, 0); /* reserved */ - ncp_add_handle_path(server, dir->volNumber, dir->dirEntNum, 1, NULL); - - if ((result = ncp_request(server, 87)) != 0) - { - ncp_unlock_server(server); - return result; - } - memcpy(target, ncp_reply_data(server, 0), sizeof(*target)); - - ncp_unlock_server(server); - return 0; -} - -/* Search for everything */ -int -ncp_search_for_file_or_subdir(struct ncp_server *server, - struct nw_search_sequence *seq, - struct nw_info_struct *target) -{ - int result; - - ncp_init_request(server); - ncp_add_byte(server, 3); /* subfunction */ - ncp_add_byte(server, server->name_space[seq->volNumber]); - ncp_add_byte(server, 0); /* data stream (???) */ - ncp_add_word(server, 0xffff); /* Search attribs */ - ncp_add_dword(server, RIM_ALL); /* return info mask */ - ncp_add_mem(server, seq, 9); - ncp_add_byte(server, 2); /* 2 byte pattern */ - ncp_add_byte(server, 0xff); /* following is a wildcard */ - ncp_add_byte(server, '*'); - - if ((result = ncp_request(server, 87)) != 0) - { - ncp_unlock_server(server); - return result; - } - memcpy(seq, ncp_reply_data(server, 0), sizeof(*seq)); - ncp_extract_file_info(ncp_reply_data(server, 10), target); - - ncp_unlock_server(server); - return 0; -} - -int -ncp_ren_or_mov_file_or_subdir(struct ncp_server *server, - struct nw_info_struct *old_dir, char *old_name, - struct nw_info_struct *new_dir, char *new_name) -{ - int result; - - if ((old_dir == NULL) || (old_name == NULL) - || (new_dir == NULL) || (new_name == NULL)) - return -EINVAL; - - ncp_init_request(server); - ncp_add_byte(server, 4); /* subfunction */ - ncp_add_byte(server, server->name_space[old_dir->volNumber]); - ncp_add_byte(server, 1); /* rename flag */ - ncp_add_word(server, 0x8006); /* search attributes */ - - /* source Handle Path */ - ncp_add_byte(server, old_dir->volNumber); - ncp_add_dword(server, old_dir->dirEntNum); - ncp_add_byte(server, 1); - ncp_add_byte(server, 1); /* 1 source component */ - - /* dest Handle Path */ - ncp_add_byte(server, new_dir->volNumber); - ncp_add_dword(server, new_dir->dirEntNum); - ncp_add_byte(server, 1); - ncp_add_byte(server, 1); /* 1 destination component */ - - /* source path string */ - ncp_add_pstring(server, old_name); - /* dest path string */ - ncp_add_pstring(server, new_name); - - result = ncp_request(server, 87); - ncp_unlock_server(server); - return result; -} - - -/* We have to transfer to/from user space */ -int -ncp_read(struct ncp_server *server, const char *file_id, - __u32 offset, __u16 to_read, - char *target, int *bytes_read) -{ - int result; - - ncp_init_request(server); - ncp_add_byte(server, 0); - ncp_add_mem(server, file_id, 6); - ncp_add_dword(server, htonl(offset)); - ncp_add_word(server, htons(to_read)); - - if ((result = ncp_request(server, 72)) != 0) - { - ncp_unlock_server(server); - return result; - } - *bytes_read = ntohs(ncp_reply_word(server, 0)); - - memcpy_tofs(target, ncp_reply_data(server, 2 + (offset & 1)), *bytes_read); - - ncp_unlock_server(server); - return 0; -} - -int -ncp_write(struct ncp_server *server, const char *file_id, - __u32 offset, __u16 to_write, - const char *source, int *bytes_written) -{ - int result; - - ncp_init_request(server); - ncp_add_byte(server, 0); - ncp_add_mem(server, file_id, 6); - ncp_add_dword(server, htonl(offset)); - ncp_add_word(server, htons(to_write)); - ncp_add_mem_fromfs(server, source, to_write); - - if ((result = ncp_request(server, 73)) != 0) - { - ncp_unlock_server(server); - return result; - } - *bytes_written = to_write; - - ncp_unlock_server(server); - return 0; -} diff --git a/kernel-1.2/src/ncplib_kernel.h b/kernel-1.2/src/ncplib_kernel.h deleted file mode 100644 index 2abcc4c..0000000 --- a/kernel-1.2/src/ncplib_kernel.h +++ /dev/null @@ -1,163 +0,0 @@ - -#ifndef _NCPLIB_H -#define _NCPLIB_H - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -int - ncp_negotiate_buffersize(struct ncp_server *server, int size, - int *target); -int - ncp_get_encryption_key(struct ncp_server *server, - char *target); -int - ncp_get_bindery_object_id(struct ncp_server *server, - int object_type, char *object_name, - struct ncp_bindery_object *target); -int - ncp_login_encrypted(struct ncp_server *server, - struct ncp_bindery_object *object, - unsigned char *key, - unsigned char *passwd); -int - ncp_login_user(struct ncp_server *server, - unsigned char *username, - unsigned char *password); -int - ncp_get_volume_info_with_number(struct ncp_server *server, int n, - struct ncp_volume_info *target); - -int - ncp_get_volume_number(struct ncp_server *server, const char *name, - int *target); - -int - ncp_file_search_init(struct ncp_server *server, - int dir_handle, const char *path, - struct ncp_filesearch_info *target); - -int - ncp_file_search_continue(struct ncp_server *server, - struct ncp_filesearch_info *fsinfo, - int attributes, const char *path, - struct ncp_file_info *target); - -int - ncp_get_finfo(struct ncp_server *server, - int dir_handle, const char *path, const char *name, - struct ncp_file_info *target); - -int - ncp_open_file(struct ncp_server *server, - int dir_handle, const char *path, - int attr, int access, - struct ncp_file_info *target); -int - ncp_close_file(struct ncp_server *server, const char *file_id); - -int - ncp_create_newfile(struct ncp_server *server, - int dir_handle, const char *path, - int attr, - struct ncp_file_info *target); - -int - ncp_create_file(struct ncp_server *server, - int dir_handle, const char *path, - int attr, - struct ncp_file_info *target); - -int - ncp_erase_file(struct ncp_server *server, - int dir_handle, const char *path, - int attr); - -int - ncp_rename_file(struct ncp_server *server, - int old_handle, const char *old_path, - int attr, - int new_handle, const char *new_path); - -int - ncp_create_directory(struct ncp_server *server, - int dir_handle, const char *path, - int inherit_mask); - -int - ncp_delete_directory(struct ncp_server *server, - int dir_handle, const char *path); - -int - ncp_rename_directory(struct ncp_server *server, - int dir_handle, - const char *old_path, const char *new_path); - -int - ncp_read(struct ncp_server *server, const char *file_id, - __u32 offset, __u16 to_read, - char *target, int *bytes_read); - -int - ncp_write(struct ncp_server *server, const char *file_id, - __u32 offset, __u16 to_write, - const char *source, int *bytes_written); - -int - ncp_obtain_info(struct ncp_server *server, - __u8 vol_num, __u32 dir_base, - char *path, /* At most 1 component */ - struct nw_info_struct *target); - -int - ncp_lookup_volume(struct ncp_server *server, - char *volname, - struct nw_info_struct *target); - - -int - ncp_modify_file_or_subdir_dos_info(struct ncp_server *server, - struct nw_info_struct *file, - __u32 info_mask, - struct nw_modify_dos_info *info); - -int - ncp_del_file_or_subdir(struct ncp_server *server, - struct nw_info_struct *dir, char *name); - -int - ncp_open_create_file_or_subdir(struct ncp_server *server, - struct nw_info_struct *dir, char *name, - int open_create_mode, - __u32 create_attributes, - int desired_acc_rights, - struct nw_file_info *target); - -int - ncp_initialize_search(struct ncp_server *server, - struct nw_info_struct *dir, - struct nw_search_sequence *target); - -int - ncp_search_for_file_or_subdir(struct ncp_server *server, - struct nw_search_sequence *seq, - struct nw_info_struct *target); - -int - ncp_ren_or_mov_file_or_subdir(struct ncp_server *server, - struct nw_info_struct *old_dir, char *old_name, - struct nw_info_struct *new_dir, char *new_name); - - -#endif /* _NCPLIB_H */ diff --git a/kernel-1.2/src/sock.c b/kernel-1.2/src/sock.c deleted file mode 100644 index af82549..0000000 --- a/kernel-1.2/src/sock.c +++ /dev/null @@ -1,555 +0,0 @@ -/* - * linux/fs/ncp/sock.c - * - * Copyright (C) 1992, 1993 Rick Sladkey - * - * Modified 1995 by Volker Lendecke to be usable for ncp - * - */ - -#include -#ifdef MODULE -#include -#include -#endif - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include "/usr/src/linux/net/inet/sock.h" - - -#define _S(nr) (1<<((nr)-1)) -static void -ncp_wdog_data_ready(struct sock *sk, int len) -{ - struct socket *sock = sk->socket; - - if (!sk->dead) - { - unsigned char packet_buf[2]; - struct sockaddr_ipx sender; - int addr_len = sizeof(struct sockaddr_ipx); - int result; - unsigned short fs; - - fs = get_fs(); - set_fs(get_ds()); - - result = sock->ops->recvfrom(sock, (void *) packet_buf, 2, 1, 0, - (struct sockaddr *) &sender, - &addr_len); - - if ((result != 2) - || (packet_buf[1] != '?') - /* How to check connection number here? */ - ) - { - /* Error, throw away the complete packet */ - sock->ops->recvfrom(sock, (void *) packet_buf, 2, 1, 0, - (struct sockaddr *) &sender, - &addr_len); - - printk("ncpfs: got strange packet on watchdog " - "socket\n"); - - } else - { - int result; - DDPRINTK("ncpfs: got watchdog from:\n"); - DDPRINTK("ncpfs: %08lX:%02X%02X%02X%02X%02X%02X:%04X," - " conn:%02X,type:%c\n", - htonl(sender.sipx_network), - sender.sipx_node[0], sender.sipx_node[1], - sender.sipx_node[2], sender.sipx_node[3], - sender.sipx_node[4], sender.sipx_node[5], - ntohs(sender.sipx_port), - packet_buf[0], packet_buf[1]); - - packet_buf[1] = 'Y'; - result = sock->ops->sendto(sock, (void *) packet_buf, - 2, 1, 0, - (struct sockaddr *) &sender, - sizeof(sender)); - DDPRINTK("send result: %d\n", result); - } - set_fs(fs); - } -} - - -int -ncp_catch_watchdog(struct ncp_server *server) -{ - struct file *file; - struct inode *inode; - struct socket *sock; - struct sock *sk; - - if ((server == NULL) - || ((file = server->wdog_filp) == NULL) - || ((inode = file->f_inode) == NULL) - || (!S_ISSOCK(inode->i_mode))) - { - printk("ncp_catch_watchdog: did not get valid server!\n"); - server->data_ready = NULL; - return -EINVAL; - } - sock = &(inode->u.socket_i); - - if (sock->type != SOCK_DGRAM) - { - printk("ncp_catch_watchdog: did not get SOCK_STREAM\n"); - server->data_ready = NULL; - return -EINVAL; - } - sk = (struct sock *) (sock->data); - - if (sk == NULL) - { - printk("ncp_catch_watchdog: sk == NULL"); - server->data_ready = NULL; - return -EINVAL; - } - DDPRINTK("ncp_catch_watchdog.: sk->d_r = %x, server->d_r = %x\n", - (unsigned int) (sk->data_ready), - (unsigned int) (server->data_ready)); - - if (sk->data_ready == ncp_wdog_data_ready) - { - printk("ncp_catch_watchdog: already done\n"); - return -EINVAL; - } - server->data_ready = sk->data_ready; - sk->data_ready = ncp_wdog_data_ready; - return 0; -} - -int -ncp_dont_catch_watchdog(struct ncp_server *server) -{ - struct file *file; - struct inode *inode; - struct socket *sock; - struct sock *sk; - - if ((server == NULL) - || ((file = server->wdog_filp) == NULL) - || ((inode = file->f_inode) == NULL) - || (!S_ISSOCK(inode->i_mode))) - { - printk("ncp_dont_catch_watchdog: " - "did not get valid server!\n"); - return -EINVAL; - } - sock = &(inode->u.socket_i); - - if (sock->type != SOCK_DGRAM) - { - printk("ncp_dont_catch_watchdog: did not get SOCK_STREAM\n"); - return -EINVAL; - } - sk = (struct sock *) (sock->data); - - if (sk == NULL) - { - printk("ncp_dont_catch_watchdog: sk == NULL"); - return -EINVAL; - } - if (server->data_ready == NULL) - { - printk("ncp_dont_catch_watchdog: " - "server->data_ready == NULL\n"); - return -EINVAL; - } - if (sk->data_ready != ncp_wdog_data_ready) - { - printk("ncp_dont_catch_watchdog: " - "sk->data_callback != ncp_data_callback\n"); - return -EINVAL; - } - DDPRINTK("ncp_dont_catch_watchdog: sk->d_r = %x, server->d_r = %x\n", - (unsigned int) (sk->data_ready), - (unsigned int) (server->data_ready)); - - sk->data_ready = server->data_ready; - server->data_ready = NULL; - return 0; -} - - - -#define NCP_SLACK_SPACE 1024 - -#define _S(nr) (1<<((nr)-1)) - -static int -do_ncp_rpc_call(struct ncp_server *server, int size) -{ - struct file *file; - struct inode *inode; - struct socket *sock; - unsigned short fs; - int result; - char *start = server->packet; - select_table wait_table; - struct select_table_entry entry; - int (*select) (struct inode *, struct file *, int, select_table *); - int init_timeout, max_timeout; - int timeout; - int retrans; - int major_timeout_seen; - int acknowledge_seen; - int n; - int addrlen; - unsigned long old_mask; - - /* We have to check the result, so store the complete header */ - struct ncp_request_header request = - *((struct ncp_request_header *) (server->packet)); - - struct ncp_reply_header reply; - - - file = server->ncp_filp; - inode = file->f_inode; - select = file->f_op->select; - sock = &inode->u.socket_i; - if (!sock) - { - printk("ncp_rpc_call: socki_lookup failed\n"); - return -EBADF; - } - init_timeout = server->m.time_out; - max_timeout = NCP_MAX_RPC_TIMEOUT; - acknowledge_seen = 0; - retrans = server->m.retry_count; - major_timeout_seen = 0; - old_mask = current->blocked; - current->blocked |= ~(_S(SIGKILL) -#if 0 - | _S(SIGSTOP) -#endif - | ((server->m.flags & NCP_MOUNT_INTR) - ? ((current->sigaction[SIGINT - 1].sa_handler == SIG_DFL - ? _S(SIGINT) : 0) - | (current->sigaction[SIGQUIT - 1].sa_handler == SIG_DFL - ? _S(SIGQUIT) : 0)) - : 0)); - fs = get_fs(); - set_fs(get_ds()); - for (n = 0, timeout = init_timeout;; n++, timeout <<= 1) - { - DDPRINTK("ncpfs: %08lX:%02X%02X%02X%02X%02X%02X:%04X\n", - htonl(server->m.serv_addr.sipx_network), - server->m.serv_addr.sipx_node[0], - server->m.serv_addr.sipx_node[1], - server->m.serv_addr.sipx_node[2], - server->m.serv_addr.sipx_node[3], - server->m.serv_addr.sipx_node[4], - server->m.serv_addr.sipx_node[5], - ntohs(server->m.serv_addr.sipx_port)); - DDPRINTK("ncpfs: req.typ: %04X, con: %d, " - "seq: %d", - request.type, - (request.conn_high << 8) + request.conn_low, - request.sequence); - DDPRINTK(" func: %d\n", - request.function); - - result = sock->ops->sendto(sock, (void *) start, size, 0, 0, - (struct sockaddr *) - &(server->m.serv_addr), - sizeof(server->m.serv_addr)); - if (result < 0) - { - printk("ncp_rpc_call: send error = %d\n", result); - break; - } - re_select: - wait_table.nr = 0; - wait_table.entry = &entry; - current->state = TASK_INTERRUPTIBLE; - if (!select(inode, file, SEL_IN, &wait_table) - && !select(inode, file, SEL_IN, NULL)) - { - if (timeout > max_timeout) - { - /* JEJB/JSP 2/7/94 - * This is useful to see if the system is - * hanging */ - if (acknowledge_seen == 0) - { - printk("NCP max timeout reached\n"); - } - timeout = max_timeout; - } - current->timeout = jiffies + timeout; - schedule(); - remove_wait_queue(entry.wait_address, &entry.wait); - current->state = TASK_RUNNING; - if (current->signal & ~current->blocked) - { - current->timeout = 0; - result = -ERESTARTSYS; - break; - } - if (!current->timeout) - { - if (n < retrans) - continue; - if (server->m.flags & NCP_MOUNT_SOFT) - { - printk("NCP server not responding\n"); - result = -EIO; - break; - } - n = 0; - timeout = init_timeout; - init_timeout <<= 1; - if (!major_timeout_seen) - { - printk("NCP server not responding\n"); - } - major_timeout_seen = 1; - continue; - } else - current->timeout = 0; - } else if (wait_table.nr) - remove_wait_queue(entry.wait_address, &entry.wait); - current->state = TASK_RUNNING; - addrlen = 0; - - /* Get the header from the next packet using a peek, so keep it - * on the recv queue. If it is wrong, it will be some reply - * we don't now need, so discard it */ - result = sock->ops->recvfrom(sock, (void *) &reply, - sizeof(reply), 1, MSG_PEEK, - NULL, &addrlen); - if (result < 0) - { - if (result == -EAGAIN) - { - DPRINTK("ncp_rpc_call: bad select ready\n"); - goto re_select; - } - if (result == -ECONNREFUSED) - { - DPRINTK("ncp_rpc_call: server playing coy\n"); - goto re_select; - } - if (result != -ERESTARTSYS) - { - printk("ncp_rpc_call: recv error = %d\n", - -result); - } - break; - } - if ((result == sizeof(reply)) - && (reply.type == NCP_POSITIVE_ACK)) - { - /* Throw away the packet */ - DPRINTK("ncp_rpc_call: got positive acknowledge\n"); - sock->ops->recvfrom(sock, (void *) &reply, - sizeof(reply), 1, 0, - NULL, &addrlen); - n = 0; - timeout = max_timeout; - acknowledge_seen = 1; - goto re_select; - } - DDPRINTK("ncpfs: rep.typ: %04X, con: %d, tsk: %d," - "seq: %d\n", - reply.type, - (reply.conn_high << 8) + reply.conn_low, - reply.task, - reply.sequence); - - if ((result >= sizeof(reply)) - && (reply.type == NCP_REPLY) - && ((request.type == NCP_ALLOC_SLOT_REQUEST) - || ((reply.sequence == request.sequence) - && (reply.conn_low == request.conn_low) -/* seem to get wrong task from NW311 && (reply.task == request.task) */ - && (reply.conn_high == request.conn_high)))) - { - if (major_timeout_seen) - printk("NCP server OK\n"); - break; - } - /* JEJB/JSP 2/7/94 - * we have xid mismatch, so discard the packet and start - * again. What a hack! but I can't call recvfrom with - * a null buffer yet. */ - sock->ops->recvfrom(sock, (void *) &reply, sizeof(reply), 1, 0, - NULL, &addrlen); - - DPRINTK("ncp_rpc_call: reply mismatch\n"); - goto re_select; - } - /* - * we have the correct reply, so read into the correct place and - * return it - */ - result = sock->ops->recvfrom(sock, (void *) start, server->packet_size, - 1, 0, NULL, &addrlen); - if (result < 0) - { - printk("NCP: notice message: result=%d\n", result); - } else if (result < sizeof(struct ncp_reply_header)) - { - printk("NCP: just caught a too small read memory size..., " - "email to NET channel\n"); - printk("NCP: result=%d,addrlen=%d\n", result, addrlen); - result = -EIO; - } - current->blocked = old_mask; - set_fs(fs); - return result; -} - - -/* - * We need the server to be locked here, so check! - */ - -static int -ncp_do_request(struct ncp_server *server, int size) -{ - if (server->lock == 0) - { - printk("ncpfs: Server not locked!\n"); - return -EIO; - } - return do_ncp_rpc_call(server, size); -} - -/* ncp_do_request assures that at least a complete reply header is - * received. It assumes that server->current_size contains the ncp - * request size */ -int -ncp_request(struct ncp_server *server, int function) -{ - struct ncp_request_header *h - = (struct ncp_request_header *) (server->packet); - struct ncp_reply_header *reply - = (struct ncp_reply_header *) (server->packet); - - int request_size = server->current_size - - sizeof(struct ncp_request_header); - - int result; - - if (server->has_subfunction != 0) - { - *(__u16 *) & (h->data[0]) = request_size - 2; - } - h->type = NCP_REQUEST; - - server->sequence += 1; - h->sequence = server->sequence; - h->conn_low = (server->connection) & 0xff; - h->conn_high = ((server->connection) & 0xff00) >> 8; - h->task = (current->pid) & 0xff; - h->function = function; - - if ((result = ncp_do_request(server, request_size + sizeof(*h))) < 0) - { - DPRINTK("ncp_request_error: %d\n", result); - return result; - } - server->completion = reply->completion_code; - server->conn_status = reply->connection_state; - server->reply_size = result; - server->ncp_reply_size = result - sizeof(struct ncp_reply_header); - - result = reply->completion_code; - - if (result != 0) - { - DPRINTK("ncp_completion_code: %x\n", result); - } - return result; -} - -int -ncp_connect(struct ncp_server *server) -{ - struct ncp_request_header *h - = (struct ncp_request_header *) (server->packet); - int result; - - h->type = NCP_ALLOC_SLOT_REQUEST; - - server->sequence = 0; - h->sequence = server->sequence; - h->conn_low = 0xff; - h->conn_high = 0xff; - h->task = (current->pid) & 0xff; - h->function = 0; - - if ((result = ncp_do_request(server, sizeof(*h))) < 0) - { - return result; - } - server->sequence = 0; - server->connection = h->conn_low + (h->conn_high * 256); - return 0; -} - -int -ncp_disconnect(struct ncp_server *server) -{ - struct ncp_request_header *h - = (struct ncp_request_header *) (server->packet); - - h->type = NCP_DEALLOC_SLOT_REQUEST; - - server->sequence += 1; - h->sequence = server->sequence; - h->conn_low = (server->connection) & 0xff; - h->conn_high = ((server->connection) & 0xff00) >> 8; - h->task = (current->pid) & 0xff; - h->function = 0; - - return ncp_do_request(server, sizeof(*h)); -} - -void -ncp_lock_server(struct ncp_server *server) -{ -#if 0 - /* For testing, only 1 process */ - if (server->lock != 0) - { - DPRINTK("ncpfs: server locked!!!\n"); - } -#endif - while (server->lock) - sleep_on(&server->wait); - server->lock = 1; -} - -void -ncp_unlock_server(struct ncp_server *server) -{ - if (server->lock != 1) - { - printk("ncp_unlock_server: was not locked!\n"); - } - server->lock = 0; - wake_up(&server->wait); -} diff --git a/lib/Makefile b/lib/Makefile index 5e73e9f..bf4738e 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -19,6 +19,15 @@ endif CFLAGS += $(PIC_FLAG) +ifdef NDS_SUPPORT +CFLAGS += -DNDS_SUPPORT +NDS_OBJ = ndslib.o mpilib.o ndscrypt.o +endif +ifdef SIGNATURES +CFLAGS += -DSIGNATURES +SIGN_OBJ = ncpsign.o +endif + default: make -C .. @@ -27,8 +36,17 @@ all: libcom_err.a ncplib_err.o $(NCP_LIB) install: $(INSTALL_LIB) +mpilib.o: mpilib.c + $(CC) $(CFLAGS) -DPORTABLE -DSMITH -DUNIT32 -DMUNIT16 -c mpilib.c +ndscrypt.o: ndscrypt.c + $(CC) $(CFLAGS) -c ndscrypt.c +ndslib.o: ndslib.c + $(CC) $(CFLAGS) -DPORTABLE -DSMITH -DUNIT32 -DMUNIT16 -c ndslib.c + +ncpsign.o: ncpsign.c + $(CC) $(CFLAGS) -c ncpsign.c ncplib.o: ncplib.c ncplib_err.h - $(CC) $(CFLAGS) -c ncplib.c + $(CC) $(CFLAGS) -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 @@ -38,8 +56,9 @@ COM_ERR_OFILES = com_err/com_err.o com_err/error_message.o com_err/et_name.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) +$(NCP_LIB): ncplib.o ncplib_err.o libcom_err.a $(SIGN_OBJ) $(NDS_OBJ) + $(LIB_LINK_COMMAND) ncplib.o ncplib_err.o $(SIGN_OBJ) \ + $(COM_ERR_OFILES) $(NDS_OBJ) ln -sf libncp.so.1.0 libncp.so.1 ln -sf libncp.so.1 libncp.so export LD_LIBRARY_PATH=`pwd`:LD_LIBRARY_PATH diff --git a/lib/mpilib.c b/lib/mpilib.c new file mode 100644 index 0000000..cdf5ff2 --- /dev/null +++ b/lib/mpilib.c @@ -0,0 +1,1881 @@ +/* C source code for multiprecision arithmetic library routines. + Implemented Nov 86 by Philip Zimmermann + Last revised 27 Nov 91 by PRZ + + Boulder Software Engineering + 3021 Eleventh Street + Boulder, CO 80304 + (303) 541-0140 + + (c) Copyright 1986-92 by Philip Zimmermann. All rights reserved. + The author assumes no liability for damages resulting from the use + of this software, even if the damage results from defects in this + software. No warranty is expressed or implied. The use of this + cryptographic software for developing weapon systems is expressly + forbidden. + + These routines implement all of the multiprecision arithmetic + necessary for number-theoretic cryptographic algorithms such as + ElGamal, Diffie-Hellman, Rabin, or factoring studies for large + composite numbers, as well as Rivest-Shamir-Adleman (RSA) public + key cryptography. + + Although originally developed in Microsoft C for the IBM PC, this code + contains few machine dependencies. It assumes 2's complement + arithmetic. It can be adapted to 8-bit, 16-bit, or 32-bit machines, + lowbyte-highbyte order or highbyte-lowbyte order. This version + has been converted to ANSI C. + + + The internal representation for these extended precision integer + "registers" is an array of "units". A unit is a machine word, which + is either an 8-bit byte, a 16-bit unsigned integer, or a 32-bit + unsigned integer, depending on the machine's word size. For example, + an IBM PC or AT uses a unit size of 16 bits. To perform arithmetic + on these huge precision integers, we pass pointers to these unit + arrays to various subroutines. A pointer to an array of units is of + type unitptr. This is a pointer to a huge integer "register". + + When calling a subroutine, we always pass a pointer to the BEGINNING + of the array of units, regardless of the byte order of the machine. + On a lowbyte-first machine, such as the Intel 80x86, this unitptr + points to the LEAST significant unit, and the array of units increases + significance to the right. On a highbyte-first machine, such as the + Motorola 680x0, this unitptr points to the MOST significant unit, and + the array of units decreases significance to the right. + + Modified 8 Apr 92 - HAJK + Implement new VAX/VMS primitive support. + + Modified 30 Sep 92 -Castor Fu + Upgraded PORTABLE support to allow sizeof(unit) == sizeof(long) + + Modified 28 Nov 92 - Thad Smith + Added Smith modmult, generalized non-portable support. +*/ + +/* #define COUNTMULTS */ /* count modmults for performance studies */ + +#ifdef DEBUG +#ifdef MSDOS +#ifdef __GO32__ /* DJGPP */ +#include +#else +#include +#endif /* __GO32__ */ +#define poll_for_break() {while (kbhit()) getch();} +#endif /* MSDOS */ +#endif /* DEBUG */ + +#ifndef poll_for_break +#define poll_for_break() /* stub */ +#endif + +#include "mpilib.h" + +#ifdef mp_smula +#ifdef mp_smul + Error: Both mp_smula and mp_smul cannot be defined. +#else +#define mp_smul mp_smula +#endif +#endif + +/* set macros for MULTUNIT */ +#ifdef MUNIT8 +#define MULTUNITSIZE 8 +typedef unsigned char MULTUNIT; +#ifdef UNIT8 +#define MULTUNIT_SIZE_SAME +#endif +#else /* not MUNIT8 */ +#ifdef MUNIT32 +#define MULTUNITSIZE 32 +typedef unsigned long MULTUNIT; +#ifdef UNIT32 +#define MULTUNIT_SIZE_SAME +#else +/* #error is not portable, this has the same effect */ +#include "UNITSIZE cannot be smaller than MULTUNITSIZE" +#endif +#else /* assume MUNIT16 */ +#define MULTUNITSIZE 16 +typedef unsigned short MULTUNIT; +#ifdef UNIT16 +#define MULTUNIT_SIZE_SAME +#endif /* UNIT16 */ +#ifdef UNIT8 +#include "UNITSIZE cannot be smaller than MULTUNITSIZE" +#endif /* UNIT8 */ +#endif /* MUNIT32 */ +#endif /* MUNIT8 */ + +#define MULTUNIT_msb ((MULTUNIT)1 << (MULTUNITSIZE-1)) /* msb of MULTUNIT */ +#define DMULTUNIT_msb (1L << (2*MULTUNITSIZE-1)) +#define MULTUNIT_mask ((MULTUNIT)((1L << MULTUNITSIZE)-1)) +#define MULTUNITs_perunit (UNITSIZE/MULTUNITSIZE) + + +void mp_smul (MULTUNIT *prod, MULTUNIT *multiplicand, MULTUNIT multiplier); +void mp_dmul (unitptr prod, unitptr multiplicand, unitptr multiplier); + +short global_precision = 0; /* units of precision for all routines */ +/* global_precision is the unit precision last set by set_precision. + Initially, set_precision() should be called to define global_precision + before using any of these other multiprecision library routines. + i.e.: set_precision(MAX_UNIT_PRECISION); +*/ + +/*************** multiprecision library primitives ****************/ +/* The following portable C primitives should be recoded in assembly. + The entry point name should be defined, in "mpilib.h" to the external + entry point name. If undefined, the C version will be used. +*/ + +typedef unsigned long int ulint; + +#ifndef mp_addc +boolean mp_addc + (register unitptr r1,register unitptr r2,register boolean carry) + /* multiprecision add with carry r2 to r1, result in r1 */ + /* carry is incoming carry flag-- value should be 0 or 1 */ +{ register unit x; + short precision; /* number of units to add */ + precision = global_precision; + make_lsbptr(r1,precision); + make_lsbptr(r2,precision); + while (precision--) + { + if (carry) + { x = *r1 + *r2 + 1; + carry = (*r2 >= (unit)(~ *r1)); + } else + { x = *r1 + *r2; + carry = (x < *r1) ; + } + post_higherunit(r2); + *post_higherunit(r1) = x; + } + return(carry); /* return the final carry flag bit */ +} /* mp_addc */ +#endif /* mp_addc */ + +#ifndef mp_subb +boolean mp_subb + (register unitptr r1,register unitptr r2,register boolean borrow) + /* multiprecision subtract with borrow, r2 from r1, result in r1 */ + /* borrow is incoming borrow flag-- value should be 0 or 1 */ +{ register unit x; + short precision; /* number of units to subtract */ + precision = global_precision; + make_lsbptr(r1,precision); + make_lsbptr(r2,precision); + while (precision--) + { if (borrow) + { x = *r1 - *r2 - 1; + borrow = (*r1 <= *r2); + } else + { x = *r1 - *r2; + borrow = (*r1 < *r2); + } + post_higherunit(r2); + *post_higherunit(r1) = x; + } + return(borrow); /* return the final carry/borrow flag bit */ +} /* mp_subb */ +#endif /* mp_subb */ + +#ifndef mp_rotate_left +boolean mp_rotate_left(register unitptr r1,register boolean carry) + /* multiprecision rotate left 1 bit with carry, result in r1. */ + /* carry is incoming carry flag-- value should be 0 or 1 */ +{ register int precision; /* number of units to rotate */ + unsigned int mcarry = carry, nextcarry; /* int is supposed to be + * the efficient size for ops*/ + precision = global_precision; + make_lsbptr(r1,precision); + while (precision--) + { + nextcarry = (((signedunit) *r1) < 0); + *r1 = (*r1 << 1) | mcarry; + mcarry = nextcarry; + pre_higherunit(r1); + } + return(nextcarry); /* return the final carry flag bit */ +} /* mp_rotate_left */ +#endif /* mp_rotate_left */ + +/************** end of primitives that should be in assembly *************/ + + +/* The mp_shift_right_bits function is not called in any time-critical + situations in public-key cryptographic functions, so it doesn't + need to be coded in assembly language. +*/ +void mp_shift_right_bits(register unitptr r1,register short bits) + /* multiprecision shift right bits, result in r1. + bits is how many bits to shift, must be <= UNITSIZE. + */ +{ register short precision; /* number of units to shift */ + register unit carry,nextcarry,bitmask; + register short unbits; + if (bits==0) return; /* shift zero bits is a no-op */ + carry = 0; + bitmask = power_of_2(bits)-1; + unbits = UNITSIZE-bits; /* shift bits must be <= UNITSIZE */ + precision = global_precision; + make_msbptr(r1,precision); + if (bits == UNITSIZE) { + while (precision--) { + nextcarry = *r1; + *r1 = carry; + carry = nextcarry; + pre_lowerunit(r1); + } + } else { + while (precision--) + { nextcarry = *r1 & bitmask; + *r1 >>= bits; + *r1 |= carry << unbits; + carry = nextcarry; + pre_lowerunit(r1); + } + } +} /* mp_shift_right_bits */ + + +#ifndef mp_compare +short mp_compare(register unitptr r1,register unitptr r2) +/* Compares multiprecision integers *r1, *r2, and returns: + -1 iff *r1 < *r2 + 0 iff *r1 == *r2 + +1 iff *r1 > *r2 +*/ +{ register short precision; /* number of units to compare */ + + precision = global_precision; + make_msbptr(r1,precision); + make_msbptr(r2,precision); + do + { if (*r1 < *r2) + return(-1); + if (*post_lowerunit(r1) > *post_lowerunit(r2)) + return(1); + } while (--precision); + return(0); /* *r1 == *r2 */ +} /* mp_compare */ +#endif /* mp_compare */ + + +boolean mp_inc(register unitptr r) + /* Increment multiprecision integer r. */ +{ register short precision; + precision = global_precision; + make_lsbptr(r,precision); + do + { if ( ++(*r) ) return(0); /* no carry */ + post_higherunit(r); + } while (--precision); + return(1); /* return carry set */ +} /* mp_inc */ + + +boolean mp_dec(register unitptr r) + /* Decrement multiprecision integer r. */ +{ register short precision; + precision = global_precision; + make_lsbptr(r,precision); + do + { if ( (signedunit) (--(*r)) != (signedunit) -1 ) + return(0); /* no borrow */ + post_higherunit(r); + } while (--precision); + return(1); /* return borrow set */ +} /* mp_dec */ + + +void mp_neg(register unitptr r) + /* Compute 2's complement, the arithmetic negative, of r */ +{ register short precision; /* number of units to negate */ + precision = global_precision; + mp_dec(r); /* 2's complement is 1's complement plus 1 */ + do /* now do 1's complement */ + { *r = ~(*r); + r++; + } while (--precision); +} /* mp_neg */ + +#ifndef mp_move +void mp_move(register unitptr dst,register unitptr src) +{ register short precision; /* number of units to move */ + precision = global_precision; + do { *dst++ = *src++; } while (--precision); +} /* mp_move */ +#endif /* mp_move */ + +void mp_init(register unitptr r, word16 value) + /* Init multiprecision register r with short value. */ +{ /* Note that mp_init doesn't extend sign bit for >32767 */ + + unitfill0( r, global_precision); + make_lsbptr(r,global_precision); + *post_higherunit(r) = value; +#ifdef UNIT8 + *post_higherunit(r) = value >> UNITSIZE; +#endif +} /* mp_init */ + + +short significance(register unitptr r) + /* Returns number of significant units in r */ +{ register short precision; + precision = global_precision; + make_msbptr(r,precision); + do + { if (*post_lowerunit(r)) + return(precision); + } while (--precision); + return(precision); +} /* significance */ + + +#ifndef unitfill0 +void unitfill0(unitptr r,word16 unitcount) + /* Zero-fill the unit buffer r. */ +{ while (unitcount--) *r++ = 0; +} /* unitfill0 */ +#endif /* unitfill0 */ + + +int mp_udiv(register unitptr remainder,register unitptr quotient, + register unitptr dividend,register unitptr divisor) + /* Unsigned divide, treats both operands as positive. */ +{ int bits; + short dprec; + register unit bitmask; + if (testeq(divisor,0)) + return(-1); /* zero divisor means divide error */ + mp_init0(remainder); + mp_init0(quotient); + /* normalize and compute number of bits in dividend first */ + init_bitsniffer(dividend,bitmask,dprec,bits); + /* rescale quotient to same precision (dprec) as dividend */ + rescale(quotient,global_precision,dprec); + make_msbptr(quotient,dprec); + + while (bits--) + { mp_rotate_left(remainder,(boolean)(sniff_bit(dividend,bitmask)!=0)); + if (mp_compare(remainder,divisor) >= 0) + { mp_sub(remainder,divisor); + stuff_bit(quotient,bitmask); + } + bump_2bitsniffers(dividend,quotient,bitmask); + } + return(0); +} /* mp_udiv */ + + +#ifdef UPTON_OR_SMITH +#define RECIPMARGIN 0 /* extra margin bits used by mp_recip() */ + +int mp_recip(register unitptr quotient,register unitptr divisor) + /* Compute reciprocal (quotient) as 1/divisor. Used by faster modmult. */ +{ int bits; + short qprec; + register unit bitmask; + unit remainder[MAX_UNIT_PRECISION]; + if (testeq(divisor,0)) + return(-1); /* zero divisor means divide error */ + mp_init0(remainder); + mp_init0(quotient); + + /* normalize and compute number of bits in quotient first */ + bits = countbits(divisor) + RECIPMARGIN; + bitmask = bitmsk(bits); /* bitmask within a single unit */ + qprec = bits2units(bits+1); + mp_setbit(remainder,(bits-RECIPMARGIN)-1); + /* rescale quotient to precision of divisor + RECIPMARGIN bits */ + rescale(quotient,global_precision,qprec); + make_msbptr(quotient,qprec); + + while (bits--) + { mp_shift_left(remainder); + if (mp_compare(remainder,divisor) >= 0) + { mp_sub(remainder,divisor); + stuff_bit(quotient,bitmask); + } + bump_bitsniffer(quotient,bitmask); + } + mp_init0(remainder); /* burn sensitive data left on stack */ + return(0); +} /* mp_recip */ +#endif /* UPTON_OR_SMITH */ + + +int mp_div(register unitptr remainder,register unitptr quotient, + register unitptr dividend,register unitptr divisor) + /* Signed divide, either or both operands may be negative. */ +{ boolean dvdsign,dsign; + int status; + dvdsign = (boolean)(mp_tstminus(dividend)!=0); + dsign = (boolean)(mp_tstminus(divisor)!=0); + if (dvdsign) mp_neg(dividend); + if (dsign) mp_neg(divisor); + status = mp_udiv(remainder,quotient,dividend,divisor); + if (dvdsign) mp_neg(dividend); /* repair caller's dividend */ + if (dsign) mp_neg(divisor); /* repair caller's divisor */ + if (status<0) return(status); /* divide error? */ + if (dvdsign) mp_neg(remainder); + if (dvdsign ^ dsign) mp_neg(quotient); + return(status); +} /* mp_div */ + + +word16 mp_shortdiv(register unitptr quotient, + register unitptr dividend,register word16 divisor) +/* This function does a fast divide and mod on a multiprecision dividend + using a short integer divisor returning a short integer remainder. + This is an unsigned divide. It treats both operands as positive. + It is used mainly for faster printing of large numbers in base 10. +*/ +{ int bits; + short dprec; + register unit bitmask; + register word16 remainder; + if (!divisor) /* if divisor == 0 */ + return(-1); /* zero divisor means divide error */ + remainder=0; + mp_init0(quotient); + /* normalize and compute number of bits in dividend first */ + init_bitsniffer(dividend,bitmask,dprec,bits); + /* rescale quotient to same precision (dprec) as dividend */ + rescale(quotient,global_precision,dprec); + make_msbptr(quotient,dprec); + + while (bits--) + { remainder <<= 1; + if (sniff_bit(dividend,bitmask)) + remainder++; + if (remainder >= divisor) + { remainder -= divisor; + stuff_bit(quotient,bitmask); + } + bump_2bitsniffers(dividend,quotient,bitmask); + } + return(remainder); +} /* mp_shortdiv */ + + +int mp_mod(register unitptr remainder, + register unitptr dividend,register unitptr divisor) + /* Unsigned divide, treats both operands as positive. */ +{ int bits; + short dprec; + register unit bitmask; + if (testeq(divisor,0)) + return(-1); /* zero divisor means divide error */ + mp_init0(remainder); + /* normalize and compute number of bits in dividend first */ + init_bitsniffer(dividend,bitmask,dprec,bits); + + while (bits--) + { mp_rotate_left(remainder,(boolean)(sniff_bit(dividend,bitmask)!=0)); + msub(remainder,divisor); + bump_bitsniffer(dividend,bitmask); + } + return(0); +} /* mp_mod */ + + +word16 mp_shortmod(register unitptr dividend,register word16 divisor) +/* This function does a fast mod operation on a multiprecision dividend + using a short integer modulus returning a short integer remainder. + This is an unsigned divide. It treats both operands as positive. + It is used mainly for fast sieve searches for large primes. +*/ +{ int bits; + short dprec; + register unit bitmask; + register word16 remainder; + if (!divisor) /* if divisor == 0 */ + return(-1); /* zero divisor means divide error */ + remainder=0; + /* normalize and compute number of bits in dividend first */ + init_bitsniffer(dividend,bitmask,dprec,bits); + + while (bits--) + { remainder <<= 1; + if (sniff_bit(dividend,bitmask)) + remainder++; + if (remainder >= divisor) remainder -= divisor; + bump_bitsniffer(dividend,bitmask); + } + return(remainder); +} /* mp_shortmod */ + + + +#ifdef COMB_MULT /* use faster "comb" multiply algorithm */ + /* We are skipping this code because it has a bug... */ + +int mp_mult(register unitptr prod, + register unitptr multiplicand, register unitptr multiplier) + /* Computes multiprecision prod = multiplicand * multiplier */ +{ /* Uses interleaved comb multiply algorithm. + This improved multiply more than twice as fast as a Russian + peasant multiply, because it does a lot fewer shifts. + Must have global_precision set to the size of the multiplicand + plus UNITSIZE-1 SLOP_BITS. Produces a product that is the sum + of the lengths of the multiplier and multiplicand. + + BUG ALERT: Unfortunately, this code has a bug. It fails for + some numbers. One such example: + x= 59DE 60CE 2345 8091 A02B 2A1C DBC3 8BE5 + x*x= 59DE 60CE 2345 26B3 993B 67A5 2499 0B7D + 52C8 CDC7 AFB3 61C8 243C 741B + --which is obviously wrong. The answer should be: + x*x= 1F8C 607B 5EA6 C061 2714 04A9 A0C6 A17A + C9AB 6095 C62F 3756 3843 E4D0 3950 7AD9 + We'll have to fix this some day. In the meantime, we'll + just have the compiler skip over this code. + + BUG NOTE: Possibly fixed. Needs testing. + */ + int bits; + register unit bitmask; + unitptr product, mplier, temp; + short mprec,mprec2; + unit mplicand[MAX_UNIT_PRECISION]; + + /* better clear full width--double precision */ + mp_init(prod+tohigher(global_precision),0); + + if (testeq(multiplicand,0)) + return(0); /* zero multiplicand means zero product */ + + mp_move(mplicand,multiplicand); /* save it from damage */ + + normalize(multiplier,mprec); + if (!mprec) + return(0); + + make_lsbptr(multiplier,mprec); + bitmask = 1; /* start scan at LSB of multiplier */ + + do /* UNITSIZE times */ + { /* do for bits 0-15 */ + product = prod; + mplier = multiplier; + mprec2 = mprec; + while (mprec2--) /* do for each word in multiplier */ + { + if (sniff_bit(mplier,bitmask)) + { if (mp_addc(product,multiplicand,0)) /* ripple carry */ + { /* After 1st time thru, this is rarely encountered. */ + temp = msbptr(product,global_precision); + pre_higherunit(temp); + /* temp now points to LSU of carry region. */ + unmake_lsbptr(temp,global_precision); + mp_inc(temp); + } /* ripple carry */ + } + pre_higherunit(mplier); + pre_higherunit(product); + } + if (!(bitmask <<= 1)) + break; + mp_shift_left(multiplicand); + + } while (TRUE); + + mp_move(multiplicand,mplicand); /* recover */ + + return(0); /* normal return */ +} /* mp_mult */ + +#endif /* COMB_MULT */ + + +/* Because the "comb" multiply has a bug, we will use the slower + Russian peasant multiply instead. Fortunately, the mp_mult + function is not called from any time-critical code. +*/ + +int mp_mult(register unitptr prod, + register unitptr multiplicand,register unitptr multiplier) + /* Computes multiprecision prod = multiplicand * multiplier */ +{ /* Uses "Russian peasant" multiply algorithm. */ + int bits; + register unit bitmask; + short mprec; + mp_init(prod,0); + if (testeq(multiplicand,0)) + return(0); /* zero multiplicand means zero product */ + /* normalize and compute number of bits in multiplier first */ + init_bitsniffer(multiplier,bitmask,mprec,bits); + + while (bits--) + { mp_shift_left(prod); + if (sniff_bit(multiplier,bitmask)) + mp_add(prod,multiplicand); + bump_bitsniffer(multiplier,bitmask); + } + return(0); +} /* mp_mult */ + + + +/* mp_modmult computes a multiprecision multiply combined with a + modulo operation. This is the most time-critical function in + this multiprecision arithmetic library for performing modulo + exponentiation. We experimented with different versions of modmult, + depending on the machine architecture and performance requirements. + We will either use a Russian Peasant modmult (peasant_modmult), + Charlie Merritt's modmult (merritt_modmult), Jimmy Upton's + modmult (upton_modmult), or Thad Smith's modmult (smith_modmult). + On machines with a hardware atomic multiply instruction, + Smith's modmult is fastest. It can utilize assembly subroutines to + speed up the hardware multiply logic and trial quotient calculation. + If the machine lacks a fast hardware multiply, Merritt's modmult + is preferred, which doesn't call any assembly multiply routine. + We use the alias names mp_modmult, stage_modulus, and modmult_burn + for the corresponding true names, which depend on what flavor of + modmult we are using. + + Before making the first call to mp_modmult, you must set up the + modulus-dependant precomputated tables by calling stage_modulus. + After making all the calls to mp_modmult, you call modmult_burn to + erase the tables created by stage_modulus that were left in memory. +*/ + +#ifdef COUNTMULTS +/* "number of modmults" counters, used for performance studies. */ +static unsigned int tally_modmults = 0; +static unsigned int tally_modsquares = 0; +#endif /* COUNTMULTS */ + + +#ifdef PEASANT +/* Conventional Russian peasant multiply with modulo algorithm. */ + +static unitptr pmodulus = 0; /* used only by mp_modmult */ + +int stage_peasant_modulus(unitptr n) +/* Must pass modulus to stage_modulus before calling modmult. + Assumes that global_precision has already been adjusted to the + size of the modulus, plus SLOP_BITS. +*/ +{ /* For this simple version of modmult, just copy unit pointer. */ + pmodulus = n; + return(0); /* normal return */ +} /* stage_peasant_modulus */ + + +int peasant_modmult(register unitptr prod, + unitptr multiplicand,register unitptr multiplier) +{ /* "Russian peasant" multiply algorithm, combined with a modulo + operation. This is a simple naive replacement for Merritt's + faster modmult algorithm. References global unitptr "modulus". + Computes: prod = (multiplicand*multiplier) mod modulus + WARNING: All the arguments must be less than the modulus! + */ + int bits; + register unit bitmask; + short mprec; + mp_init(prod,0); +/* if (testeq(multiplicand,0)) + return(0); */ /* zero multiplicand means zero product */ + /* normalize and compute number of bits in multiplier first */ + init_bitsniffer(multiplier,bitmask,mprec,bits); + + while (bits--) + { mp_shift_left(prod); + msub(prod,pmodulus); /* turns mult into modmult */ + if (sniff_bit(multiplier,bitmask)) + { mp_add(prod,multiplicand); + msub(prod,pmodulus); /* turns mult into modmult */ + } + bump_bitsniffer(multiplier,bitmask); + } + return(0); +} /* peasant_modmult */ + + +/* If we are using a version of mp_modmult that uses internal tables + in memory, we have to call modmult_burn() at the end of mp_modexp. + This is so that no sensitive data is left in memory after the program + exits. The Russian peasant method doesn't use any such tables. +*/ +void peasant_burn(void) +/* Alias for modmult_burn, called only from mp_modexp(). Destroys + internal modmult tables. This version does nothing because no + tables are used by the Russian peasant modmult. */ +{ } /* peasant_burn */ + +#endif /* PEASANT */ + + +#ifdef MERRITT +/*=========================================================================*/ +/* + This is Charlie Merritt's MODMULT algorithm, implemented in C by PRZ. + Also refined by Zhahai Stewart to reduce the number of subtracts. + Modified by Raymond Brand to reduce the number of SLOP_BITS by 1. + It performs a multiply combined with a modulo operation, without + going into "double precision". It is faster than the Russian peasant + method, and still works well on machines that lack a fast hardware + multiply instruction. +*/ + +/* The following support functions, macros, and data structures + are used only by Merritt's modmult algorithm... */ + +static void mp_lshift_unit(register unitptr r1) +/* Shift r1 1 whole unit to the left. Used only by modmult function. */ +{ register short precision; + register unitptr r2; + precision = global_precision; + make_msbptr(r1,precision); + r2 = r1; + while (--precision) + *post_lowerunit(r1) = *pre_lowerunit(r2); + *r1 = 0; +} /* mp_lshift_unit */ + + +/* moduli_buf contains shifted images of the modulus, set by stage_modulus */ +static unit moduli_buf[UNITSIZE-1][MAX_UNIT_PRECISION] = {0}; +static unitptr moduli[UNITSIZE] = /* contains pointers into moduli_buf */ +{ 0 + ,&moduli_buf[ 0][0], &moduli_buf[ 1][0], &moduli_buf[ 2][0], &moduli_buf[ 3][0], + &moduli_buf[ 4][0], &moduli_buf[ 5][0], &moduli_buf[ 6][0] +#ifndef UNIT8 + ,&moduli_buf[ 7][0] + ,&moduli_buf[ 8][0], &moduli_buf[ 9][0], &moduli_buf[10][0], &moduli_buf[11][0], + &moduli_buf[12][0], &moduli_buf[13][0], &moduli_buf[14][0] +#ifndef UNIT16 /* and not UNIT8 */ + ,&moduli_buf[15][0] + ,&moduli_buf[16][0], &moduli_buf[17][0], &moduli_buf[18][0], &moduli_buf[19][0], + &moduli_buf[20][0], &moduli_buf[21][0], &moduli_buf[22][0], &moduli_buf[23][0], + &moduli_buf[24][0], &moduli_buf[25][0], &moduli_buf[26][0], &moduli_buf[27][0], + &moduli_buf[28][0], &moduli_buf[29][0], &moduli_buf[30][0] +#endif /* UNIT16 and UNIT8 not defined */ +#endif /* UNIT8 not defined */ +}; + +/* To optimize msubs, need following 2 unit arrays, each filled + with the most significant unit and next-to-most significant unit + of the preshifted images of the modulus. */ +static unit msu_moduli[UNITSIZE] = {0}; /* most signif. unit */ +static unit nmsu_moduli[UNITSIZE] = {0}; /* next-most signif. unit */ + +/* mpdbuf contains preshifted images of the multiplicand, mod n. + It is used only by mp_modmult. It could be staticly declared + inside of mp_modmult, but we put it outside mp_modmult so that + it can be wiped clean by modmult_burn(), which is called at the + end of mp_modexp. This is so that no sensitive data is left in + memory after the program exits. +*/ +static unit mpdbuf[UNITSIZE-1][MAX_UNIT_PRECISION] = {0}; + + +static void stage_mp_images(unitptr images[UNITSIZE],unitptr r) +/* Computes UNITSIZE images of r, each shifted left 1 more bit. + Used only by modmult function. +*/ +{ short int i; + images[0] = r; /* no need to move the first image, just copy ptr */ + for (i=1; i= 0) && \ + (p_m || (mp_compare(prod,moduli[i]) >= 0) ) \ + ) mp_sub(prod,moduli[i]) +*/ + +/* Fully-optimized msubs macro (msubs2) follows... */ +#define msubs(i) if (((p_m = *msu_prod-msu_moduli[i])>0) || ( \ + (p_m==0) && ( (*nmsu_prod>nmsu_moduli[i]) || ( \ + (*nmsu_prod==nmsu_moduli[i]) && ((mp_compare(prod,moduli[i]) >= 0)) ))) ) \ + mp_sub(prod,moduli[i]) + + +int merritt_modmult(register unitptr prod, + unitptr multiplicand,register unitptr multiplier) + /* Performs combined multiply/modulo operation. + Computes: prod = (multiplicand*multiplier) mod modulus + WARNING: All the arguments must be less than the modulus! + Assumes the modulus has been predefined by first calling + stage_modulus. + */ +{ + /* p_m, msu_prod, and nmsu_prod are used by the optimized msubs macro...*/ + register signedunit p_m; + register unitptr msu_prod; /* ptr to most significant unit of product */ + register unitptr nmsu_prod; /* next-most signif. unit of product */ + short mprec; /* precision of multiplier, in units */ + /* Array mpd contains a list of pointers to preshifted images of + the multiplicand: */ + static unitptr mpd[UNITSIZE] = + { 0, &mpdbuf[ 0][0], &mpdbuf[ 1][0], &mpdbuf[ 2][0], + &mpdbuf[ 3][0], &mpdbuf[ 4][0], &mpdbuf[ 5][0], &mpdbuf[ 6][0] +#ifndef UNIT8 + ,&mpdbuf[ 7][0], &mpdbuf[ 8][0], &mpdbuf[ 9][0], &mpdbuf[10][0], + &mpdbuf[11][0], &mpdbuf[12][0], &mpdbuf[13][0], &mpdbuf[14][0] +#ifndef UNIT16 /* and not UNIT8 */ + ,&mpdbuf[15][0], &mpdbuf[16][0], &mpdbuf[17][0], &mpdbuf[18][0], + &mpdbuf[19][0], &mpdbuf[20][0], &mpdbuf[21][0], &mpdbuf[22][0], + &mpdbuf[23][0], &mpdbuf[24][0], &mpdbuf[25][0], &mpdbuf[26][0], + &mpdbuf[27][0], &mpdbuf[28][0], &mpdbuf[29][0], &mpdbuf[30][0] +#endif /* UNIT16 and UNIT8 not defined */ +#endif /* UNIT8 not defined */ + }; + + /* Compute preshifted images of multiplicand, mod n: */ + stage_mp_images(mpd,multiplicand); + + /* To optimize msubs, set up msu_prod and nmsu_prod: */ + msu_prod = msbptr(prod,global_precision); /* Get ptr to MSU of prod */ + nmsu_prod = msu_prod; + post_lowerunit(nmsu_prod); /* Get next-MSU of prod */ + + /* To understand this algorithm, it would be helpful to first + study the conventional Russian peasant modmult algorithm. + This one does about the same thing as Russian peasant, but + is organized differently to save some steps. It loops + through the multiplier a word (unit) at a time, instead of + a bit at a time. It word-shifts the product instead of + bit-shifting it, so it should be faster. It also does about + half as many subtracts as Russian peasant. + */ + + mp_init(prod,0); /* Initialize product to 0. */ + + /* The way mp_modmult is actually used in cryptographic + applications, there will NEVER be a zero multiplier or + multiplicand. So there is no need to optimize for that + condition. + */ +/* if (testeq(multiplicand,0)) + return(0); */ /* zero multiplicand means zero product */ + /* Normalize and compute number of units in multiplier first: */ + normalize(multiplier,mprec); + if (mprec==0) /* if precision of multiplier is 0 */ + return(0); /* zero multiplier means zero product */ + make_msbptr(multiplier,mprec); /* start at MSU of multiplier */ + + while (mprec--) /* Loop for the number of units in the multiplier */ + { + /* Shift the product one whole unit to the left. + This is part of the multiply phase of modmult. + */ + + mp_lshift_unit(prod); + + /* The product may have grown by as many as UNITSIZE + bits. That's why we have global_precision set to the + size of the modulus plus UNITSIZE slop bits. + Now reduce the product back down by conditionally + subtracting the preshifted images of the modulus. + This is part of the modulo reduction phase of modmult. + The following loop is unrolled for speed, using macros... + + for (i=UNITSIZE-1; i>=LOG_UNITSIZE; i--) + if (mp_compare(prod,moduli[i]) >= 0) + mp_sub(prod,moduli[i]); + */ + +#ifndef UNIT8 +#ifndef UNIT16 /* and not UNIT8 */ + msubs(31); + msubs(30); + msubs(29); + msubs(28); + msubs(27); + msubs(26); + msubs(25); + msubs(24); + msubs(23); + msubs(22); + msubs(21); + msubs(20); + msubs(19); + msubs(18); + msubs(17); + msubs(16); +#endif /* not UNIT16 and not UNIT8 */ + msubs(15); + msubs(14); + msubs(13); + msubs(12); + msubs(11); + msubs(10); + msubs(9); + msubs(8); +#endif /* not UNIT8 */ + msubs(7); + msubs(6); + msubs(5); +#ifndef UNIT32 + msubs(4); +#ifndef UNIT16 + msubs(3); +#endif +#endif + + /* Sniff each bit in the current unit of the multiplier, + and conditionally add the corresponding preshifted + image of the multiplicand to the product. + This is also part of the multiply phase of modmult. + + The following loop is unrolled for speed, using macros... + + for (i=UNITSIZE-1; i>=0; i--) + if (*multiplier & power_of_2(i)) + mp_add(prod,mpd[i]); + */ +#ifndef UNIT8 +#ifndef UNIT16 /* and not UNIT8 */ + sniffadd(31); + sniffadd(30); + sniffadd(29); + sniffadd(28); + sniffadd(27); + sniffadd(26); + sniffadd(25); + sniffadd(24); + sniffadd(23); + sniffadd(22); + sniffadd(21); + sniffadd(20); + sniffadd(19); + sniffadd(18); + sniffadd(17); + sniffadd(16); +#endif /* not UNIT16 and not UNIT8 */ + sniffadd(15); + sniffadd(14); + sniffadd(13); + sniffadd(12); + sniffadd(11); + sniffadd(10); + sniffadd(9); + sniffadd(8); +#endif /* not UNIT8 */ + sniffadd(7); + sniffadd(6); + sniffadd(5); + sniffadd(4); + sniffadd(3); + sniffadd(2); + sniffadd(1); + sniffadd(0); + + /* The product may have grown by as many as LOG_UNITSIZE+1 + bits. + Now reduce the product back down by conditionally + subtracting LOG_UNITSIZE+1 preshifted images of the + modulus. This is the modulo reduction phase of modmult. + + The following loop is unrolled for speed, using macros... + + for (i=LOG_UNITSIZE; i>=0; i--) + if (mp_compare(prod,moduli[i]) >= 0) + mp_sub(prod,moduli[i]); + */ + +#ifndef UNIT8 +#ifndef UNIT16 + msubs(5); +#endif + msubs(4); +#endif + msubs(3); + msubs(2); + msubs(1); + msubs(0); + + /* Bump pointer to next lower unit of multiplier: */ + post_lowerunit(multiplier); + + } /* Loop for the number of units in the multiplier */ + + return(0); /* normal return */ + +} /* merritt_modmult */ + + +#undef msubs +#undef sniffadd + + +/* Merritt's mp_modmult function leaves some internal tables in memory, + so we have to call modmult_burn() at the end of mp_modexp. + This is so that no cryptographically sensitive data is left in memory + after the program exits. +*/ +void merritt_burn(void) +/* Alias for modmult_burn, merritt_burn() is called only by mp_modexp. */ +{ unitfill0(&(mpdbuf[0][0]),(UNITSIZE-1)*MAX_UNIT_PRECISION); + unitfill0(&(moduli_buf[0][0]),(UNITSIZE-1)*MAX_UNIT_PRECISION); + unitfill0(msu_moduli,UNITSIZE); + unitfill0(nmsu_moduli,UNITSIZE); +} /* merritt_burn() */ + +/******* end of Merritt's MODMULT stuff. *******/ +/*=========================================================================*/ +#endif /* MERRITT */ + + +#ifdef UPTON_OR_SMITH /* Used by Upton's and Smith's modmult algorithms */ + +/* Jimmy Upton's multiprecision modmult algorithm in C. + Performs a multiply combined with a modulo operation. + + The following support functions and data structures + are used only by Upton's modmult algorithm... +*/ + +short munit_prec; /* global_precision expressed in MULTUNITs */ + +/* Note that since the SPARC CPU has no hardware integer multiply + instruction, there is not that much advantage in having an + assembly version of mp_smul on that machine. It might be faster + to use Merritt's modmult instead of Upton's modmult on the SPARC. +*/ + +/* + Multiply the single-word multiplier times the multiprecision integer + in multiplicand, accumulating result in prod. The resulting + multiprecision prod will be 1 word longer than the multiplicand. + multiplicand is munit_prec words long. We add into prod, so caller + should zero it out first. For best results, this time-critical + function should be implemented in assembly. + NOTE: Unlike other functions in the multiprecision arithmetic + library, both multiplicand and prod are pointing at the LSB, + regardless of byte order of the machine. On an 80x86, this makes + no difference. But if this assembly function is implemented + on a 680x0, it becomes important. +*/ +/* Note that this has been modified from the previous version to allow + better support for Smith's modmult: + The final carry bit is added to the existing product + array, rather than simply stored. +*/ + +#ifndef mp_smul +void mp_smul (MULTUNIT *prod, MULTUNIT *multiplicand, MULTUNIT multiplier) +{ + short i; + unsigned long p, carry; + + carry = 0; + for (i=0; i> MULTUNITSIZE; + } + /* Add carry to the next higher word of product / dividend */ + *prod += (MULTUNIT)carry; +} /* mp_smul */ +#endif + +/* mp_dmul is a double-precision multiply multiplicand times multiplier, + result into prod. prod must be pointing at a "double precision" + buffer. E.g. If global_precision is 10 words, prod must be + pointing at a 20-word buffer. +*/ +#ifndef mp_dmul +void mp_dmul (unitptr prod, unitptr multiplicand, unitptr multiplier) +{ + register int i; + register MULTUNIT *p_multiplicand, *p_multiplier; + register MULTUNIT *prodp; + + + unitfill0(prod,global_precision*2); /* Pre-zero prod */ + /* Calculate precision in units of MULTUNIT */ + munit_prec = global_precision * UNITSIZE / MULTUNITSIZE; + p_multiplicand = (MULTUNIT *)multiplicand; + p_multiplier = (MULTUNIT *)multiplier; + prodp = (MULTUNIT *)prod; + make_lsbptr(p_multiplicand,munit_prec); + make_lsbptr(p_multiplier,munit_prec); + make_lsbptr(prodp,munit_prec*2); + /* Multiply multiplicand by each word in multiplier, accumulating prod: */ + for (i=0; i 0) + mp_sub (d,modulus); + + mp_move(prod,d); + return(0); /* normal return */ +} /* upton_modmult */ + + +/* Upton's mp_modmult function leaves some internal arrays in memory, + so we have to call modmult_burn() at the end of mp_modexp. + This is so that no cryptographically sensitive data is left in memory + after the program exits. + upton_burn() is aliased to modmult_burn(). +*/ +void upton_burn(void) +{ + unitfill0(modulus,MAX_UNIT_PRECISION); + unitfill0(reciprocal,MAX_UNIT_PRECISION); + unitfill0(dhi,MAX_UNIT_PRECISION); + unitfill0(d_data,MAX_UNIT_PRECISION*2); + unitfill0(e_data,MAX_UNIT_PRECISION*2); + unitfill0(f_data,MAX_UNIT_PRECISION*2); + nbits = nbitsDivUNITSIZE = nbitsModUNITSIZE = 0; +} /* upton_burn */ + +/******* end of Upton's MODMULT stuff. *******/ +/*=========================================================================*/ +#endif /* UPTON */ + +#ifdef SMITH /* using Thad Smith's modmult algorithm */ + +/* Thad Smith's implementation of multiprecision modmult algorithm in C. + Performs a multiply combined with a modulo operation. + The multiplication is done with mp_dmul, the same as for Upton's + modmult. The modulus reduction is done by long division, in + which a trial quotient "digit" is determined, then the product of + that digit and the divisor are subtracted from the dividend. + + In this case, the digit is MULTUNIT in size and the subtraction + is done by adding the product to the one's complement of the + dividend, which allows use of the existing mp_smul routine. + + The following support functions and data structures + are used only by Smith's modmult algorithm... +*/ + +/* These scratchpad arrays are used only by smith_modmult (mp_modmult). + Some of them could be statically declared inside of mp_modmult, but we + put them outside mp_modmult so that they can be wiped clean by + modmult_burn(), which is called at the end of mp_modexp. This is + so that no sensitive data is left in memory after the program exits. +*/ + +static unit ALIGN ds_data[MAX_UNIT_PRECISION*2+2]; + +static unit mod_quotient [4]; +static unit mod_divisor [4]; /* 2 most signif. MULTUNITs of modulus */ + +static MULTUNIT *modmpl; /* ptr to modulus least significant + ** MULTUNIT */ +static int mshift; /* number of bits for + ** recip scaling */ +static MULTUNIT reciph; /* MSunit of scaled recip */ +static MULTUNIT recipl; /* LSunit of scaled recip */ + +static short modlenMULTUNITS; /* length of modulus in MULTUNITs */ +static MULTUNIT mutemp; /* temporary */ + +/* The routines mp_smul and mp_dmul are the same as for UPTON and + should be coded in assembly. Note, however, that the previous + Upton's mp_smul version has been modified to compatible with + Smith's modmult. The new version also still works for Upton's + modmult. +*/ + +#ifndef mp_set_recip +#define mp_set_recip(rh,rl,m) /* null */ +#else +/* setup routine for external mp_quo_digit */ +void mp_set_recip(MULTUNIT rh, MULTUNIT rl, int m); +#endif +MULTUNIT mp_quo_digit (MULTUNIT *dividend); + +#ifdef MULTUNIT_SIZE_SAME +#define mp_musubb mp_subb /* use existing routine */ +#else /* ! MULTUNIT_SIZE_SAME */ + +/* This performs the same function as mp_subb, but with MULTUNITs. + Note: Processors without alignment requirements may be able to use + mp_subb, even though MULTUNITs are smaller than units. In that case, + use mp_subb, since it would be faster if coded in assembly. Note that + this implementation won't work for MULTUNITs which are long -- use + mp_subb in that case. +*/ +#ifndef mp_musubb +boolean mp_musubb + (register MULTUNIT* r1,register MULTUNIT* r2,register boolean borrow) + /* multiprecision subtract of MULTUNITs with borrow, r2 from r1, + ** result in r1 */ + /* borrow is incoming borrow flag-- value should be 0 or 1 */ +{ register ulint x; /* won't work if sizeof(MULTUNIT)== + sizeof(long) */ + short precision; /* number of MULTUNITs to subtract */ + precision = global_precision * MULTUNITs_perunit; + make_lsbptr(r1,precision); + make_lsbptr(r2,precision); + while (precision--) + { x = (ulint) *r1 - (ulint) *post_higherunit(r2) - (ulint) borrow; + *post_higherunit(r1) = x; + borrow = (((1L << MULTUNITSIZE) & x) != 0L); + } + return (borrow); +} /* mp_musubb */ +#endif /* mp_musubb */ +#endif /* MULTUNIT_SIZE_SAME */ + +/* The function mp_quo_digit is the heart of Smith's modulo reduction, + which uses a form of long division. It computes a trial quotient + "digit" (MULTUNIT-sized digit) by multiplying the three most + significant MULTUNITs of the dividend by the two most significant + MULTUNITs of the reciprocal of the modulus. Note that this function + requires that MULTUNITSIZE * 2 <= sizeof(unsigned long). + + An important part of this technique is that the quotient never be + too small, although it may occasionally be too large. This was + done to eliminate the need to check and correct for a remainder + exceeding the divisor. It is easier to check for a negative + remainder. The following technique rarely needs correction for + MULTUNITs of at least 16 bits. + + The following routine has two implementations: + + ASM_PROTOTYPE defined: written to be an executable prototype for + an efficient assembly language implementation. Note that several + of the following masks and shifts can be done by directly + manipulating single precision registers on some architectures. + + ASM_PROTOTYPE undefined: a slightly more efficient implementation + in C. Although this version returns a result larger than the + optimum (which is corrected in smith_modmult) more often than the + prototype version, the faster execution in C more than makes up + for the difference. + + Parameter: dividend - points to the most significant MULTUNIT + of the dividend. Note that dividend actually contains the + one's complement of the actual dividend value (see comments for + smith_modmult). + + Return: the trial quotient digit resulting from dividing the first + three MULTUNITs at dividend by the upper two MULTUNITs of the + modulus. +*/ + +/* #define ASM_PROTOTYPE */ /* undefined: use C-optimized version */ + +#ifndef mp_quo_digit +MULTUNIT mp_quo_digit (MULTUNIT *dividend) { + unsigned long q, q0, q1, q2; + unsigned short lsb_factor; + +/* Compute the least significant product group. + The last terms of q1 and q2 perform upward rounding, which is + needed to guarantee that the result not be too small. +*/ + q1 = (dividend[tohigher(-2)] ^ MULTUNIT_mask) * (unsigned long)reciph + + reciph; + q2 = (dividend[tohigher(-1)] ^ MULTUNIT_mask) * (unsigned long)recipl + + (1L << MULTUNITSIZE) ; +#ifdef ASM_PROTOTYPE + lsb_factor = 1 & (q1>>MULTUNITSIZE) & (q2>>MULTUNITSIZE); + q = q1 + q2; + + /* The following statement is equivalent to shifting the sum right + one bit while shifting in the carry bit. + */ + q0 = (q1 > ~q2 ? DMULTUNIT_msb : 0) | (q >> 1); +#else /* optimized C version */ + q0 = (q1>>1) + (q2>>1) + 1; +#endif + +/* Compute the middle significant product group. */ + + q1 = (dividend[tohigher(-1)] ^ MULTUNIT_mask) * (unsigned long)reciph; + q2 = (dividend[ 0] ^ MULTUNIT_mask) * (unsigned long)recipl; +#ifdef ASM_PROTOTYPE + q = q1 + q2; + q = (q1 > ~q2 ? DMULTUNIT_msb : 0) | (q >> 1); + +/* Add in the most significant word of the first group. + The last term takes care of the carry from adding the lsb's + that were shifted out prior to addition. +*/ + q = (q0 >> MULTUNITSIZE)+ q + (lsb_factor & (q1 ^ q2)); +#else /* optimized C version */ + q = (q0 >> MULTUNITSIZE)+ (q1>>1) + (q2>>1) + 1; +#endif + +/* Compute the most significant term and add in the others */ + + q = (q >> (MULTUNITSIZE-2)) + + (((dividend[0] ^ MULTUNIT_mask) * (unsigned long)reciph) << 1); + q >>= mshift; + +/* Prevent overflow and then wipe out the intermediate results. */ + + mutemp = (MULTUNIT)min(q, (1L << MULTUNITSIZE) -1); + q= q0 = q1 = q2 = 0; lsb_factor = 0; (void)lsb_factor; + return mutemp; +} +#endif /* mp_quo_digit */ + +/* stage_smith_modulus() - Prepare for a Smith modmult. + + Calculate the reciprocal of modulus with a precision of two MULTUNITs. + Assumes that global_precision has already been adjusted to the + size of the modulus, plus SLOP_BITS. + + Note: This routine was designed to work with large values and + doesn't have the necessary testing or handling to work with a + modulus having less than three significant units. For such cases, + the separate multiply and modulus routines can be used. + + stage_smith_modulus() is aliased to stage_modulus(). +*/ + +int stage_smith_modulus(unitptr n_modulus) +{ + int original_precision; + int sigmod; /* significant units in modulus */ + unitptr mp; /* modulus most significant pointer */ + MULTUNIT *mpm; /* reciprocal pointer */ + int prec; /* precision of reciprocal calc in units */ + + mp_move(modulus, n_modulus); + modmpl = (MULTUNIT*) modulus; + modmpl = lsbptr (modmpl, global_precision * MULTUNITs_perunit); + nbits = countbits(modulus); + modlenMULTUNITS = (nbits+ MULTUNITSIZE-1) / MULTUNITSIZE; + + original_precision = global_precision; + + /* The following code copies the three most significant units of + * modulus to mod_divisor. + */ + mp = modulus; + sigmod = significance (modulus); + rescale (mp, original_precision, sigmod); +/* prec is the unit precision required for 3 MULTUNITs */ + prec = (3 +(MULTUNITs_perunit-1)) / MULTUNITs_perunit; + set_precision (prec); + + /* set mp = ptr to most significant units of modulus, then move + * the most significant units to mp_divisor + */ + mp = msbptr(mp,sigmod) -tohigher(prec-1); + unmake_lsbptr (mp, prec); + mp_move (mod_divisor, mp); + + /* Keep 2*MULTUNITSIZE bits in mod_divisor. + * This will (normally) result in a reciprocal of 2*MULTUNITSIZE+1 bits. + */ + mshift = countbits (mod_divisor) - 2*MULTUNITSIZE; + mp_shift_right_bits (mod_divisor, mshift); + mp_recip(mod_quotient,mod_divisor); + mp_shift_right_bits (mod_quotient,1); + + /* Reduce to: 0 < mshift <= MULTUNITSIZE */ + mshift = ((mshift + (MULTUNITSIZE-1)) % MULTUNITSIZE) +1; + /* round up, rescaling if necessary */ + mp_inc (mod_quotient); + if (countbits (mod_quotient) > 2*MULTUNITSIZE) { + mp_shift_right_bits (mod_quotient,1); + mshift--; /* now 0 <= mshift <= MULTUNITSIZE */ + } + mpm = lsbptr ((MULTUNIT*)mod_quotient, prec*MULTUNITs_perunit); + recipl = *post_higherunit (mpm); + reciph = *mpm; + mp_set_recip (reciph, recipl, mshift); + set_precision (original_precision); + return(0); /* normal return */ +} /* stage_smith_modulus */ + +/* Smith's algorithm performs a multiply combined with a modulo operation. + Computes: prod = (multiplicand*multiplier) mod modulus + The modulus must first be set by stage_smith_modulus(). + smith_modmult() is aliased to mp_modmult(). +*/ + +int +smith_modmult(unitptr prod, unitptr multiplicand, unitptr multiplier) +{ + unitptr d; /* ptr to product */ + MULTUNIT *dmph, *dmpl, *dmp; /* ptrs to dividend (high, low, first) + * aligned for subtraction */ +/* Note that dmph points one MULTUNIT higher than indicated by + global precision. This allows us to zero out a word one higher than + the normal precision. +*/ + short orig_precision; + short nqd; /* number of quotient digits remaining to + * be generated */ + short dmi; /* number of significant MULTUNITs in product */ + + d = ds_data + 1; /* room for leading MSB if HIGHFIRST */ + orig_precision = global_precision; + mp_dmul(d, multiplicand, multiplier); + + rescale(d, orig_precision * 2, orig_precision * 2 + 1); + set_precision(orig_precision * 2 + 1); + *msbptr(d, global_precision) = 0; /* leading 0 unit */ + +/* We now start working with MULTUNITs. + Determine the most significant MULTUNIT of the product so we don't + have to process leading zeros in our divide loop. +*/ + dmi = significance(d) * MULTUNITs_perunit; + if (dmi >= modlenMULTUNITS) + { /* Make dividend negative. This allows the use of mp_smul to + * "subtract" the product of the modulus and the trial divisor + * by actually adding to a negative dividend. + * The one's complement of the dividend is used, since it causes + * a zero value to be represented as all ones. This facilitates + * testing the result for possible overflow, since a sign bit + * indicates that no adjustment is necessary, and we should not + * attempt to adjust if the result of the addition is zero. + */ + mp_inc(d); + mp_neg(d); + set_precision(orig_precision); + munit_prec = global_precision * UNITSIZE / MULTUNITSIZE; + + /* Set msb, lsb, and normal ptrs of dividend */ + dmph = lsbptr((MULTUNIT *) d, (orig_precision * 2 + 1) * + MULTUNITs_perunit) + tohigher(dmi); + nqd = dmi + 1 - modlenMULTUNITS; + dmpl = dmph - tohigher(modlenMULTUNITS); + +/* Divide loop. + Each iteration computes the next quotient MULTUNIT digit, then + multiplies the divisor (modulus) by the quotient digit and adds + it to the one's complement of the dividend (equivalent to + subtracting). If the product was greater than the remaining dividend, + we get a non-negative result, in which case we subtract off the + modulus to get the proper negative remainder. +*/ + for (; nqd; nqd--) + { MULTUNIT q; /* quotient trial digit */ + + q = mp_quo_digit(dmph); + if (q > 0) + { mp_smul(dmpl, modmpl, q); + + /* Perform correction if q too large. + * This rarely occurs. + */ + if (!(*dmph & MULTUNIT_msb)) + { dmp = dmpl; + unmake_lsbptr(dmp, orig_precision * + MULTUNITs_perunit); + if (mp_musubb(dmp, + (MULTUNIT *) modulus, 0)) + (*dmph) --; + } + } + pre_lowerunit(dmph); + pre_lowerunit(dmpl); + } + /* d contains the one's complement of the remainder. */ + rescale(d, orig_precision * 2 + 1, orig_precision); + set_precision(orig_precision); + mp_neg(d); + mp_dec(d); + } else + { /* Product was less than modulus. Return it. */ + rescale(d, orig_precision * 2 + 1, orig_precision); + set_precision(orig_precision); + } + mp_move(prod, d); + return (0); /* normal return */ +} /* smith_modmult */ + + +/* Smith's mp_modmult function leaves some internal arrays in memory, + so we have to call modmult_burn() at the end of mp_modexp. + This is so that no cryptographically sensitive data is left in memory + after the program exits. + smith_burn() is aliased to modmult_burn(). +*/ +void smith_burn(void) +{ + empty_array (modulus); + empty_array (ds_data); + empty_array (mod_quotient); + empty_array (mod_divisor); + modmpl = 0; + mshift =nbits = 0; + reciph = recipl = 0; + modlenMULTUNITS = mutemp = 0; + mp_set_recip (0,0,0); +} /* smith_burn */ + +/* End of Thad Smith's implementation of modmult. */ + +#endif /* SMITH */ + + +int countbits(unitptr r) + /* Returns number of significant bits in r */ +{ int bits; + short prec; + register unit bitmask; + init_bitsniffer(r,bitmask,prec,bits); + return(bits); +} /* countbits */ + + +char *copyright_notice(void) +/* force linker to include copyright notice in the executable object image. */ +{ return ("(c)1986 Philip Zimmermann"); } /* copyright_notice */ + + +int mp_modexp(register unitptr expout,register unitptr expin, + register unitptr exponent,register unitptr modulus) +{ /* Russian peasant combined exponentiation/modulo algorithm. + Calls modmult instead of mult. + Computes: expout = (expin**exponent) mod modulus + WARNING: All the arguments must be less than the modulus! + */ + int bits; + short oldprecision; + register unit bitmask; + unit product[MAX_UNIT_PRECISION]; + short eprec; + +#ifdef COUNTMULTS + tally_modmults = 0; /* clear "number of modmults" counter */ + tally_modsquares = 0; /* clear "number of modsquares" counter */ +#endif /* COUNTMULTS */ + mp_init(expout,1); + if (testeq(exponent,0)) + { if (testeq(expin,0)) + return(-1); /* 0 to the 0th power means return error */ + return(0); /* otherwise, zero exponent means expout is 1 */ + } + if (testeq(modulus,0)) + return(-2); /* zero modulus means error */ +#if SLOP_BITS > 0 /* if there's room for sign bits */ + if (mp_tstminus(modulus)) + return(-2); /* negative modulus means error */ +#endif /* SLOP_BITS > 0 */ + if (mp_compare(expin,modulus) >= 0) + return(-3); /* if expin >= modulus, return error */ + if (mp_compare(exponent,modulus) >= 0) + return(-4); /* if exponent >= modulus, return error */ + + oldprecision = global_precision; /* save global_precision */ + /* set smallest optimum precision for this modulus */ + set_precision(bits2units(countbits(modulus)+SLOP_BITS)); + /* rescale all these registers to global_precision we just defined */ + rescale(modulus,oldprecision,global_precision); + rescale(expin,oldprecision,global_precision); + rescale(exponent,oldprecision,global_precision); + rescale(expout,oldprecision,global_precision); + + if (stage_modulus(modulus)) + { set_precision(oldprecision); /* restore original precision */ + return(-5); /* unstageable modulus (STEWART algorithm) */ + } + + /* normalize and compute number of bits in exponent first */ + init_bitsniffer(exponent,bitmask,eprec,bits); + + /* We can "optimize out" the first modsquare and modmult: */ + bits--; /* We know for sure at this point that bits>0 */ + mp_move(expout,expin); /* expout = (1*1)*expin; */ + bump_bitsniffer(exponent,bitmask); + + while (bits--) + { + poll_for_break(); /* polls keyboard, allows ctrl-C to abort program */ +#ifdef COUNTMULTS + tally_modsquares++; /* bump "number of modsquares" counter */ +#endif /* COUNTMULTS */ + mp_modsquare(product,expout); + if (sniff_bit(exponent,bitmask)) + { mp_modmult(expout,product,expin); +#ifdef COUNTMULTS + tally_modmults++; /* bump "number of modmults" counter */ +#endif /* COUNTMULTS */ + } else + { + mp_move(expout,product); + } + bump_bitsniffer(exponent,bitmask); + } /* while bits-- */ + mp_burn(product); /* burn the evidence on the stack */ + modmult_burn(); /* ask mp_modmult to also burn its own evidence */ + +#ifdef COUNTMULTS /* diagnostic analysis */ + { long atomic_mults; + unsigned int unitcount,totalmults; + unitcount = bits2units(countbits(modulus)); + /* calculation assumes modsquare takes as long as a modmult: */ + atomic_mults = (long) tally_modmults * (unitcount * unitcount); + atomic_mults += (long) tally_modsquares * (unitcount * unitcount); + printf("%ld atomic mults for ",atomic_mults); + printf("%d+%d = %d modsqr+modmlt, at %d bits, %d words.\n", + tally_modsquares,tally_modmults, + tally_modsquares+tally_modmults, + countbits(modulus), unitcount); + } +#endif /* COUNTMULTS */ + + set_precision(oldprecision); /* restore original precision */ + + /* Do an explicit reference to the copyright notice so that the linker + will be forced to include it in the executable object image... */ + copyright_notice(); /* has no real effect at run time */ + + return(0); /* normal return */ +} /* mp_modexp */ + +int mp_modexp_crt(unitptr expout, unitptr expin, + unitptr p, unitptr q, unitptr ep, unitptr eq, unitptr u) + /* This is a faster modexp for moduli with a known + factorisation into two relatively prime factors p and q, + and an input relatively prime to the modulus, + the Chinese Remainder Theorem to do the computation + mod p and mod q, and then combine the results. This + relies on a number of precomputed values, but does not + actually require the modulus n or the exponent e. + + expout = expin ^ e mod (p*q). + We form this by evaluating + p2 = (expin ^ e) mod p and + q2 = (expin ^ e) mod q + and then combining the two by the CRT. + + Two optimisations of this are possible. First, we can + reduce expin modulo p and q before starting. + + Second, since we know the factorisation of p and q + (trivially derived from the factorisation of n = p*q), + and expin is relatively prime to both p and q, + we can use Euler's theorem, expin^phi(m) = 1 (mod m), + to throw away multiples of phi(p) or phi(q) in e. + Letting ep = e mod phi(p) and + eq = e mod phi(q) + then combining these two speedups, we only need to evaluate + p2 = ((expin mod p) ^ ep) mod p and + q2 = ((expin mod q) ^ eq) mod q. + + Now we need to apply the CRT. Starting with + expout = p2 (mod p) and + expout = q2 (mod q) + we can say that expout = p2 + p * k, and if we assume that + 0 <= p2 < p, then 0 <= expout < p*q for some 0 <= k < q. + Since we want expout = q2 (mod q), then + p*k = q2-p2 (mod q). Since p and q are relatively + prime, p has a multiplicative inverse u mod q. In other + words, + u = 1/p (mod q). + Multiplying by u on both sides gives k = u*(q2-p2) (mod q). + Since we want 0 <= k < q, we can thus find k as + k = (u * (q2-p2)) mod q. + + Once we have k, evaluating p2 + p * k is easy, and + that gives us the result. + + In the detailed implementation, there is a temporary, temp, + used to hold intermediate results, p2 is held in expout, + and q2 is used as a temporary in the final step when it is + no longer needed. With that, you should be able to + understand the code below. + */ +{ + unit q2[MAX_UNIT_PRECISION]; + unit temp[MAX_UNIT_PRECISION]; + int status; + +/* First, compiute p2 (physically held in M) */ + +/* p2 = [ (expin mod p) ^ ep ] mod p */ + mp_mod(temp,expin,p); /* temp = expin mod p */ + status = mp_modexp(expout,temp,ep,p); + if (status < 0) /* mp_modexp returned an error. */ + { mp_init(expout,1); + return(status); /* error return */ + } + +/* And the same thing for q2 */ + +/* q2 = [ (expin mod q) ^ eq ] mod q */ + mp_mod(temp,expin,q); /* temp = expin mod q */ + status = mp_modexp(q2,temp,eq,q); + if (status < 0) /* mp_modexp returned an error. */ + { mp_init(expout,1); + return(status); /* error return */ + } + +/* Now use the multiplicative inverse u to glue together the + two halves. +*/ +#if 0 +/* This optimisation is useful if you commonly get small results, + but for random results and large q, the odds of (1/q) of it + being useful do not warrant the test. +*/ + if (mp_compare(expout,q2) != 0) + { +#endif + /* Find q2-p2 mod q */ + if (mp_sub(q2,expout)) /* if the result went negative */ + mp_add(q2,q); /* add q to q2 */ + + /* expout = p2 + ( p * [(q2*u) mod q] ) */ + mp_mult(temp,q2,u); /* q2*u */ + mp_mod(q2,temp,q); /* (q2*u) mod q */ + mp_mult(temp,p,q2); /* p * [(q2*u) mod q] */ + mp_add(expout,temp); /* expout = p2 + p * [...] */ +#if 0 + } +#endif + + mp_burn(q2); /* burn the evidence on the stack...*/ + mp_burn(temp); + return(0); /* normal return */ +} /* mp_modexp_crt */ + + +/****************** end of MPI library ****************************/ diff --git a/lib/mpilib.h b/lib/mpilib.h new file mode 100644 index 0000000..0227105 --- /dev/null +++ b/lib/mpilib.h @@ -0,0 +1,468 @@ +/* C include file for MPI multiprecision integer math routines. + + Boulder Software Engineering + 3021 Eleventh Street + Boulder, CO 80304 + (303) 541-0140 + + (c) Copyright 1986-92 by Philip Zimmermann. All rights reserved. + The author assumes no liability for damages resulting from the use + of this software, even if the damage results from defects in this + software. No warranty is expressed or implied. + + These routines implement all of the multiprecision arithmetic necessary + for Rivest-Shamir-Adleman (RSA) public key cryptography, as well as + other number-theoretic algorithms such as ElGamal, Diffie-Hellman, + or Rabin. + + Although originally developed in Microsoft C for the IBM PC, this code + contains few machine dependencies. It assumes 2's complement + arithmetic. It can be adapted to 8-bit, 16-bit, or 32-bit machines, + lowbyte-highbyte order or highbyte-lowbyte order. This version + has been converted to ANSI C. + + Modified 8 Apr 92 - HAJK - Implement new VAX/VMS primitive support. + Modified 29 Nov 92 - Thad Smith +*/ + +#include +#include "usuals.h" /* typedefs for byte, word16, boolean, etc. */ +#include "platform.h" /* customization for different environments */ + +/* Platform customization: + * A version which runs on almost any computer can be implemented by + * defining PORTABLE and MPORTABLE, preferably as a command line + * parameter. Faster versions can be generated by specifying specific + * parameters, such as size of unit and MULTUNIT, and by supplying some + * of the critical in assembly. See the file platform.h for more + * details on customization. + * + * The symbol HIGHFIRST, designating that integers and longs are stored + * with the most significant bit in the lowest address, should be defined + * on the command line for compiling all files, since it is used by files + * other than the mpilib routines. + */ + +#ifndef ALIGN +#define ALIGN +#endif + +#ifndef PEASANT /* if not Russian peasant modulo multiply algorithm */ +#ifndef MERRITT /* if not Merritt's modmult */ +#ifndef UPTON /* if not Upton's modmult */ +#ifndef SMITH +#define SMITH /* default: use Smith's modmult algorithm */ +#endif +#endif +#endif +#endif + +#ifdef SMITH +#define UPTON_OR_SMITH /* enable common code */ +#endif +#ifdef UPTON +#define UPTON_OR_SMITH /* enable common code */ +#endif + +#ifndef UNIT32 +#ifndef UNIT8 +#define UNIT16 /* default--use 16-bit units */ +#endif +#endif + +/*** CAUTION: If your machine has an unusual word size that is not a + power of 2 (8, 16, 32, or 64) bits wide, then the macros here that + use the symbol "LOG_UNITSIZE" must be changed. +***/ + +#ifdef UNIT8 +typedef unsigned char unit; +typedef signed char signedunit; +#define UNITSIZE 8 /* number of bits in a unit */ +#define LOG_UNITSIZE 3 +#define uppermostbit ((unit) 0x80) +#define BYTES_PER_UNIT 1 /* number of bytes in a unit */ +#define units2bits(n) ((n) << 3) /* fast multiply by UNITSIZE */ +#define units2bytes(n) (n) +#define bits2units(n) (((n)+7) >> 3) +#define bytes2units(n) (n) +#endif + +#ifdef UNIT16 +typedef word16 unit; +typedef short signedunit; +#define UNITSIZE 16 /* number of bits in a unit */ +#define LOG_UNITSIZE 4 +#define uppermostbit ((unit) 0x8000) +#define BYTES_PER_UNIT 2 /* number of bytes in a unit */ +#define units2bits(n) ((n) << 4) /* fast multiply by UNITSIZE */ +#define units2bytes(n) ((n) << 1) +#define bits2units(n) (((n)+15) >> 4) +#define bytes2units(n) (((n)+1) >> 1) +#endif + +#ifdef UNIT32 +typedef word32 unit; +typedef long signedunit; +#define UNITSIZE 32 /* number of bits in a unit */ +#define LOG_UNITSIZE 5 +#define uppermostbit ((unit) 0x80000000L) +#define BYTES_PER_UNIT 4 /* number of bytes in a unit */ +#define units2bits(n) ((n) << 5) /* fast multiply by UNITSIZE */ +#define units2bytes(n) ((n) << 2) +#define bits2units(n) (((n)+31) >> 5) +#define bytes2units(n) (((n)+3) >> 2) +#endif + +#define power_of_2(b) ((unit) 1 << (b)) /* computes power-of-2 bit masks */ +#define bits2bytes(n) (((n)+7) >> 3) +/* Some C compilers (like the ADSP2101) will not always collapse constant + expressions at compile time if the expressions contain shift operators. */ +/* #define uppermostbit power_of_2(UNITSIZE-1) */ +/* #define UNITSIZE units2bits(1) */ /* number of bits in a unit */ +/* #define bytes2units(n) bits2units((n)<<3) */ +/* #define BYTES_PER_UNIT (UNITSIZE >> 3) */ +/* LOG_UNITSIZE is the log base 2 of UNITSIZE, ie: 4 for 16-bit units */ +/* #define units2bits(n) ((n) << LOG_UNITSIZE) */ /* fast multiply by UNITSIZE */ +/* #define units2bytes(n) ((n) << (LOG_UNITSIZE-3)) */ +/* #define bits2units(n) (((n)+(UNITSIZE-1)) >> LOG_UNITSIZE) */ +/* #define bytes2units(n) (((n)+(BYTES_PER_UNIT-1)) >> (LOG_UNITSIZE-3)) */ + +typedef unit *unitptr; + + +/*--------------------- Byte ordering stuff -------------------*/ +#ifdef HIGHFIRST + +/* these definitions assume MSB comes first */ +#define tohigher(n) (-(n)) /* offset towards higher unit */ +#define pre_higherunit(r) (--(r)) +#define pre_lowerunit(r) (++(r)) +#define post_higherunit(r) ((r)--) +#define post_lowerunit(r) ((r)++) +#define bit_index(n) (global_precision-bits2units((n)+1)) +#define lsbptr(r,prec) ((r)+(prec)-1) +#define make_lsbptr(r,prec) (r) = lsbptr(r,prec) +#define unmake_lsbptr(r,prec) (r) = ((r)-(prec)+1) +#define msbptr(r,prec) (r) +#define make_msbptr(r,prec) /* (r) = msbptr(r,prec) */ + +/* The macro rescale(r,current_precision,new_precision) rescales + a multiprecision integer by adjusting r and its precision to new values. + It can be used to reverse the effects of the normalize + routine given above. See the comments in normalize concerning + Intel vs. Motorola LSB/MSB conventions. + WARNING: You can only safely call rescale on registers that + you have previously normalized with the above normalize routine, + or are known to be big enough for the new precision. You may + specify a new precision that is smaller than the current precision. + You must be careful not to specify a new_precision value that is + too big, or which adjusts the r pointer out of range. +*/ +#define rescale(r,currentp,newp) r -= ((newp) - (currentp)) + +/* The macro normalize(r,precision) "normalizes" a multiprecision integer + by adjusting r and precision to new values. For Motorola-style processors + (MSB-first), r is a pointer to the MSB of the register, and must + be adjusted to point to the first nonzero unit. For Intel/VAX-style + (LSB-first) processors, r is a pointer to the LSB of the register, + and must be left unchanged. The precision counter is always adjusted, + regardless of processor type. In the case of precision = 0, + r becomes undefined. +*/ +#define normalize(r,prec) \ + { prec = significance(r); r += (global_precision-(prec)); } + +#else /* LOWFIRST byte order */ + +/* these definitions assume LSB comes first */ +#define tohigher(n) (n) /* offset towards higher unit */ +#define pre_higherunit(r) (++(r)) +#define pre_lowerunit(r) (--(r)) +#define post_higherunit(r) ((r)++) +#define post_lowerunit(r) ((r)--) +#define bit_index(n) (bits2units((n)+1)-1) +#define lsbptr(r,prec) (r) +#define make_lsbptr(r,prec) /* (r) = lsbptr(r,prec) */ +#define unmake_lsbptr(r,prec) /* (r) = (r) */ +#define msbptr(r,prec) ((r)+(prec)-1) +#define make_msbptr(r,prec) (r) = msbptr(r,prec) + +#define rescale(r,currentp,newp) /* nil statement */ +#define normalize(r,prec) prec = significance(r) + +#endif /* LOWFIRST byte order */ +/*------------------ End byte ordering stuff -------------------*/ + +/* Note that the address calculations require that lsbptr, msbptr, + make_lsbptr, make_msbptr, mp_tstbit, mp_setbit, mp_clrbit, + and bitptr all have unitptr arguments, not byte pointer arguments. */ +#define bitptr(r,n) &((r)[bit_index(n)]) +#define bitmsk(n) power_of_2((n) & (UNITSIZE-1)) + /* bitmsk() assumes UNITSIZE is a power of 2 */ +#define mp_tstbit(r,n) (*bitptr(r,n) & bitmsk(n)) +#define mp_setbit(r,n) (*bitptr(r,n) |= bitmsk(n)) +#define mp_clrbit(r,n) (*bitptr(r,n) &= ~bitmsk(n)) +#define msunit(r) (*msbptr(r,global_precision)) +#define lsunit(r) (*lsbptr(r,global_precision)) +/* #define mp_tstminus(r) ((msunit(r) & uppermostbit)!=0) */ +#define mp_tstminus(r) ((signedunit) msunit(r) < 0) + + + /* set working precision to specified number of bits. */ +#ifdef mp_setp +void mp_setp(short nbits); +#define set_precision(prec) mp_setp(units2bits(global_precision=(prec))) +#else +#define set_precision(prec) (global_precision = (prec)) +#endif + + +#ifdef PEASANT + +/* Define C names for Russian peasant modmult primitives. */ +#define stage_modulus stage_peasant_modulus +#define mp_modmult peasant_modmult +#define modmult_burn peasant_burn +#define SLOP_BITS PEASANT_SLOP_BITS + +#else /* not PEASANT */ +#ifdef MERRITT +/* Define C names for Merritt's modmult primitives. */ +#define stage_modulus stage_merritt_modulus +#define mp_modmult merritt_modmult +#define modmult_burn merritt_burn +#define SLOP_BITS MERRITT_SLOP_BITS + +#else /* not PEASANT, MERRITT */ +#ifdef UPTON +/* Define C names for Upton's modmult primitives. */ +#define stage_modulus stage_upton_modulus +#define mp_modmult upton_modmult +#define modmult_burn upton_burn +#define SLOP_BITS UPTON_SLOP_BITS + +#else /* not PEASANT, MERRITT, UPTON */ +#ifdef SMITH +/* Define C names for Smith's modmult primitives. */ +#define stage_modulus stage_smith_modulus +#define mp_modmult smith_modmult +#define modmult_burn smith_burn +#define SLOP_BITS SMITH_SLOP_BITS + +#endif /* SMITH */ +#endif /* UPTON */ +#endif /* MERRITT */ +#endif /* PEASANT */ + + +#define mp_shift_left(r1) mp_rotate_left(r1,(boolean)0) + /* multiprecision shift left 1 bit */ + +#define mp_add(r1,r2) mp_addc(r1,r2,(boolean)0) + /* multiprecision add with no carry */ + +#define mp_sub(r1,r2) mp_subb(r1,r2,(boolean)0) + /* multiprecision subtract with no borrow */ + +#define mp_abs(r) (mp_tstminus(r) ? (mp_neg(r),TRUE) : FALSE) + +#define msub(r,m) if (mp_compare(r,m) >= 0) mp_sub(r,m) + /* Prevents r from getting bigger than modulus m */ + +#define testeq(r,i) \ + ( (lsunit(r)==(i)) && (significance(r)<=1) ) + +#define testne(r,i) \ + ( (lsunit(r)!=(i)) || (significance(r)>1) ) + +#define testge(r,i) \ + ( (lsunit(r)>=(i)) || (significance(r)>1) ) + +#define testle(r,i) \ + ( (lsunit(r)<=(i)) && (significance(r)<=1) ) + +#define mp_square(r1,r2) mp_mult(r1,r2,r2) + /* Square r2, returning product in r1 */ + +#define mp_modsquare(r1,r2) mp_modmult(r1,r2,r2) + /* Square r2, returning modulo'ed product in r1 */ + +#define countbytes(r) ((countbits(r)+7)>>3) + +/* SLOP_BITS is how many "carry bits" to allow for intermediate + calculation results to exceed the size of the modulus. + It is used by modexp to give some overflow elbow room for + modmult to use to perform modulo operations with the modulus. + The number of slop bits required is determined by the modmult + algorithm. The Russian peasant modmult algorithm only requires + 1 slop bit, for example. Note that if we use an external assembly + modmult routine, SLOP_BITS may be meaningless or may be defined in a + non-constant manner. +*/ +#define PEASANT_SLOP_BITS 1 +#define MERRITT_SLOP_BITS UNITSIZE +#define UPTON_SLOP_BITS (UNITSIZE/2) +#ifdef mp_smul /* old version requires MS word = 0 */ +#define SMITH_SLOP_BITS UNITSIZE +#else /* mp_smula or C version of mp_smul */ +#define SMITH_SLOP_BITS 0 +#endif /* mp_smul */ + +/* MAX_BIT_PRECISION is upper limit that assembly primitives can handle. + It must be less than 32704 bits, or 4088 bytes. It should be an + integer multiple of UNITSIZE*2. +*/ +#if (SLOP_BITS > 0) +#define MAX_BIT_PRECISION (1280+(2*UNITSIZE)) +#else +#define MAX_BIT_PRECISION 1280 +#endif +#define MAX_BYTE_PRECISION (MAX_BIT_PRECISION/8) +#define MAX_UNIT_PRECISION (MAX_BIT_PRECISION/UNITSIZE) + + +/* global_precision is the unit precision last set by set_precision */ +extern short global_precision; + + +/* The "bit sniffer" macros all begin sniffing at the MSB. + They are used internally by all the various multiply, divide, + modulo, exponentiation, and square root functions. +*/ +#define sniff_bit(bptr,bitmask) (*(bptr) & bitmask) + +#define init_bitsniffer(bptr,bitmask,prec,bits) \ +{ normalize(bptr,prec); \ + if (!prec) \ + return(0); \ + bits = units2bits(prec); \ + make_msbptr(bptr,prec); bitmask = uppermostbit; \ + while (!sniff_bit(bptr,bitmask)) \ + { bitmask >>= 1; bits--; \ + } \ +} + +#define bump_bitsniffer(bptr,bitmask) \ +{ if (!(bitmask >>= 1)) \ + { bitmask = uppermostbit; \ + post_lowerunit(bptr); \ + } \ +} + +/* bump_2bitsniffers is used internally by mp_udiv. */ +#define bump_2bitsniffers(bptr,bptr2,bitmask) \ +{ if (!(bitmask >>= 1)) \ + { bitmask = uppermostbit; \ + post_lowerunit(bptr); \ + post_lowerunit(bptr2); \ + } \ +} + +/* stuff_bit is used internally by mp_udiv and mp_sqrt. */ +#define stuff_bit(bptr,bitmask) *(bptr) |= bitmask + + +boolean mp_addc + (register unitptr r1,register unitptr r2,register boolean carry); + /* multiprecision add with carry r2 to r1, result in r1 */ + +boolean mp_subb + (register unitptr r1,register unitptr r2,register boolean borrow); + /* multiprecision subtract with borrow, r2 from r1, result in r1 */ + +boolean mp_rotate_left(register unitptr r1,register boolean carry); + /* multiprecision rotate left 1 bit with carry, result in r1. */ + +void mp_shift_right_bits(register unitptr r1,register short bits); + /* multiprecision shift right bits, result in r1. */ + +short mp_compare(register unitptr r1,register unitptr r2); + /* Compares registers *r1, *r2, and returns -1, 0, or 1 */ + +boolean mp_inc(register unitptr r); + /* Increment multiprecision integer r. */ + +boolean mp_dec(register unitptr r); + /* Decrement multiprecision integer r. */ + +void mp_neg(register unitptr r); + /* Compute 2's complement, the arithmetic negative, of r */ + +#ifndef mp_move +#define mp_move(d,s) memcpy((void*)(d), (void*)(s), \ + units2bytes(global_precision)) +#endif +#ifndef unitfill0 +#define unitfill0(r,ct) memset((void*)(r), 0, units2bytes(ct)) +#endif + +#ifndef mp_burn +#define mp_burn(r) mp_init(r,0) /* for burning the evidence */ +#define mp_init0(r) mp_init(r,0) +#endif + +#define empty_array(r) unitfill0(r, sizeof(r)/sizeof(r[0])/sizeof(unit)) + +void mp_init(register unitptr r, word16 value); + /* Init multiprecision register r with short value. */ + +short significance(register unitptr r); + /* Returns number of significant units in r */ + +int mp_udiv(register unitptr remainder,register unitptr quotient, + register unitptr dividend,register unitptr divisor); + /* Unsigned divide, treats both operands as positive. */ + +int mp_recip(register unitptr quotient,register unitptr divisor); + /* Compute reciprocal as 1/divisor. Used by faster modmult. */ + +int mp_div(register unitptr remainder,register unitptr quotient, + register unitptr dividend,register unitptr divisor); + /* Signed divide, either or both operands may be negative. */ + +word16 mp_shortdiv(register unitptr quotient, + register unitptr dividend,register word16 divisor); + /* Returns short remainder of unsigned divide. */ + +int mp_mod(register unitptr remainder, + register unitptr dividend,register unitptr divisor); + /* Unsigned divide, treats both operands as positive. */ + +word16 mp_shortmod(register unitptr dividend,register word16 divisor); + /* Just returns short remainder of unsigned divide. */ + +int mp_mult(register unitptr prod, + register unitptr multiplicand,register unitptr multiplier); + /* Computes multiprecision prod = multiplicand * multiplier */ + +int countbits(unitptr r); + /* Returns number of significant bits in r. */ + +int stage_peasant_modulus(unitptr n); +int stage_merritt_modulus(unitptr n); +int stage_upton_modulus(unitptr n); +int stage_smith_modulus(unitptr n); + /* Must pass modulus to stage_modulus before calling modmult. */ + +int peasant_modmult(register unitptr prod, + unitptr multiplicand,register unitptr multiplier); +int merritt_modmult(register unitptr prod, + unitptr multiplicand,register unitptr multiplier); +int upton_modmult(register unitptr prod, + unitptr multiplicand,register unitptr multiplier); +int smith_modmult(register unitptr prod, + unitptr multiplicand,register unitptr multiplier); + /* Performs combined multiply/modulo operation, with global modulus */ + + + +int mp_modexp(register unitptr expout,register unitptr expin, + register unitptr exponent,register unitptr modulus); + /* Combined exponentiation/modulo algorithm. */ + +int mp_modexp_crt(unitptr expout, unitptr expin, + unitptr p, unitptr q, unitptr ep, unitptr eq, unitptr u); + /* exponentiation and modulo using Chinese Remainder Theorem */ + +/****************** end of MPI library ****************************/ diff --git a/lib/ncplib.c b/lib/ncplib.c index d14d877..5e12de7 100644 --- a/lib/ncplib.c +++ b/lib/ncplib.c @@ -7,6 +7,12 @@ #include "ncplib.h" #include "ncplib_err.h" +#ifdef SIGNATURES +#include "ncpsign.h" +#endif +#ifdef NDS_SUPPORT +#include "ndslib.h" +#endif #include extern pid_t wait(int *); @@ -20,7 +26,7 @@ extern pid_t wait(int *); #include #include #include -#include +#include "kernel/route.h" #include #include #include @@ -28,9 +34,21 @@ extern pid_t wait(int *); #include #include +#define NCP_DEFAULT_BUFSIZE 1024 +#ifdef SIGNATURES +#define NCP_DEFAULT_OPTIONS 2 +int in_options=NCP_DEFAULT_OPTIONS; +#endif + static long ncp_negotiate_buffersize(struct ncp_conn *conn, int size, int *target); +#ifdef SIGNATURES +static long + ncp_negotiate_size_and_options(struct ncp_conn *conn, + int size, int options, + int *ret_size, int *ret_options); +#endif static long ncp_login_object(struct ncp_conn *conn, const unsigned char *username, @@ -93,7 +111,7 @@ ipx_fprint_node(FILE * file, IPXNode node) void ipx_fprint_network(FILE * file, IPXNet net) { - fprintf(file, "%08lX", ntohl(net)); + fprintf(file, "%08X", (u_int32_t)ntohl(net)); } void @@ -160,15 +178,16 @@ 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", &addr.sipx_network) != 1) + if (sscanf(buf, "%lx", &sipx_network) != 1) { return 1; } - addr.sipx_network = htonl(addr.sipx_network); + addr.sipx_network = htonl(sipx_network); if ((p = strchr(buf, ':')) == NULL) { return 1; @@ -341,6 +360,12 @@ do_ncp_call(struct ncp_conn *conn, int request_size) long err; memcpy(&request, conn->packet, sizeof(request)); +#ifdef SIGNATURES + if (conn->sign_active) + { + sign_packet(conn, &request_size); + } +#endif while (retries > 0) { @@ -394,7 +419,7 @@ do_ncp_call(struct ncp_conn *conn, int request_size) 0, 1, &err); goto re_select; } - ipx_recv(conn->ncp_sock, conn->packet, NCP_PACKET_SIZE, + len = ipx_recv(conn->ncp_sock, conn->packet, NCP_PACKET_SIZE, 0, 1, &err); conn->reply_size = len; return 0; @@ -513,6 +538,9 @@ ncp_connect_addr(struct ncp_conn *conn, const struct sockaddr_ipx *target, int ncp_sock, wdog_sock; long err; +#ifdef SIGNATURES + int options; +#endif conn->is_connected = NOT_CONNECTED; conn->verbose = 0; @@ -582,10 +610,31 @@ ncp_connect_addr(struct ncp_conn *conn, const struct sockaddr_ipx *target, BVAL(conn->packet, 3) + (BVAL(conn->packet, 5) << 8); conn->is_connected = CONN_TEMPORARY; - if ((ncp_negotiate_buffersize(conn, 1024, - &(conn->i.buffer_size)) != 0) +#ifdef SIGNATURES + conn->sign_wanted = 0; + conn->sign_active = 0; + + if ((err = ncp_negotiate_size_and_options(conn, + NCP_DEFAULT_BUFSIZE, in_options, + &(conn->i.buffer_size), &options)) == 0) + { + if ((options & 2) != (in_options & 2)) + { + if ((err = ncp_negotiate_size_and_options(conn, + NCP_DEFAULT_BUFSIZE, options & 2, &(conn->i.buffer_size), + &options)) != 0) + options = 0; + } + if (options & 2) + conn->sign_wanted = 1; + } + else +#endif + err = ncp_negotiate_buffersize(conn, NCP_DEFAULT_BUFSIZE, + &(conn->i.buffer_size)); + if ((err != 0) || (conn->i.buffer_size < 512) - || (conn->i.buffer_size > 1024)) + || (conn->i.buffer_size > NCP_DEFAULT_BUFSIZE)) { ncp_do_close(conn); return -1; @@ -693,12 +742,33 @@ ncp_open_temporary(struct ncp_conn *conn, if (strlen(spec->user) != 0) { +#ifdef NDS_SUPPORT + if (!nds_get_tree_name(conn, NULL, 0)) + { + if ((err = nds_login_auth(conn, spec->user, + spec->password))) + { + if ((err != NCPL_ET_REQUEST_ERROR) || + (conn->completion != NDS_GRACE_PERIOD)) + { + ncp_do_close(conn); + return EACCES; + } + fprintf(stderr, "Your password has expired\n"); + } + } + else + { +#endif if (ncp_login_object(conn, spec->user, spec->login_type, spec->password) != 0) { ncp_do_close(conn); return EACCES; } +#ifdef NDS_SUPPORT + } +#endif strcpy(conn->user, spec->user); } return 0; @@ -755,6 +825,16 @@ ncp_find_permanent(const struct ncp_conn_spec *spec) errno = (result == NULL) ? ENOENT : 0; return result; } +#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 static int ncp_open_permanent(struct ncp_conn *conn, @@ -792,6 +872,9 @@ ncp_open_permanent(struct ncp_conn *conn, } strcpy(conn->mount_point, mount_point); conn->is_connected = CONN_PERMANENT; +#ifdef SIGNATURES + ncp_sign_init_perm(conn); +#endif return 0; } @@ -865,6 +948,9 @@ ncp_open_mount(const char *mount_point, long *err) *err = NCPL_ET_NO_NCPFS_FILE; return NULL; } +#ifdef SIGNATURES + ncp_sign_init_perm(result); +#endif return result; } @@ -937,6 +1023,17 @@ ncp_close(struct ncp_conn *conn) return 0; } +int +ncp_get_mount_uid(int fid, uid_t* uid) { + __kernel_uid_t k_uid; + int err; + + err = ioctl(fid, NCP_IOC_GETMOUNTUID, &k_uid); + if (err) return err; + *uid = k_uid; + return 0; +} + struct ncp_conn_ent * ncp_get_conn_ent(FILE * filep) { @@ -985,7 +1082,7 @@ ncp_get_conn_ent(FILE * filep) { continue; } - if (ioctl(fid, NCP_IOC_GETMOUNTUID, &entry.uid) != 0) + if (ncp_get_mount_uid(fid, &entry.uid) != 0) { close(fid); continue; @@ -1568,6 +1665,30 @@ ncp_negotiate_buffersize(struct ncp_conn *conn, return 0; } +#ifdef SIGNATURES +static long + ncp_negotiate_size_and_options(struct ncp_conn *conn, + int size, int options, + int *ret_size, int *ret_options) +{ + long result; + + ncp_init_request(conn); + ncp_add_word_hl(conn, size); + ncp_add_byte(conn, options); + + if ((result = ncp_request(conn, 0x61)) != 0) + { + ncp_unlock_conn(conn); + return result; + } + *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, @@ -1746,6 +1867,34 @@ ncp_send_broadcast(struct ncp_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; + + i = strlen(message); + if (i > 255) + { + return NCPL_ET_MSG_TOO_LONG; + } + if (conns > 350) /* max pkt len ~ 1KB */ + /* maybe do it by handshaked length ? */ + return NCPL_ET_MSG_TOO_LONG; + + 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 */ @@ -2102,6 +2251,15 @@ ncp_login_encrypted(struct ncp_conn *conn, result = ncp_request(conn, 23); ncp_unlock_conn(conn); +#ifdef SIGNATURES + if ((result == 0) || ((result == NCPL_ET_REQUEST_ERROR) && + (conn->completion == NCP_GRACE_PERIOD))) + { + memcpy(buf + 16, key, 8); + sign_init(buf, buf); + result = ncp_sign_start(conn, buf); + } +#endif return result; } @@ -3096,6 +3254,143 @@ ncp_abort_servicing_job(struct ncp_conn *conn, __u32 queue_id, return result; } +long +ncp_get_queue_length(struct ncp_conn *conn, + __u32 queue_id, + __u32 *queue_length) +{ + long result=-EINVAL; + + ncp_init_request_s(conn, 125); + ncp_add_dword_hl(conn, queue_id); + + if ((result = ncp_request(conn, 23)) != 0) + goto out; + + if (conn->ncp_reply_size < 12) + { + ncp_printf("ncp_reply_size %d < 12\n", + conn->ncp_reply_size); + result=-EINVAL; + goto out; + } + + if (ncp_reply_dword_hl(conn,0) != queue_id) + { + printf("Ouch! Server didn't reply with same queue id in ncp_get_queue_length!\n"); + result=-EINVAL; + } + else + *queue_length = ncp_reply_dword_lh(conn,8); + + out: + ncp_unlock_conn(conn); + return result; +} + +long +ncp_get_queue_job_ids(struct ncp_conn *conn, + __u32 queue_id, + __u32 queue_section, + __u32 *length1, + __u32 *length2, + __u32 ids[]) +{ + long result; + + ncp_init_request_s(conn,129); + ncp_add_dword_hl(conn, queue_id); + ncp_add_dword_lh(conn, queue_section); + + if ((result = ncp_request(conn, 23)) != 0) + goto out; + + if (conn->ncp_reply_size < 8) + { + ncp_printf("ncp_reply_size %d < 8\n", + conn->ncp_reply_size); + result=-EINVAL; + goto out; + } + + *length2 = ncp_reply_dword_lh(conn,4); + if (conn->ncp_reply_size < 8 + 4*(*length2)) + { + ncp_printf("ncp_reply_size %d < %d\n", + conn->ncp_reply_size, 8+4*(*length2)); + result=-EINVAL; + goto out; + } + if (ids) { + int count = min(*length1, *length2)*sizeof(__u32); + int pos; + + for (pos=0; posncp_reply_size < sizeof(struct nw_queue_job_entry)) + { + ncp_printf("ncp_reply_size %d < %d\n", + conn->ncp_reply_size,sizeof(struct nw_queue_job_entry)); + result=-EINVAL; + } + else + memcpy(jobdata,ncp_reply_data(conn,0), sizeof(struct nw_queue_job_entry)); + +out: + ncp_unlock_conn(conn); + return result; +} + +long +NWRemoveJobFromQueue2 +( + struct ncp_conn* conn, + __u32 queueID, + __u32 jobNumber +) { + long result; + + ncp_init_request_s(conn, 0x80); + ncp_add_dword_hl(conn, queueID); + ncp_add_dword_lh(conn, jobNumber); + result = ncp_request(conn, 0x17); + if (result) { + if (result == NCPL_ET_REQUEST_ERROR) { + result = 0x8900 | conn->completion; + } else { + result = 0x88FF; + } + } else { + /* no output */ + } + ncp_unlock_conn(conn); + return result; +} + static int ncp_do_read(struct ncp_conn *conn, const char *file_id, __u32 offset, __u16 to_read, @@ -3243,12 +3538,16 @@ ncp_get_broadcast_message(struct ncp_conn *conn, char message[256]) long result; int length; - ncp_init_request_s(conn, 1); - - if ((result = ncp_request(conn, 21)) != 0) + ncp_init_request_s(conn, 0x0B); + if ((result = ncp_request(conn, 0x15)) != 0) { ncp_unlock_conn(conn); - return result; + 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; @@ -3325,3 +3624,180 @@ ncp_add_trustee_set(struct ncp_conn *conn, ncp_unlock_conn(conn); return result; } +#ifdef SIGNATURES +long +ncp_sign_start(struct ncp_conn *conn, const char *sign_root) +{ + char init_last[16]={0x01,0x23,0x45,0x67,0x89,0xab,0xcd,0xef, + 0xfe,0xdc,0xba,0x98,0x76,0x54,0x32,0x10}; + struct ncp_sign_init sign_init; + + if (conn->sign_wanted) + { + memcpy(sign_init.sign_root, sign_root, 8); + memcpy(sign_init.sign_last, init_last, 16); + conn->sign_active = 1; + if (conn->is_connected == CONN_PERMANENT) + { + if (ioctl(conn->mount_fid, NCP_IOC_SIGN_INIT, + &sign_init)) + return NCPL_ET_SIGNATURE_FAILED; + } + else + { + memcpy(conn->sign_root, sign_init.sign_root, 8); + memcpy(conn->sign_last, sign_init.sign_last, 16); + } + + } + return 0; +} +#endif +#ifdef NDS_SUPPORT +long +ncp_send_nds_frag(struct ncp_conn *conn, + int ndsverb, + char *inbuf, int inbuflen, + char *outbuf, int outbufsize, int *outbuflen) +{ + long result; + int sizeleft, i; + int maxdatasize = 514; + int first = 1; + int fraghnd = -1; + + 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); + return result; + } + if (inbuflen) + { + if ((ncp_reply_dword_lh(conn, 0) != 4) || + ((fraghnd = ncp_reply_dword_lh(conn, 4)) == -1)) + result = NCPL_ET_REPLY_FORMAT; + ncp_unlock_conn(conn); + if (result) return result; + } + } while (inbuflen); + i = ncp_reply_dword_lh(conn, 0) - 8; + if ((i < 0) || (ncp_reply_dword_lh(conn, 4) != -1)) + { + ncp_unlock_conn(conn); + return NCPL_ET_REPLY_FORMAT; + } + if (i > outbufsize) + { + ncp_unlock_conn(conn); + return NCPL_ET_REPLY_TOO_LARGE; + } + if (outbuf) + { + memcpy(outbuf, ncp_reply_data(conn, 12), i); + if (outbuflen) *outbuflen = i; + } + if ((conn->completion = ncp_reply_dword_lh(conn, 8))) + result = NCPL_ET_REQUEST_ERROR; + ncp_unlock_conn(conn); + return result; +} + +long +ncp_send_nds(struct ncp_conn *conn, int fn, + char *data_in, int data_in_len, + char *data_out, int data_out_max, int *data_out_len) +{ + int 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) +{ + long err; + + ncp_init_request_s(conn, 0x1d); + ncp_add_dword_lh(conn, new_state); + err = ncp_request(conn, 0x17); + ncp_unlock_conn(conn); + return err; +} + +struct ncp_conn * +ncp_open_addr(struct sockaddr_ipx *target, long *err) +{ + struct ncp_conn *conn; + FILE *p; + char buf[40]; + + sprintf(buf, "nwsfind -a %08x:%02x%02x%02x%02x%02x%02x:%04x", + (u_int32_t)ntohl(target->sipx_network), + (unsigned char)target->sipx_node[0], + (unsigned char)target->sipx_node[1], + (unsigned char)target->sipx_node[2], + (unsigned char)target->sipx_node[3], + (unsigned char)target->sipx_node[4], + (unsigned char)target->sipx_node[5], + ntohs(target->sipx_port)); + if (!(p = popen(buf, "r"))) { + *err = errno; + return NULL; + } + fgets(buf, sizeof(buf), p); + if (pclose(p)) { + *err = EHOSTUNREACH; + return NULL; + } + if (!(conn = malloc(sizeof(struct ncp_conn)))) + { + *err = ENOMEM; + return NULL; + } + memzero(*conn); + if ((*err = ncp_connect_addr(conn, target, 1))) + { + free(conn); + return NULL; + } + return conn; +} +#endif diff --git a/lib/ncplib_err.et b/lib/ncplib_err.et index 2eee6e3..233ae5d 100644 --- a/lib/ncplib_err.et +++ b/lib/ncplib_err.et @@ -42,4 +42,16 @@ ec NCPL_ET_NO_IPX, ec NCPL_ET_NO_NCPFS_FILE, "The file is probably not on a ncpfs mounted directory" -end \ No newline at end of file +ec NCPL_ET_REPLY_FORMAT, + "The reply packet is not in the expected format" + +ec NCPL_ET_REPLY_TOO_LARGE, + "The reply packet/message is too large for the allocated buffer" + +ec NCPL_ET_SIGNATURE_FAILED, + "Packet signature initializing failed" + +ec NCPL_ET_TRANSPORT_UNKNOWN, + "Unknown transport type" + +end diff --git a/lib/ncpsign.c b/lib/ncpsign.c new file mode 100644 index 0000000..f67b2bf --- /dev/null +++ b/lib/ncpsign.c @@ -0,0 +1,103 @@ +#ifdef SIGNATURES +/* + * ncpsign.c + * + * Arne de Bruijn (arne@knoware.nl), 1997 + * + */ + +#include +#include "ncplib.h" +#include "ncpsign.h" + +#define rol32(i,c) (((((i)&0xffffffff)<>(32-c))) +/* i386: 32-bit, little endian, handles mis-alignment */ +#ifdef __i386__ +#define GET_LE32(p) (*(int *)(p)) +#define PUT_LE32(p,v) { *(int *)(p)=v; } +#else +#define GET_LE32(p) DVAL_LH(p,0) +#define PUT_LE32(p,v) DSET_LH(p,0,v) +#endif + +#define min(a,b) ((a)<(b)?(a):(b)) + +static void nwsign(char *r_data1, char *r_data2, char *outdata) { + int i; + unsigned int w0,w1,w2,w3; + static int rbit[4]={0, 2, 1, 3}; +#ifdef __i386__ + unsigned int *data2=(int *)r_data2; +#else + unsigned int data2[16]; + for (i=0;i<16;i++) + data2[i]=GET_LE32(r_data2+(i<<2)); +#endif + w0=GET_LE32(r_data1); + w1=GET_LE32(r_data1+4); + w2=GET_LE32(r_data1+8); + w3=GET_LE32(r_data1+12); + for (i=0;i<16;i+=4) { + w0=rol32(w0 + ((w1 & w2) | ((~w1) & w3)) + data2[i+0],3); + w3=rol32(w3 + ((w0 & w1) | ((~w0) & w2)) + data2[i+1],7); + w2=rol32(w2 + ((w3 & w0) | ((~w3) & w1)) + data2[i+2],11); + w1=rol32(w1 + ((w2 & w3) | ((~w2) & w0)) + data2[i+3],19); + } + for (i=0;i<4;i++) { + w0=rol32(w0 + (((w2 | w3) & w1) | (w2 & w3)) + 0x5a827999 + data2[i+0],3); + w3=rol32(w3 + (((w1 | w2) & w0) | (w1 & w2)) + 0x5a827999 + data2[i+4],5); + w2=rol32(w2 + (((w0 | w1) & w3) | (w0 & w1)) + 0x5a827999 + data2[i+8],9); + w1=rol32(w1 + (((w3 | w0) & w2) | (w3 & w0)) + 0x5a827999 + data2[i+12],13); + } + for (i=0;i<4;i++) { + w0=rol32(w0 + ((w1 ^ w2) ^ w3) + 0x6ed9eba1 + data2[rbit[i]+0],3); + w3=rol32(w3 + ((w0 ^ w1) ^ w2) + 0x6ed9eba1 + data2[rbit[i]+8],9); + w2=rol32(w2 + ((w3 ^ w0) ^ w1) + 0x6ed9eba1 + data2[rbit[i]+4],11); + w1=rol32(w1 + ((w2 ^ w3) ^ w0) + 0x6ed9eba1 + data2[rbit[i]+12],15); + } + PUT_LE32(outdata,(w0+GET_LE32(r_data1)) & 0xffffffff); + PUT_LE32(outdata+4,(w1+GET_LE32(r_data1+4)) & 0xffffffff); + PUT_LE32(outdata+8,(w2+GET_LE32(r_data1+8)) & 0xffffffff); + PUT_LE32(outdata+12,(w3+GET_LE32(r_data1+12)) & 0xffffffff); +} + +/* + * Initialize packet signatures + * The first 16 bytes of logindata are the shuffled password, + * the last 8 bytes the encryption key as received from the server. + */ +void sign_init(const char *logindata, char *sign_root) { + char initlast[16]={0x01,0x23,0x45,0x67,0x89,0xab,0xcd,0xef, + 0xfe,0xdc,0xba,0x98,0x76,0x54,0x32,0x10}; + char *initdata="Authorized NetWare Client"; + char msg[64]; + char hash[16]; + + memset(msg, 0, 64); + memcpy(msg, logindata, 24); + memcpy(msg + 24, initdata, 25); + nwsign(initlast, msg, hash); + + memcpy(sign_root, hash, 8); +} + +/* + * Make a signature for the current packet and add it at the end of the + * packet. + */ +void sign_packet(struct ncp_conn *conn, int *size) { + char data[64]; + + memset(data,0,64); + memcpy(data,conn->sign_root,8); + PUT_LE32(data+8,(*size)); + memcpy(data+12,conn->packet+sizeof(struct ncp_request_header)-1, + min((*size)-sizeof(struct ncp_request_header)+1,52)); + + nwsign(conn->sign_last,data,conn->sign_last); + + memcpy(conn->packet+(*size),conn->sign_last,8); + (*size)+=8; +} +#endif diff --git a/lib/ndscrypt.c b/lib/ndscrypt.c new file mode 100644 index 0000000..2e3b45d --- /dev/null +++ b/lib/ndscrypt.c @@ -0,0 +1,295 @@ +/* + NDS client for ncpfs + Copyright (C) 1997 Arne de Bruijn + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include +#include "ndscrypt.h" + +static unsigned int rol16(unsigned int i, int c) { + return ((i << c) & 65535) | ((unsigned int)(i & 65535) >> (16 - c)); +} +static unsigned int ror16(unsigned int i, int c) { + return ((unsigned int)(i & 65535) >> c) | ((i << (16 - c)) & 65535); +} + +char nwcryptdata[256]={ + 0xD9,0x78,0xF9,0xC4,0x19,0xDD,0xB5,0xED,0x28,0xE9,0xFD,0x79, + 0x4A,0xA0,0xD8,0x9D,0xC6,0x7E,0x37,0x83,0x2B,0x76,0x53,0x8E, + 0x62,0x4C,0x64,0x88,0x44,0x8B,0xFB,0xA2,0x17,0x9A,0x59,0xF5, + 0x87,0xB3,0x4F,0x13,0x61,0x45,0x6D,0x8D,0x09,0x81,0x7D,0x32, + 0xBD,0x8F,0x40,0xEB,0x86,0xB7,0x7B,0x0B,0xF0,0x95,0x21,0x22, + 0x5C,0x6B,0x4E,0x82,0x54,0xD6,0x65,0x93,0xCE,0x60,0xB2,0x1C, + 0x73,0x56,0xC0,0x14,0xA7,0x8C,0xF1,0xDC,0x12,0x75,0xCA,0x1F, + 0x3B,0xBE,0xE4,0xD1,0x42,0x3D,0xD4,0x30,0xA3,0x3C,0xB6,0x26, + 0x6F,0xBF,0x0E,0xDA,0x46,0x69,0x07,0x57,0x27,0xF2,0x1D,0x9B, + 0xBC,0x94,0x43,0x03,0xF8,0x11,0xC7,0xF6,0x90,0xEF,0x3E,0xE7, + 0x06,0xC3,0xD5,0x2F,0xC8,0x66,0x1E,0xD7,0x08,0xE8,0xEA,0xDE, + 0x80,0x52,0xEE,0xF7,0x84,0xAA,0x72,0xAC,0x35,0x4D,0x6A,0x2A, + 0x96,0x1A,0xD2,0x71,0x5A,0x15,0x49,0x74,0x4B,0x9F,0xD0,0x5E, + 0x04,0x18,0xA4,0xEC,0xC2,0xE0,0x41,0x6E,0x0F,0x51,0xCB,0xCC, + 0x24,0x91,0xAF,0x50,0xA1,0xF4,0x70,0x39,0x99,0x7C,0x3A,0x85, + 0x23,0xB8,0xB4,0x7A,0xFC,0x02,0x36,0x5B,0x25,0x55,0x97,0x31, + 0x2D,0x5D,0xFA,0x98,0xE3,0x8A,0x92,0xAE,0x05,0xDF,0x29,0x10, + 0x67,0x6C,0xBA,0xC9,0xD3,0x00,0xE6,0xCF,0xE1,0x9E,0xA8,0x2C, + 0x63,0x16,0x01,0x3F,0x58,0xE2,0x89,0xA9,0x0D,0x38,0x34,0x1B, + 0xAB,0x33,0xFF,0xB0,0xBB,0x48,0x0C,0x5F,0xB9,0xB1,0xCD,0x2E, + 0xC5,0xF3,0xDB,0x47,0xE5,0xA5,0x9C,0x77,0x0A,0xA6,0x20,0x68, + 0xFE,0x7F,0xC1,0xAD}; + +#if 0 +char shuffle_table[32]= + {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}; + +char shuffle_table2[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}; +#endif + +char nwhashdata[256] = + {0xBD,0x56,0xEA,0xF2,0xA2,0xF1,0xAC,0x2A,0xB0,0x93,0xD1,0x9C, + 0x1B,0x33,0xFD,0xD0,0x30,0x04,0xB6,0xDC,0x7D,0xDF,0x32,0x4B, + 0xF7,0xCB,0x45,0x9B,0x31,0xBB,0x21,0x5A,0x41,0x9F,0xE1,0xD9, + 0x4A,0x4D,0x9E,0xDA,0xA0,0x68,0x2C,0xC3,0x27,0x5F,0x80,0x36, + 0x3E,0xEE,0xFB,0x95,0x1A,0xFE,0xCE,0xA8,0x34,0xA9,0x13,0xF0, + 0xA6,0x3F,0xD8,0x0C,0x78,0x24,0xAF,0x23,0x52,0xC1,0x67,0x17, + 0xF5,0x66,0x90,0xE7,0xE8,0x07,0xB8,0x60,0x48,0xE6,0x1E,0x53, + 0xF3,0x92,0xA4,0x72,0x8C,0x08,0x15,0x6E,0x86,0x00,0x84,0xFA, + 0xF4,0x7F,0x8A,0x42,0x19,0xF6,0xDB,0xCD,0x14,0x8D,0x50,0x12, + 0xBA,0x3C,0x06,0x4E,0xEC,0xB3,0x35,0x11,0xA1,0x88,0x8E,0x2B, + 0x94,0x99,0xB7,0x71,0x74,0xD3,0xE4,0xBF,0x3A,0xDE,0x96,0x0E, + 0xBC,0x0A,0xED,0x77,0xFC,0x37,0x6B,0x03,0x79,0x89,0x62,0xC6, + 0xD7,0xC0,0xD2,0x7C,0x6A,0x8B,0x22,0xA3,0x5B,0x05,0x5D,0x02, + 0x75,0xD5,0x61,0xE3,0x18,0x8F,0x55,0x51,0xAD,0x1F,0x0B,0x5E, + 0x85,0xE5,0xC2,0x57,0x63,0xCA,0x3D,0x6C,0xB4,0xC5,0xCC,0x70, + 0xB2,0x91,0x59,0x0D,0x47,0x20,0xC8,0x4F,0x58,0xE0,0x01,0xE2, + 0x16,0x38,0xC4,0x6F,0x3B,0x0F,0x65,0x46,0xBE,0x7E,0x2D,0x7B, + 0x82,0xF9,0x40,0xB5,0x1D,0x73,0xF8,0xEB,0x26,0xC7,0x87,0x97, + 0x25,0x54,0xB1,0x28,0xAA,0x98,0x9D,0xA5,0x64,0x6D,0x7A,0xD4, + 0x10,0x81,0x44,0xEF,0x49,0xD6,0xAE,0x2E,0xDD,0x76,0x5C,0x2F, + 0xA7,0x1C,0xC9,0x09,0x69,0x9A,0x83,0xCF,0x29,0x39,0xB9,0xE9, + 0x4C,0xFF,0x43,0xAB}; + + +void nwencrypt(const unsigned short *cryptbuf, const char *in, char *out) { + int i, j; + register unsigned int i1, i2, i3, i4; + unsigned short *p; + + i1 = *((unsigned short *)in); + i2 = *((unsigned short *)in + 1); + i3 = *((unsigned short *)in + 2); + i4 = *((unsigned short *)in + 3); + p = (unsigned short *)cryptbuf; + for (j = 3; j; j--) { + for (i = (j == 2) ? 6 : 5; i; i--) { + i1 = rol16(i1 + (*p++) + (i4 & i3) + (~i4 & i2), 1); + i2 = rol16(i2 + (*p++) + (i1 & i4) + (~i1 & i3), 2); + i3 = rol16(i3 + (*p++) + (i2 & i1) + (~i2 & i4), 3); + i4 = rol16(i4 + (*p++) + (i3 & i2) + (~i3 & i1), 5); + } + if (j > 1) { + i1 += cryptbuf[i4 & 63]; + i2 += cryptbuf[i1 & 63]; + i3 += cryptbuf[i2 & 63]; + i4 += cryptbuf[i3 & 63]; + } + } + *((unsigned short *)out) = i1; + *((unsigned short *)out + 1) = i2; + *((unsigned short *)out + 2) = i3; + *((unsigned short *)out + 3) = i4; +} + +void nwdecrypt(const unsigned short *cryptbuf, const char *in, char *out) { + int i, j; + unsigned short *p; + register unsigned int i1, i2, i3, i4; + + i1 = *((unsigned short *)in); + i2 = *((unsigned short *)in + 1); + i3 = *((unsigned short *)in + 2); + i4 = *((unsigned short *)in + 3); + p = (unsigned short *)cryptbuf + 64; + for (j = 3; j; j--) { + for (i = (j == 2) ? 6 : 5; i; i--) { + i4 = ror16(i4, 5) - (~i3 & i1) - (i3 & i2) - (*--p); + i3 = ror16(i3, 3) - (~i2 & i4) - (i2 & i1) - (*--p); + i2 = ror16(i2, 2) - (~i1 & i3) - (i1 & i4) - (*--p); + i1 = ror16(i1, 1) - (~i4 & i2) - (i4 & i3) - (*--p); + } + if (j > 1) { + i4 -= cryptbuf[i3 & 63]; + i3 -= cryptbuf[i2 & 63]; + i2 -= cryptbuf[i1 & 63]; + i1 -= cryptbuf[i4 & 63]; + } + } + *((unsigned short *)out) = i1; + *((unsigned short *)out + 1) = i2; + *((unsigned short *)out + 2) = i3; + *((unsigned short *)out + 3) = i4; +} + +void nwcryptinit(unsigned short *scryptbuf, const char *key) { + int i; + unsigned char cryptbuf[128], *p; + + memcpy(cryptbuf, key, 8); + for (i = 0; i < 120; i++) + cryptbuf[i + 8] = + nwcryptdata[(unsigned char)(cryptbuf[i] + cryptbuf[i + 7]) & 255]; + cryptbuf[128 - 8] = nwcryptdata[(unsigned char)cryptbuf[128 - 8] & 255]; + for (i = 127 - 8; i >= 0; i--) + cryptbuf[i] = nwcryptdata[(unsigned char)cryptbuf[i + 1] ^ + (unsigned char)cryptbuf[i + 8]]; + for (i = 0, p = cryptbuf; i < 64; i++, p += 2) + scryptbuf[i] = (*p) | (*(p+1)) << 8; +} + +void nwencryptblock(const char *cryptkey, const char *buf, int buflen, + char *outbuf) { + int i; + char nhash[8]; + unsigned short cryptbuf[64]; + + nwcryptinit(cryptbuf, cryptkey); + memset(nhash, 0, 8); + while (buflen >= 8) { + for (i = 0; i < 8; i++, buf++) + nhash[i] ^= *buf; + nwencrypt(cryptbuf, nhash, nhash); + memcpy(outbuf, nhash, 8); + outbuf += 8; + buflen -= 8; + } + memset(cryptbuf, 0, sizeof(cryptbuf)); +} + +void nwdecryptblock(const char *cryptkey, const char *buf, int buflen, + char *outbuf) { + int i; + char nhash[16], *p; + unsigned short cryptbuf[64]; + + nwcryptinit(cryptbuf, cryptkey); + memset(nhash, 0, 16); + p = nhash; + while (buflen >= 8) { + memcpy(p, buf, 8); + p = nhash + 8 - (p - nhash); + nwdecrypt(cryptbuf, buf, outbuf); + for (i = 0; i < 8; i++, outbuf++) + *outbuf ^= p[i]; + buf += 8; + buflen -= 8; + } + memset(cryptbuf, 0, sizeof(cryptbuf)); +} + +void nwhash1(char *hash, int hashlen, const char *data, int datalen) { + unsigned char *hp, *hp1, *dp, *hend, c; + + hp1 = (hp = (unsigned char *)hash) + 1; + hend = hp + hashlen; + dp = (unsigned char *)data; + while (datalen--) { + *hp = nwhashdata[*hp1 ^ *hp] ^ *dp++; + hp = hp1++; + if (hp1 == hend) + hp1 = (unsigned char *)hash; + } + while (hp-- > (unsigned char *)hash) { + hp1 = (unsigned char *)hash; + c = *hp1++; + while (*(hp1 - 1) = *hp1, ++hp1 < (unsigned char *)hash + hashlen); + *(hp1 - 1) = c; + } +} + +void nwhash2(char *hashbuf, char c) { + int i, j; + char *p = hashbuf + hashbuf[0x40]; + + p[0x20] = p[0x00] ^ (p[0x10] = c); + hashbuf[0x41] = (p[0x30] ^= nwhashdata[(unsigned char)(c ^ hashbuf[0x41])]); + if (!(hashbuf[0x40] = (hashbuf[0x40] + 1) & 15)) { + c = 0; + for (i = 18; i; i--) + for (j = 48, p = hashbuf; j; j--) + c = (*(p++) ^= nwhashdata[((unsigned char)c + j) & 255]); + } +} + +void nwhash2block(char *hashbuf, const char *data, int datalen) { + while (datalen--) + nwhash2(hashbuf, *data++); +} + +void nwhash2end(char *hashbuf) { + int i, j; + + for(j = i = 16 - hashbuf[0x40]; j; j--) + nwhash2(hashbuf, i); + for(i = 0x30; i < 0x40; i++) + nwhash2(hashbuf, hashbuf[i]); +} + +#if 0 +void shuffle(const char *objid, const char *pwd, char *out) { + unsigned char temp[32]; + int i, j, k; + i = strlen(pwd); + memset(temp, 0, 32); + for (j = 0; j < i; j++) + temp[j & 31] ^= pwd[j]; + if (i) + for (j = i; j < 32; j += i) { + temp[j++] = shuffle_table[j]; + k = 32 - j; + memcpy(temp + j, pwd, (k > i) ? i : k); + } + for (i = 0; i < 32; i++) + temp[i] ^= objid[i & 3]; + j = 0; + for (k = 0; k < 2; k++) + for (i = 0; i < 32; i++) + (char)j += temp[i] = (temp[i] + j) ^ + (temp[(i + j) & 31] - shuffle_table[i]); + for (i = 0; i < 16; i++) + out[i] = shuffle_table2[temp[i * 2]] | + (shuffle_table2[temp[i * 2 + 1]] << 4); +} +#endif + diff --git a/lib/ndscrypt.h b/lib/ndscrypt.h new file mode 100644 index 0000000..07fc788 --- /dev/null +++ b/lib/ndscrypt.h @@ -0,0 +1,47 @@ +/* + NDS client for ncpfs + Copyright (C) 1997 Arne de Bruijn + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifndef _NDSCRYPT_H +#define _NDSCRYPT_H + +#include + +void nwencrypt(const unsigned short *cryptbuf, const char *in, char *out); +void nwdecrypt(const unsigned short *cryptbuf, const char *in, char *out); +void nwcryptinit(unsigned short *scryptbuf, const char *key); +void nwencryptblock(const char *cryptkey, const char *buf, int buflen, + char *outbuf); +void nwdecryptblock(const char *cryptkey, const char *buf, int buflen, + char *outbuf); + +#define nwhash1init(hash, hashlen) memset(hash, 0, hashlen) +void nwhash1(char *hash, int hashlen, const char *data, int datalen); + +#define nwhash2init(hashbuf) memset(hashbuf, 0, 0x42) +void nwhash2(char *hashbuf, char c); +void nwhash2block(char *hashbuf, const char *data, int datalen); +void nwhash2end(char *hashbuf); + +#if 0 +void shuffle(const char *objid, const char *pwd, char *out); +#else +void shuffle(const char *objid, const char *pwd, int buflen, char *out); +#endif + +#endif /* _NDSCRYPT_H */ diff --git a/lib/ndslib.c b/lib/ndslib.c new file mode 100644 index 0000000..1cd9bc2 --- /dev/null +++ b/lib/ndslib.c @@ -0,0 +1,1235 @@ +/* + NDS client for ncpfs + Copyright (C) 1997 Arne de Bruijn + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#define RANDBUF /* if defined: read random data once from /dev/urandom */ +/*#define ERR_MSG*/ /* if defined: show error messages in nds_login_auth */ + +#include +#include +#include +#ifdef ERR_MSG +#include +#endif +#include +#include +#ifdef RANDBUF +#include +#endif +#include "ncplib.h" +#include "ncplib_err.h" +#include "ndslib.h" +#include "ndscrypt.h" + +#define USUALS +typedef __u32 word32; +typedef __u16 word16; +typedef unsigned char boolean; +#include "mpilib.h" +#include +#include +#include "kernel/ipx.h" +#include +#include "ndslib.h" + +static int buf_get_dword_lh(char **buf, char *bufend, unsigned int *v) { + if ((*buf) + 4 <= bufend) { + if (v) + *v = (unsigned char)(**buf) | (unsigned char)(*(*buf + 1)) << 8 | + (unsigned char)(*(*buf + 2)) << 16 | + (unsigned char)(*(*buf + 3)) << 24; + (*buf) += 4; + return 0; + } else + return -1; +} + +static int buf_get_lbuf(char **buf, char *bufend, char *out, int outmax, + int *outlen) { + int i, j; + + if ((!buf_get_dword_lh(buf, bufend, &i)) && (*buf + i <= bufend)) { + j = i; + if (out) { + if (j > outmax) j = outmax; + memcpy(out, *buf, j); + } + if (outlen) *outlen = j; + *buf += (i + 3) & (~3); + return 0; + } else + return -1; +} + +static int buf_put_word_lh2(char **buf, char *bufend, unsigned int v) { + if ((*buf) + 2 <= bufend) { + *((*buf)++) = v & 255; + *((*buf)++) = v >> 8; + return 0; + } else + return -1; +} + +static int buf_put_dword_lh(char **buf, char *bufend, unsigned long v) { + if ((buf) && ((*buf) + 4 <= bufend)) { + *((*buf)++) = v & 255; + *((*buf)++) = (v >> 8) & 255; + *((*buf)++) = (v >> 16) & 255; + *((*buf)++) = v >> 24; + return 0; + } else + return -1; +} + +static int buf_put_dword_hl(char **buf, char *bufend, unsigned long v) { + if ((*buf) + 4 <= bufend) { + *((*buf)++) = v >> 24; + *((*buf)++) = (v >> 16) & 255; + *((*buf)++) = (v >> 8) & 255; + *((*buf)++) = v & 255; + return 0; + } else + return -1; +} + +static int buf_put_lbuf(char **buf, char *bufend, const char *databuf, + int buflen) { + if ((databuf) && (!buf_put_dword_lh(buf, bufend, buflen)) && + (*buf + buflen <= bufend)) { + memcpy(*buf, databuf, buflen); + (*buf) += buflen; + while (buflen++ & 3) + *(*buf)++ = 0; + return 0; + } else + return -1; +} + +static int buf_put_buf(char **buf, char *bufend, const char *databuf, + int buflen) { + if ((databuf) && (*buf + buflen <= bufend)) { + memcpy(*buf, databuf, buflen); + (*buf) += buflen; + while (buflen++ & 3) + *(*buf)++ = 0; + return 0; + } else + return -1; +} + +static int buf_put_unistr(char **buf, char *bufend, const uni_char *str) { + int i = (strlen_u(str) + 1) * 2; + + if ((str) && (!buf_put_dword_lh(buf, bufend, i)) && + (*buf + i <= bufend)) { + memcpy(*buf, (char *)str, i); + (*buf) += i; + while (i++ & 3) + *(*buf)++ = 0; + return 0; + } else + return -1; +} + +static int buf_get_dword_hl(char **buf, char *bufend, unsigned int *v) { + if ((*buf) + 4 <= bufend) { + if (v) { + *v = (unsigned char)(**buf) << 24 | + (unsigned char)(*(*buf + 1)) << 16 | + (unsigned char)(*(*buf + 2)) << 8 | + (unsigned char)(*(*buf + 3)); + } + (*buf) += 4; + return 0; + } else + return -1; +} + +static int buf_get_word_lh(char **buf, char *bufend, unsigned int *v) { + if ((v) && ((*buf) + 2 <= bufend)) { + *v = (unsigned char)(**buf) | (unsigned char)(*(*buf + 1)) << 8; + (*buf) += 4; + return 0; + } else + return -1; +} + +static int buf_get_word_lh2(char **buf, char *bufend, unsigned int *v) { + if ((v) && ((*buf) + 2 <= bufend)) { + *v = (unsigned char)(**buf) | (unsigned char)(*(*buf + 1)) << 8; + (*buf) += 2; + return 0; + } else + return -1; +} + +static int buf_get_lbuf_alloc(char **buf, char *bufend, + char **outbuf, int *bufsize) { + int i, err = 0; + + if ((!buf_get_dword_lh(buf, bufend, &i)) && (*buf + i <= bufend)) { + if (outbuf) { + if (((*outbuf) = malloc(i))) + memcpy(*outbuf, *buf, i); + else + err = ENOMEM; + } + (*buf) += (i + 3) & (~3); + if (bufsize) *bufsize = i; + return err; + } else { + if (outbuf) *outbuf = NULL; + if (bufsize) *bufsize = 0; + return -1; + } +} + +static int buf_get_buf(char **buf, char *bufend, char *outbuf, int bufsize) { + if (*buf + bufsize <= bufend) { + if (outbuf) memcpy(outbuf, *buf, bufsize); + *buf += (bufsize + 3) & (~3); + return 0; + } else + return -1; +} + +int strlen_u(const uni_char *s) { + int i = 0; + while (*s++) i++; + return i; +} + +void strcpy_uc(char *d, const uni_char *s) { + while ((*d++ = *s++)); +} + +void strcpy_cu(uni_char *d, const char *s) { + while ((*d++ = *s++)); +} + +long nds_get_server_name(struct ncp_conn *conn, uni_char **server_name) { + long err; + int outlen; + char *p, *pend, *outbuf; + + if (!(outbuf = malloc(4096))) + return ENOMEM; + if (server_name) *server_name = NULL; + if ((err = ncp_send_nds_frag(conn, 53, NULL, 0, + outbuf, 4096, &outlen)) == 0) { + pend = (p = outbuf) + outlen; + if (buf_get_dword_lh(&p, pend, &outlen)) + err = NCPL_ET_REPLY_FORMAT; + else { + if (!((*server_name) = malloc(outlen))) + err = ENOMEM; + else + memcpy(*server_name, p, outlen); + } + } + free(outbuf); + return err; +} + +long nds_get_tree_name(struct ncp_conn *conn, char *name, int name_buf_len) { + char buf[128]; + int size; + long err; + char *p, *pend; + + if (!(err = ncp_send_nds(conn, 1, "\0\0\0", 3, buf, sizeof(buf), + &size))) { + p = buf + 4; + pend = buf + size; + if (buf_get_lbuf(&p, pend, name, name_buf_len, &size)) + return NCPL_ET_REPLY_FORMAT; + if (name) { + p = name + size - 1; + while ((p >= name) && (!*p)) + p--; + while ((p >= name) && (*p == '_')) + p--; + *(p + 1) = 0; + } + } + return err; +} + +/* for login */ +long nds_resolve_name(struct ncp_conn *conn, int flags, uni_char *entry_name, + int *entry_id, int *remote, struct sockaddr *serv_addr, int *addr_len) { + char *buf, *p, *pend, addr_buf[12]; + long err; + int i; + + if (!(buf = malloc(4096))) + return ENOMEM; + pend = (p = buf) + 2048; + buf_put_dword_lh(&p, pend, 0); + buf_put_dword_lh(&p, pend, flags); + buf_put_dword_lh(&p, pend, 0); + buf_put_unistr(&p, pend, entry_name); + buf_put_dword_lh(&p, pend, 1); + buf_put_dword_lh(&p, pend, 0); + buf_put_dword_lh(&p, pend, 1); + buf_put_dword_lh(&p, pend, 0); + if ((err = ncp_send_nds_frag(conn, 1, buf, p - buf, buf + 2048, 2048, + &i)) == 0) { + pend = (p = buf + 2048) + i; + if (buf_get_dword_lh(&p, pend, &i) || (i < 0) || (i > 2)) + err = NCPL_ET_REPLY_FORMAT; + else if (i == 1) { + if (remote) *remote = 0; + if (buf_get_dword_hl(&p, pend, entry_id)) + err = NCPL_ET_REPLY_FORMAT; + } else { + if (remote) *remote = 1; + if ((!serv_addr) || (!addr_len)) { + free(buf); + return 0; + } + if (buf_get_dword_hl(&p, pend, entry_id) || + buf_get_dword_lh(&p, pend, &i) || (i != 0) || + buf_get_dword_lh(&p, pend, &i) || (i == 0) || + buf_get_dword_lh(&p, pend, &i)) + err = NCPL_ET_REPLY_FORMAT; + else if (i != 0) /* no ipx? */ + err = NCPL_ET_TRANSPORT_UNKNOWN; + else if (buf_get_dword_lh(&p, pend, &i) || (i != 12) || + buf_get_buf(&p, pend, addr_buf, 12)) + err = NCPL_ET_REPLY_FORMAT; + else if (*addr_len < sizeof(struct sockaddr_ipx)) + err = EINVAL; + else { + ((struct sockaddr_ipx *)serv_addr)->sipx_family = AF_IPX; + ((struct sockaddr_ipx *)serv_addr)->sipx_type = NCP_PTYPE; + /* buf and addr both in network order */ + memcpy(&((struct sockaddr_ipx *)serv_addr)->sipx_network, + addr_buf, 4); + memcpy(((struct sockaddr_ipx *)serv_addr)->sipx_node, + addr_buf + 4, 6); + memcpy(&((struct sockaddr_ipx *)serv_addr)->sipx_port, + addr_buf + 10, 2); + *addr_len = sizeof(struct sockaddr_ipx); + } + } + } + free(buf); + return err; +} + +long nds_readentryname(struct ncp_conn *conn, int obj_id, + uni_char **name, int *namelen) { + char reqbuf[16], *p, *pend, *buf; + uni_char *p2; + long err; + int outlen; + + if (name) *name = NULL; + if (namelen) *namelen = 0; + pend = (p = reqbuf) + 16; + buf_put_dword_lh(&p, pend, 2); + buf_put_dword_lh(&p, pend, 0); + buf_put_dword_lh(&p, pend, 0x281d); + buf_put_dword_hl(&p, pend, obj_id); + if (!(buf = malloc(4096))) + return ENOMEM; + if ((err = ncp_send_nds_frag(conn, 2, reqbuf, 16, buf, 4096, &outlen))) { + free(buf); + return err; + } + pend = (p = buf) + outlen; + p += 16; + buf_get_lbuf(&p, pend, NULL, 0, NULL); + if ((buf_get_dword_lh(&p, pend, &outlen)) || + (outlen > pend - p)) { + free(buf); + return NCPL_ET_REPLY_FORMAT; + } + if (name) { + if (!(p2 = malloc(outlen))) { + free(buf); + return ENOMEM; + } + memcpy(p2, p, outlen); + *name = p2; + } + if (namelen) *namelen = outlen; + free(buf); + return 0; +} + +long nds_read(struct ncp_conn *conn, int obj_id, uni_char *propname, + char **outbuf, int *outlen) { + long err; + char *buf, *p, *pend; + int n1, n2, n3, n4, n5; + + if (outbuf) *outbuf = NULL; + if (outlen) *outlen = 0; + if (!(buf = malloc(4096))) + return ENOMEM; + pend = (p = buf) + 2048; + buf_put_dword_lh(&p, pend, 0); + buf_put_dword_lh(&p, pend, -1L); + buf_put_dword_hl(&p, pend, obj_id); + buf_put_dword_lh(&p, pend, 1); + buf_put_dword_lh(&p, pend, 0); + buf_put_dword_lh(&p, pend, 1); + buf_put_unistr(&p, pend, propname); + if (!(err = ncp_send_nds_frag(conn, 3, buf, p - buf, + buf + 2048, 2048, &n1))) { + pend = (p = (buf + 2048)) + n1; + if (!(err = buf_get_dword_lh(&p, pend, &n1)) && + !(err = buf_get_dword_lh(&p, pend, &n2)) && + !(err = buf_get_dword_lh(&p, pend, &n3)) && + !(err = buf_get_dword_lh(&p, pend, &n4)) && + !(err = buf_get_lbuf(&p, pend, NULL, 0, NULL)) && + !(err = buf_get_dword_lh(&p, pend, &n5))) { + if ((n1 != -1) || (n2 != 1) || (n3 != 1) || + (n4 != 9) || (n5 != 1)) + err = -1; + else + err = buf_get_lbuf_alloc(&p, pend, outbuf, outlen); + } + } + free(buf); + return err; +} + +#ifdef RANDBUF +#define RANDBUFSIZE 1236 /* total size of all fillrandom's for login+auth */ +char global_randbuf[RANDBUFSIZE]; +char *g_rndp = global_randbuf + RANDBUFSIZE; + +void fillrandom(char *buf, int buflen) { + int fh,i; + + do { + if (g_rndp == global_randbuf + RANDBUFSIZE) { + if ((fh = open("/dev/urandom", O_RDONLY)) >=0) { + read(fh, global_randbuf, RANDBUFSIZE); + close(fh); + } else { + g_rndp = global_randbuf; + while (g_rndp - global_randbuf < RANDBUFSIZE) + *(g_rndp++) = rand() / ((((unsigned)RAND_MAX)+255) / 256); + } + g_rndp = global_randbuf; + } + if ((i = RANDBUFSIZE - (g_rndp - global_randbuf)) > buflen) i = buflen; + memcpy(buf, g_rndp, i); + buf += i; + g_rndp += i; + buflen -= i; + } while (buflen); +} +#else +void fillrandom(char *buf, int buflen) { + int fh; + char *p; + + if (((fh = open("/dev/urandom", O_RDONLY)) >= 0) { + read(fh, buf, buflen); + close(fh); + } else { + p = buf; + while (p - buf < buflen) + *(p++) = rand() / ((((unsigned)RAND_MAX)+255) / 256); + } +} +#endif + +int countbits_l(char *buf, int bufsize) { + unsigned char b; + + while ((--bufsize) && (!buf[bufsize])); + b = (unsigned char)buf[bufsize]; + bufsize <<= 3; + while (b) { + b >>= 2; bufsize++; + } + return bufsize; +} + +void copyfill(void *outbuf, int outsize, const void *inbuf, int insize) { + if (outsize < insize) insize = outsize; + memcpy(outbuf, inbuf, insize); + memset((char *)outbuf + insize, 0, outsize - insize); +} + +uni_char c_public_key[] = {'P','u','b','l','i','c',' ','K','e','y',0}; + +char keyprefix[] = {1, 0, 0, 0, 3, 0, 1, 0}; + +int initkey(const char *key, char **keyptr, int *keylen) { /* 1=ok, 0=err */ + if (!memcmp(key, keyprefix, 8)) { + if (keylen) *keylen = (unsigned char)key[8] | \ + (unsigned char)key[9] << 8; + if (keyptr) (const char *)(*keyptr) = key + 10; + return 1; + } else + return 0; +} + +void clearkey(char *key) { + char *keyptr; + int keylen; + if (initkey(key, &keyptr, &keylen)) + memset(key, 0, keylen + 10); +} + +int findchunk(const char *keyptr, int keylen, const char *chunk, + char **chunkptr) { + const char *p; + + if ((p = keyptr)) { + while (p - keyptr < keylen) { + if ((p[0] != chunk[0]) || (p[1] != chunk[1])) + p += 4 + (unsigned char)p[2] + (unsigned char)p[3]; + else { + if (chunkptr) (const char *)(*chunkptr) = p + 4; + return (unsigned char)p[2] + (unsigned char)p[3]; + } + } + } + if (chunkptr) *chunkptr = NULL; + return 0; +} + +int checkkey(const char *key) { /* 0 - wrong key, != 0 - key ok */ + char temp[8]; + char *keyptr, *p; + int keylen; + + if ((initkey(key, &keyptr, &keylen)) && + (findchunk(keyptr, keylen, "MA", &p))) { + nwhash1init(temp, 8); + nwhash1(temp, 8, key + 10, (unsigned char)key[8] + + ((unsigned char)key[9] << 8) - 20); + return (!memcmp(p, temp, 8)); + } else + return 0; + +} + +long modexpkey(const char *s_key, char *buf, char *outbuf, int bufsize) { + char *s_keyptr; + int s_keylen, i, nbits, nblocksize; + int err = -1; + unitptr nmod, nexp, nin, nout; + char *p; + + nmod = nexp = nin = nout = NULL; + + if (!initkey(s_key, &s_keyptr, &s_keylen)) + return NCPL_ET_REPLY_FORMAT; + i = findchunk(s_keyptr, s_keylen, "NN", &p); + if (!p) + return NCPL_ET_REPLY_FORMAT; + nbits = countbits_l(p, i); + nblocksize = ((nbits + 31) & (~31)) >> 3; + if (!(nmod = malloc(nblocksize))) + return ENOMEM; + copyfill(nmod, nblocksize, p, i); + i = findchunk(s_keyptr, s_keylen, "EN", &p); + err = NCPL_ET_REPLY_FORMAT; + if (!p) goto end; + err = ENOMEM; + if (!(nexp = malloc(nblocksize))) goto end; + copyfill(nexp, nblocksize, p, i); + if (!(nin = malloc(nblocksize))) goto end; + copyfill(nin, nblocksize, buf, bufsize); + if (!(nout = malloc(nblocksize))) goto end; + set_precision(bytes2units(nblocksize)); + if (mp_modexp((unitptr) nout, (unitptr) nin, (unitptr) nexp, + (unitptr) nmod)) + err = NCPL_ET_REPLY_FORMAT; + else { + copyfill(outbuf, bufsize, nout, nblocksize); + err = 0; + } +end: + if (nout) { mp_init0(nout); free(nout); } + if (nin) { mp_init0(nin); free(nin); } + if (nexp) free(nexp); + if (nmod) free(nmod); + return err; +} + +long get_public_key(struct ncp_conn *conn, long obj_id, char **key) { + char *keybuf, *kptr; + long err; + int keylen, ofs, klen; + + if ((err = nds_read(conn, obj_id, c_public_key, &keybuf, &keylen))) { + return err; + } + ofs = (unsigned char)keybuf[10] + ((unsigned char)keybuf[11] << 8) + 0x1a; + if ((ofs > keylen) || (!initkey(keybuf + ofs, &kptr, &klen)) || + (klen + ofs > keylen) || (!checkkey(keybuf + ofs))) { + err = NCPL_ET_REPLY_FORMAT; + goto err_exit; + } + if (key) { + if (!(kptr = malloc(klen + 10))) { + err = ENOMEM; + goto err_exit; + } + memcpy(kptr, keybuf + ofs, klen + 10); + *key = kptr; + } + err = 0; +err_exit: + free(keybuf); + return err; +} + +char buf2str1[8] = {1,0,0,0,9,0,2,0}; +char buf2str2[16] = {65,0,0,0,1,0,0,0,1,0,9,0,53,0,28,0}; +char buf2str3[8] = {1,0,0,0,1,0,6,0}; +long rsa_crypt(struct ncp_conn *conn, char *data, int datalen, + long serv_id, char **outp, char *pend) { + char rand[28]; + char hashrand[8], temp[8]; + unsigned short cryptbuf[128]; + char buf2[56]; + int i; + long err; + char *s_key; + char *p; + + if ((*outp + datalen + 108) > pend) + return -1; + if ((err = get_public_key(conn, serv_id, &s_key))) + return err; + + fillrandom(rand, 28); + nwhash1init(hashrand, 8); + for (i = 10; i; i--) + nwhash1(hashrand, 8, rand, 28); + + memset(buf2 + 40, 0, 16); + buf2[0] = 11; + memcpy(buf2 + 1, rand, 28); + memset(buf2 + 29, 11, 11); + nwhash1(buf2 + 40, 5, buf2 + 1, 39); + nwhash1(buf2 + 45, 2, buf2, 45); + fillrandom(buf2 + 47, 5); + + err = modexpkey(s_key, buf2, buf2, 56); + free(s_key); + if (err) + return err; + + buf_put_dword_lh(outp, pend, datalen + 108); + buf_put_buf(outp, pend, buf2str1, sizeof(buf2str1)); + buf_put_dword_lh(outp, pend, datalen + 96); + buf_put_buf(outp, pend, buf2str2, sizeof(buf2str2)); + buf_put_buf(outp, pend, buf2, 56); + buf_put_dword_lh(outp, pend, datalen + 20); + buf_put_buf(outp, pend, buf2str3, sizeof(buf2str3)); + buf_put_dword_lh(outp, pend, (datalen + 8) | (datalen << 16)); + + memset(temp, 3, 3); + nwhash1init(temp + 3, 5); + nwhash1(temp + 3, 5, data, datalen); + nwhash1(temp + 3, 5, temp, 3); + nwencryptblock(hashrand, data, datalen, *outp); + *outp += datalen; + for (i = 0, p = *outp - 8; i < 8; i++, p++) + temp[i] ^= *p; + nwcryptinit(cryptbuf, hashrand); + nwencrypt(cryptbuf, temp, *outp); + *outp += 8; + memzero(rand); + memzero(hashrand); + memzero(temp); + memzero(cryptbuf); + memzero(buf2); + return 0; +} + +char bufstr[16]={28, 0, 0, 0, 1, 0, 0, 0, 1, 0, 6, 0, 16, 0, 4, 0}; +long nds_login(struct ncp_conn *conn, long user_id, const char *pwd, + long serv_id, char *logindata, char **u_priv_key) { + char *buf, *p, *pend; + char temp[16]; + char hashshuf[8]; + char loginid[4]; + char crypt1strc[28]; + char randno[4]; + char randbuf[1024]; + char *tempbuf; + int i, outlen; + int n1, n2, n3; + long err; + int grace_period = 0; + + if (u_priv_key) *u_priv_key = NULL; + if (!(buf = malloc(4096))) + return ENOMEM; + pend = (p = buf) + 2048; + buf_put_dword_lh(&p, pend, 0); + buf_put_dword_hl(&p, pend, user_id); + if ((err = ncp_send_nds_frag(conn, 57, buf, p - buf, buf + 2048, 2048, + &outlen))) { + free(buf); + return err; + } + pend = (p = buf + 2048) + outlen; + if ((buf_get_buf(&p, pend, temp, 4)) || + (buf_get_buf(&p, pend, loginid, 4))) { + free(buf); + return NCPL_ET_REPLY_FORMAT; + } + free(buf); + if (strlen(pwd) > 127) + return NCPL_ET_PWD_TOO_LONG; + if (!(tempbuf = malloc(1064))) + return ENOMEM; + if (!(buf = malloc(4096))) { + free(tempbuf); + return ENOMEM; + } + strcpy(randbuf, pwd); + for (p = randbuf; *p; p++) + *p = toupper(*p); +#if 0 + shuffle(temp, randbuf, temp); +#else + shuffle(temp, randbuf, strlen(randbuf), temp); +#endif + nwhash1init(hashshuf, 8); + for (i = 10; i; i--) + nwhash1(hashshuf, 8, temp, 16); + memcpy(temp, loginid, 4); + memset(temp + 4, 7, 7); + nwhash1init(temp + 11, 5); + nwhash1(temp + 11, 5, temp, 11); + memcpy(crypt1strc, bufstr + 4, 12); + nwencryptblock(hashshuf, temp, 16, crypt1strc + 12); + + fillrandom(randno, 4); + fillrandom(randbuf, 1024); + pend = (p = tempbuf) + 1064; + buf_put_buf(&p, pend, randno, 4); + buf_put_dword_lh(&p, pend, 1024); + buf_put_buf(&p, pend, randbuf, 1024); + buf_put_buf(&p, pend, bufstr, sizeof(bufstr)); + buf_put_buf(&p, pend, crypt1strc + 12, 16); + + pend = (p = buf) + 2048; + buf_put_dword_lh(&p, pend, 2); + buf_put_dword_lh(&p, pend, 0); + buf_put_dword_hl(&p, pend, user_id); + + rsa_crypt(conn, tempbuf, 1064, serv_id, &p, pend); + memset(tempbuf, 0, 1064); + free(tempbuf); + + if ((err = ncp_send_nds_frag(conn, 58, buf, p - buf, buf + 2048, 2048, + &outlen))) { + if ((err != NCPL_ET_REQUEST_ERROR) || + (conn->completion != NDS_GRACE_PERIOD)) + goto err_exit; + grace_period = 1; + } + err = NCPL_ET_REPLY_FORMAT; + pend = (p = buf + 2048) + outlen; + if ((buf_get_buf(&p, pend, logindata, 8)) || + (buf_get_dword_lh(&p, pend, &n1)) || + (n1 > pend - p)) + goto err_exit; + pend = p + n1; + if ((buf_get_dword_lh(&p, pend, &n1)) || + (buf_get_dword_lh(&p, pend, &n2)) || + (buf_get_word_lh(&p, pend, &n3)) || + (n1 != 1) || (n2 != 0x060001) || (n3 > pend - p)) + goto err_exit; + + nwhash1init(temp, 8); + for (i = 10; i; i--) + nwhash1(temp, 8, crypt1strc, 28); + nwdecryptblock(temp, p, n3, p); + nwhash1init(temp, 5); + nwhash1(temp, 5, p, n3 - 5); + if (memcmp(temp, p + n3 - 5, 5)) + goto err_exit; + pend = p + n3 - 12; + if ((buf_get_buf(&p, pend, loginid, 4)) || + (buf_get_dword_lh(&p, pend, &n2)) || + (memcmp(loginid, randno, 4)) || (n2 > pend - p)) + goto err_exit; + pend = p + n2; + for (i = 0; i < n2; i++) + p[i] ^= randbuf[i]; + if ((buf_get_dword_lh(&p, pend, &n1)) || + (buf_get_dword_lh(&p, pend, &n2)) || + (buf_get_word_lh(&p, pend, &n3)) || + (n1 != 1) || (n2 != 0x060001) || (n3 > pend - p)) + goto err_exit; + pend = p + n3; + nwdecryptblock(hashshuf, p, n3, p); + if ((buf_get_dword_lh(&p, pend, &n1)) || + (buf_get_word_lh2(&p, pend, &n2)) || + (buf_get_word_lh2(&p, pend, &n3)) || + (n1 != 1) || (n2 != 2) || (n3 > pend - p)) + goto err_exit; + if (u_priv_key) { + if (!(tempbuf = malloc(n3 + 10))) { + err = ENOMEM; + goto err_exit; + } + memset(tempbuf, 0, 8); + tempbuf[0] = 1; + tempbuf[4] = 3; + tempbuf[6] = 1; + tempbuf[8] = n3 & 255; + tempbuf[9] = n3 >> 8; + memcpy(tempbuf + 10, p, n3); + if (!checkkey(tempbuf)) { + free(tempbuf); + goto err_exit; + } + *u_priv_key = tempbuf; + } + err = 0; + if (grace_period) { + conn->completion = NDS_GRACE_PERIOD; + err = NCPL_ET_REQUEST_ERROR; + } + +err_exit: + memzero(hashshuf); + memzero(randbuf); + memzero(crypt1strc); + memzero(randno); + memzero(temp); + if (buf) free(buf); + return err; +} + +long nds_beginauth(struct ncp_conn *conn, long user_id, + long serv_id, char *authid) { + char *buf, *p, *pend, *n_temp, temp[8]; + char *s_key; + char randno[4]; + long err; + int outlen, n1, n2, n3, n4; + + if (!(buf = malloc(2048))) + return ENOMEM; + n_temp = NULL; + fillrandom(randno, 4); + pend = (p = buf) + 512; + buf_put_dword_lh(&p, pend, 0); + buf_put_dword_hl(&p, pend, user_id); + buf_put_buf(&p, pend, randno, 4); + if ((err = ncp_send_nds_frag(conn, 59, buf, p - buf, buf + 1024, 1024, + &outlen))) + goto err_exit; + + err = NCPL_ET_REPLY_FORMAT; + pend = (p = buf + 1024) + outlen; + if ((buf_get_buf(&p, pend, authid, 4)) || + (buf_get_dword_lh(&p, pend, &outlen)) || + (outlen > pend - p)) + goto err_exit; + pend = p + outlen; + if ((buf_get_dword_lh(&p, pend, &n1)) || + (buf_get_dword_lh(&p, pend, &n2)) || + (buf_get_dword_lh(&p, pend, &n3)) || + (n1 != 1) || (n2 != 0x020009) || (n3 > pend - p)) + goto err_exit; + pend = p + n3; + if ((buf_get_dword_lh(&p, pend, &n1)) || + (buf_get_dword_lh(&p, pend, &n1)) || + (buf_get_dword_lh(&p, pend, &n2)) || + (buf_get_word_lh(&p, pend, &n3)) || + (n1 != 1) || (n2 != 0x0a0001) || (n3 > pend - p)) + goto err_exit; + n1 = ((countbits_l(p, n3) + 31) & (~31)) >> 3; + if (n1 < 52) + goto err_exit; + if (!(n_temp = malloc(n1))) { + err = ENOMEM; + goto err_exit; + } + copyfill(n_temp, n1, p, n3); + p += (n3 + 3) & (~3); + + if ((err = get_public_key(conn, serv_id, &s_key))) + goto err_exit; + err = modexpkey(s_key, n_temp, n_temp, n1); + free(s_key); + if (err) + goto err_exit; + err = NCPL_ET_REPLY_FORMAT; + nwhash1init(temp, 7); + nwhash1(temp + 5, 2, n_temp, 45); + nwhash1(temp, 5, n_temp + 1, 39); + if (memcmp(temp, n_temp + 40, 7)) + goto err_exit; + nwhash1init(temp, 8); + for (n1 = 10; n1; n1--) + nwhash1(temp, 8, n_temp + 1, 28); + free(n_temp); n_temp = NULL; + if ((buf_get_dword_lh(&p, pend, &n1)) || + (buf_get_dword_lh(&p, pend, &n2)) || + (buf_get_dword_lh(&p, pend, &n3)) || + (buf_get_dword_lh(&p, pend, &n4)) || + (n1 != 28) || (n2 != 1) || (n3 != 0x060001) || (n4 != 0x040010) || + (pend - p < 16)) + goto err_exit; + nwdecryptblock(temp, p, 16, p); + nwhash1init(temp, 5); + nwhash1(temp, 5, p, 11); + if ((!memcmp(temp, p + 11, 5)) || (!memcmp(p, randno, 4))) + err = 0; +err_exit: + if (n_temp) free(n_temp); + if (buf) free(buf); + return err; +} + +char *allocfillchunk(const char *keyptr, int keylen, const char *chunk, + int destsize) { + char *p, *p2; + int i; + i = findchunk(keyptr, keylen, chunk, &p); + if (!p) + return NULL; + if (!(p2 = malloc(destsize))) + return NULL; + copyfill(p2, destsize, p, i); + return p2; +} + +long gen_auth_data(char **outp, char *outend, + const char *u_key, const char *u_priv_key, + const char *authid, char *loginstrc, int loginstrc_len) { + char *keyptr; + int keylen, i, j; + int nbits, nblocksize, nbytes; + unsigned char nmask; + unitptr n_mod, n_exp, n_pn, n_qn, n_dp, n_dq, n_cr, n_key, n_temp; + unitptr n_key_dp, n_key_dq; + unitptr up, up2; + char *p, *tempbuf; + char *randbuf = NULL; + char hashbuf[0x42]; + long err; + + n_temp = n_mod = n_exp = n_pn = n_qn = n_dp = n_dq = n_cr = n_key = + n_key_dp = n_key_dq = NULL; + if (!initkey(u_key, &keyptr, &keylen)) + return NCPL_ET_REPLY_FORMAT; + i = findchunk(keyptr, keylen, "NN", &p); + if (!p) + return NCPL_ET_REPLY_FORMAT; + nbits = countbits_l(p, i); + nbytes = (nbits + 7) >> 3; + nmask = (unsigned char)(255 >> (8 - (nbits & 7))); + nblocksize = ((nbits + 31) & (~31)) >> 3; + + set_precision(bytes2units(nblocksize)); + + n_mod = (unitptr)allocfillchunk(keyptr, keylen, "NN", nblocksize); + n_exp = (unitptr)allocfillchunk(keyptr, keylen, "EN", nblocksize); + if (!initkey(u_priv_key, &keyptr, &keylen)) { + err = NCPL_ET_REPLY_FORMAT; + goto err_exit; + } + n_pn = (unitptr)allocfillchunk(keyptr, keylen, "PN", nblocksize); + n_qn = (unitptr)allocfillchunk(keyptr, keylen, "QN", nblocksize); + n_dp = (unitptr)allocfillchunk(keyptr, keylen, "DP", nblocksize); + n_dq = (unitptr)allocfillchunk(keyptr, keylen, "DQ", nblocksize); + n_cr = (unitptr)allocfillchunk(keyptr, keylen, "CR", nblocksize); + n_key = malloc(nblocksize); + + nwhash2init(hashbuf); + nwhash2block(hashbuf, loginstrc, loginstrc_len); + nwhash2end(hashbuf); + copyfill(n_key, nblocksize, hashbuf, 16); + + if (!(tempbuf = malloc(loginstrc_len + 16))) { + err = ENOMEM; + goto err_exit; + } + memset(tempbuf, 0, 16); + tempbuf[4] = 0x3c; + memcpy(tempbuf + 8, authid, 4); + p = tempbuf + 12; + buf_put_dword_lh(&p, tempbuf + 16, loginstrc_len); + memcpy(p, loginstrc, loginstrc_len); + + nwhash2init(hashbuf); + nwhash2block(hashbuf, tempbuf, loginstrc_len + 16); + free(tempbuf); + + n_temp = malloc(nblocksize); + n_key_dp = malloc(nblocksize); + n_key_dq = malloc(nblocksize); + mp_mult(n_temp, n_pn, n_qn); + mp_modexp(n_key_dp, n_key, n_dp, n_pn); + mp_modexp(n_key_dq, n_key, n_dq, n_qn); + mp_move(n_temp, n_key_dp); + mp_add(n_temp, n_pn); + mp_sub(n_temp, n_key_dq); + stage_modulus(n_pn); + mp_modmult(n_temp, n_temp, n_cr); + mp_mult(n_key, n_temp, n_qn); + mp_add(n_key, n_key_dq); + + randbuf = malloc(nblocksize * 3); + memset(randbuf, 0, nblocksize * 3); + + buf_put_dword_lh(outp, outend, 12 + nblocksize * 6); + buf_put_dword_lh(outp, outend, 1); + buf_put_dword_lh(outp, outend, 0x100008); + buf_put_word_lh2(outp, outend, 3); + buf_put_word_lh2(outp, outend, nblocksize * 3); + memset(*outp, 0, nblocksize * 6); + + up = (unitptr)randbuf; up2 = (unitptr)*outp; + for (i = 3; i; i--) { + fillrandom((char *)up, nbytes); + ((char *)up)[nbytes - 1] &= nmask; + if (!(j = mp_compare(up, n_mod))) + mp_dec(up); + else if (j > 0) { + mp_sub(up, n_mod); + mp_neg(up); + mp_add(up, n_mod); + } + mp_modexp(up2, up, n_exp, n_mod); + ((char *)up) += nblocksize; + ((char *)up2) += nblocksize; + } + nwhash2block(hashbuf, *outp, nblocksize * 3); + nwhash2end(hashbuf); + + up = (unitptr)randbuf; + for (i = 0; i < 3; i++) { + mp_init(n_temp, (unsigned char)hashbuf[i << 1] | + ((unsigned char)hashbuf[(i << 1) + 1] << 8)); + mp_modexp(up2, n_key, n_temp, n_mod); + stage_modulus(n_mod); + mp_modmult(up2, up2, up); + ((char *)up) += nblocksize; + ((char *)up2) += nblocksize; + } + *outp = (char *)up2; + err = 0; +err_exit: + memzero(hashbuf); + free(randbuf); + if (n_temp) { mp_init0(n_temp); free(n_temp); } + if (n_key_dp) { mp_init0(n_key_dp); free(n_key_dp); } + if (n_key_dq) { mp_init0(n_key_dq); free(n_key_dq); } + if (n_pn) { mp_init0(n_pn); free(n_pn); } + if (n_qn) { mp_init0(n_qn); free(n_qn); } + if (n_dp) { mp_init0(n_dp); free(n_dp); } + if (n_dq) { mp_init0(n_dq); free(n_dq); } + if (n_cr) { mp_init0(n_cr); free(n_cr); } + free(n_mod); + free(n_exp); + return err; +} + + +long nds_authenticate(struct ncp_conn *conn, long user_id, + long serv_id, const char *logindata, const char *u_priv_key) { + char authid[4]; + long err; + int user_name_len; + uni_char *user_name = NULL; + char *loginstrc; + int loginstrc_len; + char *buf, *p, *pend; + char *u_key; +#ifdef SIGNATURES + char signkey[8]; +#endif + + u_key = loginstrc = buf = NULL; + if ((err = nds_beginauth(conn, user_id, serv_id, authid))) + return err; + if ((err = nds_readentryname(conn, user_id, &user_name, &user_name_len))) + return err; + loginstrc_len = user_name_len + 22; + if (!(loginstrc = malloc(loginstrc_len))) { + err = ENOMEM; + goto err_exit; + } + memset(loginstrc, 0, 22); + loginstrc[0] = 1; + loginstrc[4] = 6; + memcpy(loginstrc + 6, logindata, 8); + fillrandom(loginstrc + 14, 4); + loginstrc[20] = user_name_len & 255; + loginstrc[21] = user_name_len >> 8; + memcpy(loginstrc + 22, user_name, user_name_len); + free(user_name); user_name = NULL; + if ((err = get_public_key(conn, user_id, &u_key))) + goto err_exit; + if (!(buf = malloc(2048))) { + err = ENOMEM; + goto err_exit; + } + pend = (p = buf) + 2048; + buf_put_dword_lh(&p, pend, 0); +#ifdef SIGNATURES + if (conn->sign_wanted) { + fillrandom(signkey, 8); + rsa_crypt(conn, signkey, 8, serv_id, &p, pend); + } else +#endif + buf_put_dword_lh(&p, pend, 0); + buf_put_lbuf(&p, pend, loginstrc, loginstrc_len); + + if ((err = gen_auth_data(&p, pend, u_key, u_priv_key, + authid, loginstrc, loginstrc_len))) + goto err_exit; + if ((err = ncp_send_nds_frag(conn, 60, buf, p - buf, NULL, 0, NULL))) + goto err_exit; +#ifdef SIGNATURES + if ((err = ncp_sign_start(conn, signkey))) + goto err_exit; +#endif + err = ncp_change_conn_state(conn, 1); + +err_exit: + if (loginstrc) free(loginstrc); + if (buf) free(buf); + if (u_key) free(u_key); + if (user_name) free(user_name); + return err; +} + +long nds_login_auth(struct ncp_conn *conn, const char *user, + const char *pwd) { + long err; + uni_char user_u[200]; + char *u_priv_key = NULL; + char logindata[8]; + uni_char *server_name = NULL; + __u32 serv_id, user_id; + struct sockaddr_ipx wserv_addr; + struct ncp_conn *login_conn, *wserv_conn = NULL; + int not_wserv; /* =1: current server doesn't have a writable replica */ + int i; + struct timeval tv; + int grace_period = 0; +#ifdef ERR_MSG + char buf[200]; /* to print username */ +#endif + gettimeofday(&tv, NULL); + srand(tv.tv_usec); + + if (strlen(user) >= 200) + return NCPL_ET_NAMETOOLONG; + strcpy_cu(user_u, user); + i = sizeof(wserv_addr); + err = nds_resolve_name(conn, 0x64, user_u, &user_id, ¬_wserv, + (struct sockaddr *)&wserv_addr, &i); + if ((err == NCPL_ET_REQUEST_ERROR) && (conn->completion == -601) && + (user_u[strlen_u(user_u)-1] != '.')) { +#ifdef ERR_MSG + strcpy_uc(buf, user_u); + printf("User %s not found in current context.\n" + "Trying server context...\n", buf); +#endif + if ((err = nds_get_server_name(conn, &server_name)) != 0) + goto err_exit; + i = 0; + while ((server_name[i]) && (server_name[i] != '.')) + i++; + memcpy(user_u + strlen_u(user_u), server_name + i, + (strlen_u(server_name) - i + 1) * 2); + free(server_name); + server_name = NULL; + i = sizeof(wserv_addr); + err = nds_resolve_name(conn, 0x64, user_u, &user_id, ¬_wserv, + (struct sockaddr *)&wserv_addr, &i); + } + if (err) { +#ifdef ERR_MSG + if (err == NCPL_ET_REQUEST_ERROR) + fprintf(stderr, "error %d finding user\n", conn->completion); +#endif + goto err_exit; + } + if (not_wserv) { + if (!(login_conn = wserv_conn = ncp_open_addr(&wserv_addr, &err))) + goto err_exit; + } else + login_conn = conn; + if ((err = nds_get_server_name(login_conn, &server_name)) != 0) + goto err_exit2; + if ((err = nds_resolve_name(login_conn, 0x62, server_name, &serv_id, + NULL, NULL, NULL)) != 0) + goto err_exit2; + if ((err = nds_login(login_conn, user_id, pwd, serv_id, logindata, + &u_priv_key))) { + if ((err != NCPL_ET_REQUEST_ERROR) || + (login_conn->completion != NDS_GRACE_PERIOD)) { +#ifdef ERR_MSG + if (err == NCPL_ET_REQUEST_ERROR) + fprintf(stderr, "error %d logging in\n", login_conn->completion); +#endif +err_exit2:; + conn->completion = login_conn->completion; + goto err_exit; + } + grace_period = 1; + } + if (not_wserv) { + free(server_name); + if ((err = nds_get_server_name(conn, &server_name)) != 0) + goto err_exit; + if ((err = nds_resolve_name(conn, 0x62, server_name, &serv_id, + NULL, NULL, NULL)) != 0) + goto err_exit; + if ((err = nds_resolve_name(conn, 0x51, user_u, &user_id, + NULL, NULL, NULL)) !=0) + goto err_exit; + } + if ((err = nds_authenticate(conn, user_id, serv_id, logindata, + u_priv_key))) { +#ifdef ERR_MSG + if (err == NCPL_ET_REQUEST_ERROR) + fprintf(stderr, "error %d authenticating\n", conn->completion); +#endif + goto err_exit; + } + if (grace_period && (!err)) { + conn->completion = NDS_GRACE_PERIOD; + err = NCPL_ET_REQUEST_ERROR; + } +err_exit: + if (wserv_conn) ncp_close(wserv_conn); + if (u_priv_key) { clearkey(u_priv_key); free(u_priv_key); } + free(server_name); +#ifdef RANDBUF + memset(global_randbuf, 0, RANDBUFSIZE); + g_rndp = global_randbuf + RANDBUFSIZE; +#endif + return err; +} diff --git a/lib/nwcrypt.c b/lib/nwcrypt.c index 645cfdf..174375a 100644 --- a/lib/nwcrypt.c +++ b/lib/nwcrypt.c @@ -142,7 +142,7 @@ shuffle1(buf32 temp, unsigned char *target) } -static void +void shuffle(const unsigned char *lon, const unsigned char *buf, int buflen, unsigned char *target) { diff --git a/lib/platform.h b/lib/platform.h new file mode 100644 index 0000000..2358c99 --- /dev/null +++ b/lib/platform.h @@ -0,0 +1,218 @@ +/* platform.h - computer platform customization for PGP + multiprecision math package. #Included in mpilib.h. +*/ +#ifndef PLATFORM_H +#define PLATFORM_H + +/* Platform customization: + * A version which runs on almost any computer can be implemented by + * defining PORTABLE and MPORTABLE, preferably as a command line + * parameter. Faster versions can be generated by specifying specific + * parameters, such as size of unit and MULTUNIT, and by supplying some + * of the critical in assembly. + * + * This file holds customizations for different environments. + * This is done in one of two ways: + * 1. A symbol is defined on the command line which designates a + * particular environment, such as MSDOS. This file detects the + * environment symbol and sets the appropriate low-level defines. + * + * 2. If no environment is named, the low-level defines are set in + * the same manner as for PGP 2.0, thereby providing an easy upgrade. + * + * Following are a description of the low-level definition symbols: + * + * The following preprocessor symbols should be conditionally set to + * optimize for a particular environment. + * + * Define one of the following: + * UNIT8, UNIT16, or UNIT32 - specifies size of operands for + * multiprecision add, subtract, shift, and initialization operations. + * Define one of the following: + * MUNIT8, MUNIT16, MUNIT32 - specified size of operands for + * multiprecision multiply and mod_mult. This must be less than or + * equal to unit size. It should be the word size for the native + * atomic multiply instruction. For a 16x16 bit multiply yielding a + * 32-bit product, MUNIT16 should be set. + * Define one (or more) of the following: + * PEASANT, MERRITT, UPTON, SMITH -algorithm used for modmult. All defined + * algorithms are compiled, but the first defined name listed will be + * assigned to the generic entry point symbols. Multiple algorithms are + * used primarily for testing. + * HIGHFIRST - specified if longs are stored with the most significant + * bit at the lowest address (Motorola), undefined otherwise. This should + * be defined on the command line, normally in the makefile. + * + * The following symbol, if initialized, is set to specific values: + * ALIGN - variable declaration attribute which forces optimum alignment + * of words, e.g. for VAX C: ALIGN=_align(quadword) + * + * The following symbols correspond to individual multiprecision routines + * that may be implemented with assembly language. If they are implemented + * in assembly, the symbols should be defined with the name of the + * corresponding external entry points, e.g., mp_addc=P_ADDC + * mp_setp - set precision for external routines + * mp_addc - add with carry + * mp_subb - subtract with borrow + * mp_rotate_left - rotate left + * mp_compare - compare + * mp_move - move + * unitfill0 - zero fill + * mp_smul - multiply vector by single word * + * mp_smula - multiply vector by single word and accumulate * + * mp_dmul - full multiply + * mp_set_recip - setup for mp_quo_digit + * mp_quo_digit - quotient digit for modulus reduction + * + * Either mp_smul or mp_smula should be defined. mp_smula provides + * for accumulation to an existing value, while mp_smul is for use of the + * older definition of mp_smul, used in PGP 2.0, which assumed that the high + * order accumulator word is zero. Use of mp_smula causes one less word of + * precision to be used, thereby slightly increasing speed. + */ + +/******************************************************************** + * Environment customization. Please send any additions or corrections + * to Philip Zimmermann. + */ +#ifndef PORTABLE + +#ifdef MSDOS +#ifndef i386 /* gcc */ +#define UNIT16 +#define MUNIT16 +#define mp_setp P_SETP +#define mp_addc P_ADDC +#define mp_subb P_SUBB +#define mp_rotate_left P_ROTL +#define mp_smula P_SMULA +#define mp_quo_digit P_QUO_DIGIT +#define mp_set_recip P_SETRECIP +#define SMITH +#define PLATFORM_SPECIFIED +#endif /* i386 */ +#endif /* MSDOS */ + +#ifdef VMS +#define UNIT32 /* use 32-bit units */ +#define MUNIT32 /* not used in C code, only in assembler */ +#define UPTON +#define mp_setp p_setp +#define mp_addc p_addc +#define mp_subb p_subb +#define mp_rotate_left p_rotl +#define mp_smul p_smul +#define mp_dmul p_dmul +#define mp_compare p_cmp +#define ALIGN _align(quadword) + +#ifdef VAXC +/* + * A VAX is a CISC machine. Unfortunately C is at to low a level to use + * many of the instruction set enhancements so we define some macros + * here that implement fast moves and fast zero fills with single + * instructions. + */ +#pragma builtins +#define mp_move( dst, src) _MOVC3( global_precision*4, (char *) src, (char *) dst) +#define unitfill0( r, unitcount) _MOVC5( 0, (char *) 0, 0, unitcount*4, (char *) r) +#define mp_burn(r) _MOVC5(0, (char *) 0, 0, global_precision*4, (char *) r) +#define mp_init0(r) mp_burn(r) /* Just for documentation purposes */ +#endif /* VAXC */ + +#define PLATFORM_SPECIFIED +#endif /* VMS */ + +#ifdef mips +/* + * Needs r3kd.s and r3000.s (or r3000.c) + */ +#define UNIT32 +#define MUNIT32 +#define SMITH +#define mp_dmul p_dmul +#define mp_setp p_setp +#define mp_addc p_addc +#define mp_subb p_subb +#define mp_rotate_left p_rotl +#define mp_smula p_smula +#define mp_quo_digit p_quo_digit +#define mp_set_recip p_setrecip +#define PLATFORM_SPECIFIED +#endif /* mips */ + +#ifdef i386 +/* + * Needs 80386.S + */ +#define UNIT32 +#define MUNIT32 +#define SMITH +#define mp_setp P_SETP +#define mp_addc P_ADDC +#define mp_subb P_SUBB +#define mp_rotate_left P_ROTL +#define unitfill0(r,ct) memset((void*)r, 0, (ct)*sizeof(unit)) +#define mp_smula P_SMULA +#define mp_quo_digit p_quo_digit +#define mp_set_recip p_setrecip +#define PLATFORM_SPECIFIED +#endif /* i386 */ + +#ifdef sparc +/* + * Needs sparc.s + */ +#define UNIT32 +#define MERRITT +#define mp_setp P_SETP +#define mp_addc P_ADDC +#define mp_subb P_SUBB +#define mp_rotate_left P_ROTL +#define unitfill0(r,ct) memset((void*)r, 0, (ct)*sizeof(unit)) +#define PLATFORM_SPECIFIED +#endif /* sparc */ + +#if defined(mc68000) || defined(mc68020) +/* + * Needs mc68020.S + */ +#define UNIT32 +#define mp_setp P_SETP +#define mp_addc P_ADDC +#define mp_subb P_SUBB +#define mp_rotate_left P_ROTL +#define unitfill0(r,ct) memset((void*)r, 0, (ct)*sizeof(unit)) +#if defined(sun3) || defined(mc68020) +# define UPTON +# define MUNIT32 +# define mp_smul P_SMUL +/* # define mp_dmul P_DMUL */ /* mc68020.s has a bug in P_DMUL */ +#else +# define SMITH +# define MUNIT16 +#endif +#define PLATFORM_SPECIFIED +#endif /* mc68000 */ + +/* Add additional platforms here ... */ + +/**************** End of system specification ************************/ + +#ifndef PLATFORM_SPECIFIED +/* No platform explicitly selected. Customization is controlled by + * PORTABLE and MPORTABLE. + */ +#define mp_setp P_SETP +#define mp_addc P_ADDC +#define mp_subb P_SUBB +#define mp_rotate_left P_ROTL +#define UPTON +#define unitfill0(r,ct) memset((void*)r, 0, (ct)*sizeof(unit)) +#ifndef MPORTABLE +#define mp_smul P_SMUL +#endif /* MPORTABLE */ +#endif /* PLATFORM_SPECIFIED */ +#endif /* PORTABLE */ +#endif /* PLATFORM_H */ + diff --git a/lib/usuals.h b/lib/usuals.h new file mode 100644 index 0000000..3f66fad --- /dev/null +++ b/lib/usuals.h @@ -0,0 +1,41 @@ +/* usuals.h - The usual typedefs, etc. +*/ +#ifndef USUALS /* Assures no redefinitions of usual types...*/ +#define USUALS + +typedef unsigned char boolean; /* values are TRUE or FALSE */ +typedef unsigned char byte; /* values are 0-255 */ +typedef byte *byteptr; /* pointer to byte */ +typedef char *string; /* pointer to ASCII character string */ +typedef unsigned short word16; /* values are 0-65535 */ +#ifdef __alpha +typedef unsigned int word32; /* values are 0-4294967295 */ +#else +typedef unsigned long word32; /* values are 0-4294967295 */ +#endif + +#ifndef TRUE +#define FALSE 0 +#define TRUE (!FALSE) +#endif /* if TRUE not already defined */ + +#ifndef min /* if min macro not already defined */ +#define min(a,b) (((a)<(b)) ? (a) : (b) ) +#define max(a,b) (((a)>(b)) ? (a) : (b) ) +#endif /* if min macro not already defined */ + +/* void for use in pointers */ +#ifndef NO_VOID_STAR +#define VOID void +#else +#define VOID char +#endif + + /* Zero-fill the byte buffer. */ +#define fill0(buffer,count) memset( buffer, 0, count ) + + /* This macro is for burning sensitive data. Many of the + file I/O routines use it for zapping buffers */ +#define burn(x) fill0((VOID *)&(x),sizeof(x)) + +#endif /* if USUALS not already defined */ diff --git a/man/pqrm.1 b/man/pqrm.1 new file mode 100644 index 0000000..7a3288e --- /dev/null +++ b/man/pqrm.1 @@ -0,0 +1,105 @@ +.TH PQRM 1 03/03/1998 pqrm pqrm +.SH NAME +pqrm \- Remove job from NetWare print queue +.SH SYNOPSIS +.B pqrm +[ +.B -h +] [ +.B -S +.I server +] [ +.B -U +.I user name +] [ +.B -P +.I password + | +.B -n +] [ +.B -C +] +.I queue_name +.I job_ID +[ +.I another_job_ID +... ] + +.SH DESCRIPTION +.B pqrm +remove specified jobs from the NetWare print queue available +to you on some server. If you are already connected to some server, this one +is used. + +.B pqrm +looks up the file +.I $HOME/.nwclient +to find a file server, a user name and possibly a password. See +nwclient(5) for more information. Please note that the access +permissions of .nwclient MUST be 600, for security reasons. + +.SH OPTIONS + +.B queue_name +.RS 3 +.B queue_name +is used to specify queue. You can not use wildcards in the name. +.RE + +.B job_ID +, +.B another_job_ID +.RS 3 +.B job_ID +is used to specify which job has to be deleted. +.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 + +.B -P +.I password +.RS 3 +You may want to give the password required by the server on the +command line. You should be careful about using passwords in scripts. +.RE + +.B -n +.RS 3 +.B -n +should be given to mount shares which do not require a password to log in. + +If neither +.B -n +nor +.B -P +are given, pqstat prompts for a password. +.RE + +.B -C +.RS 3 +By default, passwords are converted to uppercase before they are sent +to the server, because most servers require this. You can turn off +this conversion by +.B -C. +.RE + +.SH SEE ALSO +.B nwclient(5), nprint(1), slist(1), ncpmount(8), ncpumount(8), pqlist(1), pqstat(1) + +.SH CREDITS +pqrm was written by Petr Vandrovec (vandrove@vc.cvut.cz) + diff --git a/man/pqstat.1 b/man/pqstat.1 new file mode 100644 index 0000000..6f647fd --- /dev/null +++ b/man/pqstat.1 @@ -0,0 +1,107 @@ +.TH PQSTAT 1 03/03/1998 pqstat pqstat +.SH NAME +pqstat \- List jobs in NetWare print queue +.SH SYNOPSIS +.B pqstat +[ +.B -h +] [ +.B -S +.I server +] [ +.B -U +.I user name +] [ +.B -P +.I password + | +.B -n +] [ +.B -C +] +.I queue name +[ +.I job count +] +.SH DESCRIPTION +.B pqstat +lists specified number of jobs from the specified NetWare print queue available +to you on some server. If you are already connected to some server, this one +is used. + +If pqstat does not print to a tty, the decorative header line is +not printed, so that you can count the jobs in print queue by doing + + pqstat -S server queue | wc -l + +.B pqstat +looks up the file +.I $HOME/.nwclient +to find a file server, a user name and possibly a password. See +nwclient(5) for more information. Please note that the access +permissions of .nwclient MUST be 600, for security reasons. + +.SH OPTIONS + +.B queue name +.RS 3 +.B queue name +is used to specify queue. You can not use wildcards in the name. +.RE + +.B job count +.RS 3 +.B job count +is used to specify how much entries will be shown. Default is to show all +entries. +.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 + +.B -P +.I password +.RS 3 +You may want to give the password required by the server on the +command line. You should be careful about using passwords in scripts. +.RE + +.B -n +.RS 3 +.B -n +should be given to mount shares which do not require a password to log in. + +If neither +.B -n +nor +.B -P +are given, pqstat prompts for a password. +.RE + +.B -C +.RS 3 +By default, passwords are converted to uppercase before they are sent +to the server, because most servers require this. You can turn off +this conversion by +.B -C. +.RE + +.SH SEE ALSO +.B nwclient(5), nprint(1), slist(1), ncpmount(8), ncpumount(8), pqlist(1), pqrm(1) + +.SH CREDITS +pqstat was written by David Woodhouse (dave@imladris.demon.co.uk) + diff --git a/man/pserver.1 b/man/pserver.1 index b8358a0..be34f9f 100644 --- a/man/pserver.1 +++ b/man/pserver.1 @@ -108,6 +108,9 @@ print job. %u: This field will be replaced by the name of the user who posted this print job. + +%d: This field will be replaced by the job description field of +this print job. .RE .B -j diff --git a/ncpfs-2.0.11.lsm b/ncpfs-2.0.12.lsm similarity index 75% rename from ncpfs-2.0.11.lsm rename to ncpfs-2.0.12.lsm index 1c56c9b..221b2aa 100644 --- a/ncpfs-2.0.11.lsm +++ b/ncpfs-2.0.12.lsm @@ -1,17 +1,18 @@ Begin3 Title: ncpfs -Version: 2.0.11 -Entered-date: July 14, 1997 +Version: 2.0.12 +Entered-date: March 13, 1998 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 Linux printing system. Keywords: filesystem ncp novell netware printing Author: lendecke@Math.Uni-Goettingen.de (Volker Lendecke) -Maintained-by: lendecke@Math.Uni-Goettingen.de (Volker Lendecke) +Maintained-by: lendecke@Math.Uni-Goettingen.de (Volker Lendecke), + vandrove@vc.cvut.cz (Petr Vandrovec) Primary-site: ftp.gwdg.de:/pub/linux/misc/ncpfs Alternate-site: sunsite.unc.edu:/pub/Linux/system/Filesystems/ncpfs - ~158k ncpfs-2.0.11.tgz - ~ 1k ncpfs-2.0.11.lsm + ~195k ncpfs-2.0.12.tgz + ~ 1k ncpfs-2.0.12.lsm Copying-policy: GPL End diff --git a/ncpfs-nds-0.06.CHANGES b/ncpfs-nds-0.06.CHANGES new file mode 100644 index 0000000..3b70f7a --- /dev/null +++ b/ncpfs-nds-0.06.CHANGES @@ -0,0 +1,96 @@ +Release 0.06, January 20, 1998 +* Signature support (re)included. Because of this the name has changed + and the kernel patch is added. +* Fixed segfault in nds_login_auth when trying server context. + (with thanks to K J MacDonald, kenny@holyrood.ed.ac.uk) + + +Release 0.05, November 7, 1997 +* Added include errno.h to ndslib.c (needed for glibc) +* Modified nwsfind to accept -a parameter that causes nwsfind to + interpret the server name as an address. +* Fixed segfault in ncp_open_addr +* Modified lib/ncplib.c:ncp_open_addr to call nwsfind with the address + (creates a route to the address if necessary). + +Release 0.04, November 5, 1997 +Changes since 0.03: +* Added support for NDS login/authenticate to a read-only replica + (untested) +* ncpmount has new option -b to use bindery logins to NDS servers + (actually since 0.01) +(Again with thanks to Petr Vandrovec, vandrove@vc.cvut.cz) + + +Release 0.03, November 2, 1997 +Changes since 0.02: +* (Hopefully temporarily) removed signature support, it seems to be + legally protected. Because of this the name has changed and the + kernel patch is removed. + + +Release 0.02, October 15, 1997 +Changes since 0.01: +* Fixed bug with empty passwords +* Fixed bug with beginlogin id != user id +* Fixed bug with fragger handle != 0 +* Removed ncpsign.* from ./sutil, moved ncpsign.h to ./include +* Reorganized packet signature initializing +* Added support for NDS grace logins +(With thanks to Petr Vandrovec, vandrove@vc.cvut.cz) + + +Changes made by ncpfs-nds-sign-0.01.patch: + +* Adds NDS_SUPPORT conditional variable to main Makefile +* Puts set -e; ahead of the SUBDIRS loop in the main Makefile to abort + the loop if compilation in a subdir fails +* Adds README.NDS file +* Adds fields for packet signatures to struct ncp_conn +* Adds the following functions to the lib/ncplib.c + ncp_negotiate_size_and_options Negotiate packet size and options + ncp_get_bindery_access_level Get bindery access level + ncp_init_pb_conn Initialize packet burst connection + ncp_send_nds_frag Send message with NDS fragger protocol + ncp_sign_start Initialize internal signing structures + ncp_send_nds Send request for NDS function + ncp_change_conn_state Change NW 4 connection state +* Modifies ncp_open_temporary in lib/ncplib.c to use NDS login if + compiled with -DNDS_SUPPORT and server has NDS. +* Adds two error messages to ncplib_err.et +* Modifies lib/ncplib.c to generate packet signatures when wanted. +* Adds lib/ndslib.c with the following external used functions: + strlen_u Get length of unicode string + strcpy_uc Copy unicode string to normal string + strcpy_cu Copy normal string to unicode string + nds_get_server_name Get name and domain of current server + nds_get_tree_name Get current NDS tree name + nds_login_auth NDS login and authenticate to current server +* Adds mpilib.c, mpilib.h, usuals.h and platform.h from the PGP 2.3 + source to lib/ for the RSA encryption, which is necessary for NDS + login/authenticate. +* Adds lib/ndscrypt.c with hash and encrypt functions for NDS login. +* Adds lib/ncpsign.c with a MD4 hash function for packet signatures. +* Modifies lib/Makefile to add ncpsign.o, ndslib.o, mpilib.o and + ndscrypt.o to libncp. +* Modifies sutil/Makefile to add ndslib.o,mpilib.o,ndscrypt.o to libncp. +* Adds ncp_send_nds_frag, ncp_send_nds, ncp_change_conn_state to + sutil/ncplib.c +* Modifies sutil/ncpmount.c to: + give more verbose error message if mount(2) fails; + use NDS login if compiled with -DNDS_SUPPORT and server has NDS; + start packet signature generation. +(See below for changes to kernel-1.2/*) + + +Changes made by ncpfs-nds-sign-0.01.kernel.patch: + +* Modifies Makefile to add ncpsign_kernel.o to ncpfs.o +* Modifies inode.c to query whether the server wants packet signatures. +* Adds ioctls to start packet signature generation and to query + whether the server wants packet signing. +* Adds ncp_negotiate_size_and_options to ncplib_kernel.c. +* Modifies sock.c to sign packets when enabled. +* Adds ncpsign_kernel.c to generatie packet signatures. + +Arne de Bruijn, October 4, 1997 diff --git a/ncpfs-nds-0.06.README b/ncpfs-nds-0.06.README new file mode 100644 index 0000000..625d515 --- /dev/null +++ b/ncpfs-nds-0.06.README @@ -0,0 +1,42 @@ + + NDS support for ncpfs (linux) + + ncpfs is a NetWare client for the Linux operating system, maintained + by Volker Lendecke (lendecke@namu01.gwdg.de). I have written + support for NDS logins to NetWare 4 servers. + + Warning! NDS logins require the RSA public key algorithm, which is + patented in the U.S.A. and Canada, and possibly in other countries. + Because of this you may not be allowed to use this code. Check this + before you use NDS logins! + + Warning! The NDS support for ncpfs is in early beta stage, currently + I have only tested it on my own test NW 4.10 server. The NDS support + may not work for you, or even give problems like crashing your linux + box, or the NetWare server! Please apply the patch only if you know + what you are doing. Note that this version of the patch is not + little-endian free, so it won't work on big-endian architectures. + THIS PATCH IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER + EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS + WITH YOU. SHOULD THE PATCH PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL + NECESSARY SERVICING, REPAIR OR CORRECTION. + + This file contains a patch for the non-kernel part of ncpfs: + ncpfs-nds-0.05.patch. This is against ncpfs 2.0.11, but works the same + for ncpfs 2.0.10. + + Apply the patches by cd'ing into the appropriate source directory (e.g + /usr/local/src/ncpfs-2.0.11) and do: + + patch < patch-file + + You can disable NDS support after applying the patch by uncommenting + the line NDS_SUPPORT = 1 in the main Makefile from ncpfs. + + If you have comments or suggestinons about this patch please mail them + to me. + +Arne de Bruijn +arne@knoware.nl diff --git a/patches/README b/patches/README index 9ed8629..5db9985 100644 --- a/patches/README +++ b/patches/README @@ -8,5 +8,7 @@ lockup-2.0.30.diff: Please apply this patch to your 2.0.30 kernel to get rid of the ncpfs lockups. See ../BUGS for the symptoms. -linux-2.1.26.diff: -Little fix to make ncpfs work with 2.1.26. Sent to Linus. +Patches for other kernels to support new features must be downloaded +separately because of kernel changes have no relations to ncpfs changes. +Kernel patches should be available on same place as this package or +at ftp://platan.vc.cvut.cz/pub/linux/ncpfs/latest. diff --git a/patches/linux-2.1.26.diff b/patches/linux-2.1.26.diff deleted file mode 100644 index 3b6e7fb..0000000 --- a/patches/linux-2.1.26.diff +++ /dev/null @@ -1,35 +0,0 @@ ---- 2.1.26/fs/ncpfs/sock.c Sun Jan 26 11:07:44 1997 -+++ 2.1.26-patched/fs/ncpfs/sock.c Sun Feb 16 17:05:13 1997 -@@ -24,6 +24,7 @@ - #include - #include - #include -+#include - #include - - -@@ -343,7 +344,6 @@ - char *start = server->packet; - poll_table wait_table; - struct poll_table_entry entry; -- int (*select) (struct inode *, poll_table *); - int init_timeout, max_timeout; - int timeout; - int retrans; -@@ -362,7 +362,6 @@ - - file = server->ncp_filp; - inode = file->f_inode; -- select = file->f_op->poll; - sock = &inode->u.socket_i; - if (!sock) - { -@@ -418,7 +417,7 @@ - wait_table.nr = 0; - wait_table.entry = &entry; - current->state = TASK_INTERRUPTIBLE; -- if (!select(inode, &wait_table)) -+ if (!(file->f_op->poll(file, &wait_table) & POLLIN)) - { - if (timeout > max_timeout) - { diff --git a/sutil/Makefile b/sutil/Makefile index 638f2de..0ff7f43 100644 --- a/sutil/Makefile +++ b/sutil/Makefile @@ -6,6 +6,24 @@ UTILS = ncpmount ncpumount nwsfind CC = gcc +CFLAGS += -D__MAKE_NCPMOUNT__ + +ifdef NDS_SUPPORT +CFLAGS += -DNDS_SUPPORT +NDS_OBJ = ../lib/ndslib.o ../lib/mpilib.o ../lib/ndscrypt.o +endif +ifdef SIGNATURES +CFLAGS += -DSIGNATURES +SIGN_OBJ = ../lib/ncpsign.o +endif +ifdef MOUNT2 +CFLAGS += -DMOUNT2 +endif +ifdef MOUNT3 +# _GNU_SOURCE for environ variable +CFLAGS += -DMOUNT3 -D_GNU_SOURCE +endif + default: make -C .. @@ -21,8 +39,8 @@ $(UTILS): %: %.o libncp.a 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 +libncp.a: ncplib.o ../lib/ncplib_err.o $(SIGN_OBJ) $(NDS_OBJ) + ar r libncp.a ncplib.o ../lib/ncplib_err.o $(SIGN_OBJ) $(NDS_OBJ) dep: $(CPP) -M $(INCLUDES) *.c > .depend diff --git a/sutil/ipxlib.h b/sutil/ipxlib.h deleted file mode 100644 index bf16bf4..0000000 --- a/sutil/ipxlib.h +++ /dev/null @@ -1,98 +0,0 @@ -/* - * 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 index 6421b22..268316a 100644 --- a/sutil/ncplib.c +++ b/sutil/ncplib.c @@ -7,6 +7,9 @@ #include "ncplib.h" #include "ncplib_err.h" +#ifdef SIGNATURES +#include "ncpsign.h" +#endif #include /* #include *//* generates a warning here */ @@ -21,7 +24,7 @@ extern pid_t wait(int *); #include #include #include -#include +#include "kernel/route.h" #include #include #include @@ -29,9 +32,21 @@ extern pid_t wait(int *); #include #include +#define NCP_DEFAULT_BUFSIZE 1024 +#ifdef SIGNATURES +#define NCP_DEFAULT_OPTIONS 2 +int in_options = NCP_DEFAULT_OPTIONS; +#endif + static long ncp_negotiate_buffersize(struct ncp_conn *conn, int size, int *target); +#ifdef SIGNATURES +static long + ncp_negotiate_size_and_options(struct ncp_conn *conn, + int size, int options, + int *ret_size, int *ret_options); +#endif static long ncp_login_object(struct ncp_conn *conn, const unsigned char *username, @@ -71,7 +86,7 @@ ipx_fprint_node(FILE * file, IPXNode node) void ipx_fprint_network(FILE * file, IPXNet net) { - fprintf(file, "%08lX", ntohl(net)); + fprintf(file, "%08X", (u_int32_t)ntohl(net)); } void @@ -133,6 +148,44 @@ ipx_sscanf_node(char *buf, unsigned char node[6]) return 6; } +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; +} + void ipx_assign_node(IPXNode dest, IPXNode src) { @@ -490,6 +543,12 @@ do_ncp_call(struct ncp_conn *conn, int request_size) long err; memcpy(&request, conn->packet, sizeof(request)); +#ifdef SIGNATURES + if (conn->sign_active) + { + sign_packet(conn, &request_size); + } +#endif while (retries > 0) { @@ -543,7 +602,7 @@ do_ncp_call(struct ncp_conn *conn, int request_size) 0, 1, &err); goto re_select; } - ipx_recv(conn->ncp_sock, conn->packet, NCP_PACKET_SIZE, + len = ipx_recv(conn->ncp_sock, conn->packet, NCP_PACKET_SIZE, 0, 1, &err); conn->reply_size = len; return 0; @@ -629,6 +688,9 @@ ncp_connect_addr(struct ncp_conn *conn, const struct sockaddr_ipx *target, int ncp_sock, wdog_sock; long err; +#ifdef SIGNATURES + int options; +#endif conn->is_connected = NOT_CONNECTED; conn->verbose = 0; @@ -705,10 +767,29 @@ ncp_connect_addr(struct ncp_conn *conn, const struct sockaddr_ipx *target, BVAL(conn->packet, 3) + (BVAL(conn->packet, 5) << 8); conn->is_connected = CONN_TEMPORARY; - if ((ncp_negotiate_buffersize(conn, 1024, - &(conn->i.buffer_size)) != 0) +#ifdef SIGNATURES + conn->sign_wanted = 0; + conn->sign_active = 0; + + if ((err = ncp_negotiate_size_and_options(conn, NCP_DEFAULT_BUFSIZE, in_options, + &(conn->i.buffer_size), &options)) == 0) + { + if ((options & 2) != (in_options & 2)) + { + if ((err = ncp_negotiate_size_and_options(conn, + NCP_DEFAULT_BUFSIZE, options & 2, &(conn->i.buffer_size), &options)) == 0) + { + conn->sign_wanted = (options & 2) ?1:0; + } + } + } + else +#endif + err = ncp_negotiate_buffersize(conn, NCP_DEFAULT_BUFSIZE, + &(conn->i.buffer_size)); + if ((err != 0) || (conn->i.buffer_size < 512) - || (conn->i.buffer_size > 1024)) + || (conn->i.buffer_size > NCP_DEFAULT_BUFSIZE)) { ncp_do_close(conn); return -1; @@ -914,6 +995,16 @@ ncp_find_permanent(const struct ncp_conn_spec *spec) errno = (result == NULL) ? ENOENT : 0; return result; } +#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 static int ncp_open_permanent(struct ncp_conn *conn, @@ -951,6 +1042,9 @@ ncp_open_permanent(struct ncp_conn *conn, } strcpy(conn->mount_point, mount_point); conn->is_connected = CONN_PERMANENT; +#ifdef SIGNATURES + ncp_sign_init_perm(conn); +#endif return 0; } @@ -1024,6 +1118,9 @@ ncp_open_mount(const char *mount_point, long *err) *err = NCPL_ET_NO_NCPFS_FILE; return NULL; } +#ifdef SIGNATURES + ncp_sign_init_perm(result); +#endif return result; } @@ -1096,6 +1193,18 @@ ncp_close(struct ncp_conn *conn) return 0; } +int +ncp_get_mount_uid(int fid, uid_t* uid) +{ + __kernel_uid_t k_uid; + int err; + + err = ioctl(fid, NCP_IOC_GETMOUNTUID, &k_uid); + if (err) return err; + *uid = k_uid; + return 0; +} + struct ncp_conn_ent * ncp_get_conn_ent(FILE * filep) { @@ -1144,7 +1253,7 @@ ncp_get_conn_ent(FILE * filep) { continue; } - if (ioctl(fid, NCP_IOC_GETMOUNTUID, &entry.uid) != 0) + if (ncp_get_mount_uid(fid, &entry.uid) != 0) { close(fid); continue; @@ -1270,8 +1379,8 @@ ncp_fopen_nwc(const char *user, const char *mode, long *err) } 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) +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; @@ -1333,10 +1442,12 @@ ncp_find_conn_spec(const char *server, const char *user, const char *password, str_upper(spec.user); spec.login_type = NCP_BINDERY_USER; - if (ncp_open_permanent(&conn, &spec) == 0) - { - ncp_do_close(&conn); - return &spec; + if (!allow_multiple_conns) { + if (ncp_open_permanent(&conn, &spec) == 0) + { + ncp_do_close(&conn); + return &spec; + } } if (password != NULL) { @@ -1403,6 +1514,13 @@ ncp_find_conn_spec(const char *server, const char *user, const char *password, return &spec; } +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); +} + struct ncp_conn * ncp_initialize_as(int *argc, char **argv, int login_necessary, int login_type, long *err) @@ -1694,6 +1812,30 @@ ncp_negotiate_buffersize(struct ncp_conn *conn, ncp_unlock_conn(conn); return 0; } +#ifdef SIGNATURES +static long + ncp_negotiate_size_and_options(struct ncp_conn *conn, + int size, int options, + int *ret_size, int *ret_options) +{ + long result; + + ncp_init_request(conn); + ncp_add_word_hl(conn, size); + ncp_add_byte(conn, options); + + if ((result = ncp_request(conn, 0x61)) != 0) + { + ncp_unlock_conn(conn); + return result; + } + *ret_size = min(ncp_reply_word_hl(conn, 0), size); + *ret_options = ncp_reply_byte(conn, 4); + + ncp_unlock_conn(conn); + return 0; +} +#endif /* * target is a 8-byte buffer @@ -1799,6 +1941,15 @@ ncp_login_encrypted(struct ncp_conn *conn, result = ncp_request(conn, 23); ncp_unlock_conn(conn); +#ifdef SIGNATURES + if ((result == 0) || ((result == NCPL_ET_REQUEST_ERROR) && + (conn->completion == NCP_GRACE_PERIOD))) + { + memcpy(buf + 16, key, 8); + sign_init(buf, buf); + result = ncp_sign_start(conn, buf); + } +#endif return result; } @@ -1869,3 +2020,288 @@ ncp_login_object(struct ncp_conn *conn, } return 0; } +#ifdef SIGNATURES +long +ncp_sign_start(struct ncp_conn *conn, const char *sign_root) +{ + char init_last[16]={0x01,0x23,0x45,0x67,0x89,0xab,0xcd,0xef, + 0xfe,0xdc,0xba,0x98,0x76,0x54,0x32,0x10}; + struct ncp_sign_init sign_init; + + if (conn->sign_wanted) + { + memcpy(sign_init.sign_root, sign_root, 8); + memcpy(sign_init.sign_last, init_last, 16); + conn->sign_active = 1; + if (conn->is_connected == CONN_PERMANENT) + { + if (ioctl(conn->mount_fid, NCP_IOC_SIGN_INIT, + &sign_init)) + return NCPL_ET_SIGNATURE_FAILED; + } + else + { + memcpy(conn->sign_root, sign_init.sign_root, 8); + memcpy(conn->sign_last, sign_init.sign_last, 16); + } + + } + return 0; +} +#endif +#ifdef NDS_SUPPORT +long +ncp_send_nds_frag(struct ncp_conn *conn, + int ndsverb, + char *inbuf, int inbuflen, + char *outbuf, int outbufsize, int *outbuflen) +{ + long result; + int sizeleft, i; + int maxdatasize = 514; + int first = 1; + int fraghnd = -1; + + 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); + return result; + } + if (inbuflen) + { + if ((ncp_reply_dword_lh(conn, 0) != 4) || + ((fraghnd = ncp_reply_dword_lh(conn, 4)) == -1)) + result = NCPL_ET_REPLY_FORMAT; + ncp_unlock_conn(conn); + if (result) return result; + } + } while (inbuflen); + i = ncp_reply_dword_lh(conn, 0) - 8; + if ((i < 0) || (ncp_reply_dword_lh(conn, 4) != -1)) + { + ncp_unlock_conn(conn); + return NCPL_ET_REPLY_FORMAT; + } + if (i > outbufsize) + { + ncp_unlock_conn(conn); + return NCPL_ET_REPLY_TOO_LARGE; + } + if (outbuf) + { + memcpy(outbuf, ncp_reply_data(conn, 12), i); + if (outbuflen) *outbuflen = i; + } + if ((conn->completion = ncp_reply_dword_lh(conn, 8))) + result = NCPL_ET_REQUEST_ERROR; + ncp_unlock_conn(conn); + return result; +} + +long +ncp_send_nds(struct ncp_conn *conn, int fn, + char *data_in, int data_in_len, + char *data_out, int data_out_max, int *data_out_len) +{ + int 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) +{ + long err; + + ncp_init_request_s(conn, 0x1d); + ncp_add_dword_lh(conn, new_state); + err = ncp_request(conn, 0x17); + ncp_unlock_conn(conn); + return err; +} + +struct ncp_conn * +ncp_open_addr(struct sockaddr_ipx *target, long *err) +{ + struct ncp_conn *conn; + + if (!(conn = malloc(sizeof(struct ncp_conn)))) + { + *err = ENOMEM; + return NULL; + } + memzero(*conn); + + if ((*err = ncp_connect_addr(conn, target, 1))) + { + free(conn); + return NULL; + } + return conn; +} +#endif + +long +ncp_get_broadcast_message (struct ncp_conn *conn, char message[256]) +{ + long result; + int length; + + 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; +} + +static void +ncp_add_handle_path2(struct ncp_conn *conn, + __u8 vol_num, + __u32 dir_base, int dir_style, + const unsigned char *encpath, int pathlen) +{ + ncp_add_byte(conn, vol_num); + ncp_add_dword_lh(conn, dir_base); + ncp_add_byte(conn, dir_style); /* 1 = dir_base, 0xFF = no handle, 0 = handle */ + ncp_add_mem(conn, encpath, pathlen); +} + +int +ncp_path_to_NW_format(const char* path, unsigned char* buff, int buffsize) +{ + int components = 0; + unsigned char* pos = buff+1; + buffsize--; + + if (path != NULL) { + if (*path == '/') path++; /* skip optional leading / */ + while (*path) { + const char *c; + const char *d; + int l; + + c = strchr(path, '/'); + if (!c) c=path+strlen(path); + l = c-path; + if (components == 0) { /* volume */ + d = strchr(path, ':'); /* can be separated by :, / or :/ */ + if (!d) d=path+strlen(path); + if (d < c) { + c=d; + if (c[1]=='/') c++; /* skip optional / after : */ + l = d-path; + } + } + if (l == 0) + return -EINVAL; + if (l > 255) + return -ENAMETOOLONG; + if ((l != 1)||(*path!='.')) { + buffsize -= l; + if (buffsize <= 0) return -ENOBUFS; + *pos++ = l; + memcpy(pos, path, l); + pos+=l; + components++; + } + path = c; + if (!*c) break; + path++; + } + } + *buff = components; + return pos-buff; +} + +static void +ncp_extract_file_info(void *structure, struct nw_info_struct *target) +{ + __u8 *name_len; + const int info_struct_size = sizeof(struct nw_info_struct) - 257; + + memcpy(target, structure, info_struct_size); + name_len = structure + info_struct_size; + target->nameLen = *name_len; + strncpy(target->entryName, name_len + 1, *name_len); + target->entryName[*name_len] = '\0'; + return; +} + +long +ncp_obtain_file_or_subdir_info2(struct ncp_conn *conn, + __u8 source_ns, __u8 target_ns, + __u16 search_attribs, __u32 rim, + int dir_style, + __u8 vol, __u32 dirent, const unsigned char *path, + int pathlen, struct nw_info_struct *target) +{ + long result; + + ncp_init_request(conn); + ncp_add_byte(conn, 6); + ncp_add_byte(conn, source_ns); + ncp_add_byte(conn, target_ns); + ncp_add_word_lh(conn, search_attribs); + ncp_add_dword_lh(conn, rim); + ncp_add_handle_path2(conn, vol, dirent, dir_style, path, pathlen); + + if ((result = ncp_request(conn, 87)) != 0) + { + ncp_unlock_conn(conn); + return result; + } + ncp_extract_file_info(ncp_reply_data(conn, 0), target); + ncp_unlock_conn(conn); + return 0; +} + + diff --git a/sutil/ncplib.h b/sutil/ncplib.h index a5f893b..f5aa113 100644 --- a/sutil/ncplib.h +++ b/sutil/ncplib.h @@ -8,10 +8,8 @@ #ifndef _NCPLIB_H #define _NCPLIB_H -#include -#include -#include -#include +#include +#include "ncp.h" #include #include #include @@ -150,6 +148,13 @@ struct ncp_conn int lock; char packet[NCP_PACKET_SIZE]; +#ifdef SIGNATURES + /* Field used to make packet signatures */ + int sign_wanted; + int sign_active; + char sign_root[8]; + char sign_last[16]; +#endif }; struct ncp_conn_spec @@ -236,6 +241,11 @@ 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); +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); + long ncp_get_encryption_key(struct ncp_conn *conn, char *target); @@ -273,6 +283,7 @@ long int segment, const char *prop_name, struct nw_property *target); +#define NWE_SIGNATURE_LEVEL_CONFLICT (0x8861) #define NCP_GRACE_PERIOD (0xdf) long @@ -286,4 +297,43 @@ long const unsigned char *username, const unsigned char *password); +long + ncp_get_broadcast_message(struct ncp_conn *conn, char message[256]); +#ifdef SIGNATURES +long +ncp_sign_start(struct ncp_conn *conn, const char *sign_root); +#endif +#ifdef NDS_SUPPORT +long +ncp_send_nds_frag(struct ncp_conn *conn, + int ndsverb, + char *inbuf, int inbuflen, + char *outbuf, int outbufsize, int *outbuflen); + +long +ncp_send_nds(struct ncp_conn *conn, int fn, + char *data_in, int data_in_len, + char *data_out, int data_out_max, int *data_out_len); + +long +ncp_change_conn_state(struct ncp_conn *conn, int new_state); + +struct ncp_conn * +ncp_open_addr(struct sockaddr_ipx *target, long *err); +#endif + +int +ncp_path_to_NW_format(const char* path, unsigned char* buff, int buffsize); + +long +ncp_obtain_file_or_subdir_info2(struct ncp_conn* conn, __u8 source_ns, + __u8 target_ns, __u16 search_attribs, __u32 rim, + int dir_style, __u8 vol, __u32 dirent, + const unsigned char* path, int pathlen, + struct nw_info_struct* target); + + +int +ncp_get_mount_uid(int fid, uid_t* uid); + #endif /* _NCPLIB_H */ diff --git a/sutil/ncpmount.c b/sutil/ncpmount.c index b00f383..12c6502 100644 --- a/sutil/ncpmount.c +++ b/sutil/ncpmount.c @@ -1,7 +1,7 @@ /* - * nwmount.c + * ncpmount.c * - * Copyright (C) 1995 by Volker Lendecke + * Copyright (C) 1995, 1997 by Volker Lendecke * * 1/20/96 - Steven N. Hirsch (hirsch@emba.uvm.edu) * @@ -26,7 +26,6 @@ #include #include #include - /* #include *//* generates a warning here */ extern pid_t waitpid(pid_t, int *, int); #include #include @@ -37,22 +36,38 @@ extern pid_t waitpid(pid_t, int *, int); #include #include #include -#include +#include "kernel/ipx.h" #include +#ifdef MOUNT3 +#include +#include +#endif -#include -#include -#include -#include +#include "kernel/ncp.h" +#include "kernel/ncp_fs.h" +#include "ncpmount.h" #include "ncplib.h" #include "com_err.h" +#include "ncplib_err.h" +#ifdef NDS_SUPPORT +#include "ndslib.h" +#endif + +#if defined(MOUNT2) && defined(MOUNT3) +#define MULTIVERSION_MOUNT 1 +#else +#define MULTIVERSION_MOUNT 0 +#endif + +#if MULTIVERSION_MOUNT +#include +#endif static char *progname; +static char mount_point[MAXPATHLEN + 1]; static void usage(void); static void help(void); -#ifndef HAVE_KERNELD - /* Returns 0 if the filesystem is in the kernel after this routine completes */ static int @@ -116,7 +131,51 @@ load_ncpfs(void) return status; } -#endif /* HAVE_KERNELD */ +#ifdef MOUNT3 +static int process_connection(int wdog_fd, int msg_fd); +#endif + +#if MULTIVERSION_MOUNT +int getmountver(void) { + struct utsname name; + int maj, mid; + int ver; + + if (uname(&name)) { + fprintf(stderr, "Cannot get kernel release\n"); + exit(1); + } + if (sscanf(name.release, "%d.%d", &maj, &mid) != 2) { + fprintf(stderr, "Cannot convert kernel release \"%s\" to number\n", name.release); + exit(1); + } + ver = maj*0x10000 + mid*0x100; + if (ver < 0x20100) { +#ifdef MOUNT2 + return 2; +#else + fprintf(stderr, "This kernel requires mount version 2 which is not available\n"); + exit(1); +#endif + } +#ifdef MOUNT3 + return 3; +#else + fprintf(stderr, "This kernel requires mount version 3 which is not available\n"); + exit(1); +#endif +} +#else +#ifdef MOUNT3 +#define getmountver() 3 +#else +#ifdef MOUNT2 +#define getmountver() 2 +#else +#error "You must define at least one of MOUNT2, MOUNT3" +#endif +#endif +#endif /* Check whether user is allowed to mount on the specified mount point */ static int @@ -137,12 +196,187 @@ mount_ok(struct stat *st) return 0; } +#ifdef MOUNT3 +/* + * This function changes the processes name as shown by the system. + * Stolen from Marcin Dalecki's modald :-) + */ +static void inststr(char *dst[], int argc, char *src) +{ + /* stolen from the source to perl 4.036 (assigning to $0) */ + char *ptr, *ptr2; + int count; + ptr = dst[0] + strlen(dst[0]); + for (count = 1; count < argc; count++) { + if (dst[count] == ptr + 1) + ptr += strlen(++ptr); + } + if (environ[0] == ptr + 1) { + for (count = 0; environ[count]; count++) + if (environ[count] == ptr + 1) + ptr += strlen(++ptr); + } + count = 0; + for (ptr2 = dst[0]; ptr2 <= ptr; ptr2++) { + *ptr2 = '\0'; + count++; + } + strncpy(dst[0], src, count); + for (count = 1; count < argc; count++) { + dst[count] = NULL; + } +} +#endif +struct ncp_mount_data_independent { + unsigned int ncp_fd; + unsigned int wdog_fd; + unsigned int message_fd; + uid_t mounted_uid; + struct sockaddr_ipx serv_addr; + unsigned char *server_name; + unsigned char *mount_point; + unsigned char *mounted_vol; + unsigned int time_out; + unsigned int retry_count; + struct { + unsigned int mount_soft:1; + unsigned int mount_intr:1; + unsigned int mount_strong:1; + unsigned int mount_no_os2:1; + unsigned int mount_no_nfs:1; + } flags; + uid_t uid; + gid_t gid; + mode_t file_mode; + mode_t dir_mode; +}; + +#ifdef MOUNT2 +int ncp_mount_v2(const char* mount_name, unsigned long flags, const struct ncp_mount_data_independent* data) { + struct ncp_mount_data_v2 datav2; + + datav2.version = NCP_MOUNT_VERSION_V2; + datav2.ncp_fd = data->ncp_fd; + datav2.wdog_fd = data->wdog_fd; + datav2.message_fd = data->message_fd; + datav2.mounted_uid = data->mounted_uid; + memcpy(&datav2.serv_addr, &data->serv_addr, sizeof(datav2.serv_addr)); + strncpy(datav2.server_name, data->server_name, sizeof(datav2.server_name)); + strncpy(datav2.mount_point, data->mount_point, sizeof(datav2.mount_point)); + strncpy(datav2.mounted_vol, data->mounted_vol, sizeof(datav2.mounted_vol)); + datav2.time_out = data->time_out; + datav2.retry_count = data->retry_count; + datav2.flags = 0; + datav2.flags |= data->flags.mount_soft?NCP_MOUNT2_SOFT:0; + datav2.flags |= data->flags.mount_intr?NCP_MOUNT2_INTR:0; + datav2.flags |= data->flags.mount_strong?NCP_MOUNT2_STRONG:0; + datav2.flags |= data->flags.mount_no_os2?NCP_MOUNT2_NO_OS2:0; + datav2.flags |= data->flags.mount_no_nfs?NCP_MOUNT2_NO_NFS:0; + datav2.uid = data->uid; + datav2.gid = data->gid; + datav2.file_mode = data->file_mode; + datav2.dir_mode = data->dir_mode; + return mount(mount_name, data->mount_point, "ncpfs", flags, (void*) &datav2); +} +#endif + +#ifdef MOUNT3 +int ncp_mount_v3(const char* mount_name, unsigned long flags, const struct ncp_mount_data_independent* data, int argc, char* argv[]) { + struct ncp_mount_data_v3 datav3; + int err; + + datav3.version = NCP_MOUNT_VERSION_V3; + datav3.ncp_fd = data->ncp_fd; + datav3.mounted_uid = data->mounted_uid; + strncpy(datav3.mounted_vol, data->mounted_vol, sizeof(datav3.mounted_vol)); + datav3.time_out = data->time_out; + datav3.retry_count = data->retry_count; + datav3.flags = 0; + datav3.flags |= data->flags.mount_soft?NCP_MOUNT3_SOFT:0; + datav3.flags |= data->flags.mount_intr?NCP_MOUNT3_INTR:0; + datav3.flags |= data->flags.mount_strong?NCP_MOUNT3_STRONG:0; + datav3.flags |= data->flags.mount_no_os2?NCP_MOUNT3_NO_OS2:0; + datav3.flags |= data->flags.mount_no_nfs?NCP_MOUNT3_NO_NFS:0; + datav3.uid = data->uid; + datav3.gid = data->gid; + datav3.file_mode = data->file_mode; + datav3.dir_mode = data->dir_mode; + connect(datav3.ncp_fd, (struct sockaddr *)&data->serv_addr, sizeof(data->serv_addr)); + datav3.wdog_pid = fork(); + if (datav3.wdog_pid < 0) { + fprintf(stderr, "could not fork: %s\n", strerror(errno)); + exit(1); + } + if (datav3.wdog_pid == 0) { + /* Child */ + inststr(argv, argc, "ncpd"); + process_connection(data->wdog_fd, data->message_fd); + exit(0); /* Should not return from process_connection */ + } + err=mount(mount_name, data->mount_point, "ncpfs", flags, (void*) &datav3); + if (err) { + /* Mount unsuccesful so we have to kill daemon */ + /* If mount success, kernel kills daemon (at least in 2.1.79) */ + kill(datav3.wdog_pid, SIGTERM); + } + return err; +} +#endif + +int +ncp_mount_specific(struct ncp_conn* conn, int pathNS, const unsigned char* NWpath, int pathlen) { + int result; + +#ifdef NCP_IOC_SETROOT + { + struct ncp_setroot_ioctl sr; + + if (pathlen == 1) { + sr.volNumber = -1; + sr.namespace = -1; + sr.dirEntNum = 0; + } else { + struct nw_info_struct dirinfo; + + result = ncp_obtain_file_or_subdir_info2(conn, pathNS, NW_NS_DOS, + 0x8006, RIM_ALL, 0xFF, 0, 0, NWpath, pathlen, &dirinfo); + if (result) { + return -ENOENT; + } + if (!(dirinfo.attributes & htonl(0x10000000))) { + return -ENOTDIR; + } + sr.volNumber = dirinfo.volNumber; + sr.namespace = NW_NS_DOS; + sr.dirEntNum = dirinfo.dirEntNum; + } + result = ioctl(conn->mount_fid, NCP_IOC_SETROOT, &sr); + if (!result) { + return 0; + } + } +#endif + if ((pathlen != 1) && (*NWpath != 1)) { + fprintf(stderr, "Remote directory is specified but " +#ifdef NCP_IOC_SETROOT + "kernel" +#else + "ncpmount" +#endif + " does not support subdir mounts\n"); + return -ENOPKG; + } + if (ioctl(conn->mount_fid, NCP_IOC_CONN_LOGGED_IN, NULL) != 0) { + return -errno; + } + return 0; +} int main(int argc, char *argv[]) { - struct ncp_mount_data data; + struct ncp_mount_data_independent mdata; struct stat st; char mount_name[256]; @@ -157,46 +391,75 @@ main(int argc, char *argv[]) int um; unsigned int flags; - char mount_point[MAXPATHLEN]; struct mntent ment; FILE *mtab; - char *tmp_mount; char *server = NULL; char *user = NULL; char *password = NULL; - struct ncp_conn_spec *spec; - uid_t conn_uid = getuid(); + struct ncp_conn_spec *spec; struct ncp_conn *conn; int opt; +#ifdef NDS_SUPPORT + int force_bindery_login = 0; +#endif + + char *tmp_mount; + int allow_multiple_connections = 0; + + int mount_protocol_version = -1; + + const char* remotepath = "/"; + unsigned char NWpath[512]; + int pathlen = 1; + NWpath[0] = 0; + progname = argv[0]; - memzero(data); + memzero(mdata); memzero(spec); + mdata.mounted_uid = getuid(); + if (geteuid() != 0) { fprintf(stderr, "%s must be installed suid root\n", progname); exit(1); } - data.uid = getuid(); - data.gid = getgid(); + mdata.uid = getuid(); + mdata.gid = getgid(); um = umask(0); umask(um); - data.file_mode = (S_IRWXU | S_IRWXG | S_IRWXO) & ~um; - data.dir_mode = 0; - data.flags |= NCP_MOUNT_SOFT; - data.time_out = 60; - data.retry_count = 5; + mdata.file_mode = (S_IRWXU | S_IRWXG | S_IRWXO) & ~um; + mdata.dir_mode = 0; + mdata.flags.mount_soft = 1; + mdata.flags.mount_intr = 0; + mdata.flags.mount_strong = 0; + mdata.flags.mount_no_os2 = 0; + mdata.flags.mount_no_nfs = 0; + mdata.time_out = 60; + mdata.retry_count = 5; + mdata.mounted_vol = ""; upcase_password = 1; - while ((opt = getopt(argc, argv, "CS:U:c:u:g:f:d:P:nh?vV:t:r:")) - != EOF) + while ((opt = getopt(argc, argv, "CS:U:c:u:g:f:d:P:nh?vV:t:r:" + "sN:" +#ifdef NDS_SUPPORT + "b" +#endif + "m" +#ifdef MOUNT2 + "2" +#endif +#ifdef MOUNT3 + "3" +#endif + )) != EOF) { switch (opt) { @@ -213,18 +476,12 @@ main(int argc, char *argv[]) server = optarg; break; case 'U': - if (strlen(optarg) >= sizeof(spec->user)) - { - fprintf(stderr, "Username too long: %s\n", - optarg); - return 1; - } user = optarg; break; case 'c': if (isdigit(optarg[0])) { - conn_uid = atoi(optarg); + mdata.mounted_uid = atoi(optarg); } else { struct passwd *pwd = getpwnam(optarg); @@ -234,13 +491,13 @@ main(int argc, char *argv[]) optarg); return 1; } - conn_uid = pwd->pw_uid; + mdata.mounted_uid = pwd->pw_uid; } break; case 'u': if (isdigit(optarg[0])) { - data.uid = atoi(optarg); + mdata.uid = atoi(optarg); } else { struct passwd *pwd = getpwnam(optarg); @@ -250,13 +507,13 @@ main(int argc, char *argv[]) optarg); return 1; } - data.uid = pwd->pw_uid; + mdata.uid = pwd->pw_uid; } break; case 'g': if (isdigit(optarg[0])) { - data.gid = atoi(optarg); + mdata.gid = atoi(optarg); } else { struct group *grp = getgrnam(optarg); @@ -266,14 +523,14 @@ main(int argc, char *argv[]) optarg); return 1; } - data.gid = grp->gr_gid; + mdata.gid = grp->gr_gid; } break; case 'f': - data.file_mode = strtol(optarg, NULL, 8); + mdata.file_mode = strtol(optarg, NULL, 8); break; case 'd': - data.dir_mode = strtol(optarg, NULL, 8); + mdata.dir_mode = strtol(optarg, NULL, 8); break; case 'P': if (strlen(optarg) >= sizeof(spec->password)) @@ -284,21 +541,32 @@ main(int argc, char *argv[]) password = optarg; break; case 'V': - if (strlen(optarg) >= sizeof(data.mounted_vol)) - { + pathlen = ncp_path_to_NW_format(optarg, NWpath, sizeof(NWpath)); + remotepath = optarg; + if (pathlen < 0) { + fprintf(stderr, "Volume path invalid: %s\n", strerror(-pathlen)); + exit(1); + }; + if (pathlen == 1) { + mdata.mounted_vol = ""; + remotepath = "/"; + } else if (*NWpath != 1) { + mdata.mounted_vol = "dummy"; + } else if (strlen(optarg) > NCP_VOLNAME_LEN) { printf("Volume too long: %s\n", optarg); exit(1); + } else { + mdata.mounted_vol=optarg; } - strcpy(data.mounted_vol, optarg); break; case 'n': password = ""; break; case 't': - data.time_out = atoi(optarg); + mdata.time_out = atoi(optarg); break; case 'r': - data.retry_count = atoi(optarg); + mdata.retry_count = atoi(optarg); break; case 'h': case '?': @@ -307,13 +575,58 @@ main(int argc, char *argv[]) case 'v': fprintf(stderr, "ncpfs version %s\n", NCPFS_VERSION); exit(1); + case 's': + mdata.flags.mount_strong = 1; + break; +#ifdef NDS_SUPPORT + case 'b': + force_bindery_login = 1; + break; +#endif + case 'm': + allow_multiple_connections = 1; + break; +#ifdef MOUNT2 + case '2': + mount_protocol_version = 2; + break; +#endif +#ifdef MOUNT3 + case '3': + mount_protocol_version = 3; + break; +#endif + case 'N': + { + char *inp = optarg; + char *out; + + while ((out = strtok(inp, ",;:"))!=NULL) { + inp=NULL; + if (!strcasecmp(out, "OS2")) + mdata.flags.mount_no_os2=1; + else if (!strcasecmp(out, "LONG")) + mdata.flags.mount_no_os2=1; + else if (!strcasecmp(out, "NFS")) + mdata.flags.mount_no_nfs=1; + else { + fprintf(stderr, "Unknown namespace \"%s\"\n", out); + return 128; + } + } + }; + break; + default: usage(); return -1; } } - - if ((spec = ncp_find_conn_spec(server, user, password, 1, data.uid, &err)) + + if (mount_protocol_version < 0) { + mount_protocol_version = getmountver(); + } + if ((spec = ncp_find_conn_spec2(server, user, password, 1, mdata.uid, allow_multiple_connections, &err)) == NULL) { com_err(progname, err, "in find_conn_spec"); @@ -342,7 +655,6 @@ main(int argc, char *argv[]) mount_point, strerror(errno)); exit(1); } -#ifndef HAVE_KERNELD /* Check if the ncpfs filesystem is in the kernel. If not, attempt * to load the ncpfs module */ if (load_ncpfs() != 0) @@ -350,25 +662,22 @@ main(int argc, char *argv[]) fprintf(stderr, "Error: Unable to load ncpfs, exiting...\n"); exit(1); } -#endif - data.version = NCP_MOUNT_VERSION; - data.mounted_uid = conn_uid; - memcpy(data.server_name, spec->server, sizeof(data.server_name)); + mdata.server_name = spec->server; - if (data.dir_mode == 0) + if (mdata.dir_mode == 0) { - data.dir_mode = data.file_mode; - if ((data.dir_mode & S_IRUSR) != 0) - data.dir_mode |= S_IXUSR; - if ((data.dir_mode & S_IRGRP) != 0) - data.dir_mode |= S_IXGRP; - if ((data.dir_mode & S_IROTH) != 0) - data.dir_mode |= S_IXOTH; + mdata.dir_mode = mdata.file_mode; + if ((mdata.dir_mode & S_IRUSR) != 0) + mdata.dir_mode |= S_IXUSR; + if ((mdata.dir_mode & S_IRGRP) != 0) + mdata.dir_mode |= S_IXGRP; + if ((mdata.dir_mode & S_IROTH) != 0) + mdata.dir_mode |= S_IXOTH; } - if ((tmp_mount = ncp_find_permanent(spec)) != NULL) + if ((!allow_multiple_connections)&& + ((tmp_mount = ncp_find_permanent(spec)) != NULL)) { - fprintf(stderr, "You already have mounted server %s\nas user " "%s\non mount point %s\n", spec->server, spec->user, @@ -381,25 +690,26 @@ main(int argc, char *argv[]) spec->server); exit(1); } - data.serv_addr = *server_addr; + mdata.serv_addr = *server_addr; - data.ncp_fd = socket(AF_IPX, SOCK_DGRAM, PF_IPX); - if (data.ncp_fd == -1) + mdata.ncp_fd = socket(AF_IPX, SOCK_DGRAM, PF_IPX); + if (mdata.ncp_fd == -1) { com_err("ncpmount", err, "opening ncp_socket"); exit(1); } - data.wdog_fd = socket(AF_IPX, SOCK_DGRAM, PF_IPX); - if (data.wdog_fd == -1) + mdata.wdog_fd = socket(AF_IPX, SOCK_DGRAM, PF_IPX); + if (mdata.wdog_fd == -1) { fprintf(stderr, "could not open wdog socket: %s\n", strerror(errno)); exit(1); } memzero(addr); + addr.sipx_family = AF_IPX; addr.sipx_type = NCP_PTYPE; - if (bind(data.ncp_fd, (struct sockaddr *) &addr, sizeof(addr)) == -1) + if (bind(mdata.ncp_fd, (struct sockaddr *) &addr, sizeof(addr)) == -1) { fprintf(stderr, "\nbind: %s\n", strerror(errno)); @@ -411,25 +721,25 @@ main(int argc, char *argv[]) } addrlen = sizeof(addr); - if (getsockname(data.ncp_fd, (struct sockaddr *) &addr, &addrlen) == -1) + if (getsockname(mdata.ncp_fd, (struct sockaddr *) &addr, &addrlen) == -1) { perror("getsockname ncp socket"); - close(data.ncp_fd); - close(data.wdog_fd); + close(mdata.ncp_fd); + close(mdata.wdog_fd); exit(1); } addr.sipx_port = htons(ntohs(addr.sipx_port) + 1); - if (bind(data.wdog_fd, (struct sockaddr *) &addr, sizeof(addr)) == -1) + if (bind(mdata.wdog_fd, (struct sockaddr *) &addr, sizeof(addr)) == -1) { fprintf(stderr, "bind(wdog_sock, ): %s\n", strerror(errno)); exit(1); } -#if NCP_MOUNT_VERSION>1 +#if 1 - data.message_fd = socket(AF_IPX, SOCK_DGRAM, PF_IPX); - if (data.message_fd == -1) + mdata.message_fd = socket(AF_IPX, SOCK_DGRAM, PF_IPX); + if (mdata.message_fd == -1) { fprintf(stderr, "could not open message socket: %s\n", strerror(errno)); @@ -437,16 +747,13 @@ main(int argc, char *argv[]) } addr.sipx_port = htons(ntohs(addr.sipx_port) + 1); - if (bind(data.message_fd, (struct sockaddr *) &addr, sizeof(addr)) == -1) + if (bind(mdata.message_fd, (struct sockaddr *) &addr, sizeof(addr)) == -1) { fprintf(stderr, "bind(message_sock, ): %s\n", strerror(errno)); exit(1); } - if (strlen(mount_point) < sizeof(data.mount_point)) - { - strcpy(data.mount_point, mount_point); - } + mdata.mount_point = mount_point; #endif flags = MS_MGC_VAL; @@ -455,11 +762,24 @@ main(int argc, char *argv[]) strcat(mount_name, "/"); strcat(mount_name, spec->user); - result = mount(mount_name, mount_point, "ncpfs", flags, (char *) &data); - + switch (mount_protocol_version) { +#ifdef MOUNT2 + case 2: + result = ncp_mount_v2(mount_name, flags, &mdata); + break; +#endif +#ifdef MOUNT3 + case 3: + result = ncp_mount_v3(mount_name, flags, &mdata, argc, argv); + break; +#endif + default: + fprintf(stderr, "Unsupported mount protocol version %d\n", mount_protocol_version); + exit(2); + } if (result < 0) { - printf("mount failed\n"); + com_err("ncpmount", errno, "in mount(2)"); exit(1); } if ((conn = ncp_open_mount(mount_point, &err)) == NULL) @@ -468,6 +788,28 @@ main(int argc, char *argv[]) umount(mount_point); exit(1); } +#ifdef NDS_SUPPORT + if ((!force_bindery_login) && (!nds_get_tree_name(conn, NULL, 0))) + { + if ((err = nds_login_auth(conn, spec->user, spec->password))) + { + if ((err != NCPL_ET_REQUEST_ERROR) || + (conn->completion != NDS_GRACE_PERIOD)) { + com_err("ncpmount", err, "in nds login"); + if (err == NCPL_ET_REQUEST_ERROR) + fprintf(stderr, "NDS error code %d.\n", + conn->completion); + fprintf(stderr, "Login denied.\n"); + ncp_close(conn); + umount(mount_point); + exit(1); + } + fprintf(stderr, "Your password has expired\n"); + } + } + else + { +#endif if ((err = ncp_login_user(conn, spec->user, spec->password)) != 0) { struct nw_property p; @@ -492,9 +834,12 @@ main(int argc, char *argv[]) l->GraceLogins); } } - if ((err = ioctl(conn->mount_fid, NCP_IOC_CONN_LOGGED_IN, NULL)) != 0) +#ifdef NDS_SUPPORT + } +#endif + if ((err = ncp_mount_specific(conn, NW_NS_DOS, NWpath, pathlen)) != 0) { - com_err("ncpmount", err, "in logged_indication"); + fprintf(stderr, "Cannot access path \"%s\": %s\n", remotepath, strerror(-err)); ncp_close(conn); umount(mount_point); exit(1); @@ -540,8 +885,7 @@ main(int argc, char *argv[]) return 0; } -static void -usage(void) +static void usage(void) { printf("usage: %s [options] mount-point\n", progname); printf("Try `%s -h' for more information\n", progname); @@ -570,7 +914,261 @@ help(void) "-n Do not use any password\n" " If neither -P nor -n are given, you are\n" " asked for a password.\n" + "-s Enable renaming/deletion of read-only files\n" "-h print this help text\n" "-v print ncpfs version number\n" +#ifdef NDS_SUPPORT + "-b Force bindery login to NDS servers\n" +#endif + "-m Allow multiple logins to server\n" + "-N os2,nfs Do not use specified namespaces on mounted volume\n" "\n"); } + +#ifdef MOUNT3 + +/* The following routines have been taken from util-linux-2.5's write.c */ + +/* + * term_chk - check that a terminal exists, and get the message bit + * and the access time + */ +static int +term_chk (char *tty, int *msgsokP, time_t * atimeP, int *showerror) +{ + struct stat s; + char path[MAXPATHLEN]; + + (void) sprintf(path, "/dev/%s", tty); + if (stat(path, &s) < 0) { + if (showerror) + (void) fprintf(stderr, + "write: %s: %s\n", path, strerror(errno)); + return (1); + } + *msgsokP = (s.st_mode & (S_IWRITE >> 3)) != 0; /* group write bit */ + *atimeP = s.st_atime; + return (0); +} + +/* + * search_utmp - search utmp for the "best" terminal to write to + * + * Ignores terminals with messages disabled, and of the rest, returns + * the one with the most recent access time. Returns as value the number + * of the user's terminals with messages enabled, or -1 if the user is + * not logged in at all. + * + * Special case for writing to yourself - ignore the terminal you're + * writing from, unless that's the only terminal with messages enabled. + */ +static int +search_utmp (char *user, char *tty) +{ + struct utmp u; + time_t bestatime, atime; + int ufd, nloggedttys, nttys, msgsok, user_is_me; + + char atty[sizeof (u.ut_line) + 1]; + + if ((ufd = open (_PATH_UTMP, O_RDONLY)) < 0) + { + perror ("utmp"); + return -1; + } + nloggedttys = nttys = 0; + bestatime = 0; + user_is_me = 0; + while (read (ufd, (char *) &u, sizeof (u)) == sizeof (u)) + if (strncmp (user, u.ut_name, sizeof (u.ut_name)) == 0) + { + ++nloggedttys; + + (void) strncpy (atty, u.ut_line, sizeof (u.ut_line)); + atty[sizeof (u.ut_line)] = '\0'; + + if (term_chk (atty, &msgsok, &atime, 0)) + continue; /* bad term? skip */ + if (!msgsok) + continue; /* skip ttys with msgs off */ + + if (u.ut_type != USER_PROCESS) + continue; /* it's not a valid entry */ + + ++nttys; + if (atime > bestatime) + { + bestatime = atime; + (void) strcpy (tty, atty); + } + } + (void) close (ufd); + if (nloggedttys == 0) + { + (void) fprintf (stderr, "write: %s is not logged in\n", user); + return -1; + } + return 0; +} + +static void +msg_received (void) +{ + struct ncp_conn *conn; + char message[256]; + struct ncp_fs_info info; + struct passwd *pwd; + char tty[256]; + char tty_path[256]; + FILE *tty_file; + FILE *mtab; + struct mntent *mnt; + long err; + + openlog ("nwmsg", LOG_PID, LOG_LPR); + + if ((conn = ncp_open_mount(mount_point, &err)) == NULL) { + return; + } + if (ncp_get_broadcast_message(conn, message) != 0) { + ncp_close(conn); + return; + } + if (strlen(message) == 0) { + syslog(LOG_DEBUG, "no message"); + ncp_close(conn); + return; + } + syslog(LOG_DEBUG, "message: %s", message); + + info.version = NCP_GET_FS_INFO_VERSION; + if (ioctl(conn->mount_fid, NCP_IOC_GET_FS_INFO, &info) < 0) { + ncp_close(conn); + return; + } + ncp_close(conn); + + if ((pwd = getpwuid(info.mounted_uid)) == NULL) { + fprintf(stderr, "%s: user %d not known\n", + progname, info.mounted_uid); + return; + } + if ((mtab = fopen(MOUNTED, "r")) == NULL) { + fprintf(stderr, "%s: can't open %s\n", + progname, MOUNTED); + return; + } + while ((mnt = getmntent(mtab)) != NULL) { + if (strcmp(mnt->mnt_dir, mount_point) == 0) { + break; + } + } + + if (mnt == NULL) { + syslog(LOG_DEBUG, "cannot find mtab entry\n"); + } + if (search_utmp(pwd->pw_name, tty) != 0) { + return; + } + sprintf(tty_path, "/dev/%s", tty); + if ((tty_file = fopen(tty_path, "w")) == NULL) { + fprintf(stderr, "%s: cannot open %s: %s\n", + progname, tty_path, strerror(errno)); + return; + } + fprintf(tty_file, "\r\n\007\007\007Message from NetWare Server: %s\r\n", + mnt->mnt_fsname); + fprintf(tty_file, "%s\r\n", message); + fclose(tty_file); + fclose(mtab); + return; +} + +/* MSG_DONTWAIT defined and module can run only on 2.1.x kernel */ +#if defined(MSG_DONTWAIT) && !defined(MOUNT2) +#define recvfrom_notm(fd,buf,ln,sender,addrlen) recvfrom(fd,buf,ln,MSG_DONTWAIT,sender,addrlen) +#else +int recvfrom_notm(int fd, void* buf, size_t len, struct sockaddr* sender, size_t* addrlen) { + int ret; + int flg; + + flg = fcntl(fd, F_GETFL); + if (flg == -1) return -1; + fcntl(fd, F_SETFL, flg | O_NONBLOCK); + ret = recvfrom(fd, buf, len, 0, sender, addrlen); + fcntl(fd, F_SETFL, flg); + return ret; +} +#endif + +static void +process_msg_packet (int msg_fd) +{ + struct sockaddr_ipx sender; + int addrlen = sizeof(sender); + char buf[1024]; + + if (recvfrom_notm(msg_fd, buf, sizeof(buf), + (struct sockaddr *) &sender, &addrlen) <= 0) { + return; + } + msg_received(); +} + +static void +process_wdog_packet (int wdog_fd) +{ + struct sockaddr_ipx sender; + int addrlen = sizeof(sender); + char buf[2]; + + if (recvfrom_notm(wdog_fd, buf, sizeof(buf), + (struct sockaddr *) &sender, &addrlen) < sizeof(buf)) { + return; + } + if (buf[1] != '?') { + return; + } + buf[1] = 'Y'; + sendto(wdog_fd, buf, 2, 0, (struct sockaddr *) &sender, addrlen); +} + +static int +process_connection (int wdog_fd, int msg_fd) +{ + int i; + int result; + int max; + + chdir("/"); + setsid(); + for (i = 0; i < NR_OPEN; i++) { + if ((i == wdog_fd) || (i == msg_fd)) { + continue; + } + close(i); + } + + max = (wdog_fd > msg_fd ? wdog_fd : msg_fd) + 1; + + while (1) { + fd_set rd; + + FD_ZERO(&rd); + FD_SET(wdog_fd, &rd); + FD_SET(msg_fd, &rd); + + if ((result = select(max, &rd, NULL, NULL, NULL)) == -1) { + exit(0); + } + if (FD_ISSET(wdog_fd, &rd)) { + process_wdog_packet(wdog_fd); + } + if (FD_ISSET(msg_fd, &rd)) { + process_msg_packet(msg_fd); + } + } + return 0; +} + +#endif diff --git a/sutil/ncpmount.h b/sutil/ncpmount.h new file mode 100644 index 0000000..a12df9e --- /dev/null +++ b/sutil/ncpmount.h @@ -0,0 +1,76 @@ +/* + * ncp_mount.h + * + * Copyright (C) 1995, 1996 by Volker Lendecke + * + */ + +#ifndef __NCPMOUNT_H__ +#define __NCPMOUNT_H__ + +#include "ncp.h" + +#define NCP_MOUNT_VERSION_V2 2 + +/* Values for flags */ +#define NCP_MOUNT2_SOFT 0x0001 +#define NCP_MOUNT2_INTR 0x0002 +#define NCP_MOUNT2_STRONG 0x0004 +#define NCP_MOUNT2_NO_OS2 0x0008 +#define NCP_MOUNT2_NO_NFS 0x0010 + +#define PATH_MAX_V20 1024 /* PATH_MAX for 2.0 kernel */ + +struct ncp_mount_data_v2 { + int version; + unsigned int ncp_fd; /* The socket to the ncp port */ + unsigned int wdog_fd; /* Watchdog packets come here */ + unsigned int message_fd; /* Message notifications come here */ + __ker20_uid_t mounted_uid; /* Who may umount() this filesystem? */ + + struct sockaddr_ipx serv_addr; + unsigned char server_name[NCP_BINDERY_NAME_LEN]; + + unsigned char mount_point[PATH_MAX_V20+1]; + unsigned char mounted_vol[NCP_VOLNAME_LEN+1]; + + unsigned int time_out; /* How long should I wait after + sending a NCP request? */ + unsigned int retry_count; /* And how often should I retry? */ + unsigned int flags; + + __ker20_uid_t uid; + __ker20_gid_t gid; + __ker20_mode_t file_mode; + __ker20_mode_t dir_mode; +}; + +#define NCP_MOUNT_VERSION_V3 3 + +/* Values for flags */ +#define NCP_MOUNT3_SOFT 0x0001 +#define NCP_MOUNT3_INTR 0x0002 +#define NCP_MOUNT3_STRONG 0x0004 +#define NCP_MOUNT3_NO_OS2 0x0008 +#define NCP_MOUNT3_NO_NFS 0x0010 + +struct ncp_mount_data_v3 { + int version; + unsigned int ncp_fd; /* The socket to the ncp port */ + __ker21_uid_t mounted_uid; /* Who may umount() this filesystem? */ + __ker21_pid_t wdog_pid; /* Who cares for our watchdog packets? */ + + unsigned char mounted_vol[NCP_VOLNAME_LEN + 1]; + unsigned int time_out; /* How long should I wait after + sending a NCP request? */ + unsigned int retry_count; /* And how often should I retry? */ + unsigned int flags; + + __ker21_uid_t uid; + __ker21_gid_t gid; + __ker21_mode_t file_mode; + __ker21_mode_t dir_mode; +}; + +#endif /* __NCPMOUNT_H__ */ + diff --git a/sutil/ncpumount.c b/sutil/ncpumount.c index 589eacc..1d59847 100644 --- a/sutil/ncpumount.c +++ b/sutil/ncpumount.c @@ -16,7 +16,7 @@ #include #include #include - /* #include *//* generates a warning here */ +/* #include */ /* generates a warning here */ extern pid_t waitpid(pid_t, int *, int); #include #include @@ -27,11 +27,7 @@ extern pid_t waitpid(pid_t, int *, int); #include #include -#include -#include -#include -#include -#include +#include "ncplib.h" static char *progname; @@ -53,7 +49,7 @@ umount_ok(const char *mount_point) mount_point, strerror(errno)); return -1; } - if (ioctl(fid, NCP_IOC_GETMOUNTUID, &mount_uid) != 0) + if (ncp_get_mount_uid(fid, &mount_uid) != 0) { fprintf(stderr, "%s probably not ncp-filesystem\n", mount_point); diff --git a/sutil/nwcrypt.c b/sutil/nwcrypt.c index be33914..9d6f438 100644 --- a/sutil/nwcrypt.c +++ b/sutil/nwcrypt.c @@ -142,7 +142,7 @@ shuffle1(buf32 temp, unsigned char *target) } -static void +void shuffle(const unsigned char *lon, const unsigned char *buf, int buflen, unsigned char *target) { diff --git a/sutil/nwsfind.c b/sutil/nwsfind.c index 62086cd..3e6a64b 100644 --- a/sutil/nwsfind.c +++ b/sutil/nwsfind.c @@ -13,6 +13,7 @@ #include #include #include +#include static char *progname; @@ -29,6 +30,7 @@ help(void) printf("usage: %s [server]\n", progname); printf("\n" "-t Server type, default: File server\n" + "-a server is in form ::\n" "-h Print this help text\n" "\n"); } @@ -48,7 +50,10 @@ main(int argc, char *argv[]) char *server = NULL; int object_type = NCP_BINDERY_FSERVER; struct sockaddr_ipx *result; + struct sockaddr_ipx resultbuf; + struct ncp_conn *conn; long err; + int is_address = 0; int opt; @@ -56,13 +61,16 @@ main(int argc, char *argv[]) set_com_err_hook(swallow_error); - while ((opt = getopt(argc, argv, "t:")) != EOF) + while ((opt = getopt(argc, argv, "at:")) != EOF) { switch (opt) { case 't': object_type = atoi(optarg); break; + case 'a': + is_address = 1; + break; case 'h': case '?': help(); @@ -78,16 +86,36 @@ main(int argc, char *argv[]) usage(); exit(1); } - if (optind == argc - 1) + if (is_address) { - server = argv[optind]; - if (strlen(server) >= NCP_BINDERY_NAME_LEN) + if ((optind > argc - 1) || + ipx_sscanf_saddr(argv[optind], &resultbuf)) { - com_err(argv[0], ENAMETOOLONG, "server name too long"); + usage(); exit(1); } + if ((!(conn = ncp_open_addr(&resultbuf, &err))) || + (err = ncp_close(conn))) + result = NULL; + else + { + result = &resultbuf; + server = argv[optind]; + } + } + else + { + 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); } - result = ncp_find_server(&server, object_type, &err); if (result == NULL) { diff --git a/util/Makefile b/util/Makefile index 6fed304..b7182e7 100644 --- a/util/Makefile +++ b/util/Makefile @@ -3,11 +3,13 @@ # USERUTILS = slist pqlist nwfsinfo pserver nprint nsend ncopy nwpasswd -USERUTILS += nwbols nwbocreate nwborm nwboprops +USERUTILS += nwbols nwbocreate nwborm nwboprops pqstat pqrm USERUTILS += nwbpcreate nwbprm nwbpvalues nwbpadd nwbpset USERUTILS += nwgrant nwrevoke nwuserlist nwrights nwauth USERUTILS += nwfstime nwvolinfo nwtrustee +ifdef MOUNT2 SBINUTILS = nwmsg +endif UTILS = $(USERUTILS) $(SBINUTILS) ncptest @@ -23,7 +25,7 @@ endif default: make -C .. -all: $(UTILS) ncptest +all: $(UTILS) ncptest # ipx_probe install: all for i in $(USERUTILS); \ @@ -34,6 +36,9 @@ install: all $(UTILS): %: %.o $(LIBDEP) $(CC) -o $@ $(addsuffix .o,$@) -L../lib -lncp +ipx_probe: ipx_probe.c + $(CC) $(CFLAGS) -o ipx_probe ipx_probe.c + dep: $(CPP) -M $(INCLUDES) *.c > .depend diff --git a/util/ipx_probe.c b/util/ipx_probe.c new file mode 100644 index 0000000..de2a435 --- /dev/null +++ b/util/ipx_probe.c @@ -0,0 +1,456 @@ +/* + * ipx_probe.c + * + * Check the network for frames currently active + * + * Copyright (C) 1996 by Volker Lendecke + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static char *progname; +int verbose = 0; + +static void +usage () +{ + fprintf (stderr, "usage: %s [options]\n", progname); + fprintf (stderr, "type '%s -h' for help\n", progname); +} + +static void +help () +{ + printf ("\n" + "Probe an interface for ipx networks\n" + "\n"); + printf ("usage: %s [options]\n", progname); + printf ("\n" + "-v Verbose output\n" + "-i interface Interface to probe, default: eth0\n" + "-t timeout Seconds to wait for answer, default: 3\n" + "-h Print this help text\n\n"); +} + +#define IPX_SAP_PTYPE (0x04) +#define IPX_SAP_NEAREST_QUERY (0x0003) +#define IPX_SAP_PORT (0x0452) +#define IPX_BROADCAST_NODE ("\xff\xff\xff\xff\xff\xff") + +#define BVAL(buf,pos) (((__u8 *)(buf))[pos]) +#define BSET(buf,pos,val) (BVAL(buf,pos) = (val)) +static inline void +WSET_HL (__u8 * buf, int pos, __u16 val) +{ + BSET (buf, pos, val >> 8); + BSET (buf, pos + 1, val & 0xff); +} + +struct frame_type +{ + char *ft_name; + unsigned char ft_val; +}; + +static struct frame_type frame_types[] = +{ + { + "802.2", IPX_FRAME_8022 + } + , +#ifdef IPX_FRAME_TR_8022 + { + "802.2TR", IPX_FRAME_TR_8022 + } + , +#endif + { + "802.3", IPX_FRAME_8023 + } + , + { + "SNAP", IPX_FRAME_SNAP + } + , + { + "EtherII", IPX_FRAME_ETHERII + } +}; + +#define NFTYPES (sizeof(frame_types)/sizeof(struct frame_type)) + +static char * +frame_name (int frame_type) +{ + int i; + for (i = 0; i < NFTYPES; i++) + { + if (frame_types[i].ft_val == frame_type) + { + return frame_types[i].ft_name; + } + } + return NULL; +} + +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 int +probe_frame (char *interface, int frame_type, int timeout, unsigned long *net) +{ + int i, sock, opt; + int result; + long err; + char errmsg[strlen (progname) + 20]; + char data[1024]; + + static struct ifreq id; + struct sockaddr_ipx *sipx = (struct sockaddr_ipx *) &id.ifr_addr; + + if (verbose != 0) + { + printf ("probing %s on %s -- ", frame_name (frame_type), + interface); + fflush (stdout); + } + sock = socket (AF_IPX, SOCK_DGRAM, AF_IPX); + if (sock < 0) + { + int old_errno = errno; + + sprintf (errmsg, "%s: socket", progname); + perror (errmsg); + if (old_errno == -EINVAL) + { + fprintf (stderr, "Probably you have no IPX support in " + "your kernel\n"); + } + close (sock); + return -1; + } + memset (&id, 0, sizeof (id)); + strncpy (id.ifr_name, interface, sizeof (id.ifr_name) - 1); + sipx->sipx_family = AF_IPX; + sipx->sipx_action = IPX_CRTITF; + sipx->sipx_special = IPX_PRIMARY; + sipx->sipx_network = 0L; + sipx->sipx_type = frame_type; + + i = 0; + do + { + result = ioctl (sock, SIOCSIFADDR, &id); + i++; + } + while ((i < 5) && (result < 0) && (errno == EAGAIN)); + + if (result < 0) + { + int old_errno = errno; + close (sock); + errno = old_errno; + return result; + } + /* We do a GNS request on the new socket. If something comes + back, we assume that the frame type is valid. */ + + opt = 1; + if ((result = setsockopt (sock, SOL_SOCKET, + SO_BROADCAST, &opt, sizeof (opt))) < 0) + { + int old_errno = errno; + close (sock); + errno = old_errno; + return result; + } + memset (&id, 0, sizeof (id)); + sipx->sipx_family = AF_IPX; + sipx->sipx_network = htonl (0x0); + sipx->sipx_port = htons (0x0); + sipx->sipx_type = IPX_SAP_PTYPE; + + if ((result = bind (sock, (struct sockaddr *) sipx, + sizeof (*sipx))) < 0 - 1) + { + int old_errno = errno; + close (sock); + errno = old_errno; + return result; + } + WSET_HL (data, 0, IPX_SAP_NEAREST_QUERY); + WSET_HL (data, 2, 4); + + memset (&id, 0, sizeof (id)); + sipx->sipx_family = AF_IPX; + sipx->sipx_port = htons (IPX_SAP_PORT); + sipx->sipx_type = IPX_SAP_PTYPE; + sipx->sipx_network = htonl (0x0); + memcpy (sipx->sipx_node, IPX_BROADCAST_NODE, 6); + + if ((result = sendto (sock, data, 4, 0, (struct sockaddr *) sipx, + sizeof (*sipx))) < 0) + { + int old_errno = errno; + close (sock); + errno = old_errno; + return result; + } + result = ipx_recv (sock, data, 1024, 0, timeout, &err); + + if (result > 0) + { + struct sockaddr_ipx sipx; + int namelen = sizeof (sipx); + + if (getsockname (sock, (struct sockaddr *) &sipx, + &namelen) < 0) + { + fprintf (stderr, "%s: Could not find socket address\n", + progname); + exit (1); + } + *net = ntohl (sipx.sipx_network); + } + memset (&id, 0, sizeof (id)); + strncpy (id.ifr_name, interface, sizeof (id.ifr_name) - 1); + sipx->sipx_family = AF_IPX; + sipx->sipx_action = IPX_DLTITF; + sipx->sipx_network = 0L; + sipx->sipx_type = frame_type; + result = ioctl (sock, SIOCSIFADDR, &id); + close (sock); + + if (result < 0) + { + fprintf (stderr, "%s: could not delete interface\n", + progname); + exit (1); + } + if (err == ETIMEDOUT) + { + if (verbose != 0) + { + printf ("no network found\n"); + } + return -1; + } + if (verbose != 0) + { + printf ("found IPX network %8.8lX\n", *net); + } + return 0; +} + + +static int +file_lines (char *name) +{ + FILE *f = fopen (name, "r"); + char buf[100]; + int lines = 0; + + if (f == NULL) + { + return -errno; + } + while (fgets (buf, sizeof (buf), f) != NULL) + { + lines += 1; + } + fclose (f); + return lines; +} + +static int +ipx_interfaces (void) +{ + int result = file_lines ("/proc/net/ipx_interface"); + if (result == 0) + { + result = -EIO; + } + return result - 1; +} + +static int +ipx_auto_off (void) +{ + int s; + char errmsg[strlen (progname) + 20]; + int val = 0; + + s = socket (AF_IPX, SOCK_DGRAM, AF_IPX); + if (s < 0) + { + int old_errno = errno; + + sprintf (errmsg, "%s: socket", progname); + perror (errmsg); + if (old_errno == -EINVAL) + { + fprintf (stderr, "Probably you have no IPX support in " + "your kernel\n"); + } + close (s); + return -1; + } + sprintf (errmsg, "%s: ioctl", progname); + + if (ioctl (s, SIOCAIPXPRISLT, &val) < 0) + { + perror (errmsg); + close (s); + return -1; + } + if (ioctl (s, SIOCAIPXITFCRT, &val) < 0) + { + perror (errmsg); + close (s); + return -1; + } + close (s); + return 0; +} + +int +main (int argc, char *argv[]) +{ + int interfaces; + char *interface = "eth0"; + int opt; + int timeout = 3; + + unsigned long network[5] = + {0,}; + + progname = argv[0]; + + while ((opt = getopt (argc, argv, "vi:ht:")) != EOF) + { + switch (opt) + { + case 'v': + verbose = 1; + break; + case 'i': + interface = optarg; + break; + case 't': + timeout = atoi (optarg); + break; + case 'h': + help (); + exit (1); + default: + usage (); + exit (1); + } + } + + if (ipx_auto_off () < 0) + { + exit (1); + } + interfaces = ipx_interfaces (); + if (interfaces > 0) + { + fprintf (stderr, "%s must be run with no interfaces configured." + " Found %d interface%s.\n", + progname, interfaces, + interfaces == 1 ? "" : "s"); + exit (1); + } + if (interfaces < 0) + { + fprintf (stderr, "%s: %s\n", progname, strerror (interfaces)); + exit (1); + } + probe_frame (interface, IPX_FRAME_8022, timeout, &(network[0])); + probe_frame (interface, IPX_FRAME_8023, timeout, &(network[1])); + probe_frame (interface, IPX_FRAME_SNAP, timeout, &(network[2])); + probe_frame (interface, IPX_FRAME_ETHERII, timeout, &(network[3])); + + if (verbose == 0) + { + if (network[0] != 0) + { + printf ("%s %8.8lX\n", + frame_name (IPX_FRAME_8022), network[0]); + } + if (network[1] != 0) + { + printf ("%s %8.8lX\n", + frame_name (IPX_FRAME_8023), network[1]); + } + if (network[2] != 0) + { + printf ("%s %8.8lX\n", + frame_name (IPX_FRAME_SNAP), network[2]); + } + if (network[3] != 0) + { + printf ("%s %8.8lX\n", + frame_name (IPX_FRAME_ETHERII), network[3]); + } + } + return 0; +} diff --git a/util/ncopy.c b/util/ncopy.c index b59b0f6..de73409 100644 --- a/util/ncopy.c +++ b/util/ncopy.c @@ -23,7 +23,7 @@ #include #include #include "ncplib.h" - +#include struct NCPMountRec { diff --git a/util/ncptest.c b/util/ncptest.c index 15147d6..6e5a02f 100644 --- a/util/ncptest.c +++ b/util/ncptest.c @@ -29,12 +29,11 @@ extern pid_t waitpid(pid_t, int *, int); #include #include #include -#include +#include "kernel/ipx.h" -#include -#include -#include -#include +#include "kernel/fs.h" +#include "kernel/ncp.h" +#include "kernel/ncp_fs.h" #include "ncplib.h" diff --git a/util/nsend.c b/util/nsend.c index da716b2..3af5a23 100644 --- a/util/nsend.c +++ b/util/nsend.c @@ -19,6 +19,8 @@ main(int argc, char **argv) __u8 conn_list[256] = {0,}; int no_conn; + int conn_list2[256]; + int i; char *message = NULL; char *user = NULL; @@ -51,11 +53,19 @@ main(int argc, char **argv) ncp_close(conn); exit(1); } - if ((err = ncp_send_broadcast(conn, no_conn, conn_list, message)) != 0) + for (i=0; icompletion == 0xFB) + err = ncp_send_broadcast(conn, no_conn, conn_list, message); + if (err) + { + com_err(argv[0], err, "in send_broadcast"); + ncp_close(conn); + exit(1); + } } ncp_close(conn); return 0; diff --git a/util/nwbpvalues.c b/util/nwbpvalues.c index fe1243b..2b05b72 100644 --- a/util/nwbpvalues.c +++ b/util/nwbpvalues.c @@ -268,7 +268,7 @@ print_station_addr(char *fmt, struct ncp_station_addr *addr, char *buff) buff += 4; break; case 'L': /* Lan */ - sprintf(buff, "%08lX", htonl(addr->NetWork)); + sprintf(buff, "%08X", (u_int32_t)htonl(addr->NetWork)); buff += 8; break; case '%': @@ -349,15 +349,15 @@ print_login_control(__u8 * val) } if (a->MaxDiskUsage != 0xFFFFFF7FL) { - printf("Maximum DiskQuota : %8ld blocks\n", - ntohl(a->MaxDiskUsage)); + printf("Maximum DiskQuota : %8d blocks\n", + (u_int32_t)ntohl(a->MaxDiskUsage)); } printf("Failed Logins: %5d\n", ntohs(a->BadLoginCount)); if (a->BadLoginCountDown != 0L) { - printf("Account disabled still %8ld seconds\n", - ntohl(a->BadLoginCountDown)); + printf("Account disabled still %8d seconds\n", + (u_int32_t)ntohl(a->BadLoginCountDown)); } if (a->LastIntruder.NetWork != 0L) { diff --git a/util/nwmsg.c b/util/nwmsg.c index 209ea4c..98c3b25 100644 --- a/util/nwmsg.c +++ b/util/nwmsg.c @@ -22,6 +22,7 @@ #include #include #include "ncplib.h" +#include static int search_utmp(char *user, char *tty); diff --git a/util/pqrm.c b/util/pqrm.c new file mode 100644 index 0000000..f1a7314 --- /dev/null +++ b/util/pqrm.c @@ -0,0 +1,71 @@ +/* + * pqstat.c + * + * List the jobs in a print queue on a server + * + * Copyright (C) 1998 by David Woodhouse + * Derived from pqlist.c, (C) 1996 Volker Lendecke + * + */ + +#include +#include +#include +#include +#include "ncplib.h" + +int +main(int argc, char **argv) +{ + struct ncp_conn *conn; + struct ncp_bindery_object q; + long err; + int i; + + if ((conn = ncp_initialize(&argc, argv, 1, &err)) == NULL) + { + com_err(argv[0], err, "when initializing"); + return 1; + } + + if (argc < 3) + { + fprintf(stderr, "usage: %s [ ...]\n", argv[0]); + return 1; + } + + + if (ncp_get_bindery_object_id(conn, NCP_BINDERY_PQUEUE, + argv[1], &q) != 0) + { + printf("Queue \"%s\" on server %s not found.\n", + argv[1], conn->server); + ncp_close(conn); + exit(1); + + } + + for (i=2; i +#include +#include +#include +#include +#include "ncplib.h" + +/* move this to library ? */ +int +ncp_time_to_tm(struct tm* out, __u8* netwareTime) +{ + struct tm tmp; + + tmp.tm_year = netwareTime[0]; + if (tmp.tm_year < 80) tmp.tm_year += 100; + tmp.tm_mon = netwareTime[1]-1; + if ((tmp.tm_mon < 0) || (tmp.tm_mon >= 12)) return 1; + tmp.tm_mday = netwareTime[2]; + if ((tmp.tm_mday < 1) || (tmp.tm_mday >= 32)) return 1; + tmp.tm_hour = netwareTime[3]; + if (tmp.tm_hour >= 24) return 1; + tmp.tm_min = netwareTime[4]; + if (tmp.tm_min >= 60) return 1; + tmp.tm_sec = netwareTime[5]; + if (tmp.tm_sec >= 60) return 1; + memcpy(out, &tmp, sizeof(tmp)); + return 0; +} + +int +ncp_cmp_time(struct tm* tm1, struct tm* tm2) +{ +#undef XTST +#define XTST(Y) if (tm1->tm_##Y## != tm2->tm_##Y##) { \ + return (tm1->tm_##Y## > tm2->tm_##Y##) ? 1 : -1; \ + } + XTST(year); + XTST(mon); + XTST(mday); + XTST(hour); + XTST(min); + XTST(sec); +#undef XTST + return 0; +} + +int +main(int argc, char **argv) +{ + struct ncp_conn *conn; + struct ncp_bindery_object q,u; + unsigned long maxqlen=~0; + long err; + + __u32 qlen, idl1,idl2, job_id; + struct nw_queue_job_entry j; + + if ((conn = ncp_initialize(&argc, argv, 1, &err)) == NULL) + { + com_err(argv[0], err, "when initializing"); + return 1; + } + + if (argc == 3) + { + char *end; + + maxqlen=strtoul(argv[2], &end, 10); + if (*end != 0) + argc = 4; + + } + if (argc < 2 || argc > 3) + { + fprintf(stderr, "usage: %s []\n", argv[0]); + return 1; + } + + + if (ncp_get_bindery_object_id(conn, NCP_BINDERY_PQUEUE, + argv[1], &q) != 0) + { + printf("Queue \"%s\" on server %s not found.\n", + argv[1], conn->server); + ncp_close(conn); + exit(1); + + } + + if (isatty(1)) + { + printf("\nServer: %s\tQueue: %s\tQueue ID: %8.8X\n", conn->server, q.object_name, q.object_id); + printf(" %5s %-12s %-32s %-7s %-4s %-8s\n" + + "-----------------------------------------------" + "--------------------------------\n", + "Seq","Name", + "Description", "Status", "Form", "Job ID"); + } + + + if ((err=ncp_get_queue_length(conn, q.object_id, &qlen)) != 0) + { + if (conn->completion == 0xD3) { + fprintf(stderr, "You have insufficient rights to list queue jobs\n"); + } else { + com_err(argv[0], err, ": cannot get queue length"); + } + ncp_close(conn); + exit(1); + } +/* printf("There are %d jobs in the queue.\n",qlen); */ + + idl1=1; + job_id =0; + + if ((err=ncp_get_queue_job_ids(conn, q.object_id, 1, + &idl1, &idl2, &job_id)) != 0) + { + printf("Error getting queue jobs ids: %ld\n",err); + ncp_close(conn); + exit(1); + } + +/* printf("First queue job ID is %8X\n",job_id);*/ + + while (maxqlen-- && job_id && (ncp_get_queue_job_info(conn, q.object_id, job_id, &j) == 0)) + { + const char* jst; + + char user[50]; + if ((ncp_get_bindery_object_name + (conn, ntohl(j.ClientObjectID), &u)) + == 0) + { + memcpy(user,u.object_name,48); + user[48]=0; + } + else + { + sprintf(user,""); + } + + j.JobFileName[j.FileNameLen]=0; + + if (j.JobControlFlags & 0xC0) { + jst = "Held"; + } else if (j.JobControlFlags & 0x20) { + jst = "Adding"; + } else if (j.ServerStation) { + jst = "Active"; + } else { + struct tm jobtime; + + jst = "Ready"; + if (!ncp_time_to_tm(&jobtime, j.TargetExecTime)) { + time_t ltime; + struct tm* loctime; + + time(<ime); + loctime = localtime(<ime); + + if (ncp_cmp_time(&jobtime, loctime) >= 0) + jst = "Waiting"; + } + } + + printf(" %5d %-12s %-32.32s %-7s %4d %08X\n", + j.JobPosition, user, j.JobTextDescription, jst, ntohs(j.JobType), j.JobNumber); + if (j.next == job_id) + job_id = 0; + else job_id = j.next; + } + + ncp_close(conn); + return 0; +} + diff --git a/util/pserver.c b/util/pserver.c index 83f82bf..f68d69d 100644 --- a/util/pserver.c +++ b/util/pserver.c @@ -254,7 +254,7 @@ 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 *target, int target_size, char *user) { char *s = q->command; char *target_end = target + target_size; @@ -270,7 +270,6 @@ build_command(struct nw_queue *q, struct queue_job *j, target += len; } - memset(target, 0, target_size); while ((*s != 0) && (target < target_end)) @@ -287,21 +286,16 @@ build_command(struct nw_queue *q, struct queue_job *j, case '%': *target = '%'; target += 1; + break; 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); - } + add_string(user); + break; + case 'd': + if (j->j.JobTextDescription[0]) + add_string(j->j.JobTextDescription); + else + add_string("No Description"); + break; default: *target = '%'; *(target + 1) = *(s + 1); @@ -320,6 +314,9 @@ poll_queue(struct nw_queue *q) struct queue_job job; int fd[2]; int pid; + int retcode; + struct ncp_bindery_object u; + char user[50]; if (ncp_service_queue_job(q->conn, q->queue_id, q->job_type, &job) != 0) @@ -327,6 +324,24 @@ poll_queue(struct nw_queue *q) /* No job for us */ return 0; } + + if (ncp_get_queue_job_info(q->conn, q->queue_id, job.j.JobNumber, &job.j) != 0) + { + job.j.JobTextDescription[0]=0; + } + + if ((retcode=ncp_get_bindery_object_name + (q->conn, htonl(job.j.ClientObjectID), &u)) + == 0) + { + memcpy(user,u.object_name,48); + user[48]=0; + } + else + { + sprintf(user,""); + } + if (pipe(fd) < 0) { syslog(LOG_ERR, "pipe error: %m"); @@ -380,7 +395,8 @@ poll_queue(struct nw_queue *q) } close(fd[0]); } - build_command(q, &job, command, sizeof(command)); + + build_command(q, &job, command, sizeof(command), user); execl("/bin/sh", "sh", "-c", command, NULL); syslog(LOG_ERR, "exec error: %m\n");