pyldb: avoid segfault when adding an element with no name
[nivanova/samba-autobuild/.git] / source3 / libads / ldap.c
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
60 static void gotalarm_sig(int signum)
61 {
62         gotalarm = 1;
63 }
64
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
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
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
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  */
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
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
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 = NT_STATUS_UNSUCCESSFUL;
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
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 = NT_STATUS_UNSUCCESSFUL;
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
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  **/
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  **/
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  **/
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  */
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  */
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  */
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  */
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  **/
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
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  **/
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
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
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  **/
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  **/
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  **/
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  **/
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  **/
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  **/
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  **/
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  **/
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[] = {"*", "msDS-SupportedEncryptionTypes", "nTSecurityDescriptor", NULL};
1370
1371         *res = NULL;
1372
1373         /* the easiest way to find a machine account anywhere in the tree
1374            is to look for hostname$ */
1375         if (asprintf(&expr, "(samAccountName=%s$)", machine) == -1) {
1376                 DEBUG(1, ("asprintf failed!\n"));
1377                 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1378         }
1379
1380         status = ads_search(ads, res, expr, attrs);
1381         SAFE_FREE(expr);
1382         return status;
1383 }
1384
1385 /**
1386  * Initialize a list of mods to be used in a modify request
1387  * @param ctx An initialized TALLOC_CTX
1388  * @return allocated ADS_MODLIST
1389  **/
1390 ADS_MODLIST ads_init_mods(TALLOC_CTX *ctx)
1391 {
1392 #define ADS_MODLIST_ALLOC_SIZE 10
1393         LDAPMod **mods;
1394
1395         if ((mods = talloc_zero_array(ctx, LDAPMod *, ADS_MODLIST_ALLOC_SIZE + 1)))
1396                 /* -1 is safety to make sure we don't go over the end.
1397                    need to reset it to NULL before doing ldap modify */
1398                 mods[ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1399
1400         return (ADS_MODLIST)mods;
1401 }
1402
1403
1404 /*
1405   add an attribute to the list, with values list already constructed
1406 */
1407 static ADS_STATUS ads_modlist_add(TALLOC_CTX *ctx, ADS_MODLIST *mods, 
1408                                   int mod_op, const char *name, 
1409                                   const void *_invals)
1410 {
1411         int curmod;
1412         LDAPMod **modlist = (LDAPMod **) *mods;
1413         struct berval **ber_values = NULL;
1414         char **char_values = NULL;
1415
1416         if (!_invals) {
1417                 mod_op = LDAP_MOD_DELETE;
1418         } else {
1419                 if (mod_op & LDAP_MOD_BVALUES) {
1420                         const struct berval **b;
1421                         b = discard_const_p(const struct berval *, _invals);
1422                         ber_values = ads_dup_values(ctx, b);
1423                 } else {
1424                         const char **c;
1425                         c = discard_const_p(const char *, _invals);
1426                         char_values = ads_push_strvals(ctx, c);
1427                 }
1428         }
1429
1430         /* find the first empty slot */
1431         for (curmod=0; modlist[curmod] && modlist[curmod] != (LDAPMod *) -1;
1432              curmod++);
1433         if (modlist[curmod] == (LDAPMod *) -1) {
1434                 if (!(modlist = talloc_realloc(ctx, modlist, LDAPMod *,
1435                                 curmod+ADS_MODLIST_ALLOC_SIZE+1)))
1436                         return ADS_ERROR(LDAP_NO_MEMORY);
1437                 memset(&modlist[curmod], 0, 
1438                        ADS_MODLIST_ALLOC_SIZE*sizeof(LDAPMod *));
1439                 modlist[curmod+ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1440                 *mods = (ADS_MODLIST)modlist;
1441         }
1442
1443         if (!(modlist[curmod] = talloc_zero(ctx, LDAPMod)))
1444                 return ADS_ERROR(LDAP_NO_MEMORY);
1445         modlist[curmod]->mod_type = talloc_strdup(ctx, name);
1446         if (mod_op & LDAP_MOD_BVALUES) {
1447                 modlist[curmod]->mod_bvalues = ber_values;
1448         } else if (mod_op & LDAP_MOD_DELETE) {
1449                 modlist[curmod]->mod_values = NULL;
1450         } else {
1451                 modlist[curmod]->mod_values = char_values;
1452         }
1453
1454         modlist[curmod]->mod_op = mod_op;
1455         return ADS_ERROR(LDAP_SUCCESS);
1456 }
1457
1458 /**
1459  * Add a single string value to a mod list
1460  * @param ctx An initialized TALLOC_CTX
1461  * @param mods An initialized ADS_MODLIST
1462  * @param name The attribute name to add
1463  * @param val The value to add - NULL means DELETE
1464  * @return ADS STATUS indicating success of add
1465  **/
1466 ADS_STATUS ads_mod_str(TALLOC_CTX *ctx, ADS_MODLIST *mods, 
1467                        const char *name, const char *val)
1468 {
1469         const char *values[2];
1470
1471         values[0] = val;
1472         values[1] = NULL;
1473
1474         if (!val)
1475                 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1476         return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE, name, values);
1477 }
1478
1479 /**
1480  * Add an array of string values to a mod list
1481  * @param ctx An initialized TALLOC_CTX
1482  * @param mods An initialized ADS_MODLIST
1483  * @param name The attribute name to add
1484  * @param vals The array of string values to add - NULL means DELETE
1485  * @return ADS STATUS indicating success of add
1486  **/
1487 ADS_STATUS ads_mod_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1488                            const char *name, const char **vals)
1489 {
1490         if (!vals)
1491                 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1492         return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE, 
1493                                name, (const void **) vals);
1494 }
1495
1496 #if 0
1497 /**
1498  * Add a single ber-encoded value to a mod list
1499  * @param ctx An initialized TALLOC_CTX
1500  * @param mods An initialized ADS_MODLIST
1501  * @param name The attribute name to add
1502  * @param val The value to add - NULL means DELETE
1503  * @return ADS STATUS indicating success of add
1504  **/
1505 static ADS_STATUS ads_mod_ber(TALLOC_CTX *ctx, ADS_MODLIST *mods, 
1506                               const char *name, const struct berval *val)
1507 {
1508         const struct berval *values[2];
1509
1510         values[0] = val;
1511         values[1] = NULL;
1512         if (!val)
1513                 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1514         return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE|LDAP_MOD_BVALUES,
1515                                name, (const void **) values);
1516 }
1517 #endif
1518
1519 static void ads_print_error(int ret, LDAP *ld)
1520 {
1521         if (ret != 0) {
1522                 char *ld_error = NULL;
1523                 ldap_get_option(ld, LDAP_OPT_ERROR_STRING, &ld_error);
1524                 DBG_ERR("AD LDAP ERROR: %d (%s): %s\n",
1525                         ret,
1526                         ldap_err2string(ret),
1527                         ld_error);
1528                 SAFE_FREE(ld_error);
1529         }
1530 }
1531
1532 /**
1533  * Perform an ldap modify
1534  * @param ads connection to ads server
1535  * @param mod_dn DistinguishedName to modify
1536  * @param mods list of modifications to perform
1537  * @return status of modify
1538  **/
1539 ADS_STATUS ads_gen_mod(ADS_STRUCT *ads, const char *mod_dn, ADS_MODLIST mods)
1540 {
1541         int ret,i;
1542         char *utf8_dn = NULL;
1543         size_t converted_size;
1544         /* 
1545            this control is needed to modify that contains a currently 
1546            non-existent attribute (but allowable for the object) to run
1547         */
1548         LDAPControl PermitModify = {
1549                 discard_const_p(char, ADS_PERMIT_MODIFY_OID),
1550                 {0, NULL},
1551                 (char) 1};
1552         LDAPControl *controls[2];
1553
1554         DBG_INFO("AD LDAP: Modifying %s\n", mod_dn);
1555
1556         controls[0] = &PermitModify;
1557         controls[1] = NULL;
1558
1559         if (!push_utf8_talloc(talloc_tos(), &utf8_dn, mod_dn, &converted_size)) {
1560                 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1561         }
1562
1563         /* find the end of the list, marked by NULL or -1 */
1564         for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1565         /* make sure the end of the list is NULL */
1566         mods[i] = NULL;
1567         ret = ldap_modify_ext_s(ads->ldap.ld, utf8_dn,
1568                                 (LDAPMod **) mods, controls, NULL);
1569         ads_print_error(ret, ads->ldap.ld);
1570         TALLOC_FREE(utf8_dn);
1571         return ADS_ERROR(ret);
1572 }
1573
1574 /**
1575  * Perform an ldap add
1576  * @param ads connection to ads server
1577  * @param new_dn DistinguishedName to add
1578  * @param mods list of attributes and values for DN
1579  * @return status of add
1580  **/
1581 ADS_STATUS ads_gen_add(ADS_STRUCT *ads, const char *new_dn, ADS_MODLIST mods)
1582 {
1583         int ret, i;
1584         char *utf8_dn = NULL;
1585         size_t converted_size;
1586
1587         DBG_INFO("AD LDAP: Adding %s\n", new_dn);
1588
1589         if (!push_utf8_talloc(talloc_tos(), &utf8_dn, new_dn, &converted_size)) {
1590                 DEBUG(1, ("ads_gen_add: push_utf8_talloc failed!"));
1591                 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1592         }
1593
1594         /* find the end of the list, marked by NULL or -1 */
1595         for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1596         /* make sure the end of the list is NULL */
1597         mods[i] = NULL;
1598
1599         ret = ldap_add_s(ads->ldap.ld, utf8_dn, (LDAPMod**)mods);
1600         ads_print_error(ret, ads->ldap.ld);
1601         TALLOC_FREE(utf8_dn);
1602         return ADS_ERROR(ret);
1603 }
1604
1605 /**
1606  * Delete a DistinguishedName
1607  * @param ads connection to ads server
1608  * @param new_dn DistinguishedName to delete
1609  * @return status of delete
1610  **/
1611 ADS_STATUS ads_del_dn(ADS_STRUCT *ads, char *del_dn)
1612 {
1613         int ret;
1614         char *utf8_dn = NULL;
1615         size_t converted_size;
1616         if (!push_utf8_talloc(talloc_tos(), &utf8_dn, del_dn, &converted_size)) {
1617                 DEBUG(1, ("ads_del_dn: push_utf8_talloc failed!"));
1618                 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1619         }
1620
1621         DBG_INFO("AD LDAP: Deleting %s\n", del_dn);
1622
1623         ret = ldap_delete_s(ads->ldap.ld, utf8_dn);
1624         ads_print_error(ret, ads->ldap.ld);
1625         TALLOC_FREE(utf8_dn);
1626         return ADS_ERROR(ret);
1627 }
1628
1629 /**
1630  * Build an org unit string
1631  *  if org unit is Computers or blank then assume a container, otherwise
1632  *  assume a / separated list of organisational units.
1633  * jmcd: '\' is now used for escapes so certain chars can be in the ou (e.g. #)
1634  * @param ads connection to ads server
1635  * @param org_unit Organizational unit
1636  * @return org unit string - caller must free
1637  **/
1638 char *ads_ou_string(ADS_STRUCT *ads, const char *org_unit)
1639 {
1640         char *ret = NULL;
1641
1642         if (!org_unit || !*org_unit) {
1643
1644                 ret = ads_default_ou_string(ads, DS_GUID_COMPUTERS_CONTAINER);
1645
1646                 /* samba4 might not yet respond to a wellknownobject-query */
1647                 return ret ? ret : SMB_STRDUP("cn=Computers");
1648         }
1649
1650         if (strequal(org_unit, "Computers")) {
1651                 return SMB_STRDUP("cn=Computers");
1652         }
1653
1654         /* jmcd: removed "\\" from the separation chars, because it is
1655            needed as an escape for chars like '#' which are valid in an
1656            OU name */
1657         return ads_build_path(org_unit, "/", "ou=", 1);
1658 }
1659
1660 /**
1661  * Get a org unit string for a well-known GUID
1662  * @param ads connection to ads server
1663  * @param wknguid Well known GUID
1664  * @return org unit string - caller must free
1665  **/
1666 char *ads_default_ou_string(ADS_STRUCT *ads, const char *wknguid)
1667 {
1668         ADS_STATUS status;
1669         LDAPMessage *res = NULL;
1670         char *base, *wkn_dn = NULL, *ret = NULL, **wkn_dn_exp = NULL,
1671                 **bind_dn_exp = NULL;
1672         const char *attrs[] = {"distinguishedName", NULL};
1673         int new_ln, wkn_ln, bind_ln, i;
1674
1675         if (wknguid == NULL) {
1676                 return NULL;
1677         }
1678
1679         if (asprintf(&base, "<WKGUID=%s,%s>", wknguid, ads->config.bind_path ) == -1) {
1680                 DEBUG(1, ("asprintf failed!\n"));
1681                 return NULL;
1682         }
1683
1684         status = ads_search_dn(ads, &res, base, attrs);
1685         if (!ADS_ERR_OK(status)) {
1686                 DEBUG(1,("Failed while searching for: %s\n", base));
1687                 goto out;
1688         }
1689
1690         if (ads_count_replies(ads, res) != 1) {
1691                 goto out;
1692         }
1693
1694         /* substitute the bind-path from the well-known-guid-search result */
1695         wkn_dn = ads_get_dn(ads, talloc_tos(), res);
1696         if (!wkn_dn) {
1697                 goto out;
1698         }
1699
1700         wkn_dn_exp = ldap_explode_dn(wkn_dn, 0);
1701         if (!wkn_dn_exp) {
1702                 goto out;
1703         }
1704
1705         bind_dn_exp = ldap_explode_dn(ads->config.bind_path, 0);
1706         if (!bind_dn_exp) {
1707                 goto out;
1708         }
1709
1710         for (wkn_ln=0; wkn_dn_exp[wkn_ln]; wkn_ln++)
1711                 ;
1712         for (bind_ln=0; bind_dn_exp[bind_ln]; bind_ln++)
1713                 ;
1714
1715         new_ln = wkn_ln - bind_ln;
1716
1717         ret = SMB_STRDUP(wkn_dn_exp[0]);
1718         if (!ret) {
1719                 goto out;
1720         }
1721
1722         for (i=1; i < new_ln; i++) {
1723                 char *s = NULL;
1724
1725                 if (asprintf(&s, "%s,%s", ret, wkn_dn_exp[i]) == -1) {
1726                         SAFE_FREE(ret);
1727                         goto out;
1728                 }
1729
1730                 SAFE_FREE(ret);
1731                 ret = SMB_STRDUP(s);
1732                 free(s);
1733                 if (!ret) {
1734                         goto out;
1735                 }
1736         }
1737
1738  out:
1739         SAFE_FREE(base);
1740         ads_msgfree(ads, res);
1741         TALLOC_FREE(wkn_dn);
1742         if (wkn_dn_exp) {
1743                 ldap_value_free(wkn_dn_exp);
1744         }
1745         if (bind_dn_exp) {
1746                 ldap_value_free(bind_dn_exp);
1747         }
1748
1749         return ret;
1750 }
1751
1752 /**
1753  * Adds (appends) an item to an attribute array, rather then
1754  * replacing the whole list
1755  * @param ctx An initialized TALLOC_CTX
1756  * @param mods An initialized ADS_MODLIST
1757  * @param name name of the ldap attribute to append to
1758  * @param vals an array of values to add
1759  * @return status of addition
1760  **/
1761
1762 ADS_STATUS ads_add_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1763                                 const char *name, const char **vals)
1764 {
1765         return ads_modlist_add(ctx, mods, LDAP_MOD_ADD, name,
1766                                (const void *) vals);
1767 }
1768
1769 /**
1770  * Determines the an account's current KVNO via an LDAP lookup
1771  * @param ads An initialized ADS_STRUCT
1772  * @param account_name the NT samaccountname.
1773  * @return the kvno for the account, or -1 in case of a failure.
1774  **/
1775
1776 uint32_t ads_get_kvno(ADS_STRUCT *ads, const char *account_name)
1777 {
1778         LDAPMessage *res = NULL;
1779         uint32_t kvno = (uint32_t)-1;      /* -1 indicates a failure */
1780         char *filter;
1781         const char *attrs[] = {"msDS-KeyVersionNumber", NULL};
1782         char *dn_string = NULL;
1783         ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS);
1784
1785         DEBUG(5,("ads_get_kvno: Searching for account %s\n", account_name));
1786         if (asprintf(&filter, "(samAccountName=%s)", account_name) == -1) {
1787                 return kvno;
1788         }
1789         ret = ads_search(ads, &res, filter, attrs);
1790         SAFE_FREE(filter);
1791         if (!ADS_ERR_OK(ret) || (ads_count_replies(ads, res) != 1)) {
1792                 DEBUG(1,("ads_get_kvno: Account for %s not found.\n", account_name));
1793                 ads_msgfree(ads, res);
1794                 return kvno;
1795         }
1796
1797         dn_string = ads_get_dn(ads, talloc_tos(), res);
1798         if (!dn_string) {
1799                 DEBUG(0,("ads_get_kvno: out of memory.\n"));
1800                 ads_msgfree(ads, res);
1801                 return kvno;
1802         }
1803         DEBUG(5,("ads_get_kvno: Using: %s\n", dn_string));
1804         TALLOC_FREE(dn_string);
1805
1806         /* ---------------------------------------------------------
1807          * 0 is returned as a default KVNO from this point on...
1808          * This is done because Windows 2000 does not support key
1809          * version numbers.  Chances are that a failure in the next
1810          * step is simply due to Windows 2000 being used for a
1811          * domain controller. */
1812         kvno = 0;
1813
1814         if (!ads_pull_uint32(ads, res, "msDS-KeyVersionNumber", &kvno)) {
1815                 DEBUG(3,("ads_get_kvno: Error Determining KVNO!\n"));
1816                 DEBUG(3,("ads_get_kvno: Windows 2000 does not support KVNO's, so this may be normal.\n"));
1817                 ads_msgfree(ads, res);
1818                 return kvno;
1819         }
1820
1821         /* Success */
1822         DEBUG(5,("ads_get_kvno: Looked Up KVNO of: %d\n", kvno));
1823         ads_msgfree(ads, res);
1824         return kvno;
1825 }
1826
1827 /**
1828  * Determines the computer account's current KVNO via an LDAP lookup
1829  * @param ads An initialized ADS_STRUCT
1830  * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1831  * @return the kvno for the computer account, or -1 in case of a failure.
1832  **/
1833
1834 uint32_t ads_get_machine_kvno(ADS_STRUCT *ads, const char *machine_name)
1835 {
1836         char *computer_account = NULL;
1837         uint32_t kvno = -1;
1838
1839         if (asprintf(&computer_account, "%s$", machine_name) < 0) {
1840                 return kvno;
1841         }
1842
1843         kvno = ads_get_kvno(ads, computer_account);
1844         free(computer_account);
1845
1846         return kvno;
1847 }
1848
1849 /**
1850  * This clears out all registered spn's for a given hostname
1851  * @param ads An initilaized ADS_STRUCT
1852  * @param machine_name the NetBIOS name of the computer.
1853  * @return 0 upon success, non-zero otherwise.
1854  **/
1855
1856 ADS_STATUS ads_clear_service_principal_names(ADS_STRUCT *ads, const char *machine_name)
1857 {
1858         TALLOC_CTX *ctx;
1859         LDAPMessage *res = NULL;
1860         ADS_MODLIST mods;
1861         const char *servicePrincipalName[1] = {NULL};
1862         ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS);
1863         char *dn_string = NULL;
1864
1865         ret = ads_find_machine_acct(ads, &res, machine_name);
1866         if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
1867                 DEBUG(5,("ads_clear_service_principal_names: WARNING: Host Account for %s not found... skipping operation.\n", machine_name));
1868                 DEBUG(5,("ads_clear_service_principal_names: WARNING: Service Principals for %s have NOT been cleared.\n", machine_name));
1869                 ads_msgfree(ads, res);
1870                 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1871         }
1872
1873         DEBUG(5,("ads_clear_service_principal_names: Host account for %s found\n", machine_name));
1874         ctx = talloc_init("ads_clear_service_principal_names");
1875         if (!ctx) {
1876                 ads_msgfree(ads, res);
1877                 return ADS_ERROR(LDAP_NO_MEMORY);
1878         }
1879
1880         if (!(mods = ads_init_mods(ctx))) {
1881                 talloc_destroy(ctx);
1882                 ads_msgfree(ads, res);
1883                 return ADS_ERROR(LDAP_NO_MEMORY);
1884         }
1885         ret = ads_mod_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1886         if (!ADS_ERR_OK(ret)) {
1887                 DEBUG(1,("ads_clear_service_principal_names: Error creating strlist.\n"));
1888                 ads_msgfree(ads, res);
1889                 talloc_destroy(ctx);
1890                 return ret;
1891         }
1892         dn_string = ads_get_dn(ads, talloc_tos(), res);
1893         if (!dn_string) {
1894                 talloc_destroy(ctx);
1895                 ads_msgfree(ads, res);
1896                 return ADS_ERROR(LDAP_NO_MEMORY);
1897         }
1898         ret = ads_gen_mod(ads, dn_string, mods);
1899         TALLOC_FREE(dn_string);
1900         if (!ADS_ERR_OK(ret)) {
1901                 DEBUG(1,("ads_clear_service_principal_names: Error: Updating Service Principals for machine %s in LDAP\n",
1902                         machine_name));
1903                 ads_msgfree(ads, res);
1904                 talloc_destroy(ctx);
1905                 return ret;
1906         }
1907
1908         ads_msgfree(ads, res);
1909         talloc_destroy(ctx);
1910         return ret;
1911 }
1912
1913 /**
1914  * @brief Search for an element in a string array.
1915  *
1916  * @param[in]  el_array  The string array to search.
1917  *
1918  * @param[in]  num_el    The number of elements in the string array.
1919  *
1920  * @param[in]  el        The string to search.
1921  *
1922  * @return               True if found, false if not.
1923  */
1924 bool ads_element_in_array(const char **el_array, size_t num_el, const char *el)
1925 {
1926         size_t i;
1927
1928         if (el_array == NULL || num_el == 0 || el == NULL) {
1929                 return false;
1930         }
1931
1932         for (i = 0; i < num_el && el_array[i] != NULL; i++) {
1933                 int cmp;
1934
1935                 cmp = strcasecmp_m(el_array[i], el);
1936                 if (cmp == 0) {
1937                         return true;
1938                 }
1939         }
1940
1941         return false;
1942 }
1943
1944 /**
1945  * @brief This gets the service principal names of an existing computer account.
1946  *
1947  * @param[in]  mem_ctx      The memory context to use to allocate the spn array.
1948  *
1949  * @param[in]  ads          The ADS context to use.
1950  *
1951  * @param[in]  machine_name The NetBIOS name of the computer, which is used to
1952  *                          identify the computer account.
1953  *
1954  * @param[in]  spn_array    A pointer to store the array for SPNs.
1955  *
1956  * @param[in]  num_spns     The number of principals stored in the array.
1957  *
1958  * @return                  0 on success, or a ADS error if a failure occurred.
1959  */
1960 ADS_STATUS ads_get_service_principal_names(TALLOC_CTX *mem_ctx,
1961                                            ADS_STRUCT *ads,
1962                                            const char *machine_name,
1963                                            char ***spn_array,
1964                                            size_t *num_spns)
1965 {
1966         ADS_STATUS status;
1967         LDAPMessage *res = NULL;
1968         int count;
1969
1970         status = ads_find_machine_acct(ads,
1971                                        &res,
1972                                        machine_name);
1973         if (!ADS_ERR_OK(status)) {
1974                 DEBUG(1,("Host Account for %s not found... skipping operation.\n",
1975                          machine_name));
1976                 return status;
1977         }
1978
1979         count = ads_count_replies(ads, res);
1980         if (count != 1) {
1981                 status = ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1982                 goto done;
1983         }
1984
1985         *spn_array = ads_pull_strings(ads,
1986                                       mem_ctx,
1987                                       res,
1988                                       "servicePrincipalName",
1989                                       num_spns);
1990         if (*spn_array == NULL) {
1991                 DEBUG(1, ("Host account for %s does not have service principal "
1992                           "names.\n",
1993                           machine_name));
1994                 status = ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1995                 goto done;
1996         }
1997
1998 done:
1999         ads_msgfree(ads, res);
2000
2001         return status;
2002 }
2003
2004 /**
2005  * This adds a service principal name to an existing computer account
2006  * (found by hostname) in AD.
2007  * @param ads An initialized ADS_STRUCT
2008  * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
2009  * @param spns An array or strings for the service principals to add,
2010  *        i.e. 'cifs/machine_name', 'http/machine.full.domain.com' etc.
2011  * @return 0 upon sucess, or non-zero if a failure occurs
2012  **/
2013
2014 ADS_STATUS ads_add_service_principal_names(ADS_STRUCT *ads,
2015                                            const char *machine_name,
2016                                            const char **spns)
2017 {
2018         ADS_STATUS ret;
2019         TALLOC_CTX *ctx;
2020         LDAPMessage *res = NULL;
2021         ADS_MODLIST mods;
2022         char *dn_string = NULL;
2023         const char **servicePrincipalName = spns;
2024
2025         ret = ads_find_machine_acct(ads, &res, machine_name);
2026         if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
2027                 DEBUG(1,("ads_add_service_principal_name: WARNING: Host Account for %s not found... skipping operation.\n",
2028                         machine_name));
2029                 DEBUG(1,("ads_add_service_principal_name: WARNING: Service Principals have NOT been added.\n"));
2030                 ads_msgfree(ads, res);
2031                 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
2032         }
2033
2034         DEBUG(1,("ads_add_service_principal_name: Host account for %s found\n", machine_name));
2035         if (!(ctx = talloc_init("ads_add_service_principal_name"))) {
2036                 ads_msgfree(ads, res);
2037                 return ADS_ERROR(LDAP_NO_MEMORY);
2038         }
2039
2040         DEBUG(5,("ads_add_service_principal_name: INFO: "
2041                 "Adding %s to host %s\n",
2042                 spns[0] ? "N/A" : spns[0], machine_name));
2043
2044
2045         DEBUG(5,("ads_add_service_principal_name: INFO: "
2046                 "Adding %s to host %s\n",
2047                 spns[1] ? "N/A" : spns[1], machine_name));
2048
2049         if ( (mods = ads_init_mods(ctx)) == NULL ) {
2050                 ret = ADS_ERROR(LDAP_NO_MEMORY);
2051                 goto out;
2052         }
2053
2054         ret = ads_add_strlist(ctx,
2055                               &mods,
2056                               "servicePrincipalName",
2057                               servicePrincipalName);
2058         if (!ADS_ERR_OK(ret)) {
2059                 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
2060                 goto out;
2061         }
2062
2063         if ( (dn_string = ads_get_dn(ads, ctx, res)) == NULL ) {
2064                 ret = ADS_ERROR(LDAP_NO_MEMORY);
2065                 goto out;
2066         }
2067
2068         ret = ads_gen_mod(ads, dn_string, mods);
2069         if (!ADS_ERR_OK(ret)) {
2070                 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
2071                 goto out;
2072         }
2073
2074  out:
2075         TALLOC_FREE( ctx );
2076         ads_msgfree(ads, res);
2077         return ret;
2078 }
2079
2080 /**
2081  * adds a machine account to the ADS server
2082  * @param ads An intialized ADS_STRUCT
2083  * @param machine_name - the NetBIOS machine name of this account.
2084  * @param account_type A number indicating the type of account to create
2085  * @param org_unit The LDAP path in which to place this account
2086  * @return 0 upon success, or non-zero otherwise
2087 **/
2088
2089 ADS_STATUS ads_create_machine_acct(ADS_STRUCT *ads,
2090                                    const char *machine_name,
2091                                    const char *org_unit,
2092                                    uint32_t etype_list)
2093 {
2094         ADS_STATUS ret;
2095         char *samAccountName, *controlstr;
2096         TALLOC_CTX *ctx;
2097         ADS_MODLIST mods;
2098         char *machine_escaped = NULL;
2099         char *new_dn;
2100         const char *objectClass[] = {"top", "person", "organizationalPerson",
2101                                      "user", "computer", NULL};
2102         LDAPMessage *res = NULL;
2103         uint32_t acct_control = ( UF_WORKSTATION_TRUST_ACCOUNT |\
2104                                 UF_DONT_EXPIRE_PASSWD |\
2105                                 UF_ACCOUNTDISABLE );
2106         uint32_t func_level = 0;
2107
2108         ret = ads_domain_func_level(ads, &func_level);
2109         if (!ADS_ERR_OK(ret)) {
2110                 return ret;
2111         }
2112
2113         if (!(ctx = talloc_init("ads_add_machine_acct")))
2114                 return ADS_ERROR(LDAP_NO_MEMORY);
2115
2116         ret = ADS_ERROR(LDAP_NO_MEMORY);
2117
2118         machine_escaped = escape_rdn_val_string_alloc(machine_name);
2119         if (!machine_escaped) {
2120                 goto done;
2121         }
2122
2123         ret = ads_find_machine_acct(ads, &res, machine_escaped);
2124         ads_msgfree(ads, res);
2125         if (ADS_ERR_OK(ret)) {
2126                 DBG_DEBUG("Host account for %s already exists.\n",
2127                                 machine_escaped);
2128                 ret = ADS_ERROR_LDAP(LDAP_ALREADY_EXISTS);
2129                 goto done;
2130         }
2131
2132         new_dn = talloc_asprintf(ctx, "cn=%s,%s", machine_escaped, org_unit);
2133         samAccountName = talloc_asprintf(ctx, "%s$", machine_name);
2134
2135         if ( !new_dn || !samAccountName ) {
2136                 goto done;
2137         }
2138
2139         if (!(controlstr = talloc_asprintf(ctx, "%u", acct_control))) {
2140                 goto done;
2141         }
2142
2143         if (!(mods = ads_init_mods(ctx))) {
2144                 goto done;
2145         }
2146
2147         ads_mod_str(ctx, &mods, "cn", machine_name);
2148         ads_mod_str(ctx, &mods, "sAMAccountName", samAccountName);
2149         ads_mod_strlist(ctx, &mods, "objectClass", objectClass);
2150         ads_mod_str(ctx, &mods, "userAccountControl", controlstr);
2151
2152         if (func_level >= DS_DOMAIN_FUNCTION_2008) {
2153                 const char *etype_list_str;
2154
2155                 etype_list_str = talloc_asprintf(ctx, "%d", (int)etype_list);
2156                 if (etype_list_str == NULL) {
2157                         goto done;
2158                 }
2159                 ads_mod_str(ctx, &mods, "msDS-SupportedEncryptionTypes",
2160                             etype_list_str);
2161         }
2162
2163         ret = ads_gen_add(ads, new_dn, mods);
2164
2165 done:
2166         SAFE_FREE(machine_escaped);
2167         talloc_destroy(ctx);
2168
2169         return ret;
2170 }
2171
2172 /**
2173  * move a machine account to another OU on the ADS server
2174  * @param ads - An intialized ADS_STRUCT
2175  * @param machine_name - the NetBIOS machine name of this account.
2176  * @param org_unit - The LDAP path in which to place this account
2177  * @param moved - whether we moved the machine account (optional)
2178  * @return 0 upon success, or non-zero otherwise
2179 **/
2180
2181 ADS_STATUS ads_move_machine_acct(ADS_STRUCT *ads, const char *machine_name, 
2182                                  const char *org_unit, bool *moved)
2183 {
2184         ADS_STATUS rc;
2185         int ldap_status;
2186         LDAPMessage *res = NULL;
2187         char *filter = NULL;
2188         char *computer_dn = NULL;
2189         char *parent_dn;
2190         char *computer_rdn = NULL;
2191         bool need_move = False;
2192
2193         if (asprintf(&filter, "(samAccountName=%s$)", machine_name) == -1) {
2194                 rc = ADS_ERROR(LDAP_NO_MEMORY);
2195                 goto done;
2196         }
2197
2198         /* Find pre-existing machine */
2199         rc = ads_search(ads, &res, filter, NULL);
2200         if (!ADS_ERR_OK(rc)) {
2201                 goto done;
2202         }
2203
2204         computer_dn = ads_get_dn(ads, talloc_tos(), res);
2205         if (!computer_dn) {
2206                 rc = ADS_ERROR(LDAP_NO_MEMORY);
2207                 goto done;
2208         }
2209
2210         parent_dn = ads_parent_dn(computer_dn);
2211         if (strequal(parent_dn, org_unit)) {
2212                 goto done;
2213         }
2214
2215         need_move = True;
2216
2217         if (asprintf(&computer_rdn, "CN=%s", machine_name) == -1) {
2218                 rc = ADS_ERROR(LDAP_NO_MEMORY);
2219                 goto done;
2220         }
2221
2222         ldap_status = ldap_rename_s(ads->ldap.ld, computer_dn, computer_rdn, 
2223                                     org_unit, 1, NULL, NULL);
2224         rc = ADS_ERROR(ldap_status);
2225
2226 done:
2227         ads_msgfree(ads, res);
2228         SAFE_FREE(filter);
2229         TALLOC_FREE(computer_dn);
2230         SAFE_FREE(computer_rdn);
2231
2232         if (!ADS_ERR_OK(rc)) {
2233                 need_move = False;
2234         }
2235
2236         if (moved) {
2237                 *moved = need_move;
2238         }
2239
2240         return rc;
2241 }
2242
2243 /*
2244   dump a binary result from ldap
2245 */
2246 static void dump_binary(ADS_STRUCT *ads, const char *field, struct berval **values)
2247 {
2248         size_t i;
2249         for (i=0; values[i]; i++) {
2250                 ber_len_t j;
2251                 printf("%s: ", field);
2252                 for (j=0; j<values[i]->bv_len; j++) {
2253                         printf("%02X", (unsigned char)values[i]->bv_val[j]);
2254                 }
2255                 printf("\n");
2256         }
2257 }
2258
2259 static void dump_guid(ADS_STRUCT *ads, const char *field, struct berval **values)
2260 {
2261         int i;
2262         for (i=0; values[i]; i++) {
2263                 NTSTATUS status;
2264                 DATA_BLOB in = data_blob_const(values[i]->bv_val, values[i]->bv_len);
2265                 struct GUID guid;
2266
2267                 status = GUID_from_ndr_blob(&in, &guid);
2268                 if (NT_STATUS_IS_OK(status)) {
2269                         printf("%s: %s\n", field, GUID_string(talloc_tos(), &guid));
2270                 } else {
2271                         printf("%s: INVALID GUID\n", field);
2272                 }
2273         }
2274 }
2275
2276 /*
2277   dump a sid result from ldap
2278 */
2279 static void dump_sid(ADS_STRUCT *ads, const char *field, struct berval **values)
2280 {
2281         int i;
2282         for (i=0; values[i]; i++) {
2283                 ssize_t ret;
2284                 struct dom_sid sid;
2285                 struct dom_sid_buf tmp;
2286                 ret = sid_parse((const uint8_t *)values[i]->bv_val,
2287                                 values[i]->bv_len, &sid);
2288                 if (ret == -1) {
2289                         return;
2290                 }
2291                 printf("%s: %s\n", field, dom_sid_str_buf(&sid, &tmp));
2292         }
2293 }
2294
2295 /*
2296   dump ntSecurityDescriptor
2297 */
2298 static void dump_sd(ADS_STRUCT *ads, const char *filed, struct berval **values)
2299 {
2300         TALLOC_CTX *frame = talloc_stackframe();
2301         struct security_descriptor *psd;
2302         NTSTATUS status;
2303
2304         status = unmarshall_sec_desc(talloc_tos(), (uint8_t *)values[0]->bv_val,
2305                                      values[0]->bv_len, &psd);
2306         if (!NT_STATUS_IS_OK(status)) {
2307                 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
2308                           nt_errstr(status)));
2309                 TALLOC_FREE(frame);
2310                 return;
2311         }
2312
2313         if (psd) {
2314                 ads_disp_sd(ads, talloc_tos(), psd);
2315         }
2316
2317         TALLOC_FREE(frame);
2318 }
2319
2320 /*
2321   dump a string result from ldap
2322 */
2323 static void dump_string(const char *field, char **values)
2324 {
2325         int i;
2326         for (i=0; values[i]; i++) {
2327                 printf("%s: %s\n", field, values[i]);
2328         }
2329 }
2330
2331 /*
2332   dump a field from LDAP on stdout
2333   used for debugging
2334 */
2335
2336 static bool ads_dump_field(ADS_STRUCT *ads, char *field, void **values, void *data_area)
2337 {
2338         const struct {
2339                 const char *name;
2340                 bool string;
2341                 void (*handler)(ADS_STRUCT *, const char *, struct berval **);
2342         } handlers[] = {
2343                 {"objectGUID", False, dump_guid},
2344                 {"netbootGUID", False, dump_guid},
2345                 {"nTSecurityDescriptor", False, dump_sd},
2346                 {"dnsRecord", False, dump_binary},
2347                 {"objectSid", False, dump_sid},
2348                 {"tokenGroups", False, dump_sid},
2349                 {"tokenGroupsNoGCAcceptable", False, dump_sid},
2350                 {"tokengroupsGlobalandUniversal", False, dump_sid},
2351                 {"mS-DS-CreatorSID", False, dump_sid},
2352                 {"msExchMailboxGuid", False, dump_guid},
2353                 {NULL, True, NULL}
2354         };
2355         int i;
2356
2357         if (!field) { /* must be end of an entry */
2358                 printf("\n");
2359                 return False;
2360         }
2361
2362         for (i=0; handlers[i].name; i++) {
2363                 if (strcasecmp_m(handlers[i].name, field) == 0) {
2364                         if (!values) /* first time, indicate string or not */
2365                                 return handlers[i].string;
2366                         handlers[i].handler(ads, field, (struct berval **) values);
2367                         break;
2368                 }
2369         }
2370         if (!handlers[i].name) {
2371                 if (!values) /* first time, indicate string conversion */
2372                         return True;
2373                 dump_string(field, (char **)values);
2374         }
2375         return False;
2376 }
2377
2378 /**
2379  * Dump a result from LDAP on stdout
2380  *  used for debugging
2381  * @param ads connection to ads server
2382  * @param res Results to dump
2383  **/
2384
2385  void ads_dump(ADS_STRUCT *ads, LDAPMessage *res)
2386 {
2387         ads_process_results(ads, res, ads_dump_field, NULL);
2388 }
2389
2390 /**
2391  * Walk through results, calling a function for each entry found.
2392  *  The function receives a field name, a berval * array of values,
2393  *  and a data area passed through from the start.  The function is
2394  *  called once with null for field and values at the end of each
2395  *  entry.
2396  * @param ads connection to ads server
2397  * @param res Results to process
2398  * @param fn Function for processing each result
2399  * @param data_area user-defined area to pass to function
2400  **/
2401  void ads_process_results(ADS_STRUCT *ads, LDAPMessage *res,
2402                           bool (*fn)(ADS_STRUCT *, char *, void **, void *),
2403                           void *data_area)
2404 {
2405         LDAPMessage *msg;
2406         TALLOC_CTX *ctx;
2407         size_t converted_size;
2408
2409         if (!(ctx = talloc_init("ads_process_results")))
2410                 return;
2411
2412         for (msg = ads_first_entry(ads, res); msg; 
2413              msg = ads_next_entry(ads, msg)) {
2414                 char *utf8_field;
2415                 BerElement *b;
2416
2417                 for (utf8_field=ldap_first_attribute(ads->ldap.ld,
2418                                                      (LDAPMessage *)msg,&b); 
2419                      utf8_field;
2420                      utf8_field=ldap_next_attribute(ads->ldap.ld,
2421                                                     (LDAPMessage *)msg,b)) {
2422                         struct berval **ber_vals;
2423                         char **str_vals;
2424                         char **utf8_vals;
2425                         char *field;
2426                         bool string; 
2427
2428                         if (!pull_utf8_talloc(ctx, &field, utf8_field,
2429                                               &converted_size))
2430                         {
2431                                 DEBUG(0,("ads_process_results: "
2432                                          "pull_utf8_talloc failed: %s",
2433                                          strerror(errno)));
2434                         }
2435
2436                         string = fn(ads, field, NULL, data_area);
2437
2438                         if (string) {
2439                                 const char **p;
2440
2441                                 utf8_vals = ldap_get_values(ads->ldap.ld,
2442                                                  (LDAPMessage *)msg, field);
2443                                 p = discard_const_p(const char *, utf8_vals);
2444                                 str_vals = ads_pull_strvals(ctx, p);
2445                                 fn(ads, field, (void **) str_vals, data_area);
2446                                 ldap_value_free(utf8_vals);
2447                         } else {
2448                                 ber_vals = ldap_get_values_len(ads->ldap.ld, 
2449                                                  (LDAPMessage *)msg, field);
2450                                 fn(ads, field, (void **) ber_vals, data_area);
2451
2452                                 ldap_value_free_len(ber_vals);
2453                         }
2454                         ldap_memfree(utf8_field);
2455                 }
2456                 ber_free(b, 0);
2457                 talloc_free_children(ctx);
2458                 fn(ads, NULL, NULL, data_area); /* completed an entry */
2459
2460         }
2461         talloc_destroy(ctx);
2462 }
2463
2464 /**
2465  * count how many replies are in a LDAPMessage
2466  * @param ads connection to ads server
2467  * @param res Results to count
2468  * @return number of replies
2469  **/
2470 int ads_count_replies(ADS_STRUCT *ads, void *res)
2471 {
2472         return ldap_count_entries(ads->ldap.ld, (LDAPMessage *)res);
2473 }
2474
2475 /**
2476  * pull the first entry from a ADS result
2477  * @param ads connection to ads server
2478  * @param res Results of search
2479  * @return first entry from result
2480  **/
2481  LDAPMessage *ads_first_entry(ADS_STRUCT *ads, LDAPMessage *res)
2482 {
2483         return ldap_first_entry(ads->ldap.ld, res);
2484 }
2485
2486 /**
2487  * pull the next entry from a ADS result
2488  * @param ads connection to ads server
2489  * @param res Results of search
2490  * @return next entry from result
2491  **/
2492  LDAPMessage *ads_next_entry(ADS_STRUCT *ads, LDAPMessage *res)
2493 {
2494         return ldap_next_entry(ads->ldap.ld, res);
2495 }
2496
2497 /**
2498  * pull the first message from a ADS result
2499  * @param ads connection to ads server
2500  * @param res Results of search
2501  * @return first message from result
2502  **/
2503  LDAPMessage *ads_first_message(ADS_STRUCT *ads, LDAPMessage *res)
2504 {
2505         return ldap_first_message(ads->ldap.ld, res);
2506 }
2507
2508 /**
2509  * pull the next message from a ADS result
2510  * @param ads connection to ads server
2511  * @param res Results of search
2512  * @return next message from result
2513  **/
2514  LDAPMessage *ads_next_message(ADS_STRUCT *ads, LDAPMessage *res)
2515 {
2516         return ldap_next_message(ads->ldap.ld, res);
2517 }
2518
2519 /**
2520  * pull a single string from a ADS result
2521  * @param ads connection to ads server
2522  * @param mem_ctx TALLOC_CTX to use for allocating result string
2523  * @param msg Results of search
2524  * @param field Attribute to retrieve
2525  * @return Result string in talloc context
2526  **/
2527  char *ads_pull_string(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, LDAPMessage *msg,
2528                        const char *field)
2529 {
2530         char **values;
2531         char *ret = NULL;
2532         char *ux_string;
2533         size_t converted_size;
2534
2535         values = ldap_get_values(ads->ldap.ld, msg, field);
2536         if (!values)
2537                 return NULL;
2538
2539         if (values[0] && pull_utf8_talloc(mem_ctx, &ux_string, values[0],
2540                                           &converted_size))
2541         {
2542                 ret = ux_string;
2543         }
2544         ldap_value_free(values);
2545         return ret;
2546 }
2547
2548 /**
2549  * pull an array of strings from a ADS result
2550  * @param ads connection to ads server
2551  * @param mem_ctx TALLOC_CTX to use for allocating result string
2552  * @param msg Results of search
2553  * @param field Attribute to retrieve
2554  * @return Result strings in talloc context
2555  **/
2556  char **ads_pull_strings(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2557                          LDAPMessage *msg, const char *field,
2558                          size_t *num_values)
2559 {
2560         char **values;
2561         char **ret = NULL;
2562         size_t i, converted_size;
2563
2564         values = ldap_get_values(ads->ldap.ld, msg, field);
2565         if (!values)
2566                 return NULL;
2567
2568         *num_values = ldap_count_values(values);
2569
2570         ret = talloc_array(mem_ctx, char *, *num_values + 1);
2571         if (!ret) {
2572                 ldap_value_free(values);
2573                 return NULL;
2574         }
2575
2576         for (i=0;i<*num_values;i++) {
2577                 if (!pull_utf8_talloc(mem_ctx, &ret[i], values[i],
2578                                       &converted_size))
2579                 {
2580                         ldap_value_free(values);
2581                         return NULL;
2582                 }
2583         }
2584         ret[i] = NULL;
2585
2586         ldap_value_free(values);
2587         return ret;
2588 }
2589
2590 /**
2591  * pull an array of strings from a ADS result 
2592  *  (handle large multivalue attributes with range retrieval)
2593  * @param ads connection to ads server
2594  * @param mem_ctx TALLOC_CTX to use for allocating result string
2595  * @param msg Results of search
2596  * @param field Attribute to retrieve
2597  * @param current_strings strings returned by a previous call to this function
2598  * @param next_attribute The next query should ask for this attribute
2599  * @param num_values How many values did we get this time?
2600  * @param more_values Are there more values to get?
2601  * @return Result strings in talloc context
2602  **/
2603  char **ads_pull_strings_range(ADS_STRUCT *ads, 
2604                                TALLOC_CTX *mem_ctx,
2605                                LDAPMessage *msg, const char *field,
2606                                char **current_strings,
2607                                const char **next_attribute,
2608                                size_t *num_strings,
2609                                bool *more_strings)
2610 {
2611         char *attr;
2612         char *expected_range_attrib, *range_attr;
2613         BerElement *ptr = NULL;
2614         char **strings;
2615         char **new_strings;
2616         size_t num_new_strings;
2617         unsigned long int range_start;
2618         unsigned long int range_end;
2619
2620         /* we might have been given the whole lot anyway */
2621         if ((strings = ads_pull_strings(ads, mem_ctx, msg, field, num_strings))) {
2622                 *more_strings = False;
2623                 return strings;
2624         }
2625
2626         expected_range_attrib = talloc_asprintf(mem_ctx, "%s;Range=", field);
2627
2628         /* look for Range result */
2629         for (attr = ldap_first_attribute(ads->ldap.ld, (LDAPMessage *)msg, &ptr); 
2630              attr; 
2631              attr = ldap_next_attribute(ads->ldap.ld, (LDAPMessage *)msg, ptr)) {
2632                 /* we ignore the fact that this is utf8, as all attributes are ascii... */
2633                 if (strnequal(attr, expected_range_attrib, strlen(expected_range_attrib))) {
2634                         range_attr = attr;
2635                         break;
2636                 }
2637                 ldap_memfree(attr);
2638         }
2639         if (!attr) {
2640                 ber_free(ptr, 0);
2641                 /* nothing here - this field is just empty */
2642                 *more_strings = False;
2643                 return NULL;
2644         }
2645
2646         if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-%lu", 
2647                    &range_start, &range_end) == 2) {
2648                 *more_strings = True;
2649         } else {
2650                 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-*", 
2651                            &range_start) == 1) {
2652                         *more_strings = False;
2653                 } else {
2654                         DEBUG(1, ("ads_pull_strings_range:  Cannot parse Range attriubte (%s)\n", 
2655                                   range_attr));
2656                         ldap_memfree(range_attr);
2657                         *more_strings = False;
2658                         return NULL;
2659                 }
2660         }
2661
2662         if ((*num_strings) != range_start) {
2663                 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) doesn't start at %u, but at %lu"
2664                           " - aborting range retreival\n",
2665                           range_attr, (unsigned int)(*num_strings) + 1, range_start));
2666                 ldap_memfree(range_attr);
2667                 *more_strings = False;
2668                 return NULL;
2669         }
2670
2671         new_strings = ads_pull_strings(ads, mem_ctx, msg, range_attr, &num_new_strings);
2672
2673         if (*more_strings && ((*num_strings + num_new_strings) != (range_end + 1))) {
2674                 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) tells us we have %lu "
2675                           "strings in this bunch, but we only got %lu - aborting range retreival\n",
2676                           range_attr, (unsigned long int)range_end - range_start + 1, 
2677                           (unsigned long int)num_new_strings));
2678                 ldap_memfree(range_attr);
2679                 *more_strings = False;
2680                 return NULL;
2681         }
2682
2683         strings = talloc_realloc(mem_ctx, current_strings, char *,
2684                                  *num_strings + num_new_strings);
2685
2686         if (strings == NULL) {
2687                 ldap_memfree(range_attr);
2688                 *more_strings = False;
2689                 return NULL;
2690         }
2691
2692         if (new_strings && num_new_strings) {
2693                 memcpy(&strings[*num_strings], new_strings,
2694                        sizeof(*new_strings) * num_new_strings);
2695         }
2696
2697         (*num_strings) += num_new_strings;
2698
2699         if (*more_strings) {
2700                 *next_attribute = talloc_asprintf(mem_ctx,
2701                                                   "%s;range=%d-*", 
2702                                                   field,
2703                                                   (int)*num_strings);
2704
2705                 if (!*next_attribute) {
2706                         DEBUG(1, ("talloc_asprintf for next attribute failed!\n"));
2707                         ldap_memfree(range_attr);
2708                         *more_strings = False;
2709                         return NULL;
2710                 }
2711         }
2712
2713         ldap_memfree(range_attr);
2714
2715         return strings;
2716 }
2717
2718 /**
2719  * pull a single uint32_t from a ADS result
2720  * @param ads connection to ads server
2721  * @param msg Results of search
2722  * @param field Attribute to retrieve
2723  * @param v Pointer to int to store result
2724  * @return boolean inidicating success
2725 */
2726  bool ads_pull_uint32(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
2727                       uint32_t *v)
2728 {
2729         char **values;
2730
2731         values = ldap_get_values(ads->ldap.ld, msg, field);
2732         if (!values)
2733                 return False;
2734         if (!values[0]) {
2735                 ldap_value_free(values);
2736                 return False;
2737         }
2738
2739         *v = atoi(values[0]);
2740         ldap_value_free(values);
2741         return True;
2742 }
2743
2744 /**
2745  * pull a single objectGUID from an ADS result
2746  * @param ads connection to ADS server
2747  * @param msg results of search
2748  * @param guid 37-byte area to receive text guid
2749  * @return boolean indicating success
2750  **/
2751  bool ads_pull_guid(ADS_STRUCT *ads, LDAPMessage *msg, struct GUID *guid)
2752 {
2753         DATA_BLOB blob;
2754         NTSTATUS status;
2755
2756         if (!smbldap_talloc_single_blob(talloc_tos(), ads->ldap.ld, msg, "objectGUID",
2757                                         &blob)) {
2758                 return false;
2759         }
2760
2761         status = GUID_from_ndr_blob(&blob, guid);
2762         talloc_free(blob.data);
2763         return NT_STATUS_IS_OK(status);
2764 }
2765
2766
2767 /**
2768  * pull a single struct dom_sid from a ADS result
2769  * @param ads connection to ads server
2770  * @param msg Results of search
2771  * @param field Attribute to retrieve
2772  * @param sid Pointer to sid to store result
2773  * @return boolean inidicating success
2774 */
2775  bool ads_pull_sid(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
2776                    struct dom_sid *sid)
2777 {
2778         return smbldap_pull_sid(ads->ldap.ld, msg, field, sid);
2779 }
2780
2781 /**
2782  * pull an array of struct dom_sids from a ADS result
2783  * @param ads connection to ads server
2784  * @param mem_ctx TALLOC_CTX for allocating sid array
2785  * @param msg Results of search
2786  * @param field Attribute to retrieve
2787  * @param sids pointer to sid array to allocate
2788  * @return the count of SIDs pulled
2789  **/
2790  int ads_pull_sids(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2791                    LDAPMessage *msg, const char *field, struct dom_sid **sids)
2792 {
2793         struct berval **values;
2794         int count, i;
2795
2796         values = ldap_get_values_len(ads->ldap.ld, msg, field);
2797
2798         if (!values)
2799                 return 0;
2800
2801         for (i=0; values[i]; i++)
2802                 /* nop */ ;
2803
2804         if (i) {
2805                 (*sids) = talloc_array(mem_ctx, struct dom_sid, i);
2806                 if (!(*sids)) {
2807                         ldap_value_free_len(values);
2808                         return 0;
2809                 }
2810         } else {
2811                 (*sids) = NULL;
2812         }
2813
2814         count = 0;
2815         for (i=0; values[i]; i++) {
2816                 ssize_t ret;
2817                 ret = sid_parse((const uint8_t *)values[i]->bv_val,
2818                                 values[i]->bv_len, &(*sids)[count]);
2819                 if (ret != -1) {
2820                         struct dom_sid_buf buf;
2821                         DBG_DEBUG("pulling SID: %s\n",
2822                                   dom_sid_str_buf(&(*sids)[count], &buf));
2823                         count++;
2824                 }
2825         }
2826
2827         ldap_value_free_len(values);
2828         return count;
2829 }
2830
2831 /**
2832  * pull a struct security_descriptor from a ADS result
2833  * @param ads connection to ads server
2834  * @param mem_ctx TALLOC_CTX for allocating sid array
2835  * @param msg Results of search
2836  * @param field Attribute to retrieve
2837  * @param sd Pointer to *struct security_descriptor to store result (talloc()ed)
2838  * @return boolean inidicating success
2839 */
2840  bool ads_pull_sd(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2841                   LDAPMessage *msg, const char *field,
2842                   struct security_descriptor **sd)
2843 {
2844         struct berval **values;
2845         bool ret = true;
2846
2847         values = ldap_get_values_len(ads->ldap.ld, msg, field);
2848
2849         if (!values) return false;
2850
2851         if (values[0]) {
2852                 NTSTATUS status;
2853                 status = unmarshall_sec_desc(mem_ctx,
2854                                              (uint8_t *)values[0]->bv_val,
2855                                              values[0]->bv_len, sd);
2856                 if (!NT_STATUS_IS_OK(status)) {
2857                         DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
2858                                   nt_errstr(status)));
2859                         ret = false;
2860                 }
2861         }
2862
2863         ldap_value_free_len(values);
2864         return ret;
2865 }
2866
2867 /* 
2868  * in order to support usernames longer than 21 characters we need to 
2869  * use both the sAMAccountName and the userPrincipalName attributes 
2870  * It seems that not all users have the userPrincipalName attribute set
2871  *
2872  * @param ads connection to ads server
2873  * @param mem_ctx TALLOC_CTX for allocating sid array
2874  * @param msg Results of search
2875  * @return the username
2876  */
2877  char *ads_pull_username(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2878                          LDAPMessage *msg)
2879 {
2880 #if 0   /* JERRY */
2881         char *ret, *p;
2882
2883         /* lookup_name() only works on the sAMAccountName to 
2884            returning the username portion of userPrincipalName
2885            breaks winbindd_getpwnam() */
2886
2887         ret = ads_pull_string(ads, mem_ctx, msg, "userPrincipalName");
2888         if (ret && (p = strchr_m(ret, '@'))) {
2889                 *p = 0;
2890                 return ret;
2891         }
2892 #endif
2893         return ads_pull_string(ads, mem_ctx, msg, "sAMAccountName");
2894 }
2895
2896
2897 /**
2898  * find the update serial number - this is the core of the ldap cache
2899  * @param ads connection to ads server
2900  * @param ads connection to ADS server
2901  * @param usn Pointer to retrieved update serial number
2902  * @return status of search
2903  **/
2904 ADS_STATUS ads_USN(ADS_STRUCT *ads, uint32_t *usn)
2905 {
2906         const char *attrs[] = {"highestCommittedUSN", NULL};
2907         ADS_STATUS status;
2908         LDAPMessage *res;
2909
2910         status = ads_do_search_retry(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2911         if (!ADS_ERR_OK(status)) 
2912                 return status;
2913
2914         if (ads_count_replies(ads, res) != 1) {
2915                 ads_msgfree(ads, res);
2916                 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2917         }
2918
2919         if (!ads_pull_uint32(ads, res, "highestCommittedUSN", usn)) {
2920                 ads_msgfree(ads, res);
2921                 return ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
2922         }
2923
2924         ads_msgfree(ads, res);
2925         return ADS_SUCCESS;
2926 }
2927
2928 /* parse a ADS timestring - typical string is
2929    '20020917091222.0Z0' which means 09:12.22 17th September
2930    2002, timezone 0 */
2931 static time_t ads_parse_time(const char *str)
2932 {
2933         struct tm tm;
2934
2935         ZERO_STRUCT(tm);
2936
2937         if (sscanf(str, "%4d%2d%2d%2d%2d%2d", 
2938                    &tm.tm_year, &tm.tm_mon, &tm.tm_mday, 
2939                    &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) {
2940                 return 0;
2941         }
2942         tm.tm_year -= 1900;
2943         tm.tm_mon -= 1;
2944
2945         return timegm(&tm);
2946 }
2947
2948 /********************************************************************
2949 ********************************************************************/
2950
2951 ADS_STATUS ads_current_time(ADS_STRUCT *ads)
2952 {
2953         const char *attrs[] = {"currentTime", NULL};
2954         ADS_STATUS status;
2955         LDAPMessage *res;
2956         char *timestr;
2957         TALLOC_CTX *ctx;
2958         ADS_STRUCT *ads_s = ads;
2959
2960         if (!(ctx = talloc_init("ads_current_time"))) {
2961                 return ADS_ERROR(LDAP_NO_MEMORY);
2962         }
2963
2964         /* establish a new ldap tcp session if necessary */
2965
2966         if ( !ads->ldap.ld ) {
2967                 if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup, 
2968                         ads->server.ldap_server )) == NULL )
2969                 {
2970                         status = ADS_ERROR(LDAP_NO_MEMORY);
2971                         goto done;
2972                 }
2973                 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
2974                 status = ads_connect( ads_s );
2975                 if ( !ADS_ERR_OK(status))
2976                         goto done;
2977         }
2978
2979         status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2980         if (!ADS_ERR_OK(status)) {
2981                 goto done;
2982         }
2983
2984         timestr = ads_pull_string(ads_s, ctx, res, "currentTime");
2985         if (!timestr) {
2986                 ads_msgfree(ads_s, res);
2987                 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2988                 goto done;
2989         }
2990
2991         /* but save the time and offset in the original ADS_STRUCT */   
2992
2993         ads->config.current_time = ads_parse_time(timestr);
2994
2995         if (ads->config.current_time != 0) {
2996                 ads->auth.time_offset = ads->config.current_time - time(NULL);
2997                 DEBUG(4,("KDC time offset is %d seconds\n", ads->auth.time_offset));
2998         }
2999
3000         ads_msgfree(ads, res);
3001
3002         status = ADS_SUCCESS;
3003
3004 done:
3005         /* free any temporary ads connections */
3006         if ( ads_s != ads ) {
3007                 ads_destroy( &ads_s );
3008         }
3009         talloc_destroy(ctx);
3010
3011         return status;
3012 }
3013
3014 /********************************************************************
3015 ********************************************************************/
3016
3017 ADS_STATUS ads_domain_func_level(ADS_STRUCT *ads, uint32_t *val)
3018 {
3019         const char *attrs[] = {"domainFunctionality", NULL};
3020         ADS_STATUS status;
3021         LDAPMessage *res;
3022         ADS_STRUCT *ads_s = ads;
3023
3024         *val = DS_DOMAIN_FUNCTION_2000;
3025
3026         /* establish a new ldap tcp session if necessary */
3027
3028         if ( !ads->ldap.ld ) {
3029                 if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup, 
3030                         ads->server.ldap_server )) == NULL )
3031                 {
3032                         status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
3033                         goto done;
3034                 }
3035                 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
3036                 status = ads_connect( ads_s );
3037                 if ( !ADS_ERR_OK(status))
3038                         goto done;
3039         }
3040
3041         /* If the attribute does not exist assume it is a Windows 2000 
3042            functional domain */
3043
3044         status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
3045         if (!ADS_ERR_OK(status)) {
3046                 if ( status.err.rc == LDAP_NO_SUCH_ATTRIBUTE ) {
3047                         status = ADS_SUCCESS;
3048                 }
3049                 goto done;
3050         }
3051
3052         if ( !ads_pull_uint32(ads_s, res, "domainFunctionality", val) ) {
3053                 DEBUG(5,("ads_domain_func_level: Failed to pull the domainFunctionality attribute.\n"));
3054         }
3055         DEBUG(3,("ads_domain_func_level: %d\n", *val));
3056
3057
3058         ads_msgfree(ads, res);
3059
3060 done:
3061         /* free any temporary ads connections */
3062         if ( ads_s != ads ) {
3063                 ads_destroy( &ads_s );
3064         }
3065
3066         return status;
3067 }
3068
3069 /**
3070  * find the domain sid for our domain
3071  * @param ads connection to ads server
3072  * @param sid Pointer to domain sid
3073  * @return status of search
3074  **/
3075 ADS_STATUS ads_domain_sid(ADS_STRUCT *ads, struct dom_sid *sid)
3076 {
3077         const char *attrs[] = {"objectSid", NULL};
3078         LDAPMessage *res;
3079         ADS_STATUS rc;
3080
3081         rc = ads_do_search_retry(ads, ads->config.bind_path, LDAP_SCOPE_BASE, "(objectclass=*)", 
3082                            attrs, &res);
3083         if (!ADS_ERR_OK(rc)) return rc;
3084         if (!ads_pull_sid(ads, res, "objectSid", sid)) {
3085                 ads_msgfree(ads, res);
3086                 return ADS_ERROR_SYSTEM(ENOENT);
3087         }
3088         ads_msgfree(ads, res);
3089
3090         return ADS_SUCCESS;
3091 }
3092
3093 /**
3094  * find our site name 
3095  * @param ads connection to ads server
3096  * @param mem_ctx Pointer to talloc context
3097  * @param site_name Pointer to the sitename
3098  * @return status of search
3099  **/
3100 ADS_STATUS ads_site_dn(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char **site_name)
3101 {
3102         ADS_STATUS status;
3103         LDAPMessage *res;
3104         const char *dn, *service_name;
3105         const char *attrs[] = { "dsServiceName", NULL };
3106
3107         status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
3108         if (!ADS_ERR_OK(status)) {
3109                 return status;
3110         }
3111
3112         service_name = ads_pull_string(ads, mem_ctx, res, "dsServiceName");
3113         if (service_name == NULL) {
3114                 ads_msgfree(ads, res);
3115                 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3116         }
3117
3118         ads_msgfree(ads, res);
3119
3120         /* go up three levels */
3121         dn = ads_parent_dn(ads_parent_dn(ads_parent_dn(service_name)));
3122         if (dn == NULL) {
3123                 return ADS_ERROR(LDAP_NO_MEMORY);
3124         }
3125
3126         *site_name = talloc_strdup(mem_ctx, dn);
3127         if (*site_name == NULL) {
3128                 return ADS_ERROR(LDAP_NO_MEMORY);
3129         }
3130
3131         return status;
3132         /*
3133         dsServiceName: CN=NTDS Settings,CN=W2K3DC,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,DC=ber,DC=suse,DC=de
3134         */                                               
3135 }
3136
3137 /**
3138  * find the site dn where a machine resides
3139  * @param ads connection to ads server
3140  * @param mem_ctx Pointer to talloc context
3141  * @param computer_name name of the machine
3142  * @param site_name Pointer to the sitename
3143  * @return status of search
3144  **/
3145 ADS_STATUS ads_site_dn_for_machine(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char *computer_name, const char **site_dn)
3146 {
3147         ADS_STATUS status;
3148         LDAPMessage *res;
3149         const char *parent, *filter;
3150         char *config_context = NULL;
3151         char *dn;
3152
3153         /* shortcut a query */
3154         if (strequal(computer_name, ads->config.ldap_server_name)) {
3155                 return ads_site_dn(ads, mem_ctx, site_dn);
3156         }
3157
3158         status = ads_config_path(ads, mem_ctx, &config_context);
3159         if (!ADS_ERR_OK(status)) {
3160                 return status;
3161         }
3162
3163         filter = talloc_asprintf(mem_ctx, "(cn=%s)", computer_name);
3164         if (filter == NULL) {
3165                 return ADS_ERROR(LDAP_NO_MEMORY);
3166         }
3167
3168         status = ads_do_search(ads, config_context, LDAP_SCOPE_SUBTREE, 
3169                                filter, NULL, &res);
3170         if (!ADS_ERR_OK(status)) {
3171                 return status;
3172         }
3173
3174         if (ads_count_replies(ads, res) != 1) {
3175                 ads_msgfree(ads, res);
3176                 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
3177         }
3178
3179         dn = ads_get_dn(ads, mem_ctx, res);
3180         if (dn == NULL) {
3181                 ads_msgfree(ads, res);
3182                 return ADS_ERROR(LDAP_NO_MEMORY);
3183         }
3184
3185         /* go up three levels */
3186         parent = ads_parent_dn(ads_parent_dn(ads_parent_dn(dn)));
3187         if (parent == NULL) {
3188                 ads_msgfree(ads, res);
3189                 TALLOC_FREE(dn);
3190                 return ADS_ERROR(LDAP_NO_MEMORY);
3191         }
3192
3193         *site_dn = talloc_strdup(mem_ctx, parent);
3194         if (*site_dn == NULL) {
3195                 ads_msgfree(ads, res);
3196                 TALLOC_FREE(dn);
3197                 return ADS_ERROR(LDAP_NO_MEMORY);
3198         }
3199
3200         TALLOC_FREE(dn);
3201         ads_msgfree(ads, res);
3202
3203         return status;
3204 }
3205
3206 /**
3207  * get the upn suffixes for a domain
3208  * @param ads connection to ads server
3209  * @param mem_ctx Pointer to talloc context
3210  * @param suffixes Pointer to an array of suffixes
3211  * @param num_suffixes Pointer to the number of suffixes
3212  * @return status of search
3213  **/
3214 ADS_STATUS ads_upn_suffixes(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, char ***suffixes, size_t *num_suffixes)
3215 {
3216         ADS_STATUS status;
3217         LDAPMessage *res;
3218         const char *base;
3219         char *config_context = NULL;
3220         const char *attrs[] = { "uPNSuffixes", NULL };
3221
3222         status = ads_config_path(ads, mem_ctx, &config_context);
3223         if (!ADS_ERR_OK(status)) {
3224                 return status;
3225         }
3226
3227         base = talloc_asprintf(mem_ctx, "cn=Partitions,%s", config_context);
3228         if (base == NULL) {
3229                 return ADS_ERROR(LDAP_NO_MEMORY);
3230         }
3231
3232         status = ads_search_dn(ads, &res, base, attrs);
3233         if (!ADS_ERR_OK(status)) {
3234                 return status;
3235         }
3236
3237         if (ads_count_replies(ads, res) != 1) {
3238                 ads_msgfree(ads, res);
3239                 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
3240         }
3241
3242         (*suffixes) = ads_pull_strings(ads, mem_ctx, res, "uPNSuffixes", num_suffixes);
3243         if ((*suffixes) == NULL) {
3244                 ads_msgfree(ads, res);
3245                 return ADS_ERROR(LDAP_NO_MEMORY);
3246         }
3247
3248         ads_msgfree(ads, res);
3249
3250         return status;
3251 }
3252
3253 /**
3254  * get the joinable ous for a domain
3255  * @param ads connection to ads server
3256  * @param mem_ctx Pointer to talloc context
3257  * @param ous Pointer to an array of ous
3258  * @param num_ous Pointer to the number of ous
3259  * @return status of search
3260  **/
3261 ADS_STATUS ads_get_joinable_ous(ADS_STRUCT *ads,
3262                                 TALLOC_CTX *mem_ctx,
3263                                 char ***ous,
3264                                 size_t *num_ous)
3265 {
3266         ADS_STATUS status;
3267         LDAPMessage *res = NULL;
3268         LDAPMessage *msg = NULL;
3269         const char *attrs[] = { "dn", NULL };
3270         int count = 0;
3271
3272         status = ads_search(ads, &res,
3273                             "(|(objectClass=domain)(objectclass=organizationalUnit))",
3274                             attrs);
3275         if (!ADS_ERR_OK(status)) {
3276                 return status;
3277         }
3278
3279         count = ads_count_replies(ads, res);
3280         if (count < 1) {
3281                 ads_msgfree(ads, res);
3282                 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3283         }
3284
3285         for (msg = ads_first_entry(ads, res); msg;
3286              msg = ads_next_entry(ads, msg)) {
3287                 const char **p = discard_const_p(const char *, *ous);
3288                 char *dn = NULL;
3289
3290                 dn = ads_get_dn(ads, talloc_tos(), msg);
3291                 if (!dn) {
3292                         ads_msgfree(ads, res);
3293                         return ADS_ERROR(LDAP_NO_MEMORY);
3294                 }
3295
3296                 if (!add_string_to_array(mem_ctx, dn, &p, num_ous)) {
3297                         TALLOC_FREE(dn);
3298                         ads_msgfree(ads, res);
3299                         return ADS_ERROR(LDAP_NO_MEMORY);
3300                 }
3301
3302                 TALLOC_FREE(dn);
3303                 *ous = discard_const_p(char *, p);
3304         }
3305
3306         ads_msgfree(ads, res);
3307
3308         return status;
3309 }
3310
3311
3312 /**
3313  * pull a struct dom_sid from an extended dn string
3314  * @param mem_ctx TALLOC_CTX
3315  * @param extended_dn string
3316  * @param flags string type of extended_dn
3317  * @param sid pointer to a struct dom_sid
3318  * @return NT_STATUS_OK on success,
3319  *         NT_INVALID_PARAMETER on error,
3320  *         NT_STATUS_NOT_FOUND if no SID present
3321  **/
3322 ADS_STATUS ads_get_sid_from_extended_dn(TALLOC_CTX *mem_ctx,
3323                                         const char *extended_dn,
3324                                         enum ads_extended_dn_flags flags,
3325                                         struct dom_sid *sid)
3326 {
3327         char *p, *q, *dn;
3328
3329         if (!extended_dn) {
3330                 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3331         }
3332
3333         /* otherwise extended_dn gets stripped off */
3334         if ((dn = talloc_strdup(mem_ctx, extended_dn)) == NULL) {
3335                 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3336         }
3337         /*
3338          * ADS_EXTENDED_DN_HEX_STRING:
3339          * <GUID=238e1963cb390f4bb032ba0105525a29>;<SID=010500000000000515000000bb68c8fd6b61b427572eb04556040000>;CN=gd,OU=berlin,OU=suse,DC=ber,DC=suse,DC=de
3340          *
3341          * ADS_EXTENDED_DN_STRING (only with w2k3):
3342          * <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
3343          *
3344          * Object with no SID, such as an Exchange Public Folder
3345          * <GUID=28907fb4bdf6854993e7f0a10b504e7c>;CN=public,CN=Microsoft Exchange System Objects,DC=sd2k3ms,DC=west,DC=isilon,DC=com
3346          */
3347
3348         p = strchr(dn, ';');
3349         if (!p) {
3350                 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3351         }
3352
3353         if (strncmp(p, ";<SID=", strlen(";<SID=")) != 0) {
3354                 DEBUG(5,("No SID present in extended dn\n"));
3355                 return ADS_ERROR_NT(NT_STATUS_NOT_FOUND);
3356         }
3357
3358         p += strlen(";<SID=");
3359
3360         q = strchr(p, '>');
3361         if (!q) {
3362                 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3363         }
3364
3365         *q = '\0';
3366
3367         DEBUG(100,("ads_get_sid_from_extended_dn: sid string is %s\n", p));
3368
3369         switch (flags) {
3370
3371         case ADS_EXTENDED_DN_STRING:
3372                 if (!string_to_sid(sid, p)) {
3373                         return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3374                 }
3375                 break;
3376         case ADS_EXTENDED_DN_HEX_STRING: {
3377                 ssize_t ret;
3378                 fstring buf;
3379                 size_t buf_len;
3380
3381                 buf_len = strhex_to_str(buf, sizeof(buf), p, strlen(p));
3382                 if (buf_len == 0) {
3383                         return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3384                 }
3385
3386                 ret = sid_parse((const uint8_t *)buf, buf_len, sid);
3387                 if (ret == -1) {
3388                         DEBUG(10,("failed to parse sid\n"));
3389                         return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3390                 }
3391                 break;
3392                 }
3393         default:
3394                 DEBUG(10,("unknown extended dn format\n"));
3395                 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3396         }
3397
3398         return ADS_ERROR_NT(NT_STATUS_OK);
3399 }
3400
3401 /********************************************************************
3402 ********************************************************************/
3403
3404 char* ads_get_dnshostname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3405 {
3406         LDAPMessage *res = NULL;
3407         ADS_STATUS status;
3408         int count = 0;
3409         char *name = NULL;
3410
3411         status = ads_find_machine_acct(ads, &res, machine_name);
3412         if (!ADS_ERR_OK(status)) {
3413                 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
3414                         lp_netbios_name()));
3415                 goto out;
3416         }
3417
3418         if ( (count = ads_count_replies(ads, res)) != 1 ) {
3419                 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
3420                 goto out;
3421         }
3422
3423         if ( (name = ads_pull_string(ads, ctx, res, "dNSHostName")) == NULL ) {
3424                 DEBUG(0,("ads_get_dnshostname: No dNSHostName attribute!\n"));
3425         }
3426
3427 out:
3428         ads_msgfree(ads, res);
3429
3430         return name;
3431 }
3432
3433 /********************************************************************
3434 ********************************************************************/
3435
3436 char* ads_get_upn( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3437 {
3438         LDAPMessage *res = NULL;
3439         ADS_STATUS status;
3440         int count = 0;
3441         char *name = NULL;
3442
3443         status = ads_find_machine_acct(ads, &res, machine_name);
3444         if (!ADS_ERR_OK(status)) {
3445                 DEBUG(0,("ads_get_upn: Failed to find account for %s\n",
3446                         lp_netbios_name()));
3447                 goto out;
3448         }
3449
3450         if ( (count = ads_count_replies(ads, res)) != 1 ) {
3451                 DEBUG(1,("ads_get_upn: %d entries returned!\n", count));
3452                 goto out;
3453         }
3454
3455         if ( (name = ads_pull_string(ads, ctx, res, "userPrincipalName")) == NULL ) {
3456                 DEBUG(2,("ads_get_upn: No userPrincipalName attribute!\n"));
3457         }
3458
3459 out:
3460         ads_msgfree(ads, res);
3461
3462         return name;
3463 }
3464
3465 /********************************************************************
3466 ********************************************************************/
3467
3468 bool ads_has_samaccountname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3469 {
3470         LDAPMessage *res = NULL;
3471         ADS_STATUS status;
3472         int count = 0;
3473         char *name = NULL;
3474         bool ok = false;
3475
3476         status = ads_find_machine_acct(ads, &res, machine_name);
3477         if (!ADS_ERR_OK(status)) {
3478                 DEBUG(0,("ads_has_samaccountname: Failed to find account for %s\n",
3479                         lp_netbios_name()));
3480                 goto out;
3481         }
3482
3483         if ( (count = ads_count_replies(ads, res)) != 1 ) {
3484                 DEBUG(1,("ads_has_samaccountname: %d entries returned!\n", count));
3485                 goto out;
3486         }
3487
3488         if ( (name = ads_pull_string(ads, ctx, res, "sAMAccountName")) == NULL ) {
3489                 DEBUG(0,("ads_has_samaccountname: No sAMAccountName attribute!\n"));
3490         }
3491
3492 out:
3493         ads_msgfree(ads, res);
3494         if (name != NULL) {
3495                 ok = (strlen(name) > 0);
3496         }
3497         TALLOC_FREE(name);
3498         return ok;
3499 }
3500
3501 #if 0
3502
3503    SAVED CODE - we used to join via ldap - remember how we did this. JRA.
3504
3505 /**
3506  * Join a machine to a realm
3507  *  Creates the machine account and sets the machine password
3508  * @param ads connection to ads server
3509  * @param machine name of host to add
3510  * @param org_unit Organizational unit to place machine in
3511  * @return status of join
3512  **/
3513 ADS_STATUS ads_join_realm(ADS_STRUCT *ads, const char *machine_name,
3514                         uint32_t account_type, const char *org_unit)
3515 {
3516         ADS_STATUS status;
3517         LDAPMessage *res = NULL;
3518         char *machine;
3519
3520         /* machine name must be lowercase */
3521         machine = SMB_STRDUP(machine_name);
3522         strlower_m(machine);
3523
3524         /*
3525         status = ads_find_machine_acct(ads, (void **)&res, machine);
3526         if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
3527                 DEBUG(0, ("Host account for %s already exists - deleting old account\n", machine));
3528                 status = ads_leave_realm(ads, machine);
3529                 if (!ADS_ERR_OK(status)) {
3530                         DEBUG(0, ("Failed to delete host '%s' from the '%s' realm.\n",
3531                                 machine, ads->config.realm));
3532                         return status;
3533                 }
3534         }
3535         */
3536         status = ads_add_machine_acct(ads, machine, account_type, org_unit);
3537         if (!ADS_ERR_OK(status)) {
3538                 DEBUG(0, ("ads_join_realm: ads_add_machine_acct failed (%s): %s\n", machine, ads_errstr(status)));
3539                 SAFE_FREE(machine);
3540                 return status;
3541         }
3542
3543         status = ads_find_machine_acct(ads, (void **)(void *)&res, machine);
3544         if (!ADS_ERR_OK(status)) {
3545                 DEBUG(0, ("ads_join_realm: Host account test failed for machine %s\n", machine));
3546                 SAFE_FREE(machine);
3547                 return status;
3548         }
3549
3550         SAFE_FREE(machine);
3551         ads_msgfree(ads, res);
3552
3553         return status;
3554 }
3555 #endif
3556
3557 /**
3558  * Delete a machine from the realm
3559  * @param ads connection to ads server
3560  * @param hostname Machine to remove
3561  * @return status of delete
3562  **/
3563 ADS_STATUS ads_leave_realm(ADS_STRUCT *ads, const char *hostname)
3564 {
3565         ADS_STATUS status;
3566         void *msg;
3567         LDAPMessage *res;
3568         char *hostnameDN, *host;
3569         int rc;
3570         LDAPControl ldap_control;
3571         LDAPControl  * pldap_control[2] = {NULL, NULL};
3572
3573         pldap_control[0] = &ldap_control;
3574         memset(&ldap_control, 0, sizeof(LDAPControl));
3575         ldap_control.ldctl_oid = discard_const_p(char, LDAP_SERVER_TREE_DELETE_OID);
3576
3577         /* hostname must be lowercase */
3578         host = SMB_STRDUP(hostname);
3579         if (!strlower_m(host)) {
3580                 SAFE_FREE(host);
3581                 return ADS_ERROR_SYSTEM(EINVAL);
3582         }
3583
3584         status = ads_find_machine_acct(ads, &res, host);
3585         if (!ADS_ERR_OK(status)) {
3586                 DEBUG(0, ("Host account for %s does not exist.\n", host));
3587                 SAFE_FREE(host);
3588                 return status;
3589         }
3590
3591         msg = ads_first_entry(ads, res);
3592         if (!msg) {
3593                 SAFE_FREE(host);
3594                 return ADS_ERROR_SYSTEM(ENOENT);
3595         }
3596
3597         hostnameDN = ads_get_dn(ads, talloc_tos(), (LDAPMessage *)msg);
3598         if (hostnameDN == NULL) {
3599                 SAFE_FREE(host);
3600                 return ADS_ERROR_SYSTEM(ENOENT);
3601         }
3602
3603         rc = ldap_delete_ext_s(ads->ldap.ld, hostnameDN, pldap_control, NULL);
3604         if (rc) {
3605                 DEBUG(3,("ldap_delete_ext_s failed with error code %d\n", rc));
3606         }else {
3607                 DEBUG(3,("ldap_delete_ext_s succeeded with error code %d\n", rc));
3608         }
3609
3610         if (rc != LDAP_SUCCESS) {
3611                 const char *attrs[] = { "cn", NULL };
3612                 LDAPMessage *msg_sub;
3613
3614                 /* we only search with scope ONE, we do not expect any further
3615                  * objects to be created deeper */
3616
3617                 status = ads_do_search_retry(ads, hostnameDN,
3618                                              LDAP_SCOPE_ONELEVEL,
3619                                              "(objectclass=*)", attrs, &res);
3620
3621                 if (!ADS_ERR_OK(status)) {
3622                         SAFE_FREE(host);
3623                         TALLOC_FREE(hostnameDN);
3624                         return status;
3625                 }
3626
3627                 for (msg_sub = ads_first_entry(ads, res); msg_sub;
3628                         msg_sub = ads_next_entry(ads, msg_sub)) {
3629
3630                         char *dn = NULL;
3631
3632                         if ((dn = ads_get_dn(ads, talloc_tos(), msg_sub)) == NULL) {
3633                                 SAFE_FREE(host);
3634                                 TALLOC_FREE(hostnameDN);
3635                                 return ADS_ERROR(LDAP_NO_MEMORY);
3636                         }
3637
3638                         status = ads_del_dn(ads, dn);
3639                         if (!ADS_ERR_OK(status)) {
3640                                 DEBUG(3,("failed to delete dn %s: %s\n", dn, ads_errstr(status)));
3641                                 SAFE_FREE(host);
3642                                 TALLOC_FREE(dn);
3643                                 TALLOC_FREE(hostnameDN);
3644                                 return status;
3645                         }
3646
3647                         TALLOC_FREE(dn);
3648                 }
3649
3650                 /* there should be no subordinate objects anymore */
3651                 status = ads_do_search_retry(ads, hostnameDN,
3652                                              LDAP_SCOPE_ONELEVEL,
3653                                              "(objectclass=*)", attrs, &res);
3654
3655                 if (!ADS_ERR_OK(status) || ( (ads_count_replies(ads, res)) > 0 ) ) {
3656                         SAFE_FREE(host);
3657                         TALLOC_FREE(hostnameDN);
3658                         return status;
3659                 }
3660
3661                 /* delete hostnameDN now */
3662                 status = ads_del_dn(ads, hostnameDN);
3663                 if (!ADS_ERR_OK(status)) {
3664                         SAFE_FREE(host);
3665                         DEBUG(3,("failed to delete dn %s: %s\n", hostnameDN, ads_errstr(status)));
3666                         TALLOC_FREE(hostnameDN);
3667                         return status;
3668                 }
3669         }
3670
3671         TALLOC_FREE(hostnameDN);
3672
3673         status = ads_find_machine_acct(ads, &res, host);
3674         if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
3675                 DEBUG(3, ("Failed to remove host account.\n"));
3676                 SAFE_FREE(host);
3677                 return status;
3678         }
3679
3680         SAFE_FREE(host);
3681         return status;
3682 }
3683
3684 /**
3685  * pull all token-sids from an LDAP dn
3686  * @param ads connection to ads server
3687  * @param mem_ctx TALLOC_CTX for allocating sid array
3688  * @param dn of LDAP object
3689  * @param user_sid pointer to struct dom_sid (objectSid)
3690  * @param primary_group_sid pointer to struct dom_sid (self composed)
3691  * @param sids pointer to sid array to allocate
3692  * @param num_sids counter of SIDs pulled
3693  * @return status of token query
3694  **/
3695  ADS_STATUS ads_get_tokensids(ADS_STRUCT *ads,
3696                               TALLOC_CTX *mem_ctx,
3697                               const char *dn,
3698                               struct dom_sid *user_sid,
3699                               struct dom_sid *primary_group_sid,
3700                               struct dom_sid **sids,
3701                               size_t *num_sids)
3702 {
3703         ADS_STATUS status;
3704         LDAPMessage *res = NULL;
3705         int count = 0;
3706         size_t tmp_num_sids;
3707         struct dom_sid *tmp_sids;
3708         struct dom_sid tmp_user_sid;
3709         struct dom_sid tmp_primary_group_sid;
3710         uint32_t pgid;
3711         const char *attrs[] = {
3712                 "objectSid",
3713                 "tokenGroups",
3714                 "primaryGroupID",
3715                 NULL
3716         };
3717
3718         status = ads_search_retry_dn(ads, &res, dn, attrs);
3719         if (!ADS_ERR_OK(status)) {
3720                 return status;
3721         }
3722
3723         count = ads_count_replies(ads, res);
3724         if (count != 1) {
3725                 ads_msgfree(ads, res);
3726                 return ADS_ERROR_LDAP(LDAP_NO_SUCH_OBJECT);
3727         }
3728
3729         if (!ads_pull_sid(ads, res, "objectSid", &tmp_user_sid)) {
3730                 ads_msgfree(ads, res);
3731                 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3732         }
3733
3734         if (!ads_pull_uint32(ads, res, "primaryGroupID", &pgid)) {
3735                 ads_msgfree(ads, res);
3736                 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3737         }
3738
3739         {
3740                 /* hack to compose the primary group sid without knowing the
3741                  * domsid */
3742
3743                 struct dom_sid domsid;
3744
3745                 sid_copy(&domsid, &tmp_user_sid);
3746
3747                 if (!sid_split_rid(&domsid, NULL)) {
3748                         ads_msgfree(ads, res);
3749                         return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3750                 }
3751
3752                 if (!sid_compose(&tmp_primary_group_sid, &domsid, pgid)) {
3753                         ads_msgfree(ads, res);
3754                         return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3755                 }
3756         }
3757
3758         tmp_num_sids = ads_pull_sids(ads, mem_ctx, res, "tokenGroups", &tmp_sids);
3759
3760         if (tmp_num_sids == 0 || !tmp_sids) {
3761                 ads_msgfree(ads, res);
3762                 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3763         }
3764
3765         if (num_sids) {
3766                 *num_sids = tmp_num_sids;
3767         }
3768
3769         if (sids) {
3770                 *sids = tmp_sids;
3771         }
3772
3773         if (user_sid) {
3774                 *user_sid = tmp_user_sid;
3775         }
3776
3777         if (primary_group_sid) {
3778                 *primary_group_sid = tmp_primary_group_sid;
3779         }
3780
3781         DEBUG(10,("ads_get_tokensids: returned %d sids\n", (int)tmp_num_sids + 2));
3782
3783         ads_msgfree(ads, res);
3784         return ADS_ERROR_LDAP(LDAP_SUCCESS);
3785 }
3786
3787 /**
3788  * Find a sAMAccoutName in LDAP
3789  * @param ads connection to ads server
3790  * @param mem_ctx TALLOC_CTX for allocating sid array
3791  * @param samaccountname to search
3792  * @param uac_ret uint32_t pointer userAccountControl attribute value
3793  * @param dn_ret pointer to dn
3794  * @return status of token query
3795  **/
3796 ADS_STATUS ads_find_samaccount(ADS_STRUCT *ads,
3797                                TALLOC_CTX *mem_ctx,
3798                                const char *samaccountname,
3799                                uint32_t *uac_ret,
3800                                const char **dn_ret)
3801 {
3802         ADS_STATUS status;
3803         const char *attrs[] = { "userAccountControl", NULL };
3804         const char *filter;
3805         LDAPMessage *res = NULL;
3806         char *dn = NULL;
3807         uint32_t uac = 0;
3808
3809         filter = talloc_asprintf(mem_ctx, "(&(objectclass=user)(sAMAccountName=%s))",
3810                 samaccountname);
3811         if (filter == NULL) {
3812                 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
3813                 goto out;
3814         }
3815
3816         status = ads_do_search_all(ads, ads->config.bind_path,
3817                                    LDAP_SCOPE_SUBTREE,
3818                                    filter, attrs, &res);
3819
3820         if (!ADS_ERR_OK(status)) {
3821                 goto out;
3822         }
3823
3824         if (ads_count_replies(ads, res) != 1) {
3825                 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3826                 goto out;
3827         }
3828
3829         dn = ads_get_dn(ads, talloc_tos(), res);
3830         if (dn == NULL) {
3831                 status = ADS_ERROR(LDAP_NO_MEMORY);
3832                 goto out;
3833         }
3834
3835         if (!ads_pull_uint32(ads, res, "userAccountControl", &uac)) {
3836                 status = ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
3837                 goto out;
3838         }
3839
3840         if (uac_ret) {
3841                 *uac_ret = uac;
3842         }
3843
3844         if (dn_ret) {
3845                 *dn_ret = talloc_strdup(mem_ctx, dn);
3846                 if (!*dn_ret) {
3847                         status = ADS_ERROR(LDAP_NO_MEMORY);
3848                         goto out;
3849                 }
3850         }
3851  out:
3852         TALLOC_FREE(dn);
3853         ads_msgfree(ads, res);
3854
3855         return status;
3856 }
3857
3858 /**
3859  * find our configuration path 
3860  * @param ads connection to ads server
3861  * @param mem_ctx Pointer to talloc context
3862  * @param config_path Pointer to the config path
3863  * @return status of search
3864  **/
3865 ADS_STATUS ads_config_path(ADS_STRUCT *ads, 
3866                            TALLOC_CTX *mem_ctx, 
3867                            char **config_path)
3868 {
3869         ADS_STATUS status;
3870         LDAPMessage *res = NULL;
3871         const char *config_context = NULL;
3872         const char *attrs[] = { "configurationNamingContext", NULL };
3873
3874         status = ads_do_search(ads, "", LDAP_SCOPE_BASE, 
3875                                "(objectclass=*)", attrs, &res);
3876         if (!ADS_ERR_OK(status)) {
3877                 return status;
3878         }
3879
3880         config_context = ads_pull_string(ads, mem_ctx, res, 
3881                                          "configurationNamingContext");
3882         ads_msgfree(ads, res);
3883         if (!config_context) {
3884                 return ADS_ERROR(LDAP_NO_MEMORY);
3885         }
3886
3887         if (config_path) {
3888                 *config_path = talloc_strdup(mem_ctx, config_context);
3889                 if (!*config_path) {
3890                         return ADS_ERROR(LDAP_NO_MEMORY);
3891                 }
3892         }
3893
3894         return ADS_ERROR(LDAP_SUCCESS);
3895 }
3896
3897 /**
3898  * find the displayName of an extended right 
3899  * @param ads connection to ads server
3900  * @param config_path The config path
3901  * @param mem_ctx Pointer to talloc context
3902  * @param GUID struct of the rightsGUID
3903  * @return status of search
3904  **/
3905 const char *ads_get_extended_right_name_by_guid(ADS_STRUCT *ads, 
3906                                                 const char *config_path, 
3907                                                 TALLOC_CTX *mem_ctx, 
3908                                                 const struct GUID *rights_guid)
3909 {
3910         ADS_STATUS rc;
3911         LDAPMessage *res = NULL;
3912         char *expr = NULL;
3913         const char *attrs[] = { "displayName", NULL };
3914         const char *result = NULL;
3915         const char *path;
3916
3917         if (!ads || !mem_ctx || !rights_guid) {
3918                 goto done;
3919         }
3920
3921         expr = talloc_asprintf(mem_ctx, "(rightsGuid=%s)", 
3922                                GUID_string(mem_ctx, rights_guid));
3923         if (!expr) {
3924                 goto done;
3925         }
3926
3927         path = talloc_asprintf(mem_ctx, "cn=Extended-Rights,%s", config_path);
3928         if (!path) {
3929                 goto done;
3930         }
3931
3932         rc = ads_do_search_retry(ads, path, LDAP_SCOPE_SUBTREE, 
3933                                  expr, attrs, &res);
3934         if (!ADS_ERR_OK(rc)) {
3935                 goto done;
3936         }
3937
3938         if (ads_count_replies(ads, res) != 1) {
3939                 goto done;
3940         }
3941
3942         result = ads_pull_string(ads, mem_ctx, res, "displayName");
3943
3944  done:
3945         ads_msgfree(ads, res);
3946         return result;
3947 }
3948
3949 /**
3950  * verify or build and verify an account ou
3951  * @param mem_ctx Pointer to talloc context
3952  * @param ads connection to ads server
3953  * @param account_ou
3954  * @return status of search
3955  **/
3956
3957 ADS_STATUS ads_check_ou_dn(TALLOC_CTX *mem_ctx,
3958                            ADS_STRUCT *ads,
3959                            const char **account_ou)
3960 {
3961         char **exploded_dn;
3962         const char *name;
3963         char *ou_string;
3964
3965         if (account_ou == NULL) {
3966                 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3967         }
3968
3969         if (*account_ou != NULL) {
3970                 exploded_dn = ldap_explode_dn(*account_ou, 0);
3971                 if (exploded_dn) {
3972                         ldap_value_free(exploded_dn);
3973                         return ADS_SUCCESS;
3974                 }
3975         }
3976
3977         ou_string = ads_ou_string(ads, *account_ou);
3978         if (!ou_string) {
3979                 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX);
3980         }
3981
3982         name = talloc_asprintf(mem_ctx, "%s,%s", ou_string,
3983                                ads->config.bind_path);
3984         SAFE_FREE(ou_string);
3985
3986         if (!name) {
3987                 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3988         }
3989
3990         exploded_dn = ldap_explode_dn(name, 0);
3991         if (!exploded_dn) {
3992                 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX);
3993         }
3994         ldap_value_free(exploded_dn);
3995
3996         *account_ou = name;
3997         return ADS_SUCCESS;
3998 }
3999
4000 #endif