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