Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/crypto/krb5/src/lib/kadm5/srv/svr_principal.c
39566 views
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
*/
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
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
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
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
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
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
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
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
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
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
*/
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
*/
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
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
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
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
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
2089