Skip to content

Commit

Permalink
Initial SSPI support for NTLM under Windows
Browse files Browse the repository at this point in the history
Signed-off-by: David Woodhouse <David.Woodhouse@intel.com>
  • Loading branch information
David Woodhouse authored and David Woodhouse committed Jul 4, 2014
1 parent dcbe8c6 commit 8006929
Show file tree
Hide file tree
Showing 3 changed files with 104 additions and 14 deletions.
2 changes: 1 addition & 1 deletion configure.ac
Expand Up @@ -153,7 +153,7 @@ AC_SUBST(WFLAGS, [$WFLAGS])

if test "$have_win" = yes; then
# Checking "properly" for __attribute__((dllimport,stdcall)) functions is non-trivial
LIBS="$LIBS -lws2_32 -lshlwapi"
LIBS="$LIBS -lws2_32 -lshlwapi -lsecur32"
else
AC_CHECK_FUNC(socket, [], AC_CHECK_LIB(socket, socket, [], AC_ERROR(Cannot find socket() function)))
fi
Expand Down
107 changes: 94 additions & 13 deletions ntlm.c
Expand Up @@ -42,7 +42,90 @@
#define NTLM_MANUAL 3 /* SSO challenge/response sent or skipped; manual next */
#define NTLM_MANUAL_REQ 4 /* manual type1 packet sent */

#ifndef _WIN32
#ifdef _WIN32
static int ntlm_sspi(struct openconnect_info *vpninfo, struct oc_text_buf *buf, char *challenge)
{
SECURITY_STATUS status;
SecBufferDesc input_desc, output_desc;
SecBuffer in_token, out_token;
ULONG ret_flags;

if (challenge) {
int token_len;

input_desc.cBuffers = 1;
input_desc.pBuffers = &in_token;
input_desc.ulVersion = SECBUFFER_VERSION;

in_token.BufferType = SECBUFFER_TOKEN;
token_len = openconnect_base64_decode((unsigned char **)&in_token.pvBuffer, challenge);
if (token_len < 0)
return token_len;
in_token.cbBuffer = token_len;
}

output_desc.cBuffers = 1;
output_desc.pBuffers = &out_token;
output_desc.ulVersion = SECBUFFER_VERSION;

out_token.BufferType = SECBUFFER_TOKEN;
out_token.cbBuffer = 0;
out_token.pvBuffer = NULL;

status = InitializeSecurityContext(&vpninfo->ntlm_sspi_cred, challenge ? &vpninfo->ntlm_sspi_ctx : NULL, (SEC_CHAR *)"",
ISC_REQ_ALLOCATE_MEMORY | ISC_REQ_CONFIDENTIALITY | ISC_REQ_REPLAY_DETECT | ISC_REQ_CONNECTION,
0, SECURITY_NETWORK_DREP, challenge ? &input_desc : NULL, 0, &vpninfo->ntlm_sspi_ctx,
&output_desc, &ret_flags, NULL);
if (status != SEC_E_OK && status != SEC_I_CONTINUE_NEEDED) {
vpn_progress(vpninfo, PRG_ERR,
_("InitializeSecurityContext() failed: %lx\n"), status);
return -EIO;
}

buf_append(buf, "Proxy-Authorization: NTLM ");
buf_append_base64(buf, out_token.pvBuffer, out_token.cbBuffer);
buf_append(buf, "\r\n");

FreeContextBuffer(out_token.pvBuffer);

return 0;
}

static int ntlm_helper_spawn(struct openconnect_info *vpninfo, struct oc_text_buf *buf)
{
SECURITY_STATUS status;
int ret;

status = AcquireCredentialsHandle(NULL, (SEC_CHAR *)"NTLM", SECPKG_CRED_OUTBOUND,
NULL, NULL, NULL, NULL, &vpninfo->ntlm_sspi_cred, NULL);
if (status != SEC_E_OK) {
vpn_progress(vpninfo, PRG_ERR,
_("AcquireCredentialsHandle() failed: %lx\n"), status);
return -EIO;
}

ret = ntlm_sspi(vpninfo, buf, NULL);
if (ret)
FreeCredentialsHandle(&vpninfo->ntlm_sspi_cred);

return ret;
}

static int ntlm_helper_challenge(struct openconnect_info *vpninfo, struct oc_text_buf *buf)
{
return ntlm_sspi(vpninfo, buf, vpninfo->auth[AUTH_TYPE_NTLM].challenge);
}

