xref: /plan9/sys/src/cmd/auth/factotum/fs.c (revision 3b86f2f88bade1f00206c7aa750b7add255f5724)
1 #include "dat.h"
2 
3 int		askforkeys = 1;
4 char		*authaddr;
5 int		debug;
6 int		doprivate = 1;
7 int		gflag;
8 char		*owner;
9 int		kflag;
10 char		*mtpt = "/mnt";
11 Keyring	*ring;
12 char		*service;
13 int		sflag;
14 int		uflag;
15 
16 extern Srv		fs;
17 static void		notifyf(void*, char*);
18 static void		private(void);
19 
20 char	Easproto[]		= "auth server protocol botch";
21 char Ebadarg[]		= "invalid argument";
22 char Ebadkey[]		= "bad key";
23 char Enegotiation[]	= "negotiation failed, no common protocols or keys";
24 char Etoolarge[]	= "rpc too large";
25 
26 Proto*
27 prototab[] =
28 {
29 	&apop,
30 	&chap,
31 	&cram,
32 	&httpdigest,
33 	&mschap,
34 	&p9any,
35 	&p9cr,
36 	&p9sk1,
37 	&p9sk2,
38 	&pass,
39 /*	&srs, */
40 	&rsa,
41 	&vnc,
42 	&wep,
43 	nil,
44 };
45 
46 void
usage(void)47 usage(void)
48 {
49 	fprint(2, "usage: %s [-DSdknpu] [-a authaddr] [-m mtpt] [-s service]\n",
50 		argv0);
51 	fprint(2, "or    %s -g 'params'\n", argv0);
52 	exits("usage");
53 }
54 
55 void
main(int argc,char ** argv)56 main(int argc, char **argv)
57 {
58 	int i, trysecstore;
59 	char err[ERRMAX], *s;
60 	Dir d;
61 	Proto *p;
62 	char *secstorepw;
63 
64 	trysecstore = 1;
65 	secstorepw = nil;
66 
67 	ARGBEGIN{
68 	case 'D':
69 		chatty9p++;
70 		break;
71 	case 'S':		/* server: read nvram, no prompting for keys */
72 		askforkeys = 0;
73 		trysecstore = 0;
74 		sflag = 1;
75 		break;
76 	case 'a':
77 		authaddr = EARGF(usage());
78 		break;
79 	case 'd':
80 		debug = 1;
81 		doprivate = 0;
82 		break;
83 	case 'g':		/* get: prompt for key for name and domain */
84 		gflag = 1;
85 		break;
86 	case 'k':		/* reinitialize nvram */
87 		kflag = 1;
88 		break;
89 	case 'm':		/* set default mount point */
90 		mtpt = EARGF(usage());
91 		break;
92 	case 'n':
93 		trysecstore = 0;
94 		break;
95 	case 'p':
96 		doprivate = 0;
97 		break;
98 	case 's':		/* set service name */
99 		service = EARGF(usage());
100 		break;
101 	case 'u':		/* user: set hostowner */
102 		uflag = 1;
103 		break;
104 	default:
105 		usage();
106 	}ARGEND
107 
108 	if(argc != 0 && !gflag)
109 		usage();
110 	if(doprivate)
111 		private();
112 
113 	initcap();
114 
115 	quotefmtinstall();
116 	fmtinstall('A', _attrfmt);
117 	fmtinstall('N', attrnamefmt);
118 	fmtinstall('H', encodefmt);
119 
120 	ring = emalloc(sizeof(*ring));
121 	notify(notifyf);
122 
123 	if(gflag){
124 		if(argc != 1)
125 			usage();
126 		askuser(argv[0]);
127 		exits(nil);
128 	}
129 
130 	for(i=0; prototab[i]; i++){
131 		p = prototab[i];
132 		if(p->name == nil)
133 			sysfatal("protocol %d has no name", i);
134 		if(p->init == nil)
135 			sysfatal("protocol %s has no init", p->name);
136 		if(p->write == nil)
137 			sysfatal("protocol %s has no write", p->name);
138 		if(p->read == nil)
139 			sysfatal("protocol %s has no read", p->name);
140 		if(p->close == nil)
141 			sysfatal("protocol %s has no close", p->name);
142 		if(p->keyprompt == nil)
143 			p->keyprompt = "";
144 	}
145 
146 	if(sflag){
147 		s = getnvramkey(kflag ? NVwrite : NVwriteonerr, &secstorepw);
148 		if(s == nil)
149 			fprint(2, "factotum warning: cannot read nvram: %r\n");
150 		else if(ctlwrite(s, 0) < 0)
151 			fprint(2, "factotum warning: cannot add nvram key: %r\n");
152 		if(secstorepw != nil)
153 			trysecstore = 1;
154 		if (s != nil) {
155 			memset(s, 0, strlen(s));
156 			free(s);
157 		}
158 	} else if(uflag)
159 		promptforhostowner();
160 	owner = getuser();
161 
162 	if(trysecstore){
163 		if(havesecstore() == 1){
164 			while(secstorefetch(secstorepw) < 0){
165 				rerrstr(err, sizeof err);
166 				if(strcmp(err, "cancel") == 0)
167 					break;
168 				fprint(2, "factotum: secstorefetch: %r\n");
169 				fprint(2, "Enter an empty password to quit.\n");
170 				free(secstorepw);
171 				secstorepw = nil; /* just try nvram pw once */
172 			}
173 		}else{
174 /*
175 			rerrstr(err, sizeof err);
176 			if(*err)
177 				fprint(2, "factotum: havesecstore: %r\n");
178 */
179 		}
180 	}
181 
182 	postmountsrv(&fs, service, mtpt, MBEFORE);
183 	if(service){
184 		nulldir(&d);
185 		d.mode = 0666;
186 		s = emalloc(10+strlen(service));
187 		strcpy(s, "/srv/");
188 		strcat(s, service);
189 		if(dirwstat(s, &d) < 0)
190 			fprint(2, "factotum warning: cannot chmod 666 %s: %r\n", s);
191 		free(s);
192 	}
193 	exits(nil);
194 }
195 
196 char *pmsg = "Warning! %s can't protect itself from debugging: %r\n";
197 char *smsg = "Warning! %s can't turn off swapping: %r\n";
198 
199 /* don't allow other processes to debug us and steal keys */
200 static void
private(void)201 private(void)
202 {
203 	int fd;
204 	char buf[64];
205 
206 	snprint(buf, sizeof(buf), "#p/%d/ctl", getpid());
207 	fd = open(buf, OWRITE);
208 	if(fd < 0){
209 		fprint(2, pmsg, argv0);
210 		return;
211 	}
212 	if(fprint(fd, "private") < 0)
213 		fprint(2, pmsg, argv0);
214 	if(fprint(fd, "noswap") < 0)
215 		fprint(2, smsg, argv0);
216 	close(fd);
217 }
218 
219 static void
notifyf(void *,char * s)220 notifyf(void*, char *s)
221 {
222 	if(strncmp(s, "interrupt", 9) == 0)
223 		noted(NCONT);
224 	noted(NDFLT);
225 }
226 
227 enum
228 {
229 	Qroot,
230 	Qfactotum,
231 	Qrpc,
232 	Qkeylist,
233 	Qprotolist,
234 	Qconfirm,
235 	Qlog,
236 	Qctl,
237 	Qneedkey,
238 };
239 
240 Qid
mkqid(int type,int path)241 mkqid(int type, int path)
242 {
243 	Qid q;
244 
245 	q.type = type;
246 	q.path = path;
247 	q.vers = 0;
248 	return q;
249 }
250 
251 static void
fsattach(Req * r)252 fsattach(Req *r)
253 {
254 	r->fid->qid = mkqid(QTDIR, Qroot);
255 	r->ofcall.qid = r->fid->qid;
256 	respond(r, nil);
257 }
258 
259 static struct {
260 	char *name;
261 	int qidpath;
262 	ulong perm;
263 } dirtab[] = {
264 	"confirm",	Qconfirm,	0600|DMEXCL,		/* we know this is slot #0 below */
265 	"needkey", Qneedkey,	0600|DMEXCL,		/* we know this is slot #1 below */
266 	"ctl",		Qctl,			0644,
267 	"rpc",	Qrpc,		0666,
268 	"proto",	Qprotolist,	0444,
269 	"log",	Qlog,		0400|DMEXCL,
270 };
271 static int inuse[nelem(dirtab)];
272 int *confirminuse = &inuse[0];
273 int *needkeyinuse = &inuse[1];
274 
275 static void
fillstat(Dir * dir,char * name,int type,int path,ulong perm)276 fillstat(Dir *dir, char *name, int type, int path, ulong perm)
277 {
278 	dir->name = estrdup(name);
279 	dir->uid = estrdup(owner);
280 	dir->gid = estrdup(owner);
281 	dir->mode = perm;
282 	dir->length = 0;
283 	dir->qid = mkqid(type, path);
284 	dir->atime = time(0);
285 	dir->mtime = time(0);
286 	dir->muid = estrdup("");
287 }
288 
289 static int
rootdirgen(int n,Dir * dir,void *)290 rootdirgen(int n, Dir *dir, void*)
291 {
292 	if(n > 0)
293 		return -1;
294 	fillstat(dir, "factotum", QTDIR, Qfactotum, DMDIR|0555);
295 	return 0;
296 }
297 
298 static int
fsdirgen(int n,Dir * dir,void *)299 fsdirgen(int n, Dir *dir, void*)
300 {
301 	if(n >= nelem(dirtab))
302 		return -1;
303 	fillstat(dir, dirtab[n].name, 0, dirtab[n].qidpath, dirtab[n].perm);
304 	return 0;
305 }
306 
307 static char*
fswalk1(Fid * fid,char * name,Qid * qid)308 fswalk1(Fid *fid, char *name, Qid *qid)
309 {
310 	int i;
311 
312 	switch((ulong)fid->qid.path){
313 	default:
314 		return "cannot happen";
315 	case Qroot:
316 		if(strcmp(name, "factotum") == 0){
317 			*qid = mkqid(QTDIR, Qfactotum);
318 			fid->qid = *qid;
319 			return nil;
320 		}
321 		if(strcmp(name, "..") == 0){
322 			*qid = fid->qid;
323 			return nil;
324 		}
325 		return "not found";
326 	case Qfactotum:
327 		for(i=0; i<nelem(dirtab); i++)
328 			if(strcmp(name, dirtab[i].name) == 0){
329 				*qid = mkqid(0, dirtab[i].qidpath);
330 				fid->qid = *qid;
331 				return nil;
332 			}
333 		if(strcmp(name, "..") == 0){
334 			*qid = mkqid(QTDIR, Qroot);
335 			fid->qid = *qid;
336 			return nil;
337 		}
338 		return "not found";
339 	}
340 }
341 
342 static void
fsstat(Req * r)343 fsstat(Req *r)
344 {
345 	int i;
346 	ulong path;
347 
348 	path = r->fid->qid.path;
349 	if(path == Qroot){
350 		fillstat(&r->d, "/", QTDIR, Qroot, 0555|DMDIR);
351 		respond(r, nil);
352 		return;
353 	}
354 	if(path == Qfactotum){
355 		fillstat(&r->d, "factotum", QTDIR, Qfactotum, 0555|DMDIR);
356 		respond(r, nil);
357 		return;
358 	}
359 	for(i=0; i<nelem(dirtab); i++)
360 		if(dirtab[i].qidpath == path){
361 			fillstat(&r->d, dirtab[i].name, 0, dirtab[i].qidpath, dirtab[i].perm);
362 			respond(r, nil);
363 			return;
364 		}
365 	respond(r, "file not found");
366 }
367 
368 static void
fsopen(Req * r)369 fsopen(Req *r)
370 {
371 	int i, *p, perm;
372 	static int need[4] = {4, 2, 6, 1};
373 	int n;
374 	Fsstate *fss;
375 
376 	p = nil;
377 	for(i=0; i<nelem(dirtab); i++)
378 		if(dirtab[i].qidpath == r->fid->qid.path)
379 			break;
380 	if(i < nelem(dirtab)){
381 		if(dirtab[i].perm & DMEXCL)
382 			p = &inuse[i];
383 		if(strcmp(r->fid->uid, owner) == 0)
384 			perm = dirtab[i].perm>>6;
385 		else
386 			perm = dirtab[i].perm;
387 	}else
388 		perm = 5;
389 
390 	n = need[r->ifcall.mode&3];
391 	if((r->ifcall.mode&~(3|OTRUNC)) || ((perm&n) != n)){
392 		respond(r, "permission denied");
393 		return;
394 	}
395 	if(p){
396 		if(*p){
397 			respond(r, "file in use");
398 			return;
399 		}
400 		(*p)++;
401 	}
402 
403 	r->fid->aux = fss = emalloc(sizeof(Fsstate));
404 	fss->phase = Notstarted;
405 	fss->sysuser = r->fid->uid;
406 	fss->attr = nil;
407 	strcpy(fss->err, "factotum/fs.c no error");
408 	respond(r, nil);
409 }
410 
411 static void
fsdestroyfid(Fid * fid)412 fsdestroyfid(Fid *fid)
413 {
414 	int i;
415 	Fsstate *fss;
416 
417 	if(fid->omode != -1){
418 		for(i=0; i<nelem(dirtab); i++)
419 			if(dirtab[i].qidpath == fid->qid.path)
420 				if(dirtab[i].perm&DMEXCL)
421 					inuse[i] = 0;
422 	}
423 
424 	fss = fid->aux;
425 	if(fss == nil)
426 		return;
427 	if(fss->ps)
428 		(*fss->proto->close)(fss);
429 	_freeattr(fss->attr);
430 	free(fss);
431 }
432 
433 static int
readlist(int off,int (* gen)(int,char *,uint,Fsstate *),Req * r,Fsstate * fss)434 readlist(int off, int (*gen)(int, char*, uint, Fsstate*), Req *r, Fsstate *fss)
435 {
436 	char *a, *ea;
437 	int n;
438 
439 	a = r->ofcall.data;
440 	ea = a+r->ifcall.count;
441 	for(;;){
442 		n = (*gen)(off, a, ea-a, fss);
443 		if(n == 0){
444 			r->ofcall.count = a - (char*)r->ofcall.data;
445 			return off;
446 		}
447 		a += n;
448 		off++;
449 	}
450 }
451 
452 enum { Nearend = 2, };			/* at least room for \n and NUL */
453 
454 /* result in `a', of `n' bytes maximum */
455 static int
keylist(int i,char * a,uint n,Fsstate * fss)456 keylist(int i, char *a, uint n, Fsstate *fss)
457 {
458 	int wb;
459 	Keyinfo ki;
460 	Key *k;
461 	static char zero[Nearend];
462 
463 	k = nil;
464 	mkkeyinfo(&ki, fss, nil);
465 	ki.attr = nil;
466 	ki.skip = i;
467 	ki.usedisabled = 1;
468 	if(findkey(&k, &ki, "") != RpcOk)
469 		return 0;
470 
471 	memset(a + n - Nearend, 0, Nearend);
472 	wb = snprint(a, n, "key %A %N\n", k->attr, k->privattr);
473 	closekey(k);
474 	if (wb >= n - 1 && a[n - 2] != '\n' && a[n - 2] != '\0') {
475 		/* line won't fit in `a', so just truncate */
476 		strcpy(a + n - 2, "\n");
477 		return 0;
478 	}
479 	return wb;
480 }
481 
482 static int
protolist(int i,char * a,uint n,Fsstate * fss)483 protolist(int i, char *a, uint n, Fsstate *fss)
484 {
485 	USED(fss);
486 
487 	if(i >= nelem(prototab)-1)
488 		return 0;
489 	if(strlen(prototab[i]->name)+1 > n)
490 		return 0;
491 	n = strlen(prototab[i]->name)+1;
492 	memmove(a, prototab[i]->name, n-1);
493 	a[n-1] = '\n';
494 	return n;
495 }
496 
497 static void
fsread(Req * r)498 fsread(Req *r)
499 {
500 	Fsstate *s;
501 
502 	s = r->fid->aux;
503 	switch((ulong)r->fid->qid.path){
504 	default:
505 		respond(r, "bug in fsread");
506 		break;
507 	case Qroot:
508 		dirread9p(r, rootdirgen, nil);
509 		respond(r, nil);
510 		break;
511 	case Qfactotum:
512 		dirread9p(r, fsdirgen, nil);
513 		respond(r, nil);
514 		break;
515 	case Qrpc:
516 		rpcread(r);
517 		break;
518 	case Qneedkey:
519 		needkeyread(r);
520 		break;
521 	case Qconfirm:
522 		confirmread(r);
523 		break;
524 	case Qlog:
525 		logread(r);
526 		break;
527 	case Qctl:
528 		s->listoff = readlist(s->listoff, keylist, r, s);
529 		respond(r, nil);
530 		break;
531 	case Qprotolist:
532 		s->listoff = readlist(s->listoff, protolist, r, s);
533 		respond(r, nil);
534 		break;
535 	}
536 }
537 
538 static void
fswrite(Req * r)539 fswrite(Req *r)
540 {
541 	int ret;
542 	char err[ERRMAX], *s;
543 
544 	switch((ulong)r->fid->qid.path){
545 	default:
546 		respond(r, "bug in fswrite");
547 		break;
548 	case Qrpc:
549 		rpcwrite(r);
550 		break;
551 	case Qneedkey:
552 	case Qconfirm:
553 	case Qctl:
554 		s = emalloc(r->ifcall.count+1);
555 		memmove(s, r->ifcall.data, r->ifcall.count);
556 		s[r->ifcall.count] = '\0';
557 		switch((ulong)r->fid->qid.path){
558 		default:
559 			abort();
560 		case Qneedkey:
561 			ret = needkeywrite(s);
562 			break;
563 		case Qconfirm:
564 			ret = confirmwrite(s);
565 			break;
566 		case Qctl:
567 			ret = ctlwrite(s, 0);
568 			break;
569 		}
570 		free(s);
571 		if(ret < 0){
572 			rerrstr(err, sizeof err);
573 			respond(r, err);
574 		}else{
575 			r->ofcall.count = r->ifcall.count;
576 			respond(r, nil);
577 		}
578 		break;
579 	}
580 }
581 
582 static void
fsflush(Req * r)583 fsflush(Req *r)
584 {
585 	confirmflush(r->oldreq);
586 	needkeyflush(r->oldreq);
587 	logflush(r->oldreq);
588 	respond(r, nil);
589 }
590 
591 Srv fs = {
592 .attach=	fsattach,
593 .walk1=	fswalk1,
594 .open=	fsopen,
595 .read=	fsread,
596 .write=	fswrite,
597 .stat=	fsstat,
598 .flush=	fsflush,
599 .destroyfid=	fsdestroyfid,
600 };
601 
602