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