copyright notice updates from 1.1 branch
[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     krb5_key_data       * key_data;
210     krb5_error_code       retval;
211     int                   kvno;
212     int                   i;
213
214     /* First save the old keydata */
215     kvno = get_key_data_kvno(context, db_entry->n_key_data, db_entry->key_data);
216     key_data_count = db_entry->n_key_data;
217     key_data = db_entry->key_data;
218     db_entry->key_data = NULL;
219     db_entry->n_key_data = 0;
220
221     /* increment the kvno */
222     kvno++;
223
224     retval = add_key_rnd(context, master_key, ks_tuple,
225                          ks_tuple_count, db_entry, kvno);
226     if (retval) {
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 if (keepold) {
231         for (i = 0; i < key_data_count; i++) {
232             retval = krb5_dbe_create_key_data(context, db_entry);
233             if (retval) {
234                 cleanup_key_data(context, db_entry->n_key_data,
235                                  db_entry->key_data);
236                 break;
237             }
238         }
239     } else {
240         cleanup_key_data(context, key_data_count, key_data);
241     }
242     return(retval);
243 }
244
245 /*
246  * Add random key for a krb5_db_entry 
247  * Assumes the max kvno
248  *
249  * As a side effect all old keys older than the max kvno are nuked.
250  */
251 krb5_error_code
252 krb5_dbe_ark(context, master_key, ks_tuple, ks_tuple_count, db_entry)
253     krb5_context          context;
254     krb5_keyblock       * master_key;
255     krb5_key_salt_tuple * ks_tuple;
256     int                   ks_tuple_count;
257     krb5_db_entry       * db_entry;
258 {
259     int                   key_data_count;
260     krb5_key_data       * key_data;
261     krb5_error_code       retval;
262     int                   kvno;
263     int                   i;
264
265     /* First save the old keydata */
266     kvno = get_key_data_kvno(context, db_entry->n_key_data, db_entry->key_data);
267     key_data_count = db_entry->n_key_data;
268     key_data = db_entry->key_data;
269     db_entry->key_data = NULL;
270     db_entry->n_key_data = 0;
271
272     /* increment the kvno */
273     kvno++;
274
275     if (retval = add_key_rnd(context, master_key, ks_tuple, 
276                              ks_tuple_count, db_entry, kvno)) {
277         cleanup_key_data(context, db_entry->n_key_data, db_entry->key_data);
278         db_entry->n_key_data = key_data_count;
279         db_entry->key_data = key_data;
280     } else {
281         /* Copy keys with key_data_kvno == kvno - 1 ( = old kvno ) */
282         for (i = 0; i < key_data_count; i++) {
283             if (key_data[i].key_data_kvno == (kvno - 1)) {
284                 if (retval = krb5_dbe_create_key_data(context, db_entry)) {
285                     cleanup_key_data(context, db_entry->n_key_data,
286                                      db_entry->key_data);
287                     break;
288                 }
289                 /* We should decrypt/re-encrypt the data to use the same mkvno*/
290                 db_entry->key_data[db_entry->n_key_data - 1] = key_data[i];
291                 memset(&key_data[i], 0, sizeof(krb5_key_data));
292             }
293         }
294         cleanup_key_data(context, key_data_count, key_data);
295     }
296     return(retval);
297 }
298
299 /*
300  * Add key_data for a krb5_db_entry 
301  * If passwd is NULL the assumes that the caller wants a random password.
302  */
303 static krb5_error_code
304 add_key_pwd(context, master_key, ks_tuple, ks_tuple_count, passwd, 
305             db_entry, kvno)
306     krb5_context          context;
307     krb5_keyblock       * master_key;
308     krb5_key_salt_tuple * ks_tuple;
309     int                   ks_tuple_count;
310     char                * passwd;
311     krb5_db_entry       * db_entry;
312     int                   kvno;
313 {
314     krb5_error_code       retval;
315     krb5_keysalt          key_salt;
316     krb5_keyblock         key;
317     krb5_data             pwd;
318     krb5_boolean          found;
319     int                   i, j;
320
321     retval = 0;
322
323     for (i = 0; i < ks_tuple_count; i++) {
324         krb5_boolean similar;
325
326         similar = 0;
327
328         /*
329          * We could use krb5_keysalt_iterate to replace this loop, or use
330          * krb5_keysalt_is_present for the loop below, but we want to avoid
331          * circular library dependencies.
332          */
333         for (j = 0; j < i; j++) {
334             if ((retval = krb5_c_enctype_compare(context,
335                                                  ks_tuple[i].ks_enctype,
336                                                  ks_tuple[j].ks_enctype,
337                                                  &similar)))
338                 return(retval);
339
340             if (similar &&
341                 (ks_tuple[j].ks_salttype == ks_tuple[i].ks_salttype))
342                 break;
343         }
344
345         if (j < i)
346             continue;
347
348         if (retval = krb5_dbe_create_key_data(context, db_entry)) 
349             return(retval);
350
351         /* Convert password string to key using appropriate salt */
352         switch (key_salt.type = ks_tuple[i].ks_salttype) {
353         case KRB5_KDB_SALTTYPE_ONLYREALM: {
354             krb5_data * saltdata;
355             if (retval = krb5_copy_data(context, krb5_princ_realm(context,
356                                         db_entry->princ), &saltdata))
357                 return(retval);
358
359             key_salt.data = *saltdata;
360             krb5_xfree(saltdata);
361         }
362                 break;
363         case KRB5_KDB_SALTTYPE_NOREALM:
364             if (retval=krb5_principal2salt_norealm(context, db_entry->princ,
365                                                          &key_salt.data)) 
366                 return(retval);
367             break;
368         case KRB5_KDB_SALTTYPE_NORMAL:
369             if (retval = krb5_principal2salt(context, db_entry->princ,
370                                                  &key_salt.data)) 
371                 return(retval);
372             break;
373         case KRB5_KDB_SALTTYPE_V4:
374             key_salt.data.length = 0;
375             key_salt.data.data = 0;
376             break;
377         case KRB5_KDB_SALTTYPE_AFS3: {
378 #if 0
379             krb5_data * saltdata;
380             if (retval = krb5_copy_data(context, krb5_princ_realm(context,
381                                         db_entry->princ), &saltdata))
382                 return(retval);
383
384             key_salt.data = *saltdata;
385             key_salt.data.length = -1; /*length actually used below...*/
386             krb5_xfree(saltdata);
387 #else
388             /* Why do we do this? Well, the afs_mit_string_to_key needs to
389                use strlen, and the realm is not NULL terminated.... */
390             int slen = (*krb5_princ_realm(context,db_entry->princ)).length;
391             if(!(key_salt.data.data = (char *) malloc(slen+1)))
392                 return ENOMEM;
393             key_salt.data.data[slen] = 0;
394             memcpy((char *)key_salt.data.data,
395                    (char *)(*krb5_princ_realm(context,db_entry->princ)).data,
396                    slen);
397             key_salt.data.length = -1; /*length actually used below...*/
398 #endif
399
400         }
401                 break;
402         default:
403             return(KRB5_KDB_BAD_SALTTYPE);
404         }
405
406         pwd.data = passwd;
407         pwd.length = strlen(passwd);
408
409         if ((retval = krb5_c_string_to_key(context, ks_tuple[i].ks_enctype,
410                                            &pwd, &key_salt.data, &key))) {
411              if (key_salt.data.data)
412                   free(key_salt.data.data);
413              return(retval);
414         }
415
416         if (key_salt.data.length == -1)
417             key_salt.data.length = 
418               krb5_princ_realm(context, db_entry->princ)->length;
419
420         if (retval = krb5_dbekd_encrypt_key_data(context, master_key, &key,
421                      (const krb5_keysalt *)&key_salt,
422                      kvno, &db_entry->key_data[db_entry->n_key_data-1])) {
423             if (key_salt.data.data)
424                  free(key_salt.data.data);
425             krb5_xfree(key.contents);
426             return(retval);
427         }
428         if (key_salt.data.data)
429              free(key_salt.data.data);
430         krb5_xfree(key.contents);
431     }
432     return(retval);
433 }
434
435 /*
436  * Change password for a krb5_db_entry 
437  * Assumes the max kvno
438  *
439  * As a side effect all old keys are nuked if keepold is false.
440  */
441 krb5_error_code
442 krb5_dbe_cpw(context, master_key, ks_tuple, ks_tuple_count, passwd,
443              new_kvno, keepold, db_entry)
444     krb5_context          context;
445     krb5_keyblock       * master_key;
446     krb5_key_salt_tuple * ks_tuple;
447     int                   ks_tuple_count;
448     char                * passwd;
449     int                   new_kvno;
450     krb5_boolean          keepold;
451     krb5_db_entry       * db_entry;
452 {
453     int                   key_data_count;
454     krb5_key_data       * key_data;
455     krb5_error_code       retval;
456     int                   old_kvno;
457     int                   i;
458
459     /* First save the old keydata */
460     old_kvno = get_key_data_kvno(context, db_entry->n_key_data,
461                                  db_entry->key_data);
462     key_data_count = db_entry->n_key_data;
463     key_data = db_entry->key_data;
464     db_entry->key_data = NULL;
465     db_entry->n_key_data = 0;
466
467     /* increment the kvno.  if the requested kvno is too small, 
468        increment the old kvno */
469     if (new_kvno < old_kvno+1)
470        new_kvno = old_kvno+1;
471
472     retval = add_key_pwd(context, master_key, ks_tuple, ks_tuple_count,
473                          passwd, db_entry, new_kvno);
474     if (retval) {
475         cleanup_key_data(context, db_entry->n_key_data, db_entry->key_data);
476         db_entry->n_key_data = key_data_count;
477         db_entry->key_data = key_data;
478     } else if (keepold) {
479         for (i = 0; i < key_data_count; i++) {
480             retval = krb5_dbe_create_key_data(context, db_entry);
481             if (retval) {
482                 cleanup_key_data(context, db_entry->n_key_data,
483                                  db_entry->key_data);
484                 break;
485             }
486         }
487     } else {
488         cleanup_key_data(context, key_data_count, key_data);
489     }
490     return(retval);
491 }
492
493 /*
494  * Add password for a krb5_db_entry 
495  * Assumes the max kvno
496  *
497  * As a side effect all old keys older than the max kvno are nuked.
498  */
499 krb5_error_code
500 krb5_dbe_apw(context, master_key, ks_tuple, ks_tuple_count, passwd, db_entry)
501     krb5_context          context;
502     krb5_keyblock       * master_key;
503     krb5_key_salt_tuple * ks_tuple;
504     int                   ks_tuple_count;
505     char                * passwd;
506     krb5_db_entry       * db_entry;
507 {
508     int                   key_data_count;
509     krb5_key_data       * key_data;
510     krb5_error_code       retval;
511     int                   old_kvno, new_kvno;
512     int                   i;
513
514     /* First save the old keydata */
515     old_kvno = get_key_data_kvno(context, db_entry->n_key_data,
516                                  db_entry->key_data);
517     key_data_count = db_entry->n_key_data;
518     key_data = db_entry->key_data;
519     db_entry->key_data = NULL;
520     db_entry->n_key_data = 0;
521
522     /* increment the kvno */
523     new_kvno = old_kvno+1;
524
525     if (retval = add_key_pwd(context, master_key, ks_tuple, ks_tuple_count,
526                              passwd, db_entry, new_kvno)) {
527         cleanup_key_data(context, db_entry->n_key_data, db_entry->key_data);
528         db_entry->n_key_data = key_data_count;
529         db_entry->key_data = key_data;
530     } else {
531         /* Copy keys with key_data_kvno == old_kvno */
532         for (i = 0; i < key_data_count; i++) {
533             if (key_data[i].key_data_kvno == old_kvno) {
534                 if (retval = krb5_dbe_create_key_data(context, db_entry)) {
535                     cleanup_key_data(context, db_entry->n_key_data,
536                                      db_entry->key_data);
537                     break;
538                 }
539                 /* We should decrypt/re-encrypt the data to use the same mkvno*/
540                 db_entry->key_data[db_entry->n_key_data - 1] = key_data[i];
541                 memset(&key_data[i], 0, sizeof(krb5_key_data));
542             }
543         }
544         cleanup_key_data(context, key_data_count, key_data);
545     }
546     return(retval);
547 }