1/*
2   Unix SMB/CIFS implementation.
3   ads (active directory) utility library
4   Copyright (C) Andrew Tridgell 2001
5   Copyright (C) Remus Koos 2001
6   Copyright (C) Jim McDonough <jmcd@us.ibm.com> 2002
7
8   This program is free software; you can redistribute it and/or modify
9   it under the terms of the GNU General Public License as published by
10   the Free Software Foundation; either version 2 of the License, or
11   (at your option) any later version.
12
13   This program is distributed in the hope that it will be useful,
14   but WITHOUT ANY WARRANTY; without even the implied warranty of
15   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16   GNU General Public License for more details.
17
18   You should have received a copy of the GNU General Public License
19   along with this program; if not, write to the Free Software
20   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21*/
22
23#include "includes.h"
24
25#ifdef HAVE_LDAP
26
27/**
28 * @file ldap.c
29 * @brief basic ldap client-side routines for ads server communications
30 *
31 * The routines contained here should do the necessary ldap calls for
32 * ads setups.
33 *
34 * Important note: attribute names passed into ads_ routines must
35 * already be in UTF-8 format.  We do not convert them because in almost
36 * all cases, they are just ascii (which is represented with the same
37 * codepoints in UTF-8).  This may have to change at some point
38 **/
39
40static SIG_ATOMIC_T gotalarm;
41
42/***************************************************************
43 Signal function to tell us we timed out.
44****************************************************************/
45
46static void gotalarm_sig(void)
47{
48	gotalarm = 1;
49}
50
51 LDAP *ldap_open_with_timeout(const char *server, int port, unsigned int to)
52{
53	LDAP *ldp = NULL;
54
55	/* Setup timeout */
56	gotalarm = 0;
57	CatchSignal(SIGALRM, SIGNAL_CAST gotalarm_sig);
58	alarm(to);
59	/* End setup timeout. */
60
61	ldp = ldap_open(server, port);
62
63	/* Teardown timeout. */
64	CatchSignal(SIGALRM, SIGNAL_CAST SIG_IGN);
65	alarm(0);
66
67	return ldp;
68}
69
70static int ldap_search_with_timeout(LDAP *ld,
71				    LDAP_CONST char *base,
72				    int scope,
73				    LDAP_CONST char *filter,
74				    char **attrs,
75				    int attrsonly,
76				    LDAPControl **sctrls,
77				    LDAPControl **cctrls,
78				    int sizelimit,
79				    LDAPMessage **res )
80{
81	struct timeval timeout;
82	int result;
83
84	/* Setup timeout for the ldap_search_ext_s call - local and remote. */
85	timeout.tv_sec = lp_ldap_timeout();
86	timeout.tv_usec = 0;
87
88	/* Setup alarm timeout.... Do we need both of these ? JRA. */
89	gotalarm = 0;
90	CatchSignal(SIGALRM, SIGNAL_CAST gotalarm_sig);
91	alarm(lp_ldap_timeout());
92	/* End setup timeout. */
93
94	result = ldap_search_ext_s(ld, base, scope, filter, attrs,
95				   attrsonly, sctrls, cctrls, &timeout,
96				   sizelimit, res);
97
98	/* Teardown timeout. */
99	CatchSignal(SIGALRM, SIGNAL_CAST SIG_IGN);
100	alarm(0);
101
102	if (gotalarm != 0)
103		return LDAP_TIMELIMIT_EXCEEDED;
104
105	return result;
106}
107
108/*
109  try a connection to a given ldap server, returning True and setting the servers IP
110  in the ads struct if successful
111
112  TODO : add a negative connection cache in here leveraged off of the one
113  found in the rpc code.  --jerry
114 */
115BOOL ads_try_connect(ADS_STRUCT *ads, const char *server, unsigned port)
116{
117	char *srv;
118
119	if (!server || !*server) {
120		return False;
121	}
122
123	DEBUG(5,("ads_try_connect: trying ldap server '%s' port %u\n", server, port));
124
125	/* this copes with inet_ntoa brokenness */
126	srv = SMB_STRDUP(server);
127
128	ads->ld = ldap_open_with_timeout(srv, port, lp_ldap_timeout());
129	if (!ads->ld) {
130		free(srv);
131		return False;
132	}
133	ads->ldap_port = port;
134	ads->ldap_ip = *interpret_addr2(srv);
135	free(srv);
136
137	return True;
138}
139
140/*
141  try a connection to a given ldap server, based on URL, returning True if successful
142 */
143static BOOL ads_try_connect_uri(ADS_STRUCT *ads)
144{
145#if defined(LDAP_API_FEATURE_X_OPENLDAP) && (LDAP_API_VERSION > 2000)
146	DEBUG(5,("ads_try_connect: trying ldap server at URI '%s'\n",
147		 ads->server.ldap_uri));
148
149
150	if (ldap_initialize((LDAP**)&(ads->ld), ads->server.ldap_uri) == LDAP_SUCCESS) {
151		return True;
152	}
153	DEBUG(0, ("ldap_initialize: %s\n", strerror(errno)));
154
155#else
156
157	DEBUG(1, ("no URL support in LDAP libs!\n"));
158#endif
159
160	return False;
161}
162
163/**********************************************************************
164 Try to find an AD dc using our internal name resolution routines
165 Try the realm first and then then workgroup name if netbios is not
166 disabled
167**********************************************************************/
168
169static BOOL ads_find_dc(ADS_STRUCT *ads)
170{
171	const char *c_realm;
172	int count, i=0;
173	struct ip_service *ip_list;
174	pstring realm;
175	BOOL got_realm = False;
176	BOOL use_own_domain = False;
177
178	/* if the realm and workgroup are both empty, assume they are ours */
179
180	/* realm */
181	c_realm = ads->server.realm;
182
183	if ( !c_realm || !*c_realm ) {
184		/* special case where no realm and no workgroup means our own */
185		if ( !ads->server.workgroup || !*ads->server.workgroup ) {
186			use_own_domain = True;
187			c_realm = lp_realm();
188		}
189	}
190
191	if (c_realm && *c_realm)
192		got_realm = True;
193
194again:
195	/* we need to try once with the realm name and fallback to the
196	   netbios domain name if we fail (if netbios has not been disabled */
197
198	if ( !got_realm	&& !lp_disable_netbios() ) {
199		c_realm = ads->server.workgroup;
200		if (!c_realm || !*c_realm) {
201			if ( use_own_domain )
202				c_realm = lp_workgroup();
203		}
204
205		if ( !c_realm || !*c_realm ) {
206			DEBUG(0,("ads_find_dc: no realm or workgroup!  Don't know what to do\n"));
207			return False;
208		}
209	}
210
211	pstrcpy( realm, c_realm );
212
213	DEBUG(6,("ads_find_dc: looking for %s '%s'\n",
214		(got_realm ? "realm" : "domain"), realm));
215
216	if ( !get_sorted_dc_list(realm, &ip_list, &count, got_realm) ) {
217		/* fall back to netbios if we can */
218		if ( got_realm && !lp_disable_netbios() ) {
219			got_realm = False;
220			goto again;
221		}
222
223		return False;
224	}
225
226	/* if we fail this loop, then giveup since all the IP addresses returned were dead */
227	for ( i=0; i<count; i++ ) {
228		/* since this is an ads conection request, default to LDAP_PORT is not set */
229		int port = (ip_list[i].port!=PORT_NONE) ? ip_list[i].port : LDAP_PORT;
230		fstring server;
231
232		fstrcpy( server, inet_ntoa(ip_list[i].ip) );
233
234		if ( !NT_STATUS_IS_OK(check_negative_conn_cache(realm, server)) )
235			continue;
236
237		if ( ads_try_connect(ads, server, port) ) {
238			SAFE_FREE(ip_list);
239			return True;
240		}
241
242		/* keep track of failures */
243		add_failed_connection_entry( realm, server, NT_STATUS_UNSUCCESSFUL );
244	}
245
246	SAFE_FREE(ip_list);
247
248	return False;
249}
250
251
252/**
253 * Connect to the LDAP server
254 * @param ads Pointer to an existing ADS_STRUCT
255 * @return status of connection
256 **/
257ADS_STATUS ads_connect(ADS_STRUCT *ads)
258{
259	int version = LDAP_VERSION3;
260	ADS_STATUS status;
261
262	ads->last_attempt = time(NULL);
263	ads->ld = NULL;
264
265	/* try with a URL based server */
266
267	if (ads->server.ldap_uri &&
268	    ads_try_connect_uri(ads)) {
269		goto got_connection;
270	}
271
272	/* try with a user specified server */
273	if (ads->server.ldap_server &&
274	    ads_try_connect(ads, ads->server.ldap_server, LDAP_PORT)) {
275		goto got_connection;
276	}
277
278	if (ads_find_dc(ads)) {
279		goto got_connection;
280	}
281
282	return ADS_ERROR_SYSTEM(errno?errno:ENOENT);
283
284got_connection:
285	DEBUG(3,("Connected to LDAP server %s\n", inet_ntoa(ads->ldap_ip)));
286
287	status = ads_server_info(ads);
288	if (!ADS_ERR_OK(status)) {
289		DEBUG(1,("Failed to get ldap server info\n"));
290		return status;
291	}
292
293	ldap_set_option(ads->ld, LDAP_OPT_PROTOCOL_VERSION, &version);
294
295	if (!ads->auth.user_name) {
296		/* have to use the userPrincipalName value here and
297		   not servicePrincipalName; found by Guenther Deschner @ Sernet */
298
299		asprintf(&ads->auth.user_name, "host/%s", global_myname() );
300	}
301
302	if (!ads->auth.realm) {
303		ads->auth.realm = SMB_STRDUP(ads->config.realm);
304	}
305
306	if (!ads->auth.kdc_server) {
307		ads->auth.kdc_server = SMB_STRDUP(inet_ntoa(ads->ldap_ip));
308	}
309
310#if KRB5_DNS_HACK
311	/* this is a really nasty hack to avoid ADS DNS problems. It needs a patch
312	   to MIT kerberos to work (tridge) */
313	{
314		char *env;
315		asprintf(&env, "KRB5_KDC_ADDRESS_%s", ads->config.realm);
316		setenv(env, ads->auth.kdc_server, 1);
317		free(env);
318	}
319#endif
320
321	if (ads->auth.flags & ADS_AUTH_NO_BIND) {
322		return ADS_SUCCESS;
323	}
324
325	if (ads->auth.flags & ADS_AUTH_ANON_BIND) {
326		return ADS_ERROR(ldap_simple_bind_s( ads->ld, NULL, NULL));
327	}
328
329	if (ads->auth.flags & ADS_AUTH_SIMPLE_BIND) {
330		return ADS_ERROR(ldap_simple_bind_s( ads->ld, ads->auth.user_name, ads->auth.password));
331	}
332
333	return ads_sasl_bind(ads);
334}
335
336/*
337  Duplicate a struct berval into talloc'ed memory
338 */
339static struct berval *dup_berval(TALLOC_CTX *ctx, const struct berval *in_val)
340{
341	struct berval *value;
342
343	if (!in_val) return NULL;
344
345	value = TALLOC_ZERO_P(ctx, struct berval);
346	if (value == NULL)
347		return NULL;
348	if (in_val->bv_len == 0) return value;
349
350	value->bv_len = in_val->bv_len;
351	value->bv_val = TALLOC_MEMDUP(ctx, in_val->bv_val, in_val->bv_len);
352	return value;
353}
354
355/*
356  Make a values list out of an array of (struct berval *)
357 */
358static struct berval **ads_dup_values(TALLOC_CTX *ctx,
359				      const struct berval **in_vals)
360{
361	struct berval **values;
362	int i;
363
364	if (!in_vals) return NULL;
365	for (i=0; in_vals[i]; i++)
366		; /* count values */
367	values = TALLOC_ZERO_ARRAY(ctx, struct berval *, i+1);
368	if (!values) return NULL;
369
370	for (i=0; in_vals[i]; i++) {
371		values[i] = dup_berval(ctx, in_vals[i]);
372	}
373	return values;
374}
375
376/*
377  UTF8-encode a values list out of an array of (char *)
378 */
379static char **ads_push_strvals(TALLOC_CTX *ctx, const char **in_vals)
380{
381	char **values;
382	int i;
383
384	if (!in_vals) return NULL;
385	for (i=0; in_vals[i]; i++)
386		; /* count values */
387	values = TALLOC_ZERO_ARRAY(ctx, char *, i+1);
388	if (!values) return NULL;
389
390	for (i=0; in_vals[i]; i++) {
391		push_utf8_talloc(ctx, &values[i], in_vals[i]);
392	}
393	return values;
394}
395
396/*
397  Pull a (char *) array out of a UTF8-encoded values list
398 */
399static char **ads_pull_strvals(TALLOC_CTX *ctx, const char **in_vals)
400{
401	char **values;
402	int i;
403
404	if (!in_vals) return NULL;
405	for (i=0; in_vals[i]; i++)
406		; /* count values */
407	values = TALLOC_ZERO_ARRAY(ctx, char *, i+1);
408	if (!values) return NULL;
409
410	for (i=0; in_vals[i]; i++) {
411		pull_utf8_talloc(ctx, &values[i], in_vals[i]);
412	}
413	return values;
414}
415
416/**
417 * Do a search with paged results.  cookie must be null on the first
418 *  call, and then returned on each subsequent call.  It will be null
419 *  again when the entire search is complete
420 * @param ads connection to ads server
421 * @param bind_path Base dn for the search
422 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
423 * @param expr Search expression - specified in local charset
424 * @param attrs Attributes to retrieve - specified in utf8 or ascii
425 * @param res ** which will contain results - free res* with ads_msgfree()
426 * @param count Number of entries retrieved on this page
427 * @param cookie The paged results cookie to be returned on subsequent calls
428 * @return status of search
429 **/
430ADS_STATUS ads_do_paged_search(ADS_STRUCT *ads, const char *bind_path,
431			       int scope, const char *expr,
432			       const char **attrs, void **res,
433			       int *count, void **cookie)
434{
435	int rc, i, version;
436	char *utf8_expr, *utf8_path, **search_attrs;
437	LDAPControl PagedResults, NoReferrals, *controls[3], **rcontrols;
438	BerElement *cookie_be = NULL;
439	struct berval *cookie_bv= NULL;
440	TALLOC_CTX *ctx;
441
442	*res = NULL;
443
444	if (!(ctx = talloc_init("ads_do_paged_search")))
445		return ADS_ERROR(LDAP_NO_MEMORY);
446
447	/* 0 means the conversion worked but the result was empty
448	   so we only fail if it's -1.  In any case, it always
449	   at least nulls out the dest */
450	if ((push_utf8_talloc(ctx, &utf8_expr, expr) == (size_t)-1) ||
451	    (push_utf8_talloc(ctx, &utf8_path, bind_path) == (size_t)-1)) {
452		rc = LDAP_NO_MEMORY;
453		goto done;
454	}
455
456	if (!attrs || !(*attrs))
457		search_attrs = NULL;
458	else {
459		/* This would be the utf8-encoded version...*/
460		/* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
461		if (!(str_list_copy(&search_attrs, attrs))) {
462			rc = LDAP_NO_MEMORY;
463			goto done;
464		}
465	}
466
467
468	/* Paged results only available on ldap v3 or later */
469	ldap_get_option(ads->ld, LDAP_OPT_PROTOCOL_VERSION, &version);
470	if (version < LDAP_VERSION3) {
471		rc =  LDAP_NOT_SUPPORTED;
472		goto done;
473	}
474
475	cookie_be = ber_alloc_t(LBER_USE_DER);
476	if (cookie && *cookie) {
477		ber_printf(cookie_be, "{iO}", (ber_int_t) 1000, *cookie);
478		ber_bvfree(*cookie); /* don't need it from last time */
479		*cookie = NULL;
480	} else {
481		ber_printf(cookie_be, "{io}", (ber_int_t) 1000, "", 0);
482	}
483	ber_flatten(cookie_be, &cookie_bv);
484	PagedResults.ldctl_oid = ADS_PAGE_CTL_OID;
485	PagedResults.ldctl_iscritical = (char) 1;
486	PagedResults.ldctl_value.bv_len = cookie_bv->bv_len;
487	PagedResults.ldctl_value.bv_val = cookie_bv->bv_val;
488
489	NoReferrals.ldctl_oid = ADS_NO_REFERRALS_OID;
490	NoReferrals.ldctl_iscritical = (char) 0;
491	NoReferrals.ldctl_value.bv_len = 0;
492	NoReferrals.ldctl_value.bv_val = "";
493
494
495	controls[0] = &NoReferrals;
496	controls[1] = &PagedResults;
497	controls[2] = NULL;
498
499	/* we need to disable referrals as the openldap libs don't
500	   handle them and paged results at the same time.  Using them
501	   together results in the result record containing the server
502	   page control being removed from the result list (tridge/jmcd)
503
504	   leaving this in despite the control that says don't generate
505	   referrals, in case the server doesn't support it (jmcd)
506	*/
507	ldap_set_option(ads->ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
508
509	rc = ldap_search_with_timeout(ads->ld, utf8_path, scope, utf8_expr,
510				      search_attrs, 0, controls,
511				      NULL, LDAP_NO_LIMIT,
512				      (LDAPMessage **)res);
513
514	ber_free(cookie_be, 1);
515	ber_bvfree(cookie_bv);
516
517	if (rc) {
518		DEBUG(3,("ads_do_paged_search: ldap_search_with_timeout(%s) -> %s\n", expr,
519			 ldap_err2string(rc)));
520		goto done;
521	}
522
523	rc = ldap_parse_result(ads->ld, *res, NULL, NULL, NULL,
524					NULL, &rcontrols,  0);
525
526	if (!rcontrols) {
527		goto done;
528	}
529
530	for (i=0; rcontrols[i]; i++) {
531		if (strcmp(ADS_PAGE_CTL_OID, rcontrols[i]->ldctl_oid) == 0) {
532			cookie_be = ber_init(&rcontrols[i]->ldctl_value);
533			ber_scanf(cookie_be,"{iO}", (ber_int_t *) count,
534				  &cookie_bv);
535			/* the berval is the cookie, but must be freed when
536			   it is all done */
537			if (cookie_bv->bv_len) /* still more to do */
538				*cookie=ber_bvdup(cookie_bv);
539			else
540				*cookie=NULL;
541			ber_bvfree(cookie_bv);
542			ber_free(cookie_be, 1);
543			break;
544		}
545	}
546	ldap_controls_free(rcontrols);
547
548done:
549	talloc_destroy(ctx);
550	/* if/when we decide to utf8-encode attrs, take out this next line */
551	str_list_free(&search_attrs);
552
553	return ADS_ERROR(rc);
554}
555
556
557/**
558 * Get all results for a search.  This uses ads_do_paged_search() to return
559 * all entries in a large search.
560 * @param ads connection to ads server
561 * @param bind_path Base dn for the search
562 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
563 * @param expr Search expression
564 * @param attrs Attributes to retrieve
565 * @param res ** which will contain results - free res* with ads_msgfree()
566 * @return status of search
567 **/
568ADS_STATUS ads_do_search_all(ADS_STRUCT *ads, const char *bind_path,
569			     int scope, const char *expr,
570			     const char **attrs, void **res)
571{
572	void *cookie = NULL;
573	int count = 0;
574	ADS_STATUS status;
575
576	*res = NULL;
577	status = ads_do_paged_search(ads, bind_path, scope, expr, attrs, res,
578				     &count, &cookie);
579
580	if (!ADS_ERR_OK(status)) return status;
581
582	while (cookie) {
583		void *res2 = NULL;
584		ADS_STATUS status2;
585		LDAPMessage *msg, *next;
586
587		status2 = ads_do_paged_search(ads, bind_path, scope, expr,
588					      attrs, &res2, &count, &cookie);
589
590		if (!ADS_ERR_OK(status2)) break;
591
592		/* this relies on the way that ldap_add_result_entry() works internally. I hope
593		   that this works on all ldap libs, but I have only tested with openldap */
594		for (msg = ads_first_entry(ads, res2); msg; msg = next) {
595			next = ads_next_entry(ads, msg);
596			ldap_add_result_entry((LDAPMessage **)res, msg);
597		}
598		/* note that we do not free res2, as the memory is now
599                   part of the main returned list */
600	}
601
602	return status;
603}
604
605/**
606 * Run a function on all results for a search.  Uses ads_do_paged_search() and
607 *  runs the function as each page is returned, using ads_process_results()
608 * @param ads connection to ads server
609 * @param bind_path Base dn for the search
610 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
611 * @param expr Search expression - specified in local charset
612 * @param attrs Attributes to retrieve - specified in UTF-8 or ascii
613 * @param fn Function which takes attr name, values list, and data_area
614 * @param data_area Pointer which is passed to function on each call
615 * @return status of search
616 **/
617ADS_STATUS ads_do_search_all_fn(ADS_STRUCT *ads, const char *bind_path,
618				int scope, const char *expr, const char **attrs,
619				BOOL(*fn)(char *, void **, void *),
620				void *data_area)
621{
622	void *cookie = NULL;
623	int count = 0;
624	ADS_STATUS status;
625	void *res;
626
627	status = ads_do_paged_search(ads, bind_path, scope, expr, attrs, &res,
628				     &count, &cookie);
629
630	if (!ADS_ERR_OK(status)) return status;
631
632	ads_process_results(ads, res, fn, data_area);
633	ads_msgfree(ads, res);
634
635	while (cookie) {
636		status = ads_do_paged_search(ads, bind_path, scope, expr, attrs,
637					     &res, &count, &cookie);
638
639		if (!ADS_ERR_OK(status)) break;
640
641		ads_process_results(ads, res, fn, data_area);
642		ads_msgfree(ads, res);
643	}
644
645	return status;
646}
647
648/**
649 * Do a search with a timeout.
650 * @param ads connection to ads server
651 * @param bind_path Base dn for the search
652 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
653 * @param expr Search expression
654 * @param attrs Attributes to retrieve
655 * @param res ** which will contain results - free res* with ads_msgfree()
656 * @return status of search
657 **/
658ADS_STATUS ads_do_search(ADS_STRUCT *ads, const char *bind_path, int scope,
659			 const char *expr,
660			 const char **attrs, void **res)
661{
662	int rc;
663	char *utf8_expr, *utf8_path, **search_attrs = NULL;
664	TALLOC_CTX *ctx;
665
666	*res = NULL;
667	if (!(ctx = talloc_init("ads_do_search"))) {
668		DEBUG(1,("ads_do_search: talloc_init() failed!"));
669		return ADS_ERROR(LDAP_NO_MEMORY);
670	}
671
672	/* 0 means the conversion worked but the result was empty
673	   so we only fail if it's negative.  In any case, it always
674	   at least nulls out the dest */
675	if ((push_utf8_talloc(ctx, &utf8_expr, expr) == (size_t)-1) ||
676	    (push_utf8_talloc(ctx, &utf8_path, bind_path) == (size_t)-1)) {
677		DEBUG(1,("ads_do_search: push_utf8_talloc() failed!"));
678		rc = LDAP_NO_MEMORY;
679		goto done;
680	}
681
682	if (!attrs || !(*attrs))
683		search_attrs = NULL;
684	else {
685		/* This would be the utf8-encoded version...*/
686		/* if (!(search_attrs = ads_push_strvals(ctx, attrs)))  */
687		if (!(str_list_copy(&search_attrs, attrs)))
688		{
689			DEBUG(1,("ads_do_search: str_list_copy() failed!"));
690			rc = LDAP_NO_MEMORY;
691			goto done;
692		}
693	}
694
695	/* see the note in ads_do_paged_search - we *must* disable referrals */
696	ldap_set_option(ads->ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
697
698	rc = ldap_search_with_timeout(ads->ld, utf8_path, scope, utf8_expr,
699				      search_attrs, 0, NULL, NULL,
700				      LDAP_NO_LIMIT,
701				      (LDAPMessage **)res);
702
703	if (rc == LDAP_SIZELIMIT_EXCEEDED) {
704		DEBUG(3,("Warning! sizelimit exceeded in ldap. Truncating.\n"));
705		rc = 0;
706	}
707
708 done:
709	talloc_destroy(ctx);
710	/* if/when we decide to utf8-encode attrs, take out this next line */
711	str_list_free(&search_attrs);
712	return ADS_ERROR(rc);
713}
714/**
715 * Do a general ADS search
716 * @param ads connection to ads server
717 * @param res ** which will contain results - free res* with ads_msgfree()
718 * @param expr Search expression
719 * @param attrs Attributes to retrieve
720 * @return status of search
721 **/
722ADS_STATUS ads_search(ADS_STRUCT *ads, void **res,
723		      const char *expr,
724		      const char **attrs)
725{
726	return ads_do_search(ads, ads->config.bind_path, LDAP_SCOPE_SUBTREE,
727			     expr, attrs, res);
728}
729
730/**
731 * Do a search on a specific DistinguishedName
732 * @param ads connection to ads server
733 * @param res ** which will contain results - free res* with ads_msgfree()
734 * @param dn DistinguishName to search
735 * @param attrs Attributes to retrieve
736 * @return status of search
737 **/
738ADS_STATUS ads_search_dn(ADS_STRUCT *ads, void **res,
739			 const char *dn,
740			 const char **attrs)
741{
742	return ads_do_search(ads, dn, LDAP_SCOPE_BASE, "(objectclass=*)", attrs, res);
743}
744
745/**
746 * Free up memory from a ads_search
747 * @param ads connection to ads server
748 * @param msg Search results to free
749 **/
750void ads_msgfree(ADS_STRUCT *ads, void *msg)
751{
752	if (!msg) return;
753	ldap_msgfree(msg);
754}
755
756/**
757 * Free up memory from various ads requests
758 * @param ads connection to ads server
759 * @param mem Area to free
760 **/
761void ads_memfree(ADS_STRUCT *ads, void *mem)
762{
763	SAFE_FREE(mem);
764}
765
766/**
767 * Get a dn from search results
768 * @param ads connection to ads server
769 * @param msg Search result
770 * @return dn string
771 **/
772char *ads_get_dn(ADS_STRUCT *ads, void *msg)
773{
774	char *utf8_dn, *unix_dn;
775
776	utf8_dn = ldap_get_dn(ads->ld, msg);
777
778	if (!utf8_dn) {
779		DEBUG (5, ("ads_get_dn: ldap_get_dn failed\n"));
780		return NULL;
781	}
782
783	if (pull_utf8_allocate(&unix_dn, utf8_dn) == (size_t)-1) {
784		DEBUG(0,("ads_get_dn: string conversion failure utf8 [%s]\n",
785			utf8_dn ));
786		return NULL;
787	}
788	ldap_memfree(utf8_dn);
789	return unix_dn;
790}
791
792/**
793 * Find a machine account given a hostname
794 * @param ads connection to ads server
795 * @param res ** which will contain results - free res* with ads_msgfree()
796 * @param host Hostname to search for
797 * @return status of search
798 **/
799ADS_STATUS ads_find_machine_acct(ADS_STRUCT *ads, void **res, const char *machine)
800{
801	ADS_STATUS status;
802	char *expr;
803	const char *attrs[] = {"*", "nTSecurityDescriptor", NULL};
804
805	*res = NULL;
806
807	/* the easiest way to find a machine account anywhere in the tree
808	   is to look for hostname$ */
809	if (asprintf(&expr, "(samAccountName=%s$)", machine) == -1) {
810		DEBUG(1, ("asprintf failed!\n"));
811		return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
812	}
813
814	status = ads_search(ads, res, expr, attrs);
815	SAFE_FREE(expr);
816	return status;
817}
818
819/**
820 * Initialize a list of mods to be used in a modify request
821 * @param ctx An initialized TALLOC_CTX
822 * @return allocated ADS_MODLIST
823 **/
824ADS_MODLIST ads_init_mods(TALLOC_CTX *ctx)
825{
826#define ADS_MODLIST_ALLOC_SIZE 10
827	LDAPMod **mods;
828
829	if ((mods = TALLOC_ZERO_ARRAY(ctx, LDAPMod *, ADS_MODLIST_ALLOC_SIZE + 1)))
830		/* -1 is safety to make sure we don't go over the end.
831		   need to reset it to NULL before doing ldap modify */
832		mods[ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
833
834	return mods;
835}
836
837
838/*
839  add an attribute to the list, with values list already constructed
840*/
841static ADS_STATUS ads_modlist_add(TALLOC_CTX *ctx, ADS_MODLIST *mods,
842				  int mod_op, const char *name,
843				  const void **invals)
844{
845	int curmod;
846	LDAPMod **modlist = (LDAPMod **) *mods;
847	struct berval **ber_values = NULL;
848	char **char_values = NULL;
849
850	if (!invals) {
851		mod_op = LDAP_MOD_DELETE;
852	} else {
853		if (mod_op & LDAP_MOD_BVALUES)
854			ber_values = ads_dup_values(ctx,
855						(const struct berval **)invals);
856		else
857			char_values = ads_push_strvals(ctx,
858						  (const char **) invals);
859	}
860
861	/* find the first empty slot */
862	for (curmod=0; modlist[curmod] && modlist[curmod] != (LDAPMod *) -1;
863	     curmod++);
864	if (modlist[curmod] == (LDAPMod *) -1) {
865		if (!(modlist = TALLOC_REALLOC_ARRAY(ctx, modlist, LDAPMod *,
866				curmod+ADS_MODLIST_ALLOC_SIZE+1)))
867			return ADS_ERROR(LDAP_NO_MEMORY);
868		memset(&modlist[curmod], 0,
869		       ADS_MODLIST_ALLOC_SIZE*sizeof(LDAPMod *));
870		modlist[curmod+ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
871		*mods = modlist;
872	}
873
874	if (!(modlist[curmod] = TALLOC_ZERO_P(ctx, LDAPMod)))
875		return ADS_ERROR(LDAP_NO_MEMORY);
876	modlist[curmod]->mod_type = talloc_strdup(ctx, name);
877	if (mod_op & LDAP_MOD_BVALUES) {
878		modlist[curmod]->mod_bvalues = ber_values;
879	} else if (mod_op & LDAP_MOD_DELETE) {
880		modlist[curmod]->mod_values = NULL;
881	} else {
882		modlist[curmod]->mod_values = char_values;
883	}
884
885	modlist[curmod]->mod_op = mod_op;
886	return ADS_ERROR(LDAP_SUCCESS);
887}
888
889/**
890 * Add a single string value to a mod list
891 * @param ctx An initialized TALLOC_CTX
892 * @param mods An initialized ADS_MODLIST
893 * @param name The attribute name to add
894 * @param val The value to add - NULL means DELETE
895 * @return ADS STATUS indicating success of add
896 **/
897ADS_STATUS ads_mod_str(TALLOC_CTX *ctx, ADS_MODLIST *mods,
898		       const char *name, const char *val)
899{
900	const char *values[2];
901
902	values[0] = val;
903	values[1] = NULL;
904
905	if (!val)
906		return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
907	return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE, name,
908			       (const void **) values);
909}
910
911/**
912 * Add an array of string values to a mod list
913 * @param ctx An initialized TALLOC_CTX
914 * @param mods An initialized ADS_MODLIST
915 * @param name The attribute name to add
916 * @param vals The array of string values to add - NULL means DELETE
917 * @return ADS STATUS indicating success of add
918 **/
919ADS_STATUS ads_mod_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
920			   const char *name, const char **vals)
921{
922	if (!vals)
923		return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
924	return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE,
925			       name, (const void **) vals);
926}
927
928/**
929 * Add a single ber-encoded value to a mod list
930 * @param ctx An initialized TALLOC_CTX
931 * @param mods An initialized ADS_MODLIST
932 * @param name The attribute name to add
933 * @param val The value to add - NULL means DELETE
934 * @return ADS STATUS indicating success of add
935 **/
936static ADS_STATUS ads_mod_ber(TALLOC_CTX *ctx, ADS_MODLIST *mods,
937			      const char *name, const struct berval *val)
938{
939	const struct berval *values[2];
940
941	values[0] = val;
942	values[1] = NULL;
943	if (!val)
944		return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
945	return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE|LDAP_MOD_BVALUES,
946			       name, (const void **) values);
947}
948
949/**
950 * Perform an ldap modify
951 * @param ads connection to ads server
952 * @param mod_dn DistinguishedName to modify
953 * @param mods list of modifications to perform
954 * @return status of modify
955 **/
956ADS_STATUS ads_gen_mod(ADS_STRUCT *ads, const char *mod_dn, ADS_MODLIST mods)
957{
958	int ret,i;
959	char *utf8_dn = NULL;
960	/*
961	   this control is needed to modify that contains a currently
962	   non-existent attribute (but allowable for the object) to run
963	*/
964	LDAPControl PermitModify = {
965		ADS_PERMIT_MODIFY_OID,
966		{0, NULL},
967		(char) 1};
968	LDAPControl *controls[2];
969
970	controls[0] = &PermitModify;
971	controls[1] = NULL;
972
973	if (push_utf8_allocate(&utf8_dn, mod_dn) == -1) {
974		return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
975	}
976
977	/* find the end of the list, marked by NULL or -1 */
978	for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
979	/* make sure the end of the list is NULL */
980	mods[i] = NULL;
981	ret = ldap_modify_ext_s(ads->ld, utf8_dn,
982				(LDAPMod **) mods, controls, NULL);
983	SAFE_FREE(utf8_dn);
984	return ADS_ERROR(ret);
985}
986
987/**
988 * Perform an ldap add
989 * @param ads connection to ads server
990 * @param new_dn DistinguishedName to add
991 * @param mods list of attributes and values for DN
992 * @return status of add
993 **/
994ADS_STATUS ads_gen_add(ADS_STRUCT *ads, const char *new_dn, ADS_MODLIST mods)
995{
996	int ret, i;
997	char *utf8_dn = NULL;
998
999	if (push_utf8_allocate(&utf8_dn, new_dn) == -1) {
1000		DEBUG(1, ("ads_gen_add: push_utf8_allocate failed!"));
1001		return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1002	}
1003
1004	/* find the end of the list, marked by NULL or -1 */
1005	for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1006	/* make sure the end of the list is NULL */
1007	mods[i] = NULL;
1008
1009	ret = ldap_add_s(ads->ld, utf8_dn, mods);
1010	SAFE_FREE(utf8_dn);
1011	return ADS_ERROR(ret);
1012}
1013
1014/**
1015 * Delete a DistinguishedName
1016 * @param ads connection to ads server
1017 * @param new_dn DistinguishedName to delete
1018 * @return status of delete
1019 **/
1020ADS_STATUS ads_del_dn(ADS_STRUCT *ads, char *del_dn)
1021{
1022	int ret;
1023	char *utf8_dn = NULL;
1024	if (push_utf8_allocate(&utf8_dn, del_dn) == -1) {
1025		DEBUG(1, ("ads_del_dn: push_utf8_allocate failed!"));
1026		return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1027	}
1028
1029	ret = ldap_delete_s(ads->ld, utf8_dn);
1030	return ADS_ERROR(ret);
1031}
1032
1033/**
1034 * Build an org unit string
1035 *  if org unit is Computers or blank then assume a container, otherwise
1036 *  assume a \ separated list of organisational units
1037 * @param ads connection to ads server
1038 * @param org_unit Organizational unit
1039 * @return org unit string - caller must free
1040 **/
1041char *ads_ou_string(ADS_STRUCT *ads, const char *org_unit)
1042{
1043	char *ret = NULL;
1044
1045	if (!org_unit || !*org_unit) {
1046
1047		ret = ads_default_ou_string(ads, WELL_KNOWN_GUID_COMPUTERS);
1048
1049		/* samba4 might not yet respond to a wellknownobject-query */
1050		return ret ? ret : SMB_STRDUP("cn=Computers");
1051	}
1052
1053	if (strequal(org_unit, "Computers")) {
1054		return SMB_STRDUP("cn=Computers");
1055	}
1056
1057	return ads_build_path(org_unit, "\\/", "ou=", 1);
1058}
1059
1060/**
1061 * Get a org unit string for a well-known GUID
1062 * @param ads connection to ads server
1063 * @param wknguid Well known GUID
1064 * @return org unit string - caller must free
1065 **/
1066char *ads_default_ou_string(ADS_STRUCT *ads, const char *wknguid)
1067{
1068	ADS_STATUS status;
1069	void *res;
1070	char *base, *wkn_dn, *ret, **wkn_dn_exp, **bind_dn_exp;
1071	const char *attrs[] = {"distinguishedName", NULL};
1072	int new_ln, wkn_ln, bind_ln, i;
1073
1074	if (wknguid == NULL) {
1075		return NULL;
1076	}
1077
1078	if (asprintf(&base, "<WKGUID=%s,%s>", wknguid, ads->config.bind_path ) == -1) {
1079		DEBUG(1, ("asprintf failed!\n"));
1080		return NULL;
1081	}
1082
1083	status = ads_search_dn(ads, &res, base, attrs);
1084	if (!ADS_ERR_OK(status)) {
1085		DEBUG(1,("Failed while searching for: %s\n", base));
1086		return NULL;
1087	}
1088	free(base);
1089
1090	if (ads_count_replies(ads, res) != 1) {
1091		return NULL;
1092	}
1093
1094	/* substitute the bind-path from the well-known-guid-search result */
1095	wkn_dn = ads_get_dn(ads, res);
1096	wkn_dn_exp = ldap_explode_dn(wkn_dn, 0);
1097	bind_dn_exp = ldap_explode_dn(ads->config.bind_path, 0);
1098
1099	for (wkn_ln=0; wkn_dn_exp[wkn_ln]; wkn_ln++)
1100		;
1101	for (bind_ln=0; bind_dn_exp[bind_ln]; bind_ln++)
1102		;
1103
1104	new_ln = wkn_ln - bind_ln;
1105
1106	ret = wkn_dn_exp[0];
1107
1108	for (i=1; i < new_ln; i++) {
1109		char *s;
1110		asprintf(&s, "%s,%s", ret, wkn_dn_exp[i]);
1111		ret = SMB_STRDUP(s);
1112		free(s);
1113	}
1114
1115	return ret;
1116}
1117
1118/**
1119 * Adds (appends) an item to an attribute array, rather then
1120 * replacing the whole list
1121 * @param ctx An initialized TALLOC_CTX
1122 * @param mods An initialized ADS_MODLIST
1123 * @param name name of the ldap attribute to append to
1124 * @param vals an array of values to add
1125 * @return status of addition
1126 **/
1127
1128ADS_STATUS ads_add_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1129				const char *name, const char **vals)
1130{
1131	return ads_modlist_add(ctx, mods, LDAP_MOD_ADD, name, (const void **) vals);
1132}
1133
1134/**
1135 * Determines the computer account's current KVNO via an LDAP lookup
1136 * @param ads An initialized ADS_STRUCT
1137 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1138 * @return the kvno for the computer account, or -1 in case of a failure.
1139 **/
1140
1141uint32 ads_get_kvno(ADS_STRUCT *ads, const char *machine_name)
1142{
1143	LDAPMessage *res = NULL;
1144	uint32 kvno = (uint32)-1;      /* -1 indicates a failure */
1145	char *filter;
1146	const char *attrs[] = {"msDS-KeyVersionNumber", NULL};
1147	char *dn_string = NULL;
1148	ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS);
1149
1150	DEBUG(5,("ads_get_kvno: Searching for host %s\n", machine_name));
1151	if (asprintf(&filter, "(samAccountName=%s$)", machine_name) == -1) {
1152		return kvno;
1153	}
1154	ret = ads_search(ads, (void**) &res, filter, attrs);
1155	SAFE_FREE(filter);
1156	if (!ADS_ERR_OK(ret) && ads_count_replies(ads, res)) {
1157		DEBUG(1,("ads_get_kvno: Computer Account For %s not found.\n", machine_name));
1158		ads_msgfree(ads, res);
1159		return kvno;
1160	}
1161
1162	dn_string = ads_get_dn(ads, res);
1163	if (!dn_string) {
1164		DEBUG(0,("ads_get_kvno: out of memory.\n"));
1165		ads_msgfree(ads, res);
1166		return kvno;
1167	}
1168	DEBUG(5,("ads_get_kvno: Using: %s\n", dn_string));
1169	ads_memfree(ads, dn_string);
1170
1171	/* ---------------------------------------------------------
1172	 * 0 is returned as a default KVNO from this point on...
1173	 * This is done because Windows 2000 does not support key
1174	 * version numbers.  Chances are that a failure in the next
1175	 * step is simply due to Windows 2000 being used for a
1176	 * domain controller. */
1177	kvno = 0;
1178
1179	if (!ads_pull_uint32(ads, res, "msDS-KeyVersionNumber", &kvno)) {
1180		DEBUG(3,("ads_get_kvno: Error Determining KVNO!\n"));
1181		DEBUG(3,("ads_get_kvno: Windows 2000 does not support KVNO's, so this may be normal.\n"));
1182		ads_msgfree(ads, res);
1183		return kvno;
1184	}
1185
1186	/* Success */
1187	DEBUG(5,("ads_get_kvno: Looked Up KVNO of: %d\n", kvno));
1188	ads_msgfree(ads, res);
1189	return kvno;
1190}
1191
1192/**
1193 * This clears out all registered spn's for a given hostname
1194 * @param ads An initilaized ADS_STRUCT
1195 * @param machine_name the NetBIOS name of the computer.
1196 * @return 0 upon success, non-zero otherwise.
1197 **/
1198
1199ADS_STATUS ads_clear_service_principal_names(ADS_STRUCT *ads, const char *machine_name)
1200{
1201	TALLOC_CTX *ctx;
1202	LDAPMessage *res = NULL;
1203	ADS_MODLIST mods;
1204	const char *servicePrincipalName[1] = {NULL};
1205	ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS);
1206	char *dn_string = NULL;
1207
1208	ret = ads_find_machine_acct(ads, (void **)&res, machine_name);
1209	if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
1210		DEBUG(5,("ads_clear_service_principal_names: WARNING: Host Account for %s not found... skipping operation.\n", machine_name));
1211		DEBUG(5,("ads_clear_service_principal_names: WARNING: Service Principals for %s have NOT been cleared.\n", machine_name));
1212		ads_msgfree(ads, res);
1213		return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1214	}
1215
1216	DEBUG(5,("ads_clear_service_principal_names: Host account for %s found\n", machine_name));
1217	ctx = talloc_init("ads_clear_service_principal_names");
1218	if (!ctx) {
1219		ads_msgfree(ads, res);
1220		return ADS_ERROR(LDAP_NO_MEMORY);
1221	}
1222
1223	if (!(mods = ads_init_mods(ctx))) {
1224		talloc_destroy(ctx);
1225		ads_msgfree(ads, res);
1226		return ADS_ERROR(LDAP_NO_MEMORY);
1227	}
1228	ret = ads_mod_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1229	if (!ADS_ERR_OK(ret)) {
1230		DEBUG(1,("ads_clear_service_principal_names: Error creating strlist.\n"));
1231		ads_msgfree(ads, res);
1232		talloc_destroy(ctx);
1233		return ret;
1234	}
1235	dn_string = ads_get_dn(ads, res);
1236	if (!dn_string) {
1237		talloc_destroy(ctx);
1238		ads_msgfree(ads, res);
1239		return ADS_ERROR(LDAP_NO_MEMORY);
1240	}
1241	ret = ads_gen_mod(ads, dn_string, mods);
1242	ads_memfree(ads,dn_string);
1243	if (!ADS_ERR_OK(ret)) {
1244		DEBUG(1,("ads_clear_service_principal_names: Error: Updating Service Principals for machine %s in LDAP\n",
1245			machine_name));
1246		ads_msgfree(ads, res);
1247		talloc_destroy(ctx);
1248		return ret;
1249	}
1250
1251	ads_msgfree(ads, res);
1252	talloc_destroy(ctx);
1253	return ret;
1254}
1255
1256/**
1257 * This adds a service principal name to an existing computer account
1258 * (found by hostname) in AD.
1259 * @param ads An initialized ADS_STRUCT
1260 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1261 * @param spn A string of the service principal to add, i.e. 'host'
1262 * @return 0 upon sucess, or non-zero if a failure occurs
1263 **/
1264
1265ADS_STATUS ads_add_service_principal_name(ADS_STRUCT *ads, const char *machine_name, const char *spn)
1266{
1267	ADS_STATUS ret;
1268	TALLOC_CTX *ctx;
1269	LDAPMessage *res = NULL;
1270	char *host_spn, *host_upn, *psp1, *psp2, *psp3;
1271	ADS_MODLIST mods;
1272	fstring my_fqdn;
1273	char *dn_string = NULL;
1274	const char *servicePrincipalName[4] = {NULL, NULL, NULL, NULL};
1275
1276	ret = ads_find_machine_acct(ads, (void **)&res, machine_name);
1277	if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
1278		DEBUG(1,("ads_add_service_principal_name: WARNING: Host Account for %s not found... skipping operation.\n",
1279			machine_name));
1280		DEBUG(1,("ads_add_service_principal_name: WARNING: Service Principal '%s/%s@%s' has NOT been added.\n",
1281			spn, machine_name, ads->config.realm));
1282		ads_msgfree(ads, res);
1283		return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1284	}
1285
1286	DEBUG(1,("ads_add_service_principal_name: Host account for %s found\n", machine_name));
1287	if (!(ctx = talloc_init("ads_add_service_principal_name"))) {
1288		ads_msgfree(ads, res);
1289		return ADS_ERROR(LDAP_NO_MEMORY);
1290	}
1291
1292	name_to_fqdn(my_fqdn, machine_name);
1293	strlower_m(my_fqdn);
1294
1295	if (!(host_spn = talloc_asprintf(ctx, "HOST/%s", my_fqdn))) {
1296		talloc_destroy(ctx);
1297		ads_msgfree(ads, res);
1298		return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1299	}
1300	if (!(host_upn = talloc_asprintf(ctx, "%s@%s", host_spn, ads->config.realm))) {
1301		talloc_destroy(ctx);
1302		ads_msgfree(ads, res);
1303		return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1304	}
1305
1306	/* Add the extra principal */
1307	psp1 = talloc_asprintf(ctx, "%s/%s", spn, machine_name);
1308	strupper_m(psp1);
1309	strlower_m(&psp1[strlen(spn)]);
1310	DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n", psp1, machine_name));
1311	servicePrincipalName[0] = psp1;
1312	psp2 = talloc_asprintf(ctx, "%s/%s.%s", spn, machine_name, ads->config.realm);
1313	strupper_m(psp2);
1314	strlower_m(&psp2[strlen(spn)]);
1315	DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n", psp2, machine_name));
1316	servicePrincipalName[1] = psp2;
1317
1318	/* Add another principal in case the realm != the DNS domain, so that
1319	 * the KDC doesn't send "server principal unknown" errors to clients
1320	 * which use the DNS name in determining service principal names. */
1321	psp3 = talloc_asprintf(ctx, "%s/%s", spn, my_fqdn);
1322	strupper_m(psp3);
1323	strlower_m(&psp3[strlen(spn)]);
1324	if (strcmp(psp2, psp3) != 0) {
1325		DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n", psp3, machine_name));
1326		servicePrincipalName[2] = psp3;
1327	}
1328
1329	if (!(mods = ads_init_mods(ctx))) {
1330		talloc_destroy(ctx);
1331		ads_msgfree(ads, res);
1332		return ADS_ERROR(LDAP_NO_MEMORY);
1333	}
1334	ret = ads_add_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1335	if (!ADS_ERR_OK(ret)) {
1336		DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
1337		talloc_destroy(ctx);
1338		ads_msgfree(ads, res);
1339		return ret;
1340	}
1341	dn_string = ads_get_dn(ads, res);
1342	if (!dn_string) {
1343		talloc_destroy(ctx);
1344		ads_msgfree(ads, res);
1345		return ADS_ERROR(LDAP_NO_MEMORY);
1346	}
1347	ret = ads_gen_mod(ads, dn_string, mods);
1348	ads_memfree(ads,dn_string);
1349	if (!ADS_ERR_OK(ret)) {
1350		DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
1351		talloc_destroy(ctx);
1352		ads_msgfree(ads, res);
1353		return ret;
1354	}
1355
1356	talloc_destroy(ctx);
1357	ads_msgfree(ads, res);
1358	return ret;
1359}
1360
1361/**
1362 * adds a machine account to the ADS server
1363 * @param ads An intialized ADS_STRUCT
1364 * @param machine_name - the NetBIOS machine name of this account.
1365 * @param account_type A number indicating the type of account to create
1366 * @param org_unit The LDAP path in which to place this account
1367 * @return 0 upon success, or non-zero otherwise
1368**/
1369
1370static ADS_STATUS ads_add_machine_acct(ADS_STRUCT *ads, const char *machine_name,
1371				       uint32 account_type,
1372				       const char *org_unit)
1373{
1374	ADS_STATUS ret, status;
1375	char *host_spn, *host_upn, *new_dn, *samAccountName, *controlstr;
1376	TALLOC_CTX *ctx;
1377	ADS_MODLIST mods;
1378	const char *objectClass[] = {"top", "person", "organizationalPerson",
1379				     "user", "computer", NULL};
1380	const char *servicePrincipalName[7] = {NULL, NULL, NULL, NULL, NULL, NULL, NULL};
1381	char *psp, *psp2, *psp3, *psp4;
1382	unsigned acct_control;
1383	unsigned exists=0;
1384	fstring my_fqdn;
1385	LDAPMessage *res = NULL;
1386	int i, next_spn;
1387
1388	if (!(ctx = talloc_init("ads_add_machine_acct")))
1389		return ADS_ERROR(LDAP_NO_MEMORY);
1390
1391	ret = ADS_ERROR(LDAP_NO_MEMORY);
1392
1393	name_to_fqdn(my_fqdn, machine_name);
1394
1395	status = ads_find_machine_acct(ads, (void **)&res, machine_name);
1396	if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
1397		char *dn_string = ads_get_dn(ads, res);
1398		if (!dn_string) {
1399			DEBUG(1, ("ads_add_machine_acct: ads_get_dn returned NULL (malloc failure?)\n"));
1400			goto done;
1401		}
1402		new_dn = talloc_strdup(ctx, dn_string);
1403		ads_memfree(ads,dn_string);
1404		DEBUG(0, ("ads_add_machine_acct: Host account for %s already exists - modifying old account\n",
1405			machine_name));
1406		exists=1;
1407	} else {
1408		char *ou_str = ads_ou_string(ads,org_unit);
1409		if (!ou_str) {
1410			DEBUG(1, ("ads_add_machine_acct: ads_ou_string returned NULL (malloc failure?)\n"));
1411			goto done;
1412		}
1413		new_dn = talloc_asprintf(ctx, "cn=%s,%s,%s", machine_name, ou_str,
1414				ads->config.bind_path);
1415
1416		SAFE_FREE(ou_str);
1417	}
1418
1419	if (!new_dn) {
1420		goto done;
1421	}
1422
1423	if (!(host_spn = talloc_asprintf(ctx, "HOST/%s", machine_name)))
1424		goto done;
1425	if (!(host_upn = talloc_asprintf(ctx, "%s@%s", host_spn, ads->config.realm)))
1426		goto done;
1427	servicePrincipalName[0] = talloc_asprintf(ctx, "HOST/%s", machine_name);
1428	psp = talloc_asprintf(ctx, "HOST/%s.%s",
1429				machine_name,
1430				ads->config.realm);
1431	strlower_m(&psp[5]);
1432	servicePrincipalName[1] = psp;
1433	servicePrincipalName[2] = talloc_asprintf(ctx, "CIFS/%s", machine_name);
1434	psp2 = talloc_asprintf(ctx, "CIFS/%s.%s",
1435			       machine_name,
1436			       ads->config.realm);
1437	strlower_m(&psp2[5]);
1438	servicePrincipalName[3] = psp2;
1439
1440	/* Ensure servicePrincipalName[4] and [5] are unique. */
1441	strlower_m(my_fqdn);
1442	psp3 = talloc_asprintf(ctx, "CIFS/%s", my_fqdn);
1443	strlower_m(&psp3[5]);
1444
1445	next_spn = 4;
1446	for (i = 0; i < next_spn; i++) {
1447		if (strequal(servicePrincipalName[i], psp3))
1448			break;
1449	}
1450	if (i == next_spn) {
1451		servicePrincipalName[next_spn++] = psp3;
1452	}
1453
1454	psp4 = talloc_asprintf(ctx, "HOST/%s", my_fqdn);
1455	strlower_m(&psp4[5]);
1456	for (i = 0; i < next_spn; i++) {
1457		if (strequal(servicePrincipalName[i], psp4))
1458			break;
1459	}
1460	if (i == next_spn) {
1461		servicePrincipalName[next_spn++] = psp4;
1462	}
1463
1464	if (!(samAccountName = talloc_asprintf(ctx, "%s$", machine_name))) {
1465		goto done;
1466	}
1467
1468	acct_control = account_type | UF_DONT_EXPIRE_PASSWD;
1469#ifndef ENCTYPE_ARCFOUR_HMAC
1470	acct_control |= UF_USE_DES_KEY_ONLY;
1471#endif
1472
1473	if (!(controlstr = talloc_asprintf(ctx, "%u", acct_control))) {
1474		goto done;
1475	}
1476
1477	if (!(mods = ads_init_mods(ctx))) {
1478		goto done;
1479	}
1480
1481	if (!exists) {
1482		ads_mod_str(ctx, &mods, "cn", machine_name);
1483		ads_mod_str(ctx, &mods, "sAMAccountName", samAccountName);
1484		ads_mod_str(ctx, &mods, "userAccountControl", controlstr);
1485		ads_mod_strlist(ctx, &mods, "objectClass", objectClass);
1486	}
1487	ads_mod_str(ctx, &mods, "dNSHostName", my_fqdn);
1488	ads_mod_str(ctx, &mods, "userPrincipalName", host_upn);
1489	ads_mod_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1490	ads_mod_str(ctx, &mods, "operatingSystem", "Samba");
1491	ads_mod_str(ctx, &mods, "operatingSystemVersion", SAMBA_VERSION_STRING);
1492
1493	if (!exists)  {
1494		ret = ads_gen_add(ads, new_dn, mods);
1495	} else {
1496		ret = ads_gen_mod(ads, new_dn, mods);
1497	}
1498
1499	if (!ADS_ERR_OK(ret)) {
1500		goto done;
1501	}
1502
1503	/* Do not fail if we can't set security descriptor
1504	 * it shouldn't be mandatory and probably we just
1505	 * don't have enough rights to do it.
1506	 */
1507	if (!exists) {
1508		status = ads_set_machine_sd(ads, machine_name, new_dn);
1509
1510		if (!ADS_ERR_OK(status)) {
1511			DEBUG(0, ("Warning: ads_set_machine_sd: %s\n",
1512					ads_errstr(status)));
1513		}
1514	}
1515done:
1516	ads_msgfree(ads, res);
1517	talloc_destroy(ctx);
1518	return ret;
1519}
1520
1521/*
1522  dump a binary result from ldap
1523*/
1524static void dump_binary(const char *field, struct berval **values)
1525{
1526	int i, j;
1527	for (i=0; values[i]; i++) {
1528		printf("%s: ", field);
1529		for (j=0; j<values[i]->bv_len; j++) {
1530			printf("%02X", (unsigned char)values[i]->bv_val[j]);
1531		}
1532		printf("\n");
1533	}
1534}
1535
1536static void dump_guid(const char *field, struct berval **values)
1537{
1538	int i;
1539	UUID_FLAT guid;
1540	for (i=0; values[i]; i++) {
1541		memcpy(guid.info, values[i]->bv_val, sizeof(guid.info));
1542		printf("%s: %s\n", field,
1543		       smb_uuid_string_static(smb_uuid_unpack_static(guid)));
1544	}
1545}
1546
1547/*
1548  dump a sid result from ldap
1549*/
1550static void dump_sid(const char *field, struct berval **values)
1551{
1552	int i;
1553	for (i=0; values[i]; i++) {
1554		DOM_SID sid;
1555		sid_parse(values[i]->bv_val, values[i]->bv_len, &sid);
1556		printf("%s: %s\n", field, sid_string_static(&sid));
1557	}
1558}
1559
1560/*
1561  dump ntSecurityDescriptor
1562*/
1563static void dump_sd(const char *filed, struct berval **values)
1564{
1565	prs_struct ps;
1566
1567	SEC_DESC   *psd = 0;
1568	TALLOC_CTX *ctx = 0;
1569
1570	if (!(ctx = talloc_init("sec_io_desc")))
1571		return;
1572
1573	/* prepare data */
1574	prs_init(&ps, values[0]->bv_len, ctx, UNMARSHALL);
1575	prs_copy_data_in(&ps, values[0]->bv_val, values[0]->bv_len);
1576	prs_set_offset(&ps,0);
1577
1578	/* parse secdesc */
1579	if (!sec_io_desc("sd", &psd, &ps, 1)) {
1580		prs_mem_free(&ps);
1581		talloc_destroy(ctx);
1582		return;
1583	}
1584	if (psd) ads_disp_sd(psd);
1585
1586	prs_mem_free(&ps);
1587	talloc_destroy(ctx);
1588}
1589
1590/*
1591  dump a string result from ldap
1592*/
1593static void dump_string(const char *field, char **values)
1594{
1595	int i;
1596	for (i=0; values[i]; i++) {
1597		printf("%s: %s\n", field, values[i]);
1598	}
1599}
1600
1601/*
1602  dump a field from LDAP on stdout
1603  used for debugging
1604*/
1605
1606static BOOL ads_dump_field(char *field, void **values, void *data_area)
1607{
1608	const struct {
1609		const char *name;
1610		BOOL string;
1611		void (*handler)(const char *, struct berval **);
1612	} handlers[] = {
1613		{"objectGUID", False, dump_guid},
1614		{"nTSecurityDescriptor", False, dump_sd},
1615		{"dnsRecord", False, dump_binary},
1616		{"objectSid", False, dump_sid},
1617		{"tokenGroups", False, dump_sid},
1618		{NULL, True, NULL}
1619	};
1620	int i;
1621
1622	if (!field) { /* must be end of an entry */
1623		printf("\n");
1624		return False;
1625	}
1626
1627	for (i=0; handlers[i].name; i++) {
1628		if (StrCaseCmp(handlers[i].name, field) == 0) {
1629			if (!values) /* first time, indicate string or not */
1630				return handlers[i].string;
1631			handlers[i].handler(field, (struct berval **) values);
1632			break;
1633		}
1634	}
1635	if (!handlers[i].name) {
1636		if (!values) /* first time, indicate string conversion */
1637			return True;
1638		dump_string(field, (char **)values);
1639	}
1640	return False;
1641}
1642
1643/**
1644 * Dump a result from LDAP on stdout
1645 *  used for debugging
1646 * @param ads connection to ads server
1647 * @param res Results to dump
1648 **/
1649
1650void ads_dump(ADS_STRUCT *ads, void *res)
1651{
1652	ads_process_results(ads, res, ads_dump_field, NULL);
1653}
1654
1655/**
1656 * Walk through results, calling a function for each entry found.
1657 *  The function receives a field name, a berval * array of values,
1658 *  and a data area passed through from the start.  The function is
1659 *  called once with null for field and values at the end of each
1660 *  entry.
1661 * @param ads connection to ads server
1662 * @param res Results to process
1663 * @param fn Function for processing each result
1664 * @param data_area user-defined area to pass to function
1665 **/
1666void ads_process_results(ADS_STRUCT *ads, void *res,
1667			 BOOL(*fn)(char *, void **, void *),
1668			 void *data_area)
1669{
1670	void *msg;
1671	TALLOC_CTX *ctx;
1672
1673	if (!(ctx = talloc_init("ads_process_results")))
1674		return;
1675
1676	for (msg = ads_first_entry(ads, res); msg;
1677	     msg = ads_next_entry(ads, msg)) {
1678		char *utf8_field;
1679		BerElement *b;
1680
1681		for (utf8_field=ldap_first_attribute(ads->ld,
1682						     (LDAPMessage *)msg,&b);
1683		     utf8_field;
1684		     utf8_field=ldap_next_attribute(ads->ld,
1685						    (LDAPMessage *)msg,b)) {
1686			struct berval **ber_vals;
1687			char **str_vals, **utf8_vals;
1688			char *field;
1689			BOOL string;
1690
1691			pull_utf8_talloc(ctx, &field, utf8_field);
1692			string = fn(field, NULL, data_area);
1693
1694			if (string) {
1695				utf8_vals = ldap_get_values(ads->ld,
1696					       	 (LDAPMessage *)msg, field);
1697				str_vals = ads_pull_strvals(ctx,
1698						  (const char **) utf8_vals);
1699				fn(field, (void **) str_vals, data_area);
1700				ldap_value_free(utf8_vals);
1701			} else {
1702				ber_vals = ldap_get_values_len(ads->ld,
1703						 (LDAPMessage *)msg, field);
1704				fn(field, (void **) ber_vals, data_area);
1705
1706				ldap_value_free_len(ber_vals);
1707			}
1708			ldap_memfree(utf8_field);
1709		}
1710		ber_free(b, 0);
1711		talloc_destroy_pool(ctx);
1712		fn(NULL, NULL, data_area); /* completed an entry */
1713
1714	}
1715	talloc_destroy(ctx);
1716}
1717
1718/**
1719 * count how many replies are in a LDAPMessage
1720 * @param ads connection to ads server
1721 * @param res Results to count
1722 * @return number of replies
1723 **/
1724int ads_count_replies(ADS_STRUCT *ads, void *res)
1725{
1726	return ldap_count_entries(ads->ld, (LDAPMessage *)res);
1727}
1728
1729/**
1730 * Join a machine to a realm
1731 *  Creates the machine account and sets the machine password
1732 * @param ads connection to ads server
1733 * @param machine name of host to add
1734 * @param org_unit Organizational unit to place machine in
1735 * @return status of join
1736 **/
1737ADS_STATUS ads_join_realm(ADS_STRUCT *ads, const char *machine_name,
1738			  uint32 account_type, const char *org_unit)
1739{
1740	ADS_STATUS status;
1741	LDAPMessage *res = NULL;
1742	char *machine;
1743
1744	/* machine name must be lowercase */
1745	machine = SMB_STRDUP(machine_name);
1746	strlower_m(machine);
1747
1748	/*
1749	status = ads_find_machine_acct(ads, (void **)&res, machine);
1750	if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
1751		DEBUG(0, ("Host account for %s already exists - deleting old account\n", machine));
1752		status = ads_leave_realm(ads, machine);
1753		if (!ADS_ERR_OK(status)) {
1754			DEBUG(0, ("Failed to delete host '%s' from the '%s' realm.\n",
1755				  machine, ads->config.realm));
1756			return status;
1757		}
1758	}
1759	*/
1760
1761	status = ads_add_machine_acct(ads, machine, account_type, org_unit);
1762	if (!ADS_ERR_OK(status)) {
1763		DEBUG(0, ("ads_join_realm: ads_add_machine_acct failed (%s): %s\n", machine, ads_errstr(status)));
1764		SAFE_FREE(machine);
1765		return status;
1766	}
1767
1768	status = ads_find_machine_acct(ads, (void **)&res, machine);
1769	if (!ADS_ERR_OK(status)) {
1770		DEBUG(0, ("ads_join_realm: Host account test failed for machine %s\n", machine));
1771		SAFE_FREE(machine);
1772		return status;
1773	}
1774
1775	SAFE_FREE(machine);
1776	ads_msgfree(ads, res);
1777
1778	return status;
1779}
1780
1781/**
1782 * Delete a machine from the realm
1783 * @param ads connection to ads server
1784 * @param hostname Machine to remove
1785 * @return status of delete
1786 **/
1787ADS_STATUS ads_leave_realm(ADS_STRUCT *ads, const char *hostname)
1788{
1789	ADS_STATUS status;
1790	void *res, *msg;
1791	char *hostnameDN, *host;
1792	int rc;
1793
1794	/* hostname must be lowercase */
1795	host = SMB_STRDUP(hostname);
1796	strlower_m(host);
1797
1798	status = ads_find_machine_acct(ads, &res, host);
1799	if (!ADS_ERR_OK(status)) {
1800	    DEBUG(0, ("Host account for %s does not exist.\n", host));
1801	    return status;
1802	}
1803
1804	msg = ads_first_entry(ads, res);
1805	if (!msg) {
1806		return ADS_ERROR_SYSTEM(ENOENT);
1807	}
1808
1809	hostnameDN = ads_get_dn(ads, (LDAPMessage *)msg);
1810	rc = ldap_delete_s(ads->ld, hostnameDN);
1811	ads_memfree(ads, hostnameDN);
1812	if (rc != LDAP_SUCCESS) {
1813		return ADS_ERROR(rc);
1814	}
1815
1816	status = ads_find_machine_acct(ads, &res, host);
1817	if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
1818		DEBUG(0, ("Failed to remove host account.\n"));
1819		return status;
1820	}
1821
1822	free(host);
1823
1824	return status;
1825}
1826
1827/**
1828 * add machine account to existing security descriptor
1829 * @param ads connection to ads server
1830 * @param hostname machine to add
1831 * @param dn DN of security descriptor
1832 * @return status
1833 **/
1834ADS_STATUS ads_set_machine_sd(ADS_STRUCT *ads, const char *hostname, char *dn)
1835{
1836	const char     *attrs[] = {"nTSecurityDescriptor", "objectSid", 0};
1837	char           *expr     = 0;
1838	size_t          sd_size = 0;
1839	struct berval   bval = {0, NULL};
1840	prs_struct      ps_wire;
1841	char           *escaped_hostname = escape_ldap_string_alloc(hostname);
1842
1843	LDAPMessage *res  = 0;
1844	LDAPMessage *msg  = 0;
1845	ADS_MODLIST  mods = 0;
1846
1847	NTSTATUS    status;
1848	ADS_STATUS  ret;
1849	DOM_SID     sid;
1850	SEC_DESC   *psd = NULL;
1851	TALLOC_CTX *ctx = NULL;
1852
1853	/* Avoid segmentation fault in prs_mem_free if
1854	 * we have to bail out before prs_init */
1855	ps_wire.is_dynamic = False;
1856
1857	if (!ads) return ADS_ERROR(LDAP_SERVER_DOWN);
1858
1859	ret = ADS_ERROR(LDAP_SUCCESS);
1860
1861	if (!escaped_hostname) {
1862		return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1863	}
1864
1865	if (asprintf(&expr, "(samAccountName=%s$)", escaped_hostname) == -1) {
1866		DEBUG(1, ("ads_set_machine_sd: asprintf failed!\n"));
1867		SAFE_FREE(escaped_hostname);
1868		return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1869	}
1870
1871	SAFE_FREE(escaped_hostname);
1872
1873	ret = ads_search(ads, (void *) &res, expr, attrs);
1874
1875	if (!ADS_ERR_OK(ret)) return ret;
1876
1877	if ( !(msg = ads_first_entry(ads, res) )) {
1878		ret = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
1879		goto ads_set_sd_error;
1880	}
1881
1882	if (!ads_pull_sid(ads, msg, attrs[1], &sid)) {
1883		ret = ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
1884		goto ads_set_sd_error;
1885	}
1886
1887	if (!(ctx = talloc_init("sec_io_desc"))) {
1888		ret =  ADS_ERROR(LDAP_NO_MEMORY);
1889		goto ads_set_sd_error;
1890	}
1891
1892	if (!ads_pull_sd(ads, ctx, msg, attrs[0], &psd)) {
1893		ret = ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
1894		goto ads_set_sd_error;
1895	}
1896
1897	status = sec_desc_add_sid(ctx, &psd, &sid, SEC_RIGHTS_FULL_CTRL, &sd_size);
1898
1899	if (!NT_STATUS_IS_OK(status)) {
1900		ret = ADS_ERROR_NT(status);
1901		goto ads_set_sd_error;
1902	}
1903
1904	if (!prs_init(&ps_wire, sd_size, ctx, MARSHALL)) {
1905		ret = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1906	}
1907
1908	if (!sec_io_desc("sd_wire", &psd, &ps_wire, 1)) {
1909		ret = ADS_ERROR(LDAP_NO_MEMORY);
1910		goto ads_set_sd_error;
1911	}
1912
1913#if 0
1914	file_save("/tmp/sec_desc.new", ps_wire.data_p, sd_size);
1915#endif
1916	if (!(mods = ads_init_mods(ctx))) return ADS_ERROR(LDAP_NO_MEMORY);
1917
1918	bval.bv_len = prs_offset(&ps_wire);
1919	bval.bv_val = TALLOC(ctx, bval.bv_len);
1920	if (!bval.bv_val) {
1921		ret = ADS_ERROR(LDAP_NO_MEMORY);
1922		goto ads_set_sd_error;
1923	}
1924
1925	prs_set_offset(&ps_wire, 0);
1926
1927	if (!prs_copy_data_out(bval.bv_val, &ps_wire, bval.bv_len)) {
1928		ret = ADS_ERROR(LDAP_NO_MEMORY);
1929		goto ads_set_sd_error;
1930	}
1931
1932	ret = ads_mod_ber(ctx, &mods, attrs[0], &bval);
1933	if (ADS_ERR_OK(ret)) {
1934		ret = ads_gen_mod(ads, dn, mods);
1935	}
1936
1937ads_set_sd_error:
1938	ads_msgfree(ads, res);
1939	prs_mem_free(&ps_wire);
1940	talloc_destroy(ctx);
1941	return ret;
1942}
1943
1944/**
1945 * pull the first entry from a ADS result
1946 * @param ads connection to ads server
1947 * @param res Results of search
1948 * @return first entry from result
1949 **/
1950void *ads_first_entry(ADS_STRUCT *ads, void *res)
1951{
1952	return (void *)ldap_first_entry(ads->ld, (LDAPMessage *)res);
1953}
1954
1955/**
1956 * pull the next entry from a ADS result
1957 * @param ads connection to ads server
1958 * @param res Results of search
1959 * @return next entry from result
1960 **/
1961void *ads_next_entry(ADS_STRUCT *ads, void *res)
1962{
1963	return (void *)ldap_next_entry(ads->ld, (LDAPMessage *)res);
1964}
1965
1966/**
1967 * pull a single string from a ADS result
1968 * @param ads connection to ads server
1969 * @param mem_ctx TALLOC_CTX to use for allocating result string
1970 * @param msg Results of search
1971 * @param field Attribute to retrieve
1972 * @return Result string in talloc context
1973 **/
1974char *ads_pull_string(ADS_STRUCT *ads,
1975		      TALLOC_CTX *mem_ctx, void *msg, const char *field)
1976{
1977	char **values;
1978	char *ret = NULL;
1979	char *ux_string;
1980	size_t rc;
1981
1982	values = ldap_get_values(ads->ld, msg, field);
1983	if (!values)
1984		return NULL;
1985
1986	if (values[0]) {
1987		rc = pull_utf8_talloc(mem_ctx, &ux_string,
1988				      values[0]);
1989		if (rc != (size_t)-1)
1990			ret = ux_string;
1991
1992	}
1993	ldap_value_free(values);
1994	return ret;
1995}
1996
1997/**
1998 * pull an array of strings from a ADS result
1999 * @param ads connection to ads server
2000 * @param mem_ctx TALLOC_CTX to use for allocating result string
2001 * @param msg Results of search
2002 * @param field Attribute to retrieve
2003 * @return Result strings in talloc context
2004 **/
2005char **ads_pull_strings(ADS_STRUCT *ads,
2006			TALLOC_CTX *mem_ctx, void *msg, const char *field,
2007			size_t *num_values)
2008{
2009	char **values;
2010	char **ret = NULL;
2011	int i;
2012
2013	values = ldap_get_values(ads->ld, msg, field);
2014	if (!values)
2015		return NULL;
2016
2017	*num_values = ldap_count_values(values);
2018
2019	ret = TALLOC_ARRAY(mem_ctx, char *, *num_values + 1);
2020	if (!ret) {
2021		ldap_value_free(values);
2022		return NULL;
2023	}
2024
2025	for (i=0;i<*num_values;i++) {
2026		if (pull_utf8_talloc(mem_ctx, &ret[i], values[i]) == -1) {
2027			ldap_value_free(values);
2028			return NULL;
2029		}
2030	}
2031	ret[i] = NULL;
2032
2033	ldap_value_free(values);
2034	return ret;
2035}
2036
2037/**
2038 * pull an array of strings from a ADS result
2039 *  (handle large multivalue attributes with range retrieval)
2040 * @param ads connection to ads server
2041 * @param mem_ctx TALLOC_CTX to use for allocating result string
2042 * @param msg Results of search
2043 * @param field Attribute to retrieve
2044 * @param current_strings strings returned by a previous call to this function
2045 * @param next_attribute The next query should ask for this attribute
2046 * @param num_values How many values did we get this time?
2047 * @param more_values Are there more values to get?
2048 * @return Result strings in talloc context
2049 **/
2050char **ads_pull_strings_range(ADS_STRUCT *ads,
2051			      TALLOC_CTX *mem_ctx,
2052			      void *msg, const char *field,
2053			      char **current_strings,
2054			      const char **next_attribute,
2055			      size_t *num_strings,
2056			      BOOL *more_strings)
2057{
2058	char *attr;
2059	char *expected_range_attrib, *range_attr;
2060	BerElement *ptr = NULL;
2061	char **strings;
2062	char **new_strings;
2063	size_t num_new_strings;
2064	unsigned long int range_start;
2065	unsigned long int range_end;
2066
2067	/* we might have been given the whole lot anyway */
2068	if ((strings = ads_pull_strings(ads, mem_ctx, msg, field, num_strings))) {
2069		*more_strings = False;
2070		return strings;
2071	}
2072
2073	expected_range_attrib = talloc_asprintf(mem_ctx, "%s;Range=", field);
2074
2075	/* look for Range result */
2076	for (attr = ldap_first_attribute(ads->ld, (LDAPMessage *)msg, &ptr);
2077	     attr;
2078	     attr = ldap_next_attribute(ads->ld, (LDAPMessage *)msg, ptr)) {
2079		/* we ignore the fact that this is utf8, as all attributes are ascii... */
2080		if (strnequal(attr, expected_range_attrib, strlen(expected_range_attrib))) {
2081			range_attr = attr;
2082			break;
2083		}
2084		ldap_memfree(attr);
2085	}
2086	if (!attr) {
2087		ber_free(ptr, 0);
2088		/* nothing here - this field is just empty */
2089		*more_strings = False;
2090		return NULL;
2091	}
2092
2093	if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-%lu",
2094		   &range_start, &range_end) == 2) {
2095		*more_strings = True;
2096	} else {
2097		if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-*",
2098			   &range_start) == 1) {
2099			*more_strings = False;
2100		} else {
2101			DEBUG(1, ("ads_pull_strings_range:  Cannot parse Range attriubte (%s)\n",
2102				  range_attr));
2103			ldap_memfree(range_attr);
2104			*more_strings = False;
2105			return NULL;
2106		}
2107	}
2108
2109	if ((*num_strings) != range_start) {
2110		DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) doesn't start at %u, but at %lu"
2111			  " - aborting range retreival\n",
2112			  range_attr, *num_strings + 1, range_start));
2113		ldap_memfree(range_attr);
2114		*more_strings = False;
2115		return NULL;
2116	}
2117
2118	new_strings = ads_pull_strings(ads, mem_ctx, msg, range_attr, &num_new_strings);
2119
2120	if (*more_strings && ((*num_strings + num_new_strings) != (range_end + 1))) {
2121		DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) tells us we have %lu "
2122			  "strings in this bunch, but we only got %lu - aborting range retreival\n",
2123			  range_attr, (unsigned long int)range_end - range_start + 1,
2124			  (unsigned long int)num_new_strings));
2125		ldap_memfree(range_attr);
2126		*more_strings = False;
2127		return NULL;
2128	}
2129
2130	strings = TALLOC_REALLOC_ARRAY(mem_ctx, current_strings, char *,
2131				 *num_strings + num_new_strings);
2132
2133	if (strings == NULL) {
2134		ldap_memfree(range_attr);
2135		*more_strings = False;
2136		return NULL;
2137	}
2138
2139	memcpy(&strings[*num_strings], new_strings,
2140	       sizeof(*new_strings) * num_new_strings);
2141
2142	(*num_strings) += num_new_strings;
2143
2144	if (*more_strings) {
2145		*next_attribute = talloc_asprintf(mem_ctx,
2146						  "%s;range=%d-*",
2147						  field,
2148						  *num_strings);
2149
2150		if (!*next_attribute) {
2151			DEBUG(1, ("talloc_asprintf for next attribute failed!\n"));
2152			ldap_memfree(range_attr);
2153			*more_strings = False;
2154			return NULL;
2155		}
2156	}
2157
2158	ldap_memfree(range_attr);
2159
2160	return strings;
2161}
2162
2163/**
2164 * pull a single uint32 from a ADS result
2165 * @param ads connection to ads server
2166 * @param msg Results of search
2167 * @param field Attribute to retrieve
2168 * @param v Pointer to int to store result
2169 * @return boolean inidicating success
2170*/
2171BOOL ads_pull_uint32(ADS_STRUCT *ads,
2172		     void *msg, const char *field, uint32 *v)
2173{
2174	char **values;
2175
2176	values = ldap_get_values(ads->ld, msg, field);
2177	if (!values)
2178		return False;
2179	if (!values[0]) {
2180		ldap_value_free(values);
2181		return False;
2182	}
2183
2184	*v = atoi(values[0]);
2185	ldap_value_free(values);
2186	return True;
2187}
2188
2189/**
2190 * pull a single objectGUID from an ADS result
2191 * @param ads connection to ADS server
2192 * @param msg results of search
2193 * @param guid 37-byte area to receive text guid
2194 * @return boolean indicating success
2195 **/
2196BOOL ads_pull_guid(ADS_STRUCT *ads,
2197		   void *msg, struct uuid *guid)
2198{
2199	char **values;
2200	UUID_FLAT flat_guid;
2201
2202	values = ldap_get_values(ads->ld, msg, "objectGUID");
2203	if (!values)
2204		return False;
2205
2206	if (values[0]) {
2207		memcpy(&flat_guid.info, values[0], sizeof(UUID_FLAT));
2208		smb_uuid_unpack(flat_guid, guid);
2209		ldap_value_free(values);
2210		return True;
2211	}
2212	ldap_value_free(values);
2213	return False;
2214
2215}
2216
2217
2218/**
2219 * pull a single DOM_SID from a ADS result
2220 * @param ads connection to ads server
2221 * @param msg Results of search
2222 * @param field Attribute to retrieve
2223 * @param sid Pointer to sid to store result
2224 * @return boolean inidicating success
2225*/
2226BOOL ads_pull_sid(ADS_STRUCT *ads,
2227		  void *msg, const char *field, DOM_SID *sid)
2228{
2229	struct berval **values;
2230	BOOL ret = False;
2231
2232	values = ldap_get_values_len(ads->ld, msg, field);
2233
2234	if (!values)
2235		return False;
2236
2237	if (values[0])
2238		ret = sid_parse(values[0]->bv_val, values[0]->bv_len, sid);
2239
2240	ldap_value_free_len(values);
2241	return ret;
2242}
2243
2244/**
2245 * pull an array of DOM_SIDs from a ADS result
2246 * @param ads connection to ads server
2247 * @param mem_ctx TALLOC_CTX for allocating sid array
2248 * @param msg Results of search
2249 * @param field Attribute to retrieve
2250 * @param sids pointer to sid array to allocate
2251 * @return the count of SIDs pulled
2252 **/
2253int ads_pull_sids(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2254		  void *msg, const char *field, DOM_SID **sids)
2255{
2256	struct berval **values;
2257	BOOL ret;
2258	int count, i;
2259
2260	values = ldap_get_values_len(ads->ld, msg, field);
2261
2262	if (!values)
2263		return 0;
2264
2265	for (i=0; values[i]; i++)
2266		/* nop */ ;
2267
2268	(*sids) = TALLOC_ARRAY(mem_ctx, DOM_SID, i);
2269	if (!(*sids)) {
2270		ldap_value_free_len(values);
2271		return 0;
2272	}
2273
2274	count = 0;
2275	for (i=0; values[i]; i++) {
2276		ret = sid_parse(values[i]->bv_val, values[i]->bv_len, &(*sids)[count]);
2277		if (ret) {
2278			fstring sid;
2279			DEBUG(10, ("pulling SID: %s\n", sid_to_string(sid, &(*sids)[count])));
2280			count++;
2281		}
2282	}
2283
2284	ldap_value_free_len(values);
2285	return count;
2286}
2287
2288/**
2289 * pull a SEC_DESC from a ADS result
2290 * @param ads connection to ads server
2291 * @param mem_ctx TALLOC_CTX for allocating sid array
2292 * @param msg Results of search
2293 * @param field Attribute to retrieve
2294 * @param sd Pointer to *SEC_DESC to store result (talloc()ed)
2295 * @return boolean inidicating success
2296*/
2297BOOL ads_pull_sd(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2298		  void *msg, const char *field, SEC_DESC **sd)
2299{
2300	struct berval **values;
2301	prs_struct      ps;
2302	BOOL ret = False;
2303
2304	values = ldap_get_values_len(ads->ld, msg, field);
2305
2306	if (!values) return False;
2307
2308	if (values[0]) {
2309		prs_init(&ps, values[0]->bv_len, mem_ctx, UNMARSHALL);
2310		prs_copy_data_in(&ps, values[0]->bv_val, values[0]->bv_len);
2311		prs_set_offset(&ps,0);
2312
2313		ret = sec_io_desc("sd", sd, &ps, 1);
2314	}
2315
2316	ldap_value_free_len(values);
2317	return ret;
2318}
2319
2320/*
2321 * in order to support usernames longer than 21 characters we need to
2322 * use both the sAMAccountName and the userPrincipalName attributes
2323 * It seems that not all users have the userPrincipalName attribute set
2324 *
2325 * @param ads connection to ads server
2326 * @param mem_ctx TALLOC_CTX for allocating sid array
2327 * @param msg Results of search
2328 * @return the username
2329 */
2330char *ads_pull_username(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, void *msg)
2331{
2332#if 0	/* JERRY */
2333	char *ret, *p;
2334
2335	/* lookup_name() only works on the sAMAccountName to
2336	   returning the username portion of userPrincipalName
2337	   breaks winbindd_getpwnam() */
2338
2339	ret = ads_pull_string(ads, mem_ctx, msg, "userPrincipalName");
2340	if (ret && (p = strchr_m(ret, '@'))) {
2341		*p = 0;
2342		return ret;
2343	}
2344#endif
2345	return ads_pull_string(ads, mem_ctx, msg, "sAMAccountName");
2346}
2347
2348
2349/**
2350 * find the update serial number - this is the core of the ldap cache
2351 * @param ads connection to ads server
2352 * @param ads connection to ADS server
2353 * @param usn Pointer to retrieved update serial number
2354 * @return status of search
2355 **/
2356ADS_STATUS ads_USN(ADS_STRUCT *ads, uint32 *usn)
2357{
2358	const char *attrs[] = {"highestCommittedUSN", NULL};
2359	ADS_STATUS status;
2360	void *res;
2361
2362	status = ads_do_search_retry(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2363	if (!ADS_ERR_OK(status))
2364		return status;
2365
2366	if (ads_count_replies(ads, res) != 1) {
2367		return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2368	}
2369
2370	ads_pull_uint32(ads, res, "highestCommittedUSN", usn);
2371	ads_msgfree(ads, res);
2372	return ADS_SUCCESS;
2373}
2374
2375/* parse a ADS timestring - typical string is
2376   '20020917091222.0Z0' which means 09:12.22 17th September
2377   2002, timezone 0 */
2378static time_t ads_parse_time(const char *str)
2379{
2380	struct tm tm;
2381
2382	ZERO_STRUCT(tm);
2383
2384	if (sscanf(str, "%4d%2d%2d%2d%2d%2d",
2385		   &tm.tm_year, &tm.tm_mon, &tm.tm_mday,
2386		   &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) {
2387		return 0;
2388	}
2389	tm.tm_year -= 1900;
2390	tm.tm_mon -= 1;
2391
2392	return timegm(&tm);
2393}
2394
2395
2396/**
2397 * Find the servers name and realm - this can be done before authentication
2398 *  The ldapServiceName field on w2k  looks like this:
2399 *    vnet3.home.samba.org:win2000-vnet3$@VNET3.HOME.SAMBA.ORG
2400 * @param ads connection to ads server
2401 * @return status of search
2402 **/
2403ADS_STATUS ads_server_info(ADS_STRUCT *ads)
2404{
2405	const char *attrs[] = {"ldapServiceName", "currentTime", NULL};
2406	ADS_STATUS status;
2407	void *res;
2408	char *value;
2409	char *p;
2410	char *timestr;
2411	TALLOC_CTX *ctx;
2412
2413	if (!(ctx = talloc_init("ads_server_info"))) {
2414		return ADS_ERROR(LDAP_NO_MEMORY);
2415	}
2416
2417	status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2418	if (!ADS_ERR_OK(status)) {
2419		talloc_destroy(ctx);
2420		return status;
2421	}
2422
2423	value = ads_pull_string(ads, ctx, res, "ldapServiceName");
2424	if (!value) {
2425		ads_msgfree(ads, res);
2426		talloc_destroy(ctx);
2427		return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2428	}
2429
2430	timestr = ads_pull_string(ads, ctx, res, "currentTime");
2431	if (!timestr) {
2432		ads_msgfree(ads, res);
2433		talloc_destroy(ctx);
2434		return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2435	}
2436
2437	ads_msgfree(ads, res);
2438
2439	p = strchr(value, ':');
2440	if (!p) {
2441		talloc_destroy(ctx);
2442		DEBUG(1, ("ads_server_info: returned ldap server name did not contain a ':' "
2443			  "so was deemed invalid\n"));
2444		return ADS_ERROR(LDAP_DECODING_ERROR);
2445	}
2446
2447	SAFE_FREE(ads->config.ldap_server_name);
2448
2449	ads->config.ldap_server_name = SMB_STRDUP(p+1);
2450	p = strchr(ads->config.ldap_server_name, '$');
2451	if (!p || p[1] != '@') {
2452		talloc_destroy(ctx);
2453		DEBUG(1, ("ads_server_info: returned ldap server name (%s) does not contain '$@'"
2454			  " so was deemed invalid\n", ads->config.ldap_server_name));
2455		SAFE_FREE(ads->config.ldap_server_name);
2456		return ADS_ERROR(LDAP_DECODING_ERROR);
2457	}
2458
2459	*p = 0;
2460
2461	SAFE_FREE(ads->config.realm);
2462	SAFE_FREE(ads->config.bind_path);
2463
2464	ads->config.realm = SMB_STRDUP(p+2);
2465	ads->config.bind_path = ads_build_dn(ads->config.realm);
2466
2467	DEBUG(3,("got ldap server name %s@%s, using bind path: %s\n",
2468		 ads->config.ldap_server_name, ads->config.realm,
2469		 ads->config.bind_path));
2470
2471	ads->config.current_time = ads_parse_time(timestr);
2472
2473	if (ads->config.current_time != 0) {
2474		ads->auth.time_offset = ads->config.current_time - time(NULL);
2475		DEBUG(4,("time offset is %d seconds\n", ads->auth.time_offset));
2476	}
2477
2478	talloc_destroy(ctx);
2479
2480	return ADS_SUCCESS;
2481}
2482
2483/**
2484 * find the domain sid for our domain
2485 * @param ads connection to ads server
2486 * @param sid Pointer to domain sid
2487 * @return status of search
2488 **/
2489ADS_STATUS ads_domain_sid(ADS_STRUCT *ads, DOM_SID *sid)
2490{
2491	const char *attrs[] = {"objectSid", NULL};
2492	void *res;
2493	ADS_STATUS rc;
2494
2495	rc = ads_do_search_retry(ads, ads->config.bind_path, LDAP_SCOPE_BASE, "(objectclass=*)",
2496			   attrs, &res);
2497	if (!ADS_ERR_OK(rc)) return rc;
2498	if (!ads_pull_sid(ads, res, "objectSid", sid)) {
2499		ads_msgfree(ads, res);
2500		return ADS_ERROR_SYSTEM(ENOENT);
2501	}
2502	ads_msgfree(ads, res);
2503
2504	return ADS_SUCCESS;
2505}
2506
2507/* this is rather complex - we need to find the allternate (netbios) name
2508   for the domain, but there isn't a simple query to do this. Instead
2509   we look for the principle names on the DCs account and find one that has
2510   the right form, then extract the netbios name of the domain from that
2511
2512   NOTE! better method is this:
2513
2514bin/net -Uadministrator%XXXXX ads search '(&(objectclass=crossref)(dnsroot=VNET3.HOME.SAMBA.ORG))'  nETBIOSName
2515
2516but you need to force the bind path to match the configurationNamingContext from the rootDSE
2517
2518*/
2519ADS_STATUS ads_workgroup_name(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char **workgroup)
2520{
2521	char *expr;
2522	ADS_STATUS rc;
2523	char **principles;
2524	char *prefix;
2525	int prefix_length;
2526	int i;
2527	void *res;
2528	const char *attrs[] = {"servicePrincipalName", NULL};
2529	size_t num_principals;
2530
2531	(*workgroup) = NULL;
2532
2533	asprintf(&expr, "(&(objectclass=computer)(dnshostname=%s.%s))",
2534		 ads->config.ldap_server_name, ads->config.realm);
2535	if (expr == NULL) {
2536		ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
2537	}
2538
2539	rc = ads_search(ads, &res, expr, attrs);
2540	free(expr);
2541
2542	if (!ADS_ERR_OK(rc)) {
2543		return rc;
2544	}
2545
2546	principles = ads_pull_strings(ads, mem_ctx, res,
2547				      "servicePrincipalName", &num_principals);
2548
2549	ads_msgfree(ads, res);
2550
2551	if (!principles) {
2552		return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2553	}
2554
2555	asprintf(&prefix, "HOST/%s.%s/",
2556		 ads->config.ldap_server_name,
2557		 ads->config.realm);
2558
2559	prefix_length = strlen(prefix);
2560
2561	for (i=0;principles[i]; i++) {
2562		if (strnequal(principles[i], prefix, prefix_length) &&
2563		    !strequal(ads->config.realm, principles[i]+prefix_length) &&
2564		    !strchr(principles[i]+prefix_length, '.')) {
2565			/* found an alternate (short) name for the domain. */
2566			DEBUG(3,("Found alternate name '%s' for realm '%s'\n",
2567				 principles[i]+prefix_length,
2568				 ads->config.realm));
2569			(*workgroup) = talloc_strdup(mem_ctx, principles[i]+prefix_length);
2570			break;
2571		}
2572	}
2573	free(prefix);
2574
2575	if (!*workgroup) {
2576		return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2577	}
2578
2579	return ADS_SUCCESS;
2580}
2581
2582#endif
2583