#include "AppleOD.h"
#include <pwd.h>
#include <grp.h>
#include <ctype.h>
#include <string.h>
#include <stdlib.h>
#include <syslog.h>
#include <stdio.h>
#include <unistd.h>
#include <membershipPriv.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/sysctl.h>
#include <sys/fcntl.h>
#include <uuid/uuid.h>
#include <sys/errno.h>
#include "imap_err.h"
#include "xmalloc.h"
#include "libconfig.h"
#include <CoreFoundation/CFData.h>
#include <CoreFoundation/CFString.h>
#include <CoreFoundation/CFNumber.h>
#include <CoreFoundation/CFPropertyList.h>
#include <DirectoryService/DirServices.h>
#include <DirectoryService/DirServicesUtils.h>
#include <DirectoryService/DirServicesConst.h>
#include <Security/SecKeychain.h>
#include <Security/SecKeychainItem.h>
static tDirStatus sOpen_ds ( tDirReference *inOutDirRef );
static tDirStatus sGet_search_node ( tDirReference inDirRef, tDirNodeReference *outSearchNodeRef );
static tDirStatus sGet_user_attributes( tDirReference inDirRef, tDirNodeReference inSearchNodeRef, const char *inUserID, struct od_user_opts *inOutOpts );
static tDirStatus sOpen_user_node ( tDirReference inDirRef, const char *inUserLoc, tDirNodeReference *outUserNodeRef );
static int sVerify_version ( CFDictionaryRef inCFDictRef );
static void sGet_mail_values ( char *inMailAttribute, struct od_user_opts *inOutOpts );
static void sGet_acct_state ( CFDictionaryRef inCFDictRef, struct od_user_opts *inOutOpts );
static void sGet_auto_forward ( CFDictionaryRef inCFDictRef, struct od_user_opts *inOutOpts );
static void sGet_IMAP_login ( CFDictionaryRef inCFDictRef, struct od_user_opts *inOutOpts );
static void sGet_POP3_login ( CFDictionaryRef inCFDictRef, struct od_user_opts *inOutOpts );
static void sGet_disk_quota ( CFDictionaryRef inCFDictRef, struct od_user_opts *inOutOpts );
static void sGet_acct_loc ( CFDictionaryRef inCFDictRef, struct od_user_opts *inOutOpts );
static void sGet_alt_loc ( CFDictionaryRef inCFDictRef, struct od_user_opts *inOutOpts );
static int sDoCryptAuth ( tDirReference inDirRef, tDirNodeReference inUserNodeRef, const char *inUserID, const char *inPasswd );
static int sValidateResponse ( const char *inChallenge, const char *inResponse, const char *inAuthType, struct od_user_opts *inOutOpts );
static int sGetUserOptions ( const char *inUserID, struct od_user_opts *inOutOpts );
static int sEncodeBase64 ( const char *inStr, const int inLen, char *outStr, int outLen );
static int sDecodeBase64 ( const char *inStr, const int inLen, char *outStr, int outLen, int *destLen );
static void get_random_chars ( char *out_buf, int in_len );
static int checkServiceACL ( struct od_user_opts *inOutOpts, const char *inGroup );
static int sDoCRAM_MD5_Auth ( struct protstream *inStreamIn, struct protstream *inStreamOut, struct od_user_opts *inOutOpts );
static int sDoCRAM_MD5_AuthS ( struct protstream *inStreamIn, struct protstream *inStreamOut, struct od_user_opts *inOutOpts );
static int sDoPlainAuth ( const char *inCont, const char *inResp, struct protstream *inStreamIn, struct protstream *inStreamOut, struct od_user_opts *inOutOpts );
static int sDoPlainAuthS ( const char *inCont, const char *inResp, struct protstream *inStreamIn, struct protstream *inStreamOut, struct od_user_opts *inOutOpts );
static int sDoLoginAuth ( struct protstream *inStreamIn, struct protstream *inStreamOut, struct od_user_opts *inOutOpts );
static int sDoLoginAuthS ( struct protstream *inStreamIn, struct protstream *inStreamOut, struct od_user_opts *inOutOpts );
static int sGetClientResponse ( char *inOutBuf, int inBufSize, struct protstream *inStreamIn );
int odGetUserOpts ( const char *inUserID, struct od_user_opts *inOutOpts )
{
int iResult = 0;
if ( inOutOpts != NULL )
{
if ( (inUserID != NULL) && (inOutOpts->fUserIDPtr != NULL) )
{
if ( strcasecmp( inUserID, inOutOpts->fUserIDPtr ) == 0 )
{
if ( (inOutOpts->fRecNamePtr != NULL) && (inOutOpts->fUserLocPtr) )
{
syslog( LOG_DEBUG, "AOD: user opts: no-lookup for: %s", inUserID );
return( eDSNoErr );
}
}
}
odFreeUserOpts( inOutOpts, 0 );
inOutOpts->fAccountState = eUnknownState;
inOutOpts->fDiskQuota = 0;
iResult = sGetUserOptions( inUserID, inOutOpts );
if ( (iResult == eDSNoErr) && (inOutOpts->fRecNamePtr != NULL) )
{
inOutOpts->fUserIDPtr = malloc( strlen( inUserID ) + 1 );
if ( inOutOpts->fUserIDPtr != NULL )
{
strlcpy( inOutOpts->fUserIDPtr, inUserID, strlen( inUserID ) + 1 );
}
checkServiceACL( inOutOpts, "mail" );
}
if ( inOutOpts->fRecNamePtr == NULL )
{
if ( inUserID != NULL )
{
inOutOpts->fRecNamePtr = malloc( strlen( inUserID ) + 1 );
if ( inOutOpts->fRecNamePtr != NULL )
{
strlcpy( inOutOpts->fRecNamePtr, inUserID, strlen( inUserID ) + 1 );
}
}
inOutOpts->fAccountState |= eUnknownUser;
}
}
return( iResult );
}
void odFreeUserOpts ( struct od_user_opts *inUserOpts, int inFreeOD )
{
if ( inUserOpts != NULL )
{
syslog( LOG_DEBUG, "AOD: user opts: cleaning up user options structure" );
inUserOpts->fAccountState = eUnknownState;
if ( inUserOpts->fUserIDPtr != NULL )
{
free( inUserOpts->fUserIDPtr );
inUserOpts->fUserIDPtr = NULL;
}
if ( inUserOpts->fUserUUID != NULL )
{
free( inUserOpts->fUserUUID );
inUserOpts->fUserUUID = NULL;
}
if ( inUserOpts->fAuthIDNamePtr != NULL )
{
free( inUserOpts->fAuthIDNamePtr );
inUserOpts->fAuthIDNamePtr = NULL;
}
if ( inUserOpts->fRecNamePtr != NULL )
{
free( inUserOpts->fRecNamePtr );
inUserOpts->fRecNamePtr = NULL;
}
if ( inUserOpts->fAccountLocPtr != NULL )
{
free( inUserOpts->fAccountLocPtr );
inUserOpts->fAccountLocPtr = NULL;
}
if ( inUserOpts->fAltDataLocPtr != NULL )
{
free( inUserOpts->fAltDataLocPtr );
inUserOpts->fAltDataLocPtr = NULL;
}
if ( inUserOpts->fAutoFwdPtr != NULL )
{
free( inUserOpts->fAutoFwdPtr );
inUserOpts->fAutoFwdPtr = NULL;
}
if ( inUserOpts->fUserLocPtr != NULL )
{
free( inUserOpts->fUserLocPtr );
inUserOpts->fUserLocPtr = NULL;
}
if ( inFreeOD )
{
if ( inUserOpts->fSearchNodeRef )
{
syslog( LOG_DEBUG, "AOD: user opts: releasing search node reference" );
(void)dsCloseDirNode( inUserOpts->fSearchNodeRef );
inUserOpts->fSearchNodeRef = 0;
}
if ( inUserOpts->fDirRef )
{
syslog( LOG_DEBUG, "AOD: user opts: releasing directory reference" );
(void)dsCloseDirService( inUserOpts->fDirRef );
inUserOpts->fDirRef = 0;
}
}
}
}
int odCheckPass ( const char *inPasswd, struct od_user_opts *inUserOpts )
{
int iResult = eAODParamErr;
tDirStatus dsStatus = eDSNoErr;
tDirReference dirRef = 0;
tDirNodeReference userNodeRef = 0;
if ( (inUserOpts == NULL) || (inUserOpts->fRecNamePtr == NULL) || (inPasswd == NULL) )
{
syslog( LOG_ERR, "AOD: check pass: configuration error" );
return( eAODParamErr );
}
dirRef = inUserOpts->fDirRef;
if ( (inUserOpts != NULL) && (inUserOpts->fUserLocPtr != NULL) )
{
dsStatus = sOpen_user_node( dirRef, inUserOpts->fUserLocPtr, &userNodeRef );
if ( dsStatus == eDSNoErr )
{
dsStatus = sDoCryptAuth( dirRef, userNodeRef, inUserOpts->fRecNamePtr, inPasswd );
switch ( dsStatus )
{
case eDSNoErr:
iResult = eAODNoErr;
break;
case eDSAuthNewPasswordRequired:
syslog( LOG_INFO, "AOD: check pass: new password required for user: %s", inUserOpts->fRecNamePtr );
iResult = eAODNoErr;
break;
case eDSAuthPasswordExpired:
syslog( LOG_INFO, "AOD: check pass: password expired for user: %s", inUserOpts->fRecNamePtr );
iResult = eAODNoErr;
break;
default:
syslog( LOG_INFO, "AOD: check pass: %d", dsStatus );
iResult = eAODAuthFailed;
break;
}
(void)dsCloseDirNode( userNodeRef );
}
else
{
syslog( LOG_ERR, "AOD: check pass: cannot open user directory node for user: %s (%d)", inUserOpts->fRecNamePtr, dsStatus );
iResult = eAODCantOpenUserNode;
}
}
return( iResult );
}
int odCheckAPOP ( const char *inChallenge, const char *inResponse, struct od_user_opts *inOutOpts )
{
int iResult = eAODParamErr;
char *p = NULL;
int len = 0;
char userBuf [ MAX_USER_BUF_SIZE ];
if ( (inChallenge == NULL) || (inResponse == NULL) )
{
syslog( LOG_DEBUG, "AOD: APOP: configuration error: empty challenger or response" );
return( iResult );
}
p = strchr( inResponse, ' ' );
if ( (p == NULL) || (strspn(p + 1, "0123456789abcdef") != 32) )
{
syslog( LOG_DEBUG, "AOD: APOP: parameter error: bad digest: %s", inResponse );
return( eAODParamErr );
}
len = (int)(p - inResponse);
if ( len < MAX_USER_BUF_SIZE )
{
memset( userBuf, 0, MAX_USER_BUF_SIZE );
memcpy( userBuf, inResponse, len );
odGetUserOpts( userBuf, inOutOpts );
while ( *p == ' ' )
{
p++;
}
if ( p != NULL )
{
return( sValidateResponse( inChallenge, p, kDSStdAuthAPOP, inOutOpts ) );
}
else
{
syslog( LOG_DEBUG, "AOD: APOP: configuration error: bad APOP digest: (%s)", inResponse );
iResult = eAODConfigError;
}
}
else
{
syslog( LOG_DEBUG, "AOD: APOP: username exceeded maximum limit: (%s)", inResponse );
iResult = eAODAllocError;
}
return( iResult );
}
int odCRAM_MD5 ( const char *inChallenge, const char *inResponse, struct od_user_opts *inOutOpts )
{
return( sValidateResponse( inChallenge, inResponse, kDSStdAuthCRAM_MD5, inOutOpts ) );
}
int odDoAuthenticate ( const char *inMethod,
const char *inInitialResp,
const char *inCont,
const char *inProtocol,
struct protstream *inStreamIn,
struct protstream *inStreamOut,
struct od_user_opts *inOutOpts )
{
int result = eAODNoErr;
if ( strcasecmp( inMethod, "CRAM-MD5" ) == 0 )
{
result = sDoCRAM_MD5_Auth( inStreamIn, inStreamOut, inOutOpts );
}
else if ( strcasecmp( inMethod, "LOGIN" ) == 0 )
{
result = sDoLoginAuth( inStreamIn, inStreamOut, inOutOpts );
}
else if ( strcasecmp( inMethod, "PLAIN" ) == 0 )
{
result = sDoPlainAuth( inCont, inInitialResp, inStreamIn, inStreamOut, inOutOpts );
}
else if ( strcasecmp( inMethod, "SIEVE-CRAM-MD5" ) == 0 )
{
result = sDoCRAM_MD5_AuthS( inStreamIn, inStreamOut, inOutOpts );
}
else if ( strcasecmp( inMethod, "SIEVE-LOGIN" ) == 0 )
{
result = sDoLoginAuthS( inStreamIn, inStreamOut, inOutOpts );
}
else if ( strcasecmp( inMethod, "SIEVE-PLAIN" ) == 0 )
{
result = sDoPlainAuthS( inCont, inInitialResp, inStreamIn, inStreamOut, inOutOpts );
}
else
{
result = eAODProtocolError;
}
return ( result );
}
int odIsMember ( const char *inUser, const char *inGroup )
{
int isMember = 0;
uuid_t userID;
uuid_t groupID;
if ( (inUser == NULL) || (inGroup == NULL) )
{
return( 0 );
}
if ( mbr_user_name_to_uuid( inUser, userID ) != 0 )
{
return( 0 );
}
if ( mbr_group_name_to_uuid( inGroup, groupID ) != 0 )
{
return( 0 );
}
if ( mbr_check_membership( userID, groupID, &isMember ) != 0 )
{
return( 0 );
}
return( isMember );
}
int sDoLoginAuth ( struct protstream *inStreamIn,
struct protstream *inStreamOut,
struct od_user_opts *inOutOpts )
{
int iResult = eAODNoErr;
int len = 0;
int respLen = 0;
char *p = NULL;
char userBuf [ MAX_USER_BUF_SIZE ];
char chalBuf [ MAX_CHAL_BUF_SIZE ];
char pwdBuf [ MAX_USER_BUF_SIZE ];
char ioBuf [ MAX_IO_BUF_SIZE ];
if ( !config_getswitch( IMAPOPT_IMAP_AUTH_LOGIN ) )
{
syslog( LOG_DEBUG, "AOD: LOGIN: configuration error: LOGIN authentication not enabled" );
return ( eAODMethodNotEnabled );
}
strcpy( chalBuf, "Username:" );
sEncodeBase64( chalBuf, strlen( chalBuf ), ioBuf, MAX_IO_BUF_SIZE );
prot_printf( inStreamOut, "+ %s\r\n", ioBuf );
memset( ioBuf, 0, MAX_IO_BUF_SIZE );
if ( prot_fgets( ioBuf, MAX_IO_BUF_SIZE, inStreamIn ) )
{
p = ioBuf + strlen( ioBuf ) - 1;
if ( (p >= ioBuf) && (*p == '\n') )
{
*p-- = '\0';
}
if ( (p >= ioBuf) && (*p == '\r') )
{
*p-- = '\0';
}
if ( ioBuf[ 0 ] == '*' )
{
syslog( LOG_DEBUG, "AOD: LOGIN: user canceled auth attempt" );
return( eAODAuthCanceled );
}
}
memset( userBuf, 0, MAX_USER_BUF_SIZE );
len = strlen( ioBuf );
sDecodeBase64( ioBuf, len, userBuf, MAX_USER_BUF_SIZE, &respLen );
odGetUserOpts( userBuf, inOutOpts );
strcpy( chalBuf, "Password:" );
sEncodeBase64( chalBuf, strlen( chalBuf ), ioBuf, MAX_IO_BUF_SIZE );
prot_printf( inStreamOut, "+ %s\r\n", ioBuf );
if ( prot_fgets( ioBuf, MAX_IO_BUF_SIZE, inStreamIn ) )
{
p = ioBuf + strlen( ioBuf ) - 1;
if ( (p >= ioBuf) && (*p == '\n') )
{
*p-- = '\0';
}
if ( (p >= ioBuf) && (*p == '\r') )
{
*p-- = '\0';
}
if ( ioBuf[ 0 ] == '*' )
{
syslog( LOG_DEBUG, "AOD: LOGIN: user canceled auth attempt" );
return( eAODAuthCanceled );
}
}
memset( pwdBuf, 0, MAX_USER_BUF_SIZE );
len = strlen( ioBuf );
sDecodeBase64( ioBuf, len, pwdBuf, MAX_USER_BUF_SIZE, &respLen );
iResult = odCheckPass( pwdBuf, inOutOpts );
memset( pwdBuf, 0, MAX_USER_BUF_SIZE );
memset( ioBuf, 0, MAX_IO_BUF_SIZE );
return( iResult );
}
int sDoLoginAuthS ( struct protstream *inStreamIn,
struct protstream *inStreamOut,
struct od_user_opts *inOutOpts )
{
int iResult = eAODNoErr;
int len = 0;
int respLen = 0;
char userBuf [ MAX_USER_BUF_SIZE ];
char chalBuf [ MAX_CHAL_BUF_SIZE ];
char pwdBuf [ MAX_USER_BUF_SIZE ];
char ioBuf [ MAX_IO_BUF_SIZE ];
if ( !config_getswitch( IMAPOPT_IMAP_AUTH_LOGIN ) )
{
syslog( LOG_DEBUG, "AOD: LOGIN: configuration error: LOGIN authentication not enabled" );
return ( eAODMethodNotEnabled );
}
strcpy( chalBuf, "Username:" );
sEncodeBase64( chalBuf, strlen( chalBuf ), ioBuf, MAX_IO_BUF_SIZE );
prot_printf( inStreamOut, "{%d}\r\n", (int)strlen( ioBuf ) );
prot_printf( inStreamOut, "%s\r\n", ioBuf );
iResult = sGetClientResponse( ioBuf, MAX_IO_BUF_SIZE, inStreamIn );
if ( iResult != eAODNoErr )
{
return( iResult );
}
memset( userBuf, 0, MAX_USER_BUF_SIZE );
len = strlen( ioBuf );
sDecodeBase64( ioBuf, len, userBuf, MAX_USER_BUF_SIZE, &respLen );
odGetUserOpts( userBuf, inOutOpts );
strcpy( chalBuf, "Password:" );
sEncodeBase64( chalBuf, strlen( chalBuf ), ioBuf, MAX_IO_BUF_SIZE );
prot_printf( inStreamOut, "{%d}\r\n", (int)strlen( ioBuf ) );
prot_printf( inStreamOut, "%s\r\n", ioBuf );
iResult = sGetClientResponse( ioBuf, MAX_IO_BUF_SIZE, inStreamIn );
if ( iResult != eAODNoErr )
{
return( iResult );
}
memset( pwdBuf, 0, MAX_USER_BUF_SIZE );
len = strlen( ioBuf );
sDecodeBase64( ioBuf, len, pwdBuf, MAX_USER_BUF_SIZE, &respLen );
iResult = odCheckPass( pwdBuf, inOutOpts );
memset( pwdBuf, 0, MAX_USER_BUF_SIZE );
memset( ioBuf, 0, MAX_IO_BUF_SIZE );
return( iResult );
}
int sDoPlainAuth ( const char *inCont,
const char *inResp,
struct protstream *inStreamIn,
struct protstream *inStreamOut,
struct od_user_opts *inOutOpts )
{
int iResult = eAODProtocolError;
int len = 0;
int respLen = 0;
char *p = NULL;
char ioBuf [ MAX_IO_BUF_SIZE ];
char respBuf [ MAX_IO_BUF_SIZE ];
char userBuf [ MAX_USER_BUF_SIZE ];
char authName[ MAX_USER_BUF_SIZE ];
char passwd [ MAX_USER_BUF_SIZE ];
if ( !config_getswitch( IMAPOPT_IMAP_AUTH_PLAIN ) )
{
syslog( LOG_DEBUG, "AOD: PLAIN: configuration error: PLAIN authentication not enabled" );
return ( eAODMethodNotEnabled );
}
if ( inResp != NULL )
{
strlcpy( ioBuf, inResp, MAX_IO_BUF_SIZE );
}
else
{
prot_printf( inStreamOut, "%s\r\n", inCont );
if ( prot_fgets( ioBuf, MAX_IO_BUF_SIZE, inStreamIn ) )
{
p = ioBuf + strlen( ioBuf ) - 1;
if ( (p >= ioBuf) && (*p == '\n') )
{
*p-- = '\0';
}
if ( (p >= ioBuf) && (*p == '\r') )
{
*p-- = '\0';
}
if ( ioBuf[ 0 ] == '*' )
{
syslog( LOG_DEBUG, "AOD: PLAIN: user canceled auth attempt" );
return( eAODAuthCanceled );
}
}
}
memset( respBuf, 0, MAX_IO_BUF_SIZE );
len = strlen( ioBuf );
sDecodeBase64( ioBuf, len, respBuf, MAX_IO_BUF_SIZE, &respLen );
p = respBuf;
if ( *p != '\0' )
{
strlcpy( authName, p, MAX_USER_BUF_SIZE );
p = p + (strlen( authName ) + 1 );
}
else
{
p++;
}
if ( (p != NULL) && (strlen( p ) < MAX_USER_BUF_SIZE) )
{
strlcpy( userBuf, p, MAX_USER_BUF_SIZE );
odGetUserOpts( userBuf, inOutOpts );
p = p + (strlen( userBuf ) + 1 );
if ( (p != NULL) && (strlen( p ) < MAX_USER_BUF_SIZE) )
{
strlcpy( passwd, p, MAX_USER_BUF_SIZE );
iResult = odCheckPass( passwd, inOutOpts );
}
}
memset( passwd, 0, MAX_USER_BUF_SIZE );
memset( respBuf, 0, MAX_IO_BUF_SIZE );
return( iResult );
}
int sDoPlainAuthS ( const char *inCont,
const char *inResp,
struct protstream *inStreamIn,
struct protstream *inStreamOut,
struct od_user_opts *inOutOpts )
{
int iResult = eAODProtocolError;
int len = 0;
int respLen = 0;
char *p = NULL;
char *bufPtr = NULL;
char *bufEnd = NULL;
char ioBuf [ MAX_IO_BUF_SIZE ];
char respBuf [ MAX_IO_BUF_SIZE ];
char authBuf [ MAX_USER_BUF_SIZE ]; char userBuf [ MAX_USER_BUF_SIZE ]; char passwd [ MAX_USER_BUF_SIZE ];
if ( !config_getswitch( IMAPOPT_IMAP_AUTH_PLAIN ) )
{
syslog( LOG_DEBUG, "AOD: SIEVE-PLAIN: configuration error: PLAIN authentication not enabled" );
return ( eAODMethodNotEnabled );
}
memset( ioBuf, 0, MAX_IO_BUF_SIZE );
if ( inResp != NULL )
{
if ( *inResp == '{' )
{
p = (char *)inResp;
p++;
while ( isdigit( (int)(*p) ) )
{
len = len * 10 + (*p - '0');
p++;
}
if ( *p++ == '+' )
{
if ( *p == '}' )
{
if ( (len <= 0) || (len > MAX_IO_BUF_SIZE) )
{
syslog( LOG_DEBUG, "AOD: SIEVE-PLAIN: out of bounds length. len: %d. buf size: %d", len, MAX_IO_BUF_SIZE );
return( eAODInvalidDataType );
}
bufPtr = ioBuf;
bufEnd = bufPtr + len;
while ( bufPtr < bufEnd )
{
*bufPtr++ = prot_getc( inStreamIn );
}
}
else
{
syslog( LOG_DEBUG, "AOD: SIEVE-PLAIN: bad sequence in {n+}: missing '}'");
return( eAODInvalidDataType );
}
}
else
{
syslog( LOG_DEBUG, "AOD: SIEVE-PLAIN: bad sequence in {n+}: missing '+'");
return( eAODInvalidDataType );
}
}
else
{
strlcpy( ioBuf, inResp, MAX_IO_BUF_SIZE );
}
}
else
{
prot_printf( inStreamOut, "%s\r\n", inCont );
if ( prot_fgets( ioBuf, MAX_IO_BUF_SIZE, inStreamIn ) )
{
p = ioBuf + strlen( ioBuf ) - 1;
if ( (p >= ioBuf) && (*p == '\n') )
{
*p-- = '\0';
}
if ( (p >= ioBuf) && (*p == '\r') )
{
*p-- = '\0';
}
if ( ioBuf[ 0 ] == '*' )
{
syslog( LOG_DEBUG, "AOD: SIEVE-PLAIN: user canceled auth attempt" );
return( eAODAuthCanceled );
}
}
}
memset( respBuf, 0, MAX_IO_BUF_SIZE );
memset( userBuf, 0, MAX_USER_BUF_SIZE );
len = strlen( ioBuf );
sDecodeBase64( ioBuf, len, respBuf, MAX_IO_BUF_SIZE, &respLen );
p = respBuf;
if ( *p != '\0' )
{
strlcpy( userBuf, p, MAX_USER_BUF_SIZE );
p = p + (strlen( userBuf ) + 1 );
}
else
{
p++;
}
if ( (p != NULL) && (strlen( p ) < MAX_USER_BUF_SIZE) )
{
strlcpy( authBuf, p, MAX_USER_BUF_SIZE );
odGetUserOpts( authBuf, inOutOpts );
p = p + (strlen( authBuf ) + 1 );
if ( (p != NULL) && (strlen( p ) < MAX_USER_BUF_SIZE) )
{
strlcpy( passwd, p, MAX_USER_BUF_SIZE );
iResult = odCheckPass( passwd, inOutOpts );
if ( (iResult == eAODNoErr) && (strlen( userBuf ) != 0) && (strcasecmp( userBuf, authBuf ) != 0) )
{
syslog( LOG_DEBUG, "AOD: SIEVE-PLAIN: authorizing user: %s, by: %s", userBuf, authBuf );
odGetUserOpts( userBuf, inOutOpts );
if ( inOutOpts->fAuthIDNamePtr != NULL )
{
free( inOutOpts->fAuthIDNamePtr );
inOutOpts->fAuthIDNamePtr = NULL;
}
inOutOpts->fAuthIDNamePtr = malloc( strlen( authBuf ) + 1 );
if ( inOutOpts->fAuthIDNamePtr != NULL )
{
strlcpy( inOutOpts->fAuthIDNamePtr, authBuf, strlen( authBuf ) + 1 );
}
else
{
iResult = eAODAllocError;
}
}
}
}
memset( passwd, 0, MAX_USER_BUF_SIZE );
memset( respBuf, 0, MAX_IO_BUF_SIZE );
return( iResult );
}
int sDoCRAM_MD5_AuthS ( struct protstream *inStreamIn,
struct protstream *inStreamOut,
struct od_user_opts *inOutOpts )
{
int iResult = eAODAuthFailed;
int len = 0;
int respLen = 0;
char *p = NULL;
char chalBuf [ MAX_CHAL_BUF_SIZE ];
char userBuf [ MAX_USER_BUF_SIZE ];
char respBuf [ MAX_IO_BUF_SIZE ];
char ioBuf [ MAX_IO_BUF_SIZE ];
char hostname[ MAXHOSTNAMELEN + 1 ];
struct timeval tvChalTime;
char randbuf[ 17 ];
if ( !config_getswitch( IMAPOPT_IMAP_AUTH_CRAM_MD5 ) )
{
syslog( LOG_DEBUG, "AOD: CRAM-MD5: configuration error: CRAM-MD5 authentication not enabled" );
return ( eAODMethodNotEnabled );
}
gethostname( hostname, sizeof( hostname ) );
gettimeofday (&tvChalTime, NULL);
get_random_chars( randbuf, 17 );
sprintf( chalBuf, "<%lu.%s.%lu@%s>",
(unsigned long) getpid(),
randbuf,
(unsigned long)time(0),
hostname );
sEncodeBase64( chalBuf, strlen( chalBuf ), ioBuf, MAX_IO_BUF_SIZE );
syslog( LOG_DEBUG, "AOD: SIEVE-CRAM-MD5: challenge: %s", ioBuf );
syslog( LOG_DEBUG, "AOD: SIEVE-CRAM-MD5: challenge: %s", chalBuf );
prot_printf( inStreamOut, "{%d}\r\n", (int)strlen( ioBuf ) );
prot_printf( inStreamOut, "%s\r\n", ioBuf );
iResult = sGetClientResponse( ioBuf, MAX_IO_BUF_SIZE, inStreamIn );
if ( iResult != eAODNoErr )
{
return( iResult );
}
memset( respBuf, 0, MAX_IO_BUF_SIZE );
len = strlen( ioBuf );
if ( len == 0 )
{
syslog( LOG_ERR, "AOD: CRAM-MD5: Zero length response" );
return( eAODParamErr );
}
sDecodeBase64( ioBuf, len, respBuf, MAX_IO_BUF_SIZE, &respLen );
p = strchr( respBuf, ' ' );
if ( (p == NULL) || (strspn(p + 1, "0123456789abcdef") != 32) )
{
syslog( LOG_ERR, "AOD: CRAM-MD5: parameter error: bad digest: %s", respBuf );
return( eAODParamErr );
}
len = p - respBuf;
if ( len > (MAX_USER_BUF_SIZE - 1) )
{
syslog( LOG_ERR, "AOD: CRAM-MD5: username exceeded maximum limit: (%s)", respBuf );
return( eAODParamErr );
}
memset( userBuf, 0, MAX_USER_BUF_SIZE );
memcpy( userBuf, respBuf, len );
syslog( LOG_DEBUG, "AOD: SIEVE-CRAM-MD5: user name: %s", userBuf );
odGetUserOpts( userBuf, inOutOpts );
if ( ++p != NULL )
{
iResult = odCRAM_MD5( chalBuf, p, inOutOpts );
}
else
{
syslog( LOG_ERR, "AOD: CRAM-MD5: configuration error: bad CRAM-MD5 digest: %s", respBuf );
iResult = eAODConfigError;
}
return( iResult );
}
int sDoCRAM_MD5_Auth ( struct protstream *inStreamIn,
struct protstream *inStreamOut,
struct od_user_opts *inOutOpts )
{
int iResult = eAODAuthFailed;
int len = 0;
int respLen = 0;
char *p = NULL;
char chalBuf [ MAX_CHAL_BUF_SIZE ];
char userBuf [ MAX_USER_BUF_SIZE ];
char respBuf [ MAX_IO_BUF_SIZE ];
char ioBuf [ MAX_IO_BUF_SIZE ];
char hostname[ MAXHOSTNAMELEN + 1 ];
struct timeval tvChalTime;
char randbuf[ 17 ];
if ( !config_getswitch( IMAPOPT_IMAP_AUTH_CRAM_MD5 ) )
{
syslog( LOG_DEBUG, "AOD: CRAM-MD5: configuration error: CRAM-MD5 authentication not enabled" );
return ( eAODMethodNotEnabled );
}
gethostname( hostname, sizeof( hostname ) );
gettimeofday (&tvChalTime, NULL);
get_random_chars( randbuf, 17 );
sprintf( chalBuf, "<%lu.%s.%lu@%s>",
(unsigned long) getpid(),
randbuf,
(unsigned long)time(0),
hostname );
sEncodeBase64( chalBuf, strlen( chalBuf ), ioBuf, MAX_IO_BUF_SIZE );
prot_printf( inStreamOut, "+ %s\r\n", ioBuf );
memset( ioBuf, 0, MAX_IO_BUF_SIZE );
if ( prot_fgets( ioBuf, MAX_IO_BUF_SIZE, inStreamIn ) )
{
p = ioBuf + strlen( ioBuf ) - 1;
if ( (p >= ioBuf) && (*p == '\n') )
{
*p-- = '\0';
}
if ( (p >= ioBuf) && (*p == '\r') )
{
*p-- = '\0';
}
if ( ioBuf[ 0 ] == '*' )
{
syslog( LOG_DEBUG, "AOD: CRAM-MD5: user canceled auth attempt" );
return( eAODAuthCanceled );
}
}
memset( respBuf, 0, MAX_IO_BUF_SIZE );
len = strlen( ioBuf );
if ( len == 0 )
{
syslog( LOG_DEBUG, "AOD: CRAM-MD5: Zero length response" );
return( eAODParamErr );
}
sDecodeBase64( ioBuf, len, respBuf, MAX_IO_BUF_SIZE, &respLen );
p = strchr( respBuf, ' ' );
if ( (p == NULL) || (strspn(p + 1, "0123456789abcdef") != 32) )
{
syslog( LOG_DEBUG, "AOD: CRAM-MD5: parameter error: bad digest: %s", respBuf );
return( eAODParamErr );
}
len = p - respBuf;
if ( len > (MAX_USER_BUF_SIZE - 1) )
{
syslog( LOG_ERR, "AOD: CRAM-MD5: username exceeded maximum limit: (%s)", respBuf );
return( eAODParamErr );
}
memset( userBuf, 0, MAX_USER_BUF_SIZE );
memcpy( userBuf, respBuf, len );
odGetUserOpts( userBuf, inOutOpts );
if ( ++p != NULL )
{
iResult = odCRAM_MD5( chalBuf, p, inOutOpts );
}
else
{
syslog( LOG_DEBUG, "AOD: CRAM-MD5: configuration error: bad CRAM-MD5 digest: %s", respBuf );
iResult = eAODConfigError;
}
return( iResult );
}
int sGetClientResponse ( char *inOutBuf, int inBufSize, struct protstream *inStreamIn )
{
int c = '\0';
int len = 0;
char *bufPtr = inOutBuf;
char *bufEnd = inOutBuf + inBufSize;
if ( inOutBuf == NULL )
{
return( eAODNullbuffer );
}
memset( inOutBuf, 0, inBufSize );
c = prot_getc( inStreamIn );
if ( c == EOF )
{
return( eAODLostConnection );
}
if ( c == '\"' )
{
while ( true )
{
c = prot_getc( inStreamIn );
if ( c == EOF )
{
return( eAODLostConnection );
}
if ( c == '\"' )
{
return( eAODNoErr );
}
if ( (c == '\0') || (c == '\r') ||
(c == '\n') || (0x7F < ((unsigned char)c)) )
{
syslog( LOG_DEBUG, "AOD: GetClientResponse: illegal character: %c", c );
return( eAODInvalidDataType );
}
if ( c == '\\' )
{
c = prot_getc( inStreamIn );
if ( (c != '\"') && (c != '\\') )
{
syslog( LOG_DEBUG, "AOD: GetClientResponse: illegal escape character: %c", c );
return( eAODInvalidDataType );
}
}
if ( bufPtr > bufEnd )
{
syslog( LOG_DEBUG, "AOD: GetClientResponse: exceeded buffer" );
return( eAODInvalidDataType );
}
*bufPtr++ = c;
}
}
else if ( c == '{' )
{
while ( true )
{
c = prot_getc( inStreamIn );
if ( c == EOF )
{
return( eAODLostConnection );
}
if ( isdigit( (int) c ) )
{
len = len * 10 + (c - '0');
}
else if ( c == '+' )
{
c = prot_getc( inStreamIn );
if ( c == EOF )
{
return( eAODLostConnection );
}
if ( c == '}' )
{
c = prot_getc( inStreamIn );
if ( c == EOF )
{
return( eAODLostConnection );
}
if ( c != '\r' )
{
syslog( LOG_DEBUG, "AOD: GetClientResponse: CR not found: %c", c );
return( eAODInvalidDataType );
}
c = prot_getc( inStreamIn );
if ( c == EOF )
{
return( eAODLostConnection );
}
if ( c != '\n' )
{
syslog( LOG_DEBUG, "AOD: GetClientResponse: LF not found: %c", c );
return( eAODInvalidDataType );
}
break;
}
syslog( LOG_DEBUG, "AOD: GetClientResponse: bad sequence in {n+}");
return( eAODInvalidDataType );
}
else
{
syslog( LOG_DEBUG, "AOD: GetClientResponse: bad sequence in {n+}: missing +");
return( eAODInvalidDataType );
}
}
if ( (len <= 0) || (len > inBufSize) )
{
syslog( LOG_DEBUG, "AOD: GetClientResponse: out of bounds length. len: %d. buf size: %d", len, inBufSize );
return( eAODInvalidDataType );
}
bufEnd = inOutBuf + len;
while ( bufPtr < bufEnd )
{
*bufPtr++ = prot_getc( inStreamIn );
}
c = prot_getc( inStreamIn );
if ( c == EOF )
{
return( eAODLostConnection );
}
if ( c != '\r' )
{
syslog( LOG_DEBUG, "AOD: GetClientResponse: CR not found: %c", c );
return( eAODInvalidDataType );
}
c = prot_getc( inStreamIn );
if ( c == EOF )
{
return( eAODLostConnection );
}
if ( c != '\n' )
{
syslog( LOG_DEBUG, "AOD: GetClientResponse: LF not found: %c", c );
return( eAODInvalidDataType );
}
return( eAODNoErr );
}
syslog( LOG_DEBUG, "AOD: GetClientResponse: invalid lead character: %c", c );
if ( prot_fgets( inOutBuf, MAX_IO_BUF_SIZE, inStreamIn ) )
syslog( LOG_DEBUG, "AOD: GetClientResponse: inOutBuf: %s", inOutBuf );
return( eAODInvalidDataType );
}
int sGetUserOptions ( const char *inUserID, struct od_user_opts *inOutOpts )
{
int i = 1;
tDirStatus dsStatus = eDSNoErr;
if ( (inUserID == NULL) || (inOutOpts == NULL) )
{
syslog( LOG_DEBUG, "AOD: System Error: empty user ID or user opts struct" );
return( -1 );
}
for ( i = 1; i < 4; i++ )
{
dsStatus = eDSNoErr;
if ( dsVerifyDirRefNum( inOutOpts->fDirRef ) != eDSNoErr )
{
syslog( LOG_DEBUG, "AOD: user opts: getting directory reference" );
dsStatus = sOpen_ds( &inOutOpts->fDirRef );
if ( dsStatus == eDSNoErr )
{
syslog( LOG_DEBUG, "AOD: user opts: getting search node reference" );
dsStatus = sGet_search_node( inOutOpts->fDirRef, &inOutOpts->fSearchNodeRef );
}
}
if ( dsStatus == eDSNoErr )
{
syslog( LOG_DEBUG, "AOD: user opts: looking up user record: %s", inUserID );
dsStatus = sGet_user_attributes( inOutOpts->fDirRef, inOutOpts->fSearchNodeRef, inUserID, inOutOpts );
}
if ( dsStatus != eDSInvalidReference )
{
break;
}
syslog( LOG_WARNING, "AOD Warning: Directory reference is invalid, retrying (%d)", i );
sleep( 1 );
}
return( dsStatus );
}
int checkServiceACL ( struct od_user_opts *inUserOpts, const char *inGroup )
{
int err = eAODNoErr;
int result = 0;
uuid_t userUUID;
memset( userUUID, 0, sizeof( uuid_t ) );
if ( inUserOpts->fUserUUID != NULL )
{
err = mbr_string_to_uuid( (const char *)inUserOpts->fUserUUID, userUUID );
}
else
{
err = mbr_user_name_to_uuid( inUserOpts->fRecNamePtr, userUUID );
}
if ( err != eAODNoErr )
{
syslog( LOG_DEBUG, "AOD: service ACL: mbr_user_name_to_uuid failed for user: %s (%s)", inUserOpts->fRecNamePtr, strerror( err ) );
return( -1 );
}
err = mbr_check_service_membership( userUUID, inGroup, &result );
if ( err == ENOENT )
{
syslog( LOG_DEBUG, "AOD: mbr_check_service_membership with access_all_services" );
err = mbr_check_service_membership( userUUID, "access_all_services", &result );
}
if ( err == eAODNoErr )
{
if ( result != 0 )
{
if ( inUserOpts->fAccountState & eAutoForwardedEnabled )
{
syslog( LOG_DEBUG, "AOD: valid service ACL member: auto-forward" );
inUserOpts->fAccountState &= ~eIMAPEnabled;
inUserOpts->fAccountState &= ~ePOPEnabled;
}
else
{
syslog( LOG_DEBUG, "AOD: valid service ACL member: normal access" );
inUserOpts->fAccountState |= eAccountEnabled;
inUserOpts->fAccountState |= eIMAPEnabled;
inUserOpts->fAccountState |= ePOPEnabled;
}
}
else
{
syslog( LOG_DEBUG, "AOD Warning: mbr_check_service_membership failed with: %d", result );
inUserOpts->fAccountState |= eACLNotMember;
inUserOpts->fAccountState &= ~eAccountEnabled;
inUserOpts->fAccountState &= ~eIMAPEnabled;
inUserOpts->fAccountState &= ~ePOPEnabled;
}
}
else
{
syslog( LOG_DEBUG, "AOD: mail service ACL NOT enabled" );
}
return( result );
}
int sDoesUserBelongToGroup ( const char *inUser, const char *inGroup )
{
int isMember = 0;
if ( (inUser != nil) && (inGroup != nil) )
{
struct group *passwd_group = getgrnam( inGroup );
if ( passwd_group != NULL )
{
struct passwd *passwd_ent = getpwnam( inUser );
if ( passwd_ent != NULL )
{
isMember = (passwd_ent->pw_gid == passwd_group->gr_gid);
}
if ( !isMember )
{
char **gr_mem = passwd_group->gr_mem;
while ( *gr_mem )
{
if ( strcmp( inUser, *gr_mem ) == 0 )
{
isMember = 1;
break;
}
gr_mem++;
}
}
}
}
return( isMember );
}
tDirStatus sOpen_ds ( tDirReference *inOutDirRef )
{
tDirStatus dsStatus = eDSNoErr;
dsStatus = dsOpenDirService( inOutDirRef );
return( dsStatus );
}
tDirStatus sGet_search_node ( tDirReference inDirRef,
tDirNodeReference *outSearchNodeRef )
{
tDirStatus dsStatus = eMemoryAllocError;
unsigned long uiCount = 0;
tDataBuffer *pTDataBuff = NULL;
tDataList *pDataList = NULL;
pTDataBuff = dsDataBufferAllocate( inDirRef, 8192 );
if ( pTDataBuff != NULL )
{
dsStatus = dsFindDirNodes( inDirRef, pTDataBuff, NULL, eDSSearchNodeName, &uiCount, NULL );
if ( dsStatus == eDSNoErr )
{
dsStatus = eDSNodeNotFound;
if ( uiCount == 1 )
{
dsStatus = dsGetDirNodeName( inDirRef, pTDataBuff, 1, &pDataList );
if ( dsStatus == eDSNoErr )
{
dsStatus = dsOpenDirNode( inDirRef, pDataList, outSearchNodeRef );
}
if ( pDataList != NULL )
{
(void)dsDataListDeAllocate( inDirRef, pDataList, true );
free( pDataList );
pDataList = NULL;
}
}
}
(void)dsDataBufferDeAllocate( inDirRef, pTDataBuff );
pTDataBuff = NULL;
}
return( dsStatus );
}
tDirStatus sGet_user_attributes ( tDirReference inDirRef,
tDirNodeReference inSearchNodeRef,
const char *inUserID,
struct od_user_opts *inOutOpts )
{
tDirStatus dsStatus = eMemoryAllocError;
int done = FALSE;
int i = 0;
unsigned long uiRecCount = 0;
tDataBuffer *pTDataBuff = NULL;
tDataList *pUserRecType = NULL;
tDataList *pUserAttrType = NULL;
tRecordEntry *pRecEntry = NULL;
tAttributeEntry *pAttrEntry = NULL;
tAttributeValueEntry *pValueEntry = NULL;
tAttributeValueListRef valueRef = 0;
tAttributeListRef attrListRef = 0;
tContextData pContext = NULL;
tDataList tdlRecName;
memset( &tdlRecName, 0, sizeof( tDataList ) );
pTDataBuff = dsDataBufferAllocate( inDirRef, 8192 );
if ( pTDataBuff != NULL )
{
dsStatus = dsBuildListFromStringsAlloc( inDirRef, &tdlRecName, inUserID, NULL );
if ( dsStatus == eDSNoErr )
{
dsStatus = eMemoryAllocError;
pUserRecType = dsBuildListFromStrings( inDirRef, kDSStdRecordTypeUsers, NULL );
if ( pUserRecType != NULL )
{
pUserAttrType = dsBuildListFromStrings( inDirRef,
kDS1AttrMailAttribute,
kDSNAttrRecordName,
kDS1AttrGeneratedUID,
kDSNAttrMetaNodeLocation,
NULL );
if ( pUserAttrType != NULL )
{
do {
dsStatus = dsGetRecordList( inSearchNodeRef, pTDataBuff, &tdlRecName, eDSiExact, pUserRecType,
pUserAttrType, FALSE, &uiRecCount, &pContext );
if ( dsStatus == eDSNoErr )
{
dsStatus = eDSInvalidName;
if ( uiRecCount == 1 )
{
dsStatus = dsGetRecordEntry( inSearchNodeRef, pTDataBuff, 1, &attrListRef, &pRecEntry );
if ( dsStatus == eDSNoErr )
{
for ( i = 1; i <= pRecEntry->fRecordAttributeCount; i++ )
{
dsStatus = dsGetAttributeEntry( inSearchNodeRef, pTDataBuff, attrListRef, i, &valueRef, &pAttrEntry );
if ( (dsStatus == eDSNoErr) && (pAttrEntry != NULL) )
{
if ( strcasecmp( pAttrEntry->fAttributeSignature.fBufferData, kDS1AttrMailAttribute ) == 0 )
{
dsStatus = dsGetAttributeValue( inSearchNodeRef, pTDataBuff, 1, valueRef, &pValueEntry );
if ( dsStatus == eDSNoErr )
{
syslog( LOG_DEBUG, "AOD: getting mail attribute for user: %s", inUserID );
sGet_mail_values( (char *)pValueEntry->fAttributeValueData.fBufferData, inOutOpts );
done = true;
}
}
else if ( strcasecmp( pAttrEntry->fAttributeSignature.fBufferData, kDSNAttrRecordName ) == 0 )
{
dsStatus = dsGetAttributeValue( inSearchNodeRef, pTDataBuff, 1, valueRef, &pValueEntry );
if ( dsStatus == eDSNoErr )
{
inOutOpts->fRecNamePtr = malloc( pValueEntry->fAttributeValueData.fBufferLength + 1 );
if ( inOutOpts->fRecNamePtr != NULL )
{
strlcpy( inOutOpts->fRecNamePtr, pValueEntry->fAttributeValueData.fBufferData, pValueEntry->fAttributeValueData.fBufferLength + 1 );
}
}
}
else if ( strcmp( pAttrEntry->fAttributeSignature.fBufferData, kDSNAttrMetaNodeLocation ) == 0 )
{
dsStatus = dsGetAttributeValue( inSearchNodeRef, pTDataBuff, 1, valueRef, &pValueEntry );
if ( dsStatus == eDSNoErr )
{
inOutOpts->fUserLocPtr = (char *)calloc( pValueEntry->fAttributeValueData.fBufferLength + 1, sizeof( char ) );
memcpy( inOutOpts->fUserLocPtr, pValueEntry->fAttributeValueData.fBufferData, pValueEntry->fAttributeValueData.fBufferLength );
}
}
else if ( strcmp( pAttrEntry->fAttributeSignature.fBufferData, kDS1AttrGeneratedUID ) == 0 )
{
dsStatus = dsGetAttributeValue( inSearchNodeRef, pTDataBuff, 1, valueRef, &pValueEntry );
if ( dsStatus == eDSNoErr )
{
inOutOpts->fUserUUID = (uuid_t *)calloc( pValueEntry->fAttributeValueData.fBufferLength + 1, sizeof( char ) );
memcpy( inOutOpts->fUserUUID, pValueEntry->fAttributeValueData.fBufferData, pValueEntry->fAttributeValueData.fBufferLength );
}
}
if ( pValueEntry != NULL )
{
(void)dsDeallocAttributeValueEntry( inSearchNodeRef, pValueEntry );
pValueEntry = NULL;
}
}
else
{
syslog( LOG_DEBUG, "AOD Warning: dsGetAttributeEntry failed with: %d for user: %s", dsStatus, inUserID );
}
if ( pAttrEntry != NULL )
{
(void)dsCloseAttributeValueList( valueRef );
(void)dsDeallocAttributeEntry( inSearchNodeRef, pAttrEntry );
pAttrEntry = NULL;
}
}
if ( pRecEntry != NULL )
{
(void)dsDeallocRecordEntry( inSearchNodeRef, pRecEntry );
pRecEntry = NULL;
}
}
else
{
syslog( LOG_DEBUG, "AOD Warning: dsGetRecordEntry failed with: %d for user: %s", dsStatus, inUserID );
}
}
else
{
done = true;
if ( uiRecCount > 1 )
{
syslog( LOG_NOTICE, "AOD: user attributes: duplicate users found in directory: %s", inUserID );
}
dsStatus = eDSUserUnknown;
}
}
else
{
syslog( LOG_DEBUG, "AOD Warning: dsGetRecordList failed with: %d for user: %s", dsStatus, inUserID );
}
} while ( (pContext != NULL) && (dsStatus == eDSNoErr) && (!done) );
if ( pContext != NULL )
{
(void)dsReleaseContinueData( inSearchNodeRef, pContext );
pContext = NULL;
}
(void)dsDataListDeallocate( inDirRef, pUserAttrType );
free( pUserAttrType );
pUserAttrType = NULL;
}
(void)dsDataListDeallocate( inDirRef, pUserRecType );
free( pUserRecType );
pUserRecType = NULL;
}
(void)dsDataListDeAllocate( inDirRef, &tdlRecName, TRUE );
}
(void)dsDataBufferDeAllocate( inDirRef, pTDataBuff );
pTDataBuff = NULL;
}
return( dsStatus );
}
tDirStatus sOpen_user_node ( tDirReference inDirRef, const char *inUserLoc, tDirNodeReference *outUserNodeRef )
{
tDirStatus dsStatus = eMemoryAllocError;
tDataList *pUserNode = NULL;
pUserNode = dsBuildFromPath( inDirRef, inUserLoc, "/" );
if ( pUserNode != NULL )
{
dsStatus = dsOpenDirNode( inDirRef, pUserNode, outUserNodeRef );
(void)dsDataListDeAllocate( inDirRef, pUserNode, TRUE );
free( pUserNode );
pUserNode = NULL;
}
return( dsStatus );
}
static int sDoCryptAuth ( tDirReference inDirRef,
tDirNodeReference inUserNodeRef,
const char *inUserID, const char *inPasswd )
{
tDirStatus dsStatus = eAODParamErr;
long nameLen = 0;
long passwdLen = 0;
unsigned long curr = 0;
unsigned long len = 0;
unsigned long uiBuffSzie = 0;
tDataBuffer *pAuthBuff = NULL;
tDataBuffer *pStepBuff = NULL;
tDataNode *pAuthType = NULL;
if ( (inUserID == NULL) || (inPasswd == NULL) )
{
return( eDSAuthParameterError );
}
nameLen = strlen( inUserID );
passwdLen = strlen( inPasswd );
uiBuffSzie = nameLen + passwdLen + 32;
pAuthBuff = dsDataBufferAllocate( inDirRef, uiBuffSzie );
if ( pAuthBuff != NULL )
{
pStepBuff = dsDataBufferAllocate( inDirRef, 256 );
if ( pStepBuff != NULL )
{
pAuthType = dsDataNodeAllocateString( inDirRef, kDSStdAuthNodeNativeClearTextOK );
if ( pAuthType != NULL )
{
len = nameLen;
memcpy( &(pAuthBuff->fBufferData[ curr ]), &len, sizeof( unsigned long ) );
curr += sizeof( unsigned long );
memcpy( &(pAuthBuff->fBufferData[ curr ]), inUserID, len );
curr += len;
len = passwdLen;
memcpy( &(pAuthBuff->fBufferData[ curr ]), &len, sizeof( unsigned long ) );
curr += sizeof( unsigned long );
memcpy( &(pAuthBuff->fBufferData[ curr ]), inPasswd, len );
curr += len;
pAuthBuff->fBufferLength = curr;
dsStatus = dsDoDirNodeAuth( inUserNodeRef, pAuthType, true, pAuthBuff, pStepBuff, NULL );
if ( dsStatus != eDSNoErr )
{
syslog( LOG_ERR, "AOD: crypt authentication error: authentication failed for user: %s (%d)", inUserID, dsStatus );
}
(void)dsDataNodeDeAllocate( inDirRef, pAuthType );
pAuthType = NULL;
}
else
{
syslog( LOG_ERR, "AOD: crypt authentication error: authentication failed for user: %s (%d)", inUserID, eDSAllocationFailed );
dsStatus = eDSAllocationFailed;
}
(void)dsDataNodeDeAllocate( inDirRef, pStepBuff );
pStepBuff = NULL;
}
else
{
syslog( LOG_ERR, "AOD: crypt authentication error: authentication failed for user: %s (%d)", inUserID, eDSAllocationFailed );
dsStatus = eDSAllocationFailed;
}
(void)dsDataNodeDeAllocate( inDirRef, pAuthBuff );
pAuthBuff = NULL;
}
return( dsStatus );
}
int sValidateResponse ( const char *inChallenge,
const char *inResponse,
const char *inAuthType,
struct od_user_opts *inUserOpts )
{
int iResult = eAODParamErr;
tDirStatus dsStatus = eDSNoErr;
tDirReference dirRef = 0;
tDirNodeReference userNodeRef = 0;
tDataBuffer *pAuthBuff = NULL;
tDataBuffer *pStepBuff = NULL;
tDataNode *pAuthType = NULL;
unsigned long uiNameLen = 0;
unsigned long uiChalLen = 0;
unsigned long uiRespLen = 0;
unsigned long uiBuffSzie = 0;
unsigned long uiCurr = 0;
unsigned long uiLen = 0;
if ( (inUserOpts == NULL) || (inUserOpts->fRecNamePtr == NULL) )
{
syslog( LOG_DEBUG, "AOD: validate response: configuration error: empty user" );
return( eAODParamErr );
}
if ( inChallenge == NULL )
{
syslog( LOG_DEBUG, "AOD: validate response: configuration error: challenge" );
return( eAODParamErr );
}
if ( inResponse == NULL )
{
syslog( LOG_DEBUG, "AOD: validate response: configuration error: empty response" );
return( eAODParamErr );
}
if ( inAuthType == NULL )
{
syslog( LOG_DEBUG, "AOD: validate response: configuration error: empty auth type" );
return( eAODParamErr );
}
uiNameLen = strlen( inUserOpts->fRecNamePtr );
uiChalLen = strlen( inChallenge );
uiRespLen = strlen( inResponse );
uiBuffSzie = uiNameLen + uiChalLen + uiRespLen + 32;
dirRef = inUserOpts->fDirRef;
if ( inUserOpts->fUserLocPtr != NULL )
{
dsStatus = sOpen_user_node( dirRef, inUserOpts->fUserLocPtr, &userNodeRef );
if ( dsStatus == eDSNoErr )
{
pAuthBuff = dsDataBufferAllocate( dirRef, uiBuffSzie );
if ( pAuthBuff != NULL )
{
pStepBuff = dsDataBufferAllocate( dirRef, 256 );
if ( pStepBuff != NULL )
{
pAuthType = dsDataNodeAllocateString( dirRef, inAuthType );
if ( pAuthType != NULL )
{
uiLen = uiNameLen;
memcpy( &(pAuthBuff->fBufferData[ uiCurr ]), &uiLen, sizeof( unsigned long ) );
uiCurr += sizeof( unsigned long );
memcpy( &(pAuthBuff->fBufferData[ uiCurr ]), inUserOpts->fRecNamePtr, uiLen );
uiCurr += uiLen;
uiLen = uiChalLen;
memcpy( &(pAuthBuff->fBufferData[ uiCurr ]), &uiLen, sizeof( unsigned long ) );
uiCurr += sizeof( unsigned long );
memcpy( &(pAuthBuff->fBufferData[ uiCurr ]), inChallenge, uiLen );
uiCurr += uiLen;
uiLen = uiRespLen;
memcpy( &(pAuthBuff->fBufferData[ uiCurr ]), &uiLen, sizeof( unsigned long ) );
uiCurr += sizeof( unsigned long );
memcpy( &(pAuthBuff->fBufferData[ uiCurr ]), inResponse, uiLen );
uiCurr += uiLen;
pAuthBuff->fBufferLength = uiCurr;
dsStatus = dsDoDirNodeAuth( userNodeRef, pAuthType, true, pAuthBuff, pStepBuff, NULL );
switch ( dsStatus )
{
case eDSNoErr:
iResult = eAODNoErr;
break;
case eDSAuthNewPasswordRequired:
syslog( LOG_INFO, "AOD: authentication error: new password required for user: %s", inUserOpts->fRecNamePtr );
iResult = eAODNoErr;
break;
case eDSAuthPasswordExpired:
syslog( LOG_INFO, "AOD: authentication error: password expired for user: %s", inUserOpts->fRecNamePtr );
iResult = eAODNoErr;
break;
default:
syslog( LOG_INFO, "AOD: authentication error: %d", dsStatus );
iResult = eAODAuthFailed;
break;
}
(void)dsDataNodeDeAllocate( dirRef, pAuthType );
pAuthType = NULL;
}
else
{
syslog( LOG_ERR, "AOD: authentication error: for user: %s. cannot allocate memory", inUserOpts->fRecNamePtr );
iResult = eAODAllocError;
}
(void)dsDataNodeDeAllocate( dirRef, pStepBuff );
pStepBuff = NULL;
}
else
{
syslog( LOG_ERR, "AOD: authentication error: for user: %s. cannot allocate memory", inUserOpts->fRecNamePtr );
iResult = eAODAllocError;
}
(void)dsDataNodeDeAllocate( dirRef, pAuthBuff );
pAuthBuff = NULL;
}
else
{
syslog( LOG_ERR, "AOD: authentication error: for user: %s. cannot allocate memory", inUserOpts->fRecNamePtr );
iResult = eAODAllocError;
}
(void)dsCloseDirNode( userNodeRef );
userNodeRef = 0;
}
else
{
syslog( LOG_ERR, "AOD: authentication error: cannot open user directory node for user: %s (%d)", inUserOpts->fRecNamePtr, dsStatus );
iResult = eAODCantOpenUserNode;
}
}
else
{
syslog( LOG_ERR, "AOD: authentication error: cannot find user: %s (%d)", inUserOpts->fRecNamePtr, dsStatus );
iResult = eAODUserNotFound;
}
return( iResult );
}
void sGet_mail_values ( char *inMailAttribute, struct od_user_opts *inOutOpts )
{
int iResult = 0;
unsigned long uiDataLen = 0;
CFDataRef cfDataRef = NULL;
CFPropertyListRef cfPlistRef = NULL;
CFDictionaryRef cfDictRef = NULL;
if ( inMailAttribute != NULL )
{
uiDataLen = strlen( inMailAttribute );
cfDataRef = CFDataCreate( NULL, (const UInt8 *)inMailAttribute, uiDataLen );
if ( cfDataRef != NULL )
{
cfPlistRef = CFPropertyListCreateFromXMLData( kCFAllocatorDefault, cfDataRef, kCFPropertyListImmutable, NULL );
if ( cfPlistRef != NULL )
{
if ( CFDictionaryGetTypeID() == CFGetTypeID( cfPlistRef ) )
{
cfDictRef = (CFDictionaryRef)cfPlistRef;
iResult = sVerify_version( cfDictRef );
if ( iResult == eAODNoErr )
{
sGet_acct_state( cfDictRef, inOutOpts );
sGet_IMAP_login( cfDictRef, inOutOpts );
sGet_POP3_login( cfDictRef, inOutOpts );
sGet_acct_loc( cfDictRef, inOutOpts );
sGet_alt_loc( cfDictRef, inOutOpts );
sGet_disk_quota( cfDictRef, inOutOpts );
}
}
CFRelease( cfPlistRef );
}
CFRelease( cfDataRef );
}
}
}
int sVerify_version ( CFDictionaryRef inCFDictRef )
{
int iResult = eAODInvalidDataType;
CFStringRef cfStringRef = NULL;
char *pValue = NULL;
if ( CFDictionaryContainsKey( inCFDictRef, CFSTR( kXMLKeyAttrVersion ) ) )
{
cfStringRef = (CFStringRef)CFDictionaryGetValue( inCFDictRef, CFSTR( kXMLKeyAttrVersion ) );
if ( cfStringRef != NULL )
{
if ( CFGetTypeID( cfStringRef ) == CFStringGetTypeID() )
{
iResult = eAODItemNotFound;
pValue = (char *)CFStringGetCStringPtr( cfStringRef, kCFStringEncodingMacRoman );
if ( pValue != NULL )
{
iResult = eAODWrongVersion;
if ( strcasecmp( pValue, kXMLValueVersion ) == 0 )
{
iResult = eAODNoErr;
}
}
}
}
}
return( iResult );
}
void sGet_acct_state ( CFDictionaryRef inCFDictRef, struct od_user_opts *inOutOpts )
{
CFStringRef cfStringRef = NULL;
char *pValue = NULL;
inOutOpts->fAccountState &= ~eAccountEnabled;
if ( CFDictionaryContainsKey( inCFDictRef, CFSTR( kXMLKeyAcctState ) ) )
{
cfStringRef = (CFStringRef)CFDictionaryGetValue( inCFDictRef, CFSTR( kXMLKeyAcctState ) );
if ( cfStringRef != NULL )
{
if ( CFGetTypeID( cfStringRef ) == CFStringGetTypeID() )
{
pValue = (char *)CFStringGetCStringPtr( cfStringRef, kCFStringEncodingMacRoman );
if ( pValue != NULL )
{
if ( strcasecmp( pValue, kXMLValueAcctEnabled ) == 0 )
{
inOutOpts->fAccountState |= eAccountEnabled;
syslog( LOG_DEBUG, "AOD: mail enabled" );
}
else if ( strcasecmp( pValue, kXMLValueAcctFwd ) == 0 )
{
sGet_auto_forward( inCFDictRef, inOutOpts );
syslog( LOG_DEBUG, "AOD: mail auto-forwarding enabled" );
}
else
{
syslog( LOG_DEBUG, "AOD: mail not enabled" );
}
}
}
}
}
}
void sGet_auto_forward ( CFDictionaryRef inCFDictRef, struct od_user_opts *inOutOpts )
{
CFStringRef cfStringRef = NULL;
char *pValue = NULL;
if ( CFDictionaryContainsKey( inCFDictRef, CFSTR( kXMLKeyAutoFwd ) ) )
{
cfStringRef = (CFStringRef)CFDictionaryGetValue( inCFDictRef, CFSTR( kXMLKeyAutoFwd ) );
if ( (cfStringRef != NULL) && (CFGetTypeID( cfStringRef ) == CFStringGetTypeID()) )
{
pValue = (char *)CFStringGetCStringPtr( cfStringRef, kCFStringEncodingMacRoman );
if ( (pValue != NULL) && strlen( pValue ) )
{
inOutOpts->fAutoFwdPtr = malloc( strlen( pValue ) + 1 );
if ( inOutOpts->fAutoFwdPtr != NULL )
{
strlcpy( inOutOpts->fAutoFwdPtr, pValue, strlen( pValue ) + 1 );
}
inOutOpts->fAccountState |= eAutoForwardedEnabled;
inOutOpts->fAccountState &= ~eIMAPEnabled;
inOutOpts->fAccountState &= ~ePOPEnabled;
}
}
}
}
void sGet_IMAP_login ( CFDictionaryRef inCFDictRef, struct od_user_opts *inOutOpts )
{
CFStringRef cfStringRef = NULL;
char *pValue = NULL;
inOutOpts->fAccountState &= ~eIMAPEnabled;
if ( CFDictionaryContainsKey( inCFDictRef, CFSTR( kXMLKeykIMAPLoginState ) ) )
{
cfStringRef = (CFStringRef)CFDictionaryGetValue( inCFDictRef, CFSTR( kXMLKeykIMAPLoginState ) );
if ( (cfStringRef != NULL) && (CFGetTypeID( cfStringRef ) == CFStringGetTypeID()) )
{
pValue = (char *)CFStringGetCStringPtr( cfStringRef, kCFStringEncodingMacRoman );
if ( (pValue != NULL) && (strcasecmp( pValue, kXMLValueIMAPLoginOK ) == 0) )
{
inOutOpts->fAccountState |= eIMAPEnabled;
}
}
}
}
void sGet_POP3_login ( CFDictionaryRef inCFDictRef, struct od_user_opts *inOutOpts )
{
CFStringRef cfStringRef = NULL;
char *pValue = NULL;
inOutOpts->fAccountState &= ~ePOPEnabled;
if ( CFDictionaryContainsKey( inCFDictRef, CFSTR( kXMLKeyPOP3LoginState ) ) )
{
cfStringRef = (CFStringRef)CFDictionaryGetValue( inCFDictRef, CFSTR( kXMLKeyPOP3LoginState ) );
if ( (cfStringRef != NULL) && (CFGetTypeID( cfStringRef ) == CFStringGetTypeID()) )
{
pValue = (char *)CFStringGetCStringPtr( cfStringRef, kCFStringEncodingMacRoman );
if ( (pValue != NULL) && (strcasecmp( pValue, kXMLValuePOP3LoginOK ) == 0) )
{
inOutOpts->fAccountState |= ePOPEnabled;
}
}
}
}
void sGet_acct_loc ( CFDictionaryRef inCFDictRef, struct od_user_opts *inOutOpts )
{
CFStringRef cfStringRef = NULL;
char *pValue = NULL;
if ( inOutOpts->fAccountLocPtr != NULL )
{
free( inOutOpts->fAccountLocPtr );
inOutOpts->fAccountLocPtr = NULL;
}
if ( CFDictionaryContainsKey( inCFDictRef, CFSTR( kXMLKeyAcctLoc ) ) )
{
cfStringRef = (CFStringRef)CFDictionaryGetValue( inCFDictRef, CFSTR( kXMLKeyAcctLoc ) );
if ( (cfStringRef != NULL) && (CFGetTypeID( cfStringRef ) == CFStringGetTypeID()) )
{
pValue = (char*)CFStringGetCStringPtr( cfStringRef, kCFStringEncodingMacRoman );
if ( (pValue != NULL) && strlen( pValue ) )
{
inOutOpts->fAccountLocPtr = malloc( strlen( pValue ) + 1 );
if ( inOutOpts->fAccountLocPtr != NULL )
{
strlcpy( inOutOpts->fAccountLocPtr, pValue, strlen( pValue ) + 1 );
}
}
}
}
}
void sGet_alt_loc ( CFDictionaryRef inCFDictRef, struct od_user_opts *inOutOpts )
{
CFStringRef cfStringRef = NULL;
char *pValue = NULL;
if ( inOutOpts->fAltDataLocPtr != NULL )
{
free( inOutOpts->fAltDataLocPtr );
inOutOpts->fAltDataLocPtr = NULL;
}
if ( CFDictionaryContainsKey( inCFDictRef, CFSTR( kXMLKeyAltDataStoreLoc ) ) )
{
cfStringRef = (CFStringRef)CFDictionaryGetValue( inCFDictRef, CFSTR( kXMLKeyAltDataStoreLoc ) );
if ( cfStringRef != NULL )
{
if ( CFGetTypeID( cfStringRef ) == CFStringGetTypeID() )
{
pValue = (char*)CFStringGetCStringPtr( cfStringRef, kCFStringEncodingMacRoman );
if ( (pValue != NULL) && strlen( pValue ) )
{
inOutOpts->fAltDataLocPtr = malloc( strlen( pValue ) + 1 );
if ( inOutOpts->fAltDataLocPtr != NULL )
{
strlcpy( inOutOpts->fAltDataLocPtr, pValue, strlen( pValue ) + 1 );
}
}
}
}
}
}
void sGet_disk_quota ( CFDictionaryRef inCFDictRef, struct od_user_opts *inOutOpts )
{
CFStringRef cfStringRef = NULL;
char *pValue = NULL;
inOutOpts->fDiskQuota = 0;
if ( CFDictionaryContainsKey( inCFDictRef, CFSTR( kXMLKeyDiskQuota ) ) )
{
cfStringRef = (CFStringRef)CFDictionaryGetValue( inCFDictRef, CFSTR( kXMLKeyDiskQuota ) );
if ( cfStringRef != NULL )
{
if ( CFGetTypeID( cfStringRef ) == CFStringGetTypeID() )
{
pValue = (char *)CFStringGetCStringPtr( cfStringRef, kCFStringEncodingMacRoman );
if ( pValue != NULL )
{
inOutOpts->fDiskQuota = atol( pValue );
}
}
}
}
}
int sEncodeBase64 ( const char *inStr, const int inLen, char *outStr, int outLen )
{
int i = 0;
const unsigned char *pStr = NULL;
unsigned long uiInLen = 0;
unsigned char ucVal = 0;
if ( (inStr == NULL) || (outStr == NULL) || (inLen <= 0) || (outLen <= 0) )
{
return( -1 );
}
pStr = (const unsigned char *)inStr;
uiInLen = inLen;
memset( outStr, 0, outLen );
while ( uiInLen >= 3 )
{
if ( (i + 4) > outLen )
{
return( -1 );
}
outStr[ i++ ] = ( basis_64[ pStr[ 0 ] >> 2 ] );
outStr[ i++ ] = ( basis_64[ ((pStr[ 0 ] << 4) & 0x30) | (pStr[ 1 ] >> 4) ] );
outStr[ i++ ] = ( basis_64[ ((pStr[ 1 ] << 2) & 0x3c) | (pStr[ 2 ] >> 6) ] );
outStr[ i++ ] = ( basis_64[ pStr[ 2 ] & 0x3f ] );
pStr += 3;
uiInLen -= 3;
}
if ( uiInLen > 0 )
{
if ( (i + 4) > outLen )
{
return( -1 );
}
outStr[ i++ ] = ( basis_64[ pStr[0] >> 2] );
ucVal = (pStr[0] << 4) & 0x30;
if ( uiInLen > 1 )
{
ucVal |= pStr[1] >> 4;
}
outStr[ i++ ] = ( basis_64[ ucVal ] );
outStr[ i++ ] = ( (uiInLen < 2) ? '=' : basis_64[ (pStr[ 1 ] << 2) & 0x3c ] );
outStr[ i++ ] = ( '=' );
}
return( 0 );
}
int sDecodeBase64 ( const char *inStr, const int inLen, char *outStr, int outLen, int *destLen )
{
int iResult = 0;
unsigned long i = 0;
unsigned long j = 0;
unsigned long c1 = 0;
unsigned long c2 = 0;
unsigned long c3 = 0;
unsigned long c4 = 0;
const char *pStr = NULL;
if ( (inStr == NULL) || (outStr == NULL) || (inLen <= 0) || (outLen <= 0) )
{
return( -1 );
}
pStr = (const unsigned char *)inStr;
if ( (pStr[ 0 ] == '+') && (pStr[ 1 ] == ' ') )
{
pStr += 2;
}
if ( *pStr == '\r')
{
iResult = -1;
}
else
{
for ( i = 0; i < inLen / 4; i++ )
{
c1 = pStr[ 0 ];
if ( CHAR64( c1 ) == -1 )
{
iResult = -1;
break;
}
c2 = pStr[ 1 ];
if ( CHAR64( c2 ) == -1 )
{
iResult = -1;
break;
}
c3 = pStr[ 2 ];
if ( (c3 != '=') && (CHAR64( c3 ) == -1) )
{
iResult = -1;
break;
}
c4 = pStr[ 3 ];
if (c4 != '=' && CHAR64( c4 ) == -1)
{
iResult = -1;
break;
}
pStr += 4;
outStr[ j++ ] = ( (CHAR64(c1) << 2) | (CHAR64(c2) >> 4) );
if ( j >= outLen )
{
return( -1 );
}
if ( c3 != '=' )
{
outStr[ j++ ] = ( ((CHAR64(c2) << 4) & 0xf0) | (CHAR64(c3) >> 2) );
if ( j >= outLen )
{
return( -1 );
}
if ( c4 != '=' )
{
outStr[ j++ ] = ( ((CHAR64(c3) << 6) & 0xc0) | CHAR64(c4) );
if ( j >= outLen )
{
return( -1 );
}
}
}
}
outStr[ j ] = 0;
}
if ( destLen )
{
*destLen = j;
}
return( iResult );
}
int apple_password_callback ( char *inBuf, int inSize, int in_rwflag, void *inUserData )
{
OSStatus status = noErr;
SecKeychainItemRef keyChainRef = NULL;
void *pwdBuf = NULL;
UInt32 pwdLen = 0;
char *service = "certificateManager";
CallbackUserData *cbUserData = (CallbackUserData *)inUserData;
if ( (cbUserData == NULL) || strlen( cbUserData->key ) == 0 ||
(cbUserData->len >= FILENAME_MAX) || (cbUserData->len == 0) || !inBuf )
{
syslog( LOG_ERR, "AOD Error: Invalid arguments in callback" );
return( 0 );
}
status = SecKeychainSetPreferenceDomain( kSecPreferencesDomainSystem );
if ( status != noErr )
{
syslog( LOG_ERR, "AOD: SSL callback: SecKeychainSetPreferenceDomain returned status: %d", status );
return( 0 );
}
status = SecKeychainFindGenericPassword( NULL, strlen( service ), service,
cbUserData->len, cbUserData->key,
&pwdLen, &pwdBuf,
&keyChainRef );
if ( (status == noErr) && (keyChainRef != NULL) )
{
if ( pwdLen > inSize )
{
syslog( LOG_ERR, "AOD: SSL callback: invalid buffer size callback size : %d, len : %d", inSize, pwdLen );
SecKeychainItemFreeContent( NULL, pwdBuf );
return( 0 );
}
memcpy( inBuf, (const void *)pwdBuf, pwdLen );
if ( inSize > 0 )
{
inBuf[ pwdLen ] = 0;
inBuf[ inSize - 1 ] = 0;
}
SecKeychainItemFreeContent( NULL, pwdBuf );
return( strlen(inBuf ) );
}
else if (status == errSecNotAvailable)
{
syslog( LOG_ERR, "AOD: SSL callback: SecKeychainSetPreferenceDomain: No keychain is available" );
}
else if ( status == errSecItemNotFound )
{
syslog( LOG_ERR, "AOD: SSL callback: SecKeychainSetPreferenceDomain: The requested key could not be found in the system keychain");
}
else if (status != noErr)
{
syslog( LOG_ERR, "AOD: SSL callback: SecKeychainFindGenericPassword returned status: %d", status );
}
return( 0 );
}
void get_random_chars ( char *out_buf, int in_len )
{
int count = 0;
int file;
unsigned long long microseconds = 0ULL;
struct timeval tv;
struct timezone tz;
memset( out_buf, 0, in_len );
file = open( "/dev/urandom", O_RDONLY, 0 );
if ( file == -1 )
{
syslog( LOG_ERR, "AOD: random chars: cannot open /dev/urandom" );
file = open( "/dev/random", O_RDONLY, 0 );
}
if ( file == -1 )
{
syslog( LOG_ERR, "AOD: random chars: cannot open /dev/random" );
gettimeofday( &tv, &tz );
microseconds = (unsigned long long)tv.tv_sec;
microseconds *= 1000000ULL;
microseconds += (unsigned long long)tv.tv_usec;
snprintf( out_buf, in_len, "%llu", microseconds );
}
else
{
while ( count < (in_len - 1) )
{
read( file, &out_buf[ count ], 1 );
if ( isalnum( out_buf[ count ] ) )
{
count++;
}
}
close( file );
}
}