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() */
134 char m[nelem(magic)];
139 p->pos = p->bytebase;
140 pl16(p, pos - (p->bytebase + 2)); /* 2 = sizeof bytecount */
144 if(p->s->secmode & SECMODE_SIGN_ENABLED)
147 qlock(&p->s->rpclock);
149 qunlock(&p->s->rpclock);
151 if(got < 32+NBHDRLEN){
152 werrstr("cifs packet too small (%d < %d)\n", got, 32+NBHDRLEN);
156 gmem(p, m, nelem(magic));
157 if(memcmp(m, magic, nelem(magic)) != 0){
158 werrstr("cifsrpc: bad magic number in packet 0x%02ux%02ux%02ux%02ux",
159 m[0], m[1], m[2], m[3]);
163 reply = g8(p); /* cmd */
164 err = gl32(p); /* errcode */
166 p->flags2 = gl16(p); /* flags2 */
167 gl16(p); /* PID MS bits */
168 seq = gl32(p); /* reserved */
169 gl32(p); /* MAC (if in use) */
170 gl16(p); /* Padding */
171 tid = gl16(p); /* TID */
172 gl16(p); /* PID lsbs */
173 uid = gl16(p); /* UID */
175 g8(p); /* word count */
177 if(reply != p->request){
178 fprint(2, "unexpected reply (cmd=%x/%x seq=%d/%d)\n",
179 reply, p->request, seq, p->seq);
183 if(p->s->secmode & SECMODE_SIGN_ENABLED){
184 if(macsign(p, p->seq+1) != 0 && p->s->seqrun){
185 werrstr("cifsrpc: invalid packet signature");
186 print("MAC signature bad\n");
187 // FIXME: for debug only return -1;
191 * We allow the sequence number of zero as some old samba
192 * servers seem to fall back to this unexpectedly
193 * after reporting sequence numbers correctly for a while.
195 * Some other samba servers seem to always report a sequence
196 * number of zero if MAC signing is disabled, so we have to
199 if(p->s->seqrun && seq != p->seq && seq != 0){
200 werrstr("bad sequence number (%d != %d)\n", p->seq, seq);
206 if(p->s->uid == NO_UID)
209 if(p->flags2 & FL2_NT_ERRCODES){
210 /* is it a real error rather than info/warning/chatter? */
211 if((err & 0xF0000000) == 0xC0000000){
212 werrstr("%s", nterrstr(err));
217 werrstr("%s", doserrstr(err));
226 * Some older servers (old samba) prefer to talk older
227 * dialects but if given no choice they will talk the
228 * more modern ones, so we don't give them the choice.
231 CIFSnegotiate(Session *s, long *svrtime, char *domain, int domlen, char *cname,
235 char *ispeak = "NT LM 0.12";
236 static char *dialects[] = {
237 // { "PC NETWORK PROGRAM 1.0"},
238 // { "MICROSOFT NETWORKS 1.03"},
239 // { "MICROSOFT NETWORKS 3.0"},
242 // { "NT LANMAN 1.0"},
248 * This should not be necessary, however the XP seems to use
249 * Unicode strings in its Negoiate response, but not set the
250 * Flags2 UNICODE flag.
252 * It does however echo back the FL_UNICODE flag we set in the
253 * flags2 negoiate request.
255 * The bodge is to force FL_UNICODE for this single request,
256 * clearing it after. Later we set FL2_UNICODE if the server
257 * agrees to CAP_UNICODE as it "should" be done.
259 s->flags2 |= FL2_UNICODE;
260 p = cifshdr(s, nil, SMB_COM_NEGOTIATE);
261 s->flags2 &= ~FL2_UNICODE;
264 for(i = 0; i < nelem(dialects); i++){
266 pascii(p, dialects[i]);
269 if(cifsrpc(p) == -1){
275 if(d < 0 || d > nelem(dialects)){
276 werrstr("no CIFS dialect in common");
281 if(strcmp(dialects[d], ispeak) != 0){
282 werrstr("%s dialect unsupported", dialects[d]);
287 s->secmode = g8(p); /* Security mode */
289 gl16(p); /* Max outstanding requests */
290 gl16(p); /* Max VCs */
291 s->mtu = gl32(p); /* Max buffer size */
292 gl32(p); /* Max raw buffer size (depricated) */
293 gl32(p); /* Session key */
294 s->caps = gl32(p); /* Server capabilities */
295 *svrtime = gvtime(p); /* fileserver time */
296 s->tz = (short)gl16(p) * 60; /* TZ in mins, is signed (SNIA doc is wrong) */
297 s->challen = g8(p); /* Encryption key length */
299 gmem(p, s->chal, s->challen); /* Get the challenge */
300 gstr(p, domain, domlen); /* source domain */
302 { /* NetApp Filer seem not to report its called name */
303 char *cn = emalloc9p(cnamlen);
305 gstr(p, cn, cnamlen); /* their name */
307 memcpy(cname, cn, cnamlen);
311 if(s->caps & CAP_UNICODE)
312 s->flags2 |= FL2_UNICODE;
319 CIFSsession(Session *s)
325 mycaps = CAP_UNICODE | CAP_LARGE_FILES | CAP_NT_SMBS |
326 CAP_NT_FIND | CAP_STATUS32,
329 s->seqrun = 1; /* activate the sequence number generation/checking */
331 p = cifshdr(s, nil, SMB_COM_SESSION_SETUP_ANDX);
332 p8(p, 0xFF); /* No secondary command */
333 p8(p, 0); /* Reserved (must be zero) */
334 pl16(p, 0); /* Offset to next command */
335 pl16(p, MTU); /* my max buffer size */
336 pl16(p, 1); /* my max multiplexed pending requests */
337 pl16(p, 0); /* Virtual connection # */
338 pl32(p, 0); /* Session key (if vc != 0) */
341 if((s->secmode & SECMODE_PW_ENCRYPT) == 0) {
342 pl16(p, utflen(Sess->auth->resp[0])*2 + 2); /* passwd size */
343 pl16(p, utflen(Sess->auth->resp[0])*2 + 2); /* passwd size (UPPER CASE) */
344 pl32(p, 0); /* Reserved */
348 for(q = Sess->auth->resp[0]; *q; ){
349 q += chartorune(&r, q);
350 pl16(p, toupperrune(r));
354 for(q = Sess->auth->resp[0]; *q; ){
355 q += chartorune(&r, q);
360 pl16(p, Sess->auth->len[0]); /* LM passwd size */
361 pl16(p, Sess->auth->len[1]); /* NTLM passwd size */
362 pl32(p, 0); /* Reserved */
366 pmem(p, Sess->auth->resp[0], Sess->auth->len[0]);
367 pmem(p, Sess->auth->resp[1], Sess->auth->len[1]);
370 pstr(p, Sess->auth->user); /* Account name */
371 pstr(p, Sess->auth->windom); /* Primary domain */
372 pstr(p, "plan9"); /* Client OS */
373 pstr(p, argv0); /* Client LAN Manager type */
375 if(cifsrpc(p) == -1){
380 g8(p); /* Reserved (0) */
381 gl16(p); /* Offset to next command wordcount */
382 Sess->isguest = gl16(p) & 1; /* logged in as guest */
386 /* no security blob here - we don't understand extended security anyway */
387 gstr(p, os, sizeof os);
388 s->remos = estrdup9p(os);
395 CIFStreeconnect(Session *s, char *cname, char *tree, Share *sp)
402 resp = Sess->auth->resp[0];
403 len = Sess->auth->len[0];
404 if((s->secmode & SECMODE_USER) != SECMODE_USER){
405 memset(zeros, 0, sizeof zeros);
410 p = cifshdr(s, nil, SMB_COM_TREE_CONNECT_ANDX);
411 p8(p, 0xFF); /* Secondary command */
412 p8(p, 0); /* Reserved */
413 pl16(p, 0); /* Offset to next Word Count */
414 pl16(p, 0); /* Flags */
416 if((s->secmode & SECMODE_PW_ENCRYPT) == 0){
417 pl16(p, len+1); /* password len, including null */
426 path = smprint("//%s/%s", cname, tree);
428 ppath(p, path); /* path */
431 pascii(p, "?????"); /* service type any (so we can do RAP calls) */
433 if(cifsrpc(p) == -1){
437 g8(p); /* Secondary command */
438 g8(p); /* Reserved */
439 gl16(p); /* Offset to next command */
440 sp->options = g8(p); /* options supported */
441 sp->tid = p->tid; /* get received TID from packet header */
447 CIFSlogoff(Session *s)
452 p = cifshdr(s, nil, SMB_COM_LOGOFF_ANDX);
453 p8(p, 0xFF); /* No ANDX command */
454 p8(p, 0); /* Reserved (must be zero) */
455 pl16(p, 0); /* offset ot ANDX */
464 CIFStreedisconnect(Session *s, Share *sp)
469 p = cifshdr(s, sp, SMB_COM_TREE_DISCONNECT);
479 CIFSdeletefile(Session *s, Share *sp, char *name)
484 p = cifshdr(s, sp, SMB_COM_DELETE);
485 pl16(p, ATTR_HIDDEN|ATTR_SYSTEM); /* search attributes */
487 p8(p, STR_ASCII); /* buffer format */
496 CIFSdeletedirectory(Session *s, Share *sp, char *name)
501 p = cifshdr(s, sp, SMB_COM_DELETE_DIRECTORY);
503 p8(p, STR_ASCII); /* buffer format */
512 CIFScreatedirectory(Session *s, Share *sp, char *name)
517 p = cifshdr(s, sp, SMB_COM_CREATE_DIRECTORY);
528 CIFSrename(Session *s, Share *sp, char *old, char *new)
533 p = cifshdr(s, sp, SMB_COM_RENAME);
534 pl16(p, ATTR_HIDDEN|ATTR_SYSTEM|ATTR_DIRECTORY); /* search attributes */
547 /* for NT4/Win2k/XP */
549 CIFS_NT_opencreate(Session *s, Share *sp, char *name, int flags, int options,
550 int attrs, int access, int share, int action, int *result, FInfo *fi)
555 p = cifshdr(s, sp, SMB_COM_NT_CREATE_ANDX);
556 p8(p, 0xFF); /* Secondary command */
557 p8(p, 0); /* Reserved */
558 pl16(p, 0); /* Offset to next command */
559 p8(p, 0); /* Reserved */
560 pl16(p, utflen(name) *2); /* file name len */
561 pl32(p, flags); /* Flags */
562 pl32(p, 0); /* fid of cwd, if relative path */
563 pl32(p, access); /* access desired */
564 pl64(p, 0); /* initial allocation size */
565 pl32(p, attrs); /* Extended attributes */
566 pl32(p, share); /* Share Access */
567 pl32(p, action); /* What to do on success/failure */
568 pl32(p, options); /* Options */
569 pl32(p, SECURITY_IMPERSONATION); /* Impersonation level */
570 p8(p, SECURITY_CONTEXT_TRACKING | SECURITY_EFFECTIVE_ONLY); /* security flags */
572 p8(p, 0); /* FIXME: padding? */
573 ppath(p, name); /* filename */
575 if(cifsrpc(p) == -1){
580 memset(fi, 0, sizeof(FInfo));
581 g8(p); /* Secondary command */
582 g8(p); /* Reserved */
583 gl16(p); /* Offset to next command */
584 g8(p); /* oplock granted */
585 fh = gl16(p); /* FID for opened object */
586 *result = gl32(p); /* create action taken */
587 gl64(p); /* creation time */
588 fi->accessed = gvtime(p); /* last access time */
589 fi->written = gvtime(p); /* last written time */
590 fi->changed = gvtime(p); /* change time */
591 fi->attribs = gl32(p); /* extended attributes */
592 gl64(p); /* bytes allocated */
593 fi->size = gl64(p); /* file size */
599 /* for Win95/98/ME */
600 CIFS_SMB_opencreate(Session *s, Share *sp, char *name, int access,
601 int attrs, int action, int *result)
606 p = cifshdr(s, sp, SMB_COM_OPEN_ANDX);
607 p8(p, 0xFF); /* Secondary command */
608 p8(p, 0); /* Reserved */
609 pl16(p, 0); /* Offset to next command */
610 pl16(p, 0); /* Flags (0 == no stat(2) info) */
611 pl16(p, access); /* desired access */
612 pl16(p, ATTR_HIDDEN|ATTR_SYSTEM);/* search attributes */
613 pl16(p, attrs); /* file attribytes */
614 pdatetime(p, 0); /* creation time (0 == now) */
615 pl16(p, action); /* What to do on success/failure */
616 pl32(p, 0); /* allocation size */
617 pl32(p, 0); /* reserved */
618 pl32(p, 0); /* reserved */
620 ppath(p, name); /* filename */
622 if(cifsrpc(p) == -1){
627 g8(p); /* Secondary command */
628 g8(p); /* Reserved */
629 gl16(p); /* Offset to next command */
630 fh = gl16(p); /* FID for opened object */
631 gl16(p); /* extended attributes */
632 gvtime(p); /* last written time */
633 gl32(p); /* file size */
634 gl16(p); /* file type (disk/fifo/printer etc) */
635 gl16(p); /* device status (for fifos) */
636 *result = gl16(p); /* access granted */
643 CIFSwrite(Session *s, Share *sp, int fh, uvlong off, void *buf, vlong n)
648 /* FIXME: Payload should be padded to long boundary */
649 assert((n & 0xffffffff00000000LL) == 0 || s->caps & CAP_LARGE_FILES);
650 assert((off & 0xffffffff00000000LL) == 0 || s->caps & CAP_LARGE_FILES);
651 assert(n < s->mtu - T2HDRLEN || s->caps & CAP_LARGE_WRITEX);
653 p = cifshdr(s, sp, SMB_COM_WRITE_ANDX);
654 p8(p, 0xFF); /* Secondary command */
655 p8(p, 0); /* Reserved */
656 pl16(p, 0); /* Offset to next command */
657 pl16(p, fh); /* File handle */
658 pl32(p, off & 0xffffffff); /* LSBs of Offset */
659 pl32(p, 0); /* Reserved (0) */
660 pl16(p, s->nocache); /* Write mode (0 - write through) */
661 pl16(p, 0); /* Bytes remaining */
662 pl16(p, n >> 16); /* MSBs of length */
663 pl16(p, n & 0xffffffff); /* LSBs of length */
664 pl16(p, T2HDRLEN); /* Offset to data, in bytes */
665 pl32(p, off >> 32); /* MSBs of offset */
668 p->pos = p->buf +T2HDRLEN +NBHDRLEN;
669 pmem(p, buf, n); /* Data */
671 if(cifsrpc(p) == -1){
676 g8(p); /* Secondary command */
677 g8(p); /* Reserved */
678 gl16(p); /* Offset to next command */
679 got = gl16(p); /* LSWs of bytes written */
680 gl16(p); /* remaining (space ?) */
681 got |= (gl16(p) << 16); /* MSWs of bytes written */
688 CIFSread(Session *s, Share *sp, int fh, uvlong off, void *buf, vlong n,
695 assert((n & 0xffffffff00000000LL) == 0 || s->caps & CAP_LARGE_FILES);
696 assert((off & 0xffffffff00000000LL) == 0 || s->caps & CAP_LARGE_FILES);
697 assert(n < s->mtu - T2HDRLEN || s->caps & CAP_LARGE_READX);
699 p = cifshdr(s, sp, SMB_COM_READ_ANDX);
700 p8(p, 0xFF); /* Secondary command */
701 p8(p, 0); /* Reserved */
702 pl16(p, 0); /* Offset to next command */
703 pl16(p, fh); /* File handle */
704 pl32(p, off & 0xffffffff); /* Offset to beginning of write */
705 pl16(p, n); /* Maximum number of bytes to return */
706 pl16(p, minlen); /* Minimum number of bytes to return */
707 pl32(p, (uint)n >> 16); /* MSBs of maxlen */
708 pl16(p, 0); /* Bytes remaining to satisfy request */
709 pl32(p, off >> 32); /* MS 32 bits of offset */
712 if(cifsrpc(p) == -1){
717 g8(p); /* Secondary command */
718 g8(p); /* Reserved */
719 gl16(p); /* Offset to next command */
720 gl16(p); /* Remaining */
721 gl16(p); /* Compression mode */
722 gl16(p); /* Reserved */
723 got = gl16(p); /* length */
724 doff = gl16(p); /* Offset from header to data */
725 got |= gl16(p) << 16;
727 p->pos = p->buf + doff + NBHDRLEN;
729 gmem(p, buf, got); /* data */
735 CIFSflush(Session *s, Share *sp, int fh)
740 p = cifshdr(s, sp, SMB_COM_FLUSH);
741 pl16(p, fh); /* fid */
750 * Setting the time of last write to -1 gives "now" if the file
751 * was written and leaves it the same if the file wasn't written.
754 CIFSclose(Session *s, Share *sp, int fh)
759 p = cifshdr(s, sp, SMB_COM_CLOSE);
760 pl16(p, fh); /* fid */
761 pl32(p, ~0L); /* Time of last write (none) */
771 CIFSfindclose2(Session *s, Share *sp, int sh)
776 p = cifshdr(s, sp, SMB_COM_FIND_CLOSE2);
777 pl16(p, sh); /* sid */
792 p = cifshdr(s, nil, SMB_COM_ECHO);
793 pl16(p, 1); /* number of replies */
795 pascii(p, "abcdefghijklmnopqrstuvwxyz"); /* data */
804 CIFSsetinfo(Session *s, Share *sp, char *path, FInfo *fip)
809 p = cifshdr(s, sp, SMB_COM_SET_INFORMATION);
810 pl16(p, fip->attribs);
811 pl32(p, time(nil) - s->tz); /* modified time */
812 pl64(p, 0); /* reserved */
813 pl16(p, 0); /* reserved */
816 p8(p, STR_ASCII); /* buffer format */