#include <sys_defs.h>
#include <stdlib.h>
#include <string.h>
#include <msg.h>
#include <mymalloc.h>
#include <namadr_list.h>
#include <name_mask.h>
#include <mail_params.h>
#include <smtp_stream.h>
#include "smtpd.h"
#include "smtpd_sasl_glue.h"
#include "smtpd_chat.h"
#include <DirectoryService/DirServices.h>
#include <DirectoryService/DirServicesUtils.h>
#include <DirectoryService/DirServicesConst.h>
#include <mach/boolean.h>
#ifdef USE_SASL_AUTH
#define STR(s) vstring_str(s)
#if SASL_VERSION_MAJOR < 2
#define SASL_LOG_WARN SASL_LOG_WARNING
#define SASL_LOG_NOTE SASL_LOG_INFO
#define SASL_SERVER_NEW(srv, fqdn, rlm, lport, rport, cb, secflags, pconn) \
sasl_server_new(srv, fqdn, rlm, cb, secflags, pconn)
#define SASL_SERVER_START(conn, mech, clin, clinlen, srvout, srvoutlen, err) \
sasl_server_start(conn, mech, clin, clinlen, srvout, srvoutlen, err)
#define SASL_SERVER_STEP(conn, clin, clinlen, srvout, srvoutlen, err) \
sasl_server_step(conn, clin, clinlen, srvout, srvoutlen, err)
#define SASL_DECODE64(in, inlen, out, outmaxlen, outlen) \
sasl_decode64(in, inlen, out, outlen)
#endif
#if SASL_VERSION_MAJOR >= 2
#define SASL_SERVER_NEW(srv, fqdn, rlm, lport, rport, cb, secflags, pconn) \
sasl_server_new(srv, fqdn, rlm, lport, rport, cb, secflags, pconn)
#define SASL_SERVER_START(conn, mech, clin, clinlen, srvout, srvoutlen, err) \
sasl_server_start(conn, mech, clin, clinlen, srvout, srvoutlen)
#define SASL_SERVER_STEP(conn, clin, clinlen, srvout, srvoutlen, err) \
sasl_server_step(conn, clin, clinlen, srvout, srvoutlen)
#define SASL_DECODE64(in, inlen, out, outmaxlen, outlen) \
sasl_decode64(in, inlen, out, outmaxlen, outlen)
#endif
static int smtpd_sasl_log(void *unused_context, int priority,
const char *message)
{
switch (priority) {
case SASL_LOG_ERR:
case SASL_LOG_WARN:
msg_warn("SASL authentication problem: %s", message);
break;
case SASL_LOG_NOTE:
if (msg_verbose)
msg_info("SASL authentication info: %s", message);
break;
#if SASL_VERSION_MAJOR >= 2
case SASL_LOG_FAIL:
msg_warn("SASL authentication failure: %s", message);
break;
#endif
}
return SASL_OK;
}
#define NO_CALLBACK_CONTEXT 0
static sasl_callback_t callbacks[] = {
{SASL_CB_LOG, &smtpd_sasl_log, NO_CALLBACK_CONTEXT},
{SASL_CB_LIST_END, 0, 0}
};
static NAME_MASK smtpd_sasl_mask[] = {
"noplaintext", SASL_SEC_NOPLAINTEXT,
"noactive", SASL_SEC_NOACTIVE,
"nodictionary", SASL_SEC_NODICTIONARY,
"noanonymous", SASL_SEC_NOANONYMOUS,
#if SASL_VERSION_MAJOR >= 2
"mutual_auth", SASL_SEC_MUTUAL_AUTH,
#endif
0,
};
static int smtpd_sasl_opts;
static int smtpd_pw_server_sasl_opts;
#define PW_SERVER_NONE 0x0000
#define PW_SERVER_LOGIN 0x0001
#define PW_SERVER_PLAIN 0x0002
#define PW_SERVER_CRAM_MD5 0x0004
#define PW_SERVER_GSSAPI 0x0008
static NAME_MASK smtpd_pw_server_mask[] = {
"none", PW_SERVER_NONE,
"login", PW_SERVER_LOGIN,
"plain", PW_SERVER_PLAIN,
"cram-md5", PW_SERVER_CRAM_MD5,
"gssapi", PW_SERVER_GSSAPI,
0,
};
void smtpd_sasl_initialize( int use_pw_server )
{
if ( var_smtpd_use_pw_server )
{
smtpd_pw_server_sasl_opts = name_mask( VAR_SMTPD_PW_SERVER_OPTS, smtpd_pw_server_mask,
var_smtpd_pw_server_opts );
}
else
{
if (sasl_server_init(callbacks, "smtpd") != SASL_OK)
msg_fatal("SASL per-process initialization failed");
smtpd_sasl_opts = name_mask(VAR_SMTPD_SASL_OPTS, smtpd_sasl_mask,
var_smtpd_sasl_opts);
}
}
void smtpd_sasl_connect(SMTPD_STATE *state)
{
#if SASL_VERSION_MAJOR < 2
unsigned sasl_mechanism_count;
#else
int sasl_mechanism_count;
#endif
sasl_security_properties_t sec_props;
char *server_address;
char *client_address;
state->sasl_mechanism_list = 0;
state->sasl_username = 0;
state->sasl_method = 0;
state->sasl_sender = 0;
state->sasl_conn = 0;
state->sasl_decoded = vstring_alloc(10);
state->sasl_encoded = vstring_alloc(10);
state->pw_server_enabled = var_smtpd_use_pw_server;
state->pw_server_mechanism_list = 0;
state->pw_server_opts = 0;
if ( smtpd_pw_server_sasl_opts )
{
state->pw_server_mechanism_list = malloc( 64 );
if ( state->pw_server_mechanism_list != NULL )
{
memset( state->pw_server_mechanism_list, 0, 64 );
if ( smtpd_pw_server_sasl_opts & PW_SERVER_LOGIN )
{
strcpy( state->pw_server_mechanism_list, " LOGIN" );
}
if ( smtpd_pw_server_sasl_opts & PW_SERVER_PLAIN )
{
strcat( state->pw_server_mechanism_list, " PLAIN" );
}
if ( smtpd_pw_server_sasl_opts & PW_SERVER_CRAM_MD5 )
{
strcat( state->pw_server_mechanism_list, " CRAM-MD5" );
}
if ( smtpd_pw_server_sasl_opts & PW_SERVER_GSSAPI )
{
strcat( state->pw_server_mechanism_list, " GSSAPI" );
}
state->pw_server_opts = smtpd_pw_server_sasl_opts;
}
}
if ( !state->pw_server_enabled )
{
#define NO_SECURITY_LAYERS (0)
#define NO_SESSION_CALLBACKS ((sasl_callback_t *) 0)
#define NO_AUTH_REALM ((char *) 0)
#if SASL_VERSION_MAJOR >= 2 && defined(USE_SASL_IP_AUTH)
#error "USE_SASL_IP_AUTH is not implemented"
#else
server_address = 0;
client_address = 0;
#endif
if (SASL_SERVER_NEW("smtp", var_myhostname, *var_smtpd_sasl_realm ?
var_smtpd_sasl_realm : NO_AUTH_REALM,
server_address, client_address,
NO_SESSION_CALLBACKS, NO_SECURITY_LAYERS,
&state->sasl_conn) != SASL_OK)
msg_fatal("SASL per-connection server initialization");
memset(&sec_props, 0, sizeof(sec_props));
sec_props.min_ssf = 0;
sec_props.max_ssf = 1;
sec_props.security_flags = smtpd_sasl_opts;
sec_props.maxbufsize = 0;
sec_props.property_names = 0;
sec_props.property_values = 0;
if (sasl_setprop(state->sasl_conn, SASL_SEC_PROPS,
&sec_props) != SASL_OK)
msg_fatal("SASL per-connection security setup");
#define UNSUPPORTED_USER ((char *) 0)
#define IGNORE_MECHANISM_LEN ((unsigned *) 0)
if (sasl_listmech(state->sasl_conn, UNSUPPORTED_USER,
"", " ", "",
&state->sasl_mechanism_list,
IGNORE_MECHANISM_LEN,
&sasl_mechanism_count) != SASL_OK)
msg_fatal("cannot lookup SASL authentication mechanisms");
if (sasl_mechanism_count <= 0)
msg_fatal("no SASL authentication mechanisms");
}
}
void smtpd_sasl_disconnect(SMTPD_STATE *state)
{
if ( state->pw_server_mechanism_list )
{
free( state->pw_server_mechanism_list );
state->pw_server_mechanism_list = 0;
}
if (state->sasl_mechanism_list) {
#if SASL_VERSION_MAJOR < 2
free(state->sasl_mechanism_list);
#endif
state->sasl_mechanism_list = 0;
}
if (state->sasl_conn) {
sasl_dispose(&state->sasl_conn);
state->sasl_conn = 0;
}
vstring_free(state->sasl_decoded);
vstring_free(state->sasl_encoded);
}
char *smtpd_sasl_authenticate(SMTPD_STATE *state,
const char *sasl_method,
const char *init_response)
{
char *myname = "smtpd_sasl_authenticate";
char *dec_buffer;
unsigned dec_length;
unsigned enc_length;
unsigned enc_length_out;
unsigned reply_len;
unsigned serveroutlen;
int result;
#if SASL_VERSION_MAJOR < 2
char *serverout = 0;
#else
const char *serverout = 0;
#endif
#if SASL_VERSION_MAJOR < 2
const char *errstr = 0;
#endif
#define IFELSE(e1,e2,e3) ((e1) ? (e2) : (e3))
if (msg_verbose)
msg_info("%s: sasl_method %s%s%s", myname, sasl_method,
IFELSE(init_response, ", init_response ", ""),
IFELSE(init_response, init_response, ""));
if (state->sasl_username || state->sasl_method)
msg_panic("%s: already authenticated", myname);
if (init_response) {
reply_len = strlen(init_response);
VSTRING_SPACE(state->sasl_decoded, reply_len);
dec_buffer = STR(state->sasl_decoded);
if (SASL_DECODE64(init_response, reply_len,
dec_buffer, reply_len, &dec_length) != SASL_OK)
return ("501 Authentication failed: malformed initial response");
if (msg_verbose)
msg_info("%s: decoded initial response %s", myname, dec_buffer);
} else {
dec_buffer = 0;
dec_length = 0;
}
result = SASL_SERVER_START(state->sasl_conn, sasl_method, dec_buffer,
dec_length, &serverout, &serveroutlen, &errstr);
while (result == SASL_CONTINUE) {
if (msg_verbose)
msg_info("%s: uncoded challenge: %.*s",
myname, (int) serveroutlen, serverout);
enc_length = ((serveroutlen + 2) / 3) * 4 + 1;
VSTRING_SPACE(state->sasl_encoded, enc_length);
if (sasl_encode64(serverout, serveroutlen, STR(state->sasl_encoded),
enc_length, &enc_length_out) != SASL_OK)
msg_panic("%s: sasl_encode64 botch", myname);
#if SASL_VERSION_MAJOR < 2
free(serverout);
#endif
serverout = 0;
smtpd_chat_reply(state, "334 %s", STR(state->sasl_encoded));
smtpd_chat_query(state);
if (strcmp(vstring_str(state->buffer), "*") == 0)
return ("501 Authentication aborted");
reply_len = VSTRING_LEN(state->buffer);
VSTRING_SPACE(state->sasl_decoded, reply_len);
if (SASL_DECODE64(vstring_str(state->buffer), reply_len,
STR(state->sasl_decoded), reply_len,
&dec_length) != SASL_OK)
return ("501 Error: malformed authentication response");
if (msg_verbose)
msg_info("%s: decoded response: %.*s",
myname, (int) dec_length, STR(state->sasl_decoded));
result = SASL_SERVER_STEP(state->sasl_conn, STR(state->sasl_decoded),
dec_length, &serverout, &serveroutlen, &errstr);
}
#if SASL_VERSION_MAJOR < 2
if (serverout)
free(serverout);
#endif
if (result != SASL_OK)
return ("535 Error: authentication failed");
#if SASL_VERSION_MAJOR >= 2
result = sasl_getprop(state->sasl_conn, SASL_USERNAME,
(const void **) &serverout);
#else
result = sasl_getprop(state->sasl_conn, SASL_USERNAME,
(void **) &serverout);
#endif
if (result != SASL_OK || serverout == 0)
msg_panic("%s: sasl_getprop SASL_USERNAME botch", myname);
state->sasl_username = mystrdup(serverout);
state->sasl_method = mystrdup(sasl_method);
return (0);
}
void smtpd_sasl_logout(SMTPD_STATE *state)
{
if (state->sasl_username) {
myfree(state->sasl_username);
state->sasl_username = 0;
}
if (state->sasl_method) {
myfree(state->sasl_method);
state->sasl_method = 0;
}
}
static tDirStatus sOpen_ds ( tDirReference *inOutDirRef );
static tDirStatus sGet_search_node ( tDirReference inDirRef, tDirNodeReference *outSearchNodeRef );
static tDirStatus sLook_up_user ( tDirReference inDirRef, tDirNodeReference inSearchNodeRef, const char *inUserID, char **outUserLocation );
static tDirStatus sOpen_user_node ( tDirReference inDirRef, const char *inUserLoc, tDirNodeReference *outUserNodeRef );
static int sValidateResponse ( const char *inUserID, const char *inChallenge, const char *inResponse, const char *inAuthType );
static int sDoCryptAuth ( tDirReference inDirRef, tDirNodeReference inUserNodeRef, const char *inUserID, const char *inPasswd );
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 );
static int sClearTextCrypt ( const char *inUserID, const char *inPasswd );
#define MAX_USER_BUF_SIZE 512
#define MAX_CHAL_BUF_SIZE 2048
#define MAX_IO_BUF_SIZE 21848
#include <sys/param.h>
char *smtpd_pw_server_authenticate ( SMTPD_STATE *state,
const char *sasl_method,
const char *init_response )
{
const char *host_name = NULL;
char *ptr = NULL;
char *myname = "smtpd_pw_server_authenticate";
unsigned len = 0;
char user[ MAX_USER_BUF_SIZE ];
char passwd[ MAX_USER_BUF_SIZE ];
char chal[ MAX_CHAL_BUF_SIZE ];
char resp[ MAX_IO_BUF_SIZE ];
if ( state->sasl_username || state->sasl_method )
{
msg_panic( "%s: already authenticated", myname );
}
if ( strcasecmp( sasl_method, "LOGIN" ) == 0 )
{
if ( !(state->pw_server_opts & PW_SERVER_LOGIN) )
{
return ( "504 Authentication method not enabled" );
}
strcpy( chal, "Username:" );
sEncodeBase64( chal, strlen( chal ), resp, MAX_IO_BUF_SIZE );
smtpd_chat_reply( state, "334 %s", resp );
memset( resp, 0, MAX_IO_BUF_SIZE );
smtpd_chat_query( state );
len = VSTRING_LEN( state->buffer );
sDecodeBase64( vstring_str( state->buffer ), len, user, MAX_USER_BUF_SIZE );
if ( strcmp(vstring_str( state->buffer ), "*") == 0 )
{
return ( "501 Authentication aborted" );
}
strcpy( chal, "Password:" );
sEncodeBase64( chal, strlen( chal ), resp, MAX_IO_BUF_SIZE );
smtpd_chat_reply( state, "334 %s", resp );
smtpd_chat_query( state );
len = VSTRING_LEN( state->buffer );
sDecodeBase64( vstring_str( state->buffer ), len, passwd, MAX_USER_BUF_SIZE );
if ( sClearTextCrypt( user, passwd ) == eAODNoErr )
{
state->sasl_username = mystrdup( user );
state->sasl_method = mystrdup( passwd );
return( 0 );
}
else
{
return ( "535 Error: authentication failed" );
}
}
else if ( strcasecmp( sasl_method, "PLAIN" ) == 0 )
{
if ( !(state->pw_server_opts & PW_SERVER_PLAIN) )
{
return ( "504 Authentication method not enabled" );
}
len = strlen( init_response );
sDecodeBase64( init_response, len, resp, MAX_USER_BUF_SIZE );
ptr = resp;
if ( *ptr == NULL )
{
ptr++;
}
if ( ptr != NULL )
{
if ( strlen( ptr ) < MAX_USER_BUF_SIZE )
{
strcpy( user, ptr );
ptr = ptr + (strlen( user ) + 1 );
if ( ptr != NULL )
{
if ( strlen( ptr ) < MAX_USER_BUF_SIZE )
{
strcpy( passwd, ptr );
if ( sClearTextCrypt( user, passwd ) == eAODNoErr )
{
state->sasl_username = mystrdup( user );
state->sasl_method = mystrdup( passwd );
return( 0 );
}
}
}
}
}
return ( "535 Error: authentication failed" );
}
else if ( strcasecmp( sasl_method, "CRAM-MD5" ) == 0 )
{
if ( !(state->pw_server_opts & PW_SERVER_CRAM_MD5) )
{
return ( "504 Authentication method not enabled" );
}
host_name = (const char *)get_hostname();
sprintf( chal,"<%lu.%lu@%s>",(unsigned long) getpid(), (unsigned long)time(0), host_name );
sEncodeBase64( chal, strlen( chal ), resp, MAX_IO_BUF_SIZE );
smtpd_chat_reply( state, "334 %s", resp );
smtpd_chat_query( state );
if ( strcmp( vstring_str( state->buffer ), "*" ) == 0 )
{
return ( "501 Authentication aborted" );
}
memset( resp, 0, MAX_IO_BUF_SIZE );
len = VSTRING_LEN( state->buffer );
sDecodeBase64( vstring_str( state->buffer ), len, resp, MAX_IO_BUF_SIZE );
ptr = strchr( resp, ' ' );
if ( ptr != NULL )
{
len = ptr - resp;
if ( len < MAX_USER_BUF_SIZE )
{
strncpy( user, resp, len );
ptr++;
if ( ptr != NULL )
{
if ( sValidateResponse( user, chal, ptr, kDSStdAuthCRAM_MD5 ) == eAODNoErr )
{
state->sasl_username = mystrdup( user );
state->sasl_method = mystrdup( passwd );
return( 0 );
}
}
}
}
return ( "535 Error: authentication failed" );
}
else if ( strcasecmp( sasl_method, "GSSAPI" ) == 0 )
{
if ( !(state->pw_server_opts & PW_SERVER_GSSAPI) )
{
return ( "504 Authentication method not enabled" );
}
}
return ( "504 Unsupported authentication method" );
}
#define CHAR64(c) (((c) < 0 || (c) > 127) ? -1 : index_64[(c)])
static char basis_64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????";
static char index_64[ 128 ] =
{
-1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
-1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
-1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,62, -1,-1,-1,63,
52,53,54,55, 56,57,58,59, 60,61,-1,-1, -1,-1,-1,-1,
-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10, 11,12,13,14,
15,16,17,18, 19,20,21,22, 23,24,25,-1, -1,-1,-1,-1,
-1,26,27,28, 29,30,31,32, 33,34,35,36, 37,38,39,40,
41,42,43,44, 45,46,47,48, 49,50,51,-1, -1,-1,-1,-1
};
int sEncodeBase64 ( const char *inStr, const int inLen, char *outStr, int outLen )
{
int i = 0;
const unsigned char *pStr = NULL;
unsigned long uiInLen = 0;
unsigned char ucVal = 0;
if ( (inStr == NULL) || (outStr == NULL) || (inLen <= 0) || (outLen <= 0) )
{
return( -1 );
}
pStr = (const unsigned char *)inStr;
uiInLen = inLen;
memset( outStr, 0, outLen );
while ( uiInLen >= 3 )
{
if ( (i + 4) > outLen )
{
return( -1 );
}
outStr[ i++ ] = ( basis_64[ pStr[ 0 ] >> 2 ] );
outStr[ i++ ] = ( basis_64[ ((pStr[ 0 ] << 4) & 0x30) | (pStr[ 1 ] >> 4) ] );
outStr[ i++ ] = ( basis_64[ ((pStr[ 1 ] << 2) & 0x3c) | (pStr[ 2 ] >> 6) ] );
outStr[ i++ ] = ( basis_64[ pStr[ 2 ] & 0x3f ] );
pStr += 3;
uiInLen -= 3;
}
if ( uiInLen > 0 )
{
if ( (i + 4) > outLen )
{
return( -1 );
}
outStr[ i++ ] = ( basis_64[ pStr[0] >> 2] );
ucVal = (pStr[0] << 4) & 0x30;
if ( uiInLen > 1 )
{
ucVal |= pStr[1] >> 4;
}
outStr[ i++ ] = ( basis_64[ ucVal ] );
outStr[ i++ ] = ( (uiInLen < 2) ? '=' : basis_64[ (pStr[ 1 ] << 2) & 0x3c ] );
outStr[ i++ ] = ( '=' );
}
return( 0 );
}
int sDecodeBase64 ( const char *inStr, const int inLen, char *outStr, int outLen )
{
int iResult = 0;
unsigned long i = 0;
unsigned long j = 0;
unsigned long uiInLen = 0;
unsigned long uiLen = 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;
uiInLen = inLen;
if ( (pStr[ 0 ] == '+') && (pStr[ 1 ] == ' ') )
{
pStr += 2;
}
if ( *pStr == '\r')
{
iResult = -1;
}
else
{
memset( outStr, 0, outLen );
for ( i = 0; i < uiInLen / 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) );
++uiLen;
if ( c3 != '=' )
{
outStr[ j++ ] = ( ((CHAR64(c2) << 4) & 0xf0) | (CHAR64(c3) >> 2) );
++uiLen;
if ( c4 != '=' )
{
outStr[ j++ ] = ( ((CHAR64(c3) << 6) & 0xc0) | CHAR64(c4) );
++uiLen;
}
}
}
}
return( iResult );
}
int sClearTextCrypt ( const char *inUserID, const char *inPasswd )
{
int iResult = eAODNoErr;
tDirStatus dsStatus = eDSNoErr;
tDirReference dirRef = 0;
tDirNodeReference searchNodeRef = 0;
tDirNodeReference userNodeRef = 0;
char *userLoc = NULL;
if ( (inUserID == NULL) || (inPasswd == NULL) )
{
return( eAODParamErr );
}
dsStatus = sOpen_ds( &dirRef );
if ( dsStatus == eDSNoErr )
{
dsStatus = sGet_search_node( dirRef, &searchNodeRef );
if ( dsStatus == eDSNoErr )
{
dsStatus = sLook_up_user( dirRef, searchNodeRef, inUserID, &userLoc );
if ( dsStatus == eDSNoErr )
{
dsStatus = sOpen_user_node( dirRef, userLoc, &userNodeRef );
if ( dsStatus == eDSNoErr )
{
dsStatus = sDoCryptAuth( dirRef, userNodeRef, inUserID, inPasswd );
switch ( dsStatus )
{
case eDSNoErr:
case eDSAuthNewPasswordRequired:
case eDSAuthPasswordExpired:
iResult = eAODNoErr;
break;
default:
iResult = eAODAuthFailed;
break;
}
(void)dsCloseDirNode( userNodeRef );
}
else
{
iResult = eAODCantOpenUserNode;
}
}
else
{
iResult = eAODUserNotFound;
}
(void)dsCloseDirNode( searchNodeRef );
}
else
{
iResult = eAODOpenSearchFailed;
}
(void)dsCloseDirService( dirRef );
}
else
{
iResult = eAODOpenDSFailed;
}
return( iResult );
}
tDirStatus sOpen_ds ( tDirReference *inOutDirRef )
{
tDirStatus dsStatus = eDSNoErr;
dsStatus = dsOpenDirService( inOutDirRef );
return( dsStatus );
}
tDirStatus sGet_search_node ( tDirReference inDirRef,
tDirNodeReference *outSearchNodeRef )
{
tDirStatus dsStatus = eMemoryAllocError;
unsigned long uiCount = 0;
tDataBuffer *pTDataBuff = NULL;
tDataList *pDataList = NULL;
pTDataBuff = dsDataBufferAllocate( inDirRef, 8192 );
if ( pTDataBuff != NULL )
{
dsStatus = dsFindDirNodes( inDirRef, pTDataBuff, NULL, eDSSearchNodeName, &uiCount, NULL );
if ( dsStatus == eDSNoErr )
{
dsStatus = eDSNodeNotFound;
if ( uiCount == 1 )
{
dsStatus = dsGetDirNodeName( inDirRef, pTDataBuff, 1, &pDataList );
if ( dsStatus == eDSNoErr )
{
dsStatus = dsOpenDirNode( inDirRef, pDataList, outSearchNodeRef );
}
if ( pDataList != NULL )
{
(void)dsDataListDeAllocate( inDirRef, pDataList, TRUE );
free( pDataList );
pDataList = NULL;
}
}
}
(void)dsDataBufferDeAllocate( inDirRef, pTDataBuff );
pTDataBuff = NULL;
}
return( dsStatus );
}
tDirStatus sLook_up_user ( tDirReference inDirRef,
tDirNodeReference inSearchNodeRef,
const char *inUserID,
char **outUserLocation )
{
tDirStatus dsStatus = eMemoryAllocError;
int done = FALSE;
char *pAcctName = NULL;
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, kDSNAttrMetaNodeLocation, NULL );
if ( pUserAttrType != NULL );
{
do {
dsStatus = dsGetRecordList( inSearchNodeRef, pTDataBuff, &tdlRecName, eDSiExact, pUserRecType,
pUserAttrType, FALSE, &uiRecCount, &pContext );
if ( dsStatus == eDSNoErr )
{
dsStatus = eDSInvalidName;
if ( uiRecCount == 1 )
{
dsStatus = dsGetRecordEntry( inSearchNodeRef, pTDataBuff, 1, &attrListRef, &pRecEntry );
if ( dsStatus == eDSNoErr )
{
(void)dsGetRecordNameFromEntry( pRecEntry, &pAcctName );
dsStatus = dsGetAttributeEntry( inSearchNodeRef, pTDataBuff, attrListRef, 1, &valueRef, &pAttrEntry );
if ( (dsStatus == eDSNoErr) && (pAttrEntry != NULL) )
{
dsStatus = dsGetAttributeValue( inSearchNodeRef, pTDataBuff, 1, valueRef, &pValueEntry );
if ( (dsStatus == eDSNoErr) && (pValueEntry != NULL) )
{
if ( strcmp( pAttrEntry->fAttributeSignature.fBufferData, kDSNAttrMetaNodeLocation ) == 0 )
{
*outUserLocation = (char *)calloc( pValueEntry->fAttributeValueData.fBufferLength + 1, sizeof( char ) );
memcpy( *outUserLocation, pValueEntry->fAttributeValueData.fBufferData, pValueEntry->fAttributeValueData.fBufferLength );
done = TRUE;
}
(void)dsCloseAttributeValueList( valueRef );
}
(void)dsDeallocAttributeEntry( inSearchNodeRef, pAttrEntry );
pAttrEntry = NULL;
(void)dsCloseAttributeList( attrListRef );
}
if ( pRecEntry != NULL )
{
(void)dsDeallocRecordEntry( inSearchNodeRef, pRecEntry );
pRecEntry = NULL;
}
}
}
else
{
done = TRUE;
dsStatus = eDSAuthInvalidUserName;
}
}
} while ( (pContext != NULL) && (dsStatus == eDSNoErr) && (!done) );
if ( pContext != NULL )
{
(void)dsReleaseContinueData( inDirRef, pContext );
pContext = NULL;
}
(void)dsDataListDeallocate( inDirRef, pUserAttrType );
pUserAttrType = NULL;
}
(void)dsDataListDeallocate( inDirRef, pUserRecType );
pUserRecType = NULL;
}
(void)dsDataListDeAllocate( inDirRef, &tdlRecName, TRUE );
}
(void)dsDataBufferDeAllocate( inDirRef, pTDataBuff );
pTDataBuff = NULL;
}
if ( pAcctName != NULL )
{
free( pAcctName );
pAcctName = NULL;
}
return( dsStatus );
}
tDirStatus sOpen_user_node ( tDirReference inDirRef, const char *inUserLoc, tDirNodeReference *outUserNodeRef )
{
tDirStatus dsStatus = eMemoryAllocError;
tDataList *pUserNode = NULL;
pUserNode = dsBuildFromPath( inDirRef, inUserLoc, "/" );
if ( pUserNode != NULL )
{
dsStatus = dsOpenDirNode( inDirRef, pUserNode, outUserNodeRef );
(void)dsDataListDeAllocate( inDirRef, pUserNode, TRUE );
free( pUserNode );
pUserNode = NULL;
}
return( dsStatus );
}
static int sDoCryptAuth ( tDirReference inDirRef, tDirNodeReference inUserNodeRef, const char *inUserID, const char *inPasswd )
{
tDirStatus dsStatus = eDSNoErr;
int iResult = -1;
long nameLen = 0;
long passwdLen = 0;
unsigned long curr = 0;
unsigned long len = 0;
unsigned long uiBuffSzie = 0;
tDataBuffer *pAuthBuff = NULL;
tDataBuffer *pStepBuff = NULL;
tDataNode *pAuthType = NULL;
if ( (inUserID == NULL) || (inPasswd == NULL) )
{
return( eDSAuthParameterError );
}
nameLen = strlen( inUserID );
passwdLen = strlen( inPasswd );
uiBuffSzie = nameLen + passwdLen + 32;
pAuthBuff = dsDataBufferAllocate( inDirRef, uiBuffSzie );
if ( pAuthBuff != NULL )
{
pStepBuff = dsDataBufferAllocate( inDirRef, 256 );
if ( pStepBuff != NULL )
{
pAuthType = dsDataNodeAllocateString( inDirRef, kDSStdAuthNodeNativeClearTextOK );
if ( pAuthType != NULL )
{
len = nameLen;
memcpy( &(pAuthBuff->fBufferData[ curr ]), &len, sizeof( unsigned long ) );
curr += sizeof( unsigned long );
memcpy( &(pAuthBuff->fBufferData[ curr ]), inUserID, len );
curr += len;
len = passwdLen;
memcpy( &(pAuthBuff->fBufferData[ curr ]), &len, sizeof( unsigned long ) );
curr += sizeof( unsigned long );
memcpy( &(pAuthBuff->fBufferData[ curr ]), inPasswd, len );
curr += len;
pAuthBuff->fBufferLength = curr;
dsStatus = dsDoDirNodeAuth( inUserNodeRef, pAuthType, TRUE, pAuthBuff, pStepBuff, NULL );
if ( dsStatus == eDSNoErr )
{
iResult = 0;
}
else
{
msg_warn( "AOD: Authentication failed for user %s. (Open Directroy error: %d)", inUserID, dsStatus );
iResult = -7;
}
(void)dsDataNodeDeAllocate( inDirRef, pAuthType );
pAuthType = NULL;
}
else
{
msg_warn( "AOD: Authentication failed for user %s. Unable to allocate memory.", inUserID );
iResult = -6;
}
(void)dsDataNodeDeAllocate( inDirRef, pStepBuff );
pStepBuff = NULL;
}
else
{
msg_warn( "AOD: Authentication failed for user %s. Unable to allocate memory.", inUserID );
iResult = -5;
}
(void)dsDataNodeDeAllocate( inDirRef, pAuthBuff );
pAuthBuff = NULL;
}
return( iResult );
}
int sValidateResponse ( const char *inUserID, const char *inChallenge, const char *inResponse, const char *inAuthType )
{
int iResult = -1;
tDirStatus dsStatus = eDSNoErr;
tDirReference dirRef = 0;
tDirNodeReference searchNodeRef = 0;
tDirNodeReference userNodeRef = 0;
tDataBuffer *pAuthBuff = NULL;
tDataBuffer *pStepBuff = NULL;
tDataNode *pAuthType = NULL;
char *userLoc = 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 ( (inUserID == NULL) || (inChallenge == NULL) || (inResponse == NULL) || (inAuthType == NULL) )
{
return( -1 );
}
uiNameLen = strlen( inUserID );
uiChalLen = strlen( inChallenge );
uiRespLen = strlen( inResponse );
uiBuffSzie = uiNameLen + uiChalLen + uiRespLen + 32;
dsStatus = sOpen_ds( &dirRef );
if ( dsStatus == eDSNoErr )
{
dsStatus = sGet_search_node( dirRef, &searchNodeRef );
if ( dsStatus == eDSNoErr )
{
dsStatus = sLook_up_user( dirRef, searchNodeRef, inUserID, &userLoc );
if ( dsStatus == eDSNoErr )
{
dsStatus = sOpen_user_node( dirRef, userLoc, &userNodeRef );
if ( dsStatus == eDSNoErr )
{
pAuthBuff = dsDataBufferAllocate( dirRef, uiBuffSzie );
if ( pAuthBuff != NULL )
{
pStepBuff = dsDataBufferAllocate( dirRef, 256 );
if ( pStepBuff != NULL )
{
pAuthType = dsDataNodeAllocateString( dirRef, inAuthType );
if ( pAuthType != NULL )
{
uiLen = uiNameLen;
memcpy( &(pAuthBuff->fBufferData[ uiCurr ]), &uiLen, sizeof( unsigned long ) );
uiCurr += sizeof( unsigned long );
memcpy( &(pAuthBuff->fBufferData[ uiCurr ]), inUserID, uiLen );
uiCurr += uiLen;
uiLen = uiChalLen;
memcpy( &(pAuthBuff->fBufferData[ uiCurr ]), &uiLen, sizeof( unsigned long ) );
uiCurr += sizeof( unsigned long );
memcpy( &(pAuthBuff->fBufferData[ uiCurr ]), inChallenge, uiLen );
uiCurr += uiLen;
uiLen = uiRespLen;
memcpy( &(pAuthBuff->fBufferData[ uiCurr ]), &uiLen, sizeof( unsigned long ) );
uiCurr += sizeof( unsigned long );
memcpy( &(pAuthBuff->fBufferData[ uiCurr ]), inResponse, uiLen );
uiCurr += uiLen;
pAuthBuff->fBufferLength = uiCurr;
dsStatus = dsDoDirNodeAuth( userNodeRef, pAuthType, TRUE, pAuthBuff, pStepBuff, NULL );
if ( dsStatus == eDSNoErr )
{
iResult = 0;
}
else
{
iResult = -7;
}
(void)dsDataNodeDeAllocate( dirRef, pAuthType );
pAuthType = NULL;
}
else
{
msg_warn( "AOD: Authentication failed for user %s. Unable to allocate memory.", inUserID );
iResult = -6;
}
(void)dsDataNodeDeAllocate( dirRef, pStepBuff );
pStepBuff = NULL;
}
else
{
msg_warn( "AOD: Authentication failed for user %s. Unable to allocate memory.", inUserID );
iResult = -5;
}
(void)dsDataNodeDeAllocate( dirRef, pAuthBuff );
pAuthBuff = NULL;
}
else
{
msg_warn( "AOD: Authentication failed for user %s. Unable to allocate memory.", inUserID );
iResult = -4;
}
(void)dsCloseDirNode( userNodeRef );
userNodeRef = 0;
}
else
{
msg_warn( "AOD: Unable to open user directory node for user %s. (Open Directroy error: %d)", inUserID, dsStatus );
iResult = -1;
}
}
else
{
msg_warn( "AOD: Unable to find user %s. (Open Directroy error: %d)", inUserID, dsStatus );
iResult = -1;
}
(void)dsCloseDirNode( searchNodeRef );
searchNodeRef = 0;
}
else
{
msg_warn( "AOD: Unable to open directroy search node. (Open Directroy error: %d)", dsStatus );
iResult = -1;
}
(void)dsCloseDirService( dirRef );
dirRef = 0;
}
else
{
msg_warn( "AOD: Unable to open directroy. (Open Directroy error: %d)", dsStatus );
iResult = -1;
}
return( iResult );
}
#endif