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;
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[] = {
251 // { "PC NETWORK PROGRAM 1.0"},
252 // { "MICROSOFT NETWORKS 1.03"},
253 // { "MICROSOFT NETWORKS 3.0"},
256 // { "NT LANMAN 1.0"},
262 * This should not be necessary, however the XP seems to use
263 * Unicode strings in its Negoiate response, but not set the
264 * Flags2 UNICODE flag.
266 * It does however echo back the FL_UNICODE flag we set in the
267 * flags2 negoiate request.
269 * The bodge is to force FL_UNICODE for this single request,
270 * clearing it after. Later we set FL2_UNICODE if the server
271 * agrees to CAP_UNICODE as it "should" be done.
273 s->flags2 |= FL2_UNICODE;
274 p = cifshdr(s, nil, SMB_COM_NEGOTIATE);
275 s->flags2 &= ~FL2_UNICODE;
278 for(i = 0; i < nelem(dialects); i++){
280 pascii(p, dialects[i]);
283 if(cifsrpc(p) == -1){
289 if(d < 0 || d > nelem(dialects)){
290 werrstr("no CIFS dialect in common");
295 if(strcmp(dialects[d], ispeak) != 0){
296 werrstr("%s dialect unsupported", dialects[d]);
301 s->secmode = g8(p); /* Security mode */
303 gl16(p); /* Max outstanding requests */
304 gl16(p); /* Max VCs */
305 s->mtu = gl32(p); /* Max buffer size */
306 gl32(p); /* Max raw buffer size (depricated) */
307 gl32(p); /* Session key */
308 s->caps = gl32(p); /* Server capabilities */
309 *svrtime = gvtime(p); /* fileserver time */
310 s->tz = (short)gl16(p) * 60; /* TZ in mins, is signed (SNIA doc is wrong) */
311 s->challen = g8(p); /* Encryption key length */
313 gmem(p, s->chal, s->challen); /* Get the challenge */
314 gstr(p, domain, domlen); /* source domain */
316 { /* NetApp Filer seem not to report its called name */
317 char *cn = emalloc9p(cnamlen);
319 gstr(p, cn, cnamlen); /* their name */
321 memcpy(cname, cn, cnamlen);
325 if(s->caps & CAP_UNICODE)
326 s->flags2 |= FL2_UNICODE;
333 CIFSsession(Session *s)
339 mycaps = CAP_UNICODE | CAP_LARGE_FILES | CAP_NT_SMBS |
340 CAP_NT_FIND | CAP_STATUS32,
343 s->seqrun = 1; /* activate the sequence number generation/checking */
345 p = cifshdr(s, nil, SMB_COM_SESSION_SETUP_ANDX);
346 p8(p, 0xFF); /* No secondary command */
347 p8(p, 0); /* Reserved (must be zero) */
348 pl16(p, 0); /* Offset to next command */
349 pl16(p, MTU); /* my max buffer size */
350 pl16(p, 1); /* my max multiplexed pending requests */
351 pl16(p, 0); /* Virtual connection # */
352 pl32(p, 0); /* Session key (if vc != 0) */
355 if((s->secmode & SECMODE_PW_ENCRYPT) == 0) {
356 pl16(p, utflen(Sess->auth->resp[0])*2 + 2); /* passwd size */
357 pl16(p, utflen(Sess->auth->resp[0])*2 + 2); /* passwd size (UPPER CASE) */
358 pl32(p, 0); /* Reserved */
362 for(q = Sess->auth->resp[0]; *q; ){
363 q += chartorune(&r, q);
364 pl16(p, toupperrune(r));
368 for(q = Sess->auth->resp[0]; *q; ){
369 q += chartorune(&r, q);
374 pl16(p, Sess->auth->len[0]); /* LM passwd size */
375 pl16(p, Sess->auth->len[1]); /* NTLM passwd size */
376 pl32(p, 0); /* Reserved */
380 pmem(p, Sess->auth->resp[0], Sess->auth->len[0]);
381 pmem(p, Sess->auth->resp[1], Sess->auth->len[1]);
384 pstr(p, Sess->auth->user); /* Account name */
385 pstr(p, Sess->auth->windom); /* Primary domain */
386 pstr(p, "plan9"); /* Client OS */
387 pstr(p, argv0); /* Client LAN Manager type */
389 if(cifsrpc(p) == -1){
394 g8(p); /* Reserved (0) */
395 gl16(p); /* Offset to next command wordcount */
396 Sess->isguest = gl16(p) & 1; /* logged in as guest */
400 /* no security blob here - we don't understand extended security anyway */
401 gstr(p, os, sizeof os);
402 s->remos = estrdup9p(os);
409 CIFStreeconnect(Session *s, char *cname, char *tree, Share *sp)
416 resp = Sess->auth->resp[0];
417 len = Sess->auth->len[0];
418 if((s->secmode & SECMODE_USER) != SECMODE_USER){
419 memset(zeros, 0, sizeof zeros);
424 p = cifshdr(s, nil, SMB_COM_TREE_CONNECT_ANDX);
425 p8(p, 0xFF); /* Secondary command */
426 p8(p, 0); /* Reserved */
427 pl16(p, 0); /* Offset to next Word Count */
428 pl16(p, 0); /* Flags */
430 if((s->secmode & SECMODE_PW_ENCRYPT) == 0){
431 pl16(p, len+1); /* password len, including null */
440 path = smprint("//%s/%s", cname, tree);
442 ppath(p, path); /* path */
445 pascii(p, "?????"); /* service type any (so we can do RAP calls) */
447 if(cifsrpc(p) == -1){
451 g8(p); /* Secondary command */
452 g8(p); /* Reserved */
453 gl16(p); /* Offset to next command */
454 sp->options = g8(p); /* options supported */
455 sp->tid = p->tid; /* get received TID from packet header */
461 CIFSlogoff(Session *s)
466 p = cifshdr(s, nil, SMB_COM_LOGOFF_ANDX);
467 p8(p, 0xFF); /* No ANDX command */
468 p8(p, 0); /* Reserved (must be zero) */
469 pl16(p, 0); /* offset ot ANDX */
478 CIFStreedisconnect(Session *s, Share *sp)
483 p = cifshdr(s, sp, SMB_COM_TREE_DISCONNECT);
493 CIFSdeletefile(Session *s, Share *sp, char *name)
498 p = cifshdr(s, sp, SMB_COM_DELETE);
499 pl16(p, ATTR_HIDDEN|ATTR_SYSTEM); /* search attributes */
501 p8(p, STR_ASCII); /* buffer format */
510 CIFSdeletedirectory(Session *s, Share *sp, char *name)
515 p = cifshdr(s, sp, SMB_COM_DELETE_DIRECTORY);
517 p8(p, STR_ASCII); /* buffer format */
526 CIFScreatedirectory(Session *s, Share *sp, char *name)
531 p = cifshdr(s, sp, SMB_COM_CREATE_DIRECTORY);
542 CIFSrename(Session *s, Share *sp, char *old, char *new)
547 p = cifshdr(s, sp, SMB_COM_RENAME);
548 pl16(p, ATTR_HIDDEN|ATTR_SYSTEM|ATTR_DIRECTORY); /* search attributes */
561 /* for NT4/Win2k/XP */
563 CIFS_NT_opencreate(Session *s, Share *sp, char *name, int flags, int options,
564 int attrs, int access, int share, int action, int *result, FInfo *fi)
569 p = cifshdr(s, sp, SMB_COM_NT_CREATE_ANDX);
570 p8(p, 0xFF); /* Secondary command */
571 p8(p, 0); /* Reserved */
572 pl16(p, 0); /* Offset to next command */
573 p8(p, 0); /* Reserved */
574 pl16(p, utflen(name) *2); /* file name len */
575 pl32(p, flags); /* Flags */
576 pl32(p, 0); /* fid of cwd, if relative path */
577 pl32(p, access); /* access desired */
578 pl64(p, 0); /* initial allocation size */
579 pl32(p, attrs); /* Extended attributes */
580 pl32(p, share); /* Share Access */
581 pl32(p, action); /* What to do on success/failure */
582 pl32(p, options); /* Options */
583 pl32(p, SECURITY_IMPERSONATION); /* Impersonation level */
584 p8(p, SECURITY_CONTEXT_TRACKING | SECURITY_EFFECTIVE_ONLY); /* security flags */
586 p8(p, 0); /* FIXME: padding? */
587 ppath(p, name); /* filename */
589 if(cifsrpc(p) == -1){
594 memset(fi, 0, sizeof(FInfo));
595 g8(p); /* Secondary command */
596 g8(p); /* Reserved */
597 gl16(p); /* Offset to next command */
598 g8(p); /* oplock granted */
599 fh = gl16(p); /* FID for opened object */
600 *result = gl32(p); /* create action taken */
601 gl64(p); /* creation time */
602 fi->accessed = gvtime(p); /* last access time */
603 fi->written = gvtime(p); /* last written time */
604 fi->changed = gvtime(p); /* change time */
605 fi->attribs = gl32(p); /* extended attributes */
606 gl64(p); /* bytes allocated */
607 fi->size = gl64(p); /* file size */
613 /* for Win95/98/ME */
614 CIFS_SMB_opencreate(Session *s, Share *sp, char *name, int access,
615 int attrs, int action, int *result)
620 p = cifshdr(s, sp, SMB_COM_OPEN_ANDX);
621 p8(p, 0xFF); /* Secondary command */
622 p8(p, 0); /* Reserved */
623 pl16(p, 0); /* Offset to next command */
624 pl16(p, 0); /* Flags (0 == no stat(2) info) */
625 pl16(p, access); /* desired access */
626 pl16(p, ATTR_HIDDEN|ATTR_SYSTEM);/* search attributes */
627 pl16(p, attrs); /* file attribytes */
628 pdatetime(p, 0); /* creation time (0 == now) */
629 pl16(p, action); /* What to do on success/failure */
630 pl32(p, 0); /* allocation size */
631 pl32(p, 0); /* reserved */
632 pl32(p, 0); /* reserved */
634 ppath(p, name); /* filename */
636 if(cifsrpc(p) == -1){
641 g8(p); /* Secondary command */
642 g8(p); /* Reserved */
643 gl16(p); /* Offset to next command */
644 fh = gl16(p); /* FID for opened object */
645 gl16(p); /* extended attributes */
646 gvtime(p); /* last written time */
647 gl32(p); /* file size */
648 gl16(p); /* file type (disk/fifo/printer etc) */
649 gl16(p); /* device status (for fifos) */
650 *result = gl16(p); /* access granted */
657 CIFSwrite(Session *s, Share *sp, int fh, uvlong off, void *buf, vlong n)
662 /* FIXME: Payload should be padded to long boundary */
663 assert((n & 0xffffffff00000000LL) == 0 || s->caps & CAP_LARGE_FILES);
664 assert((off & 0xffffffff00000000LL) == 0 || s->caps & CAP_LARGE_FILES);
665 assert(n < s->mtu - T2HDRLEN || s->caps & CAP_LARGE_WRITEX);
667 p = cifshdr(s, sp, SMB_COM_WRITE_ANDX);
668 p8(p, 0xFF); /* Secondary command */
669 p8(p, 0); /* Reserved */
670 pl16(p, 0); /* Offset to next command */
671 pl16(p, fh); /* File handle */
672 pl32(p, off & 0xffffffff); /* LSBs of Offset */
673 pl32(p, 0); /* Reserved (0) */
674 pl16(p, s->nocache); /* Write mode (0 - write through) */
675 pl16(p, 0); /* Bytes remaining */
676 pl16(p, n >> 16); /* MSBs of length */
677 pl16(p, n & 0xffffffff); /* LSBs of length */
678 pl16(p, T2HDRLEN); /* Offset to data, in bytes */
679 pl32(p, off >> 32); /* MSBs of offset */
682 p->pos = p->buf +T2HDRLEN +NBHDRLEN;
683 pmem(p, buf, n); /* Data */
685 if(cifsrpc(p) == -1){
690 g8(p); /* Secondary command */
691 g8(p); /* Reserved */
692 gl16(p); /* Offset to next command */
693 got = gl16(p); /* LSWs of bytes written */
694 gl16(p); /* remaining (space ?) */
695 got |= (gl16(p) << 16); /* MSWs of bytes written */
702 CIFSread(Session *s, Share *sp, int fh, uvlong off, void *buf, vlong n,
709 assert((n & 0xffffffff00000000LL) == 0 || s->caps & CAP_LARGE_FILES);
710 assert((off & 0xffffffff00000000LL) == 0 || s->caps & CAP_LARGE_FILES);
711 assert(n < s->mtu - T2HDRLEN || s->caps & CAP_LARGE_READX);
713 p = cifshdr(s, sp, SMB_COM_READ_ANDX);
714 p8(p, 0xFF); /* Secondary command */
715 p8(p, 0); /* Reserved */
716 pl16(p, 0); /* Offset to next command */
717 pl16(p, fh); /* File handle */
718 pl32(p, off & 0xffffffff); /* Offset to beginning of write */
719 pl16(p, n); /* Maximum number of bytes to return */
720 pl16(p, minlen); /* Minimum number of bytes to return */
721 pl32(p, (uint)n >> 16); /* MSBs of maxlen */
722 pl16(p, 0); /* Bytes remaining to satisfy request */
723 pl32(p, off >> 32); /* MS 32 bits of offset */
726 if(cifsrpc(p) == -1){
731 g8(p); /* Secondary command */
732 g8(p); /* Reserved */
733 gl16(p); /* Offset to next command */
734 gl16(p); /* Remaining */
735 gl16(p); /* Compression mode */
736 gl16(p); /* Reserved */
737 got = gl16(p); /* length */
738 doff = gl16(p); /* Offset from header to data */
739 got |= gl16(p) << 16;
741 p->pos = p->buf + doff + NBHDRLEN;
743 gmem(p, buf, got); /* data */
749 CIFSflush(Session *s, Share *sp, int fh)
754 p = cifshdr(s, sp, SMB_COM_FLUSH);
755 pl16(p, fh); /* fid */
764 * Setting the time of last write to -1 gives "now" if the file
765 * was written and leaves it the same if the file wasn't written.
768 CIFSclose(Session *s, Share *sp, int fh)
773 p = cifshdr(s, sp, SMB_COM_CLOSE);
774 pl16(p, fh); /* fid */
775 pl32(p, ~0L); /* Time of last write (none) */
785 CIFSfindclose2(Session *s, Share *sp, int sh)
790 p = cifshdr(s, sp, SMB_COM_FIND_CLOSE2);
791 pl16(p, sh); /* sid */
806 p = cifshdr(s, nil, SMB_COM_ECHO);
807 pl16(p, 1); /* number of replies */
809 pascii(p, "abcdefghijklmnopqrstuvwxyz"); /* data */
818 CIFSsetinfo(Session *s, Share *sp, char *path, FInfo *fip)
823 p = cifshdr(s, sp, SMB_COM_SET_INFORMATION);
824 pl16(p, fip->attribs);
825 pl32(p, time(nil) - s->tz); /* modified time */
826 pl64(p, 0); /* reserved */
827 pl16(p, 0); /* reserved */
830 p8(p, STR_ASCII); /* buffer format */