diff --git a/lib/socks_sspi.c b/lib/socks_sspi.c index ca26898e31..5937db5e69 100644 --- a/lib/socks_sspi.c +++ b/lib/socks_sspi.c @@ -56,52 +56,68 @@ static int check_sspi_err(struct Curl_easy *data, } /* This is the SSPI-using version of this function */ -CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, - struct Curl_easy *data) +static CURLcode socks5_sspi_setup(struct Curl_cfilter *cf, + struct Curl_easy *data, + CredHandle *cred_handle, + char **service_namep) { struct connectdata *conn = cf->conn; - curl_socket_t sock = conn->sock[cf->sockindex]; - CURLcode code; - size_t actualread; - size_t written; - CURLcode result; - /* Needs GSS-API authentication */ - SECURITY_STATUS status; - unsigned long sspi_ret_flags = 0; - unsigned char gss_enc; - SecBuffer sspi_send_token, sspi_recv_token, sspi_w_token[3]; - SecBufferDesc input_desc, output_desc, wrap_desc; - SecPkgContext_Sizes sspi_sizes; - CredHandle cred_handle; - CtxtHandle sspi_context; - PCtxtHandle context_handle = NULL; - SecPkgCredentials_Names names; - char *service_name = NULL; - unsigned short us_length; - unsigned long qop; - unsigned char socksreq[4]; /* room for GSS-API exchange header only */ const char *service = data->set.str[STRING_PROXY_SERVICE_NAME] ? - data->set.str[STRING_PROXY_SERVICE_NAME] : "rcmd"; - uint8_t *etbuf; - size_t etbuf_size; - - /* GSS-API request looks like - * +----+------+-----+----------------+ - * |VER | MTYP | LEN | TOKEN | - * +----+------+----------------------+ - * | 1 | 1 | 2 | up to 2^16 - 1 | - * +----+------+-----+----------------+ - */ + data->set.str[STRING_PROXY_SERVICE_NAME] : "rcmd"; + SECURITY_STATUS status; /* prepare service name */ if(strchr(service, '/')) - service_name = curlx_strdup(service); + *service_namep = curlx_strdup(service); else - service_name = curl_maprintf("%s/%s", - service, conn->socks_proxy.host.name); - if(!service_name) + *service_namep = curl_maprintf("%s/%s", + service, conn->socks_proxy.host.name); + if(!*service_namep) return CURLE_OUT_OF_MEMORY; + status = + Curl_pSecFn->AcquireCredentialsHandle(NULL, + (TCHAR *)CURL_UNCONST(TEXT("Kerberos")), + SECPKG_CRED_OUTBOUND, + NULL, NULL, NULL, NULL, + cred_handle, NULL); + + if(check_sspi_err(data, status, "AcquireCredentialsHandle")) { + failf(data, "Failed to acquire credentials."); + return CURLE_COULDNT_CONNECT; + } + + return CURLE_OK; +} + +static CURLcode socks5_free_token(SecBuffer *send_token, + CURLcode result) +{ + Curl_pSecFn->FreeContextBuffer(send_token->pvBuffer); + send_token->pvBuffer = NULL; + return result; +} + +static CURLcode socks5_sspi_loop(struct Curl_cfilter *cf, + struct Curl_easy *data, + CredHandle *cred_handle, + CtxtHandle *sspi_context, + char *service_name) +{ + struct connectdata *conn = cf->conn; + curl_socket_t sock = conn->sock[cf->sockindex]; + CURLcode result = CURLE_OK; + CURLcode code; + SECURITY_STATUS status; + unsigned long sspi_ret_flags = 0; + SecBuffer sspi_send_token, sspi_recv_token; + SecBufferDesc input_desc, output_desc; + PCtxtHandle context_handle = NULL; + unsigned short us_length; + size_t actualread; + size_t written; + unsigned char socksreq[4]; + input_desc.cBuffers = 1; input_desc.pBuffers = &sspi_recv_token; input_desc.ulVersion = SECBUFFER_VERSION; @@ -118,50 +134,15 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, sspi_send_token.cbBuffer = 0; sspi_send_token.pvBuffer = NULL; - sspi_w_token[0].pvBuffer = - sspi_w_token[1].pvBuffer = - sspi_w_token[2].pvBuffer = NULL; - - wrap_desc.cBuffers = 3; - wrap_desc.pBuffers = sspi_w_token; - wrap_desc.ulVersion = SECBUFFER_VERSION; - - memset(&cred_handle, 0, sizeof(cred_handle)); - memset(&sspi_context, 0, sizeof(sspi_context)); - - names.sUserName = NULL; - - etbuf = NULL; - etbuf_size = 0; - - status = - Curl_pSecFn->AcquireCredentialsHandle(NULL, - (TCHAR *)CURL_UNCONST(TEXT("Kerberos")), - SECPKG_CRED_OUTBOUND, - NULL, NULL, NULL, NULL, - &cred_handle, NULL); - - if(check_sspi_err(data, status, "AcquireCredentialsHandle")) { - failf(data, "Failed to acquire credentials."); - result = CURLE_COULDNT_CONNECT; - goto error; - } - (void)curlx_nonblock(sock, FALSE); - /* As long as we need to keep sending some context info, and there is no */ - /* errors, keep sending it... */ for(;;) { - TCHAR *sname; - - sname = curlx_convert_UTF8_to_tchar(service_name); - if(!sname) { - result = CURLE_OUT_OF_MEMORY; - goto error; - } + TCHAR *sname = curlx_convert_UTF8_to_tchar(service_name); + if(!sname) + return socks5_free_token(&sspi_send_token, CURLE_OUT_OF_MEMORY); status = - Curl_pSecFn->InitializeSecurityContext(&cred_handle, context_handle, + Curl_pSecFn->InitializeSecurityContext(cred_handle, context_handle, sname, ISC_REQ_MUTUAL_AUTH | ISC_REQ_ALLOCATE_MEMORY | @@ -169,19 +150,17 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, ISC_REQ_REPLAY_DETECT, 0, SECURITY_NATIVE_DREP, &input_desc, 0, - &sspi_context, + sspi_context, &output_desc, &sspi_ret_flags, NULL); curlx_free(sname); - Curl_safefree(sspi_recv_token.pvBuffer); sspi_recv_token.cbBuffer = 0; if(check_sspi_err(data, status, "InitializeSecurityContext")) { failf(data, "Failed to initialise security context."); - result = CURLE_COULDNT_CONNECT; - goto error; + return socks5_free_token(&sspi_send_token, CURLE_COULDNT_CONNECT); } if(sspi_send_token.cbBuffer) { @@ -189,8 +168,7 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, socksreq[1] = 1; /* authentication message type */ if(sspi_send_token.cbBuffer > 0xffff) { /* needs to fit in an unsigned 16-bit field */ - result = CURLE_COULDNT_CONNECT; - goto error; + return socks5_free_token(&sspi_send_token, CURLE_COULDNT_CONNECT); } us_length = htons((unsigned short)sspi_send_token.cbBuffer); memcpy(socksreq + 2, &us_length, sizeof(short)); @@ -198,8 +176,7 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, code = Curl_conn_cf_send(cf->next, data, socksreq, 4, FALSE, &written); if(code || (written != 4)) { failf(data, "Failed to send SSPI authentication request."); - result = CURLE_COULDNT_CONNECT; - goto error; + return socks5_free_token(&sspi_send_token, CURLE_COULDNT_CONNECT); } code = Curl_conn_cf_send(cf->next, data, @@ -207,15 +184,12 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, sspi_send_token.cbBuffer, FALSE, &written); if(code || (sspi_send_token.cbBuffer != written)) { failf(data, "Failed to send SSPI authentication token."); - result = CURLE_COULDNT_CONNECT; - goto error; + return socks5_free_token(&sspi_send_token, CURLE_COULDNT_CONNECT); } } - if(sspi_send_token.pvBuffer) { - Curl_pSecFn->FreeContextBuffer(sspi_send_token.pvBuffer); - sspi_send_token.pvBuffer = NULL; - } + if(sspi_send_token.pvBuffer) + socks5_free_token(&sspi_send_token, CURLE_OK); sspi_send_token.cbBuffer = 0; Curl_safefree(sspi_recv_token.pvBuffer); @@ -224,37 +198,22 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, if(status != SEC_I_CONTINUE_NEEDED) break; - /* analyse response */ - - /* GSS-API response looks like - * +----+------+-----+----------------+ - * |VER | MTYP | LEN | TOKEN | - * +----+------+----------------------+ - * | 1 | 1 | 2 | up to 2^16 - 1 | - * +----+------+-----+----------------+ - */ - result = Curl_blockread_all(cf, data, (char *)socksreq, 4, &actualread); if(result || (actualread != 4)) { failf(data, "Failed to receive SSPI authentication response."); - if(!result) - result = CURLE_COULDNT_CONNECT; - goto error; + return result ? result : CURLE_COULDNT_CONNECT; } - /* ignore the first (VER) byte */ - if(socksreq[1] == 255) { /* status / message type */ + if(socksreq[1] == 255) { failf(data, "User was rejected by the SOCKS5 server (%u %u).", (unsigned int)socksreq[0], (unsigned int)socksreq[1]); - result = CURLE_COULDNT_CONNECT; - goto error; + return CURLE_COULDNT_CONNECT; } - if(socksreq[1] != 1) { /* status / message type */ + if(socksreq[1] != 1) { failf(data, "Invalid SSPI authentication response type (%u %u).", (unsigned int)socksreq[0], (unsigned int)socksreq[1]); - result = CURLE_COULDNT_CONNECT; - goto error; + return CURLE_COULDNT_CONNECT; } memcpy(&us_length, socksreq + 2, sizeof(short)); @@ -263,26 +222,272 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, sspi_recv_token.cbBuffer = us_length; sspi_recv_token.pvBuffer = curlx_malloc(us_length); - if(!sspi_recv_token.pvBuffer) { - result = CURLE_OUT_OF_MEMORY; - goto error; - } + if(!sspi_recv_token.pvBuffer) + return CURLE_OUT_OF_MEMORY; + result = Curl_blockread_all(cf, data, (char *)sspi_recv_token.pvBuffer, sspi_recv_token.cbBuffer, &actualread); if(result || (actualread != us_length)) { failf(data, "Failed to receive SSPI authentication token."); - if(!result) - result = CURLE_COULDNT_CONNECT; - goto error; + return result ? result : CURLE_COULDNT_CONNECT; } - context_handle = &sspi_context; + context_handle = sspi_context; } + return CURLE_OK; +} + +static CURLcode socks5_free(SecBuffer *sspi_w_token, + CURLcode result) +{ + Curl_safefree(sspi_w_token[0].pvBuffer); + Curl_safefree(sspi_w_token[1].pvBuffer); + Curl_safefree(sspi_w_token[2].pvBuffer); + return result; +} + +static CURLcode socks5_sspi_encryption(struct Curl_cfilter *cf, + struct Curl_easy *data, + CtxtHandle *sspi_context) +{ + CURLcode result = CURLE_OK; + CURLcode code; + SECURITY_STATUS status; + SecPkgContext_Flags sspi_flags; + unsigned char gss_enc; + SecBuffer sspi_w_token[3]; + SecBufferDesc wrap_desc; + SecPkgContext_Sizes sspi_sizes; + unsigned short us_length; + unsigned long qop; + unsigned char socksreq[4]; + uint8_t *etbuf = NULL; + size_t etbuf_size = 0; + size_t actualread; + size_t written; + + sspi_flags.Flags = 0; + status = Curl_pSecFn->QueryContextAttributes(sspi_context, + SECPKG_ATTR_FLAGS, + &sspi_flags); + if(check_sspi_err(data, status, "QueryContextAttributes")) + return CURLE_COULDNT_CONNECT; + + gss_enc = 0; + if(sspi_flags.Flags & ISC_REQ_CONFIDENTIALITY) + gss_enc = 2; + else if(sspi_flags.Flags & ISC_REQ_INTEGRITY) + gss_enc = 1; + + infof(data, "SOCKS5 server supports GSS-API %s data protection.", + (gss_enc == 0) ? "no" : + ((gss_enc == 1) ? "integrity" : "confidentiality") ); + + sspi_w_token[0].pvBuffer = + sspi_w_token[1].pvBuffer = + sspi_w_token[2].pvBuffer = NULL; + + wrap_desc.cBuffers = 3; + wrap_desc.pBuffers = sspi_w_token; + wrap_desc.ulVersion = SECBUFFER_VERSION; + + socksreq[0] = 1; + socksreq[1] = 2; + + if(data->set.socks5_gssapi_nec) { + us_length = htons((unsigned short)1); + memcpy(socksreq + 2, &us_length, sizeof(short)); + } + else { + status = Curl_pSecFn->QueryContextAttributes(sspi_context, + SECPKG_ATTR_SIZES, + &sspi_sizes); + if(check_sspi_err(data, status, "QueryContextAttributes")) + return CURLE_COULDNT_CONNECT; + + sspi_w_token[0].cbBuffer = sspi_sizes.cbSecurityTrailer; + sspi_w_token[0].BufferType = SECBUFFER_TOKEN; + sspi_w_token[0].pvBuffer = curlx_malloc(sspi_sizes.cbSecurityTrailer); + + if(!sspi_w_token[0].pvBuffer) + return CURLE_OUT_OF_MEMORY; + + sspi_w_token[1].cbBuffer = 1; + sspi_w_token[1].pvBuffer = curlx_malloc(1); + if(!sspi_w_token[1].pvBuffer) + return socks5_free(sspi_w_token, CURLE_OUT_OF_MEMORY); + + memcpy(sspi_w_token[1].pvBuffer, &gss_enc, 1); + sspi_w_token[2].BufferType = SECBUFFER_PADDING; + sspi_w_token[2].cbBuffer = sspi_sizes.cbBlockSize; + sspi_w_token[2].pvBuffer = curlx_malloc(sspi_sizes.cbBlockSize); + if(!sspi_w_token[2].pvBuffer) + return socks5_free(sspi_w_token, CURLE_OUT_OF_MEMORY); + + status = Curl_pSecFn->EncryptMessage(sspi_context, + KERB_WRAP_NO_ENCRYPT, + &wrap_desc, 0); + if(check_sspi_err(data, status, "EncryptMessage")) + return socks5_free(sspi_w_token, CURLE_COULDNT_CONNECT); + + etbuf_size = sspi_w_token[0].cbBuffer + sspi_w_token[1].cbBuffer + + sspi_w_token[2].cbBuffer; + if(etbuf_size > 0xffff) + return socks5_free(sspi_w_token, CURLE_COULDNT_CONNECT); + + etbuf = curlx_malloc(etbuf_size); + if(!etbuf) + return socks5_free(sspi_w_token, CURLE_OUT_OF_MEMORY); + + memcpy(etbuf, sspi_w_token[0].pvBuffer, sspi_w_token[0].cbBuffer); + memcpy(etbuf + sspi_w_token[0].cbBuffer, + sspi_w_token[1].pvBuffer, sspi_w_token[1].cbBuffer); + memcpy(etbuf + sspi_w_token[0].cbBuffer + sspi_w_token[1].cbBuffer, + sspi_w_token[2].pvBuffer, sspi_w_token[2].cbBuffer); + + (void)socks5_free(sspi_w_token, CURLE_OK); + + us_length = htons((unsigned short)etbuf_size); + memcpy(socksreq + 2, &us_length, sizeof(short)); + } + + code = Curl_conn_cf_send(cf->next, data, socksreq, 4, FALSE, &written); + if(code || (written != 4)) { + failf(data, "Failed to send SSPI encryption request."); + curlx_free(etbuf); + return CURLE_COULDNT_CONNECT; + } + + if(data->set.socks5_gssapi_nec) { + memcpy(socksreq, &gss_enc, 1); + code = Curl_conn_cf_send(cf->next, data, socksreq, 1, FALSE, &written); + if(code || (written != 1)) { + failf(data, "Failed to send SSPI encryption type."); + return CURLE_COULDNT_CONNECT; + } + } + else { + code = Curl_conn_cf_send(cf->next, data, etbuf, etbuf_size, FALSE, + &written); + curlx_free(etbuf); + if(code || (etbuf_size != written)) { + failf(data, "Failed to send SSPI encryption type."); + return CURLE_COULDNT_CONNECT; + } + } + + result = Curl_blockread_all(cf, data, (char *)socksreq, 4, &actualread); + if(result || (actualread != 4)) { + failf(data, "Failed to receive SSPI encryption response."); + return result ? result : CURLE_COULDNT_CONNECT; + } + + if(socksreq[1] == 255) { + failf(data, "User was rejected by the SOCKS5 server (%u %u).", + (unsigned int)socksreq[0], (unsigned int)socksreq[1]); + return CURLE_COULDNT_CONNECT; + } + + if(socksreq[1] != 2) { + failf(data, "Invalid SSPI encryption response type (%u %u).", + (unsigned int)socksreq[0], (unsigned int)socksreq[1]); + return CURLE_COULDNT_CONNECT; + } + + memcpy(&us_length, socksreq + 2, sizeof(short)); + us_length = ntohs(us_length); + + sspi_w_token[0].cbBuffer = us_length; + sspi_w_token[0].pvBuffer = curlx_malloc(us_length); + if(!sspi_w_token[0].pvBuffer) + return CURLE_OUT_OF_MEMORY; + + result = Curl_blockread_all(cf, data, (char *)sspi_w_token[0].pvBuffer, + sspi_w_token[0].cbBuffer, &actualread); + + if(result || (actualread != us_length)) { + failf(data, "Failed to receive SSPI encryption type."); + curlx_free(sspi_w_token[0].pvBuffer); + return result ? result : CURLE_COULDNT_CONNECT; + } + + if(!data->set.socks5_gssapi_nec) { + wrap_desc.cBuffers = 2; + sspi_w_token[0].BufferType = SECBUFFER_STREAM; + sspi_w_token[1].BufferType = SECBUFFER_DATA; + sspi_w_token[1].cbBuffer = 0; + sspi_w_token[1].pvBuffer = NULL; + + status = Curl_pSecFn->DecryptMessage(sspi_context, &wrap_desc, + 0, &qop); + + if(check_sspi_err(data, status, "DecryptMessage")) { + if(sspi_w_token[1].pvBuffer) + Curl_pSecFn->FreeContextBuffer(sspi_w_token[1].pvBuffer); + curlx_free(sspi_w_token[0].pvBuffer); + return CURLE_COULDNT_CONNECT; + } + + if(sspi_w_token[1].cbBuffer != 1) { + failf(data, "Invalid SSPI encryption response length (%lu).", + (unsigned long)sspi_w_token[1].cbBuffer); + if(sspi_w_token[1].pvBuffer) + Curl_pSecFn->FreeContextBuffer(sspi_w_token[1].pvBuffer); + curlx_free(sspi_w_token[0].pvBuffer); + return CURLE_COULDNT_CONNECT; + } + + memcpy(socksreq, sspi_w_token[1].pvBuffer, sspi_w_token[1].cbBuffer); + Curl_pSecFn->FreeContextBuffer(sspi_w_token[1].pvBuffer); + } + else { + if(sspi_w_token[0].cbBuffer != 1) { + failf(data, "Invalid SSPI encryption response length (%lu).", + (unsigned long)sspi_w_token[0].cbBuffer); + curlx_free(sspi_w_token[0].pvBuffer); + return CURLE_COULDNT_CONNECT; + } + memcpy(socksreq, sspi_w_token[0].pvBuffer, sspi_w_token[0].cbBuffer); + } + curlx_free(sspi_w_token[0].pvBuffer); + + infof(data, "SOCKS5 access with%s protection granted BUT NOT USED.", + (socksreq[0] == 0) ? "out GSS-API data" : + ((socksreq[0] == 1) ? " GSS-API integrity" : + " GSS-API confidentiality")); + + return CURLE_OK; +} + +CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + struct connectdata *conn = cf->conn; + curl_socket_t sock = conn->sock[cf->sockindex]; + CURLcode result; + SECURITY_STATUS status; + CredHandle cred_handle; + CtxtHandle sspi_context; + SecPkgCredentials_Names names; + char *service_name = NULL; + + memset(&cred_handle, 0, sizeof(cred_handle)); + memset(&sspi_context, 0, sizeof(sspi_context)); + names.sUserName = NULL; + + result = socks5_sspi_setup(cf, data, &cred_handle, &service_name); + if(result) + goto error; + + result = socks5_sspi_loop(cf, data, &cred_handle, &sspi_context, + service_name); + if(result) + goto error; + Curl_safefree(service_name); - /* Everything is good so far, user was authenticated! */ status = Curl_pSecFn->QueryCredentialsAttributes(&cred_handle, SECPKG_CRED_ATTR_NAMES, &names); @@ -300,287 +505,22 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, names.sUserName = NULL; } - /* Do encryption */ - socksreq[0] = 1; /* GSS-API subnegotiation version */ - socksreq[1] = 2; /* encryption message type */ - - gss_enc = 0; /* no data protection */ - /* do confidentiality protection if supported */ - if(sspi_ret_flags & ISC_REQ_CONFIDENTIALITY) - gss_enc = 2; - /* else do integrity protection */ - else if(sspi_ret_flags & ISC_REQ_INTEGRITY) - gss_enc = 1; - - infof(data, "SOCKS5 server supports GSS-API %s data protection.", - (gss_enc == 0) ? "no" : - ((gss_enc == 1) ? "integrity" : "confidentiality") ); - - /* - * Sending the encryption type in clear seems wrong. It should be - * protected with gss_seal()/gss_wrap(). See RFC1961 extract below - * The NEC reference implementations on which this is based is - * therefore at fault - * - * +------+------+------+.......................+ - * + ver | mtyp | len | token | - * +------+------+------+.......................+ - * + 0x01 | 0x02 | 0x02 | up to 2^16 - 1 octets | - * +------+------+------+.......................+ - * - * Where: - * - * - "ver" is the protocol version number, here 1 to represent the - * first version of the SOCKS/GSS-API protocol - * - * - "mtyp" is the message type, here 2 to represent a protection - * -level negotiation message - * - * - "len" is the length of the "token" field in octets - * - * - "token" is the GSS-API encapsulated protection level - * - * The token is produced by encapsulating an octet containing the - * required protection level using gss_seal()/gss_wrap() with conf_req - * set to FALSE. The token is verified using gss_unseal()/ - * gss_unwrap(). - * - */ - - if(data->set.socks5_gssapi_nec) { - us_length = htons((unsigned short)1); - memcpy(socksreq + 2, &us_length, sizeof(short)); - } - else { - status = Curl_pSecFn->QueryContextAttributes(&sspi_context, - SECPKG_ATTR_SIZES, - &sspi_sizes); - if(check_sspi_err(data, status, "QueryContextAttributes")) { - failf(data, "Failed to query security context attributes."); - result = CURLE_COULDNT_CONNECT; - goto error; - } - - sspi_w_token[0].cbBuffer = sspi_sizes.cbSecurityTrailer; - sspi_w_token[0].BufferType = SECBUFFER_TOKEN; - sspi_w_token[0].pvBuffer = curlx_malloc(sspi_sizes.cbSecurityTrailer); - - if(!sspi_w_token[0].pvBuffer) { - result = CURLE_OUT_OF_MEMORY; - goto error; - } - - sspi_w_token[1].cbBuffer = 1; - sspi_w_token[1].pvBuffer = curlx_malloc(1); - if(!sspi_w_token[1].pvBuffer) { - result = CURLE_OUT_OF_MEMORY; - goto error; - } - - memcpy(sspi_w_token[1].pvBuffer, &gss_enc, 1); - sspi_w_token[2].BufferType = SECBUFFER_PADDING; - sspi_w_token[2].cbBuffer = sspi_sizes.cbBlockSize; - sspi_w_token[2].pvBuffer = curlx_malloc(sspi_sizes.cbBlockSize); - if(!sspi_w_token[2].pvBuffer) { - result = CURLE_OUT_OF_MEMORY; - goto error; - } - status = Curl_pSecFn->EncryptMessage(&sspi_context, - KERB_WRAP_NO_ENCRYPT, - &wrap_desc, 0); - if(check_sspi_err(data, status, "EncryptMessage")) { - failf(data, "Failed to query security context attributes."); - result = CURLE_COULDNT_CONNECT; - goto error; - } - - etbuf_size = sspi_w_token[0].cbBuffer + sspi_w_token[1].cbBuffer + - sspi_w_token[2].cbBuffer; - if(etbuf_size > 0xffff) { - /* needs to fit in an unsigned 16-bit field */ - result = CURLE_COULDNT_CONNECT; - goto error; - } - etbuf = curlx_malloc(etbuf_size); - if(!etbuf) { - result = CURLE_OUT_OF_MEMORY; - goto error; - } - - memcpy(etbuf, sspi_w_token[0].pvBuffer, sspi_w_token[0].cbBuffer); - memcpy(etbuf + sspi_w_token[0].cbBuffer, - sspi_w_token[1].pvBuffer, sspi_w_token[1].cbBuffer); - memcpy(etbuf + sspi_w_token[0].cbBuffer + sspi_w_token[1].cbBuffer, - sspi_w_token[2].pvBuffer, sspi_w_token[2].cbBuffer); - - Curl_safefree(sspi_w_token[0].pvBuffer); - sspi_w_token[0].cbBuffer = 0; - Curl_safefree(sspi_w_token[1].pvBuffer); - sspi_w_token[1].cbBuffer = 0; - Curl_safefree(sspi_w_token[2].pvBuffer); - sspi_w_token[2].cbBuffer = 0; - - us_length = htons((unsigned short)etbuf_size); - memcpy(socksreq + 2, &us_length, sizeof(short)); - } - - code = Curl_conn_cf_send(cf->next, data, socksreq, 4, FALSE, &written); - if(code || (written != 4)) { - failf(data, "Failed to send SSPI encryption request."); - result = CURLE_COULDNT_CONNECT; + result = socks5_sspi_encryption(cf, data, &sspi_context); + if(result) goto error; - } - if(data->set.socks5_gssapi_nec) { - memcpy(socksreq, &gss_enc, 1); - code = Curl_conn_cf_send(cf->next, data, socksreq, 1, FALSE, &written); - if(code || (written != 1)) { - failf(data, "Failed to send SSPI encryption type."); - result = CURLE_COULDNT_CONNECT; - goto error; - } - } - else { - code = Curl_conn_cf_send(cf->next, data, etbuf, etbuf_size, - FALSE, &written); - if(code || (etbuf_size != written)) { - failf(data, "Failed to send SSPI encryption type."); - result = CURLE_COULDNT_CONNECT; - goto error; - } - Curl_safefree(etbuf); - } - - result = Curl_blockread_all(cf, data, (char *)socksreq, 4, &actualread); - if(result || (actualread != 4)) { - failf(data, "Failed to receive SSPI encryption response."); - if(!result) - result = CURLE_COULDNT_CONNECT; - goto error; - } - - /* ignore the first (VER) byte */ - if(socksreq[1] == 255) { /* status / message type */ - failf(data, "User was rejected by the SOCKS5 server (%u %u).", - (unsigned int)socksreq[0], (unsigned int)socksreq[1]); - result = CURLE_COULDNT_CONNECT; - goto error; - } - - if(socksreq[1] != 2) { /* status / message type */ - failf(data, "Invalid SSPI encryption response type (%u %u).", - (unsigned int)socksreq[0], (unsigned int)socksreq[1]); - result = CURLE_COULDNT_CONNECT; - goto error; - } - - memcpy(&us_length, socksreq + 2, sizeof(short)); - us_length = ntohs(us_length); - - sspi_w_token[0].cbBuffer = us_length; - sspi_w_token[0].pvBuffer = curlx_malloc(us_length); - if(!sspi_w_token[0].pvBuffer) { - result = CURLE_OUT_OF_MEMORY; - goto error; - } - - result = Curl_blockread_all(cf, data, (char *)sspi_w_token[0].pvBuffer, - sspi_w_token[0].cbBuffer, &actualread); - - if(result || (actualread != us_length)) { - failf(data, "Failed to receive SSPI encryption type."); - if(!result) - result = CURLE_COULDNT_CONNECT; - goto error; - } - - if(!data->set.socks5_gssapi_nec) { - wrap_desc.cBuffers = 2; - sspi_w_token[0].BufferType = SECBUFFER_STREAM; - sspi_w_token[1].BufferType = SECBUFFER_DATA; - sspi_w_token[1].cbBuffer = 0; - sspi_w_token[1].pvBuffer = NULL; - - status = Curl_pSecFn->DecryptMessage(&sspi_context, &wrap_desc, - 0, &qop); - - /* since sspi_w_token[1].pvBuffer is allocated by the SSPI in this case, it - must be freed in this block using FreeContextBuffer() instead of - potentially in error cleanup using curlx_free(). */ - - if(check_sspi_err(data, status, "DecryptMessage")) { - failf(data, "Failed to query security context attributes."); - if(sspi_w_token[1].pvBuffer) { - Curl_pSecFn->FreeContextBuffer(sspi_w_token[1].pvBuffer); - sspi_w_token[1].pvBuffer = NULL; - } - result = CURLE_COULDNT_CONNECT; - goto error; - } - - if(sspi_w_token[1].cbBuffer != 1) { - failf(data, "Invalid SSPI encryption response length (%lu).", - (unsigned long)sspi_w_token[1].cbBuffer); - if(sspi_w_token[1].pvBuffer) { - Curl_pSecFn->FreeContextBuffer(sspi_w_token[1].pvBuffer); - sspi_w_token[1].pvBuffer = NULL; - } - result = CURLE_COULDNT_CONNECT; - goto error; - } - - memcpy(socksreq, sspi_w_token[1].pvBuffer, sspi_w_token[1].cbBuffer); - Curl_safefree(sspi_w_token[0].pvBuffer); - sspi_w_token[0].cbBuffer = 0; - Curl_pSecFn->FreeContextBuffer(sspi_w_token[1].pvBuffer); - sspi_w_token[1].pvBuffer = NULL; - sspi_w_token[1].cbBuffer = 0; - } - else { - if(sspi_w_token[0].cbBuffer != 1) { - failf(data, "Invalid SSPI encryption response length (%lu).", - sspi_w_token[0].cbBuffer); - result = CURLE_COULDNT_CONNECT; - goto error; - } - memcpy(socksreq, sspi_w_token[0].pvBuffer, sspi_w_token[0].cbBuffer); - Curl_safefree(sspi_w_token[0].pvBuffer); - sspi_w_token[0].cbBuffer = 0; - } (void)curlx_nonblock(sock, TRUE); - - infof(data, "SOCKS5 access with%s protection granted BUT NOT USED.", - (socksreq[0] == 0) ? "out GSS-API data" : - ((socksreq[0] == 1) ? " GSS-API integrity" : - " GSS-API confidentiality")); - - /* For later use if encryption is required - conn->socks5_gssapi_enctype = socksreq[0]; - if(socksreq[0] != 0) - conn->socks5_sspi_context = sspi_context; - else { - Curl_pSecFn->DeleteSecurityContext(&sspi_context); - conn->socks5_sspi_context = sspi_context; - } - */ - Curl_pSecFn->DeleteSecurityContext(&sspi_context); Curl_pSecFn->FreeCredentialsHandle(&cred_handle); return CURLE_OK; + error: (void)curlx_nonblock(sock, TRUE); curlx_free(service_name); Curl_pSecFn->DeleteSecurityContext(&sspi_context); Curl_pSecFn->FreeCredentialsHandle(&cred_handle); - curlx_free(sspi_recv_token.pvBuffer); - if(sspi_send_token.pvBuffer) - Curl_pSecFn->FreeContextBuffer(sspi_send_token.pvBuffer); if(names.sUserName) Curl_pSecFn->FreeContextBuffer(names.sUserName); - curlx_free(sspi_w_token[0].pvBuffer); - curlx_free(sspi_w_token[1].pvBuffer); - curlx_free(sspi_w_token[2].pvBuffer); - curlx_free(etbuf); return result; } #endif