xref: /illumos-gate/usr/src/cmd/cmd-inet/usr.bin/rdist/main.c (revision 45ede40b2394db7967e59f19288fae9b62efd4aa)
1 /*
2  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
3  * Use is subject to license terms.
4  */
5 
6 /*
7  * Copyright (c) 1983 Regents of the University of California.
8  * All rights reserved.
9  *
10  * Redistribution and use in source and binary forms are permitted
11  * provided that the above copyright notice and this paragraph are
12  * duplicated in all such forms and that any documentation,
13  * advertising materials, and other materials related to such
14  * distribution and use acknowledge that the software was developed
15  * by the University of California, Berkeley.  The name of the
16  * University may not be used to endorse or promote products derived
17  * from this software without specific prior written permission.
18  */
19 
20 #include "defs.h"
21 #include <string.h>
22 #include <syslog.h>
23 #include <k5-int.h>
24 #include <krb5defs.h>
25 #include <priv_utils.h>
26 
27 #define	NHOSTS 100
28 
29 /*
30  * Remote distribution program.
31  */
32 
33 char	*distfile = NULL;
34 char	Tmpfile[] = "/tmp/rdistXXXXXX";
35 char	*tmpname = &Tmpfile[5];
36 
37 int	debug;		/* debugging flag */
38 int	nflag;		/* NOP flag, just print commands without executing */
39 int	qflag;		/* Quiet. Don't print messages */
40 int	options;	/* global options */
41 int	iamremote;	/* act as remote server for transfering files */
42 
43 FILE	*fin = NULL;	/* input file pointer */
44 int	rem = -1;	/* file descriptor to remote source/sink process */
45 char	host[32];	/* host name */
46 int	nerrs;		/* number of errors while sending/receiving */
47 char	user[10];	/* user's name */
48 char	homedir[128];	/* user's home directory */
49 char	buf[RDIST_BUFSIZ];	/* general purpose buffer */
50 
51 struct	passwd *pw;	/* pointer to static area used by getpwent */
52 struct	group *gr;	/* pointer to static area used by getgrent */
53 
54 char des_inbuf[2 * RDIST_BUFSIZ];	/* needs to be > largest read size */
55 char des_outbuf[2 * RDIST_BUFSIZ];	/* needs to be > largest write size */
56 krb5_data desinbuf, desoutbuf;
57 krb5_encrypt_block eblock;		/* eblock for encrypt/decrypt */
58 krb5_context bsd_context = NULL;
59 krb5_auth_context auth_context;
60 krb5_creds *cred;
61 char *krb_cache = NULL;
62 krb5_flags authopts;
63 krb5_error_code status;
64 enum kcmd_proto kcmd_proto = KCMD_NEW_PROTOCOL;
65 
66 int encrypt_flag = 0;	/* Flag set when encryption is used */
67 int krb5auth_flag = 0;	/* Flag set, when KERBEROS is enabled */
68 static profile_options_boolean autologin_option[] = {
69 	{ "autologin", &krb5auth_flag, 0 },
70 	{ NULL, NULL, 0 }
71 };
72 static int no_krb5auth_flag = 0;
73 
74 int debug_port = 0;
75 
76 int retval = 0;
77 char *krb_realm = NULL;
78 
79 /* Flag set, if -PN / -PO is specified */
80 static boolean_t rcmdoption_done = B_FALSE;
81 
82 static int encrypt_done = 0;	/* Flag set, if -x is specified */
83 profile_options_boolean option[] = {
84 	{ "encrypt", &encrypt_flag, 0 },
85 	{ NULL, NULL, 0 }
86 };
87 
88 static char *rcmdproto = NULL;
89 profile_option_strings rcmdversion[] = {
90 	{ "rcmd_protocol", &rcmdproto, 0 },
91 	{ NULL, NULL, 0 }
92 };
93 
94 char *realmdef[] = { "realms", NULL, "rdist", NULL };
95 char *appdef[] = { "appdefaults", "rdist", NULL };
96 
97 static void usage(void);
98 static char *prtype(int t);
99 static void prsubcmd(struct subcmd *s);
100 static void docmdargs(int nargs, char *args[]);
101 void prnames();
102 void prcmd();
103 
104 int
105 main(argc, argv)
106 	int argc;
107 	char *argv[];
108 {
109 	register char *arg;
110 	int cmdargs = 0;
111 	char *dhosts[NHOSTS], **hp = dhosts;
112 
113 	(void) setlocale(LC_ALL, "");
114 
115 	pw = getpwuid(getuid());
116 	if (pw == NULL) {
117 		(void) fprintf(stderr, gettext("%s: Who are you?\n"), argv[0]);
118 		exit(1);
119 	}
120 	strncpy(user, pw->pw_name, sizeof (user));
121 	user[sizeof (user) - 1] = '\0';
122 	strncpy(homedir, pw->pw_dir, sizeof (homedir));
123 	homedir[sizeof (homedir) - 1] = '\0';
124 	gethostname(host, sizeof (host));
125 
126 	while (--argc > 0) {
127 		if ((arg = *++argv)[0] != '-')
128 			break;
129 		if ((strcmp(arg, "-Server") == 0))
130 			iamremote++;
131 		else while (*++arg) {
132 			if (strncmp(*argv, "-PO", 3) == 0) {
133 				if (rcmdoption_done == B_TRUE) {
134 					(void) fprintf(stderr, gettext("rdist: "
135 						"Only one of -PN "
136 						"and -PO allowed.\n"));
137 					usage();
138 				}
139 				kcmd_proto = KCMD_OLD_PROTOCOL;
140 				krb5auth_flag++;
141 				rcmdoption_done = B_TRUE;
142 				break;
143 			}
144 			if (strncmp(*argv, "-PN", 3) == 0) {
145 				if (rcmdoption_done == B_TRUE) {
146 					(void) fprintf(stderr, gettext("rdist: "
147 						"Only one of -PN "
148 						"and -PO allowed.\n"));
149 					usage();
150 				}
151 				kcmd_proto = KCMD_NEW_PROTOCOL;
152 				krb5auth_flag++;
153 				rcmdoption_done = B_TRUE;
154 				break;
155 			}
156 
157 			switch (*arg) {
158 #ifdef DEBUG
159 			case 'p':
160 				if (--argc <= 0)
161 					usage();
162 				debug_port = htons(atoi(*++argv));
163 				break;
164 #endif /* DEBUG */
165 			case 'k':
166 				if (--argc <= 0) {
167 					(void) fprintf(stderr, gettext("rdist: "
168 						"-k flag must be followed with "
169 						" a realm name.\n"));
170 					exit(1);
171 				}
172 				if ((krb_realm = strdup(*++argv)) == NULL) {
173 					(void) fprintf(stderr, gettext("rdist: "
174 						"Cannot malloc.\n"));
175 					exit(1);
176 				}
177 				krb5auth_flag++;
178 				break;
179 
180 			case 'K':
181 				no_krb5auth_flag++;
182 				break;
183 
184 			case 'a':
185 				krb5auth_flag++;
186 				break;
187 
188 			case 'x':
189 				encrypt_flag++;
190 				encrypt_done++;
191 				krb5auth_flag++;
192 				break;
193 
194 			case 'f':
195 				if (--argc <= 0)
196 					usage();
197 				distfile = *++argv;
198 				if (distfile[0] == '-' && distfile[1] == '\0')
199 					fin = stdin;
200 				break;
201 
202 			case 'm':
203 				if (--argc <= 0)
204 					usage();
205 				if (hp >= &dhosts[NHOSTS-2]) {
206 					(void) fprintf(stderr, gettext("rdist:"
207 						" too many destination"
208 						" hosts\n"));
209 					exit(1);
210 				}
211 				*hp++ = *++argv;
212 				break;
213 
214 			case 'd':
215 				if (--argc <= 0)
216 					usage();
217 				define(*++argv);
218 				break;
219 
220 			case 'D':
221 				debug++;
222 				break;
223 
224 			case 'c':
225 				cmdargs++;
226 				break;
227 
228 			case 'n':
229 				if (options & VERIFY) {
230 					printf("rdist: -n overrides -v\n");
231 					options &= ~VERIFY;
232 				}
233 				nflag++;
234 				break;
235 
236 			case 'q':
237 				qflag++;
238 				break;
239 
240 			case 'b':
241 				options |= COMPARE;
242 				break;
243 
244 			case 'R':
245 				options |= REMOVE;
246 				break;
247 
248 			case 'v':
249 				if (nflag) {
250 					printf("rdist: -n overrides -v\n");
251 					break;
252 				}
253 				options |= VERIFY;
254 				break;
255 
256 			case 'w':
257 				options |= WHOLE;
258 				break;
259 
260 			case 'y':
261 				options |= YOUNGER;
262 				break;
263 
264 			case 'h':
265 				options |= FOLLOW;
266 				break;
267 
268 			case 'i':
269 				options |= IGNLNKS;
270 				break;
271 
272 			default:
273 				usage();
274 			}
275 		}
276 	}
277 	*hp = NULL;
278 
279 	mktemp(Tmpfile);
280 
281 	/*
282 	 * if the user disables krb5 on the cmdline (-K), then skip
283 	 * all krb5 setup.
284 	 *
285 	 * if the user does not disable krb5 or enable krb5 on the
286 	 * cmdline, check krb5.conf to see if it should be enabled.
287 	 */
288 
289 	if (no_krb5auth_flag) {
290 		krb5auth_flag = 0;
291 		encrypt_flag = 0;
292 	} else if (!krb5auth_flag) {
293 		/* is autologin set in krb5.conf? */
294 		status = krb5_init_context(&bsd_context);
295 		/* don't sweat failure here */
296 		if (!status) {
297 			/*
298 			 * note that the call to profile_get_options_boolean
299 			 * with autologin_option can affect value of
300 			 * krb5auth_flag
301 			 */
302 			(void) profile_get_options_boolean(bsd_context->profile,
303 							appdef,
304 							autologin_option);
305 		}
306 	}
307 
308 	if (krb5auth_flag > 0) {
309 		if (!bsd_context) {
310 			status = krb5_init_context(&bsd_context);
311 			if (status) {
312 				com_err("rdist", status,
313 				    gettext("while initializing krb5"));
314 				exit(1);
315 			}
316 		}
317 
318 		/* Set up des buffers */
319 		desinbuf.data = des_inbuf;
320 		desoutbuf.data = des_outbuf;
321 		desinbuf.length = sizeof (des_inbuf);
322 		desoutbuf.length = sizeof (des_outbuf);
323 
324 		/*
325 		 * Get our local realm to look up local realm options.
326 		 */
327 		status = krb5_get_default_realm(bsd_context, &realmdef[1]);
328 		if (status) {
329 			com_err("rdist", status,
330 				gettext("while getting default realm"));
331 			exit(1);
332 		}
333 		/*
334 		 * See if encryption should be done for this realm
335 		 */
336 		profile_get_options_boolean(bsd_context->profile, realmdef,
337 						option);
338 		/*
339 		 * Check the appdefaults section
340 		 */
341 		profile_get_options_boolean(bsd_context->profile, appdef,
342 						option);
343 		profile_get_options_string(bsd_context->profile, appdef,
344 						rcmdversion);
345 
346 		if ((encrypt_done > 0) || (encrypt_flag > 0)) {
347 			if (krb5_privacy_allowed() == TRUE) {
348 				encrypt_flag++;
349 			} else {
350 				(void) fprintf(stderr, gettext("rdist: "
351 						"Encryption not supported.\n"));
352 				exit(1);
353 			}
354 		}
355 
356 		if ((rcmdoption_done == B_FALSE) && (rcmdproto != NULL)) {
357 			if (strncmp(rcmdproto, "rcmdv2", 6) == 0) {
358 				kcmd_proto = KCMD_NEW_PROTOCOL;
359 			} else if (strncmp(rcmdproto, "rcmdv1", 6) == 0) {
360 				kcmd_proto = KCMD_OLD_PROTOCOL;
361 			} else {
362 				(void) fprintf(stderr, gettext("Unrecognized "
363 					"KCMD protocol (%s)"), rcmdproto);
364 				exit(1);
365 			}
366 		}
367 	}
368 
369 	if (iamremote) {
370 		setreuid(getuid(), getuid());
371 		server();
372 		exit(nerrs != 0);
373 	}
374 	if (__init_suid_priv(0, PRIV_NET_PRIVADDR, NULL) == -1) {
375 		(void) fprintf(stderr,
376 			"rdist needs to run with sufficient privilege\n");
377 		exit(1);
378 	}
379 
380 	if (cmdargs)
381 		docmdargs(argc, argv);
382 	else {
383 		if (fin == NULL) {
384 			if (distfile == NULL) {
385 				if ((fin = fopen("distfile", "r")) == NULL)
386 					fin = fopen("Distfile", "r");
387 			} else
388 				fin = fopen(distfile, "r");
389 			if (fin == NULL) {
390 				perror(distfile ? distfile : "distfile");
391 				exit(1);
392 			}
393 		}
394 		yyparse();
395 		if (nerrs == 0)
396 			docmds(dhosts, argc, argv);
397 	}
398 
399 	return (nerrs != 0);
400 }
401 
402 static void
403 usage()
404 {
405 	printf(gettext("Usage: rdist [-nqbhirvwyDax] [-PN / -PO] "
406 #ifdef DEBUG
407 	"[-p port] "
408 #endif /* DEBUG */
409 	"[-k realm] [-f distfile] [-d var=value] [-m host] [file ...]\n"));
410 	printf(gettext("or: rdist [-nqbhirvwyDax] [-PN / -PO] [-p port] "
411 	"[-k realm] -c source [...] machine[:dest]\n"));
412 	exit(1);
413 }
414 
415 /*
416  * rcp like interface for distributing files.
417  */
418 static void
419 docmdargs(nargs, args)
420 	int nargs;
421 	char *args[];
422 {
423 	register struct namelist *nl, *prev;
424 	register char *cp;
425 	struct namelist *files, *hosts;
426 	struct subcmd *cmds;
427 	char *dest;
428 	static struct namelist tnl = { NULL, NULL };
429 	int i;
430 
431 	if (nargs < 2)
432 		usage();
433 
434 	prev = NULL;
435 	for (i = 0; i < nargs - 1; i++) {
436 		nl = makenl(args[i]);
437 		if (prev == NULL)
438 			files = prev = nl;
439 		else {
440 			prev->n_next = nl;
441 			prev = nl;
442 		}
443 	}
444 
445 	cp = args[i];
446 	if ((dest = index(cp, ':')) != NULL)
447 		*dest++ = '\0';
448 	tnl.n_name = cp;
449 	hosts = expand(&tnl, E_ALL);
450 	if (nerrs)
451 		exit(1);
452 
453 	if (dest == NULL || *dest == '\0')
454 		cmds = NULL;
455 	else {
456 		cmds = makesubcmd(INSTALL);
457 		cmds->sc_options = options;
458 		cmds->sc_name = dest;
459 	}
460 
461 	if (debug) {
462 		printf("docmdargs()\nfiles = ");
463 		prnames(files);
464 		printf("hosts = ");
465 		prnames(hosts);
466 	}
467 	insert(NULL, files, hosts, cmds);
468 	docmds(NULL, 0, NULL);
469 }
470 
471 /*
472  * Print a list of NAME blocks (mostly for debugging).
473  */
474 void
475 prnames(nl)
476 	register struct namelist *nl;
477 {
478 	printf("( ");
479 	while (nl != NULL) {
480 		printf("%s ", nl->n_name);
481 		nl = nl->n_next;
482 	}
483 	printf(")\n");
484 }
485 
486 void
487 prcmd(c)
488 	struct cmd *c;
489 {
490 	extern char *prtype();
491 
492 	while (c) {
493 		printf("c_type %s, c_name %s, c_label %s, c_files ",
494 			prtype(c->c_type), c->c_name,
495 			c->c_label?  c->c_label : "NULL");
496 		prnames(c->c_files);
497 		prsubcmd(c->c_cmds);
498 		c = c->c_next;
499 	}
500 }
501 
502 static void
503 prsubcmd(s)
504 	struct subcmd *s;
505 {
506 	extern char *prtype();
507 	extern char *proptions();
508 
509 	while (s) {
510 		printf("sc_type %s, sc_options %d%s, sc_name %s, sc_args ",
511 			prtype(s->sc_type),
512 			s->sc_options, proptions(s->sc_options),
513 			s->sc_name ? s->sc_name : "NULL");
514 		prnames(s->sc_args);
515 		s = s->sc_next;
516 	}
517 }
518 
519 char *
520 prtype(t)
521 	int t;
522 {
523 	switch (t) {
524 		case EQUAL:
525 			return ("EQUAL");
526 		case LP:
527 			return ("LP");
528 		case RP:
529 			return ("RP");
530 		case SM:
531 			return ("SM");
532 		case ARROW:
533 			return ("ARROW");
534 		case COLON:
535 			return ("COLON");
536 		case DCOLON:
537 			return ("DCOLON");
538 		case NAME:
539 			return ("NAME");
540 		case STRING:
541 			return ("STRING");
542 		case INSTALL:
543 			return ("INSTALL");
544 		case NOTIFY:
545 			return ("NOTIFY");
546 		case EXCEPT:
547 			return ("EXCEPT");
548 		case PATTERN:
549 			return ("PATTERN");
550 		case SPECIAL:
551 			return ("SPECIAL");
552 		case OPTION:
553 			return ("OPTION");
554 	}
555 	return (NULL);
556 }
557 
558 char *
559 proptions(o)
560 	int o;
561 {
562 	return (printb((unsigned short) o, OBITS));
563 }
564 
565 char *
566 printb(v, bits)
567 	register char *bits;
568 	register unsigned short v;
569 {
570 	register int i, any = 0;
571 	register char c;
572 	char *p = buf;
573 
574 	bits++;
575 	if (bits) {
576 
577 		*p++ = '<';
578 		while ((i = *bits++) != 0) {
579 			if (v & (1 << (i-1))) {
580 				if (any)
581 					*p++ = ',';
582 				any = 1;
583 				for (; (c = *bits) > 32; bits++)
584 					*p++ = c;
585 			} else
586 				for (; *bits > 32; bits++)
587 					;
588 		}
589 		*p++ = '>';
590 	}
591 
592 	*p = '\0';
593 	return (buf);
594 }
595