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