#include "odckit.h"
#include "odkit.h"
typedef struct ODCKSessionFlushable {
ODKSession *odkSession;
char serverChallengeCStr[1024];
char userNameCStr[64];
} ODCKSessionFlushable;
typedef struct ODCKSessionPriv {
ODCKSessionFlushable *flushable;
char serverResponseCStr[256];
char badParameter;
char *error;
} ODCKSessionPriv;
#define ODCK_ODKSESSION(session) \
((session)->flushable->odkSession)
#define ODCK_SERVER_CHALLENGE_CSTR(session) \
((session)->flushable->serverChallengeCStr)
#define ODCK_USERNAME_CSTR(session) \
((session)->flushable->userNameCStr)
#define ODCK_SERVER_RESPONSE_CSTR(session) \
((session)->serverResponseCStr)
#define ODCK_SESSION_PRIV(session) \
ODCKSessionOpaque(session)
#define ODCK_PARAM_ASSERT(expression) \
do { _session->badParameter = !(expression); \
if (_session->badParameter) return -1; } while(0)
#define CF_SAFE_RELEASE(cfobj) \
do { if ((cfobj) != NULL) CFRelease((cfobj)); cfobj = NULL; } while (0)
static ODCKSessionPriv* ODCKSessionOpaque(ODCKSession *session);
static int ODCKCreateSessionFlushable(ODCKSessionPriv *session, ODCKSessionFlushable **out);
static int ODCKDeleteSessionFlushable(ODCKSessionFlushable **out);
static int ODCKMaybeCreateString(ODCKSession *session, CFStringRef *dst, const char *src);
static int ODCKMaybeCreateData(ODCKSession *session, CFDataRef *dst, const char *src, const unsigned int srclen);
static int ODCKGetData(char *dst, unsigned int dstlen, unsigned int *len, CFDataRef src);
ODCKSessionPriv*
ODCKSessionOpaque(ODCKSession *session)
{
ODCKSessionPriv *priv = (ODCKSessionPriv*)session;
assert(priv != 0);
return priv;
}
int
ODCKMaybeCreateString(ODCKSession *session, CFStringRef *dst, const char *src)
{
ODCKSessionPriv *_session = ODCK_SESSION_PRIV(session);
return ODKMaybeCreateString(ODCK_ODKSESSION(_session), dst, src);
}
int
ODCKMaybeCreateData(ODCKSession *session, CFDataRef *dst, const char *src, const unsigned int srclen)
{
ODCKSessionPriv *_session = ODCK_SESSION_PRIV(session);
return ODKMaybeCreateData(ODCK_ODKSESSION(_session), dst, src, srclen);
}
int
ODCKGetData(char *dst, unsigned int dstlen, unsigned int *len, CFDataRef src)
{
int retval = -1;
unsigned int srclen = (unsigned int)CFDataGetLength(src);
if (srclen >= dstlen)
goto done;
memcpy(dst, CFDataGetBytePtr(src), (size_t)srclen);
dst[srclen] = '\0';
*len = srclen;
retval = 0;
done:
return retval;
}
int
ODCKCreateSession(ODCKSession **out)
{
int retval = -1;
ODCKSessionPriv *session = 0;
*out = 0;
session = (void*)calloc(1, sizeof(*session));
if (session == 0) {
goto done;
}
*out = (ODCKSession*)session;
retval = 0;
done:
return retval;
}
int
ODCKDeleteSession(ODCKSession **out)
{
int retval = -1;
if (out == 0)
goto done;
ODCKSessionPriv *session = *(ODCKSessionPriv**)out;
if (session != 0) {
(void)ODCKDeleteSessionFlushable(&session->flushable);
if (session->error != 0)
free(session->error);
memset(session, 0, sizeof(*session));
free(session);
}
*out = 0;
retval = 0;
done:
return retval;
}
int
ODCKCreateSessionFlushable(ODCKSessionPriv *session, ODCKSessionFlushable **out)
{
int retval = -1;
ODCKSessionFlushable *flushable = 0;
*out = 0;
flushable = (void*)calloc(1, sizeof(*flushable));
if (flushable == 0) {
session->error = strdup("Out of memory");
goto done;
}
if (ODKCreateSession(&flushable->odkSession) != 0) {
session->error = strdup("Unable to create session");
goto done;
}
*out = flushable;
retval = 0;
done:
if (retval != 0)
(void)ODCKDeleteSessionFlushable(&flushable);
return retval;
}
int
ODCKDeleteSessionFlushable(ODCKSessionFlushable **out)
{
int retval = -1;
ODCKSessionFlushable *flushable = 0;
if (out == 0)
goto done;
flushable = *out;
if (flushable != 0) {
if (flushable->odkSession != 0)
(void)ODKDeleteSession(&flushable->odkSession);
memset(flushable, 0, sizeof(*flushable));
free(flushable);
}
*out = 0;
retval = 0;
done:
return retval;
}
int
ODCKFlushSession(ODCKSession *session)
{
int retval = -1;
ODCKSessionPriv *_session = ODCK_SESSION_PRIV(session);
char *error = 0;
ODCK_PARAM_ASSERT(session != 0);
error = ODKGetError(ODCK_ODKSESSION(_session));
if (error)
_session->error = strdup(error);
(void)ODCKDeleteSessionFlushable(&_session->flushable);
retval = 0;
done:
return retval;
}
int
ODCKGetServerChallenge(ODCKSession *session, const char *authMethod, const char *userName, char **serverChallengeOut, unsigned int *serverChallengeLenOut)
{
ODCKSessionPriv *_session = ODCK_SESSION_PRIV(session);
int retval = -1;
CFStringRef cfAuthMethod = NULL;
CFStringRef cfUserName = NULL;
CFDataRef cfServerChallenge = NULL;
ODCK_PARAM_ASSERT(serverChallengeOut != 0);
ODCK_PARAM_ASSERT(serverChallengeLenOut != 0);
*serverChallengeOut = 0;
*serverChallengeLenOut = 0;
if (_session->flushable == 0) {
if (ODCKCreateSessionFlushable(_session, &_session->flushable) != 0)
goto done;
}
if (ODCKMaybeCreateString(session, &cfAuthMethod, authMethod) != 0
|| ODCKMaybeCreateString(session, &cfUserName, userName))
goto done;
if (ODKCopyServerChallenge(ODCK_ODKSESSION(_session), cfAuthMethod, cfUserName, &cfServerChallenge) != 0)
goto done;
if (ODCKGetData(ODCK_SERVER_CHALLENGE_CSTR(_session),
sizeof(ODCK_SERVER_CHALLENGE_CSTR(_session)),
serverChallengeLenOut,
cfServerChallenge) != 0)
goto done;
*serverChallengeOut = ODCK_SERVER_CHALLENGE_CSTR(_session);
retval = 0;
done:
CF_SAFE_RELEASE(cfServerChallenge);
CF_SAFE_RELEASE(cfUserName);
CF_SAFE_RELEASE(cfAuthMethod);
return retval;
}
int
ODCKSetServerChallenge(ODCKSession *session, const char *authMethod, const char *userName, const char *serverChallenge, unsigned int serverChallengeLen)
{
ODCKSessionPriv *_session = ODCK_SESSION_PRIV(session);
int retval = -1;
CFStringRef cfAuthMethod = NULL;
CFStringRef cfUserName = NULL;
CFDataRef cfServerChallenge = NULL;
unsigned int ignore;
if (_session->flushable == 0) {
if (ODCKCreateSessionFlushable(_session, &_session->flushable) != 0)
goto done;
}
if (ODCKMaybeCreateString(session, &cfAuthMethod, authMethod) != 0
|| ODCKMaybeCreateString(session, &cfUserName, userName) != 0
|| ODCKMaybeCreateData(session, &cfServerChallenge, serverChallenge, serverChallengeLen) != 0)
goto done;
if (ODCKGetData(ODCK_SERVER_CHALLENGE_CSTR(_session),
sizeof(ODCK_SERVER_CHALLENGE_CSTR(_session)),
&ignore,
cfServerChallenge) != 0)
goto done;
if (ODKSetServerChallenge(ODCK_ODKSESSION(_session), cfAuthMethod, cfUserName, cfServerChallenge) != 0)
goto done;
retval = 0;
done:
CF_SAFE_RELEASE(cfServerChallenge);
CF_SAFE_RELEASE(cfUserName);
CF_SAFE_RELEASE(cfAuthMethod);
return retval;
}
int
ODCKVerifyClientRequest(ODCKSession *session, const char *clientRequest, unsigned int clientRequestLen)
{
ODCKSessionPriv *_session = ODCK_SESSION_PRIV(session);
int retval = -1;
CFDataRef cfClientRequest = NULL;
if (_session->flushable == 0) {
_session->badParameter = 1;
goto done;
}
if (ODCKMaybeCreateData(session, &cfClientRequest,
clientRequest, clientRequestLen))
goto done;
if (ODKVerifyClientRequest(ODCK_ODKSESSION(_session), cfClientRequest) != 0)
goto done;
retval = 0;
done:
CF_SAFE_RELEASE(cfClientRequest);
return retval;
}
int
ODCKGetServerResponse(ODCKSession *session, char **userNameOut, char **serverResponseOut, unsigned int *serverResponseLenOut)
{
ODCKSessionPriv *_session = ODCK_SESSION_PRIV(session);
int retval = -1;
CFStringRef cfUserName = NULL;
CFDataRef cfServerResponse = NULL;
ODCK_PARAM_ASSERT(userNameOut != 0);
ODCK_PARAM_ASSERT(serverResponseOut != 0);
ODCK_PARAM_ASSERT(serverResponseLenOut != 0);
*userNameOut = 0;
*serverResponseOut = 0;
*serverResponseLenOut = 0;
if (_session->flushable == 0) {
_session->badParameter = 1;
goto done;
}
if (ODKCopyServerResponse(ODCK_ODKSESSION(_session), &cfUserName, &cfServerResponse))
goto done;
if (ODCKGetData(ODCK_SERVER_RESPONSE_CSTR(_session),
sizeof(ODCK_SERVER_RESPONSE_CSTR(_session)),
serverResponseLenOut,
cfServerResponse) != 0)
goto done;
*serverResponseOut = ODCK_SERVER_RESPONSE_CSTR(_session);
CFStringGetCString(cfUserName,
ODCK_USERNAME_CSTR(_session),
sizeof(ODCK_USERNAME_CSTR(_session)),
kCFStringEncodingUTF8);
*userNameOut = ODCK_USERNAME_CSTR(_session);
retval = 0;
done:
CF_SAFE_RELEASE(cfServerResponse);
CF_SAFE_RELEASE(cfUserName);
return retval;
}
char *
ODCKGetError(ODCKSession *session)
{
ODCKSessionPriv *_session = ODCK_SESSION_PRIV(session);
if (_session == 0)
return "Null parameter";
else if (_session->badParameter)
return "Bad parameter";
else if (_session->error)
return _session->error;
else if (_session->flushable)
return ODKGetError(ODCK_ODKSESSION(_session));
return "Bad parameter";
}