1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /* lib/kdb/kdb_cpw.c */
3 /*
4 * Copyright 1995, 2009, 2014 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 #include "k5-int.h"
53 #include "kdb.h"
54 #include <stdio.h>
55 #include <errno.h>
56
57 enum save { DISCARD_ALL, KEEP_LAST_KVNO, KEEP_ALL };
58
59 int
krb5_db_get_key_data_kvno(context,count,data)60 krb5_db_get_key_data_kvno(context, count, data)
61 krb5_context context;
62 int count;
63 krb5_key_data * data;
64 {
65 int i, kvno;
66 /* Find last key version number */
67 for (kvno = i = 0; i < count; i++) {
68 if (kvno < data[i].key_data_kvno) {
69 kvno = data[i].key_data_kvno;
70 }
71 }
72 return(kvno);
73 }
74
75 static void
cleanup_key_data(context,count,data)76 cleanup_key_data(context, count, data)
77 krb5_context context;
78 int count;
79 krb5_key_data * data;
80 {
81 int i;
82
83 /* If data is NULL, count is always 0 */
84 if (data == NULL) return;
85
86 for (i = 0; i < count; i++)
87 krb5_dbe_free_key_data_contents(context, &data[i]);
88 free(data);
89 }
90
91 /* Transfer key data from old_kd to new_kd, making sure that new_kd is
92 * encrypted with mkey. May steal from old_kd and zero it out. */
93 static krb5_error_code
preserve_one_old_key(krb5_context context,krb5_keyblock * mkey,krb5_db_entry * dbent,krb5_key_data * old_kd,krb5_key_data * new_kd)94 preserve_one_old_key(krb5_context context, krb5_keyblock *mkey,
95 krb5_db_entry *dbent, krb5_key_data *old_kd,
96 krb5_key_data *new_kd)
97 {
98 krb5_error_code ret;
99 krb5_keyblock kb;
100 krb5_keysalt salt;
101
102 memset(new_kd, 0, sizeof(*new_kd));
103
104 ret = krb5_dbe_decrypt_key_data(context, mkey, old_kd, &kb, NULL);
105 if (ret == 0) {
106 /* old_kd is already encrypted in mkey, so just move it. */
107 *new_kd = *old_kd;
108 memset(old_kd, 0, sizeof(*old_kd));
109 krb5_free_keyblock_contents(context, &kb);
110 return 0;
111 }
112
113 /* Decrypt and re-encrypt old_kd using mkey. */
114 ret = krb5_dbe_decrypt_key_data(context, NULL, old_kd, &kb, &salt);
115 if (ret)
116 return ret;
117 ret = krb5_dbe_encrypt_key_data(context, mkey, &kb, &salt,
118 old_kd->key_data_kvno, new_kd);
119 krb5_free_keyblock_contents(context, &kb);
120 krb5_free_data_contents(context, &salt.data);
121 return ret;
122 }
123
124 /*
125 * Add key_data to dbent, making sure that each entry is encrypted in mkey. If
126 * kvno is non-zero, preserve only keys of that kvno. May steal some elements
127 * from key_data and zero them out.
128 */
129 static krb5_error_code
preserve_old_keys(krb5_context context,krb5_keyblock * mkey,krb5_db_entry * dbent,int kvno,int n_key_data,krb5_key_data * key_data)130 preserve_old_keys(krb5_context context, krb5_keyblock *mkey,
131 krb5_db_entry *dbent, int kvno, int n_key_data,
132 krb5_key_data *key_data)
133 {
134 krb5_error_code ret;
135 int i;
136
137 for (i = 0; i < n_key_data; i++) {
138 if (kvno != 0 && key_data[i].key_data_kvno != kvno)
139 continue;
140 ret = krb5_dbe_create_key_data(context, dbent);
141 if (ret)
142 return ret;
143 ret = preserve_one_old_key(context, mkey, dbent, &key_data[i],
144 &dbent->key_data[dbent->n_key_data - 1]);
145 if (ret)
146 return ret;
147 }
148 return 0;
149 }
150
151 static krb5_error_code
add_key_rnd(context,master_key,ks_tuple,ks_tuple_count,db_entry,kvno)152 add_key_rnd(context, master_key, ks_tuple, ks_tuple_count, db_entry, kvno)
153 krb5_context context;
154 krb5_keyblock * master_key;
155 krb5_key_salt_tuple * ks_tuple;
156 int ks_tuple_count;
157 krb5_db_entry * db_entry;
158 int kvno;
159 {
160 krb5_keyblock key;
161 int i, j;
162 krb5_error_code retval;
163 krb5_key_data *kd_slot;
164
165 for (i = 0; i < ks_tuple_count; i++) {
166 krb5_boolean similar;
167
168 similar = 0;
169
170 /*
171 * We could use krb5_keysalt_iterate to replace this loop, or use
172 * krb5_keysalt_is_present for the loop below, but we want to avoid
173 * circular library dependencies.
174 */
175 for (j = 0; j < i; j++) {
176 if ((retval = krb5_c_enctype_compare(context,
177 ks_tuple[i].ks_enctype,
178 ks_tuple[j].ks_enctype,
179 &similar)))
180 return(retval);
181
182 if (similar)
183 break;
184 }
185
186 if (similar)
187 continue;
188
189 if ((retval = krb5_dbe_create_key_data(context, db_entry)))
190 return retval;
191 kd_slot = &db_entry->key_data[db_entry->n_key_data - 1];
192
193 /* there used to be code here to extract the old key, and derive
194 a new key from it. Now that there's a unified prng, that isn't
195 necessary. */
196
197 /* make new key */
198 if ((retval = krb5_c_make_random_key(context, ks_tuple[i].ks_enctype,
199 &key)))
200 return retval;
201
202 retval = krb5_dbe_encrypt_key_data(context, master_key, &key, NULL,
203 kvno, kd_slot);
204
205 krb5_free_keyblock_contents(context, &key);
206 if( retval )
207 return retval;
208 }
209
210 return 0;
211 }
212
213 /* Construct a random explicit salt. */
214 static krb5_error_code
make_random_salt(krb5_context context,krb5_keysalt * salt_out)215 make_random_salt(krb5_context context, krb5_keysalt *salt_out)
216 {
217 krb5_error_code retval;
218 unsigned char rndbuf[8];
219 krb5_data salt, rnd = make_data(rndbuf, sizeof(rndbuf));
220 unsigned int i;
221
222 /*
223 * Salts are limited by RFC 4120 to 7-bit ASCII. For ease of examination
224 * and to avoid certain folding issues for older enctypes, we use printable
225 * characters with four fixed bits and four random bits, encoding 64
226 * psuedo-random bits into 16 bytes.
227 */
228 retval = krb5_c_random_make_octets(context, &rnd);
229 if (retval)
230 return retval;
231 retval = alloc_data(&salt, sizeof(rndbuf) * 2);
232 if (retval)
233 return retval;
234 for (i = 0; i < sizeof(rndbuf); i++) {
235 salt.data[i * 2] = 0x40 | (rndbuf[i] >> 4);
236 salt.data[i * 2 + 1] = 0x40 | (rndbuf[i] & 0xf);
237 }
238
239 salt_out->type = KRB5_KDB_SALTTYPE_SPECIAL;
240 salt_out->data = salt;
241 return 0;
242 }
243
244 /*
245 * Add key_data for a krb5_db_entry
246 * If passwd is NULL the assumes that the caller wants a random password.
247 */
248 static krb5_error_code
add_key_pwd(context,master_key,ks_tuple,ks_tuple_count,passwd,db_entry,kvno)249 add_key_pwd(context, master_key, ks_tuple, ks_tuple_count, passwd,
250 db_entry, kvno)
251 krb5_context context;
252 krb5_keyblock * master_key;
253 krb5_key_salt_tuple * ks_tuple;
254 int ks_tuple_count;
255 const char * passwd;
256 krb5_db_entry * db_entry;
257 int kvno;
258 {
259 krb5_error_code retval;
260 krb5_keysalt key_salt;
261 krb5_keyblock key;
262 krb5_data pwd;
263 int i, j;
264 krb5_key_data *kd_slot;
265
266 for (i = 0; i < ks_tuple_count; i++) {
267 krb5_boolean similar;
268
269 similar = 0;
270
271 /*
272 * We could use krb5_keysalt_iterate to replace this loop, or use
273 * krb5_keysalt_is_present for the loop below, but we want to avoid
274 * circular library dependencies.
275 */
276 for (j = 0; j < i; j++) {
277 if ((retval = krb5_c_enctype_compare(context,
278 ks_tuple[i].ks_enctype,
279 ks_tuple[j].ks_enctype,
280 &similar)))
281 return(retval);
282
283 if (similar &&
284 (ks_tuple[j].ks_salttype == ks_tuple[i].ks_salttype))
285 break;
286 }
287
288 if (j < i)
289 continue;
290
291 if ((retval = krb5_dbe_create_key_data(context, db_entry)))
292 return(retval);
293 kd_slot = &db_entry->key_data[db_entry->n_key_data - 1];
294
295 /* Convert password string to key using appropriate salt */
296 switch (key_salt.type = ks_tuple[i].ks_salttype) {
297 case KRB5_KDB_SALTTYPE_ONLYREALM: {
298 krb5_data * saltdata;
299 if ((retval = krb5_copy_data(context, krb5_princ_realm(context,
300 db_entry->princ), &saltdata)))
301 return(retval);
302
303 key_salt.data = *saltdata;
304 free(saltdata);
305 }
306 break;
307 case KRB5_KDB_SALTTYPE_NOREALM:
308 if ((retval=krb5_principal2salt_norealm(context, db_entry->princ,
309 &key_salt.data)))
310 return(retval);
311 break;
312 case KRB5_KDB_SALTTYPE_NORMAL:
313 if ((retval = krb5_principal2salt(context, db_entry->princ,
314 &key_salt.data)))
315 return(retval);
316 break;
317 case KRB5_KDB_SALTTYPE_SPECIAL:
318 retval = make_random_salt(context, &key_salt);
319 if (retval)
320 return retval;
321 break;
322 default:
323 return(KRB5_KDB_BAD_SALTTYPE);
324 }
325
326 pwd = string2data((char *)passwd);
327
328 retval = krb5_c_string_to_key_with_params(context,
329 ks_tuple[i].ks_enctype,
330 &pwd, &key_salt.data,
331 NULL, &key);
332 if (retval) {
333 free(key_salt.data.data);
334 return retval;
335 }
336
337 retval = krb5_dbe_encrypt_key_data(context, master_key, &key,
338 (const krb5_keysalt *)&key_salt,
339 kvno, kd_slot);
340 if (key_salt.data.data)
341 free(key_salt.data.data);
342 free(key.contents);
343
344 if( retval )
345 return retval;
346 }
347
348 return 0;
349 }
350
351 static krb5_error_code
rekey(krb5_context context,krb5_keyblock * mkey,krb5_key_salt_tuple * ks_tuple,int ks_tuple_count,const char * password,int new_kvno,enum save savekeys,krb5_db_entry * db_entry)352 rekey(krb5_context context, krb5_keyblock *mkey, krb5_key_salt_tuple *ks_tuple,
353 int ks_tuple_count, const char *password, int new_kvno,
354 enum save savekeys, krb5_db_entry *db_entry)
355 {
356 krb5_error_code ret;
357 krb5_key_data *key_data;
358 int n_key_data, old_kvno, save_kvno;
359
360 /* Save aside the old key data. */
361 n_key_data = db_entry->n_key_data;
362 key_data = db_entry->key_data;
363 db_entry->n_key_data = 0;
364 db_entry->key_data = NULL;
365
366 /* Make sure the new kvno is greater than the old largest kvno. */
367 old_kvno = krb5_db_get_key_data_kvno(context, n_key_data, key_data);
368 if (new_kvno < old_kvno + 1)
369 new_kvno = old_kvno + 1;
370 /* Wrap from 65535 to 1; we can only store 16-bit kvno values in key_data,
371 * and we assign special meaning to kvno 0. */
372 if (new_kvno == (1 << 16))
373 new_kvno = 1;
374
375 /* Add new keys to the front of the list. */
376 if (password != NULL) {
377 ret = add_key_pwd(context, mkey, ks_tuple, ks_tuple_count, password,
378 db_entry, new_kvno);
379 } else {
380 ret = add_key_rnd(context, mkey, ks_tuple, ks_tuple_count, db_entry,
381 new_kvno);
382 }
383 if (ret) {
384 cleanup_key_data(context, db_entry->n_key_data, db_entry->key_data);
385 db_entry->n_key_data = n_key_data;
386 db_entry->key_data = key_data;
387 return ret;
388 }
389
390 /* Possibly add some or all of the old keys to the back of the list. May
391 * steal from and zero out some of the old key data entries. */
392 if (savekeys != DISCARD_ALL) {
393 save_kvno = (savekeys == KEEP_LAST_KVNO) ? old_kvno : 0;
394 ret = preserve_old_keys(context, mkey, db_entry, save_kvno, n_key_data,
395 key_data);
396 }
397
398 /* Free any old key data entries not stolen and zeroed out above. */
399 cleanup_key_data(context, n_key_data, key_data);
400 return ret;
401 }
402
403 /*
404 * Change random key for a krb5_db_entry
405 * Assumes the max kvno
406 *
407 * As a side effect all old keys are nuked if keepold is false.
408 */
409 krb5_error_code
krb5_dbe_crk(krb5_context context,krb5_keyblock * mkey,krb5_key_salt_tuple * ks_tuple,int ks_tuple_count,krb5_boolean keepold,krb5_db_entry * dbent)410 krb5_dbe_crk(krb5_context context, krb5_keyblock *mkey,
411 krb5_key_salt_tuple *ks_tuple, int ks_tuple_count,
412 krb5_boolean keepold, krb5_db_entry *dbent)
413 {
414 return rekey(context, mkey, ks_tuple, ks_tuple_count, NULL, 0,
415 keepold ? KEEP_ALL : DISCARD_ALL, dbent);
416 }
417
418 /*
419 * Add random key for a krb5_db_entry
420 * Assumes the max kvno
421 *
422 * As a side effect all old keys older than the max kvno are nuked.
423 */
424 krb5_error_code
krb5_dbe_ark(krb5_context context,krb5_keyblock * mkey,krb5_key_salt_tuple * ks_tuple,int ks_tuple_count,krb5_db_entry * dbent)425 krb5_dbe_ark(krb5_context context, krb5_keyblock *mkey,
426 krb5_key_salt_tuple *ks_tuple, int ks_tuple_count,
427 krb5_db_entry *dbent)
428 {
429 return rekey(context, mkey, ks_tuple, ks_tuple_count, NULL, 0,
430 KEEP_LAST_KVNO, dbent);
431 }
432
433 /*
434 * Change password for a krb5_db_entry
435 * Assumes the max kvno
436 *
437 * As a side effect all old keys are nuked if keepold is false.
438 */
439 krb5_error_code
krb5_dbe_def_cpw(krb5_context context,krb5_keyblock * mkey,krb5_key_salt_tuple * ks_tuple,int ks_tuple_count,char * password,int new_kvno,krb5_boolean keepold,krb5_db_entry * dbent)440 krb5_dbe_def_cpw(krb5_context context, krb5_keyblock *mkey,
441 krb5_key_salt_tuple *ks_tuple, int ks_tuple_count,
442 char *password, int new_kvno, krb5_boolean keepold,
443 krb5_db_entry *dbent)
444 {
445 return rekey(context, mkey, ks_tuple, ks_tuple_count, password, new_kvno,
446 keepold ? KEEP_ALL : DISCARD_ALL, dbent);
447 }
448
449 /*
450 * Add password for a krb5_db_entry
451 * Assumes the max kvno
452 *
453 * As a side effect all old keys older than the max kvno are nuked.
454 */
455 krb5_error_code
krb5_dbe_apw(krb5_context context,krb5_keyblock * mkey,krb5_key_salt_tuple * ks_tuple,int ks_tuple_count,char * password,krb5_db_entry * dbent)456 krb5_dbe_apw(krb5_context context, krb5_keyblock *mkey,
457 krb5_key_salt_tuple *ks_tuple, int ks_tuple_count, char *password,
458 krb5_db_entry *dbent)
459 {
460 return rekey(context, mkey, ks_tuple, ks_tuple_count, password, 0,
461 KEEP_LAST_KVNO, dbent);
462 }
463