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