Browse Source

Plan 9 from Bell Labs 2010-01-14

David du Colombier 10 years ago
parent
commit
d8fb251925

+ 8 - 2
acme/mail/src/mesg.c

@@ -949,7 +949,7 @@ ext(char *type)
 void
 mimedisplay(Message *m, char *name, char *rootdir, Window *w, int fileonly)
 {
-	char *dest;
+	char *dest, *maildest;
 
 	if(strcmp(m->disposition, "file")==0 || strlen(m->filename)!=0){
 		if(strlen(m->filename) == 0){
@@ -957,8 +957,14 @@ mimedisplay(Message *m, char *name, char *rootdir, Window *w, int fileonly)
 			dest[strlen(dest)-1] = '\0';
 		}else
 			dest = estrdup(m->filename);
-		if(m->filename[0] != '/')
+		if(maildest = getenv("maildest")){
+			maildest = eappend(maildest, "/", dest);
+			Bprint(w->body, "\tcp %s%sbody%s %q\n", rootdir, name, ext(m->type), maildest);
+			free(maildest);
+		}
+		if(m->filename[0] != '/'){
 			dest = egrow(estrdup(home), "/", dest);
+		}
 		Bprint(w->body, "\tcp %s%sbody%s %q\n", rootdir, name, ext(m->type), dest);
 		free(dest);
 	}else if(!fileonly)

+ 41 - 0
rc/bin/usbeject

@@ -0,0 +1,41 @@
+#!/bin/rc
+# usbeject - unmount usb disks given as arguments
+#	unmount all of them if no arguments given
+rfork e
+disk = ()
+mtpt = /n/usb
+
+test -e /dev/fs/ctl || bind -b '#k' /dev >[2]/dev/null
+
+test -e /dev/usb || bind -a '#u' /dev || {
+	echo $0: no '#u/usb' >[1=2]
+	exit nousb
+}
+test -e /dev/usbdctl || mount -a /srv/usb /dev || {
+	echo $0: cannot mount /srv/usb >[1=2]
+	exit nousbd
+}
+
+disks=()
+mtpt=()
+switch ($#*) {
+case 0
+	disks=`{ls -pd /n/sdU*}
+case *
+	disks=()
+	for (a) {
+		if(~ $a sd??)
+			disk=`{ls -pd /n/^$*^*}
+		if not
+			disk=$a
+		disks=($disks $disk)
+	}
+}
+if (~ $disks '''sdU*''')
+	exit ''
+for (disk in $disks) {
+	unmount /n/$disk >[2]/dev/null && echo $disk unmounted
+	if (test -e /dev/fs/ctl)
+		echo del $disk^parts/^'*' >/dev/fs/ctl >[2]/dev/null
+}
+exit ''

+ 1 - 0
sys/games/lib/fortunes

@@ -4265,3 +4265,4 @@ Sharing your DNA hasn't been this exciting since the good old days. - Genealogy.
 -rwxr-xr-x  1 rae rae 141307906 Nov 10 16:55 arm-2009q3-67-arm-none-linux-gnueabi.bin  # shell script
 Beam to 1.04TeV... Lost due to tune drifting [sh*t!]... Tunes for beam 1 were different when we returned to injection energy (no precycle)... Trimmed and incorporated... Ramped 2 beams, all the way to 1.18TeV !!
 anguish up -1 days, 00:00:01
+People admire complexity. - Rob Pike

+ 188 - 0
sys/man/4/cifs

@@ -0,0 +1,188 @@
+.TH CIFS 4
+.SH NAME
+cifs - Microsoft™ Windows network filesystem client
+.SH SYNOPSIS
+.B aux/cifs
+[
+.B -dDb
+] [
+.B -a
+.I auth-method
+] [
+.B -s
+.I srvname
+] [
+.B -n
+.I called-name
+] [
+.B -k
+.I keyparam
+] [
+.B -m
+.I mntpnt
+]
+.I host
+[
+.I share...
+]
+.SH DESCRIPTION
+.I Cifs
+translates between Microsoft's file-sharing protocol
+(a.k.a. CIFS or SMB) and 9P, allowing Plan9 clients to mount file systems
+(shares or trees in MS terminology) published by such servers.
+.PP
+The root of the mounted directory contains one subdirectory per share,
+always named in lower case, and a few virtual files of mixed case which
+give additional server, session, share, and user information.
+The arguments are:
+.TF "-a\fI auth-method"
+.PD
+.TP
+.BI -a " auth-method"
+.I Cifs
+authenticates using
+.L BNTLM
+by default, but alternative strategies may be
+selected using this option.
+.I Cifs
+eschews cleartext authentication, however
+it may be enabled with the
+.L plain
+auth method.
+The list of currently-supported methods is printed
+if no method name is supplied.
+.IP
+.I "Windows server 2003"
+requires the
+.B BNTLMv2
+method by default, though it can be configured to be more flexible.
+.TP
+.B -b
+Enable file ownership resolution in
+.IR stat (2)
+calls.
+This requires an open and close per file and thus will slow
+.I cifs
+considerably; its use is not recommended.
+.TP
+.B -d
+CIFS packet debug.
+.TP
+.B -D
+9P request debug.
+.TP
+.BI -k " keyparam"
+lists extra parameters which will be passed to
+.IR factotum (4)
+to select a specific key.
+The remote servers's domain is always included in the keyspec,
+under the assumption
+that all servers in a Windows domain share an authentication domain;
+thus
+.I cifs
+expects keys in
+.I factotum
+of the form:
+.RS
+.IP
+.EX
+key proto=pass dom=THEIR-DOMAIN service=cifs
+	user=MY-USERNAME !password=XYZZY
+.EE
+.RE
+.TP
+.BI -m " mntpnt"
+set the mount point for the remote filesystem;
+the default is
+.BI /n/ host.
+.TP
+.BI -n " called-name"
+The CIFS protocol requires clients to know the NetBios name of the
+server they are attaching to, the
+.IR Icalled-name .
+If this is not specified on the command line,
+.I cifs
+attempts to discover this name from the remote server.
+If this fails it will then try
+.IR host ,
+and finally it will try the name
+.LR *SMBSERVER .
+.TP
+.BI -s " srvname"
+post the service as
+.BI /srv/ srvname.
+.TP
+.I host
+The address of the remote server to connect to.
+.TP
+.I share
+A list of share names to attach on the remote server; if none is given,
+.I cifs
+will attempt to attach all shares published by the remote host.
+.SS "Synthetic Files"
+Several synthetic files appear in the root of the mounted filesystem:
+.TF Workstations
+.PD
+.TP
+.B Shares
+Contains a list of the currently attached shares,
+with fields giving the share name,  disk free space / capacity, the share type,
+and a descriptive comment from the server.
+.TP
+.B Connection
+Contains the username used for authentication,
+server's called name, server's domain,
+server's OS, the time slip between the local host and the server,
+the Maximum Transfer Unit (MTU) the server requested, and optionally a flag
+indicating only guest access has been granted.
+The second line contains a list of capabilities offered by the server which is
+mainly of use for debugging
+.IR cifs .
+.TP
+.B Users
+Each line contains a user's name, the user's full name,
+and a descriptive comment.
+.TP
+.B Groups
+Each line gives a group's name, and a list of the names of the users who
+are members of that group.
+.TP
+.B Sessions
+Lists the users authenticated, the client machine's NetBios name or IP address,
+the time since the connection was established,
+and the time for which the connection has been idle.
+.TP
+.B Domains
+One line per domain giving the domain name and a descriptive comment.
+.TP
+.B Workstations
+One line per domain giving the domain name and a descriptive comment,
+the version number of the OS it is running, and comma-separated list of flags
+giving the features of that OS.
+.TP
+.B Dfsroot
+Top level DFS routing giving the DFS link type, time to live of the data,
+proximity of the server, the Netbios or DNS name and
+a physical path or a machine that this maps to.
+.IP
+DNS paths are usually assigned dynamicially as a form of load balancing.
+.SH SOURCE
+.B /sys/src/cmd/cifs
+.SH SEE ALSO
+.IR factotum (4),
+.IR aquarela (8)
+.SH BUGS
+NetApp Filer compatibility has not yet been tested; there may not be any.
+.PP
+DFS support is unfinished.
+.PP
+Kerbros authentication is unfinished.
+.PP
+NetBios name resolution is not supported, though it is now rarely used.
+.PP
+.I Cifs
+has only been tested against
+.IR aquarela (8),
+Windows 95, NT4.0sp6,
+Windows server 2003, WinXP pro, Samba 3.0, and Samba 2.0 (Pluto VideoSpace).
+No support is attempted for servers predating NT 4.0.

+ 25 - 22
sys/man/4/usb

@@ -298,9 +298,9 @@ configures and manages a USB audio device.
 It implements a file system,
 normally mounted on
 .BI /dev ,
-but this can be changed with the
-.B \-m
-option, with files
+but this can be changed with
+.BR \-m ,
+containing files
 .BR volume ,
 .BR audioctl ,
 .BR audio ,
@@ -315,7 +315,9 @@ maintain backward compatibility with the Soundblaster driver.
 The
 .B \-V
 option (verbose)
-causes usbaudio to print information about the device on startup.
+causes
+.I audio
+to print information about the device on startup.
 The
 .B \-s
 option specifies a name for a file descriptor to be posted in
@@ -392,10 +394,11 @@ samples ordered primarily by time and
 secondarily by channel.
 Samples occupy the minimum integral number of bytes.
 Read and write operations of arbitrary size are allowed.
+.
 .SS Ccid
 .I Ccid
 discovers and configures SIM or SAM cards using the CCID standard.
-It provides a file system (usually seen at
+It provides a file system (usually mounted at
 .BR /dev )
 that includes three files,
 .BI ctl ,
@@ -430,16 +433,16 @@ a new ATR is written to it.
 .SS Ibuddy
 .PP
 Ibuddy supports a USB I-buddy toy, a little winged-demon.
-The driver provides one directory per attached toy with a 
-single 
-.BR ctl 
-file to control the device. Directories are named
+The driver provides one directory per attached toy with a single
+.BR ctl
+file to control the device.
+Directories are named
 .BR ibuddyN ,
-being 
+being
 .I N
 the corresponding usb device number.
-When read, the 
-.BR ctl 
+When read, the
+.BR ctl
 file provides the state of the device in this form:
 .IP
 .EX
@@ -451,25 +454,25 @@ blue on|off
 heart on|off
 .EE
 .PP
-Each line describes the status of one feature. 
-.IR  Red , 
+Each line describes the status of one feature.
+.IR  Red ,
 .IR  blue ,
-and 
+and
 .IR  green
 are the different leds in the head of
-the toy. 
-.IR  Heart 
+the toy.
+.IR  Heart
 represents the red led in the chest of
-the toy. 
-.IR  Wings 
+the toy.
+.IR  Wings
 represents the status of the wings, which
 can be closed or open.
-.IR  Hips 
+.IR  Hips
 represents the orientation
 of the toy (left or right, from the figure's point of view).
 .PP
-Lines can be written to the 
-.BR ctl 
+Lines can be written to the
+.BR ctl
 file to command the device.
 Multiple lines (six at most) can be written
 at once, with one action per line.

+ 43 - 3
sys/src/cmd/acme/exec.c

@@ -320,12 +320,35 @@ delcol(Text *et, Text*, Text*, int, int, Rune*, int)
 }
 
 void
-del(Text *et, Text*, Text*, int flag1, int, Rune*, int)
+del(Text *et, Text*, Text *argt, int flag1, int, Rune *arg, int narg)
 {
+	Window *w;
+	char *name, *p;
+	Plumbmsg *pm;
+
 	if(et->col==nil || et->w == nil)
 		return;
-	if(flag1 || et->w->body.file->ntext>1 || winclean(et->w, FALSE))
+	if(flag1 || et->w->body.file->ntext>1 || winclean(et->w, FALSE)){
+		w = et->w;
+		name = getname(&w->body, argt, arg, narg, TRUE);
+		if(name && plumbsendfd >= 0){
+			pm = emalloc(sizeof(Plumbmsg));
+			pm->src = estrdup("acme");
+			pm->dst = estrdup("close");
+			pm->wdir = estrdup(name);
+			if(p = strrchr(pm->wdir, '/'))
+				*p = '\0';
+			pm->type = estrdup("text");
+			pm->attr = nil;
+			pm->data = estrdup(name);
+			pm->ndata = strlen(pm->data);
+			if(pm->ndata < messagesize-1024)
+				plumbsend(plumbsendfd, pm);
+			else
+				plumbfree(pm);
+		}
 		colclose(et->col, et->w, TRUE);
+	}
 }
 
 void
@@ -539,10 +562,11 @@ putfile(File *f, int q0, int q1, Rune *namer, int nname)
 {
 	uint n, m;
 	Rune *r;
-	char *s, *name;
+	char *s, *name, *p;
 	int i, fd, q;
 	Dir *d, *d1;
 	Window *w;
+	Plumbmsg *pm;
 	int isapp;
 
 	w = f->curtext->w;
@@ -610,6 +634,22 @@ putfile(File *f, int q0, int q1, Rune *namer, int nname)
 			f->text[i]->w->dirty = w->dirty;
 		}
 	}
+	if(plumbsendfd >= 0){
+		pm = emalloc(sizeof(Plumbmsg));
+		pm->src = estrdup("acme");
+		pm->dst = estrdup("put");
+		pm->wdir = estrdup(name);
+		if(p = strrchr(pm->wdir, '/'))
+			*p = '\0';
+		pm->type = estrdup("text");
+		pm->attr = nil;
+		pm->data = estrdup(name);
+		pm->ndata = strlen(pm->data);
+		if(pm->ndata < messagesize-1024)
+			plumbsend(plumbsendfd, pm);
+		else
+			plumbfree(pm);
+	}
 	fbuffree(s);
 	fbuffree(r);
 	free(d);

+ 1 - 0
sys/src/cmd/acme/fns.h

@@ -16,6 +16,7 @@ char*	getarg(Text*, int, int, Rune**, int*);
 char*	getbytearg(Text*, int, int, char**);
 void	new(Text*, Text*, Text*, int, int, Rune*, int);
 void	undo(Text*, Text*, Text*, int, int, Rune*, int);
