It's not an out-of-space error if malloc(0) returns 0.
[krb5.git] / src / lib / kadm5 / srv / svr_principal.c
1 /*
2  * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved
3  *
4  * $Header$
5  */
6
7 #if !defined(lint) && !defined(__CODECENTER__)
8 static char *rcsid = "$Header$";
9 #endif
10
11 #include        <sys/types.h>
12 #include        <sys/time.h>
13 #include        <kadm5/admin.h>
14 #include        "adb.h"
15 #include        "k5-int.h"
16 #include        <krb5/kdb.h>
17 #include        <stdio.h>
18 #include        <string.h>
19 #include        "server_internal.h"
20 #include        <stdarg.h>
21 #include        <stdlib.h>
22
23 extern  krb5_principal      master_princ;
24 extern  krb5_principal      hist_princ;
25 extern  krb5_encrypt_block  master_encblock;
26 extern  krb5_encrypt_block  hist_encblock;
27 extern  krb5_keyblock       master_keyblock;
28 extern  krb5_keyblock       hist_key;
29 extern  krb5_db_entry       master_db;
30 extern  krb5_db_entry       hist_db;
31 extern  krb5_kvno           hist_kvno;
32
33 static int decrypt_key_data(krb5_context context,
34                             int n_key_data, krb5_key_data *key_data,
35                             krb5_keyblock **keyblocks, int *n_keys);
36
37 /*
38  * XXX Functions that ought to be in libkrb5.a, but aren't.
39  */
40 int krb5_free_keyblock_contents(context, key)
41      krb5_context context;
42      krb5_keyblock *key;
43 {
44      memset(key->contents, 0, key->length);
45      krb5_xfree(key->contents);
46      return 0;
47 }
48
49 kadm5_ret_t krb5_copy_key_data_contents(context, from, to)
50    krb5_context context;
51    krb5_key_data *from, *to;
52 {
53      int i, idx;
54      
55      *to = *from;
56
57      idx = (from->key_data_ver == 1 ? 1 : 2);
58
59      for (i = 0; i < idx; i++) {
60        if ( from->key_data_length[i] ) {
61          to->key_data_contents[i] = malloc(from->key_data_length[i]);
62          if (to->key_data_contents[i] == NULL) {
63            for (i = 0; i < idx; i++) {
64              if (to->key_data_contents[i]) {
65                memset(to->key_data_contents[i], 0,
66                       to->key_data_length[i]);
67                free(to->key_data_contents[i]);
68              }
69            }
70            return ENOMEM;
71          }
72          memcpy(to->key_data_contents[i], from->key_data_contents[i],
73                 from->key_data_length[i]);
74        }
75      }
76      return 0;
77 }
78
79 static krb5_tl_data *dup_tl_data(krb5_tl_data *tl)
80 {
81      krb5_tl_data *n;
82
83      n = (krb5_tl_data *) malloc(sizeof(krb5_tl_data));
84      if (n == NULL)
85           return NULL;
86      n->tl_data_contents = malloc(tl->tl_data_length);
87      if (n->tl_data_contents == NULL) {
88           free(n);
89           return NULL;
90      }
91      memcpy(n->tl_data_contents, tl->tl_data_contents, tl->tl_data_length);
92      n->tl_data_type = tl->tl_data_type;
93      n->tl_data_length = tl->tl_data_length;
94      n->tl_data_next = NULL;
95      return n;
96 }
97
98 kadm5_ret_t
99 kadm5_create_principal(void *server_handle,
100                             kadm5_principal_ent_t entry, long mask,
101                             char *password)
102 {
103     krb5_db_entry               kdb;
104     osa_princ_ent_rec           adb;
105     kadm5_policy_ent_rec        polent;
106     krb5_int32                  now;
107     krb5_tl_data                *tl_data_orig, *tl_data_tail;
108     unsigned int                ret;
109     kadm5_server_handle_t handle = server_handle;
110
111     CHECK_HANDLE(server_handle);
112
113     /*
114      * Argument sanity checking, and opening up the DB
115      */
116     if(!(mask & KADM5_PRINCIPAL) || (mask & KADM5_MOD_NAME) ||
117        (mask & KADM5_MOD_TIME) || (mask & KADM5_LAST_PWD_CHANGE) ||
118        (mask & KADM5_MKVNO) || (mask & KADM5_POLICY_CLR) ||
119        (mask & KADM5_AUX_ATTRIBUTES) || (mask & KADM5_KEY_DATA) ||
120        (mask & KADM5_LAST_SUCCESS) || (mask & KADM5_LAST_FAILED) ||
121        (mask & KADM5_FAIL_AUTH_COUNT))
122         return KADM5_BAD_MASK;
123     if((mask & ~ALL_PRINC_MASK))
124         return KADM5_BAD_MASK;
125     if (entry == (kadm5_principal_ent_t) NULL || password == NULL)
126         return EINVAL;
127
128     /*
129      * Check to see if the principal exists
130      */
131     ret = kdb_get_entry(handle, entry->principal, &kdb, &adb);
132
133     switch(ret) {
134     case KADM5_UNK_PRINC:
135         break;
136     case 0:
137         kdb_free_entry(handle, &kdb, &adb);
138         return KADM5_DUP;
139     default:
140         return ret;
141     }
142
143     memset(&kdb, 0, sizeof(krb5_db_entry));
144     memset(&adb, 0, sizeof(osa_princ_ent_rec));
145
146     /*
147      * If a policy was specified, load it.
148      * If we can not find the one specified return an error
149      */
150     if ((mask & KADM5_POLICY)) {
151          if ((ret = kadm5_get_policy(handle->lhandle, entry->policy,
152                                      &polent)) != KADM5_OK) {
153             if(ret == EINVAL) 
154                 return KADM5_BAD_POLICY;
155             else
156                 return ret;
157         }
158     }
159     if (ret = passwd_check(handle, password, (mask & KADM5_POLICY),
160                            &polent, entry->principal)) {
161         if (mask & KADM5_POLICY)
162              (void) kadm5_free_policy_ent(handle->lhandle, &polent);
163         return ret;
164     }
165     /*
166      * Start populating the various DB fields, using the
167      * "defaults" for fields that were not specified by the
168      * mask.
169      */
170     if (ret = krb5_timeofday(handle->context, &now)) {
171         if (mask & KADM5_POLICY)
172              (void) kadm5_free_policy_ent(handle->lhandle, &polent);
173         return ret;
174     }
175
176     kdb.magic = KRB5_KDB_MAGIC_NUMBER;
177     kdb.len = KRB5_KDB_V1_BASE_LENGTH; /* gag me with a chainsaw */
178
179     if ((mask & KADM5_ATTRIBUTES)) 
180         kdb.attributes = entry->attributes;
181     else
182        kdb.attributes = handle->params.flags;
183
184     if ((mask & KADM5_MAX_LIFE))
185         kdb.max_life = entry->max_life; 
186     else 
187         kdb.max_life = handle->params.max_life;
188
189     if (mask & KADM5_MAX_RLIFE)
190          kdb.max_renewable_life = entry->max_renewable_life;
191     else
192          kdb.max_renewable_life = handle->params.max_rlife;
193
194     if ((mask & KADM5_PRINC_EXPIRE_TIME))
195         kdb.expiration = entry->princ_expire_time;
196     else
197         kdb.expiration = handle->params.expiration;
198
199     kdb.pw_expiration = 0;
200     if ((mask & KADM5_POLICY)) {
201         if(polent.pw_max_life)
202             kdb.pw_expiration = now + polent.pw_max_life;
203         else
204             kdb.pw_expiration = 0;
205     }
206     if ((mask & KADM5_PW_EXPIRATION)) {
207         if(!kdb.pw_expiration)
208             kdb.pw_expiration = entry->pw_expiration;
209         else {
210             if(entry->pw_expiration != 0)
211                 kdb.pw_expiration = (entry->pw_expiration < kdb.pw_expiration) ?
212                     entry->pw_expiration : kdb.pw_expiration;
213         }
214     }
215
216     kdb.last_success = 0;
217     kdb.last_failed = 0;
218     kdb.fail_auth_count = 0;
219
220     /* this is kind of gross, but in order to free the tl data, I need
221        to free the entire kdb entry, and that will try to free the
222        principal. */
223
224     if (ret = krb5_copy_principal(handle->context,
225                                   entry->principal, &(kdb.princ))) {
226         if (mask & KADM5_POLICY)
227              (void) kadm5_free_policy_ent(handle->lhandle, &polent);
228         return(ret);
229     }
230
231     if (ret = krb5_dbe_update_last_pwd_change(handle->context, &kdb, now)) {
232         krb5_dbe_free_contents(handle->context, &kdb);
233         if (mask & KADM5_POLICY)
234              (void) kadm5_free_policy_ent(handle->lhandle, &polent);
235         return(ret);
236     }
237
238     /* initialize the keys */
239
240     if (ret = krb5_dbe_cpw(handle->context, &master_encblock,
241                            handle->params.keysalts,
242                            handle->params.num_keysalts,
243                            password,
244                            (mask & KADM5_KVNO)?entry->kvno:1, &kdb)) {
245         krb5_dbe_free_contents(handle->context, &kdb);
246         if (mask & KADM5_POLICY)
247              (void) kadm5_free_policy_ent(handle->lhandle, &polent);
248         return(ret);
249     }
250
251     /* populate the admin-server-specific fields.  In the OV server,
252        this used to be in a separate database.  Since there's already
253        marshalling code for the admin fields, to keep things simple,
254        I'm going to keep it, and make all the admin stuff occupy a
255        single tl_data record, */
256
257     adb.admin_history_kvno = hist_kvno;
258     if ((mask & KADM5_POLICY)) {
259         adb.aux_attributes = KADM5_POLICY;
260
261         /* this does *not* need to be strdup'ed, because adb is xdr */
262         /* encoded in osa_adb_create_princ, and not ever freed */
263
264         adb.policy = entry->policy;
265     }
266
267     /* increment the policy ref count, if any */
268
269     if ((mask & KADM5_POLICY)) {
270         polent.policy_refcnt++;
271         if ((ret = kadm5_modify_policy_internal(handle->lhandle, &polent,
272                                                     KADM5_REF_COUNT))
273             != KADM5_OK) {
274             krb5_dbe_free_contents(handle->context, &kdb);
275             if (mask & KADM5_POLICY)
276                  (void) kadm5_free_policy_ent(handle->lhandle, &polent);
277             return(ret);
278         }
279     }
280
281     if (mask & KADM5_TL_DATA) {
282          /* splice entry->tl_data onto the front of kdb.tl_data */
283          tl_data_orig = kdb.tl_data;
284          for (tl_data_tail = entry->tl_data; tl_data_tail->tl_data_next;
285               tl_data_tail = tl_data_tail->tl_data_next)
286               ;
287          tl_data_tail->tl_data_next = kdb.tl_data;
288          kdb.tl_data = entry->tl_data;
289     }
290
291     /* store the new db entry */
292     ret = kdb_put_entry(handle, &kdb, &adb);
293
294     if (mask & KADM5_TL_DATA) {
295          /* remove entry->tl_data from the front of kdb.tl_data */
296          tl_data_tail->tl_data_next = NULL;
297          kdb.tl_data = tl_data_orig;
298     }
299
300     krb5_dbe_free_contents(handle->context, &kdb);
301
302     if (ret) {
303         if ((mask & KADM5_POLICY)) {
304             /* decrement the policy ref count */
305
306             polent.policy_refcnt--;
307             /*
308              * if this fails, there's nothing we can do anyway.  the
309              * policy refcount wil be too high.
310              */
311             (void) kadm5_modify_policy_internal(handle->lhandle, &polent,
312                                                      KADM5_REF_COUNT);
313         }
314
315         if (mask & KADM5_POLICY)
316              (void) kadm5_free_policy_ent(handle->lhandle, &polent);
317         return(ret);
318     }
319
320     if (mask & KADM5_POLICY)
321          (void) kadm5_free_policy_ent(handle->lhandle, &polent);
322
323     return KADM5_OK;
324 }
325
326         
327 kadm5_ret_t
328 kadm5_delete_principal(void *server_handle, krb5_principal principal)
329 {
330     unsigned int                ret;
331     kadm5_policy_ent_rec        polent;
332     krb5_db_entry               kdb;
333     osa_princ_ent_rec           adb;
334     kadm5_server_handle_t handle = server_handle;
335
336     CHECK_HANDLE(server_handle);
337
338     if (principal == NULL)
339         return EINVAL;
340
341     if (ret = kdb_get_entry(handle, principal, &kdb, &adb))
342         return(ret);
343
344     if ((adb.aux_attributes & KADM5_POLICY)) {
345         if ((ret = kadm5_get_policy(handle->lhandle,
346                                     adb.policy, &polent))
347             == KADM5_OK) {
348             polent.policy_refcnt--;
349             if ((ret = kadm5_modify_policy_internal(handle->lhandle, &polent,
350                                                          KADM5_REF_COUNT))
351                 != KADM5_OK) {
352                 (void) kadm5_free_policy_ent(handle->lhandle, &polent);
353                 kdb_free_entry(handle, &kdb, &adb);
354                 return(ret);
355             }
356         }
357         if (ret = kadm5_free_policy_ent(handle->lhandle, &polent)) {
358             kdb_free_entry(handle, &kdb, &adb);
359             return ret;
360         }
361     }
362
363     ret = kdb_delete_entry(handle, principal);
364
365     kdb_free_entry(handle, &kdb, &adb);
366
367     return ret;
368 }
369
370 kadm5_ret_t
371 kadm5_modify_principal(void *server_handle,
372                             kadm5_principal_ent_t entry, long mask)
373 {
374     int                     ret, ret2, i;
375     kadm5_policy_ent_rec    npol, opol;
376     int                     have_npol = 0, have_opol = 0;
377     krb5_db_entry           kdb;
378     krb5_tl_data            *tl_data_orig, *tl_data_tail;
379     osa_princ_ent_rec       adb;
380     kadm5_server_handle_t handle = server_handle;
381
382     CHECK_HANDLE(server_handle);
383
384     if((mask & KADM5_PRINCIPAL) || (mask & KADM5_LAST_PWD_CHANGE) ||
385        (mask & KADM5_MOD_TIME) || (mask & KADM5_MOD_NAME) ||
386        (mask & KADM5_MKVNO) || (mask & KADM5_AUX_ATTRIBUTES) ||
387        (mask & KADM5_KEY_DATA) || (mask & KADM5_LAST_SUCCESS) ||
388        (mask & KADM5_LAST_FAILED))
389         return KADM5_BAD_MASK;
390     if((mask & ~ALL_PRINC_MASK))
391         return KADM5_BAD_MASK;
392     if((mask & KADM5_POLICY) && (mask & KADM5_POLICY_CLR))
393         return KADM5_BAD_MASK;
394     if(entry == (kadm5_principal_ent_t) NULL)
395         return EINVAL;
396
397     if (ret = kdb_get_entry(handle, entry->principal, &kdb, &adb))
398         return(ret);
399
400     /*
401      * This is pretty much the same as create ...
402      */
403
404     if ((mask & KADM5_POLICY)) {
405         ret = kadm5_get_policy(handle->lhandle, entry->policy, &npol);
406         switch(ret) {
407         case EINVAL:
408             ret = KADM5_BAD_POLICY;
409             break;
410         case KADM5_UNK_POLICY:
411         case KADM5_BAD_POLICY:
412             ret =  KADM5_UNK_POLICY;
413             goto done;
414             break;
415         case KADM5_OK:
416             have_npol = 1;
417             if(adb.aux_attributes & KADM5_POLICY) {
418                 if(strcmp(adb.policy, entry->policy)) {
419                     ret = kadm5_get_policy(handle->lhandle,
420                                            adb.policy, &opol);
421                     switch(ret) {
422                     case EINVAL:
423                     case KADM5_BAD_POLICY:
424                     case KADM5_UNK_POLICY:
425                         break;
426                     case KADM5_OK:
427                         have_opol = 1;
428                         opol.policy_refcnt--;
429                         break;
430                     default:
431                         goto done;
432                         break;
433                     }
434                     npol.policy_refcnt++;
435                 }
436             } else npol.policy_refcnt++;
437             adb.aux_attributes |= KADM5_POLICY;
438             if (adb.policy)
439                  free(adb.policy);
440             adb.policy = strdup(entry->policy);
441             if (npol.pw_max_life) {
442                 if (ret =
443                     krb5_dbe_lookup_last_pwd_change(handle->context, &kdb,
444                                                     &(kdb.pw_expiration)))
445                     goto done;
446                 kdb.pw_expiration += npol.pw_max_life;
447             } else {
448                 kdb.pw_expiration = 0;
449             }
450             break;
451         default:
452             goto done;
453         }
454         if ((mask & KADM5_PW_EXPIRATION)) {
455             if(kdb.pw_expiration == 0)
456                 kdb.pw_expiration = entry->pw_expiration;
457             else if(entry->pw_expiration != 0)
458                 kdb.pw_expiration = (entry->pw_expiration < kdb.pw_expiration) ?
459                                     entry->pw_expiration : kdb.pw_expiration;
460         }
461     }
462     if ((mask & KADM5_PW_EXPIRATION) && !(mask & KADM5_POLICY)) {
463             if(kdb.pw_expiration == 0)
464                 kdb.pw_expiration = entry->pw_expiration;
465             else if(entry->pw_expiration != 0)
466                 kdb.pw_expiration = (entry->pw_expiration < kdb.pw_expiration) ?
467                                     entry->pw_expiration : kdb.pw_expiration;
468     }
469
470     if ((mask & KADM5_POLICY_CLR)) {
471         if (adb.aux_attributes & KADM5_POLICY) {
472             adb.aux_attributes &= ~KADM5_POLICY;
473             kdb.pw_expiration = 0;
474             ret = kadm5_get_policy(handle->lhandle, adb.policy, &opol);
475             switch(ret) {
476             case EINVAL:
477             case KADM5_BAD_POLICY:
478             case KADM5_UNK_POLICY:
479                 ret = KADM5_BAD_DB;
480                 goto done;
481                 break;
482             case KADM5_OK:
483                 have_opol = 1;
484                 if (adb.policy)
485                      free(adb.policy);
486                 adb.policy = NULL;
487                 opol.policy_refcnt--;
488                 break;
489             default:
490                 goto done;
491                 break;
492             }
493         }
494     }
495     if (((mask & KADM5_POLICY) ||
496          (mask & KADM5_POLICY_CLR)) &&
497         (((have_opol) &&
498           (ret =
499            kadm5_modify_policy_internal(handle->lhandle, &opol,
500                                              KADM5_REF_COUNT))) ||
501          ((have_npol) &&
502           (ret =
503            kadm5_modify_policy_internal(handle->lhandle, &npol,
504                                              KADM5_REF_COUNT)))))
505         goto done;
506
507     if ((mask & KADM5_ATTRIBUTES)) 
508         kdb.attributes = entry->attributes;
509     if ((mask & KADM5_MAX_LIFE))
510         kdb.max_life = entry->max_life;
511     if ((mask & KADM5_PRINC_EXPIRE_TIME))
512         kdb.expiration = entry->princ_expire_time;
513     /* the pw_expiration logic would go here if it wasn't spread
514        all over the policy code */
515     if (mask & KADM5_MAX_RLIFE)
516          kdb.max_renewable_life = entry->max_renewable_life;
517     if (mask & KADM5_FAIL_AUTH_COUNT)
518          kdb.fail_auth_count = entry->fail_auth_count;
519     
520     if((mask & KADM5_KVNO)) {
521          for (i = 0; i < kdb.n_key_data; i++)
522               kdb.key_data[i].key_data_kvno = entry->kvno;
523     }
524
525     if (mask & KADM5_TL_DATA) {
526          /* splice entry->tl_data onto the front of kdb.tl_data */
527          tl_data_orig = kdb.tl_data;
528          for (tl_data_tail = entry->tl_data; tl_data_tail->tl_data_next;
529               tl_data_tail = tl_data_tail->tl_data_next)
530               ;
531          tl_data_tail->tl_data_next = kdb.tl_data;
532          kdb.tl_data = entry->tl_data;
533     }
534
535     if ((ret = kdb_put_entry(handle, &kdb, &adb)))
536         goto done;
537
538     if (mask & KADM5_TL_DATA) {
539          /* remove entry->tl_data from the front of kdb.tl_data */
540          tl_data_tail->tl_data_next = NULL;
541          kdb.tl_data = tl_data_orig;
542     }
543     
544     ret = KADM5_OK;
545 done:
546     if (have_opol) {
547          ret2 = kadm5_free_policy_ent(handle->lhandle, &opol);
548          ret = ret ? ret : ret2;
549     }
550     if (have_npol) {
551          ret2 = kadm5_free_policy_ent(handle->lhandle, &npol);
552          ret = ret ? ret : ret2;
553     }
554     kdb_free_entry(handle, &kdb, &adb);
555     return ret;
556 }
557     
558 kadm5_ret_t
559 kadm5_rename_principal(void *server_handle,
560                             krb5_principal source, krb5_principal target)
561 {
562     krb5_db_entry       kdb;
563     osa_princ_ent_rec   adb;
564     int                 ret, i;
565     kadm5_server_handle_t handle = server_handle;
566
567     CHECK_HANDLE(server_handle);
568
569     if (source == NULL || target == NULL)
570         return EINVAL;
571
572     if ((ret = kdb_get_entry(handle, target, &kdb, &adb)) == 0) {
573         kdb_free_entry(handle, &kdb, &adb);
574         return(KADM5_DUP);
575     }
576
577     if ((ret = kdb_get_entry(handle, source, &kdb, &adb)))
578         return ret;
579
580     /* this is kinda gross, but unavoidable */
581
582     for (i=0; i<kdb.n_key_data; i++) {
583         if ((kdb.key_data[i].key_data_ver == 1) ||
584             (kdb.key_data[i].key_data_type[1] == KRB5_KDB_SALTTYPE_NORMAL)) {
585             ret = KADM5_NO_RENAME_SALT;
586             goto done;
587         }
588     }
589
590     krb5_free_principal(handle->context, kdb.princ);
591     if (ret = krb5_copy_principal(handle->context, target, &kdb.princ)) {
592         kdb.princ = NULL; /* so freeing the dbe doesn't lose */
593         goto done;
594     }
595
596     if ((ret = kdb_put_entry(handle, &kdb, &adb)))
597         goto done;
598
599     ret = kdb_delete_entry(handle, source);
600
601 done:
602     kdb_free_entry(handle, &kdb, &adb);
603     return ret;
604 }
605
606 kadm5_ret_t
607 kadm5_get_principal(void *server_handle, krb5_principal principal,
608                     kadm5_principal_ent_t entry,
609                     long in_mask)
610 {
611     krb5_db_entry               kdb;
612     osa_princ_ent_rec           adb;
613     osa_adb_ret_t               ret = 0;
614     long                        mask;
615     int i;
616     kadm5_server_handle_t handle = server_handle;
617     kadm5_principal_ent_rec     entry_local, *entry_orig;
618
619     CHECK_HANDLE(server_handle);
620
621     /*
622      * In version 1, all the defined fields are always returned.
623      * entry is a pointer to a kadm5_principal_ent_t_v1 that should be
624      * filled with allocated memory.
625      */
626     if (handle->api_version == KADM5_API_VERSION_1) {
627          mask = KADM5_PRINCIPAL_NORMAL_MASK;
628          entry_orig = entry;
629          entry = &entry_local;
630     } else {
631          mask = in_mask;
632     }
633
634     memset((char *) entry, 0, sizeof(*entry));
635
636     if (principal == NULL)
637         return EINVAL;
638
639     if ((ret = kdb_get_entry(handle, principal, &kdb, &adb)))
640         return ret;
641
642     if ((mask & KADM5_POLICY) &&
643         adb.policy && (adb.aux_attributes & KADM5_POLICY)) {
644         if ((entry->policy = (char *) malloc(strlen(adb.policy) + 1)) == NULL) {
645             ret = ENOMEM;
646             goto done;
647         }
648         strcpy(entry->policy, adb.policy);
649     }
650
651     if (mask & KADM5_AUX_ATTRIBUTES)
652          entry->aux_attributes = adb.aux_attributes;
653
654     if ((mask & KADM5_PRINCIPAL) &&
655         (ret = krb5_copy_principal(handle->context, principal,
656                                    &entry->principal))) { 
657         goto done;
658     }
659
660     if (mask & KADM5_PRINC_EXPIRE_TIME)
661          entry->princ_expire_time = kdb.expiration;
662
663     if ((mask & KADM5_LAST_PWD_CHANGE) &&
664         (ret = krb5_dbe_lookup_last_pwd_change(handle->context, &kdb,
665                                                &(entry->last_pwd_change)))) {
666         goto done;
667     }
668
669     if (mask & KADM5_PW_EXPIRATION)
670          entry->pw_expiration = kdb.pw_expiration;
671     if (mask & KADM5_MAX_LIFE)
672          entry->max_life = kdb.max_life;
673
674     /* this is a little non-sensical because the function returns two */
675     /* values that must be checked separately against the mask */
676     if ((mask & KADM5_MOD_NAME) || (mask & KADM5_MOD_TIME)) {
677          if (ret = krb5_dbe_lookup_mod_princ_data(handle->context, &kdb,
678                                                   &(entry->mod_date),
679                                                   &(entry->mod_name))) {
680               goto done;
681          }
682          if (! (mask & KADM5_MOD_TIME))
683               entry->mod_date = 0;
684          if (! (mask & KADM5_MOD_NAME)) {
685               krb5_free_principal(handle->context, entry->principal);
686               entry->principal = NULL;
687          }
688     }
689
690     if (mask & KADM5_ATTRIBUTES)
691          entry->attributes = kdb.attributes;
692
693     if (mask & KADM5_KVNO)
694          for (entry->kvno = 0, i=0; i<kdb.n_key_data; i++)
695               if (kdb.key_data[i].key_data_kvno > entry->kvno)
696                    entry->kvno = kdb.key_data[i].key_data_kvno;
697     
698     if (handle->api_version == KADM5_API_VERSION_2)
699          entry->mkvno = 0;
700     else {
701          /* XXX I'll be damned if I know how to deal with this one --marc */
702          entry->mkvno = 1;
703     }
704
705     /*
706      * The new fields that only exist in version 2 start here
707      */
708     if (handle->api_version == KADM5_API_VERSION_2) {
709          if (mask & KADM5_MAX_RLIFE)
710               entry->max_renewable_life = kdb.max_renewable_life;
711          if (mask & KADM5_LAST_SUCCESS)
712               entry->last_success = kdb.last_success;
713          if (mask & KADM5_LAST_FAILED)
714               entry->last_failed = kdb.last_failed;
715          if (mask & KADM5_FAIL_AUTH_COUNT)
716               entry->fail_auth_count = kdb.fail_auth_count;
717          if (mask & KADM5_TL_DATA) {
718               krb5_tl_data td, *tl, *tl2;
719
720               entry->n_tl_data = kdb.n_tl_data;
721               entry->tl_data = NULL;
722               
723               tl = kdb.tl_data;
724               while (tl) {
725                    if ((tl2 = dup_tl_data(tl)) == NULL) {
726                         ret = ENOMEM;
727                         goto done;
728                    }
729                    tl2->tl_data_next = entry->tl_data;
730                    entry->tl_data = tl2;
731
732                    tl = tl->tl_data_next;
733               }
734               
735               if (kdb.e_length) {
736                    td.tl_data_type = KRB5_TL_KADM5_E_DATA;
737                    td.tl_data_length = kdb.e_length;
738                    td.tl_data_contents = kdb.e_data;
739
740                    if ((tl = dup_tl_data(&td)) == NULL) {
741                         ret = ENOMEM;
742                         goto done;
743                    }
744                    tl->tl_data_next = entry->tl_data;
745                    entry->tl_data = tl;
746               }
747          }
748          if (mask & KADM5_KEY_DATA) {
749               entry->n_key_data = kdb.n_key_data;
750               entry->key_data = (krb5_key_data *)
751                    malloc(entry->n_key_data*sizeof(krb5_key_data));
752               if (entry->key_data == NULL) {
753                    ret = ENOMEM;
754                    goto done;
755               }
756               for (i = 0; i < entry->n_key_data; i++)
757                    if (ret = krb5_copy_key_data_contents(handle->context,
758                                                          &kdb.key_data[i],
759                                                          &entry->key_data[i]))
760                         goto done;
761          }
762     }
763
764     /*
765      * If KADM5_API_VERSION_1, we return an allocated structure, and
766      * we need to convert the new structure back into the format the
767      * caller is expecting.
768      */
769     if (handle->api_version == KADM5_API_VERSION_1) {
770          kadm5_principal_ent_t_v1 newv1;
771
772          newv1 = ((kadm5_principal_ent_t_v1) calloc(1, sizeof(*newv1)));
773          if (newv1 == NULL) {
774               ret = ENOMEM;
775               goto done;
776          }
777          
778          newv1->principal = entry->principal;
779          newv1->princ_expire_time = entry->princ_expire_time;
780          newv1->last_pwd_change = entry->last_pwd_change;
781          newv1->pw_expiration = entry->pw_expiration;
782          newv1->max_life = entry->max_life;
783          newv1->mod_name = entry->mod_name;
784          newv1->mod_date = entry->mod_date;
785          newv1->attributes = entry->attributes;
786          newv1->kvno = entry->kvno;
787          newv1->mkvno = entry->mkvno;
788          newv1->policy = entry->policy;
789          newv1->aux_attributes = entry->aux_attributes;
790
791          *((kadm5_principal_ent_t_v1 *) entry_orig) = newv1;
792     }
793
794     ret = KADM5_OK;
795
796 done:
797     if (ret && entry->principal)
798          krb5_free_principal(handle->context, entry->principal);
799     kdb_free_entry(handle, &kdb, &adb);
800
801     return ret;
802 }
803
804 /*
805  * Function: check_pw_reuse
806  *
807  * Purpose: Check if a key appears in a list of keys, in order to
808  * enforce password history.
809  *
810  * Arguments:
811  *
812  *      context                 (r) the krb5 context
813  *      histkey_encblock        (r) the encblock that hist_key_data is
814  *                              encrypted in
815  *      n_new_key_data          (r) length of new_key_data
816  *      new_key_data            (r) keys to check against
817  *                              pw_hist_data, encrypted in histkey_encblock
818  *      n_pw_hist_data          (r) length of pw_hist_data
819  *      pw_hist_data            (r) passwords to check new_key_data against
820  *
821  * Effects:
822  * For each new_key in new_key_data:
823  *      decrypt new_key with the master_encblock
824  *      for each password in pw_hist_data:
825  *              for each hist_key in password:
826  *                      decrypt hist_key with histkey_encblock
827  *                      compare the new_key and hist_key
828  *
829  * Returns krb5 errors, KADM5_PASS_RESUSE if a key in
830  * new_key_data is the same as a key in pw_hist_data, or 0.
831  */
832 static kadm5_ret_t
833 check_pw_reuse(krb5_context context,
834                krb5_encrypt_block *histkey_encblock,
835                int n_new_key_data, krb5_key_data *new_key_data,
836                int n_pw_hist_data, osa_pw_hist_ent *pw_hist_data)
837 {
838     int x, y, z;
839     krb5_keyblock newkey, histkey;
840     krb5_error_code ret;
841
842     for (x = 0; x < n_new_key_data; x++) {
843          if (ret = krb5_dbekd_decrypt_key_data(context,
844                                                &master_encblock,
845                                                &(new_key_data[x]),
846                                                &newkey, NULL))
847             return(ret);
848         for (y = 0; y < n_pw_hist_data; y++) {
849              for (z = 0; z < pw_hist_data[y].n_key_data; z++) {
850                   if (ret =
851                       krb5_dbekd_decrypt_key_data(context,
852                                                   histkey_encblock,
853                                                   &pw_hist_data[y].key_data[z],
854                                                   &histkey, NULL))
855                        return(ret);             
856                   
857                   if ((newkey.length == histkey.length) &&
858                       (newkey.enctype == histkey.enctype) &&
859                       (memcmp(newkey.contents, histkey.contents,
860                               histkey.length) == 0)) {
861                        krb5_free_keyblock_contents(context, &histkey);
862                        krb5_free_keyblock_contents(context, &newkey);
863                        
864                        return(KADM5_PASS_REUSE);
865                   }
866                   krb5_free_keyblock_contents(context, &histkey);
867              }
868         }
869         krb5_free_keyblock_contents(context, &newkey);
870     }
871
872     return(0);
873 }
874
875 /*
876  * Function: create_history_entry
877  *
878  * Purpose: Creates a password history entry from an array of
879  * key_data.
880  *
881  * Arguments:
882  *
883  *      context         (r) krb5_context to use
884  *      n_key_data      (r) number of elements in key_data
885  *      key_data        (r) keys to add to the history entry
886  *      hist            (w) history entry to fill in
887  *
888  * Effects:
889  *
890  * hist->key_data is allocated to store n_key_data key_datas.  Each
891  * element of key_data is decrypted with master_encblock, re-encrypted
892  * in hist_encblock, and added to hist->key_data.  hist->n_key_data is
893  * set to n_key_data.
894  */
895 int create_history_entry(krb5_context context, int n_key_data,
896                          krb5_key_data *key_data, osa_pw_hist_ent *hist)
897 {
898      int i, ret;
899      krb5_keyblock key;
900      krb5_keysalt salt;
901      
902      hist->key_data = (krb5_key_data*)malloc(n_key_data*sizeof(krb5_key_data));
903      if (hist->key_data == NULL)
904           return ENOMEM;
905      memset(hist->key_data, 0, n_key_data*sizeof(krb5_key_data));
906
907      for (i = 0; i < n_key_data; i++) {
908           if (ret = krb5_dbekd_decrypt_key_data(context,
909                                                 &master_encblock,
910                                                 &key_data[i],
911                                                 &key, &salt))
912                return ret;
913           if (ret = krb5_dbekd_encrypt_key_data(context,
914                                                 &hist_encblock,
915                                                 &key, &salt,
916                                                 key_data[i].key_data_kvno,
917                                                 &hist->key_data[i]))
918                return ret;
919           krb5_free_keyblock_contents(context, &key);
920           /* krb5_free_keysalt(context, &salt); */
921      }
922
923      hist->n_key_data = n_key_data;
924      return 0;
925 }
926
927 int free_history_entry(krb5_context context, osa_pw_hist_ent *hist)
928 {
929      int i;
930
931      for (i = 0; i < hist->n_key_data; i++)
932           krb5_free_key_data_contents(context, &hist->key_data[i]);
933      free(hist->key_data);
934 }
935
936 /*
937  * Function: add_to_history
938  *
939  * Purpose: Adds a password to a principal's password history.
940  *
941  * Arguments:
942  *
943  *      context         (r) krb5_context to use
944  *      adb             (r/w) admin principal entry to add keys to
945  *      pol             (r) adb's policy
946  *      pw              (r) keys for the password to add to adb's key history
947  *
948  * Effects:
949  *
950  * add_to_history adds a single password to adb's password history.
951  * pw contains n_key_data keys in its key_data, in storage should be
952  * allocated but not freed by the caller (XXX blech!).
953  *
954  * This function maintains adb->old_keys as a circular queue.  It
955  * starts empty, and grows each time this function is called until it
956  * is pol->pw_history_num items long.  adb->old_key_len holds the
957  * number of allocated entries in the array, and must therefore be [0,
958  * pol->pw_history_num).  adb->old_key_next is the index into the
959  * array where the next element should be written, and must be [0,
960  * adb->old_key_len).
961  */
962 static kadm5_ret_t add_to_history(krb5_context context,
963                                   osa_princ_ent_t adb,
964                                   kadm5_policy_ent_t pol,
965                                   osa_pw_hist_ent *pw)
966 {
967      osa_pw_hist_ent hist, *histp;
968      int ret, i;
969
970      /* A history of 1 means just check the current password */
971      if (pol->pw_history_num == 1)
972           return 0;
973
974      /* resize the adb->old_keys array if necessary */
975      if (adb->old_key_len < pol->pw_history_num-1) {
976           adb->old_keys = (osa_pw_hist_ent *)
977                realloc(adb->old_keys,
978                        (adb->old_key_len+1)*sizeof(osa_pw_hist_ent));
979           if (adb->old_keys == NULL)
980                return(ENOMEM);
981           
982           memset(&adb->old_keys[adb->old_key_len],0,sizeof(osa_pw_hist_ent)); 
983           adb->old_key_len++;
984      }
985
986      /* free the old pw history entry if it contains data */
987      histp = &adb->old_keys[adb->old_key_next];
988      for (i = 0; i < histp->n_key_data; i++)
989           krb5_free_key_data_contents(context, &histp->key_data[i]);
990      
991      /* store the new entry */
992      adb->old_keys[adb->old_key_next] = *pw;
993
994      /* update the next pointer */
995      if (++adb->old_key_next == pol->pw_history_num-1)
996                adb->old_key_next = 0;
997
998      return(0);
999 }
1000
1001 kadm5_ret_t
1002 kadm5_chpass_principal(void *server_handle,
1003                             krb5_principal principal, char *password)
1004 {
1005     krb5_int32                  now;
1006     kadm5_policy_ent_rec        pol;
1007     osa_princ_ent_rec           adb;
1008     krb5_db_entry               kdb, kdb_save;
1009     int                         ret, ret2, last_pwd, i, hist_added;
1010     int                         have_pol = 0;
1011     kadm5_server_handle_t       handle = server_handle;
1012     osa_pw_hist_ent             hist;
1013
1014     CHECK_HANDLE(server_handle);
1015
1016     hist_added = 0;
1017     memset(&hist, 0, sizeof(hist));
1018
1019     if (principal == NULL || password == NULL)
1020         return EINVAL;
1021     if ((krb5_principal_compare(handle->context,
1022                                 principal, hist_princ)) == TRUE)
1023         return KADM5_PROTECT_PRINCIPAL;
1024
1025     if ((ret = kdb_get_entry(handle, principal, &kdb, &adb)))
1026        return(ret);
1027
1028     /* we are going to need the current keys after the new keys are set */
1029     if ((ret = kdb_get_entry(handle, principal, &kdb_save, NULL))) {
1030          kdb_free_entry(handle, &kdb, &adb);
1031          return(ret);
1032     }
1033     
1034     if ((adb.aux_attributes & KADM5_POLICY)) {
1035         if ((ret = kadm5_get_policy(handle->lhandle, adb.policy, &pol)))
1036              goto done;
1037         have_pol = 1;
1038     }
1039
1040     if ((ret = passwd_check(handle, password, adb.aux_attributes &
1041                             KADM5_POLICY, &pol, principal)))
1042          goto done;
1043
1044     if (ret = krb5_dbe_cpw(handle->context, &master_encblock,
1045                            handle->params.keysalts,
1046                            handle->params.num_keysalts,
1047                            password, 0 /* increment kvno */, &kdb))
1048         goto done;
1049
1050     kdb.attributes &= ~KRB5_KDB_REQUIRES_PWCHANGE;
1051
1052     if (ret = krb5_timeofday(handle->context, &now))
1053          goto done;
1054     
1055     if ((adb.aux_attributes & KADM5_POLICY)) {
1056        /* the policy was loaded before */
1057
1058         if (ret = krb5_dbe_lookup_last_pwd_change(handle->context,
1059                                                   &kdb, &last_pwd))
1060              goto done;
1061
1062 #if 0
1063          /*
1064           * The spec says this check is overridden if the caller has
1065           * modify privilege.  The admin server therefore makes this
1066           * check itself (in chpass_principal_wrapper, misc.c). A
1067           * local caller implicitly has all authorization bits.
1068           */
1069         if ((now - last_pwd) < pol.pw_min_life &&
1070             !(kdb.attributes & KRB5_KDB_REQUIRES_PWCHANGE)) {
1071              ret = KADM5_PASS_TOOSOON;
1072              goto done;
1073         }
1074 #endif
1075
1076         if (ret = create_history_entry(handle->context,
1077                                        kdb_save.n_key_data,
1078                                        kdb_save.key_data, &hist))
1079              goto done;
1080
1081         if (ret = check_pw_reuse(handle->context,
1082                                  &hist_encblock,
1083                                  kdb.n_key_data, kdb.key_data,
1084                                  1, &hist))
1085              goto done;
1086          
1087         if (pol.pw_history_num > 1) {
1088             if (adb.admin_history_kvno != hist_kvno) {
1089                 ret = KADM5_BAD_HIST_KEY;
1090                 goto done;
1091             }
1092
1093             if (ret = check_pw_reuse(handle->context,
1094                                      &hist_encblock,
1095                                      kdb.n_key_data, kdb.key_data,
1096                                      adb.old_key_len, adb.old_keys))
1097                 goto done;
1098
1099             if (ret = add_to_history(handle->context, &adb, &pol, &hist))
1100                  goto done;
1101             hist_added = 1;
1102        }
1103
1104         if (pol.pw_max_life)
1105            kdb.pw_expiration = now + pol.pw_max_life;
1106         else
1107            kdb.pw_expiration = 0;
1108     } else {
1109         kdb.pw_expiration = 0;
1110     }
1111
1112     if (ret = krb5_dbe_update_last_pwd_change(handle->context, &kdb, now))
1113         goto done;
1114
1115     if ((ret = kdb_put_entry(handle, &kdb, &adb)))
1116         goto done;
1117
1118     ret = KADM5_OK;
1119 done:
1120     if (!hist_added && hist.key_data)
1121          free_history_entry(handle->context, &hist);
1122     kdb_free_entry(handle, &kdb, &adb);
1123     kdb_free_entry(handle, &kdb_save, NULL);
1124     krb5_dbe_free_contents(handle->context, &kdb);
1125
1126     if (have_pol && (ret2 = kadm5_free_policy_ent(handle->lhandle, &pol))
1127         && !ret) 
1128          ret = ret2;
1129
1130     return ret;
1131 }
1132
1133 kadm5_ret_t
1134 kadm5_randkey_principal(void *server_handle,
1135                         krb5_principal principal,
1136                         krb5_keyblock **keyblocks,
1137                         int *n_keys)
1138 {
1139     krb5_db_entry               kdb;
1140     osa_princ_ent_rec           adb;
1141     krb5_int32                  now;
1142     kadm5_policy_ent_rec        pol;
1143     krb5_key_data               *key_data;
1144     krb5_keyblock               *keyblock;
1145     int                         ret, last_pwd, have_pol = 0;
1146     kadm5_server_handle_t       handle = server_handle;
1147
1148     if (keyblocks)
1149          *keyblocks = NULL;
1150
1151     CHECK_HANDLE(server_handle);
1152
1153     if (principal == NULL)
1154         return EINVAL;
1155     if (hist_princ && /* this will be NULL when initializing the databse */
1156         ((krb5_principal_compare(handle->context,
1157                                  principal, hist_princ)) == TRUE))
1158         return KADM5_PROTECT_PRINCIPAL;
1159
1160     if ((ret = kdb_get_entry(handle, principal, &kdb, &adb)))
1161        return(ret);
1162
1163     if (ret = krb5_dbe_crk(handle->context, &master_encblock,
1164                            handle->params.keysalts,
1165                            handle->params.num_keysalts,
1166                            &kdb))
1167        goto done;
1168
1169     kdb.attributes &= ~KRB5_KDB_REQUIRES_PWCHANGE;
1170
1171     if (ret = krb5_timeofday(handle->context, &now))
1172         goto done;
1173
1174     if ((adb.aux_attributes & KADM5_POLICY)) {
1175         if ((ret = kadm5_get_policy(handle->lhandle, adb.policy,
1176                                     &pol)) != KADM5_OK) 
1177            goto done;
1178         have_pol = 1;
1179
1180         if (ret = krb5_dbe_lookup_last_pwd_change(handle->context,
1181                                                   &kdb, &last_pwd))
1182              goto done;
1183
1184 #if 0
1185          /*
1186           * The spec says this check is overridden if the caller has
1187           * modify privilege.  The admin server therefore makes this
1188           * check itself (in chpass_principal_wrapper, misc.c).  A
1189           * local caller implicitly has all authorization bits.
1190           */
1191         if((now - last_pwd) < pol.pw_min_life &&
1192            !(kdb.attributes & KRB5_KDB_REQUIRES_PWCHANGE)) {
1193              ret = KADM5_PASS_TOOSOON;
1194              goto done;
1195         }
1196 #endif
1197
1198         if(pol.pw_history_num > 1) {
1199             if(adb.admin_history_kvno != hist_kvno) {
1200                 ret = KADM5_BAD_HIST_KEY;
1201                 goto done;
1202             }
1203
1204             if (ret = check_pw_reuse(handle->context,
1205                                      &hist_encblock,
1206                                      kdb.n_key_data, kdb.key_data,
1207                                      adb.old_key_len, adb.old_keys))
1208                 goto done;
1209         }
1210         if (pol.pw_max_life)
1211            kdb.pw_expiration = now + pol.pw_max_life;
1212         else
1213            kdb.pw_expiration = 0;
1214     } else {
1215         kdb.pw_expiration = 0;
1216     }
1217
1218     if (ret = krb5_dbe_update_last_pwd_change(handle->context, &kdb, now))
1219          goto done;
1220
1221     if (keyblocks) {
1222          if (handle->api_version == KADM5_API_VERSION_1) {
1223               /* Version 1 clients will expect to see a DES_CRC enctype. */
1224               if (ret = krb5_dbe_find_enctype(handle->context, &kdb,
1225                                               ENCTYPE_DES_CBC_CRC,
1226                                               -1, -1, &key_data))
1227                    goto done;
1228
1229               if (ret = decrypt_key_data(handle->context, 1, key_data,
1230                                          keyblocks, NULL))
1231                    goto done;
1232          } else {
1233               ret = decrypt_key_data(handle->context,
1234                                      kdb.n_key_data, kdb.key_data,
1235                                      keyblocks, n_keys);
1236               if (ret)
1237                    goto done;
1238          }
1239     }    
1240     
1241     if ((ret = kdb_put_entry(handle, &kdb, &adb)))
1242         goto done;
1243
1244     ret = KADM5_OK;
1245 done:
1246     kdb_free_entry(handle, &kdb, &adb);
1247     if (have_pol)
1248          kadm5_free_policy_ent(handle->lhandle, &pol);
1249
1250     return ret;
1251 }
1252
1253 /*
1254  * Allocate an array of n_key_data krb5_keyblocks, fill in each
1255  * element with the results of decrypting the nth key in key_data with
1256  * master_encblock, and if n_keys is not NULL fill it in with the
1257  * number of keys decrypted.
1258  */
1259 static int decrypt_key_data(krb5_context context,
1260                             int n_key_data, krb5_key_data *key_data,
1261                             krb5_keyblock **keyblocks, int *n_keys)
1262 {
1263      krb5_keyblock *keys;
1264      int ret, i;
1265
1266      keys = (krb5_keyblock *) malloc(n_key_data*sizeof(krb5_keyblock));
1267      if (keys == NULL)
1268           return ENOMEM;
1269      memset((char *) keys, 0, n_key_data*sizeof(krb5_keyblock));
1270
1271      for (i = 0; i < n_key_data; i++) {
1272           if (ret = krb5_dbekd_decrypt_key_data(context,
1273                                                 &master_encblock,
1274                                                 &key_data[i], 
1275                                                 &keys[i], NULL)) {
1276
1277                memset((char *) keys, 0, n_key_data*sizeof(krb5_keyblock));
1278                free(keys);
1279                return ret;
1280           }
1281      }
1282
1283      *keyblocks = keys;
1284      if (n_keys)
1285           *n_keys = n_key_data;
1286
1287      return 0;
1288 }
1289
1290 /*
1291  * Function: kadm5_decrypt_key
1292  *
1293  * Purpose: Retrieves and decrypts a principal key.
1294  *
1295  * Arguments:
1296  *
1297  *      server_handle   (r) kadm5 handle
1298  *      entry           (r) principal retrieved with kadm5_get_principal
1299  *      ktype           (r) enctype to search for, or -1 to ignore
1300  *      stype           (r) salt type to search for, or -1 to ignore
1301  *      kvno            (r) kvno to search for, -1 for max, 0 for max
1302  *                      only if it also matches ktype and stype
1303  *      keyblock        (w) keyblock to fill in
1304  *      keysalt         (w) keysalt to fill in, or NULL
1305  *      kvnop           (w) kvno to fill in, or NULL
1306  *
1307  * Effects: Searches the key_data array of entry, which must have been
1308  * retrived with kadm5_get_principal with the KADM5_KEY_DATA mask, to
1309  * find a key with a specified enctype, salt type, and kvno in a
1310  * principal entry.  If not found, return ENOENT.  Otherwise, decrypt
1311  * it with the master key, and return the key in keyblock, the salt
1312  * in salttype, and the key version number in kvno.
1313  *
1314  * If ktype or stype is -1, it is ignored for the search.  If kvno is
1315  * -1, ktype and stype are ignored and the key with the max kvno is
1316  * returned.  If kvno is 0, only the key with the max kvno is returned
1317  * and only if it matches the ktype and stype; otherwise, ENOENT is
1318  * returned.
1319  */
1320 kadm5_ret_t kadm5_decrypt_key(void *server_handle,
1321                               kadm5_principal_ent_t entry, krb5_int32
1322                               ktype, krb5_int32 stype, krb5_int32
1323                               kvno, krb5_keyblock *keyblock,
1324                               krb5_keysalt *keysalt, int *kvnop)
1325 {
1326     kadm5_server_handle_t handle = server_handle;
1327     krb5_db_entry dbent;
1328     krb5_key_data *key_data;
1329     int ret;
1330
1331     CHECK_HANDLE(server_handle);
1332
1333     if (entry->n_key_data == 0 || entry->key_data == NULL)
1334          return EINVAL;
1335
1336     /* find_enctype only uses these two fields */
1337     dbent.n_key_data = entry->n_key_data;
1338     dbent.key_data = entry->key_data;
1339     if (ret = krb5_dbe_find_enctype(handle->context, &dbent, ktype,
1340                                     stype, kvno, &key_data))
1341          return ret;
1342
1343     if (ret = krb5_dbekd_decrypt_key_data(handle->context,
1344                                           &master_encblock, key_data,
1345                                           keyblock, keysalt))
1346          return ret;
1347
1348     if (kvnop)
1349          *kvnop = key_data->key_data_kvno;
1350
1351     return KADM5_OK;
1352 }