HEIMDAL: move code from source4/heimdal* to third_party/heimdal*
[samba.git] / third_party / heimdal / lib / kadm5 / get_s.c
1 /*
2  * Copyright (c) 1997 - 2006 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 <krb5_locl.h>
35 #include "kadm5_locl.h"
36 #include <assert.h>
37
38 RCSID("$Id$");
39
40 static kadm5_ret_t
41 add_tl_data(kadm5_principal_ent_t ent, int16_t type,
42             const void *data, size_t size)
43 {
44     krb5_tl_data *tl;
45
46     tl = calloc(1, sizeof(*tl));
47     if (tl == NULL)
48         return _kadm5_error_code(ENOMEM);
49
50     tl->tl_data_type = type;
51     tl->tl_data_length = size;
52     tl->tl_data_contents = malloc(size);
53     if (tl->tl_data_contents == NULL && size != 0) {
54         free(tl);
55         return _kadm5_error_code(ENOMEM);
56     }
57     memcpy(tl->tl_data_contents, data, size);
58
59     tl->tl_data_next = ent->tl_data;
60     ent->tl_data = tl;
61     ent->n_tl_data++;
62
63     return 0;
64 }
65
66 static
67 krb5_error_code
68 copy_keyset_to_kadm5(kadm5_server_context *context, krb5_kvno kvno,
69                      size_t n_keys, Key *keys, krb5_salt *salt,
70                      kadm5_principal_ent_t out)
71 {
72     size_t i;
73     Key *key;
74     krb5_key_data *kd;
75     krb5_data *sp;
76     krb5_error_code ret = 0;
77
78     for (i = 0; i < n_keys; i++) {
79         key = &keys[i];
80         kd = &out->key_data[out->n_key_data];
81         kd->key_data_ver = 2;
82         kd->key_data_kvno = kvno;
83         kd->key_data_type[0] = key->key.keytype;
84         if(key->salt)
85             kd->key_data_type[1] = key->salt->type;
86         else
87             kd->key_data_type[1] = KRB5_PADATA_PW_SALT;
88         /* setup key */
89         kd->key_data_length[0] = key->key.keyvalue.length;
90         kd->key_data_contents[0] = malloc(kd->key_data_length[0]);
91         if(kd->key_data_contents[0] == NULL && kd->key_data_length[0] != 0){
92             ret = krb5_enomem(context->context);
93             break;
94         }
95         memcpy(kd->key_data_contents[0], key->key.keyvalue.data,
96                kd->key_data_length[0]);
97         /* setup salt */
98         if(key->salt)
99             sp = &key->salt->salt;
100         else
101             sp = &salt->saltvalue;
102         kd->key_data_length[1] = sp->length;
103         kd->key_data_contents[1] = malloc(kd->key_data_length[1]);
104         if(kd->key_data_length[1] != 0
105            && kd->key_data_contents[1] == NULL) {
106             memset(kd->key_data_contents[0], 0, kd->key_data_length[0]);
107             ret = krb5_enomem(context->context);
108             break;
109         }
110         memcpy(kd->key_data_contents[1], sp->data, kd->key_data_length[1]);
111         out->n_key_data++;
112     }
113
114     return ret;
115 }
116
117 kadm5_ret_t
118 kadm5_s_get_principal(void *server_handle,
119                       krb5_principal princ,
120                       kadm5_principal_ent_t out,
121                       uint32_t mask)
122 {
123     kadm5_server_context *context = server_handle;
124     kadm5_ret_t ret;
125     hdb_entry_ex ent;
126     unsigned int flags = HDB_F_GET_ANY | HDB_F_ADMIN_DATA;
127
128     if ((mask & KADM5_KEY_DATA) || (mask & KADM5_KVNO))
129         flags |= HDB_F_ALL_KVNOS | HDB_F_DECRYPT;
130
131     memset(&ent, 0, sizeof(ent));
132     memset(out, 0, sizeof(*out));
133
134     if (!context->keep_open) {
135         ret = context->db->hdb_open(context->context, context->db, O_RDONLY, 0);
136         if (ret)
137             return ret;
138     }
139
140     /*
141      * We may want to attempt to recover the log on read operations, but we
142      * because the HDB/log lock order is reversed on slaves, in order to avoid
143      * lock contention from kadm5srv apps we need to make sure that the the HDB
144      * open for read-write is optimistic and attempts only a non-blocking lock,
145      * and if it doesn't get it then it should fallback to read-only.  But we
146      * don't have that option in the hdb_open() interface at this time.
147      *
148      * For now we won't attempt to recover the log.
149      */
150
151     ret = hdb_fetch_kvno(context->context, context->db, princ, flags,
152                          0 /*timestamp*/, 0/*etype*/, 0/*kvno*/, &ent);
153
154     if (!context->keep_open)
155         context->db->hdb_close(context->context, context->db);
156     if(ret)
157         return _kadm5_error_code(ret);
158
159     if(mask & KADM5_PRINCIPAL)
160         ret  = krb5_copy_principal(context->context, ent.entry.principal,
161                                    &out->principal);
162     if(ret)
163         goto out;
164     if(mask & KADM5_PRINC_EXPIRE_TIME && ent.entry.valid_end)
165         out->princ_expire_time = *ent.entry.valid_end;
166     if(mask & KADM5_PW_EXPIRATION && ent.entry.pw_end)
167         out->pw_expiration = *ent.entry.pw_end;
168     if(mask & KADM5_LAST_PWD_CHANGE)
169         hdb_entry_get_pw_change_time(&ent.entry, &out->last_pwd_change);
170     if(mask & KADM5_ATTRIBUTES){
171         out->attributes |= ent.entry.flags.postdate ? 0 : KRB5_KDB_DISALLOW_POSTDATED;
172         out->attributes |= ent.entry.flags.forwardable ? 0 : KRB5_KDB_DISALLOW_FORWARDABLE;
173         out->attributes |= ent.entry.flags.initial ? KRB5_KDB_DISALLOW_TGT_BASED : 0;
174         out->attributes |= ent.entry.flags.renewable ? 0 : KRB5_KDB_DISALLOW_RENEWABLE;
175         out->attributes |= ent.entry.flags.proxiable ? 0 : KRB5_KDB_DISALLOW_PROXIABLE;
176         out->attributes |= ent.entry.flags.invalid ? KRB5_KDB_DISALLOW_ALL_TIX : 0;
177         out->attributes |= ent.entry.flags.require_preauth ? KRB5_KDB_REQUIRES_PRE_AUTH : 0;
178         out->attributes |= ent.entry.flags.require_pwchange ? KRB5_KDB_REQUIRES_PWCHANGE : 0;
179         out->attributes |= ent.entry.flags.client ? 0 : KRB5_KDB_DISALLOW_CLIENT;
180         out->attributes |= ent.entry.flags.server ? 0 : KRB5_KDB_DISALLOW_SVR;
181         out->attributes |= ent.entry.flags.change_pw ? KRB5_KDB_PWCHANGE_SERVICE : 0;
182         out->attributes |= ent.entry.flags.ok_as_delegate ? KRB5_KDB_OK_AS_DELEGATE : 0;
183         out->attributes |= ent.entry.flags.trusted_for_delegation ? KRB5_KDB_TRUSTED_FOR_DELEGATION : 0;
184         out->attributes |= ent.entry.flags.allow_kerberos4 ? KRB5_KDB_ALLOW_KERBEROS4 : 0;
185         out->attributes |= ent.entry.flags.allow_digest ? KRB5_KDB_ALLOW_DIGEST : 0;
186         out->attributes |= ent.entry.flags.virtual_keys ? KRB5_KDB_VIRTUAL_KEYS : 0;
187         out->attributes |= ent.entry.flags.virtual ? KRB5_KDB_VIRTUAL : 0;
188         out->attributes |= ent.entry.flags.no_auth_data_reqd ? KRB5_KDB_NO_AUTH_DATA_REQUIRED : 0;
189     }
190     if(mask & KADM5_MAX_LIFE) {
191         if(ent.entry.max_life)
192             out->max_life = *ent.entry.max_life;
193         else
194             out->max_life = INT_MAX;
195     }
196     if(mask & KADM5_MOD_TIME) {
197         if(ent.entry.modified_by)
198             out->mod_date = ent.entry.modified_by->time;
199         else
200             out->mod_date = ent.entry.created_by.time;
201     }
202     if(mask & KADM5_MOD_NAME) {
203         if(ent.entry.modified_by) {
204             if (ent.entry.modified_by->principal != NULL)
205                 ret = krb5_copy_principal(context->context,
206                                           ent.entry.modified_by->principal,
207                                           &out->mod_name);
208         } else if(ent.entry.created_by.principal != NULL)
209             ret = krb5_copy_principal(context->context,
210                                       ent.entry.created_by.principal,
211                                       &out->mod_name);
212         else
213             out->mod_name = NULL;
214     }
215     if(ret)
216         goto out;
217
218     if(mask & KADM5_KVNO)
219         out->kvno = ent.entry.kvno;
220     if(mask & KADM5_MKVNO) {
221         size_t n;
222         out->mkvno = 0; /* XXX */
223         for(n = 0; n < ent.entry.keys.len; n++)
224             if(ent.entry.keys.val[n].mkvno) {
225                 out->mkvno = *ent.entry.keys.val[n].mkvno; /* XXX this isn't right */
226                 break;
227             }
228     }
229 #if 0 /* XXX implement */
230     if(mask & KADM5_AUX_ATTRIBUTES)
231         ;
232     if(mask & KADM5_LAST_SUCCESS)
233         ;
234     if(mask & KADM5_LAST_FAILED)
235         ;
236     if(mask & KADM5_FAIL_AUTH_COUNT)
237         ;
238 #endif
239     if(mask & KADM5_POLICY) {
240         HDB_extension *ext;
241
242         ext = hdb_find_extension(&ent.entry, choice_HDB_extension_data_policy);
243         if (ext == NULL) {
244             out->policy = strdup("default");
245             /* It's OK if we retun NULL instead of "default" */
246         } else {
247             out->policy = strdup(ext->data.u.policy);
248             if (out->policy == NULL) {
249                 ret = krb5_enomem(context->context);
250                 goto out;
251             }
252         }
253     }
254     if(mask & KADM5_MAX_RLIFE) {
255         if(ent.entry.max_renew)
256             out->max_renewable_life = *ent.entry.max_renew;
257         else
258             out->max_renewable_life = INT_MAX;
259     }
260     if(mask & KADM5_KEY_DATA){
261         size_t i;
262         size_t n_keys = ent.entry.keys.len;
263         krb5_salt salt;
264         HDB_extension *ext;
265         HDB_Ext_KeySet *hist_keys = NULL;
266
267         /* Don't return stale keys to kadm5 clients */
268         ret = hdb_prune_keys(context->context, &ent.entry);
269         if (ret)
270             goto out;
271         ext = hdb_find_extension(&ent.entry, choice_HDB_extension_data_hist_keys);
272         if (ext != NULL)
273             hist_keys = &ext->data.u.hist_keys;
274
275         krb5_get_pw_salt(context->context, ent.entry.principal, &salt);
276         for (i = 0; hist_keys != NULL && i < hist_keys->len; i++)
277             n_keys += hist_keys->val[i].keys.len;
278         out->key_data = malloc(n_keys * sizeof(*out->key_data));
279         if (out->key_data == NULL && n_keys != 0) {
280             ret = krb5_enomem(context->context);
281             goto out;
282         }
283         out->n_key_data = 0;
284         ret = copy_keyset_to_kadm5(context, ent.entry.kvno, ent.entry.keys.len,
285                                    ent.entry.keys.val, &salt, out);
286         if (ret)
287             goto out;
288         for (i = 0; hist_keys != NULL && i < hist_keys->len; i++) {
289             ret = copy_keyset_to_kadm5(context, hist_keys->val[i].kvno,
290                                        hist_keys->val[i].keys.len,
291                                        hist_keys->val[i].keys.val,
292                                        &salt, out);
293             if (ret)
294                 goto out;
295         }
296         krb5_free_salt(context->context, salt);
297         assert( out->n_key_data == n_keys );
298     }
299     if (ret)
300         goto out;
301     if(mask & KADM5_TL_DATA) {
302         time_t last_pw_expire;
303         const HDB_Ext_PKINIT_acl *acl;
304         const HDB_Ext_Aliases *aliases;
305         const HDB_Ext_KeyRotation *kr;
306         heim_octet_string krb5_config;
307
308         if (ent.entry.etypes) {
309             krb5_data buf;
310             size_t len;
311
312             ASN1_MALLOC_ENCODE(HDB_EncTypeList, buf.data, buf.length,
313                                ent.entry.etypes, &len, ret);
314             if (ret == 0) {
315                 ret = add_tl_data(out, KRB5_TL_ETYPES, buf.data, buf.length);
316                 free(buf.data);
317             }
318             if (ret)
319                 goto out;
320         }
321
322         ret = hdb_entry_get_pw_change_time(&ent.entry, &last_pw_expire);
323         if (ret == 0 && last_pw_expire) {
324             unsigned char buf[4];
325             _krb5_put_int(buf, last_pw_expire, sizeof(buf));
326             ret = add_tl_data(out, KRB5_TL_LAST_PWD_CHANGE, buf, sizeof(buf));
327         }
328         if (ret == 0)
329             ret = hdb_entry_get_krb5_config(&ent.entry, &krb5_config);
330         if (ret == 0 && krb5_config.length) {
331             ret = add_tl_data(out, KRB5_TL_KRB5_CONFIG, krb5_config.data,
332                               krb5_config.length);
333         }
334         if (ret)
335             goto out;
336         /*
337          * If the client was allowed to get key data, let it have the
338          * password too.
339          */
340         if (mask & KADM5_KEY_DATA) {
341             heim_utf8_string pw;
342
343             /* XXX But not if the client doesn't have ext-keys */
344             ret = hdb_entry_get_password(context->context,
345                                          context->db, &ent.entry, &pw);
346             if (ret == 0) {
347                 ret = add_tl_data(out, KRB5_TL_PASSWORD, pw, strlen(pw) + 1);
348                 free(pw);
349             }
350             krb5_clear_error_message(context->context);
351         }
352
353         ret = hdb_entry_get_pkinit_acl(&ent.entry, &acl);
354         if (ret == 0 && acl) {
355             krb5_data buf;
356             size_t len;
357
358             ASN1_MALLOC_ENCODE(HDB_Ext_PKINIT_acl, buf.data, buf.length,
359                                 acl, &len, ret);
360             if (ret)
361                 goto out;
362             if (len != buf.length)
363                 krb5_abortx(context->context,
364                             "internal ASN.1 encoder error");
365             ret = add_tl_data(out, KRB5_TL_PKINIT_ACL, buf.data, buf.length);
366             free(buf.data);
367             if (ret)
368                 goto out;
369         }
370         if (ret)
371             goto out;
372
373         ret = hdb_entry_get_aliases(&ent.entry, &aliases);
374         if (ret == 0 && aliases) {
375             krb5_data buf;
376             size_t len;
377
378             ASN1_MALLOC_ENCODE(HDB_Ext_Aliases, buf.data, buf.length,
379                                aliases, &len, ret);
380             if (ret)
381                 goto out;
382             if (len != buf.length)
383                 krb5_abortx(context->context,
384                             "internal ASN.1 encoder error");
385             ret = add_tl_data(out, KRB5_TL_ALIASES, buf.data, buf.length);
386             free(buf.data);
387             if (ret)
388                 goto out;
389         }
390         if (ret)
391             goto out;
392
393         ret = hdb_entry_get_key_rotation(context->context, &ent.entry, &kr);
394         if (ret == 0 && kr) {
395             krb5_data buf;
396             size_t len;
397
398             ASN1_MALLOC_ENCODE(HDB_Ext_KeyRotation, buf.data, buf.length,
399                                kr, &len, ret);
400             if (ret)
401                 goto out;
402             if (len != buf.length)
403                 krb5_abortx(context->context,
404                             "internal ASN.1 encoder error");
405             ret = add_tl_data(out, KRB5_TL_KEY_ROTATION, buf.data, buf.length);
406             free(buf.data);
407             if (ret)
408                 goto out;
409         }
410         if (ret)
411             goto out;
412     }
413
414  out:
415     if (ret)
416         kadm5_free_principal_ent(context, out);
417     hdb_free_entry(context->context, &ent);
418
419     return _kadm5_error_code(ret);
420 }