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