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