xref: /freebsd/crypto/krb5/src/lib/kdb/kdb_cpw.c (revision f1c4c3daccbaf3820f0e2224de53df12fc952fcc)
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