8322 nl: misleading-indentation
[unleashed/tickless.git] / usr / src / cmd / krb5 / kadmin / server / ovsec_kadmd.c
blobd8f419f0c2add6b37a911e7fcd66fea4d2b00825
1 /*
2 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
3 * Use is subject to license terms.
4 */
7 /*
8 * WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING
10 * Openvision retains the copyright to derivative works of
11 * this source code. Do *NOT* create a derivative of this
12 * source code before consulting with your legal department.
13 * Do *NOT* integrate *ANY* of this source code into another
14 * product before consulting with your legal department.
16 * For further information, read the top-level Openvision
17 * copyright which is contained in the top-level MIT Kerberos
18 * copyright.
20 * WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING
25 * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved
30 * Copyright (C) 1998 by the FundsXpress, INC.
32 * All rights reserved.
34 * Export of this software from the United States of America may require
35 * a specific license from the United States Government. It is the
36 * responsibility of any person or organization contemplating export to
37 * obtain such a license before exporting.
39 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
40 * distribute this software and its documentation for any purpose and
41 * without fee is hereby granted, provided that the above copyright
42 * notice appear in all copies and that both that copyright notice and
43 * this permission notice appear in supporting documentation, and that
44 * the name of FundsXpress. not be used in advertising or publicity pertaining
45 * to distribution of the software without specific, written prior
46 * permission. FundsXpress makes no representations about the suitability of
47 * this software for any purpose. It is provided "as is" without express
48 * or implied warranty.
50 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
51 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
52 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
57 * SUNWresync121 XXX
58 * Beware future resyncers, this file is much diff from MIT (1.0...)
61 #include <stdio.h>
62 #include <stdio_ext.h>
63 #include <signal.h>
64 #include <syslog.h>
65 #include <sys/types.h>
66 #ifdef _AIX
67 #include <sys/select.h>
68 #endif
69 #include <sys/time.h>
70 #include <sys/socket.h>
71 #include <unistd.h>
72 #include <netinet/in.h>
73 #include <arpa/inet.h> /* inet_ntoa */
74 #include <gssapi/gssapi.h>
75 #include "gssapiP_krb5.h" /* for kg_get_context */
76 #include <rpc/rpc.h>
77 #include <kadm5/admin.h>
78 #include <kadm5/kadm_rpc.h>
79 #include <server_acl.h>
80 #include <krb5/adm_proto.h>
81 #include "kdb_kt.h" /* for krb5_ktkdb_set_context */
82 #include <string.h>
83 #include <kadm5/server_internal.h>
84 #include <gssapi_krb5.h>
85 #include <libintl.h>
86 #include <locale.h>
87 #include <sys/resource.h>
88 #include <kdb/kdb_log.h>
89 #include <kdb_kt.h>
91 #include <rpc/rpcsec_gss.h>
92 #include "misc.h"
94 #ifdef PURIFY
95 #include "purify.h"
97 int signal_pure_report = 0;
98 int signal_pure_clear = 0;
99 void request_pure_report(int);
100 void request_pure_clear(int);
101 #endif /* PURIFY */
104 #ifndef FD_SETSIZE
105 #define FD_SETSIZE 256
106 #endif
108 #ifndef MAX
109 #define MAX(a, b) (((a) > (b)) ? (a) : (b))
110 #endif
112 #if defined(NEED_DAEMON_PROTO)
113 extern int daemon(int, int);
114 #endif
116 static int signal_request_exit = 0;
117 kadm5_config_params chgpw_params;
118 void setup_signal_handlers(iprop_role iproprole);
119 void request_exit(int);
120 void sig_pipe(int);
121 void kadm_svc_run(void);
123 #ifdef POSIX_SIGNALS
124 static struct sigaction s_action;
125 #endif /* POSIX_SIGNALS */
128 #define TIMEOUT 15
130 typedef struct _auth_gssapi_name {
131 char *name;
132 gss_OID type;
133 } auth_gssapi_name;
135 gss_name_t gss_changepw_name = NULL, gss_oldchangepw_name = NULL;
136 void *global_server_handle;
139 * This is a kludge, but the server needs these constants to be
140 * compatible with old clients. They are defined in <kadm5/admin.h>,
141 * but only if USE_KADM5_API_VERSION == 1.
143 #define OVSEC_KADM_ADMIN_SERVICE_P "ovsec_adm@admin"
144 #define OVSEC_KADM_CHANGEPW_SERVICE_P "ovsec_adm@changepw"
147 extern void krb5_iprop_prog_1();
148 extern kadm5_ret_t kiprop_get_adm_host_srv_name(
149 krb5_context,
150 const char *,
151 char **);
153 static int schpw;
156 in_port_t l_port = 0; /* global local port num, for BSM audits */
158 int nofork = 0; /* global; don't fork (debug mode) */
162 * Function: usage
164 * Purpose: print out the server usage message
166 * Arguments:
167 * Requires:
168 * Effects:
169 * Modifies:
172 static void usage()
174 fprintf(stderr, gettext("Usage: kadmind [-x db_args]* [-r realm] [-m] [-d] "
175 "[-p port-number]\n"));
176 exit(1);
180 * Function: display_status
182 * Purpose: displays GSS-API messages
184 * Arguments:
186 * msg a string to be displayed with the message
187 * maj_stat the GSS-API major status code
188 * min_stat the GSS-API minor status code
190 * Effects:
192 * The GSS-API messages associated with maj_stat and min_stat are
193 * displayed on stderr, each preceeded by "GSS-API error <msg>: " and
194 * followed by a newline.
196 static void display_status_1(char *, OM_uint32, int);
198 static void display_status(msg, maj_stat, min_stat)
199 char *msg;
200 OM_uint32 maj_stat;
201 OM_uint32 min_stat;
203 display_status_1(msg, maj_stat, GSS_C_GSS_CODE);
204 display_status_1(msg, min_stat, GSS_C_MECH_CODE);
207 static void display_status_1(m, code, type)
208 char *m;
209 OM_uint32 code;
210 int type;
212 OM_uint32 maj_stat, min_stat;
213 gss_buffer_desc msg;
214 OM_uint32 msg_ctx;
216 msg_ctx = 0;
217 while (1) {
218 maj_stat = gss_display_status(&min_stat, code,
219 type, GSS_C_NULL_OID,
220 &msg_ctx, &msg);
221 fprintf(stderr, "GSS-API error %s: %s\n", m,
222 (char *)msg.value);
223 (void) gss_release_buffer(&min_stat, &msg);
225 if (!msg_ctx)
226 break;
232 * Solaris Kerberos: the following prototypes are needed because these are
233 * private interfaces that do not have prototypes in any .h
236 extern struct hostent *res_getipnodebyaddr(const void *, size_t, int, int *);
237 extern void res_freehostent(struct hostent *);
239 static void
240 freedomnames(char **npp)
242 char **tpp;
244 if (npp) {
245 tpp = npp;
246 while (*tpp++) {
247 free(*(tpp-1));
249 free(npp);
254 * Construct a list of uniq FQDNs of all the net interfaces (except
255 * krb5.conf master dups) and return it in arg 'dnames'.
257 * On successful return (0), caller must call freedomnames()
258 * to free memory.
260 static int
261 getdomnames(krb5_context ctx, char *realm, char ***dnames)
263 krb5_address **addresses = NULL;
264 krb5_address *a = NULL;
265 struct hostent *hp = NULL;
266 int ret, i, result=0, error;
267 char **npp = NULL, **tpp=NULL;
268 int dup=0, n = 0;
269 char *cfhost = NULL; /* krb5 conf file master hostname */
271 if (ret = kadm5_get_master(ctx, realm, &cfhost)) {
272 return (ret);
275 ret = krb5_os_localaddr(ctx, &addresses);
276 if (ret != 0) {
277 if (nofork)
278 (void) fprintf(stderr,
279 "kadmind: get localaddrs failed: %s",
280 error_message(ret));
281 result = ret;
282 goto err;
286 for (i=0; addresses[i]; i++) {
287 a = addresses[i];
288 hp = res_getipnodebyaddr(a->contents, a->length,
289 a->addrtype == ADDRTYPE_INET
290 ? AF_INET : AF_INET6,
291 &error);
292 if (hp != NULL) {
294 /* skip master host in krb5.conf */
295 if (strcasecmp(cfhost, hp->h_name) == 0) {
296 res_freehostent(hp);
297 hp = NULL;
298 continue;
301 dup = 0;
302 tpp = npp;
303 /* skip if hostname already exists in list */
304 while (tpp && *tpp++) {
305 if (strcasecmp(*(tpp-1), hp->h_name) == 0) {
306 dup++;
307 break;
311 if (dup) {
312 res_freehostent(hp);
313 hp = NULL;
314 continue;
317 npp = realloc(npp, sizeof(char *) * (n + 2));
318 if (!npp) {
319 result = ENOMEM;
320 goto err;
322 npp[n] = strdup(hp->h_name);
323 if (!npp[n]) {
324 result = ENOMEM;
325 goto err;
327 npp[n+1] = NULL;
328 n++;
330 res_freehostent(hp);
331 hp = NULL;
332 result = 0;
337 #ifdef DEBUG
338 printf("getdomnames: n=%d, i=%d, npp=%p\n", n, i, npp);
339 tpp = npp;
340 while (tpp && *tpp++) {
341 printf("tpp=%s\n", *(tpp-1));
343 #endif
345 goto out;
347 err:
348 if (npp) {
349 freedomnames(npp);
350 npp = NULL;
353 if (hp) {
354 res_freehostent(hp);
355 hp = NULL;
358 out:
359 if (cfhost) {
360 free (cfhost);
361 cfhost = NULL;
363 if (addresses) {
364 krb5_free_addresses(ctx, addresses);
365 addresses = NULL;
368 if (result == 0)
369 *dnames = npp;
371 return (result);
375 * Set the rpcsec_gss svc names for all net interfaces.
377 static void
378 set_svc_domnames(char *svcname, char **dnames,
379 u_int program, u_int version)
381 bool_t ret;
382 char **tpp = dnames;
384 if (!tpp)
385 return;
387 while (*tpp++) {
388 /* MAX_NAME_LEN from rpc/rpcsec_gss.h */
389 char name[MAXHOSTNAMELEN+MAX_NAME_LEN+2] = {0};
390 (void) snprintf(name, sizeof(name), "%s@%s",
391 svcname, *(tpp-1));
392 ret = rpc_gss_set_svc_name(name,
393 "kerberos_v5", 0,
394 program, version);
395 if (nofork && ret)
396 (void) fprintf(stderr,
397 "rpc_gss_set_svc_name success: %s\n",
398 name);
402 /* XXX yuck. the signal handlers need this */
403 static krb5_context context;
405 static krb5_context hctx;
407 int main(int argc, char *argv[])
409 register SVCXPRT *transp;
410 extern char *optarg;
411 extern int optind, opterr;
412 int ret, rlen, oldnames = 0;
413 OM_uint32 OMret, major_status, minor_status;
414 char *whoami;
415 FILE *acl_file;
416 gss_buffer_desc in_buf;
417 struct servent *srv;
418 struct sockaddr_in addr;
419 struct sockaddr_in *sin;
420 int s;
421 auth_gssapi_name names[6];
422 gss_buffer_desc gssbuf;
423 gss_OID nt_krb5_name_oid;
424 int optchar;
425 struct netconfig *nconf;
426 void *handlep;
427 int fd;
428 struct t_info tinfo;
429 struct t_bind tbindstr, *tres;
431 struct t_optmgmt req, resp;
432 struct opthdr *opt;
433 char reqbuf[128];
434 struct rlimit rl;
436 char *kiprop_name = NULL; /* IProp svc name */
437 kdb_log_context *log_ctx;
438 kadm5_server_handle_t handle;
439 krb5_context ctx;
441 kadm5_config_params params;
442 char **db_args = NULL;
443 int db_args_size = 0;
444 const char *errmsg;
445 char **dnames = NULL;
446 int retdn;
447 int iprop_supported;
449 /* Solaris Kerberos: Stores additional error messages */
450 char *emsg = NULL;
452 /* Solaris Kerberos: Indicates whether loalhost is master or not */
453 krb5_boolean is_master;
455 /* Solaris Kerberos: Used for checking acl file */
456 gss_name_t name;
458 /* This is OID value the Krb5_Name NameType */
459 gssbuf.value = "{1 2 840 113554 1 2 2 1}";
460 gssbuf.length = strlen(gssbuf.value);
461 major_status = gss_str_to_oid(&minor_status, &gssbuf, &nt_krb5_name_oid);
462 if (major_status != GSS_S_COMPLETE) {
463 fprintf(stderr,
464 gettext("Couldn't create KRB5 Name NameType OID\n"));
465 display_status("str_to_oid", major_status, minor_status);
466 exit(1);
469 names[0].name = names[1].name = names[2].name = names[3].name = NULL;
470 names[4].name = names[5].name =NULL;
471 names[0].type = names[1].type = names[2].type = names[3].type =
472 (gss_OID) nt_krb5_name_oid;
473 names[4].type = names[5].type = (gss_OID) nt_krb5_name_oid;
475 #ifdef PURIFY
476 purify_start_batch();
477 #endif /* PURIFY */
478 whoami = (strrchr(argv[0], '/') ? strrchr(argv[0], '/')+1 : argv[0]);
480 (void) setlocale(LC_ALL, "");
482 #if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */
483 #define TEXT_DOMAIN "SYS_TEST" /* Use this only if it weren't */
484 #endif
486 (void) textdomain(TEXT_DOMAIN);
488 nofork = 0;
490 memset((char *) &params, 0, sizeof(params));
492 while ((optchar = getopt(argc, argv, "r:mdp:x:")) != EOF) {
493 switch (optchar) {
494 case 'r':
495 if (!optarg)
496 usage();
497 params.realm = optarg;
498 params.mask |= KADM5_CONFIG_REALM;
499 break;
500 case 'm':
501 params.mkey_from_kbd = 1;
502 params.mask |= KADM5_CONFIG_MKEY_FROM_KBD;
503 break;
504 case 'd':
505 nofork = 1;
506 break;
507 case 'p':
508 if (!optarg)
509 usage();
510 params.kadmind_port = atoi(optarg);
511 params.mask |= KADM5_CONFIG_KADMIND_PORT;
512 break;
513 case 'x':
514 if (!optarg)
515 usage();
516 db_args_size++;
518 char **temp = realloc( db_args,
519 sizeof(char*) * (db_args_size+1)); /* one for NULL */
520 if( temp == NULL )
522 fprintf(stderr, gettext("%s: cannot initialize. Not enough memory\n"),
523 whoami);
524 exit(1);
526 db_args = temp;
528 db_args[db_args_size-1] = optarg;
529 db_args[db_args_size] = NULL;
530 break;
531 case '?':
532 default:
533 usage();
538 if (getrlimit(RLIMIT_NOFILE, &rl) == 0) {
539 rl.rlim_cur = rl.rlim_max = MAX(rl.rlim_max, FD_SETSIZE);
540 (void) setrlimit(RLIMIT_NOFILE, &rl);
541 (void) enable_extended_FILE_stdio(-1, -1);
544 if ((ret = kadm5_init_krb5_context(&context))) {
545 fprintf(stderr,
546 gettext("%s: %s while initializing context, aborting\n"),
547 whoami, error_message(ret));
548 exit(1);
551 krb5_klog_init(context, "admin_server", whoami, 1);
553 /* Solaris Kerberos */
554 if((ret = kadm5_init2("kadmind", NULL,
555 NULL, &params,
556 KADM5_STRUCT_VERSION,
557 KADM5_API_VERSION_2,
558 db_args,
559 &global_server_handle,
560 &emsg)) != KADM5_OK) {
561 krb5_klog_syslog(LOG_ERR,
562 gettext("%s while initializing, aborting"),
563 (emsg ? emsg : error_message(ret)));
564 fprintf(stderr,
565 gettext("%s: %s while initializing, aborting\n"),
566 whoami, (emsg ? emsg : error_message(ret)));
567 if (emsg)
568 free(emsg);
569 krb5_klog_close(context);
570 exit(1);
573 if( db_args )
575 free(db_args), db_args=NULL;
578 if ((ret = kadm5_get_config_params(context, 1, &params,
579 &params))) {
580 const char *e_txt = krb5_get_error_message (context, ret);
581 /* Solaris Kerberos: Remove double "whoami" */
582 krb5_klog_syslog(LOG_ERR, gettext("%s while initializing, aborting"),
583 e_txt);
584 fprintf(stderr,
585 gettext("%s: %s while initializing, aborting\n"),
586 whoami, e_txt);
587 kadm5_destroy(global_server_handle);
588 krb5_klog_close(context);
589 exit(1);
592 #define REQUIRED_PARAMS (KADM5_CONFIG_REALM | KADM5_CONFIG_ACL_FILE)
594 if ((params.mask & REQUIRED_PARAMS) != REQUIRED_PARAMS) {
595 /* Solaris Kerberos: Keep error messages consistent */
596 krb5_klog_syslog(LOG_ERR,
597 gettext("Missing required configuration values (%lx)"
598 "while initializing, aborting"),
599 (params.mask & REQUIRED_PARAMS) ^ REQUIRED_PARAMS);
600 fprintf(stderr,
601 gettext("%s: Missing required configuration values "
602 "(%lx) while initializing, aborting\n"), whoami,
603 (params.mask & REQUIRED_PARAMS) ^ REQUIRED_PARAMS);
604 krb5_klog_close(context);
605 kadm5_destroy(global_server_handle);
606 exit(1);
610 * When using the Horowitz/IETF protocol for
611 * password changing, the default port is 464
612 * (officially recognized by IANA)
614 * DEFAULT_KPASSWD_PORT -> 464
616 chgpw_params.kpasswd_port = DEFAULT_KPASSWD_PORT;
617 chgpw_params.mask |= KADM5_CONFIG_KPASSWD_PORT;
618 chgpw_params.kpasswd_protocol = KRB5_CHGPWD_CHANGEPW_V2;
619 chgpw_params.mask |= KADM5_CONFIG_KPASSWD_PROTOCOL;
621 if (ret = kadm5_get_config_params(context, 1, &chgpw_params,
622 &chgpw_params)) {
623 /* Solaris Kerberos: Remove double "whoami" */
624 krb5_klog_syslog(LOG_ERR, gettext("%s while initializing,"
625 " aborting"), error_message(ret));
626 fprintf(stderr,
627 gettext("%s: %s while initializing, aborting\n"),
628 whoami, error_message(ret));
629 krb5_klog_close(context);
630 exit(1);
634 * We now setup the socket and bind() to port 464, so that
635 * kadmind can now listen to and process change-pwd requests
636 * from non-Solaris Kerberos V5 clients such as Microsoft,
637 * MIT, AIX, HP etc
639 if ((schpw = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
640 const char *e_txt = krb5_get_error_message (context, ret);
641 krb5_klog_syslog(LOG_ERR,
642 gettext( "Cannot create simple " "chpw socket: %s"),
643 e_txt);
644 fprintf(stderr, gettext("Cannot create simple chpw socket: %s"),
645 e_txt);
646 kadm5_destroy(global_server_handle);
647 krb5_klog_close(context);
648 exit(1);
651 /* Solaris Kerberos: Ensure that kadmind is only run on a master kdc */
652 if (ret = kadm5_is_master(context, params.realm, &is_master)){
653 krb5_klog_syslog(LOG_ERR,
654 gettext("Failed to determine whether host is master "
655 "KDC for realm %s: %s"), params.realm,
656 error_message(ret));
657 fprintf(stderr,
658 gettext("%s: Failed to determine whether host is master "
659 "KDC for realm %s: %s\n"), whoami, params.realm,
660 error_message(ret));
661 krb5_klog_close(context);
662 exit(1);
665 if (is_master == FALSE) {
666 char *master = NULL;
667 kadm5_get_master(context, params.realm, &master);
669 krb5_klog_syslog(LOG_ERR,
670 gettext("%s can only be run on the master KDC, %s, for "
671 "realm %s"), whoami, master ? master : "unknown",
672 params.realm);
673 fprintf(stderr,
674 gettext("%s: %s can only be run on the master KDC, %s, for "
675 "realm %s\n"), whoami, whoami, master ? master: "unknown",
676 params.realm);
677 krb5_klog_close(context);
678 exit(1);
681 memset((char *) &addr, 0, sizeof (struct sockaddr_in));
682 addr.sin_family = AF_INET;
683 addr.sin_addr.s_addr = INADDR_ANY;
684 l_port = addr.sin_port = htons(params.kadmind_port);
685 sin = &addr;
687 if ((handlep = setnetconfig()) == (void *) NULL) {
688 (void) krb5_klog_syslog(LOG_ERR,
689 gettext("cannot get any transport information"));
690 krb5_klog_close(context);
691 exit(1);
694 while (nconf = getnetconfig(handlep)) {
695 if ((nconf->nc_semantics == NC_TPI_COTS_ORD) &&
696 (strcmp(nconf->nc_protofmly, NC_INET) == 0) &&
697 (strcmp(nconf->nc_proto, NC_TCP) == 0))
698 break;
701 if (nconf == (struct netconfig *) NULL) {
702 (void) endnetconfig(handlep);
703 krb5_klog_close(context);
704 exit(1);
706 fd = t_open(nconf->nc_device, O_RDWR, &tinfo);
707 if (fd == -1) {
708 krb5_klog_syslog(LOG_ERR,
709 gettext("unable to open connection for ADMIN server"));
710 krb5_klog_close(context);
711 exit(1);
713 /* LINTED */
714 opt = (struct opthdr *) reqbuf;
715 opt->level = SOL_SOCKET;
716 opt->name = SO_REUSEADDR;
717 opt->len = sizeof (int);
720 * The option value is "1". This will allow the server to restart
721 * whilst the previous process is cleaning up after itself in a
722 * FIN_WAIT_2 or TIME_WAIT state. If another process is started
723 * outside of smf(5) then bind will fail anyway, which is what we want.
725 reqbuf[sizeof (struct opthdr)] = 1;
727 req.flags = T_NEGOTIATE;
728 req.opt.len = sizeof (struct opthdr) + opt->len;
729 req.opt.buf = (char *) opt;
731 resp.flags = 0;
732 resp.opt.buf = reqbuf;
733 resp.opt.maxlen = sizeof (reqbuf);
735 if (t_optmgmt(fd, &req, &resp) < 0 || resp.flags != T_SUCCESS) {
736 t_error("t_optmgmt");
737 exit(1);
739 /* Transform addr to netbuf */
741 tres = (struct t_bind *) t_alloc(fd, T_BIND, T_ADDR);
742 if (tres == NULL) {
743 (void) t_close(fd);
744 (void) krb5_klog_syslog(LOG_ERR,
745 gettext("cannot allocate netbuf"));
746 krb5_klog_close(context);
747 exit(1);
749 tbindstr.qlen = 8;
750 tbindstr.addr.buf = (char *) sin;
751 tbindstr.addr.len = tbindstr.addr.maxlen = __rpc_get_a_size(tinfo.addr);
752 sin = (struct sockaddr_in *) tbindstr.addr.buf;
753 /* SUNWresync121 XXX (void) memset(&addr, 0, sizeof(addr)); */
755 if (t_bind(fd, &tbindstr, tres) < 0) {
756 int oerrno = errno;
757 const char *e_txt = krb5_get_error_message (context, errno);
758 fprintf(stderr, gettext("%s: Cannot bind socket.\n"), whoami);
759 fprintf(stderr, gettext("bind: %s\n"), e_txt);
760 errno = oerrno;
761 krb5_klog_syslog(LOG_ERR, gettext("Cannot bind socket: %s"), e_txt);
762 if(oerrno == EADDRINUSE) {
763 char *w = strrchr(whoami, '/');
764 if (w) {
765 w++;
767 else {
768 w = whoami;
770 fprintf(stderr, gettext(
771 "This probably means that another %s process is already\n"
772 "running, or that another program is using the server port (number %d)\n"
773 "after being assigned it by the RPC portmap daemon. If another\n"
774 "%s is already running, you should kill it before\n"
775 "restarting the server. If, on the other hand, another program is\n"
776 "using the server port, you should kill it before running\n"
777 "%s, and ensure that the conflict does not occur in the\n"
778 "future by making sure that %s is started on reboot\n"
779 "before portmap.\n"), w, ntohs(addr.sin_port), w, w, w);
780 krb5_klog_syslog(LOG_ERR, gettext("Check for already-running %s or for "
781 "another process using port %d"), w,
782 htons(addr.sin_port));
784 kadm5_destroy(global_server_handle);
785 krb5_klog_close(context);
786 exit(1);
788 memset(&addr, 0, sizeof(addr));
789 addr.sin_family = AF_INET;
790 addr.sin_addr.s_addr = INADDR_ANY;
792 addr.sin_port = htons(chgpw_params.kpasswd_port);
794 if (bind(schpw, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
795 char portbuf[32];
796 int oerrno = errno;
797 const char *e_txt = krb5_get_error_message (context, errno);
798 fprintf(stderr, gettext("%s: Cannot bind socket.\n"), whoami);
799 fprintf(stderr, gettext("bind: %s\n"), e_txt);
800 errno = oerrno;
801 (void) snprintf(portbuf, sizeof (portbuf), "%d", ntohs(addr.sin_port));
802 krb5_klog_syslog(LOG_ERR, gettext("cannot bind simple chpw socket: %s"),
803 e_txt);
804 if(oerrno == EADDRINUSE) {
805 char *w = strrchr(whoami, '/');
806 if (w) {
807 w++;
809 else {
810 w = whoami;
812 fprintf(stderr, gettext(
813 "This probably means that another %s process is already\n"
814 "running, or that another program is using the server port (number %d).\n"
815 "If another %s is already running, you should kill it before\n"
816 "restarting the server.\n"),
817 w, ntohs(addr.sin_port), w);
819 krb5_klog_close(context);
820 exit(1);
823 transp = svc_tli_create(fd, nconf, NULL, 0, 0);
824 (void) t_free((char *) tres, T_BIND);
825 (void) endnetconfig(handlep);
826 if(transp == NULL) {
827 fprintf(stderr, gettext("%s: Cannot create RPC service.\n"), whoami);
828 krb5_klog_syslog(LOG_ERR, gettext("Cannot create RPC service: %m"));
829 kadm5_destroy(global_server_handle);
830 krb5_klog_close(context);
831 exit(1);
833 if(!svc_register(transp, KADM, KADMVERS, kadm_1, 0)) {
834 fprintf(stderr, gettext("%s: Cannot register RPC service.\n"), whoami);
835 krb5_klog_syslog(LOG_ERR, gettext("Cannot register RPC service, failing."));
836 kadm5_destroy(global_server_handle);
837 krb5_klog_close(context);
838 exit(1);
842 /* Solaris Kerberos:
843 * The only service principals which matter here are
844 * -> names[0].name (kadmin/<fqdn>)
845 * -> names[1].name (changepw/<fqdn>)
846 * KADM5_ADMIN_SERVICE_P, KADM5_CHANGEPW_SERVICE_P,
847 * OVSEC_KADM_ADMIN_SERVICE_P, OVSEC_KADM_CHANGEPW_SERVICE_P
848 * are all legacy service princs and calls to rpc_gss_set_svc_name()
849 * using these principals will always fail as they are not host
850 * based principals.
853 if (ret = kadm5_get_adm_host_srv_name(context, params.realm,
854 &names[0].name)) {
855 krb5_klog_syslog(LOG_ERR,
856 gettext("Cannot get host based service name for admin "
857 "principal in realm %s: %s"), params.realm,
858 error_message(ret));
859 fprintf(stderr,
860 gettext("%s: Cannot get host based service name for admin "
861 "principal in realm %s: %s\n"), whoami, params.realm,
862 error_message(ret));
863 krb5_klog_close(context);
864 exit(1);
867 if (ret = kadm5_get_cpw_host_srv_name(context, params.realm,
868 &names[1].name)) {
869 krb5_klog_syslog(LOG_ERR,
870 gettext("Cannot get host based service name for changepw "
871 "principal in realm %s: %s"), params.realm,
872 error_message(ret));
873 fprintf(stderr,
874 gettext("%s: Cannot get host based service name for "
875 "changepw principal in realm %s: %s\n"), whoami, params.realm,
876 error_message(ret));
877 krb5_klog_close(context);
878 exit(1);
880 names[2].name = KADM5_ADMIN_SERVICE_P;
881 names[3].name = KADM5_CHANGEPW_SERVICE_P;
882 names[4].name = OVSEC_KADM_ADMIN_SERVICE_P;
883 names[5].name = OVSEC_KADM_CHANGEPW_SERVICE_P;
885 if (names[0].name == NULL || names[1].name == NULL ||
886 names[2].name == NULL || names[3].name == NULL ||
887 names[4].name == NULL || names[5].name == NULL) {
888 krb5_klog_syslog(LOG_ERR,
889 gettext("Cannot initialize GSS-API authentication, "
890 "failing."));
891 fprintf(stderr,
892 gettext("%s: Cannot initialize "
893 "GSS-API authentication.\n"),
894 whoami);
895 krb5_klog_close(context);
896 exit(1);
900 * Go through some contortions to point gssapi at a kdb keytab.
901 * This prevents kadmind from needing to use an actual file-based
902 * keytab.
904 /* XXX extract kadm5's krb5_context */
905 hctx = ((kadm5_server_handle_t)global_server_handle)->context;
906 /* Set ktkdb's internal krb5_context. */
907 ret = krb5_ktkdb_set_context(hctx);
908 if (ret) {
909 krb5_klog_syslog(LOG_ERR, "Can't set kdb keytab's internal context.");
910 goto kterr;
912 /* Solaris Kerberos */
913 ret = krb5_db_set_mkey(hctx, &((kadm5_server_handle_t)global_server_handle)->master_keyblock);
914 if (ret) {
915 krb5_klog_syslog(LOG_ERR, "Can't set master key for kdb keytab.");
916 goto kterr;
918 ret = krb5_kt_register(context, &krb5_kt_kdb_ops);
919 if (ret) {
920 krb5_klog_syslog(LOG_ERR, "Can't register kdb keytab.");
921 goto kterr;
923 /* Tell gssapi about the kdb keytab. */
924 ret = krb5_gss_register_acceptor_identity("KDB:");
925 if (ret) {
926 krb5_klog_syslog(LOG_ERR, "Can't register acceptor keytab.");
927 goto kterr;
929 kterr:
930 if (ret) {
931 krb5_klog_syslog(LOG_ERR, "%s", krb5_get_error_message (context, ret));
932 fprintf(stderr, "%s: Can't set up keytab for RPC.\n", whoami);
933 kadm5_destroy(global_server_handle);
934 krb5_klog_close(context);
935 exit(1);
939 * Try to acquire creds for the old OV services as well as the
940 * new names, but if that fails just fall back on the new names.
943 if (rpc_gss_set_svc_name(names[5].name,
944 "kerberos_v5", 0, KADM, KADMVERS) &&
945 rpc_gss_set_svc_name(names[4].name,
946 "kerberos_v5", 0, KADM, KADMVERS))
947 oldnames++;
948 if (rpc_gss_set_svc_name(names[3].name,
949 "kerberos_v5", 0, KADM, KADMVERS))
950 oldnames++;
951 if (rpc_gss_set_svc_name(names[2].name,
952 "kerberos_v5", 0, KADM, KADMVERS))
953 oldnames++;
955 /* If rpc_gss_set_svc_name() fails for either kadmin/<fqdn> or
956 * for changepw/<fqdn> then try to determine if this is caused
957 * by a missing keytab file or entry. If so, log it and continue.
959 if (rpc_gss_set_svc_name(names[0].name,
960 "kerberos_v5", 0, KADM, KADMVERS))
961 oldnames++;
963 if (rpc_gss_set_svc_name(names[1].name,
964 "kerberos_v5", 0, KADM, KADMVERS))
965 oldnames++;
967 retdn = getdomnames(context, params.realm, &dnames);
968 if (retdn == 0 && dnames) {
970 * Multi-homed KDCs sometimes may need to set svc names
971 * for multiple net interfaces so we set them for
972 * all interfaces just in case.
974 set_svc_domnames(KADM5_ADMIN_HOST_SERVICE,
975 dnames, KADM, KADMVERS);
976 set_svc_domnames(KADM5_CHANGEPW_HOST_SERVICE,
977 dnames, KADM, KADMVERS);
980 /* if set_names succeeded, this will too */
981 in_buf.value = names[1].name;
982 in_buf.length = strlen(names[1].name) + 1;
983 (void) gss_import_name(&OMret, &in_buf, (gss_OID) nt_krb5_name_oid,
984 &gss_changepw_name);
985 if (oldnames) {
986 in_buf.value = names[3].name;
987 in_buf.length = strlen(names[3].name) + 1;
988 (void) gss_import_name(&OMret, &in_buf, (gss_OID) nt_krb5_name_oid,
989 &gss_oldchangepw_name);
992 if ((ret = kadm5int_acl_init(context, 0, params.acl_file))) {
993 errmsg = krb5_get_error_message (context, ret);
994 krb5_klog_syslog(LOG_ERR, gettext("Cannot initialize acl file: %s"),
995 errmsg);
996 fprintf(stderr, gettext("%s: Cannot initialize acl file: %s\n"),
997 whoami, errmsg);
998 kadm5_destroy(global_server_handle);
999 krb5_klog_close(context);
1000 exit(1);
1004 * Solaris Kerberos:
1005 * Warn if the acl file contains an entry for a principal matching the
1006 * default (unconfigured) acl rule.
1008 gssbuf.length = strlen("x/admin@___default_realm___");
1009 gssbuf.value = "x/admin@___default_realm___";
1010 /* Use any value as the first component - 'x' in this case */
1011 if (gss_import_name(&minor_status, &gssbuf, GSS_C_NT_USER_NAME, &name)
1012 == GSS_S_COMPLETE) {
1013 if (kadm5int_acl_check(context, name, ACL_MODIFY, NULL, NULL)) {
1014 krb5_klog_syslog(LOG_WARNING,
1015 gettext("acls may not be properly configured: "
1016 "found an acl matching \"___default_realm___\" in "
1017 " %s"), params.acl_file);
1018 (void) fprintf(stderr, gettext("%s: Warning: "
1019 "acls may not be properly configured: found an acl "
1020 "matching \"___default_realm___\" in %s\n"),
1021 whoami, params.acl_file);
1023 (void) gss_release_name(&minor_status, &name);
1025 gssbuf.value = NULL;
1026 gssbuf.length = 0;
1029 * Solaris Kerberos:
1030 * List the logs (FILE, STDERR, etc) which are currently being
1031 * logged to and print to stderr. Useful when trying to
1032 * track down a failure via SMF.
1034 if (ret = krb5_klog_list_logs(whoami)) {
1035 fprintf(stderr, gettext("%s: %s while listing logs\n"),
1036 whoami, error_message(ret));
1037 krb5_klog_syslog(LOG_ERR, gettext("%s while listing logs"),
1038 error_message(ret));
1041 if (!nofork && (ret = daemon(0, 0))) {
1042 ret = errno;
1043 errmsg = krb5_get_error_message (context, ret);
1044 krb5_klog_syslog(LOG_ERR,
1045 gettext("Cannot detach from tty: %s"), errmsg);
1046 fprintf(stderr, gettext("%s: Cannot detach from tty: %s\n"),
1047 whoami, errmsg);
1048 kadm5_destroy(global_server_handle);
1049 krb5_klog_close(context);
1050 exit(1);
1053 /* SUNW14resync */
1054 #if 0
1055 krb5_klog_syslog(LOG_INFO, "Seeding random number generator");
1056 ret = krb5_c_random_os_entropy(context, 1, NULL);
1057 if (ret) {
1058 krb5_klog_syslog(LOG_ERR, "Error getting random seed: %s, aborting",
1059 krb5_get_error_message(context, ret));
1060 kadm5_destroy(global_server_handle);
1061 krb5_klog_close(context);
1062 exit(1);
1064 #endif
1067 handle = global_server_handle;
1068 ctx = handle->context;
1069 if (params.iprop_enabled == TRUE) {
1070 if (ret = krb5_db_supports_iprop(ctx, &iprop_supported)) {
1071 fprintf(stderr,
1072 gettext("%s: %s while trying to determine if KDB "
1073 "plugin supports iprop\n"), whoami,
1074 error_message(ret));
1075 krb5_klog_syslog(LOG_ERR,
1076 gettext("%s while trying to determine if KDB "
1077 "plugin supports iprop"), error_message(ret));
1078 krb5_klog_close(ctx);
1079 exit(1);
1082 if (!iprop_supported) {
1083 fprintf(stderr,
1084 gettext("%s: Warning, current KDB "
1085 "plugin does not support iprop, continuing "
1086 "with iprop disabled\n"), whoami);
1087 krb5_klog_syslog(LOG_WARNING,
1088 gettext("Warning, current KDB "
1089 "plugin does not support iprop, continuing "
1090 "with iprop disabled"));
1092 ulog_set_role(ctx, IPROP_NULL);
1093 } else
1094 ulog_set_role(ctx, IPROP_MASTER);
1095 } else
1096 ulog_set_role(ctx, IPROP_NULL);
1098 log_ctx = ctx->kdblog_context;
1100 if (log_ctx && (log_ctx->iproprole == IPROP_MASTER)) {
1102 * IProp is enabled, so let's map in the update log
1103 * and setup the service.
1105 if (ret = ulog_map(ctx, &params, FKADMIND)) {
1106 fprintf(stderr,
1107 gettext("%s: %s while mapping update log "
1108 "(`%s.ulog')\n"), whoami, error_message(ret),
1109 params.dbname);
1110 krb5_klog_syslog(LOG_ERR,
1111 gettext("%s while mapping update log "
1112 "(`%s.ulog')"), error_message(ret),
1113 params.dbname);
1114 krb5_klog_close(ctx);
1115 exit(1);
1119 if (nofork)
1120 fprintf(stderr,
1121 "%s: create IPROP svc (PROG=%d, VERS=%d)\n",
1122 whoami, KRB5_IPROP_PROG, KRB5_IPROP_VERS);
1124 if (!svc_create(krb5_iprop_prog_1,
1125 KRB5_IPROP_PROG, KRB5_IPROP_VERS,
1126 "circuit_v")) {
1127 fprintf(stderr,
1128 gettext("%s: Cannot create IProp RPC service (PROG=%d, VERS=%d)\n"),
1129 whoami,
1130 KRB5_IPROP_PROG, KRB5_IPROP_VERS);
1131 krb5_klog_syslog(LOG_ERR,
1132 gettext("Cannot create IProp RPC service (PROG=%d, VERS=%d), failing."),
1133 KRB5_IPROP_PROG, KRB5_IPROP_VERS);
1134 krb5_klog_close(ctx);
1135 exit(1);
1138 if (ret = kiprop_get_adm_host_srv_name(ctx,
1139 params.realm,
1140 &kiprop_name)) {
1141 krb5_klog_syslog(LOG_ERR,
1142 gettext("%s while getting IProp svc name, failing"),
1143 error_message(ret));
1144 fprintf(stderr,
1145 gettext("%s: %s while getting IProp svc name, failing\n"),
1146 whoami, error_message(ret));
1147 krb5_klog_close(ctx);
1148 exit(1);
1151 if (!rpc_gss_set_svc_name(kiprop_name, "kerberos_v5", 0,
1152 KRB5_IPROP_PROG, KRB5_IPROP_VERS)) {
1153 rpc_gss_error_t err;
1154 (void) rpc_gss_get_error(&err);
1156 krb5_klog_syslog(LOG_ERR,
1157 gettext("Unable to set RPCSEC_GSS service name (`%s'), failing."),
1158 kiprop_name ? kiprop_name : "<null>");
1159 fprintf(stderr,
1160 gettext("%s: Unable to set RPCSEC_GSS service name (`%s'), failing.\n"),
1161 whoami,
1162 kiprop_name ? kiprop_name : "<null>");
1164 if (nofork) {
1165 fprintf(stderr,
1166 "%s: set svc name (rpcsec err=%d, sys err=%d)\n",
1167 whoami,
1168 err.rpc_gss_error,
1169 err.system_error);
1172 exit(1);
1174 free(kiprop_name);
1176 if (retdn == 0 && dnames) {
1177 set_svc_domnames(KADM5_KIPROP_HOST_SERVICE,
1178 dnames,
1179 KRB5_IPROP_PROG, KRB5_IPROP_VERS);
1182 } else {
1183 if (!oldnames) {
1184 /* rpc_gss_set_svc_name failed for both kadmin/<fqdn> and
1185 * changepw/<fqdn>.
1187 krb5_klog_syslog(LOG_ERR,
1188 gettext("Unable to set RPCSEC_GSS service names "
1189 "('%s, %s')"),
1190 names[0].name, names[1].name);
1191 fprintf(stderr,
1192 gettext("%s: Unable to set RPCSEC_GSS service names "
1193 "('%s, %s')\n"),
1194 whoami,
1195 names[0].name, names[1].name);
1196 krb5_klog_close(context);
1197 exit(1);
1201 if (dnames)
1202 freedomnames(dnames);
1204 setup_signal_handlers(log_ctx->iproprole);
1205 krb5_klog_syslog(LOG_INFO, gettext("starting"));
1206 if (nofork)
1207 fprintf(stderr, "%s: starting...\n", whoami);
1211 * We now call our own customized async event processing
1212 * function kadm_svc_run(), as opposed to svc_run() earlier,
1213 * since this enables kadmind to also listen-to/process
1214 * non-RPCSEC_GSS based change-pwd requests apart from the
1215 * regular, RPCSEC_GSS kpasswd requests from Solaris Krb5 clients.
1217 kadm_svc_run();
1219 krb5_klog_syslog(LOG_INFO, gettext("finished, exiting"));
1220 kadm5_destroy(global_server_handle);
1221 t_close(fd);
1222 krb5_klog_close(context);
1223 exit(0);
1227 * Function: kadm_svc_run
1229 * Purpose: modified version of sunrpc svc_run.
1230 * which closes the database every TIMEOUT seconds.
1232 * Arguments:
1233 * Requires:
1234 * Effects:
1235 * Modifies:
1238 void kadm_svc_run(void)
1240 struct pollfd *rfd = 0;
1241 struct timeval timeout;
1242 int pollret;
1243 int nfds = 0;
1244 int i;
1246 while(signal_request_exit == 0) {
1247 timeout.tv_sec = TIMEOUT;
1248 timeout.tv_usec = 0;
1250 if (nfds != svc_max_pollfd) {
1251 rfd = realloc(rfd, sizeof (pollfd_t) * svc_max_pollfd);
1252 nfds = svc_max_pollfd;
1255 (void) memcpy(rfd, svc_pollfd,
1256 sizeof (pollfd_t) * svc_max_pollfd);
1258 for (i = 0; i < nfds; i++) {
1259 if (rfd[i].fd == -1) {
1260 rfd[i].fd = schpw;
1261 rfd[i].events = POLLIN;
1262 break;
1266 switch(pollret = poll(rfd, nfds,
1267 __rpc_timeval_to_msec(&timeout))) {
1268 case -1:
1269 if(errno == EINTR)
1270 continue;
1271 perror("poll");
1272 return;
1273 case 0:
1274 continue;
1275 default:
1276 for (i = 0; i < nfds; i++) {
1277 if (rfd[i].revents & POLLIN) {
1278 if (rfd[i].fd == schpw)
1279 handle_chpw(context, schpw,
1280 global_server_handle,
1281 &chgpw_params);
1282 else
1283 svc_getreq_poll(rfd, pollret);
1284 break;
1285 } else {
1286 if (i == (nfds - 1))
1287 perror("poll");
1290 break;
1297 * Function: setup_signal_handlers
1299 * Purpose: Setup signal handling functions with either
1300 * System V's signal() or POSIX_SIGNALS.
1302 void setup_signal_handlers(iprop_role iproprole) {
1303 #ifdef POSIX_SIGNALS
1304 (void) sigemptyset(&s_action.sa_mask);
1305 s_action.sa_handler = request_exit;
1306 (void) sigaction(SIGINT, &s_action, (struct sigaction *) NULL);
1307 (void) sigaction(SIGTERM, &s_action, (struct sigaction *) NULL);
1308 (void) sigaction(SIGQUIT, &s_action, (struct sigaction *) NULL);
1309 s_action.sa_handler = sig_pipe;
1310 (void) sigaction(SIGPIPE, &s_action, (struct sigaction *) NULL);
1313 * IProp will fork for a full-resync, we don't want to
1314 * wait on it and we don't want the living dead procs either.
1316 if (iproprole == IPROP_MASTER) {
1317 s_action.sa_handler = SIG_IGN;
1318 (void) sigaction(SIGCHLD, &s_action, (struct sigaction *) NULL);
1320 #else
1321 signal(SIGINT, request_exit);
1322 signal(SIGTERM, request_exit);
1323 signal(SIGQUIT, request_exit);
1324 signal(SIGPIPE, sig_pipe);
1327 * IProp will fork for a full-resync, we don't want to
1328 * wait on it and we don't want the living dead procs either.
1330 if (iproprole == IPROP_MASTER)
1331 (void) signal(SIGCHLD, SIG_IGN);
1333 #endif /* POSIX_SIGNALS */
1334 return;
1339 * Function: request_exit
1341 * Purpose: sets flags saying the server got a signal and that it
1342 * should exit when convient.
1344 * Arguments:
1345 * Requires:
1346 * Effects:
1347 * modifies signal_request_exit which ideally makes the server exit
1348 * at some point.
1350 * Modifies:
1351 * signal_request_exit
1354 void request_exit(int signum)
1356 krb5_klog_syslog(LOG_NOTICE, gettext("Got signal to request exit"));
1357 signal_request_exit = 1;
1358 return;
1362 * Function: sig_pipe
1364 * Purpose: SIGPIPE handler
1366 * Effects: krb5_klog_syslogs a message that a SIGPIPE occurred and returns,
1367 * thus causing the read() or write() to fail and, presumable, the RPC
1368 * to recover. Otherwise, the process aborts.
1370 void sig_pipe(int unused)
1372 #ifndef POSIX_SIGNALS
1373 signal(SIGPIPE, sig_pipe);
1374 #endif /* POSIX_SIGNALS */
1375 krb5_klog_syslog(LOG_NOTICE, gettext("Warning: Received a SIGPIPE; "
1376 "probably a client aborted. Continuing."));
1377 return;