s3: Prevent glibc errors: talloc()ed memory should not be SAFE_FREE()ed.
[Samba.git] / source3 / libads / ldap.c
blob1fb541d4e6721314162bdbbce65e49ba0bb8496a
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/includes.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 *mem_ctx = NULL;
196 bool ret = false;
198 if (!server || !*server) {
199 return False;
202 DEBUG(5,("ads_try_connect: sending CLDAP request to %s (realm: %s)\n",
203 server, ads->server.realm));
205 mem_ctx = talloc_init("ads_try_connect");
206 if (!mem_ctx) {
207 DEBUG(0,("out of memory\n"));
208 return false;
211 /* this copes with inet_ntoa brokenness */
213 srv = SMB_STRDUP(server);
215 ZERO_STRUCT( cldap_reply );
217 if ( !ads_cldap_netlogon_5(mem_ctx, srv, ads->server.realm, &cldap_reply ) ) {
218 DEBUG(3,("ads_try_connect: CLDAP request %s failed.\n", srv));
219 ret = false;
220 goto out;
223 /* Check the CLDAP reply flags */
225 if ( !(cldap_reply.server_type & NBT_SERVER_LDAP) ) {
226 DEBUG(1,("ads_try_connect: %s's CLDAP reply says it is not an LDAP server!\n",
227 srv));
228 ret = false;
229 goto out;
232 /* Fill in the ads->config values */
234 SAFE_FREE(ads->config.realm);
235 SAFE_FREE(ads->config.bind_path);
236 SAFE_FREE(ads->config.ldap_server_name);
237 SAFE_FREE(ads->config.server_site_name);
238 SAFE_FREE(ads->config.client_site_name);
239 SAFE_FREE(ads->server.workgroup);
241 ads->config.flags = cldap_reply.server_type;
242 ads->config.ldap_server_name = SMB_STRDUP(cldap_reply.pdc_dns_name);
243 ads->config.realm = SMB_STRDUP(cldap_reply.dns_domain);
244 strupper_m(ads->config.realm);
245 ads->config.bind_path = ads_build_dn(ads->config.realm);
246 if (*cldap_reply.server_site) {
247 ads->config.server_site_name =
248 SMB_STRDUP(cldap_reply.server_site);
250 if (*cldap_reply.client_site) {
251 ads->config.client_site_name =
252 SMB_STRDUP(cldap_reply.client_site);
254 ads->server.workgroup = SMB_STRDUP(cldap_reply.domain);
256 ads->ldap.port = gc ? LDAP_GC_PORT : LDAP_PORT;
257 if (!interpret_string_addr(&ads->ldap.ss, srv, 0)) {
258 DEBUG(1,("ads_try_connect: unable to convert %s "
259 "to an address\n",
260 srv));
261 ret = false;
262 goto out;
265 /* Store our site name. */
266 sitename_store( cldap_reply.domain, cldap_reply.client_site);
267 sitename_store( cldap_reply.dns_domain, cldap_reply.client_site);
269 ret = true;
270 out:
271 SAFE_FREE(srv);
272 TALLOC_FREE(mem_ctx);
274 return ret;
277 /**********************************************************************
278 Try to find an AD dc using our internal name resolution routines
279 Try the realm first and then then workgroup name if netbios is not
280 disabled
281 **********************************************************************/
283 static NTSTATUS ads_find_dc(ADS_STRUCT *ads)
285 const char *c_domain;
286 const char *c_realm;
287 int count, i=0;
288 struct ip_service *ip_list;
289 const char *realm;
290 const char *domain;
291 bool got_realm = False;
292 bool use_own_domain = False;
293 char *sitename;
294 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
296 /* if the realm and workgroup are both empty, assume they are ours */
298 /* realm */
299 c_realm = ads->server.realm;
301 if ( !c_realm || !*c_realm ) {
302 /* special case where no realm and no workgroup means our own */
303 if ( !ads->server.workgroup || !*ads->server.workgroup ) {
304 use_own_domain = True;
305 c_realm = lp_realm();
309 if (c_realm && *c_realm)
310 got_realm = True;
312 /* we need to try once with the realm name and fallback to the
313 netbios domain name if we fail (if netbios has not been disabled */
315 if ( !got_realm && !lp_disable_netbios() ) {
316 c_realm = ads->server.workgroup;
317 if (!c_realm || !*c_realm) {
318 if ( use_own_domain )
319 c_realm = lp_workgroup();
323 if ( !c_realm || !*c_realm ) {
324 DEBUG(0,("ads_find_dc: no realm or workgroup! Don't know what to do\n"));
325 return NT_STATUS_INVALID_PARAMETER; /* rather need MISSING_PARAMETER ... */
328 if ( use_own_domain ) {
329 c_domain = lp_workgroup();
330 } else {
331 c_domain = ads->server.workgroup;
334 realm = c_realm;
335 domain = c_domain;
338 * In case of LDAP we use get_dc_name() as that
339 * creates the custom krb5.conf file
341 if (!(ads->auth.flags & ADS_AUTH_NO_BIND)) {
342 fstring srv_name;
343 struct sockaddr_storage ip_out;
345 DEBUG(6,("ads_find_dc: (ldap) looking for %s '%s'\n",
346 (got_realm ? "realm" : "domain"), realm));
348 if (get_dc_name(domain, realm, srv_name, &ip_out)) {
350 * we call ads_try_connect() to fill in the
351 * ads->config details
353 if (ads_try_connect(ads, srv_name, false)) {
354 return NT_STATUS_OK;
358 return NT_STATUS_NO_LOGON_SERVERS;
361 sitename = sitename_fetch(realm);
363 again:
365 DEBUG(6,("ads_find_dc: (cldap) looking for %s '%s'\n",
366 (got_realm ? "realm" : "domain"), realm));
368 status = get_sorted_dc_list(realm, sitename, &ip_list, &count, got_realm);
369 if (!NT_STATUS_IS_OK(status)) {
370 /* fall back to netbios if we can */
371 if ( got_realm && !lp_disable_netbios() ) {
372 got_realm = False;
373 goto again;
376 SAFE_FREE(sitename);
377 return status;
380 /* if we fail this loop, then giveup since all the IP addresses returned were dead */
381 for ( i=0; i<count; i++ ) {
382 char server[INET6_ADDRSTRLEN];
384 print_sockaddr(server, sizeof(server), &ip_list[i].ss);
386 if ( !NT_STATUS_IS_OK(check_negative_conn_cache(realm, server)) )
387 continue;
389 if (!got_realm) {
390 /* realm in this case is a workgroup name. We need
391 to ignore any IP addresses in the negative connection
392 cache that match ip addresses returned in the ad realm
393 case. It sucks that I have to reproduce the logic above... */
394 c_realm = ads->server.realm;
395 if ( !c_realm || !*c_realm ) {
396 if ( !ads->server.workgroup || !*ads->server.workgroup ) {
397 c_realm = lp_realm();
400 if (c_realm && *c_realm &&
401 !NT_STATUS_IS_OK(check_negative_conn_cache(c_realm, server))) {
402 /* Ensure we add the workgroup name for this
403 IP address as negative too. */
404 add_failed_connection_entry( realm, server, NT_STATUS_UNSUCCESSFUL );
405 continue;
409 if ( ads_try_connect(ads, server, false) ) {
410 SAFE_FREE(ip_list);
411 SAFE_FREE(sitename);
412 return NT_STATUS_OK;
415 /* keep track of failures */
416 add_failed_connection_entry( realm, server, NT_STATUS_UNSUCCESSFUL );
419 SAFE_FREE(ip_list);
421 /* In case we failed to contact one of our closest DC on our site we
422 * need to try to find another DC, retry with a site-less SRV DNS query
423 * - Guenther */
425 if (sitename) {
426 DEBUG(1,("ads_find_dc: failed to find a valid DC on our site (%s), "
427 "trying to find another DC\n", sitename));
428 SAFE_FREE(sitename);
429 namecache_delete(realm, 0x1C);
430 goto again;
433 return NT_STATUS_NO_LOGON_SERVERS;
436 /*********************************************************************
437 *********************************************************************/
439 static NTSTATUS ads_lookup_site(void)
441 ADS_STRUCT *ads = NULL;
442 ADS_STATUS ads_status;
443 NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
445 ads = ads_init(lp_realm(), NULL, NULL);
446 if (!ads) {
447 return NT_STATUS_NO_MEMORY;
450 /* The NO_BIND here will find a DC and set the client site
451 but not establish the TCP connection */
453 ads->auth.flags = ADS_AUTH_NO_BIND;
454 ads_status = ads_connect(ads);
455 if (!ADS_ERR_OK(ads_status)) {
456 DEBUG(4, ("ads_lookup_site: ads_connect to our realm failed! (%s)\n",
457 ads_errstr(ads_status)));
459 nt_status = ads_ntstatus(ads_status);
461 if (ads) {
462 ads_destroy(&ads);
465 return nt_status;
468 /*********************************************************************
469 *********************************************************************/
471 static const char* host_dns_domain(const char *fqdn)
473 const char *p = fqdn;
475 /* go to next char following '.' */
477 if ((p = strchr_m(fqdn, '.')) != NULL) {
478 p++;
481 return p;
486 * Connect to the Global Catalog server
487 * @param ads Pointer to an existing ADS_STRUCT
488 * @return status of connection
490 * Simple wrapper around ads_connect() that fills in the
491 * GC ldap server information
494 ADS_STATUS ads_connect_gc(ADS_STRUCT *ads)
496 TALLOC_CTX *frame = talloc_stackframe();
497 struct dns_rr_srv *gcs_list;
498 int num_gcs;
499 char *realm = ads->server.realm;
500 NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
501 ADS_STATUS ads_status = ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
502 int i;
503 bool done = false;
504 char *sitename = NULL;
506 if (!realm)
507 realm = lp_realm();
509 if ((sitename = sitename_fetch(realm)) == NULL) {
510 ads_lookup_site();
511 sitename = sitename_fetch(realm);
514 do {
515 /* We try once with a sitename and once without
516 (unless we don't have a sitename and then we're
517 done */
519 if (sitename == NULL)
520 done = true;
522 nt_status = ads_dns_query_gcs(frame, realm, sitename,
523 &gcs_list, &num_gcs);
525 SAFE_FREE(sitename);
527 if (!NT_STATUS_IS_OK(nt_status)) {
528 ads_status = ADS_ERROR_NT(nt_status);
529 goto done;
532 /* Loop until we get a successful connection or have gone
533 through them all. When connecting a GC server, make sure that
534 the realm is the server's DNS name and not the forest root */
536 for (i=0; i<num_gcs; i++) {
537 ads->server.gc = true;
538 ads->server.ldap_server = SMB_STRDUP(gcs_list[i].hostname);
539 ads->server.realm = SMB_STRDUP(host_dns_domain(ads->server.ldap_server));
540 ads_status = ads_connect(ads);
541 if (ADS_ERR_OK(ads_status)) {
542 /* Reset the bind_dn to "". A Global Catalog server
543 may host multiple domain trees in a forest.
544 Windows 2003 GC server will accept "" as the search
545 path to imply search all domain trees in the forest */
547 SAFE_FREE(ads->config.bind_path);
548 ads->config.bind_path = SMB_STRDUP("");
551 goto done;
553 SAFE_FREE(ads->server.ldap_server);
554 SAFE_FREE(ads->server.realm);
557 TALLOC_FREE(gcs_list);
558 num_gcs = 0;
559 } while (!done);
561 done:
562 SAFE_FREE(sitename);
563 talloc_destroy(frame);
565 return ads_status;
570 * Connect to the LDAP server
571 * @param ads Pointer to an existing ADS_STRUCT
572 * @return status of connection
574 ADS_STATUS ads_connect(ADS_STRUCT *ads)
576 int version = LDAP_VERSION3;
577 ADS_STATUS status;
578 NTSTATUS ntstatus;
579 char addr[INET6_ADDRSTRLEN];
581 ZERO_STRUCT(ads->ldap);
582 ads->ldap.last_attempt = time(NULL);
583 ads->ldap.wrap_type = ADS_SASLWRAP_TYPE_PLAIN;
585 /* try with a user specified server */
587 if (DEBUGLEVEL >= 11) {
588 char *s = NDR_PRINT_STRUCT_STRING(talloc_tos(), ads_struct, ads);
589 DEBUG(11,("ads_connect: entering\n"));
590 DEBUGADD(11,("%s\n", s));
591 TALLOC_FREE(s);
594 if (ads->server.ldap_server)
596 if (ads_try_connect(ads, ads->server.ldap_server, ads->server.gc)) {
597 goto got_connection;
600 /* The choice of which GC use is handled one level up in
601 ads_connect_gc(). If we continue on from here with
602 ads_find_dc() we will get GC searches on port 389 which
603 doesn't work. --jerry */
605 if (ads->server.gc == true) {
606 return ADS_ERROR(LDAP_OPERATIONS_ERROR);
610 ntstatus = ads_find_dc(ads);
611 if (NT_STATUS_IS_OK(ntstatus)) {
612 goto got_connection;
615 status = ADS_ERROR_NT(ntstatus);
616 goto out;
618 got_connection:
620 print_sockaddr(addr, sizeof(addr), &ads->ldap.ss);
621 DEBUG(3,("Successfully contacted LDAP server %s\n", addr));
623 if (!ads->auth.user_name) {
624 /* Must use the userPrincipalName value here or sAMAccountName
625 and not servicePrincipalName; found by Guenther Deschner */
627 if (asprintf(&ads->auth.user_name, "%s$", global_myname() ) == -1) {
628 DEBUG(0,("ads_connect: asprintf fail.\n"));
629 ads->auth.user_name = NULL;
633 if (!ads->auth.realm) {
634 ads->auth.realm = SMB_STRDUP(ads->config.realm);
637 if (!ads->auth.kdc_server) {
638 print_sockaddr(addr, sizeof(addr), &ads->ldap.ss);
639 ads->auth.kdc_server = SMB_STRDUP(addr);
642 #if KRB5_DNS_HACK
643 /* this is a really nasty hack to avoid ADS DNS problems. It needs a patch
644 to MIT kerberos to work (tridge) */
646 char *env = NULL;
647 if (asprintf(&env, "KRB5_KDC_ADDRESS_%s", ads->config.realm) > 0) {
648 setenv(env, ads->auth.kdc_server, 1);
649 free(env);
652 #endif
654 /* If the caller() requested no LDAP bind, then we are done */
656 if (ads->auth.flags & ADS_AUTH_NO_BIND) {
657 status = ADS_SUCCESS;
658 goto out;
661 ads->ldap.mem_ctx = talloc_init("ads LDAP connection memory");
662 if (!ads->ldap.mem_ctx) {
663 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
664 goto out;
667 /* Otherwise setup the TCP LDAP session */
669 ads->ldap.ld = ldap_open_with_timeout(ads->config.ldap_server_name,
670 ads->ldap.port, lp_ldap_timeout());
671 if (ads->ldap.ld == NULL) {
672 status = ADS_ERROR(LDAP_OPERATIONS_ERROR);
673 goto out;
675 DEBUG(3,("Connected to LDAP server %s\n", ads->config.ldap_server_name));
677 /* cache the successful connection for workgroup and realm */
678 if (ads_closest_dc(ads)) {
679 saf_store( ads->server.workgroup, ads->config.ldap_server_name);
680 saf_store( ads->server.realm, ads->config.ldap_server_name);
683 ldap_set_option(ads->ldap.ld, LDAP_OPT_PROTOCOL_VERSION, &version);
685 if ( lp_ldap_ssl_ads() ) {
686 status = ADS_ERROR(smb_ldap_start_tls(ads->ldap.ld, version));
687 if (!ADS_ERR_OK(status)) {
688 goto out;
692 /* fill in the current time and offsets */
694 status = ads_current_time( ads );
695 if ( !ADS_ERR_OK(status) ) {
696 goto out;
699 /* Now do the bind */
701 if (ads->auth.flags & ADS_AUTH_ANON_BIND) {
702 status = ADS_ERROR(ldap_simple_bind_s(ads->ldap.ld, NULL, NULL));
703 goto out;
706 if (ads->auth.flags & ADS_AUTH_SIMPLE_BIND) {
707 status = ADS_ERROR(ldap_simple_bind_s(ads->ldap.ld, ads->auth.user_name, ads->auth.password));
708 goto out;
711 status = ads_sasl_bind(ads);
713 out:
714 if (DEBUGLEVEL >= 11) {
715 char *s = NDR_PRINT_STRUCT_STRING(talloc_tos(), ads_struct, ads);
716 DEBUG(11,("ads_connect: leaving with: %s\n",
717 ads_errstr(status)));
718 DEBUGADD(11,("%s\n", s));
719 TALLOC_FREE(s);
722 return status;
726 * Connect to the LDAP server using given credentials
727 * @param ads Pointer to an existing ADS_STRUCT
728 * @return status of connection
730 ADS_STATUS ads_connect_user_creds(ADS_STRUCT *ads)
732 ads->auth.flags |= ADS_AUTH_USER_CREDS;
734 return ads_connect(ads);
738 * Disconnect the LDAP server
739 * @param ads Pointer to an existing ADS_STRUCT
741 void ads_disconnect(ADS_STRUCT *ads)
743 if (ads->ldap.ld) {
744 ldap_unbind(ads->ldap.ld);
745 ads->ldap.ld = NULL;
747 if (ads->ldap.wrap_ops && ads->ldap.wrap_ops->disconnect) {
748 ads->ldap.wrap_ops->disconnect(ads);
750 if (ads->ldap.mem_ctx) {
751 talloc_free(ads->ldap.mem_ctx);
753 ZERO_STRUCT(ads->ldap);
757 Duplicate a struct berval into talloc'ed memory
759 static struct berval *dup_berval(TALLOC_CTX *ctx, const struct berval *in_val)
761 struct berval *value;
763 if (!in_val) return NULL;
765 value = TALLOC_ZERO_P(ctx, struct berval);
766 if (value == NULL)
767 return NULL;
768 if (in_val->bv_len == 0) return value;
770 value->bv_len = in_val->bv_len;
771 value->bv_val = (char *)TALLOC_MEMDUP(ctx, in_val->bv_val,
772 in_val->bv_len);
773 return value;
777 Make a values list out of an array of (struct berval *)
779 static struct berval **ads_dup_values(TALLOC_CTX *ctx,
780 const struct berval **in_vals)
782 struct berval **values;
783 int i;
785 if (!in_vals) return NULL;
786 for (i=0; in_vals[i]; i++)
787 ; /* count values */
788 values = TALLOC_ZERO_ARRAY(ctx, struct berval *, i+1);
789 if (!values) return NULL;
791 for (i=0; in_vals[i]; i++) {
792 values[i] = dup_berval(ctx, in_vals[i]);
794 return values;
798 UTF8-encode a values list out of an array of (char *)
800 static char **ads_push_strvals(TALLOC_CTX *ctx, const char **in_vals)
802 char **values;
803 int i;
804 size_t size;
806 if (!in_vals) return NULL;
807 for (i=0; in_vals[i]; i++)
808 ; /* count values */
809 values = TALLOC_ZERO_ARRAY(ctx, char *, i+1);
810 if (!values) return NULL;
812 for (i=0; in_vals[i]; i++) {
813 if (!push_utf8_talloc(ctx, &values[i], in_vals[i], &size)) {
814 TALLOC_FREE(values);
815 return NULL;
818 return values;
822 Pull a (char *) array out of a UTF8-encoded values list
824 static char **ads_pull_strvals(TALLOC_CTX *ctx, const char **in_vals)
826 char **values;
827 int i;
828 size_t converted_size;
830 if (!in_vals) return NULL;
831 for (i=0; in_vals[i]; i++)
832 ; /* count values */
833 values = TALLOC_ZERO_ARRAY(ctx, char *, i+1);
834 if (!values) return NULL;
836 for (i=0; in_vals[i]; i++) {
837 if (!pull_utf8_talloc(ctx, &values[i], in_vals[i],
838 &converted_size)) {
839 DEBUG(0,("ads_pull_strvals: pull_utf8_talloc failed: "
840 "%s", strerror(errno)));
843 return values;
847 * Do a search with paged results. cookie must be null on the first
848 * call, and then returned on each subsequent call. It will be null
849 * again when the entire search is complete
850 * @param ads connection to ads server
851 * @param bind_path Base dn for the search
852 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
853 * @param expr Search expression - specified in local charset
854 * @param attrs Attributes to retrieve - specified in utf8 or ascii
855 * @param res ** which will contain results - free res* with ads_msgfree()
856 * @param count Number of entries retrieved on this page
857 * @param cookie The paged results cookie to be returned on subsequent calls
858 * @return status of search
860 static ADS_STATUS ads_do_paged_search_args(ADS_STRUCT *ads,
861 const char *bind_path,
862 int scope, const char *expr,
863 const char **attrs, void *args,
864 LDAPMessage **res,
865 int *count, struct berval **cookie)
867 int rc, i, version;
868 char *utf8_expr, *utf8_path, **search_attrs = NULL;
869 size_t converted_size;
870 LDAPControl PagedResults, NoReferrals, ExternalCtrl, *controls[4], **rcontrols;
871 BerElement *cookie_be = NULL;
872 struct berval *cookie_bv= NULL;
873 BerElement *ext_be = NULL;
874 struct berval *ext_bv= NULL;
876 TALLOC_CTX *ctx;
877 ads_control *external_control = (ads_control *) args;
879 *res = NULL;
881 if (!(ctx = talloc_init("ads_do_paged_search_args")))
882 return ADS_ERROR(LDAP_NO_MEMORY);
884 /* 0 means the conversion worked but the result was empty
885 so we only fail if it's -1. In any case, it always
886 at least nulls out the dest */
887 if (!push_utf8_talloc(ctx, &utf8_expr, expr, &converted_size) ||
888 !push_utf8_talloc(ctx, &utf8_path, bind_path, &converted_size))
890 rc = LDAP_NO_MEMORY;
891 goto done;
894 if (!attrs || !(*attrs))
895 search_attrs = NULL;
896 else {
897 /* This would be the utf8-encoded version...*/
898 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
899 if (!(search_attrs = str_list_copy(talloc_tos(), attrs))) {
900 rc = LDAP_NO_MEMORY;
901 goto done;
905 /* Paged results only available on ldap v3 or later */
906 ldap_get_option(ads->ldap.ld, LDAP_OPT_PROTOCOL_VERSION, &version);
907 if (version < LDAP_VERSION3) {
908 rc = LDAP_NOT_SUPPORTED;
909 goto done;
912 cookie_be = ber_alloc_t(LBER_USE_DER);
913 if (*cookie) {
914 ber_printf(cookie_be, "{iO}", (ber_int_t) 1000, *cookie);
915 ber_bvfree(*cookie); /* don't need it from last time */
916 *cookie = NULL;
917 } else {
918 ber_printf(cookie_be, "{io}", (ber_int_t) 1000, "", 0);
920 ber_flatten(cookie_be, &cookie_bv);
921 PagedResults.ldctl_oid = CONST_DISCARD(char *, ADS_PAGE_CTL_OID);
922 PagedResults.ldctl_iscritical = (char) 1;
923 PagedResults.ldctl_value.bv_len = cookie_bv->bv_len;
924 PagedResults.ldctl_value.bv_val = cookie_bv->bv_val;
926 NoReferrals.ldctl_oid = CONST_DISCARD(char *, ADS_NO_REFERRALS_OID);
927 NoReferrals.ldctl_iscritical = (char) 0;
928 NoReferrals.ldctl_value.bv_len = 0;
929 NoReferrals.ldctl_value.bv_val = CONST_DISCARD(char *, "");
931 if (external_control &&
932 (strequal(external_control->control, ADS_EXTENDED_DN_OID) ||
933 strequal(external_control->control, ADS_SD_FLAGS_OID))) {
935 ExternalCtrl.ldctl_oid = CONST_DISCARD(char *, external_control->control);
936 ExternalCtrl.ldctl_iscritical = (char) external_control->critical;
938 /* win2k does not accept a ldctl_value beeing passed in */
940 if (external_control->val != 0) {
942 if ((ext_be = ber_alloc_t(LBER_USE_DER)) == NULL ) {
943 rc = LDAP_NO_MEMORY;
944 goto done;
947 if ((ber_printf(ext_be, "{i}", (ber_int_t) external_control->val)) == -1) {
948 rc = LDAP_NO_MEMORY;
949 goto done;
951 if ((ber_flatten(ext_be, &ext_bv)) == -1) {
952 rc = LDAP_NO_MEMORY;
953 goto done;
956 ExternalCtrl.ldctl_value.bv_len = ext_bv->bv_len;
957 ExternalCtrl.ldctl_value.bv_val = ext_bv->bv_val;
959 } else {
960 ExternalCtrl.ldctl_value.bv_len = 0;
961 ExternalCtrl.ldctl_value.bv_val = NULL;
964 controls[0] = &NoReferrals;
965 controls[1] = &PagedResults;
966 controls[2] = &ExternalCtrl;
967 controls[3] = NULL;
969 } else {
970 controls[0] = &NoReferrals;
971 controls[1] = &PagedResults;
972 controls[2] = NULL;
975 /* we need to disable referrals as the openldap libs don't
976 handle them and paged results at the same time. Using them
977 together results in the result record containing the server
978 page control being removed from the result list (tridge/jmcd)
980 leaving this in despite the control that says don't generate
981 referrals, in case the server doesn't support it (jmcd)
983 ldap_set_option(ads->ldap.ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
985 rc = ldap_search_with_timeout(ads->ldap.ld, utf8_path, scope, utf8_expr,
986 search_attrs, 0, controls,
987 NULL, LDAP_NO_LIMIT,
988 (LDAPMessage **)res);
990 ber_free(cookie_be, 1);
991 ber_bvfree(cookie_bv);
993 if (rc) {
994 DEBUG(3,("ads_do_paged_search_args: ldap_search_with_timeout(%s) -> %s\n", expr,
995 ldap_err2string(rc)));
996 goto done;
999 rc = ldap_parse_result(ads->ldap.ld, *res, NULL, NULL, NULL,
1000 NULL, &rcontrols, 0);
1002 if (!rcontrols) {
1003 goto done;
1006 for (i=0; rcontrols[i]; i++) {
1007 if (strcmp(ADS_PAGE_CTL_OID, rcontrols[i]->ldctl_oid) == 0) {
1008 cookie_be = ber_init(&rcontrols[i]->ldctl_value);
1009 ber_scanf(cookie_be,"{iO}", (ber_int_t *) count,
1010 &cookie_bv);
1011 /* the berval is the cookie, but must be freed when
1012 it is all done */
1013 if (cookie_bv->bv_len) /* still more to do */
1014 *cookie=ber_bvdup(cookie_bv);
1015 else
1016 *cookie=NULL;
1017 ber_bvfree(cookie_bv);
1018 ber_free(cookie_be, 1);
1019 break;
1022 ldap_controls_free(rcontrols);
1024 done:
1025 talloc_destroy(ctx);
1027 if (ext_be) {
1028 ber_free(ext_be, 1);
1031 if (ext_bv) {
1032 ber_bvfree(ext_bv);
1035 /* if/when we decide to utf8-encode attrs, take out this next line */
1036 TALLOC_FREE(search_attrs);
1038 return ADS_ERROR(rc);
1041 static ADS_STATUS ads_do_paged_search(ADS_STRUCT *ads, const char *bind_path,
1042 int scope, const char *expr,
1043 const char **attrs, LDAPMessage **res,
1044 int *count, struct berval **cookie)
1046 return ads_do_paged_search_args(ads, bind_path, scope, expr, attrs, NULL, res, count, cookie);
1051 * Get all results for a search. This uses ads_do_paged_search() to return
1052 * all entries in a large search.
1053 * @param ads connection to ads server
1054 * @param bind_path Base dn for the search
1055 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1056 * @param expr Search expression
1057 * @param attrs Attributes to retrieve
1058 * @param res ** which will contain results - free res* with ads_msgfree()
1059 * @return status of search
1061 ADS_STATUS ads_do_search_all_args(ADS_STRUCT *ads, const char *bind_path,
1062 int scope, const char *expr,
1063 const char **attrs, void *args,
1064 LDAPMessage **res)
1066 struct berval *cookie = NULL;
1067 int count = 0;
1068 ADS_STATUS status;
1070 *res = NULL;
1071 status = ads_do_paged_search_args(ads, bind_path, scope, expr, attrs, args, res,
1072 &count, &cookie);
1074 if (!ADS_ERR_OK(status))
1075 return status;
1077 #ifdef HAVE_LDAP_ADD_RESULT_ENTRY
1078 while (cookie) {
1079 LDAPMessage *res2 = NULL;
1080 ADS_STATUS status2;
1081 LDAPMessage *msg, *next;
1083 status2 = ads_do_paged_search_args(ads, bind_path, scope, expr,
1084 attrs, args, &res2, &count, &cookie);
1086 if (!ADS_ERR_OK(status2)) break;
1088 /* this relies on the way that ldap_add_result_entry() works internally. I hope
1089 that this works on all ldap libs, but I have only tested with openldap */
1090 for (msg = ads_first_message(ads, res2); msg; msg = next) {
1091 next = ads_next_message(ads, msg);
1092 ldap_add_result_entry((LDAPMessage **)res, msg);
1094 /* note that we do not free res2, as the memory is now
1095 part of the main returned list */
1097 #else
1098 DEBUG(0, ("no ldap_add_result_entry() support in LDAP libs!\n"));
1099 status = ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
1100 #endif
1102 return status;
1105 ADS_STATUS ads_do_search_all(ADS_STRUCT *ads, const char *bind_path,
1106 int scope, const char *expr,
1107 const char **attrs, LDAPMessage **res)
1109 return ads_do_search_all_args(ads, bind_path, scope, expr, attrs, NULL, res);
1112 ADS_STATUS ads_do_search_all_sd_flags(ADS_STRUCT *ads, const char *bind_path,
1113 int scope, const char *expr,
1114 const char **attrs, uint32 sd_flags,
1115 LDAPMessage **res)
1117 ads_control args;
1119 args.control = ADS_SD_FLAGS_OID;
1120 args.val = sd_flags;
1121 args.critical = True;
1123 return ads_do_search_all_args(ads, bind_path, scope, expr, attrs, &args, res);
1128 * Run a function on all results for a search. Uses ads_do_paged_search() and
1129 * runs the function as each page is returned, using ads_process_results()
1130 * @param ads connection to ads server
1131 * @param bind_path Base dn for the search
1132 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1133 * @param expr Search expression - specified in local charset
1134 * @param attrs Attributes to retrieve - specified in UTF-8 or ascii
1135 * @param fn Function which takes attr name, values list, and data_area
1136 * @param data_area Pointer which is passed to function on each call
1137 * @return status of search
1139 ADS_STATUS ads_do_search_all_fn(ADS_STRUCT *ads, const char *bind_path,
1140 int scope, const char *expr, const char **attrs,
1141 bool (*fn)(ADS_STRUCT *, char *, void **, void *),
1142 void *data_area)
1144 struct berval *cookie = NULL;
1145 int count = 0;
1146 ADS_STATUS status;
1147 LDAPMessage *res;
1149 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs, &res,
1150 &count, &cookie);
1152 if (!ADS_ERR_OK(status)) return status;
1154 ads_process_results(ads, res, fn, data_area);
1155 ads_msgfree(ads, res);
1157 while (cookie) {
1158 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs,
1159 &res, &count, &cookie);
1161 if (!ADS_ERR_OK(status)) break;
1163 ads_process_results(ads, res, fn, data_area);
1164 ads_msgfree(ads, res);
1167 return status;
1171 * Do a search with a timeout.
1172 * @param ads connection to ads server
1173 * @param bind_path Base dn for the search
1174 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1175 * @param expr Search expression
1176 * @param attrs Attributes to retrieve
1177 * @param res ** which will contain results - free res* with ads_msgfree()
1178 * @return status of search
1180 ADS_STATUS ads_do_search(ADS_STRUCT *ads, const char *bind_path, int scope,
1181 const char *expr,
1182 const char **attrs, LDAPMessage **res)
1184 int rc;
1185 char *utf8_expr, *utf8_path, **search_attrs = NULL;
1186 size_t converted_size;
1187 TALLOC_CTX *ctx;
1189 *res = NULL;
1190 if (!(ctx = talloc_init("ads_do_search"))) {
1191 DEBUG(1,("ads_do_search: talloc_init() failed!"));
1192 return ADS_ERROR(LDAP_NO_MEMORY);
1195 /* 0 means the conversion worked but the result was empty
1196 so we only fail if it's negative. In any case, it always
1197 at least nulls out the dest */
1198 if (!push_utf8_talloc(ctx, &utf8_expr, expr, &converted_size) ||
1199 !push_utf8_talloc(ctx, &utf8_path, bind_path, &converted_size))
1201 DEBUG(1,("ads_do_search: push_utf8_talloc() failed!"));
1202 rc = LDAP_NO_MEMORY;
1203 goto done;
1206 if (!attrs || !(*attrs))
1207 search_attrs = NULL;
1208 else {
1209 /* This would be the utf8-encoded version...*/
1210 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
1211 if (!(search_attrs = str_list_copy(talloc_tos(), attrs)))
1213 DEBUG(1,("ads_do_search: str_list_copy() failed!"));
1214 rc = LDAP_NO_MEMORY;
1215 goto done;
1219 /* see the note in ads_do_paged_search - we *must* disable referrals */
1220 ldap_set_option(ads->ldap.ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
1222 rc = ldap_search_with_timeout(ads->ldap.ld, utf8_path, scope, utf8_expr,
1223 search_attrs, 0, NULL, NULL,
1224 LDAP_NO_LIMIT,
1225 (LDAPMessage **)res);
1227 if (rc == LDAP_SIZELIMIT_EXCEEDED) {
1228 DEBUG(3,("Warning! sizelimit exceeded in ldap. Truncating.\n"));
1229 rc = 0;
1232 done:
1233 talloc_destroy(ctx);
1234 /* if/when we decide to utf8-encode attrs, take out this next line */
1235 TALLOC_FREE(search_attrs);
1236 return ADS_ERROR(rc);
1239 * Do a general ADS search
1240 * @param ads connection to ads server
1241 * @param res ** which will contain results - free res* with ads_msgfree()
1242 * @param expr Search expression
1243 * @param attrs Attributes to retrieve
1244 * @return status of search
1246 ADS_STATUS ads_search(ADS_STRUCT *ads, LDAPMessage **res,
1247 const char *expr, const char **attrs)
1249 return ads_do_search(ads, ads->config.bind_path, LDAP_SCOPE_SUBTREE,
1250 expr, attrs, res);
1254 * Do a search on a specific DistinguishedName
1255 * @param ads connection to ads server
1256 * @param res ** which will contain results - free res* with ads_msgfree()
1257 * @param dn DistinguishName to search
1258 * @param attrs Attributes to retrieve
1259 * @return status of search
1261 ADS_STATUS ads_search_dn(ADS_STRUCT *ads, LDAPMessage **res,
1262 const char *dn, const char **attrs)
1264 return ads_do_search(ads, dn, LDAP_SCOPE_BASE, "(objectclass=*)",
1265 attrs, res);
1269 * Free up memory from a ads_search
1270 * @param ads connection to ads server
1271 * @param msg Search results to free
1273 void ads_msgfree(ADS_STRUCT *ads, LDAPMessage *msg)
1275 if (!msg) return;
1276 ldap_msgfree(msg);
1280 * Get a dn from search results
1281 * @param ads connection to ads server
1282 * @param msg Search result
1283 * @return dn string
1285 char *ads_get_dn(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, LDAPMessage *msg)
1287 char *utf8_dn, *unix_dn;
1288 size_t converted_size;
1290 utf8_dn = ldap_get_dn(ads->ldap.ld, msg);
1292 if (!utf8_dn) {
1293 DEBUG (5, ("ads_get_dn: ldap_get_dn failed\n"));
1294 return NULL;
1297 if (!pull_utf8_talloc(mem_ctx, &unix_dn, utf8_dn, &converted_size)) {
1298 DEBUG(0,("ads_get_dn: string conversion failure utf8 [%s]\n",
1299 utf8_dn ));
1300 return NULL;
1302 ldap_memfree(utf8_dn);
1303 return unix_dn;
1307 * Get the parent from a dn
1308 * @param dn the dn to return the parent from
1309 * @return parent dn string
1311 char *ads_parent_dn(const char *dn)
1313 char *p;
1315 if (dn == NULL) {
1316 return NULL;
1319 p = strchr(dn, ',');
1321 if (p == NULL) {
1322 return NULL;
1325 return p+1;
1329 * Find a machine account given a hostname
1330 * @param ads connection to ads server
1331 * @param res ** which will contain results - free res* with ads_msgfree()
1332 * @param host Hostname to search for
1333 * @return status of search
1335 ADS_STATUS ads_find_machine_acct(ADS_STRUCT *ads, LDAPMessage **res,
1336 const char *machine)
1338 ADS_STATUS status;
1339 char *expr;
1340 const char *attrs[] = {"*", "nTSecurityDescriptor", NULL};
1342 *res = NULL;
1344 /* the easiest way to find a machine account anywhere in the tree
1345 is to look for hostname$ */
1346 if (asprintf(&expr, "(samAccountName=%s$)", machine) == -1) {
1347 DEBUG(1, ("asprintf failed!\n"));
1348 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1351 status = ads_search(ads, res, expr, attrs);
1352 SAFE_FREE(expr);
1353 return status;
1357 * Initialize a list of mods to be used in a modify request
1358 * @param ctx An initialized TALLOC_CTX
1359 * @return allocated ADS_MODLIST
1361 ADS_MODLIST ads_init_mods(TALLOC_CTX *ctx)
1363 #define ADS_MODLIST_ALLOC_SIZE 10
1364 LDAPMod **mods;
1366 if ((mods = TALLOC_ZERO_ARRAY(ctx, LDAPMod *, ADS_MODLIST_ALLOC_SIZE + 1)))
1367 /* -1 is safety to make sure we don't go over the end.
1368 need to reset it to NULL before doing ldap modify */
1369 mods[ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1371 return (ADS_MODLIST)mods;
1376 add an attribute to the list, with values list already constructed
1378 static ADS_STATUS ads_modlist_add(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1379 int mod_op, const char *name,
1380 const void *_invals)
1382 const void **invals = (const void **)_invals;
1383 int curmod;
1384 LDAPMod **modlist = (LDAPMod **) *mods;
1385 struct berval **ber_values = NULL;
1386 char **char_values = NULL;
1388 if (!invals) {
1389 mod_op = LDAP_MOD_DELETE;
1390 } else {
1391 if (mod_op & LDAP_MOD_BVALUES)
1392 ber_values = ads_dup_values(ctx,
1393 (const struct berval **)invals);
1394 else
1395 char_values = ads_push_strvals(ctx,
1396 (const char **) invals);
1399 /* find the first empty slot */
1400 for (curmod=0; modlist[curmod] && modlist[curmod] != (LDAPMod *) -1;
1401 curmod++);
1402 if (modlist[curmod] == (LDAPMod *) -1) {
1403 if (!(modlist = TALLOC_REALLOC_ARRAY(ctx, modlist, LDAPMod *,
1404 curmod+ADS_MODLIST_ALLOC_SIZE+1)))
1405 return ADS_ERROR(LDAP_NO_MEMORY);
1406 memset(&modlist[curmod], 0,
1407 ADS_MODLIST_ALLOC_SIZE*sizeof(LDAPMod *));
1408 modlist[curmod+ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1409 *mods = (ADS_MODLIST)modlist;
1412 if (!(modlist[curmod] = TALLOC_ZERO_P(ctx, LDAPMod)))
1413 return ADS_ERROR(LDAP_NO_MEMORY);
1414 modlist[curmod]->mod_type = talloc_strdup(ctx, name);
1415 if (mod_op & LDAP_MOD_BVALUES) {
1416 modlist[curmod]->mod_bvalues = ber_values;
1417 } else if (mod_op & LDAP_MOD_DELETE) {
1418 modlist[curmod]->mod_values = NULL;
1419 } else {
1420 modlist[curmod]->mod_values = char_values;
1423 modlist[curmod]->mod_op = mod_op;
1424 return ADS_ERROR(LDAP_SUCCESS);
1428 * Add a single string value to a mod list
1429 * @param ctx An initialized TALLOC_CTX
1430 * @param mods An initialized ADS_MODLIST
1431 * @param name The attribute name to add
1432 * @param val The value to add - NULL means DELETE
1433 * @return ADS STATUS indicating success of add
1435 ADS_STATUS ads_mod_str(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1436 const char *name, const char *val)
1438 const char *values[2];
1440 values[0] = val;
1441 values[1] = NULL;
1443 if (!val)
1444 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1445 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE, name, values);
1449 * Add an array of string values to a mod list
1450 * @param ctx An initialized TALLOC_CTX
1451 * @param mods An initialized ADS_MODLIST
1452 * @param name The attribute name to add
1453 * @param vals The array of string values to add - NULL means DELETE
1454 * @return ADS STATUS indicating success of add
1456 ADS_STATUS ads_mod_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1457 const char *name, const char **vals)
1459 if (!vals)
1460 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1461 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE,
1462 name, (const void **) vals);
1465 #if 0
1467 * Add a single ber-encoded value to a mod list
1468 * @param ctx An initialized TALLOC_CTX
1469 * @param mods An initialized ADS_MODLIST
1470 * @param name The attribute name to add
1471 * @param val The value to add - NULL means DELETE
1472 * @return ADS STATUS indicating success of add
1474 static ADS_STATUS ads_mod_ber(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1475 const char *name, const struct berval *val)
1477 const struct berval *values[2];
1479 values[0] = val;
1480 values[1] = NULL;
1481 if (!val)
1482 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1483 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE|LDAP_MOD_BVALUES,
1484 name, (const void **) values);
1486 #endif
1489 * Perform an ldap modify
1490 * @param ads connection to ads server
1491 * @param mod_dn DistinguishedName to modify
1492 * @param mods list of modifications to perform
1493 * @return status of modify
1495 ADS_STATUS ads_gen_mod(ADS_STRUCT *ads, const char *mod_dn, ADS_MODLIST mods)
1497 int ret,i;
1498 char *utf8_dn = NULL;
1499 size_t converted_size;
1501 this control is needed to modify that contains a currently
1502 non-existent attribute (but allowable for the object) to run
1504 LDAPControl PermitModify = {
1505 CONST_DISCARD(char *, ADS_PERMIT_MODIFY_OID),
1506 {0, NULL},
1507 (char) 1};
1508 LDAPControl *controls[2];
1510 controls[0] = &PermitModify;
1511 controls[1] = NULL;
1513 if (!push_utf8_allocate(&utf8_dn, mod_dn, &converted_size)) {
1514 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1517 /* find the end of the list, marked by NULL or -1 */
1518 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1519 /* make sure the end of the list is NULL */
1520 mods[i] = NULL;
1521 ret = ldap_modify_ext_s(ads->ldap.ld, utf8_dn,
1522 (LDAPMod **) mods, controls, NULL);
1523 SAFE_FREE(utf8_dn);
1524 return ADS_ERROR(ret);
1528 * Perform an ldap add
1529 * @param ads connection to ads server
1530 * @param new_dn DistinguishedName to add
1531 * @param mods list of attributes and values for DN
1532 * @return status of add
1534 ADS_STATUS ads_gen_add(ADS_STRUCT *ads, const char *new_dn, ADS_MODLIST mods)
1536 int ret, i;
1537 char *utf8_dn = NULL;
1538 size_t converted_size;
1540 if (!push_utf8_allocate(&utf8_dn, new_dn, &converted_size)) {
1541 DEBUG(1, ("ads_gen_add: push_utf8_allocate failed!"));
1542 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1545 /* find the end of the list, marked by NULL or -1 */
1546 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1547 /* make sure the end of the list is NULL */
1548 mods[i] = NULL;
1550 ret = ldap_add_s(ads->ldap.ld, utf8_dn, (LDAPMod**)mods);
1551 SAFE_FREE(utf8_dn);
1552 return ADS_ERROR(ret);
1556 * Delete a DistinguishedName
1557 * @param ads connection to ads server
1558 * @param new_dn DistinguishedName to delete
1559 * @return status of delete
1561 ADS_STATUS ads_del_dn(ADS_STRUCT *ads, char *del_dn)
1563 int ret;
1564 char *utf8_dn = NULL;
1565 size_t converted_size;
1566 if (!push_utf8_allocate(&utf8_dn, del_dn, &converted_size)) {
1567 DEBUG(1, ("ads_del_dn: push_utf8_allocate failed!"));
1568 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1571 ret = ldap_delete_s(ads->ldap.ld, utf8_dn);
1572 SAFE_FREE(utf8_dn);
1573 return ADS_ERROR(ret);
1577 * Build an org unit string
1578 * if org unit is Computers or blank then assume a container, otherwise
1579 * assume a / separated list of organisational units.
1580 * jmcd: '\' is now used for escapes so certain chars can be in the ou (e.g. #)
1581 * @param ads connection to ads server
1582 * @param org_unit Organizational unit
1583 * @return org unit string - caller must free
1585 char *ads_ou_string(ADS_STRUCT *ads, const char *org_unit)
1587 char *ret = NULL;
1589 if (!org_unit || !*org_unit) {
1591 ret = ads_default_ou_string(ads, WELL_KNOWN_GUID_COMPUTERS);
1593 /* samba4 might not yet respond to a wellknownobject-query */
1594 return ret ? ret : SMB_STRDUP("cn=Computers");
1597 if (strequal(org_unit, "Computers")) {
1598 return SMB_STRDUP("cn=Computers");
1601 /* jmcd: removed "\\" from the separation chars, because it is
1602 needed as an escape for chars like '#' which are valid in an
1603 OU name */
1604 return ads_build_path(org_unit, "/", "ou=", 1);
1608 * Get a org unit string for a well-known GUID
1609 * @param ads connection to ads server
1610 * @param wknguid Well known GUID
1611 * @return org unit string - caller must free
1613 char *ads_default_ou_string(ADS_STRUCT *ads, const char *wknguid)
1615 ADS_STATUS status;
1616 LDAPMessage *res = NULL;
1617 char *base, *wkn_dn = NULL, *ret = NULL, **wkn_dn_exp = NULL,
1618 **bind_dn_exp = NULL;
1619 const char *attrs[] = {"distinguishedName", NULL};
1620 int new_ln, wkn_ln, bind_ln, i;
1622 if (wknguid == NULL) {
1623 return NULL;
1626 if (asprintf(&base, "<WKGUID=%s,%s>", wknguid, ads->config.bind_path ) == -1) {
1627 DEBUG(1, ("asprintf failed!\n"));
1628 return NULL;
1631 status = ads_search_dn(ads, &res, base, attrs);
1632 if (!ADS_ERR_OK(status)) {
1633 DEBUG(1,("Failed while searching for: %s\n", base));
1634 goto out;
1637 if (ads_count_replies(ads, res) != 1) {
1638 goto out;
1641 /* substitute the bind-path from the well-known-guid-search result */
1642 wkn_dn = ads_get_dn(ads, talloc_tos(), res);
1643 if (!wkn_dn) {
1644 goto out;
1647 wkn_dn_exp = ldap_explode_dn(wkn_dn, 0);
1648 if (!wkn_dn_exp) {
1649 goto out;
1652 bind_dn_exp = ldap_explode_dn(ads->config.bind_path, 0);
1653 if (!bind_dn_exp) {
1654 goto out;
1657 for (wkn_ln=0; wkn_dn_exp[wkn_ln]; wkn_ln++)
1659 for (bind_ln=0; bind_dn_exp[bind_ln]; bind_ln++)
1662 new_ln = wkn_ln - bind_ln;
1664 ret = SMB_STRDUP(wkn_dn_exp[0]);
1665 if (!ret) {
1666 goto out;
1669 for (i=1; i < new_ln; i++) {
1670 char *s = NULL;
1672 if (asprintf(&s, "%s,%s", ret, wkn_dn_exp[i]) == -1) {
1673 SAFE_FREE(ret);
1674 goto out;
1677 SAFE_FREE(ret);
1678 ret = SMB_STRDUP(s);
1679 free(s);
1680 if (!ret) {
1681 goto out;
1685 out:
1686 SAFE_FREE(base);
1687 ads_msgfree(ads, res);
1688 TALLOC_FREE(wkn_dn);
1689 if (wkn_dn_exp) {
1690 ldap_value_free(wkn_dn_exp);
1692 if (bind_dn_exp) {
1693 ldap_value_free(bind_dn_exp);
1696 return ret;
1700 * Adds (appends) an item to an attribute array, rather then
1701 * replacing the whole list
1702 * @param ctx An initialized TALLOC_CTX
1703 * @param mods An initialized ADS_MODLIST
1704 * @param name name of the ldap attribute to append to
1705 * @param vals an array of values to add
1706 * @return status of addition
1709 ADS_STATUS ads_add_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1710 const char *name, const char **vals)
1712 return ads_modlist_add(ctx, mods, LDAP_MOD_ADD, name,
1713 (const void *) vals);
1717 * Determines the an account's current KVNO via an LDAP lookup
1718 * @param ads An initialized ADS_STRUCT
1719 * @param account_name the NT samaccountname.
1720 * @return the kvno for the account, or -1 in case of a failure.
1723 uint32 ads_get_kvno(ADS_STRUCT *ads, const char *account_name)
1725 LDAPMessage *res = NULL;
1726 uint32 kvno = (uint32)-1; /* -1 indicates a failure */
1727 char *filter;
1728 const char *attrs[] = {"msDS-KeyVersionNumber", NULL};
1729 char *dn_string = NULL;
1730 ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS);
1732 DEBUG(5,("ads_get_kvno: Searching for account %s\n", account_name));
1733 if (asprintf(&filter, "(samAccountName=%s)", account_name) == -1) {
1734 return kvno;
1736 ret = ads_search(ads, &res, filter, attrs);
1737 SAFE_FREE(filter);
1738 if (!ADS_ERR_OK(ret) || (ads_count_replies(ads, res) != 1)) {
1739 DEBUG(1,("ads_get_kvno: Account for %s not found.\n", account_name));
1740 ads_msgfree(ads, res);
1741 return kvno;
1744 dn_string = ads_get_dn(ads, talloc_tos(), res);
1745 if (!dn_string) {
1746 DEBUG(0,("ads_get_kvno: out of memory.\n"));
1747 ads_msgfree(ads, res);
1748 return kvno;
1750 DEBUG(5,("ads_get_kvno: Using: %s\n", dn_string));
1751 TALLOC_FREE(dn_string);
1753 /* ---------------------------------------------------------
1754 * 0 is returned as a default KVNO from this point on...
1755 * This is done because Windows 2000 does not support key
1756 * version numbers. Chances are that a failure in the next
1757 * step is simply due to Windows 2000 being used for a
1758 * domain controller. */
1759 kvno = 0;
1761 if (!ads_pull_uint32(ads, res, "msDS-KeyVersionNumber", &kvno)) {
1762 DEBUG(3,("ads_get_kvno: Error Determining KVNO!\n"));
1763 DEBUG(3,("ads_get_kvno: Windows 2000 does not support KVNO's, so this may be normal.\n"));
1764 ads_msgfree(ads, res);
1765 return kvno;
1768 /* Success */
1769 DEBUG(5,("ads_get_kvno: Looked Up KVNO of: %d\n", kvno));
1770 ads_msgfree(ads, res);
1771 return kvno;
1775 * Determines the computer account's current KVNO via an LDAP lookup
1776 * @param ads An initialized ADS_STRUCT
1777 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1778 * @return the kvno for the computer account, or -1 in case of a failure.
1781 uint32_t ads_get_machine_kvno(ADS_STRUCT *ads, const char *machine_name)
1783 char *computer_account = NULL;
1784 uint32_t kvno = -1;
1786 if (asprintf(&computer_account, "%s$", machine_name) < 0) {
1787 return kvno;
1790 kvno = ads_get_kvno(ads, computer_account);
1791 free(computer_account);
1793 return kvno;
1797 * This clears out all registered spn's for a given hostname
1798 * @param ads An initilaized ADS_STRUCT
1799 * @param machine_name the NetBIOS name of the computer.
1800 * @return 0 upon success, non-zero otherwise.
1803 ADS_STATUS ads_clear_service_principal_names(ADS_STRUCT *ads, const char *machine_name)
1805 TALLOC_CTX *ctx;
1806 LDAPMessage *res = NULL;
1807 ADS_MODLIST mods;
1808 const char *servicePrincipalName[1] = {NULL};
1809 ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS);
1810 char *dn_string = NULL;
1812 ret = ads_find_machine_acct(ads, &res, machine_name);
1813 if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
1814 DEBUG(5,("ads_clear_service_principal_names: WARNING: Host Account for %s not found... skipping operation.\n", machine_name));
1815 DEBUG(5,("ads_clear_service_principal_names: WARNING: Service Principals for %s have NOT been cleared.\n", machine_name));
1816 ads_msgfree(ads, res);
1817 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1820 DEBUG(5,("ads_clear_service_principal_names: Host account for %s found\n", machine_name));
1821 ctx = talloc_init("ads_clear_service_principal_names");
1822 if (!ctx) {
1823 ads_msgfree(ads, res);
1824 return ADS_ERROR(LDAP_NO_MEMORY);
1827 if (!(mods = ads_init_mods(ctx))) {
1828 talloc_destroy(ctx);
1829 ads_msgfree(ads, res);
1830 return ADS_ERROR(LDAP_NO_MEMORY);
1832 ret = ads_mod_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1833 if (!ADS_ERR_OK(ret)) {
1834 DEBUG(1,("ads_clear_service_principal_names: Error creating strlist.\n"));
1835 ads_msgfree(ads, res);
1836 talloc_destroy(ctx);
1837 return ret;
1839 dn_string = ads_get_dn(ads, talloc_tos(), res);
1840 if (!dn_string) {
1841 talloc_destroy(ctx);
1842 ads_msgfree(ads, res);
1843 return ADS_ERROR(LDAP_NO_MEMORY);
1845 ret = ads_gen_mod(ads, dn_string, mods);
1846 TALLOC_FREE(dn_string);
1847 if (!ADS_ERR_OK(ret)) {
1848 DEBUG(1,("ads_clear_service_principal_names: Error: Updating Service Principals for machine %s in LDAP\n",
1849 machine_name));
1850 ads_msgfree(ads, res);
1851 talloc_destroy(ctx);
1852 return ret;
1855 ads_msgfree(ads, res);
1856 talloc_destroy(ctx);
1857 return ret;
1861 * This adds a service principal name to an existing computer account
1862 * (found by hostname) in AD.
1863 * @param ads An initialized ADS_STRUCT
1864 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1865 * @param my_fqdn The fully qualified DNS name of the machine
1866 * @param spn A string of the service principal to add, i.e. 'host'
1867 * @return 0 upon sucess, or non-zero if a failure occurs
1870 ADS_STATUS ads_add_service_principal_name(ADS_STRUCT *ads, const char *machine_name,
1871 const char *my_fqdn, const char *spn)
1873 ADS_STATUS ret;
1874 TALLOC_CTX *ctx;
1875 LDAPMessage *res = NULL;
1876 char *psp1, *psp2;
1877 ADS_MODLIST mods;
1878 char *dn_string = NULL;
1879 const char *servicePrincipalName[3] = {NULL, NULL, NULL};
1881 ret = ads_find_machine_acct(ads, &res, machine_name);
1882 if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
1883 DEBUG(1,("ads_add_service_principal_name: WARNING: Host Account for %s not found... skipping operation.\n",
1884 machine_name));
1885 DEBUG(1,("ads_add_service_principal_name: WARNING: Service Principal '%s/%s@%s' has NOT been added.\n",
1886 spn, machine_name, ads->config.realm));
1887 ads_msgfree(ads, res);
1888 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1891 DEBUG(1,("ads_add_service_principal_name: Host account for %s found\n", machine_name));
1892 if (!(ctx = talloc_init("ads_add_service_principal_name"))) {
1893 ads_msgfree(ads, res);
1894 return ADS_ERROR(LDAP_NO_MEMORY);
1897 /* add short name spn */
1899 if ( (psp1 = talloc_asprintf(ctx, "%s/%s", spn, machine_name)) == NULL ) {
1900 talloc_destroy(ctx);
1901 ads_msgfree(ads, res);
1902 return ADS_ERROR(LDAP_NO_MEMORY);
1904 strupper_m(psp1);
1905 strlower_m(&psp1[strlen(spn)]);
1906 servicePrincipalName[0] = psp1;
1908 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n",
1909 psp1, machine_name));
1912 /* add fully qualified spn */
1914 if ( (psp2 = talloc_asprintf(ctx, "%s/%s", spn, my_fqdn)) == NULL ) {
1915 ret = ADS_ERROR(LDAP_NO_MEMORY);
1916 goto out;
1918 strupper_m(psp2);
1919 strlower_m(&psp2[strlen(spn)]);
1920 servicePrincipalName[1] = psp2;
1922 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n",
1923 psp2, machine_name));
1925 if ( (mods = ads_init_mods(ctx)) == NULL ) {
1926 ret = ADS_ERROR(LDAP_NO_MEMORY);
1927 goto out;
1930 ret = ads_add_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1931 if (!ADS_ERR_OK(ret)) {
1932 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
1933 goto out;
1936 if ( (dn_string = ads_get_dn(ads, ctx, res)) == NULL ) {
1937 ret = ADS_ERROR(LDAP_NO_MEMORY);
1938 goto out;
1941 ret = ads_gen_mod(ads, dn_string, mods);
1942 if (!ADS_ERR_OK(ret)) {
1943 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
1944 goto out;
1947 out:
1948 TALLOC_FREE( ctx );
1949 ads_msgfree(ads, res);
1950 return ret;
1954 * adds a machine account to the ADS server
1955 * @param ads An intialized ADS_STRUCT
1956 * @param machine_name - the NetBIOS machine name of this account.
1957 * @param account_type A number indicating the type of account to create
1958 * @param org_unit The LDAP path in which to place this account
1959 * @return 0 upon success, or non-zero otherwise
1962 ADS_STATUS ads_create_machine_acct(ADS_STRUCT *ads, const char *machine_name,
1963 const char *org_unit)
1965 ADS_STATUS ret;
1966 char *samAccountName, *controlstr;
1967 TALLOC_CTX *ctx;
1968 ADS_MODLIST mods;
1969 char *machine_escaped = NULL;
1970 char *new_dn;
1971 const char *objectClass[] = {"top", "person", "organizationalPerson",
1972 "user", "computer", NULL};
1973 LDAPMessage *res = NULL;
1974 uint32 acct_control = ( UF_WORKSTATION_TRUST_ACCOUNT |\
1975 UF_DONT_EXPIRE_PASSWD |\
1976 UF_ACCOUNTDISABLE );
1978 if (!(ctx = talloc_init("ads_add_machine_acct")))
1979 return ADS_ERROR(LDAP_NO_MEMORY);
1981 ret = ADS_ERROR(LDAP_NO_MEMORY);
1983 machine_escaped = escape_rdn_val_string_alloc(machine_name);
1984 if (!machine_escaped) {
1985 goto done;
1988 new_dn = talloc_asprintf(ctx, "cn=%s,%s", machine_escaped, org_unit);
1989 samAccountName = talloc_asprintf(ctx, "%s$", machine_name);
1991 if ( !new_dn || !samAccountName ) {
1992 goto done;
1995 #ifndef ENCTYPE_ARCFOUR_HMAC
1996 acct_control |= UF_USE_DES_KEY_ONLY;
1997 #endif
1999 if (!(controlstr = talloc_asprintf(ctx, "%u", acct_control))) {
2000 goto done;
2003 if (!(mods = ads_init_mods(ctx))) {
2004 goto done;
2007 ads_mod_str(ctx, &mods, "cn", machine_name);
2008 ads_mod_str(ctx, &mods, "sAMAccountName", samAccountName);
2009 ads_mod_strlist(ctx, &mods, "objectClass", objectClass);
2010 ads_mod_str(ctx, &mods, "userAccountControl", controlstr);
2012 ret = ads_gen_add(ads, new_dn, mods);
2014 done:
2015 SAFE_FREE(machine_escaped);
2016 ads_msgfree(ads, res);
2017 talloc_destroy(ctx);
2019 return ret;
2023 * move a machine account to another OU on the ADS server
2024 * @param ads - An intialized ADS_STRUCT
2025 * @param machine_name - the NetBIOS machine name of this account.
2026 * @param org_unit - The LDAP path in which to place this account
2027 * @param moved - whether we moved the machine account (optional)
2028 * @return 0 upon success, or non-zero otherwise
2031 ADS_STATUS ads_move_machine_acct(ADS_STRUCT *ads, const char *machine_name,
2032 const char *org_unit, bool *moved)
2034 ADS_STATUS rc;
2035 int ldap_status;
2036 LDAPMessage *res = NULL;
2037 char *filter = NULL;
2038 char *computer_dn = NULL;
2039 char *parent_dn;
2040 char *computer_rdn = NULL;
2041 bool need_move = False;
2043 if (asprintf(&filter, "(samAccountName=%s$)", machine_name) == -1) {
2044 rc = ADS_ERROR(LDAP_NO_MEMORY);
2045 goto done;
2048 /* Find pre-existing machine */
2049 rc = ads_search(ads, &res, filter, NULL);
2050 if (!ADS_ERR_OK(rc)) {
2051 goto done;
2054 computer_dn = ads_get_dn(ads, talloc_tos(), res);
2055 if (!computer_dn) {
2056 rc = ADS_ERROR(LDAP_NO_MEMORY);
2057 goto done;
2060 parent_dn = ads_parent_dn(computer_dn);
2061 if (strequal(parent_dn, org_unit)) {
2062 goto done;
2065 need_move = True;
2067 if (asprintf(&computer_rdn, "CN=%s", machine_name) == -1) {
2068 rc = ADS_ERROR(LDAP_NO_MEMORY);
2069 goto done;
2072 ldap_status = ldap_rename_s(ads->ldap.ld, computer_dn, computer_rdn,
2073 org_unit, 1, NULL, NULL);
2074 rc = ADS_ERROR(ldap_status);
2076 done:
2077 ads_msgfree(ads, res);
2078 SAFE_FREE(filter);
2079 TALLOC_FREE(computer_dn);
2080 SAFE_FREE(computer_rdn);
2082 if (!ADS_ERR_OK(rc)) {
2083 need_move = False;
2086 if (moved) {
2087 *moved = need_move;
2090 return rc;
2094 dump a binary result from ldap
2096 static void dump_binary(ADS_STRUCT *ads, const char *field, struct berval **values)
2098 int i, j;
2099 for (i=0; values[i]; i++) {
2100 printf("%s: ", field);
2101 for (j=0; j<values[i]->bv_len; j++) {
2102 printf("%02X", (unsigned char)values[i]->bv_val[j]);
2104 printf("\n");
2108 static void dump_guid(ADS_STRUCT *ads, const char *field, struct berval **values)
2110 int i;
2111 for (i=0; values[i]; i++) {
2113 UUID_FLAT guid;
2114 struct GUID tmp;
2116 memcpy(guid.info, values[i]->bv_val, sizeof(guid.info));
2117 smb_uuid_unpack(guid, &tmp);
2118 printf("%s: %s\n", field, GUID_string(talloc_tos(), &tmp));
2123 dump a sid result from ldap
2125 static void dump_sid(ADS_STRUCT *ads, const char *field, struct berval **values)
2127 int i;
2128 for (i=0; values[i]; i++) {
2129 DOM_SID sid;
2130 fstring tmp;
2131 sid_parse(values[i]->bv_val, values[i]->bv_len, &sid);
2132 printf("%s: %s\n", field, sid_to_fstring(tmp, &sid));
2137 dump ntSecurityDescriptor
2139 static void dump_sd(ADS_STRUCT *ads, const char *filed, struct berval **values)
2141 TALLOC_CTX *frame = talloc_stackframe();
2142 struct security_descriptor *psd;
2143 NTSTATUS status;
2145 status = unmarshall_sec_desc(talloc_tos(), (uint8 *)values[0]->bv_val,
2146 values[0]->bv_len, &psd);
2147 if (!NT_STATUS_IS_OK(status)) {
2148 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
2149 nt_errstr(status)));
2150 TALLOC_FREE(frame);
2151 return;
2154 if (psd) {
2155 ads_disp_sd(ads, talloc_tos(), psd);
2158 TALLOC_FREE(frame);
2162 dump a string result from ldap
2164 static void dump_string(const char *field, char **values)
2166 int i;
2167 for (i=0; values[i]; i++) {
2168 printf("%s: %s\n", field, values[i]);
2173 dump a field from LDAP on stdout
2174 used for debugging
2177 static bool ads_dump_field(ADS_STRUCT *ads, char *field, void **values, void *data_area)
2179 const struct {
2180 const char *name;
2181 bool string;
2182 void (*handler)(ADS_STRUCT *, const char *, struct berval **);
2183 } handlers[] = {
2184 {"objectGUID", False, dump_guid},
2185 {"netbootGUID", False, dump_guid},
2186 {"nTSecurityDescriptor", False, dump_sd},
2187 {"dnsRecord", False, dump_binary},
2188 {"objectSid", False, dump_sid},
2189 {"tokenGroups", False, dump_sid},
2190 {"tokenGroupsNoGCAcceptable", False, dump_sid},
2191 {"tokengroupsGlobalandUniversal", False, dump_sid},
2192 {"mS-DS-CreatorSID", False, dump_sid},
2193 {"msExchMailboxGuid", False, dump_guid},
2194 {NULL, True, NULL}
2196 int i;
2198 if (!field) { /* must be end of an entry */
2199 printf("\n");
2200 return False;
2203 for (i=0; handlers[i].name; i++) {
2204 if (StrCaseCmp(handlers[i].name, field) == 0) {
2205 if (!values) /* first time, indicate string or not */
2206 return handlers[i].string;
2207 handlers[i].handler(ads, field, (struct berval **) values);
2208 break;
2211 if (!handlers[i].name) {
2212 if (!values) /* first time, indicate string conversion */
2213 return True;
2214 dump_string(field, (char **)values);
2216 return False;
2220 * Dump a result from LDAP on stdout
2221 * used for debugging
2222 * @param ads connection to ads server
2223 * @param res Results to dump
2226 void ads_dump(ADS_STRUCT *ads, LDAPMessage *res)
2228 ads_process_results(ads, res, ads_dump_field, NULL);
2232 * Walk through results, calling a function for each entry found.
2233 * The function receives a field name, a berval * array of values,
2234 * and a data area passed through from the start. The function is
2235 * called once with null for field and values at the end of each
2236 * entry.
2237 * @param ads connection to ads server
2238 * @param res Results to process
2239 * @param fn Function for processing each result
2240 * @param data_area user-defined area to pass to function
2242 void ads_process_results(ADS_STRUCT *ads, LDAPMessage *res,
2243 bool (*fn)(ADS_STRUCT *, char *, void **, void *),
2244 void *data_area)
2246 LDAPMessage *msg;
2247 TALLOC_CTX *ctx;
2248 size_t converted_size;
2250 if (!(ctx = talloc_init("ads_process_results")))
2251 return;
2253 for (msg = ads_first_entry(ads, res); msg;
2254 msg = ads_next_entry(ads, msg)) {
2255 char *utf8_field;
2256 BerElement *b;
2258 for (utf8_field=ldap_first_attribute(ads->ldap.ld,
2259 (LDAPMessage *)msg,&b);
2260 utf8_field;
2261 utf8_field=ldap_next_attribute(ads->ldap.ld,
2262 (LDAPMessage *)msg,b)) {
2263 struct berval **ber_vals;
2264 char **str_vals, **utf8_vals;
2265 char *field;
2266 bool string;
2268 if (!pull_utf8_talloc(ctx, &field, utf8_field,
2269 &converted_size))
2271 DEBUG(0,("ads_process_results: "
2272 "pull_utf8_talloc failed: %s",
2273 strerror(errno)));
2276 string = fn(ads, field, NULL, data_area);
2278 if (string) {
2279 utf8_vals = ldap_get_values(ads->ldap.ld,
2280 (LDAPMessage *)msg, field);
2281 str_vals = ads_pull_strvals(ctx,
2282 (const char **) utf8_vals);
2283 fn(ads, field, (void **) str_vals, data_area);
2284 ldap_value_free(utf8_vals);
2285 } else {
2286 ber_vals = ldap_get_values_len(ads->ldap.ld,
2287 (LDAPMessage *)msg, field);
2288 fn(ads, field, (void **) ber_vals, data_area);
2290 ldap_value_free_len(ber_vals);
2292 ldap_memfree(utf8_field);
2294 ber_free(b, 0);
2295 talloc_free_children(ctx);
2296 fn(ads, NULL, NULL, data_area); /* completed an entry */
2299 talloc_destroy(ctx);
2303 * count how many replies are in a LDAPMessage
2304 * @param ads connection to ads server
2305 * @param res Results to count
2306 * @return number of replies
2308 int ads_count_replies(ADS_STRUCT *ads, void *res)
2310 return ldap_count_entries(ads->ldap.ld, (LDAPMessage *)res);
2314 * pull the first entry from a ADS result
2315 * @param ads connection to ads server
2316 * @param res Results of search
2317 * @return first entry from result
2319 LDAPMessage *ads_first_entry(ADS_STRUCT *ads, LDAPMessage *res)
2321 return ldap_first_entry(ads->ldap.ld, res);
2325 * pull the next entry from a ADS result
2326 * @param ads connection to ads server
2327 * @param res Results of search
2328 * @return next entry from result
2330 LDAPMessage *ads_next_entry(ADS_STRUCT *ads, LDAPMessage *res)
2332 return ldap_next_entry(ads->ldap.ld, res);
2336 * pull the first message from a ADS result
2337 * @param ads connection to ads server
2338 * @param res Results of search
2339 * @return first message from result
2341 LDAPMessage *ads_first_message(ADS_STRUCT *ads, LDAPMessage *res)
2343 return ldap_first_message(ads->ldap.ld, res);
2347 * pull the next message from a ADS result
2348 * @param ads connection to ads server
2349 * @param res Results of search
2350 * @return next message from result
2352 LDAPMessage *ads_next_message(ADS_STRUCT *ads, LDAPMessage *res)
2354 return ldap_next_message(ads->ldap.ld, res);
2358 * pull a single string from a ADS result
2359 * @param ads connection to ads server
2360 * @param mem_ctx TALLOC_CTX to use for allocating result string
2361 * @param msg Results of search
2362 * @param field Attribute to retrieve
2363 * @return Result string in talloc context
2365 char *ads_pull_string(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, LDAPMessage *msg,
2366 const char *field)
2368 char **values;
2369 char *ret = NULL;
2370 char *ux_string;
2371 size_t converted_size;
2373 values = ldap_get_values(ads->ldap.ld, msg, field);
2374 if (!values)
2375 return NULL;
2377 if (values[0] && pull_utf8_talloc(mem_ctx, &ux_string, values[0],
2378 &converted_size))
2380 ret = ux_string;
2382 ldap_value_free(values);
2383 return ret;
2387 * pull an array of strings from a ADS result
2388 * @param ads connection to ads server
2389 * @param mem_ctx TALLOC_CTX to use for allocating result string
2390 * @param msg Results of search
2391 * @param field Attribute to retrieve
2392 * @return Result strings in talloc context
2394 char **ads_pull_strings(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2395 LDAPMessage *msg, const char *field,
2396 size_t *num_values)
2398 char **values;
2399 char **ret = NULL;
2400 int i;
2401 size_t converted_size;
2403 values = ldap_get_values(ads->ldap.ld, msg, field);
2404 if (!values)
2405 return NULL;
2407 *num_values = ldap_count_values(values);
2409 ret = TALLOC_ARRAY(mem_ctx, char *, *num_values + 1);
2410 if (!ret) {
2411 ldap_value_free(values);
2412 return NULL;
2415 for (i=0;i<*num_values;i++) {
2416 if (!pull_utf8_talloc(mem_ctx, &ret[i], values[i],
2417 &converted_size))
2419 ldap_value_free(values);
2420 return NULL;
2423 ret[i] = NULL;
2425 ldap_value_free(values);
2426 return ret;
2430 * pull an array of strings from a ADS result
2431 * (handle large multivalue attributes with range retrieval)
2432 * @param ads connection to ads server
2433 * @param mem_ctx TALLOC_CTX to use for allocating result string
2434 * @param msg Results of search
2435 * @param field Attribute to retrieve
2436 * @param current_strings strings returned by a previous call to this function
2437 * @param next_attribute The next query should ask for this attribute
2438 * @param num_values How many values did we get this time?
2439 * @param more_values Are there more values to get?
2440 * @return Result strings in talloc context
2442 char **ads_pull_strings_range(ADS_STRUCT *ads,
2443 TALLOC_CTX *mem_ctx,
2444 LDAPMessage *msg, const char *field,
2445 char **current_strings,
2446 const char **next_attribute,
2447 size_t *num_strings,
2448 bool *more_strings)
2450 char *attr;
2451 char *expected_range_attrib, *range_attr;
2452 BerElement *ptr = NULL;
2453 char **strings;
2454 char **new_strings;
2455 size_t num_new_strings;
2456 unsigned long int range_start;
2457 unsigned long int range_end;
2459 /* we might have been given the whole lot anyway */
2460 if ((strings = ads_pull_strings(ads, mem_ctx, msg, field, num_strings))) {
2461 *more_strings = False;
2462 return strings;
2465 expected_range_attrib = talloc_asprintf(mem_ctx, "%s;Range=", field);
2467 /* look for Range result */
2468 for (attr = ldap_first_attribute(ads->ldap.ld, (LDAPMessage *)msg, &ptr);
2469 attr;
2470 attr = ldap_next_attribute(ads->ldap.ld, (LDAPMessage *)msg, ptr)) {
2471 /* we ignore the fact that this is utf8, as all attributes are ascii... */
2472 if (strnequal(attr, expected_range_attrib, strlen(expected_range_attrib))) {
2473 range_attr = attr;
2474 break;
2476 ldap_memfree(attr);
2478 if (!attr) {
2479 ber_free(ptr, 0);
2480 /* nothing here - this field is just empty */
2481 *more_strings = False;
2482 return NULL;
2485 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-%lu",
2486 &range_start, &range_end) == 2) {
2487 *more_strings = True;
2488 } else {
2489 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-*",
2490 &range_start) == 1) {
2491 *more_strings = False;
2492 } else {
2493 DEBUG(1, ("ads_pull_strings_range: Cannot parse Range attriubte (%s)\n",
2494 range_attr));
2495 ldap_memfree(range_attr);
2496 *more_strings = False;
2497 return NULL;
2501 if ((*num_strings) != range_start) {
2502 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) doesn't start at %u, but at %lu"
2503 " - aborting range retreival\n",
2504 range_attr, (unsigned int)(*num_strings) + 1, range_start));
2505 ldap_memfree(range_attr);
2506 *more_strings = False;
2507 return NULL;
2510 new_strings = ads_pull_strings(ads, mem_ctx, msg, range_attr, &num_new_strings);
2512 if (*more_strings && ((*num_strings + num_new_strings) != (range_end + 1))) {
2513 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) tells us we have %lu "
2514 "strings in this bunch, but we only got %lu - aborting range retreival\n",
2515 range_attr, (unsigned long int)range_end - range_start + 1,
2516 (unsigned long int)num_new_strings));
2517 ldap_memfree(range_attr);
2518 *more_strings = False;
2519 return NULL;
2522 strings = TALLOC_REALLOC_ARRAY(mem_ctx, current_strings, char *,
2523 *num_strings + num_new_strings);
2525 if (strings == NULL) {
2526 ldap_memfree(range_attr);
2527 *more_strings = False;
2528 return NULL;
2531 if (new_strings && num_new_strings) {
2532 memcpy(&strings[*num_strings], new_strings,
2533 sizeof(*new_strings) * num_new_strings);
2536 (*num_strings) += num_new_strings;
2538 if (*more_strings) {
2539 *next_attribute = talloc_asprintf(mem_ctx,
2540 "%s;range=%d-*",
2541 field,
2542 (int)*num_strings);
2544 if (!*next_attribute) {
2545 DEBUG(1, ("talloc_asprintf for next attribute failed!\n"));
2546 ldap_memfree(range_attr);
2547 *more_strings = False;
2548 return NULL;
2552 ldap_memfree(range_attr);
2554 return strings;
2558 * pull a single uint32 from a ADS result
2559 * @param ads connection to ads server
2560 * @param msg Results of search
2561 * @param field Attribute to retrieve
2562 * @param v Pointer to int to store result
2563 * @return boolean inidicating success
2565 bool ads_pull_uint32(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
2566 uint32 *v)
2568 char **values;
2570 values = ldap_get_values(ads->ldap.ld, msg, field);
2571 if (!values)
2572 return False;
2573 if (!values[0]) {
2574 ldap_value_free(values);
2575 return False;
2578 *v = atoi(values[0]);
2579 ldap_value_free(values);
2580 return True;
2584 * pull a single objectGUID from an ADS result
2585 * @param ads connection to ADS server
2586 * @param msg results of search
2587 * @param guid 37-byte area to receive text guid
2588 * @return boolean indicating success
2590 bool ads_pull_guid(ADS_STRUCT *ads, LDAPMessage *msg, struct GUID *guid)
2592 char **values;
2593 UUID_FLAT flat_guid;
2595 values = ldap_get_values(ads->ldap.ld, msg, "objectGUID");
2596 if (!values)
2597 return False;
2599 if (values[0]) {
2600 memcpy(&flat_guid.info, values[0], sizeof(UUID_FLAT));
2601 smb_uuid_unpack(flat_guid, guid);
2602 ldap_value_free(values);
2603 return True;
2605 ldap_value_free(values);
2606 return False;
2612 * pull a single DOM_SID from a ADS result
2613 * @param ads connection to ads server
2614 * @param msg Results of search
2615 * @param field Attribute to retrieve
2616 * @param sid Pointer to sid to store result
2617 * @return boolean inidicating success
2619 bool ads_pull_sid(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
2620 DOM_SID *sid)
2622 struct berval **values;
2623 bool ret = False;
2625 values = ldap_get_values_len(ads->ldap.ld, msg, field);
2627 if (!values)
2628 return False;
2630 if (values[0])
2631 ret = sid_parse(values[0]->bv_val, values[0]->bv_len, sid);
2633 ldap_value_free_len(values);
2634 return ret;
2638 * pull an array of DOM_SIDs from a ADS result
2639 * @param ads connection to ads server
2640 * @param mem_ctx TALLOC_CTX for allocating sid array
2641 * @param msg Results of search
2642 * @param field Attribute to retrieve
2643 * @param sids pointer to sid array to allocate
2644 * @return the count of SIDs pulled
2646 int ads_pull_sids(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2647 LDAPMessage *msg, const char *field, DOM_SID **sids)
2649 struct berval **values;
2650 bool ret;
2651 int count, i;
2653 values = ldap_get_values_len(ads->ldap.ld, msg, field);
2655 if (!values)
2656 return 0;
2658 for (i=0; values[i]; i++)
2659 /* nop */ ;
2661 if (i) {
2662 (*sids) = TALLOC_ARRAY(mem_ctx, DOM_SID, i);
2663 if (!(*sids)) {
2664 ldap_value_free_len(values);
2665 return 0;
2667 } else {
2668 (*sids) = NULL;
2671 count = 0;
2672 for (i=0; values[i]; i++) {
2673 ret = sid_parse(values[i]->bv_val, values[i]->bv_len, &(*sids)[count]);
2674 if (ret) {
2675 DEBUG(10, ("pulling SID: %s\n",
2676 sid_string_dbg(&(*sids)[count])));
2677 count++;
2681 ldap_value_free_len(values);
2682 return count;
2686 * pull a SEC_DESC from a ADS result
2687 * @param ads connection to ads server
2688 * @param mem_ctx TALLOC_CTX for allocating sid array
2689 * @param msg Results of search
2690 * @param field Attribute to retrieve
2691 * @param sd Pointer to *SEC_DESC to store result (talloc()ed)
2692 * @return boolean inidicating success
2694 bool ads_pull_sd(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2695 LDAPMessage *msg, const char *field, SEC_DESC **sd)
2697 struct berval **values;
2698 bool ret = true;
2700 values = ldap_get_values_len(ads->ldap.ld, msg, field);
2702 if (!values) return false;
2704 if (values[0]) {
2705 NTSTATUS status;
2706 status = unmarshall_sec_desc(mem_ctx,
2707 (uint8 *)values[0]->bv_val,
2708 values[0]->bv_len, sd);
2709 if (!NT_STATUS_IS_OK(status)) {
2710 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
2711 nt_errstr(status)));
2712 ret = false;
2716 ldap_value_free_len(values);
2717 return ret;
2721 * in order to support usernames longer than 21 characters we need to
2722 * use both the sAMAccountName and the userPrincipalName attributes
2723 * It seems that not all users have the userPrincipalName attribute set
2725 * @param ads connection to ads server
2726 * @param mem_ctx TALLOC_CTX for allocating sid array
2727 * @param msg Results of search
2728 * @return the username
2730 char *ads_pull_username(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2731 LDAPMessage *msg)
2733 #if 0 /* JERRY */
2734 char *ret, *p;
2736 /* lookup_name() only works on the sAMAccountName to
2737 returning the username portion of userPrincipalName
2738 breaks winbindd_getpwnam() */
2740 ret = ads_pull_string(ads, mem_ctx, msg, "userPrincipalName");
2741 if (ret && (p = strchr_m(ret, '@'))) {
2742 *p = 0;
2743 return ret;
2745 #endif
2746 return ads_pull_string(ads, mem_ctx, msg, "sAMAccountName");
2751 * find the update serial number - this is the core of the ldap cache
2752 * @param ads connection to ads server
2753 * @param ads connection to ADS server
2754 * @param usn Pointer to retrieved update serial number
2755 * @return status of search
2757 ADS_STATUS ads_USN(ADS_STRUCT *ads, uint32 *usn)
2759 const char *attrs[] = {"highestCommittedUSN", NULL};
2760 ADS_STATUS status;
2761 LDAPMessage *res;
2763 status = ads_do_search_retry(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2764 if (!ADS_ERR_OK(status))
2765 return status;
2767 if (ads_count_replies(ads, res) != 1) {
2768 ads_msgfree(ads, res);
2769 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2772 if (!ads_pull_uint32(ads, res, "highestCommittedUSN", usn)) {
2773 ads_msgfree(ads, res);
2774 return ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
2777 ads_msgfree(ads, res);
2778 return ADS_SUCCESS;
2781 /* parse a ADS timestring - typical string is
2782 '20020917091222.0Z0' which means 09:12.22 17th September
2783 2002, timezone 0 */
2784 static time_t ads_parse_time(const char *str)
2786 struct tm tm;
2788 ZERO_STRUCT(tm);
2790 if (sscanf(str, "%4d%2d%2d%2d%2d%2d",
2791 &tm.tm_year, &tm.tm_mon, &tm.tm_mday,
2792 &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) {
2793 return 0;
2795 tm.tm_year -= 1900;
2796 tm.tm_mon -= 1;
2798 return timegm(&tm);
2801 /********************************************************************
2802 ********************************************************************/
2804 ADS_STATUS ads_current_time(ADS_STRUCT *ads)
2806 const char *attrs[] = {"currentTime", NULL};
2807 ADS_STATUS status;
2808 LDAPMessage *res;
2809 char *timestr;
2810 TALLOC_CTX *ctx;
2811 ADS_STRUCT *ads_s = ads;
2813 if (!(ctx = talloc_init("ads_current_time"))) {
2814 return ADS_ERROR(LDAP_NO_MEMORY);
2817 /* establish a new ldap tcp session if necessary */
2819 if ( !ads->ldap.ld ) {
2820 if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup,
2821 ads->server.ldap_server )) == NULL )
2823 goto done;
2825 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
2826 status = ads_connect( ads_s );
2827 if ( !ADS_ERR_OK(status))
2828 goto done;
2831 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2832 if (!ADS_ERR_OK(status)) {
2833 goto done;
2836 timestr = ads_pull_string(ads_s, ctx, res, "currentTime");
2837 if (!timestr) {
2838 ads_msgfree(ads_s, res);
2839 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2840 goto done;
2843 /* but save the time and offset in the original ADS_STRUCT */
2845 ads->config.current_time = ads_parse_time(timestr);
2847 if (ads->config.current_time != 0) {
2848 ads->auth.time_offset = ads->config.current_time - time(NULL);
2849 DEBUG(4,("time offset is %d seconds\n", ads->auth.time_offset));
2852 ads_msgfree(ads, res);
2854 status = ADS_SUCCESS;
2856 done:
2857 /* free any temporary ads connections */
2858 if ( ads_s != ads ) {
2859 ads_destroy( &ads_s );
2861 talloc_destroy(ctx);
2863 return status;
2866 /********************************************************************
2867 ********************************************************************/
2869 ADS_STATUS ads_domain_func_level(ADS_STRUCT *ads, uint32 *val)
2871 const char *attrs[] = {"domainFunctionality", NULL};
2872 ADS_STATUS status;
2873 LDAPMessage *res;
2874 ADS_STRUCT *ads_s = ads;
2876 *val = DS_DOMAIN_FUNCTION_2000;
2878 /* establish a new ldap tcp session if necessary */
2880 if ( !ads->ldap.ld ) {
2881 if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup,
2882 ads->server.ldap_server )) == NULL )
2884 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
2885 goto done;
2887 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
2888 status = ads_connect( ads_s );
2889 if ( !ADS_ERR_OK(status))
2890 goto done;
2893 /* If the attribute does not exist assume it is a Windows 2000
2894 functional domain */
2896 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2897 if (!ADS_ERR_OK(status)) {
2898 if ( status.err.rc == LDAP_NO_SUCH_ATTRIBUTE ) {
2899 status = ADS_SUCCESS;
2901 goto done;
2904 if ( !ads_pull_uint32(ads_s, res, "domainFunctionality", val) ) {
2905 DEBUG(5,("ads_domain_func_level: Failed to pull the domainFunctionality attribute.\n"));
2907 DEBUG(3,("ads_domain_func_level: %d\n", *val));
2910 ads_msgfree(ads, res);
2912 done:
2913 /* free any temporary ads connections */
2914 if ( ads_s != ads ) {
2915 ads_destroy( &ads_s );
2918 return status;
2922 * find the domain sid for our domain
2923 * @param ads connection to ads server
2924 * @param sid Pointer to domain sid
2925 * @return status of search
2927 ADS_STATUS ads_domain_sid(ADS_STRUCT *ads, DOM_SID *sid)
2929 const char *attrs[] = {"objectSid", NULL};
2930 LDAPMessage *res;
2931 ADS_STATUS rc;
2933 rc = ads_do_search_retry(ads, ads->config.bind_path, LDAP_SCOPE_BASE, "(objectclass=*)",
2934 attrs, &res);
2935 if (!ADS_ERR_OK(rc)) return rc;
2936 if (!ads_pull_sid(ads, res, "objectSid", sid)) {
2937 ads_msgfree(ads, res);
2938 return ADS_ERROR_SYSTEM(ENOENT);
2940 ads_msgfree(ads, res);
2942 return ADS_SUCCESS;
2946 * find our site name
2947 * @param ads connection to ads server
2948 * @param mem_ctx Pointer to talloc context
2949 * @param site_name Pointer to the sitename
2950 * @return status of search
2952 ADS_STATUS ads_site_dn(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char **site_name)
2954 ADS_STATUS status;
2955 LDAPMessage *res;
2956 const char *dn, *service_name;
2957 const char *attrs[] = { "dsServiceName", NULL };
2959 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2960 if (!ADS_ERR_OK(status)) {
2961 return status;
2964 service_name = ads_pull_string(ads, mem_ctx, res, "dsServiceName");
2965 if (service_name == NULL) {
2966 ads_msgfree(ads, res);
2967 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2970 ads_msgfree(ads, res);
2972 /* go up three levels */
2973 dn = ads_parent_dn(ads_parent_dn(ads_parent_dn(service_name)));
2974 if (dn == NULL) {
2975 return ADS_ERROR(LDAP_NO_MEMORY);
2978 *site_name = talloc_strdup(mem_ctx, dn);
2979 if (*site_name == NULL) {
2980 return ADS_ERROR(LDAP_NO_MEMORY);
2983 return status;
2985 dsServiceName: CN=NTDS Settings,CN=W2K3DC,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,DC=ber,DC=suse,DC=de
2990 * find the site dn where a machine resides
2991 * @param ads connection to ads server
2992 * @param mem_ctx Pointer to talloc context
2993 * @param computer_name name of the machine
2994 * @param site_name Pointer to the sitename
2995 * @return status of search
2997 ADS_STATUS ads_site_dn_for_machine(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char *computer_name, const char **site_dn)
2999 ADS_STATUS status;
3000 LDAPMessage *res;
3001 const char *parent, *filter;
3002 char *config_context = NULL;
3003 char *dn;
3005 /* shortcut a query */
3006 if (strequal(computer_name, ads->config.ldap_server_name)) {
3007 return ads_site_dn(ads, mem_ctx, site_dn);
3010 status = ads_config_path(ads, mem_ctx, &config_context);
3011 if (!ADS_ERR_OK(status)) {
3012 return status;
3015 filter = talloc_asprintf(mem_ctx, "(cn=%s)", computer_name);
3016 if (filter == NULL) {
3017 return ADS_ERROR(LDAP_NO_MEMORY);
3020 status = ads_do_search(ads, config_context, LDAP_SCOPE_SUBTREE,
3021 filter, NULL, &res);
3022 if (!ADS_ERR_OK(status)) {
3023 return status;
3026 if (ads_count_replies(ads, res) != 1) {
3027 ads_msgfree(ads, res);
3028 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
3031 dn = ads_get_dn(ads, mem_ctx, res);
3032 if (dn == NULL) {
3033 ads_msgfree(ads, res);
3034 return ADS_ERROR(LDAP_NO_MEMORY);
3037 /* go up three levels */
3038 parent = ads_parent_dn(ads_parent_dn(ads_parent_dn(dn)));
3039 if (parent == NULL) {
3040 ads_msgfree(ads, res);
3041 TALLOC_FREE(dn);
3042 return ADS_ERROR(LDAP_NO_MEMORY);
3045 *site_dn = talloc_strdup(mem_ctx, parent);
3046 if (*site_dn == NULL) {
3047 ads_msgfree(ads, res);
3048 TALLOC_FREE(dn);
3049 return ADS_ERROR(LDAP_NO_MEMORY);
3052 TALLOC_FREE(dn);
3053 ads_msgfree(ads, res);
3055 return status;
3059 * get the upn suffixes for a domain
3060 * @param ads connection to ads server
3061 * @param mem_ctx Pointer to talloc context
3062 * @param suffixes Pointer to an array of suffixes
3063 * @param num_suffixes Pointer to the number of suffixes
3064 * @return status of search
3066 ADS_STATUS ads_upn_suffixes(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, char ***suffixes, size_t *num_suffixes)
3068 ADS_STATUS status;
3069 LDAPMessage *res;
3070 const char *base;
3071 char *config_context = NULL;
3072 const char *attrs[] = { "uPNSuffixes", NULL };
3074 status = ads_config_path(ads, mem_ctx, &config_context);
3075 if (!ADS_ERR_OK(status)) {
3076 return status;
3079 base = talloc_asprintf(mem_ctx, "cn=Partitions,%s", config_context);
3080 if (base == NULL) {
3081 return ADS_ERROR(LDAP_NO_MEMORY);
3084 status = ads_search_dn(ads, &res, base, attrs);
3085 if (!ADS_ERR_OK(status)) {
3086 return status;
3089 if (ads_count_replies(ads, res) != 1) {
3090 ads_msgfree(ads, res);
3091 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
3094 (*suffixes) = ads_pull_strings(ads, mem_ctx, res, "uPNSuffixes", num_suffixes);
3095 if ((*suffixes) == NULL) {
3096 ads_msgfree(ads, res);
3097 return ADS_ERROR(LDAP_NO_MEMORY);
3100 ads_msgfree(ads, res);
3102 return status;
3106 * get the joinable ous for a domain
3107 * @param ads connection to ads server
3108 * @param mem_ctx Pointer to talloc context
3109 * @param ous Pointer to an array of ous
3110 * @param num_ous Pointer to the number of ous
3111 * @return status of search
3113 ADS_STATUS ads_get_joinable_ous(ADS_STRUCT *ads,
3114 TALLOC_CTX *mem_ctx,
3115 char ***ous,
3116 size_t *num_ous)
3118 ADS_STATUS status;
3119 LDAPMessage *res = NULL;
3120 LDAPMessage *msg = NULL;
3121 const char *attrs[] = { "dn", NULL };
3122 int count = 0;
3124 status = ads_search(ads, &res,
3125 "(|(objectClass=domain)(objectclass=organizationalUnit))",
3126 attrs);
3127 if (!ADS_ERR_OK(status)) {
3128 return status;
3131 count = ads_count_replies(ads, res);
3132 if (count < 1) {
3133 ads_msgfree(ads, res);
3134 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3137 for (msg = ads_first_entry(ads, res); msg;
3138 msg = ads_next_entry(ads, msg)) {
3140 char *dn = NULL;
3142 dn = ads_get_dn(ads, talloc_tos(), msg);
3143 if (!dn) {
3144 ads_msgfree(ads, res);
3145 return ADS_ERROR(LDAP_NO_MEMORY);
3148 if (!add_string_to_array(mem_ctx, dn,
3149 (const char ***)ous,
3150 (int *)num_ous)) {
3151 TALLOC_FREE(dn);
3152 ads_msgfree(ads, res);
3153 return ADS_ERROR(LDAP_NO_MEMORY);
3156 TALLOC_FREE(dn);
3159 ads_msgfree(ads, res);
3161 return status;
3166 * pull a DOM_SID from an extended dn string
3167 * @param mem_ctx TALLOC_CTX
3168 * @param extended_dn string
3169 * @param flags string type of extended_dn
3170 * @param sid pointer to a DOM_SID
3171 * @return NT_STATUS_OK on success,
3172 * NT_INVALID_PARAMETER on error,
3173 * NT_STATUS_NOT_FOUND if no SID present
3175 ADS_STATUS ads_get_sid_from_extended_dn(TALLOC_CTX *mem_ctx,
3176 const char *extended_dn,
3177 enum ads_extended_dn_flags flags,
3178 DOM_SID *sid)
3180 char *p, *q, *dn;
3182 if (!extended_dn) {
3183 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3186 /* otherwise extended_dn gets stripped off */
3187 if ((dn = talloc_strdup(mem_ctx, extended_dn)) == NULL) {
3188 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3191 * ADS_EXTENDED_DN_HEX_STRING:
3192 * <GUID=238e1963cb390f4bb032ba0105525a29>;<SID=010500000000000515000000bb68c8fd6b61b427572eb04556040000>;CN=gd,OU=berlin,OU=suse,DC=ber,DC=suse,DC=de
3194 * ADS_EXTENDED_DN_STRING (only with w2k3):
3195 * <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
3197 * Object with no SID, such as an Exchange Public Folder
3198 * <GUID=28907fb4bdf6854993e7f0a10b504e7c>;CN=public,CN=Microsoft Exchange System Objects,DC=sd2k3ms,DC=west,DC=isilon,DC=com
3201 p = strchr(dn, ';');
3202 if (!p) {
3203 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3206 if (strncmp(p, ";<SID=", strlen(";<SID=")) != 0) {
3207 DEBUG(5,("No SID present in extended dn\n"));
3208 return ADS_ERROR_NT(NT_STATUS_NOT_FOUND);
3211 p += strlen(";<SID=");
3213 q = strchr(p, '>');
3214 if (!q) {
3215 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3218 *q = '\0';
3220 DEBUG(100,("ads_get_sid_from_extended_dn: sid string is %s\n", p));
3222 switch (flags) {
3224 case ADS_EXTENDED_DN_STRING:
3225 if (!string_to_sid(sid, p)) {
3226 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3228 break;
3229 case ADS_EXTENDED_DN_HEX_STRING: {
3230 fstring buf;
3231 size_t buf_len;
3233 buf_len = strhex_to_str(buf, sizeof(buf), p, strlen(p));
3234 if (buf_len == 0) {
3235 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3238 if (!sid_parse(buf, buf_len, sid)) {
3239 DEBUG(10,("failed to parse sid\n"));
3240 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3242 break;
3244 default:
3245 DEBUG(10,("unknown extended dn format\n"));
3246 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3249 return ADS_ERROR_NT(NT_STATUS_OK);
3253 * pull an array of DOM_SIDs from a ADS result
3254 * @param ads connection to ads server
3255 * @param mem_ctx TALLOC_CTX for allocating sid array
3256 * @param msg Results of search
3257 * @param field Attribute to retrieve
3258 * @param flags string type of extended_dn
3259 * @param sids pointer to sid array to allocate
3260 * @return the count of SIDs pulled
3262 int ads_pull_sids_from_extendeddn(ADS_STRUCT *ads,
3263 TALLOC_CTX *mem_ctx,
3264 LDAPMessage *msg,
3265 const char *field,
3266 enum ads_extended_dn_flags flags,
3267 DOM_SID **sids)
3269 int i;
3270 ADS_STATUS rc;
3271 size_t dn_count, ret_count = 0;
3272 char **dn_strings;
3274 if ((dn_strings = ads_pull_strings(ads, mem_ctx, msg, field,
3275 &dn_count)) == NULL) {
3276 return 0;
3279 (*sids) = TALLOC_ZERO_ARRAY(mem_ctx, DOM_SID, dn_count + 1);
3280 if (!(*sids)) {
3281 TALLOC_FREE(dn_strings);
3282 return 0;
3285 for (i=0; i<dn_count; i++) {
3286 rc = ads_get_sid_from_extended_dn(mem_ctx, dn_strings[i],
3287 flags, &(*sids)[i]);
3288 if (!ADS_ERR_OK(rc)) {
3289 if (NT_STATUS_EQUAL(ads_ntstatus(rc),
3290 NT_STATUS_NOT_FOUND)) {
3291 continue;
3293 else {
3294 TALLOC_FREE(*sids);
3295 TALLOC_FREE(dn_strings);
3296 return 0;
3299 ret_count++;
3302 TALLOC_FREE(dn_strings);
3304 return ret_count;
3307 /********************************************************************
3308 ********************************************************************/
3310 char* ads_get_dnshostname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3312 LDAPMessage *res = NULL;
3313 ADS_STATUS status;
3314 int count = 0;
3315 char *name = NULL;
3317 status = ads_find_machine_acct(ads, &res, global_myname());
3318 if (!ADS_ERR_OK(status)) {
3319 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
3320 global_myname()));
3321 goto out;
3324 if ( (count = ads_count_replies(ads, res)) != 1 ) {
3325 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
3326 goto out;
3329 if ( (name = ads_pull_string(ads, ctx, res, "dNSHostName")) == NULL ) {
3330 DEBUG(0,("ads_get_dnshostname: No dNSHostName attribute!\n"));
3333 out:
3334 ads_msgfree(ads, res);
3336 return name;
3339 /********************************************************************
3340 ********************************************************************/
3342 char* ads_get_upn( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3344 LDAPMessage *res = NULL;
3345 ADS_STATUS status;
3346 int count = 0;
3347 char *name = NULL;
3349 status = ads_find_machine_acct(ads, &res, machine_name);
3350 if (!ADS_ERR_OK(status)) {
3351 DEBUG(0,("ads_get_upn: Failed to find account for %s\n",
3352 global_myname()));
3353 goto out;
3356 if ( (count = ads_count_replies(ads, res)) != 1 ) {
3357 DEBUG(1,("ads_get_upn: %d entries returned!\n", count));
3358 goto out;
3361 if ( (name = ads_pull_string(ads, ctx, res, "userPrincipalName")) == NULL ) {
3362 DEBUG(2,("ads_get_upn: No userPrincipalName attribute!\n"));
3365 out:
3366 ads_msgfree(ads, res);
3368 return name;
3371 /********************************************************************
3372 ********************************************************************/
3374 char* ads_get_samaccountname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3376 LDAPMessage *res = NULL;
3377 ADS_STATUS status;
3378 int count = 0;
3379 char *name = NULL;
3381 status = ads_find_machine_acct(ads, &res, global_myname());
3382 if (!ADS_ERR_OK(status)) {
3383 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
3384 global_myname()));
3385 goto out;
3388 if ( (count = ads_count_replies(ads, res)) != 1 ) {
3389 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
3390 goto out;
3393 if ( (name = ads_pull_string(ads, ctx, res, "sAMAccountName")) == NULL ) {
3394 DEBUG(0,("ads_get_dnshostname: No sAMAccountName attribute!\n"));
3397 out:
3398 ads_msgfree(ads, res);
3400 return name;
3403 #if 0
3405 SAVED CODE - we used to join via ldap - remember how we did this. JRA.
3408 * Join a machine to a realm
3409 * Creates the machine account and sets the machine password
3410 * @param ads connection to ads server
3411 * @param machine name of host to add
3412 * @param org_unit Organizational unit to place machine in
3413 * @return status of join
3415 ADS_STATUS ads_join_realm(ADS_STRUCT *ads, const char *machine_name,
3416 uint32 account_type, const char *org_unit)
3418 ADS_STATUS status;
3419 LDAPMessage *res = NULL;
3420 char *machine;
3422 /* machine name must be lowercase */
3423 machine = SMB_STRDUP(machine_name);
3424 strlower_m(machine);
3427 status = ads_find_machine_acct(ads, (void **)&res, machine);
3428 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
3429 DEBUG(0, ("Host account for %s already exists - deleting old account\n", machine));
3430 status = ads_leave_realm(ads, machine);
3431 if (!ADS_ERR_OK(status)) {
3432 DEBUG(0, ("Failed to delete host '%s' from the '%s' realm.\n",
3433 machine, ads->config.realm));
3434 return status;
3438 status = ads_add_machine_acct(ads, machine, account_type, org_unit);
3439 if (!ADS_ERR_OK(status)) {
3440 DEBUG(0, ("ads_join_realm: ads_add_machine_acct failed (%s): %s\n", machine, ads_errstr(status)));
3441 SAFE_FREE(machine);
3442 return status;
3445 status = ads_find_machine_acct(ads, (void **)(void *)&res, machine);
3446 if (!ADS_ERR_OK(status)) {
3447 DEBUG(0, ("ads_join_realm: Host account test failed for machine %s\n", machine));
3448 SAFE_FREE(machine);
3449 return status;
3452 SAFE_FREE(machine);
3453 ads_msgfree(ads, res);
3455 return status;
3457 #endif
3460 * Delete a machine from the realm
3461 * @param ads connection to ads server
3462 * @param hostname Machine to remove
3463 * @return status of delete
3465 ADS_STATUS ads_leave_realm(ADS_STRUCT *ads, const char *hostname)
3467 ADS_STATUS status;
3468 void *msg;
3469 LDAPMessage *res;
3470 char *hostnameDN, *host;
3471 int rc;
3472 LDAPControl ldap_control;
3473 LDAPControl * pldap_control[2] = {NULL, NULL};
3475 pldap_control[0] = &ldap_control;
3476 memset(&ldap_control, 0, sizeof(LDAPControl));
3477 ldap_control.ldctl_oid = (char *)LDAP_SERVER_TREE_DELETE_OID;
3479 /* hostname must be lowercase */
3480 host = SMB_STRDUP(hostname);
3481 strlower_m(host);
3483 status = ads_find_machine_acct(ads, &res, host);
3484 if (!ADS_ERR_OK(status)) {
3485 DEBUG(0, ("Host account for %s does not exist.\n", host));
3486 SAFE_FREE(host);
3487 return status;
3490 msg = ads_first_entry(ads, res);
3491 if (!msg) {
3492 SAFE_FREE(host);
3493 return ADS_ERROR_SYSTEM(ENOENT);
3496 hostnameDN = ads_get_dn(ads, talloc_tos(), (LDAPMessage *)msg);
3498 rc = ldap_delete_ext_s(ads->ldap.ld, hostnameDN, pldap_control, NULL);
3499 if (rc) {
3500 DEBUG(3,("ldap_delete_ext_s failed with error code %d\n", rc));
3501 }else {
3502 DEBUG(3,("ldap_delete_ext_s succeeded with error code %d\n", rc));
3505 if (rc != LDAP_SUCCESS) {
3506 const char *attrs[] = { "cn", NULL };
3507 LDAPMessage *msg_sub;
3509 /* we only search with scope ONE, we do not expect any further
3510 * objects to be created deeper */
3512 status = ads_do_search_retry(ads, hostnameDN,
3513 LDAP_SCOPE_ONELEVEL,
3514 "(objectclass=*)", attrs, &res);
3516 if (!ADS_ERR_OK(status)) {
3517 SAFE_FREE(host);
3518 TALLOC_FREE(hostnameDN);
3519 return status;
3522 for (msg_sub = ads_first_entry(ads, res); msg_sub;
3523 msg_sub = ads_next_entry(ads, msg_sub)) {
3525 char *dn = NULL;
3527 if ((dn = ads_get_dn(ads, talloc_tos(), msg_sub)) == NULL) {
3528 SAFE_FREE(host);
3529 TALLOC_FREE(hostnameDN);
3530 return ADS_ERROR(LDAP_NO_MEMORY);
3533 status = ads_del_dn(ads, dn);
3534 if (!ADS_ERR_OK(status)) {
3535 DEBUG(3,("failed to delete dn %s: %s\n", dn, ads_errstr(status)));
3536 SAFE_FREE(host);
3537 TALLOC_FREE(dn);
3538 TALLOC_FREE(hostnameDN);
3539 return status;
3542 TALLOC_FREE(dn);
3545 /* there should be no subordinate objects anymore */
3546 status = ads_do_search_retry(ads, hostnameDN,
3547 LDAP_SCOPE_ONELEVEL,
3548 "(objectclass=*)", attrs, &res);
3550 if (!ADS_ERR_OK(status) || ( (ads_count_replies(ads, res)) > 0 ) ) {
3551 SAFE_FREE(host);
3552 TALLOC_FREE(hostnameDN);
3553 return status;
3556 /* delete hostnameDN now */
3557 status = ads_del_dn(ads, hostnameDN);
3558 if (!ADS_ERR_OK(status)) {
3559 SAFE_FREE(host);
3560 DEBUG(3,("failed to delete dn %s: %s\n", hostnameDN, ads_errstr(status)));
3561 TALLOC_FREE(hostnameDN);
3562 return status;
3566 TALLOC_FREE(hostnameDN);
3568 status = ads_find_machine_acct(ads, &res, host);
3569 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
3570 DEBUG(3, ("Failed to remove host account.\n"));
3571 SAFE_FREE(host);
3572 return status;
3575 SAFE_FREE(host);
3576 return status;
3580 * pull all token-sids from an LDAP dn
3581 * @param ads connection to ads server
3582 * @param mem_ctx TALLOC_CTX for allocating sid array
3583 * @param dn of LDAP object
3584 * @param user_sid pointer to DOM_SID (objectSid)
3585 * @param primary_group_sid pointer to DOM_SID (self composed)
3586 * @param sids pointer to sid array to allocate
3587 * @param num_sids counter of SIDs pulled
3588 * @return status of token query
3590 ADS_STATUS ads_get_tokensids(ADS_STRUCT *ads,
3591 TALLOC_CTX *mem_ctx,
3592 const char *dn,
3593 DOM_SID *user_sid,
3594 DOM_SID *primary_group_sid,
3595 DOM_SID **sids,
3596 size_t *num_sids)
3598 ADS_STATUS status;
3599 LDAPMessage *res = NULL;
3600 int count = 0;
3601 size_t tmp_num_sids;
3602 DOM_SID *tmp_sids;
3603 DOM_SID tmp_user_sid;
3604 DOM_SID tmp_primary_group_sid;
3605 uint32 pgid;
3606 const char *attrs[] = {
3607 "objectSid",
3608 "tokenGroups",
3609 "primaryGroupID",
3610 NULL
3613 status = ads_search_retry_dn(ads, &res, dn, attrs);
3614 if (!ADS_ERR_OK(status)) {
3615 return status;
3618 count = ads_count_replies(ads, res);
3619 if (count != 1) {
3620 ads_msgfree(ads, res);
3621 return ADS_ERROR_LDAP(LDAP_NO_SUCH_OBJECT);
3624 if (!ads_pull_sid(ads, res, "objectSid", &tmp_user_sid)) {
3625 ads_msgfree(ads, res);
3626 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3629 if (!ads_pull_uint32(ads, res, "primaryGroupID", &pgid)) {
3630 ads_msgfree(ads, res);
3631 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3635 /* hack to compose the primary group sid without knowing the
3636 * domsid */
3638 DOM_SID domsid;
3639 uint32 dummy_rid;
3641 sid_copy(&domsid, &tmp_user_sid);
3643 if (!sid_split_rid(&domsid, &dummy_rid)) {
3644 ads_msgfree(ads, res);
3645 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3648 if (!sid_compose(&tmp_primary_group_sid, &domsid, pgid)) {
3649 ads_msgfree(ads, res);
3650 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3654 tmp_num_sids = ads_pull_sids(ads, mem_ctx, res, "tokenGroups", &tmp_sids);
3656 if (tmp_num_sids == 0 || !tmp_sids) {
3657 ads_msgfree(ads, res);
3658 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3661 if (num_sids) {
3662 *num_sids = tmp_num_sids;
3665 if (sids) {
3666 *sids = tmp_sids;
3669 if (user_sid) {
3670 *user_sid = tmp_user_sid;
3673 if (primary_group_sid) {
3674 *primary_group_sid = tmp_primary_group_sid;
3677 DEBUG(10,("ads_get_tokensids: returned %d sids\n", (int)tmp_num_sids + 2));
3679 ads_msgfree(ads, res);
3680 return ADS_ERROR_LDAP(LDAP_SUCCESS);
3684 * Find a sAMAccoutName in LDAP
3685 * @param ads connection to ads server
3686 * @param mem_ctx TALLOC_CTX for allocating sid array
3687 * @param samaccountname to search
3688 * @param uac_ret uint32 pointer userAccountControl attribute value
3689 * @param dn_ret pointer to dn
3690 * @return status of token query
3692 ADS_STATUS ads_find_samaccount(ADS_STRUCT *ads,
3693 TALLOC_CTX *mem_ctx,
3694 const char *samaccountname,
3695 uint32 *uac_ret,
3696 const char **dn_ret)
3698 ADS_STATUS status;
3699 const char *attrs[] = { "userAccountControl", NULL };
3700 const char *filter;
3701 LDAPMessage *res = NULL;
3702 char *dn = NULL;
3703 uint32 uac = 0;
3705 filter = talloc_asprintf(mem_ctx, "(&(objectclass=user)(sAMAccountName=%s))",
3706 samaccountname);
3707 if (filter == NULL) {
3708 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
3709 goto out;
3712 status = ads_do_search_all(ads, ads->config.bind_path,
3713 LDAP_SCOPE_SUBTREE,
3714 filter, attrs, &res);
3716 if (!ADS_ERR_OK(status)) {
3717 goto out;
3720 if (ads_count_replies(ads, res) != 1) {
3721 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3722 goto out;
3725 dn = ads_get_dn(ads, talloc_tos(), res);
3726 if (dn == NULL) {
3727 status = ADS_ERROR(LDAP_NO_MEMORY);
3728 goto out;
3731 if (!ads_pull_uint32(ads, res, "userAccountControl", &uac)) {
3732 status = ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
3733 goto out;
3736 if (uac_ret) {
3737 *uac_ret = uac;
3740 if (dn_ret) {
3741 *dn_ret = talloc_strdup(mem_ctx, dn);
3742 if (!*dn_ret) {
3743 status = ADS_ERROR(LDAP_NO_MEMORY);
3744 goto out;
3747 out:
3748 TALLOC_FREE(dn);
3749 ads_msgfree(ads, res);
3751 return status;
3755 * find our configuration path
3756 * @param ads connection to ads server
3757 * @param mem_ctx Pointer to talloc context
3758 * @param config_path Pointer to the config path
3759 * @return status of search
3761 ADS_STATUS ads_config_path(ADS_STRUCT *ads,
3762 TALLOC_CTX *mem_ctx,
3763 char **config_path)
3765 ADS_STATUS status;
3766 LDAPMessage *res = NULL;
3767 const char *config_context = NULL;
3768 const char *attrs[] = { "configurationNamingContext", NULL };
3770 status = ads_do_search(ads, "", LDAP_SCOPE_BASE,
3771 "(objectclass=*)", attrs, &res);
3772 if (!ADS_ERR_OK(status)) {
3773 return status;
3776 config_context = ads_pull_string(ads, mem_ctx, res,
3777 "configurationNamingContext");
3778 ads_msgfree(ads, res);
3779 if (!config_context) {
3780 return ADS_ERROR(LDAP_NO_MEMORY);
3783 if (config_path) {
3784 *config_path = talloc_strdup(mem_ctx, config_context);
3785 if (!*config_path) {
3786 return ADS_ERROR(LDAP_NO_MEMORY);
3790 return ADS_ERROR(LDAP_SUCCESS);
3794 * find the displayName of an extended right
3795 * @param ads connection to ads server
3796 * @param config_path The config path
3797 * @param mem_ctx Pointer to talloc context
3798 * @param GUID struct of the rightsGUID
3799 * @return status of search
3801 const char *ads_get_extended_right_name_by_guid(ADS_STRUCT *ads,
3802 const char *config_path,
3803 TALLOC_CTX *mem_ctx,
3804 const struct GUID *rights_guid)
3806 ADS_STATUS rc;
3807 LDAPMessage *res = NULL;
3808 char *expr = NULL;
3809 const char *attrs[] = { "displayName", NULL };
3810 const char *result = NULL;
3811 const char *path;
3813 if (!ads || !mem_ctx || !rights_guid) {
3814 goto done;
3817 expr = talloc_asprintf(mem_ctx, "(rightsGuid=%s)",
3818 GUID_string(mem_ctx, rights_guid));
3819 if (!expr) {
3820 goto done;
3823 path = talloc_asprintf(mem_ctx, "cn=Extended-Rights,%s", config_path);
3824 if (!path) {
3825 goto done;
3828 rc = ads_do_search_retry(ads, path, LDAP_SCOPE_SUBTREE,
3829 expr, attrs, &res);
3830 if (!ADS_ERR_OK(rc)) {
3831 goto done;
3834 if (ads_count_replies(ads, res) != 1) {
3835 goto done;
3838 result = ads_pull_string(ads, mem_ctx, res, "displayName");
3840 done:
3841 ads_msgfree(ads, res);
3842 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;
3862 name_dn = ldb_dn_explode(mem_ctx, *account_ou);
3863 if (name_dn) {
3864 return ADS_SUCCESS;
3867 ou_string = ads_ou_string(ads, *account_ou);
3868 if (!ou_string) {
3869 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX);
3872 name = talloc_asprintf(mem_ctx, "%s,%s", ou_string,
3873 ads->config.bind_path);
3874 SAFE_FREE(ou_string);
3875 if (!name) {
3876 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3879 name_dn = ldb_dn_explode(mem_ctx, name);
3880 if (!name_dn) {
3881 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX);
3884 *account_ou = talloc_strdup(mem_ctx, name);
3885 if (!*account_ou) {
3886 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3889 return ADS_SUCCESS;
3892 #endif