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