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