socks_gssapi.c (20851B)
1 /*************************************************************************** 2 * _ _ ____ _ 3 * Project ___| | | | _ \| | 4 * / __| | | | |_) | | 5 * | (__| |_| | _ <| |___ 6 * \___|\___/|_| \_\_____| 7 * 8 * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. 9 * Copyright (C) Markus Moeller, <markus_moeller@compuserve.com> 10 * 11 * This software is licensed as described in the file COPYING, which 12 * you should have received as part of this distribution. The terms 13 * are also available at https://curl.se/docs/copyright.html. 14 * 15 * You may opt to use, copy, modify, merge, publish, distribute and/or sell 16 * copies of the Software, and permit persons to whom the Software is 17 * furnished to do so, under the terms of the COPYING file. 18 * 19 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY 20 * KIND, either express or implied. 21 * 22 * SPDX-License-Identifier: curl 23 * 24 ***************************************************************************/ 25 26 #include "curl_setup.h" 27 28 #if defined(HAVE_GSSAPI) && !defined(CURL_DISABLE_PROXY) 29 30 #include "curl_gssapi.h" 31 #include "urldata.h" 32 #include "sendf.h" 33 #include "cfilters.h" 34 #include "connect.h" 35 #include "curlx/timeval.h" 36 #include "socks.h" 37 #include "curlx/warnless.h" 38 #include "strdup.h" 39 40 /* The last 3 #include files should be in this order */ 41 #include "curl_printf.h" 42 #include "curl_memory.h" 43 #include "memdebug.h" 44 45 #if defined(__GNUC__) && defined(__APPLE__) 46 #pragma GCC diagnostic push 47 #pragma GCC diagnostic ignored "-Wdeprecated-declarations" 48 #endif 49 50 #define MAX_GSS_LEN 1024 51 52 static gss_ctx_id_t gss_context = GSS_C_NO_CONTEXT; 53 54 /* 55 * Helper GSS-API error functions. 56 */ 57 static int check_gss_err(struct Curl_easy *data, 58 OM_uint32 major_status, 59 OM_uint32 minor_status, 60 const char *function) 61 { 62 if(GSS_ERROR(major_status)) { 63 OM_uint32 maj_stat, min_stat; 64 OM_uint32 msg_ctx = 0; 65 gss_buffer_desc status_string = GSS_C_EMPTY_BUFFER; 66 struct dynbuf dbuf; 67 68 curlx_dyn_init(&dbuf, MAX_GSS_LEN); 69 msg_ctx = 0; 70 while(!msg_ctx) { 71 /* convert major status code (GSS-API error) to text */ 72 maj_stat = gss_display_status(&min_stat, major_status, 73 GSS_C_GSS_CODE, 74 GSS_C_NULL_OID, 75 &msg_ctx, &status_string); 76 if(maj_stat == GSS_S_COMPLETE) { 77 if(curlx_dyn_addn(&dbuf, status_string.value, 78 status_string.length)) 79 return 1; /* error */ 80 gss_release_buffer(&min_stat, &status_string); 81 break; 82 } 83 gss_release_buffer(&min_stat, &status_string); 84 } 85 if(curlx_dyn_addn(&dbuf, ".\n", 2)) 86 return 1; /* error */ 87 msg_ctx = 0; 88 while(!msg_ctx) { 89 /* convert minor status code (underlying routine error) to text */ 90 maj_stat = gss_display_status(&min_stat, minor_status, 91 GSS_C_MECH_CODE, 92 GSS_C_NULL_OID, 93 &msg_ctx, &status_string); 94 if(maj_stat == GSS_S_COMPLETE) { 95 if(curlx_dyn_addn(&dbuf, status_string.value, 96 status_string.length)) 97 return 1; /* error */ 98 gss_release_buffer(&min_stat, &status_string); 99 break; 100 } 101 gss_release_buffer(&min_stat, &status_string); 102 } 103 failf(data, "GSS-API error: %s failed: %s", function, 104 curlx_dyn_ptr(&dbuf)); 105 curlx_dyn_free(&dbuf); 106 return 1; 107 } 108 109 return 0; 110 } 111 112 CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, 113 struct Curl_easy *data) 114 { 115 struct connectdata *conn = cf->conn; 116 curl_socket_t sock = conn->sock[cf->sockindex]; 117 CURLcode code; 118 size_t actualread; 119 size_t nwritten; 120 int result; 121 OM_uint32 gss_major_status, gss_minor_status, gss_status; 122 OM_uint32 gss_ret_flags; 123 int gss_conf_state, gss_enc; 124 gss_buffer_desc service = GSS_C_EMPTY_BUFFER; 125 gss_buffer_desc gss_send_token = GSS_C_EMPTY_BUFFER; 126 gss_buffer_desc gss_recv_token = GSS_C_EMPTY_BUFFER; 127 gss_buffer_desc gss_w_token = GSS_C_EMPTY_BUFFER; 128 gss_buffer_desc *gss_token = GSS_C_NO_BUFFER; 129 gss_name_t server = GSS_C_NO_NAME; 130 gss_name_t gss_client_name = GSS_C_NO_NAME; 131 unsigned short us_length; 132 char *user = NULL; 133 unsigned char socksreq[4]; /* room for GSS-API exchange header only */ 134 const char *serviceptr = data->set.str[STRING_PROXY_SERVICE_NAME] ? 135 data->set.str[STRING_PROXY_SERVICE_NAME] : "rcmd"; 136 const size_t serviceptr_length = strlen(serviceptr); 137 138 /* GSS-API request looks like 139 * +----+------+-----+----------------+ 140 * |VER | MTYP | LEN | TOKEN | 141 * +----+------+----------------------+ 142 * | 1 | 1 | 2 | up to 2^16 - 1 | 143 * +----+------+-----+----------------+ 144 */ 145 146 /* prepare service name */ 147 if(strchr(serviceptr, '/')) { 148 service.length = serviceptr_length; 149 service.value = Curl_memdup(serviceptr, service.length); 150 if(!service.value) 151 return CURLE_OUT_OF_MEMORY; 152 153 gss_major_status = gss_import_name(&gss_minor_status, &service, 154 (gss_OID) GSS_C_NULL_OID, &server); 155 } 156 else { 157 service.value = malloc(serviceptr_length + 158 strlen(conn->socks_proxy.host.name) + 2); 159 if(!service.value) 160 return CURLE_OUT_OF_MEMORY; 161 service.length = serviceptr_length + 162 strlen(conn->socks_proxy.host.name) + 1; 163 msnprintf(service.value, service.length + 1, "%s@%s", 164 serviceptr, conn->socks_proxy.host.name); 165 166 gss_major_status = gss_import_name(&gss_minor_status, &service, 167 GSS_C_NT_HOSTBASED_SERVICE, &server); 168 } 169 170 gss_release_buffer(&gss_status, &service); /* clear allocated memory */ 171 172 if(check_gss_err(data, gss_major_status, 173 gss_minor_status, "gss_import_name()")) { 174 failf(data, "Failed to create service name."); 175 gss_release_name(&gss_status, &server); 176 return CURLE_COULDNT_CONNECT; 177 } 178 179 (void)curlx_nonblock(sock, FALSE); 180 181 /* As long as we need to keep sending some context info, and there is no */ 182 /* errors, keep sending it... */ 183 for(;;) { 184 gss_major_status = Curl_gss_init_sec_context(data, 185 &gss_minor_status, 186 &gss_context, 187 server, 188 &Curl_krb5_mech_oid, 189 NULL, 190 gss_token, 191 &gss_send_token, 192 TRUE, 193 &gss_ret_flags); 194 195 if(gss_token != GSS_C_NO_BUFFER) 196 gss_release_buffer(&gss_status, &gss_recv_token); 197 if(check_gss_err(data, gss_major_status, 198 gss_minor_status, "gss_init_sec_context")) { 199 gss_release_name(&gss_status, &server); 200 gss_release_buffer(&gss_status, &gss_recv_token); 201 gss_release_buffer(&gss_status, &gss_send_token); 202 Curl_gss_delete_sec_context(&gss_status, &gss_context, NULL); 203 failf(data, "Failed to initial GSS-API token."); 204 return CURLE_COULDNT_CONNECT; 205 } 206 207 if(gss_send_token.length) { 208 socksreq[0] = 1; /* GSS-API subnegotiation version */ 209 socksreq[1] = 1; /* authentication message type */ 210 us_length = htons((unsigned short)gss_send_token.length); 211 memcpy(socksreq + 2, &us_length, sizeof(short)); 212 213 code = Curl_conn_cf_send(cf->next, data, (char *)socksreq, 4, 214 FALSE, &nwritten); 215 if(code || (4 != nwritten)) { 216 failf(data, "Failed to send GSS-API authentication request."); 217 gss_release_name(&gss_status, &server); 218 gss_release_buffer(&gss_status, &gss_recv_token); 219 gss_release_buffer(&gss_status, &gss_send_token); 220 Curl_gss_delete_sec_context(&gss_status, &gss_context, NULL); 221 return CURLE_COULDNT_CONNECT; 222 } 223 224 code = Curl_conn_cf_send(cf->next, data, 225 (char *)gss_send_token.value, 226 gss_send_token.length, FALSE, &nwritten); 227 if(code || (gss_send_token.length != nwritten)) { 228 failf(data, "Failed to send GSS-API authentication token."); 229 gss_release_name(&gss_status, &server); 230 gss_release_buffer(&gss_status, &gss_recv_token); 231 gss_release_buffer(&gss_status, &gss_send_token); 232 Curl_gss_delete_sec_context(&gss_status, &gss_context, NULL); 233 return CURLE_COULDNT_CONNECT; 234 } 235 236 } 237 238 gss_release_buffer(&gss_status, &gss_send_token); 239 gss_release_buffer(&gss_status, &gss_recv_token); 240 if(gss_major_status != GSS_S_CONTINUE_NEEDED) 241 break; 242 243 /* analyse response */ 244 245 /* GSS-API response looks like 246 * +----+------+-----+----------------+ 247 * |VER | MTYP | LEN | TOKEN | 248 * +----+------+----------------------+ 249 * | 1 | 1 | 2 | up to 2^16 - 1 | 250 * +----+------+-----+----------------+ 251 */ 252 253 result = Curl_blockread_all(cf, data, (char *)socksreq, 4, &actualread); 254 if(result || (actualread != 4)) { 255 failf(data, "Failed to receive GSS-API authentication response."); 256 gss_release_name(&gss_status, &server); 257 Curl_gss_delete_sec_context(&gss_status, &gss_context, NULL); 258 return CURLE_COULDNT_CONNECT; 259 } 260 261 /* ignore the first (VER) byte */ 262 if(socksreq[1] == 255) { /* status / message type */ 263 failf(data, "User was rejected by the SOCKS5 server (%d %d).", 264 socksreq[0], socksreq[1]); 265 gss_release_name(&gss_status, &server); 266 Curl_gss_delete_sec_context(&gss_status, &gss_context, NULL); 267 return CURLE_COULDNT_CONNECT; 268 } 269 270 if(socksreq[1] != 1) { /* status / message type */ 271 failf(data, "Invalid GSS-API authentication response type (%d %d).", 272 socksreq[0], socksreq[1]); 273 gss_release_name(&gss_status, &server); 274 Curl_gss_delete_sec_context(&gss_status, &gss_context, NULL); 275 return CURLE_COULDNT_CONNECT; 276 } 277 278 memcpy(&us_length, socksreq + 2, sizeof(short)); 279 us_length = ntohs(us_length); 280 281 gss_recv_token.length = us_length; 282 gss_recv_token.value = malloc(us_length); 283 if(!gss_recv_token.value) { 284 failf(data, 285 "Could not allocate memory for GSS-API authentication " 286 "response token."); 287 gss_release_name(&gss_status, &server); 288 Curl_gss_delete_sec_context(&gss_status, &gss_context, NULL); 289 return CURLE_OUT_OF_MEMORY; 290 } 291 292 result = Curl_blockread_all(cf, data, (char *)gss_recv_token.value, 293 gss_recv_token.length, &actualread); 294 295 if(result || (actualread != us_length)) { 296 failf(data, "Failed to receive GSS-API authentication token."); 297 gss_release_name(&gss_status, &server); 298 gss_release_buffer(&gss_status, &gss_recv_token); 299 Curl_gss_delete_sec_context(&gss_status, &gss_context, NULL); 300 return CURLE_COULDNT_CONNECT; 301 } 302 303 gss_token = &gss_recv_token; 304 } 305 306 gss_release_name(&gss_status, &server); 307 308 /* Everything is good so far, user was authenticated! */ 309 gss_major_status = gss_inquire_context(&gss_minor_status, gss_context, 310 &gss_client_name, NULL, NULL, NULL, 311 NULL, NULL, NULL); 312 if(check_gss_err(data, gss_major_status, 313 gss_minor_status, "gss_inquire_context")) { 314 Curl_gss_delete_sec_context(&gss_status, &gss_context, NULL); 315 gss_release_name(&gss_status, &gss_client_name); 316 failf(data, "Failed to determine username."); 317 return CURLE_COULDNT_CONNECT; 318 } 319 gss_major_status = gss_display_name(&gss_minor_status, gss_client_name, 320 &gss_send_token, NULL); 321 if(check_gss_err(data, gss_major_status, 322 gss_minor_status, "gss_display_name")) { 323 Curl_gss_delete_sec_context(&gss_status, &gss_context, NULL); 324 gss_release_name(&gss_status, &gss_client_name); 325 gss_release_buffer(&gss_status, &gss_send_token); 326 failf(data, "Failed to determine username."); 327 return CURLE_COULDNT_CONNECT; 328 } 329 user = malloc(gss_send_token.length + 1); 330 if(!user) { 331 Curl_gss_delete_sec_context(&gss_status, &gss_context, NULL); 332 gss_release_name(&gss_status, &gss_client_name); 333 gss_release_buffer(&gss_status, &gss_send_token); 334 return CURLE_OUT_OF_MEMORY; 335 } 336 337 memcpy(user, gss_send_token.value, gss_send_token.length); 338 user[gss_send_token.length] = '\0'; 339 gss_release_name(&gss_status, &gss_client_name); 340 gss_release_buffer(&gss_status, &gss_send_token); 341 infof(data, "SOCKS5 server authenticated user %s with GSS-API.",user); 342 free(user); 343 user = NULL; 344 345 /* Do encryption */ 346 socksreq[0] = 1; /* GSS-API subnegotiation version */ 347 socksreq[1] = 2; /* encryption message type */ 348 349 gss_enc = 0; /* no data protection */ 350 /* do confidentiality protection if supported */ 351 if(gss_ret_flags & GSS_C_CONF_FLAG) 352 gss_enc = 2; 353 /* else do integrity protection */ 354 else if(gss_ret_flags & GSS_C_INTEG_FLAG) 355 gss_enc = 1; 356 357 infof(data, "SOCKS5 server supports GSS-API %s data protection.", 358 (gss_enc == 0) ? "no" : 359 ((gss_enc == 1) ? "integrity" : "confidentiality")); 360 /* force for the moment to no data protection */ 361 gss_enc = 0; 362 /* 363 * Sending the encryption type in clear seems wrong. It should be 364 * protected with gss_seal()/gss_wrap(). See RFC1961 extract below 365 * The NEC reference implementations on which this is based is 366 * therefore at fault 367 * 368 * +------+------+------+.......................+ 369 * + ver | mtyp | len | token | 370 * +------+------+------+.......................+ 371 * + 0x01 | 0x02 | 0x02 | up to 2^16 - 1 octets | 372 * +------+------+------+.......................+ 373 * 374 * Where: 375 * 376 * - "ver" is the protocol version number, here 1 to represent the 377 * first version of the SOCKS/GSS-API protocol 378 * 379 * - "mtyp" is the message type, here 2 to represent a protection 380 * -level negotiation message 381 * 382 * - "len" is the length of the "token" field in octets 383 * 384 * - "token" is the GSS-API encapsulated protection level 385 * 386 * The token is produced by encapsulating an octet containing the 387 * required protection level using gss_seal()/gss_wrap() with conf_req 388 * set to FALSE. The token is verified using gss_unseal()/ 389 * gss_unwrap(). 390 * 391 */ 392 if(data->set.socks5_gssapi_nec) { 393 us_length = htons((short)1); 394 memcpy(socksreq + 2, &us_length, sizeof(short)); 395 } 396 else { 397 gss_send_token.length = 1; 398 gss_send_token.value = Curl_memdup(&gss_enc, 1); 399 if(!gss_send_token.value) { 400 Curl_gss_delete_sec_context(&gss_status, &gss_context, NULL); 401 return CURLE_OUT_OF_MEMORY; 402 } 403 404 gss_major_status = gss_wrap(&gss_minor_status, gss_context, 0, 405 GSS_C_QOP_DEFAULT, &gss_send_token, 406 &gss_conf_state, &gss_w_token); 407 408 if(check_gss_err(data, gss_major_status, gss_minor_status, "gss_wrap")) { 409 gss_release_buffer(&gss_status, &gss_send_token); 410 gss_release_buffer(&gss_status, &gss_w_token); 411 Curl_gss_delete_sec_context(&gss_status, &gss_context, NULL); 412 failf(data, "Failed to wrap GSS-API encryption value into token."); 413 return CURLE_COULDNT_CONNECT; 414 } 415 gss_release_buffer(&gss_status, &gss_send_token); 416 417 us_length = htons((unsigned short)gss_w_token.length); 418 memcpy(socksreq + 2, &us_length, sizeof(short)); 419 } 420 421 code = Curl_conn_cf_send(cf->next, data, (char *)socksreq, 4, FALSE, 422 &nwritten); 423 if(code || (4 != nwritten)) { 424 failf(data, "Failed to send GSS-API encryption request."); 425 gss_release_buffer(&gss_status, &gss_w_token); 426 Curl_gss_delete_sec_context(&gss_status, &gss_context, NULL); 427 return CURLE_COULDNT_CONNECT; 428 } 429 430 if(data->set.socks5_gssapi_nec) { 431 memcpy(socksreq, &gss_enc, 1); 432 code = Curl_conn_cf_send(cf->next, data, (char *)socksreq, 1, FALSE, 433 &nwritten); 434 if(code || (1 != nwritten)) { 435 failf(data, "Failed to send GSS-API encryption type."); 436 Curl_gss_delete_sec_context(&gss_status, &gss_context, NULL); 437 return CURLE_COULDNT_CONNECT; 438 } 439 } 440 else { 441 code = Curl_conn_cf_send(cf->next, data, (char *)gss_w_token.value, 442 gss_w_token.length, FALSE, &nwritten); 443 if(code || (gss_w_token.length != nwritten)) { 444 failf(data, "Failed to send GSS-API encryption type."); 445 gss_release_buffer(&gss_status, &gss_w_token); 446 Curl_gss_delete_sec_context(&gss_status, &gss_context, NULL); 447 return CURLE_COULDNT_CONNECT; 448 } 449 gss_release_buffer(&gss_status, &gss_w_token); 450 } 451 452 result = Curl_blockread_all(cf, data, (char *)socksreq, 4, &actualread); 453 if(result || (actualread != 4)) { 454 failf(data, "Failed to receive GSS-API encryption response."); 455 Curl_gss_delete_sec_context(&gss_status, &gss_context, NULL); 456 return CURLE_COULDNT_CONNECT; 457 } 458 459 /* ignore the first (VER) byte */ 460 if(socksreq[1] == 255) { /* status / message type */ 461 failf(data, "User was rejected by the SOCKS5 server (%d %d).", 462 socksreq[0], socksreq[1]); 463 Curl_gss_delete_sec_context(&gss_status, &gss_context, NULL); 464 return CURLE_COULDNT_CONNECT; 465 } 466 467 if(socksreq[1] != 2) { /* status / message type */ 468 failf(data, "Invalid GSS-API encryption response type (%d %d).", 469 socksreq[0], socksreq[1]); 470 Curl_gss_delete_sec_context(&gss_status, &gss_context, NULL); 471 return CURLE_COULDNT_CONNECT; 472 } 473 474 memcpy(&us_length, socksreq + 2, sizeof(short)); 475 us_length = ntohs(us_length); 476 477 gss_recv_token.length = us_length; 478 gss_recv_token.value = malloc(gss_recv_token.length); 479 if(!gss_recv_token.value) { 480 Curl_gss_delete_sec_context(&gss_status, &gss_context, NULL); 481 return CURLE_OUT_OF_MEMORY; 482 } 483 result = Curl_blockread_all(cf, data, (char *)gss_recv_token.value, 484 gss_recv_token.length, &actualread); 485 486 if(result || (actualread != us_length)) { 487 failf(data, "Failed to receive GSS-API encryption type."); 488 gss_release_buffer(&gss_status, &gss_recv_token); 489 Curl_gss_delete_sec_context(&gss_status, &gss_context, NULL); 490 return CURLE_COULDNT_CONNECT; 491 } 492 493 if(!data->set.socks5_gssapi_nec) { 494 gss_major_status = gss_unwrap(&gss_minor_status, gss_context, 495 &gss_recv_token, &gss_w_token, 496 0, GSS_C_QOP_DEFAULT); 497 498 if(check_gss_err(data, gss_major_status, gss_minor_status, "gss_unwrap")) { 499 gss_release_buffer(&gss_status, &gss_recv_token); 500 gss_release_buffer(&gss_status, &gss_w_token); 501 Curl_gss_delete_sec_context(&gss_status, &gss_context, NULL); 502 failf(data, "Failed to unwrap GSS-API encryption value into token."); 503 return CURLE_COULDNT_CONNECT; 504 } 505 gss_release_buffer(&gss_status, &gss_recv_token); 506 507 if(gss_w_token.length != 1) { 508 failf(data, "Invalid GSS-API encryption response length (%zu).", 509 gss_w_token.length); 510 gss_release_buffer(&gss_status, &gss_w_token); 511 Curl_gss_delete_sec_context(&gss_status, &gss_context, NULL); 512 return CURLE_COULDNT_CONNECT; 513 } 514 515 memcpy(socksreq, gss_w_token.value, gss_w_token.length); 516 gss_release_buffer(&gss_status, &gss_w_token); 517 } 518 else { 519 if(gss_recv_token.length != 1) { 520 failf(data, "Invalid GSS-API encryption response length (%zu).", 521 gss_recv_token.length); 522 gss_release_buffer(&gss_status, &gss_recv_token); 523 Curl_gss_delete_sec_context(&gss_status, &gss_context, NULL); 524 return CURLE_COULDNT_CONNECT; 525 } 526 527 memcpy(socksreq, gss_recv_token.value, gss_recv_token.length); 528 gss_release_buffer(&gss_status, &gss_recv_token); 529 } 530 531 (void)curlx_nonblock(sock, TRUE); 532 533 infof(data, "SOCKS5 access with%s protection granted.", 534 (socksreq[0] == 0) ? "out GSS-API data": 535 ((socksreq[0] == 1) ? " GSS-API integrity" : 536 " GSS-API confidentiality")); 537 538 conn->socks5_gssapi_enctype = socksreq[0]; 539 if(socksreq[0] == 0) 540 Curl_gss_delete_sec_context(&gss_status, &gss_context, NULL); 541 542 return CURLE_OK; 543 } 544 545 #if defined(__GNUC__) && defined(__APPLE__) 546 #pragma GCC diagnostic pop 547 #endif 548 549 #endif /* HAVE_GSSAPI && !CURL_DISABLE_PROXY */