add kadm5_setkey_principal
[krb5.git] / src / kadmin / cli / kadmin.c
1 /*
2  * Copyright 1994 by the Massachusetts Institute of Technology.
3  * All Rights Reserved.
4  *
5  * Export of this software from the United States of America may
6  *   require a specific license from the United States Government.
7  *   It is the responsibility of any person or organization contemplating
8  *   export to obtain such a license before exporting.
9  * 
10  * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
11  * distribute this software and its documentation for any purpose and
12  * without fee is hereby granted, provided that the above copyright
13  * notice appear in all copies and that both that copyright notice and
14  * this permission notice appear in supporting documentation, and that
15  * the name of M.I.T. not be used in advertising or publicity pertaining
16  * to distribution of the software without specific, written prior
17  * permission.  M.I.T. makes no representations about the suitability of
18  * this software for any purpose.  It is provided "as is" without express
19  * or implied warranty.
20  * 
21  * kadmin.c: base functions for a kadmin command line interface using
22  * the OVSecure library
23  */
24
25 #include <krb5.h>
26 #include <k5-int.h>
27 #include <kadm5/admin.h>
28 #include <stdio.h>
29 #include <string.h>
30 #include <sys/types.h>
31 #include <math.h>
32 #include <unistd.h>
33 #include <pwd.h>
34 /* #include <sys/timeb.h> */
35 #include <time.h>
36
37 /* special struct to convert flag names for principals
38    to actual krb5_flags for a principal */
39 struct pflag {
40     char *flagname;             /* name of flag as typed to CLI */
41     int flaglen;                /* length of string (not counting -,+) */
42     krb5_flags theflag;         /* actual principal flag to set/clear */
43     int set;                    /* 0 means clear, 1 means set (on '-') */
44 };
45
46 static struct pflag flags[] = {
47 {"allow_postdated",     15,     KRB5_KDB_DISALLOW_POSTDATED,    1},
48 {"allow_forwardable",   17,     KRB5_KDB_DISALLOW_FORWARDABLE,  1},
49 {"allow_tgs_req",       13,     KRB5_KDB_DISALLOW_TGT_BASED,    1},
50 {"allow_renewable",     15,     KRB5_KDB_DISALLOW_RENEWABLE,    1},
51 {"allow_proxiable",     15,     KRB5_KDB_DISALLOW_PROXIABLE,    1},
52 {"allow_dup_skey",      14,     KRB5_KDB_DISALLOW_DUP_SKEY,     1},
53 {"allow_tix",           9,      KRB5_KDB_DISALLOW_ALL_TIX,      1},
54 {"requires_preauth",    16,     KRB5_KDB_REQUIRES_PRE_AUTH,     0},
55 {"requires_hwauth",     15,     KRB5_KDB_REQUIRES_HW_AUTH,      0},
56 {"needchange",          10,     KRB5_KDB_REQUIRES_PWCHANGE,     0},
57 {"allow_svr",           9,      KRB5_KDB_DISALLOW_SVR,          1},
58 {"password_changing_service",   25,     KRB5_KDB_PWCHANGE_SERVICE,      0 },
59 {"support_desmd5",      14,     KRB5_KDB_SUPPORT_DESMD5,        0 }
60 };
61
62 static char *prflags[] = {
63     "DISALLOW_POSTDATED",       /* 0x00000001 */
64     "DISALLOW_FORWARDABLE",     /* 0x00000002 */
65     "DISALLOW_TGT_BASED",       /* 0x00000004 */
66     "DISALLOW_RENEWABLE",       /* 0x00000008 */
67     "DISALLOW_PROXIABLE",       /* 0x00000010 */
68     "DISALLOW_DUP_SKEY",        /* 0x00000020 */
69     "DISALLOW_ALL_TIX",         /* 0x00000040 */
70     "REQUIRES_PRE_AUTH",        /* 0x00000080 */
71     "REQUIRES_HW_AUTH",         /* 0x00000100 */
72     "REQUIRES_PWCHANGE",        /* 0x00000200 */
73     "UNKNOWN_0x00000400",       /* 0x00000400 */
74     "UNKNOWN_0x00000800",       /* 0x00000800 */
75     "DISALLOW_SVR",             /* 0x00001000 */
76     "PWCHANGE_SERVICE",         /* 0x00002000 */
77     "SUPPORT_DESMD5",           /* 0x00004000 */
78     "NEW_PRINC",                /* 0x00008000 */
79 };
80
81 char *getenv();
82 int exit_status = 0;
83 char *def_realm = NULL;
84 char *whoami = NULL;
85 time_t get_date();
86
87 void *handle = NULL;
88 krb5_context context;
89 char *ccache_name = NULL;
90
91 void usage()
92 {
93     fprintf(stderr,
94          "Usage: %s [-r realm] [-p principal] [-q query] [clnt|local args]\n"
95     "\tclnt args: [-s admin_server[:port]] [[-c ccache]|[-k [-t keytab]]]\n"
96          "\tlocal args: [-d dbname] [-e \"enc:salt ...\"] [-m]\n", whoami);
97     exit(1);
98 }
99
100 char *strdur(duration)
101     time_t duration;
102 {
103     static char out[50];
104     int days, hours, minutes, seconds;
105     
106     days = duration / (24 * 3600);
107     duration %= 24 * 3600;
108     hours = duration / 3600;
109     duration %= 3600;
110     minutes = duration / 60;
111     duration %= 60;
112     seconds = duration;
113     sprintf(out, "%d %s %02d:%02d:%02d", days, days == 1 ? "day" : "days",
114             hours, minutes, seconds);
115     return out;
116 }
117
118 char *strdate(when)
119     krb5_timestamp when;
120 {
121     struct tm *tm;
122     static char out[30];
123     
124     time_t lcltim = when;
125     tm = localtime(&lcltim);
126     strftime(out, 30, "%a %b %d %H:%M:%S %Z %Y", tm);
127     return out;
128 }
129
130 /* this is a wrapper to go around krb5_parse_principal so we can set
131    the default realm up properly */
132 krb5_error_code kadmin_parse_name(name, principal)
133     char *name;
134     krb5_principal *principal;
135 {
136     char *cp, *fullname;
137     krb5_error_code retval;
138     
139     /* assumes def_realm is initialized! */
140     fullname = (char *)malloc(strlen(name) + 1 + strlen(def_realm) + 1);
141     if (fullname == NULL)
142         return ENOMEM;
143     strcpy(fullname, name);
144     cp = strchr(fullname, '@');
145     while (cp) {
146         if (cp - fullname && *(cp - 1) != '\\')
147             break;
148         else
149             cp = strchr(cp, '@');
150     }
151     if (cp == NULL) {
152         strcat(fullname, "@");
153         strcat(fullname, def_realm);
154     }
155     retval = krb5_parse_name(context, fullname, principal);
156     free(fullname);
157     return retval;
158 }
159
160 char *kadmin_startup(argc, argv)
161     int argc;
162     char *argv[];
163 {
164     extern krb5_kt_ops krb5_ktf_writable_ops;
165     extern char *optarg;
166     char *princstr = NULL, *keytab_name = NULL, *query = NULL;
167     char *password = NULL;
168     char *luser, *canon, *cp;
169     int optchar, freeprinc = 0, use_keytab = 0;
170     struct passwd *pw;
171     kadm5_ret_t retval;
172     krb5_ccache cc;
173     krb5_principal princ;
174     kadm5_config_params params;
175
176     memset((char *) &params, 0, sizeof(params));
177     
178     if (retval = krb5_init_context(&context)) {
179          com_err(whoami, retval, "while initializing krb5 library");
180          exit(1);
181     }
182                      
183     while ((optchar = getopt(argc, argv, "r:p:kq:w:d:s:mc:t:e:")) != EOF) {
184         switch (optchar) {
185         case 'r':
186             def_realm = optarg;
187             break;
188         case 'p':
189             princstr = optarg;
190             break;
191         case 'c':
192             ccache_name = optarg;
193             break;
194         case 'k':
195             use_keytab++;
196             break;
197        case 't':
198             keytab_name = optarg;
199             break;
200         case 'w':
201             password = optarg;
202             break;
203         case 'q':
204             query = optarg;
205             break;
206         case 'd':
207             params.dbname = optarg;
208             params.mask |= KADM5_CONFIG_DBNAME;
209             break;
210         case 's':
211             params.admin_server = optarg;
212             params.mask |= KADM5_CONFIG_ADMIN_SERVER;
213             break;
214         case 'm':
215             params.mkey_from_kbd = 1;
216             params.mask |= KADM5_CONFIG_MKEY_FROM_KBD;
217             break;
218         case 'e':
219             retval = krb5_string_to_keysalts(optarg,
220                                              ", \t",
221                                              ":.-",
222                                              0,
223                                              &params.keysalts,
224                                              &params.num_keysalts);
225             if (retval) {
226                  com_err(whoami, retval, "while parsing keysalts %s", optarg);
227                  exit(1);
228             }
229             params.mask |= KADM5_CONFIG_ENCTYPES;
230             break;
231         default:
232             usage();
233         }
234     }
235     if ((ccache_name && use_keytab) ||
236         (keytab_name && !use_keytab))
237          usage();
238
239     if (def_realm == NULL && krb5_get_default_realm(context, &def_realm)) {
240         if (freeprinc)
241             free(princstr);
242         fprintf(stderr, "%s: unable to get default realm\n", whoami);
243         exit(1);
244     }
245
246     params.mask |= KADM5_CONFIG_REALM;
247     params.realm = def_realm;
248
249     /*
250      * Set cc to an open credentials cache, either specified by the -c
251      * argument or the default.
252      */
253     if (ccache_name == NULL) {
254          if (retval = krb5_cc_default(context, &cc)) {
255               com_err(whoami, retval,
256                       "while opening default credentials cache");
257               exit(1);
258          }
259     } else {
260          if (retval = krb5_cc_resolve(context, ccache_name, &cc)) {
261               com_err(whoami, retval,
262                       "while opening credentials cache %s", ccache_name);
263               exit(1);
264          }
265     }
266
267     /*
268      * If no principal name is specified: If a ccache was specified
269      * and its primary principal name can be read, it is used, else if
270      * a keytab was specified, the principal name is host/hostname,
271      * otherwise append "/admin" to the primary name of the default
272      * ccache, $USER, or pw_name.
273      *
274      * Gee, 100+ lines to figure out the client principal name.  This
275      * should be compressed...
276      */
277     
278     if (princstr == NULL) {
279         if (ccache_name != NULL &&
280             !krb5_cc_get_principal(context, cc, &princ)) {
281              if (retval = krb5_unparse_name(context, princ, &princstr)) {
282                   com_err(whoami, retval,
283                           "while canonicalizing principal name");
284                   krb5_free_principal(context, princ);
285                   exit(1);
286              }
287              krb5_free_principal(context, princ);
288              freeprinc++;
289         } else if (use_keytab != 0) {
290              if (retval = krb5_sname_to_principal(context, NULL,
291                                                   "host",
292                                                   KRB5_NT_SRV_HST,
293                                                   &princ)) {
294                   com_err(whoami, retval,
295                           "creating host service principal");
296                   exit(1);
297              }
298              if (retval = krb5_unparse_name(context, princ, &princstr)) {
299                   com_err(whoami, retval,
300                           "while canonicalizing principal name");
301                   krb5_free_principal(context, princ);
302                   exit(1);
303              }
304              krb5_free_principal(context, princ);
305              freeprinc++;
306         } else if (!krb5_cc_get_principal(context, cc, &princ)) {
307             char *realm = NULL;
308             if (krb5_unparse_name(context, princ, &canon)) {
309                 fprintf(stderr,
310                         "%s: unable to canonicalize principal\n", whoami);
311                 krb5_free_principal(context, princ);
312                 exit(1);
313             }
314             /* strip out realm of principal if it's there */
315             realm = strchr(canon, '@');
316             while (realm) {
317                 if (realm - canon && *(realm - 1) != '\\')
318                     break;
319                 else
320                     realm = strchr(realm, '@');
321             }
322             if (realm)
323                 *realm++ = '\0';
324             cp = strchr(canon, '/');
325             while (cp) {
326                 if (cp - canon && *(cp - 1) != '\\')
327                     break;
328                 else
329                     cp = strchr(cp, '/');
330             }
331             if (cp != NULL)
332                 *cp = '\0';
333             princstr = (char*)malloc(strlen(canon) + 6 /* "/admin" */ +
334                                      (realm ? 1 + strlen(realm) : 0) + 1);
335             if (princstr == NULL) {
336                 fprintf(stderr, "%s: out of memory\n", whoami);
337                 exit(1);
338             }
339             strcpy(princstr, canon);
340             strcat(princstr, "/admin");
341             if (realm) {
342                 strcat(princstr, "@");
343                 strcat(princstr, realm);
344             }
345             free(canon);
346             krb5_free_principal(context, princ);
347             freeprinc++;
348         } else if (luser = getenv("USER")) {
349             princstr = (char *) malloc(strlen(luser) + 7 /* "/admin@" */
350                               + strlen(def_realm) + 1);
351             if (princstr == NULL) {
352                 fprintf(stderr, "%s: out of memory\n", whoami);
353                 exit(1);
354             }
355             strcpy(princstr, luser);
356             strcat(princstr, "/admin");
357             strcat(princstr, "@");
358             strcat(princstr, def_realm);
359             freeprinc++;
360         } else if (pw = getpwuid(getuid())) {
361             princstr = (char *) malloc(strlen(pw->pw_name) + 7 /* "/admin@" */
362                               + strlen(def_realm) + 1);
363             if (princstr == NULL) {
364                 fprintf(stderr, "%s: out of memory\n", whoami);
365                 exit(1);
366             }
367             strcpy(princstr, pw->pw_name);
368             strcat(princstr, "/admin@");
369             strcat(princstr, def_realm);
370             freeprinc++;
371         } else {
372             fprintf(stderr, "%s: unable to figure out a principal name\n",
373                     whoami);
374             exit(1);
375         }
376     }
377
378     /*
379      * Initialize the kadm5 connection.  If we were given a ccache,
380      * use it.  Otherwise, use/prompt for the password.
381      */
382     if (ccache_name) {
383          printf("Authenticating as principal %s with existing credentials.\n",
384                 princstr);
385          retval = kadm5_init_with_creds(princstr, cc,
386                                         KADM5_ADMIN_SERVICE, 
387                                         &params,
388                                         KADM5_STRUCT_VERSION,
389                                         KADM5_API_VERSION_2,
390                                         &handle);
391     } else if (use_keytab) {
392          printf("Authenticating as principal %s with keytab %s.\n",
393                 princstr, keytab_name);
394          retval = kadm5_init_with_skey(princstr, keytab_name,
395                                        KADM5_ADMIN_SERVICE, 
396                                        &params,
397                                        KADM5_STRUCT_VERSION,
398                                        KADM5_API_VERSION_2,
399                                        &handle);
400     } else {
401          printf("Authenticating as principal %s with password.\n",
402                 princstr);
403          retval = kadm5_init_with_password(princstr, password,
404                                            KADM5_ADMIN_SERVICE, 
405                                            &params,
406                                            KADM5_STRUCT_VERSION,
407                                            KADM5_API_VERSION_2,
408                                            &handle);
409     }
410     if (retval) {
411         com_err(whoami, retval, "while initializing %s interface", whoami);
412         if (retval == KADM5_BAD_CLIENT_PARAMS ||
413             retval == KADM5_BAD_SERVER_PARAMS)
414              usage();
415         exit(1);
416     }
417     if (freeprinc)
418         free(princstr);
419
420     if (retval = krb5_cc_close(context, cc)) {
421          com_err(whoami, retval, "while closing ccache %s",
422                  ccache_name);
423          exit(1);
424     }
425
426     /* register the WRFILE keytab type and set it as the default */
427     if (retval = krb5_kt_register(context, &krb5_ktf_writable_ops)) {
428          com_err(whoami, retval,
429                  "while registering writable key table functions");
430          exit(1);
431     }
432     {
433 #define DEFAULT_KEYTAB "WRFILE:/etc/krb5.keytab"
434          /* XXX krb5_defkeyname is an internal library global and
435             should go away */
436          extern char *krb5_defkeyname;
437          krb5_defkeyname = DEFAULT_KEYTAB;
438     }
439     
440     return query;
441 }
442
443 int quit()
444 {
445      krb5_ccache cc;
446      int retval;
447
448      kadm5_destroy(handle);
449      if (ccache_name != NULL) {
450           fprintf(stderr,
451                   "\n\a\a\aAdministration credentials NOT DESTROYED.\n");
452      }
453
454      /* insert more random cleanup here */
455      return 0;
456 }
457
458 void kadmin_delprinc(argc, argv)
459     int argc;
460     char *argv[];
461 {
462     kadm5_ret_t retval;
463     krb5_principal princ;
464     char *canon;
465     char reply[5];
466     
467     if (! (argc == 2 ||
468            (argc == 3 && !strcmp("-force", argv[1])))) {
469         fprintf(stderr, "usage: delete_principal [-force] principal\n");
470         return;
471     }
472     retval = kadmin_parse_name(argv[argc - 1], &princ);
473     if (retval) {
474         com_err("delete_principal", retval, "while parsing principal name");
475         return;
476     }
477     retval = krb5_unparse_name(context, princ, &canon);
478     if (retval) {
479         com_err("delete_principal", retval,
480                 "while canonicalizing principal");
481         krb5_free_principal(context, princ);
482         return;
483     }
484     if (argc == 2) {
485         printf("Are you sure you want to delete the principal \"%s\"? (yes/no): ", canon);
486         fgets(reply, sizeof (reply), stdin);
487         if (strcmp("yes\n", reply)) {
488             fprintf(stderr, "Principal \"%s\" not deleted\n", canon);
489             free(canon);
490             krb5_free_principal(context, princ);
491             return;
492         }
493     }
494     retval = kadm5_delete_principal(handle, princ);
495     krb5_free_principal(context, princ);
496     if (retval) {
497         com_err("delete_principal", retval,
498                 "while deleteing principal \"%s\"", canon);
499         free(canon);
500         return;
501     }
502     printf("Principal \"%s\" deleted.\nMake sure that you have removed this principal from all ACLs before reusing.\n", canon);
503     free(canon);
504     return;
505 }
506
507 void kadmin_cpw(argc, argv)
508     int argc;
509     char *argv[];
510 {
511     kadm5_ret_t retval;
512     static char newpw[1024];
513     static char prompt1[1024], prompt2[1024];
514     char *canon;
515     krb5_principal princ;
516     
517     if (argc < 2) {
518          goto usage;
519     }
520     
521     retval = kadmin_parse_name(argv[argc - 1], &princ);
522     if (retval) {
523         com_err("change_password", retval, "while parsing principal name");
524         return;
525     }
526     retval = krb5_unparse_name(context, princ, &canon);
527     if (retval) {
528         com_err("change_password", retval, "while canonicalizing principal");
529         krb5_free_principal(context, princ);
530         return;
531     }
532     if ((argc == 4) && (strlen(argv[1]) == 3) && !strcmp("-pw", argv[1])) {
533         retval = kadm5_chpass_principal(handle, princ, argv[2]);
534         krb5_free_principal(context, princ);
535         if (retval) {
536             com_err("change_password", retval,
537                     "while changing password for \"%s\".", canon);
538             free(canon);
539             return;
540         }
541         printf("Password for \"%s\" changed.\n", canon);
542         free(canon);
543         return;
544     } else if ((argc == 3) && (strlen(argv[1]) == 8) &&
545                !strcmp("-randkey", argv[1])) {
546         retval = kadm5_randkey_principal(handle, princ, NULL, NULL);
547         krb5_free_principal(context, princ);
548         if (retval) {
549             com_err("change_password", retval,
550                     "while randomizing key for \"%s\".", canon);
551             free(canon);
552             return;
553         }
554         printf("Key for \"%s\" randomized.\n", canon);
555         free(canon);
556         return;
557     } else if (argc == 2) {
558         int i = sizeof (newpw) - 1;
559         
560         sprintf(prompt1, "Enter password for principal \"%.900s\": ",
561                 argv[1]);
562         sprintf(prompt2,
563                 "Re-enter password for principal \"%.900s\": ",
564                 argv[1]);
565         retval = krb5_read_password(context, prompt1, prompt2,
566                                     newpw, &i);
567         if (retval) {
568             com_err("change_password", retval,
569                     "while reading password for \"%s\".", canon);
570             free(canon);
571             krb5_free_principal(context, princ);
572             return;
573         }
574         retval = kadm5_chpass_principal(handle, princ, newpw);
575         krb5_free_principal(context, princ);
576         memset(newpw, 0, sizeof (newpw));
577         if (retval) {
578             com_err("change_password", retval,
579                     "while changing password for \"%s\".", canon);
580             free(canon);
581             return;
582         }
583         printf("Password for \"%s\" changed.\n", canon);
584         free(canon);
585         return;
586    } else {
587         free(canon);
588         krb5_free_principal(context, princ);
589    usage:
590         fprintf(stderr,
591                 "usage: change_password [-randkey] [-pw password] "
592                 "principal\n");
593         return;
594    }
595 }
596
597 int kadmin_parse_princ_args(argc, argv, oprinc, mask, pass, randkey, caller)
598     int argc;
599     char *argv[];
600     kadm5_principal_ent_t oprinc;
601     long *mask;
602     char **pass;
603     int *randkey;
604     char *caller;
605 {
606     int i, j, attrib_set;
607     time_t date;
608     time_t now;
609     krb5_error_code retval;
610     
611     *mask = 0;
612     *pass = NULL;
613     time(&now);
614     *randkey = 0;
615     for (i = 1; i < argc - 1; i++) {
616         attrib_set = 0;
617         if (strlen(argv[i]) == 7 &&
618             !strcmp("-expire", argv[i])) {
619             if (++i > argc - 2)
620                 return -1;
621             else {
622                 date = get_date(argv[i], NULL);
623                 if (date == (time_t)-1) {
624                      fprintf(stderr, "Invalid date specification \"%s\".\n",
625                              argv[i]);
626                      return -1;
627                 }
628                 oprinc->princ_expire_time = date;
629                 *mask |= KADM5_PRINC_EXPIRE_TIME;
630                 continue;
631             }
632         }
633         if (strlen(argv[i]) == 9 &&
634             !strcmp("-pwexpire", argv[i])) {
635             if (++i > argc - 2)
636                 return -1;
637             else {
638                 date = get_date(argv[i], NULL);
639                 if (date == (time_t)-1) {
640                      fprintf(stderr, "Invalid date specification \"%s\".\n",
641                              argv[i]);
642                      return -1;
643                 }
644                 oprinc->pw_expiration = date;
645                 *mask |= KADM5_PW_EXPIRATION;
646                 continue;
647             }
648         }
649         if (strlen(argv[i]) == 8 &&
650             !strcmp("-maxlife", argv[i])) {
651             if (++i > argc - 2)
652                 return -1;
653             else {
654                 date = get_date(argv[i], NULL);
655                 if (date == (time_t)-1) {
656                      fprintf(stderr, "Invalid date specification \"%s\".\n",
657                              argv[i]);
658                      return -1;
659                 }
660                 oprinc->max_life = date - now;
661                 *mask |= KADM5_MAX_LIFE;
662                 continue;
663             }
664         }
665         if (strlen(argv[i]) == 13 &&
666             !strcmp("-maxrenewlife", argv[i])) {
667             if (++i > argc - 2)
668                 return -1;
669             else {
670                 date = get_date(argv[i], NULL);
671                 if (date == (time_t)-1) {
672                      fprintf(stderr, "Invalid date specification \"%s\".\n",
673                              argv[i]);
674                      return -1;
675                 }
676                 oprinc->max_renewable_life = date - now;
677                 *mask |= KADM5_MAX_RLIFE;
678                 continue;
679             }
680         }
681         if (strlen(argv[i]) == 5 &&
682             !strcmp("-kvno", argv[i])) {
683             if (++i > argc - 2)
684                 return -1;
685             else {
686                 oprinc->kvno = atoi(argv[i]);
687                 *mask |= KADM5_KVNO;
688                 continue;
689             }
690         }
691         if (strlen(argv[i]) == 7 &&
692             !strcmp("-policy", argv[i])) {
693             if (++i > argc - 2)
694                 return -1;
695             else {
696                 oprinc->policy = argv[i];
697                 *mask |= KADM5_POLICY;
698                 continue;
699             }
700         }
701         if (strlen(argv[i]) == 12 &&
702             !strcmp("-clearpolicy", argv[i])) {
703             oprinc->policy = NULL;
704             *mask |= KADM5_POLICY_CLR;
705             continue;
706         }
707         if (strlen(argv[i]) == 3 &&
708             !strcmp("-pw", argv[i])) {
709             if (++i > argc - 2)
710                 return -1;
711             else {
712                 *pass = argv[i];
713                 continue;
714             }
715         }
716         if (strlen(argv[i]) == 8 &&
717             !strcmp("-randkey", argv[i])) {
718             ++*randkey;
719             continue;
720         }
721         for (j = 0; j < sizeof (flags) / sizeof (struct pflag); j++) {
722             if (strlen(argv[i]) == flags[j].flaglen + 1 &&
723                 !strcmp(flags[j].flagname,
724                         &argv[i][1] /* strip off leading + or - */)) {
725                 if (flags[j].set && argv[i][0] == '-' ||
726                     !flags[j].set && argv[i][0] == '+') {
727                     oprinc->attributes |= flags[j].theflag;
728                     *mask |= KADM5_ATTRIBUTES;
729                     attrib_set++;
730                     break;
731                 } else if (flags[j].set && argv[i][0] == '+' ||
732                            !flags[j].set && argv[i][0] == '-') {
733                     oprinc->attributes &= ~flags[j].theflag;
734                     *mask |= KADM5_ATTRIBUTES;
735                     attrib_set++;
736                     break;
737                 } else {
738                     return -1;
739                 }
740             }
741         }
742         if (!attrib_set)
743             return -1;          /* nothing was parsed */
744     }
745     if (i != argc - 1) {
746         return -1;
747     }
748     retval = kadmin_parse_name(argv[i], &oprinc->principal);
749     if (retval) {
750         com_err(caller, retval, "while parsing principal");
751         return -1;
752     }
753     return 0;
754 }
755
756 void kadmin_addprinc_usage(func)
757    char *func;
758 {
759      fprintf(stderr, "usage: %s [options] principal\n", func);
760      fprintf(stderr, "\toptions are:\n");
761      fprintf(stderr, "\t\t[-expire expdate] [-pwexpire pwexpdate] [-maxlife maxtixlife]\n\t\t[-kvno kvno] [-policy policy] [-randkey] [-pw password]\n\t\t[-maxrenewlife maxrenewlife] [{+|-}attribute]\n");
762      fprintf(stderr, "\tattributes are:\n");
763      fprintf(stderr, "%s%s%s",
764              "\t\tallow_postdated allow_forwardable allow_tgs_req allow_renewable\n",
765              "\t\tallow_proxiable allow_dup_skey allow_tix requires_preauth\n",
766              "\t\trequires_hwauth needchange allow_svr password_changing_service\n");
767 }
768
769 void kadmin_modprinc_usage(func)
770    char *func;
771 {
772      fprintf(stderr, "usage: %s [options] principal\n", func);
773      fprintf(stderr, "\toptions are:\n");
774      fprintf(stderr, "\t\t[-expire expdate] [-pwexpire pwexpdate] [-maxlife maxtixlife]\n\t\t[-kvno kvno] [-policy policy] [-clearpolicy]\n\t\t[-maxrenewlife maxrenewlife] [{+|-}attribute]\n");
775      fprintf(stderr, "\tattributes are:\n");
776      fprintf(stderr, "%s%s%s",
777              "\t\tallow_postdated allow_forwardable allow_tgs_req allow_renewable\n",
778              "\t\tallow_proxiable allow_dup_skey allow_tix requires_preauth\n",
779              "\t\trequires_hwauth needchange allow_svr password_changing_service\n");
780 }
781
782 void kadmin_addprinc(argc, argv)
783     int argc;
784     char *argv[];
785 {
786     kadm5_principal_ent_rec princ;
787     kadm5_policy_ent_rec defpol;
788     long mask;
789     int randkey = 0, i;
790     char *pass, *canon;
791     krb5_error_code retval;
792     static char newpw[1024], dummybuf[256];
793     static char prompt1[1024], prompt2[1024];
794
795     if (dummybuf[0] == 0) {
796          for (i = 0; i < 256; i++)
797               dummybuf[i] = (i+1) % 256;
798     }
799     
800     /* Zero all fields in request structure */
801     memset(&princ, 0, sizeof(princ));
802
803     princ.attributes = 0;
804     if (kadmin_parse_princ_args(argc, argv,
805                                 &princ, &mask, &pass, &randkey,
806                                 "add_principal")) {
807          kadmin_addprinc_usage("add_principal");
808          return;
809     }
810
811     retval = krb5_unparse_name(context, princ.principal, &canon);
812     if (retval) {
813         com_err("add_principal",
814                 retval, "while canonicalizing principal");
815         krb5_free_principal(context, princ.principal);
816         return;
817     }
818
819     /*
820      * If -policy was not specified, and -clearpolicy was not
821      * specified, and the policy "default" exists, assign it.  If
822      * -clearpolicy was specified, then KADM5_POLICY_CLR should be
823      * unset, since it is never valid for kadm5_create_principal.
824      */
825     if ((! (mask & KADM5_POLICY)) &&
826         (! (mask & KADM5_POLICY_CLR))) {
827          if (! kadm5_get_policy(handle, "default", &defpol)) {
828               fprintf(stderr,
829                 "NOTICE: no policy specified for %s; assigning \"default\"\n",
830                       canon);
831               princ.policy = "default";
832               mask |= KADM5_POLICY;
833               (void) kadm5_free_policy_ent(handle, &defpol);
834          } else
835               fprintf(stderr,
836              "WARNING: no policy specified for %s; defaulting to no policy\n",
837                       canon);
838     }
839     mask &= ~KADM5_POLICY_CLR;
840     
841     if (randkey) {              /* do special stuff if -randkey specified */
842         princ.attributes |= KRB5_KDB_DISALLOW_ALL_TIX; /* set notix */
843         mask |= KADM5_ATTRIBUTES;
844         pass = dummybuf;
845     } else if (pass == NULL) {
846         int i = sizeof (newpw) - 1;
847         
848         sprintf(prompt1, "Enter password for principal \"%.900s\": ",
849                 canon);
850         sprintf(prompt2,
851                 "Re-enter password for principal \"%.900s\": ",
852                 canon);
853         retval = krb5_read_password(context, prompt1, prompt2,
854                                     newpw, &i);
855         if (retval) {
856             com_err("add_principal", retval,
857                     "while reading password for \"%s\".", canon);
858             free(canon);
859             krb5_free_principal(context, princ.principal);
860             return;
861         }
862         pass = newpw;
863     }
864     mask |= KADM5_PRINCIPAL;
865     retval = kadm5_create_principal(handle, &princ, mask, pass);
866     if (retval) {
867         com_err("add_principal", retval, "while creating \"%s\".",
868                 canon);
869         krb5_free_principal(context, princ.principal);
870         free(canon);
871         return;
872     }
873     if (randkey) {              /* more special stuff for -randkey */
874         retval = kadm5_randkey_principal(handle, princ.principal,
875                                          NULL, NULL);
876         if (retval) {
877             com_err("add_principal", retval,
878                     "while randomizing key for \"%s\".", canon);
879             krb5_free_principal(context, princ.principal);
880             free(canon);
881             return;
882         }
883         princ.attributes &= ~KRB5_KDB_DISALLOW_ALL_TIX; /* clear notix */
884         mask = KADM5_ATTRIBUTES;
885         retval = kadm5_modify_principal(handle, &princ, mask);
886         if (retval) {
887             com_err("add_principal", retval,
888                     "while clearing DISALLOW_ALL_TIX for \"%s\".", canon);
889             krb5_free_principal(context, princ.principal);
890             free(canon);
891             return;
892         }
893     }
894     krb5_free_principal(context, princ.principal);
895     printf("Principal \"%s\" created.\n", canon);
896     free(canon);
897 }
898
899 void kadmin_modprinc(argc, argv)
900     int argc;
901     char *argv[];
902 {
903     kadm5_principal_ent_rec princ, oldprinc;
904     krb5_principal kprinc;
905     long mask;
906     krb5_error_code retval;
907     char *pass, *canon;
908     int randkey = 0;
909
910     if (argc < 2) {
911          kadmin_modprinc_usage("modify_principal");
912          return;
913     }
914
915     memset(&oldprinc, 0, sizeof(oldprinc));
916     memset(&princ, 0, sizeof(princ));
917
918     retval = kadmin_parse_name(argv[argc - 1], &kprinc);
919     if (retval) {
920         com_err("modify_principal", retval, "while parsing principal");
921         return;
922     }
923     retval = krb5_unparse_name(context, kprinc, &canon);
924     if (retval) {
925         com_err("modify_principal", retval,
926                 "while canonicalizing principal");
927         krb5_free_principal(context, kprinc);
928         return;
929     }
930     retval = kadm5_get_principal(handle, kprinc, &oldprinc,
931                                  KADM5_PRINCIPAL_NORMAL_MASK);
932     krb5_free_principal(context, kprinc);
933     if (retval) {
934         com_err("modify_principal", retval, "while getting \"%s\".",
935                 canon);
936         free(canon);
937         return;
938     }
939     princ.attributes = oldprinc.attributes;
940     kadm5_free_principal_ent(handle, &oldprinc);
941     retval = kadmin_parse_princ_args(argc, argv,
942                                      &princ, &mask,
943                                      &pass, &randkey,
944                                      "modify_principal");
945     if (retval) {
946         kadmin_modprinc_usage("modify_principal");
947         free(canon);
948         return;
949     }
950     if (randkey) {
951         fprintf(stderr, "modify_principal: -randkey not allowed\n");
952         krb5_free_principal(context, princ.principal);
953         free(canon);
954         return;
955     }
956     retval = kadm5_modify_principal(handle, &princ, mask);
957     krb5_free_principal(context, princ.principal);
958     if (retval) {
959         com_err("modify_principal", retval,
960                 "while modifying \"%s\".", canon);
961         free(canon);
962         return;
963     }
964     printf("Principal \"%s\" modified.\n", canon);
965     free(canon);
966 }
967
968 void kadmin_getprinc(argc, argv)
969     int argc;
970     char *argv[];
971 {
972     kadm5_principal_ent_rec dprinc;
973     krb5_principal princ;
974     krb5_error_code retval;
975     char *canon, *modcanon;
976     int i;
977     
978     if (! (argc == 2 ||
979            (argc == 3 && !strcmp("-terse", argv[1])))) {
980         fprintf(stderr, "usage: get_principal [-terse] principal\n");
981         return;
982     }
983
984
985     memset(&dprinc, 0, sizeof(dprinc));
986     memset(&princ, 0, sizeof(princ));
987
988     retval = kadmin_parse_name(argv[argc - 1], &princ);
989     if (retval) {
990         com_err("get_principal", retval, "while parsing principal");
991         return;
992     }
993     retval = krb5_unparse_name(context, princ, &canon);
994     if (retval) {
995         com_err("get_principal", retval, "while canonicalizing principal");
996         krb5_free_principal(context, princ);
997         return;
998     }
999     retval = kadm5_get_principal(handle, princ, &dprinc,
1000                                  KADM5_PRINCIPAL_NORMAL_MASK | KADM5_KEY_DATA);
1001     krb5_free_principal(context, princ);
1002     if (retval) {
1003         com_err("get_principal", retval, "while retrieving \"%s\".", canon);
1004         free(canon);
1005         return;
1006     }
1007     retval = krb5_unparse_name(context, dprinc.mod_name, &modcanon);
1008     if (retval) {
1009         com_err("get_principal", retval, "while unparsing modname");
1010         kadm5_free_principal_ent(handle, &dprinc);
1011         free(canon);
1012         return;
1013     }
1014     if (argc == 2) {
1015         printf("Principal: %s\n", canon);
1016         printf("Expiration date: %s\n", dprinc.princ_expire_time ?
1017                strdate(dprinc.princ_expire_time) : "[never]");
1018         printf("Last password change: %s\n", dprinc.last_pwd_change ? 
1019                strdate(dprinc.last_pwd_change) : "[never]");
1020         printf("Password expiration date: %s\n",
1021                dprinc.pw_expiration ?
1022                strdate(dprinc.pw_expiration) : "[none]");
1023         printf("Maximum ticket life: %s\n", strdur(dprinc.max_life));
1024         printf("Maximum renewable life: %s\n", strdur(dprinc.max_renewable_life));
1025         printf("Last modified: %s (%s)\n", strdate(dprinc.mod_date), modcanon);
1026         printf("Last successful authentication: %s\n",
1027                dprinc.last_success ? strdate(dprinc.last_success) :
1028                "[never]"); 
1029         printf("Last failed authentication: %s\n",
1030                dprinc.last_failed ? strdate(dprinc.last_failed) :
1031                "[never]");
1032         printf("Failed password attempts: %d\n",
1033                dprinc.fail_auth_count);
1034         printf("Number of keys: %d\n", dprinc.n_key_data);
1035         for (i = 0; i < dprinc.n_key_data; i++) {
1036              krb5_key_data *key_data = &dprinc.key_data[i];
1037              char enctype[BUFSIZ], salttype[BUFSIZ];
1038              
1039              if (krb5_enctype_to_string(key_data->key_data_type[0],
1040                                         enctype, sizeof(enctype)))
1041                   sprintf(enctype, "<Encryption type 0x%x>",
1042                           key_data->key_data_type[0]);
1043              printf("Key: vno %d, %s, ", key_data->key_data_kvno, enctype);
1044              if (key_data->key_data_ver > 1) {
1045                   if (krb5_salttype_to_string(key_data->key_data_type[1],
1046                                               salttype, sizeof(salttype)))
1047                        sprintf(salttype, "<Salt type 0x%x>",
1048                                key_data->key_data_type[1]);
1049                   printf("%s\n", salttype);
1050              } else
1051                   printf("no salt\n");
1052         }
1053         
1054         printf("Attributes:");
1055         for (i = 0; i < sizeof (prflags) / sizeof (char *); i++) {
1056             if (dprinc.attributes & (krb5_flags) 1 << i)
1057                 printf(" %s", prflags[i]);
1058         }
1059         printf("\n");
1060         printf("Policy: %s\n", dprinc.policy ? dprinc.policy : "[none]");
1061     } else {
1062         printf("\"%s\"\t%d\t%d\t%d\t%d\t\"%s\"\t%d\t%d\t%d\t%d\t\"%s\""
1063                "\t%d\t%d\t%d\t%d\t%d",
1064                canon, dprinc.princ_expire_time, dprinc.last_pwd_change,
1065                dprinc.pw_expiration, dprinc.max_life, modcanon,
1066                dprinc.mod_date, dprinc.attributes, dprinc.kvno,
1067                dprinc.mkvno, dprinc.policy ? dprinc.policy : "[none]",
1068                dprinc.max_renewable_life, dprinc.last_success,
1069                dprinc.last_failed, dprinc.fail_auth_count,
1070                dprinc.n_key_data);
1071         for (i = 0; i < dprinc.n_key_data; i++)
1072              printf("\t%d\t%d\t%d\t%d",
1073                     dprinc.key_data[i].key_data_ver,
1074                     dprinc.key_data[i].key_data_kvno,
1075                     dprinc.key_data[i].key_data_type[0],
1076                     dprinc.key_data[i].key_data_type[1]);
1077         printf("\n");
1078    }
1079     free(modcanon);
1080     kadm5_free_principal_ent(handle, &dprinc);
1081     free(canon);
1082 }
1083
1084 void kadmin_getprincs(argc, argv)
1085     int argc;
1086     char *argv[];
1087 {
1088     krb5_error_code retval;
1089     char *exp, **names;
1090     int i, count;
1091
1092     exp = NULL;
1093     if (! (argc == 1 || (argc == 2 && (exp = argv[1])))) {
1094         fprintf(stderr, "usage: get_principals [expression]\n");
1095         return;
1096     }
1097     retval = kadm5_get_principals(handle, exp, &names, &count);
1098     if (retval) {
1099         com_err("get_principals", retval, "while retrieving list.");
1100         return;
1101     }
1102     for (i = 0; i < count; i++)
1103          printf("%s\n", names[i]);
1104     kadm5_free_name_list(handle, names, count);
1105 }
1106
1107 int kadmin_parse_policy_args(argc, argv, policy, mask, caller)
1108     int argc;
1109     char *argv[];
1110     kadm5_policy_ent_t policy;
1111     long *mask;
1112     char *caller;
1113 {
1114     int i;
1115     time_t now;
1116     time_t date;
1117     krb5_error_code retval;
1118
1119     time(&now);
1120     *mask = 0;
1121     for (i = 1; i < argc - 1; i++) {
1122         if (strlen(argv[i]) == 8 &&
1123             !strcmp(argv[i], "-maxlife")) {
1124             if (++i > argc -2)
1125                 return -1;
1126             else {
1127                 date = get_date(argv[i], NULL);
1128                 if (date == (time_t)-1) {
1129                      fprintf(stderr, "Invalid date specification \"%s\".\n",
1130                              argv[i]);
1131                      return -1;
1132                 }
1133                 policy->pw_max_life = date - now;
1134                 *mask |= KADM5_PW_MAX_LIFE;
1135                 continue;
1136             }
1137         } else if (strlen(argv[i]) == 8 &&
1138                    !strcmp(argv[i], "-minlife")) {
1139             if (++i > argc - 2)
1140                 return -1;
1141             else {
1142                 date = get_date(argv[i], NULL);
1143                 if (date == (time_t)-1) {
1144                      fprintf(stderr, "Invalid date specification \"%s\".\n",
1145                              argv[i]);
1146                      return -1;
1147                 }
1148                 policy->pw_min_life = date - now;
1149                 *mask |= KADM5_PW_MIN_LIFE;
1150                 continue;
1151             }
1152         } else if (strlen(argv[i]) == 10 &&
1153             !strcmp(argv[i], "-minlength")) {
1154             if (++i > argc - 2)
1155                 return -1;
1156             else {
1157                 policy->pw_min_length = atoi(argv[i]);
1158                 *mask |= KADM5_PW_MIN_LENGTH;
1159                 continue;
1160             }
1161         } else if (strlen(argv[i]) == 11 &&
1162                    !strcmp(argv[i], "-minclasses")) {
1163             if (++i > argc - 2)
1164                 return -1;
1165             else {
1166                 policy->pw_min_classes = atoi(argv[i]);
1167                 *mask |= KADM5_PW_MIN_CLASSES;
1168                 continue;
1169             }
1170         } else if (strlen(argv[i]) == 8 &&
1171                    !strcmp(argv[i], "-history")) {
1172             if (++i > argc - 2)
1173                 return -1;
1174             else {
1175                 policy->pw_history_num = atoi(argv[i]);
1176                 *mask |= KADM5_PW_HISTORY_NUM;
1177                 continue;
1178             }
1179         } else
1180             return -1;
1181     }
1182     if (i != argc -1) {
1183         fprintf(stderr, "%s: parser lost count!\n", caller);
1184         return -1;
1185     } else
1186         return 0;
1187 }
1188
1189 void kadmin_addmodpol_usage(func)
1190    char *func;
1191 {
1192      fprintf(stderr, "usage; %s [options] policy\n", func);
1193      fprintf(stderr, "\toptions are:\n");
1194      fprintf(stderr, "\t\t[-maxlife time] [-minlife time] [-minlength length]\n\t\t[-minclasses number] [-history number]\n");
1195 }
1196
1197 void kadmin_addpol(argc, argv)
1198     int argc;
1199     char *argv[];
1200 {
1201     krb5_error_code retval;
1202     long mask;
1203     kadm5_policy_ent_rec policy;
1204
1205     memset(&policy, 0, sizeof(policy));
1206     if (kadmin_parse_policy_args(argc, argv, &policy, &mask, "add_policy")) {
1207          kadmin_addmodpol_usage("add_policy");
1208          return;
1209     } else {
1210         policy.policy = argv[argc - 1];
1211         mask |= KADM5_POLICY;
1212         retval = kadm5_create_policy(handle, &policy, mask);
1213         if (retval) {
1214             com_err("add_policy", retval, "while creating policy \"%s\".",
1215                     policy.policy);
1216             return;
1217         }
1218     }
1219     return;
1220 }
1221
1222 void kadmin_modpol(argc, argv)
1223     int argc;
1224     char *argv[];
1225 {
1226     krb5_error_code retval;
1227     long mask;
1228     kadm5_policy_ent_rec policy;
1229
1230     memset(&policy, 0, sizeof(policy));
1231     if (kadmin_parse_policy_args(argc, argv, &policy, &mask,
1232                                  "modify_policy")) {
1233         kadmin_addmodpol_usage("modify_policy");
1234         return;
1235     } else {
1236         policy.policy = argv[argc - 1];
1237         retval = kadm5_modify_policy(handle, &policy, mask);
1238         if (retval) {
1239             com_err("modify_policy", retval, "while modifying policy \"%s\".",
1240                     policy.policy);
1241             return;
1242         }
1243     }
1244     return;
1245 }
1246
1247 void kadmin_delpol(argc, argv)
1248     int argc;
1249     char *argv[];
1250 {
1251     krb5_error_code retval;
1252     char reply[5];
1253     
1254     if (! (argc == 2 ||
1255            (argc == 3 && !strcmp("-force", argv[1])))) {
1256         fprintf(stderr, "usage: delete_policy [-force] policy\n");
1257         return;
1258     }
1259     if (argc == 2) {
1260         printf("Are you sure you want to delete the policy \"%s\"? (yes/no): ", argv[1]);
1261         fgets(reply, sizeof (reply), stdin);
1262         if (strcmp("yes\n", reply)) {
1263             fprintf(stderr, "Policy \"%s\" not deleted.\n", argv[1]);
1264             return;
1265         }
1266     }
1267     retval = kadm5_delete_policy(handle, argv[argc - 1]);
1268     if (retval) {
1269         com_err("delete_policy:", retval, "while deleting policy \"%s\"",
1270                 argv[argc - 1]);
1271         return;
1272     }
1273     return;
1274 }
1275
1276 void kadmin_getpol(argc, argv)
1277     int argc;
1278     char *argv[];
1279 {
1280     krb5_error_code retval;
1281     kadm5_policy_ent_rec policy;
1282     
1283     if (! (argc == 2 ||
1284            (argc == 3 && !strcmp("-terse", argv[1])))) {
1285         fprintf(stderr, "usage: get_policy [-terse] policy\n");
1286         return;
1287     }
1288     retval = kadm5_get_policy(handle, argv[argc - 1], &policy);
1289     if (retval) {
1290         com_err("get_policy", retval, "while retrieving policy \"%s\".",
1291                 argv[argc - 1]);
1292         return;
1293     }
1294     if (argc == 2) {
1295         printf("Policy: %s\n", policy.policy);
1296         printf("Maximum password life: %d\n", policy.pw_max_life);
1297         printf("Minimum password life: %d\n", policy.pw_min_life);
1298         printf("Minimum password length: %d\n", policy.pw_min_length);
1299         printf("Minimum number of password character classes: %d\n",
1300                policy.pw_min_classes);
1301         printf("Number of old keys kept: %d\n", policy.pw_history_num);
1302         printf("Reference count: %d\n", policy.policy_refcnt);
1303     } else {
1304         printf("\"%s\"\t%d\t%d\t%d\t%d\t%d\t%d\n",
1305                policy.policy, policy.pw_max_life, policy.pw_min_life,
1306                policy.pw_min_length, policy.pw_min_classes,
1307                policy.pw_history_num, policy.policy_refcnt);
1308     }
1309     kadm5_free_policy_ent(handle, &policy);
1310     return;
1311 }
1312
1313 void kadmin_getpols(argc, argv)
1314     int argc;
1315     char *argv[];
1316 {
1317     krb5_error_code retval;
1318     char *exp, **names;
1319     int i, count;
1320
1321     exp = NULL;
1322     if (! (argc == 1 || (argc == 2 && (exp = argv[1])))) {
1323         fprintf(stderr, "usage: get_policies [expression]\n");
1324         return;
1325     }
1326     retval = kadm5_get_policies(handle, exp, &names, &count);
1327     if (retval) {
1328         com_err("get_policies", retval, "while retrieving list.");
1329         return;
1330     }
1331     for (i = 0; i < count; i++)
1332          printf("%s\n", names[i]);
1333     kadm5_free_name_list(handle, names, count);
1334 }
1335
1336 void kadmin_getprivs(argc, argv)
1337     int argc;
1338     char *argv[];
1339 {
1340     static char *privs[] = {"GET", "ADD", "MODIFY", "DELETE"};
1341     krb5_error_code retval;
1342     int i;
1343     long plist;
1344
1345     if (argc != 1) {
1346         fprintf(stderr, "usage: get_privs\n");
1347         return;
1348     }
1349     retval = kadm5_get_privs(handle, &plist);
1350     if (retval) {
1351         com_err("get_privs", retval, "while retrieving privileges");
1352         return;
1353     }
1354     printf("current privileges:");
1355     for (i = 0; i < sizeof (privs) / sizeof (char *); i++) {
1356         if (plist & 1 << i)
1357             printf(" %s", privs[i]);
1358     }
1359     printf("\n");
1360     return;
1361 }