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