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