/* * Copyright (c) 2004 Apple Computer, Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * * This file contains Original Code and/or Modifications of Original Code * as defined in and that are subject to the Apple Public Source License * Version 2.0 (the 'License'). You may not use this file except in * compliance with the License. Please obtain a copy of the License at * http://www.opensource.apple.com/apsl/ and read it before using this * file. * * The Original Code and all software distributed under the License are * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. * Please see the License for the specific language governing rights and * limitations under the License. * * @APPLE_LICENSE_HEADER_END@ */ #include "AppleOD.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "imap_err.h" #include "xmalloc.h" #include "libconfig.h" #include #include #include #include #include #include #include #include #include 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 ); /* ----------------------------------------------------------- - odGetUserOpts Get user options from user record * ----------------------------------------------------------- */ 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 ); } } } /* clean up */ odFreeUserOpts( inOutOpts, 0 ); /* set default settings */ inOutOpts->fAccountState = eUnknownState; inOutOpts->fDiskQuota = 0; /* get user options for user record or service acl */ iResult = sGetUserOptions( inUserID, inOutOpts ); /* do we have a valid user record name */ if ( (iResult == eDSNoErr) && (inOutOpts->fRecNamePtr != NULL) ) { /* set the user ID */ inOutOpts->fUserIDPtr = malloc( strlen( inUserID ) + 1 ); if ( inOutOpts->fUserIDPtr != NULL ) { strlcpy( inOutOpts->fUserIDPtr, inUserID, strlen( inUserID ) + 1 ); } /* are mail service ACL's enabled */ checkServiceACL( inOutOpts, "mail" ); } /* if we failed to find a user record, set record name to user id and mark user opts with unknown user */ 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 ); } /* odGetUserOpts */ /* ----------------------------------------------------------- - odFreeUserOpts Free user opts memory * ----------------------------------------------------------- */ 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; } } } } /* odFreeUserOpts */ /* ----------------------------------------------------------------- odCheckPass () - inUserOpts must contain: - valid directory reference - valid user name - valid user node meta location ----------------------------------------------------------------- */ 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 ); } /* odCheckPass */ /* ----------------------------------------------------------------- odCheckAPOP () - APOP digets apopuser 3ea069c07cb39843c34e9fe7e0dee9ea: ----------------------------------------------------------------- */ 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 ]; /* check for bogus data */ if ( (inChallenge == NULL) || (inResponse == NULL) ) { syslog( LOG_DEBUG, "AOD: APOP: configuration error: empty challenger or response" ); return( iResult ); } /* extract the user ID from the response */ p = strchr( inResponse, ' ' ); if ( (p == NULL) || (strspn(p + 1, "0123456789abcdef") != 32) ) { syslog( LOG_DEBUG, "AOD: APOP: parameter error: bad digest: %s", inResponse ); return( eAODParamErr ); } /* user ID length */ len = (int)(p - inResponse); if ( len < MAX_USER_BUF_SIZE ) { memset( userBuf, 0, MAX_USER_BUF_SIZE ); memcpy( userBuf, inResponse, len ); /* get user options */ odGetUserOpts( userBuf, inOutOpts ); /* consuem any spaces */ while ( *p == ' ' ) { p++; } if ( p != NULL ) { /* make the call */ 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; } /* something went wrong before we could make the call */ return( iResult ); } /* odCheckAPOP */ /* ----------------------------------------------------------------- odCRAM_MD5 () ----------------------------------------------------------------- */ int odCRAM_MD5 ( const char *inChallenge, const char *inResponse, struct od_user_opts *inOutOpts ) { return( sValidateResponse( inChallenge, inResponse, kDSStdAuthCRAM_MD5, inOutOpts ) ); } /* odCRAM_MD5 */ /* ----------------------------------------------------------------- odDoAuthenticate () ----------------------------------------------------------------- */ 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 ); } /* odDoAuthenticate */ /* ----------------------------------------------------------------- odIsMember () - Does user (short name) a member of this group (short name) ----------------------------------------------------------------- */ 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 ); } /* odIsMember */ /* ----------------------------------------------------------------- sDoLoginAuth () C: A1 AUTHENTICATE LOGIN S: + VXNlciBOYW1lAA== C: bWl0bGlzdEB3d3cuc2FwaG9tZS5jb20= S: + UGFzc3dvcmQA C: c2piZmxvdw== S: A01 OK Success ----------------------------------------------------------------- */ 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 ]; /* is LOGIN authentication enabled */ if ( !config_getswitch( IMAPOPT_IMAP_AUTH_LOGIN ) ) { syslog( LOG_DEBUG, "AOD: LOGIN: configuration error: LOGIN authentication not enabled" ); return ( eAODMethodNotEnabled ); } /* encode the user name prompt and send it */ strcpy( chalBuf, "Username:" ); sEncodeBase64( chalBuf, strlen( chalBuf ), ioBuf, MAX_IO_BUF_SIZE ); prot_printf( inStreamOut, "+ %s\r\n", ioBuf ); /* reset the buffer */ memset( ioBuf, 0, MAX_IO_BUF_SIZE ); /* get the client response */ if ( prot_fgets( ioBuf, MAX_IO_BUF_SIZE, inStreamIn ) ) { /* trim CRLF */ p = ioBuf + strlen( ioBuf ) - 1; if ( (p >= ioBuf) && (*p == '\n') ) { *p-- = '\0'; } if ( (p >= ioBuf) && (*p == '\r') ) { *p-- = '\0'; } /* check if client cancelled */ if ( ioBuf[ 0 ] == '*' ) { syslog( LOG_DEBUG, "AOD: LOGIN: user canceled auth attempt" ); return( eAODAuthCanceled ); } } /* reset the buffer */ memset( userBuf, 0, MAX_USER_BUF_SIZE ); len = strlen( ioBuf ); sDecodeBase64( ioBuf, len, userBuf, MAX_USER_BUF_SIZE, &respLen ); /* get user options */ odGetUserOpts( userBuf, inOutOpts ); /* encode the password prompt and send it */ strcpy( chalBuf, "Password:" ); sEncodeBase64( chalBuf, strlen( chalBuf ), ioBuf, MAX_IO_BUF_SIZE ); prot_printf( inStreamOut, "+ %s\r\n", ioBuf ); /* get the client response */ if ( prot_fgets( ioBuf, MAX_IO_BUF_SIZE, inStreamIn ) ) { /* trim CRLF */ p = ioBuf + strlen( ioBuf ) - 1; if ( (p >= ioBuf) && (*p == '\n') ) { *p-- = '\0'; } if ( (p >= ioBuf) && (*p == '\r') ) { *p-- = '\0'; } /* check if client cancelled */ if ( ioBuf[ 0 ] == '*' ) { syslog( LOG_DEBUG, "AOD: LOGIN: user canceled auth attempt" ); return( eAODAuthCanceled ); } } /* reset the buffer */ memset( pwdBuf, 0, MAX_USER_BUF_SIZE ); len = strlen( ioBuf ); sDecodeBase64( ioBuf, len, pwdBuf, MAX_USER_BUF_SIZE, &respLen ); /* do the auth */ iResult = odCheckPass( pwdBuf, inOutOpts ); /* clear stack buffers */ memset( pwdBuf, 0, MAX_USER_BUF_SIZE ); memset( ioBuf, 0, MAX_IO_BUF_SIZE ); return( iResult ); } /* sDoLoginAuth */ /* ----------------------------------------------------------------- sDoLoginAuthS () C: AUTHENTICATE "LOGIN" S: {12} S: VXNlcm5hbWU6 Please enter your password: C: {12+} YHJaY3VeYW== S: {12} S: UGFzc3dvcmQ6 C: {12+} YHJaY3VeYW== ----------------------------------------------------------------- */ 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 ]; /* is LOGIN authentication enabled */ if ( !config_getswitch( IMAPOPT_IMAP_AUTH_LOGIN ) ) { syslog( LOG_DEBUG, "AOD: LOGIN: configuration error: LOGIN authentication not enabled" ); return ( eAODMethodNotEnabled ); } /* encode the user name prompt and send it */ 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 ); } /* reset the buffer */ memset( userBuf, 0, MAX_USER_BUF_SIZE ); len = strlen( ioBuf ); sDecodeBase64( ioBuf, len, userBuf, MAX_USER_BUF_SIZE, &respLen ); /* get user options */ odGetUserOpts( userBuf, inOutOpts ); /* encode the password prompt and send it */ 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 ); } /* reset the buffer */ memset( pwdBuf, 0, MAX_USER_BUF_SIZE ); len = strlen( ioBuf ); sDecodeBase64( ioBuf, len, pwdBuf, MAX_USER_BUF_SIZE, &respLen ); /* do the auth */ iResult = odCheckPass( pwdBuf, inOutOpts ); /* clear stack buffers */ memset( pwdBuf, 0, MAX_USER_BUF_SIZE ); memset( ioBuf, 0, MAX_IO_BUF_SIZE ); return( iResult ); } /* sDoLoginAuthS */ /* ----------------------------------------------------------------- sDoPlainAuth () C: A01 AUTHENTICATE PLAIN dGVzdAB0ZXN0AHRlc3Q= S: A01 OK Success or ... C: A01 AUTHENTICATE PLAIN (note that there is a space following the "+" in the following line) S: + C: dGVzdAB0ZXN0AHRlc3Q= S: A01 OK Success message = [authorize_id] UTF8NULL userName UTF8NULL passwd authenticate-id = 1*UTF8-SAFE ; MUST accept up to 255 octets authorize-id = 1*UTF8-SAFE ; MUST accept up to 255 octets password = 1*UTF8-SAFE ; MUST accept up to 255 octets ----------------------------------------------------------------- */ 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 ]; /* is PLAIN authentication enabled */ 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 ); /* get the client response */ if ( prot_fgets( ioBuf, MAX_IO_BUF_SIZE, inStreamIn ) ) { /* trim CRLF */ p = ioBuf + strlen( ioBuf ) - 1; if ( (p >= ioBuf) && (*p == '\n') ) { *p-- = '\0'; } if ( (p >= ioBuf) && (*p == '\r') ) { *p-- = '\0'; } /* check if client cancelled */ if ( ioBuf[ 0 ] == '*' ) { syslog( LOG_DEBUG, "AOD: PLAIN: user canceled auth attempt" ); return( eAODAuthCanceled ); } } } /* reset the buffer */ memset( respBuf, 0, MAX_IO_BUF_SIZE ); /* decode the response */ len = strlen( ioBuf ); sDecodeBase64( ioBuf, len, respBuf, MAX_IO_BUF_SIZE, &respLen ); p = respBuf; if ( *p != '\0' ) { /* get the authenticate-id */ strlcpy( authName, p, MAX_USER_BUF_SIZE ); /* skip past authenticate-id and NUL */ p = p + (strlen( authName ) + 1 ); } else { /* no authenticate-id sent, skip first NUL */ p++; } /* is response still valid */ if ( (p != NULL) && (strlen( p ) < MAX_USER_BUF_SIZE) ) { /* get authorize-id (user id) */ strlcpy( userBuf, p, MAX_USER_BUF_SIZE ); /* get user options */ odGetUserOpts( userBuf, inOutOpts ); /* skip past authorize-id and NUL */ p = p + (strlen( userBuf ) + 1 ); /* is response still valid */ if ( (p != NULL) && (strlen( p ) < MAX_USER_BUF_SIZE) ) { /* get password */ strlcpy( passwd, p, MAX_USER_BUF_SIZE ); /* do the auth */ iResult = odCheckPass( passwd, inOutOpts ); } } /* clear stack buffers */ memset( passwd, 0, MAX_USER_BUF_SIZE ); memset( respBuf, 0, MAX_IO_BUF_SIZE ); return( iResult ); } /* sDoPlainAuth */ /* ----------------------------------------------------------------- sDoPlainAuthS () C: A01 AUTHENTICATE PLAIN dGVzdAB0ZXN0AHRlc3Q= S: A01 OK Success or ... C: A01 AUTHENTICATE PLAIN (note that there is a space following the "+" in the following line) S: + C: dGVzdAB0ZXN0AHRlc3Q= S: A01 OK Success or ... C: AUTHENTICATE "PLAIN" {24+} bWlrZWQAbWlrZWQAbWlrZWQ= S: A01 OK Success or ... C: AUTH PLAIN dGVzdAB0ZXN0QHdpei5leGFtcGxlLmNvbQB0RXN0NDI= message = [authName] UTF8NULL userName UTF8NULL passwd authenticate-id = 1*UTF8-SAFE ; MUST accept up to 255 octets authorize-id = 1*UTF8-SAFE ; MUST accept up to 255 octets password = 1*UTF8-SAFE ; MUST accept up to 255 octets ----------------------------------------------------------------- */ 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 ]; //authenticate-id char userBuf [ MAX_USER_BUF_SIZE ]; // user to authorize char passwd [ MAX_USER_BUF_SIZE ]; /* is PLAIN authentication enabled */ 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 ); /* get the client response */ if ( prot_fgets( ioBuf, MAX_IO_BUF_SIZE, inStreamIn ) ) { /* trim CRLF */ p = ioBuf + strlen( ioBuf ) - 1; if ( (p >= ioBuf) && (*p == '\n') ) { *p-- = '\0'; } if ( (p >= ioBuf) && (*p == '\r') ) { *p-- = '\0'; } /* check if client cancelled */ if ( ioBuf[ 0 ] == '*' ) { syslog( LOG_DEBUG, "AOD: SIEVE-PLAIN: user canceled auth attempt" ); return( eAODAuthCanceled ); } } } /* null set buffers */ memset( respBuf, 0, MAX_IO_BUF_SIZE ); memset( userBuf, 0, MAX_USER_BUF_SIZE ); /* decode the response */ len = strlen( ioBuf ); sDecodeBase64( ioBuf, len, respBuf, MAX_IO_BUF_SIZE, &respLen ); p = respBuf; if ( *p != '\0' ) { /* get the authenticate-id */ strlcpy( userBuf, p, MAX_USER_BUF_SIZE ); /* skip past authenticate-id and NUL */ p = p + (strlen( userBuf ) + 1 ); } else { /* no authenticate-id sent, skip first NUL */ p++; } /* is response still valid */ if ( (p != NULL) && (strlen( p ) < MAX_USER_BUF_SIZE) ) { /* get authorize-id (user id) */ strlcpy( authBuf, p, MAX_USER_BUF_SIZE ); /* get user options */ odGetUserOpts( authBuf, inOutOpts ); /* skip past authorize-id and NUL */ p = p + (strlen( authBuf ) + 1 ); /* is response still valid */ if ( (p != NULL) && (strlen( p ) < MAX_USER_BUF_SIZE) ) { /* get password */ strlcpy( passwd, p, MAX_USER_BUF_SIZE ); /* do the auth */ 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; } } } } /* clear stack buffers */ memset( passwd, 0, MAX_USER_BUF_SIZE ); memset( respBuf, 0, MAX_IO_BUF_SIZE ); return( iResult ); } /* sDoPlainAuthS */ /* ----------------------------------------------------------------- sDoCRAM_MD5_AuthS () ----------------------------------------------------------------- */ 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 ]; /* is CRAM-MD5 auth enabled */ if ( !config_getswitch( IMAPOPT_IMAP_AUTH_CRAM_MD5 ) ) { syslog( LOG_DEBUG, "AOD: CRAM-MD5: configuration error: CRAM-MD5 authentication not enabled" ); return ( eAODMethodNotEnabled ); } /* create the challenge */ gethostname( hostname, sizeof( hostname ) ); gettimeofday (&tvChalTime, NULL); /* get random data string */ get_random_chars( randbuf, 17 ); sprintf( chalBuf, "<%lu.%s.%lu@%s>", (unsigned long) getpid(), randbuf, (unsigned long)time(0), hostname ); /* encode the challenge and send it */ 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 ); } /* reset the buffer */ memset( respBuf, 0, MAX_IO_BUF_SIZE ); /* decode the response */ 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 ); /* get the user name */ 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 ); } /* copy user name from response buf */ memset( userBuf, 0, MAX_USER_BUF_SIZE ); memcpy( userBuf, respBuf, len ); syslog( LOG_DEBUG, "AOD: SIEVE-CRAM-MD5: user name: %s", userBuf ); /* get user options */ odGetUserOpts( userBuf, inOutOpts ); /* move past the space */ if ( ++p != NULL ) { /* validate the response */ 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 ); } /* sDoCRAM_MD5_AuthS */ /* ----------------------------------------------------------------- sDoCRAM_MD5_Auth () ----------------------------------------------------------------- */ 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 ]; /* is CRAM-MD5 auth enabled */ if ( !config_getswitch( IMAPOPT_IMAP_AUTH_CRAM_MD5 ) ) { syslog( LOG_DEBUG, "AOD: CRAM-MD5: configuration error: CRAM-MD5 authentication not enabled" ); return ( eAODMethodNotEnabled ); } /* create the challenge */ gethostname( hostname, sizeof( hostname ) ); gettimeofday (&tvChalTime, NULL); /* get random data string */ get_random_chars( randbuf, 17 ); sprintf( chalBuf, "<%lu.%s.%lu@%s>", (unsigned long) getpid(), randbuf, (unsigned long)time(0), hostname ); /* encode the challenge and send it */ sEncodeBase64( chalBuf, strlen( chalBuf ), ioBuf, MAX_IO_BUF_SIZE ); prot_printf( inStreamOut, "+ %s\r\n", ioBuf ); /* reset the buffer */ memset( ioBuf, 0, MAX_IO_BUF_SIZE ); /* get the client response */ if ( prot_fgets( ioBuf, MAX_IO_BUF_SIZE, inStreamIn ) ) { /* trim CRLF */ p = ioBuf + strlen( ioBuf ) - 1; if ( (p >= ioBuf) && (*p == '\n') ) { *p-- = '\0'; } if ( (p >= ioBuf) && (*p == '\r') ) { *p-- = '\0'; } /* check if client cancelled */ if ( ioBuf[ 0 ] == '*' ) { syslog( LOG_DEBUG, "AOD: CRAM-MD5: user canceled auth attempt" ); return( eAODAuthCanceled ); } } /* reset the buffer */ memset( respBuf, 0, MAX_IO_BUF_SIZE ); /* decode the response */ 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 ); /* get the user name */ 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 ); } /* copy user name from response buf */ memset( userBuf, 0, MAX_USER_BUF_SIZE ); memcpy( userBuf, respBuf, len ); /* get user options */ odGetUserOpts( userBuf, inOutOpts ); /* move past the space */ if ( ++p != NULL ) { /* validate the response */ 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 ); } /* sDoCRAM_MD5_Auth */ 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 ); } /* clear out the buffer */ memset( inOutBuf, 0, inBufSize ); c = prot_getc( inStreamIn ); if ( c == EOF ) { return( eAODLostConnection ); } /* is it a quoted string */ if ( c == '\"' ) { while ( true ) { c = prot_getc( inStreamIn ); if ( c == EOF ) { return( eAODLostConnection ); } if ( c == '\"' ) { return( eAODNoErr ); } /* illegal characters */ if ( (c == '\0') || (c == '\r') || (c == '\n') || (0x7F < ((unsigned char)c)) ) { syslog( LOG_DEBUG, "AOD: GetClientResponse: illegal character: %c", c ); return( eAODInvalidDataType ); } /* escaped character */ if ( c == '\\' ) { c = prot_getc( inStreamIn ); if ( (c != '\"') && (c != '\\') ) { syslog( LOG_DEBUG, "AOD: GetClientResponse: illegal escape character: %c", c ); return( eAODInvalidDataType ); } } /* are we at the end of the buffer */ if ( bufPtr > bufEnd ) { syslog( LOG_DEBUG, "AOD: GetClientResponse: exceeded buffer" ); return( eAODInvalidDataType ); } *bufPtr++ = c; } } /* is it a literal */ else if ( c == '{' ) { /* get the octet count */ 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 == '}' ) { /* now get the \r\n */ 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; } /* all is not well */ syslog( LOG_DEBUG, "AOD: GetClientResponse: bad sequence in {n+}"); return( eAODInvalidDataType ); } else { /* we need to see a +} to be valid */ 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 ); } /* get the literal */ bufEnd = inOutBuf + len; while ( bufPtr < bufEnd ) { *bufPtr++ = prot_getc( inStreamIn ); } /* now get the \r\n */ 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 ); } /* sGetClientResponse */ /* ----------------------------------------------------------------- sGetUserOptions () ----------------------------------------------------------------- */ 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 ); } /* turns out that DS can fail with invalid ref during _any_ call we can recover by trying again. However, don't retry forever */ for ( i = 1; i < 4; i++ ) { /* reset to eDSNoErr if dir ref is valid */ dsStatus = eDSNoErr; /* no need to close dir ref if still valid */ 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" ); /* open search node */ dsStatus = sGet_search_node( inOutOpts->fDirRef, &inOutOpts->fSearchNodeRef ); } } /* look up the user record */ if ( dsStatus == eDSNoErr ) { syslog( LOG_DEBUG, "AOD: user opts: looking up user record: %s", inUserID ); /* get user attributes from applemail attribute */ dsStatus = sGet_user_attributes( inOutOpts->fDirRef, inOutOpts->fSearchNodeRef, inUserID, inOutOpts ); } /* if ref is invalid, try to get user options again */ if ( dsStatus != eDSInvalidReference ) { break; } syslog( LOG_WARNING, "AOD Warning: Directory reference is invalid, retrying (%d)", i ); /* wait and try again */ sleep( 1 ); } return( dsStatus ); } /* sGetUserOptions */ /* ----------------------------------------------------------------- checkServiceACL () ----------------------------------------------------------------- */ 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 ) ); /* we should already have this from previous user lookup */ if ( inUserOpts->fUserUUID != NULL ) { err = mbr_string_to_uuid( (const char *)inUserOpts->fUserUUID, userUUID ); } else { /* get the uuid for user */ err = mbr_user_name_to_uuid( inUserOpts->fRecNamePtr, userUUID ); } if ( err != eAODNoErr ) { /* couldn't turn user into uuid settings form user record */ syslog( LOG_DEBUG, "AOD: service ACL: mbr_user_name_to_uuid failed for user: %s (%s)", inUserOpts->fRecNamePtr, strerror( err ) ); return( -1 ); } /* check the mail service ACL */ err = mbr_check_service_membership( userUUID, inGroup, &result ); if ( err == ENOENT ) { /* look for all services acl */ syslog( LOG_DEBUG, "AOD: mbr_check_service_membership with access_all_services" ); err = mbr_check_service_membership( userUUID, "access_all_services", &result ); } /* service ACL is enabled */ if ( err == eAODNoErr ) { /* check membership */ if ( result != 0 ) { /* we are a member, enable all mail services */ /* preserve any auto-forwarding settings */ if ( inUserOpts->fAccountState & eAutoForwardedEnabled ) { syslog( LOG_DEBUG, "AOD: valid service ACL member: auto-forward" ); inUserOpts->fAccountState &= ~eIMAPEnabled; inUserOpts->fAccountState &= ~ePOPEnabled; } else { /* enable all mail services */ syslog( LOG_DEBUG, "AOD: valid service ACL member: normal access" ); inUserOpts->fAccountState |= eAccountEnabled; inUserOpts->fAccountState |= eIMAPEnabled; inUserOpts->fAccountState |= ePOPEnabled; } } else { /* we are not a member override any settings form user record */ 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 ); } /* checkServiceACL */ /* ----------------------------------------------------------------- sDoesUserBelongToGroup () ----------------------------------------------------------------- */ 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 ); } /* sDoesUserBelongToGroup */ /* ----------------------------------------------------------------- sOpen_ds () ----------------------------------------------------------------- */ tDirStatus sOpen_ds ( tDirReference *inOutDirRef ) { tDirStatus dsStatus = eDSNoErr; dsStatus = dsOpenDirService( inOutDirRef ); return( dsStatus ); } /* sOpen_ds */ /* ----------------------------------------------------------------- sGet_search_node () ----------------------------------------------------------------- */ 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 ); } /* sGet_search_node */ /* ----------------------------------------------------------------- sGet_user_attributes () ----------------------------------------------------------------- */ 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 { /* Get the user record(s) that matches the user id */ dsStatus = dsGetRecordList( inSearchNodeRef, pTDataBuff, &tdlRecName, eDSiExact, pUserRecType, pUserAttrType, FALSE, &uiRecCount, &pContext ); if ( dsStatus == eDSNoErr ) { dsStatus = eDSInvalidName; /* do we have more than 1 match */ if ( uiRecCount == 1 ) { dsStatus = dsGetRecordEntry( inSearchNodeRef, pTDataBuff, 1, &attrListRef, &pRecEntry ); if ( dsStatus == eDSNoErr ) { /* Get the attributes we care about for the record */ 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 ) { /* Only get the first attribute value */ dsStatus = dsGetAttributeValue( inSearchNodeRef, pTDataBuff, 1, valueRef, &pValueEntry ); if ( dsStatus == eDSNoErr ) { syslog( LOG_DEBUG, "AOD: getting mail attribute for user: %s", inUserID ); /* Get the individual mail attribute values */ sGet_mail_values( (char *)pValueEntry->fAttributeValueData.fBufferData, inOutOpts ); /* If we don't find duplicate users in the same node, we take the first one with a valid mail attribute */ done = true; } } else if ( strcasecmp( pAttrEntry->fAttributeSignature.fBufferData, kDSNAttrRecordName ) == 0 ) { /* Only get the first attribute value */ dsStatus = dsGetAttributeValue( inSearchNodeRef, pTDataBuff, 1, valueRef, &pValueEntry ); if ( dsStatus == eDSNoErr ) { /* Get the user record name */ 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 ) { /* Only get the first attribute value */ dsStatus = dsGetAttributeValue( inSearchNodeRef, pTDataBuff, 1, valueRef, &pValueEntry ); if ( dsStatus == eDSNoErr ) { /* Get the user location */ 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 ) { /* Only get the first attribute value */ dsStatus = dsGetAttributeValue( inSearchNodeRef, pTDataBuff, 1, valueRef, &pValueEntry ); if ( dsStatus == eDSNoErr ) { /* Get the user location */ 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 ); } /* sGet_user_attributes */ /* ----------------------------------------------------------------- sOpen_user_node () ----------------------------------------------------------------- */ 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 ); } /* sOpen_user_node */ /* ----------------------------------------------------------------- sDoCryptAuth () ----------------------------------------------------------------- */ 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 ) { /* We don't use this buffer for clear text auth */ pStepBuff = dsDataBufferAllocate( inDirRef, 256 ); if ( pStepBuff != NULL ) { pAuthType = dsDataNodeAllocateString( inDirRef, kDSStdAuthNodeNativeClearTextOK ); if ( pAuthType != NULL ) { /* set user name */ len = nameLen; memcpy( &(pAuthBuff->fBufferData[ curr ]), &len, sizeof( unsigned long ) ); curr += sizeof( unsigned long ); memcpy( &(pAuthBuff->fBufferData[ curr ]), inUserID, len ); curr += len; /* set user password */ 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 ); } /* sDoCryptAuth */ /* ----------------------------------------------------------------- sValidateResponse () ----------------------------------------------------------------- */ 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 ) { /* User name */ uiLen = uiNameLen; memcpy( &(pAuthBuff->fBufferData[ uiCurr ]), &uiLen, sizeof( unsigned long ) ); uiCurr += sizeof( unsigned long ); memcpy( &(pAuthBuff->fBufferData[ uiCurr ]), inUserOpts->fRecNamePtr, uiLen ); uiCurr += uiLen; /* Challenge */ uiLen = uiChalLen; memcpy( &(pAuthBuff->fBufferData[ uiCurr ]), &uiLen, sizeof( unsigned long ) ); uiCurr += sizeof( unsigned long ); memcpy( &(pAuthBuff->fBufferData[ uiCurr ]), inChallenge, uiLen ); uiCurr += uiLen; /* Response */ 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 ); } /* sValidateResponse */ /* ----------------------------------------------------------------- sGet_mail_values () ----------------------------------------------------------------- */ 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 ); } } } /* sGet_mail_values */ /* ----------------------------------------------------------------- sVerify_version () ----------------------------------------------------------------- */ 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 ); } /* sVerify_version */ /* ----------------------------------------------------------------- sGet_acct_state () ----------------------------------------------------------------- */ void sGet_acct_state ( CFDictionaryRef inCFDictRef, struct od_user_opts *inOutOpts ) { CFStringRef cfStringRef = NULL; char *pValue = NULL; /* Default value */ 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" ); } } } } } } /* sGet_acct_state */ /* ----------------------------------------------------------------- sGet_auto_forward () ----------------------------------------------------------------- */ 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; } } } } /* sGet_auto_forward */ /* ----------------------------------------------------------------- sGet_IMAP_login () ----------------------------------------------------------------- */ void sGet_IMAP_login ( CFDictionaryRef inCFDictRef, struct od_user_opts *inOutOpts ) { CFStringRef cfStringRef = NULL; char *pValue = NULL; /* Default value */ 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; } } } } /* sGet_IMAP_login */ /* ----------------------------------------------------------------- sGet_POP3_login () ----------------------------------------------------------------- */ void sGet_POP3_login ( CFDictionaryRef inCFDictRef, struct od_user_opts *inOutOpts ) { CFStringRef cfStringRef = NULL; char *pValue = NULL; /* Default value */ 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; } } } } /* sGet_POP3_login */ /* ----------------------------------------------------------------- sGet_acct_loc () ----------------------------------------------------------------- */ void sGet_acct_loc ( CFDictionaryRef inCFDictRef, struct od_user_opts *inOutOpts ) { CFStringRef cfStringRef = NULL; char *pValue = NULL; /* don't leak previous value (if any) */ 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 ); } } } } } /* sGet_acct_loc */ /* ----------------------------------------------------------------- sGet_alt_loc () ----------------------------------------------------------------- */ void sGet_alt_loc ( CFDictionaryRef inCFDictRef, struct od_user_opts *inOutOpts ) { CFStringRef cfStringRef = NULL; char *pValue = NULL; /* Default value */ 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 ); } } } } } } /* sGet_alt_loc */ /* ----------------------------------------------------------------- sGet_disk_quota () ----------------------------------------------------------------- */ void sGet_disk_quota ( CFDictionaryRef inCFDictRef, struct od_user_opts *inOutOpts ) { CFStringRef cfStringRef = NULL; char *pValue = NULL; /* Default value */ 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 ); } } } } } /* sGet_disk_quota */ /* ----------------------------------------------------------------- sEncodeBase64 () ----------------------------------------------------------------- */ 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 ); } /* sEncodeBase64 */ /* ----------------------------------------------------------------- sDecodeBase64 () ----------------------------------------------------------------- */ 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; /* Skip past the '+ ' */ 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 ); } /* sDecodeBase64 */ /* ----------------------------------------------------------------- apple_password_callback () ----------------------------------------------------------------- */ int apple_password_callback ( char *inBuf, int inSize, int in_rwflag, void *inUserData ) { OSStatus status = noErr; SecKeychainItemRef keyChainRef = NULL; void *pwdBuf = NULL; /* will be allocated and filled in by SecKeychainFindGenericPassword */ UInt32 pwdLen = 0; char *service = "certificateManager"; /* defined by Apple */ 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 ); } /* Set the domain to System (daemon) */ status = SecKeychainSetPreferenceDomain( kSecPreferencesDomainSystem ); if ( status != noErr ) { syslog( LOG_ERR, "AOD: SSL callback: SecKeychainSetPreferenceDomain returned status: %d", status ); return( 0 ); } // Passwords created by cert management have the keychain access dialog suppressed. 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 ); } /* apple_password_callback */ /* ----------------------------------------------------------------- get_random_chars () ----------------------------------------------------------------- */ 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 ); /* try to open /dev/urandom */ file = open( "/dev/urandom", O_RDONLY, 0 ); if ( file == -1 ) { syslog( LOG_ERR, "AOD: random chars: cannot open /dev/urandom" ); /* try to open /dev/random */ 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 { /* make sure the chars are printable */ while ( count < (in_len - 1) ) { read( file, &out_buf[ count ], 1 ); if ( isalnum( out_buf[ count ] ) ) { count++; } } close( file ); } } /* get_random_chars */