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