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    Copyright (C) Guenther Deschner 2005
8    Copyright (C) Gerald Carter 2006
9 
10    This program is free software; you can redistribute it and/or modify
11    it under the terms of the GNU General Public License as published by
12    the Free Software Foundation; either version 3 of the License, or
13    (at your option) any later version.
14 
15    This program is distributed in the hope that it will be useful,
16    but WITHOUT ANY WARRANTY; without even the implied warranty of
17    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18    GNU General Public License for more details.
19 
20    You should have received a copy of the GNU General Public License
21    along with this program.  If not, see <http://www.gnu.org/licenses/>.
22 */
23 
24 #include "includes.h"
25 #include "ads.h"
26 #include "libads/sitename_cache.h"
27 #include "libads/cldap.h"
28 #include "../lib/addns/dnsquery.h"
29 #include "../libds/common/flags.h"
30 #include "smbldap.h"
31 #include "../libcli/security/security.h"
32 #include "../librpc/gen_ndr/netlogon.h"
33 #include "lib/param/loadparm.h"
34 #include "libsmb/namequery.h"
35 
36 #ifdef HAVE_LDAP
37 
38 /**
39  * @file ldap.c
40  * @brief basic ldap client-side routines for ads server communications
41  *
42  * The routines contained here should do the necessary ldap calls for
43  * ads setups.
44  *
45  * Important note: attribute names passed into ads_ routines must
46  * already be in UTF-8 format.  We do not convert them because in almost
47  * all cases, they are just ascii (which is represented with the same
48  * codepoints in UTF-8).  This may have to change at some point
49  **/
50 
51 
52 #define LDAP_SERVER_TREE_DELETE_OID	"1.2.840.113556.1.4.805"
53 
54 static SIG_ATOMIC_T gotalarm;
55 
56 /***************************************************************
57  Signal function to tell us we timed out.
58 ****************************************************************/
59 
gotalarm_sig(int signum)60 static void gotalarm_sig(int signum)
61 {
62 	gotalarm = 1;
63 }
64 
ldap_open_with_timeout(const char * server,struct sockaddr_storage * ss,int port,unsigned int to)65  LDAP *ldap_open_with_timeout(const char *server,
66 			      struct sockaddr_storage *ss,
67 			      int port, unsigned int to)
68 {
69 	LDAP *ldp = NULL;
70 	int ldap_err;
71 	char *uri;
72 
73 	DEBUG(10, ("Opening connection to LDAP server '%s:%d', timeout "
74 		   "%u seconds\n", server, port, to));
75 
76 	if (to) {
77 		/* Setup timeout */
78 		gotalarm = 0;
79 		CatchSignal(SIGALRM, gotalarm_sig);
80 		alarm(to);
81 		/* End setup timeout. */
82 	}
83 
84 	if ( strchr_m(server, ':') ) {
85 		/* IPv6 URI */
86 		uri = talloc_asprintf(talloc_tos(), "ldap://[%s]:%u", server, port);
87 	} else {
88 		/* IPv4 URI */
89 		uri = talloc_asprintf(talloc_tos(), "ldap://%s:%u", server, port);
90 	}
91 	if (uri == NULL) {
92 		return NULL;
93 	}
94 
95 #ifdef HAVE_LDAP_INITIALIZE
96 	ldap_err = ldap_initialize(&ldp, uri);
97 #else
98 	ldp = ldap_open(server, port);
99 	if (ldp != NULL) {
100 		ldap_err = LDAP_SUCCESS;
101 	} else {
102 		ldap_err = LDAP_OTHER;
103 	}
104 #endif
105 	if (ldap_err != LDAP_SUCCESS) {
106 		DEBUG(2,("Could not initialize connection for LDAP server '%s': %s\n",
107 			 uri, ldap_err2string(ldap_err)));
108 	} else {
109 		DEBUG(10, ("Initialized connection for LDAP server '%s'\n", uri));
110 	}
111 
112 	if (to) {
113 		/* Teardown timeout. */
114 		alarm(0);
115 		CatchSignal(SIGALRM, SIG_IGN);
116 	}
117 
118 	return ldp;
119 }
120 
ldap_search_with_timeout(LDAP * ld,LDAP_CONST char * base,int scope,LDAP_CONST char * filter,char ** attrs,int attrsonly,LDAPControl ** sctrls,LDAPControl ** cctrls,int sizelimit,LDAPMessage ** res)121 static int ldap_search_with_timeout(LDAP *ld,
122 				    LDAP_CONST char *base,
123 				    int scope,
124 				    LDAP_CONST char *filter,
125 				    char **attrs,
126 				    int attrsonly,
127 				    LDAPControl **sctrls,
128 				    LDAPControl **cctrls,
129 				    int sizelimit,
130 				    LDAPMessage **res )
131 {
132 	int to = lp_ldap_timeout();
133 	struct timeval timeout;
134 	struct timeval *timeout_ptr = NULL;
135 	int result;
136 
137 	/* Setup timeout for the ldap_search_ext_s call - local and remote. */
138 	gotalarm = 0;
139 
140 	if (to) {
141 		timeout.tv_sec = to;
142 	 	timeout.tv_usec = 0;
143 		timeout_ptr = &timeout;
144 
145 		/* Setup alarm timeout. */
146 		CatchSignal(SIGALRM, gotalarm_sig);
147 		/* Make the alarm time one second beyond
148 		   the timout we're setting for the
149 		   remote search timeout, to allow that
150 		   to fire in preference. */
151 		alarm(to+1);
152 		/* End setup timeout. */
153 	}
154 
155 
156 	result = ldap_search_ext_s(ld, base, scope, filter, attrs,
157 				   attrsonly, sctrls, cctrls, timeout_ptr,
158 				   sizelimit, res);
159 
160 	if (to) {
161 		/* Teardown alarm timeout. */
162 		CatchSignal(SIGALRM, SIG_IGN);
163 		alarm(0);
164 	}
165 
166 	if (gotalarm != 0)
167 		return LDAP_TIMELIMIT_EXCEEDED;
168 
169 	/*
170 	 * A bug in OpenLDAP means ldap_search_ext_s can return
171 	 * LDAP_SUCCESS but with a NULL res pointer. Cope with
172 	 * this. See bug #6279 for details. JRA.
173 	 */
174 
175 	if (*res == NULL) {
176 		return LDAP_TIMELIMIT_EXCEEDED;
177 	}
178 
179 	return result;
180 }
181 
182 /**********************************************
183  Do client and server sitename match ?
184 **********************************************/
185 
ads_sitename_match(ADS_STRUCT * ads)186 bool ads_sitename_match(ADS_STRUCT *ads)
187 {
188 	if (ads->config.server_site_name == NULL &&
189 	    ads->config.client_site_name == NULL ) {
190 		DEBUG(10,("ads_sitename_match: both null\n"));
191 		return True;
192 	}
193 	if (ads->config.server_site_name &&
194 	    ads->config.client_site_name &&
195 	    strequal(ads->config.server_site_name,
196 		     ads->config.client_site_name)) {
197 		DEBUG(10,("ads_sitename_match: name %s match\n", ads->config.server_site_name));
198 		return True;
199 	}
200 	DEBUG(10,("ads_sitename_match: no match between server: %s and client: %s\n",
201 		ads->config.server_site_name ? ads->config.server_site_name : "NULL",
202 		ads->config.client_site_name ? ads->config.client_site_name : "NULL"));
203 	return False;
204 }
205 
206 /**********************************************
207  Is this the closest DC ?
208 **********************************************/
209 
ads_closest_dc(ADS_STRUCT * ads)210 bool ads_closest_dc(ADS_STRUCT *ads)
211 {
212 	if (ads->config.flags & NBT_SERVER_CLOSEST) {
213 		DEBUG(10,("ads_closest_dc: NBT_SERVER_CLOSEST flag set\n"));
214 		return True;
215 	}
216 
217 	/* not sure if this can ever happen */
218 	if (ads_sitename_match(ads)) {
219 		DEBUG(10,("ads_closest_dc: NBT_SERVER_CLOSEST flag not set but sites match\n"));
220 		return True;
221 	}
222 
223 	if (ads->config.client_site_name == NULL) {
224 		DEBUG(10,("ads_closest_dc: client belongs to no site\n"));
225 		return True;
226 	}
227 
228 	DEBUG(10,("ads_closest_dc: %s is not the closest DC\n",
229 		ads->config.ldap_server_name));
230 
231 	return False;
232 }
233 
234 
235 /*
236   try a connection to a given ldap server, returning True and setting the servers IP
237   in the ads struct if successful
238  */
ads_try_connect(ADS_STRUCT * ads,bool gc,struct sockaddr_storage * ss)239 static bool ads_try_connect(ADS_STRUCT *ads, bool gc,
240 			    struct sockaddr_storage *ss)
241 {
242 	struct NETLOGON_SAM_LOGON_RESPONSE_EX cldap_reply;
243 	TALLOC_CTX *frame = talloc_stackframe();
244 	bool ret = false;
245 	char addr[INET6_ADDRSTRLEN];
246 
247 	if (ss == NULL) {
248 		TALLOC_FREE(frame);
249 		return False;
250 	}
251 
252 	print_sockaddr(addr, sizeof(addr), ss);
253 
254 	DEBUG(5,("ads_try_connect: sending CLDAP request to %s (realm: %s)\n",
255 		addr, ads->server.realm));
256 
257 	ZERO_STRUCT( cldap_reply );
258 
259 	if ( !ads_cldap_netlogon_5(frame, ss, ads->server.realm, &cldap_reply ) ) {
260 		DEBUG(3,("ads_try_connect: CLDAP request %s failed.\n", addr));
261 		ret = false;
262 		goto out;
263 	}
264 
265 	/* Check the CLDAP reply flags */
266 
267 	if ( !(cldap_reply.server_type & NBT_SERVER_LDAP) ) {
268 		DEBUG(1,("ads_try_connect: %s's CLDAP reply says it is not an LDAP server!\n",
269 			addr));
270 		ret = false;
271 		goto out;
272 	}
273 
274 	/* Fill in the ads->config values */
275 
276 	SAFE_FREE(ads->config.realm);
277 	SAFE_FREE(ads->config.bind_path);
278 	SAFE_FREE(ads->config.ldap_server_name);
279 	SAFE_FREE(ads->config.server_site_name);
280 	SAFE_FREE(ads->config.client_site_name);
281 	SAFE_FREE(ads->server.workgroup);
282 
283 	if (!check_cldap_reply_required_flags(cldap_reply.server_type,
284 					      ads->config.flags)) {
285 		ret = false;
286 		goto out;
287 	}
288 
289 	ads->config.ldap_server_name   = SMB_STRDUP(cldap_reply.pdc_dns_name);
290 	ads->config.realm              = SMB_STRDUP(cldap_reply.dns_domain);
291 	if (!strupper_m(ads->config.realm)) {
292 		ret = false;
293 		goto out;
294 	}
295 
296 	ads->config.bind_path          = ads_build_dn(ads->config.realm);
297 	if (*cldap_reply.server_site) {
298 		ads->config.server_site_name =
299 			SMB_STRDUP(cldap_reply.server_site);
300 	}
301 	if (*cldap_reply.client_site) {
302 		ads->config.client_site_name =
303 			SMB_STRDUP(cldap_reply.client_site);
304 	}
305 	ads->server.workgroup          = SMB_STRDUP(cldap_reply.domain_name);
306 
307 	ads->ldap.port = gc ? LDAP_GC_PORT : LDAP_PORT;
308 	ads->ldap.ss = *ss;
309 
310 	/* Store our site name. */
311 	sitename_store( cldap_reply.domain_name, cldap_reply.client_site);
312 	sitename_store( cldap_reply.dns_domain, cldap_reply.client_site);
313 
314 	/* Leave this until last so that the flags are not clobbered */
315 	ads->config.flags	       = cldap_reply.server_type;
316 
317 	ret = true;
318 
319  out:
320 
321 	TALLOC_FREE(frame);
322 	return ret;
323 }
324 
325 /**********************************************************************
326  send a cldap ping to list of servers, one at a time, until one of
327  them answers it's an ldap server. Record success in the ADS_STRUCT.
328  Take note of and update negative connection cache.
329 **********************************************************************/
330 
cldap_ping_list(ADS_STRUCT * ads,const char * domain,struct ip_service * ip_list,int count)331 static NTSTATUS cldap_ping_list(ADS_STRUCT *ads,const char *domain,
332 				struct ip_service *ip_list, int count)
333 {
334 	int i;
335 	bool ok;
336 
337 	for (i = 0; i < count; i++) {
338 		char server[INET6_ADDRSTRLEN];
339 
340 		print_sockaddr(server, sizeof(server), &ip_list[i].ss);
341 
342 		if (!NT_STATUS_IS_OK(
343 			check_negative_conn_cache(domain, server)))
344 			continue;
345 
346 		/* Returns ok only if it matches the correct server type */
347 		ok = ads_try_connect(ads, false, &ip_list[i].ss);
348 
349 		if (ok) {
350 			return NT_STATUS_OK;
351 		}
352 
353 		/* keep track of failures */
354 		add_failed_connection_entry(domain, server,
355 					    NT_STATUS_UNSUCCESSFUL);
356 	}
357 
358 	return NT_STATUS_NO_LOGON_SERVERS;
359 }
360 
361 /***************************************************************************
362  resolve a name and perform an "ldap ping" using NetBIOS and related methods
363 ****************************************************************************/
364 
resolve_and_ping_netbios(ADS_STRUCT * ads,const char * domain,const char * realm)365 static NTSTATUS resolve_and_ping_netbios(ADS_STRUCT *ads,
366 					 const char *domain, const char *realm)
367 {
368 	int count, i;
369 	struct ip_service *ip_list;
370 	NTSTATUS status;
371 
372 	DEBUG(6, ("resolve_and_ping_netbios: (cldap) looking for domain '%s'\n",
373 		  domain));
374 
375 	status = get_sorted_dc_list(domain, NULL, &ip_list, &count,
376 				    false);
377 	if (!NT_STATUS_IS_OK(status)) {
378 		return status;
379 	}
380 
381 	/* remove servers which are known to be dead based on
382 	   the corresponding DNS method */
383 	if (*realm) {
384 		for (i = 0; i < count; ++i) {
385 			char server[INET6_ADDRSTRLEN];
386 
387 			print_sockaddr(server, sizeof(server), &ip_list[i].ss);
388 
389 			if(!NT_STATUS_IS_OK(
390 				check_negative_conn_cache(realm, server))) {
391 				/* Ensure we add the workgroup name for this
392 				   IP address as negative too. */
393 				add_failed_connection_entry(
394 				    domain, server,
395 				    NT_STATUS_UNSUCCESSFUL);
396 			}
397 		}
398 	}
399 
400 	status = cldap_ping_list(ads, domain, ip_list, count);
401 
402 	SAFE_FREE(ip_list);
403 
404 	return status;
405 }
406 
407 
408 /**********************************************************************
409  resolve a name and perform an "ldap ping" using DNS
410 **********************************************************************/
411 
resolve_and_ping_dns(ADS_STRUCT * ads,const char * sitename,const char * realm)412 static NTSTATUS resolve_and_ping_dns(ADS_STRUCT *ads, const char *sitename,
413 				     const char *realm)
414 {
415 	int count;
416 	struct ip_service *ip_list = NULL;
417 	NTSTATUS status;
418 
419 	DEBUG(6, ("resolve_and_ping_dns: (cldap) looking for realm '%s'\n",
420 		  realm));
421 
422 	status = get_sorted_dc_list(realm, sitename, &ip_list, &count,
423 				    true);
424 	if (!NT_STATUS_IS_OK(status)) {
425 		SAFE_FREE(ip_list);
426 		return status;
427 	}
428 
429 	status = cldap_ping_list(ads, realm, ip_list, count);
430 
431 	SAFE_FREE(ip_list);
432 
433 	return status;
434 }
435 
436 /**********************************************************************
437  Try to find an AD dc using our internal name resolution routines
438  Try the realm first and then then workgroup name if netbios is not
439  disabled
440 **********************************************************************/
441 
ads_find_dc(ADS_STRUCT * ads)442 static NTSTATUS ads_find_dc(ADS_STRUCT *ads)
443 {
444 	const char *c_domain = "";
445 	const char *c_realm;
446 	bool use_own_domain = False;
447 	char *sitename = NULL;
448 	NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
449 	bool ok = false;
450 
451 	/* if the realm and workgroup are both empty, assume they are ours */
452 
453 	/* realm */
454 	c_realm = ads->server.realm;
455 
456 	if (c_realm == NULL)
457 		c_realm = "";
458 
459 	if (!*c_realm) {
460 		/* special case where no realm and no workgroup means our own */
461 		if ( !ads->server.workgroup || !*ads->server.workgroup ) {
462 			use_own_domain = True;
463 			c_realm = lp_realm();
464 		}
465 	}
466 
467 	if (!lp_disable_netbios()) {
468 		if (use_own_domain) {
469 			c_domain = lp_workgroup();
470 		} else {
471 			c_domain = ads->server.workgroup;
472 			if (!*c_realm && (!c_domain || !*c_domain)) {
473 				c_domain = lp_workgroup();
474 			}
475 		}
476 
477 		if (!c_domain) {
478 			c_domain = "";
479 		}
480 	}
481 
482 	if (!*c_realm && !*c_domain) {
483 		DEBUG(0, ("ads_find_dc: no realm or workgroup!  Don't know "
484 			  "what to do\n"));
485 		return NT_STATUS_INVALID_PARAMETER; /* rather need MISSING_PARAMETER ... */
486 	}
487 
488 	/*
489 	 * In case of LDAP we use get_dc_name() as that
490 	 * creates the custom krb5.conf file
491 	 */
492 	if (!(ads->auth.flags & ADS_AUTH_NO_BIND)) {
493 		fstring srv_name;
494 		struct sockaddr_storage ip_out;
495 
496 		DEBUG(6, ("ads_find_dc: (ldap) looking for realm '%s'"
497 			  " and falling back to domain '%s'\n",
498 			  c_realm, c_domain));
499 
500 		ok = get_dc_name(c_domain, c_realm, srv_name, &ip_out);
501 		if (ok) {
502 			/*
503 			 * we call ads_try_connect() to fill in the
504 			 * ads->config details
505 			 */
506 			ok = ads_try_connect(ads, false, &ip_out);
507 			if (ok) {
508 				return NT_STATUS_OK;
509 			}
510 		}
511 
512 		return NT_STATUS_NO_LOGON_SERVERS;
513 	}
514 
515 	if (*c_realm) {
516 		sitename = sitename_fetch(talloc_tos(), c_realm);
517 		status = resolve_and_ping_dns(ads, sitename, c_realm);
518 
519 		if (NT_STATUS_IS_OK(status)) {
520 			TALLOC_FREE(sitename);
521 			return status;
522 		}
523 
524 		/* In case we failed to contact one of our closest DC on our
525 		 * site we
526 		 * need to try to find another DC, retry with a site-less SRV
527 		 * DNS query
528 		 * - Guenther */
529 
530 		if (sitename) {
531 			DEBUG(3, ("ads_find_dc: failed to find a valid DC on "
532 				  "our site (%s), Trying to find another DC "
533 				  "for realm '%s' (domain '%s')\n",
534 				  sitename, c_realm, c_domain));
535 			namecache_delete(c_realm, 0x1C);
536 			status =
537 			    resolve_and_ping_dns(ads, NULL, c_realm);
538 
539 			if (NT_STATUS_IS_OK(status)) {
540 				TALLOC_FREE(sitename);
541 				return status;
542 			}
543 		}
544 
545 		TALLOC_FREE(sitename);
546 	}
547 
548 	/* try netbios as fallback - if permitted,
549 	   or if configuration specifically requests it */
550 	if (*c_domain) {
551 		if (*c_realm) {
552 			DEBUG(3, ("ads_find_dc: falling back to netbios "
553 				  "name resolution for domain '%s' (realm '%s')\n",
554 				  c_domain, c_realm));
555 		}
556 
557 		status = resolve_and_ping_netbios(ads, c_domain, c_realm);
558 		if (NT_STATUS_IS_OK(status)) {
559 			return status;
560 		}
561 	}
562 
563 	DEBUG(1, ("ads_find_dc: "
564 		  "name resolution for realm '%s' (domain '%s') failed: %s\n",
565 		  c_realm, c_domain, nt_errstr(status)));
566 	return status;
567 }
568 /**
569  * Connect to the LDAP server
570  * @param ads Pointer to an existing ADS_STRUCT
571  * @return status of connection
572  **/
ads_connect(ADS_STRUCT * ads)573 ADS_STATUS ads_connect(ADS_STRUCT *ads)
574 {
575 	int version = LDAP_VERSION3;
576 	ADS_STATUS status;
577 	NTSTATUS ntstatus;
578 	char addr[INET6_ADDRSTRLEN];
579 
580 	ZERO_STRUCT(ads->ldap);
581 	ZERO_STRUCT(ads->ldap_wrap_data);
582 	ads->ldap.last_attempt	= time_mono(NULL);
583 	ads->ldap_wrap_data.wrap_type	= ADS_SASLWRAP_TYPE_PLAIN;
584 
585 	/* try with a user specified server */
586 
587 	if (DEBUGLEVEL >= 11) {
588 		char *s = NDR_PRINT_STRUCT_STRING(talloc_tos(), ads_struct, ads);
589 		DEBUG(11,("ads_connect: entering\n"));
590 		DEBUGADD(11,("%s\n", s));
591 		TALLOC_FREE(s);
592 	}
593 
594 	if (ads->server.ldap_server) {
595 		bool ok = false;
596 		struct sockaddr_storage ss;
597 
598 		ok = resolve_name(ads->server.ldap_server, &ss, 0x20, true);
599 		if (!ok) {
600 			DEBUG(5,("ads_connect: unable to resolve name %s\n",
601 				 ads->server.ldap_server));
602 			status = ADS_ERROR_NT(NT_STATUS_NOT_FOUND);
603 			goto out;
604 		}
605 		ok = ads_try_connect(ads, ads->server.gc, &ss);
606 		if (ok) {
607 			goto got_connection;
608 		}
609 
610 		/* The choice of which GC use is handled one level up in
611 		   ads_connect_gc().  If we continue on from here with
612 		   ads_find_dc() we will get GC searches on port 389 which
613 		   doesn't work.   --jerry */
614 
615 		if (ads->server.gc == true) {
616 			return ADS_ERROR(LDAP_OPERATIONS_ERROR);
617 		}
618 
619 		if (ads->server.no_fallback) {
620 			status = ADS_ERROR_NT(NT_STATUS_NOT_FOUND);
621 			goto out;
622 		}
623 	}
624 
625 	ntstatus = ads_find_dc(ads);
626 	if (NT_STATUS_IS_OK(ntstatus)) {
627 		goto got_connection;
628 	}
629 
630 	status = ADS_ERROR_NT(ntstatus);
631 	goto out;
632 
633 got_connection:
634 
635 	print_sockaddr(addr, sizeof(addr), &ads->ldap.ss);
636 	DEBUG(3,("Successfully contacted LDAP server %s\n", addr));
637 
638 	if (!ads->auth.user_name) {
639 		/* Must use the userPrincipalName value here or sAMAccountName
640 		   and not servicePrincipalName; found by Guenther Deschner */
641 
642 		if (asprintf(&ads->auth.user_name, "%s$", lp_netbios_name() ) == -1) {
643 			DEBUG(0,("ads_connect: asprintf fail.\n"));
644 			ads->auth.user_name = NULL;
645 		}
646 	}
647 
648 	if (!ads->auth.realm) {
649 		ads->auth.realm = SMB_STRDUP(ads->config.realm);
650 	}
651 
652 	if (!ads->auth.kdc_server) {
653 		print_sockaddr(addr, sizeof(addr), &ads->ldap.ss);
654 		ads->auth.kdc_server = SMB_STRDUP(addr);
655 	}
656 
657 	/* If the caller() requested no LDAP bind, then we are done */
658 
659 	if (ads->auth.flags & ADS_AUTH_NO_BIND) {
660 		status = ADS_SUCCESS;
661 		goto out;
662 	}
663 
664 	ads->ldap_wrap_data.mem_ctx = talloc_init("ads LDAP connection memory");
665 	if (!ads->ldap_wrap_data.mem_ctx) {
666 		status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
667 		goto out;
668 	}
669 
670 	/* Otherwise setup the TCP LDAP session */
671 
672 	ads->ldap.ld = ldap_open_with_timeout(addr,
673 					      &ads->ldap.ss,
674 					      ads->ldap.port, lp_ldap_timeout());
675 	if (ads->ldap.ld == NULL) {
676 		status = ADS_ERROR(LDAP_OPERATIONS_ERROR);
677 		goto out;
678 	}
679 	DEBUG(3,("Connected to LDAP server %s\n", ads->config.ldap_server_name));
680 
681 	/* cache the successful connection for workgroup and realm */
682 	if (ads_closest_dc(ads)) {
683 		saf_store( ads->server.workgroup, ads->config.ldap_server_name);
684 		saf_store( ads->server.realm, ads->config.ldap_server_name);
685 	}
686 
687 	ldap_set_option(ads->ldap.ld, LDAP_OPT_PROTOCOL_VERSION, &version);
688 
689 	if ( lp_ldap_ssl_ads() ) {
690 		status = ADS_ERROR(smbldap_start_tls(ads->ldap.ld, version));
691 		if (!ADS_ERR_OK(status)) {
692 			goto out;
693 		}
694 	}
695 
696 	/* fill in the current time and offsets */
697 
698 	status = ads_current_time( ads );
699 	if ( !ADS_ERR_OK(status) ) {
700 		goto out;
701 	}
702 
703 	/* Now do the bind */
704 
705 	if (ads->auth.flags & ADS_AUTH_ANON_BIND) {
706 		status = ADS_ERROR(ldap_simple_bind_s(ads->ldap.ld, NULL, NULL));
707 		goto out;
708 	}
709 
710 	if (ads->auth.flags & ADS_AUTH_SIMPLE_BIND) {
711 		status = ADS_ERROR(ldap_simple_bind_s(ads->ldap.ld, ads->auth.user_name, ads->auth.password));
712 		goto out;
713 	}
714 
715 	status = ads_sasl_bind(ads);
716 
717  out:
718 	if (DEBUGLEVEL >= 11) {
719 		char *s = NDR_PRINT_STRUCT_STRING(talloc_tos(), ads_struct, ads);
720 		DEBUG(11,("ads_connect: leaving with: %s\n",
721 			ads_errstr(status)));
722 		DEBUGADD(11,("%s\n", s));
723 		TALLOC_FREE(s);
724 	}
725 
726 	return status;
727 }
728 
729 /**
730  * Connect to the LDAP server using given credentials
731  * @param ads Pointer to an existing ADS_STRUCT
732  * @return status of connection
733  **/
ads_connect_user_creds(ADS_STRUCT * ads)734 ADS_STATUS ads_connect_user_creds(ADS_STRUCT *ads)
735 {
736 	ads->auth.flags |= ADS_AUTH_USER_CREDS;
737 
738 	return ads_connect(ads);
739 }
740 
741 /**
742  * Disconnect the LDAP server
743  * @param ads Pointer to an existing ADS_STRUCT
744  **/
ads_disconnect(ADS_STRUCT * ads)745 void ads_disconnect(ADS_STRUCT *ads)
746 {
747 	if (ads->ldap.ld) {
748 		ldap_unbind(ads->ldap.ld);
749 		ads->ldap.ld = NULL;
750 	}
751 	if (ads->ldap_wrap_data.wrap_ops &&
752 		ads->ldap_wrap_data.wrap_ops->disconnect) {
753 		ads->ldap_wrap_data.wrap_ops->disconnect(&ads->ldap_wrap_data);
754 	}
755 	if (ads->ldap_wrap_data.mem_ctx) {
756 		talloc_free(ads->ldap_wrap_data.mem_ctx);
757 	}
758 	ZERO_STRUCT(ads->ldap);
759 	ZERO_STRUCT(ads->ldap_wrap_data);
760 }
761 
762 /*
763   Duplicate a struct berval into talloc'ed memory
764  */
dup_berval(TALLOC_CTX * ctx,const struct berval * in_val)765 static struct berval *dup_berval(TALLOC_CTX *ctx, const struct berval *in_val)
766 {
767 	struct berval *value;
768 
769 	if (!in_val) return NULL;
770 
771 	value = talloc_zero(ctx, struct berval);
772 	if (value == NULL)
773 		return NULL;
774 	if (in_val->bv_len == 0) return value;
775 
776 	value->bv_len = in_val->bv_len;
777 	value->bv_val = (char *)talloc_memdup(ctx, in_val->bv_val,
778 					      in_val->bv_len);
779 	return value;
780 }
781 
782 /*
783   Make a values list out of an array of (struct berval *)
784  */
ads_dup_values(TALLOC_CTX * ctx,const struct berval ** in_vals)785 static struct berval **ads_dup_values(TALLOC_CTX *ctx,
786 				      const struct berval **in_vals)
787 {
788 	struct berval **values;
789 	int i;
790 
791 	if (!in_vals) return NULL;
792 	for (i=0; in_vals[i]; i++)
793 		; /* count values */
794 	values = talloc_zero_array(ctx, struct berval *, i+1);
795 	if (!values) return NULL;
796 
797 	for (i=0; in_vals[i]; i++) {
798 		values[i] = dup_berval(ctx, in_vals[i]);
799 	}
800 	return values;
801 }
802 
803 /*
804   UTF8-encode a values list out of an array of (char *)
805  */
ads_push_strvals(TALLOC_CTX * ctx,const char ** in_vals)806 static char **ads_push_strvals(TALLOC_CTX *ctx, const char **in_vals)
807 {
808 	char **values;
809 	int i;
810 	size_t size;
811 
812 	if (!in_vals) return NULL;
813 	for (i=0; in_vals[i]; i++)
814 		; /* count values */
815 	values = talloc_zero_array(ctx, char *, i+1);
816 	if (!values) return NULL;
817 
818 	for (i=0; in_vals[i]; i++) {
819 		if (!push_utf8_talloc(ctx, &values[i], in_vals[i], &size)) {
820 			TALLOC_FREE(values);
821 			return NULL;
822 		}
823 	}
824 	return values;
825 }
826 
827 /*
828   Pull a (char *) array out of a UTF8-encoded values list
829  */
ads_pull_strvals(TALLOC_CTX * ctx,const char ** in_vals)830 static char **ads_pull_strvals(TALLOC_CTX *ctx, const char **in_vals)
831 {
832 	char **values;
833 	int i;
834 	size_t converted_size;
835 
836 	if (!in_vals) return NULL;
837 	for (i=0; in_vals[i]; i++)
838 		; /* count values */
839 	values = talloc_zero_array(ctx, char *, i+1);
840 	if (!values) return NULL;
841 
842 	for (i=0; in_vals[i]; i++) {
843 		if (!pull_utf8_talloc(ctx, &values[i], in_vals[i],
844 				      &converted_size)) {
845 			DEBUG(0,("ads_pull_strvals: pull_utf8_talloc failed: "
846 				 "%s", strerror(errno)));
847 		}
848 	}
849 	return values;
850 }
851 
852 /**
853  * Do a search with paged results.  cookie must be null on the first
854  *  call, and then returned on each subsequent call.  It will be null
855  *  again when the entire search is complete
856  * @param ads connection to ads server
857  * @param bind_path Base dn for the search
858  * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
859  * @param expr Search expression - specified in local charset
860  * @param attrs Attributes to retrieve - specified in utf8 or ascii
861  * @param res ** which will contain results - free res* with ads_msgfree()
862  * @param count Number of entries retrieved on this page
863  * @param cookie The paged results cookie to be returned on subsequent calls
864  * @return status of search
865  **/
ads_do_paged_search_args(ADS_STRUCT * ads,const char * bind_path,int scope,const char * expr,const char ** attrs,void * args,LDAPMessage ** res,int * count,struct berval ** cookie)866 static ADS_STATUS ads_do_paged_search_args(ADS_STRUCT *ads,
867 					   const char *bind_path,
868 					   int scope, const char *expr,
869 					   const char **attrs, void *args,
870 					   LDAPMessage **res,
871 					   int *count, struct berval **cookie)
872 {
873 	int rc, i, version;
874 	char *utf8_expr, *utf8_path, **search_attrs = NULL;
875 	size_t converted_size;
876 	LDAPControl PagedResults, NoReferrals, ExternalCtrl, *controls[4], **rcontrols;
877 	BerElement *cookie_be = NULL;
878 	struct berval *cookie_bv= NULL;
879 	BerElement *ext_be = NULL;
880 	struct berval *ext_bv= NULL;
881 
882 	TALLOC_CTX *ctx;
883 	ads_control *external_control = (ads_control *) args;
884 
885 	*res = NULL;
886 
887 	if (!(ctx = talloc_init("ads_do_paged_search_args")))
888 		return ADS_ERROR(LDAP_NO_MEMORY);
889 
890 	/* 0 means the conversion worked but the result was empty
891 	   so we only fail if it's -1.  In any case, it always
892 	   at least nulls out the dest */
893 	if (!push_utf8_talloc(ctx, &utf8_expr, expr, &converted_size) ||
894 	    !push_utf8_talloc(ctx, &utf8_path, bind_path, &converted_size))
895 	{
896 		rc = LDAP_NO_MEMORY;
897 		goto done;
898 	}
899 
900 	if (!attrs || !(*attrs))
901 		search_attrs = NULL;
902 	else {
903 		/* This would be the utf8-encoded version...*/
904 		/* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
905 		if (!(search_attrs = str_list_copy(talloc_tos(), attrs))) {
906 			rc = LDAP_NO_MEMORY;
907 			goto done;
908 		}
909 	}
910 
911 	/* Paged results only available on ldap v3 or later */
912 	ldap_get_option(ads->ldap.ld, LDAP_OPT_PROTOCOL_VERSION, &version);
913 	if (version < LDAP_VERSION3) {
914 		rc =  LDAP_NOT_SUPPORTED;
915 		goto done;
916 	}
917 
918 	cookie_be = ber_alloc_t(LBER_USE_DER);
919 	if (*cookie) {
920 		ber_printf(cookie_be, "{iO}", (ber_int_t) ads->config.ldap_page_size, *cookie);
921 		ber_bvfree(*cookie); /* don't need it from last time */
922 		*cookie = NULL;
923 	} else {
924 		ber_printf(cookie_be, "{io}", (ber_int_t) ads->config.ldap_page_size, "", 0);
925 	}
926 	ber_flatten(cookie_be, &cookie_bv);
927 	PagedResults.ldctl_oid = discard_const_p(char, ADS_PAGE_CTL_OID);
928 	PagedResults.ldctl_iscritical = (char) 1;
929 	PagedResults.ldctl_value.bv_len = cookie_bv->bv_len;
930 	PagedResults.ldctl_value.bv_val = cookie_bv->bv_val;
931 
932 	NoReferrals.ldctl_oid = discard_const_p(char, ADS_NO_REFERRALS_OID);
933 	NoReferrals.ldctl_iscritical = (char) 0;
934 	NoReferrals.ldctl_value.bv_len = 0;
935 	NoReferrals.ldctl_value.bv_val = discard_const_p(char, "");
936 
937 	if (external_control &&
938 	    (strequal(external_control->control, ADS_EXTENDED_DN_OID) ||
939 	     strequal(external_control->control, ADS_SD_FLAGS_OID))) {
940 
941 		ExternalCtrl.ldctl_oid = discard_const_p(char, external_control->control);
942 		ExternalCtrl.ldctl_iscritical = (char) external_control->critical;
943 
944 		/* win2k does not accept a ldctl_value beeing passed in */
945 
946 		if (external_control->val != 0) {
947 
948 			if ((ext_be = ber_alloc_t(LBER_USE_DER)) == NULL ) {
949 				rc = LDAP_NO_MEMORY;
950 				goto done;
951 			}
952 
953 			if ((ber_printf(ext_be, "{i}", (ber_int_t) external_control->val)) == -1) {
954 				rc = LDAP_NO_MEMORY;
955 				goto done;
956 			}
957 			if ((ber_flatten(ext_be, &ext_bv)) == -1) {
958 				rc = LDAP_NO_MEMORY;
959 				goto done;
960 			}
961 
962 			ExternalCtrl.ldctl_value.bv_len = ext_bv->bv_len;
963 			ExternalCtrl.ldctl_value.bv_val = ext_bv->bv_val;
964 
965 		} else {
966 			ExternalCtrl.ldctl_value.bv_len = 0;
967 			ExternalCtrl.ldctl_value.bv_val = NULL;
968 		}
969 
970 		controls[0] = &NoReferrals;
971 		controls[1] = &PagedResults;
972 		controls[2] = &ExternalCtrl;
973 		controls[3] = NULL;
974 
975 	} else {
976 		controls[0] = &NoReferrals;
977 		controls[1] = &PagedResults;
978 		controls[2] = NULL;
979 	}
980 
981 	/* we need to disable referrals as the openldap libs don't
982 	   handle them and paged results at the same time.  Using them
983 	   together results in the result record containing the server
984 	   page control being removed from the result list (tridge/jmcd)
985 
986 	   leaving this in despite the control that says don't generate
987 	   referrals, in case the server doesn't support it (jmcd)
988 	*/
989 	ldap_set_option(ads->ldap.ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
990 
991 	rc = ldap_search_with_timeout(ads->ldap.ld, utf8_path, scope, utf8_expr,
992 				      search_attrs, 0, controls,
993 				      NULL, LDAP_NO_LIMIT,
994 				      (LDAPMessage **)res);
995 
996 	ber_free(cookie_be, 1);
997 	ber_bvfree(cookie_bv);
998 
999 	if (rc) {
1000 		DEBUG(3,("ads_do_paged_search_args: ldap_search_with_timeout(%s) -> %s\n", expr,
1001 			 ldap_err2string(rc)));
1002 		if (rc == LDAP_OTHER) {
1003 			char *ldap_errmsg;
1004 			int ret;
1005 
1006 			ret = ldap_parse_result(ads->ldap.ld,
1007 						*res,
1008 						NULL,
1009 						NULL,
1010 						&ldap_errmsg,
1011 						NULL,
1012 						NULL,
1013 						0);
1014 			if (ret == LDAP_SUCCESS) {
1015 				DEBUG(3, ("ldap_search_with_timeout(%s) "
1016 					  "error: %s\n", expr, ldap_errmsg));
1017 				ldap_memfree(ldap_errmsg);
1018 			}
1019 		}
1020 		goto done;
1021 	}
1022 
1023 	rc = ldap_parse_result(ads->ldap.ld, *res, NULL, NULL, NULL,
1024 					NULL, &rcontrols,  0);
1025 
1026 	if (!rcontrols) {
1027 		goto done;
1028 	}
1029 
1030 	for (i=0; rcontrols[i]; i++) {
1031 		if (strcmp(ADS_PAGE_CTL_OID, rcontrols[i]->ldctl_oid) == 0) {
1032 			cookie_be = ber_init(&rcontrols[i]->ldctl_value);
1033 			ber_scanf(cookie_be,"{iO}", (ber_int_t *) count,
1034 				  &cookie_bv);
1035 			/* the berval is the cookie, but must be freed when
1036 			   it is all done */
1037 			if (cookie_bv->bv_len) /* still more to do */
1038 				*cookie=ber_bvdup(cookie_bv);
1039 			else
1040 				*cookie=NULL;
1041 			ber_bvfree(cookie_bv);
1042 			ber_free(cookie_be, 1);
1043 			break;
1044 		}
1045 	}
1046 	ldap_controls_free(rcontrols);
1047 
1048 done:
1049 	talloc_destroy(ctx);
1050 
1051 	if (ext_be) {
1052 		ber_free(ext_be, 1);
1053 	}
1054 
1055 	if (ext_bv) {
1056 		ber_bvfree(ext_bv);
1057 	}
1058 
1059 	if (rc != LDAP_SUCCESS && *res != NULL) {
1060 		ads_msgfree(ads, *res);
1061 		*res = NULL;
1062 	}
1063 
1064 	/* if/when we decide to utf8-encode attrs, take out this next line */
1065 	TALLOC_FREE(search_attrs);
1066 
1067 	return ADS_ERROR(rc);
1068 }
1069 
ads_do_paged_search(ADS_STRUCT * ads,const char * bind_path,int scope,const char * expr,const char ** attrs,LDAPMessage ** res,int * count,struct berval ** cookie)1070 static ADS_STATUS ads_do_paged_search(ADS_STRUCT *ads, const char *bind_path,
1071 				      int scope, const char *expr,
1072 				      const char **attrs, LDAPMessage **res,
1073 				      int *count, struct berval **cookie)
1074 {
1075 	return ads_do_paged_search_args(ads, bind_path, scope, expr, attrs, NULL, res, count, cookie);
1076 }
1077 
1078 
1079 /**
1080  * Get all results for a search.  This uses ads_do_paged_search() to return
1081  * all entries in a large search.
1082  * @param ads connection to ads server
1083  * @param bind_path Base dn for the search
1084  * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1085  * @param expr Search expression
1086  * @param attrs Attributes to retrieve
1087  * @param res ** which will contain results - free res* with ads_msgfree()
1088  * @return status of search
1089  **/
ads_do_search_all_args(ADS_STRUCT * ads,const char * bind_path,int scope,const char * expr,const char ** attrs,void * args,LDAPMessage ** res)1090  ADS_STATUS ads_do_search_all_args(ADS_STRUCT *ads, const char *bind_path,
1091 				   int scope, const char *expr,
1092 				   const char **attrs, void *args,
1093 				   LDAPMessage **res)
1094 {
1095 	struct berval *cookie = NULL;
1096 	int count = 0;
1097 	ADS_STATUS status;
1098 
1099 	*res = NULL;
1100 	status = ads_do_paged_search_args(ads, bind_path, scope, expr, attrs, args, res,
1101 				     &count, &cookie);
1102 
1103 	if (!ADS_ERR_OK(status))
1104 		return status;
1105 
1106 #ifdef HAVE_LDAP_ADD_RESULT_ENTRY
1107 	while (cookie) {
1108 		LDAPMessage *res2 = NULL;
1109 		LDAPMessage *msg, *next;
1110 
1111 		status = ads_do_paged_search_args(ads, bind_path, scope, expr,
1112 					      attrs, args, &res2, &count, &cookie);
1113 		if (!ADS_ERR_OK(status)) {
1114 			break;
1115 		}
1116 
1117 		/* this relies on the way that ldap_add_result_entry() works internally. I hope
1118 		   that this works on all ldap libs, but I have only tested with openldap */
1119 		for (msg = ads_first_message(ads, res2); msg; msg = next) {
1120 			next = ads_next_message(ads, msg);
1121 			ldap_add_result_entry((LDAPMessage **)res, msg);
1122 		}
1123 		/* note that we do not free res2, as the memory is now
1124                    part of the main returned list */
1125 	}
1126 #else
1127 	DEBUG(0, ("no ldap_add_result_entry() support in LDAP libs!\n"));
1128 	status = ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
1129 #endif
1130 
1131 	return status;
1132 }
1133 
ads_do_search_all(ADS_STRUCT * ads,const char * bind_path,int scope,const char * expr,const char ** attrs,LDAPMessage ** res)1134  ADS_STATUS ads_do_search_all(ADS_STRUCT *ads, const char *bind_path,
1135 			      int scope, const char *expr,
1136 			      const char **attrs, LDAPMessage **res)
1137 {
1138 	return ads_do_search_all_args(ads, bind_path, scope, expr, attrs, NULL, res);
1139 }
1140 
ads_do_search_all_sd_flags(ADS_STRUCT * ads,const char * bind_path,int scope,const char * expr,const char ** attrs,uint32_t sd_flags,LDAPMessage ** res)1141  ADS_STATUS ads_do_search_all_sd_flags(ADS_STRUCT *ads, const char *bind_path,
1142 				       int scope, const char *expr,
1143 				       const char **attrs, uint32_t sd_flags,
1144 				       LDAPMessage **res)
1145 {
1146 	ads_control args;
1147 
1148 	args.control = ADS_SD_FLAGS_OID;
1149 	args.val = sd_flags;
1150 	args.critical = True;
1151 
1152 	return ads_do_search_all_args(ads, bind_path, scope, expr, attrs, &args, res);
1153 }
1154 
1155 
1156 /**
1157  * Run a function on all results for a search.  Uses ads_do_paged_search() and
1158  *  runs the function as each page is returned, using ads_process_results()
1159  * @param ads connection to ads server
1160  * @param bind_path Base dn for the search
1161  * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1162  * @param expr Search expression - specified in local charset
1163  * @param attrs Attributes to retrieve - specified in UTF-8 or ascii
1164  * @param fn Function which takes attr name, values list, and data_area
1165  * @param data_area Pointer which is passed to function on each call
1166  * @return status of search
1167  **/
ads_do_search_all_fn(ADS_STRUCT * ads,const char * bind_path,int scope,const char * expr,const char ** attrs,bool (* fn)(ADS_STRUCT *,char *,void **,void *),void * data_area)1168 ADS_STATUS ads_do_search_all_fn(ADS_STRUCT *ads, const char *bind_path,
1169 				int scope, const char *expr, const char **attrs,
1170 				bool (*fn)(ADS_STRUCT *, char *, void **, void *),
1171 				void *data_area)
1172 {
1173 	struct berval *cookie = NULL;
1174 	int count = 0;
1175 	ADS_STATUS status;
1176 	LDAPMessage *res;
1177 
1178 	status = ads_do_paged_search(ads, bind_path, scope, expr, attrs, &res,
1179 				     &count, &cookie);
1180 
1181 	if (!ADS_ERR_OK(status)) return status;
1182 
1183 	ads_process_results(ads, res, fn, data_area);
1184 	ads_msgfree(ads, res);
1185 
1186 	while (cookie) {
1187 		status = ads_do_paged_search(ads, bind_path, scope, expr, attrs,
1188 					     &res, &count, &cookie);
1189 
1190 		if (!ADS_ERR_OK(status)) break;
1191 
1192 		ads_process_results(ads, res, fn, data_area);
1193 		ads_msgfree(ads, res);
1194 	}
1195 
1196 	return status;
1197 }
1198 
1199 /**
1200  * Do a search with a timeout.
1201  * @param ads connection to ads server
1202  * @param bind_path Base dn for the search
1203  * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1204  * @param expr Search expression
1205  * @param attrs Attributes to retrieve
1206  * @param res ** which will contain results - free res* with ads_msgfree()
1207  * @return status of search
1208  **/
ads_do_search(ADS_STRUCT * ads,const char * bind_path,int scope,const char * expr,const char ** attrs,LDAPMessage ** res)1209  ADS_STATUS ads_do_search(ADS_STRUCT *ads, const char *bind_path, int scope,
1210 			  const char *expr,
1211 			  const char **attrs, LDAPMessage **res)
1212 {
1213 	int rc;
1214 	char *utf8_expr, *utf8_path, **search_attrs = NULL;
1215 	size_t converted_size;
1216 	TALLOC_CTX *ctx;
1217 
1218 	*res = NULL;
1219 	if (!(ctx = talloc_init("ads_do_search"))) {
1220 		DEBUG(1,("ads_do_search: talloc_init() failed!"));
1221 		return ADS_ERROR(LDAP_NO_MEMORY);
1222 	}
1223 
1224 	/* 0 means the conversion worked but the result was empty
1225 	   so we only fail if it's negative.  In any case, it always
1226 	   at least nulls out the dest */
1227 	if (!push_utf8_talloc(ctx, &utf8_expr, expr, &converted_size) ||
1228 	    !push_utf8_talloc(ctx, &utf8_path, bind_path, &converted_size))
1229 	{
1230 		DEBUG(1,("ads_do_search: push_utf8_talloc() failed!"));
1231 		rc = LDAP_NO_MEMORY;
1232 		goto done;
1233 	}
1234 
1235 	if (!attrs || !(*attrs))
1236 		search_attrs = NULL;
1237 	else {
1238 		/* This would be the utf8-encoded version...*/
1239 		/* if (!(search_attrs = ads_push_strvals(ctx, attrs)))  */
1240 		if (!(search_attrs = str_list_copy(talloc_tos(), attrs)))
1241 		{
1242 			DEBUG(1,("ads_do_search: str_list_copy() failed!"));
1243 			rc = LDAP_NO_MEMORY;
1244 			goto done;
1245 		}
1246 	}
1247 
1248 	/* see the note in ads_do_paged_search - we *must* disable referrals */
1249 	ldap_set_option(ads->ldap.ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
1250 
1251 	rc = ldap_search_with_timeout(ads->ldap.ld, utf8_path, scope, utf8_expr,
1252 				      search_attrs, 0, NULL, NULL,
1253 				      LDAP_NO_LIMIT,
1254 				      (LDAPMessage **)res);
1255 
1256 	if (rc == LDAP_SIZELIMIT_EXCEEDED) {
1257 		DEBUG(3,("Warning! sizelimit exceeded in ldap. Truncating.\n"));
1258 		rc = 0;
1259 	}
1260 
1261  done:
1262 	talloc_destroy(ctx);
1263 	/* if/when we decide to utf8-encode attrs, take out this next line */
1264 	TALLOC_FREE(search_attrs);
1265 	return ADS_ERROR(rc);
1266 }
1267 /**
1268  * Do a general ADS search
1269  * @param ads connection to ads server
1270  * @param res ** which will contain results - free res* with ads_msgfree()
1271  * @param expr Search expression
1272  * @param attrs Attributes to retrieve
1273  * @return status of search
1274  **/
ads_search(ADS_STRUCT * ads,LDAPMessage ** res,const char * expr,const char ** attrs)1275  ADS_STATUS ads_search(ADS_STRUCT *ads, LDAPMessage **res,
1276 		       const char *expr, const char **attrs)
1277 {
1278 	return ads_do_search(ads, ads->config.bind_path, LDAP_SCOPE_SUBTREE,
1279 			     expr, attrs, res);
1280 }
1281 
1282 /**
1283  * Do a search on a specific DistinguishedName
1284  * @param ads connection to ads server
1285  * @param res ** which will contain results - free res* with ads_msgfree()
1286  * @param dn DistinguishName to search
1287  * @param attrs Attributes to retrieve
1288  * @return status of search
1289  **/
ads_search_dn(ADS_STRUCT * ads,LDAPMessage ** res,const char * dn,const char ** attrs)1290  ADS_STATUS ads_search_dn(ADS_STRUCT *ads, LDAPMessage **res,
1291 			  const char *dn, const char **attrs)
1292 {
1293 	return ads_do_search(ads, dn, LDAP_SCOPE_BASE, "(objectclass=*)",
1294 			     attrs, res);
1295 }
1296 
1297 /**
1298  * Free up memory from a ads_search
1299  * @param ads connection to ads server
1300  * @param msg Search results to free
1301  **/
ads_msgfree(ADS_STRUCT * ads,LDAPMessage * msg)1302  void ads_msgfree(ADS_STRUCT *ads, LDAPMessage *msg)
1303 {
1304 	if (!msg) return;
1305 	ldap_msgfree(msg);
1306 }
1307 
1308 /**
1309  * Get a dn from search results
1310  * @param ads connection to ads server
1311  * @param msg Search result
1312  * @return dn string
1313  **/
ads_get_dn(ADS_STRUCT * ads,TALLOC_CTX * mem_ctx,LDAPMessage * msg)1314  char *ads_get_dn(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, LDAPMessage *msg)
1315 {
1316 	char *utf8_dn, *unix_dn;
1317 	size_t converted_size;
1318 
1319 	utf8_dn = ldap_get_dn(ads->ldap.ld, msg);
1320 
1321 	if (!utf8_dn) {
1322 		DEBUG (5, ("ads_get_dn: ldap_get_dn failed\n"));
1323 		return NULL;
1324 	}
1325 
1326 	if (!pull_utf8_talloc(mem_ctx, &unix_dn, utf8_dn, &converted_size)) {
1327 		DEBUG(0,("ads_get_dn: string conversion failure utf8 [%s]\n",
1328 			utf8_dn ));
1329 		return NULL;
1330 	}
1331 	ldap_memfree(utf8_dn);
1332 	return unix_dn;
1333 }
1334 
1335 /**
1336  * Get the parent from a dn
1337  * @param dn the dn to return the parent from
1338  * @return parent dn string
1339  **/
ads_parent_dn(const char * dn)1340 char *ads_parent_dn(const char *dn)
1341 {
1342 	char *p;
1343 
1344 	if (dn == NULL) {
1345 		return NULL;
1346 	}
1347 
1348 	p = strchr(dn, ',');
1349 
1350 	if (p == NULL) {
1351 		return NULL;
1352 	}
1353 
1354 	return p+1;
1355 }
1356 
1357 /**
1358  * Find a machine account given a hostname
1359  * @param ads connection to ads server
1360  * @param res ** which will contain results - free res* with ads_msgfree()
1361  * @param host Hostname to search for
1362  * @return status of search
1363  **/
ads_find_machine_acct(ADS_STRUCT * ads,LDAPMessage ** res,const char * machine)1364  ADS_STATUS ads_find_machine_acct(ADS_STRUCT *ads, LDAPMessage **res,
1365 				  const char *machine)
1366 {
1367 	ADS_STATUS status;
1368 	char *expr;
1369 	const char *attrs[] = {
1370 		/* This is how Windows checks for machine accounts */
1371 		"objectClass",
1372 		"SamAccountName",
1373 		"userAccountControl",
1374 		"DnsHostName",
1375 		"ServicePrincipalName",
1376 		"userPrincipalName",
1377 		"unicodePwd",
1378 
1379 		/* Additional attributes Samba checks */
1380 		"msDS-AdditionalDnsHostName",
1381 		"msDS-SupportedEncryptionTypes",
1382 		"nTSecurityDescriptor",
1383 
1384 		NULL
1385 	};
1386 	TALLOC_CTX *frame = talloc_stackframe();
1387 
1388 	*res = NULL;
1389 
1390 	/* the easiest way to find a machine account anywhere in the tree
1391 	   is to look for hostname$ */
1392 	expr = talloc_asprintf(frame, "(samAccountName=%s$)", machine);
1393 	if (expr == NULL) {
1394 		status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1395 		goto done;
1396 	}
1397 
1398 	status = ads_search(ads, res, expr, attrs);
1399 	if (ADS_ERR_OK(status)) {
1400 		if (ads_count_replies(ads, *res) != 1) {
1401 			status = ADS_ERROR_LDAP(LDAP_NO_SUCH_OBJECT);
1402 		}
1403 	}
1404 
1405 done:
1406 	TALLOC_FREE(frame);
1407 	return status;
1408 }
1409 
1410 /**
1411  * Initialize a list of mods to be used in a modify request
1412  * @param ctx An initialized TALLOC_CTX
1413  * @return allocated ADS_MODLIST
1414  **/
ads_init_mods(TALLOC_CTX * ctx)1415 ADS_MODLIST ads_init_mods(TALLOC_CTX *ctx)
1416 {
1417 #define ADS_MODLIST_ALLOC_SIZE 10
1418 	LDAPMod **mods;
1419 
1420 	if ((mods = talloc_zero_array(ctx, LDAPMod *, ADS_MODLIST_ALLOC_SIZE + 1)))
1421 		/* -1 is safety to make sure we don't go over the end.
1422 		   need to reset it to NULL before doing ldap modify */
1423 		mods[ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1424 
1425 	return (ADS_MODLIST)mods;
1426 }
1427 
1428 
1429 /*
1430   add an attribute to the list, with values list already constructed
1431 */
ads_modlist_add(TALLOC_CTX * ctx,ADS_MODLIST * mods,int mod_op,const char * name,const void * _invals)1432 static ADS_STATUS ads_modlist_add(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1433 				  int mod_op, const char *name,
1434 				  const void *_invals)
1435 {
1436 	int curmod;
1437 	LDAPMod **modlist = (LDAPMod **) *mods;
1438 	struct berval **ber_values = NULL;
1439 	char **char_values = NULL;
1440 
1441 	if (!_invals) {
1442 		mod_op = LDAP_MOD_DELETE;
1443 	} else {
1444 		if (mod_op & LDAP_MOD_BVALUES) {
1445 			const struct berval **b;
1446 			b = discard_const_p(const struct berval *, _invals);
1447 			ber_values = ads_dup_values(ctx, b);
1448 		} else {
1449 			const char **c;
1450 			c = discard_const_p(const char *, _invals);
1451 			char_values = ads_push_strvals(ctx, c);
1452 		}
1453 	}
1454 
1455 	/* find the first empty slot */
1456 	for (curmod=0; modlist[curmod] && modlist[curmod] != (LDAPMod *) -1;
1457 	     curmod++);
1458 	if (modlist[curmod] == (LDAPMod *) -1) {
1459 		if (!(modlist = talloc_realloc(ctx, modlist, LDAPMod *,
1460 				curmod+ADS_MODLIST_ALLOC_SIZE+1)))
1461 			return ADS_ERROR(LDAP_NO_MEMORY);
1462 		memset(&modlist[curmod], 0,
1463 		       ADS_MODLIST_ALLOC_SIZE*sizeof(LDAPMod *));
1464 		modlist[curmod+ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1465 		*mods = (ADS_MODLIST)modlist;
1466 	}
1467 
1468 	if (!(modlist[curmod] = talloc_zero(ctx, LDAPMod)))
1469 		return ADS_ERROR(LDAP_NO_MEMORY);
1470 	modlist[curmod]->mod_type = talloc_strdup(ctx, name);
1471 	if (mod_op & LDAP_MOD_BVALUES) {
1472 		modlist[curmod]->mod_bvalues = ber_values;
1473 	} else if (mod_op & LDAP_MOD_DELETE) {
1474 		modlist[curmod]->mod_values = NULL;
1475 	} else {
1476 		modlist[curmod]->mod_values = char_values;
1477 	}
1478 
1479 	modlist[curmod]->mod_op = mod_op;
1480 	return ADS_ERROR(LDAP_SUCCESS);
1481 }
1482 
1483 /**
1484  * Add a single string value to a mod list
1485  * @param ctx An initialized TALLOC_CTX
1486  * @param mods An initialized ADS_MODLIST
1487  * @param name The attribute name to add
1488  * @param val The value to add - NULL means DELETE
1489  * @return ADS STATUS indicating success of add
1490  **/
ads_mod_str(TALLOC_CTX * ctx,ADS_MODLIST * mods,const char * name,const char * val)1491 ADS_STATUS ads_mod_str(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1492 		       const char *name, const char *val)
1493 {
1494 	const char *values[2];
1495 
1496 	values[0] = val;
1497 	values[1] = NULL;
1498 
1499 	if (!val)
1500 		return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1501 	return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE, name, values);
1502 }
1503 
1504 /**
1505  * Add an array of string values to a mod list
1506  * @param ctx An initialized TALLOC_CTX
1507  * @param mods An initialized ADS_MODLIST
1508  * @param name The attribute name to add
1509  * @param vals The array of string values to add - NULL means DELETE
1510  * @return ADS STATUS indicating success of add
1511  **/
ads_mod_strlist(TALLOC_CTX * ctx,ADS_MODLIST * mods,const char * name,const char ** vals)1512 ADS_STATUS ads_mod_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1513 			   const char *name, const char **vals)
1514 {
1515 	if (!vals)
1516 		return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1517 	return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE,
1518 			       name, (const void **) vals);
1519 }
1520 
1521 /**
1522  * Add a single ber-encoded value to a mod list
1523  * @param ctx An initialized TALLOC_CTX
1524  * @param mods An initialized ADS_MODLIST
1525  * @param name The attribute name to add
1526  * @param val The value to add - NULL means DELETE
1527  * @return ADS STATUS indicating success of add
1528  **/
ads_mod_ber(TALLOC_CTX * ctx,ADS_MODLIST * mods,const char * name,const struct berval * val)1529 static ADS_STATUS ads_mod_ber(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1530 			      const char *name, const struct berval *val)
1531 {
1532 	const struct berval *values[2];
1533 
1534 	values[0] = val;
1535 	values[1] = NULL;
1536 	if (!val)
1537 		return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1538 	return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE|LDAP_MOD_BVALUES,
1539 			       name, (const void **) values);
1540 }
1541 
ads_print_error(int ret,LDAP * ld)1542 static void ads_print_error(int ret, LDAP *ld)
1543 {
1544 	if (ret != 0) {
1545 		char *ld_error = NULL;
1546 		ldap_get_option(ld, LDAP_OPT_ERROR_STRING, &ld_error);
1547 		DBG_ERR("AD LDAP ERROR: %d (%s): %s\n",
1548 			ret,
1549 			ldap_err2string(ret),
1550 			ld_error);
1551 		SAFE_FREE(ld_error);
1552 	}
1553 }
1554 
1555 /**
1556  * Perform an ldap modify
1557  * @param ads connection to ads server
1558  * @param mod_dn DistinguishedName to modify
1559  * @param mods list of modifications to perform
1560  * @return status of modify
1561  **/
ads_gen_mod(ADS_STRUCT * ads,const char * mod_dn,ADS_MODLIST mods)1562 ADS_STATUS ads_gen_mod(ADS_STRUCT *ads, const char *mod_dn, ADS_MODLIST mods)
1563 {
1564 	int ret,i;
1565 	char *utf8_dn = NULL;
1566 	size_t converted_size;
1567 	/*
1568 	   this control is needed to modify that contains a currently
1569 	   non-existent attribute (but allowable for the object) to run
1570 	*/
1571 	LDAPControl PermitModify = {
1572                 discard_const_p(char, ADS_PERMIT_MODIFY_OID),
1573 		{0, NULL},
1574 		(char) 1};
1575 	LDAPControl *controls[2];
1576 
1577 	DBG_INFO("AD LDAP: Modifying %s\n", mod_dn);
1578 
1579 	controls[0] = &PermitModify;
1580 	controls[1] = NULL;
1581 
1582 	if (!push_utf8_talloc(talloc_tos(), &utf8_dn, mod_dn, &converted_size)) {
1583 		return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1584 	}
1585 
1586 	/* find the end of the list, marked by NULL or -1 */
1587 	for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1588 	/* make sure the end of the list is NULL */
1589 	mods[i] = NULL;
1590 	ret = ldap_modify_ext_s(ads->ldap.ld, utf8_dn,
1591 				(LDAPMod **) mods, controls, NULL);
1592 	ads_print_error(ret, ads->ldap.ld);
1593 	TALLOC_FREE(utf8_dn);
1594 	return ADS_ERROR(ret);
1595 }
1596 
1597 /**
1598  * Perform an ldap add
1599  * @param ads connection to ads server
1600  * @param new_dn DistinguishedName to add
1601  * @param mods list of attributes and values for DN
1602  * @return status of add
1603  **/
ads_gen_add(ADS_STRUCT * ads,const char * new_dn,ADS_MODLIST mods)1604 ADS_STATUS ads_gen_add(ADS_STRUCT *ads, const char *new_dn, ADS_MODLIST mods)
1605 {
1606 	int ret, i;
1607 	char *utf8_dn = NULL;
1608 	size_t converted_size;
1609 
1610 	DBG_INFO("AD LDAP: Adding %s\n", new_dn);
1611 
1612 	if (!push_utf8_talloc(talloc_tos(), &utf8_dn, new_dn, &converted_size)) {
1613 		DEBUG(1, ("ads_gen_add: push_utf8_talloc failed!"));
1614 		return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1615 	}
1616 
1617 	/* find the end of the list, marked by NULL or -1 */
1618 	for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1619 	/* make sure the end of the list is NULL */
1620 	mods[i] = NULL;
1621 
1622 	ret = ldap_add_ext_s(ads->ldap.ld, utf8_dn, (LDAPMod**)mods, NULL, NULL);
1623 	ads_print_error(ret, ads->ldap.ld);
1624 	TALLOC_FREE(utf8_dn);
1625 	return ADS_ERROR(ret);
1626 }
1627 
1628 /**
1629  * Delete a DistinguishedName
1630  * @param ads connection to ads server
1631  * @param new_dn DistinguishedName to delete
1632  * @return status of delete
1633  **/
ads_del_dn(ADS_STRUCT * ads,char * del_dn)1634 ADS_STATUS ads_del_dn(ADS_STRUCT *ads, char *del_dn)
1635 {
1636 	int ret;
1637 	char *utf8_dn = NULL;
1638 	size_t converted_size;
1639 	if (!push_utf8_talloc(talloc_tos(), &utf8_dn, del_dn, &converted_size)) {
1640 		DEBUG(1, ("ads_del_dn: push_utf8_talloc failed!"));
1641 		return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1642 	}
1643 
1644 	DBG_INFO("AD LDAP: Deleting %s\n", del_dn);
1645 
1646 	ret = ldap_delete_s(ads->ldap.ld, utf8_dn);
1647 	ads_print_error(ret, ads->ldap.ld);
1648 	TALLOC_FREE(utf8_dn);
1649 	return ADS_ERROR(ret);
1650 }
1651 
1652 /**
1653  * Build an org unit string
1654  *  if org unit is Computers or blank then assume a container, otherwise
1655  *  assume a / separated list of organisational units.
1656  * jmcd: '\' is now used for escapes so certain chars can be in the ou (e.g. #)
1657  * @param ads connection to ads server
1658  * @param org_unit Organizational unit
1659  * @return org unit string - caller must free
1660  **/
ads_ou_string(ADS_STRUCT * ads,const char * org_unit)1661 char *ads_ou_string(ADS_STRUCT *ads, const char *org_unit)
1662 {
1663 	char *ret = NULL;
1664 
1665 	if (!org_unit || !*org_unit) {
1666 
1667 		ret = ads_default_ou_string(ads, DS_GUID_COMPUTERS_CONTAINER);
1668 
1669 		/* samba4 might not yet respond to a wellknownobject-query */
1670 		return ret ? ret : SMB_STRDUP("cn=Computers");
1671 	}
1672 
1673 	if (strequal(org_unit, "Computers")) {
1674 		return SMB_STRDUP("cn=Computers");
1675 	}
1676 
1677 	/* jmcd: removed "\\" from the separation chars, because it is
1678 	   needed as an escape for chars like '#' which are valid in an
1679 	   OU name */
1680 	return ads_build_path(org_unit, "/", "ou=", 1);
1681 }
1682 
1683 /**
1684  * Get a org unit string for a well-known GUID
1685  * @param ads connection to ads server
1686  * @param wknguid Well known GUID
1687  * @return org unit string - caller must free
1688  **/
ads_default_ou_string(ADS_STRUCT * ads,const char * wknguid)1689 char *ads_default_ou_string(ADS_STRUCT *ads, const char *wknguid)
1690 {
1691 	ADS_STATUS status;
1692 	LDAPMessage *res = NULL;
1693 	char *base, *wkn_dn = NULL, *ret = NULL, **wkn_dn_exp = NULL,
1694 		**bind_dn_exp = NULL;
1695 	const char *attrs[] = {"distinguishedName", NULL};
1696 	int new_ln, wkn_ln, bind_ln, i;
1697 
1698 	if (wknguid == NULL) {
1699 		return NULL;
1700 	}
1701 
1702 	if (asprintf(&base, "<WKGUID=%s,%s>", wknguid, ads->config.bind_path ) == -1) {
1703 		DEBUG(1, ("asprintf failed!\n"));
1704 		return NULL;
1705 	}
1706 
1707 	status = ads_search_dn(ads, &res, base, attrs);
1708 	if (!ADS_ERR_OK(status)) {
1709 		DEBUG(1,("Failed while searching for: %s\n", base));
1710 		goto out;
1711 	}
1712 
1713 	if (ads_count_replies(ads, res) != 1) {
1714 		goto out;
1715 	}
1716 
1717 	/* substitute the bind-path from the well-known-guid-search result */
1718 	wkn_dn = ads_get_dn(ads, talloc_tos(), res);
1719 	if (!wkn_dn) {
1720 		goto out;
1721 	}
1722 
1723 	wkn_dn_exp = ldap_explode_dn(wkn_dn, 0);
1724 	if (!wkn_dn_exp) {
1725 		goto out;
1726 	}
1727 
1728 	bind_dn_exp = ldap_explode_dn(ads->config.bind_path, 0);
1729 	if (!bind_dn_exp) {
1730 		goto out;
1731 	}
1732 
1733 	for (wkn_ln=0; wkn_dn_exp[wkn_ln]; wkn_ln++)
1734 		;
1735 	for (bind_ln=0; bind_dn_exp[bind_ln]; bind_ln++)
1736 		;
1737 
1738 	new_ln = wkn_ln - bind_ln;
1739 
1740 	ret = SMB_STRDUP(wkn_dn_exp[0]);
1741 	if (!ret) {
1742 		goto out;
1743 	}
1744 
1745 	for (i=1; i < new_ln; i++) {
1746 		char *s = NULL;
1747 
1748 		if (asprintf(&s, "%s,%s", ret, wkn_dn_exp[i]) == -1) {
1749 			SAFE_FREE(ret);
1750 			goto out;
1751 		}
1752 
1753 		SAFE_FREE(ret);
1754 		ret = SMB_STRDUP(s);
1755 		free(s);
1756 		if (!ret) {
1757 			goto out;
1758 		}
1759 	}
1760 
1761  out:
1762 	SAFE_FREE(base);
1763 	ads_msgfree(ads, res);
1764 	TALLOC_FREE(wkn_dn);
1765 	if (wkn_dn_exp) {
1766 		ldap_value_free(wkn_dn_exp);
1767 	}
1768 	if (bind_dn_exp) {
1769 		ldap_value_free(bind_dn_exp);
1770 	}
1771 
1772 	return ret;
1773 }
1774 
1775 /**
1776  * Adds (appends) an item to an attribute array, rather then
1777  * replacing the whole list
1778  * @param ctx An initialized TALLOC_CTX
1779  * @param mods An initialized ADS_MODLIST
1780  * @param name name of the ldap attribute to append to
1781  * @param vals an array of values to add
1782  * @return status of addition
1783  **/
1784 
ads_add_strlist(TALLOC_CTX * ctx,ADS_MODLIST * mods,const char * name,const char ** vals)1785 ADS_STATUS ads_add_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1786 				const char *name, const char **vals)
1787 {
1788 	return ads_modlist_add(ctx, mods, LDAP_MOD_ADD, name,
1789 			       (const void *) vals);
1790 }
1791 
1792 /**
1793  * Determines the an account's current KVNO via an LDAP lookup
1794  * @param ads An initialized ADS_STRUCT
1795  * @param account_name the NT samaccountname.
1796  * @return the kvno for the account, or -1 in case of a failure.
1797  **/
1798 
ads_get_kvno(ADS_STRUCT * ads,const char * account_name)1799 uint32_t ads_get_kvno(ADS_STRUCT *ads, const char *account_name)
1800 {
1801 	LDAPMessage *res = NULL;
1802 	uint32_t kvno = (uint32_t)-1;      /* -1 indicates a failure */
1803 	char *filter;
1804 	const char *attrs[] = {"msDS-KeyVersionNumber", NULL};
1805 	char *dn_string = NULL;
1806 	ADS_STATUS ret;
1807 
1808 	DEBUG(5,("ads_get_kvno: Searching for account %s\n", account_name));
1809 	if (asprintf(&filter, "(samAccountName=%s)", account_name) == -1) {
1810 		return kvno;
1811 	}
1812 	ret = ads_search(ads, &res, filter, attrs);
1813 	SAFE_FREE(filter);
1814 	if (!ADS_ERR_OK(ret) || (ads_count_replies(ads, res) != 1)) {
1815 		DEBUG(1,("ads_get_kvno: Account for %s not found.\n", account_name));
1816 		ads_msgfree(ads, res);
1817 		return kvno;
1818 	}
1819 
1820 	dn_string = ads_get_dn(ads, talloc_tos(), res);
1821 	if (!dn_string) {
1822 		DEBUG(0,("ads_get_kvno: out of memory.\n"));
1823 		ads_msgfree(ads, res);
1824 		return kvno;
1825 	}
1826 	DEBUG(5,("ads_get_kvno: Using: %s\n", dn_string));
1827 	TALLOC_FREE(dn_string);
1828 
1829 	/* ---------------------------------------------------------
1830 	 * 0 is returned as a default KVNO from this point on...
1831 	 * This is done because Windows 2000 does not support key
1832 	 * version numbers.  Chances are that a failure in the next
1833 	 * step is simply due to Windows 2000 being used for a
1834 	 * domain controller. */
1835 	kvno = 0;
1836 
1837 	if (!ads_pull_uint32(ads, res, "msDS-KeyVersionNumber", &kvno)) {
1838 		DEBUG(3,("ads_get_kvno: Error Determining KVNO!\n"));
1839 		DEBUG(3,("ads_get_kvno: Windows 2000 does not support KVNO's, so this may be normal.\n"));
1840 		ads_msgfree(ads, res);
1841 		return kvno;
1842 	}
1843 
1844 	/* Success */
1845 	DEBUG(5,("ads_get_kvno: Looked Up KVNO of: %d\n", kvno));
1846 	ads_msgfree(ads, res);
1847 	return kvno;
1848 }
1849 
1850 /**
1851  * Determines the computer account's current KVNO via an LDAP lookup
1852  * @param ads An initialized ADS_STRUCT
1853  * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1854  * @return the kvno for the computer account, or -1 in case of a failure.
1855  **/
1856 
ads_get_machine_kvno(ADS_STRUCT * ads,const char * machine_name)1857 uint32_t ads_get_machine_kvno(ADS_STRUCT *ads, const char *machine_name)
1858 {
1859 	char *computer_account = NULL;
1860 	uint32_t kvno = -1;
1861 
1862 	if (asprintf(&computer_account, "%s$", machine_name) < 0) {
1863 		return kvno;
1864 	}
1865 
1866 	kvno = ads_get_kvno(ads, computer_account);
1867 	free(computer_account);
1868 
1869 	return kvno;
1870 }
1871 
1872 /**
1873  * This clears out all registered spn's for a given hostname
1874  * @param ads An initilaized ADS_STRUCT
1875  * @param machine_name the NetBIOS name of the computer.
1876  * @return 0 upon success, non-zero otherwise.
1877  **/
1878 
ads_clear_service_principal_names(ADS_STRUCT * ads,const char * machine_name)1879 ADS_STATUS ads_clear_service_principal_names(ADS_STRUCT *ads, const char *machine_name)
1880 {
1881 	TALLOC_CTX *ctx;
1882 	LDAPMessage *res = NULL;
1883 	ADS_MODLIST mods;
1884 	const char *servicePrincipalName[1] = {NULL};
1885 	ADS_STATUS ret;
1886 	char *dn_string = NULL;
1887 
1888 	ret = ads_find_machine_acct(ads, &res, machine_name);
1889 	if (!ADS_ERR_OK(ret)) {
1890 		DEBUG(5,("ads_clear_service_principal_names: WARNING: Host Account for %s not found... skipping operation.\n", machine_name));
1891 		DEBUG(5,("ads_clear_service_principal_names: WARNING: Service Principals for %s have NOT been cleared.\n", machine_name));
1892 		ads_msgfree(ads, res);
1893 		return ret;
1894 	}
1895 
1896 	DEBUG(5,("ads_clear_service_principal_names: Host account for %s found\n", machine_name));
1897 	ctx = talloc_init("ads_clear_service_principal_names");
1898 	if (!ctx) {
1899 		ads_msgfree(ads, res);
1900 		return ADS_ERROR(LDAP_NO_MEMORY);
1901 	}
1902 
1903 	if (!(mods = ads_init_mods(ctx))) {
1904 		talloc_destroy(ctx);
1905 		ads_msgfree(ads, res);
1906 		return ADS_ERROR(LDAP_NO_MEMORY);
1907 	}
1908 	ret = ads_mod_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1909 	if (!ADS_ERR_OK(ret)) {
1910 		DEBUG(1,("ads_clear_service_principal_names: Error creating strlist.\n"));
1911 		ads_msgfree(ads, res);
1912 		talloc_destroy(ctx);
1913 		return ret;
1914 	}
1915 	dn_string = ads_get_dn(ads, talloc_tos(), res);
1916 	if (!dn_string) {
1917 		talloc_destroy(ctx);
1918 		ads_msgfree(ads, res);
1919 		return ADS_ERROR(LDAP_NO_MEMORY);
1920 	}
1921 	ret = ads_gen_mod(ads, dn_string, mods);
1922 	TALLOC_FREE(dn_string);
1923 	if (!ADS_ERR_OK(ret)) {
1924 		DEBUG(1,("ads_clear_service_principal_names: Error: Updating Service Principals for machine %s in LDAP\n",
1925 			machine_name));
1926 		ads_msgfree(ads, res);
1927 		talloc_destroy(ctx);
1928 		return ret;
1929 	}
1930 
1931 	ads_msgfree(ads, res);
1932 	talloc_destroy(ctx);
1933 	return ret;
1934 }
1935 
1936 /**
1937  * @brief Search for an element in a string array.
1938  *
1939  * @param[in]  el_array  The string array to search.
1940  *
1941  * @param[in]  num_el    The number of elements in the string array.
1942  *
1943  * @param[in]  el        The string to search.
1944  *
1945  * @return               True if found, false if not.
1946  */
ads_element_in_array(const char ** el_array,size_t num_el,const char * el)1947 bool ads_element_in_array(const char **el_array, size_t num_el, const char *el)
1948 {
1949 	size_t i;
1950 
1951 	if (el_array == NULL || num_el == 0 || el == NULL) {
1952 		return false;
1953 	}
1954 
1955 	for (i = 0; i < num_el && el_array[i] != NULL; i++) {
1956 		int cmp;
1957 
1958 		cmp = strcasecmp_m(el_array[i], el);
1959 		if (cmp == 0) {
1960 			return true;
1961 		}
1962 	}
1963 
1964 	return false;
1965 }
1966 
1967 /**
1968  * @brief This gets the service principal names of an existing computer account.
1969  *
1970  * @param[in]  mem_ctx      The memory context to use to allocate the spn array.
1971  *
1972  * @param[in]  ads          The ADS context to use.
1973  *
1974  * @param[in]  machine_name The NetBIOS name of the computer, which is used to
1975  *                          identify the computer account.
1976  *
1977  * @param[in]  spn_array    A pointer to store the array for SPNs.
1978  *
1979  * @param[in]  num_spns     The number of principals stored in the array.
1980  *
1981  * @return                  0 on success, or a ADS error if a failure occurred.
1982  */
ads_get_service_principal_names(TALLOC_CTX * mem_ctx,ADS_STRUCT * ads,const char * machine_name,char *** spn_array,size_t * num_spns)1983 ADS_STATUS ads_get_service_principal_names(TALLOC_CTX *mem_ctx,
1984 					   ADS_STRUCT *ads,
1985 					   const char *machine_name,
1986 					   char ***spn_array,
1987 					   size_t *num_spns)
1988 {
1989 	ADS_STATUS status;
1990 	LDAPMessage *res = NULL;
1991 	int count;
1992 
1993 	status = ads_find_machine_acct(ads,
1994 				       &res,
1995 				       machine_name);
1996 	if (!ADS_ERR_OK(status)) {
1997 		DEBUG(1,("Host Account for %s not found... skipping operation.\n",
1998 			 machine_name));
1999 		return status;
2000 	}
2001 
2002 	count = ads_count_replies(ads, res);
2003 	if (count != 1) {
2004 		status = ADS_ERROR(LDAP_NO_SUCH_OBJECT);
2005 		goto done;
2006 	}
2007 
2008 	*spn_array = ads_pull_strings(ads,
2009 				      mem_ctx,
2010 				      res,
2011 				      "servicePrincipalName",
2012 				      num_spns);
2013 	if (*spn_array == NULL) {
2014 		DEBUG(1, ("Host account for %s does not have service principal "
2015 			  "names.\n",
2016 			  machine_name));
2017 		status = ADS_ERROR(LDAP_NO_SUCH_OBJECT);
2018 		goto done;
2019 	}
2020 
2021 done:
2022 	ads_msgfree(ads, res);
2023 
2024 	return status;
2025 }
2026 
2027 /**
2028  * This adds a service principal name to an existing computer account
2029  * (found by hostname) in AD.
2030  * @param ads An initialized ADS_STRUCT
2031  * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
2032  * @param spns An array or strings for the service principals to add,
2033  *        i.e. 'cifs/machine_name', 'http/machine.full.domain.com' etc.
2034  * @return 0 upon sucess, or non-zero if a failure occurs
2035  **/
2036 
ads_add_service_principal_names(ADS_STRUCT * ads,const char * machine_name,const char ** spns)2037 ADS_STATUS ads_add_service_principal_names(ADS_STRUCT *ads,
2038 					   const char *machine_name,
2039                                            const char **spns)
2040 {
2041 	ADS_STATUS ret;
2042 	TALLOC_CTX *ctx;
2043 	LDAPMessage *res = NULL;
2044 	ADS_MODLIST mods;
2045 	char *dn_string = NULL;
2046 	const char **servicePrincipalName = spns;
2047 
2048 	ret = ads_find_machine_acct(ads, &res, machine_name);
2049 	if (!ADS_ERR_OK(ret)) {
2050 		DEBUG(1,("ads_add_service_principal_name: WARNING: Host Account for %s not found... skipping operation.\n",
2051 			machine_name));
2052 		DEBUG(1,("ads_add_service_principal_name: WARNING: Service Principals have NOT been added.\n"));
2053 		ads_msgfree(ads, res);
2054 		return ret;
2055 	}
2056 
2057 	DEBUG(1,("ads_add_service_principal_name: Host account for %s found\n", machine_name));
2058 	if (!(ctx = talloc_init("ads_add_service_principal_name"))) {
2059 		ads_msgfree(ads, res);
2060 		return ADS_ERROR(LDAP_NO_MEMORY);
2061 	}
2062 
2063 	DEBUG(5,("ads_add_service_principal_name: INFO: "
2064 		"Adding %s to host %s\n",
2065 		spns[0] ? "N/A" : spns[0], machine_name));
2066 
2067 
2068 	DEBUG(5,("ads_add_service_principal_name: INFO: "
2069 		"Adding %s to host %s\n",
2070 		spns[1] ? "N/A" : spns[1], machine_name));
2071 
2072 	if ( (mods = ads_init_mods(ctx)) == NULL ) {
2073 		ret = ADS_ERROR(LDAP_NO_MEMORY);
2074 		goto out;
2075 	}
2076 
2077 	ret = ads_add_strlist(ctx,
2078 			      &mods,
2079 			      "servicePrincipalName",
2080 			      servicePrincipalName);
2081 	if (!ADS_ERR_OK(ret)) {
2082 		DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
2083 		goto out;
2084 	}
2085 
2086 	if ( (dn_string = ads_get_dn(ads, ctx, res)) == NULL ) {
2087 		ret = ADS_ERROR(LDAP_NO_MEMORY);
2088 		goto out;
2089 	}
2090 
2091 	ret = ads_gen_mod(ads, dn_string, mods);
2092 	if (!ADS_ERR_OK(ret)) {
2093 		DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
2094 		goto out;
2095 	}
2096 
2097  out:
2098 	TALLOC_FREE( ctx );
2099 	ads_msgfree(ads, res);
2100 	return ret;
2101 }
2102 
ads_get_acct_ctrl(ADS_STRUCT * ads,LDAPMessage * msg)2103 static uint32_t ads_get_acct_ctrl(ADS_STRUCT *ads,
2104 				  LDAPMessage *msg)
2105 {
2106 	uint32_t acct_ctrl = 0;
2107 	bool ok;
2108 
2109 	ok = ads_pull_uint32(ads, msg, "userAccountControl", &acct_ctrl);
2110 	if (!ok) {
2111 		return 0;
2112 	}
2113 
2114 	return acct_ctrl;
2115 }
2116 
ads_change_machine_acct(ADS_STRUCT * ads,LDAPMessage * msg,const struct berval * machine_pw_val)2117 static ADS_STATUS ads_change_machine_acct(ADS_STRUCT *ads,
2118 					  LDAPMessage *msg,
2119 					  const struct berval *machine_pw_val)
2120 {
2121 	ADS_MODLIST mods;
2122 	ADS_STATUS ret;
2123 	TALLOC_CTX *frame = talloc_stackframe();
2124 	uint32_t acct_control;
2125 	char *control_str = NULL;
2126 	const char *attrs[] = {
2127 		"objectSid",
2128 		NULL
2129 	};
2130 	LDAPMessage *res = NULL;
2131 	char *dn = NULL;
2132 
2133 	dn = ads_get_dn(ads, frame, msg);
2134 	if (dn == NULL) {
2135 		ret = ADS_ERROR(LDAP_NO_MEMORY);
2136 		goto done;
2137 	}
2138 
2139 	acct_control = ads_get_acct_ctrl(ads, msg);
2140 	if (acct_control == 0) {
2141 		ret = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2142 		goto done;
2143 	}
2144 
2145 	/*
2146 	 * Changing the password, disables the account. So we need to change the
2147 	 * userAccountControl flags to enable it again.
2148 	 */
2149 	mods = ads_init_mods(frame);
2150 	if (mods == NULL) {
2151 		ret = ADS_ERROR_LDAP(LDAP_NO_MEMORY);
2152 		goto done;
2153 	}
2154 
2155 	ads_mod_ber(frame, &mods, "unicodePwd", machine_pw_val);
2156 
2157 	ret = ads_gen_mod(ads, dn, mods);
2158 	if (!ADS_ERR_OK(ret)) {
2159 		goto done;
2160 	}
2161 	TALLOC_FREE(mods);
2162 
2163 	/*
2164 	 * To activate the account, we need to disable and enable it.
2165 	 */
2166 	acct_control |= UF_ACCOUNTDISABLE;
2167 
2168 	control_str = talloc_asprintf(frame, "%u", acct_control);
2169 	if (control_str == NULL) {
2170 		ret = ADS_ERROR(LDAP_NO_MEMORY);
2171 		goto done;
2172 	}
2173 
2174 	mods = ads_init_mods(frame);
2175 	if (mods == NULL) {
2176 		ret = ADS_ERROR_LDAP(LDAP_NO_MEMORY);
2177 		goto done;
2178 	}
2179 
2180 	ads_mod_str(frame, &mods, "userAccountControl", control_str);
2181 
2182 	ret = ads_gen_mod(ads, dn, mods);
2183 	if (!ADS_ERR_OK(ret)) {
2184 		goto done;
2185 	}
2186 	TALLOC_FREE(mods);
2187 	TALLOC_FREE(control_str);
2188 
2189 	/*
2190 	 * Enable the account again.
2191 	 */
2192 	acct_control &= ~UF_ACCOUNTDISABLE;
2193 
2194 	control_str = talloc_asprintf(frame, "%u", acct_control);
2195 	if (control_str == NULL) {
2196 		ret = ADS_ERROR(LDAP_NO_MEMORY);
2197 		goto done;
2198 	}
2199 
2200 	mods = ads_init_mods(frame);
2201 	if (mods == NULL) {
2202 		ret = ADS_ERROR_LDAP(LDAP_NO_MEMORY);
2203 		goto done;
2204 	}
2205 
2206 	ads_mod_str(frame, &mods, "userAccountControl", control_str);
2207 
2208 	ret = ads_gen_mod(ads, dn, mods);
2209 	if (!ADS_ERR_OK(ret)) {
2210 		goto done;
2211 	}
2212 	TALLOC_FREE(mods);
2213 	TALLOC_FREE(control_str);
2214 
2215 	ret = ads_search_dn(ads, &res, dn, attrs);
2216 	ads_msgfree(ads, res);
2217 
2218 done:
2219 	talloc_free(frame);
2220 
2221 	return ret;
2222 }
2223 
2224 /**
2225  * adds a machine account to the ADS server
2226  * @param ads An intialized ADS_STRUCT
2227  * @param machine_name - the NetBIOS machine name of this account.
2228  * @param account_type A number indicating the type of account to create
2229  * @param org_unit The LDAP path in which to place this account
2230  * @return 0 upon success, or non-zero otherwise
2231 **/
2232 
ads_create_machine_acct(ADS_STRUCT * ads,const char * machine_name,const char * machine_password,const char * org_unit,uint32_t etype_list,const char * dns_domain_name)2233 ADS_STATUS ads_create_machine_acct(ADS_STRUCT *ads,
2234 				   const char *machine_name,
2235 				   const char *machine_password,
2236 				   const char *org_unit,
2237 				   uint32_t etype_list,
2238 				   const char *dns_domain_name)
2239 {
2240 	ADS_STATUS ret;
2241 	char *samAccountName = NULL;
2242 	char *controlstr = NULL;
2243 	TALLOC_CTX *ctx = NULL;
2244 	ADS_MODLIST mods;
2245 	char *machine_escaped = NULL;
2246 	char *dns_hostname = NULL;
2247 	char *new_dn = NULL;
2248 	char *utf8_pw = NULL;
2249 	size_t utf8_pw_len = 0;
2250 	char *utf16_pw = NULL;
2251 	size_t utf16_pw_len = 0;
2252 	struct berval machine_pw_val;
2253 	bool ok;
2254 	const char **spn_array = NULL;
2255 	size_t num_spns = 0;
2256 	const char *spn_prefix[] = {
2257 		"HOST",
2258 		"RestrictedKrbHost",
2259 	};
2260 	size_t i;
2261 	LDAPMessage *res = NULL;
2262 	uint32_t acct_control = UF_WORKSTATION_TRUST_ACCOUNT;
2263 
2264 	ctx = talloc_init("ads_add_machine_acct");
2265 	if (ctx == NULL) {
2266 		return ADS_ERROR(LDAP_NO_MEMORY);
2267 	}
2268 
2269 	machine_escaped = escape_rdn_val_string_alloc(machine_name);
2270 	if (machine_escaped == NULL) {
2271 		ret = ADS_ERROR(LDAP_NO_MEMORY);
2272 		goto done;
2273 	}
2274 
2275 	utf8_pw = talloc_asprintf(ctx, "\"%s\"", machine_password);
2276 	if (utf8_pw == NULL) {
2277 		ret = ADS_ERROR(LDAP_NO_MEMORY);
2278 		goto done;
2279 	}
2280 	utf8_pw_len = strlen(utf8_pw);
2281 
2282 	ok = convert_string_talloc(ctx,
2283 				   CH_UTF8, CH_UTF16MUNGED,
2284 				   utf8_pw, utf8_pw_len,
2285 				   (void *)&utf16_pw, &utf16_pw_len);
2286 	if (!ok) {
2287 		ret = ADS_ERROR(LDAP_NO_MEMORY);
2288 		goto done;
2289 	}
2290 
2291 	machine_pw_val = (struct berval) {
2292 		.bv_val = utf16_pw,
2293 		.bv_len = utf16_pw_len,
2294 	};
2295 
2296 	/* Check if the machine account already exists. */
2297 	ret = ads_find_machine_acct(ads, &res, machine_escaped);
2298 	if (ADS_ERR_OK(ret)) {
2299 		/* Change the machine account password */
2300 		ret = ads_change_machine_acct(ads, res, &machine_pw_val);
2301 		ads_msgfree(ads, res);
2302 
2303 		goto done;
2304 	}
2305 	ads_msgfree(ads, res);
2306 
2307 	new_dn = talloc_asprintf(ctx, "cn=%s,%s", machine_escaped, org_unit);
2308 	if (new_dn == NULL) {
2309 		ret = ADS_ERROR(LDAP_NO_MEMORY);
2310 		goto done;
2311 	}
2312 
2313 	/* Create machine account */
2314 
2315 	samAccountName = talloc_asprintf(ctx, "%s$", machine_name);
2316 	if (samAccountName == NULL) {
2317 		ret = ADS_ERROR(LDAP_NO_MEMORY);
2318 		goto done;
2319 	}
2320 
2321 	dns_hostname = talloc_asprintf(ctx,
2322 				       "%s.%s",
2323 				       machine_name,
2324 				       dns_domain_name);
2325 	if (dns_hostname == NULL) {
2326 		ret = ADS_ERROR(LDAP_NO_MEMORY);
2327 		goto done;
2328 	}
2329 
2330 	/* Add dns_hostname SPNs */
2331 	for (i = 0; i < ARRAY_SIZE(spn_prefix); i++) {
2332 		char *spn = talloc_asprintf(ctx,
2333 					    "%s/%s",
2334 					    spn_prefix[i],
2335 					    dns_hostname);
2336 		if (spn == NULL) {
2337 			ret = ADS_ERROR(LDAP_NO_MEMORY);
2338 			goto done;
2339 		}
2340 
2341 		ok = add_string_to_array(spn_array,
2342 					 spn,
2343 					 &spn_array,
2344 					 &num_spns);
2345 		if (!ok) {
2346 			ret = ADS_ERROR(LDAP_NO_MEMORY);
2347 			goto done;
2348 		}
2349 	}
2350 
2351 	/* Add machine_name SPNs */
2352 	for (i = 0; i < ARRAY_SIZE(spn_prefix); i++) {
2353 		char *spn = talloc_asprintf(ctx,
2354 					    "%s/%s",
2355 					    spn_prefix[i],
2356 					    machine_name);
2357 		if (spn == NULL) {
2358 			ret = ADS_ERROR(LDAP_NO_MEMORY);
2359 			goto done;
2360 		}
2361 
2362 		ok = add_string_to_array(spn_array,
2363 					 spn,
2364 					 &spn_array,
2365 					 &num_spns);
2366 		if (!ok) {
2367 			ret = ADS_ERROR(LDAP_NO_MEMORY);
2368 			goto done;
2369 		}
2370 	}
2371 
2372 	/* Make sure to NULL terminate the array */
2373 	spn_array = talloc_realloc(ctx, spn_array, const char *, num_spns + 1);
2374 	if (spn_array == NULL) {
2375 		ret = ADS_ERROR(LDAP_NO_MEMORY);
2376 		goto done;
2377 	}
2378 	spn_array[num_spns] = NULL;
2379 
2380 	controlstr = talloc_asprintf(ctx, "%u", acct_control);
2381 	if (controlstr == NULL) {
2382 		ret = ADS_ERROR(LDAP_NO_MEMORY);
2383 		goto done;
2384 	}
2385 
2386 	mods = ads_init_mods(ctx);
2387 	if (mods == NULL) {
2388 		ret = ADS_ERROR(LDAP_NO_MEMORY);
2389 		goto done;
2390 	}
2391 
2392 	ads_mod_str(ctx, &mods, "objectClass", "Computer");
2393 	ads_mod_str(ctx, &mods, "SamAccountName", samAccountName);
2394 	ads_mod_str(ctx, &mods, "userAccountControl", controlstr);
2395 	ads_mod_str(ctx, &mods, "DnsHostName", dns_hostname);
2396 	ads_mod_strlist(ctx, &mods, "ServicePrincipalName", spn_array);
2397 	ads_mod_ber(ctx, &mods, "unicodePwd", &machine_pw_val);
2398 
2399 	ret = ads_gen_add(ads, new_dn, mods);
2400 
2401 done:
2402 	SAFE_FREE(machine_escaped);
2403 	talloc_destroy(ctx);
2404 
2405 	return ret;
2406 }
2407 
2408 /**
2409  * move a machine account to another OU on the ADS server
2410  * @param ads - An intialized ADS_STRUCT
2411  * @param machine_name - the NetBIOS machine name of this account.
2412  * @param org_unit - The LDAP path in which to place this account
2413  * @param moved - whether we moved the machine account (optional)
2414  * @return 0 upon success, or non-zero otherwise
2415 **/
2416 
ads_move_machine_acct(ADS_STRUCT * ads,const char * machine_name,const char * org_unit,bool * moved)2417 ADS_STATUS ads_move_machine_acct(ADS_STRUCT *ads, const char *machine_name,
2418                                  const char *org_unit, bool *moved)
2419 {
2420 	ADS_STATUS rc;
2421 	int ldap_status;
2422 	LDAPMessage *res = NULL;
2423 	char *filter = NULL;
2424 	char *computer_dn = NULL;
2425 	char *parent_dn;
2426 	char *computer_rdn = NULL;
2427 	bool need_move = False;
2428 
2429 	if (asprintf(&filter, "(samAccountName=%s$)", machine_name) == -1) {
2430 		rc = ADS_ERROR(LDAP_NO_MEMORY);
2431 		goto done;
2432 	}
2433 
2434 	/* Find pre-existing machine */
2435 	rc = ads_search(ads, &res, filter, NULL);
2436 	if (!ADS_ERR_OK(rc)) {
2437 		goto done;
2438 	}
2439 
2440 	computer_dn = ads_get_dn(ads, talloc_tos(), res);
2441 	if (!computer_dn) {
2442 		rc = ADS_ERROR(LDAP_NO_MEMORY);
2443 		goto done;
2444 	}
2445 
2446 	parent_dn = ads_parent_dn(computer_dn);
2447 	if (strequal(parent_dn, org_unit)) {
2448 		goto done;
2449 	}
2450 
2451 	need_move = True;
2452 
2453 	if (asprintf(&computer_rdn, "CN=%s", machine_name) == -1) {
2454 		rc = ADS_ERROR(LDAP_NO_MEMORY);
2455 		goto done;
2456 	}
2457 
2458 	ldap_status = ldap_rename_s(ads->ldap.ld, computer_dn, computer_rdn,
2459 				    org_unit, 1, NULL, NULL);
2460 	rc = ADS_ERROR(ldap_status);
2461 
2462 done:
2463 	ads_msgfree(ads, res);
2464 	SAFE_FREE(filter);
2465 	TALLOC_FREE(computer_dn);
2466 	SAFE_FREE(computer_rdn);
2467 
2468 	if (!ADS_ERR_OK(rc)) {
2469 		need_move = False;
2470 	}
2471 
2472 	if (moved) {
2473 		*moved = need_move;
2474 	}
2475 
2476 	return rc;
2477 }
2478 
2479 /*
2480   dump a binary result from ldap
2481 */
dump_binary(ADS_STRUCT * ads,const char * field,struct berval ** values)2482 static void dump_binary(ADS_STRUCT *ads, const char *field, struct berval **values)
2483 {
2484 	size_t i;
2485 	for (i=0; values[i]; i++) {
2486 		ber_len_t j;
2487 		printf("%s: ", field);
2488 		for (j=0; j<values[i]->bv_len; j++) {
2489 			printf("%02X", (unsigned char)values[i]->bv_val[j]);
2490 		}
2491 		printf("\n");
2492 	}
2493 }
2494 
dump_guid(ADS_STRUCT * ads,const char * field,struct berval ** values)2495 static void dump_guid(ADS_STRUCT *ads, const char *field, struct berval **values)
2496 {
2497 	int i;
2498 	for (i=0; values[i]; i++) {
2499 		NTSTATUS status;
2500 		DATA_BLOB in = data_blob_const(values[i]->bv_val, values[i]->bv_len);
2501 		struct GUID guid;
2502 
2503 		status = GUID_from_ndr_blob(&in, &guid);
2504 		if (NT_STATUS_IS_OK(status)) {
2505 			printf("%s: %s\n", field, GUID_string(talloc_tos(), &guid));
2506 		} else {
2507 			printf("%s: INVALID GUID\n", field);
2508 		}
2509 	}
2510 }
2511 
2512 /*
2513   dump a sid result from ldap
2514 */
dump_sid(ADS_STRUCT * ads,const char * field,struct berval ** values)2515 static void dump_sid(ADS_STRUCT *ads, const char *field, struct berval **values)
2516 {
2517 	int i;
2518 	for (i=0; values[i]; i++) {
2519 		ssize_t ret;
2520 		struct dom_sid sid;
2521 		struct dom_sid_buf tmp;
2522 		ret = sid_parse((const uint8_t *)values[i]->bv_val,
2523 				values[i]->bv_len, &sid);
2524 		if (ret == -1) {
2525 			return;
2526 		}
2527 		printf("%s: %s\n", field, dom_sid_str_buf(&sid, &tmp));
2528 	}
2529 }
2530 
2531 /*
2532   dump ntSecurityDescriptor
2533 */
dump_sd(ADS_STRUCT * ads,const char * filed,struct berval ** values)2534 static void dump_sd(ADS_STRUCT *ads, const char *filed, struct berval **values)
2535 {
2536 	TALLOC_CTX *frame = talloc_stackframe();
2537 	struct security_descriptor *psd;
2538 	NTSTATUS status;
2539 
2540 	status = unmarshall_sec_desc(talloc_tos(), (uint8_t *)values[0]->bv_val,
2541 				     values[0]->bv_len, &psd);
2542 	if (!NT_STATUS_IS_OK(status)) {
2543 		DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
2544 			  nt_errstr(status)));
2545 		TALLOC_FREE(frame);
2546 		return;
2547 	}
2548 
2549 	if (psd) {
2550 		ads_disp_sd(ads, talloc_tos(), psd);
2551 	}
2552 
2553 	TALLOC_FREE(frame);
2554 }
2555 
2556 /*
2557   dump a string result from ldap
2558 */
dump_string(const char * field,char ** values)2559 static void dump_string(const char *field, char **values)
2560 {
2561 	int i;
2562 	for (i=0; values[i]; i++) {
2563 		printf("%s: %s\n", field, values[i]);
2564 	}
2565 }
2566 
2567 /*
2568   dump a field from LDAP on stdout
2569   used for debugging
2570 */
2571 
ads_dump_field(ADS_STRUCT * ads,char * field,void ** values,void * data_area)2572 static bool ads_dump_field(ADS_STRUCT *ads, char *field, void **values, void *data_area)
2573 {
2574 	const struct {
2575 		const char *name;
2576 		bool string;
2577 		void (*handler)(ADS_STRUCT *, const char *, struct berval **);
2578 	} handlers[] = {
2579 		{"objectGUID", False, dump_guid},
2580 		{"netbootGUID", False, dump_guid},
2581 		{"nTSecurityDescriptor", False, dump_sd},
2582 		{"dnsRecord", False, dump_binary},
2583 		{"objectSid", False, dump_sid},
2584 		{"tokenGroups", False, dump_sid},
2585 		{"tokenGroupsNoGCAcceptable", False, dump_sid},
2586 		{"tokengroupsGlobalandUniversal", False, dump_sid},
2587 		{"mS-DS-CreatorSID", False, dump_sid},
2588 		{"msExchMailboxGuid", False, dump_guid},
2589 		{NULL, True, NULL}
2590 	};
2591 	int i;
2592 
2593 	if (!field) { /* must be end of an entry */
2594 		printf("\n");
2595 		return False;
2596 	}
2597 
2598 	for (i=0; handlers[i].name; i++) {
2599 		if (strcasecmp_m(handlers[i].name, field) == 0) {
2600 			if (!values) /* first time, indicate string or not */
2601 				return handlers[i].string;
2602 			handlers[i].handler(ads, field, (struct berval **) values);
2603 			break;
2604 		}
2605 	}
2606 	if (!handlers[i].name) {
2607 		if (!values) /* first time, indicate string conversion */
2608 			return True;
2609 		dump_string(field, (char **)values);
2610 	}
2611 	return False;
2612 }
2613 
2614 /**
2615  * Dump a result from LDAP on stdout
2616  *  used for debugging
2617  * @param ads connection to ads server
2618  * @param res Results to dump
2619  **/
2620 
ads_dump(ADS_STRUCT * ads,LDAPMessage * res)2621  void ads_dump(ADS_STRUCT *ads, LDAPMessage *res)
2622 {
2623 	ads_process_results(ads, res, ads_dump_field, NULL);
2624 }
2625 
2626 /**
2627  * Walk through results, calling a function for each entry found.
2628  *  The function receives a field name, a berval * array of values,
2629  *  and a data area passed through from the start.  The function is
2630  *  called once with null for field and values at the end of each
2631  *  entry.
2632  * @param ads connection to ads server
2633  * @param res Results to process
2634  * @param fn Function for processing each result
2635  * @param data_area user-defined area to pass to function
2636  **/
ads_process_results(ADS_STRUCT * ads,LDAPMessage * res,bool (* fn)(ADS_STRUCT *,char *,void **,void *),void * data_area)2637  void ads_process_results(ADS_STRUCT *ads, LDAPMessage *res,
2638 			  bool (*fn)(ADS_STRUCT *, char *, void **, void *),
2639 			  void *data_area)
2640 {
2641 	LDAPMessage *msg;
2642 	TALLOC_CTX *ctx;
2643 	size_t converted_size;
2644 
2645 	if (!(ctx = talloc_init("ads_process_results")))
2646 		return;
2647 
2648 	for (msg = ads_first_entry(ads, res); msg;
2649 	     msg = ads_next_entry(ads, msg)) {
2650 		char *utf8_field;
2651 		BerElement *b;
2652 
2653 		for (utf8_field=ldap_first_attribute(ads->ldap.ld,
2654 						     (LDAPMessage *)msg,&b);
2655 		     utf8_field;
2656 		     utf8_field=ldap_next_attribute(ads->ldap.ld,
2657 						    (LDAPMessage *)msg,b)) {
2658 			struct berval **ber_vals;
2659 			char **str_vals;
2660 			char **utf8_vals;
2661 			char *field;
2662 			bool string;
2663 
2664 			if (!pull_utf8_talloc(ctx, &field, utf8_field,
2665 					      &converted_size))
2666 			{
2667 				DEBUG(0,("ads_process_results: "
2668 					 "pull_utf8_talloc failed: %s",
2669 					 strerror(errno)));
2670 			}
2671 
2672 			string = fn(ads, field, NULL, data_area);
2673 
2674 			if (string) {
2675 				const char **p;
2676 
2677 				utf8_vals = ldap_get_values(ads->ldap.ld,
2678 					       	 (LDAPMessage *)msg, field);
2679 				p = discard_const_p(const char *, utf8_vals);
2680 				str_vals = ads_pull_strvals(ctx, p);
2681 				fn(ads, field, (void **) str_vals, data_area);
2682 				ldap_value_free(utf8_vals);
2683 			} else {
2684 				ber_vals = ldap_get_values_len(ads->ldap.ld,
2685 						 (LDAPMessage *)msg, field);
2686 				fn(ads, field, (void **) ber_vals, data_area);
2687 
2688 				ldap_value_free_len(ber_vals);
2689 			}
2690 			ldap_memfree(utf8_field);
2691 		}
2692 		ber_free(b, 0);
2693 		talloc_free_children(ctx);
2694 		fn(ads, NULL, NULL, data_area); /* completed an entry */
2695 
2696 	}
2697 	talloc_destroy(ctx);
2698 }
2699 
2700 /**
2701  * count how many replies are in a LDAPMessage
2702  * @param ads connection to ads server
2703  * @param res Results to count
2704  * @return number of replies
2705  **/
ads_count_replies(ADS_STRUCT * ads,void * res)2706 int ads_count_replies(ADS_STRUCT *ads, void *res)
2707 {
2708 	return ldap_count_entries(ads->ldap.ld, (LDAPMessage *)res);
2709 }
2710 
2711 /**
2712  * pull the first entry from a ADS result
2713  * @param ads connection to ads server
2714  * @param res Results of search
2715  * @return first entry from result
2716  **/
ads_first_entry(ADS_STRUCT * ads,LDAPMessage * res)2717  LDAPMessage *ads_first_entry(ADS_STRUCT *ads, LDAPMessage *res)
2718 {
2719 	return ldap_first_entry(ads->ldap.ld, res);
2720 }
2721 
2722 /**
2723  * pull the next entry from a ADS result
2724  * @param ads connection to ads server
2725  * @param res Results of search
2726  * @return next entry from result
2727  **/
ads_next_entry(ADS_STRUCT * ads,LDAPMessage * res)2728  LDAPMessage *ads_next_entry(ADS_STRUCT *ads, LDAPMessage *res)
2729 {
2730 	return ldap_next_entry(ads->ldap.ld, res);
2731 }
2732 
2733 /**
2734  * pull the first message from a ADS result
2735  * @param ads connection to ads server
2736  * @param res Results of search
2737  * @return first message from result
2738  **/
ads_first_message(ADS_STRUCT * ads,LDAPMessage * res)2739  LDAPMessage *ads_first_message(ADS_STRUCT *ads, LDAPMessage *res)
2740 {
2741 	return ldap_first_message(ads->ldap.ld, res);
2742 }
2743 
2744 /**
2745  * pull the next message from a ADS result
2746  * @param ads connection to ads server
2747  * @param res Results of search
2748  * @return next message from result
2749  **/
ads_next_message(ADS_STRUCT * ads,LDAPMessage * res)2750  LDAPMessage *ads_next_message(ADS_STRUCT *ads, LDAPMessage *res)
2751 {
2752 	return ldap_next_message(ads->ldap.ld, res);
2753 }
2754 
2755 /**
2756  * pull a single string from a ADS result
2757  * @param ads connection to ads server
2758  * @param mem_ctx TALLOC_CTX to use for allocating result string
2759  * @param msg Results of search
2760  * @param field Attribute to retrieve
2761  * @return Result string in talloc context
2762  **/
ads_pull_string(ADS_STRUCT * ads,TALLOC_CTX * mem_ctx,LDAPMessage * msg,const char * field)2763  char *ads_pull_string(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, LDAPMessage *msg,
2764 		       const char *field)
2765 {
2766 	char **values;
2767 	char *ret = NULL;
2768 	char *ux_string;
2769 	size_t converted_size;
2770 
2771 	values = ldap_get_values(ads->ldap.ld, msg, field);
2772 	if (!values)
2773 		return NULL;
2774 
2775 	if (values[0] && pull_utf8_talloc(mem_ctx, &ux_string, values[0],
2776 					  &converted_size))
2777 	{
2778 		ret = ux_string;
2779 	}
2780 	ldap_value_free(values);
2781 	return ret;
2782 }
2783 
2784 /**
2785  * pull an array of strings from a ADS result
2786  * @param ads connection to ads server
2787  * @param mem_ctx TALLOC_CTX to use for allocating result string
2788  * @param msg Results of search
2789  * @param field Attribute to retrieve
2790  * @return Result strings in talloc context
2791  **/
ads_pull_strings(ADS_STRUCT * ads,TALLOC_CTX * mem_ctx,LDAPMessage * msg,const char * field,size_t * num_values)2792  char **ads_pull_strings(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2793 			 LDAPMessage *msg, const char *field,
2794 			 size_t *num_values)
2795 {
2796 	char **values;
2797 	char **ret = NULL;
2798 	size_t i, converted_size;
2799 
2800 	values = ldap_get_values(ads->ldap.ld, msg, field);
2801 	if (!values)
2802 		return NULL;
2803 
2804 	*num_values = ldap_count_values(values);
2805 
2806 	ret = talloc_array(mem_ctx, char *, *num_values + 1);
2807 	if (!ret) {
2808 		ldap_value_free(values);
2809 		return NULL;
2810 	}
2811 
2812 	for (i=0;i<*num_values;i++) {
2813 		if (!pull_utf8_talloc(mem_ctx, &ret[i], values[i],
2814 				      &converted_size))
2815 		{
2816 			ldap_value_free(values);
2817 			return NULL;
2818 		}
2819 	}
2820 	ret[i] = NULL;
2821 
2822 	ldap_value_free(values);
2823 	return ret;
2824 }
2825 
2826 /**
2827  * pull an array of strings from a ADS result
2828  *  (handle large multivalue attributes with range retrieval)
2829  * @param ads connection to ads server
2830  * @param mem_ctx TALLOC_CTX to use for allocating result string
2831  * @param msg Results of search
2832  * @param field Attribute to retrieve
2833  * @param current_strings strings returned by a previous call to this function
2834  * @param next_attribute The next query should ask for this attribute
2835  * @param num_values How many values did we get this time?
2836  * @param more_values Are there more values to get?
2837  * @return Result strings in talloc context
2838  **/
ads_pull_strings_range(ADS_STRUCT * ads,TALLOC_CTX * mem_ctx,LDAPMessage * msg,const char * field,char ** current_strings,const char ** next_attribute,size_t * num_strings,bool * more_strings)2839  char **ads_pull_strings_range(ADS_STRUCT *ads,
2840 			       TALLOC_CTX *mem_ctx,
2841 			       LDAPMessage *msg, const char *field,
2842 			       char **current_strings,
2843 			       const char **next_attribute,
2844 			       size_t *num_strings,
2845 			       bool *more_strings)
2846 {
2847 	char *attr;
2848 	char *expected_range_attrib, *range_attr;
2849 	BerElement *ptr = NULL;
2850 	char **strings;
2851 	char **new_strings;
2852 	size_t num_new_strings;
2853 	unsigned long int range_start;
2854 	unsigned long int range_end;
2855 
2856 	/* we might have been given the whole lot anyway */
2857 	if ((strings = ads_pull_strings(ads, mem_ctx, msg, field, num_strings))) {
2858 		*more_strings = False;
2859 		return strings;
2860 	}
2861 
2862 	expected_range_attrib = talloc_asprintf(mem_ctx, "%s;Range=", field);
2863 
2864 	/* look for Range result */
2865 	for (attr = ldap_first_attribute(ads->ldap.ld, (LDAPMessage *)msg, &ptr);
2866 	     attr;
2867 	     attr = ldap_next_attribute(ads->ldap.ld, (LDAPMessage *)msg, ptr)) {
2868 		/* we ignore the fact that this is utf8, as all attributes are ascii... */
2869 		if (strnequal(attr, expected_range_attrib, strlen(expected_range_attrib))) {
2870 			range_attr = attr;
2871 			break;
2872 		}
2873 		ldap_memfree(attr);
2874 	}
2875 	if (!attr) {
2876 		ber_free(ptr, 0);
2877 		/* nothing here - this field is just empty */
2878 		*more_strings = False;
2879 		return NULL;
2880 	}
2881 
2882 	if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-%lu",
2883 		   &range_start, &range_end) == 2) {
2884 		*more_strings = True;
2885 	} else {
2886 		if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-*",
2887 			   &range_start) == 1) {
2888 			*more_strings = False;
2889 		} else {
2890 			DEBUG(1, ("ads_pull_strings_range:  Cannot parse Range attriubte (%s)\n",
2891 				  range_attr));
2892 			ldap_memfree(range_attr);
2893 			*more_strings = False;
2894 			return NULL;
2895 		}
2896 	}
2897 
2898 	if ((*num_strings) != range_start) {
2899 		DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) doesn't start at %u, but at %lu"
2900 			  " - aborting range retreival\n",
2901 			  range_attr, (unsigned int)(*num_strings) + 1, range_start));
2902 		ldap_memfree(range_attr);
2903 		*more_strings = False;
2904 		return NULL;
2905 	}
2906 
2907 	new_strings = ads_pull_strings(ads, mem_ctx, msg, range_attr, &num_new_strings);
2908 
2909 	if (*more_strings && ((*num_strings + num_new_strings) != (range_end + 1))) {
2910 		DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) tells us we have %lu "
2911 			  "strings in this bunch, but we only got %lu - aborting range retreival\n",
2912 			  range_attr, (unsigned long int)range_end - range_start + 1,
2913 			  (unsigned long int)num_new_strings));
2914 		ldap_memfree(range_attr);
2915 		*more_strings = False;
2916 		return NULL;
2917 	}
2918 
2919 	strings = talloc_realloc(mem_ctx, current_strings, char *,
2920 				 *num_strings + num_new_strings);
2921 
2922 	if (strings == NULL) {
2923 		ldap_memfree(range_attr);
2924 		*more_strings = False;
2925 		return NULL;
2926 	}
2927 
2928 	if (new_strings && num_new_strings) {
2929 		memcpy(&strings[*num_strings], new_strings,
2930 		       sizeof(*new_strings) * num_new_strings);
2931 	}
2932 
2933 	(*num_strings) += num_new_strings;
2934 
2935 	if (*more_strings) {
2936 		*next_attribute = talloc_asprintf(mem_ctx,
2937 						  "%s;range=%d-*",
2938 						  field,
2939 						  (int)*num_strings);
2940 
2941 		if (!*next_attribute) {
2942 			DEBUG(1, ("talloc_asprintf for next attribute failed!\n"));
2943 			ldap_memfree(range_attr);
2944 			*more_strings = False;
2945 			return NULL;
2946 		}
2947 	}
2948 
2949 	ldap_memfree(range_attr);
2950 
2951 	return strings;
2952 }
2953 
2954 /**
2955  * pull a single uint32_t from a ADS result
2956  * @param ads connection to ads server
2957  * @param msg Results of search
2958  * @param field Attribute to retrieve
2959  * @param v Pointer to int to store result
2960  * @return boolean inidicating success
2961 */
ads_pull_uint32(ADS_STRUCT * ads,LDAPMessage * msg,const char * field,uint32_t * v)2962  bool ads_pull_uint32(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
2963 		      uint32_t *v)
2964 {
2965 	char **values;
2966 
2967 	values = ldap_get_values(ads->ldap.ld, msg, field);
2968 	if (!values)
2969 		return False;
2970 	if (!values[0]) {
2971 		ldap_value_free(values);
2972 		return False;
2973 	}
2974 
2975 	*v = atoi(values[0]);
2976 	ldap_value_free(values);
2977 	return True;
2978 }
2979 
2980 /**
2981  * pull a single objectGUID from an ADS result
2982  * @param ads connection to ADS server
2983  * @param msg results of search
2984  * @param guid 37-byte area to receive text guid
2985  * @return boolean indicating success
2986  **/
ads_pull_guid(ADS_STRUCT * ads,LDAPMessage * msg,struct GUID * guid)2987  bool ads_pull_guid(ADS_STRUCT *ads, LDAPMessage *msg, struct GUID *guid)
2988 {
2989 	DATA_BLOB blob;
2990 	NTSTATUS status;
2991 
2992 	if (!smbldap_talloc_single_blob(talloc_tos(), ads->ldap.ld, msg, "objectGUID",
2993 					&blob)) {
2994 		return false;
2995 	}
2996 
2997 	status = GUID_from_ndr_blob(&blob, guid);
2998 	talloc_free(blob.data);
2999 	return NT_STATUS_IS_OK(status);
3000 }
3001 
3002 
3003 /**
3004  * pull a single struct dom_sid from a ADS result
3005  * @param ads connection to ads server
3006  * @param msg Results of search
3007  * @param field Attribute to retrieve
3008  * @param sid Pointer to sid to store result
3009  * @return boolean inidicating success
3010 */
ads_pull_sid(ADS_STRUCT * ads,LDAPMessage * msg,const char * field,struct dom_sid * sid)3011  bool ads_pull_sid(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
3012 		   struct dom_sid *sid)
3013 {
3014 	return smbldap_pull_sid(ads->ldap.ld, msg, field, sid);
3015 }
3016 
3017 /**
3018  * pull an array of struct dom_sids from a ADS result
3019  * @param ads connection to ads server
3020  * @param mem_ctx TALLOC_CTX for allocating sid array
3021  * @param msg Results of search
3022  * @param field Attribute to retrieve
3023  * @param sids pointer to sid array to allocate
3024  * @return the count of SIDs pulled
3025  **/
ads_pull_sids(ADS_STRUCT * ads,TALLOC_CTX * mem_ctx,LDAPMessage * msg,const char * field,struct dom_sid ** sids)3026  int ads_pull_sids(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
3027 		   LDAPMessage *msg, const char *field, struct dom_sid **sids)
3028 {
3029 	struct berval **values;
3030 	int count, i;
3031 
3032 	values = ldap_get_values_len(ads->ldap.ld, msg, field);
3033 
3034 	if (!values)
3035 		return 0;
3036 
3037 	for (i=0; values[i]; i++)
3038 		/* nop */ ;
3039 
3040 	if (i) {
3041 		(*sids) = talloc_array(mem_ctx, struct dom_sid, i);
3042 		if (!(*sids)) {
3043 			ldap_value_free_len(values);
3044 			return 0;
3045 		}
3046 	} else {
3047 		(*sids) = NULL;
3048 	}
3049 
3050 	count = 0;
3051 	for (i=0; values[i]; i++) {
3052 		ssize_t ret;
3053 		ret = sid_parse((const uint8_t *)values[i]->bv_val,
3054 				values[i]->bv_len, &(*sids)[count]);
3055 		if (ret != -1) {
3056 			struct dom_sid_buf buf;
3057 			DBG_DEBUG("pulling SID: %s\n",
3058 				  dom_sid_str_buf(&(*sids)[count], &buf));
3059 			count++;
3060 		}
3061 	}
3062 
3063 	ldap_value_free_len(values);
3064 	return count;
3065 }
3066 
3067 /**
3068  * pull a struct security_descriptor from a ADS result
3069  * @param ads connection to ads server
3070  * @param mem_ctx TALLOC_CTX for allocating sid array
3071  * @param msg Results of search
3072  * @param field Attribute to retrieve
3073  * @param sd Pointer to *struct security_descriptor to store result (talloc()ed)
3074  * @return boolean inidicating success
3075 */
ads_pull_sd(ADS_STRUCT * ads,TALLOC_CTX * mem_ctx,LDAPMessage * msg,const char * field,struct security_descriptor ** sd)3076  bool ads_pull_sd(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
3077 		  LDAPMessage *msg, const char *field,
3078 		  struct security_descriptor **sd)
3079 {
3080 	struct berval **values;
3081 	bool ret = true;
3082 
3083 	values = ldap_get_values_len(ads->ldap.ld, msg, field);
3084 
3085 	if (!values) return false;
3086 
3087 	if (values[0]) {
3088 		NTSTATUS status;
3089 		status = unmarshall_sec_desc(mem_ctx,
3090 					     (uint8_t *)values[0]->bv_val,
3091 					     values[0]->bv_len, sd);
3092 		if (!NT_STATUS_IS_OK(status)) {
3093 			DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
3094 				  nt_errstr(status)));
3095 			ret = false;
3096 		}
3097 	}
3098 
3099 	ldap_value_free_len(values);
3100 	return ret;
3101 }
3102 
3103 /*
3104  * in order to support usernames longer than 21 characters we need to
3105  * use both the sAMAccountName and the userPrincipalName attributes
3106  * It seems that not all users have the userPrincipalName attribute set
3107  *
3108  * @param ads connection to ads server
3109  * @param mem_ctx TALLOC_CTX for allocating sid array
3110  * @param msg Results of search
3111  * @return the username
3112  */
ads_pull_username(ADS_STRUCT * ads,TALLOC_CTX * mem_ctx,LDAPMessage * msg)3113  char *ads_pull_username(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
3114 			 LDAPMessage *msg)
3115 {
3116 #if 0	/* JERRY */
3117 	char *ret, *p;
3118 
3119 	/* lookup_name() only works on the sAMAccountName to
3120 	   returning the username portion of userPrincipalName
3121 	   breaks winbindd_getpwnam() */
3122 
3123 	ret = ads_pull_string(ads, mem_ctx, msg, "userPrincipalName");
3124 	if (ret && (p = strchr_m(ret, '@'))) {
3125 		*p = 0;
3126 		return ret;
3127 	}
3128 #endif
3129 	return ads_pull_string(ads, mem_ctx, msg, "sAMAccountName");
3130 }
3131 
3132 
3133 /**
3134  * find the update serial number - this is the core of the ldap cache
3135  * @param ads connection to ads server
3136  * @param ads connection to ADS server
3137  * @param usn Pointer to retrieved update serial number
3138  * @return status of search
3139  **/
ads_USN(ADS_STRUCT * ads,uint32_t * usn)3140 ADS_STATUS ads_USN(ADS_STRUCT *ads, uint32_t *usn)
3141 {
3142 	const char *attrs[] = {"highestCommittedUSN", NULL};
3143 	ADS_STATUS status;
3144 	LDAPMessage *res;
3145 
3146 	status = ads_do_search_retry(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
3147 	if (!ADS_ERR_OK(status))
3148 		return status;
3149 
3150 	if (ads_count_replies(ads, res) != 1) {
3151 		ads_msgfree(ads, res);
3152 		return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3153 	}
3154 
3155 	if (!ads_pull_uint32(ads, res, "highestCommittedUSN", usn)) {
3156 		ads_msgfree(ads, res);
3157 		return ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
3158 	}
3159 
3160 	ads_msgfree(ads, res);
3161 	return ADS_SUCCESS;
3162 }
3163 
3164 /* parse a ADS timestring - typical string is
3165    '20020917091222.0Z0' which means 09:12.22 17th September
3166    2002, timezone 0 */
ads_parse_time(const char * str)3167 static time_t ads_parse_time(const char *str)
3168 {
3169 	struct tm tm;
3170 
3171 	ZERO_STRUCT(tm);
3172 
3173 	if (sscanf(str, "%4d%2d%2d%2d%2d%2d",
3174 		   &tm.tm_year, &tm.tm_mon, &tm.tm_mday,
3175 		   &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) {
3176 		return 0;
3177 	}
3178 	tm.tm_year -= 1900;
3179 	tm.tm_mon -= 1;
3180 
3181 	return timegm(&tm);
3182 }
3183 
3184 /********************************************************************
3185 ********************************************************************/
3186 
ads_current_time(ADS_STRUCT * ads)3187 ADS_STATUS ads_current_time(ADS_STRUCT *ads)
3188 {
3189 	const char *attrs[] = {"currentTime", NULL};
3190 	ADS_STATUS status;
3191 	LDAPMessage *res;
3192 	char *timestr;
3193 	TALLOC_CTX *ctx;
3194 	ADS_STRUCT *ads_s = ads;
3195 
3196 	if (!(ctx = talloc_init("ads_current_time"))) {
3197 		return ADS_ERROR(LDAP_NO_MEMORY);
3198 	}
3199 
3200         /* establish a new ldap tcp session if necessary */
3201 
3202 	if ( !ads->ldap.ld ) {
3203 		if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup,
3204 			ads->server.ldap_server, ADS_SASL_PLAIN )) == NULL )
3205 		{
3206 			status = ADS_ERROR(LDAP_NO_MEMORY);
3207 			goto done;
3208 		}
3209 		ads_s->auth.flags = ADS_AUTH_ANON_BIND;
3210 		status = ads_connect( ads_s );
3211 		if ( !ADS_ERR_OK(status))
3212 			goto done;
3213 	}
3214 
3215 	status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
3216 	if (!ADS_ERR_OK(status)) {
3217 		goto done;
3218 	}
3219 
3220 	timestr = ads_pull_string(ads_s, ctx, res, "currentTime");
3221 	if (!timestr) {
3222 		ads_msgfree(ads_s, res);
3223 		status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3224 		goto done;
3225 	}
3226 
3227 	/* but save the time and offset in the original ADS_STRUCT */
3228 
3229 	ads->config.current_time = ads_parse_time(timestr);
3230 
3231 	if (ads->config.current_time != 0) {
3232 		ads->auth.time_offset = ads->config.current_time - time(NULL);
3233 		DEBUG(4,("KDC time offset is %d seconds\n", ads->auth.time_offset));
3234 	}
3235 
3236 	ads_msgfree(ads, res);
3237 
3238 	status = ADS_SUCCESS;
3239 
3240 done:
3241 	/* free any temporary ads connections */
3242 	if ( ads_s != ads ) {
3243 		ads_destroy( &ads_s );
3244 	}
3245 	talloc_destroy(ctx);
3246 
3247 	return status;
3248 }
3249 
3250 /********************************************************************
3251 ********************************************************************/
3252 
ads_domain_func_level(ADS_STRUCT * ads,uint32_t * val)3253 ADS_STATUS ads_domain_func_level(ADS_STRUCT *ads, uint32_t *val)
3254 {
3255 	const char *attrs[] = {"domainFunctionality", NULL};
3256 	ADS_STATUS status;
3257 	LDAPMessage *res;
3258 	ADS_STRUCT *ads_s = ads;
3259 
3260 	*val = DS_DOMAIN_FUNCTION_2000;
3261 
3262         /* establish a new ldap tcp session if necessary */
3263 
3264 	if ( !ads->ldap.ld ) {
3265 		if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup,
3266 			ads->server.ldap_server, ADS_SASL_PLAIN )) == NULL )
3267 		{
3268 			status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
3269 			goto done;
3270 		}
3271 		ads_s->auth.flags = ADS_AUTH_ANON_BIND;
3272 		status = ads_connect( ads_s );
3273 		if ( !ADS_ERR_OK(status))
3274 			goto done;
3275 	}
3276 
3277 	/* If the attribute does not exist assume it is a Windows 2000
3278 	   functional domain */
3279 
3280 	status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
3281 	if (!ADS_ERR_OK(status)) {
3282 		if ( status.err.rc == LDAP_NO_SUCH_ATTRIBUTE ) {
3283 			status = ADS_SUCCESS;
3284 		}
3285 		goto done;
3286 	}
3287 
3288 	if ( !ads_pull_uint32(ads_s, res, "domainFunctionality", val) ) {
3289 		DEBUG(5,("ads_domain_func_level: Failed to pull the domainFunctionality attribute.\n"));
3290 	}
3291 	DEBUG(3,("ads_domain_func_level: %d\n", *val));
3292 
3293 
3294 	ads_msgfree(ads, res);
3295 
3296 done:
3297 	/* free any temporary ads connections */
3298 	if ( ads_s != ads ) {
3299 		ads_destroy( &ads_s );
3300 	}
3301 
3302 	return status;
3303 }
3304 
3305 /**
3306  * find the domain sid for our domain
3307  * @param ads connection to ads server
3308  * @param sid Pointer to domain sid
3309  * @return status of search
3310  **/
ads_domain_sid(ADS_STRUCT * ads,struct dom_sid * sid)3311 ADS_STATUS ads_domain_sid(ADS_STRUCT *ads, struct dom_sid *sid)
3312 {
3313 	const char *attrs[] = {"objectSid", NULL};
3314 	LDAPMessage *res;
3315 	ADS_STATUS rc;
3316 
3317 	rc = ads_do_search_retry(ads, ads->config.bind_path, LDAP_SCOPE_BASE, "(objectclass=*)",
3318 			   attrs, &res);
3319 	if (!ADS_ERR_OK(rc)) return rc;
3320 	if (!ads_pull_sid(ads, res, "objectSid", sid)) {
3321 		ads_msgfree(ads, res);
3322 		return ADS_ERROR_SYSTEM(ENOENT);
3323 	}
3324 	ads_msgfree(ads, res);
3325 
3326 	return ADS_SUCCESS;
3327 }
3328 
3329 /**
3330  * find our site name
3331  * @param ads connection to ads server
3332  * @param mem_ctx Pointer to talloc context
3333  * @param site_name Pointer to the sitename
3334  * @return status of search
3335  **/
ads_site_dn(ADS_STRUCT * ads,TALLOC_CTX * mem_ctx,const char ** site_name)3336 ADS_STATUS ads_site_dn(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char **site_name)
3337 {
3338 	ADS_STATUS status;
3339 	LDAPMessage *res;
3340 	const char *dn, *service_name;
3341 	const char *attrs[] = { "dsServiceName", NULL };
3342 
3343 	status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
3344 	if (!ADS_ERR_OK(status)) {
3345 		return status;
3346 	}
3347 
3348 	service_name = ads_pull_string(ads, mem_ctx, res, "dsServiceName");
3349 	if (service_name == NULL) {
3350 		ads_msgfree(ads, res);
3351 		return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3352 	}
3353 
3354 	ads_msgfree(ads, res);
3355 
3356 	/* go up three levels */
3357 	dn = ads_parent_dn(ads_parent_dn(ads_parent_dn(service_name)));
3358 	if (dn == NULL) {
3359 		return ADS_ERROR(LDAP_NO_MEMORY);
3360 	}
3361 
3362 	*site_name = talloc_strdup(mem_ctx, dn);
3363 	if (*site_name == NULL) {
3364 		return ADS_ERROR(LDAP_NO_MEMORY);
3365 	}
3366 
3367 	return status;
3368 	/*
3369 	dsServiceName: CN=NTDS Settings,CN=W2K3DC,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,DC=ber,DC=suse,DC=de
3370 	*/
3371 }
3372 
3373 /**
3374  * find the site dn where a machine resides
3375  * @param ads connection to ads server
3376  * @param mem_ctx Pointer to talloc context
3377  * @param computer_name name of the machine
3378  * @param site_name Pointer to the sitename
3379  * @return status of search
3380  **/
ads_site_dn_for_machine(ADS_STRUCT * ads,TALLOC_CTX * mem_ctx,const char * computer_name,const char ** site_dn)3381 ADS_STATUS ads_site_dn_for_machine(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char *computer_name, const char **site_dn)
3382 {
3383 	ADS_STATUS status;
3384 	LDAPMessage *res;
3385 	const char *parent, *filter;
3386 	char *config_context = NULL;
3387 	char *dn;
3388 
3389 	/* shortcut a query */
3390 	if (strequal(computer_name, ads->config.ldap_server_name)) {
3391 		return ads_site_dn(ads, mem_ctx, site_dn);
3392 	}
3393 
3394 	status = ads_config_path(ads, mem_ctx, &config_context);
3395 	if (!ADS_ERR_OK(status)) {
3396 		return status;
3397 	}
3398 
3399 	filter = talloc_asprintf(mem_ctx, "(cn=%s)", computer_name);
3400 	if (filter == NULL) {
3401 		return ADS_ERROR(LDAP_NO_MEMORY);
3402 	}
3403 
3404 	status = ads_do_search(ads, config_context, LDAP_SCOPE_SUBTREE,
3405 			       filter, NULL, &res);
3406 	if (!ADS_ERR_OK(status)) {
3407 		return status;
3408 	}
3409 
3410 	if (ads_count_replies(ads, res) != 1) {
3411 		ads_msgfree(ads, res);
3412 		return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
3413 	}
3414 
3415 	dn = ads_get_dn(ads, mem_ctx, res);
3416 	if (dn == NULL) {
3417 		ads_msgfree(ads, res);
3418 		return ADS_ERROR(LDAP_NO_MEMORY);
3419 	}
3420 
3421 	/* go up three levels */
3422 	parent = ads_parent_dn(ads_parent_dn(ads_parent_dn(dn)));
3423 	if (parent == NULL) {
3424 		ads_msgfree(ads, res);
3425 		TALLOC_FREE(dn);
3426 		return ADS_ERROR(LDAP_NO_MEMORY);
3427 	}
3428 
3429 	*site_dn = talloc_strdup(mem_ctx, parent);
3430 	if (*site_dn == NULL) {
3431 		ads_msgfree(ads, res);
3432 		TALLOC_FREE(dn);
3433 		return ADS_ERROR(LDAP_NO_MEMORY);
3434 	}
3435 
3436 	TALLOC_FREE(dn);
3437 	ads_msgfree(ads, res);
3438 
3439 	return status;
3440 }
3441 
3442 /**
3443  * get the upn suffixes for a domain
3444  * @param ads connection to ads server
3445  * @param mem_ctx Pointer to talloc context
3446  * @param suffixes Pointer to an array of suffixes
3447  * @param num_suffixes Pointer to the number of suffixes
3448  * @return status of search
3449  **/
ads_upn_suffixes(ADS_STRUCT * ads,TALLOC_CTX * mem_ctx,char *** suffixes,size_t * num_suffixes)3450 ADS_STATUS ads_upn_suffixes(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, char ***suffixes, size_t *num_suffixes)
3451 {
3452 	ADS_STATUS status;
3453 	LDAPMessage *res;
3454 	const char *base;
3455 	char *config_context = NULL;
3456 	const char *attrs[] = { "uPNSuffixes", NULL };
3457 
3458 	status = ads_config_path(ads, mem_ctx, &config_context);
3459 	if (!ADS_ERR_OK(status)) {
3460 		return status;
3461 	}
3462 
3463 	base = talloc_asprintf(mem_ctx, "cn=Partitions,%s", config_context);
3464 	if (base == NULL) {
3465 		return ADS_ERROR(LDAP_NO_MEMORY);
3466 	}
3467 
3468 	status = ads_search_dn(ads, &res, base, attrs);
3469 	if (!ADS_ERR_OK(status)) {
3470 		return status;
3471 	}
3472 
3473 	if (ads_count_replies(ads, res) != 1) {
3474 		ads_msgfree(ads, res);
3475 		return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
3476 	}
3477 
3478 	(*suffixes) = ads_pull_strings(ads, mem_ctx, res, "uPNSuffixes", num_suffixes);
3479 	if ((*suffixes) == NULL) {
3480 		ads_msgfree(ads, res);
3481 		return ADS_ERROR(LDAP_NO_MEMORY);
3482 	}
3483 
3484 	ads_msgfree(ads, res);
3485 
3486 	return status;
3487 }
3488 
3489 /**
3490  * get the joinable ous for a domain
3491  * @param ads connection to ads server
3492  * @param mem_ctx Pointer to talloc context
3493  * @param ous Pointer to an array of ous
3494  * @param num_ous Pointer to the number of ous
3495  * @return status of search
3496  **/
ads_get_joinable_ous(ADS_STRUCT * ads,TALLOC_CTX * mem_ctx,char *** ous,size_t * num_ous)3497 ADS_STATUS ads_get_joinable_ous(ADS_STRUCT *ads,
3498 				TALLOC_CTX *mem_ctx,
3499 				char ***ous,
3500 				size_t *num_ous)
3501 {
3502 	ADS_STATUS status;
3503 	LDAPMessage *res = NULL;
3504 	LDAPMessage *msg = NULL;
3505 	const char *attrs[] = { "dn", NULL };
3506 	int count = 0;
3507 
3508 	status = ads_search(ads, &res,
3509 			    "(|(objectClass=domain)(objectclass=organizationalUnit))",
3510 			    attrs);
3511 	if (!ADS_ERR_OK(status)) {
3512 		return status;
3513 	}
3514 
3515 	count = ads_count_replies(ads, res);
3516 	if (count < 1) {
3517 		ads_msgfree(ads, res);
3518 		return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3519 	}
3520 
3521 	for (msg = ads_first_entry(ads, res); msg;
3522 	     msg = ads_next_entry(ads, msg)) {
3523 		const char **p = discard_const_p(const char *, *ous);
3524 		char *dn = NULL;
3525 
3526 		dn = ads_get_dn(ads, talloc_tos(), msg);
3527 		if (!dn) {
3528 			ads_msgfree(ads, res);
3529 			return ADS_ERROR(LDAP_NO_MEMORY);
3530 		}
3531 
3532 		if (!add_string_to_array(mem_ctx, dn, &p, num_ous)) {
3533 			TALLOC_FREE(dn);
3534 			ads_msgfree(ads, res);
3535 			return ADS_ERROR(LDAP_NO_MEMORY);
3536 		}
3537 
3538 		TALLOC_FREE(dn);
3539 		*ous = discard_const_p(char *, p);
3540 	}
3541 
3542 	ads_msgfree(ads, res);
3543 
3544 	return status;
3545 }
3546 
3547 
3548 /**
3549  * pull a struct dom_sid from an extended dn string
3550  * @param mem_ctx TALLOC_CTX
3551  * @param extended_dn string
3552  * @param flags string type of extended_dn
3553  * @param sid pointer to a struct dom_sid
3554  * @return NT_STATUS_OK on success,
3555  *	   NT_INVALID_PARAMETER on error,
3556  *	   NT_STATUS_NOT_FOUND if no SID present
3557  **/
ads_get_sid_from_extended_dn(TALLOC_CTX * mem_ctx,const char * extended_dn,enum ads_extended_dn_flags flags,struct dom_sid * sid)3558 ADS_STATUS ads_get_sid_from_extended_dn(TALLOC_CTX *mem_ctx,
3559 					const char *extended_dn,
3560 					enum ads_extended_dn_flags flags,
3561 					struct dom_sid *sid)
3562 {
3563 	char *p, *q, *dn;
3564 
3565 	if (!extended_dn) {
3566 		return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3567 	}
3568 
3569 	/* otherwise extended_dn gets stripped off */
3570 	if ((dn = talloc_strdup(mem_ctx, extended_dn)) == NULL) {
3571 		return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3572 	}
3573 	/*
3574 	 * ADS_EXTENDED_DN_HEX_STRING:
3575 	 * <GUID=238e1963cb390f4bb032ba0105525a29>;<SID=010500000000000515000000bb68c8fd6b61b427572eb04556040000>;CN=gd,OU=berlin,OU=suse,DC=ber,DC=suse,DC=de
3576 	 *
3577 	 * ADS_EXTENDED_DN_STRING (only with w2k3):
3578 	 * <GUID=63198e23-39cb-4b0f-b032-ba0105525a29>;<SID=S-1-5-21-4257769659-666132843-1169174103-1110>;CN=gd,OU=berlin,OU=suse,DC=ber,DC=suse,DC=de
3579 	 *
3580 	 * Object with no SID, such as an Exchange Public Folder
3581 	 * <GUID=28907fb4bdf6854993e7f0a10b504e7c>;CN=public,CN=Microsoft Exchange System Objects,DC=sd2k3ms,DC=west,DC=isilon,DC=com
3582 	 */
3583 
3584 	p = strchr(dn, ';');
3585 	if (!p) {
3586 		return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3587 	}
3588 
3589 	if (strncmp(p, ";<SID=", strlen(";<SID=")) != 0) {
3590 		DEBUG(5,("No SID present in extended dn\n"));
3591 		return ADS_ERROR_NT(NT_STATUS_NOT_FOUND);
3592 	}
3593 
3594 	p += strlen(";<SID=");
3595 
3596 	q = strchr(p, '>');
3597 	if (!q) {
3598 		return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3599 	}
3600 
3601 	*q = '\0';
3602 
3603 	DEBUG(100,("ads_get_sid_from_extended_dn: sid string is %s\n", p));
3604 
3605 	switch (flags) {
3606 
3607 	case ADS_EXTENDED_DN_STRING:
3608 		if (!string_to_sid(sid, p)) {
3609 			return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3610 		}
3611 		break;
3612 	case ADS_EXTENDED_DN_HEX_STRING: {
3613 		ssize_t ret;
3614 		fstring buf;
3615 		size_t buf_len;
3616 
3617 		buf_len = strhex_to_str(buf, sizeof(buf), p, strlen(p));
3618 		if (buf_len == 0) {
3619 			return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3620 		}
3621 
3622 		ret = sid_parse((const uint8_t *)buf, buf_len, sid);
3623 		if (ret == -1) {
3624 			DEBUG(10,("failed to parse sid\n"));
3625 			return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3626 		}
3627 		break;
3628 		}
3629 	default:
3630 		DEBUG(10,("unknown extended dn format\n"));
3631 		return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3632 	}
3633 
3634 	return ADS_ERROR_NT(NT_STATUS_OK);
3635 }
3636 
3637 /********************************************************************
3638 ********************************************************************/
3639 
ads_get_dnshostname(ADS_STRUCT * ads,TALLOC_CTX * ctx,const char * machine_name)3640 char* ads_get_dnshostname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3641 {
3642 	LDAPMessage *res = NULL;
3643 	ADS_STATUS status;
3644 	int count = 0;
3645 	char *name = NULL;
3646 
3647 	status = ads_find_machine_acct(ads, &res, machine_name);
3648 	if (!ADS_ERR_OK(status)) {
3649 		DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
3650 			lp_netbios_name()));
3651 		goto out;
3652 	}
3653 
3654 	if ( (count = ads_count_replies(ads, res)) != 1 ) {
3655 		DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
3656 		goto out;
3657 	}
3658 
3659 	if ( (name = ads_pull_string(ads, ctx, res, "dNSHostName")) == NULL ) {
3660 		DEBUG(0,("ads_get_dnshostname: No dNSHostName attribute!\n"));
3661 	}
3662 
3663 out:
3664 	ads_msgfree(ads, res);
3665 
3666 	return name;
3667 }
3668 
3669 /********************************************************************
3670 ********************************************************************/
3671 
get_addl_hosts(ADS_STRUCT * ads,TALLOC_CTX * mem_ctx,LDAPMessage * msg,size_t * num_values)3672 static char **get_addl_hosts(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
3673 			      LDAPMessage *msg, size_t *num_values)
3674 {
3675 	const char *field = "msDS-AdditionalDnsHostName";
3676 	struct berval **values = NULL;
3677 	char **ret = NULL;
3678 	size_t i, converted_size;
3679 
3680 	values = ldap_get_values_len(ads->ldap.ld, msg, field);
3681 	if (values == NULL) {
3682 		return NULL;
3683 	}
3684 
3685 	*num_values = ldap_count_values_len(values);
3686 
3687 	ret = talloc_array(mem_ctx, char *, *num_values + 1);
3688 	if (ret == NULL) {
3689 		ldap_value_free_len(values);
3690 		return NULL;
3691 	}
3692 
3693 	for (i = 0; i < *num_values; i++) {
3694 		ret[i] = NULL;
3695 		if (!convert_string_talloc(mem_ctx, CH_UTF8, CH_UNIX,
3696 					   values[i]->bv_val,
3697 					   strnlen(values[i]->bv_val,
3698 						   values[i]->bv_len),
3699 					   &ret[i], &converted_size)) {
3700 			ldap_value_free_len(values);
3701 			return NULL;
3702 		}
3703 	}
3704 	ret[i] = NULL;
3705 
3706 	ldap_value_free_len(values);
3707 	return ret;
3708 }
3709 
ads_get_additional_dns_hostnames(TALLOC_CTX * mem_ctx,ADS_STRUCT * ads,const char * machine_name,char *** hostnames_array,size_t * num_hostnames)3710 ADS_STATUS ads_get_additional_dns_hostnames(TALLOC_CTX *mem_ctx,
3711 					    ADS_STRUCT *ads,
3712 					    const char *machine_name,
3713 					    char ***hostnames_array,
3714 					    size_t *num_hostnames)
3715 {
3716 	ADS_STATUS status;
3717 	LDAPMessage *res = NULL;
3718 	int count;
3719 
3720 	status = ads_find_machine_acct(ads,
3721 				       &res,
3722 				       machine_name);
3723 	if (!ADS_ERR_OK(status)) {
3724 		DEBUG(1,("Host Account for %s not found... skipping operation.\n",
3725 			 machine_name));
3726 		return status;
3727 	}
3728 
3729 	count = ads_count_replies(ads, res);
3730 	if (count != 1) {
3731 		status = ADS_ERROR(LDAP_NO_SUCH_OBJECT);
3732 		goto done;
3733 	}
3734 
3735 	*hostnames_array = get_addl_hosts(ads, mem_ctx, res, num_hostnames);
3736 	if (*hostnames_array == NULL) {
3737 		DEBUG(1, ("Host account for %s does not have msDS-AdditionalDnsHostName.\n",
3738 			  machine_name));
3739 		status = ADS_ERROR(LDAP_NO_SUCH_OBJECT);
3740 		goto done;
3741 	}
3742 
3743 done:
3744 	ads_msgfree(ads, res);
3745 
3746 	return status;
3747 }
3748 
3749 /********************************************************************
3750 ********************************************************************/
3751 
ads_get_upn(ADS_STRUCT * ads,TALLOC_CTX * ctx,const char * machine_name)3752 char* ads_get_upn( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3753 {
3754 	LDAPMessage *res = NULL;
3755 	ADS_STATUS status;
3756 	int count = 0;
3757 	char *name = NULL;
3758 
3759 	status = ads_find_machine_acct(ads, &res, machine_name);
3760 	if (!ADS_ERR_OK(status)) {
3761 		DEBUG(0,("ads_get_upn: Failed to find account for %s\n",
3762 			lp_netbios_name()));
3763 		goto out;
3764 	}
3765 
3766 	if ( (count = ads_count_replies(ads, res)) != 1 ) {
3767 		DEBUG(1,("ads_get_upn: %d entries returned!\n", count));
3768 		goto out;
3769 	}
3770 
3771 	if ( (name = ads_pull_string(ads, ctx, res, "userPrincipalName")) == NULL ) {
3772 		DEBUG(2,("ads_get_upn: No userPrincipalName attribute!\n"));
3773 	}
3774 
3775 out:
3776 	ads_msgfree(ads, res);
3777 
3778 	return name;
3779 }
3780 
3781 /********************************************************************
3782 ********************************************************************/
3783 
ads_has_samaccountname(ADS_STRUCT * ads,TALLOC_CTX * ctx,const char * machine_name)3784 bool ads_has_samaccountname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3785 {
3786 	LDAPMessage *res = NULL;
3787 	ADS_STATUS status;
3788 	int count = 0;
3789 	char *name = NULL;
3790 	bool ok = false;
3791 
3792 	status = ads_find_machine_acct(ads, &res, machine_name);
3793 	if (!ADS_ERR_OK(status)) {
3794 		DEBUG(0,("ads_has_samaccountname: Failed to find account for %s\n",
3795 			lp_netbios_name()));
3796 		goto out;
3797 	}
3798 
3799 	if ( (count = ads_count_replies(ads, res)) != 1 ) {
3800 		DEBUG(1,("ads_has_samaccountname: %d entries returned!\n", count));
3801 		goto out;
3802 	}
3803 
3804 	if ( (name = ads_pull_string(ads, ctx, res, "sAMAccountName")) == NULL ) {
3805 		DEBUG(0,("ads_has_samaccountname: No sAMAccountName attribute!\n"));
3806 	}
3807 
3808 out:
3809 	ads_msgfree(ads, res);
3810 	if (name != NULL) {
3811 		ok = (strlen(name) > 0);
3812 	}
3813 	TALLOC_FREE(name);
3814 	return ok;
3815 }
3816 
3817 #if 0
3818 
3819    SAVED CODE - we used to join via ldap - remember how we did this. JRA.
3820 
3821 /**
3822  * Join a machine to a realm
3823  *  Creates the machine account and sets the machine password
3824  * @param ads connection to ads server
3825  * @param machine name of host to add
3826  * @param org_unit Organizational unit to place machine in
3827  * @return status of join
3828  **/
3829 ADS_STATUS ads_join_realm(ADS_STRUCT *ads, const char *machine_name,
3830 			uint32_t account_type, const char *org_unit)
3831 {
3832 	ADS_STATUS status;
3833 	LDAPMessage *res = NULL;
3834 	char *machine;
3835 
3836 	/* machine name must be lowercase */
3837 	machine = SMB_STRDUP(machine_name);
3838 	strlower_m(machine);
3839 
3840 	/*
3841 	status = ads_find_machine_acct(ads, (void **)&res, machine);
3842 	if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
3843 		DEBUG(0, ("Host account for %s already exists - deleting old account\n", machine));
3844 		status = ads_leave_realm(ads, machine);
3845 		if (!ADS_ERR_OK(status)) {
3846 			DEBUG(0, ("Failed to delete host '%s' from the '%s' realm.\n",
3847 				machine, ads->config.realm));
3848 			return status;
3849 		}
3850 	}
3851 	*/
3852 	status = ads_add_machine_acct(ads, machine, account_type, org_unit);
3853 	if (!ADS_ERR_OK(status)) {
3854 		DEBUG(0, ("ads_join_realm: ads_add_machine_acct failed (%s): %s\n", machine, ads_errstr(status)));
3855 		SAFE_FREE(machine);
3856 		return status;
3857 	}
3858 
3859 	status = ads_find_machine_acct(ads, (void **)(void *)&res, machine);
3860 	if (!ADS_ERR_OK(status)) {
3861 		DEBUG(0, ("ads_join_realm: Host account test failed for machine %s\n", machine));
3862 		SAFE_FREE(machine);
3863 		return status;
3864 	}
3865 
3866 	SAFE_FREE(machine);
3867 	ads_msgfree(ads, res);
3868 
3869 	return status;
3870 }
3871 #endif
3872 
3873 /**
3874  * Delete a machine from the realm
3875  * @param ads connection to ads server
3876  * @param hostname Machine to remove
3877  * @return status of delete
3878  **/
ads_leave_realm(ADS_STRUCT * ads,const char * hostname)3879 ADS_STATUS ads_leave_realm(ADS_STRUCT *ads, const char *hostname)
3880 {
3881 	ADS_STATUS status;
3882 	void *msg;
3883 	LDAPMessage *res;
3884 	char *hostnameDN, *host;
3885 	int rc;
3886 	LDAPControl ldap_control;
3887 	LDAPControl  * pldap_control[2] = {NULL, NULL};
3888 
3889 	pldap_control[0] = &ldap_control;
3890 	memset(&ldap_control, 0, sizeof(LDAPControl));
3891 	ldap_control.ldctl_oid = discard_const_p(char, LDAP_SERVER_TREE_DELETE_OID);
3892 
3893 	/* hostname must be lowercase */
3894 	host = SMB_STRDUP(hostname);
3895 	if (!strlower_m(host)) {
3896 		SAFE_FREE(host);
3897 		return ADS_ERROR_SYSTEM(EINVAL);
3898 	}
3899 
3900 	status = ads_find_machine_acct(ads, &res, host);
3901 	if (!ADS_ERR_OK(status)) {
3902 		DEBUG(0, ("Host account for %s does not exist.\n", host));
3903 		SAFE_FREE(host);
3904 		return status;
3905 	}
3906 
3907 	msg = ads_first_entry(ads, res);
3908 	if (!msg) {
3909 		SAFE_FREE(host);
3910 		return ADS_ERROR_SYSTEM(ENOENT);
3911 	}
3912 
3913 	hostnameDN = ads_get_dn(ads, talloc_tos(), (LDAPMessage *)msg);
3914 	if (hostnameDN == NULL) {
3915 		SAFE_FREE(host);
3916 		return ADS_ERROR_SYSTEM(ENOENT);
3917 	}
3918 
3919 	rc = ldap_delete_ext_s(ads->ldap.ld, hostnameDN, pldap_control, NULL);
3920 	if (rc) {
3921 		DEBUG(3,("ldap_delete_ext_s failed with error code %d\n", rc));
3922 	}else {
3923 		DEBUG(3,("ldap_delete_ext_s succeeded with error code %d\n", rc));
3924 	}
3925 
3926 	if (rc != LDAP_SUCCESS) {
3927 		const char *attrs[] = { "cn", NULL };
3928 		LDAPMessage *msg_sub;
3929 
3930 		/* we only search with scope ONE, we do not expect any further
3931 		 * objects to be created deeper */
3932 
3933 		status = ads_do_search_retry(ads, hostnameDN,
3934 					     LDAP_SCOPE_ONELEVEL,
3935 					     "(objectclass=*)", attrs, &res);
3936 
3937 		if (!ADS_ERR_OK(status)) {
3938 			SAFE_FREE(host);
3939 			TALLOC_FREE(hostnameDN);
3940 			return status;
3941 		}
3942 
3943 		for (msg_sub = ads_first_entry(ads, res); msg_sub;
3944 			msg_sub = ads_next_entry(ads, msg_sub)) {
3945 
3946 			char *dn = NULL;
3947 
3948 			if ((dn = ads_get_dn(ads, talloc_tos(), msg_sub)) == NULL) {
3949 				SAFE_FREE(host);
3950 				TALLOC_FREE(hostnameDN);
3951 				return ADS_ERROR(LDAP_NO_MEMORY);
3952 			}
3953 
3954 			status = ads_del_dn(ads, dn);
3955 			if (!ADS_ERR_OK(status)) {
3956 				DEBUG(3,("failed to delete dn %s: %s\n", dn, ads_errstr(status)));
3957 				SAFE_FREE(host);
3958 				TALLOC_FREE(dn);
3959 				TALLOC_FREE(hostnameDN);
3960 				return status;
3961 			}
3962 
3963 			TALLOC_FREE(dn);
3964 		}
3965 
3966 		/* there should be no subordinate objects anymore */
3967 		status = ads_do_search_retry(ads, hostnameDN,
3968 					     LDAP_SCOPE_ONELEVEL,
3969 					     "(objectclass=*)", attrs, &res);
3970 
3971 		if (!ADS_ERR_OK(status) || ( (ads_count_replies(ads, res)) > 0 ) ) {
3972 			SAFE_FREE(host);
3973 			TALLOC_FREE(hostnameDN);
3974 			return status;
3975 		}
3976 
3977 		/* delete hostnameDN now */
3978 		status = ads_del_dn(ads, hostnameDN);
3979 		if (!ADS_ERR_OK(status)) {
3980 			SAFE_FREE(host);
3981 			DEBUG(3,("failed to delete dn %s: %s\n", hostnameDN, ads_errstr(status)));
3982 			TALLOC_FREE(hostnameDN);
3983 			return status;
3984 		}
3985 	}
3986 
3987 	TALLOC_FREE(hostnameDN);
3988 
3989 	status = ads_find_machine_acct(ads, &res, host);
3990 	if ((status.error_type == ENUM_ADS_ERROR_LDAP) &&
3991 	    (status.err.rc != LDAP_NO_SUCH_OBJECT)) {
3992 		DEBUG(3, ("Failed to remove host account.\n"));
3993 		SAFE_FREE(host);
3994 		return status;
3995 	}
3996 
3997 	SAFE_FREE(host);
3998 	return ADS_SUCCESS;
3999 }
4000 
4001 /**
4002  * pull all token-sids from an LDAP dn
4003  * @param ads connection to ads server
4004  * @param mem_ctx TALLOC_CTX for allocating sid array
4005  * @param dn of LDAP object
4006  * @param user_sid pointer to struct dom_sid (objectSid)
4007  * @param primary_group_sid pointer to struct dom_sid (self composed)
4008  * @param sids pointer to sid array to allocate
4009  * @param num_sids counter of SIDs pulled
4010  * @return status of token query
4011  **/
ads_get_tokensids(ADS_STRUCT * ads,TALLOC_CTX * mem_ctx,const char * dn,struct dom_sid * user_sid,struct dom_sid * primary_group_sid,struct dom_sid ** sids,size_t * num_sids)4012  ADS_STATUS ads_get_tokensids(ADS_STRUCT *ads,
4013 			      TALLOC_CTX *mem_ctx,
4014 			      const char *dn,
4015 			      struct dom_sid *user_sid,
4016 			      struct dom_sid *primary_group_sid,
4017 			      struct dom_sid **sids,
4018 			      size_t *num_sids)
4019 {
4020 	ADS_STATUS status;
4021 	LDAPMessage *res = NULL;
4022 	int count = 0;
4023 	size_t tmp_num_sids;
4024 	struct dom_sid *tmp_sids;
4025 	struct dom_sid tmp_user_sid;
4026 	struct dom_sid tmp_primary_group_sid;
4027 	uint32_t pgid;
4028 	const char *attrs[] = {
4029 		"objectSid",
4030 		"tokenGroups",
4031 		"primaryGroupID",
4032 		NULL
4033 	};
4034 
4035 	status = ads_search_retry_dn(ads, &res, dn, attrs);
4036 	if (!ADS_ERR_OK(status)) {
4037 		return status;
4038 	}
4039 
4040 	count = ads_count_replies(ads, res);
4041 	if (count != 1) {
4042 		ads_msgfree(ads, res);
4043 		return ADS_ERROR_LDAP(LDAP_NO_SUCH_OBJECT);
4044 	}
4045 
4046 	if (!ads_pull_sid(ads, res, "objectSid", &tmp_user_sid)) {
4047 		ads_msgfree(ads, res);
4048 		return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
4049 	}
4050 
4051 	if (!ads_pull_uint32(ads, res, "primaryGroupID", &pgid)) {
4052 		ads_msgfree(ads, res);
4053 		return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
4054 	}
4055 
4056 	{
4057 		/* hack to compose the primary group sid without knowing the
4058 		 * domsid */
4059 
4060 		struct dom_sid domsid;
4061 
4062 		sid_copy(&domsid, &tmp_user_sid);
4063 
4064 		if (!sid_split_rid(&domsid, NULL)) {
4065 			ads_msgfree(ads, res);
4066 			return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
4067 		}
4068 
4069 		if (!sid_compose(&tmp_primary_group_sid, &domsid, pgid)) {
4070 			ads_msgfree(ads, res);
4071 			return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
4072 		}
4073 	}
4074 
4075 	tmp_num_sids = ads_pull_sids(ads, mem_ctx, res, "tokenGroups", &tmp_sids);
4076 
4077 	if (tmp_num_sids == 0 || !tmp_sids) {
4078 		ads_msgfree(ads, res);
4079 		return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
4080 	}
4081 
4082 	if (num_sids) {
4083 		*num_sids = tmp_num_sids;
4084 	}
4085 
4086 	if (sids) {
4087 		*sids = tmp_sids;
4088 	}
4089 
4090 	if (user_sid) {
4091 		*user_sid = tmp_user_sid;
4092 	}
4093 
4094 	if (primary_group_sid) {
4095 		*primary_group_sid = tmp_primary_group_sid;
4096 	}
4097 
4098 	DEBUG(10,("ads_get_tokensids: returned %d sids\n", (int)tmp_num_sids + 2));
4099 
4100 	ads_msgfree(ads, res);
4101 	return ADS_ERROR_LDAP(LDAP_SUCCESS);
4102 }
4103 
4104 /**
4105  * Find a sAMAccoutName in LDAP
4106  * @param ads connection to ads server
4107  * @param mem_ctx TALLOC_CTX for allocating sid array
4108  * @param samaccountname to search
4109  * @param uac_ret uint32_t pointer userAccountControl attribute value
4110  * @param dn_ret pointer to dn
4111  * @return status of token query
4112  **/
ads_find_samaccount(ADS_STRUCT * ads,TALLOC_CTX * mem_ctx,const char * samaccountname,uint32_t * uac_ret,const char ** dn_ret)4113 ADS_STATUS ads_find_samaccount(ADS_STRUCT *ads,
4114 			       TALLOC_CTX *mem_ctx,
4115 			       const char *samaccountname,
4116 			       uint32_t *uac_ret,
4117 			       const char **dn_ret)
4118 {
4119 	ADS_STATUS status;
4120 	const char *attrs[] = { "userAccountControl", NULL };
4121 	const char *filter;
4122 	LDAPMessage *res = NULL;
4123 	char *dn = NULL;
4124 	uint32_t uac = 0;
4125 
4126 	filter = talloc_asprintf(mem_ctx, "(&(objectclass=user)(sAMAccountName=%s))",
4127 		samaccountname);
4128 	if (filter == NULL) {
4129 		status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
4130 		goto out;
4131 	}
4132 
4133 	status = ads_do_search_all(ads, ads->config.bind_path,
4134 				   LDAP_SCOPE_SUBTREE,
4135 				   filter, attrs, &res);
4136 
4137 	if (!ADS_ERR_OK(status)) {
4138 		goto out;
4139 	}
4140 
4141 	if (ads_count_replies(ads, res) != 1) {
4142 		status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
4143 		goto out;
4144 	}
4145 
4146 	dn = ads_get_dn(ads, talloc_tos(), res);
4147 	if (dn == NULL) {
4148 		status = ADS_ERROR(LDAP_NO_MEMORY);
4149 		goto out;
4150 	}
4151 
4152 	if (!ads_pull_uint32(ads, res, "userAccountControl", &uac)) {
4153 		status = ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
4154 		goto out;
4155 	}
4156 
4157 	if (uac_ret) {
4158 		*uac_ret = uac;
4159 	}
4160 
4161 	if (dn_ret) {
4162 		*dn_ret = talloc_strdup(mem_ctx, dn);
4163 		if (!*dn_ret) {
4164 			status = ADS_ERROR(LDAP_NO_MEMORY);
4165 			goto out;
4166 		}
4167 	}
4168  out:
4169 	TALLOC_FREE(dn);
4170 	ads_msgfree(ads, res);
4171 
4172 	return status;
4173 }
4174 
4175 /**
4176  * find our configuration path
4177  * @param ads connection to ads server
4178  * @param mem_ctx Pointer to talloc context
4179  * @param config_path Pointer to the config path
4180  * @return status of search
4181  **/
ads_config_path(ADS_STRUCT * ads,TALLOC_CTX * mem_ctx,char ** config_path)4182 ADS_STATUS ads_config_path(ADS_STRUCT *ads,
4183 			   TALLOC_CTX *mem_ctx,
4184 			   char **config_path)
4185 {
4186 	ADS_STATUS status;
4187 	LDAPMessage *res = NULL;
4188 	const char *config_context = NULL;
4189 	const char *attrs[] = { "configurationNamingContext", NULL };
4190 
4191 	status = ads_do_search(ads, "", LDAP_SCOPE_BASE,
4192 			       "(objectclass=*)", attrs, &res);
4193 	if (!ADS_ERR_OK(status)) {
4194 		return status;
4195 	}
4196 
4197 	config_context = ads_pull_string(ads, mem_ctx, res,
4198 					 "configurationNamingContext");
4199 	ads_msgfree(ads, res);
4200 	if (!config_context) {
4201 		return ADS_ERROR(LDAP_NO_MEMORY);
4202 	}
4203 
4204 	if (config_path) {
4205 		*config_path = talloc_strdup(mem_ctx, config_context);
4206 		if (!*config_path) {
4207 			return ADS_ERROR(LDAP_NO_MEMORY);
4208 		}
4209 	}
4210 
4211 	return ADS_ERROR(LDAP_SUCCESS);
4212 }
4213 
4214 /**
4215  * find the displayName of an extended right
4216  * @param ads connection to ads server
4217  * @param config_path The config path
4218  * @param mem_ctx Pointer to talloc context
4219  * @param GUID struct of the rightsGUID
4220  * @return status of search
4221  **/
ads_get_extended_right_name_by_guid(ADS_STRUCT * ads,const char * config_path,TALLOC_CTX * mem_ctx,const struct GUID * rights_guid)4222 const char *ads_get_extended_right_name_by_guid(ADS_STRUCT *ads,
4223 						const char *config_path,
4224 						TALLOC_CTX *mem_ctx,
4225 						const struct GUID *rights_guid)
4226 {
4227 	ADS_STATUS rc;
4228 	LDAPMessage *res = NULL;
4229 	char *expr = NULL;
4230 	const char *attrs[] = { "displayName", NULL };
4231 	const char *result = NULL;
4232 	const char *path;
4233 
4234 	if (!ads || !mem_ctx || !rights_guid) {
4235 		goto done;
4236 	}
4237 
4238 	expr = talloc_asprintf(mem_ctx, "(rightsGuid=%s)",
4239 			       GUID_string(mem_ctx, rights_guid));
4240 	if (!expr) {
4241 		goto done;
4242 	}
4243 
4244 	path = talloc_asprintf(mem_ctx, "cn=Extended-Rights,%s", config_path);
4245 	if (!path) {
4246 		goto done;
4247 	}
4248 
4249 	rc = ads_do_search_retry(ads, path, LDAP_SCOPE_SUBTREE,
4250 				 expr, attrs, &res);
4251 	if (!ADS_ERR_OK(rc)) {
4252 		goto done;
4253 	}
4254 
4255 	if (ads_count_replies(ads, res) != 1) {
4256 		goto done;
4257 	}
4258 
4259 	result = ads_pull_string(ads, mem_ctx, res, "displayName");
4260 
4261  done:
4262 	ads_msgfree(ads, res);
4263 	return result;
4264 }
4265 
4266 /**
4267  * verify or build and verify an account ou
4268  * @param mem_ctx Pointer to talloc context
4269  * @param ads connection to ads server
4270  * @param account_ou
4271  * @return status of search
4272  **/
4273 
ads_check_ou_dn(TALLOC_CTX * mem_ctx,ADS_STRUCT * ads,const char ** account_ou)4274 ADS_STATUS ads_check_ou_dn(TALLOC_CTX *mem_ctx,
4275 			   ADS_STRUCT *ads,
4276 			   const char **account_ou)
4277 {
4278 	char **exploded_dn;
4279 	const char *name;
4280 	char *ou_string;
4281 
4282 	if (account_ou == NULL) {
4283 		return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
4284 	}
4285 
4286 	if (*account_ou != NULL) {
4287 		exploded_dn = ldap_explode_dn(*account_ou, 0);
4288 		if (exploded_dn) {
4289 			ldap_value_free(exploded_dn);
4290 			return ADS_SUCCESS;
4291 		}
4292 	}
4293 
4294 	ou_string = ads_ou_string(ads, *account_ou);
4295 	if (!ou_string) {
4296 		return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX);
4297 	}
4298 
4299 	name = talloc_asprintf(mem_ctx, "%s,%s", ou_string,
4300 			       ads->config.bind_path);
4301 	SAFE_FREE(ou_string);
4302 
4303 	if (!name) {
4304 		return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
4305 	}
4306 
4307 	exploded_dn = ldap_explode_dn(name, 0);
4308 	if (!exploded_dn) {
4309 		return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX);
4310 	}
4311 	ldap_value_free(exploded_dn);
4312 
4313 	*account_ou = name;
4314 	return ADS_SUCCESS;
4315 }
4316 
4317 #endif
4318