xref: /plan9/sys/src/cmd/import.c (revision 6bbfed0d85c6d7248503ef0614d0f1e40438b735)
1 #include <u.h>
2 #include <libc.h>
3 #include <auth.h>
4 #include <libsec.h>
5 
6 enum {
7 	Encnone,
8 	Encssl,
9 	Enctls,
10 };
11 
12 static char *encprotos[] = {
13 	[Encnone] =	"clear",
14 	[Encssl] =	"ssl",
15 	[Enctls] = 	"tls",
16 			nil,
17 };
18 
19 char		*keyspec = "";
20 char		*filterp;
21 char		*ealgs = "rc4_256 sha1";
22 int		encproto = Encnone;
23 char		*aan = "/bin/aan";
24 AuthInfo 	*ai;
25 int		debug;
26 int		doauth = 1;
27 int		timedout;
28 
29 int	connect(char*, char*, int);
30 int	passive(void);
31 int	old9p(int);
32 void	catcher(void*, char*);
33 void	sysfatal(char*, ...);
34 void	usage(void);
35 int	filter(int, char *, char *);
36 
37 static void	mksecret(char *, uchar *);
38 
39 /*
40  * based on libthread's threadsetname, but drags in less library code.
41  * actually just sets the arguments displayed.
42  */
43 void
procsetname(char * fmt,...)44 procsetname(char *fmt, ...)
45 {
46 	int fd;
47 	char *cmdname;
48 	char buf[128];
49 	va_list arg;
50 
51 	va_start(arg, fmt);
52 	cmdname = vsmprint(fmt, arg);
53 	va_end(arg);
54 	if (cmdname == nil)
55 		return;
56 	snprint(buf, sizeof buf, "#p/%d/args", getpid());
57 	if((fd = open(buf, OWRITE)) >= 0){
58 		write(fd, cmdname, strlen(cmdname)+1);
59 		close(fd);
60 	}
61 	free(cmdname);
62 }
63 
64 void
post(char * name,char * envname,int srvfd)65 post(char *name, char *envname, int srvfd)
66 {
67 	int fd;
68 	char buf[32];
69 
70 	fd = create(name, OWRITE, 0600);
71 	if(fd < 0)
72 		return;
73 	sprint(buf, "%d",srvfd);
74 	if(write(fd, buf, strlen(buf)) != strlen(buf))
75 		sysfatal("srv write: %r");
76 	close(fd);
77 	putenv(envname, name);
78 }
79 
80 static int
lookup(char * s,char * l[])81 lookup(char *s, char *l[])
82 {
83 	int i;
84 
85 	for (i = 0; l[i] != 0; i++)
86 		if (strcmp(l[i], s) == 0)
87 			return i;
88 	return -1;
89 }
90 
91 void
main(int argc,char ** argv)92 main(int argc, char **argv)
93 {
94 	char *mntpt, *srvpost, srvfile[64];
95 	int backwards = 0, fd, mntflags, oldserver, notree;
96 
97 	quotefmtinstall();
98 	srvpost = nil;
99 	oldserver = 0;
100 	notree = 0;
101 	mntflags = MREPL;
102 	ARGBEGIN{
103 	case 'A':
104 		doauth = 0;
105 		break;
106 	case 'a':
107 		mntflags = MAFTER;
108 		break;
109 	case 'b':
110 		mntflags = MBEFORE;
111 		break;
112 	case 'c':
113 		mntflags |= MCREATE;
114 		break;
115 	case 'C':
116 		mntflags |= MCACHE;
117 		break;
118 	case 'd':
119 		debug++;
120 		break;
121 	case 'f':
122 		/* ignored but allowed for compatibility */
123 		break;
124 	case 'O':
125 	case 'o':
126 		oldserver = 1;
127 		break;
128 	case 'E':
129 		if ((encproto = lookup(EARGF(usage()), encprotos)) < 0)
130 			usage();
131 		break;
132 	case 'e':
133 		ealgs = EARGF(usage());
134 		if(*ealgs == 0 || strcmp(ealgs, "clear") == 0)
135 			ealgs = nil;
136 		break;
137 	case 'k':
138 		keyspec = EARGF(usage());
139 		break;
140 	case 'p':
141 		filterp = aan;
142 		break;
143 	case 's':
144 		srvpost = EARGF(usage());
145 		break;
146 	case 'B':
147 		backwards = 1;
148 		break;
149 	case 'm':
150 		notree = 1;
151 		break;
152 	default:
153 		usage();
154 	}ARGEND;
155 
156 	mntpt = 0;		/* to shut up compiler */
157 	if(backwards){
158 		switch(argc) {
159 		default:
160 			mntpt = argv[0];
161 			break;
162 		case 0:
163 			usage();
164 		}
165 	} else {
166 		switch(argc) {
167 		case 2:
168 			mntpt = argv[1];
169 			break;
170 		case 3:
171 			if(notree)
172 				usage();
173 			mntpt = argv[2];
174 			break;
175 		default:
176 			usage();
177 		}
178 	}
179 
180 	if (encproto == Enctls)
181 		sysfatal("%s: tls has not yet been implemented", argv[0]);
182 
183 	notify(catcher);
184 	alarm(60*1000);
185 
186 	if(backwards)
187 		fd = passive();
188 	else if(notree)
189 		fd = connect(argv[0], nil, oldserver);
190 	else
191 		fd = connect(argv[0], argv[1], oldserver);
192 
193 	if (!oldserver)
194 		fprint(fd, "impo %s %s\n", filterp? "aan": "nofilter",
195 			encprotos[encproto]);
196 
197 	if (encproto != Encnone && ealgs && ai) {
198 		uchar key[16];
199 		uchar digest[SHA1dlen];
200 		char fromclientsecret[21];
201 		char fromserversecret[21];
202 		int i;
203 
204 		memmove(key+4, ai->secret, ai->nsecret);
205 
206 		/* exchange random numbers */
207 		srand(truerand());
208 		for(i = 0; i < 4; i++)
209 			key[i] = rand();
210 		if(write(fd, key, 4) != 4)
211 			sysfatal("can't write key part: %r");
212 		if(readn(fd, key+12, 4) != 4)
213 			sysfatal("can't read key part: %r");
214 
215 		/* scramble into two secrets */
216 		sha1(key, sizeof(key), digest, nil);
217 		mksecret(fromclientsecret, digest);
218 		mksecret(fromserversecret, digest+10);
219 
220 		if (filterp)
221 			fd = filter(fd, filterp, argv[0]);
222 
223 		/* set up encryption */
224 		procsetname("pushssl");
225 		fd = pushssl(fd, ealgs, fromclientsecret, fromserversecret, nil);
226 		if(fd < 0)
227 			sysfatal("can't establish ssl connection: %r");
228 	}
229 	else if (filterp)
230 		fd = filter(fd, filterp, argv[0]);
231 
232 	if(srvpost){
233 		sprint(srvfile, "/srv/%s", srvpost);
234 		remove(srvfile);
235 		post(srvfile, srvpost, fd);
236 	}
237 	procsetname("mount on %s", mntpt);
238 	if(mount(fd, -1, mntpt, mntflags, "") < 0)
239 		sysfatal("can't mount %s: %r", argv[1]);
240 	alarm(0);
241 
242 	if(backwards && argc > 1){
243 		exec(argv[1], &argv[1]);
244 		sysfatal("exec: %r");
245 	}
246 	exits(0);
247 }
248 
249 void
catcher(void *,char * msg)250 catcher(void*, char *msg)
251 {
252 	timedout = 1;
253 	if(strcmp(msg, "alarm") == 0)
254 		noted(NCONT);
255 	noted(NDFLT);
256 }
257 
258 int
old9p(int fd)259 old9p(int fd)
260 {
261 	int p[2];
262 
263 	procsetname("old9p");
264 	if(pipe(p) < 0)
265 		sysfatal("pipe: %r");
266 
267 	switch(rfork(RFPROC|RFFDG|RFNAMEG)) {
268 	case -1:
269 		sysfatal("rfork srvold9p: %r");
270 	case 0:
271 		if(fd != 1){
272 			dup(fd, 1);
273 			close(fd);
274 		}
275 		if(p[0] != 0){
276 			dup(p[0], 0);
277 			close(p[0]);
278 		}
279 		close(p[1]);
280 		if(0){
281 			fd = open("/sys/log/cpu", OWRITE);
282 			if(fd != 2){
283 				dup(fd, 2);
284 				close(fd);
285 			}
286 			execl("/bin/srvold9p", "srvold9p", "-ds", nil);
287 		} else
288 			execl("/bin/srvold9p", "srvold9p", "-s", nil);
289 		sysfatal("exec srvold9p: %r");
290 	default:
291 		close(fd);
292 		close(p[0]);
293 	}
294 	return p[1];
295 }
296 
297 int
connect(char * system,char * tree,int oldserver)298 connect(char *system, char *tree, int oldserver)
299 {
300 	char buf[ERRMAX], dir[128], *na;
301 	int fd, n;
302 	char *authp;
303 
304 	na = netmkaddr(system, 0, "exportfs");
305 	procsetname("dial %s", na);
306 	if((fd = dial(na, 0, dir, 0)) < 0)
307 		sysfatal("can't dial %s: %r", system);
308 
309 	if(doauth){
310 		if(oldserver)
311 			authp = "p9sk2";
312 		else
313 			authp = "p9any";
314 
315 		procsetname("auth_proxy auth_getkey proto=%q role=client %s",
316 			authp, keyspec);
317 		ai = auth_proxy(fd, auth_getkey, "proto=%q role=client %s",
318 			authp, keyspec);
319 		if(ai == nil)
320 			sysfatal("%r: %s", system);
321 	}
322 
323 	if(tree != nil){
324 		procsetname("writing tree name %s", tree);
325 		n = write(fd, tree, strlen(tree));
326 		if(n < 0)
327 			sysfatal("can't write tree: %r");
328 
329 		strcpy(buf, "can't read tree");
330 
331 		procsetname("awaiting OK for %s", tree);
332 		n = read(fd, buf, sizeof buf - 1);
333 		if(n!=2 || buf[0]!='O' || buf[1]!='K'){
334 			if (timedout)
335 				sysfatal("timed out connecting to %s", na);
336 			buf[sizeof buf - 1] = '\0';
337 			sysfatal("bad remote tree: %s", buf);
338 		}
339 	}
340 
341 	if(oldserver)
342 		return old9p(fd);
343 	return fd;
344 }
345 
346 int
passive(void)347 passive(void)
348 {
349 	int fd;
350 
351 	/*
352 	 * Ignore doauth==0 on purpose.  Is it useful here?
353 	 */
354 
355 	procsetname("auth_proxy auth_getkey proto=p9any role=server");
356 	ai = auth_proxy(0, auth_getkey, "proto=p9any role=server");
357 	if(ai == nil)
358 		sysfatal("auth_proxy: %r");
359 	if(auth_chuid(ai, nil) < 0)
360 		sysfatal("auth_chuid: %r");
361 	putenv("service", "import");
362 
363 	fd = dup(0, -1);
364 	close(0);
365 	open("/dev/null", ORDWR);
366 	close(1);
367 	open("/dev/null", ORDWR);
368 
369 	return fd;
370 }
371 
372 void
usage(void)373 usage(void)
374 {
375 	fprint(2, "usage: import [-abcCm] [-A] [-E clear|ssl|tls] "
376 "[-e 'crypt auth'|clear] [-k keypattern] [-p] host remotefs [mountpoint]\n");
377 	exits("usage");
378 }
379 
380 /* Network on fd1, mount driver on fd0 */
381 int
filter(int fd,char * cmd,char * host)382 filter(int fd, char *cmd, char *host)
383 {
384 	int p[2], len, argc;
385 	char newport[256], buf[256], *s;
386 	char *argv[16], *file, *pbuf;
387 
388 	if ((len = read(fd, newport, sizeof newport - 1)) < 0)
389 		sysfatal("filter: cannot write port; %r");
390 	newport[len] = '\0';
391 
392 	if ((s = strchr(newport, '!')) == nil)
393 		sysfatal("filter: illegally formatted port %s", newport);
394 
395 	strecpy(buf, buf+sizeof buf, netmkaddr(host, "tcp", "0"));
396 	pbuf = strrchr(buf, '!');
397 	strecpy(pbuf, buf+sizeof buf, s);
398 
399 	if(debug)
400 		fprint(2, "filter: remote port %s\n", newport);
401 
402 	argc = tokenize(cmd, argv, nelem(argv)-2);
403 	if (argc == 0)
404 		sysfatal("filter: empty command");
405 	argv[argc++] = "-c";
406 	argv[argc++] = buf;
407 	argv[argc] = nil;
408 	file = argv[0];
409 	if (s = strrchr(argv[0], '/'))
410 		argv[0] = s+1;
411 
412 	if(pipe(p) < 0)
413 		sysfatal("pipe: %r");
414 
415 	switch(rfork(RFNOWAIT|RFPROC|RFFDG)) {
416 	case -1:
417 		sysfatal("rfork record module: %r");
418 	case 0:
419 		dup(p[0], 1);
420 		dup(p[0], 0);
421 		close(p[0]);
422 		close(p[1]);
423 		exec(file, argv);
424 		sysfatal("exec record module: %r");
425 	default:
426 		close(fd);
427 		close(p[0]);
428 	}
429 	return p[1];
430 }
431 
432 static void
mksecret(char * t,uchar * f)433 mksecret(char *t, uchar *f)
434 {
435 	sprint(t, "%2.2ux%2.2ux%2.2ux%2.2ux%2.2ux%2.2ux%2.2ux%2.2ux%2.2ux%2.2ux",
436 		f[0], f[1], f[2], f[3], f[4], f[5], f[6], f[7], f[8], f[9]);
437 }
438