Novell Database Abstraction Layer merge.
[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 <stdio.h>
56 #include <errno.h>
57
58 static int
59 get_key_data_kvno(context, count, data)
60     krb5_context          context;
61     int                   count;
62     krb5_key_data       * data;
63 {
64     int i, kvno;
65     /* Find last key version number */
66     for (kvno = i = 0; i < count; i++) {
67         if (kvno < data[i].key_data_kvno) {
68             kvno = data[i].key_data_kvno;
69         }
70     }
71     return(kvno);
72 }
73
74 static void
75 cleanup_key_data(context, count, data)
76     krb5_context          context;
77     int                   count;
78     krb5_key_data       * data;
79 {
80     int i, j;
81
82     /* If data is NULL, count is always 0 */
83     if (data == NULL) return;
84
85     for (i = 0; i < count; i++) {
86         for (j = 0; j < data[i].key_data_ver; j++) {
87             if (data[i].key_data_length[j]) {
88                 krb5_db_free(context, data[i].key_data_contents[j]);
89             }
90         }
91     }
92     krb5_db_free(context, data);
93 }
94
95 static krb5_error_code
96 add_key_rnd(context, master_key, ks_tuple, ks_tuple_count, db_entry, kvno)
97     krb5_context          context;
98     krb5_keyblock       * master_key;
99     krb5_key_salt_tuple * ks_tuple;
100     int                   ks_tuple_count;
101     krb5_db_entry       * db_entry;
102     int                   kvno;
103 {
104     krb5_principal        krbtgt_princ;
105     krb5_keyblock         key;
106     krb5_db_entry         krbtgt_entry;
107     krb5_boolean          more;
108     int                   max_kvno, one, i, j, k;
109     krb5_error_code       retval;
110     krb5_key_data         tmp_key_data;
111     krb5_key_data        *tptr;
112
113     memset( &tmp_key_data, 0, sizeof(tmp_key_data));
114
115
116     retval = krb5_build_principal_ext(context, &krbtgt_princ,
117                                       db_entry->princ->realm.length,
118                                       db_entry->princ->realm.data,
119                                       KRB5_TGS_NAME_SIZE,
120                                       KRB5_TGS_NAME,
121                                       db_entry->princ->realm.length,
122                                       db_entry->princ->realm.data,
123                                       0);
124     if (retval)
125         return retval;
126
127     /* Get tgt from database */
128     retval = krb5_db_get_principal(context, krbtgt_princ, &krbtgt_entry,
129                                    &one, &more);
130     krb5_free_principal(context, krbtgt_princ); /* don't need it anymore */
131     if (retval)
132         return(retval);
133     if ((one > 1) || (more)) {
134         krb5_db_free_principal(context, &krbtgt_entry, one);
135         return KRB5KDC_ERR_PRINCIPAL_NOT_UNIQUE;
136     }
137     if (!one) 
138         return KRB5_KDB_NOENTRY;
139
140     /* Get max kvno */
141     for (max_kvno = j = 0; j < krbtgt_entry.n_key_data; j++) {
142          if (max_kvno < krbtgt_entry.key_data[j].key_data_kvno) {
143              max_kvno = krbtgt_entry.key_data[j].key_data_kvno;
144         }
145     }
146
147     for (i = 0; i < ks_tuple_count; i++) {
148         krb5_boolean similar;
149
150         similar = 0;
151
152         /*
153          * We could use krb5_keysalt_iterate to replace this loop, or use
154          * krb5_keysalt_is_present for the loop below, but we want to avoid
155          * circular library dependencies.
156          */
157         for (j = 0; j < i; j++) {
158             if ((retval = krb5_c_enctype_compare(context,
159                                                  ks_tuple[i].ks_enctype,
160                                                  ks_tuple[j].ks_enctype,
161                                                  &similar)))
162                 return(retval);
163
164             if (similar)
165                 break;
166         }
167
168         if (similar)
169             continue;
170
171         if ((retval = krb5_dbe_create_key_data(context, db_entry))) 
172             goto add_key_rnd_err;
173
174         /* there used to be code here to extract the old key, and derive
175            a new key from it.  Now that there's a unified prng, that isn't
176            necessary. */
177
178         /* make new key */
179         if ((retval = krb5_c_make_random_key(context, ks_tuple[i].ks_enctype,
180                                              &key)))
181             goto add_key_rnd_err;
182
183
184         /* db library will free this. Since, its a so, it could actually be using different memory management
185            function. So, its better if the memory is allocated by the db's malloc. So, a temporary memory is used
186            here which will later be copied to the db_entry */
187         retval = krb5_dbekd_encrypt_key_data(context, master_key, 
188                                              &key, NULL, kvno, 
189                                              &tmp_key_data); 
190
191         krb5_free_keyblock_contents(context, &key);
192         if( retval )
193             goto add_key_rnd_err;
194
195         tptr = &db_entry->key_data[db_entry->n_key_data-1];
196
197         tptr->key_data_ver = tmp_key_data.key_data_ver;
198         tptr->key_data_kvno = tmp_key_data.key_data_kvno;
199
200         for( k = 0; k < tmp_key_data.key_data_ver; k++ )
201         {
202             tptr->key_data_type[k] = tmp_key_data.key_data_type[k];
203             tptr->key_data_length[k] = tmp_key_data.key_data_length[k];
204             if( tmp_key_data.key_data_contents[k] )
205             {
206                 tptr->key_data_contents[k] = krb5_db_alloc(context, NULL, tmp_key_data.key_data_length[k]);
207                 if( tptr->key_data_contents[k] == NULL )
208                 {
209                     cleanup_key_data(context, db_entry->n_key_data, db_entry->key_data);
210                     db_entry->key_data = NULL;
211                     db_entry->n_key_data = 0;
212                     retval = ENOMEM;
213                     goto add_key_rnd_err;
214                 }
215                 memcpy( tptr->key_data_contents[k], tmp_key_data.key_data_contents[k], tmp_key_data.key_data_length[k]);
216
217                 memset( tmp_key_data.key_data_contents[k], 0, tmp_key_data.key_data_length[k]);
218                 free( tmp_key_data.key_data_contents[k] );
219                 tmp_key_data.key_data_contents[k] = NULL;
220             }
221         }
222
223     }
224
225 add_key_rnd_err:
226     krb5_db_free_principal(context, &krbtgt_entry, one);
227
228     for( i = 0; i < tmp_key_data.key_data_ver; i++ )
229     {
230         if( tmp_key_data.key_data_contents[i] )
231         {
232             memset( tmp_key_data.key_data_contents[i], 0, tmp_key_data.key_data_length[i]);
233             free( tmp_key_data.key_data_contents[i] );
234         }
235     }
236     return(retval);
237 }
238
239 /*
240  * Change random key for a krb5_db_entry 
241  * Assumes the max kvno
242  *
243  * As a side effect all old keys are nuked if keepold is false.
244  */
245 krb5_error_code
246 krb5_dbe_crk(context, master_key, ks_tuple, ks_tuple_count, keepold, db_entry)
247     krb5_context          context;
248     krb5_keyblock       * master_key;
249     krb5_key_salt_tuple * ks_tuple;
250     int                   ks_tuple_count;
251     krb5_boolean          keepold;
252     krb5_db_entry       * db_entry;
253 {
254     int                   key_data_count;
255     int                   n_new_key_data;
256     krb5_key_data       * key_data;
257     krb5_error_code       retval;
258     int                   kvno;
259     int                   i;
260
261     /* First save the old keydata */
262     kvno = get_key_data_kvno(context, db_entry->n_key_data, db_entry->key_data);
263     key_data_count = db_entry->n_key_data;
264     key_data = db_entry->key_data;
265     db_entry->key_data = NULL;
266     db_entry->n_key_data = 0;
267
268     /* increment the kvno */
269     kvno++;
270
271     retval = add_key_rnd(context, master_key, ks_tuple,
272                          ks_tuple_count, db_entry, kvno);
273     if (retval) {
274         cleanup_key_data(context, db_entry->n_key_data, db_entry->key_data);
275         db_entry->n_key_data = key_data_count;
276         db_entry->key_data = key_data;
277     } else if (keepold) {
278         n_new_key_data = db_entry->n_key_data;
279         for (i = 0; i < key_data_count; i++) {
280             retval = krb5_dbe_create_key_data(context, db_entry);
281             if (retval) {
282                 cleanup_key_data(context, db_entry->n_key_data,
283                                  db_entry->key_data);
284                 break;
285             }
286             db_entry->key_data[i+n_new_key_data] = key_data[i];
287             memset(&key_data[i], 0, sizeof(krb5_key_data));
288         }
289         krb5_db_free(context, key_data); /* we moved the cotents to new memory. But, the original block which contained the data */
290     } else {
291         cleanup_key_data(context, key_data_count, key_data);
292     }
293     return(retval);
294 }
295
296 /*
297  * Add random key for a krb5_db_entry 
298  * Assumes the max kvno
299  *
300  * As a side effect all old keys older than the max kvno are nuked.
301  */
302 krb5_error_code
303 krb5_dbe_ark(context, master_key, ks_tuple, ks_tuple_count, db_entry)
304     krb5_context          context;
305     krb5_keyblock       * master_key;
306     krb5_key_salt_tuple * ks_tuple;
307     int                   ks_tuple_count;
308     krb5_db_entry       * db_entry;
309 {
310     int                   key_data_count;
311     krb5_key_data       * key_data;
312     krb5_error_code       retval;
313     int                   kvno;
314     int                   i;
315
316     /* First save the old keydata */
317     kvno = get_key_data_kvno(context, db_entry->n_key_data, db_entry->key_data);
318     key_data_count = db_entry->n_key_data;
319     key_data = db_entry->key_data;
320     db_entry->key_data = NULL;
321     db_entry->n_key_data = 0;
322
323     /* increment the kvno */
324     kvno++;
325
326     if ((retval = add_key_rnd(context, master_key, ks_tuple, 
327                              ks_tuple_count, db_entry, kvno))) {
328         cleanup_key_data(context, db_entry->n_key_data, db_entry->key_data);
329         db_entry->n_key_data = key_data_count;
330         db_entry->key_data = key_data;
331     } else {
332         /* Copy keys with key_data_kvno == kvno - 1 ( = old kvno ) */
333         for (i = 0; i < key_data_count; i++) {
334             if (key_data[i].key_data_kvno == (kvno - 1)) {
335                 if ((retval = krb5_dbe_create_key_data(context, db_entry))) {
336                     cleanup_key_data(context, db_entry->n_key_data,
337                                      db_entry->key_data);
338                     break;
339                 }
340                 /* We should decrypt/re-encrypt the data to use the same mkvno*/
341                 db_entry->key_data[db_entry->n_key_data - 1] = key_data[i];
342                 memset(&key_data[i], 0, sizeof(krb5_key_data));
343             }
344         }
345         cleanup_key_data(context, key_data_count, key_data);
346     }
347     return(retval);
348 }
349
350 /*
351  * Add key_data for a krb5_db_entry 
352  * If passwd is NULL the assumes that the caller wants a random password.
353  */
354 static krb5_error_code
355 add_key_pwd(context, master_key, ks_tuple, ks_tuple_count, passwd, 
356             db_entry, kvno)
357     krb5_context          context;
358     krb5_keyblock       * master_key;
359     krb5_key_salt_tuple * ks_tuple;
360     int                   ks_tuple_count;
361     char                * passwd;
362     krb5_db_entry       * db_entry;
363     int                   kvno;
364 {
365     krb5_error_code       retval;
366     krb5_keysalt          key_salt;
367     krb5_keyblock         key;
368     krb5_data             pwd;
369     int                   i, j, k;
370     krb5_key_data         tmp_key_data;
371     krb5_key_data        *tptr;
372
373     memset( &tmp_key_data, 0, sizeof(tmp_key_data));
374
375     retval = 0;
376
377     for (i = 0; i < ks_tuple_count; i++) {
378         krb5_boolean similar;
379
380         similar = 0;
381
382         /*
383          * We could use krb5_keysalt_iterate to replace this loop, or use
384          * krb5_keysalt_is_present for the loop below, but we want to avoid
385          * circular library dependencies.
386          */
387         for (j = 0; j < i; j++) {
388             if ((retval = krb5_c_enctype_compare(context,
389                                                  ks_tuple[i].ks_enctype,
390                                                  ks_tuple[j].ks_enctype,
391                                                  &similar)))
392                 return(retval);
393
394             if (similar &&
395                 (ks_tuple[j].ks_salttype == ks_tuple[i].ks_salttype))
396                 break;
397         }
398
399         if (j < i)
400             continue;
401
402         if ((retval = krb5_dbe_create_key_data(context, db_entry))) 
403             return(retval);
404
405         /* Convert password string to key using appropriate salt */
406         switch (key_salt.type = ks_tuple[i].ks_salttype) {
407         case KRB5_KDB_SALTTYPE_ONLYREALM: {
408             krb5_data * saltdata;
409             if ((retval = krb5_copy_data(context, krb5_princ_realm(context,
410                                               db_entry->princ), &saltdata)))
411                 return(retval);
412
413             key_salt.data = *saltdata;
414             krb5_xfree(saltdata);
415         }
416                 break;
417         case KRB5_KDB_SALTTYPE_NOREALM:
418             if ((retval=krb5_principal2salt_norealm(context, db_entry->princ,
419                                                     &key_salt.data))) 
420                 return(retval);
421             break;
422         case KRB5_KDB_SALTTYPE_NORMAL:
423             if ((retval = krb5_principal2salt(context, db_entry->princ,
424                                               &key_salt.data))) 
425                 return(retval);
426             break;
427         case KRB5_KDB_SALTTYPE_V4:
428             key_salt.data.length = 0;
429             key_salt.data.data = 0;
430             break;
431         case KRB5_KDB_SALTTYPE_AFS3: {
432 #if 0
433             krb5_data * saltdata;
434             if (retval = krb5_copy_data(context, krb5_princ_realm(context,
435                                         db_entry->princ), &saltdata))
436                 return(retval);
437
438             key_salt.data = *saltdata;
439             key_salt.data.length = SALT_TYPE_AFS_LENGTH; /*length actually used below...*/
440             krb5_xfree(saltdata);
441 #else
442             /* Why do we do this? Well, the afs_mit_string_to_key needs to
443                use strlen, and the realm is not NULL terminated.... */
444             unsigned int slen = 
445                 (*krb5_princ_realm(context,db_entry->princ)).length;
446             if(!(key_salt.data.data = (char *) malloc(slen+1)))
447                 return ENOMEM;
448             key_salt.data.data[slen] = 0;
449             memcpy((char *)key_salt.data.data,
450                    (char *)(*krb5_princ_realm(context,db_entry->princ)).data,
451                    slen);
452             key_salt.data.length = SALT_TYPE_AFS_LENGTH; /*length actually used below...*/
453 #endif
454
455         }
456                 break;
457         default:
458             return(KRB5_KDB_BAD_SALTTYPE);
459         }
460
461         pwd.data = passwd;
462         pwd.length = strlen(passwd);
463
464         /* AFS string to key will happen here */
465         if ((retval = krb5_c_string_to_key(context, ks_tuple[i].ks_enctype,
466                                            &pwd, &key_salt.data, &key))) {
467              if (key_salt.data.data)
468                   free(key_salt.data.data);
469              return(retval);
470         }
471
472         if (key_salt.data.length == SALT_TYPE_AFS_LENGTH)
473             key_salt.data.length = 
474               krb5_princ_realm(context, db_entry->princ)->length;
475
476         /* memory allocation to be done by db. So, use temporary block and later copy
477            it to the memory allocated by db */
478         retval = krb5_dbekd_encrypt_key_data(context, master_key, &key,
479                                              (const krb5_keysalt *)&key_salt,
480                                              kvno, &tmp_key_data);
481         if (key_salt.data.data)
482             free(key_salt.data.data);
483         krb5_xfree(key.contents);
484
485         if( retval )
486             return retval;
487
488         tptr = &db_entry->key_data[db_entry->n_key_data-1];
489
490         tptr->key_data_ver = tmp_key_data.key_data_ver;
491         tptr->key_data_kvno = tmp_key_data.key_data_kvno;
492
493         for( k = 0; k < tmp_key_data.key_data_ver; k++ )
494         {
495             tptr->key_data_type[k] = tmp_key_data.key_data_type[k];
496             tptr->key_data_length[k] = tmp_key_data.key_data_length[k];
497             if( tmp_key_data.key_data_contents[k] )
498             {
499                 tptr->key_data_contents[k] = krb5_db_alloc(context, NULL, tmp_key_data.key_data_length[k]);
500                 if( tptr->key_data_contents[k] == NULL )
501                 {
502                     cleanup_key_data(context, db_entry->n_key_data, db_entry->key_data);
503                     db_entry->key_data = NULL;
504                     db_entry->n_key_data = 0;
505                     retval = ENOMEM;
506                     goto add_key_pwd_err;
507                 }
508                 memcpy( tptr->key_data_contents[k], tmp_key_data.key_data_contents[k], tmp_key_data.key_data_length[k]);
509
510                 memset( tmp_key_data.key_data_contents[k], 0, tmp_key_data.key_data_length[k]);
511                 free( tmp_key_data.key_data_contents[k] );
512                 tmp_key_data.key_data_contents[k] = NULL;
513             }
514         }
515     }
516  add_key_pwd_err:
517     for( i = 0; i < tmp_key_data.key_data_ver; i++ )
518     {
519         if( tmp_key_data.key_data_contents[i] )
520         {
521             memset( tmp_key_data.key_data_contents[i], 0, tmp_key_data.key_data_length[i]);
522             free( tmp_key_data.key_data_contents[i] );
523         }
524     }
525
526     return(retval);
527 }
528
529 /*
530  * Change password for a krb5_db_entry 
531  * Assumes the max kvno
532  *
533  * As a side effect all old keys are nuked if keepold is false.
534  */
535 krb5_error_code
536 krb5_dbe_def_cpw(context, master_key, ks_tuple, ks_tuple_count, passwd,
537              new_kvno, keepold, db_entry)
538     krb5_context          context;
539     krb5_keyblock       * master_key;
540     krb5_key_salt_tuple * ks_tuple;
541     int                   ks_tuple_count;
542     char                * passwd;
543     int                   new_kvno;
544     krb5_boolean          keepold;
545     krb5_db_entry       * db_entry;
546 {
547     int                   key_data_count;
548     int                   n_new_key_data;
549     krb5_key_data       * key_data;
550     krb5_error_code       retval;
551     int                   old_kvno;
552     int                   i;
553
554     /* First save the old keydata */
555     old_kvno = get_key_data_kvno(context, db_entry->n_key_data,
556                                  db_entry->key_data);
557     key_data_count = db_entry->n_key_data;
558     key_data = db_entry->key_data;
559     db_entry->key_data = NULL;
560     db_entry->n_key_data = 0;
561
562     /* increment the kvno.  if the requested kvno is too small, 
563        increment the old kvno */
564     if (new_kvno < old_kvno+1)
565        new_kvno = old_kvno+1;
566
567     retval = add_key_pwd(context, master_key, ks_tuple, ks_tuple_count,
568                          passwd, db_entry, new_kvno);
569     if (retval) {
570         cleanup_key_data(context, db_entry->n_key_data, db_entry->key_data);
571         db_entry->n_key_data = key_data_count;
572         db_entry->key_data = key_data;
573     } else if (keepold) {
574         n_new_key_data = db_entry->n_key_data;
575         for (i = 0; i < key_data_count; i++) {
576             retval = krb5_dbe_create_key_data(context, db_entry);
577             if (retval) {
578                 cleanup_key_data(context, db_entry->n_key_data,
579                                  db_entry->key_data);
580                 break;
581             }
582             db_entry->key_data[i+n_new_key_data] = key_data[i];
583             memset(&key_data[i], 0, sizeof(krb5_key_data));
584         }
585         krb5_db_free( context, key_data );
586     } else {
587         cleanup_key_data(context, key_data_count, key_data);
588     }
589     return(retval);
590 }
591
592 /*
593  * Add password for a krb5_db_entry 
594  * Assumes the max kvno
595  *
596  * As a side effect all old keys older than the max kvno are nuked.
597  */
598 krb5_error_code
599 krb5_dbe_apw(context, master_key, ks_tuple, ks_tuple_count, passwd, db_entry)
600     krb5_context          context;
601     krb5_keyblock       * master_key;
602     krb5_key_salt_tuple * ks_tuple;
603     int                   ks_tuple_count;
604     char                * passwd;
605     krb5_db_entry       * db_entry;
606 {
607     int                   key_data_count;
608     krb5_key_data       * key_data;
609     krb5_error_code       retval;
610     int                   old_kvno, new_kvno;
611     int                   i;
612
613     /* First save the old keydata */
614     old_kvno = get_key_data_kvno(context, db_entry->n_key_data,
615                                  db_entry->key_data);
616     key_data_count = db_entry->n_key_data;
617     key_data = db_entry->key_data;
618     db_entry->key_data = NULL;
619     db_entry->n_key_data = 0;
620
621     /* increment the kvno */
622     new_kvno = old_kvno+1;
623
624     if ((retval = add_key_pwd(context, master_key, ks_tuple, ks_tuple_count,
625                              passwd, db_entry, new_kvno))) {
626         cleanup_key_data(context, db_entry->n_key_data, db_entry->key_data);
627         db_entry->n_key_data = key_data_count;
628         db_entry->key_data = key_data;
629     } else {
630         /* Copy keys with key_data_kvno == old_kvno */
631         for (i = 0; i < key_data_count; i++) {
632             if (key_data[i].key_data_kvno == old_kvno) {
633                 if ((retval = krb5_dbe_create_key_data(context, db_entry))) {
634                     cleanup_key_data(context, db_entry->n_key_data,
635                                      db_entry->key_data);
636                     break;
637                 }
638                 /* We should decrypt/re-encrypt the data to use the same mkvno*/
639                 db_entry->key_data[db_entry->n_key_data - 1] = key_data[i];
640                 memset(&key_data[i], 0, sizeof(krb5_key_data));
641             }
642         }
643         cleanup_key_data(context, key_data_count, key_data);
644     }
645     return(retval);
646 }
647
648