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

Last change on this file since 165 was 165, checked in by Paul Smedley, 16 years ago

Add 'missing' 3.0.34 diffs

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