xref: /plan9/sys/src/cmd/unix/drawterm/cpu-bl.c (revision 8ccd4a6360d974db7bd7bbd4f37e7018419ea908)
1 /*
2  * cpu.c - Make a connection to a cpu server
3  *
4  *	   Invoked by listen as 'cpu -R | -N service net netdir'
5  *	    	   by users  as 'cpu [-h system] [-c cmd args ...]'
6  */
7 
8 #include <u.h>
9 #include <libc.h>
10 #include <auth.h>
11 #include <fcall.h>
12 #include <authsrv.h>
13 #include <libsec.h>
14 #include "args.h"
15 #include "drawterm.h"
16 
17 #define Maxfdata 8192
18 #define MaxStr 128
19 
20 static void	fatal(int, char*, ...);
21 static void	usage(void);
22 static void	writestr(int, char*, char*, int);
23 static int	readstr(int, char*, int);
24 static char	*rexcall(int*, char*, char*);
25 static char *keyspec = "";
26 static AuthInfo *p9any(int);
27 
28 #define system csystem
29 static char	*system;
30 static int	cflag;
31 extern int	dbg;
32 
33 static char	*srvname = "ncpu";
34 static char	*ealgs = "rc4_256 sha1";
35 
36 /* message size for exportfs; may be larger so we can do big graphics in CPU window */
37 static int	msgsize = Maxfdata+IOHDRSZ;
38 
39 /* authentication mechanisms */
40 static int	netkeyauth(int);
41 static int	netkeysrvauth(int, char*);
42 static int	p9auth(int);
43 static int	srvp9auth(int, char*);
44 
45 char *authserver;
46 
47 typedef struct AuthMethod AuthMethod;
48 struct AuthMethod {
49 	char	*name;			/* name of method */
50 	int	(*cf)(int);		/* client side authentication */
51 	int	(*sf)(int, char*);	/* server side authentication */
52 } authmethod[] =
53 {
54 	{ "p9",		p9auth,		srvp9auth,},
55 	{ "netkey",	netkeyauth,	netkeysrvauth,},
56 //	{ "none",	noauth,		srvnoauth,},
57 	{ nil,	nil}
58 };
59 AuthMethod *am = authmethod;	/* default is p9 */
60 
61 char *p9authproto = "p9any";
62 
63 int setam(char*);
64 
65 void
66 exits(char *s)
67 {
68 	print("\ngoodbye\n");
69 	for(;;) osyield();
70 }
71 
72 void
73 usage(void)
74 {
75 	fprint(2, "usage: drawterm [-a authserver] [-c cpuserver] [-s secstore] [-u user]\n");
76 	exits("usage");
77 }
78 int fdd;
79 
80 int
81 mountfactotum(void)
82 {
83 	int fd;
84 
85 	if((fd = dialfactotum()) < 0)
86 		return -1;
87 	if(sysmount(fd, -1, "/mnt/factotum", MREPL, "") < 0){
88 		fprint(2, "mount factotum: %r\n");
89 		return -1;
90 	}
91 	if((fd = open("/mnt/factotum/ctl", OREAD)) < 0){
92 		fprint(2, "open /mnt/factotum/ctl: %r\n");
93 		return -1;
94 	}
95 	close(fd);
96 	return 0;
97 }
98 
99 void
100 cpumain(int argc, char **argv)
101 {
102 	char dat[MaxStr], buf[MaxStr], cmd[MaxStr], *err, *secstoreserver, *p, *s;
103 	int fd, ms, data;
104 
105 	/* see if we should use a larger message size */
106 	fd = open("/dev/draw", OREAD);
107 	if(fd > 0){
108 		ms = iounit(fd);
109 		if(msgsize < ms+IOHDRSZ)
110 			msgsize = ms+IOHDRSZ;
111 		close(fd);
112 	}
113 
114         user = getenv("USER");
115         if(user == nil)
116         	user = readcons("user", nil, 0);
117 	secstoreserver = nil;
118 	authserver = getenv("auth");
119 	if(authserver == nil)
120 		authserver = "lookout.cs.bell-labs.com";
121 	system = getenv("cpu");
122 	if(system == nil)
123 		system = "anna.cs.bell-labs.com";
124 	ARGBEGIN{
125 	case 'o':
126 		authserver = "plan9.bell-labs.com";
127 		system = "plan9.bell-labs.com";
128 		break;
129 	case 'a':
130 		authserver = EARGF(usage());
131 		break;
132 	case 'c':
133 		system = EARGF(usage());
134 		break;
135 	case 'd':
136 		dbg++;
137 		break;
138 	case 'e':
139 		ealgs = EARGF(usage());
140 		if(*ealgs == 0 || strcmp(ealgs, "clear") == 0)
141 			ealgs = nil;
142 		break;
143 	case 'C':
144 		cflag++;
145 		cmd[0] = '!';
146 		cmd[1] = '\0';
147 		while((p = ARGF()) != nil) {
148 			strcat(cmd, " ");
149 			strcat(cmd, p);
150 		}
151 		break;
152 	case 'k':
153 		keyspec = EARGF(usage());
154 		break;
155 	case 'u':
156 		user = EARGF(usage());
157 		break;
158 	case 's':
159 		secstoreserver = EARGF(usage());
160 		break;
161 	default:
162 		usage();
163 	}ARGEND;
164 
165 	if(argc != 0)
166 		usage();
167 
168 	if(mountfactotum() < 0){
169 		if(secstoreserver == nil)
170 			secstoreserver = authserver;
171 	        if(havesecstore(secstoreserver, user)){
172 	                s = secstorefetch(secstoreserver, user, nil);
173 	                if(s){
174 	                        if(strlen(s) >= sizeof secstorebuf)
175 	                                sysfatal("secstore data too big");
176 	                        strcpy(secstorebuf, s);
177 	                }
178 	        }
179 	}
180 
181 	if((err = rexcall(&data, system, srvname)))
182 		fatal(1, "%s: %s", err, system);
183 
184 	/* Tell the remote side the command to execute and where our working directory is */
185 	if(cflag)
186 		writestr(data, cmd, "command", 0);
187 	if(getcwd(dat, sizeof(dat)) == 0)
188 		writestr(data, "NO", "dir", 0);
189 	else
190 		writestr(data, dat, "dir", 0);
191 
192 	/*
193 	 *  Wait for the other end to execute and start our file service
194 	 *  of /mnt/term
195 	 */
196 	if(readstr(data, buf, sizeof(buf)) < 0)
197 		fatal(1, "waiting for FS: %r");
198 	if(strncmp("FS", buf, 2) != 0) {
199 		print("remote cpu: %s", buf);
200 		exits(buf);
201 	}
202 
203 	if(readstr(data, buf, sizeof buf) < 0)
204 		fatal(1, "waiting for remote export: %r");
205 	if(strcmp(buf, "/") != 0){
206 		print("remote cpu: %s" , buf);
207 		exits(buf);
208 	}
209 	write(data, "OK", 2);
210 
211 	/* Begin serving the gnot namespace */
212 	exportfs(data, msgsize);
213 	fatal(1, "starting exportfs");
214 }
215 
216 void
217 fatal(int syserr, char *fmt, ...)
218 {
219 	Fmt f;
220 	char *str;
221 	va_list arg;
222 
223 	fmtstrinit(&f);
224 	fmtprint(&f, "cpu: ");
225 	va_start(arg, fmt);
226 	fmtvprint(&f, fmt, arg);
227 	va_end(arg);
228 	if(syserr)
229 		fmtprint(&f, ": %r");
230 	fmtprint(&f, "\n");
231 	str = fmtstrflush(&f);
232 	write(2, str, strlen(str));
233 	exits(str);
234 }
235 
236 char *negstr = "negotiating authentication method";
237 
238 char bug[256];
239 
240 char*
241 rexcall(int *fd, char *host, char *service)
242 {
243 	char *na;
244 	char dir[MaxStr];
245 	char err[ERRMAX];
246 	char msg[MaxStr];
247 	int n;
248 
249 	na = netmkaddr(host, "tcp", "17010");
250 	if((*fd = dial(na, 0, dir, 0)) < 0)
251 		return "can't dial";
252 
253 	/* negotiate authentication mechanism */
254 	if(ealgs != nil)
255 		snprint(msg, sizeof(msg), "%s %s", am->name, ealgs);
256 	else
257 		snprint(msg, sizeof(msg), "%s", am->name);
258 	writestr(*fd, msg, negstr, 0);
259 	n = readstr(*fd, err, sizeof err);
260 	if(n < 0)
261 		return negstr;
262 	if(*err){
263 		werrstr(err);
264 		return negstr;
265 	}
266 
267 	/* authenticate */
268 	*fd = (*am->cf)(*fd);
269 	if(*fd < 0)
270 		return "can't authenticate";
271 	return 0;
272 }
273 
274 void
275 writestr(int fd, char *str, char *thing, int ignore)
276 {
277 	int l, n;
278 
279 	l = strlen(str);
280 	n = write(fd, str, l+1);
281 	if(!ignore && n < 0)
282 		fatal(1, "writing network: %s", thing);
283 }
284 
285 int
286 readstr(int fd, char *str, int len)
287 {
288 	int n;
289 
290 	while(len) {
291 		n = read(fd, str, 1);
292 		if(n < 0)
293 			return -1;
294 		if(*str == '\0')
295 			return 0;
296 		str++;
297 		len--;
298 	}
299 	return -1;
300 }
301 
302 static int
303 readln(char *buf, int n)
304 {
305 	int i;
306 	char *p;
307 
308 	n--;	/* room for \0 */
309 	p = buf;
310 	for(i=0; i<n; i++){
311 		if(read(0, p, 1) != 1)
312 			break;
313 		if(*p == '\n' || *p == '\r')
314 			break;
315 		p++;
316 	}
317 	*p = '\0';
318 	return p-buf;
319 }
320 
321 /*
322  *  user level challenge/response
323  */
324 static int
325 netkeyauth(int fd)
326 {
327 	char chall[32];
328 	char resp[32];
329 
330 	strecpy(chall, chall+sizeof chall, getuser());
331 	print("user[%s]: ", chall);
332 	if(readln(resp, sizeof(resp)) < 0)
333 		return -1;
334 	if(*resp != 0)
335 		strcpy(chall, resp);
336 	writestr(fd, chall, "challenge/response", 1);
337 
338 	for(;;){
339 		if(readstr(fd, chall, sizeof chall) < 0)
340 			break;
341 		if(*chall == 0)
342 			return fd;
343 		print("challenge: %s\nresponse: ", chall);
344 		if(readln(resp, sizeof(resp)) < 0)
345 			break;
346 		writestr(fd, resp, "challenge/response", 1);
347 	}
348 	return -1;
349 }
350 
351 static int
352 netkeysrvauth(int fd, char *user)
353 {
354 	return -1;
355 }
356 
357 static void
358 mksecret(char *t, uchar *f)
359 {
360 	sprint(t, "%2.2ux%2.2ux%2.2ux%2.2ux%2.2ux%2.2ux%2.2ux%2.2ux%2.2ux%2.2ux",
361 		f[0], f[1], f[2], f[3], f[4], f[5], f[6], f[7], f[8], f[9]);
362 }
363 
364 /*
365  *  plan9 authentication followed by rc4 encryption
366  */
367 static int
368 p9auth(int fd)
369 {
370 	uchar key[16];
371 	uchar digest[SHA1dlen];
372 	char fromclientsecret[21];
373 	char fromserversecret[21];
374 	int i;
375 	AuthInfo *ai;
376 
377 	ai = p9any(fd);
378 	if(ai == nil)
379 		return -1;
380 	memmove(key+4, ai->secret, ai->nsecret);
381 	if(ealgs == nil)
382 		return fd;
383 
384 	/* exchange random numbers */
385 	for(i = 0; i < 4; i++)
386 		key[i] = fastrand();
387 	if(write(fd, key, 4) != 4)
388 		return -1;
389 	if(readn(fd, key+12, 4) != 4)
390 		return -1;
391 
392 	/* scramble into two secrets */
393 	sha1(key, sizeof(key), digest, nil);
394 	mksecret(fromclientsecret, digest);
395 	mksecret(fromserversecret, digest+10);
396 
397 	/* set up encryption */
398 	i = pushssl(fd, ealgs, fromclientsecret, fromserversecret, nil);
399 	if(i < 0)
400 		werrstr("can't establish ssl connection: %r");
401 	return i;
402 }
403 
404 int
405 authdial(char *net, char *dom)
406 {
407 	int fd;
408 	fd = dial(netmkaddr(authserver, "tcp", "567"), 0, 0, 0);
409 	//print("authdial %d\n", fd);
410 	return fd;
411 }
412 
413 static int
414 getastickets(Ticketreq *tr, char *trbuf, char *tbuf)
415 {
416 	int asfd, rv;
417 	char *dom;
418 
419 	dom = tr->authdom;
420 	asfd = authdial(nil, dom);
421 	if(asfd < 0)
422 		return -1;
423 	rv = _asgetticket(asfd, trbuf, tbuf);
424 	close(asfd);
425 	return rv;
426 }
427 
428 static int
429 mkserverticket(Ticketreq *tr, char *authkey, char *tbuf)
430 {
431 	int i;
432 	Ticket t;
433 
434 	if(strcmp(tr->authid, tr->hostid) != 0)
435 		return -1;
436 	memset(&t, 0, sizeof(t));
437 	memmove(t.chal, tr->chal, CHALLEN);
438 	strcpy(t.cuid, tr->uid);
439 	strcpy(t.suid, tr->uid);
440 	for(i=0; i<DESKEYLEN; i++)
441 		t.key[i] = fastrand();
442 	t.num = AuthTc;
443 	convT2M(&t, tbuf, authkey);
444 	t.num = AuthTs;
445 	convT2M(&t, tbuf+TICKETLEN, authkey);
446 	return 0;
447 }
448 
449 static int
450 gettickets(Ticketreq *tr, char *key, char *trbuf, char *tbuf)
451 {
452 	if(getastickets(tr, trbuf, tbuf) >= 0)
453 		return 0;
454 	return mkserverticket(tr, key, tbuf);
455 }
456 
457 /*
458  *  prompt user for a key.  don't care about memory leaks, runs standalone
459  */
460 static Attr*
461 promptforkey(char *params)
462 {
463 	char *v;
464 	int fd;
465 	Attr *a, *attr;
466 	char *def;
467 
468 	fd = open("/dev/cons", ORDWR);
469 	if(fd < 0)
470 		sysfatal("opening /dev/cons: %r");
471 
472 	attr = _parseattr(params);
473 	fprint(fd, "\n!Adding key:");
474 	for(a=attr; a; a=a->next)
475 		if(a->type != AttrQuery && a->name[0] != '!')
476 			fprint(fd, " %q=%q", a->name, a->val);
477 	fprint(fd, "\n");
478 
479 	for(a=attr; a; a=a->next){
480 		v = a->name;
481 		if(a->type != AttrQuery || v[0]=='!')
482 			continue;
483 		def = nil;
484 		if(strcmp(v, "user") == 0)
485 			def = getuser();
486 		a->val = readcons(v, def, 0);
487 		if(a->val == nil)
488 			sysfatal("user terminated key input");
489 		a->type = AttrNameval;
490 	}
491 	for(a=attr; a; a=a->next){
492 		v = a->name;
493 		if(a->type != AttrQuery || v[0]!='!')
494 			continue;
495 		def = nil;
496 		if(strcmp(v+1, "user") == 0)
497 			def = getuser();
498 		a->val = readcons(v+1, def, 1);
499 		if(a->val == nil)
500 			sysfatal("user terminated key input");
501 		a->type = AttrNameval;
502 	}
503 	fprint(fd, "!\n");
504 	close(fd);
505 	return attr;
506 }
507 
508 /*
509  *  send a key to the mounted factotum
510  */
511 static int
512 sendkey(Attr *attr)
513 {
514 	int fd, rv;
515 	char buf[1024];
516 
517 	fd = open("/mnt/factotum/ctl", ORDWR);
518 	if(fd < 0)
519 		sysfatal("opening /mnt/factotum/ctl: %r");
520 	rv = fprint(fd, "key %A\n", attr);
521 	read(fd, buf, sizeof buf);
522 	close(fd);
523 	return rv;
524 }
525 
526 int
527 askuser(char *params)
528 {
529 	Attr *attr;
530 
531 	fmtinstall('A', _attrfmt);
532 
533 	attr = promptforkey(params);
534 	if(attr == nil)
535 		sysfatal("no key supplied");
536 	if(sendkey(attr) < 0)
537 		sysfatal("sending key to factotum: %r");
538 	return 0;
539 }
540 
541 AuthInfo*
542 p9anyfactotum(int fd, int afd)
543 {
544 	return auth_proxy(fd, askuser, "proto=p9any role=client %s", keyspec);
545 }
546 
547 AuthInfo*
548 p9any(int fd)
549 {
550 	char buf[1024], buf2[1024], cchal[CHALLEN], *bbuf, *p, *dom, *u;
551 	char *pass;
552 	char tbuf[TICKETLEN+TICKETLEN+AUTHENTLEN], trbuf[TICKREQLEN];
553 	char authkey[DESKEYLEN];
554 	Authenticator auth;
555 	int afd, i, v2;
556 	Ticketreq tr;
557 	Ticket t;
558 	AuthInfo *ai;
559 
560 	if((afd = open("/mnt/factotum/ctl", ORDWR)) >= 0)
561 		return p9anyfactotum(fd, afd);
562 
563 	if(readstr(fd, buf, sizeof buf) < 0)
564 		fatal(1, "cannot read p9any negotiation");
565 	bbuf = buf;
566 	v2 = 0;
567 	if(strncmp(buf, "v.2 ", 4) == 0){
568 		v2 = 1;
569 		bbuf += 4;
570 	}
571 	if((p = strchr(bbuf, ' ')))
572 		*p = 0;
573 	p = bbuf;
574 	if((dom = strchr(p, '@')) == nil)
575 		fatal(1, "bad p9any domain");
576 	*dom++ = 0;
577 	if(strcmp(p, "p9sk1") != 0)
578 		fatal(1, "server did not offer p9sk1");
579 
580 	sprint(buf2, "%s %s", p, dom);
581 	if(write(fd, buf2, strlen(buf2)+1) != strlen(buf2)+1)
582 		fatal(1, "cannot write user/domain choice in p9any");
583 	if(v2){
584 		if(readstr(fd, buf, sizeof buf) != 3)
585 			fatal(1, "cannot read OK in p9any");
586 		if(memcmp(buf, "OK\0", 3) != 0)
587 			fatal(1, "did not get OK in p9any");
588 	}
589 	for(i=0; i<CHALLEN; i++)
590 		cchal[i] = fastrand();
591 	if(write(fd, cchal, 8) != 8)
592 		fatal(1, "cannot write p9sk1 challenge");
593 
594 	if(readn(fd, trbuf, TICKREQLEN) != TICKREQLEN)
595 		fatal(1, "cannot read ticket request in p9sk1");
596 
597 
598 	convM2TR(trbuf, &tr);
599 	u = user;
600 	pass = findkey(&u, tr.authdom);
601 	if(pass == nil)
602 	again:
603 		pass = getkey(u, tr.authdom);
604 	if(pass == nil)
605 		fatal(1, "no password");
606 
607 	passtokey(authkey, pass);
608 	memset(pass, 0, strlen(pass));
609 
610 	tr.type = AuthTreq;
611 	strecpy(tr.hostid, tr.hostid+sizeof tr.hostid, u);
612 	strecpy(tr.uid, tr.uid+sizeof tr.uid, u);
613 	convTR2M(&tr, trbuf);
614 
615 	if(gettickets(&tr, authkey, trbuf, tbuf) < 0)
616 		fatal(1, "cannot get auth tickets in p9sk1");
617 
618 	convM2T(tbuf, &t, authkey);
619 	if(t.num != AuthTc){
620 		print("?password mismatch with auth server\n");
621 		goto again;
622 	}
623 	memmove(tbuf, tbuf+TICKETLEN, TICKETLEN);
624 
625 	auth.num = AuthAc;
626 	memmove(auth.chal, tr.chal, CHALLEN);
627 	auth.id = 0;
628 	convA2M(&auth, tbuf+TICKETLEN, t.key);
629 
630 	if(write(fd, tbuf, TICKETLEN+AUTHENTLEN) != TICKETLEN+AUTHENTLEN)
631 		fatal(1, "cannot send ticket and authenticator back in p9sk1");
632 
633 	if(readn(fd, tbuf, AUTHENTLEN) != AUTHENTLEN)
634 		fatal(1, "cannot read authenticator in p9sk1");
635 
636 	convM2A(tbuf, &auth, t.key);
637 	if(auth.num != AuthAs
638 	|| memcmp(auth.chal, cchal, CHALLEN) != 0
639 	|| auth.id != 0){
640 		print("?you and auth server agree about password.\n");
641 		print("?server is confused.\n");
642 		fatal(1, "server lies got %llux.%d want %llux.%d", *(vlong*)auth.chal, auth.id, *(vlong*)cchal, 0);
643 	}
644 	//print("i am %s there.\n", t.suid);
645 	ai = mallocz(sizeof(AuthInfo), 1);
646 	ai->secret = mallocz(8, 1);
647 	des56to64((uchar*)t.key, ai->secret);
648 	ai->nsecret = 8;
649 	ai->suid = strdup(t.suid);
650 	ai->cuid = strdup(t.cuid);
651 	memset(authkey, 0, sizeof authkey);
652 	return ai;
653 }
654 
655 /*
656 static int
657 noauth(int fd)
658 {
659 	ealgs = nil;
660 	return fd;
661 }
662 
663 static int
664 srvnoauth(int fd, char *user)
665 {
666 	strecpy(user, user+MaxStr, getuser());
667 	ealgs = nil;
668 	return fd;
669 }
670 */
671 
672 void
673 loghex(uchar *p, int n)
674 {
675 	char buf[100];
676 	int i;
677 
678 	for(i = 0; i < n; i++)
679 		sprint(buf+2*i, "%2.2ux", p[i]);
680 //	syslog(0, "cpu", buf);
681 }
682 
683 static int
684 srvp9auth(int fd, char *user)
685 {
686 	return -1;
687 }
688 
689 /*
690  *  set authentication mechanism
691  */
692 int
693 setam(char *name)
694 {
695 	for(am = authmethod; am->name != nil; am++)
696 		if(strcmp(am->name, name) == 0)
697 			return 0;
698 	am = authmethod;
699 	return -1;
700 }
701 
702 /*
703  *  set authentication mechanism and encryption/hash algs
704  *
705 int
706 setamalg(char *s)
707 {
708 	ealgs = strchr(s, ' ');
709 	if(ealgs != nil)
710 		*ealgs++ = 0;
711 	return setam(s);
712 }
713 
714 */
715