1/*************************************************************************** 2 * _ _ ____ _ 3 * Project ___| | | | _ \| | 4 * / __| | | | |_) | | 5 * | (__| |_| | _ <| |___ 6 * \___|\___/|_| \_\_____| 7 * 8 * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. 9 * 10 * This software is licensed as described in the file COPYING, which 11 * you should have received as part of this distribution. The terms 12 * are also available at https://curl.se/docs/copyright.html. 13 * 14 * You may opt to use, copy, modify, merge, publish, distribute and/or sell 15 * copies of the Software, and permit persons to whom the Software is 16 * furnished to do so, under the terms of the COPYING file. 17 * 18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY 19 * KIND, either express or implied. 20 * 21 * SPDX-License-Identifier: curl 22 * 23 ***************************************************************************/ 24 25/* Only provides the bare minimum to link with libcurl */ 26 27#include <stdio.h> 28#include <stdlib.h> 29#include <string.h> 30 31#include "stub_gssapi.h" 32 33/* !checksrc! disable SNPRINTF all */ 34 35#define MAX_CREDS_LENGTH 250 36#define APPROX_TOKEN_LEN 250 37 38enum min_err_code { 39 GSS_OK = 0, 40 GSS_NO_MEMORY, 41 GSS_INVALID_ARGS, 42 GSS_INVALID_CREDS, 43 GSS_INVALID_CTX, 44 GSS_SERVER_ERR, 45 GSS_NO_MECH, 46 GSS_LAST 47}; 48 49static const char *min_err_table[] = { 50 "stub-gss: no error", 51 "stub-gss: no memory", 52 "stub-gss: invalid arguments", 53 "stub-gss: invalid credentials", 54 "stub-gss: invalid context", 55 "stub-gss: server returned error", 56 "stub-gss: cannot find a mechanism", 57 NULL 58}; 59 60struct gss_ctx_id_t_desc_struct { 61 enum { NONE, KRB5, NTLM1, NTLM3 } sent; 62 int have_krb5; 63 int have_ntlm; 64 OM_uint32 flags; 65 char creds[MAX_CREDS_LENGTH]; 66}; 67 68/* simple implementation of strndup(), which isn't portable */ 69static char *my_strndup(const char *ptr, size_t len) 70{ 71 char *copy = malloc(len + 1); 72 if(!copy) 73 return NULL; 74 memcpy(copy, ptr, len); 75 copy[len] = '\0'; 76 return copy; 77} 78 79OM_uint32 gss_init_sec_context(OM_uint32 *min, 80 gss_const_cred_id_t initiator_cred_handle, 81 gss_ctx_id_t *context_handle, 82 gss_const_name_t target_name, 83 const gss_OID mech_type, 84 OM_uint32 req_flags, 85 OM_uint32 time_req, 86 const gss_channel_bindings_t input_chan_bindings, 87 const gss_buffer_t input_token, 88 gss_OID *actual_mech_type, 89 gss_buffer_t output_token, 90 OM_uint32 *ret_flags, 91 OM_uint32 *time_rec) 92{ 93 /* The token will be encoded in base64 */ 94 size_t length = APPROX_TOKEN_LEN * 3 / 4; 95 size_t used = 0; 96 char *token = NULL; 97 const char *creds = NULL; 98 gss_ctx_id_t ctx = NULL; 99 100 (void)initiator_cred_handle; 101 (void)mech_type; 102 (void)time_req; 103 (void)input_chan_bindings; 104 (void)actual_mech_type; 105 106 if(!min) 107 return GSS_S_FAILURE; 108 109 *min = 0; 110 111 if(!context_handle || !target_name || !output_token) { 112 *min = GSS_INVALID_ARGS; 113 return GSS_S_FAILURE; 114 } 115 116 creds = getenv("CURL_STUB_GSS_CREDS"); 117 if(!creds || strlen(creds) >= MAX_CREDS_LENGTH) { 118 *min = GSS_INVALID_CREDS; 119 return GSS_S_FAILURE; 120 } 121 122 ctx = *context_handle; 123 if(ctx && strcmp(ctx->creds, creds)) { 124 *min = GSS_INVALID_CREDS; 125 return GSS_S_FAILURE; 126 } 127 128 output_token->length = 0; 129 output_token->value = NULL; 130 131 if(input_token && input_token->length) { 132 if(!ctx) { 133 *min = GSS_INVALID_CTX; 134 return GSS_S_FAILURE; 135 } 136 137 /* Server response, either D (RA==) or C (Qw==) */ 138 if(((char *) input_token->value)[0] == 'D') { 139 /* Done */ 140 switch(ctx->sent) { 141 case KRB5: 142 case NTLM3: 143 if(ret_flags) 144 *ret_flags = ctx->flags; 145 if(time_rec) 146 *time_rec = GSS_C_INDEFINITE; 147 return GSS_S_COMPLETE; 148 default: 149 *min = GSS_SERVER_ERR; 150 return GSS_S_FAILURE; 151 } 152 } 153 154 if(((char *) input_token->value)[0] != 'C') { 155 /* We only support Done or Continue */ 156 *min = GSS_SERVER_ERR; 157 return GSS_S_FAILURE; 158 } 159 160 /* Continue */ 161 switch(ctx->sent) { 162 case KRB5: 163 /* We sent KRB5 and it failed, let's try NTLM */ 164 if(ctx->have_ntlm) { 165 ctx->sent = NTLM1; 166 break; 167 } 168 else { 169 *min = GSS_SERVER_ERR; 170 return GSS_S_FAILURE; 171 } 172 case NTLM1: 173 ctx->sent = NTLM3; 174 break; 175 default: 176 *min = GSS_SERVER_ERR; 177 return GSS_S_FAILURE; 178 } 179 } 180 else { 181 if(ctx) { 182 *min = GSS_INVALID_CTX; 183 return GSS_S_FAILURE; 184 } 185 186 ctx = (gss_ctx_id_t) calloc(1, sizeof(*ctx)); 187 if(!ctx) { 188 *min = GSS_NO_MEMORY; 189 return GSS_S_FAILURE; 190 } 191 192 if(strstr(creds, "KRB5")) 193 ctx->have_krb5 = 1; 194 195 if(strstr(creds, "NTLM")) 196 ctx->have_ntlm = 1; 197 198 if(ctx->have_krb5) 199 ctx->sent = KRB5; 200 else if(ctx->have_ntlm) 201 ctx->sent = NTLM1; 202 else { 203 free(ctx); 204 *min = GSS_NO_MECH; 205 return GSS_S_FAILURE; 206 } 207 208 strcpy(ctx->creds, creds); 209 ctx->flags = req_flags; 210 } 211 212 token = malloc(length); 213 if(!token) { 214 free(ctx); 215 *min = GSS_NO_MEMORY; 216 return GSS_S_FAILURE; 217 } 218 219 /* Token format: creds:target:type:padding */ 220 /* Note: this is using the *real* snprintf() and not the curl provided 221 one */ 222 used = (size_t) snprintf(token, length, "%s:%s:%d:", creds, 223 (char *) target_name, ctx->sent); 224 225 if(used >= length) { 226 free(token); 227 free(ctx); 228 *min = GSS_NO_MEMORY; 229 return GSS_S_FAILURE; 230 } 231 232 /* Overwrite null terminator */ 233 memset(token + used, 'A', length - used); 234 235 *context_handle = ctx; 236 237 output_token->value = token; 238 output_token->length = length; 239 240 return GSS_S_CONTINUE_NEEDED; 241} 242 243OM_uint32 gss_delete_sec_context(OM_uint32 *min, 244 gss_ctx_id_t *context_handle, 245 gss_buffer_t output_token) 246{ 247 (void)output_token; 248 249 if(!min) 250 return GSS_S_FAILURE; 251 252 if(!context_handle) { 253 *min = GSS_INVALID_CTX; 254 return GSS_S_FAILURE; 255 } 256 257 free(*context_handle); 258 *context_handle = NULL; 259 *min = 0; 260 261 return GSS_S_COMPLETE; 262} 263 264OM_uint32 gss_release_buffer(OM_uint32 *min, 265 gss_buffer_t buffer) 266{ 267 if(min) 268 *min = 0; 269 270 if(buffer && buffer->length) { 271 free(buffer->value); 272 buffer->length = 0; 273 } 274 275 return GSS_S_COMPLETE; 276} 277 278OM_uint32 gss_import_name(OM_uint32 *min, 279 const gss_buffer_t input_name_buffer, 280 const gss_OID input_name_type, 281 gss_name_t *output_name) 282{ 283 char *name = NULL; 284 (void)input_name_type; 285 286 if(!min) 287 return GSS_S_FAILURE; 288 289 if(!input_name_buffer || !output_name) { 290 *min = GSS_INVALID_ARGS; 291 return GSS_S_FAILURE; 292 } 293 294 name = my_strndup(input_name_buffer->value, input_name_buffer->length); 295 if(!name) { 296 *min = GSS_NO_MEMORY; 297 return GSS_S_FAILURE; 298 } 299 300 *output_name = (gss_name_t) name; 301 *min = 0; 302 303 return GSS_S_COMPLETE; 304} 305 306OM_uint32 gss_release_name(OM_uint32 *min, 307 gss_name_t *input_name) 308{ 309 if(min) 310 *min = 0; 311 312 if(input_name) 313 free(*input_name); 314 315 return GSS_S_COMPLETE; 316} 317 318OM_uint32 gss_display_status(OM_uint32 *min, 319 OM_uint32 status_value, 320 int status_type, 321 const gss_OID mech_type, 322 OM_uint32 *message_context, 323 gss_buffer_t status_string) 324{ 325 const char maj_str[] = "Stub GSS error"; 326 (void)mech_type; 327 if(min) 328 *min = 0; 329 330 if(message_context) 331 *message_context = 0; 332 333 if(status_string) { 334 status_string->value = NULL; 335 status_string->length = 0; 336 337 if(status_value >= GSS_LAST) 338 return GSS_S_FAILURE; 339 340 switch(status_type) { 341 case GSS_C_GSS_CODE: 342 status_string->value = strdup(maj_str); 343 break; 344 case GSS_C_MECH_CODE: 345 status_string->value = strdup(min_err_table[status_value]); 346 break; 347 default: 348 return GSS_S_FAILURE; 349 } 350 351 if(status_string->value) 352 status_string->length = strlen(status_string->value); 353 else 354 return GSS_S_FAILURE; 355 } 356 357 return GSS_S_COMPLETE; 358} 359 360/* Stubs returning error */ 361 362OM_uint32 gss_display_name(OM_uint32 *min, 363 gss_const_name_t input_name, 364 gss_buffer_t output_name_buffer, 365 gss_OID *output_name_type) 366{ 367 (void)min; 368 (void)input_name; 369 (void)output_name_buffer; 370 (void)output_name_type; 371 return GSS_S_FAILURE; 372} 373 374OM_uint32 gss_inquire_context(OM_uint32 *min, 375 gss_const_ctx_id_t context_handle, 376 gss_name_t *src_name, 377 gss_name_t *targ_name, 378 OM_uint32 *lifetime_rec, 379 gss_OID *mech_type, 380 OM_uint32 *ctx_flags, 381 int *locally_initiated, 382 int *open_context) 383{ 384 (void)min; 385 (void)context_handle; 386 (void)src_name; 387 (void)targ_name; 388 (void)lifetime_rec; 389 (void)mech_type; 390 (void)ctx_flags; 391 (void)locally_initiated; 392 (void)open_context; 393 return GSS_S_FAILURE; 394} 395 396OM_uint32 gss_wrap(OM_uint32 *min, 397 gss_const_ctx_id_t context_handle, 398 int conf_req_flag, 399 gss_qop_t qop_req, 400 const gss_buffer_t input_message_buffer, 401 int *conf_state, 402 gss_buffer_t output_message_buffer) 403{ 404 (void)min; 405 (void)context_handle; 406 (void)conf_req_flag; 407 (void)qop_req; 408 (void)input_message_buffer; 409 (void)conf_state; 410 (void)output_message_buffer; 411 return GSS_S_FAILURE; 412} 413 414OM_uint32 gss_unwrap(OM_uint32 *min, 415 gss_const_ctx_id_t context_handle, 416 const gss_buffer_t input_message_buffer, 417 gss_buffer_t output_message_buffer, 418 int *conf_state, 419 gss_qop_t *qop_state) 420{ 421 (void)min; 422 (void)context_handle; 423 (void)input_message_buffer; 424 (void)output_message_buffer; 425 (void)conf_state; 426 (void)qop_state; 427 return GSS_S_FAILURE; 428} 429 430OM_uint32 gss_seal(OM_uint32 *min, 431 gss_ctx_id_t context_handle, 432 int conf_req_flag, 433 int qop_req, 434 gss_buffer_t input_message_buffer, 435 int *conf_state, 436 gss_buffer_t output_message_buffer) 437{ 438 (void)min; 439 (void)context_handle; 440 (void)conf_req_flag; 441 (void)qop_req; 442 (void)input_message_buffer; 443 (void)conf_state; 444 (void)output_message_buffer; 445 return GSS_S_FAILURE; 446} 447 448OM_uint32 gss_unseal(OM_uint32 *min, 449 gss_ctx_id_t context_handle, 450 gss_buffer_t input_message_buffer, 451 gss_buffer_t output_message_buffer, 452 int *conf_state, 453 int *qop_state) 454{ 455 (void)min; 456 (void)context_handle; 457 (void)input_message_buffer; 458 (void)output_message_buffer; 459 (void)conf_state; 460 (void)qop_state; 461 return GSS_S_FAILURE; 462} 463