Blob


1 #include "stdinc.h"
3 #include "9.h"
5 enum {
6 OMODE = 0x7, /* Topen/Tcreate mode */
7 };
9 enum {
10 PermX = 1,
11 PermW = 2,
12 PermR = 4,
13 };
15 static char EPermission[] = "permission denied";
17 static int
18 permFile(File* file, Fid* fid, int perm)
19 {
20 char *u;
21 DirEntry de;
23 if(!fileGetDir(file, &de))
24 return -1;
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);
65 deCleanup(&de);
66 return 0;
67 }
69 static int
70 permFid(Fid* fid, int p)
71 {
72 return permFile(fid->file, fid, p);
73 }
75 static int
76 permParent(Fid* fid, int p)
77 {
78 int r;
79 File *parent;
81 parent = fileGetParent(fid->file);
82 r = permFile(parent, fid, p);
83 fileDecRef(parent);
85 return r;
86 }
88 int
89 validFileName(char* name)
90 {
91 char *p;
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;
104 for(p = name; *p != '\0'; p++){
105 if((*p & 0xFF) < 040){
106 werrstr("bad character in file name");
107 return 0;
111 return 1;
114 static int
115 rTwstat(Msg* m)
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;
124 if((fid = fidGet(m->con, m->t.fid, FidFWlock)) == nil)
125 return 0;
127 gid = uid = nil;
128 retval = 0;
130 if(strcmp(fid->uname, unamenone) == 0 || (fid->qid.type & QTAUTH)){
131 werrstr(EPermission);
132 goto error0;
134 if(fileIsRoFs(fid->file) || !groupWriteMember(fid->uname)){
135 werrstr("read-only filesystem");
136 goto error0;
139 if(!fileGetDir(fid->file, &de))
140 goto error0;
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;
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.
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;
166 tsync = 0;
168 if(dir.qid.vers != (u32int)~0){
169 if(dir.qid.vers != de.mcount){
170 werrstr("wstat -- attempt to change qid.vers");
171 goto error;
173 tsync = 0;
175 if(dir.muid != nil && *dir.muid != '\0'){
176 if((uid = uidByUname(dir.muid)) == nil){
177 werrstr("wstat -- unknown muid");
178 goto error;
180 if(strcmp(uid, de.mid) != 0){
181 werrstr("wstat -- attempt to change muid");
182 goto error;
184 vtfree(uid);
185 uid = nil;
186 tsync = 0;
189 /*
190 * Check .qid.type and .mode agree if neither is defaulted.
191 */
192 if(dir.qid.type != (uchar)~0 && dir.mode != (u32int)~0){
193 if(dir.qid.type != ((dir.mode>>24) & 0xFF)){
194 werrstr("wstat -- qid.type/mode mismatch");
195 goto error;
199 op = 0;
201 oldmode = de.mode;
202 if(dir.qid.type != (uchar)~0 || dir.mode != (u32int)~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;
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;
226 if((de.mode^mode) & ModeDir){
227 werrstr("wstat -- attempt to change directory bit");
228 goto error;
231 if((de.mode & (ModeAppend|ModeExclusive|ModeTemporary|0777)) != mode){
232 de.mode &= ~(ModeAppend|ModeExclusive|ModeTemporary|0777);
233 de.mode |= mode;
234 op = 1;
236 tsync = 0;
239 if(dir.mtime != (u32int)~0){
240 if(dir.mtime != de.mtime){
241 de.mtime = dir.mtime;
242 op = 1;
244 tsync = 0;
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;
257 if(de.mode & ModeDir){
258 werrstr("wstat -- attempt to change length of directory");
259 goto error;
261 de.size = dir.length;
262 op = 1;
264 tsync = 0;
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;
277 tsync = 0;
279 else
280 gid = vtstrdup(de.gid);
282 wstatallow = (fsysWstatAllow(fid->fsys) || (m->con->flags&ConWstatAllow));
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;
290 if(op && !wstatallow){
291 if(strcmp(fid->uid, de.uid) != 0 && !gl){
292 werrstr("wstat -- not owner or group leader");
293 goto error;
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;
309 vtfree(de.gid);
310 de.gid = gid;
311 gid = nil;
312 op = 1;
313 tsync = 0;
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;
331 tsync = 0;
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;
342 if(strcmp(uid, de.uid) != 0){
343 if(!wstatallow){
344 werrstr("wstat -- not owner");
345 goto error;
347 if(strcmp(uid, uidnoworld) == 0){
348 werrstr(EPermission);
349 goto error;
351 vtfree(de.uid);
352 de.uid = uid;
353 uid = nil;
354 op = 1;
356 tsync = 0;
359 if(op)
360 retval = fileSetDir(fid->file, &de, fid->uid);
361 else
362 retval = 1;
364 if(tsync){
365 /*
366 * All values were defaulted,
367 * make the state of the file exactly what it
368 * claims to be before returning...
369 */
370 USED(tsync);
373 error:
374 deCleanup(&de);
375 vtfree(strs);
376 if(gid != nil)
377 vtfree(gid);
378 if(uid != nil)
379 vtfree(uid);
380 error0:
381 fidPut(fid);
382 return retval;
383 };
385 static int
386 rTstat(Msg* m)
388 Dir dir;
389 Fid *fid;
390 DirEntry de;
392 if((fid = fidGet(m->con, m->t.fid, 0)) == nil)
393 return 0;
394 if(fid->qid.type & QTAUTH){
395 memset(&dir, 0, sizeof(Dir));
396 dir.qid = fid->qid;
397 dir.mode = DMAUTH;
398 dir.atime = time(0L);
399 dir.mtime = dir.atime;
400 dir.length = 0;
401 dir.name = "#¿";
402 dir.uid = fid->uname;
403 dir.gid = fid->uname;
404 dir.muid = fid->uname;
406 if((m->r.nstat = convD2M(&dir, m->data, m->con->msize)) == 0){
407 werrstr("stat QTAUTH botch");
408 fidPut(fid);
409 return 0;
411 m->r.stat = m->data;
413 fidPut(fid);
414 return 1;
416 if(!fileGetDir(fid->file, &de)){
417 fidPut(fid);
418 return 0;
420 fidPut(fid);
422 /*
423 * TODO: optimise this copy (in convS2M) away somehow.
424 * This pettifoggery with m->data will do for the moment.
425 */
426 m->r.nstat = dirDe2M(&de, m->data, m->con->msize);
427 m->r.stat = m->data;
428 deCleanup(&de);
430 return 1;
433 static int
434 _rTclunk(Fid* fid, int remove)
436 int rok;
438 if(fid->excl)
439 exclFree(fid);
441 rok = 1;
442 if(remove && !(fid->qid.type & QTAUTH)){
443 if((rok = permParent(fid, PermW)) > 0)
444 rok = fileRemove(fid->file, fid->uid);
446 fidClunk(fid);
448 return rok;
451 static int
452 rTremove(Msg* m)
454 Fid *fid;
456 if((fid = fidGet(m->con, m->t.fid, FidFWlock)) == nil)
457 return 0;
458 return _rTclunk(fid, 1);
461 static int
462 rTclunk(Msg* m)
464 Fid *fid;
466 if((fid = fidGet(m->con, m->t.fid, FidFWlock)) == nil)
467 return 0;
468 _rTclunk(fid, (fid->open & FidORclose));
470 return 1;
473 static int
474 rTwrite(Msg* m)
476 Fid *fid;
477 int count, n;
479 if((fid = fidGet(m->con, m->t.fid, 0)) == nil)
480 return 0;
481 if(!(fid->open & FidOWrite)){
482 werrstr("fid not open for write");
483 goto error;
486 count = m->t.count;
487 if(count < 0 || count > m->con->msize-IOHDRSZ){
488 werrstr("write count too big");
489 goto error;
491 if(m->t.offset < 0){
492 werrstr("write offset negative");
493 goto error;
495 if(fid->excl != nil && !exclUpdate(fid))
496 goto error;
498 if(fid->qid.type & QTDIR){
499 werrstr("is a directory");
500 goto error;
502 else if(fid->qid.type & QTAUTH)
503 n = authWrite(fid, m->t.data, count);
504 else
505 n = fileWrite(fid->file, m->t.data, count, m->t.offset, fid->uid);
506 if(n < 0)
507 goto error;
510 m->r.count = n;
512 fidPut(fid);
513 return 1;
515 error:
516 fidPut(fid);
517 return 0;
520 static int
521 rTread(Msg* m)
523 Fid *fid;
524 uchar *data;
525 int count, n;
527 if((fid = fidGet(m->con, m->t.fid, 0)) == nil)
528 return 0;
529 if(!(fid->open & FidORead)){
530 werrstr("fid not open for read");
531 goto error;
534 count = m->t.count;
535 if(count < 0 || count > m->con->msize-IOHDRSZ){
536 werrstr("read count too big");
537 goto error;
539 if(m->t.offset < 0){
540 werrstr("read offset negative");
541 goto error;
543 if(fid->excl != nil && !exclUpdate(fid))
544 goto error;
546 /*
547 * TODO: optimise this copy (in convS2M) away somehow.
548 * This pettifoggery with m->data will do for the moment.
549 */
550 data = m->data+IOHDRSZ;
551 if(fid->qid.type & QTDIR)
552 n = dirRead(fid, data, count, m->t.offset);
553 else if(fid->qid.type & QTAUTH)
554 n = authRead(fid, data, count);
555 else
556 n = fileRead(fid->file, data, count, m->t.offset);
557 if(n < 0)
558 goto error;
560 m->r.count = n;
561 m->r.data = (char*)data;
563 fidPut(fid);
564 return 1;
566 error:
567 fidPut(fid);
568 return 0;
571 static int
572 rTcreate(Msg* m)
574 Fid *fid;
575 File *file;
576 ulong mode;
577 int omode, open, perm;
579 if((fid = fidGet(m->con, m->t.fid, FidFWlock)) == nil)
580 return 0;
581 if(fid->open){
582 werrstr("fid open for I/O");
583 goto error;
585 if(fileIsRoFs(fid->file) || !groupWriteMember(fid->uname)){
586 werrstr("read-only filesystem");
587 goto error;
589 if(!fileIsDir(fid->file)){
590 werrstr("not a directory");
591 goto error;
593 if(permFid(fid, PermW) <= 0)
594 goto error;
595 if(!validFileName(m->t.name))
596 goto error;
597 if(strcmp(fid->uid, uidnoworld) == 0){
598 werrstr(EPermission);
599 goto error;
602 omode = m->t.mode & OMODE;
603 open = 0;
605 if(omode == OREAD || omode == ORDWR || omode == OEXEC)
606 open |= FidORead;
607 if(omode == OWRITE || omode == ORDWR)
608 open |= FidOWrite;
609 if((open & (FidOWrite|FidORead)) == 0){
610 werrstr("unknown mode");
611 goto error;
613 if(m->t.perm & DMDIR){
614 if((m->t.mode & (ORCLOSE|OTRUNC)) || (open & FidOWrite)){
615 werrstr("illegal mode");
616 goto error;
618 if(m->t.perm & DMAPPEND){
619 werrstr("illegal perm");
620 goto error;
624 mode = fileGetMode(fid->file);
625 perm = m->t.perm;
626 if(m->t.perm & DMDIR)
627 perm &= ~0777|(mode & 0777);
628 else
629 perm &= ~0666|(mode & 0666);
630 mode = perm & 0777;
631 if(m->t.perm & DMDIR)
632 mode |= ModeDir;
633 if(m->t.perm & DMAPPEND)
634 mode |= ModeAppend;
635 if(m->t.perm & DMEXCL)
636 mode |= ModeExclusive;
637 if(m->t.perm & DMTMP)
638 mode |= ModeTemporary;
640 if((file = fileCreate(fid->file, m->t.name, mode, fid->uid)) == nil){
641 fidPut(fid);
642 return 0;
644 fileDecRef(fid->file);
646 fid->qid.vers = fileGetMcount(file);
647 fid->qid.path = fileGetId(file);
648 fid->file = file;
649 mode = fileGetMode(fid->file);
650 if(mode & ModeDir)
651 fid->qid.type = QTDIR;
652 else
653 fid->qid.type = QTFILE;
654 if(mode & ModeAppend)
655 fid->qid.type |= QTAPPEND;
656 if(mode & ModeExclusive){
657 fid->qid.type |= QTEXCL;
658 assert(exclAlloc(fid) != 0);
660 if(m->t.mode & ORCLOSE)
661 open |= FidORclose;
662 fid->open = open;
664 m->r.qid = fid->qid;
665 m->r.iounit = m->con->msize-IOHDRSZ;
667 fidPut(fid);
668 return 1;
670 error:
671 fidPut(fid);
672 return 0;
675 static int
676 rTopen(Msg* m)
678 Fid *fid;
679 int isdir, mode, omode, open, rofs;
681 if((fid = fidGet(m->con, m->t.fid, FidFWlock)) == nil)
682 return 0;
683 if(fid->open){
684 werrstr("fid open for I/O");
685 goto error;
688 isdir = fileIsDir(fid->file);
689 open = 0;
690 rofs = fileIsRoFs(fid->file) || !groupWriteMember(fid->uname);
692 if(m->t.mode & ORCLOSE){
693 if(isdir){
694 werrstr("is a directory");
695 goto error;
697 if(rofs){
698 werrstr("read-only filesystem");
699 goto error;
701 if(permParent(fid, PermW) <= 0)
702 goto error;
704 open |= FidORclose;
707 omode = m->t.mode & OMODE;
708 if(omode == OREAD || omode == ORDWR){
709 if(permFid(fid, PermR) <= 0)
710 goto error;
711 open |= FidORead;
713 if(omode == OWRITE || omode == ORDWR || (m->t.mode & OTRUNC)){
714 if(isdir){
715 werrstr("is a directory");
716 goto error;
718 if(rofs){
719 werrstr("read-only filesystem");
720 goto error;
722 if(permFid(fid, PermW) <= 0)
723 goto error;
724 open |= FidOWrite;
726 if(omode == OEXEC){
727 if(isdir){
728 werrstr("is a directory");
729 goto error;
731 if(permFid(fid, PermX) <= 0)
732 goto error;
733 open |= FidORead;
735 if((open & (FidOWrite|FidORead)) == 0){
736 werrstr("unknown mode");
737 goto error;
740 mode = fileGetMode(fid->file);
741 if((mode & ModeExclusive) && exclAlloc(fid) == 0)
742 goto error;
744 /*
745 * Everything checks out, try to commit any changes.
746 */
747 if((m->t.mode & OTRUNC) && !(mode & ModeAppend))
748 if(!fileTruncate(fid->file, fid->uid))
749 goto error;
751 if(isdir && fid->db != nil){
752 dirBufFree(fid->db);
753 fid->db = nil;
756 fid->qid.vers = fileGetMcount(fid->file);
757 m->r.qid = fid->qid;
758 m->r.iounit = m->con->msize-IOHDRSZ;
760 fid->open = open;
762 fidPut(fid);
763 return 1;
765 error:
766 if(fid->excl != nil)
767 exclFree(fid);
768 fidPut(fid);
769 return 0;
772 static int
773 rTwalk(Msg* m)
775 Qid qid;
776 Fcall *r, *t;
777 int nwname, wlock;
778 File *file, *nfile;
779 Fid *fid, *ofid, *nfid;
781 t = &m->t;
782 if(t->fid == t->newfid)
783 wlock = FidFWlock;
784 else
785 wlock = 0;
787 /*
788 * The file identified by t->fid must be valid in the
789 * current session and must not have been opened for I/O
790 * by an open or create message.
791 */
792 if((ofid = fidGet(m->con, t->fid, wlock)) == nil)
793 return 0;
794 if(ofid->open){
795 werrstr("file open for I/O");
796 fidPut(ofid);
797 return 0;
800 /*
801 * If newfid is not the same as fid, allocate a new file;
802 * a side effect is checking newfid is not already in use (error);
803 * if there are no names to walk this will be equivalent to a
804 * simple 'clone' operation.
805 * It's a no-op if newfid is the same as fid and t->nwname is 0.
806 */
807 nfid = nil;
808 if(t->fid != t->newfid){
809 nfid = fidGet(m->con, t->newfid, FidFWlock|FidFCreate);
810 if(nfid == nil){
811 werrstr("%s: walk: newfid 0x%ud in use",
812 argv0, t->newfid);
813 fidPut(ofid);
814 return 0;
816 nfid->open = ofid->open & ~FidORclose;
817 nfid->file = fileIncRef(ofid->file);
818 nfid->qid = ofid->qid;
819 nfid->uid = vtstrdup(ofid->uid);
820 nfid->uname = vtstrdup(ofid->uname);
821 nfid->fsys = fsysIncRef(ofid->fsys);
822 fid = nfid;
824 else
825 fid = ofid;
827 r = &m->r;
828 r->nwqid = 0;
830 if(t->nwname == 0){
831 if(nfid != nil)
832 fidPut(nfid);
833 fidPut(ofid);
835 return 1;
838 file = fid->file;
839 fileIncRef(file);
840 qid = fid->qid;
842 for(nwname = 0; nwname < t->nwname; nwname++){
843 /*
844 * Walked elements must represent a directory and
845 * the implied user must have permission to search
846 * the directory. Walking .. is always allowed, so that
847 * you can't walk into a directory and then not be able
848 * to walk out of it.
849 */
850 if(!(qid.type & QTDIR)){
851 werrstr("not a directory");
852 break;
854 switch(permFile(file, fid, PermX)){
855 case 1:
856 break;
857 case 0:
858 if(strcmp(t->wname[nwname], "..") == 0)
859 break;
860 case -1:
861 goto Out;
863 if((nfile = fileWalk(file, t->wname[nwname])) == nil)
864 break;
865 fileDecRef(file);
866 file = nfile;
867 qid.type = QTFILE;
868 if(fileIsDir(file))
869 qid.type = QTDIR;
870 if(fileIsAppend(file))
871 qid.type |= QTAPPEND;
872 if(fileIsTemporary(file))
873 qid.type |= QTTMP;
874 if(fileIsExclusive(file))
875 qid.type |= QTEXCL;
876 qid.vers = fileGetMcount(file);
877 qid.path = fileGetId(file);
878 r->wqid[r->nwqid++] = qid;
881 if(nwname == t->nwname){
882 /*
883 * Walked all elements. Update the target fid
884 * from the temporary qid used during the walk,
885 * and tidy up.
886 */
887 fid->qid = r->wqid[r->nwqid-1];
888 fileDecRef(fid->file);
889 fid->file = file;
891 if(nfid != nil)
892 fidPut(nfid);
894 fidPut(ofid);
895 return 1;
898 Out:
899 /*
900 * Didn't walk all elements, 'clunk' nfid if it exists
901 * and leave fid untouched.
902 * It's not an error if some of the elements were walked OK.
903 */
904 fileDecRef(file);
905 if(nfid != nil)
906 fidClunk(nfid);
908 fidPut(ofid);
909 if(nwname == 0)
910 return 0;
911 return 1;
914 static int
915 rTflush(Msg* m)
917 if(m->t.oldtag != NOTAG)
918 msgFlush(m);
919 return 1;
922 static void
923 parseAname(char *aname, char **fsname, char **path)
925 char *s;
927 if(aname && aname[0])
928 s = vtstrdup(aname);
929 else
930 s = vtstrdup("main/active");
931 *fsname = s;
932 if((*path = strchr(s, '/')) != nil)
933 *(*path)++ = '\0';
934 else
935 *path = "";
938 #ifndef PLAN9PORT
939 /*
940 * Check remote IP address against /mnt/ipok.
941 * Sources.cs.bell-labs.com uses this to disallow
942 * network connections from Sudan, Libya, etc.,
943 * following U.S. cryptography export regulations.
944 */
945 static int
946 conIPCheck(Con* con)
948 char ok[256], *p;
949 int fd;
951 if(con->flags&ConIPCheck){
952 if(con->remote[0] == 0){
953 werrstr("cannot verify unknown remote address");
954 return 0;
956 if(access("/mnt/ipok/ok", AEXIST) < 0){
957 /* mount closes the fd on success */
958 if((fd = open("/srv/ipok", ORDWR)) >= 0
959 && mount(fd, -1, "/mnt/ipok", MREPL, "") < 0)
960 close(fd);
961 if(access("/mnt/ipok/ok", AEXIST) < 0){
962 werrstr("cannot verify remote address");
963 return 0;
966 snprint(ok, sizeof ok, "/mnt/ipok/ok/%s", con->remote);
967 if((p = strchr(ok, '!')) != nil)
968 *p = 0;
969 if(access(ok, AEXIST) < 0){
970 werrstr("restricted remote address");
971 return 0;
974 return 1;
976 #endif
978 static int
979 rTattach(Msg* m)
981 Fid *fid;
982 Fsys *fsys;
983 char *fsname, *path;
985 if((fid = fidGet(m->con, m->t.fid, FidFWlock|FidFCreate)) == nil)
986 return 0;
988 parseAname(m->t.aname, &fsname, &path);
989 if((fsys = fsysGet(fsname)) == nil){
990 fidClunk(fid);
991 vtfree(fsname);
992 return 0;
994 fid->fsys = fsys;
996 if(m->t.uname[0] != '\0')
997 fid->uname = vtstrdup(m->t.uname);
998 else
999 fid->uname = vtstrdup(unamenone);
1001 #ifndef PLAN9PORT
1002 if((fid->con->flags&ConIPCheck) && !conIPCheck(fid->con)){
1003 consPrint("reject %s from %s: %r\n", fid->uname, fid->con->remote);
1004 fidClunk(fid);
1005 vtfree(fsname);
1006 return 0;
1008 #endif
1009 if(fsysNoAuthCheck(fsys) || (m->con->flags&ConNoAuthCheck)){
1010 if((fid->uid = uidByUname(fid->uname)) == nil)
1011 fid->uid = vtstrdup(unamenone);
1013 else if(!authCheck(&m->t, fid, fsys)){
1014 fidClunk(fid);
1015 vtfree(fsname);
1016 return 0;
1019 fsysFsRlock(fsys);
1020 if((fid->file = fsysGetRoot(fsys, path)) == nil){
1021 fsysFsRUnlock(fsys);
1022 fidClunk(fid);
1023 vtfree(fsname);
1024 return 0;
1026 fsysFsRUnlock(fsys);
1027 vtfree(fsname);
1029 fid->qid = (Qid){fileGetId(fid->file), 0, QTDIR};
1030 m->r.qid = fid->qid;
1032 fidPut(fid);
1033 return 1;
1036 static int
1037 rTauth(Msg* m)
1039 #ifndef PLAN9PORT
1040 int afd;
1041 #endif
1042 Con *con;
1043 Fid *afid;
1044 Fsys *fsys;
1045 char *fsname, *path;
1047 parseAname(m->t.aname, &fsname, &path);
1048 if((fsys = fsysGet(fsname)) == nil){
1049 vtfree(fsname);
1050 return 0;
1052 vtfree(fsname);
1054 if(fsysNoAuthCheck(fsys) || (m->con->flags&ConNoAuthCheck)){
1055 m->con->aok = 1;
1056 werrstr("authentication disabled");
1057 fsysPut(fsys);
1058 return 0;
1060 if(strcmp(m->t.uname, unamenone) == 0){
1061 werrstr("user 'none' requires no authentication");
1062 fsysPut(fsys);
1063 return 0;
1066 con = m->con;
1067 if((afid = fidGet(con, m->t.afid, FidFWlock|FidFCreate)) == nil){
1068 fsysPut(fsys);
1069 return 0;
1071 afid->fsys = fsys;
1073 #ifndef PLAN9PORT
1074 if((afd = open("/mnt/factotum/rpc", ORDWR)) < 0){
1075 werrstr("can't open \"/mnt/factotum/rpc\"");
1076 fidClunk(afid);
1077 return 0;
1079 #endif
1081 #ifdef PLAN9PORT
1082 if((afid->rpc = auth_allocrpc()) == nil){
1083 #else
1084 if((afid->rpc = auth_allocrpc(afd)) == nil){
1085 close(afd);
1086 #endif
1087 werrstr("can't auth_allocrpc");
1088 fidClunk(afid);
1089 return 0;
1091 if(auth_rpc(afid->rpc, "start", "proto=p9any role=server", 23) != ARok){
1092 werrstr("can't auth_rpc");
1093 fidClunk(afid);
1094 return 0;
1097 afid->open = FidOWrite|FidORead;
1098 afid->qid.type = QTAUTH;
1099 afid->qid.path = m->t.afid;
1100 afid->uname = vtstrdup(m->t.uname);
1102 m->r.qid = afid->qid;
1104 fidPut(afid);
1105 return 1;
1108 static int
1109 rTversion(Msg* m)
1111 int v;
1112 Con *con;
1113 Fcall *r, *t;
1115 t = &m->t;
1116 r = &m->r;
1117 con = m->con;
1119 qlock(&con->lock);
1120 if(con->state != ConInit){
1121 qunlock(&con->lock);
1122 werrstr("Tversion: down");
1123 return 0;
1125 con->state = ConNew;
1128 * Release the karma of past lives and suffering.
1129 * Should this be done before or after checking the
1130 * validity of the Tversion?
1132 fidClunkAll(con);
1134 if(t->tag != NOTAG){
1135 qunlock(&con->lock);
1136 werrstr("Tversion: invalid tag");
1137 return 0;
1140 if(t->msize < 256){
1141 qunlock(&con->lock);
1142 werrstr("Tversion: message size too small");
1143 return 0;
1145 if(t->msize < con->msize)
1146 r->msize = t->msize;
1147 else
1148 r->msize = con->msize;
1150 r->version = "unknown";
1151 if(t->version[0] == '9' && t->version[1] == 'P'){
1153 * Currently, the only defined version
1154 * is "9P2000"; ignore any later versions.
1156 v = strtol(&t->version[2], 0, 10);
1157 if(v >= 2000){
1158 r->version = VERSION9P;
1159 con->msize = r->msize;
1160 con->state = ConUp;
1162 else if(strcmp(t->version, "9PEoF") == 0){
1163 r->version = "9PEoF";
1164 con->msize = r->msize;
1165 con->state = ConMoribund;
1168 * Don't want to attempt to write this
1169 * message as the connection may be already
1170 * closed.
1172 m->state = MsgF;
1175 qunlock(&con->lock);
1177 return 1;
1180 int (*rFcall[Tmax])(Msg*) = {
1181 [Tversion] = rTversion,
1182 [Tauth] = rTauth,
1183 [Tattach] = rTattach,
1184 [Tflush] = rTflush,
1185 [Twalk] = rTwalk,
1186 [Topen] = rTopen,
1187 [Tcreate] = rTcreate,
1188 [Tread] = rTread,
1189 [Twrite] = rTwrite,
1190 [Tclunk] = rTclunk,
1191 [Tremove] = rTremove,
1192 [Tstat] = rTstat,
1193 [Twstat] = rTwstat,