1#include "portable.h"
2#ifdef SLAPD_OVER_ODUSERS
3#include "overlayutils.h"
4
5#include <arpa/inet.h>
6#define ODUSERS_BACK_CONFIG 1
7
8#include <ac/string.h>
9#include <ac/ctype.h>
10#include <uuid/uuid.h>
11#include "slap.h"
12#include "ldif.h"
13#include "config.h"
14#define __COREFOUNDATION_CFFILESECURITY__
15#include <CoreFoundation/CoreFoundation.h>
16#include "applehelpers.h"
17
18extern AttributeDescription *policyAD;
19extern AttributeDescription *passwordRequiredDateAD;
20
21static AttributeDescription *uidAD = NULL;
22static AttributeDescription *krbAD = NULL;
23static AttributeDescription *draftkrbAD = NULL;
24static AttributeDescription *draftkrbAliasesAD = NULL;
25static AttributeDescription *aaAD = NULL;
26static AttributeDescription *altsecAD = NULL;
27
28static slap_overinst odusers;
29static ConfigDriver odusers_cf;
30static int odusers_add_authdata(Operation *op, SlapReply *rs, uuid_t newuuid);
31
32#define kDirservConfigName "cn=dirserv,cn=config"
33#define ODUSERS_EXTRA_KEY "odusers_key"
34
35typedef struct OpExtraOD {
36	OpExtra oe;
37	uuid_t uuid;
38} OpExtraOD;
39
40static ConfigTable odcfg[] = {
41	{ "odusers", "enabled", 1, 1, 0,
42	  ARG_MAGIC, odusers_cf,
43	  "( OLcfgOvAt:700.11 NAME 'olcODUsersEnabled' "
44	  "DESC 'Enable OD Users overlay' "
45	  "EQUALITY booleanMatch "
46	  "SYNTAX OMsBoolean SINGLE-VALUE )", NULL, NULL },
47	{ NULL, NULL, 0, 0, 0, ARG_IGNORED }
48};
49
50static ConfigOCs odocs[] = {
51	{ "( OLcfgOvOc:700.11 "
52	    "NAME 'olcODUsers' "
53	    "DESC 'OD Users Overlay configuration' "
54	    "SUP olcOverlayConfig "
55	    "MAY (olcODUsersEnabled) )",
56	    Cft_Overlay, odcfg, NULL, NULL },
57	{ NULL, 0, NULL }
58};
59
60#undef calloc
61#undef malloc
62#undef free
63
64
65static int odusers_cf(ConfigArgs *c) {
66	slap_overinst *on = (slap_overinst *)c->bi;
67	return 1;
68}
69
70static bool odusers_isaccount(Operation *op) {
71	bool ret = false;
72
73	if(strnstr(op->o_req_ndn.bv_val, "cn=users", op->o_req_ndn.bv_len) != NULL) ret = 1;
74	if(strnstr(op->o_req_ndn.bv_val, "cn=computers", op->o_req_ndn.bv_len) != NULL) ret = 1;
75
76	return ret;
77}
78
79static int odusers_delete(Operation *op, SlapReply *rs) {
80	OperationBuffer opbuf;
81	Operation *fakeop;
82	Entry *e = NULL;
83	Entry *p = NULL;
84	char guidstr[37];
85	struct berval *dn = &op->o_req_ndn;
86
87	if(op->o_req_ndn.bv_len < 14 || !(strnstr(op->o_req_ndn.bv_val, "cn=users,", op->o_req_ndn.bv_len)!=NULL || strnstr(op->o_req_ndn.bv_val, "cn=computers,", op->o_req_ndn.bv_len)!=NULL) || strnstr(op->o_req_ndn.bv_val, "cn=authdata", op->o_req_ndn.bv_len)!=NULL) {
88		goto out;
89	}
90
91	// Fake up a new Operation, but use the current Connection structure
92	memset(&opbuf, 0, sizeof(opbuf));
93	fakeop = (Operation*)&opbuf;
94	fakeop->o_hdr = &opbuf.ob_hdr;
95	fakeop->o_controls = opbuf.ob_controls;
96	operation_fake_init(op->o_conn, (Operation*)&opbuf, ldap_pvt_thread_pool_context(), 0);
97	fakeop = &opbuf.ob_op;
98	fakeop->o_dn = fakeop->o_ndn = op->o_ndn;
99
100	dnParent(&op->o_req_ndn, &fakeop->o_req_ndn);
101	if(fakeop->o_req_ndn.bv_len < 1) {
102		goto out;
103	}
104
105	fakeop->o_req_dn = fakeop->o_req_ndn;
106	p = odusers_copy_entry(fakeop);
107	if(!p) {
108		goto out;
109	}
110
111	// First check for delete access to children of the parent
112	if(!access_allowed(fakeop, p, slap_schema.si_ad_children, NULL, ACL_WDEL, NULL)) {
113		Debug(LDAP_DEBUG_ANY, "%s: access denied: %s attempted to delete child of %s", __PRETTY_FUNCTION__, fakeop->o_ndn.bv_val, p->e_dn);
114		goto out;
115	}
116
117	// Find the entry we're trying to delete
118	fakeop->o_req_dn = fakeop->o_req_ndn = *dn;
119	e = odusers_copy_entry(fakeop);
120	if(!e) {
121		Debug(LDAP_DEBUG_ANY, "%s: No entry associated with %s\n", __PRETTY_FUNCTION__, dn->bv_val, 0);
122		goto out;
123	}
124
125	// Check for delete access of the specific entry
126	if(!access_allowed(fakeop, e, slap_schema.si_ad_entry, NULL, ACL_WDEL, NULL)) {
127		Debug(LDAP_DEBUG_ANY, "%s: access denied: %s attempted to delete %s", __PRETTY_FUNCTION__, fakeop->o_ndn.bv_val, fakeop->o_req_ndn.bv_val);
128		goto out;
129	}
130
131	if( odusers_get_authguid(e, guidstr) ) {
132		goto out;
133	}
134
135	// Perform the removal
136	if( odusers_remove_authdata(guidstr) != 0 ) {
137		goto out;
138	}
139
140out:
141	if(e) entry_free(e);
142	if(p) entry_free(p);
143	return SLAP_CB_CONTINUE;
144}
145
146/* bridges an authdata attribute to a user container request */
147static int odusers_search_bridge_authdata(Operation *op, SlapReply *rs, const char *reqattr) {
148	OperationBuffer opbuf;
149	Operation *fakeop;
150	Connection conn = {0};
151	Entry *e = NULL;
152	Attribute *a = NULL;
153	Entry *retentry = NULL;
154	char guidstr[37];
155
156	dnNormalize( 0, NULL, NULL, &op->o_req_dn, &op->o_req_ndn, NULL );
157	e = odusers_copy_authdata(&op->o_req_ndn);
158	if(!e) {
159		Debug(LDAP_DEBUG_ANY, "%s: No entry associated with %s\n", __func__, op->o_req_ndn.bv_val, 0);
160		goto out;
161	}
162
163	for(a = e->e_attrs; a; a = a->a_next) {
164		if(strncmp(a->a_desc->ad_cname.bv_val, reqattr, a->a_desc->ad_cname.bv_len) == 0) {
165			retentry = entry_alloc();
166			if(!retentry) goto out;
167
168			retentry->e_id = NOID;
169			retentry->e_name = op->o_req_dn;
170			retentry->e_nname = op->o_req_ndn;
171
172			retentry->e_attrs = attr_dup(a);
173			if(!retentry->e_attrs) {
174				Debug(LDAP_DEBUG_ANY, "%s: could not duplicate entry: %s", __func__, retentry->e_name.bv_val, 0);
175				goto out;
176			}
177
178			op->ors_slimit = -1;
179			rs->sr_entry = retentry;
180			rs->sr_nentries = 0;
181			rs->sr_flags = 0;
182			rs->sr_ctrls = NULL;
183			rs->sr_operational_attrs = NULL;
184			rs->sr_attrs = op->ors_attrs;
185			rs->sr_err = LDAP_SUCCESS;
186			rs->sr_err = send_search_entry(op, rs);
187			if(rs->sr_err == LDAP_SIZELIMIT_EXCEEDED) {
188				Debug(LDAP_DEBUG_ANY, "%s: size limit exceeded on entry: %s", __func__, retentry->e_name.bv_val, 0);
189			}
190			rs->sr_entry = NULL;
191			send_ldap_result(op, rs);
192			if(e) entry_free(e);
193			return rs->sr_err;
194		}
195	}
196
197out:
198	if(e) entry_free(e);
199//	if (retentry) entry_free(retentry);
200	return SLAP_CB_CONTINUE;
201}
202
203static int odusers_search_accountpolicy_proxy(Operation *op, SlapReply *rs, char *attr_str) {
204	Entry *retentry = NULL;
205	Attribute *attr = NULL;
206	Filter *filter = NULL;
207	AttributeAssertion *ava = NULL;
208	char *tmpstr = NULL;
209	struct berval dn;
210
211	CFDictionaryRef userpolicyinfodict = NULL;
212	__block CFDictionaryRef policyData = NULL;
213	CFStringRef globalaccountpolicyGUID = NULL;
214	CFErrorRef cferr = NULL;
215	int64_t	policyresult = 0;
216	int64_t willExpireIn = 0;
217	bool override = false;
218
219	bool isaccount = odusers_isaccount(op);
220
221	dn = op->o_req_ndn;
222
223	if(!BER_BVISEMPTY(&dn)) {
224		if (op->ors_filter) {
225			ava = op->ors_filter->f_ava;
226			filter = op->ors_filter;
227		}
228
229		if (isaccount) {
230			userpolicyinfodict = odusers_copy_accountpolicyinfo(&dn);
231			override = odusers_accountpolicy_override(&dn);
232		} else if (strncmp(op->o_req_ndn.bv_val, kDirservConfigName, strlen(kDirservConfigName)) == 0) {
233			userpolicyinfodict = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
234			/* kAPAttributeGlobalPoliciesGUID */
235			globalaccountpolicyGUID = odusers_copy_globalaccountpolicyGUID();
236			if (globalaccountpolicyGUID) {
237				CFDictionarySetValue(userpolicyinfodict, kAPAttributeGlobalPoliciesGUID, globalaccountpolicyGUID);
238			}
239		}
240
241		if (userpolicyinfodict) {
242			if (override) { /* bypass AccountPolicy evaluation and return 'pass by default' */
243				if ( strcmp(attr_str, "apple-authenticationAllowed") == 0 || strcmp(attr_str, "apple-passwordChangeAllowed") == 0 ) {
244					policyresult = 1;
245				} else if (	strcmp(attr_str, "apple-willPasswordExpire") == 0 || strcmp(attr_str, "apple-willAuthenticationsExpire") == 0 ) {
246					policyresult = 0;
247				} else if (strcmp(attr_str, "apple-secondsUntilPasswordExpires") == 0 || strcmp(attr_str, "apple-secondsUntilAuthenticationsExpire") == 0) {
248					policyresult = -1;
249				}
250			} else if (strcmp(attr_str, "apple-authenticationAllowed") == 0) {
251				policyresult = APAuthenticationAllowed(userpolicyinfodict, true, &cferr,        ^(CFArrayRef keys){ return odusers_accountpolicy_retrievedata(&policyData, keys, &dn); }, NULL) ? (int64_t) 1 : (int64_t) CFErrorGetCode(cferr);
252			} else if (strcmp(attr_str, "apple-passwordChangeAllowed") == 0) {
253					Filter *flist = NULL;
254					if (filter ) {
255						if ( filter->f_choice == LDAP_FILTER_AND ) { /* "(&(uid=user1)(userPassword=abcd))" */
256							flist = filter->f_and;
257						} else if ( filter->f_choice == LDAP_FILTER_EQUALITY ) {  /* "(userPassword=abcd)" */
258							flist = filter;
259						}
260						if (flist && flist->f_choice == LDAP_FILTER_EQUALITY)  { /* (attr=value) */
261							CFStringRef passcfstr = NULL;
262							CFStringRef uidcfstr = NULL;
263							for ( ; flist ; flist = flist->f_next) {
264								if (flist->f_av_desc == slap_schema.si_ad_userPassword && flist->f_av_value.bv_val) {
265									passcfstr =  CFStringCreateWithBytes(kCFAllocatorDefault, flist->f_av_value.bv_val, flist->f_av_value.bv_len, kCFStringEncodingUTF8, false);
266									CFDictionarySetValue(userpolicyinfodict, kAPAttributePassword, passcfstr);
267								} else if (flist->f_av_desc == slap_schema.si_ad_uid  && flist->f_av_value.bv_val) {
268									uidcfstr =  CFStringCreateWithBytes(kCFAllocatorDefault, flist->f_av_value.bv_val, flist->f_av_value.bv_len, kCFStringEncodingUTF8, false);
269									CFDictionarySetValue(userpolicyinfodict, kAPAttributeRecordName, uidcfstr);
270								}
271							}
272							if (passcfstr) {
273								policyresult = (int64_t) APPasswordChangeAllowed(userpolicyinfodict, &cferr,	^(CFArrayRef keys){ return odusers_accountpolicy_retrievedata(&policyData, keys, &dn); }, NULL);
274							} else {
275								Debug(LDAP_DEBUG_ANY, "%s: APPasswordChangeAllowed - parameter error\n", __func__, 0, 0);
276							}
277							if (passcfstr) CFRelease(passcfstr);
278							if (uidcfstr) CFRelease(uidcfstr);
279						}
280					}
281			} else if (strcmp(attr_str, "apple-willPasswordExpire") == 0) {
282					if (ava && ava->aa_value.bv_val) {
283						willExpireIn = strtoll(ava->aa_value.bv_val, NULL, 10);
284						policyresult = (int64_t) APWillPasswordExpire(willExpireIn, userpolicyinfodict, ^(CFArrayRef keys){ return odusers_accountpolicy_retrievedata(&policyData, keys, &dn); });
285						Debug(LDAP_DEBUG_ANY, "%s: APWillPasswordExpire %lld \n", __func__, willExpireIn, 0);
286					}
287			} else if (strcmp(attr_str, "apple-willAuthenticationsExpire") == 0) {
288					if (ava && ava->aa_value.bv_val) {
289						willExpireIn = strtoll(ava->aa_value.bv_val, NULL, 10);
290						policyresult = (int64_t) APWillAuthenticationsExpire(willExpireIn, userpolicyinfodict, ^(CFArrayRef keys){ return odusers_accountpolicy_retrievedata(&policyData, keys, &dn); });
291						Debug(LDAP_DEBUG_ANY, "%s: APWillAuthenticationsExpire %lld \n", __func__, willExpireIn, 0);
292					}
293			} else if (strcmp(attr_str, "apple-secondsUntilPasswordExpires") == 0) {
294					policyresult = APSecondsUntilPasswordExpiration(userpolicyinfodict, ^(CFArrayRef keys){ return odusers_accountpolicy_retrievedata(&policyData, keys, &dn); });
295			} else if (strcmp(attr_str, "apple-secondsUntilAuthenticationsExpire") == 0) {
296					policyresult = APSecondsUntilAuthenticationExpiration(userpolicyinfodict, ^(CFArrayRef keys){ return odusers_accountpolicy_retrievedata(&policyData, keys, &dn); });
297			}
298
299			AttributeDescription *attrDesc = NULL;
300			const char *text = NULL;
301			if(slap_str2ad(attr_str, &attrDesc, &text) != 0) {
302				Debug(LDAP_DEBUG_ANY, "%s: slap_str2ad failed for %s", __PRETTY_FUNCTION__, attr_str, 0);
303				goto out;
304			}
305
306			asprintf(&tmpstr, "%lld", policyresult);
307			attr = attr_alloc(attrDesc);
308			attr->a_flags |= SLAP_ATTR_DONT_FREE_DATA;
309			attr->a_vals = ch_malloc(2 * sizeof(struct berval));
310			attr->a_vals[0].bv_val = tmpstr;
311			attr->a_vals[0].bv_len = strlen(tmpstr);
312			attr->a_vals[1].bv_len = 0;
313			attr->a_vals[1].bv_val = NULL;
314			attr->a_nvals = attr->a_vals;
315
316		} else {
317			goto out;
318		}
319	} else {
320		goto out;
321	}
322
323	retentry = entry_alloc();
324	if(!retentry) goto out;
325
326	retentry->e_id = NOID;
327	ber_dupbv(&retentry->e_name, &op->o_req_dn);
328	ber_dupbv(&retentry->e_nname, &op->o_req_ndn);
329
330	if (attr) {
331		retentry->e_attrs = attr;
332	} else {
333		retentry->e_attrs = NULL;
334	}
335
336	op->ors_slimit = -1;
337	rs->sr_entry = retentry;
338	rs->sr_nentries = 0;
339	rs->sr_flags = 0;
340	rs->sr_ctrls = NULL;
341	rs->sr_operational_attrs = NULL;
342	rs->sr_attrs = op->ors_attrs;
343	rs->sr_err = LDAP_SUCCESS;
344	rs->sr_err = send_search_entry(op, rs);
345	if(rs->sr_err == LDAP_SIZELIMIT_EXCEEDED) {
346		Debug(LDAP_DEBUG_ANY, "%s: size limit exceeded on entry: %s", __func__, retentry->e_name.bv_val, 0);
347	}
348	rs->sr_entry = NULL;
349	send_ldap_result(op, rs);
350	if(retentry) entry_free(retentry);
351	if (userpolicyinfodict) CFRelease(userpolicyinfodict);
352	if (policyData) CFRelease(policyData);
353	if (globalaccountpolicyGUID) CFRelease(globalaccountpolicyGUID);
354	if (cferr) CFRelease(cferr);
355	free(tmpstr);
356	return rs->sr_err;
357
358
359out:
360	if(retentry) entry_free(retentry);
361	if (userpolicyinfodict) CFRelease(userpolicyinfodict);
362	if (policyData) CFRelease(policyData);
363	if (globalaccountpolicyGUID) CFRelease(globalaccountpolicyGUID);
364	if (cferr) CFRelease(cferr);
365	return SLAP_CB_CONTINUE;
366}
367
368static int odusers_search_bridge_accountpolicy(Operation *op, SlapReply *rs) {
369	OperationBuffer opbuf;
370	Operation *fakeop;
371	Connection conn = {0};
372	Entry *e = NULL;
373	Attribute *a = NULL;
374	Entry *retentry = NULL;
375	char guidstr[37];
376	Attribute *pwsPolicyAttr = NULL;
377
378	dnNormalize( 0, NULL, NULL, &op->o_req_dn, &op->o_req_ndn, NULL );
379	e = odusers_copy_authdata(&op->o_req_ndn);
380	if(!e) {
381		Debug(LDAP_DEBUG_ANY, "%s: No entry associated with %s\n", __func__, op->o_req_ndn.bv_val, 0);
382		goto out;
383	}
384
385	/* translate  PasswordServer 'apple-user-passwordpolicy' queries from new 'apple-accountpolicy' format */
386	CFDictionaryRef accountpolicy = odusers_copy_accountpolicy_fromentry(e);
387	if (accountpolicy) {
388		CFDictionaryRef pwsPolicy = APLegacyPoliciesWithPolicySet(accountpolicy);
389		if (pwsPolicy) {
390			AttributeDescription *pwsPolicydesc = NULL;
391			const char *text = NULL;
392			if(slap_str2ad("apple-user-passwordpolicy", &pwsPolicydesc, &text) != 0) {
393				Debug(LDAP_DEBUG_ANY, "%s: slap_str2ad failed for global policy", __PRETTY_FUNCTION__, 0, 0);
394				goto out;
395			}
396
397			struct berval *bv = odusers_copy_dict2bv(pwsPolicy);
398			CFRelease(pwsPolicy);
399
400			pwsPolicyAttr = attr_alloc(pwsPolicydesc);
401			pwsPolicyAttr->a_vals = ch_malloc(2 * sizeof(struct berval));
402			pwsPolicyAttr->a_vals[0] = *bv;
403			pwsPolicyAttr->a_vals[1].bv_len = 0;
404			pwsPolicyAttr->a_vals[1].bv_val = NULL;
405			pwsPolicyAttr->a_nvals = pwsPolicyAttr->a_vals;
406		}
407		CFRelease(accountpolicy);
408	}
409
410	retentry = entry_alloc();
411	if(!retentry) goto out;
412
413	retentry->e_id = NOID;
414	retentry->e_name = op->o_req_dn;
415	retentry->e_nname = op->o_req_ndn;
416
417	if (pwsPolicyAttr) {
418		retentry->e_attrs = pwsPolicyAttr;
419	} else {
420		retentry->e_attrs = NULL;
421	}
422
423	op->ors_slimit = -1;
424	rs->sr_entry = retentry;
425	rs->sr_nentries = 0;
426	rs->sr_flags = 0;
427	rs->sr_ctrls = NULL;
428	rs->sr_operational_attrs = NULL;
429	rs->sr_attrs = op->ors_attrs;
430	rs->sr_err = LDAP_SUCCESS;
431	rs->sr_err = send_search_entry(op, rs);
432	if(rs->sr_err == LDAP_SIZELIMIT_EXCEEDED) {
433		Debug(LDAP_DEBUG_ANY, "%s: size limit exceeded on entry: %s", __func__, retentry->e_name.bv_val, 0);
434	}
435	rs->sr_entry = NULL;
436	send_ldap_result(op, rs);
437	if(e) entry_free(e);
438	return rs->sr_err;
439
440
441out:
442	if(e) entry_free(e);
443	return SLAP_CB_CONTINUE;
444
445}
446
447static int odusers_search_effective_userpolicy(Operation *op, SlapReply *rs) {
448	Attribute *effective = NULL;
449	const char *text = NULL;
450	CFDictionaryRef effectivedict = odusers_copy_effectiveuserpoldict(&op->o_req_ndn);
451	if(!effectivedict) {
452		Debug(LDAP_DEBUG_ANY, "%s: Unable to retrieve effective policy for %s\n", __func__, op->o_req_ndn.bv_val, 0);
453		goto out;
454	}
455
456	Entry *retentry = entry_alloc();
457	if(!retentry) goto out;
458
459	retentry->e_id = NOID;
460	retentry->e_name = op->o_req_dn;
461	retentry->e_nname = op->o_req_ndn;
462
463	struct berval *bv = odusers_copy_dict2bv(effectivedict);
464	CFRelease(effectivedict);
465	if(!bv) {
466		Debug(LDAP_DEBUG_ANY, "%s: Unable to convert effective policy to berval", __PRETTY_FUNCTION__, 0, 0);
467		goto out;
468	}
469
470	AttributeDescription *effectivedesc = NULL;
471	if(slap_str2ad("apple-user-passwordpolicy-effective", &effectivedesc, &text) != 0) {
472		Debug(LDAP_DEBUG_ANY, "%s: slap_str2ad failed for effective policy", __PRETTY_FUNCTION__, 0, 0);
473		goto out;
474	}
475
476	effective = attr_alloc(effectivedesc);
477	effective->a_vals = ch_malloc(2 * sizeof(struct berval));
478	effective->a_vals[0] = *bv;
479	ch_free(bv);
480	effective->a_vals[1].bv_len = 0;
481	effective->a_vals[1].bv_val = NULL;
482	effective->a_nvals = effective->a_vals;
483
484	retentry->e_attrs = effective;
485
486	op->ors_slimit = -1;
487	rs->sr_entry = retentry;
488	rs->sr_nentries = 0;
489	rs->sr_flags = 0;
490	rs->sr_ctrls = NULL;
491	rs->sr_operational_attrs = NULL;
492	rs->sr_attrs = op->ors_attrs;
493	rs->sr_err = LDAP_SUCCESS;
494	rs->sr_err = send_search_entry(op, rs);
495	if(rs->sr_err == LDAP_SIZELIMIT_EXCEEDED) {
496		Debug(LDAP_DEBUG_ANY, "%s: size limit exceeded on entry: %s", __PRETTY_FUNCTION__, retentry->e_name.bv_val, 0);
497	}
498	rs->sr_entry = NULL;
499	send_ldap_result(op, rs);
500	return rs->sr_err;
501
502out:
503//	if (retentry) entry_free(retentry);
504	return SLAP_CB_CONTINUE;
505}
506static int odusers_search_global_accountpolicy(Operation *op, SlapReply *rs) {
507	Attribute *attr = odusers_copy_attr("cn=access,cn=authdata", "apple-accountpolicy");
508
509	Entry *retentry = entry_alloc();
510	if(!retentry) goto out;
511
512	retentry->e_id = NOID;
513	retentry->e_name = op->o_req_dn;
514	retentry->e_nname = op->o_req_ndn;
515	retentry->e_attrs = attr;
516
517	op->ors_slimit = -1;
518	rs->sr_entry = retentry;
519	rs->sr_nentries = 0;
520	rs->sr_flags = 0;
521	rs->sr_ctrls = NULL;
522	rs->sr_operational_attrs = NULL;
523	rs->sr_attrs = op->ors_attrs;
524	rs->sr_err = LDAP_SUCCESS;
525	rs->sr_err = send_search_entry(op, rs);
526	if(rs->sr_err == LDAP_SIZELIMIT_EXCEEDED) {
527		Debug(LDAP_DEBUG_ANY, "%s: size limit exceeded on entry: %s", __PRETTY_FUNCTION__, retentry->e_name.bv_val, 0);
528	}
529	rs->sr_entry = NULL;
530	send_ldap_result(op, rs);
531	return rs->sr_err;
532
533out:
534	if(attr) attr_free(attr);
535	return SLAP_CB_CONTINUE;
536}
537
538static int odusers_search_globalpolicy(Operation *op, SlapReply *rs) {
539	Attribute *global = odusers_copy_globalpolicy();
540	if(!global) {
541		CFDictionaryRef globaldict = odusers_copy_defaultglobalpolicy();
542		struct berval *bv = odusers_copy_dict2bv(globaldict);
543		CFRelease(globaldict);
544
545		AttributeDescription *globaldesc = NULL;
546		const char *text = NULL;
547		if(slap_str2ad("apple-user-passwordpolicy", &globaldesc, &text) != 0) {
548			Debug(LDAP_DEBUG_ANY, "%s: slap_str2ad failed for global policy", __PRETTY_FUNCTION__, 0, 0);
549			goto out;
550		}
551
552		global = attr_alloc(globaldesc);
553		global->a_vals = ch_malloc(2 * sizeof(struct berval));
554		global->a_vals[0] = *bv;
555		global->a_vals[1].bv_len = 0;
556		global->a_vals[1].bv_val = NULL;
557		global->a_nvals = global->a_vals;
558	}
559
560	Entry *retentry = entry_alloc();
561	if(!retentry) goto out;
562
563	retentry->e_id = NOID;
564	retentry->e_name = op->o_req_dn;
565	retentry->e_nname = op->o_req_ndn;
566	retentry->e_attrs = global;
567
568	op->ors_slimit = -1;
569	rs->sr_entry = retentry;
570	rs->sr_nentries = 0;
571	rs->sr_flags = 0;
572	rs->sr_ctrls = NULL;
573	rs->sr_operational_attrs = NULL;
574	rs->sr_attrs = op->ors_attrs;
575	rs->sr_err = LDAP_SUCCESS;
576	rs->sr_err = send_search_entry(op, rs);
577	if(rs->sr_err == LDAP_SIZELIMIT_EXCEEDED) {
578		Debug(LDAP_DEBUG_ANY, "%s: size limit exceeded on entry: %s", __PRETTY_FUNCTION__, retentry->e_name.bv_val, 0);
579	}
580	rs->sr_entry = NULL;
581	send_ldap_result(op, rs);
582	return rs->sr_err;
583
584out:
585	if(global) attr_free(global);
586	return SLAP_CB_CONTINUE;
587}
588
589static int odusers_search_pwsprefs(Operation *op, SlapReply *rs) {
590	OperationBuffer opbuf;
591	Operation *fakeop;
592	Connection conn = {0};
593	Entry *e = NULL;
594	Attribute *a;
595	char *saslrealm = NULL;
596	int ret = 0;
597
598	CFMutableDictionaryRef prefs = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
599	int zeroint = 0;
600	CFNumberRef zero = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &zeroint);
601	CFDictionaryAddValue(prefs, CFSTR("BadTrialDelay"), zero);
602	CFRelease(zero);
603
604	CFDictionaryAddValue(prefs, CFSTR("ExternalCommand"), CFSTR("Disabled"));
605
606	CFMutableArrayRef listeners = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
607	CFArrayAppendValue(listeners, CFSTR("Ethernet"));
608	CFArrayAppendValue(listeners, CFSTR("Local"));
609	CFArrayAppendValue(listeners, CFSTR("UNIX Domain Socket"));
610	CFDictionaryAddValue(prefs, CFSTR("ListenerInterfaces"), listeners);
611	CFRelease(listeners);
612
613	CFMutableArrayRef ports = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
614	int oneohsixint = 106;
615	CFNumberRef oneohsix = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &oneohsixint);
616	CFArrayAppendValue(ports, oneohsix);
617	CFRelease(oneohsix);
618	int threesixfivenineint = 3659;
619	CFNumberRef threesixfivenine = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &threesixfivenineint);
620	CFArrayAppendValue(ports, threesixfivenine);
621	CFRelease(threesixfivenine);
622	CFDictionaryAddValue(prefs, CFSTR("ListenerPorts"), ports);
623	CFRelease(ports);
624
625	int threeint = 3;
626	CFNumberRef three = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &threeint);
627	CFDictionaryAddValue(prefs, CFSTR("Preference File Version"), three);
628	CFRelease(three);
629
630	char *suffix = op->o_req_ndn.bv_val + strlen("cn=passwordserver,cn=config,");
631	CFArrayRef mechs = odusers_copy_enabledmechs(suffix);
632	if(mechs) {
633		int i;
634		CFMutableDictionaryRef pluginstates = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
635		for(i = 0; i < CFArrayGetCount(mechs); i++) {
636			CFStringRef mech = CFArrayGetValueAtIndex(mechs, i);
637			CFDictionaryAddValue(pluginstates, mech, CFSTR("ON"));
638		}
639		CFDictionaryAddValue(prefs, CFSTR("SASLPluginStates"), pluginstates);
640		CFRelease(mechs);
641		CFRelease(pluginstates);
642	}
643
644	CFDictionaryAddValue(prefs, CFSTR("SyncSASLPlugInList"), kCFBooleanTrue);
645
646	saslrealm = odusers_copy_saslrealm();
647	if(!saslrealm) {
648		Debug(LDAP_DEBUG_ANY, "%s: unable to find sasl realm\n", __func__, 0, 0);
649		goto out;
650	}
651
652	CFStringRef cfsaslrealm = CFStringCreateWithCString(kCFAllocatorDefault, saslrealm, kCFStringEncodingUTF8);
653	CFDictionaryAddValue(prefs, CFSTR("SASLRealm"), cfsaslrealm);
654	CFRelease(cfsaslrealm);
655	ch_free(saslrealm);
656
657	e = entry_alloc();
658	e->e_id = NOID;
659	e->e_name = op->o_req_dn;
660	e->e_nname = op->o_req_ndn;
661	AttributeDescription *desc = NULL;
662	const char *text;
663	if(slap_str2ad("apple-xmlplist", &desc, &text) != 0) {
664		Debug(LDAP_DEBUG_ANY, "%s: slap_str2ad failed for apple-xmlplist\n", __func__, 0, 0);
665		goto out;
666	}
667	struct berval *bv = odusers_copy_dict2bv(prefs);
668	if(!bv) {
669		Debug(LDAP_DEBUG_ANY, "%s: Unable to convert prefs to berval\n", __func__, 0, 0);
670		goto out;
671	}
672	e->e_attrs = attr_alloc(desc);
673	e->e_attrs->a_vals = ch_malloc(2 * sizeof(struct berval));
674	e->e_attrs->a_vals[0] = *bv;
675	e->e_attrs->a_vals[1].bv_len = 0;
676	e->e_attrs->a_vals[1].bv_val = 0;
677	e->e_attrs->a_nvals = e->e_attrs->a_vals;
678	e->e_attrs->a_numvals = 1;
679	ch_free(bv);
680
681	op->ors_slimit = -1;
682	rs->sr_entry = e;
683	rs->sr_nentries = 1;
684	rs->sr_flags = 0;
685	rs->sr_ctrls = 0;
686	rs->sr_operational_attrs = NULL;
687	rs->sr_attrs = op->ors_attrs;
688	rs->sr_err = LDAP_SUCCESS;
689	rs->sr_err = send_search_entry(op, rs);
690	rs->sr_entry = NULL;
691	send_ldap_result(op, rs);
692	ret = rs->sr_err;
693
694out:
695	if(prefs) CFRelease(prefs);
696	return 0;
697}
698
699static int odusers_search(Operation *op, SlapReply *rs) {
700	bool isaccount;
701
702	if(!op || op->o_req_ndn.bv_len == 0) return SLAP_CB_CONTINUE;
703	if(!op->ors_attrs) return SLAP_CB_CONTINUE;
704	if(strnstr(op->o_req_ndn.bv_val, "cn=authdata", op->o_req_ndn.bv_len) != NULL) return SLAP_CB_CONTINUE;
705
706	isaccount = odusers_isaccount(op);
707
708	if(isaccount && strncmp(op->ors_attrs[0].an_name.bv_val, "apple-user-passwordpolicy", op->ors_attrs[0].an_name.bv_len) == 0) {
709		return odusers_search_bridge_accountpolicy(op, rs);
710	} else if(strncmp(op->ors_attrs[0].an_name.bv_val, "apple-authenticationAllowed", op->ors_attrs[0].an_name.bv_len) == 0 ||
711			   strncmp(op->ors_attrs[0].an_name.bv_val, "apple-passwordChangeAllowed", op->ors_attrs[0].an_name.bv_len) == 0 ||
712			   strncmp(op->ors_attrs[0].an_name.bv_val, "apple-willPasswordExpire", op->ors_attrs[0].an_name.bv_len) == 0 ||
713			   strncmp(op->ors_attrs[0].an_name.bv_val, "apple-willAuthenticationsExpire", op->ors_attrs[0].an_name.bv_len) == 0 ||
714			   strncmp(op->ors_attrs[0].an_name.bv_val, "apple-secondsUntilPasswordExpires", op->ors_attrs[0].an_name.bv_len) == 0 ||
715			   strncmp(op->ors_attrs[0].an_name.bv_val, "apple-secondsUntilAuthenticationsExpire", op->ors_attrs[0].an_name.bv_len) == 0 ) {
716		return odusers_search_accountpolicy_proxy(op, rs, op->ors_attrs[0].an_name.bv_val);
717	} else if(isaccount && strncmp(op->ors_attrs[0].an_name.bv_val, "apple-user-passwordpolicy-effective", op->ors_attrs[0].an_name.bv_len) == 0) {
718		return odusers_search_effective_userpolicy(op, rs);
719	} else if(isaccount && strncmp(op->ors_attrs[0].an_name.bv_val, "draft-krbPrincipalAliases", op->ors_attrs[0].an_name.bv_len) == 0) {
720		return odusers_search_bridge_authdata(op, rs, "draft-krbPrincipalAliases");
721	} else if(isaccount && strncmp(op->ors_attrs[0].an_name.bv_val, "apple-accountpolicy", op->ors_attrs[0].an_name.bv_len) == 0) {
722		return odusers_search_bridge_authdata(op, rs, "apple-accountpolicy");
723	} else if(!isaccount && (strncmp(op->o_req_ndn.bv_val, kDirservConfigName, strlen(kDirservConfigName)) == 0) && strncmp(op->ors_attrs[0].an_name.bv_val, "apple-accountpolicy", op->ors_attrs[0].an_name.bv_len) == 0) {
724		odusers_search_global_accountpolicy(op, rs);
725	} else if(!isaccount && (strncmp(op->o_req_ndn.bv_val, kDirservConfigName, strlen(kDirservConfigName)) == 0) && strncmp(op->ors_attrs[0].an_name.bv_val, "apple-user-passwordpolicy", op->ors_attrs[0].an_name.bv_len) == 0) {
726		return odusers_search_globalpolicy(op, rs);
727	} else if(!isaccount && (strncmp(op->o_req_ndn.bv_val, "cn=passwordserver,cn=config", strlen(kDirservConfigName)) == 0) && strncmp(op->ors_attrs[0].an_name.bv_val, "apple-xmlplist", op->ors_attrs[0].an_name.bv_len) == 0) {
728		return odusers_search_pwsprefs(op, rs);
729	}
730
731	return SLAP_CB_CONTINUE;
732}
733
734static int odusers_insert_vendorName(SlapReply *rs) {
735	Attribute *a = NULL;
736	// Make sure attribute doesn't exist
737	for(a = rs->sr_un.sru_search.r_entry->e_attrs; a; a = a->a_next) {
738		if(strncmp(a->a_desc->ad_cname.bv_val, "vendorName", a->a_desc->ad_cname.bv_len) == 0) {
739			goto out;
740		}
741	}
742
743	AttributeDescription *namedesc = NULL;
744	const char *text = NULL;
745	if(slap_str2ad("vendorName", &namedesc, &text) != 0) {
746		Debug(LDAP_DEBUG_ANY, "%s: slap_str2ad failed for vendorName", __PRETTY_FUNCTION__, 0, 0);
747		goto out;
748	}
749
750	a = attr_alloc(namedesc);
751	a->a_vals = ch_malloc(2 * sizeof(struct berval));
752	a->a_vals[0].bv_val = ch_strdup("Apple") ;
753	a->a_vals[0].bv_len = strlen(a->a_vals[0].bv_val);
754	a->a_vals[1].bv_len = 0;
755	a->a_vals[1].bv_val = NULL;
756	a->a_nvals = a->a_vals;
757
758	a->a_next = rs->sr_un.sru_search.r_entry->e_attrs;
759	rs->sr_un.sru_search.r_entry->e_attrs = a;
760out:
761	return 0;
762}
763
764static int odusers_insert_vendorVersion(SlapReply *rs) {
765	Attribute *a = NULL;
766	// Make sure attribute doesn't exist
767	for(a = rs->sr_un.sru_search.r_entry->e_attrs; a; a = a->a_next) {
768		if(strncmp(a->a_desc->ad_cname.bv_val, "vendorVersion", a->a_desc->ad_cname.bv_len) == 0) {
769			goto out;
770		}
771	}
772
773	AttributeDescription *namedesc = NULL;
774	const char *text = NULL;
775	if(slap_str2ad("vendorVersion", &namedesc, &text) != 0) {
776		Debug(LDAP_DEBUG_ANY, "%s: slap_str2ad failed for vendorVersion", __PRETTY_FUNCTION__, 0, 0);
777		goto out;
778	}
779
780	a = attr_alloc(namedesc);
781	a->a_vals = ch_malloc(2 * sizeof(struct berval));
782	a->a_vals[0].bv_val = ch_strdup(PROJVERSION);
783	a->a_vals[0].bv_len = strlen(a->a_vals[0].bv_val);
784	a->a_vals[1].bv_len = 0;
785	a->a_vals[1].bv_val = NULL;
786	a->a_nvals = a->a_vals;
787
788	a->a_next = rs->sr_un.sru_search.r_entry->e_attrs;
789	rs->sr_un.sru_search.r_entry->e_attrs = a;
790out:
791	return 0;
792}
793
794static int odusers_insert_operatingSystemVersion(SlapReply *rs) {
795	const char* attrName = "operatingSystemVersion";
796	Attribute *a = NULL;
797	// Make sure attribute doesn't exist
798	for(a = rs->sr_un.sru_search.r_entry->e_attrs; a; a = a->a_next) {
799		if(strncmp(a->a_desc->ad_cname.bv_val, attrName, a->a_desc->ad_cname.bv_len) == 0) {
800			goto out;
801		}
802	}
803
804	AttributeDescription *namedesc = NULL;
805	const char *text = NULL;
806	if(slap_str2ad(attrName, &namedesc, &text) != 0) {
807		Debug(LDAP_DEBUG_ANY, "%s: slap_str2ad failed for %s", __PRETTY_FUNCTION__, attrName, 0);
808		goto out;
809	}
810
811	a = attr_alloc(namedesc);
812	a->a_vals = ch_malloc(2 * sizeof(struct berval));
813	a->a_vals[0].bv_val = ch_strdup(TGT_OS_VERSION);
814	a->a_vals[0].bv_len = strlen(a->a_vals[0].bv_val);
815	a->a_vals[1].bv_len = 0;
816	a->a_vals[1].bv_val = NULL;
817	a->a_nvals = a->a_vals;
818
819	a->a_next = rs->sr_un.sru_search.r_entry->e_attrs;
820	rs->sr_un.sru_search.r_entry->e_attrs = a;
821out:
822	return 0;
823}
824
825static int odusers_response(Operation *op, SlapReply *rs) {
826	static char *version = NULL;
827	AttributeName *an = NULL;
828
829	/* If this is a result to an ADD that is a user/computer account */
830	if(op->o_tag == LDAP_REQ_ADD && (strnstr(op->o_req_ndn.bv_val, "cn=authdata", op->o_req_ndn.bv_len) == NULL) && odusers_isaccount(op)) {
831		Debug(LDAP_DEBUG_ANY, "%s: processing response to add of %s\n", __func__, op->o_req_dn.bv_val, 0);
832
833		Attribute *a = NULL;
834		OpExtraOD *oe = NULL;
835		OpExtra *oex = NULL;
836		a = attr_find( op->ora_e->e_attrs, slap_schema.si_ad_entryUUID );
837		if(!a) {
838			Debug(LDAP_DEBUG_ANY, "%s: Could not find entryUUID\n", __func__, 0, 0);
839		} else {
840			Debug(LDAP_DEBUG_ANY, "%s: entryUUID %s\n", __func__, a->a_vals[0].bv_val, 0);
841		}
842
843		LDAP_SLIST_FOREACH(oex, &op->o_extra, oe_next) {
844			if(oex->oe_key == ODUSERS_EXTRA_KEY) {
845				char uuidstr[37];
846				oe = (OpExtraOD*)oex;
847
848				uuidstr[sizeof(uuidstr)-1] = '\0';
849				uuid_unparse_lower(oe->uuid, uuidstr);
850				Debug(LDAP_DEBUG_ANY, "%s: Found uuid: %s\n", __func__, uuidstr, 0);
851				if (rs->sr_err == LDAP_SUCCESS && rs->sr_type == REP_RESULT) { // add authdata on successful creation
852					odusers_add_authdata(op, rs, oe->uuid);
853				}
854				LDAP_SLIST_REMOVE(&op->o_extra, &oe->oe, OpExtra, oe_next);
855				free(oe);
856				break;
857			}
858		}
859
860		return SLAP_CB_CONTINUE;
861	}
862
863	if ((rs->sr_type != REP_SEARCH) || (op->oq_search.rs_attrs == NULL) ) {
864		return SLAP_CB_CONTINUE;
865	}
866
867	if(!op->ors_attrs) return SLAP_CB_CONTINUE;
868
869	// Only interested in the rootDSE
870	if(op->o_req_ndn.bv_len != 0) return SLAP_CB_CONTINUE;
871
872	int i;
873	for(i = 0; op->ors_attrs[i].an_name.bv_len > 0; i++) {
874		if(op->ors_attrs[i].an_name.bv_val == NULL) break;
875		if(strncmp(op->ors_attrs[i].an_name.bv_val, "vendorName", op->ors_attrs[i].an_name.bv_len) == 0) {
876			odusers_insert_vendorName(rs);
877		} else if(strncmp(op->ors_attrs[i].an_name.bv_val, "vendorVersion", op->ors_attrs[i].an_name.bv_len) == 0) {
878			odusers_insert_vendorVersion(rs);
879		} else if(strncmp(op->ors_attrs[i].an_name.bv_val, "operatingSystemVersion", op->ors_attrs[i].an_name.bv_len) == 0) {
880			odusers_insert_operatingSystemVersion(rs);
881		} else if(strncmp(op->ors_attrs[i].an_name.bv_val, "+", op->ors_attrs[i].an_name.bv_len) == 0) {
882			odusers_insert_vendorName(rs);
883			odusers_insert_vendorVersion(rs);
884			odusers_insert_operatingSystemVersion(rs);
885		}
886	}
887
888out:
889	return SLAP_CB_CONTINUE;
890}
891
892static int odusers_modify_bridge_authdata(Operation *op, SlapReply *rs) {
893	OperationBuffer opbuf = {0};
894	Operation *fakeop;
895	Connection conn = {0};
896	Entry *e;
897
898	e = odusers_copy_authdata(&op->o_req_ndn);
899	if(!e) {
900		Debug(LDAP_DEBUG_ANY, "%s: No entry associated with %s\n", __PRETTY_FUNCTION__, op->o_req_ndn.bv_val, 0);
901		goto out;
902	}
903
904	connection_fake_init2(&conn, &opbuf, ldap_pvt_thread_pool_context(), 0);
905	fakeop = &opbuf.ob_op;
906	fakeop->o_dn = op->o_dn;
907	fakeop->o_ndn = op->o_ndn;
908	fakeop->o_req_dn = e->e_name;
909	fakeop->o_req_ndn = e->e_nname;
910	fakeop->o_tag = op->o_tag;
911	fakeop->orm_modlist = op->orm_modlist;
912	fakeop->o_conn->c_listener->sl_url.bv_val = "ldapi://%2Fvar%2Frun%2Fldapi";
913	fakeop->o_conn->c_listener->sl_url.bv_len = strlen("ldapi://%2Fvar%2Frun%2Fldapi");
914
915	// Use the frontend DB for the modification so we go through the syncrepl
916	// overlay and our change gets replicated.
917	fakeop->o_bd = frontendDB;
918
919	slap_op_time(&op->o_time, &op->o_tincr);
920
921	fakeop->o_bd->be_modify(fakeop, rs);
922	if(rs->sr_err != LDAP_SUCCESS) {
923		Debug(LDAP_DEBUG_ANY, "%s: Unable to modify authdata attribute: %s (%d)\n", __PRETTY_FUNCTION__, fakeop->o_req_ndn.bv_val, rs->sr_err);
924		goto out;
925	}
926
927	send_ldap_result(op, rs);
928	return rs->sr_err;
929
930out:
931	return SLAP_CB_CONTINUE;
932}
933
934static int odusers_modify_userpolicy(Operation *op, SlapReply *rs) {
935	OperationBuffer opbuf = {0};
936	Operation *fakeop;
937	Connection conn = {0};
938	Entry *e;
939	Modifications *m = op->orm_modlist;
940	CFDictionaryRef userdict = NULL;
941	CFDictionaryRef accountPolicyDict = NULL;
942
943	e = odusers_copy_authdata(&op->o_req_ndn);
944	if(!e) {
945		Debug(LDAP_DEBUG_ANY, "%s: No entry associated with %s\n", __func__, op->o_req_ndn.bv_val, 0);
946		goto out;
947	}
948
949	connection_fake_init2(&conn, &opbuf, ldap_pvt_thread_pool_context(), 0);
950	fakeop = &opbuf.ob_op;
951	fakeop->o_dn = op->o_dn;
952	fakeop->o_ndn = op->o_ndn;
953	fakeop->o_req_dn = e->e_name;
954	fakeop->o_req_ndn = e->e_nname;
955	fakeop->o_tag = op->o_tag;
956	fakeop->orm_modlist = op->orm_modlist;
957	fakeop->o_conn->c_listener->sl_url.bv_val = "ldapi://%2Fvar%2Frun%2Fldapi";
958	fakeop->o_conn->c_listener->sl_url.bv_len = strlen("ldapi://%2Fvar%2Frun%2Fldapi");
959
960	if(m && m->sml_numvals) {
961		userdict = CopyPolicyToDict(m->sml_values[0].bv_val, m->sml_values[0].bv_len);
962		if(!userdict) {
963			Debug(LDAP_DEBUG_ANY, "%s: Could not convert user policy to dict", __func__, 0, 0);
964		} else {
965			CFNumberRef isAdminUser = CFDictionaryGetValue(userdict, CFSTR("isAdminUser"));
966			unsigned int tmpint = 0;
967			if(isAdminUser) {
968				CFNumberGetValue(isAdminUser, kCFNumberIntType, &tmpint);
969				if(tmpint) {
970					odusers_joingroup("admin", &op->o_req_dn, 0);
971				} else {
972					odusers_joingroup("admin", &op->o_req_dn, 1);
973				}
974			}
975
976			CFNumberRef isSessionKeyAgent = CFDictionaryGetValue(userdict, CFSTR("isSessionKeyAgent"));
977			tmpint = 0;
978			if(isSessionKeyAgent) {
979				CFNumberGetValue(isSessionKeyAgent, kCFNumberIntType, &tmpint);
980				if(tmpint) {
981					odusers_joingroup("com.apple.access_sessionkey", &op->o_req_dn, 0);
982				} else {
983					odusers_joingroup("com.apple.access_sessionkey", &op->o_req_dn, 1);
984				}
985			}
986
987			CFNumberRef isDisabled = CFDictionaryGetValue(userdict, CFSTR("isDisabled"));
988			tmpint = 0;
989			if(isDisabled) {
990				CFNumberGetValue(isDisabled, kCFNumberIntType, &tmpint);
991				if(tmpint) {
992					odusers_joingroup("com.apple.access_disabled", &op->o_req_dn, 0);
993				} else {
994					odusers_joingroup("com.apple.access_disabled", &op->o_req_dn, 1);
995				}
996			}
997
998			accountPolicyDict = APPolicySetWithLegacyPolicies(userdict);
999			if (accountPolicyDict) {
1000				Debug(LDAP_DEBUG_ANY, "%s: APPolicySetWithLegacyPolicies", __func__, 0, 0);
1001				odusers_accountpolicy_set(&op->o_req_ndn, e, accountPolicyDict);
1002				CFRelease(accountPolicyDict);
1003			}
1004			CFRelease(userdict);
1005		}
1006	}
1007
1008	// Use the frontend DB for the modification so we go through the syncrepl
1009	// overlay and our change gets replicated.
1010	fakeop->o_bd = frontendDB;
1011
1012	slap_op_time(&op->o_time, &op->o_tincr);
1013
1014	fakeop->o_bd->be_modify(fakeop, rs);
1015	if(rs->sr_err != LDAP_SUCCESS) {
1016		Debug(LDAP_DEBUG_ANY, "%s: Unable to modify authdata attribute: %s (%d)\n", __PRETTY_FUNCTION__, fakeop->o_req_ndn.bv_val, rs->sr_err);
1017		goto out;
1018	}
1019
1020	send_ldap_result(op, rs);
1021	return rs->sr_err;
1022
1023out:
1024	return SLAP_CB_CONTINUE;
1025}
1026
1027static int odusers_modify_globalpolicy(Operation *op, SlapReply *rs) {
1028	OperationBuffer opbuf = {0};
1029	Operation *fakeop;
1030	Connection conn = {0};
1031	Entry *e;
1032	Modifications *m = op->orm_modlist;
1033	CFDictionaryRef globaldict = NULL;
1034
1035	connection_fake_init2(&conn, &opbuf, ldap_pvt_thread_pool_context(), 0);
1036	fakeop = &opbuf.ob_op;
1037	fakeop->o_dn = op->o_dn;
1038	fakeop->o_ndn = op->o_ndn;
1039	fakeop->o_req_dn.bv_val = "cn=access,cn=authdata";
1040	fakeop->o_req_dn.bv_len = strlen(fakeop->o_req_dn.bv_val);
1041	fakeop->o_req_ndn = fakeop->o_req_dn;
1042	fakeop->o_tag = op->o_tag;
1043	fakeop->orm_modlist = op->orm_modlist;
1044	fakeop->o_conn->c_listener->sl_url.bv_val = "ldapi://%2Fvar%2Frun%2Fldapi";
1045	fakeop->o_conn->c_listener->sl_url.bv_len = strlen("ldapi://%2Fvar%2Frun%2Fldapi");
1046
1047	if(m && m->sml_numvals) {
1048		globaldict = CopyPolicyToDict(m->sml_values[0].bv_val, m->sml_values[0].bv_len);
1049		if(!globaldict) {
1050			Debug(LDAP_DEBUG_ANY, "%s: Could not convert global policy to dict", __func__, 0, 0);
1051		} else {
1052			/* translate apple-user-passwordpolicy to apple-accountpolicy */
1053			if (strncmp(m->sml_desc->ad_cname.bv_val, "apple-user-passwordpolicy", m->sml_desc->ad_cname.bv_len) == 0) {
1054				CFDictionaryRef accountPolicyDict = NULL;
1055				accountPolicyDict = APPolicySetWithLegacyPolicies(globaldict);
1056				if (accountPolicyDict) {
1057					Debug(LDAP_DEBUG_ANY, "%s: APPolicySetWithLegacyPolicies - global", __func__, 0, 0);
1058					AttributeDescription *appleAccountPolicyAD = NULL;
1059					const char *text = NULL;
1060					struct berval *policy_bv = NULL;
1061
1062					if(slap_str2ad("apple-accountpolicy", &appleAccountPolicyAD, &text) != 0) {
1063						Debug(LDAP_DEBUG_ANY, "%s: Unable to retrieve description of apple-accountpolicy attribute", __PRETTY_FUNCTION__, 0, 0);
1064						goto out;
1065					}
1066
1067					m = (Modifications *) ch_malloc(sizeof(Modifications));
1068
1069					policy_bv = odusers_copy_dict2bv(accountPolicyDict);
1070
1071					m->sml_op = LDAP_MOD_REPLACE;
1072					m->sml_flags = 0;
1073					m->sml_type = appleAccountPolicyAD->ad_cname;
1074					m->sml_values = (struct berval*) ch_malloc(2 * sizeof(struct berval));
1075					m->sml_values[0].bv_val = policy_bv->bv_val;
1076					m->sml_values[0].bv_len = policy_bv->bv_len;
1077					m->sml_values[1].bv_val = NULL;
1078					m->sml_values[1].bv_len = 0;
1079					m->sml_nvalues = NULL;
1080					m->sml_numvals = 1;
1081					m->sml_desc = appleAccountPolicyAD;
1082					m->sml_next = fakeop->orm_modlist;
1083					fakeop->orm_modlist = m;
1084
1085					CFRelease(accountPolicyDict);
1086				}
1087			}
1088
1089			CFNumberRef newPasswordRequired = CFDictionaryGetValue(globaldict, CFSTR("newPasswordRequired"));
1090			if(newPasswordRequired) {
1091				const char *text = NULL;
1092				time_t tmptime;
1093				struct tm tmptm;
1094				tmptime = time(NULL);
1095				gmtime_r(&tmptime, &tmptm);
1096
1097				if(!passwordRequiredDateAD && (slap_str2ad("passwordRequiredDate", &passwordRequiredDateAD, &text) != 0)) {
1098					Debug(LDAP_DEBUG_ANY, "%s: Unable to retrieve description of uid attribute", __PRETTY_FUNCTION__, 0, 0);
1099					goto out;
1100				}
1101				m = ch_calloc(1, sizeof(Modifications));
1102				m->sml_op = LDAP_MOD_REPLACE;
1103				m->sml_flags = 0;
1104				m->sml_type = passwordRequiredDateAD->ad_cname;
1105				m->sml_values = (struct berval*) ch_malloc(2 * sizeof(struct berval));
1106				m->sml_values[0].bv_val = ch_calloc(1, 256);
1107				m->sml_values[0].bv_len = strftime(m->sml_values[0].bv_val, 256, "%Y%m%d%H%M%SZ", &tmptm);
1108				m->sml_values[1].bv_val = NULL;
1109				m->sml_values[1].bv_len = 0;
1110				m->sml_nvalues = NULL;
1111				m->sml_numvals = 1;
1112				m->sml_desc = passwordRequiredDateAD;
1113				m->sml_next = fakeop->orm_modlist;
1114				fakeop->orm_modlist = m;
1115			}
1116			CFRelease(globaldict);
1117		}
1118	}
1119
1120	// Use the frontend DB for the modification so we go through the syncrepl
1121	// overlay and our change gets replicated.
1122	fakeop->o_bd = frontendDB;
1123
1124	slap_op_time(&op->o_time, &op->o_tincr);
1125
1126	fakeop->o_bd->be_modify(fakeop, rs);
1127	if(rs->sr_err != LDAP_SUCCESS) {
1128		Debug(LDAP_DEBUG_ANY, "%s: Unable to modify user policy: %s (%d)\n", __PRETTY_FUNCTION__, fakeop->o_req_ndn.bv_val, rs->sr_err);
1129		goto out;
1130	}
1131
1132	APInvalidateCacheForPolicySet(NULL);
1133
1134	send_ldap_result(op, rs);
1135	return rs->sr_err;
1136
1137out:
1138	return SLAP_CB_CONTINUE;
1139}
1140
1141static int odusers_enforce_admin(Operation *op) {
1142	CFDictionaryRef policy = NULL;
1143	int ret = -1;
1144
1145	if((op->o_conn->c_listener->sl_url.bv_len == strlen("ldapi://%2Fvar%2Frun%2Fldapi")) && (strncmp(op->o_conn->c_listener->sl_url.bv_val, "ldapi://%2Fvar%2Frun%2Fldapi", op->o_conn->c_listener->sl_url.bv_len) == 0)) {
1146		ret = 0;
1147		goto out;
1148	}
1149
1150	policy = odusers_copy_effectiveuserpoldict(&op->o_conn->c_dn);
1151	if(!policy) {
1152		Debug(LDAP_DEBUG_ANY, "%s: Unable to retrieve effective policy for %s\n", __func__, op->o_conn->c_dn.bv_val, 0);
1153		return SLAP_CB_CONTINUE;
1154	}
1155	if(odusers_isdisabled(policy)) {
1156		Debug(LDAP_DEBUG_ANY, "%s: disabled user tried to set policy", __PRETTY_FUNCTION__, 0, 0);
1157		goto out;
1158	}
1159	if(!odusers_isadmin(policy)) {
1160		Debug(LDAP_DEBUG_ANY, "%s: non-admin user tried to set policy", __PRETTY_FUNCTION__, 0, 0);
1161		goto out;
1162	}
1163
1164	ret = 0;
1165out:
1166	if(policy) CFRelease(policy);
1167	return ret;
1168}
1169
1170static int odusers_rename(Operation *op, SlapReply *rs) {
1171	bool isaccount;
1172
1173	if(!op || op->o_req_ndn.bv_len == 0) return SLAP_CB_CONTINUE;
1174
1175	if(strnstr(op->o_req_ndn.bv_val, "cn=authdata", op->o_req_ndn.bv_len) != NULL) return SLAP_CB_CONTINUE;
1176
1177	isaccount = odusers_isaccount(op);
1178	if(!isaccount) {
1179		return SLAP_CB_CONTINUE;
1180	}
1181
1182	OperationBuffer opbuf = {0};
1183	Operation *fakeop = NULL;
1184	Connection conn = {0};
1185	Entry *e = NULL;
1186	const char *text = NULL;
1187	SlapReply rs2 = {REP_RESULT};
1188	Modifications *m = NULL;
1189	char *newname = NULL;
1190	char *newnameminusdollar = NULL;
1191	char *oldname = NULL;
1192	char *princname = NULL;
1193	char *oldprincname = NULL;
1194	char *realmname = NULL;
1195
1196	OperationBuffer useropbuf = {0};
1197	Operation *userfakeop = NULL;
1198	Connection userconn = {0};
1199	Entry *usere = NULL;
1200	SlapReply userrs = {REP_RESULT};
1201	Modifications *userm = NULL;
1202	Attribute *aas = NULL;
1203	Attribute *altsec = NULL;
1204	Attribute *aliases = NULL;
1205	int i;
1206	Entry *authe = NULL;
1207	AccessControlState acl_state = ACL_STATE_INIT;
1208
1209	realmname = odusers_copy_krbrealm(op);
1210	if(!realmname) {
1211		Debug(LDAP_DEBUG_ANY, "%s: Can't locate kerberos realm", __func__, 0, 0);
1212		goto out;
1213	}
1214
1215	newname = strchr(op->orr_newrdn.bv_val, '=');
1216	if(!newname) {
1217		Debug(LDAP_DEBUG_ANY, "%s: Could not determine new name from %s\n", __func__, op->orr_newrdn.bv_val, 0);
1218		goto out;
1219	}
1220	newname++;
1221
1222	e = odusers_copy_authdata(&op->o_req_ndn);
1223	if(!e) {
1224		Debug(LDAP_DEBUG_ANY, "%s: No entry associated with %s\n", __PRETTY_FUNCTION__, op->o_req_ndn.bv_val, 0);
1225		goto out;
1226	}
1227
1228	oldname = odusers_copy_recname(op);
1229	if(!oldname) {
1230		Debug(LDAP_DEBUG_ANY, "%s: could not find recname of %s\n", __PRETTY_FUNCTION__, op->o_req_ndn.bv_val, 0);
1231		goto out;
1232	}
1233
1234	asprintf(&princname, "%s@%s", newname, realmname);
1235	asprintf(&oldprincname, "%s@%s", oldname, realmname);
1236
1237	if(!uidAD && slap_str2ad("uid", &uidAD, &text) != 0) {
1238		Debug(LDAP_DEBUG_ANY, "%s: Unable to retrieve description of uid attribute", __PRETTY_FUNCTION__, 0, 0);
1239		goto out;
1240	}
1241	if(!krbAD && slap_str2ad("KerberosPrincName", &krbAD, &text) != 0) {
1242		Debug(LDAP_DEBUG_ANY, "%s: Unable to retrieve description of KerberosPrincName attribute", __PRETTY_FUNCTION__, 0, 0);
1243		goto out;
1244	}
1245	if(!draftkrbAD && slap_str2ad("draft-krbPrincipalName", &draftkrbAD, &text) != 0) {
1246		Debug(LDAP_DEBUG_ANY, "%s: Unable to retrieve description of draft-PrincipalName attribute", __PRETTY_FUNCTION__, 0, 0);
1247		goto out;
1248	}
1249	if(!draftkrbAliasesAD && slap_str2ad("draft-krbPrincipalAliases", &draftkrbAliasesAD, &text) != 0) {
1250		Debug(LDAP_DEBUG_ANY, "%s: Unable to retrieve description of draft-krbPrincipalAliases attribute", __PRETTY_FUNCTION__, 0, 0);
1251		goto out;
1252	}
1253	if(!aaAD && slap_str2ad("authAuthority", &aaAD, &text) != 0) {
1254		Debug(LDAP_DEBUG_ANY, "%s: Unable to retrieve description of authAuthority attribute", __PRETTY_FUNCTION__, 0, 0);
1255		goto out;
1256	}
1257	if(!altsecAD && slap_str2ad("altSecurityIdentities", &altsecAD, &text) != 0) {
1258		Debug(LDAP_DEBUG_ANY, "%s: Unable to retrieve description of altSecurityIdentities attribute", __PRETTY_FUNCTION__, 0, 0);
1259		goto out;
1260	}
1261
1262	connection_fake_init2(&conn, &opbuf, ldap_pvt_thread_pool_context(), 0);
1263	fakeop = &opbuf.ob_op;
1264	fakeop->o_dn = op->o_dn;
1265	fakeop->o_ndn = op->o_ndn;
1266	fakeop->o_req_dn = e->e_name;
1267	fakeop->o_req_ndn = e->e_nname;
1268	fakeop->o_tag = LDAP_REQ_MODIFY;
1269	fakeop->o_conn->c_listener->sl_url.bv_val = "ldapi://%2Fvar%2Frun%2Fldapi";
1270	fakeop->o_conn->c_listener->sl_url.bv_len = strlen("ldapi://%2Fvar%2Frun%2Fldapi");
1271	fakeop->o_bd = frontendDB;
1272	fakeop->orm_modlist = m = (Modifications *) ch_malloc(sizeof(Modifications));
1273	m->sml_op = LDAP_MOD_REPLACE;
1274	m->sml_flags = 0;
1275	m->sml_type = uidAD->ad_cname;
1276	m->sml_values = (struct berval*) ch_malloc(2 * sizeof(struct berval));
1277	m->sml_values[0].bv_val = ch_strdup(newname);
1278	m->sml_values[0].bv_len = strlen(newname);
1279	m->sml_values[1].bv_val = NULL;
1280	m->sml_values[1].bv_len = 0;
1281	m->sml_numvals = 1;
1282	m->sml_nvalues = NULL;
1283	m->sml_desc = uidAD;
1284
1285	m->sml_next = (Modifications *) ch_malloc(sizeof(Modifications));
1286	m = m->sml_next;
1287
1288	m->sml_op = LDAP_MOD_REPLACE;
1289	m->sml_flags = 0;
1290	m->sml_type = krbAD->ad_cname;
1291	m->sml_values = (struct berval*) ch_malloc(2 * sizeof(struct berval));
1292	m->sml_values[0].bv_val = ch_strdup(newname);
1293	m->sml_values[0].bv_len = strlen(newname);
1294	m->sml_values[1].bv_val = NULL;
1295	m->sml_values[1].bv_len = 0;
1296	m->sml_numvals = 1;
1297	m->sml_nvalues = NULL;
1298	m->sml_desc = krbAD;
1299	m->sml_next = (Modifications *) ch_malloc(sizeof(Modifications));
1300	m = m->sml_next;
1301
1302	m->sml_op = LDAP_MOD_REPLACE;
1303	m->sml_flags = 0;
1304	m->sml_type = draftkrbAD->ad_cname;
1305	m->sml_values = (struct berval*) ch_malloc(2 * sizeof(struct berval));
1306	m->sml_values[0].bv_val = ch_strdup(princname);
1307	m->sml_values[0].bv_len = strlen(princname);
1308	m->sml_values[1].bv_val = NULL;
1309	m->sml_values[1].bv_len = 0;
1310	m->sml_numvals = 1;
1311	m->sml_nvalues = NULL;
1312	m->sml_desc = draftkrbAD;
1313	m->sml_next = NULL;
1314
1315
1316	// Update user record now
1317	connection_fake_init2(&userconn, &useropbuf, ldap_pvt_thread_pool_context(), 0);
1318	userfakeop = &useropbuf.ob_op;
1319	userfakeop->o_dn = op->o_dn;
1320	userfakeop->o_ndn = op->o_ndn;
1321	userfakeop->o_req_dn = op->o_dn;
1322	userfakeop->o_req_ndn = op->o_ndn;
1323	authe = odusers_copy_entry(userfakeop);
1324	userfakeop->o_req_dn = op->o_req_dn;
1325	userfakeop->o_req_ndn = op->o_req_ndn;
1326	usere = odusers_copy_entry(userfakeop);
1327	if(!usere) {
1328		Debug(LDAP_DEBUG_ANY, "%s: No user entry associated with %s\n", __PRETTY_FUNCTION__, userfakeop->o_req_ndn.bv_val, 0);
1329		goto out;
1330	}
1331	userfakeop->o_bd = frontendDB;
1332	userfakeop->o_tag = LDAP_REQ_MODIFY;
1333	userfakeop->o_conn->c_listener->sl_url.bv_val = "ldapi://%2Fvar%2Frun%2Fldapi";
1334	userfakeop->o_conn->c_listener->sl_url.bv_len = strlen("ldapi://%2Fvar%2Frun%2Fldapi");
1335
1336	userfakeop->orm_modlist = userm = (Modifications *) ch_malloc(sizeof(Modifications));
1337	aas = attrs_find(usere->e_attrs, aaAD);
1338	if(!aas) {
1339		Debug(LDAP_DEBUG_ANY, "%s: User has no authauthorities: %s\n", __func__, userfakeop->o_req_dn.bv_val, 0);
1340		ch_free(userm);
1341		userm = NULL;
1342		goto out;
1343	}
1344	userm->sml_op = LDAP_MOD_REPLACE;
1345	userm->sml_flags = 0;
1346	userm->sml_type = aaAD->ad_cname;
1347	userm->sml_values = (struct berval*) ch_malloc((aas->a_numvals+1) * sizeof(struct berval));
1348	for(i = 0; i < aas->a_numvals; i++) {
1349		if(strnstr(aas->a_vals[i].bv_val, ";Kerberosv5;", aas->a_vals[i].bv_len) != NULL) {
1350			userm->sml_values[i].bv_len = asprintf(&userm->sml_values[i].bv_val, ";Kerberosv5;;%s@%s;%s;", newname, realmname, realmname);
1351		} else {
1352			ber_dupbv(&userm->sml_values[i], &aas->a_vals[i]);
1353		}
1354	}
1355	userm->sml_values[i].bv_val = NULL;
1356	userm->sml_values[i].bv_len = 0;
1357	userm->sml_numvals = i;
1358	userm->sml_nvalues = NULL;
1359	userm->sml_desc = aaAD;
1360	userm->sml_next = NULL;
1361
1362	altsec = attrs_find(usere->e_attrs, altsecAD);
1363	if(altsec) {
1364		userm->sml_next = (Modifications *) ch_malloc(sizeof(Modifications));
1365		userm = userm->sml_next;
1366		userm->sml_op = LDAP_MOD_REPLACE;
1367		userm->sml_flags = 0;
1368		userm->sml_type = altsecAD->ad_cname;
1369		userm->sml_values = (struct berval*) ch_malloc((altsec->a_numvals+1) * sizeof(struct berval));
1370		for(i = 0; i < altsec->a_numvals; i++) {
1371			if(strnstr(altsec->a_vals[i].bv_val, oldprincname, altsec->a_vals[i].bv_len) != NULL) {
1372				userm->sml_values[i].bv_len = asprintf(&userm->sml_values[i].bv_val, "Kerberos:%s", princname);
1373			} else {
1374				ber_dupbv(&userm->sml_values[i], &altsec->a_vals[i]);
1375			}
1376		}
1377		userm->sml_values[i].bv_val = NULL;
1378		userm->sml_values[i].bv_len = 0;
1379		userm->sml_numvals = i;
1380		userm->sml_nvalues = NULL;
1381		userm->sml_desc = altsecAD;
1382		userm->sml_next = NULL;
1383	}
1384
1385	aliases = attrs_find(e->e_attrs, draftkrbAliasesAD);
1386	if(aliases) {
1387		m->sml_next = (Modifications *) ch_malloc(sizeof(Modifications));
1388		m = m->sml_next;
1389		m->sml_op = LDAP_MOD_DELETE;
1390		m->sml_flags = 0;
1391		m->sml_type = draftkrbAliasesAD->ad_cname;
1392		m->sml_values = (struct berval*) ch_malloc((aliases->a_numvals+1) * sizeof(struct berval));
1393		for(i = 0; i < aliases->a_numvals; i++) {
1394			ber_dupbv(&m->sml_values[i], &aliases->a_vals[i]);
1395		}
1396		m->sml_values[i].bv_val = NULL;
1397		m->sml_values[i].bv_len = 0;
1398		m->sml_numvals = i;
1399		m->sml_nvalues = NULL;
1400		m->sml_desc = draftkrbAliasesAD;
1401		m->sml_next = NULL;
1402
1403		newnameminusdollar = ch_strdup(newname);
1404		newnameminusdollar[strlen(newnameminusdollar)-1] = '\0';
1405
1406		m->sml_next = (Modifications *) ch_malloc(sizeof(Modifications));
1407		m = m->sml_next;
1408		m->sml_op = LDAP_MOD_ADD;
1409		m->sml_flags = 0;
1410		m->sml_type = draftkrbAliasesAD->ad_cname;
1411		m->sml_values = (struct berval*) ch_malloc((aliases->a_numvals+1) * sizeof(struct berval));
1412		for(i = 0; i < aliases->a_numvals; i++) {
1413			if(strnstr(aliases->a_vals[i].bv_val, "/", aliases->a_vals[i].bv_len) != NULL) {
1414				char *tmpstr = ch_calloc(1, aliases->a_vals[i].bv_len+1);
1415				memcpy(tmpstr, aliases->a_vals[i].bv_val, aliases->a_vals[i].bv_len);
1416				char *freeme = strsep(&tmpstr, "/");
1417				m->sml_values[i].bv_len = asprintf(&m->sml_values[i].bv_val, "%s/%s@%s", freeme, newnameminusdollar, realmname);
1418				ch_free(freeme);
1419			} else {
1420				ber_dupbv(&m->sml_values[i], &aliases->a_vals[i]);
1421			}
1422		}
1423		ch_free(newnameminusdollar);
1424		m->sml_values[i].bv_val = NULL;
1425		m->sml_values[i].bv_len = 0;
1426		m->sml_numvals = i;
1427		m->sml_nvalues = NULL;
1428		m->sml_desc = draftkrbAliasesAD;
1429		m->sml_next = NULL;
1430	}
1431
1432	bool isldapi = false;
1433	if((op->o_conn->c_listener->sl_url.bv_len == strlen("ldapi://%2Fvar%2Frun%2Fldapi")) && (strncmp(op->o_conn->c_listener->sl_url.bv_val, "ldapi://%2Fvar%2Frun%2Fldapi", op->o_conn->c_listener->sl_url.bv_len) == 0)) {
1434		isldapi = true;
1435	}
1436
1437	if(!isldapi) {
1438		if(!authe) {
1439			Debug(LDAP_DEBUG_ANY, "%s: rename of %s attempted by unauthed user", __func__, userfakeop->o_req_ndn.bv_val, 0);
1440			goto out;
1441		}
1442
1443		BackendDB *tmpbd = op->o_bd;
1444		op->o_bd = select_backend(&op->o_req_ndn, 1);
1445		userrs.sr_err = access_allowed(op, authe, slap_schema.si_ad_entry, &op->o_req_ndn, ACL_WRITE, &acl_state);
1446		op->o_bd = tmpbd;
1447		if(!userrs.sr_err) {
1448			Debug(LDAP_DEBUG_ANY, "%s: access denied renaming %s: %d\n", __func__, userfakeop->o_req_ndn.bv_val, userrs.sr_err);
1449			goto out;
1450		}
1451	}
1452
1453	// Actually do the updates
1454	userfakeop->o_bd->be_modify(userfakeop, &userrs);
1455	if(userrs.sr_err != LDAP_SUCCESS) {
1456		Debug(LDAP_DEBUG_ANY, "Unable to rename userdata for user %s: %d %s\n", userfakeop->o_req_ndn.bv_val, userrs.sr_err, userrs.sr_text);
1457		goto out;
1458	}
1459
1460	fakeop->o_bd->be_modify(fakeop, &rs2);
1461	if(rs2.sr_err != LDAP_SUCCESS) {
1462		Debug(LDAP_DEBUG_ANY, "Unable to rename authdata for user %s: %d %s\n", fakeop->o_req_ndn.bv_val, rs2.sr_err, rs2.sr_text);
1463		goto out;
1464	}
1465
1466out:
1467	if(e) entry_free(e);
1468	if(usere) entry_free(usere);
1469//	if(authe) entry_free(authe);
1470	if(m) slap_mods_free(fakeop->orm_modlist, 1);
1471	if(userm) slap_mods_free(userfakeop->orm_modlist, 1);
1472	ch_free(realmname);
1473	free(princname);
1474	free(oldprincname);
1475	ch_free(oldname);
1476
1477	return SLAP_CB_CONTINUE;
1478}
1479
1480static int odusers_modify(Operation *op, SlapReply *rs) {
1481	bool isaccount;
1482	Modifications *m;
1483
1484	if(!op || op->o_req_ndn.bv_len == 0) return SLAP_CB_CONTINUE;
1485
1486	if(strnstr(op->o_req_ndn.bv_val, "cn=authdata", op->o_req_ndn.bv_len) != NULL) return SLAP_CB_CONTINUE;
1487
1488	m = op->orm_modlist;
1489	if(!m) return SLAP_CB_CONTINUE;
1490
1491	isaccount = odusers_isaccount(op);
1492
1493	// setpolicy will only work on a replacement basis, if we support
1494	// non-setpolicy modifys this needs to become policy specific.
1495	if((m->sml_op & LDAP_MOD_OP) != LDAP_MOD_REPLACE) return SLAP_CB_CONTINUE;
1496
1497	if(isaccount && strncmp(m->sml_desc->ad_cname.bv_val, "apple-user-passwordpolicy", m->sml_desc->ad_cname.bv_len) == 0) {
1498		if(odusers_enforce_admin(op) != 0) {
1499			Debug(LDAP_DEBUG_ANY, "%s: no admin privs while attempting policy modification for %s\n", __func__, op->o_req_ndn.bv_val, 0);
1500			send_ldap_error(op, rs, LDAP_INSUFFICIENT_ACCESS, "policy modification not permitted");
1501			return rs->sr_err;
1502		}
1503
1504		return odusers_modify_userpolicy(op, rs);
1505	} else if(isaccount && (strncmp(m->sml_desc->ad_cname.bv_val, "draft-krbPrincipalAliases", m->sml_desc->ad_cname.bv_len) == 0 ||
1506							strncmp(m->sml_desc->ad_cname.bv_val, "apple-accountpolicy", m->sml_desc->ad_cname.bv_len) == 0)) {
1507		if((odusers_enforce_admin(op) != 0) && (ber_bvcmp(&op->o_ndn, &op->o_req_ndn) != 0)) {
1508			send_ldap_error(op, rs, LDAP_INSUFFICIENT_ACCESS, "No access");
1509			rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
1510			return rs->sr_err;
1511		}
1512
1513		return odusers_modify_bridge_authdata(op, rs);
1514	} else if(!isaccount && (strncmp(op->o_req_ndn.bv_val, kDirservConfigName, strlen(kDirservConfigName)) == 0)
1515						 && (strncmp(m->sml_desc->ad_cname.bv_val, "apple-user-passwordpolicy", m->sml_desc->ad_cname.bv_len) == 0 ||
1516						 	 strncmp(m->sml_desc->ad_cname.bv_val, "apple-accountpolicy", m->sml_desc->ad_cname.bv_len) == 0)) {
1517		if(odusers_enforce_admin(op) != 0) {
1518			Debug(LDAP_DEBUG_ANY, "%s: no admin privs while attempting global policy modification\n", __func__, 0, 0);
1519			send_ldap_error(op, rs, LDAP_INSUFFICIENT_ACCESS, "global policy modification not permitted");
1520			return rs->sr_err;
1521		}
1522
1523		return odusers_modify_globalpolicy(op, rs);
1524	}
1525
1526	return SLAP_CB_CONTINUE;
1527}
1528
1529/* Returns a newly allocated copy of the pws pubkey attribute.
1530 * Caller is responsible for free()'ing the returned result.
1531 */
1532static char *odusers_copy_pwspubkey(Operation *op) {
1533	OperationBuffer opbuf = {0};
1534	Operation *fakeop = NULL;
1535	Connection conn = {0};
1536	Entry *e = NULL;
1537	Attribute *a = NULL;
1538	AttributeDescription *pubkeyAD = NULL;
1539	const char *text = NULL;
1540	static char *savedkey = NULL;
1541	char *ret = NULL;
1542
1543	if(savedkey) {
1544		return ch_strdup(savedkey);
1545	}
1546
1547	connection_fake_init2(&conn, &opbuf, ldap_pvt_thread_pool_context(), 0);
1548	fakeop = &opbuf.ob_op;
1549	fakeop->o_dn = op->o_dn;
1550	fakeop->o_ndn = op->o_ndn;
1551	fakeop->o_req_dn.bv_len = strlen("cn=authdata");
1552	fakeop->o_req_dn.bv_val = "cn=authdata";
1553	fakeop->o_req_ndn = fakeop->o_req_dn;
1554	fakeop->o_conn->c_listener->sl_url.bv_val = "ldapi://%2Fvar%2Frun%2Fldapi";
1555	fakeop->o_conn->c_listener->sl_url.bv_len = strlen("ldapi://%2Fvar%2Frun%2Fldapi");
1556
1557	e = odusers_copy_entry(fakeop);
1558	if(!e) {
1559		Debug(LDAP_DEBUG_TRACE, "%s: No entry associated with cn=authdata\n", __func__, 0, 0);
1560		goto out;
1561	}
1562
1563	if(slap_str2ad("PWSPublicKey", &pubkeyAD, &text) != 0) {
1564		Debug(LDAP_DEBUG_ANY, "%s: slap_str2ad failed for authGUID", __func__, 0, 0);
1565		goto out;
1566	}
1567
1568	a = attr_find(e->e_attrs, pubkeyAD);
1569	if(!a) {
1570		Debug(LDAP_DEBUG_ANY, "%s: Could not locate PWSPublicKey attribute", __func__, 0, 0);
1571		goto out;
1572	}
1573
1574	ret = ch_calloc(1, a->a_vals[0].bv_len + 1);
1575	if(!ret) goto out;
1576	memcpy(ret, a->a_vals[0].bv_val, a->a_vals[0].bv_len);
1577	savedkey = ch_strdup(ret);
1578
1579out:
1580	if(e) entry_free(e);
1581	return ret;
1582}
1583
1584/* takes a uuid and converts it to PWS slot representation of
1585 * 0xXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
1586 * appending a NUL byte.
1587 * Caller is responsible for allocation of string.
1588 */
1589static void odusers_uuid_to_pwsslot(uuid_t uuid, char *string) {
1590	char uuidstr[37];
1591	int i, n;
1592
1593	uuidstr[sizeof(uuidstr)-1] = '\0';
1594	uuid_unparse_lower(uuid, uuidstr);
1595	string[0] = '0';
1596	string[1] = 'x';
1597	for(i = 0, n = 2; n < 34 && i < 36; i++) {
1598		if( uuidstr[i] != '-' ) {
1599			string[n] = uuidstr[i];
1600			n++;
1601		}
1602	}
1603	string[n] = '\0';
1604	return;
1605}
1606
1607static int odusers_add_authdata(Operation *op, SlapReply *rs, uuid_t newuuid) {
1608	OperationBuffer opbuf = {0};
1609	Operation *fakeop = NULL;
1610	Connection conn = {0};
1611	Entry *e = NULL;
1612	Attribute *a = NULL;
1613	Attribute *attriter = NULL;
1614	Attribute *entryUUID = NULL;
1615	AttributeDescription *authGUIDAD = NULL;
1616	AttributeDescription *objectClassAD = NULL;
1617	const char *text = NULL;
1618	uuid_string_t uuidstr;
1619	SlapReply rs2 = {REP_RESULT};
1620	char *authauthority = NULL;
1621	char slotid[37];
1622	char *recname = NULL;
1623	char *realm = NULL;
1624	bool iscomputer = false;
1625
1626	uuid_unparse_lower(newuuid, uuidstr);
1627
1628	recname = odusers_copy_recname(op);
1629	if(!recname) {
1630		Debug(LDAP_DEBUG_ANY, "%s: Could not determine record name", __func__, 0, 0);
1631		goto out;
1632	}
1633
1634	if(strnstr(op->o_req_dn.bv_val, "cn=computer", op->o_req_dn.bv_len) != NULL) {
1635		iscomputer = true;
1636	}
1637
1638	realm = odusers_copy_krbrealm(op);
1639
1640	connection_fake_init2(&conn, &opbuf, ldap_pvt_thread_pool_context(), 0);
1641	fakeop = &opbuf.ob_op;
1642	fakeop->o_dn = op->o_dn;
1643	fakeop->o_ndn = op->o_ndn;
1644	fakeop->o_req_dn.bv_len = asprintf(&fakeop->o_req_dn.bv_val, "authGUID=%s,cn=users,cn=authdata", uuidstr);
1645	fakeop->o_req_ndn = fakeop->o_req_dn;
1646	fakeop->o_tag = LDAP_REQ_ADD;
1647	fakeop->o_conn->c_listener->sl_url.bv_val = "ldapi://%2Fvar%2Frun%2Fldapi";
1648	fakeop->o_conn->c_listener->sl_url.bv_len = strlen("ldapi://%2Fvar%2Frun%2Fldapi");
1649
1650	if(slap_str2ad("authGUID", &authGUIDAD, &text) != 0) {
1651		Debug(LDAP_DEBUG_ANY, "%s: slap_str2ad failed for authGUID", __func__, 0, 0);
1652		goto out;
1653	}
1654	if(slap_str2ad("objectClass", &objectClassAD, &text) != 0) {
1655		Debug(LDAP_DEBUG_ANY, "%s: slap_str2ad failed for objectClass", __func__, 0, 0);
1656		goto out;
1657	}
1658
1659	a = attr_alloc(authGUIDAD);
1660	if(!a) goto out;
1661	a->a_vals = ch_calloc(2, sizeof(struct berval));
1662	if(!a->a_vals) goto out;
1663	a->a_vals[0].bv_len = strlen(uuidstr);
1664	a->a_vals[0].bv_val = ch_calloc(1, a->a_vals[0].bv_len+1);
1665	strncpy(a->a_vals[0].bv_val, uuidstr, a->a_vals[0].bv_len);
1666	a->a_nvals = a->a_vals;
1667	a->a_numvals = 1;
1668
1669	a->a_next = attr_alloc(objectClassAD);
1670	if(!a->a_next) goto out;
1671	attriter = a->a_next;
1672	attriter->a_vals = ch_calloc(2, sizeof(struct berval));
1673	if(!attriter->a_vals) goto out;
1674	attriter->a_vals[0].bv_len = strlen("pwsAuthdata");
1675	attriter->a_vals[0].bv_val = ch_strdup("pwsAuthdata");
1676	attriter->a_nvals = attriter->a_vals;
1677	attriter->a_numvals = 1;
1678
1679	if(recname) {
1680		AttributeDescription *uidAD = NULL;
1681		if(slap_str2ad("uid", &uidAD, &text) != 0) {
1682			Debug(LDAP_DEBUG_ANY, "%s: slap_str2ad failed for uid", __func__, 0, 0);
1683			goto out;
1684		}
1685		attriter->a_next = attr_alloc(uidAD);
1686		if(!attriter->a_next) goto out;
1687		attriter = attriter->a_next;
1688		attriter->a_vals = ch_calloc(2, sizeof(struct berval));
1689		if(!attriter->a_vals) goto out;
1690		attriter->a_vals[0].bv_len = strlen(recname);
1691		attriter->a_vals[0].bv_val = ch_strdup(recname);
1692		attriter->a_nvals = attriter->a_vals;
1693		attriter->a_numvals = 1;
1694
1695		AttributeDescription *princnameAD = NULL;
1696		if(slap_str2ad("KerberosPrincName", &princnameAD, &text) != 0) {
1697			Debug(LDAP_DEBUG_ANY, "%s: slap_str2ad failed for KerberosPrincName", __func__, 0, 0);
1698			goto out;
1699		}
1700		attriter->a_next = attr_alloc(princnameAD);
1701		if(!attriter->a_next) goto out;
1702		attriter = attriter->a_next;
1703		attriter->a_vals = ch_malloc(2 * sizeof(struct berval));
1704		if(!attriter->a_vals) goto out;
1705		attriter->a_vals[0].bv_len = strlen(recname);
1706		attriter->a_vals[0].bv_val = ch_strdup(recname);
1707		attriter->a_vals[1].bv_len = 0;
1708		attriter->a_vals[1].bv_val = NULL;
1709		attriter->a_nvals = attriter->a_vals;
1710		attriter->a_numvals = 1;
1711		attriter->a_flags = 0;
1712	}
1713
1714	AttributeDescription *creationDateAD = NULL;
1715	struct tm tmtime;
1716	time_t now;
1717	if(slap_str2ad("creationDate", &creationDateAD, &text) != 0) {
1718		Debug(LDAP_DEBUG_ANY, "%s: slap_str2ad failed for creationDate", __func__, 0, 0);
1719		goto out;
1720	}
1721	now = time(NULL);
1722	gmtime_r(&now, &tmtime);
1723	attriter->a_next = attr_alloc(creationDateAD);
1724	if(!attriter->a_next) goto out;
1725	attriter = attriter->a_next;
1726	attriter->a_vals = ch_malloc(2 * sizeof(struct berval));
1727	if(!attriter->a_vals) goto out;
1728	attriter->a_vals[0].bv_val = ch_calloc(1, 256);
1729	attriter->a_vals[0].bv_len = strftime(attriter->a_vals[0].bv_val, 256, "%Y%m%d%H%M%SZ", &tmtime);
1730	attriter->a_vals[1].bv_len = 0;
1731	attriter->a_vals[1].bv_val = NULL;
1732	attriter->a_nvals = attriter->a_vals;
1733	attriter->a_numvals = 1;
1734	attriter->a_flags = 0;
1735
1736	AttributeDescription *disablereasonAD = NULL;
1737	if(slap_str2ad("disableReason", &disablereasonAD, &text) != 0) {
1738		Debug(LDAP_DEBUG_ANY, "%s: slap_str2ad failed for disableReason", __func__, 0, 0);
1739		goto out;
1740	}
1741	attriter->a_next = attr_alloc(disablereasonAD);
1742	if(!attriter->a_next) goto out;
1743	attriter = attriter->a_next;
1744	attriter->a_vals = ch_malloc(2 * sizeof(struct berval));
1745	if(!attriter->a_vals) goto out;
1746	attriter->a_vals[0].bv_len = strlen("none");
1747	attriter->a_vals[0].bv_val = ch_strdup("none");
1748	attriter->a_vals[1].bv_len = 0;
1749	attriter->a_vals[1].bv_val = NULL;
1750	attriter->a_nvals = attriter->a_vals;
1751	attriter->a_numvals = 1;
1752	attriter->a_flags = 0;
1753
1754	AttributeDescription *adminGroupsAD = NULL;
1755	if(slap_str2ad("adminGroups", &adminGroupsAD, &text) != 0) {
1756		Debug(LDAP_DEBUG_ANY, "%s: slap_str2ad failed for adminGroups", __func__, 0, 0);
1757		goto out;
1758	}
1759	attriter->a_next = attr_alloc(adminGroupsAD);
1760	if(!attriter->a_next) goto out;
1761	attriter = attriter->a_next;
1762	attriter->a_vals = ch_malloc(2 * sizeof(struct berval));
1763	if(!attriter->a_vals) goto out;
1764	attriter->a_vals[0].bv_len = strlen("unrestricted");
1765	attriter->a_vals[0].bv_val = ch_strdup("unrestricted");
1766	attriter->a_vals[1].bv_len = 0;
1767	attriter->a_vals[1].bv_val = NULL;
1768	attriter->a_nvals = attriter->a_vals;
1769	attriter->a_numvals = 1;
1770	attriter->a_flags = 0;
1771
1772	AttributeDescription *failedLoginAD = NULL;
1773	if(slap_str2ad("loginFailedAttempts", &failedLoginAD, &text) != 0) {
1774		Debug(LDAP_DEBUG_ANY, "%s: slap_str2ad failed for loginFailedAttempts", __func__, 0, 0);
1775		goto out;
1776	}
1777	attriter->a_next = attr_alloc(failedLoginAD);
1778	if(!attriter->a_next) goto out;
1779	attriter = attriter->a_next;
1780	attriter->a_vals = ch_malloc(2 * sizeof(struct berval));
1781	if(!attriter->a_vals) goto out;
1782	attriter->a_vals[0].bv_len = strlen("0");
1783	attriter->a_vals[0].bv_val = ch_strdup("0");
1784	attriter->a_vals[1].bv_len = 0;
1785	attriter->a_vals[1].bv_val = NULL;
1786	attriter->a_nvals = attriter->a_vals;
1787	attriter->a_numvals = 1;
1788	attriter->a_flags = 0;
1789
1790	entryUUID = attr_find( op->ora_e->e_attrs, slap_schema.si_ad_entryUUID );
1791	if(!entryUUID) {
1792		Debug(LDAP_DEBUG_ANY, "%s: couldn't find entryUUID attribute in copy of %s", __func__, op->o_req_ndn.bv_val, 0);
1793		goto out;
1794	}
1795
1796	AttributeDescription *userLinkAD = NULL;
1797	if(slap_str2ad("userLinkage", &userLinkAD, &text) != 0) {
1798		Debug(LDAP_DEBUG_ANY, "%s: slap_str2ad failed for userLinkage", __func__, 0, 0);
1799		goto out;
1800	}
1801	attriter->a_next = attr_alloc(userLinkAD);
1802	if(!attriter->a_next) goto out;
1803	attriter = attriter->a_next;
1804	attriter->a_vals = ch_malloc(2 * sizeof(struct berval));
1805	if(!attriter->a_vals) goto out;
1806	attriter->a_vals[0].bv_len = entryUUID->a_vals[0].bv_len;
1807	attriter->a_vals[0].bv_val = ch_malloc(entryUUID->a_vals[0].bv_len+1);
1808	memcpy(attriter->a_vals[0].bv_val, entryUUID->a_vals[0].bv_val, entryUUID->a_vals[0].bv_len);
1809	attriter->a_vals[0].bv_val[entryUUID->a_vals[0].bv_len] = '\0';
1810	attriter->a_vals[1].bv_len = 0;
1811	attriter->a_vals[1].bv_val = NULL;
1812	attriter->a_nvals = attriter->a_vals;
1813	attriter->a_numvals = 1;
1814	attriter->a_flags = 0;
1815
1816	if(realm) {
1817		AttributeDescription *draftNameAD = NULL;
1818		if(slap_str2ad("draft-krbPrincipalName", &draftNameAD, &text) != 0) {
1819			Debug(LDAP_DEBUG_ANY, "%s: slap_str2ad failed for draft-krbPrincipalName", __func__, 0, 0);
1820			goto out;
1821		}
1822		attriter->a_next = attr_alloc(draftNameAD);
1823		if(!attriter->a_next) goto out;
1824		attriter = attriter->a_next;
1825		attriter->a_vals = ch_malloc(2 * sizeof(struct berval));
1826		if(!attriter->a_vals) goto out;
1827		char *principalName = NULL;
1828		asprintf(&principalName, "%s@%s", recname, realm);
1829		ber_str2bv(principalName, strlen(principalName), 1, &attriter->a_vals[0]);
1830		free(principalName);
1831		attriter->a_vals[1].bv_len = 0;
1832		attriter->a_vals[1].bv_val = NULL;
1833		attriter->a_nvals = attriter->a_vals;
1834		attriter->a_numvals = 1;
1835		attriter->a_flags = 0;
1836
1837		AttributeDescription *draftPolicyAD = NULL;
1838		if(slap_str2ad("draft-krbTicketPolicy", &draftPolicyAD, &text) != 0) {
1839			Debug(LDAP_DEBUG_ANY, "%s: slap_str2ad failed for draft-krbTicketPolicy", __func__, 0, 0);
1840			goto out;
1841		}
1842		attriter->a_next = attr_alloc(draftPolicyAD);
1843		if(!attriter->a_next) goto out;
1844		attriter = attriter->a_next;
1845		attriter->a_vals = ch_malloc(2 * sizeof(struct berval));
1846		if(!attriter->a_vals) goto out;
1847		if(iscomputer) {
1848			attriter->a_vals[0].bv_len = strlen("358");
1849			attriter->a_vals[0].bv_val = ch_strdup("358");
1850		} else {
1851			attriter->a_vals[0].bv_len = strlen("326");
1852			attriter->a_vals[0].bv_val = ch_strdup("326");
1853		}
1854		attriter->a_vals[1].bv_len = 0;
1855		attriter->a_vals[1].bv_val = NULL;
1856		attriter->a_nvals = attriter->a_vals;
1857		attriter->a_numvals = 1;
1858		attriter->a_flags = 0;
1859	}
1860	attriter->a_next = NULL;
1861
1862	e = entry_alloc();
1863	e->e_id = NOID;
1864	ber_dupbv(&e->e_name, &fakeop->o_req_dn);
1865	ber_dupbv(&e->e_nname, &fakeop->o_req_ndn);
1866	e->e_attrs = a;
1867
1868	fakeop->ora_e = e;
1869	fakeop->o_bd = select_backend(&fakeop->o_req_ndn, 1);
1870	if(!fakeop->o_bd) {
1871		Debug(LDAP_DEBUG_ANY, "%s: could not locate backend for %s", __func__, fakeop->o_req_ndn.bv_val, 0);
1872		goto out;
1873	}
1874
1875	fakeop->o_bd->be_add(fakeop, &rs2);
1876
1877out:
1878//	if (e) entry_free(e);
1879	if(fakeop && fakeop->o_req_dn.bv_val) free(fakeop->o_req_dn.bv_val);
1880	ch_free(realm);
1881	ch_free(recname);
1882
1883	return 0;
1884}
1885
1886/* Add auth authorities and altsecurityidentities to a new user record request*/
1887static int odusers_add_aa(Operation *op, SlapReply *rs, uuid_t newuuid) {
1888	Attribute *attriter = NULL;
1889	Attribute *newAA = NULL;
1890	AttributeDescription *authAuthorityAD = NULL;
1891	const char *text = NULL;
1892	uuid_string_t uuidstr;
1893	char *pubkey = NULL;
1894	char *authauthority = NULL;
1895	char slotid[37];
1896	char *recname = NULL;
1897	char *realm = NULL;
1898	char *primary_master_ip = NULL;
1899	bool iscomputer = false;
1900
1901	recname = odusers_copy_recname(op);
1902	if(!recname) {
1903		Debug(LDAP_DEBUG_ANY, "%s: Could not determine record name", __func__, 0, 0);
1904		goto out;
1905	}
1906
1907	if(strnstr(op->o_req_dn.bv_val, "cn=computer", op->o_req_dn.bv_len) != NULL) {
1908		iscomputer = true;
1909	}
1910
1911	realm = odusers_copy_krbrealm(op);
1912
1913	pubkey = odusers_copy_pwspubkey(op);
1914	if(!pubkey) {
1915		Debug(LDAP_DEBUG_ANY, "%s: could not locate PWS public key", __func__, 0, 0);
1916		goto out;
1917	}
1918
1919	primary_master_ip = odusers_copy_primarymasterip(op);
1920	if(!primary_master_ip) {
1921		Debug(LDAP_DEBUG_ANY, "%s: could not locate Primary Master's IP address; trying System Configuration", __func__, 0, 0);
1922		primary_master_ip = CopyPrimaryIPv4Address();
1923		if (!primary_master_ip) {
1924			Debug(LDAP_DEBUG_ANY, "%s: could not locate IP address in System Configuration; using 127.0.0.1 in the auth authority", __func__, 0, 0);
1925			primary_master_ip = ch_strdup("127.0.0.1");
1926		}
1927	}
1928
1929	odusers_uuid_to_pwsslot(newuuid, slotid);
1930
1931	authAuthorityAD = slap_schema.si_ad_authAuthority;
1932	newAA = attr_alloc(authAuthorityAD);
1933	if(!newAA) goto out;
1934	attriter = newAA;
1935	attriter->a_vals = ch_malloc(3 *sizeof(struct berval));
1936	if(!attriter->a_vals) goto out;
1937	if(pubkey[strlen(pubkey)-1] == '\n') {
1938		pubkey[strlen(pubkey)-1] = '\0'; // strip of trailing newline
1939	}
1940	char *pws_authAuthority = NULL;
1941	char *krb5_authAuthority = NULL;
1942	asprintf(&pws_authAuthority, ";ApplePasswordServer;%s,%s:%s", slotid, pubkey, primary_master_ip);
1943	ber_str2bv(pws_authAuthority, strlen(pws_authAuthority), 1, &attriter->a_vals[0]);
1944	free(pws_authAuthority);
1945	asprintf(&krb5_authAuthority, ";Kerberosv5;;%s@%s;%s;", recname, realm, realm);
1946	ber_str2bv(krb5_authAuthority, strlen(krb5_authAuthority), 1, &attriter->a_vals[1]);
1947	free(krb5_authAuthority);
1948
1949	attriter->a_vals[2].bv_len = 0;
1950	attriter->a_vals[2].bv_val = NULL;
1951	attriter->a_nvals = attriter->a_vals;
1952	attriter->a_numvals = 2;
1953	attriter->a_flags = 0;
1954
1955	if(!iscomputer) {
1956		AttributeDescription *altSecAD = NULL;
1957		if(slap_str2ad("altSecurityIdentities", &altSecAD, &text) != 0) {
1958			Debug(LDAP_DEBUG_ANY, "%s: slap_str2ad failed for altSecurityIdentities", __func__, 0, 0);
1959			goto out;
1960		}
1961		attriter->a_next = attr_alloc(altSecAD);
1962		attriter = attriter->a_next;
1963		if(!attriter) goto out;
1964		attriter->a_vals = ch_malloc(2 *sizeof(struct berval));
1965		if(!attriter->a_vals) goto out;
1966		char * altSecurityIdentities = NULL;
1967		asprintf(&altSecurityIdentities, "Kerberos:%s@%s", recname, realm);
1968		ber_str2bv(altSecurityIdentities, strlen(altSecurityIdentities), 1, &attriter->a_vals[0]);
1969		free(altSecurityIdentities);
1970		attriter->a_vals[1].bv_len = 0;
1971		attriter->a_vals[1].bv_val = NULL;
1972		attriter->a_nvals = attriter->a_vals;
1973		attriter->a_numvals = 1;
1974		attriter->a_flags = 0;
1975	}
1976	attriter->a_next = NULL;
1977
1978	for(attriter = op->ora_e->e_attrs; attriter->a_next; attriter = attriter->a_next);
1979	attriter->a_next = newAA;
1980
1981out:
1982	ch_free(realm);
1983	ch_free(recname);
1984	ch_free(pubkey);
1985	ch_free(primary_master_ip);
1986
1987	return 0;
1988}
1989
1990static int odusers_add(Operation *op, SlapReply *rs) {
1991	bool isaccount;
1992	Modifications *m;
1993
1994	if(!op || op->o_req_ndn.bv_len == 0) return SLAP_CB_CONTINUE;
1995	if(strnstr(op->o_req_ndn.bv_val, "cn=authdata", op->o_req_ndn.bv_len) != NULL) return SLAP_CB_CONTINUE;
1996
1997	m = op->orm_modlist;
1998	if(!m) return SLAP_CB_CONTINUE;
1999
2000	isaccount = odusers_isaccount(op);
2001
2002	if(isaccount) {
2003		OpExtraOD *oe = NULL;
2004
2005		oe = calloc(1, sizeof(OpExtraOD));
2006		if(!oe) return SLAP_CB_CONTINUE;
2007		oe->oe.oe_key = ODUSERS_EXTRA_KEY;
2008		uuid_generate_time(oe->uuid);
2009		LDAP_SLIST_INSERT_HEAD(&op->o_extra, &oe->oe, oe_next);
2010
2011		odusers_add_aa(op, rs, oe->uuid);
2012	}
2013
2014	return SLAP_CB_CONTINUE;
2015}
2016
2017int odusers_initialize() {
2018	int rc = 0;
2019
2020	memset(&odusers, 0, sizeof(slap_overinst));
2021
2022	odusers.on_bi.bi_type = "odusers";
2023	odusers.on_bi.bi_cf_ocs = odocs;
2024	odusers.on_bi.bi_op_delete = odusers_delete;
2025	odusers.on_bi.bi_op_search = odusers_search;
2026	odusers.on_bi.bi_op_modify = odusers_modify;
2027	odusers.on_bi.bi_op_add = odusers_add;
2028	odusers.on_bi.bi_op_modrdn = odusers_rename;
2029	odusers.on_response = odusers_response;
2030
2031	rc = config_register_schema(odcfg, odocs);
2032	if(rc) return rc;
2033
2034	return overlay_register(&odusers);
2035}
2036
2037#if SLAPD_OVER_ODUSERS == SLAPD_MOD_DYNAMIC
2038int
2039init_module( int argc, char *argv[] )
2040{
2041    return odusers_initialize();
2042}
2043#endif
2044
2045#endif /* SLAPD_OVER_ODUSERS */
2046