1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /*
3  * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved
4  *
5  * $Header$
6  */
7 #include "k5-int.h"
8 #include        <sys/time.h>
9 #include        <kadm5/admin.h>
10 #include        <kdb.h>
11 #include        "server_internal.h"
12 #ifdef USE_PASSWORD_SERVER
13 #include        <sys/wait.h>
14 #include        <signal.h>
15 #endif
16 
17 #include <krb5/kadm5_hook_plugin.h>
18 
19 #ifdef USE_VALGRIND
20 #include <valgrind/memcheck.h>
21 #else
22 #define VALGRIND_CHECK_DEFINED(LVALUE) ((void)0)
23 #endif
24 
25 extern  krb5_principal      master_princ;
26 extern  krb5_principal      hist_princ;
27 extern  krb5_keyblock       master_keyblock;
28 extern  krb5_db_entry       master_db;
29 
30 static int decrypt_key_data(krb5_context context,
31                             int n_key_data, krb5_key_data *key_data,
32                             krb5_keyblock **keyblocks, int *n_keys);
33 
34 /*
35  * XXX Functions that ought to be in libkrb5.a, but aren't.
36  */
krb5_copy_key_data_contents(context,from,to)37 kadm5_ret_t krb5_copy_key_data_contents(context, from, to)
38     krb5_context context;
39     krb5_key_data *from, *to;
40 {
41     int i, idx;
42 
43     *to = *from;
44 
45     idx = (from->key_data_ver == 1 ? 1 : 2);
46 
47     for (i = 0; i < idx; i++) {
48         if ( from->key_data_length[i] ) {
49             to->key_data_contents[i] = malloc(from->key_data_length[i]);
50             if (to->key_data_contents[i] == NULL) {
51                 for (i = 0; i < idx; i++)
52                     zapfree(to->key_data_contents[i], to->key_data_length[i]);
53                 return ENOMEM;
54             }
55             memcpy(to->key_data_contents[i], from->key_data_contents[i],
56                    from->key_data_length[i]);
57         }
58     }
59     return 0;
60 }
61 
dup_tl_data(krb5_tl_data * tl)62 static krb5_tl_data *dup_tl_data(krb5_tl_data *tl)
63 {
64     krb5_tl_data *n;
65 
66     n = (krb5_tl_data *) malloc(sizeof(krb5_tl_data));
67     if (n == NULL)
68         return NULL;
69     n->tl_data_contents = malloc(tl->tl_data_length);
70     if (n->tl_data_contents == NULL) {
71         free(n);
72         return NULL;
73     }
74     memcpy(n->tl_data_contents, tl->tl_data_contents, tl->tl_data_length);
75     n->tl_data_type = tl->tl_data_type;
76     n->tl_data_length = tl->tl_data_length;
77     n->tl_data_next = NULL;
78     return n;
79 }
80 
81 /* This is in lib/kdb/kdb_cpw.c, but is static */
cleanup_key_data(context,count,data)82 static void cleanup_key_data(context, count, data)
83     krb5_context   context;
84     int                    count;
85     krb5_key_data        * data;
86 {
87     int i;
88 
89     for (i = 0; i < count; i++)
90         krb5_free_key_data_contents(context, &data[i]);
91     free(data);
92 }
93 
94 /* Check whether a ks_tuple is present in an array of ks_tuples. */
95 static krb5_boolean
ks_tuple_present(int n_ks_tuple,krb5_key_salt_tuple * ks_tuple,krb5_key_salt_tuple * looking_for)96 ks_tuple_present(int n_ks_tuple, krb5_key_salt_tuple *ks_tuple,
97                  krb5_key_salt_tuple *looking_for)
98 {
99     int i;
100 
101     for (i = 0; i < n_ks_tuple; i++) {
102         if (ks_tuple[i].ks_enctype == looking_for->ks_enctype &&
103             ks_tuple[i].ks_salttype == looking_for->ks_salttype)
104             return TRUE;
105     }
106     return FALSE;
107 }
108 
109 /* Fetch a policy if it exists; set *have_pol_out appropriately.  Return
110  * success whether or not the policy exists. */
111 static kadm5_ret_t
get_policy(kadm5_server_handle_t handle,const char * name,kadm5_policy_ent_t policy_out,krb5_boolean * have_pol_out)112 get_policy(kadm5_server_handle_t handle, const char *name,
113            kadm5_policy_ent_t policy_out, krb5_boolean *have_pol_out)
114 {
115     kadm5_ret_t ret;
116 
117     *have_pol_out = FALSE;
118     if (name == NULL)
119         return 0;
120     ret = kadm5_get_policy(handle->lhandle, (char *)name, policy_out);
121     if (ret == 0)
122         *have_pol_out = TRUE;
123     return (ret == KADM5_UNK_POLICY) ? 0 : ret;
124 }
125 
126 /*
127  * Apply the -allowedkeysalts policy (see kadmin(1)'s addpol/modpol
128  * commands).  We use the allowed key/salt tuple list as a default if
129  * no ks tuples as provided by the caller.  We reject lists that include
130  * key/salts outside the policy.  We re-order the requested ks tuples
131  * (which may be a subset of the policy) to reflect the policy order.
132  */
133 static kadm5_ret_t
apply_keysalt_policy(kadm5_server_handle_t handle,const char * policy,int n_ks_tuple,krb5_key_salt_tuple * ks_tuple,int * new_n_kstp,krb5_key_salt_tuple ** new_kstp)134 apply_keysalt_policy(kadm5_server_handle_t handle, const char *policy,
135                      int n_ks_tuple, krb5_key_salt_tuple *ks_tuple,
136                      int *new_n_kstp, krb5_key_salt_tuple **new_kstp)
137 {
138     kadm5_ret_t ret;
139     kadm5_policy_ent_rec polent;
140     krb5_boolean have_polent;
141     int ak_n_ks_tuple = 0;
142     int new_n_ks_tuple = 0;
143     krb5_key_salt_tuple *ak_ks_tuple = NULL;
144     krb5_key_salt_tuple *new_ks_tuple = NULL;
145     krb5_key_salt_tuple *subset;
146     int i, m;
147 
148     if (new_n_kstp != NULL) {
149         *new_n_kstp = 0;
150         *new_kstp = NULL;
151     }
152 
153     memset(&polent, 0, sizeof(polent));
154     ret = get_policy(handle, policy, &polent, &have_polent);
155     if (ret)
156         goto cleanup;
157 
158     if (polent.allowed_keysalts == NULL) {
159         /* Requested keysalts allowed or default to supported_enctypes. */
160         if (n_ks_tuple == 0) {
161             /* Default to supported_enctypes. */
162             n_ks_tuple = handle->params.num_keysalts;
163             ks_tuple = handle->params.keysalts;
164         }
165         /* Dup the requested or defaulted keysalt tuples. */
166         new_ks_tuple = malloc(n_ks_tuple * sizeof(*new_ks_tuple));
167         if (new_ks_tuple == NULL) {
168             ret = ENOMEM;
169             goto cleanup;
170         }
171         memcpy(new_ks_tuple, ks_tuple, n_ks_tuple * sizeof(*new_ks_tuple));
172         new_n_ks_tuple = n_ks_tuple;
173         ret = 0;
174         goto cleanup;
175     }
176 
177     ret = krb5_string_to_keysalts(polent.allowed_keysalts,
178                                   ",",   /* Tuple separators */
179                                   NULL,  /* Key/salt separators */
180                                   0,     /* No duplicates */
181                                   &ak_ks_tuple,
182                                   &ak_n_ks_tuple);
183     /*
184      * Malformed policy?  Shouldn't happen, but it's remotely possible
185      * someday, so we don't assert, just bail.
186      */
187     if (ret)
188         goto cleanup;
189 
190     /* Check that the requested ks_tuples are within policy, if we have one. */
191     for (i = 0; i < n_ks_tuple; i++) {
192         if (!ks_tuple_present(ak_n_ks_tuple, ak_ks_tuple, &ks_tuple[i])) {
193             ret = KADM5_BAD_KEYSALTS;
194             goto cleanup;
195         }
196     }
197 
198     /* Have policy but no ks_tuple input?  Output the policy. */
199     if (n_ks_tuple == 0) {
200         new_n_ks_tuple = ak_n_ks_tuple;
201         new_ks_tuple = ak_ks_tuple;
202         ak_ks_tuple = NULL;
203         goto cleanup;
204     }
205 
206     /*
207      * Now filter the policy ks tuples by the requested ones so as to
208      * preserve in the requested sub-set the relative ordering from the
209      * policy.  We could optimize this (if (n_ks_tuple == ak_n_ks_tuple)
210      * then skip this), but we don't bother.
211      */
212     subset = calloc(n_ks_tuple, sizeof(*subset));
213     if (subset == NULL) {
214         ret = ENOMEM;
215         goto cleanup;
216     }
217     for (m = 0, i = 0; i < ak_n_ks_tuple && m < n_ks_tuple; i++) {
218         if (ks_tuple_present(n_ks_tuple, ks_tuple, &ak_ks_tuple[i]))
219             subset[m++] = ak_ks_tuple[i];
220     }
221     new_ks_tuple = subset;
222     new_n_ks_tuple = m;
223     ret = 0;
224 
225 cleanup:
226     if (have_polent)
227         kadm5_free_policy_ent(handle->lhandle, &polent);
228     free(ak_ks_tuple);
229 
230     if (new_n_kstp != NULL) {
231         *new_n_kstp = new_n_ks_tuple;
232         *new_kstp = new_ks_tuple;
233     } else {
234         free(new_ks_tuple);
235     }
236     return ret;
237 }
238 
239 
240 /*
241  * Set *passptr to NULL if the request looks like the first part of a krb5 1.6
242  * addprinc -randkey operation.  The krb5 1.6 dummy password for these requests
243  * was invalid UTF-8, which runs afoul of the arcfour string-to-key.
244  */
245 static void
check_1_6_dummy(kadm5_principal_ent_t entry,long mask,int n_ks_tuple,krb5_key_salt_tuple * ks_tuple,char ** passptr)246 check_1_6_dummy(kadm5_principal_ent_t entry, long mask,
247                 int n_ks_tuple, krb5_key_salt_tuple *ks_tuple, char **passptr)
248 {
249     int i;
250     char *password = *passptr;
251 
252     /* Old-style randkey operations disallowed tickets to start. */
253     if (password == NULL || !(mask & KADM5_ATTRIBUTES) ||
254         !(entry->attributes & KRB5_KDB_DISALLOW_ALL_TIX))
255         return;
256 
257     /* The 1.6 dummy password was the octets 1..255. */
258     for (i = 0; (unsigned char) password[i] == i + 1; i++);
259     if (password[i] != '\0' || i != 255)
260         return;
261 
262     /* This will make the caller use a random password instead. */
263     *passptr = NULL;
264 }
265 
266 /* Return the number of keys with the newest kvno.  Assumes that all key data
267  * with the newest kvno are at the front of the key data array. */
268 static int
count_new_keys(int n_key_data,krb5_key_data * key_data)269 count_new_keys(int n_key_data, krb5_key_data *key_data)
270 {
271     int n;
272 
273     for (n = 1; n < n_key_data; n++) {
274         if (key_data[n - 1].key_data_kvno != key_data[n].key_data_kvno)
275             return n;
276     }
277     return n_key_data;
278 }
279 
280 kadm5_ret_t
kadm5_create_principal(void * server_handle,kadm5_principal_ent_t entry,long mask,char * password)281 kadm5_create_principal(void *server_handle,
282                        kadm5_principal_ent_t entry, long mask,
283                        char *password)
284 {
285     return
286         kadm5_create_principal_3(server_handle, entry, mask,
287                                  0, NULL, password);
288 }
289 kadm5_ret_t
kadm5_create_principal_3(void * server_handle,kadm5_principal_ent_t entry,long mask,int n_ks_tuple,krb5_key_salt_tuple * ks_tuple,char * password)290 kadm5_create_principal_3(void *server_handle,
291                          kadm5_principal_ent_t entry, long mask,
292                          int n_ks_tuple, krb5_key_salt_tuple *ks_tuple,
293                          char *password)
294 {
295     krb5_db_entry               *kdb;
296     osa_princ_ent_rec           adb;
297     kadm5_policy_ent_rec        polent;
298     krb5_boolean                have_polent = FALSE;
299     krb5_timestamp              now;
300     krb5_tl_data                *tl_data_tail;
301     unsigned int                ret;
302     kadm5_server_handle_t handle = server_handle;
303     krb5_keyblock               *act_mkey;
304     krb5_kvno                   act_kvno;
305     int                         new_n_ks_tuple = 0, i;
306     krb5_key_salt_tuple         *new_ks_tuple = NULL;
307 
308     CHECK_HANDLE(server_handle);
309 
310     krb5_clear_error_message(handle->context);
311 
312     check_1_6_dummy(entry, mask, n_ks_tuple, ks_tuple, &password);
313 
314     /*
315      * Argument sanity checking, and opening up the DB
316      */
317     if (entry == NULL)
318         return EINVAL;
319     if(!(mask & KADM5_PRINCIPAL) || (mask & KADM5_MOD_NAME) ||
320        (mask & KADM5_MOD_TIME) || (mask & KADM5_LAST_PWD_CHANGE) ||
321        (mask & KADM5_MKVNO) || (mask & KADM5_AUX_ATTRIBUTES) ||
322        (mask & KADM5_LAST_SUCCESS) || (mask & KADM5_LAST_FAILED) ||
323        (mask & KADM5_FAIL_AUTH_COUNT))
324         return KADM5_BAD_MASK;
325     if ((mask & KADM5_KEY_DATA) && entry->n_key_data != 0)
326         return KADM5_BAD_MASK;
327     if((mask & KADM5_POLICY) && entry->policy == NULL)
328         return KADM5_BAD_MASK;
329     if((mask & KADM5_POLICY) && (mask & KADM5_POLICY_CLR))
330         return KADM5_BAD_MASK;
331     if((mask & ~ALL_PRINC_MASK))
332         return KADM5_BAD_MASK;
333     if (mask & KADM5_TL_DATA) {
334         for (tl_data_tail = entry->tl_data; tl_data_tail != NULL;
335              tl_data_tail = tl_data_tail->tl_data_next) {
336             if (tl_data_tail->tl_data_type < 256)
337                 return KADM5_BAD_TL_TYPE;
338         }
339     }
340 
341     /*
342      * Check to see if the principal exists
343      */
344     ret = kdb_get_entry(handle, entry->principal, &kdb, &adb);
345 
346     switch(ret) {
347     case KADM5_UNK_PRINC:
348         break;
349     case 0:
350         kdb_free_entry(handle, kdb, &adb);
351         return KADM5_DUP;
352     default:
353         return ret;
354     }
355 
356     kdb = calloc(1, sizeof(*kdb));
357     if (kdb == NULL)
358         return ENOMEM;
359 
360     /* In all cases the principal entry is new and key data is set; let the
361      * database provider know. */
362     kdb->mask = mask | KADM5_KEY_DATA | KADM5_PRINCIPAL;
363 
364     memset(&adb, 0, sizeof(osa_princ_ent_rec));
365 
366     /*
367      * If a policy was specified, load it.
368      * If we can not find the one specified return an error
369      */
370     if ((mask & KADM5_POLICY)) {
371         ret = get_policy(handle, entry->policy, &polent, &have_polent);
372         if (ret)
373             goto cleanup;
374     }
375     if (password) {
376         ret = passwd_check(handle, password, have_polent ? &polent : NULL,
377                            entry->principal);
378         if (ret)
379             goto cleanup;
380     }
381     /*
382      * Start populating the various DB fields, using the
383      * "defaults" for fields that were not specified by the
384      * mask.
385      */
386     if ((ret = krb5_timeofday(handle->context, &now)))
387         goto cleanup;
388 
389     kdb->magic = KRB5_KDB_MAGIC_NUMBER;
390     kdb->len = KRB5_KDB_V1_BASE_LENGTH; /* gag me with a chainsaw */
391 
392     if ((mask & KADM5_ATTRIBUTES))
393         kdb->attributes = entry->attributes;
394     else
395         kdb->attributes = handle->params.flags;
396 
397     if ((mask & KADM5_MAX_LIFE))
398         kdb->max_life = entry->max_life;
399     else
400         kdb->max_life = handle->params.max_life;
401 
402     if (mask & KADM5_MAX_RLIFE)
403         kdb->max_renewable_life = entry->max_renewable_life;
404     else
405         kdb->max_renewable_life = handle->params.max_rlife;
406 
407     if ((mask & KADM5_PRINC_EXPIRE_TIME))
408         kdb->expiration = entry->princ_expire_time;
409     else
410         kdb->expiration = handle->params.expiration;
411 
412     kdb->pw_expiration = 0;
413     if (mask & KADM5_PW_EXPIRATION) {
414         kdb->pw_expiration = entry->pw_expiration;
415     } else if (have_polent && polent.pw_max_life) {
416         kdb->mask |= KADM5_PW_EXPIRATION;
417         kdb->pw_expiration = ts_incr(now, polent.pw_max_life);
418     }
419 
420     kdb->last_success = 0;
421     kdb->last_failed = 0;
422     kdb->fail_auth_count = 0;
423 
424     /* this is kind of gross, but in order to free the tl data, I need
425        to free the entire kdb entry, and that will try to free the
426        principal. */
427 
428     ret = krb5_copy_principal(handle->context, entry->principal, &kdb->princ);
429     if (ret)
430         goto cleanup;
431 
432     if ((ret = krb5_dbe_update_last_pwd_change(handle->context, kdb, now)))
433         goto cleanup;
434 
435     if (mask & KADM5_TL_DATA) {
436         /* splice entry->tl_data onto the front of kdb->tl_data */
437         for (tl_data_tail = entry->tl_data; tl_data_tail;
438              tl_data_tail = tl_data_tail->tl_data_next)
439         {
440             ret = krb5_dbe_update_tl_data(handle->context, kdb, tl_data_tail);
441             if( ret )
442                 goto cleanup;
443         }
444     }
445 
446     /*
447      * We need to have setup the TL data, so we have strings, so we can
448      * check enctype policy, which is why we check/initialize ks_tuple
449      * this late.
450      */
451     ret = apply_keysalt_policy(handle, entry->policy, n_ks_tuple, ks_tuple,
452                                &new_n_ks_tuple, &new_ks_tuple);
453     if (ret)
454         goto cleanup;
455 
456     /* initialize the keys */
457 
458     ret = kdb_get_active_mkey(handle, &act_kvno, &act_mkey);
459     if (ret)
460         goto cleanup;
461 
462     if (mask & KADM5_KEY_DATA) {
463         /* The client requested no keys for this principal. */
464         assert(entry->n_key_data == 0);
465     } else if (password) {
466         ret = krb5_dbe_cpw(handle->context, act_mkey, new_ks_tuple,
467                            new_n_ks_tuple, password,
468                            (mask & KADM5_KVNO)?entry->kvno:1,
469                            FALSE, kdb);
470     } else {
471         /* Null password means create with random key (new in 1.8). */
472         ret = krb5_dbe_crk(handle->context, &master_keyblock,
473                            new_ks_tuple, new_n_ks_tuple, FALSE, kdb);
474         if (mask & KADM5_KVNO) {
475             for (i = 0; i < kdb->n_key_data; i++)
476                 kdb->key_data[i].key_data_kvno = entry->kvno;
477         }
478     }
479     if (ret)
480         goto cleanup;
481 
482     /* Record the master key VNO used to encrypt this entry's keys */
483     ret = krb5_dbe_update_mkvno(handle->context, kdb, act_kvno);
484     if (ret)
485         goto cleanup;
486 
487     ret = k5_kadm5_hook_create(handle->context, handle->hook_handles,
488                                KADM5_HOOK_STAGE_PRECOMMIT, entry, mask,
489                                new_n_ks_tuple, new_ks_tuple, password);
490     if (ret)
491         goto cleanup;
492 
493     /* populate the admin-server-specific fields.  In the OV server,
494        this used to be in a separate database.  Since there's already
495        marshalling code for the admin fields, to keep things simple,
496        I'm going to keep it, and make all the admin stuff occupy a
497        single tl_data record, */
498 
499     adb.admin_history_kvno = INITIAL_HIST_KVNO;
500     if (mask & KADM5_POLICY) {
501         adb.aux_attributes = KADM5_POLICY;
502 
503         /* this does *not* need to be strdup'ed, because adb is xdr */
504         /* encoded in osa_adb_create_princ, and not ever freed */
505 
506         adb.policy = entry->policy;
507     }
508 
509     /* store the new db entry */
510     ret = kdb_put_entry(handle, kdb, &adb);
511 
512     (void) k5_kadm5_hook_create(handle->context, handle->hook_handles,
513                                 KADM5_HOOK_STAGE_POSTCOMMIT, entry, mask,
514                                 new_n_ks_tuple, new_ks_tuple, password);
515 
516 cleanup:
517     free(new_ks_tuple);
518     krb5_db_free_principal(handle->context, kdb);
519     if (have_polent)
520         (void) kadm5_free_policy_ent(handle->lhandle, &polent);
521     return ret;
522 }
523 
524 
525 kadm5_ret_t
kadm5_delete_principal(void * server_handle,krb5_principal principal)526 kadm5_delete_principal(void *server_handle, krb5_principal principal)
527 {
528     unsigned int                ret;
529     krb5_db_entry               *kdb;
530     osa_princ_ent_rec           adb;
531     kadm5_server_handle_t handle = server_handle;
532 
533     CHECK_HANDLE(server_handle);
534 
535     krb5_clear_error_message(handle->context);
536 
537     if (principal == NULL)
538         return EINVAL;
539 
540     /* Deleting K/M is mostly unrecoverable, so don't allow it. */
541     if (krb5_principal_compare(handle->context, principal, master_princ))
542         return KADM5_PROTECT_PRINCIPAL;
543 
544     if ((ret = kdb_get_entry(handle, principal, &kdb, &adb)))
545         return(ret);
546     ret = k5_kadm5_hook_remove(handle->context, handle->hook_handles,
547                                KADM5_HOOK_STAGE_PRECOMMIT, principal);
548     if (ret) {
549         kdb_free_entry(handle, kdb, &adb);
550         return ret;
551     }
552 
553     ret = kdb_delete_entry(handle, principal);
554 
555     kdb_free_entry(handle, kdb, &adb);
556 
557     if (ret == 0)
558         (void) k5_kadm5_hook_remove(handle->context,
559                                     handle->hook_handles,
560                                     KADM5_HOOK_STAGE_POSTCOMMIT, principal);
561 
562     return ret;
563 }
564 
565 kadm5_ret_t
kadm5_modify_principal(void * server_handle,kadm5_principal_ent_t entry,long mask)566 kadm5_modify_principal(void *server_handle,
567                        kadm5_principal_ent_t entry, long mask)
568 {
569     int                     ret, ret2, i;
570     kadm5_policy_ent_rec    pol;
571     krb5_boolean            have_pol = FALSE;
572     krb5_db_entry           *kdb;
573     krb5_tl_data            *tl_data_orig;
574     osa_princ_ent_rec       adb;
575     kadm5_server_handle_t handle = server_handle;
576 
577     CHECK_HANDLE(server_handle);
578 
579     krb5_clear_error_message(handle->context);
580 
581     if(entry == NULL)
582         return EINVAL;
583     if((mask & KADM5_PRINCIPAL) || (mask & KADM5_LAST_PWD_CHANGE) ||
584        (mask & KADM5_MOD_TIME) || (mask & KADM5_MOD_NAME) ||
585        (mask & KADM5_MKVNO) || (mask & KADM5_AUX_ATTRIBUTES) ||
586        (mask & KADM5_KEY_DATA) || (mask & KADM5_LAST_SUCCESS) ||
587        (mask & KADM5_LAST_FAILED))
588         return KADM5_BAD_MASK;
589     if((mask & ~ALL_PRINC_MASK))
590         return KADM5_BAD_MASK;
591     if((mask & KADM5_POLICY) && entry->policy == NULL)
592         return KADM5_BAD_MASK;
593     if((mask & KADM5_POLICY) && (mask & KADM5_POLICY_CLR))
594         return KADM5_BAD_MASK;
595     if (mask & KADM5_TL_DATA) {
596         tl_data_orig = entry->tl_data;
597         while (tl_data_orig) {
598             if (tl_data_orig->tl_data_type < 256)
599                 return KADM5_BAD_TL_TYPE;
600             tl_data_orig = tl_data_orig->tl_data_next;
601         }
602     }
603 
604     ret = kdb_get_entry(handle, entry->principal, &kdb, &adb);
605     if (ret)
606         return(ret);
607 
608     /* Let the mask propagate to the database provider. */
609     kdb->mask = mask;
610 
611     /*
612      * This is pretty much the same as create ...
613      */
614 
615     if ((mask & KADM5_POLICY)) {
616         ret = get_policy(handle, entry->policy, &pol, &have_pol);
617         if (ret)
618             goto done;
619 
620         /* set us up to use the new policy */
621         adb.aux_attributes |= KADM5_POLICY;
622         if (adb.policy)
623             free(adb.policy);
624         adb.policy = strdup(entry->policy);
625     }
626 
627     if (mask & KADM5_PW_EXPIRATION) {
628         kdb->pw_expiration = entry->pw_expiration;
629     } else if (have_pol) {
630         /* set pw_max_life based on new policy */
631         kdb->mask |= KADM5_PW_EXPIRATION;
632         if (pol.pw_max_life) {
633             ret = krb5_dbe_lookup_last_pwd_change(handle->context, kdb,
634                                                   &kdb->pw_expiration);
635             if (ret)
636                 goto done;
637             kdb->pw_expiration = ts_incr(kdb->pw_expiration, pol.pw_max_life);
638         } else {
639             kdb->pw_expiration = 0;
640         }
641     }
642 
643     if ((mask & KADM5_POLICY_CLR) && (adb.aux_attributes & KADM5_POLICY)) {
644         free(adb.policy);
645         adb.policy = NULL;
646         adb.aux_attributes &= ~KADM5_POLICY;
647         kdb->pw_expiration = 0;
648     }
649 
650     if ((mask & KADM5_ATTRIBUTES))
651         kdb->attributes = entry->attributes;
652     if ((mask & KADM5_MAX_LIFE))
653         kdb->max_life = entry->max_life;
654     if ((mask & KADM5_PRINC_EXPIRE_TIME))
655         kdb->expiration = entry->princ_expire_time;
656     if (mask & KADM5_MAX_RLIFE)
657         kdb->max_renewable_life = entry->max_renewable_life;
658 
659     if((mask & KADM5_KVNO)) {
660         for (i = 0; i < kdb->n_key_data; i++)
661             kdb->key_data[i].key_data_kvno = entry->kvno;
662     }
663 
664     if (mask & KADM5_TL_DATA) {
665         krb5_tl_data *tl;
666 
667         /* may have to change the version number of the API. Updates the list with the given tl_data rather than over-writing */
668 
669         for (tl = entry->tl_data; tl;
670              tl = tl->tl_data_next)
671         {
672             ret = krb5_dbe_update_tl_data(handle->context, kdb, tl);
673             if( ret )
674             {
675                 goto done;
676             }
677         }
678     }
679 
680     /*
681      * Setting entry->fail_auth_count to 0 can be used to manually unlock
682      * an account. It is not possible to set fail_auth_count to any other
683      * value using kadmin.
684      */
685     if (mask & KADM5_FAIL_AUTH_COUNT) {
686         if (entry->fail_auth_count != 0) {
687             ret = KADM5_BAD_SERVER_PARAMS;
688             goto done;
689         }
690 
691         kdb->fail_auth_count = 0;
692     }
693 
694     ret = k5_kadm5_hook_modify(handle->context, handle->hook_handles,
695                                KADM5_HOOK_STAGE_PRECOMMIT, entry, mask);
696     if (ret)
697         goto done;
698 
699     ret = kdb_put_entry(handle, kdb, &adb);
700     if (ret) goto done;
701     (void) k5_kadm5_hook_modify(handle->context, handle->hook_handles,
702                                 KADM5_HOOK_STAGE_POSTCOMMIT, entry, mask);
703 
704     ret = KADM5_OK;
705 done:
706     if (have_pol) {
707         ret2 = kadm5_free_policy_ent(handle->lhandle, &pol);
708         ret = ret ? ret : ret2;
709     }
710     kdb_free_entry(handle, kdb, &adb);
711     return ret;
712 }
713 
714 kadm5_ret_t
kadm5_rename_principal(void * server_handle,krb5_principal source,krb5_principal target)715 kadm5_rename_principal(void *server_handle,
716                        krb5_principal source, krb5_principal target)
717 {
718     krb5_db_entry *kdb;
719     osa_princ_ent_rec adb;
720     krb5_error_code ret;
721     kadm5_server_handle_t handle = server_handle;
722 
723     CHECK_HANDLE(server_handle);
724 
725     krb5_clear_error_message(handle->context);
726 
727     if (source == NULL || target == NULL)
728         return EINVAL;
729 
730     if ((ret = kdb_get_entry(handle, target, &kdb, &adb)) == 0) {
731         kdb_free_entry(handle, kdb, &adb);
732         return(KADM5_DUP);
733     }
734 
735     ret = k5_kadm5_hook_rename(handle->context, handle->hook_handles,
736                                KADM5_HOOK_STAGE_PRECOMMIT, source, target);
737     if (ret)
738         return ret;
739 
740     ret = krb5_db_rename_principal(handle->context, source, target);
741     if (ret)
742         return ret;
743 
744     /* Update the principal mod data. */
745     ret = kdb_get_entry(handle, target, &kdb, &adb);
746     if (ret)
747         return ret;
748     kdb->mask = 0;
749     ret = kdb_put_entry(handle, kdb, &adb);
750     kdb_free_entry(handle, kdb, &adb);
751     if (ret)
752         return ret;
753 
754     (void) k5_kadm5_hook_rename(handle->context, handle->hook_handles,
755                                 KADM5_HOOK_STAGE_POSTCOMMIT, source, target);
756     return 0;
757 }
758 
759 kadm5_ret_t
kadm5_get_principal(void * server_handle,krb5_principal principal,kadm5_principal_ent_t entry,long in_mask)760 kadm5_get_principal(void *server_handle, krb5_principal principal,
761                     kadm5_principal_ent_t entry,
762                     long in_mask)
763 {
764     krb5_db_entry               *kdb;
765     osa_princ_ent_rec           adb;
766     krb5_error_code             ret = 0;
767     long                        mask;
768     int i;
769     kadm5_server_handle_t handle = server_handle;
770 
771     CHECK_HANDLE(server_handle);
772 
773     krb5_clear_error_message(handle->context);
774 
775     /*
776      * In version 1, all the defined fields are always returned.
777      * entry is a pointer to a kadm5_principal_ent_t_v1 that should be
778      * filled with allocated memory.
779      */
780     mask = in_mask;
781 
782     memset(entry, 0, sizeof(*entry));
783 
784     if (principal == NULL)
785         return EINVAL;
786 
787     if ((ret = kdb_get_entry(handle, principal, &kdb, &adb)))
788         return ret;
789 
790     if ((mask & KADM5_POLICY) &&
791         adb.policy && (adb.aux_attributes & KADM5_POLICY)) {
792         if ((entry->policy = strdup(adb.policy)) == NULL) {
793             ret = ENOMEM;
794             goto done;
795         }
796     }
797 
798     if (mask & KADM5_AUX_ATTRIBUTES)
799         entry->aux_attributes = adb.aux_attributes;
800 
801     if ((mask & KADM5_PRINCIPAL) &&
802         (ret = krb5_copy_principal(handle->context, kdb->princ,
803                                    &entry->principal))) {
804         goto done;
805     }
806 
807     if (mask & KADM5_PRINC_EXPIRE_TIME)
808         entry->princ_expire_time = kdb->expiration;
809 
810     if ((mask & KADM5_LAST_PWD_CHANGE) &&
811         (ret = krb5_dbe_lookup_last_pwd_change(handle->context, kdb,
812                                                &(entry->last_pwd_change)))) {
813         goto done;
814     }
815 
816     if (mask & KADM5_PW_EXPIRATION)
817         entry->pw_expiration = kdb->pw_expiration;
818     if (mask & KADM5_MAX_LIFE)
819         entry->max_life = kdb->max_life;
820 
821     /* this is a little non-sensical because the function returns two */
822     /* values that must be checked separately against the mask */
823     if ((mask & KADM5_MOD_NAME) || (mask & KADM5_MOD_TIME)) {
824         ret = krb5_dbe_lookup_mod_princ_data(handle->context, kdb,
825                                              &(entry->mod_date),
826                                              &(entry->mod_name));
827         if (ret) {
828             goto done;
829         }
830 
831         if (! (mask & KADM5_MOD_TIME))
832             entry->mod_date = 0;
833         if (! (mask & KADM5_MOD_NAME)) {
834             krb5_free_principal(handle->context, entry->mod_name);
835             entry->mod_name = NULL;
836         }
837     }
838 
839     if (mask & KADM5_ATTRIBUTES)
840         entry->attributes = kdb->attributes;
841 
842     if (mask & KADM5_KVNO)
843         for (entry->kvno = 0, i=0; i<kdb->n_key_data; i++)
844             if ((krb5_kvno) kdb->key_data[i].key_data_kvno > entry->kvno)
845                 entry->kvno = kdb->key_data[i].key_data_kvno;
846 
847     if (mask & KADM5_MKVNO) {
848         ret = krb5_dbe_get_mkvno(handle->context, kdb, &entry->mkvno);
849         if (ret)
850             goto done;
851     }
852 
853     if (mask & KADM5_MAX_RLIFE)
854         entry->max_renewable_life = kdb->max_renewable_life;
855     if (mask & KADM5_LAST_SUCCESS)
856         entry->last_success = kdb->last_success;
857     if (mask & KADM5_LAST_FAILED)
858         entry->last_failed = kdb->last_failed;
859     if (mask & KADM5_FAIL_AUTH_COUNT)
860         entry->fail_auth_count = kdb->fail_auth_count;
861     if (mask & KADM5_TL_DATA) {
862         krb5_tl_data *tl, *tl2;
863 
864         entry->tl_data = NULL;
865 
866         tl = kdb->tl_data;
867         while (tl) {
868             if (tl->tl_data_type > 255) {
869                 if ((tl2 = dup_tl_data(tl)) == NULL) {
870                     ret = ENOMEM;
871                     goto done;
872                 }
873                 tl2->tl_data_next = entry->tl_data;
874                 entry->tl_data = tl2;
875                 entry->n_tl_data++;
876             }
877 
878             tl = tl->tl_data_next;
879         }
880     }
881     if (mask & KADM5_KEY_DATA) {
882         entry->n_key_data = kdb->n_key_data;
883         if(entry->n_key_data) {
884             entry->key_data = k5calloc(entry->n_key_data,
885                                        sizeof(krb5_key_data), &ret);
886             if (entry->key_data == NULL)
887                 goto done;
888         } else
889             entry->key_data = NULL;
890 
891         for (i = 0; i < entry->n_key_data; i++)
892             ret = krb5_copy_key_data_contents(handle->context,
893                                               &kdb->key_data[i],
894                                               &entry->key_data[i]);
895         if (ret)
896             goto done;
897     }
898 
899     ret = KADM5_OK;
900 
901 done:
902     if (ret && entry->principal) {
903         krb5_free_principal(handle->context, entry->principal);
904         entry->principal = NULL;
905     }
906     kdb_free_entry(handle, kdb, &adb);
907 
908     return ret;
909 }
910 
911 /*
912  * Function: check_pw_reuse
913  *
914  * Purpose: Check if a key appears in a list of keys, in order to
915  * enforce password history.
916  *
917  * Arguments:
918  *
919  *      context                 (r) the krb5 context
920  *      hist_keyblock           (r) the key that hist_key_data is
921  *                              encrypted in
922  *      n_new_key_data          (r) length of new_key_data
923  *      new_key_data            (r) keys to check against
924  *                              pw_hist_data, encrypted in hist_keyblock
925  *      n_pw_hist_data          (r) length of pw_hist_data
926  *      pw_hist_data            (r) passwords to check new_key_data against
927  *
928  * Effects:
929  * For each new_key in new_key_data:
930  *      decrypt new_key with the master_keyblock
931  *      for each password in pw_hist_data:
932  *              for each hist_key in password:
933  *                      decrypt hist_key with hist_keyblock
934  *                      compare the new_key and hist_key
935  *
936  * Returns krb5 errors, KADM5_PASS_RESUSE if a key in
937  * new_key_data is the same as a key in pw_hist_data, or 0.
938  */
939 static kadm5_ret_t
check_pw_reuse(krb5_context context,krb5_keyblock * hist_keyblocks,int n_new_key_data,krb5_key_data * new_key_data,unsigned int n_pw_hist_data,osa_pw_hist_ent * pw_hist_data)940 check_pw_reuse(krb5_context context,
941                krb5_keyblock *hist_keyblocks,
942                int n_new_key_data, krb5_key_data *new_key_data,
943                unsigned int n_pw_hist_data, osa_pw_hist_ent *pw_hist_data)
944 {
945     unsigned int x, y, z;
946     krb5_keyblock newkey, histkey, *kb;
947     krb5_key_data *key_data;
948     krb5_error_code ret;
949 
950     assert (n_new_key_data >= 0);
951     for (x = 0; x < (unsigned) n_new_key_data; x++) {
952         /* Check only entries with the most recent kvno. */
953         if (new_key_data[x].key_data_kvno != new_key_data[0].key_data_kvno)
954             break;
955         ret = krb5_dbe_decrypt_key_data(context, NULL, &(new_key_data[x]),
956                                         &newkey, NULL);
957         if (ret)
958             return(ret);
959         for (y = 0; y < n_pw_hist_data; y++) {
960             for (z = 0; z < (unsigned int) pw_hist_data[y].n_key_data; z++) {
961                 for (kb = hist_keyblocks; kb->enctype != 0; kb++) {
962                     key_data = &pw_hist_data[y].key_data[z];
963                     ret = krb5_dbe_decrypt_key_data(context, kb, key_data,
964                                                     &histkey, NULL);
965                     if (ret)
966                         continue;
967                     if (newkey.length == histkey.length &&
968                         newkey.enctype == histkey.enctype &&
969                         memcmp(newkey.contents, histkey.contents,
970                                histkey.length) == 0) {
971                         krb5_free_keyblock_contents(context, &histkey);
972                         krb5_free_keyblock_contents(context, &newkey);
973                         return KADM5_PASS_REUSE;
974                     }
975                     krb5_free_keyblock_contents(context, &histkey);
976                 }
977             }
978         }
979         krb5_free_keyblock_contents(context, &newkey);
980     }
981 
982     return(0);
983 }
984 
985 static void
free_history_entry(krb5_context context,osa_pw_hist_ent * hist)986 free_history_entry(krb5_context context, osa_pw_hist_ent *hist)
987 {
988     int i;
989 
990     for (i = 0; i < hist->n_key_data; i++)
991         krb5_free_key_data_contents(context, &hist->key_data[i]);
992     free(hist->key_data);
993 }
994 
995 /*
996  * Function: create_history_entry
997  *
998  * Purpose: Creates a password history entry from an array of
999  * key_data.
1000  *
1001  * Arguments:
1002  *
1003  *      context         (r) krb5_context to use
1004  *      mkey            (r) master keyblock to decrypt key data with
1005  *      hist_key        (r) history keyblock to encrypt key data with
1006  *      n_key_data      (r) number of elements in key_data
1007  *      key_data        (r) keys to add to the history entry
1008  *      hist_out        (w) history entry to fill in
1009  *
1010  * Effects:
1011  *
1012  * hist->key_data is allocated to store n_key_data key_datas.  Each
1013  * element of key_data is decrypted with master_keyblock, re-encrypted
1014  * in hist_key, and added to hist->key_data.  hist->n_key_data is
1015  * set to n_key_data.
1016  */
1017 static
create_history_entry(krb5_context context,krb5_keyblock * hist_key,int n_key_data,krb5_key_data * key_data,osa_pw_hist_ent * hist_out)1018 int create_history_entry(krb5_context context,
1019                          krb5_keyblock *hist_key, int n_key_data,
1020                          krb5_key_data *key_data, osa_pw_hist_ent *hist_out)
1021 {
1022     int i;
1023     krb5_error_code ret = 0;
1024     krb5_keyblock key;
1025     krb5_keysalt salt;
1026     krb5_ui_2 kvno;
1027     osa_pw_hist_ent hist;
1028 
1029     hist_out->key_data = NULL;
1030     hist_out->n_key_data = 0;
1031 
1032     if (n_key_data < 0)
1033         return EINVAL;
1034 
1035     memset(&key, 0, sizeof(key));
1036     memset(&hist, 0, sizeof(hist));
1037 
1038     if (n_key_data == 0)
1039         goto cleanup;
1040 
1041     hist.key_data = k5calloc(n_key_data, sizeof(krb5_key_data), &ret);
1042     if (hist.key_data == NULL)
1043         goto cleanup;
1044 
1045     /* We only want to store the most recent kvno, and key_data should already
1046      * be sorted in descending order by kvno. */
1047     kvno = key_data[0].key_data_kvno;
1048 
1049     for (i = 0; i < n_key_data; i++) {
1050         if (key_data[i].key_data_kvno < kvno)
1051             break;
1052         ret = krb5_dbe_decrypt_key_data(context, NULL,
1053                                         &key_data[i], &key,
1054                                         &salt);
1055         if (ret)
1056             goto cleanup;
1057 
1058         ret = krb5_dbe_encrypt_key_data(context, hist_key, &key, &salt,
1059                                         key_data[i].key_data_kvno,
1060                                         &hist.key_data[hist.n_key_data]);
1061         if (ret)
1062             goto cleanup;
1063         hist.n_key_data++;
1064         krb5_free_keyblock_contents(context, &key);
1065         /* krb5_free_keysalt(context, &salt); */
1066     }
1067 
1068     *hist_out = hist;
1069     hist.n_key_data = 0;
1070     hist.key_data = NULL;
1071 
1072 cleanup:
1073     krb5_free_keyblock_contents(context, &key);
1074     free_history_entry(context, &hist);
1075     return ret;
1076 }
1077 
1078 /*
1079  * Function: add_to_history
1080  *
1081  * Purpose: Adds a password to a principal's password history.
1082  *
1083  * Arguments:
1084  *
1085  *      context         (r) krb5_context to use
1086  *      hist_kvno       (r) kvno of current history key
1087  *      adb             (r/w) admin principal entry to add keys to
1088  *      pol             (r) adb's policy
1089  *      pw              (r) keys for the password to add to adb's key history
1090  *
1091  * Effects:
1092  *
1093  * add_to_history adds a single password to adb's password history.
1094  * pw contains n_key_data keys in its key_data, in storage should be
1095  * allocated but not freed by the caller (XXX blech!).
1096  *
1097  * This function maintains adb->old_keys as a circular queue.  It
1098  * starts empty, and grows each time this function is called until it
1099  * is pol->pw_history_num items long.  adb->old_key_len holds the
1100  * number of allocated entries in the array, and must therefore be [0,
1101  * pol->pw_history_num).  adb->old_key_next is the index into the
1102  * array where the next element should be written, and must be [0,
1103  * adb->old_key_len).
1104  */
add_to_history(krb5_context context,krb5_kvno hist_kvno,osa_princ_ent_t adb,kadm5_policy_ent_t pol,osa_pw_hist_ent * pw)1105 static kadm5_ret_t add_to_history(krb5_context context,
1106                                   krb5_kvno hist_kvno,
1107                                   osa_princ_ent_t adb,
1108                                   kadm5_policy_ent_t pol,
1109                                   osa_pw_hist_ent *pw)
1110 {
1111     osa_pw_hist_ent *histp;
1112     uint32_t nhist;
1113     unsigned int i, knext, nkeys;
1114 
1115     nhist = pol->pw_history_num;
1116     /* A history of 1 means just check the current password */
1117     if (nhist <= 1)
1118         return 0;
1119 
1120     if (adb->admin_history_kvno != hist_kvno) {
1121         /* The history key has changed since the last password change, so we
1122          * have to reset the password history. */
1123         free(adb->old_keys);
1124         adb->old_keys = NULL;
1125         adb->old_key_len = 0;
1126         adb->old_key_next = 0;
1127         adb->admin_history_kvno = hist_kvno;
1128     }
1129 
1130     nkeys = adb->old_key_len;
1131     knext = adb->old_key_next;
1132     /* resize the adb->old_keys array if necessary */
1133     if (nkeys + 1 < nhist) {
1134         if (adb->old_keys == NULL) {
1135             adb->old_keys = (osa_pw_hist_ent *)
1136                 malloc((nkeys + 1) * sizeof (osa_pw_hist_ent));
1137         } else {
1138             adb->old_keys = (osa_pw_hist_ent *)
1139                 realloc(adb->old_keys,
1140                         (nkeys + 1) * sizeof (osa_pw_hist_ent));
1141         }
1142         if (adb->old_keys == NULL)
1143             return(ENOMEM);
1144 
1145         memset(&adb->old_keys[nkeys], 0, sizeof(osa_pw_hist_ent));
1146         nkeys = ++adb->old_key_len;
1147         /*
1148          * To avoid losing old keys, shift forward each entry after
1149          * knext.
1150          */
1151         for (i = nkeys - 1; i > knext; i--) {
1152             adb->old_keys[i] = adb->old_keys[i - 1];
1153         }
1154         memset(&adb->old_keys[knext], 0, sizeof(osa_pw_hist_ent));
1155     } else if (nkeys + 1 > nhist) {
1156         /*
1157          * The policy must have changed!  Shrink the array.
1158          * Can't simply realloc() down, since it might be wrapped.
1159          * To understand the arithmetic below, note that we are
1160          * copying into new positions 0 .. N-1 from old positions
1161          * old_key_next-N .. old_key_next-1, modulo old_key_len,
1162          * where N = pw_history_num - 1 is the length of the
1163          * shortened list.        Matt Crawford, FNAL
1164          */
1165         /*
1166          * M = adb->old_key_len, N = pol->pw_history_num - 1
1167          *
1168          * tmp[0] .. tmp[N-1] = old[(knext-N)%M] .. old[(knext-1)%M]
1169          */
1170         int j;
1171         osa_pw_hist_t tmp;
1172 
1173         tmp = (osa_pw_hist_ent *)
1174             malloc((nhist - 1) * sizeof (osa_pw_hist_ent));
1175         if (tmp == NULL)
1176             return ENOMEM;
1177         for (i = 0; i < nhist - 1; i++) {
1178             /*
1179              * Add nkeys once before taking remainder to avoid
1180              * negative values.
1181              */
1182             j = (i + nkeys + knext - (nhist - 1)) % nkeys;
1183             tmp[i] = adb->old_keys[j];
1184         }
1185         /* Now free the ones we don't keep (the oldest ones) */
1186         for (i = 0; i < nkeys - (nhist - 1); i++) {
1187             j = (i + nkeys + knext) % nkeys;
1188             histp = &adb->old_keys[j];
1189             for (j = 0; j < histp->n_key_data; j++) {
1190                 krb5_free_key_data_contents(context, &histp->key_data[j]);
1191             }
1192             free(histp->key_data);
1193         }
1194         free(adb->old_keys);
1195         adb->old_keys = tmp;
1196         nkeys = adb->old_key_len = nhist - 1;
1197         knext = adb->old_key_next = 0;
1198     }
1199 
1200     /*
1201      * If nhist decreased since the last password change, and nkeys+1
1202      * is less than the previous nhist, it is possible for knext to
1203      * index into unallocated space.  This condition would not be
1204      * caught by the resizing code above.
1205      */
1206     if (knext + 1 > nkeys)
1207         knext = adb->old_key_next = 0;
1208     /* free the old pw history entry if it contains data */
1209     histp = &adb->old_keys[knext];
1210     for (i = 0; i < (unsigned int) histp->n_key_data; i++)
1211         krb5_free_key_data_contents(context, &histp->key_data[i]);
1212     free(histp->key_data);
1213 
1214     /* store the new entry */
1215     adb->old_keys[knext] = *pw;
1216 
1217     /* update the next pointer */
1218     if (++adb->old_key_next == nhist - 1)
1219         adb->old_key_next = 0;
1220 
1221     return(0);
1222 }
1223 
1224 /* FIXME: don't use global variable for this */
1225 krb5_boolean use_password_server = 0;
1226 
1227 #ifdef USE_PASSWORD_SERVER
1228 static krb5_boolean
kadm5_use_password_server(void)1229 kadm5_use_password_server (void)
1230 {
1231     return use_password_server;
1232 }
1233 #endif
1234 
1235 void kadm5_set_use_password_server (void);
1236 
1237 void
kadm5_set_use_password_server(void)1238 kadm5_set_use_password_server (void)
1239 {
1240     use_password_server = 1;
1241 }
1242 
1243 #ifdef USE_PASSWORD_SERVER
1244 
1245 /*
1246  * kadm5_launch_task () runs a program (task_path) to synchronize the
1247  * Apple password server with the Kerberos database.  Password server
1248  * programs can receive arguments on the command line (task_argv)
1249  * and a block of data via stdin (data_buffer).
1250  *
1251  * Because a failure to communicate with the tool results in the
1252  * password server falling out of sync with the database,
1253  * kadm5_launch_task() always fails if it can't talk to the tool.
1254  */
1255 
1256 static kadm5_ret_t
kadm5_launch_task(krb5_context context,const char * task_path,char * const task_argv[],const char * buffer)1257 kadm5_launch_task (krb5_context context,
1258                    const char *task_path, char * const task_argv[],
1259                    const char *buffer)
1260 {
1261     kadm5_ret_t ret;
1262     int data_pipe[2];
1263 
1264     ret = pipe (data_pipe);
1265     if (ret)
1266         ret = errno;
1267 
1268     if (!ret) {
1269         pid_t pid = fork ();
1270         if (pid == -1) {
1271             ret = errno;
1272             close (data_pipe[0]);
1273             close (data_pipe[1]);
1274         } else if (pid == 0) {
1275             /* The child: */
1276 
1277             if (dup2 (data_pipe[0], STDIN_FILENO) == -1)
1278                 _exit (1);
1279 
1280             close (data_pipe[0]);
1281             close (data_pipe[1]);
1282 
1283             execv (task_path, task_argv);
1284 
1285             _exit (1); /* Fail if execv fails */
1286         } else {
1287             /* The parent: */
1288             int status;
1289 
1290             ret = 0;
1291 
1292             close (data_pipe[0]);
1293 
1294             /* Write out the buffer to the child, add \n */
1295             if (buffer) {
1296                 if (krb5_net_write (context, data_pipe[1], buffer, strlen (buffer)) < 0
1297                     || krb5_net_write (context, data_pipe[1], "\n", 1) < 0)
1298                 {
1299                     /* kill the child to make sure waitpid() won't hang later */
1300                     ret = errno;
1301                     kill (pid, SIGKILL);
1302                 }
1303             }
1304             close (data_pipe[1]);
1305 
1306             waitpid (pid, &status, 0);
1307 
1308             if (!ret) {
1309                 if (WIFEXITED (status)) {
1310                     /* child read password and exited.  Check the return value. */
1311                     if ((WEXITSTATUS (status) != 0) && (WEXITSTATUS (status) != 252)) {
1312                         ret = KRB5KDC_ERR_POLICY; /* password change rejected */
1313                     }
1314                 } else {
1315                     /* child read password but crashed or was killed */
1316                     ret = KRB5KRB_ERR_GENERIC; /* FIXME: better error */
1317                 }
1318             }
1319         }
1320     }
1321 
1322     return ret;
1323 }
1324 
1325 #endif
1326 
1327 kadm5_ret_t
kadm5_chpass_principal(void * server_handle,krb5_principal principal,char * password)1328 kadm5_chpass_principal(void *server_handle,
1329                        krb5_principal principal, char *password)
1330 {
1331     return
1332         kadm5_chpass_principal_3(server_handle, principal, FALSE,
1333                                  0, NULL, password);
1334 }
1335 
1336 kadm5_ret_t
kadm5_chpass_principal_3(void * server_handle,krb5_principal principal,krb5_boolean keepold,int n_ks_tuple,krb5_key_salt_tuple * ks_tuple,char * password)1337 kadm5_chpass_principal_3(void *server_handle,
1338                          krb5_principal principal, krb5_boolean keepold,
1339                          int n_ks_tuple, krb5_key_salt_tuple *ks_tuple,
1340                          char *password)
1341 {
1342     krb5_timestamp              now;
1343     kadm5_policy_ent_rec        pol;
1344     osa_princ_ent_rec           adb;
1345     krb5_db_entry               *kdb;
1346     int                         ret, ret2, hist_added;
1347     krb5_boolean                have_pol = FALSE;
1348     kadm5_server_handle_t       handle = server_handle;
1349     osa_pw_hist_ent             hist;
1350     krb5_keyblock               *act_mkey, *hist_keyblocks = NULL;
1351     krb5_kvno                   act_kvno, hist_kvno;
1352     int                         new_n_ks_tuple = 0;
1353     krb5_key_salt_tuple         *new_ks_tuple = NULL;
1354 
1355     CHECK_HANDLE(server_handle);
1356 
1357     krb5_clear_error_message(handle->context);
1358 
1359     hist_added = 0;
1360     memset(&hist, 0, sizeof(hist));
1361 
1362     if (principal == NULL || password == NULL)
1363         return EINVAL;
1364     if ((krb5_principal_compare(handle->context,
1365                                 principal, hist_princ)) == TRUE)
1366         return KADM5_PROTECT_PRINCIPAL;
1367 
1368     if ((ret = kdb_get_entry(handle, principal, &kdb, &adb)))
1369         return(ret);
1370 
1371     /* We will always be changing the key data, attributes, auth failure count,
1372      * and password expiration time. */
1373     kdb->mask = KADM5_KEY_DATA | KADM5_ATTRIBUTES | KADM5_FAIL_AUTH_COUNT |
1374         KADM5_PW_EXPIRATION;
1375 
1376     ret = apply_keysalt_policy(handle, adb.policy, n_ks_tuple, ks_tuple,
1377                                &new_n_ks_tuple, &new_ks_tuple);
1378     if (ret)
1379         goto done;
1380 
1381     if ((adb.aux_attributes & KADM5_POLICY)) {
1382         ret = get_policy(handle, adb.policy, &pol, &have_pol);
1383         if (ret)
1384             goto done;
1385     }
1386     if (have_pol) {
1387         /* Create a password history entry before we change kdb's key_data. */
1388         ret = kdb_get_hist_key(handle, &hist_keyblocks, &hist_kvno);
1389         if (ret)
1390             goto done;
1391         ret = create_history_entry(handle->context, &hist_keyblocks[0],
1392                                    kdb->n_key_data, kdb->key_data, &hist);
1393         if (ret)
1394             goto done;
1395     }
1396 
1397     if ((ret = passwd_check(handle, password, have_pol ? &pol : NULL,
1398                             principal)))
1399         goto done;
1400 
1401     ret = kdb_get_active_mkey(handle, &act_kvno, &act_mkey);
1402     if (ret)
1403         goto done;
1404 
1405     ret = krb5_dbe_cpw(handle->context, act_mkey, new_ks_tuple, new_n_ks_tuple,
1406                        password, 0 /* increment kvno */,
1407                        keepold, kdb);
1408     if (ret)
1409         goto done;
1410 
1411     ret = krb5_dbe_update_mkvno(handle->context, kdb, act_kvno);
1412     if (ret)
1413         goto done;
1414 
1415     kdb->attributes &= ~KRB5_KDB_REQUIRES_PWCHANGE;
1416 
1417     ret = krb5_timeofday(handle->context, &now);
1418     if (ret)
1419         goto done;
1420 
1421     kdb->pw_expiration = 0;
1422     if ((adb.aux_attributes & KADM5_POLICY)) {
1423         /* the policy was loaded before */
1424 
1425         ret = check_pw_reuse(handle->context, hist_keyblocks,
1426                              kdb->n_key_data, kdb->key_data,
1427                              1, &hist);
1428         if (ret)
1429             goto done;
1430 
1431         if (pol.pw_history_num > 1) {
1432             /* If hist_kvno has changed since the last password change, we
1433              * can't check the history. */
1434             if (adb.admin_history_kvno == hist_kvno) {
1435                 ret = check_pw_reuse(handle->context, hist_keyblocks,
1436                                      kdb->n_key_data, kdb->key_data,
1437                                      adb.old_key_len, adb.old_keys);
1438                 if (ret)
1439                     goto done;
1440             }
1441 
1442             /* Don't save empty history. */
1443             if (hist.n_key_data > 0) {
1444                 ret = add_to_history(handle->context, hist_kvno, &adb, &pol,
1445                                      &hist);
1446                 if (ret)
1447                     goto done;
1448                 hist_added = 1;
1449             }
1450         }
1451 
1452         if (pol.pw_max_life)
1453             kdb->pw_expiration = ts_incr(now, pol.pw_max_life);
1454     }
1455 
1456 #ifdef USE_PASSWORD_SERVER
1457     if (kadm5_use_password_server () &&
1458         (krb5_princ_size (handle->context, principal) == 1)) {
1459         krb5_data *princ = krb5_princ_component (handle->context, principal, 0);
1460         const char *path = "/usr/sbin/mkpassdb";
1461         char *argv[] = { "mkpassdb", "-setpassword", NULL, NULL };
1462         char *pstring = NULL;
1463 
1464         if (!ret) {
1465             pstring = malloc ((princ->length + 1) * sizeof (char));
1466             if (pstring == NULL) { ret = ENOMEM; }
1467         }
1468 
1469         if (!ret) {
1470             memcpy (pstring, princ->data, princ->length);
1471             pstring [princ->length] = '\0';
1472             argv[2] = pstring;
1473 
1474             ret = kadm5_launch_task (handle->context, path, argv, password);
1475         }
1476 
1477         if (pstring != NULL)
1478             free (pstring);
1479 
1480         if (ret)
1481             goto done;
1482     }
1483 #endif
1484 
1485     ret = krb5_dbe_update_last_pwd_change(handle->context, kdb, now);
1486     if (ret)
1487         goto done;
1488 
1489     /* unlock principal on this KDC */
1490     kdb->fail_auth_count = 0;
1491 
1492     if (hist_added)
1493         kdb->mask |= KADM5_KEY_HIST;
1494 
1495     ret = k5_kadm5_hook_chpass(handle->context, handle->hook_handles,
1496                                KADM5_HOOK_STAGE_PRECOMMIT, principal, keepold,
1497                                new_n_ks_tuple, new_ks_tuple, password);
1498     if (ret)
1499         goto done;
1500 
1501     if ((ret = kdb_put_entry(handle, kdb, &adb)))
1502         goto done;
1503 
1504     (void) k5_kadm5_hook_chpass(handle->context, handle->hook_handles,
1505                                 KADM5_HOOK_STAGE_POSTCOMMIT, principal,
1506                                 keepold, new_n_ks_tuple, new_ks_tuple, password);
1507     ret = KADM5_OK;
1508 done:
1509     free(new_ks_tuple);
1510     if (!hist_added && hist.key_data)
1511         free_history_entry(handle->context, &hist);
1512     kdb_free_entry(handle, kdb, &adb);
1513     kdb_free_keyblocks(handle, hist_keyblocks);
1514 
1515     if (have_pol && (ret2 = kadm5_free_policy_ent(handle->lhandle, &pol))
1516         && !ret)
1517         ret = ret2;
1518 
1519     return ret;
1520 }
1521 
1522 kadm5_ret_t
kadm5_randkey_principal(void * server_handle,krb5_principal principal,krb5_keyblock ** keyblocks,int * n_keys)1523 kadm5_randkey_principal(void *server_handle,
1524                         krb5_principal principal,
1525                         krb5_keyblock **keyblocks,
1526                         int *n_keys)
1527 {
1528     return
1529         kadm5_randkey_principal_3(server_handle, principal,
1530                                   FALSE, 0, NULL,
1531                                   keyblocks, n_keys);
1532 }
1533 kadm5_ret_t
kadm5_randkey_principal_3(void * server_handle,krb5_principal principal,krb5_boolean keepold,int n_ks_tuple,krb5_key_salt_tuple * ks_tuple,krb5_keyblock ** keyblocks,int * n_keys)1534 kadm5_randkey_principal_3(void *server_handle,
1535                           krb5_principal principal,
1536                           krb5_boolean keepold,
1537                           int n_ks_tuple, krb5_key_salt_tuple *ks_tuple,
1538                           krb5_keyblock **keyblocks,
1539                           int *n_keys)
1540 {
1541     krb5_db_entry               *kdb;
1542     osa_princ_ent_rec           adb;
1543     krb5_timestamp              now;
1544     kadm5_policy_ent_rec        pol;
1545     int                         ret, n_new_keys;
1546     krb5_boolean                have_pol = FALSE;
1547     kadm5_server_handle_t       handle = server_handle;
1548     krb5_keyblock               *act_mkey;
1549     krb5_kvno                   act_kvno;
1550     int                         new_n_ks_tuple = 0;
1551     krb5_key_salt_tuple         *new_ks_tuple = NULL;
1552 
1553     if (keyblocks)
1554         *keyblocks = NULL;
1555 
1556     CHECK_HANDLE(server_handle);
1557 
1558     krb5_clear_error_message(handle->context);
1559 
1560     if (principal == NULL)
1561         return EINVAL;
1562 
1563     if ((ret = kdb_get_entry(handle, principal, &kdb, &adb)))
1564         return(ret);
1565 
1566     /* We will always be changing the key data, attributes, auth failure count,
1567      * and password expiration time. */
1568     kdb->mask = KADM5_KEY_DATA | KADM5_ATTRIBUTES | KADM5_FAIL_AUTH_COUNT |
1569         KADM5_PW_EXPIRATION;
1570 
1571     ret = apply_keysalt_policy(handle, adb.policy, n_ks_tuple, ks_tuple,
1572                                &new_n_ks_tuple, &new_ks_tuple);
1573     if (ret)
1574         goto done;
1575 
1576     if (krb5_principal_compare(handle->context, principal, hist_princ)) {
1577         /* If changing the history entry, the new entry must have exactly one
1578          * key. */
1579         if (keepold) {
1580             ret = KADM5_PROTECT_PRINCIPAL;
1581             goto done;
1582         }
1583         new_n_ks_tuple = 1;
1584     }
1585 
1586     ret = kdb_get_active_mkey(handle, &act_kvno, &act_mkey);
1587     if (ret)
1588         goto done;
1589 
1590     ret = krb5_dbe_crk(handle->context, act_mkey, new_ks_tuple, new_n_ks_tuple,
1591                        keepold, kdb);
1592     if (ret)
1593         goto done;
1594 
1595     ret = krb5_dbe_update_mkvno(handle->context, kdb, act_kvno);
1596     if (ret)
1597         goto done;
1598 
1599     kdb->attributes &= ~KRB5_KDB_REQUIRES_PWCHANGE;
1600 
1601     ret = krb5_timeofday(handle->context, &now);
1602     if (ret)
1603         goto done;
1604 
1605     if ((adb.aux_attributes & KADM5_POLICY)) {
1606         ret = get_policy(handle, adb.policy, &pol, &have_pol);
1607         if (ret)
1608             goto done;
1609     }
1610 
1611     kdb->pw_expiration = 0;
1612     if (have_pol && pol.pw_max_life)
1613         kdb->pw_expiration = ts_incr(now, pol.pw_max_life);
1614 
1615     ret = krb5_dbe_update_last_pwd_change(handle->context, kdb, now);
1616     if (ret)
1617         goto done;
1618 
1619     /* unlock principal on this KDC */
1620     kdb->fail_auth_count = 0;
1621 
1622     if (keyblocks) {
1623         /* Return only the new keys added by krb5_dbe_crk. */
1624         n_new_keys = count_new_keys(kdb->n_key_data, kdb->key_data);
1625         ret = decrypt_key_data(handle->context, n_new_keys, kdb->key_data,
1626                                keyblocks, n_keys);
1627         if (ret)
1628             goto done;
1629     }
1630 
1631     ret = k5_kadm5_hook_chpass(handle->context, handle->hook_handles,
1632                                KADM5_HOOK_STAGE_PRECOMMIT, principal, keepold,
1633                                new_n_ks_tuple, new_ks_tuple, NULL);
1634     if (ret)
1635         goto done;
1636     if ((ret = kdb_put_entry(handle, kdb, &adb)))
1637         goto done;
1638 
1639     (void) k5_kadm5_hook_chpass(handle->context, handle->hook_handles,
1640                                 KADM5_HOOK_STAGE_POSTCOMMIT, principal,
1641                                 keepold, new_n_ks_tuple, new_ks_tuple, NULL);
1642     ret = KADM5_OK;
1643 done:
1644     free(new_ks_tuple);
1645     kdb_free_entry(handle, kdb, &adb);
1646     if (have_pol)
1647         kadm5_free_policy_ent(handle->lhandle, &pol);
1648 
1649     return ret;
1650 }
1651 
1652 kadm5_ret_t
kadm5_setkey_principal(void * server_handle,krb5_principal principal,krb5_keyblock * keyblocks,int n_keys)1653 kadm5_setkey_principal(void *server_handle,
1654                        krb5_principal principal,
1655                        krb5_keyblock *keyblocks,
1656                        int n_keys)
1657 {
1658     return
1659         kadm5_setkey_principal_3(server_handle, principal,
1660                                  FALSE, 0, NULL,
1661                                  keyblocks, n_keys);
1662 }
1663 
1664 kadm5_ret_t
kadm5_setkey_principal_3(void * server_handle,krb5_principal principal,krb5_boolean keepold,int n_ks_tuple,krb5_key_salt_tuple * ks_tuple,krb5_keyblock * keyblocks,int n_keys)1665 kadm5_setkey_principal_3(void *server_handle,
1666                          krb5_principal principal,
1667                          krb5_boolean keepold,
1668                          int n_ks_tuple, krb5_key_salt_tuple *ks_tuple,
1669                          krb5_keyblock *keyblocks,
1670                          int n_keys)
1671 {
1672     kadm5_key_data *key_data;
1673     kadm5_ret_t ret;
1674     int i;
1675 
1676     if (keyblocks == NULL)
1677         return EINVAL;
1678 
1679     if (n_ks_tuple) {
1680         if (n_ks_tuple != n_keys)
1681             return KADM5_SETKEY3_ETYPE_MISMATCH;
1682         for (i = 0; i < n_ks_tuple; i++) {
1683             if (ks_tuple[i].ks_enctype != keyblocks[i].enctype)
1684                 return KADM5_SETKEY3_ETYPE_MISMATCH;
1685         }
1686     }
1687 
1688     key_data = calloc(n_keys, sizeof(kadm5_key_data));
1689     if (key_data == NULL)
1690         return ENOMEM;
1691 
1692     for (i = 0; i < n_keys; i++) {
1693         key_data[i].key = keyblocks[i];
1694         key_data[i].salt.type =
1695             n_ks_tuple ? ks_tuple[i].ks_salttype : KRB5_KDB_SALTTYPE_NORMAL;
1696     }
1697 
1698     ret = kadm5_setkey_principal_4(server_handle, principal, keepold,
1699                                    key_data, n_keys);
1700     free(key_data);
1701     return ret;
1702 }
1703 
1704 /* Create a key/salt list from a key_data array. */
1705 static kadm5_ret_t
make_ks_from_key_data(krb5_context context,kadm5_key_data * key_data,int n_key_data,krb5_key_salt_tuple ** out)1706 make_ks_from_key_data(krb5_context context, kadm5_key_data *key_data,
1707                       int n_key_data, krb5_key_salt_tuple **out)
1708 {
1709     int i;
1710     krb5_key_salt_tuple *ks;
1711 
1712     *out = NULL;
1713 
1714     ks = calloc(n_key_data, sizeof(*ks));
1715     if (ks == NULL)
1716         return ENOMEM;
1717 
1718     for (i = 0; i < n_key_data; i++) {
1719         ks[i].ks_enctype = key_data[i].key.enctype;
1720         ks[i].ks_salttype = key_data[i].salt.type;
1721     }
1722     *out = ks;
1723     return 0;
1724 }
1725 
1726 kadm5_ret_t
kadm5_setkey_principal_4(void * server_handle,krb5_principal principal,krb5_boolean keepold,kadm5_key_data * key_data,int n_key_data)1727 kadm5_setkey_principal_4(void *server_handle, krb5_principal principal,
1728                          krb5_boolean keepold, kadm5_key_data *key_data,
1729                          int n_key_data)
1730 {
1731     krb5_db_entry *kdb;
1732     osa_princ_ent_rec adb;
1733     krb5_timestamp now;
1734     kadm5_policy_ent_rec pol;
1735     krb5_key_data *new_key_data = NULL;
1736     int i, j, ret, n_new_key_data = 0;
1737     krb5_kvno kvno;
1738     krb5_boolean similar, have_pol = FALSE;
1739     kadm5_server_handle_t handle = server_handle;
1740     krb5_keyblock *act_mkey;
1741     krb5_key_salt_tuple *ks_from_keys = NULL;
1742 
1743     CHECK_HANDLE(server_handle);
1744 
1745     krb5_clear_error_message(handle->context);
1746 
1747     if (principal == NULL || key_data == NULL || n_key_data == 0)
1748         return EINVAL;
1749 
1750     /* hist_princ will be NULL when initializing the database. */
1751     if (hist_princ != NULL &&
1752         krb5_principal_compare(handle->context, principal, hist_princ))
1753         return KADM5_PROTECT_PRINCIPAL;
1754 
1755     /* For now, all keys must have the same kvno. */
1756     kvno = key_data[0].kvno;
1757     for (i = 1; i < n_key_data; i++) {
1758         if (key_data[i].kvno != kvno)
1759             return KADM5_SETKEY_BAD_KVNO;
1760     }
1761 
1762     ret = kdb_get_entry(handle, principal, &kdb, &adb);
1763     if (ret)
1764         return ret;
1765 
1766     /* We will always be changing the key data, attributes, auth failure count,
1767      * and password expiration time. */
1768     kdb->mask = KADM5_KEY_DATA | KADM5_ATTRIBUTES | KADM5_FAIL_AUTH_COUNT |
1769         KADM5_PW_EXPIRATION;
1770 
1771     if (kvno == 0) {
1772         /* Pick the next kvno. */
1773         for (i = 0; i < kdb->n_key_data; i++) {
1774             if (kdb->key_data[i].key_data_kvno > kvno)
1775                 kvno = kdb->key_data[i].key_data_kvno;
1776         }
1777         kvno++;
1778     } else if (keepold) {
1779         /* Check that the kvno does collide with existing keys. */
1780         for (i = 0; i < kdb->n_key_data; i++) {
1781             if (kdb->key_data[i].key_data_kvno == kvno) {
1782                 ret = KADM5_SETKEY_BAD_KVNO;
1783                 goto done;
1784             }
1785         }
1786     }
1787 
1788     ret = make_ks_from_key_data(handle->context, key_data, n_key_data,
1789                                 &ks_from_keys);
1790     if (ret)
1791         goto done;
1792 
1793     ret = apply_keysalt_policy(handle, adb.policy, n_key_data, ks_from_keys,
1794                                NULL, NULL);
1795     free(ks_from_keys);
1796     if (ret)
1797         goto done;
1798 
1799     for (i = 0; i < n_key_data; i++) {
1800         for (j = i + 1; j < n_key_data; j++) {
1801             ret = krb5_c_enctype_compare(handle->context,
1802                                          key_data[i].key.enctype,
1803                                          key_data[j].key.enctype,
1804                                          &similar);
1805             if (ret)
1806                 goto done;
1807             if (similar) {
1808                 if (key_data[i].salt.type == key_data[j].salt.type) {
1809                     ret = KADM5_SETKEY_DUP_ENCTYPES;
1810                     goto done;
1811                 }
1812             }
1813         }
1814     }
1815 
1816     n_new_key_data = n_key_data + (keepold ? kdb->n_key_data : 0);
1817     new_key_data = calloc(n_new_key_data, sizeof(krb5_key_data));
1818     if (new_key_data == NULL) {
1819         n_new_key_data = 0;
1820         ret = ENOMEM;
1821         goto done;
1822     }
1823 
1824     n_new_key_data = 0;
1825     for (i = 0; i < n_key_data; i++) {
1826 
1827         ret = kdb_get_active_mkey(handle, NULL, &act_mkey);
1828         if (ret)
1829             goto done;
1830 
1831         ret = krb5_dbe_encrypt_key_data(handle->context, act_mkey,
1832                                         &key_data[i].key, &key_data[i].salt,
1833                                         kvno, &new_key_data[i]);
1834         if (ret)
1835             goto done;
1836 
1837         n_new_key_data++;
1838     }
1839 
1840     /* Copy old key data if necessary. */
1841     if (keepold) {
1842         memcpy(new_key_data + n_new_key_data, kdb->key_data,
1843                kdb->n_key_data * sizeof(krb5_key_data));
1844         memset(kdb->key_data, 0, kdb->n_key_data * sizeof(krb5_key_data));
1845 
1846         /*
1847          * Sort the keys to maintain the defined kvno order.  We only need to
1848          * sort if we keep old keys, as otherwise we allow only a single kvno
1849          * to be specified.
1850          */
1851         krb5_dbe_sort_key_data(new_key_data, n_new_key_data);
1852     }
1853 
1854     /* Replace kdb->key_data with the new keys. */
1855     cleanup_key_data(handle->context, kdb->n_key_data, kdb->key_data);
1856     kdb->key_data = new_key_data;
1857     kdb->n_key_data = n_new_key_data;
1858     new_key_data = NULL;
1859     n_new_key_data = 0;
1860 
1861     kdb->attributes &= ~KRB5_KDB_REQUIRES_PWCHANGE;
1862 
1863     ret = krb5_timeofday(handle->context, &now);
1864     if (ret)
1865         goto done;
1866 
1867     if (adb.aux_attributes & KADM5_POLICY) {
1868         ret = get_policy(handle, adb.policy, &pol, &have_pol);
1869         if (ret)
1870             goto done;
1871     }
1872 
1873     kdb->pw_expiration = 0;
1874     if (have_pol && pol.pw_max_life)
1875         kdb->pw_expiration = ts_incr(now, pol.pw_max_life);
1876 
1877     ret = krb5_dbe_update_last_pwd_change(handle->context, kdb, now);
1878     if (ret)
1879         goto done;
1880 
1881     /* Unlock principal on this KDC. */
1882     kdb->fail_auth_count = 0;
1883 
1884     ret = kdb_put_entry(handle, kdb, &adb);
1885     if (ret)
1886         goto done;
1887 
1888     ret = KADM5_OK;
1889 
1890 done:
1891     cleanup_key_data(handle->context, n_new_key_data, new_key_data);
1892     kdb_free_entry(handle, kdb, &adb);
1893     if (have_pol)
1894         kadm5_free_policy_ent(handle->lhandle, &pol);
1895     return ret;
1896 }
1897 
1898 /*
1899  * Return the list of keys like kadm5_randkey_principal,
1900  * but don't modify the principal.
1901  */
1902 kadm5_ret_t
kadm5_get_principal_keys(void * server_handle,krb5_principal principal,krb5_kvno kvno,kadm5_key_data ** key_data_out,int * n_key_data_out)1903 kadm5_get_principal_keys(void *server_handle /* IN */,
1904                          krb5_principal principal /* IN */,
1905                          krb5_kvno kvno /* IN */,
1906                          kadm5_key_data **key_data_out /* OUT */,
1907                          int *n_key_data_out /* OUT */)
1908 {
1909     krb5_db_entry               *kdb;
1910     osa_princ_ent_rec           adb;
1911     kadm5_ret_t                 ret;
1912     kadm5_server_handle_t       handle = server_handle;
1913     kadm5_key_data              *key_data = NULL;
1914     int i, nkeys = 0;
1915 
1916     if (principal == NULL || key_data_out == NULL || n_key_data_out == NULL)
1917         return EINVAL;
1918 
1919     CHECK_HANDLE(server_handle);
1920 
1921     if ((ret = kdb_get_entry(handle, principal, &kdb, &adb)))
1922         return(ret);
1923 
1924     key_data = calloc(kdb->n_key_data, sizeof(kadm5_key_data));
1925     if (key_data == NULL) {
1926         ret = ENOMEM;
1927         goto done;
1928     }
1929 
1930     for (i = 0, nkeys = 0; i < kdb->n_key_data; i++) {
1931         if (kvno != 0 && kvno != kdb->key_data[i].key_data_kvno)
1932             continue;
1933         key_data[nkeys].kvno = kdb->key_data[i].key_data_kvno;
1934 
1935         ret = krb5_dbe_decrypt_key_data(handle->context, NULL,
1936                                         &kdb->key_data[i],
1937                                         &key_data[nkeys].key,
1938                                         &key_data[nkeys].salt);
1939         if (ret)
1940             goto done;
1941         nkeys++;
1942     }
1943 
1944     *n_key_data_out = nkeys;
1945     *key_data_out = key_data;
1946     key_data = NULL;
1947     nkeys = 0;
1948     ret = KADM5_OK;
1949 
1950 done:
1951     kdb_free_entry(handle, kdb, &adb);
1952     kadm5_free_kadm5_key_data(handle->context, nkeys, key_data);
1953 
1954     return ret;
1955 }
1956 
1957 
1958 /*
1959  * Allocate an array of n_key_data krb5_keyblocks, fill in each
1960  * element with the results of decrypting the nth key in key_data,
1961  * and if n_keys is not NULL fill it in with the
1962  * number of keys decrypted.
1963  */
decrypt_key_data(krb5_context context,int n_key_data,krb5_key_data * key_data,krb5_keyblock ** keyblocks,int * n_keys)1964 static int decrypt_key_data(krb5_context context,
1965                             int n_key_data, krb5_key_data *key_data,
1966                             krb5_keyblock **keyblocks, int *n_keys)
1967 {
1968     krb5_keyblock *keys;
1969     int ret, i;
1970 
1971     keys = (krb5_keyblock *) malloc(n_key_data*sizeof(krb5_keyblock));
1972     if (keys == NULL)
1973         return ENOMEM;
1974     memset(keys, 0, n_key_data*sizeof(krb5_keyblock));
1975 
1976     for (i = 0; i < n_key_data; i++) {
1977         ret = krb5_dbe_decrypt_key_data(context, NULL, &key_data[i], &keys[i],
1978                                         NULL);
1979         if (ret) {
1980             for (; i >= 0; i--)
1981                 krb5_free_keyblock_contents(context, &keys[i]);
1982             free(keys);
1983             return ret;
1984         }
1985     }
1986 
1987     *keyblocks = keys;
1988     if (n_keys)
1989         *n_keys = n_key_data;
1990 
1991     return 0;
1992 }
1993 
1994 /*
1995  * Function: kadm5_decrypt_key
1996  *
1997  * Purpose: Retrieves and decrypts a principal key.
1998  *
1999  * Arguments:
2000  *
2001  *      server_handle   (r) kadm5 handle
2002  *      entry           (r) principal retrieved with kadm5_get_principal
2003  *      ktype           (r) enctype to search for, or -1 to ignore
2004  *      stype           (r) salt type to search for, or -1 to ignore
2005  *      kvno            (r) kvno to search for, -1 for max, 0 for max
2006  *                      only if it also matches ktype and stype
2007  *      keyblock        (w) keyblock to fill in
2008  *      keysalt         (w) keysalt to fill in, or NULL
2009  *      kvnop           (w) kvno to fill in, or NULL
2010  *
2011  * Effects: Searches the key_data array of entry, which must have been
2012  * retrieved with kadm5_get_principal with the KADM5_KEY_DATA mask, to
2013  * find a key with a specified enctype, salt type, and kvno in a
2014  * principal entry.  If not found, return ENOENT.  Otherwise, decrypt
2015  * it with the master key, and return the key in keyblock, the salt
2016  * in salttype, and the key version number in kvno.
2017  *
2018  * If ktype or stype is -1, it is ignored for the search.  If kvno is
2019  * -1, ktype and stype are ignored and the key with the max kvno is
2020  * returned.  If kvno is 0, only the key with the max kvno is returned
2021  * and only if it matches the ktype and stype; otherwise, ENOENT is
2022  * returned.
2023  */
kadm5_decrypt_key(void * server_handle,kadm5_principal_ent_t entry,krb5_int32 ktype,krb5_int32 stype,krb5_int32 kvno,krb5_keyblock * keyblock,krb5_keysalt * keysalt,int * kvnop)2024 kadm5_ret_t kadm5_decrypt_key(void *server_handle,
2025                               kadm5_principal_ent_t entry, krb5_int32
2026                               ktype, krb5_int32 stype, krb5_int32
2027                               kvno, krb5_keyblock *keyblock,
2028                               krb5_keysalt *keysalt, int *kvnop)
2029 {
2030     kadm5_server_handle_t handle = server_handle;
2031     krb5_db_entry dbent;
2032     krb5_key_data *key_data;
2033     krb5_keyblock *mkey_ptr;
2034     int ret;
2035 
2036     CHECK_HANDLE(server_handle);
2037 
2038     if (entry->n_key_data == 0 || entry->key_data == NULL)
2039         return EINVAL;
2040 
2041     /* find_enctype only uses these two fields */
2042     dbent.n_key_data = entry->n_key_data;
2043     dbent.key_data = entry->key_data;
2044     if ((ret = krb5_dbe_find_enctype(handle->context, &dbent, ktype,
2045                                      stype, kvno, &key_data)))
2046         return ret;
2047 
2048     /* find_mkey only uses this field */
2049     dbent.tl_data = entry->tl_data;
2050     if ((ret = krb5_dbe_find_mkey(handle->context, &dbent, &mkey_ptr))) {
2051         /* try refreshing master key list */
2052         /* XXX it would nice if we had the mkvno here for optimization */
2053         if (krb5_db_fetch_mkey_list(handle->context, master_princ,
2054                                     &master_keyblock) == 0) {
2055             if ((ret = krb5_dbe_find_mkey(handle->context, &dbent,
2056                                           &mkey_ptr))) {
2057                 return ret;
2058             }
2059         } else {
2060             return ret;
2061         }
2062     }
2063 
2064     if ((ret = krb5_dbe_decrypt_key_data(handle->context, NULL, key_data,
2065                                          keyblock, keysalt)))
2066         return ret;
2067 
2068     /*
2069      * Coerce the enctype of the output keyblock in case we got an
2070      * inexact match on the enctype; this behavior will go away when
2071      * the key storage architecture gets redesigned for 1.3.
2072      */
2073     if (ktype != -1)
2074         keyblock->enctype = ktype;
2075 
2076     if (kvnop)
2077         *kvnop = key_data->key_data_kvno;
2078 
2079     return KADM5_OK;
2080 }
2081 
2082 kadm5_ret_t
kadm5_purgekeys(void * server_handle,krb5_principal principal,int keepkvno)2083 kadm5_purgekeys(void *server_handle,
2084                 krb5_principal principal,
2085                 int keepkvno)
2086 {
2087     kadm5_server_handle_t handle = server_handle;
2088     kadm5_ret_t ret;
2089     krb5_db_entry *kdb;
2090     osa_princ_ent_rec adb;
2091     krb5_key_data *old_keydata;
2092     int n_old_keydata;
2093     int i, j, k;
2094 
2095     CHECK_HANDLE(server_handle);
2096 
2097     if (principal == NULL)
2098         return EINVAL;
2099 
2100     ret = kdb_get_entry(handle, principal, &kdb, &adb);
2101     if (ret)
2102         return(ret);
2103 
2104     if (keepkvno <= 0) {
2105         keepkvno = krb5_db_get_key_data_kvno(handle->context, kdb->n_key_data,
2106                                              kdb->key_data);
2107     }
2108 
2109     old_keydata = kdb->key_data;
2110     n_old_keydata = kdb->n_key_data;
2111     kdb->n_key_data = 0;
2112     /* Allocate one extra key_data to avoid allocating 0 bytes. */
2113     kdb->key_data = calloc(n_old_keydata, sizeof(krb5_key_data));
2114     if (kdb->key_data == NULL) {
2115         ret = ENOMEM;
2116         goto done;
2117     }
2118     memset(kdb->key_data, 0, n_old_keydata * sizeof(krb5_key_data));
2119     for (i = 0, j = 0; i < n_old_keydata; i++) {
2120         if (old_keydata[i].key_data_kvno < keepkvno)
2121             continue;
2122 
2123         /* Alias the key_data_contents pointers; we null them out in the
2124          * source array immediately after. */
2125         kdb->key_data[j] = old_keydata[i];
2126         for (k = 0; k < old_keydata[i].key_data_ver; k++) {
2127             old_keydata[i].key_data_contents[k] = NULL;
2128         }
2129         j++;
2130     }
2131     kdb->n_key_data = j;
2132     cleanup_key_data(handle->context, n_old_keydata, old_keydata);
2133 
2134     kdb->mask = KADM5_KEY_DATA;
2135     ret = kdb_put_entry(handle, kdb, &adb);
2136     if (ret)
2137         goto done;
2138 
2139 done:
2140     kdb_free_entry(handle, kdb, &adb);
2141     return ret;
2142 }
2143 
2144 kadm5_ret_t
kadm5_get_strings(void * server_handle,krb5_principal principal,krb5_string_attr ** strings_out,int * count_out)2145 kadm5_get_strings(void *server_handle, krb5_principal principal,
2146                   krb5_string_attr **strings_out, int *count_out)
2147 {
2148     kadm5_server_handle_t handle = server_handle;
2149     kadm5_ret_t ret;
2150     krb5_db_entry *kdb = NULL;
2151 
2152     *strings_out = NULL;
2153     *count_out = 0;
2154     CHECK_HANDLE(server_handle);
2155     if (principal == NULL)
2156         return EINVAL;
2157 
2158     ret = kdb_get_entry(handle, principal, &kdb, NULL);
2159     if (ret)
2160         return ret;
2161 
2162     ret = krb5_dbe_get_strings(handle->context, kdb, strings_out, count_out);
2163     kdb_free_entry(handle, kdb, NULL);
2164     return ret;
2165 }
2166 
2167 kadm5_ret_t
kadm5_set_string(void * server_handle,krb5_principal principal,const char * key,const char * value)2168 kadm5_set_string(void *server_handle, krb5_principal principal,
2169                  const char *key, const char *value)
2170 {
2171     kadm5_server_handle_t handle = server_handle;
2172     kadm5_ret_t ret;
2173     krb5_db_entry *kdb;
2174     osa_princ_ent_rec adb;
2175 
2176     CHECK_HANDLE(server_handle);
2177     if (principal == NULL || key == NULL)
2178         return EINVAL;
2179 
2180     ret = kdb_get_entry(handle, principal, &kdb, &adb);
2181     if (ret)
2182         return ret;
2183 
2184     ret = krb5_dbe_set_string(handle->context, kdb, key, value);
2185     if (ret)
2186         goto done;
2187 
2188     kdb->mask = KADM5_TL_DATA;
2189     ret = kdb_put_entry(handle, kdb, &adb);
2190 
2191 done:
2192     kdb_free_entry(handle, kdb, &adb);
2193     return ret;
2194 }
2195