diff options
| author | Ken Raeburn <raeburn@mit.edu> | 2008-06-24 05:04:29 +0000 |
|---|---|---|
| committer | Ken Raeburn <raeburn@mit.edu> | 2008-06-24 05:04:29 +0000 |
| commit | 5661d1290f74312a405db970aea097da77706f71 (patch) | |
| tree | 0ab69c8078ef3275b99a3ad27f3592b607e43f70 /src/kadmin/server | |
| parent | 6879f371402854465e5276d36e4792938906097f (diff) | |
| download | krb5-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.in | 35 | ||||
| -rw-r--r-- | src/kadmin/server/ipropd_svc.c | 615 | ||||
| -rw-r--r-- | src/kadmin/server/kadm_rpc_svc.c | 9 | ||||
| -rw-r--r-- | src/kadmin/server/kadmind.M | 19 | ||||
| -rw-r--r-- | src/kadmin/server/misc.h | 4 | ||||
| -rw-r--r-- | src/kadmin/server/ovsec_kadmd.c | 269 | ||||
| -rw-r--r-- | src/kadmin/server/server_stubs.c | 5 |
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, ¶ms, ¶ms))) { 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(¶ms); 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) { |