void cleanup_ntlm_auth(struct openconnect_info *vpninfo)
{
if (vpninfo->auth[AUTH_TYPE_NTLM].state == NTLM_SSO_REQ) {
FreeCredentialsHandle(&vpninfo->ntlm_sspi_cred);
DeleteSecurityContext(&vpninfo->ntlm_sspi_ctx);
}
}

#else /* !_WIN32 */

static int ntlm_helper_spawn(struct openconnect_info *vpninfo, struct oc_text_buf *buf)
{
char *username;
Expand Down Expand Up @@ -153,13 +236,18 @@ static int ntlm_helper_challenge(struct openconnect_info *vpninfo, struct oc_tex
}
helperbuf[len - 1] = 0;
buf_append(buf, "Proxy-Authorization: NTLM %s\r\n", helperbuf + 3);
close(vpninfo->ntlm_helper_fd);
vpninfo->ntlm_helper_fd = -1;

vpn_progress(vpninfo, PRG_INFO, _("Attempting HTTP NTLM authentication to proxy (single-sign-on)\n"));
return 0;

}

void cleanup_ntlm_auth(struct openconnect_info *vpninfo)
{
if (vpninfo->auth[AUTH_TYPE_NTLM].state == NTLM_SSO_REQ) {
close(vpninfo->ntlm_helper_fd);
vpninfo->ntlm_helper_fd = -1;}
}
#endif /* !_WIN32 */

/*
Expand Down Expand Up @@ -963,7 +1051,6 @@ int ntlm_authorization(struct openconnect_info *vpninfo, struct oc_text_buf *buf
{
if (vpninfo->auth[AUTH_TYPE_NTLM].state == AUTH_AVAILABLE) {
vpninfo->auth[AUTH_TYPE_NTLM].state = NTLM_MANUAL;
#ifndef _WIN32
/* Don't attempt automatic NTLM auth if we were given a password */
if (!vpninfo->proxy_pass && !ntlm_helper_spawn(vpninfo, buf)) {
vpninfo->auth[AUTH_TYPE_NTLM].state = NTLM_SSO_REQ;
Expand All @@ -972,11 +1059,12 @@ int ntlm_authorization(struct openconnect_info *vpninfo, struct oc_text_buf *buf
}
if (vpninfo->auth[AUTH_TYPE_NTLM].state == NTLM_SSO_REQ) {
int ret;
vpninfo->auth[AUTH_TYPE_NTLM].state = NTLM_MANUAL;
ret = ntlm_helper_challenge(vpninfo, buf);
/* Clean up after it. We're done here, whether it worked or not */
cleanup_ntlm_auth(vpninfo);
vpninfo->auth[AUTH_TYPE_NTLM].state = NTLM_MANUAL;
if (!ret || ret == -EAGAIN)
return ret;
#endif
}
if (vpninfo->auth[AUTH_TYPE_NTLM].state == NTLM_MANUAL && vpninfo->proxy_user &&
vpninfo->proxy_pass) {
Expand All @@ -994,10 +1082,3 @@ int ntlm_authorization(struct openconnect_info *vpninfo, struct oc_text_buf *buf
vpninfo->auth[AUTH_TYPE_NTLM].state = AUTH_FAILED;
return -EAGAIN;
}

void cleanup_ntlm_auth(struct openconnect_info *vpninfo)
{
if (vpninfo->auth[AUTH_TYPE_NTLM].state == NTLM_SSO_REQ) {
close(vpninfo->ntlm_helper_fd);
vpninfo->ntlm_helper_fd = -1;}
}
9 changes: 9 additions & 0 deletions openconnect-internal.h
Expand Up @@ -25,6 +25,10 @@
#ifdef _WIN32
#include <winsock2.h>
#include <ws2tcpip.h>
#ifndef SECURITY_WIN32
#define SECURITY_WIN32 1
#endif
#include <security.h>
#else
#include <sys/types.h>
#include <sys/socket.h>
Expand Down Expand Up @@ -207,7 +211,12 @@ struct openconnect_info {
gss_name_t gss_target_name;
gss_ctx_id_t gss_context;
#endif
#ifdef _WIN32
CredHandle ntlm_sspi_cred;
CtxtHandle ntlm_sspi_ctx;
#else
int ntlm_helper_fd;
#endif
int authmethods_set;

char *localname;
Expand Down

0 comments on commit 8006929

Please sign in to comment.