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