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