#include "sys_defs.h"
#include "aod.h"
#include "msg.h"
#include "mail_params.h"
#include "sacl_cache_clnt.h"
#include <string.h>
#include <stdbool.h>
#include <stdlib.h>
#include <syslog.h>
#include <stdio.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/sysctl.h>
#include <mach/mig_errors.h>
#include <dtrace-postfix.h>
#include <membership.h>
#include <membershipPriv.h>
#include <CoreDaemon/CoreDaemon.h>
#include <CoreFoundation/CFData.h>
#include <CoreFoundation/CFString.h>
#include <CoreFoundation/CFNumber.h>
#include <CoreFoundation/CFPropertyList.h>
#include <OpenDirectory/OpenDirectory.h>
#include <OpenDirectory/OpenDirectoryPriv.h>
#include <DirectoryService/DirServicesConst.h>
static void print_cf_error ( CFErrorRef in_cf_err_ref, const char *in_user_name, const char *in_default_str );
static int get_user_attributes ( ODNodeRef in_node_ref, const char *in_user_name, struct od_user_opts *in_out_opts );
void get_acct_state ( CFDictionaryRef inCFDictRef, CFMutableDictionaryRef out_user_dict, struct od_user_opts *in_out_opts );
static void get_auto_forward_addr ( CFDictionaryRef inCFDictRef, CFMutableDictionaryRef out_user_dict, struct od_user_opts *in_out_opts );
static void get_alt_loc ( CFDictionaryRef inCFDictRef, CFMutableDictionaryRef out_user_dict );
static void get_mail_quota ( CFDictionaryRef inCFDictRef, CFMutableDictionaryRef out_user_dict );
static bool get_attributes_local ( struct od_user_opts *in_out_opts, const char *in_user_guid );
static void set_attributes_local ( CFMutableDictionaryRef in_user_dict, const char *in_user_guid );
static CFStringRef get_attr_from_record ( ODRecordRef in_rec_ref, CFStringRef in_attr );
static CFMutableDictionaryRef get_mail_attribute_values ( const char *in_mail_attribute, struct od_user_opts *in_out_opts );
char *user_settings_path = NULL;
#include <kvbuf.h>
#include <opendirectory/DSlibinfoMIG.h>
#include <opendirectory/DSmemberdMIG_types.h>
#include <DirectoryService/DirectoryService.h>
extern mach_port_t _ds_port;
extern int _ds_running();
int g_bad_recip_cntr = 0;
char g_client_addr[16] = "";
XSEventPortRef gEventPort = NULL;
void send_server_event ( const eEventCode in_event_code, const char *in_name, const char *in_addr )
{
CFTypeRef keys[2];
CFTypeRef values[2];
CFStringRef cfstr_addr = NULL;
CFStringRef cfstr_event = NULL;
if ( !strlen(g_client_addr) || (strcmp(g_client_addr, in_addr) != 0) ) {
strlcpy(g_client_addr, in_addr, sizeof g_client_addr);
g_bad_recip_cntr = 0;
}
if ( g_bad_recip_cntr++ < 4 )
return;
else
sleep( g_bad_recip_cntr >= 10 ? 10 : g_bad_recip_cntr );
if ( gEventPort == NULL )
gEventPort = XSEventPortCreate(nil);
keys[0] = CFSTR("eventType");
keys[1] = CFSTR("host_address");
switch ( in_event_code ) {
case eBadRecipient:
cfstr_event = CFStringCreateWithCString(NULL, "smtp.receive.badrecipient", kCFStringEncodingMacRoman);
break;
case eAuthFailure:
cfstr_event = CFStringCreateWithCString(NULL, "auth.failure", kCFStringEncodingMacRoman);
break;
case eAuthSuccess:
cfstr_event = CFStringCreateWithCString(NULL, "auth.success", kCFStringEncodingMacRoman);
break;
default:
msg_warn("Warning: unknown sever event: %d", in_event_code);
return;
}
cfstr_addr = CFStringCreateWithCString(NULL, in_addr, kCFStringEncodingMacRoman);
values[0] = cfstr_event;
values[1] = cfstr_addr;
CFDictionaryRef dict_event = CFDictionaryCreate(NULL, keys, values,
sizeof(keys) / sizeof(keys[0]),
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
(void)XSEventPortPostEvent(gEventPort, cfstr_event, dict_event);
CFRelease(cfstr_addr);
CFRelease(cfstr_event);
CFRelease(dict_event);
}
void close_server_event_port ( void )
{
if ( gEventPort != NULL )
XSEventPortDelete(gEventPort);
}
__private_extern__ kern_return_t
get_procno( const char *procname, int32_t *procno )
{
kern_return_t status;
security_token_t token;
bool lookAgain;
do {
lookAgain = false;
if (_ds_running() == 0) return KERN_FAILURE;
if (_ds_port == MACH_PORT_NULL) return KERN_FAILURE;
status = libinfoDSmig_GetProcedureNumber( _ds_port, (char *) procname, procno, &token );
switch( status )
{
case MACH_SEND_INVALID_DEST:
case MIG_SERVER_DIED:
mach_port_mod_refs( mach_task_self(), _ds_port, MACH_PORT_RIGHT_SEND, -1 );
_ds_port = MACH_PORT_NULL;
lookAgain = true;
break;
case KERN_SUCCESS:
if ( token.val[0] != 0 ) {
(*procno) = -1;
status = KERN_FAILURE;
}
break;
default:
break;
}
} while ( lookAgain == true );
return status;
}
__private_extern__ kern_return_t
ds_lookup( int32_t procno, kvbuf_t *request, kvarray_t **answer )
{
kern_return_t status;
security_token_t token;
bool lookAgain;
mach_msg_type_number_t oolen = 0;
vm_address_t oobuf = 0;
char ilbuf[MAX_MIG_INLINE_DATA];
mach_msg_type_number_t illen = 0;
do {
lookAgain = false;
if ( _ds_running() == 0 ) return KERN_FAILURE;
if ( _ds_port == MACH_PORT_NULL ) return KERN_FAILURE;
if ( request == NULL ) return KERN_FAILURE;
status = libinfoDSmig_Query( _ds_port, procno, request->databuf, request->datalen, ilbuf, &illen, &oobuf, &oolen, &token );
switch( status )
{
case MACH_SEND_INVALID_DEST:
case MIG_SERVER_DIED:
mach_port_mod_refs( mach_task_self(), _ds_port, MACH_PORT_RIGHT_SEND, -1 );
_ds_port = MACH_PORT_NULL;
lookAgain = true;
break;
case KERN_SUCCESS:
if ( token.val[0] == 0 ) {
if ( answer != NULL ) {
kvbuf_t *tempBuf;
if ( oolen != 0 ) {
tempBuf = kvbuf_init( (char *)oobuf, (uint32_t) oolen );
}
else {
tempBuf = kvbuf_init( ilbuf, illen );
}
(*answer) = kvbuf_decode( tempBuf );
if ( (*answer) == NULL ) {
kvbuf_free( tempBuf );
}
}
}
else {
procno = -1;
status = KERN_FAILURE;
}
break;
default:
break;
}
} while ( lookAgain == true );
if ( oolen != 0 ) {
vm_deallocate( mach_task_self(), oobuf, oolen );
}
return status;
}
static kvarray_t *
getpwnam_ext_real( const char *name )
{
static int32_t procno = -1;
static int32_t initProc = -1;
static bool setupList = FALSE;
kvarray_t *response = NULL;
kern_return_t status;
if ( name == NULL ) {
procno = -1;
initProc = -1;
setupList = FALSE;
return NULL;
}
if ( procno == -1 ) {
status = get_procno( "getpwnam_ext", &procno );
if ( status != KERN_SUCCESS ) return NULL;
}
if ( initProc == -1 ) {
status = get_procno( "getpwnam_initext", &initProc );
if ( status != KERN_SUCCESS ) return NULL;
}
if (!setupList) {
kvbuf_t *reqTypes = kvbuf_new();
kvbuf_add_dict( reqTypes );
kvbuf_add_key( reqTypes, "additionalAttrs" );
kvbuf_add_val( reqTypes, kDS1AttrMailAttribute );
kvbuf_add_val( reqTypes, kDSNAttrEMailAddress );
kvbuf_add_val( reqTypes, kDS1AttrFirstName );
kvbuf_add_val( reqTypes, kDS1AttrLastName );
status = ds_lookup( initProc, reqTypes, NULL );
kvbuf_free(reqTypes);
if ( status != KERN_SUCCESS ) return NULL;
setupList = TRUE;
}
kvbuf_t *request = kvbuf_query_key_val( "login", name );
if ( request != NULL ) {
ds_lookup( procno, request, &response );
kvbuf_free( request );
}
return response;
}
kvarray_t *
getpwnam_ext(const char *name)
{
kvarray_t *response = NULL;
if (name != NULL) {
response = getpwnam_ext_real(name);
if (response == NULL) {
(void) getpwnam_ext_real(NULL);
response = getpwnam_ext_real(name);
}
}
return response;
}
static const char *ds_get_value(const char *inUserID, const kvdict_t *in_dict, const char *in_attr, bool first_of_many)
{
const char *value = NULL;
int32_t i;
for (i = 0; i < in_dict->kcount; i++) {
if (!strcmp(in_dict->key[i], in_attr)) {
if (in_dict->vcount[i] == 1)
value = in_dict->val[i][0];
else if (in_dict->vcount[i] == 0) {
if ( strcmp( in_attr, kDS1AttrMailAttribute) != 0 )
msg_info("od[getpwnam_ext]: no value found for attribute %s in record for user %s", in_attr, inUserID);
} else if (first_of_many) {
if ( strcmp(in_attr, "pw_name") )
value = in_dict->val[i][0];
else {
int32_t j;
value = in_dict->val[i][0];
for (j = 0; j < in_dict->vcount[i]; j++) {
if ( strchr(in_dict->val[i][j], '@') == 0 ) {
value = in_dict->val[i][j];
break;
}
}
}
}
else
msg_info("od[getpwnam_ext]: multiple values (%u) found for attribute %s in record for user %s", in_dict->vcount[i], in_attr, inUserID);
break;
}
}
if (i >= in_dict->kcount && strcmp(in_attr, kDS1AttrMailAttribute) != 0)
msg_info("od[getpwnam_ext]: no attribute %s in record for user %s", in_attr, inUserID);
return value;
}
uid_t ads_get_uid ( const char *inUserID )
{
kvarray_t *user_data;
uid_t uid = 0;
assert(inUserID != NULL);
errno = 0;
user_data = getpwnam_ext(inUserID);
if (user_data != NULL) {
if (user_data->count == 1) {
kvdict_t *user_dict = &user_data->dict[0];
const char *value = ds_get_value(inUserID, user_dict, "pw_uid", TRUE);
if (value)
uid = atoi(value);
} else if (user_data->count > 1)
msg_error("od[getpwnam_ext]: multiple records (%u) found for user %s", user_data->count, inUserID);
else if (!user_data->count)
msg_error("od[getpwnam_ext]: no record found for user %s", inUserID);
kvarray_free(user_data);
} else if (errno)
msg_error("od[getpwnam_ext]: Unable to look up user record %s: %m", inUserID);
return(uid);
}
const char *ads_getpwnam ( const char *inUserID )
{
kvarray_t *user_data;
static char rec_name[512];
assert(inUserID != NULL);
memset(rec_name, 0, sizeof rec_name);
errno = 0;
user_data = getpwnam_ext(inUserID);
if (user_data != NULL) {
if (user_data->count == 1) {
kvdict_t *user_dict = &user_data->dict[0];
const char *value = ds_get_value(inUserID, user_dict, "pw_name", TRUE);
if (value)
strlcpy(rec_name, value, sizeof rec_name);
} else if (user_data->count > 1)
msg_error("od[getpwnam_ext]: multiple records (%u) found for user %s", user_data->count, inUserID);
else if (!user_data->count)
msg_error("od[getpwnam_ext]: no record found for user %s", inUserID);
kvarray_free(user_data);
} else if (errno)
msg_error("od[getpwnam_ext]: Unable to look up user record %s: %m", inUserID);
if (strlen(rec_name))
return(rec_name);
return( NULL );
}
int ads_get_user_options(const char *inUserID, struct od_user_opts *in_out_opts)
{
int out_status = 0;
kvarray_t *user_data;
char user_guid[ 64 ];
assert(inUserID != NULL && in_out_opts != NULL);
memset(in_out_opts, 0, sizeof *in_out_opts);
in_out_opts->fAcctState = eUnknownAcctState;
if (POSTFIX_OD_LOOKUP_START_ENABLED())
POSTFIX_OD_LOOKUP_START((char *) inUserID, in_out_opts);
errno = 0;
user_data = getpwnam_ext(inUserID);
if (user_data) {
if (user_data->count == 1) {
kvdict_t *user_dict = &user_data->dict[0];
const char *value;
memset(user_guid, 0, sizeof user_guid);
value = ds_get_value(inUserID, user_dict, "pw_uuid", FALSE);
if (value)
strlcpy(user_guid, value, sizeof user_guid);
struct stat st;
int i_stat = stat(kServerUserPath, &st);
if ( stat(kServerUserPath, &st) == 0 )
user_settings_path = kServerUserPath;
else
user_settings_path = kClientUserPath;
if ( !get_attributes_local(in_out_opts, user_guid) ) {
value = ds_get_value(inUserID, user_dict, kDS1AttrMailAttribute, FALSE);
if (value) {
CFMutableDictionaryRef user_dict = get_mail_attribute_values(value, in_out_opts);
set_attributes_local(user_dict, user_guid);
CFRelease(user_dict);
}
}
value = ds_get_value(inUserID, user_dict, "pw_name", TRUE);
if (value)
strlcpy(in_out_opts->fRecName, value, sizeof in_out_opts->fRecName);
} else if (user_data->count > 1)
msg_error("od[getpwnam_ext]: multiple records (%u) found for user %s", user_data->count, inUserID);
else if (!user_data->count)
msg_error("od[getpwnam_ext]: no record found for user %s", inUserID);
kvarray_free(user_data);
} else if (errno)
msg_error("od[getpwnam_ext]: unable to look up user record %s: %m", inUserID);
else
msg_error("od[getpwnam_ext]: no record for user %s", inUserID);
if (POSTFIX_OD_LOOKUP_FINISH_ENABLED())
POSTFIX_OD_LOOKUP_FINISH((char *) inUserID, in_out_opts, out_status);
return out_status;
}
int aod_get_user_options ( const char *inUserID, struct od_user_opts *in_out_opts )
{
assert((inUserID != NULL) && (in_out_opts != NULL));
memset( in_out_opts, 0, sizeof( struct od_user_opts ) );
in_out_opts->fAcctState = eUnknownAcctState;
if ( POSTFIX_OD_LOOKUP_START_ENABLED() )
POSTFIX_OD_LOOKUP_START((char *) inUserID, in_out_opts);
CFErrorRef cf_err_ref = NULL;
ODSessionRef od_session_ref = ODSessionCreate( kCFAllocatorDefault, NULL, &cf_err_ref );
if ( !od_session_ref ) {
print_cf_error( cf_err_ref, inUserID, "Unable to create OD Session" );
return( -1 );
}
ODNodeRef od_node_ref = ODNodeCreateWithNodeType( kCFAllocatorDefault, od_session_ref, kODNodeTypeAuthentication, &cf_err_ref );
if ( !od_node_ref ) {
print_cf_error( cf_err_ref, inUserID, "Unable to create OD Node Reference" );
CFRelease( od_session_ref );
return( -1 );
}
int out_status = get_user_attributes( od_node_ref, inUserID, in_out_opts );
CFRelease( od_node_ref );
CFRelease( od_session_ref );
if ( POSTFIX_OD_LOOKUP_FINISH_ENABLED() )
POSTFIX_OD_LOOKUP_FINISH((char *) inUserID, in_out_opts, out_status);
return( out_status );
}
static void print_cf_error ( CFErrorRef in_cf_err_ref, const char *in_user_name, const char *in_default_str )
{
if ( in_cf_err_ref != NULL ) {
CFStringRef cf_str_ref = CFErrorCopyFailureReason( in_cf_err_ref );
if ( cf_str_ref != NULL ) {
const char *err_str = CFStringGetCStringPtr( cf_str_ref, kCFStringEncodingUTF8 );
if ( err_str != NULL ) {
syslog( LOG_ERR, "od: user %s: %s", in_user_name, err_str );
CFRelease(cf_str_ref);
return;
}
CFRelease(cf_str_ref);
}
}
syslog( LOG_ERR, "od: user %s: %s", in_user_name, in_default_str );
}
static CFStringRef get_attr_from_record ( ODRecordRef in_rec_ref, CFStringRef in_attr )
{
CFErrorRef cf_err_ref = NULL;
CFArrayRef cf_arry_values = ODRecordCopyValues( in_rec_ref, in_attr, &cf_err_ref );
if ( !cf_arry_values )
return( NULL );
if ( CFArrayGetCount( cf_arry_values ) > 1 ) {
msg_error( "aod: multiple attribute values (%d) found in record user record: %s for attribute: %s",
(int)CFArrayGetCount( cf_arry_values ),
CFStringGetCStringPtr( ODRecordGetRecordName( in_rec_ref ), kCFStringEncodingUTF8 ),
CFStringGetCStringPtr( in_attr, kCFStringEncodingUTF8 ) );
CFRelease( cf_arry_values );
return( NULL );
}
CFStringRef cf_str_out = CFArrayGetValueAtIndex( cf_arry_values, 0 );
CFRetain( cf_str_out );
CFRelease( cf_arry_values );
return( cf_str_out );
}
static CFPropertyListRef read_user_settings ( const char *in_file, CFPropertyListMutabilityOptions in_opts )
{
CFPropertyListRef cf_prop_list = NULL;
CFURLRef cf_url = CFURLCreateFromFileSystemRepresentation(NULL, (const UInt8 *)in_file, strlen(in_file), FALSE);
if ( !cf_url ) {
msg_error( "aod: could not create URL from %s", in_file);
return( NULL );
}
SInt32 err;
CFDataRef cf_data = NULL;
if ( !CFURLCreateDataAndPropertiesFromResource(NULL, cf_url, &cf_data, NULL, NULL, &err) ) {
if ( msg_verbose )
msg_info("aod: no local user settings (%s), using defaults", in_file);
CFRelease(cf_url);
return( NULL );
}
CFStringRef cf_str_err = NULL;
if ( cf_data ) {
cf_prop_list = CFPropertyListCreateFromXMLData( kCFAllocatorDefault, cf_data, in_opts, &cf_str_err);
if ( cf_prop_list )
CFRetain(cf_prop_list);
} else
msg_error("aod: enable to create CFData ref for %s", in_file);
CFRelease(cf_url);
if ( cf_str_err )
CFRelease( cf_str_err );
if ( cf_data )
CFRelease( cf_data );
return( cf_prop_list );
}
static void write_user_settings ( const char *in_file, CFPropertyListRef in_data )
{
CFURLRef cf_url = CFURLCreateFromFileSystemRepresentation(NULL, (const UInt8 *)in_file, strlen(in_file), FALSE);
if ( !cf_url ) {
msg_error("aod: could not create URL from: %s", in_file);
return;
}
CFDataRef cf_data = CFPropertyListCreateXMLData(NULL, in_data);
if ( cf_data ) {
SInt32 write_err = noErr;
if ( !CFURLWriteDataAndPropertiesToResource(cf_url, cf_data, NULL, &write_err) )
if ( msg_verbose )
msg_warn("aod: could not write to %s (error: %d)", in_file, (int) write_err);
CFRelease(cf_data);
}
CFRelease(cf_url);
}
static bool get_attributes_local ( struct od_user_opts *in_out_opts, const char *in_user_guid )
{
bool b_out = FALSE;
CFDictionaryRef cf_dict_data = (CFDictionaryRef)read_user_settings( user_settings_path, kCFPropertyListImmutable );
if ( !cf_dict_data )
return( FALSE );
CFStringRef cf_str = CFStringCreateWithCString( NULL, in_user_guid, kCFStringEncodingUTF8 );
if ( cf_str ) {
if ( CFDictionaryContainsKey( cf_dict_data, cf_str ) ) {
CFDictionaryRef cf_dict_user = (CFDictionaryRef)CFDictionaryGetValue( cf_dict_data, cf_str );
if ( cf_dict_user && (CFGetTypeID( cf_dict_user ) == CFDictionaryGetTypeID()) ) {
get_acct_state( cf_dict_user, NULL, in_out_opts );
get_alt_loc( cf_dict_user, NULL );
get_mail_quota( cf_dict_user, NULL );
b_out = TRUE;
}
}
CFRelease( cf_str );
}
CFRelease( cf_dict_data );
return( b_out );
}
static void set_attributes_local ( CFMutableDictionaryRef in_user_dict, const char *in_user_guid )
{
CFDictionaryRef cf_dict_data = (CFDictionaryRef)read_user_settings(user_settings_path, kCFPropertyListMutableContainers);
if ( !cf_dict_data )
cf_dict_data = CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
&kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
if ( cf_dict_data && in_user_dict ) {
CFStringRef cf_str = CFStringCreateWithCString( NULL, in_user_guid, kCFStringEncodingUTF8 );
if ( cf_str != NULL ) {
CFMutableDictionaryRef cf_mut_dict = CFDictionaryCreateMutableCopy(kCFAllocatorDefault, 0, cf_dict_data);
if ( cf_mut_dict != NULL ) {
CFDictionaryAddValue( cf_mut_dict, cf_str, in_user_dict );
write_user_settings(user_settings_path, (CFPropertyListRef)cf_mut_dict );
CFRelease(cf_mut_dict);
}
CFRelease(cf_str);
}
}
}
static int get_user_attributes ( ODNodeRef in_node_ref, const char *in_user_name, struct od_user_opts *in_out_opts )
{
CFStringRef cf_str_ref = CFStringCreateWithCString( NULL, in_user_name, kCFStringEncodingUTF8 );
if ( !cf_str_ref ) {
msg_error( "aod: unable to create user name CFStringRef");
return( -1 );
}
ODRecordRef od_rec_ref = NULL;
CFErrorRef cf_err_ref = NULL;
CFTypeRef cf_type_ref[] = { CFSTR(kDSAttributesStandardAll) };
CFArrayRef cf_arry_ref = CFArrayCreate( NULL, cf_type_ref, 1, &kCFTypeArrayCallBacks );
ODQueryRef cf_query_ref = ODQueryCreateWithNode( NULL, in_node_ref, CFSTR(kDSStdRecordTypeUsers), CFSTR(kDSNAttrRecordName),
kODMatchInsensitiveEqualTo, cf_str_ref, cf_arry_ref, 100, &cf_err_ref );
if ( cf_query_ref ) {
CFArrayRef cf_arry_result = ODQueryCopyResults( cf_query_ref, false, &cf_err_ref );
if ( cf_arry_result ) {
if ( CFArrayGetCount( cf_arry_result ) == 1 ) {
od_rec_ref = (ODRecordRef)CFArrayGetValueAtIndex( cf_arry_result, 0 );
CFRetain(od_rec_ref);
} else {
if ( CFArrayGetCount( cf_arry_result ) == 0 )
msg_error( "aod: no user record found for: %s", in_user_name );
else
msg_error( "aod: multiple user records (%ld) found for: %s", CFArrayGetCount( cf_arry_result ), in_user_name );
}
CFRelease(cf_arry_result);
} else
print_cf_error( cf_err_ref, in_user_name, "aod: ODQueryCopyResults() failed" );
CFRelease( cf_query_ref );
} else
print_cf_error( cf_err_ref, in_user_name, "aod: ODQueryCreateWithNode() failed" );
CFRelease( cf_str_ref );
CFRelease( cf_arry_ref );
if ( !od_rec_ref ) {
print_cf_error( cf_err_ref, in_user_name, "aod: unable to lookup user record" );
return( -1 );
}
char user_guid[ 64 ];
size_t str_size = 0;
memset(user_guid, 0, sizeof user_guid);
CFStringRef cf_str_value = get_attr_from_record( od_rec_ref, CFSTR(kDS1AttrGeneratedUID) );
if ( cf_str_value ) {
str_size = CFStringGetMaximumSizeForEncoding(CFStringGetLength( cf_str_value ), kCFStringEncodingUTF8) + 1;
CFStringGetCString( cf_str_value, user_guid, str_size, kCFStringEncodingUTF8 );
CFRelease( cf_str_value );
}
cf_str_value = ODRecordGetRecordName( od_rec_ref );
if ( cf_str_value )
str_size = CFStringGetMaximumSizeForEncoding(CFStringGetLength( cf_str_value ), kCFStringEncodingUTF8) + 1;
if (str_size )
CFStringGetCString( cf_str_value, in_out_opts->fRecName, sizeof(in_out_opts->fRecName), kCFStringEncodingUTF8 );
if ( !get_attributes_local(in_out_opts, user_guid) ) {
cf_str_value = get_attr_from_record( od_rec_ref, CFSTR(kDS1AttrMailAttribute) );
if ( cf_str_value != NULL ) {
str_size = CFStringGetMaximumSizeForEncoding(CFStringGetLength( cf_str_value ), kCFStringEncodingUTF8) + 1;
char *c_str = malloc( str_size );
if ( c_str ) {
if( CFStringGetCString( cf_str_value, c_str, str_size, kCFStringEncodingUTF8 ) ) {
CFMutableDictionaryRef user_dict = get_mail_attribute_values( c_str, in_out_opts );
set_attributes_local(user_dict, user_guid);
CFRelease(user_dict);
}
free( c_str );
}
CFRelease( cf_str_value );
}
}
CFRelease(od_rec_ref);
return( 0 );
}
static CFMutableDictionaryRef get_mail_attribute_values ( const char *in_mail_attribute,
struct od_user_opts *in_out_opts )
{
CFMutableDictionaryRef cf_mut_dict_ref = NULL;
unsigned long ul_size = strlen( in_mail_attribute );
CFDataRef cf_data_ref = CFDataCreate( NULL, (const UInt8 *)in_mail_attribute, ul_size );
if ( !cf_data_ref )
return( NULL );
CFPropertyListRef cf_plist_ref = CFPropertyListCreateFromXMLData( kCFAllocatorDefault,
cf_data_ref, kCFPropertyListImmutable, NULL );
if ( cf_plist_ref ) {
if ( CFDictionaryGetTypeID() == CFGetTypeID( cf_plist_ref ) ) {
cf_mut_dict_ref = CFDictionaryCreateMutable( kCFAllocatorDefault,
0, &kCFCopyStringDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
CFRetain(cf_mut_dict_ref);
CFDictionaryAddValue( cf_mut_dict_ref, CFSTR(kXMLKeyAttrVersion), CFSTR(kXMLValueVersion2) );
CFDictionaryRef cf_dict_ref = (CFDictionaryRef)cf_plist_ref;
get_acct_state( cf_dict_ref, cf_mut_dict_ref, in_out_opts );
get_alt_loc( cf_dict_ref, cf_mut_dict_ref );
get_mail_quota( cf_dict_ref, cf_mut_dict_ref );
}
}
CFRelease( cf_data_ref );
return(cf_mut_dict_ref);
}
void get_acct_state ( CFDictionaryRef inCFDictRef, CFMutableDictionaryRef out_user_dict, struct od_user_opts *in_out_opts )
{
in_out_opts->fAcctState = eAcctEnabled;
if ( CFDictionaryContainsKey( inCFDictRef, CFSTR( kXMLKeyAcctState ) ) ) {
CFStringRef cf_str_ref = (CFStringRef)CFDictionaryGetValue( inCFDictRef, CFSTR( kXMLKeyAcctState ) );
if ( cf_str_ref ) {
if ( CFGetTypeID( cf_str_ref ) == CFStringGetTypeID() ) {
char *p_value = (char *)CFStringGetCStringPtr( cf_str_ref, kCFStringEncodingMacRoman );
if ( p_value ) {
if ( strcasecmp( p_value, kXMLValueAcctEnabled ) == 0 )
in_out_opts->fAcctState = eAcctEnabled;
else if ( strcasecmp( p_value, kXMLValueAcctDisabled ) == 0 )
in_out_opts->fAcctState = eAcctDisabled;
else if ( strcasecmp( p_value, kXMLValueAcctFwd ) == 0 )
get_auto_forward_addr( inCFDictRef, out_user_dict, in_out_opts );
if ( out_user_dict )
CFDictionaryAddValue( out_user_dict, CFSTR(kXMLKeyAcctState), cf_str_ref);
}
}
}
}
}
void get_auto_forward_addr ( CFDictionaryRef inCFDictRef, CFMutableDictionaryRef out_user_dict, struct od_user_opts *in_out_opts )
{
if ( CFDictionaryContainsKey( inCFDictRef, CFSTR( kXMLKeyAutoFwd ) ) ) {
CFStringRef cf_str_ref = (CFStringRef)CFDictionaryGetValue( inCFDictRef, CFSTR( kXMLKeyAutoFwd ) );
if ( cf_str_ref ) {
if ( CFGetTypeID( cf_str_ref ) == CFStringGetTypeID() ) {
char *p_value = (char *)CFStringGetCStringPtr( cf_str_ref, kCFStringEncodingMacRoman );
if ( p_value ) {
in_out_opts->fAcctState = eAcctForwarded;
strlcpy( in_out_opts->fAutoFwdAddr, p_value, sizeof(in_out_opts->fAutoFwdAddr) );
if ( out_user_dict )
CFDictionaryAddValue( out_user_dict, CFSTR(kXMLKeyAutoFwd), cf_str_ref);
}
}
}
}
}
static void get_alt_loc ( CFDictionaryRef inCFDictRef, CFMutableDictionaryRef out_user_dict )
{
if ( CFDictionaryContainsKey( inCFDictRef, CFSTR( kXMLKeyAltDataStoreLoc ) ) ) {
CFStringRef cf_str_ref = (CFStringRef)CFDictionaryGetValue( inCFDictRef, CFSTR( kXMLKeyAltDataStoreLoc ) );
if ( cf_str_ref && (CFGetTypeID( cf_str_ref ) == CFStringGetTypeID()) ) {
if ( out_user_dict )
CFDictionaryAddValue( out_user_dict, CFSTR(kXMLKeyAltDataStoreLoc), cf_str_ref);
}
}
}
static void get_mail_quota ( CFDictionaryRef inCFDictRef, CFMutableDictionaryRef out_user_dict )
{
if ( CFDictionaryContainsKey( inCFDictRef, CFSTR( kXMLKeyDiskQuota ) ) ) {
CFStringRef cf_str_ref = (CFStringRef)CFDictionaryGetValue( inCFDictRef, CFSTR( kXMLKeyDiskQuota ) );
if ( cf_str_ref && (CFGetTypeID( cf_str_ref ) == CFStringGetTypeID()) ) {
if ( out_user_dict )
CFDictionaryAddValue( out_user_dict, CFSTR(kXMLKeyDiskQuota), cf_str_ref);
}
}
}
int sacl_check(const char *inUserID)
{
int err, result;
uuid_t guid;
if (POSTFIX_SACL_START_ENABLED())
POSTFIX_SACL_START((char *) inUserID);
if (var_use_sacl_cache) {
result = SACL_CHECK_STATUS_UNKNOWN;
switch (sacl_cache_clnt_get(inUserID, &result)) {
case SACL_CACHE_STAT_OK:
if (result == SACL_CHECK_STATUS_AUTHORIZED) {
if (POSTFIX_SACL_CACHED_ENABLED())
POSTFIX_SACL_CACHED((char *) inUserID, 1);
return 1;
} else if (result == SACL_CHECK_STATUS_UNAUTHORIZED) {
if (POSTFIX_SACL_CACHED_ENABLED())
POSTFIX_SACL_CACHED((char *) inUserID, 0);
return 0;
} else if (result == SACL_CHECK_STATUS_NO_SACL) {
if (POSTFIX_SACL_CACHED_ENABLED())
POSTFIX_SACL_CACHED((char *) inUserID, -1);
return 1;
}
break;
case SACL_CACHE_STAT_BAD:
msg_warn("sacl_check: %s protocol error", var_sacl_cache_service);
break;
case SACL_CACHE_STAT_FAIL:
msg_warn("sacl_check: %s service failure", var_sacl_cache_service);
break;
}
}
err = mbr_user_name_to_uuid(inUserID, guid);
if (err) {
if (POSTFIX_SACL_RESOLVE_ENABLED())
POSTFIX_SACL_RESOLVE((char *) inUserID, 0);
msg_info("sacl_check: mbr_user_name_to_uuid(%s) failed: %s",
inUserID, strerror(err));
return -1;
}
if (POSTFIX_SACL_RESOLVE_ENABLED())
POSTFIX_SACL_RESOLVE((char *) inUserID, 1);
result = 0;
err = mbr_check_service_membership(guid, "mail", &result);
if (err) {
if (POSTFIX_SACL_FINISH_ENABLED())
POSTFIX_SACL_FINISH((char *) inUserID, -1);
if (err != ENOENT) {
msg_error("sacl_check: mbr_check_service_membership(%s, mail) failed: %s",
inUserID, strerror(err));
return -1;
}
if (var_use_sacl_cache) {
(void) sacl_cache_clnt_no_sacl();
}
return 1;
}
if (POSTFIX_SACL_FINISH_ENABLED())
POSTFIX_SACL_FINISH((char *) inUserID, result);
if (var_use_sacl_cache) {
switch (sacl_cache_clnt_put(inUserID, result ?
SACL_CHECK_STATUS_AUTHORIZED :
SACL_CHECK_STATUS_UNAUTHORIZED)) {
case SACL_CACHE_STAT_OK:
break;
case SACL_CACHE_STAT_BAD:
msg_warn("sacl_check: %s protocol error", var_sacl_cache_service);
break;
case SACL_CACHE_STAT_FAIL:
msg_warn("sacl_check: %s service failure", var_sacl_cache_service);
break;
}
}
return result;
}