From e5888a1ffdae813d7575f5fb02275c6bb07e5199 Mon Sep 17 00:00:00 2001 From: Taru Karttunen Date: Wed, 30 Mar 2011 15:46:40 +0300 Subject: Import sources from 2011-03-30 iso image --- sys/src/cmd/cifs/apinums.h | 222 +++++++ sys/src/cmd/cifs/auth-testcase.c | 477 ++++++++++++++ sys/src/cmd/cifs/auth.c | 416 ++++++++++++ sys/src/cmd/cifs/cifs.c | 806 ++++++++++++++++++++++++ sys/src/cmd/cifs/cifs.h | 638 +++++++++++++++++++ sys/src/cmd/cifs/dfs.c | 407 ++++++++++++ sys/src/cmd/cifs/doserrstr.c | 187 ++++++ sys/src/cmd/cifs/fs.c | 366 +++++++++++ sys/src/cmd/cifs/info.c | 106 ++++ sys/src/cmd/cifs/main.c | 1283 ++++++++++++++++++++++++++++++++++++++ sys/src/cmd/cifs/misc.c | 25 + sys/src/cmd/cifs/mkfile | 20 + sys/src/cmd/cifs/netbios.c | 475 ++++++++++++++ sys/src/cmd/cifs/nterrstr.c | 1019 ++++++++++++++++++++++++++++++ sys/src/cmd/cifs/pack.c | 463 ++++++++++++++ sys/src/cmd/cifs/ping.c | 132 ++++ sys/src/cmd/cifs/raperrstr.c | 346 ++++++++++ sys/src/cmd/cifs/remsmb.h | 466 ++++++++++++++ sys/src/cmd/cifs/sid2name.c | 120 ++++ sys/src/cmd/cifs/trans.c | 785 +++++++++++++++++++++++ sys/src/cmd/cifs/trans2.c | 539 ++++++++++++++++ sys/src/cmd/cifs/transnt.c | 167 +++++ 22 files changed, 9465 insertions(+) create mode 100755 sys/src/cmd/cifs/apinums.h create mode 100755 sys/src/cmd/cifs/auth-testcase.c create mode 100755 sys/src/cmd/cifs/auth.c create mode 100755 sys/src/cmd/cifs/cifs.c create mode 100755 sys/src/cmd/cifs/cifs.h create mode 100755 sys/src/cmd/cifs/dfs.c create mode 100755 sys/src/cmd/cifs/doserrstr.c create mode 100755 sys/src/cmd/cifs/fs.c create mode 100755 sys/src/cmd/cifs/info.c create mode 100755 sys/src/cmd/cifs/main.c create mode 100755 sys/src/cmd/cifs/misc.c create mode 100755 sys/src/cmd/cifs/mkfile create mode 100755 sys/src/cmd/cifs/netbios.c create mode 100755 sys/src/cmd/cifs/nterrstr.c create mode 100755 sys/src/cmd/cifs/pack.c create mode 100755 sys/src/cmd/cifs/ping.c create mode 100755 sys/src/cmd/cifs/raperrstr.c create mode 100755 sys/src/cmd/cifs/remsmb.h create mode 100755 sys/src/cmd/cifs/sid2name.c create mode 100755 sys/src/cmd/cifs/trans.c create mode 100755 sys/src/cmd/cifs/trans2.c create mode 100755 sys/src/cmd/cifs/transnt.c (limited to 'sys/src/cmd/cifs') diff --git a/sys/src/cmd/cifs/apinums.h b/sys/src/cmd/cifs/apinums.h new file mode 100755 index 000000000..6963d19c7 --- /dev/null +++ b/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 diff --git a/sys/src/cmd/cifs/auth-testcase.c b/sys/src/cmd/cifs/auth-testcase.c new file mode 100755 index 000000000..0bc74a773 --- /dev/null +++ b/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 +#include +#include +#include +#include +#include +#include +#include +#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; +} diff --git a/sys/src/cmd/cifs/auth.c b/sys/src/cmd/cifs/auth.c new file mode 100755 index 000000000..f72c03d2d --- /dev/null +++ b/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 +#include +#include +#include +#include +#include +#include +#include +#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; +} diff --git a/sys/src/cmd/cifs/cifs.c b/sys/src/cmd/cifs/cifs.c new file mode 100755 index 000000000..68b6a636c --- /dev/null +++ b/sys/src/cmd/cifs/cifs.c @@ -0,0 +1,806 @@ +#include +#include +#include +#include +#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; + werrstr(""); + 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){ + print("%ux != %ux bad sequence number\n", 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"; + static 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; +} diff --git a/sys/src/cmd/cifs/cifs.h b/sys/src/cmd/cifs/cifs.h new file mode 100755 index 000000000..a600ed7d4 --- /dev/null +++ b/sys/src/cmd/cifs/cifs.h @@ -0,0 +1,638 @@ +/* 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 Active; +extern int Checkcase; +extern int Dfstout; +extern char *Debug; +extern char *Host; +extern Session *Sess; +extern Share Ipc; + +extern Share Shares[MAX_SHARES]; +extern 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 *); +char *trimshare(char *); + +/* 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 *); +int T2fsvolumeinfo(Session *, Share *, long *, long *, char *, int); +int T2fssizeinfo(Session *, Share *, uvlong *, uvlong *); +int T2getdfsreferral(Session *, Share *, char *, int *, int *, Refer *, int); + +/* transnt.c */ +int TNTquerysecurity(Session *, Share *, int, char **, char **); + +/* ping.c */ +int ping(char *, int); diff --git a/sys/src/cmd/cifs/dfs.c b/sys/src/cmd/cifs/dfs.c new file mode 100755 index 000000000..73c9a3e01 --- /dev/null +++ b/sys/src/cmd/cifs/dfs.c @@ -0,0 +1,407 @@ +/* + * DNS referrals give two main fields: the path to connect to in + * /Netbios-host-name/share-name/path... form and a network + * address of how to find this path of the form domain.dom. + * + * The domain.dom is resolved in XP/Win2k etc using AD to do + * a lookup (this is a consensus view, I don't think anyone + * has proved it). I cannot do this as AD needs Kerberos and + * LDAP which I don't have. + * + * Instead I just use the NetBios names passed in the paths + * and assume that the servers are in the same DNS domain as me + * and have their DNS hostname set the same as their netbios + * called-name; thankfully this always seems to be the case (so far). + * + * I have not added support for starting another instance of + * cifs to connect to other servers referenced in DFS links, + * this is not a problem for me and I think it hides a load + * of problems of its own wrt plan9's private namespaces. + * + * The proximity of my test server (AD enabled) is always 0 but some + * systems may report more meaningful values. The expiry time is + * similarly zero, so I guess at 5 mins. + * + * If the redirection points to a "hidden" share (i.e., its name + * ends in a $) then the type of the redirection is 0 (unknown) even + * though it is a CIFS share. + * + * It would be nice to add a check for which subnet a server is on + * so our first choice is always the the server on the same subnet + * as us which replies to a ping (i.e., is up). This could short- + * circuit the tests as the a server on the same subnet will always + * be the fastest to get to. + * + * If I set Flags2_DFS then I don't see DFS links, I just get + * path not found (?!). + * + * If I do a QueryFileInfo of a DFS link point (IE when I'am doing a walk) + * Then I just see a directory, its not until I try to walk another level + * That I get "IO reparse tag not handled" error rather than + * "Path not covered". + * + * If I check the extended attributes of the QueryFileInfo in walk() then I can + * see this is a reparse point and so I can get the referral. The only + * problem here is that samba and the like may not support this. + */ +#include +#include +#include +#include +#include +#include +#include <9p.h> +#include "cifs.h" + +enum { + Nomatch, /* not found in cache */ + Exactmatch, /* perfect match found */ + Badmatch /* matched but wrong case */ +}; + +#define SINT_MAX 0x7fffffff + +typedef struct Dfscache Dfscache; +struct Dfscache { + Dfscache*next; /* next entry */ + char *src; + char *host; + char *share; + char *path; + long expiry; /* expiry time in sec */ + long rtt; /* round trip time, nsec */ + int prox; /* proximity, lower = closer */ +}; + +Dfscache *Cache; + +int +dfscacheinfo(Fmt *f) +{ + long ex; + Dfscache *cp; + + for(cp = Cache; cp; cp = cp->next){ + ex = cp->expiry - time(nil); + if(ex < 0) + ex = -1; + fmtprint(f, "%-42s %6ld %8.1f %4d %-16s %-24s %s\n", + cp->src, ex, (double)cp->rtt/1000.0L, cp->prox, + cp->host, cp->share, cp->path); + } + return 0; +} + +char * +trimshare(char *s) +{ + char *p; + static char name[128]; + + strncpy(name, s, sizeof(name)); + name[sizeof(name)-1] = 0; + if((p = strrchr(name, '$')) != nil && p[1] == 0) + *p = 0; + return name; +} + +static Dfscache * +lookup(char *path, int *match) +{ + int len, n, m; + Dfscache *cp, *best; + + if(match) + *match = Nomatch; + + len = 0; + best = nil; + m = strlen(path); + for(cp = Cache; cp; cp = cp->next){ + n = strlen(cp->src); + if(n < len) + continue; + if(strncmp(path, cp->src, n) != 0) + continue; + if(path[n] != 0 && path[n] != '/') + continue; + best = cp; + len = n; + if(n == m){ + if(match) + *match = Exactmatch; + break; + } + } + return best; +} + +char * +mapfile(char *opath) +{ + int exact; + Dfscache *cp; + char *p, *path; + static char npath[MAX_DFS_PATH]; + + path = opath; + if((cp = lookup(path, &exact)) != nil){ + snprint(npath, sizeof npath, "/%s%s%s%s", cp->share, + *cp->path? "/": "", cp->path, path + strlen(cp->src)); + path = npath; + } + + if((p = strchr(path+1, '/')) == nil) + p = "/"; + if(Debug && strstr(Debug, "dfs") != nil) + print("mapfile src=%q => dst=%q\n", opath, p); + return p; +} + +int +mapshare(char *path, Share **osp) +{ + int i; + Share *sp; + Dfscache *cp; + char *s, *try; + char *tail[] = { "", "$" }; + + if((cp = lookup(path, nil)) == nil) + return 0; + + for(sp = Shares; sp < Shares+Nshares; sp++){ + s = trimshare(sp->name); + if(cistrcmp(cp->share, s) != 0) + continue; + if(Checkcase && strcmp(cp->share, s) != 0) + continue; + if(Debug && strstr(Debug, "dfs") != nil) + print("mapshare, already connected, src=%q => dst=%q\n", path, sp->name); + *osp = sp; + return 0; + } + /* + * Try to autoconnect to share if it is not known. Note even if you + * didn't specify any shares and let the system autoconnect you may + * not already have the share you need as RAP (which we use) throws + * away names > 12 chars long. If we where to use RPC then this block + * of code would be less important, though it would still be useful + * to catch Shares added since cifs(1) was started. + */ + sp = Shares + Nshares; + for(i = 0; i < 2; i++){ + try = smprint("%s%s", cp->share, tail[i]); + if(CIFStreeconnect(Sess, Sess->cname, try, sp) == 0){ + sp->name = try; + *osp = sp; + Nshares++; + if(Debug && strstr(Debug, "dfs") != nil) + print("mapshare connected, src=%q dst=%q\n", + path, cp->share); + return 0; + } + free(try); + } + + if(Debug && strstr(Debug, "dfs") != nil) + print("mapshare failed src=%s\n", path); + werrstr("not found"); + return -1; +} + +/* + * Rtt_tol is the fractional tollerance for RTT comparisons. + * If a later (further down the list) host's RTT is less than + * 1/Rtt_tol better than my current best then I don't bother + * with it. This biases me towards entries at the top of the list + * which Active Directory has already chosen for me and prevents + * noise in RTTs from pushing me to more distant machines. + */ +static int +remap(Dfscache *cp, Refer *re) +{ + int n; + long rtt; + char *p, *a[4]; + enum { + Hostname = 1, + Sharename = 2, + Pathname = 3, + + Rtt_tol = 10 + }; + + if(Debug && strstr(Debug, "dfs") != nil) + print(" remap %s\n", re->addr); + + for(p = re->addr; *p; p++) + if(*p == '\\') + *p = '/'; + + if(cp->prox < re->prox){ + if(Debug && strstr(Debug, "dfs") != nil) + print(" remap %d < %d\n", cp->prox, re->prox); + return -1; + } + if((n = getfields(re->addr, a, sizeof(a), 0, "/")) < 3){ + if(Debug && strstr(Debug, "dfs") != nil) + print(" remap nfields=%d\n", n); + return -1; + } + if((rtt = ping(a[Hostname], Dfstout)) == -1){ + if(Debug && strstr(Debug, "dfs") != nil) + print(" remap ping failed\n"); + return -1; + } + if(cp->rtt < rtt && (rtt/labs(rtt-cp->rtt)) < Rtt_tol){ + if(Debug && strstr(Debug, "dfs") != nil) + print(" remap bad ping %ld < %ld && %ld < %d\n", + cp->rtt, rtt, (rtt/labs(rtt-cp->rtt)), Rtt_tol); + return -1; + } + + if(n < 4) + a[Pathname] = ""; + if(re->ttl == 0) + re->ttl = 60*5; + + free(cp->host); + free(cp->share); + free(cp->path); + cp->rtt = rtt; + cp->prox = re->prox; + cp->expiry = time(nil)+re->ttl; + cp->host = estrdup9p(a[Hostname]); + cp->share = estrdup9p(trimshare(a[Sharename])); + cp->path = estrdup9p(a[Pathname]); + if(Debug && strstr(Debug, "dfs") != nil) + print(" remap ping OK prox=%d host=%s share=%s path=%s\n", + cp->prox, cp->host, cp->share, cp->path); + return 0; +} + +static int +redir1(Session *s, char *path, Dfscache *cp, int level) +{ + Refer retab[16], *re; + int n, gflags, used, found; + + if(level > 8) + return -1; + + if((n = T2getdfsreferral(s, &Ipc, path, &gflags, &used, retab, + nelem(retab))) == -1) + return -1; + + if(! (gflags & DFS_HEADER_ROOT)) + used = SINT_MAX; + + found = 0; + for(re = retab; re < retab+n; re++){ + if(Debug && strstr(Debug, "dfs") != nil) + print("referal level=%d prox=%d path=%q addr=%q\n", + level, re->prox, re->path, re->addr); + + if(gflags & DFS_HEADER_STORAGE){ + if(remap(cp, re) == 0) + found = 1; + } else{ + if(redir1(s, re->addr, cp, level+1) != -1) /* ???? */ + found = 1; + } + free(re->addr); + free(re->path); + } + + if(Debug && strstr(Debug, "dfs") != nil) + print("referal level=%d path=%q found=%d used=%d\n", + level, path, found, used); + if(!found) + return -1; + return used; +} + +/* + * We can afford to ignore the used count returned by redir + * because of the semantics of 9p - we always walk to directories + * ome and we a time and we always walk before any other file operations + */ +int +redirect(Session *s, Share *sp, char *path) +{ + int match; + char *unc; + Dfscache *cp; + + if(Debug && strstr(Debug, "dfs") != nil) + print("redirect name=%q path=%q\n", sp->name, path); + + cp = lookup(path, &match); + if(match == Badmatch) + return -1; + + if(cp && match == Exactmatch){ + if(cp->expiry >= time(nil)){ /* cache hit */ + if(Debug && strstr(Debug, "dfs") != nil) + print("redirect cache=hit src=%q => share=%q path=%q\n", + cp->src, cp->share, cp->path); + return 0; + + } else{ /* cache hit, but entry stale */ + cp->rtt = SINT_MAX; + cp->prox = SINT_MAX; + + unc = smprint("//%s/%s/%s%s%s", s->auth->windom, + cp->share, cp->path, *cp->path? "/": "", + path + strlen(cp->src) + 1); + if(unc == nil) + sysfatal("no memory: %r"); + if(redir1(s, unc, cp, 1) == -1){ + if(Debug && strstr(Debug, "dfs") != nil) + print("redirect refresh failed unc=%q\n", + unc); + free(unc); + return -1; + } + free(unc); + if(Debug && strstr(Debug, "dfs") != nil) + print("redirect refresh cache=stale src=%q => share=%q path=%q\n", + cp->src, cp->share, cp->path); + return 0; + } + } + + + /* in-exact match or complete miss */ + if(cp) + unc = smprint("//%s/%s/%s%s%s", s->auth->windom, cp->share, + cp->path, *cp->path? "/": "", path + strlen(cp->src) + 1); + else + unc = smprint("//%s%s", s->auth->windom, path); + if(unc == nil) + sysfatal("no memory: %r"); + + cp = emalloc9p(sizeof(Dfscache)); + memset(cp, 0, sizeof(Dfscache)); + cp->rtt = SINT_MAX; + cp->prox = SINT_MAX; + + if(redir1(s, unc, cp, 1) == -1){ + if(Debug && strstr(Debug, "dfs") != nil) + print("redirect new failed unc=%q\n", unc); + free(unc); + free(cp); + return -1; + } + free(unc); + + cp->src = estrdup9p(path); + cp->next = Cache; + Cache = cp; + if(Debug && strstr(Debug, "dfs") != nil) + print("redirect cache=miss src=%q => share=%q path=%q\n", + cp->src, cp->share, cp->path); + return 0; +} + diff --git a/sys/src/cmd/cifs/doserrstr.c b/sys/src/cmd/cifs/doserrstr.c new file mode 100755 index 000000000..c0b8c98af --- /dev/null +++ b/sys/src/cmd/cifs/doserrstr.c @@ -0,0 +1,187 @@ +#include +#include + +/* + * This file is derrived from nterr.h in the samba distribution + */ + +/* + Unix SMB/CIFS implementation. + DOS error code constants + Copyright (C) Andrew Tridgell 1992-2000 + Copyright (C) John H Terpstra 1996-2000 + Copyright (C) Luke Kenneth Casson Leighton 1996-2000 + Copyright (C) Paul Ashton 1998-2000 + + 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. + */ + + +static struct { + int err; + char *msg; +} DOSerrs[] = { + /* smb x/open error codes for the errdos error class */ + { (0<<16)|1, "no error" }, + { (1<<16)|1, "invalid function" }, + { (2<<16)|1, "file not found" }, + { (3<<16)|1, "directory not found" }, + { (4<<16)|1, "too many open files" }, + { (5<<16)|1, "access denied" }, + { (6<<16)|1, "invalid fid" }, + { (7<<16)|1, "memory control blocks destroyed." }, + { (8<<16)|1, "out of memory" }, + { (9<<16)|1, "invalid memory block address" }, + { (10<<16)|1, "invalid environment" }, + { (12<<16)|1, "invalid open mode" }, + { (13<<16)|1, "invalid data (only from ioctl call)" }, + { (14<<16)|1, "reserved" }, + { (15<<16)|1, "invalid drive" }, + { (16<<16)|1, "attempt to delete current directory" }, + { (17<<16)|1, "rename/move across filesystems" }, + { (18<<16)|1, "no more files found" }, + { (31<<16)|1, "general failure" }, + { (32<<16)|1, "share mode conflict with open mode" }, + { (33<<16)|1, "lock conflicts" }, + { (50<<16)|1, "request unsupported" }, + { (64<<16)|1, "network name not available" }, + { (66<<16)|1, "ipc unsupported (guess)" }, + { (67<<16)|1, "invalid share name" }, + { (80<<16)|1, "file already exists" }, + { (87<<16)|1, "invalid paramater" }, + { (110<<16)|1, "cannot open" }, + { (122<<16)|1, "insufficent buffer" }, + { (123<<16)|1, "invalid name" }, + { (124<<16)|1, "unknown level" }, + { (158<<16)|1, "this region already locked" }, + + { (183<<16)|1, "rename failed" }, + + { (230<<16)|1, "named pipe invalid" }, + { (231<<16)|1, "pipe busy" }, + { (232<<16)|1, "close in progress" }, + { (233<<16)|1, "no reader of named pipe" }, + { (234<<16)|1, "more data to be returned" }, + { (259<<16)|1, "no more items" }, + { (267<<16)|1, "invalid directory name in a path" }, + { (282<<16)|1, "extended attributes" }, + { (1326<<16)|1, "authentication failed" }, + { (2123<<16)|1, "buffer too small" }, + { (2142<<16)|1, "unknown ipc" }, + { (2151<<16)|1, "no such print job" }, + { (2455<<16)|1, "invalid group" }, + + /* Error codes for the ERRSRV class */ + { (1<<16)|2, "non specific error" }, + { (2<<16)|2, "bad password" }, + { (3<<16)|2, "reserved" }, + { (4<<16)|2, "permission denied" }, + { (5<<16)|2, "tid invalid" }, + { (6<<16)|2, "invalid server name" }, + { (7<<16)|2, "invalid device" }, + { (22<<16)|2, "unknown smb" }, + { (49<<16)|2, "print queue full" }, + { (50<<16)|2, "queued item too big" }, + { (52<<16)|2, "fid invalid in print file" }, + { (64<<16)|2, "unrecognised command" }, + { (65<<16)|2, "smb server internal error" }, + { (67<<16)|2, "fid/pathname invalid" }, + { (68<<16)|2, "reserved 68" }, + { (69<<16)|2, "access is invalid" }, + { (70<<16)|2, "reserved 70" }, + { (71<<16)|2, "attribute mode invalid" }, + { (81<<16)|2, "message server paused" }, + { (82<<16)|2, "not receiving messages" }, + { (83<<16)|2, "no room for message" }, + { (87<<16)|2, "too many remote usernames" }, + { (88<<16)|2, "operation timed out" }, + { (89<<16)|2, "no resources" }, + { (90<<16)|2, "too many userids" }, + { (91<<16)|2, "bad userid" }, + { (250<<16)|2, "retry with mpx mode" }, + { (251<<16)|2, "retry with standard mode" }, + { (252<<16)|2, "resume mpx mode" }, + { (0xffff<<16)|2, "function not supported" }, + + /* Error codes for the ERRHRD class */ + { (19<<16)|3, "read only media" }, + { (20<<16)|3, "unknown device" }, + { (21<<16)|3, "drive not ready" }, + { (22<<16)|3, "unknown command" }, + { (23<<16)|3, "data (CRC) error" }, + { (24<<16)|3, "bad request length" }, + { (25<<16)|3, "seek failed" }, + { (26<<16)|3, "bad media" }, + { (27<<16)|3, "bad sector" }, + { (28<<16)|3, "no paper" }, + { (29<<16)|3, "write fault" }, + { (30<<16)|3, "read fault" }, + { (31<<16)|3, "general hardware failure" }, + { (34<<16)|3, "wrong disk" }, + { (35<<16)|3, "FCB unavailable" }, + { (36<<16)|3, "share buffer exceeded" }, + { (39<<16)|3, "disk full" }, + +}; + +char * +doserrstr(uint err) +{ + int i, match; + char *class; + static char buf[0xff]; + + switch(err & 0xff){ + case 1: + class = "dos"; + break; + case 2: + class = "network"; + break; + case 3: + class = "hardware"; + break; + case 4: + class = "Xos"; + break; + case 0xe1: + class = "mx1"; + break; + case 0xe2: + class = "mx2"; + break; + case 0xe3: + class = "mx3"; + break; + case 0xff: + class = "packet"; + break; + default: + class = "unknown"; + break; + } + + match = -1; + for(i = 0; i < nelem(DOSerrs); i++) + if(DOSerrs[i].err == err) + match = i; + + if(match != -1) + snprint(buf, sizeof(buf), "%s, %s", class, DOSerrs[match].msg); + else + snprint(buf, sizeof(buf), "%s, %ud/0x%ux - unknown error", + class, err >> 16, err >> 16); + return buf; +} diff --git a/sys/src/cmd/cifs/fs.c b/sys/src/cmd/cifs/fs.c new file mode 100755 index 000000000..153937143 --- /dev/null +++ b/sys/src/cmd/cifs/fs.c @@ -0,0 +1,366 @@ +#include +#include +#include +#include +#include +#include <9p.h> +#include "cifs.h" + +static char *period(long sec); + +int +shareinfo(Fmt *f) +{ + int i, j, n; + char *type; + Shareinfo2 si2; + Share *sp, *sip; + + if((n = RAPshareenum(Sess, &Ipc, &sip)) < 1){ + fmtprint(f, "can't enumerate shares: %r\n"); + return 0; + } + + for(i = 0; i < n; i++){ + fmtprint(f, "%-13q ", sip[i].name); + + sp = &sip[i]; + for(j = 0; j < Nshares; j++) + if(strcmp(Shares[j].name, sip[i].name) == 0){ + sp = &Shares[j]; + break; + } + if(j >= Nshares) + sp->tid = Ipc.tid; + + if(RAPshareinfo(Sess, sp, sp->name, &si2) != -1){ + switch(si2.type){ + case STYPE_DISKTREE: type = "disk"; break; + case STYPE_PRINTQ: type = "printq"; break; + case STYPE_DEVICE: type = "device"; break; + case STYPE_IPC: type = "ipc"; break; + case STYPE_SPECIAL: type = "special"; break; + case STYPE_TEMP: type = "temp"; break; + default: type = "unknown"; break; + } + + fmtprint(f, "%-8s %5d/%-5d %s", type, + si2.activeusrs, si2.maxusrs, si2.comment); + free(si2.name); + free(si2.comment); + free(si2.path); + free(si2.passwd); + } + fmtprint(f, "\n"); + + } + free(sip); + return 0; +} + +int +openfileinfo(Fmt *f) +{ + int got, i; + Fileinfo *fi; + + fi = nil; + if((got = RAPFileenum2(Sess, &Ipc, "", "", &fi)) == -1){ + fmtprint(f, "RAPfileenum: %r\n"); + return 0; + } + + for(i = 0; i < got; i++){ + fmtprint(f, "%c%c%c %-4d %-24q %q ", + (fi[i].perms & 1)? 'r': '-', + (fi[i].perms & 2)? 'w': '-', + (fi[i].perms & 4)? 'c': '-', + fi[i].locks, fi[i].user, fi[i].path); + free(fi[i].path); + free(fi[i].user); + } + free(fi); + return 0; +} + +int +conninfo(Fmt *f) +{ + int i; + typedef struct { + int val; + char *name; + } Tab; + static Tab captab[] = { + { 1, "raw-mode" }, + { 2, "mpx-mode" }, + { 4, "unicode" }, + { 8, "large-files" }, + { 0x10, "NT-smbs" }, + { 0x20, "rpc-remote-APIs" }, + { 0x40, "status32" }, + { 0x80, "l2-oplocks" }, + { 0x100, "lock-read" }, + { 0x200, "NT-find" }, + { 0x1000, "Dfs" }, + { 0x2000, "info-passthru" }, + { 0x4000, "large-readx" }, + { 0x8000, "large-writex" }, + { 0x800000, "Unix" }, + { 0x20000000, "bulk-transfer" }, + { 0x40000000, "compressed" }, + { 0x80000000, "extended-security" }, + }; + static Tab sectab[] = { + { 1, "user-auth" }, + { 2, "challange-response" }, + { 4, "signing-available" }, + { 8, "signing-required" }, + }; + + fmtprint(f, "%q %q %q %q %+ldsec %dmtu %s\n", + Sess->auth->user, Sess->cname, + Sess->auth->windom, Sess->remos, + Sess->slip, Sess->mtu, Sess->isguest? "as guest": ""); + + fmtprint(f, "caps: "); + for(i = 0; i < nelem(captab); i++) + if(Sess->caps & captab[i].val) + fmtprint(f, "%s ", captab[i].name); + fmtprint(f, "\n"); + + fmtprint(f, "security: "); + for(i = 0; i < nelem(sectab); i++) + if(Sess->secmode & sectab[i].val) + fmtprint(f, "%s ", sectab[i].name); + fmtprint(f, "\n"); + + if(Sess->nbt) + fmtprint(f, "transport: cifs over netbios\n"); + else + fmtprint(f, "transport: cifs\n"); + return 0; +} + +int +sessioninfo(Fmt *f) +{ + int got, i; + Sessinfo *si; + + si = nil; + if((got = RAPsessionenum(Sess, &Ipc, &si)) == -1){ + fmtprint(f, "RAPsessionenum: %r\n"); + return 0; + } + + for(i = 0; i < got; i++){ + fmtprint(f, "%-24q %-24q ", si[i].user, si[i].wrkstn); + fmtprint(f, "%12s ", period(si[i].sesstime)); + fmtprint(f, "%12s\n", period(si[i].idletime)); + free(si[i].wrkstn); + free(si[i].user); + } + free(si); + return 0; +} + +/* + * We request the domain referral for "" which gives the + * list of all the trusted domains in the clients forest, and + * other trusted forests. + * + * We then sumbit each of these names in turn which gives the + * names of the domain controllers for that domain. + * + * We get a DNS domain name for each domain controller as well as a + * netbios name. I THINK I am correct in saying that a name + * containing a dot ('.') must be a DNS name, as the NetBios + * name munging cannot encode one. Thus names which contain no + * dots must be netbios names. + * + */ +static void +dfsredir(Fmt *f, char *path, int depth) +{ + Refer *re, retab[128]; + int n, used, flags; + + n = T2getdfsreferral(Sess, &Ipc, path, &flags, &used, retab, nelem(retab)); + if(n == -1) + return; + for(re = retab; re < retab+n; re++){ + if(strcmp(path, re->path) != 0) + dfsredir(f, re->path, depth+1); + else + fmtprint(f, "%-32q %q\n", re->path, re->addr); + + free(re->addr); + free(re->path); + } +} + +int +dfsrootinfo(Fmt *f) +{ + dfsredir(f, "", 0); + return 0; +} + + +int +userinfo(Fmt *f) +{ + int got, i; + Namelist *nl; + Userinfo ui; + + nl = nil; + if((got = RAPuserenum2(Sess, &Ipc, &nl)) == -1) + if((got = RAPuserenum(Sess, &Ipc, &nl)) == -1){ + fmtprint(f, "RAPuserenum: %r\n"); + return 0; + } + + for(i = 0; i < got; i++){ + fmtprint(f, "%-24q ", nl[i].name); + + if(RAPuserinfo(Sess, &Ipc, nl[i].name, &ui) != -1){ + fmtprint(f, "%-48q %q", ui.fullname, ui.comment); + free(ui.user); + free(ui.comment); + free(ui.fullname); + free(ui.user_comment); + } + free(nl[i].name); + fmtprint(f, "\n"); + } + free(nl); + return 0; +} + +int +groupinfo(Fmt *f) +{ + int got1, got2, i, j; + Namelist *grps, *usrs; + + grps = nil; + if((got1 = RAPgroupenum(Sess, &Ipc, &grps)) == -1){ + fmtprint(f, "RAPgroupenum: %r\n"); + return 0; + } + + for(i = 0; i < got1; i++){ + fmtprint(f, "%q ", grps[i].name); + usrs = nil; + if((got2 = RAPgroupusers(Sess, &Ipc, grps[i].name, &usrs)) != -1){ + for(j = 0; j < got2; j++){ + fmtprint(f, "%q ", usrs[j].name); + free(usrs[j].name); + } + free(usrs); + } + free(grps[i].name); + fmtprint(f, "\n"); + } + free(grps); + return 0; +} + +static int +nodelist(Fmt *f, int type) +{ + int more, got, i, j; + Serverinfo *si; + static char *types[] = { + [0] "workstation", + [1] "server", + [2] "SQL server", + [3] "DC", + [4] "backup DC", + [5] "time source", + [6] "Apple server", + [7] "Novell server", + [8] "domain member", + [9] "printer server", + [10] "dial-up server", + [11] "Unix", + [12] "NT", + [13] "WFW", + [14] "MFPN (?)", + [15] "NT server", + [16] "potential browser", + [17] "backup browser", + [18] "LMB", + [19] "DMB", + [20] "OSF Unix", + [21] "VMS", + [22] "Win95", + [23] "DFS", + [24] "NT cluster", + [25] "Terminal server", + [26] "[26]", + [27] "[27]", + [28] "IBM DSS", + }; + + si = nil; + if((got = RAPServerenum2(Sess, &Ipc, Sess->auth->windom, type, &more, + &si)) == -1){ + fmtprint(f, "RAPServerenum2: %r\n"); + return 0; + } + if(more) + if((got = RAPServerenum3(Sess, &Ipc, Sess->auth->windom, type, + got-1, si)) == -1){ + fmtprint(f, "RAPServerenum3: %r\n"); + return 0; + } + + for(i = 0; i < got; i++){ + fmtprint(f, "%-16q %-16q ", si[i].name, si[i].comment); + if(type != LIST_DOMAINS_ONLY){ + fmtprint(f, "v%d.%d ", si[i].major, si[i].minor); + for(j = 0; j < nelem(types); j++) + if(si[i].type & (1 << j) && types[j]) + fmtprint(f, "%s,", types[j]); + } + fmtprint(f, "\n"); + free(si[i].name); + free(si[i].comment); + } + free(si); + return 0; +} + +int +domaininfo(Fmt *f) +{ + return nodelist(f, LIST_DOMAINS_ONLY); +} + +int +workstationinfo(Fmt *f) +{ + return nodelist(f, ALL_LEARNT_IN_DOMAIN); +} + +static char * +period(long sec) +{ + int days, hrs, min; + static char when[32]; + + days = sec / (60L * 60L * 24L); + sec -= days * (60L * 60L * 24L); + hrs = sec / (60L * 60L); + sec -= hrs * (60L * 60L); + min = sec / 60L; + sec -= min * 60L; + if(days) + snprint(when, sizeof(when), "%d %d:%d:%ld ", days, hrs, min, sec); + else + snprint(when, sizeof(when), "%d:%d:%ld ", hrs, min, sec); + return when; +} diff --git a/sys/src/cmd/cifs/info.c b/sys/src/cmd/cifs/info.c new file mode 100755 index 000000000..f6feb5f3b --- /dev/null +++ b/sys/src/cmd/cifs/info.c @@ -0,0 +1,106 @@ +#include +#include +#include +#include +#include +#include <9p.h> +#include "cifs.h" + + +struct { + char *name; + int (*func)(Fmt *f); + char *buf; + int len; +} Infdir[] = { + { "Users", userinfo }, + { "Groups", groupinfo }, + { "Shares", shareinfo }, + { "Connection", conninfo }, + { "Sessions", sessioninfo }, + { "Dfsroot", dfsrootinfo }, + { "Dfscache", dfscacheinfo }, + { "Domains", domaininfo }, + { "Openfiles", openfileinfo }, + { "Workstations", workstationinfo }, + { "Filetable", filetableinfo }, +}; + +int +walkinfo(char *name) +{ + int i; + + for(i = 0; i < nelem(Infdir); i++) + if(strcmp(Infdir[i].name, name) == 0) + return(i); + return -1; +} + +int +numinfo(void) +{ + return nelem(Infdir); +} + +int +dirgeninfo(int slot, Dir *d) +{ + if(slot < 0 || slot > nelem(Infdir)) + return -1; + + memset(d, 0, sizeof(Dir)); + d->type = 'N'; + d->dev = 99; + d->name = estrdup9p(Infdir[slot].name); + d->uid = estrdup9p("other"); + d->muid = estrdup9p("other"); + d->gid = estrdup9p("other"); + d->mode = 0666; + d->atime = time(0); + d->mtime = d->atime; + d->qid = mkqid(Infdir[slot].name, 0, 1, Pinfo, slot); + d->qid.vers = 1; + d->qid.path = slot; + d->qid.type = 0; + return 0; +} + +int +makeinfo(int path) +{ + Fmt f; + + if(path < 0 || path > nelem(Infdir)) + return -1; + if(Infdir[path].buf != nil) + return 0; + fmtstrinit(&f); + if((*Infdir[path].func)(&f) == -1l) + return -1; + Infdir[path].buf = fmtstrflush(&f); + Infdir[path].len = strlen(Infdir[path].buf); + return 0; +} + +int +readinfo(int path, char *buf, int len, int off) +{ + if(path < 0 || path > nelem(Infdir)) + return -1; + if(off > Infdir[path].len) + return 0; + if(len + off > Infdir[path].len) + len = Infdir[path].len - off; + memmove(buf, Infdir[path].buf + off, len); + return len; +} + +void +freeinfo(int path) +{ + if(path < 0 || path > nelem(Infdir)) + return; + free(Infdir[path].buf); + Infdir[path].buf = nil; +} diff --git a/sys/src/cmd/cifs/main.c b/sys/src/cmd/cifs/main.c new file mode 100755 index 000000000..4849c5495 --- /dev/null +++ b/sys/src/cmd/cifs/main.c @@ -0,0 +1,1283 @@ +#include +#include +#include +#include +#include +#include <9p.h> +#include "cifs.h" + +#define max(a,b) (((a) > (b))? (a): (b)) +#define min(a,b) (((a) < (b))? (a): (b)) + +typedef struct Aux Aux; +struct Aux { + Aux *next; + Aux *prev; + char *path; /* full path fo file */ + Share *sp; /* this share's info */ + long expire; /* expiration time of cache */ + long off; /* file pos of start of cache */ + long end; /* file pos of end of cache */ + char *cache; + int fh; /* file handle */ + int sh; /* search handle */ + long srch; /* find first's internal state */ +}; + +extern int chatty9p; + +int Checkcase = 1; /* enforce case significance on filenames */ +int Dfstout = 100; /* timeout (in ms) for ping of dfs servers (assume they are local) */ +int Billtrog = 1; /* enable file owner/group resolution */ +int Attachpid; /* pid of proc that attaches (ugh !) */ +char *Debug = nil; /* messages */ +Qid Root; /* root of remote system */ +Share Ipc; /* Share info of IPC$ share */ +Session *Sess; /* current session */ +int Active = IDLE_TIME; /* secs until next keepalive is sent */ +static int Keeppid; /* process ID of keepalive thread */ +Share Shares[MAX_SHARES]; /* table of connected shares */ +int Nshares = 0; /* number of Shares connected */ +Aux *Auxroot = nil; /* linked list of Aux structs */ +char *Host = nil; /* host we are connected to */ + +static char *Ipcname = "IPC$"; + +#define ptype(x) (((x) & 0xf)) +#define pindex(x) (((x) & 0xff0) >> 4) + +void +setup(void) +{ + int fd; + char buf[32]; + + /* + * This is revolting but I cannot see any other way to get + * the pid of the server. We need this as Windows doesn't + * drop the TCP connection when it closes a connection. + * Thus we keepalive() to detect when/if we are thrown off. + */ + Attachpid = getpid(); + + snprint(buf, sizeof buf, "#p/%d/args", getpid()); + if((fd = open(buf, OWRITE)) >= 0){ + fprint(fd, "%s network", Host); + close(fd); + } +} + +int +filetableinfo(Fmt *f) +{ + Aux *ap; + char *type; + + if((ap = Auxroot) != nil) + do{ + type = "walked"; + if(ap->sh != -1) + type = "opendir"; + if(ap->fh != -1) + type = "openfile"; + fmtprint(f, "%-9s %s\n", type, ap->path); + ap = ap->next; + }while(ap != Auxroot); + return 0; +} + +Qid +mkqid(char *s, int is_dir, long vers, int subtype, long path) +{ + Qid q; + union { /* align digest suitably */ + uchar digest[SHA1dlen]; + uvlong uvl; + } u; + + sha1((uchar *)s, strlen(s), u.digest, nil); + q.type = (is_dir)? QTDIR: 0; + q.vers = vers; + if(subtype){ + q.path = *((uvlong *)u.digest) & ~0xfffL; + q.path |= ((path & 0xff) << 4); + q.path |= (subtype & 0xf); + } + else + q.path = *((uvlong *)u.digest) & ~0xfL; + return q; +} + +/* + * used only for root dir and shares + */ +static void +V2D(Dir *d, Qid qid, char *name) +{ + memset(d, 0, sizeof(Dir)); + d->type = 'C'; + d->dev = 1; + d->name = estrdup9p(name); + d->uid = estrdup9p("bill"); + d->muid = estrdup9p("boyd"); + d->gid = estrdup9p("trog"); + d->mode = 0755 | DMDIR; + d->atime = time(nil); + d->mtime = d->atime; + d->length = 0; + d->qid = qid; +} + +static void +I2D(Dir *d, Share *sp, char *path, FInfo *fi) +{ + char *name; + + if((name = strrchr(fi->name, '\\')) != nil) + name++; + else + name = fi->name; + d->name = estrdup9p(name); + d->type = 'C'; + d->dev = sp->tid; + d->uid = estrdup9p("bill"); + d->gid = estrdup9p("trog"); + d->muid = estrdup9p("boyd"); + d->atime = fi->accessed; + d->mtime = fi->written; + + if(fi->attribs & ATTR_READONLY) + d->mode = 0444; + else + d->mode = 0666; + + d->length = fi->size; + d->qid = mkqid(path, fi->attribs & ATTR_DIRECTORY, fi->changed, 0, 0); + + if(fi->attribs & ATTR_DIRECTORY){ + d->length = 0; + d->mode |= DMDIR|0111; + } +} + +static void +responderrstr(Req *r) +{ + char e[ERRMAX]; + + *e = 0; + rerrstr(e, sizeof e); + respond(r, e); +} + +static char * +newpath(char *path, char *name) +{ + char *p, *q; + + assert((p = strrchr(path, '/')) != nil); + + if(strcmp(name, "..") == 0){ + if(p == path) + return estrdup9p("/"); + q = emalloc9p((p-path)+1); + strecpy(q, q+(p-path)+1, path); + return q; + } + if(strcmp(path, "/") == 0) + return smprint("/%s", name); + return smprint("%s/%s", path, name); +} + +static int +dirgen(int slot, Dir *d, void *aux) +{ + long off; + FInfo *fi; + int rc, got; + Aux *a = aux; + char *npath; + int numinf = numinfo(); + int slots = min(Sess->mtu, MTU) / sizeof(FInfo); + + if(strcmp(a->path, "/") == 0){ + if(slot < numinf){ + dirgeninfo(slot, d); + return 0; + } else + slot -= numinf; + + if(slot >= Nshares) + return -1; + V2D(d, mkqid(Shares[slot].name, 1, 1, Pshare, slot), + Shares[slot].name); + return 0; + } + + off = slot * sizeof(FInfo); + if(off >= a->off && off < a->end && time(nil) < a->expire) + goto from_cache; + + if(off == 0){ + fi = (FInfo *)a->cache; + npath = smprint("%s/*", mapfile(a->path)); + a->sh = T2findfirst(Sess, a->sp, slots, npath, &got, &a->srch, + (FInfo *)a->cache); + free(npath); + if(a->sh == -1) + return -1; + + a->off = 0; + a->end = got * sizeof(FInfo); + + if(got >= 2 && strcmp(fi[0].name, ".") == 0 && + strcmp(fi[1].name, "..") == 0){ + a->end = (got - 2) * sizeof(FInfo); + memmove(a->cache, a->cache + sizeof(FInfo)*2, + a->end - a->off); + } + } + + while(off >= a->end && a->sh != -1){ + fi = (FInfo *)(a->cache + (a->end - a->off) - sizeof(FInfo)); + a->off = a->end; + npath = smprint("%s/%s", mapfile(a->path), fi->name); + rc = T2findnext(Sess, a->sp, slots, npath, + &got, &a->srch, (FInfo *)a->cache, a->sh); + free(npath); + if(rc == -1 || got == 0) + break; + a->end = a->off + got * sizeof(FInfo); + } + a->expire = time(nil) + CACHETIME; + + if(got < slots){ + if(a->sh != -1) + CIFSfindclose2(Sess, a->sp, a->sh); + a->sh = -1; + } + + if(off >= a->end) + return -1; + +from_cache: + fi = (FInfo *)(a->cache + (off - a->off)); + npath = smprint("%s/%s", mapfile(a->path), fi->name); + I2D(d, a->sp, npath, fi); + if(Billtrog == 0) + upd_names(Sess, a->sp, npath, d); + free(npath); + return 0; +} + +static void +fsattach(Req *r) +{ + Aux *a; + static int first = 1; + char *spec = r->ifcall.aname; + + if(first) + setup(); + + if(spec && *spec){ + respond(r, "invalid attach specifier"); + return; + } + + r->ofcall.qid = mkqid("/", 1, 1, Proot, 0); + r->fid->qid = r->ofcall.qid; + + a = r->fid->aux = emalloc9p(sizeof(Aux)); + memset(a, 0, sizeof(Aux)); + a->path = estrdup9p("/"); + a->sp = nil; + a->fh = -1; + a->sh = -1; + + if(Auxroot){ + a->prev = Auxroot; + a->next = Auxroot->next; + Auxroot->next->prev = a; + Auxroot->next = a; + } else { + Auxroot = a; + a->next = a; + a->prev = a; + } + respond(r, nil); +} + +static char* +fsclone(Fid *ofid, Fid *fid) +{ + Aux *oa = ofid->aux; + Aux *a = emalloc9p(sizeof(Aux)); + + fid->aux = a; + + memset(a, 0, sizeof(Aux)); + a->sh = -1; + a->fh = -1; + a->sp = oa->sp; + a->path = estrdup9p(oa->path); + + if(Auxroot){ + a->prev = Auxroot; + a->next = Auxroot->next; + Auxroot->next->prev = a; + Auxroot->next = a; + } else { + Auxroot = a; + a->next = a; + a->prev = a; + } + return nil; +} + +/* + * for some weird reason T2queryall() returns share names + * in lower case so we have to do an extra test against + * our share table to validate filename case. + * + * on top of this here (snell & Wilcox) most of our + * redirections point to a share of the same name, + * but some do not, thus the tail of the filename + * returned by T2queryall() is not the same as + * the name we wanted. + * + * We work around this by not validating the names + * or files which resolve to share names as they must + * be correct, having been enforced in the dfs layer. + */ +static int +validfile(char *found, char *want, char *winpath, Share *sp) +{ + char *share; + + if(strcmp(want, "..") == 0) + return 1; + if(strcmp(winpath, "/") == 0){ + share = trimshare(sp->name); + if(cistrcmp(want, share) == 0) + return strcmp(want, share) == 0; + /* + * OK, a DFS redirection points us from a directory XXX + * to a share named YYY. There is no case checking we can + * do so we allow either case - it's all we can do. + */ + return 1; + } + if(cistrcmp(found, want) != 0) + return 0; + if(!Checkcase) + return 1; + if(strcmp(found, want) == 0) + return 1; + return 0; +} + + +static char* +fswalk1(Fid *fid, char *name, Qid *qid) +{ + FInfo fi; + int rc, n, i; + Aux *a = fid->aux; + static char e[ERRMAX]; + char *p, *npath, *winpath; + + *e = 0; + npath = newpath(a->path, name); + if(strcmp(npath, "/") == 0){ /* root dir */ + *qid = mkqid("/", 1, 1, Proot, 0); + free(a->path); + a->path = npath; + fid->qid = *qid; + return nil; + } + + if(strrchr(npath, '/') == npath){ /* top level dir */ + if((n = walkinfo(name)) != -1){ /* info file */ + *qid = mkqid(npath, 0, 1, Pinfo, n); + } + else { /* volume name */ + for(i = 0; i < Nshares; i++){ + n = strlen(Shares[i].name); + if(cistrncmp(npath+1, Shares[i].name, n) != 0) + continue; + if(Checkcase && strncmp(npath+1, Shares[i].name, n) != 0) + continue; + if(npath[n+1] != 0 && npath[n+1] != '/') + continue; + break; + } + if(i >= Nshares){ + free(npath); + return "not found"; + } + a->sp = Shares+i; + *qid = mkqid(npath, 1, 1, Pshare, i); + } + free(a->path); + a->path = npath; + fid->qid = *qid; + return nil; + } + + /* must be a vanilla file or directory */ +again: + if(mapshare(npath, &a->sp) == -1){ + rerrstr(e, sizeof(e)); + free(npath); + return e; + } + + winpath = mapfile(npath); + memset(&fi, 0, sizeof fi); + if(Sess->caps & CAP_NT_SMBS) + rc = T2queryall(Sess, a->sp, winpath, &fi); + else + rc = T2querystandard(Sess, a->sp, winpath, &fi); + + if(rc == -1){ + rerrstr(e, sizeof(e)); + free(npath); + return e; + } + + if((a->sp->options & SMB_SHARE_IS_IN_DFS) != 0 && + (fi.attribs & ATTR_REPARSE) != 0){ + if(redirect(Sess, a->sp, npath) != -1) + goto again; + } + + if((p = strrchr(fi.name, '/')) == nil && (p = strrchr(fi.name, '\\')) == nil) + p = fi.name; + else + p++; + + if(! validfile(p, name, winpath, a->sp)){ + free(npath); + return "not found"; + + } + *qid = mkqid(npath, fi.attribs & ATTR_DIRECTORY, fi.changed, 0, 0); + + free(a->path); + a->path = npath; + fid->qid = *qid; + return nil; +} + +static void +fsstat(Req *r) +{ + int rc; + FInfo fi; + Aux *a = r->fid->aux; + + if(ptype(r->fid->qid.path) == Proot) + V2D(&r->d, r->fid->qid, ""); + else if(ptype(r->fid->qid.path) == Pinfo) + dirgeninfo(pindex(r->fid->qid.path), &r->d); + else if(ptype(r->fid->qid.path) == Pshare) + V2D(&r->d, r->fid->qid, a->path +1); + else{ + memset(&fi, 0, sizeof fi); + if(Sess->caps & CAP_NT_SMBS) + rc = T2queryall(Sess, a->sp, mapfile(a->path), &fi); + else + rc = T2querystandard(Sess, a->sp, mapfile(a->path), &fi); + if(rc == -1){ + responderrstr(r); + return; + } + I2D(&r->d, a->sp, a->path, &fi); + if(Billtrog == 0) + upd_names(Sess, a->sp, mapfile(a->path), &r->d); + } + respond(r, nil); +} + +static int +smbcreateopen(Aux *a, char *path, int mode, int perm, int is_create, + int is_dir, FInfo *fip) +{ + int rc, action, attrs, access, result; + + if(is_create && is_dir){ + if(CIFScreatedirectory(Sess, a->sp, path) == -1) + return -1; + return 0; + } + + if(mode & DMAPPEND) { + werrstr("filesystem does not support DMAPPEND"); + return -1; + } + + if(is_create) + action = 0x12; + else if(mode & OTRUNC) + action = 0x02; + else + action = 0x01; + + if(perm & 0222) + attrs = ATTR_NORMAL; + else + attrs = ATTR_NORMAL|ATTR_READONLY; + + switch (mode & OMASK){ + case OREAD: + access = 0; + break; + case OWRITE: + access = 1; + break; + case ORDWR: + access = 2; + break; + case OEXEC: + access = 3; + break; + default: + werrstr("%d bad open mode", mode & OMASK); + return -1; + break; + } + + if(mode & DMEXCL == 0) + access |= 0x10; + else + access |= 0x40; + + if((a->fh = CIFS_SMB_opencreate(Sess, a->sp, path, access, attrs, + action, &result)) == -1) + return -1; + + if(Sess->caps & CAP_NT_SMBS) + rc = T2queryall(Sess, a->sp, mapfile(a->path), fip); + else + rc = T2querystandard(Sess, a->sp, mapfile(a->path), fip); + if(rc == -1){ + fprint(2, "internal error: stat of newly open/created file failed\n"); + return -1; + } + + if((mode & OEXCL) && (result & 0x8000) == 0){ + werrstr("%d bad open mode", mode & OMASK); + return -1; + } + return 0; +} + +/* Uncle Bill, you have a lot to answer for... */ +static int +ntcreateopen(Aux *a, char *path, int mode, int perm, int is_create, + int is_dir, FInfo *fip) +{ + int options, result, attrs, flags, access, action, share; + + if(mode & DMAPPEND){ + werrstr("CIFSopen, DMAPPEND not supported"); + return -1; + } + + if(is_create){ + if(mode & OEXCL) + action = FILE_OPEN; + else if(mode & OTRUNC) + action = FILE_CREATE; + else + action = FILE_OVERWRITE_IF; + } else { + if(mode & OTRUNC) + action = FILE_OVERWRITE_IF; + else + action = FILE_OPEN_IF; + } + + flags = 0; /* FIXME: really not sure */ + + if(mode & OEXCL) + share = FILE_NO_SHARE; + else + share = FILE_SHARE_ALL; + + switch (mode & OMASK){ + case OREAD: + access = GENERIC_READ; + break; + case OWRITE: + access = GENERIC_WRITE; + break; + case ORDWR: + access = GENERIC_ALL; + break; + case OEXEC: + access = GENERIC_EXECUTE; + break; + default: + werrstr("%d bad open mode", mode & OMASK); + return -1; + break; + } + + if(is_dir){ + action = FILE_CREATE; + options = FILE_DIRECTORY_FILE; + if(perm & 0222) + attrs = ATTR_DIRECTORY; + else + attrs = ATTR_DIRECTORY|ATTR_READONLY; + } else { + options = FILE_NON_DIRECTORY_FILE; + if(perm & 0222) + attrs = ATTR_NORMAL; + else + attrs = ATTR_NORMAL|ATTR_READONLY; + } + + if(mode & ORCLOSE){ + options |= FILE_DELETE_ON_CLOSE; + attrs |= ATTR_DELETE_ON_CLOSE; + } + + if((a->fh = CIFS_NT_opencreate(Sess, a->sp, path, flags, options, + attrs, access, share, action, &result, fip)) == -1) + return -1; + + if((mode & OEXCL) && (result & 0x8000) == 0){ + werrstr("%d bad open mode", mode & OMASK); + return -1; + } + + return 0; +} + +static void +fscreate(Req *r) +{ + FInfo fi; + int rc, is_dir; + char *npath; + Aux *a = r->fid->aux; + + a->end = a->off = 0; + a->cache = emalloc9p(max(Sess->mtu, MTU)); + + is_dir = (r->ifcall.perm & DMDIR) == DMDIR; + npath = smprint("%s/%s", a->path, r->ifcall.name); + + if(Sess->caps & CAP_NT_SMBS) + rc = ntcreateopen(a, mapfile(npath), r->ifcall.mode, + r->ifcall.perm, 1, is_dir, &fi); + else + rc = smbcreateopen(a, mapfile(npath), r->ifcall.mode, + r->ifcall.perm, 1, is_dir, &fi); + if(rc == -1){ + free(npath); + responderrstr(r); + return; + } + + r->fid->qid = mkqid(npath, fi.attribs & ATTR_DIRECTORY, fi.changed, 0, 0); + + r->ofcall.qid = r->fid->qid; + free(a->path); + a->path = npath; + + respond(r, nil); +} + +static void +fsopen(Req *r) +{ + int rc; + FInfo fi; + Aux *a = r->fid->aux; + + a->end = a->off = 0; + a->cache = emalloc9p(max(Sess->mtu, MTU)); + + if(ptype(r->fid->qid.path) == Pinfo){ + if(makeinfo(pindex(r->fid->qid.path)) != -1) + respond(r, nil); + else + respond(r, "cannot generate info"); + return; + } + + if(r->fid->qid.type & QTDIR){ + respond(r, nil); + return; + } + + if(Sess->caps & CAP_NT_SMBS) + rc = ntcreateopen(a, mapfile(a->path), r->ifcall.mode, 0777, + 0, 0, &fi); + else + rc = smbcreateopen(a, mapfile(a->path), r->ifcall.mode, 0777, + 0, 0, &fi); + if(rc == -1){ + responderrstr(r); + return; + } + respond(r, nil); +} + +static void +fswrite(Req *r) +{ + vlong n, m, got; + Aux *a = r->fid->aux; + vlong len = r->ifcall.count; + vlong off = r->ifcall.offset; + char *buf = r->ifcall.data; + + got = 0; + n = Sess->mtu -OVERHEAD; + do{ + if(len - got < n) + n = len - got; + m = CIFSwrite(Sess, a->sp, a->fh, off + got, buf + got, n); + if(m != -1) + got += m; + } while(got < len && m >= n); + + r->ofcall.count = got; + if(m == -1) + responderrstr(r); + else + respond(r, nil); +} + +static void +fsread(Req *r) +{ + vlong n, m, got; + Aux *a = r->fid->aux; + char *buf = r->ofcall.data; + vlong len = r->ifcall.count; + vlong off = r->ifcall.offset; + + if(ptype(r->fid->qid.path) == Pinfo){ + r->ofcall.count = readinfo(pindex(r->fid->qid.path), buf, len, + off); + respond(r, nil); + return; + } + + if(r->fid->qid.type & QTDIR){ + dirread9p(r, dirgen, a); + respond(r, nil); + return; + } + + got = 0; + n = Sess->mtu -OVERHEAD; + do{ + if(len - got < n) + n = len - got; + m = CIFSread(Sess, a->sp, a->fh, off + got, buf + got, n, len); + if(m != -1) + got += m; + } while(got < len && m >= n); + + r->ofcall.count = got; + if(m == -1) + responderrstr(r); + else + respond(r, nil); +} + +static void +fsdestroyfid(Fid *f) +{ + Aux *a = f->aux; + + if(ptype(f->qid.path) == Pinfo) + freeinfo(pindex(f->qid.path)); + f->omode = -1; + if(! a) + return; + if(a->fh != -1) + if(CIFSclose(Sess, a->sp, a->fh) == -1) + fprint(2, "%s: close failed fh=%d %r\n", argv0, a->fh); + if(a->sh != -1) + if(CIFSfindclose2(Sess, a->sp, a->sh) == -1) + fprint(2, "%s: findclose failed sh=%d %r\n", + argv0, a->sh); + if(a->path) + free(a->path); + if(a->cache) + free(a->cache); + + if(a == Auxroot) + Auxroot = a->next; + a->prev->next = a->next; + a->next->prev = a->prev; + if(a->next == a->prev) + Auxroot = nil; + if(a) + free(a); +} + +int +rdonly(Session *s, Share *sp, char *path, int rdonly) +{ + int rc; + FInfo fi; + + if(Sess->caps & CAP_NT_SMBS) + rc = T2queryall(s, sp, path, &fi); + else + rc = T2querystandard(s, sp, path, &fi); + if(rc == -1) + return -1; + + if((rdonly && !(fi.attribs & ATTR_READONLY)) || + (!rdonly && (fi.attribs & ATTR_READONLY))){ + fi.attribs &= ~ATTR_READONLY; + fi.attribs |= rdonly? ATTR_READONLY: 0; + rc = CIFSsetinfo(s, sp, path, &fi); + } + return rc; +} + +static void +fsremove(Req *r) +{ + int try, rc; + char e[ERRMAX]; + Aux *ap, *a = r->fid->aux; + + *e = 0; + if(ptype(r->fid->qid.path) == Proot || + ptype(r->fid->qid.path) == Pshare){ + respond(r, "illegal operation"); + return; + } + + /* close all instences of this file/dir */ + if((ap = Auxroot) != nil) + do{ + if(strcmp(ap->path, a->path) == 0){ + if(ap->sh != -1) + CIFSfindclose2(Sess, ap->sp, ap->sh); + ap->sh = -1; + if(ap->fh != -1) + CIFSclose(Sess, ap->sp, ap->fh); + ap->fh = -1; + } + ap = ap->next; + }while(ap != Auxroot); + try = 0; +again: + if(r->fid->qid.type & QTDIR) + rc = CIFSdeletedirectory(Sess, a->sp, mapfile(a->path)); + else + rc = CIFSdeletefile(Sess, a->sp, mapfile(a->path)); + + rerrstr(e, sizeof(e)); + if(rc == -1 && try++ == 0 && strcmp(e, "permission denied") == 0 && + rdonly(Sess, a->sp, mapfile(a->path), 0) == 0) + goto again; + if(rc == -1) + responderrstr(r); + else + respond(r, nil); +} + +static void +fswstat(Req *r) +{ + int fh, result, rc; + FInfo fi, tmpfi; + char *p, *from, *npath; + Aux *a = r->fid->aux; + + if(ptype(r->fid->qid.path) == Proot || + ptype(r->fid->qid.path) == Pshare){ + respond(r, "illegal operation"); + return; + } + + if((r->d.uid && r->d.uid[0]) || (r->d.gid && r->d.gid[0])){ + respond(r, "cannot change ownership"); + return; + } + + /* + * get current info + */ + if(Sess->caps & CAP_NT_SMBS) + rc = T2queryall(Sess, a->sp, mapfile(a->path), &fi); + else + rc = T2querystandard(Sess, a->sp, mapfile(a->path), &fi); + if(rc == -1){ + werrstr("(query) - %r"); + responderrstr(r); + return; + } + + /* + * always clear the readonly attribute if set, + * before trying to set any other fields. + * wstat() fails if the file/dir is readonly + * and this function is so full of races - who cares about one more? + */ + rdonly(Sess, a->sp, mapfile(a->path), 0); + + /* + * rename - one piece of joy, renaming open files + * is legal (sharing permitting). + */ + if(r->d.name && r->d.name[0]){ + if((p = strrchr(a->path, '/')) == nil){ + respond(r, "illegal path"); + return; + } + npath = emalloc9p((p-a->path)+strlen(r->d.name)+2); + strecpy(npath, npath+(p- a->path)+2, a->path); + strcat(npath, r->d.name); + + from = estrdup9p(mapfile(a->path)); + if(CIFSrename(Sess, a->sp, from, mapfile(npath)) == -1){ + werrstr("(rename) - %r"); + responderrstr(r); + free(npath); + free(from); + return; + } + free(from); + free(a->path); + a->path = npath; + } + + /* + * set the files length, do this before setting + * the file times as open() will alter them + */ + if(~r->d.length){ + fi.size = r->d.length; + + if(Sess->caps & CAP_NT_SMBS){ + if((fh = CIFS_NT_opencreate(Sess, a->sp, mapfile(a->path), + 0, FILE_NON_DIRECTORY_FILE, + ATTR_NORMAL, GENERIC_WRITE, FILE_SHARE_ALL, + FILE_OPEN_IF, &result, &tmpfi)) == -1){ + werrstr("(set length, open) - %r"); + responderrstr(r); + return; + } + rc = T2setfilelength(Sess, a->sp, fh, &fi); + CIFSclose(Sess, a->sp, fh); + if(rc == -1){ + werrstr("(set length), set) - %r"); + responderrstr(r); + return; + } + } else { + if((fh = CIFS_SMB_opencreate(Sess, a->sp, mapfile(a->path), + 1, ATTR_NORMAL, 1, &result)) == -1){ + werrstr("(set length, open) failed - %r"); + responderrstr(r); + return; + } + rc = CIFSwrite(Sess, a->sp, fh, fi.size, 0, 0); + CIFSclose(Sess, a->sp, fh); + if(rc == -1){ + werrstr("(set length, write) - %r"); + responderrstr(r); + return; + } + } + } + + /* + * This doesn't appear to set length or + * attributes, no idea why, so I do those seperately + */ + if(~r->d.mtime || ~r->d.atime){ + if(~r->d.mtime) + fi.written = r->d.mtime; + if(~r->d.atime) + fi.accessed = r->d.atime; + if(T2setpathinfo(Sess, a->sp, mapfile(a->path), &fi) == -1){ + werrstr("(set path info) - %r"); + responderrstr(r); + return; + } + } + + /* + * always update the readonly flag as + * we may have cleared it above. + */ + if(~r->d.mode){ + if(r->d.mode & 0222) + fi.attribs &= ~ATTR_READONLY; + else + fi.attribs |= ATTR_READONLY; + } + if(rdonly(Sess, a->sp, mapfile(a->path), fi.attribs & ATTR_READONLY) == -1){ + werrstr("(set info) - %r"); + responderrstr(r); + return; + } + + /* + * Win95 has a broken write-behind cache for metadata + * on open files (writes go to the cache, reads bypass + * the cache), so we must flush the file. + */ + if(r->fid->omode != -1 && CIFSflush(Sess, a->sp, a->fh) == -1){ + werrstr("(flush) %r"); + responderrstr(r); + return; + } + respond(r, nil); +} + +static void +fsend(Srv *srv) +{ + int i; + USED(srv); + + for(i = 0; i < Nshares; i++) + CIFStreedisconnect(Sess, Shares+i); + CIFSlogoff(Sess); + postnote(PNPROC, Keeppid, "die"); +} + +Srv fs = { + .destroyfid = fsdestroyfid, + .attach= fsattach, + .open= fsopen, + .create= fscreate, + .read= fsread, + .write= fswrite, + .remove= fsremove, + .stat= fsstat, + .wstat= fswstat, + .clone= fsclone, + .walk1= fswalk1, + .end= fsend, +}; + +void +usage(void) +{ + fprint(2, "usage: %s [-d name] [-Dvb] [-a auth-method] [-s srvname] " + "[-n called-name] [-k factotum-params] [-m mntpnt] " + "host [share...]\n", argv0); + exits("usage"); +} + +/* + * SMBecho looks like the function to use for keepalives, + * sadly the echo packet does not seem to reload the + * idle timer in Microsoft's servers. Instead we use + * "get file system size" on each share until we get one that succeeds. + */ +static void +keepalive(void) +{ + char buf[32]; + uvlong tot, fre; + int fd, i, slot, rc; + + snprint(buf, sizeof buf, "#p/%d/args", getpid()); + if((fd = open(buf, OWRITE)) >= 0){ + fprint(fd, "%s keepalive", Host); + close(fd); + } + + rc = 0; + slot = 0; + do{ + sleep(6000); + if(Active-- != 0) + continue; + for(i = 0; i < Nshares; i++){ + if((rc = T2fssizeinfo(Sess, &Shares[slot], &tot, &fre)) == 0) + break; + if(++slot >= Nshares) + slot = 0; + } + }while(rc != -1); + postnote(PNPROC, Attachpid, "die"); +} + + +static void +ding(void *u, char *msg) +{ + USED(u); + if(strstr(msg, "alarm") != nil) + noted(NCONT); + noted(NDFLT); +} + +void +dmpkey(char *s, void *v, int n) +{ + int i; + unsigned char *p = (unsigned char *)v; + + print("%s", s); + for(i = 0; i < n; i++) + print("%02ux ", *p++); + print("\n"); +} + +void +main(int argc, char **argv) +{ + int i, n; + long svrtime; + char windom[64], cname[64]; + char *method, *sysname, *keyp, *mtpt, *svs; + static char *sh[1024]; + + *cname = 0; + keyp = ""; + method = nil; + strcpy(windom, "unknown"); + mtpt = svs = nil; + + notify(ding); + + ARGBEGIN{ + case 'a': + method = EARGF(autherr()); + break; + case 'b': + Billtrog ^= 1; + break; + case 'D': + chatty9p++; + break; + case 'd': + Debug = EARGF(usage()); + break; + case 'i': + Checkcase = 0; + break; + case 'k': + keyp = EARGF(usage()); + break; + case 'm': + mtpt = EARGF(usage()); + break; + case 'n': + strncpy(cname, EARGF(usage()), sizeof(cname)); + cname[sizeof(cname) - 1] = 0; + break; + case 's': + svs = EARGF(usage()); + break; + case 't': + Dfstout = atoi(EARGF(usage())); + break; + default: + usage(); + break; + }ARGEND + + if(argc < 1) + usage(); + + Host = argv[0]; + + if(mtpt == nil && svs == nil) + mtpt = smprint("/n/%s", Host); + + if((sysname = getenv("sysname")) == nil) + sysname = "unknown"; + + if(*cname && (Sess = cifsdial(Host, cname, sysname)) != nil) + goto connected; + + if(calledname(Host, cname) == 0 && + (Sess = cifsdial(Host, cname, sysname)) != nil) + goto connected; + + strcpy(cname, Host); + if((Sess = cifsdial(Host, Host, sysname)) != nil || + (Sess = cifsdial(Host, "*SMBSERVER", sysname)) != nil) + goto connected; + + sysfatal("%s - cannot dial, %r\n", Host); +connected: + if(CIFSnegotiate(Sess, &svrtime, windom, sizeof windom, cname, sizeof cname) == -1) + sysfatal("%s - cannot negioate common protocol, %r\n", Host); + +#ifndef DEBUG_MAC + Sess->secmode &= ~SECMODE_SIGN_ENABLED; +#endif + + Sess->auth = getauth(method, windom, keyp, Sess->secmode, Sess->chal, + Sess->challen); + + if(CIFSsession(Sess) < 0) + sysfatal("session authentication failed, %r\n"); + + Sess->slip = svrtime - time(nil); + Sess->cname = estrdup9p(cname); + + if(CIFStreeconnect(Sess, cname, Ipcname, &Ipc) == -1) + fprint(2, "%s, %r - can't connect\n", Ipcname); + + Nshares = 0; + if(argc == 1){ + Share *sip; + + if((n = RAPshareenum(Sess, &Ipc, &sip)) < 1) + sysfatal("can't enumerate shares: %r - specify share " + "names on command line\n"); + + for(i = 0; i < n; i++){ +#ifdef NO_HIDDEN_SHARES + int l = strlen(sip[i].name); + + if(l > 1 && sip[i].name[l-1] == '$'){ + free(sip[i].name); + continue; + } +#endif + memcpy(Shares+Nshares, sip+i, sizeof(Share)); + if(CIFStreeconnect(Sess, Sess->cname, + Shares[Nshares].name, Shares+Nshares) == -1){ + free(Shares[Nshares].name); + continue; + } + Nshares++; + } + free(sip); + } else + for(i = 1; i < argc; i++){ + if(CIFStreeconnect(Sess, Sess->cname, argv[i], + Shares+Nshares) == -1){ + fprint(2, "%s: %s %q - can't connect to share" + ", %r\n", argv0, Host, argv[i]); + continue; + } + Shares[Nshares].name = strlwr(estrdup9p(argv[i])); + Nshares++; + } + + if(Nshares == 0) + fprint(2, "no available shares\n"); + + if((Keeppid = rfork(RFPROC|RFMEM|RFNOTEG|RFFDG|RFNAMEG)) == 0){ + keepalive(); + exits(nil); + } + postmountsrv(&fs, svs, mtpt, MREPL|MCREATE); + exits(nil); +} diff --git a/sys/src/cmd/cifs/misc.c b/sys/src/cmd/cifs/misc.c new file mode 100755 index 000000000..c82cc7a82 --- /dev/null +++ b/sys/src/cmd/cifs/misc.c @@ -0,0 +1,25 @@ +#include +#include +#include + +char* +strupr(char *s) +{ + char *p; + + for(p = s; *p; p++) + if(*p >= 0 && islower(*p)) + *p = toupper(*p); + return s; +} + +char* +strlwr(char *s) +{ + char *p; + + for(p = s; *p; p++) + if(*p >= 0 && isupper(*p)) + *p = tolower(*p); + return s; +} diff --git a/sys/src/cmd/cifs/mkfile b/sys/src/cmd/cifs/mkfile new file mode 100755 index 000000000..b53660103 --- /dev/null +++ b/sys/src/cmd/cifs/mkfile @@ -0,0 +1,20 @@ + +#include +#include +#include +#include +#include <9p.h> +#include "cifs.h" + +enum { + MAXNBPKT = 8096, /* max netbios packet size */ + NBquery = 0, /* packet type - query */ + + NBAdapterStatus = 0x21, /* get host interface info */ + NBInternet = 1, /* scope for info */ + + NBmessage = 0x00, /* Netbios packet types */ + NBrequest = 0x81, + NBpositive, + NBnegative, + NBretarget, + NBkeepalive, + + ISgroup = 0x8000, +}; + + +static char *NBerr[] = { + [0] "not listening on called name", + [1] "not listening for calling name", + [2] "called name not present", + [3] "insufficient resources", + [15] "unspecified error" +}; + + +static ulong +GL32(uchar **p) +{ + ulong n; + + n = *(*p)++; + n |= *(*p)++ << 8; + n |= *(*p)++ << 16; + n |= *(*p)++ << 24; + return n; +} + +static ushort +GL16(uchar **p) +{ + ushort n; + + n = *(*p)++; + n |= *(*p)++ << 8; + return n; +} + +void +Gmem(uchar **p, void *v, int n) +{ + uchar *str = v; + + while(n--) + *str++ = *(*p)++; +} + + +static ulong +GB32(uchar **p) +{ + ulong n; + + n = *(*p)++ << 24; + n |= *(*p)++ << 16; + n |= *(*p)++ << 8; + n |= *(*p)++; + return n; +} + +static ushort +GB16(uchar **p) +{ + ushort n; + + n = *(*p)++ << 8; + n |= *(*p)++; + return n; +} + +static uchar +G8(uchar **p) +{ + return *(*p)++; +} + +static void +PB16(uchar **p, uint n) +{ + *(*p)++ = n >> 8; + *(*p)++ = n; +} + +static void +P8(uchar **p, uint n) +{ + *(*p)++ = n; +} + + +static void +nbname(uchar **p, char *name, char pad) +{ + char c; + int i; + int done = 0; + + *(*p)++ = 0x20; + for(i = 0; i < 16; i++) { + c = pad; + if(!done && name[i] == '\0') + done = 1; + if(!done) + c = toupper(name[i]); + *(*p)++ = ((uchar)c >> 4) + 'A'; + *(*p)++ = (c & 0xf) + 'A'; + } + *(*p)++ = 0; +} + +int +calledname(char *host, char *name) +{ + char *addr; + uchar buf[1024], *p; + static char tmp[20]; + int num, flg, svs, j, i, fd, trn; + + trn = (getpid() ^ time(0)) & 0xffff; + if((addr = netmkaddr(host, "udp", "137")) == nil) + return -1; + + if((fd = dial(addr, "137", 0, 0)) < 0) + return -1; + p = buf; + + PB16(&p, trn); /* TRNid */ + P8(&p, 0); /* flags */ + P8(&p, 0x10); /* type */ + PB16(&p, 1); /* # questions */ + PB16(&p, 0); /* # answers */ + PB16(&p, 0); /* # authority RRs */ + PB16(&p, 0); /* # Aditional RRs */ + nbname(&p, "*", 0); + PB16(&p, NBAdapterStatus); + PB16(&p, NBInternet); + + if(Debug && strstr(Debug, "dump")) + xd(nil, buf, p-buf); + + if(write(fd, buf, p-buf) != p-buf) + return -1; + + p = buf; + for(i = 0; i < 3; i++){ + memset(buf, 0, sizeof(buf)); + alarm(NBNSTOUT); + read(fd, buf, sizeof(buf)); + alarm(0); + if(GB16(&p) == trn) + break; + } + close(fd); + if(i >= 3) + return -1; + + p = buf +56; + num = G8(&p); /* number of names */ + + for(i = 0; i < num; i++){ + memset(tmp, 0, sizeof(tmp)); + Gmem(&p, tmp, 15); + svs = G8(&p); + flg = GB16(&p); + for(j = 14; j >= 0 && tmp[j] == ' '; j--) + tmp[j] = 0; + if(svs == 0 && !(flg & ISgroup)) + strcpy(name, tmp); + } + return 0; +} + + +int +nbtdial(char *addr, char *called, char *sysname) +{ + char redir[20]; + uchar *p, *lenp, buf[1024]; + int type, len, err, fd, nkeepalive, nretarg; + + nretarg = 0; + nkeepalive = 0; +Redial: + if((addr = netmkaddr(addr, "tcp", "139")) == nil || + (fd = dial(addr, 0, 0, 0)) < 0) + return -1; + + memset(buf, 0, sizeof(buf)); + + p = buf; + P8(&p, NBrequest); /* type */ + P8(&p, 0); /* flags */ + lenp = p; PB16(&p, 0); /* length placeholder */ + nbname(&p, called, ' '); /* remote NetBios name */ + nbname(&p, sysname, ' '); /* our machine name */ + PB16(&lenp, p-lenp -2); /* length re-write */ + + if(Debug && strstr(Debug, "dump")) + xd(nil, buf, p-buf); + if(write(fd, buf, p-buf) != p-buf) + goto Error; +Reread: + p = buf; + memset(buf, 0, sizeof(buf)); + if(readn(fd, buf, 4) < 4) + goto Error; + + type = G8(&p); + G8(&p); /* flags */ + len = GB16(&p); + + if(readn(fd, buf +4, len -4) < len -4) + goto Error; + + if(Debug && strstr(Debug, "dump")) + xd(nil, buf, len+4); + + switch(type) { + case NBpositive: + return fd; + case NBnegative: + if(len < 1) { + werrstr("nbdial: bad error pkt"); + goto Error; + } + err = G8(&p); + if(err < 0 || err > nelem(NBerr) || NBerr[err] == nil) + werrstr("NBT: %d - unknown error", err); + else + werrstr("NBT: %s", NBerr[err]); + + goto Error; + case NBkeepalive: + if(++nkeepalive >= 16){ + werrstr("nbdial: too many keepalives"); + goto Error; + } + goto Reread; + + case NBretarget: + if(++nretarg >= 16) { + werrstr("nbdial: too many redirects"); + goto Error; + } + if(len < 4) { + werrstr("nbdial: bad redirect pkt"); + goto Error; + } + sprint(redir, "%d.%d.%d.%d", p[0], p[1], p[2], p[3]); + addr = redir; + goto Redial; + + default: + werrstr("nbdial: 0x%x - unknown packet in netbios handshake", type); + goto Error; + } +Error: + close(fd); + return -1; +} + +void +nbthdr(Pkt *p) +{ + p->pos = p->buf; + memset(p->buf, 0xa5, MTU); + + p8(p, NBmessage); /* type */ + p8(p, 0); /* flags */ + pb16(p, 0); /* length (filled in later) */ +} + +int +nbtrpc(Pkt *p) +{ + int len, got, type, nkeep; + + len = p->pos - p->buf; + + p->pos = p->buf +2; + pb16(p, len - NBHDRLEN); /* length */ + + if(Debug && strstr(Debug, "dump")) + xd("tx", p->buf, len); + + alarm(NBRPCTOUT); + if(write(p->s->fd, p->buf, len) != len){ + werrstr("nbtrpc: write failed - %r"); + alarm(0); + return -1; + } + + nkeep = 0; +retry: + p->pos = p->buf; + memset(p->buf, 0xa5, MTU); + + got = readn(p->s->fd, p->buf, NBHDRLEN); + + if(got < NBHDRLEN){ + werrstr("nbtrpc: short read - %r"); + alarm(0); + return -1; + } + p->eop = p->buf + got; + + type = g8(p); /* NBT type (session) */ + if(type == NBkeepalive){ + if(++nkeep > 16) { + werrstr("nbtrpc: too many keepalives (%d attempts)", nkeep); + alarm(0); + return -1; + } + goto retry; + } + + g8(p); /* NBT flags (none) */ + + len = gb16(p); /* NBT payload length */ + if((len +NBHDRLEN) > MTU){ + werrstr("nbtrpc: packet bigger than MTU, (%d > %d)", len, MTU); + alarm(0); + return -1; + } + + got = readn(p->s->fd, p->buf +NBHDRLEN, len); + alarm(0); + + if(Debug && strstr(Debug, "dump")) + xd("rx", p->buf, got +NBHDRLEN); + + if(got < 0) + return -1; + p->eop = p->buf + got +NBHDRLEN; + return got+NBHDRLEN; +} + + +void +xd(char *str, void *buf, int n) +{ + int fd, flg, flags2, cmd; + uint sum; + long err; + uchar *p, *end; + + if(n == 0) + return; + + p = buf; + end = (uchar *)buf +n; + + if(Debug && strstr(Debug, "log") != nil){ + if((fd = open("pkt.log", ORDWR)) == -1) + return; + seek(fd, 0, 2); + fprint(fd, "%d ", 0); + while(p < end) + fprint(fd, "%02x ", *p++); + fprint(fd, "\n"); + close(fd); + return; + } + + if(!str) + goto Raw; + + p = (uchar *)buf + 4; + if(GL32(&p) == 0x424d53ff){ + buf = (uchar *)buf + 4; + n -= 4; + } + end = (uchar *)buf + n; + + sum = 0; + p = buf; + while(p < end) + sum += *p++; + p = buf; + + fprint(2, "%s : len=%ud sum=%d\n", str, n, sum); + + fprint(2, "mag=0x%ulx ", GL32(&p)); + fprint(2, "cmd=0x%ux ", cmd = G8(&p)); + fprint(2, "err=0x%ulx ", err=GL32(&p)); + fprint(2, "flg=0x%02ux ", flg = G8(&p)); + fprint(2, "flg2=0x%04ux\n", flags2= GL16(&p)); + fprint(2, "dfs=%s\n", (flags2 & FL2_DFS)? "y": "n"); + + fprint(2, "pidl=%ud ", GL16(&p)); + fprint(2, "res=%uld ", GL32(&p)); + fprint(2, "sid=%ud ", GL16(&p)); + fprint(2, "seq=0x%ux ", GL16(&p)); + fprint(2, "pad=%ud ", GL16(&p)); + + fprint(2, "tid=%ud ", GL16(&p)); + fprint(2, "pid=%ud ", GL16(&p)); + fprint(2, "uid=%ud ", GL16(&p)); + fprint(2, "mid=%ud\n", GL16(&p)); + + if(cmd == 0x32 && (flg & 0x80) == 0){ /* TRANS 2, TX */ + fprint(2, "words=%ud ", G8(&p)); + fprint(2, "totparams=%ud ", GL16(&p)); + fprint(2, "totdata=%ud ", GL16(&p)); + fprint(2, "maxparam=%ud ", GL16(&p)); + fprint(2, "maxdata=%ud\n", GL16(&p)); + fprint(2, "maxsetup=%ud ", G8(&p)); + fprint(2, "reserved=%ud ", G8(&p)); + fprint(2, "flags=%ud ", GL16(&p)); + fprint(2, "timeout=%uld\n", GL32(&p)); + fprint(2, "reserved=%ud ", GL16(&p)); + fprint(2, "paramcnt=%ud ", GL16(&p)); + fprint(2, "paramoff=%ud ", GL16(&p)); + fprint(2, "datacnt=%ud ", GL16(&p)); + fprint(2, "dataoff=%ud ", GL16(&p)); + fprint(2, "setupcnt=%ud ", G8(&p)); + fprint(2, "reserved=%ud\n", G8(&p)); + fprint(2, "trans2=0x%02x ", GL16(&p)); + fprint(2, "data-words=%d ", G8(&p)); + fprint(2, "padding=%d\n", G8(&p)); + } + if(cmd == 0x32 && (flg & 0x80) == 0x80){ /* TRANS 2, RX */ + fprint(2, "words=%ud ", G8(&p)); + fprint(2, "totparams=%ud ", GL16(&p)); + fprint(2, "totdata=%ud ", GL16(&p)); + fprint(2, "reserved=%ud ", GL16(&p)); + fprint(2, "paramcnt=%ud\n", GL16(&p)); + fprint(2, "paramoff=%ud ", GL16(&p)); + fprint(2, "paramdisp=%ud ", GL16(&p)); + fprint(2, "datacnt=%ud\n", GL16(&p)); + fprint(2, "dataoff=%ud ", GL16(&p)); + fprint(2, "datadisp=%ud ", GL16(&p)); + fprint(2, "setupcnt=%ud ", G8(&p)); + fprint(2, "reserved=%ud\n", G8(&p)); + } + if(err) + if(flags2 & FL2_NT_ERRCODES) + fprint(2, "err=%s\n", nterrstr(err)); + else + fprint(2, "err=%s\n", doserrstr(err)); +Raw: + fprint(2, "\n"); + for(; p < end; p++){ + if((p - (uchar *)buf) % 16 == 0) + fprint(2, "\n%06lx\t", p - (uchar *)buf); + if(isprint((char)*p)) + fprint(2, "%c ", (char )*p); + else + fprint(2, "%02ux ", *p); + } + fprint(2, "\n"); +} diff --git a/sys/src/cmd/cifs/nterrstr.c b/sys/src/cmd/cifs/nterrstr.c new file mode 100755 index 000000000..8ec9c8ba1 --- /dev/null +++ b/sys/src/cmd/cifs/nterrstr.c @@ -0,0 +1,1019 @@ +#include +#include + +/* + * some lines commented 4APE have been changed to + * make them the same as plan9 error messages. This is not + * a problem for native programs but those built on APE + * will give unhelpful errors if this is not done + */ + +static struct { + char *msg; + int err; +} NTerrs[] = { + { "success", 0x0 }, + { "wait 1", 0x1 }, + { "wait 2", 0x2 }, + { "wait 3", 0x3 }, + { "wait 63", 0x3f }, + { "abandoned", 0x80 }, + { "abandoned wait 63", 0xbf }, + { "user apc", 0xc0 }, + { "kernel apc", 0x100 }, + { "alerted", 0x101 }, + { "timeout", 0x102 }, + { "pending", 0x103 }, + { "reparse", 0x104 }, + { "more entries", 0x105 }, + { "not all assigned", 0x106 }, + { "some not mapped", 0x107 }, + { "oplock break in progress", 0x108 }, + { "volume mounted", 0x109 }, + { "rxact committed", 0x10a }, + { "notify cleanup", 0x10b }, + { "notify enum dir", 0x10c }, + { "no quotas for account", 0x10d }, + { "primary transport connect failed", 0x10e }, + { "page fault transition", 0x110 }, + { "page fault demand zero", 0x111 }, + { "page fault copy on write", 0x112 }, + { "page fault guard page", 0x113 }, + { "page fault paging file", 0x114 }, + { "cache page locked", 0x115 }, + { "crash dump", 0x116 }, + { "buffer all zeros", 0x117 }, + { "reparse object", 0x118 }, + { "resource requirements changed", 0x119 }, + { "translation complete", 0x120 }, + { "ds membership evaluated locally", 0x121 }, + { "nothing to terminate", 0x122 }, + { "process not in job", 0x123 }, + { "process in job", 0x124 }, + { "wait for oplock", 0x367 }, + { "object name exists", 0x40000000 }, + { "thread was suspended", 0x40000001 }, + { "working set limit range", 0x40000002 }, + { "image not at base", 0x40000003 }, + { "rxact state created", 0x40000004 }, + { "segment notification", 0x40000005 }, + { "local user session key", 0x40000006 }, + { "bad current directory", 0x40000007 }, + { "serial more writes", 0x40000008 }, + { "registry recovered", 0x40000009 }, + { "ft read recovery from backup", 0x4000000a }, + { "ft write recovery", 0x4000000b }, + { "serial counter timeout", 0x4000000c }, + { "null LM password", 0x4000000d }, + { "image machine type mismatch", 0x4000000e }, + { "receive partial", 0x4000000f }, + { "receive expedited", 0x40000010 }, + { "receive partial expedited", 0x40000011 }, + { "event done", 0x40000012 }, + { "event pending", 0x40000013 }, + { "checking file system", 0x40000014 }, + { "fatal app exit", 0x40000015 }, + { "predefined handle", 0x40000016 }, + { "was unlocked", 0x40000017 }, + { "service notification", 0x40000018 }, + { "was locked", 0x40000019 }, + { "log hard error", 0x4000001a }, + { "already win32", 0x4000001b }, + { "wx86 unsimulate", 0x4000001c }, + { "wx86 continue", 0x4000001d }, + { "wx86 single step", 0x4000001e }, + { "wx86 breakpoint", 0x4000001f }, + { "wx86 exception continue", 0x40000020 }, + { "wx86 exception lastchance", 0x40000021 }, + { "wx86 exception chain", 0x40000022 }, + { "image machine type mismatch exe", 0x40000023 }, + { "no yield performed", 0x40000024 }, + { "timer resume ignored", 0x40000025 }, + { "arbitration unhandled", 0x40000026 }, + { "cardbus not supported", 0x40000027 }, + { "wx86 createwx86tib", 0x40000028 }, + { "MP processor mismatch", 0x40000029 }, + { "hibernated", 0x4000002a }, + { "resume hibernation", 0x4000002b }, + { "wake system", 0x40000294 }, + { "ds shutting down", 0x40000370 }, + { "CTX cdm connect", 0x400a0004 }, + { "CTX cdm disconnect", 0x400a0005 }, + { "SXS release activation context", 0x4015000d }, + { "guard page violation", 0x80000001 }, + { "datatype misalignment", 0x80000002 }, + { "breakpoint", 0x80000003 }, + { "single step", 0x80000004 }, + { "buffer overflow", 0x80000005 }, + { "no more files", 0x80000006 }, + { "wake system debugger", 0x80000007 }, + { "handles closed", 0x8000000a }, + { "no inheritance", 0x8000000b }, + { "GUID substitution made", 0x8000000c }, + { "partial copy", 0x8000000d }, + { "device paper empty", 0x8000000e }, + { "device powered off", 0x8000000f }, + { "device off line", 0x80000010 }, + { "device busy", 0x80000011 }, + { "no more EAs", 0x80000012 }, + { "invalid EA name", 0x80000013 }, + { "ea list inconsistent", 0x80000014 }, + { "invalid ea flag", 0x80000015 }, + { "verify required", 0x80000016 }, + { "extraneous information", 0x80000017 }, + { "rxact commit necessary", 0x80000018 }, + { "no more entries", 0x8000001a }, + { "filemark detected", 0x8000001b }, + { "media changed", 0x8000001c }, + { "bus reset", 0x8000001d }, + { "end of media", 0x8000001e }, + { "beginning of media", 0x8000001f }, + { "media check", 0x80000020 }, + { "setmark detected", 0x80000021 }, + { "no data detected", 0x80000022 }, + { "redirector has open handles", 0x80000023 }, + { "server has open handles", 0x80000024 }, + { "already disconnected", 0x80000025 }, + { "longjump", 0x80000026 }, + { "cleaner cartridge installed", 0x80000027 }, + { "plugplay query vetoed", 0x80000028 }, + { "unwind consolidate", 0x80000029 }, + { "device requires cleaning", 0x80000288 }, + { "device door open", 0x80000289 }, + { "cluster node already up", 0x80130001 }, + { "cluster node already down", 0x80130002 }, + { "cluster network already online", 0x80130003 }, + { "cluster network already offline", 0x80130004 }, + { "cluster node already member", 0x80130005 }, + { "unsuccessful", 0xc0000001 }, + { "not implemented", 0xc0000002 }, + { "invalid info class", 0xc0000003 }, + { "info length mismatch", 0xc0000004 }, + { "access violation", 0xc0000005 }, + { "in page error", 0xc0000006 }, + { "pagefile quota", 0xc0000007 }, + { "invalid handle", 0xc0000008 }, + { "bad initial stack", 0xc0000009 }, + { "bad initial PC", 0xc000000a }, + { "invalid CID", 0xc000000b }, + { "timer not canceled", 0xc000000c }, + { "invalid parameter", 0xc000000d }, + { "no such device", 0xc000000e }, + { "no such file", 0xc000000f }, + { "invalid device request", 0xc0000010 }, + { "end of file", 0xc0000011 }, + { "wrong volume", 0xc0000012 }, + { "no media in device", 0xc0000013 }, + { "unrecognized media", 0xc0000014 }, + { "nonexistent sector", 0xc0000015 }, + { "more processing required", 0xc0000016 }, + { "no memory", 0xc0000017 }, + { "conflicting addresses", 0xc0000018 }, + { "not mapped view", 0xc0000019 }, + { "unable to free VM", 0xc000001a }, + { "unable to delete section", 0xc000001b }, + { "invalid system service", 0xc000001c }, + { "illegal instruction", 0xc000001d }, + { "invalid lock sequence", 0xc000001e }, + { "invalid view size", 0xc000001f }, + { "invalid file for section", 0xc0000020 }, + { "already committed", 0xc0000021 }, + { "permission denied", 0xc0000022 }, +//4APE { "access denied", 0xc0000022 }, + { "buffer too small", 0xc0000023 }, + { "object type mismatch", 0xc0000024 }, + { "noncontinuable exception", 0xc0000025 }, + { "invalid disposition", 0xc0000026 }, + { "unwind", 0xc0000027 }, + { "bad stack", 0xc0000028 }, + { "invalid unwind target", 0xc0000029 }, + { "not locked", 0xc000002a }, + { "parity error", 0xc000002b }, + { "unable to decommit VM", 0xc000002c }, + { "not committed", 0xc000002d }, + { "invalid port attributes", 0xc000002e }, + { "port message too long", 0xc000002f }, + { "invalid parameter mix", 0xc0000030 }, + { "invalid quota lower", 0xc0000031 }, + { "disk corrupt error", 0xc0000032 }, + { "file name syntax", 0xc0000033 }, +//4APE { "object name invalid", 0xc0000033 }, + { "does not exist", 0xc0000034 }, +//4APE { "object name not found", 0xc0000034 }, + { "create -- file exists", 0xc0000035 }, +//4APE { "object name collision", 0xc0000035 }, + { "port disconnected", 0xc0000037 }, + { "device already attached", 0xc0000038 }, + { "does not exist", 0xc0000039 }, +//4APE { "object path invalid", 0xc0000039 }, + { "does not exist", 0xc000003a }, +//4APE { "object path not found", 0xc000003a }, + { "file name syntax", 0xc000003b }, +//4APE { "object path syntax bad", 0xc000003b }, + { "data overrun", 0xc000003c }, + { "data late error", 0xc000003d }, + { "data error", 0xc000003e }, + { "crc error", 0xc000003f }, + { "section too big", 0xc0000040 }, + { "port connection refused", 0xc0000041 }, + { "invalid port handle", 0xc0000042 }, + { "sharing violation", 0xc0000043 }, + { "quota exceeded", 0xc0000044 }, + { "invalid page protection", 0xc0000045 }, + { "mutant not owned", 0xc0000046 }, + { "semaphore limit exceeded", 0xc0000047 }, + { "port already set", 0xc0000048 }, + { "section not image", 0xc0000049 }, + { "suspend count exceeded", 0xc000004a }, + { "thread is terminating", 0xc000004b }, + { "bad working set limit", 0xc000004c }, + { "incompatible file map", 0xc000004d }, + { "section protection", 0xc000004e }, + { "EAs not supported", 0xc000004f }, + { "EA too large", 0xc0000050 }, + { "nonexistent ea entry", 0xc0000051 }, + { "no EAs on file", 0xc0000052 }, + { "EA corrupt error", 0xc0000053 }, + { "file lock conflict", 0xc0000054 }, + { "lock not granted", 0xc0000055 }, + { "delete pending", 0xc0000056 }, + { "ctl file not supported", 0xc0000057 }, + { "unknown revision", 0xc0000058 }, + { "revision mismatch", 0xc0000059 }, + { "invalid owner", 0xc000005a }, + { "invalid primary group", 0xc000005b }, + { "no impersonation token", 0xc000005c }, + { "cant disable mandatory", 0xc000005d }, + { "no logon servers", 0xc000005e }, + { "no such logon session", 0xc000005f }, + { "no such privilege", 0xc0000060 }, + { "privilege not held", 0xc0000061 }, + { "invalid account name", 0xc0000062 }, + { "user exists", 0xc0000063 }, + { "no such user", 0xc0000064 }, + { "group exists", 0xc0000065 }, + { "no such group", 0xc0000066 }, + { "member in group", 0xc0000067 }, + { "member not in group", 0xc0000068 }, + { "last admin", 0xc0000069 }, + { "wrong password", 0xc000006a }, + { "ill-formed password", 0xc000006b }, + { "password restriction", 0xc000006c }, + { "logon failure", 0xc000006d }, + { "account restriction", 0xc000006e }, + { "invalid logon hours", 0xc000006f }, + { "invalid workstation", 0xc0000070 }, + { "password expired", 0xc0000071 }, + { "account disabled", 0xc0000072 }, + { "none mapped", 0xc0000073 }, + { "too many luids requested", 0xc0000074 }, + { "luids exhausted", 0xc0000075 }, + { "invalid sub authority", 0xc0000076 }, + { "invalid ACL", 0xc0000077 }, + { "invalid SID", 0xc0000078 }, + { "invalid security descr", 0xc0000079 }, + { "procedure not found", 0xc000007a }, + { "invalid image format", 0xc000007b }, + { "no token", 0xc000007c }, + { "bad inheritance ACL", 0xc000007d }, + { "range not locked", 0xc000007e }, + { "disk full", 0xc000007f }, + { "server disabled", 0xc0000080 }, + { "server not disabled", 0xc0000081 }, + { "too many guids requested", 0xc0000082 }, + { "guids exhausted", 0xc0000083 }, + { "invalid id authority", 0xc0000084 }, + { "agents exhausted", 0xc0000085 }, + { "invalid volume label", 0xc0000086 }, + { "section not extended", 0xc0000087 }, + { "not mapped data", 0xc0000088 }, + { "resource data not found", 0xc0000089 }, + { "resource type not found", 0xc000008a }, + { "resource name not found", 0xc000008b }, + { "array bounds exceeded", 0xc000008c }, + { "float denormal operand", 0xc000008d }, + { "float divide by zero", 0xc000008e }, + { "float inexact result", 0xc000008f }, + { "float invalid operation", 0xc0000090 }, + { "float overflow", 0xc0000091 }, + { "float stack check", 0xc0000092 }, + { "float underflow", 0xc0000093 }, + { "integer divide by zero", 0xc0000094 }, + { "integer overflow", 0xc0000095 }, + { "privileged instruction", 0xc0000096 }, + { "too many paging files", 0xc0000097 }, + { "file invalid", 0xc0000098 }, + { "allotted space exceeded", 0xc0000099 }, + { "insufficient resources", 0xc000009a }, + { "dfs exit path found", 0xc000009b }, + { "device data error", 0xc000009c }, + { "device not connected", 0xc000009d }, + { "device power failure", 0xc000009e }, + { "free VM not at base", 0xc000009f }, + { "memory not allocated", 0xc00000a0 }, + { "working set quota", 0xc00000a1 }, + { "media write protected", 0xc00000a2 }, + { "device not ready", 0xc00000a3 }, + { "invalid group attributes", 0xc00000a4 }, + { "bad impersonation level", 0xc00000a5 }, + { "cant open anonymous", 0xc00000a6 }, + { "bad validation class", 0xc00000a7 }, + { "bad token type", 0xc00000a8 }, + { "bad master boot record", 0xc00000a9 }, + { "instruction misalignment", 0xc00000aa }, + { "instance not available", 0xc00000ab }, + { "pipe not available", 0xc00000ac }, + { "invalid pipe state", 0xc00000ad }, + { "pipe busy", 0xc00000ae }, + { "illegal function", 0xc00000af }, + { "pipe disconnected", 0xc00000b0 }, + { "pipe closing", 0xc00000b1 }, + { "pipe connected", 0xc00000b2 }, + { "pipe listening", 0xc00000b3 }, + { "invalid read mode", 0xc00000b4 }, + { "IO timeout", 0xc00000b5 }, + { "file forced closed", 0xc00000b6 }, + { "profiling not started", 0xc00000b7 }, + { "profiling not stopped", 0xc00000b8 }, + { "could not interpret", 0xc00000b9 }, + { "file is a directory", 0xc00000ba }, + { "not supported", 0xc00000bb }, + { "remote not listening", 0xc00000bc }, + { "duplicate name", 0xc00000bd }, + { "bad network path", 0xc00000be }, + { "network busy", 0xc00000bf }, + { "device does not exist", 0xc00000c0 }, + { "too many commands", 0xc00000c1 }, + { "adapter hardware error", 0xc00000c2 }, + { "invalid network response", 0xc00000c3 }, + { "unexpected network error", 0xc00000c4 }, + { "bad remote adapter", 0xc00000c5 }, + { "print queue full", 0xc00000c6 }, + { "no spool space", 0xc00000c7 }, + { "print cancelled", 0xc00000c8 }, + { "network name deleted", 0xc00000c9 }, + { "network access denied", 0xc00000ca }, + { "bad device type", 0xc00000cb }, + { "bad network name", 0xc00000cc }, + { "too many names", 0xc00000cd }, + { "too many sessions", 0xc00000ce }, + { "sharing paused", 0xc00000cf }, + { "request not accepted", 0xc00000d0 }, + { "redirector paused", 0xc00000d1 }, + { "net write fault", 0xc00000d2 }, + { "profiling at limit", 0xc00000d3 }, + { "not same device", 0xc00000d4 }, + { "file renamed", 0xc00000d5 }, + { "virtual circuit closed", 0xc00000d6 }, + { "no security on object", 0xc00000d7 }, + { "cant wait", 0xc00000d8 }, + { "pipe empty", 0xc00000d9 }, + { "cant access domain info", 0xc00000da }, + { "cant terminate self", 0xc00000db }, + { "invalid server state", 0xc00000dc }, + { "invalid domain state", 0xc00000dd }, + { "invalid domain role", 0xc00000de }, + { "no such domain", 0xc00000df }, + { "domain exists", 0xc00000e0 }, + { "domain limit exceeded", 0xc00000e1 }, + { "oplock not granted", 0xc00000e2 }, + { "invalid oplock protocol", 0xc00000e3 }, + { "internal DB corruption", 0xc00000e4 }, + { "internal error", 0xc00000e5 }, + { "generic not mapped", 0xc00000e6 }, + { "bad descriptor format", 0xc00000e7 }, + { "invalid user buffer", 0xc00000e8 }, + { "unexpected io error", 0xc00000e9 }, + { "unexpected MM create err", 0xc00000ea }, + { "unexpected MM map error", 0xc00000eb }, + { "unexpected MM extend err", 0xc00000ec }, + { "not logon process", 0xc00000ed }, + { "logon session exists", 0xc00000ee }, + { "invalid parameter 1", 0xc00000ef }, + { "invalid parameter 2", 0xc00000f0 }, + { "invalid parameter 3", 0xc00000f1 }, + { "invalid parameter 4", 0xc00000f2 }, + { "invalid parameter 5", 0xc00000f3 }, + { "invalid parameter 6", 0xc00000f4 }, + { "invalid parameter 7", 0xc00000f5 }, + { "invalid parameter 8", 0xc00000f6 }, + { "invalid parameter 9", 0xc00000f7 }, + { "invalid parameter 10", 0xc00000f8 }, + { "invalid parameter 11", 0xc00000f9 }, + { "invalid parameter 12", 0xc00000fa }, + { "redirector not started", 0xc00000fb }, + { "redirector started", 0xc00000fc }, + { "stack overflow", 0xc00000fd }, + { "no such package", 0xc00000fe }, + { "bad function table", 0xc00000ff }, + { "variable not found", 0xc0000100 }, + { "directory not empty", 0xc0000101 }, + { "file corrupt error", 0xc0000102 }, + { "not a directory", 0xc0000103 }, + { "bad logon session state", 0xc0000104 }, + { "logon session collision", 0xc0000105 }, + { "name too long", 0xc0000106 }, + { "files open", 0xc0000107 }, + { "connection in use", 0xc0000108 }, + { "message not found", 0xc0000109 }, + { "process is terminating", 0xc000010a }, + { "invalid logon type", 0xc000010b }, + { "no guid translation", 0xc000010c }, + { "cannot impersonate", 0xc000010d }, + { "image already loaded", 0xc000010e }, + { "abios not present", 0xc000010f }, + { "abios lid not exist", 0xc0000110 }, + { "abios lid already owned", 0xc0000111 }, + { "abios not lid owner", 0xc0000112 }, + { "abios invalid command", 0xc0000113 }, + { "abios invalid lid", 0xc0000114 }, + { "abios selector not available", 0xc0000115 }, + { "abios invalid selector", 0xc0000116 }, + { "no LDT", 0xc0000117 }, + { "invalid LDT size", 0xc0000118 }, + { "invalid LDT offset", 0xc0000119 }, + { "invalid LDT descriptor", 0xc000011a }, + { "invalid image NE format", 0xc000011b }, + { "rxact invalid state", 0xc000011c }, + { "rxact commit failure", 0xc000011d }, + { "mapped file size zero", 0xc000011e }, + { "too many opened files", 0xc000011f }, + { "cancelled", 0xc0000120 }, + { "permission denied", 0xc0000121 }, +// { "cannot delete", 0xc0000121 }, + { "invalid computer name", 0xc0000122 }, + { "file deleted", 0xc0000123 }, + { "special account", 0xc0000124 }, + { "special group", 0xc0000125 }, + { "special user", 0xc0000126 }, + { "members primary group", 0xc0000127 }, + { "file closed", 0xc0000128 }, + { "too many threads", 0xc0000129 }, + { "thread not in process", 0xc000012a }, + { "token already in use", 0xc000012b }, + { "pagefile quota exceeded", 0xc000012c }, + { "commitment limit", 0xc000012d }, + { "invalid image le format", 0xc000012e }, + { "invalid image not MZ", 0xc000012f }, + { "invalid image protect", 0xc0000130 }, + { "invalid image win 16", 0xc0000131 }, + { "logon server conflict", 0xc0000132 }, + { "time difference at DC", 0xc0000133 }, + { "synchronization required", 0xc0000134 }, + { "DLL not found", 0xc0000135 }, + { "open failed", 0xc0000136 }, + { "IO privilege failed", 0xc0000137 }, + { "ordinal not found", 0xc0000138 }, + { "entrypoint not found", 0xc0000139 }, + { "control-C exit", 0xc000013a }, + { "local disconnect", 0xc000013b }, + { "remote disconnect", 0xc000013c }, + { "remote resources", 0xc000013d }, + { "link failed", 0xc000013e }, + { "link timeout", 0xc000013f }, + { "invalid connection", 0xc0000140 }, + { "invalid address", 0xc0000141 }, + { "DLL init failed", 0xc0000142 }, + { "missing systemfile", 0xc0000143 }, + { "unhandled exception", 0xc0000144 }, + { "application init failure", 0xc0000145 }, + { "pagefile create failed", 0xc0000146 }, + { "no pagefile", 0xc0000147 }, + { "invalid level", 0xc0000148 }, + { "wrong password core", 0xc0000149 }, + { "illegal float context", 0xc000014a }, + { "pipe broken", 0xc000014b }, + { "registry corrupt", 0xc000014c }, + { "registry io failed", 0xc000014d }, + { "no event pair", 0xc000014e }, + { "unrecognized volume", 0xc000014f }, + { "serial no device inited", 0xc0000150 }, + { "no such alias", 0xc0000151 }, + { "member not in alias", 0xc0000152 }, + { "member in alias", 0xc0000153 }, + { "alias exists", 0xc0000154 }, + { "logon not granted", 0xc0000155 }, + { "too many secrets", 0xc0000156 }, + { "secret too long", 0xc0000157 }, + { "internal db error", 0xc0000158 }, + { "fullscreen mode", 0xc0000159 }, + { "too many context IDs", 0xc000015a }, + { "logon type not granted", 0xc000015b }, + { "not registry file", 0xc000015c }, + { "NT cross encryption required", 0xc000015d }, + { "domain ctrlr config error", 0xc000015e }, + { "ft missing member", 0xc000015f }, + { "ill formed service entry", 0xc0000160 }, + { "illegal character", 0xc0000161 }, + { "unmappable character", 0xc0000162 }, + { "undefined character", 0xc0000163 }, + { "floppy volume", 0xc0000164 }, + { "floppy id mark not found", 0xc0000165 }, + { "floppy wrong cylinder", 0xc0000166 }, + { "floppy unknown error", 0xc0000167 }, + { "floppy bad registers", 0xc0000168 }, + { "disk recalibrate failed", 0xc0000169 }, + { "disk operation failed", 0xc000016a }, + { "disk reset failed", 0xc000016b }, + { "shared IRQ busy", 0xc000016c }, + { "FT orphaning", 0xc000016d }, + { "BIOS failed to connect interrupt", 0xc000016e }, + { "partition failure", 0xc0000172 }, + { "invalid block length", 0xc0000173 }, + { "device not partitioned", 0xc0000174 }, + { "unable to lock media", 0xc0000175 }, + { "unable to unload media", 0xc0000176 }, + { "eom overflow", 0xc0000177 }, + { "no media", 0xc0000178 }, + { "no such member", 0xc000017a }, + { "invalid member", 0xc000017b }, + { "key deleted", 0xc000017c }, + { "no log space", 0xc000017d }, + { "too many SIDs", 0xc000017e }, + { "LM cross encryption required", 0xc000017f }, + { "key has children", 0xc0000180 }, + { "child must be volatile", 0xc0000181 }, + { "device configuration error", 0xc0000182 }, + { "driver internal error", 0xc0000183 }, + { "invalid device state", 0xc0000184 }, + { "io device error", 0xc0000185 }, + { "device protocol error", 0xc0000186 }, + { "backup controller", 0xc0000187 }, + { "log file full", 0xc0000188 }, + { "too late", 0xc0000189 }, + { "no trust LSA secret", 0xc000018a }, + { "no trust SAM account", 0xc000018b }, + { "trusted domain failure", 0xc000018c }, + { "trusted relationship failure", 0xc000018d }, + { "eventlog file corrupt", 0xc000018e }, + { "eventlog cant start", 0xc000018f }, + { "trust failure", 0xc0000190 }, + { "mutant limit exceeded", 0xc0000191 }, + { "netlogon not started", 0xc0000192 }, + { "account expired", 0xc0000193 }, + { "possible deadlock", 0xc0000194 }, + { "network credential conflict", 0xc0000195 }, + { "remote session limit", 0xc0000196 }, + { "eventlog file changed", 0xc0000197 }, + { "nologon interdomain trust account", 0xc0000198 }, + { "nologon workstation trust account", 0xc0000199 }, + { "nologon server trust account", 0xc000019a }, + { "domain trust inconsistent", 0xc000019b }, + { "fs driver required", 0xc000019c }, + { "no user session key", 0xc0000202 }, + { "user session deleted", 0xc0000203 }, + { "resource lang not found", 0xc0000204 }, + { "insuff server resources", 0xc0000205 }, + { "invalid buffer size", 0xc0000206 }, + { "invalid address component", 0xc0000207 }, + { "invalid address wildcard", 0xc0000208 }, + { "too many addresses", 0xc0000209 }, + { "address already exists", 0xc000020a }, + { "address closed", 0xc000020b }, + { "connection disconnected", 0xc000020c }, + { "connection reset", 0xc000020d }, + { "too many nodes", 0xc000020e }, + { "transaction aborted", 0xc000020f }, + { "transaction timed out", 0xc0000210 }, + { "transaction no release", 0xc0000211 }, + { "transaction no match", 0xc0000212 }, + { "transaction responded", 0xc0000213 }, + { "transaction invalid id", 0xc0000214 }, + { "transaction invalid type", 0xc0000215 }, + { "not server session", 0xc0000216 }, + { "not client session", 0xc0000217 }, + { "cannot load registry file", 0xc0000218 }, + { "debug attach failed", 0xc0000219 }, + { "system process terminated", 0xc000021a }, + { "data not accepted", 0xc000021b }, + { "no browser servers found", 0xc000021c }, + { "VDM hard error", 0xc000021d }, + { "driver cancel timeout", 0xc000021e }, + { "reply message mismatch", 0xc000021f }, + { "mapped alignment", 0xc0000220 }, + { "image checksum mismatch", 0xc0000221 }, + { "lost writebehind data", 0xc0000222 }, + { "client server parameters invalid", 0xc0000223 }, + { "password must change", 0xc0000224 }, + { "not found", 0xc0000225 }, + { "not tiny stream", 0xc0000226 }, + { "recovery failure", 0xc0000227 }, + { "stack overflow read", 0xc0000228 }, + { "fail check", 0xc0000229 }, + { "duplicate objectid", 0xc000022a }, + { "objectid exists", 0xc000022b }, + { "convert to large", 0xc000022c }, + { "retry", 0xc000022d }, + { "found out of scope", 0xc000022e }, + { "allocate bucket", 0xc000022f }, + { "propset not found", 0xc0000230 }, + { "marshall overflow", 0xc0000231 }, + { "invalid variant", 0xc0000232 }, + { "domain controller not found", 0xc0000233 }, + { "account locked out", 0xc0000234 }, + { "handle not closable", 0xc0000235 }, + { "connection refused", 0xc0000236 }, + { "graceful disconnect", 0xc0000237 }, + { "address already associated", 0xc0000238 }, + { "address not associated", 0xc0000239 }, + { "connection invalid", 0xc000023a }, + { "connection active", 0xc000023b }, + { "network unreachable", 0xc000023c }, + { "host unreachable", 0xc000023d }, + { "protocol unreachable", 0xc000023e }, + { "port unreachable", 0xc000023f }, + { "request aborted", 0xc0000240 }, + { "connection aborted", 0xc0000241 }, + { "bad compression buffer", 0xc0000242 }, + { "user mapped file", 0xc0000243 }, + { "audit failed", 0xc0000244 }, + { "timer resolution not set", 0xc0000245 }, + { "connection count limit", 0xc0000246 }, + { "login time restriction", 0xc0000247 }, + { "login wkstation restriction", 0xc0000248 }, + { "image mp up mismatch", 0xc0000249 }, + { "insufficient logon info", 0xc0000250 }, + { "bad DLL entrypoint", 0xc0000251 }, + { "bad service entrypoint", 0xc0000252 }, + { "lpc reply lost", 0xc0000253 }, + { "IP address conflict1", 0xc0000254 }, + { "IP address conflict2", 0xc0000255 }, + { "registry quota limit", 0xc0000256 }, + { "path not covered", 0xc0000257 }, + { "no callback active", 0xc0000258 }, + { "license quota exceeded", 0xc0000259 }, + { "password too short", 0xc000025a }, + { "password too recent", 0xc000025b }, + { "password history conflict", 0xc000025c }, + { "plugplay no device", 0xc000025e }, + { "unsupported compression", 0xc000025f }, + { "invalid hw profile", 0xc0000260 }, + { "invalid plugplay device path", 0xc0000261 }, + { "driver ordinal not found", 0xc0000262 }, + { "driver entrypoint not found", 0xc0000263 }, + { "resource not owned", 0xc0000264 }, + { "too many links", 0xc0000265 }, + { "quota list inconsistent", 0xc0000266 }, + { "file is offline", 0xc0000267 }, + { "evaluation expiration", 0xc0000268 }, + { "illegal DLL relocation", 0xc0000269 }, + { "license violation", 0xc000026a }, + { "DLL init failed logoff", 0xc000026b }, + { "driver unable to load", 0xc000026c }, + { "dfs unavailable", 0xc000026d }, + { "volume dismounted", 0xc000026e }, + { "wx86 internal error", 0xc000026f }, + { "wx86 float stack check", 0xc0000270 }, + { "validate continue", 0xc0000271 }, + { "no match", 0xc0000272 }, + { "no more matches", 0xc0000273 }, + { "not a reparse point", 0xc0000275 }, + { "IO reparse tag invalid", 0xc0000276 }, + { "IO reparse tag mismatch", 0xc0000277 }, + { "IO reparse data invalid", 0xc0000278 }, + { "IO reparse tag not handled", 0xc0000279 }, + { "reparse point not resolved", 0xc0000280 }, + { "directory is a reparse point", 0xc0000281 }, + { "range list conflict", 0xc0000282 }, + { "source element empty", 0xc0000283 }, + { "destination element full", 0xc0000284 }, + { "illegal element address", 0xc0000285 }, + { "magazine not present", 0xc0000286 }, + { "reinitialization needed", 0xc0000287 }, + { "encryption failed", 0xc000028a }, + { "decryption failed", 0xc000028b }, + { "range not found", 0xc000028c }, + { "no recovery policy", 0xc000028d }, + { "no EFS", 0xc000028e }, + { "wrong EFS", 0xc000028f }, + { "no user keys", 0xc0000290 }, + { "file not encrypted", 0xc0000291 }, + { "not export format", 0xc0000292 }, + { "file encrypted", 0xc0000293 }, + { "WMI guid not found", 0xc0000295 }, + { "WMI instance not found", 0xc0000296 }, + { "WMI itemid not found", 0xc0000297 }, + { "WMI try again", 0xc0000298 }, + { "shared policy", 0xc0000299 }, + { "policy object not found", 0xc000029a }, + { "policy only in DS", 0xc000029b }, + { "volume not upgraded", 0xc000029c }, + { "remote storage not active", 0xc000029d }, + { "remote storage media error", 0xc000029e }, + { "no tracking service", 0xc000029f }, + { "server SID mismatch", 0xc00002a0 }, + { "DS no attribute or value", 0xc00002a1 }, + { "DS invalid attribute syntax", 0xc00002a2 }, + { "DS attribute type undefined", 0xc00002a3 }, + { "DS attribute or value exists", 0xc00002a4 }, + { "DS busy", 0xc00002a5 }, + { "DS unavailable", 0xc00002a6 }, + { "DS no RIDs allocated", 0xc00002a7 }, + { "DS no more RIDs", 0xc00002a8 }, + { "DS incorrect role owner", 0xc00002a9 }, + { "DS ridmgr init error", 0xc00002aa }, + { "DS obj class violation", 0xc00002ab }, + { "DS cant on non leaf", 0xc00002ac }, + { "DS cant on RDN", 0xc00002ad }, + { "DS cant mod obj class", 0xc00002ae }, + { "DS cross dom move failed", 0xc00002af }, + { "DS GC not available", 0xc00002b0 }, + { "directory service required", 0xc00002b1 }, + { "reparse attribute conflict", 0xc00002b2 }, + { "cant enable deny only", 0xc00002b3 }, + { "float multiple faults", 0xc00002b4 }, + { "float multiple traps", 0xc00002b5 }, + { "device removed", 0xc00002b6 }, + { "journal delete in progress", 0xc00002b7 }, + { "journal not active", 0xc00002b8 }, + { "nointerface", 0xc00002b9 }, + { "DS admin limit exceeded", 0xc00002c1 }, + { "driver failed sleep", 0xc00002c2 }, + { "mutual authentication failed", 0xc00002c3 }, + { "corrupt system file", 0xc00002c4 }, + { "datatype misalignment error", 0xc00002c5 }, + { "WMI read only", 0xc00002c6 }, + { "WMI set failure", 0xc00002c7 }, + { "commitment minimum", 0xc00002c8 }, + { "reg NAT consumption", 0xc00002c9 }, + { "transport full", 0xc00002ca }, + { "DS SAM init failure", 0xc00002cb }, + { "only if connected", 0xc00002cc }, + { "DS sensitive group violation", 0xc00002cd }, + { "PNP restart enumeration", 0xc00002ce }, + { "journal entry deleted", 0xc00002cf }, + { "DS cant mod primarygroupid", 0xc00002d0 }, + { "system image bad signature", 0xc00002d1 }, + { "PNP reboot required", 0xc00002d2 }, + { "power state invalid", 0xc00002d3 }, + { "DS invalid group type", 0xc00002d4 }, + { "DS no nest globalgroup in mixeddomain", 0xc00002d5 }, + { "DS no nest localgroup in mixeddomain", 0xc00002d6 }, + { "DS global can't have local member", 0xc00002d7 }, + { "DS global can't have universal member", 0xc00002d8 }, + { "DS universal can't have local member", 0xc00002d9 }, + { "DS global can't have crossdomain member", 0xc00002da }, + { "DS local can't have crossdomain local member",0xc00002db }, + { "DS have primary members", 0xc00002dc }, + { "WMI not supported", 0xc00002dd }, + { "insufficient power", 0xc00002de }, + { "SAM need bootkey password", 0xc00002df }, + { "SAM need bootkey floppy", 0xc00002e0 }, + { "DS cant start", 0xc00002e1 }, + { "DS init failure", 0xc00002e2 }, + { "SAM init failure", 0xc00002e3 }, + { "DS gc required", 0xc00002e4 }, + { "DS local member of local only", 0xc00002e5 }, + { "DS no FPO in universal groups", 0xc00002e6 }, + { "DS machine account quota exceeded", 0xc00002e7 }, + { "multiple fault violation", 0xc00002e8 }, + { "current domain not allowed", 0xc00002e9 }, + { "cannot make", 0xc00002ea }, + { "system shutdown", 0xc00002eb }, + { "DS init failure console", 0xc00002ec }, + { "DS sam init failure console", 0xc00002ed }, + { "unfinished context deleted", 0xc00002ee }, + { "no TGT reply", 0xc00002ef }, + { "objectid not found", 0xc00002f0 }, + { "no IP addresses", 0xc00002f1 }, + { "wrong credential handle", 0xc00002f2 }, + { "crypto system invalid", 0xc00002f3 }, + { "max referrals exceeded", 0xc00002f4 }, + { "must be kdc", 0xc00002f5 }, + { "strong crypto not supported", 0xc00002f6 }, + { "too many principals", 0xc00002f7 }, + { "no PA data", 0xc00002f8 }, + { "pkinit name mismatch", 0xc00002f9 }, + { "smartcard logon required", 0xc00002fa }, + { "KDC invalid request", 0xc00002fb }, + { "KDC unable to refer", 0xc00002fc }, + { "KDC unknown etype", 0xc00002fd }, + { "shutdown in progress", 0xc00002fe }, + { "server shutdown in progress", 0xc00002ff }, + { "not supported on sbs", 0xc0000300 }, + { "WMI GUID disconnected", 0xc0000301 }, + { "WMI already disabled", 0xc0000302 }, + { "WMI already enabled", 0xc0000303 }, + { "mft too fragmented", 0xc0000304 }, + { "copy protection failure", 0xc0000305 }, + { "CSS authentication failure", 0xc0000306 }, + { "CSS key not present", 0xc0000307 }, + { "CSS key not established", 0xc0000308 }, + { "CSS scrambled sector", 0xc0000309 }, + { "CSS region mismatch", 0xc000030a }, + { "CSS resets exhausted", 0xc000030b }, + { "pkinit failure", 0xc0000320 }, + { "smartcard subsystem failure", 0xc0000321 }, + { "no kerb key", 0xc0000322 }, + { "host down", 0xc0000350 }, + { "unsupported preauth", 0xc0000351 }, + { "EFS alg blob too big", 0xc0000352 }, + { "port not set", 0xc0000353 }, + { "debugger inactive", 0xc0000354 }, + { "ds version check failure", 0xc0000355 }, + { "auditing disabled", 0xc0000356 }, + { "prent4 machine account", 0xc0000357 }, + { "DS AG can't have universal member", 0xc0000358 }, + { "invalid image Win 32", 0xc0000359 }, + { "invalid image Win 64", 0xc000035a }, + { "bad bindings", 0xc000035b }, + { "network session expired", 0xc000035c }, + { "apphelp block", 0xc000035d }, + { "all SIDs filtered", 0xc000035e }, + { "not safe mode driver", 0xc000035f }, + { "access disabled by policy default", 0xc0000361 }, + { "access disabled by policy path", 0xc0000362 }, + { "access disabled by policy publisher", 0xc0000363 }, + { "access disabled by policy other", 0xc0000364 }, + { "failed driver entry", 0xc0000365 }, + { "device enumeration error", 0xc0000366 }, + { "mount point not resolved", 0xc0000368 }, + { "invalid device object parameter", 0xc0000369 }, + { "mca occured", 0xc000036a }, + { "driver blocked critical", 0xc000036b }, + { "driver blocked", 0xc000036c }, + { "driver database error", 0xc000036d }, + { "system hive too large", 0xc000036e }, + { "invalid import of non DLL", 0xc000036f }, + { "smartcard wrong pin", 0xc0000380 }, + { "smartcard card blocked", 0xc0000381 }, + { "smartcard card not authenticated", 0xc0000382 }, + { "smartcard no card", 0xc0000383 }, + { "smartcard no key container", 0xc0000384 }, + { "smartcard no certificate", 0xc0000385 }, + { "smartcard no keyset", 0xc0000386 }, + { "smartcard io error", 0xc0000387 }, + { "downgrade detected", 0xc0000388 }, + { "smartcard cert revoked", 0xc0000389 }, + { "issuing CA untrusted", 0xc000038a }, + { "revocation offline c", 0xc000038b }, + { "pkinit client failure", 0xc000038c }, + { "smartcard cert expired", 0xc000038d }, + { "driver failed prior unload", 0xc000038e }, + { "wow assertion", 0xc0009898 }, + { "PNP bad MPS table", 0xc0040035 }, + { "PNP translation failed", 0xc0040036 }, + { "PNP IRQ translation failed", 0xc0040037 }, + { "CTX winstation name invalid", 0xc00a0001 }, + { "CTX invalid PD", 0xc00a0002 }, + { "CTX PD not found", 0xc00a0003 }, + { "CTX close pending", 0xc00a0006 }, + { "CTX no outbuf", 0xc00a0007 }, + { "CTX modem inf not found", 0xc00a0008 }, + { "CTX invalid modemname", 0xc00a0009 }, + { "CTX response error", 0xc00a000a }, + { "CTX modem response timeout", 0xc00a000b }, + { "CTX modem response no carrier", 0xc00a000c }, + { "CTX modem response no dialtone", 0xc00a000d }, + { "CTX modem response busy", 0xc00a000e }, + { "CTX modem response voice", 0xc00a000f }, + { "CTX TD error", 0xc00a0010 }, + { "CTX license client invalid", 0xc00a0012 }, + { "CTX license not available", 0xc00a0013 }, + { "CTX license expired", 0xc00a0014 }, + { "CTX winstation not found", 0xc00a0015 }, + { "CTX winstation name collision", 0xc00a0016 }, + { "CTX winstation busy", 0xc00a0017 }, + { "CTX bad video mode", 0xc00a0018 }, + { "CTX graphics invalid", 0xc00a0022 }, + { "CTX not console", 0xc00a0024 }, + { "CTX client query timeout", 0xc00a0026 }, + { "CTX console disconnect", 0xc00a0027 }, + { "CTX console connect", 0xc00a0028 }, + { "CTX shadow denied", 0xc00a002a }, + { "CTX winstation access denied", 0xc00a002b }, + { "CTX invalid wd", 0xc00a002e }, + { "CTX WD not found", 0xc00a002f }, + { "CTX shadow invalid", 0xc00a0030 }, + { "CTX shadow disabled", 0xc00a0031 }, + { "RDP protocol error", 0xc00a0032 }, + { "CTX client license not set", 0xc00a0033 }, + { "CTX client license in use", 0xc00a0034 }, + { "CTX shadow ended by mode change", 0xc00a0035 }, + { "CTX shadow not running", 0xc00a0036 }, + { "cluster invalid node", 0xc0130001 }, + { "cluster node exists", 0xc0130002 }, + { "cluster join in progress", 0xc0130003 }, + { "cluster node not found", 0xc0130004 }, + { "cluster local node not found", 0xc0130005 }, + { "cluster network exists", 0xc0130006 }, + { "cluster network not found", 0xc0130007 }, + { "cluster netinterface exists", 0xc0130008 }, + { "cluster netinterface not found", 0xc0130009 }, + { "cluster invalid request", 0xc013000a }, + { "cluster invalid network provider", 0xc013000b }, + { "cluster node down", 0xc013000c }, + { "cluster node unreachable", 0xc013000d }, + { "cluster node not member", 0xc013000e }, + { "cluster join not in progress", 0xc013000f }, + { "cluster invalid network", 0xc0130010 }, + { "cluster no net adapters", 0xc0130011 }, + { "cluster node up", 0xc0130012 }, + { "cluster node paused", 0xc0130013 }, + { "cluster node not paused", 0xc0130014 }, + { "cluster no security context", 0xc0130015 }, + { "cluster network not internal", 0xc0130016 }, + { "cluster poisoned", 0xc0130017 }, + { "ACPI invalid opcode", 0xc0140001 }, + { "ACPI stack overflow", 0xc0140002 }, + { "ACPI assert failed", 0xc0140003 }, + { "ACPI invalid index", 0xc0140004 }, + { "ACPI invalid argument", 0xc0140005 }, + { "ACPI fatal", 0xc0140006 }, + { "ACPI invalid supername", 0xc0140007 }, + { "ACPI invalid argtype", 0xc0140008 }, + { "ACPI invalid objtype", 0xc0140009 }, + { "ACPI invalid targettype", 0xc014000a }, + { "ACPI incorrect argument count", 0xc014000b }, + { "ACPI address not mapped", 0xc014000c }, + { "ACPI invalid eventtype", 0xc014000d }, + { "ACPI handler collision", 0xc014000e }, + { "ACPI invalid data", 0xc014000f }, + { "ACPI invalid region", 0xc0140010 }, + { "ACPI invalid access size", 0xc0140011 }, + { "ACPI acquire global lock", 0xc0140012 }, + { "ACPI already initialized", 0xc0140013 }, + { "ACPI not initialized", 0xc0140014 }, + { "ACPI invalid mutex level", 0xc0140015 }, + { "ACPI mutex not owned", 0xc0140016 }, + { "ACPI mutex not owner", 0xc0140017 }, + { "ACPI rs access", 0xc0140018 }, + { "ACPI invalid table", 0xc0140019 }, + { "ACPI reg handler failed", 0xc0140020 }, + { "ACPI power request failed", 0xc0140021 }, + { "SXS section not found", 0xc0150001 }, + { "SXS cant gen actctx", 0xc0150002 }, + { "SXS invalid actctx data format", 0xc0150003 }, + { "SXS assembly not found", 0xc0150004 }, + { "SXS manifest format error", 0xc0150005 }, + { "SXS manifest parse error", 0xc0150006 }, + { "SXS activation context disabled", 0xc0150007 }, + { "SXS key not found", 0xc0150008 }, + { "SXS version conflict", 0xc0150009 }, + { "SXS wrong section type", 0xc015000a }, + { "SXS thread queries disabled", 0xc015000b }, + { "SXS assembly missing", 0xc015000c }, + { "SXS process default already set", 0xc015000e }, + { "SXS early deactivation", 0xc015000f }, + { "SXS invalid deactivation", 0xc0150010 }, + { "SXS multiple deactivation", 0xc0150011 }, + { "SXS system default activation context empty",0xc0150012 }, + { "SXS process termination requested", 0xc0150013 }, +}; + +char * +nterrstr(uint err) +{ + int i, f, match; + char *why, *facility, tmp[32]; + static char buf[0xff]; + + f = (err >> 16) & 0x7ff; + switch(f){ + case 0: + facility = ""; + break; + case 1: + facility = " (hardware), "; + break; + case 2: + facility = " (dispatch), "; + break; + case 3: + facility = " (storage), "; + break; + case 4: + facility = " (itf), "; + break; + case 7: + facility = " (win32), "; + break; + case 8: + facility = " (windows), "; + break; + case 0x0a: + facility = " (control), "; + break; + default: + snprint(tmp, sizeof(tmp), " (facility=%d), ", f); + facility = tmp; + break; + } + + match = -1; + for(i = 0; i < nelem(NTerrs); i++) + if(NTerrs[i].err == err) + match = i; + + why = ""; + if(!(err & 0x80000000)) + why = "warning, "; + + if(match != -1) + snprint(buf, sizeof buf, "%s%s%s", why, facility, + NTerrs[match].msg); + else + snprint(buf, sizeof buf, "%s%s%d/0x%ux - unknown NT error", + why, facility, err, err); + return buf; +} diff --git a/sys/src/cmd/cifs/pack.c b/sys/src/cmd/cifs/pack.c new file mode 100755 index 000000000..1f0230220 --- /dev/null +++ b/sys/src/cmd/cifs/pack.c @@ -0,0 +1,463 @@ +/* packet packing and unpacking */ +#include +#include +#include +#include "cifs.h" + +void * +pmem(Pkt *p, void *v, int len) +{ + uchar *str = v; + void *s = p->pos; + + if(!len || !v) + return s; + while(len--) + *p->pos++ = *str++; + return s; +} + +void * +ppath(Pkt *p, char *str) +{ + char c; + Rune r; + void *s = p->pos; + + if(!str) + return s; + + if(p->s->caps & CAP_UNICODE){ + if(((p->pos - p->buf) % 2) != 0) /* pad to even offset */ + p8(p, 0); + while(*str){ + str += chartorune(&r, str); + if(r == L'/') + r = L'\\'; + pl16(p, r); + } + pl16(p, 0); + } else { + while((c = *str++) != 0){ + if(c == '/') + c = '\\'; + *p->pos++ = c; + } + *p->pos++ = 0; + } + return s; +} + +void * +pstr(Pkt *p, char *str) +{ + void *s = p->pos; + Rune r; + + if(!str) + return s; + + if(p->s->caps & CAP_UNICODE){ + if(((p->pos - p->buf) % 2) != 0) + p8(p, 0); /* pad to even offset */ + while(*str){ + str += chartorune(&r, str); + pl16(p, r); + } + pl16(p, 0); + } else { + while(*str) + *p->pos++ = *str++; + *p->pos++ = 0; + } + return s; +} + +void * +pascii(Pkt *p, char *str) +{ + void *s = p->pos; + + while(*str) + *p->pos++ = *str++; + *p->pos++ = 0; + return s; +} + + +void * +pl64(Pkt *p, uvlong n) +{ + void *s = p->pos; + + *p->pos++ = n; + *p->pos++ = n >> 8; + *p->pos++ = n >> 16; + *p->pos++ = n >> 24; + *p->pos++ = n >> 32; + *p->pos++ = n >> 40; + *p->pos++ = n >> 48; + *p->pos++ = n >> 56; + return s; +} + +void * +pb32(Pkt *p, uint n) +{ + void *s = p->pos; + + *p->pos++ = n >> 24; + *p->pos++ = n >> 16; + *p->pos++ = n >> 8; + *p->pos++ = n; + return s; +} + +void * +pl32(Pkt *p, uint n) +{ + void *s = p->pos; + + *p->pos++ = n; + *p->pos++ = n >> 8; + *p->pos++ = n >> 16; + *p->pos++ = n >> 24; + return s; +} + +void * +pb16(Pkt *p, uint n) +{ + void *s = p->pos; + + *p->pos++ = n >> 8; + *p->pos++ = n; + return s; +} + +void * +pl16(Pkt *p, uint n) +{ + void *s = p->pos; + + *p->pos++ = n; + *p->pos++ = n >> 8; + return s; +} + +void * +p8(Pkt *p, uint n) +{ + void *s = p->pos; + + *p->pos++ = n; + return s; +} + +/* + * Encode a Netbios name + */ +void * +pname(Pkt *p, char *name, char pad) +{ + int i, done = 0; + char c; + void *s = p->pos; + + *p->pos++ = ' '; + for(i = 0; i < 16; i++) { + c = pad; + if(!done && name[i] == '\0') + done = 1; + if(!done) + c = islower(name[i])? toupper(name[i]): name[i]; + *p->pos++ = ((uchar)c >> 4) + 'A'; + *p->pos++ = (c & 0xf) + 'A'; + } + *p->pos++ = '\0'; + return s; +} + +void * +pvtime(Pkt *p, uvlong n) +{ + void *s = p->pos; + + n += 11644473600LL; + n *= 10000000LL; + + pl32(p, n); + pl32(p, n >> 32); + return s; +} + +void * +pdatetime(Pkt *p, long utc) +{ + void *s = p->pos; + Tm *tm = localtime(utc); + int t = tm->hour << 11 | tm->min << 5 | (tm->sec / 2); + int d = (tm->year - 80) << 9 | (tm->mon + 1) << 5 | tm->mday; + + /* + * bug in word swapping in Win95 requires this + */ + if(p->s->caps & CAP_NT_SMBS){ + pl16(p, d); + pl16(p, t); + } else{ + pl16(p, t); + pl16(p, d); + } + return s; +} + + +void +gmem(Pkt *p, void *v, int n) +{ + uchar *str = v; + + if(!n || !v) + return; + while(n-- && p->pos < p->eop) + *str++ = *p->pos++; +} + +/* + * note len is the length of the source string in + * in runes or bytes, in ASCII mode this is also the size + * of the output buffer but this is not so in Unicode mode! + */ +void +gstr(Pkt *p, char *str, int n) +{ + int i; + Rune r; + + if(!n || !str) + return; + + if(p->s->caps & CAP_UNICODE){ + i = 0; + while(*p->pos && n && p->pos < p->eop){ + r = gl16(p); + i += runetochar(str +i, &r); + n -= 2; + } + *(str + i) = 0; + + while(*p->pos && p->pos < p->eop) + gl16(p); + /* + * some versions of windows terminate a rune string + * with a single nul so we do a dangerous hack... + */ + if(p->pos[1]) + g8(p); + else + gl16(p); + } else { + while(*p->pos && n-- && p->pos < p->eop) + *str++ = *p->pos++; + *str = 0; + while(*p->pos++ && p->pos < p->eop) + continue; + } +} + +void +gascii(Pkt *p, char *str, int n) +{ + if(!n || !str) + return; + + while(*p->pos && n-- && p->pos < p->eop) + *str++ = *p->pos++; + *str = 0; + while(*p->pos++ && p->pos < p->eop) + continue; +} + + +uvlong +gl64(Pkt *p) +{ + uvlong n; + + if(p->pos + 8 > p->eop) + return 0; + + n = (uvlong)*p->pos++; + n |= (uvlong)*p->pos++ << 8; + n |= (uvlong)*p->pos++ << 16; + n |= (uvlong)*p->pos++ << 24; + n |= (uvlong)*p->pos++ << 32; + n |= (uvlong)*p->pos++ << 40; + n |= (uvlong)*p->pos++ << 48; + n |= (uvlong)*p->pos++ << 56; + return n; +} + +uvlong +gb48(Pkt *p) +{ + uvlong n; + + if(p->pos + 6 > p->eop) + return 0; + + n = (uvlong)*p->pos++ << 40; + n |= (uvlong)*p->pos++ << 24; + n |= (uvlong)*p->pos++ << 32; + n |= (uvlong)*p->pos++ << 16; + n |= (uvlong)*p->pos++ << 8; + n |= (uvlong)*p->pos++; + return n; +} + +uint +gb32(Pkt *p) +{ + uint n; + + if(p->pos + 4 > p->eop) + return 0; + + n = (uint)*p->pos++ << 24; + n |= (uint)*p->pos++ << 16; + n |= (uint)*p->pos++ << 8; + n |= (uint)*p->pos++; + return n; +} + +uint +gl32(Pkt *p) +{ + uint n; + + if(p->pos + 4 > p->eop) + return 0; + + n = (uint)*p->pos++; + n |= (uint)*p->pos++ << 8; + n |= (uint)*p->pos++ << 16; + n |= (uint)*p->pos++ << 24; + return n; +} + +uint +gb16(Pkt *p) +{ + uint n; + + if(p->pos + 2 > p->eop) + return 0; + n = (uint)*p->pos++ << 8; + n |= (uint)*p->pos++; + return n; +} + +uint +gl16(Pkt *p) +{ + uint n; + + if(p->pos + 2 > p->eop) + return 0; + n = (uint)*p->pos++; + n |= (uint)*p->pos++ << 8; + return n; +} + +uint +g8(Pkt *p) +{ + if(p->pos + 1 > p->eop) + return 0; + return (uint)*p->pos++; +} + +long +gdatetime(Pkt *p) +{ + Tm tm; + uint d, t; + + if(p->pos + 4 > p->eop) + return 0; + + /* + * bug in word swapping in Win95 requires this + */ + if(p->s->caps & CAP_NT_SMBS){ + d = gl16(p); + t = gl16(p); + }else{ + t = gl16(p); + d = gl16(p); + } + + tm.year = 80 + (d >> 9); + tm.mon = ((d >> 5) & 017) - 1; + tm.mday = d & 037; + tm.zone[0] = 0; + tm.tzoff = p->s->tz; + + tm.hour = t >> 11; + tm.min = (t >> 5) & 63; + tm.sec = (t & 31) << 1; + + return tm2sec(&tm); +} + +long +gvtime(Pkt *p) +{ + uvlong vl; + + if(p->pos + 8 > p->eop) + return 0; + + vl = (uvlong)gl32(p); + vl |= (uvlong)gl32(p) << 32; + + vl /= 10000000LL; + vl -= 11644473600LL; + return vl; +} + +void +gconv(Pkt *p, int conv, char *str, int n) +{ + int off; + uchar *pos; + + off = gl32(p) & 0xffff; + if(off == 0 || p->tdata - conv + off > p->eop){ + memset(str, 0, n); + return; + } + + pos = p->pos; + p->pos = p->tdata - conv + off; + gascii(p, str, n); + p->pos = pos; +} + +void +goff(Pkt *p, uchar *base, char *str, int n) +{ + int off; + uchar *pos; + + off = gl16(p); + if(off == 0 || base + off > p->eop){ + memset(str, 0, n); + return; + } + pos = p->pos; + p->pos = base + off; + gstr(p, str, n); + p->pos = pos; +} diff --git a/sys/src/cmd/cifs/ping.c b/sys/src/cmd/cifs/ping.c new file mode 100755 index 000000000..0aaaaa7e8 --- /dev/null +++ b/sys/src/cmd/cifs/ping.c @@ -0,0 +1,132 @@ +#include +#include +#include +#include +#include +#include <9p.h> + +extern char *Debug; + +typedef struct Pingcache Pingcache; +struct Pingcache { + Pingcache*next; + long rtt; + char *host; + long expire; +}; + +typedef struct { + uchar vihl; /* Version and header length */ + uchar tos; /* Type of service */ + uchar length[2]; /* packet length */ + uchar id[2]; /* Identification */ + uchar frag[2]; /* Fragment information */ + uchar ttl; /* Time to live */ + uchar proto; /* Protocol */ + uchar ipcksum[2]; /* Header checksum */ + uchar src[4]; /* Ip source */ + uchar dst[4]; /* Ip destination */ + uchar type; + uchar code; + uchar cksum[2]; + uchar icmpid[2]; + uchar seq[2]; + uchar data[1]; +} Icmp; + +enum { /* Packet Types */ + EchoReply = 0, + Unreachable = 3, + SrcQuench = 4, + EchoRequest = 8, + TimeExceed = 11, + Timestamp = 13, + TimestampReply = 14, + InfoRequest = 15, + InfoReply = 16, + + ICMP_IPSIZE = 20, + ICMP_HDRSIZE = 8, + + Npings = 8, + Payload = 32, + + Cachetime = 60, +}; + +static Pingcache *Cache; + +/* + * We ignore the first result as that is probably bigger + * than expected due to IP sorting out the routing to the host + */ +int +ping(char *host, int timeout) +{ + int rtt, fd, i, seq; + long now; + vlong then; + uchar buf[128]; + Icmp *ip; + Pingcache *c; + + now = time(nil); + for(c = Cache; c; c = c->next) + if(strcmp(c->host, host) == 0 && now < c->expire){ + if(Debug && strstr(Debug, "dfs") != nil) + print("\t\tping host=%s timeout=%d - cache hit\n", + host, timeout); + return c->rtt; + } + + rtt = -1; + ip = (Icmp*)buf; + + if((fd = dial(netmkaddr(host, "icmp", "1"), 0, 0, 0)) == -1) + goto fail; + + for(seq = 0; seq < Npings; seq++){ + then = nsec(); + for(i = Payload; i < sizeof buf; i++) + buf[i] = i + seq; + ip->type = EchoRequest; + ip->code = 0; + ip->seq[0] = seq; + ip->seq[1] = seq; + alarm(timeout); + if(write(fd, ip, sizeof buf) != sizeof buf || + read(fd, ip, sizeof buf) != sizeof buf) + goto fail; + alarm(0); + if(ip->type != EchoReply || ip->code != 0 || + ip->seq[0] != seq || ip->seq[1] != seq) + goto fail; + for(i = Payload; i < sizeof buf; i++) + if((uchar)buf[i] != (uchar)(i + seq)) + goto fail; + rtt = (rtt + nsec() - then) / 2; + } +fail: + if(fd != -1) + close(fd); + + if(Debug && strstr(Debug, "dfs") != nil) + print("\t\tping host=%s timeout=%d rtt=%d - failed\n", + host, timeout, rtt); + + /* + * failures get cached too + */ + for(c = Cache; c; c = c->next) + if(strcmp(c->host, host) == 0) + break; + if(c == nil){ + c = emalloc9p(sizeof(Pingcache)); + c->host = estrdup9p(host); + c->next = Cache; + Cache = c; + } + c->rtt = rtt; + c->expire = now+Cachetime; + return rtt; +} diff --git a/sys/src/cmd/cifs/raperrstr.c b/sys/src/cmd/cifs/raperrstr.c new file mode 100755 index 000000000..ed944f0aa --- /dev/null +++ b/sys/src/cmd/cifs/raperrstr.c @@ -0,0 +1,346 @@ +#include +#include + +static struct { + int err; + char *msg; +} RAPerrs[] = { + { 0, "ok" }, + { 5, "permission denied" }, + { 50, "request not supported" }, + { 65, "access denied" }, + { 86, "password invalid" }, + { 128, "not listening on called name" }, + { 129, "not listening for calling name" }, + { 130, "called name not present" }, + { 131, "called name present, but insufficient resources" }, + { 234, "more data" }, + + { 2102, "workstation driver is not installed" }, + { 2103, "server could not be located" }, + { 2104, "network cannot access a shared memory segment" }, + { 2105, "A network resource shortage occurred" }, + { 2106, "This operation is not supported on workstations" }, + { 2107, "device is not connected" }, + { 2114, "Server service is not started" }, + { 2115, "queue is empty" }, + { 2116, "device or directory does not exist" }, + { 2117, "operation is invalid on a redirected resource" }, + { 2118, "name has already been shared" }, + { 2119, "server is currently out of the requested resource" }, + { 2121, "Requested addition of items exceeds the maximum allowed" }, + { 2122, "Peer service supports only two simultaneous users" }, + { 2123, "API return buffer is too small" }, + { 2127, "remote API error occurred" }, + { 2131, "cannot open or read the configuration file" }, + { 2136, "general network error occurred" }, + { 2137, "Workstation service is corrupted" }, + { 2138, "Workstation service has not been started" }, + { 2139, "requested information is not available" }, + { 2140, "internal error" }, + { 2141, "server is not configured for transactions" }, + { 2142, "requested API is not supported on the remote server" }, + { 2143, "event name is invalid" }, + { 2144, "computer name already exists on the network" }, + { 2146, "specified component could not be found in the config info" }, + { 2147, "specified parameter could not be found in the config info" }, + { 2149, "A line in the configuration file is too long" }, + { 2150, "printer does not exist" }, + { 2151, "print job does not exist" }, + { 2152, "printer destination cannot be found" }, + { 2153, "printer destination already exists" }, + { 2154, "printer queue already exists" }, + { 2155, "no more printers can be added" }, + { 2156, "no more print jobs can be added" }, + { 2157, "no more printer destinations can be added" }, + { 2158, "printer idle and cannot accept control operations" }, + { 2159, "invalid control function in printer request" }, + { 2160, "print processor not responding" }, + { 2161, "spooler not running" }, + { 2162, "operation cannot be performed on the print destination in its current state" }, + { 2163, "operation cannot be performed on the printer queue in its current state" }, + { 2164, "operation cannot be performed on the print job in its current state" }, + { 2165, "spooler - no memory" }, + { 2166, "device driver does not exist" }, + { 2167, "data type not supported by the print processor" }, + { 2168, "print processor is not installed" }, + { 2180, "service database is locked" }, + { 2181, "service table full" }, + { 2182, "requested service already started" }, + { 2183, "service does not responding" }, + { 2184, "service not started" }, + { 2185, "service name invalid" }, + { 2186, "service is not responding to the control function" }, + { 2187, "service control is busy" }, + { 2188, "configuration file contains an invalid service program name" }, + { 2189, "service could not be controlled in its present state" }, + { 2190, "service ended abnormally" }, + { 2191, "requested pause or stop is not valid for this service" }, + { 2192, "could not find the service name in the dispatch table" }, + { 2193, "service control dispatcher pipe read failed" }, + { 2194, "thread create for the new service could not be created" }, + { 2200, "This workstation is already logged on to the local-area network" }, + { 2201, "workstation is not logged on to the local-area network" }, + { 2202, "user name or group name parameter is invalid" }, + { 2203, "password parameter is invalid" }, + { 2204, "logon processor did not add the message alias" }, + { 2205, "logon processor did not add the message alias" }, + { 2206, "logoff processor did not delete the message alias" }, + { 2207, "logoff processor did not delete the message alias" }, + { 2209, "Network logons are paused" }, + { 2210, "a centralized logon-server conflict occurred" }, + { 2211, "server is configured without a valid user path" }, + { 2212, "cannot run logon script" }, + { 2214, "logon server was not specified. computer will be logged on as STANDALONE" }, + { 2215, "logon server could not be found" }, + { 2216, "already a logon domain for this computer" }, + { 2217, "logon server could not validate the logon" }, + { 2219, "security database could not be found" }, + { 2220, "group name could not be found" }, + { 2221, "user name could not be found" }, + { 2222, "resource name could not be found" }, + { 2223, "group already exists" }, + { 2224, "user account already exists" }, + { 2225, "resource permission list already exists" }, + { 2226, "This operation is only allowed on the PDC of the domain" }, + { 2227, "security database has not been started" }, + { 2228, "There are too many names in the user accounts database" }, + { 2229, "disk I/O failure occurred" }, + { 2230, "limit of 64 entries per resource was exceeded" }, + { 2231, "Deleting a user with a session is not allowed" }, + { 2232, "parent directory could not be located" }, + { 2233, "Unable to add to the security database session cache segment" }, + { 2234, "This operation is not allowed on this special group" }, + { 2235, "This user is not cached in user accounts database session cache" }, + { 2236, "user already belongs to this group" }, + { 2237, "user does not belong to this group" }, + { 2238, "This user account is undefined" }, + { 2239, "This user account has expired" }, + { 2240, "user is not allowed to log on from this workstation" }, + { 2241, "user is not allowed to log on at this time" }, + { 2242, "password of this user has expired" }, + { 2243, "password of this user cannot change" }, + { 2244, "This password cannot be used now" }, + { 2245, "password does not meet the password policy requirements" }, + { 2246, "password of this user is too recent to change" }, + { 2247, "security database is corrupted" }, + { 2248, "No updates are necessary to this replicant network/local security database" }, + { 2249, "This replicant database is outdated; synchronization is required" }, + { 2250, "network connection could not be found" }, + { 2251, "This asg_type is invalid" }, + { 2252, "This device is currently being shared" }, + { 2270, "computer name cannot be added - name may already exist" }, + { 2271, "Messenger service is already started" }, + { 2272, "Messenger service failed to start" }, + { 2273, "message alias could not be found on the network" }, + { 2274, "This message alias has already been forwarded" }, + { 2275, "This message alias has been added but is still forwarded" }, + { 2276, "This message alias already exists locally" }, + { 2277, "maximum number of added message aliases has been exceeded" }, + { 2278, "computer name could not be deleted" }, + { 2279, "Messages cannot be forwarded back to the same workstation" }, + { 2280, "An error occurred in the domain message processor" }, + { 2281, "message was sent, but the recipient has paused the Messenger service" }, + { 2282, "message was sent but not received" }, + { 2283, "message alias is currently in use. Try again later" }, + { 2284, "Messenger service has not been started" }, + { 2285, "name is not on the local computer" }, + { 2286, "forwarded message alias could not be found on the network" }, + { 2287, "message alias table on the remote station is full" }, + { 2288, "Messages for this alias are not currently being forwarded" }, + { 2289, "broadcast message was truncated" }, + { 2294, "This is an invalid device name" }, + { 2295, "A write fault occurred" }, + { 2297, "A duplicate message alias exists on the network" }, + { 2298, "This message alias will be deleted later" }, + { 2299, "message alias was not successfully deleted from all networks" }, + { 2300, "This operation is not supported on computers with multiple networks" }, + { 2310, "This shared resource does not exist" }, + { 2311, "This device is not shared" }, + { 2312, "A session does not exist with that computer name" }, + { 2314, "There is not an open file with that identification number" }, + { 2315, "A failure occurred when executing a remote administration command" }, + { 2316, "A failure occurred when opening a remote temporary file" }, + { 2317, "Data returned from a RAP command has been truncated to 64K" }, + { 2318, "This device cannot be shared as both a spooled and a non-spooled resource" }, + { 2319, "information in the list of servers may be incorrect" }, + { 2320, "computer is not active in this domain" }, + { 2321, "share must be removed from DFS before it can be deleted" }, + { 2331, "operation is invalid for this device" }, + { 2332, "This device cannot be shared" }, + { 2333, "This device was not open" }, + { 2334, "This device name list is invalid" }, + { 2335, "queue priority is invalid" }, + { 2337, "There are no shared communication devices" }, + { 2338, "queue you specified does not exist" }, + { 2340, "This list of devices is invalid" }, + { 2341, "requested device is invalid" }, + { 2342, "This device is already in use by the spooler" }, + { 2343, "This device is already in use as a communication device" }, + { 2351, "This computer name is invalid" }, + { 2354, "string and prefix specified are too long" }, + { 2356, "This path component is invalid" }, + { 2357, "Could not determine the type of input" }, + { 2362, "buffer for types is not big enough" }, + { 2370, "Profile files cannot exceed 64K" }, + { 2371, "start offset is out of range" }, + { 2372, "system cannot delete current connections to network resources" }, + { 2373, "system was unable to parse the command line in this file" }, + { 2374, "An error occurred while loading the profile file" }, + { 2375, "Errors occurred while saving the profile file" }, + { 2377, "Log file %1 is full" }, + { 2378, "This log file has changed between reads" }, + { 2379, "Log file %1 is corrupt" }, + { 2380, "source path cannot be a directory" }, + { 2381, "source path is illegal" }, + { 2382, "destination path is illegal" }, + { 2383, "source and destination paths are on different servers" }, + { 2385, "Run server you requested is paused" }, + { 2389, "An error occurred when communicating with a Run server" }, + { 2391, "An error occurred when starting a background process" }, + { 2392, "shared resource you are connected to could not be found" }, + { 2400, "LAN adapter number is invalid" }, + { 2401, "There are open files on the connection" }, + { 2402, "Active connections still exist" }, + { 2403, "This share name or password is invalid" }, + { 2404, "device is being accessed by an active process" }, + { 2405, "drive letter is in use locally" }, + { 2430, "specified client is already registered for the specified event" }, + { 2431, "alert table is full" }, + { 2432, "An invalid or nonexistent alert name was raised" }, + { 2433, "alert recipient is invalid" }, + { 2434, "A user's session with this server has been deleted" }, + { 2440, "log file does not contain the requested record number" }, + { 2450, "user accounts database is not configured correctly" }, + { 2451, "This operation is not permitted when the Netlogon service is running" }, + { 2452, "This operation is not allowed on the last administrative account" }, + { 2453, "Could not find DC for this domain" }, + { 2454, "Could not set logon information for this user" }, + { 2455, "Netlogon service has not been started" }, + { 2456, "Unable to add to the user accounts database" }, + { 2457, "This server's clock is not synchronized with the PDC's clock" }, + { 2458, "A password mismatch has been detected" }, + { 2460, "server identification does not specify a valid server" }, + { 2461, "session identification does not specify a valid session" }, + { 2462, "connection identification does not specify a valid connection" }, + { 2463, "There is no space for another entry in the table of available servers" }, + { 2464, "server has reached the maximum number of sessions it supports" }, + { 2465, "server has reached the maximum number of connections it supports" }, + { 2466, "server cannot open more files because it has reached its maximum number" }, + { 2467, "There are no alternate servers registered on this server" }, + { 2470, "Try down-level (remote admin protocol) version of API instead" }, + { 2480, "UPS driver could not be accessed by the UPS service" }, + { 2481, "UPS service is not configured correctly" }, + { 2482, "UPS service could not access the specified Comm Port" }, + { 2483, "UPS indicated a line fail or low battery situation. Service not started" }, + { 2484, "UPS service failed to perform a system shut down" }, + { 2500, "program below returned an MS-DOS error code" }, + { 2501, "program below needs more memory" }, + { 2502, "program below called an unsupported MS-DOS function" }, + { 2503, "workstation failed to boot" }, + { 2504, "file below is corrupt" }, + { 2505, "No loader is specified in the boot-block definition file" }, + { 2506, "NetBIOS returned an error: The NCB and SMB are dumped above" }, + { 2507, "A disk I/O error occurred" }, + { 2508, "Image parameter substitution failed" }, + { 2509, "Too many image parameters cross disk sector boundaries" }, + { 2510, "image was not generated from an MS-DOS diskette formatted with /S" }, + { 2511, "Remote boot will be restarted later" }, + { 2512, "call to the Remoteboot server failed" }, + { 2513, "Cannot connect to the Remoteboot server" }, + { 2514, "Cannot open image file on the Remoteboot server" }, + { 2515, "Connecting to the Remoteboot server..." }, + { 2516, "Connecting to the Remoteboot server..." }, + { 2517, "Remote boot service was stopped" }, + { 2518, "Remote boot startup failed; check the error log" }, + { 2519, "A second connection to a Remoteboot resource is not allowed" }, + { 2550, "browser service was configured with MaintainServerList=No" }, + { 2610, "Service failed to startas no network adapters started with this service" }, + { 2611, "Service failed to start due to bad startup information in the registry" }, + { 2612, "Service failed to start because its database is absent or corrupt" }, + { 2613, "Service failed to start because RPLFILES share is absent" }, + { 2614, "Service failed to start because RPLUSER group is absent" }, + { 2615, "Cannot enumerate service records" }, + { 2616, "Workstation record information has been corrupted" }, + { 2617, "Workstation record was not found" }, + { 2618, "Workstation name is in use by some other workstation" }, + { 2619, "Profile record information has been corrupted" }, + { 2620, "Profile record was not found" }, + { 2621, "Profile name is in use by some other profile" }, + { 2622, "There are workstations using this profile" }, + { 2623, "Configuration record information has been corrupted" }, + { 2624, "Configuration record was not found" }, + { 2625, "Adapter ID record information has been corrupted" }, + { 2626, "An internal service error has occurred" }, + { 2627, "Vendor ID record information has been corrupted" }, + { 2628, "Boot block record information has been corrupted" }, + { 2629, "user account for this workstation record is missing" }, + { 2630, "RPLUSER local group could not be found" }, + { 2631, "Boot block record was not found" }, + { 2632, "Chosen profile is incompatible with this workstation" }, + { 2633, "Chosen network adapter ID is in use by some other workstation" }, + { 2634, "There are profiles using this configuration" }, + { 2635, "There are workstations, profiles, or configurations using this boot block" }, + { 2636, "Service failed to backup Remoteboot database" }, + { 2637, "Adapter record was not found" }, + { 2638, "Vendor record was not found" }, + { 2639, "Vendor name is in use by some other vendor record" }, + { 2640, "(boot name, vendor ID) is in use by some other boot block record" }, + { 2641, "Configuration name is in use by some other configuration" }, + { 2660, "internal database maintained by the Dfs service is corrupt" }, + { 2661, "One of the records in the internal Dfs database is corrupt" }, + { 2662, "There is no DFS name whose entry path matches the input Entry Path" }, + { 2663, "A root or link with the given name already exists" }, + { 2664, "server share specified is already shared in the Dfs" }, + { 2665, "indicated server share does not support the indicated DFS namespace" }, + { 2666, "operation is not valid on this portion of the namespace" }, + { 2667, "operation is not valid on this portion of the namespace" }, + { 2668, "operation is ambiguous because the link has multiple servers" }, + { 2669, "Unable to create a link" }, + { 2670, "server is not Dfs Aware" }, + { 2671, "specified rename target path is invalid" }, + { 2672, "specified DFS link is offline" }, + { 2673, "specified server is not a server for this link" }, + { 2674, "A cycle in the Dfs name was detected" }, + { 2675, "operation is not supported on a server-based Dfs" }, + { 2676, "This link is already supported by the specified server-share" }, + { 2677, "Can't remove the last server-share supporting this root or link" }, + { 2678, "operation is not supported for an Inter-DFS link" }, + { 2679, "internal state of the Dfs Service has become inconsistent" }, + { 2680, "Dfs Service has been installed on the specified server" }, + { 2681, "Dfs data being reconciled is identical" }, + { 2682, "DFS root cannot be deleted. Uninstall DFS if required" }, + { 2683, "A child or parent directory of the share is already in a Dfs" }, + { 2690, "Dfs internal error" }, + { 2691, "This machine is already joined to a domain" }, + { 2692, "This machine is not currently joined to a domain" }, + { 2693, "This machine is a DC and cannot be unjoined from a domain" }, + { 2694, "destination DC does not support creating machine accounts in OUs" }, + { 2695, "specified workgroup name is invalid" }, + { 2696, "specified computer name is incompatible with the default language used on the DC" }, + { 2697, "specified computer account could not be found" }, + { 2698, "This version of Windows cannot be joined to a domain" }, + { 2701, "password must change at the next logon" }, + { 2702, "account is locked out" }, + { 2703, "password is too long" }, + { 2704, "password does not meet the complexity policy" }, + { 2705, "password does not meet the requirements of the password filter DLLs" }, +}; + +char * +raperrstr(uint err) +{ + int i, match; + static char buf[0xff]; + + match = -1; + for(i = 0; i < nelem(RAPerrs); i++) + if(RAPerrs[i].err == err) + match = i; + if(match != -1) + snprint(buf, sizeof buf, "rap: %s", RAPerrs[match].msg); + else + snprint(buf, sizeof buf, "rap: %ud/0x%ux - unknown error", + err, err); + return buf; +} diff --git a/sys/src/cmd/cifs/remsmb.h b/sys/src/cmd/cifs/remsmb.h new file mode 100755 index 000000000..ee01f4b9c --- /dev/null +++ b/sys/src/cmd/cifs/remsmb.h @@ -0,0 +1,466 @@ +/*++ + +Copyright (c) 1991-1992 Microsoft Corporation + +Module Name: + + RemSmb.h + +Abstract: + + Definition of descriptor strings for Net API remote calls. + Names defined in this file follow the format: + + RemSmb_RemDescriptor + + RemDescriptor follows one of the following formats: + + StructureName_level - info structures + StructureName_level_suffix - special info structures + ApiName_P - parameter descriptors + +Notes: + + 1. While the above formats should be followed, the equate names + cannot exceed 32 characters, and abbreviated forms should be used. + + 2. The remote API mechanism requires that the return parameter length + is less than or equal to the send parameter length. This assumption + is made in order to reduce the overhead in the buffer management + required for the API call. This restriction is not unreasonable + as the APIs were designed to return data in the data buffer and just + use return parameters for data lengths & file handles etc. + HOWEVER, if it has been spec'ed to return a large parameter field, it + is possible to pad the size of the send parameter using a REM_FILL_BYTES + field to meet the above restriction. + +Author: + +Environment: + + Portable to just about anything. + Requires ANSI C extensions: slash-slash comments, long external +names. + +Revision History: + +--*/ + +#ifndef _REMDEF_ +#define _REMDEF_ +/* + * ==================================================================== + * SMB XACT message descriptors. + * ==================================================================== + */ + +#define REMSmb_share_info_0 "B13" +#define REMSmb_share_info_1 "B13BWz" +#define REMSmb_share_info_2 "B13BWzWWWzB9B" + +#define REMSmb_share_info_90 "B13BWz" +#define REMSmb_share_info_92 "zzz" +#define REMSmb_share_info_93 "zzz" + +#define REMSmb_share_info_0_setinfo "B13" +#define REMSmb_share_info_1_setinfo "B13BWz" +#define REMSmb_share_info_2_setinfo "B13BWzWWOB9B" + +#define REMSmb_share_info_90_setinfo "B13BWz" +#define REMSmb_share_info_91_setinfo "B13BWzWWWOB9BB9BWzWWzWW" + +#define REMSmb_NetShareEnum_P "WrLeh" +#define REMSmb_NetShareGetInfo_P "zWrLh" +#define REMSmb_NetShareSetInfo_P "zWsTP" +#define REMSmb_NetShareAdd_P "WsT" +#define REMSmb_NetShareDel_P "zW" +#define REMSmb_NetShareCheck_P "zh" + +#define REMSmb_session_info_0 "z" +#define REMSmb_session_info_1 "zzWWWDDD" +#define REMSmb_session_info_2 "zzWWWDDDz" +#define REMSmb_session_info_10 "zzDD" + +#define REMSmb_NetSessionEnum_P "WrLeh" +#define REMSmb_NetSessionGetInfo_P "zWrLh" +#define REMSmb_NetSessionDel_P "zW" + +#define REMSmb_connection_info_0 "W" +#define REMSmb_connection_info_1 "WWWWDzz" + +#define REMSmb_NetConnectionEnum_P "zWrLeh" + +#define REMSmb_file_info_0 "W" +#define REMSmb_file_info_1 "WWWzz" +#define REMSmb_file_info_2 "D" +#define REMSmb_file_info_3 "DWWzz" + +#define REMSmb_NetFileEnum_P "zWrLeh" +#define REMSmb_NetFileEnum2_P "zzWrLehb8g8" +#define REMSmb_NetFileGetInfo_P "WWrLh" +#define REMSmb_NetFileGetInfo2_P "DWrLh" +#define REMSmb_NetFileClose_P "W" +#define REMSmb_NetFileClose2_P "D" + +#define REMSmb_server_info_0 "B16" +#define REMSmb_server_info_1 "B16BBDz" +#define REMSmb_server_info_2 "B16BBDzDDDWWzWWWWWWWB21BzWWWWWWWWWWWWWWWWWWWWWWz" +#define REMSmb_server_info_3 "B16BBDzDDDWWzWWWWWWWB21BzWWWWWWWWWWWWWWWWWWWWWWzDWz" + +#define REMSmb_server_info_1_setinfo "B16BBDz" +#define REMSmb_server_info_2_setinfo "B16BBDzDDDWWzWWWWWWWB21BOWWWWWWWWWWWWWWWWWWWWWWz" + +#define REMSmb_server_admin_command "B" + +#define REMSmb_server_diskenum_0 "B3" + +#define REMSmb_authenticator_info_0 "B8D" + +#define REMSmb_server_diskft_100 "B" +#define REMSmb_server_diskft_101 "BBWWWWDW" +#define REMSmb_server_diskft_102 "BBWWWWDN" +#define REMSmb_server_diskfterr_0 "DWWDDW" +#define REMSmb_ft_info_0 "WWW" +#define REMSmb_ft_drivestats_0 "BBWDDDDDDD" +#define REMSmb_ft_error_info_1 "DWWDDWBBDD" + +#define REMSmb_I_NetServerDiskEnum_P "WrLeh" +#define REMSmb_I_NetServerDiskGetInfo_P "WWrLh" +#define REMSmb_I_FTVerifyMirror_P "Wz" +#define REMSmb_I_FTAbortVerify_P "W" +#define REMSmb_I_FTGetInfo_P "WrLh" +#define REMSmb_I_FTSetInfo_P "WsTP" +#define REMSmb_I_FTLockDisk_P "WWh" +#define REMSmb_I_FTFixError_P "Dzhh2" +#define REMSmb_I_FTAbortFix_P "D" +#define REMSmb_I_FTDiagnoseError_P "Dhhhh" +#define REMSmb_I_FTGetDriveStats_P "WWrLh" +#define REMSmb_I_FTErrorGetInfo_P "DWrLh" + +#define REMSmb_NetServerEnum_P "WrLeh" +#define REMSmb_I_NetServerEnum_P "WrLeh" +#define REMSmb_NetServerEnum2_P "WrLehDz" +#define REMSmb_I_NetServerEnum2_P "WrLehDz" +#define REMSmb_NetServerEnum3_P "WrLehDzz" +#define REMSmb_NetServerGetInfo_P "WrLh" +#define REMSmb_NetServerSetInfo_P "WsTP" +#define REMSmb_NetServerDiskEnum_P "WrLeh" +#define REMSmb_NetServerAdminCommand_P "zhrLeh" +#define REMSmb_NetServerReqChalleng_P "zb8g8" +#define REMSmb_NetServerAuthenticat_P "zb8g8" +#define REMSmb_NetServerPasswordSet_P "zb12g12b16" + +#define REMSmb_NetAuditOpen_P "h" +#define REMSmb_NetAuditClear_P "zz" +#define REMSmb_NetAuditRead_P "zb16g16DhDDrLeh" + +#define REMSmb_AuditLogReturnBuf "K" + +#define REMSmb_NetErrorLogOpen_P "h" +#define REMSmb_NetErrorLogClear_P "zz" +#define REMSmb_NetErrorLogRead_P "zb16g16DhDDrLeh" + +#define REMSmb_ErrorLogReturnBuf "K" + +#define REMSmb_chardev_info_0 "B9" +#define REMSmb_chardev_info_1 "B10WB22D" +#define REMSmb_chardevQ_info_0 "B13" +#define REMSmb_chardevQ_info_1 "B14WzWW" + +#define REMSmb_NetCharDevEnum_P "WrLeh" +#define REMSmb_NetCharDevGetInfo_P "zWrLh" +#define REMSmb_NetCharDevControl_P "zW" +#define REMSmb_NetCharDevQEnum_P "zWrLeh" +#define REMSmb_NetCharDevQGetInfo_P "zzWrLh" +#define REMSmb_NetCharDevQSetInfo_P "zWsTP" +#define REMSmb_NetCharDevQPurge_P "z" +#define REMSmb_NetCharDevQPurgeSelf_P "zz" + +#define REMSmb_msg_info_0 "B16" +#define REMSmb_msg_info_1 "B16BBB16" +#define REMSmb_send_struct "K" + +#define REMSmb_NetMessageNameEnum_P "WrLeh" +#define REMSmb_NetMessageNameGetInfo_P "zWrLh" +#define REMSmb_NetMessageNameAdd_P "zW" +#define REMSmb_NetMessageNameDel_P "zW" +#define REMSmb_NetMessageNameFwd_P "zzW" +#define REMSmb_NetMessageNameUnFwd_P "z" +#define REMSmb_NetMessageBufferSend_P "zsT" +#define REMSmb_NetMessageFileSend_P "zz" +#define REMSmb_NetMessageLogFileSet_P "zW" +#define REMSmb_NetMessageLogFileGet_P "rLh" + +#define REMSmb_service_info_0 "B16" +#define REMSmb_service_info_1 "B16WDW" +#define REMSmb_service_info_2 "B16WDWB64" +#define REMSmb_service_cmd_args "K" + +#define REMSmb_NetServiceEnum_P "WrLeh" +#define REMSmb_NetServiceControl_P "zWWrL" +#define REMSmb_NetServiceInstall_P "zF88sg88T" /* See NOTE 2 */ +#define REMSmb_NetServiceGetInfo_P "zWrLh" + +#define REMSmb_access_info_0 "z" +#define REMSmb_access_info_0_setinfo "z" +#define REMSmb_access_info_1 "zWN" +#define REMSmb_access_info_1_setinfo "OWN" +#define REMSmb_access_list "B21BW" + +#define REMSmb_NetAccessEnum_P "zWWrLeh" +#define REMSmb_NetAccessGetInfo_P "zWrLh" +#define REMSmb_NetAccessSetInfo_P "zWsTP" +#define REMSmb_NetAccessAdd_P "WsT" +#define REMSmb_NetAccessDel_P "z" +#define REMSmb_NetAccessGetUserPerms_P "zzh" + +#define REMSmb_group_info_0 "B21" +#define REMSmb_group_info_1 "B21Bz" +#define REMSmb_group_users_info_0 "B21" +#define REMSmb_group_users_info_1 "B21BN" + +#define REMSmb_NetGroupEnum_P "WrLeh" +#define REMSmb_NetGroupAdd_P "WsT" +#define REMSmb_NetGroupDel_P "z" +#define REMSmb_NetGroupAddUser_P "zz" +#define REMSmb_NetGroupDelUser_P "zz" +#define REMSmb_NetGroupGetUsers_P "zWrLeh" +#define REMSmb_NetGroupSetUsers_P "zWsTW" +#define REMSmb_NetGroupGetInfo_P "zWrLh" +#define REMSmb_NetGroupSetInfo_P "zWsTP" + +#define REMSmb_user_info_0 "B21" +#define REMSmb_user_info_1 "B21BB16DWzzWz" +#define REMSmb_user_info_2 "B21BB16DWzzWzDzzzzDDDDWb21WWzWW" +#define REMSmb_user_info_10 "B21Bzzz" +#define REMSmb_user_info_11 "B21BzzzWDDzzDDWWzWzDWb21W" + +#define REMSmb_user_info_100 "DWW" +#define REMSmb_user_info_101 "B60" +#define REMSmb_user_modals_info_0 "WDDDWW" +#define REMSmb_user_modals_info_1 "Wz" +#define REMSmb_user_modals_info_100 "B50" +#define REMSmb_user_modals_info_101 "zDDzDD" +#define REMSmb_user_logon_info_0 "B21B" +#define REMSmb_user_logon_info_1 "WB21BWDWWDDDDDDDzzzD" +#define REMSmb_user_logon_info_2 "B21BzzzD" +#define REMSmb_user_logoff_info_1 "WDW" + +#define REMSmb_NetUserEnum_P "WrLeh" +#define REMSmb_NetUserAdd_P "WsTW" +#define REMSmb_NetUserAdd2_P "WsTWW" +#define REMSmb_NetUserDel_P "z" +#define REMSmb_NetUserGetInfo_P "zWrLh" +#define REMSmb_NetUserSetInfo_P "zWsTPW" +#define REMSmb_NetUserSetInfo2_P "zWsTPWW" +#define REMSmb_NetUserPasswordSet_P "zb16b16W" +#define REMSmb_NetUserPasswordSet2_P "zb16b16WW" +#define REMSmb_NetUserGetGroups_P "zWrLeh" +#define REMSmb_NetUserSetGroups_P "zWsTW" +#define REMSmb_NetUserModalsGet_P "WrLh" +#define REMSmb_NetUserModalsSet_P "WsTP" +#define REMSmb_NetUserEnum2_P "WrLDieh" +#define REMSmb_NetUserValidate2_P "Wb62WWrLhWW" + +#define REMSmb_wksta_info_0 "WDzzzzBBDWDWWWWWWWWWWWWWWWWWWWzzW" +#define REMSmb_wksta_info_0_setinfo "WDOOOOBBDWDWWWWWWWWWWWWWWWWWWWzzW" +#define REMSmb_wksta_info_1 "WDzzzzBBDWDWWWWWWWWWWWWWWWWWWWzzWzzW" +#define REMSmb_wksta_info_1_setinfo "WDOOOOBBDWDWWWWWWWWWWWWWWWWWWWzzWzzW" +#define REMSmb_wksta_info_10 "zzzBBzz" +#define REMSmb_wksta_annc_info "K" + +#define REMSmb_NetWkstaLogon_P "zzirL" +#define REMSmb_NetWkstaLogoff_P "zD" +#define REMSmb_NetWkstaSetUID_P "zzzW" +#define REMSmb_NetWkstaGetInfo_P "WrLh" +#define REMSmb_NetWkstaSetInfo_P "WsTP" +#define REMSmb_NetWkstaUserLogon_P "zzWb54WrLh" +#define REMSmb_NetWkstaUserLogoff_P "zzWb38WrLh" + +#define REMSmb_use_info_0 "B9Bz" +#define REMSmb_use_info_1 "B9BzzWWWW" + +#define REMSmb_use_info_2 "B9BzzWWWWWWWzB16" + +#define REMSmb_NetUseEnum_P "WrLeh" +#define REMSmb_NetUseAdd_P "WsT" +#define REMSmb_NetUseDel_P "zW" +#define REMSmb_NetUseGetInfo_P "zWrLh" + +#define REMSmb_printQ_0 "B13" +#define REMSmb_printQ_1 "B13BWWWzzzzzWW" +#define REMSmb_printQ_2 "B13BWWWzzzzzWN" +#define REMSmb_printQ_3 "zWWWWzzzzWWzzl" +#define REMSmb_printQ_4 "zWWWWzzzzWNzzl" +#define REMSmb_printQ_5 "z" + +#define REMSmb_DosPrintQEnum_P "WrLeh" +#define REMSmb_DosPrintQGetInfo_P "zWrLh" +#define REMSmb_DosPrintQSetInfo_P "zWsTP" +#define REMSmb_DosPrintQAdd_P "WsT" +#define REMSmb_DosPrintQDel_P "z" +#define REMSmb_DosPrintQPause_P "z" +#define REMSmb_DosPrintQPurge_P "z" +#define REMSmb_DosPrintQContinue_P "z" + +#define REMSmb_print_job_0 "W" +#define REMSmb_print_job_1 "WB21BB16B10zWWzDDz" +#define REMSmb_print_job_2 "WWzWWDDzz" +#define REMSmb_print_job_3 "WWzWWDDzzzzzzzzzzlz" + +#define REMSmb_print_job_info_1_setinfo "WB21BB16B10zWWODDz" +#define REMSmb_print_job_info_3_setinfo "WWzWWDDzzzzzOzzzzlO" + +#define REMSmb_DosPrintJobEnum_P "zWrLeh" +#define REMSmb_DosPrintJobGetInfo_P "WWrLh" +#define REMSmb_DosPrintJobSetInfo_P "WWsTP" +#define REMSmb_DosPrintJobAdd_P "zsTF129g129h" /* See note 2 */ +#define REMSmb_DosPrintJobSchedule_P "W" +#define REMSmb_DosPrintJobDel_P "W" +#define REMSmb_DosPrintJobPause_P "W" +#define REMSmb_DosPrintJobContinue_P "W" + +#define REMSmb_print_dest_0 "B9" +#define REMSmb_print_dest_1 "B9B21WWzW" +#define REMSmb_print_dest_2 "z" +#define REMSmb_print_dest_3 "zzzWWzzzWW" +#define REMSmb_print_dest_info_3_setinfo "zOzWWOzzWW" + +#define REMSmb_DosPrintDestEnum_P "WrLeh" +#define REMSmb_DosPrintDestGetInfo_P "zWrLh" +#define REMSmb_DosPrintDestControl_P "zW" +#define REMSmb_DosPrintDestAdd_P "WsT" +#define REMSmb_DosPrintDestSetInfo_P "zWsTP" +#define REMSmb_DosPrintDestDel_P "z" + +#define REMSmb_NetProfileSave_P "zDW" +#define REMSmb_NetProfileLoad_P "zDrLD" + +#define REMSmb_profile_load_info "WDzD" + +#define REMSmb_statistics_info "B" + +#define REMSmb_statistics2_info_W "B120" +#define REMSmb_stat_workstation_0 "DDDDDDDDDDDDDDDDDDDDDDDDDDDDDD" +#define REMSmb_statistics2_info_S "B68" +#define REMSmb_stat_server_0 "DDDDDDDDDDDDDDDDD" + +#define REMSmb_NetStatisticsGet_P "rLeh" +#define REMSmb_NetStatisticsClear_P "" + +#define REMSmb_NetStatisticsGet2_P "zDWDrLh" + +#define REMSmb_NetRemoteTOD_P "rL" + +#define REMSmb_time_of_day_info "DDBBBBWWBBWB" + +#define REMSmb_netbios_info_0 "B17" +#define REMSmb_netbios_info_1 "B17B9BBWWDWWW" + +#define REMSmb_NetBiosEnum_P "WrLeh" +#define REMSmb_NetBiosGetInfo_P "zWrLh" + +#define REMSmb_Spl_open_data "zzlzzzzzz" +#define REMSmb_plain_data "K" + +#define REMSmb_NetSplQmAbort_P "Di" +#define REMSmb_NetSplQmClose_P "Di" +#define REMSmb_NetSplQmEndDoc_P "Dhi" +#define REMSmb_NetSplQmOpen_P "zTsWii" +#define REMSmb_NetSplQmStartDoc_P "Dzi" +#define REMSmb_NetSplQmWrite_P "DTsi" + +#define REMSmb_configgetall_info "B" +#define REMSmb_configget_info "B" +#define REMSmb_configset_info_0 "zz" + +#define REMSmb_NetConfigGetAll_P "zzrLeh" +#define REMSmb_NetConfigGet_P "zzzrLe" +#define REMSmb_NetConfigSet_P "zzWWsTD" + +#define REMSmb_NetBuildGetInfo_P "DWrLh" +#define REMSmb_build_info_0 "WD" + +#define REMSmb_NetGetDCName_P "zrL" +#define REMSmb_dc_name "B18" + +#define REMSmb_challenge_info_0 "B8" +#define REMSmb_account_delta_info_0 "K" +#define REMSmb_account_sync_info_0 "K" + +#define REMSmb_NetAccountDeltas_P "zb12g12b24WWrLehg24" +#define REMSmb_NetAccountSync_P "zb12g12DWrLehig24" + +#define REMSmb_NetLogonEnum_P "WrLeh" + +#define REMSmb_I_NetPathType_P "ziD" +#define REMSmb_I_NetPathCanonicalize_P "zrLziDD" +#define REMSmb_I_NetPathCompare_P "zzDD" +#define REMSmb_I_NetNameValidate_P "zWD" +#define REMSmb_I_NetNameCanonicalize_P "zrLWD" +#define REMSmb_I_NetNameCompare_P "zzWD" + +#define REMSmb_LocalOnlyCall "" + +/* + * The following definitions exist for DOS LANMAN--Windows 3.0. + * Normally, there is a const char far * servername + * as the first parameter, but this will be ignored (sort of). + */ +#define REMSmb_DosPrintJobGetId_P "WrL" +#define REMSmb_GetPrintId "WB16B13B" +#define REMSmb_NetRemoteCopy_P "zzzzWWrL" +#define REMSmb_copy_info "WB1" +#define REMSmb_NetRemoteMove_P "zzzzWWrL" +#define REMSmb_move_info "WB1" +#define REMSmb_NetHandleGetInfo_P "WWrLh" +#define REMSmb_NetHandleSetInfo_P "WWsTP" +#define REMSmb_handle_info_1 "DW" +#define REMSmb_handle_info_2 "z" +#define REMSmb_WWkstaGetInfo_P "WrLhOW" + +/* The following strings are defined for RIPL APIs */ + +#define REMSmb_RplWksta_info_0 "z" +#define REMSmb_RplWksta_info_1 "zz" +#define REMSmb_RplWksta_info_2 "b13b16b15b15zN" +#define REMSmb_RplWksta_info_3 "b16b49" + +#define REMSmb_RplWkstaEnum_P "WzWrLehb4g4" +#define REMSmb_RplWkstaGetInfo_P "zWrLh" +#define REMSmb_RplWkstaSetInfo_P "zWsTPW" +#define REMSmb_RplWkstaAdd_P "WsTW" +#define REMSmb_RplWkstaDel_P "zW" + +#define REMSmb_RplProfile_info_0 "z" +#define REMSmb_RplProfile_info_1 "zz" +#define REMSmb_RplProfile_info_2 "b16b47" +#define REMSmb_RplProfile_info_3 "b16b47b16" + +#define REMSmb_RplProfileEnum_P "WzWrLehb4g4" +#define REMSmb_RplProfileGetInfo_P "zWrLh" +#define REMSmb_RplProfileSetInfo_P "zWsTP" +#define REMSmb_RplProfileAdd_P "WzsTW" +#define REMSmb_RplProfileDel_P "zW" +#define REMSmb_RplProfileClone_P "WzsTW" +#define REMSmb_RplBaseProfileEnum_P "WrLehb4g4" + + +/* LAN Manager 3.0 API strings go here */ + +#define REMSmb_I_GuidGetAgent_P "g6i" +#define REMSmb_I_GuidSetAgent_P "b6D" + + +/* update support */ + +#define REMSmb_NetAccountUpdate_P "b12g12WWrLh" +#define REMSmb_NetAccountConfirmUpd_P "b12g12D" +#define REMSmb_update_info_0 "K" + +/* + * SamrOemChangePasswordUser2 api support + */ +#define REMSmb_SamOEMChgPasswordUser2 "B516B16" /* data that is passed */ + +#endif /* ndef _REMDEF_ */ diff --git a/sys/src/cmd/cifs/sid2name.c b/sys/src/cmd/cifs/sid2name.c new file mode 100755 index 000000000..694ff317f --- /dev/null +++ b/sys/src/cmd/cifs/sid2name.c @@ -0,0 +1,120 @@ +#include +#include +#include +#include +#include +#include <9p.h> +#include "cifs.h" + +struct { /* Well known security IDs */ + char *name; + char *auth; + char *rid; +} known[] = { + /* default local users */ + { "lu.dialup", "S-1-5-1", nil }, + { "lu.network", "S-1-5-2", nil }, + { "lu.batch", "S-1-5-3", nil }, + { "lu.interactive", "S-1-5-4", nil }, + { "lu.service", "S-1-5-6", nil }, + { "lu.anon", "S-1-5-7", nil }, + { "lu.DC", "S-1-5-8", nil }, + { "lu.enterprise-domain", "S-1-5-9", nil }, + { "lu.self", "S-1-5-10", nil }, + { "lu.authenticated", "S-1-5-11", nil }, + { "lu.restricted", "S-1-5-12", nil }, + { "lu.terminal-services", "S-1-5-13", nil }, + { "lu.remote-desktop", "S-1-5-14", nil }, + { "lu.local-system", "S-1-5-18", nil }, + { "lu.local-service", "S-1-5-19", nil }, + { "lu.network-service", "S-1-5-20", nil }, + { "lu.builtin", "S-1-5-32", nil }, + + /* default local groups */ + { "lg.null", "S-1-0-0", nil }, + { "lg.world", "S-1-1-0", nil }, + { "lg.local", "S-1-2-0", nil }, + { "lg.creator-owner", "S-1-3-0", nil }, + { "lg.creator-group", "S-1-3-1", nil }, + { "lg.creator-owner-server", "S-1-3-2", nil }, + { "lg.creator-group-server", "S-1-3-3", nil }, + + /* default domain users */ + { "du.admin", "S-1-5", "500" }, + { "du.guest", "S-1-5", "501" }, + { "du.kerberos", "S-1-5", "502" }, + + /* default domain groups */ + { "dg.admins", "S-1-5-21", "512" }, + { "dg.users", "S-1-5-21", "513" }, + { "dg.guests", "S-1-5", "514" }, + { "dg.computers", "S-1-5", "515" }, + { "dg.controllers", "S-1-5", "516" }, + { "dg.cert-admins", "S-1-5", "517" }, + { "dg.schema-admins", "S-1-5", "518" }, + { "dg.enterprise-admins", "S-1-5", "519" }, + { "dg.group-policy-admins", "S-1-5", "520" }, + { "dg.remote-access", "S-1-5", "553" }, + + /* default domain aliases */ + { "da.admins", "S-1-5", "544" }, + { "da.users", "S-1-5", "545" }, + { "da.guests", "S-1-5", "546" }, + { "da.power-users", "S-1-5", "547" }, + { "da.account-operators", "S-1-5", "548" }, + { "da.server-operators", "S-1-5", "549" }, + { "da.print-operators", "S-1-5", "550" }, + { "da.backup-operators", "S-1-5", "551" }, + { "da.replicator", "S-1-5", "552" }, + { "da.RAS-servers", "S-1-5", "553" }, + +}; + +static char * +sid2name(char *sid) +{ + int i; + char *rid; + + if(sid == nil || (rid = strrchr(sid, '-')) == nil || *++rid == 0) + return estrdup9p("-"); + + for(i = 0; i < nelem(known); i++){ + if(strcmp(known[i].auth, sid) == 0 && known[i].rid == nil) + return estrdup9p(known[i].name); + + if(strlen(known[i].auth) < strlen(sid) && + strncmp(known[i].auth, sid, strlen(known[i].auth)) == 0 && + known[i].rid && strcmp(known[i].rid, rid) == 0) + return estrdup9p(known[i].name); + } + + return estrdup9p(rid); +} + +void +upd_names(Session *s, Share *sp, char *path, Dir *d) +{ + int fh, result; + char *usid, *gsid; + FInfo fi; + + if(d->uid) + free(d->uid); + if(d->gid) + free(d->gid); + + if((fh = CIFS_NT_opencreate(s, sp, path, 0, 0, 0, READ_CONTROL, + FILE_SHARE_ALL, FILE_OPEN, &result, &fi)) == -1){ + d->uid = estrdup9p("unknown"); + d->gid = estrdup9p("unknown"); + return; + } + usid = nil; + gsid = nil; + TNTquerysecurity(s, sp, fh, &usid, &gsid); + d->uid = sid2name(usid); + d->gid = sid2name(gsid); + if(fh != -1) + CIFSclose(s, sp, fh); +} diff --git a/sys/src/cmd/cifs/trans.c b/sys/src/cmd/cifs/trans.c new file mode 100755 index 000000000..d6daa2857 --- /dev/null +++ b/sys/src/cmd/cifs/trans.c @@ -0,0 +1,785 @@ +#include +#include +#include +#include +#include <9p.h> +#include "cifs.h" +#include "remsmb.h" +#include "apinums.h" + +static Pkt * +thdr(Session *s, Share *sp) +{ + Pkt *p; + + p = cifshdr(s, sp, SMB_COM_TRANSACTION); + p->tbase = pl16(p, 0); /* 0 Total parameter bytes to be sent, filled later */ + pl16(p, 0); /* 2 Total data bytes to be sent, filled later */ + pl16(p, 64); /* 4 Max parameter to return */ + pl16(p, MTU - T2HDRLEN - 128); /* 6 Max data to return */ + pl16(p, 1); /* 8 Max setup count to return */ + pl16(p, 0); /* 10 Flags */ + pl32(p, 1000); /* 12 Timeout (ms) */ + pl16(p, 0); /* 16 Reserved */ + pl16(p, 0); /* 18 Parameter count, filled later */ + pl16(p, 0); /* 20 Parameter offset, filled later */ + pl16(p, 0); /* 22 Data count, filled later */ + pl16(p, 0); /* 24 Data offset, filled later */ + pl16(p, 0); /* 26 Setup count (in words) */ + pbytes(p); /* end of cifs words section */ + return p; +} + +static void +ptparam(Pkt *p) +{ + uchar *pos; + + if(((p->pos - p->tbase) % 2) != 0) + p8(p, 0); /* pad to word boundry */ + pos = p->pos; + p->pos = p->tbase + 20; + pl16(p, pos - p->buf - NBHDRLEN); /* param offset */ + p->tparam = p->pos = pos; +} + +static void +ptdata(Pkt *p) +{ + uchar *pos = p->pos; + + assert(p->tparam != 0); + if(((p->pos - p->tbase) % 2) != 0) + p8(p, 0); /* pad to word boundry */ + + p->pos = p->tbase + 0; + pl16(p, pos - p->tparam); /* total param count */ + + p->pos = p->tbase + 18; + pl16(p, pos - p->tparam); /* param count */ + + p->pos = p->tbase + 24; + pl16(p, pos - p->buf - NBHDRLEN); /* data offset */ + + p->tdata = p->pos = pos; +} + +static int +trpc(Pkt *p) +{ + int got; + uchar *pos = p->pos; + + assert(p->tbase != 0); + assert(p->tdata != 0); + + p->pos = p->tbase + 2; + pl16(p, pos - p->tdata); /* total data count */ + + p->pos = p->tbase + 22; + pl16(p, pos - p->tdata); /* data count */ + + p->pos = pos; + if((got = cifsrpc(p)) == -1) + return -1; + + gl16(p); /* Total parameter count */ + gl16(p); /* Total data count */ + gl16(p); /* Reserved */ + gl16(p); /* Parameter count in this buffer */ + p->tparam = p->buf + NBHDRLEN + gl16(p); /* Parameter offset */ + gl16(p); /* Parameter displacement */ + gl16(p); /* Data count (this buffer); */ + p->tdata = p->buf + NBHDRLEN + gl16(p); /* Data offset */ + gl16(p); /* Data displacement */ + g8(p); /* Setup count */ + g8(p); /* Reserved */ + return got; +} + +static void +gtparam(Pkt *p) +{ + p->pos = p->tparam; +} + +static void +gtdata(Pkt *p) +{ + p->pos = p->tdata; +} + + +int +RAPshareenum(Session *s, Share *sp, Share **ent) +{ + int ngot = 0, err, navail, nret; + char tmp[1024]; + Pkt *p; + Share *q; + + p = thdr(s, sp); + pstr(p, "\\PIPE\\LANMAN"); + ptparam(p); + + pl16(p, API_WShareEnum); + pascii(p, REMSmb_NetShareEnum_P); /* request descriptor */ + pascii(p, REMSmb_share_info_0); /* reply descriptor */ + pl16(p, 0); /* detail level */ + pl16(p, MTU - 200); /* receive buffer length */ + ptdata(p); + + if(trpc(p) == -1){ + free(p); + return -1; + } + + gtparam(p); + err = gl16(p); /* error code */ + gl16(p); /* rx buffer offset */ + nret = gl16(p); /* number of entries returned */ + navail = gl16(p); /* number of entries available */ + + if(err && err != RAP_ERR_MOREINFO){ + werrstr("%s", raperrstr(err)); + free(p); + return -1; + } + + if(ngot == 0){ + *ent = emalloc9p(sizeof(Share) * navail); + memset(*ent, 0, sizeof(Share) * navail); + } + + q = *ent + ngot; + for (; ngot < navail && nret--; ngot++){ + gmem(p, tmp, 13); /* name */ + tmp[13] = 0; + q->name = estrdup9p(tmp); + q++; + } + + if(ngot < navail) + fprint(2, "%s: %d/%d - share list incomplete\n", argv0, ngot, navail); + + free(p); + return ngot; +} + + +int +RAPshareinfo(Session *s, Share *sp, char *share, Shareinfo2 *si2p) +{ + int conv, err; + char tmp[1024]; + Pkt *p; + + p = thdr(s, sp); + pstr(p, "\\PIPE\\LANMAN"); + + ptparam(p); + pl16(p, API_WShareGetInfo); + pascii(p, REMSmb_NetShareGetInfo_P); /* request descriptor */ + pascii(p, REMSmb_share_info_2); /* reply descriptor */ + pascii(p, share); + pl16(p, 1); /* detail level */ + pl16(p, MTU - 200); /* receive buffer length */ + + ptdata(p); + + if(trpc(p) == -1){ + free(p); + return -1; + } + + gtparam(p); + err = gl16(p); /* error code */ + conv = gl16(p); /* rx buffer offset */ + gl16(p); /* number of entries returned */ + gl16(p); /* number of entries available */ + + if(err){ + werrstr("%s", raperrstr(err)); + free(p); + return -1; + } + + memset(si2p, 0, sizeof(Shareinfo2)); + + gmem(p, tmp, 13); + tmp[13] = 0; + g8(p); /* padding */ + si2p->name = estrdup9p(tmp); + si2p->type = gl16(p); + gconv(p, conv, tmp, sizeof tmp); + si2p->comment = estrdup9p(tmp); + gl16(p); /* comment offset high (unused) */ + si2p->perms = gl16(p); + si2p->maxusrs = gl16(p); + si2p->activeusrs = gl16(p); + gconv(p, conv, tmp, sizeof tmp); + si2p->path = estrdup9p(tmp); + gl16(p); /* path offset high (unused) */ + gmem(p, tmp, 9); + tmp[9] = 0; + si2p->passwd = estrdup9p(tmp); + + free(p); + return 0; +} + +/* + * Tried to split sessionenum into two passes, one getting the names + * of the connected workstations and the other collecting the detailed info, + * however API_WSessionGetInfo doesn't seem to work agains win2k3 for infolevel + * ten and infolevel one and two are priviledged calls. This means this code + * will work for small numbers of sessions agains win2k3 and fail for samba 3.0 + * as it supports info levels zero and two only. + */ +int +RAPsessionenum(Session *s, Share *sp, Sessinfo **sip) +{ + int ngot = 0, conv, err, navail, nret; + char tmp[1024]; + Pkt *p; + Sessinfo *q; + + p = thdr(s, sp); + pstr(p, "\\PIPE\\LANMAN"); + ptparam(p); + + pl16(p, API_WSessionEnum); + pascii(p, REMSmb_NetSessionEnum_P); /* request descriptor */ + pascii(p, REMSmb_session_info_10); /* reply descriptor */ + pl16(p, 10); /* detail level */ + pl16(p, MTU - 200); /* receive buffer length */ + ptdata(p); + + if(trpc(p) == -1){ + free(p); + return -1; + } + + gtparam(p); + err = gl16(p); /* error code */ + conv = gl16(p); /* rx buffer offset */ + nret = gl16(p); /* number of entries returned */ + navail = gl16(p); /* number of entries available */ + + if(err && err != RAP_ERR_MOREINFO){ + werrstr("%s", raperrstr(err)); + free(p); + return -1; + } + + if(ngot == 0){ + *sip = emalloc9p(sizeof(Sessinfo) * navail); + memset(*sip, 0, sizeof(Sessinfo) * navail); + } + + q = *sip + ngot; + while(nret-- != 0){ + gconv(p, conv, tmp, sizeof tmp); + q->wrkstn = estrdup9p(tmp); + gconv(p, conv, tmp, sizeof tmp); + q->user = estrdup9p(tmp); + q->sesstime = gl32(p); + q->idletime = gl32(p); + ngot++; + q++; + } + if(ngot < navail) + fprint(2, "warning: %d/%d - session list incomplete\n", ngot, navail); + free(p); + return ngot; +} + + +int +RAPgroupenum(Session *s, Share *sp, Namelist **nlp) +{ + int ngot, err, navail, nret; + char tmp[1024]; + Pkt *p; + Namelist *q; + + ngot = 0; + p = thdr(s, sp); + pstr(p, "\\PIPE\\LANMAN"); + ptparam(p); + + pl16(p, API_WGroupEnum); + pascii(p, REMSmb_NetGroupEnum_P); /* request descriptor */ + pascii(p, REMSmb_group_info_0); /* reply descriptor */ + pl16(p, 0); /* detail level */ + pl16(p, MTU - 200); /* receive buffer length */ + ptdata(p); + + if(trpc(p) == -1){ + free(p); + return -1; + } + + gtparam(p); + err = gl16(p); /* error code */ + gl16(p); /* rx buffer offset */ + nret = gl16(p); /* number of entries returned */ + navail = gl16(p); /* number of entries available */ + + if(err && err != RAP_ERR_MOREINFO){ + werrstr("%s", raperrstr(err)); + free(p); + return -1; + } + + *nlp = emalloc9p(sizeof(Namelist) * navail); + memset(*nlp, 0, sizeof(Namelist) * navail); + + q = *nlp + ngot; + while(ngot < navail && nret--){ + gmem(p, tmp, 21); + tmp[21] = 0; + q->name = estrdup9p(tmp); + ngot++; + q++; + if(p->pos >= p->eop) /* Windows seems to lie somtimes */ + break; + } + free(p); + return ngot; +} + + +int +RAPgroupusers(Session *s, Share *sp, char *group, Namelist **nlp) +{ + int ngot, err, navail, nret; + char tmp[1024]; + Pkt *p; + Namelist *q; + + ngot = 0; + p = thdr(s, sp); + pstr(p, "\\PIPE\\LANMAN"); + ptparam(p); + + pl16(p, API_WGroupGetUsers); + pascii(p, REMSmb_NetGroupGetUsers_P); /* request descriptor */ + pascii(p, REMSmb_user_info_0); /* reply descriptor */ + pascii(p, group); /* group name for list */ + pl16(p, 0); /* detail level */ + pl16(p, MTU - 200); /* receive buffer length */ + ptdata(p); + + if(trpc(p) == -1){ + free(p); + return -1; + } + + gtparam(p); + err = gl16(p); /* error code */ + gl16(p); /* rx buffer offset */ + nret = gl16(p); /* number of entries returned */ + navail = gl16(p); /* number of entries available */ + + if(err && err != RAP_ERR_MOREINFO){ + werrstr("%s", raperrstr(err)); + free(p); + return -1; + } + + *nlp = emalloc9p(sizeof(Namelist) * navail); + memset(*nlp, 0, sizeof(Namelist) * navail); + + q = *nlp + ngot; + while(ngot < navail && nret--){ + gmem(p, tmp, 21); + tmp[21] = 0; + q->name = estrdup9p(tmp); + ngot++; + q++; + if(p->pos >= p->eop) /* Windows seems to lie somtimes */ + break; + } + free(p); + return ngot; +} + +int +RAPuserenum(Session *s, Share *sp, Namelist **nlp) +{ + int ngot, err, navail, nret; + char tmp[1024]; + Pkt *p; + Namelist *q; + + ngot = 0; + p = thdr(s, sp); + pstr(p, "\\PIPE\\LANMAN"); + ptparam(p); + + pl16(p, API_WUserEnum); + pascii(p, REMSmb_NetUserEnum_P); /* request descriptor */ + pascii(p, REMSmb_user_info_0); /* reply descriptor */ + pl16(p, 0); /* detail level */ + pl16(p, MTU - 200); /* receive buffer length */ + ptdata(p); + + if(trpc(p) == -1){ + free(p); + return -1; + } + + gtparam(p); + err = gl16(p); /* error code */ + gl16(p); /* rx buffer offset */ + nret = gl16(p); /* number of entries returned */ + navail = gl16(p); /* number of entries available */ + + if(err && err != RAP_ERR_MOREINFO){ + werrstr("%s", raperrstr(err)); + free(p); + return -1; + } + + *nlp = emalloc9p(sizeof(Namelist) * navail); + memset(*nlp, 0, sizeof(Namelist) * navail); + + q = *nlp + ngot; + while(ngot < navail && nret--){ + gmem(p, tmp, 21); + tmp[21] = 0; + q->name = estrdup9p(tmp); + ngot++; + q++; + if(p->pos >= p->eop) /* Windows seems to lie somtimes */ + break; + } + free(p); + return ngot; +} + +int +RAPuserenum2(Session *s, Share *sp, Namelist **nlp) +{ + int ngot, resume, err, navail, nret; + char tmp[1024]; + Pkt *p; + Namelist *q; + + ngot = 0; + resume = 0; +more: + p = thdr(s, sp); + pstr(p, "\\PIPE\\LANMAN"); + ptparam(p); + + pl16(p, API_WUserEnum2); + pascii(p, REMSmb_NetUserEnum2_P); /* request descriptor */ + pascii(p, REMSmb_user_info_0); /* reply descriptor */ + pl16(p, 0); /* detail level */ + pl16(p, MTU - 200); /* receive buffer length */ + pl32(p, resume); /* resume key to allow multiple fetches */ + ptdata(p); + + if(trpc(p) == -1){ + free(p); + return -1; + } + + gtparam(p); + err = gl16(p); /* error code */ + gl16(p); /* rx buffer offset */ + resume = gl32(p); /* resume key returned */ + nret = gl16(p); /* number of entries returned */ + navail = gl16(p); /* number of entries available */ + + if(err && err != RAP_ERR_MOREINFO){ + werrstr("%s", raperrstr(err)); + free(p); + return -1; + } + + if(ngot == 0){ + *nlp = emalloc9p(sizeof(Namelist) * navail); + memset(*nlp, 0, sizeof(Namelist) * navail); + } + q = *nlp + ngot; + while(ngot < navail && nret--){ + gmem(p, tmp, 21); + tmp[21] = 0; + q->name = estrdup9p(tmp); + ngot++; + q++; + if(p->pos >= p->eop) /* Windows seems to lie somtimes */ + break; + } + free(p); + if(ngot < navail) + goto more; + return ngot; +} + +int +RAPuserinfo(Session *s, Share *sp, char *user, Userinfo *uip) +{ + int conv, err; + char tmp[1024]; + Pkt *p; + + p = thdr(s, sp); + pstr(p, "\\PIPE\\LANMAN"); + ptparam(p); + + pl16(p, API_WUserGetInfo); + pascii(p, REMSmb_NetUserGetInfo_P); /* request descriptor */ + pascii(p, REMSmb_user_info_10); /* reply descriptor */ + pascii(p, user); /* username */ + pl16(p, 10); /* detail level */ + pl16(p, MTU - 200); /* receive buffer length */ + ptdata(p); + + if(trpc(p) == -1){ + free(p); + return -1; + } + + gtparam(p); + err = gl16(p); /* error code */ + conv = gl16(p); /* rx buffer offset */ + gl16(p); /* number of entries returned */ + gl16(p); /* number of entries available */ + + if(err && err != RAP_ERR_MOREINFO){ + werrstr("%s", raperrstr(err)); + free(p); + return -1; + } + + gmem(p, tmp, 21); + tmp[21] = 0; + uip->user = estrdup9p(tmp); + g8(p); /* padding */ + gconv(p, conv, tmp, sizeof tmp); + uip->comment = estrdup9p(tmp); + gconv(p, conv, tmp, sizeof tmp); + uip->user_comment = estrdup9p(tmp); + gconv(p, conv, tmp, sizeof tmp); + uip->fullname = estrdup9p(tmp); + + free(p); + return 0; +} + +/* + * This works agains win2k3 but fails + * against XP with the undocumented error 71/0x47 + */ +int +RAPServerenum2(Session *s, Share *sp, char *workgroup, int type, int *more, + Serverinfo **si) +{ + int ngot = 0, conv, err, nret, navail; + char tmp[1024]; + Pkt *p; + Serverinfo *q; + + p = thdr(s, sp); + pstr(p, "\\PIPE\\LANMAN"); + + ptparam(p); + pl16(p, API_NetServerEnum2); + pascii(p, REMSmb_NetServerEnum2_P); /* request descriptor */ + pascii(p, REMSmb_server_info_1); /* reply descriptor */ + pl16(p, 1); /* detail level */ + pl16(p, MTU - 200); /* receive buffer length */ + pl32(p, type); + pascii(p, workgroup); + + ptdata(p); + + if(trpc(p) == -1){ + free(p); + return -1; + } + + gtparam(p); + err = gl16(p); /* error code */ + conv = gl16(p); /* rx buffer offset */ + nret = gl16(p); /* number of entries returned */ + navail = gl16(p); /* number of entries available */ + + if(err && err != RAP_ERR_MOREINFO){ + werrstr("%s", raperrstr(err)); + free(p); + return -1; + } + + *si = emalloc9p(sizeof(Serverinfo) * navail); + memset(*si, 0, sizeof(Serverinfo) * navail); + + q = *si; + for (; nret-- != 0 && ngot < navail; ngot++){ + gmem(p, tmp, 16); + tmp[16] = 0; + q->name = estrdup9p(tmp); + q->major = g8(p); + q->minor = g8(p); + q->type = gl32(p); + gconv(p, conv, tmp, sizeof tmp); + q->comment = estrdup9p(tmp); + q++; + } + free(p); + *more = err == RAP_ERR_MOREINFO; + return ngot; +} + +int +RAPServerenum3(Session *s, Share *sp, char *workgroup, int type, int last, + Serverinfo *si) +{ + int conv, err, ngot, nret, navail; + char *first, tmp[1024]; + Pkt *p; + Serverinfo *q; + + ngot = last +1; + first = si[last].name; +more: + p = thdr(s, sp); + pstr(p, "\\PIPE\\LANMAN"); + + ptparam(p); + pl16(p, API_NetServerEnum3); + pascii(p, REMSmb_NetServerEnum3_P); /* request descriptor */ + pascii(p, REMSmb_server_info_1); /* reply descriptor */ + pl16(p, 1); /* detail level */ + pl16(p, MTU - 200); /* receive buffer length */ + pl32(p, type); + pascii(p, workgroup); + pascii(p, first); + + ptdata(p); + + if(trpc(p) == -1){ + free(p); + return -1; + } + + gtparam(p); + err = gl16(p); /* error code */ + conv = gl16(p); /* rx buffer offset */ + nret = gl16(p); /* number of entries returned */ + navail = gl16(p); /* number of entries available */ + + if(err && err != RAP_ERR_MOREINFO){ + werrstr("%s", raperrstr(err)); + free(p); + return -1; + } + + if(nret < 2){ /* paranoia */ + free(p); + return ngot; + } + + q = si+ngot; + while(nret-- != 0 && ngot < navail){ + gmem(p, tmp, 16); + tmp[16] = 0; + q->name = estrdup9p(tmp); + q->major = g8(p); + q->minor = g8(p); + q->type = gl32(p); + gconv(p, conv, tmp, sizeof tmp); + tmp[sizeof tmp - 1] = 0; + q->comment = estrdup9p(tmp); + if(strcmp(first, tmp) == 0){ /* 1st one thru _may_ be a repeat */ + free(q->name); + free(q->comment); + continue; + } + ngot++; + q++; + } + free(p); + if(ngot < navail) + goto more; + return ngot; +} + +/* Only the Administrator has permission to do this */ +int +RAPFileenum2(Session *s, Share *sp, char *user, char *path, Fileinfo **fip) +{ + int conv, err, ngot, resume, nret, navail; + char tmp[1024]; + Pkt *p; + Fileinfo *q; + + ngot = 0; + resume = 0; +more: + p = thdr(s, sp); + pstr(p, "\\PIPE\\LANMAN"); + + ptparam(p); + pl16(p, API_WFileEnum2); + pascii(p, REMSmb_NetFileEnum2_P); /* request descriptor */ + pascii(p, REMSmb_file_info_1); /* reply descriptor */ + pascii(p, path); + pascii(p, user); + pl16(p, 1); /* detail level */ + pl16(p, MTU - 200); /* receive buffer length */ + pl32(p, resume); /* resume key */ +/* FIXME: maybe the padding and resume key are the wrong way around? */ + pl32(p, 0); /* padding ? */ + + ptdata(p); + + if(trpc(p) == -1){ + free(p); + return -1; + } + + gtparam(p); + err = gl16(p); /* error code */ + conv = gl16(p); /* rx buffer offset */ + resume = gl32(p); /* resume key returned */ + nret = gl16(p); /* number of entries returned */ + navail = gl16(p); /* number of entries available */ + + if(err && err != RAP_ERR_MOREINFO){ + werrstr("%s", raperrstr(err)); + free(p); + return -1; + } + + if(nret < 2){ /* paranoia */ + free(p); + return ngot; + } + + if(ngot == 0){ + *fip = emalloc9p(sizeof(Fileinfo) * navail); + memset(*fip, 0, sizeof(Fileinfo) * navail); + } + q = *fip + ngot; + for(; nret-- && ngot < navail; ngot++){ + q->ident = gl16(p); + q->perms = gl16(p); + q->locks = gl16(p); + gconv(p, conv, tmp, sizeof tmp); + tmp[sizeof tmp - 1] = 0; + q->path = estrdup9p(tmp); + gconv(p, conv, tmp, sizeof tmp); + tmp[sizeof tmp - 1] = 0; + q->user = estrdup9p(tmp); + q++; + } + free(p); + if(ngot < navail) + goto more; + return ngot; +} diff --git a/sys/src/cmd/cifs/trans2.c b/sys/src/cmd/cifs/trans2.c new file mode 100755 index 000000000..090054096 --- /dev/null +++ b/sys/src/cmd/cifs/trans2.c @@ -0,0 +1,539 @@ +#include +#include +#include +#include +#include <9p.h> +#include "cifs.h" + +static Pkt * +t2hdr(Session *s, Share *sp, int cmd) +{ + Pkt *p; + + p = cifshdr(s, sp, SMB_COM_TRANSACTION2); + + p->tbase = pl16(p, 0); /* 0 Total parameter bytes to be sent, filled later */ + pl16(p, 0); /* 2 Total data bytes to be sent, filled later */ + pl16(p, 64); /* 4 Max parameter to return */ + pl16(p, (MTU - T2HDRLEN)-64); /* 6 Max data to return */ + p8(p, 0); /* 8 Max setup count to return */ + p8(p, 0); /* 9 Reserved */ + pl16(p, 0); /* 10 Flags */ + pl32(p, 1000); /* 12 Timeout (ms) */ + pl16(p, 0); /* 16 Reserved */ + pl16(p, 0); /* 18 Parameter count, filled later */ + pl16(p, 0); /* 20 Parameter offset, filled later */ + pl16(p, 0); /* 22 Data count, filled later */ + pl16(p, 0); /* 24 Data offset, filled later */ + p8(p, 1); /* 26 Setup count (in words) */ + p8(p, 0); /* 27 Reserved */ + pl16(p, cmd); /* setup[0] */ + pbytes(p); + p8(p, 0); /* padding ??!?!? */ + + return p; +} + +static void +pt2param(Pkt *p) +{ + uchar *pos = p->pos; + + assert(p->tbase != 0); + p->pos = p->tbase + 20; + pl16(p, (pos - p->buf) - NBHDRLEN); /* param offset */ + + p->tparam = p->pos = pos; +} + +static void +pt2data(Pkt *p) +{ + uchar *pos = p->pos; + + assert(p->tbase != 0); + assert(p->tparam != 0); + + p->pos = p->tbase +0; + pl16(p, pos - p->tparam); /* total param count */ + + p->pos = p->tbase +18; + pl16(p, pos - p->tparam); /* param count */ + + p->pos = p->tbase +24; + pl16(p, (pos - p->buf) - NBHDRLEN); /* data offset */ + + p->tdata = p->pos = pos; +} + +static int +t2rpc(Pkt *p) +{ + int got; + uchar *pos; + + assert(p->tbase != 0); + assert(p->tdata != 0); + + pos = p->pos; + + p->pos = p->tbase +2; + pl16(p, pos - p->tdata); /* total data count */ + + p->pos = p->tbase +22; + pl16(p, pos - p->tdata); /* data count */ + + p->pos = pos; + if((got = cifsrpc(p)) == -1) + return -1; + + gl16(p); /* Total parameter count */ + gl16(p); /* Total data count */ + gl16(p); /* Reserved */ + gl16(p); /* Parameter count in this buffer */ + p->tparam = p->buf +NBHDRLEN +gl16(p); /* Parameter offset */ + gl16(p); /* Parameter displacement */ + gl16(p); /* Data count (this buffer); */ + p->tdata = p->buf +NBHDRLEN +gl16(p); /* Data offset */ + gl16(p); /* Data displacement */ + g8(p); /* Setup count */ + g8(p); /* Reserved */ + + return got; +} + +static void +gt2param(Pkt *p) +{ + p->pos = p->tparam; +} + +static void +gt2data(Pkt *p) +{ + p->pos = p->tdata; +} + + +int +T2findfirst(Session *s, Share *sp, int slots, char *path, int *got, + long *resume, FInfo *fip) +{ + int pktlen, i, n, sh; + uchar *next; + Pkt *p; + + p = t2hdr(s, sp, TRANS2_FIND_FIRST2); + p8(p, 'D'); /* OS/2 */ + p8(p, ' '); /* OS/2 */ + + pt2param(p); + pl16(p, ATTR_HIDDEN|ATTR_SYSTEM|ATTR_DIRECTORY); /* Search attributes */ + pl16(p, slots); /* Search count */ + pl16(p, CIFS_SEARCH_RETURN_RESUME); /* Flags */ + pl16(p, SMB_FIND_FILE_FULL_DIRECTORY_INFO); /* Information level */ + pl32(p, 0); /* SearchStorage type (?) */ + ppath(p, path); /* path */ + + pt2data(p); + if((pktlen = t2rpc(p)) == -1){ + free(p); + return -1; + } + + s->lastfind = nsec(); + gt2param(p); + + sh = gl16(p); /* Sid (search handle) */ + *got = gl16(p); /* number of slots received */ + gl16(p); /* End of search flag */ + gl16(p); /* Offset into EA list if EA error */ + gl16(p); /* Offset into data to file name of last entry */ + + gt2data(p); + memset(fip, 0, slots * sizeof(FInfo)); + for(i = 0; i < *got; i++){ + next = p->pos; + next += gl32(p); /* offset to next entry */ + /* + * bug in Windows - somtimes it lies about how many + * directory entries it has put in the packet + */ + if(next - p->buf > pktlen){ + *got = i; + break; + } + + *resume = gl32(p); /* resume key for search */ + fip[i].created = gvtime(p); /* creation time */ + fip[i].accessed = gvtime(p); /* last access time */ + fip[i].written = gvtime(p); /* last written time */ + fip[i].changed = gvtime(p); /* change time */ + fip[i].size = gl64(p); /* file size */ + gl64(p); /* bytes allocated */ + fip[i].attribs = gl32(p); /* extended attributes */ + n = gl32(p); /* name length */ + gl32(p); /* EA size */ + gstr(p, fip[i].name, n); /* name */ + p->pos = next; + } + + free(p); + return sh; + +} + +int +T2findnext(Session *s, Share *sp, int slots, char *path, int *got, + long *resume, FInfo *fip, int sh) +{ + Pkt *p; + int i, n; + uchar *next; + + /* + * So I believe from comp.protocols.smb if you send + * TRANS2_FIND_NEXT2 requests too quickly to windows 95, it can + * get confused and fail to reply, so we slow up a bit in these + * circumstances. + */ + if(!(s->caps & CAP_NT_SMBS) && nsec() - s->lastfind < 200000000LL) + sleep(200); + + p = t2hdr(s, sp, TRANS2_FIND_NEXT2); + p8(p, 'D'); /* OS/2 */ + p8(p, ' '); /* OS/2 */ + + pt2param(p); + pl16(p, sh); /* search handle */ + pl16(p, slots); /* Search count */ + pl16(p, SMB_FIND_FILE_FULL_DIRECTORY_INFO); /* Information level */ + pl32(p, *resume); /* resume key */ + pl16(p, CIFS_SEARCH_CONTINUE_FROM_LAST); /* Flags */ + ppath(p, path); /* file+path to resume */ + + pt2data(p); + if(t2rpc(p) == -1){ + free(p); + return -1; + } + + s->lastfind = nsec(); + + gt2param(p); + *got = gl16(p); /* number of slots received */ + gl16(p); /* End of search flag */ + gl16(p); /* Offset into EA list if EA error */ + gl16(p); /* Offset into data to file name of last entry */ + + gt2data(p); + memset(fip, 0, slots * sizeof(FInfo)); + for(i = 0; i < *got; i++){ + next = p->pos; + next += gl32(p); /* offset to next entry */ + *resume = gl32(p); /* resume key for search */ + fip[i].created = gvtime(p); /* creation time */ + fip[i].accessed = gvtime(p); /* last access time */ + fip[i].written = gvtime(p); /* last written time */ + fip[i].changed = gvtime(p); /* change time */ + fip[i].size = gl64(p); /* file size */ + gl64(p); /* bytes allocated */ + fip[i].attribs = gl32(p); /* extended attributes */ + n = gl32(p); /* name length */ + gl32(p); /* EA size */ + gstr(p, fip[i].name, n); /* name */ + p->pos = next; + } + free(p); + return 0; +} + + +/* supported by 2k/XP/NT4 */ +int +T2queryall(Session *s, Share *sp, char *path, FInfo *fip) +{ + int n; + Pkt *p; + + p = t2hdr(s, sp, TRANS2_QUERY_PATH_INFORMATION); + pt2param(p); + pl16(p, SMB_QUERY_FILE_ALL_INFO); /* Information level */ + pl32(p, 0); /* reserved */ + ppath(p, path); /* path */ + + pt2data(p); + if(t2rpc(p) == -1){ + free(p); + return -1; + } + gt2data(p); + + /* + * The layout of this struct is wrong in the SINA + * document, this layout gained by inspection. + */ + memset(fip, 0, sizeof(FInfo)); + fip->created = gvtime(p); /* creation time */ + fip->accessed = gvtime(p); /* last access time */ + fip->written = gvtime(p); /* last written time */ + fip->changed = gvtime(p); /* change time */ + fip->attribs = gl32(p); /* attributes */ + gl32(p); /* reserved */ + gl64(p); /* bytes allocated */ + fip->size = gl64(p); /* file size */ + gl32(p); /* number of hard links */ + g8(p); /* delete pending */ + g8(p); /* is a directory */ + gl16(p); /* reserved */ + gl32(p); /* EA size */ + + n = gl32(p); + if(n >= sizeof fip->name) + n = sizeof fip->name - 1; + gstr(p, fip->name, n); + + free(p); + return 0; +} + +/* supported by 95/98/ME */ +int +T2querystandard(Session *s, Share *sp, char *path, FInfo *fip) +{ + Pkt *p; + + p = t2hdr(s, sp, TRANS2_QUERY_PATH_INFORMATION); + pt2param(p); + pl16(p, SMB_INFO_STANDARD); /* Information level */ + pl32(p, 0); /* reserved */ + ppath(p, path); /* path */ + + pt2data(p); + if(t2rpc(p) == -1){ + free(p); + return -1; + } + gt2data(p); + memset(fip, 0, sizeof(FInfo)); + fip->created = gdatetime(p); /* creation time */ + fip->accessed = gdatetime(p); /* last access time */ + fip->written = gdatetime(p); /* last written time */ + fip->changed = fip->written; /* change time */ + fip->size = gl32(p); /* file size */ + gl32(p); /* bytes allocated */ + fip->attribs = gl16(p); /* attributes */ + gl32(p); /* EA size */ + + free(p); + return 0; +} + +int +T2setpathinfo(Session *s, Share *sp, char *path, FInfo *fip) +{ + int rc; + Pkt *p; + + p = t2hdr(s, sp, TRANS2_SET_PATH_INFORMATION); + pt2param(p); + pl16(p, SMB_INFO_STANDARD); /* Information level */ + pl32(p, 0); /* reserved */ + ppath(p, path); /* path */ + + pt2data(p); + pdatetime(p, fip->created); /* created */ + pdatetime(p, fip->accessed); /* accessed */ + pdatetime(p, fip->written); /* written */ + pl32(p, fip->size); /* size */ + pl32(p, 0); /* allocated */ + pl16(p, fip->attribs); /* attributes */ + pl32(p, 0); /* EA size */ + pl16(p, 0); /* reserved */ + + rc = t2rpc(p); + free(p); + return rc; + +} + +int +T2setfilelength(Session *s, Share *sp, int fh, FInfo *fip) /* FIXME: maybe broken, needs testing */ +{ + int rc; + Pkt *p; + + p = t2hdr(s, sp, TRANS2_SET_FILE_INFORMATION); + pt2param(p); + pl16(p, fh); /* file handle */ + pl16(p, SMB_SET_FILE_END_OF_FILE_INFO); /* Information level */ + pl16(p, 0); /* reserved */ + + pt2data(p); + pl64(p, fip->size); + pl32(p, 0); /* padding ?! */ + pl16(p, 0); + + rc = t2rpc(p); + free(p); + return rc; +} + + +int +T2fsvolumeinfo(Session *s, Share *sp, long *created, long *serialno, + char *label, int labellen) +{ + Pkt *p; + long ct, sn, n; + + p = t2hdr(s, sp, TRANS2_QUERY_FS_INFORMATION); + pt2param(p); + pl16(p, SMB_QUERY_FS_VOLUME_INFO); /* Information level */ + + pt2data(p); + + if(t2rpc(p) == -1){ + free(p); + return -1; + } + + gt2data(p); + ct = gvtime(p); /* creation time */ + sn = gl32(p); /* serial number */ + n = gl32(p); /* label name length */ + g8(p); /* reserved */ + g8(p); /* reserved */ + + memset(label, 0, labellen); + if(n < labellen && n > 0) + gstr(p, label, n); /* file system label */ + if(created) + *created = ct; + if(serialno) + *serialno = sn; + + free(p); + return 0; +} + +int +T2fssizeinfo(Session *s, Share *sp, uvlong *total, uvlong *unused) +{ + Pkt *p; + uvlong t, f, n, b; + + p = t2hdr(s, sp, TRANS2_QUERY_FS_INFORMATION); + pt2param(p); + pl16(p, SMB_QUERY_FS_SIZE_INFO); /* Information level */ + + pt2data(p); + + if(t2rpc(p) == -1){ + free(p); + return -1; + } + + gt2data(p); + t = gl64(p); /* total blocks */ + f = gl64(p); /* free blocks */ + n = gl32(p); /* sectors per block */ + b = gl32(p); /* bytes per sector */ + + if(free) + *unused = f * n * b; + if(total) + *total = t * n * b; + + free(p); + return 0; +} + +int +T2getdfsreferral(Session *s, Share *sp, char *path, int *gflags, int *used, + Refer *re, int nent) +{ + int i, vers, nret, len; + char tmp[1024]; + uchar *base; + Pkt *p; + + p = t2hdr(s, sp, TRANS2_GET_DFS_REFERRAL); + pt2param(p); + pl16(p, 3); /* max info level we understand, must be >= 3 for domain requests */ + ppath(p, path); + + pt2data(p); + + if(t2rpc(p) == -1){ + free(p); + return -1; + } + + memset(re, 0, sizeof *re * nent); + gt2data(p); + + *used = gl16(p) / 2; /* length used (/2 as Windows counts in runes) */ + nret = gl16(p); /* number of referrals returned */ + *gflags = gl32(p); /* global flags */ + + for(i = 0; i < nret && i < nent && i < 16; i++){ + base = p->pos; + vers = gl16(p); /* version of records */ + len = gl16(p); /* length of records */ + re[i].type = gl16(p); /* server type */ + re[i].flags = gl16(p); /* referal flags */ + switch(vers){ + case 1: + re[i].prox = 0; /* nearby */ + re[i].ttl = 5*60; /* 5 mins */ + gstr(p, tmp, sizeof tmp); + re[i].addr = estrdup9p(tmp); + re[i].path = estrdup9p(tmp); + break; + case 2: + re[i].prox = gl32(p); /* not implemented in v2 */ + re[i].ttl = gl32(p); + goff(p, base, re[i].path, sizeof tmp); + re[i].path = estrdup9p(tmp); + goff(p, base, re[i].path, sizeof tmp);/* spurious 8.3 path */ + goff(p, base, tmp, sizeof tmp); + re[i].addr = estrdup9p(tmp); + break; + case 3: + if(re[i].flags & DFS_REFERAL_LIST){ + re[i].prox = 0; + re[i].ttl = gl32(p); + goff(p, base, tmp, sizeof tmp); + re[i].path = estrdup9p(tmp); + gl16(p); + goff(p, base, tmp, sizeof tmp); + re[i].addr = estrdup9p(tmp); + } + else{ + re[i].prox = 0; + re[i].ttl = gl32(p); + goff(p, base, tmp, sizeof tmp); + re[i].path = estrdup9p(tmp); + gl16(p); /* spurious 8.3 path */ + goff(p, base, tmp, sizeof tmp); + re[i].addr = estrdup9p(tmp); + gl16(p); /* GUID (historic) */ + } + break; + default: + /* + * this should never happen as we specify our maximum + * understood level in the request (above) + */ + fprint(2, "%d - unsupported DFS infolevel\n", vers); + re[i].path = estrdup9p(tmp); + re[i].addr = estrdup9p(tmp); + break; + } + p->pos = base+len; + } + + free(p); + return i; +} diff --git a/sys/src/cmd/cifs/transnt.c b/sys/src/cmd/cifs/transnt.c new file mode 100755 index 000000000..453beaa74 --- /dev/null +++ b/sys/src/cmd/cifs/transnt.c @@ -0,0 +1,167 @@ +#include +#include +#include +#include +#include <9p.h> +#include "cifs.h" + +static Pkt * +tnthdr(Session *s, Share *sp, int cmd) +{ + Pkt *p; + + p = cifshdr(s, sp, SMB_COM_NT_TRANSACT); + p->tbase = p8(p, 0); /* 0 Max setup count to return */ + pl16(p, 0); /* 1 reserved */ + pl32(p, 0); /* 3 Total parameter count */ + pl32(p, 0); /* 7 Total data count */ + pl32(p, 64); /* 11 Max parameter count to return */ + pl32(p, (MTU - T2HDRLEN)-64); /* 15 Max data count to return */ + pl32(p, 0); /* 19 Parameter count (in this buffer) */ + pl32(p, 0); /* 23 Offset to parameters (in this buffer) */ + pl32(p, 0); /* 27 Count of data in this buffer */ + pl32(p, 0); /* 31 Offset to data in this buffer */ + p8(p, 1); /* 35 Count of setup words */ + pl16(p, cmd); /* 37 setup[0] */ + pl16(p, 0); /* padding ??!?!? */ + pbytes(p); + return p; +} + +static void +ptntparam(Pkt *p) +{ + uchar *pos = p->pos; + assert(p->tbase != 0); + + p->pos = p->tbase +23; + pl32(p, (pos - p->buf) - NBHDRLEN); /* param offset */ + + p->tparam = p->pos = pos; +} + +static void +ptntdata(Pkt *p) +{ + uchar *pos = p->pos; + assert(p->tbase != 0); + assert(p->tparam != 0); + + p->pos = p->tbase +3; + pl32(p, pos - p->tparam); /* total param count */ + + p->pos = p->tbase +19; + pl32(p, pos - p->tparam); /* param count */ + + p->pos = p->tbase +31; + pl32(p, (pos - p->buf) - NBHDRLEN); /* data offset */ + p->tdata = p->pos = pos; +} + +static int +tntrpc(Pkt *p) +{ + int got; + uchar *pos; + assert(p->tbase != 0); + assert(p->tdata != 0); + + pos = p->pos; + + p->pos = p->tbase +7; + pl32(p, pos - p->tdata); /* total data count */ + + p->pos = p->tbase +27; + pl32(p, pos - p->tdata); /* data count */ + + p->pos = pos; + if((got = cifsrpc(p)) == -1) + return -1; + + g8(p); /* Reserved */ + g8(p); /* Reserved */ + g8(p); /* Reserved */ + gl32(p); /* Total parameter count */ + gl32(p); /* Total data count */ + gl32(p); /* Parameter count in this buffer */ + p->tparam = p->buf +NBHDRLEN +gl32(p); /* Parameter offset */ + gl32(p); /* Parameter displacement */ + gl32(p); /* Data count (this buffer); */ + p->tdata = p->buf +NBHDRLEN +gl32(p); /* Data offset */ + gl32(p); /* Data displacement */ + g8(p); /* Setup count */ + gl16(p); /* padding ??? */ + + return got; +} + +static void +gtntparam(Pkt *p) +{ + p->pos = p->tparam; +} + +static void +gtntdata(Pkt *p) +{ + p->pos = p->tdata; +} + + +int +TNTquerysecurity(Session *s, Share *sp, int fh, char **usid, char **gsid) +{ + Pkt *p; + uchar *base; + Fmt fmt, *f = &fmt; + int n, i, off2owner, off2group; + + p = tnthdr(s, sp, NT_TRANSACT_QUERY_SECURITY_DESC); + ptntparam(p); + + pl16(p, fh); /* File handle */ + pl16(p, 0); /* Reserved */ + pl32(p, QUERY_OWNER_SECURITY_INFORMATION | + QUERY_GROUP_SECURITY_INFORMATION); + + ptntdata(p); + + if(tntrpc(p) == -1){ + free(p); + return -1; + } + + gtntdata(p); + + base = p->pos; + gl16(p); /* revision */ + gl16(p); /* type */ + off2owner = gl32(p); /* offset to owner */ + off2group = gl32(p); /* offset to group */ + gl32(p); + gl32(p); + + if(off2owner){ + p->pos = base + off2owner; + fmtstrinit(f); + fmtprint(f, "S-%ud", g8(p)); /* revision */ + n = g8(p); /* num auth */ + fmtprint(f, "-%llud", gb48(p)); /* authority */ + for(i = 0; i < n; i++) + fmtprint(f, "-%ud", gl32(p)); /* sub-authorities */ + *usid = fmtstrflush(f); + } + + if(off2group){ + p->pos = base + off2group; + fmtstrinit(f); + fmtprint(f, "S-%ud", g8(p)); /* revision */ + n = g8(p); /* num auth */ + fmtprint(f, "-%llud", gb48(p)); /* authority */ + for(i = 0; i < n; i++) + fmtprint(f, "-%ud", gl32(p)); /* sub-authorities */ + *gsid = fmtstrflush(f); + } + free(p); + return 0; +} -- cgit v1.2.3