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