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