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