webdav_authcache.c [plain text]
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/syslog.h>
#include <pthread.h>
#define DEBUG_ASSERT_COMPONENT_NAME_STRING "webdavfs"
#define DEBUG_ASSERT_MESSAGE(componentNameString, \
assertionString, \
exceptionLabelString, \
errorString, \
fileName, \
lineNumber, \
errorCode) \
WebDAVDebugAssert(componentNameString, \
assertionString, \
exceptionLabelString, \
errorString, \
fileName, \
lineNumber, \
errorCode)
#include <AssertMacros.h>
#include "fetch.h"
#include "digcalc.h"
#include "webdav_authcache.h"
#if DEBUG
static void
WebDAVDebugAssert(const char * componentNameString,
const char * assertionString, const char * exceptionLabelString,
const char * errorString, const char * fileName, long lineNumber,
int errorCode);
static void
WebDAVDebugAssert(const char * componentNameString,
const char * assertionString, const char * exceptionLabelString,
const char * errorString, const char * fileName, long lineNumber,
int errorCode)
{
if ( (assertionString != NULL) && (*assertionString != '\0') )
syslog(LOG_INFO, "Assertion failed: %s: %s\n", componentNameString, assertionString);
else
syslog(LOG_INFO, "Check failed: %s:\n", componentNameString);
if ( exceptionLabelString != NULL )
syslog(LOG_INFO, " %s\n", exceptionLabelString);
if ( errorString != NULL )
syslog(LOG_INFO, " %s\n", errorString);
if ( fileName != NULL )
syslog(LOG_INFO, " file: %s\n", fileName);
if ( lineNumber != 0 )
syslog(LOG_INFO, " line: %ld\n", lineNumber);
if ( errorCode != 0 )
syslog(LOG_INFO, " error: %d\n", errorCode);
}
#endif
#define Constant_strlen(s) (sizeof(s) - 1)
struct URIRec
{
struct URIRec *next;
char *server;
size_t serverLen;
char *absPath;
size_t absPathLen;
};
typedef struct URIRec URIRec;
typedef struct WebdavAuthcacheElement WebdavAuthcacheElement;
typedef void (*MakeAuthHeaderProcPtr)(WebdavAuthcacheRetrieveRec *retrieveRec,
WebdavAuthcacheElement *elem);
#define CallMakeAuthHeaderProc(userRoutine, retrieveRec, elem) \
(*(userRoutine))((retrieveRec), (elem))
typedef void (*FreeAuthDataProcPtr)(void *authData);
#define CallFreeAuthDataProc(userRoutine, authData) \
(*(userRoutine))((authData))
struct WebdavAuthcacheElement
{
struct WebdavAuthcacheElement *next;
uid_t uid;
int isProxy;
char *realmStr;
ChallengeSecurityLevelType scheme;
char *username;
char *password;
unsigned long uriCount;
URIRec *domainHead;
MakeAuthHeaderProcPtr makeProcPtr;
FreeAuthDataProcPtr freeProcPtr;
void *authData;
};
struct WebdavAuthcacheHeader
{
pthread_mutex_t lock;
unsigned long count;
WebdavAuthcacheElement *head;
int proxyElementCount;
char *cnonce;
};
typedef struct WebdavAuthcacheHeader WebdavAuthcacheHeader;
static void GetNextNextWebdavAuthcacheElement(WebdavAuthcacheElement **elem);
static void EnqueueWebdavAuthcacheElement(WebdavAuthcacheElement *elem);
static WebdavAuthcacheElement * DequeueWebdavAuthcacheElement(uid_t uid,
int isProxy, char *realmStr);
static void FreeWebdavAuthcacheElement(WebdavAuthcacheElement *elem);
static int InsertPlaceholder(WebdavAuthcacheInsertRec *insertRec);
static int gAuthcacheInitialized = 0;
static WebdavAuthcacheHeader gAuthcacheHeader;
extern char *dest_server;
static char * ParseChallenge(char *params, char **directive, char **value,
int *error);
static char * ParseChallenge(char *params, char **directive, char **value,
int *error)
{
char *token;
*directive = *value = NULL;
*error = 0;
params = SkipLWS(params);
if ( *params != '\0' )
{
token = params;
params = SkipToken(params);
require_action((*params != '\0') && (params != token),
malformedDirectiveName, *error = EINVAL);
*directive = malloc(params - token + 1);
require_action(*directive != NULL, malloc_directive,
*error = ENOMEM);
strncpy(*directive, token, params - token);
(*directive)[params - token] = '\x00';
if ( *params == '=')
{
++params;
if ( *params == '\"' )
{
++params;
token = params;
params = SkipQuotedString(params);
require_action(*params, malformedValueQuotedString,
*error = EINVAL);
*value = malloc(params - token + 1);
require_action(*value != NULL, malloc_value,
*error = ENOMEM);
strncpy(*value, token, params - token);
(*value)[params - token] = '\x00';
++params;
}
else
{
token = params;
params = SkipToken(params);
*value = malloc(params - token + 1);
require_action(*value != NULL, malloc_value,
*error = ENOMEM);
strncpy(*value, token, params - token);
(*value)[params - token] = '\x00';
}
params = SkipLWS(params);
if ( *params != '\0' )
{
require_action(*params == ',', missingCommaSeparator,
*error = EINVAL);
while ( *params == ',' )
{
++params;
}
}
}
else
{
params = SkipLWS(params);
}
}
return ( params );
missingCommaSeparator:
free(*value);
malloc_value:
malformedValueQuotedString:
free(*directive);
malloc_directive:
malformedDirectiveName:
while ( *params != '\0' )
{
++params;
}
return ( params );
}
static char * ParseQOPs(char *params, char **qopValue, int *error)
{
char *token;
*qopValue = NULL;
*error = 0;
params = SkipLWS(params);
if ( *params != '\0' )
{
token = params;
params = SkipToken(params);
require_action(params != token, malformedValueToken,
*error = EINVAL);
*qopValue = malloc(params - token + 1);
require_action(*qopValue != NULL, malloc_qopValue,
*error = ENOMEM);
strncpy(*qopValue, token, params - token);
(*qopValue)[params - token] = '\x00';
params = SkipLWS(params);
if ( *params != '\0' )
{
require_action(*params == ',', missingCommaSeparator,
*error = EINVAL);
while ( *params == ',' )
{
++params;
}
}
}
return ( params );
missingCommaSeparator:
free(*qopValue);
malloc_qopValue:
malformedValueToken:
while ( *params != '\0' )
{
++params;
}
return ( params );
}
struct AuthDataDigest
{
char *nonce;
char *opaque;
char *algorithm;
char *uriList;
int stale;
HASHHEX HA1;
char *qop;
u_int32_t nonceCount;
};
typedef struct AuthDataDigest AuthDataDigest;
struct AuthDataBasic
{
char *credentialsStr;
};
typedef struct AuthDataBasic AuthDataBasic;
static AuthDataDigest * AllocateAuthDataDigest(void);
static void FreeAuthDataDigest(void *authData);
static int ParseAuthParmsDigest(char *authParam, char **realmStr,
AuthDataDigest *authData);
static void MakeAuthHeaderDigest(WebdavAuthcacheRetrieveRec *retrieveRec,
WebdavAuthcacheElement *elem);
static int EvaluateDigest(WebdavAuthcacheEvaluateRec *evaluateRec,
char *authParam);
char *GetURI(char *params, char **uri, int *error);
static int AddURIToURIRec(char *uri, URIRec *theURIRec);
static int InsertDigest(WebdavAuthcacheInsertRec *insertRec, char *authParam);
static AuthDataBasic * AllocateAuthDataBasic(void);
static void FreeAuthDataBasic(void *authData);
static int ParseAuthParmsBasic(char *authParam, char **realmStr);
static void MakeAuthHeaderBasic(WebdavAuthcacheRetrieveRec *retrieveRec,
WebdavAuthcacheElement *elem);
static int EvaluateBasic(WebdavAuthcacheEvaluateRec *evaluateRec,
char *authParam);
static int InsertBasic(WebdavAuthcacheInsertRec *insertRec, char *authParam);
static AuthDataDigest * AllocateAuthDataDigest(void)
{
AuthDataDigest *authData;
authData = calloc(sizeof(AuthDataDigest), 1);
check(authData != NULL);
return ( authData );
}
static void FreeAuthDataDigest(void *authData)
{
AuthDataDigest *digestAuthData;
digestAuthData = authData;
if ( digestAuthData->uriList != NULL )
{
free(digestAuthData->uriList);
}
if ( digestAuthData->nonce != NULL )
{
free(digestAuthData->nonce);
}
if ( digestAuthData->opaque != NULL )
{
free(digestAuthData->opaque);
}
if ( digestAuthData->algorithm != NULL )
{
free(digestAuthData->algorithm);
}
if ( digestAuthData->qop != NULL )
{
free(digestAuthData->qop);
}
free(digestAuthData);
}
static int ParseAuthParmsDigest(char *authParam, char **realmStr,
AuthDataDigest *authData)
{
int error;
int directiveLength;
char *directive;
char *value;
error = EINVAL;
authData->stale = FALSE;
while ( *authParam != '\0' )
{
authParam = ParseChallenge(authParam, &directive, &value, &error);
require_noerr_quiet(error, ParseChallenge);
directiveLength = strlen(directive);
if ( (Constant_strlen("realm") == directiveLength) &&
(strncasecmp(directive, "realm", directiveLength) == 0) )
{
*realmStr = value;
}
else if ( (Constant_strlen("domain") == directiveLength) &&
(strncasecmp(directive, "domain", directiveLength) == 0) )
{
authData->uriList = value;
}
else if ( (Constant_strlen("nonce") == directiveLength) &&
(strncasecmp(directive, "nonce", directiveLength) == 0) )
{
authData->nonce = value;
authData->nonceCount = 0;
}
else if ( (Constant_strlen("opaque") == directiveLength) &&
(strncasecmp(directive, "opaque", directiveLength) == 0) )
{
authData->opaque = value;
}
else if ( (Constant_strlen("stale") == directiveLength) &&
(strncasecmp(directive, "stale", directiveLength) == 0) )
{
authData->stale = strncasecmp(value, "true", Constant_strlen("true")) == 0;
free (value);
}
else if ( (Constant_strlen("algorithm") == directiveLength) &&
(strncasecmp(directive, "algorithm", directiveLength) == 0) )
{
require_action_quiet( (Constant_strlen("MD5") == strlen(value)) &&
(strncasecmp(value, "MD5", Constant_strlen("MD5")) == 0),
unsupportedAlgorithm,
free(directive); free(value); error = EINVAL);
authData->algorithm = value;
}
else if ( (Constant_strlen("qop") == directiveLength) &&
(strncasecmp(directive, "qop", directiveLength) == 0) )
{
char *qopValueList;
char *qopValue;
authData->qop = NULL;
qopValueList = value;
while ( *qopValueList != '\0' )
{
qopValueList = ParseQOPs(qopValueList, &qopValue, &error);
if ( error != 0 )
{
break;
}
if ( (Constant_strlen("auth") == strlen(qopValue)) &&
(strncasecmp(qopValue, "auth", Constant_strlen("auth")) == 0) )
{
authData->qop = qopValue;
break;
}
else
{
free(qopValue);
}
}
free(value);
}
else
{
free(value);
}
free(directive);
}
require_action((*realmStr != NULL) && (authData->nonce != NULL),
missingDirectives, error = EINVAL);
error = 0;
missingDirectives:
unsupportedAlgorithm:
ParseChallenge:
return ( error );
}
static void MakeAuthHeaderDigest(WebdavAuthcacheRetrieveRec *retrieveRec,
WebdavAuthcacheElement *elem)
{
int error;
AuthDataDigest *authData;
char *credentialsStr;
unsigned int credentialsLength;
char *requestDigestStr;
char *existingAuthorization;
char *uriString;
char nonceCountStr[9];
authData = (AuthDataDigest*)elem->authData;
uriString = malloc(strlen(retrieveRec->uri) +
((retrieveRec->query != NULL) ? strlen(retrieveRec->query) : 0) + 1 );
require_action(uriString != NULL, malloc_uriString, error = ENOMEM);
strcpy(uriString, retrieveRec->uri);
if ( retrieveRec->query != NULL )
{
strcat(uriString, retrieveRec->query);
}
if ( elem->isProxy )
{
credentialsLength = sizeof("Proxy-Authorization: Digest\r\n");
}
else
{
credentialsLength = sizeof("Authorization: Digest\r\n");
}
credentialsLength += (Constant_strlen(" username=\"\"") + strlen(elem->username));
credentialsLength += (Constant_strlen(", realm=\"\"") + strlen(elem->realmStr));
credentialsLength += (Constant_strlen(", nonce=\"\"") + strlen(authData->nonce));
credentialsLength += (Constant_strlen(", uri=\"\"") + strlen(uriString));
credentialsLength += (Constant_strlen(", response=\"\"") + HASHHEXLEN);
if ( authData->algorithm != NULL )
{
credentialsLength += (Constant_strlen(", algorithm=\"\"") +
strlen(authData->algorithm));
}
if ( authData->opaque != NULL )
{
credentialsLength += (Constant_strlen(", opaque=\"\"") +
strlen(authData->opaque));
}
if ( authData->qop != NULL )
{
credentialsLength += (Constant_strlen(", qop=\"\"") +
strlen(authData->qop) +
Constant_strlen(", cnonce=\"\"") +
strlen(gAuthcacheHeader.cnonce) +
Constant_strlen(", nc=\"\"") +
8);
}
credentialsStr = malloc(credentialsLength);
require_action(credentialsStr != NULL, malloc_credentialsStr,
error = ENOMEM);
requestDigestStr = malloc(sizeof(HASHHEX));
require_action(requestDigestStr != NULL, malloc_requestDigestStr,
error = ENOMEM);
if ( authData->qop == NULL )
{
DigestCalcResponse(authData->HA1, authData->nonce, "", "", "",
retrieveRec->method, uriString, NULL, requestDigestStr);
}
else
{
++authData->nonceCount;
snprintf(nonceCountStr, sizeof(nonceCountStr), "%.8lx",
(long unsigned int)authData->nonceCount);
DigestCalcResponse(authData->HA1, authData->nonce, nonceCountStr,
gAuthcacheHeader.cnonce, authData->qop,
retrieveRec->method, uriString, NULL, requestDigestStr);
}
strcpy(credentialsStr, (elem->isProxy ?
"Proxy-Authorization: Digest" : "Authorization: Digest"));
strcat(credentialsStr, " username=\"");
strcat(credentialsStr, elem->username);
strcat(credentialsStr, "\", realm=\"");
strcat(credentialsStr, elem->realmStr);
strcat(credentialsStr, "\", nonce=\"");
strcat(credentialsStr, authData->nonce);
strcat(credentialsStr, "\", uri=\"");
strcat(credentialsStr, uriString);
strcat(credentialsStr, "\", response=\"");
strcat(credentialsStr, requestDigestStr);
strcat(credentialsStr, "\"");
if ( authData->algorithm != NULL )
{
strcat(credentialsStr, ", algorithm=\"");
strcat(credentialsStr, authData->algorithm);
strcat(credentialsStr, "\"");
}
if ( authData->opaque != NULL )
{
strcat(credentialsStr, ", opaque=\"");
strcat(credentialsStr, authData->opaque);
strcat(credentialsStr, "\"");
}
if (authData->qop != NULL )
{
strcat(credentialsStr, ", qop=\"");
strcat(credentialsStr, authData->qop);
strcat(credentialsStr, "\", nc=\"");
strcat(credentialsStr, nonceCountStr);
strcat(credentialsStr, "\", cnonce=\"");
strcat(credentialsStr, gAuthcacheHeader.cnonce);
strcat(credentialsStr, "\"");
}
strcat(credentialsStr, "\r\n");
if ( retrieveRec->authorization == NULL )
{
retrieveRec->authorization = credentialsStr;
}
else
{
existingAuthorization = retrieveRec->authorization;
retrieveRec->authorization =
malloc(strlen(existingAuthorization) +
credentialsLength);
require_action(retrieveRec->authorization != NULL, malloc_authorization,
retrieveRec->authorization = existingAuthorization; free (credentialsStr));
strcpy(retrieveRec->authorization, existingAuthorization);
strcat(retrieveRec->authorization, credentialsStr);
free(credentialsStr);
free(existingAuthorization);
}
malloc_authorization:
free(requestDigestStr);
malloc_requestDigestStr:
malloc_credentialsStr:
free(uriString);
malloc_uriString:
return;
}
char *GetURI(char *params, char **uri, int *error)
{
char *stringStart;
*uri = NULL;
*error = 0;
if ( *params != '\0' )
{
stringStart = params;
while ( *params != '\0' )
{
if ( *params != ' ' )
{
++params;
continue;
}
break;
}
*uri = malloc(params - stringStart + 1);
require_action(*uri != NULL, malloc_uri, *error = ENOMEM);
strncpy(*uri, stringStart, params - stringStart);
(*uri)[params - stringStart] = '\0';
while ( *params != '\0' )
{
if ( *params == ' ' )
{
++params;
continue;
}
break;
}
}
malloc_uri:
return ( params );
}
static void FreeURIRec(URIRec *theURIRec)
{
if ( theURIRec->server )
{
free(theURIRec->server);
}
if ( theURIRec->absPath )
{
free(theURIRec->absPath);
}
free(theURIRec);
}
static int AddURIToURIRec(char *uri, URIRec *theURIRec)
{
int error;
theURIRec->server = theURIRec->absPath = NULL;
if ( *uri == '/' )
{
theURIRec->server = malloc(strlen(dest_server) + 1);
require_action(theURIRec->server != NULL, malloc_theURIRec_server,
error = ENOMEM);
strcpy(theURIRec->server, dest_server);
theURIRec->absPath = malloc(strlen(uri) + 1);
require_action(theURIRec->absPath != NULL, malloc_theURIRec_absPath,
error = ENOMEM);
strcpy(theURIRec->absPath, uri);
}
else
{
char *bytes;
char *server;
bytes = uri;
while ( *bytes != '\0' )
{
if ( *bytes == '/' && bytes[1] == '/' )
{
bytes += 2;
break;
}
++bytes;
}
require_action(*bytes != '\0', invalidAbsoluteURI, error = EINVAL);
server = bytes;
while ( *bytes != '\0' )
{
if ( (*bytes == ':') || (*bytes == '/') )
{
break;
}
++bytes;
}
theURIRec->server = malloc(bytes - server + 1);
require_action(theURIRec->server != NULL, malloc_theURIRec_server,
error = ENOMEM);
strncpy(theURIRec->server, server, bytes - server);
theURIRec->server[bytes - server] = '\0';
if ( *bytes == ':' )
{
while ( *bytes != '\0' )
{
if ( *bytes == '/' )
{
break;
}
++bytes;
}
}
theURIRec->absPath = malloc(strlen(bytes) + 1);
require_action(theURIRec->absPath != NULL, malloc_theURIRec_absPath,
error = ENOMEM);
strcpy(theURIRec->absPath, bytes);
}
theURIRec->serverLen = strlen(theURIRec->server);
theURIRec->absPathLen = strlen(theURIRec->absPath);
return ( 0 );
malloc_theURIRec_server:
malloc_theURIRec_absPath:
invalidAbsoluteURI:
return ( error );
}
static int UpdateElementDigest(WebdavAuthcacheElement *elem)
{
int error;
AuthDataDigest *authData;
char *params;
URIRec *theURIRec;
char *uri;
URIRec *domain;
URIRec *nextDomain;
authData = elem->authData;
error = 0;
DigestCalcHA1( authData->algorithm == NULL ? "" : authData->algorithm,
elem->username, elem->realmStr, elem->password, "", "", authData->HA1);
domain = elem->domainHead;
elem->domainHead = NULL;
while ( domain != NULL )
{
nextDomain = domain->next;
FreeURIRec(domain);
domain = nextDomain;
}
if ( !elem->isProxy && (authData->uriList != NULL) )
{
params = authData->uriList;
while ( *params != '\0' )
{
theURIRec = malloc(sizeof(URIRec));
require_action(theURIRec != NULL, malloc_theURIRec, error = ENOMEM);
params = GetURI(params, &uri, &error);
require_noerr_action_quiet(error, GetURI, free(theURIRec));
error = AddURIToURIRec(uri, theURIRec);
require_noerr_quiet(error, AddURIToURIRec);
free(uri);
theURIRec->next = elem->domainHead;
elem->domainHead = theURIRec;
++elem->uriCount;
}
}
return ( error );
AddURIToURIRec:
free(uri);
GetURI:
FreeURIRec(theURIRec);
malloc_theURIRec:
return ( error );
}
static int EvaluateDigest(WebdavAuthcacheEvaluateRec *evaluateRec,
char *authParam)
{
int error;
WebdavAuthcacheElement *elem;
AuthDataDigest *authData;
int foundPlaceHolder, foundElementToUpdate;
authData = AllocateAuthDataDigest();
require_action_quiet(authData != NULL, AllocateAuthDataDigest, error = ENOMEM);
error = ParseAuthParmsDigest(authParam, &evaluateRec->realmStr, authData);
require_noerr_quiet(error, ParseAuthParmsDigest);
elem = NULL;
foundPlaceHolder = foundElementToUpdate = FALSE;
while ( TRUE )
{
GetNextNextWebdavAuthcacheElement(&elem);
if ( elem == NULL )
{
break;
}
if ( (evaluateRec->uid == elem->uid) &&
(evaluateRec->isProxy == elem->isProxy) )
{
if ( elem->realmStr == NULL )
{
foundPlaceHolder = TRUE;
break;
}
else if ( strcmp(evaluateRec->realmStr, elem->realmStr) == 0 )
{
foundElementToUpdate = TRUE;
break;
}
}
}
if ( authData->stale )
{
require_action(foundElementToUpdate, elementToUpdateNotFound, error = EINVAL);
FreeAuthDataDigest((AuthDataDigest *)elem->authData);
(AuthDataDigest *)elem->authData = authData;
error = UpdateElementDigest(elem);
evaluateRec->updated = TRUE;
}
else
{
FreeAuthDataDigest(authData);
if ( foundPlaceHolder )
{
evaluateRec->uiNotNeeded = TRUE;
}
}
return ( 0 );
elementToUpdateNotFound:
ParseAuthParmsDigest:
FreeAuthDataDigest(authData);
AllocateAuthDataDigest:
return ( error );
}
static int InsertDigest(WebdavAuthcacheInsertRec *insertRec, char *authParam)
{
int error;
char *realmStr;
WebdavAuthcacheElement *elem;
int elemInCache;
AuthDataDigest *authData;
authData = AllocateAuthDataDigest();
require_action_quiet(authData != NULL, AllocateAuthDataDigest, error = ENOMEM);
error = ParseAuthParmsDigest(authParam, &realmStr, authData);
require_noerr_quiet(error, ParseAuthParmsDigest);
elem = NULL;
while ( TRUE )
{
GetNextNextWebdavAuthcacheElement(&elem);
if ( elem == NULL )
{
break;
}
if ( (insertRec->uid == elem->uid) &&
(insertRec->isProxy == elem->isProxy) )
{
if ( elem->realmStr == NULL )
{
break;
}
require_action(strcmp(realmStr, elem->realmStr) != 0,
DuplicateDigestAuthcacheElement, error = 0);
}
}
if ( elem == NULL )
{
elemInCache = FALSE;
elem = calloc(sizeof(WebdavAuthcacheElement), 1);
require_action(elem != NULL, calloc_elem, error = ENOMEM);
elem->uid = insertRec->uid;
elem->isProxy = insertRec->isProxy;
elem->username = malloc(strlen(insertRec->username) + 1);
require_action(elem->username != NULL, malloc_elem_username,
error = ENOMEM);
strcpy(elem->username, insertRec->username);
elem->password = malloc(strlen(insertRec->password) + 1);
require_action(elem->password != NULL, malloc_elem_password,
error = ENOMEM);
strcpy(elem->password, insertRec->password);
}
else
{
elemInCache = TRUE;
}
elem->realmStr = realmStr;
elem->scheme = kChallengeSecurityLevelDigest;
elem->makeProcPtr = MakeAuthHeaderDigest;
elem->freeProcPtr = FreeAuthDataDigest;
elem->authData = authData;
error = UpdateElementDigest(elem);
if ( !elemInCache )
{
EnqueueWebdavAuthcacheElement(elem);
}
return ( 0 );
malloc_elem_password:
if ( !elemInCache )
{
free(elem->username);
}
malloc_elem_username:
if ( !elemInCache )
{
free(elem);
}
calloc_elem:
DuplicateDigestAuthcacheElement:
free(realmStr);
ParseAuthParmsDigest:
FreeAuthDataDigest(authData);
AllocateAuthDataDigest:
return ( error );
}
static AuthDataBasic * AllocateAuthDataBasic(void)
{
AuthDataBasic *authData;
authData = calloc(sizeof(AuthDataBasic), 1);
check(authData != NULL);
return ( authData );
}
static void FreeAuthDataBasic(void *authData)
{
AuthDataBasic *basicAuthData;
basicAuthData = authData;
if ( basicAuthData->credentialsStr != NULL )
{
free(basicAuthData->credentialsStr);
}
free(basicAuthData);
}
static int ParseAuthParmsBasic(char *authParam, char **realmStr)
{
int error;
int directiveLength;
char *directive;
char *value;
error = EINVAL;
while ( *authParam != '\0' )
{
authParam = ParseChallenge(authParam, &directive, &value, &error);
require_noerr_quiet(error, ParseChallenge);
directiveLength = strlen(directive);
require_action((Constant_strlen("realm") == directiveLength) &&
(strncasecmp(directive, "realm", directiveLength) == 0),
unsupportedDirective,
free(directive); free(value); error = EINVAL);
free(directive);
*realmStr = value;
error = 0;
}
check_noerr_string(error, "missing Realm directive");
unsupportedDirective:
ParseChallenge:
return ( error );
}
static void MakeAuthHeaderBasic(WebdavAuthcacheRetrieveRec *retrieveRec,
WebdavAuthcacheElement *elem)
{
AuthDataBasic *authData;
char *existingAuthorization;
authData = (AuthDataBasic*)elem->authData;
if ( retrieveRec->authorization == NULL )
{
retrieveRec->authorization =
malloc(strlen(authData->credentialsStr) + 1);
require(retrieveRec->authorization != NULL, malloc_authorization);
strcpy(retrieveRec->authorization, authData->credentialsStr);
}
else
{
existingAuthorization = retrieveRec->authorization;
retrieveRec->authorization = malloc(
strlen(existingAuthorization) +
strlen(authData->credentialsStr) + 1);
require(retrieveRec->authorization != NULL, malloc_authorization);
strcpy(retrieveRec->authorization, existingAuthorization);
strcat(retrieveRec->authorization, authData->credentialsStr);
free (existingAuthorization);
}
malloc_authorization:
return;
}
static int EvaluateBasic(WebdavAuthcacheEvaluateRec *evaluateRec,
char *authParam)
{
int error;
WebdavAuthcacheElement *elem;
error = ParseAuthParmsBasic(authParam, &evaluateRec->realmStr);
require_noerr_quiet(error, ParseAuthParmsBasic);
evaluateRec->updated = FALSE;
elem = NULL;
while ( TRUE )
{
GetNextNextWebdavAuthcacheElement(&elem);
if ( elem == NULL )
{
break;
}
if ( (evaluateRec->uid == elem->uid) &&
(evaluateRec->isProxy == elem->isProxy) &&
(elem->realmStr == NULL) )
{
evaluateRec->uiNotNeeded = TRUE;
break;
}
}
ParseAuthParmsBasic:
return ( error );
}
static int InsertBasic(WebdavAuthcacheInsertRec *insertRec, char *authParam)
{
int error;
char *realmStr;
WebdavAuthcacheElement *elem;
int elemInCache;
AuthDataBasic *authData;
char *userPass;
char *basicCredentials;
error = ParseAuthParmsBasic(authParam, &realmStr);
require_noerr_quiet(error, ParseAuthParmsBasic);
elem = NULL;
while ( TRUE )
{
GetNextNextWebdavAuthcacheElement(&elem);
if ( elem == NULL )
{
break;
}
if ( (insertRec->uid == elem->uid) &&
(insertRec->isProxy == elem->isProxy) )
{
if ( elem->realmStr == NULL )
{
break;
}
require_action(strcmp(realmStr, elem->realmStr) != 0,
DuplicateBasicAuthcacheElement, error = 0);
}
}
if ( elem == NULL )
{
elemInCache = FALSE;
elem = calloc(sizeof(WebdavAuthcacheElement), 1);
require_action(elem != NULL, calloc_elem, error = ENOMEM);
elem->uid = insertRec->uid;
elem->isProxy = insertRec->isProxy;
elem->username = malloc(strlen(insertRec->username) + 1);
require_action(elem->username != NULL, malloc_elem_username,
error = ENOMEM);
strcpy(elem->username, insertRec->username);
elem->password = malloc(strlen(insertRec->password) + 1);
require_action(elem->password != NULL, malloc_elem_password,
error = ENOMEM);
strcpy(elem->password, insertRec->password);
}
else
{
elemInCache = TRUE;
}
authData = AllocateAuthDataBasic();
require_action_quiet(authData != NULL, AllocateAuthDataBasic, error = ENOMEM);
userPass = malloc(strlen(elem->username) +
strlen(elem->password) + 2);
require_action(userPass != NULL, malloc_userPass, error = ENOMEM);
strcpy(userPass, elem->username);
strcat(userPass, ":");
strcat(userPass, elem->password);
basicCredentials = to_base64(userPass, strlen(userPass));
require_action(basicCredentials != NULL, to_base64, error = ENOMEM);
authData->credentialsStr =
malloc((insertRec->isProxy ?
sizeof("Proxy-Authorization: Basic \r\n") :
sizeof("Authorization: Basic \r\n"))
+ strlen(basicCredentials));
require_action(authData->credentialsStr != NULL, malloc_credentialsStr,
error = ENOMEM);
strcpy(authData->credentialsStr, (insertRec->isProxy ?
"Proxy-Authorization: Basic " : "Authorization: Basic "));
strcat(authData->credentialsStr, basicCredentials);
strcat(authData->credentialsStr, "\r\n");
elem->realmStr = realmStr;
elem->scheme = kChallengeSecurityLevelBasic;
elem->uriCount = 0;
elem->domainHead = NULL;
elem->makeProcPtr = MakeAuthHeaderBasic;
elem->freeProcPtr = FreeAuthDataBasic;
elem->authData = authData;
if ( !elemInCache )
{
EnqueueWebdavAuthcacheElement(elem);
}
free(userPass);
free(basicCredentials);
return ( 0 );
malloc_credentialsStr:
free(basicCredentials);
to_base64:
free(userPass);
malloc_userPass:
FreeAuthDataBasic(authData);
AllocateAuthDataBasic:
if ( !elemInCache )
{
free(elem->password);
}
malloc_elem_password:
if ( !elemInCache )
{
free(elem->username);
}
malloc_elem_username:
if ( !elemInCache )
{
free(elem);
}
calloc_elem:
DuplicateBasicAuthcacheElement:
free(realmStr);
ParseAuthParmsBasic:
return ( error );
}
static char * GetNextChallenge(char *params,
ChallengeSecurityLevelType *level, char **authParam, int *error)
{
char *directive;
char *value;
char *authParamStart;
char *previousParams;
int schemeLength;
*level = 0;
*authParam = NULL;
params = ParseChallenge(params, &directive, &value, error);
require_noerr_quiet(*error, ParseChallenge);
require_action(value == NULL, noSchemeName, free(directive); *error = EINVAL);
schemeLength = strlen(directive);
if ( (Constant_strlen("Basic") == schemeLength) &&
(strncasecmp(directive, "Basic", schemeLength) == 0) )
{
*level = kChallengeSecurityLevelBasic;
}
else if ( (Constant_strlen("Digest") == schemeLength) &&
(strncasecmp(directive, "Digest", schemeLength) == 0) )
{
*level = kChallengeSecurityLevelDigest;
}
free(directive);
authParamStart = params;
while ( *params != '\0' )
{
previousParams = params;
params = ParseChallenge(params, &directive, &value, error);
require_noerr_quiet(*error, ParseChallenge);
if ( value != NULL )
{
free(value);
free(directive);
}
else
{
free(directive);
params = previousParams;
break;
}
}
if ( *level != 0 )
{
*authParam = malloc(params - authParamStart + 1);
require_action(*authParam != NULL, malloc_authParam, *error = ENOMEM);
strncpy(*authParam, authParamStart, params - authParamStart);
(*authParam)[params - authParamStart] = '\x00';
}
malloc_authParam:
noSchemeName:
ParseChallenge:
return ( params );
}
static void GetNextNextWebdavAuthcacheElement(WebdavAuthcacheElement **elem)
{
if ( *elem == NULL )
{
*elem = gAuthcacheHeader.head;
}
else
{
*elem = (*elem)->next;
}
}
static void EnqueueWebdavAuthcacheElement(WebdavAuthcacheElement *elem)
{
elem->next = gAuthcacheHeader.head;
gAuthcacheHeader.head = elem;
++gAuthcacheHeader.count;
}
static WebdavAuthcacheElement * DequeueWebdavAuthcacheElement(uid_t uid,
int isProxy, char *realmStr)
{
WebdavAuthcacheElement *elem;
WebdavAuthcacheElement *prevElem;
prevElem = NULL;
elem = gAuthcacheHeader.head;
while ( elem != NULL )
{
if ( (uid == elem->uid) &&
(isProxy == elem->isProxy) )
{
if ( elem->realmStr != NULL )
{
if ( strcmp(realmStr, elem->realmStr) == 0 )
{
break;
}
}
}
prevElem = elem;
elem = elem->next;
}
require(elem != NULL, elementNotFound);
if ( prevElem == NULL )
{
gAuthcacheHeader.head = elem->next;
}
else
{
prevElem->next = elem->next;
}
--gAuthcacheHeader.count;
elementNotFound:
return ( elem );
}
static void FreeWebdavAuthcacheElement(WebdavAuthcacheElement *elem)
{
URIRec *domain;
URIRec *nextDomain;
if ( elem->realmStr != NULL )
{
free(elem->realmStr);
}
if ( elem->username != NULL )
{
free(elem->username);
}
if ( elem->password != NULL )
{
free(elem->password);
}
domain = elem->domainHead;
elem->domainHead = NULL;
while ( domain != NULL )
{
nextDomain = domain->next;
FreeURIRec(domain);
domain = nextDomain;
}
if ( elem->authData != NULL )
{
CallFreeAuthDataProc(elem->freeProcPtr, elem->authData);
}
free(elem);
}
static int InsertPlaceholder(WebdavAuthcacheInsertRec *insertRec)
{
int error;
WebdavAuthcacheElement *elem;
elem = NULL;
while ( TRUE )
{
GetNextNextWebdavAuthcacheElement(&elem);
if ( elem == NULL )
{
break;
}
if ( (insertRec->uid == elem->uid) &&
(insertRec->isProxy == elem->isProxy) )
{
require_action(elem->realmStr != NULL,
DuplicateBasicAuthcacheElement, error = EINVAL);
}
}
elem = calloc(sizeof(WebdavAuthcacheElement), 1);
require_action(elem != NULL, calloc_elem, error = ENOMEM);
elem->username = malloc(strlen(insertRec->username) + 1);
require_action(elem->username != NULL, malloc_elem_username,
error = ENOMEM);
strcpy(elem->username, insertRec->username);
elem->password = malloc(strlen(insertRec->password) + 1);
require_action(elem->password != NULL, malloc_elem_password,
error = ENOMEM);
strcpy(elem->password, insertRec->password);
elem->uid = insertRec->uid;
elem->isProxy = insertRec->isProxy;
elem->realmStr = NULL;
elem->scheme = 0xffffffff;
elem->uriCount = 0;
elem->domainHead = NULL;
elem->makeProcPtr = NULL;
elem->freeProcPtr = NULL;
elem->authData = NULL;
EnqueueWebdavAuthcacheElement(elem);
return ( 0 );
malloc_elem_password:
free(elem->username);
malloc_elem_username:
free(elem);
calloc_elem:
DuplicateBasicAuthcacheElement:
return ( error );
}
static int HasMatchingURI(char *uri, WebdavAuthcacheElement *elem)
{
int result;
int error;
URIRec inputURIRec;
URIRec *domain;
error = AddURIToURIRec(uri, &inputURIRec);
require_noerr_quiet(error, AddURIToURIRec);
result = FALSE;
domain = elem->domainHead;
while ( domain != NULL )
{
if ( (inputURIRec.absPathLen >= domain->absPathLen) &&
(inputURIRec.serverLen == domain->serverLen) )
{
if ( (strncasecmp(inputURIRec.server, domain->server,
inputURIRec.serverLen) == 0) &&
(strncasecmp(inputURIRec.absPath, domain->absPath,
domain->absPathLen) == 0) )
{
result = TRUE;
break;
}
}
domain = domain->next;
}
free(inputURIRec.server);
free(inputURIRec.absPath);
return ( result );
AddURIToURIRec:
return ( FALSE );
}
int webdav_authcache_init(void)
{
int error;
pthread_mutexattr_t mutexattr;
time_t timeStamp;
pid_t privateKey;
char buf[18];
require_action(gAuthcacheInitialized == 0, AlreadyInitialized, error = 0);
gAuthcacheHeader.count = 0;
gAuthcacheHeader.head = NULL;
gAuthcacheHeader.proxyElementCount = 0;
gAuthcacheHeader.cnonce = NULL;
error = pthread_mutexattr_init(&mutexattr);
require_noerr(error, pthread_mutexattr_init);
error = pthread_mutex_init(&(gAuthcacheHeader.lock), &mutexattr);
require_noerr(error, pthread_mutex_init);
timeStamp = time(NULL);
privateKey = getpid();
snprintf(buf, sizeof(buf), "%.8lx:%.8lx", (long unsigned int)timeStamp,
(long unsigned int)privateKey);
gAuthcacheHeader.cnonce = to_base64(buf, strlen(buf));
gAuthcacheInitialized = 1;
pthread_mutex_init:
pthread_mutexattr_init:
AlreadyInitialized:
return (error);
}
int webdav_authcache_evaluate(WebdavAuthcacheEvaluateRec *evaluateRec)
{
int error, error2;
char *authParam;
char *params;
ChallengeSecurityLevelType level;
error = pthread_mutex_lock(&(gAuthcacheHeader.lock));
require_noerr(error, pthread_mutex_lock);
evaluateRec->level = 0;
evaluateRec->updated = FALSE;
evaluateRec->uiNotNeeded = FALSE;
evaluateRec->realmStr = NULL;
params = evaluateRec->challenge;
while ( *params != '\0' )
{
params = GetNextChallenge(params, &level, &authParam, &error);
require_noerr(error, GetNextChallenge);
if ( level != 0 )
{
if ( level == kChallengeSecurityLevelBasic )
{
error = EvaluateBasic(evaluateRec, authParam);
}
else if ( level == kChallengeSecurityLevelDigest )
{
error = EvaluateDigest(evaluateRec, authParam);
}
if ( error == 0 )
{
if ( level > evaluateRec->level )
{
evaluateRec->level = level;
}
}
free(authParam);
}
}
GetNextChallenge:
if ( evaluateRec->level == 0 )
{
debug_string("unsupported scheme");
error = EACCES;
}
else
{
error = 0;
}
error2 = pthread_mutex_unlock(&(gAuthcacheHeader.lock));
if ( error2 != 0 )
{
error = error2;
}
pthread_mutex_lock:
return ( error );
}
int webdav_authcache_insert(WebdavAuthcacheInsertRec *insertRec)
{
int error, error2;
char *authParam;
char *params;
ChallengeSecurityLevelType level;
error = pthread_mutex_lock(&(gAuthcacheHeader.lock));
require_noerr(error, pthread_mutex_lock);
if ( insertRec->challenge != NULL )
{
params = insertRec->challenge;
while ( *params != '\0' )
{
params = GetNextChallenge(params, &level, &authParam, &error);
require_noerr(error, GetNextChallenge);
if ( level == insertRec->level )
{
if ( level == kChallengeSecurityLevelBasic )
{
error = InsertBasic(insertRec, authParam);
}
else if ( level == kChallengeSecurityLevelDigest )
{
error = InsertDigest(insertRec, authParam);
}
free(authParam);
break;
}
else
{
free(authParam);
}
}
GetNextChallenge:
;
}
else
{
error = InsertPlaceholder(insertRec);
}
if ( (error == 0) && insertRec->isProxy )
{
verify((++gAuthcacheHeader.proxyElementCount) > 0);
}
error2 = pthread_mutex_unlock(&(gAuthcacheHeader.lock));
if ( error2 != 0 )
{
error = error2;
}
pthread_mutex_lock:
return ( error );
}
int webdav_authcache_retrieve(WebdavAuthcacheRetrieveRec *retrieveRec)
{
int error, error2;
WebdavAuthcacheElement *elem;
WebdavAuthcacheElement *lastMatch;
int foundServer, foundProxy;
error = pthread_mutex_lock(&(gAuthcacheHeader.lock));
require_noerr(error, pthread_mutex_lock);
lastMatch = NULL;
foundServer = FALSE;
foundProxy = (gAuthcacheHeader.proxyElementCount == 0);
retrieveRec->authorization = NULL;
elem = NULL;
while ( (foundServer == FALSE) || (foundProxy == FALSE) )
{
GetNextNextWebdavAuthcacheElement(&elem);
if ( elem == NULL )
{
break;
}
if ( (retrieveRec->uid == elem->uid) && (elem->realmStr != NULL) )
{
if ( (elem->domainHead == NULL) ||
HasMatchingURI(retrieveRec->uri, elem) )
{
if ( elem->isProxy )
{
check(foundProxy == FALSE);
foundProxy = TRUE;
}
else
{
check(foundServer == FALSE);
foundServer = TRUE;
}
check(elem->makeProcPtr != NULL);
CallMakeAuthHeaderProc(elem->makeProcPtr,
retrieveRec, elem);
require_action(retrieveRec->authorization != NULL,
CallMakeAuthHeaderProc, error = EACCES);
}
}
}
CallMakeAuthHeaderProc:
error2 = pthread_mutex_unlock(&(gAuthcacheHeader.lock));
if ( error2 != 0 )
{
error = error2;
}
pthread_mutex_lock:
return ( error );
}
int webdav_authcache_remove(WebdavAuthcacheRemoveRec *removeRec)
{
int error, error2;
WebdavAuthcacheElement *elem;
error = pthread_mutex_lock(&(gAuthcacheHeader.lock));
require_noerr(error, pthread_mutex_lock);
elem = DequeueWebdavAuthcacheElement(removeRec->uid, removeRec->isProxy,
removeRec->realmStr);
require_action_quiet(elem != NULL, DequeueWebdavAuthcacheElement,
error = ENOENT);
FreeWebdavAuthcacheElement(elem);
if ( removeRec->isProxy )
{
verify((--gAuthcacheHeader.proxyElementCount) >= 0);
}
DequeueWebdavAuthcacheElement:
error2 = pthread_mutex_unlock(&(gAuthcacheHeader.lock));
if ( error2 != 0 )
{
error = error2;
}
pthread_mutex_lock:
return ( error );
}