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