1 /* -*- mode: c; c-file-style: "bsd"; indent-tabs-mode: t -*- */
3 * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
4 * Use is subject to license terms.
7 /* #pragma ident "@(#)ipropd_svc.c 1.2 04/02/20 SMI" */
11 #include <stdlib.h> /* getenv, exit */
13 #include <sys/types.h>
14 #include <sys/resource.h> /* rlimit */
17 #include "k5-platform.h"
18 #include <kadm5/admin.h>
19 #include <kadm5/kadm_rpc.h>
20 #include <kadm5/server_internal.h>
21 #include <server_acl.h>
22 #include <adm_proto.h>
24 #include <gssapi_krb5.h>
25 #include <sys/socket.h>
26 #include <netinet/in.h>
27 #include <arpa/inet.h>
33 extern gss_name_t rqst2name(struct svc_req *rqstp);
35 extern void *global_server_handle;
40 /* Result is stored in a static buffer and is invalidated by the next call. */
41 static const char *client_addr(struct svc_req *svc) {
42 strlcpy(abuf, inet_ntoa(svc->rq_xprt->xp_raddr.sin_addr), sizeof(abuf));
46 static char *reply_ok_str = "UPDATE_OK";
47 static char *reply_err_str = "UPDATE_ERROR";
48 static char *reply_fr_str = "UPDATE_FULL_RESYNC_NEEDED";
49 static char *reply_busy_str = "UPDATE_BUSY";
50 static char *reply_nil_str = "UPDATE_NIL";
51 static char *reply_perm_str = "UPDATE_PERM_DENIED";
52 static char *reply_unknown_str = "<UNKNOWN_CODE>";
54 #define LOG_UNAUTH _("Unauthorized request: %s, client=%s, service=%s, addr=%s")
55 #define LOG_DONE _("Request: %s, %s, %s, client=%s, service=%s, addr=%s")
60 #define DPRINT(i) if (nofork) printf i
64 debprret(char *w, update_status_t ret, kdb_sno_t sno)
68 printf("%s: end (OK, sno=%u)\n",
72 printf("%s: end (ERROR)\n", w);
74 case UPDATE_FULL_RESYNC_NEEDED:
75 printf("%s: end (FR NEEDED)\n", w);
78 printf("%s: end (BUSY)\n", w);
81 printf("%s: end (NIL)\n", w);
83 case UPDATE_PERM_DENIED:
84 printf("%s: end (PERM)\n", w);
87 printf("%s: end (UNKNOWN return code (%d))\n", w, ret);
92 replystr(update_status_t ret)
96 return (reply_ok_str);
98 return (reply_err_str);
99 case UPDATE_FULL_RESYNC_NEEDED:
100 return (reply_fr_str);
102 return (reply_busy_str);
104 return (reply_nil_str);
105 case UPDATE_PERM_DENIED:
106 return (reply_perm_str);
108 return (reply_unknown_str);
112 /* Returns null on allocation failure.
113 Regardless of success or failure, frees the input buffer. */
115 buf_to_string(gss_buffer_desc *b)
118 char *s = malloc(b->length+1);
121 memcpy(s, b->value, b->length);
124 (void) gss_release_buffer(&min_stat, b);
129 iprop_get_updates_1_svc(kdb_last_t *arg, struct svc_req *rqstp)
131 static kdb_incr_result_t ret;
132 char *whoami = "iprop_get_updates_1";
134 kadm5_server_handle_t handle = global_server_handle;
135 char *client_name = 0, *service_name = 0;
136 char obuf[256] = {0};
138 /* default return code */
139 ret.ret = UPDATE_ERROR;
141 DPRINT(("%s: start, last_sno=%lu\n", whoami,
142 (unsigned long) arg->last_sno));
145 krb5_klog_syslog(LOG_ERR,
146 _("%s: server handle is NULL"),
152 gss_buffer_desc client_desc, service_desc;
154 if (setup_gss_names(rqstp, &client_desc, &service_desc) < 0) {
155 krb5_klog_syslog(LOG_ERR,
156 _("%s: setup_gss_names failed"),
160 client_name = buf_to_string(&client_desc);
161 service_name = buf_to_string(&service_desc);
162 if (client_name == NULL || service_name == NULL) {
165 krb5_klog_syslog(LOG_ERR,
166 _("%s: out of memory recording principal names"),
172 DPRINT(("%s: clprinc=`%s'\n\tsvcprinc=`%s'\n",
173 whoami, client_name, service_name));
175 if (!kadm5int_acl_check(handle->context,
180 ret.ret = UPDATE_PERM_DENIED;
182 krb5_klog_syslog(LOG_NOTICE, LOG_UNAUTH, whoami,
183 client_name, service_name,
188 kret = ulog_get_entries(handle->context, *arg, &ret);
190 if (ret.ret == UPDATE_OK) {
191 (void) snprintf(obuf, sizeof (obuf),
192 _("%s; Incoming SerialNo=%lu; Outgoing SerialNo=%lu"),
194 (unsigned long)arg->last_sno,
195 (unsigned long)ret.lastentry.last_sno);
197 (void) snprintf(obuf, sizeof (obuf),
198 _("%s; Incoming SerialNo=%lu; Outgoing SerialNo=N/A"),
200 (unsigned long)arg->last_sno);
203 krb5_klog_syslog(LOG_NOTICE,
204 _("Request: %s, %s, %s, client=%s, service=%s, addr=%s"),
207 ((kret == 0) ? "success" : error_message(kret)),
208 client_name, service_name,
213 debprret(whoami, ret.ret, ret.lastentry.last_sno);
221 * Given a client princ (foo/fqdn@R), copy (in arg cl) the fqdn substring.
222 * Return arg cl str ptr on success, else NULL.
225 getclhoststr(char *clprinc, char *cl, size_t len)
228 if ((s = strchr(clprinc, '/')) != NULL) {
232 if (strlcpy(cl, s, len) >= len)
234 /* XXX Copy with @REALM first, with bounds check, then
235 chop off the realm?? */
236 if ((s = strchr(cl, '@')) != NULL) {
238 return (cl); /* success */
245 static kdb_fullresync_result_t *
246 ipropx_resync(uint32_t vers, struct svc_req *rqstp)
248 static kdb_fullresync_result_t ret;
251 char clhost[MAXHOSTNAMELEN] = {0};
253 kadm5_server_handle_t handle = global_server_handle;
255 gss_name_t name = NULL;
256 char *client_name = NULL, *service_name = NULL;
257 char *whoami = "iprop_full_resync_1";
260 * vers contains the highest version number the client is
261 * willing to accept. A client can always accept a lower
262 * version: the version number is indicated in the dump
266 /* default return code */
267 ret.ret = UPDATE_ERROR;
270 krb5_klog_syslog(LOG_ERR,
271 _("%s: server handle is NULL"),
276 DPRINT(("%s: start\n", whoami));
279 gss_buffer_desc client_desc, service_desc;
281 if (setup_gss_names(rqstp, &client_desc, &service_desc) < 0) {
282 krb5_klog_syslog(LOG_ERR,
283 _("%s: setup_gss_names failed"),
287 client_name = buf_to_string(&client_desc);
288 service_name = buf_to_string(&service_desc);
289 if (client_name == NULL || service_name == NULL) {
292 krb5_klog_syslog(LOG_ERR,
293 _("%s: out of memory recording principal names"),
299 DPRINT(("%s: clprinc=`%s'\n\tsvcprinc=`%s'\n",
300 whoami, client_name, service_name));
302 if (!kadm5int_acl_check(handle->context,
307 ret.ret = UPDATE_PERM_DENIED;
309 krb5_klog_syslog(LOG_NOTICE, LOG_UNAUTH, whoami,
310 client_name, service_name,
315 if (!getclhoststr(client_name, clhost, sizeof (clhost))) {
316 krb5_klog_syslog(LOG_ERR,
317 _("%s: getclhoststr failed"),
323 * construct db dump file name; kprop style name + clnt fqdn
325 if (asprintf(&tmpf, "%s_%s", KPROP_DEFAULT_FILE, clhost) < 0) {
326 krb5_klog_syslog(LOG_ERR,
327 _("%s: unable to construct db dump file name; out of memory"),
333 * note the -i; modified version of kdb5_util dump format
334 * to include sno (serial number). This argument is now
335 * versioned (-i0 for legacy dump format, -i1 for ipropx
336 * version 1 format, etc)
338 if (asprintf(&ubuf, "%s dump -i%d %s </dev/null 2>&1",
339 KPROPD_DEFAULT_KDB5_UTIL, vers, tmpf) < 0) {
340 krb5_klog_syslog(LOG_ERR,
341 _("%s: cannot construct kdb5 util dump string too long; out of memory"),
347 * Fork to dump the db and xfer it to the slave.
348 * (the fork allows parent to return quickly and the child
349 * acts like a callback to the slave).
352 DPRINT(("%s: fork=%d (%d)\n", whoami, fret, getpid()));
359 krb5_klog_syslog(LOG_ERR,
360 _("%s: fork failed: %s"),
362 error_message(errno));
366 DPRINT(("%s: run `%s' ...\n", whoami, ubuf));
367 (void) signal(SIGCHLD, SIG_DFL);
368 /* run kdb5_util(1M) dump for IProp */
369 /* XXX popen can return NULL; is pclose(NULL) okay? */
370 pret = pclose(popen(ubuf, "w"));
371 DPRINT(("%s: pclose=%d\n", whoami, pret));
373 /* XXX popen/pclose may not set errno
374 properly, and the error could be from the
375 subprocess anyways. */
379 krb5_klog_syslog(LOG_ERR,
380 _("%s: pclose(popen) failed: %s"),
382 error_message(errno));
386 DPRINT(("%s: exec `kprop -f %s %s' ...\n",
387 whoami, tmpf, clhost));
389 if (getenv("KPROP_PORT"))
390 pret = execl(KPROPD_DEFAULT_KPROP, "kprop", "-f", tmpf,
391 "-P", getenv("KPROP_PORT"),
394 pret = execl(KPROPD_DEFAULT_KPROP, "kprop", "-f", tmpf,
400 krb5_klog_syslog(LOG_ERR,
401 _("%s: exec failed: %s"),
403 error_message(errno));
407 default: /* parent */
409 /* not used by slave (sno is retrieved from kdb5_util dump) */
410 ret.lastentry.last_sno = 0;
411 ret.lastentry.last_time.seconds = 0;
412 ret.lastentry.last_time.useconds = 0;
414 krb5_klog_syslog(LOG_NOTICE,
415 _("Request: %s, spawned resync process %d, client=%s, service=%s, addr=%s"),
417 client_name, service_name,
425 debprret(whoami, ret.ret, 0);
429 gss_release_name(&min_stat, &name);
435 kdb_fullresync_result_t *
436 iprop_full_resync_1_svc(/* LINTED */ void *argp, struct svc_req *rqstp)
438 return ipropx_resync(IPROPX_VERSION_0, rqstp);
441 kdb_fullresync_result_t *
442 iprop_full_resync_ext_1_svc(uint32_t *argp, struct svc_req *rqstp)
444 return ipropx_resync(*argp, rqstp);
448 check_iprop_rpcsec_auth(struct svc_req *rqstp)
450 /* XXX Since the client can authenticate against any principal in
451 the database, we need to do a sanity check. Only checking for
452 "kiprop" now, but that means theoretically the client could be
453 authenticating to kiprop on some other machine. */
454 /* Code taken from kadm_rpc_svc.c, tweaked. */
458 OM_uint32 maj_stat, min_stat;
460 krb5_principal princ;
462 krb5_data *c1, *c2, *realm;
463 gss_buffer_desc gss_str;
464 kadm5_server_handle_t handle;
469 handle = (kadm5_server_handle_t)global_server_handle;
471 if (rqstp->rq_cred.oa_flavor != RPCSEC_GSS)
474 ctx = rqstp->rq_svccred;
476 maj_stat = gss_inquire_context(&min_stat, ctx, NULL, &name,
477 NULL, NULL, NULL, NULL, NULL);
478 if (maj_stat != GSS_S_COMPLETE) {
479 krb5_klog_syslog(LOG_ERR,
480 _("check_rpcsec_auth: failed inquire_context, "
481 "stat=%u"), maj_stat);
482 log_badauth(maj_stat, min_stat,
483 &rqstp->rq_xprt->xp_raddr, NULL);
487 kctx = handle->context;
488 ret = gss_to_krb5_name_1(rqstp, kctx, name, &princ, &gss_str);
492 slen = gss_str.length;
493 trunc_name(&slen, &sdots);
495 * Since we accept with GSS_C_NO_NAME, the client can authenticate
496 * against the entire kdb. Therefore, ensure that the service
497 * name is something reasonable.
499 if (krb5_princ_size(kctx, princ) != 2)
502 c1 = krb5_princ_component(kctx, princ, 0);
503 c2 = krb5_princ_component(kctx, princ, 1);
504 realm = krb5_princ_realm(kctx, princ);
505 if (strncmp(handle->params.realm, realm->data, realm->length) == 0
506 && strncmp("kiprop", c1->data, c1->length) == 0) {
512 krb5_klog_syslog(LOG_ERR, _("bad service principal %.*s%s"),
513 (int) slen, (char *) gss_str.value, sdots);
515 gss_release_buffer(&min_stat, &gss_str);
516 krb5_free_principal(kctx, princ);
518 gss_release_name(&min_stat, &name);
523 krb5_iprop_prog_1(struct svc_req *rqstp,
524 register SVCXPRT *transp)
527 kdb_last_t iprop_get_updates_1_arg;
530 bool_t (*_xdr_argument)(), (*_xdr_result)();
531 char *(*local)(/* union XXX *, struct svc_req * */);
532 char *whoami = "krb5_iprop_prog_1";
534 if (!check_iprop_rpcsec_auth(rqstp)) {
535 krb5_klog_syslog(LOG_ERR, _("authentication attempt failed: %s, RPC "
536 "authentication flavor %d"),
537 inet_ntoa(rqstp->rq_xprt->xp_raddr.sin_addr),
538 rqstp->rq_cred.oa_flavor);
539 svcerr_weakauth(transp);
543 switch (rqstp->rq_proc) {
545 (void) svc_sendreply(transp, xdr_void,
549 case IPROP_GET_UPDATES:
550 _xdr_argument = xdr_kdb_last_t;
551 _xdr_result = xdr_kdb_incr_result_t;
552 local = (char *(*)()) iprop_get_updates_1_svc;
555 case IPROP_FULL_RESYNC:
556 _xdr_argument = xdr_void;
557 _xdr_result = xdr_kdb_fullresync_result_t;
558 local = (char *(*)()) iprop_full_resync_1_svc;
561 case IPROP_FULL_RESYNC_EXT:
562 _xdr_argument = xdr_u_int32;
563 _xdr_result = xdr_kdb_fullresync_result_t;
564 local = (char *(*)()) iprop_full_resync_ext_1_svc;
568 krb5_klog_syslog(LOG_ERR,
569 _("RPC unknown request: %d (%s)"),
570 rqstp->rq_proc, whoami);
571 svcerr_noproc(transp);
574 (void) memset(&argument, 0, sizeof (argument));
575 if (!svc_getargs(transp, _xdr_argument, (caddr_t)&argument)) {
576 krb5_klog_syslog(LOG_ERR,
577 _("RPC svc_getargs failed (%s)"),
579 svcerr_decode(transp);
582 result = (*local)(&argument, rqstp);
584 if (_xdr_result && result != NULL &&
585 !svc_sendreply(transp, _xdr_result, result)) {
586 krb5_klog_syslog(LOG_ERR,
587 _("RPC svc_sendreply failed (%s)"),
589 svcerr_systemerr(transp);
591 if (!svc_freeargs(transp, _xdr_argument, (caddr_t)&argument)) {
592 krb5_klog_syslog(LOG_ERR,
593 _("RPC svc_freeargs failed (%s)"),
599 if (rqstp->rq_proc == IPROP_GET_UPDATES) {
601 kdb_incr_result_t *r = (kdb_incr_result_t *)result;
603 if (r->ret == UPDATE_OK) {
604 ulog_free_entries(r->updates.kdb_ulog_t_val,
605 r->updates.kdb_ulog_t_len);
606 r->updates.kdb_ulog_t_val = NULL;
607 r->updates.kdb_ulog_t_len = 0;
615 * Get the host base service name for the kiprop principal. Returns
616 * KADM5_OK on success. Caller must free the storage allocated for
620 kiprop_get_adm_host_srv_name(krb5_context context,
622 char **host_service_name)
628 if (ret = kadm5_get_master(context, realm, &host))
631 if (asprintf(&name, "%s@%s", KIPROP_SVC_NAME, host) < 0) {
636 *host_service_name = name;