1 /***************************************************************************
3 * Project ___| | | | _ \| |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
8 * Copyright (C) 2009, 2011, Markus Moeller, <markus_moeller@compuserve.com>
9 * Copyright (C) 2012 - 2013, Daniel Stenberg, <daniel@haxx.se>, et al.
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.
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.
19 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
20 * KIND, either express or implied.
22 ***************************************************************************/
24 #include "curl_setup.h"
26 #if defined(USE_WINDOWS_SSPI) && !defined(CURL_DISABLE_PROXY)
34 #include "curl_sspi.h"
35 #include "curl_multibyte.h"
38 #define _MPRINTF_REPLACE /* use the internal *printf() functions */
39 #include <curl/mprintf.h>
41 #include "curl_memory.h"
42 /* The last #include file should be: */
46 * Definitions required from ntsecapi.h are directly provided below this point
47 * to avoid including ntsecapi.h due to a conflict with OpenSSL's safestack.h
49 #define KERB_WRAP_NO_ENCRYPT 0x80000001
52 * Helper sspi error functions.
54 static int check_sspi_err(struct connectdata *conn,
55 SECURITY_STATUS status,
58 if(status != SEC_E_OK &&
59 status != SEC_I_COMPLETE_AND_CONTINUE &&
60 status != SEC_I_COMPLETE_NEEDED &&
61 status != SEC_I_CONTINUE_NEEDED) {
62 failf(conn->data, "SSPI error: %s failed: %s", function,
63 Curl_sspi_strerror(conn, status));
69 /* This is the SSPI-using version of this function */
70 CURLcode Curl_SOCKS5_gssapi_negotiate(int sockindex,
71 struct connectdata *conn)
73 struct SessionHandle *data = conn->data;
74 curl_socket_t sock = conn->sock[sockindex];
79 /* Needs GSSAPI authentication */
80 SECURITY_STATUS status;
81 unsigned long sspi_ret_flags = 0;
83 SecBuffer sspi_send_token, sspi_recv_token, sspi_w_token[3];
84 SecBufferDesc input_desc, output_desc, wrap_desc;
85 SecPkgContext_Sizes sspi_sizes;
86 CredHandle cred_handle;
87 CtxtHandle sspi_context;
88 PCtxtHandle context_handle = NULL;
89 SecPkgCredentials_Names names;
91 char *service_name = NULL;
92 unsigned short us_length;
94 unsigned char socksreq[4]; /* room for gssapi exchange header only */
95 char *service = data->set.str[STRING_SOCKS5_GSSAPI_SERVICE];
97 /* GSSAPI request looks like
98 * +----+------+-----+----------------+
99 * |VER | MTYP | LEN | TOKEN |
100 * +----+------+----------------------+
101 * | 1 | 1 | 2 | up to 2^16 - 1 |
102 * +----+------+-----+----------------+
105 /* prepare service name */
106 if(strchr(service, '/')) {
107 service_name = malloc(strlen(service));
109 return CURLE_OUT_OF_MEMORY;
110 memcpy(service_name, service, strlen(service));
113 service_name = malloc(strlen(service) + strlen(conn->proxy.name) + 2);
115 return CURLE_OUT_OF_MEMORY;
116 snprintf(service_name,strlen(service) +strlen(conn->proxy.name)+2,"%s/%s",
117 service,conn->proxy.name);
120 input_desc.cBuffers = 1;
121 input_desc.pBuffers = &sspi_recv_token;
122 input_desc.ulVersion = SECBUFFER_VERSION;
124 sspi_recv_token.BufferType = SECBUFFER_TOKEN;
125 sspi_recv_token.cbBuffer = 0;
126 sspi_recv_token.pvBuffer = NULL;
128 output_desc.cBuffers = 1;
129 output_desc.pBuffers = &sspi_send_token;
130 output_desc.ulVersion = SECBUFFER_VERSION;
132 sspi_send_token.BufferType = SECBUFFER_TOKEN;
133 sspi_send_token.cbBuffer = 0;
134 sspi_send_token.pvBuffer = NULL;
136 wrap_desc.cBuffers = 3;
137 wrap_desc.pBuffers = sspi_w_token;
138 wrap_desc.ulVersion = SECBUFFER_VERSION;
140 cred_handle.dwLower = 0;
141 cred_handle.dwUpper = 0;
143 status = s_pSecFn->AcquireCredentialsHandle(NULL,
144 (TCHAR *) TEXT("Kerberos"),
145 SECPKG_CRED_OUTBOUND,
153 if(check_sspi_err(conn, status, "AcquireCredentialsHandle")) {
154 failf(data, "Failed to acquire credentials.");
155 Curl_safefree(service_name);
156 s_pSecFn->FreeCredentialsHandle(&cred_handle);
157 return CURLE_COULDNT_CONNECT;
160 /* As long as we need to keep sending some context info, and there's no */
161 /* errors, keep sending it... */
165 sname = Curl_convert_UTF8_to_tchar(service_name);
167 return CURLE_OUT_OF_MEMORY;
169 status = s_pSecFn->InitializeSecurityContext(&cred_handle,
172 ISC_REQ_MUTUAL_AUTH |
173 ISC_REQ_ALLOCATE_MEMORY |
174 ISC_REQ_CONFIDENTIALITY |
175 ISC_REQ_REPLAY_DETECT,
177 SECURITY_NATIVE_DREP,
185 Curl_unicodefree(sname);
187 if(sspi_recv_token.pvBuffer) {
188 s_pSecFn->FreeContextBuffer(sspi_recv_token.pvBuffer);
189 sspi_recv_token.pvBuffer = NULL;
190 sspi_recv_token.cbBuffer = 0;
193 if(check_sspi_err(conn, status, "InitializeSecurityContext")) {
194 Curl_safefree(service_name);
195 s_pSecFn->FreeCredentialsHandle(&cred_handle);
196 s_pSecFn->DeleteSecurityContext(&sspi_context);
197 s_pSecFn->FreeContextBuffer(sspi_recv_token.pvBuffer);
198 failf(data, "Failed to initialise security context.");
199 return CURLE_COULDNT_CONNECT;
202 if(sspi_send_token.cbBuffer != 0) {
203 socksreq[0] = 1; /* gssapi subnegotiation version */
204 socksreq[1] = 1; /* authentication message type */
205 us_length = htons((short)sspi_send_token.cbBuffer);
206 memcpy(socksreq+2, &us_length, sizeof(short));
208 code = Curl_write_plain(conn, sock, (char *)socksreq, 4, &written);
209 if((code != CURLE_OK) || (4 != written)) {
210 failf(data, "Failed to send SSPI authentication request.");
211 Curl_safefree(service_name);
212 s_pSecFn->FreeContextBuffer(sspi_send_token.pvBuffer);
213 s_pSecFn->FreeContextBuffer(sspi_recv_token.pvBuffer);
214 s_pSecFn->FreeCredentialsHandle(&cred_handle);
215 s_pSecFn->DeleteSecurityContext(&sspi_context);
216 return CURLE_COULDNT_CONNECT;
219 code = Curl_write_plain(conn, sock, (char *)sspi_send_token.pvBuffer,
220 sspi_send_token.cbBuffer, &written);
221 if((code != CURLE_OK) || (sspi_send_token.cbBuffer != (size_t)written)) {
222 failf(data, "Failed to send SSPI authentication token.");
223 Curl_safefree(service_name);
224 s_pSecFn->FreeContextBuffer(sspi_send_token.pvBuffer);
225 s_pSecFn->FreeContextBuffer(sspi_recv_token.pvBuffer);
226 s_pSecFn->FreeCredentialsHandle(&cred_handle);
227 s_pSecFn->DeleteSecurityContext(&sspi_context);
228 return CURLE_COULDNT_CONNECT;
233 s_pSecFn->FreeContextBuffer(sspi_send_token.pvBuffer);
234 sspi_send_token.pvBuffer = NULL;
235 sspi_send_token.cbBuffer = 0;
236 s_pSecFn->FreeContextBuffer(sspi_recv_token.pvBuffer);
237 sspi_recv_token.pvBuffer = NULL;
238 sspi_recv_token.cbBuffer = 0;
239 if(status != SEC_I_CONTINUE_NEEDED)
242 /* analyse response */
244 /* GSSAPI response looks like
245 * +----+------+-----+----------------+
246 * |VER | MTYP | LEN | TOKEN |
247 * +----+------+----------------------+
248 * | 1 | 1 | 2 | up to 2^16 - 1 |
249 * +----+------+-----+----------------+
252 result = Curl_blockread_all(conn, sock, (char *)socksreq, 4, &actualread);
253 if(result != CURLE_OK || actualread != 4) {
254 failf(data, "Failed to receive SSPI authentication response.");
255 Curl_safefree(service_name);
256 s_pSecFn->FreeCredentialsHandle(&cred_handle);
257 s_pSecFn->DeleteSecurityContext(&sspi_context);
258 return CURLE_COULDNT_CONNECT;
261 /* ignore the first (VER) byte */
262 if(socksreq[1] == 255) { /* status / message type */
263 failf(data, "User was rejected by the SOCKS5 server (%u %u).",
264 (unsigned int)socksreq[0], (unsigned int)socksreq[1]);
265 Curl_safefree(service_name);
266 s_pSecFn->FreeCredentialsHandle(&cred_handle);
267 s_pSecFn->DeleteSecurityContext(&sspi_context);
268 return CURLE_COULDNT_CONNECT;
271 if(socksreq[1] != 1) { /* status / messgae type */
272 failf(data, "Invalid SSPI authentication response type (%u %u).",
273 (unsigned int)socksreq[0], (unsigned int)socksreq[1]);
274 Curl_safefree(service_name);
275 s_pSecFn->FreeCredentialsHandle(&cred_handle);
276 s_pSecFn->DeleteSecurityContext(&sspi_context);
277 return CURLE_COULDNT_CONNECT;
280 memcpy(&us_length, socksreq+2, sizeof(short));
281 us_length = ntohs(us_length);
283 sspi_recv_token.cbBuffer = us_length;
284 sspi_recv_token.pvBuffer = malloc(us_length);
286 if(!sspi_recv_token.pvBuffer) {
287 Curl_safefree(service_name);
288 s_pSecFn->FreeCredentialsHandle(&cred_handle);
289 s_pSecFn->DeleteSecurityContext(&sspi_context);
290 return CURLE_OUT_OF_MEMORY;
292 result = Curl_blockread_all(conn, sock, (char *)sspi_recv_token.pvBuffer,
293 sspi_recv_token.cbBuffer, &actualread);
295 if(result != CURLE_OK || actualread != us_length) {
296 failf(data, "Failed to receive SSPI authentication token.");
297 Curl_safefree(service_name);
298 s_pSecFn->FreeContextBuffer(sspi_recv_token.pvBuffer);
299 s_pSecFn->FreeCredentialsHandle(&cred_handle);
300 s_pSecFn->DeleteSecurityContext(&sspi_context);
301 return CURLE_COULDNT_CONNECT;
304 context_handle = &sspi_context;
307 Curl_safefree(service_name);
309 /* Everything is good so far, user was authenticated! */
310 status = s_pSecFn->QueryCredentialsAttributes(&cred_handle,
311 SECPKG_CRED_ATTR_NAMES,
313 s_pSecFn->FreeCredentialsHandle(&cred_handle);
314 if(check_sspi_err(conn, status, "QueryCredentialAttributes")) {
315 s_pSecFn->DeleteSecurityContext(&sspi_context);
316 s_pSecFn->FreeContextBuffer(names.sUserName);
317 failf(data, "Failed to determine user name.");
318 return CURLE_COULDNT_CONNECT;
320 infof(data, "SOCKS5 server authencticated user %s with gssapi.\n",
322 s_pSecFn->FreeContextBuffer(names.sUserName);
325 socksreq[0] = 1; /* gssapi subnegotiation version */
326 socksreq[1] = 2; /* encryption message type */
328 gss_enc = 0; /* no data protection */
329 /* do confidentiality protection if supported */
330 if(sspi_ret_flags & ISC_REQ_CONFIDENTIALITY)
332 /* else do integrity protection */
333 else if(sspi_ret_flags & ISC_REQ_INTEGRITY)
336 infof(data, "SOCKS5 server supports gssapi %s data protection.\n",
337 (gss_enc==0)?"no":((gss_enc==1)?"integrity":"confidentiality") );
338 /* force to no data protection, avoid encryption/decryption for now */
341 * Sending the encryption type in clear seems wrong. It should be
342 * protected with gss_seal()/gss_wrap(). See RFC1961 extract below
343 * The NEC reference implementations on which this is based is
346 * +------+------+------+.......................+
347 * + ver | mtyp | len | token |
348 * +------+------+------+.......................+
349 * + 0x01 | 0x02 | 0x02 | up to 2^16 - 1 octets |
350 * +------+------+------+.......................+
354 * - "ver" is the protocol version number, here 1 to represent the
355 * first version of the SOCKS/GSS-API protocol
357 * - "mtyp" is the message type, here 2 to represent a protection
358 * -level negotiation message
360 * - "len" is the length of the "token" field in octets
362 * - "token" is the GSS-API encapsulated protection level
364 * The token is produced by encapsulating an octet containing the
365 * required protection level using gss_seal()/gss_wrap() with conf_req
366 * set to FALSE. The token is verified using gss_unseal()/
371 if(data->set.socks5_gssapi_nec) {
372 us_length = htons((short)1);
373 memcpy(socksreq+2, &us_length, sizeof(short));
376 status = s_pSecFn->QueryContextAttributes(&sspi_context,
379 if(check_sspi_err(conn, status, "QueryContextAttributes")) {
380 s_pSecFn->DeleteSecurityContext(&sspi_context);
381 failf(data, "Failed to query security context attributes.");
382 return CURLE_COULDNT_CONNECT;
385 sspi_w_token[0].cbBuffer = sspi_sizes.cbSecurityTrailer;
386 sspi_w_token[0].BufferType = SECBUFFER_TOKEN;
387 sspi_w_token[0].pvBuffer = malloc(sspi_sizes.cbSecurityTrailer);
389 if(!sspi_w_token[0].pvBuffer) {
390 s_pSecFn->DeleteSecurityContext(&sspi_context);
391 return CURLE_OUT_OF_MEMORY;
394 sspi_w_token[1].cbBuffer = 1;
395 sspi_w_token[1].pvBuffer = malloc(1);
396 if(!sspi_w_token[1].pvBuffer) {
397 s_pSecFn->FreeContextBuffer(sspi_w_token[0].pvBuffer);
398 s_pSecFn->DeleteSecurityContext(&sspi_context);
399 return CURLE_OUT_OF_MEMORY;
402 memcpy(sspi_w_token[1].pvBuffer,&gss_enc,1);
403 sspi_w_token[2].BufferType = SECBUFFER_PADDING;
404 sspi_w_token[2].cbBuffer = sspi_sizes.cbBlockSize;
405 sspi_w_token[2].pvBuffer = malloc(sspi_sizes.cbBlockSize);
406 if(!sspi_w_token[2].pvBuffer) {
407 s_pSecFn->FreeContextBuffer(sspi_w_token[0].pvBuffer);
408 s_pSecFn->FreeContextBuffer(sspi_w_token[1].pvBuffer);
409 s_pSecFn->DeleteSecurityContext(&sspi_context);
410 return CURLE_OUT_OF_MEMORY;
412 status = s_pSecFn->EncryptMessage(&sspi_context,
413 KERB_WRAP_NO_ENCRYPT,
416 if(check_sspi_err(conn, status, "EncryptMessage")) {
417 s_pSecFn->FreeContextBuffer(sspi_w_token[0].pvBuffer);
418 s_pSecFn->FreeContextBuffer(sspi_w_token[1].pvBuffer);
419 s_pSecFn->FreeContextBuffer(sspi_w_token[2].pvBuffer);
420 s_pSecFn->DeleteSecurityContext(&sspi_context);
421 failf(data, "Failed to query security context attributes.");
422 return CURLE_COULDNT_CONNECT;
424 sspi_send_token.cbBuffer = sspi_w_token[0].cbBuffer
425 + sspi_w_token[1].cbBuffer
426 + sspi_w_token[2].cbBuffer;
427 sspi_send_token.pvBuffer = malloc(sspi_send_token.cbBuffer);
428 if(!sspi_send_token.pvBuffer) {
429 s_pSecFn->FreeContextBuffer(sspi_w_token[0].pvBuffer);
430 s_pSecFn->FreeContextBuffer(sspi_w_token[1].pvBuffer);
431 s_pSecFn->FreeContextBuffer(sspi_w_token[2].pvBuffer);
432 s_pSecFn->DeleteSecurityContext(&sspi_context);
433 return CURLE_OUT_OF_MEMORY;
436 memcpy(sspi_send_token.pvBuffer, sspi_w_token[0].pvBuffer,
437 sspi_w_token[0].cbBuffer);
438 memcpy((PUCHAR) sspi_send_token.pvBuffer +(int)sspi_w_token[0].cbBuffer,
439 sspi_w_token[1].pvBuffer, sspi_w_token[1].cbBuffer);
440 memcpy((PUCHAR) sspi_send_token.pvBuffer
441 +sspi_w_token[0].cbBuffer
442 +sspi_w_token[1].cbBuffer,
443 sspi_w_token[2].pvBuffer, sspi_w_token[2].cbBuffer);
445 s_pSecFn->FreeContextBuffer(sspi_w_token[0].pvBuffer);
446 sspi_w_token[0].pvBuffer = NULL;
447 sspi_w_token[0].cbBuffer = 0;
448 s_pSecFn->FreeContextBuffer(sspi_w_token[1].pvBuffer);
449 sspi_w_token[1].pvBuffer = NULL;
450 sspi_w_token[1].cbBuffer = 0;
451 s_pSecFn->FreeContextBuffer(sspi_w_token[2].pvBuffer);
452 sspi_w_token[2].pvBuffer = NULL;
453 sspi_w_token[2].cbBuffer = 0;
455 us_length = htons((short)sspi_send_token.cbBuffer);
456 memcpy(socksreq+2,&us_length,sizeof(short));
459 code = Curl_write_plain(conn, sock, (char *)socksreq, 4, &written);
460 if((code != CURLE_OK) || (4 != written)) {
461 failf(data, "Failed to send SSPI encryption request.");
462 s_pSecFn->FreeContextBuffer(sspi_send_token.pvBuffer);
463 s_pSecFn->DeleteSecurityContext(&sspi_context);
464 return CURLE_COULDNT_CONNECT;
467 if(data->set.socks5_gssapi_nec) {
468 memcpy(socksreq,&gss_enc,1);
469 code = Curl_write_plain(conn, sock, (char *)socksreq, 1, &written);
470 if((code != CURLE_OK) || (1 != written)) {
471 failf(data, "Failed to send SSPI encryption type.");
472 s_pSecFn->DeleteSecurityContext(&sspi_context);
473 return CURLE_COULDNT_CONNECT;
477 code = Curl_write_plain(conn, sock, (char *)sspi_send_token.pvBuffer,
478 sspi_send_token.cbBuffer, &written);
479 if((code != CURLE_OK) || (sspi_send_token.cbBuffer != (size_t)written)) {
480 failf(data, "Failed to send SSPI encryption type.");
481 s_pSecFn->FreeContextBuffer(sspi_send_token.pvBuffer);
482 s_pSecFn->DeleteSecurityContext(&sspi_context);
483 return CURLE_COULDNT_CONNECT;
485 s_pSecFn->FreeContextBuffer(sspi_send_token.pvBuffer);
488 result = Curl_blockread_all(conn, sock, (char *)socksreq, 4, &actualread);
489 if(result != CURLE_OK || actualread != 4) {
490 failf(data, "Failed to receive SSPI encryption response.");
491 s_pSecFn->DeleteSecurityContext(&sspi_context);
492 return CURLE_COULDNT_CONNECT;
495 /* ignore the first (VER) byte */
496 if(socksreq[1] == 255) { /* status / message type */
497 failf(data, "User was rejected by the SOCKS5 server (%u %u).",
498 (unsigned int)socksreq[0], (unsigned int)socksreq[1]);
499 s_pSecFn->DeleteSecurityContext(&sspi_context);
500 return CURLE_COULDNT_CONNECT;
503 if(socksreq[1] != 2) { /* status / message type */
504 failf(data, "Invalid SSPI encryption response type (%u %u).",
505 (unsigned int)socksreq[0], (unsigned int)socksreq[1]);
506 s_pSecFn->DeleteSecurityContext(&sspi_context);
507 return CURLE_COULDNT_CONNECT;
510 memcpy(&us_length, socksreq+2, sizeof(short));
511 us_length = ntohs(us_length);
513 sspi_w_token[0].cbBuffer = us_length;
514 sspi_w_token[0].pvBuffer = malloc(us_length);
515 if(!sspi_w_token[0].pvBuffer) {
516 s_pSecFn->DeleteSecurityContext(&sspi_context);
517 return CURLE_OUT_OF_MEMORY;
520 result = Curl_blockread_all(conn, sock, (char *)sspi_w_token[0].pvBuffer,
521 sspi_w_token[0].cbBuffer, &actualread);
523 if(result != CURLE_OK || actualread != us_length) {
524 failf(data, "Failed to receive SSPI encryption type.");
525 s_pSecFn->FreeContextBuffer(sspi_w_token[0].pvBuffer);
526 s_pSecFn->DeleteSecurityContext(&sspi_context);
527 return CURLE_COULDNT_CONNECT;
531 if(!data->set.socks5_gssapi_nec) {
532 wrap_desc.cBuffers = 2;
533 sspi_w_token[0].BufferType = SECBUFFER_STREAM;
534 sspi_w_token[1].BufferType = SECBUFFER_DATA;
535 sspi_w_token[1].cbBuffer = 0;
536 sspi_w_token[1].pvBuffer = NULL;
538 status = s_pSecFn->DecryptMessage(&sspi_context,
543 if(check_sspi_err(conn, status, "DecryptMessage")) {
544 s_pSecFn->FreeContextBuffer(sspi_w_token[0].pvBuffer);
545 s_pSecFn->FreeContextBuffer(sspi_w_token[1].pvBuffer);
546 s_pSecFn->DeleteSecurityContext(&sspi_context);
547 failf(data, "Failed to query security context attributes.");
548 return CURLE_COULDNT_CONNECT;
551 if(sspi_w_token[1].cbBuffer != 1) {
552 failf(data, "Invalid SSPI encryption response length (%lu).",
553 (unsigned long)sspi_w_token[1].cbBuffer);
554 s_pSecFn->FreeContextBuffer(sspi_w_token[0].pvBuffer);
555 s_pSecFn->FreeContextBuffer(sspi_w_token[1].pvBuffer);
556 s_pSecFn->DeleteSecurityContext(&sspi_context);
557 return CURLE_COULDNT_CONNECT;
560 memcpy(socksreq,sspi_w_token[1].pvBuffer,sspi_w_token[1].cbBuffer);
561 s_pSecFn->FreeContextBuffer(sspi_w_token[0].pvBuffer);
562 s_pSecFn->FreeContextBuffer(sspi_w_token[1].pvBuffer);
565 if(sspi_w_token[0].cbBuffer != 1) {
566 failf(data, "Invalid SSPI encryption response length (%lu).",
567 (unsigned long)sspi_w_token[0].cbBuffer);
568 s_pSecFn->FreeContextBuffer(sspi_w_token[0].pvBuffer);
569 s_pSecFn->DeleteSecurityContext(&sspi_context);
570 return CURLE_COULDNT_CONNECT;
572 memcpy(socksreq,sspi_w_token[0].pvBuffer,sspi_w_token[0].cbBuffer);
573 s_pSecFn->FreeContextBuffer(sspi_w_token[0].pvBuffer);
576 infof(data, "SOCKS5 access with%s protection granted.\n",
577 (socksreq[0]==0)?"out gssapi data":
578 ((socksreq[0]==1)?" gssapi integrity":" gssapi confidentiality"));
580 /* For later use if encryption is required
581 conn->socks5_gssapi_enctype = socksreq[0];
583 conn->socks5_sspi_context = sspi_context;
585 s_pSecFn->DeleteSecurityContext(&sspi_context);
586 conn->socks5_sspi_context = sspi_context;