#include <sys/poll.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <netdb.h>
#include <sys/stat.h>
#include <sys/sysctl.h> // for struct kinfo_proc and sysctl()
#include <syslog.h>
#include <dirent.h>
#include <ctype.h>
#include <sys/utsname.h>
#include <sys/ioctl.h>
#include <net/if.h> // interface struture ifreq, ifconf
#include <net/if_dl.h> // datalink structs
#include <net/if_types.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/un.h>
#include "CPSUtilities.h"
#include "SASLCode.h"
#include "CAuthFileBase.h"
#define kMinTrialTime 140000
#define kMaxTrialTime 1250000
#define kMaxIPAddrs 32
#if 1
static bool gDSDebuggingON = false;
#define DEBUGLOG(A,args...) if (gDSDebuggingON) syslog( LOG_ALERT, (A), ##args )
#else
#define DEBUGLOG(A,args...)
#endif
#define IFR_NEXT(ifr) \
((struct ifreq *) ((char *) (ifr) + sizeof(*(ifr)) + \
MAX(0, (int) (ifr)->ifr_addr.sa_len - (int) sizeof((ifr)->ifr_addr))))
long gOpenCount = 0;
void psfwSetUSR1Debug( bool on )
{
gDSDebuggingON = on;
}
void writeToServer( FILE *out, char *buf )
{
DEBUGLOG( "sending: %s", buf);
if ( buf != NULL && out != NULL )
{
fwrite(buf, strlen(buf), 1, out);
fflush(out);
}
}
PWServerError readFromServer( int fd, char *buf, unsigned long bufLen )
{
PWServerError result;
unsigned long byteCount;
result = readFromServerGetData( fd, buf, bufLen, &byteCount );
if ( result.err == 0 )
result = readFromServerGetLine( fd, buf, bufLen, true, &byteCount );
if ( result.err == 0 )
result = readFromServerGetErrorCode( buf );
DEBUGLOG( "received: %s", buf );
return result;
}
void writeToServerWithCASTKey( FILE *out, char *buf, CAST_KEY *inKey, unsigned char *inOutIV )
{
unsigned char *ebuf;
long bufLen;
if ( out == NULL || buf == NULL || inKey == NULL || inOutIV == NULL )
return;
DEBUGLOG( "encrypting and sending: %s", buf );
bufLen = strlen( buf );
bzero( buf + bufLen, CAST_BLOCK );
ebuf = (unsigned char *) malloc( bufLen + CAST_BLOCK + 1 );
if ( ebuf == NULL )
return;
if (bufLen % CAST_BLOCK)
bufLen += CAST_BLOCK - (bufLen % CAST_BLOCK);
CAST_cbc_encrypt( (unsigned char *)buf, ebuf, bufLen, inKey, inOutIV, CAST_ENCRYPT );
ebuf[bufLen] = '\0';
DEBUGLOG( "len = %d, encrypted: %s", bufLen, ebuf );
fwrite( ebuf, bufLen, 1, out );
fflush( out );
free( ebuf );
}
PWServerError readFromServerWithCASTKey( int fd, char *buf, unsigned long bufLen, CAST_KEY *inKey, unsigned char *inOutIV )
{
PWServerError result;
unsigned char *dbuf;
unsigned long byteCount = 0;
result = readFromServerGetData( fd, buf, bufLen, &byteCount );
DEBUGLOG( "byteCount1=%d (bufLen=%d)", byteCount, bufLen );
if ( result.err != 0 || byteCount == 0 )
return result;
dbuf = (unsigned char *) malloc( byteCount + CAST_BLOCK + 1 );
if ( dbuf == NULL )
{
result.err = -1;
result.type = kConnectionError;
}
CAST_cbc_encrypt( (unsigned char *)buf, dbuf, byteCount, inKey, inOutIV, CAST_DECRYPT );
memcpy( buf, dbuf, byteCount );
result = readFromServerGetLine( fd, buf, bufLen, false, &byteCount );
DEBUGLOG( "byteCount2=%d", byteCount );
if ( result.err == 0 )
{
result = readFromServerGetErrorCode( (char *)buf );
DEBUGLOG( "decrypted and received: %s", buf );
}
free( dbuf );
return result;
}
PWServerError readFromServerGetData( int fd, char *buf, unsigned long bufLen, unsigned long *outByteCount )
{
char readChar = '\0';
PWServerError result = {0, kPolicyError};
ssize_t byteCount = 0;
if ( buf == NULL || bufLen < 3 ) {
result.err = -1;
return result;
}
if ( outByteCount != NULL )
*outByteCount = 0;
buf[0] = '\0';
byteCount = ::recvfrom( fd, &readChar, sizeof(readChar), (MSG_WAITALL | MSG_PEEK), NULL, NULL );
if ( byteCount == 0 || byteCount == -1 )
{
result.err = -1;
result.type = kConnectionError;
return result;
}
byteCount = ::recvfrom( fd, buf, bufLen - 1, (MSG_DONTWAIT | MSG_PEEK), NULL, NULL );
DEBUGLOG( "byteCount (peek): %d", (int)byteCount);
if ( outByteCount != NULL )
*outByteCount = byteCount;
return result;
}
PWServerError readFromServerGetLine( int fd, char *buf, unsigned long bufLen, bool inCanReadMore, unsigned long *inOutByteCount )
{
char readChar = '\0';
char *tstr = NULL;
char *consumeBuf;
PWServerError result = {0, kPolicyError};
ssize_t byteCount = *inOutByteCount;
ssize_t consumeLen;
char stackConsumeBuf[2048];
if ( buf == NULL || bufLen < 3 ) {
result.err = -1;
return result;
}
if ( byteCount >= 2 )
{
if ( inCanReadMore )
{
buf[byteCount] = '\0';
tstr = strstr( buf, "\r\n" );
}
consumeLen = (tstr != NULL) ? (tstr - buf + 2) : byteCount;
if ( consumeLen < (ssize_t)sizeof(stackConsumeBuf) )
consumeBuf = stackConsumeBuf;
else {
consumeBuf = (char *) malloc( consumeLen );
if ( consumeBuf == NULL ) {
result.err = -1;
return result;
}
}
byteCount = ::recvfrom( fd, consumeBuf, consumeLen, MSG_DONTWAIT, NULL, NULL );
if ( consumeBuf != stackConsumeBuf )
free( consumeBuf );
if ( inOutByteCount != NULL )
*inOutByteCount = byteCount;
DEBUGLOG( "byteCount: %d", (int)byteCount);
buf[byteCount] = '\0';
}
if ( inCanReadMore && tstr == NULL && byteCount < (ssize_t)bufLen - 1 )
{
tstr = buf + byteCount;
do
{
byteCount = ::recvfrom( fd, &readChar, sizeof(readChar), MSG_WAITALL, NULL, NULL );
if ( byteCount == 0 || byteCount == -1 )
{
*tstr = '\0';
result.err = -1;
result.type = kConnectionError;
return result;
}
if ( (unsigned long)(tstr - buf) < bufLen - 1 )
*tstr++ = readChar;
if ( inOutByteCount != NULL )
(*inOutByteCount)++;
}
while ( readChar != '\n' );
*tstr = '\0';
}
return result;
}
PWServerError readFromServerGetErrorCode( char *buf )
{
char *tstr = NULL;
PWServerError result = {0, kPolicyError};
int compareLen;
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 );
if ( result.err == 0 )
result.err = -1;
}
else
{
compareLen = strlen(kPasswordServerAuthErrPrefixStr);
if ( strncmp( tstr, kPasswordServerAuthErrPrefixStr, compareLen ) == 0 )
{
tstr += compareLen;
sscanf( tstr, "%d", &result.err );
if ( result.err == 0 )
result.err = -1;
}
}
return result;
}
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 maxLen, 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, maxLen, &sasl_outlen );
*outLen = (attached_outlen > 0) ? attached_outlen : (unsigned long)sasl_outlen;
return result;
}
int getconn_domain_socket(void)
{
register int s, len;
struct sockaddr_un sun;
if ((s = socket(AF_UNIX, SOCK_STREAM, 0)) < 0)
return -1;
sun.sun_family = AF_UNIX;
strcpy(sun.sun_path, kPWUNIXDomainSocketAddress);
len = sizeof(sun.sun_family) + strlen(sun.sun_path) + 1;
if (connect(s, (sockaddr *)&sun, len) < 0)
return -1;
return s;
}
long ConnectToServer( sPSContextData *inContext )
{
long siResult = 0;
PWServerError pwsError;
char buf[1024];
char *cur, *tptr;
int index = 0;
DEBUGLOG( "ConnectToServer trying %s:%s", inContext->psName, inContext->psPort);
inContext->isUNIXDomainSocket = false;
if ( strcmp(inContext->psName, "127.0.0.1") == 0 ||
strcmp(inContext->psName, "localhost") == 0 )
{
inContext->fd = getconn_domain_socket();
if ( inContext->fd != -1 )
inContext->isUNIXDomainSocket = true;
}
if ( !inContext->isUNIXDomainSocket )
{
siResult = getconn( inContext->psName, inContext->psPort, &inContext->fd );
if ( siResult != 0 )
return( siResult );
}
pwsError = readFromServer(inContext->fd, buf, sizeof(buf));
if ( pwsError.err < 0 && pwsError.type == kConnectionError )
{
close( inContext->fd );
inContext->fd = -1;
siResult = pwsError.err;
}
else
{
gOpenCount++;
inContext->serverOut = fdopen(inContext->fd, "w");
if ( (tptr = strstr(buf, "ApplePasswordServer")) != NULL )
{
tptr += sizeof( "ApplePasswordServer" );
while ( (cur = strsep(&tptr, ".")) != NULL ) {
sscanf( cur, "%d", &inContext->serverVers[index++] );
if ( index >= 4 )
break;
}
}
}
return siResult;
}
Boolean Connected( sPSContextData *inContext )
{
struct pollfd fdToPoll;
int result;
if ( inContext->fd == 0 || inContext->fd == -1 )
return false;
fdToPoll.fd = inContext->fd;
fdToPoll.events = POLLSTANDARD;
fdToPoll.revents = 0;
result = poll( &fdToPoll, 1, 0 );
DEBUGLOG( "XXXX poll = %d, events = %d", result, fdToPoll.revents );
if ( result == -1 )
return false;
return ( (fdToPoll.revents & POLLHUP) == 0 );
}
long IdentifyReachableReplica( CFMutableArrayRef inServerArray, const char *inHexHash, sPSServerEntry *outReplica, int *outSock )
{
sPSServerEntry *entrylist = NULL;
CFIndex servIndex = 0;
CFIndex servCount = 0;
long result = kCPSUtilServiceUnavailable;
if ( ConvertCFArrayToServerArray(inServerArray, &entrylist, &servCount) != kCPSUtilOK )
return kCPSUtilServiceUnavailable;
result = IdentifyReachableReplicaByIP( entrylist, servCount, inHexHash, outReplica, outSock );
if ( result == kCPSUtilServiceUnavailable )
{
bool foundNewIPToTry = false;
int err = 0;
struct addrinfo *res = NULL;
struct addrinfo *res0 = NULL;
char testStr[256];
for ( servIndex = 0; servIndex < servCount; servIndex++ )
{
if ( entrylist[servIndex].dns[0] != '\0' )
{
err = getaddrinfo( entrylist[servIndex].dns, NULL, NULL, &res0 );
if ( err == 0 )
{
for ( res = res0; res != NULL; res = res->ai_next )
{
if ( res->ai_family != AF_INET || res->ai_addrlen != sizeof(sockaddr_in) )
continue;
if ( inet_ntop(AF_INET, &(((struct sockaddr_in *)(res->ai_addr))->sin_addr.s_addr), testStr, sizeof(testStr)) != NULL &&
strcmp(testStr, entrylist[servIndex].ip) != 0 )
{
strlcpy( entrylist[servIndex].ip, testStr, sizeof(entrylist[servIndex].ip) );
foundNewIPToTry = true;
}
}
freeaddrinfo( res0 );
}
}
}
if ( foundNewIPToTry )
result = IdentifyReachableReplicaByIP( entrylist, servCount, inHexHash, outReplica, outSock );
}
if ( entrylist != NULL )
free( entrylist );
return result;
}
int pwsf_SortServerEntryCompare(const void *a, const void *b)
{
return ( ((sPSServerEntry *)a)->sortVal - ((sPSServerEntry *)b)->sortVal );
}
void pwsf_SortServerEntries(sPSServerEntry *inEntryList, CFIndex servCount)
{
CFIndex servIndex = 0;
unsigned long *iplist = NULL;
if ( servCount <= 1 )
return;
if ( pwsf_LocalIPList(&iplist) != kCPSUtilOK || iplist == NULL )
return;
for ( servIndex = 0; servIndex < servCount; servIndex++ )
inEntryList[servIndex].sortVal = ReplicaPriority( &inEntryList[servIndex], iplist );
free( iplist );
qsort( inEntryList, servCount, sizeof(sPSServerEntry), pwsf_SortServerEntryCompare );
#if DEBUG
for ( servIndex = 0; servIndex < servCount; servIndex++ )
DEBUGLOG("ip=%s, sortVal=%d", inEntryList[servIndex].ip, inEntryList[servIndex].sortVal);
#endif
}
long IdentifyReachableReplicaByIP(
sPSServerEntry *entrylist,
CFIndex servCount,
const char *inHexHash,
sPSServerEntry *outReplica,
int *outSock )
{
long siResult = 0;
char *portNumStr = NULL;
CFIndex servIndex = 0;
CFIndex descIndex = 0;
int connectedSocket = -1;
bool connectedSocketIsTCP = false;
int tempsock = -1;
struct timeval tcpOpenTimeout = { 1, 0 };
float connectTime = 0;
int *socketList = NULL;
bool checkUDPDescriptors = false;
bool fallbackToTCP = false;
bool usingDefaultPort106 = true;
DEBUGLOG( "IdentifyReachableReplica inHexHash=%s", inHexHash ? inHexHash : "NULL" );
if ( entrylist == NULL || outReplica == NULL || outSock == NULL )
return kCPSUtilParameterError;
bzero( outReplica, sizeof(sPSServerEntry) );
*outSock = -1;
if ( servCount == 0 || entrylist == NULL )
return kCPSUtilOK;
pwsf_SortServerEntries( entrylist, servCount );
socketList = (int *) malloc( servCount * sizeof(int) );
if ( socketList == NULL )
return kCPSUtilMemoryError;
memset( socketList, -1, servCount * sizeof(int) );
try
{
checkUDPDescriptors = false;
for ( servIndex = 0; servIndex < servCount && connectedSocket == -1; servIndex++ )
{
if ( entrylist[servIndex].ip[0] == '\0' ) {
DEBUGLOG( "entrylist[servIndex].ip[0] == 0" );
continue;
}
else {
DEBUGLOG( "trying %s", entrylist[servIndex].ip );
}
if ( inHexHash != NULL && entrylist[servIndex].id[0] != '\0' && strcmp( inHexHash, entrylist[servIndex].id ) != 0 ) {
DEBUGLOG("rejecting %s based on id", entrylist[servIndex].ip);
continue;
}
portNumStr = strchr( entrylist[servIndex].ip, ':' );
if ( portNumStr != NULL )
{
*portNumStr = '\0';
strlcpy(entrylist[servIndex].port, portNumStr+1, 10);
usingDefaultPort106 = false;
}
else
{
strcpy(entrylist[servIndex].port, "106");
}
siResult = 0;
fallbackToTCP = false;
if ( ! entrylist[servIndex].ipFromNode )
{
DEBUGLOG("testing %s:%s", entrylist[servIndex].ip, kPasswordServerPortStr);
siResult = testconn_udp( entrylist[servIndex].ip, kPasswordServerPortStr, &socketList[servIndex] );
DEBUGLOG( "testconn_udp result = %d, sock=%d", (int)siResult, socketList[servIndex] );
checkUDPDescriptors |= ( siResult == 0 );
fallbackToTCP = ( siResult != 0 );
siResult = 0;
}
if ( entrylist[servIndex].ipFromNode || fallbackToTCP )
{
DEBUGLOG("testing %s:%s", entrylist[servIndex].ip, entrylist[servIndex].port);
tempsock = 0;
siResult = getconn_async( entrylist[servIndex].ip, entrylist[servIndex].port, &tcpOpenTimeout, &connectTime, &tempsock );
DEBUGLOG( "getconn_async = %ld", siResult );
if ( siResult == kCPSUtilOK )
{
char tstr[256];
sprintf(tstr, "Connect time: %.3f", connectTime );
DEBUGLOG( "%s", tstr );
connectedSocket = tempsock;
connectedSocketIsTCP = true;
memcpy( outReplica, &entrylist[servIndex], sizeof(sPSServerEntry) );
}
}
if ( checkUDPDescriptors && socketList != NULL )
{
socklen_t structlength;
int byteCount = 0;
int descCount = 0;
struct sockaddr_in cin;
char packetData[64];
fd_set fdset;
struct timeval selectTimeout = { 0, (servIndex==0) ? 250000 : 130000 };
bzero( &cin, sizeof(cin) );
cin.sin_family = AF_INET;
cin.sin_addr.s_addr = htonl( INADDR_ANY );
cin.sin_port = htons( 0 );
FD_ZERO( &fdset );
for ( descIndex = 0; descIndex < servCount; descIndex++ )
if ( socketList[descIndex] != -1 )
FD_SET( socketList[descIndex], &fdset );
descCount = select( FD_SETSIZE, &fdset, NULL, NULL, &selectTimeout );
DEBUGLOG( "select = %d", descCount );
if ( descCount > 0 )
{
for ( descIndex = 0; descIndex < servCount && connectedSocket == -1; descIndex++ )
{
if ( socketList[descIndex] > 0 )
{
structlength = sizeof( cin );
byteCount = recvfrom( socketList[descIndex], packetData, sizeof(packetData) - 1,
MSG_DONTWAIT, (struct sockaddr *)&cin, &structlength );
DEBUGLOG( "recvfrom() byteCount=%d", byteCount );
if ( byteCount > 0 && packetData[0] != '0' )
{
if ( inHexHash != NULL && byteCount > 33 )
{
packetData[byteCount] = '\0';
char *serverHash = strchr( packetData, ';' );
if ( serverHash != NULL )
{
serverHash++;
char *endHashPtr = strchr( serverHash, ';' );
if ( endHashPtr != NULL )
*endHashPtr = '\0';
long hashLen = strlen( serverHash );
if ( hashLen == 32 && strcmp( inHexHash, serverHash ) != 0 )
continue;
}
}
connectedSocket = socketList[descIndex];
memcpy( outReplica, &entrylist[descIndex], sizeof(sPSServerEntry) );
if ( usingDefaultPort106 )
strcpy( outReplica->port, kPasswordServerPortStr );
break;
}
}
}
}
}
}
if ( socketList != NULL )
{
for ( descIndex = 0; descIndex < servCount; descIndex++ )
if ( socketList[descIndex] > 0 )
close( socketList[descIndex] );
free( socketList );
socketList = NULL;
}
}
catch( long error )
{
siResult = error;
}
catch( ... )
{
siResult = kCPSUtilServiceUnavailable;
}
if ( siResult == 0 && connectedSocket <= 0 )
siResult = kCPSUtilServiceUnavailable;
if ( connectedSocketIsTCP )
*outSock = connectedSocket;
DEBUGLOG( "IdentifyRR returning %d", (int)siResult );
return siResult;
}
long ConvertCFArrayToServerArray( CFArrayRef inCFArray, sPSServerEntry **outServerArray, CFIndex *outCount )
{
CFDataRef serverRef;
sPSServerEntry *entrylist = NULL;
CFIndex servIndex;
CFIndex servCount = 0;
const UInt8 *bytePtr = NULL;
if ( inCFArray == NULL || outServerArray == NULL || outCount == NULL )
{
DEBUGLOG( "ConvertCFArrayToServerArray called with a NULL parameter." );
return kCPSUtilParameterError;
}
*outServerArray = NULL;
*outCount = 0;
servCount = CFArrayGetCount( inCFArray );
DEBUGLOG( "Server list contains %d servers.", (int)servCount );
if ( servCount > 0 )
{
entrylist = (sPSServerEntry *) calloc( servCount, sizeof(sPSServerEntry) );
if ( entrylist == NULL ) return kCPSUtilMemoryError;
for ( servIndex = 0; servIndex < servCount; servIndex++ )
{
serverRef = (CFDataRef) CFArrayGetValueAtIndex( inCFArray, servIndex );
if ( serverRef == NULL ) {
DEBUGLOG( "serverRef == NULL" );
continue;
}
bytePtr = CFDataGetBytePtr( serverRef );
if ( bytePtr != NULL ) {
memcpy( &(entrylist[servIndex]), bytePtr, sizeof(sPSServerEntry) );
DEBUGLOG( "entrylist[%d].ip=%s, entrylist[].id=%s, ipFromNode=%d", servIndex, entrylist[servIndex].ip, entrylist[servIndex].id, entrylist[servIndex].ipFromNode );
}
}
}
*outServerArray = entrylist;
*outCount = servCount;
return kCPSUtilOK;
}
long GetBigNumber( sPSContextData *inContext, char **outBigNumStr )
{
long siResult = kCPSUtilOK;
BIGNUM *nonce = NULL;
char *bnStr = NULL;
if ( inContext == NULL || outBigNumStr == NULL )
return kCPSUtilParameterError;
*outBigNumStr = NULL;
nonce = BN_new();
if ( nonce == NULL )
return kCPSUtilMemoryError;
BN_rand(nonce, 256, 0, 0);
bnStr = BN_bn2dec(nonce);
BN_clear_free(nonce);
if ( bnStr == NULL )
return kCPSUtilMemoryError;
DEBUGLOG( "nonce = %s", bnStr);
*outBigNumStr = bnStr;
return siResult;
}
PWServerError
SendFlush(
sPSContextData *inContext,
const char *inCommandStr,
const char *inArg1Str,
const char *inArg2Str )
{
PWServerError result = { kAuthOK, kPolicyError };
char *commandBuf;
if ( inCommandStr == NULL ) {
result.err = kAuthFail;
return result;
}
commandBuf = SendFlushReadAssembleCommand( inCommandStr, inArg1Str, inArg2Str );
if ( commandBuf == NULL ) {
result.err = kAuthFail;
return result;
}
if ( inContext->castKeySet && !inContext->isUNIXDomainSocket )
writeToServerWithCASTKey( inContext->serverOut, commandBuf, &inContext->castKey, inContext->castIV );
else
writeToServer( inContext->serverOut, commandBuf );
free( commandBuf );
return result;
}
PWServerError
SendFlushRead(
sPSContextData *inContext,
const char *inCommandStr,
const char *inArg1Str,
const char *inArg2Str,
char *inOutBuf,
unsigned long inBufLen
)
{
PWServerError result = { kAuthOK, kPolicyError };
if ( inCommandStr == NULL || inOutBuf == NULL ) {
result.err = kAuthFail;
return result;
}
result = SendFlush( inContext, inCommandStr, inArg1Str, inArg2Str );
if ( result.err == kAuthOK )
{
if ( inContext->castKeySet && !inContext->isUNIXDomainSocket )
result = readFromServerWithCASTKey( inContext->fd, inOutBuf, inBufLen, &inContext->castKey, inContext->castReceiveIV );
else
result = readFromServer( inContext->fd, inOutBuf, inBufLen );
}
return result;
}
char *SendFlushReadAssembleCommand(
const char *inCommandStr,
const char *inArg1Str,
const char *inArg2Str )
{
long commandsize = 0;
char *commandBuf = NULL;
if ( inCommandStr == NULL)
return NULL;
commandsize = strlen( inCommandStr ) + 4 + CAST_BLOCK;
if ( inArg1Str != NULL )
commandsize += strlen( inArg1Str ) + 1;
if ( inArg2Str != NULL )
commandsize += strlen( inArg2Str ) + 1;
commandBuf = (char *) malloc( commandsize );
if ( commandBuf == NULL )
return NULL;
if ( inArg1Str == NULL )
sprintf( commandBuf, "%s\r\n", inCommandStr );
else
if ( inArg2Str == NULL )
sprintf( commandBuf, "%s %s\r\n", inCommandStr, inArg1Str );
else
sprintf( commandBuf, "%s %s %s\r\n", inCommandStr, inArg1Str, inArg2Str );
return commandBuf;
}
void StripRSAKey( char *inOutUserID )
{
if ( inOutUserID == NULL )
return;
char *delim = strchr( inOutUserID, ',' );
if ( delim )
*delim = '\0';
}
long
GetPasswordServerList( CFMutableArrayRef *outServerList, int inConfigSearchOptions )
{
long status = 0;
long status1 = 0;
CFMutableArrayRef serverArray;
DEBUGLOG( "GetPasswordServerList");
if ( outServerList == NULL )
return kAuthFail;
*outServerList = NULL;
if ( inConfigSearchOptions == 0 )
return 0;
serverArray = CFArrayCreateMutable( kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks );
if ( serverArray == NULL )
return kAuthFail;
if ( inConfigSearchOptions & kPWSearchLocalFile )
status = GetServerListFromLocalCache( serverArray );
if ( inConfigSearchOptions & kPWSearchReplicaFile )
{
status1 = GetServerListFromFile( serverArray );
if ( status1 != noErr && status == noErr )
status = status1;
}
if ( CFArrayGetCount(serverArray) > 0 )
{
*outServerList = serverArray;
status = 0;
}
else
{
CFRelease( serverArray );
}
return status;
}
long
GetPasswordServerListForKeyHash( CFMutableArrayRef *outServerList, int inConfigSearchOptions, const char *inKeyHash )
{
long status = 0;
long status1 = 0;
CFMutableArrayRef serverArray;
DEBUGLOG( "GetPasswordServerListForKeyHash");
if ( outServerList == NULL )
return kAuthFail;
*outServerList = NULL;
if ( inConfigSearchOptions == 0 )
return 0;
serverArray = CFArrayCreateMutable( kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks );
if ( serverArray == NULL )
return kAuthFail;
if ( inConfigSearchOptions & kPWSearchLocalFile )
status = GetServerListFromLocalCache( serverArray );
if ( inConfigSearchOptions & kPWSearchReplicaFile )
{
status1 = GetServerListFromFileForKeyHash( serverArray, inKeyHash );
if ( status1 != noErr && status == noErr )
status = status1;
}
if ( CFArrayGetCount(serverArray) > 0 )
{
*outServerList = serverArray;
status = 0;
}
else
{
CFRelease( serverArray );
}
return status;
}
long
GetServerListFromLocalCache( CFMutableArrayRef inOutServerList )
{
CFStringRef myReplicaDataFilePathRef;
CFURLRef myReplicaDataFileRef;
CFReadStreamRef myReadStreamRef;
CFPropertyListRef myPropertyListRef = NULL;
CFStringRef errorString = NULL;
CFPropertyListFormat myPLFormat;
CFIndex index, arrayCount;
CFDataRef dataRef;
long status = 0;
sPSServerEntry *anEntryPtr;
bool bLoadPreConfiguredFile = false;
struct stat sb;
DEBUGLOG( "GetServerListFromLocalCache");
if ( lstat( kPWReplicaPreConfiguredFile, &sb ) == 0 )
{
bLoadPreConfiguredFile = true;
}
else
{
if ( lstat( kPWReplicaLocalFile, &sb ) != 0 )
return kAuthFail;
bLoadPreConfiguredFile = false;
}
if ( bLoadPreConfiguredFile )
{
CReplicaFile replicaFile( true, kPWReplicaPreConfiguredFile );
status = GetServerListFromXML( &replicaFile, inOutServerList );
DEBUGLOG( "loaded manual config file = %d", (int)status );
}
else
{
myReplicaDataFilePathRef = CFStringCreateWithCString( kCFAllocatorDefault, kPWReplicaLocalFile, kCFStringEncodingUTF8 );
if ( myReplicaDataFilePathRef == NULL )
return kAuthFail;
myReplicaDataFileRef = CFURLCreateWithFileSystemPath( kCFAllocatorDefault, myReplicaDataFilePathRef, kCFURLPOSIXPathStyle, false );
CFRelease( myReplicaDataFilePathRef );
if ( myReplicaDataFileRef == NULL )
return kAuthFail;
myReadStreamRef = CFReadStreamCreateWithFile( kCFAllocatorDefault, myReplicaDataFileRef );
CFRelease( myReplicaDataFileRef );
if ( myReadStreamRef == NULL )
return kAuthFail;
if ( CFReadStreamOpen( myReadStreamRef ) )
{
myPLFormat = kCFPropertyListXMLFormat_v1_0;
myPropertyListRef = CFPropertyListCreateFromStream( kCFAllocatorDefault, myReadStreamRef, 0, kCFPropertyListMutableContainersAndLeaves, &myPLFormat, &errorString );
CFReadStreamClose( myReadStreamRef );
}
CFRelease( myReadStreamRef );
if ( errorString != NULL )
{
char errMsg[256];
if ( CFStringGetCString( errorString, errMsg, sizeof(errMsg), kCFStringEncodingUTF8 ) )
DEBUGLOG( "could not load the local replica cache file, error = %s", errMsg );
CFRelease( errorString );
}
if ( myPropertyListRef == NULL )
return kAuthFail;
if ( CFGetTypeID(myPropertyListRef) != CFArrayGetTypeID() )
{
CFRelease( myPropertyListRef );
return kAuthFail;
}
arrayCount = CFArrayGetCount( (CFArrayRef) myPropertyListRef );
for ( index = 0; index < arrayCount; index++ )
{
dataRef = (CFDataRef) CFArrayGetValueAtIndex( (CFArrayRef) myPropertyListRef, index );
if ( dataRef == NULL )
continue;
anEntryPtr = (sPSServerEntry *) CFDataGetBytePtr( dataRef );
if ( anEntryPtr->lastContact )
{
if ( index > 0 )
CFArrayExchangeValuesAtIndices( (CFMutableArrayRef) myPropertyListRef, index, 0 );
break;
}
}
if ( arrayCount > 0 )
CFArrayAppendArray( inOutServerList, (CFArrayRef) myPropertyListRef, CFRangeMake(0, arrayCount) );
CFRelease( myPropertyListRef );
}
return status;
}
long
GetServerListFromFile( CFMutableArrayRef inOutServerList )
{
return GetServerListFromFileForKeyHash( inOutServerList, NULL );
}
long
GetServerListFromFileForKeyHash( CFMutableArrayRef inOutServerList, const char *inKeyHash )
{
long status = 0;
CReplicaFile *replicaFile = NULL;
sPSServerEntry serverEntry;
bool gotID;
replicaFile = new CReplicaFile();
if ( replicaFile == NULL )
return kAuthFail;
bzero( &serverEntry, sizeof(sPSServerEntry) );
gotID = replicaFile->GetUniqueID( serverEntry.id );
if ( (gotID) && (replicaFile->ReplicaCount() == 0) )
{
if ( (inKeyHash == NULL) || (strcmp(inKeyHash, serverEntry.id) == 0) )
{
strcpy( serverEntry.ip, "127.0.0.1" );
strcpy( serverEntry.port, kPasswordServerPortStr );
AppendToArrayIfUnique( inOutServerList, &serverEntry );
delete replicaFile;
return 0;
}
}
if ( inKeyHash != NULL )
{
if ( (!gotID) || (strcmp( inKeyHash, serverEntry.id ) != 0) )
{
char filePath[sizeof(kPWReplicaRemoteFilePrefix) + strlen(inKeyHash)];
strcpy( filePath, kPWReplicaRemoteFilePrefix );
strcat( filePath, inKeyHash );
delete replicaFile;
replicaFile = new CReplicaFile( true, filePath );
if ( replicaFile == NULL )
return 0;
}
}
status = GetServerListFromXML( replicaFile, inOutServerList );
DEBUGLOG( "GetServerListFromFile = %d", (int)status);
delete replicaFile;
return status;
}
long GetServerListFromConfig( CFMutableArrayRef *outServerList, CReplicaFile *inReplicaData )
{
long status = 0;
CFMutableArrayRef serverArray;
DEBUGLOG( "GetServerListFromConfig");
if ( outServerList == NULL || inReplicaData == NULL )
return kAuthFail;
*outServerList = NULL;
serverArray = CFArrayCreateMutable( kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks );
if ( serverArray == NULL )
return kAuthFail;
status = GetServerListFromXML( inReplicaData, serverArray );
if ( CFArrayGetCount(serverArray) > 0 )
{
*outServerList = serverArray;
status = 0;
}
else
{
CFRelease( serverArray );
}
return status;
}
long
GetServerListFromXML( CReplicaFile *inReplicaFile, CFMutableArrayRef inOutServerList )
{
CFDictionaryRef serverDict = NULL;
CFStringRef ldapServerString = NULL;
UInt32 repIndex;
UInt32 repCount;
int ipIndex = 0;
long status = 0;
sPSServerEntry serverEntry;
char serverID[33];
char ldapServerStr[256] = {0};
DEBUGLOG( "in GetServerListFromXML");
if ( inOutServerList == NULL )
return kAuthFail;
bzero( &serverEntry, sizeof(sPSServerEntry) );
ldapServerString = inReplicaFile->CurrentServerForLDAP();
if ( ldapServerString != NULL )
{
CFStringGetCString( ldapServerString, ldapServerStr, sizeof(ldapServerStr), kCFStringEncodingUTF8 );
CFRelease( ldapServerString );
}
if ( inReplicaFile->GetUniqueID( serverID ) )
strcpy( serverEntry.id, serverID );
DEBUGLOG( "serverEntry.id=%s", serverEntry.id);
serverDict = inReplicaFile->GetParent();
if ( serverDict != NULL )
{
for ( ipIndex = 0, status = 0; status == 0; ipIndex++ )
{
status = GetServerFromDict( serverDict, ipIndex, &serverEntry );
if ( status == 0 )
{
if ( strcmp(serverEntry.ip, ldapServerStr) == 0 )
serverEntry.currentServerForLDAP = true;
AppendToArrayIfUnique( inOutServerList, &serverEntry );
}
}
}
repCount = inReplicaFile->ReplicaCount();
DEBUGLOG( "repCount=%d", (int)repCount);
for ( repIndex = 0; repIndex < repCount; repIndex++ )
{
serverDict = inReplicaFile->GetReplica( repIndex );
if ( serverDict != NULL )
{
for ( ipIndex = 0, status = 0; status == 0; ipIndex++ )
{
status = GetServerFromDict( serverDict, ipIndex, &serverEntry );
if ( status == 0 )
{
if ( strcmp(serverEntry.ip, ldapServerStr) == 0 )
serverEntry.currentServerForLDAP = true;
AppendToArrayIfUnique( inOutServerList, &serverEntry );
}
}
}
}
status = ( (CFArrayGetCount(inOutServerList) > 0) ? 0 : kAuthFail );
DEBUGLOG( "GetServerListFromXML = %d", (int)status);
return status;
}
long
GetServerFromDict( CFDictionaryRef serverDict, int inIPIndex, sPSServerEntry *outServerEntry )
{
long status = 0;
CFTypeRef anIPRef;
CFStringRef aString;
DEBUGLOG( "GetServerListFromDict");
if ( serverDict == NULL || outServerEntry == NULL )
return kAuthFail;
if ( ! CFDictionaryGetValueIfPresent( serverDict, CFSTR(kPWReplicaIPKey), (const void **)&anIPRef ) )
return kAuthFail;
if ( CFGetTypeID(anIPRef) == CFStringGetTypeID() )
{
if ( inIPIndex != 0 )
return kAuthFail;
if ( ! CFStringGetCString( (CFStringRef)anIPRef, outServerEntry->ip, sizeof(outServerEntry->ip), kCFStringEncodingUTF8 ) )
return kAuthFail;
}
else
if ( CFGetTypeID(anIPRef) == CFArrayGetTypeID() )
{
if ( inIPIndex >= CFArrayGetCount((CFArrayRef)anIPRef) )
return kAuthFail;
aString = (CFStringRef) CFArrayGetValueAtIndex( (CFArrayRef)anIPRef, inIPIndex );
if ( aString == NULL || CFGetTypeID(aString) != CFStringGetTypeID() )
return kAuthFail;
if ( ! CFStringGetCString( aString, outServerEntry->ip, sizeof(outServerEntry->ip), kCFStringEncodingUTF8 ) )
return kAuthFail;
}
if ( CFDictionaryGetValueIfPresent( serverDict, CFSTR("DNS"), (const void **)&aString ) &&
CFGetTypeID(aString) != CFStringGetTypeID() )
{
CFStringGetCString( aString, outServerEntry->dns, sizeof(outServerEntry->dns), kCFStringEncodingUTF8 );
}
DEBUGLOG( "GetServerListFromDict = %d", (int)status);
return status;
}
int SaveLocalReplicaCache( CFMutableArrayRef inReplicaArray, sPSServerEntry *inLastContactEntry )
{
CFIndex index, replicaCount;
CFMutableDataRef dataRef;
sPSServerEntry *anEntryPtr;
if ( inReplicaArray == NULL )
return -1;
replicaCount = CFArrayGetCount( inReplicaArray );
if ( replicaCount == 0 )
return -1;
for ( index = 0; index < replicaCount; index++ )
{
dataRef = (CFMutableDataRef) CFArrayGetValueAtIndex( inReplicaArray, index );
if ( dataRef == NULL )
continue;
anEntryPtr = (sPSServerEntry *)CFDataGetMutableBytePtr( dataRef );
if ( anEntryPtr == NULL )
continue;
anEntryPtr->lastContact = ( strcmp( anEntryPtr->ip, inLastContactEntry->ip ) == 0 );
if ( anEntryPtr->lastContact && anEntryPtr->id[0] == '\0' )
memcpy( anEntryPtr->id, inLastContactEntry->id, sizeof(anEntryPtr->id) );
}
return CReplicaFile::SaveXMLData( (CFPropertyListRef) inReplicaArray, kPWReplicaLocalFile );
}
void AppendToArrayIfUnique( CFMutableArrayRef inArray, sPSServerEntry *inServerEntry )
{
CFIndex serverIndex, serverCount;
CFDataRef serverRef;
sPSServerEntry anEntry;
sPSServerEntry *anEntryPtr;
serverCount = CFArrayGetCount( inArray );
for ( serverIndex = 0; serverIndex < serverCount; serverIndex++ )
{
serverRef = (CFDataRef) CFArrayGetValueAtIndex( inArray, serverIndex );
if ( serverRef == NULL )
continue;
memcpy( &anEntry, CFDataGetBytePtr(serverRef), sizeof(anEntry) );
if ( strcmp( inServerEntry->ip, anEntry.ip ) == 0 )
return;
}
anEntryPtr = (sPSServerEntry *) malloc( sizeof(sPSServerEntry) );
if ( anEntryPtr == NULL )
return;
memcpy( anEntryPtr, inServerEntry, sizeof(sPSServerEntry) );
serverRef = CFDataCreateWithBytesNoCopy( kCFAllocatorDefault, (const unsigned char *)anEntryPtr, sizeof(sPSServerEntry), kCFAllocatorMalloc );
if ( serverRef != NULL ) {
CFArrayAppendValue( inArray, serverRef );
CFRelease( serverRef );
}
DEBUGLOG( "AppendToArrayIfUnique adding: %s %s", inServerEntry->ip, inServerEntry->id );
}
ReplicaIPLevel ReplicaPriority( sPSServerEntry *inReplica, unsigned long *iplist )
{
unsigned char s1, s2;
char *ipStr = inReplica->ip;
struct in_addr ipAddr, ourIPAddr;
int index;
if ( inReplica->currentServerForLDAP )
return kReplicaIPSet_CurrentForLDAP;
if ( strcmp( ipStr, "127.0.0.1" ) == 0 )
return kReplicaIPSet_LocallyHosted;
if ( inet_pton( AF_INET, ipStr, &ipAddr ) != 1 )
return kReplicaIPSet_Wide;
for ( index = 0; index < kMaxIPAddrs && iplist[index] != 0; index++ )
{
if ( ipAddr.s_addr == iplist[index] )
return kReplicaIPSet_LocallyHosted;
}
ipAddr.s_addr = (ipAddr.s_addr & 0xFFFFFF00);
for ( index = 0; index < kMaxIPAddrs && iplist[index] != 0; index++ )
{
ourIPAddr.s_addr = (iplist[index] & 0xFFFFFF00);
if ( ourIPAddr.s_addr == ipAddr.s_addr )
return kReplicaIPSet_InSubnet;
}
s1 = *((unsigned char *)&ipAddr.s_addr);
s2 = *(((unsigned char *)&ipAddr.s_addr) + 1);
if ( s1 == 10 )
return kReplicaIPSet_PrivateNet;
if ( s1 == 172 && s2 <= 31 && s2 >= 16 )
return kReplicaIPSet_PrivateNet;
if ( s1 == 192 && s2 == 168 )
return kReplicaIPSet_PrivateNet;
return kReplicaIPSet_Wide;
}
bool ReplicaInIPSet( sPSServerEntry *inReplica, ReplicaIPLevel inLevel )
{
unsigned char s1, s2;
char *ipStr = inReplica->ip;
long err = 0;
struct in_addr ipAddr, ourIPAddr;
unsigned long *iplist = NULL;
int index;
if ( inLevel == kReplicaIPSet_Wide )
return true;
if ( inLevel == kReplicaIPSet_CurrentForLDAP )
return inReplica->currentServerForLDAP;
if ( strcmp( ipStr, "127.0.0.1" ) == 0 )
return (inLevel == kReplicaIPSet_LocallyHosted);
if ( inet_pton( AF_INET, ipStr, &ipAddr ) != 1 )
return false;
err = pwsf_LocalIPList( &iplist );
if ( err == kCPSUtilOK && iplist != NULL )
{
for ( index = 0; index < kMaxIPAddrs && iplist[index] != 0; index++ )
{
if ( ipAddr.s_addr == iplist[index] )
{
free( iplist );
return (inLevel == kReplicaIPSet_LocallyHosted);
}
}
}
if ( inLevel == kReplicaIPSet_LocallyHosted ) {
if ( iplist != NULL )
free( iplist );
return false;
}
if ( err == kCPSUtilOK && iplist != NULL )
{
ipAddr.s_addr = (ipAddr.s_addr & 0xFFFFFF00);
for ( index = 0; index < kMaxIPAddrs && iplist[index] != 0; index++ )
{
ourIPAddr.s_addr = (iplist[index] & 0xFFFFFF00);
if ( ourIPAddr.s_addr == ipAddr.s_addr )
{
free( iplist );
return (inLevel == kReplicaIPSet_InSubnet);
}
}
free( iplist );
iplist = NULL;
}
if ( inLevel == kReplicaIPSet_InSubnet )
return false;
s1 = *((unsigned char *)&ipAddr.s_addr);
s2 = *(((unsigned char *)&ipAddr.s_addr) + 1);
if ( s1 == 10 )
return (inLevel == kReplicaIPSet_PrivateNet);
if ( s1 == 172 && s2 <= 31 && s2 >= 16 )
return (inLevel == kReplicaIPSet_PrivateNet);
if ( s1 == 192 && s2 == 168 )
return (inLevel == kReplicaIPSet_PrivateNet);
if ( inLevel == kReplicaIPSet_PrivateNet )
return false;
return false;
}
long pwsf_LocalIPList( unsigned long **outIPList )
{
struct ifconf ifc;
struct ifreq ifrbuf[30];
struct ifreq *ifrptr;
struct sockaddr_in *sain;
unsigned long *iplist;
register int sock = 0;
register int i = 0;
register int ipcount = 0;
int rc = 0;
long result = kCPSUtilOK;
if ( outIPList == NULL )
return kCPSUtilParameterError;
*outIPList = NULL;
iplist = (unsigned long *) calloc( sizeof(unsigned long), kMaxIPAddrs + 1 );
if ( iplist == NULL )
return kCPSUtilMemoryError;
try
{
sock = socket(AF_INET, SOCK_DGRAM, 0);
if ( sock == -1 )
throw(1);
ifc.ifc_buf = (caddr_t)ifrbuf;
ifc.ifc_len = sizeof(ifrbuf);
rc = ::ioctl(sock, SIOCGIFCONF, &ifc);
close( sock );
if ( rc == -1 )
throw(1);
ipcount = 0;
for ( ifrptr = (struct ifreq *)ifc.ifc_buf, i=0;
(char *) ifrptr < &ifc.ifc_buf[ifc.ifc_len] && i < kMaxIPAddrs;
ifrptr = IFR_NEXT(ifrptr), i++ )
{
if ( ifrptr->ifr_addr.sa_family == AF_INET )
{
sain = (struct sockaddr_in *)&(ifrptr->ifr_addr);
iplist[ipcount] = ntohl(sain->sin_addr.s_addr);
ipcount++;
}
}
} catch ( ... )
{
if ( iplist != NULL ) {
free( iplist );
iplist = NULL;
}
result = kCPSUtilFail;
}
*outIPList = iplist;
return result;
}
long getconn_async( const char *host, const char *port, struct timeval *inOpenTimeout, float *outConnectTime, int *inOutSocket )
{
char servername[1024];
struct sockaddr_in sin;
struct addrinfo *res, *res0;
int sock = -1;
long siResult = 0;
int rc, err;
struct in_addr inetAddr;
char *endPtr = NULL;
struct timeval startTime, endTime;
struct timeval recvTimeoutVal = { 30, 0 };
struct timeval sendTimeoutVal = { 120, 0 };
struct timezone tz = { 0, 0 };
fd_set fdset;
int fcntlFlags = 0;
bool bOurSocketToCloseOnErr = false;
if ( host==NULL || port==NULL || inOutSocket==NULL )
return kCPSUtilParameterError;
if ( outConnectTime != NULL )
*outConnectTime = 0;
try
{
if ( *inOutSocket == 0 )
{
strlcpy(servername, host, sizeof(servername));
rc = inet_aton(servername, &inetAddr);
if ( rc == 1 )
{
sin.sin_addr.s_addr = inetAddr.s_addr;
}
else
{
err = getaddrinfo( servername, NULL, NULL, &res0 );
if (err != 0) {
DEBUGLOG("getaddrinfo");
throw((long)kCPSUtilServiceUnavailable);
}
for ( res = res0; res != NULL; res = res->ai_next )
{
if ( res->ai_family != AF_INET || res->ai_addrlen != sizeof(sockaddr_in) )
continue;
memcpy( &sin.sin_addr, &(((struct sockaddr_in *)(res->ai_addr))->sin_addr.s_addr), 4 );
}
freeaddrinfo( res0 );
}
sin.sin_port = htons(strtol(port, &endPtr, 10));
if ((sin.sin_port == 0) || (endPtr == port)) {
DEBUGLOG( "port '%s' unknown", port);
throw((long)kCPSUtilParameterError);
}
sin.sin_family = AF_INET;
for ( int dontgetzero = 0; dontgetzero < 5; dontgetzero++ )
{
sock = socket(AF_INET, SOCK_STREAM, 0);
if (sock < 0) {
DEBUGLOG("socket");
throw((long)kCPSUtilServiceUnavailable);
}
if ( sock != 0 )
break;
}
if ( sock == 0 )
{
DEBUGLOG("socket() keeps giving me zero. hate that!");
throw((long)kCPSUtilServiceUnavailable);
}
gOpenCount++;
bOurSocketToCloseOnErr = true;
if ( setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &recvTimeoutVal, sizeof(recvTimeoutVal) ) == -1 )
{
DEBUGLOG("setsockopt SO_RCVTIMEO");
throw((long)kCPSUtilServiceUnavailable);
}
if ( setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, &sendTimeoutVal, sizeof(sendTimeoutVal) ) == -1 )
{
DEBUGLOG("setsockopt SO_SNDTIMEO");
}
fcntlFlags = fcntl(sock, F_GETFL, 0);
if ( fcntlFlags == -1 )
{
DEBUGLOG("fcntl");
throw((long)kCPSUtilServiceUnavailable);
}
if ( fcntl(sock, F_SETFL, fcntlFlags | O_NONBLOCK) == -1 )
{
DEBUGLOG("fcntl");
throw((long)kCPSUtilServiceUnavailable);
}
}
else
{
sock = *inOutSocket;
}
gettimeofday( &startTime, &tz );
siResult = connect(sock, (struct sockaddr *) &sin, sizeof (sin));
if ( fcntl(sock, F_SETFL, fcntlFlags) == -1 )
{
DEBUGLOG("fcntl");
throw((long)kCPSUtilServiceUnavailable);
}
if ( siResult == -1 )
{
FD_ZERO(&fdset);
FD_SET(sock, &fdset);
siResult = select(FD_SETSIZE, NULL, &fdset, NULL, inOpenTimeout);
if ( siResult > 0 )
{
char test;
errno = 0;
siResult = recvfrom( sock, &test, 1, (MSG_PEEK | MSG_DONTWAIT), NULL, NULL );
DEBUGLOG( "getconn_async recvfrom = %d, errno = %d", (int)siResult, errno );
if ( siResult > 0 || (siResult == -1 && errno == EAGAIN) )
{
siResult = 0;
}
else
{
close( sock );
sock = -1;
gOpenCount--;
throw((long)kCPSUtilServiceUnavailable);
}
}
else
{
close( sock );
sock = -1;
gOpenCount--;
throw((long)kCPSUtilServiceUnavailable);
}
}
gettimeofday( &endTime, &tz );
if ( outConnectTime != NULL )
*outConnectTime = (endTime.tv_sec - startTime.tv_sec) + (float)(endTime.tv_usec - startTime.tv_usec)/(float)1000000;
}
catch( long error )
{
siResult = error;
}
if ( siResult != 0 && bOurSocketToCloseOnErr && sock != -1 )
{
close( sock );
sock = -1;
gOpenCount--;
}
else
*inOutSocket = sock;
return siResult;
}
long testconn_udp( const char *host, const char *port, int *outSocket )
{
char servername[1024];
struct sockaddr_in sin, cin;
int sock = -1;
long siResult = 0;
int rc;
struct in_addr inetAddr;
char *endPtr = NULL;
const char *packetData = "What do you know?";
ssize_t byteCount;
struct addrinfo *res, *res0;
if ( host==NULL || port==NULL || outSocket==NULL )
return kCPSUtilParameterError;
try
{
strlcpy(servername, host, sizeof(servername));
rc = inet_aton(servername, &inetAddr);
if ( rc == 1 )
{
sin.sin_addr.s_addr = inetAddr.s_addr;
}
else
{
rc = getaddrinfo( servername, NULL, NULL, &res0 );
if (rc != 0) {
DEBUGLOG("getaddrinfo");
throw((long)kCPSUtilServiceUnavailable);
}
for ( res = res0; res != NULL; res = res->ai_next )
{
if ( res->ai_family != AF_INET || res->ai_addrlen != sizeof(sockaddr_in) )
continue;
memcpy( &sin.sin_addr, &(((struct sockaddr_in *)(res->ai_addr))->sin_addr.s_addr), 4 );
}
freeaddrinfo( res0 );
}
sin.sin_port = htons(strtol(port, &endPtr, 10));
if ((sin.sin_port == 0) || (endPtr == port)) {
DEBUGLOG( "port '%s' unknown", port);
throw((long)kCPSUtilParameterError);
}
sin.sin_family = AF_INET;
for ( int dontgetzero = 0; dontgetzero < 5; dontgetzero++ )
{
sock = socket(AF_INET, SOCK_DGRAM, 0);
if (sock < 0) {
DEBUGLOG("socket");
throw((long)kCPSUtilServiceUnavailable);
}
if ( sock != 0 )
break;
}
if ( sock == 0 )
{
DEBUGLOG("socket() keeps giving me zero. hate that!");
throw((long)kCPSUtilServiceUnavailable);
}
bzero( &cin, sizeof(cin) );
cin.sin_family = AF_INET;
cin.sin_addr.s_addr = htonl( INADDR_ANY );
cin.sin_port = htons( 0 );
siResult = bind( sock, (struct sockaddr *) &cin, sizeof(cin) );
if ( siResult < 0 )
{
DEBUGLOG( "bind() failed." );
throw( (long)kCPSUtilServiceUnavailable );
}
byteCount = sendto( sock, packetData, strlen(packetData), 0, (struct sockaddr *)&sin, sizeof(sin) );
if ( byteCount < 0 )
{
DEBUGLOG( "sendto() failed." );
throw( (long)kCPSUtilServiceUnavailable );
}
}
catch( long error )
{
if ( error != 0 && sock > 0 )
{
close( sock );
sock = -1;
}
siResult = error;
}
*outSocket = sock;
return siResult;
}
pid_t pwsf_ProcessIsRunning( const char *inProcName )
{
register size_t i ;
register pid_t pidLast = -1 ;
int mib[] = { CTL_KERN, KERN_PROC, KERN_PROC_ALL, 0 };
size_t ulSize = 0;
if ( 0 > sysctl( mib, 4, NULL, &ulSize, NULL, 0) )
{
return( pidLast );
}
i = ulSize / sizeof( struct kinfo_proc );
struct kinfo_proc *kpspArray = new kinfo_proc[ i ];
if ( !kpspArray )
{
return( pidLast );
}
ulSize = i * sizeof( struct kinfo_proc );
if ( 0 > sysctl( mib, 4, kpspArray, &ulSize, NULL, 0 ) )
{
delete [] kpspArray;
return( pidLast );
}
register struct kinfo_proc *kpsp = kpspArray;
for ( ; i-- ; kpsp++ )
{
if ( strcmp( kpsp->kp_proc.p_comm, inProcName ) != 0 )
{
continue;
}
if ( kpsp->kp_proc.p_pid == ::getpid() )
{
continue;
}
if ( kpsp->kp_proc.p_stat == SZOMB )
{
continue;
}
if ( strcmp( kpsp->kp_proc.p_comm, inProcName ) == 0 )
{
pidLast = kpsp->kp_proc.p_pid;
break;
}
}
delete [] kpspArray;
return( pidLast );
}
bool pwsf_GetSASLMechInfo( const char *inMechName, char **outPluginPath, bool *outRequiresPlainTextOnDisk )
{
int index;
bool found = false;
SASLMechInfo knownMechList[] =
{
{"APOP", "apop.la", true},
{"CRAM-MD5", "libcrammd5.la", false},
{"CRYPT", "crypt.la", false},
{"DHX", "dhx.la", false},
{"DIGEST-MD5", "libdigestmd5.la", false},
{"GSSAPI", "libgssapiv2.la", false},
{"KERBEROS_V4", "libkerberos4.la", false},
{"MS-CHAPv2", "mschapv2.la", false},
{"NTLM", "libntlm.la", false},
{"OTP", "libotp.la", false},
{"PPS", "libpps.la", false},
{"SMB-LAN-MANAGER", "smb_lm.la", false},
{"SMB-NT", "smb_nt.la", false},
{"SMB-NTLMv2", "smb_ntlmv2.la", false},
{"TWOWAYRANDOM", "twowayrandom.la", true},
{"WEBDAV-DIGEST", "digestmd5WebDAV.la", true},
{"", "", false}
};
for ( index = 0; knownMechList[index].name[0] != '\0'; index++ )
{
if ( strcasecmp(inMechName, knownMechList[index].name) == 0 )
{
if ( outPluginPath != NULL ) {
*outPluginPath = (char *) malloc( strlen(knownMechList[index].filename) + 1 );
if ( *outPluginPath != NULL ) {
strcpy( *outPluginPath, knownMechList[index].filename );
}
}
if ( outRequiresPlainTextOnDisk != NULL )
*outRequiresPlainTextOnDisk = knownMechList[index].requiresPlain;
found = true;
break;
}
}
return found;
}
int pwsf_mkdir_p( const char *path, mode_t mode )
{
int err = 0;
char buffer[PATH_MAX];
char *segPtr;
char *inPtr;
int len = snprintf( buffer, sizeof(buffer), "%s", path );
if ( len >= (int)sizeof(buffer) - 1 )
return -1;
inPtr = buffer;
if ( *inPtr == '/' )
inPtr++;
while ( inPtr != NULL )
{
segPtr = strsep( &inPtr, "/" );
if ( segPtr != NULL )
{
err = mkdir( buffer, mode );
if ( err != 0 && errno != EEXIST )
break;
err = 0;
if ( inPtr != NULL )
*(inPtr - 1) = '/';
}
}
return err;
}
int EnumerateDirectory( const char *inDirPath, const char *inStartsWith, CFMutableArrayRef *outFileArray )
{
return pwsf_EnumerateDirectory( inDirPath, inStartsWith, outFileArray );
}
int pwsf_EnumerateDirectory( const char *inDirPath, const char *inStartsWith, CFMutableArrayRef *outFileArray )
{
char prefix[PATH_MAX+2] = {0,};
char str[PATH_MAX] = {0,};
char c;
int pos;
int position;
DIR *dp;
struct dirent *dir;
size_t minFileLength = 0;
CFMutableArrayRef lArray = NULL;
CFMutableStringRef filePathRef;
if ( inDirPath == NULL || outFileArray == NULL )
return -1;
if ( inStartsWith != NULL )
minFileLength = strlen( inStartsWith );
lArray = CFArrayCreateMutable( kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks );
if ( lArray == NULL )
return -1;
position = 0;
do
{
pos = 0;
do {
c = inDirPath[position];
position++;
str[pos] = c;
pos++;
}
while ((c != ':') && (c != '=') && (c != 0));
str[pos-1] = '\0';
strcpy( prefix, str );
strcat( prefix, "/" );
dp = opendir( str );
if ( dp != NULL )
{
while ( (dir = readdir(dp)) != NULL )
{
size_t length;
if ( dir->d_type != DT_REG )
continue;
length = strlen( dir->d_name );
if ( length < minFileLength )
continue;
if ( length + pos >= PATH_MAX )
continue;
if ( inStartsWith != NULL && strncmp(dir->d_name, inStartsWith, minFileLength) != 0 )
continue;
filePathRef = CFStringCreateMutable( kCFAllocatorDefault, 0 );
if ( filePathRef == NULL )
return -1;
CFStringAppendCString( filePathRef, prefix, kCFStringEncodingUTF8 );
CFStringAppendCString( filePathRef, dir->d_name, kCFStringEncodingUTF8 );
CFArrayAppendValue( lArray, filePathRef );
CFRelease( filePathRef );
filePathRef = NULL;
}
closedir( dp );
}
}
while ( (c != '=') && (c != 0) );
*outFileArray = lArray;
return 0;
}
int pwsf_LaunchTask(const char *path, char *const argv[])
{
int outputPipe[2] = {0};
pid_t pid = -1;
int status = -1;
int waitResult = 0;
pipe(outputPipe);
pid = vfork();
if (pid == -1)
return -1;
if (pid == 0)
{
dup2(outputPipe[1], fileno(stdout));
execv(path, argv);
_exit(1);
}
waitResult = waitpid( pid, &status, 0 );
if ( waitResult <= 0 )
status = 0;
else
if ( waitResult > 0 && WIFEXITED(status) )
status = WEXITSTATUS( status );
close(outputPipe[1]);
close(outputPipe[0]);
return status;
}
int pwsf_LaunchTaskWithIO(
const char *path,
char *const argv[],
const char* inputBuf,
char* outputBuf,
int outputBufSize,
bool *outExitedBeforeInput)
{
int inputPipe[2];
int outputPipe[2];
pid_t pid;
int status;
int waitResult = 0;
bool exitedBeforeInput = false;
if (inputBuf != NULL)
pipe(inputPipe);
if (outputBuf != NULL)
pipe(outputPipe);
pid = vfork();
if (pid == -1)
return -1;
if (pid == 0)
{
if (inputBuf != NULL)
dup2(inputPipe[0], fileno(stdin));
if (outputBuf != NULL)
dup2(outputPipe[1], fileno(stdout));
execv(path, argv);
_exit(1);
}
if (inputBuf != NULL)
{
close(inputPipe[0]);
waitResult = waitpid( pid, &status, WNOHANG );
if ( waitResult == -1 )
{
switch(errno)
{
case ECHILD:
case EFAULT:
case EINVAL:
exitedBeforeInput = true;
break;
case EINTR:
default:
break;
}
}
else
{
exitedBeforeInput = (waitResult == pid);
}
if ( !exitedBeforeInput )
write(inputPipe[1], inputBuf, strlen(inputBuf));
close(inputPipe[1]);
}
if ( ! exitedBeforeInput )
{
waitResult = waitpid( pid, &status, WNOHANG );
for ( int waitCount = 0; waitResult == 0 || waitResult == -1; waitCount++ )
{
if ( waitResult == -1 )
{
if ( errno == ECHILD ) {
break;
}
else if ( errno == EFAULT || errno == EINVAL ) {
status = -4;
break;
}
}
if ( waitCount > (100*60*10) )
{
kill( pid, SIGKILL );
waitResult = waitpid( pid, &status, 0 );
status = -4;
break;
}
usleep(10000);
waitResult = waitpid( pid, &status, WNOHANG );
if ( status == -4 )
break;
}
}
if (outputBuf != NULL)
{
int sizeRead;
close(outputPipe[1]);
sizeRead = read(outputPipe[0], outputBuf, outputBufSize-1);
outputBuf[sizeRead] = 0;
close(outputPipe[0]);
}
if ( status != -4 )
status = WEXITSTATUS(status);
if (outExitedBeforeInput != NULL)
*outExitedBeforeInput = exitedBeforeInput;
return status;
}
int pwsf_LaunchTaskWithIO2(
const char *path,
char *const argv[],
const char* inputBuf,
char* outputBuf,
int outputBufSize,
char* errBuf,
int errBufSize)
{
int inputPipe[2];
int outputPipe[2];
int errPipe[2];
pid_t pid;
int status;
int waitResult = 0;
bool exitedBeforeInput = false;
int waitCount = 0;
if (inputBuf != NULL)
pipe(inputPipe);
if (outputBuf != NULL)
pipe(outputPipe);
if (errBuf != NULL)
pipe(errPipe);
pid = vfork();
if (pid == -1)
return -1;
if (pid == 0)
{
if (inputBuf != NULL)
dup2(inputPipe[0], fileno(stdin));
if (outputBuf != NULL)
dup2(outputPipe[1], fileno(stdout));
if (errBuf != NULL)
dup2(errPipe[1], fileno(stderr));
execv(path, argv);
_exit(1);
}
if (inputBuf != NULL)
{
close(inputPipe[0]);
waitResult = waitpid( pid, &status, WNOHANG );
if ( waitResult == -1 )
{
switch(errno)
{
case ECHILD:
case EFAULT:
case EINVAL:
exitedBeforeInput = true;
break;
case EINTR:
default:
break;
}
}
else
{
exitedBeforeInput = (waitResult == pid);
}
if ( !exitedBeforeInput )
write(inputPipe[1], inputBuf, strlen(inputBuf));
close(inputPipe[1]);
}
if ( ! exitedBeforeInput )
{
waitResult = waitpid( pid, &status, WNOHANG );
for ( waitCount = 0; waitResult == 0 || waitResult == -1; waitCount++ )
{
if ( waitResult == -1 )
{
if ( errno == ECHILD ) {
break;
}
else if ( errno == EFAULT || errno == EINVAL ) {
status = -4;
break;
}
}
if ( waitCount > (100*60*10) )
{
kill( pid, SIGKILL );
waitResult = waitpid( pid, &status, 0 );
status = -4;
break;
}
usleep(10000);
waitResult = waitpid( pid, &status, WNOHANG );
if ( status == -4 )
break;
}
}
if (outputBuf != NULL)
{
int sizeRead;
close(outputPipe[1]);
sizeRead = read(outputPipe[0], outputBuf, outputBufSize-1);
outputBuf[sizeRead] = 0;
close(outputPipe[0]);
}
if (errBuf != NULL)
{
int sizeRead;
close(errPipe[1]);
sizeRead = read(errPipe[0], errBuf, errBufSize-1);
errBuf[sizeRead] = 0;
close(errPipe[0]);
}
if ( status != -4 )
status = WEXITSTATUS(status);
return status;
}