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

Last change on this file since 231 was 228, checked in by Herwig Bauernfeind, 16 years ago

Update 3.2 branch to 3.2.6

File size: 96.3 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
306 if ( !c_realm || !*c_realm ) {
307 DEBUG(0,("ads_find_dc: no realm or workgroup! Don't know what to do\n"));
308 return NT_STATUS_INVALID_PARAMETER; /* rather need MISSING_PARAMETER ... */
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 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
2643 goto done;
2644 }
2645 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
2646 status = ads_connect( ads_s );
2647 if ( !ADS_ERR_OK(status))
2648 goto done;
2649 }
2650
2651 /* If the attribute does not exist assume it is a Windows 2000
2652 functional domain */
2653
2654 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2655 if (!ADS_ERR_OK(status)) {
2656 if ( status.err.rc == LDAP_NO_SUCH_ATTRIBUTE ) {
2657 status = ADS_SUCCESS;
2658 }
2659 goto done;
2660 }
2661
2662 if ( !ads_pull_uint32(ads_s, res, "domainFunctionality", val) ) {
2663 DEBUG(5,("ads_domain_func_level: Failed to pull the domainFunctionality attribute.\n"));
2664 }
2665 DEBUG(3,("ads_domain_func_level: %d\n", *val));
2666
2667
2668 ads_msgfree(ads, res);
2669
2670done:
2671 /* free any temporary ads connections */
2672 if ( ads_s != ads ) {
2673 ads_destroy( &ads_s );
2674 }
2675
2676 return status;
2677}
2678
2679/**
2680 * find the domain sid for our domain
2681 * @param ads connection to ads server
2682 * @param sid Pointer to domain sid
2683 * @return status of search
2684 **/
2685ADS_STATUS ads_domain_sid(ADS_STRUCT *ads, DOM_SID *sid)
2686{
2687 const char *attrs[] = {"objectSid", NULL};
2688 LDAPMessage *res;
2689 ADS_STATUS rc;
2690
2691 rc = ads_do_search_retry(ads, ads->config.bind_path, LDAP_SCOPE_BASE, "(objectclass=*)",
2692 attrs, &res);
2693 if (!ADS_ERR_OK(rc)) return rc;
2694 if (!ads_pull_sid(ads, res, "objectSid", sid)) {
2695 ads_msgfree(ads, res);
2696 return ADS_ERROR_SYSTEM(ENOENT);
2697 }
2698 ads_msgfree(ads, res);
2699
2700 return ADS_SUCCESS;
2701}
2702
2703/**
2704 * find our site name
2705 * @param ads connection to ads server
2706 * @param mem_ctx Pointer to talloc context
2707 * @param site_name Pointer to the sitename
2708 * @return status of search
2709 **/
2710ADS_STATUS ads_site_dn(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char **site_name)
2711{
2712 ADS_STATUS status;
2713 LDAPMessage *res;
2714 const char *dn, *service_name;
2715 const char *attrs[] = { "dsServiceName", NULL };
2716
2717 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2718 if (!ADS_ERR_OK(status)) {
2719 return status;
2720 }
2721
2722 service_name = ads_pull_string(ads, mem_ctx, res, "dsServiceName");
2723 if (service_name == NULL) {
2724 ads_msgfree(ads, res);
2725 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2726 }
2727
2728 ads_msgfree(ads, res);
2729
2730 /* go up three levels */
2731 dn = ads_parent_dn(ads_parent_dn(ads_parent_dn(service_name)));
2732 if (dn == NULL) {
2733 return ADS_ERROR(LDAP_NO_MEMORY);
2734 }
2735
2736 *site_name = talloc_strdup(mem_ctx, dn);
2737 if (*site_name == NULL) {
2738 return ADS_ERROR(LDAP_NO_MEMORY);
2739 }
2740
2741 return status;
2742 /*
2743 dsServiceName: CN=NTDS Settings,CN=W2K3DC,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,DC=ber,DC=suse,DC=de
2744 */
2745}
2746
2747/**
2748 * find the site dn where a machine resides
2749 * @param ads connection to ads server
2750 * @param mem_ctx Pointer to talloc context
2751 * @param computer_name name of the machine
2752 * @param site_name Pointer to the sitename
2753 * @return status of search
2754 **/
2755ADS_STATUS ads_site_dn_for_machine(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char *computer_name, const char **site_dn)
2756{
2757 ADS_STATUS status;
2758 LDAPMessage *res;
2759 const char *parent, *filter;
2760 char *config_context = NULL;
2761 char *dn;
2762
2763 /* shortcut a query */
2764 if (strequal(computer_name, ads->config.ldap_server_name)) {
2765 return ads_site_dn(ads, mem_ctx, site_dn);
2766 }
2767
2768 status = ads_config_path(ads, mem_ctx, &config_context);
2769 if (!ADS_ERR_OK(status)) {
2770 return status;
2771 }
2772
2773 filter = talloc_asprintf(mem_ctx, "(cn=%s)", computer_name);
2774 if (filter == NULL) {
2775 return ADS_ERROR(LDAP_NO_MEMORY);
2776 }
2777
2778 status = ads_do_search(ads, config_context, LDAP_SCOPE_SUBTREE,
2779 filter, NULL, &res);
2780 if (!ADS_ERR_OK(status)) {
2781 return status;
2782 }
2783
2784 if (ads_count_replies(ads, res) != 1) {
2785 ads_msgfree(ads, res);
2786 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
2787 }
2788
2789 dn = ads_get_dn(ads, res);
2790 if (dn == NULL) {
2791 ads_msgfree(ads, res);
2792 return ADS_ERROR(LDAP_NO_MEMORY);
2793 }
2794
2795 /* go up three levels */
2796 parent = ads_parent_dn(ads_parent_dn(ads_parent_dn(dn)));
2797 if (parent == NULL) {
2798 ads_msgfree(ads, res);
2799 ads_memfree(ads, dn);
2800 return ADS_ERROR(LDAP_NO_MEMORY);
2801 }
2802
2803 *site_dn = talloc_strdup(mem_ctx, parent);
2804 if (*site_dn == NULL) {
2805 ads_msgfree(ads, res);
2806 ads_memfree(ads, dn);
2807 return ADS_ERROR(LDAP_NO_MEMORY);
2808 }
2809
2810 ads_memfree(ads, dn);
2811 ads_msgfree(ads, res);
2812
2813 return status;
2814}
2815
2816/**
2817 * get the upn suffixes for a domain
2818 * @param ads connection to ads server
2819 * @param mem_ctx Pointer to talloc context
2820 * @param suffixes Pointer to an array of suffixes
2821 * @param num_suffixes Pointer to the number of suffixes
2822 * @return status of search
2823 **/
2824ADS_STATUS ads_upn_suffixes(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, char ***suffixes, size_t *num_suffixes)
2825{
2826 ADS_STATUS status;
2827 LDAPMessage *res;
2828 const char *base;
2829 char *config_context = NULL;
2830 const char *attrs[] = { "uPNSuffixes", NULL };
2831
2832 status = ads_config_path(ads, mem_ctx, &config_context);
2833 if (!ADS_ERR_OK(status)) {
2834 return status;
2835 }
2836
2837 base = talloc_asprintf(mem_ctx, "cn=Partitions,%s", config_context);
2838 if (base == NULL) {
2839 return ADS_ERROR(LDAP_NO_MEMORY);
2840 }
2841
2842 status = ads_search_dn(ads, &res, base, attrs);
2843 if (!ADS_ERR_OK(status)) {
2844 return status;
2845 }
2846
2847 if (ads_count_replies(ads, res) != 1) {
2848 ads_msgfree(ads, res);
2849 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
2850 }
2851
2852 (*suffixes) = ads_pull_strings(ads, mem_ctx, res, "uPNSuffixes", num_suffixes);
2853 if ((*suffixes) == NULL) {
2854 ads_msgfree(ads, res);
2855 return ADS_ERROR(LDAP_NO_MEMORY);
2856 }
2857
2858 ads_msgfree(ads, res);
2859
2860 return status;
2861}
2862
2863/**
2864 * get the joinable ous for a domain
2865 * @param ads connection to ads server
2866 * @param mem_ctx Pointer to talloc context
2867 * @param ous Pointer to an array of ous
2868 * @param num_ous Pointer to the number of ous
2869 * @return status of search
2870 **/
2871ADS_STATUS ads_get_joinable_ous(ADS_STRUCT *ads,
2872 TALLOC_CTX *mem_ctx,
2873 char ***ous,
2874 size_t *num_ous)
2875{
2876 ADS_STATUS status;
2877 LDAPMessage *res = NULL;
2878 LDAPMessage *msg = NULL;
2879 const char *attrs[] = { "dn", NULL };
2880 int count = 0;
2881
2882 status = ads_search(ads, &res,
2883 "(|(objectClass=domain)(objectclass=organizationalUnit))",
2884 attrs);
2885 if (!ADS_ERR_OK(status)) {
2886 return status;
2887 }
2888
2889 count = ads_count_replies(ads, res);
2890 if (count < 1) {
2891 ads_msgfree(ads, res);
2892 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2893 }
2894
2895 for (msg = ads_first_entry(ads, res); msg;
2896 msg = ads_next_entry(ads, msg)) {
2897
2898 char *dn = NULL;
2899
2900 dn = ads_get_dn(ads, msg);
2901 if (!dn) {
2902 ads_msgfree(ads, res);
2903 return ADS_ERROR(LDAP_NO_MEMORY);
2904 }
2905
2906 if (!add_string_to_array(mem_ctx, dn,
2907 (const char ***)ous,
2908 (int *)num_ous)) {
2909 ads_memfree(ads, dn);
2910 ads_msgfree(ads, res);
2911 return ADS_ERROR(LDAP_NO_MEMORY);
2912 }
2913
2914 ads_memfree(ads, dn);
2915 }
2916
2917 ads_msgfree(ads, res);
2918
2919 return status;
2920}
2921
2922
2923/**
2924 * pull a DOM_SID from an extended dn string
2925 * @param mem_ctx TALLOC_CTX
2926 * @param extended_dn string
2927 * @param flags string type of extended_dn
2928 * @param sid pointer to a DOM_SID
2929 * @return NT_STATUS_OK on success,
2930 * NT_INVALID_PARAMETER on error,
2931 * NT_STATUS_NOT_FOUND if no SID present
2932 **/
2933ADS_STATUS ads_get_sid_from_extended_dn(TALLOC_CTX *mem_ctx,
2934 const char *extended_dn,
2935 enum ads_extended_dn_flags flags,
2936 DOM_SID *sid)
2937{
2938 char *p, *q, *dn;
2939
2940 if (!extended_dn) {
2941 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
2942 }
2943
2944 /* otherwise extended_dn gets stripped off */
2945 if ((dn = talloc_strdup(mem_ctx, extended_dn)) == NULL) {
2946 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
2947 }
2948 /*
2949 * ADS_EXTENDED_DN_HEX_STRING:
2950 * <GUID=238e1963cb390f4bb032ba0105525a29>;<SID=010500000000000515000000bb68c8fd6b61b427572eb04556040000>;CN=gd,OU=berlin,OU=suse,DC=ber,DC=suse,DC=de
2951 *
2952 * ADS_EXTENDED_DN_STRING (only with w2k3):
2953 * <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
2954 *
2955 * Object with no SID, such as an Exchange Public Folder
2956 * <GUID=28907fb4bdf6854993e7f0a10b504e7c>;CN=public,CN=Microsoft Exchange System Objects,DC=sd2k3ms,DC=west,DC=isilon,DC=com
2957 */
2958
2959 p = strchr(dn, ';');
2960 if (!p) {
2961 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
2962 }
2963
2964 if (strncmp(p, ";<SID=", strlen(";<SID=")) != 0) {
2965 DEBUG(5,("No SID present in extended dn\n"));
2966 return ADS_ERROR_NT(NT_STATUS_NOT_FOUND);
2967 }
2968
2969 p += strlen(";<SID=");
2970
2971 q = strchr(p, '>');
2972 if (!q) {
2973 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
2974 }
2975
2976 *q = '\0';
2977
2978 DEBUG(100,("ads_get_sid_from_extended_dn: sid string is %s\n", p));
2979
2980 switch (flags) {
2981
2982 case ADS_EXTENDED_DN_STRING:
2983 if (!string_to_sid(sid, p)) {
2984 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
2985 }
2986 break;
2987 case ADS_EXTENDED_DN_HEX_STRING: {
2988 fstring buf;
2989 size_t buf_len;
2990
2991 buf_len = strhex_to_str(buf, sizeof(buf), p, strlen(p));
2992 if (buf_len == 0) {
2993 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
2994 }
2995
2996 if (!sid_parse(buf, buf_len, sid)) {
2997 DEBUG(10,("failed to parse sid\n"));
2998 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
2999 }
3000 break;
3001 }
3002 default:
3003 DEBUG(10,("unknown extended dn format\n"));
3004 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3005 }
3006
3007 return ADS_ERROR_NT(NT_STATUS_OK);
3008}
3009
3010/**
3011 * pull an array of DOM_SIDs from a ADS result
3012 * @param ads connection to ads server
3013 * @param mem_ctx TALLOC_CTX for allocating sid array
3014 * @param msg Results of search
3015 * @param field Attribute to retrieve
3016 * @param flags string type of extended_dn
3017 * @param sids pointer to sid array to allocate
3018 * @return the count of SIDs pulled
3019 **/
3020 int ads_pull_sids_from_extendeddn(ADS_STRUCT *ads,
3021 TALLOC_CTX *mem_ctx,
3022 LDAPMessage *msg,
3023 const char *field,
3024 enum ads_extended_dn_flags flags,
3025 DOM_SID **sids)
3026{
3027 int i;
3028 ADS_STATUS rc;
3029 size_t dn_count, ret_count = 0;
3030 char **dn_strings;
3031
3032 if ((dn_strings = ads_pull_strings(ads, mem_ctx, msg, field,
3033 &dn_count)) == NULL) {
3034 return 0;
3035 }
3036
3037 (*sids) = TALLOC_ZERO_ARRAY(mem_ctx, DOM_SID, dn_count + 1);
3038 if (!(*sids)) {
3039 TALLOC_FREE(dn_strings);
3040 return 0;
3041 }
3042
3043 for (i=0; i<dn_count; i++) {
3044 rc = ads_get_sid_from_extended_dn(mem_ctx, dn_strings[i],
3045 flags, &(*sids)[i]);
3046 if (!ADS_ERR_OK(rc)) {
3047 if (NT_STATUS_EQUAL(ads_ntstatus(rc),
3048 NT_STATUS_NOT_FOUND)) {
3049 continue;
3050 }
3051 else {
3052 TALLOC_FREE(*sids);
3053 TALLOC_FREE(dn_strings);
3054 return 0;
3055 }
3056 }
3057 ret_count++;
3058 }
3059
3060 TALLOC_FREE(dn_strings);
3061
3062 return ret_count;
3063}
3064
3065/********************************************************************
3066********************************************************************/
3067
3068char* ads_get_dnshostname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3069{
3070 LDAPMessage *res = NULL;
3071 ADS_STATUS status;
3072 int count = 0;
3073 char *name = NULL;
3074
3075 status = ads_find_machine_acct(ads, &res, global_myname());
3076 if (!ADS_ERR_OK(status)) {
3077 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
3078 global_myname()));
3079 goto out;
3080 }
3081
3082 if ( (count = ads_count_replies(ads, res)) != 1 ) {
3083 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
3084 goto out;
3085 }
3086
3087 if ( (name = ads_pull_string(ads, ctx, res, "dNSHostName")) == NULL ) {
3088 DEBUG(0,("ads_get_dnshostname: No dNSHostName attribute!\n"));
3089 }
3090
3091out:
3092 ads_msgfree(ads, res);
3093
3094 return name;
3095}
3096
3097/********************************************************************
3098********************************************************************/
3099
3100char* ads_get_upn( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3101{
3102 LDAPMessage *res = NULL;
3103 ADS_STATUS status;
3104 int count = 0;
3105 char *name = NULL;
3106
3107 status = ads_find_machine_acct(ads, &res, machine_name);
3108 if (!ADS_ERR_OK(status)) {
3109 DEBUG(0,("ads_get_upn: Failed to find account for %s\n",
3110 global_myname()));
3111 goto out;
3112 }
3113
3114 if ( (count = ads_count_replies(ads, res)) != 1 ) {
3115 DEBUG(1,("ads_get_upn: %d entries returned!\n", count));
3116 goto out;
3117 }
3118
3119 if ( (name = ads_pull_string(ads, ctx, res, "userPrincipalName")) == NULL ) {
3120 DEBUG(2,("ads_get_upn: No userPrincipalName attribute!\n"));
3121 }
3122
3123out:
3124 ads_msgfree(ads, res);
3125
3126 return name;
3127}
3128
3129/********************************************************************
3130********************************************************************/
3131
3132char* ads_get_samaccountname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3133{
3134 LDAPMessage *res = NULL;
3135 ADS_STATUS status;
3136 int count = 0;
3137 char *name = NULL;
3138
3139 status = ads_find_machine_acct(ads, &res, global_myname());
3140 if (!ADS_ERR_OK(status)) {
3141 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
3142 global_myname()));
3143 goto out;
3144 }
3145
3146 if ( (count = ads_count_replies(ads, res)) != 1 ) {
3147 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
3148 goto out;
3149 }
3150
3151 if ( (name = ads_pull_string(ads, ctx, res, "sAMAccountName")) == NULL ) {
3152 DEBUG(0,("ads_get_dnshostname: No sAMAccountName attribute!\n"));
3153 }
3154
3155out:
3156 ads_msgfree(ads, res);
3157
3158 return name;
3159}
3160
3161#if 0
3162
3163 SAVED CODE - we used to join via ldap - remember how we did this. JRA.
3164
3165/**
3166 * Join a machine to a realm
3167 * Creates the machine account and sets the machine password
3168 * @param ads connection to ads server
3169 * @param machine name of host to add
3170 * @param org_unit Organizational unit to place machine in
3171 * @return status of join
3172 **/
3173ADS_STATUS ads_join_realm(ADS_STRUCT *ads, const char *machine_name,
3174 uint32 account_type, const char *org_unit)
3175{
3176 ADS_STATUS status;
3177 LDAPMessage *res = NULL;
3178 char *machine;
3179
3180 /* machine name must be lowercase */
3181 machine = SMB_STRDUP(machine_name);
3182 strlower_m(machine);
3183
3184 /*
3185 status = ads_find_machine_acct(ads, (void **)&res, machine);
3186 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
3187 DEBUG(0, ("Host account for %s already exists - deleting old account\n", machine));
3188 status = ads_leave_realm(ads, machine);
3189 if (!ADS_ERR_OK(status)) {
3190 DEBUG(0, ("Failed to delete host '%s' from the '%s' realm.\n",
3191 machine, ads->config.realm));
3192 return status;
3193 }
3194 }
3195 */
3196 status = ads_add_machine_acct(ads, machine, account_type, org_unit);
3197 if (!ADS_ERR_OK(status)) {
3198 DEBUG(0, ("ads_join_realm: ads_add_machine_acct failed (%s): %s\n", machine, ads_errstr(status)));
3199 SAFE_FREE(machine);
3200 return status;
3201 }
3202
3203 status = ads_find_machine_acct(ads, (void **)(void *)&res, machine);
3204 if (!ADS_ERR_OK(status)) {
3205 DEBUG(0, ("ads_join_realm: Host account test failed for machine %s\n", machine));
3206 SAFE_FREE(machine);
3207 return status;
3208 }
3209
3210 SAFE_FREE(machine);
3211 ads_msgfree(ads, res);
3212
3213 return status;
3214}
3215#endif
3216
3217/**
3218 * Delete a machine from the realm
3219 * @param ads connection to ads server
3220 * @param hostname Machine to remove
3221 * @return status of delete
3222 **/
3223ADS_STATUS ads_leave_realm(ADS_STRUCT *ads, const char *hostname)
3224{
3225 ADS_STATUS status;
3226 void *msg;
3227 LDAPMessage *res;
3228 char *hostnameDN, *host;
3229 int rc;
3230 LDAPControl ldap_control;
3231 LDAPControl * pldap_control[2] = {NULL, NULL};
3232
3233 pldap_control[0] = &ldap_control;
3234 memset(&ldap_control, 0, sizeof(LDAPControl));
3235 ldap_control.ldctl_oid = (char *)LDAP_SERVER_TREE_DELETE_OID;
3236
3237 /* hostname must be lowercase */
3238 host = SMB_STRDUP(hostname);
3239 strlower_m(host);
3240
3241 status = ads_find_machine_acct(ads, &res, host);
3242 if (!ADS_ERR_OK(status)) {
3243 DEBUG(0, ("Host account for %s does not exist.\n", host));
3244 SAFE_FREE(host);
3245 return status;
3246 }
3247
3248 msg = ads_first_entry(ads, res);
3249 if (!msg) {
3250 SAFE_FREE(host);
3251 return ADS_ERROR_SYSTEM(ENOENT);
3252 }
3253
3254 hostnameDN = ads_get_dn(ads, (LDAPMessage *)msg);
3255
3256 rc = ldap_delete_ext_s(ads->ldap.ld, hostnameDN, pldap_control, NULL);
3257 if (rc) {
3258 DEBUG(3,("ldap_delete_ext_s failed with error code %d\n", rc));
3259 }else {
3260 DEBUG(3,("ldap_delete_ext_s succeeded with error code %d\n", rc));
3261 }
3262
3263 if (rc != LDAP_SUCCESS) {
3264 const char *attrs[] = { "cn", NULL };
3265 LDAPMessage *msg_sub;
3266
3267 /* we only search with scope ONE, we do not expect any further
3268 * objects to be created deeper */
3269
3270 status = ads_do_search_retry(ads, hostnameDN,
3271 LDAP_SCOPE_ONELEVEL,
3272 "(objectclass=*)", attrs, &res);
3273
3274 if (!ADS_ERR_OK(status)) {
3275 SAFE_FREE(host);
3276 ads_memfree(ads, hostnameDN);
3277 return status;
3278 }
3279
3280 for (msg_sub = ads_first_entry(ads, res); msg_sub;
3281 msg_sub = ads_next_entry(ads, msg_sub)) {
3282
3283 char *dn = NULL;
3284
3285 if ((dn = ads_get_dn(ads, msg_sub)) == NULL) {
3286 SAFE_FREE(host);
3287 ads_memfree(ads, hostnameDN);
3288 return ADS_ERROR(LDAP_NO_MEMORY);
3289 }
3290
3291 status = ads_del_dn(ads, dn);
3292 if (!ADS_ERR_OK(status)) {
3293 DEBUG(3,("failed to delete dn %s: %s\n", dn, ads_errstr(status)));
3294 SAFE_FREE(host);
3295 ads_memfree(ads, dn);
3296 ads_memfree(ads, hostnameDN);
3297 return status;
3298 }
3299
3300 ads_memfree(ads, dn);
3301 }
3302
3303 /* there should be no subordinate objects anymore */
3304 status = ads_do_search_retry(ads, hostnameDN,
3305 LDAP_SCOPE_ONELEVEL,
3306 "(objectclass=*)", attrs, &res);
3307
3308 if (!ADS_ERR_OK(status) || ( (ads_count_replies(ads, res)) > 0 ) ) {
3309 SAFE_FREE(host);
3310 ads_memfree(ads, hostnameDN);
3311 return status;
3312 }
3313
3314 /* delete hostnameDN now */
3315 status = ads_del_dn(ads, hostnameDN);
3316 if (!ADS_ERR_OK(status)) {
3317 SAFE_FREE(host);
3318 DEBUG(3,("failed to delete dn %s: %s\n", hostnameDN, ads_errstr(status)));
3319 ads_memfree(ads, hostnameDN);
3320 return status;
3321 }
3322 }
3323
3324 ads_memfree(ads, hostnameDN);
3325
3326 status = ads_find_machine_acct(ads, &res, host);
3327 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
3328 DEBUG(3, ("Failed to remove host account.\n"));
3329 SAFE_FREE(host);
3330 return status;
3331 }
3332
3333 SAFE_FREE(host);
3334 return status;
3335}
3336
3337/**
3338 * pull all token-sids from an LDAP dn
3339 * @param ads connection to ads server
3340 * @param mem_ctx TALLOC_CTX for allocating sid array
3341 * @param dn of LDAP object
3342 * @param user_sid pointer to DOM_SID (objectSid)
3343 * @param primary_group_sid pointer to DOM_SID (self composed)
3344 * @param sids pointer to sid array to allocate
3345 * @param num_sids counter of SIDs pulled
3346 * @return status of token query
3347 **/
3348 ADS_STATUS ads_get_tokensids(ADS_STRUCT *ads,
3349 TALLOC_CTX *mem_ctx,
3350 const char *dn,
3351 DOM_SID *user_sid,
3352 DOM_SID *primary_group_sid,
3353 DOM_SID **sids,
3354 size_t *num_sids)
3355{
3356 ADS_STATUS status;
3357 LDAPMessage *res = NULL;
3358 int count = 0;
3359 size_t tmp_num_sids;
3360 DOM_SID *tmp_sids;
3361 DOM_SID tmp_user_sid;
3362 DOM_SID tmp_primary_group_sid;
3363 uint32 pgid;
3364 const char *attrs[] = {
3365 "objectSid",
3366 "tokenGroups",
3367 "primaryGroupID",
3368 NULL
3369 };
3370
3371 status = ads_search_retry_dn(ads, &res, dn, attrs);
3372 if (!ADS_ERR_OK(status)) {
3373 return status;
3374 }
3375
3376 count = ads_count_replies(ads, res);
3377 if (count != 1) {
3378 ads_msgfree(ads, res);
3379 return ADS_ERROR_LDAP(LDAP_NO_SUCH_OBJECT);
3380 }
3381
3382 if (!ads_pull_sid(ads, res, "objectSid", &tmp_user_sid)) {
3383 ads_msgfree(ads, res);
3384 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3385 }
3386
3387 if (!ads_pull_uint32(ads, res, "primaryGroupID", &pgid)) {
3388 ads_msgfree(ads, res);
3389 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3390 }
3391
3392 {
3393 /* hack to compose the primary group sid without knowing the
3394 * domsid */
3395
3396 DOM_SID domsid;
3397 uint32 dummy_rid;
3398
3399 sid_copy(&domsid, &tmp_user_sid);
3400
3401 if (!sid_split_rid(&domsid, &dummy_rid)) {
3402 ads_msgfree(ads, res);
3403 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3404 }
3405
3406 if (!sid_compose(&tmp_primary_group_sid, &domsid, pgid)) {
3407 ads_msgfree(ads, res);
3408 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3409 }
3410 }
3411
3412 tmp_num_sids = ads_pull_sids(ads, mem_ctx, res, "tokenGroups", &tmp_sids);
3413
3414 if (tmp_num_sids == 0 || !tmp_sids) {
3415 ads_msgfree(ads, res);
3416 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3417 }
3418
3419 if (num_sids) {
3420 *num_sids = tmp_num_sids;
3421 }
3422
3423 if (sids) {
3424 *sids = tmp_sids;
3425 }
3426
3427 if (user_sid) {
3428 *user_sid = tmp_user_sid;
3429 }
3430
3431 if (primary_group_sid) {
3432 *primary_group_sid = tmp_primary_group_sid;
3433 }
3434
3435 DEBUG(10,("ads_get_tokensids: returned %d sids\n", (int)tmp_num_sids + 2));
3436
3437 ads_msgfree(ads, res);
3438 return ADS_ERROR_LDAP(LDAP_SUCCESS);
3439}
3440
3441/**
3442 * Find a sAMAccoutName in LDAP
3443 * @param ads connection to ads server
3444 * @param mem_ctx TALLOC_CTX for allocating sid array
3445 * @param samaccountname to search
3446 * @param uac_ret uint32 pointer userAccountControl attribute value
3447 * @param dn_ret pointer to dn
3448 * @return status of token query
3449 **/
3450ADS_STATUS ads_find_samaccount(ADS_STRUCT *ads,
3451 TALLOC_CTX *mem_ctx,
3452 const char *samaccountname,
3453 uint32 *uac_ret,
3454 const char **dn_ret)
3455{
3456 ADS_STATUS status;
3457 const char *attrs[] = { "userAccountControl", NULL };
3458 const char *filter;
3459 LDAPMessage *res = NULL;
3460 char *dn = NULL;
3461 uint32 uac = 0;
3462
3463 filter = talloc_asprintf(mem_ctx, "(&(objectclass=user)(sAMAccountName=%s))",
3464 samaccountname);
3465 if (filter == NULL) {
3466 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
3467 goto out;
3468 }
3469
3470 status = ads_do_search_all(ads, ads->config.bind_path,
3471 LDAP_SCOPE_SUBTREE,
3472 filter, attrs, &res);
3473
3474 if (!ADS_ERR_OK(status)) {
3475 goto out;
3476 }
3477
3478 if (ads_count_replies(ads, res) != 1) {
3479 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3480 goto out;
3481 }
3482
3483 dn = ads_get_dn(ads, res);
3484 if (dn == NULL) {
3485 status = ADS_ERROR(LDAP_NO_MEMORY);
3486 goto out;
3487 }
3488
3489 if (!ads_pull_uint32(ads, res, "userAccountControl", &uac)) {
3490 status = ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
3491 goto out;
3492 }
3493
3494 if (uac_ret) {
3495 *uac_ret = uac;
3496 }
3497
3498 if (dn_ret) {
3499 *dn_ret = talloc_strdup(mem_ctx, dn);
3500 if (!*dn_ret) {
3501 status = ADS_ERROR(LDAP_NO_MEMORY);
3502 goto out;
3503 }
3504 }
3505 out:
3506 ads_memfree(ads, dn);
3507 ads_msgfree(ads, res);
3508
3509 return status;
3510}
3511
3512/**
3513 * find our configuration path
3514 * @param ads connection to ads server
3515 * @param mem_ctx Pointer to talloc context
3516 * @param config_path Pointer to the config path
3517 * @return status of search
3518 **/
3519ADS_STATUS ads_config_path(ADS_STRUCT *ads,
3520 TALLOC_CTX *mem_ctx,
3521 char **config_path)
3522{
3523 ADS_STATUS status;
3524 LDAPMessage *res = NULL;
3525 const char *config_context = NULL;
3526 const char *attrs[] = { "configurationNamingContext", NULL };
3527
3528 status = ads_do_search(ads, "", LDAP_SCOPE_BASE,
3529 "(objectclass=*)", attrs, &res);
3530 if (!ADS_ERR_OK(status)) {
3531 return status;
3532 }
3533
3534 config_context = ads_pull_string(ads, mem_ctx, res,
3535 "configurationNamingContext");
3536 ads_msgfree(ads, res);
3537 if (!config_context) {
3538 return ADS_ERROR(LDAP_NO_MEMORY);
3539 }
3540
3541 if (config_path) {
3542 *config_path = talloc_strdup(mem_ctx, config_context);
3543 if (!*config_path) {
3544 return ADS_ERROR(LDAP_NO_MEMORY);
3545 }
3546 }
3547
3548 return ADS_ERROR(LDAP_SUCCESS);
3549}
3550
3551/**
3552 * find the displayName of an extended right
3553 * @param ads connection to ads server
3554 * @param config_path The config path
3555 * @param mem_ctx Pointer to talloc context
3556 * @param GUID struct of the rightsGUID
3557 * @return status of search
3558 **/
3559const char *ads_get_extended_right_name_by_guid(ADS_STRUCT *ads,
3560 const char *config_path,
3561 TALLOC_CTX *mem_ctx,
3562 const struct GUID *rights_guid)
3563{
3564 ADS_STATUS rc;
3565 LDAPMessage *res = NULL;
3566 char *expr = NULL;
3567 const char *attrs[] = { "displayName", NULL };
3568 const char *result = NULL;
3569 const char *path;
3570
3571 if (!ads || !mem_ctx || !rights_guid) {
3572 goto done;
3573 }
3574
3575 expr = talloc_asprintf(mem_ctx, "(rightsGuid=%s)",
3576 smb_uuid_string(mem_ctx, *rights_guid));
3577 if (!expr) {
3578 goto done;
3579 }
3580
3581 path = talloc_asprintf(mem_ctx, "cn=Extended-Rights,%s", config_path);
3582 if (!path) {
3583 goto done;
3584 }
3585
3586 rc = ads_do_search_retry(ads, path, LDAP_SCOPE_SUBTREE,
3587 expr, attrs, &res);
3588 if (!ADS_ERR_OK(rc)) {
3589 goto done;
3590 }
3591
3592 if (ads_count_replies(ads, res) != 1) {
3593 goto done;
3594 }
3595
3596 result = ads_pull_string(ads, mem_ctx, res, "displayName");
3597
3598 done:
3599 ads_msgfree(ads, res);
3600 return result;
3601
3602}
3603
3604/**
3605 * verify or build and verify an account ou
3606 * @param mem_ctx Pointer to talloc context
3607 * @param ads connection to ads server
3608 * @param account_ou
3609 * @return status of search
3610 **/
3611
3612ADS_STATUS ads_check_ou_dn(TALLOC_CTX *mem_ctx,
3613 ADS_STRUCT *ads,
3614 const char **account_ou)
3615{
3616 struct ldb_dn *name_dn = NULL;
3617 const char *name = NULL;
3618 char *ou_string = NULL;
3619
3620 name_dn = ldb_dn_explode(mem_ctx, *account_ou);
3621 if (name_dn) {
3622 return ADS_SUCCESS;
3623 }
3624
3625 ou_string = ads_ou_string(ads, *account_ou);
3626 if (!ou_string) {
3627 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX);
3628 }
3629
3630 name = talloc_asprintf(mem_ctx, "%s,%s", ou_string,
3631 ads->config.bind_path);
3632 SAFE_FREE(ou_string);
3633 if (!name) {
3634 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3635 }
3636
3637 name_dn = ldb_dn_explode(mem_ctx, name);
3638 if (!name_dn) {
3639 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX);
3640 }
3641
3642 *account_ou = talloc_strdup(mem_ctx, name);
3643 if (!*account_ou) {
3644 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3645 }
3646
3647 return ADS_SUCCESS;
3648}
3649
3650#endif
Note: See TracBrowser for help on using the repository browser.