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