Home | History | Annotate | Download | only in cli
      1 /*
      2  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
      3  * Use is subject to license terms.
      4  */
      5 
      6 
      7 /*
      8  * Copyright 1994 by the Massachusetts Institute of Technology.
      9  * All Rights Reserved.
     10  *
     11  * Export of this software from the United States of America may
     12  *   require a specific license from the United States Government.
     13  *   It is the responsibility of any person or organization contemplating
     14  *   export to obtain such a license before exporting.
     15  *
     16  * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
     17  * distribute this software and its documentation for any purpose and
     18  * without fee is hereby granted, provided that the above copyright
     19  * notice appear in all copies and that both that copyright notice and
     20  * this permission notice appear in supporting documentation, and that
     21  * the name of M.I.T. not be used in advertising or publicity pertaining
     22  * to distribution of the software without specific, written prior
     23  * permission.  Furthermore if you modify this software you must label
     24  * your software as modified software and not distribute it in such a
     25  * fashion that it might be confused with the original M.I.T. software.
     26  * M.I.T. makes no representations about the suitability of
     27  * this software for any purpose.  It is provided "as is" without express
     28  * or implied warranty.
     29  *
     30  * kadmin.c: base functions for a kadmin command line interface using
     31  * the OVSecure library
     32  */
     33 
     34 #include <kadm5/admin.h>
     35 #include <krb5/adm_proto.h>
     36 #include <stdio.h>
     37 #include <string.h>
     38 #include <sys/types.h>
     39 #include <math.h>
     40 #include <unistd.h>
     41 #include <pwd.h>
     42 /* #include <sys/timeb.h> */
     43 #include <time.h>
     44 #include "kadmin.h"
     45 #include <libintl.h>
     46 #include <krb5.h>
     47 
     48 /*
     49  * Solaris:  the following are needed for paging
     50  */
     51 #include <signal.h>
     52 #include <sys/wait.h>
     53 
     54 /* command name when called "locally" (i.e. non-networked client ) */
     55 #define KADMIN_LOCAL_NAME "kadmin.local"
     56 
     57 /* functions defined in remote/local specific files */
     58 extern void usage(const char *);
     59 
     60 /* special struct to convert flag names for principals
     61    to actual krb5_flags for a principal */
     62 struct pflag {
     63     char *flagname;		/* name of flag as typed to CLI */
     64     int flaglen;		/* length of string (not counting -,+) */
     65     krb5_flags theflag;		/* actual principal flag to set/clear */
     66     int set;			/* 0 means clear, 1 means set (on '-') */
     67 };
     68 
     69 static struct pflag flags[] = {
     70 {"allow_postdated",	15,	KRB5_KDB_DISALLOW_POSTDATED,	1},
     71 {"allow_forwardable",	17,	KRB5_KDB_DISALLOW_FORWARDABLE,	1},
     72 {"allow_tgs_req",	13,	KRB5_KDB_DISALLOW_TGT_BASED,	1},
     73 {"allow_renewable",	15,	KRB5_KDB_DISALLOW_RENEWABLE,	1},
     74 {"allow_proxiable",	15,	KRB5_KDB_DISALLOW_PROXIABLE,	1},
     75 {"allow_dup_skey",	14,	KRB5_KDB_DISALLOW_DUP_SKEY,	1},
     76 {"allow_tix",		9,	KRB5_KDB_DISALLOW_ALL_TIX,	1},
     77 {"requires_preauth",	16,	KRB5_KDB_REQUIRES_PRE_AUTH,	0},
     78 {"requires_hwauth",	15,	KRB5_KDB_REQUIRES_HW_AUTH,	0},
     79 {"needchange", 10,	KRB5_KDB_REQUIRES_PWCHANGE,	0},
     80 {"allow_svr", 9,	KRB5_KDB_DISALLOW_SVR, 1},
     81 {"password_changing_service",	25,	KRB5_KDB_PWCHANGE_SERVICE,	0 },
     82 {"support_desmd5",	14,	KRB5_KDB_SUPPORT_DESMD5,	0 }
     83 };
     84 
     85 static char *prflags[] = {
     86     "DISALLOW_POSTDATED",	/* 0x00000001 */
     87     "DISALLOW_FORWARDABLE",	/* 0x00000002 */
     88     "DISALLOW_TGT_BASED",	/* 0x00000004 */
     89     "DISALLOW_RENEWABLE",	/* 0x00000008 */
     90     "DISALLOW_PROXIABLE",	/* 0x00000010 */
     91     "DISALLOW_DUP_SKEY",	/* 0x00000020 */
     92     "DISALLOW_ALL_TIX",		/* 0x00000040 */
     93     "REQUIRES_PRE_AUTH",	/* 0x00000080 */
     94     "REQUIRES_HW_AUTH",		/* 0x00000100 */
     95     "REQUIRES_PWCHANGE",	/* 0x00000200 */
     96     "UNKNOWN_0x00000400",	/* 0x00000400 */
     97     "UNKNOWN_0x00000800",	/* 0x00000800 */
     98     "DISALLOW_SVR",		/* 0x00001000 */
     99     "PWCHANGE_SERVICE",		/* 0x00002000 */
    100     "SUPPORT_DESMD5",		/* 0x00004000 */
    101     "NEW_PRINC",		/* 0x00008000 */
    102 };
    103 
    104 char *getenv();
    105 int exit_status = 0;
    106 char *def_realm = NULL;
    107 char *whoami = NULL;
    108 
    109 void *handle = NULL;
    110 krb5_context context;
    111 char *ccache_name = NULL;
    112 
    113 int locked = 0;
    114 static char *strdur(duration)
    115     time_t duration;
    116 {
    117     static char out[50];
    118     int neg, days, hours, minutes, seconds;
    119 
    120     if (duration < 0) {
    121 	duration *= -1;
    122 	neg = 1;
    123     } else
    124 	neg = 0;
    125     days = duration / (24 * 3600);
    126     duration %= 24 * 3600;
    127     hours = duration / 3600;
    128     duration %= 3600;
    129     minutes = duration / 60;
    130     duration %= 60;
    131     seconds = duration;
    132     snprintf(out, sizeof (out), "%s%d %s %02d:%02d:%02d", neg ? "-" : "",
    133 	    days, days == 1 ? gettext("day") : gettext("days"),
    134 	    hours, minutes, seconds);
    135     return out;
    136 }
    137 
    138 static char *strdate(when)
    139     krb5_timestamp when;
    140 {
    141     struct tm *tm;
    142     static char out[40];
    143 
    144     time_t lcltim = when;
    145     tm = localtime(&lcltim);
    146     strftime(out, sizeof(out), gettext("%a %b %d %H:%M:%S %Z %Y"), tm);
    147     return out;
    148 }
    149 
    150 /* this is a wrapper to go around krb5_parse_principal so we can set
    151    the default realm up properly */
    152 static krb5_error_code
    153 kadmin_parse_name(name, principal)
    154     char *name;
    155     krb5_principal *principal;
    156 {
    157     char *cp, *fullname;
    158     krb5_error_code retval;
    159 
    160     if (name == NULL)
    161 	return (EINVAL);
    162 
    163     /* assumes def_realm is initialized! */
    164     fullname = (char *)malloc(strlen(name) + 1 + strlen(def_realm) + 1);
    165     if (fullname == NULL)
    166 	return ENOMEM;
    167     strcpy(fullname, name);
    168     cp = strchr(fullname, '@');
    169     while (cp) {
    170 	if (cp - fullname && *(cp - 1) != '\\')
    171 	    break;
    172 	else
    173 	    cp = strchr(cp + 1, '@');
    174     }
    175     if (cp == NULL) {
    176 	strcat(fullname, "@");
    177 	strcat(fullname, def_realm);
    178     }
    179     retval = krb5_parse_name(context, fullname, principal);
    180     free(fullname);
    181     return retval;
    182 }
    183 
    184 static void extended_com_err_fn (const char *myprog, errcode_t code,
    185 				 const char *fmt, va_list args)
    186 {
    187     if (code) {
    188 	const char *emsg;
    189 	emsg = krb5_get_error_message (context, code);
    190 	fprintf (stderr, "%s: %s ", myprog, emsg);
    191 	krb5_free_error_message (context, emsg);
    192     } else {
    193 	fprintf (stderr, "%s: ", myprog);
    194     }
    195     vfprintf (stderr, fmt, args);
    196     fprintf (stderr, "\n");
    197 }
    198 char *kadmin_startup(argc, argv)
    199     int argc;
    200     char *argv[];
    201 {
    202     extern char *optarg;
    203     char *princstr = NULL, *keytab_name = NULL, *query = NULL;
    204     char *password = NULL;
    205     char *luser, *canon, *cp;
    206     int optchar, freeprinc = 0, use_keytab = 0;
    207     struct passwd *pw;
    208     kadm5_ret_t retval;
    209     krb5_ccache cc;
    210     krb5_principal princ;
    211     kadm5_config_params params;
    212     char **db_args = NULL;
    213     int db_args_size = 0;
    214     char *db_name = NULL;
    215     char *svcname = NULL;
    216 
    217     memset((char *) &params, 0, sizeof(params));
    218 
    219     if (strcmp (whoami, "kadmin.local") == 0)
    220 	set_com_err_hook(extended_com_err_fn);
    221 
    222     retval = kadm5_init_krb5_context(&context);
    223     if (retval) {
    224 	com_err(whoami, retval, gettext("while initializing krb5 library"));
    225 	exit(1);
    226     }
    227 
    228     while ((optchar = getopt(argc, argv, "x:r:p:kq:w:d:s:mc:t:e:ON")) != EOF) {
    229 	switch (optchar) {
    230 	case 'x':
    231 	    db_args_size++;
    232 	    {
    233 		char **temp = realloc(db_args, sizeof(char*) * (db_args_size+1));
    234 		if (temp == NULL) {
    235 		    fprintf(stderr, gettext("%s: Cannot initialize. Not enough memory\n"),
    236 			    argv[0]);
    237 		    exit(1);
    238 		}
    239 
    240 		db_args = temp;
    241 	    }
    242 	    db_args[db_args_size-1] = optarg;
    243 	    db_args[db_args_size]   = NULL;
    244 	    break;
    245 
    246 	case 'r':
    247 	    def_realm = optarg;
    248 	    break;
    249 	case 'p':
    250 	    princstr = optarg;
    251 	    break;
    252 	case 'c':
    253 	    ccache_name = optarg;
    254 	    break;
    255 	case 'k':
    256 	    use_keytab++;
    257 	    break;
    258 	case 't':
    259 	    keytab_name = optarg;
    260 	    break;
    261 	case 'w':
    262 	    password = optarg;
    263 	    break;
    264 	case 'q':
    265 	    query = optarg;
    266 	    break;
    267 	case 'd':
    268 	    /* now db_name is not a seperate argument. It has to be passed as part of the db_args */
    269 	    if (!db_name) {
    270 		db_name = malloc(strlen(optarg) + sizeof("dbname="));
    271 	    } else {
    272 		db_name = realloc(db_name, strlen(optarg) + sizeof("dbname="));
    273 	    }
    274 
    275 	    strcpy(db_name, "dbname=");
    276 	    strcat(db_name, optarg);
    277 
    278 	    db_args_size++;
    279 	    {
    280 		char **temp = realloc(db_args, sizeof(char*) * (db_args_size+1)); /* one for NULL */
    281 		if (temp == NULL) {
    282 		    fprintf(stderr,
    283 			    gettext("%s: Cannot initialize. Not enough memory\n"),
    284 			    argv[0]);
    285 		    exit(1);
    286 		}
    287 
    288 		db_args = temp;
    289 	    }
    290 	    db_args[db_args_size-1] = db_name;
    291 	    db_args[db_args_size]   = NULL;
    292 	    break;
    293 	case 's':
    294 	    params.admin_server = optarg;
    295 	    params.mask |= KADM5_CONFIG_ADMIN_SERVER;
    296 	    break;
    297 	case 'm':
    298 	    params.mkey_from_kbd = 1;
    299 	    params.mask |= KADM5_CONFIG_MKEY_FROM_KBD;
    300 	    break;
    301 	case 'e':
    302 	    retval = krb5_string_to_keysalts(optarg,
    303 					     ", \t",
    304 					     ":.-",
    305 					     0,
    306 					     &params.keysalts,
    307 					     &params.num_keysalts);
    308 	    if (retval) {
    309 		 com_err(whoami, retval,
    310 			gettext("while parsing keysalts %s"), optarg);
    311 		 exit(1);
    312 	    }
    313 	    params.mask |= KADM5_CONFIG_ENCTYPES;
    314 	    break;
    315 	case 'O':	/* Undocumented option for testing only */
    316 		svcname = KADM5_ADMIN_SERVICE_P;
    317 	    break;
    318 	default:
    319 	    usage(whoami);
    320 	}
    321     }
    322     if ((ccache_name && use_keytab) ||
    323 	(keytab_name && !use_keytab))
    324 	 usage(whoami);
    325 
    326     if (def_realm == NULL && krb5_get_default_realm(context, &def_realm)) {
    327 	if (freeprinc)
    328 	    free(princstr);
    329 	fprintf(stderr,
    330 		gettext("%s: unable to get default realm\n"), whoami);
    331 	exit(1);
    332     }
    333 
    334     params.mask |= KADM5_CONFIG_REALM;
    335     params.realm = def_realm;
    336 
    337     if (svcname == NULL) {
    338 	if (kadm5_get_adm_host_srv_name(context,
    339 			       def_realm, &svcname)) {
    340 		fprintf(stderr,
    341 			gettext("%s: unable to get host based "
    342 				"service name for realm %s\n"),
    343 			whoami, def_realm);
    344 		if (freeprinc)
    345 			free(princstr);
    346 		exit(1);
    347 	}
    348     }
    349 
    350     /*
    351      * Set cc to an open credentials cache, either specified by the -c
    352      * argument or the default.
    353      */
    354     if (ccache_name == NULL) {
    355 	 if ((retval = krb5_cc_default(context, &cc))) {
    356 	      com_err(whoami, retval,
    357 				gettext("while opening default "
    358 					"credentials cache"));
    359 	      exit(1);
    360 	 }
    361     } else {
    362 	 if ((retval = krb5_cc_resolve(context, ccache_name, &cc))) {
    363 	      com_err(whoami, retval,
    364 			gettext("while opening credentials cache %s"),
    365 			ccache_name);
    366 	      exit(1);
    367 	 }
    368     }
    369 
    370     /*
    371      * If no principal name is specified: If a ccache was specified
    372      * and its primary principal name can be read, it is used, else if
    373      * a keytab was specified, the principal name is host/hostname,
    374      * otherwise append "/admin" to the primary name of the default
    375      * ccache, $USER, or pw_name.
    376      *
    377      * Gee, 100+ lines to figure out the client principal name.  This
    378      * should be compressed...
    379      */
    380 
    381     if (princstr == NULL) {
    382 	if (ccache_name != NULL &&
    383 	    !krb5_cc_get_principal(context, cc, &princ)) {
    384 	     if ((retval = krb5_unparse_name(context, princ, &princstr))) {
    385 		  com_err(whoami, retval,
    386 			gettext("while canonicalizing principal name"));
    387 		  krb5_free_principal(context, princ);
    388 		  exit(1);
    389 	     }
    390 	     krb5_free_principal(context, princ);
    391 	     freeprinc++;
    392 	} else if (use_keytab != 0) {
    393 	    if ((retval = krb5_sname_to_principal(context, NULL,
    394 						  "host",
    395 						  KRB5_NT_SRV_HST,
    396 						  &princ))) {
    397 		com_err(whoami, retval,
    398 			gettext("creating host service principal"));
    399 		exit(1);
    400 	    }
    401 	    if ((retval = krb5_unparse_name(context, princ, &princstr))) {
    402 		com_err(whoami, retval,
    403 			gettext("while canonicalizing principal name"));
    404 		krb5_free_principal(context, princ);
    405 		exit(1);
    406 	    }
    407 	    krb5_free_principal(context, princ);
    408 	    freeprinc++;
    409 	} else if (!krb5_cc_get_principal(context, cc, &princ)) {
    410 	    char *realm = NULL;
    411 	    if (krb5_unparse_name(context, princ, &canon)) {
    412 		fprintf(stderr,
    413 			gettext("%s: unable to canonicalize "
    414 				"principal\n"), whoami);
    415 		krb5_free_principal(context, princ);
    416 		exit(1);
    417 	    }
    418 	    /* strip out realm of principal if it's there */
    419 	    realm = strchr(canon, '@');
    420 	    while (realm) {
    421 		if (realm - canon && *(realm - 1) != '\\')
    422 		    break;
    423 		else
    424 		    realm = strchr(realm+1, '@');
    425 	    }
    426 	    if (realm)
    427 		*realm++ = '\0';
    428 	    cp = strchr(canon, '/');
    429 	    while (cp) {
    430 		if (cp - canon && *(cp - 1) != '\\')
    431 		    break;
    432 		else
    433 		    cp = strchr(cp+1, '/');
    434 	    }
    435 	    if (cp != NULL)
    436 		*cp = '\0';
    437 	    princstr = (char*)malloc(strlen(canon) + 6 /* "/admin" */ +
    438 				     (realm ? 1 + strlen(realm) : 0) + 1);
    439 	    if (princstr == NULL) {
    440 		fprintf(stderr,
    441 			gettext("%s: out of memory\n"),
    442 			whoami);
    443 		exit(1);
    444 	    }
    445 	    strcpy(princstr, canon);
    446 	    strcat(princstr, "/admin");
    447 	    if (realm) {
    448 		strcat(princstr, "@");
    449 		strcat(princstr, realm);
    450 	    }
    451 	    free(canon);
    452 	    krb5_free_principal(context, princ);
    453 	    freeprinc++;
    454 	} else if ((luser = getenv("USER"))) {
    455 	    princstr = (char *) malloc(strlen(luser) + 7 /* "/admin@" */
    456 				       + strlen(def_realm) + 1);
    457 	    if (princstr == NULL) {
    458 		fprintf(stderr,
    459 			gettext("%s: out of memory\n"),
    460 			whoami);
    461 		exit(1);
    462 	    }
    463 	    strcpy(princstr, luser);
    464 	    strcat(princstr, "/admin");
    465 	    strcat(princstr, "@");
    466 	    strcat(princstr, def_realm);
    467 	    freeprinc++;
    468 	} else if ((pw = getpwuid(getuid()))) {
    469 	    princstr = (char *) malloc(strlen(pw->pw_name) + 7 /* "/admin@" */
    470 				       + strlen(def_realm) + 1);
    471 	    if (princstr == NULL) {
    472 		fprintf(stderr,
    473 			gettext("%s: out of memory\n"),
    474 			whoami);
    475 		exit(1);
    476 	    }
    477 	    strcpy(princstr, pw->pw_name);
    478 	    strcat(princstr, "/admin@");
    479 	    strcat(princstr, def_realm);
    480 	    freeprinc++;
    481 	} else {
    482 	    fprintf(stderr,
    483 			gettext("%s: unable to figure out "
    484 				"a principal name\n"),
    485 		    whoami);
    486 	    exit(1);
    487 	}
    488     }
    489 
    490     retval = krb5_klog_init(context, "admin_server", whoami, 0);
    491     if (retval) {
    492 	com_err(whoami, retval, "while setting up logging");
    493 	exit(1);
    494     }
    495 
    496     /*
    497      * Initialize the kadm5 connection.  If we were given a ccache,
    498      * use it.  Otherwise, use/prompt for the password.
    499      */
    500 
    501     /* Solaris Kerberos:
    502      * Send warnings to stderr
    503      */
    504     if (ccache_name) {
    505 	fprintf(stderr, gettext("Authenticating as principal %s with existing credentials.\n"),
    506 	       princstr);
    507 	retval = kadm5_init_with_creds(princstr, cc,
    508 				       svcname,
    509 				       &params,
    510 				       KADM5_STRUCT_VERSION,
    511 				       KADM5_API_VERSION_2,
    512 				       db_args,
    513 				       &handle);
    514     } else if (use_keytab) {
    515 	if (keytab_name)
    516 	    fprintf(stderr, gettext("Authenticating as principal %s with keytab %s.\n"),
    517 		   princstr, keytab_name);
    518 	else
    519 	    fprintf(stderr, gettext("Authenticating as principal %s with default keytab.\n"),
    520 		   princstr);
    521 	retval = kadm5_init_with_skey(princstr, keytab_name,
    522 				      svcname,
    523 				      &params,
    524 				      KADM5_STRUCT_VERSION,
    525 				      KADM5_API_VERSION_2,
    526 				      db_args,
    527 				      &handle);
    528     } else {
    529 	 fprintf(stderr, gettext("Authenticating as principal %s with password.\n"),
    530 	       princstr);
    531 	retval = kadm5_init_with_password(princstr, password,
    532 					  svcname,
    533 					  &params,
    534 					  KADM5_STRUCT_VERSION,
    535 					  KADM5_API_VERSION_2,
    536 					  db_args,
    537 					  &handle);
    538     }
    539     if (retval) {
    540 	    if (retval == KADM5_RPC_ERROR_CANTENCODEARGS ||
    541 		retval == KADM5_RPC_ERROR_CANTDECODEARGS) {
    542 		    com_err(whoami, KADM5_RPC_ERROR,
    543 			gettext("while initializing %s interface"), whoami);
    544 
    545 		    /* privacy-enabled mech probably not installed/configed */
    546 		    com_err(whoami, retval, gettext("."), whoami);
    547 	    } else {
    548 		    com_err(whoami, retval,
    549 			gettext("while initializing %s interface"), whoami);
    550 	if (retval == KADM5_BAD_CLIENT_PARAMS ||
    551 	    retval == KADM5_BAD_SERVER_PARAMS)
    552 		usage(whoami);
    553 	}
    554 	exit(1);
    555     }
    556     if (freeprinc)
    557 	free(princstr);
    558 
    559     if (db_name)
    560 	free(db_name), db_name=NULL;
    561 
    562     if (db_args)
    563 	free(db_args), db_args=NULL;
    564 
    565     if ((retval = krb5_cc_close(context, cc))) {
    566 	 com_err(whoami, retval, gettext("while closing ccache %s"),
    567 		 ccache_name);
    568 	 exit(1);
    569     }
    570 
    571     /* register the WRFILE keytab type and set it as the default */
    572     {
    573 #define DEFAULT_KEYTAB "WRFILE:/etc/krb5/krb5.keytab"
    574 	/* XXX krb5_defkeyname is an internal library global and
    575 	   should go away */
    576 	extern char *krb5_defkeyname;
    577 	krb5_defkeyname = DEFAULT_KEYTAB;
    578     }
    579 
    580     if ((retval = kadm5_init_iprop(handle)) != 0) {
    581 	com_err(whoami, retval, gettext("while mapping update log"));
    582 	exit(1);
    583     }
    584 
    585     /* Solaris kerberos: fix memory leak */
    586     if (svcname)
    587 	free(svcname);
    588 
    589     return query;
    590 }
    591 
    592 int quit()
    593 {
    594     kadm5_ret_t retval;
    595 
    596     if (locked) {
    597 	retval = kadm5_unlock(handle);
    598 	if (retval) {
    599 	    com_err("quit", retval, gettext("while unlocking locked database"));
    600 	    return 1;
    601 	}
    602 	locked = 0;
    603     }
    604 
    605      kadm5_destroy(handle);
    606      if (ccache_name != NULL) {
    607 	  fprintf(stderr,
    608 			gettext("\n\a\a\aAdministration credentials "
    609 				"NOT DESTROYED.\n"));
    610      }
    611 
    612      /* insert more random cleanup here */
    613      krb5_klog_close(context);
    614      krb5_free_context(context);
    615      context = NULL;
    616      return 0;
    617 }
    618 
    619 void kadmin_lock(argc, argv)
    620     int argc;
    621     char *argv[];
    622 {
    623     kadm5_ret_t retval;
    624 
    625     if (locked)
    626 	return;
    627     retval = kadm5_lock(handle);
    628     if (retval) {
    629 	com_err("lock", retval, "");
    630 	return;
    631     }
    632     locked = 1;
    633 }
    634 
    635 void kadmin_unlock(argc, argv)
    636     int argc;
    637     char *argv[];
    638 {
    639     kadm5_ret_t retval;
    640 
    641     if (!locked)
    642 	return;
    643     retval = kadm5_unlock(handle);
    644     if (retval) {
    645 	com_err("unlock", retval, "");
    646 	return;
    647     }
    648     locked = 0;
    649 }
    650 
    651 void kadmin_delprinc(argc, argv)
    652     int argc;
    653     char *argv[];
    654 {
    655     kadm5_ret_t retval;
    656     krb5_principal princ;
    657     char *canon;
    658     char reply[32];
    659 
    660     if (! (argc == 2 ||
    661 	   (argc == 3 && !strcmp("-force", argv[1])))) {
    662 	fprintf(stderr, "%s: delete_principal [-force] %s\n",
    663 			gettext("usage"), gettext("principal"));
    664 	return;
    665     }
    666     retval = kadmin_parse_name(argv[argc - 1], &princ);
    667     if (retval) {
    668 	com_err("delete_principal", retval,
    669 			gettext("while parsing principal name"));
    670 	return;
    671     }
    672     retval = krb5_unparse_name(context, princ, &canon);
    673     if (retval) {
    674 	com_err("delete_principal", retval,
    675 			gettext("while canonicalizing principal"));
    676 	krb5_free_principal(context, princ);
    677 	return;
    678     }
    679     if (argc == 2) {
    680 	printf(gettext("Are you sure you want to delete "
    681 			    "the principal \"%s\"? (yes/no): "), canon);
    682 	fgets(reply, sizeof (reply), stdin);
    683 		if (strncmp(gettext("yes\n"), reply, sizeof (reply)) &&
    684 			strncmp(gettext("y\n"), reply, sizeof (reply)) &&
    685 			strncmp(gettext("Y\n"), reply, sizeof (reply))) {
    686 			fprintf(stderr,
    687 				gettext("Principal \"%s\" not deleted\n"),
    688 				canon);
    689 	    free(canon);
    690 	    krb5_free_principal(context, princ);
    691 	    return;
    692 	}
    693     }
    694     retval = kadm5_delete_principal(handle, princ);
    695     krb5_free_principal(context, princ);
    696     if (retval) {
    697 	com_err("delete_principal", retval,
    698 			gettext("while deleting principal \"%s\""), canon);
    699 	free(canon);
    700 	return;
    701     }
    702     printf(gettext("Principal \"%s\" deleted.\n"), canon);
    703 	printf(gettext("Make sure that you have removed this principal "
    704 			"from all ACLs before reusing.\n"));
    705     free(canon);
    706     return;
    707 }
    708 
    709 void kadmin_cpw(argc, argv)
    710     int argc;
    711     char *argv[];
    712 {
    713     kadm5_ret_t retval;
    714     static char newpw[1024];
    715     static char prompt1[1024], prompt2[1024];
    716     char *canon;
    717     char *pwarg = NULL;
    718     int n_ks_tuple = 0, randkey = 0;
    719     krb5_boolean keepold = FALSE;
    720     krb5_key_salt_tuple *ks_tuple = NULL;
    721     krb5_principal princ;
    722     char **db_args = NULL;
    723     int db_args_size = 0;
    724     int local_kadmin = 0;
    725 
    726     local_kadmin = (strcmp(whoami, KADMIN_LOCAL_NAME) == 0);
    727 
    728     if (argc < 2) {
    729 	goto usage;
    730     }
    731     for (argv++, argc--; argc > 1; argc--, argv++) {
    732 	if (!strcmp("-x", *argv)) {
    733 	    argc--;
    734 	    if (argc < 1) {
    735 		fprintf(stderr, gettext("change_password: missing db argument\n"));
    736 		goto usage;
    737 	    }
    738 	    db_args_size++;
    739 	    {
    740 		char **temp = realloc(db_args, sizeof(char*) * (db_args_size+1)); /* one for NULL */
    741 		if (temp == NULL) {
    742 		    fprintf(stderr, gettext("change_password: Not enough memory\n"));
    743 		    free(db_args), db_args = NULL;
    744 		    exit(1);
    745 		}
    746 
    747 		db_args = temp;
    748 	    }
    749 	    db_args[db_args_size-1] = *++argv;
    750 	    db_args[db_args_size]   = NULL;
    751 	    continue;
    752 	}
    753 	if (!strcmp("-pw", *argv)) {
    754 	    argc--;
    755 	    if (argc < 1) {
    756 		fprintf(stderr, "change_password: %s",
    757 			gettext("missing password arg\n"));
    758 		goto usage;
    759 	    }
    760 	    pwarg = *++argv;
    761 	    continue;
    762 	}
    763 	if (!strcmp("-randkey", *argv)) {
    764 	    randkey++;
    765 	    continue;
    766 	}
    767 	if (!strcmp("-keepold", *argv)) {
    768 	    keepold = TRUE;
    769 	    continue;
    770 	}
    771 	if (!strcmp("-e", *argv)) {
    772 	    argc--;
    773 	    if (argc < 1) {
    774 		fprintf(stderr, "change_password: %s",
    775 			gettext("missing keysaltlist arg\n"));
    776 		goto usage;
    777 	    }
    778 	    retval = krb5_string_to_keysalts(*++argv, ", \t", ":.-", 0,
    779 					     &ks_tuple, &n_ks_tuple);
    780 	    if (retval) {
    781 		com_err("change_password", retval,
    782 			gettext("while parsing keysalts %s"), *argv);
    783 		return;
    784 	    }
    785 	    continue;
    786 	}
    787 	goto usage;
    788     }
    789     if (*argv == NULL) {
    790 	com_err("change_password", 0, "missing principal name");
    791 	goto usage;
    792     }
    793     retval = kadmin_parse_name(*argv, &princ);
    794     if (retval) {
    795 	com_err("change_password", retval,
    796 		gettext("while parsing principal name"));
    797 	if (ks_tuple != NULL)
    798 	    free(ks_tuple);
    799 	if (db_args) free(db_args);
    800 	goto usage;
    801     }
    802     retval = krb5_unparse_name(context, princ, &canon);
    803     if (retval) {
    804 		com_err("change_password", retval,
    805 			gettext("while canonicalizing principal"));
    806 	krb5_free_principal(context, princ);
    807 	if (ks_tuple != NULL)
    808 	    free(ks_tuple);
    809 	if (db_args) free(db_args);
    810 	return;
    811     }
    812     if (pwarg != NULL) {
    813 	if (keepold || ks_tuple != NULL) {
    814 	    retval = kadm5_chpass_principal_3(handle, princ, keepold,
    815 					      n_ks_tuple, ks_tuple, pwarg);
    816 	    if (ks_tuple != NULL)
    817 		free(ks_tuple);
    818 	} else {
    819 	    retval = kadm5_chpass_principal(handle, princ, pwarg);
    820 	}
    821 	krb5_free_principal(context, princ);
    822 	if (retval) {
    823 	    com_err("change_password", retval,
    824 				gettext("while changing password for \"%s\"."),
    825 				canon);
    826 	    free(canon);
    827 	    if (db_args) free(db_args);
    828 	    return;
    829 	}
    830 		printf(gettext("Password for \"%s\" changed.\n"), canon);
    831 	free(canon);
    832 	if (db_args) free(db_args);
    833 	return;
    834     } else if (randkey) {
    835 	if (keepold || ks_tuple != NULL || local_kadmin) {
    836 	    retval = kadm5_randkey_principal_3(handle, princ, keepold,
    837 					       n_ks_tuple, ks_tuple,
    838 					       NULL, NULL);
    839 	    if (ks_tuple != NULL)
    840 		free(ks_tuple);
    841 	} else {
    842 	    retval = kadm5_randkey_principal(handle, princ, NULL, NULL);
    843 	}
    844 	krb5_free_principal(context, princ);
    845 	if (retval) {
    846 	    com_err("change_password", retval,
    847 				gettext("while randomizing key for \"%s\"."),
    848 				canon);
    849 	    free(canon);
    850 	    if (db_args) free(db_args);
    851 	    return;
    852 	}
    853 	printf(gettext("Key for \"%s\" randomized.\n"), canon);
    854 	free(canon);
    855 	if (db_args) free(db_args);
    856 	return;
    857     } else if (argc == 1) {
    858 	unsigned int i = sizeof (newpw) - 1;
    859 
    860 		snprintf(prompt1, sizeof (prompt1),
    861 			gettext("Enter password for principal \"%.900s\""),
    862 			*argv);
    863 		snprintf(prompt2, sizeof (prompt2),
    864 			gettext("Re-enter password for principal \"%.900s\""),
    865 			*argv);
    866 	retval = krb5_read_password(context, prompt1, prompt2,
    867 				    newpw, &i);
    868 	if (retval) {
    869 	    com_err("change_password", retval,
    870 				gettext("while reading password for \"%s\"."),
    871 				canon);
    872 	    free(canon);
    873 	    if (ks_tuple != NULL)
    874 		free(ks_tuple);
    875 	    krb5_free_principal(context, princ);
    876 	    if (db_args) free(db_args);
    877 	    return;
    878 	}
    879 	if (keepold || ks_tuple != NULL) {
    880 	    retval = kadm5_chpass_principal_3(handle, princ, keepold,
    881 					      n_ks_tuple, ks_tuple,
    882 					      newpw);
    883 	    if (ks_tuple != NULL)
    884 		free(ks_tuple);
    885 	} else {
    886 	    retval = kadm5_chpass_principal(handle, princ, newpw);
    887 	}
    888 	krb5_free_principal(context, princ);
    889 	memset(newpw, 0, sizeof (newpw));
    890 	if (retval) {
    891 	    com_err("change_password", retval,
    892 				gettext("while changing password for \"%s\"."),
    893 				canon);
    894 	    free(canon);
    895 	    if (db_args) free(db_args);
    896 	    return;
    897 	}
    898 		printf(gettext("Password for \"%s\" changed.\n"), canon);
    899 	free(canon);
    900 	if (db_args) free(db_args);
    901 	return;
    902     } else {
    903 	free(canon);
    904 	krb5_free_principal(context, princ);
    905     usage:
    906 	if (ks_tuple != NULL)
    907 	    free(ks_tuple);
    908 		fprintf(stderr, "%s: change_password [-randkey] [-keepold] "
    909 			"[-e keysaltlist] [-pw password] %s\n",
    910 			gettext("usage"), gettext("principal"));
    911 	return;
    912     }
    913 }
    914 
    915 static void
    916 kadmin_free_tl_data(kadm5_principal_ent_t princ)
    917 {
    918     krb5_tl_data *tl_data = princ->tl_data;
    919     int n_tl_data         = princ->n_tl_data;
    920     int i;
    921 
    922     princ->n_tl_data = 0;
    923     princ->tl_data   = NULL;
    924 
    925     for (i = 0; tl_data && (i < n_tl_data); i++) {
    926 	krb5_tl_data *next = tl_data->tl_data_next;
    927 	if (tl_data->tl_data_contents)
    928 	    free(tl_data->tl_data_contents);
    929 	free(tl_data);
    930 	tl_data = next;
    931     }
    932 }
    933 
    934 #define KRB5_TL_DB_ARGS 0x7fff
    935 static int
    936 kadmin_parse_princ_args(argc, argv, oprinc, mask, pass, randkey,
    937 			ks_tuple, n_ks_tuple, caller)
    938     int argc;
    939     char *argv[];
    940     kadm5_principal_ent_t oprinc;
    941     long *mask;
    942     char **pass;
    943     int *randkey;
    944     krb5_key_salt_tuple **ks_tuple;
    945     int *n_ks_tuple;
    946     char *caller;
    947 {
    948     int i, j, attrib_set;
    949     time_t date;
    950     time_t now;
    951     krb5_error_code retval;
    952     krb5_tl_data *tl_data, *tail = NULL;
    953 
    954     *mask = 0;
    955     *pass = NULL;
    956     *n_ks_tuple = 0;
    957     *ks_tuple = NULL;
    958     time(&now);
    959     *randkey = 0;
    960     for (i = 1; i < argc - 1; i++) {
    961 	attrib_set = 0;
    962 	if (strlen(argv[i]) == 2 &&
    963 	    !strcmp("-x",argv[i])) {
    964 	    if (++i > argc - 2)
    965 		return -1;
    966 
    967 	    tl_data = malloc(sizeof(krb5_tl_data));
    968 	    if (tl_data == NULL) {
    969 		fprintf(stderr, gettext("Not enough memory\n"));
    970 		return ENOMEM;
    971 	    }
    972 
    973 	    memset(tl_data, 0, sizeof(krb5_tl_data));
    974 	    tl_data->tl_data_type = KRB5_TL_DB_ARGS;
    975 	    tl_data->tl_data_length  = strlen(argv[i])+1;
    976 	    tl_data->tl_data_contents = (unsigned char*)strdup(argv[i]);
    977 
    978 	    if (tail) {
    979 		tail->tl_data_next = tl_data;
    980 	    } else {
    981 		oprinc->tl_data = tl_data;
    982 	    }
    983 	    tail = tl_data;
    984 	    oprinc->n_tl_data++;
    985 
    986 	    if (tl_data->tl_data_contents == NULL) {
    987 		fprintf(stderr, gettext("Not enough memory\n"));
    988 		return ENOMEM;
    989 	    }
    990 	    *mask |= KADM5_TL_DATA;
    991 	    continue;
    992 	}
    993 	if (strlen(argv[i]) == 7 &&
    994 	    !strcmp("-expire", argv[i])) {
    995 	    if (++i > argc - 2)
    996 		return -1;
    997 	    else {
    998 		date = get_date(argv[i]);
    999  		if (date == (time_t)-1) {
   1000 		     fprintf(stderr,
   1001 						gettext("Invalid date "
   1002 							"specification "
   1003 							"\"%s\".\n"),
   1004 			     argv[i]);
   1005 		     return -1;
   1006  		}
   1007 		oprinc->princ_expire_time = date;
   1008 		*mask |= KADM5_PRINC_EXPIRE_TIME;
   1009 		continue;
   1010 	    }
   1011 	}
   1012 	if (strlen(argv[i]) == 9 &&
   1013 	    !strcmp("-pwexpire", argv[i])) {
   1014 	    if (++i > argc - 2)
   1015 		return -1;
   1016 	    else {
   1017 		date = get_date(argv[i]);
   1018  		if (date == (time_t)-1) {
   1019 		     fprintf(stderr,
   1020 						gettext("Invalid date "
   1021 							"specification "
   1022 							"\"%s\".\n"),
   1023 			     argv[i]);
   1024 		     return -1;
   1025  		}
   1026 		oprinc->pw_expiration = date;
   1027 		*mask |= KADM5_PW_EXPIRATION;
   1028 		continue;
   1029 	    }
   1030 	}
   1031 	if (strlen(argv[i]) == 8 &&
   1032 	    !strcmp("-maxlife", argv[i])) {
   1033 	    if (++i > argc - 2)
   1034 		return -1;
   1035 	    else {
   1036 		date = get_date(argv[i]);
   1037  		if (date == (time_t)-1) {
   1038 					fprintf(stderr,
   1039 						gettext("Invalid date "
   1040 							"specification "
   1041 							"\"%s\".\n"),
   1042 			     argv[i]);
   1043 		     return -1;
   1044  		}
   1045 				if (date <= now) {
   1046 					fprintf(stderr,
   1047 						gettext("Date specified is "
   1048 							"in the past "
   1049 							"\"%s\".\n"),
   1050 						argv[i]);
   1051 					return (-1);
   1052 				}
   1053 		oprinc->max_life = date - now;
   1054 		*mask |= KADM5_MAX_LIFE;
   1055 		continue;
   1056 	    }
   1057 	}
   1058 	if (strlen(argv[i]) == 13 &&
   1059 	    !strcmp("-maxrenewlife", argv[i])) {
   1060 	    if (++i > argc - 2)
   1061 		return -1;
   1062 	    else {
   1063 		date = get_date(argv[i]);
   1064  		if (date == (time_t)-1) {
   1065 					fprintf(stderr,
   1066 						gettext("Invalid date "
   1067 							"specification "
   1068 							"\"%s\".\n"),
   1069 			     argv[i]);
   1070 		     return -1;
   1071  		}
   1072 				if (date <= now) {
   1073 					fprintf(stderr,
   1074 						gettext("Date specified is "
   1075 							"in the past "
   1076 							"\"%s\".\n"),
   1077 						argv[i]);
   1078 					return (-1);
   1079 				}
   1080 		oprinc->max_renewable_life = date - now;
   1081 		*mask |= KADM5_MAX_RLIFE;
   1082 		continue;
   1083 	    }
   1084 	}
   1085 	if (strlen(argv[i]) == 5 &&
   1086 	    !strcmp("-kvno", argv[i])) {
   1087 	    if (++i > argc - 2)
   1088 		return -1;
   1089 	    else {
   1090 		oprinc->kvno = atoi(argv[i]);
   1091 		*mask |= KADM5_KVNO;
   1092 		continue;
   1093 	    }
   1094 	}
   1095 	if (strlen(argv[i]) == 7 &&
   1096 	    !strcmp("-policy", argv[i])) {
   1097 	    if (++i > argc - 2)
   1098 		return -1;
   1099 	    else {
   1100 		oprinc->policy = argv[i];
   1101 		*mask |= KADM5_POLICY;
   1102 		continue;
   1103 	    }
   1104 	}
   1105 	if (strlen(argv[i]) == 12 &&
   1106 	    !strcmp("-clearpolicy", argv[i])) {
   1107 	    oprinc->policy = NULL;
   1108 	    *mask |= KADM5_POLICY_CLR;
   1109 	    continue;
   1110 	}
   1111 	if (strlen(argv[i]) == 3 &&
   1112 	    !strcmp("-pw", argv[i])) {
   1113 	    if (++i > argc - 2)
   1114 		return -1;
   1115 	    else {
   1116 		*pass = argv[i];
   1117 		continue;
   1118 	    }
   1119 	}
   1120 	if (strlen(argv[i]) == 8 &&
   1121 	    !strcmp("-randkey", argv[i])) {
   1122 	    ++*randkey;
   1123 	    continue;
   1124 	}
   1125 	if (!strcmp("-e", argv[i])) {
   1126 	    if (++i > argc - 2)
   1127 		return -1;
   1128 	    else {
   1129 		retval = krb5_string_to_keysalts(argv[i], ", \t", ":.-", 0,
   1130 						 ks_tuple, n_ks_tuple);
   1131 		if (retval) {
   1132 		    com_err(caller, retval,
   1133 			    gettext("while parsing keysalts %s"), argv[i]);
   1134 		    return -1;
   1135 		}
   1136 	    }
   1137 	    continue;
   1138 	}
   1139 	for (j = 0; j < sizeof (flags) / sizeof (struct pflag); j++) {
   1140 	    if (strlen(argv[i]) == flags[j].flaglen + 1 &&
   1141 		!strcmp(flags[j].flagname,
   1142 			&argv[i][1] /* strip off leading + or - */)) {
   1143 		if ((flags[j].set && argv[i][0] == '-') ||
   1144 		    (!flags[j].set && argv[i][0] == '+')) {
   1145 		    oprinc->attributes |= flags[j].theflag;
   1146 		    *mask |= KADM5_ATTRIBUTES;
   1147 		    attrib_set++;
   1148 		    break;
   1149 		} else if ((flags[j].set && argv[i][0] == '+') ||
   1150 			   (!flags[j].set && argv[i][0] == '-')) {
   1151 		    oprinc->attributes &= ~flags[j].theflag;
   1152 		    *mask |= KADM5_ATTRIBUTES;
   1153 		    attrib_set++;
   1154 		    break;
   1155 		} else {
   1156 		    return -1;
   1157 		}
   1158 	    }
   1159 	}
   1160 	if (!attrib_set)
   1161 	    return -1;		/* nothing was parsed */
   1162     }
   1163     if (i != argc - 1) {
   1164 	return -1;
   1165     }
   1166     retval = kadmin_parse_name(argv[i], &oprinc->principal);
   1167     if (retval) {
   1168 	com_err(caller, retval, gettext("while parsing principal"));
   1169 	return -1;
   1170     }
   1171     return 0;
   1172 }
   1173 
   1174 static void
   1175 kadmin_addprinc_usage(func)
   1176     char *func;
   1177 {
   1178 	fprintf(stderr, "%s: %s %s\n", gettext("usage"), func,
   1179 		gettext("[options] principal"));
   1180 	fprintf(stderr, gettext("\toptions are:\n"));
   1181 	fprintf(stderr, "\t\t[-expire expdate] [-pwexpire pwexpdate] "
   1182 		"[-maxlife maxtixlife]\n\t\t[-kvno kvno] [-policy policy] "
   1183 		"[-randkey] [-pw password]\n\t\t[-maxrenewlife maxrenewlife] "
   1184 		"[-e keysaltlist] [{+|-}attribute]\n");
   1185 	fprintf(stderr, gettext("\tattributes are:\n"));
   1186      fprintf(stderr, "%s%s%s",
   1187 		"\t\tallow_postdated allow_forwardable allow_tgs_req "
   1188 		"allow_renewable\n",
   1189 		"\t\tallow_proxiable allow_dup_skey allow_tix "
   1190 		"requires_preauth\n",
   1191 		"\t\trequires_hwauth needchange allow_svr "
   1192 		"password_changing_service\n");
   1193 }
   1194 
   1195 static void
   1196 kadmin_modprinc_usage(func)
   1197     char *func;
   1198 {
   1199 	fprintf(stderr, "%s: %s %s\n", gettext("usage"), func,
   1200 		gettext("[options] principal"));
   1201 	fprintf(stderr, gettext("\toptions are:\n"));
   1202 	fprintf(stderr, "\t\t[-expire expdate] [-pwexpire pwexpdate] "
   1203 		"[-maxlife maxtixlife]\n\t\t[-kvno kvno] [-policy policy] "
   1204 		"[-clearpolicy]\n\t\t[-maxrenewlife maxrenewlife] "
   1205 		"[{+|-}attribute]\n");
   1206 	fprintf(stderr, gettext("\tattributes are:\n"));
   1207      fprintf(stderr, "%s%s%s",
   1208 		"\t\tallow_postdated allow_forwardable allow_tgs_req "
   1209 		"allow_renewable\n",
   1210 		"\t\tallow_proxiable allow_dup_skey allow_tix "
   1211 		"requires_preauth\n",
   1212 		"\t\trequires_hwauth needchange allow_svr "
   1213 		"password_changing_service\n");
   1214 }
   1215 
   1216 void kadmin_addprinc(argc, argv)
   1217     int argc;
   1218     char *argv[];
   1219 {
   1220     kadm5_principal_ent_rec princ, dprinc;
   1221     kadm5_policy_ent_rec defpol;
   1222     long mask;
   1223     int randkey = 0, i;
   1224     int n_ks_tuple;
   1225     krb5_key_salt_tuple *ks_tuple;
   1226     char *pass, *canon;
   1227     krb5_error_code retval;
   1228     static char newpw[1024], dummybuf[256];
   1229     static char prompt1[1024], prompt2[1024];
   1230     int local_kadmin = 0;
   1231 
   1232     local_kadmin = (strcmp(whoami, KADMIN_LOCAL_NAME) == 0);
   1233 
   1234     if (dummybuf[0] == 0) {
   1235 	for (i = 0; i < 256; i++)
   1236 	    dummybuf[i] = (i+1) % 256;
   1237     }
   1238 
   1239     /* Zero all fields in request structure */
   1240     memset(&princ, 0, sizeof(princ));
   1241     memset(&dprinc, 0, sizeof(dprinc));
   1242 
   1243     princ.attributes = dprinc.attributes = 0;
   1244     if (kadmin_parse_princ_args(argc, argv,
   1245 				&princ, &mask, &pass, &randkey,
   1246 				&ks_tuple, &n_ks_tuple,
   1247 				"add_principal")) {
   1248 	kadmin_addprinc_usage("add_principal");
   1249 	kadmin_free_tl_data(&princ); /* need to free ks_tuple also??? */
   1250 	return;
   1251     }
   1252 
   1253     retval = krb5_unparse_name(context, princ.principal, &canon);
   1254     if (retval) {
   1255 	com_err("add_principal",
   1256 		retval, gettext("while canonicalizing principal"));
   1257 	krb5_free_principal(context, princ.principal);
   1258 	if (ks_tuple != NULL)
   1259 	    free(ks_tuple);
   1260 	kadmin_free_tl_data(&princ);
   1261 	return;
   1262     }
   1263 
   1264     /*
   1265      * If -policy was not specified, and -clearpolicy was not
   1266      * specified, and the policy "default" exists, assign it.  If
   1267      * -clearpolicy was specified, then KADM5_POLICY_CLR should be
   1268      * unset, since it is never valid for kadm5_create_principal.
   1269      */
   1270     if ((! (mask & KADM5_POLICY)) &&
   1271 	(! (mask & KADM5_POLICY_CLR))) {
   1272 	if (! kadm5_get_policy(handle, "default", &defpol)) {
   1273 	    fprintf(stderr,
   1274 		    gettext("NOTICE: no policy specified for %s; assigning \"default\"\n"),
   1275 		    canon);
   1276 	    princ.policy = "default";
   1277 	    mask |= KADM5_POLICY;
   1278 	    (void) kadm5_free_policy_ent(handle, &defpol);
   1279 	} else
   1280 	    fprintf(stderr,
   1281 		    gettext("WARNING: no policy specified for %s; defaulting to no policy\n"),
   1282 		    canon);
   1283     }
   1284     mask &= ~KADM5_POLICY_CLR;
   1285 
   1286     /*
   1287      * Set 'notix' for randkey principals and also for principals which have
   1288      * specified flag options on the cmdline. This is because we want to apply
   1289      * generic flag settings from 'default_principal_flags' first (during
   1290      * principal creation), followed by a kadm5_modify_principal() which
   1291      * correctly applies the cli flag options. So, we do *not* want any tix
   1292      * issued in the interim.
   1293      */
   1294     if (randkey || (mask & KADM5_ATTRIBUTES))
   1295 	princ.attributes |= KRB5_KDB_DISALLOW_ALL_TIX;
   1296 
   1297     if (randkey) {
   1298 	mask |= KADM5_ATTRIBUTES;
   1299 	pass = dummybuf;
   1300     } else if (pass == NULL) {
   1301 	unsigned int sz = sizeof (newpw) - 1;
   1302 	snprintf(prompt1, sizeof (prompt1),
   1303 		gettext("Enter password for principal \"%.900s\""),
   1304 		canon);
   1305 	snprintf(prompt2, sizeof (prompt1),
   1306 		gettext("Re-enter password for principal \"%.900s\""),
   1307 		canon);
   1308 	retval = krb5_read_password(context, prompt1, prompt2,
   1309 				    newpw, &sz);
   1310 	if (retval) {
   1311 	    com_err("add_principal", retval,
   1312 		gettext("while reading password for \"%s\"."), canon);
   1313 	    free(canon);
   1314 	    krb5_free_principal(context, princ.principal);
   1315 	    kadmin_free_tl_data(&princ);
   1316 	    return;
   1317 	}
   1318 	pass = newpw;
   1319     }
   1320     mask |= KADM5_PRINCIPAL;
   1321 
   1322     /*
   1323      * If the client being used is local, always use the new
   1324      * API so we get the full set of enctype support.
   1325      */
   1326     if (ks_tuple != NULL || local_kadmin) {
   1327 	retval = kadm5_create_principal_3(handle, &princ, mask,
   1328 					  n_ks_tuple, ks_tuple, pass);
   1329     } else {
   1330 	retval = kadm5_create_principal(handle, &princ, mask, pass);
   1331     }
   1332     if (retval) {
   1333 	com_err("add_principal", retval,
   1334 		gettext("while creating \"%s\"."), canon);
   1335 	krb5_free_principal(context, princ.principal);
   1336 	free(canon);
   1337 	if (ks_tuple != NULL)
   1338 	    free(ks_tuple);
   1339 	kadmin_free_tl_data(&princ);
   1340 	return;
   1341     }
   1342     if (randkey) {		/* more special stuff for -randkey */
   1343 	if (ks_tuple != NULL || local_kadmin) {
   1344 	    retval = kadm5_randkey_principal_3(handle, princ.principal,
   1345 					       FALSE,
   1346 					       n_ks_tuple, ks_tuple,
   1347 					       NULL, NULL);
   1348 	} else {
   1349 	    retval = kadm5_randkey_principal(handle, princ.principal,
   1350 					     NULL, NULL);
   1351 	}
   1352 	if (retval) {
   1353 	    com_err("add_principal", retval,
   1354 		gettext("while randomizing key for \"%s\"."), canon);
   1355 	    krb5_free_principal(context, princ.principal);
   1356 	    free(canon);
   1357 	    if (ks_tuple != NULL)
   1358 		free(ks_tuple);
   1359 	    kadmin_free_tl_data(&princ);
   1360 	    return;
   1361 	}
   1362     }
   1363 
   1364     /*
   1365      * We now retrieve the intersection set of the generic flag settings and
   1366      * the ones specified on the cli & re-parse the princ args, just to make
   1367      * sure we account for conflicts between 'default_principal_flags' and
   1368      * the cmdline flag args. While we are here, also clear 'notix'.
   1369      */
   1370     if (randkey || (mask & KADM5_ATTRIBUTES)) {
   1371 	retval = kadm5_get_principal(handle, princ.principal, &dprinc,
   1372 			KADM5_PRINCIPAL_NORMAL_MASK);
   1373         if (retval == 0) {
   1374 	    if (dprinc.attributes != 0)
   1375 		princ.attributes = dprinc.attributes;
   1376 	} else {
   1377 	    com_err("add_principal", retval,
   1378 		gettext("while doing a get_principal on \"%s\"."), canon);
   1379 	    printf(gettext("\nWarning: Principal \"%s\" could have incomplete "
   1380 		"flag settings, as a result of a failed get_principal.\n"
   1381 		"Check the 'default_principal_flags' setting in kdc.conf(4).\n"
   1382 		"If there is a mismatch, use modprinc in kadmin(1M) to rectify "
   1383 		"the same.\n\n"), canon);
   1384 	}
   1385 
   1386 	/*
   1387 	 * Solaris Kerberos: We unset KRB5_KDB_DISALLOW_ALL_TIX before
   1388 	 * kadmin_parse_princ_args is called, because -allow_tix may
   1389 	 * have been an argument.  We still have to unset here because
   1390 	 * kadmin_parse_princ_args will not reset the attribute unless
   1391 	 * it is was explicity defined.
   1392 	 */
   1393 	princ.attributes &= ~KRB5_KDB_DISALLOW_ALL_TIX;
   1394 	(void) kadmin_parse_princ_args(argc, argv, &princ, &mask, &pass,
   1395 			&randkey, &ks_tuple, &n_ks_tuple, "add_principal");
   1396 	mask = KADM5_ATTRIBUTES;
   1397 	retval = kadm5_modify_principal(handle, &princ, mask);
   1398 	if (retval) {
   1399 	    com_err("add_principal", retval,
   1400 		gettext("while doing a modify_principal to restore flag "
   1401 			"settings for \"%s\"."), canon);
   1402 	    krb5_free_principal(context, princ.principal);
   1403 	    free(canon);
   1404 	    if (ks_tuple != NULL)
   1405 		free(ks_tuple);
   1406 	    kadmin_free_tl_data(&princ);
   1407 	    return;
   1408 	}
   1409     }
   1410     krb5_free_principal(context, princ.principal);
   1411 	printf(gettext("Principal \"%s\" created.\n"), canon);
   1412     if (ks_tuple != NULL)
   1413 	free(ks_tuple);
   1414     free(canon);
   1415     kadmin_free_tl_data(&princ);
   1416 
   1417 }
   1418 
   1419 void kadmin_modprinc(argc, argv)
   1420     int argc;
   1421     char *argv[];
   1422 {
   1423     kadm5_principal_ent_rec princ, oldprinc;
   1424     krb5_principal kprinc;
   1425     long mask;
   1426     krb5_error_code retval;
   1427     char *pass, *canon;
   1428     int randkey = 0;
   1429     int n_ks_tuple = 0;
   1430     krb5_key_salt_tuple *ks_tuple;
   1431 
   1432     if (argc < 2) {
   1433 	kadmin_modprinc_usage("modify_principal");
   1434 	return;
   1435     }
   1436 
   1437     memset(&oldprinc, 0, sizeof(oldprinc));
   1438     memset(&princ, 0, sizeof(princ));
   1439 
   1440     retval = kadmin_parse_name(argv[argc - 1], &kprinc);
   1441     if (retval) {
   1442 	com_err("modify_principal", retval,
   1443 			gettext("while parsing principal"));
   1444 	return;
   1445     }
   1446     retval = krb5_unparse_name(context, kprinc, &canon);
   1447     if (retval) {
   1448 	com_err("modify_principal", retval,
   1449 			gettext("while canonicalizing principal"));
   1450 	krb5_free_principal(context, kprinc);
   1451 	return;
   1452     }
   1453     retval = kadm5_get_principal(handle, kprinc, &oldprinc,
   1454 				 KADM5_PRINCIPAL_NORMAL_MASK);
   1455     krb5_free_principal(context, kprinc);
   1456     if (retval) {
   1457 	com_err("modify_principal", retval,
   1458 			gettext("while getting \"%s\"."), canon);
   1459 	free(canon);
   1460 	return;
   1461     }
   1462     princ.attributes = oldprinc.attributes;
   1463     kadm5_free_principal_ent(handle, &oldprinc);
   1464     retval = kadmin_parse_princ_args(argc, argv,
   1465 				     &princ, &mask,
   1466 				     &pass, &randkey,
   1467 				     &ks_tuple, &n_ks_tuple,
   1468 				     "modify_principal");
   1469     if (ks_tuple != NULL) {
   1470 	free(ks_tuple);
   1471 	kadmin_modprinc_usage("modify_principal");
   1472 	free(canon);
   1473 	kadmin_free_tl_data(&princ);
   1474 	return;
   1475     }
   1476     if (retval) {
   1477 	kadmin_modprinc_usage("modify_principal");
   1478 	free(canon);
   1479 	kadmin_free_tl_data(&princ);
   1480 	return;
   1481     }
   1482     if (randkey) {
   1483 		fprintf(stderr, "modify_principal: -randkey %s ",
   1484 			gettext("not allowed\n"));
   1485 	krb5_free_principal(context, princ.principal);
   1486 	free(canon);
   1487 	kadmin_free_tl_data(&princ);
   1488 	return;
   1489     }
   1490     if (pass) {
   1491 	fprintf(stderr,
   1492 		"modify_principal: -pw %s change_password\n",
   1493 		gettext("not allowed; use"));
   1494 	krb5_free_principal(context, princ.principal);
   1495 	free(canon);
   1496 	kadmin_free_tl_data(&princ);
   1497 	return;
   1498     }
   1499     retval = kadm5_modify_principal(handle, &princ, mask);
   1500     krb5_free_principal(context, princ.principal);
   1501     if (retval) {
   1502 	com_err("modify_principal", retval,
   1503 			gettext("while modifying \"%s\"."), canon);
   1504 	free(canon);
   1505 	kadmin_free_tl_data(&princ);
   1506 	return;
   1507     }
   1508 	printf(gettext("Principal \"%s\" modified.\n"), canon);
   1509     kadmin_free_tl_data(&princ);
   1510     free(canon);
   1511 }
   1512 
   1513 void kadmin_getprinc(argc, argv)
   1514     int argc;
   1515     char *argv[];
   1516 {
   1517     kadm5_principal_ent_rec dprinc;
   1518     krb5_principal princ;
   1519     krb5_error_code retval;
   1520     char *canon, *modcanon;
   1521     int i;
   1522 
   1523     if (! (argc == 2 ||
   1524 	   (argc == 3 && !strcmp("-terse", argv[1])))) {
   1525 		fprintf(stderr, "%s: get_principal [-terse] %s\n",
   1526 			gettext("usage"), gettext("principal"));
   1527 	return;
   1528     }
   1529 
   1530 
   1531     memset(&dprinc, 0, sizeof(dprinc));
   1532     memset(&princ, 0, sizeof(princ));
   1533 
   1534     retval = kadmin_parse_name(argv[argc - 1], &princ);
   1535     if (retval) {
   1536 	com_err("get_principal", retval,
   1537 			gettext("while parsing principal"));
   1538 	return;
   1539     }
   1540     retval = krb5_unparse_name(context, princ, &canon);
   1541     if (retval) {
   1542 	com_err("get_principal", retval,
   1543 			gettext("while canonicalizing principal"));
   1544 	krb5_free_principal(context, princ);
   1545 	return;
   1546     }
   1547     retval = kadm5_get_principal(handle, princ, &dprinc,
   1548 				 KADM5_PRINCIPAL_NORMAL_MASK | KADM5_KEY_DATA);
   1549     krb5_free_principal(context, princ);
   1550     if (retval) {
   1551 	com_err("get_principal", retval,
   1552 			gettext("while retrieving \"%s\"."), canon);
   1553 	free(canon);
   1554 	return;
   1555     }
   1556     retval = krb5_unparse_name(context, dprinc.mod_name, &modcanon);
   1557     if (retval) {
   1558 	com_err("get_principal", retval,
   1559 			gettext("while unparsing modname"));
   1560 	kadm5_free_principal_ent(handle, &dprinc);
   1561 	free(canon);
   1562 	return;
   1563     }
   1564     if (argc == 2) {
   1565 		printf(gettext("Principal: %s\n"), canon);
   1566 		printf(gettext("Expiration date: %s\n"),
   1567 		    dprinc.princ_expire_time ?
   1568 		    strdate(dprinc.princ_expire_time) :
   1569 		    gettext("[never]"));
   1570 		printf(gettext("Last password change: %s\n"),
   1571 		    dprinc.last_pwd_change ?
   1572 		    strdate(dprinc.last_pwd_change) :
   1573 		    gettext("[never]"));
   1574 		printf(gettext("Password expiration date: %s\n"),
   1575 	       dprinc.pw_expiration ?
   1576 		    strdate(dprinc.pw_expiration) : gettext("[none]"));
   1577 		printf(gettext("Maximum ticket life: %s\n"),
   1578 		    strdur(dprinc.max_life));
   1579 		printf(gettext("Maximum renewable life: %s\n"),
   1580 		    strdur(dprinc.max_renewable_life));
   1581 		printf(gettext("Last modified: %s (%s)\n"),
   1582 		    strdate(dprinc.mod_date), modcanon);
   1583 		printf(gettext("Last successful authentication: %s\n"),
   1584 	       dprinc.last_success ? strdate(dprinc.last_success) :
   1585 		    gettext("[never]"));
   1586 		printf(gettext("Last failed authentication: %s\n"),
   1587 	       dprinc.last_failed ? strdate(dprinc.last_failed) :
   1588 		    gettext("[never]"));
   1589 		printf(gettext("Failed password attempts: %d\n"),
   1590 	       dprinc.fail_auth_count);
   1591 		printf(gettext("Number of keys: %d\n"), dprinc.n_key_data);
   1592 	for (i = 0; i < dprinc.n_key_data; i++) {
   1593 	    krb5_key_data *key_data = &dprinc.key_data[i];
   1594 	    char enctype[BUFSIZ], salttype[BUFSIZ];
   1595 
   1596 	    if (krb5_enctype_to_string(key_data->key_data_type[0],
   1597 				       enctype, sizeof(enctype)))
   1598 		snprintf(enctype, sizeof (enctype), gettext("<Encryption type 0x%x>"),
   1599 			key_data->key_data_type[0]);
   1600 	    printf("Key: vno %d, %s, ", key_data->key_data_kvno, enctype);
   1601 	    if (key_data->key_data_ver > 1) {
   1602 		if (krb5_salttype_to_string(key_data->key_data_type[1],
   1603 					    salttype, sizeof(salttype)))
   1604 		    snprintf(salttype, sizeof(salttype), gettext("<Salt type 0x%x>"),
   1605 			    key_data->key_data_type[1]);
   1606 		printf("%s\n", salttype);
   1607 	    } else
   1608 		printf(gettext("no salt\n"));
   1609 	}
   1610 
   1611 	printf(gettext("Attributes:"));
   1612 	for (i = 0; i < sizeof (prflags) / sizeof (char *); i++) {
   1613 	    if (dprinc.attributes & (krb5_flags) 1 << i)
   1614 		printf(" %s", prflags[i]);
   1615 	}
   1616 	printf("\n");
   1617 		printf(gettext("Policy: %s\n"),
   1618 		    dprinc.policy ? dprinc.policy : gettext("[none]"));
   1619     } else {
   1620 	printf("\"%s\"\t%d\t%d\t%d\t%d\t\"%s\"\t%d\t%d\t%d\t%d\t\"%s\""
   1621 	       "\t%d\t%d\t%d\t%d\t%d",
   1622 	       canon, dprinc.princ_expire_time, dprinc.last_pwd_change,
   1623 	       dprinc.pw_expiration, dprinc.max_life, modcanon,
   1624 	       dprinc.mod_date, dprinc.attributes, dprinc.kvno,
   1625 	       dprinc.mkvno, dprinc.policy ? dprinc.policy : gettext("[none]"),
   1626 	       dprinc.max_renewable_life, dprinc.last_success,
   1627 	       dprinc.last_failed, dprinc.fail_auth_count,
   1628 	       dprinc.n_key_data);
   1629 	for (i = 0; i < dprinc.n_key_data; i++)
   1630 	    printf("\t%d\t%d\t%d\t%d",
   1631 		   dprinc.key_data[i].key_data_ver,
   1632 		   dprinc.key_data[i].key_data_kvno,
   1633 		   dprinc.key_data[i].key_data_type[0],
   1634 		   dprinc.key_data[i].key_data_type[1]);
   1635 	printf("\n");
   1636     }
   1637     free(modcanon);
   1638     kadm5_free_principal_ent(handle, &dprinc);
   1639     free(canon);
   1640 }
   1641 
   1642 void kadmin_getprincs(argc, argv)
   1643     int argc;
   1644     char *argv[];
   1645 {
   1646     krb5_error_code retval;
   1647     char *expr, **names;
   1648     int i, count;
   1649 
   1650 	FILE *output;
   1651 	int fd;
   1652 	struct sigaction nsig, osig;
   1653 	sigset_t nmask, omask;
   1654 	int waitb;
   1655 
   1656     expr = NULL;
   1657     if (! (argc == 1 || (argc == 2 && (expr = argv[1])))) {
   1658 		fprintf(stderr, "%s: get_principals %s\n",
   1659 			gettext("usage"), gettext("[expression]"));
   1660 	return;
   1661     }
   1662     retval = kadm5_get_principals(handle, expr, &names, &count);
   1663     if (retval) {
   1664 	com_err("get_principals", retval,
   1665 			gettext("while retrieving list."));
   1666 	return;
   1667     }
   1668 
   1669 	/*
   1670 	 * Solaris:  the following code is used for paging
   1671 	 */
   1672 
   1673 	sigemptyset(&nmask);
   1674 	sigaddset(&nmask, SIGINT);
   1675 	sigprocmask(SIG_BLOCK, &nmask, &omask);
   1676 
   1677 	nsig.sa_handler = SIG_IGN;
   1678 	sigemptyset(&nsig.sa_mask);
   1679 	nsig.sa_flags = 0;
   1680 	sigaction(SIGINT, &nsig, &osig);
   1681 
   1682 	fd = ss_pager_create();
   1683 	output = fdopen(fd, "w");
   1684 
   1685 	sigprocmask(SIG_SETMASK, &omask, (sigset_t *)0);
   1686 
   1687     for (i = 0; i < count; i++)
   1688 	 fprintf(output, "%s\n", names[i]);
   1689 
   1690 	fclose(output);
   1691 
   1692 	wait(&waitb);
   1693 
   1694 	/* Solaris Kerberos:
   1695 	 * Restore the original handler for SIGINT
   1696 	 */
   1697 	if (sigaction(SIGINT, &osig, (struct sigaction *)0) == -1) {
   1698 		perror("sigaction");
   1699 	}
   1700 
   1701     kadm5_free_name_list(handle, names, count);
   1702 }
   1703 
   1704 static int
   1705 kadmin_parse_policy_args(argc, argv, policy, mask, caller)
   1706     int argc;
   1707     char *argv[];
   1708     kadm5_policy_ent_t policy;
   1709     long *mask;
   1710     char *caller;
   1711 {
   1712     int i;
   1713     time_t now;
   1714     time_t date;
   1715 
   1716     time(&now);
   1717     *mask = 0;
   1718     for (i = 1; i < argc - 1; i++) {
   1719 	if (strlen(argv[i]) == 8 &&
   1720 	    !strcmp(argv[i], "-maxlife")) {
   1721 	    if (++i > argc -2)
   1722 		return -1;
   1723 	    else {
   1724 		date = get_date(argv[i]);
   1725 		if (date == (time_t)-1) {
   1726 		    fprintf(stderr, gettext("Invalid date specification \"%s\".\n"),
   1727 			    argv[i]);
   1728 		    return -1;
   1729 		}
   1730 
   1731 				if (date <= now) {
   1732 					fprintf(stderr,
   1733 						gettext("Date specified is "
   1734 							"in the past "
   1735 							"\"%s\".\n"),
   1736 						argv[i]);
   1737 					return (-1);
   1738 				}
   1739 		policy->pw_max_life = date - now;
   1740 		*mask |= KADM5_PW_MAX_LIFE;
   1741 		continue;
   1742 	    }
   1743 	} else if (strlen(argv[i]) == 8 &&
   1744 		   !strcmp(argv[i], "-minlife")) {
   1745 	    if (++i > argc - 2)
   1746 		return -1;
   1747 	    else {
   1748 		date = get_date(argv[i]);
   1749 		if (date == (time_t)-1) {
   1750 		    fprintf(stderr, gettext("Invalid date specification \"%s\".\n"),
   1751 			    argv[i]);
   1752 		    return -1;
   1753 		}
   1754 
   1755 				if (date <= now) {
   1756 					fprintf(stderr,
   1757 						gettext("Date specified is "
   1758 							"in the past "
   1759 							"\"%s\".\n"),
   1760 						argv[i]);
   1761 					return (-1);
   1762 				}
   1763 		policy->pw_min_life = date - now;
   1764 		*mask |= KADM5_PW_MIN_LIFE;
   1765 		continue;
   1766 	    }
   1767 	} else if (strlen(argv[i]) == 10 &&
   1768 		   !strcmp(argv[i], "-minlength")) {
   1769 	    if (++i > argc - 2)
   1770 		return -1;
   1771 	    else {
   1772 		policy->pw_min_length = atoi(argv[i]);
   1773 		*mask |= KADM5_PW_MIN_LENGTH;
   1774 		continue;
   1775 	    }
   1776 	} else if (strlen(argv[i]) == 11 &&
   1777 		   !strcmp(argv[i], "-minclasses")) {
   1778 	    if (++i > argc - 2)
   1779 		return -1;
   1780 	    else {
   1781 		policy->pw_min_classes = atoi(argv[i]);
   1782 		*mask |= KADM5_PW_MIN_CLASSES;
   1783 		continue;
   1784 	    }
   1785 	} else if (strlen(argv[i]) == 8 &&
   1786 		   !strcmp(argv[i], "-history")) {
   1787 	    if (++i > argc - 2)
   1788 		return -1;
   1789 	    else {
   1790 		policy->pw_history_num = atoi(argv[i]);
   1791 		*mask |= KADM5_PW_HISTORY_NUM;
   1792 		continue;
   1793 	    }
   1794 	} else
   1795 	    return -1;
   1796     }
   1797     if (i != argc -1) {
   1798 	fprintf(stderr, gettext("%s: parser lost count!\n"), caller);
   1799 	return -1;
   1800     } else
   1801 	return 0;
   1802 }
   1803 
   1804 static void
   1805 kadmin_addmodpol_usage(func)
   1806     char *func;
   1807 {
   1808 	fprintf(stderr, "%s: %s %s\n", gettext("usage"), func,
   1809 		gettext("[options] policy"));
   1810 	fprintf(stderr, gettext("\toptions are:\n"));
   1811 	fprintf(stderr, "\t\t[-maxlife time] [-minlife time] "
   1812 		"[-minlength length]\n\t\t[-minclasses number] "
   1813 		"[-history number]\n");
   1814 }
   1815 
   1816 void kadmin_addpol(argc, argv)
   1817     int argc;
   1818     char *argv[];
   1819 {
   1820     krb5_error_code retval;
   1821     long mask;
   1822     kadm5_policy_ent_rec policy;
   1823 
   1824     memset(&policy, 0, sizeof(policy));
   1825     if (kadmin_parse_policy_args(argc, argv, &policy, &mask, "add_policy")) {
   1826 	kadmin_addmodpol_usage("add_policy");
   1827 	return;
   1828     } else {
   1829 	policy.policy = argv[argc - 1];
   1830 	mask |= KADM5_POLICY;
   1831 	retval = kadm5_create_policy(handle, &policy, mask);
   1832 	if (retval) {
   1833 			com_err("add_policy", retval,
   1834 				gettext("while creating policy \"%s\"."),
   1835 		    policy.policy);
   1836 	    return;
   1837 	}
   1838     }
   1839     return;
   1840 }
   1841 
   1842 void kadmin_modpol(argc, argv)
   1843     int argc;
   1844     char *argv[];
   1845 {
   1846     krb5_error_code retval;
   1847     long mask;
   1848     kadm5_policy_ent_rec policy;
   1849 
   1850     memset(&policy, 0, sizeof(policy));
   1851     if (kadmin_parse_policy_args(argc, argv, &policy, &mask,
   1852 				 "modify_policy")) {
   1853 	kadmin_addmodpol_usage("modify_policy");
   1854 	return;
   1855     } else {
   1856 	policy.policy = argv[argc - 1];
   1857 	retval = kadm5_modify_policy(handle, &policy, mask);
   1858 	if (retval) {
   1859 	    com_err("modify_policy", retval, gettext("while modifying policy \"%s\"."),
   1860 		    policy.policy);
   1861 	    return;
   1862 	}
   1863     }
   1864     return;
   1865 }
   1866 
   1867 void kadmin_delpol(argc, argv)
   1868     int argc;
   1869     char *argv[];
   1870 {
   1871     krb5_error_code retval;
   1872     char reply[32];
   1873 
   1874     if (! (argc == 2 ||
   1875 	   (argc == 3 && !strcmp("-force", argv[1])))) {
   1876 	fprintf(stderr, "%s: delete_policy [-force] %s\n",
   1877 			gettext("usage"), gettext("policy"));
   1878 	return;
   1879     }
   1880     if (argc == 2) {
   1881 		printf(gettext("Are you sure you want to delete the policy "
   1882 			    "\"%s\"? (yes/no): "), argv[1]);
   1883 	fgets(reply, sizeof (reply), stdin);
   1884 		if (strncmp(gettext("yes\n"), reply, sizeof (reply)) &&
   1885 			strncmp(gettext("y\n"), reply, sizeof (reply)) &&
   1886 			strncmp(gettext("Y\n"), reply, sizeof (reply))
   1887 			) {
   1888 			fprintf(stderr,
   1889 				gettext("Policy \"%s\" not deleted.\n"),
   1890 				argv[1]);
   1891 	    return;
   1892 	}
   1893     }
   1894     retval = kadm5_delete_policy(handle, argv[argc - 1]);
   1895     if (retval) {
   1896 		com_err("delete_policy:", retval,
   1897 			gettext("while deleting policy \"%s\""),
   1898 		argv[argc - 1]);
   1899 	return;
   1900     }
   1901     return;
   1902 }
   1903 
   1904 void kadmin_getpol(argc, argv)
   1905     int argc;
   1906     char *argv[];
   1907 {
   1908     krb5_error_code retval;
   1909     kadm5_policy_ent_rec policy;
   1910 
   1911     if (! (argc == 2 ||
   1912 	   (argc == 3 && !strcmp("-terse", argv[1])))) {
   1913 		fprintf(stderr, "%s: get_policy [-terse] %s\n",
   1914 			gettext("usage"), gettext("policy"));
   1915 	return;
   1916     }
   1917     retval = kadm5_get_policy(handle, argv[argc - 1], &policy);
   1918     if (retval) {
   1919 		com_err("get_policy", retval,
   1920 			gettext("while retrieving policy \"%s\"."),
   1921 		argv[argc - 1]);
   1922 	return;
   1923     }
   1924     if (argc == 2) {
   1925 		printf(gettext("Policy: %s\n"), policy.policy);
   1926 		printf(gettext("Maximum password life: %ld\n"),
   1927 		    policy.pw_max_life);
   1928 		printf(gettext("Minimum password life: %ld\n"),
   1929 		    policy.pw_min_life);
   1930 		printf(gettext("Minimum password length: %ld\n"),
   1931 		    policy.pw_min_length);
   1932 		printf(gettext("Minimum number of password "
   1933 			    "character classes: %ld\n"),
   1934 	       policy.pw_min_classes);
   1935 		printf(gettext("Number of old keys kept: %ld\n"),
   1936 		    policy.pw_history_num);
   1937 		printf(gettext("Reference count: %ld\n"), policy.policy_refcnt);
   1938     } else {
   1939 	printf("\"%s\"\t%ld\t%ld\t%ld\t%ld\t%ld\t%ld\n",
   1940 	       policy.policy, policy.pw_max_life, policy.pw_min_life,
   1941 	       policy.pw_min_length, policy.pw_min_classes,
   1942 	       policy.pw_history_num, policy.policy_refcnt);
   1943     }
   1944     kadm5_free_policy_ent(handle, &policy);
   1945     return;
   1946 }
   1947 
   1948 void kadmin_getpols(argc, argv)
   1949     int argc;
   1950     char *argv[];
   1951 {
   1952     krb5_error_code retval;
   1953     char *expr, **names;
   1954     int i, count;
   1955 
   1956     /* Solaris Kerberos:
   1957      * Use a pager for listing policies (similar to listing princs)
   1958      */
   1959     FILE *output = NULL;
   1960     int fd;
   1961     struct sigaction nsig, osig;
   1962     sigset_t nmask, omask;
   1963     int waitb;
   1964 
   1965     expr = NULL;
   1966     if (! (argc == 1 || (argc == 2 && (expr = argv[1])))) {
   1967 	fprintf(stderr, "%s: get_policies %s\n",
   1968 			gettext("usage"), gettext("[expression]\n"));
   1969 	return;
   1970     }
   1971     retval = kadm5_get_policies(handle, expr, &names, &count);
   1972     if (retval) {
   1973 	com_err("get_policies", retval,
   1974 			gettext("while retrieving list."));
   1975 	return;
   1976     }
   1977 
   1978     if (sigemptyset(&nmask) == -1) {
   1979         perror("sigemptyset");
   1980         kadm5_free_name_list(handle, names, count);
   1981         return;
   1982     }
   1983 
   1984     if (sigaddset(&nmask, SIGINT) == -1) {
   1985         perror("sigaddset");
   1986         kadm5_free_name_list(handle, names, count);
   1987         return;
   1988     }
   1989 
   1990     if (sigemptyset(&nsig.sa_mask) == -1) {
   1991         perror("sigemptyset");
   1992         kadm5_free_name_list(handle, names, count);
   1993         return;
   1994     }
   1995 
   1996     if (sigprocmask(SIG_BLOCK, &nmask, &omask) == -1) {
   1997         perror("sigprocmask");
   1998         kadm5_free_name_list(handle, names, count);
   1999         return;
   2000     }
   2001 
   2002     nsig.sa_handler = SIG_IGN;
   2003     nsig.sa_flags = 0;
   2004     if (sigaction(SIGINT, &nsig, &osig) == -1) {
   2005         perror("sigaction");
   2006         if (sigprocmask(SIG_SETMASK, &omask, (sigset_t *)0) == -1) {
   2007             perror("sigprocmask");
   2008         }
   2009         kadm5_free_name_list(handle, names, count);
   2010         return;
   2011     }
   2012 
   2013     fd = ss_pager_create();
   2014     if (fd == -1) {
   2015         fprintf(stderr, "%s: failed to create pager\n", whoami);
   2016         if (sigprocmask(SIG_SETMASK, &omask, (sigset_t *)0) == -1) {
   2017             perror("sigprocmask");
   2018         }
   2019 
   2020         if (sigaction(SIGINT, &osig, (struct sigaction *)0) == -1) {
   2021             perror("sigaction");
   2022         }
   2023 
   2024         kadm5_free_name_list(handle, names, count);
   2025         return;
   2026     }
   2027 
   2028     output = fdopen(fd, "w");
   2029     if (output == NULL) {
   2030         perror("fdopen");
   2031     }
   2032 
   2033     if (sigprocmask(SIG_SETMASK, &omask, (sigset_t *)0) == -1) {
   2034         perror("sigprocmask");
   2035     }
   2036 
   2037     if (output != NULL) {
   2038         for (i = 0; i < count; i++)
   2039         fprintf(output, "%s\n", names[i]);
   2040     }
   2041 
   2042     if (output != NULL && fclose(output) != 0) {
   2043         perror("fclose");
   2044     }
   2045 
   2046     if (wait(&waitb) == -1) {
   2047         perror("wait");
   2048     }
   2049 
   2050     if (sigaction(SIGINT, &osig, (struct sigaction *)0) == -1) {
   2051         perror("sigaction");
   2052     }
   2053     kadm5_free_name_list(handle, names, count);
   2054 }
   2055