summaryrefslogtreecommitdiffstats
path: root/src/kadmin/server
diff options
context:
space:
mode:
authorKen Raeburn <raeburn@mit.edu>2008-06-24 05:04:29 +0000
committerKen Raeburn <raeburn@mit.edu>2008-06-24 05:04:29 +0000
commit5661d1290f74312a405db970aea097da77706f71 (patch)
tree0ab69c8078ef3275b99a3ad27f3592b607e43f70 /src/kadmin/server
parent6879f371402854465e5276d36e4792938906097f (diff)
downloadkrb5-5661d1290f74312a405db970aea097da77706f71.tar.gz
krb5-5661d1290f74312a405db970aea097da77706f71.tar.xz
krb5-5661d1290f74312a405db970aea097da77706f71.zip
Merge from branch sun-iprop
git-svn-id: svn://anonsvn.mit.edu/krb5/trunk@20465 dc483132-0cff-0310-8789-dd5450dbe970
Diffstat (limited to 'src/kadmin/server')
-rw-r--r--src/kadmin/server/Makefile.in35
-rw-r--r--src/kadmin/server/ipropd_svc.c615
-rw-r--r--src/kadmin/server/kadm_rpc_svc.c9
-rw-r--r--src/kadmin/server/kadmind.M19
-rw-r--r--src/kadmin/server/misc.h4
-rw-r--r--src/kadmin/server/ovsec_kadmd.c269
-rw-r--r--src/kadmin/server/server_stubs.c5
7 files changed, 900 insertions, 56 deletions
diff --git a/src/kadmin/server/Makefile.in b/src/kadmin/server/Makefile.in
index bc3052b25..63b4941b4 100644
--- a/src/kadmin/server/Makefile.in
+++ b/src/kadmin/server/Makefile.in
@@ -6,14 +6,15 @@ KDB_DEP_LIB=$(DL_LIB) $(THREAD_LINKOPTS)
DEFS=
LOCALINCLUDES = -I$(SRCTOP)/lib/gssapi/generic -I$(SRCTOP)/lib/gssapi/krb5 \
- -I$(BUILDTOP)/lib/gssapi/generic -I$(BUILDTOP)/lib/gssapi/krb5
+ -I$(BUILDTOP)/lib/gssapi/generic -I$(BUILDTOP)/lib/gssapi/krb5 \
+ -I$(SRCTOP)/lib/kadm5/srv
PROG_LIBPATH=-L$(TOPLIBD)
PROG_RPATH=$(KRB5_LIBDIR)
PROG = kadmind
-OBJS = kadm_rpc_svc.o server_stubs.o ovsec_kadmd.o schpw.o misc.o server_glue_v1.o
-SRCS = kadm_rpc_svc.c server_stubs.c ovsec_kadmd.c schpw.c misc.c server_glue_v1.c
+OBJS = kadm_rpc_svc.o server_stubs.o ovsec_kadmd.o schpw.o misc.o server_glue_v1.o ipropd_svc.o
+SRCS = kadm_rpc_svc.c server_stubs.c ovsec_kadmd.c schpw.c misc.c server_glue_v1.c ipropd_svc.c
all:: $(PROG)
@@ -75,15 +76,16 @@ $(OUTPRE)ovsec_kadmd.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
$(SRCTOP)/include/gssrpc/rename.h $(SRCTOP)/include/gssrpc/rpc.h \
$(SRCTOP)/include/gssrpc/rpc_msg.h $(SRCTOP)/include/gssrpc/svc.h \
$(SRCTOP)/include/gssrpc/svc_auth.h $(SRCTOP)/include/gssrpc/xdr.h \
+ $(SRCTOP)/include/iprop.h $(SRCTOP)/include/iprop_hdr.h \
$(SRCTOP)/include/k5-err.h $(SRCTOP)/include/k5-int-pkinit.h \
$(SRCTOP)/include/k5-int.h $(SRCTOP)/include/k5-platform.h \
$(SRCTOP)/include/k5-plugin.h $(SRCTOP)/include/k5-thread.h \
$(SRCTOP)/include/kdb.h $(SRCTOP)/include/kdb_kt.h \
- $(SRCTOP)/include/krb5.h $(SRCTOP)/include/krb5/locate_plugin.h \
- $(SRCTOP)/include/krb5/preauth_plugin.h $(SRCTOP)/include/port-sockets.h \
- $(SRCTOP)/include/socket-utils.h $(SRCTOP)/lib/gssapi/generic/gssapiP_generic.h \
- $(SRCTOP)/lib/gssapi/generic/gssapi_generic.h $(SRCTOP)/lib/gssapi/krb5/gssapiP_krb5.h \
- misc.h ovsec_kadmd.c
+ $(SRCTOP)/include/kdb_log.h $(SRCTOP)/include/krb5.h \
+ $(SRCTOP)/include/krb5/locate_plugin.h $(SRCTOP)/include/krb5/preauth_plugin.h \
+ $(SRCTOP)/include/port-sockets.h $(SRCTOP)/include/socket-utils.h \
+ $(SRCTOP)/lib/gssapi/generic/gssapiP_generic.h $(SRCTOP)/lib/gssapi/generic/gssapi_generic.h \
+ $(SRCTOP)/lib/gssapi/krb5/gssapiP_krb5.h misc.h ovsec_kadmd.c
$(OUTPRE)schpw.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
$(BUILDTOP)/include/gssapi/gssapi.h $(BUILDTOP)/include/gssrpc/types.h \
$(BUILDTOP)/include/kadm5/admin.h $(BUILDTOP)/include/kadm5/chpass_util_strings.h \
@@ -130,3 +132,20 @@ $(OUTPRE)server_glue_v1.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
$(SRCTOP)/include/k5-platform.h $(SRCTOP)/include/k5-thread.h \
$(SRCTOP)/include/kdb.h $(SRCTOP)/include/krb5.h misc.h \
server_glue_v1.c
+$(OUTPRE)ipropd_svc.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
+ $(BUILDTOP)/include/gssapi/gssapi.h $(BUILDTOP)/include/gssrpc/types.h \
+ $(BUILDTOP)/include/kadm5/admin.h $(BUILDTOP)/include/kadm5/admin_internal.h \
+ $(BUILDTOP)/include/kadm5/chpass_util_strings.h $(BUILDTOP)/include/kadm5/kadm_err.h \
+ $(BUILDTOP)/include/kadm5/kadm_rpc.h $(BUILDTOP)/include/kadm5/server_internal.h \
+ $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \
+ $(BUILDTOP)/lib/gssapi/krb5/gssapi_krb5.h $(COM_ERR_DEPS) \
+ $(SRCTOP)/include/adm_proto.h $(SRCTOP)/include/gssrpc/auth.h \
+ $(SRCTOP)/include/gssrpc/auth_gss.h $(SRCTOP)/include/gssrpc/auth_unix.h \
+ $(SRCTOP)/include/gssrpc/clnt.h $(SRCTOP)/include/gssrpc/rename.h \
+ $(SRCTOP)/include/gssrpc/rpc.h $(SRCTOP)/include/gssrpc/rpc_msg.h \
+ $(SRCTOP)/include/gssrpc/svc.h $(SRCTOP)/include/gssrpc/svc_auth.h \
+ $(SRCTOP)/include/gssrpc/xdr.h $(SRCTOP)/include/iprop.h \
+ $(SRCTOP)/include/iprop_hdr.h $(SRCTOP)/include/k5-platform.h \
+ $(SRCTOP)/include/k5-thread.h $(SRCTOP)/include/kdb.h \
+ $(SRCTOP)/include/kdb_log.h $(SRCTOP)/include/krb5.h \
+ $(SRCTOP)/lib/kadm5/srv/server_acl.h ipropd_svc.c misc.h
diff --git a/src/kadmin/server/ipropd_svc.c b/src/kadmin/server/ipropd_svc.c
new file mode 100644
index 000000000..673d2a9af
--- /dev/null
+++ b/src/kadmin/server/ipropd_svc.c
@@ -0,0 +1,615 @@
+/*
+ * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/* #pragma ident "@(#)ipropd_svc.c 1.2 04/02/20 SMI" */
+
+
+#include <stdio.h>
+#include <stdlib.h> /* getenv, exit */
+#include <signal.h>
+#include <sys/types.h>
+#include <sys/resource.h> /* rlimit */
+#include <syslog.h>
+
+#include "k5-platform.h"
+#include <kadm5/admin.h>
+#include <kadm5/kadm_rpc.h>
+#include <kadm5/server_internal.h>
+#include <server_acl.h>
+#include <adm_proto.h>
+#include <string.h>
+#include <gssapi_krb5.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <kdb_log.h>
+#include "misc.h"
+#include "osconf.h"
+
+extern gss_name_t rqst2name(struct svc_req *rqstp);
+
+extern int setup_gss_names(struct svc_req *, gss_buffer_desc *,
+ gss_buffer_desc *);
+extern char *client_addr(struct svc_req *, char *);
+extern void *global_server_handle;
+extern int nofork;
+extern short l_port;
+static char abuf[33];
+
+char *client_addr(struct svc_req *svc, char *buf) {
+ return strcpy(buf, inet_ntoa(svc->rq_xprt->xp_raddr.sin_addr));
+}
+
+static char *reply_ok_str = "UPDATE_OK";
+static char *reply_err_str = "UPDATE_ERROR";
+static char *reply_fr_str = "UPDATE_FULL_RESYNC_NEEDED";
+static char *reply_busy_str = "UPDATE_BUSY";
+static char *reply_nil_str = "UPDATE_NIL";
+static char *reply_perm_str = "UPDATE_PERM_DENIED";
+static char *reply_unknown_str = "<UNKNOWN_CODE>";
+
+#define LOG_UNAUTH _("Unauthorized request: %s, %s, " \
+ "client=%s, service=%s, addr=%s")
+#define LOG_DONE _("Request: %s, %s, %s, client=%s, " \
+ "service=%s, addr=%s")
+
+#ifdef DPRINT
+#undef DPRINT
+#endif
+#define DPRINT(i) if (nofork) printf i
+
+
+static void
+debprret(char *w, update_status_t ret, kdb_sno_t sno)
+{
+ switch (ret) {
+ case UPDATE_OK:
+ printf("%s: end (OK, sno=%u)\n",
+ w, sno);
+ break;
+ case UPDATE_ERROR:
+ printf("%s: end (ERROR)\n", w);
+ break;
+ case UPDATE_FULL_RESYNC_NEEDED:
+ printf("%s: end (FR NEEDED)\n", w);
+ break;
+ case UPDATE_BUSY:
+ printf("%s: end (BUSY)\n", w);
+ break;
+ case UPDATE_NIL:
+ printf("%s: end (NIL)\n", w);
+ break;
+ case UPDATE_PERM_DENIED:
+ printf("%s: end (PERM)\n", w);
+ break;
+ default:
+ printf("%s: end (UNKNOWN return code (%d))\n", w, ret);
+ }
+}
+
+static char *
+replystr(update_status_t ret)
+{
+ switch (ret) {
+ case UPDATE_OK:
+ return (reply_ok_str);
+ case UPDATE_ERROR:
+ return (reply_err_str);
+ case UPDATE_FULL_RESYNC_NEEDED:
+ return (reply_fr_str);
+ case UPDATE_BUSY:
+ return (reply_busy_str);
+ case UPDATE_NIL:
+ return (reply_nil_str);
+ case UPDATE_PERM_DENIED:
+ return (reply_perm_str);
+ default:
+ return (reply_unknown_str);
+ }
+}
+
+/* Returns null on allocation failure.
+ Regardless of success or failure, frees the input buffer. */
+static char *
+buf_to_string(gss_buffer_desc *b)
+{
+ OM_uint32 min_stat;
+ char *s = malloc(b->length+1);
+
+ if (s) {
+ memcpy(s, b->value, b->length);
+ s[b->length] = 0;
+ }
+ (void) gss_release_buffer(&min_stat, b);
+ return s;
+}
+
+kdb_incr_result_t *
+iprop_get_updates_1_svc(kdb_last_t *arg, struct svc_req *rqstp)
+{
+ static kdb_incr_result_t ret;
+ char *whoami = "iprop_get_updates_1";
+ int kret;
+ kadm5_server_handle_t handle = global_server_handle;
+ char *client_name = 0, *service_name = 0;
+ char obuf[256] = {0};
+
+ /* default return code */
+ ret.ret = UPDATE_ERROR;
+
+ DPRINT(("%s: start, last_sno=%lu\n", whoami,
+ (unsigned long) arg->last_sno));
+
+ if (!handle) {
+ krb5_klog_syslog(LOG_ERR,
+ _("%s: server handle is NULL"),
+ whoami);
+ goto out;
+ }
+
+ {
+ gss_buffer_desc client_desc, service_desc;
+
+ if (setup_gss_names(rqstp, &client_desc, &service_desc) < 0) {
+ krb5_klog_syslog(LOG_ERR,
+ _("%s: setup_gss_names failed"),
+ whoami);
+ goto out;
+ }
+ client_name = buf_to_string(&client_desc);
+ service_name = buf_to_string(&service_desc);
+ if (client_name == NULL || service_name == NULL) {
+ free(client_name);
+ free(service_name);
+ krb5_klog_syslog(LOG_ERR,
+ "%s: out of memory recording principal names",
+ whoami);
+ goto out;
+ }
+ }
+
+ DPRINT(("%s: clprinc=`%s'\n\tsvcprinc=`%s'\n",
+ whoami, client_name, service_name));
+
+ if (!kadm5int_acl_check(handle->context,
+ rqst2name(rqstp),
+ ACL_IPROP,
+ NULL,
+ NULL)) {
+ ret.ret = UPDATE_PERM_DENIED;
+
+ krb5_klog_syslog(LOG_NOTICE, LOG_UNAUTH, whoami,
+ "<null>", client_name, service_name,
+ client_addr(rqstp, abuf));
+ goto out;
+ }
+
+ kret = ulog_get_entries(handle->context, *arg, &ret);
+
+ if (ret.ret == UPDATE_OK) {
+ (void) snprintf(obuf, sizeof (obuf),
+ _("%s; Incoming SerialNo=%lu; Outgoing SerialNo=%lu"),
+ replystr(ret.ret),
+ (unsigned long)arg->last_sno,
+ (unsigned long)ret.lastentry.last_sno);
+ } else {
+ (void) snprintf(obuf, sizeof (obuf),
+ _("%s; Incoming SerialNo=%lu; Outgoing SerialNo=N/A"),
+ replystr(ret.ret),
+ (unsigned long)arg->last_sno);
+ }
+
+ krb5_klog_syslog(LOG_NOTICE, LOG_DONE, whoami,
+ obuf,
+ ((kret == 0) ? "success" : error_message(kret)),
+ client_name, service_name,
+ client_addr(rqstp, abuf));
+
+out:
+ if (nofork)
+ debprret(whoami, ret.ret, ret.lastentry.last_sno);
+ free(client_name);
+ free(service_name);
+ return (&ret);
+}
+
+
+/*
+ * Given a client princ (foo/fqdn@R), copy (in arg cl) the fqdn substring.
+ * Return arg cl str ptr on success, else NULL.
+ */
+static char *
+getclhoststr(char *clprinc, char *cl, int len)
+{
+ char *s;
+ if ((s = strchr(clprinc, '/')) != NULL) {
+ /* XXX "!++s"? */
+ if (!++s)
+ return NULL;
+ if (strlen(s) >= len)
+ return NULL;
+ strcpy(cl, s);
+ /* XXX Copy with @REALM first, with bounds check, then
+ chop off the realm?? */
+ if ((s = strchr(cl, '@')) != NULL) {
+ *s = '\0';
+ return (cl); /* success */
+ }
+ }
+
+ return (NULL);
+}
+
+kdb_fullresync_result_t *
+iprop_full_resync_1_svc(/* LINTED */ void *argp, struct svc_req *rqstp)
+{
+ static kdb_fullresync_result_t ret;
+ char *tmpf = 0;
+ char *ubuf = 0;
+ char clhost[MAXHOSTNAMELEN] = {0};
+ int pret, fret;
+ kadm5_server_handle_t handle = global_server_handle;
+ OM_uint32 min_stat;
+ gss_name_t name = NULL;
+ char *client_name = NULL, *service_name = NULL;
+ char *whoami = "iprop_full_resync_1";
+
+ /* default return code */
+ ret.ret = UPDATE_ERROR;
+
+ if (!handle) {
+ krb5_klog_syslog(LOG_ERR,
+ _("%s: server handle is NULL"),
+ whoami);
+ goto out;
+ }
+
+ DPRINT(("%s: start\n", whoami));
+
+ {
+ gss_buffer_desc client_desc, service_desc;
+
+ if (setup_gss_names(rqstp, &client_desc, &service_desc) < 0) {
+ krb5_klog_syslog(LOG_ERR,
+ _("%s: setup_gss_names failed"),
+ whoami);
+ goto out;
+ }
+ client_name = buf_to_string(&client_desc);
+ service_name = buf_to_string(&service_desc);
+ if (client_name == NULL || service_name == NULL) {
+ free(client_name);
+ free(service_name);
+ krb5_klog_syslog(LOG_ERR,
+ "%s: out of memory recording principal names",
+ whoami);
+ goto out;
+ }
+ }
+
+ DPRINT(("%s: clprinc=`%s'\n\tsvcprinc=`%s'\n",
+ whoami, client_name, service_name));
+
+ if (!kadm5int_acl_check(handle->context,
+ rqst2name(rqstp),
+ ACL_IPROP,
+ NULL,
+ NULL)) {
+ ret.ret = UPDATE_PERM_DENIED;
+
+ krb5_klog_syslog(LOG_NOTICE, LOG_UNAUTH, whoami,
+ "<null>", client_name, service_name,
+ client_addr(rqstp, abuf));
+ goto out;
+ }
+
+ if (!getclhoststr(client_name, clhost, sizeof (clhost))) {
+ krb5_klog_syslog(LOG_ERR,
+ _("%s: getclhoststr failed"),
+ whoami);
+ goto out;
+ }
+
+ /*
+ * construct db dump file name; kprop style name + clnt fqdn
+ */
+ if (asprintf(&tmpf, "%s_%s", KPROP_DEFAULT_FILE, clhost) < 0) {
+ krb5_klog_syslog(LOG_ERR,
+ _("%s: unable to construct db dump file name; out of memory"),
+ whoami);
+ goto out;
+ }
+
+ /*
+ * note the -i; modified version of kdb5_util dump format
+ * to include sno (serial number)
+ */
+ if (asprintf(&ubuf, "%s dump -i %s", KPROPD_DEFAULT_KDB5_UTIL,
+ tmpf) < 0) {
+ krb5_klog_syslog(LOG_ERR,
+ _("%s: cannot construct kdb5 util dump string too long; out of memory"),
+ whoami);
+ goto out;
+ }
+
+ /*
+ * Fork to dump the db and xfer it to the slave.
+ * (the fork allows parent to return quickly and the child
+ * acts like a callback to the slave).
+ */
+ fret = fork();
+ DPRINT(("%s: fork=%d (%d)\n", whoami, fret, getpid()));
+
+ switch (fret) {
+ case -1: /* error */
+ if (nofork) {
+ perror(whoami);
+ }
+ krb5_klog_syslog(LOG_ERR,
+ _("%s: fork failed: %s"),
+ whoami,
+ error_message(errno));
+ goto out;
+
+ case 0: /* child */
+ DPRINT(("%s: run `%s' ...\n", whoami, ubuf));
+ (void) signal(SIGCHLD, SIG_DFL);
+ /* run kdb5_util(1M) dump for IProp */
+ /* XXX popen can return NULL; is pclose(NULL) okay? */
+ pret = pclose(popen(ubuf, "w"));
+ DPRINT(("%s: pclose=%d\n", whoami, pret));
+ if (pret != 0) {
+ /* XXX popen/pclose may not set errno
+ properly, and the error could be from the
+ subprocess anyways. */
+ if (nofork) {
+ perror(whoami);
+ }
+ krb5_klog_syslog(LOG_ERR,
+ _("%s: pclose(popen) failed: %s"),
+ whoami,
+ error_message(errno));
+ goto out;
+ }
+
+ DPRINT(("%s: exec `kprop -f %s %s' ...\n",
+ whoami, tmpf, clhost));
+ /* XXX Yuck! */
+ if (getenv("KPROP_PORT"))
+ pret = execl(KPROPD_DEFAULT_KPROP, "kprop", "-f", tmpf,
+ "-P", getenv("KPROP_PORT"),
+ clhost, NULL);
+ else
+ pret = execl(KPROPD_DEFAULT_KPROP, "kprop", "-f", tmpf,
+ clhost, NULL);
+ if (pret == -1) {
+ if (nofork) {
+ perror(whoami);
+ }
+ krb5_klog_syslog(LOG_ERR,
+ _("%s: exec failed: %s"),
+ whoami,
+ error_message(errno));
+ goto out;
+ }
+
+ default: /* parent */
+ ret.ret = UPDATE_OK;
+ /* not used by slave (sno is retrieved from kdb5_util dump) */
+ ret.lastentry.last_sno = 0;
+ ret.lastentry.last_time.seconds = 0;
+ ret.lastentry.last_time.useconds = 0;
+
+ krb5_klog_syslog(LOG_NOTICE, LOG_DONE, whoami,
+ "<null>",
+ "success",
+ client_name, service_name,
+ client_addr(rqstp, abuf));
+
+ goto out;
+ }
+
+out:
+ if (nofork)
+ debprret(whoami, ret.ret, 0);
+ free(client_name);
+ free(service_name);
+ if (name)
+ gss_release_name(&min_stat, &name);
+ free(tmpf);
+ free(ubuf);
+ return (&ret);
+}
+
+static int
+check_iprop_rpcsec_auth(struct svc_req *rqstp)
+{
+ /* XXX Since the client can authenticate against any principal in
+ the database, we need to do a sanity check. Only checking for
+ "kiprop" now, but that means theoretically the client could be
+ authenticating to kiprop on some other machine. */
+ /* Code taken from kadm_rpc_svc.c, tweaked. */
+
+ gss_ctx_id_t ctx;
+ krb5_context kctx;
+ OM_uint32 maj_stat, min_stat;
+ gss_name_t name;
+ krb5_principal princ;
+ int ret, success;
+ krb5_data *c1, *c2, *realm;
+ gss_buffer_desc gss_str;
+ kadm5_server_handle_t handle;
+ size_t slen;
+ char *sdots;
+
+ success = 0;
+ handle = (kadm5_server_handle_t)global_server_handle;
+
+ if (rqstp->rq_cred.oa_flavor != RPCSEC_GSS)
+ return 0;
+
+ ctx = rqstp->rq_svccred;
+
+ maj_stat = gss_inquire_context(&min_stat, ctx, NULL, &name,
+ NULL, NULL, NULL, NULL, NULL);
+ if (maj_stat != GSS_S_COMPLETE) {
+ krb5_klog_syslog(LOG_ERR, "check_rpcsec_auth: "
+ "failed inquire_context, stat=%u", maj_stat);
+ log_badauth(maj_stat, min_stat,
+ &rqstp->rq_xprt->xp_raddr, NULL);
+ goto fail_name;
+ }
+
+ kctx = handle->context;
+ ret = gss_to_krb5_name_1(rqstp, kctx, name, &princ, &gss_str);
+ if (ret == 0)
+ goto fail_name;
+
+ slen = gss_str.length;
+ trunc_name(&slen, &sdots);
+ /*
+ * Since we accept with GSS_C_NO_NAME, the client can authenticate
+ * against the entire kdb. Therefore, ensure that the service
+ * name is something reasonable.
+ */
+ if (krb5_princ_size(kctx, princ) != 2)
+ goto fail_princ;
+
+ c1 = krb5_princ_component(kctx, princ, 0);
+ c2 = krb5_princ_component(kctx, princ, 1);
+ realm = krb5_princ_realm(kctx, princ);
+ if (strncmp(handle->params.realm, realm->data, realm->length) == 0
+ && strncmp("kiprop", c1->data, c1->length) == 0) {
+ success = 1;
+ }
+
+fail_princ:
+ if (!success) {
+ krb5_klog_syslog(LOG_ERR, "bad service principal %.*s%s",
+ (int) slen, (char *) gss_str.value, sdots);
+ }
+ gss_release_buffer(&min_stat, &gss_str);
+ krb5_free_principal(kctx, princ);
+fail_name:
+ gss_release_name(&min_stat, &name);
+ return success;
+}
+
+void
+krb5_iprop_prog_1(struct svc_req *rqstp,
+ register SVCXPRT *transp)
+{
+ union {
+ kdb_last_t iprop_get_updates_1_arg;
+ } argument;
+ char *result;
+ bool_t (*_xdr_argument)(), (*_xdr_result)();
+ char *(*local)(/* union XXX *, struct svc_req * */);
+ char *whoami = "krb5_iprop_prog_1";
+
+ if (!check_iprop_rpcsec_auth(rqstp)) {
+ krb5_klog_syslog(LOG_ERR,
+ "authentication attempt failed: %s, RPC authentication flavor %d",
+ inet_ntoa(rqstp->rq_xprt->xp_raddr.sin_addr),
+ rqstp->rq_cred.oa_flavor);
+ svcerr_weakauth(transp);
+ return;
+ }
+
+ switch (rqstp->rq_proc) {
+ case NULLPROC:
+ (void) svc_sendreply(transp, xdr_void,
+ (char *)NULL);
+ return;
+
+ case IPROP_GET_UPDATES:
+ _xdr_argument = xdr_kdb_last_t;
+ _xdr_result = xdr_kdb_incr_result_t;
+ local = (char *(*)()) iprop_get_updates_1_svc;
+ break;
+
+ case IPROP_FULL_RESYNC:
+ _xdr_argument = xdr_void;
+ _xdr_result = xdr_kdb_fullresync_result_t;
+ local = (char *(*)()) iprop_full_resync_1_svc;
+ break;
+
+ default:
+ krb5_klog_syslog(LOG_ERR,
+ _("RPC unknown request: %d (%s)"),
+ rqstp->rq_proc, whoami);
+ svcerr_noproc(transp);
+ return;
+ }
+ (void) memset((char *)&argument, 0, sizeof (argument));
+ if (!svc_getargs(transp, _xdr_argument, (caddr_t)&argument)) {
+ krb5_klog_syslog(LOG_ERR,
+ _("RPC svc_getargs failed (%s)"),
+ whoami);
+ svcerr_decode(transp);
+ return;
+ }
+ result = (*local)(&argument, rqstp);
+
+ if (_xdr_result && result != NULL &&
+ !svc_sendreply(transp, _xdr_result, result)) {
+ krb5_klog_syslog(LOG_ERR,
+ _("RPC svc_sendreply failed (%s)"),
+ whoami);
+ svcerr_systemerr(transp);
+ }
+ if (!svc_freeargs(transp, _xdr_argument, (caddr_t)&argument)) {
+ krb5_klog_syslog(LOG_ERR,
+ _("RPC svc_freeargs failed (%s)"),
+ whoami);
+
+ exit(1);
+ }
+
+ if (rqstp->rq_proc == IPROP_GET_UPDATES) {
+ /* LINTED */
+ kdb_incr_result_t *r = (kdb_incr_result_t *)result;
+
+ if (r->ret == UPDATE_OK) {
+ ulog_free_entries(r->updates.kdb_ulog_t_val,
+ r->updates.kdb_ulog_t_len);
+ r->updates.kdb_ulog_t_val = NULL;
+ r->updates.kdb_ulog_t_len = 0;
+ }
+ }
+
+}
+
+#if 0
+/*
+ * Get the host base service name for the kiprop principal. Returns
+ * KADM5_OK on success. Caller must free the storage allocated for
+ * host_service_name.
+ */
+kadm5_ret_t
+kiprop_get_adm_host_srv_name(krb5_context context,
+ const char *realm,
+ char **host_service_name)
+{
+ kadm5_ret_t ret;
+ char *name;
+ char *host;
+
+ if (ret = kadm5_get_master(context, realm, &host))
+ return (ret);
+
+ name = malloc(strlen(KIPROP_SVC_NAME)+ strlen(host) + 2);
+ if (name == NULL) {
+ free(host);
+ return (ENOMEM);
+ }
+ (void) sprintf(name, "%s@%s", KIPROP_SVC_NAME, host);
+ free(host);
+ *host_service_name = name;
+
+ return (KADM5_OK);
+}
+#endif
diff --git a/src/kadmin/server/kadm_rpc_svc.c b/src/kadmin/server/kadm_rpc_svc.c
index 733602052..c56eedd0b 100644
--- a/src/kadmin/server/kadm_rpc_svc.c
+++ b/src/kadmin/server/kadm_rpc_svc.c
@@ -24,7 +24,6 @@
extern void *global_server_handle;
static int check_rpcsec_auth(struct svc_req *);
-static int gss_to_krb5_name(struct svc_req *, krb5_context, gss_name_t, krb5_principal *, gss_buffer_t);
void log_badauth(OM_uint32 major, OM_uint32 minor,
struct sockaddr_in *addr, char *data);
@@ -272,7 +271,7 @@ check_rpcsec_auth(struct svc_req *rqstp)
}
kctx = handle->context;
- ret = gss_to_krb5_name(rqstp, kctx, name, &princ, &gss_str);
+ ret = gss_to_krb5_name_1(rqstp, kctx, name, &princ, &gss_str);
if (ret == 0)
goto fail_name;
@@ -310,9 +309,9 @@ fail_name:
return success;
}
-static int
-gss_to_krb5_name(struct svc_req *rqstp, krb5_context ctx, gss_name_t gss_name,
- krb5_principal *princ, gss_buffer_t gss_str)
+int
+gss_to_krb5_name_1(struct svc_req *rqstp, krb5_context ctx, gss_name_t gss_name,
+ krb5_principal *princ, gss_buffer_t gss_str)
{
OM_uint32 status, minor_stat;
gss_OID gss_type;
diff --git a/src/kadmin/server/kadmind.M b/src/kadmin/server/kadmind.M
index ad810e6f2..2a227fb4e 100644
--- a/src/kadmin/server/kadmind.M
+++ b/src/kadmin/server/kadmind.M
@@ -54,6 +54,21 @@ section below.
.PP
After the server begins running, it puts itself in the background and
disassociates itself from its controlling terminal.
+.PP
+kadmind can be configured for incremental database propagation.
+Incremental propagation allows slave KDC servers to receive principal
+and policy updates incrementally instead of receiving full dumps of
+the database. This facility can be enabled in the
+.I kdc.conf
+file with the
+.I iprop_enable
+option. See the
+.I kdc.conf
+documentation for other options for tuning incremental propagation
+parameters. Incremental propagation requires the principal
+"kiprop/MASTER@REALM" (where MASTER is the master KDC's canonical host
+name, and REALM the realm name) to be registered in the database.
+
.SH OPTIONS
.TP
\fB\-x\fP \fIdb_args\fP
@@ -199,6 +214,10 @@ character is lower-case, then the operation is permitted.
[Dis]allows the listing of principals or policies in the database.
.sp -1v
.TP
+.B p
+[Dis]allows the propagation of the principal database.
+.sp -1v
+.TP
.B x
Short for
.IR admcil .
diff --git a/src/kadmin/server/misc.h b/src/kadmin/server/misc.h
index a020874fd..74d703c4a 100644
--- a/src/kadmin/server/misc.h
+++ b/src/kadmin/server/misc.h
@@ -47,3 +47,7 @@ void kadm_1(struct svc_req *, SVCXPRT *);
#endif
void trunc_name(size_t *len, char **dots);
+
+int
+gss_to_krb5_name_1(struct svc_req *rqstp, krb5_context ctx, gss_name_t gss_name,
+ krb5_principal *princ, gss_buffer_t gss_str);
diff --git a/src/kadmin/server/ovsec_kadmd.c b/src/kadmin/server/ovsec_kadmd.c
index 659ebfba3..88a60e026 100644
--- a/src/kadmin/server/ovsec_kadmd.c
+++ b/src/kadmin/server/ovsec_kadmd.c
@@ -53,6 +53,7 @@
#include "kdb_kt.h" /* for krb5_ktkdb_set_context */
#include <string.h>
#include "kadm5/server_internal.h" /* XXX for kadm5_server_handle_t */
+#include <kdb_log.h>
#include "misc.h"
@@ -71,7 +72,7 @@ extern int daemon(int, int);
volatile int signal_request_exit = 0;
volatile int signal_request_hup = 0;
-void setup_signal_handlers(void);
+void setup_signal_handlers(iprop_role iproprole);
void request_exit(int);
void request_hup(int);
void reset_db(void);
@@ -114,10 +115,18 @@ void log_badauth_display_status_1(char *m, OM_uint32 code, int type,
int schpw;
void do_schpw(int s, kadm5_config_params *params);
+int ipropfd;
+
#ifdef USE_PASSWORD_SERVER
void kadm5_set_use_password_server (void);
#endif
+extern void krb5_iprop_prog_1();
+extern kadm5_ret_t kiprop_get_adm_host_srv_name(
+ krb5_context,
+ const char *,
+ char **);
+
/*
* Function: usage
*
@@ -199,12 +208,14 @@ static krb5_context context;
static krb5_context hctx;
+int nofork = 0;
+
int main(int argc, char *argv[])
{
- register SVCXPRT *transp;
+ register SVCXPRT *transp, *iproptransp;
extern char *optarg;
extern int optind, opterr;
- int ret, nofork, oldnames = 0;
+ int ret, oldnames = 0;
OM_uint32 OMret, major_status, minor_status;
char *whoami;
gss_buffer_desc in_buf;
@@ -218,6 +229,9 @@ int main(int argc, char *argv[])
int db_args_size = 0;
char *errmsg;
+ char *kiprop_name = NULL; /* iprop svc name */
+ kdb_log_context *log_ctx;
+
setvbuf(stderr, NULL, _IONBF, 0);
/* This is OID value the Krb5_Name NameType */
@@ -316,11 +330,6 @@ int main(int argc, char *argv[])
exit(1);
}
- if( db_args )
- {
- free(db_args), db_args=NULL;
- }
-
if ((ret = kadm5_get_config_params(context, 1, &params,
&params))) {
const char *e_txt = krb5_get_error_message (context, ret);
@@ -353,19 +362,23 @@ int main(int argc, char *argv[])
addr.sin_port = htons(params.kadmind_port);
if ((s = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
- const char *e_txt = krb5_get_error_message (context, ret);
- krb5_klog_syslog(LOG_ERR, "Cannot create TCP socket: %s",
- e_txt);
- fprintf(stderr, "Cannot create TCP socket: %s",
- e_txt);
- kadm5_destroy(global_server_handle);
- krb5_klog_close(context);
- exit(1);
+ const char *e_txt;
+ ret = SOCKET_ERRNO;
+ e_txt = krb5_get_error_message (context, ret);
+ krb5_klog_syslog(LOG_ERR, "Cannot create TCP socket: %s",
+ e_txt);
+ fprintf(stderr, "Cannot create TCP socket: %s",
+ e_txt);
+ kadm5_destroy(global_server_handle);
+ krb5_klog_close(context);
+ exit(1);
}
set_cloexec_fd(s);
if ((schpw = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
- const char *e_txt = krb5_get_error_message (context, ret);
+ const char *e_txt;
+ ret = SOCKET_ERRNO;
+ e_txt = krb5_get_error_message (context, ret);
krb5_klog_syslog(LOG_ERR,
"cannot create simple chpw socket: %s",
e_txt);
@@ -377,6 +390,21 @@ int main(int argc, char *argv[])
}
set_cloexec_fd(schpw);
+ if ((ipropfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
+ const char *e_txt;
+ ret = SOCKET_ERRNO;
+ e_txt = krb5_get_error_message (context, ret);
+ krb5_klog_syslog(LOG_ERR,
+ "cannot create iprop listening socket: %s",
+ e_txt);
+ fprintf(stderr, "cannot create iprop listening socket: %s",
+ e_txt);
+ kadm5_destroy(global_server_handle);
+ krb5_klog_close(context);
+ exit(1);
+ }
+ set_cloexec_fd(ipropfd);
+
#ifdef SO_REUSEADDR
/* the old admin server turned on SO_REUSEADDR for non-default
port numbers. this was necessary, on solaris, for the tests
@@ -390,12 +418,16 @@ int main(int argc, char *argv[])
int allowed;
allowed = 1;
- if (setsockopt(s,
- SOL_SOCKET,
- SO_REUSEADDR,
- (char *) &allowed,
- sizeof(allowed)) < 0) {
- const char *e_txt = krb5_get_error_message (context, ret);
+ if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR,
+ (char *) &allowed, sizeof(allowed)) < 0 ||
+ setsockopt(schpw, SOL_SOCKET, SO_REUSEADDR,
+ (char *) &allowed, sizeof(allowed)) < 0 ||
+ setsockopt(ipropfd, SOL_SOCKET, SO_REUSEADDR,
+ (char *) &allowed, sizeof(allowed)) < 0
+ ) {
+ const char *e_txt;
+ ret = SOCKET_ERRNO;
+ e_txt = krb5_get_error_message (context, ret);
krb5_klog_syslog(LOG_ERR, "Cannot set SO_REUSEADDR: %s",
e_txt);
fprintf(stderr, "Cannot set SO_REUSEADDR: %s", e_txt);
@@ -403,19 +435,6 @@ int main(int argc, char *argv[])
krb5_klog_close(context);
exit(1);
}
- if (setsockopt(schpw, SOL_SOCKET, SO_REUSEADDR,
- (char *) &allowed, sizeof(allowed)) < 0) {
- const char *e_txt = krb5_get_error_message (context, ret);
- krb5_klog_syslog(LOG_ERR,
- "cannot set SO_REUSEADDR on simple chpw socket: %s",
- e_txt);
- fprintf(stderr,
- "Cannot set SO_REUSEADDR on simple chpw socket: %s",
- e_txt);
- kadm5_destroy(global_server_handle);
- krb5_klog_close(context);
- }
-
}
#endif /* SO_REUSEADDR */
memset(&addr, 0, sizeof(addr));
@@ -456,6 +475,7 @@ int main(int argc, char *argv[])
krb5_klog_close(context);
exit(1);
}
+
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = INADDR_ANY;
@@ -491,7 +511,41 @@ int main(int argc, char *argv[])
krb5_klog_close(context);
exit(1);
}
-
+
+ memset(&addr, 0, sizeof(addr));
+ addr.sin_family = AF_INET;
+ addr.sin_addr.s_addr = INADDR_ANY;
+ addr.sin_port = htons(params.iprop_port);
+ if (bind(ipropfd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
+ char portbuf[32];
+ int oerrno = errno;
+ const char *e_txt = krb5_get_error_message (context, errno);
+ fprintf(stderr, "%s: Cannot bind socket.\n", whoami);
+ fprintf(stderr, "bind: %s\n", e_txt);
+ errno = oerrno;
+ snprintf(portbuf, sizeof(portbuf), "%d", ntohs(addr.sin_port));
+ krb5_klog_syslog(LOG_ERR, "cannot bind iprop socket: %s",
+ e_txt);
+ if(oerrno == EADDRINUSE) {
+ char *w = strrchr(whoami, '/');
+ if (w) {
+ w++;
+ }
+ else {
+ w = whoami;
+ }
+ fprintf(stderr,
+"This probably means that another %s process is already\n"
+"running, or that another program is using the server port (number %d).\n"
+"If another %s is already running, you should kill it before\n"
+"restarting the server.\n",
+ w, ntohs(addr.sin_port), w);
+ }
+ kadm5_destroy(global_server_handle);
+ krb5_klog_close(context);
+ exit(1);
+ }
+
transp = svctcp_create(s, 0, 0);
if(transp == NULL) {
fprintf(stderr, "%s: Cannot create RPC service.\n", whoami);
@@ -508,6 +562,25 @@ int main(int argc, char *argv[])
exit(1);
}
+ iproptransp = svctcp_create(ipropfd, 0, 0);
+ if (iproptransp == NULL) {
+ fprintf(stderr, "%s: Cannot create RPC service.\n", whoami);
+ krb5_klog_syslog(LOG_ERR, "Cannot create RPC service: %m");
+ kadm5_destroy(global_server_handle);
+ krb5_klog_close(context);
+ exit(1);
+ }
+ if (!svc_register(iproptransp, KRB5_IPROP_PROG, KRB5_IPROP_VERS, krb5_iprop_prog_1, IPPROTO_TCP)) {
+ fprintf(stderr, "%s: Cannot register RPC service.\n", whoami);
+ krb5_klog_syslog(LOG_ERR, "Cannot register RPC service, continuing.");
+#if 0
+ kadm5_destroy(global_server_handle);
+ krb5_klog_close(context);
+ exit(1);
+#endif
+ }
+
+
names[0].name = build_princ_name(KADM5_ADMIN_SERVICE, params.realm);
names[1].name = build_princ_name(KADM5_CHANGEPW_SERVICE, params.realm);
names[2].name = build_princ_name(OVSEC_KADM_ADMIN_SERVICE, params.realm);
@@ -643,8 +716,108 @@ kterr:
exit(1);
}
- setup_signal_handlers();
- krb5_klog_syslog(LOG_INFO, "starting");
+ if (params.iprop_enabled == TRUE)
+ ulog_set_role(hctx, IPROP_MASTER);
+ else
+ ulog_set_role(hctx, IPROP_NULL);
+
+ log_ctx = hctx->kdblog_context;
+
+ if (log_ctx && (log_ctx->iproprole == IPROP_MASTER)) {
+ /*
+ * IProp is enabled, so let's map in the update log
+ * and setup the service.
+ */
+ if ((ret = ulog_map(hctx, params.iprop_logfile,
+ params.iprop_ulogsize, FKADMIND, db_args)) != 0) {
+ fprintf(stderr,
+ _("%s: %s while mapping update log (`%s.ulog')\n"),
+ whoami, error_message(ret), params.dbname);
+ krb5_klog_syslog(LOG_ERR,
+ _("%s while mapping update log (`%s.ulog')"),
+ error_message(ret), params.dbname);
+ krb5_klog_close(context);
+ exit(1);
+ }
+
+
+ if (nofork)
+ fprintf(stderr,
+ "%s: create IPROP svc (PROG=%d, VERS=%d)\n",
+ whoami, KRB5_IPROP_PROG, KRB5_IPROP_VERS);
+
+#if 0
+ if (!svc_create(krb5_iprop_prog_1,
+ KRB5_IPROP_PROG, KRB5_IPROP_VERS,
+ "circuit_v")) {
+ fprintf(stderr,
+ _("%s: Cannot create IProp RPC service (PROG=%d, VERS=%d)\n"),
+ whoami,
+ KRB5_IPROP_PROG, KRB5_IPROP_VERS);
+ krb5_klog_syslog(LOG_ERR,
+ _("Cannot create IProp RPC service (PROG=%d, VERS=%d), failing."),
+ KRB5_IPROP_PROG, KRB5_IPROP_VERS);
+ krb5_klog_close(context);
+ exit(1);
+ }
+#endif
+
+#if 0 /* authgss only? */
+ if ((ret = kiprop_get_adm_host_srv_name(context,
+ params.realm,
+ &kiprop_name)) != 0) {
+ krb5_klog_syslog(LOG_ERR,
+ _("%s while getting IProp svc name, failing"),
+ error_message(ret));
+ fprintf(stderr,
+ _("%s: %s while getting IProp svc name, failing\n"),
+ whoami, error_message(ret));
+ krb5_klog_close(context);
+ exit(1);
+ }
+
+ auth_gssapi_name iprop_name;
+ iprop_name.name = build_princ_name(foo, bar);
+ if (iprop_name.name == NULL) {
+ foo error;
+ }
+ iprop_name.type = nt_krb5_name_oid;
+ if (svcauth_gssapi_set_names(&iprop_name, 1) == FALSE) {
+ foo error;
+ }
+ if (!rpc_gss_set_svc_name(kiprop_name, "kerberos_v5", 0,
+ KRB5_IPROP_PROG, KRB5_IPROP_VERS)) {
+ rpc_gss_error_t err;
+ (void) rpc_gss_get_error(&err);
+
+ krb5_klog_syslog(LOG_ERR,
+ _("Unable to set RPCSEC_GSS service name (`%s'), failing."),
+ kiprop_name ? kiprop_name : "<null>");
+
+ fprintf(stderr,
+ _("%s: Unable to set RPCSEC_GSS service name (`%s'), failing.\n"),
+ whoami,
+ kiprop_name ? kiprop_name : "<null>");
+
+ if (nofork) {
+ fprintf(stderr,
+ "%s: set svc name (rpcsec err=%d, sys err=%d)\n",
+ whoami,
+ err.rpc_gss_error,
+ err.system_error);
+ }
+
+ exit(1);
+ }
+ free(kiprop_name);
+#endif
+ }
+
+ setup_signal_handlers(log_ctx->iproprole);
+ krb5_klog_syslog(LOG_INFO, _("starting"));
+ if (nofork)
+ fprintf(stderr, "%s: starting...\n", whoami);
+
kadm_svc_run(&params);
krb5_klog_syslog(LOG_INFO, "finished, exiting");
@@ -677,7 +850,7 @@ kterr:
* if possible, otherwise with System V's signal().
*/
-void setup_signal_handlers(void) {
+void setup_signal_handlers(iprop_role iproprole) {
#ifdef POSIX_SIGNALS
(void) sigemptyset(&s_action.sa_mask);
s_action.sa_handler = request_exit;
@@ -694,6 +867,15 @@ void setup_signal_handlers(void) {
s_action.sa_handler = request_pure_clear;
(void) sigaction(SIGUSR2, &s_action, (struct sigaction *) NULL);
#endif /* PURIFY */
+
+ /*
+ * IProp will fork for a full-resync, we don't want to
+ * wait on it and we don't want the living dead procs either.
+ */
+ if (iproprole == IPROP_MASTER) {
+ s_action.sa_handler = SIG_IGN;
+ (void) sigaction(SIGCHLD, &s_action, (struct sigaction *) NULL);
+ }
#else /* POSIX_SIGNALS */
signal(SIGINT, request_exit);
signal(SIGTERM, request_exit);
@@ -704,6 +886,13 @@ void setup_signal_handlers(void) {
signal(SIGUSR1, request_pure_report);
signal(SIGUSR2, request_pure_clear);
#endif /* PURIFY */
+
+ /*
+ * IProp will fork for a full-resync, we don't want to
+ * wait on it and we don't want the living dead procs either.
+ */
+ if (iproprole == IPROP_MASTER)
+ (void) signal(SIGCHLD, SIG_IGN);
#endif /* POSIX_SIGNALS */
}
diff --git a/src/kadmin/server/server_stubs.c b/src/kadmin/server/server_stubs.c
index 5e50971f1..cf93e8667 100644
--- a/src/kadmin/server/server_stubs.c
+++ b/src/kadmin/server/server_stubs.c
@@ -37,7 +37,7 @@ static int gss_name_to_string(gss_name_t gss_name, gss_buffer_desc *str);
static gss_name_t acceptor_name(gss_ctx_id_t context);
-static gss_name_t rqst2name(struct svc_req *rqstp);
+gss_name_t rqst2name(struct svc_req *rqstp);
static int cmp_gss_names(gss_name_t n1, gss_name_t n2)
{
@@ -158,7 +158,6 @@ static void free_server_handle(kadm5_server_handle_t handle)
* server_name, both of which must be freed by the caller. Returns 0
* on success and -1 on failure.
*/
-static
int setup_gss_names(struct svc_req *rqstp,
gss_buffer_desc *client_name,
gss_buffer_desc *server_name)
@@ -1657,7 +1656,7 @@ exit_func:
return(&ret);
}
-static gss_name_t
+gss_name_t
rqst2name(struct svc_req *rqstp)
{