HEIMDAL: move code from source4/heimdal* to third_party/heimdal*
[samba.git] / third_party / heimdal / lib / gssapi / krb5 / set_sec_context_option.c
1 /*
2  * Copyright (c) 2004, PADL Software Pty Ltd.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  *
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  *
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * 3. Neither the name of PADL Software nor the names of its contributors
17  *    may be used to endorse or promote products derived from this software
18  *    without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY PADL SOFTWARE AND CONTRIBUTORS ``AS IS'' AND
21  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23  * ARE DISCLAIMED.  IN NO EVENT SHALL PADL SOFTWARE OR CONTRIBUTORS BE LIABLE
24  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30  * SUCH DAMAGE.
31  */
32
33 /*
34  *  glue routine for _gsskrb5_inquire_sec_context_by_oid
35  */
36
37 #include "gsskrb5_locl.h"
38
39 static OM_uint32
40 get_bool(OM_uint32 *minor_status,
41          const gss_buffer_t value,
42          int *flag)
43 {
44     if (value->value == NULL || value->length != 1) {
45         *minor_status = EINVAL;
46         return GSS_S_FAILURE;
47     }
48     *flag = *((const char *)value->value) != 0;
49     return GSS_S_COMPLETE;
50 }
51
52 static OM_uint32
53 get_string(OM_uint32 *minor_status,
54            const gss_buffer_t value,
55            char **str)
56 {
57     if (value == NULL || value->length == 0) {
58         *str = NULL;
59     } else {
60         *str = malloc(value->length + 1);
61         if (*str == NULL) {
62             *minor_status = 0;
63             return GSS_S_UNAVAILABLE;
64         }
65         memcpy(*str, value->value, value->length);
66         (*str)[value->length] = '\0';
67     }
68     return GSS_S_COMPLETE;
69 }
70
71 static OM_uint32
72 get_int32(OM_uint32 *minor_status,
73           const gss_buffer_t value,
74           OM_uint32 *ret)
75 {
76     *minor_status = 0;
77     if (value == NULL || value->length == 0)
78         *ret = 0;
79     else if (value->length == sizeof(*ret))
80         memcpy(ret, value->value, sizeof(*ret));
81     else
82         return GSS_S_UNAVAILABLE;
83
84     return GSS_S_COMPLETE;
85 }
86
87 static OM_uint32
88 set_int32(OM_uint32 *minor_status,
89           const gss_buffer_t value,
90           OM_uint32 set)
91 {
92     *minor_status = 0;
93     if (value->length == sizeof(set))
94         memcpy(value->value, &set, sizeof(set));
95     else
96         return GSS_S_UNAVAILABLE;
97
98     return GSS_S_COMPLETE;
99 }
100
101 /*
102  * GSS_KRB5_IMPORT_RFC4121_CONTEXT_X is an internal, private interface
103  * to allow SAnon to create a skeletal context for using RFC4121 message
104  * protection services.
105  *
106  * rfc4121_args ::= initiator_flag || flags || enctype || session key
107  */
108 static OM_uint32
109 make_rfc4121_context(OM_uint32 *minor,
110                      krb5_context context,
111                      gss_ctx_id_t *context_handle,
112                      gss_const_buffer_t rfc4121_args)
113 {
114     OM_uint32 major = GSS_S_FAILURE, tmp;
115     krb5_error_code ret;
116     krb5_storage *sp = NULL;
117     gsskrb5_ctx ctx = NULL;
118     uint8_t initiator_flag;
119     int32_t enctype;
120     size_t keysize;
121     krb5_keyblock *key;
122
123     *minor = 0;
124
125     sp = krb5_storage_from_readonly_mem(rfc4121_args->value, rfc4121_args->length);
126     if (sp == NULL) {
127         ret = ENOMEM;
128         goto out;
129     }
130
131     krb5_storage_set_byteorder(sp, KRB5_STORAGE_BYTEORDER_HOST);
132
133     ctx = calloc(1, sizeof(*ctx));
134     if (ctx == NULL) {
135         ret = ENOMEM;
136         goto out;
137     }
138
139     ret = krb5_ret_uint8(sp, &initiator_flag);
140     if (ret != 0)
141         goto out;
142
143     ret = krb5_ret_uint32(sp, &ctx->flags);
144     if (ret != 0)
145         goto out;
146
147     ctx->more_flags = IS_CFX | ACCEPTOR_SUBKEY | OPEN;
148     if (initiator_flag)
149         ctx->more_flags |= LOCAL;
150
151     ctx->state = initiator_flag ? INITIATOR_READY : ACCEPTOR_READY;
152
153     ret = krb5_ret_int32(sp, &enctype);
154     if (ret != 0)
155         goto out;
156
157     ret = krb5_enctype_keysize(context, enctype, &keysize);
158     if (ret != 0)
159         goto out;
160
161     ctx->auth_context = calloc(1, sizeof(*ctx->auth_context));
162     if (ctx->auth_context == NULL) {
163         ret = ENOMEM;
164         goto out;
165     }
166
167     key = calloc(1, sizeof(*key));
168     if (key == NULL) {
169         ret = ENOMEM;
170         goto out;
171     }
172     if (initiator_flag)
173         ctx->auth_context->remote_subkey = key;
174     else
175         ctx->auth_context->local_subkey = key;
176
177     key->keytype = enctype;
178     key->keyvalue.data = malloc(keysize);
179     if (key->keyvalue.data == NULL) {
180         ret = ENOMEM;
181         goto out;
182     }
183
184     if (krb5_storage_read(sp, key->keyvalue.data, keysize) != keysize) {
185         ret = EINVAL;
186         goto out;
187     }
188     key->keyvalue.length = keysize;
189
190     ret = krb5_crypto_init(context, key, 0, &ctx->crypto);
191     if (ret != 0)
192         goto out;
193
194     major = _gssapi_msg_order_create(minor, &ctx->order,
195                                      _gssapi_msg_order_f(ctx->flags),
196                                      0, 0, 1);
197     if (major != GSS_S_COMPLETE)
198         goto out;
199
200 out:
201     krb5_storage_free(sp);
202
203     if (major != GSS_S_COMPLETE) {
204         if (*minor == 0)
205             *minor = ret;
206         _gsskrb5_delete_sec_context(&tmp, (gss_ctx_id_t *)&ctx, GSS_C_NO_BUFFER);
207     } else {
208         *context_handle = (gss_ctx_id_t)ctx;
209     }
210
211     return major;
212 }
213
214 OM_uint32 GSSAPI_CALLCONV
215 _gsskrb5_set_sec_context_option
216            (OM_uint32 *minor_status,
217             gss_ctx_id_t *context_handle,
218             const gss_OID desired_object,
219             const gss_buffer_t value)
220 {
221     krb5_context context;
222     OM_uint32 maj_stat;
223
224     GSSAPI_KRB5_INIT (&context);
225
226     if (value == GSS_C_NO_BUFFER) {
227         *minor_status = EINVAL;
228         return GSS_S_FAILURE;
229     }
230
231     if (gss_oid_equal(desired_object, GSS_KRB5_COMPAT_DES3_MIC_X)) {
232         gsskrb5_ctx ctx;
233         int flag;
234
235         if (*context_handle == GSS_C_NO_CONTEXT) {
236             *minor_status = EINVAL;
237             return GSS_S_NO_CONTEXT;
238         }
239
240         maj_stat = get_bool(minor_status, value, &flag);
241         if (maj_stat != GSS_S_COMPLETE)
242             return maj_stat;
243
244         ctx = (gsskrb5_ctx)*context_handle;
245         HEIMDAL_MUTEX_lock(&ctx->ctx_id_mutex);
246         if (flag)
247             ctx->more_flags |= COMPAT_OLD_DES3;
248         else
249             ctx->more_flags &= ~COMPAT_OLD_DES3;
250         ctx->more_flags |= COMPAT_OLD_DES3_SELECTED;
251         HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex);
252         return GSS_S_COMPLETE;
253     } else if (gss_oid_equal(desired_object, GSS_KRB5_SET_DNS_CANONICALIZE_X)) {
254         int flag;
255
256         maj_stat = get_bool(minor_status, value, &flag);
257         if (maj_stat != GSS_S_COMPLETE)
258             return maj_stat;
259
260         krb5_set_dns_canonicalize_hostname(context, flag);
261         return GSS_S_COMPLETE;
262
263     } else if (gss_oid_equal(desired_object, GSS_KRB5_REGISTER_ACCEPTOR_IDENTITY_X)) {
264         char *str;
265
266         maj_stat = get_string(minor_status, value, &str);
267         if (maj_stat != GSS_S_COMPLETE)
268             return maj_stat;
269
270         maj_stat = _gsskrb5_register_acceptor_identity(minor_status, str);
271         free(str);
272
273         return maj_stat;
274
275     } else if (gss_oid_equal(desired_object, GSS_KRB5_SET_DEFAULT_REALM_X)) {
276         char *str;
277
278         maj_stat = get_string(minor_status, value, &str);
279         if (maj_stat != GSS_S_COMPLETE)
280             return maj_stat;
281         if (str == NULL) {
282             *minor_status = 0;
283             return GSS_S_CALL_INACCESSIBLE_READ;
284         }
285
286         krb5_set_default_realm(context, str);
287         free(str);
288
289         *minor_status = 0;
290         return GSS_S_COMPLETE;
291
292     } else if (gss_oid_equal(desired_object, GSS_KRB5_SEND_TO_KDC_X)) {
293
294         *minor_status = EINVAL;
295         return GSS_S_FAILURE;
296
297     } else if (gss_oid_equal(desired_object, GSS_KRB5_SET_TIME_OFFSET_X)) {
298         OM_uint32 offset;
299         time_t t;
300
301         maj_stat = get_int32(minor_status, value, &offset);
302         if (maj_stat != GSS_S_COMPLETE)
303             return maj_stat;
304
305         t = time(NULL) + offset;
306
307         krb5_set_real_time(context, t, 0);
308
309         *minor_status = 0;
310         return GSS_S_COMPLETE;
311     } else if (gss_oid_equal(desired_object, GSS_KRB5_GET_TIME_OFFSET_X)) {
312         krb5_timestamp sec;
313         int32_t usec;
314         time_t t;
315
316         t = time(NULL);
317
318         krb5_us_timeofday (context, &sec, &usec);
319
320         maj_stat = set_int32(minor_status, value, sec - t);
321         if (maj_stat != GSS_S_COMPLETE)
322             return maj_stat;
323
324         *minor_status = 0;
325         return GSS_S_COMPLETE;
326     } else if (gss_oid_equal(desired_object, GSS_KRB5_PLUGIN_REGISTER_X)) {
327         struct gsskrb5_krb5_plugin c;
328
329         if (value->length != sizeof(c)) {
330             *minor_status = EINVAL;
331             return GSS_S_FAILURE;
332         }
333         memcpy(&c, value->value, sizeof(c));
334         krb5_plugin_register(context, c.type, c.name, c.symbol);
335
336         *minor_status = 0;
337         return GSS_S_COMPLETE;
338     } else if (gss_oid_equal(desired_object, GSS_KRB5_CCACHE_NAME_X)) {
339         struct gsskrb5_ccache_name_args *args = value->value;
340
341         if (value->length != sizeof(*args)) {
342             *minor_status = EINVAL;
343             return GSS_S_FAILURE;
344         }
345
346         return _gsskrb5_krb5_ccache_name(minor_status, args->name, &args->out_name);
347     } else if (gss_oid_equal(desired_object, GSS_KRB5_IMPORT_RFC4121_CONTEXT_X)) {
348         return make_rfc4121_context(minor_status, context, context_handle, value);
349     }
350
351     *minor_status = EINVAL;
352     return GSS_S_FAILURE;
353 }