+char*	getname(Text*, Text*, Rune*, int, int);
 void	scrsleep(uint);
 void	savemouse(Window*);
 void	restoremouse(Window*);

+ 222 - 0
sys/src/cmd/cifs/apinums.h

@@ -0,0 +1,222 @@
+/********************************************************************/
+/**                     Microsoft LAN Manager                      **/
+/**            Copyright(c) Microsoft Corp., 1987-1991             **/
+/********************************************************************/
+
+#define API_WShareEnum			0
+#define API_WShareGetInfo		1
+#define API_WShareSetInfo		2
+#define API_WShareAdd			3
+#define API_WShareDel			4
+#define API_NetShareCheck		5
+#define API_WSessionEnum		6
+#define API_WSessionGetInfo		7
+#define API_WSessionDel			8
+#define API_WConnectionEnum		9
+#define API_WFileEnum			10
+#define API_WFileGetInfo		11
+#define API_WFileClose			12
+#define API_WServerGetInfo		13
+#define API_WServerSetInfo		14
+#define API_WServerDiskEnum		15
+#define API_WServerAdminCommand		16
+#define API_NetAuditOpen		17
+#define API_WAuditClear			18
+#define API_NetErrorLogOpen		19
+#define API_WErrorLogClear		20
+#define API_NetCharDevEnum		21
+#define API_NetCharDevGetInfo		22
+#define API_WCharDevControl		23
+#define API_NetCharDevQEnum		24
+#define API_NetCharDevQGetInfo		25
+#define API_WCharDevQSetInfo		26
+#define API_WCharDevQPurge		27
+#define API_WCharDevQPurgeSelf		28
+#define API_WMessageNameEnum		29
+#define API_WMessageNameGetInfo		30
+#define API_WMessageNameAdd		31
+#define API_WMessageNameDel		32
+#define API_WMessageNameFwd		33
+#define API_WMessageNameUnFwd		34
+#define API_WMessageBufferSend		35
+#define API_WMessageFileSend		36
+#define API_WMessageLogFileSet		37
+#define API_WMessageLogFileGet		38
+#define API_WServiceEnum		39
+#define API_WServiceInstall		40
+#define API_WServiceControl		41
+#define API_WAccessEnum			42
+#define API_WAccessGetInfo		43
+#define API_WAccessSetInfo		44
+#define API_WAccessAdd			45
+#define API_WAccessDel			46
+#define API_WGroupEnum			47
+#define API_WGroupAdd			48
+#define API_WGroupDel			49
+#define API_WGroupAddUser		50
+#define API_WGroupDelUser		51
+#define API_WGroupGetUsers		52
+#define API_WUserEnum			53
+#define API_WUserAdd			54
+#define API_WUserDel			55
+#define API_WUserGetInfo		56
+#define API_WUserSetInfo		57
+#define API_WUserPasswordSet		58
+#define API_WUserGetGroups		59
+#define API_DeadTableEntry		60
+/* This line and number replaced a Dead Entry */
+#define API_WWkstaSetUID		62
+#define API_WWkstaGetInfo		63
+#define API_WWkstaSetInfo		64
+#define API_WUseEnum			65
+#define API_WUseAdd			66
+#define API_WUseDel			67
+#define API_WUseGetInfo			68
+#define API_WPrintQEnum			69
+#define API_WPrintQGetInfo		70
+#define API_WPrintQSetInfo		71
+#define API_WPrintQAdd			72
+#define API_WPrintQDel			73
+#define API_WPrintQPause		74
+#define API_WPrintQContinue		75
+#define API_WPrintJobEnum		76
+#define API_WPrintJobGetInfo		77
+#define API_WPrintJobSetInfo_OLD	78
+/* This line and number replaced a Dead Entry */
+/* This line and number replaced a Dead Entry */
+#define API_WPrintJobDel		81
+#define API_WPrintJobPause		82
+#define API_WPrintJobContinue		83
+#define API_WPrintDestEnum		84
+#define API_WPrintDestGetInfo		85
+#define API_WPrintDestControl		86
+#define API_WProfileSave		87
+#define API_WProfileLoad		88
+#define API_WStatisticsGet		89
+#define API_WStatisticsClear		90
+#define API_NetRemoteTOD		91
+#define API_WNetBiosEnum		92
+#define API_WNetBiosGetInfo		93
+#define API_NetServerEnum		94
+#define API_I_NetServerEnum		95
+#define API_WServiceGetInfo		96
+/* This line and number replaced a Dead Entry */
+/* This line and number replaced a Dead Entry */
+/* This line and number replaced a Dead Entry */
+/* This line and number replaced a Dead Entry */
+/* This line and number replaced a Dead Entry */
+/* This line and number replaced a Dead Entry */
+#define API_WPrintQPurge		103
+#define API_NetServerEnum2		104
+#define API_WAccessGetUserPerms		105
+#define API_WGroupGetInfo		106
+#define API_WGroupSetInfo		107
+#define API_WGroupSetUsers		108
+#define API_WUserSetGroups		109
+#define API_WUserModalsGet		110
+#define API_WUserModalsSet		111
+#define API_WFileEnum2			112
+#define API_WUserAdd2			113
+#define API_WUserSetInfo2		114
+#define API_WUserPasswordSet2		115
+#define API_I_NetServerEnum2		116
+#define API_WConfigGet2			117
+#define API_WConfigGetAll2		118
+#define API_WGetDCName			119
+#define API_NetHandleGetInfo		120
+#define API_NetHandleSetInfo		121
+#define API_WStatisticsGet2		122
+#define API_WBuildGetInfo		123
+#define API_WFileGetInfo2		124
+#define API_WFileClose2			125
+#define API_WNetServerReqChallenge	126
+#define API_WNetServerAuthenticate	127
+#define API_WNetServerPasswordSet	128
+#define API_WNetAccountDeltas		129
+#define API_WNetAccountSync		130
+#define API_WUserEnum2			131
+#define API_WWkstaUserLogon		132
+#define API_WWkstaUserLogoff		133
+#define API_WLogonEnum			134
+#define API_WErrorLogRead		135
+#define API_WI_NetPathType		136
+#define API_WI_NetPathCanonicalize	137
+#define API_WI_NetPathCompare		138
+#define API_WI_NetNameValidate		139
+#define API_WI_NetNameCanonicalize	140
+#define API_WI_NetNameCompare		141
+#define API_WAuditRead			142
+#define API_WPrintDestAdd		143
+#define API_WPrintDestSetInfo		144
+#define API_WPrintDestDel		145
+#define API_WUserValidate2		146
+#define API_WPrintJobSetInfo		147
+#define API_TI_NetServerDiskEnum	148
+#define API_TI_NetServerDiskGetInfo	149
+#define API_TI_FTVerifyMirror		150
+#define API_TI_FTAbortVerify		151
+#define API_TI_FTGetInfo		152
+#define API_TI_FTSetInfo		153
+#define API_TI_FTLockDisk		154
+#define API_TI_FTFixError		155
+#define API_TI_FTAbortFix		156
+#define API_TI_FTDiagnoseError		157
+#define API_TI_FTGetDriveStats		158
+/* This line and number replaced a Dead Entry */
+#define API_TI_FTErrorGetInfo		160
+/* This line and number replaced a Dead Entry */
+/* This line and number replaced a Dead Entry */
+#define API_NetAccessCheck		163
+#define API_NetAlertRaise		164
+#define API_NetAlertStart		165
+#define API_NetAlertStop		166
+#define API_NetAuditWrite		167
+#define API_NetIRemoteAPI		168
+#define API_NetServiceStatus		169
+#define API_I_NetServerRegister		170
+#define API_I_NetServerDeregister	171
+#define API_I_NetSessionEntryMake	172
+#define API_I_NetSessionEntryClear	173
+#define API_I_NetSessionEntryGetInfo	174
+#define API_I_NetSessionEntrySetInfo	175
+#define API_I_NetConnectionEntryMake	176
+#define API_I_NetConnectionEntryClear	177
+#define API_I_NetConnectionEntrySetInfo	178
+#define API_I_NetConnectionEntryGetInfo	179
+#define API_I_NetFileEntryMake		180
+#define API_I_NetFileEntryClear		181
+#define API_I_NetFileEntrySetInfo	182
+#define API_I_NetFileEntryGetInfo	183
+#define API_AltSrvMessageBufferSend	184
+#define API_AltSrvMessageFileSend	185
+#define API_wI_NetRplWkstaEnum		186
+#define API_wI_NetRplWkstaGetInfo	187
+#define API_wI_NetRplWkstaSetInfo	188
+#define API_wI_NetRplWkstaAdd		189
+#define API_wI_NetRplWkstaDel		190
+#define API_wI_NetRplProfileEnum	191
+#define API_wI_NetRplProfileGetInfo	192
+#define API_wI_NetRplProfileSetInfo	193
+#define API_wI_NetRplProfileAdd		194
+#define API_wI_NetRplProfileDel		195
+#define API_wI_NetRplProfileClone	196
+#define API_wI_NetRplBaseProfileEnum	197
+/* This line and number replaced a Dead Entry */
+/* This line and number replaced a Dead Entry */
+/* This line and number replaced a Dead Entry */
+#define API_WIServerSetInfo		201
+/* This line and number replaced a Dead Entry */
+/* This line and number replaced a Dead Entry */
+/* This line and number replaced a Dead Entry */
+#define API_WPrintDriverEnum		205
+#define API_WPrintQProcessorEnum	206
+#define API_WPrintPortEnum		207
+#define API_WNetWriteUpdateLog		208
+#define API_WNetAccountUpdate		209
+#define API_WNetAccountConfirmUpdate	210
+#define API_WConfigSet			211
+#define API_WAccountsReplicate		212
+/*   213 is used by WfW  */
+#define API_SamOEMChgPasswordUser2_P	214
+#define API_NetServerEnum3		215
+#define MAX_API				215

+ 477 - 0
sys/src/cmd/cifs/auth-testcase.c

