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 OM_uint32 GSSAPI_CALLCONV
102 _gsskrb5_set_sec_context_option
103            (OM_uint32 *minor_status,
104             gss_ctx_id_t *context_handle,
105             const gss_OID desired_object,
106             const gss_buffer_t value)
107 {
108     krb5_context context;
109     OM_uint32 maj_stat;
110 
111     GSSAPI_KRB5_INIT (&context);
112 
113     if (value == GSS_C_NO_BUFFER) {
114 	*minor_status = EINVAL;
115 	return GSS_S_FAILURE;
116     }
117 
118     if (gss_oid_equal(desired_object, GSS_KRB5_COMPAT_DES3_MIC_X)) {
119 	gsskrb5_ctx ctx;
120 	int flag;
121 
122 	if (*context_handle == GSS_C_NO_CONTEXT) {
123 	    *minor_status = EINVAL;
124 	    return GSS_S_NO_CONTEXT;
125 	}
126 
127 	maj_stat = get_bool(minor_status, value, &flag);
128 	if (maj_stat != GSS_S_COMPLETE)
129 	    return maj_stat;
130 
131 	ctx = (gsskrb5_ctx)*context_handle;
132 	HEIMDAL_MUTEX_lock(&ctx->ctx_id_mutex);
133 	if (flag)
134 	    ctx->more_flags |= COMPAT_OLD_DES3;
135 	else
136 	    ctx->more_flags &= ~COMPAT_OLD_DES3;
137 	ctx->more_flags |= COMPAT_OLD_DES3_SELECTED;
138 	HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex);
139 	return GSS_S_COMPLETE;
140     } else if (gss_oid_equal(desired_object, GSS_KRB5_SET_DNS_CANONICALIZE_X)) {
141 	int flag;
142 
143 	maj_stat = get_bool(minor_status, value, &flag);
144 	if (maj_stat != GSS_S_COMPLETE)
145 	    return maj_stat;
146 
147 	krb5_set_dns_canonicalize_hostname(context, flag);
148 	return GSS_S_COMPLETE;
149 
150     } else if (gss_oid_equal(desired_object, GSS_KRB5_REGISTER_ACCEPTOR_IDENTITY_X)) {
151 	char *str;
152 
153 	maj_stat = get_string(minor_status, value, &str);
154 	if (maj_stat != GSS_S_COMPLETE)
155 	    return maj_stat;
156 
157 	maj_stat = _gsskrb5_register_acceptor_identity(minor_status, str);
158 	free(str);
159 
160 	return maj_stat;
161 
162     } else if (gss_oid_equal(desired_object, GSS_KRB5_SET_DEFAULT_REALM_X)) {
163 	char *str;
164 
165 	maj_stat = get_string(minor_status, value, &str);
166 	if (maj_stat != GSS_S_COMPLETE)
167 	    return maj_stat;
168 	if (str == NULL) {
169 	    *minor_status = 0;
170 	    return GSS_S_CALL_INACCESSIBLE_READ;
171 	}
172 
173 	krb5_set_default_realm(context, str);
174 	free(str);
175 
176 	*minor_status = 0;
177 	return GSS_S_COMPLETE;
178 
179     } else if (gss_oid_equal(desired_object, GSS_KRB5_SEND_TO_KDC_X)) {
180 
181 	if (value == NULL || value->length == 0) {
182 	    krb5_set_send_to_kdc_func(context, NULL, NULL);
183 	} else {
184 	    struct gsskrb5_send_to_kdc c;
185 
186 	    if (value->length != sizeof(c)) {
187 		*minor_status = EINVAL;
188 		return GSS_S_FAILURE;
189 	    }
190 	    memcpy(&c, value->value, sizeof(c));
191 	    krb5_set_send_to_kdc_func(context,
192 				      (krb5_send_to_kdc_func)c.func,
193 				      c.ptr);
194 	}
195 
196 	*minor_status = 0;
197 	return GSS_S_COMPLETE;
198     } else if (gss_oid_equal(desired_object, GSS_KRB5_CCACHE_NAME_X)) {
199 	char *str;
200 
201 	maj_stat = get_string(minor_status, value, &str);
202 	if (maj_stat != GSS_S_COMPLETE)
203 	    return maj_stat;
204 	if (str == NULL) {
205 	    *minor_status = 0;
206 	    return GSS_S_CALL_INACCESSIBLE_READ;
207 	}
208 
209 	*minor_status = krb5_cc_set_default_name(context, str);
210 	free(str);
211 	if (*minor_status)
212 	    return GSS_S_FAILURE;
213 
214 	return GSS_S_COMPLETE;
215     } else if (gss_oid_equal(desired_object, GSS_KRB5_SET_TIME_OFFSET_X)) {
216 	OM_uint32 offset;
217 	time_t t;
218 
219 	maj_stat = get_int32(minor_status, value, &offset);
220 	if (maj_stat != GSS_S_COMPLETE)
221 	    return maj_stat;
222 
223 	t = time(NULL) + offset;
224 
225 	krb5_set_real_time(context, t, 0);
226 
227 	*minor_status = 0;
228 	return GSS_S_COMPLETE;
229     } else if (gss_oid_equal(desired_object, GSS_KRB5_GET_TIME_OFFSET_X)) {
230 	krb5_timestamp sec;
231 	int32_t usec;
232 	time_t t;
233 
234 	t = time(NULL);
235 
236 	krb5_us_timeofday (context, &sec, &usec);
237 
238 	maj_stat = set_int32(minor_status, value, sec - t);
239 	if (maj_stat != GSS_S_COMPLETE)
240 	    return maj_stat;
241 
242 	*minor_status = 0;
243 	return GSS_S_COMPLETE;
244     } else if (gss_oid_equal(desired_object, GSS_KRB5_PLUGIN_REGISTER_X)) {
245 	struct gsskrb5_krb5_plugin c;
246 
247 	if (value->length != sizeof(c)) {
248 	    *minor_status = EINVAL;
249 	    return GSS_S_FAILURE;
250 	}
251 	memcpy(&c, value->value, sizeof(c));
252 	krb5_plugin_register(context, c.type, c.name, c.symbol);
253 
254 	*minor_status = 0;
255 	return GSS_S_COMPLETE;
256     }
257 
258     *minor_status = EINVAL;
259     return GSS_S_FAILURE;
260 }
261