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