1#include "portable.h" 2 3#include <ac/string.h> 4#include <ac/ctype.h> 5#include "slap.h" 6#include "ldif.h" 7#include "config.h" 8#define __COREFOUNDATION_CFFILESECURITY__ 9#include <CoreFoundation/CoreFoundation.h> 10#include <HeimODAdmin/HeimODAdmin.h> 11#include <Heimdal/krb5.h> 12#include <CommonAuth/CommonAuth.h> 13#include <SystemConfiguration/SystemConfiguration.h> 14#include "applehelpers.h" 15#include <sys/types.h> 16#include <sys/socket.h> 17#include <ifaddrs.h> 18#include <arpa/inet.h> 19 20/* For PWS compatible DES encrypted plaintext password */ 21#include <PasswordServer/AuthDBFileDefs.h> 22#include <PasswordServer/AuthFile.h> 23 24extern AttributeDescription *idattr_uuid; 25extern AttributeDescription *idattr_memberships; 26extern AttributeDescription *idattr_memberUid; 27extern int idattr_is_member(Operation *op, struct berval* groupDN, AttributeDescription *searchAttr, struct berval* searchID, u_int32_t* result); 28 29extern BerVarray cf_accountpolicy_override; 30 31static Filter generic_filter = { LDAP_FILTER_PRESENT, { 0 }, NULL }; 32static struct berval generic_filterstr = BER_BVC("(objectclass=*)"); 33 34static AttributeDescription *failedLoginsAD = NULL; 35static AttributeDescription *creationDateAD = NULL; 36static AttributeDescription *passModDateAD = NULL; 37static AttributeDescription *lastLoginAD = NULL; 38static AttributeDescription *lastFailedLoginAD = NULL; 39static AttributeDescription *disableReasonAD = NULL; 40static AttributeDescription *realnameAD = NULL; 41static AttributeDescription *pwslocAD = NULL; 42static AttributeDescription *appleAccountPolicyAD = NULL; 43// shared with odusers 44AttributeDescription *passwordRequiredDateAD = NULL; 45AttributeDescription *policyAD = NULL; 46 47#undef malloc 48#undef free 49 50// This is the generic callback function for odusers_copy_entry, which just 51// returns a copy of the found Entry* 52static int odusers_lookup(Operation *op, SlapReply *rs) { 53 if(rs->sr_type != REP_SEARCH) return 0; 54 if(rs->sr_err != LDAP_SUCCESS) { 55 Debug(LDAP_DEBUG_ANY, "%s: Unable to locate entry (%d)\n", __PRETTY_FUNCTION__, rs->sr_err, 0); 56 return -1; 57 } 58 59 if(rs->sr_un.sru_search.r_entry) { 60 *(Entry**)(op->o_callback->sc_private) = entry_dup(rs->sr_un.sru_search.r_entry); 61 return 0; 62 } 63 64 return -1; 65} 66 67Attribute *odusers_copy_attr(char *dn, char*attribute) 68{ 69 OperationBuffer opbuf; 70 Connection conn; 71 Operation *fakeop = NULL; 72 Entry *e = NULL; 73 AttributeDescription *attrdesc = NULL; 74 const char *text = NULL; 75 Attribute *attr = NULL, *tmpattr = NULL; 76 77 connection_fake_init2(&conn, &opbuf, ldap_pvt_thread_pool_context(), 0); 78 fakeop = &opbuf.ob_op; 79 fakeop->o_req_dn.bv_len = asprintf(&fakeop->o_req_dn.bv_val, dn); 80 fakeop->o_req_dn.bv_len = strlen(fakeop->o_req_dn.bv_val); 81 fakeop->o_dn = fakeop->o_ndn = fakeop->o_req_ndn = fakeop->o_req_dn; 82 fakeop->o_conn->c_listener->sl_url.bv_val = "ldapi://%2Fvar%2Frun%2Fldapi"; 83 fakeop->o_conn->c_listener->sl_url.bv_len = strlen("ldapi://%2Fvar%2Frun%2Fldapi"); 84 85 e = odusers_copy_entry(fakeop); 86 if(!e) goto out; 87 88 if(slap_str2ad(attribute, &attrdesc, &text) != 0) { 89 Debug(LDAP_DEBUG_ANY, "%s: Unable to retrieve description of %s attribute", __PRETTY_FUNCTION__, attribute, 0); 90 goto out; 91 } 92 93 tmpattr = attr_find( e->e_attrs, attrdesc ); 94 95 if (tmpattr) { 96 attr = attr_dup(tmpattr); 97 } 98out: 99 if(e) entry_free(e); 100 if(fakeop && fakeop->o_req_dn.bv_val) { 101 free(fakeop->o_req_dn.bv_val); 102 fakeop->o_req_dn.bv_val = NULL; 103 } 104 return attr; 105} 106 107// Queries for the specified DN, returns the Entry pointer. The caller needs 108// to free this Entry. 109// NULL is returned on error or if nothing is found. 110Entry *odusers_copy_entry(Operation *op) { 111 Entry *e = NULL; 112 SlapReply rs = {REP_RESULT}; 113 slap_callback cb = {NULL, odusers_lookup, NULL, NULL}; 114 115 if(!op) { 116 Debug(LDAP_DEBUG_TRACE, "%s: no operation to perform\n", __PRETTY_FUNCTION__, 0, 0); 117 return NULL; 118 } 119 120 op->o_bd = select_backend(&op->o_req_ndn, 1); 121 if(!op->o_bd) { 122 Debug(LDAP_DEBUG_TRACE, "%s: could not find backend for: %s\n", __PRETTY_FUNCTION__, op->o_req_ndn.bv_val, 0); 123 return NULL; 124 } 125 126 generic_filter.f_desc = slap_schema.si_ad_objectClass; 127 op->o_do_not_cache = 1; 128 slap_op_time(&op->o_time, &op->o_tincr); 129 op->o_tag = LDAP_REQ_SEARCH; 130 op->ors_scope = LDAP_SCOPE_BASE; 131 op->ors_deref = LDAP_DEREF_NEVER; 132 op->ors_tlimit = SLAP_NO_LIMIT; 133 op->ors_slimit = 1; 134 op->ors_filter = &generic_filter; 135 op->ors_filterstr = generic_filterstr; 136 op->ors_attrs = NULL; 137 cb.sc_private = &e; 138 op->o_callback = &cb; 139 140 op->o_bd->be_search(op, &rs); 141 if(rs.sr_err != LDAP_SUCCESS) { 142 Debug(LDAP_DEBUG_TRACE, "%s: Unable to locate %s (%d)\n", __PRETTY_FUNCTION__, op->o_req_ndn.bv_val, rs.sr_err); 143 return NULL; 144 } 145 146 return e; 147} 148 149int odusers_remove_authdata(char *slotid) { 150 OperationBuffer opbuf; 151 Operation *op; 152 Connection conn = {0}; 153 Entry *e = NULL; 154 SlapReply rs = {REP_RESULT}; 155 slap_callback cb = {NULL, odusers_lookup, NULL, NULL}; 156 struct berval dn; 157 int ret = -1; 158 159 dn.bv_val = NULL; 160 dn.bv_len = asprintf(&dn.bv_val, "authGUID=%s,cn=users,cn=authdata", slotid); 161 162 memset(&opbuf, 0, sizeof(opbuf)); 163 memset(&conn, 0, sizeof(conn)); 164 connection_fake_init2(&conn, &opbuf, ldap_pvt_thread_pool_context(), 0); 165 op = &opbuf.ob_op; 166 167 op->o_dn = op->o_ndn = op->o_req_dn = op->o_req_ndn = dn; 168 op->o_bd = frontendDB; 169 170 slap_op_time(&op->o_time, &op->o_tincr); 171 op->o_tag = LDAP_REQ_DELETE; 172 op->o_conn->c_listener->sl_url.bv_val = "ldapi://%2Fvar%2Frun%2Fldapi"; 173 op->o_conn->c_listener->sl_url.bv_len = strlen("ldapi://%2Fvar%2Frun%2Fldapi"); 174 175 op->o_bd->be_delete(op, &rs); 176 if(rs.sr_err != LDAP_SUCCESS) { 177 Debug(LDAP_DEBUG_ANY, "%s: Unable to delete %s (%d)\n", __PRETTY_FUNCTION__, dn.bv_val, rs.sr_err); 178 goto out; 179 } 180 181 ret = 0; 182 183out: 184 if(dn.bv_val) free(dn.bv_val); 185 return ret; 186} 187 188int odusers_get_authguid(Entry *e, char *guidstr) { 189 int ret = -1; 190 // Figure out the authdata record's DN from the PWS auth authority 191 AttributeDescription *aa = slap_schema.si_ad_authAuthority; 192 Attribute *a = attr_find(e->e_attrs, aa); 193 if(!a) { 194 Debug(LDAP_DEBUG_ANY, "%s: could not locate authAuthority attribute for: %s\n", __PRETTY_FUNCTION__, e->e_dn, 0); 195 goto out; 196 } 197 198 int i; 199 for(i = 0; i < a->a_numvals; i++) { 200 if(memcmp(a->a_vals[i].bv_val, ";ApplePasswordServer;", 21) == 0) { 201 strncpy(guidstr, a->a_vals[i].bv_val+23, 8); 202 guidstr[8] = '-'; 203 strncpy(guidstr+9, a->a_vals[i].bv_val+31, 4); 204 guidstr[13] = '-'; 205 strncpy(guidstr+14, a->a_vals[i].bv_val+35, 4); 206 guidstr[18] = '-'; 207 strncpy(guidstr+19, a->a_vals[i].bv_val+39, 4); 208 guidstr[23] = '-'; 209 strncpy(guidstr+24, a->a_vals[i].bv_val+43, 12); 210 guidstr[36] = '\0'; 211 212 break; 213 } 214 } 215 216 if(guidstr[0] == '\0') { 217 Debug(LDAP_DEBUG_ANY, "%s: error parsing slotid", __PRETTY_FUNCTION__, 0, 0); 218 goto out; 219 } 220 221 ret = 0; 222out: 223 224 return ret; 225} 226 227Entry *odusers_copy_authdata(struct berval *dn) { 228 OperationBuffer opbuf; 229 Connection conn; 230 Operation *fakeop = NULL; 231 Entry *e = NULL; 232 Entry *ret = NULL; 233 char guidstr[37]; 234 struct berval authdn; 235 236 connection_fake_init2(&conn, &opbuf, ldap_pvt_thread_pool_context(), 0); 237 fakeop = &opbuf.ob_op; 238 fakeop->o_dn = *dn; 239 dnNormalize(0, NULL, NULL, dn, &fakeop->o_ndn, NULL); 240 fakeop->o_req_dn = *dn; 241 dnNormalize(0, NULL, NULL, dn, &fakeop->o_req_ndn, NULL); 242 243 e = odusers_copy_entry(fakeop); 244 if(!e) { 245 Debug(LDAP_DEBUG_TRACE, "%s: No entry associated with %s\n", __PRETTY_FUNCTION__, fakeop->o_req_ndn.bv_val, 0); 246 goto out; 247 } 248 249 if( odusers_get_authguid(e, guidstr) != 0) { 250 Debug(LDAP_DEBUG_TRACE, "%s: Could not locate authguid for record %s", __PRETTY_FUNCTION__, dn->bv_val, 0); 251 goto out; 252 } 253 254 entry_free(e); 255 e = NULL; 256 257 authdn.bv_len = asprintf(&authdn.bv_val, "authGUID=%s,cn=users,cn=authdata", guidstr); 258 259 if(fakeop && !BER_BVISNULL(&fakeop->o_ndn)) ch_free(fakeop->o_ndn.bv_val); 260 if(fakeop && !BER_BVISNULL(&fakeop->o_req_ndn)) ch_free(fakeop->o_req_ndn.bv_val); 261 fakeop->o_dn = fakeop->o_req_dn = authdn; 262 dnNormalize(0, NULL, NULL, &fakeop->o_dn, &fakeop->o_ndn, NULL); 263 dnNormalize(0, NULL, NULL, &fakeop->o_req_dn, &fakeop->o_req_ndn, NULL); 264 fakeop->o_conn->c_listener->sl_url.bv_val = "ldapi://%2Fvar%2Frun%2Fldapi"; 265 fakeop->o_conn->c_listener->sl_url.bv_len = strlen("ldapi://%2Fvar%2Frun%2Fldapi"); 266 267 ret = odusers_copy_entry(fakeop); 268 free(authdn.bv_val); 269 270out: 271 if(fakeop && !BER_BVISNULL(&fakeop->o_ndn)) { 272 ch_free(fakeop->o_ndn.bv_val); 273 fakeop->o_ndn.bv_val = NULL; 274 } 275 if(fakeop && !BER_BVISNULL(&fakeop->o_req_ndn)) { 276 ch_free(fakeop->o_req_ndn.bv_val); 277 fakeop->o_req_ndn.bv_val = NULL; 278 } 279 if(e) entry_free(e); 280 return ret; 281} 282 283Attribute *odusers_copy_globalpolicy(void) { 284 OperationBuffer opbuf; 285 Connection conn; 286 Operation *fakeop = NULL; 287 Entry *e = NULL; 288 struct berval policydn; 289 Attribute *ret = NULL; 290 291 connection_fake_init2(&conn, &opbuf, ldap_pvt_thread_pool_context(), 0); 292 policydn.bv_val = "cn=access,cn=authdata"; 293 policydn.bv_len = strlen(policydn.bv_val); 294 fakeop = &opbuf.ob_op; 295 fakeop->o_dn = fakeop->o_ndn = policydn; 296 fakeop->o_req_dn = fakeop->o_req_ndn = policydn; 297 fakeop->o_conn->c_listener->sl_url.bv_val = "ldapi://%2Fvar%2Frun%2Fldapi"; 298 fakeop->o_conn->c_listener->sl_url.bv_len = strlen("ldapi://%2Fvar%2Frun%2Fldapi"); 299 300 e = odusers_copy_entry(fakeop); 301 if(!e) goto out; 302 303 Attribute *a; 304 for(a = e->e_attrs; a; a = a->a_next) { 305 if(strncmp(a->a_desc->ad_cname.bv_val, "apple-user-passwordpolicy", a->a_desc->ad_cname.bv_len) == 0) { 306 ret = attr_dup(a); 307 } 308 } 309 310 311out: 312 if(e) entry_free(e); 313 return ret; 314} 315 316static Attribute *odusers_copy_passwordRequiredDate(void) { 317 OperationBuffer opbuf; 318 Connection conn; 319 Operation *fakeop = NULL; 320 Entry *e = NULL; 321 struct berval policydn; 322 Attribute *ret = NULL; 323 324 connection_fake_init2(&conn, &opbuf, ldap_pvt_thread_pool_context(), 0); 325 policydn.bv_val = "cn=access,cn=authdata"; 326 policydn.bv_len = strlen(policydn.bv_val); 327 fakeop = &opbuf.ob_op; 328 fakeop->o_dn = fakeop->o_ndn = policydn; 329 fakeop->o_req_dn = fakeop->o_req_ndn = policydn; 330 fakeop->o_conn->c_listener->sl_url.bv_val = "ldapi://%2Fvar%2Frun%2Fldapi"; 331 fakeop->o_conn->c_listener->sl_url.bv_len = strlen("ldapi://%2Fvar%2Frun%2Fldapi"); 332 333 e = odusers_copy_entry(fakeop); 334 if(!e) goto out; 335 336 Attribute *a; 337 for(a = e->e_attrs; a; a = a->a_next) { 338 if(strncmp(a->a_desc->ad_cname.bv_val, "passwordRequiredDate", a->a_desc->ad_cname.bv_len) == 0) { 339 ret = attr_dup(a); 340 } 341 } 342 343 344out: 345 if(e) entry_free(e); 346 return ret; 347} 348 349CFMutableDictionaryRef CopyPolicyToDict(const char *policyplist, int len) { 350 CFDataRef xmlData = NULL; 351 CFMutableDictionaryRef ret = NULL; 352 CFErrorRef err = NULL; 353 354 xmlData = CFDataCreate(kCFAllocatorDefault, (const unsigned char*)policyplist, len); 355 if(!xmlData) goto out; 356 357 ret = (CFMutableDictionaryRef)CFPropertyListCreateWithData(kCFAllocatorDefault, xmlData, kCFPropertyListMutableContainersAndLeaves, NULL, &err); 358 if(!ret) goto out; 359 360out: 361 if(xmlData) CFRelease(xmlData); 362 return ret; 363} 364 365static void MergePolicyIntValue(CFDictionaryRef global, CFDictionaryRef user, CFMutableDictionaryRef merged, CFStringRef policy, int defaultvalue) { 366 unsigned int tmpbool = 0; 367 CFNumberRef anumber = CFDictionaryGetValue(user, policy); 368 if(anumber) { 369 CFNumberGetValue(anumber, kCFNumberIntType, &tmpbool); 370 if(tmpbool == defaultvalue) { 371 anumber = CFDictionaryGetValue(global, policy); 372 if(anumber) CFNumberGetValue(anumber, kCFNumberIntType, &tmpbool); 373 if(tmpbool) { 374 CFDictionarySetValue(merged, policy, anumber); 375 } 376 } 377 } else { 378 anumber = CFDictionaryGetValue(global, policy); 379 if(anumber) { 380 CFNumberGetValue(anumber, kCFNumberIntType, &tmpbool); 381 CFDictionarySetValue(merged, policy, anumber); 382 } 383 } 384 return; 385} 386 387static CFMutableDictionaryRef CopyEffectivePolicy(CFDictionaryRef global, CFDictionaryRef user) { 388 CFMutableDictionaryRef ret = CFDictionaryCreateMutableCopy(kCFAllocatorDefault, 0, user); 389 390 MergePolicyIntValue(global, user, ret, CFSTR("isDisabled"), 0); 391 MergePolicyIntValue(global, user, ret, CFSTR("isAdminUser"), 0); 392 MergePolicyIntValue(global, user, ret, CFSTR("newPasswordRequired"), 0); 393 MergePolicyIntValue(global, user, ret, CFSTR("canModifyPasswordforSelf"), 1); 394 MergePolicyIntValue(global, user, ret, CFSTR("usingExpirationDate"), 0); 395 MergePolicyIntValue(global, user, ret, CFSTR("usingHardExpirationDate"), 0); 396 MergePolicyIntValue(global, user, ret, CFSTR("notGuessablePattern"), 0); 397 MergePolicyIntValue(global, user, ret, CFSTR("isSessionKeyAgent"), 0); 398 MergePolicyIntValue(global, user, ret, CFSTR("isComputerAccount"), 0); 399 400 MergePolicyIntValue(global, user, ret, CFSTR("requiresMixedCase"), 0); 401 MergePolicyIntValue(global, user, ret, CFSTR("requiresSymbol"), 0); 402 MergePolicyIntValue(global, user, ret, CFSTR("requiresAlpha"), 0); 403 MergePolicyIntValue(global, user, ret, CFSTR("requiresNumeric"), 0); 404 MergePolicyIntValue(global, user, ret, CFSTR("passwordCannotBeName"), 0); 405 MergePolicyIntValue(global, user, ret, CFSTR("maxMinutesUntilChangePassword"), 0); 406 MergePolicyIntValue(global, user, ret, CFSTR("maxMinutesUntilDisabled"), 0); 407 MergePolicyIntValue(global, user, ret, CFSTR("maxMinutesOfNonUse"), 0); 408 MergePolicyIntValue(global, user, ret, CFSTR("maxFailedLoginAttempts"), 0); 409 MergePolicyIntValue(global, user, ret, CFSTR("minChars"), 0); 410 MergePolicyIntValue(global, user, ret, CFSTR("maxChars"), 0); 411 MergePolicyIntValue(global, user, ret, CFSTR("usingHistory"), 0); 412 413 // Merge expiration date if not set in the user policy and is set 414 // in global policy 415 unsigned int tmpint = 0; 416 unsigned int tmpshort = 0; 417 CFNumberRef anumber = CFDictionaryGetValue(user, CFSTR("usingExpirationDate")); 418 if(anumber) CFNumberGetValue(anumber, kCFNumberIntType, &tmpint); 419 if(!tmpint) { 420 anumber = CFDictionaryGetValue(global, CFSTR("usingExpirationDate")); 421 if(anumber) CFNumberGetValue(anumber, kCFNumberIntType, &tmpint); 422 if(tmpint) { 423 CFDateRef expdate = CFDictionaryGetValue(global, CFSTR("expirationDateGMT")); 424 CFDictionarySetValue(ret, CFSTR("usingExpirationDate"), anumber); 425 CFDictionarySetValue(ret, CFSTR("expirationDateGMT"), expdate); 426 } 427 } 428 429 // Merge hard expiration date if not set in the user policy and is set 430 // in global policy. 431 anumber = CFDictionaryGetValue(user, CFSTR("usingHardExpirationDate")); 432 if(anumber) CFNumberGetValue(anumber, kCFNumberIntType, &tmpint); 433 if(!tmpint) { 434 anumber = CFDictionaryGetValue(global, CFSTR("usingHardExpirationDate")); 435 if(anumber) CFNumberGetValue(anumber, kCFNumberIntType, &tmpint); 436 if(tmpint) { 437 CFDateRef expdate = CFDictionaryGetValue(global, CFSTR("hardExpireDateGMT")); 438 CFDictionarySetValue(ret, CFSTR("hardExpireDateGMT"), expdate); 439 CFDictionarySetValue(ret, CFSTR("usingHardExpirationDate"), anumber); 440 } 441 } 442 443 444 return ret; 445} 446 447static int GetDisabledStatus(CFMutableDictionaryRef policy, CFDateRef ctime, CFDateRef lastLogin, CFDateRef passModDate, uint16_t *failedattempts, int disableReason) { 448 int ret = 0; 449 bool setToDisabled = false; 450 short tmpshort = 0; 451 long tmplong = 0; 452 CFAbsoluteTime tmptime = 0; 453 454 // Admins are exempt and should never be disabled due to policy 455 CFNumberRef isadmin = CFDictionaryGetValue(policy, CFSTR("isAdminUser")); 456 if(isadmin) CFNumberGetValue(isadmin, kCFNumberShortType, &tmpshort); 457 if(tmpshort != 0) { 458 int zero = 0; 459 CFNumberRef cfzero = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &zero); 460 CFDictionarySetValue(policy, CFSTR("newPasswordRequired"), cfzero); 461 CFRelease(cfzero); 462 return 0; 463 } 464 465 // Computers are exempt from policy too, since we have no way to cope 466 // with computer accounts being disabled. 467 CFNumberRef iscomputer = CFDictionaryGetValue(policy, CFSTR("isComputerAccount")); 468 if(iscomputer) CFNumberGetValue(iscomputer, kCFNumberShortType, &tmpshort); 469 if(tmpshort != 0) { 470 int zero = 0; 471 CFNumberRef cfzero = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &zero); 472 CFDictionarySetValue(policy, CFSTR("newPasswordRequired"), cfzero); 473 CFRelease(cfzero); 474 return 0; 475 } 476 477 CFDateRef now = CFDateCreate(kCFAllocatorDefault, CFAbsoluteTimeGetCurrent()); 478 479 // If the user is disabled, and the validAfter policy is in use, and 480 // it is after the validAfter date, and the disable reason was 481 // kDisabledInactive (the closest we can come to determining whether 482 // their disabled status was caused by validAfter), then we reenable. 483 CFNumberRef isdisabled = CFDictionaryGetValue(policy, CFSTR("isDisabled")); 484 if(isdisabled) CFNumberGetValue(isdisabled, kCFNumberShortType, &tmpshort); 485 if(tmpshort) { 486 CFDateRef validAfter = CFDictionaryGetValue(policy, CFSTR("validAfter")); 487 ret = kDisabledByAdmin; 488 if(validAfter) { 489 if(CFDateCompare(validAfter, now, NULL) == kCFCompareLessThan) { 490 if(disableReason == kDisabledInactive) { 491 int zero = 0; 492 CFNumberRef cfzero = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &zero); 493 CFDictionarySetValue(policy, CFSTR("isDisabled"), cfzero); 494 CFRelease(cfzero); 495 ret = 0; 496 } 497 } 498 } 499 } 500 501 if(ret) goto out; 502 503 // Check to see if the user's policy has a max failed login set 504 CFNumberRef maxFailedLogins = CFDictionaryGetValue(policy, CFSTR("maxFailedLoginAttempts")); 505 if(maxFailedLogins) CFNumberGetValue(maxFailedLogins, kCFNumberShortType, &tmpshort); 506 if(tmpshort > 0) { 507 if(*failedattempts >= tmpshort) { 508 *failedattempts = 0; 509 ret = kDisabledTooManyFailedLogins; 510 Debug(LDAP_DEBUG_TRACE, "%s: disabling due to failed logins", __PRETTY_FUNCTION__, 0, 0); 511 goto out; 512 } 513 } 514 515 tmpshort = 0; 516 CFNumberRef usingHardExpire = CFDictionaryGetValue(policy, CFSTR("usingHardExpirationDate")); 517 if(usingHardExpire) CFNumberGetValue(usingHardExpire, kCFNumberShortType, &tmpshort); 518 if(tmpshort) { 519 CFTypeRef hardExpire = CFDictionaryGetValue(policy, CFSTR("hardExpireDateGMT")); 520 if(hardExpire) { 521 if(CFGetTypeID(hardExpire) == CFDateGetTypeID()) { 522 if(CFDateCompare(hardExpire, now, NULL) != kCFCompareGreaterThan) { 523 ret = kDisabledExpired; 524 Debug(LDAP_DEBUG_TRACE, "%s: disabling due to hard expire", __PRETTY_FUNCTION__, 0, 0); 525 goto out; 526 } 527 } else if(CFGetTypeID(hardExpire) == CFNumberGetTypeID()) { 528 CFNumberGetValue(hardExpire, kCFNumberLongType, &tmplong); 529 CFDateRef tmpcfdate = CFDateCreate(kCFAllocatorDefault, tmplong - kCFAbsoluteTimeIntervalSince1970); 530 if(CFDateCompare(tmpcfdate, now, NULL) != kCFCompareGreaterThan) { 531 if(tmpcfdate) CFRelease(tmpcfdate); 532 ret = kDisabledExpired; 533 Debug(LDAP_DEBUG_TRACE, "%s: disabling due to hard expire", __PRETTY_FUNCTION__, 0, 0); 534 goto out; 535 } 536 if(tmpcfdate) CFRelease(tmpcfdate); 537 } 538 } 539 } 540 541 tmpshort = tmplong = 0; 542 CFNumberRef maxMinutesUntilDisabled = CFDictionaryGetValue(policy, CFSTR("maxMinutesUntilDisabled")); 543 if(maxMinutesUntilDisabled) CFNumberGetValue(maxMinutesUntilDisabled, kCFNumberLongType, &tmplong); 544 if(tmplong > 0) { 545 tmptime = CFDateGetAbsoluteTime(ctime); 546 if( ((CFAbsoluteTimeGetCurrent() - tmptime)/60) > tmplong ) { 547 Debug(LDAP_DEBUG_TRACE, "%s: disabling due to max minutes expired", __PRETTY_FUNCTION__, 0, 0); 548 ret = kDisabledExpired; 549 goto out; 550 } 551 } 552 553 if(lastLogin) { 554 tmplong = 0; 555 CFNumberRef maxMinutesOfNonUse = CFDictionaryGetValue(policy, CFSTR("maxMinutesOfNonUse")); 556 if(maxMinutesOfNonUse) CFNumberGetValue(maxMinutesOfNonUse, kCFNumberLongType, &tmplong); 557 if(tmplong > 0) { 558 tmptime = CFDateGetAbsoluteTime(lastLogin); 559 if( ((CFAbsoluteTimeGetCurrent() - tmptime)/60) > tmplong ) { 560 Debug(LDAP_DEBUG_TRACE, "%s: disabling due to max minutes of non use", __PRETTY_FUNCTION__, 0, 0); 561 ret = kDisabledInactive; 562 goto out; 563 } 564 } 565 } 566 567 CFDateRef validAfter = CFDictionaryGetValue(policy, CFSTR("validAfter")); 568 if(validAfter) { 569 if(CFDateCompare(validAfter, now, NULL) == kCFCompareGreaterThan) { 570 Debug(LDAP_DEBUG_TRACE, "%s: disabling due to validafter", __PRETTY_FUNCTION__, 0, 0); 571 ret = kDisabledInactive; 572 goto out; 573 } 574 } 575 576 // The change password policies must be the last ones evaluated. 577 // They only get set if the user would otherwise be allowed. 578 tmpshort = tmplong = 0; 579 CFNumberRef maxMinutesUntilChangePassword = CFDictionaryGetValue(policy, CFSTR("maxMinutesUntilChangePassword")); 580 if(maxMinutesUntilChangePassword) CFNumberGetValue(maxMinutesUntilChangePassword, kCFNumberLongType, &tmplong); 581 if(tmplong > 0) { 582 tmptime = CFDateGetAbsoluteTime(passModDate); 583 if( ((CFAbsoluteTimeGetCurrent() - tmptime)/60) > tmplong ) { 584 Debug(LDAP_DEBUG_TRACE, "%s: disabling due password expiration", __func__, 0, 0); 585 if(!ret) ret = kDisabledNewPasswordRequired; 586 } 587 } 588 589 tmpshort = 0; 590 CFNumberRef newPasswordRequired = CFDictionaryGetValue(policy, CFSTR("newPasswordRequired")); 591 if(newPasswordRequired) CFNumberGetValue(newPasswordRequired, kCFNumberShortType, &tmpshort); 592 if(tmpshort > 0) { 593 if(!ret) ret = kDisabledNewPasswordRequired; 594 } 595 596out: 597 if(ret != 0) { 598 int one = 1; 599 CFNumberRef cfone = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &one); 600 CFDictionarySetValue(policy, CFSTR("isDisabled"), cfone); 601 CFRelease(cfone); 602 } 603 604 CFNumberRef cfdisableReason = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &ret); 605 CFDictionarySetValue(policy, CFSTR("effectiveDisableReason"), cfdisableReason); 606 if(cfdisableReason) CFRelease(cfdisableReason); 607 608 if(now) CFRelease(now); 609 return ret; 610} 611 612CFDictionaryRef odusers_copydefaultuserpolicy(void) { 613 CFMutableDictionaryRef ret = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); 614 615 int one = 1; 616 int zero = 0; 617 CFNumberRef cfone = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &one); 618 CFNumberRef cfzero = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &zero); 619 620 CFDictionarySetValue(ret, CFSTR("isDisabled"), cfzero); 621 CFDictionarySetValue(ret, CFSTR("isAdminUser"), cfzero); 622 CFDictionarySetValue(ret, CFSTR("newPasswordRequired"), cfzero); 623 CFDictionarySetValue(ret, CFSTR("usingHistory"), cfzero); 624 CFDictionarySetValue(ret, CFSTR("canModifyPasswordforSelf"), cfone); 625 CFDictionarySetValue(ret, CFSTR("usingExpirationDate"), cfzero); 626 CFDictionarySetValue(ret, CFSTR("usingHardExpirationDate"), cfzero); 627 CFDictionarySetValue(ret, CFSTR("requiresAlpha"), cfzero); 628 CFDictionarySetValue(ret, CFSTR("requiresNumeric"), cfzero); 629 CFDictionarySetValue(ret, CFSTR("maxMinutesUntilChangePassword"), cfzero); 630 CFDictionarySetValue(ret, CFSTR("maxMinutesUntilDisabled"), cfzero); 631 CFDictionarySetValue(ret, CFSTR("maxMinutesOfNonUse"), cfzero); 632 CFDictionarySetValue(ret, CFSTR("maxFailedLoginAttempts"), cfzero); 633 CFDictionarySetValue(ret, CFSTR("minChars"), cfzero); 634 CFDictionarySetValue(ret, CFSTR("maxChars"), cfzero); 635 CFDictionarySetValue(ret, CFSTR("passwordCannotBeName"), cfzero); 636 CFDictionarySetValue(ret, CFSTR("requiresMixedCase"), cfzero); 637 CFDictionarySetValue(ret, CFSTR("requiresSymbol"), cfzero); 638 CFDictionarySetValue(ret, CFSTR("notGuessablePattern"), cfzero); 639 CFDictionarySetValue(ret, CFSTR("isSessionKeyAgent"), cfzero); 640 CFDictionarySetValue(ret, CFSTR("isComputerAccount"), cfzero); 641 return ret; 642} 643 644CFDictionaryRef odusers_copy_defaultglobalpolicy(void) { 645 CFMutableDictionaryRef ret = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); 646 647 int one = 1; 648 int zero = 0; 649 CFNumberRef cfone = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &one); 650 CFNumberRef cfzero = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &zero); 651 CFDictionarySetValue(ret, CFSTR("usingHistory"), cfzero); 652 CFDictionarySetValue(ret, CFSTR("canModifyPasswordforSelf"), cfone); 653 CFDictionarySetValue(ret, CFSTR("usingExpirationDate"), cfzero); 654 CFDictionarySetValue(ret, CFSTR("usingHardExpirationDate"), cfzero); 655 CFDictionarySetValue(ret, CFSTR("requiresAlpha"), cfzero); 656 CFDictionarySetValue(ret, CFSTR("requiresNumeric"), cfzero); 657 CFDictionarySetValue(ret, CFSTR("maxMinutesUntilChangePassword"), cfzero); 658 CFDictionarySetValue(ret, CFSTR("maxMinutesUntilDisabled"), cfzero); 659 CFDictionarySetValue(ret, CFSTR("maxMinutesOfNonUse"), cfzero); 660 CFDictionarySetValue(ret, CFSTR("maxFailedLoginAttempts"), cfzero); 661 CFDictionarySetValue(ret, CFSTR("minChars"), cfzero); 662 CFDictionarySetValue(ret, CFSTR("maxChars"), cfzero); 663 CFDictionarySetValue(ret, CFSTR("passwordCannotBeName"), cfzero); 664 CFDictionarySetValue(ret, CFSTR("requiresMixedCase"), cfzero); 665 CFDictionarySetValue(ret, CFSTR("requiresSymbol"), cfzero); 666 CFDictionarySetValue(ret, CFSTR("newPasswordRequired"), cfzero); 667 CFDictionarySetValue(ret, CFSTR("minutesUntilFailedLoginReset"), cfzero); 668 CFDictionarySetValue(ret, CFSTR("notGuessablePattern"), cfzero); 669 670 return ret; 671} 672 673struct berval *odusers_copy_dict2bv(CFDictionaryRef dict) { 674 CFDataRef xmlData = CFPropertyListCreateData(kCFAllocatorDefault, (CFPropertyListRef)dict, kCFPropertyListXMLFormat_v1_0, 0, NULL); 675 if(!xmlData) { 676 Debug(LDAP_DEBUG_ANY, "%s: Could not convert CFDictionary to CFData", __PRETTY_FUNCTION__, 0, 0); 677 return NULL; 678 } 679 680 struct berval *ret = ber_mem2bv((LDAP_CONST char *)CFDataGetBytePtr(xmlData), CFDataGetLength(xmlData), 1, NULL); 681 682 CFRelease(xmlData); 683 return ret; 684} 685 686bool odusers_ismember(struct berval *userdn, struct berval *groupdn) { 687 OperationBuffer opbuf = {0}; 688 Operation *fakeop = NULL; 689 Connection conn = {0}; 690 SlapReply rs = {REP_RESULT}; 691 u_int32_t result; 692 Entry *usere = NULL; 693 Attribute *guidattr = NULL; 694 bool ret = false; 695 struct berval groupndn; 696 697 dnNormalize(0, NULL, NULL, groupdn, &groupndn, NULL); 698 699 connection_fake_init2(&conn, &opbuf, ldap_pvt_thread_pool_context(), 0); 700 fakeop = &opbuf.ob_op; 701 fakeop->o_dn = *userdn; 702 dnNormalize(0, NULL, NULL, userdn, &fakeop->o_ndn, NULL); 703 fakeop->o_req_dn = *userdn; 704 dnNormalize(0, NULL, NULL, userdn, &fakeop->o_req_ndn, NULL); 705 706 usere = odusers_copy_entry(fakeop); 707 if(!usere) { 708 Debug(LDAP_DEBUG_ANY, "%s: Could not copy user entry for dn %s\n", __func__, userdn->bv_val, 0); 709 goto out; 710 } 711 712 if(idattr_memberships == NULL) { 713 int rc; 714 const char *text = NULL; 715 rc = slap_str2ad( "apple-group-memberguid", &idattr_memberships, &text ); 716 if(rc != LDAP_SUCCESS) { 717 Debug(LDAP_DEBUG_ANY, "%s: Unable to lookup attribute description for apple-group-memberguid\n", __func__, 0, 0); 718 goto out; 719 } 720 } 721 if(idattr_uuid == NULL) { 722 int rc; 723 const char *text = NULL; 724 rc = slap_str2ad( "apple-generateduid", &idattr_uuid, &text ); 725 if(rc != LDAP_SUCCESS) { 726 Debug(LDAP_DEBUG_ANY, "%s: Unable to lookup attribute description for apple-generateduid\n", __func__, 0, 0); 727 goto out; 728 } 729 } 730 731 guidattr = attr_find(usere->e_attrs, idattr_uuid); 732 if(!guidattr) { 733 Debug(LDAP_DEBUG_ANY, "%s: Unable to find apple-generateduid attribute for dn %s\n", __func__, userdn->bv_val, 0); 734 goto out; 735 } 736 idattr_is_member(fakeop, &groupndn, idattr_memberships, guidattr->a_nvals, &result); 737 738 if(result) { 739 ret = true; 740 } 741 742out: 743 if(fakeop && !BER_BVISNULL(&fakeop->o_ndn)) ch_free(fakeop->o_ndn.bv_val); 744 if(fakeop && !BER_BVISNULL(&fakeop->o_req_ndn)) ch_free(fakeop->o_req_ndn.bv_val); 745 if(!BER_BVISNULL(&groupndn)) ch_free(groupndn.bv_val); 746 if(usere) entry_free(usere); 747 return ret; 748} 749 750CFDictionaryRef odusers_copy_effectiveuserpoldict(struct berval *dn) { 751 Entry *e = NULL; 752 Attribute *effective = NULL; 753 Attribute *global_attr = odusers_copy_globalpolicy(); 754 Attribute *passwordRequiredDateAttr = odusers_copy_passwordRequiredDate(); 755 CFDictionaryRef globaldict = NULL; 756 CFMutableDictionaryRef effectivedict = NULL; 757 CFDictionaryRef userdict = NULL; 758 Attribute *policyAttr = NULL; 759 Attribute *failedLogins = NULL; 760 Attribute *creationDate = NULL; 761 Attribute *passModDate = NULL; 762 Attribute *modDate = NULL; 763 Attribute *lastLogin = NULL; 764 Attribute *disableReason = NULL; 765 int disableReasonInt = 0; 766 const char *text = NULL; 767 CFDateRef lastLoginCF = NULL; 768 CFDateRef creationDateCF = NULL; 769 CFDateRef passModDateCF = NULL; 770 CFDateRef passwordRequiredDateCF = NULL; 771 uint16_t loginattempts = 0; 772 int one = 1; 773 int zero = 0; 774 CFNumberRef cfone = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &one); 775 CFNumberRef cfzero = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &zero); 776 777 e = odusers_copy_authdata(dn); 778 if(!e) { 779 Debug(LDAP_DEBUG_ANY, "%s: No entry associated with %s\n", __PRETTY_FUNCTION__, dn->bv_val, 0); 780 goto out; 781 } 782 783 if(!policyAD && slap_str2ad("apple-user-passwordpolicy", &policyAD, &text) != 0) { 784 Debug(LDAP_DEBUG_ANY, "%s: Unable to retrieve description of apple-user-passwordpolicy attribute", __PRETTY_FUNCTION__, 0, 0); 785 goto out; 786 } 787 if(!lastLoginAD && slap_str2ad("lastLoginTime", &lastLoginAD, &text) != 0) { 788 Debug(LDAP_DEBUG_ANY, "%s: Unable to retrieve description of lastLoginTime attribute", __PRETTY_FUNCTION__, 0, 0); 789 goto out; 790 } 791 if(!creationDateAD && slap_str2ad("creationDate", &creationDateAD, &text) != 0) { 792 Debug(LDAP_DEBUG_ANY, "%s: Unable to retrieve description of creationDate attribute", __PRETTY_FUNCTION__, 0, 0); 793 goto out; 794 } 795 if(!passModDateAD && slap_str2ad("passwordModDate", &passModDateAD, &text) != 0) { 796 Debug(LDAP_DEBUG_ANY, "%s: Unable to retrieve description of passwordModDate attribute", __PRETTY_FUNCTION__, 0, 0); 797 goto out; 798 } 799 if(!failedLoginsAD && slap_str2ad("loginFailedAttempts", &failedLoginsAD, &text) != 0) { 800 Debug(LDAP_DEBUG_ANY, "%s: Unable to retrieve description of loginFailedAttempts attribute", __PRETTY_FUNCTION__, 0, 0); 801 goto out; 802 } 803 if(!disableReasonAD && slap_str2ad("disableReason", &disableReasonAD, &text) != 0) { 804 Debug(LDAP_DEBUG_ANY, "%s: Unable to retrieve description of disableReason attribute", __PRETTY_FUNCTION__, 0, 0); 805 goto out; 806 } 807 808 if(!global_attr || global_attr->a_numvals == 0) { 809 globaldict = odusers_copy_defaultglobalpolicy(); 810 } else { 811 globaldict = CopyPolicyToDict(global_attr->a_vals[0].bv_val, global_attr->a_vals[0].bv_len); 812 if(!globaldict) { 813 Debug(LDAP_DEBUG_ANY, "%s: Unable to convert retrieved global policy to CFDictionary", __PRETTY_FUNCTION__, 0, 0); 814 goto out; 815 } 816 } 817 818 policyAttr = attrs_find(e->e_attrs, policyAD); 819 if(policyAttr) { 820 CFDictionaryRef tmpdict = CopyPolicyToDict(policyAttr->a_vals[0].bv_val, policyAttr->a_vals[0].bv_len); 821 CFDictionaryRef defaultdict = odusers_copydefaultuserpolicy(); 822 userdict = CopyEffectivePolicy(defaultdict, tmpdict); 823 CFRelease(defaultdict); 824 CFRelease(tmpdict); 825 } else { 826 userdict = odusers_copydefaultuserpolicy(); 827 } 828 829 if(!userdict) { 830 Debug(LDAP_DEBUG_ANY, "%s: Unable to convert retrieved user policy to CFDictionary", __PRETTY_FUNCTION__, 0, 0); 831 goto out; 832 } 833 834 835 effectivedict = CopyEffectivePolicy(globaldict, userdict); 836 if(!effectivedict) { 837 Debug(LDAP_DEBUG_ANY, "%s: Unable to compute effective policy from global and user dictionaries", __PRETTY_FUNCTION__, 0, 0); 838 goto out; 839 } 840 841 lastLogin = attrs_find(e->e_attrs, lastLoginAD); 842 struct tm tmptm = {0}; 843 time_t tmptime = 0; 844 if(lastLogin && lastLogin->a_numvals && lastLogin->a_nvals[0].bv_len) { 845 strptime(lastLogin->a_nvals[0].bv_val, "%Y%m%d%H%M%SZ", &tmptm); 846 tmptime = timegm(&tmptm); 847 lastLoginCF = CFDateCreate(kCFAllocatorDefault, tmptime - kCFAbsoluteTimeIntervalSince1970); 848 } 849 850 creationDate = attrs_find(e->e_attrs, creationDateAD); 851 tmptime = 0; 852 if(creationDate && creationDate->a_numvals && creationDate->a_nvals[0].bv_len) { 853 strptime(creationDate->a_nvals[0].bv_val, "%Y%m%d%H%M%SZ", &tmptm); 854 tmptime = timegm(&tmptm); 855 } 856 creationDateCF = CFDateCreate(kCFAllocatorDefault, tmptime - kCFAbsoluteTimeIntervalSince1970); 857 858 passModDate = attrs_find(e->e_attrs, passModDateAD); 859 tmptime = 0; 860 if(passModDate && passModDate->a_numvals && passModDate->a_nvals[0].bv_len) { 861 strptime(passModDate->a_nvals[0].bv_val, "%Y%m%d%H%M%SZ", &tmptm); 862 tmptime = timegm(&tmptm); 863 CFNumberRef cftmptime = CFNumberCreate(kCFAllocatorDefault, kCFNumberLongType, &tmptime); 864 CFDictionarySetValue(effectivedict, CFSTR("passwordLastSetTime"), cftmptime); 865 CFRelease(cftmptime); 866 } 867 passModDateCF = CFDateCreate(kCFAllocatorDefault, tmptime - kCFAbsoluteTimeIntervalSince1970); 868 if(!passModDateCF) { 869 passModDateCF = CFDateCreate(kCFAllocatorDefault, 0); 870 } 871 872 if(passwordRequiredDateAttr && passwordRequiredDateAttr->a_numvals && passwordRequiredDateAttr->a_nvals[0].bv_len) { 873 strptime(passwordRequiredDateAttr->a_nvals[0].bv_val, "%Y%m%d%H%M%SZ", &tmptm); 874 tmptime = timegm(&tmptm); 875 } 876 passwordRequiredDateCF = CFDateCreate(kCFAllocatorDefault, tmptime - kCFAbsoluteTimeIntervalSince1970); 877 878 failedLogins = attrs_find(e->e_attrs, failedLoginsAD); 879 if(failedLogins && failedLogins->a_numvals && failedLogins->a_nvals[0].bv_len) { 880 long long tmpll; 881 errno = 0; 882 tmpll = strtoll(failedLogins->a_nvals[0].bv_val, NULL, 10); 883 if( ((tmpll == LLONG_MAX) || (tmpll == LLONG_MIN)) && (errno == ERANGE) ) { 884 tmpll = 0; 885 } 886 if( (tmpll > USHRT_MAX) || (tmpll < 0) ) { 887 tmpll = 0; 888 } 889 loginattempts = tmpll; 890 } 891 892 893 // newPasswordRequired is a bit more complicated. 894 // In PWS, when setting the global newPasswordRequired policy, PWS would 895 // iterate over all user records setting the policy on the individual record, 896 // then update the individual record as the password is changed. 897 // That gets pretty inefficient, especially with ldap. 898 // So instead we store the passwordRequiredDate when global newPasswordRequired 899 // is set. If the policy is set on the user's record, that always wins over global. 900 // If not set on the user's record, but is set in the global policy, compare 901 // the global passwordRequiredDate to the user's passwordModDate to see if they 902 // changed it more recently than the policy was set. 903 short tmpshort = 0; 904 CFDictionarySetValue(effectivedict, CFSTR("newPasswordRequired"), cfzero); 905 CFNumberRef newPasswordRequired = CFDictionaryGetValue(userdict, CFSTR("newPasswordRequired")); 906 if(newPasswordRequired) CFNumberGetValue(newPasswordRequired, kCFNumberShortType, &tmpshort); 907 if(tmpshort) { 908 CFDictionarySetValue(effectivedict, CFSTR("newPasswordRequired"), newPasswordRequired); 909 } else { 910 tmpshort = 0; 911 newPasswordRequired = CFDictionaryGetValue(globaldict, CFSTR("newPasswordRequired")); 912 if(newPasswordRequired) CFNumberGetValue(newPasswordRequired, kCFNumberShortType, &tmpshort); 913 if(tmpshort) { 914 if(passwordRequiredDateCF) { 915 if((CFDateCompare(passwordRequiredDateCF, passModDateCF, NULL) == kCFCompareGreaterThan) || (CFDateCompare(passwordRequiredDateCF, passModDateCF, NULL) == kCFCompareEqualTo)) { 916 CFDictionarySetValue(effectivedict, CFSTR("newPasswordRequired"), newPasswordRequired); 917 } 918 } else { 919 Debug(LDAP_DEBUG_ANY, "%s: inconsistency detected, global newPasswordRequired policy set, but no passwordRequiredDate found in cn=access,cn=authdata\n", __func__, 0, 0); 920 } 921 } 922 } 923 924 long tmplong = 0; 925 CFNumberRef minutesUntilFailedLoginReset = CFDictionaryGetValue(effectivedict, CFSTR("minutesUntilFailedLoginReset")); 926 if(minutesUntilFailedLoginReset) CFNumberGetValue(minutesUntilFailedLoginReset, kCFNumberLongType, &tmplong); 927 if(tmplong > 0) { 928 modDate = attrs_find(e->e_attrs, slap_schema.si_ad_modifyTimestamp); 929 tmptime = 0; 930 if(modDate && modDate->a_numvals && modDate->a_nvals[0].bv_len) { 931 strptime(modDate->a_nvals[0].bv_val, "%Y%m%d%H%M%SZ", &tmptm); 932 tmptime = timegm(&tmptm); 933 } 934 935 if( time(NULL) >= (tmptime + (tmplong*60)) ) { 936 loginattempts = 0; 937 if(odusers_reset_failedlogin(dn) != 0) { 938 Debug(LDAP_DEBUG_ANY, "%s: error resetting failed login count for %s\n", __func__, dn->bv_val, 0); 939 } 940 } 941 } 942 943 disableReason = attrs_find(e->e_attrs, disableReasonAD); 944 if(disableReason && disableReason->a_numvals && disableReason->a_nvals[0].bv_len) { 945 long long tmpll; 946 errno = 0; 947 tmpll = strtoll(disableReason->a_nvals[0].bv_val, NULL, 10); 948 if( ((tmpll == LLONG_MAX) || (tmpll == LLONG_MIN)) && (errno == ERANGE) ) { 949 tmpll = 0; 950 } 951 if( (tmpll > USHRT_MAX) || (tmpll < 0) ) { 952 tmpll = 0; 953 } 954 disableReasonInt = tmpll; 955 } 956 957 bool isAdmin, isSessionKeyAgent; 958 char *suffix = odusers_copy_suffix(); 959 char *groupstr = NULL; 960 char *sessiongroupstr = NULL; 961 struct berval *groupdn = NULL; 962 struct berval *sessiongroupdn = NULL; 963 int grouplen = asprintf(&groupstr, "cn=admin,cn=groups,%s", suffix); 964 groupdn = ber_str2bv(groupstr, grouplen, 1, NULL); 965 isAdmin = odusers_ismember(dn, groupdn); 966 967 grouplen = asprintf(&sessiongroupstr, "cn=com.apple.access_sessionkey,cn=groups,%s", suffix); 968 sessiongroupdn = ber_str2bv(sessiongroupstr, grouplen, 1, NULL); 969 isSessionKeyAgent = odusers_ismember(dn, sessiongroupdn); 970 free(groupstr); 971 free(sessiongroupstr); 972 ber_bvfree(groupdn); 973 ber_bvfree(sessiongroupdn); 974 ch_free(suffix); 975 976 CFNumberRef cfisadmin = CFDictionaryGetValue(effectivedict, CFSTR("isAdminUser")); 977 if(cfisadmin) CFNumberGetValue(cfisadmin, kCFNumberShortType, &tmpshort); 978 if(tmpshort == 0) { 979 CFDictionarySetValue(effectivedict, CFSTR("isAdminUser"), isAdmin ? cfone : cfzero); 980 } 981 if(strnstr(dn->bv_val, "cn=computer", dn->bv_len) != NULL) { 982 CFDictionarySetValue(effectivedict, CFSTR("isComputerAccount"), cfone); 983 } 984 CFNumberRef cfissessionkeyagent = CFDictionaryGetValue(effectivedict, CFSTR("isSessionKeyAgent")); 985 if(cfissessionkeyagent) CFNumberGetValue(cfissessionkeyagent, kCFNumberShortType, &tmpshort); 986 if(tmpshort == 0) { 987 CFDictionarySetValue(effectivedict, CFSTR("isSessionKeyAgent"), isSessionKeyAgent ? cfone : cfzero); 988 } 989 990 GetDisabledStatus(effectivedict, creationDateCF, lastLoginCF, passModDateCF, &loginattempts, disableReasonInt); 991 if(cfzero) CFRelease(cfzero); 992 if(cfone) CFRelease(cfone); 993 if(lastLoginCF) CFRelease(lastLoginCF); 994 if(creationDateCF) CFRelease(creationDateCF); 995 if(passModDateCF) CFRelease(passModDateCF); 996 if(passwordRequiredDateCF) CFRelease(passwordRequiredDateCF); 997 if(userdict) CFRelease(userdict); 998 if(globaldict) CFRelease(globaldict); 999 if (global_attr) attr_free(global_attr); 1000 if (passwordRequiredDateAttr) attr_free(passwordRequiredDateAttr); 1001 if (e) entry_free(e); 1002 return effectivedict; 1003 1004out: 1005 if(cfzero) CFRelease(cfzero); 1006 if(cfone) CFRelease(cfone); 1007 if (global_attr) attr_free(global_attr); 1008 if (e) entry_free(e); 1009 if (globaldict) CFRelease(globaldict); 1010 return NULL; 1011} 1012 1013int odusers_isdisabled(CFDictionaryRef policy) { 1014 short tmpshort = 0; 1015 CFNumberRef isdisabled = CFDictionaryGetValue(policy, CFSTR("isDisabled")); 1016 if(isdisabled) CFNumberGetValue(isdisabled, kCFNumberShortType, &tmpshort); 1017 if(tmpshort) { 1018 CFNumberRef disableReason = CFDictionaryGetValue(policy, CFSTR("effectiveDisableReason")); 1019 if(disableReason) { 1020 int tmpint; 1021 CFNumberGetValue(disableReason, kCFNumberIntType, &tmpint); 1022 if(tmpint) return tmpint; 1023 } 1024 return true; 1025 } 1026 return false; 1027} 1028 1029bool odusers_isadmin(CFDictionaryRef policy) { 1030 short tmpshort = 0; 1031 CFNumberRef isdisabled = CFDictionaryGetValue(policy, CFSTR("isAdminUser")); 1032 if(isdisabled) CFNumberGetValue(isdisabled, kCFNumberShortType, &tmpshort); 1033 if(tmpshort) { 1034 return true; 1035 } 1036 return false; 1037} 1038 1039int odusers_reset_failedlogin(struct berval *dn) { 1040 int ret = -1; 1041 OperationBuffer opbuf = {0}; 1042 Operation *fakeop = NULL; 1043 Connection conn = {0}; 1044 SlapReply rs = {REP_RESULT}; 1045 1046 Modifications *mod = NULL; 1047 1048 Entry *e = NULL; 1049 Attribute *failedLogins = NULL; 1050 const char *text = NULL; 1051 short optype = LDAP_MOD_ADD; 1052 1053 if(!failedLoginsAD && slap_str2ad("loginFailedAttempts", &failedLoginsAD, &text) != 0) { 1054 Debug(LDAP_DEBUG_TRACE, "%s: Unable to retrieve description of loginFailedAttempts attribute", __PRETTY_FUNCTION__, 0, 0); 1055 goto out; 1056 } 1057 1058 e = odusers_copy_authdata(dn); 1059 if(!e) { 1060 Debug(LDAP_DEBUG_ANY, "%s: No entry associated with %s\n", __func__, dn->bv_val, 0); 1061 goto out; 1062 } 1063 1064 mod = (Modifications *) ch_malloc(sizeof(Modifications)); 1065 1066 mod->sml_op = optype; 1067 mod->sml_flags = 0; 1068 mod->sml_type = failedLoginsAD->ad_cname; 1069 mod->sml_values = (struct berval*) ch_malloc(2 * sizeof(struct berval)); 1070 mod->sml_values[0].bv_val = ch_strdup("0"); 1071 mod->sml_values[0].bv_len = 1; 1072 mod->sml_values[1].bv_val = NULL; 1073 mod->sml_values[1].bv_len = 0; 1074 mod->sml_numvals = 1; 1075 mod->sml_nvalues = NULL; 1076 1077 mod->sml_desc = failedLoginsAD; 1078 mod->sml_next = NULL; 1079 1080 connection_fake_init2(&conn, &opbuf, ldap_pvt_thread_pool_context(), 0); 1081 fakeop = &opbuf.ob_op; 1082 fakeop->o_dn = *dn; 1083 dnNormalize(0, NULL, NULL, dn, &fakeop->o_ndn, NULL); 1084 fakeop->o_req_dn = e->e_name; 1085 fakeop->o_req_ndn = e->e_nname; 1086 fakeop->orm_modlist = mod; 1087 fakeop->o_tag = LDAP_REQ_MODIFY; 1088 fakeop->o_conn->c_listener->sl_url.bv_val = "ldapi://%2Fvar%2Frun%2Fldapi"; 1089 fakeop->o_conn->c_listener->sl_url.bv_len = strlen("ldapi://%2Fvar%2Frun%2Fldapi"); 1090 fakeop->o_bd = frontendDB; 1091 1092 fakeop->o_bd->be_modify(fakeop, &rs); 1093 if(rs.sr_err != LDAP_SUCCESS) { 1094 //Debug(LDAP_DEBUG_ANY, "Unable to modify failedlogins for user %s: %d %s\n", fakeop->o_req_ndn.bv_val, rs.sr_err, rs.sr_text); 1095 goto out; 1096 } 1097 1098 ret = 0; 1099 1100out: 1101 if(fakeop && !BER_BVISNULL(&fakeop->o_ndn)) ch_free(fakeop->o_ndn.bv_val); 1102 if(e) entry_free(e); 1103 if(mod) slap_mods_free(mod, 1); 1104 return ret; 1105} 1106 1107int odusers_increment_failedlogin(struct berval *dn) { 1108 int ret = -1; 1109 OperationBuffer opbuf = {0}; 1110 Operation *fakeop = NULL; 1111 Connection conn = {0}; 1112 SlapReply rs = {REP_RESULT}; 1113 1114 Modifications *mod = NULL; 1115 char *attemptsstr = NULL; 1116 1117 Entry *e = NULL; 1118 Attribute *failedLogins = NULL; 1119 const char *text = NULL; 1120 uint16_t loginattempts = 0; 1121 short optype = LDAP_MOD_ADD; 1122 1123 if(!failedLoginsAD && slap_str2ad("loginFailedAttempts", &failedLoginsAD, &text) != 0) { 1124 Debug(LDAP_DEBUG_TRACE, "%s: Unable to retrieve description of loginFailedAttempts attribute", __PRETTY_FUNCTION__, 0, 0); 1125 goto out; 1126 } 1127 1128 e = odusers_copy_authdata(dn); 1129 if(!e) { 1130 Debug(LDAP_DEBUG_TRACE, "%s: No entry associated with %s\n", __PRETTY_FUNCTION__, dn->bv_val, 0); 1131 goto out; 1132 } 1133 1134 failedLogins = attrs_find(e->e_attrs, failedLoginsAD); 1135 if(failedLogins && failedLogins->a_numvals && failedLogins->a_nvals[0].bv_len) { 1136 long long tmpll; 1137 errno = 0; 1138 tmpll = strtoll(failedLogins->a_nvals[0].bv_val, NULL, 10); 1139 if( ((tmpll == LLONG_MAX) || (tmpll == LLONG_MIN)) && (errno == ERANGE) ) { 1140 tmpll = 0; 1141 } 1142 if( (tmpll > USHRT_MAX) || (tmpll < 0) ) { 1143 tmpll = 0; 1144 } 1145 loginattempts = tmpll; 1146 optype = LDAP_MOD_REPLACE; 1147 } 1148 1149 loginattempts++; 1150 asprintf(&attemptsstr, "%hu", loginattempts); 1151 1152 mod = (Modifications *) ch_malloc(sizeof(Modifications)); 1153 1154 mod->sml_op = optype; 1155 mod->sml_flags = 0; 1156 mod->sml_type = failedLoginsAD->ad_cname; 1157 mod->sml_values = (struct berval*) ch_malloc(2 * sizeof(struct berval)); 1158 mod->sml_values[0].bv_val = attemptsstr; 1159 mod->sml_values[0].bv_len = strlen(attemptsstr); 1160 mod->sml_values[1].bv_val = NULL; 1161 mod->sml_values[1].bv_len = 0; 1162 mod->sml_numvals = 1; 1163 mod->sml_nvalues = NULL; 1164 1165 mod->sml_desc = failedLoginsAD; 1166 mod->sml_next = NULL; 1167 1168 connection_fake_init2(&conn, &opbuf, ldap_pvt_thread_pool_context(), 0); 1169 fakeop = &opbuf.ob_op; 1170 fakeop->o_dn = *dn; 1171 fakeop->o_ndn = *dn; 1172 fakeop->o_req_dn = e->e_name; 1173 fakeop->o_req_ndn = e->e_name; 1174 fakeop->orm_modlist = mod; 1175 fakeop->o_tag = LDAP_REQ_MODIFY; 1176 fakeop->o_conn->c_listener->sl_url.bv_val = "ldapi://%2Fvar%2Frun%2Fldapi"; 1177 fakeop->o_conn->c_listener->sl_url.bv_len = strlen("ldapi://%2Fvar%2Frun%2Fldapi"); 1178 fakeop->o_bd = frontendDB; 1179 1180 fakeop->o_bd->be_modify(fakeop, &rs); 1181 if(rs.sr_err != LDAP_SUCCESS) { 1182 Debug(LDAP_DEBUG_ANY, "Unable to modify failedlogins for user %s: %d %s\n", fakeop->o_req_ndn.bv_val, rs.sr_err, rs.sr_text); 1183 goto out; 1184 } 1185 1186 ret = 0; 1187 1188out: 1189 if(e) entry_free(e); 1190 if(mod) slap_mods_free(mod, 1); 1191 return ret; 1192} 1193 1194char *odusers_copy_saslrealm(void) { 1195 OperationBuffer opbuf = {0}; 1196 Connection conn = {0}; 1197 Entry *e = NULL; 1198 Attribute *a = NULL; 1199 char *ret = NULL; 1200 const char *text = NULL; 1201 1202 connection_fake_init2(&conn, &opbuf, ldap_pvt_thread_pool_context(), 0); 1203 1204 if(root_dse_info(&conn, &e, &text) != LDAP_SUCCESS) { 1205 Debug(LDAP_DEBUG_ANY, "%s: root_dse_info failed\n", __func__, 0, 0); 1206 goto out; 1207 } 1208 if(!e) { 1209 Debug(LDAP_DEBUG_ANY, "%s: No entry found\n", __func__, 0, 0); 1210 goto out; 1211 } 1212 1213 a = attr_find(e->e_attrs, slap_schema.si_ad_saslRealm); 1214 if(!a) { 1215 Debug(LDAP_DEBUG_ANY, "%s: Could not locate sasl realm\n", __func__, 0, 0); 1216 goto out; 1217 } 1218 1219 if(a->a_numvals == 0) { 1220 Debug(LDAP_DEBUG_ANY, "%s: No values returned\n", __func__, 0, 0); 1221 goto out; 1222 } 1223 1224 ret = ch_calloc(1, a->a_nvals[0].bv_len + 1); 1225 if(!ret) goto out; 1226 memcpy(ret, a->a_nvals[0].bv_val, a->a_nvals[0].bv_len); 1227 1228out: 1229 if(e) entry_free(e); 1230 return ret; 1231} 1232 1233char *odusers_copy_suffix(void) { 1234 OperationBuffer opbuf = {0}; 1235 Connection conn = {0}; 1236 Entry *e = NULL; 1237 Attribute *a = NULL; 1238 char *ret = NULL; 1239 const char *text = NULL; 1240 1241 connection_fake_init2(&conn, &opbuf, ldap_pvt_thread_pool_context(), 0); 1242 1243 if(root_dse_info(&conn, &e, &text) != LDAP_SUCCESS) { 1244 Debug(LDAP_DEBUG_ANY, "%s: root_dse_info failed\n", __func__, 0, 0); 1245 goto out; 1246 } 1247 if(!e) { 1248 Debug(LDAP_DEBUG_ANY, "%s: No entry found\n", __func__, 0, 0); 1249 goto out; 1250 } 1251 1252 a = attr_find(e->e_attrs, slap_schema.si_ad_namingContexts); 1253 if(!a) { 1254 Debug(LDAP_DEBUG_ANY, "%s: Could not locate naming contexts\n", __func__, 0, 0); 1255 goto out; 1256 } 1257 1258 if(a->a_numvals == 0) { 1259 Debug(LDAP_DEBUG_ANY, "%s: No values returned\n", __func__, 0, 0); 1260 goto out; 1261 } 1262 1263 ret = ch_calloc(1, a->a_nvals[0].bv_len +1); 1264 if(!ret) goto out; 1265 memcpy(ret, a->a_nvals[0].bv_val, a->a_nvals[0].bv_len); 1266 1267out: 1268 if(e) entry_free(e); 1269 return ret; 1270} 1271 1272/* Returns a newly allocated copy of the kerberos realm. 1273 * Caller is responsible for free()'ing the returned result. 1274 */ 1275char *odusers_copy_krbrealm(Operation *op) { 1276 OperationBuffer opbuf = {0}; 1277 Operation *fakeop = NULL; 1278 Connection conn = {0}; 1279 Entry *e = NULL; 1280 Attribute *a = NULL; 1281 const char *text = NULL; 1282 static char *savedrealm = NULL; 1283 char *ret = NULL; 1284 char *suffix = NULL; 1285 struct berval *suffixdn = NULL; 1286 struct berval *configdn = NULL; 1287 1288 if(savedrealm) { 1289 return ch_strdup(savedrealm); 1290 } 1291 1292 suffix = strnstr(op->o_req_ndn.bv_val, "dc=", op->o_req_ndn.bv_len); 1293 if(!suffix) { 1294 Debug(LDAP_DEBUG_ANY, "%s: Could not locate suffix of request %s", __func__, op->o_req_ndn.bv_val, 0); 1295 return NULL; 1296 } 1297 1298 suffixdn = ber_str2bv(suffix, strlen(suffix), 1, NULL); 1299 configdn = ber_str2bv("cn=kerberoskdc,cn=config", strlen("cn=kerberoskdc,cn=config"), 1, NULL); 1300 1301 connection_fake_init2(&conn, &opbuf, ldap_pvt_thread_pool_context(), 0); 1302 fakeop = &opbuf.ob_op; 1303// fakeop->o_req_dn.bv_len = asprintf(&fakeop->o_req_dn.bv_val, "cn=kerberoskdc,cn=config,%s", suffix); 1304// fakeop->o_req_dn.bv_len = strlen(fakeop->o_req_dn.bv_val); 1305 build_new_dn( &fakeop->o_req_dn, suffixdn, configdn, NULL ); 1306 fakeop->o_dn = fakeop->o_ndn = fakeop->o_req_ndn = fakeop->o_req_dn; 1307 fakeop->o_conn->c_listener->sl_url.bv_val = "ldapi://%2Fvar%2Frun%2Fldapi"; 1308 fakeop->o_conn->c_listener->sl_url.bv_len = strlen("ldapi://%2Fvar%2Frun%2Fldapi"); 1309 fakeop->ors_attrs = NULL; 1310 1311 e = odusers_copy_entry(fakeop); 1312 if(!e) { 1313 Debug(LDAP_DEBUG_ANY, "%s: No entry associated with KerberosKDC %s\n", __func__, fakeop->o_req_dn.bv_val, 0); 1314 goto out; 1315 } 1316 1317 if(!realnameAD && slap_str2ad("apple-config-realname", &realnameAD, &text) != 0) { 1318 Debug(LDAP_DEBUG_ANY, "%s: slap_str2ad failed for apple-config-realname", __func__, 0, 0); 1319 goto out; 1320 } 1321 1322 a = attr_find(e->e_attrs, realnameAD); 1323 if(!a) { 1324 Debug(LDAP_DEBUG_ANY, "%s: Could not locate apple-config-realname attribute", __func__, 0, 0); 1325 goto out; 1326 } 1327 1328 ret = ch_calloc(1, a->a_vals[0].bv_len + 1); 1329 if(!ret) goto out; 1330 memcpy(ret, a->a_vals[0].bv_val, a->a_vals[0].bv_len); 1331 savedrealm = ch_strdup(ret); 1332 1333out: 1334 if(e) entry_free(e); 1335 if(suffixdn && !BER_BVISNULL(suffixdn)) ber_bvfree(suffixdn); 1336 if(configdn && !BER_BVISNULL(configdn)) ber_bvfree(configdn); 1337 if(fakeop && !BER_BVISNULL(&fakeop->o_req_ndn)) { 1338 ch_free(fakeop->o_req_ndn.bv_val); 1339 fakeop->o_req_ndn.bv_val = NULL; 1340 } 1341 return ret; 1342} 1343 1344char *odusers_copy_recname(Operation *op) { 1345 Attribute *attriter = NULL; 1346 char *recname = NULL; 1347 char *start = NULL; 1348 char *end = NULL; 1349 bool isuser = false; 1350 1351 start = strnstr(op->o_req_dn.bv_val, "=", op->o_req_dn.bv_len); 1352 if(!start) return NULL; 1353 start++; 1354 end = strnstr(op->o_req_dn.bv_val, ",", op->o_req_dn.bv_len); 1355 if(!end) return NULL; 1356 recname = ch_calloc(1, (end - start) + 1); 1357 if(!recname) return NULL; 1358 memcpy(recname, start, (end - start)); 1359 1360 return recname; 1361} 1362 1363/* Returns a newly allocated copy of the primary master's IP address. 1364 * Caller is responsible for free()'ing the returned result. 1365 */ 1366char *odusers_copy_primarymasterip(Operation *op) { 1367 OperationBuffer opbuf = {0}; 1368 Operation *fakeop = NULL; 1369 Connection conn = {0}; 1370 Entry *e = NULL; 1371 Attribute *a = NULL; 1372 const char *text = NULL; 1373 static char *savedprimarymasterip = NULL; 1374 char *ret = NULL; 1375 char *suffix = NULL; 1376 1377 if(savedprimarymasterip) { 1378 return ch_strdup(savedprimarymasterip); 1379 } 1380 1381 suffix = strnstr(op->o_req_ndn.bv_val, "dc=", op->o_req_ndn.bv_len); 1382 if(!suffix) { 1383 Debug(LDAP_DEBUG_ANY, "%s: Could not locate suffix of request %s", __func__, op->o_req_ndn.bv_val, 0); 1384 return NULL; 1385 } 1386 1387 connection_fake_init2(&conn, &opbuf, ldap_pvt_thread_pool_context(), 0); 1388 fakeop = &opbuf.ob_op; 1389 fakeop->o_req_dn.bv_len = asprintf(&fakeop->o_req_dn.bv_val, "cn=passwordserver,cn=config,%s", suffix); 1390 fakeop->o_req_dn.bv_len = strlen(fakeop->o_req_dn.bv_val); 1391 fakeop->o_dn = fakeop->o_ndn = fakeop->o_req_ndn = fakeop->o_req_dn; 1392 fakeop->o_conn->c_listener->sl_url.bv_val = "ldapi://%2Fvar%2Frun%2Fldapi"; 1393 fakeop->o_conn->c_listener->sl_url.bv_len = strlen("ldapi://%2Fvar%2Frun%2Fldapi"); 1394 fakeop->ors_attrs = NULL; 1395 1396 e = odusers_copy_entry(fakeop); 1397 if(!e) { 1398 Debug(LDAP_DEBUG_ANY, "%s: No entry associated with passwordserver %s\n", __func__, fakeop->o_req_dn.bv_val, 0); 1399 goto out; 1400 } 1401 1402 if(!pwslocAD && slap_str2ad("apple-password-server-location", &pwslocAD, &text) != 0) { 1403 Debug(LDAP_DEBUG_ANY, "%s: slap_str2ad failed for apple-password-server-location", __func__, 0, 0); 1404 goto out; 1405 } 1406 1407 a = attr_find(e->e_attrs, pwslocAD); 1408 if(!a) { 1409 Debug(LDAP_DEBUG_ANY, "%s: Could not locate apple-password-server-location attribute", __func__, 0, 0); 1410 goto out; 1411 } 1412 1413 ret = ch_calloc(1, a->a_vals[0].bv_len + 1); 1414 if(!ret) goto out; 1415 memcpy(ret, a->a_vals[0].bv_val, a->a_vals[0].bv_len); 1416 savedprimarymasterip = ch_strdup(ret); 1417 1418out: 1419 if(e) entry_free(e); 1420 if(fakeop && fakeop->o_req_dn.bv_val) free(fakeop->o_req_dn.bv_val); 1421 return ret; 1422} 1423 1424static void ConvertHexToBinary( const char *inHexStr, unsigned char *outData, unsigned long *outLen ) 1425{ 1426 unsigned char *tptr = outData; 1427 unsigned char val; 1428 1429 while ( *inHexStr && *(inHexStr+1) ) 1430 { 1431 if ( *inHexStr >= 'a' ) 1432 val = (*inHexStr - 'a' + 0x0A) << 4; 1433 else 1434 val = (*inHexStr - '0') << 4; 1435 1436 inHexStr++; 1437 1438 if ( *inHexStr >= 'a' ) 1439 val += (*inHexStr - 'a' + 0x0A); 1440 else 1441 val += (*inHexStr - '0'); 1442 1443 inHexStr++; 1444 *tptr++ = val; 1445 } 1446 1447 *outLen = (tptr - outData); 1448} 1449 1450static bool ConvertBinaryToHex( const unsigned char *inData, long len, char *outHexStr ) 1451{ 1452 bool result = true; 1453 char *tptr = outHexStr; 1454 char base16table[16] = { '0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F' }; 1455 1456 if ( inData == nil || outHexStr == nil ) 1457 return false; 1458 1459 for ( int idx = 0; idx < len; idx++ ) 1460 { 1461 *tptr++ = base16table[(inData[idx] >> 4) & 0x0F]; 1462 *tptr++ = base16table[(inData[idx] & 0x0F)]; 1463 } 1464 *tptr = '\0'; 1465 1466 return result; 1467} 1468 1469int odusers_clear_authattr(char *authguid, char *attribute) { 1470 int ret = -1; 1471 OperationBuffer opbuf = {0}; 1472 Operation *fakeop = NULL; 1473 Connection conn = {0}; 1474 char *authdatadn = NULL; 1475 AttributeDescription *genericAD = NULL; 1476 Modifications *mhead = NULL; 1477 Modifications *m = NULL; 1478 SlapReply rs = {0}; 1479 1480 connection_fake_init2(&conn, &opbuf, ldap_pvt_thread_pool_context(), 0); 1481 fakeop = &opbuf.ob_op; 1482 fakeop->o_conn->c_listener->sl_url.bv_val = "ldapi://%2Fvar%2Frun%2Fldapi"; 1483 fakeop->o_conn->c_listener->sl_url.bv_len = strlen("ldapi://%2Fvar%2Frun%2Fldapi"); 1484 fakeop->o_req_dn.bv_len = asprintf(&authdatadn, "authGUID=%s,cn=users,cn=authdata", authguid); 1485 fakeop->o_req_dn.bv_val = authdatadn; 1486 fakeop->o_req_ndn = fakeop->o_req_dn; 1487 fakeop->o_dn = fakeop->o_ndn = fakeop->o_req_ndn = fakeop->o_req_dn; 1488 fakeop->o_tag = LDAP_REQ_MODIFY; 1489 /* Internal ops, never replicate these */ 1490 fakeop->orm_no_opattrs = 1; 1491 fakeop->o_dont_replicate = 1; 1492 1493 const char *text = NULL; 1494 if(slap_str2ad(attribute, &genericAD, &text) != 0) { 1495 Debug(LDAP_DEBUG_ANY, "%s: Could not find attribute description for %s\n", __func__, attribute, 0); 1496 goto out; 1497 } 1498 m = ch_calloc(1, sizeof(Modifications)); 1499 m->sml_op = LDAP_MOD_DELETE; 1500 m->sml_flags = SLAP_MOD_INTERNAL; 1501 m->sml_type = genericAD->ad_cname; 1502 m->sml_desc = genericAD; 1503 m->sml_numvals = 0; 1504 m->sml_values = NULL; 1505 m->sml_nvalues = NULL; 1506 m->sml_next = mhead; 1507 mhead = m; 1508 1509 fakeop->orm_modlist = mhead; 1510 fakeop->o_bd = select_backend(&fakeop->o_req_ndn, 1); 1511 if(!fakeop->o_bd) { 1512 Debug(LDAP_DEBUG_ANY, "%s: Could not locate backend for %s\n", __func__, authdatadn, 0); 1513 goto out; 1514 } 1515 1516 fakeop->o_callback = NULL; 1517 fakeop->o_bd->be_modify(fakeop, &rs); 1518 slap_mods_free(mhead, 1); 1519 1520 // Ignore the return. It will fail if any of the above attributes 1521 // don't exist, which is entirely likely. We just want them all 1522 // gone if they exist. 1523 1524 ret = 0; 1525out: 1526 // if (genericAD) ad_destroy(genericAD); 1527 if(authdatadn) free(authdatadn); 1528 return ret; 1529} 1530 1531int odusers_clear_authhashes(char *authguid) { 1532 /* Each hash deletion needs to be its own modify operation, 1533 * otherwise, if the removal of one fails (such as it doesn't 1534 * exist), all subsequent mods in the same operation will 1535 * not be processed. 1536 */ 1537 odusers_clear_authattr(authguid, "cmusaslsecretCRAM-MD5"); 1538 odusers_clear_authattr(authguid, "cmusaslsecretDIGEST-MD5"); 1539 odusers_clear_authattr(authguid, "cmusaslsecretDIGEST-UMD5"); 1540 odusers_clear_authattr(authguid, "cmusaslsecretPPS"); 1541 odusers_clear_authattr(authguid, "cmusaslsecretSMBNT"); 1542 odusers_clear_authattr(authguid, "password"); 1543 odusers_clear_authattr(authguid, "draft-krbKeySet"); 1544 return 0; 1545} 1546 1547int odusers_set_password(struct berval *dn, char *password, int isChangingOwnPassword) { 1548 int ret = -1; 1549 OperationBuffer opbuf = {0}; 1550 Operation *fakeop = NULL; 1551 Connection conn = {0}; 1552 Entry *usere = NULL; 1553 Entry *authe = NULL; 1554 char authguid[37] = {0}; 1555 char *recname = NULL; 1556 char *kerbrealm = NULL; 1557 char *saslrealm = NULL; 1558 char *authdatadn = NULL; 1559 char *suffix = NULL; 1560 CFErrorRef error = NULL; 1561 CFArrayRef keyset = NULL; 1562 CFStringRef principal = NULL; 1563 CFStringRef cfpassword = NULL; 1564 CFTypeRef encVals[] = {CFSTR("aes256-cts-hmac-sha1-96"), CFSTR("aes128-cts-hmac-sha1-96"), CFSTR("des3-cbc-sha1") }; 1565 CFArrayRef enctypes = NULL; 1566 SlapReply rs = {0}; 1567 CFArrayRef enabledmechArray = NULL; 1568 CFIndex i; 1569 Modifications *mhead = NULL; 1570 Modifications *m = NULL; 1571 const char *text = NULL; 1572 unsigned long tmplong; 1573 char needPlaintext = 0; 1574 1575 connection_fake_init2(&conn, &opbuf, ldap_pvt_thread_pool_context(), 0); 1576 fakeop = &opbuf.ob_op; 1577 fakeop->o_dn = fakeop->o_req_dn = *dn; 1578 dnNormalize(0, NULL, NULL, dn, &fakeop->o_ndn, NULL); 1579 fakeop->o_req_ndn = fakeop->o_ndn; 1580 fakeop->o_conn->c_listener->sl_url.bv_val = "ldapi://%2Fvar%2Frun%2Fldapi"; 1581 fakeop->o_conn->c_listener->sl_url.bv_len = strlen("ldapi://%2Fvar%2Frun%2Fldapi"); 1582 1583 usere = odusers_copy_entry(fakeop); 1584 if(!usere) { 1585 Debug(LDAP_DEBUG_ANY, "%s: Could not locate user record for %s\n", __func__, dn->bv_val, 0); 1586 goto out; 1587 } 1588 1589 if( odusers_get_authguid(usere, authguid) != 0) { 1590 Debug(LDAP_DEBUG_ANY, "%s: Could not locate authguid for user %s\n", __func__, dn->bv_val, 0); 1591 goto out; 1592 } 1593 1594 recname = odusers_copy_recname(fakeop); 1595 if(!recname) { 1596 Debug(LDAP_DEBUG_ANY, "%s: Could not identify record name for %s\n", __func__, dn->bv_val, 0); 1597 goto out; 1598 } 1599 1600 char slotid[37]; 1601 int sloti, slotn; 1602 slotid[0] = '0'; 1603 slotid[1] = 'x'; 1604 for(sloti = 0, slotn = 2; slotn < 34 && sloti < 36; sloti++) { 1605 if( authguid[sloti] != '-' ) { 1606 slotid[slotn] = authguid[sloti]; 1607 slotn++; 1608 } 1609 } 1610 slotid[slotn] = '\0'; 1611 1612 saslrealm = odusers_copy_saslrealm(); 1613 if(!saslrealm) { 1614 Debug(LDAP_DEBUG_ANY, "%s: Could not find sasl realm\n", __func__, 0, 0); 1615 goto out; 1616 } 1617 1618 suffix = odusers_copy_suffix(); 1619 if(!suffix) { 1620 Debug(LDAP_DEBUG_ANY, "%s: Could not find default naming context\n", __func__, 0, 0); 1621 goto out; 1622 } 1623 1624 enabledmechArray = odusers_copy_enabledmechs(suffix); 1625 if(!enabledmechArray) { 1626 Debug(LDAP_DEBUG_ANY, "%s: Could not find enabled mech list\n", __func__, 0, 0); 1627 goto out; 1628 } 1629 1630 for(i = 0; i < CFArrayGetCount(enabledmechArray); i++) { 1631 CFStringRef mech = CFArrayGetValueAtIndex(enabledmechArray, i); 1632 if(!mech) continue; 1633 if(CFStringCompare(mech, CFSTR("GSSAPI"), kCFCompareCaseInsensitive) == kCFCompareEqualTo) { 1634 kerbrealm = odusers_copy_krbrealm(fakeop); 1635 if(!kerbrealm) { 1636 Debug(LDAP_DEBUG_ANY, "%s: Could not find kerberos realm\n", __func__, dn->bv_val, 0); 1637 goto out; 1638 } 1639 1640 principal = CFStringCreateWithFormat(NULL, NULL, CFSTR("%s@%s"), recname, kerbrealm); 1641 cfpassword = CFStringCreateWithCString(NULL, password, kCFStringEncodingUTF8); 1642 enctypes = CFArrayCreate(NULL, (const void**)&encVals, sizeof(encVals)/sizeof(*encVals), &kCFTypeArrayCallBacks); 1643 if (!enctypes) 1644 { 1645 Debug(LDAP_DEBUG_ANY, "%s: Unable to create CFArray of enctypes\n", __func__, 0, 0); 1646 goto out; 1647 } 1648 1649 keyset = HeimODModifyKeys(NULL, principal, enctypes, cfpassword, 0, &error); 1650 if (!keyset) 1651 { 1652 Debug(LDAP_DEBUG_ANY, "%s: HeimODModifyKeys() returned a NULL keyset: %ld\n", __func__, error ? CFErrorGetCode(error) : 0, 0); 1653 goto out; 1654 } 1655 if (CFArrayGetCount(keyset) == 0) 1656 { 1657 Debug(LDAP_DEBUG_ANY, "%s: HeimODModifyKeys() returned an empty keyset\n", __func__,0, 0); 1658 goto out; 1659 } 1660 1661 1662 AttributeDescription *krbkeysAD = NULL; 1663 int n; 1664 1665 if(slap_str2ad("draft-krbKeySet", &krbkeysAD, &text) != 0) { 1666 Debug(LDAP_DEBUG_ANY, "%s: Could not find attribute description for draft-krbKeySet\n", __func__, 0, 0); 1667 goto out; 1668 } 1669 1670 m = ch_calloc(1, sizeof(Modifications)); 1671 m->sml_op = LDAP_MOD_REPLACE; 1672 m->sml_flags = 0; 1673 m->sml_type = krbkeysAD->ad_cname; 1674 m->sml_desc = krbkeysAD; 1675 m->sml_numvals = CFArrayGetCount(keyset); 1676 m->sml_values = ch_calloc(m->sml_numvals+1, sizeof(struct berval)); 1677 m->sml_nvalues = ch_calloc(m->sml_numvals+1, sizeof(struct berval)); 1678 for(n = 0; n < CFArrayGetCount(keyset); n++) { 1679 CFDataRef key = CFArrayGetValueAtIndex(keyset, n); 1680 m->sml_values[n].bv_len = CFDataGetLength(key); 1681 m->sml_values[n].bv_val = ch_calloc(1, CFDataGetLength(key)); 1682 memcpy(m->sml_values[n].bv_val, (void*)CFDataGetBytePtr(key), CFDataGetLength(key)); 1683 1684 m->sml_nvalues[n].bv_len = CFDataGetLength(key); 1685 m->sml_nvalues[n].bv_val = ch_calloc(1, CFDataGetLength(key)); 1686 memcpy(m->sml_nvalues[n].bv_val, (void*)CFDataGetBytePtr(key), CFDataGetLength(key)); 1687 } 1688 m->sml_next = mhead; 1689 mhead = m; 1690 } 1691 1692 if(CFStringCompare(mech, CFSTR("CRAM-MD5"), kCFCompareCaseInsensitive) == kCFCompareEqualTo) { 1693 AttributeDescription *crammd5AD = NULL; 1694 heim_CRAM_MD5_STATE crammd5state; 1695 heim_cram_md5_export(password, &crammd5state); 1696 if(slap_str2ad("cmusaslsecretCRAM-MD5", &crammd5AD, &text) != 0) { 1697 Debug(LDAP_DEBUG_ANY, "%s: Could not find attribute description for cmusaslsecretCRAM-MD5\n", __func__, 0, 0); 1698 goto out; 1699 } 1700 m = ch_calloc(1, sizeof(Modifications)); 1701 m->sml_op = LDAP_MOD_REPLACE; 1702 m->sml_flags = 0; 1703 m->sml_type = crammd5AD->ad_cname; 1704 m->sml_desc = crammd5AD; 1705 m->sml_numvals = 1; 1706 m->sml_values = ch_calloc(m->sml_numvals+1, sizeof(struct berval)); 1707 m->sml_nvalues = ch_calloc(m->sml_numvals+1, sizeof(struct berval)); 1708 m->sml_values[0].bv_len = sizeof(crammd5state); 1709 m->sml_values[0].bv_val = ch_calloc(1, sizeof(crammd5state)); 1710 memcpy(m->sml_values[0].bv_val, &crammd5state, sizeof(crammd5state)); 1711 m->sml_nvalues[0].bv_len = sizeof(crammd5state); 1712 m->sml_nvalues[0].bv_val = ch_calloc(1, sizeof(crammd5state)); 1713 memcpy(m->sml_nvalues[0].bv_val, &crammd5state, sizeof(crammd5state)); 1714 m->sml_next = mhead; 1715 mhead = m; 1716 } 1717 1718 if((CFStringCompare(mech, CFSTR("MS-CHAPv2"), kCFCompareCaseInsensitive) == kCFCompareEqualTo) || (CFStringCompare(mech, CFSTR("NTLM"), kCFCompareCaseInsensitive) == kCFCompareEqualTo) || (CFStringCompare(mech, CFSTR("SMB-NT"), kCFCompareCaseInsensitive) == kCFCompareEqualTo) || (CFStringCompare(mech, CFSTR("SMB-NTLMv2"), kCFCompareCaseInsensitive) == kCFCompareEqualTo)) { 1719 AttributeDescription *ntkeyAD = NULL; 1720 struct ntlm_buf ntlmstate; 1721 int rc; 1722 rc = heim_ntlm_nt_key(password, &ntlmstate); 1723 if(rc != 0) { 1724 Debug(LDAP_DEBUG_ANY, "%s: heim_ntlm_nt_key returned %d\n", __func__, rc, 0); 1725 goto out; 1726 } 1727 if(slap_str2ad("cmusaslsecretSMBNT", &ntkeyAD, &text) != 0) { 1728 Debug(LDAP_DEBUG_ANY, "%s: Could not find attribute description for cmusaslsecretSMBNT\n", __func__, 0, 0); 1729 goto out; 1730 } 1731 m = ch_calloc(1, sizeof(Modifications)); 1732 m->sml_op = LDAP_MOD_REPLACE; 1733 m->sml_flags = 0; 1734 m->sml_type = ntkeyAD->ad_cname; 1735 m->sml_desc = ntkeyAD; 1736 m->sml_numvals = 1; 1737 m->sml_values = ch_calloc(m->sml_numvals+1, sizeof(struct berval)); 1738 m->sml_nvalues = ch_calloc(m->sml_numvals+1, sizeof(struct berval)); 1739 m->sml_values[0].bv_len = ntlmstate.length * 2; 1740 m->sml_values[0].bv_val = ch_calloc(1, m->sml_values[0].bv_len + 1); 1741 ConvertBinaryToHex(ntlmstate.data, ntlmstate.length, m->sml_values[0].bv_val); 1742 heim_ntlm_free_buf(&ntlmstate); 1743 m->sml_nvalues[0].bv_len = m->sml_values[0].bv_len; 1744 m->sml_nvalues[0].bv_val = ch_calloc(1, m->sml_nvalues[0].bv_len); 1745 memcpy(m->sml_nvalues[0].bv_val, m->sml_values[0].bv_val, m->sml_nvalues[0].bv_len); 1746 m->sml_next = mhead; 1747 mhead = m; 1748 } 1749 if(CFStringCompare(mech, CFSTR("DIGEST-MD5"), kCFCompareCaseInsensitive) == kCFCompareEqualTo) { 1750 AttributeDescription *digestmd5AD = NULL; 1751 /* Convert authguid into slotid for use with digest-md5 hash */ 1752 char *digest_userhash = heim_digest_userhash(slotid, saslrealm, password); 1753 if(slap_str2ad("cmusaslsecretDIGEST-MD5", &digestmd5AD, &text) != 0) { 1754 Debug(LDAP_DEBUG_ANY, "%s: Could not find attribute description for cmusaslsecretDIGEST-MD5\n", __func__, 0, 0); 1755 goto out; 1756 } 1757 m = ch_calloc(1, sizeof(Modifications)); 1758 m->sml_op = LDAP_MOD_REPLACE; 1759 m->sml_flags = 0; 1760 m->sml_type = digestmd5AD->ad_cname; 1761 m->sml_desc = digestmd5AD; 1762 m->sml_numvals = 1; 1763 m->sml_values = ch_calloc(m->sml_numvals+1, sizeof(struct berval)); 1764 m->sml_nvalues = ch_calloc(m->sml_numvals+1, sizeof(struct berval)); 1765 m->sml_values[0].bv_len = tmplong = 16; 1766 m->sml_values[0].bv_val = ch_calloc(1, m->sml_values[0].bv_len+1); 1767 ConvertHexToBinary(digest_userhash, (unsigned char*)m->sml_values[0].bv_val, &tmplong); 1768 free(digest_userhash); 1769 m->sml_nvalues[0].bv_len = m->sml_values[0].bv_len; 1770 m->sml_nvalues[0].bv_val = ch_calloc(1, m->sml_nvalues[0].bv_len); 1771 memcpy(m->sml_nvalues[0].bv_val, m->sml_values[0].bv_val, m->sml_nvalues[0].bv_len); 1772 m->sml_next = mhead; 1773 mhead = m; 1774 } 1775 1776 if((CFStringCompare(mech, CFSTR("DIGEST-MD5"), kCFCompareCaseInsensitive) == kCFCompareEqualTo) || (CFStringCompare(mech, CFSTR("WEBDAV-DIGEST"), kCFCompareCaseInsensitive) == kCFCompareEqualTo)) { 1777 AttributeDescription *digestumd5AD = NULL; 1778 char *udigest_userhash = heim_digest_userhash(recname, saslrealm, password); 1779 if(slap_str2ad("cmusaslsecretDIGEST-UMD5", &digestumd5AD, &text) != 0) { 1780 Debug(LDAP_DEBUG_ANY, "%s: Could not find attribute description for cmusaslsecretDIGEST-UMD5\n", __func__, 0, 0); 1781 goto out; 1782 } 1783 needPlaintext = 1; 1784 m = ch_calloc(1, sizeof(Modifications)); 1785 m->sml_op = LDAP_MOD_REPLACE; 1786 m->sml_flags = 0; 1787 m->sml_type = digestumd5AD->ad_cname; 1788 m->sml_desc = digestumd5AD; 1789 m->sml_numvals = 1; 1790 m->sml_values = ch_calloc(m->sml_numvals+1, sizeof(struct berval)); 1791 m->sml_nvalues = ch_calloc(m->sml_numvals+1, sizeof(struct berval)); 1792 m->sml_values[0].bv_len = tmplong = 16; 1793 m->sml_values[0].bv_val = ch_calloc(1, m->sml_values[0].bv_len+1); 1794 ConvertHexToBinary(udigest_userhash, (unsigned char*)m->sml_values[0].bv_val, &tmplong); 1795 free(udigest_userhash); 1796 m->sml_nvalues[0].bv_len = m->sml_values[0].bv_len; 1797 m->sml_nvalues[0].bv_val = ch_calloc(1, m->sml_nvalues[0].bv_len); 1798 memcpy(m->sml_nvalues[0].bv_val, m->sml_values[0].bv_val, m->sml_nvalues[0].bv_len); 1799 m->sml_next = mhead; 1800 mhead = m; 1801 } 1802 1803 if(CFStringCompare(mech, CFSTR("APOP"), kCFCompareCaseInsensitive) == kCFCompareEqualTo) { 1804 needPlaintext = 1; 1805 } 1806 } 1807 1808 if(needPlaintext) { 1809 AttributeDescription *passwordAD = NULL; 1810 int encodeLen = strlen(password); 1811 encodeLen += (kFixedDESChunk - (encodeLen % kFixedDESChunk)); 1812 1813 if(slap_str2ad("password", &passwordAD, &text) != 0) { 1814 Debug(LDAP_DEBUG_ANY, "%s: Could not find attribute description for password\n", __func__, 0, 0); 1815 goto out; 1816 } 1817 m = ch_calloc(1, sizeof(Modifications)); 1818 m->sml_op = LDAP_MOD_REPLACE; 1819 m->sml_flags = 0; 1820 m->sml_type = passwordAD->ad_cname; 1821 m->sml_desc = passwordAD; 1822 m->sml_numvals = 1; 1823 m->sml_values = ch_calloc(m->sml_numvals+1, sizeof(struct berval)); 1824 m->sml_nvalues = ch_calloc(m->sml_numvals+1, sizeof(struct berval)); 1825 m->sml_values[0].bv_len = encodeLen; 1826 m->sml_values[0].bv_val = ch_calloc(1, m->sml_values[0].bv_len+1); 1827 strlcpy(m->sml_values[0].bv_val, password, encodeLen); 1828 pwsf_DESEncode(m->sml_values[0].bv_val, encodeLen); 1829 1830 m->sml_nvalues[0].bv_len = m->sml_values[0].bv_len; 1831 m->sml_nvalues[0].bv_val = ch_calloc(1, m->sml_nvalues[0].bv_len); 1832 memcpy(m->sml_nvalues[0].bv_val, m->sml_values[0].bv_val, m->sml_nvalues[0].bv_len); 1833 m->sml_next = mhead; 1834 mhead = m; 1835 } 1836 1837 if(!passModDateAD && slap_str2ad("passwordModDate", &passModDateAD, &text) != 0) { 1838 Debug(LDAP_DEBUG_ANY, "%s: Unable to retrieve description of passwordModDate attribute", __PRETTY_FUNCTION__, 0, 0); 1839 goto out; 1840 } 1841 1842 authe = odusers_copy_authdata(dn); 1843 if(!authe) { 1844 Debug(LDAP_DEBUG_ANY, "%s: Could not locate authdata for %s\n", __func__, dn->bv_val, 0); 1845 goto out; 1846 } 1847 1848 time_t tmptime; 1849 struct tm tmptm; 1850 tmptime = time(NULL); 1851 gmtime_r(&tmptime, &tmptm); 1852 m = ch_calloc(1, sizeof(Modifications)); 1853 m->sml_op = LDAP_MOD_REPLACE; 1854 m->sml_flags = 0; 1855 m->sml_type = passModDateAD->ad_cname; 1856 m->sml_values = (struct berval*) ch_calloc(2, sizeof(struct berval)); 1857 m->sml_values[0].bv_val = ch_calloc(1, 256); 1858 m->sml_values[0].bv_len = strftime(m->sml_values[0].bv_val, 256, "%Y%m%d%H%M%SZ", &tmptm); 1859 m->sml_nvalues = NULL; 1860 m->sml_numvals = 1; 1861 m->sml_desc = passModDateAD; 1862 m->sml_next = mhead; 1863 mhead = m; 1864 1865 if(!failedLoginsAD && slap_str2ad("loginFailedAttempts", &failedLoginsAD, &text) != 0) { 1866 Debug(LDAP_DEBUG_ANY, "%s: Unable to retrieve description of loginFailedAttempts attribute", __PRETTY_FUNCTION__, 0, 0); 1867 goto out; 1868 } 1869 m = (Modifications *) ch_malloc(sizeof(Modifications)); 1870 1871 m->sml_op = LDAP_MOD_REPLACE; 1872 m->sml_flags = 0; 1873 m->sml_type = failedLoginsAD->ad_cname; 1874 m->sml_values = (struct berval*) ch_calloc(2, sizeof(struct berval)); 1875 m->sml_values[0].bv_val = ch_strdup("0"); 1876 m->sml_values[0].bv_len = 1; 1877 m->sml_numvals = 1; 1878 m->sml_nvalues = NULL; 1879 1880 m->sml_desc = failedLoginsAD; 1881 m->sml_next = mhead; 1882 mhead = m; 1883 1884 odusers_clear_authhashes(authguid); 1885 1886 OperationBuffer opbuf2 = {0}; 1887 Operation *fakeop2 = NULL; 1888 Connection conn2 = {0}; 1889 1890 connection_fake_init2(&conn2, &opbuf2, ldap_pvt_thread_pool_context(), 0); 1891 fakeop2 = &opbuf2.ob_op; 1892 fakeop2->o_conn->c_listener->sl_url.bv_val = "ldapi://%2Fvar%2Frun%2Fldapi"; 1893 fakeop2->o_conn->c_listener->sl_url.bv_len = strlen("ldapi://%2Fvar%2Frun%2Fldapi"); 1894 1895 fakeop2->o_req_dn.bv_len = asprintf(&authdatadn, "authGUID=%s,cn=users,cn=authdata", authguid); 1896 fakeop2->o_req_dn.bv_val = authdatadn; 1897 fakeop2->o_req_ndn = fakeop2->o_req_dn; 1898 fakeop2->o_tag = LDAP_REQ_MODIFY; 1899 fakeop2->orm_modlist = mhead; 1900 fakeop2->o_bd = select_backend(&fakeop2->o_req_ndn, 1); 1901 if(!fakeop2->o_bd) { 1902 Debug(LDAP_DEBUG_ANY, "%s: Could not locate backend for %s\n", __func__, authdatadn, 0); 1903 goto out; 1904 } 1905 1906 fakeop2->o_callback = NULL; 1907 fakeop2->o_bd->be_modify(fakeop2, &rs); 1908 slap_mods_free(mhead, 1); 1909 if(rs.sr_err != LDAP_SUCCESS) { 1910 Debug(LDAP_DEBUG_ANY, "%s: Error modifying authdata with new password\n", __func__, 0, 0); 1911 goto out; 1912 } 1913 1914 odusers_store_history(dn, password); 1915 ret = 0; 1916out: 1917 if(usere) entry_free(usere); 1918 if(authe) entry_free(authe); 1919 ch_free(suffix); 1920 if(enabledmechArray) CFRelease(enabledmechArray); 1921 if(keyset) CFRelease(keyset); 1922 if(enctypes) CFRelease(enctypes); 1923 if(principal) CFRelease(principal); 1924 if(cfpassword) CFRelease(cfpassword); 1925 if(fakeop && !BER_BVISNULL(&fakeop->o_ndn)) ch_free(fakeop->o_ndn.bv_val); 1926 ch_free(kerbrealm); 1927 ch_free(recname); 1928 free(authdatadn); 1929 ch_free(saslrealm); 1930 return ret; 1931} 1932 1933int odusers_verify_passwordquality(const char *password, const char *username, CFDictionaryRef effectivepolicy, SlapReply *rs) { 1934 int requiresAlpha = 0; 1935 int requiresNumeric = 0; 1936 int requiresSymbol = 0; 1937 int requiresMixedCase = 0; 1938 unsigned int minChars = 0; 1939 unsigned int maxChars = 0; 1940 int passwordCannotBeName = 0; 1941 CFNumberRef tmpnum; 1942 int len = 0; 1943 1944 tmpnum = CFDictionaryGetValue(effectivepolicy, CFSTR("requiresAlpha")); 1945 if(tmpnum) { 1946 CFNumberGetValue(tmpnum, kCFNumberIntType, &requiresAlpha); 1947 } 1948 tmpnum = CFDictionaryGetValue(effectivepolicy, CFSTR("requiresNumeric")); 1949 if(tmpnum) { 1950 CFNumberGetValue(tmpnum, kCFNumberIntType, &requiresNumeric); 1951 } 1952 tmpnum = CFDictionaryGetValue(effectivepolicy, CFSTR("minChars")); 1953 if(tmpnum) { 1954 CFNumberGetValue(tmpnum, kCFNumberIntType, &minChars); 1955 } 1956 tmpnum = CFDictionaryGetValue(effectivepolicy, CFSTR("requiresSymbol")); 1957 if(tmpnum) { 1958 CFNumberGetValue(tmpnum, kCFNumberIntType, &requiresSymbol); 1959 } 1960 tmpnum = CFDictionaryGetValue(effectivepolicy, CFSTR("requiresMixedCase")); 1961 if(tmpnum) { 1962 CFNumberGetValue(tmpnum, kCFNumberIntType, &requiresMixedCase); 1963 } 1964 tmpnum = CFDictionaryGetValue(effectivepolicy, CFSTR("maxChars")); 1965 if(tmpnum) { 1966 CFNumberGetValue(tmpnum, kCFNumberIntType, &maxChars); 1967 } 1968 tmpnum = CFDictionaryGetValue(effectivepolicy, CFSTR("passwordCannotBeName")); 1969 if(tmpnum) { 1970 CFNumberGetValue(tmpnum, kCFNumberIntType, &passwordCannotBeName); 1971 } 1972 1973 len = strlen(password); 1974 if(len == 0) { 1975 rs->sr_text = "too short"; 1976 rs->sr_err = LDAP_UNWILLING_TO_PERFORM; 1977 return rs->sr_err; 1978 } 1979 1980 if(len < minChars) { 1981 rs->sr_text = "too short"; 1982 rs->sr_err = LDAP_UNWILLING_TO_PERFORM; 1983 return rs->sr_err; 1984 } 1985 1986 if(maxChars > 0 && len > maxChars) { 1987 rs->sr_text = "too long"; 1988 rs->sr_err = LDAP_UNWILLING_TO_PERFORM; 1989 return rs->sr_err; 1990 } 1991 1992 if(requiresAlpha) { 1993 bool hasAlpha = false; 1994 int index; 1995 for(index = 0; index < len; index++) { 1996 if(isalpha(password[index])) { 1997 hasAlpha = true; 1998 break; 1999 } 2000 } 2001 2002 if(!hasAlpha) { 2003 rs->sr_text = "Needs alpha"; 2004 rs->sr_err = LDAP_UNWILLING_TO_PERFORM; 2005 return rs->sr_err; 2006 } 2007 } 2008 2009 if(requiresNumeric) { 2010 bool hasNumeric = false; 2011 int index; 2012 for(index = 0; index < len; index++) { 2013 if(isdigit(password[index])) { 2014 hasNumeric = true; 2015 break; 2016 } 2017 } 2018 2019 if(!hasNumeric) { 2020 rs->sr_text = "Needs number"; 2021 rs->sr_err = LDAP_UNWILLING_TO_PERFORM; 2022 return rs->sr_err; 2023 } 2024 } 2025 2026 if(requiresMixedCase) { 2027 bool hasUpper = false; 2028 bool hasLower = false; 2029 int index; 2030 for(index = 0; index < len; index++) { 2031 if(password[index] >= 'A' && password[index] <= 'Z') { 2032 hasUpper = true; 2033 } else if(password[index] >= 'a' && password[index] <= 'z') { 2034 hasLower = true; 2035 } 2036 if(hasUpper && hasLower) { 2037 break; 2038 } 2039 } 2040 2041 if(!(hasUpper && hasLower)) { 2042 rs->sr_text = "Needs mixed case"; 2043 rs->sr_err = LDAP_UNWILLING_TO_PERFORM; 2044 return rs->sr_err; 2045 } 2046 } 2047 2048 if(requiresSymbol) { 2049 bool hasSymbol = false; 2050 int index; 2051 for(index = 0; index < len; index++) { 2052 if(password[index] >= 'A' && password[index] <= 'Z') { 2053 continue; 2054 } 2055 if(password[index] >= 'a' && password[index] <= 'z') { 2056 continue; 2057 } 2058 if(password[index] >= '0' && password[index] <= '9') { 2059 continue; 2060 } 2061 hasSymbol = true; 2062 break; 2063 } 2064 2065 if(!hasSymbol) { 2066 rs->sr_text = "Needs symbol"; 2067 rs->sr_err = LDAP_UNWILLING_TO_PERFORM; 2068 return rs->sr_err; 2069 } 2070 } 2071 2072 if(passwordCannotBeName) { 2073 uint16_t unamelen = strlen(username); 2074 uint16_t smallerlen = ((len < unamelen) ? len : unamelen); 2075 2076 if(strncasecmp(password, username, smallerlen) == 0) { 2077 rs->sr_text = "Cannot be username"; 2078 rs->sr_err = LDAP_UNWILLING_TO_PERFORM; 2079 return rs->sr_err; 2080 } 2081 } 2082 2083 return 0; 2084} 2085 2086CFArrayRef odusers_copy_enabledmechs(const char *suffix) { 2087 OperationBuffer opbuf; 2088 Connection conn; 2089 Operation *fakeop = NULL; 2090 Entry *e = NULL; 2091 struct berval dn; 2092 CFMutableArrayRef ret = NULL; 2093 char *searchdn = NULL; 2094 2095 dn.bv_len = asprintf(&searchdn, "cn=dirserv,cn=config,%s", suffix); 2096 dn.bv_val = searchdn; 2097 connection_fake_init2(&conn, &opbuf, ldap_pvt_thread_pool_context(), 0); 2098 fakeop = &opbuf.ob_op; 2099 fakeop->o_dn = fakeop->o_req_dn = dn; 2100 dnNormalize(0, NULL, NULL, &dn, &fakeop->o_req_ndn, NULL); 2101 fakeop->o_ndn = fakeop->o_req_ndn; 2102 fakeop->o_conn->c_listener->sl_url.bv_val = "ldapi://%2Fvar%2Frun%2Fldapi"; 2103 fakeop->o_conn->c_listener->sl_url.bv_len = strlen("ldapi://%2Fvar%2Frun%2Fldapi"); 2104 2105 e = odusers_copy_entry(fakeop); 2106 if(!e) { 2107 Debug(LDAP_DEBUG_ANY, "%s: could not locate %s\n", __func__, searchdn, 0); 2108 goto out; 2109 } 2110 2111 Attribute *a; 2112 AttributeDescription *ad = NULL; 2113 const char *text = NULL; 2114 if(slap_str2ad("apple-enabled-auth-mech", &ad, &text) != 0) { 2115 Debug(LDAP_DEBUG_ANY, "%s: could not get attribute description for apple-enabled-auth-mech\n", __func__, 0, 0); 2116 goto out; 2117 } 2118 a = attr_find(e->e_attrs, ad); 2119 if(!a) { 2120 Debug(LDAP_DEBUG_ANY, "%s: could not locate any apple-enabled-auth-mech attributes\n", __func__, 0, 0); 2121 goto out; 2122 } 2123 2124 int i; 2125 ret = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); 2126 if(!ret) { 2127 Debug(LDAP_DEBUG_ANY, "%s: could not create mutable array\n", __func__, 0, 0); 2128 goto out; 2129 } 2130 for(i = 0; i < a->a_numvals; i++) { 2131 CFStringRef mech = CFStringCreateWithCString(NULL, a->a_vals[i].bv_val, kCFStringEncodingUTF8); 2132 if(!mech) { 2133 Debug(LDAP_DEBUG_ANY, "%s: could not process mech %s\n", __func__, a->a_vals[i].bv_val, 0); 2134 continue; 2135 } 2136 CFArrayAppendValue(ret, mech); 2137 CFRelease(mech); 2138 } 2139out: 2140 if(e) entry_free(e); 2141 if(searchdn) free(searchdn); 2142 if(fakeop && !BER_BVISNULL(&fakeop->o_req_ndn)) ch_free(fakeop->o_req_ndn.bv_val); 2143 2144 return ret; 2145} 2146 2147int odusers_krb_auth(Operation *op, char *password) { 2148 char *name = odusers_copy_recname(op); 2149 char *realm = odusers_copy_krbrealm(op); 2150 krb5_error_code problem; 2151 krb5_context krbctx = NULL; 2152 krb5_principal princ = NULL; 2153 krb5_creds creds = {0}; 2154 int ret = -1; 2155 2156 if(!name) { 2157 Debug(LDAP_DEBUG_ANY, "%s: could not retrieve record name\n", __func__, 0, 0); 2158 goto out; 2159 } 2160 if(!realm) { 2161 Debug(LDAP_DEBUG_ANY, "%s: could not retrieve krb realm while authing %s\n", __func__, name, 0); 2162 goto out; 2163 } 2164 2165 problem = krb5_init_context(&krbctx); 2166 if(problem) { 2167 Debug(LDAP_DEBUG_ANY, "%s: Error initting krb ctx for %s: %d\n", __func__, name, problem); 2168 goto out; 2169 } 2170 2171 problem = krb5_build_principal(krbctx, &princ, (int)strlen(realm), realm, name, NULL); 2172 if(problem) { 2173 Debug(LDAP_DEBUG_ANY, "%s: Error building principal for %s: %d", __func__, name, problem); 2174 goto out; 2175 } 2176 2177 problem = krb5_get_init_creds_password(krbctx, &creds, princ, password, NULL, 0, 0, NULL, NULL); 2178 if(problem) { 2179 Debug(LDAP_DEBUG_ANY, "%s: Error obtaining credentials for %s: %d", __func__, name, problem); 2180 goto out; 2181 } 2182 2183 ret = 0; 2184out: 2185 ch_free(name); 2186 ch_free(realm); 2187 if (krbctx) { 2188 if (princ) 2189 krb5_free_principal(krbctx, princ); 2190 krb5_free_cred_contents(krbctx, &creds); 2191 krb5_free_context(krbctx); 2192 } 2193 return ret; 2194} 2195 2196char *odusers_copy_owner(struct berval *dn) { 2197 OperationBuffer opbuf; 2198 Connection conn; 2199 Operation *fakeop = NULL; 2200 Entry *e = NULL; 2201 char *ret = NULL; 2202 Attribute *a = NULL; 2203 2204 connection_fake_init2(&conn, &opbuf, ldap_pvt_thread_pool_context(), 0); 2205 fakeop = &opbuf.ob_op; 2206 fakeop->o_dn = fakeop->o_ndn = *dn; 2207 fakeop->o_req_dn = fakeop->o_req_ndn = *dn; 2208 fakeop->o_conn->c_listener->sl_url.bv_val = "ldapi://%2Fvar%2Frun%2Fldapi"; 2209 fakeop->o_conn->c_listener->sl_url.bv_len = strlen("ldapi://%2Fvar%2Frun%2Fldapi"); 2210 2211 e = odusers_copy_entry(fakeop); 2212 if(!e) { 2213 Debug(LDAP_DEBUG_ANY, "%s: could not locate %s\n", __func__, dn->bv_val, 0); 2214 goto out; 2215 } 2216 2217 a = attr_find(e->e_attrs, slap_schema.si_ad_creatorsName); 2218 if(!a) { 2219 Debug(LDAP_DEBUG_ANY, "%s: could not locate creatorsName attribute for %s\n", __func__, dn->bv_val, 0); 2220 goto out; 2221 } 2222 2223 if(a->a_numvals < 1) { 2224 Debug(LDAP_DEBUG_ANY, "%s: no values associated with creatorsName for %s\n", __func__, dn->bv_val, 0); 2225 goto out; 2226 } 2227 2228 ret = ch_calloc(1, a->a_nvals[0].bv_len + 1); 2229 if(!ret) goto out; 2230 memcpy(ret, a->a_nvals[0].bv_val, a->a_nvals[0].bv_len); 2231out: 2232 if(e) entry_free(e); 2233 return ret; 2234} 2235 2236int odusers_store_history(struct berval *dn, const char *password) { 2237 Entry *authe = NULL; 2238 int ret = -1; 2239 Attribute *a = NULL; 2240 AttributeDescription *ad = NULL; 2241 const char *text = NULL; 2242 heim_CRAM_MD5_STATE crammd5state; 2243 heim_CRAM_MD5_STATE empytcrammd5state; 2244 char *history = NULL; 2245 int i; 2246 short historyCount = 0; 2247 char *newhistory = NULL; 2248 Modifications *mod = NULL; 2249 short optype = LDAP_MOD_ADD; 2250 OperationBuffer opbuf = {0}; 2251 Operation *fakeop = NULL; 2252 Connection conn = {0}; 2253 SlapReply rs = {REP_RESULT}; 2254 2255 bzero(&empytcrammd5state, sizeof(empytcrammd5state)); 2256 heim_cram_md5_export(password, &crammd5state); 2257 2258 authe = odusers_copy_authdata(dn); 2259 if(!authe) { 2260 Debug(LDAP_DEBUG_ANY, "%s: Could not locate authdata for %s\n", __func__, dn->bv_val, 0); 2261 goto out; 2262 } 2263 2264 if(slap_str2ad("historyData", &ad, &text) != 0) { 2265 Debug(LDAP_DEBUG_ANY, "%s: could not get attribute description for historyData\n", __func__, 0, 0); 2266 goto out; 2267 } 2268 newhistory = ch_malloc(sizeof(heim_CRAM_MD5_STATE)*16); 2269 2270 a = attr_find(authe->e_attrs, ad); 2271 if(a) { 2272 if(a->a_numvals < 1) { 2273 Debug(LDAP_DEBUG_ANY, "%s: history attribute lacks values for %s\n", __func__, dn->bv_val, 0); 2274 ret = 0; 2275 goto out; 2276 } 2277 2278 if(a->a_nvals[0].bv_len > (16*sizeof(crammd5state))) { 2279 Debug(LDAP_DEBUG_ANY, "%s: history data is larger than expected for %s\n", __func__, dn->bv_val, 0); 2280 goto out; 2281 } 2282 2283 2284 history = a->a_nvals[0].bv_val; 2285 for(i = 0; i < 15; i++) { 2286 if(memcmp(&crammd5state, history, sizeof(crammd5state)) == 0) { /* If the password already exists in the history, return success */ 2287 ret = 0; 2288 goto out; 2289 } else if (memcmp(&empytcrammd5state, history, sizeof(crammd5state)) == 0) { /* history list is null padded - halt on first empty entry */ 2290 break; 2291 } 2292 historyCount++; 2293 2294 history += sizeof(crammd5state); 2295 } 2296 2297 history = a->a_nvals[0].bv_val; 2298 memcpy(newhistory, a->a_nvals[0].bv_val, a->a_nvals[0].bv_len); 2299 for(i = 15; i >= 1; i--) { 2300 memcpy(newhistory + i*sizeof(crammd5state), newhistory + (i-1)*sizeof(crammd5state), sizeof(crammd5state)); 2301 } 2302 optype = LDAP_MOD_REPLACE; 2303 } else { /* history is empty - add first password */ 2304 optype = LDAP_MOD_ADD; 2305 } 2306 2307 /* place newPassword hash in first entry of history */ 2308 memcpy(newhistory, &crammd5state, sizeof(crammd5state)); 2309 historyCount++; 2310 2311 /* null pad empty entries of history */ 2312 if((historyCount-1) < 15) { 2313 bzero(newhistory + historyCount*sizeof(crammd5state), (16 - historyCount) * sizeof(crammd5state)); 2314 } 2315 2316 mod = (Modifications *)ch_malloc(sizeof(Modifications)); 2317 mod->sml_op = optype; 2318 mod->sml_flags = 0; 2319 mod->sml_type = ad->ad_cname; 2320 mod->sml_values = (struct berval*)ch_malloc(2 * sizeof(struct berval)); 2321 mod->sml_values[0].bv_val = newhistory; 2322 mod->sml_values[0].bv_len = sizeof(heim_CRAM_MD5_STATE)*16; 2323 mod->sml_values[1].bv_len = 0; 2324 mod->sml_values[1].bv_val = NULL; 2325 mod->sml_numvals = 1; 2326 mod->sml_nvalues = NULL; 2327 mod->sml_desc = ad; 2328 mod->sml_next = NULL; 2329 2330 connection_fake_init2(&conn, &opbuf, ldap_pvt_thread_pool_context(), 0); 2331 fakeop = &opbuf.ob_op; 2332 fakeop->o_dn = fakeop->o_ndn = *dn; 2333 fakeop->o_req_dn = authe->e_name; 2334 fakeop->o_req_ndn = authe->e_nname; 2335 fakeop->orm_modlist = mod; 2336 fakeop->o_tag = LDAP_REQ_MODIFY; 2337 fakeop->o_conn->c_listener->sl_url.bv_val = "ldapi://%2Fvar%2Frun%2Fldapi"; 2338 fakeop->o_conn->c_listener->sl_url.bv_len = strlen("ldapi://%2Fvar%2Frun%2Fldapi"); 2339 fakeop->o_bd = frontendDB; 2340 fakeop->o_bd->be_modify(fakeop, &rs); 2341 if(rs.sr_err != LDAP_SUCCESS) { 2342 Debug(LDAP_DEBUG_ANY, "Unable to modify history for %s: %d %s\n", dn->bv_val, rs.sr_err, rs.sr_text); 2343 goto out; 2344 } 2345 2346 ret = 0; 2347out: 2348 if(authe) entry_free(authe); 2349 if(mod) { 2350 slap_mods_free(mod, 1); 2351 } else { 2352 if(newhistory) ch_free(newhistory); 2353 } 2354 return ret; 2355} 2356 2357void odusers_accountpolicy_set_passwordinfo(CFMutableDictionaryRef accountpolicyinfo, const char *password) { 2358 2359 /* kAPAttributePassword */ 2360 CFStringRef passcfstr = CFStringCreateWithCString(kCFAllocatorDefault, password, kCFStringEncodingUTF8); 2361 CFDictionarySetValue(accountpolicyinfo, kAPAttributePassword, passcfstr); 2362 2363 /* kAPAttributePasswordHashes */ 2364 heim_CRAM_MD5_STATE crammd5state; 2365 heim_cram_md5_export(password, &crammd5state); 2366 CFDataRef passcfdata = CFDataCreate(kCFAllocatorDefault, (UInt8*)&crammd5state, sizeof(crammd5state)); 2367 CFMutableArrayRef passwdhashArray = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); 2368 CFArrayAppendValue(passwdhashArray, passcfdata); 2369 CFDictionarySetValue(accountpolicyinfo, kAPAttributePasswordHashes, passwdhashArray); 2370 2371 CFRelease(passcfstr); 2372 CFRelease(passcfdata); 2373 CFRelease(passwdhashArray); 2374} 2375 2376char *CopyPrimaryIPv4Address(void) 2377{ 2378 char *ret = NULL; 2379 char *primaryInterfaceNameCStr = NULL; 2380 2381 SCDynamicStoreRef session = SCDynamicStoreCreate(NULL, CFSTR("org.openldap.slapd"), NULL, NULL); 2382 if (session == NULL) { 2383 return NULL; 2384 } 2385 2386 CFStringRef primaryInterfaceKey = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL, (CFStringRef)kSCDynamicStoreDomainState, (CFStringRef)kSCEntNetIPv4); 2387 if (primaryInterfaceKey != NULL) { 2388 CFDictionaryRef primaryInterfaceDict = SCDynamicStoreCopyValue(session, primaryInterfaceKey); 2389 if (primaryInterfaceDict) { 2390 if (CFGetTypeID(primaryInterfaceDict) == CFDictionaryGetTypeID()) { 2391 CFStringRef primaryInterfaceName = CFDictionaryGetValue(primaryInterfaceDict, kSCDynamicStorePropNetPrimaryInterface); 2392 if (primaryInterfaceName) { 2393 CFIndex size = CFStringGetMaximumSizeForEncoding(CFStringGetLength(primaryInterfaceName), kCFStringEncodingUTF8) + 1; 2394 primaryInterfaceNameCStr = malloc(size); 2395 if (primaryInterfaceNameCStr) { 2396 if (!CFStringGetCString(primaryInterfaceName, primaryInterfaceNameCStr, size, kCFStringEncodingUTF8)) { 2397 free(primaryInterfaceNameCStr); 2398 primaryInterfaceNameCStr = NULL; 2399 } 2400 } 2401 } 2402 } 2403 CFRelease(primaryInterfaceDict); 2404 } 2405 CFRelease(primaryInterfaceKey); 2406 } 2407 CFRelease(session); 2408 session = NULL; 2409 2410 if (!primaryInterfaceNameCStr) { 2411 return NULL; 2412 } 2413 2414 struct ifaddrs *ifap = NULL; 2415 if (getifaddrs(&ifap) != 0) { 2416 return NULL; 2417 } 2418 2419 struct ifaddrs *ifa; 2420 for (ifa = ifap; ifa != NULL; ifa = ifa->ifa_next) 2421 { 2422 if (ifa->ifa_name == NULL) continue; 2423 if (ifa->ifa_addr == NULL) continue; 2424 if (strncmp(ifa->ifa_name, "lo", 2) == 0) continue; 2425 if (ifa->ifa_addr->sa_family == AF_INET) { 2426 if (strcmp(ifa->ifa_name, primaryInterfaceNameCStr) == 0) { 2427 char ipv4AddressCStr[INET_ADDRSTRLEN] = { 0 }; 2428 if (inet_ntop(AF_INET, &((struct sockaddr_in*)ifa->ifa_addr)->sin_addr.s_addr, ipv4AddressCStr, sizeof(ipv4AddressCStr))) { 2429 ret = ch_strdup(ipv4AddressCStr); 2430 break; 2431 } 2432 } 2433 } 2434 } 2435 freeifaddrs(ifap); 2436 2437 return ret; 2438} 2439 2440int odusers_accountpolicy_set(struct berval *dn, Entry *authe, CFDictionaryRef accountpolicydict) 2441{ 2442 int ret = -1; 2443 OperationBuffer opbuf = {0}; 2444 Operation *fakeop = NULL; 2445 Connection conn = {0}; 2446 SlapReply rs = {REP_RESULT}; 2447 const char *text = NULL; 2448 struct berval *policy_bv = NULL; 2449 2450 Modifications *mod = NULL; 2451 Modifications *modhead = NULL; 2452 2453 if(!appleAccountPolicyAD && slap_str2ad("apple-accountpolicy", &appleAccountPolicyAD, &text) != 0) { 2454 Debug(LDAP_DEBUG_ANY, "%s: Unable to retrieve description of apple-accountpolicy attribute", __PRETTY_FUNCTION__, 0, 0); 2455 goto out; 2456 } 2457 2458 mod = (Modifications *) ch_malloc(sizeof(Modifications)); 2459 2460 policy_bv = odusers_copy_dict2bv(accountpolicydict); 2461 2462 mod->sml_op = LDAP_MOD_REPLACE; 2463 mod->sml_flags = 0; 2464 mod->sml_type = appleAccountPolicyAD->ad_cname; 2465 mod->sml_values = (struct berval*) ch_malloc(2 * sizeof(struct berval)); 2466 mod->sml_values[0].bv_val = policy_bv->bv_val; 2467 mod->sml_values[0].bv_len = policy_bv->bv_len; 2468 mod->sml_values[1].bv_val = NULL; 2469 mod->sml_values[1].bv_len = 0; 2470 mod->sml_nvalues = NULL; 2471 mod->sml_numvals = 1; 2472 2473 mod->sml_desc = appleAccountPolicyAD; 2474 mod->sml_next = modhead; 2475 modhead = mod; 2476 2477 if(!modhead) { 2478 ret = 0; 2479 goto out; 2480 } 2481 2482 connection_fake_init2(&conn, &opbuf, ldap_pvt_thread_pool_context(), 0); 2483 fakeop = &opbuf.ob_op; 2484 fakeop->o_dn = *dn; 2485 fakeop->o_ndn = *dn; 2486 fakeop->o_req_dn = authe->e_name; 2487 fakeop->o_req_ndn = authe->e_name; 2488 fakeop->orm_modlist = modhead; 2489 fakeop->o_tag = LDAP_REQ_MODIFY; 2490 fakeop->o_conn->c_listener->sl_url.bv_val = "ldapi://%2Fvar%2Frun%2Fldapi"; 2491 fakeop->o_conn->c_listener->sl_url.bv_len = strlen("ldapi://%2Fvar%2Frun%2Fldapi"); 2492 fakeop->o_bd = frontendDB; 2493 2494 fakeop->o_bd->be_modify(fakeop, &rs); 2495 if(rs.sr_err != LDAP_SUCCESS) { 2496 Debug(LDAP_DEBUG_ANY, "Unable to modify accountpolicy for user %s: %d %s\n", fakeop->o_req_ndn.bv_val, rs.sr_err, rs.sr_text); 2497 goto out; 2498 } 2499 2500 APInvalidateCacheForPolicySet(NULL); 2501 2502 ret = 0; 2503 2504out: 2505 if(modhead) slap_mods_free(modhead, 1); 2506 ch_free(policy_bv); 2507 return ret; 2508} 2509 2510void odusers_accountpolicy_updatedata( CFDictionaryRef updates, struct berval *dn ) 2511{ 2512 OperationBuffer opbuf = {0}; 2513 Operation *fakeop = NULL; 2514 Connection conn = {0}; 2515 SlapReply rs = {REP_RESULT}; 2516 2517 Modifications *mod = NULL; 2518 Modifications *modhead = NULL; 2519 char *failedLoginsStr = NULL; 2520 2521 Entry *e = NULL; 2522 CFNumberRef failedLogins = NULL; 2523 CFNumberRef lastFailedLoginTime = NULL; 2524 CFNumberRef lastLoginTime = NULL; 2525 CFNumberRef passModDate = NULL; 2526 const char *text = NULL; 2527 int cFailedLogins = 0; 2528 long long cLastLoginTime = 0; 2529 long long cLastFailedLoginTime = 0; 2530 long long cPassModTime = 0; 2531 2532 char * pass = NULL; 2533 2534 if (!updates) return; 2535 2536 e = odusers_copy_authdata(dn); 2537 if(!e) { 2538 Debug(LDAP_DEBUG_ANY, "%s: No entry associated with %s\n", __PRETTY_FUNCTION__, dn->bv_val, 0); 2539 goto out; 2540 } 2541 2542 failedLogins = CFDictionaryGetValue(updates, kAPAttributeFailedAuthentications); 2543 if(failedLogins) CFNumberGetValue(failedLogins, kCFNumberIntType, &cFailedLogins); 2544 if(cFailedLogins) { 2545 if(!failedLoginsAD && slap_str2ad("loginFailedAttempts", &failedLoginsAD, &text) != 0) { 2546 Debug(LDAP_DEBUG_ANY, "%s: Unable to retrieve description of loginFailedAttempts attribute", __PRETTY_FUNCTION__, 0, 0); 2547 goto out; 2548 } 2549 2550 asprintf(&failedLoginsStr, "%d", cFailedLogins); 2551 2552 mod = (Modifications *) ch_malloc(sizeof(Modifications)); 2553 2554 mod->sml_op = LDAP_MOD_REPLACE; 2555 mod->sml_flags = 0; 2556 mod->sml_type = failedLoginsAD->ad_cname; 2557 mod->sml_values = (struct berval*) ch_malloc(2 * sizeof(struct berval)); 2558 ber_str2bv(failedLoginsStr, strlen(failedLoginsStr), 1, &mod->sml_values[0]); 2559 free(failedLoginsStr); 2560 2561 mod->sml_values[1].bv_val = NULL; 2562 mod->sml_values[1].bv_len = 0; 2563 mod->sml_nvalues = NULL; 2564 mod->sml_numvals = 1; 2565 2566 mod->sml_desc = failedLoginsAD; 2567 mod->sml_next = modhead; 2568 modhead = mod; 2569 } 2570 2571 lastLoginTime = CFDictionaryGetValue(updates, kAPAttributeLastAuthenticationTime); 2572 if(lastLoginTime) CFNumberGetValue(lastLoginTime, kCFNumberLongLongType, &cLastLoginTime); 2573 if(cLastLoginTime) { 2574 time_t tmptime = (time_t)cLastLoginTime; 2575 struct tm tmptm; 2576 2577 if(!lastLoginAD && slap_str2ad("lastLoginTime", &lastLoginAD, &text) != 0) { 2578 Debug(LDAP_DEBUG_ANY, "%s: Unable to retrieve description of lastLoginTime attribute", __PRETTY_FUNCTION__, 0, 0); 2579 goto out; 2580 } 2581 2582 gmtime_r(&tmptime, &tmptm); 2583 2584 mod = (Modifications *) ch_malloc(sizeof(Modifications)); 2585 2586 mod->sml_op = LDAP_MOD_REPLACE; 2587 mod->sml_flags = 0; 2588 mod->sml_type = lastLoginAD->ad_cname; 2589 mod->sml_values = (struct berval*) ch_malloc(2 * sizeof(struct berval)); 2590 mod->sml_values[0].bv_val = ch_calloc(1, 256); 2591 mod->sml_values[0].bv_len = strftime(mod->sml_values[0].bv_val, 256, "%Y%m%d%H%M%SZ", &tmptm); 2592 mod->sml_values[1].bv_val = NULL; 2593 mod->sml_values[1].bv_len = 0; 2594 mod->sml_nvalues = NULL; 2595 mod->sml_numvals = 1; 2596 2597 mod->sml_desc = lastLoginAD; 2598 mod->sml_next = modhead; 2599 modhead = mod; 2600 } 2601 2602 lastFailedLoginTime = CFDictionaryGetValue(updates, kAPAttributeLastFailedAuthenticationTime); 2603 if(lastFailedLoginTime) CFNumberGetValue(lastFailedLoginTime, kCFNumberLongLongType, &cLastFailedLoginTime); 2604 if(cLastFailedLoginTime) { 2605 time_t tmptime = (time_t)cLastFailedLoginTime; 2606 struct tm tmptm; 2607 2608 if(!lastFailedLoginAD && slap_str2ad("lastFailedLoginTime", &lastFailedLoginAD, &text) != 0) { 2609 Debug(LDAP_DEBUG_ANY, "%s: Unable to retrieve description of lastFailedLoginTime attribute", __PRETTY_FUNCTION__, 0, 0); 2610 goto out; 2611 } 2612 2613 gmtime_r(&tmptime, &tmptm); 2614 2615 mod = (Modifications *) ch_malloc(sizeof(Modifications)); 2616 2617 mod->sml_op = LDAP_MOD_REPLACE; 2618 mod->sml_flags = 0; 2619 mod->sml_type = lastLoginAD->ad_cname; 2620 mod->sml_values = (struct berval*) ch_malloc(2 * sizeof(struct berval)); 2621 mod->sml_values[0].bv_val = ch_calloc(1, 256); 2622 mod->sml_values[0].bv_len = strftime(mod->sml_values[0].bv_val, 256, "%Y%m%d%H%M%SZ", &tmptm); 2623 mod->sml_values[1].bv_val = NULL; 2624 mod->sml_values[1].bv_len = 0; 2625 mod->sml_nvalues = NULL; 2626 mod->sml_numvals = 1; 2627 2628 mod->sml_desc = lastFailedLoginAD; 2629 mod->sml_next = modhead; 2630 modhead = mod; 2631 } 2632 2633/* for APPasswordChangeAllowed */ 2634 passModDate = CFDictionaryGetValue(updates, kAPAttributeLastPasswordChangeTime); 2635 if(passModDate) CFNumberGetValue(passModDate, kCFNumberLongLongType, &cPassModTime); 2636 if(cPassModTime) { 2637 time_t tmptime = (time_t)cPassModTime; 2638 struct tm tmptm; 2639 2640 if(!passModDateAD && slap_str2ad("passwordModDate", &passModDateAD, &text) != 0) { 2641 Debug(LDAP_DEBUG_ANY, "%s: Unable to retrieve description of passwordModDate attribute", __PRETTY_FUNCTION__, 0, 0); 2642 goto out; 2643 } 2644 2645 gmtime_r(&tmptime, &tmptm); 2646 2647 mod = (Modifications *) ch_malloc(sizeof(Modifications)); 2648 2649 mod->sml_op = LDAP_MOD_REPLACE; 2650 mod->sml_flags = 0; 2651 mod->sml_type = passModDateAD->ad_cname; 2652 mod->sml_values = (struct berval*) ch_malloc(2 * sizeof(struct berval)); 2653 mod->sml_values[0].bv_val = ch_calloc(1, 256); 2654 mod->sml_values[0].bv_len = strftime(mod->sml_values[0].bv_val, 256, "%Y%m%d%H%M%SZ", &tmptm); 2655 mod->sml_values[1].bv_val = NULL; 2656 mod->sml_values[1].bv_len = 0; 2657 mod->sml_nvalues = NULL; 2658 mod->sml_numvals = 1; 2659 2660 mod->sml_desc = passModDateAD; 2661 mod->sml_next = modhead; 2662 modhead = mod; 2663 } 2664 2665 CFStringRef pwd = CFDictionaryGetValue(updates, kAPAttributePassword); 2666 if (pwd) { 2667 CFIndex strsize = CFStringGetMaximumSizeForEncoding(CFStringGetLength(pwd), kCFStringEncodingUTF8) + 1; 2668 pass = ch_calloc(1, strsize); 2669 if (CFStringGetCString(pwd, pass, strsize,kCFStringEncodingUTF8)) { 2670 if (odusers_set_password(dn, pass, 0) != 0) { 2671 Debug(LDAP_DEBUG_ANY, "%s: set password for user %s failed\n", __func__, dn->bv_val, 0); 2672 goto out; 2673 } 2674 } else { 2675 Debug(LDAP_DEBUG_ANY, "%s: could not retrieve user password for %s\n", __func__, dn->bv_val, 0); 2676 } 2677 } 2678 2679 if(!modhead) { 2680 goto out; 2681 } 2682 2683 connection_fake_init2(&conn, &opbuf, ldap_pvt_thread_pool_context(), 0); 2684 fakeop = &opbuf.ob_op; 2685 fakeop->o_dn = *dn; 2686 fakeop->o_ndn = *dn; 2687 fakeop->o_req_dn = e->e_name; 2688 fakeop->o_req_ndn = e->e_name; 2689 fakeop->orm_modlist = modhead; 2690 fakeop->o_tag = LDAP_REQ_MODIFY; 2691 fakeop->o_conn->c_listener->sl_url.bv_val = "ldapi://%2Fvar%2Frun%2Fldapi"; 2692 fakeop->o_conn->c_listener->sl_url.bv_len = strlen("ldapi://%2Fvar%2Frun%2Fldapi"); 2693 fakeop->o_bd = frontendDB; 2694 2695 fakeop->o_bd->be_modify(fakeop, &rs); 2696 if(rs.sr_err != LDAP_SUCCESS) { 2697 Debug(LDAP_DEBUG_ANY, "Unable to modify record for successful login for user %s: %d %s\n", fakeop->o_req_ndn.bv_val, rs.sr_err, rs.sr_text); 2698 goto out; 2699 } 2700 2701out: 2702 if(e) entry_free(e); 2703 if(modhead) slap_mods_free(modhead, 1); 2704 if ( fakeop && !BER_BVISNULL( &fakeop->o_csn ) ) { 2705 fakeop->o_tmpfree( fakeop->o_csn.bv_val, fakeop->o_tmpmemctx ); 2706 } 2707 ch_free(pass); 2708} 2709 2710CFDictionaryRef odusers_accountpolicy_retrievedata( CFDictionaryRef *policyData, CFArrayRef keys, struct berval *dn ) 2711{ 2712 CFMutableDictionaryRef data = NULL; 2713 int i; 2714 int count; 2715 2716 Debug(LDAP_DEBUG_TRACE, "%s: %s\n", __func__, dn->bv_val, 0); 2717 if (keys) { 2718 for ( i = 0, 2719 count = CFArrayGetCount(keys), 2720 data = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks) 2721 ; i < count 2722 ; i++) { 2723 CFStringRef value = CFArrayGetValueAtIndex(keys, i); 2724 if(CFStringCompare(value, kAPAttributeGlobalPolicies, kCFCompareCaseInsensitive) == kCFCompareEqualTo) { 2725 CFDictionaryRef globalaccountpolicyDict = odusers_copy_globalaccountpolicy(); 2726 if (globalaccountpolicyDict) { 2727 CFDictionarySetValue(data, kAPAttributeGlobalPolicies, globalaccountpolicyDict); 2728 CFRelease(globalaccountpolicyDict); 2729 } 2730 } else if (CFStringCompare(value, kAPAttributeRecordPolicies, kCFCompareCaseInsensitive) == kCFCompareEqualTo) { 2731 CFDictionaryRef accountPolicyDict = odusers_copy_accountpolicy(dn); 2732 if (accountPolicyDict) { 2733 CFDictionarySetValue(data, kAPAttributeRecordPolicies, accountPolicyDict); 2734 CFRelease(accountPolicyDict); 2735 } 2736 } 2737 } 2738 } 2739 2740 if (policyData) *policyData = data; 2741 return data; 2742} 2743 2744CFStringRef odusers_copy_globalaccountpolicyGUID() 2745{ 2746 OperationBuffer opbuf; 2747 Connection conn; 2748 Operation *fakeop = NULL; 2749 Entry *e = NULL; 2750 Attribute *entryUUID = NULL; 2751 CFStringRef globalaccountpolicyGUID_cfstr = NULL; 2752 2753 connection_fake_init2(&conn, &opbuf, ldap_pvt_thread_pool_context(), 0); 2754 fakeop = &opbuf.ob_op; 2755 fakeop->o_req_dn.bv_len = asprintf(&fakeop->o_req_dn.bv_val, "cn=access,cn=authdata"); 2756 fakeop->o_req_dn.bv_len = strlen(fakeop->o_req_dn.bv_val); 2757 fakeop->o_dn = fakeop->o_ndn = fakeop->o_req_ndn = fakeop->o_req_dn; 2758 fakeop->o_conn->c_listener->sl_url.bv_val = "ldapi://%2Fvar%2Frun%2Fldapi"; 2759 fakeop->o_conn->c_listener->sl_url.bv_len = strlen("ldapi://%2Fvar%2Frun%2Fldapi"); 2760 2761 e = odusers_copy_entry(fakeop); 2762 if(!e) goto out; 2763 2764 entryUUID = attr_find( e->e_attrs, slap_schema.si_ad_entryUUID ); 2765 if(!entryUUID) { 2766 Debug(LDAP_DEBUG_ANY, "%s: couldn't find entryUUID attribute for %s", __PRETTY_FUNCTION__, fakeop->o_req_dn.bv_val, 0); 2767 goto out; 2768 } 2769 2770 globalaccountpolicyGUID_cfstr = CFStringCreateWithCString(NULL, entryUUID->a_vals[0].bv_val, kCFStringEncodingUTF8); 2771 2772out: 2773 if(e) entry_free(e); 2774 if(fakeop && fakeop->o_req_dn.bv_val) { 2775 free(fakeop->o_req_dn.bv_val); 2776 fakeop->o_req_dn.bv_val = NULL; 2777 } 2778 return globalaccountpolicyGUID_cfstr; 2779} 2780 2781CFDictionaryRef odusers_copy_globalaccountpolicy() 2782{ 2783 OperationBuffer opbuf; 2784 Connection conn; 2785 Operation *fakeop = NULL; 2786 Entry *e = NULL; 2787 Attribute *appleAccountPolicyAttr = NULL; 2788 const char *text = NULL; 2789 CFDictionaryRef globaldict = NULL; 2790 2791 connection_fake_init2(&conn, &opbuf, ldap_pvt_thread_pool_context(), 0); 2792 fakeop = &opbuf.ob_op; 2793 fakeop->o_req_dn.bv_len = asprintf(&fakeop->o_req_dn.bv_val, "cn=access,cn=authdata"); 2794 fakeop->o_req_dn.bv_len = strlen(fakeop->o_req_dn.bv_val); 2795 fakeop->o_dn = fakeop->o_ndn = fakeop->o_req_ndn = fakeop->o_req_dn; 2796 fakeop->o_conn->c_listener->sl_url.bv_val = "ldapi://%2Fvar%2Frun%2Fldapi"; 2797 fakeop->o_conn->c_listener->sl_url.bv_len = strlen("ldapi://%2Fvar%2Frun%2Fldapi"); 2798 2799 e = odusers_copy_entry(fakeop); 2800 if(!e) goto out; 2801 2802 if(!appleAccountPolicyAD && slap_str2ad("apple-accountpolicy", &appleAccountPolicyAD, &text) != 0) { 2803 Debug(LDAP_DEBUG_ANY, "%s: Unable to retrieve description of apple-accountpolicy attribute", __PRETTY_FUNCTION__, 0, 0); 2804 goto out; 2805 } 2806 2807 appleAccountPolicyAttr = attr_find( e->e_attrs, appleAccountPolicyAD ); 2808 2809 if(appleAccountPolicyAttr && appleAccountPolicyAttr->a_numvals != 0) { 2810 globaldict = CopyPolicyToDict(appleAccountPolicyAttr->a_vals[0].bv_val, appleAccountPolicyAttr->a_vals[0].bv_len); 2811 if(!globaldict) { 2812 Debug(LDAP_DEBUG_ANY, "%s: Unable to convert retrieved global account policy to CFDictionary", __PRETTY_FUNCTION__, 0, 0); 2813 goto out; 2814 } 2815 } 2816 2817out: 2818 if(e) entry_free(e); 2819 if(fakeop && fakeop->o_req_dn.bv_val) free(fakeop->o_req_dn.bv_val); 2820 return globaldict; 2821} 2822 2823CFDictionaryRef odusers_copy_accountpolicy_fromentry(Entry *authe) 2824{ 2825 const char *text = NULL; 2826 Attribute *appleAccountPolicyAttr = NULL; 2827 CFDictionaryRef policyDict = NULL; 2828 2829 if(!appleAccountPolicyAD && slap_str2ad("apple-accountpolicy", &appleAccountPolicyAD, &text) != 0) { 2830 Debug(LDAP_DEBUG_ANY, "%s: Unable to retrieve description of apple-accountpolicy attribute", __PRETTY_FUNCTION__, 0, 0); 2831 goto out; 2832 } 2833 2834 appleAccountPolicyAttr = attr_find( authe->e_attrs, appleAccountPolicyAD ); 2835 2836 if(appleAccountPolicyAttr && appleAccountPolicyAttr->a_numvals != 0) { 2837 policyDict = CopyPolicyToDict(appleAccountPolicyAttr->a_vals[0].bv_val, appleAccountPolicyAttr->a_vals[0].bv_len); 2838 if(!policyDict) { 2839 Debug(LDAP_DEBUG_ANY, "%s: Unable to convert retrieved account policy to CFDictionary", __PRETTY_FUNCTION__, 0, 0); 2840 goto out; 2841 } 2842 } 2843out: 2844 return policyDict; 2845} 2846 2847CFDictionaryRef odusers_copy_accountpolicy(struct berval *dn) 2848{ 2849 Entry *authe = NULL; 2850 const char *text = NULL; 2851 Attribute *appleAccountPolicyAttr = NULL; 2852 CFDictionaryRef policyDict = NULL; 2853 2854 authe = odusers_copy_authdata(dn); 2855 if(!authe) goto out; 2856 2857 if(!appleAccountPolicyAD && slap_str2ad("apple-accountpolicy", &appleAccountPolicyAD, &text) != 0) { 2858 Debug(LDAP_DEBUG_ANY, "%s: Unable to retrieve description of apple-accountpolicy attribute", __PRETTY_FUNCTION__, 0, 0); 2859 goto out; 2860 } 2861 2862 appleAccountPolicyAttr = attr_find( authe->e_attrs, appleAccountPolicyAD ); 2863 2864 if(appleAccountPolicyAttr && appleAccountPolicyAttr->a_numvals != 0) { 2865 policyDict = CopyPolicyToDict(appleAccountPolicyAttr->a_vals[0].bv_val, appleAccountPolicyAttr->a_vals[0].bv_len); 2866 if(!policyDict) { 2867 Debug(LDAP_DEBUG_ANY, "%s: Unable to convert retrieved account policy to CFDictionary", __PRETTY_FUNCTION__, 0, 0); 2868 goto out; 2869 } 2870 } 2871 2872out: 2873 if(authe) entry_free(authe); 2874 return policyDict; 2875} 2876 2877CFDictionaryRef odusers_copy_accountpolicyinfo(struct berval *dn) { 2878 OperationBuffer opbuf = {0}; 2879 Operation *fakeop = NULL; 2880 Connection conn = {0}; 2881 Entry *usere = NULL; 2882 Entry *authe = NULL; 2883 char *recname = NULL; 2884 bool isAdmin = false; 2885 bool isDisabled = false; 2886 char *suffix = odusers_copy_suffix(); 2887 2888 char *groupstr = NULL; 2889 struct berval *groupdn = NULL; 2890 int groupdnlen = 0; 2891 2892 Attribute *failedLogins = NULL; 2893 Attribute *creationDate = NULL; 2894 Attribute *passModDate = NULL; 2895 Attribute *lastLogin = NULL; 2896 Attribute *lastFailedLogin = NULL; 2897 Attribute *usereUUID = NULL; 2898 Attribute *historyData = NULL; 2899 AttributeDescription *historyDataAD = NULL; 2900 const char *text = NULL; 2901 uint16_t loginattempts = 0; 2902 2903 CFDictionaryRef userdict = NULL; 2904 CFStringRef userpolicyGUID = NULL; 2905 CFStringRef globalaccountpolicyGUID = NULL; 2906 CFStringRef cfrecordname = NULL; 2907 CFNumberRef cfzero = NULL; 2908 CFNumberRef cfone = NULL; 2909 2910 connection_fake_init2(&conn, &opbuf, ldap_pvt_thread_pool_context(), 0); 2911 fakeop = &opbuf.ob_op; 2912 fakeop->o_dn = fakeop->o_req_dn = *dn; 2913 dnNormalize(0, NULL, NULL, dn, &fakeop->o_ndn, NULL); 2914 fakeop->o_req_ndn = fakeop->o_ndn; 2915 fakeop->o_conn->c_listener->sl_url.bv_val = "ldapi://%2Fvar%2Frun%2Fldapi"; 2916 fakeop->o_conn->c_listener->sl_url.bv_len = strlen("ldapi://%2Fvar%2Frun%2Fldapi"); 2917 2918 usere = odusers_copy_entry(fakeop); 2919 if(!usere) { 2920 Debug(LDAP_DEBUG_ANY, "%s: Could not locate user record for %s\n", __func__, dn->bv_val, 0); 2921 goto out; 2922 } 2923 2924 recname = odusers_copy_recname(fakeop); 2925 if(!recname) { 2926 Debug(LDAP_DEBUG_ANY, "%s: Could not identify record name for %s\n", __func__, dn->bv_val, 0); 2927 goto out; 2928 } 2929 groupdnlen = asprintf(&groupstr, "cn=admin,cn=groups,%s", suffix); 2930 groupdn = ber_str2bv(groupstr, groupdnlen, 1, NULL); 2931 isAdmin = odusers_ismember(dn, groupdn); 2932 2933 if (groupstr) { 2934 free(groupstr); 2935 groupstr = NULL; 2936 } 2937 if(groupdn && !BER_BVISNULL(groupdn)) ber_bvfree(groupdn); 2938 2939 groupdnlen = asprintf(&groupstr, "cn=com.apple.access_disabled,cn=groups,%s", suffix); 2940 groupdn = ber_str2bv(groupstr, groupdnlen, 1, NULL); 2941 isDisabled = odusers_ismember(dn, groupdn); 2942 2943 int one = 1; 2944 int zero = 0; 2945 cfone = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &one); 2946 cfzero = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &zero); 2947 2948 cfrecordname = CFStringCreateWithCString(NULL, recname, kCFStringEncodingUTF8); 2949 2950 CFMutableDictionaryRef ret = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); 2951 if (strnstr(dn->bv_val, "cn=computer", dn->bv_len) == NULL) { 2952 CFDictionarySetValue(ret, kAPAttributeRecordType, kAPAttributeRecordTypeUser); 2953 } else { 2954 CFDictionarySetValue(ret, kAPAttributeRecordType, kAPAttributeRecordTypeComputer); 2955 } 2956 CFDictionarySetValue(ret,kAPAttributeRecordName, cfrecordname); 2957 CFDictionarySetValue(ret, kAPAttributeIsAdmin, isAdmin ? cfone : cfzero); 2958 CFDictionarySetValue(ret, kAPAttributeIsAdministrativelyDisabled, isDisabled ? cfone : cfzero); 2959 2960 userdict = ret; 2961 2962 usereUUID = attr_find( usere->e_attrs, slap_schema.si_ad_entryUUID ); 2963 if(!usereUUID) { 2964 Debug(LDAP_DEBUG_ANY, "%s: couldn't find usereUUID attribute for %s", __PRETTY_FUNCTION__, dn->bv_val, 0); 2965 goto out; 2966 } 2967 2968 authe = odusers_copy_authdata(dn); 2969 if(!authe) { 2970 Debug(LDAP_DEBUG_ANY, "%s: Could not locate authdata for %s\n", __func__, dn->bv_val, 0); 2971 goto out; 2972 } 2973 2974 if(!lastLoginAD && slap_str2ad("lastLoginTime", &lastLoginAD, &text) != 0) { 2975 Debug(LDAP_DEBUG_ANY, "%s: Unable to retrieve description of lastLoginTime attribute", __PRETTY_FUNCTION__, 0, 0); 2976 goto out; 2977 } 2978 if(!creationDateAD && slap_str2ad("creationDate", &creationDateAD, &text) != 0) { 2979 Debug(LDAP_DEBUG_ANY, "%s: Unable to retrieve description of creationDate attribute", __PRETTY_FUNCTION__, 0, 0); 2980 goto out; 2981 } 2982 if(!passModDateAD && slap_str2ad("passwordModDate", &passModDateAD, &text) != 0) { 2983 Debug(LDAP_DEBUG_ANY, "%s: Unable to retrieve description of passwordModDate attribute", __PRETTY_FUNCTION__, 0, 0); 2984 goto out; 2985 } 2986 if(!failedLoginsAD && slap_str2ad("loginFailedAttempts", &failedLoginsAD, &text) != 0) { 2987 Debug(LDAP_DEBUG_ANY, "%s: Unable to retrieve description of loginFailedAttempts attribute", __PRETTY_FUNCTION__, 0, 0); 2988 goto out; 2989 } 2990 if(!lastFailedLoginAD && slap_str2ad("lastFailedLoginTime", &lastFailedLoginAD, &text) != 0) { 2991 Debug(LDAP_DEBUG_ANY, "%s: Unable to retrieve description of lastLoginTime attribute", __PRETTY_FUNCTION__, 0, 0); 2992 goto out; 2993 } 2994 if(slap_str2ad("historyData", &historyDataAD, &text) != 0) { 2995 Debug(LDAP_DEBUG_ANY, "%s: Unable to retrieve description of historyData attribute", __PRETTY_FUNCTION__, 0, 0); 2996 goto out; 2997 } 2998 2999 /* kAPAttributeLastAuthenticationTime (lastLoginTime) */ 3000 lastLogin = attrs_find(authe->e_attrs, lastLoginAD); 3001 struct tm tmptm = {0}; 3002 time_t tmptime = 0; 3003 if(lastLogin && lastLogin->a_numvals && lastLogin->a_nvals[0].bv_len) { 3004 strptime(lastLogin->a_nvals[0].bv_val, "%Y%m%d%H%M%SZ", &tmptm); 3005 tmptime = timegm(&tmptm); 3006 CFNumberRef cftmptime = CFNumberCreate(kCFAllocatorDefault, kCFNumberLongType, &tmptime); 3007 CFDictionarySetValue(ret, kAPAttributeLastAuthenticationTime, cftmptime); 3008 CFRelease(cftmptime); 3009 } 3010 /* kAPAttributeLastPasswordChangeTime (passwordModDate) */ 3011 passModDate = attrs_find(authe->e_attrs, passModDateAD); 3012 memset(&tmptm, 0, sizeof(tmptm)); 3013 tmptime = 0; 3014 if(passModDate && passModDate->a_numvals && passModDate->a_nvals[0].bv_len) { 3015 strptime(passModDate->a_nvals[0].bv_val, "%Y%m%d%H%M%SZ", &tmptm); 3016 tmptime = timegm(&tmptm); 3017 CFNumberRef cftmptime = CFNumberCreate(kCFAllocatorDefault, kCFNumberLongType, &tmptime); 3018 CFDictionarySetValue(ret, kAPAttributeLastPasswordChangeTime, cftmptime); 3019 CFRelease(cftmptime); 3020 } 3021 /* kAPAttributeFailedAuthentications (loginFailedAttempts) */ 3022 failedLogins = attrs_find(authe->e_attrs, failedLoginsAD); 3023 if(failedLogins && failedLogins->a_numvals && failedLogins->a_nvals[0].bv_len) { 3024 long long tmpll; 3025 tmpll = strtoll(failedLogins->a_nvals[0].bv_val, NULL, 10); 3026 if( ((tmpll == LLONG_MAX) || (tmpll == LLONG_MIN)) && (errno == ERANGE) ) { 3027 tmpll = 0; 3028 } 3029 if( (tmpll > USHRT_MAX) || (tmpll < 0) ) { 3030 tmpll = 0; 3031 } 3032 loginattempts = tmpll; 3033 CFNumberRef cftmpnum = CFNumberCreate(kCFAllocatorDefault, kCFNumberLongType, &loginattempts); 3034 CFDictionarySetValue(ret, kAPAttributeFailedAuthentications, cftmpnum); 3035 CFRelease(cftmpnum); 3036 } 3037 /* kAPAttributeLastFailedAuthenticationTime (lastFailedLogin) */ 3038 lastFailedLogin = attrs_find(authe->e_attrs, lastFailedLoginAD); 3039 memset(&tmptm, 0, sizeof(tmptm)); 3040 tmptime = 0; 3041 if(lastFailedLogin && lastFailedLogin->a_numvals && lastFailedLogin->a_nvals[0].bv_len) { 3042 strptime(lastFailedLogin->a_nvals[0].bv_val, "%Y%m%d%H%M%SZ", &tmptm); 3043 tmptime = timegm(&tmptm); 3044 CFNumberRef cftmptime = CFNumberCreate(kCFAllocatorDefault, kCFNumberLongType, &tmptime); 3045 CFDictionarySetValue(ret, kAPAttributeLastFailedAuthenticationTime, cftmptime); 3046 CFRelease(cftmptime); 3047 } 3048 /* kAPAttributeCreationTime (creationDate) */ 3049 creationDate = attrs_find(authe->e_attrs, creationDateAD); 3050 memset(&tmptm, 0, sizeof(tmptm)); 3051 tmptime = 0; 3052 if(creationDate && creationDate->a_numvals && creationDate->a_nvals[0].bv_len) { 3053 strptime(creationDate->a_nvals[0].bv_val, "%Y%m%d%H%M%SZ", &tmptm); 3054 tmptime = timegm(&tmptm); 3055 CFNumberRef cftmptime = CFNumberCreate(kCFAllocatorDefault, kCFNumberLongType, &tmptime); 3056 CFDictionarySetValue(ret, kAPAttributeCreationTime, cftmptime); 3057 CFRelease(cftmptime); 3058 } 3059 /* kAPAttributeRecordPoliciesGUID */ 3060 userpolicyGUID = CFStringCreateWithCString(NULL, usereUUID->a_vals[0].bv_val, kCFStringEncodingUTF8); 3061 if (userpolicyGUID) { 3062 CFDictionarySetValue(ret, kAPAttributeRecordPoliciesGUID, userpolicyGUID); 3063 } 3064 /* kAPAttributeGlobalPoliciesGUID */ 3065 globalaccountpolicyGUID = odusers_copy_globalaccountpolicyGUID(); 3066 if (globalaccountpolicyGUID) { 3067 CFDictionarySetValue(ret, kAPAttributeGlobalPoliciesGUID, globalaccountpolicyGUID); 3068 } 3069 3070 /* kAPAttributePasswordHistory */ 3071 historyData = attrs_find(authe->e_attrs, historyDataAD); 3072 if (historyData) { 3073 char historyArray[sizeof(heim_CRAM_MD5_STATE)*16] = {0}; 3074 heim_CRAM_MD5_STATE empytcrammd5state = {0}; 3075 CFMutableArrayRef historyDataCFArray = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); 3076 bzero(&empytcrammd5state, sizeof(heim_CRAM_MD5_STATE)); 3077 memcpy(historyArray, historyData->a_nvals[0].bv_val, historyData->a_nvals[0].bv_len); 3078 for (int i = 0; i < 16; i++) { 3079 if (memcmp(&empytcrammd5state, historyArray, sizeof(heim_CRAM_MD5_STATE)) != 0) { 3080 CFDataRef hash = CFDataCreate(NULL, (UInt8*)historyArray+(i* sizeof(heim_CRAM_MD5_STATE)), sizeof(heim_CRAM_MD5_STATE)); 3081 if (hash) { 3082 CFArrayAppendValue(historyDataCFArray, hash); 3083 CFRelease(hash); 3084 } 3085 } 3086 } 3087 CFDictionaryAddValue(userdict, kAPAttributePasswordHistory, historyDataCFArray); 3088 CFRelease(historyDataCFArray); 3089 } 3090 3091out: 3092 if(cfone) CFRelease(cfone); 3093 if(cfzero) CFRelease(cfzero); 3094 if(usere) entry_free(usere); 3095 if(authe) entry_free(authe); 3096 if(recname) ch_free(recname); 3097 if(fakeop && !BER_BVISNULL(&fakeop->o_ndn)) ch_free(fakeop->o_ndn.bv_val); 3098 if (groupstr) free(groupstr); 3099 if(groupdn && !BER_BVISNULL(groupdn)) ber_bvfree(groupdn); 3100 if (cfrecordname) CFRelease(cfrecordname); 3101 if (userpolicyGUID) CFRelease(userpolicyGUID); 3102 if (globalaccountpolicyGUID) CFRelease(globalaccountpolicyGUID); 3103 ch_free(suffix); 3104 3105 return userdict; 3106} 3107 3108int odusers_joingroup(const char *group, struct berval *dn, bool remove) { 3109 int ret = 0; 3110 char *suffix = NULL; 3111 struct berval groupdn; 3112 OperationBuffer opbuf; 3113 Connection conn; 3114 Operation *fakeop = NULL; 3115 Entry *usere = NULL; 3116 Attribute *guidattr = NULL; 3117 Attribute *nameattr = NULL; 3118 char *guid = NULL; 3119 char *tmpptr1 = NULL; 3120 char *tmpptr2 = NULL; 3121 char *name = NULL; 3122 Modifications *mod = NULL; 3123 SlapReply rs = {REP_RESULT}; 3124 3125 suffix = odusers_copy_suffix(); 3126 if(!suffix) { 3127 Debug(LDAP_DEBUG_ANY, "%s: Could not find default naming context\n", __func__, 0, 0); 3128 goto out; 3129 } 3130 3131 groupdn.bv_len = asprintf(&groupdn.bv_val, "cn=%s,cn=groups,%s", group, suffix); 3132 3133 connection_fake_init2(&conn, &opbuf, ldap_pvt_thread_pool_context(), 0); 3134 fakeop = &opbuf.ob_op; 3135 fakeop->o_dn = fakeop->o_ndn = *dn; 3136 fakeop->o_req_dn = fakeop->o_req_ndn = *dn; 3137 fakeop->o_conn->c_listener->sl_url.bv_val = "ldapi://%2Fvar%2Frun%2Fldapi"; 3138 fakeop->o_conn->c_listener->sl_url.bv_len = strlen("ldapi://%2Fvar%2Frun%2Fldapi"); 3139 3140 usere = odusers_copy_entry(fakeop); 3141 if(!usere) { 3142 Debug(LDAP_DEBUG_ANY, "%s: could not locate %s\n", __func__, dn->bv_val, 0); 3143 goto out; 3144 } 3145 3146 if(idattr_uuid == NULL) { 3147 int rc; 3148 const char *text = NULL; 3149 rc = slap_str2ad( "apple-generateduid", &idattr_uuid, &text ); 3150 if(rc != LDAP_SUCCESS) { 3151 Debug(LDAP_DEBUG_ANY, "%s: Unable to lookup attribute description for apple-generateduid\n", __func__, 0, 0); 3152 goto out; 3153 } 3154 } 3155 3156 guidattr = attr_find(usere->e_attrs, idattr_uuid); 3157 if(!guidattr) { 3158 Debug(LDAP_DEBUG_ANY, "%s: could not locate apple-generateduid attribute for %s\n", __func__, dn->bv_val, 0); 3159 goto out; 3160 } 3161 3162 if(guidattr->a_numvals < 1) { 3163 Debug(LDAP_DEBUG_ANY, "%s: no values associated with apple-generateduid for %s\n", __func__, dn->bv_val, 0); 3164 goto out; 3165 } 3166 3167 guid = ch_calloc(1, guidattr->a_nvals[0].bv_len + 1); 3168 if(!guid) { 3169 Debug(LDAP_DEBUG_ANY, "%s: Could not locate apple-generateduid for %s\n", __func__, dn->bv_val, 0); 3170 goto out; 3171 } 3172 memcpy(guid, guidattr->a_nvals[0].bv_val, guidattr->a_nvals[0].bv_len); 3173 3174 tmpptr1 = strnstr(dn->bv_val, "=", dn->bv_len); 3175 if(!tmpptr1) { 3176 Debug(LDAP_DEBUG_ANY, "%s: Could not parse dn: %s\n", __func__, dn->bv_val, 0); 3177 goto out; 3178 } 3179 tmpptr1++; 3180 tmpptr2 = strnstr(dn->bv_val, ",", dn->bv_len); 3181 if(!tmpptr2) { 3182 Debug(LDAP_DEBUG_ANY, "%s: Could not parse dn: %s\n", __func__, dn->bv_val, 0); 3183 goto out; 3184 } 3185 name = ch_calloc(1, (tmpptr2 - tmpptr1) + 1); 3186 memcpy(name, tmpptr1, (tmpptr2 - tmpptr1)); 3187 3188 if(idattr_memberships == NULL) { 3189 int rc; 3190 const char *text = NULL; 3191 rc = slap_str2ad( "apple-group-memberguid", &idattr_memberships, &text ); 3192 if(rc != LDAP_SUCCESS) { 3193 Debug(LDAP_DEBUG_ANY, "%s: Unable to lookup attribute description for apple-group-memberguid\n", __func__, 0, 0); 3194 goto out; 3195 } 3196 } 3197 3198 if(idattr_memberUid == NULL) { 3199 int rc; 3200 const char *text = NULL; 3201 rc = slap_str2ad( "memberUid", &idattr_memberUid, &text ); 3202 if(rc != LDAP_SUCCESS) { 3203 Debug(LDAP_DEBUG_ANY, "%s: Unable to lookup attribute description for memberUid\n", __func__, 0, 0); 3204 goto out; 3205 } 3206 } 3207 3208 mod = (Modifications *) ch_calloc(1, sizeof(Modifications)); 3209 mod->sml_op = remove ? LDAP_MOD_DELETE : LDAP_MOD_ADD; 3210 mod->sml_flags = 0; 3211 mod->sml_type = idattr_memberships->ad_cname; 3212 mod->sml_values = (struct berval*) ch_calloc(2, sizeof(struct berval)); 3213 mod->sml_values[0].bv_val = ch_strdup(guid); 3214 mod->sml_values[0].bv_len = strlen(guid); 3215 mod->sml_values[1].bv_val = NULL; 3216 mod->sml_values[1].bv_len = 0; 3217 mod->sml_numvals = 1; 3218 mod->sml_nvalues = (struct berval*) ch_calloc(2, sizeof(struct berval)); 3219 mod->sml_nvalues[0].bv_val = ch_strdup(guid); 3220 mod->sml_nvalues[0].bv_len = strlen(guid); 3221 mod->sml_nvalues[1].bv_val = NULL; 3222 mod->sml_nvalues[1].bv_len = 0; 3223 mod->sml_desc = idattr_memberships; 3224 mod->sml_next = NULL; 3225 3226 fakeop->orm_modlist = mod; 3227 3228 mod->sml_next = (Modifications *) ch_calloc(1, sizeof(Modifications)); 3229 mod = mod->sml_next; 3230 3231 mod->sml_op = remove ? LDAP_MOD_DELETE : LDAP_MOD_ADD; 3232 mod->sml_flags = 0; 3233 mod->sml_type = idattr_memberUid->ad_cname; 3234 mod->sml_values = (struct berval*) ch_calloc(2, sizeof(struct berval)); 3235 mod->sml_values[0].bv_val = ch_strdup(name); 3236 mod->sml_values[0].bv_len = strlen(name); 3237 mod->sml_values[1].bv_val = NULL; 3238 mod->sml_values[1].bv_len = 0; 3239 mod->sml_numvals = 1; 3240 mod->sml_nvalues = (struct berval*) ch_calloc(2, sizeof(struct berval)); 3241 mod->sml_nvalues[0].bv_val = ch_strdup(name); 3242 mod->sml_nvalues[0].bv_len = strlen(name); 3243 mod->sml_nvalues[1].bv_val = NULL; 3244 mod->sml_nvalues[1].bv_len = 0; 3245 mod->sml_desc = idattr_memberUid; 3246 3247 fakeop->o_dn = fakeop->o_ndn = groupdn; 3248 fakeop->o_req_dn = fakeop->o_req_ndn = groupdn; 3249 3250 fakeop->o_tag = LDAP_REQ_MODIFY; 3251 fakeop->o_protocol = LDAP_VERSION3; 3252 fakeop->o_bd = frontendDB; 3253 fakeop->o_bd->be_modify(fakeop, &rs); 3254 if(rs.sr_err != LDAP_SUCCESS) { 3255 // If removing and the user has already been removed, 3256 // LDAP_NO_SUCH_ATTRIBUTE will be returned, and that's not 3257 // considered an error here. 3258 // If adding a user and the user is already a member, 3259 // LDAP_TYPE_OR_VALUE_EXISTS will be returned, and that's not 3260 // considered an error here. 3261 if((remove && (rs.sr_err != LDAP_NO_SUCH_ATTRIBUTE)) || (!remove && (rs.sr_err != LDAP_TYPE_OR_VALUE_EXISTS))) { 3262 Debug(LDAP_DEBUG_ANY, "Unable to update group membership of group %s for %s: 0x%x\n", group, dn->bv_val, rs.sr_err); 3263 goto out; 3264 } 3265 } 3266 3267out: 3268 if(!BER_BVISNULL(&groupdn)) ch_free(groupdn.bv_val); 3269 ch_free(suffix); 3270 ch_free(name); 3271 ch_free(guid); 3272 if(usere) entry_free(usere); 3273 if(fakeop && fakeop->orm_modlist) slap_mods_free(fakeop->orm_modlist, 1); 3274 return ret; 3275} 3276 3277int odusers_accountpolicy_override(struct berval *account_dn) { 3278 int match = 0; 3279 int rc = 0; 3280 const char *text; 3281 3282 if ( cf_accountpolicy_override ) { 3283 rc = value_match( &match, slap_schema.si_ad_entryDN, slap_schema.si_ad_entryDN->ad_type->sat_equality, 0, account_dn, cf_accountpolicy_override, &text ); 3284 } 3285 3286 Debug(LDAP_DEBUG_TRACE, "odusers_accountpolicy_override: match(%d) rc(%d) dn(%s)\n", match, rc, account_dn->bv_val); 3287 return ( rc == LDAP_SUCCESS && match == 0 ); 3288} 3289