1/*
2 * Copyright (c) 2004 - 2005 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 "hdb_locl.h"
35#include <der.h>
36
37krb5_error_code
38hdb_entry_check_mandatory(krb5_context context, const hdb_entry *ent)
39{
40    size_t i;
41
42    if (ent->extensions == NULL)
43	return 0;
44
45    /*
46     * check for unknown extensions and if they where tagged mandatory
47     */
48
49    for (i = 0; i < ent->extensions->len; i++) {
50	if (ent->extensions->val[i].data.element !=
51	    choice_HDB_extension_data_asn1_ellipsis)
52	    continue;
53	if (ent->extensions->val[i].mandatory) {
54	    krb5_set_error_message(context, HDB_ERR_MANDATORY_OPTION,
55				   "Principal have unknown "
56				   "mandatory extension");
57	    return HDB_ERR_MANDATORY_OPTION;
58	}
59    }
60    return 0;
61}
62
63HDB_extension *
64hdb_find_extension(const hdb_entry *entry, int type)
65{
66    size_t i;
67
68    if (entry->extensions == NULL)
69	return NULL;
70
71    for (i = 0; i < entry->extensions->len; i++)
72	if (entry->extensions->val[i].data.element == (unsigned)type)
73	    return &entry->extensions->val[i];
74    return NULL;
75}
76
77/*
78 * Replace the extension `ext' in `entry'. Make a copy of the
79 * extension, so the caller must still free `ext' on both success and
80 * failure. Returns 0 or error code.
81 */
82
83krb5_error_code
84hdb_replace_extension(krb5_context context,
85		      hdb_entry *entry,
86		      const HDB_extension *ext)
87{
88    HDB_extension *ext2;
89    HDB_extension *es;
90    int ret;
91
92    ext2 = NULL;
93
94    if (entry->extensions == NULL) {
95	entry->extensions = calloc(1, sizeof(*entry->extensions));
96	if (entry->extensions == NULL) {
97	    krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
98	    return ENOMEM;
99	}
100    } else if (ext->data.element != choice_HDB_extension_data_asn1_ellipsis) {
101	ext2 = hdb_find_extension(entry, ext->data.element);
102    } else {
103	/*
104	 * This is an unknown extention, and we are asked to replace a
105	 * possible entry in `entry' that is of the same type. This
106	 * might seem impossible, but ASN.1 CHOICE comes to our
107	 * rescue. The first tag in each branch in the CHOICE is
108	 * unique, so just find the element in the list that have the
109	 * same tag was we are putting into the list.
110	 */
111	Der_class replace_class, list_class;
112	Der_type replace_type, list_type;
113	unsigned int replace_tag, list_tag;
114	size_t size;
115	size_t i;
116
117	ret = der_get_tag(ext->data.u.asn1_ellipsis.data,
118			  ext->data.u.asn1_ellipsis.length,
119			  &replace_class, &replace_type, &replace_tag,
120			  &size);
121	if (ret) {
122	    krb5_set_error_message(context, ret, "hdb: failed to decode "
123				   "replacement hdb extention");
124	    return ret;
125	}
126
127	for (i = 0; i < entry->extensions->len; i++) {
128	    HDB_extension *ext3 = &entry->extensions->val[i];
129
130	    if (ext3->data.element != choice_HDB_extension_data_asn1_ellipsis)
131		continue;
132
133	    ret = der_get_tag(ext3->data.u.asn1_ellipsis.data,
134			      ext3->data.u.asn1_ellipsis.length,
135			      &list_class, &list_type, &list_tag,
136			      &size);
137	    if (ret) {
138		krb5_set_error_message(context, ret, "hdb: failed to decode "
139				       "present hdb extention");
140		return ret;
141	    }
142
143	    if (MAKE_TAG(replace_class,replace_type,replace_type) ==
144		MAKE_TAG(list_class,list_type,list_type)) {
145		ext2 = ext3;
146		break;
147	    }
148	}
149    }
150
151    if (ext2) {
152	free_HDB_extension(ext2);
153	ret = copy_HDB_extension(ext, ext2);
154	if (ret)
155	    krb5_set_error_message(context, ret, "hdb: failed to copy replacement "
156				   "hdb extention");
157	return ret;
158    }
159
160    es = realloc(entry->extensions->val,
161		 (entry->extensions->len+1)*sizeof(entry->extensions->val[0]));
162    if (es == NULL) {
163	krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
164	return ENOMEM;
165    }
166    entry->extensions->val = es;
167
168    ret = copy_HDB_extension(ext,
169			     &entry->extensions->val[entry->extensions->len]);
170    if (ret == 0)
171	entry->extensions->len++;
172    else
173	krb5_set_error_message(context, ret, "hdb: failed to copy new extension");
174
175    return ret;
176}
177
178krb5_error_code
179hdb_clear_extension(krb5_context context,
180		    hdb_entry *entry,
181		    int type)
182{
183    size_t i;
184
185    if (entry->extensions == NULL)
186	return 0;
187
188    for (i = 0; i < entry->extensions->len; i++) {
189	if (entry->extensions->val[i].data.element == (unsigned)type) {
190	    free_HDB_extension(&entry->extensions->val[i]);
191	    memmove(&entry->extensions->val[i],
192		    &entry->extensions->val[i + 1],
193		    sizeof(entry->extensions->val[i]) * (entry->extensions->len - i - 1));
194	    entry->extensions->len--;
195	}
196    }
197    if (entry->extensions->len == 0) {
198	free(entry->extensions->val);
199	free(entry->extensions);
200	entry->extensions = NULL;
201    }
202
203    return 0;
204}
205
206
207krb5_error_code
208hdb_entry_get_pkinit_acl(const hdb_entry *entry, const HDB_Ext_PKINIT_acl **a)
209{
210    const HDB_extension *ext;
211
212    ext = hdb_find_extension(entry, choice_HDB_extension_data_pkinit_acl);
213    if (ext)
214	*a = &ext->data.u.pkinit_acl;
215    else
216	*a = NULL;
217
218    return 0;
219}
220
221krb5_error_code
222hdb_entry_get_pkinit_hash(const hdb_entry *entry, const HDB_Ext_PKINIT_hash **a)
223{
224    const HDB_extension *ext;
225
226    ext = hdb_find_extension(entry, choice_HDB_extension_data_pkinit_cert_hash);
227    if (ext)
228	*a = &ext->data.u.pkinit_cert_hash;
229    else
230	*a = NULL;
231
232    return 0;
233}
234
235krb5_error_code
236hdb_entry_get_pkinit_cert(const hdb_entry *entry, const HDB_Ext_PKINIT_cert **a)
237{
238    const HDB_extension *ext;
239
240    ext = hdb_find_extension(entry, choice_HDB_extension_data_pkinit_cert);
241    if (ext)
242	*a = &ext->data.u.pkinit_cert;
243    else
244	*a = NULL;
245
246    return 0;
247}
248
249krb5_error_code
250hdb_entry_get_pw_change_time(const hdb_entry *entry, time_t *t)
251{
252    const HDB_extension *ext;
253
254    ext = hdb_find_extension(entry, choice_HDB_extension_data_last_pw_change);
255    if (ext)
256	*t = ext->data.u.last_pw_change;
257    else
258	*t = 0;
259
260    return 0;
261}
262
263krb5_error_code
264hdb_entry_set_pw_change_time(krb5_context context,
265			     hdb_entry *entry,
266			     time_t t)
267{
268    HDB_extension ext;
269
270    ext.mandatory = FALSE;
271    ext.data.element = choice_HDB_extension_data_last_pw_change;
272    if (t == 0)
273	t = time(NULL);
274    ext.data.u.last_pw_change = t;
275
276    return hdb_replace_extension(context, entry, &ext);
277}
278
279int
280hdb_entry_get_password(krb5_context context, HDB *db,
281		       const hdb_entry *entry, char **p)
282{
283    HDB_extension *ext;
284    char *str;
285    int ret;
286
287    ext = hdb_find_extension(entry, choice_HDB_extension_data_password);
288    if (ext) {
289	heim_utf8_string xstr;
290	heim_octet_string pw;
291
292	if (db->hdb_master_key_set && ext->data.u.password.mkvno) {
293	    hdb_master_key key;
294
295	    key = _hdb_find_master_key(ext->data.u.password.mkvno,
296				       db->hdb_master_key);
297
298	    if (key == NULL) {
299		krb5_set_error_message(context, HDB_ERR_NO_MKEY,
300				       "master key %d missing",
301				       *ext->data.u.password.mkvno);
302		return HDB_ERR_NO_MKEY;
303	    }
304
305	    ret = _hdb_mkey_decrypt(context, key, HDB_KU_MKEY,
306				    ext->data.u.password.password.data,
307				    ext->data.u.password.password.length,
308				    &pw);
309	} else {
310	    ret = der_copy_octet_string(&ext->data.u.password.password, &pw);
311	}
312	if (ret) {
313	    krb5_clear_error_message(context);
314	    return ret;
315	}
316
317	xstr = pw.data;
318	if (xstr[pw.length - 1] != '\0') {
319	    krb5_set_error_message(context, EINVAL, "malformed password");
320	    return EINVAL;
321	}
322
323	*p = strdup(xstr);
324
325	der_free_octet_string(&pw);
326	if (*p == NULL) {
327	    krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
328	    return ENOMEM;
329	}
330	return 0;
331    }
332
333    ret = krb5_unparse_name(context, entry->principal, &str);
334    if (ret == 0) {
335	krb5_set_error_message(context, ENOENT,
336			       "no password attribute for %s", str);
337	free(str);
338    } else
339	krb5_clear_error_message(context);
340
341    return ENOENT;
342}
343
344int
345hdb_entry_set_password(krb5_context context, HDB *db,
346		       hdb_entry *entry, const char *p)
347{
348    HDB_extension ext;
349    hdb_master_key key;
350    int ret;
351
352    ext.mandatory = FALSE;
353    ext.data.element = choice_HDB_extension_data_password;
354
355    if (db->hdb_master_key_set) {
356
357	key = _hdb_find_master_key(NULL, db->hdb_master_key);
358	if (key == NULL) {
359	    krb5_set_error_message(context, HDB_ERR_NO_MKEY,
360				   "hdb_entry_set_password: "
361				   "failed to find masterkey");
362	    return HDB_ERR_NO_MKEY;
363	}
364
365	ret = _hdb_mkey_encrypt(context, key, HDB_KU_MKEY,
366				p, strlen(p) + 1,
367				&ext.data.u.password.password);
368	if (ret)
369	    return ret;
370
371	ext.data.u.password.mkvno =
372	    malloc(sizeof(*ext.data.u.password.mkvno));
373	if (ext.data.u.password.mkvno == NULL) {
374	    free_HDB_extension(&ext);
375	    krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
376	    return ENOMEM;
377	}
378	*ext.data.u.password.mkvno = _hdb_mkey_version(key);
379
380    } else {
381	ext.data.u.password.mkvno = NULL;
382
383	ret = krb5_data_copy(&ext.data.u.password.password,
384			     p, strlen(p) + 1);
385	if (ret) {
386	    krb5_set_error_message(context, ret, "malloc: out of memory");
387	    free_HDB_extension(&ext);
388	    return ret;
389	}
390    }
391
392    ret = hdb_replace_extension(context, entry, &ext);
393
394    free_HDB_extension(&ext);
395
396    return ret;
397}
398
399int
400hdb_entry_clear_password(krb5_context context, hdb_entry *entry)
401{
402    return hdb_clear_extension(context, entry,
403			       choice_HDB_extension_data_password);
404}
405
406krb5_error_code
407hdb_entry_get_ConstrainedDelegACL(const hdb_entry *entry,
408				  const HDB_Ext_Constrained_delegation_acl **a)
409{
410    const HDB_extension *ext;
411
412    ext = hdb_find_extension(entry,
413			     choice_HDB_extension_data_allowed_to_delegate_to);
414    if (ext)
415	*a = &ext->data.u.allowed_to_delegate_to;
416    else
417	*a = NULL;
418
419    return 0;
420}
421
422krb5_error_code
423hdb_entry_get_aliases(const hdb_entry *entry, const HDB_Ext_Aliases **a)
424{
425    const HDB_extension *ext;
426
427    ext = hdb_find_extension(entry, choice_HDB_extension_data_aliases);
428    if (ext)
429	*a = &ext->data.u.aliases;
430    else
431	*a = NULL;
432
433    return 0;
434}
435