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>
#include <Security/SecKey.h>
#include <Security/SecKeychain.h>
#include <Security/SecKeychainItem.h>
#include <Security/SecKeychainSearch.h>
#include "webdav_authentication.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 "webdavd.h"
#include "webdav_authcache.h"
#ifdef 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", componentNameString, assertionString);
else
syslog(LOG_INFO, "Check failed: %s:", componentNameString);
if ( exceptionLabelString != NULL )
syslog(LOG_INFO, " %s", exceptionLabelString);
if ( errorString != NULL )
syslog(LOG_INFO, " %s", errorString);
if ( fileName != NULL )
syslog(LOG_INFO, " file: %s", fileName);
if ( lineNumber != 0 )
syslog(LOG_INFO, " line: %ld", lineNumber);
if ( errorCode != 0 )
syslog(LOG_INFO, " error: %d", 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;
AuthFlagsType authflags;
};
struct WebdavAuthcacheHeader
{
pthread_mutex_t lock;
unsigned long count;
WebdavAuthcacheElement *head;
int proxyElementCount;
char *cnonce;
unsigned long generation;
};
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 OSStatus KeychainItemCopyAccountPassword(SecKeychainItemRef itemRef, char *user, char *pass);
static int gAuthcacheInitialized = 0;
static WebdavAuthcacheHeader gAuthcacheHeader;
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((size_t)(params - token + 1));
require_action(*directive != NULL, malloc_directive,
*error = ENOMEM);
strncpy(*directive, token, (size_t)(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((size_t)(params - token + 1));
require_action(*value != NULL, malloc_value,
*error = ENOMEM);
strncpy(*value, token, (size_t)(params - token));
(*value)[params - token] = '\x00';
++params;
}
else
{
token = params;
params = SkipToken(params);
*value = malloc((size_t)(params - token + 1));
require_action(*value != NULL, malloc_value,
*error = ENOMEM);
strncpy(*value, token, (size_t)(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;
}
syslog(LOG_ERR, "ParseChallenge: %s", strerror(*error));
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((size_t)(params - token + 1));
require_action(*qopValue != NULL, malloc_qopValue,
*error = ENOMEM);
strncpy(*qopValue, token, (size_t)(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;
}
syslog(LOG_ERR, "ParseQOPs: %s", strerror(*error));
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);
if (authData == NULL)
{
syslog(LOG_ERR, "AllocateAuthDataDigest: %s", strerror(errno));
}
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", (size_t)directiveLength) == 0) )
{
*realmStr = value;
}
else if ( (Constant_strlen("domain") == directiveLength) &&
(strncasecmp(directive, "domain", (size_t)directiveLength) == 0) )
{
authData->uriList = value;
}
else if ( (Constant_strlen("nonce") == directiveLength) &&
(strncasecmp(directive, "nonce", (size_t)directiveLength) == 0) )
{
authData->nonce = value;
authData->nonceCount = 0;
}
else if ( (Constant_strlen("opaque") == directiveLength) &&
(strncasecmp(directive, "opaque", (size_t)directiveLength) == 0) )
{
authData->opaque = value;
}
else if ( (Constant_strlen("stale") == directiveLength) &&
(strncasecmp(directive, "stale", (size_t)directiveLength) == 0) )
{
authData->stale = strncasecmp(value, "true", Constant_strlen("true")) == 0;
free (value);
}
else if ( (Constant_strlen("algorithm") == directiveLength) &&
(strncasecmp(directive, "algorithm", (size_t)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", (size_t)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;
return ( error );
missingDirectives:
unsupportedAlgorithm:
syslog(LOG_ERR, "ParseAuthParmsDigest: %s", strerror(error));
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];
error = 0;
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:
if ( error )
{
syslog(LOG_ERR, "MakeAuthHeaderDigest: %s", strerror(error));
}
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((size_t)(params - stringStart + 1));
require_action(*uri != NULL, malloc_uri, *error = ENOMEM);
strncpy(*uri, stringStart, (size_t)(params - stringStart));
(*uri)[params - stringStart] = '\0';
while ( *params != '\0' )
{
if ( *params == ' ' )
{
++params;
continue;
}
break;
}
}
malloc_uri:
if ( *error )
{
syslog(LOG_ERR, "GetURI: %s", strerror(*error));
}
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 == '/' || *uri == '\0' )
{
theURIRec->server = malloc(strlen(dest_server) + 1);
require_action(theURIRec->server != NULL, malloc_theURIRec_server,
error = ENOMEM);
strcpy(theURIRec->server, dest_server);
if ( *uri != '\0' )
{
theURIRec->absPath = percent_decode(uri);
}
else
{
theURIRec->absPath = malloc(2);
if ( theURIRec->absPath )
{
theURIRec->absPath[0] = '/';
theURIRec->absPath[1] = '\0';
}
}
require_action(theURIRec->absPath != NULL, malloc_theURIRec_absPath,
error = ENOMEM);
}
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((size_t)(bytes - server + 1));
require_action(theURIRec->server != NULL, malloc_theURIRec_server,
error = ENOMEM);
strncpy(theURIRec->server, server, (size_t)(bytes - server));
theURIRec->server[bytes - server] = '\0';
if ( *bytes == ':' )
{
while ( *bytes != '\0' )
{
if ( *bytes == '/' )
{
break;
}
++bytes;
}
}
if ( *bytes != '\0' )
{
theURIRec->absPath = percent_decode(bytes);
}
else
{
theURIRec->absPath = malloc(2);
if ( theURIRec->absPath )
{
theURIRec->absPath[0] = '/';
theURIRec->absPath[1] = '\0';
}
}
require_action(theURIRec->absPath != NULL, malloc_theURIRec_absPath,
error = ENOMEM);
}
theURIRec->serverLen = strlen(theURIRec->server);
theURIRec->absPathLen = strlen(theURIRec->absPath);
return ( 0 );
malloc_theURIRec_server:
malloc_theURIRec_absPath:
invalidAbsoluteURI:
syslog(LOG_ERR, "AddURIToURIRec: %s", strerror(error));
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);
return ( error );
malloc_theURIRec:
syslog(LOG_ERR, "UpdateElementDigest: %s", strerror(error));
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:
syslog(LOG_ERR, "EvaluateDigest: %s", strerror(error));
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;
elem->authflags = ((insertRec->uid != 0) && (insertRec->uid != 1)) ?
insertRec->authflags : kAuthNone;
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:
syslog(LOG_ERR, "InsertDigest: %s", strerror(error));
DuplicateDigestAuthcacheElement:
free(realmStr);
ParseAuthParmsDigest:
FreeAuthDataDigest(authData);
AllocateAuthDataDigest:
return ( error );
}
static AuthDataBasic * AllocateAuthDataBasic(void)
{
AuthDataBasic *authData;
authData = calloc(sizeof(AuthDataBasic), 1);
if (authData == NULL)
{
syslog(LOG_ERR, "AllocateAuthDataBasic: %s", strerror(errno));
}
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", (size_t)directiveLength) == 0),
unsupportedDirective,
free(directive); free(value); error = EINVAL);
free(directive);
*realmStr = value;
error = 0;
}
check_noerr_string(error, "missing Realm directive");
unsupportedDirective:
if ( error )
{
syslog(LOG_ERR, "ParseAuthParmsBasic: %s", strerror(error));
}
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);
}
return;
malloc_authorization:
syslog(LOG_ERR, "MakeAuthHeaderBasic: %s", strerror(errno));
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;
elem->authflags = ((insertRec->uid != 0) && (insertRec->uid != 1)) ?
insertRec->authflags : kAuthNone;
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:
syslog(LOG_ERR, "InsertBasic: %s", strerror(error));
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", (size_t)schemeLength) == 0) )
{
*level = kChallengeSecurityLevelBasic;
}
else if ( (Constant_strlen("Digest") == schemeLength) &&
(strncasecmp(directive, "Digest", (size_t)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((size_t)(params - authParamStart + 1));
require_action(*authParam != NULL, malloc_authParam, *error = ENOMEM);
strncpy(*authParam, authParamStart, (size_t)(params - authParamStart));
(*authParam)[params - authParamStart] = '\x00';
}
return ( params );
malloc_authParam:
noSchemeName:
syslog(LOG_ERR, "GetNextChallenge: %s", strerror(*error));
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;
elem->authflags = kAuthFromPlaceholder;
EnqueueWebdavAuthcacheElement(elem);
return ( 0 );
malloc_elem_password:
free(elem->username);
malloc_elem_username:
free(elem);
calloc_elem:
DuplicateBasicAuthcacheElement:
syslog(LOG_ERR, "InsertPlaceholder: %s", strerror(error));
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) &&
(strncmp(inputURIRec.absPath, domain->absPath,
domain->absPathLen) == 0) )
{
result = TRUE;
break;
}
}
domain = domain->next;
}
free(inputURIRec.server);
free(inputURIRec.absPath);
return ( result );
AddURIToURIRec:
return ( FALSE );
}
static OSStatus KeychainItemCopyAccountPassword(SecKeychainItemRef itemRef, char *user, char *pass)
{
OSStatus result;
SecKeychainAttribute attr;
SecKeychainAttributeList attrList;
UInt32 length;
void *outData;
attr.tag = kSecAccountItemAttr;
attr.length = 0;
attr.data = NULL;
attrList.count = 1;
attrList.attr = &attr;
result = SecKeychainItemCopyContent(itemRef, NULL, &attrList, &length, &outData);
if ( result == noErr )
{
if ( (attr.length < WEBDAV_MAX_USERNAME_LEN) && (length < WEBDAV_MAX_PASSWORD_LEN) )
{
memcpy(user, attr.data, attr.length);
memcpy(pass, outData, length);
}
else
{
result = errSecDataTooLarge;
}
(void) SecKeychainItemFreeContent(&attrList, outData);
}
return ( result );
}
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;
gAuthcacheHeader.generation = 1;
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:
if ( error != 0 )
{
syslog(LOG_ERR, "webdav_authcache_init: %s", strerror(error));
}
return (error);
}
int webdav_authcache_evaluate(WebdavAuthcacheEvaluateRec *evaluateRec)
{
int error, error2;
char *authParam;
char *params;
ChallengeSecurityLevelType level;
error = pthread_mutex_lock(&(gAuthcacheHeader.lock));
if ( error )
{
syslog(LOG_ERR, "webdav_authcache_evaluate: pthread_mutex_lock(): %s", strerror(error));
webdav_kill(-1);
}
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 )
{
syslog(LOG_ERR, "webdav_authcache_evaluate: pthread_mutex_unlock(): %s", strerror(error2));
webdav_kill(-1);
error = error2;
}
pthread_mutex_lock:
if ( error != 0 )
{
syslog(LOG_ERR, "webdav_authcache_evaluate: %s", strerror(error));
}
return ( error );
}
int webdav_authcache_insert(WebdavAuthcacheInsertRec *insertRec, int getLock)
{
int error, error2;
char *authParam;
char *params;
ChallengeSecurityLevelType level;
if ( getLock )
{
error = pthread_mutex_lock(&(gAuthcacheHeader.lock));
if ( error )
{
syslog(LOG_ERR, "webdav_authcache_insert: pthread_mutex_lock(): %s", strerror(error));
webdav_kill(-1);
}
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 )
{
++gAuthcacheHeader.generation;
if ( gAuthcacheHeader.generation == 0 )
{
gAuthcacheHeader.generation = 1;
}
if ( insertRec->isProxy )
{
verify((++gAuthcacheHeader.proxyElementCount) > 0);
}
}
if ( getLock )
{
error2 = pthread_mutex_unlock(&(gAuthcacheHeader.lock));
if ( error2 != 0 )
{
syslog(LOG_ERR, "webdav_authcache_insert: pthread_mutex_unlock(): %s", strerror(error2));
webdav_kill(-1);
error = error2;
}
}
pthread_mutex_lock:
if ( error != 0 )
{
syslog(LOG_ERR, "webdav_authcache_insert failed: %s", strerror(error));
}
return ( error );
}
int webdav_authcache_retrieve(WebdavAuthcacheRetrieveRec *retrieveRec)
{
int error, error2;
WebdavAuthcacheElement *elem;
int foundServer, foundProxy;
error = pthread_mutex_lock(&(gAuthcacheHeader.lock));
if ( error )
{
syslog(LOG_ERR, "webdav_authcache_retrieve: pthread_mutex_lock(): %s", strerror(error));
webdav_kill(-1);
}
require_noerr(error, pthread_mutex_lock);
retrieveRec->generation = gAuthcacheHeader.generation;
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);
retrieveRec->authflags = elem->authflags;
}
}
}
CallMakeAuthHeaderProc:
error2 = pthread_mutex_unlock(&(gAuthcacheHeader.lock));
if ( error2 != 0 )
{
syslog(LOG_ERR, "webdav_authcache_retrieve: pthread_mutex_unlock(): %s", strerror(error2));
webdav_kill(-1);
error = error2;
}
pthread_mutex_lock:
if ( error != 0 )
{
syslog(LOG_ERR, "webdav_authcache_retrieve: %s", strerror(error));
}
return ( error );
}
int webdav_authcache_remove(WebdavAuthcacheRemoveRec *removeRec, int getLock)
{
int error, error2;
WebdavAuthcacheElement *elem;
error = 0;
if ( getLock )
{
error = pthread_mutex_lock(&(gAuthcacheHeader.lock));
if ( error )
{
syslog(LOG_ERR, "webdav_authcache_remove: pthread_mutex_lock(): %s", strerror(error));
webdav_kill(-1);
}
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:
if ( error == 0 )
{
++gAuthcacheHeader.generation;
if ( gAuthcacheHeader.generation == 0 )
{
gAuthcacheHeader.generation = 1;
}
}
if ( getLock )
{
error2 = pthread_mutex_unlock(&(gAuthcacheHeader.lock));
if ( error2 != 0 )
{
syslog(LOG_ERR, "webdav_authcache_remove: pthread_mutex_unlock(): %s", strerror(error2));
webdav_kill(-1);
error = error2;
}
}
pthread_mutex_lock:
return ( error );
}
int webdav_authcache_update(WebdavAuthcacheUpdateRec *updateRec)
{
int error, error2;
char user[WEBDAV_MAX_USERNAME_LEN];
char pass[WEBDAV_MAX_PASSWORD_LEN];
int addtokeychain;
AuthFlagsType newAuthflags;
error = pthread_mutex_lock(&(gAuthcacheHeader.lock));
if ( error )
{
syslog(LOG_ERR, "webdav_authcache_update: pthread_mutex_lock(): %s", strerror(error));
webdav_kill(-1);
}
require_noerr(error, pthread_mutex_lock);
require_quiet((updateRec->generation == gAuthcacheHeader.generation) ||
(updateRec->generation == 0), updateComplete);
require_action(updateRec->level != 0, auth_level_zero, error = EACCES);
newAuthflags = kAuthNone;
bzero(user, WEBDAV_MAX_USERNAME_LEN);
bzero(pass, WEBDAV_MAX_PASSWORD_LEN);
if (!updateRec->uiNotNeeded)
{
char *allocatedURL = NULL;
char *displayURL;
int promptUser;
WebdavAuthcacheElement *elem;
promptUser = TRUE;
if ( (updateRec->authflags & kAuthFromKeychain) == 0 )
{
OSStatus result;
char *serverName;
char *path;
SecKeychainItemRef itemRef;
serverName = (updateRec->isProxy ? proxy_server : dest_server);
path = (updateRec->isProxy ? NULL : dest_path);
if ( updateRec->isProxy )
{
result = SecKeychainFindInternetPassword(NULL,
strlen(serverName), serverName,
0, NULL,
0, NULL,
0, NULL,
proxy_port,
kSecProtocolTypeHTTPProxy,
0,
0, NULL,
&itemRef);
}
else
{
result = SecKeychainFindInternetPassword(NULL,
strlen(serverName), serverName,
strlen(updateRec->realmStr), updateRec->realmStr,
0, NULL,
(path == NULL) ? 0 : strlen(path), path,
dest_port,
kSecProtocolTypeHTTP,
(updateRec->level == kChallengeSecurityLevelDigest) ?
kSecAuthenticationTypeHTTPDigest :
kSecAuthenticationTypeDefault,
0, NULL,
&itemRef);
}
if ( result == noErr )
{
if ( KeychainItemCopyAccountPassword(itemRef, user, pass) == noErr )
{
promptUser = FALSE;
newAuthflags = kAuthFromKeychain;
}
CFRelease(itemRef);
}
}
if ( promptUser )
{
if ( !updateRec->isProxy )
{
if ( reconstruct_url(http_hostname, updateRec->uri, &allocatedURL) == 0 )
{
displayURL = allocatedURL;
}
else
{
allocatedURL = NULL;
displayURL = updateRec->uri;
}
}
else
{
displayURL = proxy_server;
}
elem = gAuthcacheHeader.head;
while ( elem != NULL )
{
if ( (updateRec->uid == elem->uid) &&
(updateRec->isProxy == elem->isProxy) )
{
if ( elem->realmStr != NULL )
{
if ( strcmp(updateRec->realmStr, elem->realmStr) == 0 )
{
break;
}
}
}
elem = elem->next;
}
if ( elem != NULL )
{
strcpy(user, elem->username);
}
else
{
char *loginname;
loginname = getlogin();
if (loginname != NULL )
{
strcpy(user, loginname);
}
}
if ( gSuppressAllUI )
{
error = EACCES;
}
else
{
error = webdav_get_authentication(user, sizeof(user), pass, sizeof(pass),
(const char *)displayURL, (const char *)updateRec->realmStr, updateRec->level,
&addtokeychain, ((elem != NULL) ? ((elem->authflags & kAuthProvisional) != 0) : FALSE) );
if ( error == 0 )
{
newAuthflags = kAuthFromUI | kAuthProvisional;
}
else
{
error = EACCES;
}
}
if ( addtokeychain )
{
newAuthflags |= kAuthAddToKeychain;
}
if ( allocatedURL != NULL )
{
free(allocatedURL);
}
}
}
require_noerr(error, webdav_get_authentication);
WebdavAuthcacheRemoveRec auth_rem =
{
updateRec->uid, updateRec->isProxy, updateRec->realmStr
};
WebdavAuthcacheInsertRec auth_insert =
{
updateRec->uid, updateRec->challenge, updateRec->level, updateRec->isProxy,
user, pass, newAuthflags
};
(void)webdav_authcache_remove(&auth_rem, FALSE);
if (updateRec->uid != 0)
{
auth_rem.uid = 0;
(void)webdav_authcache_remove(&auth_rem, FALSE);
}
if (updateRec->uid != 1)
{
auth_rem.uid = 1;
(void)webdav_authcache_remove(&auth_rem, FALSE);
}
error = webdav_authcache_insert(&auth_insert, FALSE);
if (!error)
{
if (updateRec->uid != 0)
{
auth_insert.uid = 0;
(void)webdav_authcache_insert(&auth_insert, FALSE);
}
if (updateRec->uid != 1)
{
auth_insert.uid = 1;
(void)webdav_authcache_insert(&auth_insert, FALSE);
}
}
bzero(user, WEBDAV_MAX_USERNAME_LEN);
bzero(pass, WEBDAV_MAX_PASSWORD_LEN);
webdav_get_authentication:
auth_level_zero:
updateComplete:
error2 = pthread_mutex_unlock(&(gAuthcacheHeader.lock));
if ( error2 != 0 )
{
syslog(LOG_ERR, "webdav_authcache_update: pthread_mutex_unlock(): %s", strerror(error2));
webdav_kill(-1);
error = error2;
}
pthread_mutex_lock:
return ( error );
}
int webdav_authcache_keychain(WebdavAuthcacheKeychainRec *keychainRec)
{
int error, error2;
WebdavAuthcacheElement *elem;
int foundServer, foundProxy;
error = pthread_mutex_lock(&(gAuthcacheHeader.lock));
if ( error )
{
syslog(LOG_ERR, "webdav_authcache_keychain: pthread_mutex_lock(): %s", strerror(error));
webdav_kill(-1);
}
require_noerr(error, pthread_mutex_lock);
require_quiet(keychainRec->generation == gAuthcacheHeader.generation, generationChanged);
foundServer = FALSE;
foundProxy = (gAuthcacheHeader.proxyElementCount == 0);
elem = NULL;
while ( (foundServer == FALSE) || (foundProxy == FALSE) )
{
GetNextNextWebdavAuthcacheElement(&elem);
if ( elem == NULL )
{
break;
}
if ( (keychainRec->uid == elem->uid) && (elem->realmStr != NULL) )
{
if ( (elem->domainHead == NULL) ||
HasMatchingURI(keychainRec->uri, elem) )
{
char *serverName;
char *path;
SecKeychainItemRef itemRef;
OSStatus result;
if ( elem->isProxy )
{
check(foundProxy == FALSE);
foundProxy = TRUE;
}
else
{
check(foundServer == FALSE);
foundServer = TRUE;
}
elem->authflags &= ~kAuthProvisional;
if (elem->authflags & kAuthAddToKeychain)
{
serverName = (elem->isProxy ? proxy_server : dest_server);
path = (elem->isProxy ? NULL : dest_path);
if ( elem->isProxy )
{
result = SecKeychainFindInternetPassword(NULL,
strlen(serverName), serverName,
0, NULL,
0, NULL,
0, NULL,
proxy_port,
kSecProtocolTypeHTTPProxy,
0,
NULL, NULL,
&itemRef);
}
else
{
result = SecKeychainFindInternetPassword(NULL,
strlen(serverName), serverName,
strlen(elem->realmStr), elem->realmStr,
strlen(elem->username), elem->username,
(path == NULL) ? 0 : strlen(path), path,
dest_port,
kSecProtocolTypeHTTP,
(elem->scheme == kChallengeSecurityLevelDigest) ?
kSecAuthenticationTypeHTTPDigest :
kSecAuthenticationTypeDefault,
NULL, NULL,
&itemRef);
}
if ( result == noErr )
{
result = SecKeychainItemModifyContent(itemRef, NULL, strlen(elem->password), (void *)elem->password);
CFRelease(itemRef);
}
else
{
result = SecKeychainAddInternetPassword(NULL,
strlen(serverName), serverName,
strlen(elem->realmStr), elem->realmStr,
strlen(elem->username), elem->username,
(path == NULL) ? 0 : strlen(path), path,
elem->isProxy ? proxy_port : dest_port,
elem->isProxy ? kSecProtocolTypeHTTPProxy : kSecProtocolTypeHTTP,
(elem->scheme == kChallengeSecurityLevelDigest) ?
kSecAuthenticationTypeHTTPDigest :
kSecAuthenticationTypeDefault,
strlen(elem->password), elem->password,
&itemRef);
CFRelease(itemRef);
}
if ( result == noErr )
{
elem->authflags = kAuthFromKeychain;
}
}
}
}
}
generationChanged:
error2 = pthread_mutex_unlock(&(gAuthcacheHeader.lock));
if ( error2 != 0 )
{
syslog(LOG_ERR, "webdav_authcache_keychain: pthread_mutex_unlock(): %s", strerror(error2));
webdav_kill(-1);
error = error2;
}
pthread_mutex_lock:
return ( error );
}