s4-netlogon: implement dcesrv_netr_DsRAddressToSitenamesExW
[Samba/aatanasov.git] / source3 / libads / ldap.c
blobbb8d43c96f5cbcefe319599908fb23ea45067b0a
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
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.
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.
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/>.
24 #include "includes.h"
25 #include "lib/ldb/include/ldb.h"
27 #ifdef HAVE_LDAP
29 /**
30 * @file ldap.c
31 * @brief basic ldap client-side routines for ads server communications
33 * The routines contained here should do the necessary ldap calls for
34 * ads setups.
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 **/
43 #define LDAP_SERVER_TREE_DELETE_OID "1.2.840.113556.1.4.805"
45 static SIG_ATOMIC_T gotalarm;
47 /***************************************************************
48 Signal function to tell us we timed out.
49 ****************************************************************/
51 static void gotalarm_sig(void)
53 gotalarm = 1;
56 LDAP *ldap_open_with_timeout(const char *server, int port, unsigned int to)
58 LDAP *ldp = NULL;
61 DEBUG(10, ("Opening connection to LDAP server '%s:%d', timeout "
62 "%u seconds\n", server, port, to));
64 /* Setup timeout */
65 gotalarm = 0;
66 CatchSignal(SIGALRM, SIGNAL_CAST gotalarm_sig);
67 alarm(to);
68 /* End setup timeout. */
70 ldp = ldap_open(server, port);
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));
79 /* Teardown timeout. */
80 CatchSignal(SIGALRM, SIGNAL_CAST SIG_IGN);
81 alarm(0);
83 return ldp;
86 static 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 )
97 struct timeval timeout;
98 int result;
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;
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. */
110 result = ldap_search_ext_s(ld, base, scope, filter, attrs,
111 attrsonly, sctrls, cctrls, &timeout,
112 sizelimit, res);
114 /* Teardown timeout. */
115 CatchSignal(SIGALRM, SIGNAL_CAST SIG_IGN);
116 alarm(0);
118 if (gotalarm != 0)
119 return LDAP_TIMELIMIT_EXCEEDED;
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.
127 if (*res == NULL) {
128 return LDAP_TIMELIMIT_EXCEEDED;
131 return result;
134 /**********************************************
135 Do client and server sitename match ?
136 **********************************************/
138 bool ads_sitename_match(ADS_STRUCT *ads)
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;
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;
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;
158 /**********************************************
159 Is this the closest DC ?
160 **********************************************/
162 bool ads_closest_dc(ADS_STRUCT *ads)
164 if (ads->config.flags & NBT_SERVER_CLOSEST) {
165 DEBUG(10,("ads_closest_dc: NBT_SERVER_CLOSEST flag set\n"));
166 return True;
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;
175 if (ads->config.client_site_name == NULL) {
176 DEBUG(10,("ads_closest_dc: client belongs to no site\n"));
177 return True;
180 DEBUG(10,("ads_closest_dc: %s is not the closest DC\n",
181 ads->config.ldap_server_name));
183 return False;
188 try a connection to a given ldap server, returning True and setting the servers IP
189 in the ads struct if successful
191 static bool ads_try_connect(ADS_STRUCT *ads, const char *server, bool gc)
193 char *srv;
194 struct NETLOGON_SAM_LOGON_RESPONSE_EX cldap_reply;
195 TALLOC_CTX *frame = talloc_stackframe();
196 bool ret = false;
198 if (!server || !*server) {
199 TALLOC_FREE(frame);
200 return False;
203 if (!is_ipaddress(server)) {
204 struct sockaddr_storage ss;
205 char addr[INET6_ADDRSTRLEN];
207 if (!resolve_name(server, &ss, 0x20, true)) {
208 DEBUG(5,("ads_try_connect: unable to resolve name %s\n",
209 server ));
210 TALLOC_FREE(frame);
211 return false;
213 print_sockaddr(addr, sizeof(addr), &ss);
214 srv = talloc_strdup(frame, addr);
215 } else {
216 /* this copes with inet_ntoa brokenness */
217 srv = talloc_strdup(frame, server);
220 if (!srv) {
221 TALLOC_FREE(frame);
222 return false;
225 DEBUG(5,("ads_try_connect: sending CLDAP request to %s (realm: %s)\n",
226 srv, ads->server.realm));
228 ZERO_STRUCT( cldap_reply );
230 if ( !ads_cldap_netlogon_5(frame, srv, ads->server.realm, &cldap_reply ) ) {
231 DEBUG(3,("ads_try_connect: CLDAP request %s failed.\n", srv));
232 ret = false;
233 goto out;
236 /* Check the CLDAP reply flags */
238 if ( !(cldap_reply.server_type & NBT_SERVER_LDAP) ) {
239 DEBUG(1,("ads_try_connect: %s's CLDAP reply says it is not an LDAP server!\n",
240 srv));
241 ret = false;
242 goto out;
245 /* Fill in the ads->config values */
247 SAFE_FREE(ads->config.realm);
248 SAFE_FREE(ads->config.bind_path);
249 SAFE_FREE(ads->config.ldap_server_name);
250 SAFE_FREE(ads->config.server_site_name);
251 SAFE_FREE(ads->config.client_site_name);
252 SAFE_FREE(ads->server.workgroup);
254 ads->config.flags = cldap_reply.server_type;
255 ads->config.ldap_server_name = SMB_STRDUP(cldap_reply.pdc_dns_name);
256 ads->config.realm = SMB_STRDUP(cldap_reply.dns_domain);
257 strupper_m(ads->config.realm);
258 ads->config.bind_path = ads_build_dn(ads->config.realm);
259 if (*cldap_reply.server_site) {
260 ads->config.server_site_name =
261 SMB_STRDUP(cldap_reply.server_site);
263 if (*cldap_reply.client_site) {
264 ads->config.client_site_name =
265 SMB_STRDUP(cldap_reply.client_site);
267 ads->server.workgroup = SMB_STRDUP(cldap_reply.domain);
269 ads->ldap.port = gc ? LDAP_GC_PORT : LDAP_PORT;
270 if (!interpret_string_addr(&ads->ldap.ss, srv, 0)) {
271 DEBUG(1,("ads_try_connect: unable to convert %s "
272 "to an address\n",
273 srv));
274 ret = false;
275 goto out;
278 /* Store our site name. */
279 sitename_store( cldap_reply.domain, cldap_reply.client_site);
280 sitename_store( cldap_reply.dns_domain, cldap_reply.client_site);
282 ret = true;
284 out:
286 TALLOC_FREE(frame);
287 return ret;
290 /**********************************************************************
291 Try to find an AD dc using our internal name resolution routines
292 Try the realm first and then then workgroup name if netbios is not
293 disabled
294 **********************************************************************/
296 static NTSTATUS ads_find_dc(ADS_STRUCT *ads)
298 const char *c_domain;
299 const char *c_realm;
300 int count, i=0;
301 struct ip_service *ip_list;
302 const char *realm;
303 const char *domain;
304 bool got_realm = False;
305 bool use_own_domain = False;
306 char *sitename;
307 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
309 /* if the realm and workgroup are both empty, assume they are ours */
311 /* realm */
312 c_realm = ads->server.realm;
314 if ( !c_realm || !*c_realm ) {
315 /* special case where no realm and no workgroup means our own */
316 if ( !ads->server.workgroup || !*ads->server.workgroup ) {
317 use_own_domain = True;
318 c_realm = lp_realm();
322 if (c_realm && *c_realm)
323 got_realm = True;
325 /* we need to try once with the realm name and fallback to the
326 netbios domain name if we fail (if netbios has not been disabled */
328 if ( !got_realm && !lp_disable_netbios() ) {
329 c_realm = ads->server.workgroup;
330 if (!c_realm || !*c_realm) {
331 if ( use_own_domain )
332 c_realm = lp_workgroup();
336 if ( !c_realm || !*c_realm ) {
337 DEBUG(0,("ads_find_dc: no realm or workgroup! Don't know what to do\n"));
338 return NT_STATUS_INVALID_PARAMETER; /* rather need MISSING_PARAMETER ... */
341 if ( use_own_domain ) {
342 c_domain = lp_workgroup();
343 } else {
344 c_domain = ads->server.workgroup;
347 realm = c_realm;
348 domain = c_domain;
351 * In case of LDAP we use get_dc_name() as that
352 * creates the custom krb5.conf file
354 if (!(ads->auth.flags & ADS_AUTH_NO_BIND)) {
355 fstring srv_name;
356 struct sockaddr_storage ip_out;
358 DEBUG(6,("ads_find_dc: (ldap) looking for %s '%s'\n",
359 (got_realm ? "realm" : "domain"), realm));
361 if (get_dc_name(domain, realm, srv_name, &ip_out)) {
363 * we call ads_try_connect() to fill in the
364 * ads->config details
366 if (ads_try_connect(ads, srv_name, false)) {
367 return NT_STATUS_OK;
371 return NT_STATUS_NO_LOGON_SERVERS;
374 sitename = sitename_fetch(realm);
376 again:
378 DEBUG(6,("ads_find_dc: (cldap) looking for %s '%s'\n",
379 (got_realm ? "realm" : "domain"), realm));
381 status = get_sorted_dc_list(realm, sitename, &ip_list, &count, got_realm);
382 if (!NT_STATUS_IS_OK(status)) {
383 /* fall back to netbios if we can */
384 if ( got_realm && !lp_disable_netbios() ) {
385 got_realm = False;
386 goto again;
389 SAFE_FREE(sitename);
390 return status;
393 /* if we fail this loop, then giveup since all the IP addresses returned were dead */
394 for ( i=0; i<count; i++ ) {
395 char server[INET6_ADDRSTRLEN];
397 print_sockaddr(server, sizeof(server), &ip_list[i].ss);
399 if ( !NT_STATUS_IS_OK(check_negative_conn_cache(realm, server)) )
400 continue;
402 if (!got_realm) {
403 /* realm in this case is a workgroup name. We need
404 to ignore any IP addresses in the negative connection
405 cache that match ip addresses returned in the ad realm
406 case. It sucks that I have to reproduce the logic above... */
407 c_realm = ads->server.realm;
408 if ( !c_realm || !*c_realm ) {
409 if ( !ads->server.workgroup || !*ads->server.workgroup ) {
410 c_realm = lp_realm();
413 if (c_realm && *c_realm &&
414 !NT_STATUS_IS_OK(check_negative_conn_cache(c_realm, server))) {
415 /* Ensure we add the workgroup name for this
416 IP address as negative too. */
417 add_failed_connection_entry( realm, server, NT_STATUS_UNSUCCESSFUL );
418 continue;
422 if ( ads_try_connect(ads, server, false) ) {
423 SAFE_FREE(ip_list);
424 SAFE_FREE(sitename);
425 return NT_STATUS_OK;
428 /* keep track of failures */
429 add_failed_connection_entry( realm, server, NT_STATUS_UNSUCCESSFUL );
432 SAFE_FREE(ip_list);
434 /* In case we failed to contact one of our closest DC on our site we
435 * need to try to find another DC, retry with a site-less SRV DNS query
436 * - Guenther */
438 if (sitename) {
439 DEBUG(1,("ads_find_dc: failed to find a valid DC on our site (%s), "
440 "trying to find another DC\n", sitename));
441 SAFE_FREE(sitename);
442 namecache_delete(realm, 0x1C);
443 goto again;
446 return NT_STATUS_NO_LOGON_SERVERS;
449 /*********************************************************************
450 *********************************************************************/
452 static NTSTATUS ads_lookup_site(void)
454 ADS_STRUCT *ads = NULL;
455 ADS_STATUS ads_status;
456 NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
458 ads = ads_init(lp_realm(), NULL, NULL);
459 if (!ads) {
460 return NT_STATUS_NO_MEMORY;
463 /* The NO_BIND here will find a DC and set the client site
464 but not establish the TCP connection */
466 ads->auth.flags = ADS_AUTH_NO_BIND;
467 ads_status = ads_connect(ads);
468 if (!ADS_ERR_OK(ads_status)) {
469 DEBUG(4, ("ads_lookup_site: ads_connect to our realm failed! (%s)\n",
470 ads_errstr(ads_status)));
472 nt_status = ads_ntstatus(ads_status);
474 if (ads) {
475 ads_destroy(&ads);
478 return nt_status;
481 /*********************************************************************
482 *********************************************************************/
484 static const char* host_dns_domain(const char *fqdn)
486 const char *p = fqdn;
488 /* go to next char following '.' */
490 if ((p = strchr_m(fqdn, '.')) != NULL) {
491 p++;
494 return p;
499 * Connect to the Global Catalog server
500 * @param ads Pointer to an existing ADS_STRUCT
501 * @return status of connection
503 * Simple wrapper around ads_connect() that fills in the
504 * GC ldap server information
507 ADS_STATUS ads_connect_gc(ADS_STRUCT *ads)
509 TALLOC_CTX *frame = talloc_stackframe();
510 struct dns_rr_srv *gcs_list;
511 int num_gcs;
512 char *realm = ads->server.realm;
513 NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
514 ADS_STATUS ads_status = ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
515 int i;
516 bool done = false;
517 char *sitename = NULL;
519 if (!realm)
520 realm = lp_realm();
522 if ((sitename = sitename_fetch(realm)) == NULL) {
523 ads_lookup_site();
524 sitename = sitename_fetch(realm);
527 do {
528 /* We try once with a sitename and once without
529 (unless we don't have a sitename and then we're
530 done */
532 if (sitename == NULL)
533 done = true;
535 nt_status = ads_dns_query_gcs(frame, realm, sitename,
536 &gcs_list, &num_gcs);
538 SAFE_FREE(sitename);
540 if (!NT_STATUS_IS_OK(nt_status)) {
541 ads_status = ADS_ERROR_NT(nt_status);
542 goto done;
545 /* Loop until we get a successful connection or have gone
546 through them all. When connecting a GC server, make sure that
547 the realm is the server's DNS name and not the forest root */
549 for (i=0; i<num_gcs; i++) {
550 ads->server.gc = true;
551 ads->server.ldap_server = SMB_STRDUP(gcs_list[i].hostname);
552 ads->server.realm = SMB_STRDUP(host_dns_domain(ads->server.ldap_server));
553 ads_status = ads_connect(ads);
554 if (ADS_ERR_OK(ads_status)) {
555 /* Reset the bind_dn to "". A Global Catalog server
556 may host multiple domain trees in a forest.
557 Windows 2003 GC server will accept "" as the search
558 path to imply search all domain trees in the forest */
560 SAFE_FREE(ads->config.bind_path);
561 ads->config.bind_path = SMB_STRDUP("");
564 goto done;
566 SAFE_FREE(ads->server.ldap_server);
567 SAFE_FREE(ads->server.realm);
570 TALLOC_FREE(gcs_list);
571 num_gcs = 0;
572 } while (!done);
574 done:
575 SAFE_FREE(sitename);
576 talloc_destroy(frame);
578 return ads_status;
583 * Connect to the LDAP server
584 * @param ads Pointer to an existing ADS_STRUCT
585 * @return status of connection
587 ADS_STATUS ads_connect(ADS_STRUCT *ads)
589 int version = LDAP_VERSION3;
590 ADS_STATUS status;
591 NTSTATUS ntstatus;
592 char addr[INET6_ADDRSTRLEN];
594 ZERO_STRUCT(ads->ldap);
595 ads->ldap.last_attempt = time(NULL);
596 ads->ldap.wrap_type = ADS_SASLWRAP_TYPE_PLAIN;
598 /* try with a user specified server */
600 if (DEBUGLEVEL >= 11) {
601 char *s = NDR_PRINT_STRUCT_STRING(talloc_tos(), ads_struct, ads);
602 DEBUG(11,("ads_connect: entering\n"));
603 DEBUGADD(11,("%s\n", s));
604 TALLOC_FREE(s);
607 if (ads->server.ldap_server)
609 if (ads_try_connect(ads, ads->server.ldap_server, ads->server.gc)) {
610 goto got_connection;
613 /* The choice of which GC use is handled one level up in
614 ads_connect_gc(). If we continue on from here with
615 ads_find_dc() we will get GC searches on port 389 which
616 doesn't work. --jerry */
618 if (ads->server.gc == true) {
619 return ADS_ERROR(LDAP_OPERATIONS_ERROR);
623 ntstatus = ads_find_dc(ads);
624 if (NT_STATUS_IS_OK(ntstatus)) {
625 goto got_connection;
628 status = ADS_ERROR_NT(ntstatus);
629 goto out;
631 got_connection:
633 print_sockaddr(addr, sizeof(addr), &ads->ldap.ss);
634 DEBUG(3,("Successfully contacted LDAP server %s\n", addr));
636 if (!ads->auth.user_name) {
637 /* Must use the userPrincipalName value here or sAMAccountName
638 and not servicePrincipalName; found by Guenther Deschner */
640 if (asprintf(&ads->auth.user_name, "%s$", global_myname() ) == -1) {
641 DEBUG(0,("ads_connect: asprintf fail.\n"));
642 ads->auth.user_name = NULL;
646 if (!ads->auth.realm) {
647 ads->auth.realm = SMB_STRDUP(ads->config.realm);
650 if (!ads->auth.kdc_server) {
651 print_sockaddr(addr, sizeof(addr), &ads->ldap.ss);
652 ads->auth.kdc_server = SMB_STRDUP(addr);
655 #if KRB5_DNS_HACK
656 /* this is a really nasty hack to avoid ADS DNS problems. It needs a patch
657 to MIT kerberos to work (tridge) */
659 char *env = NULL;
660 if (asprintf(&env, "KRB5_KDC_ADDRESS_%s", ads->config.realm) > 0) {
661 setenv(env, ads->auth.kdc_server, 1);
662 free(env);
665 #endif
667 /* If the caller() requested no LDAP bind, then we are done */
669 if (ads->auth.flags & ADS_AUTH_NO_BIND) {
670 status = ADS_SUCCESS;
671 goto out;
674 ads->ldap.mem_ctx = talloc_init("ads LDAP connection memory");
675 if (!ads->ldap.mem_ctx) {
676 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
677 goto out;
680 /* Otherwise setup the TCP LDAP session */
682 ads->ldap.ld = ldap_open_with_timeout(ads->config.ldap_server_name,
683 ads->ldap.port, lp_ldap_timeout());
684 if (ads->ldap.ld == NULL) {
685 status = ADS_ERROR(LDAP_OPERATIONS_ERROR);
686 goto out;
688 DEBUG(3,("Connected to LDAP server %s\n", ads->config.ldap_server_name));
690 /* cache the successful connection for workgroup and realm */
691 if (ads_closest_dc(ads)) {
692 saf_store( ads->server.workgroup, ads->config.ldap_server_name);
693 saf_store( ads->server.realm, ads->config.ldap_server_name);
696 ldap_set_option(ads->ldap.ld, LDAP_OPT_PROTOCOL_VERSION, &version);
698 if ( lp_ldap_ssl_ads() ) {
699 status = ADS_ERROR(smb_ldap_start_tls(ads->ldap.ld, version));
700 if (!ADS_ERR_OK(status)) {
701 goto out;
705 /* fill in the current time and offsets */
707 status = ads_current_time( ads );
708 if ( !ADS_ERR_OK(status) ) {
709 goto out;
712 /* Now do the bind */
714 if (ads->auth.flags & ADS_AUTH_ANON_BIND) {
715 status = ADS_ERROR(ldap_simple_bind_s(ads->ldap.ld, NULL, NULL));
716 goto out;
719 if (ads->auth.flags & ADS_AUTH_SIMPLE_BIND) {
720 status = ADS_ERROR(ldap_simple_bind_s(ads->ldap.ld, ads->auth.user_name, ads->auth.password));
721 goto out;
724 status = ads_sasl_bind(ads);
726 out:
727 if (DEBUGLEVEL >= 11) {
728 char *s = NDR_PRINT_STRUCT_STRING(talloc_tos(), ads_struct, ads);
729 DEBUG(11,("ads_connect: leaving with: %s\n",
730 ads_errstr(status)));
731 DEBUGADD(11,("%s\n", s));
732 TALLOC_FREE(s);
735 return status;
739 * Connect to the LDAP server using given credentials
740 * @param ads Pointer to an existing ADS_STRUCT
741 * @return status of connection
743 ADS_STATUS ads_connect_user_creds(ADS_STRUCT *ads)
745 ads->auth.flags |= ADS_AUTH_USER_CREDS;
747 return ads_connect(ads);
751 * Disconnect the LDAP server
752 * @param ads Pointer to an existing ADS_STRUCT
754 void ads_disconnect(ADS_STRUCT *ads)
756 if (ads->ldap.ld) {
757 ldap_unbind(ads->ldap.ld);
758 ads->ldap.ld = NULL;
760 if (ads->ldap.wrap_ops && ads->ldap.wrap_ops->disconnect) {
761 ads->ldap.wrap_ops->disconnect(ads);
763 if (ads->ldap.mem_ctx) {
764 talloc_free(ads->ldap.mem_ctx);
766 ZERO_STRUCT(ads->ldap);
770 Duplicate a struct berval into talloc'ed memory
772 static struct berval *dup_berval(TALLOC_CTX *ctx, const struct berval *in_val)
774 struct berval *value;
776 if (!in_val) return NULL;
778 value = TALLOC_ZERO_P(ctx, struct berval);
779 if (value == NULL)
780 return NULL;
781 if (in_val->bv_len == 0) return value;
783 value->bv_len = in_val->bv_len;
784 value->bv_val = (char *)TALLOC_MEMDUP(ctx, in_val->bv_val,
785 in_val->bv_len);
786 return value;
790 Make a values list out of an array of (struct berval *)
792 static struct berval **ads_dup_values(TALLOC_CTX *ctx,
793 const struct berval **in_vals)
795 struct berval **values;
796 int i;
798 if (!in_vals) return NULL;
799 for (i=0; in_vals[i]; i++)
800 ; /* count values */
801 values = TALLOC_ZERO_ARRAY(ctx, struct berval *, i+1);
802 if (!values) return NULL;
804 for (i=0; in_vals[i]; i++) {
805 values[i] = dup_berval(ctx, in_vals[i]);
807 return values;
811 UTF8-encode a values list out of an array of (char *)
813 static char **ads_push_strvals(TALLOC_CTX *ctx, const char **in_vals)
815 char **values;
816 int i;
817 size_t size;
819 if (!in_vals) return NULL;
820 for (i=0; in_vals[i]; i++)
821 ; /* count values */
822 values = TALLOC_ZERO_ARRAY(ctx, char *, i+1);
823 if (!values) return NULL;
825 for (i=0; in_vals[i]; i++) {
826 if (!push_utf8_talloc(ctx, &values[i], in_vals[i], &size)) {
827 TALLOC_FREE(values);
828 return NULL;
831 return values;
835 Pull a (char *) array out of a UTF8-encoded values list
837 static char **ads_pull_strvals(TALLOC_CTX *ctx, const char **in_vals)
839 char **values;
840 int i;
841 size_t converted_size;
843 if (!in_vals) return NULL;
844 for (i=0; in_vals[i]; i++)
845 ; /* count values */
846 values = TALLOC_ZERO_ARRAY(ctx, char *, i+1);
847 if (!values) return NULL;
849 for (i=0; in_vals[i]; i++) {
850 if (!pull_utf8_talloc(ctx, &values[i], in_vals[i],
851 &converted_size)) {
852 DEBUG(0,("ads_pull_strvals: pull_utf8_talloc failed: "
853 "%s", strerror(errno)));
856 return values;
860 * Do a search with paged results. cookie must be null on the first
861 * call, and then returned on each subsequent call. It will be null
862 * again when the entire search is complete
863 * @param ads connection to ads server
864 * @param bind_path Base dn for the search
865 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
866 * @param expr Search expression - specified in local charset
867 * @param attrs Attributes to retrieve - specified in utf8 or ascii
868 * @param res ** which will contain results - free res* with ads_msgfree()
869 * @param count Number of entries retrieved on this page
870 * @param cookie The paged results cookie to be returned on subsequent calls
871 * @return status of search
873 static ADS_STATUS ads_do_paged_search_args(ADS_STRUCT *ads,
874 const char *bind_path,
875 int scope, const char *expr,
876 const char **attrs, void *args,
877 LDAPMessage **res,
878 int *count, struct berval **cookie)
880 int rc, i, version;
881 char *utf8_expr, *utf8_path, **search_attrs = NULL;
882 size_t converted_size;
883 LDAPControl PagedResults, NoReferrals, ExternalCtrl, *controls[4], **rcontrols;
884 BerElement *cookie_be = NULL;
885 struct berval *cookie_bv= NULL;
886 BerElement *ext_be = NULL;
887 struct berval *ext_bv= NULL;
889 TALLOC_CTX *ctx;
890 ads_control *external_control = (ads_control *) args;
892 *res = NULL;
894 if (!(ctx = talloc_init("ads_do_paged_search_args")))
895 return ADS_ERROR(LDAP_NO_MEMORY);
897 /* 0 means the conversion worked but the result was empty
898 so we only fail if it's -1. In any case, it always
899 at least nulls out the dest */
900 if (!push_utf8_talloc(ctx, &utf8_expr, expr, &converted_size) ||
901 !push_utf8_talloc(ctx, &utf8_path, bind_path, &converted_size))
903 rc = LDAP_NO_MEMORY;
904 goto done;
907 if (!attrs || !(*attrs))
908 search_attrs = NULL;
909 else {
910 /* This would be the utf8-encoded version...*/
911 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
912 if (!(search_attrs = str_list_copy(talloc_tos(), attrs))) {
913 rc = LDAP_NO_MEMORY;
914 goto done;
918 /* Paged results only available on ldap v3 or later */
919 ldap_get_option(ads->ldap.ld, LDAP_OPT_PROTOCOL_VERSION, &version);
920 if (version < LDAP_VERSION3) {
921 rc = LDAP_NOT_SUPPORTED;
922 goto done;
925 cookie_be = ber_alloc_t(LBER_USE_DER);
926 if (*cookie) {
927 ber_printf(cookie_be, "{iO}", (ber_int_t) 1000, *cookie);
928 ber_bvfree(*cookie); /* don't need it from last time */
929 *cookie = NULL;
930 } else {
931 ber_printf(cookie_be, "{io}", (ber_int_t) 1000, "", 0);
933 ber_flatten(cookie_be, &cookie_bv);
934 PagedResults.ldctl_oid = CONST_DISCARD(char *, ADS_PAGE_CTL_OID);
935 PagedResults.ldctl_iscritical = (char) 1;
936 PagedResults.ldctl_value.bv_len = cookie_bv->bv_len;
937 PagedResults.ldctl_value.bv_val = cookie_bv->bv_val;
939 NoReferrals.ldctl_oid = CONST_DISCARD(char *, ADS_NO_REFERRALS_OID);
940 NoReferrals.ldctl_iscritical = (char) 0;
941 NoReferrals.ldctl_value.bv_len = 0;
942 NoReferrals.ldctl_value.bv_val = CONST_DISCARD(char *, "");
944 if (external_control &&
945 (strequal(external_control->control, ADS_EXTENDED_DN_OID) ||
946 strequal(external_control->control, ADS_SD_FLAGS_OID))) {
948 ExternalCtrl.ldctl_oid = CONST_DISCARD(char *, external_control->control);
949 ExternalCtrl.ldctl_iscritical = (char) external_control->critical;
951 /* win2k does not accept a ldctl_value beeing passed in */
953 if (external_control->val != 0) {
955 if ((ext_be = ber_alloc_t(LBER_USE_DER)) == NULL ) {
956 rc = LDAP_NO_MEMORY;
957 goto done;
960 if ((ber_printf(ext_be, "{i}", (ber_int_t) external_control->val)) == -1) {
961 rc = LDAP_NO_MEMORY;
962 goto done;
964 if ((ber_flatten(ext_be, &ext_bv)) == -1) {
965 rc = LDAP_NO_MEMORY;
966 goto done;
969 ExternalCtrl.ldctl_value.bv_len = ext_bv->bv_len;
970 ExternalCtrl.ldctl_value.bv_val = ext_bv->bv_val;
972 } else {
973 ExternalCtrl.ldctl_value.bv_len = 0;
974 ExternalCtrl.ldctl_value.bv_val = NULL;
977 controls[0] = &NoReferrals;
978 controls[1] = &PagedResults;
979 controls[2] = &ExternalCtrl;
980 controls[3] = NULL;
982 } else {
983 controls[0] = &NoReferrals;
984 controls[1] = &PagedResults;
985 controls[2] = NULL;
988 /* we need to disable referrals as the openldap libs don't
989 handle them and paged results at the same time. Using them
990 together results in the result record containing the server
991 page control being removed from the result list (tridge/jmcd)
993 leaving this in despite the control that says don't generate
994 referrals, in case the server doesn't support it (jmcd)
996 ldap_set_option(ads->ldap.ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
998 rc = ldap_search_with_timeout(ads->ldap.ld, utf8_path, scope, utf8_expr,
999 search_attrs, 0, controls,
1000 NULL, LDAP_NO_LIMIT,
1001 (LDAPMessage **)res);
1003 ber_free(cookie_be, 1);
1004 ber_bvfree(cookie_bv);
1006 if (rc) {
1007 DEBUG(3,("ads_do_paged_search_args: ldap_search_with_timeout(%s) -> %s\n", expr,
1008 ldap_err2string(rc)));
1009 goto done;
1012 rc = ldap_parse_result(ads->ldap.ld, *res, NULL, NULL, NULL,
1013 NULL, &rcontrols, 0);
1015 if (!rcontrols) {
1016 goto done;
1019 for (i=0; rcontrols[i]; i++) {
1020 if (strcmp(ADS_PAGE_CTL_OID, rcontrols[i]->ldctl_oid) == 0) {
1021 cookie_be = ber_init(&rcontrols[i]->ldctl_value);
1022 ber_scanf(cookie_be,"{iO}", (ber_int_t *) count,
1023 &cookie_bv);
1024 /* the berval is the cookie, but must be freed when
1025 it is all done */
1026 if (cookie_bv->bv_len) /* still more to do */
1027 *cookie=ber_bvdup(cookie_bv);
1028 else
1029 *cookie=NULL;
1030 ber_bvfree(cookie_bv);
1031 ber_free(cookie_be, 1);
1032 break;
1035 ldap_controls_free(rcontrols);
1037 done:
1038 talloc_destroy(ctx);
1040 if (ext_be) {
1041 ber_free(ext_be, 1);
1044 if (ext_bv) {
1045 ber_bvfree(ext_bv);
1048 /* if/when we decide to utf8-encode attrs, take out this next line */
1049 TALLOC_FREE(search_attrs);
1051 return ADS_ERROR(rc);
1054 static ADS_STATUS ads_do_paged_search(ADS_STRUCT *ads, const char *bind_path,
1055 int scope, const char *expr,
1056 const char **attrs, LDAPMessage **res,
1057 int *count, struct berval **cookie)
1059 return ads_do_paged_search_args(ads, bind_path, scope, expr, attrs, NULL, res, count, cookie);
1064 * Get all results for a search. This uses ads_do_paged_search() to return
1065 * all entries in a large search.
1066 * @param ads connection to ads server
1067 * @param bind_path Base dn for the search
1068 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1069 * @param expr Search expression
1070 * @param attrs Attributes to retrieve
1071 * @param res ** which will contain results - free res* with ads_msgfree()
1072 * @return status of search
1074 ADS_STATUS ads_do_search_all_args(ADS_STRUCT *ads, const char *bind_path,
1075 int scope, const char *expr,
1076 const char **attrs, void *args,
1077 LDAPMessage **res)
1079 struct berval *cookie = NULL;
1080 int count = 0;
1081 ADS_STATUS status;
1083 *res = NULL;
1084 status = ads_do_paged_search_args(ads, bind_path, scope, expr, attrs, args, res,
1085 &count, &cookie);
1087 if (!ADS_ERR_OK(status))
1088 return status;
1090 #ifdef HAVE_LDAP_ADD_RESULT_ENTRY
1091 while (cookie) {
1092 LDAPMessage *res2 = NULL;
1093 ADS_STATUS status2;
1094 LDAPMessage *msg, *next;
1096 status2 = ads_do_paged_search_args(ads, bind_path, scope, expr,
1097 attrs, args, &res2, &count, &cookie);
1099 if (!ADS_ERR_OK(status2)) break;
1101 /* this relies on the way that ldap_add_result_entry() works internally. I hope
1102 that this works on all ldap libs, but I have only tested with openldap */
1103 for (msg = ads_first_message(ads, res2); msg; msg = next) {
1104 next = ads_next_message(ads, msg);
1105 ldap_add_result_entry((LDAPMessage **)res, msg);
1107 /* note that we do not free res2, as the memory is now
1108 part of the main returned list */
1110 #else
1111 DEBUG(0, ("no ldap_add_result_entry() support in LDAP libs!\n"));
1112 status = ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
1113 #endif
1115 return status;
1118 ADS_STATUS ads_do_search_all(ADS_STRUCT *ads, const char *bind_path,
1119 int scope, const char *expr,
1120 const char **attrs, LDAPMessage **res)
1122 return ads_do_search_all_args(ads, bind_path, scope, expr, attrs, NULL, res);
1125 ADS_STATUS ads_do_search_all_sd_flags(ADS_STRUCT *ads, const char *bind_path,
1126 int scope, const char *expr,
1127 const char **attrs, uint32 sd_flags,
1128 LDAPMessage **res)
1130 ads_control args;
1132 args.control = ADS_SD_FLAGS_OID;
1133 args.val = sd_flags;
1134 args.critical = True;
1136 return ads_do_search_all_args(ads, bind_path, scope, expr, attrs, &args, res);
1141 * Run a function on all results for a search. Uses ads_do_paged_search() and
1142 * runs the function as each page is returned, using ads_process_results()
1143 * @param ads connection to ads server
1144 * @param bind_path Base dn for the search
1145 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1146 * @param expr Search expression - specified in local charset
1147 * @param attrs Attributes to retrieve - specified in UTF-8 or ascii
1148 * @param fn Function which takes attr name, values list, and data_area
1149 * @param data_area Pointer which is passed to function on each call
1150 * @return status of search
1152 ADS_STATUS ads_do_search_all_fn(ADS_STRUCT *ads, const char *bind_path,
1153 int scope, const char *expr, const char **attrs,
1154 bool (*fn)(ADS_STRUCT *, char *, void **, void *),
1155 void *data_area)
1157 struct berval *cookie = NULL;
1158 int count = 0;
1159 ADS_STATUS status;
1160 LDAPMessage *res;
1162 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs, &res,
1163 &count, &cookie);
1165 if (!ADS_ERR_OK(status)) return status;
1167 ads_process_results(ads, res, fn, data_area);
1168 ads_msgfree(ads, res);
1170 while (cookie) {
1171 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs,
1172 &res, &count, &cookie);
1174 if (!ADS_ERR_OK(status)) break;
1176 ads_process_results(ads, res, fn, data_area);
1177 ads_msgfree(ads, res);
1180 return status;
1184 * Do a search with a timeout.
1185 * @param ads connection to ads server
1186 * @param bind_path Base dn for the search
1187 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1188 * @param expr Search expression
1189 * @param attrs Attributes to retrieve
1190 * @param res ** which will contain results - free res* with ads_msgfree()
1191 * @return status of search
1193 ADS_STATUS ads_do_search(ADS_STRUCT *ads, const char *bind_path, int scope,
1194 const char *expr,
1195 const char **attrs, LDAPMessage **res)
1197 int rc;
1198 char *utf8_expr, *utf8_path, **search_attrs = NULL;
1199 size_t converted_size;
1200 TALLOC_CTX *ctx;
1202 *res = NULL;
1203 if (!(ctx = talloc_init("ads_do_search"))) {
1204 DEBUG(1,("ads_do_search: talloc_init() failed!"));
1205 return ADS_ERROR(LDAP_NO_MEMORY);
1208 /* 0 means the conversion worked but the result was empty
1209 so we only fail if it's negative. In any case, it always
1210 at least nulls out the dest */
1211 if (!push_utf8_talloc(ctx, &utf8_expr, expr, &converted_size) ||
1212 !push_utf8_talloc(ctx, &utf8_path, bind_path, &converted_size))
1214 DEBUG(1,("ads_do_search: push_utf8_talloc() failed!"));
1215 rc = LDAP_NO_MEMORY;
1216 goto done;
1219 if (!attrs || !(*attrs))
1220 search_attrs = NULL;
1221 else {
1222 /* This would be the utf8-encoded version...*/
1223 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
1224 if (!(search_attrs = str_list_copy(talloc_tos(), attrs)))
1226 DEBUG(1,("ads_do_search: str_list_copy() failed!"));
1227 rc = LDAP_NO_MEMORY;
1228 goto done;
1232 /* see the note in ads_do_paged_search - we *must* disable referrals */
1233 ldap_set_option(ads->ldap.ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
1235 rc = ldap_search_with_timeout(ads->ldap.ld, utf8_path, scope, utf8_expr,
1236 search_attrs, 0, NULL, NULL,
1237 LDAP_NO_LIMIT,
1238 (LDAPMessage **)res);
1240 if (rc == LDAP_SIZELIMIT_EXCEEDED) {
1241 DEBUG(3,("Warning! sizelimit exceeded in ldap. Truncating.\n"));
1242 rc = 0;
1245 done:
1246 talloc_destroy(ctx);
1247 /* if/when we decide to utf8-encode attrs, take out this next line */
1248 TALLOC_FREE(search_attrs);
1249 return ADS_ERROR(rc);
1252 * Do a general ADS search
1253 * @param ads connection to ads server
1254 * @param res ** which will contain results - free res* with ads_msgfree()
1255 * @param expr Search expression
1256 * @param attrs Attributes to retrieve
1257 * @return status of search
1259 ADS_STATUS ads_search(ADS_STRUCT *ads, LDAPMessage **res,
1260 const char *expr, const char **attrs)
1262 return ads_do_search(ads, ads->config.bind_path, LDAP_SCOPE_SUBTREE,
1263 expr, attrs, res);
1267 * Do a search on a specific DistinguishedName
1268 * @param ads connection to ads server
1269 * @param res ** which will contain results - free res* with ads_msgfree()
1270 * @param dn DistinguishName to search
1271 * @param attrs Attributes to retrieve
1272 * @return status of search
1274 ADS_STATUS ads_search_dn(ADS_STRUCT *ads, LDAPMessage **res,
1275 const char *dn, const char **attrs)
1277 return ads_do_search(ads, dn, LDAP_SCOPE_BASE, "(objectclass=*)",
1278 attrs, res);
1282 * Free up memory from a ads_search
1283 * @param ads connection to ads server
1284 * @param msg Search results to free
1286 void ads_msgfree(ADS_STRUCT *ads, LDAPMessage *msg)
1288 if (!msg) return;
1289 ldap_msgfree(msg);
1293 * Get a dn from search results
1294 * @param ads connection to ads server
1295 * @param msg Search result
1296 * @return dn string
1298 char *ads_get_dn(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, LDAPMessage *msg)
1300 char *utf8_dn, *unix_dn;
1301 size_t converted_size;
1303 utf8_dn = ldap_get_dn(ads->ldap.ld, msg);
1305 if (!utf8_dn) {
1306 DEBUG (5, ("ads_get_dn: ldap_get_dn failed\n"));
1307 return NULL;
1310 if (!pull_utf8_talloc(mem_ctx, &unix_dn, utf8_dn, &converted_size)) {
1311 DEBUG(0,("ads_get_dn: string conversion failure utf8 [%s]\n",
1312 utf8_dn ));
1313 return NULL;
1315 ldap_memfree(utf8_dn);
1316 return unix_dn;
1320 * Get the parent from a dn
1321 * @param dn the dn to return the parent from
1322 * @return parent dn string
1324 char *ads_parent_dn(const char *dn)
1326 char *p;
1328 if (dn == NULL) {
1329 return NULL;
1332 p = strchr(dn, ',');
1334 if (p == NULL) {
1335 return NULL;
1338 return p+1;
1342 * Find a machine account given a hostname
1343 * @param ads connection to ads server
1344 * @param res ** which will contain results - free res* with ads_msgfree()
1345 * @param host Hostname to search for
1346 * @return status of search
1348 ADS_STATUS ads_find_machine_acct(ADS_STRUCT *ads, LDAPMessage **res,
1349 const char *machine)
1351 ADS_STATUS status;
1352 char *expr;
1353 const char *attrs[] = {"*", "nTSecurityDescriptor", NULL};
1355 *res = NULL;
1357 /* the easiest way to find a machine account anywhere in the tree
1358 is to look for hostname$ */
1359 if (asprintf(&expr, "(samAccountName=%s$)", machine) == -1) {
1360 DEBUG(1, ("asprintf failed!\n"));
1361 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1364 status = ads_search(ads, res, expr, attrs);
1365 SAFE_FREE(expr);
1366 return status;
1370 * Initialize a list of mods to be used in a modify request
1371 * @param ctx An initialized TALLOC_CTX
1372 * @return allocated ADS_MODLIST
1374 ADS_MODLIST ads_init_mods(TALLOC_CTX *ctx)
1376 #define ADS_MODLIST_ALLOC_SIZE 10
1377 LDAPMod **mods;
1379 if ((mods = TALLOC_ZERO_ARRAY(ctx, LDAPMod *, ADS_MODLIST_ALLOC_SIZE + 1)))
1380 /* -1 is safety to make sure we don't go over the end.
1381 need to reset it to NULL before doing ldap modify */
1382 mods[ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1384 return (ADS_MODLIST)mods;
1389 add an attribute to the list, with values list already constructed
1391 static ADS_STATUS ads_modlist_add(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1392 int mod_op, const char *name,
1393 const void *_invals)
1395 const void **invals = (const void **)_invals;
1396 int curmod;
1397 LDAPMod **modlist = (LDAPMod **) *mods;
1398 struct berval **ber_values = NULL;
1399 char **char_values = NULL;
1401 if (!invals) {
1402 mod_op = LDAP_MOD_DELETE;
1403 } else {
1404 if (mod_op & LDAP_MOD_BVALUES)
1405 ber_values = ads_dup_values(ctx,
1406 (const struct berval **)invals);
1407 else
1408 char_values = ads_push_strvals(ctx,
1409 (const char **) invals);
1412 /* find the first empty slot */
1413 for (curmod=0; modlist[curmod] && modlist[curmod] != (LDAPMod *) -1;
1414 curmod++);
1415 if (modlist[curmod] == (LDAPMod *) -1) {
1416 if (!(modlist = TALLOC_REALLOC_ARRAY(ctx, modlist, LDAPMod *,
1417 curmod+ADS_MODLIST_ALLOC_SIZE+1)))
1418 return ADS_ERROR(LDAP_NO_MEMORY);
1419 memset(&modlist[curmod], 0,
1420 ADS_MODLIST_ALLOC_SIZE*sizeof(LDAPMod *));
1421 modlist[curmod+ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1422 *mods = (ADS_MODLIST)modlist;
1425 if (!(modlist[curmod] = TALLOC_ZERO_P(ctx, LDAPMod)))
1426 return ADS_ERROR(LDAP_NO_MEMORY);
1427 modlist[curmod]->mod_type = talloc_strdup(ctx, name);
1428 if (mod_op & LDAP_MOD_BVALUES) {
1429 modlist[curmod]->mod_bvalues = ber_values;
1430 } else if (mod_op & LDAP_MOD_DELETE) {
1431 modlist[curmod]->mod_values = NULL;
1432 } else {
1433 modlist[curmod]->mod_values = char_values;
1436 modlist[curmod]->mod_op = mod_op;
1437 return ADS_ERROR(LDAP_SUCCESS);
1441 * Add a single string value to a mod list
1442 * @param ctx An initialized TALLOC_CTX
1443 * @param mods An initialized ADS_MODLIST
1444 * @param name The attribute name to add
1445 * @param val The value to add - NULL means DELETE
1446 * @return ADS STATUS indicating success of add
1448 ADS_STATUS ads_mod_str(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1449 const char *name, const char *val)
1451 const char *values[2];
1453 values[0] = val;
1454 values[1] = NULL;
1456 if (!val)
1457 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1458 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE, name, values);
1462 * Add an array of string values to a mod list
1463 * @param ctx An initialized TALLOC_CTX
1464 * @param mods An initialized ADS_MODLIST
1465 * @param name The attribute name to add
1466 * @param vals The array of string values to add - NULL means DELETE
1467 * @return ADS STATUS indicating success of add
1469 ADS_STATUS ads_mod_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1470 const char *name, const char **vals)
1472 if (!vals)
1473 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1474 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE,
1475 name, (const void **) vals);
1478 #if 0
1480 * Add a single ber-encoded value to a mod list
1481 * @param ctx An initialized TALLOC_CTX
1482 * @param mods An initialized ADS_MODLIST
1483 * @param name The attribute name to add
1484 * @param val The value to add - NULL means DELETE
1485 * @return ADS STATUS indicating success of add
1487 static ADS_STATUS ads_mod_ber(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1488 const char *name, const struct berval *val)
1490 const struct berval *values[2];
1492 values[0] = val;
1493 values[1] = NULL;
1494 if (!val)
1495 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1496 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE|LDAP_MOD_BVALUES,
1497 name, (const void **) values);
1499 #endif
1502 * Perform an ldap modify
1503 * @param ads connection to ads server
1504 * @param mod_dn DistinguishedName to modify
1505 * @param mods list of modifications to perform
1506 * @return status of modify
1508 ADS_STATUS ads_gen_mod(ADS_STRUCT *ads, const char *mod_dn, ADS_MODLIST mods)
1510 int ret,i;
1511 char *utf8_dn = NULL;
1512 size_t converted_size;
1514 this control is needed to modify that contains a currently
1515 non-existent attribute (but allowable for the object) to run
1517 LDAPControl PermitModify = {
1518 CONST_DISCARD(char *, ADS_PERMIT_MODIFY_OID),
1519 {0, NULL},
1520 (char) 1};
1521 LDAPControl *controls[2];
1523 controls[0] = &PermitModify;
1524 controls[1] = NULL;
1526 if (!push_utf8_talloc(talloc_tos(), &utf8_dn, mod_dn, &converted_size)) {
1527 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1530 /* find the end of the list, marked by NULL or -1 */
1531 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1532 /* make sure the end of the list is NULL */
1533 mods[i] = NULL;
1534 ret = ldap_modify_ext_s(ads->ldap.ld, utf8_dn,
1535 (LDAPMod **) mods, controls, NULL);
1536 TALLOC_FREE(utf8_dn);
1537 return ADS_ERROR(ret);
1541 * Perform an ldap add
1542 * @param ads connection to ads server
1543 * @param new_dn DistinguishedName to add
1544 * @param mods list of attributes and values for DN
1545 * @return status of add
1547 ADS_STATUS ads_gen_add(ADS_STRUCT *ads, const char *new_dn, ADS_MODLIST mods)
1549 int ret, i;
1550 char *utf8_dn = NULL;
1551 size_t converted_size;
1553 if (!push_utf8_talloc(talloc_tos(), &utf8_dn, new_dn, &converted_size)) {
1554 DEBUG(1, ("ads_gen_add: push_utf8_talloc failed!"));
1555 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1558 /* find the end of the list, marked by NULL or -1 */
1559 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1560 /* make sure the end of the list is NULL */
1561 mods[i] = NULL;
1563 ret = ldap_add_s(ads->ldap.ld, utf8_dn, (LDAPMod**)mods);
1564 TALLOC_FREE(utf8_dn);
1565 return ADS_ERROR(ret);
1569 * Delete a DistinguishedName
1570 * @param ads connection to ads server
1571 * @param new_dn DistinguishedName to delete
1572 * @return status of delete
1574 ADS_STATUS ads_del_dn(ADS_STRUCT *ads, char *del_dn)
1576 int ret;
1577 char *utf8_dn = NULL;
1578 size_t converted_size;
1579 if (!push_utf8_talloc(talloc_tos(), &utf8_dn, del_dn, &converted_size)) {
1580 DEBUG(1, ("ads_del_dn: push_utf8_talloc failed!"));
1581 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1584 ret = ldap_delete_s(ads->ldap.ld, utf8_dn);
1585 TALLOC_FREE(utf8_dn);
1586 return ADS_ERROR(ret);
1590 * Build an org unit string
1591 * if org unit is Computers or blank then assume a container, otherwise
1592 * assume a / separated list of organisational units.
1593 * jmcd: '\' is now used for escapes so certain chars can be in the ou (e.g. #)
1594 * @param ads connection to ads server
1595 * @param org_unit Organizational unit
1596 * @return org unit string - caller must free
1598 char *ads_ou_string(ADS_STRUCT *ads, const char *org_unit)
1600 char *ret = NULL;
1602 if (!org_unit || !*org_unit) {
1604 ret = ads_default_ou_string(ads, WELL_KNOWN_GUID_COMPUTERS);
1606 /* samba4 might not yet respond to a wellknownobject-query */
1607 return ret ? ret : SMB_STRDUP("cn=Computers");
1610 if (strequal(org_unit, "Computers")) {
1611 return SMB_STRDUP("cn=Computers");
1614 /* jmcd: removed "\\" from the separation chars, because it is
1615 needed as an escape for chars like '#' which are valid in an
1616 OU name */
1617 return ads_build_path(org_unit, "/", "ou=", 1);
1621 * Get a org unit string for a well-known GUID
1622 * @param ads connection to ads server
1623 * @param wknguid Well known GUID
1624 * @return org unit string - caller must free
1626 char *ads_default_ou_string(ADS_STRUCT *ads, const char *wknguid)
1628 ADS_STATUS status;
1629 LDAPMessage *res = NULL;
1630 char *base, *wkn_dn = NULL, *ret = NULL, **wkn_dn_exp = NULL,
1631 **bind_dn_exp = NULL;
1632 const char *attrs[] = {"distinguishedName", NULL};
1633 int new_ln, wkn_ln, bind_ln, i;
1635 if (wknguid == NULL) {
1636 return NULL;
1639 if (asprintf(&base, "<WKGUID=%s,%s>", wknguid, ads->config.bind_path ) == -1) {
1640 DEBUG(1, ("asprintf failed!\n"));
1641 return NULL;
1644 status = ads_search_dn(ads, &res, base, attrs);
1645 if (!ADS_ERR_OK(status)) {
1646 DEBUG(1,("Failed while searching for: %s\n", base));
1647 goto out;
1650 if (ads_count_replies(ads, res) != 1) {
1651 goto out;
1654 /* substitute the bind-path from the well-known-guid-search result */
1655 wkn_dn = ads_get_dn(ads, talloc_tos(), res);
1656 if (!wkn_dn) {
1657 goto out;
1660 wkn_dn_exp = ldap_explode_dn(wkn_dn, 0);
1661 if (!wkn_dn_exp) {
1662 goto out;
1665 bind_dn_exp = ldap_explode_dn(ads->config.bind_path, 0);
1666 if (!bind_dn_exp) {
1667 goto out;
1670 for (wkn_ln=0; wkn_dn_exp[wkn_ln]; wkn_ln++)
1672 for (bind_ln=0; bind_dn_exp[bind_ln]; bind_ln++)
1675 new_ln = wkn_ln - bind_ln;
1677 ret = SMB_STRDUP(wkn_dn_exp[0]);
1678 if (!ret) {
1679 goto out;
1682 for (i=1; i < new_ln; i++) {
1683 char *s = NULL;
1685 if (asprintf(&s, "%s,%s", ret, wkn_dn_exp[i]) == -1) {
1686 SAFE_FREE(ret);
1687 goto out;
1690 SAFE_FREE(ret);
1691 ret = SMB_STRDUP(s);
1692 free(s);
1693 if (!ret) {
1694 goto out;
1698 out:
1699 SAFE_FREE(base);
1700 ads_msgfree(ads, res);
1701 TALLOC_FREE(wkn_dn);
1702 if (wkn_dn_exp) {
1703 ldap_value_free(wkn_dn_exp);
1705 if (bind_dn_exp) {
1706 ldap_value_free(bind_dn_exp);
1709 return ret;
1713 * Adds (appends) an item to an attribute array, rather then
1714 * replacing the whole list
1715 * @param ctx An initialized TALLOC_CTX
1716 * @param mods An initialized ADS_MODLIST
1717 * @param name name of the ldap attribute to append to
1718 * @param vals an array of values to add
1719 * @return status of addition
1722 ADS_STATUS ads_add_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1723 const char *name, const char **vals)
1725 return ads_modlist_add(ctx, mods, LDAP_MOD_ADD, name,
1726 (const void *) vals);
1730 * Determines the an account's current KVNO via an LDAP lookup
1731 * @param ads An initialized ADS_STRUCT
1732 * @param account_name the NT samaccountname.
1733 * @return the kvno for the account, or -1 in case of a failure.
1736 uint32 ads_get_kvno(ADS_STRUCT *ads, const char *account_name)
1738 LDAPMessage *res = NULL;
1739 uint32 kvno = (uint32)-1; /* -1 indicates a failure */
1740 char *filter;
1741 const char *attrs[] = {"msDS-KeyVersionNumber", NULL};
1742 char *dn_string = NULL;
1743 ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS);
1745 DEBUG(5,("ads_get_kvno: Searching for account %s\n", account_name));
1746 if (asprintf(&filter, "(samAccountName=%s)", account_name) == -1) {
1747 return kvno;
1749 ret = ads_search(ads, &res, filter, attrs);
1750 SAFE_FREE(filter);
1751 if (!ADS_ERR_OK(ret) || (ads_count_replies(ads, res) != 1)) {
1752 DEBUG(1,("ads_get_kvno: Account for %s not found.\n", account_name));
1753 ads_msgfree(ads, res);
1754 return kvno;
1757 dn_string = ads_get_dn(ads, talloc_tos(), res);
1758 if (!dn_string) {
1759 DEBUG(0,("ads_get_kvno: out of memory.\n"));
1760 ads_msgfree(ads, res);
1761 return kvno;
1763 DEBUG(5,("ads_get_kvno: Using: %s\n", dn_string));
1764 TALLOC_FREE(dn_string);
1766 /* ---------------------------------------------------------
1767 * 0 is returned as a default KVNO from this point on...
1768 * This is done because Windows 2000 does not support key
1769 * version numbers. Chances are that a failure in the next
1770 * step is simply due to Windows 2000 being used for a
1771 * domain controller. */
1772 kvno = 0;
1774 if (!ads_pull_uint32(ads, res, "msDS-KeyVersionNumber", &kvno)) {
1775 DEBUG(3,("ads_get_kvno: Error Determining KVNO!\n"));
1776 DEBUG(3,("ads_get_kvno: Windows 2000 does not support KVNO's, so this may be normal.\n"));
1777 ads_msgfree(ads, res);
1778 return kvno;
1781 /* Success */
1782 DEBUG(5,("ads_get_kvno: Looked Up KVNO of: %d\n", kvno));
1783 ads_msgfree(ads, res);
1784 return kvno;
1788 * Determines the computer account's current KVNO via an LDAP lookup
1789 * @param ads An initialized ADS_STRUCT
1790 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1791 * @return the kvno for the computer account, or -1 in case of a failure.
1794 uint32_t ads_get_machine_kvno(ADS_STRUCT *ads, const char *machine_name)
1796 char *computer_account = NULL;
1797 uint32_t kvno = -1;
1799 if (asprintf(&computer_account, "%s$", machine_name) < 0) {
1800 return kvno;
1803 kvno = ads_get_kvno(ads, computer_account);
1804 free(computer_account);
1806 return kvno;
1810 * This clears out all registered spn's for a given hostname
1811 * @param ads An initilaized ADS_STRUCT
1812 * @param machine_name the NetBIOS name of the computer.
1813 * @return 0 upon success, non-zero otherwise.
1816 ADS_STATUS ads_clear_service_principal_names(ADS_STRUCT *ads, const char *machine_name)
1818 TALLOC_CTX *ctx;
1819 LDAPMessage *res = NULL;
1820 ADS_MODLIST mods;
1821 const char *servicePrincipalName[1] = {NULL};
1822 ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS);
1823 char *dn_string = NULL;
1825 ret = ads_find_machine_acct(ads, &res, machine_name);
1826 if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
1827 DEBUG(5,("ads_clear_service_principal_names: WARNING: Host Account for %s not found... skipping operation.\n", machine_name));
1828 DEBUG(5,("ads_clear_service_principal_names: WARNING: Service Principals for %s have NOT been cleared.\n", machine_name));
1829 ads_msgfree(ads, res);
1830 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1833 DEBUG(5,("ads_clear_service_principal_names: Host account for %s found\n", machine_name));
1834 ctx = talloc_init("ads_clear_service_principal_names");
1835 if (!ctx) {
1836 ads_msgfree(ads, res);
1837 return ADS_ERROR(LDAP_NO_MEMORY);
1840 if (!(mods = ads_init_mods(ctx))) {
1841 talloc_destroy(ctx);
1842 ads_msgfree(ads, res);
1843 return ADS_ERROR(LDAP_NO_MEMORY);
1845 ret = ads_mod_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1846 if (!ADS_ERR_OK(ret)) {
1847 DEBUG(1,("ads_clear_service_principal_names: Error creating strlist.\n"));
1848 ads_msgfree(ads, res);
1849 talloc_destroy(ctx);
1850 return ret;
1852 dn_string = ads_get_dn(ads, talloc_tos(), res);
1853 if (!dn_string) {
1854 talloc_destroy(ctx);
1855 ads_msgfree(ads, res);
1856 return ADS_ERROR(LDAP_NO_MEMORY);
1858 ret = ads_gen_mod(ads, dn_string, mods);
1859 TALLOC_FREE(dn_string);
1860 if (!ADS_ERR_OK(ret)) {
1861 DEBUG(1,("ads_clear_service_principal_names: Error: Updating Service Principals for machine %s in LDAP\n",
1862 machine_name));
1863 ads_msgfree(ads, res);
1864 talloc_destroy(ctx);
1865 return ret;
1868 ads_msgfree(ads, res);
1869 talloc_destroy(ctx);
1870 return ret;
1874 * This adds a service principal name to an existing computer account
1875 * (found by hostname) in AD.
1876 * @param ads An initialized ADS_STRUCT
1877 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1878 * @param my_fqdn The fully qualified DNS name of the machine
1879 * @param spn A string of the service principal to add, i.e. 'host'
1880 * @return 0 upon sucess, or non-zero if a failure occurs
1883 ADS_STATUS ads_add_service_principal_name(ADS_STRUCT *ads, const char *machine_name,
1884 const char *my_fqdn, const char *spn)
1886 ADS_STATUS ret;
1887 TALLOC_CTX *ctx;
1888 LDAPMessage *res = NULL;
1889 char *psp1, *psp2;
1890 ADS_MODLIST mods;
1891 char *dn_string = NULL;
1892 const char *servicePrincipalName[3] = {NULL, NULL, NULL};
1894 ret = ads_find_machine_acct(ads, &res, machine_name);
1895 if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
1896 DEBUG(1,("ads_add_service_principal_name: WARNING: Host Account for %s not found... skipping operation.\n",
1897 machine_name));
1898 DEBUG(1,("ads_add_service_principal_name: WARNING: Service Principal '%s/%s@%s' has NOT been added.\n",
1899 spn, machine_name, ads->config.realm));
1900 ads_msgfree(ads, res);
1901 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1904 DEBUG(1,("ads_add_service_principal_name: Host account for %s found\n", machine_name));
1905 if (!(ctx = talloc_init("ads_add_service_principal_name"))) {
1906 ads_msgfree(ads, res);
1907 return ADS_ERROR(LDAP_NO_MEMORY);
1910 /* add short name spn */
1912 if ( (psp1 = talloc_asprintf(ctx, "%s/%s", spn, machine_name)) == NULL ) {
1913 talloc_destroy(ctx);
1914 ads_msgfree(ads, res);
1915 return ADS_ERROR(LDAP_NO_MEMORY);
1917 strupper_m(psp1);
1918 strlower_m(&psp1[strlen(spn)]);
1919 servicePrincipalName[0] = psp1;
1921 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n",
1922 psp1, machine_name));
1925 /* add fully qualified spn */
1927 if ( (psp2 = talloc_asprintf(ctx, "%s/%s", spn, my_fqdn)) == NULL ) {
1928 ret = ADS_ERROR(LDAP_NO_MEMORY);
1929 goto out;
1931 strupper_m(psp2);
1932 strlower_m(&psp2[strlen(spn)]);
1933 servicePrincipalName[1] = psp2;
1935 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n",
1936 psp2, machine_name));
1938 if ( (mods = ads_init_mods(ctx)) == NULL ) {
1939 ret = ADS_ERROR(LDAP_NO_MEMORY);
1940 goto out;
1943 ret = ads_add_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1944 if (!ADS_ERR_OK(ret)) {
1945 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
1946 goto out;
1949 if ( (dn_string = ads_get_dn(ads, ctx, res)) == NULL ) {
1950 ret = ADS_ERROR(LDAP_NO_MEMORY);
1951 goto out;
1954 ret = ads_gen_mod(ads, dn_string, mods);
1955 if (!ADS_ERR_OK(ret)) {
1956 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
1957 goto out;
1960 out:
1961 TALLOC_FREE( ctx );
1962 ads_msgfree(ads, res);
1963 return ret;
1967 * adds a machine account to the ADS server
1968 * @param ads An intialized ADS_STRUCT
1969 * @param machine_name - the NetBIOS machine name of this account.
1970 * @param account_type A number indicating the type of account to create
1971 * @param org_unit The LDAP path in which to place this account
1972 * @return 0 upon success, or non-zero otherwise
1975 ADS_STATUS ads_create_machine_acct(ADS_STRUCT *ads, const char *machine_name,
1976 const char *org_unit)
1978 ADS_STATUS ret;
1979 char *samAccountName, *controlstr;
1980 TALLOC_CTX *ctx;
1981 ADS_MODLIST mods;
1982 char *machine_escaped = NULL;
1983 char *new_dn;
1984 const char *objectClass[] = {"top", "person", "organizationalPerson",
1985 "user", "computer", NULL};
1986 LDAPMessage *res = NULL;
1987 uint32 acct_control = ( UF_WORKSTATION_TRUST_ACCOUNT |\
1988 UF_DONT_EXPIRE_PASSWD |\
1989 UF_ACCOUNTDISABLE );
1991 if (!(ctx = talloc_init("ads_add_machine_acct")))
1992 return ADS_ERROR(LDAP_NO_MEMORY);
1994 ret = ADS_ERROR(LDAP_NO_MEMORY);
1996 machine_escaped = escape_rdn_val_string_alloc(machine_name);
1997 if (!machine_escaped) {
1998 goto done;
2001 new_dn = talloc_asprintf(ctx, "cn=%s,%s", machine_escaped, org_unit);
2002 samAccountName = talloc_asprintf(ctx, "%s$", machine_name);
2004 if ( !new_dn || !samAccountName ) {
2005 goto done;
2008 #ifndef ENCTYPE_ARCFOUR_HMAC
2009 acct_control |= UF_USE_DES_KEY_ONLY;
2010 #endif
2012 if (!(controlstr = talloc_asprintf(ctx, "%u", acct_control))) {
2013 goto done;
2016 if (!(mods = ads_init_mods(ctx))) {
2017 goto done;
2020 ads_mod_str(ctx, &mods, "cn", machine_name);
2021 ads_mod_str(ctx, &mods, "sAMAccountName", samAccountName);
2022 ads_mod_strlist(ctx, &mods, "objectClass", objectClass);
2023 ads_mod_str(ctx, &mods, "userAccountControl", controlstr);
2025 ret = ads_gen_add(ads, new_dn, mods);
2027 done:
2028 SAFE_FREE(machine_escaped);
2029 ads_msgfree(ads, res);
2030 talloc_destroy(ctx);
2032 return ret;
2036 * move a machine account to another OU on the ADS server
2037 * @param ads - An intialized ADS_STRUCT
2038 * @param machine_name - the NetBIOS machine name of this account.
2039 * @param org_unit - The LDAP path in which to place this account
2040 * @param moved - whether we moved the machine account (optional)
2041 * @return 0 upon success, or non-zero otherwise
2044 ADS_STATUS ads_move_machine_acct(ADS_STRUCT *ads, const char *machine_name,
2045 const char *org_unit, bool *moved)
2047 ADS_STATUS rc;
2048 int ldap_status;
2049 LDAPMessage *res = NULL;
2050 char *filter = NULL;
2051 char *computer_dn = NULL;
2052 char *parent_dn;
2053 char *computer_rdn = NULL;
2054 bool need_move = False;
2056 if (asprintf(&filter, "(samAccountName=%s$)", machine_name) == -1) {
2057 rc = ADS_ERROR(LDAP_NO_MEMORY);
2058 goto done;
2061 /* Find pre-existing machine */
2062 rc = ads_search(ads, &res, filter, NULL);
2063 if (!ADS_ERR_OK(rc)) {
2064 goto done;
2067 computer_dn = ads_get_dn(ads, talloc_tos(), res);
2068 if (!computer_dn) {
2069 rc = ADS_ERROR(LDAP_NO_MEMORY);
2070 goto done;
2073 parent_dn = ads_parent_dn(computer_dn);
2074 if (strequal(parent_dn, org_unit)) {
2075 goto done;
2078 need_move = True;
2080 if (asprintf(&computer_rdn, "CN=%s", machine_name) == -1) {
2081 rc = ADS_ERROR(LDAP_NO_MEMORY);
2082 goto done;
2085 ldap_status = ldap_rename_s(ads->ldap.ld, computer_dn, computer_rdn,
2086 org_unit, 1, NULL, NULL);
2087 rc = ADS_ERROR(ldap_status);
2089 done:
2090 ads_msgfree(ads, res);
2091 SAFE_FREE(filter);
2092 SAFE_FREE(computer_dn);
2093 SAFE_FREE(computer_rdn);
2095 if (!ADS_ERR_OK(rc)) {
2096 need_move = False;
2099 if (moved) {
2100 *moved = need_move;
2103 return rc;
2107 dump a binary result from ldap
2109 static void dump_binary(ADS_STRUCT *ads, const char *field, struct berval **values)
2111 int i, j;
2112 for (i=0; values[i]; i++) {
2113 printf("%s: ", field);
2114 for (j=0; j<values[i]->bv_len; j++) {
2115 printf("%02X", (unsigned char)values[i]->bv_val[j]);
2117 printf("\n");
2121 static void dump_guid(ADS_STRUCT *ads, const char *field, struct berval **values)
2123 int i;
2124 for (i=0; values[i]; i++) {
2126 UUID_FLAT guid;
2127 struct GUID tmp;
2129 memcpy(guid.info, values[i]->bv_val, sizeof(guid.info));
2130 smb_uuid_unpack(guid, &tmp);
2131 printf("%s: %s\n", field, GUID_string(talloc_tos(), &tmp));
2136 dump a sid result from ldap
2138 static void dump_sid(ADS_STRUCT *ads, const char *field, struct berval **values)
2140 int i;
2141 for (i=0; values[i]; i++) {
2142 DOM_SID sid;
2143 fstring tmp;
2144 sid_parse(values[i]->bv_val, values[i]->bv_len, &sid);
2145 printf("%s: %s\n", field, sid_to_fstring(tmp, &sid));
2150 dump ntSecurityDescriptor
2152 static void dump_sd(ADS_STRUCT *ads, const char *filed, struct berval **values)
2154 TALLOC_CTX *frame = talloc_stackframe();
2155 struct security_descriptor *psd;
2156 NTSTATUS status;
2158 status = unmarshall_sec_desc(talloc_tos(), (uint8 *)values[0]->bv_val,
2159 values[0]->bv_len, &psd);
2160 if (!NT_STATUS_IS_OK(status)) {
2161 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
2162 nt_errstr(status)));
2163 TALLOC_FREE(frame);
2164 return;
2167 if (psd) {
2168 ads_disp_sd(ads, talloc_tos(), psd);
2171 TALLOC_FREE(frame);
2175 dump a string result from ldap
2177 static void dump_string(const char *field, char **values)
2179 int i;
2180 for (i=0; values[i]; i++) {
2181 printf("%s: %s\n", field, values[i]);
2186 dump a field from LDAP on stdout
2187 used for debugging
2190 static bool ads_dump_field(ADS_STRUCT *ads, char *field, void **values, void *data_area)
2192 const struct {
2193 const char *name;
2194 bool string;
2195 void (*handler)(ADS_STRUCT *, const char *, struct berval **);
2196 } handlers[] = {
2197 {"objectGUID", False, dump_guid},
2198 {"netbootGUID", False, dump_guid},
2199 {"nTSecurityDescriptor", False, dump_sd},
2200 {"dnsRecord", False, dump_binary},
2201 {"objectSid", False, dump_sid},
2202 {"tokenGroups", False, dump_sid},
2203 {"tokenGroupsNoGCAcceptable", False, dump_sid},
2204 {"tokengroupsGlobalandUniversal", False, dump_sid},
2205 {"mS-DS-CreatorSID", False, dump_sid},
2206 {"msExchMailboxGuid", False, dump_guid},
2207 {NULL, True, NULL}
2209 int i;
2211 if (!field) { /* must be end of an entry */
2212 printf("\n");
2213 return False;
2216 for (i=0; handlers[i].name; i++) {
2217 if (StrCaseCmp(handlers[i].name, field) == 0) {
2218 if (!values) /* first time, indicate string or not */
2219 return handlers[i].string;
2220 handlers[i].handler(ads, field, (struct berval **) values);
2221 break;
2224 if (!handlers[i].name) {
2225 if (!values) /* first time, indicate string conversion */
2226 return True;
2227 dump_string(field, (char **)values);
2229 return False;
2233 * Dump a result from LDAP on stdout
2234 * used for debugging
2235 * @param ads connection to ads server
2236 * @param res Results to dump
2239 void ads_dump(ADS_STRUCT *ads, LDAPMessage *res)
2241 ads_process_results(ads, res, ads_dump_field, NULL);
2245 * Walk through results, calling a function for each entry found.
2246 * The function receives a field name, a berval * array of values,
2247 * and a data area passed through from the start. The function is
2248 * called once with null for field and values at the end of each
2249 * entry.
2250 * @param ads connection to ads server
2251 * @param res Results to process
2252 * @param fn Function for processing each result
2253 * @param data_area user-defined area to pass to function
2255 void ads_process_results(ADS_STRUCT *ads, LDAPMessage *res,
2256 bool (*fn)(ADS_STRUCT *, char *, void **, void *),
2257 void *data_area)
2259 LDAPMessage *msg;
2260 TALLOC_CTX *ctx;
2261 size_t converted_size;
2263 if (!(ctx = talloc_init("ads_process_results")))
2264 return;
2266 for (msg = ads_first_entry(ads, res); msg;
2267 msg = ads_next_entry(ads, msg)) {
2268 char *utf8_field;
2269 BerElement *b;
2271 for (utf8_field=ldap_first_attribute(ads->ldap.ld,
2272 (LDAPMessage *)msg,&b);
2273 utf8_field;
2274 utf8_field=ldap_next_attribute(ads->ldap.ld,
2275 (LDAPMessage *)msg,b)) {
2276 struct berval **ber_vals;
2277 char **str_vals, **utf8_vals;
2278 char *field;
2279 bool string;
2281 if (!pull_utf8_talloc(ctx, &field, utf8_field,
2282 &converted_size))
2284 DEBUG(0,("ads_process_results: "
2285 "pull_utf8_talloc failed: %s",
2286 strerror(errno)));
2289 string = fn(ads, field, NULL, data_area);
2291 if (string) {
2292 utf8_vals = ldap_get_values(ads->ldap.ld,
2293 (LDAPMessage *)msg, field);
2294 str_vals = ads_pull_strvals(ctx,
2295 (const char **) utf8_vals);
2296 fn(ads, field, (void **) str_vals, data_area);
2297 ldap_value_free(utf8_vals);
2298 } else {
2299 ber_vals = ldap_get_values_len(ads->ldap.ld,
2300 (LDAPMessage *)msg, field);
2301 fn(ads, field, (void **) ber_vals, data_area);
2303 ldap_value_free_len(ber_vals);
2305 ldap_memfree(utf8_field);
2307 ber_free(b, 0);
2308 talloc_free_children(ctx);
2309 fn(ads, NULL, NULL, data_area); /* completed an entry */
2312 talloc_destroy(ctx);
2316 * count how many replies are in a LDAPMessage
2317 * @param ads connection to ads server
2318 * @param res Results to count
2319 * @return number of replies
2321 int ads_count_replies(ADS_STRUCT *ads, void *res)
2323 return ldap_count_entries(ads->ldap.ld, (LDAPMessage *)res);
2327 * pull the first entry from a ADS result
2328 * @param ads connection to ads server
2329 * @param res Results of search
2330 * @return first entry from result
2332 LDAPMessage *ads_first_entry(ADS_STRUCT *ads, LDAPMessage *res)
2334 return ldap_first_entry(ads->ldap.ld, res);
2338 * pull the next entry from a ADS result
2339 * @param ads connection to ads server
2340 * @param res Results of search
2341 * @return next entry from result
2343 LDAPMessage *ads_next_entry(ADS_STRUCT *ads, LDAPMessage *res)
2345 return ldap_next_entry(ads->ldap.ld, res);
2349 * pull the first message from a ADS result
2350 * @param ads connection to ads server
2351 * @param res Results of search
2352 * @return first message from result
2354 LDAPMessage *ads_first_message(ADS_STRUCT *ads, LDAPMessage *res)
2356 return ldap_first_message(ads->ldap.ld, res);
2360 * pull the next message from a ADS result
2361 * @param ads connection to ads server
2362 * @param res Results of search
2363 * @return next message from result
2365 LDAPMessage *ads_next_message(ADS_STRUCT *ads, LDAPMessage *res)
2367 return ldap_next_message(ads->ldap.ld, res);
2371 * pull a single string from a ADS result
2372 * @param ads connection to ads server
2373 * @param mem_ctx TALLOC_CTX to use for allocating result string
2374 * @param msg Results of search
2375 * @param field Attribute to retrieve
2376 * @return Result string in talloc context
2378 char *ads_pull_string(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, LDAPMessage *msg,
2379 const char *field)
2381 char **values;
2382 char *ret = NULL;
2383 char *ux_string;
2384 size_t converted_size;
2386 values = ldap_get_values(ads->ldap.ld, msg, field);
2387 if (!values)
2388 return NULL;
2390 if (values[0] && pull_utf8_talloc(mem_ctx, &ux_string, values[0],
2391 &converted_size))
2393 ret = ux_string;
2395 ldap_value_free(values);
2396 return ret;
2400 * pull an array of strings from a ADS result
2401 * @param ads connection to ads server
2402 * @param mem_ctx TALLOC_CTX to use for allocating result string
2403 * @param msg Results of search
2404 * @param field Attribute to retrieve
2405 * @return Result strings in talloc context
2407 char **ads_pull_strings(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2408 LDAPMessage *msg, const char *field,
2409 size_t *num_values)
2411 char **values;
2412 char **ret = NULL;
2413 int i;
2414 size_t converted_size;
2416 values = ldap_get_values(ads->ldap.ld, msg, field);
2417 if (!values)
2418 return NULL;
2420 *num_values = ldap_count_values(values);
2422 ret = TALLOC_ARRAY(mem_ctx, char *, *num_values + 1);
2423 if (!ret) {
2424 ldap_value_free(values);
2425 return NULL;
2428 for (i=0;i<*num_values;i++) {
2429 if (!pull_utf8_talloc(mem_ctx, &ret[i], values[i],
2430 &converted_size))
2432 ldap_value_free(values);
2433 return NULL;
2436 ret[i] = NULL;
2438 ldap_value_free(values);
2439 return ret;
2443 * pull an array of strings from a ADS result
2444 * (handle large multivalue attributes with range retrieval)
2445 * @param ads connection to ads server
2446 * @param mem_ctx TALLOC_CTX to use for allocating result string
2447 * @param msg Results of search
2448 * @param field Attribute to retrieve
2449 * @param current_strings strings returned by a previous call to this function
2450 * @param next_attribute The next query should ask for this attribute
2451 * @param num_values How many values did we get this time?
2452 * @param more_values Are there more values to get?
2453 * @return Result strings in talloc context
2455 char **ads_pull_strings_range(ADS_STRUCT *ads,
2456 TALLOC_CTX *mem_ctx,
2457 LDAPMessage *msg, const char *field,
2458 char **current_strings,
2459 const char **next_attribute,
2460 size_t *num_strings,
2461 bool *more_strings)
2463 char *attr;
2464 char *expected_range_attrib, *range_attr;
2465 BerElement *ptr = NULL;
2466 char **strings;
2467 char **new_strings;
2468 size_t num_new_strings;
2469 unsigned long int range_start;
2470 unsigned long int range_end;
2472 /* we might have been given the whole lot anyway */
2473 if ((strings = ads_pull_strings(ads, mem_ctx, msg, field, num_strings))) {
2474 *more_strings = False;
2475 return strings;
2478 expected_range_attrib = talloc_asprintf(mem_ctx, "%s;Range=", field);
2480 /* look for Range result */
2481 for (attr = ldap_first_attribute(ads->ldap.ld, (LDAPMessage *)msg, &ptr);
2482 attr;
2483 attr = ldap_next_attribute(ads->ldap.ld, (LDAPMessage *)msg, ptr)) {
2484 /* we ignore the fact that this is utf8, as all attributes are ascii... */
2485 if (strnequal(attr, expected_range_attrib, strlen(expected_range_attrib))) {
2486 range_attr = attr;
2487 break;
2489 ldap_memfree(attr);
2491 if (!attr) {
2492 ber_free(ptr, 0);
2493 /* nothing here - this field is just empty */
2494 *more_strings = False;
2495 return NULL;
2498 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-%lu",
2499 &range_start, &range_end) == 2) {
2500 *more_strings = True;
2501 } else {
2502 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-*",
2503 &range_start) == 1) {
2504 *more_strings = False;
2505 } else {
2506 DEBUG(1, ("ads_pull_strings_range: Cannot parse Range attriubte (%s)\n",
2507 range_attr));
2508 ldap_memfree(range_attr);
2509 *more_strings = False;
2510 return NULL;
2514 if ((*num_strings) != range_start) {
2515 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) doesn't start at %u, but at %lu"
2516 " - aborting range retreival\n",
2517 range_attr, (unsigned int)(*num_strings) + 1, range_start));
2518 ldap_memfree(range_attr);
2519 *more_strings = False;
2520 return NULL;
2523 new_strings = ads_pull_strings(ads, mem_ctx, msg, range_attr, &num_new_strings);
2525 if (*more_strings && ((*num_strings + num_new_strings) != (range_end + 1))) {
2526 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) tells us we have %lu "
2527 "strings in this bunch, but we only got %lu - aborting range retreival\n",
2528 range_attr, (unsigned long int)range_end - range_start + 1,
2529 (unsigned long int)num_new_strings));
2530 ldap_memfree(range_attr);
2531 *more_strings = False;
2532 return NULL;
2535 strings = TALLOC_REALLOC_ARRAY(mem_ctx, current_strings, char *,
2536 *num_strings + num_new_strings);
2538 if (strings == NULL) {
2539 ldap_memfree(range_attr);
2540 *more_strings = False;
2541 return NULL;
2544 if (new_strings && num_new_strings) {
2545 memcpy(&strings[*num_strings], new_strings,
2546 sizeof(*new_strings) * num_new_strings);
2549 (*num_strings) += num_new_strings;
2551 if (*more_strings) {
2552 *next_attribute = talloc_asprintf(mem_ctx,
2553 "%s;range=%d-*",
2554 field,
2555 (int)*num_strings);
2557 if (!*next_attribute) {
2558 DEBUG(1, ("talloc_asprintf for next attribute failed!\n"));
2559 ldap_memfree(range_attr);
2560 *more_strings = False;
2561 return NULL;
2565 ldap_memfree(range_attr);
2567 return strings;
2571 * pull a single uint32 from a ADS result
2572 * @param ads connection to ads server
2573 * @param msg Results of search
2574 * @param field Attribute to retrieve
2575 * @param v Pointer to int to store result
2576 * @return boolean inidicating success
2578 bool ads_pull_uint32(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
2579 uint32 *v)
2581 char **values;
2583 values = ldap_get_values(ads->ldap.ld, msg, field);
2584 if (!values)
2585 return False;
2586 if (!values[0]) {
2587 ldap_value_free(values);
2588 return False;
2591 *v = atoi(values[0]);
2592 ldap_value_free(values);
2593 return True;
2597 * pull a single objectGUID from an ADS result
2598 * @param ads connection to ADS server
2599 * @param msg results of search
2600 * @param guid 37-byte area to receive text guid
2601 * @return boolean indicating success
2603 bool ads_pull_guid(ADS_STRUCT *ads, LDAPMessage *msg, struct GUID *guid)
2605 char **values;
2606 UUID_FLAT flat_guid;
2608 values = ldap_get_values(ads->ldap.ld, msg, "objectGUID");
2609 if (!values)
2610 return False;
2612 if (values[0]) {
2613 memcpy(&flat_guid.info, values[0], sizeof(UUID_FLAT));
2614 smb_uuid_unpack(flat_guid, guid);
2615 ldap_value_free(values);
2616 return True;
2618 ldap_value_free(values);
2619 return False;
2625 * pull a single DOM_SID from a ADS result
2626 * @param ads connection to ads server
2627 * @param msg Results of search
2628 * @param field Attribute to retrieve
2629 * @param sid Pointer to sid to store result
2630 * @return boolean inidicating success
2632 bool ads_pull_sid(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
2633 DOM_SID *sid)
2635 return smbldap_pull_sid(ads->ldap.ld, msg, field, sid);
2639 * pull an array of DOM_SIDs from a ADS result
2640 * @param ads connection to ads server
2641 * @param mem_ctx TALLOC_CTX for allocating sid array
2642 * @param msg Results of search
2643 * @param field Attribute to retrieve
2644 * @param sids pointer to sid array to allocate
2645 * @return the count of SIDs pulled
2647 int ads_pull_sids(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2648 LDAPMessage *msg, const char *field, DOM_SID **sids)
2650 struct berval **values;
2651 bool ret;
2652 int count, i;
2654 values = ldap_get_values_len(ads->ldap.ld, msg, field);
2656 if (!values)
2657 return 0;
2659 for (i=0; values[i]; i++)
2660 /* nop */ ;
2662 if (i) {
2663 (*sids) = TALLOC_ARRAY(mem_ctx, DOM_SID, i);
2664 if (!(*sids)) {
2665 ldap_value_free_len(values);
2666 return 0;
2668 } else {
2669 (*sids) = NULL;
2672 count = 0;
2673 for (i=0; values[i]; i++) {
2674 ret = sid_parse(values[i]->bv_val, values[i]->bv_len, &(*sids)[count]);
2675 if (ret) {
2676 DEBUG(10, ("pulling SID: %s\n",
2677 sid_string_dbg(&(*sids)[count])));
2678 count++;
2682 ldap_value_free_len(values);
2683 return count;
2687 * pull a SEC_DESC from a ADS result
2688 * @param ads connection to ads server
2689 * @param mem_ctx TALLOC_CTX for allocating sid array
2690 * @param msg Results of search
2691 * @param field Attribute to retrieve
2692 * @param sd Pointer to *SEC_DESC to store result (talloc()ed)
2693 * @return boolean inidicating success
2695 bool ads_pull_sd(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2696 LDAPMessage *msg, const char *field, SEC_DESC **sd)
2698 struct berval **values;
2699 bool ret = true;
2701 values = ldap_get_values_len(ads->ldap.ld, msg, field);
2703 if (!values) return false;
2705 if (values[0]) {
2706 NTSTATUS status;
2707 status = unmarshall_sec_desc(mem_ctx,
2708 (uint8 *)values[0]->bv_val,
2709 values[0]->bv_len, sd);
2710 if (!NT_STATUS_IS_OK(status)) {
2711 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
2712 nt_errstr(status)));
2713 ret = false;
2717 ldap_value_free_len(values);
2718 return ret;
2722 * in order to support usernames longer than 21 characters we need to
2723 * use both the sAMAccountName and the userPrincipalName attributes
2724 * It seems that not all users have the userPrincipalName attribute set
2726 * @param ads connection to ads server
2727 * @param mem_ctx TALLOC_CTX for allocating sid array
2728 * @param msg Results of search
2729 * @return the username
2731 char *ads_pull_username(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2732 LDAPMessage *msg)
2734 #if 0 /* JERRY */
2735 char *ret, *p;
2737 /* lookup_name() only works on the sAMAccountName to
2738 returning the username portion of userPrincipalName
2739 breaks winbindd_getpwnam() */
2741 ret = ads_pull_string(ads, mem_ctx, msg, "userPrincipalName");
2742 if (ret && (p = strchr_m(ret, '@'))) {
2743 *p = 0;
2744 return ret;
2746 #endif
2747 return ads_pull_string(ads, mem_ctx, msg, "sAMAccountName");
2752 * find the update serial number - this is the core of the ldap cache
2753 * @param ads connection to ads server
2754 * @param ads connection to ADS server
2755 * @param usn Pointer to retrieved update serial number
2756 * @return status of search
2758 ADS_STATUS ads_USN(ADS_STRUCT *ads, uint32 *usn)
2760 const char *attrs[] = {"highestCommittedUSN", NULL};
2761 ADS_STATUS status;
2762 LDAPMessage *res;
2764 status = ads_do_search_retry(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2765 if (!ADS_ERR_OK(status))
2766 return status;
2768 if (ads_count_replies(ads, res) != 1) {
2769 ads_msgfree(ads, res);
2770 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2773 if (!ads_pull_uint32(ads, res, "highestCommittedUSN", usn)) {
2774 ads_msgfree(ads, res);
2775 return ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
2778 ads_msgfree(ads, res);
2779 return ADS_SUCCESS;
2782 /* parse a ADS timestring - typical string is
2783 '20020917091222.0Z0' which means 09:12.22 17th September
2784 2002, timezone 0 */
2785 static time_t ads_parse_time(const char *str)
2787 struct tm tm;
2789 ZERO_STRUCT(tm);
2791 if (sscanf(str, "%4d%2d%2d%2d%2d%2d",
2792 &tm.tm_year, &tm.tm_mon, &tm.tm_mday,
2793 &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) {
2794 return 0;
2796 tm.tm_year -= 1900;
2797 tm.tm_mon -= 1;
2799 return timegm(&tm);
2802 /********************************************************************
2803 ********************************************************************/
2805 ADS_STATUS ads_current_time(ADS_STRUCT *ads)
2807 const char *attrs[] = {"currentTime", NULL};
2808 ADS_STATUS status;
2809 LDAPMessage *res;
2810 char *timestr;
2811 TALLOC_CTX *ctx;
2812 ADS_STRUCT *ads_s = ads;
2814 if (!(ctx = talloc_init("ads_current_time"))) {
2815 return ADS_ERROR(LDAP_NO_MEMORY);
2818 /* establish a new ldap tcp session if necessary */
2820 if ( !ads->ldap.ld ) {
2821 if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup,
2822 ads->server.ldap_server )) == NULL )
2824 goto done;
2826 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
2827 status = ads_connect( ads_s );
2828 if ( !ADS_ERR_OK(status))
2829 goto done;
2832 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2833 if (!ADS_ERR_OK(status)) {
2834 goto done;
2837 timestr = ads_pull_string(ads_s, ctx, res, "currentTime");
2838 if (!timestr) {
2839 ads_msgfree(ads_s, res);
2840 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2841 goto done;
2844 /* but save the time and offset in the original ADS_STRUCT */
2846 ads->config.current_time = ads_parse_time(timestr);
2848 if (ads->config.current_time != 0) {
2849 ads->auth.time_offset = ads->config.current_time - time(NULL);
2850 DEBUG(4,("time offset is %d seconds\n", ads->auth.time_offset));
2853 ads_msgfree(ads, res);
2855 status = ADS_SUCCESS;
2857 done:
2858 /* free any temporary ads connections */
2859 if ( ads_s != ads ) {
2860 ads_destroy( &ads_s );
2862 talloc_destroy(ctx);
2864 return status;
2867 /********************************************************************
2868 ********************************************************************/
2870 ADS_STATUS ads_domain_func_level(ADS_STRUCT *ads, uint32 *val)
2872 const char *attrs[] = {"domainFunctionality", NULL};
2873 ADS_STATUS status;
2874 LDAPMessage *res;
2875 ADS_STRUCT *ads_s = ads;
2877 *val = DS_DOMAIN_FUNCTION_2000;
2879 /* establish a new ldap tcp session if necessary */
2881 if ( !ads->ldap.ld ) {
2882 if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup,
2883 ads->server.ldap_server )) == NULL )
2885 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
2886 goto done;
2888 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
2889 status = ads_connect( ads_s );
2890 if ( !ADS_ERR_OK(status))
2891 goto done;
2894 /* If the attribute does not exist assume it is a Windows 2000
2895 functional domain */
2897 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2898 if (!ADS_ERR_OK(status)) {
2899 if ( status.err.rc == LDAP_NO_SUCH_ATTRIBUTE ) {
2900 status = ADS_SUCCESS;
2902 goto done;
2905 if ( !ads_pull_uint32(ads_s, res, "domainFunctionality", val) ) {
2906 DEBUG(5,("ads_domain_func_level: Failed to pull the domainFunctionality attribute.\n"));
2908 DEBUG(3,("ads_domain_func_level: %d\n", *val));
2911 ads_msgfree(ads, res);
2913 done:
2914 /* free any temporary ads connections */
2915 if ( ads_s != ads ) {
2916 ads_destroy( &ads_s );
2919 return status;
2923 * find the domain sid for our domain
2924 * @param ads connection to ads server
2925 * @param sid Pointer to domain sid
2926 * @return status of search
2928 ADS_STATUS ads_domain_sid(ADS_STRUCT *ads, DOM_SID *sid)
2930 const char *attrs[] = {"objectSid", NULL};
2931 LDAPMessage *res;
2932 ADS_STATUS rc;
2934 rc = ads_do_search_retry(ads, ads->config.bind_path, LDAP_SCOPE_BASE, "(objectclass=*)",
2935 attrs, &res);
2936 if (!ADS_ERR_OK(rc)) return rc;
2937 if (!ads_pull_sid(ads, res, "objectSid", sid)) {
2938 ads_msgfree(ads, res);
2939 return ADS_ERROR_SYSTEM(ENOENT);
2941 ads_msgfree(ads, res);
2943 return ADS_SUCCESS;
2947 * find our site name
2948 * @param ads connection to ads server
2949 * @param mem_ctx Pointer to talloc context
2950 * @param site_name Pointer to the sitename
2951 * @return status of search
2953 ADS_STATUS ads_site_dn(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char **site_name)
2955 ADS_STATUS status;
2956 LDAPMessage *res;
2957 const char *dn, *service_name;
2958 const char *attrs[] = { "dsServiceName", NULL };
2960 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2961 if (!ADS_ERR_OK(status)) {
2962 return status;
2965 service_name = ads_pull_string(ads, mem_ctx, res, "dsServiceName");
2966 if (service_name == NULL) {
2967 ads_msgfree(ads, res);
2968 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2971 ads_msgfree(ads, res);
2973 /* go up three levels */
2974 dn = ads_parent_dn(ads_parent_dn(ads_parent_dn(service_name)));
2975 if (dn == NULL) {
2976 return ADS_ERROR(LDAP_NO_MEMORY);
2979 *site_name = talloc_strdup(mem_ctx, dn);
2980 if (*site_name == NULL) {
2981 return ADS_ERROR(LDAP_NO_MEMORY);
2984 return status;
2986 dsServiceName: CN=NTDS Settings,CN=W2K3DC,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,DC=ber,DC=suse,DC=de
2991 * find the site dn where a machine resides
2992 * @param ads connection to ads server
2993 * @param mem_ctx Pointer to talloc context
2994 * @param computer_name name of the machine
2995 * @param site_name Pointer to the sitename
2996 * @return status of search
2998 ADS_STATUS ads_site_dn_for_machine(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char *computer_name, const char **site_dn)
3000 ADS_STATUS status;
3001 LDAPMessage *res;
3002 const char *parent, *filter;
3003 char *config_context = NULL;
3004 char *dn;
3006 /* shortcut a query */
3007 if (strequal(computer_name, ads->config.ldap_server_name)) {
3008 return ads_site_dn(ads, mem_ctx, site_dn);
3011 status = ads_config_path(ads, mem_ctx, &config_context);
3012 if (!ADS_ERR_OK(status)) {
3013 return status;
3016 filter = talloc_asprintf(mem_ctx, "(cn=%s)", computer_name);
3017 if (filter == NULL) {
3018 return ADS_ERROR(LDAP_NO_MEMORY);
3021 status = ads_do_search(ads, config_context, LDAP_SCOPE_SUBTREE,
3022 filter, NULL, &res);
3023 if (!ADS_ERR_OK(status)) {
3024 return status;
3027 if (ads_count_replies(ads, res) != 1) {
3028 ads_msgfree(ads, res);
3029 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
3032 dn = ads_get_dn(ads, mem_ctx, res);
3033 if (dn == NULL) {
3034 ads_msgfree(ads, res);
3035 return ADS_ERROR(LDAP_NO_MEMORY);
3038 /* go up three levels */
3039 parent = ads_parent_dn(ads_parent_dn(ads_parent_dn(dn)));
3040 if (parent == NULL) {
3041 ads_msgfree(ads, res);
3042 TALLOC_FREE(dn);
3043 return ADS_ERROR(LDAP_NO_MEMORY);
3046 *site_dn = talloc_strdup(mem_ctx, parent);
3047 if (*site_dn == NULL) {
3048 ads_msgfree(ads, res);
3049 TALLOC_FREE(dn);
3050 return ADS_ERROR(LDAP_NO_MEMORY);
3053 TALLOC_FREE(dn);
3054 ads_msgfree(ads, res);
3056 return status;
3060 * get the upn suffixes for a domain
3061 * @param ads connection to ads server
3062 * @param mem_ctx Pointer to talloc context
3063 * @param suffixes Pointer to an array of suffixes
3064 * @param num_suffixes Pointer to the number of suffixes
3065 * @return status of search
3067 ADS_STATUS ads_upn_suffixes(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, char ***suffixes, size_t *num_suffixes)
3069 ADS_STATUS status;
3070 LDAPMessage *res;
3071 const char *base;
3072 char *config_context = NULL;
3073 const char *attrs[] = { "uPNSuffixes", NULL };
3075 status = ads_config_path(ads, mem_ctx, &config_context);
3076 if (!ADS_ERR_OK(status)) {
3077 return status;
3080 base = talloc_asprintf(mem_ctx, "cn=Partitions,%s", config_context);
3081 if (base == NULL) {
3082 return ADS_ERROR(LDAP_NO_MEMORY);
3085 status = ads_search_dn(ads, &res, base, attrs);
3086 if (!ADS_ERR_OK(status)) {
3087 return status;
3090 if (ads_count_replies(ads, res) != 1) {
3091 ads_msgfree(ads, res);
3092 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
3095 (*suffixes) = ads_pull_strings(ads, mem_ctx, res, "uPNSuffixes", num_suffixes);
3096 if ((*suffixes) == NULL) {
3097 ads_msgfree(ads, res);
3098 return ADS_ERROR(LDAP_NO_MEMORY);
3101 ads_msgfree(ads, res);
3103 return status;
3107 * get the joinable ous for a domain
3108 * @param ads connection to ads server
3109 * @param mem_ctx Pointer to talloc context
3110 * @param ous Pointer to an array of ous
3111 * @param num_ous Pointer to the number of ous
3112 * @return status of search
3114 ADS_STATUS ads_get_joinable_ous(ADS_STRUCT *ads,
3115 TALLOC_CTX *mem_ctx,
3116 char ***ous,
3117 size_t *num_ous)
3119 ADS_STATUS status;
3120 LDAPMessage *res = NULL;
3121 LDAPMessage *msg = NULL;
3122 const char *attrs[] = { "dn", NULL };
3123 int count = 0;
3125 status = ads_search(ads, &res,
3126 "(|(objectClass=domain)(objectclass=organizationalUnit))",
3127 attrs);
3128 if (!ADS_ERR_OK(status)) {
3129 return status;
3132 count = ads_count_replies(ads, res);
3133 if (count < 1) {
3134 ads_msgfree(ads, res);
3135 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3138 for (msg = ads_first_entry(ads, res); msg;
3139 msg = ads_next_entry(ads, msg)) {
3141 char *dn = NULL;
3143 dn = ads_get_dn(ads, talloc_tos(), msg);
3144 if (!dn) {
3145 ads_msgfree(ads, res);
3146 return ADS_ERROR(LDAP_NO_MEMORY);
3149 if (!add_string_to_array(mem_ctx, dn,
3150 (const char ***)ous,
3151 (int *)num_ous)) {
3152 TALLOC_FREE(dn);
3153 ads_msgfree(ads, res);
3154 return ADS_ERROR(LDAP_NO_MEMORY);
3157 TALLOC_FREE(dn);
3160 ads_msgfree(ads, res);
3162 return status;
3167 * pull a DOM_SID from an extended dn string
3168 * @param mem_ctx TALLOC_CTX
3169 * @param extended_dn string
3170 * @param flags string type of extended_dn
3171 * @param sid pointer to a DOM_SID
3172 * @return NT_STATUS_OK on success,
3173 * NT_INVALID_PARAMETER on error,
3174 * NT_STATUS_NOT_FOUND if no SID present
3176 ADS_STATUS ads_get_sid_from_extended_dn(TALLOC_CTX *mem_ctx,
3177 const char *extended_dn,
3178 enum ads_extended_dn_flags flags,
3179 DOM_SID *sid)
3181 char *p, *q, *dn;
3183 if (!extended_dn) {
3184 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3187 /* otherwise extended_dn gets stripped off */
3188 if ((dn = talloc_strdup(mem_ctx, extended_dn)) == NULL) {
3189 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3192 * ADS_EXTENDED_DN_HEX_STRING:
3193 * <GUID=238e1963cb390f4bb032ba0105525a29>;<SID=010500000000000515000000bb68c8fd6b61b427572eb04556040000>;CN=gd,OU=berlin,OU=suse,DC=ber,DC=suse,DC=de
3195 * ADS_EXTENDED_DN_STRING (only with w2k3):
3196 * <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
3198 * Object with no SID, such as an Exchange Public Folder
3199 * <GUID=28907fb4bdf6854993e7f0a10b504e7c>;CN=public,CN=Microsoft Exchange System Objects,DC=sd2k3ms,DC=west,DC=isilon,DC=com
3202 p = strchr(dn, ';');
3203 if (!p) {
3204 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3207 if (strncmp(p, ";<SID=", strlen(";<SID=")) != 0) {
3208 DEBUG(5,("No SID present in extended dn\n"));
3209 return ADS_ERROR_NT(NT_STATUS_NOT_FOUND);
3212 p += strlen(";<SID=");
3214 q = strchr(p, '>');
3215 if (!q) {
3216 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3219 *q = '\0';
3221 DEBUG(100,("ads_get_sid_from_extended_dn: sid string is %s\n", p));
3223 switch (flags) {
3225 case ADS_EXTENDED_DN_STRING:
3226 if (!string_to_sid(sid, p)) {
3227 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3229 break;
3230 case ADS_EXTENDED_DN_HEX_STRING: {
3231 fstring buf;
3232 size_t buf_len;
3234 buf_len = strhex_to_str(buf, sizeof(buf), p, strlen(p));
3235 if (buf_len == 0) {
3236 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3239 if (!sid_parse(buf, buf_len, sid)) {
3240 DEBUG(10,("failed to parse sid\n"));
3241 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3243 break;
3245 default:
3246 DEBUG(10,("unknown extended dn format\n"));
3247 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3250 return ADS_ERROR_NT(NT_STATUS_OK);
3254 * pull an array of DOM_SIDs from a ADS result
3255 * @param ads connection to ads server
3256 * @param mem_ctx TALLOC_CTX for allocating sid array
3257 * @param msg Results of search
3258 * @param field Attribute to retrieve
3259 * @param flags string type of extended_dn
3260 * @param sids pointer to sid array to allocate
3261 * @return the count of SIDs pulled
3263 int ads_pull_sids_from_extendeddn(ADS_STRUCT *ads,
3264 TALLOC_CTX *mem_ctx,
3265 LDAPMessage *msg,
3266 const char *field,
3267 enum ads_extended_dn_flags flags,
3268 DOM_SID **sids)
3270 int i;
3271 ADS_STATUS rc;
3272 size_t dn_count, ret_count = 0;
3273 char **dn_strings;
3275 if ((dn_strings = ads_pull_strings(ads, mem_ctx, msg, field,
3276 &dn_count)) == NULL) {
3277 return 0;
3280 (*sids) = TALLOC_ZERO_ARRAY(mem_ctx, DOM_SID, dn_count + 1);
3281 if (!(*sids)) {
3282 TALLOC_FREE(dn_strings);
3283 return 0;
3286 for (i=0; i<dn_count; i++) {
3287 rc = ads_get_sid_from_extended_dn(mem_ctx, dn_strings[i],
3288 flags, &(*sids)[i]);
3289 if (!ADS_ERR_OK(rc)) {
3290 if (NT_STATUS_EQUAL(ads_ntstatus(rc),
3291 NT_STATUS_NOT_FOUND)) {
3292 continue;
3294 else {
3295 TALLOC_FREE(*sids);
3296 TALLOC_FREE(dn_strings);
3297 return 0;
3300 ret_count++;
3303 TALLOC_FREE(dn_strings);
3305 return ret_count;
3308 /********************************************************************
3309 ********************************************************************/
3311 char* ads_get_dnshostname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3313 LDAPMessage *res = NULL;
3314 ADS_STATUS status;
3315 int count = 0;
3316 char *name = NULL;
3318 status = ads_find_machine_acct(ads, &res, global_myname());
3319 if (!ADS_ERR_OK(status)) {
3320 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
3321 global_myname()));
3322 goto out;
3325 if ( (count = ads_count_replies(ads, res)) != 1 ) {
3326 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
3327 goto out;
3330 if ( (name = ads_pull_string(ads, ctx, res, "dNSHostName")) == NULL ) {
3331 DEBUG(0,("ads_get_dnshostname: No dNSHostName attribute!\n"));
3334 out:
3335 ads_msgfree(ads, res);
3337 return name;
3340 /********************************************************************
3341 ********************************************************************/
3343 char* ads_get_upn( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3345 LDAPMessage *res = NULL;
3346 ADS_STATUS status;
3347 int count = 0;
3348 char *name = NULL;
3350 status = ads_find_machine_acct(ads, &res, machine_name);
3351 if (!ADS_ERR_OK(status)) {
3352 DEBUG(0,("ads_get_upn: Failed to find account for %s\n",
3353 global_myname()));
3354 goto out;
3357 if ( (count = ads_count_replies(ads, res)) != 1 ) {
3358 DEBUG(1,("ads_get_upn: %d entries returned!\n", count));
3359 goto out;
3362 if ( (name = ads_pull_string(ads, ctx, res, "userPrincipalName")) == NULL ) {
3363 DEBUG(2,("ads_get_upn: No userPrincipalName attribute!\n"));
3366 out:
3367 ads_msgfree(ads, res);
3369 return name;
3372 /********************************************************************
3373 ********************************************************************/
3375 char* ads_get_samaccountname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3377 LDAPMessage *res = NULL;
3378 ADS_STATUS status;
3379 int count = 0;
3380 char *name = NULL;
3382 status = ads_find_machine_acct(ads, &res, global_myname());
3383 if (!ADS_ERR_OK(status)) {
3384 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
3385 global_myname()));
3386 goto out;
3389 if ( (count = ads_count_replies(ads, res)) != 1 ) {
3390 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
3391 goto out;
3394 if ( (name = ads_pull_string(ads, ctx, res, "sAMAccountName")) == NULL ) {
3395 DEBUG(0,("ads_get_dnshostname: No sAMAccountName attribute!\n"));
3398 out:
3399 ads_msgfree(ads, res);
3401 return name;
3404 #if 0
3406 SAVED CODE - we used to join via ldap - remember how we did this. JRA.
3409 * Join a machine to a realm
3410 * Creates the machine account and sets the machine password
3411 * @param ads connection to ads server
3412 * @param machine name of host to add
3413 * @param org_unit Organizational unit to place machine in
3414 * @return status of join
3416 ADS_STATUS ads_join_realm(ADS_STRUCT *ads, const char *machine_name,
3417 uint32 account_type, const char *org_unit)
3419 ADS_STATUS status;
3420 LDAPMessage *res = NULL;
3421 char *machine;
3423 /* machine name must be lowercase */
3424 machine = SMB_STRDUP(machine_name);
3425 strlower_m(machine);
3428 status = ads_find_machine_acct(ads, (void **)&res, machine);
3429 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
3430 DEBUG(0, ("Host account for %s already exists - deleting old account\n", machine));
3431 status = ads_leave_realm(ads, machine);
3432 if (!ADS_ERR_OK(status)) {
3433 DEBUG(0, ("Failed to delete host '%s' from the '%s' realm.\n",
3434 machine, ads->config.realm));
3435 return status;
3439 status = ads_add_machine_acct(ads, machine, account_type, org_unit);
3440 if (!ADS_ERR_OK(status)) {
3441 DEBUG(0, ("ads_join_realm: ads_add_machine_acct failed (%s): %s\n", machine, ads_errstr(status)));
3442 SAFE_FREE(machine);
3443 return status;
3446 status = ads_find_machine_acct(ads, (void **)(void *)&res, machine);
3447 if (!ADS_ERR_OK(status)) {
3448 DEBUG(0, ("ads_join_realm: Host account test failed for machine %s\n", machine));
3449 SAFE_FREE(machine);
3450 return status;
3453 SAFE_FREE(machine);
3454 ads_msgfree(ads, res);
3456 return status;
3458 #endif
3461 * Delete a machine from the realm
3462 * @param ads connection to ads server
3463 * @param hostname Machine to remove
3464 * @return status of delete
3466 ADS_STATUS ads_leave_realm(ADS_STRUCT *ads, const char *hostname)
3468 ADS_STATUS status;
3469 void *msg;
3470 LDAPMessage *res;
3471 char *hostnameDN, *host;
3472 int rc;
3473 LDAPControl ldap_control;
3474 LDAPControl * pldap_control[2] = {NULL, NULL};
3476 pldap_control[0] = &ldap_control;
3477 memset(&ldap_control, 0, sizeof(LDAPControl));
3478 ldap_control.ldctl_oid = (char *)LDAP_SERVER_TREE_DELETE_OID;
3480 /* hostname must be lowercase */
3481 host = SMB_STRDUP(hostname);
3482 strlower_m(host);
3484 status = ads_find_machine_acct(ads, &res, host);
3485 if (!ADS_ERR_OK(status)) {
3486 DEBUG(0, ("Host account for %s does not exist.\n", host));
3487 SAFE_FREE(host);
3488 return status;
3491 msg = ads_first_entry(ads, res);
3492 if (!msg) {
3493 SAFE_FREE(host);
3494 return ADS_ERROR_SYSTEM(ENOENT);
3497 hostnameDN = ads_get_dn(ads, talloc_tos(), (LDAPMessage *)msg);
3499 rc = ldap_delete_ext_s(ads->ldap.ld, hostnameDN, pldap_control, NULL);
3500 if (rc) {
3501 DEBUG(3,("ldap_delete_ext_s failed with error code %d\n", rc));
3502 }else {
3503 DEBUG(3,("ldap_delete_ext_s succeeded with error code %d\n", rc));
3506 if (rc != LDAP_SUCCESS) {
3507 const char *attrs[] = { "cn", NULL };
3508 LDAPMessage *msg_sub;
3510 /* we only search with scope ONE, we do not expect any further
3511 * objects to be created deeper */
3513 status = ads_do_search_retry(ads, hostnameDN,
3514 LDAP_SCOPE_ONELEVEL,
3515 "(objectclass=*)", attrs, &res);
3517 if (!ADS_ERR_OK(status)) {
3518 SAFE_FREE(host);
3519 TALLOC_FREE(hostnameDN);
3520 return status;
3523 for (msg_sub = ads_first_entry(ads, res); msg_sub;
3524 msg_sub = ads_next_entry(ads, msg_sub)) {
3526 char *dn = NULL;
3528 if ((dn = ads_get_dn(ads, talloc_tos(), msg_sub)) == NULL) {
3529 SAFE_FREE(host);
3530 TALLOC_FREE(hostnameDN);
3531 return ADS_ERROR(LDAP_NO_MEMORY);
3534 status = ads_del_dn(ads, dn);
3535 if (!ADS_ERR_OK(status)) {
3536 DEBUG(3,("failed to delete dn %s: %s\n", dn, ads_errstr(status)));
3537 SAFE_FREE(host);
3538 TALLOC_FREE(dn);
3539 TALLOC_FREE(hostnameDN);
3540 return status;
3543 TALLOC_FREE(dn);
3546 /* there should be no subordinate objects anymore */
3547 status = ads_do_search_retry(ads, hostnameDN,
3548 LDAP_SCOPE_ONELEVEL,
3549 "(objectclass=*)", attrs, &res);
3551 if (!ADS_ERR_OK(status) || ( (ads_count_replies(ads, res)) > 0 ) ) {
3552 SAFE_FREE(host);
3553 TALLOC_FREE(hostnameDN);
3554 return status;
3557 /* delete hostnameDN now */
3558 status = ads_del_dn(ads, hostnameDN);
3559 if (!ADS_ERR_OK(status)) {
3560 SAFE_FREE(host);
3561 DEBUG(3,("failed to delete dn %s: %s\n", hostnameDN, ads_errstr(status)));
3562 TALLOC_FREE(hostnameDN);
3563 return status;
3567 TALLOC_FREE(hostnameDN);
3569 status = ads_find_machine_acct(ads, &res, host);
3570 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
3571 DEBUG(3, ("Failed to remove host account.\n"));
3572 SAFE_FREE(host);
3573 return status;
3576 SAFE_FREE(host);
3577 return status;
3581 * pull all token-sids from an LDAP dn
3582 * @param ads connection to ads server
3583 * @param mem_ctx TALLOC_CTX for allocating sid array
3584 * @param dn of LDAP object
3585 * @param user_sid pointer to DOM_SID (objectSid)
3586 * @param primary_group_sid pointer to DOM_SID (self composed)
3587 * @param sids pointer to sid array to allocate
3588 * @param num_sids counter of SIDs pulled
3589 * @return status of token query
3591 ADS_STATUS ads_get_tokensids(ADS_STRUCT *ads,
3592 TALLOC_CTX *mem_ctx,
3593 const char *dn,
3594 DOM_SID *user_sid,
3595 DOM_SID *primary_group_sid,
3596 DOM_SID **sids,
3597 size_t *num_sids)
3599 ADS_STATUS status;
3600 LDAPMessage *res = NULL;
3601 int count = 0;
3602 size_t tmp_num_sids;
3603 DOM_SID *tmp_sids;
3604 DOM_SID tmp_user_sid;
3605 DOM_SID tmp_primary_group_sid;
3606 uint32 pgid;
3607 const char *attrs[] = {
3608 "objectSid",
3609 "tokenGroups",
3610 "primaryGroupID",
3611 NULL
3614 status = ads_search_retry_dn(ads, &res, dn, attrs);
3615 if (!ADS_ERR_OK(status)) {
3616 return status;
3619 count = ads_count_replies(ads, res);
3620 if (count != 1) {
3621 ads_msgfree(ads, res);
3622 return ADS_ERROR_LDAP(LDAP_NO_SUCH_OBJECT);
3625 if (!ads_pull_sid(ads, res, "objectSid", &tmp_user_sid)) {
3626 ads_msgfree(ads, res);
3627 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3630 if (!ads_pull_uint32(ads, res, "primaryGroupID", &pgid)) {
3631 ads_msgfree(ads, res);
3632 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3636 /* hack to compose the primary group sid without knowing the
3637 * domsid */
3639 DOM_SID domsid;
3640 uint32 dummy_rid;
3642 sid_copy(&domsid, &tmp_user_sid);
3644 if (!sid_split_rid(&domsid, &dummy_rid)) {
3645 ads_msgfree(ads, res);
3646 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3649 if (!sid_compose(&tmp_primary_group_sid, &domsid, pgid)) {
3650 ads_msgfree(ads, res);
3651 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3655 tmp_num_sids = ads_pull_sids(ads, mem_ctx, res, "tokenGroups", &tmp_sids);
3657 if (tmp_num_sids == 0 || !tmp_sids) {
3658 ads_msgfree(ads, res);
3659 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3662 if (num_sids) {
3663 *num_sids = tmp_num_sids;
3666 if (sids) {
3667 *sids = tmp_sids;
3670 if (user_sid) {
3671 *user_sid = tmp_user_sid;
3674 if (primary_group_sid) {
3675 *primary_group_sid = tmp_primary_group_sid;
3678 DEBUG(10,("ads_get_tokensids: returned %d sids\n", (int)tmp_num_sids + 2));
3680 ads_msgfree(ads, res);
3681 return ADS_ERROR_LDAP(LDAP_SUCCESS);
3685 * Find a sAMAccoutName in LDAP
3686 * @param ads connection to ads server
3687 * @param mem_ctx TALLOC_CTX for allocating sid array
3688 * @param samaccountname to search
3689 * @param uac_ret uint32 pointer userAccountControl attribute value
3690 * @param dn_ret pointer to dn
3691 * @return status of token query
3693 ADS_STATUS ads_find_samaccount(ADS_STRUCT *ads,
3694 TALLOC_CTX *mem_ctx,
3695 const char *samaccountname,
3696 uint32 *uac_ret,
3697 const char **dn_ret)
3699 ADS_STATUS status;
3700 const char *attrs[] = { "userAccountControl", NULL };
3701 const char *filter;
3702 LDAPMessage *res = NULL;
3703 char *dn = NULL;
3704 uint32 uac = 0;
3706 filter = talloc_asprintf(mem_ctx, "(&(objectclass=user)(sAMAccountName=%s))",
3707 samaccountname);
3708 if (filter == NULL) {
3709 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
3710 goto out;
3713 status = ads_do_search_all(ads, ads->config.bind_path,
3714 LDAP_SCOPE_SUBTREE,
3715 filter, attrs, &res);
3717 if (!ADS_ERR_OK(status)) {
3718 goto out;
3721 if (ads_count_replies(ads, res) != 1) {
3722 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3723 goto out;
3726 dn = ads_get_dn(ads, talloc_tos(), res);
3727 if (dn == NULL) {
3728 status = ADS_ERROR(LDAP_NO_MEMORY);
3729 goto out;
3732 if (!ads_pull_uint32(ads, res, "userAccountControl", &uac)) {
3733 status = ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
3734 goto out;
3737 if (uac_ret) {
3738 *uac_ret = uac;
3741 if (dn_ret) {
3742 *dn_ret = talloc_strdup(mem_ctx, dn);
3743 if (!*dn_ret) {
3744 status = ADS_ERROR(LDAP_NO_MEMORY);
3745 goto out;
3748 out:
3749 TALLOC_FREE(dn);
3750 ads_msgfree(ads, res);
3752 return status;
3756 * find our configuration path
3757 * @param ads connection to ads server
3758 * @param mem_ctx Pointer to talloc context
3759 * @param config_path Pointer to the config path
3760 * @return status of search
3762 ADS_STATUS ads_config_path(ADS_STRUCT *ads,
3763 TALLOC_CTX *mem_ctx,
3764 char **config_path)
3766 ADS_STATUS status;
3767 LDAPMessage *res = NULL;
3768 const char *config_context = NULL;
3769 const char *attrs[] = { "configurationNamingContext", NULL };
3771 status = ads_do_search(ads, "", LDAP_SCOPE_BASE,
3772 "(objectclass=*)", attrs, &res);
3773 if (!ADS_ERR_OK(status)) {
3774 return status;
3777 config_context = ads_pull_string(ads, mem_ctx, res,
3778 "configurationNamingContext");
3779 ads_msgfree(ads, res);
3780 if (!config_context) {
3781 return ADS_ERROR(LDAP_NO_MEMORY);
3784 if (config_path) {
3785 *config_path = talloc_strdup(mem_ctx, config_context);
3786 if (!*config_path) {
3787 return ADS_ERROR(LDAP_NO_MEMORY);
3791 return ADS_ERROR(LDAP_SUCCESS);
3795 * find the displayName of an extended right
3796 * @param ads connection to ads server
3797 * @param config_path The config path
3798 * @param mem_ctx Pointer to talloc context
3799 * @param GUID struct of the rightsGUID
3800 * @return status of search
3802 const char *ads_get_extended_right_name_by_guid(ADS_STRUCT *ads,
3803 const char *config_path,
3804 TALLOC_CTX *mem_ctx,
3805 const struct GUID *rights_guid)
3807 ADS_STATUS rc;
3808 LDAPMessage *res = NULL;
3809 char *expr = NULL;
3810 const char *attrs[] = { "displayName", NULL };
3811 const char *result = NULL;
3812 const char *path;
3814 if (!ads || !mem_ctx || !rights_guid) {
3815 goto done;
3818 expr = talloc_asprintf(mem_ctx, "(rightsGuid=%s)",
3819 GUID_string(mem_ctx, rights_guid));
3820 if (!expr) {
3821 goto done;
3824 path = talloc_asprintf(mem_ctx, "cn=Extended-Rights,%s", config_path);
3825 if (!path) {
3826 goto done;
3829 rc = ads_do_search_retry(ads, path, LDAP_SCOPE_SUBTREE,
3830 expr, attrs, &res);
3831 if (!ADS_ERR_OK(rc)) {
3832 goto done;
3835 if (ads_count_replies(ads, res) != 1) {
3836 goto done;
3839 result = ads_pull_string(ads, mem_ctx, res, "displayName");
3841 done:
3842 ads_msgfree(ads, res);
3843 return result;
3847 * verify or build and verify an account ou
3848 * @param mem_ctx Pointer to talloc context
3849 * @param ads connection to ads server
3850 * @param account_ou
3851 * @return status of search
3854 ADS_STATUS ads_check_ou_dn(TALLOC_CTX *mem_ctx,
3855 ADS_STRUCT *ads,
3856 const char **account_ou)
3858 struct ldb_dn *name_dn = NULL;
3859 const char *name = NULL;
3860 char *ou_string = NULL;
3861 struct ldb_context *ldb = ldb_init(mem_ctx, NULL);
3863 name_dn = ldb_dn_new(mem_ctx, ldb, *account_ou);
3864 if (name_dn && ldb_dn_validate(name_dn)) {
3865 talloc_free(ldb);
3866 return ADS_SUCCESS;
3869 ou_string = ads_ou_string(ads, *account_ou);
3870 if (!ou_string) {
3871 talloc_free(ldb);
3872 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX);
3875 name_dn = ldb_dn_new_fmt(mem_ctx, ldb, "%s,%s", ou_string,
3876 ads->config.bind_path);
3877 SAFE_FREE(ou_string);
3879 if (!name_dn || !ldb_dn_validate(name_dn)) {
3880 talloc_free(ldb);
3881 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX);
3884 *account_ou = talloc_strdup(mem_ctx, name);
3885 if (!*account_ou) {
3886 talloc_free(ldb);
3887 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3890 talloc_free(ldb);
3891 return ADS_SUCCESS;
3894 #endif