source: branches/samba-3.2.x/source/libads/ldap.c@ 204

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

Update source to 3.2.0 GA level

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