Imported Upstream version 1.15.1
[platform/upstream/krb5.git] / src / kadmin / dbutil / kdb5_util.c
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
77 void usage()
78 {
79     fprintf(stderr,
80             _("Usage: kdb5_util [-x db_args]* [-r realm] [-d dbname] "
81               "[-k mkeytype] [-M mkeyname]\n"
82               "\t        [-kv mkeyVNO] [-sf stashfilename] [-m] cmd "
83               "[cmd_options]\n"
84               "\tcreate  [-s]\n"
85               "\tdestroy [-f]\n"
86               "\tstash   [-f keyfile]\n"
87               "\tdump    [-old|-ov|-b6|-b7|-r13|-r18] [-verbose]\n"
88               "\t        [-mkey_convert] [-new_mkey_file mkey_file]\n"
89               "\t        [-rev] [-recurse] [filename [princs...]]\n"
90               "\tload    [-old|-ov|-b6|-b7|-r13|-r18] [-verbose] [-update] "
91               "filename\n"
92               "\tark     [-e etype_list] principal\n"
93               "\tadd_mkey [-e etype] [-s]\n"
94               "\tuse_mkey kvno [time]\n"
95               "\tlist_mkeys\n"));
96     /* avoid a string length compiler warning */
97     fprintf(stderr,
98             _("\tupdate_princ_encryption [-f] [-n] [-v] [princ-pattern]\n"
99               "\tpurge_mkeys [-f] [-n] [-v]\n"
100               "\ttabdump [-H] [-c] [-e] [-n] [-o outfile] dumptype\n"
101               "\nwhere,\n\t[-x db_args]* - any number of database specific "
102               "arguments.\n"
103               "\t\t\tLook at each database documentation for supported "
104               "arguments\n"));
105     exit(1);
106 }
107
108 krb5_keyblock master_keyblock;
109 krb5_kvno   master_kvno; /* fetched */
110 extern krb5_principal master_princ;
111 char *mkey_fullname;
112 krb5_db_entry *master_entry = NULL;
113 int     valid_master_key = 0;
114
115 char *progname;
116 krb5_boolean manual_mkey = FALSE;
117 krb5_boolean dbactive = FALSE;
118
119 static int open_db_and_mkey(void);
120
121 static void add_random_key(int, char **);
122
123 typedef void (*cmd_func)(int, char **);
124
125 struct _cmd_table {
126     char *name;
127     cmd_func func;
128     int opendb;
129 } cmd_table[] = {
130     {"create", kdb5_create, 0},
131     {"destroy", kdb5_destroy, 1}, /* 1 opens the kdb */
132     {"stash", kdb5_stash, 1},
133     {"dump", dump_db, 1},
134     {"load", load_db, 0},
135     {"ark", add_random_key, 1},
136     {"add_mkey", kdb5_add_mkey, 1},
137     {"use_mkey", kdb5_use_mkey, 1},
138     {"list_mkeys", kdb5_list_mkeys, 1},
139     {"update_princ_encryption", kdb5_update_princ_encryption, 1},
140     {"purge_mkeys", kdb5_purge_mkeys, 1},
141     {"tabdump", tabdump, 1},
142     {NULL, NULL, 0},
143 };
144
145 static struct _cmd_table *cmd_lookup(name)
146     char *name;
147 {
148     struct _cmd_table *cmd = cmd_table;
149     while (cmd->name) {
150         if (strcmp(cmd->name, name) == 0)
151             return cmd;
152         else
153             cmd++;
154     }
155
156     return NULL;
157 }
158
159 #define ARG_VAL (--argc > 0 ? (koptarg = *(++argv)) : (char *)(usage(), NULL))
160
161 char **db5util_db_args = NULL;
162 int    db5util_db_args_size = 0;
163
164 static void extended_com_err_fn (const char *myprog, errcode_t code,
165                                  const char *fmt, va_list args)
166 {
167     const char *emsg;
168     if (code) {
169         emsg = krb5_get_error_message (util_context, code);
170         fprintf (stderr, "%s: %s ", myprog, emsg);
171         krb5_free_error_message (util_context, emsg);
172     } else {
173         fprintf (stderr, "%s: ", myprog);
174     }
175     vfprintf (stderr, fmt, args);
176     fprintf (stderr, "\n");
177 }
178
179 int add_db_arg(char *arg)
180 {
181     char **temp;
182     db5util_db_args_size++;
183     temp = realloc(db5util_db_args,
184                    sizeof(char *) * (db5util_db_args_size + 1));
185     if (temp == NULL)
186         return 0;
187     db5util_db_args = temp;
188     db5util_db_args[db5util_db_args_size-1] = arg;
189     db5util_db_args[db5util_db_args_size]   = NULL;
190     return 1;
191 }
192
193 int main(argc, argv)
194     int argc;
195     char *argv[];
196 {
197     struct _cmd_table *cmd = NULL;
198     char *koptarg, **cmd_argv;
199     char *db_name_tmp = NULL;
200     int cmd_argc;
201     krb5_error_code retval;
202
203     setlocale(LC_ALL, "");
204     set_com_err_hook(extended_com_err_fn);
205
206     /*
207      * Ensure that "progname" is set before calling com_err.
208      */
209     progname = (strrchr(argv[0], '/') ?
210                 strrchr(argv[0], '/') + 1 : argv[0]);
211
212     retval = kadm5_init_krb5_context(&util_context);
213     if (retval) {
214         com_err (progname, retval, _("while initializing Kerberos code"));
215         exit(1);
216     }
217
218     cmd_argv = (char **) malloc(sizeof(char *)*argc);
219     if (cmd_argv == NULL) {
220         com_err(progname, ENOMEM, _("while creating sub-command arguments"));
221         exit(1);
222     }
223     memset(cmd_argv, 0, sizeof(char *)*argc);
224     cmd_argc = 1;
225
226     argv++; argc--;
227     while (*argv) {
228         if (strcmp(*argv, "-P") == 0 && ARG_VAL) {
229             mkey_password = koptarg;
230             manual_mkey = TRUE;
231         } else if (strcmp(*argv, "-d") == 0 && ARG_VAL) {
232             global_params.dbname = koptarg;
233             global_params.mask |= KADM5_CONFIG_DBNAME;
234
235             if (asprintf(&db_name_tmp, "dbname=%s", global_params.dbname) < 0)
236             {
237                 com_err(progname, ENOMEM,
238                         _("while parsing command arguments"));
239                 exit(1);
240             }
241
242             if (!add_db_arg(db_name_tmp)) {
243                 com_err(progname, ENOMEM,
244                         _("while parsing command arguments\n"));
245                 exit(1);
246             }
247
248         } else if (strcmp(*argv, "-x") == 0 && ARG_VAL) {
249             if (!add_db_arg(koptarg)) {
250                 com_err(progname, ENOMEM,
251                         _("while parsing command arguments\n"));
252                 exit(1);
253             }
254
255         } else if (strcmp(*argv, "-r") == 0 && ARG_VAL) {
256             global_params.realm = koptarg;
257             global_params.mask |= KADM5_CONFIG_REALM;
258             /* not sure this is really necessary */
259             if ((retval = krb5_set_default_realm(util_context,
260                                                  global_params.realm))) {
261                 com_err(progname, retval,
262                         _("while setting default realm name"));
263                 exit(1);
264             }
265         } else if (strcmp(*argv, "-k") == 0 && ARG_VAL) {
266             if (krb5_string_to_enctype(koptarg, &global_params.enctype)) {
267                 com_err(progname, EINVAL, _(": %s is an invalid enctype"),
268                         koptarg);
269                 exit(1);
270             } else
271                 global_params.mask |= KADM5_CONFIG_ENCTYPE;
272         } else if (strcmp(*argv, "-kv") == 0 && ARG_VAL) {
273             global_params.kvno = (krb5_kvno) atoi(koptarg);
274             if (global_params.kvno == IGNORE_VNO) {
275                 com_err(progname, EINVAL, _(": %s is an invalid mkeyVNO"),
276                         koptarg);
277                 exit(1);
278             } else
279                 global_params.mask |= KADM5_CONFIG_KVNO;
280         } else if (strcmp(*argv, "-M") == 0 && ARG_VAL) {
281             global_params.mkey_name = koptarg;
282             global_params.mask |= KADM5_CONFIG_MKEY_NAME;
283         } else if (strcmp(*argv, "-sf") == 0 && ARG_VAL) {
284             global_params.stash_file = koptarg;
285             global_params.mask |= KADM5_CONFIG_STASH_FILE;
286         } else if (strcmp(*argv, "-m") == 0) {
287             manual_mkey = TRUE;
288             global_params.mkey_from_kbd = 1;
289             global_params.mask |= KADM5_CONFIG_MKEY_FROM_KBD;
290         } else if (cmd_lookup(*argv) != NULL) {
291             if (cmd_argv[0] == NULL)
292                 cmd_argv[0] = *argv;
293             else
294                 usage();
295         } else {
296             cmd_argv[cmd_argc++] = *argv;
297         }
298         argv++; argc--;
299     }
300
301     if (cmd_argv[0] == 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 retreiving 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     cmd = cmd_lookup(cmd_argv[0]);
338     if (cmd->opendb && open_db_and_mkey())
339         return exit_status;
340
341     if (global_params.iprop_enabled == TRUE)
342         ulog_set_role(util_context, IPROP_MASTER);
343     else
344         ulog_set_role(util_context, IPROP_NULL);
345
346     (*cmd->func)(cmd_argc, cmd_argv);
347
348     if( db_name_tmp )
349         free( db_name_tmp );
350
351     if( db5util_db_args )
352         free(db5util_db_args);
353
354     quit();
355     kadm5_free_config_params(util_context, &global_params);
356     krb5_free_context(util_context);
357     free(cmd_argv);
358     return exit_status;
359 }
360
361 #if 0
362 /*
363  * This function is no longer used in kdb5_util (and it would no
364  * longer work, anyway).
365  */
366 void set_dbname(argc, argv)
367     int argc;
368     char *argv[];
369 {
370     krb5_error_code retval;
371
372     if (argc < 3) {
373         com_err(argv[0], 0, _("Too few arguments"));
374         com_err(progname, 0, _("Usage: %s dbpathname realmname"), argv[0]);
375         exit_status++;
376         return;
377     }
378     if (dbactive) {
379         if ((retval = krb5_db_fini(util_context)) && retval!= KRB5_KDB_DBNOTINITED) {
380             com_err(progname, retval, _("while closing previous database"));
381             exit_status++;
382             return;
383         }
384         if (valid_master_key) {
385             krb5_free_keyblock_contents(util_context, &master_keyblock);
386             master_keyblock.contents = NULL;
387             valid_master_key = 0;
388         }
389         krb5_free_principal(util_context, master_princ);
390         free(mkey_fullname);
391         dbactive = FALSE;
392     }
393
394     (void) set_dbname_help(progname, argv[1]);
395     return;
396 }
397 #endif
398
399 /*
400  * open_db_and_mkey: Opens the KDC and policy database, and sets the
401  * global master_* variables.  Sets dbactive to TRUE if the databases
402  * are opened, and valid_master_key to 1 if the global master
403  * variables are set properly.  Returns 0 on success, and 1 on
404  * failure, but it is not considered a failure if the master key
405  * cannot be fetched (the master key stash file may not exist when the
406  * program is run).
407  */
408 static int open_db_and_mkey()
409 {
410     krb5_error_code retval;
411     krb5_data scratch, pwd, seed;
412
413     dbactive = FALSE;
414     valid_master_key = 0;
415
416     if ((retval = krb5_db_open(util_context, db5util_db_args,
417                                KRB5_KDB_OPEN_RW | KRB5_KDB_SRV_TYPE_ADMIN))) {
418         com_err(progname, retval, _("while initializing database"));
419         exit_status++;
420         return(1);
421     }
422
423     /* assemble & parse the master key name */
424
425     if ((retval = krb5_db_setup_mkey_name(util_context,
426                                           global_params.mkey_name,
427                                           global_params.realm,
428                                           &mkey_fullname, &master_princ))) {
429         com_err(progname, retval, _("while setting up master key name"));
430         exit_status++;
431         return(1);
432     }
433     if ((retval = krb5_db_get_principal(util_context, master_princ, 0,
434                                         &master_entry))) {
435         com_err(progname, retval, _("while retrieving master entry"));
436         exit_status++;
437         (void) krb5_db_fini(util_context);
438         return(1);
439     }
440
441     if (global_params.mask & KADM5_CONFIG_KVNO)
442         master_kvno = global_params.kvno; /* user specified */
443     else
444         master_kvno = IGNORE_VNO;
445
446     /* the databases are now open, and the master principal exists */
447     dbactive = TRUE;
448
449     if (mkey_password) {
450         pwd.data = mkey_password;
451         pwd.length = strlen(mkey_password);
452         retval = krb5_principal2salt(util_context, master_princ, &scratch);
453         if (retval) {
454             com_err(progname, retval, _("while calculated master key salt"));
455             exit_status++;
456             return(1);
457         }
458
459         /* If no encryption type is set, use the default */
460         if (master_keyblock.enctype == ENCTYPE_UNKNOWN)
461             master_keyblock.enctype = DEFAULT_KDC_ENCTYPE;
462         if (!krb5_c_valid_enctype(master_keyblock.enctype))
463             com_err(progname, KRB5_PROG_KEYTYPE_NOSUPP,
464                     "while setting up enctype %d",
465                     master_keyblock.enctype);
466
467         retval = krb5_c_string_to_key(util_context, master_keyblock.enctype,
468                                       &pwd, &scratch, &master_keyblock);
469         if (retval) {
470             com_err(progname, retval,
471                     _("while transforming master key from password"));
472             exit_status++;
473             return(1);
474         }
475         free(scratch.data);
476         mkey_password = 0;
477
478     } else {
479         if ((retval = krb5_db_fetch_mkey(util_context, master_princ,
480                                          master_keyblock.enctype,
481                                          manual_mkey, FALSE,
482                                          global_params.stash_file,
483                                          &master_kvno,
484                                          0, &master_keyblock))) {
485             com_err(progname, retval, _("while reading master key"));
486             com_err(progname, 0, _("Warning: proceeding without master key"));
487             exit_status++;
488             return(0);
489         }
490     }
491
492     if ((retval = krb5_db_fetch_mkey_list(util_context, master_princ,
493                                           &master_keyblock))) {
494         com_err(progname, retval, "while getting master key list");
495         com_err(progname, 0, "Warning: proceeding without master key list");
496         exit_status++;
497         return(0);
498     }
499
500     seed.length = master_keyblock.length;
501     seed.data = (char *) master_keyblock.contents;
502
503     if ((retval = krb5_c_random_seed(util_context, &seed))) {
504         com_err(progname, retval, _("while seeding random number generator"));
505         exit_status++;
506         memset(master_keyblock.contents, 0, master_keyblock.length);
507         krb5_free_keyblock_contents(util_context, &master_keyblock);
508         return(1);
509     }
510
511     if (global_params.iprop_enabled) {
512         if (ulog_map(util_context, global_params.iprop_logfile,
513                      global_params.iprop_ulogsize)) {
514             fprintf(stderr, _("%s: Could not map log\n"), progname);
515             exit_status++;
516             return(1);
517         }
518     }
519
520     valid_master_key = 1;
521     dbactive = TRUE;
522     return 0;
523 }
524
525 #ifdef HAVE_GETCWD
526 #undef getwd
527 #endif
528
529 int
530 quit()
531 {
532     krb5_error_code retval;
533     static krb5_boolean finished = 0;
534
535     if (finished)
536         return 0;
537     ulog_fini(util_context);
538     retval = krb5_db_fini(util_context);
539     zapfree(master_keyblock.contents, master_keyblock.length);
540     krb5_free_principal(util_context, master_princ);
541     finished = TRUE;
542     if (retval && retval != KRB5_KDB_DBNOTINITED) {
543         com_err(progname, retval, _("while closing database"));
544         exit_status++;
545         return 1;
546     }
547     return 0;
548 }
549
550 static void
551 add_random_key(argc, argv)
552     int argc;
553     char **argv;
554 {
555     krb5_error_code ret;
556     krb5_principal princ;
557     krb5_db_entry *dbent;
558     krb5_timestamp now;
559
560     krb5_key_salt_tuple *keysalts = NULL;
561     krb5_int32 num_keysalts = 0;
562
563     int free_keysalts;
564     char *me = progname;
565     char *ks_str = NULL;
566     char *pr_str;
567     krb5_keyblock *tmp_mkey;
568
569     if (argc < 2)
570         usage();
571     for (argv++, argc--; *argv; argv++, argc--) {
572         if (!strcmp(*argv, "-e")) {
573             argv++; argc--;
574             ks_str = *argv;
575             continue;
576         } else
577             break;
578     }
579     if (argc < 1)
580         usage();
581     pr_str = *argv;
582     ret = krb5_parse_name(util_context, pr_str, &princ);
583     if (ret) {
584         com_err(me, ret, _("while parsing principal name %s"), pr_str);
585         exit_status++;
586         return;
587     }
588     ret = krb5_db_get_principal(util_context, princ, 0, &dbent);
589     if (ret) {
590         com_err(me, ret, _("while fetching principal %s"), pr_str);
591         exit_status++;
592         return;
593     }
594     ret = krb5_string_to_keysalts(ks_str,
595                                   NULL, NULL, 0,
596                                   &keysalts,
597                                   &num_keysalts);
598     if (ret) {
599         com_err(me, ret, _("while parsing keysalts %s"), ks_str);
600         exit_status++;
601         return;
602     }
603     if (!num_keysalts || keysalts == NULL) {
604         num_keysalts = global_params.num_keysalts;
605         keysalts = global_params.keysalts;
606         free_keysalts = 0;
607     } else
608         free_keysalts = 1;
609
610     /* Find the mkey used to protect the existing keys */
611     ret = krb5_dbe_find_mkey(util_context, dbent, &tmp_mkey);
612     if (ret) {
613         com_err(me, ret, _("while finding mkey"));
614         krb5_db_free_principal(util_context, dbent);
615         exit_status++;
616         return;
617     }
618
619     ret = krb5_dbe_ark(util_context, tmp_mkey, keysalts, num_keysalts, dbent);
620     if (free_keysalts)
621         free(keysalts);
622     if (ret) {
623         com_err(me, ret, "while randomizing principal %s", pr_str);
624         krb5_db_free_principal(util_context, dbent);
625         exit_status++;
626         return;
627     }
628     dbent->attributes &= ~KRB5_KDB_REQUIRES_PWCHANGE;
629     ret = krb5_timeofday(util_context, &now);
630     if (ret) {
631         com_err(me, ret, _("while getting time"));
632         krb5_db_free_principal(util_context, dbent);
633         exit_status++;
634         return;
635     }
636     ret = krb5_dbe_update_last_pwd_change(util_context, dbent, now);
637     if (ret) {
638         com_err(me, ret, _("while setting changetime"));
639         krb5_db_free_principal(util_context, dbent);
640         exit_status++;
641         return;
642     }
643     ret = krb5_db_put_principal(util_context, dbent);
644     krb5_db_free_principal(util_context, dbent);
645     if (ret) {
646         com_err(me, ret, _("while saving principal %s"), pr_str);
647         exit_status++;
648         return;
649     }
650     printf(_("%s changed\n"), pr_str);
651 }