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