10Sstevel@tonic-gate /*
2*12253SPeter.Shoults@Sun.COM * Copyright (c) 2001, 2010, Oracle and/or its affiliates. All rights reserved.
30Sstevel@tonic-gate */
40Sstevel@tonic-gate
50Sstevel@tonic-gate /*
60Sstevel@tonic-gate * WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING
70Sstevel@tonic-gate *
80Sstevel@tonic-gate * Openvision retains the copyright to derivative works of
90Sstevel@tonic-gate * this source code. Do *NOT* create a derivative of this
100Sstevel@tonic-gate * source code before consulting with your legal department.
110Sstevel@tonic-gate * Do *NOT* integrate *ANY* of this source code into another
120Sstevel@tonic-gate * product before consulting with your legal department.
130Sstevel@tonic-gate *
140Sstevel@tonic-gate * For further information, read the top-level Openvision
150Sstevel@tonic-gate * copyright which is contained in the top-level MIT Kerberos
160Sstevel@tonic-gate * copyright.
170Sstevel@tonic-gate *
180Sstevel@tonic-gate * WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING
190Sstevel@tonic-gate *
200Sstevel@tonic-gate */
210Sstevel@tonic-gate
220Sstevel@tonic-gate /*
230Sstevel@tonic-gate * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved
240Sstevel@tonic-gate *
257934SMark.Phalan@Sun.COM * $Header$
260Sstevel@tonic-gate */
270Sstevel@tonic-gate
280Sstevel@tonic-gate #if !defined(lint) && !defined(__CODECENTER__)
297934SMark.Phalan@Sun.COM static char *rcsid = "$Header$";
300Sstevel@tonic-gate #endif
310Sstevel@tonic-gate
320Sstevel@tonic-gate #include <sys/types.h>
330Sstevel@tonic-gate #include <sys/time.h>
347934SMark.Phalan@Sun.COM #include <errno.h>
357934SMark.Phalan@Sun.COM #include "server_internal.h"
360Sstevel@tonic-gate #include <kadm5/admin.h>
374960Swillf #include <kdb.h>
380Sstevel@tonic-gate #include <stdio.h>
390Sstevel@tonic-gate #include <string.h>
400Sstevel@tonic-gate #include <stdarg.h>
410Sstevel@tonic-gate #include <stdlib.h>
42*12253SPeter.Shoults@Sun.COM #include <k5-int.h>
43*12253SPeter.Shoults@Sun.COM #include <kadm5/server_internal.h>
44*12253SPeter.Shoults@Sun.COM #include <kadm5/admin.h>
452881Smp153739 #ifdef USE_PASSWORD_SERVER
462881Smp153739 #include <sys/wait.h>
472881Smp153739 #endif
480Sstevel@tonic-gate
490Sstevel@tonic-gate extern krb5_principal master_princ;
500Sstevel@tonic-gate extern krb5_principal hist_princ;
510Sstevel@tonic-gate extern krb5_keyblock hist_key;
520Sstevel@tonic-gate extern krb5_db_entry master_db;
530Sstevel@tonic-gate extern krb5_db_entry hist_db;
540Sstevel@tonic-gate extern krb5_kvno hist_kvno;
550Sstevel@tonic-gate
560Sstevel@tonic-gate static int decrypt_key_data(krb5_context context,
572881Smp153739 krb5_keyblock *, int n_key_data, krb5_key_data *key_data,
582881Smp153739 krb5_keyblock **keyblocks, int *n_keys);
590Sstevel@tonic-gate
604960Swillf static krb5_error_code
kadm5_copy_principal(krb5_context context,krb5_const_principal inprinc,krb5_principal * outprinc)614960Swillf kadm5_copy_principal(krb5_context context, krb5_const_principal inprinc, krb5_principal *outprinc)
624960Swillf {
634960Swillf register krb5_principal tempprinc;
644960Swillf register int i, nelems;
654960Swillf
664960Swillf tempprinc = (krb5_principal)krb5_db_alloc(context, NULL, sizeof(krb5_principal_data));
674960Swillf
684960Swillf if (tempprinc == 0)
694960Swillf return ENOMEM;
704960Swillf
714960Swillf memcpy(tempprinc, inprinc, sizeof(krb5_principal_data));
724960Swillf
734960Swillf nelems = (int) krb5_princ_size(context, inprinc);
744960Swillf tempprinc->data = krb5_db_alloc(context, NULL, nelems * sizeof(krb5_data));
754960Swillf
764960Swillf if (tempprinc->data == 0) {
774960Swillf krb5_db_free(context, (char *)tempprinc);
784960Swillf return ENOMEM;
794960Swillf }
804960Swillf
814960Swillf for (i = 0; i < nelems; i++) {
824960Swillf unsigned int len = krb5_princ_component(context, inprinc, i)->length;
834960Swillf krb5_princ_component(context, tempprinc, i)->length = len;
844960Swillf if (((krb5_princ_component(context, tempprinc, i)->data =
854960Swillf krb5_db_alloc(context, NULL, len)) == 0) && len) {
864960Swillf while (--i >= 0)
874960Swillf krb5_db_free(context, krb5_princ_component(context, tempprinc, i)->data);
884960Swillf krb5_db_free (context, tempprinc->data);
894960Swillf krb5_db_free (context, tempprinc);
904960Swillf return ENOMEM;
914960Swillf }
924960Swillf if (len)
934960Swillf memcpy(krb5_princ_component(context, tempprinc, i)->data,
944960Swillf krb5_princ_component(context, inprinc, i)->data, len);
954960Swillf }
964960Swillf
974960Swillf tempprinc->realm.data =
984960Swillf krb5_db_alloc(context, NULL, tempprinc->realm.length = inprinc->realm.length);
994960Swillf if (!tempprinc->realm.data && tempprinc->realm.length) {
1004960Swillf for (i = 0; i < nelems; i++)
1014960Swillf krb5_db_free(context, krb5_princ_component(context, tempprinc, i)->data);
1024960Swillf krb5_db_free(context, tempprinc->data);
1034960Swillf krb5_db_free(context, tempprinc);
1044960Swillf return ENOMEM;
1054960Swillf }
1064960Swillf if (tempprinc->realm.length)
1074960Swillf memcpy(tempprinc->realm.data, inprinc->realm.data,
1084960Swillf inprinc->realm.length);
1094960Swillf
1104960Swillf *outprinc = tempprinc;
1114960Swillf return 0;
1124960Swillf }
1134960Swillf
1144960Swillf static void
kadm5_free_principal(krb5_context context,krb5_principal val)1154960Swillf kadm5_free_principal(krb5_context context, krb5_principal val)
1164960Swillf {
1174960Swillf register krb5_int32 i;
1184960Swillf
1194960Swillf if (!val)
1204960Swillf return;
1214960Swillf
1224960Swillf if (val->data) {
1234960Swillf i = krb5_princ_size(context, val);
1244960Swillf while(--i >= 0)
1254960Swillf krb5_db_free(context, krb5_princ_component(context, val, i)->data);
1264960Swillf krb5_db_free(context, val->data);
1274960Swillf }
1284960Swillf if (val->realm.data)
1294960Swillf krb5_db_free(context, val->realm.data);
1304960Swillf krb5_db_free(context, val);
1314960Swillf }
1324960Swillf
1330Sstevel@tonic-gate /*
1340Sstevel@tonic-gate * XXX Functions that ought to be in libkrb5.a, but aren't.
1350Sstevel@tonic-gate */
krb5_copy_key_data_contents(context,from,to)1360Sstevel@tonic-gate kadm5_ret_t krb5_copy_key_data_contents(context, from, to)
1370Sstevel@tonic-gate krb5_context context;
1380Sstevel@tonic-gate krb5_key_data *from, *to;
1390Sstevel@tonic-gate {
1400Sstevel@tonic-gate int i, idx;
1410Sstevel@tonic-gate
1420Sstevel@tonic-gate *to = *from;
1430Sstevel@tonic-gate
1440Sstevel@tonic-gate idx = (from->key_data_ver == 1 ? 1 : 2);
1450Sstevel@tonic-gate
1460Sstevel@tonic-gate for (i = 0; i < idx; i++) {
1470Sstevel@tonic-gate if ( from->key_data_length[i] ) {
1480Sstevel@tonic-gate to->key_data_contents[i] = malloc(from->key_data_length[i]);
1490Sstevel@tonic-gate if (to->key_data_contents[i] == NULL) {
1500Sstevel@tonic-gate for (i = 0; i < idx; i++) {
1510Sstevel@tonic-gate if (to->key_data_contents[i]) {
1520Sstevel@tonic-gate memset(to->key_data_contents[i], 0,
1530Sstevel@tonic-gate to->key_data_length[i]);
1540Sstevel@tonic-gate free(to->key_data_contents[i]);
1550Sstevel@tonic-gate }
1560Sstevel@tonic-gate }
1570Sstevel@tonic-gate return ENOMEM;
1580Sstevel@tonic-gate }
1590Sstevel@tonic-gate memcpy(to->key_data_contents[i], from->key_data_contents[i],
1600Sstevel@tonic-gate from->key_data_length[i]);
1610Sstevel@tonic-gate }
1620Sstevel@tonic-gate }
1630Sstevel@tonic-gate return 0;
1640Sstevel@tonic-gate }
1650Sstevel@tonic-gate
dup_tl_data(krb5_tl_data * tl)1660Sstevel@tonic-gate static krb5_tl_data *dup_tl_data(krb5_tl_data *tl)
1670Sstevel@tonic-gate {
1680Sstevel@tonic-gate krb5_tl_data *n;
1690Sstevel@tonic-gate
1700Sstevel@tonic-gate n = (krb5_tl_data *) malloc(sizeof(krb5_tl_data));
1710Sstevel@tonic-gate if (n == NULL)
1720Sstevel@tonic-gate return NULL;
1730Sstevel@tonic-gate n->tl_data_contents = malloc(tl->tl_data_length);
1740Sstevel@tonic-gate if (n->tl_data_contents == NULL) {
1750Sstevel@tonic-gate free(n);
1760Sstevel@tonic-gate return NULL;
1770Sstevel@tonic-gate }
1780Sstevel@tonic-gate memcpy(n->tl_data_contents, tl->tl_data_contents, tl->tl_data_length);
1790Sstevel@tonic-gate n->tl_data_type = tl->tl_data_type;
1800Sstevel@tonic-gate n->tl_data_length = tl->tl_data_length;
1810Sstevel@tonic-gate n->tl_data_next = NULL;
1820Sstevel@tonic-gate return n;
1830Sstevel@tonic-gate }
1840Sstevel@tonic-gate
1850Sstevel@tonic-gate /* This is in lib/kdb/kdb_cpw.c, but is static */
cleanup_key_data(context,count,data)1860Sstevel@tonic-gate static void cleanup_key_data(context, count, data)
1870Sstevel@tonic-gate krb5_context context;
1880Sstevel@tonic-gate int count;
1890Sstevel@tonic-gate krb5_key_data * data;
1900Sstevel@tonic-gate {
1910Sstevel@tonic-gate int i, j;
1920Sstevel@tonic-gate
1930Sstevel@tonic-gate for (i = 0; i < count; i++)
1940Sstevel@tonic-gate for (j = 0; j < data[i].key_data_ver; j++)
1950Sstevel@tonic-gate if (data[i].key_data_length[j])
1964960Swillf krb5_db_free(context, data[i].key_data_contents[j]);
1974960Swillf krb5_db_free(context, data);
1980Sstevel@tonic-gate }
1990Sstevel@tonic-gate
2000Sstevel@tonic-gate kadm5_ret_t
kadm5_create_principal(void * server_handle,kadm5_principal_ent_t entry,long mask,char * password)2010Sstevel@tonic-gate kadm5_create_principal(void *server_handle,
2020Sstevel@tonic-gate kadm5_principal_ent_t entry, long mask,
2030Sstevel@tonic-gate char *password)
2040Sstevel@tonic-gate {
2052881Smp153739 return
2062881Smp153739 kadm5_create_principal_3(server_handle, entry, mask,
2072881Smp153739 0, NULL, password);
2080Sstevel@tonic-gate }
2090Sstevel@tonic-gate 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)2100Sstevel@tonic-gate kadm5_create_principal_3(void *server_handle,
2110Sstevel@tonic-gate kadm5_principal_ent_t entry, long mask,
2120Sstevel@tonic-gate int n_ks_tuple, krb5_key_salt_tuple *ks_tuple,
2130Sstevel@tonic-gate char *password)
2140Sstevel@tonic-gate {
2150Sstevel@tonic-gate krb5_db_entry kdb;
2160Sstevel@tonic-gate osa_princ_ent_rec adb;
2170Sstevel@tonic-gate kadm5_policy_ent_rec polent;
2180Sstevel@tonic-gate krb5_int32 now;
2190Sstevel@tonic-gate krb5_tl_data *tl_data_orig, *tl_data_tail;
2200Sstevel@tonic-gate unsigned int ret;
2210Sstevel@tonic-gate kadm5_server_handle_t handle = server_handle;
2220Sstevel@tonic-gate
2230Sstevel@tonic-gate CHECK_HANDLE(server_handle);
2240Sstevel@tonic-gate
2254960Swillf krb5_clear_error_message(handle->context);
2264960Swillf
2270Sstevel@tonic-gate /*
2280Sstevel@tonic-gate * Argument sanity checking, and opening up the DB
2290Sstevel@tonic-gate */
2300Sstevel@tonic-gate if(!(mask & KADM5_PRINCIPAL) || (mask & KADM5_MOD_NAME) ||
2310Sstevel@tonic-gate (mask & KADM5_MOD_TIME) || (mask & KADM5_LAST_PWD_CHANGE) ||
2320Sstevel@tonic-gate (mask & KADM5_MKVNO) || (mask & KADM5_POLICY_CLR) ||
2330Sstevel@tonic-gate (mask & KADM5_AUX_ATTRIBUTES) || (mask & KADM5_KEY_DATA) ||
2340Sstevel@tonic-gate (mask & KADM5_LAST_SUCCESS) || (mask & KADM5_LAST_FAILED) ||
2350Sstevel@tonic-gate (mask & KADM5_FAIL_AUTH_COUNT))
2360Sstevel@tonic-gate return KADM5_BAD_MASK;
2370Sstevel@tonic-gate if((mask & ~ALL_PRINC_MASK))
2380Sstevel@tonic-gate return KADM5_BAD_MASK;
2390Sstevel@tonic-gate if (entry == (kadm5_principal_ent_t) NULL || password == NULL)
2400Sstevel@tonic-gate return EINVAL;
2410Sstevel@tonic-gate
2420Sstevel@tonic-gate /*
2430Sstevel@tonic-gate * Check to see if the principal exists
2440Sstevel@tonic-gate */
2450Sstevel@tonic-gate ret = kdb_get_entry(handle, entry->principal, &kdb, &adb);
2460Sstevel@tonic-gate
2470Sstevel@tonic-gate switch(ret) {
2480Sstevel@tonic-gate case KADM5_UNK_PRINC:
2497934SMark.Phalan@Sun.COM /* Solaris Kerberos */
2504960Swillf memset(&kdb, 0, sizeof(krb5_db_entry));
2514960Swillf memset(&adb, 0, sizeof(osa_princ_ent_rec));
2520Sstevel@tonic-gate break;
2530Sstevel@tonic-gate case 0:
2544960Swillf /*
2554960Swillf * Solaris Kerberos: this allows an addprinc to be done on a mix-in
2564960Swillf * princ which has no keys initially.
2574960Swillf */
2584960Swillf if (kdb.n_key_data != 0) {
2594960Swillf /* have a princ with keys, return dupe princ error */
2604960Swillf kdb_free_entry(handle, &kdb, &adb);
2614960Swillf return KADM5_DUP;
2624960Swillf } else {
2634960Swillf /*
2644960Swillf * have a princ with no keys, let's replace it. Note, want to
2654960Swillf * keep the existing kdb tl_data (specifically the LDAP plugin
2664960Swillf * adds the DN to the tl_data which is needed to locate the dir.
2674960Swillf * entry).
2684960Swillf */
2694960Swillf kdb_free_entry(handle, NULL, &adb);
2704960Swillf memset(&adb, 0, sizeof(osa_princ_ent_rec));
2714960Swillf }
2724960Swillf break;
2730Sstevel@tonic-gate default:
2740Sstevel@tonic-gate return ret;
2750Sstevel@tonic-gate }
2760Sstevel@tonic-gate
2770Sstevel@tonic-gate /*
2780Sstevel@tonic-gate * If a policy was specified, load it.
2790Sstevel@tonic-gate * If we can not find the one specified return an error
2800Sstevel@tonic-gate */
2810Sstevel@tonic-gate if ((mask & KADM5_POLICY)) {
2820Sstevel@tonic-gate if ((ret = kadm5_get_policy(handle->lhandle, entry->policy,
2830Sstevel@tonic-gate &polent)) != KADM5_OK) {
2840Sstevel@tonic-gate if(ret == EINVAL)
2850Sstevel@tonic-gate return KADM5_BAD_POLICY;
2860Sstevel@tonic-gate else
2870Sstevel@tonic-gate return ret;
2880Sstevel@tonic-gate }
2890Sstevel@tonic-gate }
2902881Smp153739 if ((ret = passwd_check(handle, password, (mask & KADM5_POLICY),
2912881Smp153739 &polent, entry->principal))) {
2920Sstevel@tonic-gate if (mask & KADM5_POLICY)
2930Sstevel@tonic-gate (void) kadm5_free_policy_ent(handle->lhandle, &polent);
2940Sstevel@tonic-gate return ret;
2950Sstevel@tonic-gate }
2960Sstevel@tonic-gate /*
2970Sstevel@tonic-gate * Start populating the various DB fields, using the
2980Sstevel@tonic-gate * "defaults" for fields that were not specified by the
2990Sstevel@tonic-gate * mask.
3000Sstevel@tonic-gate */
3012881Smp153739 if ((ret = krb5_timeofday(handle->context, &now))) {
3022881Smp153739 if (mask & KADM5_POLICY)
3032881Smp153739 (void) kadm5_free_policy_ent(handle->lhandle, &polent);
3042881Smp153739 return ret;
3050Sstevel@tonic-gate }
3060Sstevel@tonic-gate
3070Sstevel@tonic-gate kdb.magic = KRB5_KDB_MAGIC_NUMBER;
3080Sstevel@tonic-gate kdb.len = KRB5_KDB_V1_BASE_LENGTH; /* gag me with a chainsaw */
3090Sstevel@tonic-gate
3100Sstevel@tonic-gate /*
3117934SMark.Phalan@Sun.COM * Solaris Kerberos:
3120Sstevel@tonic-gate * If KADM5_ATTRIBUTES is set, we want to rope in not only
3130Sstevel@tonic-gate * entry->attributes, but also the generic params.flags
3140Sstevel@tonic-gate * obtained previously via kadm5_get_config_params.
3150Sstevel@tonic-gate */
3160Sstevel@tonic-gate if ((mask & KADM5_ATTRIBUTES)) {
3170Sstevel@tonic-gate kdb.attributes = handle->params.flags;
3180Sstevel@tonic-gate kdb.attributes |= entry->attributes;
3190Sstevel@tonic-gate } else {
3204960Swillf kdb.attributes = handle->params.flags;
3210Sstevel@tonic-gate }
3220Sstevel@tonic-gate
3230Sstevel@tonic-gate if ((mask & KADM5_MAX_LIFE))
3240Sstevel@tonic-gate kdb.max_life = entry->max_life;
3250Sstevel@tonic-gate else
3260Sstevel@tonic-gate kdb.max_life = handle->params.max_life;
3270Sstevel@tonic-gate
3280Sstevel@tonic-gate if (mask & KADM5_MAX_RLIFE)
3290Sstevel@tonic-gate kdb.max_renewable_life = entry->max_renewable_life;
3300Sstevel@tonic-gate else
3310Sstevel@tonic-gate kdb.max_renewable_life = handle->params.max_rlife;
3320Sstevel@tonic-gate
3330Sstevel@tonic-gate if ((mask & KADM5_PRINC_EXPIRE_TIME))
3340Sstevel@tonic-gate kdb.expiration = entry->princ_expire_time;
3350Sstevel@tonic-gate else
3360Sstevel@tonic-gate kdb.expiration = handle->params.expiration;
3370Sstevel@tonic-gate
3380Sstevel@tonic-gate kdb.pw_expiration = 0;
3390Sstevel@tonic-gate if ((mask & KADM5_POLICY)) {
3400Sstevel@tonic-gate if(polent.pw_max_life)
3410Sstevel@tonic-gate kdb.pw_expiration = now + polent.pw_max_life;
3420Sstevel@tonic-gate else
3430Sstevel@tonic-gate kdb.pw_expiration = 0;
3440Sstevel@tonic-gate }
3450Sstevel@tonic-gate if ((mask & KADM5_PW_EXPIRATION))
3460Sstevel@tonic-gate kdb.pw_expiration = entry->pw_expiration;
3470Sstevel@tonic-gate
3480Sstevel@tonic-gate kdb.last_success = 0;
3490Sstevel@tonic-gate kdb.last_failed = 0;
3500Sstevel@tonic-gate kdb.fail_auth_count = 0;
3510Sstevel@tonic-gate
3520Sstevel@tonic-gate /* this is kind of gross, but in order to free the tl data, I need
3530Sstevel@tonic-gate to free the entire kdb entry, and that will try to free the
3540Sstevel@tonic-gate principal. */
3550Sstevel@tonic-gate
3564960Swillf if ((ret = kadm5_copy_principal(handle->context,
3574960Swillf entry->principal, &(kdb.princ)))) {
3580Sstevel@tonic-gate if (mask & KADM5_POLICY)
3590Sstevel@tonic-gate (void) kadm5_free_policy_ent(handle->lhandle, &polent);
3600Sstevel@tonic-gate return(ret);
3610Sstevel@tonic-gate }
3620Sstevel@tonic-gate
3632881Smp153739 if ((ret = krb5_dbe_update_last_pwd_change(handle->context, &kdb, now))) {
3644960Swillf krb5_db_free_principal(handle->context, &kdb, 1);
3652881Smp153739 if (mask & KADM5_POLICY)
3660Sstevel@tonic-gate (void) kadm5_free_policy_ent(handle->lhandle, &polent);
3672881Smp153739 return(ret);
3680Sstevel@tonic-gate }
3690Sstevel@tonic-gate
3704960Swillf if (mask & KADM5_TL_DATA) {
3714960Swillf /* splice entry->tl_data onto the front of kdb.tl_data */
3724960Swillf tl_data_orig = kdb.tl_data;
3734960Swillf for (tl_data_tail = entry->tl_data; tl_data_tail;
3744960Swillf tl_data_tail = tl_data_tail->tl_data_next)
3754960Swillf {
3764960Swillf ret = krb5_dbe_update_tl_data(handle->context, &kdb, tl_data_tail);
3774960Swillf if( ret )
3784960Swillf {
3794960Swillf krb5_db_free_principal(handle->context, &kdb, 1);
3804960Swillf if (mask & KADM5_POLICY)
3814960Swillf (void) kadm5_free_policy_ent(handle->lhandle, &polent);
3824960Swillf return ret;
3834960Swillf }
3844960Swillf }
3854960Swillf }
3864960Swillf
3870Sstevel@tonic-gate /* initialize the keys */
3880Sstevel@tonic-gate
3892881Smp153739 if ((ret = krb5_dbe_cpw(handle->context, &handle->master_keyblock,
3902881Smp153739 n_ks_tuple?ks_tuple:handle->params.keysalts,
3912881Smp153739 n_ks_tuple?n_ks_tuple:handle->params.num_keysalts,
3922881Smp153739 password,
3932881Smp153739 (mask & KADM5_KVNO)?entry->kvno:1,
3942881Smp153739 FALSE, &kdb))) {
3954960Swillf krb5_db_free_principal(handle->context, &kdb, 1);
3960Sstevel@tonic-gate if (mask & KADM5_POLICY)
3970Sstevel@tonic-gate (void) kadm5_free_policy_ent(handle->lhandle, &polent);
3980Sstevel@tonic-gate return(ret);
3990Sstevel@tonic-gate }
4000Sstevel@tonic-gate
4010Sstevel@tonic-gate /* populate the admin-server-specific fields. In the OV server,
4020Sstevel@tonic-gate this used to be in a separate database. Since there's already
4030Sstevel@tonic-gate marshalling code for the admin fields, to keep things simple,
4040Sstevel@tonic-gate I'm going to keep it, and make all the admin stuff occupy a
4050Sstevel@tonic-gate single tl_data record, */
4060Sstevel@tonic-gate
4070Sstevel@tonic-gate adb.admin_history_kvno = hist_kvno;
4080Sstevel@tonic-gate if ((mask & KADM5_POLICY)) {
4090Sstevel@tonic-gate adb.aux_attributes = KADM5_POLICY;
4100Sstevel@tonic-gate
4110Sstevel@tonic-gate /* this does *not* need to be strdup'ed, because adb is xdr */
4120Sstevel@tonic-gate /* encoded in osa_adb_create_princ, and not ever freed */
4130Sstevel@tonic-gate
4140Sstevel@tonic-gate adb.policy = entry->policy;
4150Sstevel@tonic-gate }
4160Sstevel@tonic-gate
4170Sstevel@tonic-gate /* increment the policy ref count, if any */
4180Sstevel@tonic-gate
4190Sstevel@tonic-gate if ((mask & KADM5_POLICY)) {
4200Sstevel@tonic-gate polent.policy_refcnt++;
4210Sstevel@tonic-gate if ((ret = kadm5_modify_policy_internal(handle->lhandle, &polent,
4220Sstevel@tonic-gate KADM5_REF_COUNT))
4230Sstevel@tonic-gate != KADM5_OK) {
4244960Swillf krb5_db_free_principal(handle->context, &kdb, 1);
4250Sstevel@tonic-gate if (mask & KADM5_POLICY)
4260Sstevel@tonic-gate (void) kadm5_free_policy_ent(handle->lhandle, &polent);
4270Sstevel@tonic-gate return(ret);
4280Sstevel@tonic-gate }
4290Sstevel@tonic-gate }
4300Sstevel@tonic-gate
4314960Swillf /* In all cases key and the principal data is set, let the database provider know */
4324960Swillf kdb.mask = mask | KADM5_KEY_DATA | KADM5_PRINCIPAL ;
4330Sstevel@tonic-gate
4340Sstevel@tonic-gate /* store the new db entry */
4350Sstevel@tonic-gate ret = kdb_put_entry(handle, &kdb, &adb);
4360Sstevel@tonic-gate
4374960Swillf krb5_db_free_principal(handle->context, &kdb, 1);
4380Sstevel@tonic-gate
4390Sstevel@tonic-gate if (ret) {
4400Sstevel@tonic-gate if ((mask & KADM5_POLICY)) {
4410Sstevel@tonic-gate /* decrement the policy ref count */
4420Sstevel@tonic-gate
4430Sstevel@tonic-gate polent.policy_refcnt--;
4440Sstevel@tonic-gate /*
4450Sstevel@tonic-gate * if this fails, there's nothing we can do anyway. the
4460Sstevel@tonic-gate * policy refcount wil be too high.
4470Sstevel@tonic-gate */
4480Sstevel@tonic-gate (void) kadm5_modify_policy_internal(handle->lhandle, &polent,
4490Sstevel@tonic-gate KADM5_REF_COUNT);
4500Sstevel@tonic-gate }
4510Sstevel@tonic-gate
4520Sstevel@tonic-gate if (mask & KADM5_POLICY)
4530Sstevel@tonic-gate (void) kadm5_free_policy_ent(handle->lhandle, &polent);
4540Sstevel@tonic-gate return(ret);
4550Sstevel@tonic-gate }
4560Sstevel@tonic-gate
4570Sstevel@tonic-gate if (mask & KADM5_POLICY)
4580Sstevel@tonic-gate (void) kadm5_free_policy_ent(handle->lhandle, &polent);
4590Sstevel@tonic-gate
4600Sstevel@tonic-gate return KADM5_OK;
4610Sstevel@tonic-gate }
4620Sstevel@tonic-gate
4630Sstevel@tonic-gate
4640Sstevel@tonic-gate kadm5_ret_t
kadm5_delete_principal(void * server_handle,krb5_principal principal)4650Sstevel@tonic-gate kadm5_delete_principal(void *server_handle, krb5_principal principal)
4660Sstevel@tonic-gate {
4670Sstevel@tonic-gate unsigned int ret;
4680Sstevel@tonic-gate kadm5_policy_ent_rec polent;
4690Sstevel@tonic-gate krb5_db_entry kdb;
4700Sstevel@tonic-gate osa_princ_ent_rec adb;
4710Sstevel@tonic-gate kadm5_server_handle_t handle = server_handle;
4720Sstevel@tonic-gate
4730Sstevel@tonic-gate CHECK_HANDLE(server_handle);
4740Sstevel@tonic-gate
4754960Swillf krb5_clear_error_message(handle->context);
4764960Swillf
4770Sstevel@tonic-gate if (principal == NULL)
4780Sstevel@tonic-gate return EINVAL;
4790Sstevel@tonic-gate
4802881Smp153739 if ((ret = kdb_get_entry(handle, principal, &kdb, &adb)))
4810Sstevel@tonic-gate return(ret);
4820Sstevel@tonic-gate
4830Sstevel@tonic-gate if ((adb.aux_attributes & KADM5_POLICY)) {
4840Sstevel@tonic-gate if ((ret = kadm5_get_policy(handle->lhandle,
4850Sstevel@tonic-gate adb.policy, &polent))
4860Sstevel@tonic-gate == KADM5_OK) {
4870Sstevel@tonic-gate polent.policy_refcnt--;
4880Sstevel@tonic-gate if ((ret = kadm5_modify_policy_internal(handle->lhandle, &polent,
4890Sstevel@tonic-gate KADM5_REF_COUNT))
4900Sstevel@tonic-gate != KADM5_OK) {
4910Sstevel@tonic-gate (void) kadm5_free_policy_ent(handle->lhandle, &polent);
4920Sstevel@tonic-gate kdb_free_entry(handle, &kdb, &adb);
4930Sstevel@tonic-gate return(ret);
4940Sstevel@tonic-gate }
4950Sstevel@tonic-gate }
4962881Smp153739 if ((ret = kadm5_free_policy_ent(handle->lhandle, &polent))) {
4972881Smp153739 kdb_free_entry(handle, &kdb, &adb);
4982881Smp153739 return ret;
4990Sstevel@tonic-gate }
5000Sstevel@tonic-gate }
5010Sstevel@tonic-gate
5020Sstevel@tonic-gate ret = kdb_delete_entry(handle, principal);
5030Sstevel@tonic-gate
5040Sstevel@tonic-gate kdb_free_entry(handle, &kdb, &adb);
5050Sstevel@tonic-gate
5060Sstevel@tonic-gate return ret;
5070Sstevel@tonic-gate }
5080Sstevel@tonic-gate
5090Sstevel@tonic-gate kadm5_ret_t
kadm5_modify_principal(void * server_handle,kadm5_principal_ent_t entry,long mask)5100Sstevel@tonic-gate kadm5_modify_principal(void *server_handle,
5110Sstevel@tonic-gate kadm5_principal_ent_t entry, long mask)
5120Sstevel@tonic-gate {
5130Sstevel@tonic-gate int ret, ret2, i;
5140Sstevel@tonic-gate kadm5_policy_ent_rec npol, opol;
5150Sstevel@tonic-gate int have_npol = 0, have_opol = 0;
5160Sstevel@tonic-gate krb5_db_entry kdb;
5172881Smp153739 krb5_tl_data *tl_data_orig;
5180Sstevel@tonic-gate osa_princ_ent_rec adb;
5190Sstevel@tonic-gate kadm5_server_handle_t handle = server_handle;
5200Sstevel@tonic-gate
5210Sstevel@tonic-gate CHECK_HANDLE(server_handle);
5220Sstevel@tonic-gate
5234960Swillf krb5_clear_error_message(handle->context);
5244960Swillf
5250Sstevel@tonic-gate if((mask & KADM5_PRINCIPAL) || (mask & KADM5_LAST_PWD_CHANGE) ||
5260Sstevel@tonic-gate (mask & KADM5_MOD_TIME) || (mask & KADM5_MOD_NAME) ||
5270Sstevel@tonic-gate (mask & KADM5_MKVNO) || (mask & KADM5_AUX_ATTRIBUTES) ||
5280Sstevel@tonic-gate (mask & KADM5_KEY_DATA) || (mask & KADM5_LAST_SUCCESS) ||
5290Sstevel@tonic-gate (mask & KADM5_LAST_FAILED))
5300Sstevel@tonic-gate return KADM5_BAD_MASK;
5310Sstevel@tonic-gate if((mask & ~ALL_PRINC_MASK))
5320Sstevel@tonic-gate return KADM5_BAD_MASK;
5330Sstevel@tonic-gate if((mask & KADM5_POLICY) && (mask & KADM5_POLICY_CLR))
5340Sstevel@tonic-gate return KADM5_BAD_MASK;
5350Sstevel@tonic-gate if(entry == (kadm5_principal_ent_t) NULL)
5360Sstevel@tonic-gate return EINVAL;
5370Sstevel@tonic-gate if (mask & KADM5_TL_DATA) {
5380Sstevel@tonic-gate tl_data_orig = entry->tl_data;
5390Sstevel@tonic-gate while (tl_data_orig) {
5400Sstevel@tonic-gate if (tl_data_orig->tl_data_type < 256)
5410Sstevel@tonic-gate return KADM5_BAD_TL_TYPE;
5420Sstevel@tonic-gate tl_data_orig = tl_data_orig->tl_data_next;
5430Sstevel@tonic-gate }
5440Sstevel@tonic-gate }
5450Sstevel@tonic-gate
5462881Smp153739 ret = kdb_get_entry(handle, entry->principal, &kdb, &adb);
5472881Smp153739 if (ret)
5480Sstevel@tonic-gate return(ret);
5490Sstevel@tonic-gate
5500Sstevel@tonic-gate /*
5510Sstevel@tonic-gate * This is pretty much the same as create ...
5520Sstevel@tonic-gate */
5530Sstevel@tonic-gate
5540Sstevel@tonic-gate if ((mask & KADM5_POLICY)) {
5550Sstevel@tonic-gate /* get the new policy */
5560Sstevel@tonic-gate ret = kadm5_get_policy(handle->lhandle, entry->policy, &npol);
5570Sstevel@tonic-gate if (ret) {
5580Sstevel@tonic-gate switch (ret) {
5590Sstevel@tonic-gate case EINVAL:
5600Sstevel@tonic-gate ret = KADM5_BAD_POLICY;
5610Sstevel@tonic-gate break;
5620Sstevel@tonic-gate case KADM5_UNK_POLICY:
5630Sstevel@tonic-gate case KADM5_BAD_POLICY:
5640Sstevel@tonic-gate ret = KADM5_UNK_POLICY;
5650Sstevel@tonic-gate break;
5660Sstevel@tonic-gate }
5670Sstevel@tonic-gate goto done;
5680Sstevel@tonic-gate }
5690Sstevel@tonic-gate have_npol = 1;
5700Sstevel@tonic-gate
5710Sstevel@tonic-gate /* if we already have a policy, get it to decrement the refcnt */
5720Sstevel@tonic-gate if(adb.aux_attributes & KADM5_POLICY) {
5730Sstevel@tonic-gate /* ... but not if the old and new are the same */
5740Sstevel@tonic-gate if(strcmp(adb.policy, entry->policy)) {
5750Sstevel@tonic-gate ret = kadm5_get_policy(handle->lhandle,
5760Sstevel@tonic-gate adb.policy, &opol);
5770Sstevel@tonic-gate switch(ret) {
5780Sstevel@tonic-gate case EINVAL:
5790Sstevel@tonic-gate case KADM5_BAD_POLICY:
5800Sstevel@tonic-gate case KADM5_UNK_POLICY:
5810Sstevel@tonic-gate break;
5820Sstevel@tonic-gate case KADM5_OK:
5830Sstevel@tonic-gate have_opol = 1;
5840Sstevel@tonic-gate opol.policy_refcnt--;
5850Sstevel@tonic-gate break;
5860Sstevel@tonic-gate default:
5870Sstevel@tonic-gate goto done;
5882881Smp153739 break;
5890Sstevel@tonic-gate }
5900Sstevel@tonic-gate npol.policy_refcnt++;
5910Sstevel@tonic-gate }
5920Sstevel@tonic-gate } else npol.policy_refcnt++;
5930Sstevel@tonic-gate
5940Sstevel@tonic-gate /* set us up to use the new policy */
5950Sstevel@tonic-gate adb.aux_attributes |= KADM5_POLICY;
5960Sstevel@tonic-gate if (adb.policy)
5970Sstevel@tonic-gate free(adb.policy);
5980Sstevel@tonic-gate adb.policy = strdup(entry->policy);
5990Sstevel@tonic-gate
6000Sstevel@tonic-gate /* set pw_max_life based on new policy */
6010Sstevel@tonic-gate if (npol.pw_max_life) {
6022881Smp153739 ret = krb5_dbe_lookup_last_pwd_change(handle->context, &kdb,
6032881Smp153739 &(kdb.pw_expiration));
6042881Smp153739 if (ret)
6052881Smp153739 goto done;
6062881Smp153739 kdb.pw_expiration += npol.pw_max_life;
6070Sstevel@tonic-gate } else {
6082881Smp153739 kdb.pw_expiration = 0;
6090Sstevel@tonic-gate }
6100Sstevel@tonic-gate }
6110Sstevel@tonic-gate
6120Sstevel@tonic-gate if ((mask & KADM5_POLICY_CLR) &&
6130Sstevel@tonic-gate (adb.aux_attributes & KADM5_POLICY)) {
6140Sstevel@tonic-gate ret = kadm5_get_policy(handle->lhandle, adb.policy, &opol);
6150Sstevel@tonic-gate switch(ret) {
6160Sstevel@tonic-gate case EINVAL:
6170Sstevel@tonic-gate case KADM5_BAD_POLICY:
6180Sstevel@tonic-gate case KADM5_UNK_POLICY:
6190Sstevel@tonic-gate ret = KADM5_BAD_DB;
6200Sstevel@tonic-gate goto done;
6212881Smp153739 break;
6220Sstevel@tonic-gate case KADM5_OK:
6230Sstevel@tonic-gate have_opol = 1;
6240Sstevel@tonic-gate if (adb.policy)
6250Sstevel@tonic-gate free(adb.policy);
6260Sstevel@tonic-gate adb.policy = NULL;
6270Sstevel@tonic-gate adb.aux_attributes &= ~KADM5_POLICY;
6280Sstevel@tonic-gate kdb.pw_expiration = 0;
6290Sstevel@tonic-gate opol.policy_refcnt--;
6300Sstevel@tonic-gate break;
6310Sstevel@tonic-gate default:
6320Sstevel@tonic-gate goto done;
6332881Smp153739 break;
6340Sstevel@tonic-gate }
6350Sstevel@tonic-gate }
6360Sstevel@tonic-gate
6370Sstevel@tonic-gate if (((mask & KADM5_POLICY) || (mask & KADM5_POLICY_CLR)) &&
6380Sstevel@tonic-gate (((have_opol) &&
6390Sstevel@tonic-gate (ret =
6400Sstevel@tonic-gate kadm5_modify_policy_internal(handle->lhandle, &opol,
6410Sstevel@tonic-gate KADM5_REF_COUNT))) ||
6420Sstevel@tonic-gate ((have_npol) &&
6430Sstevel@tonic-gate (ret =
6440Sstevel@tonic-gate kadm5_modify_policy_internal(handle->lhandle, &npol,
6450Sstevel@tonic-gate KADM5_REF_COUNT)))))
6460Sstevel@tonic-gate goto done;
6470Sstevel@tonic-gate
6480Sstevel@tonic-gate if ((mask & KADM5_ATTRIBUTES))
6490Sstevel@tonic-gate kdb.attributes = entry->attributes;
6500Sstevel@tonic-gate if ((mask & KADM5_MAX_LIFE))
6510Sstevel@tonic-gate kdb.max_life = entry->max_life;
6520Sstevel@tonic-gate if ((mask & KADM5_PRINC_EXPIRE_TIME))
6530Sstevel@tonic-gate kdb.expiration = entry->princ_expire_time;
6540Sstevel@tonic-gate if (mask & KADM5_PW_EXPIRATION)
6550Sstevel@tonic-gate kdb.pw_expiration = entry->pw_expiration;
6560Sstevel@tonic-gate if (mask & KADM5_MAX_RLIFE)
6570Sstevel@tonic-gate kdb.max_renewable_life = entry->max_renewable_life;
6580Sstevel@tonic-gate if (mask & KADM5_FAIL_AUTH_COUNT)
6590Sstevel@tonic-gate kdb.fail_auth_count = entry->fail_auth_count;
6600Sstevel@tonic-gate
6610Sstevel@tonic-gate if((mask & KADM5_KVNO)) {
6620Sstevel@tonic-gate for (i = 0; i < kdb.n_key_data; i++)
6630Sstevel@tonic-gate kdb.key_data[i].key_data_kvno = entry->kvno;
6640Sstevel@tonic-gate }
6650Sstevel@tonic-gate
6660Sstevel@tonic-gate if (mask & KADM5_TL_DATA) {
6674960Swillf krb5_tl_data *tl;
6684960Swillf
6694960Swillf /* may have to change the version number of the API. Updates the list with the given tl_data rather than over-writting */
6700Sstevel@tonic-gate
6714960Swillf for (tl = entry->tl_data; tl;
6724960Swillf tl = tl->tl_data_next)
6734960Swillf {
6744960Swillf ret = krb5_dbe_update_tl_data(handle->context, &kdb, tl);
6754960Swillf if( ret )
6764960Swillf {
6774960Swillf goto done;
6784960Swillf }
6790Sstevel@tonic-gate }
6800Sstevel@tonic-gate }
6810Sstevel@tonic-gate
6824960Swillf /* let the mask propagate to the database provider */
6834960Swillf kdb.mask = mask;
6844960Swillf
6850Sstevel@tonic-gate ret = kdb_put_entry(handle, &kdb, &adb);
6860Sstevel@tonic-gate if (ret) goto done;
6870Sstevel@tonic-gate
6880Sstevel@tonic-gate ret = KADM5_OK;
6890Sstevel@tonic-gate done:
6900Sstevel@tonic-gate if (have_opol) {
6910Sstevel@tonic-gate ret2 = kadm5_free_policy_ent(handle->lhandle, &opol);
6920Sstevel@tonic-gate ret = ret ? ret : ret2;
6930Sstevel@tonic-gate }
6940Sstevel@tonic-gate if (have_npol) {
6950Sstevel@tonic-gate ret2 = kadm5_free_policy_ent(handle->lhandle, &npol);
6960Sstevel@tonic-gate ret = ret ? ret : ret2;
6970Sstevel@tonic-gate }
6980Sstevel@tonic-gate kdb_free_entry(handle, &kdb, &adb);
6990Sstevel@tonic-gate return ret;
7000Sstevel@tonic-gate }
7010Sstevel@tonic-gate
7020Sstevel@tonic-gate kadm5_ret_t
kadm5_rename_principal(void * server_handle,krb5_principal source,krb5_principal target)7030Sstevel@tonic-gate kadm5_rename_principal(void *server_handle,
7040Sstevel@tonic-gate krb5_principal source, krb5_principal target)
7050Sstevel@tonic-gate {
7060Sstevel@tonic-gate krb5_db_entry kdb;
7070Sstevel@tonic-gate osa_princ_ent_rec adb;
7080Sstevel@tonic-gate int ret, i;
7090Sstevel@tonic-gate kadm5_server_handle_t handle = server_handle;
7100Sstevel@tonic-gate
7110Sstevel@tonic-gate CHECK_HANDLE(server_handle);
7120Sstevel@tonic-gate
7134960Swillf krb5_clear_error_message(handle->context);
7144960Swillf
7150Sstevel@tonic-gate if (source == NULL || target == NULL)
7160Sstevel@tonic-gate return EINVAL;
7170Sstevel@tonic-gate
7180Sstevel@tonic-gate if ((ret = kdb_get_entry(handle, target, &kdb, &adb)) == 0) {
7190Sstevel@tonic-gate kdb_free_entry(handle, &kdb, &adb);
7200Sstevel@tonic-gate return(KADM5_DUP);
7210Sstevel@tonic-gate }
7220Sstevel@tonic-gate
7230Sstevel@tonic-gate if ((ret = kdb_get_entry(handle, source, &kdb, &adb)))
7240Sstevel@tonic-gate return ret;
7250Sstevel@tonic-gate
7260Sstevel@tonic-gate /* this is kinda gross, but unavoidable */
7270Sstevel@tonic-gate
7280Sstevel@tonic-gate for (i=0; i<kdb.n_key_data; i++) {
7290Sstevel@tonic-gate if ((kdb.key_data[i].key_data_ver == 1) ||
7300Sstevel@tonic-gate (kdb.key_data[i].key_data_type[1] == KRB5_KDB_SALTTYPE_NORMAL)) {
7310Sstevel@tonic-gate ret = KADM5_NO_RENAME_SALT;
7320Sstevel@tonic-gate goto done;
7330Sstevel@tonic-gate }
7340Sstevel@tonic-gate }
7350Sstevel@tonic-gate
7364960Swillf kadm5_free_principal(handle->context, kdb.princ);
7374960Swillf ret = kadm5_copy_principal(handle->context, target, &kdb.princ);
7382881Smp153739 if (ret) {
7390Sstevel@tonic-gate kdb.princ = NULL; /* so freeing the dbe doesn't lose */
7400Sstevel@tonic-gate goto done;
7410Sstevel@tonic-gate }
7420Sstevel@tonic-gate
7430Sstevel@tonic-gate if ((ret = kdb_put_entry(handle, &kdb, &adb)))
7440Sstevel@tonic-gate goto done;
7450Sstevel@tonic-gate
7460Sstevel@tonic-gate ret = kdb_delete_entry(handle, source);
7470Sstevel@tonic-gate
7480Sstevel@tonic-gate done:
7490Sstevel@tonic-gate kdb_free_entry(handle, &kdb, &adb);
7500Sstevel@tonic-gate return ret;
7510Sstevel@tonic-gate }
7520Sstevel@tonic-gate
7530Sstevel@tonic-gate kadm5_ret_t
kadm5_get_principal(void * server_handle,krb5_principal principal,kadm5_principal_ent_t entry,long in_mask)7540Sstevel@tonic-gate kadm5_get_principal(void *server_handle, krb5_principal principal,
7550Sstevel@tonic-gate kadm5_principal_ent_t entry,
7560Sstevel@tonic-gate long in_mask)
7570Sstevel@tonic-gate {
7580Sstevel@tonic-gate krb5_db_entry kdb;
7590Sstevel@tonic-gate osa_princ_ent_rec adb;
7604960Swillf krb5_error_code ret = 0;
7610Sstevel@tonic-gate long mask;
7620Sstevel@tonic-gate int i;
7630Sstevel@tonic-gate kadm5_server_handle_t handle = server_handle;
7640Sstevel@tonic-gate kadm5_principal_ent_rec entry_local, *entry_orig;
7650Sstevel@tonic-gate
7660Sstevel@tonic-gate CHECK_HANDLE(server_handle);
7670Sstevel@tonic-gate
7684960Swillf krb5_clear_error_message(handle->context);
7694960Swillf
7700Sstevel@tonic-gate /*
7710Sstevel@tonic-gate * In version 1, all the defined fields are always returned.
7720Sstevel@tonic-gate * entry is a pointer to a kadm5_principal_ent_t_v1 that should be
7730Sstevel@tonic-gate * filled with allocated memory.
7740Sstevel@tonic-gate */
7750Sstevel@tonic-gate if (handle->api_version == KADM5_API_VERSION_1) {
7760Sstevel@tonic-gate mask = KADM5_PRINCIPAL_NORMAL_MASK;
7770Sstevel@tonic-gate entry_orig = entry;
7780Sstevel@tonic-gate entry = &entry_local;
7790Sstevel@tonic-gate } else {
7800Sstevel@tonic-gate mask = in_mask;
7810Sstevel@tonic-gate }
7820Sstevel@tonic-gate
7830Sstevel@tonic-gate memset((char *) entry, 0, sizeof(*entry));
7840Sstevel@tonic-gate
7850Sstevel@tonic-gate if (principal == NULL)
7860Sstevel@tonic-gate return EINVAL;
7870Sstevel@tonic-gate
7880Sstevel@tonic-gate if ((ret = kdb_get_entry(handle, principal, &kdb, &adb)))
7890Sstevel@tonic-gate return ret;
7900Sstevel@tonic-gate
7910Sstevel@tonic-gate if ((mask & KADM5_POLICY) &&
7920Sstevel@tonic-gate adb.policy && (adb.aux_attributes & KADM5_POLICY)) {
7930Sstevel@tonic-gate if ((entry->policy = (char *) malloc(strlen(adb.policy) + 1)) == NULL) {
7940Sstevel@tonic-gate ret = ENOMEM;
7950Sstevel@tonic-gate goto done;
7960Sstevel@tonic-gate }
7970Sstevel@tonic-gate strcpy(entry->policy, adb.policy);
7980Sstevel@tonic-gate }
7990Sstevel@tonic-gate
8000Sstevel@tonic-gate if (mask & KADM5_AUX_ATTRIBUTES)
8010Sstevel@tonic-gate entry->aux_attributes = adb.aux_attributes;
8020Sstevel@tonic-gate
8030Sstevel@tonic-gate if ((mask & KADM5_PRINCIPAL) &&
8040Sstevel@tonic-gate (ret = krb5_copy_principal(handle->context, principal,
8050Sstevel@tonic-gate &entry->principal))) {
8060Sstevel@tonic-gate goto done;
8070Sstevel@tonic-gate }
8080Sstevel@tonic-gate
8090Sstevel@tonic-gate if (mask & KADM5_PRINC_EXPIRE_TIME)
8100Sstevel@tonic-gate entry->princ_expire_time = kdb.expiration;
8110Sstevel@tonic-gate
8120Sstevel@tonic-gate if ((mask & KADM5_LAST_PWD_CHANGE) &&
8130Sstevel@tonic-gate (ret = krb5_dbe_lookup_last_pwd_change(handle->context, &kdb,
8140Sstevel@tonic-gate &(entry->last_pwd_change)))) {
8150Sstevel@tonic-gate goto done;
8160Sstevel@tonic-gate }
8170Sstevel@tonic-gate
8180Sstevel@tonic-gate if (mask & KADM5_PW_EXPIRATION)
8190Sstevel@tonic-gate entry->pw_expiration = kdb.pw_expiration;
8200Sstevel@tonic-gate if (mask & KADM5_MAX_LIFE)
8210Sstevel@tonic-gate entry->max_life = kdb.max_life;
8220Sstevel@tonic-gate
8230Sstevel@tonic-gate /* this is a little non-sensical because the function returns two */
8240Sstevel@tonic-gate /* values that must be checked separately against the mask */
8250Sstevel@tonic-gate if ((mask & KADM5_MOD_NAME) || (mask & KADM5_MOD_TIME)) {
8262881Smp153739 ret = krb5_dbe_lookup_mod_princ_data(handle->context, &kdb,
8272881Smp153739 &(entry->mod_date),
8282881Smp153739 &(entry->mod_name));
8292881Smp153739 if (ret) {
8302881Smp153739 goto done;
8312881Smp153739 }
8322881Smp153739
8332881Smp153739 if (! (mask & KADM5_MOD_TIME))
8342881Smp153739 entry->mod_date = 0;
8352881Smp153739 if (! (mask & KADM5_MOD_NAME)) {
8362881Smp153739 krb5_free_principal(handle->context, entry->principal);
8372881Smp153739 entry->principal = NULL;
8382881Smp153739 }
8390Sstevel@tonic-gate }
8400Sstevel@tonic-gate
8410Sstevel@tonic-gate if (mask & KADM5_ATTRIBUTES)
8420Sstevel@tonic-gate entry->attributes = kdb.attributes;
8430Sstevel@tonic-gate
8440Sstevel@tonic-gate if (mask & KADM5_KVNO)
8450Sstevel@tonic-gate for (entry->kvno = 0, i=0; i<kdb.n_key_data; i++)
8460Sstevel@tonic-gate if (kdb.key_data[i].key_data_kvno > entry->kvno)
8470Sstevel@tonic-gate entry->kvno = kdb.key_data[i].key_data_kvno;
8480Sstevel@tonic-gate
8490Sstevel@tonic-gate if (handle->api_version == KADM5_API_VERSION_2)
8500Sstevel@tonic-gate entry->mkvno = 0;
8510Sstevel@tonic-gate else {
8520Sstevel@tonic-gate /* XXX I'll be damned if I know how to deal with this one --marc */
8530Sstevel@tonic-gate entry->mkvno = 1;
8540Sstevel@tonic-gate }
8550Sstevel@tonic-gate
8560Sstevel@tonic-gate /*
8570Sstevel@tonic-gate * The new fields that only exist in version 2 start here
8580Sstevel@tonic-gate */
8590Sstevel@tonic-gate if (handle->api_version == KADM5_API_VERSION_2) {
8600Sstevel@tonic-gate if (mask & KADM5_MAX_RLIFE)
8610Sstevel@tonic-gate entry->max_renewable_life = kdb.max_renewable_life;
8620Sstevel@tonic-gate if (mask & KADM5_LAST_SUCCESS)
8630Sstevel@tonic-gate entry->last_success = kdb.last_success;
8640Sstevel@tonic-gate if (mask & KADM5_LAST_FAILED)
8650Sstevel@tonic-gate entry->last_failed = kdb.last_failed;
8660Sstevel@tonic-gate if (mask & KADM5_FAIL_AUTH_COUNT)
8670Sstevel@tonic-gate entry->fail_auth_count = kdb.fail_auth_count;
8680Sstevel@tonic-gate if (mask & KADM5_TL_DATA) {
8692881Smp153739 krb5_tl_data *tl, *tl2;
8700Sstevel@tonic-gate
8710Sstevel@tonic-gate entry->tl_data = NULL;
8720Sstevel@tonic-gate
8730Sstevel@tonic-gate tl = kdb.tl_data;
8740Sstevel@tonic-gate while (tl) {
8750Sstevel@tonic-gate if (tl->tl_data_type > 255) {
8760Sstevel@tonic-gate if ((tl2 = dup_tl_data(tl)) == NULL) {
8770Sstevel@tonic-gate ret = ENOMEM;
8780Sstevel@tonic-gate goto done;
8790Sstevel@tonic-gate }
8800Sstevel@tonic-gate tl2->tl_data_next = entry->tl_data;
8810Sstevel@tonic-gate entry->tl_data = tl2;
8820Sstevel@tonic-gate entry->n_tl_data++;
8830Sstevel@tonic-gate }
8840Sstevel@tonic-gate
8850Sstevel@tonic-gate tl = tl->tl_data_next;
8860Sstevel@tonic-gate }
8870Sstevel@tonic-gate }
8880Sstevel@tonic-gate if (mask & KADM5_KEY_DATA) {
8890Sstevel@tonic-gate entry->n_key_data = kdb.n_key_data;
8900Sstevel@tonic-gate if(entry->n_key_data) {
8910Sstevel@tonic-gate entry->key_data = (krb5_key_data *)
8920Sstevel@tonic-gate malloc(entry->n_key_data*sizeof(krb5_key_data));
8930Sstevel@tonic-gate if (entry->key_data == NULL) {
8940Sstevel@tonic-gate ret = ENOMEM;
8950Sstevel@tonic-gate goto done;
8960Sstevel@tonic-gate }
8970Sstevel@tonic-gate } else
8980Sstevel@tonic-gate entry->key_data = NULL;
8990Sstevel@tonic-gate
9000Sstevel@tonic-gate for (i = 0; i < entry->n_key_data; i++)
9012881Smp153739 ret = krb5_copy_key_data_contents(handle->context,
9022881Smp153739 &kdb.key_data[i],
9032881Smp153739 &entry->key_data[i]);
9042881Smp153739 if (ret)
9050Sstevel@tonic-gate goto done;
9060Sstevel@tonic-gate }
9070Sstevel@tonic-gate }
9080Sstevel@tonic-gate
9090Sstevel@tonic-gate /*
9100Sstevel@tonic-gate * If KADM5_API_VERSION_1, we return an allocated structure, and
9110Sstevel@tonic-gate * we need to convert the new structure back into the format the
9120Sstevel@tonic-gate * caller is expecting.
9130Sstevel@tonic-gate */
9140Sstevel@tonic-gate if (handle->api_version == KADM5_API_VERSION_1) {
9150Sstevel@tonic-gate kadm5_principal_ent_t_v1 newv1;
9160Sstevel@tonic-gate
9170Sstevel@tonic-gate newv1 = ((kadm5_principal_ent_t_v1) calloc(1, sizeof(*newv1)));
9180Sstevel@tonic-gate if (newv1 == NULL) {
9190Sstevel@tonic-gate ret = ENOMEM;
9200Sstevel@tonic-gate goto done;
9210Sstevel@tonic-gate }
9220Sstevel@tonic-gate
9230Sstevel@tonic-gate newv1->principal = entry->principal;
9240Sstevel@tonic-gate newv1->princ_expire_time = entry->princ_expire_time;
9250Sstevel@tonic-gate newv1->last_pwd_change = entry->last_pwd_change;
9260Sstevel@tonic-gate newv1->pw_expiration = entry->pw_expiration;
9270Sstevel@tonic-gate newv1->max_life = entry->max_life;
9280Sstevel@tonic-gate newv1->mod_name = entry->mod_name;
9290Sstevel@tonic-gate newv1->mod_date = entry->mod_date;
9300Sstevel@tonic-gate newv1->attributes = entry->attributes;
9310Sstevel@tonic-gate newv1->kvno = entry->kvno;
9320Sstevel@tonic-gate newv1->mkvno = entry->mkvno;
9330Sstevel@tonic-gate newv1->policy = entry->policy;
9340Sstevel@tonic-gate newv1->aux_attributes = entry->aux_attributes;
9350Sstevel@tonic-gate
9360Sstevel@tonic-gate *((kadm5_principal_ent_t_v1 *) entry_orig) = newv1;
9370Sstevel@tonic-gate }
9380Sstevel@tonic-gate
9390Sstevel@tonic-gate ret = KADM5_OK;
9400Sstevel@tonic-gate
9410Sstevel@tonic-gate done:
9420Sstevel@tonic-gate if (ret && entry->principal)
9430Sstevel@tonic-gate krb5_free_principal(handle->context, entry->principal);
9440Sstevel@tonic-gate kdb_free_entry(handle, &kdb, &adb);
9450Sstevel@tonic-gate
9460Sstevel@tonic-gate return ret;
9470Sstevel@tonic-gate }
9480Sstevel@tonic-gate
9490Sstevel@tonic-gate /*
9500Sstevel@tonic-gate * Function: check_pw_reuse
9510Sstevel@tonic-gate *
9520Sstevel@tonic-gate * Purpose: Check if a key appears in a list of keys, in order to
9530Sstevel@tonic-gate * enforce password history.
9540Sstevel@tonic-gate *
9550Sstevel@tonic-gate * Arguments:
9560Sstevel@tonic-gate *
9570Sstevel@tonic-gate * context (r) the krb5 context
9580Sstevel@tonic-gate * hist_keyblock (r) the key that hist_key_data is
9590Sstevel@tonic-gate * encrypted in
9600Sstevel@tonic-gate * n_new_key_data (r) length of new_key_data
9610Sstevel@tonic-gate * new_key_data (r) keys to check against
9620Sstevel@tonic-gate * pw_hist_data, encrypted in hist_keyblock
9630Sstevel@tonic-gate * n_pw_hist_data (r) length of pw_hist_data
9640Sstevel@tonic-gate * pw_hist_data (r) passwords to check new_key_data against
9650Sstevel@tonic-gate *
9660Sstevel@tonic-gate * Effects:
9670Sstevel@tonic-gate * For each new_key in new_key_data:
9680Sstevel@tonic-gate * decrypt new_key with the master_keyblock
9690Sstevel@tonic-gate * for each password in pw_hist_data:
9700Sstevel@tonic-gate * for each hist_key in password:
9710Sstevel@tonic-gate * decrypt hist_key with hist_keyblock
9720Sstevel@tonic-gate * compare the new_key and hist_key
9730Sstevel@tonic-gate *
9740Sstevel@tonic-gate * Returns krb5 errors, KADM5_PASS_RESUSE if a key in
9750Sstevel@tonic-gate * new_key_data is the same as a key in pw_hist_data, or 0.
9760Sstevel@tonic-gate */
9770Sstevel@tonic-gate 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)9780Sstevel@tonic-gate check_pw_reuse(krb5_context context,
9790Sstevel@tonic-gate krb5_keyblock *master_keyblock,
9800Sstevel@tonic-gate krb5_keyblock *hist_keyblock,
9810Sstevel@tonic-gate int n_new_key_data, krb5_key_data *new_key_data,
9822881Smp153739 unsigned int n_pw_hist_data, osa_pw_hist_ent *pw_hist_data)
9830Sstevel@tonic-gate {
9840Sstevel@tonic-gate int x, y, z;
9850Sstevel@tonic-gate krb5_keyblock newkey, histkey;
9860Sstevel@tonic-gate krb5_error_code ret;
9870Sstevel@tonic-gate
9880Sstevel@tonic-gate for (x = 0; x < n_new_key_data; x++) {
9892881Smp153739 ret = krb5_dbekd_decrypt_key_data(context,
9902881Smp153739 master_keyblock,
9912881Smp153739 &(new_key_data[x]),
9922881Smp153739 &newkey, NULL);
9932881Smp153739 if (ret)
9940Sstevel@tonic-gate return(ret);
9950Sstevel@tonic-gate for (y = 0; y < n_pw_hist_data; y++) {
9960Sstevel@tonic-gate for (z = 0; z < pw_hist_data[y].n_key_data; z++) {
9972881Smp153739 ret = krb5_dbekd_decrypt_key_data(context,
9982881Smp153739 hist_keyblock,
9992881Smp153739 &pw_hist_data[y].key_data[z],
10002881Smp153739 &histkey, NULL);
10012881Smp153739 if (ret)
10022881Smp153739 return(ret);
10032881Smp153739
10042881Smp153739 if ((newkey.length == histkey.length) &&
10052881Smp153739 (newkey.enctype == histkey.enctype) &&
10062881Smp153739 (memcmp(newkey.contents, histkey.contents,
10072881Smp153739 histkey.length) == 0)) {
10082881Smp153739 krb5_free_keyblock_contents(context, &histkey);
10092881Smp153739 krb5_free_keyblock_contents(context, &newkey);
10102881Smp153739
10112881Smp153739 return(KADM5_PASS_REUSE);
10122881Smp153739 }
10132881Smp153739 krb5_free_keyblock_contents(context, &histkey);
10140Sstevel@tonic-gate }
10150Sstevel@tonic-gate }
10160Sstevel@tonic-gate krb5_free_keyblock_contents(context, &newkey);
10170Sstevel@tonic-gate }
10180Sstevel@tonic-gate
10190Sstevel@tonic-gate return(0);
10200Sstevel@tonic-gate }
10210Sstevel@tonic-gate
10220Sstevel@tonic-gate /*
10230Sstevel@tonic-gate * Function: create_history_entry
10240Sstevel@tonic-gate *
10250Sstevel@tonic-gate * Purpose: Creates a password history entry from an array of
10260Sstevel@tonic-gate * key_data.
10270Sstevel@tonic-gate *
10280Sstevel@tonic-gate * Arguments:
10290Sstevel@tonic-gate *
10300Sstevel@tonic-gate * context (r) krb5_context to use
10310Sstevel@tonic-gate * master_keyblcok (r) master key block
10320Sstevel@tonic-gate * n_key_data (r) number of elements in key_data
10330Sstevel@tonic-gate * key_data (r) keys to add to the history entry
10340Sstevel@tonic-gate * hist (w) history entry to fill in
10350Sstevel@tonic-gate *
10360Sstevel@tonic-gate * Effects:
10370Sstevel@tonic-gate *
10380Sstevel@tonic-gate * hist->key_data is allocated to store n_key_data key_datas. Each
10390Sstevel@tonic-gate * element of key_data is decrypted with master_keyblock, re-encrypted
10400Sstevel@tonic-gate * in hist_key, and added to hist->key_data. hist->n_key_data is
10410Sstevel@tonic-gate * set to n_key_data.
10420Sstevel@tonic-gate */
10430Sstevel@tonic-gate 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)10440Sstevel@tonic-gate int create_history_entry(krb5_context context,
10450Sstevel@tonic-gate krb5_keyblock *master_keyblock, int n_key_data,
10460Sstevel@tonic-gate krb5_key_data *key_data, osa_pw_hist_ent *hist)
10470Sstevel@tonic-gate {
10480Sstevel@tonic-gate int i, ret;
10490Sstevel@tonic-gate krb5_keyblock key;
10500Sstevel@tonic-gate krb5_keysalt salt;
10510Sstevel@tonic-gate
10520Sstevel@tonic-gate hist->key_data = (krb5_key_data*)malloc(n_key_data*sizeof(krb5_key_data));
10530Sstevel@tonic-gate if (hist->key_data == NULL)
10540Sstevel@tonic-gate return ENOMEM;
10550Sstevel@tonic-gate memset(hist->key_data, 0, n_key_data*sizeof(krb5_key_data));
10560Sstevel@tonic-gate
10570Sstevel@tonic-gate for (i = 0; i < n_key_data; i++) {
10582881Smp153739 ret = krb5_dbekd_decrypt_key_data(context,
10592881Smp153739 master_keyblock,
10602881Smp153739 &key_data[i],
10612881Smp153739 &key, &salt);
10622881Smp153739 if (ret)
10632881Smp153739 return ret;
10642881Smp153739
10652881Smp153739 ret = krb5_dbekd_encrypt_key_data(context, &hist_key,
10662881Smp153739 &key, &salt,
10672881Smp153739 key_data[i].key_data_kvno,
10682881Smp153739 &hist->key_data[i]);
10692881Smp153739 if (ret)
10702881Smp153739 return ret;
10712881Smp153739
10722881Smp153739 krb5_free_keyblock_contents(context, &key);
10732881Smp153739 /* krb5_free_keysalt(context, &salt); */
10740Sstevel@tonic-gate }
10750Sstevel@tonic-gate
10760Sstevel@tonic-gate hist->n_key_data = n_key_data;
10770Sstevel@tonic-gate return 0;
10780Sstevel@tonic-gate }
10790Sstevel@tonic-gate
10802881Smp153739 static
free_history_entry(krb5_context context,osa_pw_hist_ent * hist)10810Sstevel@tonic-gate void free_history_entry(krb5_context context, osa_pw_hist_ent *hist)
10820Sstevel@tonic-gate {
10830Sstevel@tonic-gate int i;
10840Sstevel@tonic-gate
10850Sstevel@tonic-gate for (i = 0; i < hist->n_key_data; i++)
10860Sstevel@tonic-gate krb5_free_key_data_contents(context, &hist->key_data[i]);
10870Sstevel@tonic-gate free(hist->key_data);
10880Sstevel@tonic-gate }
10890Sstevel@tonic-gate
10900Sstevel@tonic-gate /*
10910Sstevel@tonic-gate * Function: add_to_history
10920Sstevel@tonic-gate *
10930Sstevel@tonic-gate * Purpose: Adds a password to a principal's password history.
10940Sstevel@tonic-gate *
10950Sstevel@tonic-gate * Arguments:
10960Sstevel@tonic-gate *
10970Sstevel@tonic-gate * context (r) krb5_context to use
10980Sstevel@tonic-gate * adb (r/w) admin principal entry to add keys to
10990Sstevel@tonic-gate * pol (r) adb's policy
11000Sstevel@tonic-gate * pw (r) keys for the password to add to adb's key history
11010Sstevel@tonic-gate *
11020Sstevel@tonic-gate * Effects:
11030Sstevel@tonic-gate *
11040Sstevel@tonic-gate * add_to_history adds a single password to adb's password history.
11050Sstevel@tonic-gate * pw contains n_key_data keys in its key_data, in storage should be
11060Sstevel@tonic-gate * allocated but not freed by the caller (XXX blech!).
11070Sstevel@tonic-gate *
11080Sstevel@tonic-gate * This function maintains adb->old_keys as a circular queue. It
11090Sstevel@tonic-gate * starts empty, and grows each time this function is called until it
11100Sstevel@tonic-gate * is pol->pw_history_num items long. adb->old_key_len holds the
11110Sstevel@tonic-gate * number of allocated entries in the array, and must therefore be [0,
11120Sstevel@tonic-gate * pol->pw_history_num). adb->old_key_next is the index into the
11130Sstevel@tonic-gate * array where the next element should be written, and must be [0,
11140Sstevel@tonic-gate * adb->old_key_len).
11150Sstevel@tonic-gate */
11160Sstevel@tonic-gate #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)11172881Smp153739 static kadm5_ret_t add_to_history(krb5_context context,
11182881Smp153739 osa_princ_ent_t adb,
11192881Smp153739 kadm5_policy_ent_t pol,
11202881Smp153739 osa_pw_hist_ent *pw)
11210Sstevel@tonic-gate {
11222881Smp153739 osa_pw_hist_ent *histp;
11237934SMark.Phalan@Sun.COM uint32_t nhist;
11247934SMark.Phalan@Sun.COM unsigned int i, knext, nkeys;
11250Sstevel@tonic-gate
11267934SMark.Phalan@Sun.COM nhist = pol->pw_history_num;
11277934SMark.Phalan@Sun.COM /* A history of 1 means just check the current password */
11287934SMark.Phalan@Sun.COM if (nhist <= 1)
11297934SMark.Phalan@Sun.COM return 0;
11300Sstevel@tonic-gate
11317934SMark.Phalan@Sun.COM nkeys = adb->old_key_len;
11327934SMark.Phalan@Sun.COM knext = adb->old_key_next;
11337934SMark.Phalan@Sun.COM /* resize the adb->old_keys array if necessary */
11347934SMark.Phalan@Sun.COM if (nkeys + 1 < nhist) {
11357934SMark.Phalan@Sun.COM if (adb->old_keys == NULL) {
11367934SMark.Phalan@Sun.COM adb->old_keys = (osa_pw_hist_ent *)
11377934SMark.Phalan@Sun.COM malloc((nkeys + 1) * sizeof (osa_pw_hist_ent));
11387934SMark.Phalan@Sun.COM } else {
11397934SMark.Phalan@Sun.COM adb->old_keys = (osa_pw_hist_ent *)
11407934SMark.Phalan@Sun.COM realloc(adb->old_keys,
11417934SMark.Phalan@Sun.COM (nkeys + 1) * sizeof (osa_pw_hist_ent));
11427934SMark.Phalan@Sun.COM }
11437934SMark.Phalan@Sun.COM if (adb->old_keys == NULL)
11447934SMark.Phalan@Sun.COM return(ENOMEM);
11457934SMark.Phalan@Sun.COM
11467934SMark.Phalan@Sun.COM memset(&adb->old_keys[nkeys], 0, sizeof(osa_pw_hist_ent));
11477934SMark.Phalan@Sun.COM nkeys = ++adb->old_key_len;
11487934SMark.Phalan@Sun.COM /*
11497934SMark.Phalan@Sun.COM * To avoid losing old keys, shift forward each entry after
11507934SMark.Phalan@Sun.COM * knext.
11517934SMark.Phalan@Sun.COM */
11527934SMark.Phalan@Sun.COM for (i = nkeys - 1; i > knext; i--) {
11537934SMark.Phalan@Sun.COM adb->old_keys[i] = adb->old_keys[i - 1];
11547934SMark.Phalan@Sun.COM }
11557934SMark.Phalan@Sun.COM memset(&adb->old_keys[knext], 0, sizeof(osa_pw_hist_ent));
11567934SMark.Phalan@Sun.COM } else if (nkeys + 1 > nhist) {
11577934SMark.Phalan@Sun.COM /*
11587934SMark.Phalan@Sun.COM * The policy must have changed! Shrink the array.
11597934SMark.Phalan@Sun.COM * Can't simply realloc() down, since it might be wrapped.
11607934SMark.Phalan@Sun.COM * To understand the arithmetic below, note that we are
11617934SMark.Phalan@Sun.COM * copying into new positions 0 .. N-1 from old positions
11627934SMark.Phalan@Sun.COM * old_key_next-N .. old_key_next-1, modulo old_key_len,
11637934SMark.Phalan@Sun.COM * where N = pw_history_num - 1 is the length of the
11647934SMark.Phalan@Sun.COM * shortened list. Matt Crawford, FNAL
11657934SMark.Phalan@Sun.COM */
11667934SMark.Phalan@Sun.COM /*
11677934SMark.Phalan@Sun.COM * M = adb->old_key_len, N = pol->pw_history_num - 1
11687934SMark.Phalan@Sun.COM *
11697934SMark.Phalan@Sun.COM * tmp[0] .. tmp[N-1] = old[(knext-N)%M] .. old[(knext-1)%M]
11707934SMark.Phalan@Sun.COM */
11717934SMark.Phalan@Sun.COM int j;
11727934SMark.Phalan@Sun.COM osa_pw_hist_t tmp;
11730Sstevel@tonic-gate
11747934SMark.Phalan@Sun.COM tmp = (osa_pw_hist_ent *)
11757934SMark.Phalan@Sun.COM malloc((nhist - 1) * sizeof (osa_pw_hist_ent));
11767934SMark.Phalan@Sun.COM if (tmp == NULL)
11777934SMark.Phalan@Sun.COM return ENOMEM;
11787934SMark.Phalan@Sun.COM for (i = 0; i < nhist - 1; i++) {
11797934SMark.Phalan@Sun.COM /*
11807934SMark.Phalan@Sun.COM * Add nkeys once before taking remainder to avoid
11817934SMark.Phalan@Sun.COM * negative values.
11827934SMark.Phalan@Sun.COM */
11837934SMark.Phalan@Sun.COM j = (i + nkeys + knext - (nhist - 1)) % nkeys;
11847934SMark.Phalan@Sun.COM tmp[i] = adb->old_keys[j];
11857934SMark.Phalan@Sun.COM }
11867934SMark.Phalan@Sun.COM /* Now free the ones we don't keep (the oldest ones) */
11877934SMark.Phalan@Sun.COM for (i = 0; i < nkeys - (nhist - 1); i++) {
11887934SMark.Phalan@Sun.COM j = (i + nkeys + knext) % nkeys;
11897934SMark.Phalan@Sun.COM histp = &adb->old_keys[j];
11907934SMark.Phalan@Sun.COM for (j = 0; j < histp->n_key_data; j++) {
11917934SMark.Phalan@Sun.COM krb5_free_key_data_contents(context, &histp->key_data[j]);
11927934SMark.Phalan@Sun.COM }
11937934SMark.Phalan@Sun.COM free(histp->key_data);
11947934SMark.Phalan@Sun.COM }
11957934SMark.Phalan@Sun.COM free((void *)adb->old_keys);
11967934SMark.Phalan@Sun.COM adb->old_keys = tmp;
11977934SMark.Phalan@Sun.COM nkeys = adb->old_key_len = nhist - 1;
11987934SMark.Phalan@Sun.COM knext = adb->old_key_next = 0;
11997934SMark.Phalan@Sun.COM }
12000Sstevel@tonic-gate
12017934SMark.Phalan@Sun.COM /*
12027934SMark.Phalan@Sun.COM * If nhist decreased since the last password change, and nkeys+1
12037934SMark.Phalan@Sun.COM * is less than the previous nhist, it is possible for knext to
12047934SMark.Phalan@Sun.COM * index into unallocated space. This condition would not be
12057934SMark.Phalan@Sun.COM * caught by the resizing code above.
12067934SMark.Phalan@Sun.COM */
12077934SMark.Phalan@Sun.COM if (knext + 1 > nkeys)
12087934SMark.Phalan@Sun.COM knext = adb->old_key_next = 0;
12097934SMark.Phalan@Sun.COM /* free the old pw history entry if it contains data */
12107934SMark.Phalan@Sun.COM histp = &adb->old_keys[knext];
12117934SMark.Phalan@Sun.COM for (i = 0; i < histp->n_key_data; i++)
12127934SMark.Phalan@Sun.COM krb5_free_key_data_contents(context, &histp->key_data[i]);
12137934SMark.Phalan@Sun.COM free(histp->key_data);
12140Sstevel@tonic-gate
12157934SMark.Phalan@Sun.COM /* store the new entry */
12167934SMark.Phalan@Sun.COM adb->old_keys[knext] = *pw;
12170Sstevel@tonic-gate
12187934SMark.Phalan@Sun.COM /* update the next pointer */
12197934SMark.Phalan@Sun.COM if (++adb->old_key_next == nhist - 1)
12207934SMark.Phalan@Sun.COM adb->old_key_next = 0;
12217934SMark.Phalan@Sun.COM
12227934SMark.Phalan@Sun.COM return(0);
12230Sstevel@tonic-gate }
12240Sstevel@tonic-gate #undef KADM_MOD
12250Sstevel@tonic-gate
12267934SMark.Phalan@Sun.COM #ifdef USE_PASSWORD_SERVER
12277934SMark.Phalan@Sun.COM /* FIXME: don't use global variable for this */
12287934SMark.Phalan@Sun.COM krb5_boolean use_password_server = 0;
12297934SMark.Phalan@Sun.COM
12307934SMark.Phalan@Sun.COM static krb5_boolean
kadm5_use_password_server(void)12317934SMark.Phalan@Sun.COM kadm5_use_password_server (void)
12327934SMark.Phalan@Sun.COM {
12337934SMark.Phalan@Sun.COM return use_password_server;
12347934SMark.Phalan@Sun.COM }
12357934SMark.Phalan@Sun.COM
12367934SMark.Phalan@Sun.COM void
kadm5_set_use_password_server(void)12377934SMark.Phalan@Sun.COM kadm5_set_use_password_server (void)
12387934SMark.Phalan@Sun.COM {
12397934SMark.Phalan@Sun.COM use_password_server = 1;
12407934SMark.Phalan@Sun.COM }
12417934SMark.Phalan@Sun.COM #endif
12427934SMark.Phalan@Sun.COM
12437934SMark.Phalan@Sun.COM #ifdef USE_PASSWORD_SERVER
12447934SMark.Phalan@Sun.COM
12457934SMark.Phalan@Sun.COM /*
12467934SMark.Phalan@Sun.COM * kadm5_launch_task () runs a program (task_path) to synchronize the
12477934SMark.Phalan@Sun.COM * Apple password server with the Kerberos database. Password server
12487934SMark.Phalan@Sun.COM * programs can receive arguments on the command line (task_argv)
12497934SMark.Phalan@Sun.COM * and a block of data via stdin (data_buffer).
12507934SMark.Phalan@Sun.COM *
12517934SMark.Phalan@Sun.COM * Because a failure to communicate with the tool results in the
12527934SMark.Phalan@Sun.COM * password server falling out of sync with the database,
12537934SMark.Phalan@Sun.COM * kadm5_launch_task() always fails if it can't talk to the tool.
12547934SMark.Phalan@Sun.COM */
12557934SMark.Phalan@Sun.COM
12567934SMark.Phalan@Sun.COM static kadm5_ret_t
kadm5_launch_task(krb5_context context,const char * task_path,char * const task_argv[],const char * data_buffer)12577934SMark.Phalan@Sun.COM kadm5_launch_task (krb5_context context,
12587934SMark.Phalan@Sun.COM const char *task_path, char * const task_argv[],
12597934SMark.Phalan@Sun.COM const char *data_buffer)
12607934SMark.Phalan@Sun.COM {
12617934SMark.Phalan@Sun.COM kadm5_ret_t ret = 0;
12627934SMark.Phalan@Sun.COM int data_pipe[2];
12637934SMark.Phalan@Sun.COM
12647934SMark.Phalan@Sun.COM if (data_buffer != NULL) {
12657934SMark.Phalan@Sun.COM ret = pipe (data_pipe);
12667934SMark.Phalan@Sun.COM if (ret) { ret = errno; }
12677934SMark.Phalan@Sun.COM }
12687934SMark.Phalan@Sun.COM
12697934SMark.Phalan@Sun.COM if (!ret) {
12707934SMark.Phalan@Sun.COM pid_t pid = fork ();
12717934SMark.Phalan@Sun.COM if (pid == -1) {
12727934SMark.Phalan@Sun.COM ret = errno;
12737934SMark.Phalan@Sun.COM } else if (pid == 0) {
12747934SMark.Phalan@Sun.COM /* The child: */
12757934SMark.Phalan@Sun.COM
12767934SMark.Phalan@Sun.COM if (data_buffer != NULL) {
12777934SMark.Phalan@Sun.COM if (dup2 (data_pipe[0], STDIN_FILENO) == -1) {
12787934SMark.Phalan@Sun.COM _exit (1);
12797934SMark.Phalan@Sun.COM }
12807934SMark.Phalan@Sun.COM } else {
12817934SMark.Phalan@Sun.COM close (data_pipe[0]);
12827934SMark.Phalan@Sun.COM }
12837934SMark.Phalan@Sun.COM
12847934SMark.Phalan@Sun.COM close (data_pipe[1]);
12857934SMark.Phalan@Sun.COM
12867934SMark.Phalan@Sun.COM execv (task_path, task_argv);
12877934SMark.Phalan@Sun.COM
12887934SMark.Phalan@Sun.COM _exit (1); /* Fail if execv fails */
12897934SMark.Phalan@Sun.COM } else {
12907934SMark.Phalan@Sun.COM /* The parent: */
12917934SMark.Phalan@Sun.COM int status;
12927934SMark.Phalan@Sun.COM
12937934SMark.Phalan@Sun.COM if (data_buffer != NULL) {
12947934SMark.Phalan@Sun.COM /* Write out the buffer to the child */
12957934SMark.Phalan@Sun.COM if (krb5_net_write (context, data_pipe[1],
12967934SMark.Phalan@Sun.COM data_buffer, strlen (data_buffer)) < 0) {
12977934SMark.Phalan@Sun.COM /* kill the child to make sure waitpid() won't hang later */
12987934SMark.Phalan@Sun.COM ret = errno;
12997934SMark.Phalan@Sun.COM kill (pid, SIGKILL);
13007934SMark.Phalan@Sun.COM }
13017934SMark.Phalan@Sun.COM }
13027934SMark.Phalan@Sun.COM
13037934SMark.Phalan@Sun.COM close (data_buffer[0]);
13047934SMark.Phalan@Sun.COM close (data_buffer[1]);
13057934SMark.Phalan@Sun.COM
13067934SMark.Phalan@Sun.COM waitpid (pid, &status, 0);
13077934SMark.Phalan@Sun.COM
13087934SMark.Phalan@Sun.COM if (!ret) {
13097934SMark.Phalan@Sun.COM if (WIFEXITED (status)) {
13107934SMark.Phalan@Sun.COM /* child read password and exited. Check the return value. */
13117934SMark.Phalan@Sun.COM if ((WEXITSTATUS (status) != 0) && (WEXITSTATUS (status) != 252)) {
13127934SMark.Phalan@Sun.COM ret = KRB5KDC_ERR_POLICY; /* password change rejected */
13137934SMark.Phalan@Sun.COM }
13147934SMark.Phalan@Sun.COM } else {
13157934SMark.Phalan@Sun.COM /* child read password but crashed or was killed */
13167934SMark.Phalan@Sun.COM ret = KRB5KRB_ERR_GENERIC; /* FIXME: better error */
13177934SMark.Phalan@Sun.COM }
13187934SMark.Phalan@Sun.COM }
13197934SMark.Phalan@Sun.COM }
13207934SMark.Phalan@Sun.COM }
13217934SMark.Phalan@Sun.COM
13227934SMark.Phalan@Sun.COM return ret;
13237934SMark.Phalan@Sun.COM }
13247934SMark.Phalan@Sun.COM
13257934SMark.Phalan@Sun.COM #endif
13267934SMark.Phalan@Sun.COM
13270Sstevel@tonic-gate kadm5_ret_t
kadm5_chpass_principal(void * server_handle,krb5_principal principal,char * password)13280Sstevel@tonic-gate kadm5_chpass_principal(void *server_handle,
13290Sstevel@tonic-gate krb5_principal principal, char *password)
13300Sstevel@tonic-gate {
13312881Smp153739 return
13322881Smp153739 kadm5_chpass_principal_3(server_handle, principal, FALSE,
13332881Smp153739 0, NULL, password);
13340Sstevel@tonic-gate }
13350Sstevel@tonic-gate
13360Sstevel@tonic-gate 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)13370Sstevel@tonic-gate kadm5_chpass_principal_3(void *server_handle,
13380Sstevel@tonic-gate krb5_principal principal, krb5_boolean keepold,
13390Sstevel@tonic-gate int n_ks_tuple, krb5_key_salt_tuple *ks_tuple,
13400Sstevel@tonic-gate char *password)
13410Sstevel@tonic-gate {
13420Sstevel@tonic-gate krb5_int32 now;
13430Sstevel@tonic-gate kadm5_policy_ent_rec pol;
13440Sstevel@tonic-gate osa_princ_ent_rec adb;
13450Sstevel@tonic-gate krb5_db_entry kdb, kdb_save;
13462881Smp153739 int ret, ret2, last_pwd, hist_added;
13470Sstevel@tonic-gate int have_pol = 0;
13480Sstevel@tonic-gate kadm5_server_handle_t handle = server_handle;
13490Sstevel@tonic-gate osa_pw_hist_ent hist;
13500Sstevel@tonic-gate
13510Sstevel@tonic-gate CHECK_HANDLE(server_handle);
13520Sstevel@tonic-gate
1353*12253SPeter.Shoults@Sun.COM /* Solaris Kerberos - kadm5_check_min_life checks for null principal. */
1354*12253SPeter.Shoults@Sun.COM ret = kadm5_check_min_life(server_handle,principal,NULL,0);
1355*12253SPeter.Shoults@Sun.COM if (ret)
1356*12253SPeter.Shoults@Sun.COM return (ret);
13574960Swillf krb5_clear_error_message(handle->context);
13584960Swillf
13590Sstevel@tonic-gate hist_added = 0;
13600Sstevel@tonic-gate memset(&hist, 0, sizeof(hist));
13610Sstevel@tonic-gate
13620Sstevel@tonic-gate if (principal == NULL || password == NULL)
13630Sstevel@tonic-gate return EINVAL;
13640Sstevel@tonic-gate if ((krb5_principal_compare(handle->context,
13650Sstevel@tonic-gate principal, hist_princ)) == TRUE)
13660Sstevel@tonic-gate return KADM5_PROTECT_PRINCIPAL;
13670Sstevel@tonic-gate
13680Sstevel@tonic-gate if ((ret = kdb_get_entry(handle, principal, &kdb, &adb)))
13690Sstevel@tonic-gate return(ret);
13700Sstevel@tonic-gate
13710Sstevel@tonic-gate /* we are going to need the current keys after the new keys are set */
13720Sstevel@tonic-gate if ((ret = kdb_get_entry(handle, principal, &kdb_save, NULL))) {
13730Sstevel@tonic-gate kdb_free_entry(handle, &kdb, &adb);
13740Sstevel@tonic-gate return(ret);
13750Sstevel@tonic-gate }
13760Sstevel@tonic-gate
13770Sstevel@tonic-gate if ((adb.aux_attributes & KADM5_POLICY)) {
13780Sstevel@tonic-gate if ((ret = kadm5_get_policy(handle->lhandle, adb.policy, &pol)))
13790Sstevel@tonic-gate goto done;
13800Sstevel@tonic-gate have_pol = 1;
13810Sstevel@tonic-gate }
13820Sstevel@tonic-gate
13830Sstevel@tonic-gate if ((ret = passwd_check(handle, password, adb.aux_attributes &
13840Sstevel@tonic-gate KADM5_POLICY, &pol, principal)))
13850Sstevel@tonic-gate goto done;
13860Sstevel@tonic-gate
13872881Smp153739 ret = krb5_dbe_cpw(handle->context, &handle->master_keyblock,
13882881Smp153739 n_ks_tuple?ks_tuple:handle->params.keysalts,
13892881Smp153739 n_ks_tuple?n_ks_tuple:handle->params.num_keysalts,
13902881Smp153739 password, 0 /* increment kvno */,
13912881Smp153739 keepold, &kdb);
13922881Smp153739 if (ret)
13930Sstevel@tonic-gate goto done;
13940Sstevel@tonic-gate
13950Sstevel@tonic-gate kdb.attributes &= ~KRB5_KDB_REQUIRES_PWCHANGE;
13960Sstevel@tonic-gate
13972881Smp153739 ret = krb5_timeofday(handle->context, &now);
13982881Smp153739 if (ret)
13990Sstevel@tonic-gate goto done;
14000Sstevel@tonic-gate
14010Sstevel@tonic-gate if ((adb.aux_attributes & KADM5_POLICY)) {
14020Sstevel@tonic-gate /* the policy was loaded before */
14030Sstevel@tonic-gate
14042881Smp153739 ret = krb5_dbe_lookup_last_pwd_change(handle->context,
14052881Smp153739 &kdb, &last_pwd);
14062881Smp153739 if (ret)
14072881Smp153739 goto done;
14080Sstevel@tonic-gate
14090Sstevel@tonic-gate #if 0
14100Sstevel@tonic-gate /*
14110Sstevel@tonic-gate * The spec says this check is overridden if the caller has
14120Sstevel@tonic-gate * modify privilege. The admin server therefore makes this
14130Sstevel@tonic-gate * check itself (in chpass_principal_wrapper, misc.c). A
14140Sstevel@tonic-gate * local caller implicitly has all authorization bits.
14150Sstevel@tonic-gate */
14160Sstevel@tonic-gate if ((now - last_pwd) < pol.pw_min_life &&
14170Sstevel@tonic-gate !(kdb.attributes & KRB5_KDB_REQUIRES_PWCHANGE)) {
14180Sstevel@tonic-gate ret = KADM5_PASS_TOOSOON;
14190Sstevel@tonic-gate goto done;
14200Sstevel@tonic-gate }
14210Sstevel@tonic-gate #endif
14220Sstevel@tonic-gate
14232881Smp153739 ret = create_history_entry(handle->context,
14242881Smp153739 &handle->master_keyblock, kdb_save.n_key_data,
14252881Smp153739 kdb_save.key_data, &hist);
14262881Smp153739 if (ret)
14272881Smp153739 goto done;
14280Sstevel@tonic-gate
14292881Smp153739 ret = check_pw_reuse(handle->context,
14302881Smp153739 &handle->master_keyblock,
14312881Smp153739 &hist_key,
14322881Smp153739 kdb.n_key_data, kdb.key_data,
14332881Smp153739 1, &hist);
14342881Smp153739 if (ret)
14352881Smp153739 goto done;
14360Sstevel@tonic-gate
14370Sstevel@tonic-gate if (pol.pw_history_num > 1) {
14380Sstevel@tonic-gate if (adb.admin_history_kvno != hist_kvno) {
14390Sstevel@tonic-gate ret = KADM5_BAD_HIST_KEY;
14400Sstevel@tonic-gate goto done;
14410Sstevel@tonic-gate }
14420Sstevel@tonic-gate
14432881Smp153739 ret = check_pw_reuse(handle->context,
14440Sstevel@tonic-gate &handle->master_keyblock,
14450Sstevel@tonic-gate &hist_key,
14462881Smp153739 kdb.n_key_data, kdb.key_data,
14472881Smp153739 adb.old_key_len, adb.old_keys);
14482881Smp153739 if (ret)
14490Sstevel@tonic-gate goto done;
14500Sstevel@tonic-gate
14512881Smp153739 ret = add_to_history(handle->context, &adb, &pol, &hist);
14522881Smp153739 if (ret)
14532881Smp153739 goto done;
14540Sstevel@tonic-gate hist_added = 1;
14550Sstevel@tonic-gate }
14560Sstevel@tonic-gate
14570Sstevel@tonic-gate if (pol.pw_max_life)
14580Sstevel@tonic-gate kdb.pw_expiration = now + pol.pw_max_life;
14590Sstevel@tonic-gate else
14600Sstevel@tonic-gate kdb.pw_expiration = 0;
14610Sstevel@tonic-gate } else {
14620Sstevel@tonic-gate kdb.pw_expiration = 0;
14630Sstevel@tonic-gate }
14640Sstevel@tonic-gate
14657934SMark.Phalan@Sun.COM #ifdef USE_PASSWORD_SERVER
14667934SMark.Phalan@Sun.COM if (kadm5_use_password_server () &&
14677934SMark.Phalan@Sun.COM (krb5_princ_size (handle->context, principal) == 1)) {
14687934SMark.Phalan@Sun.COM krb5_data *princ = krb5_princ_component (handle->context, principal, 0);
14697934SMark.Phalan@Sun.COM const char *path = "/usr/sbin/mkpassdb";
14707934SMark.Phalan@Sun.COM char *argv[] = { "mkpassdb", "-setpassword", NULL, NULL };
14717934SMark.Phalan@Sun.COM char *pstring = NULL;
14727934SMark.Phalan@Sun.COM char pwbuf[256];
14737934SMark.Phalan@Sun.COM int pwlen = strlen (password);
14747934SMark.Phalan@Sun.COM
14757934SMark.Phalan@Sun.COM if (pwlen > 254) pwlen = 254;
14767934SMark.Phalan@Sun.COM strncpy (pwbuf, password, pwlen);
14777934SMark.Phalan@Sun.COM pwbuf[pwlen] = '\n';
14787934SMark.Phalan@Sun.COM pwbuf[pwlen + 1] = '\0';
14797934SMark.Phalan@Sun.COM
14807934SMark.Phalan@Sun.COM if (!ret) {
14817934SMark.Phalan@Sun.COM pstring = malloc ((princ->length + 1) * sizeof (char));
14827934SMark.Phalan@Sun.COM if (pstring == NULL) { ret = errno; }
14837934SMark.Phalan@Sun.COM }
14847934SMark.Phalan@Sun.COM
14857934SMark.Phalan@Sun.COM if (!ret) {
14867934SMark.Phalan@Sun.COM memcpy (pstring, princ->data, princ->length);
14877934SMark.Phalan@Sun.COM pstring [princ->length] = '\0';
14887934SMark.Phalan@Sun.COM argv[2] = pstring;
14897934SMark.Phalan@Sun.COM
14907934SMark.Phalan@Sun.COM ret = kadm5_launch_task (handle->context, path, argv, pwbuf);
14917934SMark.Phalan@Sun.COM }
14927934SMark.Phalan@Sun.COM
14937934SMark.Phalan@Sun.COM if (pstring != NULL)
14947934SMark.Phalan@Sun.COM free (pstring);
14957934SMark.Phalan@Sun.COM
14967934SMark.Phalan@Sun.COM if (ret)
14977934SMark.Phalan@Sun.COM goto done;
14987934SMark.Phalan@Sun.COM }
14997934SMark.Phalan@Sun.COM #endif
15007934SMark.Phalan@Sun.COM
15012881Smp153739 ret = krb5_dbe_update_last_pwd_change(handle->context, &kdb, now);
15022881Smp153739 if (ret)
15030Sstevel@tonic-gate goto done;
15040Sstevel@tonic-gate
15054960Swillf /* key data and attributes changed, let the database provider know */
15065916Swillf /* Solaris Kerberos: adding support for key history in LDAP KDB */
15075916Swillf if (hist_added == 1)
15085916Swillf kdb.mask = KADM5_KEY_DATA | KADM5_ATTRIBUTES | KADM5_KEY_HIST
15095916Swillf /* | KADM5_CPW_FUNCTION */;
15105916Swillf else
15115916Swillf kdb.mask = KADM5_KEY_DATA | KADM5_ATTRIBUTES /* | KADM5_CPW_FUNCTION */;
15124960Swillf
15130Sstevel@tonic-gate if ((ret = kdb_put_entry(handle, &kdb, &adb)))
15140Sstevel@tonic-gate goto done;
15150Sstevel@tonic-gate
15160Sstevel@tonic-gate ret = KADM5_OK;
15170Sstevel@tonic-gate done:
15180Sstevel@tonic-gate if (!hist_added && hist.key_data)
15190Sstevel@tonic-gate free_history_entry(handle->context, &hist);
15200Sstevel@tonic-gate kdb_free_entry(handle, &kdb, &adb);
15210Sstevel@tonic-gate kdb_free_entry(handle, &kdb_save, NULL);
15224960Swillf krb5_db_free_principal(handle->context, &kdb, 1);
15230Sstevel@tonic-gate
15240Sstevel@tonic-gate if (have_pol && (ret2 = kadm5_free_policy_ent(handle->lhandle, &pol))
15250Sstevel@tonic-gate && !ret)
15260Sstevel@tonic-gate ret = ret2;
15270Sstevel@tonic-gate
15280Sstevel@tonic-gate return ret;
15290Sstevel@tonic-gate }
15300Sstevel@tonic-gate
15310Sstevel@tonic-gate kadm5_ret_t
kadm5_randkey_principal(void * server_handle,krb5_principal principal,krb5_keyblock ** keyblocks,int * n_keys)15320Sstevel@tonic-gate kadm5_randkey_principal(void *server_handle,
15330Sstevel@tonic-gate krb5_principal principal,
15340Sstevel@tonic-gate krb5_keyblock **keyblocks,
15350Sstevel@tonic-gate int *n_keys)
15360Sstevel@tonic-gate {
15377934SMark.Phalan@Sun.COM /* Solaris Kerberos: */
15380Sstevel@tonic-gate krb5_key_salt_tuple keysalts[2];
15390Sstevel@tonic-gate
15400Sstevel@tonic-gate /*
15410Sstevel@tonic-gate * Anyone calling this routine is forced to use only DES
15420Sstevel@tonic-gate * enctypes to be compatible with earlier releases that
15430Sstevel@tonic-gate * did not support stronger crypto.
15440Sstevel@tonic-gate *
15450Sstevel@tonic-gate * S10 (and later) kadmin clients will not use this API,
15460Sstevel@tonic-gate * so we can assume the request is from an older version.
15470Sstevel@tonic-gate */
15480Sstevel@tonic-gate keysalts[0].ks_enctype = ENCTYPE_DES_CBC_MD5;
15490Sstevel@tonic-gate keysalts[0].ks_salttype = KRB5_KDB_SALTTYPE_NORMAL;
15500Sstevel@tonic-gate keysalts[1].ks_enctype = ENCTYPE_DES_CBC_CRC;
15510Sstevel@tonic-gate keysalts[1].ks_salttype = KRB5_KDB_SALTTYPE_NORMAL;
15520Sstevel@tonic-gate
15530Sstevel@tonic-gate return (kadm5_randkey_principal_3(server_handle, principal,
15540Sstevel@tonic-gate FALSE, 2, keysalts, keyblocks, n_keys));
15550Sstevel@tonic-gate }
15560Sstevel@tonic-gate 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)15570Sstevel@tonic-gate kadm5_randkey_principal_3(void *server_handle,
15580Sstevel@tonic-gate krb5_principal principal,
15590Sstevel@tonic-gate krb5_boolean keepold,
15600Sstevel@tonic-gate int n_ks_tuple, krb5_key_salt_tuple *ks_tuple,
15610Sstevel@tonic-gate krb5_keyblock **keyblocks,
15620Sstevel@tonic-gate int *n_keys)
15630Sstevel@tonic-gate {
15640Sstevel@tonic-gate krb5_db_entry kdb;
15650Sstevel@tonic-gate osa_princ_ent_rec adb;
15660Sstevel@tonic-gate krb5_int32 now;
15670Sstevel@tonic-gate kadm5_policy_ent_rec pol;
15680Sstevel@tonic-gate krb5_key_data *key_data;
15690Sstevel@tonic-gate int ret, last_pwd, have_pol = 0;
15700Sstevel@tonic-gate kadm5_server_handle_t handle = server_handle;
15710Sstevel@tonic-gate
15720Sstevel@tonic-gate if (keyblocks)
15730Sstevel@tonic-gate *keyblocks = NULL;
15740Sstevel@tonic-gate
15750Sstevel@tonic-gate CHECK_HANDLE(server_handle);
15760Sstevel@tonic-gate
15774960Swillf krb5_clear_error_message(handle->context);
15784960Swillf
15790Sstevel@tonic-gate if (principal == NULL)
15800Sstevel@tonic-gate return EINVAL;
15810Sstevel@tonic-gate if (hist_princ && /* this will be NULL when initializing the databse */
15820Sstevel@tonic-gate ((krb5_principal_compare(handle->context,
15830Sstevel@tonic-gate principal, hist_princ)) == TRUE))
15840Sstevel@tonic-gate return KADM5_PROTECT_PRINCIPAL;
15850Sstevel@tonic-gate
15860Sstevel@tonic-gate if ((ret = kdb_get_entry(handle, principal, &kdb, &adb)))
15870Sstevel@tonic-gate return(ret);
15880Sstevel@tonic-gate
15892881Smp153739 ret = krb5_dbe_crk(handle->context, &handle->master_keyblock,
15902881Smp153739 n_ks_tuple?ks_tuple:handle->params.keysalts,
15912881Smp153739 n_ks_tuple?n_ks_tuple:handle->params.num_keysalts,
15922881Smp153739 keepold,
15932881Smp153739 &kdb);
15942881Smp153739 if (ret)
15952881Smp153739 goto done;
15960Sstevel@tonic-gate
15970Sstevel@tonic-gate kdb.attributes &= ~KRB5_KDB_REQUIRES_PWCHANGE;
15980Sstevel@tonic-gate
15992881Smp153739 ret = krb5_timeofday(handle->context, &now);
16002881Smp153739 if (ret)
16010Sstevel@tonic-gate goto done;
16020Sstevel@tonic-gate
16030Sstevel@tonic-gate if ((adb.aux_attributes & KADM5_POLICY)) {
16040Sstevel@tonic-gate if ((ret = kadm5_get_policy(handle->lhandle, adb.policy,
16050Sstevel@tonic-gate &pol)) != KADM5_OK)
16060Sstevel@tonic-gate goto done;
16070Sstevel@tonic-gate have_pol = 1;
16080Sstevel@tonic-gate
16092881Smp153739 ret = krb5_dbe_lookup_last_pwd_change(handle->context,
16102881Smp153739 &kdb, &last_pwd);
16112881Smp153739 if (ret)
16120Sstevel@tonic-gate goto done;
16130Sstevel@tonic-gate
16140Sstevel@tonic-gate #if 0
16150Sstevel@tonic-gate /*
16160Sstevel@tonic-gate * The spec says this check is overridden if the caller has
16170Sstevel@tonic-gate * modify privilege. The admin server therefore makes this
16180Sstevel@tonic-gate * check itself (in chpass_principal_wrapper, misc.c). A
16190Sstevel@tonic-gate * local caller implicitly has all authorization bits.
16200Sstevel@tonic-gate */
16210Sstevel@tonic-gate if((now - last_pwd) < pol.pw_min_life &&
16220Sstevel@tonic-gate !(kdb.attributes & KRB5_KDB_REQUIRES_PWCHANGE)) {
16230Sstevel@tonic-gate ret = KADM5_PASS_TOOSOON;
16240Sstevel@tonic-gate goto done;
16250Sstevel@tonic-gate }
16260Sstevel@tonic-gate #endif
16270Sstevel@tonic-gate
16280Sstevel@tonic-gate if(pol.pw_history_num > 1) {
16290Sstevel@tonic-gate if(adb.admin_history_kvno != hist_kvno) {
16300Sstevel@tonic-gate ret = KADM5_BAD_HIST_KEY;
16310Sstevel@tonic-gate goto done;
16320Sstevel@tonic-gate }
16330Sstevel@tonic-gate
16342881Smp153739 ret = check_pw_reuse(handle->context,
16352881Smp153739 &handle->master_keyblock,
16362881Smp153739 &hist_key,
16372881Smp153739 kdb.n_key_data, kdb.key_data,
16382881Smp153739 adb.old_key_len, adb.old_keys);
16392881Smp153739 if (ret)
16400Sstevel@tonic-gate goto done;
16410Sstevel@tonic-gate }
16420Sstevel@tonic-gate if (pol.pw_max_life)
16430Sstevel@tonic-gate kdb.pw_expiration = now + pol.pw_max_life;
16440Sstevel@tonic-gate else
16450Sstevel@tonic-gate kdb.pw_expiration = 0;
16460Sstevel@tonic-gate } else {
16470Sstevel@tonic-gate kdb.pw_expiration = 0;
16480Sstevel@tonic-gate }
16490Sstevel@tonic-gate
16502881Smp153739 ret = krb5_dbe_update_last_pwd_change(handle->context, &kdb, now);
16512881Smp153739 if (ret)
16520Sstevel@tonic-gate goto done;
16530Sstevel@tonic-gate
16540Sstevel@tonic-gate if (keyblocks) {
16550Sstevel@tonic-gate if (handle->api_version == KADM5_API_VERSION_1) {
16560Sstevel@tonic-gate /* Version 1 clients will expect to see a DES_CRC enctype. */
16572881Smp153739 ret = krb5_dbe_find_enctype(handle->context, &kdb,
16582881Smp153739 ENCTYPE_DES_CBC_CRC,
16592881Smp153739 -1, -1, &key_data);
16602881Smp153739 if (ret)
16612881Smp153739 goto done;
16622881Smp153739
16632881Smp153739 ret = decrypt_key_data(handle->context,
16640Sstevel@tonic-gate &handle->master_keyblock, 1, key_data,
16652881Smp153739 keyblocks, NULL);
16662881Smp153739 if (ret)
16672881Smp153739 goto done;
16680Sstevel@tonic-gate } else {
16692881Smp153739 ret = decrypt_key_data(handle->context,
16702881Smp153739 &handle->master_keyblock,
16712881Smp153739 kdb.n_key_data, kdb.key_data,
16722881Smp153739 keyblocks, n_keys);
16732881Smp153739 if (ret)
16742881Smp153739 goto done;
16750Sstevel@tonic-gate }
16760Sstevel@tonic-gate }
16770Sstevel@tonic-gate
16784960Swillf /* key data changed, let the database provider know */
16794960Swillf kdb.mask = KADM5_KEY_DATA /* | KADM5_RANDKEY_USED */;
16804960Swillf
16810Sstevel@tonic-gate if ((ret = kdb_put_entry(handle, &kdb, &adb)))
16820Sstevel@tonic-gate goto done;
16830Sstevel@tonic-gate
16840Sstevel@tonic-gate ret = KADM5_OK;
16850Sstevel@tonic-gate done:
16860Sstevel@tonic-gate kdb_free_entry(handle, &kdb, &adb);
16870Sstevel@tonic-gate if (have_pol)
16880Sstevel@tonic-gate kadm5_free_policy_ent(handle->lhandle, &pol);
16890Sstevel@tonic-gate
16900Sstevel@tonic-gate return ret;
16910Sstevel@tonic-gate }
16920Sstevel@tonic-gate
16937934SMark.Phalan@Sun.COM #if 0 /* Solaris Kerberos */
16947934SMark.Phalan@Sun.COM /*
16957934SMark.Phalan@Sun.COM * kadm5_setv4key_principal:
16967934SMark.Phalan@Sun.COM *
16977934SMark.Phalan@Sun.COM * Set only ONE key of the principal, removing all others. This key
16987934SMark.Phalan@Sun.COM * must have the DES_CBC_CRC enctype and is entered as having the
16997934SMark.Phalan@Sun.COM * krb4 salttype. This is to enable things like kadmind4 to work.
17007934SMark.Phalan@Sun.COM */
17017934SMark.Phalan@Sun.COM kadm5_ret_t
17027934SMark.Phalan@Sun.COM kadm5_setv4key_principal(void *server_handle,
17037934SMark.Phalan@Sun.COM krb5_principal principal,
17047934SMark.Phalan@Sun.COM krb5_keyblock *keyblock)
17057934SMark.Phalan@Sun.COM {
17067934SMark.Phalan@Sun.COM krb5_db_entry kdb;
17077934SMark.Phalan@Sun.COM osa_princ_ent_rec adb;
17087934SMark.Phalan@Sun.COM krb5_int32 now;
17097934SMark.Phalan@Sun.COM kadm5_policy_ent_rec pol;
17107934SMark.Phalan@Sun.COM krb5_keysalt keysalt;
17117934SMark.Phalan@Sun.COM int i, k, kvno, ret, have_pol = 0;
17127934SMark.Phalan@Sun.COM #if 0
17137934SMark.Phalan@Sun.COM int last_pwd;
17147934SMark.Phalan@Sun.COM #endif
17157934SMark.Phalan@Sun.COM kadm5_server_handle_t handle = server_handle;
17167934SMark.Phalan@Sun.COM krb5_key_data tmp_key_data;
17177934SMark.Phalan@Sun.COM
17187934SMark.Phalan@Sun.COM memset( &tmp_key_data, 0, sizeof(tmp_key_data));
17197934SMark.Phalan@Sun.COM
17207934SMark.Phalan@Sun.COM CHECK_HANDLE(server_handle);
17217934SMark.Phalan@Sun.COM
17227934SMark.Phalan@Sun.COM krb5_clear_error_message(handle->context);
17237934SMark.Phalan@Sun.COM
17247934SMark.Phalan@Sun.COM if (principal == NULL || keyblock == NULL)
17257934SMark.Phalan@Sun.COM return EINVAL;
17267934SMark.Phalan@Sun.COM if (hist_princ && /* this will be NULL when initializing the databse */
17277934SMark.Phalan@Sun.COM ((krb5_principal_compare(handle->context,
17287934SMark.Phalan@Sun.COM principal, hist_princ)) == TRUE))
17297934SMark.Phalan@Sun.COM return KADM5_PROTECT_PRINCIPAL;
17307934SMark.Phalan@Sun.COM
17317934SMark.Phalan@Sun.COM if (keyblock->enctype != ENCTYPE_DES_CBC_CRC)
17327934SMark.Phalan@Sun.COM return KADM5_SETV4KEY_INVAL_ENCTYPE;
17337934SMark.Phalan@Sun.COM
17347934SMark.Phalan@Sun.COM if ((ret = kdb_get_entry(handle, principal, &kdb, &adb)))
17357934SMark.Phalan@Sun.COM return(ret);
17367934SMark.Phalan@Sun.COM
17377934SMark.Phalan@Sun.COM for (kvno = 0, i=0; i<kdb.n_key_data; i++)
17387934SMark.Phalan@Sun.COM if (kdb.key_data[i].key_data_kvno > kvno)
17397934SMark.Phalan@Sun.COM kvno = kdb.key_data[i].key_data_kvno;
17407934SMark.Phalan@Sun.COM
17417934SMark.Phalan@Sun.COM if (kdb.key_data != NULL)
17427934SMark.Phalan@Sun.COM cleanup_key_data(handle->context, kdb.n_key_data, kdb.key_data);
17437934SMark.Phalan@Sun.COM
17447934SMark.Phalan@Sun.COM kdb.key_data = (krb5_key_data*)krb5_db_alloc(handle->context, NULL, sizeof(krb5_key_data));
17457934SMark.Phalan@Sun.COM if (kdb.key_data == NULL)
17467934SMark.Phalan@Sun.COM return ENOMEM;
17477934SMark.Phalan@Sun.COM memset(kdb.key_data, 0, sizeof(krb5_key_data));
17487934SMark.Phalan@Sun.COM kdb.n_key_data = 1;
17497934SMark.Phalan@Sun.COM keysalt.type = KRB5_KDB_SALTTYPE_V4;
17507934SMark.Phalan@Sun.COM /* XXX data.magic? */
17517934SMark.Phalan@Sun.COM keysalt.data.length = 0;
17527934SMark.Phalan@Sun.COM keysalt.data.data = NULL;
17537934SMark.Phalan@Sun.COM
17547934SMark.Phalan@Sun.COM /* use tmp_key_data as temporary location and reallocate later */
17557934SMark.Phalan@Sun.COM ret = krb5_dbekd_encrypt_key_data(handle->context, &master_keyblock,
17567934SMark.Phalan@Sun.COM keyblock, &keysalt, kvno + 1,
17577934SMark.Phalan@Sun.COM &tmp_key_data);
17587934SMark.Phalan@Sun.COM if (ret) {
17597934SMark.Phalan@Sun.COM goto done;
17607934SMark.Phalan@Sun.COM }
17617934SMark.Phalan@Sun.COM
17627934SMark.Phalan@Sun.COM for (k = 0; k < tmp_key_data.key_data_ver; k++) {
17637934SMark.Phalan@Sun.COM kdb.key_data->key_data_type[k] = tmp_key_data.key_data_type[k];
17647934SMark.Phalan@Sun.COM kdb.key_data->key_data_length[k] = tmp_key_data.key_data_length[k];
17657934SMark.Phalan@Sun.COM if (tmp_key_data.key_data_contents[k]) {
17667934SMark.Phalan@Sun.COM kdb.key_data->key_data_contents[k] = krb5_db_alloc(handle->context, NULL, tmp_key_data.key_data_length[k]);
17677934SMark.Phalan@Sun.COM if (kdb.key_data->key_data_contents[k] == NULL) {
17687934SMark.Phalan@Sun.COM cleanup_key_data(handle->context, kdb.n_key_data, kdb.key_data);
17697934SMark.Phalan@Sun.COM kdb.key_data = NULL;
17707934SMark.Phalan@Sun.COM kdb.n_key_data = 0;
17717934SMark.Phalan@Sun.COM ret = ENOMEM;
17727934SMark.Phalan@Sun.COM goto done;
17737934SMark.Phalan@Sun.COM }
17747934SMark.Phalan@Sun.COM memcpy (kdb.key_data->key_data_contents[k], tmp_key_data.key_data_contents[k], tmp_key_data.key_data_length[k]);
17757934SMark.Phalan@Sun.COM
17767934SMark.Phalan@Sun.COM memset (tmp_key_data.key_data_contents[k], 0, tmp_key_data.key_data_length[k]);
17777934SMark.Phalan@Sun.COM free (tmp_key_data.key_data_contents[k]);
17787934SMark.Phalan@Sun.COM tmp_key_data.key_data_contents[k] = NULL;
17797934SMark.Phalan@Sun.COM }
17807934SMark.Phalan@Sun.COM }
17817934SMark.Phalan@Sun.COM
17827934SMark.Phalan@Sun.COM
17837934SMark.Phalan@Sun.COM
17847934SMark.Phalan@Sun.COM kdb.attributes &= ~KRB5_KDB_REQUIRES_PWCHANGE;
17857934SMark.Phalan@Sun.COM
17867934SMark.Phalan@Sun.COM ret = krb5_timeofday(handle->context, &now);
17877934SMark.Phalan@Sun.COM if (ret)
17887934SMark.Phalan@Sun.COM goto done;
17897934SMark.Phalan@Sun.COM
17907934SMark.Phalan@Sun.COM if ((adb.aux_attributes & KADM5_POLICY)) {
17917934SMark.Phalan@Sun.COM if ((ret = kadm5_get_policy(handle->lhandle, adb.policy,
17927934SMark.Phalan@Sun.COM &pol)) != KADM5_OK)
17937934SMark.Phalan@Sun.COM goto done;
17947934SMark.Phalan@Sun.COM have_pol = 1;
17957934SMark.Phalan@Sun.COM
17967934SMark.Phalan@Sun.COM #if 0
17977934SMark.Phalan@Sun.COM /*
17987934SMark.Phalan@Sun.COM * The spec says this check is overridden if the caller has
17997934SMark.Phalan@Sun.COM * modify privilege. The admin server therefore makes this
18007934SMark.Phalan@Sun.COM * check itself (in chpass_principal_wrapper, misc.c). A
18017934SMark.Phalan@Sun.COM * local caller implicitly has all authorization bits.
18027934SMark.Phalan@Sun.COM */
18037934SMark.Phalan@Sun.COM if (ret = krb5_dbe_lookup_last_pwd_change(handle->context,
18047934SMark.Phalan@Sun.COM &kdb, &last_pwd))
18057934SMark.Phalan@Sun.COM goto done;
18067934SMark.Phalan@Sun.COM if((now - last_pwd) < pol.pw_min_life &&
18077934SMark.Phalan@Sun.COM !(kdb.attributes & KRB5_KDB_REQUIRES_PWCHANGE)) {
18087934SMark.Phalan@Sun.COM ret = KADM5_PASS_TOOSOON;
18097934SMark.Phalan@Sun.COM goto done;
18107934SMark.Phalan@Sun.COM }
18117934SMark.Phalan@Sun.COM #endif
18127934SMark.Phalan@Sun.COM #if 0
18137934SMark.Phalan@Sun.COM /*
18147934SMark.Phalan@Sun.COM * Should we be checking/updating pw history here?
18157934SMark.Phalan@Sun.COM */
18167934SMark.Phalan@Sun.COM if(pol.pw_history_num > 1) {
18177934SMark.Phalan@Sun.COM if(adb.admin_history_kvno != hist_kvno) {
18187934SMark.Phalan@Sun.COM ret = KADM5_BAD_HIST_KEY;
18197934SMark.Phalan@Sun.COM goto done;
18207934SMark.Phalan@Sun.COM }
18217934SMark.Phalan@Sun.COM
18227934SMark.Phalan@Sun.COM if (ret = check_pw_reuse(handle->context,
18237934SMark.Phalan@Sun.COM &hist_key,
18247934SMark.Phalan@Sun.COM kdb.n_key_data, kdb.key_data,
18257934SMark.Phalan@Sun.COM adb.old_key_len, adb.old_keys))
18267934SMark.Phalan@Sun.COM goto done;
18277934SMark.Phalan@Sun.COM }
18287934SMark.Phalan@Sun.COM #endif
18297934SMark.Phalan@Sun.COM
18307934SMark.Phalan@Sun.COM if (pol.pw_max_life)
18317934SMark.Phalan@Sun.COM kdb.pw_expiration = now + pol.pw_max_life;
18327934SMark.Phalan@Sun.COM else
18337934SMark.Phalan@Sun.COM kdb.pw_expiration = 0;
18347934SMark.Phalan@Sun.COM } else {
18357934SMark.Phalan@Sun.COM kdb.pw_expiration = 0;
18367934SMark.Phalan@Sun.COM }
18377934SMark.Phalan@Sun.COM
18387934SMark.Phalan@Sun.COM ret = krb5_dbe_update_last_pwd_change(handle->context, &kdb, now);
18397934SMark.Phalan@Sun.COM if (ret)
18407934SMark.Phalan@Sun.COM goto done;
18417934SMark.Phalan@Sun.COM
18427934SMark.Phalan@Sun.COM if ((ret = kdb_put_entry(handle, &kdb, &adb)))
18437934SMark.Phalan@Sun.COM goto done;
18447934SMark.Phalan@Sun.COM
18457934SMark.Phalan@Sun.COM ret = KADM5_OK;
18467934SMark.Phalan@Sun.COM done:
18477934SMark.Phalan@Sun.COM for (i = 0; i < tmp_key_data.key_data_ver; i++) {
18487934SMark.Phalan@Sun.COM if (tmp_key_data.key_data_contents[i]) {
18497934SMark.Phalan@Sun.COM memset (tmp_key_data.key_data_contents[i], 0, tmp_key_data.key_data_length[i]);
18507934SMark.Phalan@Sun.COM free (tmp_key_data.key_data_contents[i]);
18517934SMark.Phalan@Sun.COM }
18527934SMark.Phalan@Sun.COM }
18537934SMark.Phalan@Sun.COM
18547934SMark.Phalan@Sun.COM kdb_free_entry(handle, &kdb, &adb);
18557934SMark.Phalan@Sun.COM if (have_pol)
18567934SMark.Phalan@Sun.COM kadm5_free_policy_ent(handle->lhandle, &pol);
18577934SMark.Phalan@Sun.COM
18587934SMark.Phalan@Sun.COM return ret;
18597934SMark.Phalan@Sun.COM }
18607934SMark.Phalan@Sun.COM #endif
18617934SMark.Phalan@Sun.COM
18620Sstevel@tonic-gate kadm5_ret_t
kadm5_setkey_principal(void * server_handle,krb5_principal principal,krb5_keyblock * keyblocks,int n_keys)18630Sstevel@tonic-gate kadm5_setkey_principal(void *server_handle,
18640Sstevel@tonic-gate krb5_principal principal,
18650Sstevel@tonic-gate krb5_keyblock *keyblocks,
18660Sstevel@tonic-gate int n_keys)
18670Sstevel@tonic-gate {
18682881Smp153739 return
18692881Smp153739 kadm5_setkey_principal_3(server_handle, principal,
18702881Smp153739 FALSE, 0, NULL,
18712881Smp153739 keyblocks, n_keys);
18720Sstevel@tonic-gate }
18730Sstevel@tonic-gate
18740Sstevel@tonic-gate 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)18750Sstevel@tonic-gate kadm5_setkey_principal_3(void *server_handle,
18760Sstevel@tonic-gate krb5_principal principal,
18770Sstevel@tonic-gate krb5_boolean keepold,
18780Sstevel@tonic-gate int n_ks_tuple, krb5_key_salt_tuple *ks_tuple,
18790Sstevel@tonic-gate krb5_keyblock *keyblocks,
18800Sstevel@tonic-gate int n_keys)
18810Sstevel@tonic-gate {
18820Sstevel@tonic-gate krb5_db_entry kdb;
18830Sstevel@tonic-gate osa_princ_ent_rec adb;
18840Sstevel@tonic-gate krb5_int32 now;
18850Sstevel@tonic-gate kadm5_policy_ent_rec pol;
18860Sstevel@tonic-gate krb5_key_data *old_key_data;
18870Sstevel@tonic-gate int n_old_keys;
18884960Swillf int i, j, k, kvno, ret, have_pol = 0;
18894960Swillf #if 0
18904960Swillf int last_pwd;
18914960Swillf #endif
18920Sstevel@tonic-gate kadm5_server_handle_t handle = server_handle;
18930Sstevel@tonic-gate krb5_boolean similar;
18940Sstevel@tonic-gate krb5_keysalt keysalt;
18954960Swillf krb5_key_data tmp_key_data;
18964960Swillf krb5_key_data *tptr;
18970Sstevel@tonic-gate
18980Sstevel@tonic-gate CHECK_HANDLE(server_handle);
18990Sstevel@tonic-gate
19004960Swillf krb5_clear_error_message(handle->context);
19014960Swillf
19020Sstevel@tonic-gate if (principal == NULL || keyblocks == NULL)
19030Sstevel@tonic-gate return EINVAL;
19040Sstevel@tonic-gate if (hist_princ && /* this will be NULL when initializing the databse */
19050Sstevel@tonic-gate ((krb5_principal_compare(handle->context,
19060Sstevel@tonic-gate principal, hist_princ)) == TRUE))
19070Sstevel@tonic-gate return KADM5_PROTECT_PRINCIPAL;
19080Sstevel@tonic-gate
19090Sstevel@tonic-gate for (i = 0; i < n_keys; i++) {
19100Sstevel@tonic-gate for (j = i+1; j < n_keys; j++) {
19112881Smp153739 if ((ret = krb5_c_enctype_compare(handle->context,
19122881Smp153739 keyblocks[i].enctype,
19132881Smp153739 keyblocks[j].enctype,
19142881Smp153739 &similar)))
19150Sstevel@tonic-gate return(ret);
19162881Smp153739 if (similar) {
19170Sstevel@tonic-gate if (n_ks_tuple) {
19180Sstevel@tonic-gate if (ks_tuple[i].ks_salttype == ks_tuple[j].ks_salttype)
19190Sstevel@tonic-gate return KADM5_SETKEY_DUP_ENCTYPES;
19200Sstevel@tonic-gate } else
19210Sstevel@tonic-gate return KADM5_SETKEY_DUP_ENCTYPES;
19222881Smp153739 }
19230Sstevel@tonic-gate }
19240Sstevel@tonic-gate }
19250Sstevel@tonic-gate
19262881Smp153739 if (n_ks_tuple && n_ks_tuple != n_keys)
19270Sstevel@tonic-gate return KADM5_SETKEY3_ETYPE_MISMATCH;
19280Sstevel@tonic-gate
19290Sstevel@tonic-gate if ((ret = kdb_get_entry(handle, principal, &kdb, &adb)))
19300Sstevel@tonic-gate return(ret);
19310Sstevel@tonic-gate
19320Sstevel@tonic-gate for (kvno = 0, i=0; i<kdb.n_key_data; i++)
19330Sstevel@tonic-gate if (kdb.key_data[i].key_data_kvno > kvno)
19340Sstevel@tonic-gate kvno = kdb.key_data[i].key_data_kvno;
19350Sstevel@tonic-gate
19360Sstevel@tonic-gate if (keepold) {
19370Sstevel@tonic-gate old_key_data = kdb.key_data;
19380Sstevel@tonic-gate n_old_keys = kdb.n_key_data;
19390Sstevel@tonic-gate } else {
19400Sstevel@tonic-gate if (kdb.key_data != NULL)
19410Sstevel@tonic-gate cleanup_key_data(handle->context, kdb.n_key_data, kdb.key_data);
19420Sstevel@tonic-gate n_old_keys = 0;
19430Sstevel@tonic-gate old_key_data = NULL;
19440Sstevel@tonic-gate }
19450Sstevel@tonic-gate
19464960Swillf kdb.key_data = (krb5_key_data*)krb5_db_alloc(handle->context, NULL, (n_keys+n_old_keys)
19474960Swillf *sizeof(krb5_key_data));
19484960Swillf if (kdb.key_data == NULL) {
19494960Swillf ret = ENOMEM;
19504960Swillf goto done;
19514960Swillf }
19524960Swillf
19530Sstevel@tonic-gate memset(kdb.key_data, 0, (n_keys+n_old_keys)*sizeof(krb5_key_data));
19540Sstevel@tonic-gate kdb.n_key_data = 0;
19550Sstevel@tonic-gate
19560Sstevel@tonic-gate for (i = 0; i < n_keys; i++) {
19570Sstevel@tonic-gate if (n_ks_tuple) {
19580Sstevel@tonic-gate keysalt.type = ks_tuple[i].ks_salttype;
19590Sstevel@tonic-gate keysalt.data.length = 0;
19600Sstevel@tonic-gate keysalt.data.data = NULL;
19610Sstevel@tonic-gate if (ks_tuple[i].ks_enctype != keyblocks[i].enctype) {
19624960Swillf ret = KADM5_SETKEY3_ETYPE_MISMATCH;
19634960Swillf goto done;
19640Sstevel@tonic-gate }
19650Sstevel@tonic-gate }
19664960Swillf memset (&tmp_key_data, 0, sizeof(tmp_key_data));
19674960Swillf
19680Sstevel@tonic-gate ret = krb5_dbekd_encrypt_key_data(handle->context,
19690Sstevel@tonic-gate &handle->master_keyblock,
19700Sstevel@tonic-gate &keyblocks[i],
19710Sstevel@tonic-gate n_ks_tuple ? &keysalt : NULL,
19720Sstevel@tonic-gate kvno + 1,
19734960Swillf &tmp_key_data);
19740Sstevel@tonic-gate if (ret) {
19754960Swillf goto done;
19764960Swillf }
19774960Swillf tptr = &kdb.key_data[i];
19784960Swillf for (k = 0; k < tmp_key_data.key_data_ver; k++) {
19794960Swillf tptr->key_data_type[k] = tmp_key_data.key_data_type[k];
19804960Swillf tptr->key_data_length[k] = tmp_key_data.key_data_length[k];
19814960Swillf if (tmp_key_data.key_data_contents[k]) {
19824960Swillf tptr->key_data_contents[k] = krb5_db_alloc(handle->context, NULL, tmp_key_data.key_data_length[k]);
19834960Swillf if (tptr->key_data_contents[k] == NULL) {
19844960Swillf int i1;
19854960Swillf for (i1 = k; i1 < tmp_key_data.key_data_ver; i1++) {
19864960Swillf if (tmp_key_data.key_data_contents[i1]) {
19874960Swillf memset (tmp_key_data.key_data_contents[i1], 0, tmp_key_data.key_data_length[i1]);
19884960Swillf free (tmp_key_data.key_data_contents[i1]);
19894960Swillf }
19904960Swillf }
19914960Swillf
19924960Swillf ret = ENOMEM;
19934960Swillf goto done;
19944960Swillf }
19954960Swillf memcpy (tptr->key_data_contents[k], tmp_key_data.key_data_contents[k], tmp_key_data.key_data_length[k]);
19964960Swillf
19974960Swillf memset (tmp_key_data.key_data_contents[k], 0, tmp_key_data.key_data_length[k]);
19984960Swillf free (tmp_key_data.key_data_contents[k]);
19994960Swillf tmp_key_data.key_data_contents[k] = NULL;
20004960Swillf }
20010Sstevel@tonic-gate }
20020Sstevel@tonic-gate kdb.n_key_data++;
20030Sstevel@tonic-gate }
20040Sstevel@tonic-gate
20050Sstevel@tonic-gate /* copy old key data if necessary */
20060Sstevel@tonic-gate for (i = 0; i < n_old_keys; i++) {
20070Sstevel@tonic-gate kdb.key_data[i+n_keys] = old_key_data[i];
20080Sstevel@tonic-gate memset(&old_key_data[i], 0, sizeof (krb5_key_data));
20090Sstevel@tonic-gate kdb.n_key_data++;
20100Sstevel@tonic-gate }
20114960Swillf
20124960Swillf if (old_key_data)
20134960Swillf krb5_db_free(handle->context, old_key_data);
20144960Swillf
20150Sstevel@tonic-gate /* assert(kdb.n_key_data == n_keys + n_old_keys) */
20160Sstevel@tonic-gate kdb.attributes &= ~KRB5_KDB_REQUIRES_PWCHANGE;
20170Sstevel@tonic-gate
20182881Smp153739 if ((ret = krb5_timeofday(handle->context, &now)))
20190Sstevel@tonic-gate goto done;
20200Sstevel@tonic-gate
20210Sstevel@tonic-gate if ((adb.aux_attributes & KADM5_POLICY)) {
20220Sstevel@tonic-gate if ((ret = kadm5_get_policy(handle->lhandle, adb.policy,
20230Sstevel@tonic-gate &pol)) != KADM5_OK)
20240Sstevel@tonic-gate goto done;
20250Sstevel@tonic-gate have_pol = 1;
20260Sstevel@tonic-gate
20270Sstevel@tonic-gate #if 0
20280Sstevel@tonic-gate /*
20290Sstevel@tonic-gate * The spec says this check is overridden if the caller has
20300Sstevel@tonic-gate * modify privilege. The admin server therefore makes this
20310Sstevel@tonic-gate * check itself (in chpass_principal_wrapper, misc.c). A
20320Sstevel@tonic-gate * local caller implicitly has all authorization bits.
20330Sstevel@tonic-gate */
20340Sstevel@tonic-gate if (ret = krb5_dbe_lookup_last_pwd_change(handle->context,
20350Sstevel@tonic-gate &kdb, &last_pwd))
20360Sstevel@tonic-gate goto done;
20370Sstevel@tonic-gate if((now - last_pwd) < pol.pw_min_life &&
20380Sstevel@tonic-gate !(kdb.attributes & KRB5_KDB_REQUIRES_PWCHANGE)) {
20390Sstevel@tonic-gate ret = KADM5_PASS_TOOSOON;
20400Sstevel@tonic-gate goto done;
20410Sstevel@tonic-gate }
20420Sstevel@tonic-gate #endif
20430Sstevel@tonic-gate #if 0
20440Sstevel@tonic-gate /*
20450Sstevel@tonic-gate * Should we be checking/updating pw history here?
20460Sstevel@tonic-gate */
20474960Swillf if (pol.pw_history_num > 1) {
20480Sstevel@tonic-gate if(adb.admin_history_kvno != hist_kvno) {
20490Sstevel@tonic-gate ret = KADM5_BAD_HIST_KEY;
20500Sstevel@tonic-gate goto done;
20510Sstevel@tonic-gate }
20520Sstevel@tonic-gate
20530Sstevel@tonic-gate if (ret = check_pw_reuse(handle->context,
20540Sstevel@tonic-gate &handle->master_keyblock,
20550Sstevel@tonic-gate &hist_key,
20560Sstevel@tonic-gate kdb.n_key_data, kdb.key_data,
20570Sstevel@tonic-gate adb.old_key_len, adb.old_keys))
20580Sstevel@tonic-gate goto done;
20590Sstevel@tonic-gate }
20600Sstevel@tonic-gate #endif
20610Sstevel@tonic-gate
20620Sstevel@tonic-gate if (pol.pw_max_life)
20630Sstevel@tonic-gate kdb.pw_expiration = now + pol.pw_max_life;
20640Sstevel@tonic-gate else
20650Sstevel@tonic-gate kdb.pw_expiration = 0;
20660Sstevel@tonic-gate } else {
20670Sstevel@tonic-gate kdb.pw_expiration = 0;
20680Sstevel@tonic-gate }
20690Sstevel@tonic-gate
20702881Smp153739 if ((ret = krb5_dbe_update_last_pwd_change(handle->context, &kdb, now)))
20712881Smp153739 goto done;
20720Sstevel@tonic-gate
20730Sstevel@tonic-gate if ((ret = kdb_put_entry(handle, &kdb, &adb)))
20740Sstevel@tonic-gate goto done;
20750Sstevel@tonic-gate
20760Sstevel@tonic-gate ret = KADM5_OK;
20770Sstevel@tonic-gate done:
20780Sstevel@tonic-gate kdb_free_entry(handle, &kdb, &adb);
20790Sstevel@tonic-gate if (have_pol)
20800Sstevel@tonic-gate kadm5_free_policy_ent(handle->lhandle, &pol);
20810Sstevel@tonic-gate
20820Sstevel@tonic-gate return ret;
20830Sstevel@tonic-gate }
20840Sstevel@tonic-gate
20850Sstevel@tonic-gate /*
20860Sstevel@tonic-gate * Allocate an array of n_key_data krb5_keyblocks, fill in each
20870Sstevel@tonic-gate * element with the results of decrypting the nth key in key_data with
20880Sstevel@tonic-gate * master_keyblock, and if n_keys is not NULL fill it in with the
20890Sstevel@tonic-gate * number of keys decrypted.
20900Sstevel@tonic-gate */
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)20910Sstevel@tonic-gate static int decrypt_key_data(krb5_context context,
20922881Smp153739 krb5_keyblock *master_keyblock,
20932881Smp153739 int n_key_data, krb5_key_data *key_data,
20942881Smp153739 krb5_keyblock **keyblocks, int *n_keys)
20950Sstevel@tonic-gate {
20960Sstevel@tonic-gate krb5_keyblock *keys;
20970Sstevel@tonic-gate int ret, i;
20980Sstevel@tonic-gate
20990Sstevel@tonic-gate keys = (krb5_keyblock *) malloc(n_key_data*sizeof(krb5_keyblock));
21000Sstevel@tonic-gate if (keys == NULL)
21010Sstevel@tonic-gate return ENOMEM;
21020Sstevel@tonic-gate memset((char *) keys, 0, n_key_data*sizeof(krb5_keyblock));
21030Sstevel@tonic-gate
21040Sstevel@tonic-gate for (i = 0; i < n_key_data; i++) {
21052881Smp153739 ret = krb5_dbekd_decrypt_key_data(context,
21062881Smp153739 master_keyblock,
21072881Smp153739 &key_data[i],
21082881Smp153739 &keys[i], NULL);
21092881Smp153739 if (ret) {
21104960Swillf for (; i >= 0; i--) {
21114960Swillf if (keys[i].contents) {
21124960Swillf memset (keys[i].contents, 0, keys[i].length);
21134960Swillf free( keys[i].contents );
21144960Swillf }
21154960Swillf }
21160Sstevel@tonic-gate
21170Sstevel@tonic-gate memset((char *) keys, 0, n_key_data*sizeof(krb5_keyblock));
21180Sstevel@tonic-gate free(keys);
21190Sstevel@tonic-gate return ret;
21200Sstevel@tonic-gate }
21210Sstevel@tonic-gate }
21220Sstevel@tonic-gate
21230Sstevel@tonic-gate *keyblocks = keys;
21240Sstevel@tonic-gate if (n_keys)
21250Sstevel@tonic-gate *n_keys = n_key_data;
21260Sstevel@tonic-gate
21270Sstevel@tonic-gate return 0;
21280Sstevel@tonic-gate }
21290Sstevel@tonic-gate
21300Sstevel@tonic-gate /*
21310Sstevel@tonic-gate * Function: kadm5_decrypt_key
21320Sstevel@tonic-gate *
21330Sstevel@tonic-gate * Purpose: Retrieves and decrypts a principal key.
21340Sstevel@tonic-gate *
21350Sstevel@tonic-gate * Arguments:
21360Sstevel@tonic-gate *
21370Sstevel@tonic-gate * server_handle (r) kadm5 handle
21380Sstevel@tonic-gate * entry (r) principal retrieved with kadm5_get_principal
21390Sstevel@tonic-gate * ktype (r) enctype to search for, or -1 to ignore
21400Sstevel@tonic-gate * stype (r) salt type to search for, or -1 to ignore
21410Sstevel@tonic-gate * kvno (r) kvno to search for, -1 for max, 0 for max
21420Sstevel@tonic-gate * only if it also matches ktype and stype
21430Sstevel@tonic-gate * keyblock (w) keyblock to fill in
21440Sstevel@tonic-gate * keysalt (w) keysalt to fill in, or NULL
21450Sstevel@tonic-gate * kvnop (w) kvno to fill in, or NULL
21460Sstevel@tonic-gate *
21470Sstevel@tonic-gate * Effects: Searches the key_data array of entry, which must have been
21480Sstevel@tonic-gate * retrived with kadm5_get_principal with the KADM5_KEY_DATA mask, to
21490Sstevel@tonic-gate * find a key with a specified enctype, salt type, and kvno in a
21500Sstevel@tonic-gate * principal entry. If not found, return ENOENT. Otherwise, decrypt
21510Sstevel@tonic-gate * it with the master key, and return the key in keyblock, the salt
21520Sstevel@tonic-gate * in salttype, and the key version number in kvno.
21530Sstevel@tonic-gate *
21540Sstevel@tonic-gate * If ktype or stype is -1, it is ignored for the search. If kvno is
21550Sstevel@tonic-gate * -1, ktype and stype are ignored and the key with the max kvno is
21560Sstevel@tonic-gate * returned. If kvno is 0, only the key with the max kvno is returned
21570Sstevel@tonic-gate * and only if it matches the ktype and stype; otherwise, ENOENT is
21580Sstevel@tonic-gate * returned.
21590Sstevel@tonic-gate */
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)21600Sstevel@tonic-gate kadm5_ret_t kadm5_decrypt_key(void *server_handle,
21610Sstevel@tonic-gate kadm5_principal_ent_t entry, krb5_int32
21620Sstevel@tonic-gate ktype, krb5_int32 stype, krb5_int32
21630Sstevel@tonic-gate kvno, krb5_keyblock *keyblock,
21640Sstevel@tonic-gate krb5_keysalt *keysalt, int *kvnop)
21650Sstevel@tonic-gate {
21660Sstevel@tonic-gate kadm5_server_handle_t handle = server_handle;
21670Sstevel@tonic-gate krb5_db_entry dbent;
21680Sstevel@tonic-gate krb5_key_data *key_data;
21690Sstevel@tonic-gate int ret;
21700Sstevel@tonic-gate
21710Sstevel@tonic-gate CHECK_HANDLE(server_handle);
21720Sstevel@tonic-gate
21730Sstevel@tonic-gate if (entry->n_key_data == 0 || entry->key_data == NULL)
21740Sstevel@tonic-gate return EINVAL;
21750Sstevel@tonic-gate
21760Sstevel@tonic-gate /* find_enctype only uses these two fields */
21770Sstevel@tonic-gate dbent.n_key_data = entry->n_key_data;
21780Sstevel@tonic-gate dbent.key_data = entry->key_data;
21792881Smp153739 if ((ret = krb5_dbe_find_enctype(handle->context, &dbent, ktype,
21802881Smp153739 stype, kvno, &key_data)))
21810Sstevel@tonic-gate return ret;
21820Sstevel@tonic-gate
21832881Smp153739 if ((ret = krb5_dbekd_decrypt_key_data(handle->context,
21842881Smp153739 &handle->master_keyblock, key_data,
21852881Smp153739 keyblock, keysalt)))
21860Sstevel@tonic-gate return ret;
21870Sstevel@tonic-gate
21887934SMark.Phalan@Sun.COM /*
21897934SMark.Phalan@Sun.COM * Coerce the enctype of the output keyblock in case we got an
21907934SMark.Phalan@Sun.COM * inexact match on the enctype; this behavior will go away when
21917934SMark.Phalan@Sun.COM * the key storage architecture gets redesigned for 1.3.
21927934SMark.Phalan@Sun.COM */
21937934SMark.Phalan@Sun.COM keyblock->enctype = ktype;
21947934SMark.Phalan@Sun.COM
21950Sstevel@tonic-gate if (kvnop)
21960Sstevel@tonic-gate *kvnop = key_data->key_data_kvno;
21970Sstevel@tonic-gate
21980Sstevel@tonic-gate return KADM5_OK;
21990Sstevel@tonic-gate }
22004960Swillf
2201*12253SPeter.Shoults@Sun.COM /* Solaris Kerberos */
2202*12253SPeter.Shoults@Sun.COM kadm5_ret_t
kadm5_check_min_life(void * server_handle,krb5_principal principal,char * msg_ret,unsigned int msg_len)2203*12253SPeter.Shoults@Sun.COM kadm5_check_min_life(void *server_handle, krb5_principal principal,
2204*12253SPeter.Shoults@Sun.COM char *msg_ret, unsigned int msg_len)
2205*12253SPeter.Shoults@Sun.COM {
2206*12253SPeter.Shoults@Sun.COM krb5_int32 now;
2207*12253SPeter.Shoults@Sun.COM kadm5_ret_t ret;
2208*12253SPeter.Shoults@Sun.COM kadm5_policy_ent_rec pol;
2209*12253SPeter.Shoults@Sun.COM kadm5_principal_ent_rec princ;
2210*12253SPeter.Shoults@Sun.COM kadm5_server_handle_t handle = server_handle;
2211*12253SPeter.Shoults@Sun.COM
2212*12253SPeter.Shoults@Sun.COM if (msg_ret != NULL)
2213*12253SPeter.Shoults@Sun.COM *msg_ret = '\0';
2214*12253SPeter.Shoults@Sun.COM
2215*12253SPeter.Shoults@Sun.COM ret = krb5_timeofday(handle->context, &now);
2216*12253SPeter.Shoults@Sun.COM if (ret)
2217*12253SPeter.Shoults@Sun.COM return ret;
2218*12253SPeter.Shoults@Sun.COM
2219*12253SPeter.Shoults@Sun.COM ret = kadm5_get_principal(handle->lhandle, principal,
2220*12253SPeter.Shoults@Sun.COM &princ, KADM5_PRINCIPAL_NORMAL_MASK);
2221*12253SPeter.Shoults@Sun.COM if(ret)
2222*12253SPeter.Shoults@Sun.COM return ret;
2223*12253SPeter.Shoults@Sun.COM if(princ.aux_attributes & KADM5_POLICY) {
2224*12253SPeter.Shoults@Sun.COM if((ret=kadm5_get_policy(handle->lhandle,
2225*12253SPeter.Shoults@Sun.COM princ.policy, &pol)) != KADM5_OK) {
2226*12253SPeter.Shoults@Sun.COM (void) kadm5_free_principal_ent(handle->lhandle, &princ);
2227*12253SPeter.Shoults@Sun.COM return ret;
2228*12253SPeter.Shoults@Sun.COM }
2229*12253SPeter.Shoults@Sun.COM if((now - princ.last_pwd_change) < pol.pw_min_life &&
2230*12253SPeter.Shoults@Sun.COM !(princ.attributes & KRB5_KDB_REQUIRES_PWCHANGE)) {
2231*12253SPeter.Shoults@Sun.COM if (msg_ret != NULL) {
2232*12253SPeter.Shoults@Sun.COM time_t until;
2233*12253SPeter.Shoults@Sun.COM char *time_string, *ptr, *errstr;
2234*12253SPeter.Shoults@Sun.COM
2235*12253SPeter.Shoults@Sun.COM until = princ.last_pwd_change + pol.pw_min_life;
2236*12253SPeter.Shoults@Sun.COM
2237*12253SPeter.Shoults@Sun.COM time_string = ctime(&until);
2238*12253SPeter.Shoults@Sun.COM errstr = (char *)error_message(CHPASS_UTIL_PASSWORD_TOO_SOON);
2239*12253SPeter.Shoults@Sun.COM
2240*12253SPeter.Shoults@Sun.COM if (strlen(errstr) + strlen(time_string) >= msg_len) {
2241*12253SPeter.Shoults@Sun.COM *errstr = '\0';
2242*12253SPeter.Shoults@Sun.COM } else {
2243*12253SPeter.Shoults@Sun.COM if (*(ptr = &time_string[strlen(time_string)-1]) == '\n')
2244*12253SPeter.Shoults@Sun.COM *ptr = '\0';
2245*12253SPeter.Shoults@Sun.COM sprintf(msg_ret, errstr, time_string);
2246*12253SPeter.Shoults@Sun.COM }
2247*12253SPeter.Shoults@Sun.COM }
2248*12253SPeter.Shoults@Sun.COM
2249*12253SPeter.Shoults@Sun.COM (void) kadm5_free_policy_ent(handle->lhandle, &pol);
2250*12253SPeter.Shoults@Sun.COM (void) kadm5_free_principal_ent(handle->lhandle, &princ);
2251*12253SPeter.Shoults@Sun.COM return KADM5_PASS_TOOSOON;
2252*12253SPeter.Shoults@Sun.COM }
2253*12253SPeter.Shoults@Sun.COM
2254*12253SPeter.Shoults@Sun.COM ret = kadm5_free_policy_ent(handle->lhandle, &pol);
2255*12253SPeter.Shoults@Sun.COM if (ret) {
2256*12253SPeter.Shoults@Sun.COM (void) kadm5_free_principal_ent(handle->lhandle, &princ);
2257*12253SPeter.Shoults@Sun.COM return ret;
2258*12253SPeter.Shoults@Sun.COM }
2259*12253SPeter.Shoults@Sun.COM }
2260*12253SPeter.Shoults@Sun.COM
2261*12253SPeter.Shoults@Sun.COM return kadm5_free_principal_ent(handle->lhandle, &princ);
2262*12253SPeter.Shoults@Sun.COM }
2263