~zulcss/samba/server-dailies-3.4

« back to all changes in this revision

Viewing changes to source3/libads/ldap.c

  • Committer: Chuck Short
  • Date: 2010-09-28 20:38:39 UTC
  • Revision ID: zulcss@ubuntu.com-20100928203839-pgjulytsi9ue63x1
Initial version

Show diffs side-by-side

added added

removed removed

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