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