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