Udiff kpropd.c
--- /net/etna.eng/build7/semery/mit2/webrev/usr/src/cmd/krb5/slave/kpropd.c- Wed Sep 8 16:59:56 2004
+++ kpropd.c Wed Sep 8 13:41:50 2004
@@ -23,11 +23,11 @@
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
* WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*/
-#pragma ident "@(#)kpropd.c 1.7 04/09/08 SMI"
+#pragma ident "@(#)kpropd.c 1.6 04/07/27 SMI"
/*
* slave/kpropd.c
*
* Copyright 1990,1991 by the Massachusetts Institute of Technology.
@@ -86,13 +86,36 @@
#include <socket-utils.h>
#include "com_err.h"
#include <errno.h>
#include "kprop.h"
+#include <iprop_hdr.h>
+#include "iprop.h"
+#include <kadm5/admin.h>
+#include <kdb/kdb_log.h>
#define SYSLOG_CLASS LOG_DAEMON
+char *poll_time = NULL;
+char *def_realm = NULL;
+boolean_t runonce = B_FALSE;
+
+/*
+ * This struct simulates the use of _kadm5_server_handle_t
+ */
+typedef struct _kadm5_iprop_handle_t {
+ krb5_ui_4 magic_number;
+ krb5_ui_4 struct_version;
+ krb5_ui_4 api_version;
+ char *cache_name;
+ int destroy_cache;
+ CLIENT *clnt;
+ krb5_context context;
+ kadm5_config_params params;
+ struct _kadm5_iprop_handle_t *lhandle;
+} *kadm5_iprop_handle_t;
+
static char *kprop_version = KPROP_PROT_VERSION;
char *progname;
int debug = 0;
char *srvtab = 0;
@@ -115,13 +138,15 @@
short port = 0;
void PRS
(int, char**);
int do_standalone
- (void);
+ (iprop_role iproprole);
void doit
(int);
+krb5_error_code do_iprop(kdb_log_context *log_ctx);
+
void kerberos_authenticate
(krb5_context,
int,
krb5_principal *,
krb5_enctype *,
@@ -146,10 +171,14 @@
krb5_error_code,
char *);
void recv_error
(krb5_context,
krb5_data *);
+int convert_polltime
+ (char *);
+unsigned int backoff_from_master
+ (int *);
static void usage()
{
fprintf(stderr,
gettext("\nUsage: %s\n"), /* progname may be a long pathname */
@@ -169,28 +198,49 @@
int
main(argc, argv)
int argc;
char **argv;
{
+ krb5_error_code retval;
+ int ret = 0;
+ kdb_log_context *log_ctx;
+
PRS(argc, argv);
+ log_ctx = kpropd_context->kdblog_context;
+
+ if (log_ctx && (log_ctx->iproprole == IPROP_SLAVE)) {
+ /*
+ * We wanna do iprop !
+ */
+ retval = do_iprop(log_ctx);
+ if (retval) {
+ com_err(progname, retval,
+ gettext("do_iprop failed.\n"));
+ exit(1);
+ }
+
+ } else {
if (standalone)
- do_standalone();
+ ret = do_standalone(IPROP_NULL);
else
doit(0);
- exit(0);
}
-int do_standalone()
+ exit(ret);
+}
+
+int do_standalone(iprop_role iproprole)
{
struct linger linger;
struct servent *sp;
int finet, fromlen, s;
int on = 1;
int ret, status = 0;
struct sockaddr_in6 sin6 = { AF_INET6 };
int sin6_size = sizeof (sin6);
+
/* listen for either ipv4 or ipv6 */
finet = socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP);
if (finet < 0 ) {
com_err(progname, errno, gettext("while obtaining socket"));
exit(1);
@@ -200,13 +250,32 @@
sp = getservbyname(KPROP_SERVICE, "tcp");
if (sp == NULL) {
com_err(progname, 0, gettext("%s/tcp: unknown service"),
KPROP_SERVICE);
exit(1);
+ }
sin6.sin6_port = sp->s_port;
} else
sin6.sin6_port = port;
+
+ /*
+ * We need to close the socket immediately if iprop is enabled,
+ * since back-to-back full resyncs are possible, so we do not
+ * linger around for too long
+ */
+ if (iproprole == IPROP_SLAVE) {
+ if (setsockopt(finet, SOL_SOCKET, SO_REUSEADDR,
+ (char *)&on, sizeof(on)) < 0)
+ com_err(progname, errno,
+ gettext("in setsockopt(SO_REUSEADDR)"));
+ linger.l_onoff = 1;
+ linger.l_linger = 2;
+ if (setsockopt(finet, SOL_SOCKET, SO_LINGER,
+ (void *)&linger, sizeof(linger)) < 0)
+ com_err(progname, errno,
+ gettext("in setsockopt(SO_LINGER)"));
+ }
if ((ret = bind(finet, (struct sockaddr *)&sin6, sizeof(sin6))) < 0) {
if (debug) {
on = 1;
fprintf(stderr,
gettext("%s: attempting to rebind socket "
@@ -225,11 +294,11 @@
gettext("while binding listener socket"));
exit(1);
}
}
- if (!debug)
+ if (!debug && (iproprole != IPROP_SLAVE))
daemon(1, 0);
#ifdef PID_FILE
if ((pidfile = fopen(PID_FILE, "w")) != NULL) {
fprintf(pidfile, gettext("%d\n"), getpid());
@@ -242,25 +311,28 @@
if (listen(finet, 5) < 0) {
com_err(progname, errno, gettext("in listen call"));
exit(1);
}
+
while (1) {
int child_pid;
+
s = accept(finet, (struct sockaddr *) &sin6, &sin6_size);
if (s < 0) {
if (errno != EINTR)
com_err(progname, errno,
gettext("from accept system call"));
continue;
}
- if (debug)
+ if (debug && (iproprole != IPROP_SLAVE))
child_pid = 0;
else
child_pid = fork();
+
switch (child_pid) {
case -1:
com_err(progname, errno, gettext("while forking"));
exit(1);
/*NOTREACHED*/
@@ -271,16 +343,29 @@
close(s);
_exit(0);
/*NOTREACHED*/
default:
/* parent */
- wait(0);
+ if (wait(&status) < 0) {
+ com_err(progname, errno,
+ gettext("while waiting to receive database"));
+ exit(1);
+ }
+
close(s);
+ if (iproprole == IPROP_SLAVE)
+ close(finet);
+ if ((ret = WEXITSTATUS(status)) != 0)
+ return (ret);
}
+ if (iproprole == IPROP_SLAVE)
+ break;
}
+
+ return (0);
}
void doit(fd)
int fd;
{
@@ -293,16 +378,20 @@
int lock_fd;
int omask;
krb5_enctype etype;
char ntop[NI_MAXHOST] = "";
krb5_context doit_context;
+ kdb_log_context *log_ctx;
retval = krb5_init_context(&doit_context);
if (retval) {
com_err(progname, retval, gettext("while initializing krb5"));
exit(1);
}
+ log_ctx = kpropd_context->kdblog_context;
+ if (log_ctx && (log_ctx->iproprole == IPROP_SLAVE))
+ ulog_set_role(doit_context, IPROP_SLAVE);
fromlen = (socklen_t)sizeof (from);
if (getpeername(fd, (struct sockaddr *) &from, &fromlen) < 0) {
fprintf(stderr, "%s: ", progname);
@@ -448,10 +537,423 @@
}
exit(0);
}
+
+/*
+ * Routine to handle incremental update transfer(s) from master KDC
+ */
+krb5_error_code do_iprop(kdb_log_context *log_ctx) {
+ CLIENT *cl;
+ kadm5_ret_t retval;
+ kadm5_config_params params;
+ krb5_ccache cc;
+ krb5_principal iprop_svc_principal;
+ void *server_handle = NULL;
+ char *iprop_svc_princstr = NULL;
+ char *master_svc_princstr = NULL;
+ char *admin_server = NULL;
+ char *keytab_name = NULL;
+ unsigned int pollin, backoff_time;
+ int backoff_cnt = 0;
+ int reinit_cnt = 0;
+ int ret;
+ boolean_t frdone = B_FALSE;
+
+ kdb_incr_result_t *incr_ret;
+ static kdb_last_t mylast;
+
+ kdb_fullresync_result_t *full_ret;
+ char *full_resync_arg = NULL;
+
+ kadm5_iprop_handle_t handle;
+ kdb_hlog_t *ulog;
+
+ if (!debug)
+ daemon(0, 0);
+
+ pollin = (unsigned int)0;
+ (void) memset((char *)¶ms, 0, sizeof (params));
+ ulog = log_ctx->ulog;
+
+ params.mask |= KADM5_CONFIG_REALM;
+ params.realm = def_realm;
+
+ if (master_svc_princstr == NULL) {
+ if (retval = kadm5_get_kiprop_host_srv_name(kpropd_context,
+ def_realm, &master_svc_princstr)) {
+ com_err(progname, retval,
+ gettext("%s: unable to get kiprop host based "
+ "service name for realm %s\n"),
+ progname, def_realm);
+ exit(1);
+ }
+ }
+
+ /*
+ * Set cc to the default credentials cache
+ */
+ if (retval = krb5_cc_default(kpropd_context, &cc)) {
+ com_err(progname, retval,
+ gettext("while opening default "
+ "credentials cache"));
+ exit(1);
+ }
+
+ retval = krb5_sname_to_principal(kpropd_context, NULL, KIPROP_SVC_NAME,
+ KRB5_NT_SRV_HST, &iprop_svc_principal);
+ if (retval) {
+ com_err(progname, retval, gettext("while trying to construct "
+ "host service principal"));
+ exit(1);
+ }
+
+ if (retval = krb5_unparse_name(kpropd_context, iprop_svc_principal,
+ &iprop_svc_princstr)) {
+ com_err(progname, retval,
+ gettext("while canonicalizing "
+ "principal name"));
+ krb5_free_principal(kpropd_context, iprop_svc_principal);
+ exit(1);
+ }
+ krb5_free_principal(kpropd_context, iprop_svc_principal);
+
+reinit:
+ /*
+ * Authentication, initialize rpcsec_gss handle etc.
+ */
+ retval = kadm5_init_with_skey(iprop_svc_princstr, keytab_name,
+ master_svc_princstr,
+ ¶ms,
+ KADM5_STRUCT_VERSION,
+ KADM5_API_VERSION_2,
+ &server_handle);
+
+ if (retval) {
+ if (retval == KADM5_RPC_ERROR) {
+ reinit_cnt++;
+ if (server_handle)
+ kadm5_destroy((void *) server_handle);
+ server_handle = (void *)NULL;
+ handle = (kadm5_iprop_handle_t)NULL;
+
+ com_err(progname, retval, gettext(
+ "while attempting to connect"
+ " to master KDC ... retrying"));
+ backoff_time = backoff_from_master(&reinit_cnt);
+ (void) sleep(backoff_time);
+ goto reinit;
+ } else {
+ com_err(progname, retval,
+ gettext("while initializing %s interface"),
+ progname);
+ if (retval == KADM5_BAD_CLIENT_PARAMS ||
+ retval == KADM5_BAD_SERVER_PARAMS)
+ usage();
+ exit(1);
+ }
+ }
+
+ /*
+ * Reset re-initialization count to zero now.
+ */
+ reinit_cnt = backoff_time = 0;
+
+ /*
+ * Reset the handle to the correct type for the RPC call
+ */
+ handle = server_handle;
+
+ /*
+ * If we have reached this far, we have succesfully established
+ * a RPCSEC_GSS connection; we now start polling for updates
+ */
+ if (poll_time == NULL) {
+ if ((poll_time = (char *)strdup("2m")) == NULL) {
+ com_err(progname, ENOMEM,
+ gettext("Unable to allocate poll_time"));
+ exit(1);
+ }
+ }
+
+ if (pollin == (unsigned int)0)
+ pollin = convert_polltime(poll_time);
+
+ for (;;) {
+ incr_ret = NULL;
+ full_ret = NULL;
+
+ /*
+ * Get the most recent ulog entry sno + ts, which
+ * we package in the request to the master KDC
+ */
+ mylast.last_sno = ulog->kdb_last_sno;
+ mylast.last_time = ulog->kdb_last_time;
+
+ /*
+ * Loop continuously on an iprop_get_updates_1(),
+ * so that we can keep probing the master for updates
+ * or (if needed) do a full resync of the krb5 db.
+ */
+
+ incr_ret = iprop_get_updates_1(&mylast, handle->clnt);
+ if (incr_ret == (kdb_incr_result_t *)NULL) {
+ clnt_perror(handle->clnt,
+ "iprop_get_updates call failed");
+ if (server_handle)
+ kadm5_destroy((void *)server_handle);
+ server_handle = (void *)NULL;
+ handle = (kadm5_iprop_handle_t)NULL;
+ goto reinit;
+ }
+
+ switch (incr_ret->ret) {
+
+ case UPDATE_FULL_RESYNC_NEEDED:
+ /*
+ * We dont do a full resync again, if the last
+ * X'fer was a resync and if the master sno is
+ * still "0", i.e. no updates so far.
+ */
+ if ((frdone == B_TRUE) && (incr_ret->lastentry.last_sno
+ == 0)) {
+ break;
+ } else {
+
+ full_ret = iprop_full_resync_1((void *)
+ &full_resync_arg, handle->clnt);
+
+ if (full_ret == (kdb_fullresync_result_t *)
+ NULL) {
+ clnt_perror(handle->clnt,
+ "iprop_full_resync call failed");
+ if (server_handle)
+ kadm5_destroy((void *)
+ server_handle);
+ server_handle = (void *)NULL;
+ handle = (kadm5_iprop_handle_t)NULL;
+ goto reinit;
+ }
+ }
+
+ switch (full_ret->ret) {
+ case UPDATE_OK:
+ backoff_cnt = 0;
+ /*
+ * We now listen on the kprop port for
+ * the full dump
+ */
+ ret = do_standalone(log_ctx->iproprole);
+ if (ret)
+ syslog(LOG_WARNING,
+ gettext("kpropd: Full resync, "
+ "invalid return."));
+ if (debug)
+ if (ret)
+ fprintf(stderr,
+ gettext("Full resync "
+ "was unsuccessful\n"));
+ else
+ fprintf(stderr,
+ gettext("Full resync "
+ "was successful\n"));
+ frdone = B_TRUE;
+ break;
+
+ case UPDATE_BUSY:
+ /*
+ * Exponential backoff
+ */
+ backoff_cnt++;
+ break;
+
+ case UPDATE_FULL_RESYNC_NEEDED:
+ case UPDATE_NIL:
+ default:
+ backoff_cnt = 0;
+ frdone = B_FALSE;
+ syslog(LOG_ERR, gettext("kpropd: Full resync,"
+ " invalid return from master KDC."));
+ break;
+
+ case UPDATE_PERM_DENIED:
+ syslog(LOG_ERR, gettext("kpropd: Full resync,"
+ " permission denied."));
+ goto error;
+
+ case UPDATE_ERROR:
+ syslog(LOG_ERR, gettext("kpropd: Full resync,"
+ " error returned from master KDC."));
+ goto error;
+ }
+ break;
+
+ case UPDATE_OK:
+ backoff_cnt = 0;
+ frdone = B_FALSE;
+
+ /*
+ * ulog_replay() will convert the ulog updates to db
+ * entries using the kdb conv api and will commit
+ * the entries to the slave kdc database
+ */
+ retval = ulog_replay(kpropd_context, incr_ret);
+
+ if (retval) {
+ syslog(LOG_ERR, gettext("kpropd: ulog_replay"
+ " failed, updates not registered."));
+ break;
+ }
+
+ if (debug)
+ fprintf(stderr, gettext("Update transfer "
+ "from master was OK\n"));
+ break;
+
+ case UPDATE_PERM_DENIED:
+ syslog(LOG_ERR, gettext("kpropd: get_updates,"
+ " permission denied."));
+ goto error;
+
+ case UPDATE_ERROR:
+ syslog(LOG_ERR, gettext("kpropd: get_updates, error "
+ "returned from master KDC."));
+ goto error;
+
+ case UPDATE_BUSY:
+ /*
+ * Exponential backoff
+ */
+ backoff_cnt++;
+ break;
+
+ case UPDATE_NIL:
+ /*
+ * Master-slave are in sync
+ */
+ if (debug)
+ fprintf(stderr, gettext("Master, slave KDC's "
+ "are in-sync, no updates\n"));
+ backoff_cnt = 0;
+ frdone = B_FALSE;
+ break;
+
+ default:
+ backoff_cnt = 0;
+ syslog(LOG_ERR, gettext("kpropd: get_updates,"
+ " invalid return from master KDC."));
+ break;
+ }
+
+ if (runonce == B_TRUE)
+ goto done;
+
+ /*
+ * Sleep for the specified poll interval (Default is 2 mts),
+ * or do a binary exponential backoff if we get an
+ * UPDATE_BUSY signal
+ */
+ if (backoff_cnt > 0) {
+ backoff_time = backoff_from_master(&backoff_cnt);
+ if (debug)
+ fprintf(stderr, gettext("Busy signal received "
+ "from master, backoff for %d secs\n"),
+ backoff_time);
+ (void) sleep(backoff_time);
+ }
+ else
+ (void) sleep(pollin);
+
+ }
+
+
+error:
+ if (debug)
+ fprintf(stderr, gettext("ERROR returned by master, bailing\n"));
+ syslog(LOG_ERR, gettext("kpropd: ERROR returned by master KDC,"
+ " bailing.\n"));
+done:
+ if (poll_time)
+ free(poll_time);
+ if(iprop_svc_princstr)
+ free(iprop_svc_princstr);
+ if (master_svc_princstr)
+ free(master_svc_princstr);
+ if (retval = krb5_cc_close(kpropd_context, cc)) {
+ com_err(progname, retval,
+ gettext("while closing default ccache"));
+ exit(1);
+ }
+ if (def_realm)
+ free(def_realm);
+ if (server_handle)
+ kadm5_destroy((void *)server_handle);
+ if (kpropd_context)
+ krb5_free_context(kpropd_context);
+
+ if (runonce == B_TRUE)
+ return (0);
+ else
+ exit(1);
+}
+
+
+/*
+ * Do exponential backoff, since master KDC is BUSY or down
+ */
+unsigned int backoff_from_master(int *cnt) {
+ unsigned int btime;
+
+ btime = (unsigned int)(2<<(*cnt));
+ if (btime > MAX_BACKOFF) {
+ btime = MAX_BACKOFF;
+ *cnt--;
+ }
+
+ return (btime);
+}
+
+
+/*
+ * Routine to convert the `pollstr' string to seconds
+ */
+int convert_polltime(char *pollstr) {
+ char *tokenptr = NULL;
+ int len, polltime;
+
+ len = polltime = 0;
+
+ if ((len = strcspn(pollstr, "s")) < strlen(pollstr)) {
+ tokenptr = malloc((len + 1) * sizeof(char));
+ (void) strlcpy(tokenptr, pollstr, len + 1);
+ polltime = atoi(tokenptr);
+ }
+
+ if ((len = strcspn(pollstr, "m")) < strlen(pollstr)) {
+ tokenptr = malloc((len + 1) * sizeof(char));
+ (void) strlcpy(tokenptr, pollstr, len + 1);
+ polltime = atoi(tokenptr) * 60;
+ }
+
+ if ((len = strcspn(pollstr, "h")) < strlen(pollstr)) {
+ tokenptr = malloc((len + 1) * sizeof(char));
+ (void) strlcpy(tokenptr, pollstr, len + 1);
+ polltime = atoi(tokenptr) * 3600;
+ }
+
+ if (tokenptr != NULL)
+ free(tokenptr);
+ /*
+ * If we have a bogus pollstr value, set polltime to the
+ * default of 2 mts (120 seconds).
+ */
+ if (polltime == 0)
+ polltime = 120;
+ return (polltime);
+}
+
static void
kpropd_com_err_proc(whoami, code, fmt, args)
const char *whoami;
long code;
const char *fmt;
@@ -475,12 +977,12 @@
int c;
struct hostent *hp;
char my_host_name[MAXHOSTNAMELEN], buf[BUFSIZ];
krb5_error_code retval;
static const char tmp[] = ".temp";
-
kadm5_config_params params;
+
(void) setlocale(LC_ALL, "");
#if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */
#define TEXT_DOMAIN "KPROPD_TEST" /* Use this only if it weren't */
#endif
@@ -494,15 +996,25 @@
com_err(argv[0], retval,
gettext("while initializing krb5"));
exit(1);
}
progname = argv[0];
- while ((c = getopt(argc, argv, "df:F:p:P:r:s:Sa:")) != EOF){
+ while ((c = getopt(argc, argv, "dtf:F:p:P:r:s:Sa:")) != EOF){
switch (c) {
case 'd':
debug++;
break;
+ case 't':
+ /*
+ * Undocumented option - for testing only.
+ *
+ * Option to run the kpropd server exactly
+ * once (this is true only if iprop is enabled).
+ */
+ runonce = B_TRUE;
+ break;
+
case 'f':
file = optarg;
if (!file)
usage();
break;
@@ -523,14 +1035,14 @@
break;
case 'r':
realm = optarg;
if (!realm)
usage();
- break;
- case 's':
params.realm = realm;
params.mask |= KADM5_CONFIG_REALM;
+ break;
+ case 's':
srvtab = optarg;
if (!srvtab)
usage();
break;
case 'S':
@@ -579,13 +1091,42 @@
gettext("while allocating filename for temp file"));
exit(1);
}
strcpy(temp_file_name, file);
strcat(temp_file_name, tmp);
+
+ retval = kadm5_get_config_params(kpropd_context, NULL, NULL, ¶ms,
+ ¶ms);
+ if (retval) {
+ com_err(progname, retval, gettext("while initializing"));
+ exit(1);
}
+ if (params.iprop_enabled == TRUE) {
+ ulog_set_role(kpropd_context, IPROP_SLAVE);
+ poll_time = params.iprop_polltime;
+ if (ulog_map(kpropd_context, ¶ms, FKPROPD)) {
+ com_err(progname, errno,
+ gettext("Unable to map log!\n"));
+ exit(1);
+ }
+ }
+
/*
+ * Grab the realm info and check if iprop is enabled.
+ */
+ if (def_realm == NULL) {
+ retval = krb5_get_default_realm(kpropd_context, &def_realm);
+ if (retval) {
+ com_err(progname, retval,
+ gettext("Unable to get default realm"));
+ exit(1);
+ }
+ }
+}
+
+/*
* Figure out who's calling on the other end of the connection....
*/
void
kerberos_authenticate(context, fd, clientp, etype, ss)
krb5_context context;
@@ -955,14 +1496,17 @@
int error_ret, save_stderr;
int child_pid;
int count;
int waitb;
krb5_error_code retval;
+ kdb_log_context *log_ctx;
if (debug)
printf(gettext("calling kdb5_util to load database\n"));
+ log_ctx = context->kdblog_context;
+
edit_av[0] = kdb5_util;
count = 1;
if (realm) {
edit_av[count++] = "-r";
edit_av[count++] = realm;
@@ -970,10 +1514,14 @@
edit_av[count++] = "load";
if (kerb_database) {
edit_av[count++] = "-d";
edit_av[count++] = kerb_database;
}
+
+ if (log_ctx && (log_ctx->iproprole == IPROP_SLAVE)) {
+ edit_av[count++] = "-i";
+ }
edit_av[count++] = database_file_name;
edit_av[count++] = NULL;
switch(child_pid = fork()) {
case -1: