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