VERSION: Raise version number.
[Samba/gbeck.git] / source / libads / ldap.c
blob291932d51a60e6f60babc0fcb211d4c08b91bdb0
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;
121 return result;
124 /**********************************************
125 Do client and server sitename match ?
126 **********************************************/
128 bool ads_sitename_match(ADS_STRUCT *ads)
130 if (ads->config.server_site_name == NULL &&
131 ads->config.client_site_name == NULL ) {
132 DEBUG(10,("ads_sitename_match: both null\n"));
133 return True;
135 if (ads->config.server_site_name &&
136 ads->config.client_site_name &&
137 strequal(ads->config.server_site_name,
138 ads->config.client_site_name)) {
139 DEBUG(10,("ads_sitename_match: name %s match\n", ads->config.server_site_name));
140 return True;
142 DEBUG(10,("ads_sitename_match: no match between server: %s and client: %s\n",
143 ads->config.server_site_name ? ads->config.server_site_name : "NULL",
144 ads->config.client_site_name ? ads->config.client_site_name : "NULL"));
145 return False;
148 /**********************************************
149 Is this the closest DC ?
150 **********************************************/
152 bool ads_closest_dc(ADS_STRUCT *ads)
154 if (ads->config.flags & NBT_SERVER_CLOSEST) {
155 DEBUG(10,("ads_closest_dc: NBT_SERVER_CLOSEST flag set\n"));
156 return True;
159 /* not sure if this can ever happen */
160 if (ads_sitename_match(ads)) {
161 DEBUG(10,("ads_closest_dc: NBT_SERVER_CLOSEST flag not set but sites match\n"));
162 return True;
165 if (ads->config.client_site_name == NULL) {
166 DEBUG(10,("ads_closest_dc: client belongs to no site\n"));
167 return True;
170 DEBUG(10,("ads_closest_dc: %s is not the closest DC\n",
171 ads->config.ldap_server_name));
173 return False;
178 try a connection to a given ldap server, returning True and setting the servers IP
179 in the ads struct if successful
181 static bool ads_try_connect(ADS_STRUCT *ads, const char *server, bool gc)
183 char *srv;
184 struct NETLOGON_SAM_LOGON_RESPONSE_EX cldap_reply;
185 TALLOC_CTX *mem_ctx = NULL;
186 bool ret = false;
188 if (!server || !*server) {
189 return False;
192 DEBUG(5,("ads_try_connect: sending CLDAP request to %s (realm: %s)\n",
193 server, ads->server.realm));
195 mem_ctx = talloc_init("ads_try_connect");
196 if (!mem_ctx) {
197 DEBUG(0,("out of memory\n"));
198 return false;
201 /* this copes with inet_ntoa brokenness */
203 srv = SMB_STRDUP(server);
205 ZERO_STRUCT( cldap_reply );
207 if ( !ads_cldap_netlogon_5(mem_ctx, srv, ads->server.realm, &cldap_reply ) ) {
208 DEBUG(3,("ads_try_connect: CLDAP request %s failed.\n", srv));
209 ret = false;
210 goto out;
213 /* Check the CLDAP reply flags */
215 if ( !(cldap_reply.server_type & NBT_SERVER_LDAP) ) {
216 DEBUG(1,("ads_try_connect: %s's CLDAP reply says it is not an LDAP server!\n",
217 srv));
218 ret = false;
219 goto out;
222 /* Fill in the ads->config values */
224 SAFE_FREE(ads->config.realm);
225 SAFE_FREE(ads->config.bind_path);
226 SAFE_FREE(ads->config.ldap_server_name);
227 SAFE_FREE(ads->config.server_site_name);
228 SAFE_FREE(ads->config.client_site_name);
229 SAFE_FREE(ads->server.workgroup);
231 ads->config.flags = cldap_reply.server_type;
232 ads->config.ldap_server_name = SMB_STRDUP(cldap_reply.pdc_dns_name);
233 ads->config.realm = SMB_STRDUP(cldap_reply.dns_domain);
234 strupper_m(ads->config.realm);
235 ads->config.bind_path = ads_build_dn(ads->config.realm);
236 if (*cldap_reply.server_site) {
237 ads->config.server_site_name =
238 SMB_STRDUP(cldap_reply.server_site);
240 if (*cldap_reply.client_site) {
241 ads->config.client_site_name =
242 SMB_STRDUP(cldap_reply.client_site);
244 ads->server.workgroup = SMB_STRDUP(cldap_reply.domain);
246 ads->ldap.port = gc ? LDAP_GC_PORT : LDAP_PORT;
247 if (!interpret_string_addr(&ads->ldap.ss, srv, 0)) {
248 DEBUG(1,("ads_try_connect: unable to convert %s "
249 "to an address\n",
250 srv));
251 ret = false;
252 goto out;
255 /* Store our site name. */
256 sitename_store( cldap_reply.domain, cldap_reply.client_site);
257 sitename_store( cldap_reply.dns_domain, cldap_reply.client_site);
259 ret = true;
260 out:
261 SAFE_FREE(srv);
262 TALLOC_FREE(mem_ctx);
264 return ret;
267 /**********************************************************************
268 Try to find an AD dc using our internal name resolution routines
269 Try the realm first and then then workgroup name if netbios is not
270 disabled
271 **********************************************************************/
273 static NTSTATUS ads_find_dc(ADS_STRUCT *ads)
275 const char *c_domain;
276 const char *c_realm;
277 int count, i=0;
278 struct ip_service *ip_list;
279 const char *realm;
280 const char *domain;
281 bool got_realm = False;
282 bool use_own_domain = False;
283 char *sitename;
284 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
286 /* if the realm and workgroup are both empty, assume they are ours */
288 /* realm */
289 c_realm = ads->server.realm;
291 if ( !c_realm || !*c_realm ) {
292 /* special case where no realm and no workgroup means our own */
293 if ( !ads->server.workgroup || !*ads->server.workgroup ) {
294 use_own_domain = True;
295 c_realm = lp_realm();
299 if (c_realm && *c_realm)
300 got_realm = True;
302 /* we need to try once with the realm name and fallback to the
303 netbios domain name if we fail (if netbios has not been disabled */
305 if ( !got_realm && !lp_disable_netbios() ) {
306 c_realm = ads->server.workgroup;
307 if (!c_realm || !*c_realm) {
308 if ( use_own_domain )
309 c_realm = lp_workgroup();
313 if ( !c_realm || !*c_realm ) {
314 DEBUG(0,("ads_find_dc: no realm or workgroup! Don't know what to do\n"));
315 return NT_STATUS_INVALID_PARAMETER; /* rather need MISSING_PARAMETER ... */
318 if ( use_own_domain ) {
319 c_domain = lp_workgroup();
320 } else {
321 c_domain = ads->server.workgroup;
324 realm = c_realm;
325 domain = c_domain;
328 * In case of LDAP we use get_dc_name() as that
329 * creates the custom krb5.conf file
331 if (!(ads->auth.flags & ADS_AUTH_NO_BIND)) {
332 fstring srv_name;
333 struct sockaddr_storage ip_out;
335 DEBUG(6,("ads_find_dc: (ldap) looking for %s '%s'\n",
336 (got_realm ? "realm" : "domain"), realm));
338 if (get_dc_name(domain, realm, srv_name, &ip_out)) {
340 * we call ads_try_connect() to fill in the
341 * ads->config details
343 if (ads_try_connect(ads, srv_name, false)) {
344 return NT_STATUS_OK;
348 return NT_STATUS_NO_LOGON_SERVERS;
351 sitename = sitename_fetch(realm);
353 again:
355 DEBUG(6,("ads_find_dc: (cldap) looking for %s '%s'\n",
356 (got_realm ? "realm" : "domain"), realm));
358 status = get_sorted_dc_list(realm, sitename, &ip_list, &count, got_realm);
359 if (!NT_STATUS_IS_OK(status)) {
360 /* fall back to netbios if we can */
361 if ( got_realm && !lp_disable_netbios() ) {
362 got_realm = False;
363 goto again;
366 SAFE_FREE(sitename);
367 return status;
370 /* if we fail this loop, then giveup since all the IP addresses returned were dead */
371 for ( i=0; i<count; i++ ) {
372 char server[INET6_ADDRSTRLEN];
374 print_sockaddr(server, sizeof(server), &ip_list[i].ss);
376 if ( !NT_STATUS_IS_OK(check_negative_conn_cache(realm, server)) )
377 continue;
379 if (!got_realm) {
380 /* realm in this case is a workgroup name. We need
381 to ignore any IP addresses in the negative connection
382 cache that match ip addresses returned in the ad realm
383 case. It sucks that I have to reproduce the logic above... */
384 c_realm = ads->server.realm;
385 if ( !c_realm || !*c_realm ) {
386 if ( !ads->server.workgroup || !*ads->server.workgroup ) {
387 c_realm = lp_realm();
390 if (c_realm && *c_realm &&
391 !NT_STATUS_IS_OK(check_negative_conn_cache(c_realm, server))) {
392 /* Ensure we add the workgroup name for this
393 IP address as negative too. */
394 add_failed_connection_entry( realm, server, NT_STATUS_UNSUCCESSFUL );
395 continue;
399 if ( ads_try_connect(ads, server, false) ) {
400 SAFE_FREE(ip_list);
401 SAFE_FREE(sitename);
402 return NT_STATUS_OK;
405 /* keep track of failures */
406 add_failed_connection_entry( realm, server, NT_STATUS_UNSUCCESSFUL );
409 SAFE_FREE(ip_list);
411 /* In case we failed to contact one of our closest DC on our site we
412 * need to try to find another DC, retry with a site-less SRV DNS query
413 * - Guenther */
415 if (sitename) {
416 DEBUG(1,("ads_find_dc: failed to find a valid DC on our site (%s), "
417 "trying to find another DC\n", sitename));
418 SAFE_FREE(sitename);
419 namecache_delete(realm, 0x1C);
420 goto again;
423 return NT_STATUS_NO_LOGON_SERVERS;
426 /*********************************************************************
427 *********************************************************************/
429 static NTSTATUS ads_lookup_site(void)
431 ADS_STRUCT *ads = NULL;
432 ADS_STATUS ads_status;
433 NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
435 ads = ads_init(lp_realm(), NULL, NULL);
436 if (!ads) {
437 return NT_STATUS_NO_MEMORY;
440 /* The NO_BIND here will find a DC and set the client site
441 but not establish the TCP connection */
443 ads->auth.flags = ADS_AUTH_NO_BIND;
444 ads_status = ads_connect(ads);
445 if (!ADS_ERR_OK(ads_status)) {
446 DEBUG(4, ("ads_lookup_site: ads_connect to our realm failed! (%s)\n",
447 ads_errstr(ads_status)));
449 nt_status = ads_ntstatus(ads_status);
451 if (ads) {
452 ads_destroy(&ads);
455 return nt_status;
458 /*********************************************************************
459 *********************************************************************/
461 static const char* host_dns_domain(const char *fqdn)
463 const char *p = fqdn;
465 /* go to next char following '.' */
467 if ((p = strchr_m(fqdn, '.')) != NULL) {
468 p++;
471 return p;
476 * Connect to the Global Catalog server
477 * @param ads Pointer to an existing ADS_STRUCT
478 * @return status of connection
480 * Simple wrapper around ads_connect() that fills in the
481 * GC ldap server information
484 ADS_STATUS ads_connect_gc(ADS_STRUCT *ads)
486 TALLOC_CTX *frame = talloc_stackframe();
487 struct dns_rr_srv *gcs_list;
488 int num_gcs;
489 char *realm = ads->server.realm;
490 NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
491 ADS_STATUS ads_status = ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
492 int i;
493 bool done = false;
494 char *sitename = NULL;
496 if (!realm)
497 realm = lp_realm();
499 if ((sitename = sitename_fetch(realm)) == NULL) {
500 ads_lookup_site();
501 sitename = sitename_fetch(realm);
504 do {
505 /* We try once with a sitename and once without
506 (unless we don't have a sitename and then we're
507 done */
509 if (sitename == NULL)
510 done = true;
512 nt_status = ads_dns_query_gcs(frame, realm, sitename,
513 &gcs_list, &num_gcs);
515 SAFE_FREE(sitename);
517 if (!NT_STATUS_IS_OK(nt_status)) {
518 ads_status = ADS_ERROR_NT(nt_status);
519 goto done;
522 /* Loop until we get a successful connection or have gone
523 through them all. When connecting a GC server, make sure that
524 the realm is the server's DNS name and not the forest root */
526 for (i=0; i<num_gcs; i++) {
527 ads->server.gc = true;
528 ads->server.ldap_server = SMB_STRDUP(gcs_list[i].hostname);
529 ads->server.realm = SMB_STRDUP(host_dns_domain(ads->server.ldap_server));
530 ads_status = ads_connect(ads);
531 if (ADS_ERR_OK(ads_status)) {
532 /* Reset the bind_dn to "". A Global Catalog server
533 may host multiple domain trees in a forest.
534 Windows 2003 GC server will accept "" as the search
535 path to imply search all domain trees in the forest */
537 SAFE_FREE(ads->config.bind_path);
538 ads->config.bind_path = SMB_STRDUP("");
541 goto done;
543 SAFE_FREE(ads->server.ldap_server);
544 SAFE_FREE(ads->server.realm);
547 TALLOC_FREE(gcs_list);
548 num_gcs = 0;
549 } while (!done);
551 done:
552 SAFE_FREE(sitename);
553 talloc_destroy(frame);
555 return ads_status;
560 * Connect to the LDAP server
561 * @param ads Pointer to an existing ADS_STRUCT
562 * @return status of connection
564 ADS_STATUS ads_connect(ADS_STRUCT *ads)
566 int version = LDAP_VERSION3;
567 ADS_STATUS status;
568 NTSTATUS ntstatus;
569 char addr[INET6_ADDRSTRLEN];
571 ZERO_STRUCT(ads->ldap);
572 ads->ldap.last_attempt = time(NULL);
573 ads->ldap.wrap_type = ADS_SASLWRAP_TYPE_PLAIN;
575 /* try with a user specified server */
577 if (DEBUGLEVEL >= 11) {
578 char *s = NDR_PRINT_STRUCT_STRING(talloc_tos(), ads_struct, ads);
579 DEBUG(11,("ads_connect: entering\n"));
580 DEBUGADD(11,("%s\n", s));
581 TALLOC_FREE(s);
584 if (ads->server.ldap_server &&
585 ads_try_connect(ads, ads->server.ldap_server, ads->server.gc)) {
586 goto got_connection;
589 ntstatus = ads_find_dc(ads);
590 if (NT_STATUS_IS_OK(ntstatus)) {
591 goto got_connection;
594 status = ADS_ERROR_NT(ntstatus);
595 goto out;
597 got_connection:
599 print_sockaddr(addr, sizeof(addr), &ads->ldap.ss);
600 DEBUG(3,("Successfully contacted LDAP server %s\n", addr));
602 if (!ads->auth.user_name) {
603 /* Must use the userPrincipalName value here or sAMAccountName
604 and not servicePrincipalName; found by Guenther Deschner */
606 if (asprintf(&ads->auth.user_name, "%s$", global_myname() ) == -1) {
607 DEBUG(0,("ads_connect: asprintf fail.\n"));
608 ads->auth.user_name = NULL;
612 if (!ads->auth.realm) {
613 ads->auth.realm = SMB_STRDUP(ads->config.realm);
616 if (!ads->auth.kdc_server) {
617 print_sockaddr(addr, sizeof(addr), &ads->ldap.ss);
618 ads->auth.kdc_server = SMB_STRDUP(addr);
621 #if KRB5_DNS_HACK
622 /* this is a really nasty hack to avoid ADS DNS problems. It needs a patch
623 to MIT kerberos to work (tridge) */
625 char *env = NULL;
626 if (asprintf(&env, "KRB5_KDC_ADDRESS_%s", ads->config.realm) > 0) {
627 setenv(env, ads->auth.kdc_server, 1);
628 free(env);
631 #endif
633 /* If the caller() requested no LDAP bind, then we are done */
635 if (ads->auth.flags & ADS_AUTH_NO_BIND) {
636 status = ADS_SUCCESS;
637 goto out;
640 ads->ldap.mem_ctx = talloc_init("ads LDAP connection memory");
641 if (!ads->ldap.mem_ctx) {
642 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
643 goto out;
646 /* Otherwise setup the TCP LDAP session */
648 ads->ldap.ld = ldap_open_with_timeout(ads->config.ldap_server_name,
649 ads->ldap.port, lp_ldap_timeout());
650 if (ads->ldap.ld == NULL) {
651 status = ADS_ERROR(LDAP_OPERATIONS_ERROR);
652 goto out;
654 DEBUG(3,("Connected to LDAP server %s\n", ads->config.ldap_server_name));
656 /* cache the successful connection for workgroup and realm */
657 if (ads_closest_dc(ads)) {
658 saf_store( ads->server.workgroup, ads->config.ldap_server_name);
659 saf_store( ads->server.realm, ads->config.ldap_server_name);
662 ldap_set_option(ads->ldap.ld, LDAP_OPT_PROTOCOL_VERSION, &version);
664 status = ADS_ERROR(smb_ldap_start_tls(ads->ldap.ld, version));
665 if (!ADS_ERR_OK(status)) {
666 goto out;
669 /* fill in the current time and offsets */
671 status = ads_current_time( ads );
672 if ( !ADS_ERR_OK(status) ) {
673 goto out;
676 /* Now do the bind */
678 if (ads->auth.flags & ADS_AUTH_ANON_BIND) {
679 status = ADS_ERROR(ldap_simple_bind_s(ads->ldap.ld, NULL, NULL));
680 goto out;
683 if (ads->auth.flags & ADS_AUTH_SIMPLE_BIND) {
684 status = ADS_ERROR(ldap_simple_bind_s(ads->ldap.ld, ads->auth.user_name, ads->auth.password));
685 goto out;
688 status = ads_sasl_bind(ads);
690 out:
691 if (DEBUGLEVEL >= 11) {
692 char *s = NDR_PRINT_STRUCT_STRING(talloc_tos(), ads_struct, ads);
693 DEBUG(11,("ads_connect: leaving with: %s\n",
694 ads_errstr(status)));
695 DEBUGADD(11,("%s\n", s));
696 TALLOC_FREE(s);
699 return status;
703 * Connect to the LDAP server using given credentials
704 * @param ads Pointer to an existing ADS_STRUCT
705 * @return status of connection
707 ADS_STATUS ads_connect_user_creds(ADS_STRUCT *ads)
709 ads->auth.flags |= ADS_AUTH_USER_CREDS;
711 return ads_connect(ads);
715 * Disconnect the LDAP server
716 * @param ads Pointer to an existing ADS_STRUCT
718 void ads_disconnect(ADS_STRUCT *ads)
720 if (ads->ldap.ld) {
721 ldap_unbind(ads->ldap.ld);
722 ads->ldap.ld = NULL;
724 if (ads->ldap.wrap_ops && ads->ldap.wrap_ops->disconnect) {
725 ads->ldap.wrap_ops->disconnect(ads);
727 if (ads->ldap.mem_ctx) {
728 talloc_free(ads->ldap.mem_ctx);
730 ZERO_STRUCT(ads->ldap);
734 Duplicate a struct berval into talloc'ed memory
736 static struct berval *dup_berval(TALLOC_CTX *ctx, const struct berval *in_val)
738 struct berval *value;
740 if (!in_val) return NULL;
742 value = TALLOC_ZERO_P(ctx, struct berval);
743 if (value == NULL)
744 return NULL;
745 if (in_val->bv_len == 0) return value;
747 value->bv_len = in_val->bv_len;
748 value->bv_val = (char *)TALLOC_MEMDUP(ctx, in_val->bv_val,
749 in_val->bv_len);
750 return value;
754 Make a values list out of an array of (struct berval *)
756 static struct berval **ads_dup_values(TALLOC_CTX *ctx,
757 const struct berval **in_vals)
759 struct berval **values;
760 int i;
762 if (!in_vals) return NULL;
763 for (i=0; in_vals[i]; i++)
764 ; /* count values */
765 values = TALLOC_ZERO_ARRAY(ctx, struct berval *, i+1);
766 if (!values) return NULL;
768 for (i=0; in_vals[i]; i++) {
769 values[i] = dup_berval(ctx, in_vals[i]);
771 return values;
775 UTF8-encode a values list out of an array of (char *)
777 static char **ads_push_strvals(TALLOC_CTX *ctx, const char **in_vals)
779 char **values;
780 int i;
781 size_t size;
783 if (!in_vals) return NULL;
784 for (i=0; in_vals[i]; i++)
785 ; /* count values */
786 values = TALLOC_ZERO_ARRAY(ctx, char *, i+1);
787 if (!values) return NULL;
789 for (i=0; in_vals[i]; i++) {
790 if (!push_utf8_talloc(ctx, &values[i], in_vals[i], &size)) {
791 TALLOC_FREE(values);
792 return NULL;
795 return values;
799 Pull a (char *) array out of a UTF8-encoded values list
801 static char **ads_pull_strvals(TALLOC_CTX *ctx, const char **in_vals)
803 char **values;
804 int i;
805 size_t converted_size;
807 if (!in_vals) return NULL;
808 for (i=0; in_vals[i]; i++)
809 ; /* count values */
810 values = TALLOC_ZERO_ARRAY(ctx, char *, i+1);
811 if (!values) return NULL;
813 for (i=0; in_vals[i]; i++) {
814 if (!pull_utf8_talloc(ctx, &values[i], in_vals[i],
815 &converted_size)) {
816 DEBUG(0,("ads_pull_strvals: pull_utf8_talloc failed: "
817 "%s", strerror(errno)));
820 return values;
824 * Do a search with paged results. cookie must be null on the first
825 * call, and then returned on each subsequent call. It will be null
826 * again when the entire search is complete
827 * @param ads connection to ads server
828 * @param bind_path Base dn for the search
829 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
830 * @param expr Search expression - specified in local charset
831 * @param attrs Attributes to retrieve - specified in utf8 or ascii
832 * @param res ** which will contain results - free res* with ads_msgfree()
833 * @param count Number of entries retrieved on this page
834 * @param cookie The paged results cookie to be returned on subsequent calls
835 * @return status of search
837 static ADS_STATUS ads_do_paged_search_args(ADS_STRUCT *ads,
838 const char *bind_path,
839 int scope, const char *expr,
840 const char **attrs, void *args,
841 LDAPMessage **res,
842 int *count, struct berval **cookie)
844 int rc, i, version;
845 char *utf8_expr, *utf8_path, **search_attrs;
846 size_t converted_size;
847 LDAPControl PagedResults, NoReferrals, ExternalCtrl, *controls[4], **rcontrols;
848 BerElement *cookie_be = NULL;
849 struct berval *cookie_bv= NULL;
850 BerElement *ext_be = NULL;
851 struct berval *ext_bv= NULL;
853 TALLOC_CTX *ctx;
854 ads_control *external_control = (ads_control *) args;
856 *res = NULL;
858 if (!(ctx = talloc_init("ads_do_paged_search_args")))
859 return ADS_ERROR(LDAP_NO_MEMORY);
861 /* 0 means the conversion worked but the result was empty
862 so we only fail if it's -1. In any case, it always
863 at least nulls out the dest */
864 if (!push_utf8_talloc(ctx, &utf8_expr, expr, &converted_size) ||
865 !push_utf8_talloc(ctx, &utf8_path, bind_path, &converted_size))
867 rc = LDAP_NO_MEMORY;
868 goto done;
871 if (!attrs || !(*attrs))
872 search_attrs = NULL;
873 else {
874 /* This would be the utf8-encoded version...*/
875 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
876 if (!(str_list_copy(talloc_tos(), &search_attrs, attrs))) {
877 rc = LDAP_NO_MEMORY;
878 goto done;
882 /* Paged results only available on ldap v3 or later */
883 ldap_get_option(ads->ldap.ld, LDAP_OPT_PROTOCOL_VERSION, &version);
884 if (version < LDAP_VERSION3) {
885 rc = LDAP_NOT_SUPPORTED;
886 goto done;
889 cookie_be = ber_alloc_t(LBER_USE_DER);
890 if (*cookie) {
891 ber_printf(cookie_be, "{iO}", (ber_int_t) 1000, *cookie);
892 ber_bvfree(*cookie); /* don't need it from last time */
893 *cookie = NULL;
894 } else {
895 ber_printf(cookie_be, "{io}", (ber_int_t) 1000, "", 0);
897 ber_flatten(cookie_be, &cookie_bv);
898 PagedResults.ldctl_oid = CONST_DISCARD(char *, ADS_PAGE_CTL_OID);
899 PagedResults.ldctl_iscritical = (char) 1;
900 PagedResults.ldctl_value.bv_len = cookie_bv->bv_len;
901 PagedResults.ldctl_value.bv_val = cookie_bv->bv_val;
903 NoReferrals.ldctl_oid = CONST_DISCARD(char *, ADS_NO_REFERRALS_OID);
904 NoReferrals.ldctl_iscritical = (char) 0;
905 NoReferrals.ldctl_value.bv_len = 0;
906 NoReferrals.ldctl_value.bv_val = CONST_DISCARD(char *, "");
908 if (external_control &&
909 (strequal(external_control->control, ADS_EXTENDED_DN_OID) ||
910 strequal(external_control->control, ADS_SD_FLAGS_OID))) {
912 ExternalCtrl.ldctl_oid = CONST_DISCARD(char *, external_control->control);
913 ExternalCtrl.ldctl_iscritical = (char) external_control->critical;
915 /* win2k does not accept a ldctl_value beeing passed in */
917 if (external_control->val != 0) {
919 if ((ext_be = ber_alloc_t(LBER_USE_DER)) == NULL ) {
920 rc = LDAP_NO_MEMORY;
921 goto done;
924 if ((ber_printf(ext_be, "{i}", (ber_int_t) external_control->val)) == -1) {
925 rc = LDAP_NO_MEMORY;
926 goto done;
928 if ((ber_flatten(ext_be, &ext_bv)) == -1) {
929 rc = LDAP_NO_MEMORY;
930 goto done;
933 ExternalCtrl.ldctl_value.bv_len = ext_bv->bv_len;
934 ExternalCtrl.ldctl_value.bv_val = ext_bv->bv_val;
936 } else {
937 ExternalCtrl.ldctl_value.bv_len = 0;
938 ExternalCtrl.ldctl_value.bv_val = NULL;
941 controls[0] = &NoReferrals;
942 controls[1] = &PagedResults;
943 controls[2] = &ExternalCtrl;
944 controls[3] = NULL;
946 } else {
947 controls[0] = &NoReferrals;
948 controls[1] = &PagedResults;
949 controls[2] = NULL;
952 /* we need to disable referrals as the openldap libs don't
953 handle them and paged results at the same time. Using them
954 together results in the result record containing the server
955 page control being removed from the result list (tridge/jmcd)
957 leaving this in despite the control that says don't generate
958 referrals, in case the server doesn't support it (jmcd)
960 ldap_set_option(ads->ldap.ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
962 rc = ldap_search_with_timeout(ads->ldap.ld, utf8_path, scope, utf8_expr,
963 search_attrs, 0, controls,
964 NULL, LDAP_NO_LIMIT,
965 (LDAPMessage **)res);
967 ber_free(cookie_be, 1);
968 ber_bvfree(cookie_bv);
970 if (rc) {
971 DEBUG(3,("ads_do_paged_search_args: ldap_search_with_timeout(%s) -> %s\n", expr,
972 ldap_err2string(rc)));
973 goto done;
976 rc = ldap_parse_result(ads->ldap.ld, *res, NULL, NULL, NULL,
977 NULL, &rcontrols, 0);
979 if (!rcontrols) {
980 goto done;
983 for (i=0; rcontrols[i]; i++) {
984 if (strcmp(ADS_PAGE_CTL_OID, rcontrols[i]->ldctl_oid) == 0) {
985 cookie_be = ber_init(&rcontrols[i]->ldctl_value);
986 ber_scanf(cookie_be,"{iO}", (ber_int_t *) count,
987 &cookie_bv);
988 /* the berval is the cookie, but must be freed when
989 it is all done */
990 if (cookie_bv->bv_len) /* still more to do */
991 *cookie=ber_bvdup(cookie_bv);
992 else
993 *cookie=NULL;
994 ber_bvfree(cookie_bv);
995 ber_free(cookie_be, 1);
996 break;
999 ldap_controls_free(rcontrols);
1001 done:
1002 talloc_destroy(ctx);
1004 if (ext_be) {
1005 ber_free(ext_be, 1);
1008 if (ext_bv) {
1009 ber_bvfree(ext_bv);
1012 /* if/when we decide to utf8-encode attrs, take out this next line */
1013 TALLOC_FREE(search_attrs);
1015 return ADS_ERROR(rc);
1018 static ADS_STATUS ads_do_paged_search(ADS_STRUCT *ads, const char *bind_path,
1019 int scope, const char *expr,
1020 const char **attrs, LDAPMessage **res,
1021 int *count, struct berval **cookie)
1023 return ads_do_paged_search_args(ads, bind_path, scope, expr, attrs, NULL, res, count, cookie);
1028 * Get all results for a search. This uses ads_do_paged_search() to return
1029 * all entries in a large search.
1030 * @param ads connection to ads server
1031 * @param bind_path Base dn for the search
1032 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1033 * @param expr Search expression
1034 * @param attrs Attributes to retrieve
1035 * @param res ** which will contain results - free res* with ads_msgfree()
1036 * @return status of search
1038 ADS_STATUS ads_do_search_all_args(ADS_STRUCT *ads, const char *bind_path,
1039 int scope, const char *expr,
1040 const char **attrs, void *args,
1041 LDAPMessage **res)
1043 struct berval *cookie = NULL;
1044 int count = 0;
1045 ADS_STATUS status;
1047 *res = NULL;
1048 status = ads_do_paged_search_args(ads, bind_path, scope, expr, attrs, args, res,
1049 &count, &cookie);
1051 if (!ADS_ERR_OK(status))
1052 return status;
1054 #ifdef HAVE_LDAP_ADD_RESULT_ENTRY
1055 while (cookie) {
1056 LDAPMessage *res2 = NULL;
1057 ADS_STATUS status2;
1058 LDAPMessage *msg, *next;
1060 status2 = ads_do_paged_search_args(ads, bind_path, scope, expr,
1061 attrs, args, &res2, &count, &cookie);
1063 if (!ADS_ERR_OK(status2)) break;
1065 /* this relies on the way that ldap_add_result_entry() works internally. I hope
1066 that this works on all ldap libs, but I have only tested with openldap */
1067 for (msg = ads_first_message(ads, res2); msg; msg = next) {
1068 next = ads_next_message(ads, msg);
1069 ldap_add_result_entry((LDAPMessage **)res, msg);
1071 /* note that we do not free res2, as the memory is now
1072 part of the main returned list */
1074 #else
1075 DEBUG(0, ("no ldap_add_result_entry() support in LDAP libs!\n"));
1076 status = ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
1077 #endif
1079 return status;
1082 ADS_STATUS ads_do_search_all(ADS_STRUCT *ads, const char *bind_path,
1083 int scope, const char *expr,
1084 const char **attrs, LDAPMessage **res)
1086 return ads_do_search_all_args(ads, bind_path, scope, expr, attrs, NULL, res);
1089 ADS_STATUS ads_do_search_all_sd_flags(ADS_STRUCT *ads, const char *bind_path,
1090 int scope, const char *expr,
1091 const char **attrs, uint32 sd_flags,
1092 LDAPMessage **res)
1094 ads_control args;
1096 args.control = ADS_SD_FLAGS_OID;
1097 args.val = sd_flags;
1098 args.critical = True;
1100 return ads_do_search_all_args(ads, bind_path, scope, expr, attrs, &args, res);
1105 * Run a function on all results for a search. Uses ads_do_paged_search() and
1106 * runs the function as each page is returned, using ads_process_results()
1107 * @param ads connection to ads server
1108 * @param bind_path Base dn for the search
1109 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1110 * @param expr Search expression - specified in local charset
1111 * @param attrs Attributes to retrieve - specified in UTF-8 or ascii
1112 * @param fn Function which takes attr name, values list, and data_area
1113 * @param data_area Pointer which is passed to function on each call
1114 * @return status of search
1116 ADS_STATUS ads_do_search_all_fn(ADS_STRUCT *ads, const char *bind_path,
1117 int scope, const char *expr, const char **attrs,
1118 bool (*fn)(ADS_STRUCT *, char *, void **, void *),
1119 void *data_area)
1121 struct berval *cookie = NULL;
1122 int count = 0;
1123 ADS_STATUS status;
1124 LDAPMessage *res;
1126 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs, &res,
1127 &count, &cookie);
1129 if (!ADS_ERR_OK(status)) return status;
1131 ads_process_results(ads, res, fn, data_area);
1132 ads_msgfree(ads, res);
1134 while (cookie) {
1135 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs,
1136 &res, &count, &cookie);
1138 if (!ADS_ERR_OK(status)) break;
1140 ads_process_results(ads, res, fn, data_area);
1141 ads_msgfree(ads, res);
1144 return status;
1148 * Do a search with a timeout.
1149 * @param ads connection to ads server
1150 * @param bind_path Base dn for the search
1151 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1152 * @param expr Search expression
1153 * @param attrs Attributes to retrieve
1154 * @param res ** which will contain results - free res* with ads_msgfree()
1155 * @return status of search
1157 ADS_STATUS ads_do_search(ADS_STRUCT *ads, const char *bind_path, int scope,
1158 const char *expr,
1159 const char **attrs, LDAPMessage **res)
1161 int rc;
1162 char *utf8_expr, *utf8_path, **search_attrs = NULL;
1163 size_t converted_size;
1164 TALLOC_CTX *ctx;
1166 *res = NULL;
1167 if (!(ctx = talloc_init("ads_do_search"))) {
1168 DEBUG(1,("ads_do_search: talloc_init() failed!"));
1169 return ADS_ERROR(LDAP_NO_MEMORY);
1172 /* 0 means the conversion worked but the result was empty
1173 so we only fail if it's negative. In any case, it always
1174 at least nulls out the dest */
1175 if (!push_utf8_talloc(ctx, &utf8_expr, expr, &converted_size) ||
1176 !push_utf8_talloc(ctx, &utf8_path, bind_path, &converted_size))
1178 DEBUG(1,("ads_do_search: push_utf8_talloc() failed!"));
1179 rc = LDAP_NO_MEMORY;
1180 goto done;
1183 if (!attrs || !(*attrs))
1184 search_attrs = NULL;
1185 else {
1186 /* This would be the utf8-encoded version...*/
1187 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
1188 if (!(str_list_copy(talloc_tos(), &search_attrs, attrs)))
1190 DEBUG(1,("ads_do_search: str_list_copy() failed!"));
1191 rc = LDAP_NO_MEMORY;
1192 goto done;
1196 /* see the note in ads_do_paged_search - we *must* disable referrals */
1197 ldap_set_option(ads->ldap.ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
1199 rc = ldap_search_with_timeout(ads->ldap.ld, utf8_path, scope, utf8_expr,
1200 search_attrs, 0, NULL, NULL,
1201 LDAP_NO_LIMIT,
1202 (LDAPMessage **)res);
1204 if (rc == LDAP_SIZELIMIT_EXCEEDED) {
1205 DEBUG(3,("Warning! sizelimit exceeded in ldap. Truncating.\n"));
1206 rc = 0;
1209 done:
1210 talloc_destroy(ctx);
1211 /* if/when we decide to utf8-encode attrs, take out this next line */
1212 TALLOC_FREE(search_attrs);
1213 return ADS_ERROR(rc);
1216 * Do a general ADS search
1217 * @param ads connection to ads server
1218 * @param res ** which will contain results - free res* with ads_msgfree()
1219 * @param expr Search expression
1220 * @param attrs Attributes to retrieve
1221 * @return status of search
1223 ADS_STATUS ads_search(ADS_STRUCT *ads, LDAPMessage **res,
1224 const char *expr, const char **attrs)
1226 return ads_do_search(ads, ads->config.bind_path, LDAP_SCOPE_SUBTREE,
1227 expr, attrs, res);
1231 * Do a search on a specific DistinguishedName
1232 * @param ads connection to ads server
1233 * @param res ** which will contain results - free res* with ads_msgfree()
1234 * @param dn DistinguishName to search
1235 * @param attrs Attributes to retrieve
1236 * @return status of search
1238 ADS_STATUS ads_search_dn(ADS_STRUCT *ads, LDAPMessage **res,
1239 const char *dn, const char **attrs)
1241 return ads_do_search(ads, dn, LDAP_SCOPE_BASE, "(objectclass=*)",
1242 attrs, res);
1246 * Free up memory from a ads_search
1247 * @param ads connection to ads server
1248 * @param msg Search results to free
1250 void ads_msgfree(ADS_STRUCT *ads, LDAPMessage *msg)
1252 if (!msg) return;
1253 ldap_msgfree(msg);
1257 * Free up memory from various ads requests
1258 * @param ads connection to ads server
1259 * @param mem Area to free
1261 void ads_memfree(ADS_STRUCT *ads, void *mem)
1263 SAFE_FREE(mem);
1267 * Get a dn from search results
1268 * @param ads connection to ads server
1269 * @param msg Search result
1270 * @return dn string
1272 char *ads_get_dn(ADS_STRUCT *ads, LDAPMessage *msg)
1274 char *utf8_dn, *unix_dn;
1275 size_t converted_size;
1277 utf8_dn = ldap_get_dn(ads->ldap.ld, msg);
1279 if (!utf8_dn) {
1280 DEBUG (5, ("ads_get_dn: ldap_get_dn failed\n"));
1281 return NULL;
1284 if (!pull_utf8_allocate(&unix_dn, utf8_dn, &converted_size)) {
1285 DEBUG(0,("ads_get_dn: string conversion failure utf8 [%s]\n",
1286 utf8_dn ));
1287 return NULL;
1289 ldap_memfree(utf8_dn);
1290 return unix_dn;
1294 * Get the parent from a dn
1295 * @param dn the dn to return the parent from
1296 * @return parent dn string
1298 char *ads_parent_dn(const char *dn)
1300 char *p;
1302 if (dn == NULL) {
1303 return NULL;
1306 p = strchr(dn, ',');
1308 if (p == NULL) {
1309 return NULL;
1312 return p+1;
1316 * Find a machine account given a hostname
1317 * @param ads connection to ads server
1318 * @param res ** which will contain results - free res* with ads_msgfree()
1319 * @param host Hostname to search for
1320 * @return status of search
1322 ADS_STATUS ads_find_machine_acct(ADS_STRUCT *ads, LDAPMessage **res,
1323 const char *machine)
1325 ADS_STATUS status;
1326 char *expr;
1327 const char *attrs[] = {"*", "nTSecurityDescriptor", NULL};
1329 *res = NULL;
1331 /* the easiest way to find a machine account anywhere in the tree
1332 is to look for hostname$ */
1333 if (asprintf(&expr, "(samAccountName=%s$)", machine) == -1) {
1334 DEBUG(1, ("asprintf failed!\n"));
1335 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1338 status = ads_search(ads, res, expr, attrs);
1339 SAFE_FREE(expr);
1340 return status;
1344 * Initialize a list of mods to be used in a modify request
1345 * @param ctx An initialized TALLOC_CTX
1346 * @return allocated ADS_MODLIST
1348 ADS_MODLIST ads_init_mods(TALLOC_CTX *ctx)
1350 #define ADS_MODLIST_ALLOC_SIZE 10
1351 LDAPMod **mods;
1353 if ((mods = TALLOC_ZERO_ARRAY(ctx, LDAPMod *, ADS_MODLIST_ALLOC_SIZE + 1)))
1354 /* -1 is safety to make sure we don't go over the end.
1355 need to reset it to NULL before doing ldap modify */
1356 mods[ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1358 return (ADS_MODLIST)mods;
1363 add an attribute to the list, with values list already constructed
1365 static ADS_STATUS ads_modlist_add(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1366 int mod_op, const char *name,
1367 const void *_invals)
1369 const void **invals = (const void **)_invals;
1370 int curmod;
1371 LDAPMod **modlist = (LDAPMod **) *mods;
1372 struct berval **ber_values = NULL;
1373 char **char_values = NULL;
1375 if (!invals) {
1376 mod_op = LDAP_MOD_DELETE;
1377 } else {
1378 if (mod_op & LDAP_MOD_BVALUES)
1379 ber_values = ads_dup_values(ctx,
1380 (const struct berval **)invals);
1381 else
1382 char_values = ads_push_strvals(ctx,
1383 (const char **) invals);
1386 /* find the first empty slot */
1387 for (curmod=0; modlist[curmod] && modlist[curmod] != (LDAPMod *) -1;
1388 curmod++);
1389 if (modlist[curmod] == (LDAPMod *) -1) {
1390 if (!(modlist = TALLOC_REALLOC_ARRAY(ctx, modlist, LDAPMod *,
1391 curmod+ADS_MODLIST_ALLOC_SIZE+1)))
1392 return ADS_ERROR(LDAP_NO_MEMORY);
1393 memset(&modlist[curmod], 0,
1394 ADS_MODLIST_ALLOC_SIZE*sizeof(LDAPMod *));
1395 modlist[curmod+ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1396 *mods = (ADS_MODLIST)modlist;
1399 if (!(modlist[curmod] = TALLOC_ZERO_P(ctx, LDAPMod)))
1400 return ADS_ERROR(LDAP_NO_MEMORY);
1401 modlist[curmod]->mod_type = talloc_strdup(ctx, name);
1402 if (mod_op & LDAP_MOD_BVALUES) {
1403 modlist[curmod]->mod_bvalues = ber_values;
1404 } else if (mod_op & LDAP_MOD_DELETE) {
1405 modlist[curmod]->mod_values = NULL;
1406 } else {
1407 modlist[curmod]->mod_values = char_values;
1410 modlist[curmod]->mod_op = mod_op;
1411 return ADS_ERROR(LDAP_SUCCESS);
1415 * Add a single string value to a mod list
1416 * @param ctx An initialized TALLOC_CTX
1417 * @param mods An initialized ADS_MODLIST
1418 * @param name The attribute name to add
1419 * @param val The value to add - NULL means DELETE
1420 * @return ADS STATUS indicating success of add
1422 ADS_STATUS ads_mod_str(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1423 const char *name, const char *val)
1425 const char *values[2];
1427 values[0] = val;
1428 values[1] = NULL;
1430 if (!val)
1431 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1432 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE, name, values);
1436 * Add an array of string values to a mod list
1437 * @param ctx An initialized TALLOC_CTX
1438 * @param mods An initialized ADS_MODLIST
1439 * @param name The attribute name to add
1440 * @param vals The array of string values to add - NULL means DELETE
1441 * @return ADS STATUS indicating success of add
1443 ADS_STATUS ads_mod_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1444 const char *name, const char **vals)
1446 if (!vals)
1447 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1448 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE,
1449 name, (const void **) vals);
1452 #if 0
1454 * Add a single ber-encoded value to a mod list
1455 * @param ctx An initialized TALLOC_CTX
1456 * @param mods An initialized ADS_MODLIST
1457 * @param name The attribute name to add
1458 * @param val The value to add - NULL means DELETE
1459 * @return ADS STATUS indicating success of add
1461 static ADS_STATUS ads_mod_ber(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1462 const char *name, const struct berval *val)
1464 const struct berval *values[2];
1466 values[0] = val;
1467 values[1] = NULL;
1468 if (!val)
1469 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1470 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE|LDAP_MOD_BVALUES,
1471 name, (const void **) values);
1473 #endif
1476 * Perform an ldap modify
1477 * @param ads connection to ads server
1478 * @param mod_dn DistinguishedName to modify
1479 * @param mods list of modifications to perform
1480 * @return status of modify
1482 ADS_STATUS ads_gen_mod(ADS_STRUCT *ads, const char *mod_dn, ADS_MODLIST mods)
1484 int ret,i;
1485 char *utf8_dn = NULL;
1486 size_t converted_size;
1488 this control is needed to modify that contains a currently
1489 non-existent attribute (but allowable for the object) to run
1491 LDAPControl PermitModify = {
1492 CONST_DISCARD(char *, ADS_PERMIT_MODIFY_OID),
1493 {0, NULL},
1494 (char) 1};
1495 LDAPControl *controls[2];
1497 controls[0] = &PermitModify;
1498 controls[1] = NULL;
1500 if (!push_utf8_allocate(&utf8_dn, mod_dn, &converted_size)) {
1501 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1504 /* find the end of the list, marked by NULL or -1 */
1505 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1506 /* make sure the end of the list is NULL */
1507 mods[i] = NULL;
1508 ret = ldap_modify_ext_s(ads->ldap.ld, utf8_dn,
1509 (LDAPMod **) mods, controls, NULL);
1510 SAFE_FREE(utf8_dn);
1511 return ADS_ERROR(ret);
1515 * Perform an ldap add
1516 * @param ads connection to ads server
1517 * @param new_dn DistinguishedName to add
1518 * @param mods list of attributes and values for DN
1519 * @return status of add
1521 ADS_STATUS ads_gen_add(ADS_STRUCT *ads, const char *new_dn, ADS_MODLIST mods)
1523 int ret, i;
1524 char *utf8_dn = NULL;
1525 size_t converted_size;
1527 if (!push_utf8_allocate(&utf8_dn, new_dn, &converted_size)) {
1528 DEBUG(1, ("ads_gen_add: push_utf8_allocate failed!"));
1529 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1532 /* find the end of the list, marked by NULL or -1 */
1533 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1534 /* make sure the end of the list is NULL */
1535 mods[i] = NULL;
1537 ret = ldap_add_s(ads->ldap.ld, utf8_dn, (LDAPMod**)mods);
1538 SAFE_FREE(utf8_dn);
1539 return ADS_ERROR(ret);
1543 * Delete a DistinguishedName
1544 * @param ads connection to ads server
1545 * @param new_dn DistinguishedName to delete
1546 * @return status of delete
1548 ADS_STATUS ads_del_dn(ADS_STRUCT *ads, char *del_dn)
1550 int ret;
1551 char *utf8_dn = NULL;
1552 size_t converted_size;
1553 if (!push_utf8_allocate(&utf8_dn, del_dn, &converted_size)) {
1554 DEBUG(1, ("ads_del_dn: push_utf8_allocate failed!"));
1555 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1558 ret = ldap_delete_s(ads->ldap.ld, utf8_dn);
1559 SAFE_FREE(utf8_dn);
1560 return ADS_ERROR(ret);
1564 * Build an org unit string
1565 * if org unit is Computers or blank then assume a container, otherwise
1566 * assume a / separated list of organisational units.
1567 * jmcd: '\' is now used for escapes so certain chars can be in the ou (e.g. #)
1568 * @param ads connection to ads server
1569 * @param org_unit Organizational unit
1570 * @return org unit string - caller must free
1572 char *ads_ou_string(ADS_STRUCT *ads, const char *org_unit)
1574 char *ret = NULL;
1576 if (!org_unit || !*org_unit) {
1578 ret = ads_default_ou_string(ads, WELL_KNOWN_GUID_COMPUTERS);
1580 /* samba4 might not yet respond to a wellknownobject-query */
1581 return ret ? ret : SMB_STRDUP("cn=Computers");
1584 if (strequal(org_unit, "Computers")) {
1585 return SMB_STRDUP("cn=Computers");
1588 /* jmcd: removed "\\" from the separation chars, because it is
1589 needed as an escape for chars like '#' which are valid in an
1590 OU name */
1591 return ads_build_path(org_unit, "/", "ou=", 1);
1595 * Get a org unit string for a well-known GUID
1596 * @param ads connection to ads server
1597 * @param wknguid Well known GUID
1598 * @return org unit string - caller must free
1600 char *ads_default_ou_string(ADS_STRUCT *ads, const char *wknguid)
1602 ADS_STATUS status;
1603 LDAPMessage *res = NULL;
1604 char *base, *wkn_dn = NULL, *ret = NULL, **wkn_dn_exp = NULL,
1605 **bind_dn_exp = NULL;
1606 const char *attrs[] = {"distinguishedName", NULL};
1607 int new_ln, wkn_ln, bind_ln, i;
1609 if (wknguid == NULL) {
1610 return NULL;
1613 if (asprintf(&base, "<WKGUID=%s,%s>", wknguid, ads->config.bind_path ) == -1) {
1614 DEBUG(1, ("asprintf failed!\n"));
1615 return NULL;
1618 status = ads_search_dn(ads, &res, base, attrs);
1619 if (!ADS_ERR_OK(status)) {
1620 DEBUG(1,("Failed while searching for: %s\n", base));
1621 goto out;
1624 if (ads_count_replies(ads, res) != 1) {
1625 goto out;
1628 /* substitute the bind-path from the well-known-guid-search result */
1629 wkn_dn = ads_get_dn(ads, res);
1630 if (!wkn_dn) {
1631 goto out;
1634 wkn_dn_exp = ldap_explode_dn(wkn_dn, 0);
1635 if (!wkn_dn_exp) {
1636 goto out;
1639 bind_dn_exp = ldap_explode_dn(ads->config.bind_path, 0);
1640 if (!bind_dn_exp) {
1641 goto out;
1644 for (wkn_ln=0; wkn_dn_exp[wkn_ln]; wkn_ln++)
1646 for (bind_ln=0; bind_dn_exp[bind_ln]; bind_ln++)
1649 new_ln = wkn_ln - bind_ln;
1651 ret = SMB_STRDUP(wkn_dn_exp[0]);
1652 if (!ret) {
1653 goto out;
1656 for (i=1; i < new_ln; i++) {
1657 char *s = NULL;
1659 if (asprintf(&s, "%s,%s", ret, wkn_dn_exp[i]) == -1) {
1660 SAFE_FREE(ret);
1661 goto out;
1664 SAFE_FREE(ret);
1665 ret = SMB_STRDUP(s);
1666 free(s);
1667 if (!ret) {
1668 goto out;
1672 out:
1673 SAFE_FREE(base);
1674 ads_msgfree(ads, res);
1675 ads_memfree(ads, wkn_dn);
1676 if (wkn_dn_exp) {
1677 ldap_value_free(wkn_dn_exp);
1679 if (bind_dn_exp) {
1680 ldap_value_free(bind_dn_exp);
1683 return ret;
1687 * Adds (appends) an item to an attribute array, rather then
1688 * replacing the whole list
1689 * @param ctx An initialized TALLOC_CTX
1690 * @param mods An initialized ADS_MODLIST
1691 * @param name name of the ldap attribute to append to
1692 * @param vals an array of values to add
1693 * @return status of addition
1696 ADS_STATUS ads_add_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1697 const char *name, const char **vals)
1699 return ads_modlist_add(ctx, mods, LDAP_MOD_ADD, name,
1700 (const void *) vals);
1704 * Determines the an account's current KVNO via an LDAP lookup
1705 * @param ads An initialized ADS_STRUCT
1706 * @param account_name the NT samaccountname.
1707 * @return the kvno for the account, or -1 in case of a failure.
1710 uint32 ads_get_kvno(ADS_STRUCT *ads, const char *account_name)
1712 LDAPMessage *res = NULL;
1713 uint32 kvno = (uint32)-1; /* -1 indicates a failure */
1714 char *filter;
1715 const char *attrs[] = {"msDS-KeyVersionNumber", NULL};
1716 char *dn_string = NULL;
1717 ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS);
1719 DEBUG(5,("ads_get_kvno: Searching for account %s\n", account_name));
1720 if (asprintf(&filter, "(samAccountName=%s)", account_name) == -1) {
1721 return kvno;
1723 ret = ads_search(ads, &res, filter, attrs);
1724 SAFE_FREE(filter);
1725 if (!ADS_ERR_OK(ret) || (ads_count_replies(ads, res) != 1)) {
1726 DEBUG(1,("ads_get_kvno: Account for %s not found.\n", account_name));
1727 ads_msgfree(ads, res);
1728 return kvno;
1731 dn_string = ads_get_dn(ads, res);
1732 if (!dn_string) {
1733 DEBUG(0,("ads_get_kvno: out of memory.\n"));
1734 ads_msgfree(ads, res);
1735 return kvno;
1737 DEBUG(5,("ads_get_kvno: Using: %s\n", dn_string));
1738 ads_memfree(ads, dn_string);
1740 /* ---------------------------------------------------------
1741 * 0 is returned as a default KVNO from this point on...
1742 * This is done because Windows 2000 does not support key
1743 * version numbers. Chances are that a failure in the next
1744 * step is simply due to Windows 2000 being used for a
1745 * domain controller. */
1746 kvno = 0;
1748 if (!ads_pull_uint32(ads, res, "msDS-KeyVersionNumber", &kvno)) {
1749 DEBUG(3,("ads_get_kvno: Error Determining KVNO!\n"));
1750 DEBUG(3,("ads_get_kvno: Windows 2000 does not support KVNO's, so this may be normal.\n"));
1751 ads_msgfree(ads, res);
1752 return kvno;
1755 /* Success */
1756 DEBUG(5,("ads_get_kvno: Looked Up KVNO of: %d\n", kvno));
1757 ads_msgfree(ads, res);
1758 return kvno;
1762 * Determines the computer account's current KVNO via an LDAP lookup
1763 * @param ads An initialized ADS_STRUCT
1764 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1765 * @return the kvno for the computer account, or -1 in case of a failure.
1768 uint32_t ads_get_machine_kvno(ADS_STRUCT *ads, const char *machine_name)
1770 char *computer_account = NULL;
1771 uint32_t kvno = -1;
1773 if (asprintf(&computer_account, "%s$", machine_name) < 0) {
1774 return kvno;
1777 kvno = ads_get_kvno(ads, computer_account);
1778 free(computer_account);
1780 return kvno;
1784 * This clears out all registered spn's for a given hostname
1785 * @param ads An initilaized ADS_STRUCT
1786 * @param machine_name the NetBIOS name of the computer.
1787 * @return 0 upon success, non-zero otherwise.
1790 ADS_STATUS ads_clear_service_principal_names(ADS_STRUCT *ads, const char *machine_name)
1792 TALLOC_CTX *ctx;
1793 LDAPMessage *res = NULL;
1794 ADS_MODLIST mods;
1795 const char *servicePrincipalName[1] = {NULL};
1796 ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS);
1797 char *dn_string = NULL;
1799 ret = ads_find_machine_acct(ads, &res, machine_name);
1800 if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
1801 DEBUG(5,("ads_clear_service_principal_names: WARNING: Host Account for %s not found... skipping operation.\n", machine_name));
1802 DEBUG(5,("ads_clear_service_principal_names: WARNING: Service Principals for %s have NOT been cleared.\n", machine_name));
1803 ads_msgfree(ads, res);
1804 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1807 DEBUG(5,("ads_clear_service_principal_names: Host account for %s found\n", machine_name));
1808 ctx = talloc_init("ads_clear_service_principal_names");
1809 if (!ctx) {
1810 ads_msgfree(ads, res);
1811 return ADS_ERROR(LDAP_NO_MEMORY);
1814 if (!(mods = ads_init_mods(ctx))) {
1815 talloc_destroy(ctx);
1816 ads_msgfree(ads, res);
1817 return ADS_ERROR(LDAP_NO_MEMORY);
1819 ret = ads_mod_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1820 if (!ADS_ERR_OK(ret)) {
1821 DEBUG(1,("ads_clear_service_principal_names: Error creating strlist.\n"));
1822 ads_msgfree(ads, res);
1823 talloc_destroy(ctx);
1824 return ret;
1826 dn_string = ads_get_dn(ads, res);
1827 if (!dn_string) {
1828 talloc_destroy(ctx);
1829 ads_msgfree(ads, res);
1830 return ADS_ERROR(LDAP_NO_MEMORY);
1832 ret = ads_gen_mod(ads, dn_string, mods);
1833 ads_memfree(ads,dn_string);
1834 if (!ADS_ERR_OK(ret)) {
1835 DEBUG(1,("ads_clear_service_principal_names: Error: Updating Service Principals for machine %s in LDAP\n",
1836 machine_name));
1837 ads_msgfree(ads, res);
1838 talloc_destroy(ctx);
1839 return ret;
1842 ads_msgfree(ads, res);
1843 talloc_destroy(ctx);
1844 return ret;
1848 * This adds a service principal name to an existing computer account
1849 * (found by hostname) in AD.
1850 * @param ads An initialized ADS_STRUCT
1851 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1852 * @param my_fqdn The fully qualified DNS name of the machine
1853 * @param spn A string of the service principal to add, i.e. 'host'
1854 * @return 0 upon sucess, or non-zero if a failure occurs
1857 ADS_STATUS ads_add_service_principal_name(ADS_STRUCT *ads, const char *machine_name,
1858 const char *my_fqdn, const char *spn)
1860 ADS_STATUS ret;
1861 TALLOC_CTX *ctx;
1862 LDAPMessage *res = NULL;
1863 char *psp1, *psp2;
1864 ADS_MODLIST mods;
1865 char *dn_string = NULL;
1866 const char *servicePrincipalName[3] = {NULL, NULL, NULL};
1868 ret = ads_find_machine_acct(ads, &res, machine_name);
1869 if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
1870 DEBUG(1,("ads_add_service_principal_name: WARNING: Host Account for %s not found... skipping operation.\n",
1871 machine_name));
1872 DEBUG(1,("ads_add_service_principal_name: WARNING: Service Principal '%s/%s@%s' has NOT been added.\n",
1873 spn, machine_name, ads->config.realm));
1874 ads_msgfree(ads, res);
1875 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1878 DEBUG(1,("ads_add_service_principal_name: Host account for %s found\n", machine_name));
1879 if (!(ctx = talloc_init("ads_add_service_principal_name"))) {
1880 ads_msgfree(ads, res);
1881 return ADS_ERROR(LDAP_NO_MEMORY);
1884 /* add short name spn */
1886 if ( (psp1 = talloc_asprintf(ctx, "%s/%s", spn, machine_name)) == NULL ) {
1887 talloc_destroy(ctx);
1888 ads_msgfree(ads, res);
1889 return ADS_ERROR(LDAP_NO_MEMORY);
1891 strupper_m(psp1);
1892 strlower_m(&psp1[strlen(spn)]);
1893 servicePrincipalName[0] = psp1;
1895 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n",
1896 psp1, machine_name));
1899 /* add fully qualified spn */
1901 if ( (psp2 = talloc_asprintf(ctx, "%s/%s", spn, my_fqdn)) == NULL ) {
1902 ret = ADS_ERROR(LDAP_NO_MEMORY);
1903 goto out;
1905 strupper_m(psp2);
1906 strlower_m(&psp2[strlen(spn)]);
1907 servicePrincipalName[1] = psp2;
1909 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n",
1910 psp2, machine_name));
1912 if ( (mods = ads_init_mods(ctx)) == NULL ) {
1913 ret = ADS_ERROR(LDAP_NO_MEMORY);
1914 goto out;
1917 ret = ads_add_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1918 if (!ADS_ERR_OK(ret)) {
1919 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
1920 goto out;
1923 if ( (dn_string = ads_get_dn(ads, res)) == NULL ) {
1924 ret = ADS_ERROR(LDAP_NO_MEMORY);
1925 goto out;
1928 ret = ads_gen_mod(ads, dn_string, mods);
1929 ads_memfree(ads,dn_string);
1930 if (!ADS_ERR_OK(ret)) {
1931 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
1932 goto out;
1935 out:
1936 TALLOC_FREE( ctx );
1937 ads_msgfree(ads, res);
1938 return ret;
1942 * adds a machine account to the ADS server
1943 * @param ads An intialized ADS_STRUCT
1944 * @param machine_name - the NetBIOS machine name of this account.
1945 * @param account_type A number indicating the type of account to create
1946 * @param org_unit The LDAP path in which to place this account
1947 * @return 0 upon success, or non-zero otherwise
1950 ADS_STATUS ads_create_machine_acct(ADS_STRUCT *ads, const char *machine_name,
1951 const char *org_unit)
1953 ADS_STATUS ret;
1954 char *samAccountName, *controlstr;
1955 TALLOC_CTX *ctx;
1956 ADS_MODLIST mods;
1957 char *machine_escaped = NULL;
1958 char *new_dn;
1959 const char *objectClass[] = {"top", "person", "organizationalPerson",
1960 "user", "computer", NULL};
1961 LDAPMessage *res = NULL;
1962 uint32 acct_control = ( UF_WORKSTATION_TRUST_ACCOUNT |\
1963 UF_DONT_EXPIRE_PASSWD |\
1964 UF_ACCOUNTDISABLE );
1966 if (!(ctx = talloc_init("ads_add_machine_acct")))
1967 return ADS_ERROR(LDAP_NO_MEMORY);
1969 ret = ADS_ERROR(LDAP_NO_MEMORY);
1971 machine_escaped = escape_rdn_val_string_alloc(machine_name);
1972 if (!machine_escaped) {
1973 goto done;
1976 new_dn = talloc_asprintf(ctx, "cn=%s,%s", machine_escaped, org_unit);
1977 samAccountName = talloc_asprintf(ctx, "%s$", machine_name);
1979 if ( !new_dn || !samAccountName ) {
1980 goto done;
1983 #ifndef ENCTYPE_ARCFOUR_HMAC
1984 acct_control |= UF_USE_DES_KEY_ONLY;
1985 #endif
1987 if (!(controlstr = talloc_asprintf(ctx, "%u", acct_control))) {
1988 goto done;
1991 if (!(mods = ads_init_mods(ctx))) {
1992 goto done;
1995 ads_mod_str(ctx, &mods, "cn", machine_name);
1996 ads_mod_str(ctx, &mods, "sAMAccountName", samAccountName);
1997 ads_mod_strlist(ctx, &mods, "objectClass", objectClass);
1998 ads_mod_str(ctx, &mods, "userAccountControl", controlstr);
2000 ret = ads_gen_add(ads, new_dn, mods);
2002 done:
2003 SAFE_FREE(machine_escaped);
2004 ads_msgfree(ads, res);
2005 talloc_destroy(ctx);
2007 return ret;
2011 * move a machine account to another OU on the ADS server
2012 * @param ads - An intialized ADS_STRUCT
2013 * @param machine_name - the NetBIOS machine name of this account.
2014 * @param org_unit - The LDAP path in which to place this account
2015 * @param moved - whether we moved the machine account (optional)
2016 * @return 0 upon success, or non-zero otherwise
2019 ADS_STATUS ads_move_machine_acct(ADS_STRUCT *ads, const char *machine_name,
2020 const char *org_unit, bool *moved)
2022 ADS_STATUS rc;
2023 int ldap_status;
2024 LDAPMessage *res = NULL;
2025 char *filter = NULL;
2026 char *computer_dn = NULL;
2027 char *parent_dn;
2028 char *computer_rdn = NULL;
2029 bool need_move = False;
2031 if (asprintf(&filter, "(samAccountName=%s$)", machine_name) == -1) {
2032 rc = ADS_ERROR(LDAP_NO_MEMORY);
2033 goto done;
2036 /* Find pre-existing machine */
2037 rc = ads_search(ads, &res, filter, NULL);
2038 if (!ADS_ERR_OK(rc)) {
2039 goto done;
2042 computer_dn = ads_get_dn(ads, res);
2043 if (!computer_dn) {
2044 rc = ADS_ERROR(LDAP_NO_MEMORY);
2045 goto done;
2048 parent_dn = ads_parent_dn(computer_dn);
2049 if (strequal(parent_dn, org_unit)) {
2050 goto done;
2053 need_move = True;
2055 if (asprintf(&computer_rdn, "CN=%s", machine_name) == -1) {
2056 rc = ADS_ERROR(LDAP_NO_MEMORY);
2057 goto done;
2060 ldap_status = ldap_rename_s(ads->ldap.ld, computer_dn, computer_rdn,
2061 org_unit, 1, NULL, NULL);
2062 rc = ADS_ERROR(ldap_status);
2064 done:
2065 ads_msgfree(ads, res);
2066 SAFE_FREE(filter);
2067 SAFE_FREE(computer_dn);
2068 SAFE_FREE(computer_rdn);
2070 if (!ADS_ERR_OK(rc)) {
2071 need_move = False;
2074 if (moved) {
2075 *moved = need_move;
2078 return rc;
2082 dump a binary result from ldap
2084 static void dump_binary(ADS_STRUCT *ads, const char *field, struct berval **values)
2086 int i, j;
2087 for (i=0; values[i]; i++) {
2088 printf("%s: ", field);
2089 for (j=0; j<values[i]->bv_len; j++) {
2090 printf("%02X", (unsigned char)values[i]->bv_val[j]);
2092 printf("\n");
2096 static void dump_guid(ADS_STRUCT *ads, const char *field, struct berval **values)
2098 int i;
2099 for (i=0; values[i]; i++) {
2101 UUID_FLAT guid;
2102 struct GUID tmp;
2104 memcpy(guid.info, values[i]->bv_val, sizeof(guid.info));
2105 smb_uuid_unpack(guid, &tmp);
2106 printf("%s: %s\n", field, smb_uuid_string(talloc_tos(), tmp));
2111 dump a sid result from ldap
2113 static void dump_sid(ADS_STRUCT *ads, const char *field, struct berval **values)
2115 int i;
2116 for (i=0; values[i]; i++) {
2117 DOM_SID sid;
2118 fstring tmp;
2119 sid_parse(values[i]->bv_val, values[i]->bv_len, &sid);
2120 printf("%s: %s\n", field, sid_to_fstring(tmp, &sid));
2125 dump ntSecurityDescriptor
2127 static void dump_sd(ADS_STRUCT *ads, const char *filed, struct berval **values)
2129 TALLOC_CTX *frame = talloc_stackframe();
2130 struct security_descriptor *psd;
2131 NTSTATUS status;
2133 status = unmarshall_sec_desc(talloc_tos(), (uint8 *)values[0]->bv_val,
2134 values[0]->bv_len, &psd);
2135 if (!NT_STATUS_IS_OK(status)) {
2136 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
2137 nt_errstr(status)));
2138 TALLOC_FREE(frame);
2139 return;
2142 if (psd) {
2143 ads_disp_sd(ads, talloc_tos(), psd);
2146 TALLOC_FREE(frame);
2150 dump a string result from ldap
2152 static void dump_string(const char *field, char **values)
2154 int i;
2155 for (i=0; values[i]; i++) {
2156 printf("%s: %s\n", field, values[i]);
2161 dump a field from LDAP on stdout
2162 used for debugging
2165 static bool ads_dump_field(ADS_STRUCT *ads, char *field, void **values, void *data_area)
2167 const struct {
2168 const char *name;
2169 bool string;
2170 void (*handler)(ADS_STRUCT *, const char *, struct berval **);
2171 } handlers[] = {
2172 {"objectGUID", False, dump_guid},
2173 {"netbootGUID", False, dump_guid},
2174 {"nTSecurityDescriptor", False, dump_sd},
2175 {"dnsRecord", False, dump_binary},
2176 {"objectSid", False, dump_sid},
2177 {"tokenGroups", False, dump_sid},
2178 {"tokenGroupsNoGCAcceptable", False, dump_sid},
2179 {"tokengroupsGlobalandUniversal", False, dump_sid},
2180 {"mS-DS-CreatorSID", False, dump_sid},
2181 {"msExchMailboxGuid", False, dump_guid},
2182 {NULL, True, NULL}
2184 int i;
2186 if (!field) { /* must be end of an entry */
2187 printf("\n");
2188 return False;
2191 for (i=0; handlers[i].name; i++) {
2192 if (StrCaseCmp(handlers[i].name, field) == 0) {
2193 if (!values) /* first time, indicate string or not */
2194 return handlers[i].string;
2195 handlers[i].handler(ads, field, (struct berval **) values);
2196 break;
2199 if (!handlers[i].name) {
2200 if (!values) /* first time, indicate string conversion */
2201 return True;
2202 dump_string(field, (char **)values);
2204 return False;
2208 * Dump a result from LDAP on stdout
2209 * used for debugging
2210 * @param ads connection to ads server
2211 * @param res Results to dump
2214 void ads_dump(ADS_STRUCT *ads, LDAPMessage *res)
2216 ads_process_results(ads, res, ads_dump_field, NULL);
2220 * Walk through results, calling a function for each entry found.
2221 * The function receives a field name, a berval * array of values,
2222 * and a data area passed through from the start. The function is
2223 * called once with null for field and values at the end of each
2224 * entry.
2225 * @param ads connection to ads server
2226 * @param res Results to process
2227 * @param fn Function for processing each result
2228 * @param data_area user-defined area to pass to function
2230 void ads_process_results(ADS_STRUCT *ads, LDAPMessage *res,
2231 bool (*fn)(ADS_STRUCT *, char *, void **, void *),
2232 void *data_area)
2234 LDAPMessage *msg;
2235 TALLOC_CTX *ctx;
2236 size_t converted_size;
2238 if (!(ctx = talloc_init("ads_process_results")))
2239 return;
2241 for (msg = ads_first_entry(ads, res); msg;
2242 msg = ads_next_entry(ads, msg)) {
2243 char *utf8_field;
2244 BerElement *b;
2246 for (utf8_field=ldap_first_attribute(ads->ldap.ld,
2247 (LDAPMessage *)msg,&b);
2248 utf8_field;
2249 utf8_field=ldap_next_attribute(ads->ldap.ld,
2250 (LDAPMessage *)msg,b)) {
2251 struct berval **ber_vals;
2252 char **str_vals, **utf8_vals;
2253 char *field;
2254 bool string;
2256 if (!pull_utf8_talloc(ctx, &field, utf8_field,
2257 &converted_size))
2259 DEBUG(0,("ads_process_results: "
2260 "pull_utf8_talloc failed: %s",
2261 strerror(errno)));
2264 string = fn(ads, field, NULL, data_area);
2266 if (string) {
2267 utf8_vals = ldap_get_values(ads->ldap.ld,
2268 (LDAPMessage *)msg, field);
2269 str_vals = ads_pull_strvals(ctx,
2270 (const char **) utf8_vals);
2271 fn(ads, field, (void **) str_vals, data_area);
2272 ldap_value_free(utf8_vals);
2273 } else {
2274 ber_vals = ldap_get_values_len(ads->ldap.ld,
2275 (LDAPMessage *)msg, field);
2276 fn(ads, field, (void **) ber_vals, data_area);
2278 ldap_value_free_len(ber_vals);
2280 ldap_memfree(utf8_field);
2282 ber_free(b, 0);
2283 talloc_free_children(ctx);
2284 fn(ads, NULL, NULL, data_area); /* completed an entry */
2287 talloc_destroy(ctx);
2291 * count how many replies are in a LDAPMessage
2292 * @param ads connection to ads server
2293 * @param res Results to count
2294 * @return number of replies
2296 int ads_count_replies(ADS_STRUCT *ads, void *res)
2298 return ldap_count_entries(ads->ldap.ld, (LDAPMessage *)res);
2302 * pull the first entry from a ADS result
2303 * @param ads connection to ads server
2304 * @param res Results of search
2305 * @return first entry from result
2307 LDAPMessage *ads_first_entry(ADS_STRUCT *ads, LDAPMessage *res)
2309 return ldap_first_entry(ads->ldap.ld, res);
2313 * pull the next entry from a ADS result
2314 * @param ads connection to ads server
2315 * @param res Results of search
2316 * @return next entry from result
2318 LDAPMessage *ads_next_entry(ADS_STRUCT *ads, LDAPMessage *res)
2320 return ldap_next_entry(ads->ldap.ld, res);
2324 * pull the first message from a ADS result
2325 * @param ads connection to ads server
2326 * @param res Results of search
2327 * @return first message from result
2329 LDAPMessage *ads_first_message(ADS_STRUCT *ads, LDAPMessage *res)
2331 return ldap_first_message(ads->ldap.ld, res);
2335 * pull the next message from a ADS result
2336 * @param ads connection to ads server
2337 * @param res Results of search
2338 * @return next message from result
2340 LDAPMessage *ads_next_message(ADS_STRUCT *ads, LDAPMessage *res)
2342 return ldap_next_message(ads->ldap.ld, res);
2346 * pull a single string from a ADS result
2347 * @param ads connection to ads server
2348 * @param mem_ctx TALLOC_CTX to use for allocating result string
2349 * @param msg Results of search
2350 * @param field Attribute to retrieve
2351 * @return Result string in talloc context
2353 char *ads_pull_string(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, LDAPMessage *msg,
2354 const char *field)
2356 char **values;
2357 char *ret = NULL;
2358 char *ux_string;
2359 size_t converted_size;
2361 values = ldap_get_values(ads->ldap.ld, msg, field);
2362 if (!values)
2363 return NULL;
2365 if (values[0] && pull_utf8_talloc(mem_ctx, &ux_string, values[0],
2366 &converted_size))
2368 ret = ux_string;
2370 ldap_value_free(values);
2371 return ret;
2375 * pull an array of strings from a ADS result
2376 * @param ads connection to ads server
2377 * @param mem_ctx TALLOC_CTX to use for allocating result string
2378 * @param msg Results of search
2379 * @param field Attribute to retrieve
2380 * @return Result strings in talloc context
2382 char **ads_pull_strings(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2383 LDAPMessage *msg, const char *field,
2384 size_t *num_values)
2386 char **values;
2387 char **ret = NULL;
2388 int i;
2389 size_t converted_size;
2391 values = ldap_get_values(ads->ldap.ld, msg, field);
2392 if (!values)
2393 return NULL;
2395 *num_values = ldap_count_values(values);
2397 ret = TALLOC_ARRAY(mem_ctx, char *, *num_values + 1);
2398 if (!ret) {
2399 ldap_value_free(values);
2400 return NULL;
2403 for (i=0;i<*num_values;i++) {
2404 if (!pull_utf8_talloc(mem_ctx, &ret[i], values[i],
2405 &converted_size))
2407 ldap_value_free(values);
2408 return NULL;
2411 ret[i] = NULL;
2413 ldap_value_free(values);
2414 return ret;
2418 * pull an array of strings from a ADS result
2419 * (handle large multivalue attributes with range retrieval)
2420 * @param ads connection to ads server
2421 * @param mem_ctx TALLOC_CTX to use for allocating result string
2422 * @param msg Results of search
2423 * @param field Attribute to retrieve
2424 * @param current_strings strings returned by a previous call to this function
2425 * @param next_attribute The next query should ask for this attribute
2426 * @param num_values How many values did we get this time?
2427 * @param more_values Are there more values to get?
2428 * @return Result strings in talloc context
2430 char **ads_pull_strings_range(ADS_STRUCT *ads,
2431 TALLOC_CTX *mem_ctx,
2432 LDAPMessage *msg, const char *field,
2433 char **current_strings,
2434 const char **next_attribute,
2435 size_t *num_strings,
2436 bool *more_strings)
2438 char *attr;
2439 char *expected_range_attrib, *range_attr;
2440 BerElement *ptr = NULL;
2441 char **strings;
2442 char **new_strings;
2443 size_t num_new_strings;
2444 unsigned long int range_start;
2445 unsigned long int range_end;
2447 /* we might have been given the whole lot anyway */
2448 if ((strings = ads_pull_strings(ads, mem_ctx, msg, field, num_strings))) {
2449 *more_strings = False;
2450 return strings;
2453 expected_range_attrib = talloc_asprintf(mem_ctx, "%s;Range=", field);
2455 /* look for Range result */
2456 for (attr = ldap_first_attribute(ads->ldap.ld, (LDAPMessage *)msg, &ptr);
2457 attr;
2458 attr = ldap_next_attribute(ads->ldap.ld, (LDAPMessage *)msg, ptr)) {
2459 /* we ignore the fact that this is utf8, as all attributes are ascii... */
2460 if (strnequal(attr, expected_range_attrib, strlen(expected_range_attrib))) {
2461 range_attr = attr;
2462 break;
2464 ldap_memfree(attr);
2466 if (!attr) {
2467 ber_free(ptr, 0);
2468 /* nothing here - this field is just empty */
2469 *more_strings = False;
2470 return NULL;
2473 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-%lu",
2474 &range_start, &range_end) == 2) {
2475 *more_strings = True;
2476 } else {
2477 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-*",
2478 &range_start) == 1) {
2479 *more_strings = False;
2480 } else {
2481 DEBUG(1, ("ads_pull_strings_range: Cannot parse Range attriubte (%s)\n",
2482 range_attr));
2483 ldap_memfree(range_attr);
2484 *more_strings = False;
2485 return NULL;
2489 if ((*num_strings) != range_start) {
2490 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) doesn't start at %u, but at %lu"
2491 " - aborting range retreival\n",
2492 range_attr, (unsigned int)(*num_strings) + 1, range_start));
2493 ldap_memfree(range_attr);
2494 *more_strings = False;
2495 return NULL;
2498 new_strings = ads_pull_strings(ads, mem_ctx, msg, range_attr, &num_new_strings);
2500 if (*more_strings && ((*num_strings + num_new_strings) != (range_end + 1))) {
2501 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) tells us we have %lu "
2502 "strings in this bunch, but we only got %lu - aborting range retreival\n",
2503 range_attr, (unsigned long int)range_end - range_start + 1,
2504 (unsigned long int)num_new_strings));
2505 ldap_memfree(range_attr);
2506 *more_strings = False;
2507 return NULL;
2510 strings = TALLOC_REALLOC_ARRAY(mem_ctx, current_strings, char *,
2511 *num_strings + num_new_strings);
2513 if (strings == NULL) {
2514 ldap_memfree(range_attr);
2515 *more_strings = False;
2516 return NULL;
2519 if (new_strings && num_new_strings) {
2520 memcpy(&strings[*num_strings], new_strings,
2521 sizeof(*new_strings) * num_new_strings);
2524 (*num_strings) += num_new_strings;
2526 if (*more_strings) {
2527 *next_attribute = talloc_asprintf(mem_ctx,
2528 "%s;range=%d-*",
2529 field,
2530 (int)*num_strings);
2532 if (!*next_attribute) {
2533 DEBUG(1, ("talloc_asprintf for next attribute failed!\n"));
2534 ldap_memfree(range_attr);
2535 *more_strings = False;
2536 return NULL;
2540 ldap_memfree(range_attr);
2542 return strings;
2546 * pull a single uint32 from a ADS result
2547 * @param ads connection to ads server
2548 * @param msg Results of search
2549 * @param field Attribute to retrieve
2550 * @param v Pointer to int to store result
2551 * @return boolean inidicating success
2553 bool ads_pull_uint32(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
2554 uint32 *v)
2556 char **values;
2558 values = ldap_get_values(ads->ldap.ld, msg, field);
2559 if (!values)
2560 return False;
2561 if (!values[0]) {
2562 ldap_value_free(values);
2563 return False;
2566 *v = atoi(values[0]);
2567 ldap_value_free(values);
2568 return True;
2572 * pull a single objectGUID from an ADS result
2573 * @param ads connection to ADS server
2574 * @param msg results of search
2575 * @param guid 37-byte area to receive text guid
2576 * @return boolean indicating success
2578 bool ads_pull_guid(ADS_STRUCT *ads, LDAPMessage *msg, struct GUID *guid)
2580 char **values;
2581 UUID_FLAT flat_guid;
2583 values = ldap_get_values(ads->ldap.ld, msg, "objectGUID");
2584 if (!values)
2585 return False;
2587 if (values[0]) {
2588 memcpy(&flat_guid.info, values[0], sizeof(UUID_FLAT));
2589 smb_uuid_unpack(flat_guid, guid);
2590 ldap_value_free(values);
2591 return True;
2593 ldap_value_free(values);
2594 return False;
2600 * pull a single DOM_SID from a ADS result
2601 * @param ads connection to ads server
2602 * @param msg Results of search
2603 * @param field Attribute to retrieve
2604 * @param sid Pointer to sid to store result
2605 * @return boolean inidicating success
2607 bool ads_pull_sid(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
2608 DOM_SID *sid)
2610 struct berval **values;
2611 bool ret = False;
2613 values = ldap_get_values_len(ads->ldap.ld, msg, field);
2615 if (!values)
2616 return False;
2618 if (values[0])
2619 ret = sid_parse(values[0]->bv_val, values[0]->bv_len, sid);
2621 ldap_value_free_len(values);
2622 return ret;
2626 * pull an array of DOM_SIDs from a ADS result
2627 * @param ads connection to ads server
2628 * @param mem_ctx TALLOC_CTX for allocating sid array
2629 * @param msg Results of search
2630 * @param field Attribute to retrieve
2631 * @param sids pointer to sid array to allocate
2632 * @return the count of SIDs pulled
2634 int ads_pull_sids(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2635 LDAPMessage *msg, const char *field, DOM_SID **sids)
2637 struct berval **values;
2638 bool ret;
2639 int count, i;
2641 values = ldap_get_values_len(ads->ldap.ld, msg, field);
2643 if (!values)
2644 return 0;
2646 for (i=0; values[i]; i++)
2647 /* nop */ ;
2649 if (i) {
2650 (*sids) = TALLOC_ARRAY(mem_ctx, DOM_SID, i);
2651 if (!(*sids)) {
2652 ldap_value_free_len(values);
2653 return 0;
2655 } else {
2656 (*sids) = NULL;
2659 count = 0;
2660 for (i=0; values[i]; i++) {
2661 ret = sid_parse(values[i]->bv_val, values[i]->bv_len, &(*sids)[count]);
2662 if (ret) {
2663 DEBUG(10, ("pulling SID: %s\n",
2664 sid_string_dbg(&(*sids)[count])));
2665 count++;
2669 ldap_value_free_len(values);
2670 return count;
2674 * pull a SEC_DESC from a ADS result
2675 * @param ads connection to ads server
2676 * @param mem_ctx TALLOC_CTX for allocating sid array
2677 * @param msg Results of search
2678 * @param field Attribute to retrieve
2679 * @param sd Pointer to *SEC_DESC to store result (talloc()ed)
2680 * @return boolean inidicating success
2682 bool ads_pull_sd(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2683 LDAPMessage *msg, const char *field, SEC_DESC **sd)
2685 struct berval **values;
2686 bool ret = true;
2688 values = ldap_get_values_len(ads->ldap.ld, msg, field);
2690 if (!values) return false;
2692 if (values[0]) {
2693 NTSTATUS status;
2694 status = unmarshall_sec_desc(mem_ctx,
2695 (uint8 *)values[0]->bv_val,
2696 values[0]->bv_len, sd);
2697 if (!NT_STATUS_IS_OK(status)) {
2698 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
2699 nt_errstr(status)));
2700 ret = false;
2704 ldap_value_free_len(values);
2705 return ret;
2709 * in order to support usernames longer than 21 characters we need to
2710 * use both the sAMAccountName and the userPrincipalName attributes
2711 * It seems that not all users have the userPrincipalName attribute set
2713 * @param ads connection to ads server
2714 * @param mem_ctx TALLOC_CTX for allocating sid array
2715 * @param msg Results of search
2716 * @return the username
2718 char *ads_pull_username(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2719 LDAPMessage *msg)
2721 #if 0 /* JERRY */
2722 char *ret, *p;
2724 /* lookup_name() only works on the sAMAccountName to
2725 returning the username portion of userPrincipalName
2726 breaks winbindd_getpwnam() */
2728 ret = ads_pull_string(ads, mem_ctx, msg, "userPrincipalName");
2729 if (ret && (p = strchr_m(ret, '@'))) {
2730 *p = 0;
2731 return ret;
2733 #endif
2734 return ads_pull_string(ads, mem_ctx, msg, "sAMAccountName");
2739 * find the update serial number - this is the core of the ldap cache
2740 * @param ads connection to ads server
2741 * @param ads connection to ADS server
2742 * @param usn Pointer to retrieved update serial number
2743 * @return status of search
2745 ADS_STATUS ads_USN(ADS_STRUCT *ads, uint32 *usn)
2747 const char *attrs[] = {"highestCommittedUSN", NULL};
2748 ADS_STATUS status;
2749 LDAPMessage *res;
2751 status = ads_do_search_retry(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2752 if (!ADS_ERR_OK(status))
2753 return status;
2755 if (ads_count_replies(ads, res) != 1) {
2756 ads_msgfree(ads, res);
2757 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2760 if (!ads_pull_uint32(ads, res, "highestCommittedUSN", usn)) {
2761 ads_msgfree(ads, res);
2762 return ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
2765 ads_msgfree(ads, res);
2766 return ADS_SUCCESS;
2769 /* parse a ADS timestring - typical string is
2770 '20020917091222.0Z0' which means 09:12.22 17th September
2771 2002, timezone 0 */
2772 static time_t ads_parse_time(const char *str)
2774 struct tm tm;
2776 ZERO_STRUCT(tm);
2778 if (sscanf(str, "%4d%2d%2d%2d%2d%2d",
2779 &tm.tm_year, &tm.tm_mon, &tm.tm_mday,
2780 &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) {
2781 return 0;
2783 tm.tm_year -= 1900;
2784 tm.tm_mon -= 1;
2786 return timegm(&tm);
2789 /********************************************************************
2790 ********************************************************************/
2792 ADS_STATUS ads_current_time(ADS_STRUCT *ads)
2794 const char *attrs[] = {"currentTime", NULL};
2795 ADS_STATUS status;
2796 LDAPMessage *res;
2797 char *timestr;
2798 TALLOC_CTX *ctx;
2799 ADS_STRUCT *ads_s = ads;
2801 if (!(ctx = talloc_init("ads_current_time"))) {
2802 return ADS_ERROR(LDAP_NO_MEMORY);
2805 /* establish a new ldap tcp session if necessary */
2807 if ( !ads->ldap.ld ) {
2808 if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup,
2809 ads->server.ldap_server )) == NULL )
2811 goto done;
2813 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
2814 status = ads_connect( ads_s );
2815 if ( !ADS_ERR_OK(status))
2816 goto done;
2819 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2820 if (!ADS_ERR_OK(status)) {
2821 goto done;
2824 timestr = ads_pull_string(ads_s, ctx, res, "currentTime");
2825 if (!timestr) {
2826 ads_msgfree(ads_s, res);
2827 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2828 goto done;
2831 /* but save the time and offset in the original ADS_STRUCT */
2833 ads->config.current_time = ads_parse_time(timestr);
2835 if (ads->config.current_time != 0) {
2836 ads->auth.time_offset = ads->config.current_time - time(NULL);
2837 DEBUG(4,("time offset is %d seconds\n", ads->auth.time_offset));
2840 ads_msgfree(ads, res);
2842 status = ADS_SUCCESS;
2844 done:
2845 /* free any temporary ads connections */
2846 if ( ads_s != ads ) {
2847 ads_destroy( &ads_s );
2849 talloc_destroy(ctx);
2851 return status;
2854 /********************************************************************
2855 ********************************************************************/
2857 ADS_STATUS ads_domain_func_level(ADS_STRUCT *ads, uint32 *val)
2859 const char *attrs[] = {"domainFunctionality", NULL};
2860 ADS_STATUS status;
2861 LDAPMessage *res;
2862 ADS_STRUCT *ads_s = ads;
2864 *val = DS_DOMAIN_FUNCTION_2000;
2866 /* establish a new ldap tcp session if necessary */
2868 if ( !ads->ldap.ld ) {
2869 if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup,
2870 ads->server.ldap_server )) == NULL )
2872 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
2873 goto done;
2875 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
2876 status = ads_connect( ads_s );
2877 if ( !ADS_ERR_OK(status))
2878 goto done;
2881 /* If the attribute does not exist assume it is a Windows 2000
2882 functional domain */
2884 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2885 if (!ADS_ERR_OK(status)) {
2886 if ( status.err.rc == LDAP_NO_SUCH_ATTRIBUTE ) {
2887 status = ADS_SUCCESS;
2889 goto done;
2892 if ( !ads_pull_uint32(ads_s, res, "domainFunctionality", val) ) {
2893 DEBUG(5,("ads_domain_func_level: Failed to pull the domainFunctionality attribute.\n"));
2895 DEBUG(3,("ads_domain_func_level: %d\n", *val));
2898 ads_msgfree(ads, res);
2900 done:
2901 /* free any temporary ads connections */
2902 if ( ads_s != ads ) {
2903 ads_destroy( &ads_s );
2906 return status;
2910 * find the domain sid for our domain
2911 * @param ads connection to ads server
2912 * @param sid Pointer to domain sid
2913 * @return status of search
2915 ADS_STATUS ads_domain_sid(ADS_STRUCT *ads, DOM_SID *sid)
2917 const char *attrs[] = {"objectSid", NULL};
2918 LDAPMessage *res;
2919 ADS_STATUS rc;
2921 rc = ads_do_search_retry(ads, ads->config.bind_path, LDAP_SCOPE_BASE, "(objectclass=*)",
2922 attrs, &res);
2923 if (!ADS_ERR_OK(rc)) return rc;
2924 if (!ads_pull_sid(ads, res, "objectSid", sid)) {
2925 ads_msgfree(ads, res);
2926 return ADS_ERROR_SYSTEM(ENOENT);
2928 ads_msgfree(ads, res);
2930 return ADS_SUCCESS;
2934 * find our site name
2935 * @param ads connection to ads server
2936 * @param mem_ctx Pointer to talloc context
2937 * @param site_name Pointer to the sitename
2938 * @return status of search
2940 ADS_STATUS ads_site_dn(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char **site_name)
2942 ADS_STATUS status;
2943 LDAPMessage *res;
2944 const char *dn, *service_name;
2945 const char *attrs[] = { "dsServiceName", NULL };
2947 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2948 if (!ADS_ERR_OK(status)) {
2949 return status;
2952 service_name = ads_pull_string(ads, mem_ctx, res, "dsServiceName");
2953 if (service_name == NULL) {
2954 ads_msgfree(ads, res);
2955 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2958 ads_msgfree(ads, res);
2960 /* go up three levels */
2961 dn = ads_parent_dn(ads_parent_dn(ads_parent_dn(service_name)));
2962 if (dn == NULL) {
2963 return ADS_ERROR(LDAP_NO_MEMORY);
2966 *site_name = talloc_strdup(mem_ctx, dn);
2967 if (*site_name == NULL) {
2968 return ADS_ERROR(LDAP_NO_MEMORY);
2971 return status;
2973 dsServiceName: CN=NTDS Settings,CN=W2K3DC,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,DC=ber,DC=suse,DC=de
2978 * find the site dn where a machine resides
2979 * @param ads connection to ads server
2980 * @param mem_ctx Pointer to talloc context
2981 * @param computer_name name of the machine
2982 * @param site_name Pointer to the sitename
2983 * @return status of search
2985 ADS_STATUS ads_site_dn_for_machine(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char *computer_name, const char **site_dn)
2987 ADS_STATUS status;
2988 LDAPMessage *res;
2989 const char *parent, *filter;
2990 char *config_context = NULL;
2991 char *dn;
2993 /* shortcut a query */
2994 if (strequal(computer_name, ads->config.ldap_server_name)) {
2995 return ads_site_dn(ads, mem_ctx, site_dn);
2998 status = ads_config_path(ads, mem_ctx, &config_context);
2999 if (!ADS_ERR_OK(status)) {
3000 return status;
3003 filter = talloc_asprintf(mem_ctx, "(cn=%s)", computer_name);
3004 if (filter == NULL) {
3005 return ADS_ERROR(LDAP_NO_MEMORY);
3008 status = ads_do_search(ads, config_context, LDAP_SCOPE_SUBTREE,
3009 filter, NULL, &res);
3010 if (!ADS_ERR_OK(status)) {
3011 return status;
3014 if (ads_count_replies(ads, res) != 1) {
3015 ads_msgfree(ads, res);
3016 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
3019 dn = ads_get_dn(ads, res);
3020 if (dn == NULL) {
3021 ads_msgfree(ads, res);
3022 return ADS_ERROR(LDAP_NO_MEMORY);
3025 /* go up three levels */
3026 parent = ads_parent_dn(ads_parent_dn(ads_parent_dn(dn)));
3027 if (parent == NULL) {
3028 ads_msgfree(ads, res);
3029 ads_memfree(ads, dn);
3030 return ADS_ERROR(LDAP_NO_MEMORY);
3033 *site_dn = talloc_strdup(mem_ctx, parent);
3034 if (*site_dn == NULL) {
3035 ads_msgfree(ads, res);
3036 ads_memfree(ads, dn);
3037 return ADS_ERROR(LDAP_NO_MEMORY);
3040 ads_memfree(ads, dn);
3041 ads_msgfree(ads, res);
3043 return status;
3047 * get the upn suffixes for a domain
3048 * @param ads connection to ads server
3049 * @param mem_ctx Pointer to talloc context
3050 * @param suffixes Pointer to an array of suffixes
3051 * @param num_suffixes Pointer to the number of suffixes
3052 * @return status of search
3054 ADS_STATUS ads_upn_suffixes(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, char ***suffixes, size_t *num_suffixes)
3056 ADS_STATUS status;
3057 LDAPMessage *res;
3058 const char *base;
3059 char *config_context = NULL;
3060 const char *attrs[] = { "uPNSuffixes", NULL };
3062 status = ads_config_path(ads, mem_ctx, &config_context);
3063 if (!ADS_ERR_OK(status)) {
3064 return status;
3067 base = talloc_asprintf(mem_ctx, "cn=Partitions,%s", config_context);
3068 if (base == NULL) {
3069 return ADS_ERROR(LDAP_NO_MEMORY);
3072 status = ads_search_dn(ads, &res, base, attrs);
3073 if (!ADS_ERR_OK(status)) {
3074 return status;
3077 if (ads_count_replies(ads, res) != 1) {
3078 ads_msgfree(ads, res);
3079 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
3082 (*suffixes) = ads_pull_strings(ads, mem_ctx, res, "uPNSuffixes", num_suffixes);
3083 if ((*suffixes) == NULL) {
3084 ads_msgfree(ads, res);
3085 return ADS_ERROR(LDAP_NO_MEMORY);
3088 ads_msgfree(ads, res);
3090 return status;
3094 * get the joinable ous for a domain
3095 * @param ads connection to ads server
3096 * @param mem_ctx Pointer to talloc context
3097 * @param ous Pointer to an array of ous
3098 * @param num_ous Pointer to the number of ous
3099 * @return status of search
3101 ADS_STATUS ads_get_joinable_ous(ADS_STRUCT *ads,
3102 TALLOC_CTX *mem_ctx,
3103 char ***ous,
3104 size_t *num_ous)
3106 ADS_STATUS status;
3107 LDAPMessage *res = NULL;
3108 LDAPMessage *msg = NULL;
3109 const char *attrs[] = { "dn", NULL };
3110 int count = 0;
3112 status = ads_search(ads, &res,
3113 "(|(objectClass=domain)(objectclass=organizationalUnit))",
3114 attrs);
3115 if (!ADS_ERR_OK(status)) {
3116 return status;
3119 count = ads_count_replies(ads, res);
3120 if (count < 1) {
3121 ads_msgfree(ads, res);
3122 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3125 for (msg = ads_first_entry(ads, res); msg;
3126 msg = ads_next_entry(ads, msg)) {
3128 char *dn = NULL;
3130 dn = ads_get_dn(ads, msg);
3131 if (!dn) {
3132 ads_msgfree(ads, res);
3133 return ADS_ERROR(LDAP_NO_MEMORY);
3136 if (!add_string_to_array(mem_ctx, dn,
3137 (const char ***)ous,
3138 (int *)num_ous)) {
3139 ads_memfree(ads, dn);
3140 ads_msgfree(ads, res);
3141 return ADS_ERROR(LDAP_NO_MEMORY);
3144 ads_memfree(ads, dn);
3147 ads_msgfree(ads, res);
3149 return status;
3154 * pull a DOM_SID from an extended dn string
3155 * @param mem_ctx TALLOC_CTX
3156 * @param extended_dn string
3157 * @param flags string type of extended_dn
3158 * @param sid pointer to a DOM_SID
3159 * @return NT_STATUS_OK on success,
3160 * NT_INVALID_PARAMETER on error,
3161 * NT_STATUS_NOT_FOUND if no SID present
3163 ADS_STATUS ads_get_sid_from_extended_dn(TALLOC_CTX *mem_ctx,
3164 const char *extended_dn,
3165 enum ads_extended_dn_flags flags,
3166 DOM_SID *sid)
3168 char *p, *q, *dn;
3170 if (!extended_dn) {
3171 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3174 /* otherwise extended_dn gets stripped off */
3175 if ((dn = talloc_strdup(mem_ctx, extended_dn)) == NULL) {
3176 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3179 * ADS_EXTENDED_DN_HEX_STRING:
3180 * <GUID=238e1963cb390f4bb032ba0105525a29>;<SID=010500000000000515000000bb68c8fd6b61b427572eb04556040000>;CN=gd,OU=berlin,OU=suse,DC=ber,DC=suse,DC=de
3182 * ADS_EXTENDED_DN_STRING (only with w2k3):
3183 * <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
3185 * Object with no SID, such as an Exchange Public Folder
3186 * <GUID=28907fb4bdf6854993e7f0a10b504e7c>;CN=public,CN=Microsoft Exchange System Objects,DC=sd2k3ms,DC=west,DC=isilon,DC=com
3189 p = strchr(dn, ';');
3190 if (!p) {
3191 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3194 if (strncmp(p, ";<SID=", strlen(";<SID=")) != 0) {
3195 DEBUG(5,("No SID present in extended dn\n"));
3196 return ADS_ERROR_NT(NT_STATUS_NOT_FOUND);
3199 p += strlen(";<SID=");
3201 q = strchr(p, '>');
3202 if (!q) {
3203 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3206 *q = '\0';
3208 DEBUG(100,("ads_get_sid_from_extended_dn: sid string is %s\n", p));
3210 switch (flags) {
3212 case ADS_EXTENDED_DN_STRING:
3213 if (!string_to_sid(sid, p)) {
3214 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3216 break;
3217 case ADS_EXTENDED_DN_HEX_STRING: {
3218 fstring buf;
3219 size_t buf_len;
3221 buf_len = strhex_to_str(buf, sizeof(buf), p, strlen(p));
3222 if (buf_len == 0) {
3223 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3226 if (!sid_parse(buf, buf_len, sid)) {
3227 DEBUG(10,("failed to parse sid\n"));
3228 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3230 break;
3232 default:
3233 DEBUG(10,("unknown extended dn format\n"));
3234 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3237 return ADS_ERROR_NT(NT_STATUS_OK);
3241 * pull an array of DOM_SIDs from a ADS result
3242 * @param ads connection to ads server
3243 * @param mem_ctx TALLOC_CTX for allocating sid array
3244 * @param msg Results of search
3245 * @param field Attribute to retrieve
3246 * @param flags string type of extended_dn
3247 * @param sids pointer to sid array to allocate
3248 * @return the count of SIDs pulled
3250 int ads_pull_sids_from_extendeddn(ADS_STRUCT *ads,
3251 TALLOC_CTX *mem_ctx,
3252 LDAPMessage *msg,
3253 const char *field,
3254 enum ads_extended_dn_flags flags,
3255 DOM_SID **sids)
3257 int i;
3258 ADS_STATUS rc;
3259 size_t dn_count, ret_count = 0;
3260 char **dn_strings;
3262 if ((dn_strings = ads_pull_strings(ads, mem_ctx, msg, field,
3263 &dn_count)) == NULL) {
3264 return 0;
3267 (*sids) = TALLOC_ZERO_ARRAY(mem_ctx, DOM_SID, dn_count + 1);
3268 if (!(*sids)) {
3269 TALLOC_FREE(dn_strings);
3270 return 0;
3273 for (i=0; i<dn_count; i++) {
3274 rc = ads_get_sid_from_extended_dn(mem_ctx, dn_strings[i],
3275 flags, &(*sids)[i]);
3276 if (!ADS_ERR_OK(rc)) {
3277 if (NT_STATUS_EQUAL(ads_ntstatus(rc),
3278 NT_STATUS_NOT_FOUND)) {
3279 continue;
3281 else {
3282 TALLOC_FREE(*sids);
3283 TALLOC_FREE(dn_strings);
3284 return 0;
3287 ret_count++;
3290 TALLOC_FREE(dn_strings);
3292 return ret_count;
3295 /********************************************************************
3296 ********************************************************************/
3298 char* ads_get_dnshostname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3300 LDAPMessage *res = NULL;
3301 ADS_STATUS status;
3302 int count = 0;
3303 char *name = NULL;
3305 status = ads_find_machine_acct(ads, &res, global_myname());
3306 if (!ADS_ERR_OK(status)) {
3307 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
3308 global_myname()));
3309 goto out;
3312 if ( (count = ads_count_replies(ads, res)) != 1 ) {
3313 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
3314 goto out;
3317 if ( (name = ads_pull_string(ads, ctx, res, "dNSHostName")) == NULL ) {
3318 DEBUG(0,("ads_get_dnshostname: No dNSHostName attribute!\n"));
3321 out:
3322 ads_msgfree(ads, res);
3324 return name;
3327 /********************************************************************
3328 ********************************************************************/
3330 char* ads_get_upn( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3332 LDAPMessage *res = NULL;
3333 ADS_STATUS status;
3334 int count = 0;
3335 char *name = NULL;
3337 status = ads_find_machine_acct(ads, &res, machine_name);
3338 if (!ADS_ERR_OK(status)) {
3339 DEBUG(0,("ads_get_upn: Failed to find account for %s\n",
3340 global_myname()));
3341 goto out;
3344 if ( (count = ads_count_replies(ads, res)) != 1 ) {
3345 DEBUG(1,("ads_get_upn: %d entries returned!\n", count));
3346 goto out;
3349 if ( (name = ads_pull_string(ads, ctx, res, "userPrincipalName")) == NULL ) {
3350 DEBUG(2,("ads_get_upn: No userPrincipalName attribute!\n"));
3353 out:
3354 ads_msgfree(ads, res);
3356 return name;
3359 /********************************************************************
3360 ********************************************************************/
3362 char* ads_get_samaccountname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3364 LDAPMessage *res = NULL;
3365 ADS_STATUS status;
3366 int count = 0;
3367 char *name = NULL;
3369 status = ads_find_machine_acct(ads, &res, global_myname());
3370 if (!ADS_ERR_OK(status)) {
3371 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
3372 global_myname()));
3373 goto out;
3376 if ( (count = ads_count_replies(ads, res)) != 1 ) {
3377 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
3378 goto out;
3381 if ( (name = ads_pull_string(ads, ctx, res, "sAMAccountName")) == NULL ) {
3382 DEBUG(0,("ads_get_dnshostname: No sAMAccountName attribute!\n"));
3385 out:
3386 ads_msgfree(ads, res);
3388 return name;
3391 #if 0
3393 SAVED CODE - we used to join via ldap - remember how we did this. JRA.
3396 * Join a machine to a realm
3397 * Creates the machine account and sets the machine password
3398 * @param ads connection to ads server
3399 * @param machine name of host to add
3400 * @param org_unit Organizational unit to place machine in
3401 * @return status of join
3403 ADS_STATUS ads_join_realm(ADS_STRUCT *ads, const char *machine_name,
3404 uint32 account_type, const char *org_unit)
3406 ADS_STATUS status;
3407 LDAPMessage *res = NULL;
3408 char *machine;
3410 /* machine name must be lowercase */
3411 machine = SMB_STRDUP(machine_name);
3412 strlower_m(machine);
3415 status = ads_find_machine_acct(ads, (void **)&res, machine);
3416 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
3417 DEBUG(0, ("Host account for %s already exists - deleting old account\n", machine));
3418 status = ads_leave_realm(ads, machine);
3419 if (!ADS_ERR_OK(status)) {
3420 DEBUG(0, ("Failed to delete host '%s' from the '%s' realm.\n",
3421 machine, ads->config.realm));
3422 return status;
3426 status = ads_add_machine_acct(ads, machine, account_type, org_unit);
3427 if (!ADS_ERR_OK(status)) {
3428 DEBUG(0, ("ads_join_realm: ads_add_machine_acct failed (%s): %s\n", machine, ads_errstr(status)));
3429 SAFE_FREE(machine);
3430 return status;
3433 status = ads_find_machine_acct(ads, (void **)(void *)&res, machine);
3434 if (!ADS_ERR_OK(status)) {
3435 DEBUG(0, ("ads_join_realm: Host account test failed for machine %s\n", machine));
3436 SAFE_FREE(machine);
3437 return status;
3440 SAFE_FREE(machine);
3441 ads_msgfree(ads, res);
3443 return status;
3445 #endif
3448 * Delete a machine from the realm
3449 * @param ads connection to ads server
3450 * @param hostname Machine to remove
3451 * @return status of delete
3453 ADS_STATUS ads_leave_realm(ADS_STRUCT *ads, const char *hostname)
3455 ADS_STATUS status;
3456 void *msg;
3457 LDAPMessage *res;
3458 char *hostnameDN, *host;
3459 int rc;
3460 LDAPControl ldap_control;
3461 LDAPControl * pldap_control[2] = {NULL, NULL};
3463 pldap_control[0] = &ldap_control;
3464 memset(&ldap_control, 0, sizeof(LDAPControl));
3465 ldap_control.ldctl_oid = (char *)LDAP_SERVER_TREE_DELETE_OID;
3467 /* hostname must be lowercase */
3468 host = SMB_STRDUP(hostname);
3469 strlower_m(host);
3471 status = ads_find_machine_acct(ads, &res, host);
3472 if (!ADS_ERR_OK(status)) {
3473 DEBUG(0, ("Host account for %s does not exist.\n", host));
3474 SAFE_FREE(host);
3475 return status;
3478 msg = ads_first_entry(ads, res);
3479 if (!msg) {
3480 SAFE_FREE(host);
3481 return ADS_ERROR_SYSTEM(ENOENT);
3484 hostnameDN = ads_get_dn(ads, (LDAPMessage *)msg);
3486 rc = ldap_delete_ext_s(ads->ldap.ld, hostnameDN, pldap_control, NULL);
3487 if (rc) {
3488 DEBUG(3,("ldap_delete_ext_s failed with error code %d\n", rc));
3489 }else {
3490 DEBUG(3,("ldap_delete_ext_s succeeded with error code %d\n", rc));
3493 if (rc != LDAP_SUCCESS) {
3494 const char *attrs[] = { "cn", NULL };
3495 LDAPMessage *msg_sub;
3497 /* we only search with scope ONE, we do not expect any further
3498 * objects to be created deeper */
3500 status = ads_do_search_retry(ads, hostnameDN,
3501 LDAP_SCOPE_ONELEVEL,
3502 "(objectclass=*)", attrs, &res);
3504 if (!ADS_ERR_OK(status)) {
3505 SAFE_FREE(host);
3506 ads_memfree(ads, hostnameDN);
3507 return status;
3510 for (msg_sub = ads_first_entry(ads, res); msg_sub;
3511 msg_sub = ads_next_entry(ads, msg_sub)) {
3513 char *dn = NULL;
3515 if ((dn = ads_get_dn(ads, msg_sub)) == NULL) {
3516 SAFE_FREE(host);
3517 ads_memfree(ads, hostnameDN);
3518 return ADS_ERROR(LDAP_NO_MEMORY);
3521 status = ads_del_dn(ads, dn);
3522 if (!ADS_ERR_OK(status)) {
3523 DEBUG(3,("failed to delete dn %s: %s\n", dn, ads_errstr(status)));
3524 SAFE_FREE(host);
3525 ads_memfree(ads, dn);
3526 ads_memfree(ads, hostnameDN);
3527 return status;
3530 ads_memfree(ads, dn);
3533 /* there should be no subordinate objects anymore */
3534 status = ads_do_search_retry(ads, hostnameDN,
3535 LDAP_SCOPE_ONELEVEL,
3536 "(objectclass=*)", attrs, &res);
3538 if (!ADS_ERR_OK(status) || ( (ads_count_replies(ads, res)) > 0 ) ) {
3539 SAFE_FREE(host);
3540 ads_memfree(ads, hostnameDN);
3541 return status;
3544 /* delete hostnameDN now */
3545 status = ads_del_dn(ads, hostnameDN);
3546 if (!ADS_ERR_OK(status)) {
3547 SAFE_FREE(host);
3548 DEBUG(3,("failed to delete dn %s: %s\n", hostnameDN, ads_errstr(status)));
3549 ads_memfree(ads, hostnameDN);
3550 return status;
3554 ads_memfree(ads, hostnameDN);
3556 status = ads_find_machine_acct(ads, &res, host);
3557 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
3558 DEBUG(3, ("Failed to remove host account.\n"));
3559 SAFE_FREE(host);
3560 return status;
3563 SAFE_FREE(host);
3564 return status;
3568 * pull all token-sids from an LDAP dn
3569 * @param ads connection to ads server
3570 * @param mem_ctx TALLOC_CTX for allocating sid array
3571 * @param dn of LDAP object
3572 * @param user_sid pointer to DOM_SID (objectSid)
3573 * @param primary_group_sid pointer to DOM_SID (self composed)
3574 * @param sids pointer to sid array to allocate
3575 * @param num_sids counter of SIDs pulled
3576 * @return status of token query
3578 ADS_STATUS ads_get_tokensids(ADS_STRUCT *ads,
3579 TALLOC_CTX *mem_ctx,
3580 const char *dn,
3581 DOM_SID *user_sid,
3582 DOM_SID *primary_group_sid,
3583 DOM_SID **sids,
3584 size_t *num_sids)
3586 ADS_STATUS status;
3587 LDAPMessage *res = NULL;
3588 int count = 0;
3589 size_t tmp_num_sids;
3590 DOM_SID *tmp_sids;
3591 DOM_SID tmp_user_sid;
3592 DOM_SID tmp_primary_group_sid;
3593 uint32 pgid;
3594 const char *attrs[] = {
3595 "objectSid",
3596 "tokenGroups",
3597 "primaryGroupID",
3598 NULL
3601 status = ads_search_retry_dn(ads, &res, dn, attrs);
3602 if (!ADS_ERR_OK(status)) {
3603 return status;
3606 count = ads_count_replies(ads, res);
3607 if (count != 1) {
3608 ads_msgfree(ads, res);
3609 return ADS_ERROR_LDAP(LDAP_NO_SUCH_OBJECT);
3612 if (!ads_pull_sid(ads, res, "objectSid", &tmp_user_sid)) {
3613 ads_msgfree(ads, res);
3614 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3617 if (!ads_pull_uint32(ads, res, "primaryGroupID", &pgid)) {
3618 ads_msgfree(ads, res);
3619 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3623 /* hack to compose the primary group sid without knowing the
3624 * domsid */
3626 DOM_SID domsid;
3627 uint32 dummy_rid;
3629 sid_copy(&domsid, &tmp_user_sid);
3631 if (!sid_split_rid(&domsid, &dummy_rid)) {
3632 ads_msgfree(ads, res);
3633 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3636 if (!sid_compose(&tmp_primary_group_sid, &domsid, pgid)) {
3637 ads_msgfree(ads, res);
3638 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3642 tmp_num_sids = ads_pull_sids(ads, mem_ctx, res, "tokenGroups", &tmp_sids);
3644 if (tmp_num_sids == 0 || !tmp_sids) {
3645 ads_msgfree(ads, res);
3646 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3649 if (num_sids) {
3650 *num_sids = tmp_num_sids;
3653 if (sids) {
3654 *sids = tmp_sids;
3657 if (user_sid) {
3658 *user_sid = tmp_user_sid;
3661 if (primary_group_sid) {
3662 *primary_group_sid = tmp_primary_group_sid;
3665 DEBUG(10,("ads_get_tokensids: returned %d sids\n", (int)tmp_num_sids + 2));
3667 ads_msgfree(ads, res);
3668 return ADS_ERROR_LDAP(LDAP_SUCCESS);
3672 * Find a sAMAccoutName in LDAP
3673 * @param ads connection to ads server
3674 * @param mem_ctx TALLOC_CTX for allocating sid array
3675 * @param samaccountname to search
3676 * @param uac_ret uint32 pointer userAccountControl attribute value
3677 * @param dn_ret pointer to dn
3678 * @return status of token query
3680 ADS_STATUS ads_find_samaccount(ADS_STRUCT *ads,
3681 TALLOC_CTX *mem_ctx,
3682 const char *samaccountname,
3683 uint32 *uac_ret,
3684 const char **dn_ret)
3686 ADS_STATUS status;
3687 const char *attrs[] = { "userAccountControl", NULL };
3688 const char *filter;
3689 LDAPMessage *res = NULL;
3690 char *dn = NULL;
3691 uint32 uac = 0;
3693 filter = talloc_asprintf(mem_ctx, "(&(objectclass=user)(sAMAccountName=%s))",
3694 samaccountname);
3695 if (filter == NULL) {
3696 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
3697 goto out;
3700 status = ads_do_search_all(ads, ads->config.bind_path,
3701 LDAP_SCOPE_SUBTREE,
3702 filter, attrs, &res);
3704 if (!ADS_ERR_OK(status)) {
3705 goto out;
3708 if (ads_count_replies(ads, res) != 1) {
3709 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3710 goto out;
3713 dn = ads_get_dn(ads, res);
3714 if (dn == NULL) {
3715 status = ADS_ERROR(LDAP_NO_MEMORY);
3716 goto out;
3719 if (!ads_pull_uint32(ads, res, "userAccountControl", &uac)) {
3720 status = ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
3721 goto out;
3724 if (uac_ret) {
3725 *uac_ret = uac;
3728 if (dn_ret) {
3729 *dn_ret = talloc_strdup(mem_ctx, dn);
3730 if (!*dn_ret) {
3731 status = ADS_ERROR(LDAP_NO_MEMORY);
3732 goto out;
3735 out:
3736 ads_memfree(ads, dn);
3737 ads_msgfree(ads, res);
3739 return status;
3743 * find our configuration path
3744 * @param ads connection to ads server
3745 * @param mem_ctx Pointer to talloc context
3746 * @param config_path Pointer to the config path
3747 * @return status of search
3749 ADS_STATUS ads_config_path(ADS_STRUCT *ads,
3750 TALLOC_CTX *mem_ctx,
3751 char **config_path)
3753 ADS_STATUS status;
3754 LDAPMessage *res = NULL;
3755 const char *config_context = NULL;
3756 const char *attrs[] = { "configurationNamingContext", NULL };
3758 status = ads_do_search(ads, "", LDAP_SCOPE_BASE,
3759 "(objectclass=*)", attrs, &res);
3760 if (!ADS_ERR_OK(status)) {
3761 return status;
3764 config_context = ads_pull_string(ads, mem_ctx, res,
3765 "configurationNamingContext");
3766 ads_msgfree(ads, res);
3767 if (!config_context) {
3768 return ADS_ERROR(LDAP_NO_MEMORY);
3771 if (config_path) {
3772 *config_path = talloc_strdup(mem_ctx, config_context);
3773 if (!*config_path) {
3774 return ADS_ERROR(LDAP_NO_MEMORY);
3778 return ADS_ERROR(LDAP_SUCCESS);
3782 * find the displayName of an extended right
3783 * @param ads connection to ads server
3784 * @param config_path The config path
3785 * @param mem_ctx Pointer to talloc context
3786 * @param GUID struct of the rightsGUID
3787 * @return status of search
3789 const char *ads_get_extended_right_name_by_guid(ADS_STRUCT *ads,
3790 const char *config_path,
3791 TALLOC_CTX *mem_ctx,
3792 const struct GUID *rights_guid)
3794 ADS_STATUS rc;
3795 LDAPMessage *res = NULL;
3796 char *expr = NULL;
3797 const char *attrs[] = { "displayName", NULL };
3798 const char *result = NULL;
3799 const char *path;
3801 if (!ads || !mem_ctx || !rights_guid) {
3802 goto done;
3805 expr = talloc_asprintf(mem_ctx, "(rightsGuid=%s)",
3806 smb_uuid_string(mem_ctx, *rights_guid));
3807 if (!expr) {
3808 goto done;
3811 path = talloc_asprintf(mem_ctx, "cn=Extended-Rights,%s", config_path);
3812 if (!path) {
3813 goto done;
3816 rc = ads_do_search_retry(ads, path, LDAP_SCOPE_SUBTREE,
3817 expr, attrs, &res);
3818 if (!ADS_ERR_OK(rc)) {
3819 goto done;
3822 if (ads_count_replies(ads, res) != 1) {
3823 goto done;
3826 result = ads_pull_string(ads, mem_ctx, res, "displayName");
3828 done:
3829 ads_msgfree(ads, res);
3830 return result;
3835 * verify or build and verify an account ou
3836 * @param mem_ctx Pointer to talloc context
3837 * @param ads connection to ads server
3838 * @param account_ou
3839 * @return status of search
3842 ADS_STATUS ads_check_ou_dn(TALLOC_CTX *mem_ctx,
3843 ADS_STRUCT *ads,
3844 const char **account_ou)
3846 struct ldb_dn *name_dn = NULL;
3847 const char *name = NULL;
3848 char *ou_string = NULL;
3850 name_dn = ldb_dn_explode(mem_ctx, *account_ou);
3851 if (name_dn) {
3852 return ADS_SUCCESS;
3855 ou_string = ads_ou_string(ads, *account_ou);
3856 if (!ou_string) {
3857 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX);
3860 name = talloc_asprintf(mem_ctx, "%s,%s", ou_string,
3861 ads->config.bind_path);
3862 SAFE_FREE(ou_string);
3863 if (!name) {
3864 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3867 name_dn = ldb_dn_explode(mem_ctx, name);
3868 if (!name_dn) {
3869 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX);
3872 *account_ou = talloc_strdup(mem_ctx, name);
3873 if (!*account_ou) {
3874 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3877 return ADS_SUCCESS;
3880 #endif