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