8 static char magic[] = { 0xff, 'S', 'M', 'B' };
11 cifsdial(char *host, char *called, char *sysname)
18 fprint(2, "cifsdial: host=%s called=%s sysname=%s\n", host, called, sysname);
20 if((addr = netmkaddr(host, "tcp", "cifs")) == nil)
24 if((fd = dial(addr, nil, nil, nil)) == -1){
26 if((fd = nbtdial(host, called, sysname)) == -1)
30 s = emalloc9p(sizeof(Session));
31 memset(s, 0, sizeof(Session));
37 s->mid = time(nil) ^ getpid();
41 s->secmode = SECMODE_SIGN_ENABLED; /* hope for the best */
42 s->flags2 = FL2_KNOWS_LONG_NAMES | FL2_HAS_LONG_NAMES | FL2_PAGEING_IO | FL2_UNICODE;
61 cifshdr(Session *s, Share *sp, int cmd)
70 sign = s->secmode & SECMODE_SIGN_ENABLED? FL2_PACKET_SIGNATURES: 0;
74 // FIXME! if(sp->options & SMB_SHARE_IS_IN_DFS)
75 // FIXME! dfs = FL2_DFS;
78 p = emalloc9p(sizeof(Pkt) + MTU);
79 memset(p, 0, sizeof(Pkt) +MTU);
81 p->buf = (uchar *)p + sizeof(Pkt);
83 p->request = cmd; /* for debug */
88 s->seq = (s->seq + 2) % 0x10000;
93 pmem(p, magic, nelem(magic));
95 pl32(p, 0); /* status (error) */
96 p8(p, FL_CASELESS_NAMES | FL_CANNONICAL_NAMES); /* flags */
97 pl16(p, s->flags2 | dfs | sign); /* flags2 */
98 pl16(p, (s->pid >> 16) & 0xffff); /* PID MS bits */
99 pl32(p, p->seq); /* MAC / sequence number */
100 pl32(p, 0); /* MAC */
101 pl16(p, 0); /* padding */
104 pl16(p, s->pid & 0xffff);
108 p->wordbase = p8(p, 0); /* filled in by pbytes() */
118 assert(p->wordbase != nil); /* cifshdr not called */
119 assert(p->bytebase == nil); /* called twice */
121 n = p->pos - p->wordbase;
122 assert(n % 2 != 0); /* even addr */
123 *p->wordbase = n / 2;
125 p->bytebase = pl16(p, 0); /* filled in by cifsrpc() */
129 dmp(int seq, uchar *buf)
137 for(i = 0; i < 8; i++)
138 print("%02x ", buf[i] & 0xff);
148 char m[nelem(magic)];
153 p->pos = p->bytebase;
154 pl16(p, pos - (p->bytebase + 2)); /* 2 = sizeof bytecount */
158 if(p->s->secmode & SECMODE_SIGN_ENABLED)
161 qlock(&p->s->rpclock);
163 qunlock(&p->s->rpclock);
165 if(got < 32+NBHDRLEN){
166 werrstr("cifs packet too small (%d < %d)\n", got, 32+NBHDRLEN);
170 gmem(p, m, nelem(magic));
171 if(memcmp(m, magic, nelem(magic)) != 0){
172 werrstr("cifsrpc: bad magic number in packet 0x%02ux%02ux%02ux%02ux",
173 m[0], m[1], m[2], m[3]);
177 reply = g8(p); /* cmd */
178 err = gl32(p); /* errcode */
180 p->flags2 = gl16(p); /* flags2 */
181 gl16(p); /* PID MS bits */
182 seq = gl32(p); /* reserved */
183 gl32(p); /* MAC (if in use) */
184 gl16(p); /* Padding */
185 tid = gl16(p); /* TID */
186 gl16(p); /* PID lsbs */
187 uid = gl16(p); /* UID */
189 g8(p); /* word count */
191 if(reply != p->request){
192 fprint(2, "unexpected reply (cmd=%x/%x seq=%d/%d)\n",
193 reply, p->request, seq, p->seq);
197 if(p->s->secmode & SECMODE_SIGN_ENABLED){
198 if(macsign(p, p->seq+1) != 0 && p->s->seqrun){
199 werrstr("cifsrpc: invalid packet signature");
200 print("MAC signature bad\n");
201 // FIXME: for debug only return -1;
205 * We allow the sequence number of zero as some old samba
206 * servers seem to fall back to this unexpectedly
207 * after reporting sequence numbers correctly for a while.
209 * Some other samba servers seem to always report a sequence
210 * number of zero if MAC signing is disabled, so we have to
213 if(p->s->seqrun && seq != p->seq && seq != 0){
214 werrstr("bad sequence number (%d != %d)\n", p->seq, seq);
220 if(p->s->uid == NO_UID)
223 if(p->flags2 & FL2_NT_ERRCODES){
224 /* is it a real error rather than info/warning/chatter? */
225 if((err & 0xF0000000) == 0xC0000000){
226 werrstr("%s", nterrstr(err));
231 werrstr("%s", doserrstr(err));
240 * Some older servers (old samba) prefer to talk older
241 * dialects but if given no choice they will talk the
242 * more modern ones, so we don't give them the choice.
245 CIFSnegotiate(Session *s, long *svrtime, char *domain, int domlen, char *cname,
249 char *ispeak = "NT LM 0.12";
250 static char *dialects[] = { { "NT LM 0.12" } };
253 p = cifshdr(s, nil, SMB_COM_NEGOTIATE);
256 for(i = 0; i < nelem(dialects); i++){
258 pascii(p, dialects[i]);
261 if(cifsrpc(p) == -1){
267 if(d < 0 || d > nelem(dialects)){
268 werrstr("no CIFS dialect in common");
273 if(strcmp(dialects[d], ispeak) != 0){
274 werrstr("%s dialect unsupported", dialects[d]);
279 s->secmode = g8(p); /* Security mode */
281 gl16(p); /* Max outstanding requests */
282 gl16(p); /* Max VCs */
283 s->mtu = gl32(p); /* Max buffer size */
284 gl32(p); /* Max raw buffer size (depricated) */
285 gl32(p); /* Session key */
286 s->caps = gl32(p); /* Server capabilities */
287 *svrtime = gvtime(p); /* fileserver time */
288 s->tz = (short)gl16(p) * 60; /* TZ in mins, is signed (SNIA doc is wrong) */
289 s->challen = g8(p); /* Encryption key length */
291 gmem(p, s->chal, s->challen); /* Get the challenge */
294 * for some weird reason the following two string always seem to be in unicode,
295 * however they are NOT byte aligned, every other packet is correctly aligned
297 gstr_noalign(p, domain, domlen); /* source domain */
298 { /* NetApp Filer seem not to report its called name */
299 char *cn = emalloc9p(cnamlen);
300 gstr_noalign(p, cn, cnamlen); /* their name */
302 memcpy(cname, cn, cnamlen);
306 if(s->caps & CAP_UNICODE)
307 s->flags2 |= FL2_UNICODE;
309 s->flags2 &= ~FL2_UNICODE;
316 CIFSsession(Session *s)
322 mycaps = CAP_UNICODE | CAP_LARGE_FILES | CAP_NT_SMBS |
323 CAP_NT_FIND | CAP_STATUS32,
326 s->seqrun = 1; /* activate the sequence number generation/checking */
328 p = cifshdr(s, nil, SMB_COM_SESSION_SETUP_ANDX);
329 p8(p, 0xFF); /* No secondary command */
330 p8(p, 0); /* Reserved (must be zero) */
331 pl16(p, 0); /* Offset to next command */
332 pl16(p, MTU); /* my max buffer size */
333 pl16(p, 1); /* my max multiplexed pending requests */
334 pl16(p, 0); /* Virtual connection # */
335 pl32(p, 0); /* Session key (if vc != 0) */
337 if(Debug && strstr(Debug, "auth") != nil)
338 fprint(2, "mycaps=%x\n", mycaps);
340 if((s->secmode & SECMODE_PW_ENCRYPT) == 0) {
341 if(Debug && strstr(Debug, "auth") != nil)
342 fprint(2, "user=%d %q\npass=%d %q\n", Sess->auth->len[0], Sess->auth->resp[0], Sess->auth->len[1], Sess->auth->resp[1]);
344 pl16(p, utflen(Sess->auth->resp[0])*2 + 2); /* passwd size */
345 pl16(p, utflen(Sess->auth->resp[0])*2 + 2); /* passwd size (UPPER CASE) */
346 pl32(p, 0); /* Reserved */
350 for(q = Sess->auth->resp[0]; *q; ){
351 q += chartorune(&r, q);
353 sysfatal("CIFSsession: '%C' utf too wide for windows\n", r);
354 pl16(p, toupperrune(r));
358 for(q = Sess->auth->resp[0]; *q; ){
359 q += chartorune(&r, q);
361 sysfatal("CIFSsession: '%C' utf too wide for windows\n", r);
366 if(Debug && strstr(Debug, "auth") != nil)
367 fprint(2, "encrypted len=%d,%d\n", Sess->auth->len[0], Sess->auth->len[1]);
369 pl16(p, Sess->auth->len[0]); /* LM passwd size */
370 pl16(p, Sess->auth->len[1]); /* NTLM passwd size */
371 pl32(p, 0); /* Reserved */
375 pmem(p, Sess->auth->resp[0], Sess->auth->len[0]);
376 pmem(p, Sess->auth->resp[1], Sess->auth->len[1]);
379 if(Debug && strstr(Debug, "auth") != nil)
380 fprint(2, "user=%q\nwindom=%q\nos=%s\nmanager=%s\n", Sess->auth->user, Sess->auth->windom, "plan9", argv0);
382 pstr(p, Sess->auth->user); /* Account name */
383 pstr(p, Sess->auth->windom); /* Primary domain */
384 pstr(p, "plan9"); /* Client OS */
385 pstr(p, argv0); /* Client LAN Manager type */
387 if(cifsrpc(p) == -1){
392 g8(p); /* Reserved (0) */
393 gl16(p); /* Offset to next command wordcount */
394 Sess->isguest = gl16(p) & 1; /* logged in as guest */
398 /* no security blob here - we don't understand extended security anyway */
399 gstr(p, os, sizeof os);
400 s->remos = estrdup9p(os);
407 CIFStreeconnect(Session *s, char *cname, char *tree, Share *sp)
414 resp = Sess->auth->resp[0];
415 len = Sess->auth->len[0];
416 if((s->secmode & SECMODE_USER) != SECMODE_USER){
417 memset(zeros, 0, sizeof zeros);
422 p = cifshdr(s, nil, SMB_COM_TREE_CONNECT_ANDX);
423 p8(p, 0xFF); /* Secondary command */
424 p8(p, 0); /* Reserved */
425 pl16(p, 0); /* Offset to next Word Count */
426 pl16(p, 0); /* Flags */
428 if((s->secmode & SECMODE_PW_ENCRYPT) == 0){
429 pl16(p, len+1); /* password len, including null */
438 path = smprint("//%s/%s", cname, tree);
440 ppath(p, path); /* path */
443 pascii(p, "?????"); /* service type any (so we can do RAP calls) */
445 if(cifsrpc(p) == -1){
449 g8(p); /* Secondary command */
450 g8(p); /* Reserved */
451 gl16(p); /* Offset to next command */
452 sp->options = g8(p); /* options supported */
453 sp->tid = p->tid; /* get received TID from packet header */
459 CIFSlogoff(Session *s)
464 p = cifshdr(s, nil, SMB_COM_LOGOFF_ANDX);
465 p8(p, 0xFF); /* No ANDX command */
466 p8(p, 0); /* Reserved (must be zero) */
467 pl16(p, 0); /* offset ot ANDX */
476 CIFStreedisconnect(Session *s, Share *sp)
481 p = cifshdr(s, sp, SMB_COM_TREE_DISCONNECT);
491 CIFSdeletefile(Session *s, Share *sp, char *name)
496 p = cifshdr(s, sp, SMB_COM_DELETE);
497 pl16(p, ATTR_HIDDEN|ATTR_SYSTEM); /* search attributes */
499 p8(p, STR_ASCII); /* buffer format */
508 CIFSdeletedirectory(Session *s, Share *sp, char *name)
513 p = cifshdr(s, sp, SMB_COM_DELETE_DIRECTORY);
515 p8(p, STR_ASCII); /* buffer format */
524 CIFScreatedirectory(Session *s, Share *sp, char *name)
529 p = cifshdr(s, sp, SMB_COM_CREATE_DIRECTORY);
540 CIFSrename(Session *s, Share *sp, char *old, char *new)
545 p = cifshdr(s, sp, SMB_COM_RENAME);
546 pl16(p, ATTR_HIDDEN|ATTR_SYSTEM|ATTR_DIRECTORY); /* search attributes */
559 /* for NT4/Win2k/XP */
561 CIFS_NT_opencreate(Session *s, Share *sp, char *name, int flags, int options,
562 int attrs, int access, int share, int action, int *result, FInfo *fi)
567 p = cifshdr(s, sp, SMB_COM_NT_CREATE_ANDX);
568 p8(p, 0xFF); /* Secondary command */
569 p8(p, 0); /* Reserved */
570 pl16(p, 0); /* Offset to next command */
571 p8(p, 0); /* Reserved */
572 pl16(p, utflen(name) *2); /* file name len */
573 pl32(p, flags); /* Flags */
574 pl32(p, 0); /* fid of cwd, if relative path */
575 pl32(p, access); /* access desired */
576 pl64(p, 0); /* initial allocation size */
577 pl32(p, attrs); /* Extended attributes */
578 pl32(p, share); /* Share Access */
579 pl32(p, action); /* What to do on success/failure */
580 pl32(p, options); /* Options */
581 pl32(p, SECURITY_IMPERSONATION); /* Impersonation level */
582 p8(p, SECURITY_CONTEXT_TRACKING | SECURITY_EFFECTIVE_ONLY); /* security flags */
584 p8(p, 0); /* FIXME: padding? */
585 ppath(p, name); /* filename */
587 if(cifsrpc(p) == -1){
592 memset(fi, 0, sizeof(FInfo));
593 g8(p); /* Secondary command */
594 g8(p); /* Reserved */
595 gl16(p); /* Offset to next command */
596 g8(p); /* oplock granted */
597 fh = gl16(p); /* FID for opened object */
598 *result = gl32(p); /* create action taken */
599 gl64(p); /* creation time */
600 fi->accessed = gvtime(p); /* last access time */
601 fi->written = gvtime(p); /* last written time */
602 fi->changed = gvtime(p); /* change time */
603 fi->attribs = gl32(p); /* extended attributes */
604 gl64(p); /* bytes allocated */
605 fi->size = gl64(p); /* file size */
611 /* for Win95/98/ME */
612 CIFS_SMB_opencreate(Session *s, Share *sp, char *name, int access,
613 int attrs, int action, int *result)
618 p = cifshdr(s, sp, SMB_COM_OPEN_ANDX);
619 p8(p, 0xFF); /* Secondary command */
620 p8(p, 0); /* Reserved */
621 pl16(p, 0); /* Offset to next command */
622 pl16(p, 0); /* Flags (0 == no stat(2) info) */
623 pl16(p, access); /* desired access */
624 pl16(p, ATTR_HIDDEN|ATTR_SYSTEM);/* search attributes */
625 pl16(p, attrs); /* file attribytes */
626 pdatetime(p, 0); /* creation time (0 == now) */
627 pl16(p, action); /* What to do on success/failure */
628 pl32(p, 0); /* allocation size */
629 pl32(p, 0); /* reserved */
630 pl32(p, 0); /* reserved */
632 ppath(p, name); /* filename */
634 if(cifsrpc(p) == -1){
639 g8(p); /* Secondary command */
640 g8(p); /* Reserved */
641 gl16(p); /* Offset to next command */
642 fh = gl16(p); /* FID for opened object */
643 gl16(p); /* extended attributes */
644 gvtime(p); /* last written time */
645 gl32(p); /* file size */
646 gl16(p); /* file type (disk/fifo/printer etc) */
647 gl16(p); /* device status (for fifos) */
648 *result = gl16(p); /* access granted */
655 CIFSwrite(Session *s, Share *sp, int fh, uvlong off, void *buf, vlong n)
660 /* FIXME: Payload should be padded to long boundary */
661 assert((n & 0xffffffff00000000LL) == 0 || s->caps & CAP_LARGE_FILES);
662 assert((off & 0xffffffff00000000LL) == 0 || s->caps & CAP_LARGE_FILES);
663 assert(n < s->mtu - T2HDRLEN || s->caps & CAP_LARGE_WRITEX);
665 p = cifshdr(s, sp, SMB_COM_WRITE_ANDX);
666 p8(p, 0xFF); /* Secondary command */
667 p8(p, 0); /* Reserved */
668 pl16(p, 0); /* Offset to next command */
669 pl16(p, fh); /* File handle */
670 pl32(p, off & 0xffffffff); /* LSBs of Offset */
671 pl32(p, 0); /* Reserved (0) */
672 pl16(p, s->nocache); /* Write mode (0 - write through) */
673 pl16(p, 0); /* Bytes remaining */
674 pl16(p, n >> 16); /* MSBs of length */
675 pl16(p, n & 0xffffffff); /* LSBs of length */
676 pl16(p, T2HDRLEN); /* Offset to data, in bytes */
677 pl32(p, off >> 32); /* MSBs of offset */
680 p->pos = p->buf +T2HDRLEN +NBHDRLEN;
681 pmem(p, buf, n); /* Data */
683 if(cifsrpc(p) == -1){
688 g8(p); /* Secondary command */
689 g8(p); /* Reserved */
690 gl16(p); /* Offset to next command */
691 got = gl16(p); /* LSWs of bytes written */
692 gl16(p); /* remaining (space ?) */
693 got |= (gl16(p) << 16); /* MSWs of bytes written */
700 CIFSread(Session *s, Share *sp, int fh, uvlong off, void *buf, vlong n,
707 assert((n & 0xffffffff00000000LL) == 0 || s->caps & CAP_LARGE_FILES);
708 assert((off & 0xffffffff00000000LL) == 0 || s->caps & CAP_LARGE_FILES);
709 assert(n < s->mtu - T2HDRLEN || s->caps & CAP_LARGE_READX);
711 p = cifshdr(s, sp, SMB_COM_READ_ANDX);
712 p8(p, 0xFF); /* Secondary command */
713 p8(p, 0); /* Reserved */
714 pl16(p, 0); /* Offset to next command */
715 pl16(p, fh); /* File handle */
716 pl32(p, off & 0xffffffff); /* Offset to beginning of write */
717 pl16(p, n); /* Maximum number of bytes to return */
718 pl16(p, minlen); /* Minimum number of bytes to return */
719 pl32(p, (uint)n >> 16); /* MSBs of maxlen */
720 pl16(p, 0); /* Bytes remaining to satisfy request */
721 pl32(p, off >> 32); /* MS 32 bits of offset */
724 if(cifsrpc(p) == -1){
729 g8(p); /* Secondary command */
730 g8(p); /* Reserved */
731 gl16(p); /* Offset to next command */
732 gl16(p); /* Remaining */
733 gl16(p); /* Compression mode */
734 gl16(p); /* Reserved */
735 got = gl16(p); /* length */
736 doff = gl16(p); /* Offset from header to data */
737 got |= gl16(p) << 16;
739 p->pos = p->buf + doff + NBHDRLEN;
741 gmem(p, buf, got); /* data */
747 CIFSflush(Session *s, Share *sp, int fh)
752 p = cifshdr(s, sp, SMB_COM_FLUSH);
753 pl16(p, fh); /* fid */
762 * Setting the time of last write to -1 gives "now" if the file
763 * was written and leaves it the same if the file wasn't written.
766 CIFSclose(Session *s, Share *sp, int fh)
771 p = cifshdr(s, sp, SMB_COM_CLOSE);
772 pl16(p, fh); /* fid */
773 pl32(p, ~0L); /* Time of last write (none) */
783 CIFSfindclose2(Session *s, Share *sp, int sh)
788 p = cifshdr(s, sp, SMB_COM_FIND_CLOSE2);
789 pl16(p, sh); /* sid */
804 p = cifshdr(s, nil, SMB_COM_ECHO);
805 pl16(p, 1); /* number of replies */
807 pascii(p, "abcdefghijklmnopqrstuvwxyz"); /* data */
816 CIFSsetinfo(Session *s, Share *sp, char *path, FInfo *fip)
821 p = cifshdr(s, sp, SMB_COM_SET_INFORMATION);
822 pl16(p, fip->attribs);
823 pl32(p, time(nil) - s->tz); /* modified time */
824 pl64(p, 0); /* reserved */
825 pl16(p, 0); /* reserved */
828 p8(p, STR_ASCII); /* buffer format */