xref: /freebsd/crypto/krb5/src/kadmin/dbutil/kdb5_util.c (revision f1c4c3daccbaf3820f0e2224de53df12fc952fcc)
1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /* kadmin/dbutil/kdb5_util.c - Administer a KDC database */
3 /*
4  * (C) Copyright 1990,1991, 1996, 2008, 2009 by the Massachusetts Institute of Technology.
5  * All Rights Reserved.
6  *
7  * Export of this software from the United States of America may
8  *   require a specific license from the United States Government.
9  *   It is the responsibility of any person or organization contemplating
10  *   export to obtain such a license before exporting.
11  *
12  * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
13  * distribute this software and its documentation for any purpose and
14  * without fee is hereby granted, provided that the above copyright
15  * notice appear in all copies and that both that copyright notice and
16  * this permission notice appear in supporting documentation, and that
17  * the name of M.I.T. not be used in advertising or publicity pertaining
18  * to distribution of the software without specific, written prior
19  * permission.  Furthermore if you modify this software you must label
20  * your software as modified software and not distribute it in such a
21  * fashion that it might be confused with the original M.I.T. software.
22  * M.I.T. makes no representations about the suitability of
23  * this software for any purpose.  It is provided "as is" without express
24  * or implied warranty.
25  */
26 /*
27  * Copyright (C) 1998 by the FundsXpress, INC.
28  *
29  * All rights reserved.
30  *
31  * Export of this software from the United States of America may require
32  * a specific license from the United States Government.  It is the
33  * responsibility of any person or organization contemplating export to
34  * obtain such a license before exporting.
35  *
36  * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
37  * distribute this software and its documentation for any purpose and
38  * without fee is hereby granted, provided that the above copyright
39  * notice appear in all copies and that both that copyright notice and
40  * this permission notice appear in supporting documentation, and that
41  * the name of FundsXpress. not be used in advertising or publicity pertaining
42  * to distribution of the software without specific, written prior
43  * permission.  FundsXpress makes no representations about the suitability of
44  * this software for any purpose.  It is provided "as is" without express
45  * or implied warranty.
46  *
47  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
48  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
49  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
50  */
51 /*
52  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
53  * Use is subject to license terms.
54  */
55 
56 #include <k5-int.h>
57 #include <kadm5/admin.h>
58 #include <locale.h>
59 #include <adm_proto.h>
60 #include <time.h>
61 #include "kdb5_util.h"
62 
63 /*
64  * XXX Ick, ick, ick.  These global variables shouldn't be global....
65  */
66 char *mkey_password = 0;
67 
68 /*
69  * I can't figure out any way for this not to be global, given how ss
70  * works.
71  */
72 
73 int exit_status = 0;
74 krb5_context util_context;
75 kadm5_config_params global_params;
76 
usage(void)77 void usage(void)
78 {
79     fprintf(stderr,
80             _("Usage: kdb5_util [-r realm] [-d dbname] "
81               "[-k mkeytype] [-kv mkeyVNO]\n"
82               "\t        [-M mkeyname] [-m] [-sf stashfilename] "
83               "[-P password]\n"
84               "\t        [-x db_args]* cmd [cmd_options]\n"
85               "\tcreate  [-s]\n"
86               "\tdestroy [-f]\n"
87               "\tstash   [-f keyfile]\n"
88               "\tdump    [-b7|-r13|-r18] [-verbose]\n"
89               "\t        [-mkey_convert] [-new_mkey_file mkey_file]\n"
90               "\t        [-rev] [-recurse] [filename [princs...]]\n"
91               "\tload    [-b7|-r13|-r18] [-hash] [-verbose] [-update] "
92               "filename\n"
93               "\tark     [-e etype_list] principal\n"
94               "\tadd_mkey [-e etype] [-s]\n"
95               "\tuse_mkey kvno [time]\n"
96               "\tlist_mkeys\n"));
97     /* avoid a string length compiler warning */
98     fprintf(stderr,
99             _("\tupdate_princ_encryption [-f] [-n] [-v] [princ-pattern]\n"
100               "\tpurge_mkeys [-f] [-n] [-v]\n"
101               "\ttabdump [-H] [-c] [-e] [-n] [-o outfile] dumptype\n"
102               "\nwhere,\n\t[-x db_args]* - any number of database specific "
103               "arguments.\n"
104               "\t\t\tLook at each database documentation for supported "
105               "arguments\n"));
106     exit(1);
107 }
108 
109 krb5_keyblock master_keyblock;
110 krb5_kvno   master_kvno; /* fetched */
111 extern krb5_principal master_princ;
112 char *mkey_fullname;
113 krb5_db_entry *master_entry = NULL;
114 int     valid_master_key = 0;
115 
116 char *progname;
117 krb5_boolean manual_mkey = FALSE;
118 krb5_boolean dbactive = FALSE;
119 
120 static int open_db_and_mkey(void);
121 
122 static void add_random_key(int, char **);
123 
124 typedef void (*cmd_func)(int, char **);
125 
126 struct _cmd_table {
127     char *name;
128     cmd_func func;
129     int opendb;
130 } cmd_table[] = {
131     {"create", kdb5_create, 0},
132     {"destroy", kdb5_destroy, 1}, /* 1 opens the kdb */
133     {"stash", kdb5_stash, 1},
134     {"dump", dump_db, 1},
135     {"load", load_db, 0},
136     {"ark", add_random_key, 1},
137     {"add_mkey", kdb5_add_mkey, 1},
138     {"use_mkey", kdb5_use_mkey, 1},
139     {"list_mkeys", kdb5_list_mkeys, 1},
140     {"update_princ_encryption", kdb5_update_princ_encryption, 1},
141     {"purge_mkeys", kdb5_purge_mkeys, 1},
142     {"tabdump", tabdump, 1},
143     {NULL, NULL, 0},
144 };
145 
146 static struct _cmd_table *
cmd_lookup(char * name)147 cmd_lookup(char *name)
148 {
149     struct _cmd_table *cmd = cmd_table;
150     while (cmd->name) {
151         if (strcmp(cmd->name, name) == 0)
152             return cmd;
153         else
154             cmd++;
155     }
156 
157     return NULL;
158 }
159 
160 #define ARG_VAL (--argc > 0 ? (koptarg = *(++argv)) : (char *)(usage(), NULL))
161 
162 char **db5util_db_args = NULL;
163 size_t db5util_db_args_size = 0;
164 
165 static void
extended_com_err_fn(const char * myprog,errcode_t code,const char * fmt,va_list args)166 extended_com_err_fn(const char *myprog, errcode_t code, const char *fmt,
167                     va_list args)
168 {
169     const char *emsg;
170     if (code) {
171         emsg = krb5_get_error_message (util_context, code);
172         fprintf (stderr, "%s: %s ", myprog, emsg);
173         krb5_free_error_message (util_context, emsg);
174     } else {
175         fprintf (stderr, "%s: ", myprog);
176     }
177     vfprintf (stderr, fmt, args);
178     fprintf (stderr, "\n");
179 }
180 
181 int
add_db_arg(char * arg)182 add_db_arg(char *arg)
183 {
184     char **temp;
185     db5util_db_args_size++;
186     temp = realloc(db5util_db_args,
187                    sizeof(char *) * (db5util_db_args_size + 1));
188     if (temp == NULL)
189         return 0;
190     db5util_db_args = temp;
191     db5util_db_args[db5util_db_args_size-1] = arg;
192     db5util_db_args[db5util_db_args_size]   = NULL;
193     return 1;
194 }
195 
196 int
main(int argc,char * argv[])197 main(int argc, char *argv[])
198 {
199     struct _cmd_table *cmd = NULL;
200     char *koptarg, **cmd_argv;
201     char *db_name_tmp = NULL;
202     int cmd_argc;
203     krb5_error_code retval;
204 
205     setlocale(LC_ALL, "");
206     set_com_err_hook(extended_com_err_fn);
207 
208     /*
209      * Ensure that "progname" is set before calling com_err.
210      */
211     progname = (strrchr(argv[0], '/') ?
212                 strrchr(argv[0], '/') + 1 : argv[0]);
213 
214     retval = kadm5_init_krb5_context(&util_context);
215     if (retval) {
216         com_err (progname, retval, _("while initializing Kerberos code"));
217         exit(1);
218     }
219 
220     cmd_argv = (char **) malloc(sizeof(char *)*argc);
221     if (cmd_argv == NULL) {
222         com_err(progname, ENOMEM, _("while creating sub-command arguments"));
223         exit(1);
224     }
225     memset(cmd_argv, 0, sizeof(char *)*argc);
226     cmd_argc = 0;
227 
228     argv++; argc--;
229     while (*argv) {
230         if (strcmp(*argv, "-P") == 0 && ARG_VAL) {
231             mkey_password = koptarg;
232             manual_mkey = TRUE;
233         } else if (strcmp(*argv, "-d") == 0 && ARG_VAL) {
234             global_params.dbname = koptarg;
235             global_params.mask |= KADM5_CONFIG_DBNAME;
236 
237             if (asprintf(&db_name_tmp, "dbname=%s", global_params.dbname) < 0)
238             {
239                 com_err(progname, ENOMEM,
240                         _("while parsing command arguments"));
241                 exit(1);
242             }
243 
244             if (!add_db_arg(db_name_tmp)) {
245                 com_err(progname, ENOMEM,
246                         _("while parsing command arguments\n"));
247                 exit(1);
248             }
249 
250         } else if (strcmp(*argv, "-x") == 0 && ARG_VAL) {
251             if (!add_db_arg(koptarg)) {
252                 com_err(progname, ENOMEM,
253                         _("while parsing command arguments\n"));
254                 exit(1);
255             }
256 
257         } else if (strcmp(*argv, "-r") == 0 && ARG_VAL) {
258             global_params.realm = koptarg;
259             global_params.mask |= KADM5_CONFIG_REALM;
260             /* not sure this is really necessary */
261             if ((retval = krb5_set_default_realm(util_context,
262                                                  global_params.realm))) {
263                 com_err(progname, retval,
264                         _("while setting default realm name"));
265                 exit(1);
266             }
267         } else if (strcmp(*argv, "-k") == 0 && ARG_VAL) {
268             if (krb5_string_to_enctype(koptarg, &global_params.enctype)) {
269                 com_err(progname, EINVAL, _(": %s is an invalid enctype"),
270                         koptarg);
271                 exit(1);
272             } else
273                 global_params.mask |= KADM5_CONFIG_ENCTYPE;
274         } else if (strcmp(*argv, "-kv") == 0 && ARG_VAL) {
275             global_params.kvno = (krb5_kvno) atoi(koptarg);
276             if (global_params.kvno == IGNORE_VNO) {
277                 com_err(progname, EINVAL, _(": %s is an invalid mkeyVNO"),
278                         koptarg);
279                 exit(1);
280             } else
281                 global_params.mask |= KADM5_CONFIG_KVNO;
282         } else if (strcmp(*argv, "-M") == 0 && ARG_VAL) {
283             global_params.mkey_name = koptarg;
284             global_params.mask |= KADM5_CONFIG_MKEY_NAME;
285         } else if (strcmp(*argv, "-sf") == 0 && ARG_VAL) {
286             global_params.stash_file = koptarg;
287             global_params.mask |= KADM5_CONFIG_STASH_FILE;
288         } else if (strcmp(*argv, "-m") == 0) {
289             manual_mkey = TRUE;
290             global_params.mkey_from_kbd = 1;
291             global_params.mask |= KADM5_CONFIG_MKEY_FROM_KBD;
292         } else {
293             cmd_argv[cmd_argc++] = *argv;
294         }
295         argv++; argc--;
296     }
297 
298     if (cmd_argv[0] == NULL)
299         usage();
300     cmd = cmd_lookup(cmd_argv[0]);
301     if (cmd == NULL)
302         usage();
303 
304     if( !util_context->default_realm )
305     {
306         char *temp = NULL;
307         retval = krb5_get_default_realm(util_context, &temp);
308         if( retval )
309         {
310             com_err(progname, retval, _("while getting default realm"));
311             exit(1);
312         }
313         krb5_free_default_realm(util_context, temp);
314     }
315 
316     retval = kadm5_get_config_params(util_context, 1,
317                                      &global_params, &global_params);
318     if (retval) {
319         com_err(progname, retval,
320                 _("while retrieving configuration parameters"));
321         exit(1);
322     }
323 
324     /*
325      * Dump creates files which should not be world-readable.  It is
326      * easiest to do a single umask call here.
327      */
328     (void) umask(077);
329 
330     master_keyblock.enctype = global_params.enctype;
331     if ((master_keyblock.enctype != ENCTYPE_UNKNOWN) &&
332         (!krb5_c_valid_enctype(master_keyblock.enctype))) {
333         com_err(progname, KRB5_PROG_KEYTYPE_NOSUPP,
334                 "while setting up enctype %d", master_keyblock.enctype);
335     }
336 
337     if (cmd->opendb && open_db_and_mkey())
338         return exit_status;
339 
340     if (global_params.iprop_enabled == TRUE)
341         ulog_set_role(util_context, IPROP_PRIMARY);
342     else
343         ulog_set_role(util_context, IPROP_NULL);
344 
345     (*cmd->func)(cmd_argc, cmd_argv);
346 
347     if( db_name_tmp )
348         free( db_name_tmp );
349 
350     if( db5util_db_args )
351         free(db5util_db_args);
352 
353     quit();
354     kadm5_free_config_params(util_context, &global_params);
355     krb5_free_context(util_context);
356     free(cmd_argv);
357     return exit_status;
358 }
359 
360 /*
361  * open_db_and_mkey: Opens the KDC and policy database, and sets the
362  * global master_* variables.  Sets dbactive to TRUE if the databases
363  * are opened, and valid_master_key to 1 if the global master
364  * variables are set properly.  Returns 0 on success, and 1 on
365  * failure, but it is not considered a failure if the master key
366  * cannot be fetched (the master key stash file may not exist when the
367  * program is run).
368  */
369 static int
open_db_and_mkey(void)370 open_db_and_mkey(void)
371 {
372     krb5_error_code retval;
373     krb5_data scratch, pwd, seed;
374 
375     dbactive = FALSE;
376     valid_master_key = 0;
377 
378     if ((retval = krb5_db_open(util_context, db5util_db_args,
379                                KRB5_KDB_OPEN_RW | KRB5_KDB_SRV_TYPE_ADMIN))) {
380         com_err(progname, retval, _("while initializing database"));
381         exit_status++;
382         return(1);
383     }
384 
385     /* assemble & parse the master key name */
386 
387     if ((retval = krb5_db_setup_mkey_name(util_context,
388                                           global_params.mkey_name,
389                                           global_params.realm,
390                                           &mkey_fullname, &master_princ))) {
391         com_err(progname, retval, _("while setting up master key name"));
392         exit_status++;
393         return(1);
394     }
395     if ((retval = krb5_db_get_principal(util_context, master_princ, 0,
396                                         &master_entry))) {
397         com_err(progname, retval, _("while retrieving master entry"));
398         exit_status++;
399         (void) krb5_db_fini(util_context);
400         return(1);
401     }
402 
403     if (global_params.mask & KADM5_CONFIG_KVNO)
404         master_kvno = global_params.kvno; /* user specified */
405     else
406         master_kvno = IGNORE_VNO;
407 
408     /* the databases are now open, and the master principal exists */
409     dbactive = TRUE;
410 
411     if (mkey_password) {
412         pwd.data = mkey_password;
413         pwd.length = strlen(mkey_password);
414         retval = krb5_principal2salt(util_context, master_princ, &scratch);
415         if (retval) {
416             com_err(progname, retval, _("while calculated master key salt"));
417             exit_status++;
418             return(1);
419         }
420 
421         /* If no encryption type is set, use the default */
422         if (master_keyblock.enctype == ENCTYPE_UNKNOWN)
423             master_keyblock.enctype = DEFAULT_KDC_ENCTYPE;
424         if (!krb5_c_valid_enctype(master_keyblock.enctype))
425             com_err(progname, KRB5_PROG_KEYTYPE_NOSUPP,
426                     "while setting up enctype %d",
427                     master_keyblock.enctype);
428 
429         retval = krb5_c_string_to_key(util_context, master_keyblock.enctype,
430                                       &pwd, &scratch, &master_keyblock);
431         if (retval) {
432             com_err(progname, retval,
433                     _("while transforming master key from password"));
434             exit_status++;
435             return(1);
436         }
437         free(scratch.data);
438         mkey_password = 0;
439 
440     } else {
441         if ((retval = krb5_db_fetch_mkey(util_context, master_princ,
442                                          master_keyblock.enctype,
443                                          manual_mkey, FALSE,
444                                          global_params.stash_file,
445                                          &master_kvno,
446                                          0, &master_keyblock))) {
447             com_err(progname, retval, _("while reading master key"));
448             com_err(progname, 0, _("Warning: proceeding without master key"));
449             exit_status++;
450             return(0);
451         }
452     }
453 
454     if ((retval = krb5_db_fetch_mkey_list(util_context, master_princ,
455                                           &master_keyblock))) {
456         com_err(progname, retval, "while getting master key list");
457         com_err(progname, 0, "Warning: proceeding without master key list");
458         exit_status++;
459         return(0);
460     }
461 
462     seed.length = master_keyblock.length;
463     seed.data = (char *) master_keyblock.contents;
464 
465     if ((retval = krb5_c_random_seed(util_context, &seed))) {
466         com_err(progname, retval, _("while seeding random number generator"));
467         exit_status++;
468         memset(master_keyblock.contents, 0, master_keyblock.length);
469         krb5_free_keyblock_contents(util_context, &master_keyblock);
470         return(1);
471     }
472 
473     if (global_params.iprop_enabled) {
474         if (ulog_map(util_context, global_params.iprop_logfile,
475                      global_params.iprop_ulogsize)) {
476             fprintf(stderr, _("%s: Could not map log\n"), progname);
477             exit_status++;
478             return(1);
479         }
480     }
481 
482     valid_master_key = 1;
483     dbactive = TRUE;
484     return 0;
485 }
486 
487 #ifdef HAVE_GETCWD
488 #undef getwd
489 #endif
490 
491 int
quit(void)492 quit(void)
493 {
494     krb5_error_code retval;
495     static krb5_boolean finished = 0;
496 
497     if (finished)
498         return 0;
499     ulog_fini(util_context);
500     retval = krb5_db_fini(util_context);
501     zapfree(master_keyblock.contents, master_keyblock.length);
502     krb5_free_principal(util_context, master_princ);
503     finished = TRUE;
504     if (retval && retval != KRB5_KDB_DBNOTINITED) {
505         com_err(progname, retval, _("while closing database"));
506         exit_status++;
507         return 1;
508     }
509     return 0;
510 }
511 
512 static void
add_random_key(int argc,char ** argv)513 add_random_key(int argc, char **argv)
514 {
515     krb5_error_code ret;
516     krb5_principal princ;
517     krb5_db_entry *dbent;
518     krb5_timestamp now;
519 
520     krb5_key_salt_tuple *keysalts = NULL;
521     krb5_int32 num_keysalts = 0;
522 
523     int free_keysalts;
524     char *me = progname;
525     char *ks_str = "";
526     char *pr_str;
527     krb5_keyblock *tmp_mkey;
528 
529     if (argc < 2)
530         usage();
531     for (argv++, argc--; *argv; argv++, argc--) {
532         if (!strcmp(*argv, "-e")) {
533             argv++; argc--;
534             ks_str = *argv;
535             continue;
536         } else
537             break;
538     }
539     if (argc < 1)
540         usage();
541     pr_str = *argv;
542     ret = krb5_parse_name(util_context, pr_str, &princ);
543     if (ret) {
544         com_err(me, ret, _("while parsing principal name %s"), pr_str);
545         exit_status++;
546         return;
547     }
548     ret = krb5_db_get_principal(util_context, princ, 0, &dbent);
549     if (ret) {
550         com_err(me, ret, _("while fetching principal %s"), pr_str);
551         exit_status++;
552         return;
553     }
554     ret = krb5_string_to_keysalts(ks_str,
555                                   NULL, NULL, 0,
556                                   &keysalts,
557                                   &num_keysalts);
558     if (ret) {
559         com_err(me, ret, _("while parsing keysalts %s"), ks_str);
560         exit_status++;
561         return;
562     }
563     if (!num_keysalts || keysalts == NULL) {
564         num_keysalts = global_params.num_keysalts;
565         keysalts = global_params.keysalts;
566         free_keysalts = 0;
567     } else
568         free_keysalts = 1;
569 
570     /* Find the mkey used to protect the existing keys */
571     ret = krb5_dbe_find_mkey(util_context, dbent, &tmp_mkey);
572     if (ret) {
573         com_err(me, ret, _("while finding mkey"));
574         krb5_db_free_principal(util_context, dbent);
575         exit_status++;
576         return;
577     }
578 
579     ret = krb5_dbe_ark(util_context, tmp_mkey, keysalts, num_keysalts, dbent);
580     if (free_keysalts)
581         free(keysalts);
582     if (ret) {
583         com_err(me, ret, "while randomizing principal %s", pr_str);
584         krb5_db_free_principal(util_context, dbent);
585         exit_status++;
586         return;
587     }
588     dbent->attributes &= ~KRB5_KDB_REQUIRES_PWCHANGE;
589     ret = krb5_timeofday(util_context, &now);
590     if (ret) {
591         com_err(me, ret, _("while getting time"));
592         krb5_db_free_principal(util_context, dbent);
593         exit_status++;
594         return;
595     }
596     ret = krb5_dbe_update_last_pwd_change(util_context, dbent, now);
597     if (ret) {
598         com_err(me, ret, _("while setting changetime"));
599         krb5_db_free_principal(util_context, dbent);
600         exit_status++;
601         return;
602     }
603 
604     dbent->mask |= KADM5_ATTRIBUTES | KADM5_KEY_DATA | KADM5_TL_DATA;
605 
606     ret = krb5_db_put_principal(util_context, dbent);
607     krb5_db_free_principal(util_context, dbent);
608     if (ret) {
609         com_err(me, ret, _("while saving principal %s"), pr_str);
610         exit_status++;
611         return;
612     }
613     printf(_("%s changed\n"), pr_str);
614 }
615