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