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