1 #include "stdinc.h"
2
3 #include "9.h"
4
5 enum {
6 OMODE = 0x7, /* Topen/Tcreate mode */
7 };
8
9 enum {
10 PermX = 1,
11 PermW = 2,
12 PermR = 4,
13 };
14
15 static char EPermission[] = "permission denied";
16
17 static int
permFile(File * file,Fid * fid,int perm)18 permFile(File* file, Fid* fid, int perm)
19 {
20 char *u;
21 DirEntry de;
22
23 if(!fileGetDir(file, &de))
24 return -1;
25
26 /*
27 * User none only gets other permissions.
28 */
29 if(strcmp(fid->uname, unamenone) != 0){
30 /*
31 * There is only one uid<->uname mapping
32 * and it's already cached in the Fid, but
33 * it might have changed during the lifetime
34 * if this Fid.
35 */
36 if((u = unameByUid(de.uid)) != nil){
37 if(strcmp(fid->uname, u) == 0 && ((perm<<6) & de.mode)){
38 vtfree(u);
39 deCleanup(&de);
40 return 1;
41 }
42 vtfree(u);
43 }
44 if(groupMember(de.gid, fid->uname) && ((perm<<3) & de.mode)){
45 deCleanup(&de);
46 return 1;
47 }
48 }
49 if(perm & de.mode){
50 if(perm == PermX && (de.mode & ModeDir)){
51 deCleanup(&de);
52 return 1;
53 }
54 if(!groupMember(uidnoworld, fid->uname)){
55 deCleanup(&de);
56 return 1;
57 }
58 }
59 if(fsysNoPermCheck(fid->fsys) || (fid->con->flags&ConNoPermCheck)){
60 deCleanup(&de);
61 return 1;
62 }
63 werrstr(EPermission);
64
65 deCleanup(&de);
66 return 0;
67 }
68
69 static int
permFid(Fid * fid,int p)70 permFid(Fid* fid, int p)
71 {
72 return permFile(fid->file, fid, p);
73 }
74
75 static int
permParent(Fid * fid,int p)76 permParent(Fid* fid, int p)
77 {
78 int r;
79 File *parent;
80
81 parent = fileGetParent(fid->file);
82 r = permFile(parent, fid, p);
83 fileDecRef(parent);
84
85 return r;
86 }
87
88 int
validFileName(char * name)89 validFileName(char* name)
90 {
91 char *p;
92
93 if(name == nil || name[0] == '\0'){
94 werrstr("no file name");
95 return 0;
96 }
97 if(name[0] == '.'){
98 if(name[1] == '\0' || (name[1] == '.' && name[2] == '\0')){
99 werrstr(". and .. illegal as file name");
100 return 0;
101 }
102 }
103
104 for(p = name; *p != '\0'; p++){
105 if((*p & 0xFF) < 040){
106 werrstr("bad character in file name");
107 return 0;
108 }
109 }
110
111 return 1;
112 }
113
114 static int
rTwstat(Msg * m)115 rTwstat(Msg* m)
116 {
117 Dir dir;
118 Fid *fid;
119 ulong mode, oldmode;
120 DirEntry de;
121 char *gid, *strs, *uid;
122 int gl, op, retval, tsync, wstatallow;
123
124 if((fid = fidGet(m->con, m->t.fid, FidFWlock)) == nil)
125 return 0;
126
127 gid = uid = nil;
128 retval = 0;
129
130 if(strcmp(fid->uname, unamenone) == 0 || (fid->qid.type & QTAUTH)){
131 werrstr(EPermission);
132 goto error0;
133 }
134 if(fileIsRoFs(fid->file) || !groupWriteMember(fid->uname)){
135 werrstr("read-only filesystem");
136 goto error0;
137 }
138
139 if(!fileGetDir(fid->file, &de))
140 goto error0;
141
142 strs = vtmalloc(m->t.nstat);
143 if(convM2D(m->t.stat, m->t.nstat, &dir, strs) == 0){
144 werrstr("wstat -- protocol botch");
145 goto error;
146 }
147
148 /*
149 * Run through each of the (sub-)fields in the provided Dir
150 * checking for validity and whether it's a default:
151 * .type, .dev and .atime are completely ignored and not checked;
152 * .qid.path, .qid.vers and .muid are checked for validity but
153 * any attempt to change them is an error.
154 * .qid.type/.mode, .mtime, .name, .length, .uid and .gid can
155 * possibly be changed.
156 *
157 * 'Op' flags there are changed fields, i.e. it's not a no-op.
158 * 'Tsync' flags all fields are defaulted.
159 */
160 tsync = 1;
161 if(dir.qid.path != ~0){
162 if(dir.qid.path != de.qid){
163 werrstr("wstat -- attempt to change qid.path");
164 goto error;
165 }
166 tsync = 0;
167 }
168 if(dir.qid.vers != ~0){
169 if(dir.qid.vers != de.mcount){
170 werrstr("wstat -- attempt to change qid.vers");
171 goto error;
172 }
173 tsync = 0;
174 }
175 if(dir.muid != nil && *dir.muid != '\0'){
176 if((uid = uidByUname(dir.muid)) == nil){
177 werrstr("wstat -- unknown muid");
178 goto error;
179 }
180 if(strcmp(uid, de.mid) != 0){
181 werrstr("wstat -- attempt to change muid");
182 goto error;
183 }
184 vtfree(uid);
185 uid = nil;
186 tsync = 0;
187 }
188
189 /*
190 * Check .qid.type and .mode agree if neither is defaulted.
191 */
192 if(dir.qid.type != (uchar)~0 && dir.mode != ~0){
193 if(dir.qid.type != ((dir.mode>>24) & 0xFF)){
194 werrstr("wstat -- qid.type/mode mismatch");
195 goto error;
196 }
197 }
198
199 op = 0;
200
201 oldmode = de.mode;
202 if(dir.qid.type != (uchar)~0 || dir.mode != ~0){
203 /*
204 * .qid.type or .mode isn't defaulted, check for unknown bits.
205 */
206 if(dir.mode == ~0)
207 dir.mode = (dir.qid.type<<24)|(de.mode & 0777);
208 if(dir.mode & ~(DMDIR|DMAPPEND|DMEXCL|DMTMP|0777)){
209 werrstr("wstat -- unknown bits in qid.type/mode");
210 goto error;
211 }
212
213 /*
214 * Synthesise a mode to check against the current settings.
215 */
216 mode = dir.mode & 0777;
217 if(dir.mode & DMEXCL)
218 mode |= ModeExclusive;
219 if(dir.mode & DMAPPEND)
220 mode |= ModeAppend;
221 if(dir.mode & DMDIR)
222 mode |= ModeDir;
223 if(dir.mode & DMTMP)
224 mode |= ModeTemporary;
225
226 if((de.mode^mode) & ModeDir){
227 werrstr("wstat -- attempt to change directory bit");
228 goto error;
229 }
230
231 if((de.mode & (ModeAppend|ModeExclusive|ModeTemporary|0777)) != mode){
232 de.mode &= ~(ModeAppend|ModeExclusive|ModeTemporary|0777);
233 de.mode |= mode;
234 op = 1;
235 }
236 tsync = 0;
237 }
238
239 if(dir.mtime != ~0){
240 if(dir.mtime != de.mtime){
241 de.mtime = dir.mtime;
242 op = 1;
243 }
244 tsync = 0;
245 }
246
247 if(dir.length != ~0){
248 if(dir.length != de.size){
249 /*
250 * Cannot change length on append-only files.
251 * If we're changing the append bit, it's okay.
252 */
253 if(de.mode & oldmode & ModeAppend){
254 werrstr("wstat -- attempt to change length of append-only file");
255 goto error;
256 }
257 if(de.mode & ModeDir){
258 werrstr("wstat -- attempt to change length of directory");
259 goto error;
260 }
261 de.size = dir.length;
262 op = 1;
263 }
264 tsync = 0;
265 }
266
267 /*
268 * Check for permission to change .mode, .mtime or .length,
269 * must be owner or leader of either group, for which test gid
270 * is needed; permission checks on gid will be done later.
271 */
272 if(dir.gid != nil && *dir.gid != '\0'){
273 if((gid = uidByUname(dir.gid)) == nil){
274 werrstr("wstat -- unknown gid");
275 goto error;
276 }
277 tsync = 0;
278 }
279 else
280 gid = vtstrdup(de.gid);
281
282 wstatallow = (fsysWstatAllow(fid->fsys) || (m->con->flags&ConWstatAllow));
283
284 /*
285 * 'Gl' counts whether neither, one or both groups are led.
286 */
287 gl = groupLeader(gid, fid->uname) != 0;
288 gl += groupLeader(de.gid, fid->uname) != 0;
289
290 if(op && !wstatallow){
291 if(strcmp(fid->uid, de.uid) != 0 && !gl){
292 werrstr("wstat -- not owner or group leader");
293 goto error;
294 }
295 }
296
297 /*
298 * Check for permission to change group, must be
299 * either owner and in new group or leader of both groups.
300 * If gid is nil here then
301 */
302 if(strcmp(gid, de.gid) != 0){
303 if(!wstatallow
304 && !(strcmp(fid->uid, de.uid) == 0 && groupMember(gid, fid->uname))
305 && !(gl == 2)){
306 werrstr("wstat -- not owner and not group leaders");
307 goto error;
308 }
309 vtfree(de.gid);
310 de.gid = gid;
311 gid = nil;
312 op = 1;
313 tsync = 0;
314 }
315
316 /*
317 * Rename.
318 * Check .name is valid and different to the current.
319 * If so, check write permission in parent.
320 */
321 if(dir.name != nil && *dir.name != '\0'){
322 if(!validFileName(dir.name))
323 goto error;
324 if(strcmp(dir.name, de.elem) != 0){
325 if(permParent(fid, PermW) <= 0)
326 goto error;
327 vtfree(de.elem);
328 de.elem = vtstrdup(dir.name);
329 op = 1;
330 }
331 tsync = 0;
332 }
333
334 /*
335 * Check for permission to change owner - must be god.
336 */
337 if(dir.uid != nil && *dir.uid != '\0'){
338 if((uid = uidByUname(dir.uid)) == nil){
339 werrstr("wstat -- unknown uid");
340 goto error;
341 }
342 if(strcmp(uid, de.uid) != 0){
343 if(!wstatallow){
344 werrstr("wstat -- not owner");
345 goto error;
346 }
347 if(strcmp(uid, uidnoworld) == 0){
348 werrstr(EPermission);
349 goto error;
350 }
351 vtfree(de.uid);
352 de.uid = uid;
353 uid = nil;
354 op = 1;
355 }
356 tsync = 0;
357 }
358
359 if(op)
360 retval = fileSetDir(fid->file, &de, fid->uid);
361 else
362 retval = 1;
363
364 fid->qid.vers = fileGetMcount(fid->file);
365 m->r.qid = fid->qid;
366 m->r.iounit = m->con->msize-IOHDRSZ;
367
368 if(tsync){
369 /*
370 * All values were defaulted,
371 * make the state of the file exactly what it
372 * claims to be before returning...
373 */
374 USED(tsync);
375 }
376
377 error:
378 deCleanup(&de);
379 vtfree(strs);
380 if(gid != nil)
381 vtfree(gid);
382 if(uid != nil)
383 vtfree(uid);
384 error0:
385 fidPut(fid);
386 return retval;
387 };
388
389 static int
rTstat(Msg * m)390 rTstat(Msg* m)
391 {
392 Dir dir;
393 Fid *fid;
394 DirEntry de;
395
396 if((fid = fidGet(m->con, m->t.fid, 0)) == nil)
397 return 0;
398 if(fid->qid.type & QTAUTH){
399 memset(&dir, 0, sizeof(Dir));
400 dir.qid = fid->qid;
401 dir.mode = DMAUTH;
402 dir.atime = time(0L);
403 dir.mtime = dir.atime;
404 dir.length = 0;
405 dir.name = "#¿";
406 dir.uid = fid->uname;
407 dir.gid = fid->uname;
408 dir.muid = fid->uname;
409
410 if((m->r.nstat = convD2M(&dir, m->data, m->con->msize)) == 0){
411 werrstr("stat QTAUTH botch");
412 fidPut(fid);
413 return 0;
414 }
415 m->r.stat = m->data;
416
417 fidPut(fid);
418 return 1;
419 }
420 if(!fileGetDir(fid->file, &de)){
421 fidPut(fid);
422 return 0;
423 }
424 fidPut(fid);
425
426 /*
427 * TODO: optimise this copy (in convS2M) away somehow.
428 * This pettifoggery with m->data will do for the moment.
429 */
430 m->r.nstat = dirDe2M(&de, m->data, m->con->msize);
431 m->r.stat = m->data;
432 deCleanup(&de);
433
434 return 1;
435 }
436
437 static int
_rTclunk(Fid * fid,int remove)438 _rTclunk(Fid* fid, int remove)
439 {
440 int rok;
441
442 if(fid->excl)
443 exclFree(fid);
444
445 rok = 1;
446 if(remove && !(fid->qid.type & QTAUTH)){
447 if((rok = permParent(fid, PermW)) > 0)
448 rok = fileRemove(fid->file, fid->uid);
449 }
450 fidClunk(fid);
451
452 return rok;
453 }
454
455 static int
rTremove(Msg * m)456 rTremove(Msg* m)
457 {
458 Fid *fid;
459
460 if((fid = fidGet(m->con, m->t.fid, FidFWlock)) == nil)
461 return 0;
462 return _rTclunk(fid, 1);
463 }
464
465 static int
rTclunk(Msg * m)466 rTclunk(Msg* m)
467 {
468 Fid *fid;
469
470 if((fid = fidGet(m->con, m->t.fid, FidFWlock)) == nil)
471 return 0;
472 _rTclunk(fid, (fid->open & FidORclose));
473
474 return 1;
475 }
476
477 static int
rTwrite(Msg * m)478 rTwrite(Msg* m)
479 {
480 Fid *fid;
481 int count, n;
482
483 if((fid = fidGet(m->con, m->t.fid, 0)) == nil)
484 return 0;
485 if(!(fid->open & FidOWrite)){
486 werrstr("fid not open for write");
487 goto error;
488 }
489
490 count = m->t.count;
491 if(count < 0 || count > m->con->msize-IOHDRSZ){
492 werrstr("write count too big");
493 goto error;
494 }
495 if(m->t.offset < 0){
496 werrstr("write offset negative");
497 goto error;
498 }
499 if(fid->excl != nil && !exclUpdate(fid))
500 goto error;
501
502 if(fid->qid.type & QTDIR){
503 werrstr("is a directory");
504 goto error;
505 }
506 else if(fid->qid.type & QTAUTH)
507 n = authWrite(fid, m->t.data, count);
508 else
509 n = fileWrite(fid->file, m->t.data, count, m->t.offset, fid->uid);
510 if(n < 0)
511 goto error;
512
513
514 m->r.count = n;
515
516 fidPut(fid);
517 return 1;
518
519 error:
520 fidPut(fid);
521 return 0;
522 }
523
524 static int
rTread(Msg * m)525 rTread(Msg* m)
526 {
527 Fid *fid;
528 uchar *data;
529 int count, n;
530
531 if((fid = fidGet(m->con, m->t.fid, 0)) == nil)
532 return 0;
533 if(!(fid->open & FidORead)){
534 werrstr("fid not open for read");
535 goto error;
536 }
537
538 count = m->t.count;
539 if(count < 0 || count > m->con->msize-IOHDRSZ){
540 werrstr("read count too big");
541 goto error;
542 }
543 if(m->t.offset < 0){
544 werrstr("read offset negative");
545 goto error;
546 }
547 if(fid->excl != nil && !exclUpdate(fid))
548 goto error;
549
550 /*
551 * TODO: optimise this copy (in convS2M) away somehow.
552 * This pettifoggery with m->data will do for the moment.
553 */
554 data = m->data+IOHDRSZ;
555 if(fid->qid.type & QTDIR)
556 n = dirRead(fid, data, count, m->t.offset);
557 else if(fid->qid.type & QTAUTH)
558 n = authRead(fid, data, count);
559 else
560 n = fileRead(fid->file, data, count, m->t.offset);
561 if(n < 0)
562 goto error;
563
564 m->r.count = n;
565 m->r.data = (char*)data;
566
567 fidPut(fid);
568 return 1;
569
570 error:
571 fidPut(fid);
572 return 0;
573 }
574
575 static int
rTcreate(Msg * m)576 rTcreate(Msg* m)
577 {
578 Fid *fid;
579 File *file;
580 ulong mode;
581 int omode, open, perm;
582
583 if((fid = fidGet(m->con, m->t.fid, FidFWlock)) == nil)
584 return 0;
585 if(fid->open){
586 werrstr("fid open for I/O");
587 goto error;
588 }
589 if(fileIsRoFs(fid->file) || !groupWriteMember(fid->uname)){
590 werrstr("read-only filesystem");
591 goto error;
592 }
593 if(!fileIsDir(fid->file)){
594 werrstr("not a directory");
595 goto error;
596 }
597 if(permFid(fid, PermW) <= 0)
598 goto error;
599 if(!validFileName(m->t.name))
600 goto error;
601 if(strcmp(fid->uid, uidnoworld) == 0){
602 werrstr(EPermission);
603 goto error;
604 }
605
606 omode = m->t.mode & OMODE;
607 open = 0;
608
609 if(omode == OREAD || omode == ORDWR || omode == OEXEC)
610 open |= FidORead;
611 if(omode == OWRITE || omode == ORDWR)
612 open |= FidOWrite;
613 if((open & (FidOWrite|FidORead)) == 0){
614 werrstr("unknown mode");
615 goto error;
616 }
617 if(m->t.perm & DMDIR){
618 if((m->t.mode & (ORCLOSE|OTRUNC)) || (open & FidOWrite)){
619 werrstr("illegal mode");
620 goto error;
621 }
622 if(m->t.perm & DMAPPEND){
623 werrstr("illegal perm");
624 goto error;
625 }
626 }
627
628 mode = fileGetMode(fid->file);
629 perm = m->t.perm;
630 if(m->t.perm & DMDIR)
631 perm &= ~0777|(mode & 0777);
632 else
633 perm &= ~0666|(mode & 0666);
634 mode = perm & 0777;
635 if(m->t.perm & DMDIR)
636 mode |= ModeDir;
637 if(m->t.perm & DMAPPEND)
638 mode |= ModeAppend;
639 if(m->t.perm & DMEXCL)
640 mode |= ModeExclusive;
641 if(m->t.perm & DMTMP)
642 mode |= ModeTemporary;
643
644 if((file = fileCreate(fid->file, m->t.name, mode, fid->uid)) == nil){
645 fidPut(fid);
646 return 0;
647 }
648 fileDecRef(fid->file);
649
650 fid->qid.vers = fileGetMcount(file);
651 fid->qid.path = fileGetId(file);
652 fid->file = file;
653 mode = fileGetMode(fid->file);
654 if(mode & ModeDir)
655 fid->qid.type = QTDIR;
656 else
657 fid->qid.type = QTFILE;
658 if(mode & ModeAppend)
659 fid->qid.type |= QTAPPEND;
660 if(mode & ModeExclusive){
661 fid->qid.type |= QTEXCL;
662 assert(exclAlloc(fid) != 0);
663 }
664 if(m->t.mode & ORCLOSE)
665 open |= FidORclose;
666 fid->open = open;
667
668 m->r.qid = fid->qid;
669 m->r.iounit = m->con->msize-IOHDRSZ;
670
671 fidPut(fid);
672 return 1;
673
674 error:
675 fidPut(fid);
676 return 0;
677 }
678
679 static int
rTopen(Msg * m)680 rTopen(Msg* m)
681 {
682 Fid *fid;
683 int isdir, mode, omode, open, rofs;
684
685 if((fid = fidGet(m->con, m->t.fid, FidFWlock)) == nil)
686 return 0;
687 if(fid->open){
688 werrstr("fid open for I/O");
689 goto error;
690 }
691
692 isdir = fileIsDir(fid->file);
693 open = 0;
694 rofs = fileIsRoFs(fid->file) || !groupWriteMember(fid->uname);
695
696 if(m->t.mode & ORCLOSE){
697 if(isdir){
698 werrstr("is a directory");
699 goto error;
700 }
701 if(rofs){
702 werrstr("read-only filesystem");
703 goto error;
704 }
705 if(permParent(fid, PermW) <= 0)
706 goto error;
707
708 open |= FidORclose;
709 }
710
711 omode = m->t.mode & OMODE;
712 if(omode == OREAD || omode == ORDWR){
713 if(permFid(fid, PermR) <= 0)
714 goto error;
715 open |= FidORead;
716 }
717 if(omode == OWRITE || omode == ORDWR || (m->t.mode & OTRUNC)){
718 if(isdir){
719 werrstr("is a directory");
720 goto error;
721 }
722 if(rofs){
723 werrstr("read-only filesystem");
724 goto error;
725 }
726 if(permFid(fid, PermW) <= 0)
727 goto error;
728 open |= FidOWrite;
729 }
730 if(omode == OEXEC){
731 if(isdir){
732 werrstr("is a directory");
733 goto error;
734 }
735 if(permFid(fid, PermX) <= 0)
736 goto error;
737 open |= FidORead;
738 }
739 if((open & (FidOWrite|FidORead)) == 0){
740 werrstr("unknown mode");
741 goto error;
742 }
743
744 mode = fileGetMode(fid->file);
745 if((mode & ModeExclusive) && exclAlloc(fid) == 0)
746 goto error;
747
748 /*
749 * Everything checks out, try to commit any changes.
750 */
751 if((m->t.mode & OTRUNC) && !(mode & ModeAppend))
752 if(!fileTruncate(fid->file, fid->uid))
753 goto error;
754
755 if(isdir && fid->db != nil){
756 dirBufFree(fid->db);
757 fid->db = nil;
758 }
759
760 fid->qid.vers = fileGetMcount(fid->file);
761 m->r.qid = fid->qid;
762 m->r.iounit = m->con->msize-IOHDRSZ;
763
764 fid->open = open;
765
766 fidPut(fid);
767 return 1;
768
769 error:
770 if(fid->excl != nil)
771 exclFree(fid);
772 fidPut(fid);
773 return 0;
774 }
775
776 static int
rTwalk(Msg * m)777 rTwalk(Msg* m)
778 {
779 Qid qid;
780 Fcall *r, *t;
781 int nwname, wlock;
782 File *file, *nfile;
783 Fid *fid, *ofid, *nfid;
784
785 t = &m->t;
786 if(t->fid == t->newfid)
787 wlock = FidFWlock;
788 else
789 wlock = 0;
790
791 /*
792 * The file identified by t->fid must be valid in the
793 * current session and must not have been opened for I/O
794 * by an open or create message.
795 */
796 if((ofid = fidGet(m->con, t->fid, wlock)) == nil)
797 return 0;
798 if(ofid->open){
799 werrstr("file open for I/O");
800 fidPut(ofid);
801 return 0;
802 }
803
804 /*
805 * If newfid is not the same as fid, allocate a new file;
806 * a side effect is checking newfid is not already in use (error);
807 * if there are no names to walk this will be equivalent to a
808 * simple 'clone' operation.
809 * It's a no-op if newfid is the same as fid and t->nwname is 0.
810 */
811 nfid = nil;
812 if(t->fid != t->newfid){
813 nfid = fidGet(m->con, t->newfid, FidFWlock|FidFCreate);
814 if(nfid == nil){
815 werrstr("%s: walk: newfid 0x%ud in use",
816 argv0, t->newfid);
817 fidPut(ofid);
818 return 0;
819 }
820 nfid->open = ofid->open & ~FidORclose;
821 nfid->file = fileIncRef(ofid->file);
822 nfid->qid = ofid->qid;
823 nfid->uid = vtstrdup(ofid->uid);
824 nfid->uname = vtstrdup(ofid->uname);
825 nfid->fsys = fsysIncRef(ofid->fsys);
826 fid = nfid;
827 }
828 else
829 fid = ofid;
830
831 r = &m->r;
832 r->nwqid = 0;
833
834 if(t->nwname == 0){
835 if(nfid != nil)
836 fidPut(nfid);
837 fidPut(ofid);
838
839 return 1;
840 }
841
842 file = fid->file;
843 fileIncRef(file);
844 qid = fid->qid;
845
846 for(nwname = 0; nwname < t->nwname; nwname++){
847 /*
848 * Walked elements must represent a directory and
849 * the implied user must have permission to search
850 * the directory. Walking .. is always allowed, so that
851 * you can't walk into a directory and then not be able
852 * to walk out of it.
853 */
854 if(!(qid.type & QTDIR)){
855 werrstr("not a directory");
856 break;
857 }
858 switch(permFile(file, fid, PermX)){
859 case 1:
860 break;
861 case 0:
862 if(strcmp(t->wname[nwname], "..") == 0)
863 break;
864 case -1:
865 goto Out;
866 }
867 if((nfile = fileWalk(file, t->wname[nwname])) == nil)
868 break;
869 fileDecRef(file);
870 file = nfile;
871 qid.type = QTFILE;
872 if(fileIsDir(file))
873 qid.type = QTDIR;
874 if(fileIsAppend(file))
875 qid.type |= QTAPPEND;
876 if(fileIsTemporary(file))
877 qid.type |= QTTMP;
878 if(fileIsExclusive(file))
879 qid.type |= QTEXCL;
880 qid.vers = fileGetMcount(file);
881 qid.path = fileGetId(file);
882 r->wqid[r->nwqid++] = qid;
883 }
884
885 if(nwname == t->nwname){
886 /*
887 * Walked all elements. Update the target fid
888 * from the temporary qid used during the walk,
889 * and tidy up.
890 */
891 fid->qid = r->wqid[r->nwqid-1];
892 fileDecRef(fid->file);
893 fid->file = file;
894
895 if(nfid != nil)
896 fidPut(nfid);
897
898 fidPut(ofid);
899 return 1;
900 }
901
902 Out:
903 /*
904 * Didn't walk all elements, 'clunk' nfid if it exists
905 * and leave fid untouched.
906 * It's not an error if some of the elements were walked OK.
907 */
908 fileDecRef(file);
909 if(nfid != nil)
910 fidClunk(nfid);
911
912 fidPut(ofid);
913 if(nwname == 0)
914 return 0;
915 return 1;
916 }
917
918 static int
rTflush(Msg * m)919 rTflush(Msg* m)
920 {
921 if(m->t.oldtag != NOTAG)
922 msgFlush(m);
923 return 1;
924 }
925
926 static void
parseAname(char * aname,char ** fsname,char ** path)927 parseAname(char *aname, char **fsname, char **path)
928 {
929 char *s;
930
931 if(aname && aname[0])
932 s = vtstrdup(aname);
933 else
934 s = vtstrdup("main/active");
935 *fsname = s;
936 if((*path = strchr(s, '/')) != nil)
937 *(*path)++ = '\0';
938 else
939 *path = "";
940 }
941
942 /*
943 * Check remote IP address against /mnt/ipok.
944 * Sources.cs.bell-labs.com uses this to disallow
945 * network connections from Sudan, Libya, etc.,
946 * following U.S. cryptography export regulations.
947 */
948 static int
conIPCheck(Con * con)949 conIPCheck(Con* con)
950 {
951 char ok[256], *p;
952 int fd;
953
954 if(con->flags&ConIPCheck){
955 if(con->remote[0] == 0){
956 werrstr("cannot verify unknown remote address");
957 return 0;
958 }
959 if(access("/mnt/ipok/ok", AEXIST) < 0){
960 /* mount closes the fd on success */
961 if((fd = open("/srv/ipok", ORDWR)) >= 0
962 && mount(fd, -1, "/mnt/ipok", MREPL, "") < 0)
963 close(fd);
964 if(access("/mnt/ipok/ok", AEXIST) < 0){
965 werrstr("cannot verify remote address");
966 return 0;
967 }
968 }
969 snprint(ok, sizeof ok, "/mnt/ipok/ok/%s", con->remote);
970 if((p = strchr(ok, '!')) != nil)
971 *p = 0;
972 if(access(ok, AEXIST) < 0){
973 werrstr("restricted remote address");
974 return 0;
975 }
976 }
977 return 1;
978 }
979
980 static int
rTattach(Msg * m)981 rTattach(Msg* m)
982 {
983 Fid *fid;
984 Fsys *fsys;
985 char *fsname, *path;
986
987 if((fid = fidGet(m->con, m->t.fid, FidFWlock|FidFCreate)) == nil)
988 return 0;
989
990 parseAname(m->t.aname, &fsname, &path);
991 if((fsys = fsysGet(fsname)) == nil){
992 fidClunk(fid);
993 vtfree(fsname);
994 return 0;
995 }
996 fid->fsys = fsys;
997
998 if(m->t.uname[0] != '\0')
999 fid->uname = vtstrdup(m->t.uname);
1000 else
1001 fid->uname = vtstrdup(unamenone);
1002
1003 if((fid->con->flags&ConIPCheck) && !conIPCheck(fid->con)){
1004 consPrint("reject %s from %s: %r\n", fid->uname, fid->con->remote);
1005 fidClunk(fid);
1006 vtfree(fsname);
1007 return 0;
1008 }
1009 if(fsysNoAuthCheck(fsys) || (m->con->flags&ConNoAuthCheck)){
1010 if((fid->uid = uidByUname(fid->uname)) == nil)
1011 fid->uid = vtstrdup(unamenone);
1012 }
1013 else if(!authCheck(&m->t, fid, fsys)){
1014 fidClunk(fid);
1015 vtfree(fsname);
1016 return 0;
1017 }
1018
1019 fsysFsRlock(fsys);
1020 if((fid->file = fsysGetRoot(fsys, path)) == nil){
1021 fsysFsRUnlock(fsys);
1022 fidClunk(fid);
1023 vtfree(fsname);
1024 return 0;
1025 }
1026 fsysFsRUnlock(fsys);
1027 vtfree(fsname);
1028
1029 fid->qid = (Qid){fileGetId(fid->file), 0, QTDIR};
1030 m->r.qid = fid->qid;
1031
1032 fidPut(fid);
1033 return 1;
1034 }
1035
1036 static int
rTauth(Msg * m)1037 rTauth(Msg* m)
1038 {
1039 int afd;
1040 Con *con;
1041 Fid *afid;
1042 Fsys *fsys;
1043 char *fsname, *path;
1044
1045 parseAname(m->t.aname, &fsname, &path);
1046 if((fsys = fsysGet(fsname)) == nil){
1047 vtfree(fsname);
1048 return 0;
1049 }
1050 vtfree(fsname);
1051
1052 if(fsysNoAuthCheck(fsys) || (m->con->flags&ConNoAuthCheck)){
1053 m->con->aok = 1;
1054 werrstr("authentication disabled");
1055 fsysPut(fsys);
1056 return 0;
1057 }
1058 if(strcmp(m->t.uname, unamenone) == 0){
1059 werrstr("user 'none' requires no authentication");
1060 fsysPut(fsys);
1061 return 0;
1062 }
1063
1064 con = m->con;
1065 if((afid = fidGet(con, m->t.afid, FidFWlock|FidFCreate)) == nil){
1066 fsysPut(fsys);
1067 return 0;
1068 }
1069 afid->fsys = fsys;
1070
1071 if((afd = open("/mnt/factotum/rpc", ORDWR)) < 0){
1072 werrstr("can't open \"/mnt/factotum/rpc\"");
1073 fidClunk(afid);
1074 return 0;
1075 }
1076 if((afid->rpc = auth_allocrpc(afd)) == nil){
1077 close(afd);
1078 werrstr("can't auth_allocrpc");
1079 fidClunk(afid);
1080 return 0;
1081 }
1082 if(auth_rpc(afid->rpc, "start", "proto=p9any role=server", 23) != ARok){
1083 werrstr("can't auth_rpc");
1084 fidClunk(afid);
1085 return 0;
1086 }
1087
1088 afid->open = FidOWrite|FidORead;
1089 afid->qid.type = QTAUTH;
1090 afid->qid.path = m->t.afid;
1091 afid->uname = vtstrdup(m->t.uname);
1092
1093 m->r.qid = afid->qid;
1094
1095 fidPut(afid);
1096 return 1;
1097 }
1098
1099 static int
rTversion(Msg * m)1100 rTversion(Msg* m)
1101 {
1102 int v;
1103 Con *con;
1104 Fcall *r, *t;
1105
1106 t = &m->t;
1107 r = &m->r;
1108 con = m->con;
1109
1110 qlock(&con->lock);
1111 if(con->state != ConInit){
1112 qunlock(&con->lock);
1113 werrstr("Tversion: down");
1114 return 0;
1115 }
1116 con->state = ConNew;
1117
1118 /*
1119 * Release the karma of past lives and suffering.
1120 * Should this be done before or after checking the
1121 * validity of the Tversion?
1122 */
1123 fidClunkAll(con);
1124
1125 if(t->tag != NOTAG){
1126 qunlock(&con->lock);
1127 werrstr("Tversion: invalid tag");
1128 return 0;
1129 }
1130
1131 if(t->msize < 256){
1132 qunlock(&con->lock);
1133 werrstr("Tversion: message size too small");
1134 return 0;
1135 }
1136 if(t->msize < con->msize)
1137 r->msize = t->msize;
1138 else
1139 r->msize = con->msize;
1140
1141 r->version = "unknown";
1142 if(t->version[0] == '9' && t->version[1] == 'P'){
1143 /*
1144 * Currently, the only defined version
1145 * is "9P2000"; ignore any later versions.
1146 */
1147 v = strtol(&t->version[2], 0, 10);
1148 if(v >= 2000){
1149 r->version = VERSION9P;
1150 con->msize = r->msize;
1151 con->state = ConUp;
1152 }
1153 else if(strcmp(t->version, "9PEoF") == 0){
1154 r->version = "9PEoF";
1155 con->msize = r->msize;
1156 con->state = ConMoribund;
1157
1158 /*
1159 * Don't want to attempt to write this
1160 * message as the connection may be already
1161 * closed.
1162 */
1163 m->state = MsgF;
1164 }
1165 }
1166 qunlock(&con->lock);
1167
1168 return 1;
1169 }
1170
1171 int (*rFcall[Tmax])(Msg*) = {
1172 [Tversion] = rTversion,
1173 [Tauth] = rTauth,
1174 [Tattach] = rTattach,
1175 [Tflush] = rTflush,
1176 [Twalk] = rTwalk,
1177 [Topen] = rTopen,
1178 [Tcreate] = rTcreate,
1179 [Tread] = rTread,
1180 [Twrite] = rTwrite,
1181 [Tclunk] = rTclunk,
1182 [Tremove] = rTremove,
1183 [Tstat] = rTstat,
1184 [Twstat] = rTwstat,
1185 };
1186