Home | History | Annotate | Download | only in server
      1 /*
      2  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
      3  * Use is subject to license terms.
      4  */
      5 
      6 
      7 /*
      8  * WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING
      9  *
     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.
     15  *
     16  *	For further information, read the top-level Openvision
     17  *	copyright which is contained in the top-level MIT Kerberos
     18  *	copyright.
     19  *
     20  * WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING
     21  *
     22  */
     23 
     24 /*
     25  * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved
     26  *
     27  */
     28 
     29 /*
     30  * Copyright (C) 1998 by the FundsXpress, INC.
     31  *
     32  * All rights reserved.
     33  *
     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.
     38  *
     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.
     49  *
     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.
     53  */
     54 
     55 
     56 /*
     57  * SUNWresync121 XXX
     58  * Beware future resyncers, this file is much diff from MIT (1.0...)
     59  */
     60 
     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>
     90 
     91 #include <rpc/rpcsec_gss.h>
     92 #include    "misc.h"
     93 
     94 #ifdef PURIFY
     95 #include    "purify.h"
     96 
     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 */
    102 
    103 
    104 #ifndef	FD_SETSIZE
    105 #define	FD_SETSIZE	256
    106 #endif
    107 
    108 #ifndef MAX
    109 #define	MAX(a, b)	(((a) > (b)) ? (a) : (b))
    110 #endif
    111 
    112 #if defined(NEED_DAEMON_PROTO)
    113 extern int daemon(int, int);
    114 #endif
    115 
    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);
    122 
    123 #ifdef POSIX_SIGNALS
    124 static struct sigaction s_action;
    125 #endif /* POSIX_SIGNALS */
    126 
    127 
    128 #define	TIMEOUT	15
    129 
    130 typedef struct _auth_gssapi_name {
    131 	char *name;
    132 	gss_OID type;
    133 } auth_gssapi_name;
    134 
    135 gss_name_t gss_changepw_name = NULL, gss_oldchangepw_name = NULL;
    136 void *global_server_handle;
    137 
    138 /*
    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.
    142  */
    143 #define OVSEC_KADM_ADMIN_SERVICE_P	"ovsec_adm@admin"
    144 #define OVSEC_KADM_CHANGEPW_SERVICE_P	"ovsec_adm@changepw"
    145 
    146 
    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 **);
    152 
    153 static int schpw;
    154 
    155 
    156 in_port_t l_port = 0;	/* global local port num, for BSM audits */
    157 
    158 int nofork = 0; /* global; don't fork (debug mode) */
    159 
    160 
    161 /*
    162  * Function: usage
    163  *
    164  * Purpose: print out the server usage message
    165  *
    166  * Arguments:
    167  * Requires:
    168  * Effects:
    169  * Modifies:
    170  */
    171 
    172 static void usage()
    173 {
    174      fprintf(stderr, gettext("Usage: kadmind [-x db_args]* [-r realm] [-m] [-d] "
    175          "[-p port-number]\n"));
    176      exit(1);
    177 }
    178 
    179 /*
    180  * Function: display_status
    181  *
    182  * Purpose: displays GSS-API messages
    183  *
    184  * Arguments:
    185  *
    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
    189  *
    190  * Effects:
    191  *
    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.
    195  */
    196 static void display_status_1(char *, OM_uint32, int);
    197 
    198 static void display_status(msg, maj_stat, min_stat)
    199      char *msg;
    200      OM_uint32 maj_stat;
    201      OM_uint32 min_stat;
    202 {
    203      display_status_1(msg, maj_stat, GSS_C_GSS_CODE);
    204      display_status_1(msg, min_stat, GSS_C_MECH_CODE);
    205 }
    206 
    207 static void display_status_1(m, code, type)
    208      char *m;
    209      OM_uint32 code;
    210      int type;
    211 {
    212 	OM_uint32 maj_stat, min_stat;
    213 	gss_buffer_desc msg;
    214 	OM_uint32 msg_ctx;
    215 
    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);
    224 
    225 		if (!msg_ctx)
    226 			break;
    227 	}
    228 }
    229 
    230 
    231 /*
    232  * Solaris Kerberos: the following prototypes are needed because these are
    233  * private interfaces that do not have prototypes in any .h
    234  */
    235 
    236 extern struct hostent   *res_getipnodebyaddr(const void *, size_t, int, int *);
    237 extern void             res_freehostent(struct hostent *);
    238 
    239 static void
    240 freedomnames(char **npp)
    241 {
    242 	char **tpp;
    243 
    244 	if (npp) {
    245 		tpp = npp;
    246 		while (*tpp++) {
    247 			free(*(tpp-1));
    248 		}
    249 		free(npp);
    250 	}
    251 }
    252 
    253 /*
    254  * Construct a list of uniq FQDNs of all the net interfaces (except
    255  * krb5.conf master dups) and return it in arg 'dnames'.
    256  *
    257  * On successful return (0), caller must call freedomnames()
    258  * to free memory.
    259  */
    260 static int
    261 getdomnames(krb5_context ctx, char *realm, char ***dnames)
    262 {
    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 */
    270 
    271 	if (ret = kadm5_get_master(ctx, realm, &cfhost)) {
    272 		return (ret);
    273 	}
    274 
    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;
    283 	}
    284 
    285 
    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) {
    293 
    294 			/* skip master host in krb5.conf */
    295 			if (strcasecmp(cfhost, hp->h_name) == 0) {
    296 				res_freehostent(hp);
    297 				hp = NULL;
    298 				continue;
    299 			}
    300 
    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;
    308 				}
    309 			}
    310 
    311 			if (dup) {
    312 				res_freehostent(hp);
    313 				hp = NULL;
    314 				continue;
    315 			}
    316 
    317 			npp = realloc(npp, sizeof(char *) * (n + 2));
    318 			if (!npp) {
    319 				result = ENOMEM;
    320 				goto err;
    321 			}
    322 			npp[n] = strdup(hp->h_name);
    323 			if (!npp[n]) {
    324 				result = ENOMEM;
    325 				goto err;
    326 			}
    327 			npp[n+1] = NULL;
    328 			n++;
    329 
    330 			res_freehostent(hp);
    331 			hp = NULL;
    332 			result = 0;
    333 		}
    334 
    335 	}
    336 
    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));
    342 	}
    343 #endif
    344 
    345 	goto out;
    346 
    347 err:
    348 	if (npp) {
    349 		freedomnames(npp);
    350 		npp = NULL;
    351 	}
    352 
    353 	if (hp) {
    354 		res_freehostent(hp);
    355 		hp = NULL;
    356 	}
    357 
    358 out:
    359 	if (cfhost) {
    360 		free (cfhost);
    361 		cfhost = NULL;
    362 	}
    363 	if (addresses) {
    364 		krb5_free_addresses(ctx, addresses);
    365 		addresses = NULL;
    366 	}
    367 
    368 	if (result == 0)
    369 		*dnames = npp;
    370 
    371 	return (result);
    372 }
    373 
    374 /*
    375  * Set the rpcsec_gss svc names for all net interfaces.
    376  */
    377 static void
    378 set_svc_domnames(char *svcname, char **dnames,
    379 		u_int program, u_int version)
    380 {
    381 	bool_t ret;
    382 	char **tpp = dnames;
    383 
    384 	if (!tpp)
    385 		return;
    386 
    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);
    399 	}
    400 }
    401 
    402 /* XXX yuck.  the signal handlers need this */
    403 static krb5_context context;
    404 
    405 static krb5_context hctx;
    406 
    407 int main(int argc, char *argv[])
    408 {
    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;
    430 
    431      struct t_optmgmt req, resp;
    432      struct opthdr *opt;
    433      char reqbuf[128];
    434      struct rlimit rl;
    435 
    436      char *kiprop_name = NULL; /* IProp svc name */
    437      kdb_log_context *log_ctx;
    438      kadm5_server_handle_t handle;
    439      krb5_context ctx;
    440 
    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;
    448 
    449      /* Solaris Kerberos: Stores additional error messages */
    450      char *emsg = NULL;
    451 
    452      /* Solaris Kerberos: Indicates whether loalhost is master or not */
    453      krb5_boolean is_master;
    454 
    455      /* Solaris Kerberos: Used for checking acl file */
    456      gss_name_t name;
    457 
    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);
    467      }
    468 
    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;
    474 
    475 #ifdef PURIFY
    476      purify_start_batch();
    477 #endif /* PURIFY */
    478      whoami = (strrchr(argv[0], '/') ? strrchr(argv[0], '/')+1 : argv[0]);
    479 
    480 	(void) setlocale(LC_ALL, "");
    481 
    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
    485 
    486 	(void) textdomain(TEXT_DOMAIN);
    487 
    488      nofork = 0;
    489 
    490      memset((char *) &params, 0, sizeof(params));
    491 
    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++;
    517 			{
    518 			    char **temp = realloc( db_args,
    519 				sizeof(char*) * (db_args_size+1)); /* one for NULL */
    520 			    if( temp == NULL )
    521 			    {
    522 				fprintf(stderr, gettext("%s: cannot initialize. Not enough memory\n"),
    523 				    whoami);
    524 				exit(1);
    525 			    }
    526 			    db_args = temp;
    527 			}
    528 			db_args[db_args_size-1] = optarg;
    529 			db_args[db_args_size]   = NULL;
    530 			break;
    531 		case '?':
    532 		default:
    533 			usage();
    534 		}
    535 	}
    536 
    537 
    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);
    542 	}
    543 
    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);
    549      }
    550 
    551      krb5_klog_init(context, "admin_server", whoami, 1);
    552 
    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);
    571      }
    572 
    573      if( db_args )
    574      {
    575 	 free(db_args), db_args=NULL;
    576      }
    577 
    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);
    590      }
    591 
    592 #define REQUIRED_PARAMS (KADM5_CONFIG_REALM | KADM5_CONFIG_ACL_FILE)
    593 
    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);
    607      }
    608 
    609 	/*
    610 	 * When using the Horowitz/IETF protocol for
    611 	 * password changing, the default port is 464
    612 	 * (officially recognized by IANA)
    613 	 *
    614 	 * DEFAULT_KPASSWD_PORT -> 464
    615 	 */
    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;
    620 
    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);
    631      }
    632 
    633 	/*
    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
    638 	 */
    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);
    649      }
    650 
    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);
    663 	}
    664 
    665 	if (is_master == FALSE) {
    666 		char *master = NULL;
    667 		kadm5_get_master(context, params.realm, &master);
    668 
    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);
    679 	}
    680 
    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;
    686 
    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);
    692 	}
    693 
    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;
    699 	}
    700 
    701 	if (nconf == (struct netconfig *) NULL) {
    702 		(void) endnetconfig(handlep);
    703 		krb5_klog_close(context);
    704 		exit(1);
    705 	}
    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);
    712 	}
    713 	/* LINTED */
    714 	opt = (struct opthdr *) reqbuf;
    715 	opt->level = SOL_SOCKET;
    716 	opt->name = SO_REUSEADDR;
    717 	opt->len = sizeof (int);
    718 
    719 	/*
    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.
    724 	 */
    725 	reqbuf[sizeof (struct opthdr)] = 1;
    726 
    727 	req.flags = T_NEGOTIATE;
    728 	req.opt.len = sizeof (struct opthdr) + opt->len;
    729 	req.opt.buf = (char *) opt;
    730 
    731 	resp.flags = 0;
    732 	resp.opt.buf = reqbuf;
    733 	resp.opt.maxlen = sizeof (reqbuf);
    734 
    735 	if (t_optmgmt(fd, &req, &resp) < 0 || resp.flags != T_SUCCESS) {
    736 		t_error("t_optmgmt");
    737 		exit(1);
    738 	}
    739 	/* Transform addr to netbuf */
    740 
    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);
    748 	}
    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)); */
    754 
    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++;
    766 	       }
    767 	       else {
    768 		    w = whoami;
    769 	       }
    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));
    783 	  }
    784 	  kadm5_destroy(global_server_handle);
    785 	  krb5_klog_close(context);
    786 	  exit(1);
    787      }
    788      memset(&addr, 0, sizeof(addr));
    789      addr.sin_family = AF_INET;
    790      addr.sin_addr.s_addr = INADDR_ANY;
    791 
    792      addr.sin_port = htons(chgpw_params.kpasswd_port);
    793 
    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++;
    808 	       }
    809 	       else {
    810 		    w = whoami;
    811 	       }
    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);
    818  	  }
    819  	  krb5_klog_close(context);
    820 	  exit(1);
    821      }
    822 
    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);
    832      }
    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);
    839      }
    840 
    841 
    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.
    851 	 */
    852 
    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);
    865 	}
    866 
    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);
    879 	}
    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;
    884 
    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);
    897 	}
    898 
    899      /*
    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.
    903       */
    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;
    911      }
    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;
    917      }
    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;
    922      }
    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;
    928      }
    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);
    936      }
    937 
    938      /*
    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.
    941       */
    942 
    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++;
    954 
    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.
    958      */
    959 	if (rpc_gss_set_svc_name(names[0].name,
    960 				"kerberos_v5", 0, KADM, KADMVERS))
    961 		oldnames++;
    962 
    963 	if (rpc_gss_set_svc_name(names[1].name,
    964 				"kerberos_v5", 0, KADM, KADMVERS))
    965 		oldnames++;
    966 
    967 	retdn = getdomnames(context, params.realm, &dnames);
    968 	if (retdn == 0 && dnames) {
    969 		/*
    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.
    973 		 */
    974 		set_svc_domnames(KADM5_ADMIN_HOST_SERVICE,
    975 				dnames, KADM, KADMVERS);
    976 		set_svc_domnames(KADM5_CHANGEPW_HOST_SERVICE,
    977 				dnames, KADM, KADMVERS);
    978 	}
    979 
    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);
    990      }
    991 
    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);
   1001      }
   1002 
   1003 	/*
   1004 	 * Solaris Kerberos:
   1005 	 * Warn if the acl file contains an entry for a principal matching the
   1006 	 * default (unconfigured) acl rule.
   1007 	 */
   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);
   1022 		}
   1023 		(void) gss_release_name(&minor_status, &name);
   1024 	}
   1025 	gssbuf.value = NULL;
   1026 	gssbuf.length = 0;
   1027 
   1028 	/*
   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.
   1033 	 */
   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));
   1039 	}
   1040 
   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);
   1051      }
   1052 
   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);
   1063      }
   1064 #endif
   1065 
   1066 
   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);
   1080 		}
   1081 
   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"));
   1091 
   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);
   1097 
   1098 	log_ctx = ctx->kdblog_context;
   1099 
   1100 	if (log_ctx && (log_ctx->iproprole == IPROP_MASTER)) {
   1101 		/*
   1102 		 * IProp is enabled, so let's map in the update log
   1103 		 * and setup the service.
   1104 		 */
   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);
   1116 		}
   1117 
   1118 
   1119 		if (nofork)
   1120 			fprintf(stderr,
   1121 				"%s: create IPROP svc (PROG=%d, VERS=%d)\n",
   1122 				whoami, KRB5_IPROP_PROG, KRB5_IPROP_VERS);
   1123 
   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);
   1136 		}
   1137 
   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);
   1149 		}
   1150 
   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);
   1155 
   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>");
   1163 
   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);
   1170 			}
   1171 
   1172 			exit(1);
   1173 		}
   1174 		free(kiprop_name);
   1175 
   1176 		if (retdn == 0 && dnames) {
   1177 			set_svc_domnames(KADM5_KIPROP_HOST_SERVICE,
   1178 					dnames,
   1179 					KRB5_IPROP_PROG, KRB5_IPROP_VERS);
   1180 		}
   1181 
   1182 	} else {
   1183 		if (!oldnames) {
   1184 		/* rpc_gss_set_svc_name failed for both kadmin/<fqdn> and
   1185 		 * changepw/<fqdn>.
   1186 		 */
   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);
   1198 		}
   1199 	}
   1200 
   1201 	if (dnames)
   1202 		freedomnames(dnames);
   1203 
   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);
   1208 
   1209 
   1210 	/*
   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.
   1216 	 */
   1217 	kadm_svc_run();
   1218 
   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);
   1224 }
   1225 
   1226 /*
   1227  * Function: kadm_svc_run
   1228  *
   1229  * Purpose: modified version of sunrpc svc_run.
   1230  *	    which closes the database every TIMEOUT seconds.
   1231  *
   1232  * Arguments:
   1233  * Requires:
   1234  * Effects:
   1235  * Modifies:
   1236  */
   1237 
   1238 void kadm_svc_run(void)
   1239 {
   1240      struct pollfd	*rfd = 0;
   1241      struct	timeval	    timeout;
   1242      int pollret;
   1243      int nfds = 0;
   1244      int i;
   1245 
   1246      while(signal_request_exit == 0) {
   1247 		timeout.tv_sec = TIMEOUT;
   1248 		timeout.tv_usec = 0;
   1249 
   1250 		if (nfds != svc_max_pollfd) {
   1251 			rfd = realloc(rfd, sizeof (pollfd_t) * svc_max_pollfd);
   1252 			nfds = svc_max_pollfd;
   1253 		}
   1254 
   1255 		(void) memcpy(rfd, svc_pollfd,
   1256 			sizeof (pollfd_t) * svc_max_pollfd);
   1257 
   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;
   1263 			}
   1264 		}
   1265 
   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");
   1288 				}
   1289 			}
   1290 			break;
   1291 		}
   1292 	}
   1293 }
   1294 
   1295 
   1296 /*
   1297  * Function: setup_signal_handlers
   1298  *
   1299  * Purpose: Setup signal handling functions with either
   1300  * System V's signal() or POSIX_SIGNALS.
   1301  */
   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);
   1311 
   1312 	/*
   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.
   1315 	 */
   1316 	if (iproprole == IPROP_MASTER) {
   1317 		s_action.sa_handler = SIG_IGN;
   1318 		(void) sigaction(SIGCHLD, &s_action, (struct sigaction *) NULL);
   1319 	}
   1320 #else
   1321      signal(SIGINT, request_exit);
   1322      signal(SIGTERM, request_exit);
   1323      signal(SIGQUIT, request_exit);
   1324      signal(SIGPIPE, sig_pipe);
   1325 
   1326 	/*
   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.
   1329 	 */
   1330 	if (iproprole == IPROP_MASTER)
   1331 		(void) signal(SIGCHLD, SIG_IGN);
   1332 
   1333 #endif /* POSIX_SIGNALS */
   1334 	return;
   1335 }
   1336 
   1337 
   1338 /*
   1339  * Function: request_exit
   1340  *
   1341  * Purpose: sets flags saying the server got a signal and that it
   1342  *	    should exit when convient.
   1343  *
   1344  * Arguments:
   1345  * Requires:
   1346  * Effects:
   1347  *	modifies signal_request_exit which ideally makes the server exit
   1348  *	at some point.
   1349  *
   1350  * Modifies:
   1351  *	signal_request_exit
   1352  */
   1353 
   1354 void request_exit(int signum)
   1355 {
   1356      krb5_klog_syslog(LOG_NOTICE, gettext("Got signal to request exit"));
   1357      signal_request_exit = 1;
   1358      return;
   1359 }
   1360 
   1361 /*
   1362  * Function: sig_pipe
   1363  *
   1364  * Purpose: SIGPIPE handler
   1365  *
   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.
   1369  */
   1370 void sig_pipe(int unused)
   1371 {
   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;
   1378 }
   1379 
   1380