diff options
author | Taru Karttunen <taruti@taruti.net> | 2011-03-30 15:46:40 +0300 |
---|---|---|
committer | Taru Karttunen <taruti@taruti.net> | 2011-03-30 15:46:40 +0300 |
commit | e5888a1ffdae813d7575f5fb02275c6bb07e5199 (patch) | |
tree | d8d51eac403f07814b9e936eed0c9a79195e2450 /sys/src/cmd/cifs/cifs.c |
Import sources from 2011-03-30 iso image
Diffstat (limited to 'sys/src/cmd/cifs/cifs.c')
-rwxr-xr-x | sys/src/cmd/cifs/cifs.c | 806 |
1 files changed, 806 insertions, 0 deletions
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 <u.h> +#include <libc.h> +#include <fcall.h> +#include <thread.h> +#include <9p.h> +#include "cifs.h" + +static char magic[] = { 0xff, 'S', 'M', 'B' }; + +Session * +cifsdial(char *host, char *called, char *sysname) +{ + int nbt, fd; + char *addr; + Session *s; + + if(Debug) + fprint(2, "cifsdial: host=%s called=%s sysname=%s\n", host, called, sysname); + + if((addr = netmkaddr(host, "tcp", "cifs")) == nil) + return nil; + + nbt = 0; + if((fd = dial(addr, nil, nil, nil)) == -1){ + nbt = 1; + if((fd = nbtdial(host, called, sysname)) == -1) + return nil; + } + + s = emalloc9p(sizeof(Session)); + memset(s, 0, sizeof(Session)); + + s->fd = fd; + s->nbt = nbt; + s->mtu = MTU; + s->pid = getpid(); + s->mid = time(nil) ^ getpid(); + s->uid = NO_UID; + s->seq = 0; + s->seqrun = 0; + s->secmode = SECMODE_SIGN_ENABLED; /* hope for the best */ + s->flags2 = FL2_KNOWS_LONG_NAMES | FL2_HAS_LONG_NAMES | FL2_PAGEING_IO; + s->macidx = -1; + + return s; +} + +void +cifsclose(Session *s) +{ + if(s->fd) + close(s->fd); + free(s); +} + +Pkt * +cifshdr(Session *s, Share *sp, int cmd) +{ + Pkt *p; + int sign, tid, dfs; + + dfs = 0; + tid = NO_TID; + Active = IDLE_TIME; + 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; +} |