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