xref: /freebsd/crypto/krb5/src/tests/create/kdb5_mkdums.c (revision f1c4c3daccbaf3820f0e2224de53df12fc952fcc)
1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /* tests/create/kdb5_mkdums.c */
3 /*
4  * Copyright 1990,1991 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 /*
28  *
29  * Edit a KDC database.
30  */
31 
32 #include "k5-int.h"
33 #include "kdb.h"
34 #include "com_err.h"
35 #include <ss/ss.h>
36 #include <stdio.h>
37 
38 
39 #define REALM_SEP       '@'
40 #define REALM_SEP_STR   "@"
41 
42 struct mblock {
43     krb5_deltat max_life;
44     krb5_deltat max_rlife;
45     krb5_timestamp expiration;
46     krb5_flags flags;
47     krb5_kvno mkvno;
48 } mblock = {                            /* XXX */
49     KRB5_KDB_MAX_LIFE,
50     KRB5_KDB_MAX_RLIFE,
51     KRB5_KDB_EXPIRATION,
52     KRB5_KDB_DEF_FLAGS,
53     1
54 };
55 
56 int set_dbname_help (char *, char *);
57 
58 static void
usage(char * who,int status)59 usage(char *who, int status)
60 {
61     fprintf(stderr,
62             "usage: %s -p prefix -n num_to_create [-d dbpathname] [-r realmname]\n",
63             who);
64     fprintf(stderr, "\t [-D depth] [-k enctype] [-M mkeyname]\n");
65 
66     exit(status);
67 }
68 
69 int master_princ_set = 0;
70 krb5_keyblock master_keyblock;
71 krb5_principal master_princ;
72 krb5_pointer master_random;
73 krb5_context test_context;
74 
75 static char *progname;
76 static char *cur_realm = 0;
77 static char *mkey_name = 0;
78 static char *mkey_password = 0;
79 static krb5_boolean manual_mkey = FALSE;
80 
81 void add_princ (krb5_context, char *);
82 
83 int
main(int argc,char * argv[])84 main(int argc, char *argv[])
85 {
86     extern char *optarg;
87     int optchar, i, n;
88     char tmp[4096], tmp2[BUFSIZ], *str_newprinc;
89 
90     krb5_error_code retval;
91     char *dbname = 0;
92     int enctypedone = 0;
93     int num_to_create;
94     char principal_string[BUFSIZ];
95     char *suffix = 0;
96     size_t suffix_size = 0;
97     int depth;
98 
99     krb5_init_context(&test_context);
100 
101     if (strrchr(argv[0], '/'))
102         argv[0] = strrchr(argv[0], '/')+1;
103 
104     progname = argv[0];
105 
106     memset(principal_string, 0, sizeof(principal_string));
107     num_to_create = 0;
108     depth = 1;
109 
110     while ((optchar = getopt(argc, argv, "D:P:p:n:d:r:k:M:e:m")) != -1) {
111         switch(optchar) {
112         case 'D':
113             depth = atoi(optarg);       /* how deep to go */
114             break;
115         case 'P':               /* Only used for testing!!! */
116             mkey_password = optarg;
117             break;
118         case 'p':                       /* prefix name to create */
119             strncpy(principal_string, optarg, sizeof(principal_string) - 1);
120             principal_string[sizeof(principal_string) - 1] = '\0';
121             suffix = principal_string + strlen(principal_string);
122             suffix_size = sizeof(principal_string) -
123                 (suffix - principal_string);
124             break;
125         case 'n':                        /* how many to create */
126             num_to_create = atoi(optarg);
127             break;
128         case 'd':                       /* set db name */
129             dbname = optarg;
130             break;
131         case 'r':
132             cur_realm = optarg;
133             break;
134         case 'k':
135             master_keyblock.enctype = atoi(optarg);
136             enctypedone++;
137             break;
138         case 'M':                       /* master key name in DB */
139             mkey_name = optarg;
140             break;
141         case 'm':
142             manual_mkey = TRUE;
143             break;
144         case '?':
145         default:
146             usage(progname, 1);
147             /*NOTREACHED*/
148         }
149     }
150 
151     if (!(num_to_create && suffix)) usage(progname, 1);
152 
153     if (!enctypedone)
154         master_keyblock.enctype = DEFAULT_KDC_ENCTYPE;
155 
156     if (!krb5_c_valid_enctype(master_keyblock.enctype)) {
157         com_err(progname, KRB5_PROG_ETYPE_NOSUPP,
158                 "while setting up enctype %d", master_keyblock.enctype);
159         exit(1);
160     }
161 
162     if (!dbname)
163         dbname = DEFAULT_KDB_FILE;      /* XXX? */
164 
165     if (!cur_realm) {
166         if ((retval = krb5_get_default_realm(test_context, &cur_realm))) {
167             com_err(progname, retval, "while retrieving default realm name");
168             exit(1);
169         }
170     }
171     if ((retval = set_dbname_help(progname, dbname)))
172         exit(retval);
173 
174     for (n = 1; n <= num_to_create; n++) {
175         /* build the new principal name */
176         /* we can't pick random names because we need to generate all the names
177            again given a prefix and count to test the db lib and kdb */
178         (void) snprintf(suffix, suffix_size, "%d", n);
179         (void) snprintf(tmp, sizeof(tmp), "%s-DEPTH-1", principal_string);
180         tmp[sizeof(tmp) - 1] = '\0';
181         str_newprinc = tmp;
182         add_princ(test_context, str_newprinc);
183 
184         for (i = 2; i <= depth; i++) {
185             (void) snprintf(tmp2, sizeof(tmp2), "/%s-DEPTH-%d",
186                             principal_string, i);
187             tmp2[sizeof(tmp2) - 1] = '\0';
188             strncat(tmp, tmp2, sizeof(tmp) - 1 - strlen(tmp));
189             str_newprinc = tmp;
190             add_princ(test_context, str_newprinc);
191         }
192     }
193 
194     retval = krb5_db_fini(test_context);
195     memset(master_keyblock.contents, 0,
196            (size_t) master_keyblock.length);
197     if (retval && retval != KRB5_KDB_DBNOTINITED) {
198         com_err(progname, retval, "while closing database");
199         exit(1);
200     }
201     if (master_princ_set)
202         krb5_free_principal(test_context, master_princ);
203     krb5_free_context(test_context);
204     exit(0);
205 }
206 
207 void
add_princ(krb5_context context,char * str_newprinc)208 add_princ(krb5_context context, char *str_newprinc)
209 {
210     krb5_error_code       retval;
211     krb5_principal        newprinc;
212     krb5_db_entry         *newentry;
213     char                  princ_name[4096];
214 
215     newentry = calloc(1, sizeof(*newentry));
216     if (newentry == NULL) {
217         com_err(progname, ENOMEM, "while allocating DB entry");
218         return;
219     }
220     snprintf(princ_name, sizeof(princ_name), "%s@%s", str_newprinc, cur_realm);
221     if ((retval = krb5_parse_name(context, princ_name, &newprinc))) {
222         com_err(progname, retval, "while parsing '%s'", princ_name);
223         return;
224     }
225 
226     /* Add basic data */
227     newentry->len = KRB5_KDB_V1_BASE_LENGTH;
228     newentry->attributes = mblock.flags;
229     newentry->max_life = mblock.max_life;
230     newentry->max_renewable_life = mblock.max_rlife;
231     newentry->expiration = mblock.expiration;
232     newentry->pw_expiration = mblock.expiration;
233 
234     /* Add princ to db entry */
235     if ((retval = krb5_copy_principal(context, newprinc, &newentry->princ))) {
236         com_err(progname, retval, "while encoding princ to db entry for '%s'",
237                 princ_name);
238         krb5_free_principal(context, newprinc);
239         goto error;
240     }
241 
242     {
243         /* Add mod princ to db entry */
244         krb5_timestamp now;
245 
246         retval = krb5_timeofday(context, &now);
247         if (retval) {
248             com_err(progname, retval, "while fetching date");
249             krb5_free_principal(context, newprinc);
250             goto error;
251         }
252         retval = krb5_dbe_update_mod_princ_data(context, newentry, now,
253                                                 master_princ);
254         if (retval) {
255             com_err(progname, retval, "while encoding mod_princ data");
256             krb5_free_principal(context, newprinc);
257             goto error;
258         }
259     }
260 
261     {   /* Add key and salt data to db entry */
262         krb5_data pwd, salt;
263         krb5_keyblock key;
264 
265         if ((retval = krb5_principal2salt(context, newprinc, &salt))) {
266             com_err(progname, retval, "while converting princ to salt for '%s'",
267                     princ_name);
268             krb5_free_principal(context, newprinc);
269             goto error;
270         }
271 
272         krb5_free_principal(context, newprinc);
273 
274         pwd.length = strlen(princ_name);
275         pwd.data = princ_name;  /* must be able to regenerate */
276         if ((retval = krb5_c_string_to_key(context, master_keyblock.enctype,
277                                            &pwd, &salt, &key))) {
278             com_err(progname,retval,"while converting password to key for '%s'",
279                     princ_name);
280             krb5_free_data_contents(context, &salt);
281             goto error;
282         }
283         krb5_free_data_contents(context, &salt);
284 
285         if ((retval = krb5_dbe_create_key_data(context, newentry))) {
286             com_err(progname, retval, "while creating key_data for '%s'",
287                     princ_name);
288             free(key.contents);
289             goto error;
290         }
291 
292         if ((retval = krb5_dbe_encrypt_key_data(context, &master_keyblock,
293                                                 &key, NULL, 1,
294                                                 newentry->key_data))) {
295             com_err(progname, retval, "while encrypting key for '%s'",
296                     princ_name);
297             free(key.contents);
298             goto error;
299         }
300         free(key.contents);
301     }
302 
303     if ((retval = krb5_db_put_principal(context, newentry))) {
304         com_err(progname, retval, "while storing principal date");
305         goto error;
306     }
307 
308 error: /* Do cleanup of newentry regardless of error */
309     krb5_db_free_principal(context, newentry);
310     return;
311 }
312 
313 int
set_dbname_help(char * pname,char * dbname)314 set_dbname_help(char *pname, char *dbname)
315 {
316     krb5_error_code retval;
317     krb5_data pwd, scratch;
318     char *args[2];
319     krb5_db_entry *master_entry;
320 
321     /* assemble & parse the master key name */
322 
323     if ((retval = krb5_db_setup_mkey_name(test_context, mkey_name, cur_realm,
324                                           0, &master_princ))) {
325         com_err(pname, retval, "while setting up master key name");
326         return(1);
327     }
328     master_princ_set = 1;
329     if (mkey_password) {
330         pwd.data = mkey_password;
331         pwd.length = strlen(mkey_password);
332         retval = krb5_principal2salt(test_context, master_princ, &scratch);
333         if (retval) {
334             com_err(pname, retval, "while calculated master key salt");
335             return(1);
336         }
337         if ((retval = krb5_c_string_to_key(test_context,
338                                            master_keyblock.enctype,
339                                            &pwd, &scratch,
340                                            &master_keyblock))) {
341             com_err(pname, retval,
342                     "while transforming master key from password");
343             return(1);
344         }
345         free(scratch.data);
346     } else {
347         if ((retval = krb5_db_fetch_mkey(test_context, master_princ,
348                                          master_keyblock.enctype, manual_mkey,
349                                          FALSE, 0, NULL, NULL,
350                                          &master_keyblock))) {
351             com_err(pname, retval, "while reading master key");
352             return(1);
353         }
354     }
355 
356     /* Ick!  Current DAL interface requires that the default_realm
357        field be set in the krb5_context.  */
358     if ((retval = krb5_set_default_realm(test_context, cur_realm))) {
359         com_err(pname, retval, "setting default realm");
360         return 1;
361     }
362     /* Pathname is passed to db2 via 'args' parameter.  */
363     args[1] = NULL;
364     if (asprintf(&args[0], "dbname=%s", dbname) < 0) {
365         com_err(pname, errno, "while setting up db parameters");
366         return 1;
367     }
368 
369     if ((retval = krb5_db_open(test_context, args, KRB5_KDB_OPEN_RO))) {
370         com_err(pname, retval, "while initializing database");
371         return(1);
372     }
373     /* Done with args */
374     free(args[0]);
375 
376     if ((retval = krb5_db_fetch_mkey_list(test_context, master_princ,
377                                           &master_keyblock))){
378         com_err(pname, retval, "while verifying master key");
379         (void) krb5_db_fini(test_context);
380         return(1);
381     }
382     if ((retval = krb5_db_get_principal(test_context, master_princ, 0,
383                                         &master_entry))) {
384         com_err(pname, retval, "while retrieving master entry");
385         (void) krb5_db_fini(test_context);
386         return(1);
387     }
388 
389     mblock.max_life = master_entry->max_life;
390     mblock.max_rlife = master_entry->max_renewable_life;
391     mblock.expiration = master_entry->expiration;
392 
393     /* don't set flags, master has some extra restrictions */
394     mblock.mkvno = master_entry->key_data[0].key_data_kvno;
395 
396     krb5_db_free_principal(test_context, master_entry);
397     return 0;
398 }
399