s3-libads: Use the IP instead of the name.
[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(addr,
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                 LDAPMessage *msg, *next;
1144
1145                 status = ads_do_paged_search_args(ads, bind_path, scope, expr,
1146                                               attrs, args, &res2, &count, &cookie);
1147                 if (!ADS_ERR_OK(status)) {
1148                         /* Ensure we free all collected results */
1149                         ads_msgfree(ads, *res);
1150                         *res = NULL;
1151                         break;
1152                 }
1153
1154                 /* this relies on the way that ldap_add_result_entry() works internally. I hope
1155                    that this works on all ldap libs, but I have only tested with openldap */
1156                 for (msg = ads_first_message(ads, res2); msg; msg = next) {
1157                         next = ads_next_message(ads, msg);
1158                         ldap_add_result_entry((LDAPMessage **)res, msg);
1159                 }
1160                 /* note that we do not free res2, as the memory is now
1161                    part of the main returned list */
1162         }
1163 #else
1164         DEBUG(0, ("no ldap_add_result_entry() support in LDAP libs!\n"));
1165         status = ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
1166 #endif
1167
1168         return status;
1169 }
1170
1171  ADS_STATUS ads_do_search_all(ADS_STRUCT *ads, const char *bind_path,
1172                               int scope, const char *expr,
1173                               const char **attrs, LDAPMessage **res)
1174 {
1175         return ads_do_search_all_args(ads, bind_path, scope, expr, attrs, NULL, res);
1176 }
1177
1178  ADS_STATUS ads_do_search_all_sd_flags(ADS_STRUCT *ads, const char *bind_path,
1179                                        int scope, const char *expr,
1180                                        const char **attrs, uint32 sd_flags, 
1181                                        LDAPMessage **res)
1182 {
1183         ads_control args;
1184
1185         args.control = ADS_SD_FLAGS_OID;
1186         args.val = sd_flags;
1187         args.critical = True;
1188
1189         return ads_do_search_all_args(ads, bind_path, scope, expr, attrs, &args, res);
1190 }
1191
1192
1193 /**
1194  * Run a function on all results for a search.  Uses ads_do_paged_search() and
1195  *  runs the function as each page is returned, using ads_process_results()
1196  * @param ads connection to ads server
1197  * @param bind_path Base dn for the search
1198  * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1199  * @param expr Search expression - specified in local charset
1200  * @param attrs Attributes to retrieve - specified in UTF-8 or ascii
1201  * @param fn Function which takes attr name, values list, and data_area
1202  * @param data_area Pointer which is passed to function on each call
1203  * @return status of search
1204  **/
1205 ADS_STATUS ads_do_search_all_fn(ADS_STRUCT *ads, const char *bind_path,
1206                                 int scope, const char *expr, const char **attrs,
1207                                 bool (*fn)(ADS_STRUCT *, char *, void **, void *), 
1208                                 void *data_area)
1209 {
1210         struct berval *cookie = NULL;
1211         int count = 0;
1212         ADS_STATUS status;
1213         LDAPMessage *res;
1214
1215         status = ads_do_paged_search(ads, bind_path, scope, expr, attrs, &res,
1216                                      &count, &cookie);
1217
1218         if (!ADS_ERR_OK(status)) return status;
1219
1220         ads_process_results(ads, res, fn, data_area);
1221         ads_msgfree(ads, res);
1222
1223         while (cookie) {
1224                 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs,
1225                                              &res, &count, &cookie);
1226
1227                 if (!ADS_ERR_OK(status)) break;
1228
1229                 ads_process_results(ads, res, fn, data_area);
1230                 ads_msgfree(ads, res);
1231         }
1232
1233         return status;
1234 }
1235
1236 /**
1237  * Do a search with a timeout.
1238  * @param ads connection to ads server
1239  * @param bind_path Base dn for the search
1240  * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1241  * @param expr Search expression
1242  * @param attrs Attributes to retrieve
1243  * @param res ** which will contain results - free res* with ads_msgfree()
1244  * @return status of search
1245  **/
1246  ADS_STATUS ads_do_search(ADS_STRUCT *ads, const char *bind_path, int scope, 
1247                           const char *expr,
1248                           const char **attrs, LDAPMessage **res)
1249 {
1250         int rc;
1251         char *utf8_expr, *utf8_path, **search_attrs = NULL;
1252         size_t converted_size;
1253         TALLOC_CTX *ctx;
1254
1255         *res = NULL;
1256         if (!(ctx = talloc_init("ads_do_search"))) {
1257                 DEBUG(1,("ads_do_search: talloc_init() failed!"));
1258                 return ADS_ERROR(LDAP_NO_MEMORY);
1259         }
1260
1261         /* 0 means the conversion worked but the result was empty 
1262            so we only fail if it's negative.  In any case, it always 
1263            at least nulls out the dest */
1264         if (!push_utf8_talloc(ctx, &utf8_expr, expr, &converted_size) ||
1265             !push_utf8_talloc(ctx, &utf8_path, bind_path, &converted_size))
1266         {
1267                 DEBUG(1,("ads_do_search: push_utf8_talloc() failed!"));
1268                 rc = LDAP_NO_MEMORY;
1269                 goto done;
1270         }
1271
1272         if (!attrs || !(*attrs))
1273                 search_attrs = NULL;
1274         else {
1275                 /* This would be the utf8-encoded version...*/
1276                 /* if (!(search_attrs = ads_push_strvals(ctx, attrs)))  */
1277                 if (!(search_attrs = str_list_copy(talloc_tos(), attrs)))
1278                 {
1279                         DEBUG(1,("ads_do_search: str_list_copy() failed!"));
1280                         rc = LDAP_NO_MEMORY;
1281                         goto done;
1282                 }
1283         }
1284
1285         /* see the note in ads_do_paged_search - we *must* disable referrals */
1286         ldap_set_option(ads->ldap.ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
1287
1288         rc = ldap_search_with_timeout(ads->ldap.ld, utf8_path, scope, utf8_expr,
1289                                       search_attrs, 0, NULL, NULL, 
1290                                       LDAP_NO_LIMIT,
1291                                       (LDAPMessage **)res);
1292
1293         if (rc == LDAP_SIZELIMIT_EXCEEDED) {
1294                 DEBUG(3,("Warning! sizelimit exceeded in ldap. Truncating.\n"));
1295                 rc = 0;
1296         }
1297
1298  done:
1299         talloc_destroy(ctx);
1300         /* if/when we decide to utf8-encode attrs, take out this next line */
1301         TALLOC_FREE(search_attrs);
1302         return ADS_ERROR(rc);
1303 }
1304 /**
1305  * Do a general ADS search
1306  * @param ads connection to ads server
1307  * @param res ** which will contain results - free res* with ads_msgfree()
1308  * @param expr Search expression
1309  * @param attrs Attributes to retrieve
1310  * @return status of search
1311  **/
1312  ADS_STATUS ads_search(ADS_STRUCT *ads, LDAPMessage **res, 
1313                        const char *expr, const char **attrs)
1314 {
1315         return ads_do_search(ads, ads->config.bind_path, LDAP_SCOPE_SUBTREE, 
1316                              expr, attrs, res);
1317 }
1318
1319 /**
1320  * Do a search on a specific DistinguishedName
1321  * @param ads connection to ads server
1322  * @param res ** which will contain results - free res* with ads_msgfree()
1323  * @param dn DistinguishName to search
1324  * @param attrs Attributes to retrieve
1325  * @return status of search
1326  **/
1327  ADS_STATUS ads_search_dn(ADS_STRUCT *ads, LDAPMessage **res, 
1328                           const char *dn, const char **attrs)
1329 {
1330         return ads_do_search(ads, dn, LDAP_SCOPE_BASE, "(objectclass=*)",
1331                              attrs, res);
1332 }
1333
1334 /**
1335  * Free up memory from a ads_search
1336  * @param ads connection to ads server
1337  * @param msg Search results to free
1338  **/
1339  void ads_msgfree(ADS_STRUCT *ads, LDAPMessage *msg)
1340 {
1341         if (!msg) return;
1342         ldap_msgfree(msg);
1343 }
1344
1345 /**
1346  * Get a dn from search results
1347  * @param ads connection to ads server
1348  * @param msg Search result
1349  * @return dn string
1350  **/
1351  char *ads_get_dn(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, LDAPMessage *msg)
1352 {
1353         char *utf8_dn, *unix_dn;
1354         size_t converted_size;
1355
1356         utf8_dn = ldap_get_dn(ads->ldap.ld, msg);
1357
1358         if (!utf8_dn) {
1359                 DEBUG (5, ("ads_get_dn: ldap_get_dn failed\n"));
1360                 return NULL;
1361         }
1362
1363         if (!pull_utf8_talloc(mem_ctx, &unix_dn, utf8_dn, &converted_size)) {
1364                 DEBUG(0,("ads_get_dn: string conversion failure utf8 [%s]\n",
1365                         utf8_dn ));
1366                 return NULL;
1367         }
1368         ldap_memfree(utf8_dn);
1369         return unix_dn;
1370 }
1371
1372 /**
1373  * Get the parent from a dn
1374  * @param dn the dn to return the parent from
1375  * @return parent dn string
1376  **/
1377 char *ads_parent_dn(const char *dn)
1378 {
1379         char *p;
1380
1381         if (dn == NULL) {
1382                 return NULL;
1383         }
1384
1385         p = strchr(dn, ',');
1386
1387         if (p == NULL) {
1388                 return NULL;
1389         }
1390
1391         return p+1;
1392 }
1393
1394 /**
1395  * Find a machine account given a hostname
1396  * @param ads connection to ads server
1397  * @param res ** which will contain results - free res* with ads_msgfree()
1398  * @param host Hostname to search for
1399  * @return status of search
1400  **/
1401  ADS_STATUS ads_find_machine_acct(ADS_STRUCT *ads, LDAPMessage **res,
1402                                   const char *machine)
1403 {
1404         ADS_STATUS status;
1405         char *expr;
1406         const char *attrs[] = {"*", "nTSecurityDescriptor", NULL};
1407
1408         *res = NULL;
1409
1410         /* the easiest way to find a machine account anywhere in the tree
1411            is to look for hostname$ */
1412         if (asprintf(&expr, "(samAccountName=%s$)", machine) == -1) {
1413                 DEBUG(1, ("asprintf failed!\n"));
1414                 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1415         }
1416
1417         status = ads_search(ads, res, expr, attrs);
1418         SAFE_FREE(expr);
1419         return status;
1420 }
1421
1422 /**
1423  * Initialize a list of mods to be used in a modify request
1424  * @param ctx An initialized TALLOC_CTX
1425  * @return allocated ADS_MODLIST
1426  **/
1427 ADS_MODLIST ads_init_mods(TALLOC_CTX *ctx)
1428 {
1429 #define ADS_MODLIST_ALLOC_SIZE 10
1430         LDAPMod **mods;
1431
1432         if ((mods = talloc_zero_array(ctx, LDAPMod *, ADS_MODLIST_ALLOC_SIZE + 1)))
1433                 /* -1 is safety to make sure we don't go over the end.
1434                    need to reset it to NULL before doing ldap modify */
1435                 mods[ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1436
1437         return (ADS_MODLIST)mods;
1438 }
1439
1440
1441 /*
1442   add an attribute to the list, with values list already constructed
1443 */
1444 static ADS_STATUS ads_modlist_add(TALLOC_CTX *ctx, ADS_MODLIST *mods, 
1445                                   int mod_op, const char *name, 
1446                                   const void *_invals)
1447 {
1448         const void **invals = (const void **)_invals;
1449         int curmod;
1450         LDAPMod **modlist = (LDAPMod **) *mods;
1451         struct berval **ber_values = NULL;
1452         char **char_values = NULL;
1453
1454         if (!invals) {
1455                 mod_op = LDAP_MOD_DELETE;
1456         } else {
1457                 if (mod_op & LDAP_MOD_BVALUES)
1458                         ber_values = ads_dup_values(ctx, 
1459                                                 (const struct berval **)invals);
1460                 else
1461                         char_values = ads_push_strvals(ctx, 
1462                                                   (const char **) invals);
1463         }
1464
1465         /* find the first empty slot */
1466         for (curmod=0; modlist[curmod] && modlist[curmod] != (LDAPMod *) -1;
1467              curmod++);
1468         if (modlist[curmod] == (LDAPMod *) -1) {
1469                 if (!(modlist = talloc_realloc(ctx, modlist, LDAPMod *,
1470                                 curmod+ADS_MODLIST_ALLOC_SIZE+1)))
1471                         return ADS_ERROR(LDAP_NO_MEMORY);
1472                 memset(&modlist[curmod], 0, 
1473                        ADS_MODLIST_ALLOC_SIZE*sizeof(LDAPMod *));
1474                 modlist[curmod+ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1475                 *mods = (ADS_MODLIST)modlist;
1476         }
1477
1478         if (!(modlist[curmod] = talloc_zero(ctx, LDAPMod)))
1479                 return ADS_ERROR(LDAP_NO_MEMORY);
1480         modlist[curmod]->mod_type = talloc_strdup(ctx, name);
1481         if (mod_op & LDAP_MOD_BVALUES) {
1482                 modlist[curmod]->mod_bvalues = ber_values;
1483         } else if (mod_op & LDAP_MOD_DELETE) {
1484                 modlist[curmod]->mod_values = NULL;
1485         } else {
1486                 modlist[curmod]->mod_values = char_values;
1487         }
1488
1489         modlist[curmod]->mod_op = mod_op;
1490         return ADS_ERROR(LDAP_SUCCESS);
1491 }
1492
1493 /**
1494  * Add a single string value to a mod list
1495  * @param ctx An initialized TALLOC_CTX
1496  * @param mods An initialized ADS_MODLIST
1497  * @param name The attribute name to add
1498  * @param val The value to add - NULL means DELETE
1499  * @return ADS STATUS indicating success of add
1500  **/
1501 ADS_STATUS ads_mod_str(TALLOC_CTX *ctx, ADS_MODLIST *mods, 
1502                        const char *name, const char *val)
1503 {
1504         const char *values[2];
1505
1506         values[0] = val;
1507         values[1] = NULL;
1508
1509         if (!val)
1510                 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1511         return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE, name, values);
1512 }
1513
1514 /**
1515  * Add an array of string values to a mod list
1516  * @param ctx An initialized TALLOC_CTX
1517  * @param mods An initialized ADS_MODLIST
1518  * @param name The attribute name to add
1519  * @param vals The array of string values to add - NULL means DELETE
1520  * @return ADS STATUS indicating success of add
1521  **/
1522 ADS_STATUS ads_mod_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1523                            const char *name, const char **vals)
1524 {
1525         if (!vals)
1526                 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1527         return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE, 
1528                                name, (const void **) vals);
1529 }
1530
1531 #if 0
1532 /**
1533  * Add a single ber-encoded value to a mod list
1534  * @param ctx An initialized TALLOC_CTX
1535  * @param mods An initialized ADS_MODLIST
1536  * @param name The attribute name to add
1537  * @param val The value to add - NULL means DELETE
1538  * @return ADS STATUS indicating success of add
1539  **/
1540 static ADS_STATUS ads_mod_ber(TALLOC_CTX *ctx, ADS_MODLIST *mods, 
1541                               const char *name, const struct berval *val)
1542 {
1543         const struct berval *values[2];
1544
1545         values[0] = val;
1546         values[1] = NULL;
1547         if (!val)
1548                 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1549         return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE|LDAP_MOD_BVALUES,
1550                                name, (const void **) values);
1551 }
1552 #endif
1553
1554 /**
1555  * Perform an ldap modify
1556  * @param ads connection to ads server
1557  * @param mod_dn DistinguishedName to modify
1558  * @param mods list of modifications to perform
1559  * @return status of modify
1560  **/
1561 ADS_STATUS ads_gen_mod(ADS_STRUCT *ads, const char *mod_dn, ADS_MODLIST mods)
1562 {
1563         int ret,i;
1564         char *utf8_dn = NULL;
1565         size_t converted_size;
1566         /* 
1567            this control is needed to modify that contains a currently 
1568            non-existent attribute (but allowable for the object) to run
1569         */
1570         LDAPControl PermitModify = {
1571                 discard_const_p(char, ADS_PERMIT_MODIFY_OID),
1572                 {0, NULL},
1573                 (char) 1};
1574         LDAPControl *controls[2];
1575
1576         controls[0] = &PermitModify;
1577         controls[1] = NULL;
1578
1579         if (!push_utf8_talloc(talloc_tos(), &utf8_dn, mod_dn, &converted_size)) {
1580                 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1581         }
1582
1583         /* find the end of the list, marked by NULL or -1 */
1584         for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1585         /* make sure the end of the list is NULL */
1586         mods[i] = NULL;
1587         ret = ldap_modify_ext_s(ads->ldap.ld, utf8_dn,
1588                                 (LDAPMod **) mods, controls, NULL);
1589         TALLOC_FREE(utf8_dn);
1590         return ADS_ERROR(ret);
1591 }
1592
1593 /**
1594  * Perform an ldap add
1595  * @param ads connection to ads server
1596  * @param new_dn DistinguishedName to add
1597  * @param mods list of attributes and values for DN
1598  * @return status of add
1599  **/
1600 ADS_STATUS ads_gen_add(ADS_STRUCT *ads, const char *new_dn, ADS_MODLIST mods)
1601 {
1602         int ret, i;
1603         char *utf8_dn = NULL;
1604         size_t converted_size;
1605
1606         if (!push_utf8_talloc(talloc_tos(), &utf8_dn, new_dn, &converted_size)) {
1607                 DEBUG(1, ("ads_gen_add: push_utf8_talloc failed!"));
1608                 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1609         }
1610
1611         /* find the end of the list, marked by NULL or -1 */
1612         for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1613         /* make sure the end of the list is NULL */
1614         mods[i] = NULL;
1615
1616         ret = ldap_add_s(ads->ldap.ld, utf8_dn, (LDAPMod**)mods);
1617         TALLOC_FREE(utf8_dn);
1618         return ADS_ERROR(ret);
1619 }
1620
1621 /**
1622  * Delete a DistinguishedName
1623  * @param ads connection to ads server
1624  * @param new_dn DistinguishedName to delete
1625  * @return status of delete
1626  **/
1627 ADS_STATUS ads_del_dn(ADS_STRUCT *ads, char *del_dn)
1628 {
1629         int ret;
1630         char *utf8_dn = NULL;
1631         size_t converted_size;
1632         if (!push_utf8_talloc(talloc_tos(), &utf8_dn, del_dn, &converted_size)) {
1633                 DEBUG(1, ("ads_del_dn: push_utf8_talloc failed!"));
1634                 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1635         }
1636
1637         ret = ldap_delete_s(ads->ldap.ld, utf8_dn);
1638         TALLOC_FREE(utf8_dn);
1639         return ADS_ERROR(ret);
1640 }
1641
1642 /**
1643  * Build an org unit string
1644  *  if org unit is Computers or blank then assume a container, otherwise
1645  *  assume a / separated list of organisational units.
1646  * jmcd: '\' is now used for escapes so certain chars can be in the ou (e.g. #)
1647  * @param ads connection to ads server
1648  * @param org_unit Organizational unit
1649  * @return org unit string - caller must free
1650  **/
1651 char *ads_ou_string(ADS_STRUCT *ads, const char *org_unit)
1652 {
1653         char *ret = NULL;
1654
1655         if (!org_unit || !*org_unit) {
1656
1657                 ret = ads_default_ou_string(ads, DS_GUID_COMPUTERS_CONTAINER);
1658
1659                 /* samba4 might not yet respond to a wellknownobject-query */
1660                 return ret ? ret : SMB_STRDUP("cn=Computers");
1661         }
1662
1663         if (strequal(org_unit, "Computers")) {
1664                 return SMB_STRDUP("cn=Computers");
1665         }
1666
1667         /* jmcd: removed "\\" from the separation chars, because it is
1668            needed as an escape for chars like '#' which are valid in an
1669            OU name */
1670         return ads_build_path(org_unit, "/", "ou=", 1);
1671 }
1672
1673 /**
1674  * Get a org unit string for a well-known GUID
1675  * @param ads connection to ads server
1676  * @param wknguid Well known GUID
1677  * @return org unit string - caller must free
1678  **/
1679 char *ads_default_ou_string(ADS_STRUCT *ads, const char *wknguid)
1680 {
1681         ADS_STATUS status;
1682         LDAPMessage *res = NULL;
1683         char *base, *wkn_dn = NULL, *ret = NULL, **wkn_dn_exp = NULL,
1684                 **bind_dn_exp = NULL;
1685         const char *attrs[] = {"distinguishedName", NULL};
1686         int new_ln, wkn_ln, bind_ln, i;
1687
1688         if (wknguid == NULL) {
1689                 return NULL;
1690         }
1691
1692         if (asprintf(&base, "<WKGUID=%s,%s>", wknguid, ads->config.bind_path ) == -1) {
1693                 DEBUG(1, ("asprintf failed!\n"));
1694                 return NULL;
1695         }
1696
1697         status = ads_search_dn(ads, &res, base, attrs);
1698         if (!ADS_ERR_OK(status)) {
1699                 DEBUG(1,("Failed while searching for: %s\n", base));
1700                 goto out;
1701         }
1702
1703         if (ads_count_replies(ads, res) != 1) {
1704                 goto out;
1705         }
1706
1707         /* substitute the bind-path from the well-known-guid-search result */
1708         wkn_dn = ads_get_dn(ads, talloc_tos(), res);
1709         if (!wkn_dn) {
1710                 goto out;
1711         }
1712
1713         wkn_dn_exp = ldap_explode_dn(wkn_dn, 0);
1714         if (!wkn_dn_exp) {
1715                 goto out;
1716         }
1717
1718         bind_dn_exp = ldap_explode_dn(ads->config.bind_path, 0);
1719         if (!bind_dn_exp) {
1720                 goto out;
1721         }
1722
1723         for (wkn_ln=0; wkn_dn_exp[wkn_ln]; wkn_ln++)
1724                 ;
1725         for (bind_ln=0; bind_dn_exp[bind_ln]; bind_ln++)
1726                 ;
1727
1728         new_ln = wkn_ln - bind_ln;
1729
1730         ret = SMB_STRDUP(wkn_dn_exp[0]);
1731         if (!ret) {
1732                 goto out;
1733         }
1734
1735         for (i=1; i < new_ln; i++) {
1736                 char *s = NULL;
1737
1738                 if (asprintf(&s, "%s,%s", ret, wkn_dn_exp[i]) == -1) {
1739                         SAFE_FREE(ret);
1740                         goto out;
1741                 }
1742
1743                 SAFE_FREE(ret);
1744                 ret = SMB_STRDUP(s);
1745                 free(s);
1746                 if (!ret) {
1747                         goto out;
1748                 }
1749         }
1750
1751  out:
1752         SAFE_FREE(base);
1753         ads_msgfree(ads, res);
1754         TALLOC_FREE(wkn_dn);
1755         if (wkn_dn_exp) {
1756                 ldap_value_free(wkn_dn_exp);
1757         }
1758         if (bind_dn_exp) {
1759                 ldap_value_free(bind_dn_exp);
1760         }
1761
1762         return ret;
1763 }
1764
1765 /**
1766  * Adds (appends) an item to an attribute array, rather then
1767  * replacing the whole list
1768  * @param ctx An initialized TALLOC_CTX
1769  * @param mods An initialized ADS_MODLIST
1770  * @param name name of the ldap attribute to append to
1771  * @param vals an array of values to add
1772  * @return status of addition
1773  **/
1774
1775 ADS_STATUS ads_add_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1776                                 const char *name, const char **vals)
1777 {
1778         return ads_modlist_add(ctx, mods, LDAP_MOD_ADD, name,
1779                                (const void *) vals);
1780 }
1781
1782 /**
1783  * Determines the an account's current KVNO via an LDAP lookup
1784  * @param ads An initialized ADS_STRUCT
1785  * @param account_name the NT samaccountname.
1786  * @return the kvno for the account, or -1 in case of a failure.
1787  **/
1788
1789 uint32 ads_get_kvno(ADS_STRUCT *ads, const char *account_name)
1790 {
1791         LDAPMessage *res = NULL;
1792         uint32 kvno = (uint32)-1;      /* -1 indicates a failure */
1793         char *filter;
1794         const char *attrs[] = {"msDS-KeyVersionNumber", NULL};
1795         char *dn_string = NULL;
1796         ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS);
1797
1798         DEBUG(5,("ads_get_kvno: Searching for account %s\n", account_name));
1799         if (asprintf(&filter, "(samAccountName=%s)", account_name) == -1) {
1800                 return kvno;
1801         }
1802         ret = ads_search(ads, &res, filter, attrs);
1803         SAFE_FREE(filter);
1804         if (!ADS_ERR_OK(ret) || (ads_count_replies(ads, res) != 1)) {
1805                 DEBUG(1,("ads_get_kvno: Account for %s not found.\n", account_name));
1806                 ads_msgfree(ads, res);
1807                 return kvno;
1808         }
1809
1810         dn_string = ads_get_dn(ads, talloc_tos(), res);
1811         if (!dn_string) {
1812                 DEBUG(0,("ads_get_kvno: out of memory.\n"));
1813                 ads_msgfree(ads, res);
1814                 return kvno;
1815         }
1816         DEBUG(5,("ads_get_kvno: Using: %s\n", dn_string));
1817         TALLOC_FREE(dn_string);
1818
1819         /* ---------------------------------------------------------
1820          * 0 is returned as a default KVNO from this point on...
1821          * This is done because Windows 2000 does not support key
1822          * version numbers.  Chances are that a failure in the next
1823          * step is simply due to Windows 2000 being used for a
1824          * domain controller. */
1825         kvno = 0;
1826
1827         if (!ads_pull_uint32(ads, res, "msDS-KeyVersionNumber", &kvno)) {
1828                 DEBUG(3,("ads_get_kvno: Error Determining KVNO!\n"));
1829                 DEBUG(3,("ads_get_kvno: Windows 2000 does not support KVNO's, so this may be normal.\n"));
1830                 ads_msgfree(ads, res);
1831                 return kvno;
1832         }
1833
1834         /* Success */
1835         DEBUG(5,("ads_get_kvno: Looked Up KVNO of: %d\n", kvno));
1836         ads_msgfree(ads, res);
1837         return kvno;
1838 }
1839
1840 /**
1841  * Determines the computer account's current KVNO via an LDAP lookup
1842  * @param ads An initialized ADS_STRUCT
1843  * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1844  * @return the kvno for the computer account, or -1 in case of a failure.
1845  **/
1846
1847 uint32_t ads_get_machine_kvno(ADS_STRUCT *ads, const char *machine_name)
1848 {
1849         char *computer_account = NULL;
1850         uint32_t kvno = -1;
1851
1852         if (asprintf(&computer_account, "%s$", machine_name) < 0) {
1853                 return kvno;
1854         }
1855
1856         kvno = ads_get_kvno(ads, computer_account);
1857         free(computer_account);
1858
1859         return kvno;
1860 }
1861
1862 /**
1863  * This clears out all registered spn's for a given hostname
1864  * @param ads An initilaized ADS_STRUCT
1865  * @param machine_name the NetBIOS name of the computer.
1866  * @return 0 upon success, non-zero otherwise.
1867  **/
1868
1869 ADS_STATUS ads_clear_service_principal_names(ADS_STRUCT *ads, const char *machine_name)
1870 {
1871         TALLOC_CTX *ctx;
1872         LDAPMessage *res = NULL;
1873         ADS_MODLIST mods;
1874         const char *servicePrincipalName[1] = {NULL};
1875         ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS);
1876         char *dn_string = NULL;
1877
1878         ret = ads_find_machine_acct(ads, &res, machine_name);
1879         if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
1880                 DEBUG(5,("ads_clear_service_principal_names: WARNING: Host Account for %s not found... skipping operation.\n", machine_name));
1881                 DEBUG(5,("ads_clear_service_principal_names: WARNING: Service Principals for %s have NOT been cleared.\n", machine_name));
1882                 ads_msgfree(ads, res);
1883                 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1884         }
1885
1886         DEBUG(5,("ads_clear_service_principal_names: Host account for %s found\n", machine_name));
1887         ctx = talloc_init("ads_clear_service_principal_names");
1888         if (!ctx) {
1889                 ads_msgfree(ads, res);
1890                 return ADS_ERROR(LDAP_NO_MEMORY);
1891         }
1892
1893         if (!(mods = ads_init_mods(ctx))) {
1894                 talloc_destroy(ctx);
1895                 ads_msgfree(ads, res);
1896                 return ADS_ERROR(LDAP_NO_MEMORY);
1897         }
1898         ret = ads_mod_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1899         if (!ADS_ERR_OK(ret)) {
1900                 DEBUG(1,("ads_clear_service_principal_names: Error creating strlist.\n"));
1901                 ads_msgfree(ads, res);
1902                 talloc_destroy(ctx);
1903                 return ret;
1904         }
1905         dn_string = ads_get_dn(ads, talloc_tos(), res);
1906         if (!dn_string) {
1907                 talloc_destroy(ctx);
1908                 ads_msgfree(ads, res);
1909                 return ADS_ERROR(LDAP_NO_MEMORY);
1910         }
1911         ret = ads_gen_mod(ads, dn_string, mods);
1912         TALLOC_FREE(dn_string);
1913         if (!ADS_ERR_OK(ret)) {
1914                 DEBUG(1,("ads_clear_service_principal_names: Error: Updating Service Principals for machine %s in LDAP\n",
1915                         machine_name));
1916                 ads_msgfree(ads, res);
1917                 talloc_destroy(ctx);
1918                 return ret;
1919         }
1920
1921         ads_msgfree(ads, res);
1922         talloc_destroy(ctx);
1923         return ret;
1924 }
1925
1926 /**
1927  * This adds a service principal name to an existing computer account
1928  * (found by hostname) in AD.
1929  * @param ads An initialized ADS_STRUCT
1930  * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1931  * @param my_fqdn The fully qualified DNS name of the machine
1932  * @param spn A string of the service principal to add, i.e. 'host'
1933  * @return 0 upon sucess, or non-zero if a failure occurs
1934  **/
1935
1936 ADS_STATUS ads_add_service_principal_name(ADS_STRUCT *ads, const char *machine_name, 
1937                                           const char *my_fqdn, const char *spn)
1938 {
1939         ADS_STATUS ret;
1940         TALLOC_CTX *ctx;
1941         LDAPMessage *res = NULL;
1942         char *psp1, *psp2;
1943         ADS_MODLIST mods;
1944         char *dn_string = NULL;
1945         const char *servicePrincipalName[3] = {NULL, NULL, NULL};
1946
1947         ret = ads_find_machine_acct(ads, &res, machine_name);
1948         if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
1949                 DEBUG(1,("ads_add_service_principal_name: WARNING: Host Account for %s not found... skipping operation.\n",
1950                         machine_name));
1951                 DEBUG(1,("ads_add_service_principal_name: WARNING: Service Principal '%s/%s@%s' has NOT been added.\n",
1952                         spn, machine_name, ads->config.realm));
1953                 ads_msgfree(ads, res);
1954                 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1955         }
1956
1957         DEBUG(1,("ads_add_service_principal_name: Host account for %s found\n", machine_name));
1958         if (!(ctx = talloc_init("ads_add_service_principal_name"))) {
1959                 ads_msgfree(ads, res);
1960                 return ADS_ERROR(LDAP_NO_MEMORY);
1961         }
1962
1963         /* add short name spn */
1964
1965         if ( (psp1 = talloc_asprintf(ctx, "%s/%s", spn, machine_name)) == NULL ) {
1966                 talloc_destroy(ctx);
1967                 ads_msgfree(ads, res);
1968                 return ADS_ERROR(LDAP_NO_MEMORY);
1969         }
1970         if (!strlower_m(&psp1[strlen(spn) + 1])) {
1971                 ret = ADS_ERROR(LDAP_NO_MEMORY);
1972                 goto out;
1973         }
1974         servicePrincipalName[0] = psp1;
1975
1976         DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n", 
1977                 psp1, machine_name));
1978
1979
1980         /* add fully qualified spn */
1981
1982         if ( (psp2 = talloc_asprintf(ctx, "%s/%s", spn, my_fqdn)) == NULL ) {
1983                 ret = ADS_ERROR(LDAP_NO_MEMORY);
1984                 goto out;
1985         }
1986         if (!strlower_m(&psp2[strlen(spn) + 1])) {
1987                 ret = ADS_ERROR(LDAP_NO_MEMORY);
1988                 goto out;
1989         }
1990         servicePrincipalName[1] = psp2;
1991
1992         DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n", 
1993                 psp2, machine_name));
1994
1995         if ( (mods = ads_init_mods(ctx)) == NULL ) {
1996                 ret = ADS_ERROR(LDAP_NO_MEMORY);
1997                 goto out;
1998         }
1999
2000         ret = ads_add_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
2001         if (!ADS_ERR_OK(ret)) {
2002                 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
2003                 goto out;
2004         }
2005
2006         if ( (dn_string = ads_get_dn(ads, ctx, res)) == NULL ) {
2007                 ret = ADS_ERROR(LDAP_NO_MEMORY);
2008                 goto out;
2009         }
2010
2011         ret = ads_gen_mod(ads, dn_string, mods);
2012         if (!ADS_ERR_OK(ret)) {
2013                 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
2014                 goto out;
2015         }
2016
2017  out:
2018         TALLOC_FREE( ctx );
2019         ads_msgfree(ads, res);
2020         return ret;
2021 }
2022
2023 /**
2024  * adds a machine account to the ADS server
2025  * @param ads An intialized ADS_STRUCT
2026  * @param machine_name - the NetBIOS machine name of this account.
2027  * @param account_type A number indicating the type of account to create
2028  * @param org_unit The LDAP path in which to place this account
2029  * @return 0 upon success, or non-zero otherwise
2030 **/
2031
2032 ADS_STATUS ads_create_machine_acct(ADS_STRUCT *ads, const char *machine_name, 
2033                                    const char *org_unit)
2034 {
2035         ADS_STATUS ret;
2036         char *samAccountName, *controlstr;
2037         TALLOC_CTX *ctx;
2038         ADS_MODLIST mods;
2039         char *machine_escaped = NULL;
2040         char *new_dn;
2041         const char *objectClass[] = {"top", "person", "organizationalPerson",
2042                                      "user", "computer", NULL};
2043         LDAPMessage *res = NULL;
2044         uint32 acct_control = ( UF_WORKSTATION_TRUST_ACCOUNT |\
2045                                 UF_DONT_EXPIRE_PASSWD |\
2046                                 UF_ACCOUNTDISABLE );
2047
2048         if (!(ctx = talloc_init("ads_add_machine_acct")))
2049                 return ADS_ERROR(LDAP_NO_MEMORY);
2050
2051         ret = ADS_ERROR(LDAP_NO_MEMORY);
2052
2053         machine_escaped = escape_rdn_val_string_alloc(machine_name);
2054         if (!machine_escaped) {
2055                 goto done;
2056         }
2057
2058         new_dn = talloc_asprintf(ctx, "cn=%s,%s", machine_escaped, org_unit);
2059         samAccountName = talloc_asprintf(ctx, "%s$", machine_name);
2060
2061         if ( !new_dn || !samAccountName ) {
2062                 goto done;
2063         }
2064
2065 #ifndef ENCTYPE_ARCFOUR_HMAC
2066         acct_control |= UF_USE_DES_KEY_ONLY;
2067 #endif
2068
2069         if (!(controlstr = talloc_asprintf(ctx, "%u", acct_control))) {
2070                 goto done;
2071         }
2072
2073         if (!(mods = ads_init_mods(ctx))) {
2074                 goto done;
2075         }
2076
2077         ads_mod_str(ctx, &mods, "cn", machine_name);
2078         ads_mod_str(ctx, &mods, "sAMAccountName", samAccountName);
2079         ads_mod_strlist(ctx, &mods, "objectClass", objectClass);
2080         ads_mod_str(ctx, &mods, "userAccountControl", controlstr);
2081
2082         ret = ads_gen_add(ads, new_dn, mods);
2083
2084 done:
2085         SAFE_FREE(machine_escaped);
2086         ads_msgfree(ads, res);
2087         talloc_destroy(ctx);
2088
2089         return ret;
2090 }
2091
2092 /**
2093  * move a machine account to another OU on the ADS server
2094  * @param ads - An intialized ADS_STRUCT
2095  * @param machine_name - the NetBIOS machine name of this account.
2096  * @param org_unit - The LDAP path in which to place this account
2097  * @param moved - whether we moved the machine account (optional)
2098  * @return 0 upon success, or non-zero otherwise
2099 **/
2100
2101 ADS_STATUS ads_move_machine_acct(ADS_STRUCT *ads, const char *machine_name, 
2102                                  const char *org_unit, bool *moved)
2103 {
2104         ADS_STATUS rc;
2105         int ldap_status;
2106         LDAPMessage *res = NULL;
2107         char *filter = NULL;
2108         char *computer_dn = NULL;
2109         char *parent_dn;
2110         char *computer_rdn = NULL;
2111         bool need_move = False;
2112
2113         if (asprintf(&filter, "(samAccountName=%s$)", machine_name) == -1) {
2114                 rc = ADS_ERROR(LDAP_NO_MEMORY);
2115                 goto done;
2116         }
2117
2118         /* Find pre-existing machine */
2119         rc = ads_search(ads, &res, filter, NULL);
2120         if (!ADS_ERR_OK(rc)) {
2121                 goto done;
2122         }
2123
2124         computer_dn = ads_get_dn(ads, talloc_tos(), res);
2125         if (!computer_dn) {
2126                 rc = ADS_ERROR(LDAP_NO_MEMORY);
2127                 goto done;
2128         }
2129
2130         parent_dn = ads_parent_dn(computer_dn);
2131         if (strequal(parent_dn, org_unit)) {
2132                 goto done;
2133         }
2134
2135         need_move = True;
2136
2137         if (asprintf(&computer_rdn, "CN=%s", machine_name) == -1) {
2138                 rc = ADS_ERROR(LDAP_NO_MEMORY);
2139                 goto done;
2140         }
2141
2142         ldap_status = ldap_rename_s(ads->ldap.ld, computer_dn, computer_rdn, 
2143                                     org_unit, 1, NULL, NULL);
2144         rc = ADS_ERROR(ldap_status);
2145
2146 done:
2147         ads_msgfree(ads, res);
2148         SAFE_FREE(filter);
2149         TALLOC_FREE(computer_dn);
2150         SAFE_FREE(computer_rdn);
2151
2152         if (!ADS_ERR_OK(rc)) {
2153                 need_move = False;
2154         }
2155
2156         if (moved) {
2157                 *moved = need_move;
2158         }
2159
2160         return rc;
2161 }
2162
2163 /*
2164   dump a binary result from ldap
2165 */
2166 static void dump_binary(ADS_STRUCT *ads, const char *field, struct berval **values)
2167 {
2168         int i, j;
2169         for (i=0; values[i]; i++) {
2170                 printf("%s: ", field);
2171                 for (j=0; j<values[i]->bv_len; j++) {
2172                         printf("%02X", (unsigned char)values[i]->bv_val[j]);
2173                 }
2174                 printf("\n");
2175         }
2176 }
2177
2178 static void dump_guid(ADS_STRUCT *ads, const char *field, struct berval **values)
2179 {
2180         int i;
2181         for (i=0; values[i]; i++) {
2182                 NTSTATUS status;
2183                 DATA_BLOB in = data_blob_const(values[i]->bv_val, values[i]->bv_len);
2184                 struct GUID guid;
2185
2186                 status = GUID_from_ndr_blob(&in, &guid);
2187                 if (NT_STATUS_IS_OK(status)) {
2188                         printf("%s: %s\n", field, GUID_string(talloc_tos(), &guid));
2189                 } else {
2190                         printf("%s: INVALID GUID\n", field);
2191                 }
2192         }
2193 }
2194
2195 /*
2196   dump a sid result from ldap
2197 */
2198 static void dump_sid(ADS_STRUCT *ads, const char *field, struct berval **values)
2199 {
2200         int i;
2201         for (i=0; values[i]; i++) {
2202                 struct dom_sid sid;
2203                 fstring tmp;
2204                 if (!sid_parse(values[i]->bv_val, values[i]->bv_len, &sid)) {
2205                         return;
2206                 }
2207                 printf("%s: %s\n", field, sid_to_fstring(tmp, &sid));
2208         }
2209 }
2210
2211 /*
2212   dump ntSecurityDescriptor
2213 */
2214 static void dump_sd(ADS_STRUCT *ads, const char *filed, struct berval **values)
2215 {
2216         TALLOC_CTX *frame = talloc_stackframe();
2217         struct security_descriptor *psd;
2218         NTSTATUS status;
2219
2220         status = unmarshall_sec_desc(talloc_tos(), (uint8 *)values[0]->bv_val,
2221                                      values[0]->bv_len, &psd);
2222         if (!NT_STATUS_IS_OK(status)) {
2223                 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
2224                           nt_errstr(status)));
2225                 TALLOC_FREE(frame);
2226                 return;
2227         }
2228
2229         if (psd) {
2230                 ads_disp_sd(ads, talloc_tos(), psd);
2231         }
2232
2233         TALLOC_FREE(frame);
2234 }
2235
2236 /*
2237   dump a string result from ldap
2238 */
2239 static void dump_string(const char *field, char **values)
2240 {
2241         int i;
2242         for (i=0; values[i]; i++) {
2243                 printf("%s: %s\n", field, values[i]);
2244         }
2245 }
2246
2247 /*
2248   dump a field from LDAP on stdout
2249   used for debugging
2250 */
2251
2252 static bool ads_dump_field(ADS_STRUCT *ads, char *field, void **values, void *data_area)
2253 {
2254         const struct {
2255                 const char *name;
2256                 bool string;
2257                 void (*handler)(ADS_STRUCT *, const char *, struct berval **);
2258         } handlers[] = {
2259                 {"objectGUID", False, dump_guid},
2260                 {"netbootGUID", False, dump_guid},
2261                 {"nTSecurityDescriptor", False, dump_sd},
2262                 {"dnsRecord", False, dump_binary},
2263                 {"objectSid", False, dump_sid},
2264                 {"tokenGroups", False, dump_sid},
2265                 {"tokenGroupsNoGCAcceptable", False, dump_sid},
2266                 {"tokengroupsGlobalandUniversal", False, dump_sid},
2267                 {"mS-DS-CreatorSID", False, dump_sid},
2268                 {"msExchMailboxGuid", False, dump_guid},
2269                 {NULL, True, NULL}
2270         };
2271         int i;
2272
2273         if (!field) { /* must be end of an entry */
2274                 printf("\n");
2275                 return False;
2276         }
2277
2278         for (i=0; handlers[i].name; i++) {
2279                 if (strcasecmp_m(handlers[i].name, field) == 0) {
2280                         if (!values) /* first time, indicate string or not */
2281                                 return handlers[i].string;
2282                         handlers[i].handler(ads, field, (struct berval **) values);
2283                         break;
2284                 }
2285         }
2286         if (!handlers[i].name) {
2287                 if (!values) /* first time, indicate string conversion */
2288                         return True;
2289                 dump_string(field, (char **)values);
2290         }
2291         return False;
2292 }
2293
2294 /**
2295  * Dump a result from LDAP on stdout
2296  *  used for debugging
2297  * @param ads connection to ads server
2298  * @param res Results to dump
2299  **/
2300
2301  void ads_dump(ADS_STRUCT *ads, LDAPMessage *res)
2302 {
2303         ads_process_results(ads, res, ads_dump_field, NULL);
2304 }
2305
2306 /**
2307  * Walk through results, calling a function for each entry found.
2308  *  The function receives a field name, a berval * array of values,
2309  *  and a data area passed through from the start.  The function is
2310  *  called once with null for field and values at the end of each
2311  *  entry.
2312  * @param ads connection to ads server
2313  * @param res Results to process
2314  * @param fn Function for processing each result
2315  * @param data_area user-defined area to pass to function
2316  **/
2317  void ads_process_results(ADS_STRUCT *ads, LDAPMessage *res,
2318                           bool (*fn)(ADS_STRUCT *, char *, void **, void *),
2319                           void *data_area)
2320 {
2321         LDAPMessage *msg;
2322         TALLOC_CTX *ctx;
2323         size_t converted_size;
2324
2325         if (!(ctx = talloc_init("ads_process_results")))
2326                 return;
2327
2328         for (msg = ads_first_entry(ads, res); msg; 
2329              msg = ads_next_entry(ads, msg)) {
2330                 char *utf8_field;
2331                 BerElement *b;
2332
2333                 for (utf8_field=ldap_first_attribute(ads->ldap.ld,
2334                                                      (LDAPMessage *)msg,&b); 
2335                      utf8_field;
2336                      utf8_field=ldap_next_attribute(ads->ldap.ld,
2337                                                     (LDAPMessage *)msg,b)) {
2338                         struct berval **ber_vals;
2339                         char **str_vals, **utf8_vals;
2340                         char *field;
2341                         bool string; 
2342
2343                         if (!pull_utf8_talloc(ctx, &field, utf8_field,
2344                                               &converted_size))
2345                         {
2346                                 DEBUG(0,("ads_process_results: "
2347                                          "pull_utf8_talloc failed: %s",
2348                                          strerror(errno)));
2349                         }
2350
2351                         string = fn(ads, field, NULL, data_area);
2352
2353                         if (string) {
2354                                 utf8_vals = ldap_get_values(ads->ldap.ld,
2355                                                  (LDAPMessage *)msg, field);
2356                                 str_vals = ads_pull_strvals(ctx, 
2357                                                   (const char **) utf8_vals);
2358                                 fn(ads, field, (void **) str_vals, data_area);
2359                                 ldap_value_free(utf8_vals);
2360                         } else {
2361                                 ber_vals = ldap_get_values_len(ads->ldap.ld, 
2362                                                  (LDAPMessage *)msg, field);
2363                                 fn(ads, field, (void **) ber_vals, data_area);
2364
2365                                 ldap_value_free_len(ber_vals);
2366                         }
2367                         ldap_memfree(utf8_field);
2368                 }
2369                 ber_free(b, 0);
2370                 talloc_free_children(ctx);
2371                 fn(ads, NULL, NULL, data_area); /* completed an entry */
2372
2373         }
2374         talloc_destroy(ctx);
2375 }
2376
2377 /**
2378  * count how many replies are in a LDAPMessage
2379  * @param ads connection to ads server
2380  * @param res Results to count
2381  * @return number of replies
2382  **/
2383 int ads_count_replies(ADS_STRUCT *ads, void *res)
2384 {
2385         return ldap_count_entries(ads->ldap.ld, (LDAPMessage *)res);
2386 }
2387
2388 /**
2389  * pull the first entry from a ADS result
2390  * @param ads connection to ads server
2391  * @param res Results of search
2392  * @return first entry from result
2393  **/
2394  LDAPMessage *ads_first_entry(ADS_STRUCT *ads, LDAPMessage *res)
2395 {
2396         return ldap_first_entry(ads->ldap.ld, res);
2397 }
2398
2399 /**
2400  * pull the next entry from a ADS result
2401  * @param ads connection to ads server
2402  * @param res Results of search
2403  * @return next entry from result
2404  **/
2405  LDAPMessage *ads_next_entry(ADS_STRUCT *ads, LDAPMessage *res)
2406 {
2407         return ldap_next_entry(ads->ldap.ld, res);
2408 }
2409
2410 /**
2411  * pull the first message from a ADS result
2412  * @param ads connection to ads server
2413  * @param res Results of search
2414  * @return first message from result
2415  **/
2416  LDAPMessage *ads_first_message(ADS_STRUCT *ads, LDAPMessage *res)
2417 {
2418         return ldap_first_message(ads->ldap.ld, res);
2419 }
2420
2421 /**
2422  * pull the next message from a ADS result
2423  * @param ads connection to ads server
2424  * @param res Results of search
2425  * @return next message from result
2426  **/
2427  LDAPMessage *ads_next_message(ADS_STRUCT *ads, LDAPMessage *res)
2428 {
2429         return ldap_next_message(ads->ldap.ld, res);
2430 }
2431
2432 /**
2433  * pull a single string from a ADS result
2434  * @param ads connection to ads server
2435  * @param mem_ctx TALLOC_CTX to use for allocating result string
2436  * @param msg Results of search
2437  * @param field Attribute to retrieve
2438  * @return Result string in talloc context
2439  **/
2440  char *ads_pull_string(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, LDAPMessage *msg,
2441                        const char *field)
2442 {
2443         char **values;
2444         char *ret = NULL;
2445         char *ux_string;
2446         size_t converted_size;
2447
2448         values = ldap_get_values(ads->ldap.ld, msg, field);
2449         if (!values)
2450                 return NULL;
2451
2452         if (values[0] && pull_utf8_talloc(mem_ctx, &ux_string, values[0],
2453                                           &converted_size))
2454         {
2455                 ret = ux_string;
2456         }
2457         ldap_value_free(values);
2458         return ret;
2459 }
2460
2461 /**
2462  * pull an array of strings from a ADS result
2463  * @param ads connection to ads server
2464  * @param mem_ctx TALLOC_CTX to use for allocating result string
2465  * @param msg Results of search
2466  * @param field Attribute to retrieve
2467  * @return Result strings in talloc context
2468  **/
2469  char **ads_pull_strings(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2470                          LDAPMessage *msg, const char *field,
2471                          size_t *num_values)
2472 {
2473         char **values;
2474         char **ret = NULL;
2475         int i;
2476         size_t converted_size;
2477
2478         values = ldap_get_values(ads->ldap.ld, msg, field);
2479         if (!values)
2480                 return NULL;
2481
2482         *num_values = ldap_count_values(values);
2483
2484         ret = talloc_array(mem_ctx, char *, *num_values + 1);
2485         if (!ret) {
2486                 ldap_value_free(values);
2487                 return NULL;
2488         }
2489
2490         for (i=0;i<*num_values;i++) {
2491                 if (!pull_utf8_talloc(mem_ctx, &ret[i], values[i],
2492                                       &converted_size))
2493                 {
2494                         ldap_value_free(values);
2495                         return NULL;
2496                 }
2497         }
2498         ret[i] = NULL;
2499
2500         ldap_value_free(values);
2501         return ret;
2502 }
2503
2504 /**
2505  * pull an array of strings from a ADS result 
2506  *  (handle large multivalue attributes with range retrieval)
2507  * @param ads connection to ads server
2508  * @param mem_ctx TALLOC_CTX to use for allocating result string
2509  * @param msg Results of search
2510  * @param field Attribute to retrieve
2511  * @param current_strings strings returned by a previous call to this function
2512  * @param next_attribute The next query should ask for this attribute
2513  * @param num_values How many values did we get this time?
2514  * @param more_values Are there more values to get?
2515  * @return Result strings in talloc context
2516  **/
2517  char **ads_pull_strings_range(ADS_STRUCT *ads, 
2518                                TALLOC_CTX *mem_ctx,
2519                                LDAPMessage *msg, const char *field,
2520                                char **current_strings,
2521                                const char **next_attribute,
2522                                size_t *num_strings,
2523                                bool *more_strings)
2524 {
2525         char *attr;
2526         char *expected_range_attrib, *range_attr;
2527         BerElement *ptr = NULL;
2528         char **strings;
2529         char **new_strings;
2530         size_t num_new_strings;
2531         unsigned long int range_start;
2532         unsigned long int range_end;
2533
2534         /* we might have been given the whole lot anyway */
2535         if ((strings = ads_pull_strings(ads, mem_ctx, msg, field, num_strings))) {
2536                 *more_strings = False;
2537                 return strings;
2538         }
2539
2540         expected_range_attrib = talloc_asprintf(mem_ctx, "%s;Range=", field);
2541
2542         /* look for Range result */
2543         for (attr = ldap_first_attribute(ads->ldap.ld, (LDAPMessage *)msg, &ptr); 
2544              attr; 
2545              attr = ldap_next_attribute(ads->ldap.ld, (LDAPMessage *)msg, ptr)) {
2546                 /* we ignore the fact that this is utf8, as all attributes are ascii... */
2547                 if (strnequal(attr, expected_range_attrib, strlen(expected_range_attrib))) {
2548                         range_attr = attr;
2549                         break;
2550                 }
2551                 ldap_memfree(attr);
2552         }
2553         if (!attr) {
2554                 ber_free(ptr, 0);
2555                 /* nothing here - this field is just empty */
2556                 *more_strings = False;
2557                 return NULL;
2558         }
2559
2560         if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-%lu", 
2561                    &range_start, &range_end) == 2) {
2562                 *more_strings = True;
2563         } else {
2564                 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-*", 
2565                            &range_start) == 1) {
2566                         *more_strings = False;
2567                 } else {
2568                         DEBUG(1, ("ads_pull_strings_range:  Cannot parse Range attriubte (%s)\n", 
2569                                   range_attr));
2570                         ldap_memfree(range_attr);
2571                         *more_strings = False;
2572                         return NULL;
2573                 }
2574         }
2575
2576         if ((*num_strings) != range_start) {
2577                 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) doesn't start at %u, but at %lu"
2578                           " - aborting range retreival\n",
2579                           range_attr, (unsigned int)(*num_strings) + 1, range_start));
2580                 ldap_memfree(range_attr);
2581                 *more_strings = False;
2582                 return NULL;
2583         }
2584
2585         new_strings = ads_pull_strings(ads, mem_ctx, msg, range_attr, &num_new_strings);
2586
2587         if (*more_strings && ((*num_strings + num_new_strings) != (range_end + 1))) {
2588                 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) tells us we have %lu "
2589                           "strings in this bunch, but we only got %lu - aborting range retreival\n",
2590                           range_attr, (unsigned long int)range_end - range_start + 1, 
2591                           (unsigned long int)num_new_strings));
2592                 ldap_memfree(range_attr);
2593                 *more_strings = False;
2594                 return NULL;
2595         }
2596
2597         strings = talloc_realloc(mem_ctx, current_strings, char *,
2598                                  *num_strings + num_new_strings);
2599
2600         if (strings == NULL) {
2601                 ldap_memfree(range_attr);
2602                 *more_strings = False;
2603                 return NULL;
2604         }
2605
2606         if (new_strings && num_new_strings) {
2607                 memcpy(&strings[*num_strings], new_strings,
2608                        sizeof(*new_strings) * num_new_strings);
2609         }
2610
2611         (*num_strings) += num_new_strings;
2612
2613         if (*more_strings) {
2614                 *next_attribute = talloc_asprintf(mem_ctx,
2615                                                   "%s;range=%d-*", 
2616                                                   field,
2617                                                   (int)*num_strings);
2618
2619                 if (!*next_attribute) {
2620                         DEBUG(1, ("talloc_asprintf for next attribute failed!\n"));
2621                         ldap_memfree(range_attr);
2622                         *more_strings = False;
2623                         return NULL;
2624                 }
2625         }
2626
2627         ldap_memfree(range_attr);
2628
2629         return strings;
2630 }
2631
2632 /**
2633  * pull a single uint32 from a ADS result
2634  * @param ads connection to ads server
2635  * @param msg Results of search
2636  * @param field Attribute to retrieve
2637  * @param v Pointer to int to store result
2638  * @return boolean inidicating success
2639 */
2640  bool ads_pull_uint32(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
2641                       uint32 *v)
2642 {
2643         char **values;
2644
2645         values = ldap_get_values(ads->ldap.ld, msg, field);
2646         if (!values)
2647                 return False;
2648         if (!values[0]) {
2649                 ldap_value_free(values);
2650                 return False;
2651         }
2652
2653         *v = atoi(values[0]);
2654         ldap_value_free(values);
2655         return True;
2656 }
2657
2658 /**
2659  * pull a single objectGUID from an ADS result
2660  * @param ads connection to ADS server
2661  * @param msg results of search
2662  * @param guid 37-byte area to receive text guid
2663  * @return boolean indicating success
2664  **/
2665  bool ads_pull_guid(ADS_STRUCT *ads, LDAPMessage *msg, struct GUID *guid)
2666 {
2667         DATA_BLOB blob;
2668         NTSTATUS status;
2669
2670         if (!smbldap_talloc_single_blob(talloc_tos(), ads->ldap.ld, msg, "objectGUID",
2671                                         &blob)) {
2672                 return false;
2673         }
2674
2675         status = GUID_from_ndr_blob(&blob, guid);
2676         talloc_free(blob.data);
2677         return NT_STATUS_IS_OK(status);
2678 }
2679
2680
2681 /**
2682  * pull a single struct dom_sid from a ADS result
2683  * @param ads connection to ads server
2684  * @param msg Results of search
2685  * @param field Attribute to retrieve
2686  * @param sid Pointer to sid to store result
2687  * @return boolean inidicating success
2688 */
2689  bool ads_pull_sid(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
2690                    struct dom_sid *sid)
2691 {
2692         return smbldap_pull_sid(ads->ldap.ld, msg, field, sid);
2693 }
2694
2695 /**
2696  * pull an array of struct dom_sids from a ADS result
2697  * @param ads connection to ads server
2698  * @param mem_ctx TALLOC_CTX for allocating sid array
2699  * @param msg Results of search
2700  * @param field Attribute to retrieve
2701  * @param sids pointer to sid array to allocate
2702  * @return the count of SIDs pulled
2703  **/
2704  int ads_pull_sids(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2705                    LDAPMessage *msg, const char *field, struct dom_sid **sids)
2706 {
2707         struct berval **values;
2708         bool ret;
2709         int count, i;
2710
2711         values = ldap_get_values_len(ads->ldap.ld, msg, field);
2712
2713         if (!values)
2714                 return 0;
2715
2716         for (i=0; values[i]; i++)
2717                 /* nop */ ;
2718
2719         if (i) {
2720                 (*sids) = talloc_array(mem_ctx, struct dom_sid, i);
2721                 if (!(*sids)) {
2722                         ldap_value_free_len(values);
2723                         return 0;
2724                 }
2725         } else {
2726                 (*sids) = NULL;
2727         }
2728
2729         count = 0;
2730         for (i=0; values[i]; i++) {
2731                 ret = sid_parse(values[i]->bv_val, values[i]->bv_len, &(*sids)[count]);
2732                 if (ret) {
2733                         DEBUG(10, ("pulling SID: %s\n",
2734                                    sid_string_dbg(&(*sids)[count])));
2735                         count++;
2736                 }
2737         }
2738
2739         ldap_value_free_len(values);
2740         return count;
2741 }
2742
2743 /**
2744  * pull a struct security_descriptor from a ADS result
2745  * @param ads connection to ads server
2746  * @param mem_ctx TALLOC_CTX for allocating sid array
2747  * @param msg Results of search
2748  * @param field Attribute to retrieve
2749  * @param sd Pointer to *struct security_descriptor to store result (talloc()ed)
2750  * @return boolean inidicating success
2751 */
2752  bool ads_pull_sd(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2753                   LDAPMessage *msg, const char *field,
2754                   struct security_descriptor **sd)
2755 {
2756         struct berval **values;
2757         bool ret = true;
2758
2759         values = ldap_get_values_len(ads->ldap.ld, msg, field);
2760
2761         if (!values) return false;
2762
2763         if (values[0]) {
2764                 NTSTATUS status;
2765                 status = unmarshall_sec_desc(mem_ctx,
2766                                              (uint8 *)values[0]->bv_val,
2767                                              values[0]->bv_len, sd);
2768                 if (!NT_STATUS_IS_OK(status)) {
2769                         DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
2770                                   nt_errstr(status)));
2771                         ret = false;
2772                 }
2773         }
2774
2775         ldap_value_free_len(values);
2776         return ret;
2777 }
2778
2779 /* 
2780  * in order to support usernames longer than 21 characters we need to 
2781  * use both the sAMAccountName and the userPrincipalName attributes 
2782  * It seems that not all users have the userPrincipalName attribute set
2783  *
2784  * @param ads connection to ads server
2785  * @param mem_ctx TALLOC_CTX for allocating sid array
2786  * @param msg Results of search
2787  * @return the username
2788  */
2789  char *ads_pull_username(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2790                          LDAPMessage *msg)
2791 {
2792 #if 0   /* JERRY */
2793         char *ret, *p;
2794
2795         /* lookup_name() only works on the sAMAccountName to 
2796            returning the username portion of userPrincipalName
2797            breaks winbindd_getpwnam() */
2798
2799         ret = ads_pull_string(ads, mem_ctx, msg, "userPrincipalName");
2800         if (ret && (p = strchr_m(ret, '@'))) {
2801                 *p = 0;
2802                 return ret;
2803         }
2804 #endif
2805         return ads_pull_string(ads, mem_ctx, msg, "sAMAccountName");
2806 }
2807
2808
2809 /**
2810  * find the update serial number - this is the core of the ldap cache
2811  * @param ads connection to ads server
2812  * @param ads connection to ADS server
2813  * @param usn Pointer to retrieved update serial number
2814  * @return status of search
2815  **/
2816 ADS_STATUS ads_USN(ADS_STRUCT *ads, uint32 *usn)
2817 {
2818         const char *attrs[] = {"highestCommittedUSN", NULL};
2819         ADS_STATUS status;
2820         LDAPMessage *res;
2821
2822         status = ads_do_search_retry(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2823         if (!ADS_ERR_OK(status)) 
2824                 return status;
2825
2826         if (ads_count_replies(ads, res) != 1) {
2827                 ads_msgfree(ads, res);
2828                 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2829         }
2830
2831         if (!ads_pull_uint32(ads, res, "highestCommittedUSN", usn)) {
2832                 ads_msgfree(ads, res);
2833                 return ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
2834         }
2835
2836         ads_msgfree(ads, res);
2837         return ADS_SUCCESS;
2838 }
2839
2840 /* parse a ADS timestring - typical string is
2841    '20020917091222.0Z0' which means 09:12.22 17th September
2842    2002, timezone 0 */
2843 static time_t ads_parse_time(const char *str)
2844 {
2845         struct tm tm;
2846
2847         ZERO_STRUCT(tm);
2848
2849         if (sscanf(str, "%4d%2d%2d%2d%2d%2d", 
2850                    &tm.tm_year, &tm.tm_mon, &tm.tm_mday, 
2851                    &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) {
2852                 return 0;
2853         }
2854         tm.tm_year -= 1900;
2855         tm.tm_mon -= 1;
2856
2857         return timegm(&tm);
2858 }
2859
2860 /********************************************************************
2861 ********************************************************************/
2862
2863 ADS_STATUS ads_current_time(ADS_STRUCT *ads)
2864 {
2865         const char *attrs[] = {"currentTime", NULL};
2866         ADS_STATUS status;
2867         LDAPMessage *res;
2868         char *timestr;
2869         TALLOC_CTX *ctx;
2870         ADS_STRUCT *ads_s = ads;
2871
2872         if (!(ctx = talloc_init("ads_current_time"))) {
2873                 return ADS_ERROR(LDAP_NO_MEMORY);
2874         }
2875
2876         /* establish a new ldap tcp session if necessary */
2877
2878         if ( !ads->ldap.ld ) {
2879                 if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup, 
2880                         ads->server.ldap_server )) == NULL )
2881                 {
2882                         goto done;
2883                 }
2884                 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
2885                 status = ads_connect( ads_s );
2886                 if ( !ADS_ERR_OK(status))
2887                         goto done;
2888         }
2889
2890         status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2891         if (!ADS_ERR_OK(status)) {
2892                 goto done;
2893         }
2894
2895         timestr = ads_pull_string(ads_s, ctx, res, "currentTime");
2896         if (!timestr) {
2897                 ads_msgfree(ads_s, res);
2898                 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2899                 goto done;
2900         }
2901
2902         /* but save the time and offset in the original ADS_STRUCT */   
2903
2904         ads->config.current_time = ads_parse_time(timestr);
2905
2906         if (ads->config.current_time != 0) {
2907                 ads->auth.time_offset = ads->config.current_time - time(NULL);
2908                 DEBUG(4,("KDC time offset is %d seconds\n", ads->auth.time_offset));
2909         }
2910
2911         ads_msgfree(ads, res);
2912
2913         status = ADS_SUCCESS;
2914
2915 done:
2916         /* free any temporary ads connections */
2917         if ( ads_s != ads ) {
2918                 ads_destroy( &ads_s );
2919         }
2920         talloc_destroy(ctx);
2921
2922         return status;
2923 }
2924
2925 /********************************************************************
2926 ********************************************************************/
2927
2928 ADS_STATUS ads_domain_func_level(ADS_STRUCT *ads, uint32 *val)
2929 {
2930         const char *attrs[] = {"domainFunctionality", NULL};
2931         ADS_STATUS status;
2932         LDAPMessage *res;
2933         ADS_STRUCT *ads_s = ads;
2934
2935         *val = DS_DOMAIN_FUNCTION_2000;
2936
2937         /* establish a new ldap tcp session if necessary */
2938
2939         if ( !ads->ldap.ld ) {
2940                 if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup, 
2941                         ads->server.ldap_server )) == NULL )
2942                 {
2943                         status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
2944                         goto done;
2945                 }
2946                 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
2947                 status = ads_connect( ads_s );
2948                 if ( !ADS_ERR_OK(status))
2949                         goto done;
2950         }
2951
2952         /* If the attribute does not exist assume it is a Windows 2000 
2953            functional domain */
2954
2955         status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2956         if (!ADS_ERR_OK(status)) {
2957                 if ( status.err.rc == LDAP_NO_SUCH_ATTRIBUTE ) {
2958                         status = ADS_SUCCESS;
2959                 }
2960                 goto done;
2961         }
2962
2963         if ( !ads_pull_uint32(ads_s, res, "domainFunctionality", val) ) {
2964                 DEBUG(5,("ads_domain_func_level: Failed to pull the domainFunctionality attribute.\n"));
2965         }
2966         DEBUG(3,("ads_domain_func_level: %d\n", *val));
2967
2968
2969         ads_msgfree(ads, res);
2970
2971 done:
2972         /* free any temporary ads connections */
2973         if ( ads_s != ads ) {
2974                 ads_destroy( &ads_s );
2975         }
2976
2977         return status;
2978 }
2979
2980 /**
2981  * find the domain sid for our domain
2982  * @param ads connection to ads server
2983  * @param sid Pointer to domain sid
2984  * @return status of search
2985  **/
2986 ADS_STATUS ads_domain_sid(ADS_STRUCT *ads, struct dom_sid *sid)
2987 {
2988         const char *attrs[] = {"objectSid", NULL};
2989         LDAPMessage *res;
2990         ADS_STATUS rc;
2991
2992         rc = ads_do_search_retry(ads, ads->config.bind_path, LDAP_SCOPE_BASE, "(objectclass=*)", 
2993                            attrs, &res);
2994         if (!ADS_ERR_OK(rc)) return rc;
2995         if (!ads_pull_sid(ads, res, "objectSid", sid)) {
2996                 ads_msgfree(ads, res);
2997                 return ADS_ERROR_SYSTEM(ENOENT);
2998         }
2999         ads_msgfree(ads, res);
3000
3001         return ADS_SUCCESS;
3002 }
3003
3004 /**
3005  * find our site name 
3006  * @param ads connection to ads server
3007  * @param mem_ctx Pointer to talloc context
3008  * @param site_name Pointer to the sitename
3009  * @return status of search
3010  **/
3011 ADS_STATUS ads_site_dn(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char **site_name)
3012 {
3013         ADS_STATUS status;
3014         LDAPMessage *res;
3015         const char *dn, *service_name;
3016         const char *attrs[] = { "dsServiceName", NULL };
3017
3018         status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
3019         if (!ADS_ERR_OK(status)) {
3020                 return status;
3021         }
3022
3023         service_name = ads_pull_string(ads, mem_ctx, res, "dsServiceName");
3024         if (service_name == NULL) {
3025                 ads_msgfree(ads, res);
3026                 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3027         }
3028
3029         ads_msgfree(ads, res);
3030
3031         /* go up three levels */
3032         dn = ads_parent_dn(ads_parent_dn(ads_parent_dn(service_name)));
3033         if (dn == NULL) {
3034                 return ADS_ERROR(LDAP_NO_MEMORY);
3035         }
3036
3037         *site_name = talloc_strdup(mem_ctx, dn);
3038         if (*site_name == NULL) {
3039                 return ADS_ERROR(LDAP_NO_MEMORY);
3040         }
3041
3042         return status;
3043         /*
3044         dsServiceName: CN=NTDS Settings,CN=W2K3DC,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,DC=ber,DC=suse,DC=de
3045         */                                               
3046 }
3047
3048 /**
3049  * find the site dn where a machine resides
3050  * @param ads connection to ads server
3051  * @param mem_ctx Pointer to talloc context
3052  * @param computer_name name of the machine
3053  * @param site_name Pointer to the sitename
3054  * @return status of search
3055  **/
3056 ADS_STATUS ads_site_dn_for_machine(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char *computer_name, const char **site_dn)
3057 {
3058         ADS_STATUS status;
3059         LDAPMessage *res;
3060         const char *parent, *filter;
3061         char *config_context = NULL;
3062         char *dn;
3063
3064         /* shortcut a query */
3065         if (strequal(computer_name, ads->config.ldap_server_name)) {
3066                 return ads_site_dn(ads, mem_ctx, site_dn);
3067         }
3068
3069         status = ads_config_path(ads, mem_ctx, &config_context);
3070         if (!ADS_ERR_OK(status)) {
3071                 return status;
3072         }
3073
3074         filter = talloc_asprintf(mem_ctx, "(cn=%s)", computer_name);
3075         if (filter == NULL) {
3076                 return ADS_ERROR(LDAP_NO_MEMORY);
3077         }
3078
3079         status = ads_do_search(ads, config_context, LDAP_SCOPE_SUBTREE, 
3080                                filter, NULL, &res);
3081         if (!ADS_ERR_OK(status)) {
3082                 return status;
3083         }
3084
3085         if (ads_count_replies(ads, res) != 1) {
3086                 ads_msgfree(ads, res);
3087                 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
3088         }
3089
3090         dn = ads_get_dn(ads, mem_ctx, res);
3091         if (dn == NULL) {
3092                 ads_msgfree(ads, res);
3093                 return ADS_ERROR(LDAP_NO_MEMORY);
3094         }
3095
3096         /* go up three levels */
3097         parent = ads_parent_dn(ads_parent_dn(ads_parent_dn(dn)));
3098         if (parent == NULL) {
3099                 ads_msgfree(ads, res);
3100                 TALLOC_FREE(dn);
3101                 return ADS_ERROR(LDAP_NO_MEMORY);
3102         }
3103
3104         *site_dn = talloc_strdup(mem_ctx, parent);
3105         if (*site_dn == NULL) {
3106                 ads_msgfree(ads, res);
3107                 TALLOC_FREE(dn);
3108                 return ADS_ERROR(LDAP_NO_MEMORY);
3109         }
3110
3111         TALLOC_FREE(dn);
3112         ads_msgfree(ads, res);
3113
3114         return status;
3115 }
3116
3117 /**
3118  * get the upn suffixes for a domain
3119  * @param ads connection to ads server
3120  * @param mem_ctx Pointer to talloc context
3121  * @param suffixes Pointer to an array of suffixes
3122  * @param num_suffixes Pointer to the number of suffixes
3123  * @return status of search
3124  **/
3125 ADS_STATUS ads_upn_suffixes(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, char ***suffixes, size_t *num_suffixes)
3126 {
3127         ADS_STATUS status;
3128         LDAPMessage *res;
3129         const char *base;
3130         char *config_context = NULL;
3131         const char *attrs[] = { "uPNSuffixes", NULL };
3132
3133         status = ads_config_path(ads, mem_ctx, &config_context);
3134         if (!ADS_ERR_OK(status)) {
3135                 return status;
3136         }
3137
3138         base = talloc_asprintf(mem_ctx, "cn=Partitions,%s", config_context);
3139         if (base == NULL) {
3140                 return ADS_ERROR(LDAP_NO_MEMORY);
3141         }
3142
3143         status = ads_search_dn(ads, &res, base, attrs);
3144         if (!ADS_ERR_OK(status)) {
3145                 return status;
3146         }
3147
3148         if (ads_count_replies(ads, res) != 1) {
3149                 ads_msgfree(ads, res);
3150                 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
3151         }
3152
3153         (*suffixes) = ads_pull_strings(ads, mem_ctx, res, "uPNSuffixes", num_suffixes);
3154         if ((*suffixes) == NULL) {
3155                 ads_msgfree(ads, res);
3156                 return ADS_ERROR(LDAP_NO_MEMORY);
3157         }
3158
3159         ads_msgfree(ads, res);
3160
3161         return status;
3162 }
3163
3164 /**
3165  * get the joinable ous for a domain
3166  * @param ads connection to ads server
3167  * @param mem_ctx Pointer to talloc context
3168  * @param ous Pointer to an array of ous
3169  * @param num_ous Pointer to the number of ous
3170  * @return status of search
3171  **/
3172 ADS_STATUS ads_get_joinable_ous(ADS_STRUCT *ads,
3173                                 TALLOC_CTX *mem_ctx,
3174                                 char ***ous,
3175                                 size_t *num_ous)
3176 {
3177         ADS_STATUS status;
3178         LDAPMessage *res = NULL;
3179         LDAPMessage *msg = NULL;
3180         const char *attrs[] = { "dn", NULL };
3181         int count = 0;
3182
3183         status = ads_search(ads, &res,
3184                             "(|(objectClass=domain)(objectclass=organizationalUnit))",
3185                             attrs);
3186         if (!ADS_ERR_OK(status)) {
3187                 return status;
3188         }
3189
3190         count = ads_count_replies(ads, res);
3191         if (count < 1) {
3192                 ads_msgfree(ads, res);
3193                 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3194         }
3195
3196         for (msg = ads_first_entry(ads, res); msg;
3197              msg = ads_next_entry(ads, msg)) {
3198
3199                 char *dn = NULL;
3200
3201                 dn = ads_get_dn(ads, talloc_tos(), msg);
3202                 if (!dn) {
3203                         ads_msgfree(ads, res);
3204                         return ADS_ERROR(LDAP_NO_MEMORY);
3205                 }
3206
3207                 if (!add_string_to_array(mem_ctx, dn,
3208                                          (const char ***)ous,
3209                                          (int *)num_ous)) {
3210                         TALLOC_FREE(dn);
3211                         ads_msgfree(ads, res);
3212                         return ADS_ERROR(LDAP_NO_MEMORY);
3213                 }
3214
3215                 TALLOC_FREE(dn);
3216         }
3217
3218         ads_msgfree(ads, res);
3219
3220         return status;
3221 }
3222
3223
3224 /**
3225  * pull a struct dom_sid from an extended dn string
3226  * @param mem_ctx TALLOC_CTX
3227  * @param extended_dn string
3228  * @param flags string type of extended_dn
3229  * @param sid pointer to a struct dom_sid
3230  * @return NT_STATUS_OK on success,
3231  *         NT_INVALID_PARAMETER on error,
3232  *         NT_STATUS_NOT_FOUND if no SID present
3233  **/
3234 ADS_STATUS ads_get_sid_from_extended_dn(TALLOC_CTX *mem_ctx,
3235                                         const char *extended_dn,
3236                                         enum ads_extended_dn_flags flags,
3237                                         struct dom_sid *sid)
3238 {
3239         char *p, *q, *dn;
3240
3241         if (!extended_dn) {
3242                 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3243         }
3244
3245         /* otherwise extended_dn gets stripped off */
3246         if ((dn = talloc_strdup(mem_ctx, extended_dn)) == NULL) {
3247                 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3248         }
3249         /*
3250          * ADS_EXTENDED_DN_HEX_STRING:
3251          * <GUID=238e1963cb390f4bb032ba0105525a29>;<SID=010500000000000515000000bb68c8fd6b61b427572eb04556040000>;CN=gd,OU=berlin,OU=suse,DC=ber,DC=suse,DC=de
3252          *
3253          * ADS_EXTENDED_DN_STRING (only with w2k3):
3254          * <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
3255          *
3256          * Object with no SID, such as an Exchange Public Folder
3257          * <GUID=28907fb4bdf6854993e7f0a10b504e7c>;CN=public,CN=Microsoft Exchange System Objects,DC=sd2k3ms,DC=west,DC=isilon,DC=com
3258          */
3259
3260         p = strchr(dn, ';');
3261         if (!p) {
3262                 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3263         }
3264
3265         if (strncmp(p, ";<SID=", strlen(";<SID=")) != 0) {
3266                 DEBUG(5,("No SID present in extended dn\n"));
3267                 return ADS_ERROR_NT(NT_STATUS_NOT_FOUND);
3268         }
3269
3270         p += strlen(";<SID=");
3271
3272         q = strchr(p, '>');
3273         if (!q) {
3274                 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3275         }
3276
3277         *q = '\0';
3278
3279         DEBUG(100,("ads_get_sid_from_extended_dn: sid string is %s\n", p));
3280
3281         switch (flags) {
3282
3283         case ADS_EXTENDED_DN_STRING:
3284                 if (!string_to_sid(sid, p)) {
3285                         return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3286                 }
3287                 break;
3288         case ADS_EXTENDED_DN_HEX_STRING: {
3289                 fstring buf;
3290                 size_t buf_len;
3291
3292                 buf_len = strhex_to_str(buf, sizeof(buf), p, strlen(p));
3293                 if (buf_len == 0) {
3294                         return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3295                 }
3296
3297                 if (!sid_parse(buf, buf_len, sid)) {
3298                         DEBUG(10,("failed to parse sid\n"));
3299                         return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3300                 }
3301                 break;
3302                 }
3303         default:
3304                 DEBUG(10,("unknown extended dn format\n"));
3305                 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3306         }
3307
3308         return ADS_ERROR_NT(NT_STATUS_OK);
3309 }
3310
3311 /********************************************************************
3312 ********************************************************************/
3313
3314 char* ads_get_dnshostname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3315 {
3316         LDAPMessage *res = NULL;
3317         ADS_STATUS status;
3318         int count = 0;
3319         char *name = NULL;
3320
3321         status = ads_find_machine_acct(ads, &res, lp_netbios_name());
3322         if (!ADS_ERR_OK(status)) {
3323                 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
3324                         lp_netbios_name()));
3325                 goto out;
3326         }
3327
3328         if ( (count = ads_count_replies(ads, res)) != 1 ) {
3329                 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
3330                 goto out;
3331         }
3332
3333         if ( (name = ads_pull_string(ads, ctx, res, "dNSHostName")) == NULL ) {
3334                 DEBUG(0,("ads_get_dnshostname: No dNSHostName attribute!\n"));
3335         }
3336
3337 out:
3338         ads_msgfree(ads, res);
3339
3340         return name;
3341 }
3342
3343 /********************************************************************
3344 ********************************************************************/
3345
3346 char* ads_get_upn( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3347 {
3348         LDAPMessage *res = NULL;
3349         ADS_STATUS status;
3350         int count = 0;
3351         char *name = NULL;
3352
3353         status = ads_find_machine_acct(ads, &res, machine_name);
3354         if (!ADS_ERR_OK(status)) {
3355                 DEBUG(0,("ads_get_upn: Failed to find account for %s\n",
3356                         lp_netbios_name()));
3357                 goto out;
3358         }
3359
3360         if ( (count = ads_count_replies(ads, res)) != 1 ) {
3361                 DEBUG(1,("ads_get_upn: %d entries returned!\n", count));
3362                 goto out;
3363         }
3364
3365         if ( (name = ads_pull_string(ads, ctx, res, "userPrincipalName")) == NULL ) {
3366                 DEBUG(2,("ads_get_upn: No userPrincipalName attribute!\n"));
3367         }
3368
3369 out:
3370         ads_msgfree(ads, res);
3371
3372         return name;
3373 }
3374
3375 /********************************************************************
3376 ********************************************************************/
3377
3378 char* ads_get_samaccountname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3379 {
3380         LDAPMessage *res = NULL;
3381         ADS_STATUS status;
3382         int count = 0;
3383         char *name = NULL;
3384
3385         status = ads_find_machine_acct(ads, &res, lp_netbios_name());
3386         if (!ADS_ERR_OK(status)) {
3387                 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
3388                         lp_netbios_name()));
3389                 goto out;
3390         }
3391
3392         if ( (count = ads_count_replies(ads, res)) != 1 ) {
3393                 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
3394                 goto out;
3395         }
3396
3397         if ( (name = ads_pull_string(ads, ctx, res, "sAMAccountName")) == NULL ) {
3398                 DEBUG(0,("ads_get_dnshostname: No sAMAccountName attribute!\n"));
3399         }
3400
3401 out:
3402         ads_msgfree(ads, res);
3403
3404         return name;
3405 }
3406
3407 #if 0
3408
3409    SAVED CODE - we used to join via ldap - remember how we did this. JRA.
3410
3411 /**
3412  * Join a machine to a realm
3413  *  Creates the machine account and sets the machine password
3414  * @param ads connection to ads server
3415  * @param machine name of host to add
3416  * @param org_unit Organizational unit to place machine in
3417  * @return status of join
3418  **/
3419 ADS_STATUS ads_join_realm(ADS_STRUCT *ads, const char *machine_name,
3420                         uint32 account_type, const char *org_unit)
3421 {
3422         ADS_STATUS status;
3423         LDAPMessage *res = NULL;
3424         char *machine;
3425
3426         /* machine name must be lowercase */
3427         machine = SMB_STRDUP(machine_name);
3428         strlower_m(machine);
3429
3430         /*
3431         status = ads_find_machine_acct(ads, (void **)&res, machine);
3432         if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
3433                 DEBUG(0, ("Host account for %s already exists - deleting old account\n", machine));
3434                 status = ads_leave_realm(ads, machine);
3435                 if (!ADS_ERR_OK(status)) {
3436                         DEBUG(0, ("Failed to delete host '%s' from the '%s' realm.\n",
3437                                 machine, ads->config.realm));
3438                         return status;
3439                 }
3440         }
3441         */
3442         status = ads_add_machine_acct(ads, machine, account_type, org_unit);
3443         if (!ADS_ERR_OK(status)) {
3444                 DEBUG(0, ("ads_join_realm: ads_add_machine_acct failed (%s): %s\n", machine, ads_errstr(status)));
3445                 SAFE_FREE(machine);
3446                 return status;
3447         }
3448
3449         status = ads_find_machine_acct(ads, (void **)(void *)&res, machine);
3450         if (!ADS_ERR_OK(status)) {
3451                 DEBUG(0, ("ads_join_realm: Host account test failed for machine %s\n", machine));
3452                 SAFE_FREE(machine);
3453                 return status;
3454         }
3455
3456         SAFE_FREE(machine);
3457         ads_msgfree(ads, res);
3458
3459         return status;
3460 }
3461 #endif
3462
3463 /**
3464  * Delete a machine from the realm
3465  * @param ads connection to ads server
3466  * @param hostname Machine to remove
3467  * @return status of delete
3468  **/
3469 ADS_STATUS ads_leave_realm(ADS_STRUCT *ads, const char *hostname)
3470 {
3471         ADS_STATUS status;
3472         void *msg;
3473         LDAPMessage *res;
3474         char *hostnameDN, *host;
3475         int rc;
3476         LDAPControl ldap_control;
3477         LDAPControl  * pldap_control[2] = {NULL, NULL};
3478
3479         pldap_control[0] = &ldap_control;
3480         memset(&ldap_control, 0, sizeof(LDAPControl));
3481         ldap_control.ldctl_oid = discard_const_p(char, LDAP_SERVER_TREE_DELETE_OID);
3482
3483         /* hostname must be lowercase */
3484         host = SMB_STRDUP(hostname);
3485         if (!strlower_m(host)) {
3486                 SAFE_FREE(host);
3487                 return ADS_ERROR_SYSTEM(EINVAL);
3488         }
3489
3490         status = ads_find_machine_acct(ads, &res, host);
3491         if (!ADS_ERR_OK(status)) {
3492                 DEBUG(0, ("Host account for %s does not exist.\n", host));
3493                 SAFE_FREE(host);
3494                 return status;
3495         }
3496
3497         msg = ads_first_entry(ads, res);
3498         if (!msg) {
3499                 SAFE_FREE(host);
3500                 return ADS_ERROR_SYSTEM(ENOENT);
3501         }
3502
3503         hostnameDN = ads_get_dn(ads, talloc_tos(), (LDAPMessage *)msg);
3504         if (hostnameDN == NULL) {
3505                 SAFE_FREE(host);
3506                 return ADS_ERROR_SYSTEM(ENOENT);
3507         }
3508
3509         rc = ldap_delete_ext_s(ads->ldap.ld, hostnameDN, pldap_control, NULL);
3510         if (rc) {
3511                 DEBUG(3,("ldap_delete_ext_s failed with error code %d\n", rc));
3512         }else {
3513                 DEBUG(3,("ldap_delete_ext_s succeeded with error code %d\n", rc));
3514         }
3515
3516         if (rc != LDAP_SUCCESS) {
3517                 const char *attrs[] = { "cn", NULL };
3518                 LDAPMessage *msg_sub;
3519
3520                 /* we only search with scope ONE, we do not expect any further
3521                  * objects to be created deeper */
3522
3523                 status = ads_do_search_retry(ads, hostnameDN,
3524                                              LDAP_SCOPE_ONELEVEL,
3525                                              "(objectclass=*)", attrs, &res);
3526
3527                 if (!ADS_ERR_OK(status)) {
3528                         SAFE_FREE(host);
3529                         TALLOC_FREE(hostnameDN);
3530                         return status;
3531                 }
3532
3533                 for (msg_sub = ads_first_entry(ads, res); msg_sub;
3534                         msg_sub = ads_next_entry(ads, msg_sub)) {
3535
3536                         char *dn = NULL;
3537
3538                         if ((dn = ads_get_dn(ads, talloc_tos(), msg_sub)) == NULL) {
3539                                 SAFE_FREE(host);
3540                                 TALLOC_FREE(hostnameDN);
3541                                 return ADS_ERROR(LDAP_NO_MEMORY);
3542                         }
3543
3544                         status = ads_del_dn(ads, dn);
3545                         if (!ADS_ERR_OK(status)) {
3546                                 DEBUG(3,("failed to delete dn %s: %s\n", dn, ads_errstr(status)));
3547                                 SAFE_FREE(host);
3548                                 TALLOC_FREE(dn);
3549                                 TALLOC_FREE(hostnameDN);
3550                                 return status;
3551                         }
3552
3553                         TALLOC_FREE(dn);
3554                 }
3555
3556                 /* there should be no subordinate objects anymore */
3557                 status = ads_do_search_retry(ads, hostnameDN,
3558                                              LDAP_SCOPE_ONELEVEL,
3559                                              "(objectclass=*)", attrs, &res);
3560
3561                 if (!ADS_ERR_OK(status) || ( (ads_count_replies(ads, res)) > 0 ) ) {
3562                         SAFE_FREE(host);
3563                         TALLOC_FREE(hostnameDN);
3564                         return status;
3565                 }
3566
3567                 /* delete hostnameDN now */
3568                 status = ads_del_dn(ads, hostnameDN);
3569                 if (!ADS_ERR_OK(status)) {
3570                         SAFE_FREE(host);
3571                         DEBUG(3,("failed to delete dn %s: %s\n", hostnameDN, ads_errstr(status)));
3572                         TALLOC_FREE(hostnameDN);
3573                         return status;
3574                 }
3575         }
3576
3577         TALLOC_FREE(hostnameDN);
3578
3579         status = ads_find_machine_acct(ads, &res, host);
3580         if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
3581                 DEBUG(3, ("Failed to remove host account.\n"));
3582                 SAFE_FREE(host);
3583                 return status;
3584         }
3585
3586         SAFE_FREE(host);
3587         return status;
3588 }
3589
3590 /**
3591  * pull all token-sids from an LDAP dn
3592  * @param ads connection to ads server
3593  * @param mem_ctx TALLOC_CTX for allocating sid array
3594  * @param dn of LDAP object
3595  * @param user_sid pointer to struct dom_sid (objectSid)
3596  * @param primary_group_sid pointer to struct dom_sid (self composed)
3597  * @param sids pointer to sid array to allocate
3598  * @param num_sids counter of SIDs pulled
3599  * @return status of token query
3600  **/
3601  ADS_STATUS ads_get_tokensids(ADS_STRUCT *ads,
3602                               TALLOC_CTX *mem_ctx,
3603                               const char *dn,
3604                               struct dom_sid *user_sid,
3605                               struct dom_sid *primary_group_sid,
3606                               struct dom_sid **sids,
3607                               size_t *num_sids)
3608 {
3609         ADS_STATUS status;
3610         LDAPMessage *res = NULL;
3611         int count = 0;
3612         size_t tmp_num_sids;
3613         struct dom_sid *tmp_sids;
3614         struct dom_sid tmp_user_sid;
3615         struct dom_sid tmp_primary_group_sid;
3616         uint32 pgid;
3617         const char *attrs[] = {
3618                 "objectSid",
3619                 "tokenGroups",
3620                 "primaryGroupID",
3621                 NULL
3622         };
3623
3624         status = ads_search_retry_dn(ads, &res, dn, attrs);
3625         if (!ADS_ERR_OK(status)) {
3626                 return status;
3627         }
3628
3629         count = ads_count_replies(ads, res);
3630         if (count != 1) {
3631                 ads_msgfree(ads, res);
3632                 return ADS_ERROR_LDAP(LDAP_NO_SUCH_OBJECT);
3633         }
3634
3635         if (!ads_pull_sid(ads, res, "objectSid", &tmp_user_sid)) {
3636                 ads_msgfree(ads, res);
3637                 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3638         }
3639
3640         if (!ads_pull_uint32(ads, res, "primaryGroupID", &pgid)) {
3641                 ads_msgfree(ads, res);
3642                 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3643         }
3644
3645         {
3646                 /* hack to compose the primary group sid without knowing the
3647                  * domsid */
3648
3649                 struct dom_sid domsid;
3650
3651                 sid_copy(&domsid, &tmp_user_sid);
3652
3653                 if (!sid_split_rid(&domsid, NULL)) {
3654                         ads_msgfree(ads, res);
3655                         return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3656                 }
3657
3658                 if (!sid_compose(&tmp_primary_group_sid, &domsid, pgid)) {
3659                         ads_msgfree(ads, res);
3660                         return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3661                 }
3662         }
3663
3664         tmp_num_sids = ads_pull_sids(ads, mem_ctx, res, "tokenGroups", &tmp_sids);
3665
3666         if (tmp_num_sids == 0 || !tmp_sids) {
3667                 ads_msgfree(ads, res);
3668                 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3669         }
3670
3671         if (num_sids) {
3672                 *num_sids = tmp_num_sids;
3673         }
3674
3675         if (sids) {
3676                 *sids = tmp_sids;
3677         }
3678
3679         if (user_sid) {
3680                 *user_sid = tmp_user_sid;
3681         }
3682
3683         if (primary_group_sid) {
3684                 *primary_group_sid = tmp_primary_group_sid;
3685         }
3686
3687         DEBUG(10,("ads_get_tokensids: returned %d sids\n", (int)tmp_num_sids + 2));
3688
3689         ads_msgfree(ads, res);
3690         return ADS_ERROR_LDAP(LDAP_SUCCESS);
3691 }
3692
3693 /**
3694  * Find a sAMAccoutName in LDAP
3695  * @param ads connection to ads server
3696  * @param mem_ctx TALLOC_CTX for allocating sid array
3697  * @param samaccountname to search
3698  * @param uac_ret uint32 pointer userAccountControl attribute value
3699  * @param dn_ret pointer to dn
3700  * @return status of token query
3701  **/
3702 ADS_STATUS ads_find_samaccount(ADS_STRUCT *ads,
3703                                TALLOC_CTX *mem_ctx,
3704                                const char *samaccountname,
3705                                uint32 *uac_ret,
3706                                const char **dn_ret)
3707 {
3708         ADS_STATUS status;
3709         const char *attrs[] = { "userAccountControl", NULL };
3710         const char *filter;
3711         LDAPMessage *res = NULL;
3712         char *dn = NULL;
3713         uint32 uac = 0;
3714
3715         filter = talloc_asprintf(mem_ctx, "(&(objectclass=user)(sAMAccountName=%s))",
3716                 samaccountname);
3717         if (filter == NULL) {
3718                 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
3719                 goto out;
3720         }
3721
3722         status = ads_do_search_all(ads, ads->config.bind_path,
3723                                    LDAP_SCOPE_SUBTREE,
3724                                    filter, attrs, &res);
3725
3726         if (!ADS_ERR_OK(status)) {
3727                 goto out;
3728         }
3729
3730         if (ads_count_replies(ads, res) != 1) {
3731                 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3732                 goto out;
3733         }
3734
3735         dn = ads_get_dn(ads, talloc_tos(), res);
3736         if (dn == NULL) {
3737                 status = ADS_ERROR(LDAP_NO_MEMORY);
3738                 goto out;
3739         }
3740
3741         if (!ads_pull_uint32(ads, res, "userAccountControl", &uac)) {
3742                 status = ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
3743                 goto out;
3744         }
3745
3746         if (uac_ret) {
3747                 *uac_ret = uac;
3748         }
3749
3750         if (dn_ret) {
3751                 *dn_ret = talloc_strdup(mem_ctx, dn);
3752                 if (!*dn_ret) {
3753                         status = ADS_ERROR(LDAP_NO_MEMORY);
3754                         goto out;
3755                 }
3756         }
3757  out:
3758         TALLOC_FREE(dn);
3759         ads_msgfree(ads, res);
3760
3761         return status;
3762 }
3763
3764 /**
3765  * find our configuration path 
3766  * @param ads connection to ads server
3767  * @param mem_ctx Pointer to talloc context
3768  * @param config_path Pointer to the config path
3769  * @return status of search
3770  **/
3771 ADS_STATUS ads_config_path(ADS_STRUCT *ads, 
3772                            TALLOC_CTX *mem_ctx, 
3773                            char **config_path)
3774 {
3775         ADS_STATUS status;
3776         LDAPMessage *res = NULL;
3777         const char *config_context = NULL;
3778         const char *attrs[] = { "configurationNamingContext", NULL };
3779
3780         status = ads_do_search(ads, "", LDAP_SCOPE_BASE, 
3781                                "(objectclass=*)", attrs, &res);
3782         if (!ADS_ERR_OK(status)) {
3783                 return status;
3784         }
3785
3786         config_context = ads_pull_string(ads, mem_ctx, res, 
3787                                          "configurationNamingContext");
3788         ads_msgfree(ads, res);
3789         if (!config_context) {
3790                 return ADS_ERROR(LDAP_NO_MEMORY);
3791         }
3792
3793         if (config_path) {
3794                 *config_path = talloc_strdup(mem_ctx, config_context);
3795                 if (!*config_path) {
3796                         return ADS_ERROR(LDAP_NO_MEMORY);
3797                 }
3798         }
3799
3800         return ADS_ERROR(LDAP_SUCCESS);
3801 }
3802
3803 /**
3804  * find the displayName of an extended right 
3805  * @param ads connection to ads server
3806  * @param config_path The config path
3807  * @param mem_ctx Pointer to talloc context
3808  * @param GUID struct of the rightsGUID
3809  * @return status of search
3810  **/
3811 const char *ads_get_extended_right_name_by_guid(ADS_STRUCT *ads, 
3812                                                 const char *config_path, 
3813                                                 TALLOC_CTX *mem_ctx, 
3814                                                 const struct GUID *rights_guid)
3815 {
3816         ADS_STATUS rc;
3817         LDAPMessage *res = NULL;
3818         char *expr = NULL;
3819         const char *attrs[] = { "displayName", NULL };
3820         const char *result = NULL;
3821         const char *path;
3822
3823         if (!ads || !mem_ctx || !rights_guid) {
3824                 goto done;
3825         }
3826
3827         expr = talloc_asprintf(mem_ctx, "(rightsGuid=%s)", 
3828                                GUID_string(mem_ctx, rights_guid));
3829         if (!expr) {
3830                 goto done;
3831         }
3832
3833         path = talloc_asprintf(mem_ctx, "cn=Extended-Rights,%s", config_path);
3834         if (!path) {
3835                 goto done;
3836         }
3837
3838         rc = ads_do_search_retry(ads, path, LDAP_SCOPE_SUBTREE, 
3839                                  expr, attrs, &res);
3840         if (!ADS_ERR_OK(rc)) {
3841                 goto done;
3842         }
3843
3844         if (ads_count_replies(ads, res) != 1) {
3845                 goto done;
3846         }
3847
3848         result = ads_pull_string(ads, mem_ctx, res, "displayName");
3849
3850  done:
3851         ads_msgfree(ads, res);
3852         return result;
3853 }
3854
3855 /**
3856  * verify or build and verify an account ou
3857  * @param mem_ctx Pointer to talloc context
3858  * @param ads connection to ads server
3859  * @param account_ou
3860  * @return status of search
3861  **/
3862
3863 ADS_STATUS ads_check_ou_dn(TALLOC_CTX *mem_ctx,
3864                            ADS_STRUCT *ads,
3865                            const char **account_ou)
3866 {
3867         char **exploded_dn;
3868         const char *name;
3869         char *ou_string;
3870
3871         exploded_dn = ldap_explode_dn(*account_ou, 0);
3872         if (exploded_dn) {
3873                 ldap_value_free(exploded_dn);
3874                 return ADS_SUCCESS;
3875         }
3876
3877         ou_string = ads_ou_string(ads, *account_ou);
3878         if (!ou_string) {
3879                 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX);
3880         }
3881
3882         name = talloc_asprintf(mem_ctx, "%s,%s", ou_string,
3883                                ads->config.bind_path);
3884         SAFE_FREE(ou_string);
3885
3886         if (!name) {
3887                 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3888         }
3889
3890         exploded_dn = ldap_explode_dn(name, 0);
3891         if (!exploded_dn) {
3892                 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX);
3893         }
3894         ldap_value_free(exploded_dn);
3895
3896         *account_ou = name;
3897         return ADS_SUCCESS;
3898 }
3899
3900 #endif