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