1/*
2 * Copyright (c) 1997 - 2001, 2003 Kungliga Tekniska Högskolan
3 * (Royal Institute of Technology, Stockholm, Sweden).
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 *
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 *
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 *
17 * 3. Neither the name of the Institute nor the names of its contributors
18 *    may be used to endorse or promote products derived from this software
19 *    without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
32 */
33
34#include "kadm5_locl.h"
35
36RCSID("$Id$");
37
38/*
39 * Set the keys of `ent' to the string-to-key of `password'
40 */
41
42kadm5_ret_t
43_kadm5_set_keys(kadm5_server_context *context,
44		hdb_entry *ent,
45		const char *password,
46		uint32_t n_ks_tuple, krb5_key_salt_tuple *ks_tuple)
47{
48    Key *keys;
49    size_t num_keys;
50    kadm5_ret_t ret;
51
52    ret = hdb_generate_key_set_password(context->context,
53					ent->principal,
54					password, n_ks_tuple, ks_tuple,
55					&keys, &num_keys);
56    if (ret)
57	return ret;
58
59
60    _kadm5_free_keys (context->context, ent->keys.len, ent->keys.val);
61    ent->keys.val = keys;
62    ent->keys.len = (int)num_keys;
63
64    hdb_entry_set_pw_change_time(context->context, ent, 0);
65
66    if (krb5_config_get_bool_default(context->context, NULL, FALSE,
67				     "kadmin", "save-password", NULL))
68    {
69	ret = hdb_entry_set_password(context->context, context->db,
70				     ent, password);
71	if (ret)
72	    return ret;
73    }
74
75    return 0;
76}
77
78static void
79setup_Key(Key *k, Salt *s, krb5_key_data *kd, size_t kd_offset)
80{
81    memset(k, 0, sizeof (*k)); /* sets mkvno and salt */
82    k->key.keytype = kd[kd_offset].key_data_type[0];
83    k->key.keyvalue.length = kd[kd_offset].key_data_length[0];
84    k->key.keyvalue.data = kd[kd_offset].key_data_contents[0];
85
86    if(kd[kd_offset].key_data_ver == 2) {
87	memset(s, 0, sizeof (*s));
88	s->type = kd[kd_offset].key_data_type[1];
89	s->salt.length = kd[kd_offset].key_data_length[1];
90	s->salt.data = kd[kd_offset].key_data_contents[1];
91	k->salt = s;
92    }
93}
94
95/*
96 * Set the keys of `ent' to (`n_key_data', `key_data')
97 */
98
99kadm5_ret_t
100_kadm5_set_keys2(kadm5_server_context *context,
101		 hdb_entry *ent,
102		 int16_t n_key_data,
103		 krb5_key_data *key_data)
104{
105    krb5_error_code ret;
106    size_t i, k;
107    HDB_extension ext;
108    HDB_extension *extp = NULL;
109    HDB_Ext_KeySet *hist_keys = &ext.data.u.hist_keys;
110    Key key;
111    Salt salt;
112    Keys keys;
113    hdb_keyset hkset;
114    krb5_kvno kvno = -1;
115    int one_key_set = 1;
116    int replace_hist_keys = 0;
117
118    if (n_key_data == 0) {
119	/* Clear all keys! */
120	ret = hdb_clear_extension(context->context, ent,
121				  choice_HDB_extension_data_hist_keys);
122	if (ret)
123	    return ret;
124	free_Keys(&ent->keys);
125	return 0;
126    }
127
128    memset(&keys, 0, sizeof (keys));
129    memset(&hkset, 0, sizeof (hkset)); /* set set_time */
130    ext.data.element = choice_HDB_extension_data_hist_keys;
131    memset(hist_keys, 0, sizeof (*hist_keys));
132
133    for (i = 0; i < n_key_data; i++) {
134	if (kvno != -1 && kvno != key_data[i].key_data_kvno) {
135	    one_key_set = 0;
136	    break;
137	}
138	kvno = key_data[i].key_data_kvno;
139    }
140    if (one_key_set) {
141	/*
142	 * If we're updating KADM5_KEY_DATA with a single keyset then we
143	 * assume we must be setting the principal's kvno as well!
144	 *
145	 * Just have to be careful about old clients that might have
146	 * sent 0 as the kvno...  This may seem ugly, but it's the price
147	 * of backwards compatibility with pre-multi-kvno kadmin clients
148	 * (besides, who's to say that updating KADM5_KEY_DATA requires
149	 * updating the entry's kvno?)
150	 *
151	 * Note that we do nothing special for the case where multiple
152	 * keysets are given but the entry's kvno is not set and not in
153	 * the given set of keysets.  If this happens we'll just update
154	 * the key history only and leave the current keyset alone.
155	 */
156	if (kvno == 0) {
157	    /* Force kvno to 1 if it was 0; (ank would do this anyways) */
158	    if (ent->kvno == 0)
159		ent->kvno = 1;
160	    /* Below we need key_data[*].kvno to be reasonable */
161	    for (i = 0; i < n_key_data; i++)
162		key_data[i].key_data_kvno = ent->kvno;
163	} else {
164	    /*
165	     * Or force the entry's kvno to match the one from the new,
166	     * singular keyset
167	     */
168	    ent->kvno = kvno;
169	}
170    }
171
172    for (i = 0; i < n_key_data; i++) {
173	if (key_data[i].key_data_kvno == ent->kvno) {
174	    /* A current key; add to current key set */
175	    setup_Key(&key, &salt, key_data, i);
176	    ret = add_Keys(&keys, &key);
177	    continue;
178	}
179
180	/*
181	 * This kvno is historical.  Build an hdb_keyset for keys of
182	 * this enctype and add them to the new key history.
183	 */
184	for (k = 0; k < hist_keys->len; k++) {
185	    if (hist_keys->val[k].kvno == key_data[i].key_data_kvno)
186		break;
187	}
188	if (hist_keys->len > k &&
189	    hist_keys->val[k].kvno == key_data[i].key_data_kvno)
190	    /* We've added all keys of this kvno already (see below) */
191	    continue;
192
193	memset(&hkset, 0, sizeof (hkset)); /* set set_time */
194	hkset.kvno = key_data[i].key_data_kvno;
195	for (k = 0; k < n_key_data; k++) {
196	    /* Find all keys of this kvno and add them to the new keyset */
197	    if (key_data[k].key_data_kvno != hkset.kvno)
198		continue;
199
200	    setup_Key(&key, &salt, key_data, k);
201	    ret = add_Keys(&hkset.keys, &key);
202	    if (ret)
203		goto out;
204	}
205	ret = add_HDB_Ext_KeySet(hist_keys, &hkset);
206	if (ret)
207	    goto out;
208	replace_hist_keys = 1;
209    }
210
211    if (replace_hist_keys)
212	/* No key history given -> leave it alone */
213	extp = hdb_find_extension(ent, choice_HDB_extension_data_hist_keys);
214    if (extp != NULL) {
215	HDB_Ext_KeySet *old_hist_keys;
216
217	/*
218	 * Try to keep the very useful set_time values from the old hist
219	 * keys.  kadm5 loses this info, so this heuristic is the best we
220	 * can do.
221	 */
222	old_hist_keys = &extp->data.u.hist_keys;
223	for (i = 0; i < old_hist_keys->len; i++) {
224	    if (old_hist_keys->val[i].set_time == NULL)
225		continue;
226	    for (k = 0; k < hist_keys->len; k++) {
227		if (hist_keys->val[k].kvno != old_hist_keys->val[k].kvno)
228		    continue;
229		hist_keys->val[k].set_time = old_hist_keys->val[k].set_time;
230		old_hist_keys->val[k].set_time = NULL;
231	    }
232	}
233    }
234
235    if (replace_hist_keys) {
236	/* If hist keys not given in key_data then don't blow away hist_keys */
237	ret = hdb_replace_extension(context->context, ent, &ext);
238	if (ret)
239	    goto out;
240    }
241
242    /*
243     * A structure copy is more efficient here than this would be:
244     *
245     * copy_Keys(&keys, &ent->keys);
246     * free_Keys(&keys);
247     *
248     * Of course, the above hdb_replace_extension() is not at all efficient...
249     */
250    free_Keys(&ent->keys);
251    ent->keys = keys;
252    hdb_entry_set_pw_change_time(context->context, ent, 0);
253    hdb_entry_clear_password(context->context, ent);
254
255    return 0;
256
257out:
258    free_Keys(&keys);
259    free_hdb_keyset(&hkset);
260    free_HDB_extension(&ext);
261    return ret;
262}
263
264/*
265 * Set the keys of `ent' to `n_keys, keys'
266 */
267
268kadm5_ret_t
269_kadm5_set_keys3(kadm5_server_context *context,
270		 hdb_entry *ent,
271		 int n_keys,
272		 krb5_keyblock *keyblocks)
273{
274    krb5_error_code ret;
275    int i;
276    unsigned len;
277    Key *keys;
278
279    len  = n_keys;
280    keys = malloc (len * sizeof(*keys));
281    if (keys == NULL && len != 0)
282	return ENOMEM;
283
284    _kadm5_init_keys (keys, len);
285
286    for(i = 0; i < n_keys; i++) {
287	keys[i].mkvno = NULL;
288	ret = krb5_copy_keyblock_contents (context->context,
289					   &keyblocks[i],
290					   &keys[i].key);
291	if(ret)
292	    goto out;
293	keys[i].salt = NULL;
294    }
295    _kadm5_free_keys (context->context, ent->keys.len, ent->keys.val);
296    ent->keys.len = len;
297    ent->keys.val = keys;
298
299    hdb_entry_set_pw_change_time(context->context, ent, 0);
300    hdb_entry_clear_password(context->context, ent);
301
302    return 0;
303 out:
304    _kadm5_free_keys (context->context, len, keys);
305    return ret;
306}
307
308/*
309 *
310 */
311
312static int
313is_des_key_p(int keytype)
314{
315    return keytype == ETYPE_DES_CBC_CRC ||
316    	keytype == ETYPE_DES_CBC_MD4 ||
317	keytype == ETYPE_DES_CBC_MD5;
318}
319
320
321/*
322 * Set the keys of `ent' to random keys and return them in `n_keys'
323 * and `new_keys'.
324 */
325
326kadm5_ret_t
327_kadm5_set_keys_randomly (kadm5_server_context *context,
328			  hdb_entry *ent,
329			  int n_ks_tuple,
330			  krb5_key_salt_tuple *ks_tuple,
331			  krb5_keyblock **new_keys,
332			  int *n_keys)
333{
334   krb5_keyblock *kblock = NULL;
335   kadm5_ret_t ret = 0;
336   ssize_t des_keyblock;
337   size_t i, num_keys;
338   Key *keys;
339
340   ret = hdb_generate_key_set(context->context, ent->principal,
341			      n_ks_tuple, ks_tuple,
342			      &keys, &num_keys, 1);
343   if (ret)
344	return ret;
345
346   kblock = malloc(num_keys * sizeof(kblock[0]));
347   if (kblock == NULL) {
348	ret = ENOMEM;
349	_kadm5_free_keys (context->context, (int)num_keys, keys);
350	return ret;
351   }
352   memset(kblock, 0, num_keys * sizeof(kblock[0]));
353
354   des_keyblock = -1;
355   for (i = 0; i < num_keys; i++) {
356
357	/*
358	 * To make sure all des keys are the the same we generate only
359	 * the first one and then copy key to all other des keys.
360	 */
361
362	if (des_keyblock != -1 && is_des_key_p(keys[i].key.keytype)) {
363	    ret = krb5_copy_keyblock_contents (context->context,
364					       &kblock[des_keyblock],
365					       &kblock[i]);
366	    if (ret)
367		goto out;
368	    kblock[i].keytype = keys[i].key.keytype;
369	} else {
370	    ret = krb5_generate_random_keyblock (context->context,
371						 keys[i].key.keytype,
372						 &kblock[i]);
373	    if (ret)
374		goto out;
375
376	    if (is_des_key_p(keys[i].key.keytype))
377		des_keyblock = i;
378	}
379
380	ret = krb5_copy_keyblock_contents (context->context,
381					   &kblock[i],
382					   &keys[i].key);
383	if (ret)
384	    goto out;
385   }
386
387out:
388   if(ret) {
389	for (i = 0; i < num_keys; ++i)
390	    krb5_free_keyblock_contents (context->context, &kblock[i]);
391	free(kblock);
392	_kadm5_free_keys (context->context, (int)num_keys, keys);
393	return ret;
394   }
395
396   _kadm5_free_keys (context->context, ent->keys.len, ent->keys.val);
397   ent->keys.val = keys;
398   ent->keys.len = (int)num_keys;
399   if (n_keys && new_keys) {
400       *new_keys     = kblock;
401       *n_keys       = (int)num_keys;
402   }
403
404   hdb_entry_set_pw_change_time(context->context, ent, 0);
405   hdb_entry_clear_password(context->context, ent);
406
407   return 0;
408}
409