1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /* plugins/gssapi/negoextest/main.c - GSS test module for NegoEx */
3 /*
4 * Copyright (C) 2019 by the Massachusetts Institute of Technology.
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 *
11 * * Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 *
14 * * Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in
16 * the documentation and/or other materials provided with the
17 * distribution.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
22 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
23 * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
24 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
25 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
26 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
28 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
30 * OF THE POSSIBILITY OF SUCH DAMAGE.
31 */
32
33 #include "k5-int.h"
34 #include <gssapi/gssapi.h>
35 #include <gssapi/gssapi_ext.h>
36 #include <gssapi/gssapi_alloc.h>
37
38 struct test_context {
39 int initiator;
40 uint8_t hops; /* hops remaining; 0 means established */
41 };
42
43 OM_uint32 KRB5_CALLCONV
gss_init_sec_context(OM_uint32 * minor_status,gss_cred_id_t claimant_cred_handle,gss_ctx_id_t * context_handle,gss_name_t target_name,gss_OID mech_type,OM_uint32 req_flags,OM_uint32 time_req,gss_channel_bindings_t input_chan_bindings,gss_buffer_t input_token,gss_OID * actual_mech,gss_buffer_t output_token,OM_uint32 * ret_flags,OM_uint32 * time_rec)44 gss_init_sec_context(OM_uint32 *minor_status,
45 gss_cred_id_t claimant_cred_handle,
46 gss_ctx_id_t *context_handle, gss_name_t target_name,
47 gss_OID mech_type, OM_uint32 req_flags,
48 OM_uint32 time_req,
49 gss_channel_bindings_t input_chan_bindings,
50 gss_buffer_t input_token, gss_OID *actual_mech,
51 gss_buffer_t output_token, OM_uint32 *ret_flags,
52 OM_uint32 *time_rec)
53 {
54 struct test_context *ctx = (struct test_context *)*context_handle;
55 OM_uint32 major;
56 gss_buffer_desc tok;
57 const char *envstr;
58 uint8_t hops, mech_last_octet;
59
60 envstr = getenv("GSS_INIT_BINDING");
61 if (envstr != NULL) {
62 assert(strlen(envstr) > 0);
63 assert(input_chan_bindings != GSS_C_NO_CHANNEL_BINDINGS);
64 assert(strlen(envstr) == input_chan_bindings->application_data.length);
65 assert(strcmp((char *)input_chan_bindings->application_data.value,
66 envstr) == 0);
67 }
68
69 if (input_token == GSS_C_NO_BUFFER || input_token->length == 0) {
70 envstr = getenv("HOPS");
71 hops = (envstr != NULL) ? atoi(envstr) : 1;
72 assert(hops > 0);
73 } else if (input_token->length == 4 &&
74 memcmp(input_token->value, "fail", 4) == 0) {
75 *minor_status = 12345;
76 return GSS_S_FAILURE;
77 } else {
78 hops = ((uint8_t *)input_token->value)[0];
79 }
80
81 mech_last_octet = ((uint8_t *)mech_type->elements)[mech_type->length - 1];
82 envstr = getenv("INIT_FAIL");
83 if (envstr != NULL && atoi(envstr) == mech_last_octet)
84 return GSS_S_FAILURE;
85
86 if (ctx == NULL) {
87 ctx = malloc(sizeof(*ctx));
88 assert(ctx != NULL);
89 ctx->initiator = 1;
90 ctx->hops = hops;
91 *context_handle = (gss_ctx_id_t)ctx;
92 } else if (ctx != NULL) {
93 assert(ctx->initiator);
94 ctx->hops--;
95 assert(ctx->hops == hops);
96 }
97
98 if (ctx->hops > 0) {
99 /* Generate a token containing the remaining hop count. */
100 ctx->hops--;
101 tok.value = &ctx->hops;
102 tok.length = 1;
103 major = gss_encapsulate_token(&tok, mech_type, output_token);
104 assert(major == GSS_S_COMPLETE);
105 }
106
107 return (ctx->hops > 0) ? GSS_S_CONTINUE_NEEDED : GSS_S_COMPLETE;
108 }
109
110 OM_uint32 KRB5_CALLCONV
gss_accept_sec_context(OM_uint32 * minor_status,gss_ctx_id_t * context_handle,gss_cred_id_t verifier_cred_handle,gss_buffer_t input_token,gss_channel_bindings_t input_chan_bindings,gss_name_t * src_name,gss_OID * mech_type,gss_buffer_t output_token,OM_uint32 * ret_flags,OM_uint32 * time_rec,gss_cred_id_t * delegated_cred_handle)111 gss_accept_sec_context(OM_uint32 *minor_status, gss_ctx_id_t *context_handle,
112 gss_cred_id_t verifier_cred_handle,
113 gss_buffer_t input_token,
114 gss_channel_bindings_t input_chan_bindings,
115 gss_name_t *src_name, gss_OID *mech_type,
116 gss_buffer_t output_token, OM_uint32 *ret_flags,
117 OM_uint32 *time_rec,
118 gss_cred_id_t *delegated_cred_handle)
119 {
120 struct test_context *ctx = (struct test_context *)*context_handle;
121 uint8_t hops, mech_last_octet;
122 const char *envstr;
123
124 envstr = getenv("GSS_ACCEPT_BINDING");
125 if (envstr != NULL) {
126 assert(strlen(envstr) > 0);
127 assert(input_chan_bindings != GSS_C_NO_CHANNEL_BINDINGS);
128 assert(strlen(envstr) == input_chan_bindings->application_data.length);
129 assert(strcmp((char *)input_chan_bindings->application_data.value,
130 envstr) == 0);
131 }
132
133 /*
134 * The unwrapped token sits at the end and is just one byte giving the
135 * remaining number of hops. The final octet of the mech encoding should
136 * be just prior to it.
137 */
138 assert(input_token->length >= 2);
139 hops = ((uint8_t *)input_token->value)[input_token->length - 1];
140 mech_last_octet = ((uint8_t *)input_token->value)[input_token->length - 2];
141
142 envstr = getenv("ACCEPT_FAIL");
143 if (envstr != NULL && atoi(envstr) == mech_last_octet) {
144 output_token->value = gssalloc_strdup("fail");
145 assert(output_token->value != NULL);
146 output_token->length = 4;
147 return GSS_S_FAILURE;
148 }
149
150 if (*context_handle == GSS_C_NO_CONTEXT) {
151 ctx = malloc(sizeof(*ctx));
152 assert(ctx != NULL);
153 ctx->initiator = 0;
154 ctx->hops = hops;
155 *context_handle = (gss_ctx_id_t)ctx;
156 } else {
157 assert(!ctx->initiator);
158 ctx->hops--;
159 assert(ctx->hops == hops);
160 }
161
162 if (ctx->hops > 0) {
163 /* Generate a token containing the remaining hop count. */
164 ctx->hops--;
165 output_token->value = gssalloc_malloc(1);
166 assert(output_token->value != NULL);
167 memcpy(output_token->value, &ctx->hops, 1);
168 output_token->length = 1;
169 }
170
171 return (ctx->hops > 0) ? GSS_S_CONTINUE_NEEDED : GSS_S_COMPLETE;
172 }
173
174 OM_uint32 KRB5_CALLCONV
gss_delete_sec_context(OM_uint32 * minor_status,gss_ctx_id_t * context_handle,gss_buffer_t output_token)175 gss_delete_sec_context(OM_uint32 *minor_status, gss_ctx_id_t *context_handle,
176 gss_buffer_t output_token)
177 {
178 free(*context_handle);
179 *context_handle = GSS_C_NO_CONTEXT;
180 return GSS_S_COMPLETE;
181 }
182
183 OM_uint32 KRB5_CALLCONV
gss_acquire_cred(OM_uint32 * minor_status,gss_name_t desired_name,OM_uint32 time_req,gss_OID_set desired_mechs,gss_cred_usage_t cred_usage,gss_cred_id_t * output_cred_handle,gss_OID_set * actual_mechs,OM_uint32 * time_rec)184 gss_acquire_cred(OM_uint32 *minor_status, gss_name_t desired_name,
185 OM_uint32 time_req, gss_OID_set desired_mechs,
186 gss_cred_usage_t cred_usage,
187 gss_cred_id_t *output_cred_handle, gss_OID_set *actual_mechs,
188 OM_uint32 *time_rec)
189 {
190 return GSS_S_COMPLETE;
191 }
192
193 OM_uint32 KRB5_CALLCONV
gss_acquire_cred_with_password(OM_uint32 * minor_status,const gss_name_t desired_name,const gss_buffer_t password,OM_uint32 time_req,const gss_OID_set desired_mechs,gss_cred_usage_t cred_usage,gss_cred_id_t * output_cred_handle,gss_OID_set * actual_mechs,OM_uint32 * time_rec)194 gss_acquire_cred_with_password(OM_uint32 *minor_status,
195 const gss_name_t desired_name,
196 const gss_buffer_t password, OM_uint32 time_req,
197 const gss_OID_set desired_mechs,
198 gss_cred_usage_t cred_usage,
199 gss_cred_id_t *output_cred_handle,
200 gss_OID_set *actual_mechs, OM_uint32 *time_rec)
201 {
202 return GSS_S_COMPLETE;
203 }
204
205 OM_uint32 KRB5_CALLCONV
gss_release_cred(OM_uint32 * minor_status,gss_cred_id_t * cred_handle)206 gss_release_cred(OM_uint32 *minor_status, gss_cred_id_t *cred_handle)
207 {
208 return GSS_S_COMPLETE;
209 }
210
211 OM_uint32 KRB5_CALLCONV
gss_import_name(OM_uint32 * minor_status,gss_buffer_t input_name_buffer,gss_OID input_name_type,gss_name_t * output_name)212 gss_import_name(OM_uint32 *minor_status, gss_buffer_t input_name_buffer,
213 gss_OID input_name_type, gss_name_t *output_name)
214 {
215 static int dummy;
216
217 /*
218 * We don't need to remember anything about names, but we do need to
219 * distinguish them from GSS_C_NO_NAME (to determine the direction of
220 * gss_query_meta_data() and gss_exchange_meta_data()), so assign an
221 * arbitrary data pointer.
222 */
223 *output_name = (gss_name_t)&dummy;
224 return GSS_S_COMPLETE;
225 }
226
227 OM_uint32 KRB5_CALLCONV
gss_release_name(OM_uint32 * minor_status,gss_name_t * input_name)228 gss_release_name(OM_uint32 *minor_status, gss_name_t *input_name)
229 {
230 return GSS_S_COMPLETE;
231 }
232
233 OM_uint32 KRB5_CALLCONV
gss_display_status(OM_uint32 * minor_status,OM_uint32 status_value,int status_type,gss_OID mech_type,OM_uint32 * message_context,gss_buffer_t status_string)234 gss_display_status(OM_uint32 *minor_status, OM_uint32 status_value,
235 int status_type, gss_OID mech_type,
236 OM_uint32 *message_context, gss_buffer_t status_string)
237 {
238 if (status_type == GSS_C_MECH_CODE && status_value == 12345) {
239 status_string->value = gssalloc_strdup("failure from acceptor");
240 assert(status_string->value != NULL);
241 status_string->length = strlen(status_string->value);
242 return GSS_S_COMPLETE;
243 }
244 return GSS_S_BAD_STATUS;
245 }
246
247 OM_uint32 KRB5_CALLCONV
gssspi_query_meta_data(OM_uint32 * minor_status,gss_const_OID mech_oid,gss_cred_id_t cred_handle,gss_ctx_id_t * context_handle,const gss_name_t targ_name,OM_uint32 req_flags,gss_buffer_t meta_data)248 gssspi_query_meta_data(OM_uint32 *minor_status, gss_const_OID mech_oid,
249 gss_cred_id_t cred_handle, gss_ctx_id_t *context_handle,
250 const gss_name_t targ_name, OM_uint32 req_flags,
251 gss_buffer_t meta_data)
252 {
253 const char *envstr;
254 uint8_t mech_last_octet;
255 int initiator = (targ_name != GSS_C_NO_NAME);
256
257 mech_last_octet = ((uint8_t *)mech_oid->elements)[mech_oid->length - 1];
258 envstr = getenv(initiator ? "INIT_QUERY_FAIL" : "ACCEPT_QUERY_FAIL");
259 if (envstr != NULL && atoi(envstr) == mech_last_octet)
260 return GSS_S_FAILURE;
261 envstr = getenv(initiator ? "INIT_QUERY_NONE" : "ACCEPT_QUERY_NONE");
262 if (envstr != NULL && atoi(envstr) == mech_last_octet)
263 return GSS_S_COMPLETE;
264
265 meta_data->value = gssalloc_strdup("X");
266 meta_data->length = 1;
267 return GSS_S_COMPLETE;
268 }
269
270 OM_uint32 KRB5_CALLCONV
gssspi_exchange_meta_data(OM_uint32 * minor_status,gss_const_OID mech_oid,gss_cred_id_t cred_handle,gss_ctx_id_t * context_handle,const gss_name_t targ_name,OM_uint32 req_flags,gss_const_buffer_t meta_data)271 gssspi_exchange_meta_data(OM_uint32 *minor_status, gss_const_OID mech_oid,
272 gss_cred_id_t cred_handle,
273 gss_ctx_id_t *context_handle,
274 const gss_name_t targ_name, OM_uint32 req_flags,
275 gss_const_buffer_t meta_data)
276 {
277 const char *envstr;
278 uint8_t mech_last_octet;
279 int initiator = (targ_name != GSS_C_NO_NAME);
280
281 mech_last_octet = ((uint8_t *)mech_oid->elements)[mech_oid->length - 1];
282 envstr = getenv(initiator ? "INIT_EXCHANGE_FAIL" : "ACCEPT_EXCHANGE_FAIL");
283 if (envstr != NULL && atoi(envstr) == mech_last_octet)
284 return GSS_S_FAILURE;
285
286 assert(meta_data->length == 1 && memcmp(meta_data->value, "X", 1) == 0);
287 return GSS_S_COMPLETE;
288 }
289
290 OM_uint32 KRB5_CALLCONV
gssspi_query_mechanism_info(OM_uint32 * minor_status,gss_const_OID mech_oid,unsigned char auth_scheme[16])291 gssspi_query_mechanism_info(OM_uint32 *minor_status, gss_const_OID mech_oid,
292 unsigned char auth_scheme[16])
293 {
294 /* Copy the mech OID encoding and right-pad it with zeros. */
295 memset(auth_scheme, 0, 16);
296 assert(mech_oid->length <= 16);
297 memcpy(auth_scheme, mech_oid->elements, mech_oid->length);
298 return GSS_S_COMPLETE;
299 }
300
301 OM_uint32 KRB5_CALLCONV
gss_inquire_sec_context_by_oid(OM_uint32 * minor_status,const gss_ctx_id_t context_handle,const gss_OID desired_object,gss_buffer_set_t * data_set)302 gss_inquire_sec_context_by_oid(OM_uint32 *minor_status,
303 const gss_ctx_id_t context_handle,
304 const gss_OID desired_object,
305 gss_buffer_set_t *data_set)
306 {
307 struct test_context *ctx = (struct test_context *)context_handle;
308 OM_uint32 major;
309 uint8_t keybytes[32] = { 0 };
310 uint8_t typebytes[4];
311 gss_buffer_desc key, type;
312 const char *envstr;
313 int ask_verify;
314
315 if (gss_oid_equal(desired_object, GSS_C_INQ_NEGOEX_KEY))
316 ask_verify = 0;
317 else if (gss_oid_equal(desired_object, GSS_C_INQ_NEGOEX_VERIFY_KEY))
318 ask_verify = 1;
319 else
320 return GSS_S_UNAVAILABLE;
321
322 /*
323 * By default, make a key available only if the context is established.
324 * This can be overridden to "always", "init-always", "accept-always",
325 * or "never".
326 */
327 envstr = getenv("KEY");
328 if (envstr != NULL && strcmp(envstr, "never") == 0) {
329 return GSS_S_UNAVAILABLE;
330 } else if (ctx->hops > 0) {
331 if (envstr == NULL)
332 return GSS_S_UNAVAILABLE;
333 else if (strcmp(envstr, "init-always") == 0 && !ctx->initiator)
334 return GSS_S_UNAVAILABLE;
335 else if (strcmp(envstr, "accept-always") == 0 && ctx->initiator)
336 return GSS_S_UNAVAILABLE;
337 }
338
339 /* Perturb the key so that each side's verifier key is equal to the other's
340 * checksum key. */
341 keybytes[0] = ask_verify ^ ctx->initiator;
342
343 /* Supply an all-zeros aes256-sha1 negoex key. */
344 if (gss_oid_equal(desired_object, GSS_C_INQ_NEGOEX_KEY) ||
345 gss_oid_equal(desired_object, GSS_C_INQ_NEGOEX_VERIFY_KEY)) {
346 store_32_le(ENCTYPE_AES256_CTS_HMAC_SHA1_96, typebytes);
347 key.value = keybytes;
348 key.length = sizeof(keybytes);
349 type.value = typebytes;
350 type.length = sizeof(typebytes);
351 major = gss_add_buffer_set_member(minor_status, &key, data_set);
352 if (major != GSS_S_COMPLETE)
353 return major;
354 return gss_add_buffer_set_member(minor_status, &type, data_set);
355 }
356
357 return GSS_S_UNAVAILABLE;
358 }
359