source: branches/samba-3.0/source/libads/ldap.c@ 134

Last change on this file since 134 was 134, checked in by Paul Smedley, 17 years ago

Update source to 3.0.29

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