* fetch_mkey.c: Signed/unsigned int cleanup
[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_boolean          more;
106     int                   max_kvno, one, i, j;
107     krb5_error_code       retval;
108
109     retval = krb5_build_principal_ext(context, &krbtgt_princ,
110                                       db_entry->princ->realm.length,
111                                       db_entry->princ->realm.data,
112                                       KRB5_TGS_NAME_SIZE,
113                                       KRB5_TGS_NAME,
114                                       db_entry->princ->realm.length,
115                                       db_entry->princ->realm.data,
116                                       0);
117     if (retval)
118         return retval;
119
120     /* Get tgt from database */
121     retval = krb5_db_get_principal(context, krbtgt_princ, &krbtgt_entry,
122                                    &one, &more);
123     krb5_free_principal(context, krbtgt_princ); /* don't need it anymore */
124     if (retval)
125         return(retval);
126     if ((one > 1) || (more)) {
127         krb5_db_free_principal(context, &krbtgt_entry, one);
128         return KRB5KDC_ERR_PRINCIPAL_NOT_UNIQUE;
129     }
130     if (!one) 
131         return KRB5_KDB_NOENTRY;
132
133     /* Get max kvno */
134     for (max_kvno = j = 0; j < krbtgt_entry.n_key_data; j++) {
135          if (max_kvno < krbtgt_entry.key_data[j].key_data_kvno) {
136              max_kvno = krbtgt_entry.key_data[j].key_data_kvno;
137         }
138     }
139
140     for (i = 0; i < ks_tuple_count; i++) {
141         krb5_boolean similar;
142
143         similar = 0;
144
145         /*
146          * We could use krb5_keysalt_iterate to replace this loop, or use
147          * krb5_keysalt_is_present for the loop below, but we want to avoid
148          * circular library dependencies.
149          */
150         for (j = 0; j < i; j++) {
151             if ((retval = krb5_c_enctype_compare(context,
152                                                  ks_tuple[i].ks_enctype,
153                                                  ks_tuple[j].ks_enctype,
154                                                  &similar)))
155                 return(retval);
156
157             if (similar)
158                 break;
159         }
160
161         if (similar)
162             continue;
163
164         if ((retval = krb5_dbe_create_key_data(context, db_entry))) 
165             goto add_key_rnd_err;
166
167         /* there used to be code here to extract the old key, and derive
168            a new key from it.  Now that there's a unified prng, that isn't
169            necessary. */
170
171         /* make new key */
172         if ((retval = krb5_c_make_random_key(context, ks_tuple[i].ks_enctype,
173                                              &key)))
174             goto add_key_rnd_err;
175
176         retval = krb5_dbekd_encrypt_key_data(context, master_key, 
177                                              &key, NULL, kvno, 
178                                              &db_entry->key_data[db_entry->n_key_data-1]);
179
180         krb5_free_keyblock_contents(context, &key);
181
182         if (retval)
183             goto add_key_rnd_err;
184     }
185
186 add_key_rnd_err:
187     krb5_db_free_principal(context, &krbtgt_entry, one);
188
189     return(retval);
190 }
191
192 /*
193  * Change random key for a krb5_db_entry 
194  * Assumes the max kvno
195  *
196  * As a side effect all old keys are nuked if keepold is false.
197  */
198 krb5_error_code
199 krb5_dbe_crk(context, master_key, ks_tuple, ks_tuple_count, keepold, db_entry)
200     krb5_context          context;
201     krb5_keyblock       * master_key;
202     krb5_key_salt_tuple * ks_tuple;
203     int                   ks_tuple_count;
204     krb5_boolean          keepold;
205     krb5_db_entry       * db_entry;
206 {
207     int                   key_data_count;
208     int                   n_new_key_data;
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         n_new_key_data = db_entry->n_key_data;
232         for (i = 0; i < key_data_count; i++) {
233             retval = krb5_dbe_create_key_data(context, db_entry);
234             if (retval) {
235                 cleanup_key_data(context, db_entry->n_key_data,
236                                  db_entry->key_data);
237                 break;
238             }
239             db_entry->key_data[i+n_new_key_data] = key_data[i];
240             memset(&key_data[i], 0, sizeof(krb5_key_data));
241         }
242     } else {
243         cleanup_key_data(context, key_data_count, key_data);
244     }
245     return(retval);
246 }
247
248 /*
249  * Add random key for a krb5_db_entry 
250  * Assumes the max kvno
251  *
252  * As a side effect all old keys older than the max kvno are nuked.
253  */
254 krb5_error_code
255 krb5_dbe_ark(context, master_key, ks_tuple, ks_tuple_count, db_entry)
256     krb5_context          context;
257     krb5_keyblock       * master_key;
258     krb5_key_salt_tuple * ks_tuple;
259     int                   ks_tuple_count;
260     krb5_db_entry       * db_entry;
261 {
262     int                   key_data_count;
263     krb5_key_data       * key_data;
264     krb5_error_code       retval;
265     int                   kvno;
266     int                   i;
267
268     /* First save the old keydata */
269     kvno = get_key_data_kvno(context, db_entry->n_key_data, db_entry->key_data);
270     key_data_count = db_entry->n_key_data;
271     key_data = db_entry->key_data;
272     db_entry->key_data = NULL;
273     db_entry->n_key_data = 0;
274
275     /* increment the kvno */
276     kvno++;
277
278     if ((retval = add_key_rnd(context, master_key, ks_tuple, 
279                              ks_tuple_count, db_entry, kvno))) {
280         cleanup_key_data(context, db_entry->n_key_data, db_entry->key_data);
281         db_entry->n_key_data = key_data_count;
282         db_entry->key_data = key_data;
283     } else {
284         /* Copy keys with key_data_kvno == kvno - 1 ( = old kvno ) */
285         for (i = 0; i < key_data_count; i++) {
286             if (key_data[i].key_data_kvno == (kvno - 1)) {
287                 if ((retval = krb5_dbe_create_key_data(context, db_entry))) {
288                     cleanup_key_data(context, db_entry->n_key_data,
289                                      db_entry->key_data);
290                     break;
291                 }
292                 /* We should decrypt/re-encrypt the data to use the same mkvno*/
293                 db_entry->key_data[db_entry->n_key_data - 1] = key_data[i];
294                 memset(&key_data[i], 0, sizeof(krb5_key_data));
295             }
296         }
297         cleanup_key_data(context, key_data_count, key_data);
298     }
299     return(retval);
300 }
301
302 /*
303  * Add key_data for a krb5_db_entry 
304  * If passwd is NULL the assumes that the caller wants a random password.
305  */
306 static krb5_error_code
307 add_key_pwd(context, master_key, ks_tuple, ks_tuple_count, passwd, 
308             db_entry, kvno)
309     krb5_context          context;
310     krb5_keyblock       * master_key;
311     krb5_key_salt_tuple * ks_tuple;
312     int                   ks_tuple_count;
313     char                * passwd;
314     krb5_db_entry       * db_entry;
315     int                   kvno;
316 {
317     krb5_error_code       retval;
318     krb5_keysalt          key_salt;
319     krb5_keyblock         key;
320     krb5_data             pwd;
321     int                   i, j;
322
323     retval = 0;
324
325     for (i = 0; i < ks_tuple_count; i++) {
326         krb5_boolean similar;
327
328         similar = 0;
329
330         /*
331          * We could use krb5_keysalt_iterate to replace this loop, or use
332          * krb5_keysalt_is_present for the loop below, but we want to avoid
333          * circular library dependencies.
334          */
335         for (j = 0; j < i; j++) {
336             if ((retval = krb5_c_enctype_compare(context,
337                                                  ks_tuple[i].ks_enctype,
338                                                  ks_tuple[j].ks_enctype,
339                                                  &similar)))
340                 return(retval);
341
342             if (similar &&
343                 (ks_tuple[j].ks_salttype == ks_tuple[i].ks_salttype))
344                 break;
345         }
346
347         if (j < i)
348             continue;
349
350         if ((retval = krb5_dbe_create_key_data(context, db_entry))) 
351             return(retval);
352
353         /* Convert password string to key using appropriate salt */
354         switch (key_salt.type = ks_tuple[i].ks_salttype) {
355         case KRB5_KDB_SALTTYPE_ONLYREALM: {
356             krb5_data * saltdata;
357             if ((retval = krb5_copy_data(context, krb5_princ_realm(context,
358                                               db_entry->princ), &saltdata)))
359                 return(retval);
360
361             key_salt.data = *saltdata;
362             krb5_xfree(saltdata);
363         }
364                 break;
365         case KRB5_KDB_SALTTYPE_NOREALM:
366             if ((retval=krb5_principal2salt_norealm(context, db_entry->princ,
367                                                     &key_salt.data))) 
368                 return(retval);
369             break;
370         case KRB5_KDB_SALTTYPE_NORMAL:
371             if ((retval = krb5_principal2salt(context, db_entry->princ,
372                                               &key_salt.data))) 
373                 return(retval);
374             break;
375         case KRB5_KDB_SALTTYPE_V4:
376             key_salt.data.length = 0;
377             key_salt.data.data = 0;
378             break;
379         case KRB5_KDB_SALTTYPE_AFS3: {
380 #if 0
381             krb5_data * saltdata;
382             if (retval = krb5_copy_data(context, krb5_princ_realm(context,
383                                         db_entry->princ), &saltdata))
384                 return(retval);
385
386             key_salt.data = *saltdata;
387             key_salt.data.length = SALT_TYPE_AFS_LENGTH; /*length actually used below...*/
388             krb5_xfree(saltdata);
389 #else
390             /* Why do we do this? Well, the afs_mit_string_to_key needs to
391                use strlen, and the realm is not NULL terminated.... */
392             int slen = (*krb5_princ_realm(context,db_entry->princ)).length;
393             if(!(key_salt.data.data = (char *) malloc(slen+1)))
394                 return ENOMEM;
395             key_salt.data.data[slen] = 0;
396             memcpy((char *)key_salt.data.data,
397                    (char *)(*krb5_princ_realm(context,db_entry->princ)).data,
398                    slen);
399             key_salt.data.length = SALT_TYPE_AFS_LENGTH; /*length actually used below...*/
400 #endif
401
402         }
403                 break;
404         default:
405             return(KRB5_KDB_BAD_SALTTYPE);
406         }
407
408         pwd.data = passwd;
409         pwd.length = strlen(passwd);
410
411         /* AFS string to key will happen here */
412         if ((retval = krb5_c_string_to_key(context, ks_tuple[i].ks_enctype,
413                                            &pwd, &key_salt.data, &key))) {
414              if (key_salt.data.data)
415                   free(key_salt.data.data);
416              return(retval);
417         }
418
419         if (key_salt.data.length == SALT_TYPE_AFS_LENGTH)
420             key_salt.data.length = 
421               krb5_princ_realm(context, db_entry->princ)->length;
422
423         if ((retval = krb5_dbekd_encrypt_key_data(context, master_key, &key,
424                      (const krb5_keysalt *)&key_salt,
425                      kvno, &db_entry->key_data[db_entry->n_key_data-1]))) {
426             if (key_salt.data.data)
427                  free(key_salt.data.data);
428             krb5_xfree(key.contents);
429             return(retval);
430         }
431         if (key_salt.data.data)
432              free(key_salt.data.data);
433         krb5_xfree(key.contents);
434     }
435     return(retval);
436 }
437
438 /*
439  * Change password for a krb5_db_entry 
440  * Assumes the max kvno
441  *
442  * As a side effect all old keys are nuked if keepold is false.
443  */
444 krb5_error_code
445 krb5_dbe_cpw(context, master_key, ks_tuple, ks_tuple_count, passwd,
446              new_kvno, keepold, db_entry)
447     krb5_context          context;
448     krb5_keyblock       * master_key;
449     krb5_key_salt_tuple * ks_tuple;
450     int                   ks_tuple_count;
451     char                * passwd;
452     int                   new_kvno;
453     krb5_boolean          keepold;
454     krb5_db_entry       * db_entry;
455 {
456     int                   key_data_count;
457     int                   n_new_key_data;
458     krb5_key_data       * key_data;
459     krb5_error_code       retval;
460     int                   old_kvno;
461     int                   i;
462
463     /* First save the old keydata */
464     old_kvno = get_key_data_kvno(context, db_entry->n_key_data,
465                                  db_entry->key_data);
466     key_data_count = db_entry->n_key_data;
467     key_data = db_entry->key_data;
468     db_entry->key_data = NULL;
469     db_entry->n_key_data = 0;
470
471     /* increment the kvno.  if the requested kvno is too small, 
472        increment the old kvno */
473     if (new_kvno < old_kvno+1)
474        new_kvno = old_kvno+1;
475
476     retval = add_key_pwd(context, master_key, ks_tuple, ks_tuple_count,
477                          passwd, db_entry, new_kvno);
478     if (retval) {
479         cleanup_key_data(context, db_entry->n_key_data, db_entry->key_data);
480         db_entry->n_key_data = key_data_count;
481         db_entry->key_data = key_data;
482     } else if (keepold) {
483         n_new_key_data = db_entry->n_key_data;
484         for (i = 0; i < key_data_count; i++) {
485             retval = krb5_dbe_create_key_data(context, db_entry);
486             if (retval) {
487                 cleanup_key_data(context, db_entry->n_key_data,
488                                  db_entry->key_data);
489                 break;
490             }
491             db_entry->key_data[i+n_new_key_data] = key_data[i];
492             memset(&key_data[i], 0, sizeof(krb5_key_data));
493         }
494     } else {
495         cleanup_key_data(context, key_data_count, key_data);
496     }
497     return(retval);
498 }
499
500 /*
501  * Add password for a krb5_db_entry 
502  * Assumes the max kvno
503  *
504  * As a side effect all old keys older than the max kvno are nuked.
505  */
506 krb5_error_code
507 krb5_dbe_apw(context, master_key, ks_tuple, ks_tuple_count, passwd, db_entry)
508     krb5_context          context;
509     krb5_keyblock       * master_key;
510     krb5_key_salt_tuple * ks_tuple;
511     int                   ks_tuple_count;
512     char                * passwd;
513     krb5_db_entry       * db_entry;
514 {
515     int                   key_data_count;
516     krb5_key_data       * key_data;
517     krb5_error_code       retval;
518     int                   old_kvno, new_kvno;
519     int                   i;
520
521     /* First save the old keydata */
522     old_kvno = get_key_data_kvno(context, db_entry->n_key_data,
523                                  db_entry->key_data);
524     key_data_count = db_entry->n_key_data;
525     key_data = db_entry->key_data;
526     db_entry->key_data = NULL;
527     db_entry->n_key_data = 0;
528
529     /* increment the kvno */
530     new_kvno = old_kvno+1;
531
532     if ((retval = add_key_pwd(context, master_key, ks_tuple, ks_tuple_count,
533                              passwd, db_entry, new_kvno))) {
534         cleanup_key_data(context, db_entry->n_key_data, db_entry->key_data);
535         db_entry->n_key_data = key_data_count;
536         db_entry->key_data = key_data;
537     } else {
538         /* Copy keys with key_data_kvno == old_kvno */
539         for (i = 0; i < key_data_count; i++) {
540             if (key_data[i].key_data_kvno == old_kvno) {
541                 if ((retval = krb5_dbe_create_key_data(context, db_entry))) {
542                     cleanup_key_data(context, db_entry->n_key_data,
543                                      db_entry->key_data);
544                     break;
545                 }
546                 /* We should decrypt/re-encrypt the data to use the same mkvno*/
547                 db_entry->key_data[db_entry->n_key_data - 1] = key_data[i];
548                 memset(&key_data[i], 0, sizeof(krb5_key_data));
549             }
550         }
551         cleanup_key_data(context, key_data_count, key_data);
552     }
553     return(retval);
554 }