1178825Sdfr/*
2233294Sstas * Copyright (c) 2004 - 2005 Kungliga Tekniska H��gskolan
3233294Sstas * (Royal Institute of Technology, Stockholm, Sweden).
4233294Sstas * All rights reserved.
5178825Sdfr *
6233294Sstas * Redistribution and use in source and binary forms, with or without
7233294Sstas * modification, are permitted provided that the following conditions
8233294Sstas * are met:
9178825Sdfr *
10233294Sstas * 1. Redistributions of source code must retain the above copyright
11233294Sstas *    notice, this list of conditions and the following disclaimer.
12178825Sdfr *
13233294Sstas * 2. Redistributions in binary form must reproduce the above copyright
14233294Sstas *    notice, this list of conditions and the following disclaimer in the
15233294Sstas *    documentation and/or other materials provided with the distribution.
16178825Sdfr *
17233294Sstas * 3. Neither the name of the Institute nor the names of its contributors
18233294Sstas *    may be used to endorse or promote products derived from this software
19233294Sstas *    without specific prior written permission.
20178825Sdfr *
21233294Sstas * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
22233294Sstas * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23233294Sstas * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24233294Sstas * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
25233294Sstas * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26233294Sstas * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27233294Sstas * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28233294Sstas * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29233294Sstas * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30233294Sstas * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31233294Sstas * SUCH DAMAGE.
32178825Sdfr */
33178825Sdfr
34178825Sdfr#include "hdb_locl.h"
35178825Sdfr#include <der.h>
36178825Sdfr
37178825Sdfrkrb5_error_code
38178825Sdfrhdb_entry_check_mandatory(krb5_context context, const hdb_entry *ent)
39178825Sdfr{
40233294Sstas    size_t i;
41178825Sdfr
42178825Sdfr    if (ent->extensions == NULL)
43178825Sdfr	return 0;
44178825Sdfr
45233294Sstas    /*
46178825Sdfr     * check for unknown extensions and if they where tagged mandatory
47178825Sdfr     */
48178825Sdfr
49178825Sdfr    for (i = 0; i < ent->extensions->len; i++) {
50233294Sstas	if (ent->extensions->val[i].data.element !=
51178825Sdfr	    choice_HDB_extension_data_asn1_ellipsis)
52178825Sdfr	    continue;
53178825Sdfr	if (ent->extensions->val[i].mandatory) {
54233294Sstas	    krb5_set_error_message(context, HDB_ERR_MANDATORY_OPTION,
55233294Sstas				   "Principal have unknown "
56233294Sstas				   "mandatory extension");
57178825Sdfr	    return HDB_ERR_MANDATORY_OPTION;
58178825Sdfr	}
59178825Sdfr    }
60178825Sdfr    return 0;
61178825Sdfr}
62178825Sdfr
63178825SdfrHDB_extension *
64178825Sdfrhdb_find_extension(const hdb_entry *entry, int type)
65178825Sdfr{
66233294Sstas    size_t i;
67178825Sdfr
68178825Sdfr    if (entry->extensions == NULL)
69178825Sdfr	return NULL;
70178825Sdfr
71178825Sdfr    for (i = 0; i < entry->extensions->len; i++)
72233294Sstas	if (entry->extensions->val[i].data.element == (unsigned)type)
73178825Sdfr	    return &entry->extensions->val[i];
74178825Sdfr    return NULL;
75178825Sdfr}
76178825Sdfr
77178825Sdfr/*
78178825Sdfr * Replace the extension `ext' in `entry'. Make a copy of the
79178825Sdfr * extension, so the caller must still free `ext' on both success and
80178825Sdfr * failure. Returns 0 or error code.
81178825Sdfr */
82178825Sdfr
83178825Sdfrkrb5_error_code
84233294Sstashdb_replace_extension(krb5_context context,
85233294Sstas		      hdb_entry *entry,
86178825Sdfr		      const HDB_extension *ext)
87178825Sdfr{
88178825Sdfr    HDB_extension *ext2;
89178825Sdfr    HDB_extension *es;
90178825Sdfr    int ret;
91178825Sdfr
92178825Sdfr    ext2 = NULL;
93178825Sdfr
94178825Sdfr    if (entry->extensions == NULL) {
95178825Sdfr	entry->extensions = calloc(1, sizeof(*entry->extensions));
96178825Sdfr	if (entry->extensions == NULL) {
97233294Sstas	    krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
98178825Sdfr	    return ENOMEM;
99178825Sdfr	}
100178825Sdfr    } else if (ext->data.element != choice_HDB_extension_data_asn1_ellipsis) {
101178825Sdfr	ext2 = hdb_find_extension(entry, ext->data.element);
102178825Sdfr    } else {
103233294Sstas	/*
104178825Sdfr	 * This is an unknown extention, and we are asked to replace a
105178825Sdfr	 * possible entry in `entry' that is of the same type. This
106178825Sdfr	 * might seem impossible, but ASN.1 CHOICE comes to our
107178825Sdfr	 * rescue. The first tag in each branch in the CHOICE is
108178825Sdfr	 * unique, so just find the element in the list that have the
109178825Sdfr	 * same tag was we are putting into the list.
110178825Sdfr	 */
111178825Sdfr	Der_class replace_class, list_class;
112178825Sdfr	Der_type replace_type, list_type;
113178825Sdfr	unsigned int replace_tag, list_tag;
114178825Sdfr	size_t size;
115233294Sstas	size_t i;
116178825Sdfr
117178825Sdfr	ret = der_get_tag(ext->data.u.asn1_ellipsis.data,
118178825Sdfr			  ext->data.u.asn1_ellipsis.length,
119178825Sdfr			  &replace_class, &replace_type, &replace_tag,
120178825Sdfr			  &size);
121178825Sdfr	if (ret) {
122233294Sstas	    krb5_set_error_message(context, ret, "hdb: failed to decode "
123233294Sstas				   "replacement hdb extention");
124178825Sdfr	    return ret;
125178825Sdfr	}
126178825Sdfr
127178825Sdfr	for (i = 0; i < entry->extensions->len; i++) {
128178825Sdfr	    HDB_extension *ext3 = &entry->extensions->val[i];
129178825Sdfr
130178825Sdfr	    if (ext3->data.element != choice_HDB_extension_data_asn1_ellipsis)
131178825Sdfr		continue;
132178825Sdfr
133178825Sdfr	    ret = der_get_tag(ext3->data.u.asn1_ellipsis.data,
134178825Sdfr			      ext3->data.u.asn1_ellipsis.length,
135178825Sdfr			      &list_class, &list_type, &list_tag,
136178825Sdfr			      &size);
137178825Sdfr	    if (ret) {
138233294Sstas		krb5_set_error_message(context, ret, "hdb: failed to decode "
139233294Sstas				       "present hdb extention");
140178825Sdfr		return ret;
141178825Sdfr	    }
142178825Sdfr
143178825Sdfr	    if (MAKE_TAG(replace_class,replace_type,replace_type) ==
144178825Sdfr		MAKE_TAG(list_class,list_type,list_type)) {
145178825Sdfr		ext2 = ext3;
146178825Sdfr		break;
147178825Sdfr	    }
148178825Sdfr	}
149178825Sdfr    }
150178825Sdfr
151178825Sdfr    if (ext2) {
152178825Sdfr	free_HDB_extension(ext2);
153178825Sdfr	ret = copy_HDB_extension(ext, ext2);
154178825Sdfr	if (ret)
155233294Sstas	    krb5_set_error_message(context, ret, "hdb: failed to copy replacement "
156233294Sstas				   "hdb extention");
157178825Sdfr	return ret;
158178825Sdfr    }
159178825Sdfr
160233294Sstas    es = realloc(entry->extensions->val,
161178825Sdfr		 (entry->extensions->len+1)*sizeof(entry->extensions->val[0]));
162178825Sdfr    if (es == NULL) {
163233294Sstas	krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
164178825Sdfr	return ENOMEM;
165178825Sdfr    }
166178825Sdfr    entry->extensions->val = es;
167178825Sdfr
168178825Sdfr    ret = copy_HDB_extension(ext,
169178825Sdfr			     &entry->extensions->val[entry->extensions->len]);
170178825Sdfr    if (ret == 0)
171178825Sdfr	entry->extensions->len++;
172178825Sdfr    else
173233294Sstas	krb5_set_error_message(context, ret, "hdb: failed to copy new extension");
174178825Sdfr
175178825Sdfr    return ret;
176178825Sdfr}
177178825Sdfr
178178825Sdfrkrb5_error_code
179233294Sstashdb_clear_extension(krb5_context context,
180233294Sstas		    hdb_entry *entry,
181178825Sdfr		    int type)
182178825Sdfr{
183233294Sstas    size_t i;
184178825Sdfr
185178825Sdfr    if (entry->extensions == NULL)
186178825Sdfr	return 0;
187178825Sdfr
188178825Sdfr    for (i = 0; i < entry->extensions->len; i++) {
189233294Sstas	if (entry->extensions->val[i].data.element == (unsigned)type) {
190178825Sdfr	    free_HDB_extension(&entry->extensions->val[i]);
191178825Sdfr	    memmove(&entry->extensions->val[i],
192178825Sdfr		    &entry->extensions->val[i + 1],
193178825Sdfr		    sizeof(entry->extensions->val[i]) * (entry->extensions->len - i - 1));
194178825Sdfr	    entry->extensions->len--;
195178825Sdfr	}
196178825Sdfr    }
197178825Sdfr    if (entry->extensions->len == 0) {
198178825Sdfr	free(entry->extensions->val);
199178825Sdfr	free(entry->extensions);
200178825Sdfr	entry->extensions = NULL;
201178825Sdfr    }
202178825Sdfr
203178825Sdfr    return 0;
204178825Sdfr}
205178825Sdfr
206178825Sdfr
207178825Sdfrkrb5_error_code
208178825Sdfrhdb_entry_get_pkinit_acl(const hdb_entry *entry, const HDB_Ext_PKINIT_acl **a)
209178825Sdfr{
210178825Sdfr    const HDB_extension *ext;
211178825Sdfr
212178825Sdfr    ext = hdb_find_extension(entry, choice_HDB_extension_data_pkinit_acl);
213178825Sdfr    if (ext)
214178825Sdfr	*a = &ext->data.u.pkinit_acl;
215178825Sdfr    else
216178825Sdfr	*a = NULL;
217178825Sdfr
218178825Sdfr    return 0;
219178825Sdfr}
220178825Sdfr
221178825Sdfrkrb5_error_code
222178825Sdfrhdb_entry_get_pkinit_hash(const hdb_entry *entry, const HDB_Ext_PKINIT_hash **a)
223178825Sdfr{
224178825Sdfr    const HDB_extension *ext;
225178825Sdfr
226178825Sdfr    ext = hdb_find_extension(entry, choice_HDB_extension_data_pkinit_cert_hash);
227178825Sdfr    if (ext)
228178825Sdfr	*a = &ext->data.u.pkinit_cert_hash;
229178825Sdfr    else
230178825Sdfr	*a = NULL;
231178825Sdfr
232178825Sdfr    return 0;
233178825Sdfr}
234178825Sdfr
235178825Sdfrkrb5_error_code
236233294Sstashdb_entry_get_pkinit_cert(const hdb_entry *entry, const HDB_Ext_PKINIT_cert **a)
237233294Sstas{
238233294Sstas    const HDB_extension *ext;
239233294Sstas
240233294Sstas    ext = hdb_find_extension(entry, choice_HDB_extension_data_pkinit_cert);
241233294Sstas    if (ext)
242233294Sstas	*a = &ext->data.u.pkinit_cert;
243233294Sstas    else
244233294Sstas	*a = NULL;
245233294Sstas
246233294Sstas    return 0;
247233294Sstas}
248233294Sstas
249233294Sstaskrb5_error_code
250178825Sdfrhdb_entry_get_pw_change_time(const hdb_entry *entry, time_t *t)
251178825Sdfr{
252178825Sdfr    const HDB_extension *ext;
253178825Sdfr
254178825Sdfr    ext = hdb_find_extension(entry, choice_HDB_extension_data_last_pw_change);
255178825Sdfr    if (ext)
256178825Sdfr	*t = ext->data.u.last_pw_change;
257178825Sdfr    else
258178825Sdfr	*t = 0;
259178825Sdfr
260178825Sdfr    return 0;
261178825Sdfr}
262178825Sdfr
263178825Sdfrkrb5_error_code
264233294Sstashdb_entry_set_pw_change_time(krb5_context context,
265178825Sdfr			     hdb_entry *entry,
266178825Sdfr			     time_t t)
267178825Sdfr{
268178825Sdfr    HDB_extension ext;
269178825Sdfr
270178825Sdfr    ext.mandatory = FALSE;
271178825Sdfr    ext.data.element = choice_HDB_extension_data_last_pw_change;
272178825Sdfr    if (t == 0)
273178825Sdfr	t = time(NULL);
274178825Sdfr    ext.data.u.last_pw_change = t;
275178825Sdfr
276178825Sdfr    return hdb_replace_extension(context, entry, &ext);
277178825Sdfr}
278178825Sdfr
279178825Sdfrint
280233294Sstashdb_entry_get_password(krb5_context context, HDB *db,
281178825Sdfr		       const hdb_entry *entry, char **p)
282178825Sdfr{
283178825Sdfr    HDB_extension *ext;
284178825Sdfr    char *str;
285178825Sdfr    int ret;
286178825Sdfr
287178825Sdfr    ext = hdb_find_extension(entry, choice_HDB_extension_data_password);
288178825Sdfr    if (ext) {
289233294Sstas	heim_utf8_string xstr;
290178825Sdfr	heim_octet_string pw;
291178825Sdfr
292178825Sdfr	if (db->hdb_master_key_set && ext->data.u.password.mkvno) {
293178825Sdfr	    hdb_master_key key;
294178825Sdfr
295233294Sstas	    key = _hdb_find_master_key(ext->data.u.password.mkvno,
296178825Sdfr				       db->hdb_master_key);
297178825Sdfr
298178825Sdfr	    if (key == NULL) {
299233294Sstas		krb5_set_error_message(context, HDB_ERR_NO_MKEY,
300233294Sstas				       "master key %d missing",
301233294Sstas				       *ext->data.u.password.mkvno);
302178825Sdfr		return HDB_ERR_NO_MKEY;
303178825Sdfr	    }
304178825Sdfr
305178825Sdfr	    ret = _hdb_mkey_decrypt(context, key, HDB_KU_MKEY,
306178825Sdfr				    ext->data.u.password.password.data,
307178825Sdfr				    ext->data.u.password.password.length,
308178825Sdfr				    &pw);
309178825Sdfr	} else {
310178825Sdfr	    ret = der_copy_octet_string(&ext->data.u.password.password, &pw);
311178825Sdfr	}
312178825Sdfr	if (ret) {
313233294Sstas	    krb5_clear_error_message(context);
314178825Sdfr	    return ret;
315178825Sdfr	}
316178825Sdfr
317233294Sstas	xstr = pw.data;
318233294Sstas	if (xstr[pw.length - 1] != '\0') {
319233294Sstas	    krb5_set_error_message(context, EINVAL, "malformed password");
320178825Sdfr	    return EINVAL;
321178825Sdfr	}
322178825Sdfr
323233294Sstas	*p = strdup(xstr);
324178825Sdfr
325178825Sdfr	der_free_octet_string(&pw);
326178825Sdfr	if (*p == NULL) {
327233294Sstas	    krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
328178825Sdfr	    return ENOMEM;
329178825Sdfr	}
330178825Sdfr	return 0;
331178825Sdfr    }
332178825Sdfr
333178825Sdfr    ret = krb5_unparse_name(context, entry->principal, &str);
334178825Sdfr    if (ret == 0) {
335233294Sstas	krb5_set_error_message(context, ENOENT,
336233294Sstas			       "no password attribute for %s", str);
337178825Sdfr	free(str);
338233294Sstas    } else
339233294Sstas	krb5_clear_error_message(context);
340178825Sdfr
341178825Sdfr    return ENOENT;
342178825Sdfr}
343178825Sdfr
344178825Sdfrint
345233294Sstashdb_entry_set_password(krb5_context context, HDB *db,
346178825Sdfr		       hdb_entry *entry, const char *p)
347178825Sdfr{
348178825Sdfr    HDB_extension ext;
349178825Sdfr    hdb_master_key key;
350178825Sdfr    int ret;
351178825Sdfr
352178825Sdfr    ext.mandatory = FALSE;
353178825Sdfr    ext.data.element = choice_HDB_extension_data_password;
354178825Sdfr
355178825Sdfr    if (db->hdb_master_key_set) {
356178825Sdfr
357178825Sdfr	key = _hdb_find_master_key(NULL, db->hdb_master_key);
358178825Sdfr	if (key == NULL) {
359233294Sstas	    krb5_set_error_message(context, HDB_ERR_NO_MKEY,
360233294Sstas				   "hdb_entry_set_password: "
361233294Sstas				   "failed to find masterkey");
362178825Sdfr	    return HDB_ERR_NO_MKEY;
363178825Sdfr	}
364178825Sdfr
365178825Sdfr	ret = _hdb_mkey_encrypt(context, key, HDB_KU_MKEY,
366233294Sstas				p, strlen(p) + 1,
367178825Sdfr				&ext.data.u.password.password);
368178825Sdfr	if (ret)
369178825Sdfr	    return ret;
370178825Sdfr
371233294Sstas	ext.data.u.password.mkvno =
372178825Sdfr	    malloc(sizeof(*ext.data.u.password.mkvno));
373178825Sdfr	if (ext.data.u.password.mkvno == NULL) {
374178825Sdfr	    free_HDB_extension(&ext);
375233294Sstas	    krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
376178825Sdfr	    return ENOMEM;
377178825Sdfr	}
378178825Sdfr	*ext.data.u.password.mkvno = _hdb_mkey_version(key);
379178825Sdfr
380178825Sdfr    } else {
381178825Sdfr	ext.data.u.password.mkvno = NULL;
382178825Sdfr
383233294Sstas	ret = krb5_data_copy(&ext.data.u.password.password,
384178825Sdfr			     p, strlen(p) + 1);
385178825Sdfr	if (ret) {
386233294Sstas	    krb5_set_error_message(context, ret, "malloc: out of memory");
387178825Sdfr	    free_HDB_extension(&ext);
388178825Sdfr	    return ret;
389178825Sdfr	}
390178825Sdfr    }
391178825Sdfr
392178825Sdfr    ret = hdb_replace_extension(context, entry, &ext);
393178825Sdfr
394178825Sdfr    free_HDB_extension(&ext);
395178825Sdfr
396178825Sdfr    return ret;
397178825Sdfr}
398178825Sdfr
399178825Sdfrint
400178825Sdfrhdb_entry_clear_password(krb5_context context, hdb_entry *entry)
401178825Sdfr{
402233294Sstas    return hdb_clear_extension(context, entry,
403178825Sdfr			       choice_HDB_extension_data_password);
404178825Sdfr}
405178825Sdfr
406178825Sdfrkrb5_error_code
407233294Sstashdb_entry_get_ConstrainedDelegACL(const hdb_entry *entry,
408178825Sdfr				  const HDB_Ext_Constrained_delegation_acl **a)
409178825Sdfr{
410178825Sdfr    const HDB_extension *ext;
411178825Sdfr
412233294Sstas    ext = hdb_find_extension(entry,
413178825Sdfr			     choice_HDB_extension_data_allowed_to_delegate_to);
414178825Sdfr    if (ext)
415178825Sdfr	*a = &ext->data.u.allowed_to_delegate_to;
416178825Sdfr    else
417178825Sdfr	*a = NULL;
418178825Sdfr
419178825Sdfr    return 0;
420178825Sdfr}
421178825Sdfr
422178825Sdfrkrb5_error_code
423178825Sdfrhdb_entry_get_aliases(const hdb_entry *entry, const HDB_Ext_Aliases **a)
424178825Sdfr{
425178825Sdfr    const HDB_extension *ext;
426178825Sdfr
427178825Sdfr    ext = hdb_find_extension(entry, choice_HDB_extension_data_aliases);
428178825Sdfr    if (ext)
429178825Sdfr	*a = &ext->data.u.aliases;
430178825Sdfr    else
431178825Sdfr	*a = NULL;
432178825Sdfr
433178825Sdfr    return 0;
434178825Sdfr}
435