curl_gssapi.c (12412B)
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 #include "curl_setup.h" 26 27 #ifdef HAVE_GSSAPI 28 29 #include "curl_gssapi.h" 30 #include "sendf.h" 31 32 /* The last 3 #include files should be in this order */ 33 #include "curl_printf.h" 34 #include "curl_memory.h" 35 #include "memdebug.h" 36 37 #if defined(__GNUC__) 38 #define CURL_ALIGN8 __attribute__((aligned(8))) 39 #else 40 #define CURL_ALIGN8 41 #endif 42 43 #if defined(__GNUC__) && defined(__APPLE__) 44 #pragma GCC diagnostic push 45 #pragma GCC diagnostic ignored "-Wdeprecated-declarations" 46 #endif 47 48 gss_OID_desc Curl_spnego_mech_oid CURL_ALIGN8 = { 49 6, CURL_UNCONST("\x2b\x06\x01\x05\x05\x02") 50 }; 51 gss_OID_desc Curl_krb5_mech_oid CURL_ALIGN8 = { 52 9, CURL_UNCONST("\x2a\x86\x48\x86\xf7\x12\x01\x02\x02") 53 }; 54 55 #ifdef DEBUGBUILD 56 enum min_err_code { 57 STUB_GSS_OK = 0, 58 STUB_GSS_NO_MEMORY, 59 STUB_GSS_INVALID_ARGS, 60 STUB_GSS_INVALID_CREDS, 61 STUB_GSS_INVALID_CTX, 62 STUB_GSS_SERVER_ERR, 63 STUB_GSS_NO_MECH, 64 STUB_GSS_LAST 65 }; 66 67 /* libcurl is also passing this struct to these functions, which are not yet 68 * stubbed: 69 * gss_inquire_context() 70 * gss_unwrap() 71 * gss_wrap() 72 */ 73 struct stub_gss_ctx_id_t_desc { 74 enum { STUB_GSS_NONE, STUB_GSS_KRB5, STUB_GSS_NTLM1, STUB_GSS_NTLM3 } sent; 75 int have_krb5; 76 int have_ntlm; 77 OM_uint32 flags; 78 char creds[250]; 79 }; 80 81 static OM_uint32 82 stub_gss_init_sec_context(OM_uint32 *min, 83 gss_cred_id_t initiator_cred_handle, 84 struct stub_gss_ctx_id_t_desc **context, 85 gss_name_t target_name, 86 const gss_OID mech_type, 87 OM_uint32 req_flags, 88 OM_uint32 time_req, 89 const gss_channel_bindings_t input_chan_bindings, 90 gss_buffer_desc *input_token, 91 gss_OID *actual_mech_type, 92 gss_buffer_desc *output_token, 93 OM_uint32 *ret_flags, 94 OM_uint32 *time_rec) 95 { 96 struct stub_gss_ctx_id_t_desc *ctx = NULL; 97 98 /* The token will be encoded in base64 */ 99 size_t length = sizeof(ctx->creds) * 3 / 4; 100 size_t used = 0; 101 char *token = NULL; 102 const char *creds = NULL; 103 104 (void)initiator_cred_handle; 105 (void)mech_type; 106 (void)time_req; 107 (void)input_chan_bindings; 108 (void)actual_mech_type; 109 110 if(!min) 111 return GSS_S_FAILURE; 112 113 *min = 0; 114 115 if(!context || !target_name || !output_token) { 116 *min = STUB_GSS_INVALID_ARGS; 117 return GSS_S_FAILURE; 118 } 119 120 creds = getenv("CURL_STUB_GSS_CREDS"); 121 if(!creds || strlen(creds) >= sizeof(ctx->creds)) { 122 *min = STUB_GSS_INVALID_CREDS; 123 return GSS_S_FAILURE; 124 } 125 126 ctx = *context; 127 if(ctx && strcmp(ctx->creds, creds)) { 128 *min = STUB_GSS_INVALID_CREDS; 129 return GSS_S_FAILURE; 130 } 131 132 output_token->length = 0; 133 output_token->value = NULL; 134 135 if(input_token && input_token->length) { 136 if(!ctx) { 137 *min = STUB_GSS_INVALID_CTX; 138 return GSS_S_FAILURE; 139 } 140 141 /* Server response, either D (RA==) or C (Qw==) */ 142 if(((char *) input_token->value)[0] == 'D') { 143 /* Done */ 144 switch(ctx->sent) { 145 case STUB_GSS_KRB5: 146 case STUB_GSS_NTLM3: 147 if(ret_flags) 148 *ret_flags = ctx->flags; 149 if(time_rec) 150 *time_rec = GSS_C_INDEFINITE; 151 return GSS_S_COMPLETE; 152 default: 153 *min = STUB_GSS_SERVER_ERR; 154 return GSS_S_FAILURE; 155 } 156 } 157 158 if(((char *) input_token->value)[0] != 'C') { 159 /* We only support Done or Continue */ 160 *min = STUB_GSS_SERVER_ERR; 161 return GSS_S_FAILURE; 162 } 163 164 /* Continue */ 165 switch(ctx->sent) { 166 case STUB_GSS_KRB5: 167 /* We sent KRB5 and it failed, let's try NTLM */ 168 if(ctx->have_ntlm) { 169 ctx->sent = STUB_GSS_NTLM1; 170 break; 171 } 172 else { 173 *min = STUB_GSS_SERVER_ERR; 174 return GSS_S_FAILURE; 175 } 176 case STUB_GSS_NTLM1: 177 ctx->sent = STUB_GSS_NTLM3; 178 break; 179 default: 180 *min = STUB_GSS_SERVER_ERR; 181 return GSS_S_FAILURE; 182 } 183 } 184 else { 185 if(ctx) { 186 *min = STUB_GSS_INVALID_CTX; 187 return GSS_S_FAILURE; 188 } 189 190 ctx = calloc(1, sizeof(*ctx)); 191 if(!ctx) { 192 *min = STUB_GSS_NO_MEMORY; 193 return GSS_S_FAILURE; 194 } 195 196 if(strstr(creds, "KRB5")) 197 ctx->have_krb5 = 1; 198 199 if(strstr(creds, "NTLM")) 200 ctx->have_ntlm = 1; 201 202 if(ctx->have_krb5) 203 ctx->sent = STUB_GSS_KRB5; 204 else if(ctx->have_ntlm) 205 ctx->sent = STUB_GSS_NTLM1; 206 else { 207 free(ctx); 208 *min = STUB_GSS_NO_MECH; 209 return GSS_S_FAILURE; 210 } 211 212 strcpy(ctx->creds, creds); 213 ctx->flags = req_flags; 214 } 215 216 /* To avoid memdebug macro replacement, wrap the name in parentheses to call 217 the original version. It is freed via the GSS API gss_release_buffer(). */ 218 token = (malloc)(length); 219 if(!token) { 220 free(ctx); 221 *min = STUB_GSS_NO_MEMORY; 222 return GSS_S_FAILURE; 223 } 224 225 { 226 gss_buffer_desc target_desc; 227 gss_OID name_type = GSS_C_NO_OID; 228 OM_uint32 minor_status; 229 OM_uint32 major_status; 230 major_status = gss_display_name(&minor_status, target_name, 231 &target_desc, &name_type); 232 if(GSS_ERROR(major_status)) { 233 (free)(token); 234 free(ctx); 235 *min = STUB_GSS_NO_MEMORY; 236 return GSS_S_FAILURE; 237 } 238 239 if(strlen(creds) + target_desc.length + 5 >= sizeof(ctx->creds)) { 240 (free)(token); 241 free(ctx); 242 *min = STUB_GSS_NO_MEMORY; 243 return GSS_S_FAILURE; 244 } 245 246 /* Token format: creds:target:type:padding */ 247 used = msnprintf(token, length, "%s:%.*s:%d:", creds, 248 (int)target_desc.length, (const char *)target_desc.value, 249 ctx->sent); 250 251 gss_release_buffer(&minor_status, &target_desc); 252 } 253 254 if(used >= length) { 255 (free)(token); 256 free(ctx); 257 *min = STUB_GSS_NO_MEMORY; 258 return GSS_S_FAILURE; 259 } 260 261 /* Overwrite null-terminator */ 262 memset(token + used, 'A', length - used); 263 264 *context = ctx; 265 266 output_token->value = token; 267 output_token->length = length; 268 269 return GSS_S_CONTINUE_NEEDED; 270 } 271 272 static OM_uint32 273 stub_gss_delete_sec_context(OM_uint32 *min, 274 struct stub_gss_ctx_id_t_desc **context, 275 gss_buffer_t output_token) 276 { 277 (void)output_token; 278 279 if(!min) 280 return GSS_S_FAILURE; 281 282 if(!context) { 283 *min = STUB_GSS_INVALID_CTX; 284 return GSS_S_FAILURE; 285 } 286 if(!*context) { 287 *min = STUB_GSS_INVALID_CTX; 288 return GSS_S_FAILURE; 289 } 290 291 free(*context); 292 *context = NULL; 293 *min = 0; 294 295 return GSS_S_COMPLETE; 296 } 297 #endif /* DEBUGBUILD */ 298 299 OM_uint32 Curl_gss_init_sec_context(struct Curl_easy *data, 300 OM_uint32 *minor_status, 301 gss_ctx_id_t *context, 302 gss_name_t target_name, 303 gss_OID mech_type, 304 gss_channel_bindings_t input_chan_bindings, 305 gss_buffer_t input_token, 306 gss_buffer_t output_token, 307 const bool mutual_auth, 308 OM_uint32 *ret_flags) 309 { 310 OM_uint32 req_flags = GSS_C_REPLAY_FLAG; 311 312 if(mutual_auth) 313 req_flags |= GSS_C_MUTUAL_FLAG; 314 315 if(data->set.gssapi_delegation & CURLGSSAPI_DELEGATION_POLICY_FLAG) { 316 #ifdef GSS_C_DELEG_POLICY_FLAG 317 req_flags |= GSS_C_DELEG_POLICY_FLAG; 318 #else 319 infof(data, "WARNING: support for CURLGSSAPI_DELEGATION_POLICY_FLAG not " 320 "compiled in"); 321 #endif 322 } 323 324 if(data->set.gssapi_delegation & CURLGSSAPI_DELEGATION_FLAG) 325 req_flags |= GSS_C_DELEG_FLAG; 326 327 #ifdef DEBUGBUILD 328 if(getenv("CURL_STUB_GSS_CREDS")) 329 return stub_gss_init_sec_context(minor_status, 330 GSS_C_NO_CREDENTIAL, /* cred_handle */ 331 (struct stub_gss_ctx_id_t_desc **)context, 332 target_name, 333 mech_type, 334 req_flags, 335 0, /* time_req */ 336 input_chan_bindings, 337 input_token, 338 NULL, /* actual_mech_type */ 339 output_token, 340 ret_flags, 341 NULL /* time_rec */); 342 #endif /* DEBUGBUILD */ 343 344 return gss_init_sec_context(minor_status, 345 GSS_C_NO_CREDENTIAL, /* cred_handle */ 346 context, 347 target_name, 348 mech_type, 349 req_flags, 350 0, /* time_req */ 351 input_chan_bindings, 352 input_token, 353 NULL, /* actual_mech_type */ 354 output_token, 355 ret_flags, 356 NULL /* time_rec */); 357 } 358 359 OM_uint32 Curl_gss_delete_sec_context(OM_uint32 *min, 360 gss_ctx_id_t *context, 361 gss_buffer_t output_token) 362 { 363 #ifdef DEBUGBUILD 364 if(getenv("CURL_STUB_GSS_CREDS")) 365 return stub_gss_delete_sec_context(min, 366 (struct stub_gss_ctx_id_t_desc **)context, 367 output_token); 368 #endif /* DEBUGBUILD */ 369 370 return gss_delete_sec_context(min, context, output_token); 371 } 372 373 #define GSS_LOG_BUFFER_LEN 1024 374 static size_t display_gss_error(OM_uint32 status, int type, 375 char *buf, size_t len) { 376 OM_uint32 maj_stat; 377 OM_uint32 min_stat; 378 OM_uint32 msg_ctx = 0; 379 gss_buffer_desc status_string = GSS_C_EMPTY_BUFFER; 380 381 do { 382 maj_stat = gss_display_status(&min_stat, 383 status, 384 type, 385 GSS_C_NO_OID, 386 &msg_ctx, 387 &status_string); 388 if(maj_stat == GSS_S_COMPLETE && status_string.length > 0) { 389 if(GSS_LOG_BUFFER_LEN > len + status_string.length + 3) { 390 len += msnprintf(buf + len, GSS_LOG_BUFFER_LEN - len, 391 "%.*s. ", (int)status_string.length, 392 (char *)status_string.value); 393 } 394 } 395 gss_release_buffer(&min_stat, &status_string); 396 } while(!GSS_ERROR(maj_stat) && msg_ctx); 397 398 return len; 399 } 400 401 /* 402 * Curl_gss_log_error() 403 * 404 * This is used to log a GSS-API error status. 405 * 406 * Parameters: 407 * 408 * data [in] - The session handle. 409 * prefix [in] - The prefix of the log message. 410 * major [in] - The major status code. 411 * minor [in] - The minor status code. 412 */ 413 void Curl_gss_log_error(struct Curl_easy *data, const char *prefix, 414 OM_uint32 major, OM_uint32 minor) 415 { 416 char buf[GSS_LOG_BUFFER_LEN]; 417 size_t len = 0; 418 419 if(major != GSS_S_FAILURE) 420 len = display_gss_error(major, GSS_C_GSS_CODE, buf, len); 421 422 display_gss_error(minor, GSS_C_MECH_CODE, buf, len); 423 424 infof(data, "%s%s", prefix, buf); 425 #ifdef CURL_DISABLE_VERBOSE_STRINGS 426 (void)data; 427 (void)prefix; 428 #endif 429 } 430 431 #if defined(__GNUC__) && defined(__APPLE__) 432 #pragma GCC diagnostic pop 433 #endif 434 435 #endif /* HAVE_GSSAPI */