/*
* Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
* Copyright (c) 1983 Regents of the University of California.
* All rights reserved.
*
* Redistribution and use in source and binary forms are permitted
* provided that the above copyright notice and this paragraph are
* duplicated in all such forms and that any documentation,
* advertising materials, and other materials related to such
* distribution and use acknowledge that the software was developed
* by the University of California, Berkeley. The name of the
* University may not be used to endorse or promote products derived
* from this software without specific prior written permission.
*/
#include "defs.h"
#include <string.h>
#include <syslog.h>
#include <k5-int.h>
#include <krb5defs.h>
#include <priv_utils.h>
#define NHOSTS 100
/*
* Remote distribution program.
*/
char *distfile = NULL;
char Tmpfile[] = "/tmp/rdistXXXXXX";
char *tmpname = &Tmpfile[5];
int debug; /* debugging flag */
int nflag; /* NOP flag, just print commands without executing */
int qflag; /* Quiet. Don't print messages */
int options; /* global options */
int iamremote; /* act as remote server for transfering files */
FILE *fin = NULL; /* input file pointer */
int rem = -1; /* file descriptor to remote source/sink process */
char host[32]; /* host name */
int nerrs; /* number of errors while sending/receiving */
char user[10]; /* user's name */
char homedir[128]; /* user's home directory */
char buf[RDIST_BUFSIZ]; /* general purpose buffer */
struct passwd *pw; /* pointer to static area used by getpwent */
struct group *gr; /* pointer to static area used by getgrent */
char des_inbuf[2 * RDIST_BUFSIZ]; /* needs to be > largest read size */
char des_outbuf[2 * RDIST_BUFSIZ]; /* needs to be > largest write size */
krb5_data desinbuf, desoutbuf;
krb5_encrypt_block eblock; /* eblock for encrypt/decrypt */
krb5_context bsd_context = NULL;
krb5_auth_context auth_context;
krb5_creds *cred;
char *krb_cache = NULL;
krb5_flags authopts;
krb5_error_code status;
enum kcmd_proto kcmd_proto = KCMD_NEW_PROTOCOL;
int encrypt_flag = 0; /* Flag set when encryption is used */
int krb5auth_flag = 0; /* Flag set, when KERBEROS is enabled */
static profile_options_boolean autologin_option[] = {
{ "autologin", &krb5auth_flag, 0 },
{ NULL, NULL, 0 }
};
static int no_krb5auth_flag = 0;
int debug_port = 0;
int retval = 0;
char *krb_realm = NULL;
/* Flag set, if -PN / -PO is specified */
static boolean_t rcmdoption_done = B_FALSE;
static int encrypt_done = 0; /* Flag set, if -x is specified */
profile_options_boolean option[] = {
{ "encrypt", &encrypt_flag, 0 },
{ NULL, NULL, 0 }
};
static char *rcmdproto = NULL;
profile_option_strings rcmdversion[] = {
{ "rcmd_protocol", &rcmdproto, 0 },
{ NULL, NULL, 0 }
};
char *realmdef[] = { "realms", NULL, "rdist", NULL };
char *appdef[] = { "appdefaults", "rdist", NULL };
static void usage(void);
static char *prtype(int t);
static void prsubcmd(struct subcmd *s);
static void docmdargs(int nargs, char *args[]);
void prnames();
void prcmd();
int
main(argc, argv)
int argc;
char *argv[];
{
register char *arg;
int cmdargs = 0;
char *dhosts[NHOSTS], **hp = dhosts;
(void) setlocale(LC_ALL, "");
pw = getpwuid(getuid());
if (pw == NULL) {
(void) fprintf(stderr, gettext("%s: Who are you?\n"), argv[0]);
exit(1);
}
strncpy(user, pw->pw_name, sizeof (user));
user[sizeof (user) - 1] = '\0';
strncpy(homedir, pw->pw_dir, sizeof (homedir));
homedir[sizeof (homedir) - 1] = '\0';
gethostname(host, sizeof (host));
while (--argc > 0) {
if ((arg = *++argv)[0] != '-')
break;
if ((strcmp(arg, "-Server") == 0))
iamremote++;
else while (*++arg) {
if (strncmp(*argv, "-PO", 3) == 0) {
if (rcmdoption_done == B_TRUE) {
(void) fprintf(stderr, gettext("rdist: "
"Only one of -PN "
"and -PO allowed.\n"));
usage();
}
kcmd_proto = KCMD_OLD_PROTOCOL;
krb5auth_flag++;
rcmdoption_done = B_TRUE;
break;
}
if (strncmp(*argv, "-PN", 3) == 0) {
if (rcmdoption_done == B_TRUE) {
(void) fprintf(stderr, gettext("rdist: "
"Only one of -PN "
"and -PO allowed.\n"));
usage();
}
kcmd_proto = KCMD_NEW_PROTOCOL;
krb5auth_flag++;
rcmdoption_done = B_TRUE;
break;
}
switch (*arg) {
#ifdef DEBUG
case 'p':
if (--argc <= 0)
usage();
debug_port = htons(atoi(*++argv));
break;
#endif /* DEBUG */
case 'k':
if (--argc <= 0) {
(void) fprintf(stderr, gettext("rdist: "
"-k flag must be followed with "
" a realm name.\n"));
exit(1);
}
if ((krb_realm = strdup(*++argv)) == NULL) {
(void) fprintf(stderr, gettext("rdist: "
"Cannot malloc.\n"));
exit(1);
}
krb5auth_flag++;
break;
case 'K':
no_krb5auth_flag++;
break;
case 'a':
krb5auth_flag++;
break;
case 'x':
encrypt_flag++;
encrypt_done++;
krb5auth_flag++;
break;
case 'f':
if (--argc <= 0)
usage();
distfile = *++argv;
if (distfile[0] == '-' && distfile[1] == '\0')
fin = stdin;
break;
case 'm':
if (--argc <= 0)
usage();
if (hp >= &dhosts[NHOSTS-2]) {
(void) fprintf(stderr, gettext("rdist:"
" too many destination"
" hosts\n"));
exit(1);
}
*hp++ = *++argv;
break;
case 'd':
if (--argc <= 0)
usage();
define(*++argv);
break;
case 'D':
debug++;
break;
case 'c':
cmdargs++;
break;
case 'n':
if (options & VERIFY) {
printf("rdist: -n overrides -v\n");
options &= ~VERIFY;
}
nflag++;
break;
case 'q':
qflag++;
break;
case 'b':
options |= COMPARE;
break;
case 'R':
options |= REMOVE;
break;
case 'v':
if (nflag) {
printf("rdist: -n overrides -v\n");
break;
}
options |= VERIFY;
break;
case 'w':
options |= WHOLE;
break;
case 'y':
options |= YOUNGER;
break;
case 'h':
options |= FOLLOW;
break;
case 'i':
options |= IGNLNKS;
break;
default:
usage();
}
}
}
*hp = NULL;
mktemp(Tmpfile);
/*
* if the user disables krb5 on the cmdline (-K), then skip
* all krb5 setup.
*
* if the user does not disable krb5 or enable krb5 on the
* cmdline, check krb5.conf to see if it should be enabled.
*/
if (no_krb5auth_flag) {
krb5auth_flag = 0;
encrypt_flag = 0;
} else if (!krb5auth_flag) {
/* is autologin set in krb5.conf? */
status = krb5_init_context(&bsd_context);
/* don't sweat failure here */
if (!status) {
/*
* note that the call to profile_get_options_boolean
* with autologin_option can affect value of
* krb5auth_flag
*/
(void) profile_get_options_boolean(bsd_context->profile,
appdef,
autologin_option);
}
}
if (krb5auth_flag > 0) {
if (!bsd_context) {
status = krb5_init_context(&bsd_context);
if (status) {
com_err("rdist", status,
gettext("while initializing krb5"));
exit(1);
}
}
/* Set up des buffers */
desinbuf.data = des_inbuf;
desoutbuf.data = des_outbuf;
desinbuf.length = sizeof (des_inbuf);
desoutbuf.length = sizeof (des_outbuf);
/*
* Get our local realm to look up local realm options.
*/
status = krb5_get_default_realm(bsd_context, &realmdef[1]);
if (status) {
com_err("rdist", status,
gettext("while getting default realm"));
exit(1);
}
/*
* See if encryption should be done for this realm
*/
profile_get_options_boolean(bsd_context->profile, realmdef,
option);
/*
* Check the appdefaults section
*/
profile_get_options_boolean(bsd_context->profile, appdef,
option);
profile_get_options_string(bsd_context->profile, appdef,
rcmdversion);
if ((encrypt_done > 0) || (encrypt_flag > 0)) {
if (krb5_privacy_allowed() == TRUE) {
encrypt_flag++;
} else {
(void) fprintf(stderr, gettext("rdist: "
"Encryption not supported.\n"));
exit(1);
}
}
if ((rcmdoption_done == B_FALSE) && (rcmdproto != NULL)) {
if (strncmp(rcmdproto, "rcmdv2", 6) == 0) {
kcmd_proto = KCMD_NEW_PROTOCOL;
} else if (strncmp(rcmdproto, "rcmdv1", 6) == 0) {
kcmd_proto = KCMD_OLD_PROTOCOL;
} else {
(void) fprintf(stderr, gettext("Unrecognized "
"KCMD protocol (%s)"), rcmdproto);
exit(1);
}
}
}
if (iamremote) {
setreuid(getuid(), getuid());
server();
exit(nerrs != 0);
}
if (__init_suid_priv(0, PRIV_NET_PRIVADDR, NULL) == -1) {
(void) fprintf(stderr,
"rdist needs to run with sufficient privilege\n");
exit(1);
}
if (cmdargs)
docmdargs(argc, argv);
else {
if (fin == NULL) {
if (distfile == NULL) {
if ((fin = fopen("distfile", "r")) == NULL)
fin = fopen("Distfile", "r");
} else
fin = fopen(distfile, "r");
if (fin == NULL) {
perror(distfile ? distfile : "distfile");
exit(1);
}
}
yyparse();
if (nerrs == 0)
docmds(dhosts, argc, argv);
}
return (nerrs != 0);
}
static void
usage()
{
printf(gettext("Usage: rdist [-nqbhirvwyDax] [-PN / -PO] "
#ifdef DEBUG
"[-p port] "
#endif /* DEBUG */
"[-k realm] [-f distfile] [-d var=value] [-m host] [file ...]\n"));
printf(gettext("or: rdist [-nqbhirvwyDax] [-PN / -PO] [-p port] "
"[-k realm] -c source [...] machine[:dest]\n"));
exit(1);
}
/*
* rcp like interface for distributing files.
*/
static void
docmdargs(nargs, args)
int nargs;
char *args[];
{
register struct namelist *nl, *prev;
register char *cp;
struct namelist *files, *hosts;
struct subcmd *cmds;
char *dest;
static struct namelist tnl = { NULL, NULL };
int i;
if (nargs < 2)
usage();
prev = NULL;
for (i = 0; i < nargs - 1; i++) {
nl = makenl(args[i]);
if (prev == NULL)
files = prev = nl;
else {
prev->n_next = nl;
prev = nl;
}
}
cp = args[i];
if ((dest = index(cp, ':')) != NULL)
*dest++ = '\0';
tnl.n_name = cp;
hosts = expand(&tnl, E_ALL);
if (nerrs)
exit(1);
if (dest == NULL || *dest == '\0')
cmds = NULL;
else {
cmds = makesubcmd(INSTALL);
cmds->sc_options = options;
cmds->sc_name = dest;
}
if (debug) {
printf("docmdargs()\nfiles = ");
prnames(files);
printf("hosts = ");
prnames(hosts);
}
insert(NULL, files, hosts, cmds);
docmds(NULL, 0, NULL);
}
/*
* Print a list of NAME blocks (mostly for debugging).
*/
void
prnames(nl)
register struct namelist *nl;
{
printf("( ");
while (nl != NULL) {
printf("%s ", nl->n_name);
nl = nl->n_next;
}
printf(")\n");
}
void
prcmd(c)
struct cmd *c;
{
extern char *prtype();
while (c) {
printf("c_type %s, c_name %s, c_label %s, c_files ",
prtype(c->c_type), c->c_name,
c->c_label? c->c_label : "NULL");
prnames(c->c_files);
prsubcmd(c->c_cmds);
c = c->c_next;
}
}
static void
prsubcmd(s)
struct subcmd *s;
{
extern char *prtype();
extern char *proptions();
while (s) {
printf("sc_type %s, sc_options %d%s, sc_name %s, sc_args ",
prtype(s->sc_type),
s->sc_options, proptions(s->sc_options),
s->sc_name ? s->sc_name : "NULL");
prnames(s->sc_args);
s = s->sc_next;
}
}
char *
prtype(t)
int t;
{
switch (t) {
case EQUAL:
return ("EQUAL");
case LP:
return ("LP");
case RP:
return ("RP");
case SM:
return ("SM");
case ARROW:
return ("ARROW");
case COLON:
return ("COLON");
case DCOLON:
return ("DCOLON");
case NAME:
return ("NAME");
case STRING:
return ("STRING");
case INSTALL:
return ("INSTALL");
case NOTIFY:
return ("NOTIFY");
case EXCEPT:
return ("EXCEPT");
case PATTERN:
return ("PATTERN");
case SPECIAL:
return ("SPECIAL");
case OPTION:
return ("OPTION");
}
return (NULL);
}
char *
proptions(o)
int o;
{
return (printb((unsigned short) o, OBITS));
}
char *
printb(v, bits)
register char *bits;
register unsigned short v;
{
register int i, any = 0;
register char c;
char *p = buf;
bits++;
if (bits) {
*p++ = '<';
while ((i = *bits++) != 0) {
if (v & (1 << (i-1))) {
if (any)
*p++ = ',';
any = 1;
for (; (c = *bits) > 32; bits++)
*p++ = c;
} else
for (; *bits > 32; bits++)
;
}
*p++ = '>';
}
*p = '\0';
return (buf);
}