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