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