xref: /illumos-gate/usr/src/cmd/krb5/kadmin/cli/kadmin.c (revision 79033acb)
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     int local_kadmin = 0;
635 
636     local_kadmin = (strcmp(whoami, KADMIN_LOCAL_NAME) == 0);
637 
638     if (argc < 2) {
639 	 goto usage;
640     }
641     for (argv++, argc--; argc > 1; argc--, argv++) {
642 	if (!strcmp("-pw", *argv)) {
643 	    argc--;
644 	    if (argc < 1) {
645 		fprintf(stderr, "change_password: %s",
646 			gettext("missing password arg\n"));
647 		goto usage;
648 	    }
649 	    pwarg = *++argv;
650 	    continue;
651 	}
652 	if (!strcmp("-randkey", *argv)) {
653 	    randkey++;
654 	    continue;
655 	}
656 	if (!strcmp("-keepold", *argv)) {
657 	    keepold++;
658 	    continue;
659 	}
660 	if (!strcmp("-e", *argv)) {
661 	    argc--;
662 	    if (argc < 1) {
663 		fprintf(stderr, "change_password: %s",
664 			gettext("missing keysaltlist arg\n"));
665 		goto usage;
666 	    }
667 	    retval = krb5_string_to_keysalts(*++argv, ", \t", ":.-", 0,
668 					     &ks_tuple, &n_ks_tuple);
669 	    if (retval) {
670 		com_err("change_password", retval,
671 			gettext("while parsing keysalts %s"), *argv);
672 		return;
673 	    }
674 	    continue;
675 	}
676 	goto usage;
677     }
678     retval = kadmin_parse_name(*argv, &princ);
679     if (retval) {
680 	com_err("change_password", retval,
681 		gettext("while parsing principal name"));
682 	if (ks_tuple != NULL)
683 	    free(ks_tuple);
684 	goto usage;
685     }
686     retval = krb5_unparse_name(context, princ, &canon);
687     if (retval) {
688 		com_err("change_password", retval,
689 			gettext("while canonicalizing principal"));
690 	krb5_free_principal(context, princ);
691 	if (ks_tuple != NULL)
692 	    free(ks_tuple);
693 	return;
694     }
695     if (pwarg != NULL) {
696 	if (keepold || ks_tuple != NULL) {
697 	    retval = kadm5_chpass_principal_3(handle, princ, keepold,
698 					      n_ks_tuple, ks_tuple, pwarg);
699 	    if (ks_tuple != NULL)
700 		free(ks_tuple);
701 	} else {
702 	    retval = kadm5_chpass_principal(handle, princ, pwarg);
703 	}
704 	krb5_free_principal(context, princ);
705 	if (retval) {
706 	    com_err("change_password", retval,
707 				gettext("while changing password for \"%s\"."),
708 				canon);
709 	    free(canon);
710 	    return;
711 	}
712 		printf(gettext("Password for \"%s\" changed.\n"), canon);
713 	free(canon);
714 	return;
715     } else if (randkey) {
716 	if (keepold || ks_tuple != NULL || local_kadmin) {
717 	    retval = kadm5_randkey_principal_3(handle, princ, keepold,
718 					       n_ks_tuple, ks_tuple,
719 					       NULL, NULL);
720 	    if (ks_tuple != NULL)
721 		free(ks_tuple);
722 	} else {
723 	    retval = kadm5_randkey_principal(handle, princ, NULL, NULL);
724 	}
725 	krb5_free_principal(context, princ);
726 	if (retval) {
727 	    com_err("change_password", retval,
728 				gettext("while randomizing key for \"%s\"."),
729 				canon);
730 	    free(canon);
731 	    return;
732 	}
733 	printf(gettext("Key for \"%s\" randomized.\n"), canon);
734 	free(canon);
735 	return;
736     } else if (argc == 1) {
737 	unsigned int i = sizeof (newpw) - 1;
738 
739 		snprintf(prompt1, sizeof (prompt1),
740 			gettext("Enter password for principal \"%.900s\""),
741 			*argv);
742 		snprintf(prompt2, sizeof (prompt2),
743 			gettext("Re-enter password for principal \"%.900s\""),
744 			*argv);
745 	retval = krb5_read_password(context, prompt1, prompt2,
746 				    newpw, &i);
747 	if (retval) {
748 	    com_err("change_password", retval,
749 				gettext("while reading password for \"%s\"."),
750 				canon);
751 	    free(canon);
752 	    if (ks_tuple != NULL)
753 		free(ks_tuple);
754 	    krb5_free_principal(context, princ);
755 	    return;
756 	}
757 	if (keepold || ks_tuple != NULL) {
758 	    retval = kadm5_chpass_principal_3(handle, princ, keepold,
759 					      n_ks_tuple, ks_tuple,
760 					      newpw);
761 	    if (ks_tuple != NULL)
762 		free(ks_tuple);
763 	} else {
764 	    retval = kadm5_chpass_principal(handle, princ, newpw);
765 	}
766 	krb5_free_principal(context, princ);
767 	memset(newpw, 0, sizeof (newpw));
768 	if (retval) {
769 	    com_err("change_password", retval,
770 				gettext("while changing password for \"%s\"."),
771 				canon);
772 	    free(canon);
773 	    return;
774 	}
775 		printf(gettext("Password for \"%s\" changed.\n"), canon);
776 	free(canon);
777 	return;
778    } else {
779 	free(canon);
780 	krb5_free_principal(context, princ);
781    usage:
782 		fprintf(stderr, "%s: change_password [-randkey] [-keepold] "
783 			"[-e keysaltlist] [-pw password] %s\n",
784 			gettext("usage"), gettext("principal"));
785 	return;
786    }
787 }
788 
789 int kadmin_parse_princ_args(argc, argv, oprinc, mask, pass, randkey,
790 			    ks_tuple, n_ks_tuple, caller)
791     int argc;
792     char *argv[];
793     kadm5_principal_ent_t oprinc;
794     long *mask;
795     char **pass;
796     int *randkey;
797     krb5_key_salt_tuple **ks_tuple;
798     int *n_ks_tuple;
799     char *caller;
800 {
801     int i, j, attrib_set;
802     time_t date;
803     time_t now;
804     krb5_error_code retval;
805 
806     *mask = 0;
807     *pass = NULL;
808     *n_ks_tuple = 0;
809     *ks_tuple = NULL;
810     time(&now);
811     *randkey = 0;
812     for (i = 1; i < argc - 1; i++) {
813 	attrib_set = 0;
814 	if (strlen(argv[i]) == 7 &&
815 		    strcmp("-expire", argv[i]) == 0) {
816 	    if (++i > argc - 2)
817 				return (-1);
818 	    else {
819 		date = get_date(argv[i], NULL);
820  		if (date == (time_t)-1) {
821 					fprintf(stderr,
822 						gettext("Invalid date "
823 							"specification "
824 							"\"%s\".\n"),
825 			     argv[i]);
826 					return (-1);
827  		}
828 		oprinc->princ_expire_time = date;
829 		*mask |= KADM5_PRINC_EXPIRE_TIME;
830 		continue;
831 	    }
832 	}
833 	if (strlen(argv[i]) == 9 &&
834 		    strcmp("-pwexpire", argv[i]) == 0) {
835 	    if (++i > argc - 2)
836 				return (-1);
837 	    else {
838 		date = get_date(argv[i], NULL);
839  		if (date == (time_t)-1) {
840 					fprintf(stderr,
841 						gettext("Invalid date "
842 							"specification "
843 							"\"%s\".\n"),
844 			     argv[i]);
845 					return (-1);
846  		}
847 		oprinc->pw_expiration = date;
848 		*mask |= KADM5_PW_EXPIRATION;
849 		continue;
850 	    }
851 	}
852 	if (strlen(argv[i]) == 8 &&
853 		    strcmp("-maxlife", argv[i]) == 0) {
854 	    if (++i > argc - 2)
855 				return (-1);
856 	    else {
857 		date = get_date(argv[i], NULL);
858  		if (date == (time_t)-1) {
859 					fprintf(stderr,
860 						gettext("Invalid date "
861 							"specification "
862 							"\"%s\".\n"),
863 			     argv[i]);
864 					return (-1);
865  		}
866 				if (date <= now) {
867 					fprintf(stderr,
868 						gettext("Date specified is "
869 							"in the past "
870 							"\"%s\".\n"),
871 						argv[i]);
872 					return (-1);
873 				}
874 		oprinc->max_life = date - now;
875 		*mask |= KADM5_MAX_LIFE;
876 		continue;
877 	    }
878 	}
879 	if (strlen(argv[i]) == 13 &&
880 		    strcmp("-maxrenewlife", argv[i]) == 0) {
881 	    if (++i > argc - 2)
882 				return (-1);
883 	    else {
884 		date = get_date(argv[i], NULL);
885  		if (date == (time_t)-1) {
886 					fprintf(stderr,
887 						gettext("Invalid date "
888 							"specification "
889 							"\"%s\".\n"),
890 			     argv[i]);
891 					return (-1);
892  		}
893 				if (date <= now) {
894 					fprintf(stderr,
895 						gettext("Date specified is "
896 							"in the past "
897 							"\"%s\".\n"),
898 						argv[i]);
899 					return (-1);
900 				}
901 		oprinc->max_renewable_life = date - now;
902 		*mask |= KADM5_MAX_RLIFE;
903 		continue;
904 	    }
905 	}
906 	if (strlen(argv[i]) == 5 &&
907 		    strcmp("-kvno", argv[i]) == 0) {
908 	    if (++i > argc - 2)
909 				return (-1);
910 	    else {
911 		oprinc->kvno = atoi(argv[i]);
912 		*mask |= KADM5_KVNO;
913 		continue;
914 	    }
915 	}
916 	if (strlen(argv[i]) == 7 &&
917 		    strcmp("-policy", argv[i]) == 0) {
918 	    if (++i > argc - 2)
919 				return (-1);
920 	    else {
921 		oprinc->policy = argv[i];
922 		*mask |= KADM5_POLICY;
923 		continue;
924 	    }
925 	}
926 	if (strlen(argv[i]) == 12 &&
927 		    strcmp("-clearpolicy", argv[i]) == 0) {
928 	    oprinc->policy = NULL;
929 	    *mask |= KADM5_POLICY_CLR;
930 	    continue;
931 	}
932 	if (strlen(argv[i]) == 3 &&
933 		    strcmp("-pw", argv[i]) == 0) {
934 	    if (++i > argc - 2)
935 				return (-1);
936 	    else {
937 		*pass = argv[i];
938 		continue;
939 	    }
940 	}
941 	if (strlen(argv[i]) == 8 &&
942 		    strcmp("-randkey", argv[i]) == 0) {
943 	    ++*randkey;
944 	    continue;
945 	}
946 	if (!strcmp("-e", argv[i])) {
947 	    if (++i > argc - 2)
948 		return -1;
949 	    else {
950 		retval = krb5_string_to_keysalts(argv[i], ", \t", ":.-", 0,
951 						 ks_tuple, n_ks_tuple);
952 		if (retval) {
953 		    com_err(caller, retval,
954 			    gettext("while parsing keysalts %s"), argv[i]);
955 		    return -1;
956 		}
957 	    }
958 	    continue;
959 	}
960 	for (j = 0; j < sizeof (flags) / sizeof (struct pflag); j++) {
961 	    if (strlen(argv[i]) == flags[j].flaglen + 1 &&
962 			    strcmp(flags[j].flagname,
963 				    /* strip off leading + or - */
964 				    &argv[i][1]) == 0) {
965 		if (flags[j].set && argv[i][0] == '-' ||
966 		    !flags[j].set && argv[i][0] == '+') {
967 		    oprinc->attributes |= flags[j].theflag;
968 		    *mask |= KADM5_ATTRIBUTES;
969 		    attrib_set++;
970 		    break;
971 		} else if (flags[j].set && argv[i][0] == '+' ||
972 			   !flags[j].set && argv[i][0] == '-') {
973 		    oprinc->attributes &= ~flags[j].theflag;
974 		    *mask |= KADM5_ATTRIBUTES;
975 		    attrib_set++;
976 		    break;
977 		} else {
978 					return (-1);
979 		}
980 	    }
981 	}
982 	if (!attrib_set)
983 			return (-1);	/* nothing was parsed */
984     }
985     if (i != argc - 1) {
986 		return (-1);
987     }
988     retval = kadmin_parse_name(argv[i], &oprinc->principal);
989     if (retval) {
990 		com_err(caller, retval, gettext("while parsing principal"));
991 		return (-1);
992     }
993 	return (0);
994 }
995 
996 void
997 kadmin_addprinc_usage(func)
998    char *func;
999 {
1000 	fprintf(stderr, "%s: %s %s\n", gettext("usage"), func,
1001 		gettext("[options] principal"));
1002 	fprintf(stderr, gettext("\toptions are:\n"));
1003 	fprintf(stderr, "\t\t[-expire expdate] [-pwexpire pwexpdate] "
1004 		"[-maxlife maxtixlife]\n\t\t[-kvno kvno] [-policy policy] "
1005 		"[-randkey] [-pw password]\n\t\t[-maxrenewlife maxrenewlife] "
1006 		"[-e keysaltlist] [{+|-}attribute]\n");
1007 	fprintf(stderr, gettext("\tattributes are:\n"));
1008      fprintf(stderr, "%s%s%s",
1009 		"\t\tallow_postdated allow_forwardable allow_tgs_req "
1010 		"allow_renewable\n",
1011 		"\t\tallow_proxiable allow_dup_skey allow_tix "
1012 		"requires_preauth\n",
1013 		"\t\trequires_hwauth needchange allow_svr "
1014 		"password_changing_service\n");
1015 }
1016 
1017 void
1018 kadmin_modprinc_usage(func)
1019    char *func;
1020 {
1021 	fprintf(stderr, "%s: %s %s\n", gettext("usage"), func,
1022 		gettext("[options] principal"));
1023 	fprintf(stderr, gettext("\toptions are:\n"));
1024 	fprintf(stderr, "\t\t[-expire expdate] [-pwexpire pwexpdate] "
1025 		"[-maxlife maxtixlife]\n\t\t[-kvno kvno] [-policy policy] "
1026 		"[-clearpolicy]\n\t\t[-maxrenewlife maxrenewlife] "
1027 		"[{+|-}attribute]\n");
1028 	fprintf(stderr, gettext("\tattributes are:\n"));
1029      fprintf(stderr, "%s%s%s",
1030 		"\t\tallow_postdated allow_forwardable allow_tgs_req "
1031 		"allow_renewable\n",
1032 		"\t\tallow_proxiable allow_dup_skey allow_tix "
1033 		"requires_preauth\n",
1034 		"\t\trequires_hwauth needchange allow_svr "
1035 		"password_changing_service\n");
1036 }
1037 
1038 void
1039 kadmin_addprinc(argc, argv)
1040     int argc;
1041     char *argv[];
1042 {
1043     kadm5_principal_ent_rec princ, dprinc;
1044     kadm5_policy_ent_rec defpol;
1045     long mask;
1046     int randkey = 0, i;
1047     int n_ks_tuple;
1048     krb5_key_salt_tuple *ks_tuple;
1049     char *pass, *canon;
1050     krb5_error_code retval;
1051     static char newpw[1024], dummybuf[256];
1052     static char prompt1[1024], prompt2[1024];
1053     int local_kadmin = 0;
1054 
1055     local_kadmin = (strcmp(whoami, KADMIN_LOCAL_NAME) == 0);
1056 
1057     if (dummybuf[0] == 0) {
1058 	 for (i = 0; i < 256; i++)
1059 	      dummybuf[i] = (i+1) % 256;
1060     }
1061 
1062     /* Zero all fields in request structure */
1063     memset(&princ, 0, sizeof(princ));
1064     memset(&dprinc, 0, sizeof(dprinc));
1065 
1066     princ.attributes = dprinc.attributes = 0;
1067     if (kadmin_parse_princ_args(argc, argv,
1068 				&princ, &mask, &pass, &randkey,
1069 				&ks_tuple, &n_ks_tuple,
1070 				"add_principal")) {
1071 	 kadmin_addprinc_usage("add_principal");
1072 	 return;
1073     }
1074 
1075     retval = krb5_unparse_name(context, princ.principal, &canon);
1076     if (retval) {
1077 	com_err("add_principal", retval,
1078 		gettext("while canonicalizing principal"));
1079 	krb5_free_principal(context, princ.principal);
1080 	if (ks_tuple != NULL)
1081 	    free(ks_tuple);
1082 	return;
1083     }
1084 
1085     /*
1086      * If -policy was not specified, and -clearpolicy was not
1087      * specified, and the policy "default" exists, assign it.  If
1088      * -clearpolicy was specified, then KADM5_POLICY_CLR should be
1089      * unset, since it is never valid for kadm5_create_principal.
1090      */
1091     if ((! (mask & KADM5_POLICY)) &&
1092 	(! (mask & KADM5_POLICY_CLR))) {
1093 	 if (! kadm5_get_policy(handle, "default", &defpol)) {
1094 	      fprintf(stderr,
1095 		gettext(
1096 		"NOTICE: no policy specified for %s; assigning \"default\"\n"),
1097 		    canon);
1098 	      princ.policy = "default";
1099 	      mask |= KADM5_POLICY;
1100 	      (void) kadm5_free_policy_ent(handle, &defpol);
1101 	 } else
1102 	      fprintf(stderr, gettext("WARNING: no policy specified "
1103 			"for %s; defaulting to no policy\n"), canon);
1104     }
1105     mask &= ~KADM5_POLICY_CLR;
1106 
1107     /*
1108      * Set 'notix' for randkey principals and also for principals which have
1109      * specified flag options on the cmdline. This is because we want to apply
1110      * generic flag settings from 'default_principal_flags' first (during
1111      * principal creation), followed by a kadm5_modify_principal() which
1112      * correctly applies the cli flag options. So, we do *not* want any tix
1113      * issued in the interim.
1114      */
1115     if (randkey || (mask & KADM5_ATTRIBUTES))
1116 	princ.attributes |= KRB5_KDB_DISALLOW_ALL_TIX;
1117 
1118     if (randkey) {
1119 	pass = dummybuf;
1120 	mask |= KADM5_ATTRIBUTES;
1121     } else if (pass == NULL) {
1122 	unsigned int i = sizeof (newpw) - 1;
1123 	snprintf(prompt1, sizeof (prompt1),
1124 		gettext("Enter password for principal \"%.900s\""),
1125 		canon);
1126 	snprintf(prompt2, sizeof (prompt1),
1127 		gettext("Re-enter password for principal \"%.900s\""),
1128 		canon);
1129 	retval = krb5_read_password(context, prompt1, prompt2,
1130 		    newpw, &i);
1131 	if (retval) {
1132 	    com_err("add_principal", retval,
1133 		gettext("while reading password for \"%s\"."), canon);
1134 	    free(canon);
1135 	    krb5_free_principal(context, princ.principal);
1136 	    return;
1137 	}
1138 	pass = newpw;
1139     }
1140     mask |= KADM5_PRINCIPAL;
1141 
1142     /*
1143      * If the client being used is local, always use the new
1144      * API so we get the full set of enctype support.
1145      */
1146     if (ks_tuple != NULL || local_kadmin) {
1147 	retval = kadm5_create_principal_3(handle, &princ, mask,
1148 					  n_ks_tuple, ks_tuple, pass);
1149     } else {
1150 	retval = kadm5_create_principal(handle, &princ, mask, pass);
1151     }
1152     if (retval) {
1153 	com_err("add_principal", retval,
1154 		gettext("while creating \"%s\"."), canon);
1155 	krb5_free_principal(context, princ.principal);
1156 	free(canon);
1157 	if (ks_tuple != NULL)
1158 	    free(ks_tuple);
1159 	return;
1160     }
1161 
1162     if (randkey) { /* more special stuff for -randkey */
1163 	if (ks_tuple != NULL || local_kadmin) {
1164 	    retval = kadm5_randkey_principal_3(handle, princ.principal,
1165 					       FALSE,
1166 					       n_ks_tuple, ks_tuple,
1167 					       NULL, NULL);
1168 	} else {
1169 	    retval = kadm5_randkey_principal(handle, princ.principal,
1170 					     NULL, NULL);
1171 	}
1172 	if (retval) {
1173 	    com_err("add_principal", retval,
1174 		gettext("while randomizing key for \"%s\"."), canon);
1175 	    krb5_free_principal(context, princ.principal);
1176 	    free(canon);
1177 	    if (ks_tuple != NULL)
1178 		free(ks_tuple);
1179 	    return;
1180 	}
1181     }
1182 
1183     /*
1184      * We now retrieve the intersection set of the generic flag settings and
1185      * the ones specified on the cli & re-parse the princ args, just to make
1186      * sure we account for conflicts between 'default_principal_flags' and
1187      * the cmdline flag args. While we are here, also clear 'notix'.
1188      */
1189     if (randkey || (mask & KADM5_ATTRIBUTES)) {
1190 	retval = kadm5_get_principal(handle, princ.principal, &dprinc,
1191 			KADM5_PRINCIPAL_NORMAL_MASK);
1192         if (retval == 0) {
1193 	    if (dprinc.attributes != 0)
1194 		princ.attributes = dprinc.attributes;
1195 	} else {
1196 	    com_err("add_principal", retval,
1197 		gettext("while doing a get_principal on \"%s\"."), canon);
1198 	    printf(gettext("\nWarning: Principal \"%s\" could have incomplete "
1199 		"flag settings, as a result of a failed get_principal.\n"
1200 		"Check the 'default_principal_flags' setting in kdc.conf(4).\n"
1201 		"If there is a mismatch, use modprinc in kadmin(1M) to rectify "
1202 		"the same.\n\n"), canon);
1203 	}
1204 
1205 	(void) kadmin_parse_princ_args(argc, argv, &princ, &mask, &pass,
1206 			&randkey, &ks_tuple, &n_ks_tuple, "add_principal");
1207 
1208 	princ.attributes &= ~KRB5_KDB_DISALLOW_ALL_TIX;
1209 	mask = KADM5_ATTRIBUTES;
1210 	retval = kadm5_modify_principal(handle, &princ, mask);
1211 	if (retval) {
1212 	    com_err("add_principal", retval,
1213 		gettext("while doing a modify_principal to restore flag "
1214 			"settings for \"%s\"."), canon);
1215 	    krb5_free_principal(context, princ.principal);
1216 	    free(canon);
1217 	    if (ks_tuple != NULL)
1218 		free(ks_tuple);
1219 	    return;
1220 	}
1221     }
1222 
1223     krb5_free_principal(context, princ.principal);
1224 	printf(gettext("Principal \"%s\" created.\n"), canon);
1225     if (ks_tuple != NULL)
1226 	free(ks_tuple);
1227     free(canon);
1228 }
1229 
1230 void
1231 kadmin_modprinc(argc, argv)
1232     int argc;
1233     char *argv[];
1234 {
1235     kadm5_principal_ent_rec princ, oldprinc;
1236     krb5_principal kprinc;
1237     long mask;
1238     krb5_error_code retval;
1239     char *pass, *canon;
1240     int randkey = 0;
1241     int n_ks_tuple = 0;
1242     krb5_key_salt_tuple *ks_tuple;
1243 
1244     if (argc < 2) {
1245 	 kadmin_modprinc_usage("modify_principal");
1246 	 return;
1247     }
1248 
1249     memset(&oldprinc, 0, sizeof(oldprinc));
1250     memset(&princ, 0, sizeof(princ));
1251 
1252     retval = kadmin_parse_name(argv[argc - 1], &kprinc);
1253     if (retval) {
1254 		com_err("modify_principal", retval,
1255 			gettext("while parsing principal"));
1256 	return;
1257     }
1258     retval = krb5_unparse_name(context, kprinc, &canon);
1259     if (retval) {
1260 	com_err("modify_principal", retval,
1261 			gettext("while canonicalizing principal"));
1262 	krb5_free_principal(context, kprinc);
1263 	return;
1264     }
1265     retval = kadm5_get_principal(handle, kprinc, &oldprinc,
1266 				 KADM5_PRINCIPAL_NORMAL_MASK);
1267     krb5_free_principal(context, kprinc);
1268     if (retval) {
1269 		com_err("modify_principal", retval,
1270 			gettext("while getting \"%s\"."), canon);
1271 	free(canon);
1272 	return;
1273     }
1274     princ.attributes = oldprinc.attributes;
1275     kadm5_free_principal_ent(handle, &oldprinc);
1276     retval = kadmin_parse_princ_args(argc, argv,
1277 				     &princ, &mask,
1278 				     &pass, &randkey,
1279 				     &ks_tuple, &n_ks_tuple,
1280 				     "modify_principal");
1281     if (ks_tuple != NULL) {
1282 	free(ks_tuple);
1283 	kadmin_modprinc_usage("modify_principal");
1284 	free(canon);
1285 	return;
1286     }
1287     if (retval) {
1288 	kadmin_modprinc_usage("modify_principal");
1289 	free(canon);
1290 	return;
1291     }
1292     if (randkey) {
1293 		fprintf(stderr, "modify_principal: -randkey %s ",
1294 			gettext("not allowed\n"));
1295 	krb5_free_principal(context, princ.principal);
1296 	free(canon);
1297 	return;
1298     }
1299     if (pass) {
1300 	fprintf(stderr,
1301 		"modify_principal: -pw %s change_password\n",
1302 		gettext("not allowed; use"));
1303 	krb5_free_principal(context, princ.principal);
1304 	free(canon);
1305 	return;
1306     }
1307     retval = kadm5_modify_principal(handle, &princ, mask);
1308     krb5_free_principal(context, princ.principal);
1309     if (retval) {
1310 	com_err("modify_principal", retval,
1311 			gettext("while modifying \"%s\"."), canon);
1312 	free(canon);
1313 	return;
1314     }
1315 	printf(gettext("Principal \"%s\" modified.\n"), canon);
1316     free(canon);
1317 }
1318 
1319 void
1320 kadmin_getprinc(argc, argv)
1321     int argc;
1322     char *argv[];
1323 {
1324     kadm5_principal_ent_rec dprinc;
1325     krb5_principal princ;
1326     krb5_error_code retval;
1327     char *canon, *modcanon;
1328     int i;
1329 
1330     if (! (argc == 2 ||
1331 		(argc == 3 && strcmp("-terse", argv[1]) == 0))) {
1332 		fprintf(stderr, "%s: get_principal [-terse] %s\n",
1333 			gettext("usage"), gettext("principal"));
1334 	return;
1335     }
1336     memset(&dprinc, 0, sizeof(dprinc));
1337     memset(&princ, 0, sizeof(princ));
1338 
1339     retval = kadmin_parse_name(argv[argc - 1], &princ);
1340     if (retval) {
1341 		com_err("get_principal", retval,
1342 			gettext("while parsing principal"));
1343 	return;
1344     }
1345     retval = krb5_unparse_name(context, princ, &canon);
1346     if (retval) {
1347 		com_err("get_principal", retval,
1348 			gettext("while canonicalizing principal"));
1349 	krb5_free_principal(context, princ);
1350 	return;
1351     }
1352     retval = kadm5_get_principal(handle, princ, &dprinc,
1353 				 KADM5_PRINCIPAL_NORMAL_MASK | KADM5_KEY_DATA);
1354     krb5_free_principal(context, princ);
1355     if (retval) {
1356 		com_err("get_principal", retval,
1357 			gettext("while retrieving \"%s\"."), canon);
1358 	free(canon);
1359 	return;
1360     }
1361     retval = krb5_unparse_name(context, dprinc.mod_name, &modcanon);
1362     if (retval) {
1363 		com_err("get_principal", retval,
1364 			gettext("while unparsing modname"));
1365 	kadm5_free_principal_ent(handle, &dprinc);
1366 	free(canon);
1367 	return;
1368     }
1369     if (argc == 2) {
1370 		printf(gettext("Principal: %s\n"), canon);
1371 		printf(gettext("Expiration date: %s\n"),
1372 		    dprinc.princ_expire_time ?
1373 		    strdate(dprinc.princ_expire_time) :
1374 		    gettext("[never]"));
1375 		printf(gettext("Last password change: %s\n"),
1376 		    dprinc.last_pwd_change ?
1377 		    strdate(dprinc.last_pwd_change) :
1378 		    gettext("[never]"));
1379 		printf(gettext("Password expiration date: %s\n"),
1380 	       dprinc.pw_expiration ?
1381 		    strdate(dprinc.pw_expiration) : gettext("[none]"));
1382 		printf(gettext("Maximum ticket life: %s\n"),
1383 		    strdur(dprinc.max_life));
1384 		printf(gettext("Maximum renewable life: %s\n"),
1385 		    strdur(dprinc.max_renewable_life));
1386 		printf(gettext("Last modified: %s (%s)\n"),
1387 		    strdate(dprinc.mod_date), modcanon);
1388 		printf(gettext("Last successful authentication: %s\n"),
1389 	       dprinc.last_success ? strdate(dprinc.last_success) :
1390 		    gettext("[never]"));
1391 		printf(gettext("Last failed authentication: %s\n"),
1392 	       dprinc.last_failed ? strdate(dprinc.last_failed) :
1393 		    gettext("[never]"));
1394 		printf(gettext("Failed password attempts: %d\n"),
1395 	       dprinc.fail_auth_count);
1396 		printf(gettext("Number of keys: %d\n"), dprinc.n_key_data);
1397 	for (i = 0; i < dprinc.n_key_data; i++) {
1398 	     krb5_key_data *key_data = &dprinc.key_data[i];
1399 	     char enctype[BUFSIZ], salttype[BUFSIZ];
1400 
1401 	     if (krb5_enctype_to_string(key_data->key_data_type[0],
1402 					enctype, sizeof(enctype)))
1403 				snprintf(enctype, sizeof (enctype),
1404 					gettext("<Encryption type 0x%x>"),
1405 			  key_data->key_data_type[0]);
1406 			printf(gettext("Key: vno %d, %s, "),
1407 			    key_data->key_data_kvno, enctype);
1408 	     if (key_data->key_data_ver > 1) {
1409 				if (krb5_salttype_to_string(
1410 					key_data->key_data_type[1],
1411 					      salttype, sizeof(salttype)))
1412 					snprintf(salttype, sizeof (salttype),
1413 						gettext("<Salt type 0x%x>"),
1414 			       key_data->key_data_type[1]);
1415 		  printf("%s\n", salttype);
1416 	     } else
1417 				printf(gettext("no salt\n"));
1418 	}
1419 
1420 		printf(gettext("Attributes:"));
1421 	for (i = 0; i < sizeof (prflags) / sizeof (char *); i++) {
1422 	    if (dprinc.attributes & (krb5_flags) 1 << i)
1423 		printf(" %s", prflags[i]);
1424 	}
1425 	printf("\n");
1426 		printf(gettext("Policy: %s\n"),
1427 		    dprinc.policy ? dprinc.policy : gettext("[none]"));
1428     } else {
1429 	printf("\"%s\"\t%d\t%d\t%d\t%d\t\"%s\"\t%d\t%d\t%d\t%d\t\"%s\""
1430 	       "\t%d\t%d\t%d\t%d\t%d",
1431 	       canon, dprinc.princ_expire_time, dprinc.last_pwd_change,
1432 	       dprinc.pw_expiration, dprinc.max_life, modcanon,
1433 	       dprinc.mod_date, dprinc.attributes, dprinc.kvno,
1434 		    dprinc.mkvno, dprinc.policy ?
1435 		    dprinc.policy : gettext("[none]"),
1436 	       dprinc.max_renewable_life, dprinc.last_success,
1437 	       dprinc.last_failed, dprinc.fail_auth_count,
1438 	       dprinc.n_key_data);
1439 	for (i = 0; i < dprinc.n_key_data; i++)
1440 	     printf("\t%d\t%d\t%d\t%d",
1441 		    dprinc.key_data[i].key_data_ver,
1442 		    dprinc.key_data[i].key_data_kvno,
1443 		    dprinc.key_data[i].key_data_type[0],
1444 		    dprinc.key_data[i].key_data_type[1]);
1445 	printf("\n");
1446    }
1447     free(modcanon);
1448     kadm5_free_principal_ent(handle, &dprinc);
1449     free(canon);
1450 }
1451 
1452 void
1453 kadmin_getprincs(argc, argv)
1454     int argc;
1455     char *argv[];
1456 {
1457     krb5_error_code retval;
1458     char *exp, **names;
1459     int i, count;
1460 
1461 	FILE *output;
1462 	int fd;
1463 	struct sigaction nsig, osig;
1464 	sigset_t nmask, omask;
1465 	int waitb;
1466 
1467     exp = NULL;
1468     if (! (argc == 1 || (argc == 2 && (exp = argv[1])))) {
1469 		fprintf(stderr, "%s: get_principals %s\n",
1470 			gettext("usage"), gettext("[expression]"));
1471 	return;
1472     }
1473     retval = kadm5_get_principals(handle, exp, &names, &count);
1474     if (retval) {
1475 		com_err("get_principals", retval,
1476 			gettext("while retrieving list."));
1477 	return;
1478     }
1479 
1480 	/*
1481 	 * Solaris:  the following code is used for paging
1482 	 */
1483 
1484 	sigemptyset(&nmask);
1485 	sigaddset(&nmask, SIGINT);
1486 	sigprocmask(SIG_BLOCK, &nmask, &omask);
1487 
1488 	nsig.sa_handler = SIG_IGN;
1489 	sigemptyset(&nsig.sa_mask);
1490 	nsig.sa_flags = 0;
1491 	sigaction(SIGINT, &nsig, &osig);
1492 
1493 	fd = ss_pager_create();
1494 	output = fdopen(fd, "w");
1495 
1496 	sigprocmask(SIG_SETMASK, &omask, (sigset_t *)0);
1497 
1498     for (i = 0; i < count; i++)
1499 		fprintf(output, "%s\n", names[i]);
1500 
1501 	fclose(output);
1502 
1503 	wait(&waitb);
1504 
1505     kadm5_free_name_list(handle, names, count);
1506 }
1507 
1508 int
1509 kadmin_parse_policy_args(argc, argv, policy, mask, caller)
1510     int argc;
1511     char *argv[];
1512     kadm5_policy_ent_t policy;
1513     long *mask;
1514     char *caller;
1515 {
1516     int i;
1517     time_t now;
1518     time_t date;
1519     krb5_error_code retval;
1520 
1521     time(&now);
1522     *mask = 0;
1523     for (i = 1; i < argc - 1; i++) {
1524 	if (strlen(argv[i]) == 8 &&
1525 		    strcmp(argv[i], "-maxlife") == 0) {
1526 	    if (++i > argc -2)
1527 				return (-1);
1528 	    else {
1529 		date = get_date(argv[i], NULL);
1530  		if (date == (time_t)-1) {
1531 					fprintf(stderr,
1532 						gettext("Invalid date "
1533 							"specification "
1534 							"\"%s\".\n"),
1535 			     argv[i]);
1536 					return (-1);
1537  		}
1538 				if (date <= now) {
1539 					fprintf(stderr,
1540 						gettext("Date specified is "
1541 							"in the past "
1542 							"\"%s\".\n"),
1543 						argv[i]);
1544 					return (-1);
1545 				}
1546 		policy->pw_max_life = date - now;
1547 		*mask |= KADM5_PW_MAX_LIFE;
1548 		continue;
1549 	    }
1550 	} else if (strlen(argv[i]) == 8 &&
1551 			strcmp(argv[i], "-minlife") == 0) {
1552 	    if (++i > argc - 2)
1553 				return (-1);
1554 	    else {
1555 		date = get_date(argv[i], NULL);
1556  		if (date == (time_t)-1) {
1557 					fprintf(stderr,
1558 						gettext("Invalid date "
1559 							"specification "
1560 							"\"%s\".\n"),
1561 			     argv[i]);
1562 					return (-1);
1563  		}
1564 				if (date <= now) {
1565 					fprintf(stderr,
1566 						gettext("Date specified is "
1567 							"in the past "
1568 							"\"%s\".\n"),
1569 						argv[i]);
1570 					return (-1);
1571 				}
1572 		policy->pw_min_life = date - now;
1573 		*mask |= KADM5_PW_MIN_LIFE;
1574 		continue;
1575 	    }
1576 	} else if (strlen(argv[i]) == 10 &&
1577 			strcmp(argv[i], "-minlength") == 0) {
1578 	    if (++i > argc - 2)
1579 				return (-1);
1580 	    else {
1581 		policy->pw_min_length = atoi(argv[i]);
1582 		*mask |= KADM5_PW_MIN_LENGTH;
1583 		continue;
1584 	    }
1585 	} else if (strlen(argv[i]) == 11 &&
1586 			strcmp(argv[i], "-minclasses") == 0) {
1587 	    if (++i > argc - 2)
1588 				return (-1);
1589 	    else {
1590 		policy->pw_min_classes = atoi(argv[i]);
1591 		*mask |= KADM5_PW_MIN_CLASSES;
1592 		continue;
1593 	    }
1594 	} else if (strlen(argv[i]) == 8 &&
1595 			strcmp(argv[i], "-history") == 0) {
1596 	    if (++i > argc - 2)
1597 				return (-1);
1598 	    else {
1599 		policy->pw_history_num = atoi(argv[i]);
1600 		*mask |= KADM5_PW_HISTORY_NUM;
1601 		continue;
1602 	    }
1603 	} else
1604 			return (-1);
1605     }
1606     if (i != argc -1) {
1607 		fprintf(stderr, gettext("%s: parser lost count!\n"), caller);
1608 		return (-1);
1609     } else
1610 		return (0);
1611 }
1612 
1613 void
1614 kadmin_addmodpol_usage(func)
1615    char *func;
1616 {
1617 	fprintf(stderr, "%s: %s %s\n", gettext("usage"), func,
1618 		gettext("[options] policy"));
1619 	fprintf(stderr, gettext("\toptions are:\n"));
1620 	fprintf(stderr, "\t\t[-maxlife time] [-minlife time] "
1621 		"[-minlength length]\n\t\t[-minclasses number] "
1622 		"[-history number]\n");
1623 }
1624 
1625 void
1626 kadmin_addpol(argc, argv)
1627     int argc;
1628     char *argv[];
1629 {
1630     krb5_error_code retval;
1631     long mask;
1632     kadm5_policy_ent_rec policy;
1633 
1634     memset(&policy, 0, sizeof(policy));
1635 	if (kadmin_parse_policy_args(argc, argv,
1636 				    &policy, &mask, "add_policy")) {
1637 	 kadmin_addmodpol_usage("add_policy");
1638 	 return;
1639     } else {
1640 	policy.policy = argv[argc - 1];
1641 	mask |= KADM5_POLICY;
1642 	retval = kadm5_create_policy(handle, &policy, mask);
1643 	if (retval) {
1644 			com_err("add_policy", retval,
1645 				gettext("while creating policy \"%s\"."),
1646 		    policy.policy);
1647 	    return;
1648 	}
1649     }
1650 }
1651 
1652 void
1653 kadmin_modpol(argc, argv)
1654     int argc;
1655     char *argv[];
1656 {
1657     krb5_error_code retval;
1658     long mask;
1659     kadm5_policy_ent_rec policy;
1660 
1661     memset(&policy, 0, sizeof(policy));
1662     if (kadmin_parse_policy_args(argc, argv, &policy, &mask,
1663 				 "modify_policy")) {
1664 	kadmin_addmodpol_usage("modify_policy");
1665 	return;
1666     } else {
1667 	policy.policy = argv[argc - 1];
1668 	retval = kadm5_modify_policy(handle, &policy, mask);
1669 	if (retval) {
1670 			com_err("modify_policy", retval,
1671 				gettext("while modifying policy \"%s\"."),
1672 		    policy.policy);
1673 	    return;
1674 	}
1675     }
1676 }
1677 
1678 void
1679 kadmin_delpol(argc, argv)
1680     int argc;
1681     char *argv[];
1682 {
1683     krb5_error_code retval;
1684 	char reply[32];
1685 
1686     if (! (argc == 2 ||
1687 		(argc == 3 && strcmp("-force", argv[1]) == 0))) {
1688 		fprintf(stderr, "%s: delete_policy [-force] %s\n",
1689 			gettext("usage"), gettext("policy"));
1690 	return;
1691     }
1692     if (argc == 2) {
1693 		printf(gettext("Are you sure you want to delete the policy "
1694 			    "\"%s\"? (yes/no): "), argv[1]);
1695 	fgets(reply, sizeof (reply), stdin);
1696 		if (strncmp(gettext("yes\n"), reply, sizeof (reply)) &&
1697 			strncmp(gettext("y\n"), reply, sizeof (reply)) &&
1698 			strncmp(gettext("Y\n"), reply, sizeof (reply))
1699 			) {
1700 			fprintf(stderr,
1701 				gettext("Policy \"%s\" not deleted.\n"),
1702 				argv[1]);
1703 	    return;
1704 	}
1705     }
1706     retval = kadm5_delete_policy(handle, argv[argc - 1]);
1707     if (retval) {
1708 		com_err("delete_policy:", retval,
1709 			gettext("while deleting policy \"%s\""),
1710 		argv[argc - 1]);
1711 	return;
1712     }
1713 }
1714 
1715 void
1716 kadmin_getpol(argc, argv)
1717     int argc;
1718     char *argv[];
1719 {
1720     krb5_error_code retval;
1721     kadm5_policy_ent_rec policy;
1722 
1723     if (! (argc == 2 ||
1724 		(argc == 3 && strcmp("-terse", argv[1]) == 0))) {
1725 		fprintf(stderr, "%s: get_policy [-terse] %s\n",
1726 			gettext("usage"), gettext("policy"));
1727 	return;
1728     }
1729     retval = kadm5_get_policy(handle, argv[argc - 1], &policy);
1730     if (retval) {
1731 		com_err("get_policy", retval,
1732 			gettext("while retrieving policy \"%s\"."),
1733 		argv[argc - 1]);
1734 	return;
1735     }
1736     if (argc == 2) {
1737 		printf(gettext("Policy: %s\n"), policy.policy);
1738 		printf(gettext("Maximum password life: %d\n"),
1739 		    policy.pw_max_life);
1740 		printf(gettext("Minimum password life: %d\n"),
1741 		    policy.pw_min_life);
1742 		printf(gettext("Minimum password length: %d\n"),
1743 		    policy.pw_min_length);
1744 		printf(gettext("Minimum number of password "
1745 			    "character classes: %d\n"),
1746 	       policy.pw_min_classes);
1747 		printf(gettext("Number of old keys kept: %d\n"),
1748 		    policy.pw_history_num);
1749 		printf(gettext("Reference count: %d\n"), policy.policy_refcnt);
1750     } else {
1751 	printf("\"%s\"\t%d\t%d\t%d\t%d\t%d\t%d\n",
1752 	       policy.policy, policy.pw_max_life, policy.pw_min_life,
1753 	       policy.pw_min_length, policy.pw_min_classes,
1754 	       policy.pw_history_num, policy.policy_refcnt);
1755     }
1756     kadm5_free_policy_ent(handle, &policy);
1757 }
1758 
1759 void
1760 kadmin_getpols(argc, argv)
1761     int argc;
1762     char *argv[];
1763 {
1764     krb5_error_code retval;
1765     char *exp, **names;
1766     int i, count;
1767 
1768     exp = NULL;
1769     if (! (argc == 1 || (argc == 2 && (exp = argv[1])))) {
1770 		fprintf(stderr, "%s: get_policies %s\n",
1771 			gettext("usage"), gettext("[expression]\n"));
1772 	return;
1773     }
1774     retval = kadm5_get_policies(handle, exp, &names, &count);
1775     if (retval) {
1776 		com_err("get_policies", retval,
1777 			gettext("while retrieving list."));
1778 	return;
1779     }
1780     for (i = 0; i < count; i++)
1781 	 printf("%s\n", names[i]);
1782     kadm5_free_name_list(handle, names, count);
1783 }
1784