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

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

Update 3.2 to 3.2.12

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