Fix bug 7045 - Bad (non memory copying) interfaces in smbc_setXXXX calls.
[Samba.git] / source / libads / ldap.c
blobd9598e5dd8308ce4edca71027fd33bf7447c5576
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 again:
314 /* we need to try once with the realm name and fallback to the
315 netbios domain name if we fail (if netbios has not been disabled */
317 if ( !got_realm && !lp_disable_netbios() ) {
318 c_realm = ads->server.workgroup;
319 if (!c_realm || !*c_realm) {
320 if ( use_own_domain )
321 c_realm = lp_workgroup();
325 if ( !c_realm || !*c_realm ) {
326 DEBUG(0,("ads_find_dc: no realm or workgroup! Don't know what to do\n"));
327 return NT_STATUS_INVALID_PARAMETER; /* rather need MISSING_PARAMETER ... */
330 if ( use_own_domain ) {
331 c_domain = lp_workgroup();
332 } else {
333 c_domain = ads->server.workgroup;
336 realm = c_realm;
337 domain = c_domain;
340 * In case of LDAP we use get_dc_name() as that
341 * creates the custom krb5.conf file
343 if (!(ads->auth.flags & ADS_AUTH_NO_BIND)) {
344 fstring srv_name;
345 struct sockaddr_storage ip_out;
347 DEBUG(6,("ads_find_dc: (ldap) looking for %s '%s'\n",
348 (got_realm ? "realm" : "domain"), realm));
350 if (get_dc_name(domain, realm, srv_name, &ip_out)) {
352 * we call ads_try_connect() to fill in the
353 * ads->config details
355 if (ads_try_connect(ads, srv_name, false)) {
356 return NT_STATUS_OK;
360 return NT_STATUS_NO_LOGON_SERVERS;
363 sitename = sitename_fetch(realm);
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;
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 (!(str_list_copy(talloc_tos(), &search_attrs, 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 (!(str_list_copy(talloc_tos(), &search_attrs, 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 * Free up memory from various ads requests
1281 * @param ads connection to ads server
1282 * @param mem Area to free
1284 void ads_memfree(ADS_STRUCT *ads, void *mem)
1286 SAFE_FREE(mem);
1290 * Get a dn from search results
1291 * @param ads connection to ads server
1292 * @param msg Search result
1293 * @return dn string
1295 char *ads_get_dn(ADS_STRUCT *ads, LDAPMessage *msg)
1297 char *utf8_dn, *unix_dn;
1298 size_t converted_size;
1300 utf8_dn = ldap_get_dn(ads->ldap.ld, msg);
1302 if (!utf8_dn) {
1303 DEBUG (5, ("ads_get_dn: ldap_get_dn failed\n"));
1304 return NULL;
1307 if (!pull_utf8_allocate(&unix_dn, utf8_dn, &converted_size)) {
1308 DEBUG(0,("ads_get_dn: string conversion failure utf8 [%s]\n",
1309 utf8_dn ));
1310 return NULL;
1312 ldap_memfree(utf8_dn);
1313 return unix_dn;
1317 * Get the parent from a dn
1318 * @param dn the dn to return the parent from
1319 * @return parent dn string
1321 char *ads_parent_dn(const char *dn)
1323 char *p;
1325 if (dn == NULL) {
1326 return NULL;
1329 p = strchr(dn, ',');
1331 if (p == NULL) {
1332 return NULL;
1335 return p+1;
1339 * Find a machine account given a hostname
1340 * @param ads connection to ads server
1341 * @param res ** which will contain results - free res* with ads_msgfree()
1342 * @param host Hostname to search for
1343 * @return status of search
1345 ADS_STATUS ads_find_machine_acct(ADS_STRUCT *ads, LDAPMessage **res,
1346 const char *machine)
1348 ADS_STATUS status;
1349 char *expr;
1350 const char *attrs[] = {"*", "nTSecurityDescriptor", NULL};
1352 *res = NULL;
1354 /* the easiest way to find a machine account anywhere in the tree
1355 is to look for hostname$ */
1356 if (asprintf(&expr, "(samAccountName=%s$)", machine) == -1) {
1357 DEBUG(1, ("asprintf failed!\n"));
1358 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1361 status = ads_search(ads, res, expr, attrs);
1362 SAFE_FREE(expr);
1363 return status;
1367 * Initialize a list of mods to be used in a modify request
1368 * @param ctx An initialized TALLOC_CTX
1369 * @return allocated ADS_MODLIST
1371 ADS_MODLIST ads_init_mods(TALLOC_CTX *ctx)
1373 #define ADS_MODLIST_ALLOC_SIZE 10
1374 LDAPMod **mods;
1376 if ((mods = TALLOC_ZERO_ARRAY(ctx, LDAPMod *, ADS_MODLIST_ALLOC_SIZE + 1)))
1377 /* -1 is safety to make sure we don't go over the end.
1378 need to reset it to NULL before doing ldap modify */
1379 mods[ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1381 return (ADS_MODLIST)mods;
1386 add an attribute to the list, with values list already constructed
1388 static ADS_STATUS ads_modlist_add(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1389 int mod_op, const char *name,
1390 const void *_invals)
1392 const void **invals = (const void **)_invals;
1393 int curmod;
1394 LDAPMod **modlist = (LDAPMod **) *mods;
1395 struct berval **ber_values = NULL;
1396 char **char_values = NULL;
1398 if (!invals) {
1399 mod_op = LDAP_MOD_DELETE;
1400 } else {
1401 if (mod_op & LDAP_MOD_BVALUES)
1402 ber_values = ads_dup_values(ctx,
1403 (const struct berval **)invals);
1404 else
1405 char_values = ads_push_strvals(ctx,
1406 (const char **) invals);
1409 /* find the first empty slot */
1410 for (curmod=0; modlist[curmod] && modlist[curmod] != (LDAPMod *) -1;
1411 curmod++);
1412 if (modlist[curmod] == (LDAPMod *) -1) {
1413 if (!(modlist = TALLOC_REALLOC_ARRAY(ctx, modlist, LDAPMod *,
1414 curmod+ADS_MODLIST_ALLOC_SIZE+1)))
1415 return ADS_ERROR(LDAP_NO_MEMORY);
1416 memset(&modlist[curmod], 0,
1417 ADS_MODLIST_ALLOC_SIZE*sizeof(LDAPMod *));
1418 modlist[curmod+ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1419 *mods = (ADS_MODLIST)modlist;
1422 if (!(modlist[curmod] = TALLOC_ZERO_P(ctx, LDAPMod)))
1423 return ADS_ERROR(LDAP_NO_MEMORY);
1424 modlist[curmod]->mod_type = talloc_strdup(ctx, name);
1425 if (mod_op & LDAP_MOD_BVALUES) {
1426 modlist[curmod]->mod_bvalues = ber_values;
1427 } else if (mod_op & LDAP_MOD_DELETE) {
1428 modlist[curmod]->mod_values = NULL;
1429 } else {
1430 modlist[curmod]->mod_values = char_values;
1433 modlist[curmod]->mod_op = mod_op;
1434 return ADS_ERROR(LDAP_SUCCESS);
1438 * Add a single string value to a mod list
1439 * @param ctx An initialized TALLOC_CTX
1440 * @param mods An initialized ADS_MODLIST
1441 * @param name The attribute name to add
1442 * @param val The value to add - NULL means DELETE
1443 * @return ADS STATUS indicating success of add
1445 ADS_STATUS ads_mod_str(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1446 const char *name, const char *val)
1448 const char *values[2];
1450 values[0] = val;
1451 values[1] = NULL;
1453 if (!val)
1454 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1455 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE, name, values);
1459 * Add an array of string values to a mod list
1460 * @param ctx An initialized TALLOC_CTX
1461 * @param mods An initialized ADS_MODLIST
1462 * @param name The attribute name to add
1463 * @param vals The array of string values to add - NULL means DELETE
1464 * @return ADS STATUS indicating success of add
1466 ADS_STATUS ads_mod_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1467 const char *name, const char **vals)
1469 if (!vals)
1470 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1471 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE,
1472 name, (const void **) vals);
1475 #if 0
1477 * Add a single ber-encoded value to a mod list
1478 * @param ctx An initialized TALLOC_CTX
1479 * @param mods An initialized ADS_MODLIST
1480 * @param name The attribute name to add
1481 * @param val The value to add - NULL means DELETE
1482 * @return ADS STATUS indicating success of add
1484 static ADS_STATUS ads_mod_ber(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1485 const char *name, const struct berval *val)
1487 const struct berval *values[2];
1489 values[0] = val;
1490 values[1] = NULL;
1491 if (!val)
1492 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1493 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE|LDAP_MOD_BVALUES,
1494 name, (const void **) values);
1496 #endif
1499 * Perform an ldap modify
1500 * @param ads connection to ads server
1501 * @param mod_dn DistinguishedName to modify
1502 * @param mods list of modifications to perform
1503 * @return status of modify
1505 ADS_STATUS ads_gen_mod(ADS_STRUCT *ads, const char *mod_dn, ADS_MODLIST mods)
1507 int ret,i;
1508 char *utf8_dn = NULL;
1509 size_t converted_size;
1511 this control is needed to modify that contains a currently
1512 non-existent attribute (but allowable for the object) to run
1514 LDAPControl PermitModify = {
1515 CONST_DISCARD(char *, ADS_PERMIT_MODIFY_OID),
1516 {0, NULL},
1517 (char) 1};
1518 LDAPControl *controls[2];
1520 controls[0] = &PermitModify;
1521 controls[1] = NULL;
1523 if (!push_utf8_allocate(&utf8_dn, mod_dn, &converted_size)) {
1524 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1527 /* find the end of the list, marked by NULL or -1 */
1528 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1529 /* make sure the end of the list is NULL */
1530 mods[i] = NULL;
1531 ret = ldap_modify_ext_s(ads->ldap.ld, utf8_dn,
1532 (LDAPMod **) mods, controls, NULL);
1533 SAFE_FREE(utf8_dn);
1534 return ADS_ERROR(ret);
1538 * Perform an ldap add
1539 * @param ads connection to ads server
1540 * @param new_dn DistinguishedName to add
1541 * @param mods list of attributes and values for DN
1542 * @return status of add
1544 ADS_STATUS ads_gen_add(ADS_STRUCT *ads, const char *new_dn, ADS_MODLIST mods)
1546 int ret, i;
1547 char *utf8_dn = NULL;
1548 size_t converted_size;
1550 if (!push_utf8_allocate(&utf8_dn, new_dn, &converted_size)) {
1551 DEBUG(1, ("ads_gen_add: push_utf8_allocate failed!"));
1552 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1555 /* find the end of the list, marked by NULL or -1 */
1556 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1557 /* make sure the end of the list is NULL */
1558 mods[i] = NULL;
1560 ret = ldap_add_s(ads->ldap.ld, utf8_dn, (LDAPMod**)mods);
1561 SAFE_FREE(utf8_dn);
1562 return ADS_ERROR(ret);
1566 * Delete a DistinguishedName
1567 * @param ads connection to ads server
1568 * @param new_dn DistinguishedName to delete
1569 * @return status of delete
1571 ADS_STATUS ads_del_dn(ADS_STRUCT *ads, char *del_dn)
1573 int ret;
1574 char *utf8_dn = NULL;
1575 size_t converted_size;
1576 if (!push_utf8_allocate(&utf8_dn, del_dn, &converted_size)) {
1577 DEBUG(1, ("ads_del_dn: push_utf8_allocate failed!"));
1578 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1581 ret = ldap_delete_s(ads->ldap.ld, utf8_dn);
1582 SAFE_FREE(utf8_dn);
1583 return ADS_ERROR(ret);
1587 * Build an org unit string
1588 * if org unit is Computers or blank then assume a container, otherwise
1589 * assume a / separated list of organisational units.
1590 * jmcd: '\' is now used for escapes so certain chars can be in the ou (e.g. #)
1591 * @param ads connection to ads server
1592 * @param org_unit Organizational unit
1593 * @return org unit string - caller must free
1595 char *ads_ou_string(ADS_STRUCT *ads, const char *org_unit)
1597 char *ret = NULL;
1599 if (!org_unit || !*org_unit) {
1601 ret = ads_default_ou_string(ads, WELL_KNOWN_GUID_COMPUTERS);
1603 /* samba4 might not yet respond to a wellknownobject-query */
1604 return ret ? ret : SMB_STRDUP("cn=Computers");
1607 if (strequal(org_unit, "Computers")) {
1608 return SMB_STRDUP("cn=Computers");
1611 /* jmcd: removed "\\" from the separation chars, because it is
1612 needed as an escape for chars like '#' which are valid in an
1613 OU name */
1614 return ads_build_path(org_unit, "/", "ou=", 1);
1618 * Get a org unit string for a well-known GUID
1619 * @param ads connection to ads server
1620 * @param wknguid Well known GUID
1621 * @return org unit string - caller must free
1623 char *ads_default_ou_string(ADS_STRUCT *ads, const char *wknguid)
1625 ADS_STATUS status;
1626 LDAPMessage *res = NULL;
1627 char *base, *wkn_dn = NULL, *ret = NULL, **wkn_dn_exp = NULL,
1628 **bind_dn_exp = NULL;
1629 const char *attrs[] = {"distinguishedName", NULL};
1630 int new_ln, wkn_ln, bind_ln, i;
1632 if (wknguid == NULL) {
1633 return NULL;
1636 if (asprintf(&base, "<WKGUID=%s,%s>", wknguid, ads->config.bind_path ) == -1) {
1637 DEBUG(1, ("asprintf failed!\n"));
1638 return NULL;
1641 status = ads_search_dn(ads, &res, base, attrs);
1642 if (!ADS_ERR_OK(status)) {
1643 DEBUG(1,("Failed while searching for: %s\n", base));
1644 goto out;
1647 if (ads_count_replies(ads, res) != 1) {
1648 goto out;
1651 /* substitute the bind-path from the well-known-guid-search result */
1652 wkn_dn = ads_get_dn(ads, res);
1653 if (!wkn_dn) {
1654 goto out;
1657 wkn_dn_exp = ldap_explode_dn(wkn_dn, 0);
1658 if (!wkn_dn_exp) {
1659 goto out;
1662 bind_dn_exp = ldap_explode_dn(ads->config.bind_path, 0);
1663 if (!bind_dn_exp) {
1664 goto out;
1667 for (wkn_ln=0; wkn_dn_exp[wkn_ln]; wkn_ln++)
1669 for (bind_ln=0; bind_dn_exp[bind_ln]; bind_ln++)
1672 new_ln = wkn_ln - bind_ln;
1674 ret = SMB_STRDUP(wkn_dn_exp[0]);
1675 if (!ret) {
1676 goto out;
1679 for (i=1; i < new_ln; i++) {
1680 char *s = NULL;
1682 if (asprintf(&s, "%s,%s", ret, wkn_dn_exp[i]) == -1) {
1683 SAFE_FREE(ret);
1684 goto out;
1687 SAFE_FREE(ret);
1688 ret = SMB_STRDUP(s);
1689 free(s);
1690 if (!ret) {
1691 goto out;
1695 out:
1696 SAFE_FREE(base);
1697 ads_msgfree(ads, res);
1698 ads_memfree(ads, wkn_dn);
1699 if (wkn_dn_exp) {
1700 ldap_value_free(wkn_dn_exp);
1702 if (bind_dn_exp) {
1703 ldap_value_free(bind_dn_exp);
1706 return ret;
1710 * Adds (appends) an item to an attribute array, rather then
1711 * replacing the whole list
1712 * @param ctx An initialized TALLOC_CTX
1713 * @param mods An initialized ADS_MODLIST
1714 * @param name name of the ldap attribute to append to
1715 * @param vals an array of values to add
1716 * @return status of addition
1719 ADS_STATUS ads_add_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1720 const char *name, const char **vals)
1722 return ads_modlist_add(ctx, mods, LDAP_MOD_ADD, name,
1723 (const void *) vals);
1727 * Determines the an account's current KVNO via an LDAP lookup
1728 * @param ads An initialized ADS_STRUCT
1729 * @param account_name the NT samaccountname.
1730 * @return the kvno for the account, or -1 in case of a failure.
1733 uint32 ads_get_kvno(ADS_STRUCT *ads, const char *account_name)
1735 LDAPMessage *res = NULL;
1736 uint32 kvno = (uint32)-1; /* -1 indicates a failure */
1737 char *filter;
1738 const char *attrs[] = {"msDS-KeyVersionNumber", NULL};
1739 char *dn_string = NULL;
1740 ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS);
1742 DEBUG(5,("ads_get_kvno: Searching for account %s\n", account_name));
1743 if (asprintf(&filter, "(samAccountName=%s)", account_name) == -1) {
1744 return kvno;
1746 ret = ads_search(ads, &res, filter, attrs);
1747 SAFE_FREE(filter);
1748 if (!ADS_ERR_OK(ret) || (ads_count_replies(ads, res) != 1)) {
1749 DEBUG(1,("ads_get_kvno: Account for %s not found.\n", account_name));
1750 ads_msgfree(ads, res);
1751 return kvno;
1754 dn_string = ads_get_dn(ads, res);
1755 if (!dn_string) {
1756 DEBUG(0,("ads_get_kvno: out of memory.\n"));
1757 ads_msgfree(ads, res);
1758 return kvno;
1760 DEBUG(5,("ads_get_kvno: Using: %s\n", dn_string));
1761 ads_memfree(ads, dn_string);
1763 /* ---------------------------------------------------------
1764 * 0 is returned as a default KVNO from this point on...
1765 * This is done because Windows 2000 does not support key
1766 * version numbers. Chances are that a failure in the next
1767 * step is simply due to Windows 2000 being used for a
1768 * domain controller. */
1769 kvno = 0;
1771 if (!ads_pull_uint32(ads, res, "msDS-KeyVersionNumber", &kvno)) {
1772 DEBUG(3,("ads_get_kvno: Error Determining KVNO!\n"));
1773 DEBUG(3,("ads_get_kvno: Windows 2000 does not support KVNO's, so this may be normal.\n"));
1774 ads_msgfree(ads, res);
1775 return kvno;
1778 /* Success */
1779 DEBUG(5,("ads_get_kvno: Looked Up KVNO of: %d\n", kvno));
1780 ads_msgfree(ads, res);
1781 return kvno;
1785 * Determines the computer account's current KVNO via an LDAP lookup
1786 * @param ads An initialized ADS_STRUCT
1787 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1788 * @return the kvno for the computer account, or -1 in case of a failure.
1791 uint32_t ads_get_machine_kvno(ADS_STRUCT *ads, const char *machine_name)
1793 char *computer_account = NULL;
1794 uint32_t kvno = -1;
1796 if (asprintf(&computer_account, "%s$", machine_name) < 0) {
1797 return kvno;
1800 kvno = ads_get_kvno(ads, computer_account);
1801 free(computer_account);
1803 return kvno;
1807 * This clears out all registered spn's for a given hostname
1808 * @param ads An initilaized ADS_STRUCT
1809 * @param machine_name the NetBIOS name of the computer.
1810 * @return 0 upon success, non-zero otherwise.
1813 ADS_STATUS ads_clear_service_principal_names(ADS_STRUCT *ads, const char *machine_name)
1815 TALLOC_CTX *ctx;
1816 LDAPMessage *res = NULL;
1817 ADS_MODLIST mods;
1818 const char *servicePrincipalName[1] = {NULL};
1819 ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS);
1820 char *dn_string = NULL;
1822 ret = ads_find_machine_acct(ads, &res, machine_name);
1823 if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
1824 DEBUG(5,("ads_clear_service_principal_names: WARNING: Host Account for %s not found... skipping operation.\n", machine_name));
1825 DEBUG(5,("ads_clear_service_principal_names: WARNING: Service Principals for %s have NOT been cleared.\n", machine_name));
1826 ads_msgfree(ads, res);
1827 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1830 DEBUG(5,("ads_clear_service_principal_names: Host account for %s found\n", machine_name));
1831 ctx = talloc_init("ads_clear_service_principal_names");
1832 if (!ctx) {
1833 ads_msgfree(ads, res);
1834 return ADS_ERROR(LDAP_NO_MEMORY);
1837 if (!(mods = ads_init_mods(ctx))) {
1838 talloc_destroy(ctx);
1839 ads_msgfree(ads, res);
1840 return ADS_ERROR(LDAP_NO_MEMORY);
1842 ret = ads_mod_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1843 if (!ADS_ERR_OK(ret)) {
1844 DEBUG(1,("ads_clear_service_principal_names: Error creating strlist.\n"));
1845 ads_msgfree(ads, res);
1846 talloc_destroy(ctx);
1847 return ret;
1849 dn_string = ads_get_dn(ads, res);
1850 if (!dn_string) {
1851 talloc_destroy(ctx);
1852 ads_msgfree(ads, res);
1853 return ADS_ERROR(LDAP_NO_MEMORY);
1855 ret = ads_gen_mod(ads, dn_string, mods);
1856 ads_memfree(ads,dn_string);
1857 if (!ADS_ERR_OK(ret)) {
1858 DEBUG(1,("ads_clear_service_principal_names: Error: Updating Service Principals for machine %s in LDAP\n",
1859 machine_name));
1860 ads_msgfree(ads, res);
1861 talloc_destroy(ctx);
1862 return ret;
1865 ads_msgfree(ads, res);
1866 talloc_destroy(ctx);
1867 return ret;
1871 * This adds a service principal name to an existing computer account
1872 * (found by hostname) in AD.
1873 * @param ads An initialized ADS_STRUCT
1874 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1875 * @param my_fqdn The fully qualified DNS name of the machine
1876 * @param spn A string of the service principal to add, i.e. 'host'
1877 * @return 0 upon sucess, or non-zero if a failure occurs
1880 ADS_STATUS ads_add_service_principal_name(ADS_STRUCT *ads, const char *machine_name,
1881 const char *my_fqdn, const char *spn)
1883 ADS_STATUS ret;
1884 TALLOC_CTX *ctx;
1885 LDAPMessage *res = NULL;
1886 char *psp1, *psp2;
1887 ADS_MODLIST mods;
1888 char *dn_string = NULL;
1889 const char *servicePrincipalName[3] = {NULL, NULL, NULL};
1891 ret = ads_find_machine_acct(ads, &res, machine_name);
1892 if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
1893 DEBUG(1,("ads_add_service_principal_name: WARNING: Host Account for %s not found... skipping operation.\n",
1894 machine_name));
1895 DEBUG(1,("ads_add_service_principal_name: WARNING: Service Principal '%s/%s@%s' has NOT been added.\n",
1896 spn, machine_name, ads->config.realm));
1897 ads_msgfree(ads, res);
1898 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1901 DEBUG(1,("ads_add_service_principal_name: Host account for %s found\n", machine_name));
1902 if (!(ctx = talloc_init("ads_add_service_principal_name"))) {
1903 ads_msgfree(ads, res);
1904 return ADS_ERROR(LDAP_NO_MEMORY);
1907 /* add short name spn */
1909 if ( (psp1 = talloc_asprintf(ctx, "%s/%s", spn, machine_name)) == NULL ) {
1910 talloc_destroy(ctx);
1911 ads_msgfree(ads, res);
1912 return ADS_ERROR(LDAP_NO_MEMORY);
1914 strupper_m(psp1);
1915 strlower_m(&psp1[strlen(spn)]);
1916 servicePrincipalName[0] = psp1;
1918 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n",
1919 psp1, machine_name));
1922 /* add fully qualified spn */
1924 if ( (psp2 = talloc_asprintf(ctx, "%s/%s", spn, my_fqdn)) == NULL ) {
1925 ret = ADS_ERROR(LDAP_NO_MEMORY);
1926 goto out;
1928 strupper_m(psp2);
1929 strlower_m(&psp2[strlen(spn)]);
1930 servicePrincipalName[1] = psp2;
1932 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n",
1933 psp2, machine_name));
1935 if ( (mods = ads_init_mods(ctx)) == NULL ) {
1936 ret = ADS_ERROR(LDAP_NO_MEMORY);
1937 goto out;
1940 ret = ads_add_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1941 if (!ADS_ERR_OK(ret)) {
1942 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
1943 goto out;
1946 if ( (dn_string = ads_get_dn(ads, res)) == NULL ) {
1947 ret = ADS_ERROR(LDAP_NO_MEMORY);
1948 goto out;
1951 ret = ads_gen_mod(ads, dn_string, mods);
1952 ads_memfree(ads,dn_string);
1953 if (!ADS_ERR_OK(ret)) {
1954 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
1955 goto out;
1958 out:
1959 TALLOC_FREE( ctx );
1960 ads_msgfree(ads, res);
1961 return ret;
1965 * adds a machine account to the ADS server
1966 * @param ads An intialized ADS_STRUCT
1967 * @param machine_name - the NetBIOS machine name of this account.
1968 * @param account_type A number indicating the type of account to create
1969 * @param org_unit The LDAP path in which to place this account
1970 * @return 0 upon success, or non-zero otherwise
1973 ADS_STATUS ads_create_machine_acct(ADS_STRUCT *ads, const char *machine_name,
1974 const char *org_unit)
1976 ADS_STATUS ret;
1977 char *samAccountName, *controlstr;
1978 TALLOC_CTX *ctx;
1979 ADS_MODLIST mods;
1980 char *machine_escaped = NULL;
1981 char *new_dn;
1982 const char *objectClass[] = {"top", "person", "organizationalPerson",
1983 "user", "computer", NULL};
1984 LDAPMessage *res = NULL;
1985 uint32 acct_control = ( UF_WORKSTATION_TRUST_ACCOUNT |\
1986 UF_DONT_EXPIRE_PASSWD |\
1987 UF_ACCOUNTDISABLE );
1989 if (!(ctx = talloc_init("ads_add_machine_acct")))
1990 return ADS_ERROR(LDAP_NO_MEMORY);
1992 ret = ADS_ERROR(LDAP_NO_MEMORY);
1994 machine_escaped = escape_rdn_val_string_alloc(machine_name);
1995 if (!machine_escaped) {
1996 goto done;
1999 new_dn = talloc_asprintf(ctx, "cn=%s,%s", machine_escaped, org_unit);
2000 samAccountName = talloc_asprintf(ctx, "%s$", machine_name);
2002 if ( !new_dn || !samAccountName ) {
2003 goto done;
2006 #ifndef ENCTYPE_ARCFOUR_HMAC
2007 acct_control |= UF_USE_DES_KEY_ONLY;
2008 #endif
2010 if (!(controlstr = talloc_asprintf(ctx, "%u", acct_control))) {
2011 goto done;
2014 if (!(mods = ads_init_mods(ctx))) {
2015 goto done;
2018 ads_mod_str(ctx, &mods, "cn", machine_name);
2019 ads_mod_str(ctx, &mods, "sAMAccountName", samAccountName);
2020 ads_mod_strlist(ctx, &mods, "objectClass", objectClass);
2021 ads_mod_str(ctx, &mods, "userAccountControl", controlstr);
2023 ret = ads_gen_add(ads, new_dn, mods);
2025 done:
2026 SAFE_FREE(machine_escaped);
2027 ads_msgfree(ads, res);
2028 talloc_destroy(ctx);
2030 return ret;
2034 * move a machine account to another OU on the ADS server
2035 * @param ads - An intialized ADS_STRUCT
2036 * @param machine_name - the NetBIOS machine name of this account.
2037 * @param org_unit - The LDAP path in which to place this account
2038 * @param moved - whether we moved the machine account (optional)
2039 * @return 0 upon success, or non-zero otherwise
2042 ADS_STATUS ads_move_machine_acct(ADS_STRUCT *ads, const char *machine_name,
2043 const char *org_unit, bool *moved)
2045 ADS_STATUS rc;
2046 int ldap_status;
2047 LDAPMessage *res = NULL;
2048 char *filter = NULL;
2049 char *computer_dn = NULL;
2050 char *parent_dn;
2051 char *computer_rdn = NULL;
2052 bool need_move = False;
2054 if (asprintf(&filter, "(samAccountName=%s$)", machine_name) == -1) {
2055 rc = ADS_ERROR(LDAP_NO_MEMORY);
2056 goto done;
2059 /* Find pre-existing machine */
2060 rc = ads_search(ads, &res, filter, NULL);
2061 if (!ADS_ERR_OK(rc)) {
2062 goto done;
2065 computer_dn = ads_get_dn(ads, res);
2066 if (!computer_dn) {
2067 rc = ADS_ERROR(LDAP_NO_MEMORY);
2068 goto done;
2071 parent_dn = ads_parent_dn(computer_dn);
2072 if (strequal(parent_dn, org_unit)) {
2073 goto done;
2076 need_move = True;
2078 if (asprintf(&computer_rdn, "CN=%s", machine_name) == -1) {
2079 rc = ADS_ERROR(LDAP_NO_MEMORY);
2080 goto done;
2083 ldap_status = ldap_rename_s(ads->ldap.ld, computer_dn, computer_rdn,
2084 org_unit, 1, NULL, NULL);
2085 rc = ADS_ERROR(ldap_status);
2087 done:
2088 ads_msgfree(ads, res);
2089 SAFE_FREE(filter);
2090 SAFE_FREE(computer_dn);
2091 SAFE_FREE(computer_rdn);
2093 if (!ADS_ERR_OK(rc)) {
2094 need_move = False;
2097 if (moved) {
2098 *moved = need_move;
2101 return rc;
2105 dump a binary result from ldap
2107 static void dump_binary(ADS_STRUCT *ads, const char *field, struct berval **values)
2109 int i, j;
2110 for (i=0; values[i]; i++) {
2111 printf("%s: ", field);
2112 for (j=0; j<values[i]->bv_len; j++) {
2113 printf("%02X", (unsigned char)values[i]->bv_val[j]);
2115 printf("\n");
2119 static void dump_guid(ADS_STRUCT *ads, const char *field, struct berval **values)
2121 int i;
2122 for (i=0; values[i]; i++) {
2124 UUID_FLAT guid;
2125 struct GUID tmp;
2127 memcpy(guid.info, values[i]->bv_val, sizeof(guid.info));
2128 smb_uuid_unpack(guid, &tmp);
2129 printf("%s: %s\n", field, smb_uuid_string(talloc_tos(), tmp));
2134 dump a sid result from ldap
2136 static void dump_sid(ADS_STRUCT *ads, const char *field, struct berval **values)
2138 int i;
2139 for (i=0; values[i]; i++) {
2140 DOM_SID sid;
2141 fstring tmp;
2142 sid_parse(values[i]->bv_val, values[i]->bv_len, &sid);
2143 printf("%s: %s\n", field, sid_to_fstring(tmp, &sid));
2148 dump ntSecurityDescriptor
2150 static void dump_sd(ADS_STRUCT *ads, const char *filed, struct berval **values)
2152 TALLOC_CTX *frame = talloc_stackframe();
2153 struct security_descriptor *psd;
2154 NTSTATUS status;
2156 status = unmarshall_sec_desc(talloc_tos(), (uint8 *)values[0]->bv_val,
2157 values[0]->bv_len, &psd);
2158 if (!NT_STATUS_IS_OK(status)) {
2159 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
2160 nt_errstr(status)));
2161 TALLOC_FREE(frame);
2162 return;
2165 if (psd) {
2166 ads_disp_sd(ads, talloc_tos(), psd);
2169 TALLOC_FREE(frame);
2173 dump a string result from ldap
2175 static void dump_string(const char *field, char **values)
2177 int i;
2178 for (i=0; values[i]; i++) {
2179 printf("%s: %s\n", field, values[i]);
2184 dump a field from LDAP on stdout
2185 used for debugging
2188 static bool ads_dump_field(ADS_STRUCT *ads, char *field, void **values, void *data_area)
2190 const struct {
2191 const char *name;
2192 bool string;
2193 void (*handler)(ADS_STRUCT *, const char *, struct berval **);
2194 } handlers[] = {
2195 {"objectGUID", False, dump_guid},
2196 {"netbootGUID", False, dump_guid},
2197 {"nTSecurityDescriptor", False, dump_sd},
2198 {"dnsRecord", False, dump_binary},
2199 {"objectSid", False, dump_sid},
2200 {"tokenGroups", False, dump_sid},
2201 {"tokenGroupsNoGCAcceptable", False, dump_sid},
2202 {"tokengroupsGlobalandUniversal", False, dump_sid},
2203 {"mS-DS-CreatorSID", False, dump_sid},
2204 {"msExchMailboxGuid", False, dump_guid},
2205 {NULL, True, NULL}
2207 int i;
2209 if (!field) { /* must be end of an entry */
2210 printf("\n");
2211 return False;
2214 for (i=0; handlers[i].name; i++) {
2215 if (StrCaseCmp(handlers[i].name, field) == 0) {
2216 if (!values) /* first time, indicate string or not */
2217 return handlers[i].string;
2218 handlers[i].handler(ads, field, (struct berval **) values);
2219 break;
2222 if (!handlers[i].name) {
2223 if (!values) /* first time, indicate string conversion */
2224 return True;
2225 dump_string(field, (char **)values);
2227 return False;
2231 * Dump a result from LDAP on stdout
2232 * used for debugging
2233 * @param ads connection to ads server
2234 * @param res Results to dump
2237 void ads_dump(ADS_STRUCT *ads, LDAPMessage *res)
2239 ads_process_results(ads, res, ads_dump_field, NULL);
2243 * Walk through results, calling a function for each entry found.
2244 * The function receives a field name, a berval * array of values,
2245 * and a data area passed through from the start. The function is
2246 * called once with null for field and values at the end of each
2247 * entry.
2248 * @param ads connection to ads server
2249 * @param res Results to process
2250 * @param fn Function for processing each result
2251 * @param data_area user-defined area to pass to function
2253 void ads_process_results(ADS_STRUCT *ads, LDAPMessage *res,
2254 bool (*fn)(ADS_STRUCT *, char *, void **, void *),
2255 void *data_area)
2257 LDAPMessage *msg;
2258 TALLOC_CTX *ctx;
2259 size_t converted_size;
2261 if (!(ctx = talloc_init("ads_process_results")))
2262 return;
2264 for (msg = ads_first_entry(ads, res); msg;
2265 msg = ads_next_entry(ads, msg)) {
2266 char *utf8_field;
2267 BerElement *b;
2269 for (utf8_field=ldap_first_attribute(ads->ldap.ld,
2270 (LDAPMessage *)msg,&b);
2271 utf8_field;
2272 utf8_field=ldap_next_attribute(ads->ldap.ld,
2273 (LDAPMessage *)msg,b)) {
2274 struct berval **ber_vals;
2275 char **str_vals, **utf8_vals;
2276 char *field;
2277 bool string;
2279 if (!pull_utf8_talloc(ctx, &field, utf8_field,
2280 &converted_size))
2282 DEBUG(0,("ads_process_results: "
2283 "pull_utf8_talloc failed: %s",
2284 strerror(errno)));
2287 string = fn(ads, field, NULL, data_area);
2289 if (string) {
2290 utf8_vals = ldap_get_values(ads->ldap.ld,
2291 (LDAPMessage *)msg, field);
2292 str_vals = ads_pull_strvals(ctx,
2293 (const char **) utf8_vals);
2294 fn(ads, field, (void **) str_vals, data_area);
2295 ldap_value_free(utf8_vals);
2296 } else {
2297 ber_vals = ldap_get_values_len(ads->ldap.ld,
2298 (LDAPMessage *)msg, field);
2299 fn(ads, field, (void **) ber_vals, data_area);
2301 ldap_value_free_len(ber_vals);
2303 ldap_memfree(utf8_field);
2305 ber_free(b, 0);
2306 talloc_free_children(ctx);
2307 fn(ads, NULL, NULL, data_area); /* completed an entry */
2310 talloc_destroy(ctx);
2314 * count how many replies are in a LDAPMessage
2315 * @param ads connection to ads server
2316 * @param res Results to count
2317 * @return number of replies
2319 int ads_count_replies(ADS_STRUCT *ads, void *res)
2321 return ldap_count_entries(ads->ldap.ld, (LDAPMessage *)res);
2325 * pull the first entry from a ADS result
2326 * @param ads connection to ads server
2327 * @param res Results of search
2328 * @return first entry from result
2330 LDAPMessage *ads_first_entry(ADS_STRUCT *ads, LDAPMessage *res)
2332 return ldap_first_entry(ads->ldap.ld, res);
2336 * pull the next entry from a ADS result
2337 * @param ads connection to ads server
2338 * @param res Results of search
2339 * @return next entry from result
2341 LDAPMessage *ads_next_entry(ADS_STRUCT *ads, LDAPMessage *res)
2343 return ldap_next_entry(ads->ldap.ld, res);
2347 * pull the first message from a ADS result
2348 * @param ads connection to ads server
2349 * @param res Results of search
2350 * @return first message from result
2352 LDAPMessage *ads_first_message(ADS_STRUCT *ads, LDAPMessage *res)
2354 return ldap_first_message(ads->ldap.ld, res);
2358 * pull the next message from a ADS result
2359 * @param ads connection to ads server
2360 * @param res Results of search
2361 * @return next message from result
2363 LDAPMessage *ads_next_message(ADS_STRUCT *ads, LDAPMessage *res)
2365 return ldap_next_message(ads->ldap.ld, res);
2369 * pull a single string from a ADS result
2370 * @param ads connection to ads server
2371 * @param mem_ctx TALLOC_CTX to use for allocating result string
2372 * @param msg Results of search
2373 * @param field Attribute to retrieve
2374 * @return Result string in talloc context
2376 char *ads_pull_string(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, LDAPMessage *msg,
2377 const char *field)
2379 char **values;
2380 char *ret = NULL;
2381 char *ux_string;
2382 size_t converted_size;
2384 values = ldap_get_values(ads->ldap.ld, msg, field);
2385 if (!values)
2386 return NULL;
2388 if (values[0] && pull_utf8_talloc(mem_ctx, &ux_string, values[0],
2389 &converted_size))
2391 ret = ux_string;
2393 ldap_value_free(values);
2394 return ret;
2398 * pull an array of strings from a ADS result
2399 * @param ads connection to ads server
2400 * @param mem_ctx TALLOC_CTX to use for allocating result string
2401 * @param msg Results of search
2402 * @param field Attribute to retrieve
2403 * @return Result strings in talloc context
2405 char **ads_pull_strings(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2406 LDAPMessage *msg, const char *field,
2407 size_t *num_values)
2409 char **values;
2410 char **ret = NULL;
2411 int i;
2412 size_t converted_size;
2414 values = ldap_get_values(ads->ldap.ld, msg, field);
2415 if (!values)
2416 return NULL;
2418 *num_values = ldap_count_values(values);
2420 ret = TALLOC_ARRAY(mem_ctx, char *, *num_values + 1);
2421 if (!ret) {
2422 ldap_value_free(values);
2423 return NULL;
2426 for (i=0;i<*num_values;i++) {
2427 if (!pull_utf8_talloc(mem_ctx, &ret[i], values[i],
2428 &converted_size))
2430 ldap_value_free(values);
2431 return NULL;
2434 ret[i] = NULL;
2436 ldap_value_free(values);
2437 return ret;
2441 * pull an array of strings from a ADS result
2442 * (handle large multivalue attributes with range retrieval)
2443 * @param ads connection to ads server
2444 * @param mem_ctx TALLOC_CTX to use for allocating result string
2445 * @param msg Results of search
2446 * @param field Attribute to retrieve
2447 * @param current_strings strings returned by a previous call to this function
2448 * @param next_attribute The next query should ask for this attribute
2449 * @param num_values How many values did we get this time?
2450 * @param more_values Are there more values to get?
2451 * @return Result strings in talloc context
2453 char **ads_pull_strings_range(ADS_STRUCT *ads,
2454 TALLOC_CTX *mem_ctx,
2455 LDAPMessage *msg, const char *field,
2456 char **current_strings,
2457 const char **next_attribute,
2458 size_t *num_strings,
2459 bool *more_strings)
2461 char *attr;
2462 char *expected_range_attrib, *range_attr;
2463 BerElement *ptr = NULL;
2464 char **strings;
2465 char **new_strings;
2466 size_t num_new_strings;
2467 unsigned long int range_start;
2468 unsigned long int range_end;
2470 /* we might have been given the whole lot anyway */
2471 if ((strings = ads_pull_strings(ads, mem_ctx, msg, field, num_strings))) {
2472 *more_strings = False;
2473 return strings;
2476 expected_range_attrib = talloc_asprintf(mem_ctx, "%s;Range=", field);
2478 /* look for Range result */
2479 for (attr = ldap_first_attribute(ads->ldap.ld, (LDAPMessage *)msg, &ptr);
2480 attr;
2481 attr = ldap_next_attribute(ads->ldap.ld, (LDAPMessage *)msg, ptr)) {
2482 /* we ignore the fact that this is utf8, as all attributes are ascii... */
2483 if (strnequal(attr, expected_range_attrib, strlen(expected_range_attrib))) {
2484 range_attr = attr;
2485 break;
2487 ldap_memfree(attr);
2489 if (!attr) {
2490 ber_free(ptr, 0);
2491 /* nothing here - this field is just empty */
2492 *more_strings = False;
2493 return NULL;
2496 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-%lu",
2497 &range_start, &range_end) == 2) {
2498 *more_strings = True;
2499 } else {
2500 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-*",
2501 &range_start) == 1) {
2502 *more_strings = False;
2503 } else {
2504 DEBUG(1, ("ads_pull_strings_range: Cannot parse Range attriubte (%s)\n",
2505 range_attr));
2506 ldap_memfree(range_attr);
2507 *more_strings = False;
2508 return NULL;
2512 if ((*num_strings) != range_start) {
2513 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) doesn't start at %u, but at %lu"
2514 " - aborting range retreival\n",
2515 range_attr, (unsigned int)(*num_strings) + 1, range_start));
2516 ldap_memfree(range_attr);
2517 *more_strings = False;
2518 return NULL;
2521 new_strings = ads_pull_strings(ads, mem_ctx, msg, range_attr, &num_new_strings);
2523 if (*more_strings && ((*num_strings + num_new_strings) != (range_end + 1))) {
2524 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) tells us we have %lu "
2525 "strings in this bunch, but we only got %lu - aborting range retreival\n",
2526 range_attr, (unsigned long int)range_end - range_start + 1,
2527 (unsigned long int)num_new_strings));
2528 ldap_memfree(range_attr);
2529 *more_strings = False;
2530 return NULL;
2533 strings = TALLOC_REALLOC_ARRAY(mem_ctx, current_strings, char *,
2534 *num_strings + num_new_strings);
2536 if (strings == NULL) {
2537 ldap_memfree(range_attr);
2538 *more_strings = False;
2539 return NULL;
2542 if (new_strings && num_new_strings) {
2543 memcpy(&strings[*num_strings], new_strings,
2544 sizeof(*new_strings) * num_new_strings);
2547 (*num_strings) += num_new_strings;
2549 if (*more_strings) {
2550 *next_attribute = talloc_asprintf(mem_ctx,
2551 "%s;range=%d-*",
2552 field,
2553 (int)*num_strings);
2555 if (!*next_attribute) {
2556 DEBUG(1, ("talloc_asprintf for next attribute failed!\n"));
2557 ldap_memfree(range_attr);
2558 *more_strings = False;
2559 return NULL;
2563 ldap_memfree(range_attr);
2565 return strings;
2569 * pull a single uint32 from a ADS result
2570 * @param ads connection to ads server
2571 * @param msg Results of search
2572 * @param field Attribute to retrieve
2573 * @param v Pointer to int to store result
2574 * @return boolean inidicating success
2576 bool ads_pull_uint32(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
2577 uint32 *v)
2579 char **values;
2581 values = ldap_get_values(ads->ldap.ld, msg, field);
2582 if (!values)
2583 return False;
2584 if (!values[0]) {
2585 ldap_value_free(values);
2586 return False;
2589 *v = atoi(values[0]);
2590 ldap_value_free(values);
2591 return True;
2595 * pull a single objectGUID from an ADS result
2596 * @param ads connection to ADS server
2597 * @param msg results of search
2598 * @param guid 37-byte area to receive text guid
2599 * @return boolean indicating success
2601 bool ads_pull_guid(ADS_STRUCT *ads, LDAPMessage *msg, struct GUID *guid)
2603 char **values;
2604 UUID_FLAT flat_guid;
2606 values = ldap_get_values(ads->ldap.ld, msg, "objectGUID");
2607 if (!values)
2608 return False;
2610 if (values[0]) {
2611 memcpy(&flat_guid.info, values[0], sizeof(UUID_FLAT));
2612 smb_uuid_unpack(flat_guid, guid);
2613 ldap_value_free(values);
2614 return True;
2616 ldap_value_free(values);
2617 return False;
2623 * pull a single DOM_SID from a ADS result
2624 * @param ads connection to ads server
2625 * @param msg Results of search
2626 * @param field Attribute to retrieve
2627 * @param sid Pointer to sid to store result
2628 * @return boolean inidicating success
2630 bool ads_pull_sid(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
2631 DOM_SID *sid)
2633 struct berval **values;
2634 bool ret = False;
2636 values = ldap_get_values_len(ads->ldap.ld, msg, field);
2638 if (!values)
2639 return False;
2641 if (values[0])
2642 ret = sid_parse(values[0]->bv_val, values[0]->bv_len, sid);
2644 ldap_value_free_len(values);
2645 return ret;
2649 * pull an array of DOM_SIDs from a ADS result
2650 * @param ads connection to ads server
2651 * @param mem_ctx TALLOC_CTX for allocating sid array
2652 * @param msg Results of search
2653 * @param field Attribute to retrieve
2654 * @param sids pointer to sid array to allocate
2655 * @return the count of SIDs pulled
2657 int ads_pull_sids(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2658 LDAPMessage *msg, const char *field, DOM_SID **sids)
2660 struct berval **values;
2661 bool ret;
2662 int count, i;
2664 values = ldap_get_values_len(ads->ldap.ld, msg, field);
2666 if (!values)
2667 return 0;
2669 for (i=0; values[i]; i++)
2670 /* nop */ ;
2672 if (i) {
2673 (*sids) = TALLOC_ARRAY(mem_ctx, DOM_SID, i);
2674 if (!(*sids)) {
2675 ldap_value_free_len(values);
2676 return 0;
2678 } else {
2679 (*sids) = NULL;
2682 count = 0;
2683 for (i=0; values[i]; i++) {
2684 ret = sid_parse(values[i]->bv_val, values[i]->bv_len, &(*sids)[count]);
2685 if (ret) {
2686 DEBUG(10, ("pulling SID: %s\n",
2687 sid_string_dbg(&(*sids)[count])));
2688 count++;
2692 ldap_value_free_len(values);
2693 return count;
2697 * pull a SEC_DESC from a ADS result
2698 * @param ads connection to ads server
2699 * @param mem_ctx TALLOC_CTX for allocating sid array
2700 * @param msg Results of search
2701 * @param field Attribute to retrieve
2702 * @param sd Pointer to *SEC_DESC to store result (talloc()ed)
2703 * @return boolean inidicating success
2705 bool ads_pull_sd(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2706 LDAPMessage *msg, const char *field, SEC_DESC **sd)
2708 struct berval **values;
2709 bool ret = true;
2711 values = ldap_get_values_len(ads->ldap.ld, msg, field);
2713 if (!values) return false;
2715 if (values[0]) {
2716 NTSTATUS status;
2717 status = unmarshall_sec_desc(mem_ctx,
2718 (uint8 *)values[0]->bv_val,
2719 values[0]->bv_len, sd);
2720 if (!NT_STATUS_IS_OK(status)) {
2721 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
2722 nt_errstr(status)));
2723 ret = false;
2727 ldap_value_free_len(values);
2728 return ret;
2732 * in order to support usernames longer than 21 characters we need to
2733 * use both the sAMAccountName and the userPrincipalName attributes
2734 * It seems that not all users have the userPrincipalName attribute set
2736 * @param ads connection to ads server
2737 * @param mem_ctx TALLOC_CTX for allocating sid array
2738 * @param msg Results of search
2739 * @return the username
2741 char *ads_pull_username(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2742 LDAPMessage *msg)
2744 #if 0 /* JERRY */
2745 char *ret, *p;
2747 /* lookup_name() only works on the sAMAccountName to
2748 returning the username portion of userPrincipalName
2749 breaks winbindd_getpwnam() */
2751 ret = ads_pull_string(ads, mem_ctx, msg, "userPrincipalName");
2752 if (ret && (p = strchr_m(ret, '@'))) {
2753 *p = 0;
2754 return ret;
2756 #endif
2757 return ads_pull_string(ads, mem_ctx, msg, "sAMAccountName");
2762 * find the update serial number - this is the core of the ldap cache
2763 * @param ads connection to ads server
2764 * @param ads connection to ADS server
2765 * @param usn Pointer to retrieved update serial number
2766 * @return status of search
2768 ADS_STATUS ads_USN(ADS_STRUCT *ads, uint32 *usn)
2770 const char *attrs[] = {"highestCommittedUSN", NULL};
2771 ADS_STATUS status;
2772 LDAPMessage *res;
2774 status = ads_do_search_retry(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2775 if (!ADS_ERR_OK(status))
2776 return status;
2778 if (ads_count_replies(ads, res) != 1) {
2779 ads_msgfree(ads, res);
2780 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2783 if (!ads_pull_uint32(ads, res, "highestCommittedUSN", usn)) {
2784 ads_msgfree(ads, res);
2785 return ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
2788 ads_msgfree(ads, res);
2789 return ADS_SUCCESS;
2792 /* parse a ADS timestring - typical string is
2793 '20020917091222.0Z0' which means 09:12.22 17th September
2794 2002, timezone 0 */
2795 static time_t ads_parse_time(const char *str)
2797 struct tm tm;
2799 ZERO_STRUCT(tm);
2801 if (sscanf(str, "%4d%2d%2d%2d%2d%2d",
2802 &tm.tm_year, &tm.tm_mon, &tm.tm_mday,
2803 &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) {
2804 return 0;
2806 tm.tm_year -= 1900;
2807 tm.tm_mon -= 1;
2809 return timegm(&tm);
2812 /********************************************************************
2813 ********************************************************************/
2815 ADS_STATUS ads_current_time(ADS_STRUCT *ads)
2817 const char *attrs[] = {"currentTime", NULL};
2818 ADS_STATUS status;
2819 LDAPMessage *res;
2820 char *timestr;
2821 TALLOC_CTX *ctx;
2822 ADS_STRUCT *ads_s = ads;
2824 if (!(ctx = talloc_init("ads_current_time"))) {
2825 return ADS_ERROR(LDAP_NO_MEMORY);
2828 /* establish a new ldap tcp session if necessary */
2830 if ( !ads->ldap.ld ) {
2831 if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup,
2832 ads->server.ldap_server )) == NULL )
2834 goto done;
2836 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
2837 status = ads_connect( ads_s );
2838 if ( !ADS_ERR_OK(status))
2839 goto done;
2842 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2843 if (!ADS_ERR_OK(status)) {
2844 goto done;
2847 timestr = ads_pull_string(ads_s, ctx, res, "currentTime");
2848 if (!timestr) {
2849 ads_msgfree(ads_s, res);
2850 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2851 goto done;
2854 /* but save the time and offset in the original ADS_STRUCT */
2856 ads->config.current_time = ads_parse_time(timestr);
2858 if (ads->config.current_time != 0) {
2859 ads->auth.time_offset = ads->config.current_time - time(NULL);
2860 DEBUG(4,("time offset is %d seconds\n", ads->auth.time_offset));
2863 ads_msgfree(ads, res);
2865 status = ADS_SUCCESS;
2867 done:
2868 /* free any temporary ads connections */
2869 if ( ads_s != ads ) {
2870 ads_destroy( &ads_s );
2872 talloc_destroy(ctx);
2874 return status;
2877 /********************************************************************
2878 ********************************************************************/
2880 ADS_STATUS ads_domain_func_level(ADS_STRUCT *ads, uint32 *val)
2882 const char *attrs[] = {"domainFunctionality", NULL};
2883 ADS_STATUS status;
2884 LDAPMessage *res;
2885 ADS_STRUCT *ads_s = ads;
2887 *val = DS_DOMAIN_FUNCTION_2000;
2889 /* establish a new ldap tcp session if necessary */
2891 if ( !ads->ldap.ld ) {
2892 if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup,
2893 ads->server.ldap_server )) == NULL )
2895 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
2896 goto done;
2898 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
2899 status = ads_connect( ads_s );
2900 if ( !ADS_ERR_OK(status))
2901 goto done;
2904 /* If the attribute does not exist assume it is a Windows 2000
2905 functional domain */
2907 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2908 if (!ADS_ERR_OK(status)) {
2909 if ( status.err.rc == LDAP_NO_SUCH_ATTRIBUTE ) {
2910 status = ADS_SUCCESS;
2912 goto done;
2915 if ( !ads_pull_uint32(ads_s, res, "domainFunctionality", val) ) {
2916 DEBUG(5,("ads_domain_func_level: Failed to pull the domainFunctionality attribute.\n"));
2918 DEBUG(3,("ads_domain_func_level: %d\n", *val));
2921 ads_msgfree(ads, res);
2923 done:
2924 /* free any temporary ads connections */
2925 if ( ads_s != ads ) {
2926 ads_destroy( &ads_s );
2929 return status;
2933 * find the domain sid for our domain
2934 * @param ads connection to ads server
2935 * @param sid Pointer to domain sid
2936 * @return status of search
2938 ADS_STATUS ads_domain_sid(ADS_STRUCT *ads, DOM_SID *sid)
2940 const char *attrs[] = {"objectSid", NULL};
2941 LDAPMessage *res;
2942 ADS_STATUS rc;
2944 rc = ads_do_search_retry(ads, ads->config.bind_path, LDAP_SCOPE_BASE, "(objectclass=*)",
2945 attrs, &res);
2946 if (!ADS_ERR_OK(rc)) return rc;
2947 if (!ads_pull_sid(ads, res, "objectSid", sid)) {
2948 ads_msgfree(ads, res);
2949 return ADS_ERROR_SYSTEM(ENOENT);
2951 ads_msgfree(ads, res);
2953 return ADS_SUCCESS;
2957 * find our site name
2958 * @param ads connection to ads server
2959 * @param mem_ctx Pointer to talloc context
2960 * @param site_name Pointer to the sitename
2961 * @return status of search
2963 ADS_STATUS ads_site_dn(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char **site_name)
2965 ADS_STATUS status;
2966 LDAPMessage *res;
2967 const char *dn, *service_name;
2968 const char *attrs[] = { "dsServiceName", NULL };
2970 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2971 if (!ADS_ERR_OK(status)) {
2972 return status;
2975 service_name = ads_pull_string(ads, mem_ctx, res, "dsServiceName");
2976 if (service_name == NULL) {
2977 ads_msgfree(ads, res);
2978 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2981 ads_msgfree(ads, res);
2983 /* go up three levels */
2984 dn = ads_parent_dn(ads_parent_dn(ads_parent_dn(service_name)));
2985 if (dn == NULL) {
2986 return ADS_ERROR(LDAP_NO_MEMORY);
2989 *site_name = talloc_strdup(mem_ctx, dn);
2990 if (*site_name == NULL) {
2991 return ADS_ERROR(LDAP_NO_MEMORY);
2994 return status;
2996 dsServiceName: CN=NTDS Settings,CN=W2K3DC,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,DC=ber,DC=suse,DC=de
3001 * find the site dn where a machine resides
3002 * @param ads connection to ads server
3003 * @param mem_ctx Pointer to talloc context
3004 * @param computer_name name of the machine
3005 * @param site_name Pointer to the sitename
3006 * @return status of search
3008 ADS_STATUS ads_site_dn_for_machine(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char *computer_name, const char **site_dn)
3010 ADS_STATUS status;
3011 LDAPMessage *res;
3012 const char *parent, *filter;
3013 char *config_context = NULL;
3014 char *dn;
3016 /* shortcut a query */
3017 if (strequal(computer_name, ads->config.ldap_server_name)) {
3018 return ads_site_dn(ads, mem_ctx, site_dn);
3021 status = ads_config_path(ads, mem_ctx, &config_context);
3022 if (!ADS_ERR_OK(status)) {
3023 return status;
3026 filter = talloc_asprintf(mem_ctx, "(cn=%s)", computer_name);
3027 if (filter == NULL) {
3028 return ADS_ERROR(LDAP_NO_MEMORY);
3031 status = ads_do_search(ads, config_context, LDAP_SCOPE_SUBTREE,
3032 filter, NULL, &res);
3033 if (!ADS_ERR_OK(status)) {
3034 return status;
3037 if (ads_count_replies(ads, res) != 1) {
3038 ads_msgfree(ads, res);
3039 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
3042 dn = ads_get_dn(ads, res);
3043 if (dn == NULL) {
3044 ads_msgfree(ads, res);
3045 return ADS_ERROR(LDAP_NO_MEMORY);
3048 /* go up three levels */
3049 parent = ads_parent_dn(ads_parent_dn(ads_parent_dn(dn)));
3050 if (parent == NULL) {
3051 ads_msgfree(ads, res);
3052 ads_memfree(ads, dn);
3053 return ADS_ERROR(LDAP_NO_MEMORY);
3056 *site_dn = talloc_strdup(mem_ctx, parent);
3057 if (*site_dn == NULL) {
3058 ads_msgfree(ads, res);
3059 ads_memfree(ads, dn);
3060 return ADS_ERROR(LDAP_NO_MEMORY);
3063 ads_memfree(ads, dn);
3064 ads_msgfree(ads, res);
3066 return status;
3070 * get the upn suffixes for a domain
3071 * @param ads connection to ads server
3072 * @param mem_ctx Pointer to talloc context
3073 * @param suffixes Pointer to an array of suffixes
3074 * @param num_suffixes Pointer to the number of suffixes
3075 * @return status of search
3077 ADS_STATUS ads_upn_suffixes(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, char ***suffixes, size_t *num_suffixes)
3079 ADS_STATUS status;
3080 LDAPMessage *res;
3081 const char *base;
3082 char *config_context = NULL;
3083 const char *attrs[] = { "uPNSuffixes", NULL };
3085 status = ads_config_path(ads, mem_ctx, &config_context);
3086 if (!ADS_ERR_OK(status)) {
3087 return status;
3090 base = talloc_asprintf(mem_ctx, "cn=Partitions,%s", config_context);
3091 if (base == NULL) {
3092 return ADS_ERROR(LDAP_NO_MEMORY);
3095 status = ads_search_dn(ads, &res, base, attrs);
3096 if (!ADS_ERR_OK(status)) {
3097 return status;
3100 if (ads_count_replies(ads, res) != 1) {
3101 ads_msgfree(ads, res);
3102 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
3105 (*suffixes) = ads_pull_strings(ads, mem_ctx, res, "uPNSuffixes", num_suffixes);
3106 if ((*suffixes) == NULL) {
3107 ads_msgfree(ads, res);
3108 return ADS_ERROR(LDAP_NO_MEMORY);
3111 ads_msgfree(ads, res);
3113 return status;
3117 * get the joinable ous for a domain
3118 * @param ads connection to ads server
3119 * @param mem_ctx Pointer to talloc context
3120 * @param ous Pointer to an array of ous
3121 * @param num_ous Pointer to the number of ous
3122 * @return status of search
3124 ADS_STATUS ads_get_joinable_ous(ADS_STRUCT *ads,
3125 TALLOC_CTX *mem_ctx,
3126 char ***ous,
3127 size_t *num_ous)
3129 ADS_STATUS status;
3130 LDAPMessage *res = NULL;
3131 LDAPMessage *msg = NULL;
3132 const char *attrs[] = { "dn", NULL };
3133 int count = 0;
3135 status = ads_search(ads, &res,
3136 "(|(objectClass=domain)(objectclass=organizationalUnit))",
3137 attrs);
3138 if (!ADS_ERR_OK(status)) {
3139 return status;
3142 count = ads_count_replies(ads, res);
3143 if (count < 1) {
3144 ads_msgfree(ads, res);
3145 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3148 for (msg = ads_first_entry(ads, res); msg;
3149 msg = ads_next_entry(ads, msg)) {
3151 char *dn = NULL;
3153 dn = ads_get_dn(ads, msg);
3154 if (!dn) {
3155 ads_msgfree(ads, res);
3156 return ADS_ERROR(LDAP_NO_MEMORY);
3159 if (!add_string_to_array(mem_ctx, dn,
3160 (const char ***)ous,
3161 (int *)num_ous)) {
3162 ads_memfree(ads, dn);
3163 ads_msgfree(ads, res);
3164 return ADS_ERROR(LDAP_NO_MEMORY);
3167 ads_memfree(ads, dn);
3170 ads_msgfree(ads, res);
3172 return status;
3177 * pull a DOM_SID from an extended dn string
3178 * @param mem_ctx TALLOC_CTX
3179 * @param extended_dn string
3180 * @param flags string type of extended_dn
3181 * @param sid pointer to a DOM_SID
3182 * @return NT_STATUS_OK on success,
3183 * NT_INVALID_PARAMETER on error,
3184 * NT_STATUS_NOT_FOUND if no SID present
3186 ADS_STATUS ads_get_sid_from_extended_dn(TALLOC_CTX *mem_ctx,
3187 const char *extended_dn,
3188 enum ads_extended_dn_flags flags,
3189 DOM_SID *sid)
3191 char *p, *q, *dn;
3193 if (!extended_dn) {
3194 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3197 /* otherwise extended_dn gets stripped off */
3198 if ((dn = talloc_strdup(mem_ctx, extended_dn)) == NULL) {
3199 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3202 * ADS_EXTENDED_DN_HEX_STRING:
3203 * <GUID=238e1963cb390f4bb032ba0105525a29>;<SID=010500000000000515000000bb68c8fd6b61b427572eb04556040000>;CN=gd,OU=berlin,OU=suse,DC=ber,DC=suse,DC=de
3205 * ADS_EXTENDED_DN_STRING (only with w2k3):
3206 * <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
3208 * Object with no SID, such as an Exchange Public Folder
3209 * <GUID=28907fb4bdf6854993e7f0a10b504e7c>;CN=public,CN=Microsoft Exchange System Objects,DC=sd2k3ms,DC=west,DC=isilon,DC=com
3212 p = strchr(dn, ';');
3213 if (!p) {
3214 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3217 if (strncmp(p, ";<SID=", strlen(";<SID=")) != 0) {
3218 DEBUG(5,("No SID present in extended dn\n"));
3219 return ADS_ERROR_NT(NT_STATUS_NOT_FOUND);
3222 p += strlen(";<SID=");
3224 q = strchr(p, '>');
3225 if (!q) {
3226 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3229 *q = '\0';
3231 DEBUG(100,("ads_get_sid_from_extended_dn: sid string is %s\n", p));
3233 switch (flags) {
3235 case ADS_EXTENDED_DN_STRING:
3236 if (!string_to_sid(sid, p)) {
3237 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3239 break;
3240 case ADS_EXTENDED_DN_HEX_STRING: {
3241 fstring buf;
3242 size_t buf_len;
3244 buf_len = strhex_to_str(buf, sizeof(buf), p, strlen(p));
3245 if (buf_len == 0) {
3246 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3249 if (!sid_parse(buf, buf_len, sid)) {
3250 DEBUG(10,("failed to parse sid\n"));
3251 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3253 break;
3255 default:
3256 DEBUG(10,("unknown extended dn format\n"));
3257 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3260 return ADS_ERROR_NT(NT_STATUS_OK);
3264 * pull an array of DOM_SIDs from a ADS result
3265 * @param ads connection to ads server
3266 * @param mem_ctx TALLOC_CTX for allocating sid array
3267 * @param msg Results of search
3268 * @param field Attribute to retrieve
3269 * @param flags string type of extended_dn
3270 * @param sids pointer to sid array to allocate
3271 * @return the count of SIDs pulled
3273 int ads_pull_sids_from_extendeddn(ADS_STRUCT *ads,
3274 TALLOC_CTX *mem_ctx,
3275 LDAPMessage *msg,
3276 const char *field,
3277 enum ads_extended_dn_flags flags,
3278 DOM_SID **sids)
3280 int i;
3281 ADS_STATUS rc;
3282 size_t dn_count, ret_count = 0;
3283 char **dn_strings;
3285 if ((dn_strings = ads_pull_strings(ads, mem_ctx, msg, field,
3286 &dn_count)) == NULL) {
3287 return 0;
3290 (*sids) = TALLOC_ZERO_ARRAY(mem_ctx, DOM_SID, dn_count + 1);
3291 if (!(*sids)) {
3292 TALLOC_FREE(dn_strings);
3293 return 0;
3296 for (i=0; i<dn_count; i++) {
3297 rc = ads_get_sid_from_extended_dn(mem_ctx, dn_strings[i],
3298 flags, &(*sids)[i]);
3299 if (!ADS_ERR_OK(rc)) {
3300 if (NT_STATUS_EQUAL(ads_ntstatus(rc),
3301 NT_STATUS_NOT_FOUND)) {
3302 continue;
3304 else {
3305 TALLOC_FREE(*sids);
3306 TALLOC_FREE(dn_strings);
3307 return 0;
3310 ret_count++;
3313 TALLOC_FREE(dn_strings);
3315 return ret_count;
3318 /********************************************************************
3319 ********************************************************************/
3321 char* ads_get_dnshostname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3323 LDAPMessage *res = NULL;
3324 ADS_STATUS status;
3325 int count = 0;
3326 char *name = NULL;
3328 status = ads_find_machine_acct(ads, &res, global_myname());
3329 if (!ADS_ERR_OK(status)) {
3330 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
3331 global_myname()));
3332 goto out;
3335 if ( (count = ads_count_replies(ads, res)) != 1 ) {
3336 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
3337 goto out;
3340 if ( (name = ads_pull_string(ads, ctx, res, "dNSHostName")) == NULL ) {
3341 DEBUG(0,("ads_get_dnshostname: No dNSHostName attribute!\n"));
3344 out:
3345 ads_msgfree(ads, res);
3347 return name;
3350 /********************************************************************
3351 ********************************************************************/
3353 char* ads_get_upn( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3355 LDAPMessage *res = NULL;
3356 ADS_STATUS status;
3357 int count = 0;
3358 char *name = NULL;
3360 status = ads_find_machine_acct(ads, &res, machine_name);
3361 if (!ADS_ERR_OK(status)) {
3362 DEBUG(0,("ads_get_upn: Failed to find account for %s\n",
3363 global_myname()));
3364 goto out;
3367 if ( (count = ads_count_replies(ads, res)) != 1 ) {
3368 DEBUG(1,("ads_get_upn: %d entries returned!\n", count));
3369 goto out;
3372 if ( (name = ads_pull_string(ads, ctx, res, "userPrincipalName")) == NULL ) {
3373 DEBUG(2,("ads_get_upn: No userPrincipalName attribute!\n"));
3376 out:
3377 ads_msgfree(ads, res);
3379 return name;
3382 /********************************************************************
3383 ********************************************************************/
3385 char* ads_get_samaccountname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3387 LDAPMessage *res = NULL;
3388 ADS_STATUS status;
3389 int count = 0;
3390 char *name = NULL;
3392 status = ads_find_machine_acct(ads, &res, global_myname());
3393 if (!ADS_ERR_OK(status)) {
3394 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
3395 global_myname()));
3396 goto out;
3399 if ( (count = ads_count_replies(ads, res)) != 1 ) {
3400 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
3401 goto out;
3404 if ( (name = ads_pull_string(ads, ctx, res, "sAMAccountName")) == NULL ) {
3405 DEBUG(0,("ads_get_dnshostname: No sAMAccountName attribute!\n"));
3408 out:
3409 ads_msgfree(ads, res);
3411 return name;
3414 #if 0
3416 SAVED CODE - we used to join via ldap - remember how we did this. JRA.
3419 * Join a machine to a realm
3420 * Creates the machine account and sets the machine password
3421 * @param ads connection to ads server
3422 * @param machine name of host to add
3423 * @param org_unit Organizational unit to place machine in
3424 * @return status of join
3426 ADS_STATUS ads_join_realm(ADS_STRUCT *ads, const char *machine_name,
3427 uint32 account_type, const char *org_unit)
3429 ADS_STATUS status;
3430 LDAPMessage *res = NULL;
3431 char *machine;
3433 /* machine name must be lowercase */
3434 machine = SMB_STRDUP(machine_name);
3435 strlower_m(machine);
3438 status = ads_find_machine_acct(ads, (void **)&res, machine);
3439 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
3440 DEBUG(0, ("Host account for %s already exists - deleting old account\n", machine));
3441 status = ads_leave_realm(ads, machine);
3442 if (!ADS_ERR_OK(status)) {
3443 DEBUG(0, ("Failed to delete host '%s' from the '%s' realm.\n",
3444 machine, ads->config.realm));
3445 return status;
3449 status = ads_add_machine_acct(ads, machine, account_type, org_unit);
3450 if (!ADS_ERR_OK(status)) {
3451 DEBUG(0, ("ads_join_realm: ads_add_machine_acct failed (%s): %s\n", machine, ads_errstr(status)));
3452 SAFE_FREE(machine);
3453 return status;
3456 status = ads_find_machine_acct(ads, (void **)(void *)&res, machine);
3457 if (!ADS_ERR_OK(status)) {
3458 DEBUG(0, ("ads_join_realm: Host account test failed for machine %s\n", machine));
3459 SAFE_FREE(machine);
3460 return status;
3463 SAFE_FREE(machine);
3464 ads_msgfree(ads, res);
3466 return status;
3468 #endif
3471 * Delete a machine from the realm
3472 * @param ads connection to ads server
3473 * @param hostname Machine to remove
3474 * @return status of delete
3476 ADS_STATUS ads_leave_realm(ADS_STRUCT *ads, const char *hostname)
3478 ADS_STATUS status;
3479 void *msg;
3480 LDAPMessage *res;
3481 char *hostnameDN, *host;
3482 int rc;
3483 LDAPControl ldap_control;
3484 LDAPControl * pldap_control[2] = {NULL, NULL};
3486 pldap_control[0] = &ldap_control;
3487 memset(&ldap_control, 0, sizeof(LDAPControl));
3488 ldap_control.ldctl_oid = (char *)LDAP_SERVER_TREE_DELETE_OID;
3490 /* hostname must be lowercase */
3491 host = SMB_STRDUP(hostname);
3492 strlower_m(host);
3494 status = ads_find_machine_acct(ads, &res, host);
3495 if (!ADS_ERR_OK(status)) {
3496 DEBUG(0, ("Host account for %s does not exist.\n", host));
3497 SAFE_FREE(host);
3498 return status;
3501 msg = ads_first_entry(ads, res);
3502 if (!msg) {
3503 SAFE_FREE(host);
3504 return ADS_ERROR_SYSTEM(ENOENT);
3507 hostnameDN = ads_get_dn(ads, (LDAPMessage *)msg);
3509 rc = ldap_delete_ext_s(ads->ldap.ld, hostnameDN, pldap_control, NULL);
3510 if (rc) {
3511 DEBUG(3,("ldap_delete_ext_s failed with error code %d\n", rc));
3512 }else {
3513 DEBUG(3,("ldap_delete_ext_s succeeded with error code %d\n", rc));
3516 if (rc != LDAP_SUCCESS) {
3517 const char *attrs[] = { "cn", NULL };
3518 LDAPMessage *msg_sub;
3520 /* we only search with scope ONE, we do not expect any further
3521 * objects to be created deeper */
3523 status = ads_do_search_retry(ads, hostnameDN,
3524 LDAP_SCOPE_ONELEVEL,
3525 "(objectclass=*)", attrs, &res);
3527 if (!ADS_ERR_OK(status)) {
3528 SAFE_FREE(host);
3529 ads_memfree(ads, hostnameDN);
3530 return status;
3533 for (msg_sub = ads_first_entry(ads, res); msg_sub;
3534 msg_sub = ads_next_entry(ads, msg_sub)) {
3536 char *dn = NULL;
3538 if ((dn = ads_get_dn(ads, msg_sub)) == NULL) {
3539 SAFE_FREE(host);
3540 ads_memfree(ads, hostnameDN);
3541 return ADS_ERROR(LDAP_NO_MEMORY);
3544 status = ads_del_dn(ads, dn);
3545 if (!ADS_ERR_OK(status)) {
3546 DEBUG(3,("failed to delete dn %s: %s\n", dn, ads_errstr(status)));
3547 SAFE_FREE(host);
3548 ads_memfree(ads, dn);
3549 ads_memfree(ads, hostnameDN);
3550 return status;
3553 ads_memfree(ads, dn);
3556 /* there should be no subordinate objects anymore */
3557 status = ads_do_search_retry(ads, hostnameDN,
3558 LDAP_SCOPE_ONELEVEL,
3559 "(objectclass=*)", attrs, &res);
3561 if (!ADS_ERR_OK(status) || ( (ads_count_replies(ads, res)) > 0 ) ) {
3562 SAFE_FREE(host);
3563 ads_memfree(ads, hostnameDN);
3564 return status;
3567 /* delete hostnameDN now */
3568 status = ads_del_dn(ads, hostnameDN);
3569 if (!ADS_ERR_OK(status)) {
3570 SAFE_FREE(host);
3571 DEBUG(3,("failed to delete dn %s: %s\n", hostnameDN, ads_errstr(status)));
3572 ads_memfree(ads, hostnameDN);
3573 return status;
3577 ads_memfree(ads, hostnameDN);
3579 status = ads_find_machine_acct(ads, &res, host);
3580 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
3581 DEBUG(3, ("Failed to remove host account.\n"));
3582 SAFE_FREE(host);
3583 return status;
3586 SAFE_FREE(host);
3587 return status;
3591 * pull all token-sids from an LDAP dn
3592 * @param ads connection to ads server
3593 * @param mem_ctx TALLOC_CTX for allocating sid array
3594 * @param dn of LDAP object
3595 * @param user_sid pointer to DOM_SID (objectSid)
3596 * @param primary_group_sid pointer to DOM_SID (self composed)
3597 * @param sids pointer to sid array to allocate
3598 * @param num_sids counter of SIDs pulled
3599 * @return status of token query
3601 ADS_STATUS ads_get_tokensids(ADS_STRUCT *ads,
3602 TALLOC_CTX *mem_ctx,
3603 const char *dn,
3604 DOM_SID *user_sid,
3605 DOM_SID *primary_group_sid,
3606 DOM_SID **sids,
3607 size_t *num_sids)
3609 ADS_STATUS status;
3610 LDAPMessage *res = NULL;
3611 int count = 0;
3612 size_t tmp_num_sids;
3613 DOM_SID *tmp_sids;
3614 DOM_SID tmp_user_sid;
3615 DOM_SID tmp_primary_group_sid;
3616 uint32 pgid;
3617 const char *attrs[] = {
3618 "objectSid",
3619 "tokenGroups",
3620 "primaryGroupID",
3621 NULL
3624 status = ads_search_retry_dn(ads, &res, dn, attrs);
3625 if (!ADS_ERR_OK(status)) {
3626 return status;
3629 count = ads_count_replies(ads, res);
3630 if (count != 1) {
3631 ads_msgfree(ads, res);
3632 return ADS_ERROR_LDAP(LDAP_NO_SUCH_OBJECT);
3635 if (!ads_pull_sid(ads, res, "objectSid", &tmp_user_sid)) {
3636 ads_msgfree(ads, res);
3637 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3640 if (!ads_pull_uint32(ads, res, "primaryGroupID", &pgid)) {
3641 ads_msgfree(ads, res);
3642 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3646 /* hack to compose the primary group sid without knowing the
3647 * domsid */
3649 DOM_SID domsid;
3650 uint32 dummy_rid;
3652 sid_copy(&domsid, &tmp_user_sid);
3654 if (!sid_split_rid(&domsid, &dummy_rid)) {
3655 ads_msgfree(ads, res);
3656 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3659 if (!sid_compose(&tmp_primary_group_sid, &domsid, pgid)) {
3660 ads_msgfree(ads, res);
3661 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3665 tmp_num_sids = ads_pull_sids(ads, mem_ctx, res, "tokenGroups", &tmp_sids);
3667 if (tmp_num_sids == 0 || !tmp_sids) {
3668 ads_msgfree(ads, res);
3669 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3672 if (num_sids) {
3673 *num_sids = tmp_num_sids;
3676 if (sids) {
3677 *sids = tmp_sids;
3680 if (user_sid) {
3681 *user_sid = tmp_user_sid;
3684 if (primary_group_sid) {
3685 *primary_group_sid = tmp_primary_group_sid;
3688 DEBUG(10,("ads_get_tokensids: returned %d sids\n", (int)tmp_num_sids + 2));
3690 ads_msgfree(ads, res);
3691 return ADS_ERROR_LDAP(LDAP_SUCCESS);
3695 * Find a sAMAccoutName in LDAP
3696 * @param ads connection to ads server
3697 * @param mem_ctx TALLOC_CTX for allocating sid array
3698 * @param samaccountname to search
3699 * @param uac_ret uint32 pointer userAccountControl attribute value
3700 * @param dn_ret pointer to dn
3701 * @return status of token query
3703 ADS_STATUS ads_find_samaccount(ADS_STRUCT *ads,
3704 TALLOC_CTX *mem_ctx,
3705 const char *samaccountname,
3706 uint32 *uac_ret,
3707 const char **dn_ret)
3709 ADS_STATUS status;
3710 const char *attrs[] = { "userAccountControl", NULL };
3711 const char *filter;
3712 LDAPMessage *res = NULL;
3713 char *dn = NULL;
3714 uint32 uac = 0;
3716 filter = talloc_asprintf(mem_ctx, "(&(objectclass=user)(sAMAccountName=%s))",
3717 samaccountname);
3718 if (filter == NULL) {
3719 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
3720 goto out;
3723 status = ads_do_search_all(ads, ads->config.bind_path,
3724 LDAP_SCOPE_SUBTREE,
3725 filter, attrs, &res);
3727 if (!ADS_ERR_OK(status)) {
3728 goto out;
3731 if (ads_count_replies(ads, res) != 1) {
3732 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3733 goto out;
3736 dn = ads_get_dn(ads, res);
3737 if (dn == NULL) {
3738 status = ADS_ERROR(LDAP_NO_MEMORY);
3739 goto out;
3742 if (!ads_pull_uint32(ads, res, "userAccountControl", &uac)) {
3743 status = ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
3744 goto out;
3747 if (uac_ret) {
3748 *uac_ret = uac;
3751 if (dn_ret) {
3752 *dn_ret = talloc_strdup(mem_ctx, dn);
3753 if (!*dn_ret) {
3754 status = ADS_ERROR(LDAP_NO_MEMORY);
3755 goto out;
3758 out:
3759 ads_memfree(ads, dn);
3760 ads_msgfree(ads, res);
3762 return status;
3766 * find our configuration path
3767 * @param ads connection to ads server
3768 * @param mem_ctx Pointer to talloc context
3769 * @param config_path Pointer to the config path
3770 * @return status of search
3772 ADS_STATUS ads_config_path(ADS_STRUCT *ads,
3773 TALLOC_CTX *mem_ctx,
3774 char **config_path)
3776 ADS_STATUS status;
3777 LDAPMessage *res = NULL;
3778 const char *config_context = NULL;
3779 const char *attrs[] = { "configurationNamingContext", NULL };
3781 status = ads_do_search(ads, "", LDAP_SCOPE_BASE,
3782 "(objectclass=*)", attrs, &res);
3783 if (!ADS_ERR_OK(status)) {
3784 return status;
3787 config_context = ads_pull_string(ads, mem_ctx, res,
3788 "configurationNamingContext");
3789 ads_msgfree(ads, res);
3790 if (!config_context) {
3791 return ADS_ERROR(LDAP_NO_MEMORY);
3794 if (config_path) {
3795 *config_path = talloc_strdup(mem_ctx, config_context);
3796 if (!*config_path) {
3797 return ADS_ERROR(LDAP_NO_MEMORY);
3801 return ADS_ERROR(LDAP_SUCCESS);
3805 * find the displayName of an extended right
3806 * @param ads connection to ads server
3807 * @param config_path The config path
3808 * @param mem_ctx Pointer to talloc context
3809 * @param GUID struct of the rightsGUID
3810 * @return status of search
3812 const char *ads_get_extended_right_name_by_guid(ADS_STRUCT *ads,
3813 const char *config_path,
3814 TALLOC_CTX *mem_ctx,
3815 const struct GUID *rights_guid)
3817 ADS_STATUS rc;
3818 LDAPMessage *res = NULL;
3819 char *expr = NULL;
3820 const char *attrs[] = { "displayName", NULL };
3821 const char *result = NULL;
3822 const char *path;
3824 if (!ads || !mem_ctx || !rights_guid) {
3825 goto done;
3828 expr = talloc_asprintf(mem_ctx, "(rightsGuid=%s)",
3829 smb_uuid_string(mem_ctx, *rights_guid));
3830 if (!expr) {
3831 goto done;
3834 path = talloc_asprintf(mem_ctx, "cn=Extended-Rights,%s", config_path);
3835 if (!path) {
3836 goto done;
3839 rc = ads_do_search_retry(ads, path, LDAP_SCOPE_SUBTREE,
3840 expr, attrs, &res);
3841 if (!ADS_ERR_OK(rc)) {
3842 goto done;
3845 if (ads_count_replies(ads, res) != 1) {
3846 goto done;
3849 result = ads_pull_string(ads, mem_ctx, res, "displayName");
3851 done:
3852 ads_msgfree(ads, res);
3853 return result;
3858 * verify or build and verify an account ou
3859 * @param mem_ctx Pointer to talloc context
3860 * @param ads connection to ads server
3861 * @param account_ou
3862 * @return status of search
3865 ADS_STATUS ads_check_ou_dn(TALLOC_CTX *mem_ctx,
3866 ADS_STRUCT *ads,
3867 const char **account_ou)
3869 struct ldb_dn *name_dn = NULL;
3870 const char *name = NULL;
3871 char *ou_string = NULL;
3873 name_dn = ldb_dn_explode(mem_ctx, *account_ou);
3874 if (name_dn) {
3875 return ADS_SUCCESS;
3878 ou_string = ads_ou_string(ads, *account_ou);
3879 if (!ou_string) {
3880 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX);
3883 name = talloc_asprintf(mem_ctx, "%s,%s", ou_string,
3884 ads->config.bind_path);
3885 SAFE_FREE(ou_string);
3886 if (!name) {
3887 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3890 name_dn = ldb_dn_explode(mem_ctx, name);
3891 if (!name_dn) {
3892 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX);
3895 *account_ou = talloc_strdup(mem_ctx, name);
3896 if (!*account_ou) {
3897 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3900 return ADS_SUCCESS;
3903 #endif