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