~vcs-imports/samba/main

« back to all changes in this revision

Viewing changes to source/libads/ldap.c

  • Committer: jerry
  • Date: 2006-07-14 21:48:39 UTC
  • Revision ID: vcs-imports@canonical.com-20060714214839-586d8c489a8fcead
gutting trunk to move to svn:externals

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