summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--config.hw.in2
-rw-r--r--neon.mak3
-rw-r--r--src/ne_auth.c114
-rw-r--r--src/ne_socket.c15
-rw-r--r--src/ne_sspi.c546
-rw-r--r--src/ne_sspi.h48
6 files changed, 723 insertions, 5 deletions
diff --git a/config.hw.in b/config.hw.in
index 5a5fe1b..7813889 100644
--- a/config.hw.in
+++ b/config.hw.in
@@ -37,6 +37,8 @@
#define HAVE_MEMCPY
#define HAVE_SETSOCKOPT
+#define HAVE_SSPI
+
#define NE_FMT_SIZE_T "u"
#define NE_FMT_SSIZE_T "d"
#define NE_FMT_OFF_T "ld"
diff --git a/neon.mak b/neon.mak
index 6842b0a..475a509 100644
--- a/neon.mak
+++ b/neon.mak
@@ -115,6 +115,7 @@ LIB32_OBJS= \
"$(INTDIR)\ne_request.obj" \
"$(INTDIR)\ne_session.obj" \
"$(INTDIR)\ne_socket.obj" \
+ "$(INTDIR)\ne_sspi.obj" \
"$(INTDIR)\ne_string.obj" \
"$(INTDIR)\ne_uri.obj" \
"$(INTDIR)\ne_utils.obj"
@@ -169,6 +170,7 @@ CLEAN: $(ZLIB_CLEAN)
-@erase "$(INTDIR)\ne_openssl.obj"
-@erase "$(INTDIR)\ne_stubssl.obj"
-@erase "$(INTDIR)\ne_socket.obj"
+ -@erase "$(INTDIR)\ne_sspi.obj"
-@erase "$(INTDIR)\ne_string.obj"
-@erase "$(INTDIR)\ne_uri.obj"
-@erase "$(INTDIR)\ne_utils.obj"
@@ -214,6 +216,7 @@ CLEAN: $(ZLIB_CLEAN)
"$(INTDIR)\ne_openssl.obj": .\src\ne_openssl.c
"$(INTDIR)\ne_stubssl.obj": .\src\ne_stubssl.c
"$(INTDIR)\ne_socket.obj": .\src\ne_socket.c
+"$(INTDIR)\ne_sspi.obj": .\src\ne_sspi.c
"$(INTDIR)\ne_string.obj": .\src\ne_string.c
"$(INTDIR)\ne_uri.obj": .\src\ne_uri.c
"$(INTDIR)\ne_utils.obj": .\src\ne_utils.c
diff --git a/src/ne_auth.c b/src/ne_auth.c
index 8cf7ea9..659ab5f 100644
--- a/src/ne_auth.c
+++ b/src/ne_auth.c
@@ -1,6 +1,6 @@
/*
HTTP Authentication routines
- Copyright (C) 1999-2004, Joe Orton <joe@manyfish.co.uk>
+ Copyright (C) 1999-2005, Joe Orton <joe@manyfish.co.uk>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
@@ -77,6 +77,10 @@
#endif
#endif
+#ifdef HAVE_SSPI
+#include "ne_sspi.h"
+#endif
+
/* TODO: should remove this eventually. Need it for
* ne_pull_request_body. */
#include "ne_private.h"
@@ -88,7 +92,9 @@
typedef enum {
auth_scheme_basic,
auth_scheme_digest,
- auth_scheme_gssapi
+ auth_scheme_gssapi,
+ auth_scheme_sspi_negotiate,
+ auth_scheme_sspi_ntlm
} auth_scheme;
typedef enum {
@@ -167,6 +173,11 @@ typedef struct {
gss_name_t gssname;
gss_OID gssmech;
#endif
+#ifdef HAVE_SSPI
+ /* This is used for SSPI (Negotiate/NTLM) auth */
+ char *sspi_token;
+ void *sspi_context;
+#endif
/* These all used for Digest auth */
char *realm;
char *nonce;
@@ -227,6 +238,11 @@ static void clean_session(auth_session *sess)
}
NE_FREE(sess->gssapi_token);
#endif
+#ifdef HAVE_SSPI
+ NE_FREE(sess->sspi_token);
+ ne_sspi_destroy_context(sess->sspi_context);
+ sess->sspi_context = NULL;
+#endif
}
/* Returns client nonce string. */
@@ -491,6 +507,53 @@ static int verify_negotiate_response(auth_session *sess, char *hdr)
}
#endif
+#ifdef HAVE_SSPI
+static char *request_sspi(auth_session *sess)
+{
+ const char *mechanism;
+
+ if (ne_sspi_get_mechanism(sess->sspi_context, &mechanism)) {
+ return NULL;
+ }
+
+ return ne_concat(mechanism, " ", sess->sspi_token, "\r\n", NULL);
+}
+
+static int sspi_challenge(auth_session *sess, struct auth_challenge *parms,
+ int ntlm)
+{
+ int status;
+ char *response = NULL;
+
+ NE_DEBUG(NE_DBG_HTTPAUTH, "auth: SSPI challenge.\n");
+
+ if (!sess->sspi_context) {
+ status = ne_sspi_create_context(&sess->sspi_context,
+ sess->sess->server.hostname, ntlm);
+ if (status) {
+ return status;
+ }
+ }
+
+ status = ne_sspi_authenticate(sess->sspi_context, parms->opaque, &response);
+ if (status) {
+ return status;
+ }
+
+ sess->sspi_token = response;
+
+ NE_DEBUG(NE_DBG_HTTPAUTH, "auth: SSPI challenge [%s]\n", sess->sspi_token);
+
+ if (ntlm) {
+ sess->scheme = auth_scheme_sspi_ntlm;
+ } else {
+ sess->scheme = auth_scheme_sspi_negotiate;
+ }
+
+ return 0;
+}
+#endif
+
/* Examine a digest challenge: return 0 if it is a valid Digest challenge,
* else non-zero. */
static int digest_challenge(auth_session *sess, struct auth_challenge *parms)
@@ -963,6 +1026,14 @@ static int auth_challenge(auth_session *sess, const char *value)
else if (strcasecmp(key, "negotiate") == 0) {
scheme = auth_scheme_gssapi;
}
+#else
+#ifdef HAVE_SSPI
+ else if (strcasecmp(key, "negotiate") == 0) {
+ scheme = auth_scheme_sspi_negotiate;
+ } else if (strcasecmp(key, "ntlm") == 0) {
+ scheme = auth_scheme_sspi_ntlm;
+ }
+#endif
#endif
else {
NE_DEBUG(NE_DBG_HTTPAUTH, "Ignoring challenge '%s'.\n", key);
@@ -976,7 +1047,10 @@ static int auth_challenge(auth_session *sess, const char *value)
chall->next = challenges;
challenges = chall;
- if (scheme == auth_scheme_gssapi && sep == ' ') {
+ if (sep == ' ' &&
+ (scheme == auth_scheme_gssapi
+ || scheme == auth_scheme_sspi_negotiate
+ || scheme == auth_scheme_sspi_ntlm) ) {
/* Cope with the fact that the unquoted base64
* paramater token doesn't match the 2617 auth-param
* grammar: */
@@ -1057,6 +1131,32 @@ static int auth_challenge(auth_session *sess, const char *value)
}
#endif
+#ifdef HAVE_SSPI
+ if (!success) {
+ NE_DEBUG(NE_DBG_HTTPAUTH, "Looking for SSPI/Negotiate.\n");
+ for (chall = challenges; chall != NULL; chall = chall->next) {
+ if (chall->scheme == auth_scheme_sspi_negotiate) {
+ if (!sspi_challenge(sess, chall, 0)) {
+ success = 1;
+ break;
+ }
+ }
+ }
+ }
+
+ if (!success) {
+ NE_DEBUG(NE_DBG_HTTPAUTH, "Looking for SSPI/NTLM.\n");
+ for (chall = challenges; chall != NULL; chall = chall->next) {
+ if (chall->scheme == auth_scheme_sspi_ntlm) {
+ if (!sspi_challenge(sess, chall, 1)) {
+ success = 1;
+ break;
+ }
+ }
+ }
+ }
+#endif
+
/* Try a digest challenge */
if (!success) {
NE_DEBUG(NE_DBG_HTTPAUTH, "Looking for Digest challenges.\n");
@@ -1170,7 +1270,13 @@ static void ah_pre_send(ne_request *r, void *cookie, ne_buffer *request)
value = request_gssapi(sess);
break;
#endif
- default:
+#ifdef HAVE_SSPI
+ case auth_scheme_sspi_negotiate:
+ case auth_scheme_sspi_ntlm:
+ value = request_sspi(sess);
+ break;
+#endif
+ default:
value = NULL;
break;
}
diff --git a/src/ne_socket.c b/src/ne_socket.c
index 2ab3b61..2a0b865 100644
--- a/src/ne_socket.c
+++ b/src/ne_socket.c
@@ -1,6 +1,6 @@
/*
Socket handling routines
- Copyright (C) 1998-2004, Joe Orton <joe@manyfish.co.uk>,
+ Copyright (C) 1998-2005, Joe Orton <joe@manyfish.co.uk>,
Copyright (C) 1999-2000 Tommi Komulainen <Tommi.Komulainen@iki.fi>
Copyright (C) 2004 Aleix Conchillo Flaque <aleix@member.fsf.org>
@@ -134,6 +134,7 @@ typedef struct in_addr ne_inet_addr;
#include "ne_string.h"
#include "ne_socket.h"
#include "ne_alloc.h"
+#include "ne_sspi.h"
#if defined(__BEOS__) && !defined(BONE_VERSION)
/* pre-BONE */
@@ -320,6 +321,13 @@ int ne_sock_init(void)
#endif
+#ifdef HAVE_SSPI
+ if (ne_sspi_init() < 0) {
+ init_result = -1;
+ return init_result;
+ }
+#endif
+
#ifdef NE_HAVE_SOCKS
SOCKSinit("neon");
#endif
@@ -348,6 +356,11 @@ void ne_sock_exit(void)
#ifdef HAVE_GNUTLS
gnutls_global_deinit();
#endif
+
+#ifdef HAVE_SSPI
+ ne_sspi_deinit();
+#endif
+
init_result = 0;
}
diff --git a/src/ne_sspi.c b/src/ne_sspi.c
new file mode 100644
index 0000000..4fc4e90
--- /dev/null
+++ b/src/ne_sspi.c
@@ -0,0 +1,546 @@
+/*
+ Microsoft SSPI based authentication routines
+ Copyright (C) 2004-2005, Vladimir Berezniker @ http://public.xdi.org/=vmpn
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA
+
+*/
+
+#include "config.h"
+
+#include "ne_utils.h"
+#include "ne_string.h"
+#include "ne_sspi.h"
+
+#define SEC_SUCCESS(Status) ((Status) >= 0)
+
+struct SSPIContextStruct {
+ CtxtHandle context;
+ char *serverName;
+ CredHandle credentials;
+ int continueNeeded;
+ char *mechanism;
+ int ntlm;
+ ULONG maxTokenSize;
+};
+
+typedef struct SSPIContextStruct SSPIContext;
+
+static ULONG negotiateMaxTokenSize = 0;
+static ULONG ntlmMaxTokenSize = 0;
+static HINSTANCE hSecDll = NULL;
+static PSecurityFunctionTable pSFT = NULL;
+static int initialized = 0;
+
+/*
+ * Query specified package for it's maximum token size.
+ */
+static int getMaxTokenSize(const char *package, ULONG * maxTokenSize)
+{
+ SECURITY_STATUS status;
+ SecPkgInfo *packageSecurityInfo = NULL;
+
+ status = pSFT->QuerySecurityPackageInfo(package, &packageSecurityInfo);
+ if (status == SEC_E_OK) {
+ *maxTokenSize = packageSecurityInfo->cbMaxToken;
+ if (pSFT->FreeContextBuffer(packageSecurityInfo) != SEC_E_OK) {
+ NE_DEBUG(NE_DBG_HTTPAUTH,
+ "sspi: Unable to free security package info.");
+ }
+ } else {
+ NE_DEBUG(NE_DBG_HTTPAUTH,
+ "sspi: QuerySecurityPackageInfo [failed] [%x].", status);
+ return -1;
+ }
+
+ return 0;
+}
+
+/*
+ * Initialize all the SSPI data
+ */
+static void initDll(HINSTANCE hSecDll)
+{
+ INIT_SECURITY_INTERFACE initSecurityInterface = NULL;
+
+ initSecurityInterface =
+ (INIT_SECURITY_INTERFACE) GetProcAddress(hSecDll,
+ SECURITY_ENTRYPOINT);
+
+ if (initSecurityInterface == NULL) {
+ NE_DEBUG(NE_DBG_HTTPAUTH,
+ "sspi: Obtaining security interface [fail].\n");
+ initialized = -1;
+ return;
+ } else {
+ NE_DEBUG(NE_DBG_HTTPAUTH,
+ "sspi: Obtaining security interface [ok].\n");
+ }
+
+ pSFT = (initSecurityInterface) ();
+
+ if (pSFT == NULL) {
+ NE_DEBUG(NE_DBG_HTTPAUTH, "sspi: Security Function Table [fail].\n");
+ initialized = -2;
+ return;
+ } else {
+ NE_DEBUG(NE_DBG_HTTPAUTH, "sspi: Security Function Table [ok].\n");
+ }
+
+ if (getMaxTokenSize("Negotiate", &negotiateMaxTokenSize)) {
+ NE_DEBUG(NE_DBG_HTTPAUTH,
+ "sspi: Unable to get negotiate maximum packet size");
+ initialized = -3;
+ }
+
+ if (getMaxTokenSize("NTLM", &ntlmMaxTokenSize)) {
+ NE_DEBUG(NE_DBG_HTTPAUTH,
+ "sspi: Unable to get negotiate maximum packet size");
+ initialized = -3;
+ }
+}
+
+/*
+ * This function needs to be called at least once before using any other.
+ */
+int sspiInit()
+{
+
+ if (initialized) {
+ return 0;
+ }
+
+ NE_DEBUG(NE_DBG_SOCKET, "sspiInit\n");
+ NE_DEBUG(NE_DBG_HTTPAUTH, "sspi: Loading security dll.\n");
+ hSecDll = LoadLibrary("security.dll");
+
+ if (hSecDll == NULL) {
+ NE_DEBUG(NE_DBG_HTTPAUTH, "sspi: Loading of security dll [fail].\n");
+ } else {
+ NE_DEBUG(NE_DBG_HTTPAUTH, "sspi: Loading of security dll [ok].\n");
+ initDll(hSecDll);
+ if (initialized == 0) {
+ initialized = 1;
+ }
+ }
+
+ NE_DEBUG(NE_DBG_HTTPAUTH, "sspi: sspiInit [%d].\n", initialized);
+ if (initialized < 0) {
+ return initialized;
+ } else {
+ return 0;
+ }
+}
+
+/*
+ * This function can be called to free resources used by SSPI.
+ */
+int ne_sspi_init(void)
+{
+ NE_DEBUG(NE_DBG_SOCKET, "sspi: DeInit\n");
+ if (initialized <= 0) {
+ return initialized;
+ }
+
+ pSFT = NULL;
+
+ if (hSecDll != NULL) {
+ NE_DEBUG(NE_DBG_HTTPAUTH, "sspi: Unloading security dll.\n");
+ if (FreeLibrary(hSecDll)) {
+ NE_DEBUG(NE_DBG_HTTPAUTH,
+ "sspi: Unloading of security dll [ok].\n");
+ } else {
+ NE_DEBUG(NE_DBG_HTTPAUTH,
+ "sspi: Unloading of security dll [fail].\n");
+ return -1;
+ }
+ hSecDll = NULL;
+ }
+
+ initialized = 0;
+ return 0;
+}
+
+/*
+ * Simplification wrapper arround AcquireCredentialsHandle as most of
+ * the parameters do not change.
+ */
+static int acquireCredentialsHandle(CredHandle * credentials, char *package)
+{
+ SECURITY_STATUS status;
+ TimeStamp timestamp;
+
+ status =
+ pSFT->AcquireCredentialsHandle(NULL, package, SECPKG_CRED_OUTBOUND,
+ NULL, NULL, NULL, NULL, credentials,
+ &timestamp);
+
+ if (status != SEC_E_OK) {
+ NE_DEBUG(NE_DBG_HTTPAUTH,
+ "sspi: AcquireCredentialsHandle [fail] [%x].\n", status);
+ return -1;
+ }
+
+ return 0;
+}
+
+/*
+ * Wrapper arround initializeSecurityContext. Supplies several
+ * default parameters as well as logging in case of errors.
+ */
+static SECURITY_STATUS
+initializeSecurityContext(CredHandle * credentials, CtxtHandle * context,
+ char *spn, ULONG contextReq,
+ SecBufferDesc * inBuffer, CtxtHandle * newContext,
+ SecBufferDesc * outBuffer)
+{
+ ULONG contextAttributes;
+ SECURITY_STATUS status;
+
+ status =
+ pSFT->InitializeSecurityContext(credentials, context, spn, contextReq,
+ 0, SECURITY_NETWORK_DREP, inBuffer, 0,
+ newContext, outBuffer,
+ &contextAttributes, NULL);
+
+ if (!SEC_SUCCESS(status)) {
+ if (status == SEC_E_INVALID_TOKEN) {
+ NE_DEBUG(NE_DBG_HTTPAUTH,
+ "InitializeSecurityContext [fail] SEC_E_INVALID_TOKEN.\n");
+ } else if (status == SEC_E_UNSUPPORTED_FUNCTION) {
+ NE_DEBUG(NE_DBG_HTTPAUTH,
+ "InitializeSecurityContext [fail] SEC_E_UNSUPPORTED_FUNCTION.\n");
+ } else {
+ NE_DEBUG(NE_DBG_HTTPAUTH,
+ "InitializeSecurityContext [fail] [%x].\n", status);
+ }
+ }
+
+ return status;
+}
+
+/*
+ * Validates that the pointer is not NULL and converts it to its real type.
+ */
+static int getContext(void *context, SSPIContext **sspiContext)
+{
+ if (!context) {
+ return -1;
+ }
+
+ *sspiContext = context;
+ return 0;
+}
+
+/*
+ * Verifies that the buffer descriptor point only to one buffer and
+ * returns the pointer to it.
+ */
+static int getSingleBufferDescriptor(SecBufferDesc *secBufferDesc,
+ SecBuffer **secBuffer)
+{
+ if (secBufferDesc->cBuffers != 1) {
+ NE_DEBUG(NE_DBG_HTTPAUTH,
+ "sspi: fillBufferDescriptor "
+ "[fail] numbers of descriptor buffers. 1 != [%d].\n",
+ secBufferDesc->cBuffers);
+ return -1;
+ }
+
+ *secBuffer = secBufferDesc->pBuffers;
+ return 0;
+}
+
+/*
+ * Decodes BASE64 string into SSPI SecBuffer
+ */
+static int base64ToBuffer(const char *token, SecBufferDesc * secBufferDesc)
+{
+ SecBuffer *buffer;
+ if (getSingleBufferDescriptor(secBufferDesc, &buffer)) {
+ return -1;
+ }
+
+ buffer->BufferType = SECBUFFER_TOKEN;
+ buffer->cbBuffer =
+ ne_unbase64(token, &((unsigned char *) buffer->pvBuffer));
+
+ if (buffer->cbBuffer == 0) {
+ NE_DEBUG(NE_DBG_HTTPAUTH,
+ "sspi: Unable to decode BASE64 SSPI token.\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+/*
+ * Creates a SecBuffer of a specified size.
+ */
+static int makeBuffer(SecBufferDesc * secBufferDesc, ULONG size)
+{
+ SecBuffer *buffer;
+ if (getSingleBufferDescriptor(secBufferDesc, &buffer)) {
+ return -1;
+ }
+
+ buffer->BufferType = SECBUFFER_TOKEN;
+ buffer->cbBuffer = size;
+ buffer->pvBuffer = ne_calloc(size);
+
+ return 0;
+}
+
+/*
+ * Frees data allocated in the buffer.
+ */
+static int freeBuffer(SecBufferDesc * secBufferDesc)
+{
+ SecBuffer *buffer;
+ if (getSingleBufferDescriptor(secBufferDesc, &buffer)) {
+ return -1;
+ }
+
+ if (buffer->cbBuffer > 0 && buffer->pvBuffer) {
+ ne_free(buffer->pvBuffer);
+ buffer->cbBuffer = 0;
+ buffer->pvBuffer = NULL;
+ }
+
+ return 0;
+}
+
+/*
+ * Returns mechanism string for the specified context.
+ */
+int ne_sspi_get_mechanism(void *context, char const **mechanism)
+{
+ int status;
+ SSPIContext *sspiContext;
+
+ if (initialized <= 0) {
+ return -1;
+ }
+
+ status = getContext(context, &sspiContext);
+ if (status) {
+ return status;
+ }
+
+ *mechanism = sspiContext->mechanism;
+ return 0;
+}
+
+/*
+ * Create a context to authenticate to specified server, using either
+ * ntlm or negotiate.
+ */
+int ne_sspi_create_context(void **context, char *serverName, int ntlm)
+{
+ SSPIContext *sspiContext;
+
+ if (initialized <= 0) {
+ return -1;
+ }
+
+ sspiContext = ne_calloc(sizeof(SSPIContext));
+ sspiContext->continueNeeded = 0;
+
+ if (ntlm) {
+ sspiContext->mechanism = "NTLM";
+ sspiContext->serverName = ne_strdup(serverName);
+ sspiContext->maxTokenSize = ntlmMaxTokenSize;
+ } else {
+ sspiContext->mechanism = "Negotiate";
+ sspiContext->serverName = ne_concat("HTTP/", serverName, NULL);
+ sspiContext->maxTokenSize = negotiateMaxTokenSize;
+ }
+
+ sspiContext->ntlm = ntlm;
+ *context = sspiContext;
+ return 0;
+}
+
+/*
+ * Resets the context
+ */
+static void resetContext(SSPIContext * sspiContext)
+{
+ pSFT->DeleteSecurityContext(&(sspiContext->context));
+ pSFT->FreeCredentialsHandle(&(sspiContext->credentials));
+ sspiContext->continueNeeded = 0;
+}
+
+/*
+ * Initializes supplied SecBufferDesc to point to supplied SecBuffer
+ * that is also initialized;
+ */
+static void
+initSingleEmptyBuffer(SecBufferDesc * bufferDesc, SecBuffer * buffer)
+{
+ buffer->BufferType = SECBUFFER_EMPTY;
+ buffer->cbBuffer = 0;
+ buffer->pvBuffer = NULL;
+
+ bufferDesc->cBuffers = 1;
+ bufferDesc->ulVersion = SECBUFFER_VERSION;
+ bufferDesc->pBuffers = buffer;
+
+}
+
+/*
+ * Destroyes the supplied context.
+ */
+int ne_sspi_destroy_context(void *context)
+{
+
+ int status;
+ SSPIContext *sspiContext;
+
+ if (initialized <= 0) {
+ return -1;
+ }
+
+ status = getContext(context, &sspiContext);
+ if (status) {
+ return status;
+ }
+
+ resetContext(sspiContext);
+ if (sspiContext->serverName) {
+ ne_free(sspiContext->serverName);
+ sspiContext->serverName = NULL;
+ }
+
+ ne_free(sspiContext);
+ return 0;
+}
+
+/*
+ * Processes received authentication tokens as well as supplies the
+ * response token.
+ */
+int ne_sspi_authenticate(void *context, const char *base64Token, char **responseToken)
+{
+ SecBufferDesc outBufferDesc;
+ SecBuffer outBuffer;
+ int status;
+ SECURITY_STATUS securityStatus;
+ ULONG contextFlags;
+
+ SSPIContext *sspiContext;
+ if (initialized <= 0) {
+ return -1;
+ }
+
+ status = getContext(context, &sspiContext);
+ if (status) {
+ return status;
+ }
+
+ /* TODO: Not sure what flags should be set. joe: this needs to be
+ * driven by the ne_auth interface; the GSSAPI code needs similar
+ * flags. */
+ contextFlags = ISC_REQ_CONFIDENTIALITY | ISC_REQ_MUTUAL_AUTH;
+
+ initSingleEmptyBuffer(&outBufferDesc, &outBuffer);
+ status = makeBuffer(&outBufferDesc, sspiContext->maxTokenSize);
+ if (status) {
+ return status;
+ }
+
+ if (base64Token) {
+ SecBufferDesc inBufferDesc;
+ SecBuffer inBuffer;
+
+ if (!sspiContext->continueNeeded) {
+ NE_DEBUG(NE_DBG_HTTPAUTH, "sspi: Got an unexpected token.\n");
+ return -1;
+ }
+
+ initSingleEmptyBuffer(&inBufferDesc, &inBuffer);
+
+ status = base64ToBuffer(base64Token, &inBufferDesc);
+ if (status) {
+ return status;
+ }
+
+ securityStatus =
+ initializeSecurityContext(&sspiContext->credentials,
+ &(sspiContext->context),
+ sspiContext->serverName, contextFlags,
+ &inBufferDesc, &(sspiContext->context),
+ &outBufferDesc);
+ freeBuffer(&inBufferDesc);
+ } else {
+ if (sspiContext->continueNeeded) {
+ NE_DEBUG(NE_DBG_HTTPAUTH, "sspi: Expected a token from server.\n");
+ return -1;
+ }
+
+ /* Reset any existing context since we are starting over */
+ resetContext(sspiContext);
+
+ if (acquireCredentialsHandle
+ (&sspiContext->credentials, sspiContext->mechanism) != SEC_E_OK) {
+ NE_DEBUG(NE_DBG_HTTPAUTH,
+ "sspi: acquireCredentialsHandle failed.\n");
+ return -1;
+ }
+
+ securityStatus =
+ initializeSecurityContext(&sspiContext->credentials, NULL,
+ sspiContext->serverName, contextFlags,
+ NULL, &(sspiContext->context),
+ &outBufferDesc);
+ }
+
+ if (securityStatus == SEC_I_COMPLETE_AND_CONTINUE
+ || securityStatus == SEC_I_COMPLETE_NEEDED) {
+ SECURITY_STATUS compleStatus =
+ pSFT->CompleteAuthToken(&(sspiContext->context), &outBufferDesc);
+
+ if (compleStatus != SEC_E_OK) {
+ freeBuffer(&outBufferDesc);
+ NE_DEBUG(NE_DBG_HTTPAUTH, "sspi: CompleteAuthToken failed.\n");
+ return -1;
+ }
+ }
+
+ if (securityStatus == SEC_I_COMPLETE_AND_CONTINUE
+ || securityStatus == SEC_I_CONTINUE_NEEDED) {
+ sspiContext->continueNeeded = 1;
+ } else {
+ sspiContext->continueNeeded = 0;
+ }
+
+ if (!(securityStatus == SEC_I_COMPLETE_AND_CONTINUE
+ || securityStatus == SEC_I_COMPLETE_NEEDED
+ || securityStatus == SEC_I_CONTINUE_NEEDED
+ || securityStatus == SEC_E_OK)) {
+ NE_DEBUG(NE_DBG_HTTPAUTH,
+ "sspi: initializeSecurityContext [failed] [%x].\n",
+ securityStatus);
+ freeBuffer(&outBufferDesc);
+ return -1;
+ }
+
+ *responseToken = ne_base64(outBufferDesc.pBuffers->pvBuffer,
+ outBufferDesc.pBuffers->cbBuffer);
+ freeBuffer(&outBufferDesc);
+
+ return 0;
+}
diff --git a/src/ne_sspi.h b/src/ne_sspi.h
new file mode 100644
index 0000000..4c707a9
--- /dev/null
+++ b/src/ne_sspi.h
@@ -0,0 +1,48 @@
+/*
+ Microsoft SSPI based authentication routines
+ Copyright (C) 2004-2005, Vladimir Berezniker @ http://public.xdi.org/=vmpn
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA
+
+*/
+
+#ifndef NE_SSPI_H
+#define NE_SSPI_H
+
+/* Win32 SSPI-based authentication interfaces. PRIVATE TO NEON -- NOT
+ * PART OF THE EXTERNAL API. */
+
+#ifdef HAVE_SSPI
+
+#include <windows.h>
+#define SECURITY_WIN32
+#include <security.h>
+
+int ne_sspi_init(void);
+fint ne_sspi_deinit(void);
+
+int ne_sspi_create_context(void **context, char * serverName, int ntlm);
+
+int ne_sspi_destroy_context(void *context);
+
+int ne_sspi_get_mechanism(void *context, char const **mechanism);
+
+int ne_sspi_authenticate(void *context, const char *base64Token,
+ char **responseToken);
+
+#endif /* HAVE_SSPI */
+
+#endif /* NE_SSPI_H */