1 /***************************************************************************
3 * Project ___| | | | _ \| |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
8 * Copyright (C) 2012 - 2016, Daniel Stenberg, <daniel@haxx.se>, et al.
9 * Copyright (C) 2009, 2011, Markus Moeller, <markus_moeller@compuserve.com>
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.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 /* The last 3 #include files should be in this order */
39 #include "curl_printf.h"
40 #include "curl_memory.h"
44 * Helper sspi error functions.
46 static int check_sspi_err(struct connectdata *conn,
47 SECURITY_STATUS status,
50 if(status != SEC_E_OK &&
51 status != SEC_I_COMPLETE_AND_CONTINUE &&
52 status != SEC_I_COMPLETE_NEEDED &&
53 status != SEC_I_CONTINUE_NEEDED) {
54 failf(conn->data, "SSPI error: %s failed: %s", function,
55 Curl_sspi_strerror(conn, status));
61 /* This is the SSPI-using version of this function */
62 CURLcode Curl_SOCKS5_gssapi_negotiate(int sockindex,
63 struct connectdata *conn)
65 struct Curl_easy *data = conn->data;
66 curl_socket_t sock = conn->sock[sockindex];
71 /* Needs GSS-API authentication */
72 SECURITY_STATUS status;
73 unsigned long sspi_ret_flags = 0;
74 unsigned char gss_enc;
75 SecBuffer sspi_send_token, sspi_recv_token, sspi_w_token[3];
76 SecBufferDesc input_desc, output_desc, wrap_desc;
77 SecPkgContext_Sizes sspi_sizes;
78 CredHandle cred_handle;
79 CtxtHandle sspi_context;
80 PCtxtHandle context_handle = NULL;
81 SecPkgCredentials_Names names;
83 char *service_name = NULL;
84 unsigned short us_length;
86 unsigned char socksreq[4]; /* room for GSS-API exchange header only */
87 const char *service = data->set.str[STRING_PROXY_SERVICE_NAME] ?
88 data->set.str[STRING_PROXY_SERVICE_NAME] : "rcmd";
89 const size_t service_length = strlen(service);
91 /* GSS-API request looks like
92 * +----+------+-----+----------------+
93 * |VER | MTYP | LEN | TOKEN |
94 * +----+------+----------------------+
95 * | 1 | 1 | 2 | up to 2^16 - 1 |
96 * +----+------+-----+----------------+
99 /* prepare service name */
100 if(strchr(service, '/')) {
101 service_name = strdup(service);
103 return CURLE_OUT_OF_MEMORY;
106 service_name = malloc(service_length +
107 strlen(conn->socks_proxy.host.name) + 2);
109 return CURLE_OUT_OF_MEMORY;
110 snprintf(service_name, service_length +
111 strlen(conn->socks_proxy.host.name)+2, "%s/%s",
112 service, conn->socks_proxy.host.name);
115 input_desc.cBuffers = 1;
116 input_desc.pBuffers = &sspi_recv_token;
117 input_desc.ulVersion = SECBUFFER_VERSION;
119 sspi_recv_token.BufferType = SECBUFFER_TOKEN;
120 sspi_recv_token.cbBuffer = 0;
121 sspi_recv_token.pvBuffer = NULL;
123 output_desc.cBuffers = 1;
124 output_desc.pBuffers = &sspi_send_token;
125 output_desc.ulVersion = SECBUFFER_VERSION;
127 sspi_send_token.BufferType = SECBUFFER_TOKEN;
128 sspi_send_token.cbBuffer = 0;
129 sspi_send_token.pvBuffer = NULL;
131 wrap_desc.cBuffers = 3;
132 wrap_desc.pBuffers = sspi_w_token;
133 wrap_desc.ulVersion = SECBUFFER_VERSION;
135 cred_handle.dwLower = 0;
136 cred_handle.dwUpper = 0;
138 status = s_pSecFn->AcquireCredentialsHandle(NULL,
139 (TCHAR *) TEXT("Kerberos"),
140 SECPKG_CRED_OUTBOUND,
148 if(check_sspi_err(conn, status, "AcquireCredentialsHandle")) {
149 failf(data, "Failed to acquire credentials.");
151 s_pSecFn->FreeCredentialsHandle(&cred_handle);
152 return CURLE_COULDNT_CONNECT;
155 /* As long as we need to keep sending some context info, and there's no */
156 /* errors, keep sending it... */
160 sname = Curl_convert_UTF8_to_tchar(service_name);
162 return CURLE_OUT_OF_MEMORY;
164 status = s_pSecFn->InitializeSecurityContext(&cred_handle,
167 ISC_REQ_MUTUAL_AUTH |
168 ISC_REQ_ALLOCATE_MEMORY |
169 ISC_REQ_CONFIDENTIALITY |
170 ISC_REQ_REPLAY_DETECT,
172 SECURITY_NATIVE_DREP,
180 Curl_unicodefree(sname);
182 if(sspi_recv_token.pvBuffer) {
183 s_pSecFn->FreeContextBuffer(sspi_recv_token.pvBuffer);
184 sspi_recv_token.pvBuffer = NULL;
185 sspi_recv_token.cbBuffer = 0;
188 if(check_sspi_err(conn, status, "InitializeSecurityContext")) {
190 s_pSecFn->FreeCredentialsHandle(&cred_handle);
191 s_pSecFn->DeleteSecurityContext(&sspi_context);
192 if(sspi_recv_token.pvBuffer)
193 s_pSecFn->FreeContextBuffer(sspi_recv_token.pvBuffer);
194 failf(data, "Failed to initialise security context.");
195 return CURLE_COULDNT_CONNECT;
198 if(sspi_send_token.cbBuffer != 0) {
199 socksreq[0] = 1; /* GSS-API subnegotiation version */
200 socksreq[1] = 1; /* authentication message type */
201 us_length = htons((short)sspi_send_token.cbBuffer);
202 memcpy(socksreq+2, &us_length, sizeof(short));
204 code = Curl_write_plain(conn, sock, (char *)socksreq, 4, &written);
205 if(code || (4 != written)) {
206 failf(data, "Failed to send SSPI authentication request.");
208 if(sspi_send_token.pvBuffer)
209 s_pSecFn->FreeContextBuffer(sspi_send_token.pvBuffer);
210 if(sspi_recv_token.pvBuffer)
211 s_pSecFn->FreeContextBuffer(sspi_recv_token.pvBuffer);
212 s_pSecFn->FreeCredentialsHandle(&cred_handle);
213 s_pSecFn->DeleteSecurityContext(&sspi_context);
214 return CURLE_COULDNT_CONNECT;
217 code = Curl_write_plain(conn, sock, (char *)sspi_send_token.pvBuffer,
218 sspi_send_token.cbBuffer, &written);
219 if(code || (sspi_send_token.cbBuffer != (size_t)written)) {
220 failf(data, "Failed to send SSPI authentication token.");
222 if(sspi_send_token.pvBuffer)
223 s_pSecFn->FreeContextBuffer(sspi_send_token.pvBuffer);
224 if(sspi_recv_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 if(sspi_send_token.pvBuffer) {
234 s_pSecFn->FreeContextBuffer(sspi_send_token.pvBuffer);
235 sspi_send_token.pvBuffer = NULL;
237 sspi_send_token.cbBuffer = 0;
239 if(sspi_recv_token.pvBuffer) {
240 s_pSecFn->FreeContextBuffer(sspi_recv_token.pvBuffer);
241 sspi_recv_token.pvBuffer = NULL;
243 sspi_recv_token.cbBuffer = 0;
245 if(status != SEC_I_CONTINUE_NEEDED)
248 /* analyse response */
250 /* GSS-API response looks like
251 * +----+------+-----+----------------+
252 * |VER | MTYP | LEN | TOKEN |
253 * +----+------+----------------------+
254 * | 1 | 1 | 2 | up to 2^16 - 1 |
255 * +----+------+-----+----------------+
258 result = Curl_blockread_all(conn, sock, (char *)socksreq, 4, &actualread);
259 if(result || (actualread != 4)) {
260 failf(data, "Failed to receive SSPI authentication response.");
262 s_pSecFn->FreeCredentialsHandle(&cred_handle);
263 s_pSecFn->DeleteSecurityContext(&sspi_context);
264 return CURLE_COULDNT_CONNECT;
267 /* ignore the first (VER) byte */
268 if(socksreq[1] == 255) { /* status / message type */
269 failf(data, "User was rejected by the SOCKS5 server (%u %u).",
270 (unsigned int)socksreq[0], (unsigned int)socksreq[1]);
272 s_pSecFn->FreeCredentialsHandle(&cred_handle);
273 s_pSecFn->DeleteSecurityContext(&sspi_context);
274 return CURLE_COULDNT_CONNECT;
277 if(socksreq[1] != 1) { /* status / messgae type */
278 failf(data, "Invalid SSPI authentication response type (%u %u).",
279 (unsigned int)socksreq[0], (unsigned int)socksreq[1]);
281 s_pSecFn->FreeCredentialsHandle(&cred_handle);
282 s_pSecFn->DeleteSecurityContext(&sspi_context);
283 return CURLE_COULDNT_CONNECT;
286 memcpy(&us_length, socksreq+2, sizeof(short));
287 us_length = ntohs(us_length);
289 sspi_recv_token.cbBuffer = us_length;
290 sspi_recv_token.pvBuffer = malloc(us_length);
292 if(!sspi_recv_token.pvBuffer) {
294 s_pSecFn->FreeCredentialsHandle(&cred_handle);
295 s_pSecFn->DeleteSecurityContext(&sspi_context);
296 return CURLE_OUT_OF_MEMORY;
298 result = Curl_blockread_all(conn, sock, (char *)sspi_recv_token.pvBuffer,
299 sspi_recv_token.cbBuffer, &actualread);
301 if(result || (actualread != us_length)) {
302 failf(data, "Failed to receive SSPI authentication token.");
304 if(sspi_recv_token.pvBuffer)
305 s_pSecFn->FreeContextBuffer(sspi_recv_token.pvBuffer);
306 s_pSecFn->FreeCredentialsHandle(&cred_handle);
307 s_pSecFn->DeleteSecurityContext(&sspi_context);
308 return CURLE_COULDNT_CONNECT;
311 context_handle = &sspi_context;
316 /* Everything is good so far, user was authenticated! */
317 status = s_pSecFn->QueryCredentialsAttributes(&cred_handle,
318 SECPKG_CRED_ATTR_NAMES,
320 s_pSecFn->FreeCredentialsHandle(&cred_handle);
321 if(check_sspi_err(conn, status, "QueryCredentialAttributes")) {
322 s_pSecFn->DeleteSecurityContext(&sspi_context);
323 s_pSecFn->FreeContextBuffer(names.sUserName);
324 failf(data, "Failed to determine user name.");
325 return CURLE_COULDNT_CONNECT;
327 infof(data, "SOCKS5 server authencticated user %s with GSS-API.\n",
329 s_pSecFn->FreeContextBuffer(names.sUserName);
332 socksreq[0] = 1; /* GSS-API subnegotiation version */
333 socksreq[1] = 2; /* encryption message type */
335 gss_enc = 0; /* no data protection */
336 /* do confidentiality protection if supported */
337 if(sspi_ret_flags & ISC_REQ_CONFIDENTIALITY)
339 /* else do integrity protection */
340 else if(sspi_ret_flags & ISC_REQ_INTEGRITY)
343 infof(data, "SOCKS5 server supports GSS-API %s data protection.\n",
344 (gss_enc==0)?"no":((gss_enc==1)?"integrity":"confidentiality") );
345 /* force to no data protection, avoid encryption/decryption for now */
348 * Sending the encryption type in clear seems wrong. It should be
349 * protected with gss_seal()/gss_wrap(). See RFC1961 extract below
350 * The NEC reference implementations on which this is based is
353 * +------+------+------+.......................+
354 * + ver | mtyp | len | token |
355 * +------+------+------+.......................+
356 * + 0x01 | 0x02 | 0x02 | up to 2^16 - 1 octets |
357 * +------+------+------+.......................+
361 * - "ver" is the protocol version number, here 1 to represent the
362 * first version of the SOCKS/GSS-API protocol
364 * - "mtyp" is the message type, here 2 to represent a protection
365 * -level negotiation message
367 * - "len" is the length of the "token" field in octets
369 * - "token" is the GSS-API encapsulated protection level
371 * The token is produced by encapsulating an octet containing the
372 * required protection level using gss_seal()/gss_wrap() with conf_req
373 * set to FALSE. The token is verified using gss_unseal()/
378 if(data->set.socks5_gssapi_nec) {
379 us_length = htons((short)1);
380 memcpy(socksreq+2, &us_length, sizeof(short));
383 status = s_pSecFn->QueryContextAttributes(&sspi_context,
386 if(check_sspi_err(conn, status, "QueryContextAttributes")) {
387 s_pSecFn->DeleteSecurityContext(&sspi_context);
388 failf(data, "Failed to query security context attributes.");
389 return CURLE_COULDNT_CONNECT;
392 sspi_w_token[0].cbBuffer = sspi_sizes.cbSecurityTrailer;
393 sspi_w_token[0].BufferType = SECBUFFER_TOKEN;
394 sspi_w_token[0].pvBuffer = malloc(sspi_sizes.cbSecurityTrailer);
396 if(!sspi_w_token[0].pvBuffer) {
397 s_pSecFn->DeleteSecurityContext(&sspi_context);
398 return CURLE_OUT_OF_MEMORY;
401 sspi_w_token[1].cbBuffer = 1;
402 sspi_w_token[1].pvBuffer = malloc(1);
403 if(!sspi_w_token[1].pvBuffer) {
404 s_pSecFn->FreeContextBuffer(sspi_w_token[0].pvBuffer);
405 s_pSecFn->DeleteSecurityContext(&sspi_context);
406 return CURLE_OUT_OF_MEMORY;
409 memcpy(sspi_w_token[1].pvBuffer, &gss_enc, 1);
410 sspi_w_token[2].BufferType = SECBUFFER_PADDING;
411 sspi_w_token[2].cbBuffer = sspi_sizes.cbBlockSize;
412 sspi_w_token[2].pvBuffer = malloc(sspi_sizes.cbBlockSize);
413 if(!sspi_w_token[2].pvBuffer) {
414 s_pSecFn->FreeContextBuffer(sspi_w_token[0].pvBuffer);
415 s_pSecFn->FreeContextBuffer(sspi_w_token[1].pvBuffer);
416 s_pSecFn->DeleteSecurityContext(&sspi_context);
417 return CURLE_OUT_OF_MEMORY;
419 status = s_pSecFn->EncryptMessage(&sspi_context,
420 KERB_WRAP_NO_ENCRYPT,
423 if(check_sspi_err(conn, status, "EncryptMessage")) {
424 s_pSecFn->FreeContextBuffer(sspi_w_token[0].pvBuffer);
425 s_pSecFn->FreeContextBuffer(sspi_w_token[1].pvBuffer);
426 s_pSecFn->FreeContextBuffer(sspi_w_token[2].pvBuffer);
427 s_pSecFn->DeleteSecurityContext(&sspi_context);
428 failf(data, "Failed to query security context attributes.");
429 return CURLE_COULDNT_CONNECT;
431 sspi_send_token.cbBuffer = sspi_w_token[0].cbBuffer
432 + sspi_w_token[1].cbBuffer
433 + sspi_w_token[2].cbBuffer;
434 sspi_send_token.pvBuffer = malloc(sspi_send_token.cbBuffer);
435 if(!sspi_send_token.pvBuffer) {
436 s_pSecFn->FreeContextBuffer(sspi_w_token[0].pvBuffer);
437 s_pSecFn->FreeContextBuffer(sspi_w_token[1].pvBuffer);
438 s_pSecFn->FreeContextBuffer(sspi_w_token[2].pvBuffer);
439 s_pSecFn->DeleteSecurityContext(&sspi_context);
440 return CURLE_OUT_OF_MEMORY;
443 memcpy(sspi_send_token.pvBuffer, sspi_w_token[0].pvBuffer,
444 sspi_w_token[0].cbBuffer);
445 memcpy((PUCHAR) sspi_send_token.pvBuffer +(int)sspi_w_token[0].cbBuffer,
446 sspi_w_token[1].pvBuffer, sspi_w_token[1].cbBuffer);
447 memcpy((PUCHAR) sspi_send_token.pvBuffer
448 +sspi_w_token[0].cbBuffer
449 +sspi_w_token[1].cbBuffer,
450 sspi_w_token[2].pvBuffer, sspi_w_token[2].cbBuffer);
452 s_pSecFn->FreeContextBuffer(sspi_w_token[0].pvBuffer);
453 sspi_w_token[0].pvBuffer = NULL;
454 sspi_w_token[0].cbBuffer = 0;
455 s_pSecFn->FreeContextBuffer(sspi_w_token[1].pvBuffer);
456 sspi_w_token[1].pvBuffer = NULL;
457 sspi_w_token[1].cbBuffer = 0;
458 s_pSecFn->FreeContextBuffer(sspi_w_token[2].pvBuffer);
459 sspi_w_token[2].pvBuffer = NULL;
460 sspi_w_token[2].cbBuffer = 0;
462 us_length = htons((short)sspi_send_token.cbBuffer);
463 memcpy(socksreq+2, &us_length, sizeof(short));
466 code = Curl_write_plain(conn, sock, (char *)socksreq, 4, &written);
467 if(code || (4 != written)) {
468 failf(data, "Failed to send SSPI encryption request.");
469 if(sspi_send_token.pvBuffer)
470 s_pSecFn->FreeContextBuffer(sspi_send_token.pvBuffer);
471 s_pSecFn->DeleteSecurityContext(&sspi_context);
472 return CURLE_COULDNT_CONNECT;
475 if(data->set.socks5_gssapi_nec) {
476 memcpy(socksreq, &gss_enc, 1);
477 code = Curl_write_plain(conn, sock, (char *)socksreq, 1, &written);
478 if(code || (1 != written)) {
479 failf(data, "Failed to send SSPI encryption type.");
480 s_pSecFn->DeleteSecurityContext(&sspi_context);
481 return CURLE_COULDNT_CONNECT;
485 code = Curl_write_plain(conn, sock, (char *)sspi_send_token.pvBuffer,
486 sspi_send_token.cbBuffer, &written);
487 if(code || (sspi_send_token.cbBuffer != (size_t)written)) {
488 failf(data, "Failed to send SSPI encryption type.");
489 if(sspi_send_token.pvBuffer)
490 s_pSecFn->FreeContextBuffer(sspi_send_token.pvBuffer);
491 s_pSecFn->DeleteSecurityContext(&sspi_context);
492 return CURLE_COULDNT_CONNECT;
494 if(sspi_send_token.pvBuffer)
495 s_pSecFn->FreeContextBuffer(sspi_send_token.pvBuffer);
498 result = Curl_blockread_all(conn, sock, (char *)socksreq, 4, &actualread);
499 if(result || (actualread != 4)) {
500 failf(data, "Failed to receive SSPI encryption response.");
501 s_pSecFn->DeleteSecurityContext(&sspi_context);
502 return CURLE_COULDNT_CONNECT;
505 /* ignore the first (VER) byte */
506 if(socksreq[1] == 255) { /* status / message type */
507 failf(data, "User was rejected by the SOCKS5 server (%u %u).",
508 (unsigned int)socksreq[0], (unsigned int)socksreq[1]);
509 s_pSecFn->DeleteSecurityContext(&sspi_context);
510 return CURLE_COULDNT_CONNECT;
513 if(socksreq[1] != 2) { /* status / message type */
514 failf(data, "Invalid SSPI encryption response type (%u %u).",
515 (unsigned int)socksreq[0], (unsigned int)socksreq[1]);
516 s_pSecFn->DeleteSecurityContext(&sspi_context);
517 return CURLE_COULDNT_CONNECT;
520 memcpy(&us_length, socksreq+2, sizeof(short));
521 us_length = ntohs(us_length);
523 sspi_w_token[0].cbBuffer = us_length;
524 sspi_w_token[0].pvBuffer = malloc(us_length);
525 if(!sspi_w_token[0].pvBuffer) {
526 s_pSecFn->DeleteSecurityContext(&sspi_context);
527 return CURLE_OUT_OF_MEMORY;
530 result = Curl_blockread_all(conn, sock, (char *)sspi_w_token[0].pvBuffer,
531 sspi_w_token[0].cbBuffer, &actualread);
533 if(result || (actualread != us_length)) {
534 failf(data, "Failed to receive SSPI encryption type.");
535 s_pSecFn->FreeContextBuffer(sspi_w_token[0].pvBuffer);
536 s_pSecFn->DeleteSecurityContext(&sspi_context);
537 return CURLE_COULDNT_CONNECT;
541 if(!data->set.socks5_gssapi_nec) {
542 wrap_desc.cBuffers = 2;
543 sspi_w_token[0].BufferType = SECBUFFER_STREAM;
544 sspi_w_token[1].BufferType = SECBUFFER_DATA;
545 sspi_w_token[1].cbBuffer = 0;
546 sspi_w_token[1].pvBuffer = NULL;
548 status = s_pSecFn->DecryptMessage(&sspi_context,
553 if(check_sspi_err(conn, status, "DecryptMessage")) {
554 if(sspi_w_token[0].pvBuffer)
555 s_pSecFn->FreeContextBuffer(sspi_w_token[0].pvBuffer);
556 if(sspi_w_token[1].pvBuffer)
557 s_pSecFn->FreeContextBuffer(sspi_w_token[1].pvBuffer);
558 s_pSecFn->DeleteSecurityContext(&sspi_context);
559 failf(data, "Failed to query security context attributes.");
560 return CURLE_COULDNT_CONNECT;
563 if(sspi_w_token[1].cbBuffer != 1) {
564 failf(data, "Invalid SSPI encryption response length (%lu).",
565 (unsigned long)sspi_w_token[1].cbBuffer);
566 if(sspi_w_token[0].pvBuffer)
567 s_pSecFn->FreeContextBuffer(sspi_w_token[0].pvBuffer);
568 if(sspi_w_token[1].pvBuffer)
569 s_pSecFn->FreeContextBuffer(sspi_w_token[1].pvBuffer);
570 s_pSecFn->DeleteSecurityContext(&sspi_context);
571 return CURLE_COULDNT_CONNECT;
574 memcpy(socksreq, sspi_w_token[1].pvBuffer, sspi_w_token[1].cbBuffer);
575 s_pSecFn->FreeContextBuffer(sspi_w_token[0].pvBuffer);
576 s_pSecFn->FreeContextBuffer(sspi_w_token[1].pvBuffer);
579 if(sspi_w_token[0].cbBuffer != 1) {
580 failf(data, "Invalid SSPI encryption response length (%lu).",
581 (unsigned long)sspi_w_token[0].cbBuffer);
582 s_pSecFn->FreeContextBuffer(sspi_w_token[0].pvBuffer);
583 s_pSecFn->DeleteSecurityContext(&sspi_context);
584 return CURLE_COULDNT_CONNECT;
586 memcpy(socksreq, sspi_w_token[0].pvBuffer, sspi_w_token[0].cbBuffer);
587 s_pSecFn->FreeContextBuffer(sspi_w_token[0].pvBuffer);
590 infof(data, "SOCKS5 access with%s protection granted.\n",
591 (socksreq[0]==0)?"out GSS-API data":
592 ((socksreq[0]==1)?" GSS-API integrity":" GSS-API confidentiality"));
594 /* For later use if encryption is required
595 conn->socks5_gssapi_enctype = socksreq[0];
597 conn->socks5_sspi_context = sspi_context;
599 s_pSecFn->DeleteSecurityContext(&sspi_context);
600 conn->socks5_sspi_context = sspi_context;