Imported Upstream version 1.15.1
[platform/upstream/krb5.git] / src / lib / kdb / kdb_default.c
1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /* lib/kdb/kdb_default.c */
3 /*
4  * Copyright 1995, 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 2009 Sun Microsystems, Inc.  All rights reserved.
28  * Use is subject to license terms.
29  */
30
31 #include "k5-int.h"
32 #include "kdb.h"
33 #include <string.h>
34 #include <stdio.h>
35 #include <errno.h>
36 #include <arpa/inet.h>
37
38
39 /*
40  * Given a particular enctype and optional salttype and kvno, find the
41  * most appropriate krb5_key_data entry of the database entry.
42  *
43  * If stype or kvno is negative, it is ignored.
44  * If kvno is 0 get the key which is maxkvno for the princ and matches
45  * the other attributes.
46  */
47 krb5_error_code
48 krb5_dbe_def_search_enctype(kcontext, dbentp, start, ktype, stype, kvno, kdatap)
49     krb5_context        kcontext;
50     krb5_db_entry       *dbentp;
51     krb5_int32          *start;
52     krb5_int32          ktype;
53     krb5_int32          stype;
54     krb5_int32          kvno;
55     krb5_key_data       **kdatap;
56 {
57     int                 i, idx;
58     int                 maxkvno;
59     krb5_key_data       *datap;
60     krb5_error_code     ret;
61     krb5_boolean        saw_non_permitted = FALSE;
62
63     ret = 0;
64     if (ktype != -1 && !krb5_is_permitted_enctype(kcontext, ktype))
65         return KRB5_KDB_NO_PERMITTED_KEY;
66
67     if (kvno == -1 && stype == -1 && ktype == -1)
68         kvno = 0;
69
70     if (kvno == 0) {
71         /* Get the max key version */
72         for (i = 0; i < dbentp->n_key_data; i++) {
73             if (kvno < dbentp->key_data[i].key_data_kvno) {
74                 kvno = dbentp->key_data[i].key_data_kvno;
75             }
76         }
77     }
78
79     maxkvno = -1;
80     idx = -1;
81     datap = (krb5_key_data *) NULL;
82     for (i = *start; i < dbentp->n_key_data; i++) {
83         krb5_boolean    similar;
84         krb5_int32      db_stype;
85
86         ret = 0;
87         if (dbentp->key_data[i].key_data_ver > 1) {
88             db_stype = dbentp->key_data[i].key_data_type[1];
89         } else {
90             db_stype = KRB5_KDB_SALTTYPE_NORMAL;
91         }
92
93         /* Match this entry against the arguments. */
94         if (ktype != -1) {
95             ret = krb5_c_enctype_compare(kcontext, (krb5_enctype) ktype,
96                                          dbentp->key_data[i].key_data_type[0],
97                                          &similar);
98             if (ret != 0 || !similar)
99                 continue;
100         }
101         if (stype >= 0 && db_stype != stype)
102             continue;
103         if (kvno >= 0 && dbentp->key_data[i].key_data_kvno != kvno)
104             continue;
105
106         /* Filter out non-permitted enctypes. */
107         if (!krb5_is_permitted_enctype(kcontext,
108                                        dbentp->key_data[i].key_data_type[0])) {
109             saw_non_permitted = TRUE;
110             continue;
111         }
112
113         if (dbentp->key_data[i].key_data_kvno > maxkvno) {
114             maxkvno = dbentp->key_data[i].key_data_kvno;
115             datap = &dbentp->key_data[i];
116             idx = i;
117         }
118     }
119     /* If we scanned the whole set of keys and matched only non-permitted
120      * enctypes, indicate that. */
121     if (maxkvno < 0 && *start == 0 && saw_non_permitted)
122         ret = KRB5_KDB_NO_PERMITTED_KEY;
123     if (maxkvno < 0)
124         return ret ? ret : KRB5_KDB_NO_MATCHING_KEY;
125     *kdatap = datap;
126     *start = idx+1;
127     return 0;
128 }
129
130 /*
131  *  kdb default functions. Ideally, some other file should have this functions. For now, TBD.
132  */
133 #ifndef min
134 #define min(a,b) (((a) < (b)) ? (a) : (b))
135 #endif
136
137 krb5_error_code
138 krb5_def_store_mkey_list(krb5_context       context,
139                          char               *keyfile,
140                          krb5_principal     mname,
141                          krb5_keylist_node  *keylist,
142                          char               *master_pwd)
143 {
144     krb5_error_code retval = 0;
145     char defkeyfile[MAXPATHLEN+1];
146     char *tmp_ktname = NULL, *tmp_ktpath;
147     krb5_data *realm = krb5_princ_realm(context, mname);
148     krb5_keytab kt = NULL;
149     krb5_keytab_entry new_entry;
150     struct stat stb;
151     int statrc;
152
153     if (!keyfile) {
154         (void) snprintf(defkeyfile, sizeof(defkeyfile), "%s%s",
155                         DEFAULT_KEYFILE_STUB, realm->data);
156         keyfile = defkeyfile;
157     }
158
159     if ((statrc = stat(keyfile, &stb)) >= 0) {
160         /* if keyfile exists it better be a regular file */
161         if (!S_ISREG(stb.st_mode)) {
162             retval = EINVAL;
163             k5_setmsg(context, retval,
164                       _("keyfile (%s) is not a regular file: %s"),
165                       keyfile, error_message(retval));
166             goto out;
167         }
168     }
169
170     /*
171      * We assume the stash file is in a directory writable only by root.
172      * As such, don't worry about collisions, just do an atomic rename.
173      */
174     retval = asprintf(&tmp_ktname, "FILE:%s_tmp", keyfile);
175     if (retval < 0) {
176         k5_setmsg(context, retval,
177                   _("Could not create temp keytab file name."));
178         goto out;
179     }
180
181     /*
182      * Set tmp_ktpath to point to the keyfile path (skip FILE:).  Subtracting
183      * 1 to account for NULL terminator in sizeof calculation of a string
184      * constant.  Used further down.
185      */
186     tmp_ktpath = tmp_ktname + (sizeof("FILE:") - 1);
187
188     /*
189      * This time-of-check-to-time-of-access race is fine; we care only
190      * about an administrator running the command twice, not an attacker
191      * trying to beat us to creating the file.  Per the above comment, we
192      * assume the stash file is in a directory writable only by root.
193      */
194     statrc = stat(tmp_ktpath, &stb);
195     if (statrc == -1 && errno != ENOENT) {
196         /* ENOENT is the expected case */
197         retval = errno;
198         goto out;
199     } else if (statrc == 0) {
200         retval = EEXIST;
201         k5_setmsg(context, retval,
202                   _("Temporary stash file already exists: %s."), tmp_ktpath);
203         goto out;
204     }
205
206     /* create new stash keytab using temp file name */
207     retval = krb5_kt_resolve(context, tmp_ktname, &kt);
208     if (retval != 0)
209         goto out;
210
211     while (keylist && !retval) {
212         memset(&new_entry, 0, sizeof(new_entry));
213         new_entry.principal = mname;
214         new_entry.key = keylist->keyblock;
215         new_entry.vno = keylist->kvno;
216
217         retval = krb5_kt_add_entry(context, kt, &new_entry);
218         keylist = keylist->next;
219     }
220     krb5_kt_close(context, kt);
221
222     if (retval != 0) {
223         /* Clean up by deleting the tmp keyfile if it exists. */
224         (void)unlink(tmp_ktpath);
225     } else {
226         /* Atomically rename temp keyfile to original filename. */
227         if (rename(tmp_ktpath, keyfile) < 0) {
228             retval = errno;
229             k5_setmsg(context, retval,
230                       _("rename of temporary keyfile (%s) to (%s) failed: %s"),
231                       tmp_ktpath, keyfile, error_message(errno));
232         }
233     }
234
235 out:
236     if (tmp_ktname != NULL)
237         free(tmp_ktname);
238
239     return retval;
240 }
241
242 static krb5_error_code
243 krb5_db_def_fetch_mkey_stash(krb5_context   context,
244                              const char *keyfile,
245                              krb5_keyblock *key,
246                              krb5_kvno     *kvno)
247 {
248     krb5_error_code retval = 0;
249     krb5_ui_2 enctype;
250     krb5_ui_4 keylength;
251     FILE *kf = NULL;
252
253     if (!(kf = fopen(keyfile, "rb")))
254         return KRB5_KDB_CANTREAD_STORED;
255     set_cloexec_file(kf);
256
257     if (fread((krb5_pointer) &enctype, 2, 1, kf) != 1) {
258         retval = KRB5_KDB_CANTREAD_STORED;
259         goto errout;
260     }
261
262 #if BIG_ENDIAN_MASTER_KEY
263     enctype = ntohs((uint16_t) enctype);
264 #endif
265
266     if (key->enctype == ENCTYPE_UNKNOWN)
267         key->enctype = enctype;
268     else if (enctype != key->enctype) {
269         retval = KRB5_KDB_BADSTORED_MKEY;
270         goto errout;
271     }
272
273     if (fread((krb5_pointer) &keylength,
274               sizeof(keylength), 1, kf) != 1) {
275         retval = KRB5_KDB_CANTREAD_STORED;
276         goto errout;
277     }
278
279 #if BIG_ENDIAN_MASTER_KEY
280     key->length = ntohl((uint32_t) keylength);
281 #else
282     key->length = keylength;
283 #endif
284
285     if (!key->length || ((int) key->length) < 0) {
286         retval = KRB5_KDB_BADSTORED_MKEY;
287         goto errout;
288     }
289
290     if (!(key->contents = (krb5_octet *)malloc(key->length))) {
291         retval = ENOMEM;
292         goto errout;
293     }
294
295     if (fread((krb5_pointer) key->contents, sizeof(key->contents[0]),
296               key->length, kf) != key->length) {
297         retval = KRB5_KDB_CANTREAD_STORED;
298         zap(key->contents, key->length);
299         free(key->contents);
300         key->contents = 0;
301     } else
302         retval = 0;
303
304     /*
305      * Note, the old stash format did not store the kvno and at this point it
306      * can be assumed to be 1 as is the case for the mkey princ.  If the kvno is
307      * passed in and isn't ignore_vno just leave it alone as this could cause
308      * verifcation trouble if the mkey princ is using a kvno other than 1.
309      */
310     if (kvno && *kvno == IGNORE_VNO)
311         *kvno = 1;
312
313 errout:
314     (void) fclose(kf);
315     return retval;
316 }
317
318 static krb5_error_code
319 krb5_db_def_fetch_mkey_keytab(krb5_context   context,
320                               const char     *keyfile,
321                               krb5_principal mname,
322                               krb5_keyblock  *key,
323                               krb5_kvno      *kvno)
324 {
325     krb5_error_code retval = 0;
326     krb5_keytab kt = NULL;
327     krb5_keytab_entry kt_ent;
328     krb5_enctype enctype = IGNORE_ENCTYPE;
329
330     if ((retval = krb5_kt_resolve(context, keyfile, &kt)) != 0)
331         goto errout;
332
333     /* override default */
334     if (key->enctype != ENCTYPE_UNKNOWN)
335         enctype = key->enctype;
336
337     if ((retval = krb5_kt_get_entry(context, kt, mname,
338                                     kvno ? *kvno : IGNORE_VNO,
339                                     enctype,
340                                     &kt_ent)) == 0) {
341
342         if (key->enctype == ENCTYPE_UNKNOWN)
343             key->enctype = kt_ent.key.enctype;
344
345         if (((int) kt_ent.key.length) < 0) {
346             retval = KRB5_KDB_BADSTORED_MKEY;
347             krb5_kt_free_entry(context, &kt_ent);
348             goto errout;
349         }
350
351         key->length = kt_ent.key.length;
352
353         /*
354          * If a kvno pointer was passed in and it dereferences the
355          * IGNORE_VNO value then it should be assigned the value of the kvno
356          * found in the keytab otherwise the KNVO specified should be the
357          * same as the one returned from the keytab.
358          */
359         if (kvno != NULL && *kvno == IGNORE_VNO)
360             *kvno = kt_ent.vno;
361
362         /*
363          * kt_ent will be free'd so need to allocate and copy key contents for
364          * output to caller.
365          */
366         key->contents = k5memdup(kt_ent.key.contents, kt_ent.key.length,
367                                  &retval);
368         if (key->contents == NULL) {
369             krb5_kt_free_entry(context, &kt_ent);
370             goto errout;
371         }
372         krb5_kt_free_entry(context, &kt_ent);
373     }
374
375 errout:
376     if (kt)
377         krb5_kt_close(context, kt);
378
379     return retval;
380 }
381
382 krb5_error_code
383 krb5_db_def_fetch_mkey(krb5_context   context,
384                        krb5_principal mname,
385                        krb5_keyblock *key,
386                        krb5_kvno     *kvno,
387                        char          *db_args)
388 {
389     krb5_error_code retval;
390     char keyfile[MAXPATHLEN+1];
391     krb5_data *realm = krb5_princ_realm(context, mname);
392
393     key->magic = KV5M_KEYBLOCK;
394
395     if (db_args != NULL) {
396         (void) strncpy(keyfile, db_args, sizeof(keyfile));
397     } else {
398         (void) snprintf(keyfile, sizeof(keyfile), "%s%s",
399                         DEFAULT_KEYFILE_STUB, realm->data);
400     }
401     /* null terminate no matter what */
402     keyfile[sizeof(keyfile) - 1] = '\0';
403
404     /* Try the keytab and old stash file formats. */
405     retval = krb5_db_def_fetch_mkey_keytab(context, keyfile, mname, key, kvno);
406     if (retval == KRB5_KEYTAB_BADVNO)
407         retval = krb5_db_def_fetch_mkey_stash(context, keyfile, key, kvno);
408
409     /*
410      * Use a generic error code for failure to retrieve the master
411      * key, but set a message indicating the actual error.
412      */
413     if (retval != 0) {
414         k5_setmsg(context, KRB5_KDB_CANTREAD_STORED,
415                   _("Can not fetch master key (error: %s)."),
416                   error_message(retval));
417         return KRB5_KDB_CANTREAD_STORED;
418     } else
419         return 0;
420 }
421
422 krb5_error_code
423 krb5_def_fetch_mkey_list(krb5_context        context,
424                          krb5_principal        mprinc,
425                          const krb5_keyblock  *mkey,
426                          krb5_keylist_node  **mkeys_list)
427 {
428     krb5_error_code retval;
429     krb5_db_entry *master_entry;
430     krb5_boolean found_key = FALSE;
431     krb5_keyblock cur_mkey;
432     krb5_keylist_node *mkey_list_head = NULL, **mkey_list_node;
433     krb5_key_data *key_data;
434     krb5_mkey_aux_node  *mkey_aux_data_list = NULL, *aux_data_entry;
435     int i;
436
437     if (mkeys_list == NULL)
438         return (EINVAL);
439
440     memset(&cur_mkey, 0, sizeof(cur_mkey));
441
442     retval = krb5_db_get_principal(context, mprinc, 0, &master_entry);
443     if (retval == KRB5_KDB_NOENTRY)
444         return (KRB5_KDB_NOMASTERKEY);
445     if (retval)
446         return (retval);
447
448     if (master_entry->n_key_data == 0) {
449         retval = KRB5_KDB_NOMASTERKEY;
450         goto clean_n_exit;
451     }
452
453     /*
454      * Check if the input mkey is the latest key and if it isn't then find the
455      * latest mkey.
456      */
457
458     if (mkey->enctype == master_entry->key_data[0].key_data_type[0]) {
459         if (krb5_dbe_decrypt_key_data(context, mkey,
460                                       &master_entry->key_data[0],
461                                       &cur_mkey, NULL) == 0) {
462             found_key = TRUE;
463         }
464     }
465
466     if (!found_key) {
467         if ((retval = krb5_dbe_lookup_mkey_aux(context, master_entry,
468                                                &mkey_aux_data_list)))
469             goto clean_n_exit;
470
471         for (aux_data_entry = mkey_aux_data_list; aux_data_entry != NULL;
472              aux_data_entry = aux_data_entry->next) {
473
474             if (krb5_dbe_decrypt_key_data(context, mkey,
475                                           &aux_data_entry->latest_mkey,
476                                           &cur_mkey, NULL) == 0) {
477                 found_key = TRUE;
478                 break;
479             }
480         }
481         if (found_key != TRUE) {
482             k5_setmsg(context, KRB5_KDB_BADMASTERKEY,
483                       _("Unable to decrypt latest master key with the "
484                         "provided master key\n"));
485             retval = KRB5_KDB_BADMASTERKEY;
486             goto clean_n_exit;
487         }
488     }
489
490     /*
491      * Extract all the mkeys from master_entry using the most current mkey and
492      * create a mkey list for the mkeys field in kdc_realm_t.
493      */
494
495     mkey_list_head = (krb5_keylist_node *) malloc(sizeof(krb5_keylist_node));
496     if (mkey_list_head == NULL) {
497         retval = ENOMEM;
498         goto clean_n_exit;
499     }
500
501     memset(mkey_list_head, 0, sizeof(krb5_keylist_node));
502
503     /* Set mkey_list_head to the current mkey as an optimization. */
504     /* mkvno may not be latest so ... */
505     mkey_list_head->kvno = master_entry->key_data[0].key_data_kvno;
506     /* this is the latest clear mkey (avoids a redundant decrypt) */
507     mkey_list_head->keyblock = cur_mkey;
508
509     /* loop through any other master keys creating a list of krb5_keylist_nodes */
510     mkey_list_node = &mkey_list_head->next;
511     for (i = 1; i < master_entry->n_key_data; i++) {
512         if (*mkey_list_node == NULL) {
513             /* *mkey_list_node points to next field of previous node */
514             *mkey_list_node = (krb5_keylist_node *) malloc(sizeof(krb5_keylist_node));
515             if (*mkey_list_node == NULL) {
516                 retval = ENOMEM;
517                 goto clean_n_exit;
518             }
519             memset(*mkey_list_node, 0, sizeof(krb5_keylist_node));
520         }
521         key_data = &master_entry->key_data[i];
522         retval = krb5_dbe_decrypt_key_data(context, &cur_mkey, key_data,
523                                            &((*mkey_list_node)->keyblock),
524                                            NULL);
525         if (retval)
526             goto clean_n_exit;
527
528         (*mkey_list_node)->kvno = key_data->key_data_kvno;
529         mkey_list_node = &((*mkey_list_node)->next);
530     }
531
532     *mkeys_list = mkey_list_head;
533
534 clean_n_exit:
535     krb5_db_free_principal(context, master_entry);
536     krb5_dbe_free_mkey_aux_list(context, mkey_aux_data_list);
537     if (retval != 0)
538         krb5_dbe_free_key_list(context, mkey_list_head);
539     return retval;
540 }
541
542 krb5_error_code
543 krb5_db_def_rename_principal(krb5_context kcontext,
544                              krb5_const_principal source,
545                              krb5_const_principal target)
546 {
547     krb5_db_entry *kdb = NULL;
548     krb5_principal oldprinc;
549     krb5_error_code ret;
550
551     if (source == NULL || target == NULL)
552         return EINVAL;
553
554     ret = krb5_db_get_principal(kcontext, source, KRB5_KDB_FLAG_ALIAS_OK,
555                                 &kdb);
556     if (ret)
557         goto cleanup;
558
559     /* Store salt values explicitly so that they don't depend on the principal
560      * name. */
561     ret = krb5_dbe_specialize_salt(kcontext, kdb);
562     if (ret)
563         goto cleanup;
564
565     /* Temporarily alias kdb->princ to target and put the principal entry. */
566     oldprinc = kdb->princ;
567     kdb->princ = (krb5_principal)target;
568     ret = krb5_db_put_principal(kcontext, kdb);
569     kdb->princ = oldprinc;
570     if (ret)
571         goto cleanup;
572
573     ret = krb5_db_delete_principal(kcontext, (krb5_principal)source);
574
575
576 cleanup:
577     krb5_db_free_principal(kcontext, kdb);
578     return ret;
579 }