Fix some nonempty blank lines
[ira/wip.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 "lib/ldb/include/ldb.h"
26
27 #ifdef HAVE_LDAP
28
29 /**
30  * @file ldap.c
31  * @brief basic ldap client-side routines for ads server communications
32  *
33  * The routines contained here should do the necessary ldap calls for
34  * ads setups.
35  * 
36  * Important note: attribute names passed into ads_ routines must
37  * already be in UTF-8 format.  We do not convert them because in almost
38  * all cases, they are just ascii (which is represented with the same
39  * codepoints in UTF-8).  This may have to change at some point
40  **/
41
42
43 #define LDAP_SERVER_TREE_DELETE_OID     "1.2.840.113556.1.4.805"
44
45 static SIG_ATOMIC_T gotalarm;
46
47 /***************************************************************
48  Signal function to tell us we timed out.
49 ****************************************************************/
50
51 static void gotalarm_sig(void)
52 {
53         gotalarm = 1;
54 }
55
56  LDAP *ldap_open_with_timeout(const char *server, int port, unsigned int to)
57 {
58         LDAP *ldp = NULL;
59
60
61         DEBUG(10, ("Opening connection to LDAP server '%s:%d', timeout "
62                    "%u seconds\n", server, port, to));
63
64         /* Setup timeout */
65         gotalarm = 0;
66         CatchSignal(SIGALRM, SIGNAL_CAST gotalarm_sig);
67         alarm(to);
68         /* End setup timeout. */
69
70         ldp = ldap_open(server, port);
71
72         if (ldp == NULL) {
73                 DEBUG(2,("Could not open connection to LDAP server %s:%d: %s\n",
74                          server, port, strerror(errno)));
75         } else {
76                 DEBUG(10, ("Connected to LDAP server '%s:%d'\n", server, port));
77         }
78
79         /* Teardown timeout. */
80         CatchSignal(SIGALRM, SIGNAL_CAST SIG_IGN);
81         alarm(0);
82
83         return ldp;
84 }
85
86 static int ldap_search_with_timeout(LDAP *ld,
87                                     LDAP_CONST char *base,
88                                     int scope,
89                                     LDAP_CONST char *filter,
90                                     char **attrs,
91                                     int attrsonly,
92                                     LDAPControl **sctrls,
93                                     LDAPControl **cctrls,
94                                     int sizelimit,
95                                     LDAPMessage **res )
96 {
97         struct timeval timeout;
98         int result;
99
100         /* Setup timeout for the ldap_search_ext_s call - local and remote. */
101         timeout.tv_sec = lp_ldap_timeout();
102         timeout.tv_usec = 0;
103
104         /* Setup alarm timeout.... Do we need both of these ? JRA. */
105         gotalarm = 0;
106         CatchSignal(SIGALRM, SIGNAL_CAST gotalarm_sig);
107         alarm(lp_ldap_timeout());
108         /* End setup timeout. */
109
110         result = ldap_search_ext_s(ld, base, scope, filter, attrs,
111                                    attrsonly, sctrls, cctrls, &timeout,
112                                    sizelimit, res);
113
114         /* Teardown timeout. */
115         CatchSignal(SIGALRM, SIGNAL_CAST SIG_IGN);
116         alarm(0);
117
118         if (gotalarm != 0)
119                 return LDAP_TIMELIMIT_EXCEEDED;
120
121         /*
122          * A bug in OpenLDAP means ldap_search_ext_s can return
123          * LDAP_SUCCESS but with a NULL res pointer. Cope with
124          * this. See bug #6279 for details. JRA.
125          */
126
127         if (*res == NULL) {
128                 return LDAP_TIMELIMIT_EXCEEDED;
129         }
130
131         return result;
132 }
133
134 /**********************************************
135  Do client and server sitename match ?
136 **********************************************/
137
138 bool ads_sitename_match(ADS_STRUCT *ads)
139 {
140         if (ads->config.server_site_name == NULL &&
141             ads->config.client_site_name == NULL ) {
142                 DEBUG(10,("ads_sitename_match: both null\n"));
143                 return True;
144         }
145         if (ads->config.server_site_name &&
146             ads->config.client_site_name &&
147             strequal(ads->config.server_site_name,
148                      ads->config.client_site_name)) {
149                 DEBUG(10,("ads_sitename_match: name %s match\n", ads->config.server_site_name));
150                 return True;
151         }
152         DEBUG(10,("ads_sitename_match: no match between server: %s and client: %s\n",
153                 ads->config.server_site_name ? ads->config.server_site_name : "NULL",
154                 ads->config.client_site_name ? ads->config.client_site_name : "NULL"));
155         return False;
156 }
157
158 /**********************************************
159  Is this the closest DC ?
160 **********************************************/
161
162 bool ads_closest_dc(ADS_STRUCT *ads)
163 {
164         if (ads->config.flags & NBT_SERVER_CLOSEST) {
165                 DEBUG(10,("ads_closest_dc: NBT_SERVER_CLOSEST flag set\n"));
166                 return True;
167         }
168
169         /* not sure if this can ever happen */
170         if (ads_sitename_match(ads)) {
171                 DEBUG(10,("ads_closest_dc: NBT_SERVER_CLOSEST flag not set but sites match\n"));
172                 return True;
173         }
174
175         if (ads->config.client_site_name == NULL) {
176                 DEBUG(10,("ads_closest_dc: client belongs to no site\n"));
177                 return True;
178         }
179
180         DEBUG(10,("ads_closest_dc: %s is not the closest DC\n", 
181                 ads->config.ldap_server_name));
182
183         return False;
184 }
185
186
187 /*
188   try a connection to a given ldap server, returning True and setting the servers IP
189   in the ads struct if successful
190  */
191 static bool ads_try_connect(ADS_STRUCT *ads, const char *server, bool gc)
192 {
193         char *srv;
194         struct NETLOGON_SAM_LOGON_RESPONSE_EX cldap_reply;
195         TALLOC_CTX *mem_ctx = NULL;
196         bool ret = false;
197
198         if (!server || !*server) {
199                 return False;
200         }
201
202         DEBUG(5,("ads_try_connect: sending CLDAP request to %s (realm: %s)\n", 
203                 server, ads->server.realm));
204
205         mem_ctx = talloc_init("ads_try_connect");
206         if (!mem_ctx) {
207                 DEBUG(0,("out of memory\n"));
208                 return false;
209         }
210
211         /* this copes with inet_ntoa brokenness */
212
213         srv = SMB_STRDUP(server);
214
215         ZERO_STRUCT( cldap_reply );
216
217         if ( !ads_cldap_netlogon_5(mem_ctx, srv, ads->server.realm, &cldap_reply ) ) {
218                 DEBUG(3,("ads_try_connect: CLDAP request %s failed.\n", srv));
219                 ret = false;
220                 goto out;
221         }
222
223         /* Check the CLDAP reply flags */
224
225         if ( !(cldap_reply.server_type & NBT_SERVER_LDAP) ) {
226                 DEBUG(1,("ads_try_connect: %s's CLDAP reply says it is not an LDAP server!\n",
227                         srv));
228                 ret = false;
229                 goto out;
230         }
231
232         /* Fill in the ads->config values */
233
234         SAFE_FREE(ads->config.realm);
235         SAFE_FREE(ads->config.bind_path);
236         SAFE_FREE(ads->config.ldap_server_name);
237         SAFE_FREE(ads->config.server_site_name);
238         SAFE_FREE(ads->config.client_site_name);
239         SAFE_FREE(ads->server.workgroup);
240
241         ads->config.flags              = cldap_reply.server_type;
242         ads->config.ldap_server_name   = SMB_STRDUP(cldap_reply.pdc_dns_name);
243         ads->config.realm              = SMB_STRDUP(cldap_reply.dns_domain);
244         strupper_m(ads->config.realm);
245         ads->config.bind_path          = ads_build_dn(ads->config.realm);
246         if (*cldap_reply.server_site) {
247                 ads->config.server_site_name =
248                         SMB_STRDUP(cldap_reply.server_site);
249         }
250         if (*cldap_reply.client_site) {
251                 ads->config.client_site_name =
252                         SMB_STRDUP(cldap_reply.client_site);
253         }
254         ads->server.workgroup          = SMB_STRDUP(cldap_reply.domain);
255
256         ads->ldap.port = gc ? LDAP_GC_PORT : LDAP_PORT;
257         if (!interpret_string_addr(&ads->ldap.ss, srv, 0)) {
258                 DEBUG(1,("ads_try_connect: unable to convert %s "
259                         "to an address\n",
260                         srv));
261                 ret = false;
262                 goto out;
263         }
264
265         /* Store our site name. */
266         sitename_store( cldap_reply.domain, cldap_reply.client_site);
267         sitename_store( cldap_reply.dns_domain, cldap_reply.client_site);
268
269         ret = true;
270  out:
271         SAFE_FREE(srv);
272         TALLOC_FREE(mem_ctx);
273
274         return ret;
275 }
276
277 /**********************************************************************
278  Try to find an AD dc using our internal name resolution routines
279  Try the realm first and then then workgroup name if netbios is not 
280  disabled
281 **********************************************************************/
282
283 static NTSTATUS ads_find_dc(ADS_STRUCT *ads)
284 {
285         const char *c_domain;
286         const char *c_realm;
287         int count, i=0;
288         struct ip_service *ip_list;
289         const char *realm;
290         const char *domain;
291         bool got_realm = False;
292         bool use_own_domain = False;
293         char *sitename;
294         NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
295
296         /* if the realm and workgroup are both empty, assume they are ours */
297
298         /* realm */
299         c_realm = ads->server.realm;
300
301         if ( !c_realm || !*c_realm ) {
302                 /* special case where no realm and no workgroup means our own */
303                 if ( !ads->server.workgroup || !*ads->server.workgroup ) {
304                         use_own_domain = True;
305                         c_realm = lp_realm();
306                 }
307         }
308
309         if (c_realm && *c_realm)
310                 got_realm = True;
311
312         /* we need to try once with the realm name and fallback to the
313            netbios domain name if we fail (if netbios has not been disabled */
314
315         if ( !got_realm && !lp_disable_netbios() ) {
316                 c_realm = ads->server.workgroup;
317                 if (!c_realm || !*c_realm) {
318                         if ( use_own_domain )
319                                 c_realm = lp_workgroup();
320                 }
321         }
322
323         if ( !c_realm || !*c_realm ) {
324                 DEBUG(0,("ads_find_dc: no realm or workgroup!  Don't know what to do\n"));
325                 return NT_STATUS_INVALID_PARAMETER; /* rather need MISSING_PARAMETER ... */
326         }
327
328         if ( use_own_domain ) {
329                 c_domain = lp_workgroup();
330         } else {
331                 c_domain = ads->server.workgroup;
332         }
333
334         realm = c_realm;
335         domain = c_domain;
336
337         /*
338          * In case of LDAP we use get_dc_name() as that
339          * creates the custom krb5.conf file
340          */
341         if (!(ads->auth.flags & ADS_AUTH_NO_BIND)) {
342                 fstring srv_name;
343                 struct sockaddr_storage ip_out;
344
345                 DEBUG(6,("ads_find_dc: (ldap) looking for %s '%s'\n",
346                         (got_realm ? "realm" : "domain"), realm));
347
348                 if (get_dc_name(domain, realm, srv_name, &ip_out)) {
349                         /*
350                          * we call ads_try_connect() to fill in the
351                          * ads->config details
352                          */
353                         if (ads_try_connect(ads, srv_name, false)) {
354                                 return NT_STATUS_OK;
355                         }
356                 }
357
358                 return NT_STATUS_NO_LOGON_SERVERS;
359         }
360
361         sitename = sitename_fetch(realm);
362
363  again:
364
365         DEBUG(6,("ads_find_dc: (cldap) looking for %s '%s'\n",
366                 (got_realm ? "realm" : "domain"), realm));
367
368         status = get_sorted_dc_list(realm, sitename, &ip_list, &count, got_realm);
369         if (!NT_STATUS_IS_OK(status)) {
370                 /* fall back to netbios if we can */
371                 if ( got_realm && !lp_disable_netbios() ) {
372                         got_realm = False;
373                         goto again;
374                 }
375
376                 SAFE_FREE(sitename);
377                 return status;
378         }
379
380         /* if we fail this loop, then giveup since all the IP addresses returned were dead */
381         for ( i=0; i<count; i++ ) {
382                 char server[INET6_ADDRSTRLEN];
383
384                 print_sockaddr(server, sizeof(server), &ip_list[i].ss);
385
386                 if ( !NT_STATUS_IS_OK(check_negative_conn_cache(realm, server)) )
387                         continue;
388
389                 if (!got_realm) {
390                         /* realm in this case is a workgroup name. We need
391                            to ignore any IP addresses in the negative connection
392                            cache that match ip addresses returned in the ad realm
393                            case. It sucks that I have to reproduce the logic above... */
394                         c_realm = ads->server.realm;
395                         if ( !c_realm || !*c_realm ) {
396                                 if ( !ads->server.workgroup || !*ads->server.workgroup ) {
397                                         c_realm = lp_realm();
398                                 }
399                         }
400                         if (c_realm && *c_realm &&
401                                         !NT_STATUS_IS_OK(check_negative_conn_cache(c_realm, server))) {
402                                 /* Ensure we add the workgroup name for this
403                                    IP address as negative too. */
404                                 add_failed_connection_entry( realm, server, NT_STATUS_UNSUCCESSFUL );
405                                 continue;
406                         }
407                 }
408
409                 if ( ads_try_connect(ads, server, false) ) {
410                         SAFE_FREE(ip_list);
411                         SAFE_FREE(sitename);
412                         return NT_STATUS_OK;
413                 }
414
415                 /* keep track of failures */
416                 add_failed_connection_entry( realm, server, NT_STATUS_UNSUCCESSFUL );
417         }
418
419         SAFE_FREE(ip_list);
420
421         /* In case we failed to contact one of our closest DC on our site we
422          * need to try to find another DC, retry with a site-less SRV DNS query
423          * - Guenther */
424
425         if (sitename) {
426                 DEBUG(1,("ads_find_dc: failed to find a valid DC on our site (%s), "
427                                 "trying to find another DC\n", sitename));
428                 SAFE_FREE(sitename);
429                 namecache_delete(realm, 0x1C);
430                 goto again;
431         }
432
433         return NT_STATUS_NO_LOGON_SERVERS;
434 }
435
436 /*********************************************************************
437  *********************************************************************/
438
439 static NTSTATUS ads_lookup_site(void)
440 {
441         ADS_STRUCT *ads = NULL;
442         ADS_STATUS ads_status;
443         NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
444
445         ads = ads_init(lp_realm(), NULL, NULL);
446         if (!ads) {
447                 return NT_STATUS_NO_MEMORY;
448         }
449
450         /* The NO_BIND here will find a DC and set the client site
451            but not establish the TCP connection */
452
453         ads->auth.flags = ADS_AUTH_NO_BIND;
454         ads_status = ads_connect(ads);
455         if (!ADS_ERR_OK(ads_status)) {
456                 DEBUG(4, ("ads_lookup_site: ads_connect to our realm failed! (%s)\n",
457                           ads_errstr(ads_status)));
458         }
459         nt_status = ads_ntstatus(ads_status);
460
461         if (ads) {
462                 ads_destroy(&ads);
463         }
464
465         return nt_status;
466 }
467
468 /*********************************************************************
469  *********************************************************************/
470
471 static const char* host_dns_domain(const char *fqdn)
472 {
473         const char *p = fqdn;
474
475         /* go to next char following '.' */
476
477         if ((p = strchr_m(fqdn, '.')) != NULL) {
478                 p++;
479         }
480
481         return p;
482 }
483
484
485 /**
486  * Connect to the Global Catalog server
487  * @param ads Pointer to an existing ADS_STRUCT
488  * @return status of connection
489  *
490  * Simple wrapper around ads_connect() that fills in the
491  * GC ldap server information
492  **/
493
494 ADS_STATUS ads_connect_gc(ADS_STRUCT *ads)
495 {
496         TALLOC_CTX *frame = talloc_stackframe();
497         struct dns_rr_srv *gcs_list;
498         int num_gcs;
499         char *realm = ads->server.realm;
500         NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
501         ADS_STATUS ads_status = ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
502         int i;
503         bool done = false;
504         char *sitename = NULL;
505
506         if (!realm)
507                 realm = lp_realm();
508
509         if ((sitename = sitename_fetch(realm)) == NULL) {
510                 ads_lookup_site();
511                 sitename = sitename_fetch(realm);
512         }
513
514         do {
515                 /* We try once with a sitename and once without
516                    (unless we don't have a sitename and then we're
517                    done */
518
519                 if (sitename == NULL)
520                         done = true;
521
522                 nt_status = ads_dns_query_gcs(frame, realm, sitename,
523                                               &gcs_list, &num_gcs);
524
525                 SAFE_FREE(sitename);
526
527                 if (!NT_STATUS_IS_OK(nt_status)) {
528                         ads_status = ADS_ERROR_NT(nt_status);
529                         goto done;
530                 }
531
532                 /* Loop until we get a successful connection or have gone
533                    through them all.  When connecting a GC server, make sure that
534                    the realm is the server's DNS name and not the forest root */
535
536                 for (i=0; i<num_gcs; i++) {
537                         ads->server.gc = true;
538                         ads->server.ldap_server = SMB_STRDUP(gcs_list[i].hostname);
539                         ads->server.realm = SMB_STRDUP(host_dns_domain(ads->server.ldap_server));
540                         ads_status = ads_connect(ads);
541                         if (ADS_ERR_OK(ads_status)) {
542                                 /* Reset the bind_dn to "".  A Global Catalog server
543                                    may host  multiple domain trees in a forest.
544                                    Windows 2003 GC server will accept "" as the search
545                                    path to imply search all domain trees in the forest */
546
547                                 SAFE_FREE(ads->config.bind_path);
548                                 ads->config.bind_path = SMB_STRDUP("");
549
550
551                                 goto done;
552                         }
553                         SAFE_FREE(ads->server.ldap_server);
554                         SAFE_FREE(ads->server.realm);
555                 }
556
557                 TALLOC_FREE(gcs_list);
558                 num_gcs = 0;
559         } while (!done);
560
561 done:
562         SAFE_FREE(sitename);
563         talloc_destroy(frame);
564
565         return ads_status;
566 }
567
568
569 /**
570  * Connect to the LDAP server
571  * @param ads Pointer to an existing ADS_STRUCT
572  * @return status of connection
573  **/
574 ADS_STATUS ads_connect(ADS_STRUCT *ads)
575 {
576         int version = LDAP_VERSION3;
577         ADS_STATUS status;
578         NTSTATUS ntstatus;
579         char addr[INET6_ADDRSTRLEN];
580
581         ZERO_STRUCT(ads->ldap);
582         ads->ldap.last_attempt  = time(NULL);
583         ads->ldap.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         {
596                 if (ads_try_connect(ads, ads->server.ldap_server, ads->server.gc)) {
597                         goto got_connection;
598                 }
599
600                 /* The choice of which GC use is handled one level up in
601                    ads_connect_gc().  If we continue on from here with
602                    ads_find_dc() we will get GC searches on port 389 which
603                    doesn't work.   --jerry */
604
605                 if (ads->server.gc == true) {
606                         return ADS_ERROR(LDAP_OPERATIONS_ERROR);
607                 }
608         }
609
610         ntstatus = ads_find_dc(ads);
611         if (NT_STATUS_IS_OK(ntstatus)) {
612                 goto got_connection;
613         }
614
615         status = ADS_ERROR_NT(ntstatus);
616         goto out;
617
618 got_connection:
619
620         print_sockaddr(addr, sizeof(addr), &ads->ldap.ss);
621         DEBUG(3,("Successfully contacted LDAP server %s\n", addr));
622
623         if (!ads->auth.user_name) {
624                 /* Must use the userPrincipalName value here or sAMAccountName
625                    and not servicePrincipalName; found by Guenther Deschner */
626
627                 if (asprintf(&ads->auth.user_name, "%s$", global_myname() ) == -1) {
628                         DEBUG(0,("ads_connect: asprintf fail.\n"));
629                         ads->auth.user_name = NULL;
630                 }
631         }
632
633         if (!ads->auth.realm) {
634                 ads->auth.realm = SMB_STRDUP(ads->config.realm);
635         }
636
637         if (!ads->auth.kdc_server) {
638                 print_sockaddr(addr, sizeof(addr), &ads->ldap.ss);
639                 ads->auth.kdc_server = SMB_STRDUP(addr);
640         }
641
642 #if KRB5_DNS_HACK
643         /* this is a really nasty hack to avoid ADS DNS problems. It needs a patch
644            to MIT kerberos to work (tridge) */
645         {
646                 char *env = NULL;
647                 if (asprintf(&env, "KRB5_KDC_ADDRESS_%s", ads->config.realm) > 0) {
648                         setenv(env, ads->auth.kdc_server, 1);
649                         free(env);
650                 }
651         }
652 #endif
653
654         /* If the caller() requested no LDAP bind, then we are done */
655
656         if (ads->auth.flags & ADS_AUTH_NO_BIND) {
657                 status = ADS_SUCCESS;
658                 goto out;
659         }
660
661         ads->ldap.mem_ctx = talloc_init("ads LDAP connection memory");
662         if (!ads->ldap.mem_ctx) {
663                 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
664                 goto out;
665         }
666
667         /* Otherwise setup the TCP LDAP session */
668
669         ads->ldap.ld = ldap_open_with_timeout(ads->config.ldap_server_name,
670                                               ads->ldap.port, lp_ldap_timeout());
671         if (ads->ldap.ld == NULL) {
672                 status = ADS_ERROR(LDAP_OPERATIONS_ERROR);
673                 goto out;
674         }
675         DEBUG(3,("Connected to LDAP server %s\n", ads->config.ldap_server_name));
676
677         /* cache the successful connection for workgroup and realm */
678         if (ads_closest_dc(ads)) {
679                 saf_store( ads->server.workgroup, ads->config.ldap_server_name);
680                 saf_store( ads->server.realm, ads->config.ldap_server_name);
681         }
682
683         ldap_set_option(ads->ldap.ld, LDAP_OPT_PROTOCOL_VERSION, &version);
684
685         if ( lp_ldap_ssl_ads() ) {
686                 status = ADS_ERROR(smb_ldap_start_tls(ads->ldap.ld, version));
687                 if (!ADS_ERR_OK(status)) {
688                         goto out;
689                 }
690         }
691
692         /* fill in the current time and offsets */
693
694         status = ads_current_time( ads );
695         if ( !ADS_ERR_OK(status) ) {
696                 goto out;
697         }
698
699         /* Now do the bind */
700
701         if (ads->auth.flags & ADS_AUTH_ANON_BIND) {
702                 status = ADS_ERROR(ldap_simple_bind_s(ads->ldap.ld, NULL, NULL));
703                 goto out;
704         }
705
706         if (ads->auth.flags & ADS_AUTH_SIMPLE_BIND) {
707                 status = ADS_ERROR(ldap_simple_bind_s(ads->ldap.ld, ads->auth.user_name, ads->auth.password));
708                 goto out;
709         }
710
711         status = ads_sasl_bind(ads);
712
713  out:
714         if (DEBUGLEVEL >= 11) {
715                 char *s = NDR_PRINT_STRUCT_STRING(talloc_tos(), ads_struct, ads);
716                 DEBUG(11,("ads_connect: leaving with: %s\n",
717                         ads_errstr(status)));
718                 DEBUGADD(11,("%s\n", s));
719                 TALLOC_FREE(s);
720         }
721
722         return status;
723 }
724
725 /**
726  * Connect to the LDAP server using given credentials
727  * @param ads Pointer to an existing ADS_STRUCT
728  * @return status of connection
729  **/
730 ADS_STATUS ads_connect_user_creds(ADS_STRUCT *ads)
731 {
732         ads->auth.flags |= ADS_AUTH_USER_CREDS;
733
734         return ads_connect(ads);
735 }
736
737 /**
738  * Disconnect the LDAP server
739  * @param ads Pointer to an existing ADS_STRUCT
740  **/
741 void ads_disconnect(ADS_STRUCT *ads)
742 {
743         if (ads->ldap.ld) {
744                 ldap_unbind(ads->ldap.ld);
745                 ads->ldap.ld = NULL;
746         }
747         if (ads->ldap.wrap_ops && ads->ldap.wrap_ops->disconnect) {
748                 ads->ldap.wrap_ops->disconnect(ads);
749         }
750         if (ads->ldap.mem_ctx) {
751                 talloc_free(ads->ldap.mem_ctx);
752         }
753         ZERO_STRUCT(ads->ldap);
754 }
755
756 /*
757   Duplicate a struct berval into talloc'ed memory
758  */
759 static struct berval *dup_berval(TALLOC_CTX *ctx, const struct berval *in_val)
760 {
761         struct berval *value;
762
763         if (!in_val) return NULL;
764
765         value = TALLOC_ZERO_P(ctx, struct berval);
766         if (value == NULL)
767                 return NULL;
768         if (in_val->bv_len == 0) return value;
769
770         value->bv_len = in_val->bv_len;
771         value->bv_val = (char *)TALLOC_MEMDUP(ctx, in_val->bv_val,
772                                               in_val->bv_len);
773         return value;
774 }
775
776 /*
777   Make a values list out of an array of (struct berval *)
778  */
779 static struct berval **ads_dup_values(TALLOC_CTX *ctx, 
780                                       const struct berval **in_vals)
781 {
782         struct berval **values;
783         int i;
784
785         if (!in_vals) return NULL;
786         for (i=0; in_vals[i]; i++)
787                 ; /* count values */
788         values = TALLOC_ZERO_ARRAY(ctx, struct berval *, i+1);
789         if (!values) return NULL;
790
791         for (i=0; in_vals[i]; i++) {
792                 values[i] = dup_berval(ctx, in_vals[i]);
793         }
794         return values;
795 }
796
797 /*
798   UTF8-encode a values list out of an array of (char *)
799  */
800 static char **ads_push_strvals(TALLOC_CTX *ctx, const char **in_vals)
801 {
802         char **values;
803         int i;
804         size_t size;
805
806         if (!in_vals) return NULL;
807         for (i=0; in_vals[i]; i++)
808                 ; /* count values */
809         values = TALLOC_ZERO_ARRAY(ctx, char *, i+1);
810         if (!values) return NULL;
811
812         for (i=0; in_vals[i]; i++) {
813                 if (!push_utf8_talloc(ctx, &values[i], in_vals[i], &size)) {
814                         TALLOC_FREE(values);
815                         return NULL;
816                 }
817         }
818         return values;
819 }
820
821 /*
822   Pull a (char *) array out of a UTF8-encoded values list
823  */
824 static char **ads_pull_strvals(TALLOC_CTX *ctx, const char **in_vals)
825 {
826         char **values;
827         int i;
828         size_t converted_size;
829
830         if (!in_vals) return NULL;
831         for (i=0; in_vals[i]; i++)
832                 ; /* count values */
833         values = TALLOC_ZERO_ARRAY(ctx, char *, i+1);
834         if (!values) return NULL;
835
836         for (i=0; in_vals[i]; i++) {
837                 if (!pull_utf8_talloc(ctx, &values[i], in_vals[i],
838                                       &converted_size)) {
839                         DEBUG(0,("ads_pull_strvals: pull_utf8_talloc failed: "
840                                  "%s", strerror(errno)));
841                 }
842         }
843         return values;
844 }
845
846 /**
847  * Do a search with paged results.  cookie must be null on the first
848  *  call, and then returned on each subsequent call.  It will be null
849  *  again when the entire search is complete 
850  * @param ads connection to ads server 
851  * @param bind_path Base dn for the search
852  * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
853  * @param expr Search expression - specified in local charset
854  * @param attrs Attributes to retrieve - specified in utf8 or ascii
855  * @param res ** which will contain results - free res* with ads_msgfree()
856  * @param count Number of entries retrieved on this page
857  * @param cookie The paged results cookie to be returned on subsequent calls
858  * @return status of search
859  **/
860 static ADS_STATUS ads_do_paged_search_args(ADS_STRUCT *ads,
861                                            const char *bind_path,
862                                            int scope, const char *expr,
863                                            const char **attrs, void *args,
864                                            LDAPMessage **res, 
865                                            int *count, struct berval **cookie)
866 {
867         int rc, i, version;
868         char *utf8_expr, *utf8_path, **search_attrs = NULL;
869         size_t converted_size;
870         LDAPControl PagedResults, NoReferrals, ExternalCtrl, *controls[4], **rcontrols;
871         BerElement *cookie_be = NULL;
872         struct berval *cookie_bv= NULL;
873         BerElement *ext_be = NULL;
874         struct berval *ext_bv= NULL;
875
876         TALLOC_CTX *ctx;
877         ads_control *external_control = (ads_control *) args;
878
879         *res = NULL;
880
881         if (!(ctx = talloc_init("ads_do_paged_search_args")))
882                 return ADS_ERROR(LDAP_NO_MEMORY);
883
884         /* 0 means the conversion worked but the result was empty 
885            so we only fail if it's -1.  In any case, it always 
886            at least nulls out the dest */
887         if (!push_utf8_talloc(ctx, &utf8_expr, expr, &converted_size) ||
888             !push_utf8_talloc(ctx, &utf8_path, bind_path, &converted_size))
889         {
890                 rc = LDAP_NO_MEMORY;
891                 goto done;
892         }
893
894         if (!attrs || !(*attrs))
895                 search_attrs = NULL;
896         else {
897                 /* This would be the utf8-encoded version...*/
898                 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
899                 if (!(search_attrs = str_list_copy(talloc_tos(), attrs))) {
900                         rc = LDAP_NO_MEMORY;
901                         goto done;
902                 }
903         }
904
905         /* Paged results only available on ldap v3 or later */
906         ldap_get_option(ads->ldap.ld, LDAP_OPT_PROTOCOL_VERSION, &version);
907         if (version < LDAP_VERSION3) {
908                 rc =  LDAP_NOT_SUPPORTED;
909                 goto done;
910         }
911
912         cookie_be = ber_alloc_t(LBER_USE_DER);
913         if (*cookie) {
914                 ber_printf(cookie_be, "{iO}", (ber_int_t) 1000, *cookie);
915                 ber_bvfree(*cookie); /* don't need it from last time */
916                 *cookie = NULL;
917         } else {
918                 ber_printf(cookie_be, "{io}", (ber_int_t) 1000, "", 0);
919         }
920         ber_flatten(cookie_be, &cookie_bv);
921         PagedResults.ldctl_oid = CONST_DISCARD(char *, ADS_PAGE_CTL_OID);
922         PagedResults.ldctl_iscritical = (char) 1;
923         PagedResults.ldctl_value.bv_len = cookie_bv->bv_len;
924         PagedResults.ldctl_value.bv_val = cookie_bv->bv_val;
925
926         NoReferrals.ldctl_oid = CONST_DISCARD(char *, ADS_NO_REFERRALS_OID);
927         NoReferrals.ldctl_iscritical = (char) 0;
928         NoReferrals.ldctl_value.bv_len = 0;
929         NoReferrals.ldctl_value.bv_val = CONST_DISCARD(char *, "");
930
931         if (external_control && 
932             (strequal(external_control->control, ADS_EXTENDED_DN_OID) || 
933              strequal(external_control->control, ADS_SD_FLAGS_OID))) {
934
935                 ExternalCtrl.ldctl_oid = CONST_DISCARD(char *, external_control->control);
936                 ExternalCtrl.ldctl_iscritical = (char) external_control->critical;
937
938                 /* win2k does not accept a ldctl_value beeing passed in */
939
940                 if (external_control->val != 0) {
941
942                         if ((ext_be = ber_alloc_t(LBER_USE_DER)) == NULL ) {
943                                 rc = LDAP_NO_MEMORY;
944                                 goto done;
945                         }
946
947                         if ((ber_printf(ext_be, "{i}", (ber_int_t) external_control->val)) == -1) {
948                                 rc = LDAP_NO_MEMORY;
949                                 goto done;
950                         }
951                         if ((ber_flatten(ext_be, &ext_bv)) == -1) {
952                                 rc = LDAP_NO_MEMORY;
953                                 goto done;
954                         }
955
956                         ExternalCtrl.ldctl_value.bv_len = ext_bv->bv_len;
957                         ExternalCtrl.ldctl_value.bv_val = ext_bv->bv_val;
958
959                 } else {
960                         ExternalCtrl.ldctl_value.bv_len = 0;
961                         ExternalCtrl.ldctl_value.bv_val = NULL;
962                 }
963
964                 controls[0] = &NoReferrals;
965                 controls[1] = &PagedResults;
966                 controls[2] = &ExternalCtrl;
967                 controls[3] = NULL;
968
969         } else {
970                 controls[0] = &NoReferrals;
971                 controls[1] = &PagedResults;
972                 controls[2] = NULL;
973         }
974
975         /* we need to disable referrals as the openldap libs don't
976            handle them and paged results at the same time.  Using them
977            together results in the result record containing the server 
978            page control being removed from the result list (tridge/jmcd) 
979
980            leaving this in despite the control that says don't generate
981            referrals, in case the server doesn't support it (jmcd)
982         */
983         ldap_set_option(ads->ldap.ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
984
985         rc = ldap_search_with_timeout(ads->ldap.ld, utf8_path, scope, utf8_expr, 
986                                       search_attrs, 0, controls,
987                                       NULL, LDAP_NO_LIMIT,
988                                       (LDAPMessage **)res);
989
990         ber_free(cookie_be, 1);
991         ber_bvfree(cookie_bv);
992
993         if (rc) {
994                 DEBUG(3,("ads_do_paged_search_args: ldap_search_with_timeout(%s) -> %s\n", expr,
995                          ldap_err2string(rc)));
996                 goto done;
997         }
998
999         rc = ldap_parse_result(ads->ldap.ld, *res, NULL, NULL, NULL,
1000                                         NULL, &rcontrols,  0);
1001
1002         if (!rcontrols) {
1003                 goto done;
1004         }
1005
1006         for (i=0; rcontrols[i]; i++) {
1007                 if (strcmp(ADS_PAGE_CTL_OID, rcontrols[i]->ldctl_oid) == 0) {
1008                         cookie_be = ber_init(&rcontrols[i]->ldctl_value);
1009                         ber_scanf(cookie_be,"{iO}", (ber_int_t *) count,
1010                                   &cookie_bv);
1011                         /* the berval is the cookie, but must be freed when
1012                            it is all done */
1013                         if (cookie_bv->bv_len) /* still more to do */
1014                                 *cookie=ber_bvdup(cookie_bv);
1015                         else
1016                                 *cookie=NULL;
1017                         ber_bvfree(cookie_bv);
1018                         ber_free(cookie_be, 1);
1019                         break;
1020                 }
1021         }
1022         ldap_controls_free(rcontrols);
1023
1024 done:
1025         talloc_destroy(ctx);
1026
1027         if (ext_be) {
1028                 ber_free(ext_be, 1);
1029         }
1030
1031         if (ext_bv) {
1032                 ber_bvfree(ext_bv);
1033         }
1034
1035         /* if/when we decide to utf8-encode attrs, take out this next line */
1036         TALLOC_FREE(search_attrs);
1037
1038         return ADS_ERROR(rc);
1039 }
1040
1041 static ADS_STATUS ads_do_paged_search(ADS_STRUCT *ads, const char *bind_path,
1042                                       int scope, const char *expr,
1043                                       const char **attrs, LDAPMessage **res, 
1044                                       int *count, struct berval **cookie)
1045 {
1046         return ads_do_paged_search_args(ads, bind_path, scope, expr, attrs, NULL, res, count, cookie);
1047 }
1048
1049
1050 /**
1051  * Get all results for a search.  This uses ads_do_paged_search() to return 
1052  * all entries in a large search.
1053  * @param ads connection to ads server 
1054  * @param bind_path Base dn for the search
1055  * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1056  * @param expr Search expression
1057  * @param attrs Attributes to retrieve
1058  * @param res ** which will contain results - free res* with ads_msgfree()
1059  * @return status of search
1060  **/
1061  ADS_STATUS ads_do_search_all_args(ADS_STRUCT *ads, const char *bind_path,
1062                                    int scope, const char *expr,
1063                                    const char **attrs, void *args,
1064                                    LDAPMessage **res)
1065 {
1066         struct berval *cookie = NULL;
1067         int count = 0;
1068         ADS_STATUS status;
1069
1070         *res = NULL;
1071         status = ads_do_paged_search_args(ads, bind_path, scope, expr, attrs, args, res,
1072                                      &count, &cookie);
1073
1074         if (!ADS_ERR_OK(status)) 
1075                 return status;
1076
1077 #ifdef HAVE_LDAP_ADD_RESULT_ENTRY
1078         while (cookie) {
1079                 LDAPMessage *res2 = NULL;
1080                 ADS_STATUS status2;
1081                 LDAPMessage *msg, *next;
1082
1083                 status2 = ads_do_paged_search_args(ads, bind_path, scope, expr, 
1084                                               attrs, args, &res2, &count, &cookie);
1085
1086                 if (!ADS_ERR_OK(status2)) break;
1087
1088                 /* this relies on the way that ldap_add_result_entry() works internally. I hope
1089                    that this works on all ldap libs, but I have only tested with openldap */
1090                 for (msg = ads_first_message(ads, res2); msg; msg = next) {
1091                         next = ads_next_message(ads, msg);
1092                         ldap_add_result_entry((LDAPMessage **)res, msg);
1093                 }
1094                 /* note that we do not free res2, as the memory is now
1095                    part of the main returned list */
1096         }
1097 #else
1098         DEBUG(0, ("no ldap_add_result_entry() support in LDAP libs!\n"));
1099         status = ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
1100 #endif
1101
1102         return status;
1103 }
1104
1105  ADS_STATUS ads_do_search_all(ADS_STRUCT *ads, const char *bind_path,
1106                               int scope, const char *expr,
1107                               const char **attrs, LDAPMessage **res)
1108 {
1109         return ads_do_search_all_args(ads, bind_path, scope, expr, attrs, NULL, res);
1110 }
1111
1112  ADS_STATUS ads_do_search_all_sd_flags(ADS_STRUCT *ads, const char *bind_path,
1113                                        int scope, const char *expr,
1114                                        const char **attrs, uint32 sd_flags, 
1115                                        LDAPMessage **res)
1116 {
1117         ads_control args;
1118
1119         args.control = ADS_SD_FLAGS_OID;
1120         args.val = sd_flags;
1121         args.critical = True;
1122
1123         return ads_do_search_all_args(ads, bind_path, scope, expr, attrs, &args, res);
1124 }
1125
1126
1127 /**
1128  * Run a function on all results for a search.  Uses ads_do_paged_search() and
1129  *  runs the function as each page is returned, using ads_process_results()
1130  * @param ads connection to ads server
1131  * @param bind_path Base dn for the search
1132  * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1133  * @param expr Search expression - specified in local charset
1134  * @param attrs Attributes to retrieve - specified in UTF-8 or ascii
1135  * @param fn Function which takes attr name, values list, and data_area
1136  * @param data_area Pointer which is passed to function on each call
1137  * @return status of search
1138  **/
1139 ADS_STATUS ads_do_search_all_fn(ADS_STRUCT *ads, const char *bind_path,
1140                                 int scope, const char *expr, const char **attrs,
1141                                 bool (*fn)(ADS_STRUCT *, char *, void **, void *), 
1142                                 void *data_area)
1143 {
1144         struct berval *cookie = NULL;
1145         int count = 0;
1146         ADS_STATUS status;
1147         LDAPMessage *res;
1148
1149         status = ads_do_paged_search(ads, bind_path, scope, expr, attrs, &res,
1150                                      &count, &cookie);
1151
1152         if (!ADS_ERR_OK(status)) return status;
1153
1154         ads_process_results(ads, res, fn, data_area);
1155         ads_msgfree(ads, res);
1156
1157         while (cookie) {
1158                 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs,
1159                                              &res, &count, &cookie);
1160
1161                 if (!ADS_ERR_OK(status)) break;
1162
1163                 ads_process_results(ads, res, fn, data_area);
1164                 ads_msgfree(ads, res);
1165         }
1166
1167         return status;
1168 }
1169
1170 /**
1171  * Do a search with a timeout.
1172  * @param ads connection to ads server
1173  * @param bind_path Base dn for the search
1174  * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1175  * @param expr Search expression
1176  * @param attrs Attributes to retrieve
1177  * @param res ** which will contain results - free res* with ads_msgfree()
1178  * @return status of search
1179  **/
1180  ADS_STATUS ads_do_search(ADS_STRUCT *ads, const char *bind_path, int scope, 
1181                           const char *expr,
1182                           const char **attrs, LDAPMessage **res)
1183 {
1184         int rc;
1185         char *utf8_expr, *utf8_path, **search_attrs = NULL;
1186         size_t converted_size;
1187         TALLOC_CTX *ctx;
1188
1189         *res = NULL;
1190         if (!(ctx = talloc_init("ads_do_search"))) {
1191                 DEBUG(1,("ads_do_search: talloc_init() failed!"));
1192                 return ADS_ERROR(LDAP_NO_MEMORY);
1193         }
1194
1195         /* 0 means the conversion worked but the result was empty 
1196            so we only fail if it's negative.  In any case, it always 
1197            at least nulls out the dest */
1198         if (!push_utf8_talloc(ctx, &utf8_expr, expr, &converted_size) ||
1199             !push_utf8_talloc(ctx, &utf8_path, bind_path, &converted_size))
1200         {
1201                 DEBUG(1,("ads_do_search: push_utf8_talloc() failed!"));
1202                 rc = LDAP_NO_MEMORY;
1203                 goto done;
1204         }
1205
1206         if (!attrs || !(*attrs))
1207                 search_attrs = NULL;
1208         else {
1209                 /* This would be the utf8-encoded version...*/
1210                 /* if (!(search_attrs = ads_push_strvals(ctx, attrs)))  */
1211                 if (!(search_attrs = str_list_copy(talloc_tos(), attrs)))
1212                 {
1213                         DEBUG(1,("ads_do_search: str_list_copy() failed!"));
1214                         rc = LDAP_NO_MEMORY;
1215                         goto done;
1216                 }
1217         }
1218
1219         /* see the note in ads_do_paged_search - we *must* disable referrals */
1220         ldap_set_option(ads->ldap.ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
1221
1222         rc = ldap_search_with_timeout(ads->ldap.ld, utf8_path, scope, utf8_expr,
1223                                       search_attrs, 0, NULL, NULL, 
1224                                       LDAP_NO_LIMIT,
1225                                       (LDAPMessage **)res);
1226
1227         if (rc == LDAP_SIZELIMIT_EXCEEDED) {
1228                 DEBUG(3,("Warning! sizelimit exceeded in ldap. Truncating.\n"));
1229                 rc = 0;
1230         }
1231
1232  done:
1233         talloc_destroy(ctx);
1234         /* if/when we decide to utf8-encode attrs, take out this next line */
1235         TALLOC_FREE(search_attrs);
1236         return ADS_ERROR(rc);
1237 }
1238 /**
1239  * Do a general ADS search
1240  * @param ads connection to ads server
1241  * @param res ** which will contain results - free res* with ads_msgfree()
1242  * @param expr Search expression
1243  * @param attrs Attributes to retrieve
1244  * @return status of search
1245  **/
1246  ADS_STATUS ads_search(ADS_STRUCT *ads, LDAPMessage **res, 
1247                        const char *expr, const char **attrs)
1248 {
1249         return ads_do_search(ads, ads->config.bind_path, LDAP_SCOPE_SUBTREE, 
1250                              expr, attrs, res);
1251 }
1252
1253 /**
1254  * Do a search on a specific DistinguishedName
1255  * @param ads connection to ads server
1256  * @param res ** which will contain results - free res* with ads_msgfree()
1257  * @param dn DistinguishName to search
1258  * @param attrs Attributes to retrieve
1259  * @return status of search
1260  **/
1261  ADS_STATUS ads_search_dn(ADS_STRUCT *ads, LDAPMessage **res, 
1262                           const char *dn, const char **attrs)
1263 {
1264         return ads_do_search(ads, dn, LDAP_SCOPE_BASE, "(objectclass=*)",
1265                              attrs, res);
1266 }
1267
1268 /**
1269  * Free up memory from a ads_search
1270  * @param ads connection to ads server
1271  * @param msg Search results to free
1272  **/
1273  void ads_msgfree(ADS_STRUCT *ads, LDAPMessage *msg)
1274 {
1275         if (!msg) return;
1276         ldap_msgfree(msg);
1277 }
1278
1279 /**
1280  * Get a dn from search results
1281  * @param ads connection to ads server
1282  * @param msg Search result
1283  * @return dn string
1284  **/
1285  char *ads_get_dn(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, LDAPMessage *msg)
1286 {
1287         char *utf8_dn, *unix_dn;
1288         size_t converted_size;
1289
1290         utf8_dn = ldap_get_dn(ads->ldap.ld, msg);
1291
1292         if (!utf8_dn) {
1293                 DEBUG (5, ("ads_get_dn: ldap_get_dn failed\n"));
1294                 return NULL;
1295         }
1296
1297         if (!pull_utf8_talloc(mem_ctx, &unix_dn, utf8_dn, &converted_size)) {
1298                 DEBUG(0,("ads_get_dn: string conversion failure utf8 [%s]\n",
1299                         utf8_dn ));
1300                 return NULL;
1301         }
1302         ldap_memfree(utf8_dn);
1303         return unix_dn;
1304 }
1305
1306 /**
1307  * Get the parent from a dn
1308  * @param dn the dn to return the parent from
1309  * @return parent dn string
1310  **/
1311 char *ads_parent_dn(const char *dn)
1312 {
1313         char *p;
1314
1315         if (dn == NULL) {
1316                 return NULL;
1317         }
1318
1319         p = strchr(dn, ',');
1320
1321         if (p == NULL) {
1322                 return NULL;
1323         }
1324
1325         return p+1;
1326 }
1327
1328 /**
1329  * Find a machine account given a hostname
1330  * @param ads connection to ads server
1331  * @param res ** which will contain results - free res* with ads_msgfree()
1332  * @param host Hostname to search for
1333  * @return status of search
1334  **/
1335  ADS_STATUS ads_find_machine_acct(ADS_STRUCT *ads, LDAPMessage **res,
1336                                   const char *machine)
1337 {
1338         ADS_STATUS status;
1339         char *expr;
1340         const char *attrs[] = {"*", "nTSecurityDescriptor", NULL};
1341
1342         *res = NULL;
1343
1344         /* the easiest way to find a machine account anywhere in the tree
1345            is to look for hostname$ */
1346         if (asprintf(&expr, "(samAccountName=%s$)", machine) == -1) {
1347                 DEBUG(1, ("asprintf failed!\n"));
1348                 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1349         }
1350
1351         status = ads_search(ads, res, expr, attrs);
1352         SAFE_FREE(expr);
1353         return status;
1354 }
1355
1356 /**
1357  * Initialize a list of mods to be used in a modify request
1358  * @param ctx An initialized TALLOC_CTX
1359  * @return allocated ADS_MODLIST
1360  **/
1361 ADS_MODLIST ads_init_mods(TALLOC_CTX *ctx)
1362 {
1363 #define ADS_MODLIST_ALLOC_SIZE 10
1364         LDAPMod **mods;
1365
1366         if ((mods = TALLOC_ZERO_ARRAY(ctx, LDAPMod *, ADS_MODLIST_ALLOC_SIZE + 1)))
1367                 /* -1 is safety to make sure we don't go over the end.
1368                    need to reset it to NULL before doing ldap modify */
1369                 mods[ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1370
1371         return (ADS_MODLIST)mods;
1372 }
1373
1374
1375 /*
1376   add an attribute to the list, with values list already constructed
1377 */
1378 static ADS_STATUS ads_modlist_add(TALLOC_CTX *ctx, ADS_MODLIST *mods, 
1379                                   int mod_op, const char *name, 
1380                                   const void *_invals)
1381 {
1382         const void **invals = (const void **)_invals;
1383         int curmod;
1384         LDAPMod **modlist = (LDAPMod **) *mods;
1385         struct berval **ber_values = NULL;
1386         char **char_values = NULL;
1387
1388         if (!invals) {
1389                 mod_op = LDAP_MOD_DELETE;
1390         } else {
1391                 if (mod_op & LDAP_MOD_BVALUES)
1392                         ber_values = ads_dup_values(ctx, 
1393                                                 (const struct berval **)invals);
1394                 else
1395                         char_values = ads_push_strvals(ctx, 
1396                                                   (const char **) invals);
1397         }
1398
1399         /* find the first empty slot */
1400         for (curmod=0; modlist[curmod] && modlist[curmod] != (LDAPMod *) -1;
1401              curmod++);
1402         if (modlist[curmod] == (LDAPMod *) -1) {
1403                 if (!(modlist = TALLOC_REALLOC_ARRAY(ctx, modlist, LDAPMod *,
1404                                 curmod+ADS_MODLIST_ALLOC_SIZE+1)))
1405                         return ADS_ERROR(LDAP_NO_MEMORY);
1406                 memset(&modlist[curmod], 0, 
1407                        ADS_MODLIST_ALLOC_SIZE*sizeof(LDAPMod *));
1408                 modlist[curmod+ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1409                 *mods = (ADS_MODLIST)modlist;
1410         }
1411
1412         if (!(modlist[curmod] = TALLOC_ZERO_P(ctx, LDAPMod)))
1413                 return ADS_ERROR(LDAP_NO_MEMORY);
1414         modlist[curmod]->mod_type = talloc_strdup(ctx, name);
1415         if (mod_op & LDAP_MOD_BVALUES) {
1416                 modlist[curmod]->mod_bvalues = ber_values;
1417         } else if (mod_op & LDAP_MOD_DELETE) {
1418                 modlist[curmod]->mod_values = NULL;
1419         } else {
1420                 modlist[curmod]->mod_values = char_values;
1421         }
1422
1423         modlist[curmod]->mod_op = mod_op;
1424         return ADS_ERROR(LDAP_SUCCESS);
1425 }
1426
1427 /**
1428  * Add a single string value to a mod list
1429  * @param ctx An initialized TALLOC_CTX
1430  * @param mods An initialized ADS_MODLIST
1431  * @param name The attribute name to add
1432  * @param val The value to add - NULL means DELETE
1433  * @return ADS STATUS indicating success of add
1434  **/
1435 ADS_STATUS ads_mod_str(TALLOC_CTX *ctx, ADS_MODLIST *mods, 
1436                        const char *name, const char *val)
1437 {
1438         const char *values[2];
1439
1440         values[0] = val;
1441         values[1] = NULL;
1442
1443         if (!val)
1444                 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1445         return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE, name, values);
1446 }
1447
1448 /**
1449  * Add an array of string values to a mod list
1450  * @param ctx An initialized TALLOC_CTX
1451  * @param mods An initialized ADS_MODLIST
1452  * @param name The attribute name to add
1453  * @param vals The array of string values to add - NULL means DELETE
1454  * @return ADS STATUS indicating success of add
1455  **/
1456 ADS_STATUS ads_mod_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1457                            const char *name, const char **vals)
1458 {
1459         if (!vals)
1460                 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1461         return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE, 
1462                                name, (const void **) vals);
1463 }
1464
1465 #if 0
1466 /**
1467  * Add a single ber-encoded value to a mod list
1468  * @param ctx An initialized TALLOC_CTX
1469  * @param mods An initialized ADS_MODLIST
1470  * @param name The attribute name to add
1471  * @param val The value to add - NULL means DELETE
1472  * @return ADS STATUS indicating success of add
1473  **/
1474 static ADS_STATUS ads_mod_ber(TALLOC_CTX *ctx, ADS_MODLIST *mods, 
1475                               const char *name, const struct berval *val)
1476 {
1477         const struct berval *values[2];
1478
1479         values[0] = val;
1480         values[1] = NULL;
1481         if (!val)
1482                 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1483         return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE|LDAP_MOD_BVALUES,
1484                                name, (const void **) values);
1485 }
1486 #endif
1487
1488 /**
1489  * Perform an ldap modify
1490  * @param ads connection to ads server
1491  * @param mod_dn DistinguishedName to modify
1492  * @param mods list of modifications to perform
1493  * @return status of modify
1494  **/
1495 ADS_STATUS ads_gen_mod(ADS_STRUCT *ads, const char *mod_dn, ADS_MODLIST mods)
1496 {
1497         int ret,i;
1498         char *utf8_dn = NULL;
1499         size_t converted_size;
1500         /* 
1501            this control is needed to modify that contains a currently 
1502            non-existent attribute (but allowable for the object) to run
1503         */
1504         LDAPControl PermitModify = {
1505                 CONST_DISCARD(char *, ADS_PERMIT_MODIFY_OID),
1506                 {0, NULL},
1507                 (char) 1};
1508         LDAPControl *controls[2];
1509
1510         controls[0] = &PermitModify;
1511         controls[1] = NULL;
1512
1513         if (!push_utf8_talloc(talloc_tos(), &utf8_dn, mod_dn, &converted_size)) {
1514                 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1515         }
1516
1517         /* find the end of the list, marked by NULL or -1 */
1518         for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1519         /* make sure the end of the list is NULL */
1520         mods[i] = NULL;
1521         ret = ldap_modify_ext_s(ads->ldap.ld, utf8_dn,
1522                                 (LDAPMod **) mods, controls, NULL);
1523         TALLOC_FREE(utf8_dn);
1524         return ADS_ERROR(ret);
1525 }
1526
1527 /**
1528  * Perform an ldap add
1529  * @param ads connection to ads server
1530  * @param new_dn DistinguishedName to add
1531  * @param mods list of attributes and values for DN
1532  * @return status of add
1533  **/
1534 ADS_STATUS ads_gen_add(ADS_STRUCT *ads, const char *new_dn, ADS_MODLIST mods)
1535 {
1536         int ret, i;
1537         char *utf8_dn = NULL;
1538         size_t converted_size;
1539
1540         if (!push_utf8_talloc(talloc_tos(), &utf8_dn, new_dn, &converted_size)) {
1541                 DEBUG(1, ("ads_gen_add: push_utf8_talloc failed!"));
1542                 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1543         }
1544
1545         /* find the end of the list, marked by NULL or -1 */
1546         for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1547         /* make sure the end of the list is NULL */
1548         mods[i] = NULL;
1549
1550         ret = ldap_add_s(ads->ldap.ld, utf8_dn, (LDAPMod**)mods);
1551         TALLOC_FREE(utf8_dn);
1552         return ADS_ERROR(ret);
1553 }
1554
1555 /**
1556  * Delete a DistinguishedName
1557  * @param ads connection to ads server
1558  * @param new_dn DistinguishedName to delete
1559  * @return status of delete
1560  **/
1561 ADS_STATUS ads_del_dn(ADS_STRUCT *ads, char *del_dn)
1562 {
1563         int ret;
1564         char *utf8_dn = NULL;
1565         size_t converted_size;
1566         if (!push_utf8_talloc(talloc_tos(), &utf8_dn, del_dn, &converted_size)) {
1567                 DEBUG(1, ("ads_del_dn: push_utf8_talloc failed!"));
1568                 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1569         }
1570
1571         ret = ldap_delete_s(ads->ldap.ld, utf8_dn);
1572         TALLOC_FREE(utf8_dn);
1573         return ADS_ERROR(ret);
1574 }
1575
1576 /**
1577  * Build an org unit string
1578  *  if org unit is Computers or blank then assume a container, otherwise
1579  *  assume a / separated list of organisational units.
1580  * jmcd: '\' is now used for escapes so certain chars can be in the ou (e.g. #)
1581  * @param ads connection to ads server
1582  * @param org_unit Organizational unit
1583  * @return org unit string - caller must free
1584  **/
1585 char *ads_ou_string(ADS_STRUCT *ads, const char *org_unit)
1586 {
1587         char *ret = NULL;
1588
1589         if (!org_unit || !*org_unit) {
1590
1591                 ret = ads_default_ou_string(ads, WELL_KNOWN_GUID_COMPUTERS);
1592
1593                 /* samba4 might not yet respond to a wellknownobject-query */
1594                 return ret ? ret : SMB_STRDUP("cn=Computers");
1595         }
1596
1597         if (strequal(org_unit, "Computers")) {
1598                 return SMB_STRDUP("cn=Computers");
1599         }
1600
1601         /* jmcd: removed "\\" from the separation chars, because it is
1602            needed as an escape for chars like '#' which are valid in an
1603            OU name */
1604         return ads_build_path(org_unit, "/", "ou=", 1);
1605 }
1606
1607 /**
1608  * Get a org unit string for a well-known GUID
1609  * @param ads connection to ads server
1610  * @param wknguid Well known GUID
1611  * @return org unit string - caller must free
1612  **/
1613 char *ads_default_ou_string(ADS_STRUCT *ads, const char *wknguid)
1614 {
1615         ADS_STATUS status;
1616         LDAPMessage *res = NULL;
1617         char *base, *wkn_dn = NULL, *ret = NULL, **wkn_dn_exp = NULL,
1618                 **bind_dn_exp = NULL;
1619         const char *attrs[] = {"distinguishedName", NULL};
1620         int new_ln, wkn_ln, bind_ln, i;
1621
1622         if (wknguid == NULL) {
1623                 return NULL;
1624         }
1625
1626         if (asprintf(&base, "<WKGUID=%s,%s>", wknguid, ads->config.bind_path ) == -1) {
1627                 DEBUG(1, ("asprintf failed!\n"));
1628                 return NULL;
1629         }
1630
1631         status = ads_search_dn(ads, &res, base, attrs);
1632         if (!ADS_ERR_OK(status)) {
1633                 DEBUG(1,("Failed while searching for: %s\n", base));
1634                 goto out;
1635         }
1636
1637         if (ads_count_replies(ads, res) != 1) {
1638                 goto out;
1639         }
1640
1641         /* substitute the bind-path from the well-known-guid-search result */
1642         wkn_dn = ads_get_dn(ads, talloc_tos(), res);
1643         if (!wkn_dn) {
1644                 goto out;
1645         }
1646
1647         wkn_dn_exp = ldap_explode_dn(wkn_dn, 0);
1648         if (!wkn_dn_exp) {
1649                 goto out;
1650         }
1651
1652         bind_dn_exp = ldap_explode_dn(ads->config.bind_path, 0);
1653         if (!bind_dn_exp) {
1654                 goto out;
1655         }
1656
1657         for (wkn_ln=0; wkn_dn_exp[wkn_ln]; wkn_ln++)
1658                 ;
1659         for (bind_ln=0; bind_dn_exp[bind_ln]; bind_ln++)
1660                 ;
1661
1662         new_ln = wkn_ln - bind_ln;
1663
1664         ret = SMB_STRDUP(wkn_dn_exp[0]);
1665         if (!ret) {
1666                 goto out;
1667         }
1668
1669         for (i=1; i < new_ln; i++) {
1670                 char *s = NULL;
1671
1672                 if (asprintf(&s, "%s,%s", ret, wkn_dn_exp[i]) == -1) {
1673                         SAFE_FREE(ret);
1674                         goto out;
1675                 }
1676
1677                 SAFE_FREE(ret);
1678                 ret = SMB_STRDUP(s);
1679                 free(s);
1680                 if (!ret) {
1681                         goto out;
1682                 }
1683         }
1684
1685  out:
1686         SAFE_FREE(base);
1687         ads_msgfree(ads, res);
1688         TALLOC_FREE(wkn_dn);
1689         if (wkn_dn_exp) {
1690                 ldap_value_free(wkn_dn_exp);
1691         }
1692         if (bind_dn_exp) {
1693                 ldap_value_free(bind_dn_exp);
1694         }
1695
1696         return ret;
1697 }
1698
1699 /**
1700  * Adds (appends) an item to an attribute array, rather then
1701  * replacing the whole list
1702  * @param ctx An initialized TALLOC_CTX
1703  * @param mods An initialized ADS_MODLIST
1704  * @param name name of the ldap attribute to append to
1705  * @param vals an array of values to add
1706  * @return status of addition
1707  **/
1708
1709 ADS_STATUS ads_add_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1710                                 const char *name, const char **vals)
1711 {
1712         return ads_modlist_add(ctx, mods, LDAP_MOD_ADD, name,
1713                                (const void *) vals);
1714 }
1715
1716 /**
1717  * Determines the an account's current KVNO via an LDAP lookup
1718  * @param ads An initialized ADS_STRUCT
1719  * @param account_name the NT samaccountname.
1720  * @return the kvno for the account, or -1 in case of a failure.
1721  **/
1722
1723 uint32 ads_get_kvno(ADS_STRUCT *ads, const char *account_name)
1724 {
1725         LDAPMessage *res = NULL;
1726         uint32 kvno = (uint32)-1;      /* -1 indicates a failure */
1727         char *filter;
1728         const char *attrs[] = {"msDS-KeyVersionNumber", NULL};
1729         char *dn_string = NULL;
1730         ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS);
1731
1732         DEBUG(5,("ads_get_kvno: Searching for account %s\n", account_name));
1733         if (asprintf(&filter, "(samAccountName=%s)", account_name) == -1) {
1734                 return kvno;
1735         }
1736         ret = ads_search(ads, &res, filter, attrs);
1737         SAFE_FREE(filter);
1738         if (!ADS_ERR_OK(ret) || (ads_count_replies(ads, res) != 1)) {
1739                 DEBUG(1,("ads_get_kvno: Account for %s not found.\n", account_name));
1740                 ads_msgfree(ads, res);
1741                 return kvno;
1742         }
1743
1744         dn_string = ads_get_dn(ads, talloc_tos(), res);
1745         if (!dn_string) {
1746                 DEBUG(0,("ads_get_kvno: out of memory.\n"));
1747                 ads_msgfree(ads, res);
1748                 return kvno;
1749         }
1750         DEBUG(5,("ads_get_kvno: Using: %s\n", dn_string));
1751         TALLOC_FREE(dn_string);
1752
1753         /* ---------------------------------------------------------
1754          * 0 is returned as a default KVNO from this point on...
1755          * This is done because Windows 2000 does not support key
1756          * version numbers.  Chances are that a failure in the next
1757          * step is simply due to Windows 2000 being used for a
1758          * domain controller. */
1759         kvno = 0;
1760
1761         if (!ads_pull_uint32(ads, res, "msDS-KeyVersionNumber", &kvno)) {
1762                 DEBUG(3,("ads_get_kvno: Error Determining KVNO!\n"));
1763                 DEBUG(3,("ads_get_kvno: Windows 2000 does not support KVNO's, so this may be normal.\n"));
1764                 ads_msgfree(ads, res);
1765                 return kvno;
1766         }
1767
1768         /* Success */
1769         DEBUG(5,("ads_get_kvno: Looked Up KVNO of: %d\n", kvno));
1770         ads_msgfree(ads, res);
1771         return kvno;
1772 }
1773
1774 /**
1775  * Determines the computer account's current KVNO via an LDAP lookup
1776  * @param ads An initialized ADS_STRUCT
1777  * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1778  * @return the kvno for the computer account, or -1 in case of a failure.
1779  **/
1780
1781 uint32_t ads_get_machine_kvno(ADS_STRUCT *ads, const char *machine_name)
1782 {
1783         char *computer_account = NULL;
1784         uint32_t kvno = -1;
1785
1786         if (asprintf(&computer_account, "%s$", machine_name) < 0) {
1787                 return kvno;
1788         }
1789
1790         kvno = ads_get_kvno(ads, computer_account);
1791         free(computer_account);
1792
1793         return kvno;
1794 }
1795
1796 /**
1797  * This clears out all registered spn's for a given hostname
1798  * @param ads An initilaized ADS_STRUCT
1799  * @param machine_name the NetBIOS name of the computer.
1800  * @return 0 upon success, non-zero otherwise.
1801  **/
1802
1803 ADS_STATUS ads_clear_service_principal_names(ADS_STRUCT *ads, const char *machine_name)
1804 {
1805         TALLOC_CTX *ctx;
1806         LDAPMessage *res = NULL;
1807         ADS_MODLIST mods;
1808         const char *servicePrincipalName[1] = {NULL};
1809         ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS);
1810         char *dn_string = NULL;
1811
1812         ret = ads_find_machine_acct(ads, &res, machine_name);
1813         if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
1814                 DEBUG(5,("ads_clear_service_principal_names: WARNING: Host Account for %s not found... skipping operation.\n", machine_name));
1815                 DEBUG(5,("ads_clear_service_principal_names: WARNING: Service Principals for %s have NOT been cleared.\n", machine_name));
1816                 ads_msgfree(ads, res);
1817                 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1818         }
1819
1820         DEBUG(5,("ads_clear_service_principal_names: Host account for %s found\n", machine_name));
1821         ctx = talloc_init("ads_clear_service_principal_names");
1822         if (!ctx) {
1823                 ads_msgfree(ads, res);
1824                 return ADS_ERROR(LDAP_NO_MEMORY);
1825         }
1826
1827         if (!(mods = ads_init_mods(ctx))) {
1828                 talloc_destroy(ctx);
1829                 ads_msgfree(ads, res);
1830                 return ADS_ERROR(LDAP_NO_MEMORY);
1831         }
1832         ret = ads_mod_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1833         if (!ADS_ERR_OK(ret)) {
1834                 DEBUG(1,("ads_clear_service_principal_names: Error creating strlist.\n"));
1835                 ads_msgfree(ads, res);
1836                 talloc_destroy(ctx);
1837                 return ret;
1838         }
1839         dn_string = ads_get_dn(ads, talloc_tos(), res);
1840         if (!dn_string) {
1841                 talloc_destroy(ctx);
1842                 ads_msgfree(ads, res);
1843                 return ADS_ERROR(LDAP_NO_MEMORY);
1844         }
1845         ret = ads_gen_mod(ads, dn_string, mods);
1846         TALLOC_FREE(dn_string);
1847         if (!ADS_ERR_OK(ret)) {
1848                 DEBUG(1,("ads_clear_service_principal_names: Error: Updating Service Principals for machine %s in LDAP\n",
1849                         machine_name));
1850                 ads_msgfree(ads, res);
1851                 talloc_destroy(ctx);
1852                 return ret;
1853         }
1854
1855         ads_msgfree(ads, res);
1856         talloc_destroy(ctx);
1857         return ret;
1858 }
1859
1860 /**
1861  * This adds a service principal name to an existing computer account
1862  * (found by hostname) in AD.
1863  * @param ads An initialized ADS_STRUCT
1864  * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1865  * @param my_fqdn The fully qualified DNS name of the machine
1866  * @param spn A string of the service principal to add, i.e. 'host'
1867  * @return 0 upon sucess, or non-zero if a failure occurs
1868  **/
1869
1870 ADS_STATUS ads_add_service_principal_name(ADS_STRUCT *ads, const char *machine_name, 
1871                                           const char *my_fqdn, const char *spn)
1872 {
1873         ADS_STATUS ret;
1874         TALLOC_CTX *ctx;
1875         LDAPMessage *res = NULL;
1876         char *psp1, *psp2;
1877         ADS_MODLIST mods;
1878         char *dn_string = NULL;
1879         const char *servicePrincipalName[3] = {NULL, NULL, NULL};
1880
1881         ret = ads_find_machine_acct(ads, &res, machine_name);
1882         if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
1883                 DEBUG(1,("ads_add_service_principal_name: WARNING: Host Account for %s not found... skipping operation.\n",
1884                         machine_name));
1885                 DEBUG(1,("ads_add_service_principal_name: WARNING: Service Principal '%s/%s@%s' has NOT been added.\n",
1886                         spn, machine_name, ads->config.realm));
1887                 ads_msgfree(ads, res);
1888                 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1889         }
1890
1891         DEBUG(1,("ads_add_service_principal_name: Host account for %s found\n", machine_name));
1892         if (!(ctx = talloc_init("ads_add_service_principal_name"))) {
1893                 ads_msgfree(ads, res);
1894                 return ADS_ERROR(LDAP_NO_MEMORY);
1895         }
1896
1897         /* add short name spn */
1898
1899         if ( (psp1 = talloc_asprintf(ctx, "%s/%s", spn, machine_name)) == NULL ) {
1900                 talloc_destroy(ctx);
1901                 ads_msgfree(ads, res);
1902                 return ADS_ERROR(LDAP_NO_MEMORY);
1903         }
1904         strupper_m(psp1);
1905         strlower_m(&psp1[strlen(spn)]);
1906         servicePrincipalName[0] = psp1;
1907
1908         DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n", 
1909                 psp1, machine_name));
1910
1911
1912         /* add fully qualified spn */
1913
1914         if ( (psp2 = talloc_asprintf(ctx, "%s/%s", spn, my_fqdn)) == NULL ) {
1915                 ret = ADS_ERROR(LDAP_NO_MEMORY);
1916                 goto out;
1917         }
1918         strupper_m(psp2);
1919         strlower_m(&psp2[strlen(spn)]);
1920         servicePrincipalName[1] = psp2;
1921
1922         DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n", 
1923                 psp2, machine_name));
1924
1925         if ( (mods = ads_init_mods(ctx)) == NULL ) {
1926                 ret = ADS_ERROR(LDAP_NO_MEMORY);
1927                 goto out;
1928         }
1929
1930         ret = ads_add_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1931         if (!ADS_ERR_OK(ret)) {
1932                 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
1933                 goto out;
1934         }
1935
1936         if ( (dn_string = ads_get_dn(ads, ctx, res)) == NULL ) {
1937                 ret = ADS_ERROR(LDAP_NO_MEMORY);
1938                 goto out;
1939         }
1940
1941         ret = ads_gen_mod(ads, dn_string, mods);
1942         if (!ADS_ERR_OK(ret)) {
1943                 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
1944                 goto out;
1945         }
1946
1947  out:
1948         TALLOC_FREE( ctx );
1949         ads_msgfree(ads, res);
1950         return ret;
1951 }
1952
1953 /**
1954  * adds a machine account to the ADS server
1955  * @param ads An intialized ADS_STRUCT
1956  * @param machine_name - the NetBIOS machine name of this account.
1957  * @param account_type A number indicating the type of account to create
1958  * @param org_unit The LDAP path in which to place this account
1959  * @return 0 upon success, or non-zero otherwise
1960 **/
1961
1962 ADS_STATUS ads_create_machine_acct(ADS_STRUCT *ads, const char *machine_name, 
1963                                    const char *org_unit)
1964 {
1965         ADS_STATUS ret;
1966         char *samAccountName, *controlstr;
1967         TALLOC_CTX *ctx;
1968         ADS_MODLIST mods;
1969         char *machine_escaped = NULL;
1970         char *new_dn;
1971         const char *objectClass[] = {"top", "person", "organizationalPerson",
1972                                      "user", "computer", NULL};
1973         LDAPMessage *res = NULL;
1974         uint32 acct_control = ( UF_WORKSTATION_TRUST_ACCOUNT |\
1975                                 UF_DONT_EXPIRE_PASSWD |\
1976                                 UF_ACCOUNTDISABLE );
1977
1978         if (!(ctx = talloc_init("ads_add_machine_acct")))
1979                 return ADS_ERROR(LDAP_NO_MEMORY);
1980
1981         ret = ADS_ERROR(LDAP_NO_MEMORY);
1982
1983         machine_escaped = escape_rdn_val_string_alloc(machine_name);
1984         if (!machine_escaped) {
1985                 goto done;
1986         }
1987
1988         new_dn = talloc_asprintf(ctx, "cn=%s,%s", machine_escaped, org_unit);
1989         samAccountName = talloc_asprintf(ctx, "%s$", machine_name);
1990
1991         if ( !new_dn || !samAccountName ) {
1992                 goto done;
1993         }
1994
1995 #ifndef ENCTYPE_ARCFOUR_HMAC
1996         acct_control |= UF_USE_DES_KEY_ONLY;
1997 #endif
1998
1999         if (!(controlstr = talloc_asprintf(ctx, "%u", acct_control))) {
2000                 goto done;
2001         }
2002
2003         if (!(mods = ads_init_mods(ctx))) {
2004                 goto done;
2005         }
2006
2007         ads_mod_str(ctx, &mods, "cn", machine_name);
2008         ads_mod_str(ctx, &mods, "sAMAccountName", samAccountName);
2009         ads_mod_strlist(ctx, &mods, "objectClass", objectClass);
2010         ads_mod_str(ctx, &mods, "userAccountControl", controlstr);
2011
2012         ret = ads_gen_add(ads, new_dn, mods);
2013
2014 done:
2015         SAFE_FREE(machine_escaped);
2016         ads_msgfree(ads, res);
2017         talloc_destroy(ctx);
2018
2019         return ret;
2020 }
2021
2022 /**
2023  * move a machine account to another OU on the ADS server
2024  * @param ads - An intialized ADS_STRUCT
2025  * @param machine_name - the NetBIOS machine name of this account.
2026  * @param org_unit - The LDAP path in which to place this account
2027  * @param moved - whether we moved the machine account (optional)
2028  * @return 0 upon success, or non-zero otherwise
2029 **/
2030
2031 ADS_STATUS ads_move_machine_acct(ADS_STRUCT *ads, const char *machine_name, 
2032                                  const char *org_unit, bool *moved)
2033 {
2034         ADS_STATUS rc;
2035         int ldap_status;
2036         LDAPMessage *res = NULL;
2037         char *filter = NULL;
2038         char *computer_dn = NULL;
2039         char *parent_dn;
2040         char *computer_rdn = NULL;
2041         bool need_move = False;
2042
2043         if (asprintf(&filter, "(samAccountName=%s$)", machine_name) == -1) {
2044                 rc = ADS_ERROR(LDAP_NO_MEMORY);
2045                 goto done;
2046         }
2047
2048         /* Find pre-existing machine */
2049         rc = ads_search(ads, &res, filter, NULL);
2050         if (!ADS_ERR_OK(rc)) {
2051                 goto done;
2052         }
2053
2054         computer_dn = ads_get_dn(ads, talloc_tos(), res);
2055         if (!computer_dn) {
2056                 rc = ADS_ERROR(LDAP_NO_MEMORY);
2057                 goto done;
2058         }
2059
2060         parent_dn = ads_parent_dn(computer_dn);
2061         if (strequal(parent_dn, org_unit)) {
2062                 goto done;
2063         }
2064
2065         need_move = True;
2066
2067         if (asprintf(&computer_rdn, "CN=%s", machine_name) == -1) {
2068                 rc = ADS_ERROR(LDAP_NO_MEMORY);
2069                 goto done;
2070         }
2071
2072         ldap_status = ldap_rename_s(ads->ldap.ld, computer_dn, computer_rdn, 
2073                                     org_unit, 1, NULL, NULL);
2074         rc = ADS_ERROR(ldap_status);
2075
2076 done:
2077         ads_msgfree(ads, res);
2078         SAFE_FREE(filter);
2079         SAFE_FREE(computer_dn);
2080         SAFE_FREE(computer_rdn);
2081
2082         if (!ADS_ERR_OK(rc)) {
2083                 need_move = False;
2084         }
2085
2086         if (moved) {
2087                 *moved = need_move;
2088         }
2089
2090         return rc;
2091 }
2092
2093 /*
2094   dump a binary result from ldap
2095 */
2096 static void dump_binary(ADS_STRUCT *ads, const char *field, struct berval **values)
2097 {
2098         int i, j;
2099         for (i=0; values[i]; i++) {
2100                 printf("%s: ", field);
2101                 for (j=0; j<values[i]->bv_len; j++) {
2102                         printf("%02X", (unsigned char)values[i]->bv_val[j]);
2103                 }
2104                 printf("\n");
2105         }
2106 }
2107
2108 static void dump_guid(ADS_STRUCT *ads, const char *field, struct berval **values)
2109 {
2110         int i;
2111         for (i=0; values[i]; i++) {
2112
2113                 UUID_FLAT guid;
2114                 struct GUID tmp;
2115
2116                 memcpy(guid.info, values[i]->bv_val, sizeof(guid.info));
2117                 smb_uuid_unpack(guid, &tmp);
2118                 printf("%s: %s\n", field, GUID_string(talloc_tos(), &tmp));
2119         }
2120 }
2121
2122 /*
2123   dump a sid result from ldap
2124 */
2125 static void dump_sid(ADS_STRUCT *ads, const char *field, struct berval **values)
2126 {
2127         int i;
2128         for (i=0; values[i]; i++) {
2129                 DOM_SID sid;
2130                 fstring tmp;
2131                 sid_parse(values[i]->bv_val, values[i]->bv_len, &sid);
2132                 printf("%s: %s\n", field, sid_to_fstring(tmp, &sid));
2133         }
2134 }
2135
2136 /*
2137   dump ntSecurityDescriptor
2138 */
2139 static void dump_sd(ADS_STRUCT *ads, const char *filed, struct berval **values)
2140 {
2141         TALLOC_CTX *frame = talloc_stackframe();
2142         struct security_descriptor *psd;
2143         NTSTATUS status;
2144
2145         status = unmarshall_sec_desc(talloc_tos(), (uint8 *)values[0]->bv_val,
2146                                      values[0]->bv_len, &psd);
2147         if (!NT_STATUS_IS_OK(status)) {
2148                 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
2149                           nt_errstr(status)));
2150                 TALLOC_FREE(frame);
2151                 return;
2152         }
2153
2154         if (psd) {
2155                 ads_disp_sd(ads, talloc_tos(), psd);
2156         }
2157
2158         TALLOC_FREE(frame);
2159 }
2160
2161 /*
2162   dump a string result from ldap
2163 */
2164 static void dump_string(const char *field, char **values)
2165 {
2166         int i;
2167         for (i=0; values[i]; i++) {
2168                 printf("%s: %s\n", field, values[i]);
2169         }
2170 }
2171
2172 /*
2173   dump a field from LDAP on stdout
2174   used for debugging
2175 */
2176
2177 static bool ads_dump_field(ADS_STRUCT *ads, char *field, void **values, void *data_area)
2178 {
2179         const struct {
2180                 const char *name;
2181                 bool string;
2182                 void (*handler)(ADS_STRUCT *, const char *, struct berval **);
2183         } handlers[] = {
2184                 {"objectGUID", False, dump_guid},
2185                 {"netbootGUID", False, dump_guid},
2186                 {"nTSecurityDescriptor", False, dump_sd},
2187                 {"dnsRecord", False, dump_binary},
2188                 {"objectSid", False, dump_sid},
2189                 {"tokenGroups", False, dump_sid},
2190                 {"tokenGroupsNoGCAcceptable", False, dump_sid},
2191                 {"tokengroupsGlobalandUniversal", False, dump_sid},
2192                 {"mS-DS-CreatorSID", False, dump_sid},
2193                 {"msExchMailboxGuid", False, dump_guid},
2194                 {NULL, True, NULL}
2195         };
2196         int i;
2197
2198         if (!field) { /* must be end of an entry */
2199                 printf("\n");
2200                 return False;
2201         }
2202
2203         for (i=0; handlers[i].name; i++) {
2204                 if (StrCaseCmp(handlers[i].name, field) == 0) {
2205                         if (!values) /* first time, indicate string or not */
2206                                 return handlers[i].string;
2207                         handlers[i].handler(ads, field, (struct berval **) values);
2208                         break;
2209                 }
2210         }
2211         if (!handlers[i].name) {
2212                 if (!values) /* first time, indicate string conversion */
2213                         return True;
2214                 dump_string(field, (char **)values);
2215         }
2216         return False;
2217 }
2218
2219 /**
2220  * Dump a result from LDAP on stdout
2221  *  used for debugging
2222  * @param ads connection to ads server
2223  * @param res Results to dump
2224  **/
2225
2226  void ads_dump(ADS_STRUCT *ads, LDAPMessage *res)
2227 {
2228         ads_process_results(ads, res, ads_dump_field, NULL);
2229 }
2230
2231 /**
2232  * Walk through results, calling a function for each entry found.
2233  *  The function receives a field name, a berval * array of values,
2234  *  and a data area passed through from the start.  The function is
2235  *  called once with null for field and values at the end of each
2236  *  entry.
2237  * @param ads connection to ads server
2238  * @param res Results to process
2239  * @param fn Function for processing each result
2240  * @param data_area user-defined area to pass to function
2241  **/
2242  void ads_process_results(ADS_STRUCT *ads, LDAPMessage *res,
2243                           bool (*fn)(ADS_STRUCT *, char *, void **, void *),
2244                           void *data_area)
2245 {
2246         LDAPMessage *msg;
2247         TALLOC_CTX *ctx;
2248         size_t converted_size;
2249
2250         if (!(ctx = talloc_init("ads_process_results")))
2251                 return;
2252
2253         for (msg = ads_first_entry(ads, res); msg; 
2254              msg = ads_next_entry(ads, msg)) {
2255                 char *utf8_field;
2256                 BerElement *b;
2257
2258                 for (utf8_field=ldap_first_attribute(ads->ldap.ld,
2259                                                      (LDAPMessage *)msg,&b); 
2260                      utf8_field;
2261                      utf8_field=ldap_next_attribute(ads->ldap.ld,
2262                                                     (LDAPMessage *)msg,b)) {
2263                         struct berval **ber_vals;
2264                         char **str_vals, **utf8_vals;
2265                         char *field;
2266                         bool string; 
2267
2268                         if (!pull_utf8_talloc(ctx, &field, utf8_field,
2269                                               &converted_size))
2270                         {
2271                                 DEBUG(0,("ads_process_results: "
2272                                          "pull_utf8_talloc failed: %s",
2273                                          strerror(errno)));
2274                         }
2275
2276                         string = fn(ads, field, NULL, data_area);
2277
2278                         if (string) {
2279                                 utf8_vals = ldap_get_values(ads->ldap.ld,
2280                                                  (LDAPMessage *)msg, field);
2281                                 str_vals = ads_pull_strvals(ctx, 
2282                                                   (const char **) utf8_vals);
2283                                 fn(ads, field, (void **) str_vals, data_area);
2284                                 ldap_value_free(utf8_vals);
2285                         } else {
2286                                 ber_vals = ldap_get_values_len(ads->ldap.ld, 
2287                                                  (LDAPMessage *)msg, field);
2288                                 fn(ads, field, (void **) ber_vals, data_area);
2289
2290                                 ldap_value_free_len(ber_vals);
2291                         }
2292                         ldap_memfree(utf8_field);
2293                 }
2294                 ber_free(b, 0);
2295                 talloc_free_children(ctx);
2296                 fn(ads, NULL, NULL, data_area); /* completed an entry */
2297
2298         }
2299         talloc_destroy(ctx);
2300 }
2301
2302 /**
2303  * count how many replies are in a LDAPMessage
2304  * @param ads connection to ads server
2305  * @param res Results to count
2306  * @return number of replies
2307  **/
2308 int ads_count_replies(ADS_STRUCT *ads, void *res)
2309 {
2310         return ldap_count_entries(ads->ldap.ld, (LDAPMessage *)res);
2311 }
2312
2313 /**
2314  * pull the first entry from a ADS result
2315  * @param ads connection to ads server
2316  * @param res Results of search
2317  * @return first entry from result
2318  **/
2319  LDAPMessage *ads_first_entry(ADS_STRUCT *ads, LDAPMessage *res)
2320 {
2321         return ldap_first_entry(ads->ldap.ld, res);
2322 }
2323
2324 /**
2325  * pull the next entry from a ADS result
2326  * @param ads connection to ads server
2327  * @param res Results of search
2328  * @return next entry from result
2329  **/
2330  LDAPMessage *ads_next_entry(ADS_STRUCT *ads, LDAPMessage *res)
2331 {
2332         return ldap_next_entry(ads->ldap.ld, res);
2333 }
2334
2335 /**
2336  * pull the first message from a ADS result
2337  * @param ads connection to ads server
2338  * @param res Results of search
2339  * @return first message from result
2340  **/
2341  LDAPMessage *ads_first_message(ADS_STRUCT *ads, LDAPMessage *res)
2342 {
2343         return ldap_first_message(ads->ldap.ld, res);
2344 }
2345
2346 /**
2347  * pull the next message from a ADS result
2348  * @param ads connection to ads server
2349  * @param res Results of search
2350  * @return next message from result
2351  **/
2352  LDAPMessage *ads_next_message(ADS_STRUCT *ads, LDAPMessage *res)
2353 {
2354         return ldap_next_message(ads->ldap.ld, res);
2355 }
2356
2357 /**
2358  * pull a single string from a ADS result
2359  * @param ads connection to ads server
2360  * @param mem_ctx TALLOC_CTX to use for allocating result string
2361  * @param msg Results of search
2362  * @param field Attribute to retrieve
2363  * @return Result string in talloc context
2364  **/
2365  char *ads_pull_string(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, LDAPMessage *msg,
2366                        const char *field)
2367 {
2368         char **values;
2369         char *ret = NULL;
2370         char *ux_string;
2371         size_t converted_size;
2372
2373         values = ldap_get_values(ads->ldap.ld, msg, field);
2374         if (!values)
2375                 return NULL;
2376
2377         if (values[0] && pull_utf8_talloc(mem_ctx, &ux_string, values[0],
2378                                           &converted_size))
2379         {
2380                 ret = ux_string;
2381         }
2382         ldap_value_free(values);
2383         return ret;
2384 }
2385
2386 /**
2387  * pull an array of strings from a ADS result
2388  * @param ads connection to ads server
2389  * @param mem_ctx TALLOC_CTX to use for allocating result string
2390  * @param msg Results of search
2391  * @param field Attribute to retrieve
2392  * @return Result strings in talloc context
2393  **/
2394  char **ads_pull_strings(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2395                          LDAPMessage *msg, const char *field,
2396                          size_t *num_values)
2397 {
2398         char **values;
2399         char **ret = NULL;
2400         int i;
2401         size_t converted_size;
2402
2403         values = ldap_get_values(ads->ldap.ld, msg, field);
2404         if (!values)
2405                 return NULL;
2406
2407         *num_values = ldap_count_values(values);
2408
2409         ret = TALLOC_ARRAY(mem_ctx, char *, *num_values + 1);
2410         if (!ret) {
2411                 ldap_value_free(values);
2412                 return NULL;
2413         }
2414
2415         for (i=0;i<*num_values;i++) {
2416                 if (!pull_utf8_talloc(mem_ctx, &ret[i], values[i],
2417                                       &converted_size))
2418                 {
2419                         ldap_value_free(values);
2420                         return NULL;
2421                 }
2422         }
2423         ret[i] = NULL;
2424
2425         ldap_value_free(values);
2426         return ret;
2427 }
2428
2429 /**
2430  * pull an array of strings from a ADS result 
2431  *  (handle large multivalue attributes with range retrieval)
2432  * @param ads connection to ads server
2433  * @param mem_ctx TALLOC_CTX to use for allocating result string
2434  * @param msg Results of search
2435  * @param field Attribute to retrieve
2436  * @param current_strings strings returned by a previous call to this function
2437  * @param next_attribute The next query should ask for this attribute
2438  * @param num_values How many values did we get this time?
2439  * @param more_values Are there more values to get?
2440  * @return Result strings in talloc context
2441  **/
2442  char **ads_pull_strings_range(ADS_STRUCT *ads, 
2443                                TALLOC_CTX *mem_ctx,
2444                                LDAPMessage *msg, const char *field,
2445                                char **current_strings,
2446                                const char **next_attribute,
2447                                size_t *num_strings,
2448                                bool *more_strings)
2449 {
2450         char *attr;
2451         char *expected_range_attrib, *range_attr;
2452         BerElement *ptr = NULL;
2453         char **strings;
2454         char **new_strings;
2455         size_t num_new_strings;
2456         unsigned long int range_start;
2457         unsigned long int range_end;
2458
2459         /* we might have been given the whole lot anyway */
2460         if ((strings = ads_pull_strings(ads, mem_ctx, msg, field, num_strings))) {
2461                 *more_strings = False;
2462                 return strings;
2463         }
2464
2465         expected_range_attrib = talloc_asprintf(mem_ctx, "%s;Range=", field);
2466
2467         /* look for Range result */
2468         for (attr = ldap_first_attribute(ads->ldap.ld, (LDAPMessage *)msg, &ptr); 
2469              attr; 
2470              attr = ldap_next_attribute(ads->ldap.ld, (LDAPMessage *)msg, ptr)) {
2471                 /* we ignore the fact that this is utf8, as all attributes are ascii... */
2472                 if (strnequal(attr, expected_range_attrib, strlen(expected_range_attrib))) {
2473                         range_attr = attr;
2474                         break;
2475                 }
2476                 ldap_memfree(attr);
2477         }
2478         if (!attr) {
2479                 ber_free(ptr, 0);
2480                 /* nothing here - this field is just empty */
2481                 *more_strings = False;
2482                 return NULL;
2483         }
2484
2485         if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-%lu", 
2486                    &range_start, &range_end) == 2) {
2487                 *more_strings = True;
2488         } else {
2489                 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-*", 
2490                            &range_start) == 1) {
2491                         *more_strings = False;
2492                 } else {
2493                         DEBUG(1, ("ads_pull_strings_range:  Cannot parse Range attriubte (%s)\n", 
2494                                   range_attr));
2495                         ldap_memfree(range_attr);
2496                         *more_strings = False;
2497                         return NULL;
2498                 }
2499         }
2500
2501         if ((*num_strings) != range_start) {
2502                 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) doesn't start at %u, but at %lu"
2503                           " - aborting range retreival\n",
2504                           range_attr, (unsigned int)(*num_strings) + 1, range_start));
2505                 ldap_memfree(range_attr);
2506                 *more_strings = False;
2507                 return NULL;
2508         }
2509
2510         new_strings = ads_pull_strings(ads, mem_ctx, msg, range_attr, &num_new_strings);
2511
2512         if (*more_strings && ((*num_strings + num_new_strings) != (range_end + 1))) {
2513                 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) tells us we have %lu "
2514                           "strings in this bunch, but we only got %lu - aborting range retreival\n",
2515                           range_attr, (unsigned long int)range_end - range_start + 1, 
2516                           (unsigned long int)num_new_strings));
2517                 ldap_memfree(range_attr);
2518                 *more_strings = False;
2519                 return NULL;
2520         }
2521
2522         strings = TALLOC_REALLOC_ARRAY(mem_ctx, current_strings, char *,
2523                                  *num_strings + num_new_strings);
2524
2525         if (strings == NULL) {
2526                 ldap_memfree(range_attr);
2527                 *more_strings = False;
2528                 return NULL;
2529         }
2530
2531         if (new_strings && num_new_strings) {
2532                 memcpy(&strings[*num_strings], new_strings,
2533                        sizeof(*new_strings) * num_new_strings);
2534         }
2535
2536         (*num_strings) += num_new_strings;
2537
2538         if (*more_strings) {
2539                 *next_attribute = talloc_asprintf(mem_ctx,
2540                                                   "%s;range=%d-*", 
2541                                                   field,
2542                                                   (int)*num_strings);
2543
2544                 if (!*next_attribute) {
2545                         DEBUG(1, ("talloc_asprintf for next attribute failed!\n"));
2546                         ldap_memfree(range_attr);
2547                         *more_strings = False;
2548                         return NULL;
2549                 }
2550         }
2551
2552         ldap_memfree(range_attr);
2553
2554         return strings;
2555 }
2556
2557 /**
2558  * pull a single uint32 from a ADS result
2559  * @param ads connection to ads server
2560  * @param msg Results of search
2561  * @param field Attribute to retrieve
2562  * @param v Pointer to int to store result
2563  * @return boolean inidicating success
2564 */
2565  bool ads_pull_uint32(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
2566                       uint32 *v)
2567 {
2568         char **values;
2569
2570         values = ldap_get_values(ads->ldap.ld, msg, field);
2571         if (!values)
2572                 return False;
2573         if (!values[0]) {
2574                 ldap_value_free(values);
2575                 return False;
2576         }
2577
2578         *v = atoi(values[0]);
2579         ldap_value_free(values);
2580         return True;
2581 }
2582
2583 /**
2584  * pull a single objectGUID from an ADS result
2585  * @param ads connection to ADS server
2586  * @param msg results of search
2587  * @param guid 37-byte area to receive text guid
2588  * @return boolean indicating success
2589  **/
2590  bool ads_pull_guid(ADS_STRUCT *ads, LDAPMessage *msg, struct GUID *guid)
2591 {
2592         char **values;
2593         UUID_FLAT flat_guid;
2594
2595         values = ldap_get_values(ads->ldap.ld, msg, "objectGUID");
2596         if (!values)
2597                 return False;
2598
2599         if (values[0]) {
2600                 memcpy(&flat_guid.info, values[0], sizeof(UUID_FLAT));
2601                 smb_uuid_unpack(flat_guid, guid);
2602                 ldap_value_free(values);
2603                 return True;
2604         }
2605         ldap_value_free(values);
2606         return False;
2607
2608 }
2609
2610
2611 /**
2612  * pull a single DOM_SID from a ADS result
2613  * @param ads connection to ads server
2614  * @param msg Results of search
2615  * @param field Attribute to retrieve
2616  * @param sid Pointer to sid to store result
2617  * @return boolean inidicating success
2618 */
2619  bool ads_pull_sid(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
2620                    DOM_SID *sid)
2621 {
2622         return smbldap_pull_sid(ads->ldap.ld, msg, field, sid);
2623 }
2624
2625 /**
2626  * pull an array of DOM_SIDs from a ADS result
2627  * @param ads connection to ads server
2628  * @param mem_ctx TALLOC_CTX for allocating sid array
2629  * @param msg Results of search
2630  * @param field Attribute to retrieve
2631  * @param sids pointer to sid array to allocate
2632  * @return the count of SIDs pulled
2633  **/
2634  int ads_pull_sids(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2635                    LDAPMessage *msg, const char *field, DOM_SID **sids)
2636 {
2637         struct berval **values;
2638         bool ret;
2639         int count, i;
2640
2641         values = ldap_get_values_len(ads->ldap.ld, msg, field);
2642
2643         if (!values)
2644                 return 0;
2645
2646         for (i=0; values[i]; i++)
2647                 /* nop */ ;
2648
2649         if (i) {
2650                 (*sids) = TALLOC_ARRAY(mem_ctx, DOM_SID, i);
2651                 if (!(*sids)) {
2652                         ldap_value_free_len(values);
2653                         return 0;
2654                 }
2655         } else {
2656                 (*sids) = NULL;
2657         }
2658
2659         count = 0;
2660         for (i=0; values[i]; i++) {
2661                 ret = sid_parse(values[i]->bv_val, values[i]->bv_len, &(*sids)[count]);
2662                 if (ret) {
2663                         DEBUG(10, ("pulling SID: %s\n",
2664                                    sid_string_dbg(&(*sids)[count])));
2665                         count++;
2666                 }
2667         }
2668
2669         ldap_value_free_len(values);
2670         return count;
2671 }
2672
2673 /**
2674  * pull a SEC_DESC from a ADS result
2675  * @param ads connection to ads server
2676  * @param mem_ctx TALLOC_CTX for allocating sid array
2677  * @param msg Results of search
2678  * @param field Attribute to retrieve
2679  * @param sd Pointer to *SEC_DESC to store result (talloc()ed)
2680  * @return boolean inidicating success
2681 */
2682  bool ads_pull_sd(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2683                   LDAPMessage *msg, const char *field, SEC_DESC **sd)
2684 {
2685         struct berval **values;
2686         bool ret = true;
2687
2688         values = ldap_get_values_len(ads->ldap.ld, msg, field);
2689
2690         if (!values) return false;
2691
2692         if (values[0]) {
2693                 NTSTATUS status;
2694                 status = unmarshall_sec_desc(mem_ctx,
2695                                              (uint8 *)values[0]->bv_val,
2696                                              values[0]->bv_len, sd);
2697                 if (!NT_STATUS_IS_OK(status)) {
2698                         DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
2699                                   nt_errstr(status)));
2700                         ret = false;
2701                 }
2702         }
2703
2704         ldap_value_free_len(values);
2705         return ret;
2706 }
2707
2708 /* 
2709  * in order to support usernames longer than 21 characters we need to 
2710  * use both the sAMAccountName and the userPrincipalName attributes 
2711  * It seems that not all users have the userPrincipalName attribute set
2712  *
2713  * @param ads connection to ads server
2714  * @param mem_ctx TALLOC_CTX for allocating sid array
2715  * @param msg Results of search
2716  * @return the username
2717  */
2718  char *ads_pull_username(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2719                          LDAPMessage *msg)
2720 {
2721 #if 0   /* JERRY */
2722         char *ret, *p;
2723
2724         /* lookup_name() only works on the sAMAccountName to 
2725            returning the username portion of userPrincipalName
2726            breaks winbindd_getpwnam() */
2727
2728         ret = ads_pull_string(ads, mem_ctx, msg, "userPrincipalName");
2729         if (ret && (p = strchr_m(ret, '@'))) {
2730                 *p = 0;
2731                 return ret;
2732         }
2733 #endif
2734         return ads_pull_string(ads, mem_ctx, msg, "sAMAccountName");
2735 }
2736
2737
2738 /**
2739  * find the update serial number - this is the core of the ldap cache
2740  * @param ads connection to ads server
2741  * @param ads connection to ADS server
2742  * @param usn Pointer to retrieved update serial number
2743  * @return status of search
2744  **/
2745 ADS_STATUS ads_USN(ADS_STRUCT *ads, uint32 *usn)
2746 {
2747         const char *attrs[] = {"highestCommittedUSN", NULL};
2748         ADS_STATUS status;
2749         LDAPMessage *res;
2750
2751         status = ads_do_search_retry(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2752         if (!ADS_ERR_OK(status)) 
2753                 return status;
2754
2755         if (ads_count_replies(ads, res) != 1) {
2756                 ads_msgfree(ads, res);
2757                 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2758         }
2759
2760         if (!ads_pull_uint32(ads, res, "highestCommittedUSN", usn)) {
2761                 ads_msgfree(ads, res);
2762                 return ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
2763         }
2764
2765         ads_msgfree(ads, res);
2766         return ADS_SUCCESS;
2767 }
2768
2769 /* parse a ADS timestring - typical string is
2770    '20020917091222.0Z0' which means 09:12.22 17th September
2771    2002, timezone 0 */
2772 static time_t ads_parse_time(const char *str)
2773 {
2774         struct tm tm;
2775
2776         ZERO_STRUCT(tm);
2777
2778         if (sscanf(str, "%4d%2d%2d%2d%2d%2d", 
2779                    &tm.tm_year, &tm.tm_mon, &tm.tm_mday, 
2780                    &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) {
2781                 return 0;
2782         }
2783         tm.tm_year -= 1900;
2784         tm.tm_mon -= 1;
2785
2786         return timegm(&tm);
2787 }
2788
2789 /********************************************************************
2790 ********************************************************************/
2791
2792 ADS_STATUS ads_current_time(ADS_STRUCT *ads)
2793 {
2794         const char *attrs[] = {"currentTime", NULL};
2795         ADS_STATUS status;
2796         LDAPMessage *res;
2797         char *timestr;
2798         TALLOC_CTX *ctx;
2799         ADS_STRUCT *ads_s = ads;
2800
2801         if (!(ctx = talloc_init("ads_current_time"))) {
2802                 return ADS_ERROR(LDAP_NO_MEMORY);
2803         }
2804
2805         /* establish a new ldap tcp session if necessary */
2806
2807         if ( !ads->ldap.ld ) {
2808                 if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup, 
2809                         ads->server.ldap_server )) == NULL )
2810                 {
2811                         goto done;
2812                 }
2813                 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
2814                 status = ads_connect( ads_s );
2815                 if ( !ADS_ERR_OK(status))
2816                         goto done;
2817         }
2818
2819         status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2820         if (!ADS_ERR_OK(status)) {
2821                 goto done;
2822         }
2823
2824         timestr = ads_pull_string(ads_s, ctx, res, "currentTime");
2825         if (!timestr) {
2826                 ads_msgfree(ads_s, res);
2827                 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2828                 goto done;
2829         }
2830
2831         /* but save the time and offset in the original ADS_STRUCT */   
2832
2833         ads->config.current_time = ads_parse_time(timestr);
2834
2835         if (ads->config.current_time != 0) {
2836                 ads->auth.time_offset = ads->config.current_time - time(NULL);
2837                 DEBUG(4,("time offset is %d seconds\n", ads->auth.time_offset));
2838         }
2839
2840         ads_msgfree(ads, res);
2841
2842         status = ADS_SUCCESS;
2843
2844 done:
2845         /* free any temporary ads connections */
2846         if ( ads_s != ads ) {
2847                 ads_destroy( &ads_s );
2848         }
2849         talloc_destroy(ctx);
2850
2851         return status;
2852 }
2853
2854 /********************************************************************
2855 ********************************************************************/
2856
2857 ADS_STATUS ads_domain_func_level(ADS_STRUCT *ads, uint32 *val)
2858 {
2859         const char *attrs[] = {"domainFunctionality", NULL};
2860         ADS_STATUS status;
2861         LDAPMessage *res;
2862         ADS_STRUCT *ads_s = ads;
2863
2864         *val = DS_DOMAIN_FUNCTION_2000;
2865
2866         /* establish a new ldap tcp session if necessary */
2867
2868         if ( !ads->ldap.ld ) {
2869                 if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup, 
2870                         ads->server.ldap_server )) == NULL )
2871                 {
2872                         status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
2873                         goto done;
2874                 }
2875                 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
2876                 status = ads_connect( ads_s );
2877                 if ( !ADS_ERR_OK(status))
2878                         goto done;
2879         }
2880
2881         /* If the attribute does not exist assume it is a Windows 2000 
2882            functional domain */
2883
2884         status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2885         if (!ADS_ERR_OK(status)) {
2886                 if ( status.err.rc == LDAP_NO_SUCH_ATTRIBUTE ) {
2887                         status = ADS_SUCCESS;
2888                 }
2889                 goto done;
2890         }
2891
2892         if ( !ads_pull_uint32(ads_s, res, "domainFunctionality", val) ) {
2893                 DEBUG(5,("ads_domain_func_level: Failed to pull the domainFunctionality attribute.\n"));
2894         }
2895         DEBUG(3,("ads_domain_func_level: %d\n", *val));
2896
2897
2898         ads_msgfree(ads, res);
2899
2900 done:
2901         /* free any temporary ads connections */
2902         if ( ads_s != ads ) {
2903                 ads_destroy( &ads_s );
2904         }
2905
2906         return status;
2907 }
2908
2909 /**
2910  * find the domain sid for our domain
2911  * @param ads connection to ads server
2912  * @param sid Pointer to domain sid
2913  * @return status of search
2914  **/
2915 ADS_STATUS ads_domain_sid(ADS_STRUCT *ads, DOM_SID *sid)
2916 {
2917         const char *attrs[] = {"objectSid", NULL};
2918         LDAPMessage *res;
2919         ADS_STATUS rc;
2920
2921         rc = ads_do_search_retry(ads, ads->config.bind_path, LDAP_SCOPE_BASE, "(objectclass=*)", 
2922                            attrs, &res);
2923         if (!ADS_ERR_OK(rc)) return rc;
2924         if (!ads_pull_sid(ads, res, "objectSid", sid)) {
2925                 ads_msgfree(ads, res);
2926                 return ADS_ERROR_SYSTEM(ENOENT);
2927         }
2928         ads_msgfree(ads, res);
2929
2930         return ADS_SUCCESS;
2931 }
2932
2933 /**
2934  * find our site name 
2935  * @param ads connection to ads server
2936  * @param mem_ctx Pointer to talloc context
2937  * @param site_name Pointer to the sitename
2938  * @return status of search
2939  **/
2940 ADS_STATUS ads_site_dn(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char **site_name)
2941 {
2942         ADS_STATUS status;
2943         LDAPMessage *res;
2944         const char *dn, *service_name;
2945         const char *attrs[] = { "dsServiceName", NULL };
2946
2947         status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2948         if (!ADS_ERR_OK(status)) {
2949                 return status;
2950         }
2951
2952         service_name = ads_pull_string(ads, mem_ctx, res, "dsServiceName");
2953         if (service_name == NULL) {
2954                 ads_msgfree(ads, res);
2955                 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2956         }
2957
2958         ads_msgfree(ads, res);
2959
2960         /* go up three levels */
2961         dn = ads_parent_dn(ads_parent_dn(ads_parent_dn(service_name)));
2962         if (dn == NULL) {
2963                 return ADS_ERROR(LDAP_NO_MEMORY);
2964         }
2965
2966         *site_name = talloc_strdup(mem_ctx, dn);
2967         if (*site_name == NULL) {
2968                 return ADS_ERROR(LDAP_NO_MEMORY);
2969         }
2970
2971         return status;
2972         /*
2973         dsServiceName: CN=NTDS Settings,CN=W2K3DC,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,DC=ber,DC=suse,DC=de
2974         */                                               
2975 }
2976
2977 /**
2978  * find the site dn where a machine resides
2979  * @param ads connection to ads server
2980  * @param mem_ctx Pointer to talloc context
2981  * @param computer_name name of the machine
2982  * @param site_name Pointer to the sitename
2983  * @return status of search
2984  **/
2985 ADS_STATUS ads_site_dn_for_machine(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char *computer_name, const char **site_dn)
2986 {
2987         ADS_STATUS status;
2988         LDAPMessage *res;
2989         const char *parent, *filter;
2990         char *config_context = NULL;
2991         char *dn;
2992
2993         /* shortcut a query */
2994         if (strequal(computer_name, ads->config.ldap_server_name)) {
2995                 return ads_site_dn(ads, mem_ctx, site_dn);
2996         }
2997
2998         status = ads_config_path(ads, mem_ctx, &config_context);
2999         if (!ADS_ERR_OK(status)) {
3000                 return status;
3001         }
3002
3003         filter = talloc_asprintf(mem_ctx, "(cn=%s)", computer_name);
3004         if (filter == NULL) {
3005                 return ADS_ERROR(LDAP_NO_MEMORY);
3006         }
3007
3008         status = ads_do_search(ads, config_context, LDAP_SCOPE_SUBTREE, 
3009                                filter, NULL, &res);
3010         if (!ADS_ERR_OK(status)) {
3011                 return status;
3012         }
3013
3014         if (ads_count_replies(ads, res) != 1) {
3015                 ads_msgfree(ads, res);
3016                 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
3017         }
3018
3019         dn = ads_get_dn(ads, mem_ctx, res);
3020         if (dn == NULL) {
3021                 ads_msgfree(ads, res);
3022                 return ADS_ERROR(LDAP_NO_MEMORY);
3023         }
3024
3025         /* go up three levels */
3026         parent = ads_parent_dn(ads_parent_dn(ads_parent_dn(dn)));
3027         if (parent == NULL) {
3028                 ads_msgfree(ads, res);
3029                 TALLOC_FREE(dn);
3030                 return ADS_ERROR(LDAP_NO_MEMORY);
3031         }
3032
3033         *site_dn = talloc_strdup(mem_ctx, parent);
3034         if (*site_dn == NULL) {
3035                 ads_msgfree(ads, res);
3036                 TALLOC_FREE(dn);
3037                 return ADS_ERROR(LDAP_NO_MEMORY);
3038         }
3039
3040         TALLOC_FREE(dn);
3041         ads_msgfree(ads, res);
3042
3043         return status;
3044 }
3045
3046 /**
3047  * get the upn suffixes for a domain
3048  * @param ads connection to ads server
3049  * @param mem_ctx Pointer to talloc context
3050  * @param suffixes Pointer to an array of suffixes
3051  * @param num_suffixes Pointer to the number of suffixes
3052  * @return status of search
3053  **/
3054 ADS_STATUS ads_upn_suffixes(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, char ***suffixes, size_t *num_suffixes)
3055 {
3056         ADS_STATUS status;
3057         LDAPMessage *res;
3058         const char *base;
3059         char *config_context = NULL;
3060         const char *attrs[] = { "uPNSuffixes", NULL };
3061
3062         status = ads_config_path(ads, mem_ctx, &config_context);
3063         if (!ADS_ERR_OK(status)) {
3064                 return status;
3065         }
3066
3067         base = talloc_asprintf(mem_ctx, "cn=Partitions,%s", config_context);
3068         if (base == NULL) {
3069                 return ADS_ERROR(LDAP_NO_MEMORY);
3070         }
3071
3072         status = ads_search_dn(ads, &res, base, attrs);
3073         if (!ADS_ERR_OK(status)) {
3074                 return status;
3075         }
3076
3077         if (ads_count_replies(ads, res) != 1) {
3078                 ads_msgfree(ads, res);
3079                 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
3080         }
3081
3082         (*suffixes) = ads_pull_strings(ads, mem_ctx, res, "uPNSuffixes", num_suffixes);
3083         if ((*suffixes) == NULL) {
3084                 ads_msgfree(ads, res);
3085                 return ADS_ERROR(LDAP_NO_MEMORY);
3086         }
3087
3088         ads_msgfree(ads, res);
3089
3090         return status;
3091 }
3092
3093 /**
3094  * get the joinable ous for a domain
3095  * @param ads connection to ads server
3096  * @param mem_ctx Pointer to talloc context
3097  * @param ous Pointer to an array of ous
3098  * @param num_ous Pointer to the number of ous
3099  * @return status of search
3100  **/
3101 ADS_STATUS ads_get_joinable_ous(ADS_STRUCT *ads,
3102                                 TALLOC_CTX *mem_ctx,
3103                                 char ***ous,
3104                                 size_t *num_ous)
3105 {
3106         ADS_STATUS status;
3107         LDAPMessage *res = NULL;
3108         LDAPMessage *msg = NULL;
3109         const char *attrs[] = { "dn", NULL };
3110         int count = 0;
3111
3112         status = ads_search(ads, &res,
3113                             "(|(objectClass=domain)(objectclass=organizationalUnit))",
3114                             attrs);
3115         if (!ADS_ERR_OK(status)) {
3116                 return status;
3117         }
3118
3119         count = ads_count_replies(ads, res);
3120         if (count < 1) {
3121                 ads_msgfree(ads, res);
3122                 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3123         }
3124
3125         for (msg = ads_first_entry(ads, res); msg;
3126              msg = ads_next_entry(ads, msg)) {
3127
3128                 char *dn = NULL;
3129
3130                 dn = ads_get_dn(ads, talloc_tos(), msg);
3131                 if (!dn) {
3132                         ads_msgfree(ads, res);
3133                         return ADS_ERROR(LDAP_NO_MEMORY);
3134                 }
3135
3136                 if (!add_string_to_array(mem_ctx, dn,
3137                                          (const char ***)ous,
3138                                          (int *)num_ous)) {
3139                         TALLOC_FREE(dn);
3140                         ads_msgfree(ads, res);
3141                         return ADS_ERROR(LDAP_NO_MEMORY);
3142                 }
3143
3144                 TALLOC_FREE(dn);
3145         }
3146
3147         ads_msgfree(ads, res);
3148
3149         return status;
3150 }
3151
3152
3153 /**
3154  * pull a DOM_SID from an extended dn string
3155  * @param mem_ctx TALLOC_CTX
3156  * @param extended_dn string
3157  * @param flags string type of extended_dn
3158  * @param sid pointer to a DOM_SID
3159  * @return NT_STATUS_OK on success,
3160  *         NT_INVALID_PARAMETER on error,
3161  *         NT_STATUS_NOT_FOUND if no SID present
3162  **/
3163 ADS_STATUS ads_get_sid_from_extended_dn(TALLOC_CTX *mem_ctx,
3164                                         const char *extended_dn,
3165                                         enum ads_extended_dn_flags flags,
3166                                         DOM_SID *sid)
3167 {
3168         char *p, *q, *dn;
3169
3170         if (!extended_dn) {
3171                 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3172         }
3173
3174         /* otherwise extended_dn gets stripped off */
3175         if ((dn = talloc_strdup(mem_ctx, extended_dn)) == NULL) {
3176                 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3177         }
3178         /*
3179          * ADS_EXTENDED_DN_HEX_STRING:
3180          * <GUID=238e1963cb390f4bb032ba0105525a29>;<SID=010500000000000515000000bb68c8fd6b61b427572eb04556040000>;CN=gd,OU=berlin,OU=suse,DC=ber,DC=suse,DC=de
3181          *
3182          * ADS_EXTENDED_DN_STRING (only with w2k3):
3183          * <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
3184          *
3185          * Object with no SID, such as an Exchange Public Folder
3186          * <GUID=28907fb4bdf6854993e7f0a10b504e7c>;CN=public,CN=Microsoft Exchange System Objects,DC=sd2k3ms,DC=west,DC=isilon,DC=com
3187          */
3188
3189         p = strchr(dn, ';');
3190         if (!p) {
3191                 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3192         }
3193
3194         if (strncmp(p, ";<SID=", strlen(";<SID=")) != 0) {
3195                 DEBUG(5,("No SID present in extended dn\n"));
3196                 return ADS_ERROR_NT(NT_STATUS_NOT_FOUND);
3197         }
3198
3199         p += strlen(";<SID=");
3200
3201         q = strchr(p, '>');
3202         if (!q) {
3203                 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3204         }
3205
3206         *q = '\0';
3207
3208         DEBUG(100,("ads_get_sid_from_extended_dn: sid string is %s\n", p));
3209
3210         switch (flags) {
3211
3212         case ADS_EXTENDED_DN_STRING:
3213                 if (!string_to_sid(sid, p)) {
3214                         return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3215                 }
3216                 break;
3217         case ADS_EXTENDED_DN_HEX_STRING: {
3218                 fstring buf;
3219                 size_t buf_len;
3220
3221                 buf_len = strhex_to_str(buf, sizeof(buf), p, strlen(p));
3222                 if (buf_len == 0) {
3223                         return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3224                 }
3225
3226                 if (!sid_parse(buf, buf_len, sid)) {
3227                         DEBUG(10,("failed to parse sid\n"));
3228                         return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3229                 }
3230                 break;
3231                 }
3232         default:
3233                 DEBUG(10,("unknown extended dn format\n"));
3234                 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3235         }
3236
3237         return ADS_ERROR_NT(NT_STATUS_OK);
3238 }
3239
3240 /**
3241  * pull an array of DOM_SIDs from a ADS result
3242  * @param ads connection to ads server
3243  * @param mem_ctx TALLOC_CTX for allocating sid array
3244  * @param msg Results of search
3245  * @param field Attribute to retrieve
3246  * @param flags string type of extended_dn
3247  * @param sids pointer to sid array to allocate
3248  * @return the count of SIDs pulled
3249  **/
3250  int ads_pull_sids_from_extendeddn(ADS_STRUCT *ads,
3251                                    TALLOC_CTX *mem_ctx,
3252                                    LDAPMessage *msg,
3253                                    const char *field,
3254                                    enum ads_extended_dn_flags flags,
3255                                    DOM_SID **sids)
3256 {
3257         int i;
3258         ADS_STATUS rc;
3259         size_t dn_count, ret_count = 0;
3260         char **dn_strings;
3261
3262         if ((dn_strings = ads_pull_strings(ads, mem_ctx, msg, field,
3263                                            &dn_count)) == NULL) {
3264                 return 0;
3265         }
3266
3267         (*sids) = TALLOC_ZERO_ARRAY(mem_ctx, DOM_SID, dn_count + 1);
3268         if (!(*sids)) {
3269                 TALLOC_FREE(dn_strings);
3270                 return 0;
3271         }
3272
3273         for (i=0; i<dn_count; i++) {
3274                 rc = ads_get_sid_from_extended_dn(mem_ctx, dn_strings[i],
3275                                                   flags, &(*sids)[i]);
3276                 if (!ADS_ERR_OK(rc)) {
3277                         if (NT_STATUS_EQUAL(ads_ntstatus(rc),
3278                             NT_STATUS_NOT_FOUND)) {
3279                                 continue;
3280                         }
3281                         else {
3282                                 TALLOC_FREE(*sids);
3283                                 TALLOC_FREE(dn_strings);
3284                                 return 0;
3285                         }
3286                 }
3287                 ret_count++;
3288         }
3289
3290         TALLOC_FREE(dn_strings);
3291
3292         return ret_count;
3293 }
3294
3295 /********************************************************************
3296 ********************************************************************/
3297
3298 char* ads_get_dnshostname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3299 {
3300         LDAPMessage *res = NULL;
3301         ADS_STATUS status;
3302         int count = 0;
3303         char *name = NULL;
3304
3305         status = ads_find_machine_acct(ads, &res, global_myname());
3306         if (!ADS_ERR_OK(status)) {
3307                 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
3308                         global_myname()));
3309                 goto out;
3310         }
3311
3312         if ( (count = ads_count_replies(ads, res)) != 1 ) {
3313                 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
3314                 goto out;
3315         }
3316
3317         if ( (name = ads_pull_string(ads, ctx, res, "dNSHostName")) == NULL ) {
3318                 DEBUG(0,("ads_get_dnshostname: No dNSHostName attribute!\n"));
3319         }
3320
3321 out:
3322         ads_msgfree(ads, res);
3323
3324         return name;
3325 }
3326
3327 /********************************************************************
3328 ********************************************************************/
3329
3330 char* ads_get_upn( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3331 {
3332         LDAPMessage *res = NULL;
3333         ADS_STATUS status;
3334         int count = 0;
3335         char *name = NULL;
3336
3337         status = ads_find_machine_acct(ads, &res, machine_name);
3338         if (!ADS_ERR_OK(status)) {
3339                 DEBUG(0,("ads_get_upn: Failed to find account for %s\n",
3340                         global_myname()));
3341                 goto out;
3342         }
3343
3344         if ( (count = ads_count_replies(ads, res)) != 1 ) {
3345                 DEBUG(1,("ads_get_upn: %d entries returned!\n", count));
3346                 goto out;
3347         }
3348
3349         if ( (name = ads_pull_string(ads, ctx, res, "userPrincipalName")) == NULL ) {
3350                 DEBUG(2,("ads_get_upn: No userPrincipalName attribute!\n"));
3351         }
3352
3353 out:
3354         ads_msgfree(ads, res);
3355
3356         return name;
3357 }
3358
3359 /********************************************************************
3360 ********************************************************************/
3361
3362 char* ads_get_samaccountname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3363 {
3364         LDAPMessage *res = NULL;
3365         ADS_STATUS status;
3366         int count = 0;
3367         char *name = NULL;
3368
3369         status = ads_find_machine_acct(ads, &res, global_myname());
3370         if (!ADS_ERR_OK(status)) {
3371                 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
3372                         global_myname()));
3373                 goto out;
3374         }
3375
3376         if ( (count = ads_count_replies(ads, res)) != 1 ) {
3377                 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
3378                 goto out;
3379         }
3380
3381         if ( (name = ads_pull_string(ads, ctx, res, "sAMAccountName")) == NULL ) {
3382                 DEBUG(0,("ads_get_dnshostname: No sAMAccountName attribute!\n"));
3383         }
3384
3385 out:
3386         ads_msgfree(ads, res);
3387
3388         return name;
3389 }
3390
3391 #if 0
3392
3393    SAVED CODE - we used to join via ldap - remember how we did this. JRA.
3394
3395 /**
3396  * Join a machine to a realm
3397  *  Creates the machine account and sets the machine password
3398  * @param ads connection to ads server
3399  * @param machine name of host to add
3400  * @param org_unit Organizational unit to place machine in
3401  * @return status of join
3402  **/
3403 ADS_STATUS ads_join_realm(ADS_STRUCT *ads, const char *machine_name,
3404                         uint32 account_type, const char *org_unit)
3405 {
3406         ADS_STATUS status;
3407         LDAPMessage *res = NULL;
3408         char *machine;
3409
3410         /* machine name must be lowercase */<