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