base
This commit is contained in:
+548
@@ -0,0 +1,548 @@
|
||||
/*
|
||||
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"
|
||||
|
||||
#ifdef HAVE_SSPI
|
||||
|
||||
#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(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 ne_sspi_init(void)
|
||||
{
|
||||
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_deinit(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->FreeCredentialHandle(&(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;
|
||||
}
|
||||
#endif /* HAVE_SSPI */
|
||||
Reference in New Issue
Block a user