@@ -0,0 +1,477 @@
+/*
+ * Beware the LM hash is easy to crack (google for l0phtCrack)
+ * and though NTLM is more secure it is still breakable.
+ * Ntlmv2 is better and seen as good enough by the Windows community.
+ * For real security use Kerberos.
+ */
+#include <u.h>
+#include <libc.h>
+#include <mp.h>
+#include <auth.h>
+#include <libsec.h>
+#include <ctype.h>
+#include <fcall.h>
+#include <thread.h>
+#include <9p.h>
+#include "cifs.h"
+
+#define NTLMV2_TEST	1
+#define DEF_AUTH 	"ntlmv2"
+
+static enum {
+	MACkeylen	= 40,	/* MAC key len */
+	MAClen		= 8,	/* signature length */
+	MACoff		= 14,	/* sign. offset from start of SMB (not netbios) pkt */
+	Bliplen		= 8,	/* size of LMv2 client nonce */
+};
+
+static void
+dmp(char *s, int seq, void *buf, int n)
+{
+	int i;
+	char *p = buf;
+
+	print("%s %3d      ", s, seq);
+	while(n > 0){
+		for(i = 0; i < 16 && n > 0; i++, n--)
+			print("%02x ", *p++ & 0xff);
+		if(n > 0)
+			print("\n");
+	}
+	print("\n");
+}
+
+static Auth *
+auth_plain(char *windom, char *keyp, uchar *chal, int len)
+{
+	UserPasswd *up;
+	static Auth *ap;
+
+	USED(chal, len);
+
+	up = auth_getuserpasswd(auth_getkey, "windom=%s proto=pass service=cifs %s",
+		windom, keyp);
+	if(! up)
+		sysfatal("cannot get key - %r");
+
+	ap = emalloc9p(sizeof(Auth));
+	memset(ap, 0, sizeof(ap));
+	ap->user = estrdup9p(up->user);
+	ap->windom = estrdup9p(windom);
+
+	ap->resp[0] = estrdup9p(up->passwd);
+	ap->len[0] = strlen(up->passwd);
+	memset(up->passwd, 0, strlen(up->passwd));
+	free(up);
+
+	return ap;
+}
+
+static Auth *
+auth_lm_and_ntlm(char *windom, char *keyp, uchar *chal, int len)
+{
+	int err;
+	Auth *ap;
+	char user[64];
+	MSchapreply mcr;
+
+	err = auth_respond(chal, len, user, sizeof user, &mcr, sizeof mcr,
+		auth_getkey, "windom=%s proto=mschap role=client service=cifs %s",
+		windom, keyp);
+	if(err == -1)
+		sysfatal("cannot get key - %r");
+
+	ap = emalloc9p(sizeof(Auth));
+	memset(ap, 0, sizeof(ap));
+	ap->user = estrdup9p(user);
+	ap->windom = estrdup9p(windom);
+
+	/* LM response */
+	ap->len[0] = sizeof(mcr.LMresp);
+	ap->resp[0] = emalloc9p(ap->len[0]);
+	memcpy(ap->resp[0], mcr.LMresp, ap->len[0]);
+
+	/* NTLM response */
+	ap->len[1] = sizeof(mcr.NTresp);
+	ap->resp[1] = emalloc9p(ap->len[1]);
+	memcpy(ap->resp[1], mcr.NTresp, ap->len[1]);
+
+	return ap;
+}
+
+/*
+ * NTLM response only, the LM response is a just
+ * copy of the NTLM one.  We do this because the lm
+ * response is easily reversed - Google for l0pht for more info.
+ */
+static Auth *
+auth_ntlm(char *windom, char *keyp, uchar *chal, int len)
+{
+	Auth *ap;
+
+	if((ap = auth_lm_and_ntlm(windom, keyp, chal, len)) == nil)
+		return nil;
+
+	free(ap->resp[0]);
+	ap->len[0] = ap->len[1];
+	ap->resp[0] = emalloc9p(ap->len[0]);
+	memcpy(ap->resp[0], ap->resp[1], ap->len[0]);
+	return ap;
+}
+
+/*
+ * This is not really nescessary as all fields hmac_md5'ed
+ * in the ntlmv2 protocol are less than 64 bytes long, however
+ * I still do this for completeness.
+ */
+static DigestState *
+hmac_t64(uchar *data, ulong dlen, uchar *key, ulong klen, uchar *digest,
+	DigestState *state)
+{
+	if(klen > 64)
+		klen = 64;
+	return hmac_md5(data, dlen, key, klen, digest, state);
+}
+
+
+static int
+ntv2_blob(uchar *blob, int len, char *windom)
+{
+	int n;
+	uvlong nttime;
+	Rune r;
+	char *d;
+	uchar *p;
+	enum {			/* name types */
+		Beof,		/* end of name list */
+		Bnetbios,	/* Netbios machine name */
+		Bdomain,	/* Windows Domain name (NT) */
+		Bdnsfqdn,	/* DNS Fully Qualified Domain Name */
+		Bdnsname,	/* DNS machine name (win2k) */
+	};
+
+	p = blob;
+	*p++ = 1;		/* response type */
+	*p++ = 1;		/* max response type understood by client */
+
+	*p++ = 0;
+	*p++ = 0;		/* 2 bytes reserved */
+
+	*p++ = 0;
+	*p++ = 0;
+	*p++ = 0;
+	*p++ = 0;		/* 4 bytes unknown */
+
+#ifdef NTLMV2_TEST
+	*p++ = 0xf0;
+	*p++ = 0x20;
+	*p++ = 0xd0;
+	*p++ = 0xb6;
+	*p++ = 0xc2;
+	*p++ = 0x92;
+	*p++ = 0xbe;
+	*p++ = 0x01;
+#else
+	nttime = time(nil);	/* nt time now */
+	nttime = nttime + 11644473600LL;
+	nttime = nttime * 10000000LL;
+	*p++ = nttime & 0xff;
+	*p++ = (nttime >> 8) & 0xff;
+	*p++ = (nttime >> 16) & 0xff;
+	*p++ = (nttime >> 24) & 0xff;
+	*p++ = (nttime >> 32) & 0xff;
+	*p++ = (nttime >> 40) & 0xff;
+	*p++ = (nttime >> 48) & 0xff;
+	*p++ = (nttime >> 56) & 0xff;
+#endif
+#ifdef NTLMV2_TEST
+	*p++ = 0x05;
+	*p++ = 0x83;
+	*p++ = 0x32;
+	*p++ = 0xec;
+	*p++ = 0xfa;
+	*p++ = 0xe4;
+	*p++ = 0xf3;
+	*p++ = 0x6d;
+#else
+	genrandom(p, 8);
+	p += 8;			/* client nonce */
+#endif
+	*p++ = 0x6f;
+	*p++ = 0;
+	*p++ = 0x6e;
+	*p++ = 0;		/* unknown data */
+
+	*p++ = Bdomain;
+	*p++ = 0;		/* name type */
+
+	n = utflen(windom) * 2;
+	*p++ = n;
+	*p++ = n >> 8;		/* name length */
+
+	d = windom;
+	while(*d && p - blob < len - 8){
+		d += chartorune(&r, d);
+		r = toupperrune(r);
+		*p++ = r;
+		*p++ = r >> 8;
+	}
+
+	*p++ = 0;
+	*p++ = Beof;		/* name type */
+
+	*p++ = 0;
+	*p++ = 0;		/* name length */
+
+	*p++ = 0x65;
+	*p++ = 0;
+	*p++ = 0;
+	*p++ = 0;		/* unknown data */
+	return p - blob;
+}
+
+static Auth *
+auth_ntlmv2(char *windom, char *keyp, uchar *chal, int len)
+{
+	int i, n;
+	Rune r;
+	char *p, *u;
+	uchar c, lm_hmac[MD5dlen], nt_hmac[MD5dlen], nt_sesskey[MD5dlen];
+	uchar lm_sesskey[MD5dlen];
+	uchar v1hash[MD5dlen], blip[Bliplen], blob[1024], v2hash[MD5dlen];
+	DigestState *ds;
+	UserPasswd *up;
+	static Auth *ap;
+
+	up = auth_getuserpasswd(auth_getkey, "windom=%s proto=pass service=cifs-ntlmv2 %s",
+		windom, keyp);
+	if(! up)
+		sysfatal("cannot get key - %r");
+
+#ifdef NTLMV2_TEST
+{
+	static uchar srvchal[] = { 0x52, 0xaa, 0xc8, 0xe8, 0x2c, 0x06, 0x7f, 0xa1 };
+	up->user = "ADMINISTRATOR";
+	windom = "rocknroll";
+	chal = srvchal;
+}
+#endif
+	ap = emalloc9p(sizeof(Auth));
+	memset(ap, 0, sizeof(ap));
+
+	/* Standard says unlimited length, experience says 128 max */
+	if((n = strlen(up->passwd)) > 128)
+		n = 128;
+
+	ds = md4(nil, 0, nil, nil);
+	for(i = 0, p = up->passwd; i < n; i++) {
+		p += chartorune(&r, p);
+		c = r;
+		md4(&c, 1, nil, ds);
+		c = r >> 8;
+		md4(&c, 1, nil, ds);
+	}
+	md4(nil, 0, v1hash, ds);
+
+#ifdef NTLMV2_TEST
+{
+	uchar v1[] = {
+		0x0c, 0xb6, 0x94, 0x88, 0x05, 0xf7, 0x97, 0xbf,
+		0x2a, 0x82, 0x80, 0x79, 0x73, 0xb8, 0x95, 0x37
+	;
+	memcpy(v1hash, v1, sizeof(v1));
+}
+#endif
+	/*
+	 * Some documentation insists that the username must be forced to
+	 * uppercase, but the domain name should not be. Other shows both
+	 * being forced to uppercase.  I am pretty sure this is irrevevant as
+	 * the domain name passed from the remote server always seems to be in
+	 * uppercase already.
+	 */
+        ds = hmac_t64(nil, 0, v1hash, MD5dlen, nil, nil);
+	u = up->user;
+	while(*u){
+		u += chartorune(&r, u);
+		r = toupperrune(r);
+		c = r & 0xff;
+        	hmac_t64(&c, 1, v1hash, MD5dlen, nil, ds);
+		c = r >> 8;
+        	hmac_t64(&c, 1, v1hash, MD5dlen, nil, ds);
+	}
+	u = windom;
+
+	while(*u){
+		u += chartorune(&r, u);
+		c = r;
+        	hmac_t64(&c, 1, v1hash, MD5dlen, nil, ds);
+		c = r >> 8;
+        	hmac_t64(&c, 1, v1hash, MD5dlen, nil, ds);
+	}
+        hmac_t64(nil, 0, v1hash, MD5dlen, v2hash, ds);
+#ifdef NTLMV2_TEST
+	print("want:               40 e1 b3 24...\n");
+	dmp("v2hash==kr", 0, v2hash, MD5dlen);
+#endif
+	ap->user = estrdup9p(up->user);
+	ap->windom = estrdup9p(windom);
+
+	/* LM v2 */
+
+	genrandom(blip, Bliplen);
+#ifdef NTLMV2_TEST
+{
+	uchar t[] = { 0x05, 0x83, 0x32, 0xec, 0xfa, 0xe4, 0xf3, 0x6d };
+	memcpy(blip, t, 8);
+}
+#endif
+        ds = hmac_t64(chal, len, v2hash, MD5dlen, nil, nil);
+	hmac_t64(blip, Bliplen, v2hash, MD5dlen, lm_hmac, ds);
+	ap->len[0] = MD5dlen+Bliplen;
+	ap->resp[0] = emalloc9p(ap->len[0]);
+	memcpy(ap->resp[0], lm_hmac, MD5dlen);
+	memcpy(ap->resp[0]+MD5dlen, blip, Bliplen);
+#ifdef NTLMV2_TEST
+	print("want:               38 6b ae...\n");
+	dmp("lmv2 resp ", 0, lm_hmac, MD5dlen);
+#endif
+
+	/* LM v2 session key */
+	hmac_t64(lm_hmac, MD5dlen, v2hash, MD5dlen, lm_sesskey, nil);
+
+	/* LM v2 MAC key */
+	ap->mackey[0] = emalloc9p(MACkeylen);
+	memcpy(ap->mackey[0], lm_sesskey, MD5dlen);
+	memcpy(ap->mackey[0]+MD5dlen, ap->resp[0], MACkeylen-MD5dlen);
+
+	/* NTLM v2 */
+	n = ntv2_blob(blob, sizeof(blob), windom);
+        ds = hmac_t64(chal, len, v2hash, MD5dlen, nil, nil);
+	hmac_t64(blob, n, v2hash, MD5dlen, nt_hmac, ds);
+	ap->len[1] = MD5dlen+n;
+	ap->resp[1] = emalloc9p(ap->len[1]);
+	memcpy(ap->resp[1], nt_hmac, MD5dlen);
+	memcpy(ap->resp[1]+MD5dlen, blob, n);
+#ifdef NTLMV2_TEST
+	print("want:               1a ad 55...\n");
+	dmp("ntv2 resp ", 0, nt_hmac, MD5dlen);
+#endif
+
+	/* NTLM v2 session key */
+	hmac_t64(nt_hmac, MD5dlen, v2hash, MD5dlen, nt_sesskey, nil);
+
+	/* NTLM v2 MAC key */
+	ap->mackey[1] = emalloc9p(MACkeylen);
+	memcpy(ap->mackey[1], nt_sesskey, MD5dlen);
+	memcpy(ap->mackey[1]+MD5dlen, ap->resp[1], MACkeylen-MD5dlen);
+	free(up);
+
+	return ap;
+}
+
+struct {
+	char	*name;
+	Auth	*(*func)(char *, char *, uchar *, int);
+} methods[] = {
+	{ "plain",	auth_plain },
+	{ "lm+ntlm",	auth_lm_and_ntlm },
+	{ "ntlm",	auth_ntlm },
+	{ "ntlmv2",	auth_ntlmv2 },
+//	{ "kerberos",	auth_kerberos },
+};
+
+void
+autherr(void)
+{
+	int i;
+
+	fprint(2, "supported auth methods:\t");
+	for(i = 0; i < nelem(methods); i++)
+		fprint(2, "%s ", methods[i].name);
+	fprint(2, "\n");
+	exits("usage");
+}
+
+Auth *
+getauth(char *name, char *windom, char *keyp, int secmode, uchar *chal, int len)
+{
+	int i;
+	Auth *ap;
+
+	if(name == nil){
+		name = DEF_AUTH;
+		if((secmode & SECMODE_PW_ENCRYPT) == 0)
+			sysfatal("plaintext authentication required, use '-a plain'");
+	}
+
+	ap = nil;
+	for(i = 0; i < nelem(methods); i++)
+		if(strcmp(methods[i].name, name) == 0){
+			ap = methods[i].func(windom, keyp, chal, len);
+			break;
+		}
+
+	if(! ap){
+		fprint(2, "%s: %s - unknown auth method\n", argv0, name);
+		autherr();		/* never returns */
+	}
+	return ap;
+}
+
+static int
+genmac(uchar *buf, int len, int seq, uchar key[MACkeylen], uchar mine[MAClen])
+{
+	DigestState *ds;
+	uchar *sig, digest[MD5dlen], their[MAClen];
+
+	sig = buf+MACoff;
+	memcpy(their, sig, MAClen);
+	memset(sig, 0, MAClen);
+	sig[0] = seq;
+	sig[1] = seq >> 8;
+	sig[2] = seq >> 16;
+	sig[3] = seq >> 24;
+
+	ds = md5(key, MACkeylen, nil, nil);
+	md5(buf, len, nil, ds);
+	md5(nil, 0, digest, ds);
+	memcpy(mine, digest, MAClen);
+
+	return memcmp(their, mine, MAClen);
+}
+
+int
+macsign(Pkt *p)
+{
+	int i, len;
+	uchar *sig, *buf, mac[MAClen], zeros[MACkeylen];
+
+	sig = p->buf + NBHDRLEN + MACoff;
+	buf = p->buf + NBHDRLEN;
+	len = (p->pos - p->buf) - NBHDRLEN;
+
+	for(i = -3; i < 4; i++){
+		memset(zeros, 0, sizeof(zeros));
+		if(genmac(buf, len, p->seq+i, zeros, mac) == 0){
+			dmp("got", 0, buf, len);
+			dmp("Zero OK", p->seq, mac, MAClen);
+			return 0;
+		}
+
+		if(genmac(buf, len, p->seq+i, p->s->auth->mackey[0], mac) == 0){
+			dmp("got", 0, buf, len);
+			dmp("LM-hash OK", p->seq, mac, MAClen);
+			return 0;
+		}
+
+		if(genmac(buf, len, p->seq+i, p->s->auth->mackey[1], mac) == 0){
+			dmp("got", 0, buf, len);
+			dmp("NT-hash OK", p->seq, mac, MAClen);
+			return 0;
+		}
+	}
+	genmac(buf, len, p->seq, p->s->auth->mackey[0], mac);
+
+	memcpy(sig, mac, MAClen);
+	return -1;
+}

+ 416 - 0
sys/src/cmd/cifs/auth.c

@@ -0,0 +1,416 @@
+/*
+ * Beware the LM hash is easy to crack (google for l0phtCrack)
+ * and though NTLM is more secure it is still breakable.
+ * Ntlmv2 is better and seen as good enough by the windows community.
+ * For real security use kerberos.
+ */
+#include <u.h>
+#include <libc.h>
+#include <mp.h>
+#include <auth.h>
+#include <libsec.h>
+#include <ctype.h>
+#include <fcall.h>
+#include <thread.h>
+#include <9p.h>
+#include "cifs.h"
+
+#define DEF_AUTH 	"ntlmv2"
+
+static enum {
+	MACkeylen	= 40,	/* MAC key len */
+	MAClen		= 8,	/* signature length */
+	MACoff		= 14,	/* sign. offset from start of SMB (not netbios) pkt */
+	Bliplen		= 8,	/* size of LMv2 client nonce */
+};
+
+static void
+dmp(char *s, int seq, void *buf, int n)
+{
+	int i;
+	char *p = buf;
+
+	print("%s %3d      ", s, seq);
+	while(n > 0){
+		for(i = 0; i < 16 && n > 0; i++, n--)
+			print("%02x ", *p++ & 0xff);
+		if(n > 0)
+			print("\n");
+	}
+	print("\n");
+}
+
+static Auth *
+auth_plain(char *windom, char *keyp, uchar *chal, int len)
+{
+	UserPasswd *up;
+	static Auth *ap;
+
+	USED(chal, len);
+
+	up = auth_getuserpasswd(auth_getkey, "windom=%s proto=pass service=cifs %s",
+		windom, keyp);
+	if(! up)
+		sysfatal("cannot get key - %r");
+
+	ap = emalloc9p(sizeof(Auth));
+	memset(ap, 0, sizeof(ap));
+	ap->user = estrdup9p(up->user);
+	ap->windom = estrdup9p(windom);
+
+	ap->resp[0] = estrdup9p(up->passwd);
+	ap->len[0] = strlen(up->passwd);
+	memset(up->passwd, 0, strlen(up->passwd));
+	free(up);
+
+	return ap;
+}
+
+static Auth *
+auth_lm_and_ntlm(char *windom, char *keyp, uchar *chal, int len)
+{
+	int err;
+	char user[64];
+	Auth *ap;
+	MSchapreply mcr;
+
+	err = auth_respond(chal, len, user, sizeof user, &mcr, sizeof mcr,
+		auth_getkey, "windom=%s proto=mschap role=client service=cifs %s",
+		windom, keyp);
+	if(err == -1)
+		sysfatal("cannot get key - %r");
+
+	ap = emalloc9p(sizeof(Auth));
+	memset(ap, 0, sizeof(ap));
+	ap->user = estrdup9p(user);
+	ap->windom = estrdup9p(windom);
+
+	/* LM response */
+	ap->len[0] = sizeof(mcr.LMresp);
+	ap->resp[0] = emalloc9p(ap->len[0]);
+	memcpy(ap->resp[0], mcr.LMresp, ap->len[0]);
+
+	/* NTLM response */
+	ap->len[1] = sizeof(mcr.NTresp);
+	ap->resp[1] = emalloc9p(ap->len[1]);
+	memcpy(ap->resp[1], mcr.NTresp, ap->len[1]);
+
+	return ap;
+}
+
+/*
+ * NTLM response only, the LM response is a just
+ * copy of the NTLM one. we do this because the lm
+ * response is easily reversed - Google for l0pht
+ * for more info.
+ */
+static Auth *
+auth_ntlm(char *windom, char *keyp, uchar *chal, int len)
+{
+	Auth *ap;
+
+	if((ap = auth_lm_and_ntlm(windom, keyp, chal, len)) == nil)
+		return nil;
+
+	free(ap->resp[0]);
+	ap->len[0] = ap->len[1];
+	ap->resp[0] = emalloc9p(ap->len[0]);
+	memcpy(ap->resp[0], ap->resp[1], ap->len[0]);
+	return ap;
+}
+
+/*
+ * This is not really nescessary as all fields hmac_md5'ed
+ * in the ntlmv2 protocol are less than 64 bytes long, however
+ * I still do this for completeness
+ */
+static DigestState *
+hmac_t64(uchar *data, ulong dlen, uchar *key, ulong klen, uchar *digest,
+	DigestState *state)
+{
+	if(klen > 64)
+		klen = 64;
+	return hmac_md5(data, dlen, key, klen, digest, state);
+}
+
+
+static int
+ntv2_blob(uchar *blob, int len, char *windom)
+{
+	int n;
+	uvlong nttime;
+	Rune r;
+	char *d;
+	uchar *p;
+	enum {			/* name types */
+		Beof,		/* end of name list */
+		Bnetbios,	/* Netbios machine name */
+		Bdomain,	/* Windows Domain name (NT) */
+		Bdnsfqdn,	/* DNS Fully Qualified Domain Name */
+		Bdnsname,	/* DNS machine name (win2k) */
+	};
+
+	p = blob;
+	*p++ = 1;		/* response type */
+	*p++ = 1;		/* max response type understood by client */
+
+	*p++ = 0;
+	*p++ = 0;		/* 2 bytes reserved */
+
+	*p++ = 0;
+	*p++ = 0;
+	*p++ = 0;
+	*p++ = 0;		/* 4 bytes unknown */
+
+	nttime = time(nil);	/* nt time now */
+	nttime += 11644473600LL;
+	nttime *= 10000000LL;
+	*p++ = nttime;
+	*p++ = nttime >> 8;
+	*p++ = nttime >> 16;
+	*p++ = nttime >> 24;
+	*p++ = nttime >> 32;
+	*p++ = nttime >> 40;
+	*p++ = nttime >> 48;
+	*p++ = nttime >> 56;
+
+	genrandom(p, 8);
+	p += 8;			/* client nonce */
+	*p++ = 0x6f;
+	*p++ = 0;
+	*p++ = 0x6e;
+	*p++ = 0;		/* unknown data */
+
+	*p++ = Bdomain;
+	*p++ = 0;		/* name type */
+
+	n = utflen(windom) * 2;
+	*p++ = n;
+	*p++ = n >> 8;		/* name length */
+
+	d = windom;
+	while(*d && p-blob < (len-8)){
+		d += chartorune(&r, d);
+		r = toupperrune(r);
+		*p++ = r;
+		*p++ = r >> 8;
+	}
+
+	*p++ = 0;
+	*p++ = Beof;		/* name type */
+
+	*p++ = 0;
+	*p++ = 0;		/* name length */
+
+	*p++ = 0x65;
+	*p++ = 0;
+	*p++ = 0;
+	*p++ = 0;		/* unknown data */
+	return p - blob;
+}
+
+static Auth *
+auth_ntlmv2(char *windom, char *keyp, uchar *chal, int len)
+{
+	int i, n;
+	Rune r;
+	char *p, *u;
+	uchar v1hash[MD5dlen], blip[Bliplen], blob[1024], v2hash[MD5dlen];
+	uchar c, lm_hmac[MD5dlen], nt_hmac[MD5dlen], nt_sesskey[MD5dlen],
+		lm_sesskey[MD5dlen];
+	DigestState *ds;
+	UserPasswd *up;
+	static Auth *ap;
+
+	up = auth_getuserpasswd(auth_getkey, "windom=%s proto=pass  service=cifs-ntlmv2 %s",
+		windom, keyp);
+	if(!up)
+		sysfatal("cannot get key - %r");
+
+	ap = emalloc9p(sizeof(Auth));
+	memset(ap, 0, sizeof(ap));
+
+	/* Standard says unlimited length, experience says 128 max */
+	if((n = strlen(up->passwd)) > 128)
+		n = 128;
+
+	ds = md4(nil, 0, nil, nil);
+	for(i=0, p=up->passwd; i < n; i++) {
+		p += chartorune(&r, p);
+		c = r;
+		md4(&c, 1, nil, ds);
+		c = r >> 8;
+		md4(&c, 1, nil, ds);
+	}
+	md4(nil, 0, v1hash, ds);
+
+	/*
+	 * Some documentation insists that the username must be forced to
+	 * uppercase, but the domain name should not be. Other shows both
+	 * being forced to uppercase. I am pretty sure this is irrevevant as the
+	 * domain name passed from the remote server always seems to be in
+	 * uppercase already.
+	 */
+        ds = hmac_t64(nil, 0, v1hash, MD5dlen, nil, nil);
+	u = up->user;
+	while(*u){
+		u += chartorune(&r, u);
+		r = toupperrune(r);
+		c = r;
+        	hmac_t64(&c, 1, v1hash, MD5dlen, nil, ds);
+		c = r >> 8;
+        	hmac_t64(&c, 1, v1hash, MD5dlen, nil, ds);
+	}
+	u = windom;
+
+	while(*u){
+		u += chartorune(&r, u);
+		c = r;
+        	hmac_t64(&c, 1, v1hash, MD5dlen, nil, ds);
+		c = r >> 8;
+        	hmac_t64(&c, 1, v1hash, MD5dlen, nil, ds);
+	}
+        hmac_t64(nil, 0, v1hash, MD5dlen, v2hash, ds);
+	ap->user = estrdup9p(up->user);
+	ap->windom = estrdup9p(windom);
+
+	/* LM v2 */
+
+	genrandom(blip, Bliplen);
+        ds = hmac_t64(chal, len, v2hash, MD5dlen, nil, nil);
+	hmac_t64(blip, Bliplen, v2hash, MD5dlen, lm_hmac, ds);
+	ap->len[0] = MD5dlen+Bliplen;
+	ap->resp[0] = emalloc9p(ap->len[0]);
+	memcpy(ap->resp[0], lm_hmac, MD5dlen);
+	memcpy(ap->resp[0]+MD5dlen, blip, Bliplen);
+
+	/* LM v2 session key */
+	hmac_t64(lm_hmac, MD5dlen, v2hash, MD5dlen, lm_sesskey, nil);
+
+	/* LM v2 MAC key */
+	ap->mackey[0] = emalloc9p(MACkeylen);
+	memcpy(ap->mackey[0], lm_sesskey, MD5dlen);
+	memcpy(ap->mackey[0]+MD5dlen, ap->resp[0], MACkeylen-MD5dlen);
+
+	/* NTLM v2 */
+	n = ntv2_blob(blob, sizeof(blob), windom);
+        ds = hmac_t64(chal, len, v2hash, MD5dlen, nil, nil);
+	hmac_t64(blob, n, v2hash, MD5dlen, nt_hmac, ds);
+	ap->len[1] = MD5dlen+n;
+	ap->resp[1] = emalloc9p(ap->len[1]);
+	memcpy(ap->resp[1], nt_hmac, MD5dlen);
+	memcpy(ap->resp[1]+MD5dlen, blob, n);
+
+	/*
+	 * v2hash definitely OK by
+	 * the time we get here.
+	 */
+	/* NTLM v2 session key */
+	hmac_t64(nt_hmac, MD5dlen, v2hash, MD5dlen, nt_sesskey, nil);
+
+	/* NTLM v2 MAC key */
+	ap->mackey[1] = emalloc9p(MACkeylen);
+	memcpy(ap->mackey[1], nt_sesskey, MD5dlen);
+	memcpy(ap->mackey[1]+MD5dlen, ap->resp[1], MACkeylen-MD5dlen);
+	free(up);
+
+	return ap;
+}
+
+struct {
+	char	*name;
+	Auth	*(*func)(char *, char *, uchar *, int);
+} methods[] = {
+	{ "plain",	auth_plain },
+	{ "lm+ntlm",	auth_lm_and_ntlm },
+	{ "ntlm",	auth_ntlm },
+	{ "ntlmv2",	auth_ntlmv2 },
+//	{ "kerberos",	auth_kerberos },
+};
+
+void
+autherr(void)
+{
+	int i;
+
+	fprint(2, "supported auth methods:\t");
+	for(i = 0; i < nelem(methods); i++)
+		fprint(2, "%s ", methods[i].name);
+	fprint(2, "\n");
+	exits("usage");
+}
+
+Auth *
+getauth(char *name, char *windom, char *keyp, int secmode, uchar *chal, int len)
+{
+	int i;
+	Auth *ap;
+
+	if(name == nil){
+		name = DEF_AUTH;
+		if((secmode & SECMODE_PW_ENCRYPT) == 0)
+			sysfatal("plaintext authentication required, use '-a plain'");
+	}
+
+	ap = nil;
+	for(i = 0; i < nelem(methods); i++)
+		if(strcmp(methods[i].name, name) == 0){
+			ap = methods[i].func(windom, keyp, chal, len);
+			break;
+		}
+
+	if(! ap){
+		fprint(2, "%s: %s - unknown auth method\n", argv0, name);
+		autherr();	/* never returns */
+	}
+	return ap;
+}
+
+static int
+genmac(uchar *buf, int len, int seq, uchar key[MACkeylen], uchar ours[MAClen])
+{
+	DigestState *ds;
+	uchar *sig, digest[MD5dlen], theirs[MAClen];
+
+	sig = buf+MACoff;
+	memcpy(theirs, sig, MAClen);
+
+	memset(sig, 0, MAClen);
+	sig[0] = seq;
+	sig[1] = seq >> 8;
+	sig[2] = seq >> 16;
+	sig[3] = seq >> 24;
+
+	ds = md5(key, MACkeylen, nil, nil);
+	md5(buf, len, digest, ds);
+	memcpy(ours, digest, MAClen);
+
+	return memcmp(theirs, ours, MAClen);
+}
+
+int
+macsign(Pkt *p, int seq)
+{
+	int rc, len;
+	uchar *sig, *buf, mac[MAClen];
+
+	sig = p->buf + NBHDRLEN + MACoff;
+	buf = p->buf + NBHDRLEN;
+	len = (p->pos - p->buf) - NBHDRLEN;
+
+#ifdef DEBUG_MAC
+	if(seq & 1)
+		dmp("rx", seq, sig, MAClen);
+#endif
+	rc = 0;
+	if(! p->s->seqrun)
+		memcpy(mac, "BSRSPYL ", 8);	/* no idea, ask MS */
+	else
+		rc = genmac(buf, len, seq, p->s->auth->mackey[0], mac);
+#ifdef DEBUG_MAC
+	if(!(seq & 1))
+		dmp("tx", seq, mac, MAClen);
+#endif
+	memcpy(sig, mac, MAClen);
+	return rc;
+}

+ 804 - 0
sys/src/cmd/cifs/cifs.c

@@ -0,0 +1,804 @@
+#include <u.h>
+#include <libc.h>
+#include <fcall.h>
+#include <thread.h>
+#include <9p.h>
+#include "cifs.h"
+
+static char magic[] = { 0xff, 'S', 'M', 'B' };
+
+Session *
+cifsdial(char *host, char *called, char *sysname)
+{
+	int nbt, fd;
+	char *addr;
+	Session *s;
+
+	if(Debug)
+		fprint(2, "cifsdial: host=%s called=%s sysname=%s\n", host, called, sysname);
+
+	if((addr = netmkaddr(host, "tcp", "cifs")) == nil)
+		return nil;
+
+	nbt = 0;
+	if((fd = dial(addr, nil, nil, nil)) == -1){
+		nbt = 1;
+		if((fd = nbtdial(host, called, sysname)) == -1)
+			return nil;
+	}
+
+	s = emalloc9p(sizeof(Session));
+	memset(s, 0, sizeof(Session));
+
+	s->fd = fd;
+	s->nbt = nbt;
+	s->mtu = MTU;
+	s->pid = getpid();
+	s->mid = time(nil) ^ getpid();
+	s->uid = NO_UID;
+	s->seq = 0;
+	s->seqrun = 0;
+	s->secmode = SECMODE_SIGN_ENABLED;	/* hope for the best */
+	s->flags2 = FL2_KNOWS_LONG_NAMES | FL2_HAS_LONG_NAMES | FL2_PAGEING_IO;
+	s->macidx = -1;
+
+	return s;
+}
+
+void
+cifsclose(Session *s)
+{
+	if(s->fd)
+		close(s->fd);
+	free(s);
+}
+
+Pkt *
+cifshdr(Session *s, Share *sp, int cmd)
+{
+	Pkt *p;
+	int sign, tid, dfs;
+
+	dfs = 0;
+	tid = NO_TID;
+	Active = IDLE_TIME;
+	sign = s->secmode & SECMODE_SIGN_ENABLED? FL2_PACKET_SIGNATURES: 0;
+
+	if(sp){
+		tid = sp->tid;
+// FIXME!		if(sp->options & SMB_SHARE_IS_IN_DFS)
+// FIXME!			dfs = FL2_DFS;
+	}
+
+	p = emalloc9p(sizeof(Pkt) + MTU);
+	memset(p, 0, sizeof(Pkt) +MTU);
+
+	p->buf = (uchar *)p + sizeof(Pkt);
+	p->s = s;
+
+	qlock(&s->seqlock);
+	if(s->seqrun){
+		p->seq = s->seq;
+		s->seq = (s->seq + 2) % 0x10000;
+	}
+	qunlock(&s->seqlock);
+
+	nbthdr(p);
+	pmem(p, magic, nelem(magic));
+	p8(p, cmd);
+	pl32(p, 0);				/* status (error) */
+	p8(p, FL_CASELESS_NAMES | FL_CANNONICAL_NAMES); /* flags */
+	pl16(p, s->flags2 | dfs | sign);	/* flags2 */
+	pl16(p, (s->pid >> 16) & 0xffff);	/* PID MS bits */
+	pl32(p, p->seq);			/* MAC / sequence number */
+	pl32(p, 0);				/* MAC */
+	pl16(p, 0);				/* padding */
+
+	pl16(p, tid);
+	pl16(p, s->pid & 0xffff);
+	pl16(p, s->uid);
+	pl16(p, s->mid);
+
+	p->wordbase = p8(p, 0);		/* filled in by pbytes() */
+
+	return p;
+}
+
+void
+pbytes(Pkt *p)
+{
+	int n;
+
+	assert(p->wordbase != nil);	/* cifshdr not called */
+	assert(p->bytebase == nil);	/* called twice */
+
+	n = p->pos - p->wordbase;
+	assert(n % 2 != 0);		/* even addr */
+	*p->wordbase = n / 2;
+
+	p->bytebase = pl16(p, 0);	/* filled in by cifsrpc() */
+}
+
+static void
+dmp(int seq, uchar *buf)
+{
+	int i;
+
+	if(seq == 99)
+		print("\n   ");
+	else
+		print("%+2d ", seq);
+	for(i = 0; i < 8; i++)
+		print("%02x ", buf[i] & 0xff);
+	print("\n");
+}
+
+int
+cifsrpc(Pkt *p)
+{
+	int flags2, got, err;
+	uint tid, uid, seq;
+	uchar *pos;
+	char m[nelem(magic)];
+
+	pos = p->pos;
+	if(p->bytebase){
+		p->pos = p->bytebase;
+		pl16(p, pos - (p->bytebase + 2)); /* 2 = sizeof bytecount */
+	}
+	p->pos = pos;
+
+	if(p->s->secmode & SECMODE_SIGN_ENABLED)
+		macsign(p, p->seq);
+
+	qlock(&p->s->rpclock);
+	got = nbtrpc(p);
+	qunlock(&p->s->rpclock);
+	if(got == -1)
+		return -1;
+
+	gmem(p, m, nelem(magic));
+	if(memcmp(m, magic, nelem(magic)) != 0){
+		werrstr("cifsrpc: bad magic number in packet %20ux%02ux%02ux%02ux",
+			m[0], m[1], m[2], m[3]);
+		return -1;
+	}
+
+	g8(p);				/* cmd */
+	err = gl32(p);			/* errcode */
+	g8(p);				/* flags */
+	flags2 = gl16(p);		/* flags2 */
+	gl16(p);			/* PID MS bits */
+	seq = gl32(p);			/* reserved */
+	gl32(p);			/* MAC (if in use) */
+	gl16(p);			/* Padding */
+	tid = gl16(p);			/* TID */
+	gl16(p);			/* PID lsbs */
+	uid = gl16(p);			/* UID */
+	gl16(p);			/* mid */
+	g8(p);				/* word count */
+
+	if(p->s->secmode & SECMODE_SIGN_ENABLED){
+		if(macsign(p, p->seq+1) != 0 && p->s->seqrun){
+			werrstr("cifsrpc: invalid packet signature");
+print("MAC signature bad\n");
+// FIXME: for debug only			return -1;
+		}
+	}else{
+		/*
+		 * We allow the sequence number of zero as some old samba
+		 * servers seem to fall back to this unexpectedly
+		 * after reporting sequence numbers correctly for a while.
+		 *
+		 * Some other samba servers seem to always report a sequence
+		 * number of zero if MAC signing is disabled, so we have to
+		 * catch that too.
+		 */
+		if(p->s->seqrun && seq != p->seq && seq != 0){
+			werrstr("%ux != %ux bad sequence number", seq, p->seq);
+			return -1;
+		}
+	}
+
+	p->tid = tid;
+	if(p->s->uid == NO_UID)
+		p->s->uid = uid;
+
+	if(flags2 & FL2_NT_ERRCODES){
+		/* is it a real error rather than info/warning/chatter */
+		if((err & 0xF0000000) == 0xC0000000){
+			werrstr("%s", nterrstr(err));
+			return -1;
+		}
+	}else{
+		if(err){
+			werrstr("%s", doserrstr(err));
+			return -1;
+		}
+	}
+	return got;
+}
+
+
+/*
+ * Some older servers (old samba) prefer to talk older
+ * dialects but if given no choice they will talk the
+ * more modern ones, so we don't give them the choice.
+ */
+int
+CIFSnegotiate(Session *s, long *svrtime, char *domain, int domlen, char *cname, int cnamlen)
+{
+	int d, i;
+	char *ispeak = "NT LM 0.12";
+	char *dialects[] = {
+//		{ "PC NETWORK PROGRAM 1.0"},
+//		{ "MICROSOFT NETWORKS 1.03"},
+//		{ "MICROSOFT NETWORKS 3.0"},
+//		{ "LANMAN1.0"},
+//		{ "LM1.2X002"},
+//		{ "NT LANMAN 1.0"},
+		{ "NT LM 0.12" },
+	};
+	Pkt *p;
+
+	p = cifshdr(s, nil, SMB_COM_NEGOTIATE);
+	pbytes(p);
+	for(i = 0; i < nelem(dialects); i++){
+		p8(p, STR_DIALECT);
+		pstr(p, dialects[i]);
+	}
+
+	if(cifsrpc(p) == -1){
+		free(p);
+		return -1;
+	}
+
+	d = gl16(p);
+	if(d < 0 || d > nelem(dialects)){
+		werrstr("no CIFS dialect in common");
+		free(p);
+		return -1;
+	}
+
+	if(strcmp(dialects[d], ispeak) != 0){
+		werrstr("%s dialect unsupported", dialects[d]);
+		free(p);
+		return -1;
+	}
+
+	s->secmode = g8(p);			/* Security mode */
+
+	gl16(p);				/* Max outstanding requests */
+	gl16(p);				/* Max VCs */
+	s->mtu = gl32(p);			/* Max buffer size */
+	gl32(p);				/* Max raw buffer size (depricated) */
+	gl32(p);				/* Session key */
+	s->caps = gl32(p);			/* Server capabilities */
+	*svrtime = gvtime(p);			/* fileserver time */
+	s->tz = (short)gl16(p) * 60; /* TZ in mins, is signed (SNIA doc is wrong) */
+	s->challen = g8(p);			/* Encryption key length */
+	gl16(p);
+	gmem(p, s->chal, s->challen);		/* Get the challenge */
+	gstr(p, domain, domlen);		/* source domain */
+
+	{		/* NetApp Filer seem not to report its called name */
+		char *cn = emalloc9p(cnamlen);
+
+		gstr(p, cn, cnamlen);		/* their name */
+		if(strlen(cn) > 0)
+			memcpy(cname, cn, cnamlen);
+		free(cn);
+	}
+
+	if(s->caps & CAP_UNICODE)
+		s->flags2 |= FL2_UNICODE;
+
+	free(p);
+	return 0;
+}
+
+int
+CIFSsession(Session *s)
+{
+	char os[64], *q;
+	Rune r;
+	Pkt *p;
+	enum {
+		mycaps = CAP_UNICODE | CAP_LARGE_FILES | CAP_NT_SMBS |
+			CAP_NT_FIND | CAP_STATUS32,
+	};
+
+	s->seqrun = 1;	/* activate the sequence number generation/checking */
+
+	p = cifshdr(s, nil, SMB_COM_SESSION_SETUP_ANDX);
+	p8(p, 0xFF);			/* No secondary command */
+	p8(p, 0);			/* Reserved (must be zero) */
+	pl16(p, 0);			/* Offset to next command */
+	pl16(p, MTU);			/* my max buffer size */
+	pl16(p, 1);			/* my max multiplexed pending requests */
+	pl16(p, 0);			/* Virtual connection # */
+	pl32(p, 0);			/* Session key (if vc != 0) */
+
+
+	if((s->secmode & SECMODE_PW_ENCRYPT) == 0) {
+		pl16(p, utflen(Sess->auth->resp[0])*2 + 2); /* passwd size */
+		pl16(p, utflen(Sess->auth->resp[0])*2 + 2); /* passwd size (UPPER CASE) */
+		pl32(p, 0);			/* Reserved */
+		pl32(p, mycaps);
+		pbytes(p);
+
+		for(q = Sess->auth->resp[0]; *q; ){
+			q += chartorune(&r, q);
+			pl16(p, toupperrune(r));
+		}
+		pl16(p, 0);
+
+		for(q = Sess->auth->resp[0]; *q; ){
+			q += chartorune(&r, q);
+			pl16(p, r);
+		}
+		pl16(p, 0);
+	}else{
+		pl16(p, Sess->auth->len[0]);	/* LM passwd size */
+		pl16(p, Sess->auth->len[1]);	/* NTLM passwd size */
+		pl32(p, 0);			/* Reserved  */
+		pl32(p, mycaps);
+		pbytes(p);
+
+		pmem(p, Sess->auth->resp[0], Sess->auth->len[0]);
+		pmem(p, Sess->auth->resp[1], Sess->auth->len[1]);
+	}
+
+	pstr(p, Sess->auth->user);	/* Account name */
+	pstr(p, Sess->auth->windom);	/* Primary domain */
+	pstr(p, "plan9");		/* Client OS */
+	pstr(p, argv0);			/* Client LAN Manager type */
+
+	if(cifsrpc(p) == -1){
+		free(p);
+		return -1;
+	}
+
+	g8(p);				/* Reserved (0) */
+	gl16(p);			/* Offset to next command wordcount */
+	Sess->isguest = gl16(p) & 1;	/* logged in as guest */
+
+	gl16(p);
+	gl16(p);
+	/* no security blob here - we don't understand extended security anyway */
+	gstr(p, os, sizeof(os));
+	s->remos = estrdup9p(os);
+
+	free(p);
+	return 0;
+}
+
+
+CIFStreeconnect(Session *s, char *cname, char *tree, Share *sp)
+{
+	int len;
+	char *resp, *path;
+	char zeros[24];
+	Pkt *p;
+
+	resp = Sess->auth->resp[0];
+	len  = Sess->auth->len[0];
+	if((s->secmode & SECMODE_USER) != SECMODE_USER){
+		memset(zeros, 0, sizeof(zeros));
+		resp = zeros;
+		len = sizeof(zeros);
+	}
+
+	p = cifshdr(s, nil, SMB_COM_TREE_CONNECT_ANDX);
+	p8(p, 0xFF);			/* Secondary command */
+	p8(p, 0);			/* Reserved */
+	pl16(p, 0);			/* Offset to next Word Count */
+	pl16(p, 0);			/* Flags */
+
+	if((s->secmode & SECMODE_PW_ENCRYPT) == 0){
+		pl16(p, len+1);		/* password len, including null */
+		pbytes(p);
+		pascii(p, resp);
+	}else{
+		pl16(p, len);
+		pbytes(p);
+		pmem(p, resp, len);
+	}
+
+	path = smprint("//%s/%s", cname, tree);
+	strupr(path);
+	ppath(p, path);			/* path */
+	free(path);
+
+	pascii(p, "?????");	/* service type any (so we can do RAP calls) */
+
+	if(cifsrpc(p) == -1){
+		free(p);
+		return -1;
+	}
+	g8(p);				/* Secondary command */
+	g8(p);				/* Reserved */
+	gl16(p);			/* Offset to next command */
+	sp->options = g8(p);		/* options supported */
+	sp->tid = p->tid;		/* get received TID from packet header */
+	free(p);
+	return 0;
+}
+
+int
+CIFSlogoff(Session *s)
+{
+	int rc;
+	Pkt *p;
+
+	p = cifshdr(s, nil, SMB_COM_LOGOFF_ANDX);
+	p8(p, 0xFF);			/* No ANDX command */
+	p8(p, 0);			/* Reserved (must be zero) */
+	pl16(p, 0);			/* offset ot ANDX */
+	pbytes(p);
+	rc = cifsrpc(p);
+
+	free(p);
+	return rc;
+}
+
+int
+CIFStreedisconnect(Session *s, Share *sp)
+{
+	int rc;
+	Pkt *p;
+
+	p = cifshdr(s, sp, SMB_COM_TREE_DISCONNECT);
+	pbytes(p);
+	rc = cifsrpc(p);
+
+	free(p);
+	return rc;
+}
+
+
+int
+CIFSdeletefile(Session *s, Share *sp, char *name)
+{
+	int rc;
+	Pkt *p;
+
+	p = cifshdr(s, sp, SMB_COM_DELETE);
+	pl16(p, ATTR_HIDDEN|ATTR_SYSTEM);	/* search attributes */
+	pbytes(p);
+	p8(p, STR_ASCII);			/* buffer format */
+	ppath(p, name);
+	rc = cifsrpc(p);
+
+	free(p);
+	return rc;
+}
+
+int
+CIFSdeletedirectory(Session *s, Share *sp, char *name)
+{
+	int rc;
+	Pkt *p;
+
+	p = cifshdr(s, sp, SMB_COM_DELETE_DIRECTORY);
+	pbytes(p);
+	p8(p, STR_ASCII);		/* buffer format */
+	ppath(p, name);
+	rc = cifsrpc(p);
+
+	free(p);
+	return rc;
+}
+
+int
+CIFScreatedirectory(Session *s, Share *sp, char *name)
+{
+	int rc;
+	Pkt *p;
+
+	p = cifshdr(s, sp, SMB_COM_CREATE_DIRECTORY);
+	pbytes(p);
+	p8(p, STR_ASCII);
+	ppath(p, name);
+	rc = cifsrpc(p);
+
+	free(p);
+	return rc;
+}
+
+int
+CIFSrename(Session *s, Share *sp, char *old, char *new)
+{
+	int rc;
+	Pkt *p;
+
+	p = cifshdr(s, sp, SMB_COM_RENAME);
+	pl16(p, ATTR_HIDDEN|ATTR_SYSTEM|ATTR_DIRECTORY); /* search attributes */
+	pbytes(p);
+	p8(p, STR_ASCII);
+	ppath(p, old);
+	p8(p, STR_ASCII);
+	ppath(p, new);
+	rc = cifsrpc(p);
+
+	free(p);
+	return rc;
+}
+
+
+/* for NT4/Win2k/XP */
+int
+CIFS_NT_opencreate(Session *s, Share *sp, char *name, int flags, int options,
+	int attrs, int access, int share, int action, int *result, FInfo *fi)
+{
+	Pkt *p;
+	int fh;
+
+	p = cifshdr(s, sp, SMB_COM_NT_CREATE_ANDX);
+	p8(p, 0xFF);			/* Secondary command */
+	p8(p, 0);			/* Reserved */
+	pl16(p, 0);			/* Offset to next command */
+	p8(p, 0);			/* Reserved */
+	pl16(p, utflen(name) *2);	/* file name len */
+	pl32(p, flags);			/* Flags */
+	pl32(p, 0);			/* fid of cwd, if relative path */
+	pl32(p, access);		/* access desired */
+	pl64(p, 0);			/* initial allocation size */
+	pl32(p, attrs);			/* Extended attributes */
+	pl32(p, share);			/* Share Access */
+	pl32(p, action);		/* What to do on success/failure */
+	pl32(p, options);		/* Options */
+	pl32(p, SECURITY_IMPERSONATION); /* Impersonation level */
+	p8(p, SECURITY_CONTEXT_TRACKING | SECURITY_EFFECTIVE_ONLY); /* security flags */
+	pbytes(p);
+	p8(p, 0);			/* FIXME: padding? */
+	ppath(p, name);			/* filename */
+
+	if(cifsrpc(p) == -1){
+		free(p);
+		return -1;
+	}
+
+	memset(fi, 0, sizeof(FInfo));
+	g8(p);				/* Secondary command */
+	g8(p);				/* Reserved */
+	gl16(p);			/* Offset to next command */
+	g8(p);				/* oplock granted */
+	fh = gl16(p);			/* FID for opened object */
+	*result = gl32(p);		/* create action taken */
+	gl64(p);			/* creation time */
+	fi->accessed = gvtime(p);	/* last access time */
+	fi->written = gvtime(p);	/* last written time */
+	fi->changed = gvtime(p);	/* change time */
+	fi->attribs = gl32(p);		/* extended attributes */
+	gl64(p);			/* bytes allocated */
+	fi->size = gl64(p);		/* file size */
+
+	free(p);
+	return fh;
+}
+
+/* for Win95/98/ME */
+CIFS_SMB_opencreate(Session *s, Share *sp, char *name, int access,
+	int attrs, int action, int *result)
+{
+	Pkt *p;
+	int fh;
+
+	p = cifshdr(s, sp, SMB_COM_OPEN_ANDX);
+	p8(p, 0xFF);			/* Secondary command */
+	p8(p, 0);			/* Reserved */
+	pl16(p, 0);			/* Offset to next command */
+	pl16(p, 0);			/* Flags (0 == no stat(2) info) */
+	pl16(p, access);		/* desired access */
+	pl16(p, ATTR_HIDDEN|ATTR_SYSTEM);/* search attributes */
+	pl16(p, attrs);			/* file attribytes */
+	pdatetime(p, 0);		/* creation time (0 == now) */
+	pl16(p, action);		/* What to do on success/failure */
+	pl32(p, 0);			/* allocation size */
+	pl32(p, 0);			/* reserved */
+	pl32(p, 0);			/* reserved */
+	pbytes(p);
+	ppath(p, name);			/* filename */
+
+	if(cifsrpc(p) == -1){
+		free(p);
+		return -1;
+	}
+
+	g8(p);				/* Secondary command */
+	g8(p);				/* Reserved */
+	gl16(p);			/* Offset to next command */
+	fh = gl16(p);			/* FID for opened object */
+	gl16(p);			/* extended attributes */
+	gvtime(p);			/* last written time */
+	gl32(p);			/* file size */
+	gl16(p);			/* file type (disk/fifo/printer etc) */
+	gl16(p);			/* device status (for fifos) */
+	*result = gl16(p);		/* access granted */
+
+	free(p);
+	return fh;
+}
+
+vlong
+CIFSwrite(Session *s, Share *sp, int fh, uvlong off, void *buf, vlong n)
+{
+	Pkt *p;
+	vlong got;
+
+	/* FIXME: Payload should be padded to long boundary */
+	assert((n   & 0xffffffff00000000LL) == 0 || s->caps & CAP_LARGE_FILES);
+	assert((off & 0xffffffff00000000LL) == 0 || s->caps & CAP_LARGE_FILES);
+	assert(n < s->mtu - T2HDRLEN || s->caps & CAP_LARGE_WRITEX);
+
+	p = cifshdr(s, sp, SMB_COM_WRITE_ANDX);
+	p8(p, 0xFF);			/* Secondary command */
+	p8(p, 0);			/* Reserved */
+	pl16(p, 0);			/* Offset to next command */
+	pl16(p, fh);			/* File handle */
+	pl32(p, off & 0xffffffff);	/* LSBs of Offset */
+	pl32(p, 0);			/* Reserved (0) */
+	pl16(p, s->nocache);		/* Write mode (0 - write through) */
+	pl16(p, 0);			/* Bytes remaining */
+	pl16(p, n >> 16);		/* MSBs of length */
+	pl16(p, n & 0xffffffff);	/* LSBs of length */
+	pl16(p, T2HDRLEN);		/* Offset to data, in bytes */
+	pl32(p, off >> 32);		/* MSBs of offset */
+	pbytes(p);
+
+	p->pos = p->buf +T2HDRLEN +NBHDRLEN;
+	pmem(p, buf, n);		/* Data */
+
+	if(cifsrpc(p) == -1){
+		free(p);
+		return -1;
+	}
+
+	g8(p);				/* Secondary command */
+	g8(p);				/* Reserved */
+	gl16(p);			/* Offset to next command */
+	got = gl16(p);			/* LSWs of bytes written */
+	gl16(p);			/* remaining (space ?) */
+	got |= (gl16(p) << 16);		/* MSWs of bytes written */
+
+	free(p);
+	return got;
+}
+
+vlong
+CIFSread(Session *s, Share *sp, int fh, uvlong off, void *buf, vlong n,
+	vlong minlen)
+{
+	int doff;
+	vlong got;
+	Pkt *p;
+
+	assert((n   & 0xffffffff00000000LL) == 0 || s->caps & CAP_LARGE_FILES);
+	assert((off & 0xffffffff00000000LL) == 0 || s->caps & CAP_LARGE_FILES);
+	assert(n < s->mtu - T2HDRLEN || s->caps & CAP_LARGE_READX);
+
+	p = cifshdr(s, sp, SMB_COM_READ_ANDX);
+	p8(p, 0xFF);			/* Secondary command */
+	p8(p, 0);			/* Reserved */
+	pl16(p, 0);			/* Offset to next command */
+	pl16(p, fh);			/* File handle */
+	pl32(p, off & 0xffffffff);	/* Offset to beginning of write */
+	pl16(p, n);			/* Maximum number of bytes to return */
+	pl16(p, minlen);		/* Minimum number of bytes to return */
+	pl32(p, (uint)n >> 16);		/* MSBs of maxlen */
+	pl16(p, 0);			/* Bytes remaining to satisfy request */
+	pl32(p, off >> 32);		/* MS 32 bits of offset */
+	pbytes(p);
+
+	if(cifsrpc(p) == -1){
+		free(p);
+		return -1;
+	}
+
+	g8(p);				/* Secondary command */
+	g8(p);				/* Reserved */
+	gl16(p);			/* Offset to next command */
+	gl16(p);			/* Remaining */
+	gl16(p);			/* Compression mode */
+	gl16(p);			/* Reserved */
+	got = gl16(p);			/* length */
+	doff = gl16(p);			/* Offset from header to data */
+	got |= gl16(p) << 16;
+
+	p->pos = p->buf + doff + NBHDRLEN;
+
+	gmem(p, buf, got);		 /* data */
+	free(p);
+	return got;
+}
+
+int
+CIFSflush(Session *s, Share *sp, int fh)
+{
+	int rc;
+	Pkt *p;
+
+	p = cifshdr(s, sp, SMB_COM_FLUSH);
+	pl16(p, fh);			/* fid */
+	pbytes(p);
+	rc = cifsrpc(p);
+
+	free(p);
+	return rc;
+}
+
+/*
+ * Setting the time of last write to -1 gives "now" if the file
+ * was written and leaves it the same if the file wasn't written.
+ */
+int
+CIFSclose(Session *s, Share *sp, int fh)
+{
+	int rc;
+	Pkt *p;
+
+	p = cifshdr(s, sp, SMB_COM_CLOSE);
+	pl16(p, fh);			/* fid */
+	pl32(p, ~0L);			/* Time of last write (none) */
+	pbytes(p);
+	rc = cifsrpc(p);
+
+	free(p);
+	return rc;
+}
+
+
+int
+CIFSfindclose2(Session *s, Share *sp, int sh)
+{
+	int rc;
+	Pkt *p;
+
+	p = cifshdr(s, sp, SMB_COM_FIND_CLOSE2);
+	pl16(p, sh);			/* sid */
+	pbytes(p);
+	rc = cifsrpc(p);
+
+	free(p);
+	return rc;
+}
+
+
+int
+CIFSecho(Session *s)
+{
+	Pkt *p;
+	int rc;
+
+	p = cifshdr(s, nil, SMB_COM_ECHO);
+	pl16(p, 1);				/* number of replies */
+	pbytes(p);
+	pascii(p, "abcdefghijklmnopqrstuvwxyz"); /* data */
+
+	rc = cifsrpc(p);
+	free(p);
+	return rc;
+}
+
+
+int
+CIFSsetinfo(Session *s, Share *sp, char *path, FInfo *fip)
+{
+	int rc;
+	Pkt *p;
+
+	p = cifshdr(s, sp, SMB_COM_SET_INFORMATION);
+	pl16(p, fip->attribs);
+	pl32(p, time(nil) - s->tz);	/* modified time */
+	pl64(p, 0);			/* reserved */
+	pl16(p, 0);			/* reserved */
+
+	pbytes(p);
+	p8(p, STR_ASCII);		/* buffer format */
+	ppath(p, path);
+
+	rc = cifsrpc(p);
+	free(p);
+	return rc;
+}

+ 635 - 0
sys/src/cmd/cifs/cifs.h

@@ -0,0 +1,635 @@
+/* cifs.h */
+
+enum {
+	Proot		= 1,		/* LSBs of qid.path for root dir */
+	Pinfo		= 2,		/* LSBs of qid.path for info files */
+	Pshare		= 4,		/* LSBs of qid.path for share dirs */
+	NBHDRLEN	= 4,		/* length of a netbios header */
+	T2HDRLEN	= 64,		/* Transaction2 header length */
+	NO_UID		= 0xffff,	/* initial UID on connect */
+	NO_TID		= 0xffff,	/* initial TID on connect */
+	MTU		= 0xefff,	/* our MTU */
+	CACHETIME	= 2,		/* seconds read-ahead is valid for */
+	CIFS_FNAME_MAX	= 0xff,		/* max file path component len */
+	OVERHEAD	= 80,		/* max packet overhead when reading & writing */
+	IDLE_TIME	= 10,		/* keepalive send rate in mins */
+	NBNSTOUT	= 300,		/* Netbios Name Service Timeout (300ms x 3retrys) */
+	NBRPCTOUT	= 45*60*1000,	/* Netbios RPC Timeout (45sec) */
+	MAX_SHARES	= 4096,		/* static table of shares attached */
+	RAP_ERR_MOREINFO= 234,		/* non-error code, more info to be fetched */
+	MAX_DFS_PATH	= 512,		/* MS says never more than 250 chars... */
+};
+
+enum {
+	SMB_COM_CREATE_DIRECTORY	= 0x00,
+	SMB_COM_DELETE_DIRECTORY	= 0x01,
+	SMB_COM_OPEN			= 0x02,
+	SMB_COM_CREATE			= 0x03,
+	SMB_COM_CLOSE			= 0x04,
+	SMB_COM_FLUSH			= 0x05,
+	SMB_COM_DELETE			= 0x06,
+	SMB_COM_RENAME			= 0x07,
+	SMB_COM_QUERY_INFORMATION	= 0x08,
+	SMB_COM_SET_INFORMATION		= 0x09,
+	SMB_COM_READ			= 0x0A,
+	SMB_COM_WRITE			= 0x0B,
+	SMB_COM_LOCK_BYTE_RANGE		= 0x0C,
+	SMB_COM_UNLOCK_BYTE_RANGE	= 0x0D,
+	SMB_COM_CREATE_TEMPORARY	= 0x0E,
+	SMB_COM_CREATE_NEW		= 0x0F,
+	SMB_COM_CHECK_DIRECTORY		= 0x10,
+	SMB_COM_PROCESS_EXIT		= 0x11,
+	SMB_COM_SEEK			= 0x12,
+	SMB_COM_LOCK_AND_READ		= 0x13,
+	SMB_COM_WRITE_AND_UNLOCK	= 0x14,
+	SMB_COM_READ_RAW		= 0x1A,
+	SMB_COM_READ_MPX		= 0x1B,
+	SMB_COM_READ_MPX_SECONDARY	= 0x1C,
+	SMB_COM_WRITE_RAW		= 0x1D,
+	SMB_COM_WRITE_MPX		= 0x1E,
+	SMB_COM_WRITE_MPX_SECONDARY	= 0x1F,
+	SMB_COM_WRITE_COMPLETE		= 0x20,
+	SMB_COM_QUERY_SERVER		= 0x21,
+	SMB_COM_SET_INFORMATION2	= 0x22,
+	SMB_COM_QUERY_INFORMATION2	= 0x23,
+	SMB_COM_LOCKING_ANDX		= 0x24,
+	SMB_COM_TRANSACTION		= 0x25,
+	SMB_COM_TRANSACTION_SECONDARY	= 0x26,
+	SMB_COM_IOCTL			= 0x27,
+	SMB_COM_IOCTL_SECONDARY		= 0x28,
+	SMB_COM_COPY			= 0x29,
+	SMB_COM_MOVE			= 0x2A,
+	SMB_COM_ECHO			= 0x2B,
+	SMB_COM_WRITE_AND_CLOSE		= 0x2C,
+	SMB_COM_OPEN_ANDX		= 0x2D,
+	SMB_COM_READ_ANDX		= 0x2E,
+	SMB_COM_WRITE_ANDX		= 0x2F,
+	SMB_COM_NEW_FILE_SIZE		= 0x30,
+	SMB_COM_CLOSE_AND_TREE_DISC	= 0x31,
+	SMB_COM_TRANSACTION2		= 0x32,
+	SMB_COM_TRANSACTION2_SECONDARY	= 0x33,
+	SMB_COM_FIND_CLOSE2		= 0x34,
+	SMB_COM_FIND_NOTIFY_CLOSE	= 0x35,
+	SMB_COM_TREE_CONNECT		= 0x70,
+	SMB_COM_TREE_DISCONNECT		= 0x71,
+	SMB_COM_NEGOTIATE		= 0x72,
+	SMB_COM_SESSION_SETUP_ANDX	= 0x73,
+	SMB_COM_LOGOFF_ANDX		= 0x74,
+	SMB_COM_TREE_CONNECT_ANDX	= 0x75,
+	SMB_COM_QUERY_INFORMATION_DISK	= 0x80,
+	SMB_COM_SEARCH			= 0x81,
+	SMB_COM_FIND			= 0x82,
+	SMB_COM_FIND_UNIQUE		= 0x83,
+	SMB_COM_FIND_CLOSE		= 0x84,
+	SMB_COM_NT_TRANSACT		= 0xA0,
+	SMB_COM_NT_TRANSACT_SECONDARY	= 0xA1,
+	SMB_COM_NT_CREATE_ANDX		= 0xA2,
+	SMB_COM_NT_CANCEL		= 0xA4,
+	SMB_COM_NT_RENAME		= 0xA5,
+	SMB_COM_OPEN_PRINT_FILE		= 0xC0,
+	SMB_COM_WRITE_PRINT_FILE	= 0xC1,
+	SMB_COM_CLOSE_PRINT_FILE	= 0xC2,
+	SMB_COM_GET_PRINT_QUEUE		= 0xC3,
+	SMB_COM_READ_BULK		= 0xD8,
+	SMB_COM_WRITE_BULK		= 0xD9,
+	SMB_COM_WRITE_BULK_DATA		= 0xDA,
+
+	TRANS2_OPEN2			= 0x00,
+	TRANS2_FIND_FIRST2		= 0x01,
+	TRANS2_FIND_NEXT2		= 0x02,
+	TRANS2_QUERY_FS_INFORMATION	= 0x03,
+	TRANS2_QUERY_PATH_INFORMATION	= 0x05,
+	TRANS2_SET_PATH_INFORMATION	= 0x06,
+	TRANS2_QUERY_FILE_INFORMATION	= 0x07,
+	TRANS2_SET_FILE_INFORMATION	= 0x08,
+	TRANS2_CREATE_DIRECTORY 	= 0x0D,
+	TRANS2_SESSION_SETUP		= 0x0E,
+	TRANS2_GET_DFS_REFERRAL		= 0x10,
+
+	NT_TRANSACT_CREATE 		= 0x01,
+	NT_TRANSACT_IOCTL 		= 0x02,
+	NT_TRANSACT_SET_SECURITY_DESC 	= 0x03,
+	NT_TRANSACT_NOTIFY_CHANGE 	= 0x04,
+	NT_TRANSACT_RENAME 		= 0x05,
+	NT_TRANSACT_QUERY_SECURITY_DESC = 0x06
+};
+
+enum {						/* CIFS flags */
+	FL_CASELESS_NAMES	= 1<<3,
+	FL_CANNONICAL_NAMES	= 1<<4,
+
+	FL2_KNOWS_LONG_NAMES	= 1<<0,
+	FL2_PACKET_SIGNATURES	= 1<<2,
+	FL2_HAS_LONG_NAMES	= 1<<6,
+	FL2_EXTENDED_SECURITY	= 1<<11,
+	FL2_DFS			= 1<<12,
+	FL2_PAGEING_IO		= 1<<13,	/* allow read of exec only files */
+	FL2_NT_ERRCODES		= 1<<14,
+	FL2_UNICODE		= 1<<15,
+};
+
+enum {						/* Capabilities Negoiated */
+	CAP_RAW_MODE		= 1,
+	CAP_MPX_MODE		= 1<<1,
+	CAP_UNICODE		= 1<<2,
+	CAP_LARGE_FILES		= 1<<3,		/* 64 bit files */
+	CAP_NT_SMBS		= 1<<4,
+	CAP_RPC_REMOTE_APIS	= 1<<5,
+	CAP_STATUS32		= 1<<6,
+	CAP_L2_OPLOCKS		= 1<<7,
+	CAP_LOCK_READ		= 1<<8,
+	CAP_NT_FIND		= 1<<9,
+	CAP_DFS			= 1<<12,
+	CAP_INFO_PASSTHRU	= 1<<13,
+	CAP_LARGE_READX		= 1<<14,
+	CAP_LARGE_WRITEX	= 1<<15,
+	CAP_UNIX		= 1<<23,
+	CAP_BULK_TRANSFER	= 1<<29,
+	CAP_COMPRESSED		= 1<<30,
+	CAP_EX_SECURE		= 1<<31
+};
+
+enum {	/* string prefixes */
+	STR_DIALECT 		= 2,
+	STR_PATH 		= 3,
+	STR_ASCII 		= 4,
+};
+
+enum {	/* optional support bits in treeconnect */
+	SMB_SUPPROT_SEARCH_BITS = 1,
+	SMB_SHARE_IS_IN_DFS 	= 2,
+};
+
+enum {	/* DFS referal header flags */
+	DFS_HEADER_ROOT	 	= 1,	/* Server type, returns root targets */
+	DFS_HEADER_STORAGE 	= 2,	/* server has storage, no more referals */
+	DFS_HEADER_FAILBACK 	= 4,	/* target failback enabled */
+};
+
+enum {	/* DFS referal entry flags */
+	DFS_SERVER_ROOT	 	= 1,	/* Server type, returns root targets */
+	DFS_REFERAL_LIST 	= 0x200,	/* reply is a list (v3 only) */
+	DFS_REFERAL_SET 	= 0x400,	/* target is a member of a set */
+};
+
+enum {	/* share types */
+	STYPE_DISKTREE		= 0,
+	STYPE_PRINTQ		= 1,
+	STYPE_DEVICE		= 2,
+	STYPE_IPC		= 3,
+	STYPE_SPECIAL		= 4,
+	STYPE_TEMP		= 5,
+};
+
+enum {	/* Security */
+	SECMODE_USER		= 0x01,	/* i.e. not share level security */
+	SECMODE_PW_ENCRYPT	= 0x02,
+	SECMODE_SIGN_ENABLED	= 0x04,
+	SECMODE_SIGN_REQUIRED	= 0x08,
+};
+
+enum {	/* file access rights */
+	DELETE			= 0x00010000,
+	SYNCHRONIZE		= 0x00100000,
+
+	READ_CONTROL		= 0x00020000,
+	GENERIC_ALL		= 0x10000000,
+	GENERIC_EXECUTE		= 0x20000000,
+	GENERIC_WRITE		= 0x40000000,
+	GENERIC_READ		= 0x80000000,
+
+	ATTR_READONLY 		= 0x0001,
+	ATTR_HIDDEN   		= 0x0002,
+	ATTR_SYSTEM   		= 0x0004,
+	ATTR_VOLUME   		= 0x0008,
+	ATTR_DIRECTORY		= 0x0010,
+	ATTR_ARCHIVE  		= 0x0020,
+	ATTR_DEVICE   		= 0x0040,
+	ATTR_NORMAL   		= 0x0080,
+	ATTR_TEMPORARY		= 0x0100,
+	ATTR_SPARSE   		= 0x0200,
+	ATTR_REPARSE  		= 0x0400,
+	ATTR_COMPRESSED		= 0x0800,
+	ATTR_OFFLINE   		= 0x100,	/* offline storage */
+	ATTR_NOT_CONTENT_INDEXED= 0x2000,
+	ATTR_ENCRYPTED 		= 0x4000,
+	ATTR_POSIX_SEMANTICS	= 0x01000000,
+	ATTR_BACKUP_SEMANTICS	= 0x02000000,
+	ATTR_DELETE_ON_CLOSE	= 0x04000000,
+	ATTR_SEQUENTIAL_SCAN	= 0x08000000,
+	ATTR_RANDOM_ACCESS  	= 0x10000000,
+	ATTR_NO_BUFFERING   	= 0x20000000,
+	ATTR_WRITE_THROUGH  	= 0x80000000,
+
+	/* ShareAccess flags */
+	FILE_NO_SHARE    	= 0,
+	FILE_SHARE_READ  	= 1,
+	FILE_SHARE_WRITE 	= 2,
+	FILE_SHARE_DELETE	= 4,
+	FILE_SHARE_ALL   	= 7,
+
+	/* CreateDisposition flags */
+	FILE_SUPERSEDE   	= 0,
+	FILE_OPEN		= 1,
+	FILE_CREATE		= 2,
+	FILE_OPEN_IF		= 3,
+	FILE_OVERWRITE		= 4,
+	FILE_OVERWRITE_IF	= 5,
+
+	/* CreateOptions */
+	FILE_DIRECTORY_FILE		= 0x00000001,
+	FILE_WRITE_THROUGH		= 0x00000002,
+	FILE_SEQUENTIAL_ONLY		= 0x00000004,
+	FILE_NO_INTERMEDIATE_BUFFERING	= 0x00000008,
+	FILE_SYNCHRONOUS_IO_ALERT	= 0x00000010,
+	FILE_SYNCHRONOUS_IO_NONALERT	= 0x00000020,
+	FILE_NON_DIRECTORY_FILE		= 0x00000040,
+	FILE_CREATE_TREE_CONNECTION	= 0x00000080,
+	FILE_COMPLETE_IF_OPLOCKED	= 0x00000100,
+	FILE_NO_EA_KNOWLEDGE		= 0x00000200,
+	FILE_OPEN_FOR_RECOVERY		= 0x00000400,
+	FILE_EIGHT_DOT_THREE_ONLY	= 0x00000400,	/* samba source says so... */
+	FILE_RANDOM_ACCESS		= 0x00000800,
+	FILE_DELETE_ON_CLOSE		= 0x00001000,
+	FILE_OPEN_BY_FILE_ID		= 0x00002000,
+	FILE_OPEN_FOR_BACKUP_INTENT	= 0x00004000,
+	FILE_NO_COMPRESSION		= 0x00008000,
+
+	/* open/create result codes */
+	FILE_WAS_OPENED			= 1,
+	FILE_WAS_CREATED		= 2,
+	FILE_WAS_OVERWRITTEN		= 3,
+
+	/* ImpersonationLevel flags */
+	SECURITY_ANONYMOUS     		= 0,
+	SECURITY_IDENTIFICATION		= 1,
+	SECURITY_IMPERSONATION		= 2,
+	SECURITY_DELEGATION		= 3,
+
+	/* SecurityFlags */
+	SECURITY_CONTEXT_TRACKING 	= 1,
+	SECURITY_EFFECTIVE_ONLY		= 2,
+
+	/* security descriptor bitmask */
+	QUERY_OWNER_SECURITY_INFORMATION = 1,
+	QUERY_GROUP_SECURITY_INFORMATION = 2,
+	QUERY_DACL_SECURITY_INFORMATION = 4,
+	QUERY_SACL_SECURITY_INFORMATION = 8,
+
+};
+
+enum {	/* PathInfo/FileInfo infolevels */
+	SMB_INFO_STANDARD              	= 0x1,
+	SMB_INFO_IS_NAME_VALID         	= 0x6,
+	SMB_QUERY_FILE_BASIC_INFO      	= 0x101,
+	SMB_QUERY_FILE_STANDARD_INFO   	= 0x102,
+	SMB_QUERY_FILE_NAME_INFO       	= 0x104,
+	SMB_QUERY_FILE_ALLOCATION_INFO 	= 0x105,
+	SMB_QUERY_FILE_END_OF_FILE_INFO = 0x106,
+	SMB_QUERY_FILE_ALL_INFO        	= 0x107,
+	SMB_QUERY_ALT_NAME_INFO        	= 0x108,
+	SMB_QUERY_FILE_STREAM_INFO     	= 0x109,
+	SMB_QUERY_FILE_COMPRESSION_INFO	= 0x10b,
+	SMB_QUERY_FILE_UNIX_BASIC      	= 0x200,
+	SMB_QUERY_FILE_UNIX_LINK       	= 0x201,
+
+	SMB_SET_FILE_BASIC_INFO	       	= 0x101,
+	SMB_SET_FILE_DISPOSITION_INFO  	= 0x102,
+	SMB_SET_FILE_ALLOCATION_INFO   	= 0x103,
+	SMB_SET_FILE_END_OF_FILE_INFO  	= 0x104,
+	SMB_SET_FILE_UNIX_BASIC        	= 0x200,
+	SMB_SET_FILE_UNIX_LINK         	= 0x201,
+	SMB_SET_FILE_UNIX_HLINK        	= 0x203,
+	SMB_SET_FILE_BASIC_INFO2       	= 0x3ec,
+	SMB_SET_FILE_RENAME_INFORMATION	= 0x3f2,
+	SMB_SET_FILE_ALLOCATION_INFO2  	= 0x3fb,
+	SMB_SET_FILE_END_OF_FILE_INFO2 	= 0x3fc,
+
+	/* Find File infolevels */
+	SMB_FIND_FILE_DIRECTORY_INFO	= 0x101,
+	SMB_FIND_FILE_FULL_DIRECTORY_INFO= 0x102,
+	SMB_FIND_FILE_NAMES_INFO	= 0x103,
+	SMB_FIND_FILE_BOTH_DIRECTORY_INFO= 0x104,
+	SMB_FIND_FILE_UNIX		= 0x202,
+
+	/* Trans2 FindFirst & FindNext */
+	CIFS_SEARCH_CLOSE_ALWAYS	= 0x0001,
+	CIFS_SEARCH_CLOSE_AT_END	= 0x0002,
+	CIFS_SEARCH_RETURN_RESUME	= 0x0004,
+	CIFS_SEARCH_CONTINUE_FROM_LAST	= 0x0008,
+	CIFS_SEARCH_BACKUP_SEARCH	= 0x0010,
+
+	/* Trans2 FsInfo */
+	SMB_INFO_ALLOCATION		= 0x1,
+	SMB_INFO_VOLUME			= 0x2,
+	SMB_QUERY_FS_VOLUME_INFO	= 0x102,
+	SMB_QUERY_FS_SIZE_INFO		= 0x103,
+	SMB_QUERY_FS_DEVICE_INFO	= 0x104,
+	SMB_QUERY_FS_ATTRIBUTE_INFO	= 0x105,
+	SMB_QUERY_CIFS_UNIX_INFO	= 0x200,
+};
+
+enum {	/* things to search for in server lookups */
+	LOCAL_AUTHORATIVE_ONLY	= 0x40000000,
+	LIST_DOMAINS_ONLY	= 0x80000000,
+	ALL_LEARNT_IN_DOMAIN	= 0xFFFFFFFF
+};
+
+typedef struct {
+	char	*user;		/* username */
+	char	*windom;	/* remote server's domain name */
+	char	*resp[2];	/* ASCII/Unicode or LM/NTLM keys */
+	int	len[2];		/* length of above */
+	uchar	*mackey[2];	/* Message Authentication key */
+} Auth;
+
+typedef struct {
+	int	fd;		/* File descriptor for I/O  */
+	int	nbt;		/* am using cifs over netbios */
+	int	trn;		/* TRN (unique RPC) ID  */
+	int	uid;		/* user (authentication) ID  */
+	int	seq;		/* sequence number */
+	int	seqrun;		/* sequence numbering active */
+	int	caps;		/* server capabilities */
+	int	support;	/* support bits */
+	int	flags;		/* SMB flags  */
+	int	flags2;		/* SMB flags 2  */
+	int	nocache;	/* disable write behind caching in server */
+	int	pid;		/* process ID  */
+	int	mid;		/* multiplex ID */
+	int	mtu;		/* max size of packet  */
+	int	tz;		/* Timezone, mins from UTC  */
+	int	isguest;	/* logged in as guest */
+	int	secmode;	/* security mode  */
+	int	macidx;		/* which MAC is in use, -1 is none */
+	uchar	chal[0xff +1];	/* server's challange for authentication  */
+	int	challen;	/* length of challange */
+	long	slip;		/* time difference between the server and us */
+	uvlong	lastfind;	/* nsec when last find peformed */
+	QLock	seqlock;	/* sequence number increment */
+	QLock	rpclock;	/* actual remote procedure call */
+	char	*cname;		/* remote hosts called name (for info) */
+	char	*remos;		/* remote hosts OS (for info) */
+	Auth	*auth;		/* authentication info */
+} Session;
+
+typedef struct {
+	Session *s;
+
+	int tid;		/* tree ID received from server */
+	int seq;		/* sequence number expected in reply */
+
+	uchar *seqbase; 	/* cifs: pos of sequence number in packet */
+	uchar *wordbase; 	/* cifs: base of words section of data  */
+	uchar *bytebase; 	/* cifs: base of bytes section of data  */
+	uchar *tbase;		/* transactions: start of trans packet */
+	uchar *tsetup;		/* transactions: start of setup section */
+	uchar *tparam; 		/* transactions: start of params section */
+	uchar *tdata; 		/* transactions: start of data section */
+
+	uchar *eop;		/* received end of packet */
+	uchar *pos;		/* current pos in packet  */
+	uchar *buf;		/* packet buffer, must be last entry in struct  */
+} Pkt;
+
+typedef struct {
+	char	*name;
+	int	tid;		/* not part of the protocol, housekeeping */
+	int	options;	/* not part of the protocol, housekeeping */
+} Share;
+
+typedef struct {
+	long	created;	/* last access time */
+	long	accessed;	/* last access time */
+	long	written;	/* last written time */
+	long	changed;	/* change time */
+	uvlong	size;		/* file size */
+	long	attribs;	/* attributes */
+	char	name[CIFS_FNAME_MAX +1]; /* name */
+} FInfo;
+
+typedef struct {
+	char	*wrkstn;
+	char	*user;
+	long	sesstime;
+	long	idletime;
+} Sessinfo;
+
+typedef struct {
+	char	*name;
+} Namelist;
+
+typedef struct {
+	char	*user;
+	char	*comment;
+	char	*user_comment;
+	char	*fullname;
+} Userinfo;
+
+typedef struct {
+	char	*name;
+	int	type;
+	char	*comment;
+	int	perms;
+	int	maxusrs;
+	int	activeusrs;
+	char	*path;
+	char	*passwd;
+} Shareinfo2;
+
+typedef struct {
+	char	*name;
+	int	major;
+	int	minor;
+	int	type;
+	char	*comment;
+} Serverinfo;
+
+typedef struct {
+	int	type;	/* o=unknown, 1=CIFS, 2=netware 3=domain */
+	int	flags;	/* 1 == strip off consumed chars before resubmitting */
+	int	ttl;	/* time to live of this info in secs */
+	int	prox;	/* lower value is preferred */
+	char	*path;	/* new path */
+	char	*addr;	/* new server */
+} Refer;
+
+typedef struct {
+	char	*node;
+	char	*user;
+	char	*langroup;
+	int	major;
+	int	minor;
+	char	*pridom;
+	char	*otherdoms;
+} Wrkstainfo;
+
+typedef struct {
+	int	ident;
+	int	perms;
+	int	locks;
+	char	*path;
+	char	*user;
+} Fileinfo;
+
+extern int Dfstout;
+extern char *Debug;
+extern Share Ipc;
+extern Session *Sess;
+extern int Active;
+
+Share Shares[MAX_SHARES];
+int Nshares;
+
+/* main.c */
+Qid	mkqid(char *, int, long, int, long);
+
+/* auth.c */
+Auth	*getauth(char *, char *, char *, int, uchar *, int);
+void	autherr(void);
+int	macsign(Pkt *, int);
+
+/* cifs.c */
+Session	*cifsdial(char *, char *, char *);
+void	cifsclose(Session *);
+Pkt	*cifshdr(Session *, Share *, int);
+void	pbytes(Pkt *);
+int	cifsrpc(Pkt *);
+int	CIFSnegotiate(Session *, long *, char *, int, char *, int);
+int	CIFSsession(Session *);
+int	CIFStreeconnect(Session *, char *, char *, Share *);
+int	CIFSlogoff(Session *);
+int	CIFStreedisconnect(Session *, Share *);
+int	CIFSdeletefile(Session *, Share *, char *);
+int	CIFSdeletedirectory(Session *, Share *, char *);
+int	CIFScreatedirectory(Session *, Share *, char *);
+int	CIFSrename(Session *, Share *, char *, char *);
+int	CIFS_NT_opencreate(Session *, Share *, char *, int, int, int, int, int, int, int *, FInfo *);
+int	CIFS_SMB_opencreate(Session *, Share *, char *, int, int, int, int *);
+vlong	CIFSwrite(Session *, Share *, int, uvlong, void *, vlong);
+vlong	CIFSread(Session *, Share *, int, uvlong, void *, vlong, vlong);
+int	CIFSflush(Session *, Share *, int);
+int	CIFSclose(Session *, Share *, int);
+int	CIFSfindclose2(Session *, Share *, int);
+int	CIFSecho(Session *);
+int	CIFSsetinfo(Session *, Share *, char *, FInfo *);
+void	goff(Pkt *, uchar *, char *, int);
+
+/* dfs.c */
+char	*mapfile(char *);
+int	mapshare(char *, Share **);
+int	redirect(Session *, Share *s, char *);
+int	dfscacheinfo(Fmt *);
+
+/* doserrstr.c */
+char	*doserrstr(uint);
+
+/* fs.c */
+int	shareinfo(Fmt *);
+int	conninfo(Fmt *);
+int	sessioninfo(Fmt *);
+int	userinfo(Fmt *);
+int	groupinfo(Fmt *);
+int	domaininfo(Fmt *);
+int	workstationinfo(Fmt *);
+int	dfsrootinfo(Fmt *);
+int	openfileinfo(Fmt *);
+int	dfsrootinfo(Fmt *);
+int	filetableinfo(Fmt *); 	/* is in main.c due to C scope */
+
+/* info.c */
+int	walkinfo(char *);
+int	numinfo(void);
+int	dirgeninfo(int, Dir *);
+int	makeinfo(int);
+int	readinfo(int, char *, int, int);
+void	freeinfo(int);
+
+/* main.c */
+void	usage(void);
+void	dmpkey(char *, void *, int);
+void	main(int, char **);
+
+/* misc.c */
+char	*strupr(char *);
+char	*strlwr(char *);
+
+/* netbios.c */
+void	Gmem(uchar **, void *, int);
+int	calledname(char *, char *);
+int	nbtdial(char *, char *, char *);
+void	nbthdr(Pkt *);
+int	nbtrpc(Pkt *);
+void	xd(char *, void *, int);
+
+/* nterrstr.c */
+char	*nterrstr(uint);
+
+/* pack.c */
+void	*pmem(Pkt *, void *, int);
+void	*ppath(Pkt *, char *);
+void	*pstr(Pkt *, char *);
+void	*pascii(Pkt *, char *);
+void	*pl64(Pkt *, uvlong);
+void	*pb32(Pkt *, uint);
+void	*pl32(Pkt *, uint);
+void	*pb16(Pkt *, uint);
+void	*pl16(Pkt *, uint);
+void	*p8(Pkt *, uint);
+void	*pname(Pkt *, char *, char);
+void	*pvtime(Pkt *, uvlong);
+void	*pdatetime(Pkt *, long);
+void	gmem(Pkt *, void *, int);
+void	gstr(Pkt *, char *, int);
+void	gascii(Pkt *, char *, int);
+uvlong	gl64(Pkt *);
+uvlong	gb48(Pkt *);
+uint	gb32(Pkt *);
+uint	gl32(Pkt *);
+uint	gb16(Pkt *);
+uint	gl16(Pkt *);
+uint	g8(Pkt *);
+long	gdatetime(Pkt *);
+long	gvtime(Pkt *);
+void	gconv(Pkt *, int, char *, int);
+
+/* raperrstr.c */
+char	*raperrstr(uint);
+
+/* sid2name.c */
+void	upd_names(Session *, Share *, char *, Dir *);
+
+/* trans.c */
+int	RAPshareenum(Session *, Share *, Share **);
+int	RAPshareinfo(Session *, Share *, char *, Shareinfo2 *);
+
+int	RAPsessionenum(Session *, Share *, Sessinfo **);
+
+int	RAPgroupenum(Session *, Share *, Namelist **);
+int	RAPgroupusers(Session *, Share *, char *, Namelist **);
+
+int	RAPuserenum(Session *, Share *, Namelist **);
+int	RAPuserenum2(Session *, Share *, Namelist **);
+int	RAPuserinfo(Session *, Share *, char *, Userinfo *);
+
+int	RAPServerenum2(Session *, Share *, char *, int, int *, Serverinfo **);
+int	RAPServerenum3(Session *, Share *, char *, int, int, Serverinfo *);
+
+int	RAPFileenum2(Session *, Share *, char *, char *, Fileinfo **);
+
+/* trans2.c */
+int	T2findfirst(Session *, Share *, int, char *, int *, long *, FInfo *);
+int	T2findnext(Session *, Share *, int, char *, int *, long *, FInfo *, int);
+int	T2queryall(Session *, Share *, char *, FInfo *);
+int	T2querystandard(Session *, Share *, char *, FInfo *);
+int	T2setpathinfo(Session *, Share *, char *, FInfo *);
+int	T2setfilelength(Session *, Share *, int, FInfo *);