1/* 2 * Copyright (c) 2004-2011 Apple Computer, Inc. All rights reserved. 3 * 4 * @APPLE_LICENSE_HEADER_START@ 5 * 6 * This file contains Original Code and/or Modifications of Original Code 7 * as defined in and that are subject to the Apple Public Source License 8 * Version 2.0 (the 'License'). You may not use this file except in 9 * compliance with the License. Please obtain a copy of the License at 10 * http://www.opensource.apple.com/apsl/ and read it before using this 11 * file. 12 * 13 * The Original Code and all software distributed under the License are 14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 18 * Please see the License for the specific language governing rights and 19 * limitations under the License. 20 * 21 * @APPLE_LICENSE_HEADER_END@ 22 */ 23 24#include "sys_defs.h" 25#include "aod.h" 26#include "msg.h" 27#include "mail_params.h" 28#include "sacl_cache_clnt.h" 29 30#include <string.h> 31#include <stdbool.h> 32#include <stdlib.h> 33#include <syslog.h> 34#include <stdio.h> 35#include <fcntl.h> 36#include <sys/stat.h> 37#include <sys/types.h> 38#include <sys/sysctl.h> 39#include <mach/mig_errors.h> 40 41#include <dtrace-postfix.h> 42 43#include <membership.h> 44#include <membershipPriv.h> 45 46#include <CoreDaemon/CoreDaemon.h> 47 48#include <CoreFoundation/CFData.h> 49#include <CoreFoundation/CFString.h> 50#include <CoreFoundation/CFNumber.h> 51#include <CoreFoundation/CFPropertyList.h> 52 53#include <OpenDirectory/OpenDirectory.h> 54#include <OpenDirectory/OpenDirectoryPriv.h> 55#include <DirectoryService/DirServicesConst.h> 56 57/* ----------------------------------------------------------------- 58 Prototypes 59 ----------------------------------------------------------------- */ 60 61static void print_cf_error ( CFErrorRef in_cf_err_ref, const char *in_user_name, const char *in_default_str ); 62static int get_user_attributes ( ODNodeRef in_node_ref, const char *in_user_name, struct od_user_opts *in_out_opts ); 63void get_acct_state ( CFDictionaryRef inCFDictRef, CFMutableDictionaryRef out_user_dict, struct od_user_opts *in_out_opts ); 64static void get_auto_forward_addr ( CFDictionaryRef inCFDictRef, CFMutableDictionaryRef out_user_dict, struct od_user_opts *in_out_opts ); 65static void get_alt_loc ( CFDictionaryRef inCFDictRef, CFMutableDictionaryRef out_user_dict ); 66static void get_mail_quota ( CFDictionaryRef inCFDictRef, CFMutableDictionaryRef out_user_dict ); 67static bool get_attributes_local ( struct od_user_opts *in_out_opts, const char *in_user_guid ); 68static void set_attributes_local ( CFMutableDictionaryRef in_user_dict, const char *in_user_guid ); 69 70static CFStringRef get_attr_from_record ( ODRecordRef in_rec_ref, CFStringRef in_attr ); 71static CFMutableDictionaryRef get_mail_attribute_values ( const char *in_mail_attribute, struct od_user_opts *in_out_opts ); 72 73char *user_settings_path = NULL; 74 75/* Begin DS SPI Glue */ 76#include <kvbuf.h> 77#include <opendirectory/DSlibinfoMIG.h> 78#include <opendirectory/DSmemberdMIG_types.h> 79#include <DirectoryService/DirectoryService.h> 80 81extern mach_port_t _ds_port; 82extern int _ds_running(); 83 84int g_bad_recip_cntr = 0; 85char g_client_addr[16] = ""; 86XSEventPortRef gEventPort = NULL; 87 88/* send server events 89 * event code 1: bad recipient, possible directory harvesting attack 90 * event code 2: failed authentication, possible SMTP relay password attach 91 */ 92 93void send_server_event ( const eEventCode in_event_code, const char *in_name, const char *in_addr ) 94{ 95 CFTypeRef keys[2]; 96 CFTypeRef values[2]; 97 CFStringRef cfstr_addr = NULL; 98 CFStringRef cfstr_event = NULL; 99 100 if ( !strlen(g_client_addr) || (strcmp(g_client_addr, in_addr) != 0) ) { 101 strlcpy(g_client_addr, in_addr, sizeof g_client_addr); 102 g_bad_recip_cntr = 0; 103 } 104 105 if ( g_bad_recip_cntr++ < 4 ) 106 return; 107 else 108 sleep( g_bad_recip_cntr >= 10 ? 10 : g_bad_recip_cntr ); 109 110 /* create a port to the event server */ 111 if ( gEventPort == NULL ) 112 gEventPort = XSEventPortCreate(nil); 113 114 keys[0] = CFSTR("eventType"); 115 keys[1] = CFSTR("host_address"); 116 117 /* set event code string */ 118 switch ( in_event_code ) { 119 case eBadRecipient: 120 cfstr_event = CFStringCreateWithCString(NULL, "smtp.receive.badrecipient", kCFStringEncodingMacRoman); 121 break; 122 case eAuthFailure: 123 cfstr_event = CFStringCreateWithCString(NULL, "auth.failure", kCFStringEncodingMacRoman); 124 break; 125 case eAuthSuccess: 126 cfstr_event = CFStringCreateWithCString(NULL, "auth.success", kCFStringEncodingMacRoman); 127 break; 128 default: 129 msg_warn("Warning: unknown sever event: %d", in_event_code); 130 return; 131 } 132 133 cfstr_addr = CFStringCreateWithCString(NULL, in_addr, kCFStringEncodingMacRoman); 134 135 values[0] = cfstr_event; 136 values[1] = cfstr_addr; 137 138 CFDictionaryRef dict_event = CFDictionaryCreate(NULL, keys, values, 139 sizeof(keys) / sizeof(keys[0]), 140 &kCFTypeDictionaryKeyCallBacks, 141 &kCFTypeDictionaryValueCallBacks); 142 143 /* send the event */ 144 (void)XSEventPortPostEvent(gEventPort, cfstr_event, dict_event); 145 146 CFRelease(cfstr_addr); 147 CFRelease(cfstr_event); 148 CFRelease(dict_event); 149} /* send_server_event */ 150 151 152void close_server_event_port ( void ) 153{ 154 if ( gEventPort != NULL ) 155 XSEventPortDelete(gEventPort); 156} /* close_server_event_port */ 157 158 159__private_extern__ kern_return_t 160get_procno( const char *procname, int32_t *procno ) 161{ 162 kern_return_t status; 163 security_token_t token; 164 bool lookAgain; 165 166 do { 167 lookAgain = false; 168 169 if (_ds_running() == 0) return KERN_FAILURE; 170 if (_ds_port == MACH_PORT_NULL) return KERN_FAILURE; 171 172 status = libinfoDSmig_GetProcedureNumber( _ds_port, (char *) procname, procno, &token ); 173 switch( status ) 174 { 175 case MACH_SEND_INVALID_DEST: 176 case MIG_SERVER_DIED: 177 mach_port_mod_refs( mach_task_self(), _ds_port, MACH_PORT_RIGHT_SEND, -1 ); 178 _ds_port = MACH_PORT_NULL; 179 lookAgain = true; 180 break; 181 182 case KERN_SUCCESS: 183 // is there a security call to parse this private token? 184 if ( token.val[0] != 0 ) { 185 (*procno) = -1; 186 status = KERN_FAILURE; 187 } 188 break; 189 190 default: 191 break; 192 } 193 } while ( lookAgain == true ); 194 195 return status; 196} 197 198__private_extern__ kern_return_t 199ds_lookup( int32_t procno, kvbuf_t *request, kvarray_t **answer ) 200{ 201 kern_return_t status; 202 security_token_t token; 203 bool lookAgain; 204 mach_msg_type_number_t oolen = 0; 205 vm_address_t oobuf = 0; 206 char ilbuf[MAX_MIG_INLINE_DATA]; 207 mach_msg_type_number_t illen = 0; 208 209 do { 210 lookAgain = false; 211 212 if ( _ds_running() == 0 ) return KERN_FAILURE; 213 if ( _ds_port == MACH_PORT_NULL ) return KERN_FAILURE; 214 if ( request == NULL ) return KERN_FAILURE; 215 216 status = libinfoDSmig_Query( _ds_port, procno, request->databuf, request->datalen, ilbuf, &illen, &oobuf, &oolen, &token ); 217 switch( status ) 218 { 219 case MACH_SEND_INVALID_DEST: 220 case MIG_SERVER_DIED: 221 mach_port_mod_refs( mach_task_self(), _ds_port, MACH_PORT_RIGHT_SEND, -1 ); 222 _ds_port = MACH_PORT_NULL; 223 lookAgain = true; 224 break; 225 226 case KERN_SUCCESS: 227 // is there a security call to parse this private token? 228 if ( token.val[0] == 0 ) { 229 if ( answer != NULL ) { 230 kvbuf_t *tempBuf; 231 232 if ( oolen != 0 ) { 233 tempBuf = kvbuf_init( (char *)oobuf, (uint32_t) oolen ); 234 } 235 else { 236 tempBuf = kvbuf_init( ilbuf, illen ); 237 } 238 239 (*answer) = kvbuf_decode( tempBuf ); 240 if ( (*answer) == NULL ) { 241 kvbuf_free( tempBuf ); 242 } 243 } 244 } 245 else { 246 // response came from a process not running as root 247 procno = -1; 248 status = KERN_FAILURE; 249 } 250 break; 251 252 default: 253 break; 254 } 255 } while ( lookAgain == true ); 256 257 if ( oolen != 0 ) { 258 vm_deallocate( mach_task_self(), oobuf, oolen ); 259 } 260 261 return status; 262} 263 264static kvarray_t * 265getpwnam_ext_real( const char *name ) 266{ 267 static int32_t procno = -1; 268 static int32_t initProc = -1; 269 static bool setupList = FALSE; 270 kvarray_t *response = NULL; 271 kern_return_t status; 272 273 if ( name == NULL ) { 274 /* reset cached state */ 275 procno = -1; 276 initProc = -1; 277 setupList = FALSE; 278 return NULL; 279 } 280 281 if ( procno == -1 ) { 282 status = get_procno( "getpwnam_ext", &procno ); 283 if ( status != KERN_SUCCESS ) return NULL; 284 } 285 286 if ( initProc == -1 ) { 287 status = get_procno( "getpwnam_initext", &initProc ); 288 if ( status != KERN_SUCCESS ) return NULL; 289 } 290 291 if (!setupList) { 292 kvbuf_t *reqTypes = kvbuf_new(); 293 294 /* The following are already included by default: 295 * kDSNAttrRecordName - pw_name 296 * kDS1AttrPassword - pw_pass 297 * kDS1AttrUniqueID - pw_uid 298 * kDS1AttrPrimaryGroupID - pw_gid 299 * kDS1AttrNFSHomeDirectory - pw_dir 300 * kDS1AttrUserShell - pw_shell 301 * kDS1AttrDistinguishedName- pw_gecos 302 * kDS1AttrGeneratedUID - pw_uuid 303 * 304 * kDSNAttrKeywords - not included, please file radar against DirectoryService 305 * kDSNAttrMetaNodeLocation - as-is 306 */ 307 308 kvbuf_add_dict( reqTypes ); 309 kvbuf_add_key( reqTypes, "additionalAttrs" ); 310 kvbuf_add_val( reqTypes, kDS1AttrMailAttribute ); 311 kvbuf_add_val( reqTypes, kDS1AttrFirstName ); 312 kvbuf_add_val( reqTypes, kDS1AttrLastName ); 313 314 status = ds_lookup( initProc, reqTypes, NULL ); 315 kvbuf_free(reqTypes); 316 if ( status != KERN_SUCCESS ) return NULL; 317 setupList = TRUE; 318 } 319 320 kvbuf_t *request = kvbuf_query_key_val( "login", name ); 321 if ( request != NULL ) { 322 ds_lookup( procno, request, &response ); 323 kvbuf_free( request ); 324 } 325 326 return response; 327} 328 329kvarray_t * 330getpwnam_ext(const char *name) 331{ 332 kvarray_t *response = NULL; 333 334 if (name != NULL) { 335 response = getpwnam_ext_real(name); 336 if (response == NULL) { 337 /* reset cached state */ 338 (void) getpwnam_ext_real(NULL); 339 340 /* retry once */ 341 response = getpwnam_ext_real(name); 342 } 343 } 344 345 return response; 346} 347/* End DS SPI Glue */ 348 349/* ------------------------------------------------------------------ 350 * ds_get_value () 351 */ 352static const char *ds_get_value(const char *inUserID, const kvdict_t *in_dict, const char *in_attr, bool first_of_many) 353{ 354 const char *value = NULL; 355 int32_t i; 356 357 for (i = 0; i < in_dict->kcount; i++) { 358 if (!strcmp(in_dict->key[i], in_attr)) { 359 if (in_dict->vcount[i] == 1) 360 value = in_dict->val[i][0]; 361 else if (in_dict->vcount[i] == 0) { 362 if ( strcmp( in_attr, kDS1AttrMailAttribute) != 0 ) 363 msg_info("od[getpwnam_ext]: no value found for attribute %s in record for user %s", in_attr, inUserID); 364 } else if (first_of_many) { 365 if ( strcmp(in_attr, "pw_name") ) 366 value = in_dict->val[i][0]; 367 else { 368 int32_t j; 369 value = in_dict->val[i][0]; 370 for (j = 0; j < in_dict->vcount[i]; j++) { 371 if ( strchr(in_dict->val[i][j], '@') == 0 ) { 372 value = in_dict->val[i][j]; 373 break; 374 } 375 } 376 } 377 } 378 else 379 msg_info("od[getpwnam_ext]: multiple values (%u) found for attribute %s in record for user %s", in_dict->vcount[i], in_attr, inUserID); 380 break; 381 } 382 } 383 if (i >= in_dict->kcount && strcmp(in_attr, kDS1AttrMailAttribute) != 0) 384 msg_info("od[getpwnam_ext]: no attribute %s in record for user %s", in_attr, inUserID); 385 386 return value; 387} /* ds_get_value */ 388 389/* ------------------------------------------------------------------ 390 * ads_get_uid () 391 */ 392uid_t ads_get_uid ( const char *inUserID ) 393{ 394 kvarray_t *user_data; 395 uid_t uid = 0; 396 397 assert(inUserID != NULL); 398 399 errno = 0; 400 user_data = getpwnam_ext(inUserID); 401 if (user_data != NULL) { 402 if (user_data->count == 1) { 403 kvdict_t *user_dict = &user_data->dict[0]; 404 const char *value = ds_get_value(inUserID, user_dict, "pw_uid", TRUE); 405 if (value) 406 uid = atoi(value); 407 } else if (user_data->count > 1) 408 msg_error("od[getpwnam_ext]: multiple records (%u) found for user %s", user_data->count, inUserID); 409 else if (!user_data->count) 410 msg_error("od[getpwnam_ext]: no record found for user %s", inUserID); 411 412 kvarray_free(user_data); 413 } else if (errno) 414 msg_error("od[getpwnam_ext]: Unable to look up user record %s: %m", inUserID); 415 416 return(uid); 417} /* ads_get_uid */ 418 419/* ------------------------------------------------------------------ 420 * ads_getpwnam () 421 */ 422const char *ads_getpwnam ( const char *inUserID ) 423{ 424 kvarray_t *user_data; 425 static char rec_name[512]; 426 427 assert(inUserID != NULL); 428 memset(rec_name, 0, sizeof rec_name); 429 430 errno = 0; 431 user_data = getpwnam_ext(inUserID); 432 if (user_data != NULL) { 433 if (user_data->count == 1) { 434 kvdict_t *user_dict = &user_data->dict[0]; 435 const char *value = ds_get_value(inUserID, user_dict, "pw_name", TRUE); 436 if (value) 437 strlcpy(rec_name, value, sizeof rec_name); 438 } else if (user_data->count > 1) 439 msg_error("od[getpwnam_ext]: multiple records (%u) found for user %s", user_data->count, inUserID); 440 else if (!user_data->count) 441 msg_error("od[getpwnam_ext]: no record found for user %s", inUserID); 442 443 kvarray_free(user_data); 444 } else if (errno) 445 msg_error("od[getpwnam_ext]: Unable to look up user record %s: %m", inUserID); 446 447 if (strlen(rec_name)) 448 return(rec_name); 449 return( NULL ); 450} /* ads_getpwnam */ 451 452/* ------------------------------------------------------------------ 453 * ads_get_user_options () 454 */ 455int ads_get_user_options(const char *inUserID, struct od_user_opts *in_out_opts) 456{ 457 int out_status = 0; 458 kvarray_t *user_data; 459 char user_guid[ 64 ]; 460 461 assert(inUserID != NULL && in_out_opts != NULL); 462 memset(in_out_opts, 0, sizeof *in_out_opts); 463 in_out_opts->fAcctState = eUnknownAcctState; 464 465 if (POSTFIX_OD_LOOKUP_START_ENABLED()) 466 POSTFIX_OD_LOOKUP_START((char *) inUserID, in_out_opts); 467 468 errno = 0; 469 user_data = getpwnam_ext(inUserID); 470 if (user_data) { 471 if (user_data->count == 1) { 472 kvdict_t *user_dict = &user_data->dict[0]; 473 const char *value; 474 475 /* get guid */ 476 memset(user_guid, 0, sizeof user_guid); 477 value = ds_get_value(inUserID, user_dict, "pw_uuid", FALSE); 478 if (value) 479 strlcpy(user_guid, value, sizeof user_guid); 480 481 struct stat st; 482 int i_stat = stat(kServerUserPath, &st); 483 if ( stat(kServerUserPath, &st) == 0 ) 484 user_settings_path = kServerUserPath; 485 else 486 user_settings_path = kClientUserPath; 487 488 if ( !get_attributes_local(in_out_opts, user_guid) ) { 489 value = ds_get_value(inUserID, user_dict, kDS1AttrMailAttribute, FALSE); 490 if (value) { 491 CFMutableDictionaryRef user_dict = get_mail_attribute_values(value, in_out_opts); 492 set_attributes_local(user_dict, user_guid); 493 CFRelease(user_dict); 494 } 495 } 496 497 // kDSNAttrRecordName 498 value = ds_get_value(inUserID, user_dict, "pw_name", TRUE); 499 if (value) 500 strlcpy(in_out_opts->fRecName, value, sizeof in_out_opts->fRecName); 501 } else if (user_data->count > 1) 502 msg_error("od[getpwnam_ext]: multiple records (%u) found for user %s", user_data->count, inUserID); 503 else if (!user_data->count) 504 msg_error("od[getpwnam_ext]: no record found for user %s", inUserID); 505 506 kvarray_free(user_data); 507 } else if (errno) 508 msg_error("od[getpwnam_ext]: unable to look up user record %s: %m", inUserID); 509 else 510 msg_error("od[getpwnam_ext]: no record for user %s", inUserID); 511 512 if (POSTFIX_OD_LOOKUP_FINISH_ENABLED()) 513 POSTFIX_OD_LOOKUP_FINISH((char *) inUserID, in_out_opts, out_status); 514 515 return out_status; 516} /* ads_get_user_options */ 517 518/* ------------------------------------------------------------------ 519 * aod_get_user_options () 520 */ 521int aod_get_user_options ( const char *inUserID, struct od_user_opts *in_out_opts ) 522{ 523 assert((inUserID != NULL) && (in_out_opts != NULL)); 524 525 memset( in_out_opts, 0, sizeof( struct od_user_opts ) ); 526 in_out_opts->fAcctState = eUnknownAcctState; 527 528 if ( POSTFIX_OD_LOOKUP_START_ENABLED() ) 529 POSTFIX_OD_LOOKUP_START((char *) inUserID, in_out_opts); 530 531 /* create default session */ 532 CFErrorRef cf_err_ref = NULL; 533 ODSessionRef od_session_ref = ODSessionCreate( kCFAllocatorDefault, NULL, &cf_err_ref ); 534 if ( !od_session_ref ) { 535 /* print the error and bail */ 536 print_cf_error( cf_err_ref, inUserID, "Unable to create OD Session" ); 537 return( -1 ); 538 } 539 540 /* get seach node */ 541 ODNodeRef od_node_ref = ODNodeCreateWithNodeType( kCFAllocatorDefault, od_session_ref, kODNodeTypeAuthentication, &cf_err_ref ); 542 if ( !od_node_ref ) { 543 /* print the error and bail */ 544 print_cf_error( cf_err_ref, inUserID, "Unable to create OD Node Reference" ); 545 546 /* release OD session */ 547 CFRelease( od_session_ref ); 548 return( -1 ); 549 } 550 551 /* get account state and auto-forward address, if any */ 552 int out_status = get_user_attributes( od_node_ref, inUserID, in_out_opts ); 553 554 CFRelease( od_node_ref ); 555 CFRelease( od_session_ref ); 556 557 if ( POSTFIX_OD_LOOKUP_FINISH_ENABLED() ) 558 POSTFIX_OD_LOOKUP_FINISH((char *) inUserID, in_out_opts, out_status); 559 560 return( out_status ); 561} /* aod_get_user_options */ 562 563 564/* ----------------------------------------------------------------- 565 Static functions 566 ----------------------------------------------------------------- */ 567 568/* ------------------------------------------------------------------ 569 * print_cf_error () 570 * 571 * print error returned in CFErrorRef 572 */ 573static void print_cf_error ( CFErrorRef in_cf_err_ref, const char *in_user_name, const char *in_default_str ) 574{ 575 if ( in_cf_err_ref != NULL ) { 576 CFStringRef cf_str_ref = CFErrorCopyFailureReason( in_cf_err_ref ); 577 if ( cf_str_ref != NULL ) { 578 const char *err_str = CFStringGetCStringPtr( cf_str_ref, kCFStringEncodingUTF8 ); 579 if ( err_str != NULL ) { 580 syslog( LOG_ERR, "od: user %s: %s", in_user_name, err_str ); 581 CFRelease(cf_str_ref); 582 return; 583 } 584 CFRelease(cf_str_ref); 585 } 586 } 587 syslog( LOG_ERR, "od: user %s: %s", in_user_name, in_default_str ); 588} /* print_cf_error */ 589 590/* ------------------------------------------------------------------ 591 * get_attr_from_record () 592 */ 593static CFStringRef get_attr_from_record ( ODRecordRef in_rec_ref, CFStringRef in_attr ) 594{ 595 CFErrorRef cf_err_ref = NULL; 596 CFArrayRef cf_arry_values = ODRecordCopyValues( in_rec_ref, in_attr, &cf_err_ref ); 597 if ( !cf_arry_values ) 598 return( NULL ); 599 600 if ( CFArrayGetCount( cf_arry_values ) > 1 ) { 601 msg_error( "aod: multiple attribute values (%d) found in record user record: %s for attribute: %s", 602 (int)CFArrayGetCount( cf_arry_values ), 603 CFStringGetCStringPtr( ODRecordGetRecordName( in_rec_ref ), kCFStringEncodingUTF8 ), 604 CFStringGetCStringPtr( in_attr, kCFStringEncodingUTF8 ) ); 605 CFRelease( cf_arry_values ); 606 return( NULL ); 607 } 608 CFStringRef cf_str_out = CFArrayGetValueAtIndex( cf_arry_values, 0 ); 609 CFRetain( cf_str_out ); 610 611 CFRelease( cf_arry_values ); 612 613 return( cf_str_out ); 614} /* get_attr_from_record */ 615 616/* ------------------------------------------------------------------ 617 * read_user_settings () 618 */ 619static CFPropertyListRef read_user_settings ( const char *in_file, CFPropertyListMutabilityOptions in_opts ) 620{ 621 CFPropertyListRef cf_prop_list = NULL; 622 623 CFURLRef cf_url = CFURLCreateFromFileSystemRepresentation(NULL, (const UInt8 *)in_file, strlen(in_file), FALSE); 624 if ( !cf_url ) { 625 msg_error( "aod: could not create URL from %s", in_file); 626 return( NULL ); 627 } 628 629 SInt32 err; 630 CFDataRef cf_data = NULL; 631 if ( !CFURLCreateDataAndPropertiesFromResource(NULL, cf_url, &cf_data, NULL, NULL, &err) ) { 632 if ( msg_verbose ) 633 msg_info("aod: no local user settings (%s), using defaults", in_file); 634 CFRelease(cf_url); 635 return( NULL ); 636 } 637 638 CFStringRef cf_str_err = NULL; 639 if ( cf_data ) { 640 cf_prop_list = CFPropertyListCreateFromXMLData( kCFAllocatorDefault, cf_data, in_opts, &cf_str_err); 641 if ( cf_prop_list ) 642 CFRetain(cf_prop_list); 643 } else 644 msg_error("aod: enable to create CFData ref for %s", in_file); 645 646 CFRelease(cf_url); 647 if ( cf_str_err ) 648 CFRelease( cf_str_err ); 649 if ( cf_data ) 650 CFRelease( cf_data ); 651 652 return( cf_prop_list ); 653} /* read_user_settings */ 654 655/* ------------------------------------------------------------------ 656 * write_user_settings () 657 */ 658static void write_user_settings ( const char *in_file, CFPropertyListRef in_data ) 659{ 660 CFURLRef cf_url = CFURLCreateFromFileSystemRepresentation(NULL, (const UInt8 *)in_file, strlen(in_file), FALSE); 661 if ( !cf_url ) { 662 msg_error("aod: could not create URL from: %s", in_file); 663 return; 664 } 665 666 CFDataRef cf_data = CFPropertyListCreateXMLData(NULL, in_data); 667 if ( cf_data ) { 668 SInt32 write_err = noErr; 669 if ( !CFURLWriteDataAndPropertiesToResource(cf_url, cf_data, NULL, &write_err) ) 670 if ( msg_verbose ) 671 msg_warn("aod: could not write to %s (error: %d)", in_file, (int) write_err); 672 CFRelease(cf_data); 673 } 674 CFRelease(cf_url); 675} /* write_user_settings */ 676 677/* ------------------------------------------------------------------ 678 * get_attributes_local () 679 */ 680static bool get_attributes_local ( struct od_user_opts *in_out_opts, const char *in_user_guid ) 681{ 682 bool b_out = FALSE; 683 684 /* look in local file first */ 685 CFDictionaryRef cf_dict_data = (CFDictionaryRef)read_user_settings( user_settings_path, kCFPropertyListImmutable ); 686 if ( !cf_dict_data ) 687 return( FALSE ); 688 689 CFStringRef cf_str = CFStringCreateWithCString( NULL, in_user_guid, kCFStringEncodingUTF8 ); 690 if ( cf_str ) { 691 if ( CFDictionaryContainsKey( cf_dict_data, cf_str ) ) { 692 CFDictionaryRef cf_dict_user = (CFDictionaryRef)CFDictionaryGetValue( cf_dict_data, cf_str ); 693 if ( cf_dict_user && (CFGetTypeID( cf_dict_user ) == CFDictionaryGetTypeID()) ) { 694 get_acct_state( cf_dict_user, NULL, in_out_opts ); 695 get_alt_loc( cf_dict_user, NULL ); 696 get_mail_quota( cf_dict_user, NULL ); 697 b_out = TRUE; 698 } 699 } 700 CFRelease( cf_str ); 701 } 702 CFRelease( cf_dict_data ); 703 704 return( b_out ); 705} /* get_attributes_local */ 706 707/* ------------------------------------------------------------------ 708 * set_attributes_local () 709 */ 710static void set_attributes_local ( CFMutableDictionaryRef in_user_dict, const char *in_user_guid ) 711{ 712 /* Get the file data */ 713 CFDictionaryRef cf_dict_data = (CFDictionaryRef)read_user_settings(user_settings_path, kCFPropertyListMutableContainers); 714 if ( !cf_dict_data ) 715 cf_dict_data = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, 716 &kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); 717 718 if ( cf_dict_data && in_user_dict ) { 719 CFStringRef cf_str = CFStringCreateWithCString( NULL, in_user_guid, kCFStringEncodingUTF8 ); 720 if ( cf_str != NULL ) { 721 CFMutableDictionaryRef cf_mut_dict = CFDictionaryCreateMutableCopy(kCFAllocatorDefault, 0, cf_dict_data); 722 if ( cf_mut_dict != NULL ) { 723 CFDictionaryAddValue( cf_mut_dict, cf_str, in_user_dict ); 724 write_user_settings(user_settings_path, (CFPropertyListRef)cf_mut_dict ); 725 CFRelease(cf_mut_dict); 726 } 727 CFRelease(cf_str); 728 } 729 } 730} /* set_attributes_local */ 731 732/* ------------------------------------------------------------------ 733 * get_user_attributes () 734 */ 735static int get_user_attributes ( ODNodeRef in_node_ref, const char *in_user_name, struct od_user_opts *in_out_opts ) 736{ 737 CFStringRef cf_str_ref = CFStringCreateWithCString( NULL, in_user_name, kCFStringEncodingUTF8 ); 738 if ( !cf_str_ref ) { 739 msg_error( "aod: unable to create user name CFStringRef"); 740 return( -1 ); 741 } 742 743 /* look up user record */ 744 ODRecordRef od_rec_ref = NULL; 745 CFErrorRef cf_err_ref = NULL; 746 CFTypeRef cf_type_ref[] = { CFSTR(kDSAttributesStandardAll) }; 747 CFArrayRef cf_arry_ref = CFArrayCreate( NULL, cf_type_ref, 1, &kCFTypeArrayCallBacks ); 748 ODQueryRef cf_query_ref = ODQueryCreateWithNode( NULL, in_node_ref, CFSTR(kDSStdRecordTypeUsers), CFSTR(kDSNAttrRecordName), 749 kODMatchInsensitiveEqualTo, cf_str_ref, cf_arry_ref, 100, &cf_err_ref ); 750 if ( cf_query_ref ) { 751 CFArrayRef cf_arry_result = ODQueryCopyResults( cf_query_ref, false, &cf_err_ref ); 752 if ( cf_arry_result ) { 753 if ( CFArrayGetCount( cf_arry_result ) == 1 ) { 754 od_rec_ref = (ODRecordRef)CFArrayGetValueAtIndex( cf_arry_result, 0 ); 755 CFRetain(od_rec_ref); 756 } else { 757 if ( CFArrayGetCount( cf_arry_result ) == 0 ) 758 msg_error( "aod: no user record found for: %s", in_user_name ); 759 else 760 msg_error( "aod: multiple user records (%ld) found for: %s", CFArrayGetCount( cf_arry_result ), in_user_name ); 761 } 762 CFRelease(cf_arry_result); 763 } else 764 print_cf_error( cf_err_ref, in_user_name, "aod: ODQueryCopyResults() failed" ); 765 766 CFRelease( cf_query_ref ); 767 } else 768 print_cf_error( cf_err_ref, in_user_name, "aod: ODQueryCreateWithNode() failed" ); 769 770 CFRelease( cf_str_ref ); 771 CFRelease( cf_arry_ref ); 772 773 if ( !od_rec_ref ) { 774 /* print the error and bail */ 775 print_cf_error( cf_err_ref, in_user_name, "aod: unable to lookup user record" ); 776 return( -1 ); 777 } 778 779 /* get guid */ 780 char user_guid[ 64 ]; 781 size_t str_size = 0; 782 memset(user_guid, 0, sizeof user_guid); 783 CFStringRef cf_str_value = get_attr_from_record( od_rec_ref, CFSTR(kDS1AttrGeneratedUID) ); 784 if ( cf_str_value ) { 785 str_size = CFStringGetMaximumSizeForEncoding(CFStringGetLength( cf_str_value ), kCFStringEncodingUTF8) + 1; 786 CFStringGetCString( cf_str_value, user_guid, str_size, kCFStringEncodingUTF8 ); 787 788 CFRelease( cf_str_value ); 789 } 790 791 /* get record name */ 792 cf_str_value = ODRecordGetRecordName( od_rec_ref ); 793 if ( cf_str_value ) 794 str_size = CFStringGetMaximumSizeForEncoding(CFStringGetLength( cf_str_value ), kCFStringEncodingUTF8) + 1; 795 if (str_size ) 796 CFStringGetCString( cf_str_value, in_out_opts->fRecName, sizeof(in_out_opts->fRecName), kCFStringEncodingUTF8 ); 797 798 /* get mail attribute */ 799 if ( !get_attributes_local(in_out_opts, user_guid) ) { 800 cf_str_value = get_attr_from_record( od_rec_ref, CFSTR(kDS1AttrMailAttribute) ); 801 if ( cf_str_value != NULL ) { 802 str_size = CFStringGetMaximumSizeForEncoding(CFStringGetLength( cf_str_value ), kCFStringEncodingUTF8) + 1; 803 char *c_str = malloc( str_size ); 804 if ( c_str ) { 805 if( CFStringGetCString( cf_str_value, c_str, str_size, kCFStringEncodingUTF8 ) ) { 806 CFMutableDictionaryRef user_dict = get_mail_attribute_values( c_str, in_out_opts ); 807 set_attributes_local(user_dict, user_guid); 808 CFRelease(user_dict); 809 } 810 free( c_str ); 811 } 812 CFRelease( cf_str_value ); 813 } 814 } 815 816 CFRelease(od_rec_ref); 817 818 return( 0 ); 819} /* get_user_attributes */ 820 821/* ------------------------------------------------------------------ 822 * get_mail_attribute_values () 823 */ 824static CFMutableDictionaryRef get_mail_attribute_values ( const char *in_mail_attribute, 825 struct od_user_opts *in_out_opts ) 826{ 827 CFMutableDictionaryRef cf_mut_dict_ref = NULL; 828 unsigned long ul_size = strlen( in_mail_attribute ); 829 CFDataRef cf_data_ref = CFDataCreate( NULL, (const UInt8 *)in_mail_attribute, ul_size ); 830 if ( !cf_data_ref ) 831 return( NULL ); 832 833 CFPropertyListRef cf_plist_ref = CFPropertyListCreateFromXMLData( kCFAllocatorDefault, 834 cf_data_ref, kCFPropertyListImmutable, NULL ); 835 if ( cf_plist_ref ) { 836 if ( CFDictionaryGetTypeID() == CFGetTypeID( cf_plist_ref ) ) { 837 cf_mut_dict_ref = CFDictionaryCreateMutable( kCFAllocatorDefault, 838 0, &kCFCopyStringDictionaryKeyCallBacks, 839 &kCFTypeDictionaryValueCallBacks); 840 CFRetain(cf_mut_dict_ref); 841 CFDictionaryAddValue( cf_mut_dict_ref, CFSTR(kXMLKeyAttrVersion), CFSTR(kXMLValueVersion2) ); 842 843 CFDictionaryRef cf_dict_ref = (CFDictionaryRef)cf_plist_ref; 844 get_acct_state( cf_dict_ref, cf_mut_dict_ref, in_out_opts ); 845 get_alt_loc( cf_dict_ref, cf_mut_dict_ref ); 846 get_mail_quota( cf_dict_ref, cf_mut_dict_ref ); 847 } 848 } 849 CFRelease( cf_data_ref ); 850 851 return(cf_mut_dict_ref); 852} /* get_mail_attribute_values */ 853 854/* ------------------------------------------------------------------ 855 * get_acct_state () 856 */ 857void get_acct_state ( CFDictionaryRef inCFDictRef, CFMutableDictionaryRef out_user_dict, struct od_user_opts *in_out_opts ) 858{ 859 /* enabled by default */ 860 in_out_opts->fAcctState = eAcctEnabled; 861 862 if ( CFDictionaryContainsKey( inCFDictRef, CFSTR( kXMLKeyAcctState ) ) ) { 863 CFStringRef cf_str_ref = (CFStringRef)CFDictionaryGetValue( inCFDictRef, CFSTR( kXMLKeyAcctState ) ); 864 if ( cf_str_ref ) { 865 if ( CFGetTypeID( cf_str_ref ) == CFStringGetTypeID() ) { 866 char *p_value = (char *)CFStringGetCStringPtr( cf_str_ref, kCFStringEncodingMacRoman ); 867 if ( p_value ) { 868 if ( strcasecmp( p_value, kXMLValueAcctEnabled ) == 0 ) 869 in_out_opts->fAcctState = eAcctEnabled; 870 else if ( strcasecmp( p_value, kXMLValueAcctDisabled ) == 0 ) 871 in_out_opts->fAcctState = eAcctDisabled; 872 else if ( strcasecmp( p_value, kXMLValueAcctFwd ) == 0 ) 873 get_auto_forward_addr( inCFDictRef, out_user_dict, in_out_opts ); 874 875 if ( out_user_dict ) 876 CFDictionaryAddValue( out_user_dict, CFSTR(kXMLKeyAcctState), cf_str_ref); 877 } 878 } 879 } 880 } 881} /* get_acct_state */ 882 883/* ------------------------------------------------------------------ 884 * get_auto_forward_addr () 885 */ 886void get_auto_forward_addr ( CFDictionaryRef inCFDictRef, CFMutableDictionaryRef out_user_dict, struct od_user_opts *in_out_opts ) 887{ 888 if ( CFDictionaryContainsKey( inCFDictRef, CFSTR( kXMLKeyAutoFwd ) ) ) { 889 CFStringRef cf_str_ref = (CFStringRef)CFDictionaryGetValue( inCFDictRef, CFSTR( kXMLKeyAutoFwd ) ); 890 if ( cf_str_ref ) { 891 if ( CFGetTypeID( cf_str_ref ) == CFStringGetTypeID() ) { 892 char *p_value = (char *)CFStringGetCStringPtr( cf_str_ref, kCFStringEncodingMacRoman ); 893 if ( p_value ) { 894 in_out_opts->fAcctState = eAcctForwarded; 895 strlcpy( in_out_opts->fAutoFwdAddr, p_value, sizeof(in_out_opts->fAutoFwdAddr) ); 896 897 if ( out_user_dict ) 898 CFDictionaryAddValue( out_user_dict, CFSTR(kXMLKeyAutoFwd), cf_str_ref); 899 } 900 } 901 } 902 } 903} /* get_auto_forward_addr */ 904 905/* ------------------------------------------------------------------ 906 * get_alt_loc () 907 */ 908static void get_alt_loc ( CFDictionaryRef inCFDictRef, CFMutableDictionaryRef out_user_dict ) 909{ 910 if ( CFDictionaryContainsKey( inCFDictRef, CFSTR( kXMLKeyAltDataStoreLoc ) ) ) { 911 CFStringRef cf_str_ref = (CFStringRef)CFDictionaryGetValue( inCFDictRef, CFSTR( kXMLKeyAltDataStoreLoc ) ); 912 if ( cf_str_ref && (CFGetTypeID( cf_str_ref ) == CFStringGetTypeID()) ) { 913 if ( out_user_dict ) 914 CFDictionaryAddValue( out_user_dict, CFSTR(kXMLKeyAltDataStoreLoc), cf_str_ref); 915 } 916 } 917} /* get_alt_loc */ 918 919/* ------------------------------------------------------------------ 920 * get_mail_quota () 921 */ 922static void get_mail_quota ( CFDictionaryRef inCFDictRef, CFMutableDictionaryRef out_user_dict ) 923{ 924 if ( CFDictionaryContainsKey( inCFDictRef, CFSTR( kXMLKeyDiskQuota ) ) ) { 925 CFStringRef cf_str_ref = (CFStringRef)CFDictionaryGetValue( inCFDictRef, CFSTR( kXMLKeyDiskQuota ) ); 926 if ( cf_str_ref && (CFGetTypeID( cf_str_ref ) == CFStringGetTypeID()) ) { 927 if ( out_user_dict ) 928 CFDictionaryAddValue( out_user_dict, CFSTR(kXMLKeyDiskQuota), cf_str_ref); 929 } 930 } 931} /* get_mail_quota */ 932 933 934int sacl_check(const char *inUserID) 935{ 936 int err, result; 937 uuid_t guid; 938 939 if (POSTFIX_SACL_START_ENABLED()) 940 POSTFIX_SACL_START((char *) inUserID); 941 942 if (var_use_sacl_cache) { 943 /* look up in cache */ 944 result = SACL_CHECK_STATUS_UNKNOWN; 945 switch (sacl_cache_clnt_get(inUserID, &result)) { 946 case SACL_CACHE_STAT_OK: 947 if (result == SACL_CHECK_STATUS_AUTHORIZED) { 948 if (POSTFIX_SACL_CACHED_ENABLED()) 949 POSTFIX_SACL_CACHED((char *) inUserID, 1); 950 return 1; 951 } else if (result == SACL_CHECK_STATUS_UNAUTHORIZED) { 952 if (POSTFIX_SACL_CACHED_ENABLED()) 953 POSTFIX_SACL_CACHED((char *) inUserID, 0); 954 return 0; 955 } else if (result == SACL_CHECK_STATUS_NO_SACL) { 956 if (POSTFIX_SACL_CACHED_ENABLED()) 957 POSTFIX_SACL_CACHED((char *) inUserID, -1); 958 return 1; 959 } 960 break; 961 case SACL_CACHE_STAT_BAD: 962 msg_warn("sacl_check: %s protocol error", var_sacl_cache_service); 963 break; 964 case SACL_CACHE_STAT_FAIL: 965 msg_warn("sacl_check: %s service failure", var_sacl_cache_service); 966 break; 967 } 968 } 969 970 /* cache miss; perform SACL check */ 971 err = mbr_user_name_to_uuid(inUserID, guid); 972 if (err) { 973 if (POSTFIX_SACL_RESOLVE_ENABLED()) 974 POSTFIX_SACL_RESOLVE((char *) inUserID, 0); 975 if ( msg_verbose ) 976 msg_info("sacl_check: mbr_user_name_to_uuid(%s) failed: %s", 977 inUserID, strerror(err)); 978 979 return( -1 ); 980 } 981 982 if (POSTFIX_SACL_RESOLVE_ENABLED()) 983 POSTFIX_SACL_RESOLVE((char *) inUserID, 1); 984 985 result = 0; 986 err = mbr_check_service_membership(guid, "mail", &result); 987 if (err) { 988 if (POSTFIX_SACL_FINISH_ENABLED()) 989 POSTFIX_SACL_FINISH((char *) inUserID, -1); 990 991 if (err != ENOENT) { 992 msg_error("sacl_check: mbr_check_service_membership(%s, mail) failed: %s", 993 inUserID, strerror(err)); 994 return -1; 995 } 996 997 if (var_use_sacl_cache) { 998 /* mail SACL is off. tell cache */ 999 (void) sacl_cache_clnt_no_sacl(); 1000 } 1001 return 1; 1002 } 1003 1004 if (POSTFIX_SACL_FINISH_ENABLED()) 1005 POSTFIX_SACL_FINISH((char *) inUserID, result); 1006 1007 if (var_use_sacl_cache) { 1008 /* update cache */ 1009 switch (sacl_cache_clnt_put(inUserID, result ? 1010 SACL_CHECK_STATUS_AUTHORIZED : 1011 SACL_CHECK_STATUS_UNAUTHORIZED)) { 1012 case SACL_CACHE_STAT_OK: 1013 break; 1014 case SACL_CACHE_STAT_BAD: 1015 msg_warn("sacl_check: %s protocol error", var_sacl_cache_service); 1016 break; 1017 case SACL_CACHE_STAT_FAIL: 1018 msg_warn("sacl_check: %s service failure", var_sacl_cache_service); 1019 break; 1020 } 1021 } 1022 1023 return result; 1024} /* sacl_check */ 1025