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