1218 lines · 22.5 KB · C
RawDownload
/* * cpu.c - Make a connection to a cpu server * *	   Invoked by listen as 'cpu -R | -N service net netdir' *	    	   by users  as 'cpu [-h system] [-c cmd args ...]' */ #include <u.h>#include <libc.h>#include <auth.h>#include <fcall.h>#include <libsec.h> #define	Maxfdata 8192#define MaxStr 128 void	remoteside(void);void	fatal(char*, ...);void	lclnoteproc(int);void	rmtnoteproc(void);void	catcher(void*, char*);void	usage(void);void	writestr(int, char*, char*, int);int	readstr(int, char*, int);char	*rexcall(int*, char*, char*);int	setamalg(char*);char	*keyspec = ""; int 	notechan;int	exportpid;char	*system;int	cflag;int	nflag;int	dbg;char	*user;char	*patternfile;char	*origargs; char	*srvname = "ncpu";char	*exportfs = "/bin/exportfs";char	*ealgs = "rc4_256 sha1"; /* message size for exportfs; may be larger so we can do big graphics in CPU window */int	msgsize = Maxfdata+IOHDRSZ; /* encryption mechanisms */static int	clear(int); int (*encryption)(int) = clear; /* authentication mechanisms */static int	netkeyauth(int);static int	netkeysrvauth(int, char*);static int	p9auth(int);static int	srvp9auth(int, char*);static int	noauth(int);static int	srvnoauth(int, char*); typedef struct AuthMethod AuthMethod;struct AuthMethod {	char	*name;			/* name of method */	int	(*cf)(int);		/* client side authentication */	int	(*sf)(int, char*);	/* server side authentication */} authmethod[] = {	{ "p9",		p9auth,		srvp9auth,},	{ "netkey",	netkeyauth,	netkeysrvauth,},	{ "none",	noauth,		srvnoauth,},	{ nil,	nil}};AuthMethod *am = authmethod;	/* default is p9 */ int setam(char*); char	*aan = "/bin/aan";char	*anstring = "tcp!*!0";char	*filterp = nil; int filter(int fd, char *host); voidusage(void){	fprint(2, "usage: cpu [-p] [-h system] [-u user] [-a authmethod] "		"[-e 'crypt hash'] [-k keypattern] [-P patternfile] "		"[-c cmd arg ...]\n");	exits("usage");} /* * reading /proc/pid/args yields either "name args" or "name [display args]", * so return only args or display args. */static char *procgetname(void){	int fd, n;	char *lp, *rp;	char buf[256]; 	snprint(buf, sizeof buf, "#p/%d/args", getpid());	if((fd = open(buf, OREAD)) < 0)		return strdup("");	*buf = '\0';	n = read(fd, buf, sizeof buf-1);	close(fd);	if (n >= 0)		buf[n] = '\0';	if ((lp = strchr(buf, '[')) == nil || (rp = strrchr(buf, ']')) == nil) {		lp = strchr(buf, ' ');		if (lp == nil)			return strdup("");		else			return strdup(lp+1);	}	*rp = '\0';	return strdup(lp+1);} voidmain(int argc, char **argv){	char dat[MaxStr], buf[MaxStr], cmd[MaxStr], *p, *s, *err;	int ac, fd, ms, data;	char *av[10]; 	quotefmtinstall();	origargs = procgetname();	/* see if we should use a larger message size */	fd = open("/dev/draw", OREAD);	if(fd >= 0){		ms = iounit(fd);		if(msgsize < ms+IOHDRSZ)			msgsize = ms+IOHDRSZ;		close(fd);	} 	user = getuser();	if(user == nil)		fatal("can't read user name: %r");	ARGBEGIN{	case 'a':		p = EARGF(usage());		if(setam(p) < 0)			fatal("unknown auth method %s", p);		break;	case 'e':		ealgs = EARGF(usage());		if(*ealgs == 0 || strcmp(ealgs, "clear") == 0)			ealgs = nil;		break;	case 'd':		dbg++;		break;	case 'f':		/* ignored but accepted for compatibility */		break;	case 'n':		/* must be specified before -R/-O */		nflag++;		break;	case 'A':		anstring = EARGF(usage());		break;	case 'R':				/* From listen */		remoteside();		break;	case 'h':		system = EARGF(usage());		break;	case 'c':		cflag++;		s = cmd;		*s++ = '!';		*s = 0;		while(p = ARGF())			s = seprint(s, cmd+sizeof(cmd), " %q", p);		break;	case 'k':		keyspec = smprint("%s %s", keyspec, EARGF(usage()));		break;	case 'P':		patternfile = EARGF(usage());		break;	case 'u':		user = EARGF(usage());		keyspec = smprint("%s user=%s", keyspec, user);		break;	case 'p':		filterp = aan;		break;	default:		usage();	}ARGEND;  	if(argc != 0)		usage(); 	if(system == nil) {		p = getenv("cpu");		if(p == 0)			fatal("set $cpu");		system = p;	} 	if(err = rexcall(&data, system, srvname))		fatal("%s: %s: %r", err, system); 	procsetname("%s", origargs);	/* Tell the remote side the command to execute and where our working directory is */	if(cflag)		writestr(data, cmd, "command", 0);	if(getwd(dat, sizeof(dat)) == 0)		writestr(data, "NO", "dir", 0);	else		writestr(data, dat, "dir", 0); 	/* start up a process to pass along notes */	lclnoteproc(data); 	/* 	 *  Wait for the other end to execute and start our file service	 *  of /mnt/term	 */	if(readstr(data, buf, sizeof(buf)) < 0)		fatal("waiting for FS: %r");	if(strncmp("FS", buf, 2) != 0) {		print("remote cpu: %s", buf);		exits(buf);	} 	/* Begin serving the gnot namespace */	close(0);	dup(data, 0);	close(data); 	sprint(buf, "%d", msgsize);	ac = 0;	av[ac++] = exportfs;	av[ac++] = "-m";	av[ac++] = buf;	if(dbg)		av[ac++] = "-d";	if(patternfile != nil){		av[ac++] = "-P";		av[ac++] = patternfile;	}	av[ac] = nil;	exec(exportfs, av);	fatal("starting exportfs: %r");} voidfatal(char *fmt, ...){	char buf[1024];	va_list arg; 	va_start(arg, fmt);	vsnprint(buf, sizeof(buf), fmt, arg);	va_end(arg);	fprint(2, "cpu: %s\n", buf);	syslog(0, "cpu", "%s", buf);	exits(buf);} char *negstr = "negotiating authentication method"; /* Invoked with stdin and stdout connected to the network connection */voidremoteside(void){	char user[MaxStr], buf[MaxStr], xdir[MaxStr], cmd[MaxStr];	int i, n, fd, badchdir, gotcmd; 	rfork(RFENVG);	putenv("service", "cpu");	fd = 0; 	/* negotiate authentication mechanism */	n = readstr(fd, cmd, sizeof(cmd));	if(n < 0)		fatal("authenticating: %r");	filterp = nil;	if(strcmp(cmd, "aan") == 0){		filterp = aan;		writestr(fd, "", nil, 1);		n = readstr(fd, cmd, sizeof(cmd));		if(n < 0)			fatal("authenticating: %r");	}	if(setamalg(cmd) < 0 || (nflag == 0 && am->sf == srvnoauth)) {		writestr(fd, "unsupported auth method", nil, 0);		fatal("bad auth method %s", cmd);	} else		writestr(fd, "", "", 1); 	if((fd = (*am->sf)(fd, user)) < 0)		fatal("srvauth: %r");	if((fd = filter(fd, nil)) < 0)		fatal("filter: %r");	if((fd = encryption(fd)) < 0)		fatal("encrypt: %r"); 	/* Now collect invoking cpu's current directory or possibly a command */	gotcmd = 0;	if(readstr(fd, xdir, sizeof(xdir)) < 0)		fatal("dir/cmd: %r");	if(xdir[0] == '!') {		strcpy(cmd, &xdir[1]);		gotcmd = 1;		if(readstr(fd, xdir, sizeof(xdir)) < 0)			fatal("dir: %r");	} 	/* Establish the new process at the current working directory of the gnot */	badchdir = 0;	if(strcmp(xdir, "NO") != 0)		if(chdir(xdir) < 0)			badchdir = 1; 	/* Start the gnot serving its namespace */	writestr(fd, "FS", "FS", 0);	writestr(fd, "/", "exportfs dir", 0); 	n = read(fd, buf, sizeof(buf));	if(n != 2 || buf[0] != 'O' || buf[1] != 'K')		exits("remote tree"); 	/* make sure buffers are big by doing fversion explicitly; pick a huge number; other side will trim */	strcpy(buf, VERSION9P);	if(fversion(fd, 64*1024, buf, sizeof buf) < 0)		exits("fversion failed");	if(mount(fd, -1, "/mnt/term", MCREATE|MREPL, "") < 0)		exits("mount failed"); 	close(fd); 	/* the remote noteproc uses the mount so it must follow it */	rmtnoteproc(); 	for(i = 0; i < 3; i++)		close(i); 	if(open("/mnt/term/dev/cons", OREAD) != 0)		exits("open stdin");	if(open("/mnt/term/dev/cons", OWRITE) != 1)		exits("open stdout");	dup(1, 2); 	if(badchdir)		print("cpu: failed to chdir to '%s'\n", xdir); 	if(gotcmd)		execl("/bin/rc", "rc", "-lc", cmd, nil);	else		execl("/bin/rc", "rc", "-li", nil);	fatal("exec shell: %r");} char*rexcall(int *fd, char *host, char *service){	char *na;	char err[ERRMAX];	char msg[MaxStr];	int n; 	na = netmkaddr(host, 0, service);	procsetname("dialing %s", na);	if((*fd = dial(na, 0, 0, 0)) < 0)		return "can't dial"; 	/* negotiate aan filter extension */	if(filterp == aan){		writestr(*fd, "aan", "negotiating aan", 0);		n = readstr(*fd, err, sizeof err);		if(n < 0)			return "negotiating aan";		if(*err){			errstr(err, sizeof err);			return negstr;		}	} 	/* negotiate authentication mechanism */	if(ealgs != nil)		snprint(msg, sizeof(msg), "%s %s", am->name, ealgs);	else		snprint(msg, sizeof(msg), "%s", am->name);	procsetname("writing %s", msg);	writestr(*fd, msg, negstr, 0);	procsetname("awaiting auth method");	n = readstr(*fd, err, sizeof err);	if(n < 0)		return negstr;	if(*err){		errstr(err, sizeof err);		return negstr;	} 	/* authenticate */	procsetname("%s: auth via %s", origargs, am->name);	if((*fd = (*am->cf)(*fd)) < 0)		return "can't authenticate";	if((*fd = filter(*fd, system)) < 0)		return "can't filter";	if((*fd = encryption(*fd)) < 0)		return "can't encrypt";	return nil;} voidwritestr(int fd, char *str, char *thing, int ignore){	int l, n; 	l = strlen(str);	n = write(fd, str, l+1);	if(!ignore && n < 0)		fatal("writing network: %s: %r", thing);} intreadstr(int fd, char *str, int len){	int n; 	while(len) {		n = read(fd, str, 1);		if(n < 0) 			return -1;		if(*str == '\0')			return 0;		str++;		len--;	}	return -1;} static intreadln(char *buf, int n){	int i;	char *p; 	n--;	/* room for \0 */	p = buf;	for(i=0; i<n; i++){		if(read(0, p, 1) != 1)			break;		if(*p == '\n' || *p == '\r')			break;		p++;	}	*p = '\0';	return p-buf;} /* *  user level challenge/response */static intnetkeyauth(int fd){	char chall[32];	char resp[32]; 	strecpy(chall, chall+sizeof chall, getuser());	print("user[%s]: ", chall);	if(readln(resp, sizeof(resp)) < 0)		return -1;	if(*resp != 0)		strcpy(chall, resp);	writestr(fd, chall, "challenge/response", 1); 	for(;;){		if(readstr(fd, chall, sizeof chall) < 0)			break;		if(*chall == 0)			return fd;		print("challenge: %s\nresponse: ", chall);		if(readln(resp, sizeof(resp)) < 0)			break;		writestr(fd, resp, "challenge/response", 1);	}	return -1;} static intnetkeysrvauth(int fd, char *user){	char response[32];	Chalstate *ch;	int tries;	AuthInfo *ai; 	if(readstr(fd, user, MaxStr) < 0)		return -1; 

Lines 1–500 of 1218.