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