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 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 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 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 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 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 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 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 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 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 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 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 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 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