summaryrefslogtreecommitdiff
path: root/src/ne_sspi.c
diff options
context:
space:
mode:
authorjoe <joe@61a7d7f5-40b7-0310-9c16-bb0ea8cb1845>2005-01-20 22:04:23 +0000
committerjoe <joe@61a7d7f5-40b7-0310-9c16-bb0ea8cb1845>2005-01-20 22:04:23 +0000
commit1adeba8f71fadec4304a39681aff479fb363c100 (patch)
tree1543dab3314b94a5618f3a66b648eed6c9208f9b /src/ne_sspi.c
parent48eeba4c8342d5950a87b6a160f6415a57e3b389 (diff)
downloadneon-1adeba8f71fadec4304a39681aff479fb363c100.tar.gz
Windows SSPI NTLM/Negotiate implementation from Vladimir Berezniker:
* config.hw.in: Define HAVE_SSPI. * src/ne_sspi.c, src/ne_sspi.h: New files. * src/ne_auth.c (auth_scheme): Add new schemes to enum. [HAVE_SSPI] (auth_session): Add sspi_token, sspi_context fields. (clean_session): Clean up sspi fields. (request_sspi, sspi_challenge): New functions. (auth_challenge, ah_pre_send): Handle Negotiate/NTLM-using-SSPI schemes. * src/ne_socket.c [HAVE_SSPI] (ne_sock_init, ne_sock_exit): Initialize/de-initialize SSPI global state. git-svn-id: http://svn.webdav.org/repos/projects/neon/trunk@430 61a7d7f5-40b7-0310-9c16-bb0ea8cb1845
Diffstat (limited to 'src/ne_sspi.c')
-rw-r--r--src/ne_sspi.c546
1 files changed, 546 insertions, 0 deletions
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;
+}