#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 "applehelpers.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 *lastLoginAD = NULL;
static AttributeDescription *disableReasonAD = 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 *usere = 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 = fakeop->o_ndn = *dn;
fakeop->o_req_dn = fakeop->o_req_ndn = *dn;
usere = odusers_copy_entry(fakeop);
if(!usere) {
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(usere, guidstr) != 0) {
Debug(LDAP_DEBUG_TRACE, "%s: Could not locate authguid for record %s", __PRETTY_FUNCTION__, dn->bv_val, 0);
goto out;
}
entry_free(usere);
usere = NULL;
authdn.bv_len = asprintf(&authdn.bv_val, "authGUID=%s,cn=users,cn=authdata", guidstr);
fakeop->o_dn = fakeop->o_ndn = fakeop->o_req_dn = fakeop->o_req_ndn = authdn;
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(usere) entry_free(usere);
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 CFDictionaryRef CopyPolicyToDict(const char *policyplist, int len) {
CFDataRef xmlData = NULL;
CFDictionaryRef ret = NULL;
CFErrorRef err = NULL;
xmlData = CFDataCreate(kCFAllocatorDefault, (const unsigned char*)policyplist, len);
if(!xmlData) goto out;
ret = (CFDictionaryRef)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) {
unsigned int tmpbool = 0;
CFNumberRef anumber = CFDictionaryGetValue(user, policy);
if(anumber) CFNumberGetValue(anumber, kCFNumberIntType, &tmpbool);
if(!tmpbool) {
anumber = CFDictionaryGetValue(global, policy);
if(anumber) CFNumberGetValue(anumber, kCFNumberIntType, &tmpbool);
if(tmpbool) {
CFDictionarySetValue(merged, policy, anumber);
}
}
return;
}
static CFMutableDictionaryRef CopyEffectivePolicy(CFDictionaryRef global, CFDictionaryRef user) {
CFMutableDictionaryRef ret = CFDictionaryCreateMutableCopy(kCFAllocatorDefault, 0, user);
MergePolicyIntValue(global, user, ret, CFSTR("requiresMixedCase"));
MergePolicyIntValue(global, user, ret, CFSTR("requiresSymbol"));
MergePolicyIntValue(global, user, ret, CFSTR("requiresAlpha"));
MergePolicyIntValue(global, user, ret, CFSTR("requiresNumeric"));
MergePolicyIntValue(global, user, ret, CFSTR("passwordCannotBeName"));
MergePolicyIntValue(global, user, ret, CFSTR("maxMinutesUntilChangePasswo"));
MergePolicyIntValue(global, user, ret, CFSTR("maxMinutesUntilDisabled"));
MergePolicyIntValue(global, user, ret, CFSTR("maxMinutesOfNonUse"));
MergePolicyIntValue(global, user, ret, CFSTR("maxFailedLoginAttempts"));
MergePolicyIntValue(global, user, ret, CFSTR("minChars"));
MergePolicyIntValue(global, user, ret, CFSTR("maxChars"));
MergePolicyIntValue(global, user, ret, CFSTR("usingHistory"));
unsigned int tmpint = 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, 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) 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"));
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);
}
}
}
}
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);
}
}
tmpshort = 0;
CFNumberRef usingHardExpire = CFDictionaryGetValue(policy, CFSTR("usingHardExpirationDate"));
if(usingHardExpire) CFNumberGetValue(usingHardExpire, kCFNumberShortType, &tmpshort);
if(tmpshort) {
CFDateRef hardExpire = CFDictionaryGetValue(policy, CFSTR("hardExpireDateGMT"));
if(hardExpire) {
if(CFDateCompare(hardExpire, now, NULL) != kCFCompareGreaterThan) {
ret = kDisabledExpired;
Debug(LDAP_DEBUG_TRACE, "%s: disabling due to hard expire", __PRETTY_FUNCTION__, 0, 0);
}
}
}
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;
}
}
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;
}
}
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;
}
}
if(ret != 0) {
int one = 1;
CFNumberRef cfone = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &one);
CFDictionarySetValue(policy, CFSTR("isDisabled"), cfone);
CFRelease(cfone);
}
if(now) CFRelease(now);
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(CFDataGetBytePtr(xmlData), CFDataGetLength(xmlData), 1, NULL);
CFRelease(xmlData);
return ret;
}
CFDictionaryRef odusers_copy_effectiveuserpoldict(struct berval *dn) {
Entry *e = NULL;
Attribute *a;
Attribute *global_attr = 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;
}
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) {
Attribute *effective = NULL;
global_attr = odusers_copy_globalpolicy();
CFDictionaryRef globaldict = NULL;
CFMutableDictionaryRef effectivedict = NULL;
CFDictionaryRef userdict = NULL;
Attribute *failedLogins = NULL;
Attribute *creationDate = NULL;
Attribute *lastLogin = NULL;
Attribute *disableReason = NULL;
int disableReasonInt = 0;
const char *text = NULL;
CFDateRef lastLoginCF = NULL;
CFDateRef creationDateCF = NULL;
uint16_t loginattempts = 0;
int one = 1;
CFNumberRef cfone = NULL;
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;
}
}
userdict = CopyPolicyToDict(a->a_vals[0].bv_val, a->a_vals[0].bv_len);
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);
CFRelease(globaldict);
globaldict = NULL;
CFRelease(userdict);
userdict = NULL;
if(!effectivedict) {
Debug(LDAP_DEBUG_ANY, "%s: Unable to compute effective policy from global and user dictionaries", __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(!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;
}
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);
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;
}
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, &loginattempts, disableReasonInt);
if(lastLoginCF) CFRelease(lastLoginCF);
if(creationDateCF) CFRelease(creationDateCF);
if (e) entry_free(e);
return effectivedict;
}
}
out:
if (global_attr) attr_free(global_attr);
if (e) entry_free(e);
return NULL;
}
bool odusers_isdisabled(CFDictionaryRef policy) {
short tmpshort = 0;
CFNumberRef isdisabled = CFDictionaryGetValue(policy, CFSTR("isDisabled"));
if(isdisabled) CFNumberGetValue(isdisabled, kCFNumberShortType, &tmpshort);
if(tmpshort) {
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_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_calloc(sizeof(Modifications), 2);
mod->sml_op = optype;
mod->sml_flags = 0;
mod->sml_type = failedLoginsAD->ad_cname;
mod->sml_values = (struct berval*) ch_calloc(sizeof(struct berval), 2);
ber_str2bv(attemptsstr, strlen(attemptsstr), 1, &mod->sml_values[0]);
if (attemptsstr) free(attemptsstr);
attemptsstr = NULL;
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_calloc(sizeof(struct berval), 2);
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_calloc(sizeof(struct berval), 2);
char time_str[256] = {0};
int time_str_len = 0;
time_str_len = strftime(time_str, sizeof(time_str), "%Y%m%d%H%M%SZ", &tmptm);
ber_str2bv(time_str, time_str_len, 1, &mod->sml_values[0]);
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;
}
CFDictionaryRef odusers_copy_pwsprefs(const char *suffix) {
OperationBuffer opbuf;
Connection conn;
Operation *fakeop = NULL;
Entry *e = NULL;
struct berval dn;
CFDictionaryRef ret = NULL;
char *searchdn = NULL;
asprintf(&searchdn, "cn=passwordserver,cn=config%s", suffix);
connection_fake_init2(&conn, &opbuf, ldap_pvt_thread_pool_context(), 0);
dn.bv_val = searchdn;
dn.bv_len = strlen(dn.bv_val);
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) goto out;
Attribute *a;
for(a = e->e_attrs; a; a = a->a_next) {
if(strncmp(a->a_desc->ad_cname.bv_val, "apple-xmlplist", a->a_desc->ad_cname.bv_len) == 0) {
ret = CopyPolicyToDict(a->a_vals[0].bv_val, a->a_vals[0].bv_len);
break;
}
}
out:
if(e) entry_free(e);
if(searchdn) free(searchdn);
return ret;
}