source: trunk/samba/source/libads/ldap.c @ 26

Last change on this file since 26 was 26, checked in by Paul Smedley, 14 years ago

Updated source to 3.0.25rc1

File size: 82.4 KB
Line 
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 2 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, write to the Free Software
22   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23*/
24
25#include "includes.h"
26
27#ifdef HAVE_LDAP
28
29/**
30 * @file ldap.c
31 * @brief basic ldap client-side routines for ads server communications
32 *
33 * The routines contained here should do the necessary ldap calls for
34 * ads setups.
35 *
36 * Important note: attribute names passed into ads_ routines must
37 * already be in UTF-8 format.  We do not convert them because in almost
38 * all cases, they are just ascii (which is represented with the same
39 * codepoints in UTF-8).  This may have to change at some point
40 **/
41
42
43#define LDAP_SERVER_TREE_DELETE_OID     "1.2.840.113556.1.4.805"
44
45static SIG_ATOMIC_T gotalarm;
46                                                                                                                   
47/***************************************************************
48 Signal function to tell us we timed out.
49****************************************************************/
50
51static void gotalarm_sig(void)
52{
53        gotalarm = 1;
54}
55
56 LDAP *ldap_open_with_timeout(const char *server, int port, unsigned int to)
57{
58        LDAP *ldp = NULL;
59
60        /* Setup timeout */
61        gotalarm = 0;
62        CatchSignal(SIGALRM, SIGNAL_CAST gotalarm_sig);
63        alarm(to);
64        /* End setup timeout. */
65
66        ldp = ldap_open(server, port);
67
68        if (ldp == NULL) {
69                DEBUG(2,("Could not open LDAP connection to %s:%d: %s\n",
70                         server, port, strerror(errno)));
71        }
72
73        /* Teardown timeout. */
74        CatchSignal(SIGALRM, SIGNAL_CAST SIG_IGN);
75        alarm(0);
76
77        return ldp;
78}
79
80static int ldap_search_with_timeout(LDAP *ld,
81                                    LDAP_CONST char *base,
82                                    int scope,
83                                    LDAP_CONST char *filter,
84                                    char **attrs,
85                                    int attrsonly,
86                                    LDAPControl **sctrls,
87                                    LDAPControl **cctrls,
88                                    int sizelimit,
89                                    LDAPMessage **res )
90{
91        struct timeval timeout;
92        int result;
93
94        /* Setup timeout for the ldap_search_ext_s call - local and remote. */
95        timeout.tv_sec = lp_ldap_timeout();
96        timeout.tv_usec = 0;
97
98        /* Setup alarm timeout.... Do we need both of these ? JRA. */
99        gotalarm = 0;
100        CatchSignal(SIGALRM, SIGNAL_CAST gotalarm_sig);
101        alarm(lp_ldap_timeout());
102        /* End setup timeout. */
103
104        result = ldap_search_ext_s(ld, base, scope, filter, attrs,
105                                   attrsonly, sctrls, cctrls, &timeout,
106                                   sizelimit, res);
107
108        /* Teardown timeout. */
109        CatchSignal(SIGALRM, SIGNAL_CAST SIG_IGN);
110        alarm(0);
111
112        if (gotalarm != 0)
113                return LDAP_TIMELIMIT_EXCEEDED;
114
115        return result;
116}
117
118/**********************************************
119 Do client and server sitename match ?
120**********************************************/
121
122BOOL ads_sitename_match(ADS_STRUCT *ads)
123{
124        if (ads->config.server_site_name == NULL &&
125            ads->config.client_site_name == NULL ) {
126                DEBUG(10,("ads_sitename_match: both null\n"));
127                return True;
128        }
129        if (ads->config.server_site_name &&
130            ads->config.client_site_name &&
131            strequal(ads->config.server_site_name,
132                     ads->config.client_site_name)) {
133                DEBUG(10,("ads_sitename_match: name %s match\n", ads->config.server_site_name));
134                return True;
135        }
136        DEBUG(10,("ads_sitename_match: no match between server: %s and client: %s\n",
137                ads->config.server_site_name ? ads->config.server_site_name : "NULL",
138                ads->config.client_site_name ? ads->config.client_site_name : "NULL"));
139        return False;
140}
141
142/**********************************************
143 Is this the closest DC ?
144**********************************************/
145
146BOOL ads_closest_dc(ADS_STRUCT *ads)
147{
148        if (ads->config.flags & ADS_CLOSEST) {
149                DEBUG(10,("ads_closest_dc: ADS_CLOSEST flag set\n"));
150                return True;
151        }
152
153        /* not sure if this can ever happen */
154        if (ads_sitename_match(ads)) {
155                DEBUG(10,("ads_closest_dc: ADS_CLOSEST flag not set but sites match\n"));
156                return True;
157        }
158
159        DEBUG(10,("ads_closest_dc: %s is not the closest DC\n", 
160                ads->config.ldap_server_name));
161
162        return False;
163}
164
165
166/*
167  try a connection to a given ldap server, returning True and setting the servers IP
168  in the ads struct if successful
169 */
170BOOL ads_try_connect(ADS_STRUCT *ads, const char *server )
171{
172        char *srv;
173        struct cldap_netlogon_reply cldap_reply;
174
175        if (!server || !*server) {
176                return False;
177        }
178       
179        DEBUG(5,("ads_try_connect: sending CLDAP request to %s (realm: %s)\n", 
180                server, ads->server.realm));
181
182        /* this copes with inet_ntoa brokenness */
183       
184        srv = SMB_STRDUP(server);
185
186        ZERO_STRUCT( cldap_reply );
187
188        if ( !ads_cldap_netlogon( srv, ads->server.realm, &cldap_reply ) ) {
189                DEBUG(3,("ads_try_connect: CLDAP request %s failed.\n", srv));
190                SAFE_FREE( srv );
191                return False;
192        }
193
194        /* Check the CLDAP reply flags */
195
196        if ( !(cldap_reply.flags & ADS_LDAP) ) {
197                DEBUG(1,("ads_try_connect: %s's CLDAP reply says it is not an LDAP server!\n",
198                        srv));
199                SAFE_FREE( srv );
200                return False;
201        }
202
203        /* Fill in the ads->config values */
204
205        SAFE_FREE(ads->config.realm);
206        SAFE_FREE(ads->config.bind_path);
207        SAFE_FREE(ads->config.ldap_server_name);
208        SAFE_FREE(ads->config.server_site_name);
209        SAFE_FREE(ads->config.client_site_name);
210        SAFE_FREE(ads->server.workgroup);
211
212        ads->config.flags              = cldap_reply.flags;
213        ads->config.ldap_server_name   = SMB_STRDUP(cldap_reply.hostname);
214        strupper_m(cldap_reply.domain);
215        ads->config.realm              = SMB_STRDUP(cldap_reply.domain);
216        ads->config.bind_path          = ads_build_dn(ads->config.realm);
217        if (*cldap_reply.server_site_name) {
218                ads->config.server_site_name =
219                        SMB_STRDUP(cldap_reply.server_site_name);
220        }
221        if (*cldap_reply.client_site_name) {
222                ads->config.client_site_name =
223                        SMB_STRDUP(cldap_reply.client_site_name);
224        }
225               
226        ads->server.workgroup          = SMB_STRDUP(cldap_reply.netbios_domain);
227
228        ads->ldap_port = LDAP_PORT;
229        ads->ldap_ip = *interpret_addr2(srv);
230        SAFE_FREE(srv);
231       
232        /* Store our site name. */
233        sitename_store( cldap_reply.domain, cldap_reply.client_site_name );
234
235        return True;
236}
237
238/**********************************************************************
239 Try to find an AD dc using our internal name resolution routines
240 Try the realm first and then then workgroup name if netbios is not
241 disabled
242**********************************************************************/
243
244static NTSTATUS ads_find_dc(ADS_STRUCT *ads)
245{
246        const char *c_realm;
247        int count, i=0;
248        struct ip_service *ip_list;
249        pstring realm;
250        BOOL got_realm = False;
251        BOOL use_own_domain = False;
252        char *sitename;
253        NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
254
255        /* if the realm and workgroup are both empty, assume they are ours */
256
257        /* realm */
258        c_realm = ads->server.realm;
259       
260        if ( !c_realm || !*c_realm ) {
261                /* special case where no realm and no workgroup means our own */
262                if ( !ads->server.workgroup || !*ads->server.workgroup ) {
263                        use_own_domain = True;
264                        c_realm = lp_realm();
265                }
266        }
267       
268        if (c_realm && *c_realm) 
269                got_realm = True;
270                   
271        /* we need to try once with the realm name and fallback to the
272           netbios domain name if we fail (if netbios has not been disabled */
273           
274        if ( !got_realm && !lp_disable_netbios() ) {
275                c_realm = ads->server.workgroup;
276                if (!c_realm || !*c_realm) {
277                        if ( use_own_domain )
278                                c_realm = lp_workgroup();
279                }
280               
281                if ( !c_realm || !*c_realm ) {
282                        DEBUG(0,("ads_find_dc: no realm or workgroup!  Don't know what to do\n"));
283                        return NT_STATUS_INVALID_PARAMETER; /* rather need MISSING_PARAMETER ... */
284                }
285        }
286       
287        pstrcpy( realm, c_realm );
288
289        sitename = sitename_fetch(realm);
290
291 again:
292
293        DEBUG(6,("ads_find_dc: looking for %s '%s'\n", 
294                (got_realm ? "realm" : "domain"), realm));
295
296        status = get_sorted_dc_list(realm, sitename, &ip_list, &count, got_realm);
297        if (!NT_STATUS_IS_OK(status)) {
298                /* fall back to netbios if we can */
299                if ( got_realm && !lp_disable_netbios() ) {
300                        got_realm = False;
301                        goto again;
302                }
303               
304                SAFE_FREE(sitename);
305                return status;
306        }
307
308        /* if we fail this loop, then giveup since all the IP addresses returned were dead */
309        for ( i=0; i<count; i++ ) {
310                fstring server;
311               
312                fstrcpy( server, inet_ntoa(ip_list[i].ip) );
313               
314                if ( !NT_STATUS_IS_OK(check_negative_conn_cache(realm, server)) )
315                        continue;
316
317                if (!got_realm) {
318                        /* realm in this case is a workgroup name. We need
319                           to ignore any IP addresses in the negative connection
320                           cache that match ip addresses returned in the ad realm
321                           case. It sucks that I have to reproduce the logic above... */
322                        c_realm = ads->server.realm;
323                        if ( !c_realm || !*c_realm ) {
324                                if ( !ads->server.workgroup || !*ads->server.workgroup ) {
325                                        c_realm = lp_realm();
326                                }
327                        }
328                        if (c_realm && *c_realm &&
329                                        !NT_STATUS_IS_OK(check_negative_conn_cache(c_realm, server))) {
330                                /* Ensure we add the workgroup name for this
331                                   IP address as negative too. */
332                                add_failed_connection_entry( realm, server, NT_STATUS_UNSUCCESSFUL );
333                                continue;
334                        }
335                }
336                       
337                if ( ads_try_connect(ads, server) ) {
338                        SAFE_FREE(ip_list);
339                        SAFE_FREE(sitename);
340                        return NT_STATUS_OK;
341                }
342               
343                /* keep track of failures */
344                add_failed_connection_entry( realm, server, NT_STATUS_UNSUCCESSFUL );
345        }
346
347        SAFE_FREE(ip_list);
348
349        /* In case we failed to contact one of our closest DC on our site we
350         * need to try to find another DC, retry with a site-less SRV DNS query
351         * - Guenther */
352
353        if (sitename) {
354                DEBUG(1,("ads_find_dc: failed to find a valid DC on our site (%s), "
355                                "trying to find another DC\n", sitename));
356                SAFE_FREE(sitename);
357                namecache_delete(realm, 0x1C);
358                goto again;
359        }
360
361        return NT_STATUS_NO_LOGON_SERVERS;
362}
363
364
365/**
366 * Connect to the LDAP server
367 * @param ads Pointer to an existing ADS_STRUCT
368 * @return status of connection
369 **/
370ADS_STATUS ads_connect(ADS_STRUCT *ads)
371{
372        int version = LDAP_VERSION3;
373        ADS_STATUS status;
374        NTSTATUS ntstatus;
375
376        ads->last_attempt = time(NULL);
377        ads->ld = NULL;
378
379        /* try with a user specified server */
380
381        if (ads->server.ldap_server && 
382            ads_try_connect(ads, ads->server.ldap_server)) {
383                goto got_connection;
384        }
385
386        ntstatus = ads_find_dc(ads);
387        if (NT_STATUS_IS_OK(ntstatus)) {
388                goto got_connection;
389        }
390
391        return ADS_ERROR_NT(ntstatus);
392
393got_connection:
394        DEBUG(3,("Connected to LDAP server %s\n", inet_ntoa(ads->ldap_ip)));
395
396        if (!ads->auth.user_name) {
397                /* Must use the userPrincipalName value here or sAMAccountName
398                   and not servicePrincipalName; found by Guenther Deschner */
399
400                asprintf(&ads->auth.user_name, "%s$", global_myname() );
401        }
402
403        if (!ads->auth.realm) {
404                ads->auth.realm = SMB_STRDUP(ads->config.realm);
405        }
406
407        if (!ads->auth.kdc_server) {
408                ads->auth.kdc_server = SMB_STRDUP(inet_ntoa(ads->ldap_ip));
409        }
410
411#if KRB5_DNS_HACK
412        /* this is a really nasty hack to avoid ADS DNS problems. It needs a patch
413           to MIT kerberos to work (tridge) */
414        {
415                char *env;
416                asprintf(&env, "KRB5_KDC_ADDRESS_%s", ads->config.realm);
417                setenv(env, ads->auth.kdc_server, 1);
418                free(env);
419        }
420#endif
421
422        /* If the caller() requested no LDAP bind, then we are done */
423       
424        if (ads->auth.flags & ADS_AUTH_NO_BIND) {
425                return ADS_SUCCESS;
426        }
427       
428        /* Otherwise setup the TCP LDAP session */
429
430        if ( (ads->ld = ldap_open_with_timeout(ads->config.ldap_server_name, 
431                LDAP_PORT, lp_ldap_timeout())) == NULL )
432        {
433                return ADS_ERROR(LDAP_OPERATIONS_ERROR);
434        }
435
436        /* cache the successful connection for workgroup and realm */
437        if (ads_closest_dc(ads)) {
438                saf_store( ads->server.workgroup, inet_ntoa(ads->ldap_ip));
439                saf_store( ads->server.realm, inet_ntoa(ads->ldap_ip));
440        }
441
442        ldap_set_option(ads->ld, LDAP_OPT_PROTOCOL_VERSION, &version);
443
444        status = ADS_ERROR(smb_ldap_start_tls(ads->ld, version));
445        if (!ADS_ERR_OK(status)) {
446                return status;
447        }
448
449        /* fill in the current time and offsets */
450       
451        status = ads_current_time( ads );
452        if ( !ADS_ERR_OK(status) ) {
453                return status;
454        }
455
456        /* Now do the bind */
457       
458        if (ads->auth.flags & ADS_AUTH_ANON_BIND) {
459                return ADS_ERROR(ldap_simple_bind_s( ads->ld, NULL, NULL));
460        }
461
462        if (ads->auth.flags & ADS_AUTH_SIMPLE_BIND) {
463                return ADS_ERROR(ldap_simple_bind_s( ads->ld, ads->auth.user_name, ads->auth.password));
464        }
465
466        return ads_sasl_bind(ads);
467}
468
469/*
470  Duplicate a struct berval into talloc'ed memory
471 */
472static struct berval *dup_berval(TALLOC_CTX *ctx, const struct berval *in_val)
473{
474        struct berval *value;
475
476        if (!in_val) return NULL;
477
478        value = TALLOC_ZERO_P(ctx, struct berval);
479        if (value == NULL)
480                return NULL;
481        if (in_val->bv_len == 0) return value;
482
483        value->bv_len = in_val->bv_len;
484        value->bv_val = (char *)TALLOC_MEMDUP(ctx, in_val->bv_val,
485                                              in_val->bv_len);
486        return value;
487}
488
489/*
490  Make a values list out of an array of (struct berval *)
491 */
492static struct berval **ads_dup_values(TALLOC_CTX *ctx, 
493                                      const struct berval **in_vals)
494{
495        struct berval **values;
496        int i;
497       
498        if (!in_vals) return NULL;
499        for (i=0; in_vals[i]; i++)
500                ; /* count values */
501        values = TALLOC_ZERO_ARRAY(ctx, struct berval *, i+1);
502        if (!values) return NULL;
503
504        for (i=0; in_vals[i]; i++) {
505                values[i] = dup_berval(ctx, in_vals[i]);
506        }
507        return values;
508}
509
510/*
511  UTF8-encode a values list out of an array of (char *)
512 */
513static char **ads_push_strvals(TALLOC_CTX *ctx, const char **in_vals)
514{
515        char **values;
516        int i;
517       
518        if (!in_vals) return NULL;
519        for (i=0; in_vals[i]; i++)
520                ; /* count values */
521        values = TALLOC_ZERO_ARRAY(ctx, char *, i+1);
522        if (!values) return NULL;
523
524        for (i=0; in_vals[i]; i++) {
525                push_utf8_talloc(ctx, &values[i], in_vals[i]);
526        }
527        return values;
528}
529
530/*
531  Pull a (char *) array out of a UTF8-encoded values list
532 */
533static char **ads_pull_strvals(TALLOC_CTX *ctx, const char **in_vals)
534{
535        char **values;
536        int i;
537       
538        if (!in_vals) return NULL;
539        for (i=0; in_vals[i]; i++)
540                ; /* count values */
541        values = TALLOC_ZERO_ARRAY(ctx, char *, i+1);
542        if (!values) return NULL;
543
544        for (i=0; in_vals[i]; i++) {
545                pull_utf8_talloc(ctx, &values[i], in_vals[i]);
546        }
547        return values;
548}
549
550/**
551 * Do a search with paged results.  cookie must be null on the first
552 *  call, and then returned on each subsequent call.  It will be null
553 *  again when the entire search is complete
554 * @param ads connection to ads server
555 * @param bind_path Base dn for the search
556 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
557 * @param expr Search expression - specified in local charset
558 * @param attrs Attributes to retrieve - specified in utf8 or ascii
559 * @param res ** which will contain results - free res* with ads_msgfree()
560 * @param count Number of entries retrieved on this page
561 * @param cookie The paged results cookie to be returned on subsequent calls
562 * @return status of search
563 **/
564static ADS_STATUS ads_do_paged_search_args(ADS_STRUCT *ads,
565                                           const char *bind_path,
566                                           int scope, const char *expr,
567                                           const char **attrs, void *args,
568                                           LDAPMessage **res, 
569                                           int *count, struct berval **cookie)
570{
571        int rc, i, version;
572        char *utf8_expr, *utf8_path, **search_attrs;
573        LDAPControl PagedResults, NoReferrals, ExtendedDn, *controls[4], **rcontrols;
574        BerElement *cookie_be = NULL;
575        struct berval *cookie_bv= NULL;
576        BerElement *extdn_be = NULL;
577        struct berval *extdn_bv= NULL;
578
579        TALLOC_CTX *ctx;
580        ads_control *external_control = (ads_control *) args;
581
582        *res = NULL;
583
584        if (!(ctx = talloc_init("ads_do_paged_search_args")))
585                return ADS_ERROR(LDAP_NO_MEMORY);
586
587        /* 0 means the conversion worked but the result was empty
588           so we only fail if it's -1.  In any case, it always
589           at least nulls out the dest */
590        if ((push_utf8_talloc(ctx, &utf8_expr, expr) == (size_t)-1) ||
591            (push_utf8_talloc(ctx, &utf8_path, bind_path) == (size_t)-1)) {
592                rc = LDAP_NO_MEMORY;
593                goto done;
594        }
595
596        if (!attrs || !(*attrs))
597                search_attrs = NULL;
598        else {
599                /* This would be the utf8-encoded version...*/
600                /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
601                if (!(str_list_copy(&search_attrs, attrs))) {
602                        rc = LDAP_NO_MEMORY;
603                        goto done;
604                }
605        }
606               
607               
608        /* Paged results only available on ldap v3 or later */
609        ldap_get_option(ads->ld, LDAP_OPT_PROTOCOL_VERSION, &version);
610        if (version < LDAP_VERSION3) {
611                rc =  LDAP_NOT_SUPPORTED;
612                goto done;
613        }
614
615        cookie_be = ber_alloc_t(LBER_USE_DER);
616        if (*cookie) {
617                ber_printf(cookie_be, "{iO}", (ber_int_t) 1000, *cookie);
618                ber_bvfree(*cookie); /* don't need it from last time */
619                *cookie = NULL;
620        } else {
621                ber_printf(cookie_be, "{io}", (ber_int_t) 1000, "", 0);
622        }
623        ber_flatten(cookie_be, &cookie_bv);
624        PagedResults.ldctl_oid = CONST_DISCARD(char *, ADS_PAGE_CTL_OID);
625        PagedResults.ldctl_iscritical = (char) 1;
626        PagedResults.ldctl_value.bv_len = cookie_bv->bv_len;
627        PagedResults.ldctl_value.bv_val = cookie_bv->bv_val;
628
629        NoReferrals.ldctl_oid = CONST_DISCARD(char *, ADS_NO_REFERRALS_OID);
630        NoReferrals.ldctl_iscritical = (char) 0;
631        NoReferrals.ldctl_value.bv_len = 0;
632        NoReferrals.ldctl_value.bv_val = CONST_DISCARD(char *, "");
633
634        if (external_control && strequal(external_control->control, ADS_EXTENDED_DN_OID)) {
635
636                ExtendedDn.ldctl_oid = CONST_DISCARD(char *, external_control->control);
637                ExtendedDn.ldctl_iscritical = (char) external_control->critical;
638
639                /* win2k does not accept a ldctl_value beeing passed in */
640
641                if (external_control->val != 0) {
642
643                        if ((extdn_be = ber_alloc_t(LBER_USE_DER)) == NULL ) {
644                                rc = LDAP_NO_MEMORY;
645                                goto done;
646                        }
647
648                        if ((ber_printf(extdn_be, "{i}", (ber_int_t) external_control->val)) == -1) {
649                                rc = LDAP_NO_MEMORY;
650                                goto done;
651                        }
652                        if ((ber_flatten(extdn_be, &extdn_bv)) == -1) {
653                                rc = LDAP_NO_MEMORY;
654                                goto done;
655                        }
656
657                        ExtendedDn.ldctl_value.bv_len = extdn_bv->bv_len;
658                        ExtendedDn.ldctl_value.bv_val = extdn_bv->bv_val;
659
660                } else {
661                        ExtendedDn.ldctl_value.bv_len = 0;
662                        ExtendedDn.ldctl_value.bv_val = NULL;
663                }
664
665                controls[0] = &NoReferrals;
666                controls[1] = &PagedResults;
667                controls[2] = &ExtendedDn;
668                controls[3] = NULL;
669
670        } else {
671                controls[0] = &NoReferrals;
672                controls[1] = &PagedResults;
673                controls[2] = NULL;
674        }
675
676        /* we need to disable referrals as the openldap libs don't
677           handle them and paged results at the same time.  Using them
678           together results in the result record containing the server
679           page control being removed from the result list (tridge/jmcd)
680       
681           leaving this in despite the control that says don't generate
682           referrals, in case the server doesn't support it (jmcd)
683        */
684        ldap_set_option(ads->ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
685
686        rc = ldap_search_with_timeout(ads->ld, utf8_path, scope, utf8_expr, 
687                                      search_attrs, 0, controls,
688                                      NULL, LDAP_NO_LIMIT,
689                                      (LDAPMessage **)res);
690
691        ber_free(cookie_be, 1);
692        ber_bvfree(cookie_bv);
693
694        if (rc) {
695                DEBUG(3,("ads_do_paged_search_args: ldap_search_with_timeout(%s) -> %s\n", expr,
696                         ldap_err2string(rc)));
697                goto done;
698        }
699
700        rc = ldap_parse_result(ads->ld, *res, NULL, NULL, NULL,
701                                        NULL, &rcontrols,  0);
702
703        if (!rcontrols) {
704                goto done;
705        }
706
707        for (i=0; rcontrols[i]; i++) {
708                if (strcmp(ADS_PAGE_CTL_OID, rcontrols[i]->ldctl_oid) == 0) {
709                        cookie_be = ber_init(&rcontrols[i]->ldctl_value);
710                        ber_scanf(cookie_be,"{iO}", (ber_int_t *) count,
711                                  &cookie_bv);
712                        /* the berval is the cookie, but must be freed when
713                           it is all done */
714                        if (cookie_bv->bv_len) /* still more to do */
715                                *cookie=ber_bvdup(cookie_bv);
716                        else
717                                *cookie=NULL;
718                        ber_bvfree(cookie_bv);
719                        ber_free(cookie_be, 1);
720                        break;
721                }
722        }
723        ldap_controls_free(rcontrols);
724
725done:
726        talloc_destroy(ctx);
727
728        if (extdn_be) {
729                ber_free(extdn_be, 1);
730        }
731
732        if (extdn_bv) {
733                ber_bvfree(extdn_bv);
734        }
735 
736        /* if/when we decide to utf8-encode attrs, take out this next line */
737        str_list_free(&search_attrs);
738
739        return ADS_ERROR(rc);
740}
741
742static ADS_STATUS ads_do_paged_search(ADS_STRUCT *ads, const char *bind_path,
743                                      int scope, const char *expr,
744                                      const char **attrs, LDAPMessage **res, 
745                                      int *count, struct berval **cookie)
746{
747        return ads_do_paged_search_args(ads, bind_path, scope, expr, attrs, NULL, res, count, cookie);
748}
749
750
751/**
752 * Get all results for a search.  This uses ads_do_paged_search() to return
753 * all entries in a large search.
754 * @param ads connection to ads server
755 * @param bind_path Base dn for the search
756 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
757 * @param expr Search expression
758 * @param attrs Attributes to retrieve
759 * @param res ** which will contain results - free res* with ads_msgfree()
760 * @return status of search
761 **/
762 ADS_STATUS ads_do_search_all_args(ADS_STRUCT *ads, const char *bind_path,
763                                   int scope, const char *expr,
764                                   const char **attrs, void *args,
765                                   LDAPMessage **res)
766{
767        struct berval *cookie = NULL;
768        int count = 0;
769        ADS_STATUS status;
770
771        *res = NULL;
772        status = ads_do_paged_search_args(ads, bind_path, scope, expr, attrs, args, res,
773                                     &count, &cookie);
774
775        if (!ADS_ERR_OK(status)) 
776                return status;
777
778#ifdef HAVE_LDAP_ADD_RESULT_ENTRY
779        while (cookie) {
780                LDAPMessage *res2 = NULL;
781                ADS_STATUS status2;
782                LDAPMessage *msg, *next;
783
784                status2 = ads_do_paged_search_args(ads, bind_path, scope, expr, 
785                                              attrs, args, &res2, &count, &cookie);
786
787                if (!ADS_ERR_OK(status2)) break;
788
789                /* this relies on the way that ldap_add_result_entry() works internally. I hope
790                   that this works on all ldap libs, but I have only tested with openldap */
791                for (msg = ads_first_entry(ads, res2); msg; msg = next) {
792                        next = ads_next_entry(ads, msg);
793                        ldap_add_result_entry((LDAPMessage **)res, msg);
794                }
795                /* note that we do not free res2, as the memory is now
796                   part of the main returned list */
797        }
798#else
799        DEBUG(0, ("no ldap_add_result_entry() support in LDAP libs!\n"));
800        status = ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
801#endif
802
803        return status;
804}
805
806 ADS_STATUS ads_do_search_all(ADS_STRUCT *ads, const char *bind_path,
807                              int scope, const char *expr,
808                              const char **attrs, LDAPMessage **res)
809{
810        return ads_do_search_all_args(ads, bind_path, scope, expr, attrs, NULL, res);
811}
812
813/**
814 * Run a function on all results for a search.  Uses ads_do_paged_search() and
815 *  runs the function as each page is returned, using ads_process_results()
816 * @param ads connection to ads server
817 * @param bind_path Base dn for the search
818 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
819 * @param expr Search expression - specified in local charset
820 * @param attrs Attributes to retrieve - specified in UTF-8 or ascii
821 * @param fn Function which takes attr name, values list, and data_area
822 * @param data_area Pointer which is passed to function on each call
823 * @return status of search
824 **/
825ADS_STATUS ads_do_search_all_fn(ADS_STRUCT *ads, const char *bind_path,
826                                int scope, const char *expr, const char **attrs,
827                                BOOL(*fn)(char *, void **, void *), 
828                                void *data_area)
829{
830        struct berval *cookie = NULL;
831        int count = 0;
832        ADS_STATUS status;
833        LDAPMessage *res;
834
835        status = ads_do_paged_search(ads, bind_path, scope, expr, attrs, &res,
836                                     &count, &cookie);
837
838        if (!ADS_ERR_OK(status)) return status;
839
840        ads_process_results(ads, res, fn, data_area);
841        ads_msgfree(ads, res);
842
843        while (cookie) {
844                status = ads_do_paged_search(ads, bind_path, scope, expr, attrs,
845                                             &res, &count, &cookie);
846
847                if (!ADS_ERR_OK(status)) break;
848               
849                ads_process_results(ads, res, fn, data_area);
850                ads_msgfree(ads, res);
851        }
852
853        return status;
854}
855
856/**
857 * Do a search with a timeout.
858 * @param ads connection to ads server
859 * @param bind_path Base dn for the search
860 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
861 * @param expr Search expression
862 * @param attrs Attributes to retrieve
863 * @param res ** which will contain results - free res* with ads_msgfree()
864 * @return status of search
865 **/
866 ADS_STATUS ads_do_search(ADS_STRUCT *ads, const char *bind_path, int scope, 
867                          const char *expr,
868                          const char **attrs, LDAPMessage **res)
869{
870        int rc;
871        char *utf8_expr, *utf8_path, **search_attrs = NULL;
872        TALLOC_CTX *ctx;
873
874        *res = NULL;
875        if (!(ctx = talloc_init("ads_do_search"))) {
876                DEBUG(1,("ads_do_search: talloc_init() failed!"));
877                return ADS_ERROR(LDAP_NO_MEMORY);
878        }
879
880        /* 0 means the conversion worked but the result was empty
881           so we only fail if it's negative.  In any case, it always
882           at least nulls out the dest */
883        if ((push_utf8_talloc(ctx, &utf8_expr, expr) == (size_t)-1) ||
884            (push_utf8_talloc(ctx, &utf8_path, bind_path) == (size_t)-1)) {
885                DEBUG(1,("ads_do_search: push_utf8_talloc() failed!"));
886                rc = LDAP_NO_MEMORY;
887                goto done;
888        }
889
890        if (!attrs || !(*attrs))
891                search_attrs = NULL;
892        else {
893                /* This would be the utf8-encoded version...*/
894                /* if (!(search_attrs = ads_push_strvals(ctx, attrs)))  */
895                if (!(str_list_copy(&search_attrs, attrs)))
896                {
897                        DEBUG(1,("ads_do_search: str_list_copy() failed!"));
898                        rc = LDAP_NO_MEMORY;
899                        goto done;
900                }
901        }
902
903        /* see the note in ads_do_paged_search - we *must* disable referrals */
904        ldap_set_option(ads->ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
905
906        rc = ldap_search_with_timeout(ads->ld, utf8_path, scope, utf8_expr,
907                                      search_attrs, 0, NULL, NULL, 
908                                      LDAP_NO_LIMIT,
909                                      (LDAPMessage **)res);
910
911        if (rc == LDAP_SIZELIMIT_EXCEEDED) {
912                DEBUG(3,("Warning! sizelimit exceeded in ldap. Truncating.\n"));
913                rc = 0;
914        }
915
916 done:
917        talloc_destroy(ctx);
918        /* if/when we decide to utf8-encode attrs, take out this next line */
919        str_list_free(&search_attrs);
920        return ADS_ERROR(rc);
921}
922/**
923 * Do a general ADS search
924 * @param ads connection to ads server
925 * @param res ** which will contain results - free res* with ads_msgfree()
926 * @param expr Search expression
927 * @param attrs Attributes to retrieve
928 * @return status of search
929 **/
930 ADS_STATUS ads_search(ADS_STRUCT *ads, LDAPMessage **res, 
931                       const char *expr, const char **attrs)
932{
933        return ads_do_search(ads, ads->config.bind_path, LDAP_SCOPE_SUBTREE, 
934                             expr, attrs, res);
935}
936
937/**
938 * Do a search on a specific DistinguishedName
939 * @param ads connection to ads server
940 * @param res ** which will contain results - free res* with ads_msgfree()
941 * @param dn DistinguishName to search
942 * @param attrs Attributes to retrieve
943 * @return status of search
944 **/
945 ADS_STATUS ads_search_dn(ADS_STRUCT *ads, LDAPMessage **res, 
946                          const char *dn, const char **attrs)
947{
948        return ads_do_search(ads, dn, LDAP_SCOPE_BASE, "(objectclass=*)",
949                             attrs, res);
950}
951
952/**
953 * Free up memory from a ads_search
954 * @param ads connection to ads server
955 * @param msg Search results to free
956 **/
957 void ads_msgfree(ADS_STRUCT *ads, LDAPMessage *msg)
958{
959        if (!msg) return;
960        ldap_msgfree(msg);
961}
962
963/**
964 * Free up memory from various ads requests
965 * @param ads connection to ads server
966 * @param mem Area to free
967 **/
968void ads_memfree(ADS_STRUCT *ads, void *mem)
969{
970        SAFE_FREE(mem);
971}
972
973/**
974 * Get a dn from search results
975 * @param ads connection to ads server
976 * @param msg Search result
977 * @return dn string
978 **/
979 char *ads_get_dn(ADS_STRUCT *ads, LDAPMessage *msg)
980{
981        char *utf8_dn, *unix_dn;
982
983        utf8_dn = ldap_get_dn(ads->ld, msg);
984
985        if (!utf8_dn) {
986                DEBUG (5, ("ads_get_dn: ldap_get_dn failed\n"));
987                return NULL;
988        }
989
990        if (pull_utf8_allocate(&unix_dn, utf8_dn) == (size_t)-1) {
991                DEBUG(0,("ads_get_dn: string conversion failure utf8 [%s]\n",
992                        utf8_dn ));
993                return NULL;
994        }
995        ldap_memfree(utf8_dn);
996        return unix_dn;
997}
998
999/**
1000 * Get the parent from a dn
1001 * @param dn the dn to return the parent from
1002 * @return parent dn string
1003 **/
1004char *ads_parent_dn(const char *dn)
1005{
1006        char *p;
1007
1008        if (dn == NULL) {
1009                return NULL;
1010        }
1011
1012        p = strchr(dn, ',');
1013
1014        if (p == NULL) {
1015                return NULL;
1016        }
1017
1018        return p+1;
1019}
1020
1021/**
1022 * Find a machine account given a hostname
1023 * @param ads connection to ads server
1024 * @param res ** which will contain results - free res* with ads_msgfree()
1025 * @param host Hostname to search for
1026 * @return status of search
1027 **/
1028 ADS_STATUS ads_find_machine_acct(ADS_STRUCT *ads, LDAPMessage **res,
1029                                  const char *machine)
1030{
1031        ADS_STATUS status;
1032        char *expr;
1033        const char *attrs[] = {"*", "nTSecurityDescriptor", NULL};
1034
1035        *res = NULL;
1036
1037        /* the easiest way to find a machine account anywhere in the tree
1038           is to look for hostname$ */
1039        if (asprintf(&expr, "(samAccountName=%s$)", machine) == -1) {
1040                DEBUG(1, ("asprintf failed!\n"));
1041                return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1042        }
1043       
1044        status = ads_search(ads, res, expr, attrs);
1045        SAFE_FREE(expr);
1046        return status;
1047}
1048
1049/**
1050 * Initialize a list of mods to be used in a modify request
1051 * @param ctx An initialized TALLOC_CTX
1052 * @return allocated ADS_MODLIST
1053 **/
1054ADS_MODLIST ads_init_mods(TALLOC_CTX *ctx)
1055{
1056#define ADS_MODLIST_ALLOC_SIZE 10
1057        LDAPMod **mods;
1058       
1059        if ((mods = TALLOC_ZERO_ARRAY(ctx, LDAPMod *, ADS_MODLIST_ALLOC_SIZE + 1)))
1060                /* -1 is safety to make sure we don't go over the end.
1061                   need to reset it to NULL before doing ldap modify */
1062                mods[ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1063       
1064        return (ADS_MODLIST)mods;
1065}
1066
1067
1068/*
1069  add an attribute to the list, with values list already constructed
1070*/
1071static ADS_STATUS ads_modlist_add(TALLOC_CTX *ctx, ADS_MODLIST *mods, 
1072                                  int mod_op, const char *name, 
1073                                  const void *_invals)
1074{
1075        const void **invals = (const void **)_invals;
1076        int curmod;
1077        LDAPMod **modlist = (LDAPMod **) *mods;
1078        struct berval **ber_values = NULL;
1079        char **char_values = NULL;
1080
1081        if (!invals) {
1082                mod_op = LDAP_MOD_DELETE;
1083        } else {
1084                if (mod_op & LDAP_MOD_BVALUES)
1085                        ber_values = ads_dup_values(ctx, 
1086                                                (const struct berval **)invals);
1087                else
1088                        char_values = ads_push_strvals(ctx, 
1089                                                  (const char **) invals);
1090        }
1091
1092        /* find the first empty slot */
1093        for (curmod=0; modlist[curmod] && modlist[curmod] != (LDAPMod *) -1;
1094             curmod++);
1095        if (modlist[curmod] == (LDAPMod *) -1) {
1096                if (!(modlist = TALLOC_REALLOC_ARRAY(ctx, modlist, LDAPMod *,
1097                                curmod+ADS_MODLIST_ALLOC_SIZE+1)))
1098                        return ADS_ERROR(LDAP_NO_MEMORY);
1099                memset(&modlist[curmod], 0, 
1100                       ADS_MODLIST_ALLOC_SIZE*sizeof(LDAPMod *));
1101                modlist[curmod+ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1102                *mods = (ADS_MODLIST)modlist;
1103        }
1104               
1105        if (!(modlist[curmod] = TALLOC_ZERO_P(ctx, LDAPMod)))
1106                return ADS_ERROR(LDAP_NO_MEMORY);
1107        modlist[curmod]->mod_type = talloc_strdup(ctx, name);
1108        if (mod_op & LDAP_MOD_BVALUES) {
1109                modlist[curmod]->mod_bvalues = ber_values;
1110        } else if (mod_op & LDAP_MOD_DELETE) {
1111                modlist[curmod]->mod_values = NULL;
1112        } else {
1113                modlist[curmod]->mod_values = char_values;
1114        }
1115
1116        modlist[curmod]->mod_op = mod_op;
1117        return ADS_ERROR(LDAP_SUCCESS);
1118}
1119
1120/**
1121 * Add a single string value to a mod list
1122 * @param ctx An initialized TALLOC_CTX
1123 * @param mods An initialized ADS_MODLIST
1124 * @param name The attribute name to add
1125 * @param val The value to add - NULL means DELETE
1126 * @return ADS STATUS indicating success of add
1127 **/
1128ADS_STATUS ads_mod_str(TALLOC_CTX *ctx, ADS_MODLIST *mods, 
1129                       const char *name, const char *val)
1130{
1131        const char *values[2];
1132
1133        values[0] = val;
1134        values[1] = NULL;
1135
1136        if (!val)
1137                return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1138        return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE, name, values);
1139}
1140
1141/**
1142 * Add an array of string values to a mod list
1143 * @param ctx An initialized TALLOC_CTX
1144 * @param mods An initialized ADS_MODLIST
1145 * @param name The attribute name to add
1146 * @param vals The array of string values to add - NULL means DELETE
1147 * @return ADS STATUS indicating success of add
1148 **/
1149ADS_STATUS ads_mod_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1150                           const char *name, const char **vals)
1151{
1152        if (!vals)
1153                return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1154        return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE, 
1155                               name, (const void **) vals);
1156}
1157
1158#if 0
1159/**
1160 * Add a single ber-encoded value to a mod list
1161 * @param ctx An initialized TALLOC_CTX
1162 * @param mods An initialized ADS_MODLIST
1163 * @param name The attribute name to add
1164 * @param val The value to add - NULL means DELETE
1165 * @return ADS STATUS indicating success of add
1166 **/
1167static ADS_STATUS ads_mod_ber(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1168                              const char *name, const struct berval *val)
1169{
1170        const struct berval *values[2];
1171
1172        values[0] = val;
1173        values[1] = NULL;
1174        if (!val)
1175                return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1176        return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE|LDAP_MOD_BVALUES,
1177                               name, (const void **) values);
1178}
1179#endif
1180
1181/**
1182 * Perform an ldap modify
1183 * @param ads connection to ads server
1184 * @param mod_dn DistinguishedName to modify
1185 * @param mods list of modifications to perform
1186 * @return status of modify
1187 **/
1188ADS_STATUS ads_gen_mod(ADS_STRUCT *ads, const char *mod_dn, ADS_MODLIST mods)
1189{
1190        int ret,i;
1191        char *utf8_dn = NULL;
1192        /*
1193           this control is needed to modify that contains a currently
1194           non-existent attribute (but allowable for the object) to run
1195        */
1196        LDAPControl PermitModify = {
1197                CONST_DISCARD(char *, ADS_PERMIT_MODIFY_OID),
1198                {0, NULL},
1199                (char) 1};
1200        LDAPControl *controls[2];
1201
1202        controls[0] = &PermitModify;
1203        controls[1] = NULL;
1204
1205        if (push_utf8_allocate(&utf8_dn, mod_dn) == -1) {
1206                return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1207        }
1208
1209        /* find the end of the list, marked by NULL or -1 */
1210        for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1211        /* make sure the end of the list is NULL */
1212        mods[i] = NULL;
1213        ret = ldap_modify_ext_s(ads->ld, utf8_dn,
1214                                (LDAPMod **) mods, controls, NULL);
1215        SAFE_FREE(utf8_dn);
1216        return ADS_ERROR(ret);
1217}
1218
1219/**
1220 * Perform an ldap add
1221 * @param ads connection to ads server
1222 * @param new_dn DistinguishedName to add
1223 * @param mods list of attributes and values for DN
1224 * @return status of add
1225 **/
1226ADS_STATUS ads_gen_add(ADS_STRUCT *ads, const char *new_dn, ADS_MODLIST mods)
1227{
1228        int ret, i;
1229        char *utf8_dn = NULL;
1230
1231        if (push_utf8_allocate(&utf8_dn, new_dn) == -1) {
1232                DEBUG(1, ("ads_gen_add: push_utf8_allocate failed!"));
1233                return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1234        }
1235       
1236        /* find the end of the list, marked by NULL or -1 */
1237        for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1238        /* make sure the end of the list is NULL */
1239        mods[i] = NULL;
1240
1241        ret = ldap_add_s(ads->ld, utf8_dn, (LDAPMod**)mods);
1242        SAFE_FREE(utf8_dn);
1243        return ADS_ERROR(ret);
1244}
1245
1246/**
1247 * Delete a DistinguishedName
1248 * @param ads connection to ads server
1249 * @param new_dn DistinguishedName to delete
1250 * @return status of delete
1251 **/
1252ADS_STATUS ads_del_dn(ADS_STRUCT *ads, char *del_dn)
1253{
1254        int ret;
1255        char *utf8_dn = NULL;
1256        if (push_utf8_allocate(&utf8_dn, del_dn) == -1) {
1257                DEBUG(1, ("ads_del_dn: push_utf8_allocate failed!"));
1258                return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1259        }
1260       
1261        ret = ldap_delete_s(ads->ld, utf8_dn);
1262        SAFE_FREE(utf8_dn);
1263        return ADS_ERROR(ret);
1264}
1265
1266/**
1267 * Build an org unit string
1268 *  if org unit is Computers or blank then assume a container, otherwise
1269 *  assume a / separated list of organisational units.
1270 * jmcd: '\' is now used for escapes so certain chars can be in the ou (e.g. #)
1271 * @param ads connection to ads server
1272 * @param org_unit Organizational unit
1273 * @return org unit string - caller must free
1274 **/
1275char *ads_ou_string(ADS_STRUCT *ads, const char *org_unit)
1276{
1277        char *ret = NULL;
1278
1279        if (!org_unit || !*org_unit) {
1280
1281                ret = ads_default_ou_string(ads, WELL_KNOWN_GUID_COMPUTERS);
1282
1283                /* samba4 might not yet respond to a wellknownobject-query */
1284                return ret ? ret : SMB_STRDUP("cn=Computers");
1285        }
1286       
1287        if (strequal(org_unit, "Computers")) {
1288                return SMB_STRDUP("cn=Computers");
1289        }
1290
1291        /* jmcd: removed "\\" from the separation chars, because it is
1292           needed as an escape for chars like '#' which are valid in an
1293           OU name */
1294        return ads_build_path(org_unit, "/", "ou=", 1);
1295}
1296
1297/**
1298 * Get a org unit string for a well-known GUID
1299 * @param ads connection to ads server
1300 * @param wknguid Well known GUID
1301 * @return org unit string - caller must free
1302 **/
1303char *ads_default_ou_string(ADS_STRUCT *ads, const char *wknguid)
1304{
1305        ADS_STATUS status;
1306        LDAPMessage *res = NULL;
1307        char *base, *wkn_dn = NULL, *ret = NULL, **wkn_dn_exp = NULL,
1308                **bind_dn_exp = NULL;
1309        const char *attrs[] = {"distinguishedName", NULL};
1310        int new_ln, wkn_ln, bind_ln, i;
1311
1312        if (wknguid == NULL) {
1313                return NULL;
1314        }
1315
1316        if (asprintf(&base, "<WKGUID=%s,%s>", wknguid, ads->config.bind_path ) == -1) {
1317                DEBUG(1, ("asprintf failed!\n"));
1318                return NULL;
1319        }
1320
1321        status = ads_search_dn(ads, &res, base, attrs);
1322        if (!ADS_ERR_OK(status)) {
1323                DEBUG(1,("Failed while searching for: %s\n", base));
1324                goto out;
1325        }
1326
1327        if (ads_count_replies(ads, res) != 1) {
1328                goto out;
1329        }
1330
1331        /* substitute the bind-path from the well-known-guid-search result */
1332        wkn_dn = ads_get_dn(ads, res);
1333        if (!wkn_dn) {
1334                goto out;
1335        }
1336
1337        wkn_dn_exp = ldap_explode_dn(wkn_dn, 0);
1338        if (!wkn_dn_exp) {
1339                goto out;
1340        }
1341
1342        bind_dn_exp = ldap_explode_dn(ads->config.bind_path, 0);
1343        if (!bind_dn_exp) {
1344                goto out;
1345        }
1346
1347        for (wkn_ln=0; wkn_dn_exp[wkn_ln]; wkn_ln++)
1348                ;
1349        for (bind_ln=0; bind_dn_exp[bind_ln]; bind_ln++)
1350                ;
1351
1352        new_ln = wkn_ln - bind_ln;
1353
1354        ret = SMB_STRDUP(wkn_dn_exp[0]);
1355        if (!ret) {
1356                goto out;
1357        }
1358
1359        for (i=1; i < new_ln; i++) {
1360                char *s = NULL;
1361               
1362                if (asprintf(&s, "%s,%s", ret, wkn_dn_exp[i]) == -1) {
1363                        SAFE_FREE(ret);
1364                        goto out;
1365                }
1366
1367                SAFE_FREE(ret);
1368                ret = SMB_STRDUP(s);
1369                free(s);
1370                if (!ret) {
1371                        goto out;
1372                }
1373        }
1374
1375 out:
1376        SAFE_FREE(base);
1377        ads_msgfree(ads, res);
1378        ads_memfree(ads, wkn_dn);
1379        if (wkn_dn_exp) {
1380                ldap_value_free(wkn_dn_exp);
1381        }
1382        if (bind_dn_exp) {
1383                ldap_value_free(bind_dn_exp);
1384        }
1385
1386        return ret;
1387}
1388
1389/**
1390 * Adds (appends) an item to an attribute array, rather then
1391 * replacing the whole list
1392 * @param ctx An initialized TALLOC_CTX
1393 * @param mods An initialized ADS_MODLIST
1394 * @param name name of the ldap attribute to append to
1395 * @param vals an array of values to add
1396 * @return status of addition
1397 **/
1398
1399ADS_STATUS ads_add_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1400                                const char *name, const char **vals)
1401{
1402        return ads_modlist_add(ctx, mods, LDAP_MOD_ADD, name,
1403                               (const void *) vals);
1404}
1405
1406/**
1407 * Determines the computer account's current KVNO via an LDAP lookup
1408 * @param ads An initialized ADS_STRUCT
1409 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1410 * @return the kvno for the computer account, or -1 in case of a failure.
1411 **/
1412
1413uint32 ads_get_kvno(ADS_STRUCT *ads, const char *machine_name)
1414{
1415        LDAPMessage *res = NULL;
1416        uint32 kvno = (uint32)-1;      /* -1 indicates a failure */
1417        char *filter;
1418        const char *attrs[] = {"msDS-KeyVersionNumber", NULL};
1419        char *dn_string = NULL;
1420        ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS);
1421
1422        DEBUG(5,("ads_get_kvno: Searching for host %s\n", machine_name));
1423        if (asprintf(&filter, "(samAccountName=%s$)", machine_name) == -1) {
1424                return kvno;
1425        }
1426        ret = ads_search(ads, &res, filter, attrs);
1427        SAFE_FREE(filter);
1428        if (!ADS_ERR_OK(ret) && ads_count_replies(ads, res)) {
1429                DEBUG(1,("ads_get_kvno: Computer Account For %s not found.\n", machine_name));
1430                ads_msgfree(ads, res);
1431                return kvno;
1432        }
1433
1434        dn_string = ads_get_dn(ads, res);
1435        if (!dn_string) {
1436                DEBUG(0,("ads_get_kvno: out of memory.\n"));
1437                ads_msgfree(ads, res);
1438                return kvno;
1439        }
1440        DEBUG(5,("ads_get_kvno: Using: %s\n", dn_string));
1441        ads_memfree(ads, dn_string);
1442
1443        /* ---------------------------------------------------------
1444         * 0 is returned as a default KVNO from this point on...
1445         * This is done because Windows 2000 does not support key
1446         * version numbers.  Chances are that a failure in the next
1447         * step is simply due to Windows 2000 being used for a
1448         * domain controller. */
1449        kvno = 0;
1450
1451        if (!ads_pull_uint32(ads, res, "msDS-KeyVersionNumber", &kvno)) {
1452                DEBUG(3,("ads_get_kvno: Error Determining KVNO!\n"));
1453                DEBUG(3,("ads_get_kvno: Windows 2000 does not support KVNO's, so this may be normal.\n"));
1454                ads_msgfree(ads, res);
1455                return kvno;
1456        }
1457
1458        /* Success */
1459        DEBUG(5,("ads_get_kvno: Looked Up KVNO of: %d\n", kvno));
1460        ads_msgfree(ads, res);
1461        return kvno;
1462}
1463
1464/**
1465 * This clears out all registered spn's for a given hostname
1466 * @param ads An initilaized ADS_STRUCT
1467 * @param machine_name the NetBIOS name of the computer.
1468 * @return 0 upon success, non-zero otherwise.
1469 **/
1470
1471ADS_STATUS ads_clear_service_principal_names(ADS_STRUCT *ads, const char *machine_name)
1472{
1473        TALLOC_CTX *ctx;
1474        LDAPMessage *res = NULL;
1475        ADS_MODLIST mods;
1476        const char *servicePrincipalName[1] = {NULL};
1477        ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS);
1478        char *dn_string = NULL;
1479
1480        ret = ads_find_machine_acct(ads, &res, machine_name);
1481        if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
1482                DEBUG(5,("ads_clear_service_principal_names: WARNING: Host Account for %s not found... skipping operation.\n", machine_name));
1483                DEBUG(5,("ads_clear_service_principal_names: WARNING: Service Principals for %s have NOT been cleared.\n", machine_name));
1484                ads_msgfree(ads, res);
1485                return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1486        }
1487
1488        DEBUG(5,("ads_clear_service_principal_names: Host account for %s found\n", machine_name));
1489        ctx = talloc_init("ads_clear_service_principal_names");
1490        if (!ctx) {
1491                ads_msgfree(ads, res);
1492                return ADS_ERROR(LDAP_NO_MEMORY);
1493        }
1494
1495        if (!(mods = ads_init_mods(ctx))) {
1496                talloc_destroy(ctx);
1497                ads_msgfree(ads, res);
1498                return ADS_ERROR(LDAP_NO_MEMORY);
1499        }
1500        ret = ads_mod_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1501        if (!ADS_ERR_OK(ret)) {
1502                DEBUG(1,("ads_clear_service_principal_names: Error creating strlist.\n"));
1503                ads_msgfree(ads, res);
1504                talloc_destroy(ctx);
1505                return ret;
1506        }
1507        dn_string = ads_get_dn(ads, res);
1508        if (!dn_string) {
1509                talloc_destroy(ctx);
1510                ads_msgfree(ads, res);
1511                return ADS_ERROR(LDAP_NO_MEMORY);
1512        }
1513        ret = ads_gen_mod(ads, dn_string, mods);
1514        ads_memfree(ads,dn_string);
1515        if (!ADS_ERR_OK(ret)) {
1516                DEBUG(1,("ads_clear_service_principal_names: Error: Updating Service Principals for machine %s in LDAP\n",
1517                        machine_name));
1518                ads_msgfree(ads, res);
1519                talloc_destroy(ctx);
1520                return ret;
1521        }
1522
1523        ads_msgfree(ads, res);
1524        talloc_destroy(ctx);
1525        return ret;
1526}
1527
1528/**
1529 * This adds a service principal name to an existing computer account
1530 * (found by hostname) in AD.
1531 * @param ads An initialized ADS_STRUCT
1532 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1533 * @param my_fqdn The fully qualified DNS name of the machine
1534 * @param spn A string of the service principal to add, i.e. 'host'
1535 * @return 0 upon sucess, or non-zero if a failure occurs
1536 **/
1537
1538ADS_STATUS ads_add_service_principal_name(ADS_STRUCT *ads, const char *machine_name, 
1539                                          const char *my_fqdn, const char *spn)
1540{
1541        ADS_STATUS ret;
1542        TALLOC_CTX *ctx;
1543        LDAPMessage *res = NULL;
1544        char *psp1, *psp2;
1545        ADS_MODLIST mods;
1546        char *dn_string = NULL;
1547        const char *servicePrincipalName[3] = {NULL, NULL, NULL};
1548
1549        ret = ads_find_machine_acct(ads, &res, machine_name);
1550        if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
1551                DEBUG(1,("ads_add_service_principal_name: WARNING: Host Account for %s not found... skipping operation.\n",
1552                        machine_name));
1553                DEBUG(1,("ads_add_service_principal_name: WARNING: Service Principal '%s/%s@%s' has NOT been added.\n",
1554                        spn, machine_name, ads->config.realm));
1555                ads_msgfree(ads, res);
1556                return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1557        }
1558
1559        DEBUG(1,("ads_add_service_principal_name: Host account for %s found\n", machine_name));
1560        if (!(ctx = talloc_init("ads_add_service_principal_name"))) {
1561                ads_msgfree(ads, res);
1562                return ADS_ERROR(LDAP_NO_MEMORY);
1563        }
1564
1565        /* add short name spn */
1566       
1567        if ( (psp1 = talloc_asprintf(ctx, "%s/%s", spn, machine_name)) == NULL ) {
1568                talloc_destroy(ctx);
1569                ads_msgfree(ads, res);
1570                return ADS_ERROR(LDAP_NO_MEMORY);
1571        }
1572        strupper_m(psp1);
1573        strlower_m(&psp1[strlen(spn)]);
1574        servicePrincipalName[0] = psp1;
1575       
1576        DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n", 
1577                psp1, machine_name));
1578
1579
1580        /* add fully qualified spn */
1581       
1582        if ( (psp2 = talloc_asprintf(ctx, "%s/%s", spn, my_fqdn)) == NULL ) {
1583                ret = ADS_ERROR(LDAP_NO_MEMORY);
1584                goto out;
1585        }
1586        strupper_m(psp2);
1587        strlower_m(&psp2[strlen(spn)]);
1588        servicePrincipalName[1] = psp2;
1589
1590        DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n", 
1591                psp2, machine_name));
1592
1593        if ( (mods = ads_init_mods(ctx)) == NULL ) {
1594                ret = ADS_ERROR(LDAP_NO_MEMORY);
1595                goto out;
1596        }
1597       
1598        ret = ads_add_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1599        if (!ADS_ERR_OK(ret)) {
1600                DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
1601                goto out;
1602        }
1603       
1604        if ( (dn_string = ads_get_dn(ads, res)) == NULL ) {
1605                ret = ADS_ERROR(LDAP_NO_MEMORY);
1606                goto out;
1607        }
1608       
1609        ret = ads_gen_mod(ads, dn_string, mods);
1610        ads_memfree(ads,dn_string);
1611        if (!ADS_ERR_OK(ret)) {
1612                DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
1613                goto out;
1614        }
1615
1616 out:
1617        TALLOC_FREE( ctx );
1618        ads_msgfree(ads, res);
1619        return ret;
1620}
1621
1622/**
1623 * adds a machine account to the ADS server
1624 * @param ads An intialized ADS_STRUCT
1625 * @param machine_name - the NetBIOS machine name of this account.
1626 * @param account_type A number indicating the type of account to create
1627 * @param org_unit The LDAP path in which to place this account
1628 * @return 0 upon success, or non-zero otherwise
1629**/
1630
1631ADS_STATUS ads_create_machine_acct(ADS_STRUCT *ads, const char *machine_name, 
1632                                   const char *org_unit)
1633{
1634        ADS_STATUS ret;
1635        char *samAccountName, *controlstr;
1636        TALLOC_CTX *ctx;
1637        ADS_MODLIST mods;
1638        char *machine_escaped = NULL;
1639        char *new_dn;
1640        const char *objectClass[] = {"top", "person", "organizationalPerson",
1641                                     "user", "computer", NULL};
1642        LDAPMessage *res = NULL;
1643        uint32 acct_control = ( UF_WORKSTATION_TRUST_ACCOUNT |\
1644                                UF_DONT_EXPIRE_PASSWD |\
1645                                UF_ACCOUNTDISABLE );
1646                             
1647        if (!(ctx = talloc_init("ads_add_machine_acct")))
1648                return ADS_ERROR(LDAP_NO_MEMORY);
1649
1650        ret = ADS_ERROR(LDAP_NO_MEMORY);
1651
1652        machine_escaped = escape_rdn_val_string_alloc(machine_name);
1653        if (!machine_escaped) {
1654                goto done;
1655        }
1656
1657        new_dn = talloc_asprintf(ctx, "cn=%s,%s", machine_escaped, org_unit);
1658        samAccountName = talloc_asprintf(ctx, "%s$", machine_name);
1659
1660        if ( !new_dn || !samAccountName ) {
1661                goto done;
1662        }
1663       
1664#ifndef ENCTYPE_ARCFOUR_HMAC
1665        acct_control |= UF_USE_DES_KEY_ONLY;
1666#endif
1667
1668        if (!(controlstr = talloc_asprintf(ctx, "%u", acct_control))) {
1669                goto done;
1670        }
1671
1672        if (!(mods = ads_init_mods(ctx))) {
1673                goto done;
1674        }
1675       
1676        ads_mod_str(ctx, &mods, "cn", machine_name);
1677        ads_mod_str(ctx, &mods, "sAMAccountName", samAccountName);
1678        ads_mod_strlist(ctx, &mods, "objectClass", objectClass);
1679        ads_mod_str(ctx, &mods, "userAccountControl", controlstr);
1680
1681        ret = ads_gen_add(ads, new_dn, mods);
1682
1683done:
1684        SAFE_FREE(machine_escaped);
1685        ads_msgfree(ads, res);
1686        talloc_destroy(ctx);
1687       
1688        return ret;
1689}
1690
1691/*
1692  dump a binary result from ldap
1693*/
1694static void dump_binary(const char *field, struct berval **values)
1695{
1696        int i, j;
1697        for (i=0; values[i]; i++) {
1698                printf("%s: ", field);
1699                for (j=0; j<values[i]->bv_len; j++) {
1700                        printf("%02X", (unsigned char)values[i]->bv_val[j]);
1701                }
1702                printf("\n");
1703        }
1704}
1705
1706static void dump_guid(const char *field, struct berval **values)
1707{
1708        int i;
1709        UUID_FLAT guid;
1710        for (i=0; values[i]; i++) {
1711                memcpy(guid.info, values[i]->bv_val, sizeof(guid.info));
1712                printf("%s: %s\n", field, 
1713                       smb_uuid_string_static(smb_uuid_unpack_static(guid)));
1714        }
1715}
1716
1717/*
1718  dump a sid result from ldap
1719*/
1720static void dump_sid(const char *field, struct berval **values)
1721{
1722        int i;
1723        for (i=0; values[i]; i++) {
1724                DOM_SID sid;
1725                sid_parse(values[i]->bv_val, values[i]->bv_len, &sid);
1726                printf("%s: %s\n", field, sid_string_static(&sid));
1727        }
1728}
1729
1730/*
1731  dump ntSecurityDescriptor
1732*/
1733static void dump_sd(const char *filed, struct berval **values)
1734{
1735        prs_struct ps;
1736       
1737        SEC_DESC   *psd = 0;
1738        TALLOC_CTX *ctx = 0;
1739
1740        if (!(ctx = talloc_init("sec_io_desc")))
1741                return;
1742
1743        /* prepare data */
1744        prs_init(&ps, values[0]->bv_len, ctx, UNMARSHALL);
1745        prs_copy_data_in(&ps, values[0]->bv_val, values[0]->bv_len);
1746        prs_set_offset(&ps,0);
1747
1748        /* parse secdesc */
1749        if (!sec_io_desc("sd", &psd, &ps, 1)) {
1750                prs_mem_free(&ps);
1751                talloc_destroy(ctx);
1752                return;
1753        }
1754        if (psd) ads_disp_sd(psd);
1755
1756        prs_mem_free(&ps);
1757        talloc_destroy(ctx);
1758}
1759
1760/*
1761  dump a string result from ldap
1762*/
1763static void dump_string(const char *field, char **values)
1764{
1765        int i;
1766        for (i=0; values[i]; i++) {
1767                printf("%s: %s\n", field, values[i]);
1768        }
1769}
1770
1771/*
1772  dump a field from LDAP on stdout
1773  used for debugging
1774*/
1775
1776static BOOL ads_dump_field(char *field, void **values, void *data_area)
1777{
1778        const struct {
1779                const char *name;
1780                BOOL string;
1781                void (*handler)(const char *, struct berval **);
1782        } handlers[] = {
1783                {"objectGUID", False, dump_guid},
1784                {"netbootGUID", False, dump_guid},
1785                {"nTSecurityDescriptor", False, dump_sd},
1786                {"dnsRecord", False, dump_binary},
1787                {"objectSid", False, dump_sid},
1788                {"tokenGroups", False, dump_sid},
1789                {"tokenGroupsNoGCAcceptable", False, dump_sid},
1790                {"tokengroupsGlobalandUniversal", False, dump_sid},
1791                {"mS-DS-CreatorSID", False, dump_sid},
1792                {NULL, True, NULL}
1793        };
1794        int i;
1795
1796        if (!field) { /* must be end of an entry */
1797                printf("\n");
1798                return False;
1799        }
1800
1801        for (i=0; handlers[i].name; i++) {
1802                if (StrCaseCmp(handlers[i].name, field) == 0) {
1803                        if (!values) /* first time, indicate string or not */
1804                                return handlers[i].string;
1805                        handlers[i].handler(field, (struct berval **) values);
1806                        break;
1807                }
1808        }
1809        if (!handlers[i].name) {
1810                if (!values) /* first time, indicate string conversion */
1811                        return True;
1812                dump_string(field, (char **)values);
1813        }
1814        return False;
1815}
1816
1817/**
1818 * Dump a result from LDAP on stdout
1819 *  used for debugging
1820 * @param ads connection to ads server
1821 * @param res Results to dump
1822 **/
1823
1824 void ads_dump(ADS_STRUCT *ads, LDAPMessage *res)
1825{
1826        ads_process_results(ads, res, ads_dump_field, NULL);
1827}
1828
1829/**
1830 * Walk through results, calling a function for each entry found.
1831 *  The function receives a field name, a berval * array of values,
1832 *  and a data area passed through from the start.  The function is
1833 *  called once with null for field and values at the end of each
1834 *  entry.
1835 * @param ads connection to ads server
1836 * @param res Results to process
1837 * @param fn Function for processing each result
1838 * @param data_area user-defined area to pass to function
1839 **/
1840 void ads_process_results(ADS_STRUCT *ads, LDAPMessage *res,
1841                          BOOL(*fn)(char *, void **, void *),
1842                          void *data_area)
1843{
1844        LDAPMessage *msg;
1845        TALLOC_CTX *ctx;
1846
1847        if (!(ctx = talloc_init("ads_process_results")))
1848                return;
1849
1850        for (msg = ads_first_entry(ads, res); msg; 
1851             msg = ads_next_entry(ads, msg)) {
1852                char *utf8_field;
1853                BerElement *b;
1854       
1855                for (utf8_field=ldap_first_attribute(ads->ld,
1856                                                     (LDAPMessage *)msg,&b); 
1857                     utf8_field;
1858                     utf8_field=ldap_next_attribute(ads->ld,
1859                                                    (LDAPMessage *)msg,b)) {
1860                        struct berval **ber_vals;
1861                        char **str_vals, **utf8_vals;
1862                        char *field;
1863                        BOOL string; 
1864
1865                        pull_utf8_talloc(ctx, &field, utf8_field);
1866                        string = fn(field, NULL, data_area);
1867
1868                        if (string) {
1869                                utf8_vals = ldap_get_values(ads->ld,
1870                                                 (LDAPMessage *)msg, field);
1871                                str_vals = ads_pull_strvals(ctx, 
1872                                                  (const char **) utf8_vals);
1873                                fn(field, (void **) str_vals, data_area);
1874                                ldap_value_free(utf8_vals);
1875                        } else {
1876                                ber_vals = ldap_get_values_len(ads->ld, 
1877                                                 (LDAPMessage *)msg, field);
1878                                fn(field, (void **) ber_vals, data_area);
1879
1880                                ldap_value_free_len(ber_vals);
1881                        }
1882                        ldap_memfree(utf8_field);
1883                }
1884                ber_free(b, 0);
1885                talloc_free_children(ctx);
1886                fn(NULL, NULL, data_area); /* completed an entry */
1887
1888        }
1889        talloc_destroy(ctx);
1890}
1891
1892/**
1893 * count how many replies are in a LDAPMessage
1894 * @param ads connection to ads server
1895 * @param res Results to count
1896 * @return number of replies
1897 **/
1898int ads_count_replies(ADS_STRUCT *ads, void *res)
1899{
1900        return ldap_count_entries(ads->ld, (LDAPMessage *)res);
1901}
1902
1903/**
1904 * pull the first entry from a ADS result
1905 * @param ads connection to ads server
1906 * @param res Results of search
1907 * @return first entry from result
1908 **/
1909 LDAPMessage *ads_first_entry(ADS_STRUCT *ads, LDAPMessage *res)
1910{
1911        return ldap_first_entry(ads->ld, res);
1912}
1913
1914/**
1915 * pull the next entry from a ADS result
1916 * @param ads connection to ads server
1917 * @param res Results of search
1918 * @return next entry from result
1919 **/
1920 LDAPMessage *ads_next_entry(ADS_STRUCT *ads, LDAPMessage *res)
1921{
1922        return ldap_next_entry(ads->ld, res);
1923}
1924
1925/**
1926 * pull a single string from a ADS result
1927 * @param ads connection to ads server
1928 * @param mem_ctx TALLOC_CTX to use for allocating result string
1929 * @param msg Results of search
1930 * @param field Attribute to retrieve
1931 * @return Result string in talloc context
1932 **/
1933 char *ads_pull_string(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, LDAPMessage *msg,
1934                       const char *field)
1935{
1936        char **values;
1937        char *ret = NULL;
1938        char *ux_string;
1939        size_t rc;
1940
1941        values = ldap_get_values(ads->ld, msg, field);
1942        if (!values)
1943                return NULL;
1944       
1945        if (values[0]) {
1946                rc = pull_utf8_talloc(mem_ctx, &ux_string, 
1947                                      values[0]);
1948                if (rc != (size_t)-1)
1949                        ret = ux_string;
1950               
1951        }
1952        ldap_value_free(values);
1953        return ret;
1954}
1955
1956/**
1957 * pull an array of strings from a ADS result
1958 * @param ads connection to ads server
1959 * @param mem_ctx TALLOC_CTX to use for allocating result string
1960 * @param msg Results of search
1961 * @param field Attribute to retrieve
1962 * @return Result strings in talloc context
1963 **/
1964 char **ads_pull_strings(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
1965                         LDAPMessage *msg, const char *field,
1966                         size_t *num_values)
1967{
1968        char **values;
1969        char **ret = NULL;
1970        int i;
1971
1972        values = ldap_get_values(ads->ld, msg, field);
1973        if (!values)
1974                return NULL;
1975
1976        *num_values = ldap_count_values(values);
1977
1978        ret = TALLOC_ARRAY(mem_ctx, char *, *num_values + 1);
1979        if (!ret) {
1980                ldap_value_free(values);
1981                return NULL;
1982        }
1983
1984        for (i=0;i<*num_values;i++) {
1985                if (pull_utf8_talloc(mem_ctx, &ret[i], values[i]) == -1) {
1986                        ldap_value_free(values);
1987                        return NULL;
1988                }
1989        }
1990        ret[i] = NULL;
1991
1992        ldap_value_free(values);
1993        return ret;
1994}
1995
1996/**
1997 * pull an array of strings from a ADS result
1998 *  (handle large multivalue attributes with range retrieval)
1999 * @param ads connection to ads server
2000 * @param mem_ctx TALLOC_CTX to use for allocating result string
2001 * @param msg Results of search
2002 * @param field Attribute to retrieve
2003 * @param current_strings strings returned by a previous call to this function
2004 * @param next_attribute The next query should ask for this attribute
2005 * @param num_values How many values did we get this time?
2006 * @param more_values Are there more values to get?
2007 * @return Result strings in talloc context
2008 **/
2009 char **ads_pull_strings_range(ADS_STRUCT *ads, 
2010                               TALLOC_CTX *mem_ctx,
2011                               LDAPMessage *msg, const char *field,
2012                               char **current_strings,
2013                               const char **next_attribute,
2014                               size_t *num_strings,
2015                               BOOL *more_strings)
2016{
2017        char *attr;
2018        char *expected_range_attrib, *range_attr;
2019        BerElement *ptr = NULL;
2020        char **strings;
2021        char **new_strings;
2022        size_t num_new_strings;
2023        unsigned long int range_start;
2024        unsigned long int range_end;
2025       
2026        /* we might have been given the whole lot anyway */
2027        if ((strings = ads_pull_strings(ads, mem_ctx, msg, field, num_strings))) {
2028                *more_strings = False;
2029                return strings;
2030        }
2031
2032        expected_range_attrib = talloc_asprintf(mem_ctx, "%s;Range=", field);
2033
2034        /* look for Range result */
2035        for (attr = ldap_first_attribute(ads->ld, (LDAPMessage *)msg, &ptr); 
2036             attr; 
2037             attr = ldap_next_attribute(ads->ld, (LDAPMessage *)msg, ptr)) {
2038                /* we ignore the fact that this is utf8, as all attributes are ascii... */
2039                if (strnequal(attr, expected_range_attrib, strlen(expected_range_attrib))) {
2040                        range_attr = attr;
2041                        break;
2042                }
2043                ldap_memfree(attr);
2044        }
2045        if (!attr) {
2046                ber_free(ptr, 0);
2047                /* nothing here - this field is just empty */
2048                *more_strings = False;
2049                return NULL;
2050        }
2051       
2052        if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-%lu", 
2053                   &range_start, &range_end) == 2) {
2054                *more_strings = True;
2055        } else {
2056                if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-*", 
2057                           &range_start) == 1) {
2058                        *more_strings = False;
2059                } else {
2060                        DEBUG(1, ("ads_pull_strings_range:  Cannot parse Range attriubte (%s)\n", 
2061                                  range_attr));
2062                        ldap_memfree(range_attr);
2063                        *more_strings = False;
2064                        return NULL;
2065                }
2066        }
2067
2068        if ((*num_strings) != range_start) {
2069                DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) doesn't start at %u, but at %lu"
2070                          " - aborting range retreival\n",
2071                          range_attr, (unsigned int)(*num_strings) + 1, range_start));
2072                ldap_memfree(range_attr);
2073                *more_strings = False;
2074                return NULL;
2075        }
2076
2077        new_strings = ads_pull_strings(ads, mem_ctx, msg, range_attr, &num_new_strings);
2078       
2079        if (*more_strings && ((*num_strings + num_new_strings) != (range_end + 1))) {
2080                DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) tells us we have %lu "
2081                          "strings in this bunch, but we only got %lu - aborting range retreival\n",
2082                          range_attr, (unsigned long int)range_end - range_start + 1, 
2083                          (unsigned long int)num_new_strings));
2084                ldap_memfree(range_attr);
2085                *more_strings = False;
2086                return NULL;
2087        }
2088
2089        strings = TALLOC_REALLOC_ARRAY(mem_ctx, current_strings, char *,
2090                                 *num_strings + num_new_strings);
2091       
2092        if (strings == NULL) {
2093                ldap_memfree(range_attr);
2094                *more_strings = False;
2095                return NULL;
2096        }
2097       
2098        if (new_strings && num_new_strings) {
2099                memcpy(&strings[*num_strings], new_strings,
2100                       sizeof(*new_strings) * num_new_strings);
2101        }
2102
2103        (*num_strings) += num_new_strings;
2104
2105        if (*more_strings) {
2106                *next_attribute = talloc_asprintf(mem_ctx,
2107                                                  "%s;range=%d-*", 
2108                                                  field,
2109                                                  (int)*num_strings);
2110               
2111                if (!*next_attribute) {
2112                        DEBUG(1, ("talloc_asprintf for next attribute failed!\n"));
2113                        ldap_memfree(range_attr);
2114                        *more_strings = False;
2115                        return NULL;
2116                }
2117        }
2118
2119        ldap_memfree(range_attr);
2120
2121        return strings;
2122}
2123
2124/**
2125 * pull a single uint32 from a ADS result
2126 * @param ads connection to ads server
2127 * @param msg Results of search
2128 * @param field Attribute to retrieve
2129 * @param v Pointer to int to store result
2130 * @return boolean inidicating success
2131*/
2132 BOOL ads_pull_uint32(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
2133                      uint32 *v)
2134{
2135        char **values;
2136
2137        values = ldap_get_values(ads->ld, msg, field);
2138        if (!values)
2139                return False;
2140        if (!values[0]) {
2141                ldap_value_free(values);
2142                return False;
2143        }
2144
2145        *v = atoi(values[0]);
2146        ldap_value_free(values);
2147        return True;
2148}
2149
2150/**
2151 * pull a single objectGUID from an ADS result
2152 * @param ads connection to ADS server
2153 * @param msg results of search
2154 * @param guid 37-byte area to receive text guid
2155 * @return boolean indicating success
2156 **/
2157 BOOL ads_pull_guid(ADS_STRUCT *ads, LDAPMessage *msg, struct GUID *guid)
2158{
2159        char **values;
2160        UUID_FLAT flat_guid;
2161
2162        values = ldap_get_values(ads->ld, msg, "objectGUID");
2163        if (!values)
2164                return False;
2165       
2166        if (values[0]) {
2167                memcpy(&flat_guid.info, values[0], sizeof(UUID_FLAT));
2168                smb_uuid_unpack(flat_guid, guid);
2169                ldap_value_free(values);
2170                return True;
2171        }
2172        ldap_value_free(values);
2173        return False;
2174
2175}
2176
2177
2178/**
2179 * pull a single DOM_SID from a ADS result
2180 * @param ads connection to ads server
2181 * @param msg Results of search
2182 * @param field Attribute to retrieve
2183 * @param sid Pointer to sid to store result
2184 * @return boolean inidicating success
2185*/
2186 BOOL ads_pull_sid(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
2187                   DOM_SID *sid)
2188{
2189        struct berval **values;
2190        BOOL ret = False;
2191
2192        values = ldap_get_values_len(ads->ld, msg, field);
2193
2194        if (!values)
2195                return False;
2196
2197        if (values[0])
2198                ret = sid_parse(values[0]->bv_val, values[0]->bv_len, sid);
2199       
2200        ldap_value_free_len(values);
2201        return ret;
2202}
2203
2204/**
2205 * pull an array of DOM_SIDs from a ADS result
2206 * @param ads connection to ads server
2207 * @param mem_ctx TALLOC_CTX for allocating sid array
2208 * @param msg Results of search
2209 * @param field Attribute to retrieve
2210 * @param sids pointer to sid array to allocate
2211 * @return the count of SIDs pulled
2212 **/
2213 int ads_pull_sids(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2214                   LDAPMessage *msg, const char *field, DOM_SID **sids)
2215{
2216        struct berval **values;
2217        BOOL ret;
2218        int count, i;
2219
2220        values = ldap_get_values_len(ads->ld, msg, field);
2221
2222        if (!values)
2223                return 0;
2224
2225        for (i=0; values[i]; i++)
2226                /* nop */ ;
2227
2228        (*sids) = TALLOC_ARRAY(mem_ctx, DOM_SID, i);
2229        if (!(*sids)) {
2230                ldap_value_free_len(values);
2231                return 0;
2232        }
2233
2234        count = 0;
2235        for (i=0; values[i]; i++) {
2236                ret = sid_parse(values[i]->bv_val, values[i]->bv_len, &(*sids)[count]);
2237                if (ret) {
2238                        fstring sid;
2239                        DEBUG(10, ("pulling SID: %s\n", sid_to_string(sid, &(*sids)[count])));
2240                        count++;
2241                }
2242        }
2243       
2244        ldap_value_free_len(values);
2245        return count;
2246}
2247
2248/**
2249 * pull a SEC_DESC from a ADS result
2250 * @param ads connection to ads server
2251 * @param mem_ctx TALLOC_CTX for allocating sid array
2252 * @param msg Results of search
2253 * @param field Attribute to retrieve
2254 * @param sd Pointer to *SEC_DESC to store result (talloc()ed)
2255 * @return boolean inidicating success
2256*/
2257 BOOL ads_pull_sd(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2258                  LDAPMessage *msg, const char *field, SEC_DESC **sd)
2259{
2260        struct berval **values;
2261        BOOL ret = False;
2262
2263        values = ldap_get_values_len(ads->ld, msg, field);
2264
2265        if (!values) return False;
2266
2267        if (values[0]) {
2268                prs_struct ps;
2269                prs_init(&ps, values[0]->bv_len, mem_ctx, UNMARSHALL);
2270                prs_copy_data_in(&ps, values[0]->bv_val, values[0]->bv_len);
2271                prs_set_offset(&ps,0);
2272
2273                ret = sec_io_desc("sd", sd, &ps, 1);
2274                prs_mem_free(&ps);
2275        }
2276       
2277        ldap_value_free_len(values);
2278        return ret;
2279}
2280
2281/*
2282 * in order to support usernames longer than 21 characters we need to
2283 * use both the sAMAccountName and the userPrincipalName attributes
2284 * It seems that not all users have the userPrincipalName attribute set
2285 *
2286 * @param ads connection to ads server
2287 * @param mem_ctx TALLOC_CTX for allocating sid array
2288 * @param msg Results of search
2289 * @return the username
2290 */
2291 char *ads_pull_username(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2292                         LDAPMessage *msg)
2293{
2294#if 0   /* JERRY */
2295        char *ret, *p;
2296
2297        /* lookup_name() only works on the sAMAccountName to
2298           returning the username portion of userPrincipalName
2299           breaks winbindd_getpwnam() */
2300
2301        ret = ads_pull_string(ads, mem_ctx, msg, "userPrincipalName");
2302        if (ret && (p = strchr_m(ret, '@'))) {
2303                *p = 0;
2304                return ret;
2305        }
2306#endif
2307        return ads_pull_string(ads, mem_ctx, msg, "sAMAccountName");
2308}
2309
2310
2311/**
2312 * find the update serial number - this is the core of the ldap cache
2313 * @param ads connection to ads server
2314 * @param ads connection to ADS server
2315 * @param usn Pointer to retrieved update serial number
2316 * @return status of search
2317 **/
2318ADS_STATUS ads_USN(ADS_STRUCT *ads, uint32 *usn)
2319{
2320        const char *attrs[] = {"highestCommittedUSN", NULL};
2321        ADS_STATUS status;
2322        LDAPMessage *res;
2323
2324        status = ads_do_search_retry(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2325        if (!ADS_ERR_OK(status)) 
2326                return status;
2327
2328        if (ads_count_replies(ads, res) != 1) {
2329                ads_msgfree(ads, res);
2330                return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2331        }
2332
2333        if (!ads_pull_uint32(ads, res, "highestCommittedUSN", usn)) {
2334                ads_msgfree(ads, res);
2335                return ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
2336        }
2337
2338        ads_msgfree(ads, res);
2339        return ADS_SUCCESS;
2340}
2341
2342/* parse a ADS timestring - typical string is
2343   '20020917091222.0Z0' which means 09:12.22 17th September
2344   2002, timezone 0 */
2345static time_t ads_parse_time(const char *str)
2346{
2347        struct tm tm;
2348
2349        ZERO_STRUCT(tm);
2350
2351        if (sscanf(str, "%4d%2d%2d%2d%2d%2d", 
2352                   &tm.tm_year, &tm.tm_mon, &tm.tm_mday, 
2353                   &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) {
2354                return 0;
2355        }
2356        tm.tm_year -= 1900;
2357        tm.tm_mon -= 1;
2358
2359        return timegm(&tm);
2360}
2361
2362/********************************************************************
2363********************************************************************/
2364
2365ADS_STATUS ads_current_time(ADS_STRUCT *ads)
2366{
2367        const char *attrs[] = {"currentTime", NULL};
2368        ADS_STATUS status;
2369        LDAPMessage *res;
2370        char *timestr;
2371        TALLOC_CTX *ctx;
2372        ADS_STRUCT *ads_s = ads;
2373
2374        if (!(ctx = talloc_init("ads_current_time"))) {
2375                return ADS_ERROR(LDAP_NO_MEMORY);
2376        }
2377
2378        /* establish a new ldap tcp session if necessary */
2379
2380        if ( !ads->ld ) {
2381                if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup, 
2382                        ads->server.ldap_server )) == NULL )
2383                {
2384                        goto done;
2385                }
2386                ads_s->auth.flags = ADS_AUTH_ANON_BIND;
2387                status = ads_connect( ads_s );
2388                if ( !ADS_ERR_OK(status))
2389                        goto done;
2390        }
2391
2392        status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2393        if (!ADS_ERR_OK(status)) {
2394                goto done;
2395        }
2396
2397        timestr = ads_pull_string(ads_s, ctx, res, "currentTime");
2398        if (!timestr) {
2399                ads_msgfree(ads_s, res);
2400                status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2401                goto done;
2402        }
2403
2404        /* but save the time and offset in the original ADS_STRUCT */   
2405       
2406        ads->config.current_time = ads_parse_time(timestr);
2407
2408        if (ads->config.current_time != 0) {
2409                ads->auth.time_offset = ads->config.current_time - time(NULL);
2410                DEBUG(4,("time offset is %d seconds\n", ads->auth.time_offset));
2411        }
2412
2413        ads_msgfree(ads, res);
2414
2415        status = ADS_SUCCESS;
2416
2417done:
2418        /* free any temporary ads connections */
2419        if ( ads_s != ads ) {
2420                ads_destroy( &ads_s );
2421        }
2422        talloc_destroy(ctx);
2423
2424        return status;
2425}
2426
2427/********************************************************************
2428********************************************************************/
2429
2430ADS_STATUS ads_domain_func_level(ADS_STRUCT *ads, uint32 *val)
2431{
2432        const char *attrs[] = {"domainFunctionality", NULL};
2433        ADS_STATUS status;
2434        LDAPMessage *res;
2435        ADS_STRUCT *ads_s = ads;
2436       
2437        *val = DS_DOMAIN_FUNCTION_2000;
2438
2439        /* establish a new ldap tcp session if necessary */
2440
2441        if ( !ads->ld ) {
2442                if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup, 
2443                        ads->server.ldap_server )) == NULL )
2444                {
2445                        goto done;
2446                }
2447                ads_s->auth.flags = ADS_AUTH_ANON_BIND;
2448                status = ads_connect( ads_s );
2449                if ( !ADS_ERR_OK(status))
2450                        goto done;
2451        }
2452
2453        /* If the attribute does not exist assume it is a Windows 2000
2454           functional domain */
2455           
2456        status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2457        if (!ADS_ERR_OK(status)) {
2458                if ( status.err.rc == LDAP_NO_SUCH_ATTRIBUTE ) {
2459                        status = ADS_SUCCESS;
2460                }
2461                goto done;
2462        }
2463
2464        if ( !ads_pull_uint32(ads_s, res, "domainFunctionality", val) ) {
2465                DEBUG(5,("ads_domain_func_level: Failed to pull the domainFunctionality attribute.\n"));
2466        }
2467        DEBUG(3,("ads_domain_func_level: %d\n", *val));
2468
2469       
2470        ads_msgfree(ads, res);
2471
2472done:
2473        /* free any temporary ads connections */
2474        if ( ads_s != ads ) {
2475                ads_destroy( &ads_s );
2476        }
2477
2478        return status;
2479}
2480
2481/**
2482 * find the domain sid for our domain
2483 * @param ads connection to ads server
2484 * @param sid Pointer to domain sid
2485 * @return status of search
2486 **/
2487ADS_STATUS ads_domain_sid(ADS_STRUCT *ads, DOM_SID *sid)
2488{
2489        const char *attrs[] = {"objectSid", NULL};
2490        LDAPMessage *res;
2491        ADS_STATUS rc;
2492
2493        rc = ads_do_search_retry(ads, ads->config.bind_path, LDAP_SCOPE_BASE, "(objectclass=*)", 
2494                           attrs, &res);
2495        if (!ADS_ERR_OK(rc)) return rc;
2496        if (!ads_pull_sid(ads, res, "objectSid", sid)) {
2497                ads_msgfree(ads, res);
2498                return ADS_ERROR_SYSTEM(ENOENT);
2499        }
2500        ads_msgfree(ads, res);
2501       
2502        return ADS_SUCCESS;
2503}
2504
2505/**
2506 * find our site name
2507 * @param ads connection to ads server
2508 * @param mem_ctx Pointer to talloc context
2509 * @param site_name Pointer to the sitename
2510 * @return status of search
2511 **/
2512ADS_STATUS ads_site_dn(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char **site_name)
2513{
2514        ADS_STATUS status;
2515        LDAPMessage *res;
2516        const char *dn, *service_name;
2517        const char *attrs[] = { "dsServiceName", NULL };
2518
2519        status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2520        if (!ADS_ERR_OK(status)) {
2521                return status;
2522        }
2523
2524        service_name = ads_pull_string(ads, mem_ctx, res, "dsServiceName");
2525        if (service_name == NULL) {
2526                ads_msgfree(ads, res);
2527                return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2528        }
2529
2530        ads_msgfree(ads, res);
2531
2532        /* go up three levels */
2533        dn = ads_parent_dn(ads_parent_dn(ads_parent_dn(service_name)));
2534        if (dn == NULL) {
2535                return ADS_ERROR(LDAP_NO_MEMORY);
2536        }
2537
2538        *site_name = talloc_strdup(mem_ctx, dn);
2539        if (*site_name == NULL) {
2540                return ADS_ERROR(LDAP_NO_MEMORY);
2541        }
2542
2543        return status;
2544        /*
2545        dsServiceName: CN=NTDS Settings,CN=W2K3DC,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,DC=ber,DC=suse,DC=de
2546        */                                               
2547}
2548
2549/**
2550 * find the site dn where a machine resides
2551 * @param ads connection to ads server
2552 * @param mem_ctx Pointer to talloc context
2553 * @param computer_name name of the machine
2554 * @param site_name Pointer to the sitename
2555 * @return status of search
2556 **/
2557ADS_STATUS ads_site_dn_for_machine(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char *computer_name, const char **site_dn)
2558{
2559        ADS_STATUS status;
2560        LDAPMessage *res;
2561        const char *parent, *config_context, *filter;
2562        const char *attrs[] = { "configurationNamingContext", NULL };
2563        char *dn;
2564
2565        /* shortcut a query */
2566        if (strequal(computer_name, ads->config.ldap_server_name)) {
2567                return ads_site_dn(ads, mem_ctx, site_dn);
2568        }
2569
2570        status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2571        if (!ADS_ERR_OK(status)) {
2572                return status;
2573        }
2574
2575        config_context = ads_pull_string(ads, mem_ctx, res, "configurationNamingContext");
2576        if (config_context == NULL) {
2577                ads_msgfree(ads, res);
2578                return ADS_ERROR(LDAP_NO_MEMORY);
2579        }
2580
2581        filter = talloc_asprintf(mem_ctx, "(cn=%s)", computer_name);
2582        if (filter == NULL) {
2583                ads_msgfree(ads, res);
2584                return ADS_ERROR(LDAP_NO_MEMORY);
2585        }
2586
2587        ads_msgfree(ads, res);
2588
2589        status = ads_do_search(ads, config_context, LDAP_SCOPE_SUBTREE, filter, NULL, &res);
2590        if (!ADS_ERR_OK(status)) {
2591                return status;
2592        }
2593
2594        if (ads_count_replies(ads, res) != 1) {
2595                ads_msgfree(ads, res);
2596                return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
2597        }
2598
2599        dn = ads_get_dn(ads, res);
2600        if (dn == NULL) {
2601                ads_msgfree(ads, res);
2602                return ADS_ERROR(LDAP_NO_MEMORY);
2603        }
2604
2605        /* go up three levels */
2606        parent = ads_parent_dn(ads_parent_dn(ads_parent_dn(dn)));
2607        if (parent == NULL) {
2608                ads_msgfree(ads, res);
2609                ads_memfree(ads, dn);
2610                return ADS_ERROR(LDAP_NO_MEMORY);
2611        }
2612
2613        *site_dn = talloc_strdup(mem_ctx, parent);
2614        if (*site_dn == NULL) {
2615                ads_msgfree(ads, res);
2616                ads_memfree(ads, dn);
2617                ADS_ERROR(LDAP_NO_MEMORY);
2618        }
2619
2620        ads_memfree(ads, dn);
2621        ads_msgfree(ads, res);
2622
2623        return status;
2624}
2625
2626/**
2627 * get the upn suffixes for a domain
2628 * @param ads connection to ads server
2629 * @param mem_ctx Pointer to talloc context
2630 * @param suffixes Pointer to an array of suffixes
2631 * @param num_suffixes Pointer to the number of suffixes
2632 * @return status of search
2633 **/
2634ADS_STATUS ads_upn_suffixes(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, char ***suffixes, size_t *num_suffixes)
2635{
2636        ADS_STATUS status;
2637        LDAPMessage *res;
2638        const char *config_context, *base;
2639        const char *attrs[] = { "configurationNamingContext", NULL };
2640        const char *attrs2[] = { "uPNSuffixes", NULL };
2641
2642        status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2643        if (!ADS_ERR_OK(status)) {
2644                return status;
2645        }
2646
2647        config_context = ads_pull_string(ads, mem_ctx, res, "configurationNamingContext");
2648        if (config_context == NULL) {
2649                ads_msgfree(ads, res);
2650                return ADS_ERROR(LDAP_NO_MEMORY);
2651        }
2652
2653        ads_msgfree(ads, res);
2654
2655        base = talloc_asprintf(mem_ctx, "cn=Partitions,%s", config_context);
2656        if (base == NULL) {
2657                return ADS_ERROR(LDAP_NO_MEMORY);
2658        }
2659
2660        status = ads_search_dn(ads, &res, base, attrs2); 
2661        if (!ADS_ERR_OK(status)) {
2662                return status;
2663        }
2664
2665        if (ads_count_replies(ads, res) != 1) {
2666                return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
2667        }
2668
2669        (*suffixes) = ads_pull_strings(ads, mem_ctx, res, "uPNSuffixes", num_suffixes);
2670        if ((*suffixes) == NULL) {
2671                ads_msgfree(ads, res);
2672                return ADS_ERROR(LDAP_NO_MEMORY);
2673        }
2674
2675        ads_msgfree(ads, res);
2676
2677        return status;
2678}
2679
2680/**
2681 * pull a DOM_SID from an extended dn string
2682 * @param mem_ctx TALLOC_CTX
2683 * @param flags string type of extended_dn
2684 * @param sid pointer to a DOM_SID
2685 * @return boolean inidicating success
2686 **/
2687BOOL ads_get_sid_from_extended_dn(TALLOC_CTX *mem_ctx, 
2688                                  const char *dn, 
2689                                  enum ads_extended_dn_flags flags, 
2690                                  DOM_SID *sid)
2691{
2692        char *p, *q;
2693
2694        if (!dn) {
2695                return False;
2696        }
2697
2698        /*
2699         * ADS_EXTENDED_DN_HEX_STRING:
2700         * <GUID=238e1963cb390f4bb032ba0105525a29>;<SID=010500000000000515000000bb68c8fd6b61b427572eb04556040000>;CN=gd,OU=berlin,OU=suse,DC=ber,DC=suse,DC=de
2701         *
2702         * ADS_EXTENDED_DN_STRING (only with w2k3):
2703        <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
2704         */
2705
2706        p = strchr(dn, ';');
2707        if (!p) {
2708                return False;
2709        }
2710
2711        if (strncmp(p, ";<SID=", strlen(";<SID=")) != 0) {
2712                return False;
2713        }
2714
2715        p += strlen(";<SID=");
2716
2717        q = strchr(p, '>');
2718        if (!q) {
2719                return False;
2720        }
2721       
2722        *q = '\0';
2723
2724        DEBUG(100,("ads_get_sid_from_extended_dn: sid string is %s\n", p));
2725
2726        switch (flags) {
2727       
2728        case ADS_EXTENDED_DN_STRING:
2729                if (!string_to_sid(sid, p)) {
2730                        return False;
2731                }
2732                break;
2733        case ADS_EXTENDED_DN_HEX_STRING: {
2734                pstring buf;
2735                size_t buf_len;
2736
2737                buf_len = strhex_to_str(buf, strlen(p), p);
2738                if (buf_len == 0) {
2739                        return False;
2740                }
2741
2742                if (!sid_parse(buf, buf_len, sid)) {
2743                        DEBUG(10,("failed to parse sid\n"));
2744                        return False;
2745                }
2746                break;
2747                }
2748        default:
2749                DEBUG(10,("unknown extended dn format\n"));
2750                return False;
2751        }
2752
2753        return True;
2754}
2755
2756/**
2757 * pull an array of DOM_SIDs from a ADS result
2758 * @param ads connection to ads server
2759 * @param mem_ctx TALLOC_CTX for allocating sid array
2760 * @param msg Results of search
2761 * @param field Attribute to retrieve
2762 * @param flags string type of extended_dn
2763 * @param sids pointer to sid array to allocate
2764 * @return the count of SIDs pulled
2765 **/
2766 int ads_pull_sids_from_extendeddn(ADS_STRUCT *ads, 
2767                                   TALLOC_CTX *mem_ctx, 
2768                                   LDAPMessage *msg, 
2769                                   const char *field,
2770                                   enum ads_extended_dn_flags flags,
2771                                   DOM_SID **sids)
2772{
2773        int i;
2774        size_t dn_count;
2775        char **dn_strings;
2776
2777        if ((dn_strings = ads_pull_strings(ads, mem_ctx, msg, field, 
2778                                           &dn_count)) == NULL) {
2779                return 0;
2780        }
2781
2782        (*sids) = TALLOC_ZERO_ARRAY(mem_ctx, DOM_SID, dn_count + 1);
2783        if (!(*sids)) {
2784                TALLOC_FREE(dn_strings);
2785                return 0;
2786        }
2787
2788        for (i=0; i<dn_count; i++) {
2789
2790                if (!ads_get_sid_from_extended_dn(mem_ctx, dn_strings[i], 
2791                                                  flags, &(*sids)[i])) {
2792                        TALLOC_FREE(*sids);
2793                        TALLOC_FREE(dn_strings);
2794                        return 0;
2795                }
2796        }
2797
2798        TALLOC_FREE(dn_strings);
2799
2800        return dn_count;
2801}
2802
2803/********************************************************************
2804********************************************************************/
2805
2806char* ads_get_dnshostname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
2807{
2808        LDAPMessage *res = NULL;
2809        ADS_STATUS status;
2810        int count = 0;
2811        char *name = NULL;
2812       
2813        status = ads_find_machine_acct(ads, &res, global_myname());
2814        if (!ADS_ERR_OK(status)) {
2815                DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
2816                        global_myname()));
2817                goto out;
2818        }
2819               
2820        if ( (count = ads_count_replies(ads, res)) != 1 ) {
2821                DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
2822                goto out;
2823        }
2824               
2825        if ( (name = ads_pull_string(ads, ctx, res, "dNSHostName")) == NULL ) {
2826                DEBUG(0,("ads_get_dnshostname: No dNSHostName attribute!\n"));
2827        }
2828
2829out:
2830        ads_msgfree(ads, res);
2831       
2832        return name;
2833}
2834
2835/********************************************************************
2836********************************************************************/
2837
2838char* ads_get_upn( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
2839{
2840        LDAPMessage *res = NULL;
2841        ADS_STATUS status;
2842        int count = 0;
2843        char *name = NULL;
2844       
2845        status = ads_find_machine_acct(ads, &res, global_myname());
2846        if (!ADS_ERR_OK(status)) {
2847                DEBUG(0,("ads_get_upn: Failed to find account for %s\n",
2848                        global_myname()));
2849                goto out;
2850        }
2851               
2852        if ( (count = ads_count_replies(ads, res)) != 1 ) {
2853                DEBUG(1,("ads_get_upn: %d entries returned!\n", count));
2854                goto out;
2855        }
2856               
2857        if ( (name = ads_pull_string(ads, ctx, res, "userPrincipalName")) == NULL ) {
2858                DEBUG(2,("ads_get_upn: No userPrincipalName attribute!\n"));
2859        }
2860
2861out:
2862        ads_msgfree(ads, res);
2863       
2864        return name;
2865}
2866
2867/********************************************************************
2868********************************************************************/
2869
2870char* ads_get_samaccountname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
2871{
2872        LDAPMessage *res = NULL;
2873        ADS_STATUS status;
2874        int count = 0;
2875        char *name = NULL;
2876       
2877        status = ads_find_machine_acct(ads, &res, global_myname());
2878        if (!ADS_ERR_OK(status)) {
2879                DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
2880                        global_myname()));
2881                goto out;
2882        }
2883               
2884        if ( (count = ads_count_replies(ads, res)) != 1 ) {
2885                DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
2886                goto out;
2887        }
2888               
2889        if ( (name = ads_pull_string(ads, ctx, res, "sAMAccountName")) == NULL ) {
2890                DEBUG(0,("ads_get_dnshostname: No sAMAccountName attribute!\n"));
2891        }
2892
2893out:
2894        ads_msgfree(ads, res);
2895       
2896        return name;
2897}
2898
2899#if 0
2900
2901   SAVED CODE - we used to join via ldap - remember how we did this. JRA.
2902
2903/**
2904 * Join a machine to a realm
2905 *  Creates the machine account and sets the machine password
2906 * @param ads connection to ads server
2907 * @param machine name of host to add
2908 * @param org_unit Organizational unit to place machine in
2909 * @return status of join
2910 **/
2911ADS_STATUS ads_join_realm(ADS_STRUCT *ads, const char *machine_name,
2912                        uint32 account_type, const char *org_unit)
2913{
2914        ADS_STATUS status;
2915        LDAPMessage *res = NULL;
2916        char *machine;
2917
2918        /* machine name must be lowercase */
2919        machine = SMB_STRDUP(machine_name);
2920        strlower_m(machine);
2921
2922        /*
2923        status = ads_find_machine_acct(ads, (void **)&res, machine);
2924        if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
2925                DEBUG(0, ("Host account for %s already exists - deleting old account\n", machine));
2926                status = ads_leave_realm(ads, machine);
2927                if (!ADS_ERR_OK(status)) {
2928                        DEBUG(0, ("Failed to delete host '%s' from the '%s' realm.\n",
2929                                machine, ads->config.realm));
2930                        return status;
2931                }
2932        }
2933        */
2934        status = ads_add_machine_acct(ads, machine, account_type, org_unit);
2935        if (!ADS_ERR_OK(status)) {
2936                DEBUG(0, ("ads_join_realm: ads_add_machine_acct failed (%s): %s\n", machine, ads_errstr(status)));
2937                SAFE_FREE(machine);
2938                return status;
2939        }
2940
2941        status = ads_find_machine_acct(ads, (void **)(void *)&res, machine);
2942        if (!ADS_ERR_OK(status)) {
2943                DEBUG(0, ("ads_join_realm: Host account test failed for machine %s\n", machine));
2944                SAFE_FREE(machine);
2945                return status;
2946        }
2947
2948        SAFE_FREE(machine);
2949        ads_msgfree(ads, res);
2950
2951        return status;
2952}
2953#endif
2954
2955/**
2956 * Delete a machine from the realm
2957 * @param ads connection to ads server
2958 * @param hostname Machine to remove
2959 * @return status of delete
2960 **/
2961ADS_STATUS ads_leave_realm(ADS_STRUCT *ads, const char *hostname)
2962{
2963        ADS_STATUS status;
2964        void *msg;
2965        LDAPMessage *res;
2966        char *hostnameDN, *host;
2967        int rc;
2968        LDAPControl ldap_control;
2969        LDAPControl  * pldap_control[2] = {NULL, NULL};
2970
2971        pldap_control[0] = &ldap_control;
2972        memset(&ldap_control, 0, sizeof(LDAPControl));
2973        ldap_control.ldctl_oid = (char *)LDAP_SERVER_TREE_DELETE_OID;
2974
2975        /* hostname must be lowercase */
2976        host = SMB_STRDUP(hostname);
2977        strlower_m(host);
2978
2979        status = ads_find_machine_acct(ads, &res, host);
2980        if (!ADS_ERR_OK(status)) {
2981                DEBUG(0, ("Host account for %s does not exist.\n", host));
2982                SAFE_FREE(host);
2983                return status;
2984        }
2985
2986        msg = ads_first_entry(ads, res);
2987        if (!msg) {
2988                SAFE_FREE(host);
2989                return ADS_ERROR_SYSTEM(ENOENT);
2990        }
2991
2992        hostnameDN = ads_get_dn(ads, (LDAPMessage *)msg);
2993
2994        rc = ldap_delete_ext_s(ads->ld, hostnameDN, pldap_control, NULL);
2995        if (rc) {
2996                DEBUG(3,("ldap_delete_ext_s failed with error code %d\n", rc));
2997        }else {
2998                DEBUG(3,("ldap_delete_ext_s succeeded with error code %d\n", rc));
2999        }
3000
3001        if (rc != LDAP_SUCCESS) {
3002                const char *attrs[] = { "cn", NULL };
3003                LDAPMessage *msg_sub;
3004
3005                /* we only search with scope ONE, we do not expect any further
3006                 * objects to be created deeper */
3007
3008                status = ads_do_search_retry(ads, hostnameDN,
3009                                             LDAP_SCOPE_ONELEVEL,
3010                                             "(objectclass=*)", attrs, &res);
3011
3012                if (!ADS_ERR_OK(status)) {
3013                        SAFE_FREE(host);
3014                        ads_memfree(ads, hostnameDN);
3015                        return status;
3016                }
3017
3018                for (msg_sub = ads_first_entry(ads, res); msg_sub;
3019                        msg_sub = ads_next_entry(ads, msg_sub)) {
3020
3021                        char *dn = NULL;
3022
3023                        if ((dn = ads_get_dn(ads, msg_sub)) == NULL) {
3024                                SAFE_FREE(host);
3025                                ads_memfree(ads, hostnameDN);
3026                                return ADS_ERROR(LDAP_NO_MEMORY);
3027                        }
3028
3029                        status = ads_del_dn(ads, dn);
3030                        if (!ADS_ERR_OK(status)) {
3031                                DEBUG(3,("failed to delete dn %s: %s\n", dn, ads_errstr(status)));
3032                                SAFE_FREE(host);
3033                                ads_memfree(ads, dn);
3034                                ads_memfree(ads, hostnameDN);
3035                                return status;
3036                        }
3037
3038                        ads_memfree(ads, dn);
3039                }
3040
3041                /* there should be no subordinate objects anymore */
3042                status = ads_do_search_retry(ads, hostnameDN,
3043                                             LDAP_SCOPE_ONELEVEL,
3044                                             "(objectclass=*)", attrs, &res);
3045
3046                if (!ADS_ERR_OK(status) || ( (ads_count_replies(ads, res)) > 0 ) ) {
3047                        SAFE_FREE(host);
3048                        ads_memfree(ads, hostnameDN);
3049                        return status;
3050                }
3051
3052                /* delete hostnameDN now */
3053                status = ads_del_dn(ads, hostnameDN);
3054                if (!ADS_ERR_OK(status)) {
3055                        SAFE_FREE(host);
3056                        DEBUG(3,("failed to delete dn %s: %s\n", hostnameDN, ads_errstr(status)));
3057                        ads_memfree(ads, hostnameDN);
3058                        return status;
3059                }
3060        }
3061
3062        ads_memfree(ads, hostnameDN);
3063
3064        status = ads_find_machine_acct(ads, &res, host);
3065        if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
3066                DEBUG(3, ("Failed to remove host account.\n"));
3067                SAFE_FREE(host);
3068                return status;
3069        }
3070
3071        SAFE_FREE(host);
3072        return status;
3073}
3074
3075#endif
Note: See TracBrowser for help on using the repository browser.