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