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