* kdb_cpw.c (krb5_dbe_crk):
[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.  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
28 /*
29  * Copyright (C) 1998 by the FundsXpress, INC.
30  * 
31  * All rights reserved.
32  * 
33  * Export of this software from the United States of America may require
34  * a specific license from the United States Government.  It is the
35  * responsibility of any person or organization contemplating export to
36  * obtain such a license before exporting.
37  * 
38  * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
39  * distribute this software and its documentation for any purpose and
40  * without fee is hereby granted, provided that the above copyright
41  * notice appear in all copies and that both that copyright notice and
42  * this permission notice appear in supporting documentation, and that
43  * the name of FundsXpress. not be used in advertising or publicity pertaining
44  * to distribution of the software without specific, written prior
45  * permission.  FundsXpress makes no representations about the suitability of
46  * this software for any purpose.  It is provided "as is" without express
47  * or implied warranty.
48  * 
49  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
50  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
51  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
52  */
53
54 #include "k5-int.h"
55 #include "krb5/adm.h"
56 #include <stdio.h>
57 #include <errno.h>
58
59 static int
60 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
76 cleanup_key_data(context, count, data)
77     krb5_context          context;
78     int                   count;
79     krb5_key_data       * data;
80 {
81     int i, j;
82
83     for (i = 0; i < count; i++) {
84         for (j = 0; j < data[i].key_data_ver; j++) {
85             if (data[i].key_data_length[j]) {
86                 free(data[i].key_data_contents[j]);
87             }
88         }
89     }
90     free(data);
91 }
92
93 static krb5_error_code
94 add_key_rnd(context, master_key, ks_tuple, ks_tuple_count, db_entry, kvno)
95     krb5_context          context;
96     krb5_keyblock       * master_key;
97     krb5_key_salt_tuple * ks_tuple;
98     int                   ks_tuple_count;
99     krb5_db_entry       * db_entry;
100     int                   kvno;
101 {
102     krb5_principal        krbtgt_princ;
103     krb5_keyblock         key;
104     krb5_db_entry         krbtgt_entry;
105     krb5_key_data       * krbtgt_kdata;
106     krb5_boolean          more;
107     int                   max_kvno, one, i, j;
108     krb5_error_code       retval;
109
110     retval = krb5_build_principal_ext(context, &krbtgt_princ,
111                                       db_entry->princ->realm.length,
112                                       db_entry->princ->realm.data,
113                                       KRB5_TGS_NAME_SIZE,
114                                       KRB5_TGS_NAME,
115                                       db_entry->princ->realm.length,
116                                       db_entry->princ->realm.data,
117                                       0);
118     if (retval)
119         return retval;
120
121     /* Get tgt from database */
122     retval = krb5_db_get_principal(context, krbtgt_princ, &krbtgt_entry,
123                                    &one, &more);
124     krb5_free_principal(context, krbtgt_princ); /* don't need it anymore */
125     if (retval)
126         return(retval);
127     if ((one > 1) || (more)) {
128         krb5_db_free_principal(context, &krbtgt_entry, one);
129         return KRB5KDC_ERR_PRINCIPAL_NOT_UNIQUE;
130     }
131     if (!one) 
132         return KRB5_KDB_NOENTRY;
133
134     /* Get max kvno */
135     for (max_kvno = j = 0; j < krbtgt_entry.n_key_data; j++) {
136          if (max_kvno < krbtgt_entry.key_data[j].key_data_kvno) {
137              max_kvno = krbtgt_entry.key_data[j].key_data_kvno;
138         }
139     }
140
141     for (i = 0; i < ks_tuple_count; i++) {
142         krb5_boolean similar;
143
144         similar = 0;
145
146         /*
147          * We could use krb5_keysalt_iterate to replace this loop, or use
148          * krb5_keysalt_is_present for the loop below, but we want to avoid
149          * circular library dependencies.
150          */
151         for (j = 0; j < i; j++) {
152             if ((retval = krb5_c_enctype_compare(context,
153                                                  ks_tuple[i].ks_enctype,
154                                                  ks_tuple[j].ks_enctype,
155                                                  &similar)))
156                 return(retval);
157
158             if (similar)
159                 break;
160         }
161
162         if (similar)
163             continue;
164
165         if (retval = krb5_dbe_create_key_data(context, db_entry)) 
166             goto add_key_rnd_err;
167
168         /* there used to be code here to extract the old key, and derive
169            a new key from it.  Now that there's a unified prng, that isn't
170            necessary. */
171
172         /* make new key */
173         if ((retval = krb5_c_make_random_key(context, ks_tuple[i].ks_enctype,
174                                              &key)))
175             goto add_key_rnd_err;
176
177         retval = krb5_dbekd_encrypt_key_data(context, master_key, 
178                                              &key, NULL, kvno, 
179                                              &db_entry->key_data[db_entry->n_key_data-1]);
180
181         krb5_free_keyblock_contents(context, &key);
182
183         if (retval)
184             goto add_key_rnd_err;
185     }
186
187 add_key_rnd_err:
188     krb5_db_free_principal(context, &krbtgt_entry, one);
189
190     return(retval);
191 }
192
193 /*
194  * Change random key for a krb5_db_entry 
195  * Assumes the max kvno
196  *
197  * As a side effect all old keys are nuked if keepold is false.
198  */
199 krb5_error_code
200 krb5_dbe_crk(context, master_key, ks_tuple, ks_tuple_count, keepold, db_entry)
201     krb5_context          context;
202     krb5_keyblock       * master_key;
203     krb5_key_salt_tuple * ks_tuple;
204     int                   ks_tuple_count;
205     krb5_boolean          keepold;
206     krb5_db_entry       * db_entry;
207 {
208     int                   key_data_count;
209     int                   n_new_key_data;
210     krb5_key_data       * key_data;
211     krb5_error_code       retval;
212     int                   kvno;
213     int                   i;
214
215     /* First save the old keydata */
216     kvno = get_key_data_kvno(context, db_entry->n_key_data, db_entry->key_data);
217     key_data_count = db_entry->n_key_data;
218     key_data = db_entry->key_data;
219     db_entry->key_data = NULL;
220     db_entry->n_key_data = 0;
221
222     /* increment the kvno */
223     kvno++;
224
225     retval = add_key_rnd(context, master_key, ks_tuple,
226                          ks_tuple_count, db_entry, kvno);
227     if (retval) {
228         cleanup_key_data(context, db_entry->n_key_data, db_entry->key_data);
229         db_entry->n_key_data = key_data_count;
230         db_entry->key_data = key_data;
231     } else if (keepold) {
232         n_new_key_data = db_entry->n_key_data;
233         for (i = 0; i < key_data_count; i++) {
234             retval = krb5_dbe_create_key_data(context, db_entry);
235             if (retval) {
236                 cleanup_key_data(context, db_entry->n_key_data,
237                                  db_entry->key_data);
238                 break;
239             }
240             db_entry->key_data[i+n_new_key_data] = key_data[i];
241             memset(&key_data[i], 0, sizeof(krb5_key_data));
242         }
243     } else {
244         cleanup_key_data(context, key_data_count, key_data);
245     }
246     return(retval);
247 }
248
249 /*
250  * Add random key for a krb5_db_entry 
251  * Assumes the max kvno
252  *
253  * As a side effect all old keys older than the max kvno are nuked.
254  */
255 krb5_error_code
256 krb5_dbe_ark(context, master_key, ks_tuple, ks_tuple_count, db_entry)
257     krb5_context          context;
258     krb5_keyblock       * master_key;
259     krb5_key_salt_tuple * ks_tuple;
260     int                   ks_tuple_count;
261     krb5_db_entry       * db_entry;
262 {
263     int                   key_data_count;
264     krb5_key_data       * key_data;
265     krb5_error_code       retval;
266     int                   kvno;
267     int                   i;
268
269     /* First save the old keydata */
270     kvno = get_key_data_kvno(context, db_entry->n_key_data, db_entry->key_data);
271     key_data_count = db_entry->n_key_data;
272     key_data = db_entry->key_data;
273     db_entry->key_data = NULL;
274     db_entry->n_key_data = 0;
275
276     /* increment the kvno */
277     kvno++;
278
279     if (retval = add_key_rnd(context, master_key, ks_tuple, 
280                              ks_tuple_count, db_entry, kvno)) {
281         cleanup_key_data(context, db_entry->n_key_data, db_entry->key_data);
282         db_entry->n_key_data = key_data_count;
283         db_entry->key_data = key_data;
284     } else {
285         /* Copy keys with key_data_kvno == kvno - 1 ( = old kvno ) */
286         for (i = 0; i < key_data_count; i++) {
287             if (key_data[i].key_data_kvno == (kvno - 1)) {
288                 if (retval = krb5_dbe_create_key_data(context, db_entry)) {
289                     cleanup_key_data(context, db_entry->n_key_data,
290                                      db_entry->key_data);
291                     break;
292                 }
293                 /* We should decrypt/re-encrypt the data to use the same mkvno*/
294                 db_entry->key_data[db_entry->n_key_data - 1] = key_data[i];
295                 memset(&key_data[i], 0, sizeof(krb5_key_data));
296             }
297         }
298         cleanup_key_data(context, key_data_count, key_data);
299     }
300     return(retval);
301 }
302
303 /*
304  * Add key_data for a krb5_db_entry 
305  * If passwd is NULL the assumes that the caller wants a random password.
306  */
307 static krb5_error_code
308 add_key_pwd(context, master_key, ks_tuple, ks_tuple_count, passwd, 
309             db_entry, kvno)
310     krb5_context          context;
311     krb5_keyblock       * master_key;
312     krb5_key_salt_tuple * ks_tuple;
313     int                   ks_tuple_count;
314     char                * passwd;
315     krb5_db_entry       * db_entry;
316     int                   kvno;
317 {
318     krb5_error_code       retval;
319     krb5_keysalt          key_salt;
320     krb5_keyblock         key;
321     krb5_data             pwd;
322     krb5_boolean          found;
323     int                   i, j;
324
325     retval = 0;
326
327     for (i = 0; i < ks_tuple_count; i++) {
328         krb5_boolean similar;
329
330         similar = 0;
331
332         /*
333          * We could use krb5_keysalt_iterate to replace this loop, or use
334          * krb5_keysalt_is_present for the loop below, but we want to avoid
335          * circular library dependencies.
336          */
337         for (j = 0; j < i; j++) {
338             if ((retval = krb5_c_enctype_compare(context,
339                                                  ks_tuple[i].ks_enctype,
340                                                  ks_tuple[j].ks_enctype,
341                                                  &similar)))
342                 return(retval);
343
344             if (similar &&
345                 (ks_tuple[j].ks_salttype == ks_tuple[i].ks_salttype))
346                 break;
347         }
348
349         if (j < i)
350             continue;
351
352         if (retval = krb5_dbe_create_key_data(context, db_entry)) 
353             return(retval);
354
355         /* Convert password string to key using appropriate salt */
356         switch (key_salt.type = ks_tuple[i].ks_salttype) {
357         case KRB5_KDB_SALTTYPE_ONLYREALM: {
358             krb5_data * saltdata;
359             if (retval = krb5_copy_data(context, krb5_princ_realm(context,
360                                         db_entry->princ), &saltdata))
361                 return(retval);
362
363             key_salt.data = *saltdata;
364             krb5_xfree(saltdata);
365         }
366                 break;
367         case KRB5_KDB_SALTTYPE_NOREALM:
368             if (retval=krb5_principal2salt_norealm(context, db_entry->princ,
369                                                          &key_salt.data)) 
370                 return(retval);
371             break;
372         case KRB5_KDB_SALTTYPE_NORMAL:
373             if (retval = krb5_principal2salt(context, db_entry->princ,
374                                                  &key_salt.data)) 
375                 return(retval);
376             break;
377         case KRB5_KDB_SALTTYPE_V4:
378             key_salt.data.length = 0;
379             key_salt.data.data = 0;
380             break;
381         case KRB5_KDB_SALTTYPE_AFS3: {
382 #if 0
383             krb5_data * saltdata;
384             if (retval = krb5_copy_data(context, krb5_princ_realm(context,
385                                         db_entry->princ), &saltdata))
386                 return(retval);
387
388             key_salt.data = *saltdata;
389             key_salt.data.length = -1; /*length actually used below...*/
390             krb5_xfree(saltdata);
391 #else
392             /* Why do we do this? Well, the afs_mit_string_to_key needs to
393                use strlen, and the realm is not NULL terminated.... */
394             int slen = (*krb5_princ_realm(context,db_entry->princ)).length;
395             if(!(key_salt.data.data = (char *) malloc(slen+1)))
396                 return ENOMEM;
397             key_salt.data.data[slen] = 0;
398             memcpy((char *)key_salt.data.data,
399                    (char *)(*krb5_princ_realm(context,db_entry->princ)).data,
400                    slen);
401             key_salt.data.length = -1; /*length actually used below...*/
402 #endif
403
404         }
405                 break;
406         default:
407             return(KRB5_KDB_BAD_SALTTYPE);
408         }
409
410         pwd.data = passwd;
411         pwd.length = strlen(passwd);
412
413         if ((retval = krb5_c_string_to_key(context, ks_tuple[i].ks_enctype,
414                                            &pwd, &key_salt.data, &key))) {
415              if (key_salt.data.data)
416                   free(key_salt.data.data);
417              return(retval);
418         }
419
420         if (key_salt.data.length == -1)
421             key_salt.data.length = 
422               krb5_princ_realm(context, db_entry->princ)->length;
423
424         if (retval = krb5_dbekd_encrypt_key_data(context, master_key, &key,
425                      (const krb5_keysalt *)&key_salt,
426                      kvno, &db_entry->key_data[db_entry->n_key_data-1])) {
427             if (key_salt.data.data)
428                  free(key_salt.data.data);
429             krb5_xfree(key.contents);
430             return(retval);
431         }
432         if (key_salt.data.data)
433              free(key_salt.data.data);
434         krb5_xfree(key.contents);
435     }
436     return(retval);
437 }
438
439 /*
440  * Change password for a krb5_db_entry 
441  * Assumes the max kvno
442  *
443  * As a side effect all old keys are nuked if keepold is false.
444  */
445 krb5_error_code
446 krb5_dbe_cpw(context, master_key, ks_tuple, ks_tuple_count, passwd,
447              new_kvno, keepold, db_entry)
448     krb5_context          context;
449     krb5_keyblock       * master_key;
450     krb5_key_salt_tuple * ks_tuple;
451     int                   ks_tuple_count;
452     char                * passwd;
453     int                   new_kvno;
454     krb5_boolean          keepold;
455     krb5_db_entry       * db_entry;
456 {
457     int                   key_data_count;
458     int                   n_new_key_data;
459     krb5_key_data       * key_data;
460     krb5_error_code       retval;
461     int                   old_kvno;
462     int                   i;
463
464     /* First save the old keydata */
465     old_kvno = get_key_data_kvno(context, db_entry->n_key_data,
466                                  db_entry->key_data);
467     key_data_count = db_entry->n_key_data;
468     key_data = db_entry->key_data;
469     db_entry->key_data = NULL;
470     db_entry->n_key_data = 0;
471
472     /* increment the kvno.  if the requested kvno is too small, 
473        increment the old kvno */
474     if (new_kvno < old_kvno+1)
475        new_kvno = old_kvno+1;
476
477     retval = add_key_pwd(context, master_key, ks_tuple, ks_tuple_count,
478                          passwd, db_entry, new_kvno);
479     if (retval) {
480         cleanup_key_data(context, db_entry->n_key_data, db_entry->key_data);
481         db_entry->n_key_data = key_data_count;
482         db_entry->key_data = key_data;
483     } else if (keepold) {
484         n_new_key_data = db_entry->n_key_data;
485         for (i = 0; i < key_data_count; i++) {
486             retval = krb5_dbe_create_key_data(context, db_entry);
487             if (retval) {
488                 cleanup_key_data(context, db_entry->n_key_data,
489                                  db_entry->key_data);
490                 break;
491             }
492             db_entry->key_data[i+n_new_key_data] = key_data[i];
493             memset(&key_data[i], 0, sizeof(krb5_key_data));
494         }
495     } else {
496         cleanup_key_data(context, key_data_count, key_data);
497     }
498     return(retval);
499 }
500
501 /*
502  * Add password for a krb5_db_entry 
503  * Assumes the max kvno
504  *
505  * As a side effect all old keys older than the max kvno are nuked.
506  */
507 krb5_error_code
508 krb5_dbe_apw(context, master_key, ks_tuple, ks_tuple_count, passwd, db_entry)
509     krb5_context          context;
510     krb5_keyblock       * master_key;
511     krb5_key_salt_tuple * ks_tuple;
512     int                   ks_tuple_count;
513     char                * passwd;
514     krb5_db_entry       * db_entry;
515 {
516     int                   key_data_count;
517     krb5_key_data       * key_data;
518     krb5_error_code       retval;
519     int                   old_kvno, new_kvno;
520     int                   i;
521
522     /* First save the old keydata */
523     old_kvno = get_key_data_kvno(context, db_entry->n_key_data,
524                                  db_entry->key_data);
525     key_data_count = db_entry->n_key_data;
526     key_data = db_entry->key_data;
527     db_entry->key_data = NULL;
528     db_entry->n_key_data = 0;
529
530     /* increment the kvno */
531     new_kvno = old_kvno+1;
532
533     if (retval = add_key_pwd(context, master_key, ks_tuple, ks_tuple_count,
534                              passwd, db_entry, new_kvno)) {
535         cleanup_key_data(context, db_entry->n_key_data, db_entry->key_data);
536         db_entry->n_key_data = key_data_count;
537         db_entry->key_data = key_data;
538     } else {
539         /* Copy keys with key_data_kvno == old_kvno */
540         for (i = 0; i < key_data_count; i++) {
541             if (key_data[i].key_data_kvno == old_kvno) {
542                 if (retval = krb5_dbe_create_key_data(context, db_entry)) {
543                     cleanup_key_data(context, db_entry->n_key_data,
544                                      db_entry->key_data);
545                     break;
546                 }
547                 /* We should decrypt/re-encrypt the data to use the same mkvno*/
548                 db_entry->key_data[db_entry->n_key_data - 1] = key_data[i];
549                 memset(&key_data[i], 0, sizeof(krb5_key_data));
550             }
551         }
552         cleanup_key_data(context, key_data_count, key_data);
553     }
554     return(retval);
555 }