diff options
author | joe <joe@61a7d7f5-40b7-0310-9c16-bb0ea8cb1845> | 2005-01-20 22:04:23 +0000 |
---|---|---|
committer | joe <joe@61a7d7f5-40b7-0310-9c16-bb0ea8cb1845> | 2005-01-20 22:04:23 +0000 |
commit | 1adeba8f71fadec4304a39681aff479fb363c100 (patch) | |
tree | 1543dab3314b94a5618f3a66b648eed6c9208f9b /src/ne_sspi.c | |
parent | 48eeba4c8342d5950a87b6a160f6415a57e3b389 (diff) | |
download | neon-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.c | 546 |
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, + ×tamp); + + 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; +} |