8 NEGOTIATE_USER_SECURITY = 1,
9 NEGOTIATE_ENCRYPT_PASSWORDS = 2,
12 static Chalstate *smbcs;
13 static int sessionkey;
14 static int sessionuid;
15 static int negotiated;
18 smbnegotiate(Req *r, uchar *h, uchar *p, uchar *e)
20 uchar *d, *de, *c, *ce, dom[256];
24 if(!unpack(h, p, e, "#0b{*2}#1w[]", &d, &de)){
26 r->respond(r, STATUS_INVALID_SMB);
31 while(unpack(h, d, de, "_f.", smbstrunpack8, &s, &d)){
33 fprint(2, "[%d] %s\n", i, s);
34 if(x < 0 && !cistrcmp(s, "NT LM 0.12"))
49 if(smbcs = auth_challenge("proto=ntlm role=server")){
50 c = (uchar*)smbcs->chal;
51 ce = c + smbcs->nchal;
52 mode = NEGOTIATE_USER_SECURITY | NEGOTIATE_ENCRYPT_PASSWORDS;
54 logit("auth_challenge: %r");
58 * <89> Section 2.2.4.52.2: Windows NT servers always send the DomainName
59 * field in Unicode characters and never add a padding byte for alignment.
60 * Windows clients ignore the DomainName field in the server response.
63 de = dom + smbstrpack16(d, d, d + sizeof(dom), domain);
64 if(!pack(r->rh, r->rp, r->re, "#0b{*2wbwwllllvw#2b}#1w{[][]}.",
65 x, mode, 50, 1, BUFFERSIZE, 0x10000, sessionkey,
66 CAP_UNICODE | CAP_LARGEFILES |
67 CAP_NT_FIND | CAP_NT_SMBS | CAP_NT_STATUS,
68 tofiletime(time(0)), -tzoff/60, c, ce, d, de, &r->rp))
76 SMB_SETUP_USE_LANMAN_KEY = 2,
80 smbsessionsetupandx(Req *r, uchar *h, uchar *p, uchar *e)
82 uchar *lm, *lme, *nt, *nte, *xp;
83 char *user, *dom, *os, *lanman;
84 int xcmd, cap, bs, sk;
87 user = dom = os = lanman = nil;
88 if(!unpack(h, p, e, "#0b{*2b_@4ww____l#2w#3w____l}#1w{[][]ffff}{?.}",
89 &xcmd, &bs, &sk, &cap, &lm, &lme, &nt, &nte,
90 r->o->strunpack, &user, r->o->strunpack, &dom,
91 r->o->strunpack, &os, r->o->strunpack, &lanman, &xp)){
92 r->respond(r, STATUS_NOT_SUPPORTED);
96 fprint(2, "bs=%x cap=%x user=%s dom=%s os=%s lanman=%s\n",
97 bs, cap, user, dom, os, lanman);
99 logit("ignoring bad session key");
104 if(smbcs == nil || strlen(user) == 0)
108 smbcs->nresp = (nte - nt)+sizeof(*mcr)-sizeof(mcr->NTresp);
109 if(smbcs->nresp < sizeof(*mcr))
110 smbcs->nresp = sizeof(*mcr);
111 mcr = mallocz(smbcs->nresp, 1);
112 if((lme - lm) <= sizeof(mcr->LMresp))
113 memmove(mcr->LMresp, lm, lme - lm);
115 memmove(mcr->NTresp, nt, nte - nt);
117 ai = auth_response(smbcs);
119 logit("auth_response: %r");
121 break; /* allow retry with the same challenge */
123 if(auth_chuid(ai, nil) < 0)
124 logit("auth_chuid: %r");
126 auth_freechal(smbcs);
130 remoteuser = getuser();
131 logit("auth successfull");
134 sessionuid = (namehash(getuser()) & 0x7FFF) | 1;
136 if(bs >= 1024 || bs <= BUFFERSIZE)
137 remotebuffersize = bs;
138 if(!pack(r->rh, r->rp, r->re, "#0b{*2b_@2ww}#1w{fff}{.}",
139 xcmd, remoteuser ? 0 : SMB_SETUP_GUEST,
140 r->o->strpack, osname, r->o->strpack, progname,
141 r->o->strpack, domain, &r->rp))
142 r->respond(r, STATUS_INVALID_SMB);
144 smbcmd(r, xcmd, h, xp, e);
153 smblogoffandx(Req *r, uchar *h, uchar *p, uchar *e)
158 if(!unpack(h, p, e, "#0b{*2b_}#1w{}{?.}", &xcmd, &xp)){
160 r->respond(r, STATUS_NOT_SUPPORTED);
164 if(remoteuser && needauth)
170 if(!pack(r->rh, r->rp, r->re, "#0b{*2b_}#1w{}{.}", xcmd, &r->rp))
171 r->respond(r, STATUS_INVALID_SMB);
173 smbcmd(r, xcmd, h, xp, e);
177 SMB_SUPPORT_SEARCH_BITS = 1,
181 smbtreeconnectandx(Req *r, uchar *h, uchar *p, uchar *e)
183 int err, xcmd, flags;
184 char *path, *service;
185 uchar *pw, *pwe, *xp;
188 path = service = nil;
189 if(!unpack(h, p, e, "#0b{*2b_@3ww#2w}#1w{[]ff}{?.}",
190 &xcmd, &flags, &pw, &pwe, r->o->strunpack, &path,
191 smbstrunpack8, &service, &xp)){
192 r->respond(r, STATUS_NOT_SUPPORTED);
196 disconnecttree(r->tid);
199 if((t = connecttree(service, path, &err)) == nil){
203 if(!pack(r->rh, r->rp, r->re, "#0b{*2b_@2ww}#1w{ff}{.}",
204 xcmd, SMB_SUPPORT_SEARCH_BITS,
205 smbstrpack8, t->share->service,
206 r->o->strpack, t->share->fsname, &r->rp)){
207 disconnecttree(t->tid);
208 r->respond(r, STATUS_INVALID_SMB);
211 smbcmd(r, xcmd, h, xp, e);
219 READ_WRITE_LOCK = 0x00,
221 OPLOCK_RELEASE = 0x02,
222 CHANGE_LOCKTYPE = 0x04,
228 smblockingandx(Req *r, uchar *h, uchar *p, uchar *e)
230 int i, err, xcmd, fid, tol, timeout, nunlock, nlock, pid;
231 unsigned int loff, hoff, llen, hlen;
237 if(!unpack(h, p, e, "#0b{*2b_@2wwb_lww}#1w[]{?.}",
238 &xcmd, &fid, &tol, &timeout, &nunlock, &nlock, &d, &de, &xp)){
240 r->respond(r, STATUS_NOT_SUPPORTED);
243 if((f = getfile(r->tid, fid, nil, &err)) == nil){
248 fprint(2, "tol %x\ntimeout %d\nunnlock %d\nnlock %d\n", tol, timeout, nunlock, nlock);
249 if(tol & (SHARED_LOCK | CHANGE_LOCKTYPE))
251 for(i=0; i<nunlock+nlock; i++){
252 if(tol & LARGE_FILES){
253 if(!unpack(d, d, de, "w__llll[]", &pid, &hoff, &loff, &hlen, &llen, &d, nil))
256 if(!unpack(d, d, de, "wll[]", &pid, &loff, &llen, &d, nil))
260 off = (vlong)hoff<<32 | loff;
261 len = (vlong)hlen<<32 | llen;
263 fprint(2, "%s %x %llux %llux\n", (i < nunlock) ? "unlock" : "lock", pid, off, len);
265 if(!pack(r->rh, r->rp, r->re, "#0b{*2b_@2w}#1w{}{.}", xcmd, &r->rp))
266 r->respond(r, STATUS_INVALID_SMB);
268 smbcmd(r, xcmd, h, xp, e);
276 REQ_OPLOCK_BATCH = 0x04,
280 smbopenandx(Req *r, uchar *h, uchar *p, uchar *e)
282 int err, nfid, xcmd, flags, amode, omode, fattr, act, csize, ctime;
289 static int amode2dacc[] = {
291 [0x01] GENERIC_WRITE,
292 [0x02] GENERIC_READ | GENERIC_WRITE,
293 [0x03] GENERIC_EXECUTE,
295 [0x00] FILE_SHARE_COMPAT, /* compat */
296 [0x01] FILE_SHARE_NONE, /* exclusive use */
297 [0x02] FILE_SHARE_READ, /* deny write */
298 [0x03] FILE_SHARE_WRITE, /* deny read */
299 [0x04] FILE_SHARE_READ | FILE_SHARE_WRITE, /* shared read/write */
306 [0x02] FILE_OVERWRITE,
310 [0x12] FILE_OVERWRITE_IF,
317 if(!unpack(h, p, e, "#0b{*2b_@2www__wlwl________}#1w{f}{?.}",
318 &xcmd, &flags, &amode, &fattr, &ctime, &omode,
319 &csize, r->o->nameunpack, &name, &xp)){
320 r->respond(r, STATUS_NOT_SUPPORTED);
323 if((path = getpath(r->tid, name, &t, &err)) == nil)
325 if((f = createfile(path, r->namecmp,
326 amode2dacc[amode & 3], amode2sacc[(amode>>4) & 7], omode2cdisp[omode & 0x13],
327 FILE_NON_DIRECTORY_FILE, (ulong)csize, fattr,
328 &act, (flags & REQ_ATTRIB) ? &d : nil, &err)) == nil){
335 if(f->dacc & READMASK)
337 if(f->dacc & WRITEMASK)
339 if(!pack(r->rh, r->rp, r->re, "#0b{*2b_@2wwwllww__w______}#1w{}{.}",
341 !d ? 0 : dosfileattr(d),
342 !d ? 0 : d->mtime+tzoff,
343 !d ? 0 : filesize32(d->length),
346 !d ? 0 : act, &r->rp)){
348 r->respond(r, STATUS_INVALID_SMB);
350 smbcmd(r, xcmd, h, xp, e);
359 NT_CREATE_REQUEST_OPLOCK = 0x02,
360 NT_CREATE_REQUEST_OPBATCH = 0x04,
361 NT_CREATE_OPEN_TARGET_DIR = 0x08,
365 smbntcreatendx(Req *r, uchar *h, uchar *p, uchar *e)
367 int err, nfid, xcmd, flags, rootfid, fattr, dacc, sacc, cdisp, copt, act;
378 if(!unpack(h, p, e, "#0b{*2b_@2w___lllvllll_____}#1w{f}{?.}",
379 &xcmd, &flags, &rootfid, &dacc, &csize, &fattr, &sacc, &cdisp, &copt,
380 r->o->nameunpack, &name, &xp)){
381 r->respond(r, STATUS_NOT_SUPPORTED);
385 if((f = getfile(r->tid, rootfid, &t, &err)) == nil)
387 path = conspath(f->path, name);
389 } else if((path = getpath(r->tid, name, &t, &err)) == nil)
391 if((f = createfile(path, r->namecmp, dacc, sacc, cdisp, copt, csize, fattr, &act, &d, &err)) == nil){
397 if(!pack(r->rh, r->rp, r->re, "#0b{*2b_@2wbwlvvvvlvvw__b}#1w{}{.}",
398 xcmd, 0, nfid, act, tofiletime(d->mtime), tofiletime(d->atime),
399 tofiletime(d->mtime), tofiletime(d->mtime), extfileattr(d),
400 allocsize(d->length, t->share->blocksize),
401 d->length, f->rtype, (d->qid.type & QTDIR) != 0, &r->rp)){
403 r->respond(r, STATUS_INVALID_SMB);
405 smbcmd(r, xcmd, h, xp, e);
414 smbreadandx(Req *r, uchar *h, uchar *p, uchar *e)
416 int n, xcmd, fid, mincount, maxcount;
417 unsigned int loff, hoff;
418 uchar *rb, *rp, *re, *xp;
424 if((unpack(h, p, e, "#0b{*2b_@2wwlww______l}#1w{}{?.}",
425 &xcmd, &fid, &loff, &mincount, &maxcount, &hoff, &xp) == 0) &&
426 (unpack(h, p, e, "#0b{*2b_@2wwlww______}#1w{}{?.}",
427 &xcmd, &fid, &loff, &mincount, &maxcount, &xp) == 0)){
428 r->respond(r, STATUS_NOT_SUPPORTED);
431 if((f = getfile(r->tid, fid, nil, &n)) == nil){
435 if((f->fd < 0) || (f->dacc & READMASK) == 0){
436 r->respond(r, STATUS_ACCESS_DENIED);
439 /* dont really pack, just to get the pointer to the response data */
440 if(!pack(r->rh, r->rp, r->re, "#0b{*2________________________}#1w{%2.}", &rb)){
442 r->respond(r, STATUS_INVALID_SMB);
448 if(maxcount > mincount){
455 off = (vlong)hoff<<32 | loff;
457 if((n = pread(f->fd, rp, re - rp, off)) <= 0)
463 r->respond(r, smbmkerror());
466 if(!pack(r->rh, r->rp, r->re, "#0b{*2b_@3www__#2w@2w__________}#1w{%2[]}{.}",
467 xcmd, 0xFFFF, 0x0000, rb, rp, &r->rp))
469 smbcmd(r, xcmd, h, xp, e);
475 smbwriteandx(Req *r, uchar *h, uchar *p, uchar *e)
477 int n, xcmd, fid, bufoff, buflen;
478 unsigned int loff, hoff;
484 if((unpack(h, p, e, "#0b{*2b_@2wwl__________wwl}#1w{}{?.}",
485 &xcmd, &fid, &loff, &buflen, &bufoff, &hoff, &xp) == 0) &&
486 (unpack(h, p, e, "#0b{*2b_@2wwl__________ww}#1w{}{?.}",
487 &xcmd, &fid, &loff, &buflen, &bufoff, &xp) == 0)){
488 r->respond(r, STATUS_NOT_SUPPORTED);
495 r->respond(r, STATUS_INVALID_SMB);
498 if((f = getfile(r->tid, fid, nil, &n)) == nil){
502 if((f->fd < 0) || (f->dacc & WRITEMASK) == 0){
503 r->respond(r, STATUS_ACCESS_DENIED);
506 if((n = pwrite(f->fd, d, de - d, (vlong)hoff<<32 | loff)) < 0){
507 r->respond(r, smbmkerror());
510 if(!pack(r->rh, r->rp, r->re, "#0b{*2b_@2www____}#1w{}{.}", xcmd, n, 0xFFFF, &r->rp))
512 smbcmd(r, xcmd, h, xp, e);
518 smbwrite(Req *r, uchar *h, uchar *p, uchar *e)
520 int n, fid, count, bf;
526 if(!unpack(h, p, e, "#0b{*2wwl__}#1w{b#2w[]}", &fid, &count, &off, &bf, &d, &de)){
528 r->respond(r, STATUS_NOT_SUPPORTED);
533 if((f = getfile(r->tid, fid, nil, &n)) == nil){
537 if((f->fd < 0) || (f->dacc & WRITEMASK) == 0){
538 r->respond(r, STATUS_ACCESS_DENIED);
541 if(count != (de - d)){
542 r->respond(r, STATUS_INVALID_SMB);
545 if((n = pwrite(f->fd, d, count, off)) < 0){
546 r->respond(r, smbmkerror());
549 if(!pack(r->rh, r->rp, r->re, "#0b{*2w}#1w{}.", n, &r->rp))
550 r->respond(r, STATUS_INVALID_SMB);
558 smbcloseflush(Req *r, uchar *h, uchar *p, uchar *e)
567 if(!unpack(h, p, e, "#0b{*2w}#1w{}", &fid)){
568 r->respond(r, STATUS_NOT_SUPPORTED);
572 case 0x05: /* SMB_COM_FLUSH */
574 if(gettree(r->tid) == nil){
575 r->respond(r, STATUS_SMB_BAD_TID);
581 case 0x04: /* SMB_COM_CLOSE */
582 if((f = getfile(r->tid, fid, &t, &err)) == nil){
589 case 0x34: /* SMB_COM_FIND_CLOSE2 */
590 if((s = getfind(r->tid, fid, &t, &err)) == nil){
597 if(!pack(r->rh, r->rp, r->re, "#0b{*2}#1w{}.", &r->rp))
598 r->respond(r, STATUS_INVALID_SMB);
607 smbcreatedirectory(Req *r, uchar *h, uchar *p, uchar *e)
613 if(!unpack(h, p, e, "#0b{*2}#1w{_f}", r->o->nameunpack, &name)){
614 r->respond(r, STATUS_NOT_SUPPORTED);
617 if((path = getpath(r->tid, name, nil, &err)) == nil){
621 if(access(path, AEXIST) == 0){
622 r->respond(r, STATUS_OBJECT_NAME_COLLISION);
625 if((fd = create(path, OREAD, DMDIR | 0777)) < 0){
626 r->respond(r, smbmkerror());
630 if(!pack(r->rh, r->rp, r->re, "#0b{*2}#1w{}.", &r->rp))
631 r->respond(r, STATUS_INVALID_SMB);
640 smbrename(Req *r, uchar *h, uchar *p, uchar *e)
642 char *name1, *name2, *path1, *path2, *x, *y;
647 name1 = name2 = path1 = path2 = nil;
648 if(!unpack(h, p, e, "#0b{*2w}#1w{_f_f}", &sattr,
649 r->o->nameunpack, &name1, r->o->nameunpack, &name2)){
650 r->respond(r, STATUS_NOT_SUPPORTED);
653 if((path1 = getpath(r->tid, name1, nil, &err)) == nil){
657 if((path2 = getpath(r->tid, name2, nil, &err)) == nil){
661 if((d = xdirstat(&path1, r->namecmp)) == nil){
662 r->respond(r, smbmkerror());
665 if(!matchattr(d, sattr)){
666 r->respond(r, STATUS_NO_SUCH_FILE);
670 if(x = strrchr(path1, '/')){
674 r->respond(r, STATUS_OBJECT_PATH_SYNTAX_BAD);
677 if(y = strrchr(path2, '/'))
681 if(r->namecmp(path1, path2)){
682 r->respond(r, STATUS_NOT_SAME_DEVICE);
688 if(dirwstat(path1, &nd) < 0){
689 r->respond(r, smbmkerror());
692 if(!pack(r->rh, r->rp, r->re, "#0b{*2}#1w{}.", &r->rp))
693 r->respond(r, STATUS_INVALID_SMB);
696 xdirflush(path1, r->namecmp);
706 smbdelete(Req *r, uchar *h, uchar *p, uchar *e)
708 char *name, *path, *tmp;
709 int n, err, i, sattr;
715 if(!unpack(h, p, e, "#0b{*2w}#1w{_f}", &sattr, r->o->nameunpack, &name)){
716 r->respond(r, STATUS_NOT_SUPPORTED);
719 if((path = getpath(r->tid, name, nil, &err)) == nil){
724 if((f = openfind(path, r->namecmp, sattr, 0, &err)) == nil)
727 while((i = readfind(f, f->index, &d)) >= 0){
728 tmp = conspath(f->base, d->name);
739 err = STATUS_NO_SUCH_FILE;
742 if(!pack(r->rh, r->rp, r->re, "#0b{*2}#1w{}.", &r->rp))
743 r->respond(r, STATUS_INVALID_SMB);
753 smbdeletedirectory(Req *r, uchar *h, uchar *p, uchar *e)
760 if(!unpack(h, p, e, "#0b{*2}#1w{_f}", r->o->nameunpack, &name)){
761 r->respond(r, STATUS_NOT_SUPPORTED);
764 if((path = getpath(r->tid, name, nil, &err)) == nil){
768 if(remove(path) < 0){
770 if((d = xdirstat(&path, r->namecmp)) == nil){
775 if(remove(path) < 0){
776 r->respond(r, smbmkerror());
780 if(!pack(r->rh, r->rp, r->re, "#0b{*2}#1w{}.", &r->rp))
781 r->respond(r, STATUS_INVALID_SMB);
790 smbecho(Req *r, uchar *h, uchar *p, uchar *e)
795 if(!unpack(h, p, e, "#0b{*2w}#1w[]", &n, &d, &de)){
796 r->respond(r, STATUS_NOT_SUPPORTED);
799 if((r->tid != 0xFFFF) && (gettree(r->tid) == nil)){
800 r->respond(r, STATUS_SMB_BAD_TID);
804 for(i=0; i < n; i++){
805 if(!pack(r->rh, r->rp, r->re, "#0b{*2w}#1w[].", i, d, de, &r->rp)){
806 r->respond(r, STATUS_INVALID_SMB);
815 smbdisconnecttree(Req *r, uchar *h, uchar *p, uchar *e)
819 if(!unpack(h, p, e, "#0b{*2}#1w{}")){
820 r->respond(r, STATUS_NOT_SUPPORTED);
823 if(err = disconnecttree(r->tid)){
827 if(!pack(r->rh, r->rp, r->re, "#0b{*2}#1w{}.", &r->rp))
828 r->respond(r, STATUS_INVALID_SMB);
834 smbqueryinformation(Req *r, uchar *h, uchar *p, uchar *e)
842 if(!unpack(h, p, e, "#0b{*2}#1w{_f}", r->o->nameunpack, &name)){
843 r->respond(r, STATUS_NOT_SUPPORTED);
846 if((path = getpath(r->tid, name, nil, &err)) == nil){
850 if((d = xdirstat(&path, r->namecmp)) == nil){
851 r->respond(r, smbmkerror());
854 mtime = d->mtime + tzoff;
855 if(!pack(r->rh, r->rp, r->re, "#0b{*2wll__________}#1w{}.",
856 dosfileattr(d), mtime, filesize32(d->length), &r->rp))
857 r->respond(r, STATUS_INVALID_SMB);
867 smbsetinformation(Req *r, uchar *h, uchar *p, uchar *e)
870 int err, attr, mtime;
875 if(!unpack(h, p, e, "#0b{*2wl__________}#1w{_f}", &attr, &mtime, r->o->nameunpack, &name)){
876 r->respond(r, STATUS_NOT_SUPPORTED);
879 if((path = getpath(r->tid, name, nil, &err)) == nil){
883 if((d = xdirstat(&path, r->namecmp)) == nil){
884 r->respond(r, smbmkerror());
889 nd.mtime = mtime-tzoff;
891 if(attr & ATTR_READONLY){
895 if((nd.mode & 0222) == 0)
898 if(attr & ATTR_ARCHIVE)
902 if(nd.mode == d->mode)
904 if(dirwstat(path, &nd) < 0){
905 r->respond(r, smbmkerror());
908 if(!pack(r->rh, r->rp, r->re, "#0b{*2}#1w{}.", &r->rp))
909 r->respond(r, STATUS_INVALID_SMB);
912 xdirflush(path, r->namecmp);
920 smbcheckdirectory(Req *r, uchar *h, uchar *p, uchar *e)
928 if(!unpack(h, p, e, "#0b{*2}#1w{_f}", r->o->nameunpack, &name)){
929 r->respond(r, STATUS_NOT_SUPPORTED);
932 if((path = getpath(r->tid, name, nil, &err)) == nil){
936 if((d = xdirstat(&path, r->namecmp)) == nil){
937 r->respond(r, smbmkerror());
940 if((d->qid.type & QTDIR) == 0){
941 r->respond(r, STATUS_OBJECT_PATH_NOT_FOUND);
944 if(!pack(r->rh, r->rp, r->re, "#0b{*2}#1w{}.", &r->rp))
945 r->respond(r, STATUS_INVALID_SMB);
955 smbqueryinformation2(Req *r, uchar *h, uchar *p, uchar *e)
957 int err, fid, adate, atime, mdate, mtime;
965 if(!unpack(h, p, e, "#0b{*2w}#1w{}", &fid)){
966 r->respond(r, STATUS_NOT_SUPPORTED);
969 if((f = getfile(r->tid, fid, &t, &err)) == nil){
973 if((d = statfile(f)) == nil){
974 r->respond(r, smbmkerror());
977 todatetime(d->atime+tzoff, &adate, &atime);
978 todatetime(d->mtime+tzoff, &mdate, &mtime);
979 if(!pack(r->rh, r->rp, r->re, "#0b{*2wwwwwwllw}#1w{}.",
980 mdate, mtime, adate, atime, mdate, mtime,
981 filesize32(d->length), filesize32(allocsize(d->length, t->share->blocksize)),
982 dosfileattr(d), &r->rp))
983 r->respond(r, STATUS_INVALID_SMB);
992 smbqueryinformationdisk(Req *r, uchar *h, uchar *p, uchar *e)
997 if(!unpack(h, p, e, "#0b{*2}#1w{}")){
998 r->respond(r, STATUS_NOT_SUPPORTED);
1001 if((t = gettree(r->tid)) == nil){
1002 r->respond(r, STATUS_SMB_BAD_TID);
1006 if(!pack(r->rh, r->rp, r->re, "#0b{*2wwww__}#1w{}.",
1007 (int)(allocsize(s->allocsize + s->freesize, s->blocksize) / s->blocksize),
1008 s->blocksize / s->sectorsize, s->sectorsize,
1009 (int)(allocsize(s->freesize, s->blocksize) / s->blocksize), &r->rp))
1010 r->respond(r, STATUS_INVALID_SMB);
1016 fpackdir(Req *r, Dir *d, Tree *t, int i, int level, uchar *b, uchar *p, uchar *e, uchar **prevoff, uchar **nameoff)
1018 vlong atime, mtime, alen, dlen;
1019 uchar shortname[2*12];
1026 alen = allocsize(dlen, share->blocksize);
1027 atime = tofiletime(d->atime);
1028 mtime = tofiletime(d->mtime);
1029 memset(shortname, 0, sizeof(shortname));
1032 case 0x0101: /* SMB_FIND_FILE_DIRECTORY_INFO */
1033 n = pack(b, p, e, "llvvvvvvl#0l{.f}%4",
1034 0, i, mtime, atime, mtime, mtime, dlen, alen, extfileattr(d),
1035 &namep, r->o->untermnamepack, d->name);
1038 case 0x0102: /* SMB_FIND_FILE_FULL_DIRECTORY_INFO */
1039 n = pack(b, p, e, "llvvvvvvl#0ll{.f}%4",
1040 0, i, mtime, atime, mtime, mtime, dlen, alen, extfileattr(d), 0,
1041 &namep, r->o->untermnamepack, d->name);
1044 case 0x0103: /* SMB_FIND_FILE_NAMES_INFO */
1045 n = pack(b, p, e, "ll#0l{.f}%4",
1046 0, i, &namep, r->o->untermnamepack, d->name);
1049 case 0x0104: /* SMB_FIND_FILE_BOTH_DIRECTORY_INFO */
1050 n = pack(b, p, e, "llvvvvvvl#1l#2lb_[]{.f}{}____%4",
1051 0, i, mtime, atime, mtime, mtime, dlen, alen, extfileattr(d),
1052 0, shortname, shortname+sizeof(shortname),
1053 &namep, r->o->untermnamepack, d->name);
1057 logit("[%.4x] unknown FIND infolevel", level);
1064 if(prevoff && *prevoff)
1065 pack(b, *prevoff, e, "l", (int)(p - *prevoff));
1072 qpackdir(Req *, Dir *d, Tree *t, File *f, int level, uchar *b, uchar *p, uchar *e)
1074 vlong atime, mtime, dlen, alen;
1075 int link, delete, isdir;
1079 fprint(2, "QYERY level %.4x\n", level);
1083 alen = allocsize(dlen, share->blocksize);
1084 atime = tofiletime(d->atime);
1085 mtime = tofiletime(d->mtime);
1086 isdir = (d->qid.type & QTDIR) != 0;
1087 delete = f && deletedfile(f);
1091 case 0x0101: /* SMB_QUERY_FILE_BASIC_INFO */
1092 return pack(b, p, e, "vvvvl____", mtime, atime, mtime, mtime, extfileattr(d));
1094 case 0x0102: /* SMB_QUERY_FILE_STANDARD_INFO */
1095 return pack(b, p, e, "vvlbb", alen, dlen, link, delete, isdir);
1097 case 0x0103: /* SMB_QUERY_FILE_EA_INFO */
1098 return pack(b, p, e, "l", 0);
1100 case 0x0107: /* SMB_QUERY_FILE_ALL_INFO */
1101 return pack(b, p, e, "vvvvl____vvlbb__#1l#0l{f}{}",
1102 mtime, atime, mtime, mtime, extfileattr(d), alen, dlen, link, delete, isdir,
1103 smbuntermnamepack16, d->name);
1105 case 0x0109: /* SMB_QUERY_FILE_STREAM_INFO */
1108 return pack(b, p, e, "l#0lvv{f}", 0, dlen, alen, smbuntermstrpack16, "::$DATA");
1111 logit("[%.4x] unknown QUERY infolevel", level);
1117 trans2querypathinformation(Trans *t)
1126 if(!unpack(t->in.param.b, t->in.param.p, t->in.param.e, "w____f",
1127 &level, t->o->nameunpack, &name)){
1128 t->respond(t, STATUS_NOT_SUPPORTED);
1131 if((path = getpath(t->r->tid, name, &tree, &n)) == nil){
1135 if((d = xdirstat(&path, t->namecmp)) == nil){
1136 t->respond(t, smbmkerror());
1139 pack(t->out.param.b, t->out.param.p, t->out.param.e, "__.", &t->out.param.p);
1140 if((n = qpackdir(t->r, d, tree, nil, level, t->out.data.b, t->out.data.p, t->out.data.e)) < 0)
1141 t->respond(t, STATUS_OS2_INVALID_LEVEL);
1153 trans2queryfileinformation(Trans *t)
1162 if(!unpack(t->in.param.b, t->in.param.p, t->in.param.e, "ww", &fid, &level)){
1163 t->respond(t, STATUS_NOT_SUPPORTED);
1166 if((f = getfile(t->r->tid, fid, &tree, &n)) == nil){
1170 if((d = statfile(f)) == nil){
1171 t->respond(t, smbmkerror());
1174 pack(t->out.param.b, t->out.param.p, t->out.param.e, "__.", &t->out.param.p);
1175 if((n = qpackdir(t->r, d, tree, f, level, t->out.data.b, t->out.data.p, t->out.data.e)) < 0)
1176 t->respond(t, STATUS_OS2_INVALID_LEVEL);
1187 setfilepathinformation(Req *r, Dir *d, File *f, char *path, int level, uchar *b, uchar *p, uchar *e)
1189 int attr, adt, atm, mdt, mtm, delete;
1190 vlong len, atime, mtime;
1195 fprint(2, "SET level %.4x\n", level);
1197 case 0x0001: /* SMB_INFO_STANDARD */
1198 if(!unpack(b, p, e, "____wwww__________", &adt, &atm, &mdt, &mtm))
1200 nd.atime = fromdatetime(adt, atm)-tzoff;
1201 nd.mtime = fromdatetime(mdt, mtm)-tzoff;
1204 case 0x0101: /* SMB_SET_FILE_BASIC_INFO */
1205 if(f == nil || !unpack(b, p, e, "________vv________l____", &atime, &mtime, &attr))
1207 if(atime && atime != -1LL)
1208 nd.atime = fromfiletime(atime);
1209 if(mtime && mtime != -1LL)
1210 nd.mtime = fromfiletime(mtime);
1212 if(attr & ATTR_READONLY){
1214 nd.mode = d->mode & ~0222;
1216 if((d->mode & 0222) == 0)
1217 nd.mode = d->mode | 0222;
1222 case 0x0102: /* SMB_SET_FILE_DISPOSITION_INFO */
1223 if(f == nil || !unpack(b, p, e, "b", &delete))
1225 if((f->dacc & FILE_DELETE) == 0)
1226 return STATUS_ACCESS_DENIED;
1227 deletefile(f, delete);
1230 case 0x0103: /* SMB_SET_FILE_ALLOCATION_INFO */
1231 case 0x0104: /* SMB_SET_FILE_END_OF_FILE_INFO */
1232 if(f == nil || !unpack(b, p, e, "v", &len))
1234 if(d->qid.type & QTDIR)
1235 return STATUS_OS2_INVALID_ACCESS;
1241 logit("[%.4x] unknown SET infolevel", level);
1242 return STATUS_OS2_INVALID_LEVEL;
1244 return STATUS_NOT_SUPPORTED;
1247 fprint(2, "wstat\nmode %lo\natime %ld\nmtime %ld\nlength %llux\n",
1248 nd.mode, nd.atime, nd.mtime, nd.length);
1249 if(((f && f->fd >= 0) ? dirfwstat(f->fd, &nd) : dirwstat(path, &nd)) < 0)
1250 return smbmkerror();
1251 xdirflush(path, r->namecmp);
1256 trans2setpathinformation(Trans *t)
1265 if(!unpack(t->in.param.b, t->in.param.p, t->in.param.e, "w____f", &level,
1266 t->o->nameunpack, &name)){
1267 t->respond(t, STATUS_NOT_SUPPORTED);
1270 if((path = getpath(t->r->tid, name, &tree, &err)) == nil)
1272 if((d = xdirstat(&path, t->namecmp)) == nil){
1273 t->respond(t, smbmkerror());
1276 if(err = setfilepathinformation(t->r, d, nil, path, level, t->in.data.b, t->in.data.p, t->in.data.e)){
1281 pack(t->out.param.b, t->out.param.p, t->out.param.e, "__.", &t->out.param.p);
1290 trans2setfileinformation(Trans *t)
1292 int err, fid, level;
1299 if(!unpack(t->in.param.b, t->in.param.p, t->in.param.e, "ww__", &fid, &level)){
1300 t->respond(t, STATUS_NOT_SUPPORTED);
1303 if((f = getfile(t->r->tid, fid, &tree, &err)) == nil)
1305 if((d = statfile(f)) == nil){
1306 t->respond(t, smbmkerror());
1309 if(err = setfilepathinformation(t->r, d, f, f->path, level, t->in.data.b, t->in.data.p, t->in.data.e)){
1314 pack(t->out.param.b, t->out.param.p, t->out.param.e, "__.", &t->out.param.p);
1322 FILE_CASE_SENSITIVE_SEARCH = 1,
1323 FILE_CASE_PRESERVED_NAMES = 2,
1324 FILE_UNICODE_ON_DISK = 4,
1328 trans2queryfsinformation(Trans *t)
1336 if(!unpack(t->in.param.b, t->in.param.p, t->in.param.e, "w", &level)){
1337 t->respond(t, STATUS_NOT_SUPPORTED);
1340 if((tree = gettree(t->r->tid)) == nil){
1341 t->respond(t, STATUS_SMB_BAD_TID);
1344 share = tree->share;
1346 fprint(2, "FS level %.4x\n", level);
1348 case 0x0001: /* SMB_INFO_ALLOCATION */
1349 n = pack(t->out.data.b, t->out.data.p, t->out.data.e, "llllw",
1350 0x00000000, share->blocksize/share->sectorsize,
1351 filesize32(allocsize(share->allocsize+share->freesize, share->blocksize)/share->blocksize),
1352 filesize32(allocsize(share->freesize, share->blocksize)/share->blocksize),
1356 case 0x0002: /* SMB_INFO_VOLUME */
1357 s = smprint("%.12s", share->name);
1358 n = pack(t->out.data.b, t->out.data.p, t->out.data.e, "l#0b{f}",
1359 (int)namehash(share->root), smbuntermstrpack8, s);
1362 case 0x0102: /* SMB_QUERY_FS_VOLUME_INFO */
1363 s = smprint("%.12s", share->name);
1364 n = pack(t->out.data.b, t->out.data.p, t->out.data.e, "vl#0l__{f}",
1365 tofiletime(starttime), (int)namehash(share->root), smbuntermstrpack16, s);
1368 case 0x0103: /* SMB_QUERY_FS_SIZE_INFO */
1369 n = pack(t->out.data.b, t->out.data.p, t->out.data.e, "vvll",
1370 allocsize(share->allocsize+share->freesize, share->blocksize)/share->blocksize,
1371 allocsize(share->freesize, share->blocksize)/share->blocksize,
1372 share->blocksize/share->sectorsize,
1376 case 0x0105: /* SMB_QUERY_FS_ATTRIBUTE_INFO */
1377 n = pack(t->out.data.b, t->out.data.p, t->out.data.e, "ll#0l{f}",
1378 FILE_CASE_SENSITIVE_SEARCH |
1379 FILE_CASE_PRESERVED_NAMES |
1380 FILE_UNICODE_ON_DISK,
1381 share->namelen, smbuntermstrpack16, share->fsname);
1385 logit("[%.4x] unknown FS infolevel", level);
1386 t->respond(t, STATUS_OS2_INVALID_LEVEL);
1390 t->respond(t, STATUS_INVALID_SMB);
1400 SMB_FIND_CLOSE_AFTER_REQUEST = 0x1,
1401 SMB_FIND_CLOSE_AT_EOS = 0x2,
1402 SMB_FIND_RETURN_RESUME_KEYS = 0x4,
1403 SMB_FIND_CONTINUE_FROM_LAST = 0x8,
1407 trans2findfirst2(Trans *t)
1409 int i, nsid, eos, n, attr, count, flags, level;
1410 uchar *prevoff, *nameoff;
1418 if(!unpack(t->in.param.b, t->in.param.p, t->in.param.e, "wwww____f",
1419 &attr, &count, &flags, &level, t->o->nameunpack, &name)){
1420 t->respond(t, STATUS_NOT_SUPPORTED);
1424 fprint(2, "FIND level %.4x\n", level);
1425 if((path = getpath(t->r->tid, name, &tree, &n)) == nil){
1429 if((f = openfind(path, t->namecmp, attr, 1, &n)) == nil){
1434 prevoff = nameoff = nil;
1435 for(i = 0; i < count; i++){
1436 if((eos = readfind(f, f->index, &d)) < 0)
1438 if((n = fpackdir(t->r, d, tree, 0, level,
1439 t->out.data.b, t->out.data.p, t->out.data.e,
1440 &prevoff, &nameoff)) <= 0)
1445 if((n < 0) || (flags & SMB_FIND_CLOSE_AFTER_REQUEST) ||
1446 ((flags & SMB_FIND_CLOSE_AT_EOS) && (eos < 0))){
1448 t->respond(t, STATUS_OS2_INVALID_LEVEL);
1454 nsid = newsid(tree, f);
1456 if(!i && (eos < 0)){
1457 t->respond(t, STATUS_NO_MORE_FILES);
1460 if(!pack(t->out.param.b, t->out.param.p, t->out.param.e, "wwwww.",
1461 nsid, i, (eos < 0), 0, (int)(nameoff - t->out.data.b), &t->out.param.p)){
1462 t->respond(t, STATUS_INVALID_SMB);
1473 trans2findnext2(Trans *t)
1475 int i, n, eos, sid, count, level, index, flags;
1476 uchar *prevoff, *nameoff;
1484 if(!unpack(t->in.param.b, t->in.param.p, t->in.param.e, "wwwlwf",
1485 &sid, &count, &level, &index, &flags, t->o->nameunpack, &name)){
1486 t->respond(t, STATUS_NOT_SUPPORTED);
1490 fprint(2, "FIND level %.4x\n", level);
1491 if((f = getfind(t->r->tid, sid, &tree, &n)) == nil){
1496 if((flags & SMB_FIND_CONTINUE_FROM_LAST) == 0){
1498 while((eos = readfind(f, f->index, &d)) >= 0){
1500 if(strcmp(name, d->name) == 0)
1504 prevoff = nameoff = nil;
1505 for(i = 0; i < count; i++){
1506 if((eos = readfind(f, f->index, &d)) < 0)
1508 if((n = fpackdir(t->r, d, tree, 0, level,
1509 t->out.data.b, t->out.data.p, t->out.data.e,
1510 &prevoff, &nameoff)) <= 0)
1515 if((flags & SMB_FIND_CLOSE_AFTER_REQUEST) ||
1516 ((flags & SMB_FIND_CLOSE_AT_EOS) && (eos < 0))){
1520 if(!i && (eos < 0)){
1521 t->respond(t, STATUS_NO_MORE_FILES);
1524 if(!pack(t->out.param.b, t->out.param.p, t->out.param.e, "wwww.",
1525 i, (eos < 0), 0, (int)(nameoff - t->out.data.b), &t->out.param.p))
1526 t->respond(t, STATUS_INVALID_SMB);
1535 transrespond(Trans *t, int err)
1542 if(!err && !pack(r->rh, r->rp, r->re,
1543 "#0b{*2ww__#3w@3ww#4w@4ww#1b_[*2]}#2w{%4[]%4[]}.",
1544 t->out.param.p - t->out.param.b,
1545 t->out.data.p - t->out.data.b,
1547 t->out.setup.b, t->out.setup.p,
1548 t->out.param.b, t->out.param.p,
1549 t->out.data.b, t->out.data.p, &r->rp))
1550 r->respond(r, STATUS_INVALID_SMB);
1553 free(t->out.param.b);
1554 free(t->out.data.b);
1555 free(t->out.setup.b);
1560 void (*fun)(Trans *t);
1562 [0x0000] { "TRANS_RAP", transrap },
1563 }, trans2optab[] = {
1564 [0x0001] { "TRANS2_FIND_FIRST2", trans2findfirst2 },
1565 [0x0002] { "TRANS2_FIND_NEXT2", trans2findnext2 },
1566 [0x0003] { "TRANS2_QUERY_FS_INFORMATION", trans2queryfsinformation },
1567 [0x0005] { "TRANS2_QUERY_PATH_INFORMATION", trans2querypathinformation },
1568 [0x0007] { "TRANS2_QUERY_FILE_INFORMATION", trans2queryfileinformation },
1569 [0x0006] { "TRANS2_SET_PATH_INFORMATION", trans2setpathinformation },
1570 [0x0008] { "TRANS2_SET_FILE_INFORMATION", trans2setfileinformation },
1574 smbtransaction(Req *r, uchar *h, uchar *p, uchar *e)
1576 int tpc, tdc, rpc, rdc, rsc;
1577 uchar *sa, *se, *da, *de, *pa, *pe;
1578 void (*fun)(Trans *t);
1583 t.namecmp = r->namecmp;
1585 t.respond = transrespond;
1586 if(!unpack(h, p, e, "#0b{*2wwwwb_w______#3w@3w#4w@4w#1b_[*2]}#2w{[?][?]}",
1587 &tpc, &tdc, &rpc, &rdc, &rsc, &t.flags, &sa, &se, &pa, &pe, &da, &de)){
1589 r->respond(r, STATUS_NOT_SUPPORTED);
1592 unpack(sa, sa, se, "w", &t.cmd);
1595 case 0x25: /* SMB_COM_TRANSACTION */
1596 if((t.cmd >= nelem(transoptab)) || ((fun = transoptab[t.cmd].fun) == nil)){
1597 logit("[%.4x] transaction subcommand not implemented", t.cmd);
1600 t.name = transoptab[t.cmd].name;
1602 case 0x32: /* SMB_COM_TRANSACTION2 */
1603 if((t.cmd >= nelem(trans2optab)) || ((fun = trans2optab[t.cmd].fun) == nil)){
1604 logit("[%.4x] transaction2 subcommand not implemented", t.cmd);
1607 t.name = trans2optab[t.cmd].name;
1613 if((tpc > (pe - pa)) || (tdc > (de - da))){
1614 logit("[%.4x] %s request truncated", t.cmd, t.name);
1617 if(57+((rsc+1)&~1)+((rpc+3)&~3)+((rdc+3)&~3) > remotebuffersize){
1618 rdc = remotebuffersize-(57+((rsc+1)&~1)+((rpc+3)&~3)) & ~3;
1620 logit("[%.4x] %s response doesnt fit in client buffer", t.cmd, t.name);
1625 t.in.param.b = t.in.param.p = pa; t.in.param.e = pe;
1626 t.in.data.b = t.in.data.p = da; t.in.data.e = de;
1627 t.in.setup.b = t.in.setup.p = sa; t.in.setup.e = se;
1629 t.out.param.b = t.out.param.p = t.out.param.e = (rpc > 0) ? malloc(rpc) : nil;
1630 t.out.param.e += rpc;
1631 t.out.data.b = t.out.data.p = t.out.data.e = (rdc > 0) ? malloc(rdc) : nil;
1632 t.out.data.e += rdc;
1633 t.out.setup.b = t.out.setup.p = t.out.setup.e = (rsc > 0) ? malloc(rsc) : nil;
1634 t.out.setup.e += rsc;
1637 fprint(2, "[%.4x] %s\n", t.cmd, t.name);
1642 smbnoandxcommand(Req *r, uchar *, uchar *, uchar *)
1644 r->respond(r, (r->cmd == 0xFF) ? STATUS_INVALID_SMB : 0);
1649 void (*fun)(Req *, uchar *, uchar *, uchar *);
1651 [0x00] { "SMB_COM_CREATE_DIRECTORY", smbcreatedirectory },
1652 [0x01] { "SMB_COM_DELETE_DIRECTORY", smbdeletedirectory },
1653 [0x04] { "SMB_COM_CLOSE", smbcloseflush },
1654 [0x05] { "SMB_COM_FLUSH", smbcloseflush },
1655 [0x06] { "SMB_COM_DELETE", smbdelete },
1656 [0x07] { "SMB_COM_RENAME", smbrename },
1657 [0x08] { "SMB_COM_QUERY_INFORMATION", smbqueryinformation },
1658 [0x09] { "SMB_COM_SET_INFORMATION", smbsetinformation },
1659 [0x10] { "SMB_CON_CHECK_DIRECTORY", smbcheckdirectory },
1660 [0x0b] { "SMB_COM_WRITE", smbwrite },
1661 [0x23] { "SMB_COM_QUERY_INFORMATION2", smbqueryinformation2 },
1662 [0x24] { "SMB_COM_LOCKING_ANDX", smblockingandx },
1663 [0x25] { "SMB_COM_TRANSACTION", smbtransaction },
1664 [0x2b] { "SMB_COM_ECHO", smbecho },
1665 [0x2d] { "SMB_COM_OPEN_ANDX", smbopenandx },
1666 [0x2e] { "SMB_COM_READ_ANDX", smbreadandx },
1667 [0x2f] { "SMB_COM_WRITE_ANDX", smbwriteandx },
1668 [0x32] { "SMB_COM_TRANSACTION2", smbtransaction },
1669 [0x34] { "SMB_COM_FIND_CLOSE2", smbcloseflush },
1670 [0x71] { "SMB_COM_DISCONNECT_TREE", smbdisconnecttree },
1671 [0x72] { "SMB_COM_NEGOTIATE", smbnegotiate },
1672 [0x73] { "SMB_COM_SESSION_SETUP_ANX", smbsessionsetupandx },
1673 [0x74] { "SMB_COM_LOGOFF_ANDX", smblogoffandx },
1674 [0x75] { "SMB_COM_TREE_CONNECT_ANDX", smbtreeconnectandx },
1675 [0x80] { "SMB_COM_QUERY_INFORMATION_DISK", smbqueryinformationdisk },
1676 [0xa2] { "SMB_COM_NT_CREATE_ANDX", smbntcreatendx },
1677 [0xFF] { "SMB_COM_NO_ANDX_COMMAND", smbnoandxcommand },
1681 smbcmd(Req *r, int cmd, uchar *h, uchar *p, uchar *e)
1683 if((cmd >= nelem(optab)) || (optab[cmd].fun == nil)){
1684 logit("[%.2x] command not implemented", cmd);
1685 r->respond(r, STATUS_NOT_SUPPORTED);
1688 r->name = optab[cmd].name;
1690 fprint(2, "[%.2x] %s\n", cmd, r->name);
1691 if((!negotiated && cmd != 0x72) || (negotiated && cmd == 0x72)){
1692 r->respond(r, STATUS_INVALID_SMB);
1697 case 0x72: /* SMB_COM_NEGOTIATE */
1698 case 0x73: /* SMB_COM_SESSION_SETUP_ANX */
1699 case 0x74: /* SMB_COM_LOGOFF_ANDX */
1700 case 0xFF: /* SMB_COM_NO_ANDX_COMMAND */
1703 logit("auth not completed in %s request", r->name);
1704 case 0x75: /* SMB_COM_TREE_CONNECT_ANDX */
1705 r->respond(r, STATUS_LOGON_FAILURE);
1708 } else if(r->uid != sessionuid){
1710 case 0x73: /* SMB_COM_SESSION_SETUP_ANX */
1711 case 0x2b: /* SMB_COM_ECHO */
1714 logit("bad uid %.4x in %s request", r->uid, r->name);
1715 r->respond(r, STATUS_SMB_BAD_UID);
1719 (*optab[cmd].fun)(r, h, p, e);