1/*************************************************************************** 2 * _ _ ____ _ 3 * Project ___| | | | _ \| | 4 * / __| | | | |_) | | 5 * | (__| |_| | _ <| |___ 6 * \___|\___/|_| \_\_____| 7 * 8 * Copyright (C) 2009, 2011, Markus Moeller, <markus_moeller@compuserve.com> 9 * Copyright (C) 2012, Daniel Stenberg, <daniel@haxx.se>, et al. 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 http://curl.haxx.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 ***************************************************************************/ 23 24#include "curl_setup.h" 25 26#ifndef CURL_DISABLE_PROXY 27 28#ifdef HAVE_GSSAPI 29#ifdef HAVE_OLD_GSSMIT 30#define GSS_C_NT_HOSTBASED_SERVICE gss_nt_service_name 31#define NCOMPAT 1 32#endif 33#ifndef gss_nt_service_name 34#define gss_nt_service_name GSS_C_NT_HOSTBASED_SERVICE 35#endif 36 37#include "curl_gssapi.h" 38#include "urldata.h" 39#include "sendf.h" 40#include "connect.h" 41#include "timeval.h" 42#include "socks.h" 43#include "warnless.h" 44 45#define _MPRINTF_REPLACE /* use our functions only */ 46#include <curl/mprintf.h> 47 48#include "curl_memory.h" 49/* The last #include file should be: */ 50#include "memdebug.h" 51 52static gss_ctx_id_t gss_context = GSS_C_NO_CONTEXT; 53 54/* 55 * Helper gssapi error functions. 56 */ 57static int check_gss_err(struct SessionHandle *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; 66 char buf[1024]; 67 size_t len; 68 69 len = 0; 70 msg_ctx = 0; 71 while(!msg_ctx) { 72 /* convert major status code (GSS-API error) to text */ 73 maj_stat = gss_display_status(&min_stat, major_status, 74 GSS_C_GSS_CODE, 75 GSS_C_NULL_OID, 76 &msg_ctx, &status_string); 77 if(maj_stat == GSS_S_COMPLETE) { 78 if(sizeof(buf) > len + status_string.length + 1) { 79 strcpy(buf+len, (char*) status_string.value); 80 len += status_string.length; 81 } 82 gss_release_buffer(&min_stat, &status_string); 83 break; 84 } 85 gss_release_buffer(&min_stat, &status_string); 86 } 87 if(sizeof(buf) > len + 3) { 88 strcpy(buf+len, ".\n"); 89 len += 2; 90 } 91 msg_ctx = 0; 92 while(!msg_ctx) { 93 /* convert minor status code (underlying routine error) to text */ 94 maj_stat = gss_display_status(&min_stat, minor_status, 95 GSS_C_MECH_CODE, 96 GSS_C_NULL_OID, 97 &msg_ctx, &status_string); 98 if(maj_stat == GSS_S_COMPLETE) { 99 if(sizeof(buf) > len + status_string.length) 100 strcpy(buf+len, (char*) status_string.value); 101 gss_release_buffer(&min_stat, &status_string); 102 break; 103 } 104 gss_release_buffer(&min_stat, &status_string); 105 } 106 failf(data, "GSSAPI error: %s failed:\n%s", function, buf); 107 return(1); 108 } 109 110 return(0); 111} 112 113CURLcode Curl_SOCKS5_gssapi_negotiate(int sockindex, 114 struct connectdata *conn) 115{ 116 struct SessionHandle *data = conn->data; 117 curl_socket_t sock = conn->sock[sockindex]; 118 CURLcode code; 119 ssize_t actualread; 120 ssize_t written; 121 int result; 122 OM_uint32 gss_major_status, gss_minor_status, gss_status; 123 OM_uint32 gss_ret_flags; 124 int gss_conf_state, gss_enc; 125 gss_buffer_desc service = GSS_C_EMPTY_BUFFER; 126 gss_buffer_desc gss_send_token = GSS_C_EMPTY_BUFFER; 127 gss_buffer_desc gss_recv_token = GSS_C_EMPTY_BUFFER; 128 gss_buffer_desc gss_w_token = GSS_C_EMPTY_BUFFER; 129 gss_buffer_desc* gss_token = GSS_C_NO_BUFFER; 130 gss_name_t server = GSS_C_NO_NAME; 131 gss_name_t gss_client_name = GSS_C_NO_NAME; 132 unsigned short us_length; 133 char *user=NULL; 134 unsigned char socksreq[4]; /* room for gssapi exchange header only */ 135 char *serviceptr = data->set.str[STRING_SOCKS5_GSSAPI_SERVICE]; 136 137 /* GSSAPI request looks like 138 * +----+------+-----+----------------+ 139 * |VER | MTYP | LEN | TOKEN | 140 * +----+------+----------------------+ 141 * | 1 | 1 | 2 | up to 2^16 - 1 | 142 * +----+------+-----+----------------+ 143 */ 144 145 /* prepare service name */ 146 if(strchr(serviceptr,'/')) { 147 service.value = malloc(strlen(serviceptr)); 148 if(!service.value) 149 return CURLE_OUT_OF_MEMORY; 150 service.length = strlen(serviceptr); 151 memcpy(service.value, serviceptr, service.length); 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(strlen(serviceptr) +strlen(conn->proxy.name)+2); 158 if(!service.value) 159 return CURLE_OUT_OF_MEMORY; 160 service.length = strlen(serviceptr) +strlen(conn->proxy.name)+1; 161 snprintf(service.value, service.length+1, "%s@%s", 162 serviceptr, conn->proxy.name); 163 164 gss_major_status = gss_import_name(&gss_minor_status, &service, 165 gss_nt_service_name, &server); 166 } 167 168 gss_release_buffer(&gss_status, &service); /* clear allocated memory */ 169 170 if(check_gss_err(data,gss_major_status, 171 gss_minor_status,"gss_import_name()")) { 172 failf(data, "Failed to create service name."); 173 gss_release_name(&gss_status, &server); 174 return CURLE_COULDNT_CONNECT; 175 } 176 177 /* As long as we need to keep sending some context info, and there's no */ 178 /* errors, keep sending it... */ 179 for(;;) { 180 gss_major_status = Curl_gss_init_sec_context(data, 181 &gss_minor_status, 182 &gss_context, 183 server, 184 NULL, 185 gss_token, 186 &gss_send_token, 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 GSSAPI token."); 198 return CURLE_COULDNT_CONNECT; 199 } 200 201 if(gss_send_token.length != 0) { 202 socksreq[0] = 1; /* gssapi 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 code = Curl_write_plain(conn, sock, (char *)socksreq, 4, &written); 208 if((code != CURLE_OK) || (4 != written)) { 209 failf(data, "Failed to send GSSAPI 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 code = Curl_write_plain(conn, sock, (char *)gss_send_token.value, 218 gss_send_token.length, &written); 219 220 if((code != CURLE_OK) || ((ssize_t)gss_send_token.length != written)) { 221 failf(data, "Failed to send GSSAPI 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) break; 234 235 /* analyse response */ 236 237 /* GSSAPI response looks like 238 * +----+------+-----+----------------+ 239 * |VER | MTYP | LEN | TOKEN | 240 * +----+------+----------------------+ 241 * | 1 | 1 | 2 | up to 2^16 - 1 | 242 * +----+------+-----+----------------+ 243 */ 244 245 result=Curl_blockread_all(conn, sock, (char *)socksreq, 4, &actualread); 246 if(result != CURLE_OK || actualread != 4) { 247 failf(data, "Failed to receive GSSAPI authentication response."); 248 gss_release_name(&gss_status, &server); 249 gss_delete_sec_context(&gss_status, &gss_context, NULL); 250 return CURLE_COULDNT_CONNECT; 251 } 252 253 /* ignore the first (VER) byte */ 254 if(socksreq[1] == 255) { /* status / message type */ 255 failf(data, "User was rejected by the SOCKS5 server (%d %d).", 256 socksreq[0], socksreq[1]); 257 gss_release_name(&gss_status, &server); 258 gss_delete_sec_context(&gss_status, &gss_context, NULL); 259 return CURLE_COULDNT_CONNECT; 260 } 261 262 if(socksreq[1] != 1) { /* status / messgae type */ 263 failf(data, "Invalid GSSAPI authentication response type (%d %d).", 264 socksreq[0], socksreq[1]); 265 gss_release_name(&gss_status, &server); 266 gss_delete_sec_context(&gss_status, &gss_context, NULL); 267 return CURLE_COULDNT_CONNECT; 268 } 269 270 memcpy(&us_length, socksreq+2, sizeof(short)); 271 us_length = ntohs(us_length); 272 273 gss_recv_token.length=us_length; 274 gss_recv_token.value=malloc(us_length); 275 if(!gss_recv_token.value) { 276 failf(data, 277 "Could not allocate memory for GSSAPI authentication " 278 "response token."); 279 gss_release_name(&gss_status, &server); 280 gss_delete_sec_context(&gss_status, &gss_context, NULL); 281 return CURLE_OUT_OF_MEMORY; 282 } 283 284 result=Curl_blockread_all(conn, sock, (char *)gss_recv_token.value, 285 gss_recv_token.length, &actualread); 286 287 if(result != CURLE_OK || actualread != us_length) { 288 failf(data, "Failed to receive GSSAPI authentication token."); 289 gss_release_name(&gss_status, &server); 290 gss_release_buffer(&gss_status, &gss_recv_token); 291 gss_delete_sec_context(&gss_status, &gss_context, NULL); 292 return CURLE_COULDNT_CONNECT; 293 } 294 295 gss_token = &gss_recv_token; 296 } 297 298 gss_release_name(&gss_status, &server); 299 300 /* Everything is good so far, user was authenticated! */ 301 gss_major_status = gss_inquire_context (&gss_minor_status, gss_context, 302 &gss_client_name, NULL, NULL, NULL, 303 NULL, NULL, NULL); 304 if(check_gss_err(data,gss_major_status, 305 gss_minor_status,"gss_inquire_context")) { 306 gss_delete_sec_context(&gss_status, &gss_context, NULL); 307 gss_release_name(&gss_status, &gss_client_name); 308 failf(data, "Failed to determine user name."); 309 return CURLE_COULDNT_CONNECT; 310 } 311 gss_major_status = gss_display_name(&gss_minor_status, gss_client_name, 312 &gss_send_token, NULL); 313 if(check_gss_err(data,gss_major_status, 314 gss_minor_status,"gss_display_name")) { 315 gss_delete_sec_context(&gss_status, &gss_context, NULL); 316 gss_release_name(&gss_status, &gss_client_name); 317 gss_release_buffer(&gss_status, &gss_send_token); 318 failf(data, "Failed to determine user name."); 319 return CURLE_COULDNT_CONNECT; 320 } 321 user=malloc(gss_send_token.length+1); 322 if(!user) { 323 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 return CURLE_OUT_OF_MEMORY; 327 } 328 329 memcpy(user, gss_send_token.value, gss_send_token.length); 330 user[gss_send_token.length] = '\0'; 331 gss_release_name(&gss_status, &gss_client_name); 332 gss_release_buffer(&gss_status, &gss_send_token); 333 infof(data, "SOCKS5 server authencticated user %s with gssapi.\n",user); 334 free(user); 335 user=NULL; 336 337 /* Do encryption */ 338 socksreq[0] = 1; /* gssapi subnegotiation version */ 339 socksreq[1] = 2; /* encryption message type */ 340 341 gss_enc = 0; /* no data protection */ 342 /* do confidentiality protection if supported */ 343 if(gss_ret_flags & GSS_C_CONF_FLAG) 344 gss_enc = 2; 345 /* else do integrity protection */ 346 else if(gss_ret_flags & GSS_C_INTEG_FLAG) 347 gss_enc = 1; 348 349 infof(data, "SOCKS5 server supports gssapi %s data protection.\n", 350 (gss_enc==0)?"no":((gss_enc==1)?"integrity":"confidentiality")); 351 /* force for the moment to no data protection */ 352 gss_enc = 0; 353 /* 354 * Sending the encryption type in clear seems wrong. It should be 355 * protected with gss_seal()/gss_wrap(). See RFC1961 extract below 356 * The NEC reference implementations on which this is based is 357 * therefore at fault 358 * 359 * +------+------+------+.......................+ 360 * + ver | mtyp | len | token | 361 * +------+------+------+.......................+ 362 * + 0x01 | 0x02 | 0x02 | up to 2^16 - 1 octets | 363 * +------+------+------+.......................+ 364 * 365 * Where: 366 * 367 * - "ver" is the protocol version number, here 1 to represent the 368 * first version of the SOCKS/GSS-API protocol 369 * 370 * - "mtyp" is the message type, here 2 to represent a protection 371 * -level negotiation message 372 * 373 * - "len" is the length of the "token" field in octets 374 * 375 * - "token" is the GSS-API encapsulated protection level 376 * 377 * The token is produced by encapsulating an octet containing the 378 * required protection level using gss_seal()/gss_wrap() with conf_req 379 * set to FALSE. The token is verified using gss_unseal()/ 380 * gss_unwrap(). 381 * 382 */ 383 if(data->set.socks5_gssapi_nec) { 384 us_length = htons((short)1); 385 memcpy(socksreq+2,&us_length,sizeof(short)); 386 } 387 else { 388 gss_send_token.length = 1; 389 gss_send_token.value = malloc(1); 390 if(!gss_send_token.value) { 391 gss_delete_sec_context(&gss_status, &gss_context, NULL); 392 return CURLE_OUT_OF_MEMORY; 393 } 394 memcpy(gss_send_token.value, &gss_enc, 1); 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 GSSAPI 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 code = Curl_write_plain(conn, sock, (char *)socksreq, 4, &written); 414 if((code != CURLE_OK) || (4 != written)) { 415 failf(data, "Failed to send GSSAPI 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 code = Curl_write_plain(conn, sock, socksreq, 1, &written); 424 if((code != CURLE_OK) || ( 1 != written)) { 425 failf(data, "Failed to send GSSAPI encryption type."); 426 gss_delete_sec_context(&gss_status, &gss_context, NULL); 427 return CURLE_COULDNT_CONNECT; 428 } 429 } 430 else { 431 code = Curl_write_plain(conn, sock, (char *)gss_w_token.value, 432 gss_w_token.length, &written); 433 if((code != CURLE_OK) || ((ssize_t)gss_w_token.length != written)) { 434 failf(data, "Failed to send GSSAPI encryption type."); 435 gss_release_buffer(&gss_status, &gss_w_token); 436 gss_delete_sec_context(&gss_status, &gss_context, NULL); 437 return CURLE_COULDNT_CONNECT; 438 } 439 gss_release_buffer(&gss_status, &gss_w_token); 440 } 441 442 result=Curl_blockread_all(conn, sock, (char *)socksreq, 4, &actualread); 443 if(result != CURLE_OK || actualread != 4) { 444 failf(data, "Failed to receive GSSAPI encryption response."); 445 gss_delete_sec_context(&gss_status, &gss_context, NULL); 446 return CURLE_COULDNT_CONNECT; 447 } 448 449 /* ignore the first (VER) byte */ 450 if(socksreq[1] == 255) { /* status / message type */ 451 failf(data, "User was rejected by the SOCKS5 server (%d %d).", 452 socksreq[0], socksreq[1]); 453 gss_delete_sec_context(&gss_status, &gss_context, NULL); 454 return CURLE_COULDNT_CONNECT; 455 } 456 457 if(socksreq[1] != 2) { /* status / messgae type */ 458 failf(data, "Invalid GSSAPI encryption response type (%d %d).", 459 socksreq[0], socksreq[1]); 460 gss_delete_sec_context(&gss_status, &gss_context, NULL); 461 return CURLE_COULDNT_CONNECT; 462 } 463 464 memcpy(&us_length, socksreq+2, sizeof(short)); 465 us_length = ntohs(us_length); 466 467 gss_recv_token.length= us_length; 468 gss_recv_token.value=malloc(gss_recv_token.length); 469 if(!gss_recv_token.value) { 470 gss_delete_sec_context(&gss_status, &gss_context, NULL); 471 return CURLE_OUT_OF_MEMORY; 472 } 473 result=Curl_blockread_all(conn, sock, (char *)gss_recv_token.value, 474 gss_recv_token.length, &actualread); 475 476 if(result != CURLE_OK || actualread != us_length) { 477 failf(data, "Failed to receive GSSAPI encryptrion type."); 478 gss_release_buffer(&gss_status, &gss_recv_token); 479 gss_delete_sec_context(&gss_status, &gss_context, NULL); 480 return CURLE_COULDNT_CONNECT; 481 } 482 483 if(!data->set.socks5_gssapi_nec) { 484 gss_major_status = gss_unwrap(&gss_minor_status, gss_context, 485 &gss_recv_token, &gss_w_token, 486 0, GSS_C_QOP_DEFAULT); 487 488 if(check_gss_err(data,gss_major_status,gss_minor_status,"gss_unwrap")) { 489 gss_release_buffer(&gss_status, &gss_recv_token); 490 gss_release_buffer(&gss_status, &gss_w_token); 491 gss_delete_sec_context(&gss_status, &gss_context, NULL); 492 failf(data, "Failed to unwrap GSSAPI encryption value into token."); 493 return CURLE_COULDNT_CONNECT; 494 } 495 gss_release_buffer(&gss_status, &gss_recv_token); 496 497 if(gss_w_token.length != 1) { 498 failf(data, "Invalid GSSAPI encryption response length (%d).", 499 gss_w_token.length); 500 gss_release_buffer(&gss_status, &gss_w_token); 501 gss_delete_sec_context(&gss_status, &gss_context, NULL); 502 return CURLE_COULDNT_CONNECT; 503 } 504 505 memcpy(socksreq,gss_w_token.value,gss_w_token.length); 506 gss_release_buffer(&gss_status, &gss_w_token); 507 } 508 else { 509 if(gss_recv_token.length != 1) { 510 failf(data, "Invalid GSSAPI encryption response length (%d).", 511 gss_recv_token.length); 512 gss_release_buffer(&gss_status, &gss_recv_token); 513 gss_delete_sec_context(&gss_status, &gss_context, NULL); 514 return CURLE_COULDNT_CONNECT; 515 } 516 517 memcpy(socksreq,gss_recv_token.value,gss_recv_token.length); 518 gss_release_buffer(&gss_status, &gss_recv_token); 519 } 520 521 infof(data, "SOCKS5 access with%s protection granted.\n", 522 (socksreq[0]==0)?"out gssapi data": 523 ((socksreq[0]==1)?" gssapi integrity":" gssapi confidentiality")); 524 525 conn->socks5_gssapi_enctype = socksreq[0]; 526 if(socksreq[0] == 0) 527 gss_delete_sec_context(&gss_status, &gss_context, NULL); 528 529 return CURLE_OK; 530} 531#endif 532 533#endif /* CURL_DISABLE_PROXY */ 534