#include "CPSPlugIn.h"
using namespace std;
#include <DirectoryServiceCore/ServerModuleLib.h>
#include <DirectoryServiceCore/CRCCalc.h>
#include <DirectoryServiceCore/CPlugInRef.h>
#include <DirectoryServiceCore/DSCThread.h>
#include <DirectoryServiceCore/CContinue.h>
#include <DirectoryServiceCore/DSEventSemaphore.h>
#include <DirectoryServiceCore/DSMutexSemaphore.h>
#include <DirectoryServiceCore/CSharedData.h>
#include <DirectoryServiceCore/DSUtils.h>
#include <DirectoryServiceCore/PrivateTypes.h>
#define kPasswordServerPrefixStr "/PasswordServer/"
#define kSASLListPrefix "(SASL "
#define kPasswordServerErrPrefixStr "-ERR "
#define kPasswordServerSASLErrPrefixStr "SASL "
#define kEmptyPasswordAltStr "<1-empty-insecure-1>"
#define kChangePassPaddedBufferSize 512
#define kOneKBuffer 1024
#if 0
#define DEBUGLOG(A,args...) CShared::LogIt( 0x0F, (A), ##args )
#else
#define DEBUGLOG(A,args...)
#endif
#define Throw_NULL(A,B) if ((A)==NULL) throw((sInt32)B)
extern "C" {
int getnameinfo(const struct sockaddr *, size_t, char *, size_t, char *, size_t, int);
typedef int sasl_cbproc();
int getrealm(void *context ,
int id,
const char **availrealms,
const char **result);
int simple(void *context ,
int id,
const char **result,
unsigned *len);
int
getsecret(sasl_conn_t *conn,
void *context ,
int id,
sasl_secret_t **psecret);
}
CPlugInRef *gPSContextTable = NULL;
static DSEventSemaphore *gKickSearchRequests = NULL;
static DSMutexSemaphore *gSASLMutex = NULL;
CContinue *gContinue = NULL;
static const uInt32 kBuffPad = 16;
static void myMemcpy ( void *s1, void *s2, size_t n );
void myMemcpy ( void *s1, void *s2, size_t n )
{
::memcpy( s1, s2, n );
}
extern "C" {
CFUUIDRef ModuleFactoryUUID = CFUUIDGetConstantUUIDWithBytes ( NULL, \
0xF8, 0xAC, 0xD8, 0x6B, 0x3C, 0x66, 0x11, 0xD6, \
0x93, 0x9C, 0x00, 0x03, 0x93, 0x50, 0xEB, 0x4E );
}
void writeToServer( FILE *out, char *buf );
void writeToServer( FILE *out, char *buf )
{
DEBUGLOG( "sending: %s\n", buf);
if ( out == NULL )
return;
if ( buf != NULL )
{
fwrite(buf, strlen(buf), 1, out);
fflush(out);
}
}
PWServerError readFromServer( FILE *in, char *buf, unsigned long bufLen );
PWServerError readFromServer( FILE *in, char *buf, unsigned long bufLen )
{
char readChar;
char *tstr = buf;
PWServerError result = {0, kPolicyError};
int compareLen;
if ( in == nil || buf == nil || bufLen < 2 ) {
result.err = -1;
return result;
}
*buf = '\0';
do
{
fscanf( in, "%c", &readChar );
if ( (unsigned long)(tstr - buf) < bufLen - 1 )
*tstr++ = readChar;
}
while ( readChar && readChar != '\n' );
*tstr = '\0';
DEBUGLOG( "received: %s\n", buf);
tstr = buf;
compareLen = strlen(kPasswordServerErrPrefixStr);
if ( strncmp( tstr, kPasswordServerErrPrefixStr, compareLen ) == 0 )
{
tstr += compareLen;
compareLen = strlen(kPasswordServerSASLErrPrefixStr);
if ( strncmp(tstr, kPasswordServerSASLErrPrefixStr, compareLen) == 0 ) {
tstr += compareLen;
result.type = kSASLError;
}
sscanf( tstr, "%d", &result.err );
}
return result;
}
static void chop(char *s)
{
char *p;
if (s==NULL)
return;
p = s + strlen(s) - 1;
if (p[0] == '\n') {
*p-- = '\0';
}
if (p >= s && p[0] == '\r') {
*p-- = '\0';
}
}
void ConvertBinaryToHex( const unsigned char *inData, unsigned long inLen, char *outHexStr )
{
const unsigned char *sptr = inData;
char *tptr = outHexStr;
unsigned long index;
char high, low;
for ( index = 0; index < inLen; index++ )
{
high = (*sptr & 0xF0) >> 4;
low = (*sptr & 0x0F);
if ( high >= 0x0A )
*tptr++ = (high - 0x0A) + 'A';
else
*tptr++ = high + '0';
if ( low >= 0x0A )
*tptr++ = (low - 0x0A) + 'A';
else
*tptr++ = low + '0';
sptr++;
}
*tptr = '\0';
}
void ConvertHexToBinary( const char *inHexStr, unsigned char *outData, unsigned long *outLen )
{
unsigned char *tptr = outData;
unsigned char val;
while ( *inHexStr && *(inHexStr+1) )
{
if ( *inHexStr >= 'A' )
val = (*inHexStr - 'A' + 0x0A) << 4;
else
val = (*inHexStr - '0') << 4;
inHexStr++;
if ( *inHexStr >= 'A' )
val += (*inHexStr - 'A' + 0x0A);
else
val += (*inHexStr - '0');
inHexStr++;
*tptr++ = val;
}
*outLen = (tptr - outData);
}
int ConvertBinaryTo64( const char *inData, unsigned long inLen, char *outHexStr )
{
int result;
unsigned int outLen;
char *tempBuf;
unsigned long bufLen = (inLen+3) * 4 / 3 + 1;
tempBuf = (char *) malloc( bufLen + 1 );
if ( tempBuf == NULL )
return -1;
result = sasl_encode64( (char *)inData, inLen, tempBuf, bufLen, &outLen );
tempBuf[outLen] = '\0';
sprintf( outHexStr, "{%lu}%s", inLen, tempBuf );
free( tempBuf );
return result;
}
int Convert64ToBinary( const char *inHexStr, char *outData, unsigned long *outLen )
{
int result;
unsigned int sasl_outlen;
unsigned long attached_outlen = 0;
const char *readPtr = inHexStr;
if ( readPtr == NULL )
return -1;
if ( *readPtr == '{' )
{
sscanf( readPtr + 1, "%lu", &attached_outlen );
readPtr = strchr( readPtr, '}' );
if ( readPtr == NULL )
return -1;
readPtr++;
}
result = sasl_decode64( readPtr, strlen(readPtr), (char *)outData, 256, &sasl_outlen );
*outLen = (attached_outlen > 0) ? attached_outlen : (unsigned long)sasl_outlen;
return result;
}
int getrealm(void *context ,
int id,
const char **availrealms,
const char **result)
{
if (id != SASL_CB_GETREALM) return SASL_BADPARAM;
if (!result) return SASL_BADPARAM;
if ( availrealms ) {
*result = *availrealms;
}
return SASL_OK;
}
int simple(void *context ,
int id,
const char **result,
unsigned *len)
{
DEBUGLOG( "in simple\n");
if (! result)
return SASL_BADPARAM;
*result = NULL;
switch (id) {
case SASL_CB_USER:
case SASL_CB_AUTHNAME:
*result = ((sPSContextData *)context)->last.username;
break;
default:
return SASL_BADPARAM;
}
if (*result != NULL && len != NULL)
*len = strlen(*result);
return SASL_OK;
}
int
getsecret(sasl_conn_t *conn,
void *context ,
int id,
sasl_secret_t **psecret)
{
size_t len = 0;
sasl_secret_t *xsec = NULL;
DEBUGLOG( "in getsecret\n");
if (! conn || ! psecret || id != SASL_CB_PASS)
return SASL_BADPARAM;
*psecret = NULL;
if (((sPSContextData *)context)->last.password != NULL)
{
len = ((sPSContextData *)context)->last.passwordLen;
xsec = (sasl_secret_t *) malloc(sizeof(sasl_secret_t) + len + 1);
if (xsec == NULL)
return SASL_NOMEM;
xsec->len = len;
memcpy( xsec->data, ((sPSContextData *)context)->last.password, len );
xsec->data[len] = '\0';
}
*psecret = xsec;
return SASL_OK;
}
sInt32 getconn(const char *host, const char *port, int *outSocket)
{
char servername[1024];
struct sockaddr_in sin;
struct hostent *hp;
int sock = 0;
sInt32 siResult = eDSNoErr;
int rc;
struct in_addr inetAddr;
char *endPtr = NULL;
if ( host==NULL || port==NULL || outSocket==NULL )
return eParameterError;
try
{
strncpy(servername, host, sizeof(servername) - 1);
servername[sizeof(servername) - 1] = '\0';
rc = inet_aton(servername, &inetAddr);
if ( rc == 1 )
{
sin.sin_addr.s_addr = inetAddr.s_addr;
}
else
{
if ((hp = gethostbyname(servername)) == NULL) {
DEBUGLOG("gethostbyname");
throw((sInt32)eDSServiceUnavailable);
}
memcpy(&sin.sin_addr, hp->h_addr, hp->h_length);
}
sin.sin_port = htons(strtol(port, &endPtr, 10));
if ((sin.sin_port == 0) || (endPtr == port)) {
DEBUGLOG( "port '%s' unknown\n", port);
throw((sInt32)eParameterError);
}
sin.sin_family = AF_INET;
sock = socket(AF_INET, SOCK_STREAM, 0);
if (sock < 0) {
DEBUGLOG("socket");
throw((sInt32)eDSServiceUnavailable);
}
if (connect(sock, (struct sockaddr *) &sin, sizeof (sin)) < 0) {
DEBUGLOG("connect");
throw((sInt32)eDSServiceUnavailable);
}
}
catch( sInt32 error )
{
siResult = error;
}
*outSocket = sock;
return siResult;
}
static CDSServerModule* _Creator ( void )
{
return( new CPSPlugIn );
}
CDSServerModule::tCreator CDSServerModule::sCreator = _Creator;
CPSPlugIn::CPSPlugIn ( void )
{
fState = kUnknownState;
fHasInitializedSASL = false;
try
{
if ( gPSContextTable == NULL )
{
gPSContextTable = new CPlugInRef( CPSPlugIn::ContextDeallocProc );
Throw_NULL( gPSContextTable, eMemoryAllocError );
}
if ( gKickSearchRequests == NULL )
{
gKickSearchRequests = new DSEventSemaphore();
Throw_NULL( gKickSearchRequests, eMemoryAllocError );
}
if ( gSASLMutex == NULL )
{
gSASLMutex = new DSMutexSemaphore();
Throw_NULL( gSASLMutex, eMemoryAllocError );
}
if ( gContinue == NULL )
{
gContinue = new CContinue( CPSPlugIn::ContinueDeallocProc );
Throw_NULL( gContinue, eMemoryAllocError );
}
}
catch (sInt32 err)
{
DEBUGLOG( "CPSPlugIn::CPSPlugIn failed: eMemoryAllocError\n");
throw( err );
}
}
CPSPlugIn::~CPSPlugIn ( void )
{
}
sInt32 CPSPlugIn::Validate ( const char *inVersionStr, const uInt32 inSignature )
{
fSignature = inSignature;
return( noErr );
}
sInt32 CPSPlugIn::Initialize ( void )
{
sInt32 siResult = eDSNoErr;
fState = kUnknownState;
fState += kInitalized;
fState += kActive;
WakeUpRequests();
return( siResult );
}
void CPSPlugIn::FillWithRandomData( char *inBuffer, uInt32 inLen )
{
}
sInt32 CPSPlugIn::SetPluginState ( const uInt32 inState )
{
if (kActive & inState) {
if (fState & kActive) {
}
else
{
Initialize(); }
}
if (kInactive & inState) {
if (!(fState & kInactive))
{
fState += kInactive;
}
if (fState & kActive)
{
fState -= kActive;
}
}
return( eDSNoErr );
}
void CPSPlugIn::WakeUpRequests ( void )
{
gKickSearchRequests->Signal();
}
void CPSPlugIn::WaitForInit ( void )
{
volatile uInt32 uiAttempts = 0;
if (!(fState & kActive))
{
while ( !(fState & kInitalized) &&
!(fState & kFailedToInit) )
{
try
{
if ( uiAttempts++ >= 240 )
{
return;
}
gKickSearchRequests->Wait( (uInt32)(.5 * kMilliSecsPerSec) );
try
{
gKickSearchRequests->Reset();
}
catch( long err )
{
}
}
catch( long err1 )
{
}
}
}}
sInt32 CPSPlugIn::ProcessRequest ( void *inData )
{
sInt32 siResult = 0;
if ( inData == NULL )
{
return( ePlugInDataError );
}
WaitForInit();
if ( (fState & kFailedToInit) )
{
return( ePlugInFailedToInitialize );
}
if ( ((fState & kInactive) || !(fState & kActive))
&& (((sHeader *)inData)->fType != kDoPlugInCustomCall)
&& (((sHeader *)inData)->fType != kOpenDirNode) )
{
return( ePlugInNotActive );
}
if ( ((sHeader *)inData)->fType == kHandleNetworkTransition )
{
siResult = Initialize(); }
else
{
siResult = HandleRequest( inData );
}
return( siResult );
}
sInt32 CPSPlugIn::HandleRequest ( void *inData )
{
sInt32 siResult = 0;
sHeader *pMsgHdr = NULL;
if ( inData == NULL )
{
return( -8088 );
}
pMsgHdr = (sHeader *)inData;
switch ( pMsgHdr->fType )
{
case kOpenDirNode:
siResult = OpenDirNode( (sOpenDirNode *)inData );
break;
case kCloseDirNode:
siResult = CloseDirNode( (sCloseDirNode *)inData );
break;
case kGetDirNodeInfo:
siResult = GetDirNodeInfo( (sGetDirNodeInfo *)inData );
break;
case kGetAttributeEntry:
siResult = GetAttributeEntry( (sGetAttributeEntry *)inData );
break;
case kGetAttributeValue:
siResult = GetAttributeValue( (sGetAttributeValue *)inData );
break;
case kCloseAttributeList:
siResult = CloseAttributeList( (sCloseAttributeList *)inData );
break;
case kCloseAttributeValueList:
siResult = CloseAttributeValueList( (sCloseAttributeValueList *)inData );
break;
case kDoDirNodeAuth:
siResult = DoAuthentication( (sDoDirNodeAuth *)inData );
break;
case kDoPlugInCustomCall:
siResult = DoPlugInCustomCall( (sDoPlugInCustomCall *)inData );
break;
default:
siResult = eNotHandledByThisNode;
break;
}
pMsgHdr->fResult = siResult;
return( siResult );
}
sInt32 CPSPlugIn::ReleaseContinueData ( sReleaseContinueData *inData )
{
sInt32 siResult = eDSNoErr;
if ( gContinue->RemoveItem( inData->fInContinueData ) != eDSNoErr )
{
siResult = eDSInvalidContext;
}
return( siResult );
}
sInt32 CPSPlugIn::OpenDirNode ( sOpenDirNode *inData )
{
sInt32 siResult = eDSNoErr;
tDataListPtr pNodeList = NULL;
char *pathStr = NULL;
char *psName = NULL;
char *subStr = NULL;
sPSContextData *pContext = NULL;
pNodeList = inData->fInDirNodeName;
DEBUGLOG( "CPSPlugIn::OpenDirNode \n");
try
{
if ( inData != NULL )
{
pathStr = dsGetPathFromListPriv( pNodeList, (char *)"/" );
Throw_NULL( pathStr, eDSNullNodeName );
DEBUGLOG( "CPSPlugIn::OpenDirNode path = %s\n", pathStr);
unsigned int prefixLen = strlen(kPasswordServerPrefixStr);
if (::strcmp(pathStr,"/PasswordServer") == 0)
{
siResult = eDSOpenNodeFailed;
}
else
if ( (strlen(pathStr) > prefixLen) && (::strncmp(pathStr,kPasswordServerPrefixStr,prefixLen) == 0) )
{
int result;
unsigned count;
char *tptr, *end;
int salen;
subStr = pathStr + prefixLen;
if ( strncmp( subStr, "ipv4/", 5 ) == 0 )
subStr += 5;
else
if ( strncmp( subStr, "ipv6/", 5 ) == 0 )
subStr += 5;
else
if ( strncmp( subStr, "dns/", 4 ) == 0 )
subStr += 4;
psName = (char *) calloc( 1, strlen(subStr) );
Throw_NULL( psName, eDSNullNodeName );
::strcpy( psName, subStr );
pContext = MakeContextData();
pContext->psName = psName;
char *portNumStr = strchr( pContext->psName, ':' );
if ( portNumStr != NULL )
{
*portNumStr = '\0';
strncpy(pContext->psPort, portNumStr+1, 10);
pContext->psPort[9] = '\0';
}
else
{
strcpy(pContext->psPort, "106");
}
char hbuf[NI_MAXHOST], pbuf[NI_MAXSERV];
struct sockaddr_storage local_ip, remote_ip;
char buf[4096];
if ( !fHasInitializedSASL )
{
gSASLMutex->Wait();
result = sasl_client_init(NULL);
gSASLMutex->Signal();
if ( result != SASL_OK ) {
DEBUGLOG( "sasl_client_init failed.\n");
return eDSOpenNodeFailed;
}
fHasInitializedSASL = true;
}
siResult = ConnectToServer( pContext );
if ( siResult != eDSNoErr )
throw( siResult );
salen = sizeof(local_ip);
if (getsockname(pContext->fd, (struct sockaddr *)&local_ip, &salen) < 0) {
DEBUGLOG("getsockname");
}
getnameinfo((struct sockaddr *)&local_ip, salen,
hbuf, sizeof(hbuf), pbuf, sizeof(pbuf),
NI_NUMERICHOST | NI_WITHSCOPEID | NI_NUMERICSERV);
snprintf(pContext->localaddr, sizeof(pContext->localaddr), "%s;%s", hbuf, pbuf);
salen = sizeof(remote_ip);
if (getpeername(pContext->fd, (struct sockaddr *)&remote_ip, &salen) < 0) {
DEBUGLOG("getpeername");
}
getnameinfo((struct sockaddr *)&remote_ip, salen,
hbuf, sizeof(hbuf), pbuf, sizeof(pbuf),
NI_NUMERICHOST | NI_WITHSCOPEID | NI_NUMERICSERV);
snprintf(pContext->remoteaddr, sizeof(pContext->remoteaddr), "%s;%s", hbuf, pbuf);
siResult = this->GetRSAPublicKey(pContext);
if ( siResult != eDSNoErr ) {
DEBUGLOG( "rsapublic = %l\n", siResult);
throw(siResult);
}
writeToServer(pContext->serverOut, "LIST\r\n");
readFromServer(pContext->serverIn, buf, 4096);
chop(buf);
tptr = buf;
for (count=0; tptr; count++ ) {
tptr = strchr( tptr, ' ' );
if (tptr) tptr++;
}
if (count > 0) {
pContext->mech = (AuthMethName *)calloc(count, sizeof(AuthMethName));
Throw_NULL( pContext->mech, eMemoryAllocError );
pContext->mechCount = count;
}
tptr = strstr( buf, kSASLListPrefix );
if ( tptr )
{
tptr += strlen( kSASLListPrefix );
for ( ; tptr && count > 0; count-- )
{
if ( *tptr == '\"' )
tptr++;
else
break;
end = strchr( tptr, '\"' );
if ( end != NULL )
*end = '\0';
strcpy( pContext->mech[count-1].method, tptr );
DEBUGLOG( "mech=%s\n", tptr);
tptr = end;
if ( tptr != NULL )
tptr += 2;
}
}
gPSContextTable->AddItem( inData->fOutNodeRef, pContext );
} else
{
siResult = eDSOpenNodeFailed;
}
} } catch( sInt32 err )
{
siResult = err;
if (pContext != NULL)
{
gPSContextTable->RemoveItem( inData->fOutNodeRef );
}
}
if (pathStr != NULL)
{
delete( pathStr );
pathStr = NULL;
}
return( siResult );
}
sInt32 CPSPlugIn::CloseDirNode ( sCloseDirNode *inData )
{
sInt32 siResult = eDSNoErr;
sPSContextData *pContext = NULL;
char buf[kOneKBuffer];
try
{
pContext = (sPSContextData *)gPSContextTable->GetItemData( inData->fInNodeRef );
Throw_NULL( pContext, eDSBadContextData );
writeToServer(pContext->serverOut, "QUIT\r\n");
readFromServer( pContext->serverIn, buf, kOneKBuffer );
this->CleanContextData( pContext );
gPSContextTable->RemoveItem( inData->fInNodeRef );
gContinue->RemoveItems( inData->fInNodeRef );
}
catch( sInt32 err )
{
siResult = err;
}
return( siResult );
}
sInt32 CPSPlugIn::ConnectToServer( sPSContextData *inContext )
{
sInt32 siResult = eDSNoErr;
char buf[kOneKBuffer];
siResult = getconn(inContext->psName, inContext->psPort, &inContext->fd);
if ( siResult != eDSNoErr )
return( siResult );
inContext->serverIn = fdopen(inContext->fd, "r");
inContext->serverOut = fdopen(inContext->fd, "w");
readFromServer(inContext->serverIn, buf, kOneKBuffer);
return siResult;
}
Boolean CPSPlugIn::Connected ( sPSContextData *inContext )
{
int bytesReadable = 0;
char temp[1];
if ( inContext->fd == 0 )
return false;
bytesReadable = ::recvfrom( inContext->fd, temp, sizeof (temp), (MSG_DONTWAIT | MSG_PEEK), NULL, NULL );
DEBUGLOG( "CPSPlugIn::Connected bytesReadable = %d, errno = %d", bytesReadable, errno );
if ( bytesReadable == -1 )
{
switch ( errno )
{
case EAGAIN:
return true;
break;
case EBADF:
case ENOTCONN:
case ENOTSOCK:
case EINTR:
case EFAULT:
return false;
break;
default:
return true;
break;
}
}
if ( bytesReadable == 0 )
{
return( false );
}
return( true );
}
sInt32 CPSPlugIn::GetRSAPublicKey( sPSContextData *inContext )
{
sInt32 siResult = eDSNoErr;
PWServerError serverResult;
char buf[kOneKBuffer];
char *keyStr;
int bits = 0;
try
{
Throw_NULL( inContext, eDSBadContextData );
writeToServer( inContext->serverOut, "RSAPUBLIC\r\n" );
serverResult = readFromServer( inContext->serverIn, buf, kOneKBuffer );
if ( serverResult.err != 0 )
{
DEBUGLOG("no public key\n");
throw(eDSAuthServerError);
}
chop( buf );
inContext->rsaPublicKeyStr = (char *) calloc( 1, strlen(buf)+1 );
Throw_NULL( inContext->rsaPublicKeyStr, eMemoryAllocError );
strcpy( inContext->rsaPublicKeyStr, buf + 4 );
inContext->rsaPublicKey = key_new( KEY_RSA );
Throw_NULL( inContext->rsaPublicKey, eDSAllocationFailed );
keyStr = buf + 4;
bits = key_read(inContext->rsaPublicKey, &keyStr);
if (bits == 0) {
DEBUGLOG( "no key bits\n");
throw( (sInt32)eDSAuthServerError );
}
}
catch( sInt32 err )
{
DEBUGLOG( "catch in GetRSAPublicKey = %l\n", err);
siResult = err;
}
return siResult;
}
sInt32 CPSPlugIn::DoRSAValidation ( sPSContextData *inContext, const char *inUserKey )
{
sInt32 siResult = eDSNoErr;
char *encodedStr = NULL;
PWServerError serverResult;
char buf[kOneKBuffer];
BIGNUM *nonce;
BN_CTX *ctx;
char *bnStr = NULL;
int len;
try
{
Throw_NULL( inContext, eDSBadContextData );
if ( strcmp(inContext->rsaPublicKeyStr, inUserKey) != 0 )
throw( (sInt32)eDSAuthServerError );
nonce = BN_new();
Throw_NULL( nonce, eDSAllocationFailed );
BN_rand(nonce, 256, 0, 0);
ctx = BN_CTX_new();
BN_mod(nonce, nonce, inContext->rsaPublicKey->rsa->n, ctx);
BN_CTX_free(ctx);
bnStr = BN_bn2dec(nonce);
BN_clear_free(nonce);
DEBUGLOG( "nonce = %s\n", bnStr);
if ( bnStr == NULL )
throw( (sInt32)eMemoryError );
int nonceLen = strlen(bnStr);
encodedStr = (char *)malloc(kOneKBuffer);
Throw_NULL( encodedStr, eMemoryError );
len = RSA_public_encrypt(nonceLen,
(unsigned char *)bnStr,
(unsigned char *)encodedStr,
inContext->rsaPublicKey->rsa,
RSA_PKCS1_PADDING);
if ( len <= 0 ) {
DEBUGLOG( "rsa_public_encrypt() failed");
throw( (sInt32)eDSAuthServerError );
}
if ( ConvertBinaryTo64( encodedStr, (unsigned)len, buf ) == SASL_OK )
{
char writeBuf[kOneKBuffer];
UInt32 encodedStrLen;
snprintf( writeBuf, kOneKBuffer, "RSAVALIDATE %s\r\n", buf );
writeBuf[kOneKBuffer-1] = '\0';
writeToServer( inContext->serverOut, writeBuf );
serverResult = readFromServer( inContext->serverIn, buf, kOneKBuffer );
if ( Convert64ToBinary( buf + 4, encodedStr, &encodedStrLen ) == SASL_OK )
{
encodedStr[nonceLen] = '\0';
DEBUGLOG( "nonce = %s\n", encodedStr);
if (memcmp(bnStr, encodedStr, nonceLen) != 0)
siResult = eDSAuthServerError;
}
}
}
catch( sInt32 err )
{
siResult = err;
}
if ( bnStr != NULL )
free( bnStr );
if ( encodedStr != NULL )
free( encodedStr );
return siResult;
}
sPSContextData* CPSPlugIn::MakeContextData ( void )
{
sPSContextData *pOut = NULL;
sInt32 siResult = eDSNoErr;
pOut = (sPSContextData *) calloc(1, sizeof(sPSContextData));
if ( pOut != NULL )
{
siResult = CleanContextData(pOut);
}
return( pOut );
}
sInt32 CPSPlugIn::CleanContextData ( sPSContextData *inContext )
{
sInt32 siResult = eDSNoErr;
if ( inContext == NULL )
{
siResult = eDSBadContextData;
}
else
{
if (inContext->psName != NULL)
{
free( inContext->psName );
inContext->psName = NULL;
}
inContext->offset = 0;
if (inContext->conn != NULL)
{
gSASLMutex->Wait();
sasl_dispose(&inContext->conn);
gSASLMutex->Signal();
inContext->conn = NULL;
}
if (inContext->serverIn != NULL)
{
fclose(inContext->serverIn);
inContext->serverIn = NULL;
}
if (inContext->serverOut != NULL)
{
fclose(inContext->serverOut);
inContext->serverOut = NULL;
}
if (inContext->fd != NULL)
{
close(inContext->fd);
inContext->fd = NULL;
}
if (inContext->rsaPublicKeyStr != NULL)
{
free(inContext->rsaPublicKeyStr);
inContext->rsaPublicKeyStr = NULL;
}
if (inContext->rsaPublicKey != NULL)
{
key_free(inContext->rsaPublicKey);
inContext->rsaPublicKey = NULL;
}
if (inContext->mech != NULL)
{
free(inContext->mech);
inContext->mech = NULL;
}
inContext->mechCount = 0;
memset(inContext->last.username, 0, sizeof(inContext->last.username));
if (inContext->last.password != NULL)
{
memset(inContext->last.password, 0, inContext->last.passwordLen);
free(inContext->last.password);
inContext->last.password = NULL;
}
inContext->last.passwordLen = 0;
inContext->last.successfulAuth = false;
memset(inContext->nao.username, 0, sizeof(inContext->nao.username));
if (inContext->nao.password != NULL)
{
memset(inContext->nao.password, 0, inContext->nao.passwordLen);
free(inContext->nao.password);
inContext->nao.password = NULL;
}
inContext->nao.passwordLen = 0;
inContext->nao.successfulAuth = false;
}
return( siResult );
}
sInt32 CPSPlugIn::GetAttributeEntry ( sGetAttributeEntry *inData )
{
sInt32 siResult = eDSNoErr;
uInt16 usAttrTypeLen = 0;
uInt16 usAttrCnt = 0;
uInt16 usAttrLen = 0;
uInt16 usValueCnt = 0;
uInt16 usValueLen = 0;
uInt32 i = 0;
uInt32 uiIndex = 0;
uInt32 uiAttrEntrySize = 0;
uInt32 uiOffset = 0;
uInt32 uiTotalValueSize = 0;
uInt32 offset = 4;
uInt32 buffSize = 0;
uInt32 buffLen = 0;
char *p = NULL;
char *pAttrType = NULL;
tDataBufferPtr pDataBuff = NULL;
tAttributeEntryPtr pAttribInfo = NULL;
sPSContextData *pAttrContext = NULL;
sPSContextData *pValueContext = NULL;
try
{
Throw_NULL( inData, eMemoryError );
pAttrContext = (sPSContextData *)gPSContextTable->GetItemData( inData->fInAttrListRef );
Throw_NULL( pAttrContext, eDSBadContextData );
uiIndex = inData->fInAttrInfoIndex;
if (uiIndex == 0)
throw( eDSInvalidIndex );
pDataBuff = inData->fInOutDataBuff;
Throw_NULL( pDataBuff, eDSNullDataBuff );
buffSize = pDataBuff->fBufferSize;
p = pDataBuff->fBufferData + pAttrContext->offset;
offset = pAttrContext->offset;
if ( 2 > (sInt32)(buffSize - offset) )
throw( eDSInvalidBuffFormat );
::myMemcpy( &usAttrCnt, p, 2 );
if (uiIndex > usAttrCnt)
throw( eDSInvalidIndex );
p += 2;
offset += 2;
for ( i = 1; i < uiIndex; i++ )
{
if (2 > (sInt32)(buffSize - offset) )
throw( eDSInvalidBuffFormat );
::myMemcpy( &usAttrLen, p, 2 );
p += 2 + usAttrLen;
offset += 2 + usAttrLen;
}
uiOffset = offset;
if (2 > (sInt32)(buffSize - offset))
throw( eDSInvalidBuffFormat );
::myMemcpy( &usAttrLen, p, 2 );
p += 2;
offset += 2;
buffLen = offset + usAttrLen;
if (2 > (sInt32)(buffLen - offset))
throw( eDSInvalidBuffFormat );
::myMemcpy( &usAttrTypeLen, p, 2 );
pAttrType = p + 2;
p += 2 + usAttrTypeLen;
offset += 2 + usAttrTypeLen;
if (2 > (sInt32)(buffLen - offset))
throw( eDSInvalidBuffFormat );
::myMemcpy( &usValueCnt, p, 2 );
p += 2;
offset += 2;
for ( i = 0; i < usValueCnt; i++ )
{
if (2 > (sInt32)(buffLen - offset))
throw( eDSInvalidBuffFormat );
::myMemcpy( &usValueLen, p, 2 );
p += 2 + usValueLen;
offset += 2 + usValueLen;
uiTotalValueSize += usValueLen;
}
uiAttrEntrySize = sizeof( tAttributeEntry ) + usAttrTypeLen + kBuffPad;
pAttribInfo = (tAttributeEntry *)::calloc( 1, uiAttrEntrySize );
pAttribInfo->fAttributeValueCount = usValueCnt;
pAttribInfo->fAttributeDataSize = uiTotalValueSize;
pAttribInfo->fAttributeValueMaxSize = 512; pAttribInfo->fAttributeSignature.fBufferSize = usAttrTypeLen + kBuffPad;
pAttribInfo->fAttributeSignature.fBufferLength = usAttrTypeLen;
::memcpy( pAttribInfo->fAttributeSignature.fBufferData, pAttrType, usAttrTypeLen );
pValueContext = MakeContextData();
Throw_NULL( pValueContext , eMemoryAllocError );
pValueContext->offset = uiOffset;
gPSContextTable->AddItem( inData->fOutAttrValueListRef, pValueContext );
inData->fOutAttrInfoPtr = pAttribInfo;
}
catch( sInt32 err )
{
siResult = err;
}
return( siResult );
}
sInt32 CPSPlugIn::GetAttributeValue ( sGetAttributeValue *inData )
{
sInt32 siResult = eDSNoErr;
uInt16 usValueCnt = 0;
uInt16 usValueLen = 0;
uInt16 usAttrNameLen = 0;
uInt32 i = 0;
uInt32 uiIndex = 0;
uInt32 offset = 0;
char *p = NULL;
tDataBuffer *pDataBuff = NULL;
tAttributeValueEntry *pAttrValue = NULL;
sPSContextData *pValueContext = NULL;
uInt32 buffSize = 0;
uInt32 buffLen = 0;
uInt16 attrLen = 0;
try
{
pValueContext = (sPSContextData *)gPSContextTable->GetItemData( inData->fInAttrValueListRef );
Throw_NULL( pValueContext , eDSBadContextData );
uiIndex = inData->fInAttrValueIndex;
if (uiIndex == 0)
throw( eDSInvalidIndex );
pDataBuff = inData->fInOutDataBuff;
Throw_NULL( pDataBuff , eDSNullDataBuff );
buffSize = pDataBuff->fBufferSize;
p = pDataBuff->fBufferData + pValueContext->offset;
offset = pValueContext->offset;
if (2 > (sInt32)(buffSize - offset))
throw( eDSInvalidBuffFormat );
::myMemcpy( &attrLen, p, 2 );
buffLen = attrLen + pValueContext->offset + 2;
if (buffLen > buffSize)
throw( eDSInvalidBuffFormat );
p += 2;
offset += 2;
if (2 > (sInt32)(buffLen - offset))
throw( eDSInvalidBuffFormat );
::myMemcpy( &usAttrNameLen, p, 2 );
p += 2 + usAttrNameLen;
offset += 2 + usAttrNameLen;
if (2 > (sInt32)(buffLen - offset))
throw( eDSInvalidBuffFormat );
::myMemcpy( &usValueCnt, p, 2 );
p += 2;
offset += 2;
if (uiIndex > usValueCnt)
throw( eDSInvalidIndex );
for ( i = 1; i < uiIndex; i++ )
{
if (2 > (sInt32)(buffLen - offset))
throw( eDSInvalidBuffFormat );
::myMemcpy( &usValueLen, p, 2 );
p += 2 + usValueLen;
offset += 2 + usValueLen;
}
if (2 > (sInt32)(buffLen - offset))
throw( eDSInvalidBuffFormat );
::myMemcpy( &usValueLen, p, 2 );
p += 2;
offset += 2;
pAttrValue = (tAttributeValueEntry *)::calloc( 1, sizeof( tAttributeValueEntry ) + usValueLen + kBuffPad );
Throw_NULL(pAttrValue, eMemoryAllocError);
pAttrValue->fAttributeValueData.fBufferSize = usValueLen + kBuffPad;
pAttrValue->fAttributeValueData.fBufferLength = usValueLen;
if ( usValueLen > (sInt32)(buffLen - offset) )
throw ( eDSInvalidBuffFormat );
::memcpy( pAttrValue->fAttributeValueData.fBufferData, p, usValueLen );
pAttrValue->fAttributeValueID = CalcCRC( pAttrValue->fAttributeValueData.fBufferData );
inData->fOutAttrValue = pAttrValue;
}
catch ( sInt32 err )
{
siResult = err;
}
return( siResult );
}
uInt32 CPSPlugIn::CalcCRC ( char *inStr )
{
char *p = inStr;
sInt32 siI = 0;
sInt32 siStrLen = 0;
uInt32 uiCRC = 0xFFFFFFFF;
CRCCalc aCRCCalc;
if ( inStr != NULL )
{
siStrLen = ::strlen( inStr );
for ( siI = 0; siI < siStrLen; ++siI )
{
uiCRC = aCRCCalc.UPDC32( *p, uiCRC );
p++;
}
}
return( uiCRC );
}
sInt32 CPSPlugIn::GetDirNodeInfo ( sGetDirNodeInfo *inData )
{
sInt32 siResult = eDSNoErr;
uInt32 uiOffset = 0;
uInt32 uiCntr = 1;
uInt32 uiAttrCnt = 0;
CAttributeList *inAttrList = NULL;
char *pAttrName = NULL;
char *pData = NULL;
sPSContextData *pContext = NULL;
sPSContextData *pAttrContext = NULL;
CBuff outBuff;
CDataBuff *aRecData = NULL;
CDataBuff *aAttrData = NULL;
CDataBuff *aTmpData = NULL;
try
{
Throw_NULL( inData , eMemoryError );
pContext = (sPSContextData *)gPSContextTable->GetItemData( inData->fInNodeRef );
Throw_NULL( pContext , eDSBadContextData );
inAttrList = new CAttributeList( inData->fInDirNodeInfoTypeList );
Throw_NULL( inAttrList, eDSNullNodeInfoTypeList );
if (inAttrList->GetCount() == 0)
throw( eDSEmptyNodeInfoTypeList );
siResult = outBuff.Initialize( inData->fOutDataBuff, true );
if ( siResult != eDSNoErr )
throw( siResult );
siResult = outBuff.SetBuffType( 'Gdni' ); if ( siResult != eDSNoErr )
throw( siResult );
aRecData = new CDataBuff();
Throw_NULL( aRecData , eMemoryError );
aAttrData = new CDataBuff();
Throw_NULL( aAttrData , eMemoryError );
aTmpData = new CDataBuff();
Throw_NULL( aTmpData , eMemoryError );
aRecData->AppendShort( ::strlen( "dsAttrTypeStandard:DirectoryNodeInfo" ) );
aRecData->AppendString( (char *)"dsAttrTypeStandard:DirectoryNodeInfo" );
aRecData->AppendShort( ::strlen( "DirectoryNodeInfo" ) );
aRecData->AppendString( (char *)"DirectoryNodeInfo" );
while ( inAttrList->GetAttribute( uiCntr++, &pAttrName ) == eDSNoErr )
{
if ((::strcmp( pAttrName, kDSAttributesAll ) == 0) ||
(::strcmp( pAttrName, kDSNAttrNodePath ) == 0) )
{
aTmpData->Clear();
uiAttrCnt++;
aTmpData->AppendShort( ::strlen( kDSNAttrNodePath ) );
aTmpData->AppendString( kDSNAttrNodePath );
if ( inData->fInAttrInfoOnly == false )
{
aTmpData->AppendShort( 2 );
aTmpData->AppendShort( ::strlen( "PasswordServer" ) );
aTmpData->AppendString( (char *)"PasswordServer" );
char *tmpStr = NULL;
if (pContext->psName != NULL)
{
tmpStr = new char[1+::strlen(pContext->psName)];
::strcpy( tmpStr, pContext->psName );
}
else
{
tmpStr = new char[1+::strlen("Unknown Node Location")];
::strcpy( tmpStr, "Unknown Node Location" );
}
aTmpData->AppendShort( ::strlen( tmpStr ) );
aTmpData->AppendString( tmpStr );
delete( tmpStr );
}
aAttrData->AppendShort( aTmpData->GetLength() );
aAttrData->AppendBlock( aTmpData->GetData(), aTmpData->GetLength() );
aTmpData->Clear();
}
if ( (::strcmp( pAttrName, kDSAttributesAll ) == 0) ||
(::strcmp( pAttrName, kDS1AttrReadOnlyNode ) == 0) )
{
aTmpData->Clear();
uiAttrCnt++;
aTmpData->AppendShort( ::strlen( kDS1AttrReadOnlyNode ) );
aTmpData->AppendString( kDS1AttrReadOnlyNode );
if ( inData->fInAttrInfoOnly == false )
{
aTmpData->AppendShort( 1 );
aTmpData->AppendShort( ::strlen( "ReadOnly" ) );
aTmpData->AppendString( "ReadOnly" );
}
aAttrData->AppendShort( aTmpData->GetLength() );
aAttrData->AppendBlock( aTmpData->GetData(), aTmpData->GetLength() );
aTmpData->Clear();
}
if ((::strcmp( pAttrName, kDSAttributesAll ) == 0) ||
(::strcmp( pAttrName, kDSNAttrAuthMethod ) == 0) )
{
aTmpData->Clear();
uiAttrCnt++;
aTmpData->AppendShort( ::strlen( kDSNAttrAuthMethod ) );
aTmpData->AppendString( kDSNAttrAuthMethod );
if ( inData->fInAttrInfoOnly == false )
{
int idx, mechCount = 0;
char dsTypeStr[256];
for ( idx = 0; idx < pContext->mechCount; idx++ )
{
GetAuthMethodFromSASLName( pContext->mech[idx].method, dsTypeStr );
if ( dsTypeStr[0] != '\0' )
mechCount++;
}
aTmpData->AppendShort( 7 + mechCount );
aTmpData->AppendShort( ::strlen( kDSStdAuthClearText ) );
aTmpData->AppendString( kDSStdAuthClearText );
aTmpData->AppendShort( ::strlen( kDSStdAuthSetPasswd ) );
aTmpData->AppendString( kDSStdAuthSetPasswd );
aTmpData->AppendShort( ::strlen( kDSStdAuthChangePasswd ) );
aTmpData->AppendString( kDSStdAuthChangePasswd );
aTmpData->AppendShort( ::strlen( kDSStdAuthSetPasswdAsRoot ) );
aTmpData->AppendString( kDSStdAuthSetPasswdAsRoot );
aTmpData->AppendShort( ::strlen( kDSStdAuthNodeNativeClearTextOK ) );
aTmpData->AppendString( kDSStdAuthNodeNativeClearTextOK );
aTmpData->AppendShort( ::strlen( kDSStdAuthNodeNativeNoClearText ) );
aTmpData->AppendString( kDSStdAuthNodeNativeNoClearText );
aTmpData->AppendShort( ::strlen( kDSStdAuth2WayRandomChangePasswd ) );
aTmpData->AppendString( kDSStdAuth2WayRandomChangePasswd );
for ( idx = 0; idx < pContext->mechCount; idx++ )
{
GetAuthMethodFromSASLName( pContext->mech[idx].method, dsTypeStr );
if ( dsTypeStr[0] != '\0' )
{
aTmpData->AppendShort( ::strlen( dsTypeStr ) );
aTmpData->AppendString( dsTypeStr );
}
}
}
aAttrData->AppendShort( aTmpData->GetLength() );
aAttrData->AppendBlock( aTmpData->GetData(), aTmpData->GetLength() );
aTmpData->Clear();
}
}
aRecData->AppendShort( uiAttrCnt );
if (uiAttrCnt > 0)
{
aRecData->AppendBlock( aAttrData->GetData(), aAttrData->GetLength() );
}
outBuff.AddData( aRecData->GetData(), aRecData->GetLength() );
inData->fOutAttrInfoCount = uiAttrCnt;
pData = outBuff.GetDataBlock( 1, &uiOffset );
if ( pData != NULL )
{
pAttrContext = MakeContextData();
Throw_NULL( pAttrContext , eMemoryAllocError );
pAttrContext->offset = uiOffset + 61;
gPSContextTable->AddItem( inData->fOutAttrListRef, pAttrContext );
}
}
catch( sInt32 err )
{
siResult = err;
}
if ( inAttrList != NULL )
{
delete( inAttrList );
inAttrList = NULL;
}
if ( aRecData != NULL )
{
delete( aRecData );
aRecData = NULL;
}
if ( aAttrData != NULL )
{
delete( aAttrData );
aAttrData = NULL;
}
if ( aTmpData != NULL )
{
delete( aTmpData );
aTmpData = NULL;
}
return( siResult );
}
sInt32 CPSPlugIn::CloseAttributeList ( sCloseAttributeList *inData )
{
sInt32 siResult = eDSNoErr;
sPSContextData *pContext = NULL;
pContext = (sPSContextData *)gPSContextTable->GetItemData( inData->fInAttributeListRef );
if ( pContext != NULL )
{
gPSContextTable->RemoveItem( inData->fInAttributeListRef );
}
else
{
siResult = eDSInvalidAttrListRef;
}
return( siResult );
}
sInt32 CPSPlugIn::CloseAttributeValueList ( sCloseAttributeValueList *inData )
{
sInt32 siResult = eDSNoErr;
sPSContextData *pContext = NULL;
pContext = (sPSContextData *)gPSContextTable->GetItemData( inData->fInAttributeValueListRef );
if ( pContext != NULL )
{
gPSContextTable->RemoveItem( inData->fInAttributeValueListRef );
}
else
{
siResult = eDSInvalidAttrValueRef;
}
return( siResult );
}
sInt32 CPSPlugIn::GetStringFromAuthBuffer(tDataBufferPtr inAuthData, int stringNum, char **outString)
{
tDataListPtr dataList = dsAuthBufferGetDataListAllocPriv(inAuthData);
if (dataList != NULL)
{
*outString = dsDataListGetNodeStringPriv(dataList, stringNum);
dsDataListDeallocatePriv(dataList);
free(dataList);
dataList = NULL;
return eDSNoErr;
}
return eDSInvalidBuffFormat;
}
sInt32 CPSPlugIn::GetDataFromAuthBuffer(tDataBufferPtr inAuthData, int nodeNum, unsigned char **outData, long *outLen)
{
tDataNodePtr pDataNode;
tDirStatus status;
*outLen = 0;
tDataListPtr dataList = dsAuthBufferGetDataListAllocPriv(inAuthData);
if (dataList != NULL)
{
status = dsDataListGetNodePriv(dataList, nodeNum, &pDataNode);
if ( status != eDSNoErr )
return status;
*outData = (unsigned char *) malloc(pDataNode->fBufferLength);
if ( ! (*outData) )
return eMemoryAllocError;
memcpy(*outData, ((tDataBufferPriv*)pDataNode)->fBufferData, pDataNode->fBufferLength);
*outLen = pDataNode->fBufferLength;
dsDataListDeallocatePriv(dataList);
free(dataList);
dataList = NULL;
return eDSNoErr;
}
return eDSInvalidBuffFormat;
}
sInt32 CPSPlugIn::DoAuthentication ( sDoDirNodeAuth *inData )
{
sInt32 siResult = noErr;
UInt32 uiAuthMethod = 0;
sPSContextData *pContext = NULL;
char saslMechNameStr[256];
char *userName = NULL;
char *password = NULL;
long passwordLen = 0;
char *challenge = NULL;
char *userIDToSet = NULL;
char *paramStr = NULL;
Boolean bHasValidAuth = false;
sPSContinueData *pContinue = NULL;
char *stepData = NULL;
DEBUGLOG( "CPSPlugIn::DoAuthentication\n");
try
{
pContext = (sPSContextData *)gPSContextTable->GetItemData( inData->fInNodeRef );
Throw_NULL( pContext, eDSBadContextData );
if ( ! Connected(pContext) )
{
siResult = ConnectToServer( pContext );
if ( siResult != 0 )
throw( siResult );
}
siResult = GetAuthMethodConstant( pContext, inData->fInAuthMethod, &uiAuthMethod, saslMechNameStr );
DEBUGLOG( "GetAuthMethodConstant siResult=%l, uiAuthMethod=%l, mech=%s\n", siResult, uiAuthMethod,saslMechNameStr);
if ( siResult == noErr &&
uiAuthMethod != kAuthNativeMethod &&
uiAuthMethod != kAuth2WayRandomChangePass )
{
siResult = GetAuthMethodSASLName( uiAuthMethod, inData->fInDirNodeAuthOnlyFlag, saslMechNameStr );
DEBUGLOG( "GetAuthMethodSASLName siResult=%l, mech=%s\n", siResult, saslMechNameStr);
}
if ( inData->fIOContinueData == NULL )
{
siResult = UnpackUsernameAndPassword( pContext,
uiAuthMethod,
inData->fInAuthStepData,
&userName,
&password,
&passwordLen,
&challenge );
}
else
{
if ( gContinue->VerifyItem( inData->fIOContinueData ) == false )
throw( (sInt32)eDSInvalidContinueData );
}
if ( siResult == noErr &&
(pContext->last.successfulAuth || pContext->nao.successfulAuth) &&
userName != NULL )
{
long len = strlen( userName );
char *strippedUserName = (char *) malloc( len + 1 );
Throw_NULL( strippedUserName, eMemoryError );
strcpy( strippedUserName, userName );
StripRSAKey( strippedUserName );
if ( strcmp( strippedUserName, pContext->last.username ) == 0 )
{
switch (uiAuthMethod)
{
case kAuthGetPolicy:
case kAuthSetPolicy:
case kAuthGetGlobalPolicy:
case kAuthSetGlobalPolicy:
case kAuthGetUserName:
case kAuthSetUserName:
case kAuthGetUserData:
case kAuthSetUserData:
case kAuthDeleteUser:
case kAuthNewUser:
case kAuthSetPasswdAsRoot:
case kAuthGetIDByName:
bHasValidAuth = true;
break;
default:
bHasValidAuth = false;
}
}
else
if ( pContext->nao.successfulAuth && strcmp( strippedUserName, pContext->nao.username ) == 0 )
{
memcpy( pContext->last.username, pContext->nao.username, kMaxUserNameLength + 1 );
if ( pContext->last.password != NULL ) {
memset( pContext->last.password, 0, pContext->last.passwordLen );
free( pContext->last.password );
pContext->last.password = NULL;
pContext->last.passwordLen = 0;
}
pContext->last.password = (char *) malloc( pContext->nao.passwordLen + 1 );
Throw_NULL( pContext->last.password, eMemoryError );
memcpy( pContext->last.password, pContext->nao.password, pContext->nao.passwordLen );
pContext->last.password[pContext->nao.passwordLen] = '\0';
pContext->last.passwordLen = pContext->nao.passwordLen;
}
free( strippedUserName );
}
if ( !bHasValidAuth &&
siResult == noErr &&
uiAuthMethod != kAuthGetPolicy &&
uiAuthMethod != kAuthGetGlobalPolicy &&
uiAuthMethod != kAuthGetIDByName &&
uiAuthMethod != kAuth2WayRandomChangePass
)
{
char *rsaKeyPtr;
if ( userName != NULL )
{
rsaKeyPtr = strchr( userName, ',' );
if ( rsaKeyPtr != NULL )
siResult = DoRSAValidation( pContext, rsaKeyPtr + 1 );
}
if ( siResult == noErr )
{
pContext->last.successfulAuth = false;
if ( uiAuthMethod == kAuth2WayRandom )
{
if ( inData->fIOContinueData == NULL )
{
pContinue = (sPSContinueData *)::calloc( 1, sizeof( sPSContinueData ) );
Throw_NULL( pContinue, eMemoryError );
gContinue->AddItem( pContinue, inData->fInNodeRef );
inData->fIOContinueData = pContinue;
pContinue->fAuthPass = 0;
pContinue->fData = NULL;
pContinue->fDataLen = 0;
}
siResult = DoSASLTwoWayRandAuth( pContext,
userName,
saslMechNameStr,
inData );
}
else
{
siResult = DoSASLAuth( pContext,
userName,
password,
passwordLen,
challenge,
saslMechNameStr,
&stepData );
}
if ( siResult == noErr && uiAuthMethod != kAuth2WayRandom )
{
pContext->last.successfulAuth = true;
if ( inData->fInDirNodeAuthOnlyFlag == false )
{
memcpy( pContext->nao.username, pContext->last.username, kMaxUserNameLength + 1 );
pContext->nao.password = (char *) malloc( pContext->last.passwordLen + 1 );
Throw_NULL( pContext->nao.password, eMemoryError );
memcpy( pContext->nao.password, pContext->last.password, pContext->last.passwordLen );
pContext->nao.password[pContext->last.passwordLen] = '\0';
pContext->nao.passwordLen = pContext->last.passwordLen;
pContext->nao.successfulAuth = true;
}
}
}
}
if ( siResult == eDSNoErr || siResult == eDSAuthNewPasswordRequired )
{
tDataBufferPtr outBuf = inData->fOutAuthStepDataResponse;
const char *encodedStr;
unsigned int encodedStrLen;
char encoded64Str[kOneKBuffer];
char buf[kOneKBuffer];
PWServerError result;
int saslResult;
switch( uiAuthMethod )
{
case kAuthDIGEST_MD5:
case kAuthDIGEST_MD5_Reauth:
#pragma mark kAuthDigestMD5
if ( stepData != NULL )
{
unsigned long lenOfStepData = strlen( stepData );
if ( outBuf->fBufferSize >= 4 + lenOfStepData + 1 )
{
memcpy( outBuf->fBufferData, &lenOfStepData, 4 );
strcpy( outBuf->fBufferData + 4, stepData );
outBuf->fBufferLength = 4 + lenOfStepData;
}
else
{
siResult = eDSBufferTooSmall;
}
}
break;
case kAuthSetPasswd:
case kAuthSetPasswdAsRoot:
#pragma mark kAuthSetPasswd
siResult = GetStringFromAuthBuffer( inData->fInAuthStepData, 1, &userIDToSet );
if ( siResult == noErr )
{
StripRSAKey(userIDToSet);
siResult = GetStringFromAuthBuffer( inData->fInAuthStepData, 2, ¶mStr );
}
if ( siResult == noErr )
{
if ( paramStr == NULL )
throw( eDSInvalidBuffFormat );
if (strlen(paramStr) > kChangePassPaddedBufferSize )
throw( eDSAuthParameterError );
if ( *paramStr == '\0' )
{
free( paramStr );
paramStr = (char *) malloc( strlen(kEmptyPasswordAltStr) + 1 );
strcpy( paramStr, kEmptyPasswordAltStr );
}
this->FillWithRandomData(buf, kChangePassPaddedBufferSize);
strcpy(buf, paramStr);
gSASLMutex->Wait();
saslResult = sasl_encode(pContext->conn,
buf,
kChangePassPaddedBufferSize,
&encodedStr,
&encodedStrLen);
gSASLMutex->Signal();
}
if ( siResult == noErr && saslResult == SASL_OK && userIDToSet != NULL )
{
if ( ConvertBinaryTo64( encodedStr, encodedStrLen, encoded64Str ) == SASL_OK )
{
char *commandBuf;
long commandBufLen = 11+strlen(userIDToSet)+1+strlen(encoded64Str)+3 + 40;
commandBuf = (char *) malloc( commandBufLen );
Throw_NULL(commandBuf, eMemoryAllocError);
snprintf(commandBuf, commandBufLen, "CHANGEPASS %s %s\r\n", userIDToSet, encoded64Str);
commandBuf[commandBufLen-1] = '\0';
writeToServer(pContext->serverOut, commandBuf);
result = readFromServer( pContext->serverIn, buf, kOneKBuffer );
free(commandBuf);
if ( result.err != 0 )
siResult = PWSErrToDirServiceError(result);
}
}
else
{
printf("encode64 failed\n");
}
break;
case kAuthChangePasswd:
#pragma mark kAuthChangePasswd
siResult = GetStringFromAuthBuffer( inData->fInAuthStepData, 3, ¶mStr );
if ( siResult == noErr )
{
if ( paramStr == NULL )
throw( eDSInvalidBuffFormat );
if ( strlen(paramStr) > kChangePassPaddedBufferSize )
throw( eDSAuthParameterError );
if ( *paramStr == '\0' )
{
free( paramStr );
paramStr = (char *) malloc( strlen(kEmptyPasswordAltStr) + 1 );
strcpy( paramStr, kEmptyPasswordAltStr );
}
this->FillWithRandomData(buf, kChangePassPaddedBufferSize);
strcpy(buf, paramStr);
gSASLMutex->Wait();
saslResult = sasl_encode(pContext->conn,
buf,
kChangePassPaddedBufferSize,
&encodedStr,
&encodedStrLen);
gSASLMutex->Signal();
}
if ( siResult == noErr && saslResult == SASL_OK && userName != NULL )
{
if ( ConvertBinaryTo64( encodedStr, encodedStrLen, encoded64Str ) == SASL_OK )
{
char *commandBuf;
long commandBufLen = 11+strlen(userName)+1+strlen(encoded64Str)+3 + 40;
commandBuf = (char *) malloc(commandBufLen);
Throw_NULL(commandBuf, eMemoryAllocError);
StripRSAKey(userName);
snprintf(commandBuf, commandBufLen, "CHANGEPASS %s %s\r\n", userName, encoded64Str);
commandBuf[commandBufLen-1] = '\0';
writeToServer(pContext->serverOut, commandBuf);
result = readFromServer( pContext->serverIn, buf, kOneKBuffer );
free(commandBuf);
if ( result.err != 0 )
siResult = PWSErrToDirServiceError(result);
}
}
else
{
printf("encode64 failed\n");
}
break;
case kAuthNewUser:
#pragma mark kAuthNewUser
siResult = GetStringFromAuthBuffer( inData->fInAuthStepData, 3, &userIDToSet );
if ( siResult == noErr )
siResult = GetStringFromAuthBuffer( inData->fInAuthStepData, 4, ¶mStr );
if ( siResult == noErr )
{
if ( userIDToSet == NULL || paramStr == NULL )
throw( eDSInvalidBuffFormat );
if ( strlen(paramStr) > kChangePassPaddedBufferSize )
throw( eDSAuthParameterError );
if ( *paramStr == '\0' )
{
free( paramStr );
paramStr = (char *) malloc( strlen(kEmptyPasswordAltStr) + 1 );
strcpy( paramStr, kEmptyPasswordAltStr );
}
this->FillWithRandomData(buf, kChangePassPaddedBufferSize);
strcpy(buf, paramStr);
gSASLMutex->Wait();
saslResult = sasl_encode(pContext->conn,
buf,
kChangePassPaddedBufferSize,
&encodedStr,
&encodedStrLen);
gSASLMutex->Signal();
}
if ( siResult == noErr && saslResult == SASL_OK )
{
if ( ConvertBinaryTo64( encodedStr, encodedStrLen, encoded64Str ) == SASL_OK )
{
if ( pContext->rsaPublicKeyStr == NULL )
throw( eDSAuthServerError );
fprintf(pContext->serverOut, "NEWUSER %s %s\r\n", userIDToSet, encoded64Str);
fflush(pContext->serverOut);
result = readFromServer( pContext->serverIn, buf, kOneKBuffer );
if ( result.err != 0 )
throw( PWSErrToDirServiceError(result) );
chop( buf );
encodedStrLen = strlen(buf) + 1 + strlen(pContext->rsaPublicKeyStr);
if ( encodedStrLen > outBuf->fBufferSize )
throw( eDSBufferTooSmall );
encodedStrLen -= 4;
memcpy( outBuf->fBufferData, &encodedStrLen, 4 );
outBuf->fBufferLength = 4;
encodedStrLen = strlen(buf+4);
memcpy( outBuf->fBufferData + outBuf->fBufferLength, buf+4, encodedStrLen );
outBuf->fBufferLength += encodedStrLen;
outBuf->fBufferData[outBuf->fBufferLength] = ',';
outBuf->fBufferLength++;
strcpy( outBuf->fBufferData + outBuf->fBufferLength, pContext->rsaPublicKeyStr );
outBuf->fBufferLength += strlen(pContext->rsaPublicKeyStr);
}
}
else
{
printf("encode64 failed\n");
}
break;
case kAuthGetPolicy:
#pragma mark kAuthGetPolicy
siResult = GetStringFromAuthBuffer( inData->fInAuthStepData, 3, &userIDToSet );
if ( siResult == noErr )
{
char commandBuf[256];
if ( userIDToSet == NULL )
throw( eDSInvalidBuffFormat );
StripRSAKey(userIDToSet);
snprintf(commandBuf, 256, "GETPOLICY %s\r\n", userIDToSet);
commandBuf[255] = '\0';
writeToServer(pContext->serverOut, commandBuf);
result = readFromServer( pContext->serverIn, buf, kOneKBuffer );
if ( result.err != 0 )
throw( PWSErrToDirServiceError(result) );
encodedStrLen = strlen(buf);
if ( encodedStrLen <= outBuf->fBufferSize )
{
::memcpy( buf, &encodedStrLen, 4 );
::memcpy( outBuf->fBufferData, buf, encodedStrLen );
outBuf->fBufferLength = encodedStrLen;
}
else
{
siResult = eDSBufferTooSmall;
}
}
break;
case kAuthSetPolicy:
#pragma mark kAuthSetPolicy
siResult = GetStringFromAuthBuffer( inData->fInAuthStepData, 3, &userIDToSet );
if ( siResult == noErr )
{
StripRSAKey(userIDToSet);
siResult = GetStringFromAuthBuffer( inData->fInAuthStepData, 4, ¶mStr );
}
if ( siResult == noErr )
{
fprintf(pContext->serverOut, "SETPOLICY %s %s\r\n", userIDToSet, paramStr );
fflush(pContext->serverOut);
result = readFromServer( pContext->serverIn, buf, kOneKBuffer );
if ( result.err != 0 )
siResult = PWSErrToDirServiceError(result);
}
break;
case kAuthGetGlobalPolicy:
#pragma mark kAuthGetGlobalPolicy
writeToServer(pContext->serverOut, "GETGLOBALPOLICY\r\n");
result = readFromServer( pContext->serverIn, buf, kOneKBuffer );
if ( result.err != 0 )
throw( PWSErrToDirServiceError(result) );
encodedStrLen = strlen(buf);
if ( encodedStrLen <= outBuf->fBufferSize )
{
::memcpy( buf, &encodedStrLen, 4 );
::memcpy( outBuf->fBufferData, buf, encodedStrLen );
outBuf->fBufferLength = encodedStrLen;
}
else
{
siResult = eDSBufferTooSmall;
}
break;
case kAuthSetGlobalPolicy:
#pragma mark kAuthSetGlobalPolicy
siResult = GetStringFromAuthBuffer( inData->fInAuthStepData, 3, ¶mStr );
if ( siResult == noErr )
{
fprintf(pContext->serverOut, "SETGLOBALPOLICY %s\r\n", paramStr );
fflush(pContext->serverOut);
result = readFromServer( pContext->serverIn, buf, kOneKBuffer );
if ( result.err != 0 )
siResult = PWSErrToDirServiceError(result);
}
break;
case kAuthGetUserName:
#pragma mark kAuthGetUserName
siResult = GetStringFromAuthBuffer( inData->fInAuthStepData, 3, &userIDToSet );
if ( siResult == noErr )
{
StripRSAKey(userIDToSet);
fprintf(pContext->serverOut, "GETUSERNAME %s\r\n", userIDToSet );
fflush(pContext->serverOut);
result = readFromServer( pContext->serverIn, buf, kOneKBuffer );
if ( result.err != 0 )
throw( PWSErrToDirServiceError(result) );
encodedStrLen = strlen(buf);
if ( encodedStrLen <= outBuf->fBufferSize )
{
::memcpy( buf, &encodedStrLen, 4 );
::memcpy( outBuf->fBufferData, buf, encodedStrLen );
outBuf->fBufferLength = encodedStrLen;
}
else
{
siResult = eDSBufferTooSmall;
}
}
break;
case kAuthSetUserName:
#pragma mark kAuthSetUserName
siResult = GetStringFromAuthBuffer( inData->fInAuthStepData, 3, &userIDToSet );
if ( siResult == noErr )
siResult = GetStringFromAuthBuffer( inData->fInAuthStepData, 4, ¶mStr );
if ( siResult == noErr )
{
StripRSAKey(userIDToSet);
fprintf(pContext->serverOut, "SETUSERNAME %s %s\r\n", userIDToSet, paramStr );
fflush(pContext->serverOut);
result = readFromServer( pContext->serverIn, buf, kOneKBuffer );
if ( result.err != 0 )
siResult = PWSErrToDirServiceError(result);
}
break;
case kAuthGetUserData:
#pragma mark kAuthGetUserData
siResult = GetStringFromAuthBuffer( inData->fInAuthStepData, 3, &userIDToSet );
if ( siResult == noErr )
{
char *outData = NULL;
unsigned long decodedStrLen;
StripRSAKey(userIDToSet);
fprintf(pContext->serverOut, "GETUSERDATA %s\r\n", userIDToSet );
fflush(pContext->serverOut);
result = readFromServer( pContext->serverIn, buf, kOneKBuffer );
if ( result.err != 0 )
siResult = PWSErrToDirServiceError(result);
if ( siResult == eDSNoErr )
{
outData = (char *)malloc(strlen(buf));
Throw_NULL(outData, eMemoryError);
if ( Convert64ToBinary( buf, outData, &decodedStrLen ) == 0 )
{
if ( decodedStrLen <= outBuf->fBufferSize )
{
::memcpy( outBuf->fBufferData, &decodedStrLen, 4 );
::memcpy( outBuf->fBufferData + 4, outData, decodedStrLen );
outBuf->fBufferLength = decodedStrLen;
}
else
{
siResult = eDSBufferTooSmall;
}
}
free(outData);
}
}
break;
case kAuthSetUserData:
#pragma mark kAuthSetUserData
{
char *tptr;
long dataSegmentLen;
siResult = GetStringFromAuthBuffer( inData->fInAuthStepData, 3, &userIDToSet );
if ( siResult == noErr )
{
StripRSAKey(userIDToSet);
tptr = inData->fInAuthStepData->fBufferData;
for (int repeatCount = 3; repeatCount > 0; repeatCount--)
{
memcpy(&dataSegmentLen, tptr, 4);
tptr += 4 + dataSegmentLen;
}
memcpy(&dataSegmentLen, tptr, 4);
paramStr = (char *)malloc( dataSegmentLen * 4/3 + 20 );
Throw_NULL( paramStr, eMemoryError );
siResult = ConvertBinaryTo64( tptr, dataSegmentLen, paramStr );
}
if ( siResult == noErr )
{
fprintf(pContext->serverOut, "SETUSERDATA %s %s\r\n", userIDToSet, paramStr );
fflush(pContext->serverOut);
result = readFromServer( pContext->serverIn, buf, kOneKBuffer );
if ( result.err != 0 )
siResult = PWSErrToDirServiceError(result);
}
}
break;
case kAuthDeleteUser:
#pragma mark kAuthDeleteUser
siResult = GetStringFromAuthBuffer( inData->fInAuthStepData, 3, &userIDToSet );
if ( siResult == noErr )
{
StripRSAKey(userIDToSet);
fprintf(pContext->serverOut, "DELETEUSER %s\r\n", userIDToSet );
fflush(pContext->serverOut);
result = readFromServer( pContext->serverIn, buf, kOneKBuffer );
if ( result.err != 0 )
siResult = PWSErrToDirServiceError(result);
}
break;
case kAuthGetIDByName:
#pragma mark kAuthGetIDByName
siResult = GetStringFromAuthBuffer( inData->fInAuthStepData, 3, ¶mStr );
if ( siResult == noErr )
{
fprintf(pContext->serverOut, "GETIDBYNAME %s\r\n", paramStr );
fflush(pContext->serverOut);
result = readFromServer( pContext->serverIn, buf, kOneKBuffer );
if ( result.err != 0 )
throw( PWSErrToDirServiceError(result) );
chop(buf);
if ( pContext->rsaPublicKeyStr )
{
strcat(buf, ",");
strcat(buf, pContext->rsaPublicKeyStr);
}
encodedStrLen = strlen(buf);
if ( encodedStrLen <= outBuf->fBufferSize )
{
::memcpy( buf, &encodedStrLen, 4 );
::memcpy( outBuf->fBufferData, buf, encodedStrLen );
outBuf->fBufferLength = encodedStrLen;
}
else
{
siResult = eDSBufferTooSmall;
}
}
break;
case kAuth2WayRandomChangePass:
#pragma mark kAuth2WayRandomChangePass
StripRSAKey(userName);
siResult = ConvertBinaryTo64( password, 8, encoded64Str );
if ( siResult == noErr )
{
char *commandBuf;
long commandBufLen = 16 + strlen(userName) + 1 + 16 + 1 + 16 + 3;
commandBuf = (char *) malloc(commandBufLen);
Throw_NULL(commandBuf, eMemoryAllocError);
sprintf(commandBuf, "TWRNDCHANGEPASS %s %s ", userName, encoded64Str);
siResult = ConvertBinaryTo64( password + 8, 8, encoded64Str );
if ( siResult == noErr )
{
strcat( commandBuf, encoded64Str );
strcat( commandBuf, "\r\n" );
writeToServer(pContext->serverOut, commandBuf);
result = readFromServer( pContext->serverIn, buf, sizeof(buf) );
}
free(commandBuf);
if ( siResult == noErr && result.err != 0 )
siResult = PWSErrToDirServiceError(result);
}
break;
}
}
}
catch ( sInt32 err )
{
siResult = err;
}
inData->fResult = siResult;
if ( userName != NULL )
free( userName );
if ( password != NULL )
free( password );
if ( userIDToSet != NULL )
free( userIDToSet );
if ( paramStr != NULL )
free( paramStr );
if ( stepData != NULL )
free( stepData );
DEBUGLOG( "CPSPlugIn::DoAuthentication returning %l\n", siResult);
return( siResult );
}
sInt32
CPSPlugIn::UnpackUsernameAndPassword(
sPSContextData *inContext,
UInt32 uiAuthMethod,
tDataBufferPtr inAuthBuf,
char **outUserName,
char **outPassword,
long *outPasswordLen,
char **outChallenge )
{
sInt32 siResult = eDSNoErr;
unsigned char *challenge = NULL;
unsigned char *digest = NULL;
long len = 0;
if ( outUserName == NULL || outPassword == NULL || outPasswordLen == NULL || outChallenge == NULL )
return eParameterError;
*outUserName = NULL;
*outPassword = NULL;
*outPasswordLen = 0;
*outChallenge = NULL;
try
{
switch (uiAuthMethod)
{
case kAuthAPOP:
siResult = GetStringFromAuthBuffer( inAuthBuf, 1, outUserName );
if ( siResult == noErr )
siResult = GetStringFromAuthBuffer( inAuthBuf, 2, (char **)&challenge );
if ( siResult == noErr )
siResult = GetStringFromAuthBuffer( inAuthBuf, 3, (char **)&digest );
if ( siResult == noErr )
{
if ( challenge == NULL || digest == NULL )
throw( eDSAuthParameterError );
long challengeLen = strlen((char *)challenge);
long digestLen = strlen((char *)digest);
if ( challengeLen > 0 && digestLen > 0 )
{
*outPasswordLen = challengeLen + 1 + digestLen;
*outPassword = (char *) malloc( *outPasswordLen + 1 );
Throw_NULL( (*outPassword), eMemoryAllocError );
strcpy( *outPassword, (char *)challenge );
strcat( *outPassword, " " );
strcat( *outPassword, (char *)digest );
}
}
break;
case kAuthDIGEST_MD5_Reauth:
siResult = GetStringFromAuthBuffer( inAuthBuf, 1, outUserName );
if ( siResult == noErr )
siResult = GetDataFromAuthBuffer( inAuthBuf, 2, &digest, &len );
if ( siResult == noErr && digest != NULL )
{
*outPassword = (char *) malloc( len + 1 );
Throw_NULL( (*outPassword), eMemoryAllocError );
**outPassword = '\0';
memcpy( (*outPassword) + 1, digest, len );
*outPasswordLen = len + 1;
}
break;
case kAuthDIGEST_MD5:
{
char *method = NULL;
siResult = GetStringFromAuthBuffer( inAuthBuf, 1, outUserName );
if ( siResult == noErr )
siResult = GetStringFromAuthBuffer( inAuthBuf, 2, (char **) &challenge );
if ( siResult == noErr )
siResult = GetDataFromAuthBuffer( inAuthBuf, 3, &digest, &len );
if ( siResult == noErr )
siResult = GetStringFromAuthBuffer( inAuthBuf, 4, &method );
if ( siResult == noErr )
{
if ( challenge != NULL && digest != NULL && method != NULL )
{
*outPassword = (char *) malloc( len + 1 );
Throw_NULL( (*outPassword), eMemoryAllocError );
**outPassword = '\0';
memcpy( (*outPassword) + 1, digest, len );
*outPasswordLen = len + 1;
*outChallenge = (char *) malloc( strlen((char *)challenge) + 10 + strlen(method) );
strcpy( *outChallenge, (char *)challenge );
strcat( *outChallenge, ",method=\"" );
strcat( *outChallenge, method );
strcat( *outChallenge, "\"" );
}
else
{
siResult = eDSAuthInBuffFormatError;
}
}
}
break;
case kAuthCRAM_MD5:
siResult = GetStringFromAuthBuffer( inAuthBuf, 1, outUserName );
if ( siResult == noErr )
siResult = GetStringFromAuthBuffer( inAuthBuf, 2, outChallenge );
if ( siResult == noErr )
siResult = GetDataFromAuthBuffer( inAuthBuf, 3, &digest, &len );
if ( siResult == noErr && digest != NULL )
{
*outPassword = (char *) malloc( len + 1 );
Throw_NULL( (*outPassword), eMemoryAllocError );
**outPassword = '\0';
memcpy( (*outPassword) + 1, digest, len );
*outPasswordLen = len + 1;
}
break;
case kAuthSMB_NT_Key:
case kAuthSMB_LM_Key:
siResult = GetStringFromAuthBuffer( inAuthBuf, 1, outUserName );
if ( siResult == noErr )
{
*outPassword = (char *)malloc(32);
Throw_NULL( (*outPassword), eMemoryAllocError );
*outPasswordLen = 32;
siResult = GetDataFromAuthBuffer( inAuthBuf, 2, &challenge, &len );
if ( siResult != noErr || challenge == NULL || len != 8 )
throw( (sInt32)eDSInvalidBuffFormat );
siResult = GetDataFromAuthBuffer( inAuthBuf, 3, &digest, &len );
if ( siResult != noErr || digest == NULL || len != 24 )
throw( (sInt32)eDSInvalidBuffFormat );
memcpy( *outPassword, challenge, 8 );
memcpy( (*outPassword) + 8, digest, 24 );
free( challenge );
challenge = NULL;
free( digest );
digest = NULL;
}
break;
case kAuth2WayRandom:
if ( inAuthBuf->fBufferLength > inAuthBuf->fBufferSize )
throw( (sInt32)eDSInvalidBuffFormat );
*outUserName = (char*)calloc( inAuthBuf->fBufferLength + 1, 1 );
strncpy( *outUserName, inAuthBuf->fBufferData, inAuthBuf->fBufferLength );
(*outUserName)[inAuthBuf->fBufferLength] = '\0';
break;
case kAuth2WayRandomChangePass:
siResult = GetStringFromAuthBuffer( inAuthBuf, 1, outUserName );
if ( siResult == noErr )
{
char *tempPWStr = NULL;
siResult = GetStringFromAuthBuffer( inAuthBuf, 2, &tempPWStr );
if ( siResult == noErr && tempPWStr != NULL && strlen(tempPWStr) == 8 )
{
*outPasswordLen = 16;
*outPassword = (char *)malloc(16);
memcpy( *outPassword, tempPWStr, 8 );
free( tempPWStr );
siResult = GetStringFromAuthBuffer( inAuthBuf, 3, &tempPWStr );
if ( siResult == noErr && tempPWStr != NULL && strlen(tempPWStr) == 8 )
{
memcpy( *outPassword + 8, tempPWStr, 8 );
free( tempPWStr );
}
}
}
break;
case kAuthSetPasswd:
siResult = GetStringFromAuthBuffer( inAuthBuf, 3, outUserName );
if ( siResult == noErr )
siResult = GetStringFromAuthBuffer( inAuthBuf, 4, outPassword );
if ( siResult == noErr && *outPassword != NULL )
*outPasswordLen = strlen( *outPassword );
break;
case kAuthSetPasswdAsRoot:
if ( inContext->nao.successfulAuth && inContext->nao.password != NULL )
{
long pwLen;
*outUserName = (char *)malloc(kUserIDLength + 1);
strncpy(*outUserName, inContext->nao.username, kUserIDLength);
(*outUserName)[kUserIDLength] = '\0';
pwLen = strlen(inContext->nao.password);
*outPassword = (char *)malloc(pwLen + 1);
strncpy(*outPassword, inContext->nao.password, pwLen);
(*outPassword)[pwLen] = '\0';
*outPasswordLen = pwLen;
siResult = eDSNoErr;
}
else
{
siResult = eDSNotAuthorized;
}
break;
default:
siResult = GetStringFromAuthBuffer( inAuthBuf, 1, outUserName );
if ( siResult == noErr )
siResult = GetStringFromAuthBuffer( inAuthBuf, 2, outPassword );
if ( siResult == noErr && *outPassword != NULL )
*outPasswordLen = strlen( *outPassword );
if ( *outPassword == NULL || *outPasswordLen == 0 )
{
if ( *outPassword != NULL )
free( *outPassword );
*outPasswordLen = strlen( kEmptyPasswordAltStr );
*outPassword = (char *) malloc( *outPasswordLen + 1 );
strcpy( *outPassword, kEmptyPasswordAltStr );
}
}
}
catch ( sInt32 error )
{
siResult = error;
}
catch (...)
{
DEBUGLOG( "PasswordServer PlugIn: uncasted throw" );
siResult = eDSAuthFailed;
}
if ( challenge != NULL ) {
free( challenge );
challenge = NULL;
}
if ( digest != NULL ) {
free( digest );
digest = NULL;
}
if ( siResult == eDSNoErr && *outUserName == NULL && uiAuthMethod != kAuth2WayRandom )
siResult = eDSUserUnknown;
return siResult;
}
void
CPSPlugIn::StripRSAKey( char *inOutUserID )
{
if ( inOutUserID == NULL )
return;
char *delim = strchr( inOutUserID, ',' );
if ( delim )
*delim = '\0';
}
sInt32
CPSPlugIn::GetAuthMethodConstant(
sPSContextData *inContext,
tDataNode *inData,
uInt32 *outAuthMethod,
char *outNativeAuthMethodSASLName )
{
sInt32 siResult = noErr;
char *p = NULL;
sInt32 prefixLen;
if ( inData == NULL )
{
*outAuthMethod = kAuthUnknownMethod;
return( eDSAuthParameterError );
}
if ( outNativeAuthMethodSASLName != NULL )
*outNativeAuthMethodSASLName = '\0';
p = (char *)inData->fBufferData;
DEBUGLOG( "PasswordServer PlugIn: Attempting use of authentication method %s", p );
prefixLen = strlen(kDSNativeAuthMethodPrefix);
if ( ::strncmp( p, kDSNativeAuthMethodPrefix, prefixLen ) == 0 )
{
sInt32 index;
*outAuthMethod = kAuthUnknownMethod;
siResult = eDSAuthMethodNotSupported;
p += prefixLen;
if ( strcmp( p, "dsAuthGetIDByName" ) == 0 )
{
*outAuthMethod = kAuthGetIDByName;
return eDSNoErr;
}
for ( index = inContext->mechCount - 1; index >= 0; index-- )
{
if ( strcmp( p, inContext->mech[index].method ) == 0 )
{
if ( outNativeAuthMethodSASLName != NULL )
strcpy( outNativeAuthMethodSASLName, inContext->mech[index].method );
*outAuthMethod = kAuthNativeMethod;
siResult = noErr;
break;
}
}
}
else
if ( ::strcmp( p, kDSStdAuthClearText ) == 0 )
{
*outAuthMethod = kAuthClearText;
}
else
if ( ::strcmp( p, kDSStdAuthNodeNativeClearTextOK ) == 0 )
{
*outAuthMethod = kAuthNativeClearTextOK;
}
else
if ( ::strcmp( p, kDSStdAuthNodeNativeNoClearText ) == 0 )
{
*outAuthMethod = kAuthNativeNoClearText;
}
else
if ( ::strcmp( p, kDSStdAuthSetPasswd ) == 0 )
{
*outAuthMethod = kAuthSetPasswd;
}
else
if ( ::strcmp( p, kDSStdAuthChangePasswd ) == 0 )
{
*outAuthMethod = kAuthChangePasswd;
}
else
if ( ::strcmp( p, kDSStdAuthSetPasswdAsRoot ) == 0 )
{
*outAuthMethod = kAuthSetPasswdAsRoot;
}
else
if ( ::strcmp( p, kDSStdAuthAPOP ) == 0 )
{
*outAuthMethod = kAuthAPOP;
}
else
if ( ::strcmp( p, kDSStdAuth2WayRandom ) == 0 )
{
*outAuthMethod = kAuth2WayRandom;
}
else
if ( ::strcmp( p, kDSStdAuth2WayRandomChangePasswd ) == 0 )
{
*outAuthMethod = kAuth2WayRandomChangePass;
}
else
if ( ::strcmp( p, kDSStdAuthSMB_NT_Key ) == 0 )
{
*outAuthMethod = kAuthSMB_NT_Key;
}
else
if ( ::strcmp( p, kDSStdAuthSMB_LM_Key ) == 0 )
{
*outAuthMethod = kAuthSMB_LM_Key;
}
else
if ( ::strcmp( p, kDSStdAuthDIGEST_MD5 ) == 0 )
{
*outAuthMethod = kAuthDIGEST_MD5;
}
else
if ( ::strcmp( p, "dsAuthMethodStandard:dsAuthNodeDIGEST-MD5-Reauth" ) == 0 )
{
*outAuthMethod = kAuthDIGEST_MD5_Reauth;
}
else
if ( ::strcmp( p, kDSStdAuthCRAM_MD5 ) == 0 )
{
*outAuthMethod = kAuthCRAM_MD5;
}
else
if ( ::strcmp( p, kDSStdAuthNewUser ) == 0 )
{
*outAuthMethod = kAuthNewUser;
}
else
if ( ::strcmp( p, kDSStdAuthGetPolicy ) == 0 )
{
*outAuthMethod = kAuthGetPolicy;
}
else
if ( ::strcmp( p, kDSStdAuthSetPolicy ) == 0 )
{
*outAuthMethod = kAuthSetPolicy;
}
else
if ( ::strcmp( p, kDSStdAuthGetGlobalPolicy ) == 0 )
{
*outAuthMethod = kAuthGetGlobalPolicy;
}
else
if ( ::strcmp( p, kDSStdAuthSetGlobalPolicy ) == 0 )
{
*outAuthMethod = kAuthSetGlobalPolicy;
}
else
if ( ::strcmp( p, kDSStdAuthGetUserName ) == 0 )
{
*outAuthMethod = kAuthGetUserName;
}
else
if ( ::strcmp( p, kDSStdAuthSetUserName ) == 0 )
{
*outAuthMethod = kAuthSetUserName;
}
else
if ( ::strcmp( p, kDSStdAuthGetUserData ) == 0 )
{
*outAuthMethod = kAuthGetUserData;
}
else
if ( ::strcmp( p, kDSStdAuthSetUserData ) == 0 )
{
*outAuthMethod = kAuthSetUserData;
}
else
if ( ::strcmp( p, kDSStdAuthDeleteUser ) == 0 )
{
*outAuthMethod = kAuthDeleteUser;
}
else
{
*outAuthMethod = kAuthUnknownMethod;
siResult = eDSAuthMethodNotSupported;
}
return( siResult );
}
sInt32
CPSPlugIn::GetAuthMethodSASLName ( uInt32 inAuthMethodConstant, bool inAuthOnly, char *outMechName )
{
sInt32 result = noErr;
if ( outMechName == NULL )
return -1;
*outMechName = '\0';
switch ( inAuthMethodConstant )
{
case kAuthClearText:
strcpy( outMechName, "PLAIN" );
break;
case kAuthCrypt:
strcpy( outMechName, "CRYPT" );
break;
case kAuthSetPasswd:
strcpy( outMechName, kDHX_SASL_Name );
break;
case kAuthChangePasswd:
strcpy( outMechName, kDHX_SASL_Name );
break;
case kAuthSetPasswdAsRoot:
strcpy( outMechName, kDHX_SASL_Name );
break;
case kAuthAPOP:
strcpy( outMechName, "APOP" );
break;
case kAuth2WayRandom:
strcpy( outMechName, "TWOWAYRANDOM" );
break;
case kAuthNativeClearTextOK:
strcpy( outMechName, inAuthOnly ? kAuthNative_Priority : kDHX_SASL_Name );
strcat( outMechName, " PLAIN" );
break;
case kAuthNativeNoClearText:
strcpy( outMechName, inAuthOnly ? kAuthNative_Priority : kDHX_SASL_Name );
break;
case kAuthSMB_NT_Key:
strcpy( outMechName, "SMB-NT" );
break;
case kAuthSMB_LM_Key:
strcpy( outMechName, "SMB-LAN-MANAGER" );
break;
case kAuthDIGEST_MD5:
case kAuthDIGEST_MD5_Reauth:
strcpy( outMechName, "WEBDAV-DIGEST" );
break;
case kAuthCRAM_MD5:
strcpy( outMechName, "CRAM-MD5" );
break;
case kAuthGetPolicy:
case kAuthSetPolicy:
case kAuthGetGlobalPolicy:
case kAuthSetGlobalPolicy:
case kAuthGetUserName:
case kAuthSetUserName:
case kAuthGetUserData:
case kAuthSetUserData:
case kAuthDeleteUser:
strcpy( outMechName, kAuthNative_Priority );
break;
case kAuthNewUser:
strcpy( outMechName, kDHX_SASL_Name );
break;
case kAuthUnknownMethod:
case kAuthNativeMethod:
default:
result = eDSAuthMethodNotSupported;
}
return result;
}
void CPSPlugIn::GetAuthMethodFromSASLName( const char *inMechName, char *outDSType )
{
if ( outDSType == NULL )
return;
*outDSType = '\0';
if ( inMechName == NULL )
return;
if ( strcmp( inMechName, "TWOWAYRANDOM" ) == 0 )
{
strcpy( outDSType, kDSStdAuth2WayRandom );
}
else
if ( strcmp( inMechName, "SMB-NT" ) == 0 )
{
strcpy( outDSType, kDSStdAuthSMB_NT_Key );
}
else
if ( strcmp( inMechName, "SMB-LAN-MANAGER" ) == 0 )
{
strcpy( outDSType, kDSStdAuthSMB_LM_Key );
}
else
if ( strcmp( inMechName, "DIGEST-MD5" ) == 0 )
{
strcpy( outDSType, kDSStdAuthDIGEST_MD5 );
}
else
if ( strcmp( inMechName, "CRAM-MD5" ) == 0 )
{
strcpy( outDSType, kDSStdAuthCRAM_MD5 );
}
else
if ( strcmp( inMechName, "CRYPT" ) == 0 )
{
strcpy( outDSType, kDSStdAuthCrypt );
}
else
if ( strcmp( inMechName, "APOP" ) == 0 )
{
strcpy( outDSType, kDSStdAuthAPOP );
}
}
sInt32 CPSPlugIn::PWSErrToDirServiceError( PWServerError inError )
{
sInt32 result = 0;
if ( inError.err == 0 )
return 0;
switch ( inError.type )
{
case kPolicyError:
result = PolicyErrToDirServiceError( inError.err );
break;
case kSASLError:
result = SASLErrToDirServiceError( inError.err );
break;
}
return result;
}
sInt32 CPSPlugIn::PolicyErrToDirServiceError( int inPolicyError )
{
sInt32 dirServiceErr = eDSAuthFailed;
switch( inPolicyError )
{
case kAuthOK: dirServiceErr = eDSNoErr; break;
case kAuthFail: dirServiceErr = eDSAuthFailed; break;
case kAuthUserDisabled: dirServiceErr = eDSAuthAccountDisabled; break;
case kAuthNeedAdminPrivs: dirServiceErr = eDSAuthFailed; break;
case kAuthUserNotSet: dirServiceErr = eDSAuthUnknownUser; break;
case kAuthUserNotAuthenticated: dirServiceErr = eDSAuthFailed; break;
case kAuthPasswordExpired: dirServiceErr = eDSAuthAccountExpired; break;
case kAuthPasswordNeedsChange: dirServiceErr = eDSAuthNewPasswordRequired; break;
case kAuthPasswordNotChangeable: dirServiceErr = eDSAuthFailed; break;
case kAuthPasswordTooShort: dirServiceErr = eDSAuthPasswordTooShort; break;
case kAuthPasswordTooLong: dirServiceErr = eDSAuthPasswordTooLong; break;
case kAuthPasswordNeedsAlpha: dirServiceErr = eDSAuthPasswordNeedsLetter; break;
case kAuthPasswordNeedsDecimal: dirServiceErr = eDSAuthPasswordNeedsDigit; break;
case kAuthMethodTooWeak: dirServiceErr = eDSAuthMethodNotSupported; break;
}
return dirServiceErr;
}
sInt32 CPSPlugIn::SASLErrToDirServiceError( int inSASLError )
{
sInt32 dirServiceErr = eDSAuthFailed;
switch (inSASLError)
{
case SASL_CONTINUE: dirServiceErr = eDSNoErr; break;
case SASL_OK: dirServiceErr = eDSNoErr; break;
case SASL_FAIL: dirServiceErr = eDSAuthFailed; break;
case SASL_NOMEM: dirServiceErr = eMemoryError; break;
case SASL_BUFOVER: dirServiceErr = eDSBufferTooSmall; break;
case SASL_NOMECH: dirServiceErr = eDSAuthMethodNotSupported; break;
case SASL_BADPROT: dirServiceErr = eDSAuthParameterError; break;
case SASL_NOTDONE: dirServiceErr = eDSAuthFailed; break;
case SASL_BADPARAM: dirServiceErr = eDSAuthParameterError; break;
case SASL_TRYAGAIN: dirServiceErr = eDSAuthFailed; break;
case SASL_BADMAC: dirServiceErr = eDSAuthFailed; break;
case SASL_NOTINIT: dirServiceErr = eDSAuthFailed; break;
case SASL_INTERACT: dirServiceErr = eDSAuthParameterError; break;
case SASL_BADSERV: dirServiceErr = eDSAuthFailed; break;
case SASL_WRONGMECH: dirServiceErr = eDSAuthParameterError; break;
case SASL_BADAUTH: dirServiceErr = eDSAuthBadPassword; break;
case SASL_NOAUTHZ: dirServiceErr = eDSAuthBadPassword; break;
case SASL_TOOWEAK: dirServiceErr = eDSAuthMethodNotSupported; break;
case SASL_ENCRYPT: dirServiceErr = eDSAuthInBuffFormatError; break;
case SASL_TRANS: dirServiceErr = eDSAuthFailed; break;
case SASL_EXPIRED: dirServiceErr = eDSAuthFailed; break;
case SASL_DISABLED: dirServiceErr = eDSAuthFailed; break;
case SASL_NOUSER: dirServiceErr = eDSAuthUnknownUser; break;
case SASL_BADVERS: dirServiceErr = eDSAuthServerError; break;
case SASL_UNAVAIL: dirServiceErr = eDSAuthNoAuthServerFound; break;
case SASL_NOVERIFY: dirServiceErr = eDSAuthNoAuthServerFound; break;
case SASL_PWLOCK: dirServiceErr = eDSAuthFailed; break;
case SASL_NOCHANGE: dirServiceErr = eDSAuthFailed; break;
case SASL_WEAKPASS: dirServiceErr = eDSAuthBadPassword; break;
case SASL_NOUSERPASS: dirServiceErr = eDSAuthFailed; break;
}
return dirServiceErr;
}
sInt32
CPSPlugIn::DoSASLAuth(
sPSContextData *inContext,
const char *userName,
const char *password,
long inPasswordLen,
const char *inChallenge,
const char *inMechName,
char **outStepData )
{
sInt32 siResult = eDSAuthFailed;
DEBUGLOG( "CPSPlugIn::DoSASLAuth\n");
try
{
Throw_NULL( inContext, eDSBadContextData );
Throw_NULL( password, eParameterError );
if ( outStepData != NULL )
*outStepData = NULL;
DEBUGLOG( "PasswordServer PlugIn: Attempting Authentication" );
{
char buf[4096];
const char *data;
char dataBuf[4096];
unsigned long binLen;
const char *chosenmech = NULL;
unsigned int len = 0;
int r;
PWServerError serverResult;
sasl_security_properties_t secprops = {0,65535,4096,0,NULL,NULL};
if ( userName != NULL )
{
long userNameLen;
char *userNameEnd = strchr( userName, ',' );
if ( userNameEnd != NULL )
{
userNameLen = userNameEnd - userName;
if ( userNameLen >= kMaxUserNameLength )
throw( (sInt32)eDSAuthInvalidUserName );
strncpy(inContext->last.username, userName, userNameLen );
inContext->last.username[userNameLen] = '\0';
}
else
{
strncpy( inContext->last.username, userName, kMaxUserNameLength );
inContext->last.username[kMaxUserNameLength-1] = '\0';
}
}
if ( inContext->last.password != NULL )
{
memset( inContext->last.password, 0, inContext->last.passwordLen );
free( inContext->last.password );
inContext->last.password = NULL;
inContext->last.passwordLen = 0;
}
inContext->last.password = (char *) malloc( inPasswordLen + 1 );
Throw_NULL( inContext->last.password, eMemoryError );
memcpy( inContext->last.password, password, inPasswordLen );
inContext->last.password[inPasswordLen] = '\0';
inContext->last.passwordLen = inPasswordLen;
if ( inContext->conn != NULL )
{
gSASLMutex->Wait();
sasl_dispose(&inContext->conn);
gSASLMutex->Signal();
inContext->conn = NULL;
}
inContext->callbacks[0].id = SASL_CB_GETREALM;
inContext->callbacks[0].proc = (sasl_cbproc *)&getrealm;
inContext->callbacks[0].context = inContext;
inContext->callbacks[1].id = SASL_CB_USER;
inContext->callbacks[1].proc = (sasl_cbproc *)&simple;
inContext->callbacks[1].context = inContext;
inContext->callbacks[2].id = SASL_CB_AUTHNAME;
inContext->callbacks[2].proc = (sasl_cbproc *)&simple;
inContext->callbacks[2].context = inContext;
inContext->callbacks[3].id = SASL_CB_PASS;
inContext->callbacks[3].proc = (sasl_cbproc *)&getsecret;
inContext->callbacks[3].context = inContext;
inContext->callbacks[4].id = SASL_CB_LIST_END;
inContext->callbacks[4].proc = NULL;
inContext->callbacks[4].context = NULL;
gSASLMutex->Wait();
r = sasl_client_new( "rcmd",
inContext->psName,
inContext->localaddr,
inContext->remoteaddr,
inContext->callbacks,
0,
&inContext->conn);
gSASLMutex->Signal();
if ( r != SASL_OK || inContext->conn == NULL ) {
DEBUGLOG( "sasl_client_new failed, err=%d.\n", r);
throw( SASLErrToDirServiceError(r) );
}
gSASLMutex->Wait();
r = sasl_setprop(inContext->conn, SASL_SEC_PROPS, &secprops);
r = sasl_client_start( inContext->conn, inMechName, NULL, &data, &len, &chosenmech );
gSASLMutex->Signal();
DEBUGLOG( "chosenmech=%s, datalen=%u\n", chosenmech, len);
if ( r != SASL_OK && r != SASL_CONTINUE ) {
DEBUGLOG( "starting SASL negotiation, err=%d\n", r);
throw( SASLErrToDirServiceError(r) );
}
snprintf(dataBuf, 4096, "USER %s\r\n", userName);
dataBuf[4095] = '\0';
writeToServer(inContext->serverOut, dataBuf);
serverResult = readFromServer( inContext->serverIn, buf, 4096 );
if (serverResult.err != 0) {
DEBUGLOG( "server returned an error, err=%d\n", serverResult.err);
throw( PWSErrToDirServiceError(serverResult) );
}
dataBuf[0] = 0;
if ( inChallenge != NULL )
{
if ( strcmp(chosenmech, "WEBDAV-DIGEST") == 0 )
{
strcpy(dataBuf, "replay ");
ConvertBinaryToHex( (const unsigned char *)inChallenge, strlen(inChallenge), dataBuf+7 );
len = strlen(dataBuf);
}
else
{
ConvertBinaryToHex( (const unsigned char *)inChallenge, strlen(inChallenge), dataBuf );
len = strlen(dataBuf);
}
}
else
if ( len > 0 )
ConvertBinaryToHex( (const unsigned char *)data, len, dataBuf );
if ( len > 0 )
snprintf(buf, 4096, "AUTH %s %s\r\n", chosenmech, dataBuf);
else
snprintf(buf, 4096, "AUTH %s\r\n", chosenmech);
buf[4095] = '\0';
writeToServer(inContext->serverOut, buf);
serverResult = readFromServer(inContext->serverIn, buf, 4096);
if (serverResult.err != 0) {
DEBUGLOG( "server returned an error, err=%d\n", serverResult.err);
throw( PWSErrToDirServiceError(serverResult) );
}
chop(buf);
len = strlen(buf);
while ( 1 )
{
if ( len >= 3 && strncmp( buf, "+OK", 3 ) == 0 )
{
if ( len > 4 )
{
ConvertHexToBinary( buf + 4, (unsigned char *) dataBuf, &binLen );
gSASLMutex->Wait();
r = sasl_client_step(inContext->conn, dataBuf, binLen, NULL, &data, &len);
gSASLMutex->Signal();
}
else
{
data = NULL;
len = 0;
r = SASL_OK;
}
}
else
r = SASL_FAIL;
if (r != SASL_OK && r != SASL_CONTINUE) {
DEBUGLOG( "sasl_client_step=%d\n", r);
throw( SASLErrToDirServiceError(r) );
}
if (data && len != 0)
{
DEBUGLOG( "sending response length %d...\n", len);
ConvertBinaryToHex( (const unsigned char *)data, len, dataBuf );
DEBUGLOG( "AUTH2 %s\r\n", dataBuf);
fprintf(inContext->serverOut, "AUTH2 %s\r\n", dataBuf );
fflush(inContext->serverOut);
}
else
if (r==SASL_CONTINUE)
{
DEBUGLOG( "sending null response...\n");
fprintf(inContext->serverOut, "AUTH2 \r\n" );
fflush(inContext->serverOut);
}
else
break;
serverResult = readFromServer(inContext->serverIn, buf, 4096 );
if ( serverResult.err != 0 ) {
DEBUGLOG( "server returned an error, err=%d\n", serverResult.err);
throw( PWSErrToDirServiceError(serverResult) );
}
chop(buf);
len = strlen(buf);
if ( r != SASL_CONTINUE )
break;
}
if ( outStepData != NULL && strcmp(chosenmech, "WEBDAV-DIGEST") == 0 )
{
*outStepData = (char *) malloc( binLen + 1 );
if ( *outStepData == NULL )
throw( eMemoryError );
memcpy( *outStepData, dataBuf, binLen );
(*outStepData)[binLen] = '\0';
DEBUGLOG( "outStepData = %s\n", *outStepData );
}
throw( SASLErrToDirServiceError(r) );
}
}
catch ( sInt32 err )
{
DEBUGLOG( "PasswordServer PlugIn: SASL authentication error %l", err );
siResult = err;
}
catch ( ... )
{
DEBUGLOG( "PasswordServer PlugIn: SASL uncasted authentication error" );
siResult = eDSAuthFailed;
}
return( siResult );
}
sInt32
CPSPlugIn::DoSASLTwoWayRandAuth(
sPSContextData *inContext,
const char *userName,
const char *inMechName,
sDoDirNodeAuth *inData )
{
sInt32 siResult = eDSAuthFailed;
char buf[4096];
const char *data;
char dataBuf[4096];
const char *chosenmech = NULL;
unsigned int len = 0;
int r;
PWServerError serverResult;
sasl_security_properties_t secprops = {0,65535,4096,0,NULL,NULL};
sPSContinueData *pContinue = (sPSContinueData *) inData->fIOContinueData;
tDataBufferPtr outAuthBuff = inData->fOutAuthStepDataResponse;
tDataBufferPtr inAuthBuff = inData->fInAuthStepData;
DEBUGLOG( "CPSPlugIn::DoSASLTwoWayRandAuth\n");
try
{
Throw_NULL( inContext, eDSBadContextData );
Throw_NULL( inMechName, eParameterError );
Throw_NULL( inData, eParameterError );
Throw_NULL( inAuthBuff, eDSNullAuthStepData );
Throw_NULL( outAuthBuff, eDSNullAuthStepDataResp );
Throw_NULL( pContinue, eDSAuthContinueDataBad );
if ( outAuthBuff->fBufferSize < 8 )
throw( (sInt32)eDSAuthResponseBufTooSmall );
DEBUGLOG( "PasswordServer PlugIn: Attempting Authentication" );
if ( pContinue->fAuthPass == 0 )
{
if ( userName != NULL )
{
long userNameLen;
char *userNameEnd = strchr( userName, ',' );
if ( userNameEnd != NULL )
{
userNameLen = userNameEnd - userName;
if ( userNameLen >= kMaxUserNameLength )
throw( (sInt32)eDSAuthInvalidUserName );
strncpy(inContext->last.username, userName, userNameLen );
inContext->last.username[userNameLen] = '\0';
}
else
{
strncpy( inContext->last.username, userName, kMaxUserNameLength );
inContext->last.username[kMaxUserNameLength-1] = '\0';
}
}
if ( inContext->conn != NULL ) {
sasl_dispose(&inContext->conn);
inContext->conn = NULL;
}
inContext->callbacks[0].id = SASL_CB_GETREALM;
inContext->callbacks[0].proc = (sasl_cbproc *)&getrealm;
inContext->callbacks[0].context = inContext;
inContext->callbacks[1].id = SASL_CB_USER;
inContext->callbacks[1].proc = (sasl_cbproc *)&simple;
inContext->callbacks[1].context = inContext;
inContext->callbacks[2].id = SASL_CB_AUTHNAME;
inContext->callbacks[2].proc = (sasl_cbproc *)&simple;
inContext->callbacks[2].context = inContext;
inContext->callbacks[3].id = SASL_CB_PASS;
inContext->callbacks[3].proc = (sasl_cbproc *)&getsecret;
inContext->callbacks[3].context = inContext;
inContext->callbacks[4].id = SASL_CB_LIST_END;
inContext->callbacks[4].proc = NULL;
inContext->callbacks[4].context = NULL;
gSASLMutex->Wait();
r = sasl_client_new( "rcmd",
inContext->psName,
inContext->localaddr,
inContext->remoteaddr,
inContext->callbacks,
0,
&inContext->conn);
gSASLMutex->Signal();
if ( r != SASL_OK || inContext->conn == NULL ) {
DEBUGLOG( "sasl_client_new failed, err=%d.\n", r);
throw( SASLErrToDirServiceError(r) );
}
r = sasl_setprop(inContext->conn, SASL_SEC_PROPS, &secprops);
snprintf(dataBuf, sizeof(dataBuf), "USER %s\r\n", userName);
writeToServer(inContext->serverOut, dataBuf);
serverResult = readFromServer( inContext->serverIn, buf, sizeof(buf) );
if (serverResult.err != 0) {
DEBUGLOG( "server returned an error, err=%d\n", serverResult.err);
throw( PWSErrToDirServiceError(serverResult) );
}
snprintf(buf, sizeof(buf), "AUTH %s\r\n", inMechName);
writeToServer(inContext->serverOut, buf);
serverResult = readFromServer(inContext->serverIn, buf, sizeof(buf));
if (serverResult.err != 0) {
DEBUGLOG( "server returned an error, err=%d\n", serverResult.err);
throw( PWSErrToDirServiceError(serverResult) );
}
chop(buf);
len = strlen(buf);
if ( len >= 3 && strncmp( buf, "+OK", 3 ) == 0 )
{
if ( len > 4 )
{
unsigned long binLen;
unsigned long num1, num2;
char *num2Ptr = NULL;
unsigned char *saveData = NULL;
ConvertHexToBinary( buf + 4, (unsigned char *) dataBuf, &binLen );
dataBuf[binLen] = '\0';
saveData = (unsigned char *) malloc(binLen + 1);
Throw_NULL( saveData, eMemoryError );
memcpy(saveData, dataBuf, binLen+1);
pContinue->fData = saveData;
pContinue->fDataLen = binLen;
num2Ptr = strchr( dataBuf, ' ' );
if ( binLen < 3 || num2Ptr == NULL )
throw( eDSInvalidBuffFormat );
sscanf(dataBuf, "%lu", &num1);
sscanf(num2Ptr+1, "%lu", &num2);
outAuthBuff->fBufferLength = 8;
memcpy(outAuthBuff->fBufferData, &num1, sizeof(long));
memcpy(outAuthBuff->fBufferData + sizeof(long), &num2, sizeof(long));
siResult = eDSNoErr;
}
else
{
data = NULL;
len = 0;
r = SASL_OK;
}
}
else
{
r = SASL_FAIL;
}
}
else
if ( pContinue->fAuthPass == 1 )
{
DEBUGLOG( "inAuthBuff->fBufferLength=%lu\n", inAuthBuff->fBufferLength);
if ( inAuthBuff->fBufferLength < 16 )
throw( (sInt32)eDSAuthInBuffFormatError );
if ( inContext->last.password != NULL )
{
memset( inContext->last.password, 0, inContext->last.passwordLen );
free( inContext->last.password );
inContext->last.password = NULL;
inContext->last.passwordLen = 0;
}
inContext->last.password = (char *) malloc( inAuthBuff->fBufferLength );
Throw_NULL( inContext->last.password, eMemoryError );
memcpy( inContext->last.password, inAuthBuff->fBufferData, inAuthBuff->fBufferLength );
inContext->last.passwordLen = inAuthBuff->fBufferLength;
r = sasl_client_start( inContext->conn, inMechName, NULL, &data, &len, &chosenmech );
DEBUGLOG( "chosenmech=%s, datalen=%u\n", chosenmech, len);
if ( r != SASL_OK && r != SASL_CONTINUE ) {
DEBUGLOG( "starting SASL negotiation, err=%d\n", r);
throw( SASLErrToDirServiceError(r) );
}
r = sasl_client_step(inContext->conn, (const char *)pContinue->fData, pContinue->fDataLen, NULL, &data, &len);
if ( pContinue->fData != NULL ) {
free( pContinue->fData );
pContinue->fData = NULL;
}
pContinue->fDataLen = 0;
if ( r != SASL_OK && r != SASL_CONTINUE ) {
DEBUGLOG( "stepping SASL negotiation, err=%d\n", r);
throw( SASLErrToDirServiceError(r) );
}
if (data && len != 0)
{
ConvertBinaryToHex( (const unsigned char *)data, len, dataBuf );
DEBUGLOG( "AUTH2 %s\r\n", dataBuf);
fprintf(inContext->serverOut, "AUTH2 %s\r\n", dataBuf );
fflush(inContext->serverOut);
serverResult = readFromServer(inContext->serverIn, buf, sizeof(buf));
if (serverResult.err != 0) {
DEBUGLOG( "server returned an error, err=%d\n", serverResult.err);
throw( PWSErrToDirServiceError(serverResult) );
}
chop(buf);
len = strlen(buf);
if ( len > 4 )
{
unsigned long binLen;
ConvertHexToBinary( buf + 4, (unsigned char *)dataBuf, &binLen );
if ( binLen > outAuthBuff->fBufferSize )
throw( (sInt32)eDSAuthResponseBufTooSmall );
outAuthBuff->fBufferLength = binLen;
memcpy(outAuthBuff->fBufferData, dataBuf, binLen);
siResult = eDSNoErr;
}
}
}
else
{
siResult = eDSAuthFailed;
}
}
catch ( sInt32 err )
{
DEBUGLOG( "PasswordServer PlugIn: SASL authentication error %l", err );
siResult = err;
}
catch ( ... )
{
DEBUGLOG( "PasswordServer PlugIn: SASL uncasted authentication error" );
siResult = eDSAuthFailed;
}
if ( pContinue->fAuthPass == 1 )
{
gContinue->RemoveItem( pContinue );
inData->fIOContinueData = NULL;
}
else
{
pContinue->fAuthPass++;
}
return( siResult );
}
sInt32 CPSPlugIn::DoPlugInCustomCall ( sDoPlugInCustomCall *inData )
{
sInt32 siResult = eDSNoErr;
return( siResult );
}
void CPSPlugIn::ContinueDeallocProc ( void* inContinueData )
{
sPSContinueData *pContinue = (sPSContinueData *)inContinueData;
if ( pContinue != nil )
{
if ( pContinue->fData != NULL )
{
free( pContinue->fData );
pContinue->fData = NULL;
}
free( pContinue );
pContinue = nil;
}
}
void CPSPlugIn::ContextDeallocProc ( void* inContextData )
{
sPSContextData *pContext = (sPSContextData *) inContextData;
if ( pContext != NULL )
{
CleanContextData( pContext );
free( pContext );
pContext = NULL;
}
}