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