#include "portable.h"
#include <ac/string.h>
#include <ac/ctype.h>
#include "slap.h"
#include "ldif.h"
#include "config.h"
#define __COREFOUNDATION_CFFILESECURITY__
#include <CoreFoundation/CoreFoundation.h>
#include <HeimODAdmin/HeimODAdmin.h>
#include <Heimdal/krb5.h>
#include <CommonAuth/CommonAuth.h>
#include <SystemConfiguration/SystemConfiguration.h>
#include "applehelpers.h"
#include <sys/types.h>
#include <sys/socket.h>
#include <ifaddrs.h>
#include <arpa/inet.h>
#include <PasswordServer/AuthDBFileDefs.h>
#include <PasswordServer/AuthFile.h>
static Filter generic_filter = { LDAP_FILTER_PRESENT, { 0 }, NULL };
static struct berval generic_filterstr = BER_BVC("(objectclass=*)");
static AttributeDescription *failedLoginsAD = NULL;
static AttributeDescription *creationDateAD = NULL;
static AttributeDescription *passModDateAD = NULL;
static AttributeDescription *lastLoginAD = NULL;
static AttributeDescription *disableReasonAD = NULL;
static AttributeDescription *realnameAD = NULL;
static AttributeDescription *pwslocAD = NULL;
AttributeDescription *passwordRequiredDateAD = NULL;
AttributeDescription *policyAD = NULL;
static int odusers_lookup(Operation *op, SlapReply *rs) {
if(rs->sr_type != REP_SEARCH) return 0;
if(rs->sr_err != LDAP_SUCCESS) {
Debug(LDAP_DEBUG_ANY, "%s: Unable to locate entry (%d)\n", __PRETTY_FUNCTION__, rs->sr_err, 0);
return -1;
}
if(rs->sr_un.sru_search.r_entry) {
*(Entry**)(op->o_callback->sc_private) = entry_dup(rs->sr_un.sru_search.r_entry);
return 0;
}
return -1;
}
Entry *odusers_copy_entry(Operation *op) {
Entry *e = NULL;
SlapReply rs = {REP_RESULT};
slap_callback cb = {NULL, odusers_lookup, NULL, NULL};
if(!op) {
Debug(LDAP_DEBUG_TRACE, "%s: no operation to perform\n", __PRETTY_FUNCTION__, 0, 0);
return NULL;
}
op->o_bd = select_backend(&op->o_req_ndn, 1);
if(!op->o_bd) {
Debug(LDAP_DEBUG_TRACE, "%s: could not find backend for: %s\n", __PRETTY_FUNCTION__, op->o_req_ndn.bv_val, 0);
return NULL;
}
generic_filter.f_desc = slap_schema.si_ad_objectClass;
op->o_do_not_cache = 1;
slap_op_time(&op->o_time, &op->o_tincr);
op->o_tag = LDAP_REQ_SEARCH;
op->ors_scope = LDAP_SCOPE_BASE;
op->ors_deref = LDAP_DEREF_NEVER;
op->ors_tlimit = SLAP_NO_LIMIT;
op->ors_slimit = 1;
op->ors_filter = &generic_filter;
op->ors_filterstr = generic_filterstr;
op->ors_attrs = NULL;
cb.sc_private = &e;
op->o_callback = &cb;
op->o_bd->be_search(op, &rs);
if(rs.sr_err != LDAP_SUCCESS) {
Debug(LDAP_DEBUG_TRACE, "%s: Unable to locate %s (%d)\n", __PRETTY_FUNCTION__, op->o_req_ndn.bv_val, rs.sr_err);
return NULL;
}
return e;
}
int odusers_remove_authdata(char *slotid) {
OperationBuffer opbuf;
Operation *op;
Connection conn = {0};
Entry *e = NULL;
SlapReply rs = {REP_RESULT};
slap_callback cb = {NULL, odusers_lookup, NULL, NULL};
struct berval dn;
int ret = -1;
dn.bv_val = NULL;
dn.bv_len = asprintf(&dn.bv_val, "authGUID=%s,cn=users,cn=authdata", slotid);
memset(&opbuf, 0, sizeof(opbuf));
memset(&conn, 0, sizeof(conn));
connection_fake_init2(&conn, &opbuf, ldap_pvt_thread_pool_context(), 0);
op = &opbuf.ob_op;
op->o_dn = op->o_ndn = op->o_req_dn = op->o_req_ndn = dn;
op->o_bd = frontendDB;
slap_op_time(&op->o_time, &op->o_tincr);
op->o_tag = LDAP_REQ_DELETE;
op->o_conn->c_listener->sl_url.bv_val = "ldapi://%2Fvar%2Frun%2Fldapi";
op->o_conn->c_listener->sl_url.bv_len = strlen("ldapi://%2Fvar%2Frun%2Fldapi");
op->o_bd->be_delete(op, &rs);
if(rs.sr_err != LDAP_SUCCESS) {
Debug(LDAP_DEBUG_ANY, "%s: Unable to delete %s (%d)\n", __PRETTY_FUNCTION__, dn.bv_val, rs.sr_err);
goto out;
}
ret = 0;
out:
if(dn.bv_val) free(dn.bv_val);
return ret;
}
int odusers_get_authguid(Entry *e, char *guidstr) {
int ret = -1;
AttributeDescription *aa = slap_schema.si_ad_authAuthority;
Attribute *a = attr_find(e->e_attrs, aa);
if(!a) {
Debug(LDAP_DEBUG_ANY, "%s: could not locate authAuthority attribute for: %s\n", __PRETTY_FUNCTION__, e->e_dn, 0);
goto out;
}
int i;
for(i = 0; i < a->a_numvals; i++) {
if(memcmp(a->a_vals[i].bv_val, ";ApplePasswordServer;", 21) == 0) {
strncpy(guidstr, a->a_vals[i].bv_val+23, 8);
guidstr[8] = '-';
strncpy(guidstr+9, a->a_vals[i].bv_val+31, 4);
guidstr[13] = '-';
strncpy(guidstr+14, a->a_vals[i].bv_val+35, 4);
guidstr[18] = '-';
strncpy(guidstr+19, a->a_vals[i].bv_val+39, 4);
guidstr[23] = '-';
strncpy(guidstr+24, a->a_vals[i].bv_val+43, 12);
guidstr[36] = '\0';
break;
}
}
if(guidstr[0] == '\0') {
Debug(LDAP_DEBUG_ANY, "%s: error parsing slotid", __PRETTY_FUNCTION__, 0, 0);
goto out;
}
ret = 0;
out:
return ret;
}
Entry *odusers_copy_authdata(struct berval *dn) {
OperationBuffer opbuf;
Connection conn;
Operation *fakeop = NULL;
Entry *e = NULL;
Entry *ret = NULL;
char guidstr[37];
struct berval authdn;
connection_fake_init2(&conn, &opbuf, ldap_pvt_thread_pool_context(), 0);
fakeop = &opbuf.ob_op;
fakeop->o_dn = *dn;
dnNormalize(0, NULL, NULL, dn, &fakeop->o_ndn, NULL);
fakeop->o_req_dn = *dn;
dnNormalize(0, NULL, NULL, dn, &fakeop->o_req_ndn, NULL);
e = odusers_copy_entry(fakeop);
if(!e) {
Debug(LDAP_DEBUG_TRACE, "%s: No entry associated with %s\n", __PRETTY_FUNCTION__, fakeop->o_req_ndn.bv_val, 0);
goto out;
}
if( odusers_get_authguid(e, guidstr) != 0) {
Debug(LDAP_DEBUG_TRACE, "%s: Could not locate authguid for record %s", __PRETTY_FUNCTION__, dn->bv_val, 0);
goto out;
}
entry_free(e);
e = NULL;
authdn.bv_len = asprintf(&authdn.bv_val, "authGUID=%s,cn=users,cn=authdata", guidstr);
if(!BER_BVISNULL(&fakeop->o_ndn)) ch_free(fakeop->o_ndn.bv_val);
if(!BER_BVISNULL(&fakeop->o_req_ndn)) ch_free(fakeop->o_req_ndn.bv_val);
fakeop->o_dn = fakeop->o_req_dn = authdn;
dnNormalize(0, NULL, NULL, &fakeop->o_dn, &fakeop->o_ndn, NULL);
dnNormalize(0, NULL, NULL, &fakeop->o_req_dn, &fakeop->o_req_ndn, NULL);
fakeop->o_conn->c_listener->sl_url.bv_val = "ldapi://%2Fvar%2Frun%2Fldapi";
fakeop->o_conn->c_listener->sl_url.bv_len = strlen("ldapi://%2Fvar%2Frun%2Fldapi");
ret = odusers_copy_entry(fakeop);
free(authdn.bv_val);
out:
if(fakeop && !BER_BVISNULL(&fakeop->o_ndn)) ch_free(fakeop->o_ndn.bv_val);
if(fakeop && !BER_BVISNULL(&fakeop->o_req_ndn)) ch_free(fakeop->o_req_ndn.bv_val);
if(e) entry_free(e);
return ret;
}
Attribute *odusers_copy_globalpolicy(void) {
OperationBuffer opbuf;
Connection conn;
Operation *fakeop = NULL;
Entry *e = NULL;
struct berval policydn;
Attribute *ret = NULL;
connection_fake_init2(&conn, &opbuf, ldap_pvt_thread_pool_context(), 0);
policydn.bv_val = "cn=access,cn=authdata";
policydn.bv_len = strlen(policydn.bv_val);
fakeop = &opbuf.ob_op;
fakeop->o_dn = fakeop->o_ndn = policydn;
fakeop->o_req_dn = fakeop->o_req_ndn = policydn;
fakeop->o_conn->c_listener->sl_url.bv_val = "ldapi://%2Fvar%2Frun%2Fldapi";
fakeop->o_conn->c_listener->sl_url.bv_len = strlen("ldapi://%2Fvar%2Frun%2Fldapi");
e = odusers_copy_entry(fakeop);
if(!e) goto out;
Attribute *a;
for(a = e->e_attrs; a; a = a->a_next) {
if(strncmp(a->a_desc->ad_cname.bv_val, "apple-user-passwordpolicy", a->a_desc->ad_cname.bv_len) == 0) {
ret = attr_dup(a);
}
}
out:
if(e) entry_free(e);
return ret;
}
static Attribute *odusers_copy_passwordRequiredDate(void) {
OperationBuffer opbuf;
Connection conn;
Operation *fakeop = NULL;
Entry *e = NULL;
struct berval policydn;
Attribute *ret = NULL;
connection_fake_init2(&conn, &opbuf, ldap_pvt_thread_pool_context(), 0);
policydn.bv_val = "cn=access,cn=authdata";
policydn.bv_len = strlen(policydn.bv_val);
fakeop = &opbuf.ob_op;
fakeop->o_dn = fakeop->o_ndn = policydn;
fakeop->o_req_dn = fakeop->o_req_ndn = policydn;
fakeop->o_conn->c_listener->sl_url.bv_val = "ldapi://%2Fvar%2Frun%2Fldapi";
fakeop->o_conn->c_listener->sl_url.bv_len = strlen("ldapi://%2Fvar%2Frun%2Fldapi");
e = odusers_copy_entry(fakeop);
if(!e) goto out;
Attribute *a;
for(a = e->e_attrs; a; a = a->a_next) {
if(strncmp(a->a_desc->ad_cname.bv_val, "passwordRequiredDate", a->a_desc->ad_cname.bv_len) == 0) {
ret = attr_dup(a);
}
}
out:
if(e) entry_free(e);
return ret;
}
CFMutableDictionaryRef CopyPolicyToDict(const char *policyplist, int len) {
CFDataRef xmlData = NULL;
CFMutableDictionaryRef ret = NULL;
CFErrorRef err = NULL;
xmlData = CFDataCreate(kCFAllocatorDefault, (const unsigned char*)policyplist, len);
if(!xmlData) goto out;
ret = (CFMutableDictionaryRef)CFPropertyListCreateWithData(kCFAllocatorDefault, xmlData, kCFPropertyListMutableContainersAndLeaves, NULL, &err);
if(!ret) goto out;
out:
if(xmlData) CFRelease(xmlData);
return ret;
}
static void MergePolicyIntValue(CFDictionaryRef global, CFDictionaryRef user, CFMutableDictionaryRef merged, CFStringRef policy, int defaultvalue) {
unsigned int tmpbool = 0;
CFNumberRef anumber = CFDictionaryGetValue(user, policy);
if(anumber) {
CFNumberGetValue(anumber, kCFNumberIntType, &tmpbool);
if(tmpbool == defaultvalue) {
anumber = CFDictionaryGetValue(global, policy);
if(anumber) CFNumberGetValue(anumber, kCFNumberIntType, &tmpbool);
if(tmpbool) {
CFDictionarySetValue(merged, policy, anumber);
}
}
} else {
anumber = CFDictionaryGetValue(global, policy);
if(anumber) {
CFNumberGetValue(anumber, kCFNumberIntType, &tmpbool);
CFDictionarySetValue(merged, policy, anumber);
}
}
return;
}
static CFMutableDictionaryRef CopyEffectivePolicy(CFDictionaryRef global, CFDictionaryRef user) {
CFMutableDictionaryRef ret = CFDictionaryCreateMutableCopy(kCFAllocatorDefault, 0, user);
MergePolicyIntValue(global, user, ret, CFSTR("isDisabled"), 0);
MergePolicyIntValue(global, user, ret, CFSTR("isAdminUser"), 0);
MergePolicyIntValue(global, user, ret, CFSTR("newPasswordRequired"), 0);
MergePolicyIntValue(global, user, ret, CFSTR("canModifyPasswordforSelf"), 1);
MergePolicyIntValue(global, user, ret, CFSTR("usingExpirationDate"), 0);
MergePolicyIntValue(global, user, ret, CFSTR("usingHardExpirationDate"), 0);
MergePolicyIntValue(global, user, ret, CFSTR("notGuessablePattern"), 0);
MergePolicyIntValue(global, user, ret, CFSTR("isSessionKeyAgent"), 0);
MergePolicyIntValue(global, user, ret, CFSTR("isComputerAccount"), 0);
MergePolicyIntValue(global, user, ret, CFSTR("requiresMixedCase"), 0);
MergePolicyIntValue(global, user, ret, CFSTR("requiresSymbol"), 0);
MergePolicyIntValue(global, user, ret, CFSTR("requiresAlpha"), 0);
MergePolicyIntValue(global, user, ret, CFSTR("requiresNumeric"), 0);
MergePolicyIntValue(global, user, ret, CFSTR("passwordCannotBeName"), 0);
MergePolicyIntValue(global, user, ret, CFSTR("maxMinutesUntilChangePassword"), 0);
MergePolicyIntValue(global, user, ret, CFSTR("maxMinutesUntilDisabled"), 0);
MergePolicyIntValue(global, user, ret, CFSTR("maxMinutesOfNonUse"), 0);
MergePolicyIntValue(global, user, ret, CFSTR("maxFailedLoginAttempts"), 0);
MergePolicyIntValue(global, user, ret, CFSTR("minChars"), 0);
MergePolicyIntValue(global, user, ret, CFSTR("maxChars"), 0);
MergePolicyIntValue(global, user, ret, CFSTR("usingHistory"), 0);
unsigned int tmpint = 0;
unsigned int tmpshort = 0;
CFNumberRef anumber = CFDictionaryGetValue(user, CFSTR("usingExpirationDate"));
if(anumber) CFNumberGetValue(anumber, kCFNumberIntType, &tmpint);
if(!tmpint) {
anumber = CFDictionaryGetValue(global, CFSTR("usingExpirationDate"));
if(anumber) CFNumberGetValue(anumber, kCFNumberIntType, &tmpint);
if(tmpint) {
CFDateRef expdate = CFDictionaryGetValue(global, CFSTR("expirationDateGMT"));
CFDictionarySetValue(ret, CFSTR("usingExpirationDate"), anumber);
CFDictionarySetValue(ret, CFSTR("expirationDateGMT"), expdate);
}
}
anumber = CFDictionaryGetValue(user, CFSTR("usingHardExpirationDate"));
if(anumber) CFNumberGetValue(anumber, kCFNumberIntType, &tmpint);
if(!tmpint) {
anumber = CFDictionaryGetValue(global, CFSTR("usingHardExpirationDate"));
if(anumber) CFNumberGetValue(anumber, kCFNumberIntType, &tmpint);
if(tmpint) {
CFDateRef expdate = CFDictionaryGetValue(global, CFSTR("hardExpireDateGMT"));
CFDictionarySetValue(ret, CFSTR("hardExpireDateGMT"), expdate);
CFDictionarySetValue(ret, CFSTR("usingHardExpirationDate"), anumber);
}
}
return ret;
}
static int GetDisabledStatus(CFMutableDictionaryRef policy, CFDateRef ctime, CFDateRef lastLogin, CFDateRef passModDate, uint16_t *failedattempts, int disableReason) {
int ret = 0;
bool setToDisabled = false;
short tmpshort = 0;
long tmplong = 0;
CFAbsoluteTime tmptime = 0;
CFNumberRef isadmin = CFDictionaryGetValue(policy, CFSTR("isAdminUser"));
if(isadmin) CFNumberGetValue(isadmin, kCFNumberShortType, &tmpshort);
if(tmpshort != 0) {
int zero = 0;
CFNumberRef cfzero = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &zero);
CFDictionarySetValue(policy, CFSTR("newPasswordRequired"), cfzero);
CFRelease(cfzero);
return 0;
}
CFNumberRef iscomputer = CFDictionaryGetValue(policy, CFSTR("isComputer"));
if(iscomputer) CFNumberGetValue(iscomputer, kCFNumberShortType, &tmpshort);
if(tmpshort != 0) return 0;
CFDateRef now = CFDateCreate(kCFAllocatorDefault, CFAbsoluteTimeGetCurrent());
CFNumberRef isdisabled = CFDictionaryGetValue(policy, CFSTR("isDisabled"));
if(isdisabled) CFNumberGetValue(isdisabled, kCFNumberShortType, &tmpshort);
if(tmpshort) {
CFDateRef validAfter = CFDictionaryGetValue(policy, CFSTR("validAfter"));
ret = kDisabledByAdmin;
if(validAfter) {
if(CFDateCompare(validAfter, now, NULL) == kCFCompareLessThan) {
if(disableReason == kDisabledInactive) {
int zero = 0;
CFNumberRef cfzero = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &zero);
CFDictionarySetValue(policy, CFSTR("isDisabled"), cfzero);
CFRelease(cfzero);
ret = 0;
}
}
}
}
if(ret) goto out;
CFNumberRef maxFailedLogins = CFDictionaryGetValue(policy, CFSTR("maxFailedLoginAttempts"));
if(maxFailedLogins) CFNumberGetValue(maxFailedLogins, kCFNumberShortType, &tmpshort);
if(tmpshort > 0) {
if(*failedattempts >= tmpshort) {
*failedattempts = 0;
ret = kDisabledTooManyFailedLogins;
Debug(LDAP_DEBUG_TRACE, "%s: disabling due to failed logins", __PRETTY_FUNCTION__, 0, 0);
goto out;
}
}
tmpshort = 0;
CFNumberRef usingHardExpire = CFDictionaryGetValue(policy, CFSTR("usingHardExpirationDate"));
if(usingHardExpire) CFNumberGetValue(usingHardExpire, kCFNumberShortType, &tmpshort);
if(tmpshort) {
CFTypeRef hardExpire = CFDictionaryGetValue(policy, CFSTR("hardExpireDateGMT"));
if(hardExpire) {
if(CFGetTypeID(hardExpire) == CFDateGetTypeID()) {
if(CFDateCompare(hardExpire, now, NULL) != kCFCompareGreaterThan) {
ret = kDisabledExpired;
Debug(LDAP_DEBUG_TRACE, "%s: disabling due to hard expire", __PRETTY_FUNCTION__, 0, 0);
goto out;
}
} else if(CFGetTypeID(hardExpire) == CFNumberGetTypeID()) {
CFNumberGetValue(hardExpire, kCFNumberLongType, &tmplong);
CFDateRef tmpcfdate = CFDateCreate(kCFAllocatorDefault, tmplong - kCFAbsoluteTimeIntervalSince1970);
if(CFDateCompare(tmpcfdate, now, NULL) != kCFCompareGreaterThan) {
if(tmpcfdate) CFRelease(tmpcfdate);
ret = kDisabledExpired;
Debug(LDAP_DEBUG_TRACE, "%s: disabling due to hard expire", __PRETTY_FUNCTION__, 0, 0);
goto out;
}
if(tmpcfdate) CFRelease(tmpcfdate);
}
}
}
tmpshort = tmplong = 0;
CFNumberRef maxMinutesUntilDisabled = CFDictionaryGetValue(policy, CFSTR("maxMinutesUntilDisabled"));
if(maxMinutesUntilDisabled) CFNumberGetValue(maxMinutesUntilDisabled, kCFNumberLongType, &tmplong);
if(tmplong > 0) {
tmptime = CFDateGetAbsoluteTime(ctime);
if( ((CFAbsoluteTimeGetCurrent() - tmptime)/60) > tmplong ) {
Debug(LDAP_DEBUG_TRACE, "%s: disabling due to max minutes expired", __PRETTY_FUNCTION__, 0, 0);
ret = kDisabledExpired;
goto out;
}
}
if(lastLogin) {
tmplong = 0;
CFNumberRef maxMinutesOfNonUse = CFDictionaryGetValue(policy, CFSTR("maxMinutesOfNonUse"));
if(maxMinutesOfNonUse) CFNumberGetValue(maxMinutesOfNonUse, kCFNumberLongType, &tmplong);
if(tmplong > 0) {
tmptime = CFDateGetAbsoluteTime(lastLogin);
if( ((CFAbsoluteTimeGetCurrent() - tmptime)/60) > tmplong ) {
Debug(LDAP_DEBUG_TRACE, "%s: disabling due to max minutes of non use", __PRETTY_FUNCTION__, 0, 0);
ret = kDisabledInactive;
goto out;
}
}
}
CFDateRef validAfter = CFDictionaryGetValue(policy, CFSTR("validAfter"));
if(validAfter) {
if(CFDateCompare(validAfter, now, NULL) == kCFCompareGreaterThan) {
Debug(LDAP_DEBUG_TRACE, "%s: disabling due to validafter", __PRETTY_FUNCTION__, 0, 0);
ret = kDisabledInactive;
goto out;
}
}
tmpshort = tmplong = 0;
CFNumberRef maxMinutesUntilChangePassword = CFDictionaryGetValue(policy, CFSTR("maxMinutesUntilChangePassword"));
if(maxMinutesUntilChangePassword) CFNumberGetValue(maxMinutesUntilChangePassword, kCFNumberLongType, &tmplong);
if(tmplong > 0) {
tmptime = CFDateGetAbsoluteTime(passModDate);
if( ((CFAbsoluteTimeGetCurrent() - tmptime)/60) > tmplong ) {
Debug(LDAP_DEBUG_TRACE, "%s: disabling due password expiration", __func__, 0, 0);
if(!ret) ret = kDisabledNewPasswordRequired;
}
}
tmpshort = 0;
CFNumberRef newPasswordRequired = CFDictionaryGetValue(policy, CFSTR("newPasswordRequired"));
if(newPasswordRequired) CFNumberGetValue(newPasswordRequired, kCFNumberShortType, &tmpshort);
if(tmpshort > 0) {
if(!ret) ret = kDisabledNewPasswordRequired;
}
out:
if(ret != 0) {
int one = 1;
CFNumberRef cfone = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &one);
CFDictionarySetValue(policy, CFSTR("isDisabled"), cfone);
CFRelease(cfone);
}
CFNumberRef cfdisableReason = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &ret);
CFDictionarySetValue(policy, CFSTR("effectiveDisableReason"), cfdisableReason);
if(cfdisableReason) CFRelease(cfdisableReason);
if(now) CFRelease(now);
return ret;
}
CFDictionaryRef odusers_copydefaultuserpolicy(void) {
CFMutableDictionaryRef ret = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
int one = 1;
int zero = 0;
CFNumberRef cfone = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &one);
CFNumberRef cfzero = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &zero);
CFDictionarySetValue(ret, CFSTR("isDisabled"), cfzero);
CFDictionarySetValue(ret, CFSTR("isAdminUser"), cfzero);
CFDictionarySetValue(ret, CFSTR("newPasswordRequired"), cfzero);
CFDictionarySetValue(ret, CFSTR("usingHistory"), cfzero);
CFDictionarySetValue(ret, CFSTR("canModifyPasswordforSelf"), cfone);
CFDictionarySetValue(ret, CFSTR("usingExpirationDate"), cfzero);
CFDictionarySetValue(ret, CFSTR("usingHardExpirationDate"), cfzero);
CFDictionarySetValue(ret, CFSTR("requiresAlpha"), cfzero);
CFDictionarySetValue(ret, CFSTR("requiresNumeric"), cfzero);
CFDictionarySetValue(ret, CFSTR("maxMinutesUntilChangePassword"), cfzero);
CFDictionarySetValue(ret, CFSTR("maxMinutesUntilDisabled"), cfzero);
CFDictionarySetValue(ret, CFSTR("maxMinutesOfNonUse"), cfzero);
CFDictionarySetValue(ret, CFSTR("maxFailedLoginAttempts"), cfzero);
CFDictionarySetValue(ret, CFSTR("minChars"), cfzero);
CFDictionarySetValue(ret, CFSTR("maxChars"), cfzero);
CFDictionarySetValue(ret, CFSTR("passwordCannotBeName"), cfzero);
CFDictionarySetValue(ret, CFSTR("requiresMixedCase"), cfzero);
CFDictionarySetValue(ret, CFSTR("requiresSymbol"), cfzero);
CFDictionarySetValue(ret, CFSTR("notGuessablePattern"), cfzero);
CFDictionarySetValue(ret, CFSTR("isSessionKeyAgent"), cfzero);
CFDictionarySetValue(ret, CFSTR("isComputerAccount"), cfzero);
return ret;
}
CFDictionaryRef odusers_copy_defaultglobalpolicy(void) {
CFMutableDictionaryRef ret = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
int one = 1;
int zero = 0;
CFNumberRef cfone = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &one);
CFNumberRef cfzero = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &zero);
CFDictionarySetValue(ret, CFSTR("usingHistory"), cfzero);
CFDictionarySetValue(ret, CFSTR("canModifyPasswordforSelf"), cfone);
CFDictionarySetValue(ret, CFSTR("usingExpirationDate"), cfzero);
CFDictionarySetValue(ret, CFSTR("usingHardExpirationDate"), cfzero);
CFDictionarySetValue(ret, CFSTR("requiresAlpha"), cfzero);
CFDictionarySetValue(ret, CFSTR("requiresNumeric"), cfzero);
CFDictionarySetValue(ret, CFSTR("maxMinutesUntilChangePassword"), cfzero);
CFDictionarySetValue(ret, CFSTR("maxMinutesUntilDisabled"), cfzero);
CFDictionarySetValue(ret, CFSTR("maxMinutesOfNonUse"), cfzero);
CFDictionarySetValue(ret, CFSTR("maxFailedLoginAttempts"), cfzero);
CFDictionarySetValue(ret, CFSTR("minChars"), cfzero);
CFDictionarySetValue(ret, CFSTR("maxChars"), cfzero);
CFDictionarySetValue(ret, CFSTR("passwordCannotBeName"), cfzero);
CFDictionarySetValue(ret, CFSTR("requiresMixedCase"), cfzero);
CFDictionarySetValue(ret, CFSTR("requiresSymbol"), cfzero);
CFDictionarySetValue(ret, CFSTR("newPasswordRequired"), cfzero);
CFDictionarySetValue(ret, CFSTR("minutesUntilFailedLoginReset"), cfzero);
CFDictionarySetValue(ret, CFSTR("notGuessablePattern"), cfzero);
return ret;
}
struct berval *odusers_copy_dict2bv(CFDictionaryRef dict) {
CFDataRef xmlData = CFPropertyListCreateData(kCFAllocatorDefault, (CFPropertyListRef)dict, kCFPropertyListXMLFormat_v1_0, 0, NULL);
if(!xmlData) {
Debug(LDAP_DEBUG_ANY, "%s: Could not convert CFDictionary to CFData", __PRETTY_FUNCTION__, 0, 0);
return NULL;
}
struct berval *ret = ber_mem2bv((LDAP_CONST char *)CFDataGetBytePtr(xmlData), CFDataGetLength(xmlData), 1, NULL);
CFRelease(xmlData);
return ret;
}
CFDictionaryRef odusers_copy_effectiveuserpoldict(struct berval *dn) {
Entry *e = NULL;
Attribute *effective = NULL;
Attribute *global_attr = odusers_copy_globalpolicy();
Attribute *passwordRequiredDateAttr = odusers_copy_passwordRequiredDate();
CFDictionaryRef globaldict = NULL;
CFMutableDictionaryRef effectivedict = NULL;
CFDictionaryRef userdict = NULL;
Attribute *policyAttr = NULL;
Attribute *failedLogins = NULL;
Attribute *creationDate = NULL;
Attribute *passModDate = NULL;
Attribute *modDate = NULL;
Attribute *lastLogin = NULL;
Attribute *disableReason = NULL;
int disableReasonInt = 0;
const char *text = NULL;
CFDateRef lastLoginCF = NULL;
CFDateRef creationDateCF = NULL;
CFDateRef passModDateCF = NULL;
CFDateRef passwordRequiredDateCF = NULL;
uint16_t loginattempts = 0;
int one = 1;
CFNumberRef cfone = NULL;
e = odusers_copy_authdata(dn);
if(!e) {
Debug(LDAP_DEBUG_ANY, "%s: No entry associated with %s\n", __PRETTY_FUNCTION__, dn->bv_val, 0);
goto out;
}
if(!policyAD && slap_str2ad("apple-user-passwordpolicy", &policyAD, &text) != 0) {
Debug(LDAP_DEBUG_ANY, "%s: Unable to retrieve description of apple-user-passwordpolicy attribute", __PRETTY_FUNCTION__, 0, 0);
goto out;
}
if(!lastLoginAD && slap_str2ad("lastLoginTime", &lastLoginAD, &text) != 0) {
Debug(LDAP_DEBUG_ANY, "%s: Unable to retrieve description of lastLoginTime attribute", __PRETTY_FUNCTION__, 0, 0);
goto out;
}
if(!creationDateAD && slap_str2ad("creationDate", &creationDateAD, &text) != 0) {
Debug(LDAP_DEBUG_ANY, "%s: Unable to retrieve description of creationDate attribute", __PRETTY_FUNCTION__, 0, 0);
goto out;
}
if(!passModDateAD && slap_str2ad("passwordModDate", &passModDateAD, &text) != 0) {
Debug(LDAP_DEBUG_ANY, "%s: Unable to retrieve description of passwordModDate attribute", __PRETTY_FUNCTION__, 0, 0);
goto out;
}
if(!failedLoginsAD && slap_str2ad("loginFailedAttempts", &failedLoginsAD, &text) != 0) {
Debug(LDAP_DEBUG_ANY, "%s: Unable to retrieve description of loginFailedAttempts attribute", __PRETTY_FUNCTION__, 0, 0);
goto out;
}
if(!disableReasonAD && slap_str2ad("disableReason", &disableReasonAD, &text) != 0) {
Debug(LDAP_DEBUG_ANY, "%s: Unable to retrieve description of disableReason attribute", __PRETTY_FUNCTION__, 0, 0);
goto out;
}
if(!global_attr || global_attr->a_numvals == 0) {
globaldict = odusers_copy_defaultglobalpolicy();
} else {
globaldict = CopyPolicyToDict(global_attr->a_vals[0].bv_val, global_attr->a_vals[0].bv_len);
if(!globaldict) {
Debug(LDAP_DEBUG_ANY, "%s: Unable to convert retrieved global policy to CFDictionary", __PRETTY_FUNCTION__, 0, 0);
goto out;
}
}
policyAttr = attrs_find(e->e_attrs, policyAD);
if(policyAttr) {
CFDictionaryRef tmpdict = CopyPolicyToDict(policyAttr->a_vals[0].bv_val, policyAttr->a_vals[0].bv_len);
CFDictionaryRef defaultdict = odusers_copydefaultuserpolicy();
userdict = CopyEffectivePolicy(defaultdict, tmpdict);
CFRelease(defaultdict);
CFRelease(tmpdict);
} else {
userdict = odusers_copydefaultuserpolicy();
}
if(!userdict) {
Debug(LDAP_DEBUG_ANY, "%s: Unable to convert retrieved user policy to CFDictionary", __PRETTY_FUNCTION__, 0, 0);
goto out;
}
effectivedict = CopyEffectivePolicy(globaldict, userdict);
if(!effectivedict) {
Debug(LDAP_DEBUG_ANY, "%s: Unable to compute effective policy from global and user dictionaries", __PRETTY_FUNCTION__, 0, 0);
goto out;
}
lastLogin = attrs_find(e->e_attrs, lastLoginAD);
struct tm tmptm = {0};
time_t tmptime = 0;
if(lastLogin && lastLogin->a_numvals && lastLogin->a_nvals[0].bv_len) {
strptime(lastLogin->a_nvals[0].bv_val, "%Y%m%d%H%M%SZ", &tmptm);
tmptime = timegm(&tmptm);
lastLoginCF = CFDateCreate(kCFAllocatorDefault, tmptime - kCFAbsoluteTimeIntervalSince1970);
}
creationDate = attrs_find(e->e_attrs, creationDateAD);
tmptime = 0;
if(creationDate && creationDate->a_numvals && creationDate->a_nvals[0].bv_len) {
strptime(creationDate->a_nvals[0].bv_val, "%Y%m%d%H%M%SZ", &tmptm);
tmptime = timegm(&tmptm);
}
creationDateCF = CFDateCreate(kCFAllocatorDefault, tmptime - kCFAbsoluteTimeIntervalSince1970);
passModDate = attrs_find(e->e_attrs, passModDateAD);
tmptime = 0;
if(passModDate && passModDate->a_numvals && passModDate->a_nvals[0].bv_len) {
strptime(passModDate->a_nvals[0].bv_val, "%Y%m%d%H%M%SZ", &tmptm);
tmptime = timegm(&tmptm);
CFNumberRef cftmptime = CFNumberCreate(kCFAllocatorDefault, kCFNumberLongType, &tmptime);
CFDictionarySetValue(effectivedict, CFSTR("passwordLastSetTime"), cftmptime);
CFRelease(cftmptime);
}
passModDateCF = CFDateCreate(kCFAllocatorDefault, tmptime - kCFAbsoluteTimeIntervalSince1970);
if(!passModDateCF) {
passModDateCF = CFDateCreate(kCFAllocatorDefault, 0);
}
if(passwordRequiredDateAttr && passwordRequiredDateAttr->a_numvals && passwordRequiredDateAttr->a_nvals[0].bv_len) {
strptime(passwordRequiredDateAttr->a_nvals[0].bv_val, "%Y%m%d%H%M%SZ", &tmptm);
tmptime = timegm(&tmptm);
}
passwordRequiredDateCF = CFDateCreate(kCFAllocatorDefault, tmptime - kCFAbsoluteTimeIntervalSince1970);
failedLogins = attrs_find(e->e_attrs, failedLoginsAD);
if(failedLogins && failedLogins->a_numvals && failedLogins->a_nvals[0].bv_len) {
long long tmpll;
errno = 0;
tmpll = strtoll(failedLogins->a_nvals[0].bv_val, NULL, 10);
if( ((tmpll == LLONG_MAX) || (tmpll == LLONG_MIN)) && (errno == ERANGE) ) {
tmpll = 0;
}
if( (tmpll > USHRT_MAX) || (tmpll < 0) ) {
tmpll = 0;
}
loginattempts = tmpll;
}
short tmpshort = 0;
int zero = 0;
CFNumberRef cfzero = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &zero);
CFDictionarySetValue(effectivedict, CFSTR("newPasswordRequired"), cfzero);
CFRelease(cfzero);
CFNumberRef newPasswordRequired = CFDictionaryGetValue(userdict, CFSTR("newPasswordRequired"));
if(newPasswordRequired) CFNumberGetValue(newPasswordRequired, kCFNumberShortType, &tmpshort);
if(tmpshort) {
CFDictionarySetValue(effectivedict, CFSTR("newPasswordRequired"), newPasswordRequired);
} else {
tmpshort = 0;
newPasswordRequired = CFDictionaryGetValue(globaldict, CFSTR("newPasswordRequired"));
if(newPasswordRequired) CFNumberGetValue(newPasswordRequired, kCFNumberShortType, &tmpshort);
if(tmpshort) {
if(passwordRequiredDateCF) {
if((CFDateCompare(passwordRequiredDateCF, passModDateCF, NULL) == kCFCompareGreaterThan) || (CFDateCompare(passwordRequiredDateCF, passModDateCF, NULL) == kCFCompareEqualTo)) {
CFDictionarySetValue(effectivedict, CFSTR("newPasswordRequired"), newPasswordRequired);
}
} else {
Debug(LDAP_DEBUG_ANY, "%s: inconsistency detected, global newPasswordRequired policy set, but no passwordRequiredDate found in cn=access,cn=authdata\n", __func__, 0, 0);
}
}
}
long tmplong = 0;
CFNumberRef minutesUntilFailedLoginReset = CFDictionaryGetValue(effectivedict, CFSTR("minutesUntilFailedLoginReset"));
if(minutesUntilFailedLoginReset) CFNumberGetValue(minutesUntilFailedLoginReset, kCFNumberLongType, &tmplong);
if(tmplong > 0) {
modDate = attrs_find(e->e_attrs, slap_schema.si_ad_modifyTimestamp);
tmptime = 0;
if(modDate && modDate->a_numvals && modDate->a_nvals[0].bv_len) {
strptime(modDate->a_nvals[0].bv_val, "%Y%m%d%H%M%SZ", &tmptm);
tmptime = timegm(&tmptm);
}
if( time(NULL) >= (tmptime + (tmplong*60)) ) {
loginattempts = 0;
if(odusers_reset_failedlogin(dn) != 0) {
Debug(LDAP_DEBUG_ANY, "%s: error resetting failed login count for %s\n", __func__, dn->bv_val, 0);
}
}
}
disableReason = attrs_find(e->e_attrs, disableReasonAD);
if(disableReason && disableReason->a_numvals && disableReason->a_nvals[0].bv_len) {
long long tmpll;
errno = 0;
tmpll = strtoll(disableReason->a_nvals[0].bv_val, NULL, 10);
if( ((tmpll == LLONG_MAX) || (tmpll == LLONG_MIN)) && (errno == ERANGE) ) {
tmpll = 0;
}
if( (tmpll > USHRT_MAX) || (tmpll < 0) ) {
tmpll = 0;
}
disableReasonInt = tmpll;
}
GetDisabledStatus(effectivedict, creationDateCF, lastLoginCF, passModDateCF, &loginattempts, disableReasonInt);
if(lastLoginCF) CFRelease(lastLoginCF);
if(creationDateCF) CFRelease(creationDateCF);
if(passModDateCF) CFRelease(passModDateCF);
if(passwordRequiredDateCF) CFRelease(passwordRequiredDateCF);
if(userdict) CFRelease(userdict);
if(globaldict) CFRelease(globaldict);
if (global_attr) attr_free(global_attr);
if (passwordRequiredDateAttr) attr_free(passwordRequiredDateAttr);
if (e) entry_free(e);
return effectivedict;
out:
if (global_attr) attr_free(global_attr);
if (e) entry_free(e);
if (globaldict) CFRelease(globaldict);
return NULL;
}
int odusers_isdisabled(CFDictionaryRef policy) {
short tmpshort = 0;
CFNumberRef isdisabled = CFDictionaryGetValue(policy, CFSTR("isDisabled"));
if(isdisabled) CFNumberGetValue(isdisabled, kCFNumberShortType, &tmpshort);
if(tmpshort) {
CFNumberRef disableReason = CFDictionaryGetValue(policy, CFSTR("effectiveDisableReason"));
if(disableReason) {
int tmpint;
CFNumberGetValue(disableReason, kCFNumberIntType, &tmpint);
if(tmpint) return tmpint;
}
return true;
}
return false;
}
bool odusers_isadmin(CFDictionaryRef policy) {
short tmpshort = 0;
CFNumberRef isdisabled = CFDictionaryGetValue(policy, CFSTR("isAdminUser"));
if(isdisabled) CFNumberGetValue(isdisabled, kCFNumberShortType, &tmpshort);
if(tmpshort) {
return true;
}
return false;
}
int odusers_reset_failedlogin(struct berval *dn) {
int ret = -1;
OperationBuffer opbuf = {0};
Operation *fakeop = NULL;
Connection conn = {0};
SlapReply rs = {REP_RESULT};
Modifications *mod = NULL;
Entry *e = NULL;
Attribute *failedLogins = NULL;
const char *text = NULL;
short optype = LDAP_MOD_ADD;
if(!failedLoginsAD && slap_str2ad("loginFailedAttempts", &failedLoginsAD, &text) != 0) {
Debug(LDAP_DEBUG_TRACE, "%s: Unable to retrieve description of loginFailedAttempts attribute", __PRETTY_FUNCTION__, 0, 0);
goto out;
}
e = odusers_copy_authdata(dn);
if(!e) {
Debug(LDAP_DEBUG_ANY, "%s: No entry associated with %s\n", __func__, dn->bv_val, 0);
goto out;
}
mod = (Modifications *) ch_malloc(sizeof(Modifications));
mod->sml_op = optype;
mod->sml_flags = 0;
mod->sml_type = failedLoginsAD->ad_cname;
mod->sml_values = (struct berval*) ch_malloc(2 * sizeof(struct berval));
mod->sml_values[0].bv_val = strdup("0");
mod->sml_values[0].bv_len = 1;
mod->sml_values[1].bv_val = NULL;
mod->sml_values[1].bv_len = 0;
mod->sml_numvals = 1;
mod->sml_nvalues = NULL;
mod->sml_desc = failedLoginsAD;
mod->sml_next = NULL;
connection_fake_init2(&conn, &opbuf, ldap_pvt_thread_pool_context(), 0);
fakeop = &opbuf.ob_op;
fakeop->o_dn = *dn;
dnNormalize(0, NULL, NULL, dn, &fakeop->o_ndn, NULL);
fakeop->o_req_dn = e->e_name;
fakeop->o_req_ndn = e->e_nname;
fakeop->orm_modlist = mod;
fakeop->o_tag = LDAP_REQ_MODIFY;
fakeop->o_conn->c_listener->sl_url.bv_val = "ldapi://%2Fvar%2Frun%2Fldapi";
fakeop->o_conn->c_listener->sl_url.bv_len = strlen("ldapi://%2Fvar%2Frun%2Fldapi");
fakeop->o_bd = frontendDB;
fakeop->o_bd->be_modify(fakeop, &rs);
if(rs.sr_err != LDAP_SUCCESS) {
goto out;
}
ret = 0;
out:
if(fakeop && !BER_BVISNULL(&fakeop->o_ndn)) ch_free(fakeop->o_ndn.bv_val);
if(e) entry_free(e);
if(mod) slap_mods_free(mod, 1);
return ret;
}
int odusers_increment_failedlogin(struct berval *dn) {
int ret = -1;
OperationBuffer opbuf = {0};
Operation *fakeop = NULL;
Connection conn = {0};
SlapReply rs = {REP_RESULT};
Modifications *mod = NULL;
char *attemptsstr = NULL;
Entry *e = NULL;
Attribute *failedLogins = NULL;
const char *text = NULL;
uint16_t loginattempts = 0;
short optype = LDAP_MOD_ADD;
if(!failedLoginsAD && slap_str2ad("loginFailedAttempts", &failedLoginsAD, &text) != 0) {
Debug(LDAP_DEBUG_TRACE, "%s: Unable to retrieve description of loginFailedAttempts attribute", __PRETTY_FUNCTION__, 0, 0);
goto out;
}
e = odusers_copy_authdata(dn);
if(!e) {
Debug(LDAP_DEBUG_TRACE, "%s: No entry associated with %s\n", __PRETTY_FUNCTION__, dn->bv_val, 0);
goto out;
}
failedLogins = attrs_find(e->e_attrs, failedLoginsAD);
if(failedLogins && failedLogins->a_numvals && failedLogins->a_nvals[0].bv_len) {
long long tmpll;
errno = 0;
tmpll = strtoll(failedLogins->a_nvals[0].bv_val, NULL, 10);
if( ((tmpll == LLONG_MAX) || (tmpll == LLONG_MIN)) && (errno == ERANGE) ) {
tmpll = 0;
}
if( (tmpll > USHRT_MAX) || (tmpll < 0) ) {
tmpll = 0;
}
loginattempts = tmpll;
optype = LDAP_MOD_REPLACE;
}
loginattempts++;
asprintf(&attemptsstr, "%hu", loginattempts);
mod = (Modifications *) ch_malloc(sizeof(Modifications));
mod->sml_op = optype;
mod->sml_flags = 0;
mod->sml_type = failedLoginsAD->ad_cname;
mod->sml_values = (struct berval*) ch_malloc(2 * sizeof(struct berval));
mod->sml_values[0].bv_val = attemptsstr;
mod->sml_values[0].bv_len = strlen(attemptsstr);
mod->sml_values[1].bv_val = NULL;
mod->sml_values[1].bv_len = 0;
mod->sml_numvals = 1;
mod->sml_nvalues = NULL;
mod->sml_desc = failedLoginsAD;
mod->sml_next = NULL;
connection_fake_init2(&conn, &opbuf, ldap_pvt_thread_pool_context(), 0);
fakeop = &opbuf.ob_op;
fakeop->o_dn = *dn;
fakeop->o_ndn = *dn;
fakeop->o_req_dn = e->e_name;
fakeop->o_req_ndn = e->e_name;
fakeop->orm_modlist = mod;
fakeop->o_tag = LDAP_REQ_MODIFY;
fakeop->o_conn->c_listener->sl_url.bv_val = "ldapi://%2Fvar%2Frun%2Fldapi";
fakeop->o_conn->c_listener->sl_url.bv_len = strlen("ldapi://%2Fvar%2Frun%2Fldapi");
fakeop->o_bd = frontendDB;
fakeop->o_bd->be_modify(fakeop, &rs);
if(rs.sr_err != LDAP_SUCCESS) {
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);
goto out;
}
ret = 0;
out:
if(e) entry_free(e);
if(mod) slap_mods_free(mod, 1);
return ret;
}
int odusers_successful_auth(struct berval *dn, CFDictionaryRef policy) {
int ret = -1;
OperationBuffer opbuf = {0};
Operation *fakeop = NULL;
Connection conn = {0};
SlapReply rs = {REP_RESULT};
Modifications *mod = NULL;
Modifications *modhead = NULL;
char *attemptsstr = NULL;
Entry *e = NULL;
Attribute *failedLogins = NULL;
const char *text = NULL;
short tmpshort = 0;
CFNumberRef maxMins = NULL;
if(!failedLoginsAD && slap_str2ad("loginFailedAttempts", &failedLoginsAD, &text) != 0) {
Debug(LDAP_DEBUG_ANY, "%s: Unable to retrieve description of loginFailedAttempts attribute", __PRETTY_FUNCTION__, 0, 0);
goto out;
}
e = odusers_copy_authdata(dn);
if(!e) {
Debug(LDAP_DEBUG_ANY, "%s: No entry associated with %s\n", __PRETTY_FUNCTION__, dn->bv_val, 0);
goto out;
}
failedLogins = attrs_find(e->e_attrs, failedLoginsAD);
if(failedLogins && failedLogins->a_numvals && failedLogins->a_nvals[0].bv_len) {
mod = (Modifications *) ch_malloc(sizeof(Modifications));
mod->sml_op = LDAP_MOD_REPLACE;
mod->sml_flags = 0;
mod->sml_type = failedLoginsAD->ad_cname;
mod->sml_values = (struct berval*) ch_malloc(2 * sizeof(struct berval));
mod->sml_values[0].bv_val = ch_strdup("0");
mod->sml_values[0].bv_len = 1;
mod->sml_values[1].bv_val = NULL;
mod->sml_values[1].bv_len = 0;
mod->sml_nvalues = NULL;
mod->sml_numvals = 1;
mod->sml_desc = failedLoginsAD;
mod->sml_next = modhead;
modhead = mod;
}
maxMins = CFDictionaryGetValue(policy, CFSTR("maxMinutesOfNonUse"));
if(maxMins) CFNumberGetValue(maxMins, kCFNumberShortType, &tmpshort);
if(tmpshort) {
time_t tmptime;
struct tm tmptm;
if(!lastLoginAD && slap_str2ad("lastLoginTime", &lastLoginAD, &text) != 0) {
Debug(LDAP_DEBUG_ANY, "%s: Unable to retrieve description of lastLoginTime attribute", __PRETTY_FUNCTION__, 0, 0);
goto out;
}
tmptime = time(NULL);
gmtime_r(&tmptime, &tmptm);
mod = (Modifications *) ch_malloc(sizeof(Modifications));
mod->sml_op = LDAP_MOD_REPLACE;
mod->sml_flags = 0;
mod->sml_type = lastLoginAD->ad_cname;
mod->sml_values = (struct berval*) ch_malloc(2 * sizeof(struct berval));
mod->sml_values[0].bv_val = ch_calloc(256, 1);
mod->sml_values[0].bv_len = strftime(mod->sml_values[0].bv_val, 256, "%Y%m%d%H%M%SZ", &tmptm);
mod->sml_values[1].bv_val = NULL;
mod->sml_values[1].bv_len = 0;
mod->sml_nvalues = NULL;
mod->sml_numvals = 1;
mod->sml_desc = lastLoginAD;
mod->sml_next = modhead;
modhead = mod;
}
if(!modhead) {
ret = 0;
goto out;
}
connection_fake_init2(&conn, &opbuf, ldap_pvt_thread_pool_context(), 0);
fakeop = &opbuf.ob_op;
fakeop->o_dn = *dn;
fakeop->o_ndn = *dn;
fakeop->o_req_dn = e->e_name;
fakeop->o_req_ndn = e->e_name;
fakeop->orm_modlist = modhead;
fakeop->o_tag = LDAP_REQ_MODIFY;
fakeop->o_conn->c_listener->sl_url.bv_val = "ldapi://%2Fvar%2Frun%2Fldapi";
fakeop->o_conn->c_listener->sl_url.bv_len = strlen("ldapi://%2Fvar%2Frun%2Fldapi");
fakeop->o_bd = frontendDB;
fakeop->o_bd->be_modify(fakeop, &rs);
if(rs.sr_err != LDAP_SUCCESS) {
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);
goto out;
}
ret = 0;
out:
if(e) entry_free(e);
if(modhead) slap_mods_free(modhead, 1);
return ret;
}
char *odusers_copy_saslrealm(void) {
OperationBuffer opbuf = {0};
Connection conn = {0};
Entry *e = NULL;
Attribute *a = NULL;
char *ret = NULL;
const char *text = NULL;
connection_fake_init2(&conn, &opbuf, ldap_pvt_thread_pool_context(), 0);
if(root_dse_info(&conn, &e, &text) != LDAP_SUCCESS) {
Debug(LDAP_DEBUG_ANY, "%s: root_dse_info failed\n", __func__, 0, 0);
goto out;
}
if(!e) {
Debug(LDAP_DEBUG_ANY, "%s: No entry found\n", __func__, 0, 0);
goto out;
}
a = attr_find(e->e_attrs, slap_schema.si_ad_saslRealm);
if(!a) {
Debug(LDAP_DEBUG_ANY, "%s: Could not locate sasl realm\n", __func__, 0, 0);
goto out;
}
if(a->a_numvals == 0) {
Debug(LDAP_DEBUG_ANY, "%s: No values returned\n", __func__, 0, 0);
goto out;
}
ret = ch_calloc(a->a_nvals[0].bv_len +1, 1);
if(!ret) goto out;
memcpy(ret, a->a_nvals[0].bv_val, a->a_nvals[0].bv_len);
out:
if(e) entry_free(e);
return ret;
}
char *odusers_copy_suffix(void) {
OperationBuffer opbuf = {0};
Connection conn = {0};
Entry *e = NULL;
Attribute *a = NULL;
char *ret = NULL;
const char *text = NULL;
connection_fake_init2(&conn, &opbuf, ldap_pvt_thread_pool_context(), 0);
if(root_dse_info(&conn, &e, &text) != LDAP_SUCCESS) {
Debug(LDAP_DEBUG_ANY, "%s: root_dse_info failed\n", __func__, 0, 0);
goto out;
}
if(!e) {
Debug(LDAP_DEBUG_ANY, "%s: No entry found\n", __func__, 0, 0);
goto out;
}
a = attr_find(e->e_attrs, slap_schema.si_ad_namingContexts);
if(!a) {
Debug(LDAP_DEBUG_ANY, "%s: Could not locate naming contexts\n", __func__, 0, 0);
goto out;
}
if(a->a_numvals == 0) {
Debug(LDAP_DEBUG_ANY, "%s: No values returned\n", __func__, 0, 0);
goto out;
}
ret = ch_calloc(a->a_nvals[0].bv_len +1, 1);
if(!ret) goto out;
memcpy(ret, a->a_nvals[0].bv_val, a->a_nvals[0].bv_len);
out:
if(e) entry_free(e);
return ret;
}
char *odusers_copy_krbrealm(Operation *op) {
OperationBuffer opbuf = {0};
Operation *fakeop = NULL;
Connection conn = {0};
Entry *e = NULL;
Attribute *a = NULL;
const char *text = NULL;
static char *savedrealm = NULL;
char *ret = NULL;
char *suffix = NULL;
if(savedrealm) {
return strdup(savedrealm);
}
suffix = strnstr(op->o_req_ndn.bv_val, "dc=", op->o_req_ndn.bv_len);
if(!suffix) {
Debug(LDAP_DEBUG_ANY, "%s: Could not locate suffix of request %s", __func__, op->o_req_ndn.bv_val, 0);
return NULL;
}
connection_fake_init2(&conn, &opbuf, ldap_pvt_thread_pool_context(), 0);
fakeop = &opbuf.ob_op;
fakeop->o_req_dn.bv_len = asprintf(&fakeop->o_req_dn.bv_val, "cn=kerberoskdc,cn=config,%s", suffix);
fakeop->o_req_dn.bv_len = strlen(fakeop->o_req_dn.bv_val);
fakeop->o_dn = fakeop->o_ndn = fakeop->o_req_ndn = fakeop->o_req_dn;
fakeop->o_conn->c_listener->sl_url.bv_val = "ldapi://%2Fvar%2Frun%2Fldapi";
fakeop->o_conn->c_listener->sl_url.bv_len = strlen("ldapi://%2Fvar%2Frun%2Fldapi");
fakeop->ors_attrs = NULL;
e = odusers_copy_entry(fakeop);
if(!e) {
Debug(LDAP_DEBUG_ANY, "%s: No entry associated with KerberosKDC %s\n", __func__, fakeop->o_req_dn.bv_val, 0);
goto out;
}
if(!realnameAD && slap_str2ad("apple-config-realname", &realnameAD, &text) != 0) {
Debug(LDAP_DEBUG_ANY, "%s: slap_str2ad failed for apple-config-realname", __func__, 0, 0);
goto out;
}
a = attr_find(e->e_attrs, realnameAD);
if(!a) {
Debug(LDAP_DEBUG_ANY, "%s: Could not locate apple-config-realname attribute", __func__, 0, 0);
goto out;
}
ret = calloc(1, a->a_vals[0].bv_len + 1);
if(!ret) goto out;
memcpy(ret, a->a_vals[0].bv_val, a->a_vals[0].bv_len);
savedrealm = strdup(ret);
out:
if(e) entry_free(e);
if(fakeop && fakeop->o_req_dn.bv_val) free(fakeop->o_req_dn.bv_val);
return ret;
}
char *odusers_copy_recname(Operation *op) {
Attribute *attriter = NULL;
char *recname = NULL;
char *start = NULL;
char *end = NULL;
bool isuser = false;
start = strnstr(op->o_req_dn.bv_val, "=", op->o_req_dn.bv_len);
if(!start) return NULL;
start++;
end = strnstr(op->o_req_dn.bv_val, ",", op->o_req_dn.bv_len);
if(!end) return NULL;
recname = calloc(1, (end - start) + 1);
if(!recname) return NULL;
memcpy(recname, start, (end - start));
return recname;
}
char *odusers_copy_primarymasterip(Operation *op) {
OperationBuffer opbuf = {0};
Operation *fakeop = NULL;
Connection conn = {0};
Entry *e = NULL;
Attribute *a = NULL;
const char *text = NULL;
static char *savedprimarymasterip = NULL;
char *ret = NULL;
char *suffix = NULL;
if(savedprimarymasterip) {
return strdup(savedprimarymasterip);
}
suffix = strnstr(op->o_req_ndn.bv_val, "dc=", op->o_req_ndn.bv_len);
if(!suffix) {
Debug(LDAP_DEBUG_ANY, "%s: Could not locate suffix of request %s", __func__, op->o_req_ndn.bv_val, 0);
return NULL;
}
connection_fake_init2(&conn, &opbuf, ldap_pvt_thread_pool_context(), 0);
fakeop = &opbuf.ob_op;
fakeop->o_req_dn.bv_len = asprintf(&fakeop->o_req_dn.bv_val, "cn=passwordserver,cn=config,%s", suffix);
fakeop->o_req_dn.bv_len = strlen(fakeop->o_req_dn.bv_val);
fakeop->o_dn = fakeop->o_ndn = fakeop->o_req_ndn = fakeop->o_req_dn;
fakeop->o_conn->c_listener->sl_url.bv_val = "ldapi://%2Fvar%2Frun%2Fldapi";
fakeop->o_conn->c_listener->sl_url.bv_len = strlen("ldapi://%2Fvar%2Frun%2Fldapi");
fakeop->ors_attrs = NULL;
e = odusers_copy_entry(fakeop);
if(!e) {
Debug(LDAP_DEBUG_ANY, "%s: No entry associated with passwordserver %s\n", __func__, fakeop->o_req_dn.bv_val, 0);
goto out;
}
if(!pwslocAD && slap_str2ad("apple-password-server-location", &pwslocAD, &text) != 0) {
Debug(LDAP_DEBUG_ANY, "%s: slap_str2ad failed for apple-password-server-location", __func__, 0, 0);
goto out;
}
a = attr_find(e->e_attrs, pwslocAD);
if(!a) {
Debug(LDAP_DEBUG_ANY, "%s: Could not locate apple-password-server-location attribute", __func__, 0, 0);
goto out;
}
ret = calloc(1, a->a_vals[0].bv_len + 1);
if(!ret) goto out;
memcpy(ret, a->a_vals[0].bv_val, a->a_vals[0].bv_len);
savedprimarymasterip = strdup(ret);
out:
if(e) entry_free(e);
if(fakeop && fakeop->o_req_dn.bv_val) free(fakeop->o_req_dn.bv_val);
return ret;
}
static void ConvertHexToBinary( const char *inHexStr, unsigned char *outData, unsigned long *outLen )
{
unsigned char *tptr = outData;
unsigned char val;
while ( *inHexStr && *(inHexStr+1) )
{
if ( *inHexStr >= 'a' )
val = (*inHexStr - 'a' + 0x0A) << 4;
else
val = (*inHexStr - '0') << 4;
inHexStr++;
if ( *inHexStr >= 'a' )
val += (*inHexStr - 'a' + 0x0A);
else
val += (*inHexStr - '0');
inHexStr++;
*tptr++ = val;
}
*outLen = (tptr - outData);
}
static bool ConvertBinaryToHex( const unsigned char *inData, long len, char *outHexStr )
{
bool result = true;
char *tptr = outHexStr;
char base16table[16] = { '0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F' };
if ( inData == nil || outHexStr == nil )
return false;
for ( int idx = 0; idx < len; idx++ )
{
*tptr++ = base16table[(inData[idx] >> 4) & 0x0F];
*tptr++ = base16table[(inData[idx] & 0x0F)];
}
*tptr = '\0';
return result;
}
int odusers_clear_authattr(char *authguid, char *attribute) {
int ret = -1;
OperationBuffer opbuf = {0};
Operation *fakeop = NULL;
Connection conn = {0};
char *authdatadn = NULL;
AttributeDescription *genericAD = NULL;
Modifications *mhead = NULL;
Modifications *m = NULL;
SlapReply rs = {0};
connection_fake_init2(&conn, &opbuf, ldap_pvt_thread_pool_context(), 0);
fakeop = &opbuf.ob_op;
fakeop->o_conn->c_listener->sl_url.bv_val = "ldapi://%2Fvar%2Frun%2Fldapi";
fakeop->o_conn->c_listener->sl_url.bv_len = strlen("ldapi://%2Fvar%2Frun%2Fldapi");
fakeop->o_req_dn.bv_len = asprintf(&authdatadn, "authGUID=%s,cn=users,cn=authdata", authguid);
fakeop->o_req_dn.bv_val = authdatadn;
fakeop->o_req_ndn = fakeop->o_req_dn;
fakeop->o_dn = fakeop->o_ndn = fakeop->o_req_ndn = fakeop->o_req_dn;
fakeop->o_tag = LDAP_REQ_MODIFY;
fakeop->orm_no_opattrs = 1;
fakeop->o_dont_replicate = 1;
const char *text = NULL;
if(slap_str2ad(attribute, &genericAD, &text) != 0) {
Debug(LDAP_DEBUG_ANY, "%s: Could not find attribute description for %s\n", __func__, attribute, 0);
goto out;
}
m = ch_calloc(sizeof(Modifications), 1);
m->sml_op = LDAP_MOD_DELETE;
m->sml_flags = SLAP_MOD_INTERNAL;
m->sml_type = genericAD->ad_cname;
m->sml_desc = genericAD;
m->sml_numvals = 0;
m->sml_values = NULL;
m->sml_nvalues = NULL;
m->sml_next = mhead;
mhead = m;
fakeop->orm_modlist = mhead;
fakeop->o_bd = select_backend(&fakeop->o_req_ndn, 1);
if(!fakeop->o_bd) {
Debug(LDAP_DEBUG_ANY, "%s: Could not locate backend for %s\n", __func__, authdatadn, 0);
goto out;
}
fakeop->o_callback = NULL;
fakeop->o_bd->be_modify(fakeop, &rs);
slap_mods_free(mhead, 1);
ret = 0;
out:
if(authdatadn) free(authdatadn);
return ret;
}
int odusers_clear_authhashes(char *authguid) {
odusers_clear_authattr(authguid, "cmusaslsecretCRAM-MD5");
odusers_clear_authattr(authguid, "cmusaslsecretDIGEST-MD5");
odusers_clear_authattr(authguid, "cmusaslsecretDIGEST-UMD5");
odusers_clear_authattr(authguid, "cmusaslsecretPPS");
odusers_clear_authattr(authguid, "cmusaslsecretSMBNT");
odusers_clear_authattr(authguid, "password");
odusers_clear_authattr(authguid, "draft-krbKeySet");
return 0;
}
int odusers_set_password(struct berval *dn, char *password, int isChangingOwnPassword) {
int ret = -1;
OperationBuffer opbuf = {0};
Operation *fakeop = NULL;
Connection conn = {0};
Entry *usere = NULL;
Entry *authe = NULL;
Attribute *polattr = NULL;
char authguid[37] = {0};
char *recname = NULL;
char *kerbrealm = NULL;
char *saslrealm = NULL;
char *authdatadn = NULL;
char *suffix = NULL;
CFErrorRef error = NULL;
CFArrayRef keyset = NULL;
CFStringRef principal = NULL;
CFStringRef cfpassword = NULL;
CFTypeRef encVals[] = {CFSTR("aes256-cts-hmac-sha1-96"), CFSTR("aes128-cts-hmac-sha1-96"), CFSTR("des3-cbc-sha1") };
CFArrayRef enctypes = NULL;
SlapReply rs = {0};
CFArrayRef enabledmechArray = NULL;
CFIndex i;
Modifications *mhead = NULL;
Modifications *m = NULL;
const char *text = NULL;
unsigned long tmplong;
char needPlaintext = 0;
connection_fake_init2(&conn, &opbuf, ldap_pvt_thread_pool_context(), 0);
fakeop = &opbuf.ob_op;
fakeop->o_dn = fakeop->o_req_dn = *dn;
dnNormalize(0, NULL, NULL, dn, &fakeop->o_ndn, NULL);
fakeop->o_req_ndn = fakeop->o_ndn;
fakeop->o_conn->c_listener->sl_url.bv_val = "ldapi://%2Fvar%2Frun%2Fldapi";
fakeop->o_conn->c_listener->sl_url.bv_len = strlen("ldapi://%2Fvar%2Frun%2Fldapi");
usere = odusers_copy_entry(fakeop);
if(!usere) {
Debug(LDAP_DEBUG_ANY, "%s: Could not locate user record for %s\n", __func__, dn->bv_val, 0);
goto out;
}
if( odusers_get_authguid(usere, authguid) != 0) {
Debug(LDAP_DEBUG_ANY, "%s: Could not locate authguid for user %s\n", __func__, dn->bv_val, 0);
goto out;
}
recname = odusers_copy_recname(fakeop);
if(!recname) {
Debug(LDAP_DEBUG_ANY, "%s: Could not identify record name for %s\n", __func__, dn->bv_val, 0);
goto out;
}
char slotid[37];
int sloti, slotn;
slotid[0] = '0';
slotid[1] = 'x';
for(sloti = 0, slotn = 2; slotn < 34 && sloti < 36; sloti++) {
if( authguid[sloti] != '-' ) {
slotid[slotn] = authguid[sloti];
slotn++;
}
}
slotid[slotn] = '\0';
saslrealm = odusers_copy_saslrealm();
if(!saslrealm) {
Debug(LDAP_DEBUG_ANY, "%s: Could not find sasl realm\n", __func__, 0, 0);
goto out;
}
suffix = odusers_copy_suffix();
if(!suffix) {
Debug(LDAP_DEBUG_ANY, "%s: Could not find default naming context\n", __func__, 0, 0);
goto out;
}
enabledmechArray = odusers_copy_enabledmechs(suffix);
if(!enabledmechArray) {
Debug(LDAP_DEBUG_ANY, "%s: Could not find enabled mech list\n", __func__, 0, 0);
goto out;
}
for(i = 0; i < CFArrayGetCount(enabledmechArray); i++) {
CFStringRef mech = CFArrayGetValueAtIndex(enabledmechArray, i);
if(!mech) continue;
if(CFStringCompare(mech, CFSTR("GSSAPI"), kCFCompareCaseInsensitive) == kCFCompareEqualTo) {
kerbrealm = odusers_copy_krbrealm(fakeop);
if(!kerbrealm) {
Debug(LDAP_DEBUG_ANY, "%s: Could not find kerberos realm\n", __func__, dn->bv_val, 0);
goto out;
}
principal = CFStringCreateWithFormat(NULL, NULL, CFSTR("%s@%s"), recname, kerbrealm);
cfpassword = CFStringCreateWithCString(NULL, password, kCFStringEncodingUTF8);
enctypes = CFArrayCreate(NULL, (const void**)&encVals, sizeof(encVals)/sizeof(*encVals), &kCFTypeArrayCallBacks);
if (!enctypes)
{
Debug(LDAP_DEBUG_ANY, "%s: Unable to create CFArray of enctypes\n", __func__, 0, 0);
goto out;
}
keyset = HeimODModifyKeys(NULL, principal, enctypes, cfpassword, 0, &error);
if (!keyset)
{
Debug(LDAP_DEBUG_ANY, "%s: HeimODModifyKeys() returned a NULL keyset: %ld\n", __func__, error ? CFErrorGetCode(error) : 0, 0);
goto out;
}
if (CFArrayGetCount(keyset) == 0)
{
Debug(LDAP_DEBUG_ANY, "%s: HeimODModifyKeys() returned an empty keyset\n", __func__,0, 0);
goto out;
}
AttributeDescription *krbkeysAD = NULL;
int n;
if(slap_str2ad("draft-krbKeySet", &krbkeysAD, &text) != 0) {
Debug(LDAP_DEBUG_ANY, "%s: Could not find attribute description for draft-krbKeySet\n", __func__, 0, 0);
goto out;
}
m = ch_calloc(sizeof(Modifications), 1);
m->sml_op = LDAP_MOD_REPLACE;
m->sml_flags = 0;
m->sml_type = krbkeysAD->ad_cname;
m->sml_desc = krbkeysAD;
m->sml_numvals = CFArrayGetCount(keyset);
m->sml_values = ch_calloc(sizeof(struct berval), m->sml_numvals+1);
m->sml_nvalues = ch_calloc(sizeof(struct berval), m->sml_numvals+1);
for(n = 0; n < CFArrayGetCount(keyset); n++) {
CFDataRef key = CFArrayGetValueAtIndex(keyset, n);
m->sml_values[n].bv_len = CFDataGetLength(key);
m->sml_values[n].bv_val = ch_calloc(CFDataGetLength(key), 1);
memcpy(m->sml_values[n].bv_val, (void*)CFDataGetBytePtr(key), CFDataGetLength(key));
m->sml_nvalues[n].bv_len = CFDataGetLength(key);
m->sml_nvalues[n].bv_val = ch_calloc(CFDataGetLength(key), 1);
memcpy(m->sml_nvalues[n].bv_val, (void*)CFDataGetBytePtr(key), CFDataGetLength(key));
}
m->sml_next = mhead;
mhead = m;
}
if(CFStringCompare(mech, CFSTR("CRAM-MD5"), kCFCompareCaseInsensitive) == kCFCompareEqualTo) {
AttributeDescription *crammd5AD = NULL;
heim_CRAM_MD5_STATE crammd5state;
heim_cram_md5_export(password, &crammd5state);
if(slap_str2ad("cmusaslsecretCRAM-MD5", &crammd5AD, &text) != 0) {
Debug(LDAP_DEBUG_ANY, "%s: Could not find attribute description for cmusaslsecretCRAM-MD5\n", __func__, 0, 0);
goto out;
}
m = ch_calloc(sizeof(Modifications), 1);
m->sml_op = LDAP_MOD_REPLACE;
m->sml_flags = 0;
m->sml_type = crammd5AD->ad_cname;
m->sml_desc = crammd5AD;
m->sml_numvals = 1;
m->sml_values = ch_calloc(sizeof(struct berval), m->sml_numvals+1);
m->sml_nvalues = ch_calloc(sizeof(struct berval), m->sml_numvals+1);
m->sml_values[0].bv_len = sizeof(crammd5state);
m->sml_values[0].bv_val = ch_calloc(sizeof(crammd5state), 1);
memcpy(m->sml_values[0].bv_val, &crammd5state, sizeof(crammd5state));
m->sml_nvalues[0].bv_len = sizeof(crammd5state);
m->sml_nvalues[0].bv_val = ch_calloc(sizeof(crammd5state), 1);
memcpy(m->sml_nvalues[0].bv_val, &crammd5state, sizeof(crammd5state));
m->sml_next = mhead;
mhead = m;
}
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)) {
AttributeDescription *ntkeyAD = NULL;
struct ntlm_buf ntlmstate;
int rc;
rc = heim_ntlm_nt_key(password, &ntlmstate);
if(rc != 0) {
Debug(LDAP_DEBUG_ANY, "%s: heim_ntlm_nt_key returned %d\n", __func__, rc, 0);
goto out;
}
if(slap_str2ad("cmusaslsecretSMBNT", &ntkeyAD, &text) != 0) {
Debug(LDAP_DEBUG_ANY, "%s: Could not find attribute description for cmusaslsecretSMBNT\n", __func__, 0, 0);
goto out;
}
m = ch_calloc(sizeof(Modifications), 1);
m->sml_op = LDAP_MOD_REPLACE;
m->sml_flags = 0;
m->sml_type = ntkeyAD->ad_cname;
m->sml_desc = ntkeyAD;
m->sml_numvals = 1;
m->sml_values = ch_calloc(sizeof(struct berval), m->sml_numvals+1);
m->sml_nvalues = ch_calloc(sizeof(struct berval), m->sml_numvals+1);
m->sml_values[0].bv_len = ntlmstate.length * 2;
m->sml_values[0].bv_val = ch_calloc(m->sml_values[0].bv_len + 1, 1);
ConvertBinaryToHex(ntlmstate.data, ntlmstate.length, m->sml_values[0].bv_val);
heim_ntlm_free_buf(&ntlmstate);
m->sml_nvalues[0].bv_len = m->sml_values[0].bv_len;
m->sml_nvalues[0].bv_val = ch_calloc(m->sml_nvalues[0].bv_len, 1);
memcpy(m->sml_nvalues[0].bv_val, m->sml_values[0].bv_val, m->sml_nvalues[0].bv_len);
m->sml_next = mhead;
mhead = m;
}
if(CFStringCompare(mech, CFSTR("DIGEST-MD5"), kCFCompareCaseInsensitive) == kCFCompareEqualTo) {
AttributeDescription *digestmd5AD = NULL;
char *digest_userhash = heim_digest_userhash(slotid, saslrealm, password);
if(slap_str2ad("cmusaslsecretDIGEST-MD5", &digestmd5AD, &text) != 0) {
Debug(LDAP_DEBUG_ANY, "%s: Could not find attribute description for cmusaslsecretDIGEST-MD5\n", __func__, 0, 0);
goto out;
}
m = ch_calloc(sizeof(Modifications), 1);
m->sml_op = LDAP_MOD_REPLACE;
m->sml_flags = 0;
m->sml_type = digestmd5AD->ad_cname;
m->sml_desc = digestmd5AD;
m->sml_numvals = 1;
m->sml_values = ch_calloc(sizeof(struct berval), m->sml_numvals+1);
m->sml_nvalues = ch_calloc(sizeof(struct berval), m->sml_numvals+1);
m->sml_values[0].bv_len = tmplong = 16;
m->sml_values[0].bv_val = ch_calloc(m->sml_values[0].bv_len+1, 1);
ConvertHexToBinary(digest_userhash, (unsigned char*)m->sml_values[0].bv_val, &tmplong);
free(digest_userhash);
m->sml_nvalues[0].bv_len = m->sml_values[0].bv_len;
m->sml_nvalues[0].bv_val = ch_calloc(m->sml_nvalues[0].bv_len, 1);
memcpy(m->sml_nvalues[0].bv_val, m->sml_values[0].bv_val, m->sml_nvalues[0].bv_len);
m->sml_next = mhead;
mhead = m;
}
if((CFStringCompare(mech, CFSTR("DIGEST-MD5"), kCFCompareCaseInsensitive) == kCFCompareEqualTo) || (CFStringCompare(mech, CFSTR("WEBDAV-DIGEST"), kCFCompareCaseInsensitive) == kCFCompareEqualTo)) {
AttributeDescription *digestumd5AD = NULL;
char *udigest_userhash = heim_digest_userhash(recname, saslrealm, password);
if(slap_str2ad("cmusaslsecretDIGEST-UMD5", &digestumd5AD, &text) != 0) {
Debug(LDAP_DEBUG_ANY, "%s: Could not find attribute description for cmusaslsecretDIGEST-UMD5\n", __func__, 0, 0);
goto out;
}
needPlaintext = 1;
m = ch_calloc(sizeof(Modifications), 1);
m->sml_op = LDAP_MOD_REPLACE;
m->sml_flags = 0;
m->sml_type = digestumd5AD->ad_cname;
m->sml_desc = digestumd5AD;
m->sml_numvals = 1;
m->sml_values = ch_calloc(sizeof(struct berval), m->sml_numvals+1);
m->sml_nvalues = ch_calloc(sizeof(struct berval), m->sml_numvals+1);
m->sml_values[0].bv_len = tmplong = 16;
m->sml_values[0].bv_val = ch_calloc(m->sml_values[0].bv_len+1, 1);
ConvertHexToBinary(udigest_userhash, (unsigned char*)m->sml_values[0].bv_val, &tmplong);
free(udigest_userhash);
m->sml_nvalues[0].bv_len = m->sml_values[0].bv_len;
m->sml_nvalues[0].bv_val = ch_calloc(m->sml_nvalues[0].bv_len, 1);
memcpy(m->sml_nvalues[0].bv_val, m->sml_values[0].bv_val, m->sml_nvalues[0].bv_len);
m->sml_next = mhead;
mhead = m;
}
if(CFStringCompare(mech, CFSTR("APOP"), kCFCompareCaseInsensitive) == kCFCompareEqualTo) {
needPlaintext = 1;
}
}
if(needPlaintext) {
AttributeDescription *passwordAD = NULL;
int encodeLen = strlen(password);
encodeLen += (kFixedDESChunk - (encodeLen % kFixedDESChunk));
if(slap_str2ad("password", &passwordAD, &text) != 0) {
Debug(LDAP_DEBUG_ANY, "%s: Could not find attribute description for password\n", __func__, 0, 0);
goto out;
}
m = ch_calloc(sizeof(Modifications), 1);
m->sml_op = LDAP_MOD_REPLACE;
m->sml_flags = 0;
m->sml_type = passwordAD->ad_cname;
m->sml_desc = passwordAD;
m->sml_numvals = 1;
m->sml_values = ch_calloc(sizeof(struct berval), m->sml_numvals+1);
m->sml_nvalues = ch_calloc(sizeof(struct berval), m->sml_numvals+1);
m->sml_values[0].bv_len = encodeLen;
m->sml_values[0].bv_val = ch_calloc(m->sml_values[0].bv_len+1, 1);
strlcpy(m->sml_values[0].bv_val, password, encodeLen);
pwsf_DESEncode(m->sml_values[0].bv_val, encodeLen);
m->sml_nvalues[0].bv_len = m->sml_values[0].bv_len;
m->sml_nvalues[0].bv_val = ch_calloc(m->sml_nvalues[0].bv_len, 1);
memcpy(m->sml_nvalues[0].bv_val, m->sml_values[0].bv_val, m->sml_nvalues[0].bv_len);
m->sml_next = mhead;
mhead = m;
}
if(!policyAD && slap_str2ad("apple-user-passwordpolicy", &policyAD, &text) != 0) {
Debug(LDAP_DEBUG_ANY, "%s: Unable to retrieve description of apple-user-passwordpolicy attribute", __PRETTY_FUNCTION__, 0, 0);
goto out;
}
if(!passModDateAD && slap_str2ad("passwordModDate", &passModDateAD, &text) != 0) {
Debug(LDAP_DEBUG_ANY, "%s: Unable to retrieve description of passwordModDate attribute", __PRETTY_FUNCTION__, 0, 0);
goto out;
}
authe = odusers_copy_authdata(dn);
if(!authe) {
Debug(LDAP_DEBUG_ANY, "%s: Could not locate authdata for %s\n", __func__, dn->bv_val, 0);
goto out;
}
polattr = attr_find(authe->e_attrs, policyAD);
if(polattr) {
CFMutableDictionaryRef poldict = CopyPolicyToDict(polattr->a_vals[0].bv_val, polattr->a_vals[0].bv_len);
if(!poldict) {
Debug(LDAP_DEBUG_ANY, "%s: Could not convert policy to CFDictionary for %s\n", __func__, dn->bv_val, 0);
goto out;
}
if(isChangingOwnPassword) {
short tmpshort = 0;
CFNumberRef newPasswordRequired = CFDictionaryGetValue(poldict, CFSTR("newPasswordRequired"));
if(newPasswordRequired) CFNumberGetValue(newPasswordRequired, kCFNumberShortType, &tmpshort);
if(tmpshort > 0) {
struct berval *berdict = NULL;
tmpshort = 0;
newPasswordRequired = CFNumberCreate(kCFAllocatorDefault, kCFNumberShortType, &tmpshort);
CFDictionarySetValue(poldict, CFSTR("newPasswordRequired"), newPasswordRequired);
CFRelease(newPasswordRequired);
berdict = odusers_copy_dict2bv(poldict);
m = ch_calloc(sizeof(Modifications), 1);
m->sml_op = LDAP_MOD_REPLACE;
m->sml_flags = 0;
m->sml_type = policyAD->ad_cname;
m->sml_values = (struct berval*) ch_malloc(2 * sizeof(struct berval));
m->sml_values[0].bv_val = berdict->bv_val;
m->sml_values[0].bv_len = berdict->bv_len;
ch_free(berdict);
m->sml_values[1].bv_val = NULL;
m->sml_values[1].bv_len = 0;
m->sml_nvalues = NULL;
m->sml_numvals = 1;
m->sml_desc = policyAD;
m->sml_next = mhead;
mhead = m;
}
}
CFRelease(poldict);
}
time_t tmptime;
struct tm tmptm;
tmptime = time(NULL);
gmtime_r(&tmptime, &tmptm);
m = ch_calloc(sizeof(Modifications), 1);
m->sml_op = LDAP_MOD_REPLACE;
m->sml_flags = 0;
m->sml_type = passModDateAD->ad_cname;
m->sml_values = (struct berval*) ch_malloc(2 * sizeof(struct berval));
m->sml_values[0].bv_val = ch_calloc(256, 1);
m->sml_values[0].bv_len = strftime(m->sml_values[0].bv_val, 256, "%Y%m%d%H%M%SZ", &tmptm);
m->sml_values[1].bv_val = NULL;
m->sml_values[1].bv_len = 0;
m->sml_nvalues = NULL;
m->sml_numvals = 1;
m->sml_desc = passModDateAD;
m->sml_next = mhead;
mhead = m;
if(!failedLoginsAD && slap_str2ad("loginFailedAttempts", &failedLoginsAD, &text) != 0) {
Debug(LDAP_DEBUG_ANY, "%s: Unable to retrieve description of loginFailedAttempts attribute", __PRETTY_FUNCTION__, 0, 0);
goto out;
}
m = (Modifications *) ch_malloc(sizeof(Modifications));
m->sml_op = LDAP_MOD_REPLACE;
m->sml_flags = 0;
m->sml_type = failedLoginsAD->ad_cname;
m->sml_values = (struct berval*) ch_malloc(2 * sizeof(struct berval));
m->sml_values[0].bv_val = strdup("0");
m->sml_values[0].bv_len = 1;
m->sml_values[1].bv_val = NULL;
m->sml_values[1].bv_len = 0;
m->sml_numvals = 1;
m->sml_nvalues = NULL;
m->sml_desc = failedLoginsAD;
m->sml_next = mhead;
mhead = m;
odusers_clear_authhashes(authguid);
OperationBuffer opbuf2 = {0};
Operation *fakeop2 = NULL;
Connection conn2 = {0};
connection_fake_init2(&conn2, &opbuf2, ldap_pvt_thread_pool_context(), 0);
fakeop2 = &opbuf2.ob_op;
fakeop2->o_conn->c_listener->sl_url.bv_val = "ldapi://%2Fvar%2Frun%2Fldapi";
fakeop2->o_conn->c_listener->sl_url.bv_len = strlen("ldapi://%2Fvar%2Frun%2Fldapi");
fakeop2->o_req_dn.bv_len = asprintf(&authdatadn, "authGUID=%s,cn=users,cn=authdata", authguid);
fakeop2->o_req_dn.bv_val = authdatadn;
fakeop2->o_req_ndn = fakeop2->o_req_dn;
fakeop2->o_tag = LDAP_REQ_MODIFY;
fakeop2->orm_modlist = mhead;
fakeop2->o_bd = select_backend(&fakeop2->o_req_ndn, 1);
if(!fakeop2->o_bd) {
Debug(LDAP_DEBUG_ANY, "%s: Could not locate backend for %s\n", __func__, authdatadn, 0);
goto out;
}
fakeop2->o_callback = NULL;
fakeop2->o_bd->be_modify(fakeop2, &rs);
slap_mods_free(mhead, 1);
if(rs.sr_err != LDAP_SUCCESS) {
Debug(LDAP_DEBUG_ANY, "%s: Error modifying authdata with new password\n", __func__, 0, 0);
goto out;
}
ret = 0;
out:
if(usere) entry_free(usere);
if(authe) entry_free(authe);
if(suffix) free(suffix);
if(enabledmechArray) CFRelease(enabledmechArray);
if(keyset) CFRelease(keyset);
if(enctypes) CFRelease(enctypes);
if(principal) CFRelease(principal);
if(cfpassword) CFRelease(cfpassword);
if(kerbrealm) free(kerbrealm);
if(recname) free(recname);
if(authdatadn) free(authdatadn);
if(saslrealm) free(saslrealm);
if ( !BER_BVISNULL( &fakeop->o_ndn ) ) ber_memfree( fakeop->o_ndn.bv_val );
return ret;
}
int odusers_verify_passwordquality(const char *password, const char *username, CFDictionaryRef effectivepolicy, SlapReply *rs) {
int requiresAlpha = 0;
int requiresNumeric = 0;
int requiresSymbol = 0;
int requiresMixedCase = 0;
unsigned int minChars = 0;
unsigned int maxChars = 0;
int passwordCannotBeName = 0;
CFNumberRef tmpnum;
int len = 0;
tmpnum = CFDictionaryGetValue(effectivepolicy, CFSTR("requiresAlpha"));
if(tmpnum) {
CFNumberGetValue(tmpnum, kCFNumberIntType, &requiresAlpha);
}
tmpnum = CFDictionaryGetValue(effectivepolicy, CFSTR("requiresNumeric"));
if(tmpnum) {
CFNumberGetValue(tmpnum, kCFNumberIntType, &requiresNumeric);
}
tmpnum = CFDictionaryGetValue(effectivepolicy, CFSTR("minChars"));
if(tmpnum) {
CFNumberGetValue(tmpnum, kCFNumberIntType, &minChars);
}
tmpnum = CFDictionaryGetValue(effectivepolicy, CFSTR("requiresSymbol"));
if(tmpnum) {
CFNumberGetValue(tmpnum, kCFNumberIntType, &requiresSymbol);
}
tmpnum = CFDictionaryGetValue(effectivepolicy, CFSTR("requiresMixedCase"));
if(tmpnum) {
CFNumberGetValue(tmpnum, kCFNumberIntType, &requiresMixedCase);
}
tmpnum = CFDictionaryGetValue(effectivepolicy, CFSTR("maxChars"));
if(tmpnum) {
CFNumberGetValue(tmpnum, kCFNumberIntType, &maxChars);
}
tmpnum = CFDictionaryGetValue(effectivepolicy, CFSTR("passwordCannotBeName"));
if(tmpnum) {
CFNumberGetValue(tmpnum, kCFNumberIntType, &passwordCannotBeName);
}
len = strlen(password);
if(len == 0) {
rs->sr_text = "too short";
rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
return rs->sr_err;
}
if(len < minChars) {
rs->sr_text = "too short";
rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
return rs->sr_err;
}
if(maxChars > 0 && len > maxChars) {
rs->sr_text = "too long";
rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
return rs->sr_err;
}
if(requiresAlpha) {
bool hasAlpha = false;
int index;
for(index = 0; index < len; index++) {
if(isalpha(password[index])) {
hasAlpha = true;
break;
}
}
if(!hasAlpha) {
rs->sr_text = "Needs alpha";
rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
return rs->sr_err;
}
}
if(requiresNumeric) {
bool hasNumeric = false;
int index;
for(index = 0; index < len; index++) {
if(isdigit(password[index])) {
hasNumeric = true;
break;
}
}
if(!hasNumeric) {
rs->sr_text = "Needs number";
rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
return rs->sr_err;
}
}
if(requiresMixedCase) {
bool hasUpper = false;
bool hasLower = false;
int index;
for(index = 0; index < len; index++) {
if(password[index] >= 'A' && password[index] <= 'Z') {
hasUpper = true;
} else if(password[index] >= 'a' && password[index] <= 'z') {
hasLower = true;
}
if(hasUpper && hasLower) {
break;
}
}
if(!(hasUpper && hasLower)) {
rs->sr_text = "Needs mixed case";
rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
return rs->sr_err;
}
}
if(requiresSymbol) {
bool hasSymbol = false;
int index;
for(index = 0; index < len; index++) {
if(password[index] >= 'A' && password[index] <= 'Z') {
continue;
}
if(password[index] >= 'a' && password[index] <= 'z') {
continue;
}
if(password[index] >= '0' && password[index] <= '9') {
continue;
}
hasSymbol = true;
break;
}
if(!hasSymbol) {
rs->sr_text = "Needs symbol";
rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
return rs->sr_err;
}
}
if(passwordCannotBeName) {
uint16_t unamelen = strlen(username);
uint16_t smallerlen = ((len < unamelen) ? len : unamelen);
if(strncasecmp(password, username, smallerlen) == 0) {
rs->sr_text = "Cannot be username";
rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
return rs->sr_err;
}
}
return 0;
}
CFArrayRef odusers_copy_enabledmechs(const char *suffix) {
OperationBuffer opbuf;
Connection conn;
Operation *fakeop = NULL;
Entry *e = NULL;
struct berval dn;
CFMutableArrayRef ret = NULL;
char *searchdn = NULL;
dn.bv_len = asprintf(&searchdn, "cn=dirserv,cn=config,%s", suffix);
dn.bv_val = searchdn;
connection_fake_init2(&conn, &opbuf, ldap_pvt_thread_pool_context(), 0);
fakeop = &opbuf.ob_op;
fakeop->o_dn = fakeop->o_req_dn = dn;
dnNormalize(0, NULL, NULL, &dn, &fakeop->o_req_ndn, NULL);
fakeop->o_ndn = fakeop->o_req_ndn;
fakeop->o_conn->c_listener->sl_url.bv_val = "ldapi://%2Fvar%2Frun%2Fldapi";
fakeop->o_conn->c_listener->sl_url.bv_len = strlen("ldapi://%2Fvar%2Frun%2Fldapi");
e = odusers_copy_entry(fakeop);
if(!e) {
Debug(LDAP_DEBUG_ANY, "%s: could not locate %s\n", __func__, searchdn, 0);
goto out;
}
Attribute *a;
AttributeDescription *ad = NULL;
const char *text = NULL;
if(slap_str2ad("apple-enabled-auth-mech", &ad, &text) != 0) {
Debug(LDAP_DEBUG_ANY, "%s: could not get attribute description for apple-enabled-auth-mech\n", __func__, 0, 0);
goto out;
}
a = attr_find(e->e_attrs, ad);
if(!a) {
Debug(LDAP_DEBUG_ANY, "%s: could not locate any apple-enabled-auth-mech attributes\n", __func__, 0, 0);
goto out;
}
int i;
ret = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
if(!ret) {
Debug(LDAP_DEBUG_ANY, "%s: could not create mutable array\n", __func__, 0, 0);
goto out;
}
for(i = 0; i < a->a_numvals; i++) {
CFStringRef mech = CFStringCreateWithCString(NULL, a->a_vals[i].bv_val, kCFStringEncodingUTF8);
if(!mech) {
Debug(LDAP_DEBUG_ANY, "%s: could not process mech %s\n", __func__, a->a_vals[i].bv_val, 0);
continue;
}
CFArrayAppendValue(ret, mech);
CFRelease(mech);
}
out:
if(e) entry_free(e);
if(searchdn) free(searchdn);
if ( !BER_BVISNULL( &fakeop->o_req_ndn ) ) ber_memfree( fakeop->o_req_ndn.bv_val );
return ret;
}
int odusers_krb_auth(Operation *op, char *password) {
char *name = odusers_copy_recname(op);
char *realm = odusers_copy_krbrealm(op);
krb5_error_code problem;
krb5_context krbctx = NULL;
krb5_principal princ = NULL;
krb5_creds creds = {0};
int ret = -1;
if(!name) {
Debug(LDAP_DEBUG_ANY, "%s: could not retrieve record name\n", __func__, 0, 0);
goto out;
}
if(!realm) {
Debug(LDAP_DEBUG_ANY, "%s: could not retrieve krb realm while authing %s\n", __func__, name, 0);
goto out;
}
problem = krb5_init_context(&krbctx);
if(problem) {
Debug(LDAP_DEBUG_ANY, "%s: Error initting krb ctx for %s: %d\n", __func__, name, problem);
goto out;
}
problem = krb5_build_principal(krbctx, &princ, (int)strlen(realm), realm, name, NULL);
if(problem) {
Debug(LDAP_DEBUG_ANY, "%s: Error building principal for %s: %d", __func__, name, problem);
goto out;
}
problem = krb5_get_init_creds_password(krbctx, &creds, princ, password, NULL, 0, 0, NULL, NULL);
if(problem) {
Debug(LDAP_DEBUG_ANY, "%s: Error obtaining credentials for %s: %d", __func__, name, problem);
goto out;
}
ret = 0;
out:
if (name)
free(name);
if (realm)
free(realm);
if (krbctx) {
if (princ)
krb5_free_principal(krbctx, princ);
krb5_free_cred_contents(krbctx, &creds);
krb5_free_context(krbctx);
}
return ret;
}
char *odusers_copy_owner(struct berval *dn) {
OperationBuffer opbuf;
Connection conn;
Operation *fakeop = NULL;
Entry *e = NULL;
char *ret = NULL;
Attribute *a = NULL;
connection_fake_init2(&conn, &opbuf, ldap_pvt_thread_pool_context(), 0);
fakeop = &opbuf.ob_op;
fakeop->o_dn = fakeop->o_ndn = *dn;
fakeop->o_req_dn = fakeop->o_req_ndn = *dn;
fakeop->o_conn->c_listener->sl_url.bv_val = "ldapi://%2Fvar%2Frun%2Fldapi";
fakeop->o_conn->c_listener->sl_url.bv_len = strlen("ldapi://%2Fvar%2Frun%2Fldapi");
e = odusers_copy_entry(fakeop);
if(!e) {
Debug(LDAP_DEBUG_ANY, "%s: could not locate %s\n", __func__, dn->bv_val, 0);
goto out;
}
a = attr_find(e->e_attrs, slap_schema.si_ad_creatorsName);
if(!a) {
Debug(LDAP_DEBUG_ANY, "%s: could not locate creatorsName attribute for %s\n", __func__, dn->bv_val, 0);
goto out;
}
if(a->a_numvals < 1) {
Debug(LDAP_DEBUG_ANY, "%s: no values associated with creatorsName for %s\n", __func__, dn->bv_val, 0);
goto out;
}
ret = calloc(1,a->a_nvals[0].bv_len + 1);
if(!ret) goto out;
memcpy(ret, a->a_nvals[0].bv_val, a->a_nvals[0].bv_len);
out:
if(e) entry_free(e);
return ret;
}
int odusers_store_history(struct berval *dn, const char *password, CFDictionaryRef policy) {
Entry *e = NULL;
int ret = -1;
Attribute *a = NULL;
AttributeDescription *ad = NULL;
const char *text = NULL;
heim_CRAM_MD5_STATE crammd5state;
char *history = NULL;
int i;
short historyCount = 0;
char newhistory[sizeof(heim_CRAM_MD5_STATE)*16];
Modifications *mod = NULL;
short optype = LDAP_MOD_ADD;
OperationBuffer opbuf = {0};
Operation *fakeop = NULL;
Connection conn = {0};
SlapReply rs = {REP_RESULT};
if(!policy) return -1;
CFNumberRef usingHistory = CFDictionaryGetValue(policy, CFSTR("usingHistory"));
if(!usingHistory) return -1;
CFNumberGetValue(usingHistory, kCFNumberShortType, &historyCount);
if(!historyCount) return -1;
if(historyCount > 16) historyCount = 16;
heim_cram_md5_export(password, &crammd5state);
e = odusers_copy_authdata(dn);
if(!e) {
Debug(LDAP_DEBUG_ANY, "%s: Could not locate authdata for %s\n", __func__, dn->bv_val, 0);
goto out;
}
if(slap_str2ad("historyData", &ad, &text) != 0) {
Debug(LDAP_DEBUG_ANY, "%s: could not get attribute description for historyData\n", __func__, 0, 0);
goto out;
}
a = attr_find(e->e_attrs, ad);
if(a) {
if(a->a_numvals < 1) {
Debug(LDAP_DEBUG_ANY, "%s: history attribute lacks values for %s\n", __func__, dn->bv_val, 0);
ret = 0;
goto out;
}
if(a->a_nvals[0].bv_len > (16*sizeof(crammd5state))) {
Debug(LDAP_DEBUG_ANY, "%s: history data is larger than expected for %s\n", __func__, dn->bv_val, 0);
goto out;
}
history = a->a_nvals[0].bv_val;
for(i = 0; i < historyCount; i++) {
if(memcmp(&crammd5state, history, sizeof(crammd5state)) == 0) {
ret = 0;
goto out;
}
history += sizeof(crammd5state);
}
history = a->a_nvals[0].bv_val;
memcpy(newhistory, a->a_nvals[0].bv_val, a->a_nvals[0].bv_len);
for(i = 15; i >= 1; i--) {
memcpy(newhistory + i*sizeof(crammd5state), newhistory + (i-1)*sizeof(crammd5state), sizeof(crammd5state));
}
optype = LDAP_MOD_REPLACE;
}
memcpy(newhistory, &crammd5state, sizeof(crammd5state));
if((historyCount-1) < 15) {
bzero(newhistory + historyCount*sizeof(crammd5state), sizeof(newhistory) - historyCount*sizeof(crammd5state));
}
mod = (Modifications *)ch_malloc(sizeof(Modifications));
mod->sml_op = optype;
mod->sml_flags = 0;
mod->sml_type = ad->ad_cname;
mod->sml_values = (struct berval*)ch_malloc(2 * sizeof(struct berval));
mod->sml_values[0].bv_val = newhistory;
mod->sml_values[0].bv_len = sizeof(newhistory);
mod->sml_values[1].bv_len = 0;
mod->sml_values[1].bv_val = NULL;
mod->sml_numvals = 1;
mod->sml_nvalues = NULL;
mod->sml_desc = ad;
mod->sml_next = NULL;
connection_fake_init2(&conn, &opbuf, ldap_pvt_thread_pool_context(), 0);
fakeop = &opbuf.ob_op;
fakeop->o_dn = fakeop->o_ndn = *dn;
fakeop->o_req_dn = e->e_name;
fakeop->o_req_ndn = e->e_nname;
fakeop->orm_modlist = mod;
fakeop->o_tag = LDAP_REQ_MODIFY;
fakeop->o_conn->c_listener->sl_url.bv_val = "ldapi://%2Fvar%2Frun%2Fldapi";
fakeop->o_conn->c_listener->sl_url.bv_len = strlen("ldapi://%2Fvar%2Frun%2Fldapi");
fakeop->o_bd = frontendDB;
fakeop->o_bd->be_modify(fakeop, &rs);
if(rs.sr_err != LDAP_SUCCESS) {
Debug(LDAP_DEBUG_ANY, "Unable to modify history for %s: %d %s\n", dn->bv_val, rs.sr_err, rs.sr_text);
goto out;
}
ret = 0;
out:
if(e) entry_free(e);
return ret;
}
int odusers_check_history(struct berval *dn, const char *password, CFDictionaryRef policy) {
Entry *e = NULL;
int ret = -1;
Attribute *a = NULL;
AttributeDescription *ad = NULL;
const char *text = NULL;
heim_CRAM_MD5_STATE crammd5state;
char *history = NULL;
int i;
short historyCount = 0;
if(!policy) return -1;
CFNumberRef usingHistory = CFDictionaryGetValue(policy, CFSTR("usingHistory"));
if(!usingHistory) return 0;
CFNumberGetValue(usingHistory, kCFNumberShortType, &historyCount);
if(!historyCount) return 0;
if(historyCount > 16) historyCount = 16;
e = odusers_copy_authdata(dn);
if(!e) {
Debug(LDAP_DEBUG_ANY, "%s: Could not locate authdata for %s\n", __func__, dn->bv_val, 0);
goto out;
}
if(slap_str2ad("historyData", &ad, &text) != 0) {
Debug(LDAP_DEBUG_ANY, "%s: could not get attribute description for historyData\n", __func__, 0, 0);
goto out;
}
a = attr_find(e->e_attrs, ad);
if(!a) {
Debug(LDAP_DEBUG_ANY, "%s: no history data found for %s\n", __func__, dn->bv_val, 0);
ret = 0;
goto out;
}
if(a->a_numvals < 1) {
Debug(LDAP_DEBUG_ANY, "%s: history attribute lacks values for %s\n", __func__, dn->bv_val, 0);
ret = 0;
goto out;
}
if(a->a_nvals[0].bv_len > (16*sizeof(crammd5state))) {
Debug(LDAP_DEBUG_ANY, "%s: history data is larger than expected for %s\n", __func__, dn->bv_val, 0);
goto out;
}
heim_cram_md5_export(password, &crammd5state);
history = a->a_nvals[0].bv_val;
for(i = 0; i < historyCount; i++) {
if(memcmp(&crammd5state, history, sizeof(crammd5state)) == 0) {
ret = 1;
goto out;
}
history += sizeof(crammd5state);
}
ret = 0;
out:
if(e) entry_free(e);
return ret;
}
char *CopyPrimaryIPv4Address(void)
{
char *ret = NULL;
char *primaryInterfaceNameCStr = NULL;
SCDynamicStoreRef session = SCDynamicStoreCreate(NULL, CFSTR("org.openldap.slapd"), NULL, NULL);
if (session == NULL) {
return NULL;
}
CFStringRef primaryInterfaceKey = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL, (CFStringRef)kSCDynamicStoreDomainState, (CFStringRef)kSCEntNetIPv4);
if (primaryInterfaceKey != NULL) {
CFDictionaryRef primaryInterfaceDict = SCDynamicStoreCopyValue(session, primaryInterfaceKey);
if (primaryInterfaceDict) {
if (CFGetTypeID(primaryInterfaceDict) == CFDictionaryGetTypeID()) {
CFStringRef primaryInterfaceName = CFDictionaryGetValue(primaryInterfaceDict, kSCDynamicStorePropNetPrimaryInterface);
if (primaryInterfaceName) {
CFIndex size = CFStringGetMaximumSizeForEncoding(CFStringGetLength(primaryInterfaceName), kCFStringEncodingUTF8) + 1;
primaryInterfaceNameCStr = malloc(size);
if (primaryInterfaceNameCStr) {
if (!CFStringGetCString(primaryInterfaceName, primaryInterfaceNameCStr, size, kCFStringEncodingUTF8)) {
free(primaryInterfaceNameCStr);
primaryInterfaceNameCStr = NULL;
}
}
}
}
CFRelease(primaryInterfaceDict);
}
CFRelease(primaryInterfaceKey);
}
CFRelease(session);
session = NULL;
if (!primaryInterfaceNameCStr) {
return NULL;
}
struct ifaddrs *ifap = NULL;
if (getifaddrs(&ifap) != 0) {
return NULL;
}
struct ifaddrs *ifa;
for (ifa = ifap; ifa != NULL; ifa = ifa->ifa_next)
{
if (ifa->ifa_name == NULL) continue;
if (ifa->ifa_addr == NULL) continue;
if (strncmp(ifa->ifa_name, "lo", 2) == 0) continue;
if (ifa->ifa_addr->sa_family == AF_INET) {
if (strcmp(ifa->ifa_name, primaryInterfaceNameCStr) == 0) {
char ipv4AddressCStr[INET_ADDRSTRLEN] = { 0 };
if (inet_ntop(AF_INET, &((struct sockaddr_in*)ifa->ifa_addr)->sin_addr.s_addr, ipv4AddressCStr, sizeof(ipv4AddressCStr))) {
ret = strdup(ipv4AddressCStr);
break;
}
}
}
}
freeifaddrs(ifap);
return ret;
}