Fix various memory allocation and key/salt tuple related bugs
[krb5.git] / src / lib / kdb / kdb_cpw.c
1 /*
2  * lib/kdb/kdb_cpw.c
3  *
4  * Copyright 1995 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.  M.I.T. makes no representations about the suitability of
20  * this software for any purpose.  It is provided "as is" without express
21  * or implied warranty.
22  * 
23  */
24
25 #include "k5-int.h"
26 #include "krb5/adm.h"
27 #include <stdio.h>
28 #include <errno.h>
29
30 static int
31 get_key_data_kvno(context, count, data)
32     krb5_context          context;
33     int                   count;
34     krb5_key_data       * data;
35 {
36     int i, kvno;
37     /* Find last key version number */
38     for (kvno = i = 0; i < count; i++) {
39         if (kvno < data[i].key_data_kvno) {
40             kvno = data[i].key_data_kvno;
41         }
42     }
43     return(kvno);
44 }
45
46 static void
47 cleanup_key_data(context, count, data)
48     krb5_context          context;
49     int                   count;
50     krb5_key_data       * data;
51 {
52     int i, j;
53
54     for (i = 0; i < count; i++) {
55         for (j = 0; j < data[i].key_data_ver; j++) {
56             if (data[i].key_data_length[j]) {
57                 free(data[i].key_data_contents[j]);
58             }
59         }
60     }
61 }
62
63 /*
64  * Currently we can only generate random keys for preinitialized
65  * krb5_encrypt_block with a seed. This is bogus but currently
66  * necessary to insure that we don't generate two keys with the 
67  * same data.
68  */
69 static krb5_error_code
70 add_key_rnd(context, master_eblock, ks_tuple, ks_tuple_count, db_entry, kvno)
71     krb5_context          context;
72     krb5_encrypt_block  * master_eblock;
73     krb5_key_salt_tuple * ks_tuple;
74     int                   ks_tuple_count;
75     krb5_db_entry       * db_entry;
76     int                   kvno;
77 {
78     krb5_principal        krbtgt_princ;
79     krb5_keyblock         krbtgt_keyblock, * key;
80     krb5_pointer          krbtgt_seed;  
81     krb5_encrypt_block    krbtgt_eblock;
82     krb5_db_entry         krbtgt_entry;
83     krb5_boolean          more, found;
84     int                   max_kvno, one, i, j;
85     krb5_error_code       retval;
86
87     memset(&krbtgt_keyblock, 0, sizeof(krbtgt_keyblock));
88     retval = krb5_build_principal_ext(context, &krbtgt_princ,
89                                       db_entry->princ->realm.length,
90                                       db_entry->princ->realm.data,
91                                       KRB5_TGS_NAME_SIZE,
92                                       KRB5_TGS_NAME,
93                                       db_entry->princ->realm.length,
94                                       db_entry->princ->realm.data,
95                                       0);
96     if (retval)
97         return retval;
98
99     /* Get tgt from database */
100     retval = krb5_db_get_principal(context, krbtgt_princ, &krbtgt_entry,
101                                    &one, &more);
102     krb5_free_principal(context, krbtgt_princ); /* don't need it anymore */
103     if (retval)
104         return(retval);
105     if ((one > 1) || (more)) {
106         krb5_db_free_principal(context, &krbtgt_entry, one);
107         return KRB5KDC_ERR_PRINCIPAL_NOT_UNIQUE;
108     }
109     if (!one) 
110         return KRB5_KDB_NOENTRY;
111
112     /* Get max kvno */
113     for (max_kvno = j = 0; j < krbtgt_entry.n_key_data; j++) {
114          if (max_kvno < krbtgt_entry.key_data[j].key_data_kvno) {
115              max_kvno = krbtgt_entry.key_data[j].key_data_kvno;
116         }
117     }
118
119     for (i = 0; i < ks_tuple_count; i++) {
120         /*
121          * We could use krb5_keysalt_iterate to replace this loop, or use
122          * krb5_keysalt_is_present for the loop below, but we want to avoid
123          * circular library dependencies.
124          */
125         found = 0;
126         for (j = 0; j < i; j++) {
127             if (ks_tuple[j].ks_keytype == ks_tuple[i].ks_keytype) {
128                 found = 1;
129                 break;
130             }
131         }
132         if (found)
133             continue;
134         if (retval = krb5_dbe_create_key_data(context, db_entry)) 
135             goto add_key_rnd_err;
136
137         for (j = 0; j < krbtgt_entry.n_key_data; j++) {
138              if ((krbtgt_entry.key_data[j].key_data_kvno == max_kvno) &&
139                  (krbtgt_entry.key_data[j].key_data_type[0] == 
140                   ks_tuple[i].ks_keytype)) {
141                 break;
142             }
143         }
144
145         if (j == krbtgt_entry.n_key_data) {
146             retval = KRB5_KDB_BAD_KEYTYPE;
147             goto add_key_rnd_err;
148         }
149
150         /* Decrypt key */
151         if (retval = krb5_dbekd_decrypt_key_data(context, master_eblock, 
152                                                  &krbtgt_entry.key_data[j],
153                                                  &krbtgt_keyblock, NULL)) {
154             goto add_key_rnd_err;
155         }
156
157         /* Init key */
158         krb5_use_keytype(context, &krbtgt_eblock, ks_tuple[i].ks_keytype);
159         if (retval = krb5_process_key(context,&krbtgt_eblock,&krbtgt_keyblock)){
160             goto add_key_rnd_err;
161         }
162
163         /* Init random generator */
164         if (retval = krb5_init_random_key(context, &krbtgt_eblock,
165                                           &krbtgt_keyblock, &krbtgt_seed)) {
166             krb5_finish_key(context, &krbtgt_eblock);
167             goto add_key_rnd_err;
168         }
169
170         if (retval = krb5_random_key(context,&krbtgt_eblock,krbtgt_seed,&key)) {
171             krb5_finish_random_key(context, &krbtgt_eblock, &krbtgt_seed);
172             krb5_finish_key(context, &krbtgt_eblock);
173             goto add_key_rnd_err;
174         }
175
176         krb5_finish_random_key(context, &krbtgt_eblock, &krbtgt_seed);
177         krb5_finish_key(context, &krbtgt_eblock);
178
179         if (retval = krb5_dbekd_encrypt_key_data(context, master_eblock, 
180                                                  key, NULL, kvno + 1, 
181                                                  &db_entry->key_data[db_entry->n_key_data-1])) {
182             krb5_free_keyblock(context, key);
183             goto add_key_rnd_err;
184         }
185
186         /* Finish random key */
187         krb5_free_keyblock(context, key);
188     }
189
190 add_key_rnd_err:;
191     krb5_db_free_principal(context, &krbtgt_entry, one);
192     if (krbtgt_keyblock.contents && krbtgt_keyblock.length) {
193         memset(krbtgt_keyblock.contents, 0, krbtgt_keyblock.length);
194         krb5_xfree(krbtgt_keyblock.contents);
195     }
196     return(retval);
197 }
198
199 /*
200  * Change random key for a krb5_db_entry 
201  * Assumes the max kvno
202  *
203  * As a side effect all old keys are nuked.
204  */
205 krb5_error_code
206 krb5_dbe_crk(context, master_eblock, ks_tuple, ks_tuple_count, db_entry)
207     krb5_context          context;
208     krb5_encrypt_block  * master_eblock;
209     krb5_key_salt_tuple * ks_tuple;
210     int                   ks_tuple_count;
211     krb5_db_entry       * db_entry;
212 {
213     int                   key_data_count;
214     krb5_key_data       * key_data;
215     krb5_error_code       retval;
216     int                   kvno;
217
218     /* First save the old keydata */
219     kvno = get_key_data_kvno(context, db_entry->n_key_data, db_entry->key_data);
220     key_data_count = db_entry->n_key_data;
221     key_data = db_entry->key_data;
222     db_entry->key_data = NULL;
223     db_entry->n_key_data = 0;
224
225     if (retval = add_key_rnd(context, master_eblock, ks_tuple, 
226                              ks_tuple_count, db_entry, kvno)) {
227         cleanup_key_data(context, db_entry->n_key_data, db_entry->key_data);
228         db_entry->n_key_data = key_data_count;
229         db_entry->key_data = key_data;
230     } else {
231         cleanup_key_data(context, key_data_count, key_data);
232     }
233     return(retval);
234 }
235
236 /*
237  * Add random key for a krb5_db_entry 
238  * Assumes the max kvno
239  *
240  * As a side effect all old keys older than the max kvno are nuked.
241  */
242 krb5_error_code
243 krb5_dbe_ark(context, master_eblock, ks_tuple, ks_tuple_count, db_entry)
244     krb5_context          context;
245     krb5_encrypt_block  * master_eblock;
246     krb5_key_salt_tuple * ks_tuple;
247     int                   ks_tuple_count;
248     krb5_db_entry       * db_entry;
249 {
250     int                   key_data_count;
251     krb5_key_data       * key_data;
252     krb5_error_code       retval;
253     int                   kvno;
254     int                   i;
255
256     /* First save the old keydata */
257     kvno = get_key_data_kvno(context, db_entry->n_key_data, db_entry->key_data);
258     key_data_count = db_entry->n_key_data;
259     key_data = db_entry->key_data;
260     db_entry->key_data = NULL;
261     db_entry->n_key_data = 0;
262
263     if (retval = add_key_rnd(context, master_eblock, ks_tuple, 
264                              ks_tuple_count, db_entry, kvno)) {
265         cleanup_key_data(context, db_entry->n_key_data, db_entry->key_data);
266         db_entry->n_key_data = key_data_count;
267         db_entry->key_data = key_data;
268     } else {
269         /* Copy keys with key_data_kvno = kvno */
270         for (i = 0; i < key_data_count; i++) {
271             if (key_data[i].key_data_kvno = kvno) {
272                 if (retval = krb5_dbe_create_key_data(context, db_entry)) {
273                     cleanup_key_data(context, db_entry->n_key_data,
274                                      db_entry->key_data);
275                     break;
276                 }
277                 /* We should decrypt/re-encrypt the data to use the same mkvno*/
278                 db_entry->key_data[db_entry->n_key_data - 1] = key_data[i];
279                 memset(&key_data[i], 0, sizeof(krb5_key_data));
280             }
281         }
282         cleanup_key_data(context, key_data_count, key_data);
283     }
284     return(retval);
285 }
286
287 /*
288  * Add key_data for a krb5_db_entry 
289  * If passwd is NULL the assumes that the caller wants a random password.
290  */
291 static krb5_error_code
292 add_key_pwd(context, master_eblock, ks_tuple, ks_tuple_count, passwd, 
293             db_entry, kvno)
294     krb5_context          context;
295     krb5_encrypt_block  * master_eblock;
296     krb5_key_salt_tuple * ks_tuple;
297     int                   ks_tuple_count;
298     char                * passwd;
299     krb5_db_entry       * db_entry;
300     int                   kvno;
301 {
302     krb5_error_code       retval;
303     krb5_encrypt_block    key_eblock;
304     krb5_keysalt          key_salt;
305     krb5_keyblock         key;
306     krb5_data             pwd;
307     krb5_boolean          found;
308     int                   i, j;
309
310     for (i = 0; i < ks_tuple_count; i++) {
311         /*
312          * We could use krb5_keysalt_iterate to replace this loop, or use
313          * krb5_keysalt_is_present for the loop below, but we want to avoid
314          * circular library dependencies.
315          */
316         found = 0;
317         for (j = 0; j < i; j++) {
318             if ((ks_tuple[j].ks_keytype == ks_tuple[i].ks_keytype) &&
319                 (ks_tuple[j].ks_salttype == ks_tuple[i].ks_salttype)) {
320                 found = 1;
321                 break;
322             }
323         }
324         if (found)
325             continue;
326         krb5_use_keytype(context, &key_eblock, ks_tuple[i].ks_keytype);
327         if (retval = krb5_dbe_create_key_data(context, db_entry)) 
328             return(retval);
329
330         /* Convert password string to key using appropriate salt */
331         switch (key_salt.type = ks_tuple[i].ks_salttype) {
332         case KRB5_KDB_SALTTYPE_ONLYREALM: {
333             krb5_data * saltdata;
334             if (retval = krb5_copy_data(context, krb5_princ_realm(context,
335                                         db_entry->princ), &saltdata))
336                 return(retval);
337
338             key_salt.data = *saltdata;
339             krb5_xfree(saltdata);
340         }
341                 break;
342         case KRB5_KDB_SALTTYPE_NOREALM:
343             if (retval=krb5_principal2salt_norealm(context, db_entry->princ,
344                                                          &key_salt.data)) 
345                 return(retval);
346             break;
347         case KRB5_KDB_SALTTYPE_NORMAL:
348             if (retval = krb5_principal2salt(context, db_entry->princ,
349                                                  &key_salt.data)) 
350                 return(retval);
351             break;
352         case KRB5_KDB_SALTTYPE_V4:
353             key_salt.data.length = 0;
354             key_salt.data.data = 0;
355             break;
356         default:
357             return(KRB5_KDB_BAD_SALTTYPE);
358         }
359
360         pwd.data = passwd;
361         pwd.length = strlen(passwd);
362         if (retval = krb5_string_to_key(context, &key_eblock, 
363                                         ks_tuple[i].ks_keytype, &key,
364                                         &pwd, &key_salt.data))
365             return(retval);
366
367         if (retval = krb5_dbekd_encrypt_key_data(context, master_eblock, &key,
368                      (const krb5_keysalt *)&key_salt,
369                      kvno + 1, &db_entry->key_data[db_entry->n_key_data-1])) {
370             krb5_xfree(key.contents);
371             return(retval);
372         }
373         krb5_xfree(key.contents);
374     }
375     return(retval);
376 }
377
378 /*
379  * Change password for a krb5_db_entry 
380  * Assumes the max kvno
381  *
382  * As a side effect all old keys are nuked.
383  */
384 krb5_error_code
385 krb5_dbe_cpw(context, master_eblock, ks_tuple, ks_tuple_count, passwd, db_entry)
386     krb5_context          context;
387     krb5_encrypt_block  * master_eblock;
388     krb5_key_salt_tuple * ks_tuple;
389     int                   ks_tuple_count;
390     char                * passwd;
391     krb5_db_entry       * db_entry;
392 {
393     int                   key_data_count;
394     krb5_key_data       * key_data;
395     krb5_error_code       retval;
396     int                   kvno;
397
398     /* First save the old keydata */
399     kvno = get_key_data_kvno(context, db_entry->n_key_data, db_entry->key_data);
400     key_data_count = db_entry->n_key_data;
401     key_data = db_entry->key_data;
402     db_entry->key_data = NULL;
403     db_entry->n_key_data = 0;
404
405     if (retval = add_key_pwd(context, master_eblock, ks_tuple, ks_tuple_count,
406                              passwd, db_entry, kvno)) {
407         cleanup_key_data(context, db_entry->n_key_data, db_entry->key_data);
408         db_entry->n_key_data = key_data_count;
409         db_entry->key_data = key_data;
410     } else {
411         cleanup_key_data(context, key_data_count, key_data);
412     }
413     return(retval);
414 }
415
416 /*
417  * Add password for a krb5_db_entry 
418  * Assumes the max kvno
419  *
420  * As a side effect all old keys older than the max kvno are nuked.
421  */
422 krb5_error_code
423 krb5_dbe_apw(context, master_eblock, ks_tuple, ks_tuple_count, passwd, db_entry)
424     krb5_context          context;
425     krb5_encrypt_block  * master_eblock;
426     krb5_key_salt_tuple * ks_tuple;
427     int                   ks_tuple_count;
428     char                * passwd;
429     krb5_db_entry       * db_entry;
430 {
431     int                   key_data_count;
432     krb5_key_data       * key_data;
433     krb5_error_code       retval;
434     int                   kvno;
435     int                   i;
436
437     /* First save the old keydata */
438     kvno = get_key_data_kvno(context, db_entry->n_key_data, db_entry->key_data);
439     key_data_count = db_entry->n_key_data;
440     key_data = db_entry->key_data;
441     db_entry->key_data = NULL;
442     db_entry->n_key_data = 0;
443
444     if (retval = add_key_pwd(context, master_eblock, ks_tuple, ks_tuple_count,
445                              passwd, db_entry, kvno)) {
446         cleanup_key_data(context, db_entry->n_key_data, db_entry->key_data);
447         db_entry->n_key_data = key_data_count;
448         db_entry->key_data = key_data;
449     } else {
450         /* Copy keys with key_data_kvno = kvno */
451         for (i = 0; i < key_data_count; i++) {
452             if (key_data[i].key_data_kvno = kvno) {
453                 if (retval = krb5_dbe_create_key_data(context, db_entry)) {
454                     cleanup_key_data(context, db_entry->n_key_data,
455                                      db_entry->key_data);
456                     break;
457                 }
458                 /* We should decrypt/re-encrypt the data to use the same mkvno*/
459                 db_entry->key_data[db_entry->n_key_data - 1] = key_data[i];
460                 memset(&key_data[i], 0, sizeof(krb5_key_data));
461             }
462         }
463         cleanup_key_data(context, key_data_count, key_data);
464     }
465     return(retval);
466 }