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