s3: avoid global include of ads.h.
[anatoliy/anatoliy.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(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                 sid_parse(values[i]->bv_val, values[i]->bv_len, &sid);
2149                 printf("%s: %s\n", field, sid_to_fstring(tmp, &sid));
2150         }
2151 }
2152
2153 /*
2154   dump ntSecurityDescriptor
2155 */
2156 static void dump_sd(ADS_STRUCT *ads, const char *filed, struct berval **values)
2157 {
2158         TALLOC_CTX *frame = talloc_stackframe();
2159         struct security_descriptor *psd;
2160         NTSTATUS status;
2161
2162         status = unmarshall_sec_desc(talloc_tos(), (uint8 *)values[0]->bv_val,
2163                                      values[0]->bv_len, &psd);
2164         if (!NT_STATUS_IS_OK(status)) {
2165                 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
2166                           nt_errstr(status)));
2167                 TALLOC_FREE(frame);
2168                 return;
2169         }
2170
2171         if (psd) {
2172                 ads_disp_sd(ads, talloc_tos(), psd);
2173         }
2174
2175         TALLOC_FREE(frame);
2176 }
2177
2178 /*
2179   dump a string result from ldap
2180 */
2181 static void dump_string(const char *field, char **values)
2182 {
2183         int i;
2184         for (i=0; values[i]; i++) {
2185                 printf("%s: %s\n", field, values[i]);
2186         }
2187 }
2188
2189 /*
2190   dump a field from LDAP on stdout
2191   used for debugging
2192 */
2193
2194 static bool ads_dump_field(ADS_STRUCT *ads, char *field, void **values, void *data_area)
2195 {
2196         const struct {
2197                 const char *name;
2198                 bool string;
2199                 void (*handler)(ADS_STRUCT *, const char *, struct berval **);
2200         } handlers[] = {
2201                 {"objectGUID", False, dump_guid},
2202                 {"netbootGUID", False, dump_guid},
2203                 {"nTSecurityDescriptor", False, dump_sd},
2204                 {"dnsRecord", False, dump_binary},
2205                 {"objectSid", False, dump_sid},
2206                 {"tokenGroups", False, dump_sid},
2207                 {"tokenGroupsNoGCAcceptable", False, dump_sid},
2208                 {"tokengroupsGlobalandUniversal", False, dump_sid},
2209                 {"mS-DS-CreatorSID", False, dump_sid},
2210                 {"msExchMailboxGuid", False, dump_guid},
2211                 {NULL, True, NULL}
2212         };
2213         int i;
2214
2215         if (!field) { /* must be end of an entry */
2216                 printf("\n");
2217                 return False;
2218         }
2219
2220         for (i=0; handlers[i].name; i++) {
2221                 if (StrCaseCmp(handlers[i].name, field) == 0) {
2222                         if (!values) /* first time, indicate string or not */
2223                                 return handlers[i].string;
2224                         handlers[i].handler(ads, field, (struct berval **) values);
2225                         break;
2226                 }
2227         }
2228         if (!handlers[i].name) {
2229                 if (!values) /* first time, indicate string conversion */
2230                         return True;
2231                 dump_string(field, (char **)values);
2232         }
2233         return False;
2234 }
2235
2236 /**
2237  * Dump a result from LDAP on stdout
2238  *  used for debugging
2239  * @param ads connection to ads server
2240  * @param res Results to dump
2241  **/
2242
2243  void ads_dump(ADS_STRUCT *ads, LDAPMessage *res)
2244 {
2245         ads_process_results(ads, res, ads_dump_field, NULL);
2246 }
2247
2248 /**
2249  * Walk through results, calling a function for each entry found.
2250  *  The function receives a field name, a berval * array of values,
2251  *  and a data area passed through from the start.  The function is
2252  *  called once with null for field and values at the end of each
2253  *  entry.
2254  * @param ads connection to ads server
2255  * @param res Results to process
2256  * @param fn Function for processing each result
2257  * @param data_area user-defined area to pass to function
2258  **/
2259  void ads_process_results(ADS_STRUCT *ads, LDAPMessage *res,
2260                           bool (*fn)(ADS_STRUCT *, char *, void **, void *),
2261                           void *data_area)
2262 {
2263         LDAPMessage *msg;
2264         TALLOC_CTX *ctx;
2265         size_t converted_size;
2266
2267         if (!(ctx = talloc_init("ads_process_results")))
2268                 return;
2269
2270         for (msg = ads_first_entry(ads, res); msg; 
2271              msg = ads_next_entry(ads, msg)) {
2272                 char *utf8_field;
2273                 BerElement *b;
2274
2275                 for (utf8_field=ldap_first_attribute(ads->ldap.ld,
2276                                                      (LDAPMessage *)msg,&b); 
2277                      utf8_field;
2278                      utf8_field=ldap_next_attribute(ads->ldap.ld,
2279                                                     (LDAPMessage *)msg,b)) {
2280                         struct berval **ber_vals;
2281                         char **str_vals, **utf8_vals;
2282                         char *field;
2283                         bool string; 
2284
2285                         if (!pull_utf8_talloc(ctx, &field, utf8_field,
2286                                               &converted_size))
2287                         {
2288                                 DEBUG(0,("ads_process_results: "
2289                                          "pull_utf8_talloc failed: %s",
2290                                          strerror(errno)));
2291                         }
2292
2293                         string = fn(ads, field, NULL, data_area);
2294
2295                         if (string) {
2296                                 utf8_vals = ldap_get_values(ads->ldap.ld,
2297                                                  (LDAPMessage *)msg, field);
2298                                 str_vals = ads_pull_strvals(ctx, 
2299                                                   (const char **) utf8_vals);
2300                                 fn(ads, field, (void **) str_vals, data_area);
2301                                 ldap_value_free(utf8_vals);
2302                         } else {
2303                                 ber_vals = ldap_get_values_len(ads->ldap.ld, 
2304                                                  (LDAPMessage *)msg, field);
2305                                 fn(ads, field, (void **) ber_vals, data_area);
2306
2307                                 ldap_value_free_len(ber_vals);
2308                         }
2309                         ldap_memfree(utf8_field);
2310                 }
2311                 ber_free(b, 0);
2312                 talloc_free_children(ctx);
2313                 fn(ads, NULL, NULL, data_area); /* completed an entry */
2314
2315         }
2316         talloc_destroy(ctx);
2317 }
2318
2319 /**
2320  * count how many replies are in a LDAPMessage
2321  * @param ads connection to ads server
2322  * @param res Results to count
2323  * @return number of replies
2324  **/
2325 int ads_count_replies(ADS_STRUCT *ads, void *res)
2326 {
2327         return ldap_count_entries(ads->ldap.ld, (LDAPMessage *)res);
2328 }
2329
2330 /**
2331  * pull the first entry from a ADS result
2332  * @param ads connection to ads server
2333  * @param res Results of search
2334  * @return first entry from result
2335  **/
2336  LDAPMessage *ads_first_entry(ADS_STRUCT *ads, LDAPMessage *res)
2337 {
2338         return ldap_first_entry(ads->ldap.ld, res);
2339 }
2340
2341 /**
2342  * pull the next entry from a ADS result
2343  * @param ads connection to ads server
2344  * @param res Results of search
2345  * @return next entry from result
2346  **/
2347  LDAPMessage *ads_next_entry(ADS_STRUCT *ads, LDAPMessage *res)
2348 {
2349         return ldap_next_entry(ads->ldap.ld, res);
2350 }
2351
2352 /**
2353  * pull the first message from a ADS result
2354  * @param ads connection to ads server
2355  * @param res Results of search
2356  * @return first message from result
2357  **/
2358  LDAPMessage *ads_first_message(ADS_STRUCT *ads, LDAPMessage *res)
2359 {
2360         return ldap_first_message(ads->ldap.ld, res);
2361 }
2362
2363 /**
2364  * pull the next message from a ADS result
2365  * @param ads connection to ads server
2366  * @param res Results of search
2367  * @return next message from result
2368  **/
2369  LDAPMessage *ads_next_message(ADS_STRUCT *ads, LDAPMessage *res)
2370 {
2371         return ldap_next_message(ads->ldap.ld, res);
2372 }
2373
2374 /**
2375  * pull a single string from a ADS result
2376  * @param ads connection to ads server
2377  * @param mem_ctx TALLOC_CTX to use for allocating result string
2378  * @param msg Results of search
2379  * @param field Attribute to retrieve
2380  * @return Result string in talloc context
2381  **/
2382  char *ads_pull_string(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, LDAPMessage *msg,
2383                        const char *field)
2384 {
2385         char **values;
2386         char *ret = NULL;
2387         char *ux_string;
2388         size_t converted_size;
2389
2390         values = ldap_get_values(ads->ldap.ld, msg, field);
2391         if (!values)
2392                 return NULL;
2393
2394         if (values[0] && pull_utf8_talloc(mem_ctx, &ux_string, values[0],
2395                                           &converted_size))
2396         {
2397                 ret = ux_string;
2398         }
2399         ldap_value_free(values);
2400         return ret;
2401 }
2402
2403 /**
2404  * pull an array of strings from a ADS result
2405  * @param ads connection to ads server
2406  * @param mem_ctx TALLOC_CTX to use for allocating result string
2407  * @param msg Results of search
2408  * @param field Attribute to retrieve
2409  * @return Result strings in talloc context
2410  **/
2411  char **ads_pull_strings(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2412                          LDAPMessage *msg, const char *field,
2413                          size_t *num_values)
2414 {
2415         char **values;
2416         char **ret = NULL;
2417         int i;
2418         size_t converted_size;
2419
2420         values = ldap_get_values(ads->ldap.ld, msg, field);
2421         if (!values)
2422                 return NULL;
2423
2424         *num_values = ldap_count_values(values);
2425
2426         ret = TALLOC_ARRAY(mem_ctx, char *, *num_values + 1);
2427         if (!ret) {
2428                 ldap_value_free(values);
2429                 return NULL;
2430         }
2431
2432         for (i=0;i<*num_values;i++) {
2433                 if (!pull_utf8_talloc(mem_ctx, &ret[i], values[i],
2434                                       &converted_size))
2435                 {
2436                         ldap_value_free(values);
2437                         return NULL;
2438                 }
2439         }
2440         ret[i] = NULL;
2441
2442         ldap_value_free(values);
2443         return ret;
2444 }
2445
2446 /**
2447  * pull an array of strings from a ADS result 
2448  *  (handle large multivalue attributes with range retrieval)
2449  * @param ads connection to ads server
2450  * @param mem_ctx TALLOC_CTX to use for allocating result string
2451  * @param msg Results of search
2452  * @param field Attribute to retrieve
2453  * @param current_strings strings returned by a previous call to this function
2454  * @param next_attribute The next query should ask for this attribute
2455  * @param num_values How many values did we get this time?
2456  * @param more_values Are there more values to get?
2457  * @return Result strings in talloc context
2458  **/
2459  char **ads_pull_strings_range(ADS_STRUCT *ads, 
2460                                TALLOC_CTX *mem_ctx,
2461                                LDAPMessage *msg, const char *field,
2462                                char **current_strings,
2463                                const char **next_attribute,
2464                                size_t *num_strings,
2465                                bool *more_strings)
2466 {
2467         char *attr;
2468         char *expected_range_attrib, *range_attr;
2469         BerElement *ptr = NULL;
2470         char **strings;
2471         char **new_strings;
2472         size_t num_new_strings;
2473         unsigned long int range_start;
2474         unsigned long int range_end;
2475
2476         /* we might have been given the whole lot anyway */
2477         if ((strings = ads_pull_strings(ads, mem_ctx, msg, field, num_strings))) {
2478                 *more_strings = False;
2479                 return strings;
2480         }
2481
2482         expected_range_attrib = talloc_asprintf(mem_ctx, "%s;Range=", field);
2483
2484         /* look for Range result */
2485         for (attr = ldap_first_attribute(ads->ldap.ld, (LDAPMessage *)msg, &ptr); 
2486              attr; 
2487              attr = ldap_next_attribute(ads->ldap.ld, (LDAPMessage *)msg, ptr)) {
2488                 /* we ignore the fact that this is utf8, as all attributes are ascii... */
2489                 if (strnequal(attr, expected_range_attrib, strlen(expected_range_attrib))) {
2490                         range_attr = attr;
2491                         break;
2492                 }
2493                 ldap_memfree(attr);
2494         }
2495         if (!attr) {
2496                 ber_free(ptr, 0);
2497                 /* nothing here - this field is just empty */
2498                 *more_strings = False;
2499                 return NULL;
2500         }
2501
2502         if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-%lu", 
2503                    &range_start, &range_end) == 2) {
2504                 *more_strings = True;
2505         } else {
2506                 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-*", 
2507                            &range_start) == 1) {
2508                         *more_strings = False;
2509                 } else {
2510                         DEBUG(1, ("ads_pull_strings_range:  Cannot parse Range attriubte (%s)\n", 
2511                                   range_attr));
2512                         ldap_memfree(range_attr);
2513                         *more_strings = False;
2514                         return NULL;
2515                 }
2516         }
2517
2518         if ((*num_strings) != range_start) {
2519                 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) doesn't start at %u, but at %lu"
2520                           " - aborting range retreival\n",
2521                           range_attr, (unsigned int)(*num_strings) + 1, range_start));
2522                 ldap_memfree(range_attr);
2523                 *more_strings = False;
2524                 return NULL;
2525         }
2526
2527         new_strings = ads_pull_strings(ads, mem_ctx, msg, range_attr, &num_new_strings);
2528
2529         if (*more_strings && ((*num_strings + num_new_strings) != (range_end + 1))) {
2530                 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) tells us we have %lu "
2531                           "strings in this bunch, but we only got %lu - aborting range retreival\n",
2532                           range_attr, (unsigned long int)range_end - range_start + 1, 
2533                           (unsigned long int)num_new_strings));
2534                 ldap_memfree(range_attr);
2535                 *more_strings = False;
2536                 return NULL;
2537         }
2538
2539         strings = TALLOC_REALLOC_ARRAY(mem_ctx, current_strings, char *,
2540                                  *num_strings + num_new_strings);
2541
2542         if (strings == NULL) {
2543                 ldap_memfree(range_attr);
2544                 *more_strings = False;
2545                 return NULL;
2546         }
2547
2548         if (new_strings && num_new_strings) {
2549                 memcpy(&strings[*num_strings], new_strings,
2550                        sizeof(*new_strings) * num_new_strings);
2551         }
2552
2553         (*num_strings) += num_new_strings;
2554
2555         if (*more_strings) {
2556                 *next_attribute = talloc_asprintf(mem_ctx,
2557                                                   "%s;range=%d-*", 
2558                                                   field,
2559                                                   (int)*num_strings);
2560
2561                 if (!*next_attribute) {
2562                         DEBUG(1, ("talloc_asprintf for next attribute failed!\n"));
2563                         ldap_memfree(range_attr);
2564                         *more_strings = False;
2565                         return NULL;
2566                 }
2567         }
2568
2569         ldap_memfree(range_attr);
2570
2571         return strings;
2572 }
2573
2574 /**
2575  * pull a single uint32 from a ADS result
2576  * @param ads connection to ads server
2577  * @param msg Results of search
2578  * @param field Attribute to retrieve
2579  * @param v Pointer to int to store result
2580  * @return boolean inidicating success
2581 */
2582  bool ads_pull_uint32(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
2583                       uint32 *v)
2584 {
2585         char **values;
2586
2587         values = ldap_get_values(ads->ldap.ld, msg, field);
2588         if (!values)
2589                 return False;
2590         if (!values[0]) {
2591                 ldap_value_free(values);
2592                 return False;
2593         }
2594
2595         *v = atoi(values[0]);
2596         ldap_value_free(values);
2597         return True;
2598 }
2599
2600 /**
2601  * pull a single objectGUID from an ADS result
2602  * @param ads connection to ADS server
2603  * @param msg results of search
2604  * @param guid 37-byte area to receive text guid
2605  * @return boolean indicating success
2606  **/
2607  bool ads_pull_guid(ADS_STRUCT *ads, LDAPMessage *msg, struct GUID *guid)
2608 {
2609         char **values;
2610         UUID_FLAT flat_guid;
2611
2612         values = ldap_get_values(ads->ldap.ld, msg, "objectGUID");
2613         if (!values)
2614                 return False;
2615
2616         if (values[0]) {
2617                 memcpy(&flat_guid.info, values[0], sizeof(UUID_FLAT));
2618                 smb_uuid_unpack(flat_guid, guid);
2619                 ldap_value_free(values);
2620                 return True;
2621         }
2622         ldap_value_free(values);
2623         return False;
2624
2625 }
2626
2627
2628 /**
2629  * pull a single struct dom_sid from a ADS result
2630  * @param ads connection to ads server
2631  * @param msg Results of search
2632  * @param field Attribute to retrieve
2633  * @param sid Pointer to sid to store result
2634  * @return boolean inidicating success
2635 */
2636  bool ads_pull_sid(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
2637                    struct dom_sid *sid)
2638 {
2639         return smbldap_pull_sid(ads->ldap.ld, msg, field, sid);
2640 }
2641
2642 /**
2643  * pull an array of struct dom_sids from a ADS result
2644  * @param ads connection to ads server
2645  * @param mem_ctx TALLOC_CTX for allocating sid array
2646  * @param msg Results of search
2647  * @param field Attribute to retrieve
2648  * @param sids pointer to sid array to allocate
2649  * @return the count of SIDs pulled
2650  **/
2651  int ads_pull_sids(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2652                    LDAPMessage *msg, const char *field, struct dom_sid **sids)
2653 {
2654         struct berval **values;
2655         bool ret;
2656         int count, i;
2657
2658         values = ldap_get_values_len(ads->ldap.ld, msg, field);
2659
2660         if (!values)
2661                 return 0;
2662
2663         for (i=0; values[i]; i++)
2664                 /* nop */ ;
2665
2666         if (i) {
2667                 (*sids) = TALLOC_ARRAY(mem_ctx, struct dom_sid, i);
2668                 if (!(*sids)) {
2669                         ldap_value_free_len(values);
2670                         return 0;
2671                 }
2672         } else {
2673                 (*sids) = NULL;
2674         }
2675
2676         count = 0;
2677         for (i=0; values[i]; i++) {
2678                 ret = sid_parse(values[i]->bv_val, values[i]->bv_len, &(*sids)[count]);
2679                 if (ret) {
2680                         DEBUG(10, ("pulling SID: %s\n",
2681                                    sid_string_dbg(&(*sids)[count])));
2682                         count++;
2683                 }
2684         }
2685
2686         ldap_value_free_len(values);
2687         return count;
2688 }
2689
2690 /**
2691  * pull a struct security_descriptor from a ADS result
2692  * @param ads connection to ads server
2693  * @param mem_ctx TALLOC_CTX for allocating sid array
2694  * @param msg Results of search
2695  * @param field Attribute to retrieve
2696  * @param sd Pointer to *struct security_descriptor to store result (talloc()ed)
2697  * @return boolean inidicating success
2698 */
2699  bool ads_pull_sd(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2700                   LDAPMessage *msg, const char *field,
2701                   struct security_descriptor **sd)
2702 {
2703         struct berval **values;
2704         bool ret = true;
2705
2706         values = ldap_get_values_len(ads->ldap.ld, msg, field);
2707
2708         if (!values) return false;
2709
2710         if (values[0]) {
2711                 NTSTATUS status;
2712                 status = unmarshall_sec_desc(mem_ctx,
2713                                              (uint8 *)values[0]->bv_val,
2714                                              values[0]->bv_len, sd);
2715                 if (!NT_STATUS_IS_OK(status)) {
2716                         DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
2717                                   nt_errstr(status)));
2718                         ret = false;
2719                 }
2720         }
2721
2722         ldap_value_free_len(values);
2723         return ret;
2724 }
2725
2726 /* 
2727  * in order to support usernames longer than 21 characters we need to 
2728  * use both the sAMAccountName and the userPrincipalName attributes 
2729  * It seems that not all users have the userPrincipalName attribute set
2730  *
2731  * @param ads connection to ads server
2732  * @param mem_ctx TALLOC_CTX for allocating sid array
2733  * @param msg Results of search
2734  * @return the username
2735  */
2736  char *ads_pull_username(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2737                          LDAPMessage *msg)
2738 {
2739 #if 0   /* JERRY */
2740         char *ret, *p;
2741
2742         /* lookup_name() only works on the sAMAccountName to 
2743            returning the username portion of userPrincipalName
2744            breaks winbindd_getpwnam() */
2745
2746         ret = ads_pull_string(ads, mem_ctx, msg, "userPrincipalName");
2747         if (ret && (p = strchr_m(ret, '@'))) {
2748                 *p = 0;
2749                 return ret;
2750         }
2751 #endif
2752         return ads_pull_string(ads, mem_ctx, msg, "sAMAccountName");
2753 }
2754
2755
2756 /**
2757  * find the update serial number - this is the core of the ldap cache
2758  * @param ads connection to ads server
2759  * @param ads connection to ADS server
2760  * @param usn Pointer to retrieved update serial number
2761  * @return status of search
2762  **/
2763 ADS_STATUS ads_USN(ADS_STRUCT *ads, uint32 *usn)
2764 {
2765         const char *attrs[] = {"highestCommittedUSN", NULL};
2766         ADS_STATUS status;
2767         LDAPMessage *res;
2768
2769         status = ads_do_search_retry(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2770         if (!ADS_ERR_OK(status)) 
2771                 return status;
2772
2773         if (ads_count_replies(ads, res) != 1) {
2774                 ads_msgfree(ads, res);
2775                 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2776         }
2777
2778         if (!ads_pull_uint32(ads, res, "highestCommittedUSN", usn)) {
2779                 ads_msgfree(ads, res);
2780                 return ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
2781         }
2782
2783         ads_msgfree(ads, res);
2784         return ADS_SUCCESS;
2785 }
2786
2787 /* parse a ADS timestring - typical string is
2788    '20020917091222.0Z0' which means 09:12.22 17th September
2789    2002, timezone 0 */
2790 static time_t ads_parse_time(const char *str)
2791 {
2792         struct tm tm;
2793
2794         ZERO_STRUCT(tm);
2795
2796         if (sscanf(str, "%4d%2d%2d%2d%2d%2d", 
2797                    &tm.tm_year, &tm.tm_mon, &tm.tm_mday, 
2798                    &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) {
2799                 return 0;
2800         }
2801         tm.tm_year -= 1900;
2802         tm.tm_mon -= 1;
2803
2804         return timegm(&tm);
2805 }
2806
2807 /********************************************************************
2808 ********************************************************************/
2809
2810 ADS_STATUS ads_current_time(ADS_STRUCT *ads)
2811 {
2812         const char *attrs[] = {"currentTime", NULL};
2813         ADS_STATUS status;
2814         LDAPMessage *res;
2815         char *timestr;
2816         TALLOC_CTX *ctx;
2817         ADS_STRUCT *ads_s = ads;
2818
2819         if (!(ctx = talloc_init("ads_current_time"))) {
2820                 return ADS_ERROR(LDAP_NO_MEMORY);
2821         }
2822
2823         /* establish a new ldap tcp session if necessary */
2824
2825         if ( !ads->ldap.ld ) {
2826                 if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup, 
2827                         ads->server.ldap_server )) == NULL )
2828                 {
2829                         goto done;
2830                 }
2831                 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
2832                 status = ads_connect( ads_s );
2833                 if ( !ADS_ERR_OK(status))
2834                         goto done;
2835         }
2836
2837         status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2838         if (!ADS_ERR_OK(status)) {
2839                 goto done;
2840         }
2841
2842         timestr = ads_pull_string(ads_s, ctx, res, "currentTime");
2843         if (!timestr) {
2844                 ads_msgfree(ads_s, res);
2845                 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2846                 goto done;
2847         }
2848
2849         /* but save the time and offset in the original ADS_STRUCT */   
2850
2851         ads->config.current_time = ads_parse_time(timestr);
2852
2853         if (ads->config.current_time != 0) {
2854                 ads->auth.time_offset = ads->config.current_time - time(NULL);
2855                 DEBUG(4,("time offset is %d seconds\n", ads->auth.time_offset));
2856         }
2857
2858         ads_msgfree(ads, res);
2859
2860         status = ADS_SUCCESS;
2861
2862 done:
2863         /* free any temporary ads connections */
2864         if ( ads_s != ads ) {
2865                 ads_destroy( &ads_s );
2866         }
2867         talloc_destroy(ctx);
2868
2869         return status;
2870 }
2871
2872 /********************************************************************
2873 ********************************************************************/
2874
2875 ADS_STATUS ads_domain_func_level(ADS_STRUCT *ads, uint32 *val)
2876 {
2877         const char *attrs[] = {"domainFunctionality", NULL};
2878         ADS_STATUS status;
2879         LDAPMessage *res;
2880         ADS_STRUCT *ads_s = ads;
2881
2882         *val = DS_DOMAIN_FUNCTION_2000;
2883
2884         /* establish a new ldap tcp session if necessary */
2885
2886         if ( !ads->ldap.ld ) {
2887                 if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup, 
2888                         ads->server.ldap_server )) == NULL )
2889                 {
2890                         status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
2891                         goto done;
2892                 }
2893                 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
2894                 status = ads_connect( ads_s );
2895                 if ( !ADS_ERR_OK(status))
2896                         goto done;
2897         }
2898
2899         /* If the attribute does not exist assume it is a Windows 2000 
2900            functional domain */
2901
2902         status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2903         if (!ADS_ERR_OK(status)) {
2904                 if ( status.err.rc == LDAP_NO_SUCH_ATTRIBUTE ) {
2905                         status = ADS_SUCCESS;
2906                 }
2907                 goto done;
2908         }
2909
2910         if ( !ads_pull_uint32(ads_s, res, "domainFunctionality", val) ) {
2911                 DEBUG(5,("ads_domain_func_level: Failed to pull the domainFunctionality attribute.\n"));
2912         }
2913         DEBUG(3,("ads_domain_func_level: %d\n", *val));
2914
2915
2916         ads_msgfree(ads, res);
2917
2918 done:
2919         /* free any temporary ads connections */
2920         if ( ads_s != ads ) {
2921                 ads_destroy( &ads_s );
2922         }
2923
2924         return status;
2925 }
2926
2927 /**
2928  * find the domain sid for our domain
2929  * @param ads connection to ads server
2930  * @param sid Pointer to domain sid
2931  * @return status of search
2932  **/
2933 ADS_STATUS ads_domain_sid(ADS_STRUCT *ads, struct dom_sid *sid)
2934 {
2935         const char *attrs[] = {"objectSid", NULL};
2936         LDAPMessage *res;
2937         ADS_STATUS rc;
2938
2939         rc = ads_do_search_retry(ads, ads->config.bind_path, LDAP_SCOPE_BASE, "(objectclass=*)", 
2940                            attrs, &res);
2941         if (!ADS_ERR_OK(rc)) return rc;
2942         if (!ads_pull_sid(ads, res, "objectSid", sid)) {
2943                 ads_msgfree(ads, res);
2944                 return ADS_ERROR_SYSTEM(ENOENT);
2945         }
2946         ads_msgfree(ads, res);
2947
2948         return ADS_SUCCESS;
2949 }
2950
2951 /**
2952  * find our site name 
2953  * @param ads connection to ads server
2954  * @param mem_ctx Pointer to talloc context
2955  * @param site_name Pointer to the sitename
2956  * @return status of search
2957  **/
2958 ADS_STATUS ads_site_dn(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char **site_name)
2959 {
2960         ADS_STATUS status;
2961         LDAPMessage *res;
2962         const char *dn, *service_name;
2963         const char *attrs[] = { "dsServiceName", NULL };
2964
2965         status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2966         if (!ADS_ERR_OK(status)) {
2967                 return status;
2968         }
2969
2970         service_name = ads_pull_string(ads, mem_ctx, res, "dsServiceName");
2971         if (service_name == NULL) {
2972                 ads_msgfree(ads, res);
2973                 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2974         }
2975
2976         ads_msgfree(ads, res);
2977
2978         /* go up three levels */
2979         dn = ads_parent_dn(ads_parent_dn(ads_parent_dn(service_name)));
2980         if (dn == NULL) {
2981                 return ADS_ERROR(LDAP_NO_MEMORY);
2982         }
2983
2984         *site_name = talloc_strdup(mem_ctx, dn);
2985         if (*site_name == NULL) {
2986                 return ADS_ERROR(LDAP_NO_MEMORY);
2987         }
2988
2989         return status;
2990         /*
2991         dsServiceName: CN=NTDS Settings,CN=W2K3DC,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,DC=ber,DC=suse,DC=de
2992         */                                               
2993 }
2994
2995 /**
2996  * find the site dn where a machine resides
2997  * @param ads connection to ads server
2998  * @param mem_ctx Pointer to talloc context
2999  * @param computer_name name of the machine
3000  * @param site_name Pointer to the sitename
3001  * @return status of search
3002  **/
3003 ADS_STATUS ads_site_dn_for_machine(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char *computer_name, const char **site_dn)
3004 {
3005         ADS_STATUS status;
3006         LDAPMessage *res;
3007         const char *parent, *filter;
3008         char *config_context = NULL;
3009         char *dn;
3010
3011         /* shortcut a query */
3012         if (strequal(computer_name, ads->config.ldap_server_name)) {
3013                 return ads_site_dn(ads, mem_ctx, site_dn);
3014         }
3015
3016         status = ads_config_path(ads, mem_ctx, &config_context);
3017         if (!ADS_ERR_OK(status)) {
3018                 return status;
3019         }
3020
3021         filter = talloc_asprintf(mem_ctx, "(cn=%s)", computer_name);
3022         if (filter == NULL) {
3023                 return ADS_ERROR(LDAP_NO_MEMORY);
3024         }
3025
3026         status = ads_do_search(ads, config_context, LDAP_SCOPE_SUBTREE, 
3027                                filter, NULL, &res);
3028         if (!ADS_ERR_OK(status)) {
3029                 return status;
3030         }
3031
3032         if (ads_count_replies(ads, res) != 1) {
3033                 ads_msgfree(ads, res);
3034                 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
3035         }
3036
3037         dn = ads_get_dn(ads, mem_ctx, res);
3038         if (dn == NULL) {
3039                 ads_msgfree(ads, res);
3040                 return ADS_ERROR(LDAP_NO_MEMORY);
3041         }
3042
3043         /* go up three levels */
3044         parent = ads_parent_dn(ads_parent_dn(ads_parent_dn(dn)));
3045         if (parent == NULL) {
3046                 ads_msgfree(ads, res);
3047                 TALLOC_FREE(dn);
3048                 return ADS_ERROR(LDAP_NO_MEMORY);
3049         }
3050
3051         *site_dn = talloc_strdup(mem_ctx, parent);
3052         if (*site_dn == NULL) {
3053                 ads_msgfree(ads, res);
3054                 TALLOC_FREE(dn);
3055                 return ADS_ERROR(LDAP_NO_MEMORY);
3056         }
3057
3058         TALLOC_FREE(dn);
3059         ads_msgfree(ads, res);
3060
3061         return status;
3062 }
3063
3064 /**
3065  * get the upn suffixes for a domain
3066  * @param ads connection to ads server
3067  * @param mem_ctx Pointer to talloc context
3068  * @param suffixes Pointer to an array of suffixes
3069  * @param num_suffixes Pointer to the number of suffixes
3070  * @return status of search
3071  **/
3072 ADS_STATUS ads_upn_suffixes(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, char ***suffixes, size_t *num_suffixes)
3073 {
3074         ADS_STATUS status;
3075         LDAPMessage *res;
3076         const char *base;
3077         char *config_context = NULL;
3078         const char *attrs[] = { "uPNSuffixes", NULL };
3079
3080         status = ads_config_path(ads, mem_ctx, &config_context);
3081         if (!ADS_ERR_OK(status)) {
3082                 return status;
3083         }
3084
3085         base = talloc_asprintf(mem_ctx, "cn=Partitions,%s", config_context);
3086         if (base == NULL) {
3087                 return ADS_ERROR(LDAP_NO_MEMORY);
3088         }
3089
3090         status = ads_search_dn(ads, &res, base, attrs);
3091         if (!ADS_ERR_OK(status)) {
3092                 return status;
3093         }
3094
3095         if (ads_count_replies(ads, res) != 1) {
3096                 ads_msgfree(ads, res);
3097                 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
3098         }
3099
3100         (*suffixes) = ads_pull_strings(ads, mem_ctx, res, "uPNSuffixes", num_suffixes);
3101         if ((*suffixes) == NULL) {
3102                 ads_msgfree(ads, res);
3103                 return ADS_ERROR(LDAP_NO_MEMORY);
3104         }
3105
3106         ads_msgfree(ads, res);
3107
3108         return status;
3109 }
3110
3111 /**
3112  * get the joinable ous for a domain
3113  * @param ads connection to ads server
3114  * @param mem_ctx Pointer to talloc context
3115  * @param ous Pointer to an array of ous
3116  * @param num_ous Pointer to the number of ous
3117  * @return status of search
3118  **/
3119 ADS_STATUS ads_get_joinable_ous(ADS_STRUCT *ads,
3120                                 TALLOC_CTX *mem_ctx,
3121                                 char ***ous,
3122                                 size_t *num_ous)
3123 {
3124         ADS_STATUS status;
3125         LDAPMessage *res = NULL;
3126         LDAPMessage *msg = NULL;
3127         const char *attrs[] = { "dn", NULL };
3128         int count = 0;
3129
3130         status = ads_search(ads, &res,
3131                             "(|(objectClass=domain)(objectclass=organizationalUnit))",
3132                             attrs);
3133         if (!ADS_ERR_OK(status)) {
3134                 return status;
3135         }
3136
3137         count = ads_count_replies(ads, res);
3138         if (count < 1) {
3139                 ads_msgfree(ads, res);
3140                 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3141         }
3142
3143         for (msg = ads_first_entry(ads, res); msg;
3144              msg = ads_next_entry(ads, msg)) {
3145
3146                 char *dn = NULL;
3147
3148                 dn = ads_get_dn(ads, talloc_tos(), msg);
3149                 if (!dn) {
3150                         ads_msgfree(ads, res);
3151                         return ADS_ERROR(LDAP_NO_MEMORY);
3152                 }
3153
3154                 if (!add_string_to_array(mem_ctx, dn,
3155                                          (const char ***)ous,
3156                                          (int *)num_ous)) {
3157                         TALLOC_FREE(dn);
3158                         ads_msgfree(ads, res);
3159                         return ADS_ERROR(LDAP_NO_MEMORY);
3160                 }
3161
3162                 TALLOC_FREE(dn);
3163         }
3164
3165         ads_msgfree(ads, res);
3166
3167         return status;
3168 }
3169
3170
3171 /**
3172  * pull a struct dom_sid from an extended dn string
3173  * @param mem_ctx TALLOC_CTX
3174  * @param extended_dn string
3175  * @param flags string type of extended_dn
3176  * @param sid pointer to a struct dom_sid
3177  * @return NT_STATUS_OK on success,
3178  *         NT_INVALID_PARAMETER on error,
3179  *         NT_STATUS_NOT_FOUND if no SID present
3180  **/
3181 ADS_STATUS ads_get_sid_from_extended_dn(TALLOC_CTX *mem_ctx,
3182                                         const char *extended_dn,
3183                                         enum ads_extended_dn_flags flags,
3184                                         struct dom_sid *sid)
3185 {
3186         char *p, *q, *dn;
3187
3188         if (!extended_dn) {
3189                 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3190         }
3191
3192         /* otherwise extended_dn gets stripped off */
3193         if ((dn = talloc_strdup(mem_ctx, extended_dn)) == NULL) {
3194                 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3195         }
3196         /*
3197          * ADS_EXTENDED_DN_HEX_STRING:
3198          * <GUID=238e1963cb390f4bb032ba0105525a29>;<SID=010500000000000515000000bb68c8fd6b61b427572eb04556040000>;CN=gd,OU=berlin,OU=suse,DC=ber,DC=suse,DC=de
3199          *
3200          * ADS_EXTENDED_DN_STRING (only with w2k3):
3201          * <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
3202          *
3203          * Object with no SID, such as an Exchange Public Folder
3204          * <GUID=28907fb4bdf6854993e7f0a10b504e7c>;CN=public,CN=Microsoft Exchange System Objects,DC=sd2k3ms,DC=west,DC=isilon,DC=com
3205          */
3206
3207         p = strchr(dn, ';');
3208         if (!p) {
3209                 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3210         }
3211
3212         if (strncmp(p, ";<SID=", strlen(";<SID=")) != 0) {
3213                 DEBUG(5,("No SID present in extended dn\n"));
3214                 return ADS_ERROR_NT(NT_STATUS_NOT_FOUND);
3215         }
3216
3217         p += strlen(";<SID=");
3218
3219         q = strchr(p, '>');
3220         if (!q) {
3221                 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3222         }
3223
3224         *q = '\0';
3225
3226         DEBUG(100,("ads_get_sid_from_extended_dn: sid string is %s\n", p));
3227
3228         switch (flags) {
3229
3230         case ADS_EXTENDED_DN_STRING:
3231                 if (!string_to_sid(sid, p)) {
3232                         return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3233                 }
3234                 break;
3235         case ADS_EXTENDED_DN_HEX_STRING: {
3236                 fstring buf;
3237                 size_t buf_len;
3238
3239                 buf_len = strhex_to_str(buf, sizeof(buf), p, strlen(p));
3240                 if (buf_len == 0) {
3241                         return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3242                 }
3243
3244                 if (!sid_parse(buf, buf_len, sid)) {
3245                         DEBUG(10,("failed to parse sid\n"));
3246                         return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3247                 }
3248                 break;
3249                 }
3250         default:
3251                 DEBUG(10,("unknown extended dn format\n"));
3252                 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3253         }
3254
3255         return ADS_ERROR_NT(NT_STATUS_OK);
3256 }
3257
3258 /**
3259  * pull an array of struct dom_sids from a ADS result
3260  * @param ads connection to ads server
3261  * @param mem_ctx TALLOC_CTX for allocating sid array
3262  * @param msg Results of search
3263  * @param field Attribute to retrieve
3264  * @param flags string type of extended_dn
3265  * @param sids pointer to sid array to allocate
3266  * @return the count of SIDs pulled
3267  **/
3268  int ads_pull_sids_from_extendeddn(ADS_STRUCT *ads,
3269                                    TALLOC_CTX *mem_ctx,
3270                                    LDAPMessage *msg,
3271                                    const char *field,
3272                                    enum ads_extended_dn_flags flags,
3273                                    struct dom_sid **sids)
3274 {
3275         int i;
3276         ADS_STATUS rc;
3277         size_t dn_count, ret_count = 0;
3278         char **dn_strings;
3279
3280         if ((dn_strings = ads_pull_strings(ads, mem_ctx, msg, field,
3281                                            &dn_count)) == NULL) {
3282                 return 0;
3283         }
3284
3285         (*sids) = TALLOC_ZERO_ARRAY(mem_ctx, struct dom_sid, dn_count + 1);
3286         if (!(*sids)) {
3287                 TALLOC_FREE(dn_strings);
3288                 return 0;
3289         }
3290
3291         for (i=0; i<dn_count; i++) {
3292                 rc = ads_get_sid_from_extended_dn(mem_ctx, dn_strings[i],
3293                                                   flags, &(*sids)[i]);
3294                 if (!ADS_ERR_OK(rc)) {
3295                         if (NT_STATUS_EQUAL(ads_ntstatus(rc),
3296                             NT_STATUS_NOT_FOUND)) {
3297                                 continue;
3298                         }
3299                         else {
3300                                 TALLOC_FREE(*sids);
3301                                 TALLOC_FREE(dn_strings);
3302                                 return 0;
3303                         }
3304                 }
3305                 ret_count++;
3306         }
3307
3308         TALLOC_FREE(dn_strings);
3309
3310         return ret_count;
3311 }
3312
3313 /********************************************************************
3314 ********************************************************************/
3315
3316 char* ads_get_dnshostname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3317 {
3318         LDAPMessage *res = NULL;
3319         ADS_STATUS status;
3320         int count = 0;
3321         char *name = NULL;
3322
3323         status = ads_find_machine_acct(ads, &res, global_myname());
3324         if (!ADS_ERR_OK(status)) {
3325                 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
3326                         global_myname()));
3327                 goto out;
3328         }
3329
3330         if ( (count = ads_count_replies(ads, res)) != 1 ) {
3331                 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
3332                 goto out;
3333         }
3334
3335         if ( (name = ads_pull_string(ads, ctx, res, "dNSHostName")) == NULL ) {
3336                 DEBUG(0,("ads_get_dnshostname: No dNSHostName attribute!\n"));
3337         }
3338
3339 out:
3340         ads_msgfree(ads, res);
3341
3342         return name;
3343 }
3344
3345 /********************************************************************
3346 ********************************************************************/
3347
3348 char* ads_get_upn( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3349 {
3350         LDAPMessage *res = NULL;
3351         ADS_STATUS status;
3352         int count = 0;
3353         char *name = NULL;
3354
3355         status = ads_find_machine_acct(ads, &res, machine_name);
3356         if (!ADS_ERR_OK(status)) {
3357                 DEBUG(0,("ads_get_upn: Failed to find account for %s\n",
3358                         global_myname()));
3359                 goto out;
3360         }
3361
3362         if ( (count = ads_count_replies(ads, res)) != 1 ) {
3363                 DEBUG(1,("ads_get_upn: %d entries returned!\n", count));
3364                 goto out;
3365         }
3366
3367         if ( (name = ads_pull_string(ads, ctx, res, "userPrincipalName")) == NULL ) {
3368                 DEBUG(2,("ads_get_upn: No userPrincipalName attribute!\n"));
3369         }
3370
3371 out:
3372         ads_msgfree(ads, res);
3373
3374         return name;
3375 }
3376
3377 /********************************************************************
3378 ********************************************************************/
3379
3380 char* ads_get_samaccountname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3381 {
3382         LDAPMessage *res = NULL;
3383         ADS_STATUS status;
3384         int count = 0;
3385         char *name = NULL;
3386
3387         status = ads_find_machine_acct(ads, &res, global_myname());
3388         if (!ADS_ERR_OK(status)) {
3389                 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
3390                         global_myname()));
3391                 goto out;
3392         }
3393
3394         if ( (count = ads_count_replies(ads, res)) != 1 ) {
3395                 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
3396                 goto out;
3397         }
3398
3399         if ( (name = ads_pull_string(ads, ctx, res, "sAMAccountName")) == NULL ) {
3400                 DEBUG(0,("ads_get_dnshostname: No sAMAccountName attribute!\n"));
3401         }
3402
3403 out:
3404         ads_msgfree(ads, res);
3405
3406         return name;
3407 }
3408
3409 #if 0
3410
3411    SAVED CODE - we used to join via ldap - remember how we did this. JRA.
3412
3413 /**
3414  * Join a machine to a realm
3415  *  Creates the machine account and sets the machine password
3416  * @param ads connection to ads server
3417  * @param machine name of host to add
3418  * @param org_unit Organizational unit to place machine in
3419  * @return status of join
3420  **/
3421 ADS_STATUS ads_join_realm(ADS_STRUCT *ads, const char *machine_name,
3422                         uint32 account_type, const char *org_unit)
3423 {
3424         ADS_STATUS status;
3425         LDAPMessage *res = NULL;
3426         char *machine;
3427
3428         /* machine name must be lowercase */
3429         machine = SMB_STRDUP(machine_name);
3430         strlower_m(machine);
3431
3432         /*
3433         status = ads_find_machine_acct(ads, (void **)&res, machine);
3434         if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
3435                 DEBUG(0, ("Host account for %s already exists - deleting old account\n", machine));
3436                 status = ads_leave_realm(ads, machine);
3437                 if (!ADS_ERR_OK(status)) {
3438                         DEBUG(0, ("Failed to delete host '%s' from the '%s' realm.\n",
3439                                 machine, ads->config.realm));
3440                         return status;
3441                 }
3442         }
3443         */
3444         status = ads_add_machine_acct(ads, machine, account_type, org_unit);
3445         if (!ADS_ERR_OK(status)) {
3446                 DEBUG(0, ("ads_join_realm: ads_add_machine_acct failed (%s): %s\n", machine, ads_errstr(status)));
3447                 SAFE_FREE(machine);
3448                 return status;
3449         }
3450
3451         status = ads_find_machine_acct(ads, (void **)(void *)&res, machine);
3452         if (!ADS_ERR_OK(status)) {
3453                 DEBUG(0, ("ads_join_realm: Host account test failed for machine %s\n", machine));
3454                 SAFE_FREE(machine);
3455                 return status;
3456         }
3457
3458         SAFE_FREE(machine);
3459         ads_msgfree(ads, res);
3460
3461         return status;
3462 }
3463 #endif
3464
3465 /**
3466  * Delete a machine from the realm
3467  * @param ads connection to ads server
3468  * @param hostname Machine to remove
3469  * @return status of delete
3470  **/
3471 ADS_STATUS ads_leave_realm(ADS_STRUCT *ads, const char *hostname)
3472 {
3473         ADS_STATUS status;
3474         void *msg;
3475         LDAPMessage *res;
3476         char *hostnameDN, *host;
3477         int rc;
3478         LDAPControl ldap_control;
3479         LDAPControl  * pldap_control[2] = {NULL, NULL};
3480
3481         pldap_control[0] = &ldap_control;
3482         memset(&ldap_control, 0, sizeof(LDAPControl));
3483         ldap_control.ldctl_oid = (char *)LDAP_SERVER_TREE_DELETE_OID;
3484
3485         /* hostname must be lowercase */
3486         host = SMB_STRDUP(hostname);
3487         strlower_m(host);
3488
3489         status = ads_find_machine_acct(ads, &res, host);
3490         if (!ADS_ERR_OK(status)) {
3491                 DEBUG(0, ("Host account for %s does not exist.\n", host));
3492                 SAFE_FREE(host);
3493                 return status;
3494         }
3495
3496         msg = ads_first_entry(ads, res);
3497         if (!msg) {
3498                 SAFE_FREE(host);
3499                 return ADS_ERROR_SYSTEM(ENOENT);
3500         }
3501
3502         hostnameDN = ads_get_dn(ads, talloc_tos(), (LDAPMessage *)msg);
3503
3504         rc = ldap_delete_ext_s(ads->ldap.ld, hostnameDN, pldap_control, NULL);
3505         if (rc) {
3506                 DEBUG(3,("ldap_delete_ext_s failed with error code %d\n", rc));
3507         }else {
3508                 DEBUG(3,("ldap_delete_ext_s succeeded with error code %d\n", rc));
3509         }
3510
3511         if (rc != LDAP_SUCCESS) {
3512                 const char *attrs[] = { "cn", NULL };
3513                 LDAPMessage *msg_sub;
3514
3515                 /* we only search with scope ONE, we do not expect any further
3516                  * objects to be created deeper */
3517
3518                 status = ads_do_search_retry(ads, hostnameDN,
3519                                              LDAP_SCOPE_ONELEVEL,
3520                                              "(objectclass=*)", attrs, &res);
3521
3522                 if (!ADS_ERR_OK(status)) {
3523                         SAFE_FREE(host);
3524                         TALLOC_FREE(hostnameDN);
3525                         return status;
3526                 }
3527
3528                 for (msg_sub = ads_first_entry(ads, res); msg_sub;
3529                         msg_sub = ads_next_entry(ads, msg_sub)) {
3530
3531                         char *dn = NULL;
3532
3533                         if ((dn = ads_get_dn(ads, talloc_tos(), msg_sub)) == NULL) {
3534                                 SAFE_FREE(host);
3535                                 TALLOC_FREE(hostnameDN);
3536                                 return ADS_ERROR(LDAP_NO_MEMORY);
3537                         }
3538
3539                         status = ads_del_dn(ads, dn);
3540                         if (!ADS_ERR_OK(status)) {
3541                                 DEBUG(3,("failed to delete dn %s: %s\n", dn, ads_errstr(status)));
3542                                 SAFE_FREE(host);
3543                                 TALLOC_FREE(dn);
3544                                 TALLOC_FREE(hostnameDN);
3545                                 return status;
3546                         }
3547
3548                         TALLOC_FREE(dn);
3549                 }
3550
3551                 /* there should be no subordinate objects anymore */
3552                 status = ads_do_search_retry(ads, hostnameDN,
3553                                              LDAP_SCOPE_ONELEVEL,
3554                                              "(objectclass=*)", attrs, &res);
3555
3556                 if (!ADS_ERR_OK(status) || ( (ads_count_replies(ads, res)) > 0 ) ) {
3557                         SAFE_FREE(host);
3558                         TALLOC_FREE(hostnameDN);
3559                         return status;
3560                 }
3561
3562                 /* delete hostnameDN now */
3563                 status = ads_del_dn(ads, hostnameDN);
3564                 if (!ADS_ERR_OK(status)) {
3565                         SAFE_FREE(host);
3566                         DEBUG(3,("failed to delete dn %s: %s\n", hostnameDN, ads_errstr(status)));
3567                         TALLOC_FREE(hostnameDN);
3568                         return status;
3569                 }
3570         }
3571
3572         TALLOC_FREE(hostnameDN);
3573
3574         status = ads_find_machine_acct(ads, &res, host);
3575         if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
3576                 DEBUG(3, ("Failed to remove host account.\n"));
3577                 SAFE_FREE(host);
3578                 return status;
3579         }
3580
3581         SAFE_FREE(host);
3582         return status;
3583 }
3584
3585 /**
3586  * pull all token-sids from an LDAP dn
3587  * @param ads connection to ads server
3588  * @param mem_ctx TALLOC_CTX for allocating sid array
3589  * @param dn of LDAP object
3590  * @param user_sid pointer to struct dom_sid (objectSid)
3591  * @param primary_group_sid pointer to struct dom_sid (self composed)
3592  * @param sids pointer to sid array to allocate
3593  * @param num_sids counter of SIDs pulled
3594  * @return status of token query
3595  **/
3596  ADS_STATUS ads_get_tokensids(ADS_STRUCT *ads,
3597                               TALLOC_CTX *mem_ctx,
3598                               const char *dn,
3599                               struct dom_sid *user_sid,
3600                               struct dom_sid *primary_group_sid,
3601                               struct dom_sid **sids,
3602                               size_t *num_sids)
3603 {
3604         ADS_STATUS status;
3605         LDAPMessage *res = NULL;
3606         int count = 0;
3607         size_t tmp_num_sids;
3608         struct dom_sid *tmp_sids;
3609         struct dom_sid tmp_user_sid;
3610         struct dom_sid tmp_primary_group_sid;
3611         uint32 pgid;
3612         const char *attrs[] = {
3613                 "objectSid",
3614                 "tokenGroups",
3615                 "primaryGroupID",
3616                 NULL
3617         };
3618
3619         status = ads_search_retry_dn(ads, &res, dn, attrs);
3620         if (!ADS_ERR_OK(status)) {
3621                 return status;
3622         }
3623
3624         count = ads_count_replies(ads, res);
3625         if (count != 1) {
3626                 ads_msgfree(ads, res);
3627                 return ADS_ERROR_LDAP(LDAP_NO_SUCH_OBJECT);
3628         }
3629
3630         if (!ads_pull_sid(ads, res, "objectSid", &tmp_user_sid)) {
3631                 ads_msgfree(ads, res);
3632                 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3633         }
3634
3635         if (!ads_pull_uint32(ads, res, "primaryGroupID", &pgid)) {
3636                 ads_msgfree(ads, res);
3637                 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3638         }
3639
3640         {
3641                 /* hack to compose the primary group sid without knowing the
3642                  * domsid */
3643
3644                 struct dom_sid domsid;
3645                 uint32 dummy_rid;
3646
3647                 sid_copy(&domsid, &tmp_user_sid);
3648
3649                 if (!sid_split_rid(&domsid, &dummy_rid)) {
3650                         ads_msgfree(ads, res);
3651                         return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3652                 }
3653
3654                 if (!sid_compose(&tmp_primary_group_sid, &domsid, pgid)) {
3655                         ads_msgfree(ads, res);
3656                         return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3657                 }
3658         }
3659
3660         tmp_num_sids = ads_pull_sids(ads, mem_ctx, res, "tokenGroups", &tmp_sids);
3661
3662         if (tmp_num_sids == 0 || !tmp_sids) {
3663                 ads_msgfree(ads, res);
3664                 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3665         }
3666
3667         if (num_sids) {
3668                 *num_sids = tmp_num_sids;
3669         }
3670
3671         if (sids) {
3672                 *sids = tmp_sids;
3673         }
3674
3675         if (user_sid) {
3676                 *user_sid = tmp_user_sid;
3677         }
3678
3679         if (primary_group_sid) {
3680                 *primary_group_sid = tmp_primary_group_sid;
3681         }
3682
3683         DEBUG(10,("ads_get_tokensids: returned %d sids\n", (int)tmp_num_sids + 2));
3684
3685         ads_msgfree(ads, res);
3686         return ADS_ERROR_LDAP(LDAP_SUCCESS);
3687 }
3688
3689 /**
3690  * Find a sAMAccoutName in LDAP
3691  * @param ads connection to ads server
3692  * @param mem_ctx TALLOC_CTX for allocating sid array
3693  * @param samaccountname to search
3694  * @param uac_ret uint32 pointer userAccountControl attribute value
3695  * @param dn_ret pointer to dn
3696  * @return status of token query
3697  **/
3698 ADS_STATUS ads_find_samaccount(ADS_STRUCT *ads,
3699                                TALLOC_CTX *mem_ctx,
3700                                const char *samaccountname,
3701                                uint32 *uac_ret,
3702                                const char **dn_ret)
3703 {
3704         ADS_STATUS status;
3705         const char *attrs[] = { "userAccountControl", NULL };
3706         const char *filter;
3707         LDAPMessage *res = NULL;
3708         char *dn = NULL;
3709         uint32 uac = 0;
3710
3711         filter = talloc_asprintf(mem_ctx, "(&(objectclass=user)(sAMAccountName=%s))",
3712                 samaccountname);
3713         if (filter == NULL) {
3714                 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
3715                 goto out;
3716         }
3717
3718         status = ads_do_search_all(ads, ads->config.bind_path,
3719                                    LDAP_SCOPE_SUBTREE,
3720                                    filter, attrs, &res);
3721
3722         if (!ADS_ERR_OK(status)) {
3723                 goto out;
3724         }
3725
3726         if (ads_count_replies(ads, res) != 1) {
3727                 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3728                 goto out;
3729         }
3730
3731         dn = ads_get_dn(ads, talloc_tos(), res);
3732         if (dn == NULL) {
3733                 status = ADS_ERROR(LDAP_NO_MEMORY);
3734                 goto out;
3735         }
3736
3737         if (!ads_pull_uint32(ads, res, "userAccountControl", &uac)) {
3738                 status = ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
3739                 goto out;
3740         }
3741
3742         if (uac_ret) {
3743                 *uac_ret = uac;
3744         }
3745
3746         if (dn_ret) {
3747                 *dn_ret = talloc_strdup(mem_ctx, dn);
3748                 if (!*dn_ret) {
3749                         status = ADS_ERROR(LDAP_NO_MEMORY);
3750                         goto out;
3751                 }
3752         }
3753  out:
3754         TALLOC_FREE(dn);
3755         ads_msgfree(ads, res);
3756
3757         return status;
3758 }
3759
3760 /**
3761  * find our configuration path 
3762  * @param ads connection to ads server
3763  * @param mem_ctx Pointer to talloc context
3764  * @param config_path Pointer to the config path
3765  * @return status of search
3766  **/
3767 ADS_STATUS ads_config_path(ADS_STRUCT *ads, 
3768                            TALLOC_CTX *mem_ctx, 
3769                            char **config_path)
3770 {
3771         ADS_STATUS status;
3772         LDAPMessage *res = NULL;
3773         const char *config_context = NULL;
3774         const char *attrs[] = { "configurationNamingContext", NULL };
3775
3776         status = ads_do_search(ads, "", LDAP_SCOPE_BASE, 
3777                                "(objectclass=*)", attrs, &res);
3778         if (!ADS_ERR_OK(status)) {
3779                 return status;
3780         }
3781
3782         config_context = ads_pull_string(ads, mem_ctx, res, 
3783                                          "configurationNamingContext");
3784         ads_msgfree(ads, res);
3785         if (!config_context) {
3786                 return ADS_ERROR(LDAP_NO_MEMORY);
3787         }
3788
3789         if (config_path) {
3790                 *config_path = talloc_strdup(mem_ctx, config_context);
3791                 if (!*config_path) {
3792                         return ADS_ERROR(LDAP_NO_MEMORY);
3793                 }
3794         }
3795
3796         return ADS_ERROR(LDAP_SUCCESS);
3797 }
3798
3799 /**
3800  * find the displayName of an extended right 
3801  * @param ads connection to ads server
3802  * @param config_path The config path
3803  * @param mem_ctx Pointer to talloc context
3804  * @param GUID struct of the rightsGUID
3805  * @return status of search
3806  **/
3807 const char *ads_get_extended_right_name_by_guid(ADS_STRUCT *ads, 
3808                                                 const char *config_path, 
3809                                                 TALLOC_CTX *mem_ctx, 
3810                                                 const struct GUID *rights_guid)
3811 {
3812         ADS_STATUS rc;
3813         LDAPMessage *res = NULL;
3814         char *expr = NULL;
3815         const char *attrs[] = { "displayName", NULL };
3816         const char *result = NULL;
3817         const char *path;
3818
3819         if (!ads || !mem_ctx || !rights_guid) {
3820                 goto done;
3821         }
3822
3823         expr = talloc_asprintf(mem_ctx, "(rightsGuid=%s)", 
3824                                GUID_string(mem_ctx, rights_guid));
3825         if (!expr) {
3826                 goto done;
3827         }
3828
3829         path = talloc_asprintf(mem_ctx, "cn=Extended-Rights,%s", config_path);
3830         if (!path) {
3831                 goto done;
3832         }
3833
3834         rc = ads_do_search_retry(ads, path, LDAP_SCOPE_SUBTREE, 
3835                                  expr, attrs, &res);
3836         if (!ADS_ERR_OK(rc)) {
3837                 goto done;
3838         }
3839
3840         if (ads_count_replies(ads, res) != 1) {
3841                 goto done;
3842         }
3843
3844         result = ads_pull_string(ads, mem_ctx, res, "displayName");
3845
3846  done:
3847         ads_msgfree(ads, res);
3848         return result;
3849 }
3850
3851 /**
3852  * verify or build and verify an account ou
3853  * @param mem_ctx Pointer to talloc context
3854  * @param ads connection to ads server
3855  * @param account_ou
3856  * @return status of search
3857  **/
3858
3859 ADS_STATUS ads_check_ou_dn(TALLOC_CTX *mem_ctx,
3860                            ADS_STRUCT *ads,
3861                            const char **account_ou)
3862 {
3863         char **exploded_dn;
3864         const char *name;
3865         char *ou_string;
3866
3867         exploded_dn = ldap_explode_dn(*account_ou, 0);
3868         if (exploded_dn) {
3869                 ldap_value_free(exploded_dn);
3870                 return ADS_SUCCESS;
3871         }
3872
3873         ou_string = ads_ou_string(ads, *account_ou);
3874         if (!ou_string) {
3875                 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX);
3876         }
3877
3878         name = talloc_asprintf(mem_ctx, "%s,%s", ou_string,
3879                                ads->config.bind_path);
3880         SAFE_FREE(ou_string);
3881
3882         if (!name) {
3883                 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3884         }
3885
3886         exploded_dn = ldap_explode_dn(name, 0);
3887         if (!exploded_dn) {
3888                 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX);
3889         }
3890         ldap_value_free(exploded_dn);
3891
3892         *account_ou = name;
3893         return ADS_SUCCESS;
3894 }
3895
3896 #endif