Merge branch 'master' of ssh://jra@git.samba.org/data/git/samba
[Samba/nascimento.git] / source3 / libads / ldap.c
blobcf8a7ebb1b3750f844df59faa6294d7c2ed38a47
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 asprintf(&ads->auth.user_name, "%s$", global_myname() );
609 if (!ads->auth.realm) {
610 ads->auth.realm = SMB_STRDUP(ads->config.realm);
613 if (!ads->auth.kdc_server) {
614 print_sockaddr(addr, sizeof(addr), &ads->ldap.ss);
615 ads->auth.kdc_server = SMB_STRDUP(addr);
618 #if KRB5_DNS_HACK
619 /* this is a really nasty hack to avoid ADS DNS problems. It needs a patch
620 to MIT kerberos to work (tridge) */
622 char *env;
623 asprintf(&env, "KRB5_KDC_ADDRESS_%s", ads->config.realm);
624 setenv(env, ads->auth.kdc_server, 1);
625 free(env);
627 #endif
629 /* If the caller() requested no LDAP bind, then we are done */
631 if (ads->auth.flags & ADS_AUTH_NO_BIND) {
632 status = ADS_SUCCESS;
633 goto out;
636 ads->ldap.mem_ctx = talloc_init("ads LDAP connection memory");
637 if (!ads->ldap.mem_ctx) {
638 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
639 goto out;
642 /* Otherwise setup the TCP LDAP session */
644 ads->ldap.ld = ldap_open_with_timeout(ads->config.ldap_server_name,
645 ads->ldap.port, lp_ldap_timeout());
646 if (ads->ldap.ld == NULL) {
647 status = ADS_ERROR(LDAP_OPERATIONS_ERROR);
648 goto out;
650 DEBUG(3,("Connected to LDAP server %s\n", ads->config.ldap_server_name));
652 /* cache the successful connection for workgroup and realm */
653 if (ads_closest_dc(ads)) {
654 saf_store( ads->server.workgroup, ads->config.ldap_server_name);
655 saf_store( ads->server.realm, ads->config.ldap_server_name);
658 ldap_set_option(ads->ldap.ld, LDAP_OPT_PROTOCOL_VERSION, &version);
660 status = ADS_ERROR(smb_ldap_start_tls(ads->ldap.ld, version));
661 if (!ADS_ERR_OK(status)) {
662 goto out;
665 /* fill in the current time and offsets */
667 status = ads_current_time( ads );
668 if ( !ADS_ERR_OK(status) ) {
669 goto out;
672 /* Now do the bind */
674 if (ads->auth.flags & ADS_AUTH_ANON_BIND) {
675 status = ADS_ERROR(ldap_simple_bind_s(ads->ldap.ld, NULL, NULL));
676 goto out;
679 if (ads->auth.flags & ADS_AUTH_SIMPLE_BIND) {
680 status = ADS_ERROR(ldap_simple_bind_s(ads->ldap.ld, ads->auth.user_name, ads->auth.password));
681 goto out;
684 status = ads_sasl_bind(ads);
686 out:
687 if (DEBUGLEVEL >= 11) {
688 char *s = NDR_PRINT_STRUCT_STRING(talloc_tos(), ads_struct, ads);
689 DEBUG(11,("ads_connect: leaving with: %s\n",
690 ads_errstr(status)));
691 DEBUGADD(11,("%s\n", s));
692 TALLOC_FREE(s);
695 return status;
699 * Connect to the LDAP server using given credentials
700 * @param ads Pointer to an existing ADS_STRUCT
701 * @return status of connection
703 ADS_STATUS ads_connect_user_creds(ADS_STRUCT *ads)
705 ads->auth.flags |= ADS_AUTH_USER_CREDS;
707 return ads_connect(ads);
711 * Disconnect the LDAP server
712 * @param ads Pointer to an existing ADS_STRUCT
714 void ads_disconnect(ADS_STRUCT *ads)
716 if (ads->ldap.ld) {
717 ldap_unbind(ads->ldap.ld);
718 ads->ldap.ld = NULL;
720 if (ads->ldap.wrap_ops && ads->ldap.wrap_ops->disconnect) {
721 ads->ldap.wrap_ops->disconnect(ads);
723 if (ads->ldap.mem_ctx) {
724 talloc_free(ads->ldap.mem_ctx);
726 ZERO_STRUCT(ads->ldap);
730 Duplicate a struct berval into talloc'ed memory
732 static struct berval *dup_berval(TALLOC_CTX *ctx, const struct berval *in_val)
734 struct berval *value;
736 if (!in_val) return NULL;
738 value = TALLOC_ZERO_P(ctx, struct berval);
739 if (value == NULL)
740 return NULL;
741 if (in_val->bv_len == 0) return value;
743 value->bv_len = in_val->bv_len;
744 value->bv_val = (char *)TALLOC_MEMDUP(ctx, in_val->bv_val,
745 in_val->bv_len);
746 return value;
750 Make a values list out of an array of (struct berval *)
752 static struct berval **ads_dup_values(TALLOC_CTX *ctx,
753 const struct berval **in_vals)
755 struct berval **values;
756 int i;
758 if (!in_vals) return NULL;
759 for (i=0; in_vals[i]; i++)
760 ; /* count values */
761 values = TALLOC_ZERO_ARRAY(ctx, struct berval *, i+1);
762 if (!values) return NULL;
764 for (i=0; in_vals[i]; i++) {
765 values[i] = dup_berval(ctx, in_vals[i]);
767 return values;
771 UTF8-encode a values list out of an array of (char *)
773 static char **ads_push_strvals(TALLOC_CTX *ctx, const char **in_vals)
775 char **values;
776 int i;
777 size_t size;
779 if (!in_vals) return NULL;
780 for (i=0; in_vals[i]; i++)
781 ; /* count values */
782 values = TALLOC_ZERO_ARRAY(ctx, char *, i+1);
783 if (!values) return NULL;
785 for (i=0; in_vals[i]; i++) {
786 if (!push_utf8_talloc(ctx, &values[i], in_vals[i], &size)) {
787 TALLOC_FREE(values);
788 return NULL;
791 return values;
795 Pull a (char *) array out of a UTF8-encoded values list
797 static char **ads_pull_strvals(TALLOC_CTX *ctx, const char **in_vals)
799 char **values;
800 int i;
801 size_t converted_size;
803 if (!in_vals) return NULL;
804 for (i=0; in_vals[i]; i++)
805 ; /* count values */
806 values = TALLOC_ZERO_ARRAY(ctx, char *, i+1);
807 if (!values) return NULL;
809 for (i=0; in_vals[i]; i++) {
810 if (!pull_utf8_talloc(ctx, &values[i], in_vals[i],
811 &converted_size)) {
812 DEBUG(0,("ads_pull_strvals: pull_utf8_talloc failed: "
813 "%s", strerror(errno)));
816 return values;
820 * Do a search with paged results. cookie must be null on the first
821 * call, and then returned on each subsequent call. It will be null
822 * again when the entire search is complete
823 * @param ads connection to ads server
824 * @param bind_path Base dn for the search
825 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
826 * @param expr Search expression - specified in local charset
827 * @param attrs Attributes to retrieve - specified in utf8 or ascii
828 * @param res ** which will contain results - free res* with ads_msgfree()
829 * @param count Number of entries retrieved on this page
830 * @param cookie The paged results cookie to be returned on subsequent calls
831 * @return status of search
833 static ADS_STATUS ads_do_paged_search_args(ADS_STRUCT *ads,
834 const char *bind_path,
835 int scope, const char *expr,
836 const char **attrs, void *args,
837 LDAPMessage **res,
838 int *count, struct berval **cookie)
840 int rc, i, version;
841 char *utf8_expr, *utf8_path, **search_attrs = NULL;
842 size_t converted_size;
843 LDAPControl PagedResults, NoReferrals, ExternalCtrl, *controls[4], **rcontrols;
844 BerElement *cookie_be = NULL;
845 struct berval *cookie_bv= NULL;
846 BerElement *ext_be = NULL;
847 struct berval *ext_bv= NULL;
849 TALLOC_CTX *ctx;
850 ads_control *external_control = (ads_control *) args;
852 *res = NULL;
854 if (!(ctx = talloc_init("ads_do_paged_search_args")))
855 return ADS_ERROR(LDAP_NO_MEMORY);
857 /* 0 means the conversion worked but the result was empty
858 so we only fail if it's -1. In any case, it always
859 at least nulls out the dest */
860 if (!push_utf8_talloc(ctx, &utf8_expr, expr, &converted_size) ||
861 !push_utf8_talloc(ctx, &utf8_path, bind_path, &converted_size))
863 rc = LDAP_NO_MEMORY;
864 goto done;
867 if (!attrs || !(*attrs))
868 search_attrs = NULL;
869 else {
870 /* This would be the utf8-encoded version...*/
871 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
872 if (!(search_attrs = str_list_copy(talloc_tos(), attrs))) {
873 rc = LDAP_NO_MEMORY;
874 goto done;
878 /* Paged results only available on ldap v3 or later */
879 ldap_get_option(ads->ldap.ld, LDAP_OPT_PROTOCOL_VERSION, &version);
880 if (version < LDAP_VERSION3) {
881 rc = LDAP_NOT_SUPPORTED;
882 goto done;
885 cookie_be = ber_alloc_t(LBER_USE_DER);
886 if (*cookie) {
887 ber_printf(cookie_be, "{iO}", (ber_int_t) 1000, *cookie);
888 ber_bvfree(*cookie); /* don't need it from last time */
889 *cookie = NULL;
890 } else {
891 ber_printf(cookie_be, "{io}", (ber_int_t) 1000, "", 0);
893 ber_flatten(cookie_be, &cookie_bv);
894 PagedResults.ldctl_oid = CONST_DISCARD(char *, ADS_PAGE_CTL_OID);
895 PagedResults.ldctl_iscritical = (char) 1;
896 PagedResults.ldctl_value.bv_len = cookie_bv->bv_len;
897 PagedResults.ldctl_value.bv_val = cookie_bv->bv_val;
899 NoReferrals.ldctl_oid = CONST_DISCARD(char *, ADS_NO_REFERRALS_OID);
900 NoReferrals.ldctl_iscritical = (char) 0;
901 NoReferrals.ldctl_value.bv_len = 0;
902 NoReferrals.ldctl_value.bv_val = CONST_DISCARD(char *, "");
904 if (external_control &&
905 (strequal(external_control->control, ADS_EXTENDED_DN_OID) ||
906 strequal(external_control->control, ADS_SD_FLAGS_OID))) {
908 ExternalCtrl.ldctl_oid = CONST_DISCARD(char *, external_control->control);
909 ExternalCtrl.ldctl_iscritical = (char) external_control->critical;
911 /* win2k does not accept a ldctl_value beeing passed in */
913 if (external_control->val != 0) {
915 if ((ext_be = ber_alloc_t(LBER_USE_DER)) == NULL ) {
916 rc = LDAP_NO_MEMORY;
917 goto done;
920 if ((ber_printf(ext_be, "{i}", (ber_int_t) external_control->val)) == -1) {
921 rc = LDAP_NO_MEMORY;
922 goto done;
924 if ((ber_flatten(ext_be, &ext_bv)) == -1) {
925 rc = LDAP_NO_MEMORY;
926 goto done;
929 ExternalCtrl.ldctl_value.bv_len = ext_bv->bv_len;
930 ExternalCtrl.ldctl_value.bv_val = ext_bv->bv_val;
932 } else {
933 ExternalCtrl.ldctl_value.bv_len = 0;
934 ExternalCtrl.ldctl_value.bv_val = NULL;
937 controls[0] = &NoReferrals;
938 controls[1] = &PagedResults;
939 controls[2] = &ExternalCtrl;
940 controls[3] = NULL;
942 } else {
943 controls[0] = &NoReferrals;
944 controls[1] = &PagedResults;
945 controls[2] = NULL;
948 /* we need to disable referrals as the openldap libs don't
949 handle them and paged results at the same time. Using them
950 together results in the result record containing the server
951 page control being removed from the result list (tridge/jmcd)
953 leaving this in despite the control that says don't generate
954 referrals, in case the server doesn't support it (jmcd)
956 ldap_set_option(ads->ldap.ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
958 rc = ldap_search_with_timeout(ads->ldap.ld, utf8_path, scope, utf8_expr,
959 search_attrs, 0, controls,
960 NULL, LDAP_NO_LIMIT,
961 (LDAPMessage **)res);
963 ber_free(cookie_be, 1);
964 ber_bvfree(cookie_bv);
966 if (rc) {
967 DEBUG(3,("ads_do_paged_search_args: ldap_search_with_timeout(%s) -> %s\n", expr,
968 ldap_err2string(rc)));
969 goto done;
972 rc = ldap_parse_result(ads->ldap.ld, *res, NULL, NULL, NULL,
973 NULL, &rcontrols, 0);
975 if (!rcontrols) {
976 goto done;
979 for (i=0; rcontrols[i]; i++) {
980 if (strcmp(ADS_PAGE_CTL_OID, rcontrols[i]->ldctl_oid) == 0) {
981 cookie_be = ber_init(&rcontrols[i]->ldctl_value);
982 ber_scanf(cookie_be,"{iO}", (ber_int_t *) count,
983 &cookie_bv);
984 /* the berval is the cookie, but must be freed when
985 it is all done */
986 if (cookie_bv->bv_len) /* still more to do */
987 *cookie=ber_bvdup(cookie_bv);
988 else
989 *cookie=NULL;
990 ber_bvfree(cookie_bv);
991 ber_free(cookie_be, 1);
992 break;
995 ldap_controls_free(rcontrols);
997 done:
998 talloc_destroy(ctx);
1000 if (ext_be) {
1001 ber_free(ext_be, 1);
1004 if (ext_bv) {
1005 ber_bvfree(ext_bv);
1008 /* if/when we decide to utf8-encode attrs, take out this next line */
1009 TALLOC_FREE(search_attrs);
1011 return ADS_ERROR(rc);
1014 static ADS_STATUS ads_do_paged_search(ADS_STRUCT *ads, const char *bind_path,
1015 int scope, const char *expr,
1016 const char **attrs, LDAPMessage **res,
1017 int *count, struct berval **cookie)
1019 return ads_do_paged_search_args(ads, bind_path, scope, expr, attrs, NULL, res, count, cookie);
1024 * Get all results for a search. This uses ads_do_paged_search() to return
1025 * all entries in a large search.
1026 * @param ads connection to ads server
1027 * @param bind_path Base dn for the search
1028 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1029 * @param expr Search expression
1030 * @param attrs Attributes to retrieve
1031 * @param res ** which will contain results - free res* with ads_msgfree()
1032 * @return status of search
1034 ADS_STATUS ads_do_search_all_args(ADS_STRUCT *ads, const char *bind_path,
1035 int scope, const char *expr,
1036 const char **attrs, void *args,
1037 LDAPMessage **res)
1039 struct berval *cookie = NULL;
1040 int count = 0;
1041 ADS_STATUS status;
1043 *res = NULL;
1044 status = ads_do_paged_search_args(ads, bind_path, scope, expr, attrs, args, res,
1045 &count, &cookie);
1047 if (!ADS_ERR_OK(status))
1048 return status;
1050 #ifdef HAVE_LDAP_ADD_RESULT_ENTRY
1051 while (cookie) {
1052 LDAPMessage *res2 = NULL;
1053 ADS_STATUS status2;
1054 LDAPMessage *msg, *next;
1056 status2 = ads_do_paged_search_args(ads, bind_path, scope, expr,
1057 attrs, args, &res2, &count, &cookie);
1059 if (!ADS_ERR_OK(status2)) break;
1061 /* this relies on the way that ldap_add_result_entry() works internally. I hope
1062 that this works on all ldap libs, but I have only tested with openldap */
1063 for (msg = ads_first_message(ads, res2); msg; msg = next) {
1064 next = ads_next_message(ads, msg);
1065 ldap_add_result_entry((LDAPMessage **)res, msg);
1067 /* note that we do not free res2, as the memory is now
1068 part of the main returned list */
1070 #else
1071 DEBUG(0, ("no ldap_add_result_entry() support in LDAP libs!\n"));
1072 status = ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
1073 #endif
1075 return status;
1078 ADS_STATUS ads_do_search_all(ADS_STRUCT *ads, const char *bind_path,
1079 int scope, const char *expr,
1080 const char **attrs, LDAPMessage **res)
1082 return ads_do_search_all_args(ads, bind_path, scope, expr, attrs, NULL, res);
1085 ADS_STATUS ads_do_search_all_sd_flags(ADS_STRUCT *ads, const char *bind_path,
1086 int scope, const char *expr,
1087 const char **attrs, uint32 sd_flags,
1088 LDAPMessage **res)
1090 ads_control args;
1092 args.control = ADS_SD_FLAGS_OID;
1093 args.val = sd_flags;
1094 args.critical = True;
1096 return ads_do_search_all_args(ads, bind_path, scope, expr, attrs, &args, res);
1101 * Run a function on all results for a search. Uses ads_do_paged_search() and
1102 * runs the function as each page is returned, using ads_process_results()
1103 * @param ads connection to ads server
1104 * @param bind_path Base dn for the search
1105 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1106 * @param expr Search expression - specified in local charset
1107 * @param attrs Attributes to retrieve - specified in UTF-8 or ascii
1108 * @param fn Function which takes attr name, values list, and data_area
1109 * @param data_area Pointer which is passed to function on each call
1110 * @return status of search
1112 ADS_STATUS ads_do_search_all_fn(ADS_STRUCT *ads, const char *bind_path,
1113 int scope, const char *expr, const char **attrs,
1114 bool (*fn)(ADS_STRUCT *, char *, void **, void *),
1115 void *data_area)
1117 struct berval *cookie = NULL;
1118 int count = 0;
1119 ADS_STATUS status;
1120 LDAPMessage *res;
1122 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs, &res,
1123 &count, &cookie);
1125 if (!ADS_ERR_OK(status)) return status;
1127 ads_process_results(ads, res, fn, data_area);
1128 ads_msgfree(ads, res);
1130 while (cookie) {
1131 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs,
1132 &res, &count, &cookie);
1134 if (!ADS_ERR_OK(status)) break;
1136 ads_process_results(ads, res, fn, data_area);
1137 ads_msgfree(ads, res);
1140 return status;
1144 * Do a search with a timeout.
1145 * @param ads connection to ads server
1146 * @param bind_path Base dn for the search
1147 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1148 * @param expr Search expression
1149 * @param attrs Attributes to retrieve
1150 * @param res ** which will contain results - free res* with ads_msgfree()
1151 * @return status of search
1153 ADS_STATUS ads_do_search(ADS_STRUCT *ads, const char *bind_path, int scope,
1154 const char *expr,
1155 const char **attrs, LDAPMessage **res)
1157 int rc;
1158 char *utf8_expr, *utf8_path, **search_attrs = NULL;
1159 size_t converted_size;
1160 TALLOC_CTX *ctx;
1162 *res = NULL;
1163 if (!(ctx = talloc_init("ads_do_search"))) {
1164 DEBUG(1,("ads_do_search: talloc_init() failed!"));
1165 return ADS_ERROR(LDAP_NO_MEMORY);
1168 /* 0 means the conversion worked but the result was empty
1169 so we only fail if it's negative. In any case, it always
1170 at least nulls out the dest */
1171 if (!push_utf8_talloc(ctx, &utf8_expr, expr, &converted_size) ||
1172 !push_utf8_talloc(ctx, &utf8_path, bind_path, &converted_size))
1174 DEBUG(1,("ads_do_search: push_utf8_talloc() failed!"));
1175 rc = LDAP_NO_MEMORY;
1176 goto done;
1179 if (!attrs || !(*attrs))
1180 search_attrs = NULL;
1181 else {
1182 /* This would be the utf8-encoded version...*/
1183 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
1184 if (!(search_attrs = str_list_copy(talloc_tos(), attrs)))
1186 DEBUG(1,("ads_do_search: str_list_copy() failed!"));
1187 rc = LDAP_NO_MEMORY;
1188 goto done;
1192 /* see the note in ads_do_paged_search - we *must* disable referrals */
1193 ldap_set_option(ads->ldap.ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
1195 rc = ldap_search_with_timeout(ads->ldap.ld, utf8_path, scope, utf8_expr,
1196 search_attrs, 0, NULL, NULL,
1197 LDAP_NO_LIMIT,
1198 (LDAPMessage **)res);
1200 if (rc == LDAP_SIZELIMIT_EXCEEDED) {
1201 DEBUG(3,("Warning! sizelimit exceeded in ldap. Truncating.\n"));
1202 rc = 0;
1205 done:
1206 talloc_destroy(ctx);
1207 /* if/when we decide to utf8-encode attrs, take out this next line */
1208 TALLOC_FREE(search_attrs);
1209 return ADS_ERROR(rc);
1212 * Do a general ADS search
1213 * @param ads connection to ads server
1214 * @param res ** which will contain results - free res* with ads_msgfree()
1215 * @param expr Search expression
1216 * @param attrs Attributes to retrieve
1217 * @return status of search
1219 ADS_STATUS ads_search(ADS_STRUCT *ads, LDAPMessage **res,
1220 const char *expr, const char **attrs)
1222 return ads_do_search(ads, ads->config.bind_path, LDAP_SCOPE_SUBTREE,
1223 expr, attrs, res);
1227 * Do a search on a specific DistinguishedName
1228 * @param ads connection to ads server
1229 * @param res ** which will contain results - free res* with ads_msgfree()
1230 * @param dn DistinguishName to search
1231 * @param attrs Attributes to retrieve
1232 * @return status of search
1234 ADS_STATUS ads_search_dn(ADS_STRUCT *ads, LDAPMessage **res,
1235 const char *dn, const char **attrs)
1237 return ads_do_search(ads, dn, LDAP_SCOPE_BASE, "(objectclass=*)",
1238 attrs, res);
1242 * Free up memory from a ads_search
1243 * @param ads connection to ads server
1244 * @param msg Search results to free
1246 void ads_msgfree(ADS_STRUCT *ads, LDAPMessage *msg)
1248 if (!msg) return;
1249 ldap_msgfree(msg);
1253 * Free up memory from various ads requests
1254 * @param ads connection to ads server
1255 * @param mem Area to free
1257 void ads_memfree(ADS_STRUCT *ads, void *mem)
1259 SAFE_FREE(mem);
1263 * Get a dn from search results
1264 * @param ads connection to ads server
1265 * @param msg Search result
1266 * @return dn string
1268 char *ads_get_dn(ADS_STRUCT *ads, LDAPMessage *msg)
1270 char *utf8_dn, *unix_dn;
1271 size_t converted_size;
1273 utf8_dn = ldap_get_dn(ads->ldap.ld, msg);
1275 if (!utf8_dn) {
1276 DEBUG (5, ("ads_get_dn: ldap_get_dn failed\n"));
1277 return NULL;
1280 if (!pull_utf8_allocate(&unix_dn, utf8_dn, &converted_size)) {
1281 DEBUG(0,("ads_get_dn: string conversion failure utf8 [%s]\n",
1282 utf8_dn ));
1283 return NULL;
1285 ldap_memfree(utf8_dn);
1286 return unix_dn;
1290 * Get the parent from a dn
1291 * @param dn the dn to return the parent from
1292 * @return parent dn string
1294 char *ads_parent_dn(const char *dn)
1296 char *p;
1298 if (dn == NULL) {
1299 return NULL;
1302 p = strchr(dn, ',');
1304 if (p == NULL) {
1305 return NULL;
1308 return p+1;
1312 * Find a machine account given a hostname
1313 * @param ads connection to ads server
1314 * @param res ** which will contain results - free res* with ads_msgfree()
1315 * @param host Hostname to search for
1316 * @return status of search
1318 ADS_STATUS ads_find_machine_acct(ADS_STRUCT *ads, LDAPMessage **res,
1319 const char *machine)
1321 ADS_STATUS status;
1322 char *expr;
1323 const char *attrs[] = {"*", "nTSecurityDescriptor", NULL};
1325 *res = NULL;
1327 /* the easiest way to find a machine account anywhere in the tree
1328 is to look for hostname$ */
1329 if (asprintf(&expr, "(samAccountName=%s$)", machine) == -1) {
1330 DEBUG(1, ("asprintf failed!\n"));
1331 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1334 status = ads_search(ads, res, expr, attrs);
1335 SAFE_FREE(expr);
1336 return status;
1340 * Initialize a list of mods to be used in a modify request
1341 * @param ctx An initialized TALLOC_CTX
1342 * @return allocated ADS_MODLIST
1344 ADS_MODLIST ads_init_mods(TALLOC_CTX *ctx)
1346 #define ADS_MODLIST_ALLOC_SIZE 10
1347 LDAPMod **mods;
1349 if ((mods = TALLOC_ZERO_ARRAY(ctx, LDAPMod *, ADS_MODLIST_ALLOC_SIZE + 1)))
1350 /* -1 is safety to make sure we don't go over the end.
1351 need to reset it to NULL before doing ldap modify */
1352 mods[ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1354 return (ADS_MODLIST)mods;
1359 add an attribute to the list, with values list already constructed
1361 static ADS_STATUS ads_modlist_add(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1362 int mod_op, const char *name,
1363 const void *_invals)
1365 const void **invals = (const void **)_invals;
1366 int curmod;
1367 LDAPMod **modlist = (LDAPMod **) *mods;
1368 struct berval **ber_values = NULL;
1369 char **char_values = NULL;
1371 if (!invals) {
1372 mod_op = LDAP_MOD_DELETE;
1373 } else {
1374 if (mod_op & LDAP_MOD_BVALUES)
1375 ber_values = ads_dup_values(ctx,
1376 (const struct berval **)invals);
1377 else
1378 char_values = ads_push_strvals(ctx,
1379 (const char **) invals);
1382 /* find the first empty slot */
1383 for (curmod=0; modlist[curmod] && modlist[curmod] != (LDAPMod *) -1;
1384 curmod++);
1385 if (modlist[curmod] == (LDAPMod *) -1) {
1386 if (!(modlist = TALLOC_REALLOC_ARRAY(ctx, modlist, LDAPMod *,
1387 curmod+ADS_MODLIST_ALLOC_SIZE+1)))
1388 return ADS_ERROR(LDAP_NO_MEMORY);
1389 memset(&modlist[curmod], 0,
1390 ADS_MODLIST_ALLOC_SIZE*sizeof(LDAPMod *));
1391 modlist[curmod+ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1392 *mods = (ADS_MODLIST)modlist;
1395 if (!(modlist[curmod] = TALLOC_ZERO_P(ctx, LDAPMod)))
1396 return ADS_ERROR(LDAP_NO_MEMORY);
1397 modlist[curmod]->mod_type = talloc_strdup(ctx, name);
1398 if (mod_op & LDAP_MOD_BVALUES) {
1399 modlist[curmod]->mod_bvalues = ber_values;
1400 } else if (mod_op & LDAP_MOD_DELETE) {
1401 modlist[curmod]->mod_values = NULL;
1402 } else {
1403 modlist[curmod]->mod_values = char_values;
1406 modlist[curmod]->mod_op = mod_op;
1407 return ADS_ERROR(LDAP_SUCCESS);
1411 * Add a single string value to a mod list
1412 * @param ctx An initialized TALLOC_CTX
1413 * @param mods An initialized ADS_MODLIST
1414 * @param name The attribute name to add
1415 * @param val The value to add - NULL means DELETE
1416 * @return ADS STATUS indicating success of add
1418 ADS_STATUS ads_mod_str(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1419 const char *name, const char *val)
1421 const char *values[2];
1423 values[0] = val;
1424 values[1] = NULL;
1426 if (!val)
1427 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1428 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE, name, values);
1432 * Add an array of string values to a mod list
1433 * @param ctx An initialized TALLOC_CTX
1434 * @param mods An initialized ADS_MODLIST
1435 * @param name The attribute name to add
1436 * @param vals The array of string values to add - NULL means DELETE
1437 * @return ADS STATUS indicating success of add
1439 ADS_STATUS ads_mod_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1440 const char *name, const char **vals)
1442 if (!vals)
1443 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1444 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE,
1445 name, (const void **) vals);
1448 #if 0
1450 * Add a single ber-encoded value to a mod list
1451 * @param ctx An initialized TALLOC_CTX
1452 * @param mods An initialized ADS_MODLIST
1453 * @param name The attribute name to add
1454 * @param val The value to add - NULL means DELETE
1455 * @return ADS STATUS indicating success of add
1457 static ADS_STATUS ads_mod_ber(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1458 const char *name, const struct berval *val)
1460 const struct berval *values[2];
1462 values[0] = val;
1463 values[1] = NULL;
1464 if (!val)
1465 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1466 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE|LDAP_MOD_BVALUES,
1467 name, (const void **) values);
1469 #endif
1472 * Perform an ldap modify
1473 * @param ads connection to ads server
1474 * @param mod_dn DistinguishedName to modify
1475 * @param mods list of modifications to perform
1476 * @return status of modify
1478 ADS_STATUS ads_gen_mod(ADS_STRUCT *ads, const char *mod_dn, ADS_MODLIST mods)
1480 int ret,i;
1481 char *utf8_dn = NULL;
1482 size_t converted_size;
1484 this control is needed to modify that contains a currently
1485 non-existent attribute (but allowable for the object) to run
1487 LDAPControl PermitModify = {
1488 CONST_DISCARD(char *, ADS_PERMIT_MODIFY_OID),
1489 {0, NULL},
1490 (char) 1};
1491 LDAPControl *controls[2];
1493 controls[0] = &PermitModify;
1494 controls[1] = NULL;
1496 if (!push_utf8_allocate(&utf8_dn, mod_dn, &converted_size)) {
1497 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1500 /* find the end of the list, marked by NULL or -1 */
1501 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1502 /* make sure the end of the list is NULL */
1503 mods[i] = NULL;
1504 ret = ldap_modify_ext_s(ads->ldap.ld, utf8_dn,
1505 (LDAPMod **) mods, controls, NULL);
1506 SAFE_FREE(utf8_dn);
1507 return ADS_ERROR(ret);
1511 * Perform an ldap add
1512 * @param ads connection to ads server
1513 * @param new_dn DistinguishedName to add
1514 * @param mods list of attributes and values for DN
1515 * @return status of add
1517 ADS_STATUS ads_gen_add(ADS_STRUCT *ads, const char *new_dn, ADS_MODLIST mods)
1519 int ret, i;
1520 char *utf8_dn = NULL;
1521 size_t converted_size;
1523 if (!push_utf8_allocate(&utf8_dn, new_dn, &converted_size)) {
1524 DEBUG(1, ("ads_gen_add: push_utf8_allocate failed!"));
1525 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1528 /* find the end of the list, marked by NULL or -1 */
1529 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1530 /* make sure the end of the list is NULL */
1531 mods[i] = NULL;
1533 ret = ldap_add_s(ads->ldap.ld, utf8_dn, (LDAPMod**)mods);
1534 SAFE_FREE(utf8_dn);
1535 return ADS_ERROR(ret);
1539 * Delete a DistinguishedName
1540 * @param ads connection to ads server
1541 * @param new_dn DistinguishedName to delete
1542 * @return status of delete
1544 ADS_STATUS ads_del_dn(ADS_STRUCT *ads, char *del_dn)
1546 int ret;
1547 char *utf8_dn = NULL;
1548 size_t converted_size;
1549 if (!push_utf8_allocate(&utf8_dn, del_dn, &converted_size)) {
1550 DEBUG(1, ("ads_del_dn: push_utf8_allocate failed!"));
1551 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1554 ret = ldap_delete_s(ads->ldap.ld, utf8_dn);
1555 SAFE_FREE(utf8_dn);
1556 return ADS_ERROR(ret);
1560 * Build an org unit string
1561 * if org unit is Computers or blank then assume a container, otherwise
1562 * assume a / separated list of organisational units.
1563 * jmcd: '\' is now used for escapes so certain chars can be in the ou (e.g. #)
1564 * @param ads connection to ads server
1565 * @param org_unit Organizational unit
1566 * @return org unit string - caller must free
1568 char *ads_ou_string(ADS_STRUCT *ads, const char *org_unit)
1570 char *ret = NULL;
1572 if (!org_unit || !*org_unit) {
1574 ret = ads_default_ou_string(ads, WELL_KNOWN_GUID_COMPUTERS);
1576 /* samba4 might not yet respond to a wellknownobject-query */
1577 return ret ? ret : SMB_STRDUP("cn=Computers");
1580 if (strequal(org_unit, "Computers")) {
1581 return SMB_STRDUP("cn=Computers");
1584 /* jmcd: removed "\\" from the separation chars, because it is
1585 needed as an escape for chars like '#' which are valid in an
1586 OU name */
1587 return ads_build_path(org_unit, "/", "ou=", 1);
1591 * Get a org unit string for a well-known GUID
1592 * @param ads connection to ads server
1593 * @param wknguid Well known GUID
1594 * @return org unit string - caller must free
1596 char *ads_default_ou_string(ADS_STRUCT *ads, const char *wknguid)
1598 ADS_STATUS status;
1599 LDAPMessage *res = NULL;
1600 char *base, *wkn_dn = NULL, *ret = NULL, **wkn_dn_exp = NULL,
1601 **bind_dn_exp = NULL;
1602 const char *attrs[] = {"distinguishedName", NULL};
1603 int new_ln, wkn_ln, bind_ln, i;
1605 if (wknguid == NULL) {
1606 return NULL;
1609 if (asprintf(&base, "<WKGUID=%s,%s>", wknguid, ads->config.bind_path ) == -1) {
1610 DEBUG(1, ("asprintf failed!\n"));
1611 return NULL;
1614 status = ads_search_dn(ads, &res, base, attrs);
1615 if (!ADS_ERR_OK(status)) {
1616 DEBUG(1,("Failed while searching for: %s\n", base));
1617 goto out;
1620 if (ads_count_replies(ads, res) != 1) {
1621 goto out;
1624 /* substitute the bind-path from the well-known-guid-search result */
1625 wkn_dn = ads_get_dn(ads, res);
1626 if (!wkn_dn) {
1627 goto out;
1630 wkn_dn_exp = ldap_explode_dn(wkn_dn, 0);
1631 if (!wkn_dn_exp) {
1632 goto out;
1635 bind_dn_exp = ldap_explode_dn(ads->config.bind_path, 0);
1636 if (!bind_dn_exp) {
1637 goto out;
1640 for (wkn_ln=0; wkn_dn_exp[wkn_ln]; wkn_ln++)
1642 for (bind_ln=0; bind_dn_exp[bind_ln]; bind_ln++)
1645 new_ln = wkn_ln - bind_ln;
1647 ret = SMB_STRDUP(wkn_dn_exp[0]);
1648 if (!ret) {
1649 goto out;
1652 for (i=1; i < new_ln; i++) {
1653 char *s = NULL;
1655 if (asprintf(&s, "%s,%s", ret, wkn_dn_exp[i]) == -1) {
1656 SAFE_FREE(ret);
1657 goto out;
1660 SAFE_FREE(ret);
1661 ret = SMB_STRDUP(s);
1662 free(s);
1663 if (!ret) {
1664 goto out;
1668 out:
1669 SAFE_FREE(base);
1670 ads_msgfree(ads, res);
1671 ads_memfree(ads, wkn_dn);
1672 if (wkn_dn_exp) {
1673 ldap_value_free(wkn_dn_exp);
1675 if (bind_dn_exp) {
1676 ldap_value_free(bind_dn_exp);
1679 return ret;
1683 * Adds (appends) an item to an attribute array, rather then
1684 * replacing the whole list
1685 * @param ctx An initialized TALLOC_CTX
1686 * @param mods An initialized ADS_MODLIST
1687 * @param name name of the ldap attribute to append to
1688 * @param vals an array of values to add
1689 * @return status of addition
1692 ADS_STATUS ads_add_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1693 const char *name, const char **vals)
1695 return ads_modlist_add(ctx, mods, LDAP_MOD_ADD, name,
1696 (const void *) vals);
1700 * Determines the an account's current KVNO via an LDAP lookup
1701 * @param ads An initialized ADS_STRUCT
1702 * @param account_name the NT samaccountname.
1703 * @return the kvno for the account, or -1 in case of a failure.
1706 uint32 ads_get_kvno(ADS_STRUCT *ads, const char *account_name)
1708 LDAPMessage *res = NULL;
1709 uint32 kvno = (uint32)-1; /* -1 indicates a failure */
1710 char *filter;
1711 const char *attrs[] = {"msDS-KeyVersionNumber", NULL};
1712 char *dn_string = NULL;
1713 ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS);
1715 DEBUG(5,("ads_get_kvno: Searching for account %s\n", account_name));
1716 if (asprintf(&filter, "(samAccountName=%s)", account_name) == -1) {
1717 return kvno;
1719 ret = ads_search(ads, &res, filter, attrs);
1720 SAFE_FREE(filter);
1721 if (!ADS_ERR_OK(ret) || (ads_count_replies(ads, res) != 1)) {
1722 DEBUG(1,("ads_get_kvno: Account for %s not found.\n", account_name));
1723 ads_msgfree(ads, res);
1724 return kvno;
1727 dn_string = ads_get_dn(ads, res);
1728 if (!dn_string) {
1729 DEBUG(0,("ads_get_kvno: out of memory.\n"));
1730 ads_msgfree(ads, res);
1731 return kvno;
1733 DEBUG(5,("ads_get_kvno: Using: %s\n", dn_string));
1734 ads_memfree(ads, dn_string);
1736 /* ---------------------------------------------------------
1737 * 0 is returned as a default KVNO from this point on...
1738 * This is done because Windows 2000 does not support key
1739 * version numbers. Chances are that a failure in the next
1740 * step is simply due to Windows 2000 being used for a
1741 * domain controller. */
1742 kvno = 0;
1744 if (!ads_pull_uint32(ads, res, "msDS-KeyVersionNumber", &kvno)) {
1745 DEBUG(3,("ads_get_kvno: Error Determining KVNO!\n"));
1746 DEBUG(3,("ads_get_kvno: Windows 2000 does not support KVNO's, so this may be normal.\n"));
1747 ads_msgfree(ads, res);
1748 return kvno;
1751 /* Success */
1752 DEBUG(5,("ads_get_kvno: Looked Up KVNO of: %d\n", kvno));
1753 ads_msgfree(ads, res);
1754 return kvno;
1758 * Determines the computer account's current KVNO via an LDAP lookup
1759 * @param ads An initialized ADS_STRUCT
1760 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1761 * @return the kvno for the computer account, or -1 in case of a failure.
1764 uint32_t ads_get_machine_kvno(ADS_STRUCT *ads, const char *machine_name)
1766 char *computer_account = NULL;
1767 uint32_t kvno = -1;
1769 if (asprintf(&computer_account, "%s$", machine_name) < 0) {
1770 return kvno;
1773 kvno = ads_get_kvno(ads, computer_account);
1774 free(computer_account);
1776 return kvno;
1780 * This clears out all registered spn's for a given hostname
1781 * @param ads An initilaized ADS_STRUCT
1782 * @param machine_name the NetBIOS name of the computer.
1783 * @return 0 upon success, non-zero otherwise.
1786 ADS_STATUS ads_clear_service_principal_names(ADS_STRUCT *ads, const char *machine_name)
1788 TALLOC_CTX *ctx;
1789 LDAPMessage *res = NULL;
1790 ADS_MODLIST mods;
1791 const char *servicePrincipalName[1] = {NULL};
1792 ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS);
1793 char *dn_string = NULL;
1795 ret = ads_find_machine_acct(ads, &res, machine_name);
1796 if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
1797 DEBUG(5,("ads_clear_service_principal_names: WARNING: Host Account for %s not found... skipping operation.\n", machine_name));
1798 DEBUG(5,("ads_clear_service_principal_names: WARNING: Service Principals for %s have NOT been cleared.\n", machine_name));
1799 ads_msgfree(ads, res);
1800 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1803 DEBUG(5,("ads_clear_service_principal_names: Host account for %s found\n", machine_name));
1804 ctx = talloc_init("ads_clear_service_principal_names");
1805 if (!ctx) {
1806 ads_msgfree(ads, res);
1807 return ADS_ERROR(LDAP_NO_MEMORY);
1810 if (!(mods = ads_init_mods(ctx))) {
1811 talloc_destroy(ctx);
1812 ads_msgfree(ads, res);
1813 return ADS_ERROR(LDAP_NO_MEMORY);
1815 ret = ads_mod_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1816 if (!ADS_ERR_OK(ret)) {
1817 DEBUG(1,("ads_clear_service_principal_names: Error creating strlist.\n"));
1818 ads_msgfree(ads, res);
1819 talloc_destroy(ctx);
1820 return ret;
1822 dn_string = ads_get_dn(ads, res);
1823 if (!dn_string) {
1824 talloc_destroy(ctx);
1825 ads_msgfree(ads, res);
1826 return ADS_ERROR(LDAP_NO_MEMORY);
1828 ret = ads_gen_mod(ads, dn_string, mods);
1829 ads_memfree(ads,dn_string);
1830 if (!ADS_ERR_OK(ret)) {
1831 DEBUG(1,("ads_clear_service_principal_names: Error: Updating Service Principals for machine %s in LDAP\n",
1832 machine_name));
1833 ads_msgfree(ads, res);
1834 talloc_destroy(ctx);
1835 return ret;
1838 ads_msgfree(ads, res);
1839 talloc_destroy(ctx);
1840 return ret;
1844 * This adds a service principal name to an existing computer account
1845 * (found by hostname) in AD.
1846 * @param ads An initialized ADS_STRUCT
1847 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1848 * @param my_fqdn The fully qualified DNS name of the machine
1849 * @param spn A string of the service principal to add, i.e. 'host'
1850 * @return 0 upon sucess, or non-zero if a failure occurs
1853 ADS_STATUS ads_add_service_principal_name(ADS_STRUCT *ads, const char *machine_name,
1854 const char *my_fqdn, const char *spn)
1856 ADS_STATUS ret;
1857 TALLOC_CTX *ctx;
1858 LDAPMessage *res = NULL;
1859 char *psp1, *psp2;
1860 ADS_MODLIST mods;
1861 char *dn_string = NULL;
1862 const char *servicePrincipalName[3] = {NULL, NULL, NULL};
1864 ret = ads_find_machine_acct(ads, &res, machine_name);
1865 if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
1866 DEBUG(1,("ads_add_service_principal_name: WARNING: Host Account for %s not found... skipping operation.\n",
1867 machine_name));
1868 DEBUG(1,("ads_add_service_principal_name: WARNING: Service Principal '%s/%s@%s' has NOT been added.\n",
1869 spn, machine_name, ads->config.realm));
1870 ads_msgfree(ads, res);
1871 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1874 DEBUG(1,("ads_add_service_principal_name: Host account for %s found\n", machine_name));
1875 if (!(ctx = talloc_init("ads_add_service_principal_name"))) {
1876 ads_msgfree(ads, res);
1877 return ADS_ERROR(LDAP_NO_MEMORY);
1880 /* add short name spn */
1882 if ( (psp1 = talloc_asprintf(ctx, "%s/%s", spn, machine_name)) == NULL ) {
1883 talloc_destroy(ctx);
1884 ads_msgfree(ads, res);
1885 return ADS_ERROR(LDAP_NO_MEMORY);
1887 strupper_m(psp1);
1888 strlower_m(&psp1[strlen(spn)]);
1889 servicePrincipalName[0] = psp1;
1891 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n",
1892 psp1, machine_name));
1895 /* add fully qualified spn */
1897 if ( (psp2 = talloc_asprintf(ctx, "%s/%s", spn, my_fqdn)) == NULL ) {
1898 ret = ADS_ERROR(LDAP_NO_MEMORY);
1899 goto out;
1901 strupper_m(psp2);
1902 strlower_m(&psp2[strlen(spn)]);
1903 servicePrincipalName[1] = psp2;
1905 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n",
1906 psp2, machine_name));
1908 if ( (mods = ads_init_mods(ctx)) == NULL ) {
1909 ret = ADS_ERROR(LDAP_NO_MEMORY);
1910 goto out;
1913 ret = ads_add_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1914 if (!ADS_ERR_OK(ret)) {
1915 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
1916 goto out;
1919 if ( (dn_string = ads_get_dn(ads, res)) == NULL ) {
1920 ret = ADS_ERROR(LDAP_NO_MEMORY);
1921 goto out;
1924 ret = ads_gen_mod(ads, dn_string, mods);
1925 ads_memfree(ads,dn_string);
1926 if (!ADS_ERR_OK(ret)) {
1927 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
1928 goto out;
1931 out:
1932 TALLOC_FREE( ctx );
1933 ads_msgfree(ads, res);
1934 return ret;
1938 * adds a machine account to the ADS server
1939 * @param ads An intialized ADS_STRUCT
1940 * @param machine_name - the NetBIOS machine name of this account.
1941 * @param account_type A number indicating the type of account to create
1942 * @param org_unit The LDAP path in which to place this account
1943 * @return 0 upon success, or non-zero otherwise
1946 ADS_STATUS ads_create_machine_acct(ADS_STRUCT *ads, const char *machine_name,
1947 const char *org_unit)
1949 ADS_STATUS ret;
1950 char *samAccountName, *controlstr;
1951 TALLOC_CTX *ctx;
1952 ADS_MODLIST mods;
1953 char *machine_escaped = NULL;
1954 char *new_dn;
1955 const char *objectClass[] = {"top", "person", "organizationalPerson",
1956 "user", "computer", NULL};
1957 LDAPMessage *res = NULL;
1958 uint32 acct_control = ( UF_WORKSTATION_TRUST_ACCOUNT |\
1959 UF_DONT_EXPIRE_PASSWD |\
1960 UF_ACCOUNTDISABLE );
1962 if (!(ctx = talloc_init("ads_add_machine_acct")))
1963 return ADS_ERROR(LDAP_NO_MEMORY);
1965 ret = ADS_ERROR(LDAP_NO_MEMORY);
1967 machine_escaped = escape_rdn_val_string_alloc(machine_name);
1968 if (!machine_escaped) {
1969 goto done;
1972 new_dn = talloc_asprintf(ctx, "cn=%s,%s", machine_escaped, org_unit);
1973 samAccountName = talloc_asprintf(ctx, "%s$", machine_name);
1975 if ( !new_dn || !samAccountName ) {
1976 goto done;
1979 #ifndef ENCTYPE_ARCFOUR_HMAC
1980 acct_control |= UF_USE_DES_KEY_ONLY;
1981 #endif
1983 if (!(controlstr = talloc_asprintf(ctx, "%u", acct_control))) {
1984 goto done;
1987 if (!(mods = ads_init_mods(ctx))) {
1988 goto done;
1991 ads_mod_str(ctx, &mods, "cn", machine_name);
1992 ads_mod_str(ctx, &mods, "sAMAccountName", samAccountName);
1993 ads_mod_strlist(ctx, &mods, "objectClass", objectClass);
1994 ads_mod_str(ctx, &mods, "userAccountControl", controlstr);
1996 ret = ads_gen_add(ads, new_dn, mods);
1998 done:
1999 SAFE_FREE(machine_escaped);
2000 ads_msgfree(ads, res);
2001 talloc_destroy(ctx);
2003 return ret;
2007 * move a machine account to another OU on the ADS server
2008 * @param ads - An intialized ADS_STRUCT
2009 * @param machine_name - the NetBIOS machine name of this account.
2010 * @param org_unit - The LDAP path in which to place this account
2011 * @param moved - whether we moved the machine account (optional)
2012 * @return 0 upon success, or non-zero otherwise
2015 ADS_STATUS ads_move_machine_acct(ADS_STRUCT *ads, const char *machine_name,
2016 const char *org_unit, bool *moved)
2018 ADS_STATUS rc;
2019 int ldap_status;
2020 LDAPMessage *res = NULL;
2021 char *filter = NULL;
2022 char *computer_dn = NULL;
2023 char *parent_dn;
2024 char *computer_rdn = NULL;
2025 bool need_move = False;
2027 if (asprintf(&filter, "(samAccountName=%s$)", machine_name) == -1) {
2028 rc = ADS_ERROR(LDAP_NO_MEMORY);
2029 goto done;
2032 /* Find pre-existing machine */
2033 rc = ads_search(ads, &res, filter, NULL);
2034 if (!ADS_ERR_OK(rc)) {
2035 goto done;
2038 computer_dn = ads_get_dn(ads, res);
2039 if (!computer_dn) {
2040 rc = ADS_ERROR(LDAP_NO_MEMORY);
2041 goto done;
2044 parent_dn = ads_parent_dn(computer_dn);
2045 if (strequal(parent_dn, org_unit)) {
2046 goto done;
2049 need_move = True;
2051 if (asprintf(&computer_rdn, "CN=%s", machine_name) == -1) {
2052 rc = ADS_ERROR(LDAP_NO_MEMORY);
2053 goto done;
2056 ldap_status = ldap_rename_s(ads->ldap.ld, computer_dn, computer_rdn,
2057 org_unit, 1, NULL, NULL);
2058 rc = ADS_ERROR(ldap_status);
2060 done:
2061 ads_msgfree(ads, res);
2062 SAFE_FREE(filter);
2063 SAFE_FREE(computer_dn);
2064 SAFE_FREE(computer_rdn);
2066 if (!ADS_ERR_OK(rc)) {
2067 need_move = False;
2070 if (moved) {
2071 *moved = need_move;
2074 return rc;
2078 dump a binary result from ldap
2080 static void dump_binary(ADS_STRUCT *ads, const char *field, struct berval **values)
2082 int i, j;
2083 for (i=0; values[i]; i++) {
2084 printf("%s: ", field);
2085 for (j=0; j<values[i]->bv_len; j++) {
2086 printf("%02X", (unsigned char)values[i]->bv_val[j]);
2088 printf("\n");
2092 static void dump_guid(ADS_STRUCT *ads, const char *field, struct berval **values)
2094 int i;
2095 for (i=0; values[i]; i++) {
2097 UUID_FLAT guid;
2098 struct GUID tmp;
2100 memcpy(guid.info, values[i]->bv_val, sizeof(guid.info));
2101 smb_uuid_unpack(guid, &tmp);
2102 printf("%s: %s\n", field, GUID_string(talloc_tos(), &tmp));
2107 dump a sid result from ldap
2109 static void dump_sid(ADS_STRUCT *ads, const char *field, struct berval **values)
2111 int i;
2112 for (i=0; values[i]; i++) {
2113 DOM_SID sid;
2114 fstring tmp;
2115 sid_parse(values[i]->bv_val, values[i]->bv_len, &sid);
2116 printf("%s: %s\n", field, sid_to_fstring(tmp, &sid));
2121 dump ntSecurityDescriptor
2123 static void dump_sd(ADS_STRUCT *ads, const char *filed, struct berval **values)
2125 TALLOC_CTX *frame = talloc_stackframe();
2126 struct security_descriptor *psd;
2127 NTSTATUS status;
2129 status = unmarshall_sec_desc(talloc_tos(), (uint8 *)values[0]->bv_val,
2130 values[0]->bv_len, &psd);
2131 if (!NT_STATUS_IS_OK(status)) {
2132 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
2133 nt_errstr(status)));
2134 TALLOC_FREE(frame);
2135 return;
2138 if (psd) {
2139 ads_disp_sd(ads, talloc_tos(), psd);
2142 TALLOC_FREE(frame);
2146 dump a string result from ldap
2148 static void dump_string(const char *field, char **values)
2150 int i;
2151 for (i=0; values[i]; i++) {
2152 printf("%s: %s\n", field, values[i]);
2157 dump a field from LDAP on stdout
2158 used for debugging
2161 static bool ads_dump_field(ADS_STRUCT *ads, char *field, void **values, void *data_area)
2163 const struct {
2164 const char *name;
2165 bool string;
2166 void (*handler)(ADS_STRUCT *, const char *, struct berval **);
2167 } handlers[] = {
2168 {"objectGUID", False, dump_guid},
2169 {"netbootGUID", False, dump_guid},
2170 {"nTSecurityDescriptor", False, dump_sd},
2171 {"dnsRecord", False, dump_binary},
2172 {"objectSid", False, dump_sid},
2173 {"tokenGroups", False, dump_sid},
2174 {"tokenGroupsNoGCAcceptable", False, dump_sid},
2175 {"tokengroupsGlobalandUniversal", False, dump_sid},
2176 {"mS-DS-CreatorSID", False, dump_sid},
2177 {"msExchMailboxGuid", False, dump_guid},
2178 {NULL, True, NULL}
2180 int i;
2182 if (!field) { /* must be end of an entry */
2183 printf("\n");
2184 return False;
2187 for (i=0; handlers[i].name; i++) {
2188 if (StrCaseCmp(handlers[i].name, field) == 0) {
2189 if (!values) /* first time, indicate string or not */
2190 return handlers[i].string;
2191 handlers[i].handler(ads, field, (struct berval **) values);
2192 break;
2195 if (!handlers[i].name) {
2196 if (!values) /* first time, indicate string conversion */
2197 return True;
2198 dump_string(field, (char **)values);
2200 return False;
2204 * Dump a result from LDAP on stdout
2205 * used for debugging
2206 * @param ads connection to ads server
2207 * @param res Results to dump
2210 void ads_dump(ADS_STRUCT *ads, LDAPMessage *res)
2212 ads_process_results(ads, res, ads_dump_field, NULL);
2216 * Walk through results, calling a function for each entry found.
2217 * The function receives a field name, a berval * array of values,
2218 * and a data area passed through from the start. The function is
2219 * called once with null for field and values at the end of each
2220 * entry.
2221 * @param ads connection to ads server
2222 * @param res Results to process
2223 * @param fn Function for processing each result
2224 * @param data_area user-defined area to pass to function
2226 void ads_process_results(ADS_STRUCT *ads, LDAPMessage *res,
2227 bool (*fn)(ADS_STRUCT *, char *, void **, void *),
2228 void *data_area)
2230 LDAPMessage *msg;
2231 TALLOC_CTX *ctx;
2232 size_t converted_size;
2234 if (!(ctx = talloc_init("ads_process_results")))
2235 return;
2237 for (msg = ads_first_entry(ads, res); msg;
2238 msg = ads_next_entry(ads, msg)) {
2239 char *utf8_field;
2240 BerElement *b;
2242 for (utf8_field=ldap_first_attribute(ads->ldap.ld,
2243 (LDAPMessage *)msg,&b);
2244 utf8_field;
2245 utf8_field=ldap_next_attribute(ads->ldap.ld,
2246 (LDAPMessage *)msg,b)) {
2247 struct berval **ber_vals;
2248 char **str_vals, **utf8_vals;
2249 char *field;
2250 bool string;
2252 if (!pull_utf8_talloc(ctx, &field, utf8_field,
2253 &converted_size))
2255 DEBUG(0,("ads_process_results: "
2256 "pull_utf8_talloc failed: %s",
2257 strerror(errno)));
2260 string = fn(ads, field, NULL, data_area);
2262 if (string) {
2263 utf8_vals = ldap_get_values(ads->ldap.ld,
2264 (LDAPMessage *)msg, field);
2265 str_vals = ads_pull_strvals(ctx,
2266 (const char **) utf8_vals);
2267 fn(ads, field, (void **) str_vals, data_area);
2268 ldap_value_free(utf8_vals);
2269 } else {
2270 ber_vals = ldap_get_values_len(ads->ldap.ld,
2271 (LDAPMessage *)msg, field);
2272 fn(ads, field, (void **) ber_vals, data_area);
2274 ldap_value_free_len(ber_vals);
2276 ldap_memfree(utf8_field);
2278 ber_free(b, 0);
2279 talloc_free_children(ctx);
2280 fn(ads, NULL, NULL, data_area); /* completed an entry */
2283 talloc_destroy(ctx);
2287 * count how many replies are in a LDAPMessage
2288 * @param ads connection to ads server
2289 * @param res Results to count
2290 * @return number of replies
2292 int ads_count_replies(ADS_STRUCT *ads, void *res)
2294 return ldap_count_entries(ads->ldap.ld, (LDAPMessage *)res);
2298 * pull the first entry from a ADS result
2299 * @param ads connection to ads server
2300 * @param res Results of search
2301 * @return first entry from result
2303 LDAPMessage *ads_first_entry(ADS_STRUCT *ads, LDAPMessage *res)
2305 return ldap_first_entry(ads->ldap.ld, res);
2309 * pull the next entry from a ADS result
2310 * @param ads connection to ads server
2311 * @param res Results of search
2312 * @return next entry from result
2314 LDAPMessage *ads_next_entry(ADS_STRUCT *ads, LDAPMessage *res)
2316 return ldap_next_entry(ads->ldap.ld, res);
2320 * pull the first message from a ADS result
2321 * @param ads connection to ads server
2322 * @param res Results of search
2323 * @return first message from result
2325 LDAPMessage *ads_first_message(ADS_STRUCT *ads, LDAPMessage *res)
2327 return ldap_first_message(ads->ldap.ld, res);
2331 * pull the next message from a ADS result
2332 * @param ads connection to ads server
2333 * @param res Results of search
2334 * @return next message from result
2336 LDAPMessage *ads_next_message(ADS_STRUCT *ads, LDAPMessage *res)
2338 return ldap_next_message(ads->ldap.ld, res);
2342 * pull a single string from a ADS result
2343 * @param ads connection to ads server
2344 * @param mem_ctx TALLOC_CTX to use for allocating result string
2345 * @param msg Results of search
2346 * @param field Attribute to retrieve
2347 * @return Result string in talloc context
2349 char *ads_pull_string(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, LDAPMessage *msg,
2350 const char *field)
2352 char **values;
2353 char *ret = NULL;
2354 char *ux_string;
2355 size_t converted_size;
2357 values = ldap_get_values(ads->ldap.ld, msg, field);
2358 if (!values)
2359 return NULL;
2361 if (values[0] && pull_utf8_talloc(mem_ctx, &ux_string, values[0],
2362 &converted_size))
2364 ret = ux_string;
2366 ldap_value_free(values);
2367 return ret;
2371 * pull an array of strings from a ADS result
2372 * @param ads connection to ads server
2373 * @param mem_ctx TALLOC_CTX to use for allocating result string
2374 * @param msg Results of search
2375 * @param field Attribute to retrieve
2376 * @return Result strings in talloc context
2378 char **ads_pull_strings(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2379 LDAPMessage *msg, const char *field,
2380 size_t *num_values)
2382 char **values;
2383 char **ret = NULL;
2384 int i;
2385 size_t converted_size;
2387 values = ldap_get_values(ads->ldap.ld, msg, field);
2388 if (!values)
2389 return NULL;
2391 *num_values = ldap_count_values(values);
2393 ret = TALLOC_ARRAY(mem_ctx, char *, *num_values + 1);
2394 if (!ret) {
2395 ldap_value_free(values);
2396 return NULL;
2399 for (i=0;i<*num_values;i++) {
2400 if (!pull_utf8_talloc(mem_ctx, &ret[i], values[i],
2401 &converted_size))
2403 ldap_value_free(values);
2404 return NULL;
2407 ret[i] = NULL;
2409 ldap_value_free(values);
2410 return ret;
2414 * pull an array of strings from a ADS result
2415 * (handle large multivalue attributes with range retrieval)
2416 * @param ads connection to ads server
2417 * @param mem_ctx TALLOC_CTX to use for allocating result string
2418 * @param msg Results of search
2419 * @param field Attribute to retrieve
2420 * @param current_strings strings returned by a previous call to this function
2421 * @param next_attribute The next query should ask for this attribute
2422 * @param num_values How many values did we get this time?
2423 * @param more_values Are there more values to get?
2424 * @return Result strings in talloc context
2426 char **ads_pull_strings_range(ADS_STRUCT *ads,
2427 TALLOC_CTX *mem_ctx,
2428 LDAPMessage *msg, const char *field,
2429 char **current_strings,
2430 const char **next_attribute,
2431 size_t *num_strings,
2432 bool *more_strings)
2434 char *attr;
2435 char *expected_range_attrib, *range_attr;
2436 BerElement *ptr = NULL;
2437 char **strings;
2438 char **new_strings;
2439 size_t num_new_strings;
2440 unsigned long int range_start;
2441 unsigned long int range_end;
2443 /* we might have been given the whole lot anyway */
2444 if ((strings = ads_pull_strings(ads, mem_ctx, msg, field, num_strings))) {
2445 *more_strings = False;
2446 return strings;
2449 expected_range_attrib = talloc_asprintf(mem_ctx, "%s;Range=", field);
2451 /* look for Range result */
2452 for (attr = ldap_first_attribute(ads->ldap.ld, (LDAPMessage *)msg, &ptr);
2453 attr;
2454 attr = ldap_next_attribute(ads->ldap.ld, (LDAPMessage *)msg, ptr)) {
2455 /* we ignore the fact that this is utf8, as all attributes are ascii... */
2456 if (strnequal(attr, expected_range_attrib, strlen(expected_range_attrib))) {
2457 range_attr = attr;
2458 break;
2460 ldap_memfree(attr);
2462 if (!attr) {
2463 ber_free(ptr, 0);
2464 /* nothing here - this field is just empty */
2465 *more_strings = False;
2466 return NULL;
2469 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-%lu",
2470 &range_start, &range_end) == 2) {
2471 *more_strings = True;
2472 } else {
2473 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-*",
2474 &range_start) == 1) {
2475 *more_strings = False;
2476 } else {
2477 DEBUG(1, ("ads_pull_strings_range: Cannot parse Range attriubte (%s)\n",
2478 range_attr));
2479 ldap_memfree(range_attr);
2480 *more_strings = False;
2481 return NULL;
2485 if ((*num_strings) != range_start) {
2486 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) doesn't start at %u, but at %lu"
2487 " - aborting range retreival\n",
2488 range_attr, (unsigned int)(*num_strings) + 1, range_start));
2489 ldap_memfree(range_attr);
2490 *more_strings = False;
2491 return NULL;
2494 new_strings = ads_pull_strings(ads, mem_ctx, msg, range_attr, &num_new_strings);
2496 if (*more_strings && ((*num_strings + num_new_strings) != (range_end + 1))) {
2497 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) tells us we have %lu "
2498 "strings in this bunch, but we only got %lu - aborting range retreival\n",
2499 range_attr, (unsigned long int)range_end - range_start + 1,
2500 (unsigned long int)num_new_strings));
2501 ldap_memfree(range_attr);
2502 *more_strings = False;
2503 return NULL;
2506 strings = TALLOC_REALLOC_ARRAY(mem_ctx, current_strings, char *,
2507 *num_strings + num_new_strings);
2509 if (strings == NULL) {
2510 ldap_memfree(range_attr);
2511 *more_strings = False;
2512 return NULL;
2515 if (new_strings && num_new_strings) {
2516 memcpy(&strings[*num_strings], new_strings,
2517 sizeof(*new_strings) * num_new_strings);
2520 (*num_strings) += num_new_strings;
2522 if (*more_strings) {
2523 *next_attribute = talloc_asprintf(mem_ctx,
2524 "%s;range=%d-*",
2525 field,
2526 (int)*num_strings);
2528 if (!*next_attribute) {
2529 DEBUG(1, ("talloc_asprintf for next attribute failed!\n"));
2530 ldap_memfree(range_attr);
2531 *more_strings = False;
2532 return NULL;
2536 ldap_memfree(range_attr);
2538 return strings;
2542 * pull a single uint32 from a ADS result
2543 * @param ads connection to ads server
2544 * @param msg Results of search
2545 * @param field Attribute to retrieve
2546 * @param v Pointer to int to store result
2547 * @return boolean inidicating success
2549 bool ads_pull_uint32(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
2550 uint32 *v)
2552 char **values;
2554 values = ldap_get_values(ads->ldap.ld, msg, field);
2555 if (!values)
2556 return False;
2557 if (!values[0]) {
2558 ldap_value_free(values);
2559 return False;
2562 *v = atoi(values[0]);
2563 ldap_value_free(values);
2564 return True;
2568 * pull a single objectGUID from an ADS result
2569 * @param ads connection to ADS server
2570 * @param msg results of search
2571 * @param guid 37-byte area to receive text guid
2572 * @return boolean indicating success
2574 bool ads_pull_guid(ADS_STRUCT *ads, LDAPMessage *msg, struct GUID *guid)
2576 char **values;
2577 UUID_FLAT flat_guid;
2579 values = ldap_get_values(ads->ldap.ld, msg, "objectGUID");
2580 if (!values)
2581 return False;
2583 if (values[0]) {
2584 memcpy(&flat_guid.info, values[0], sizeof(UUID_FLAT));
2585 smb_uuid_unpack(flat_guid, guid);
2586 ldap_value_free(values);
2587 return True;
2589 ldap_value_free(values);
2590 return False;
2596 * pull a single DOM_SID from a ADS result
2597 * @param ads connection to ads server
2598 * @param msg Results of search
2599 * @param field Attribute to retrieve
2600 * @param sid Pointer to sid to store result
2601 * @return boolean inidicating success
2603 bool ads_pull_sid(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
2604 DOM_SID *sid)
2606 struct berval **values;
2607 bool ret = False;
2609 values = ldap_get_values_len(ads->ldap.ld, msg, field);
2611 if (!values)
2612 return False;
2614 if (values[0])
2615 ret = sid_parse(values[0]->bv_val, values[0]->bv_len, sid);
2617 ldap_value_free_len(values);
2618 return ret;
2622 * pull an array of DOM_SIDs from a ADS result
2623 * @param ads connection to ads server
2624 * @param mem_ctx TALLOC_CTX for allocating sid array
2625 * @param msg Results of search
2626 * @param field Attribute to retrieve
2627 * @param sids pointer to sid array to allocate
2628 * @return the count of SIDs pulled
2630 int ads_pull_sids(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2631 LDAPMessage *msg, const char *field, DOM_SID **sids)
2633 struct berval **values;
2634 bool ret;
2635 int count, i;
2637 values = ldap_get_values_len(ads->ldap.ld, msg, field);
2639 if (!values)
2640 return 0;
2642 for (i=0; values[i]; i++)
2643 /* nop */ ;
2645 if (i) {
2646 (*sids) = TALLOC_ARRAY(mem_ctx, DOM_SID, i);
2647 if (!(*sids)) {
2648 ldap_value_free_len(values);
2649 return 0;
2651 } else {
2652 (*sids) = NULL;
2655 count = 0;
2656 for (i=0; values[i]; i++) {
2657 ret = sid_parse(values[i]->bv_val, values[i]->bv_len, &(*sids)[count]);
2658 if (ret) {
2659 DEBUG(10, ("pulling SID: %s\n",
2660 sid_string_dbg(&(*sids)[count])));
2661 count++;
2665 ldap_value_free_len(values);
2666 return count;
2670 * pull a SEC_DESC from a ADS result
2671 * @param ads connection to ads server
2672 * @param mem_ctx TALLOC_CTX for allocating sid array
2673 * @param msg Results of search
2674 * @param field Attribute to retrieve
2675 * @param sd Pointer to *SEC_DESC to store result (talloc()ed)
2676 * @return boolean inidicating success
2678 bool ads_pull_sd(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2679 LDAPMessage *msg, const char *field, SEC_DESC **sd)
2681 struct berval **values;
2682 bool ret = true;
2684 values = ldap_get_values_len(ads->ldap.ld, msg, field);
2686 if (!values) return false;
2688 if (values[0]) {
2689 NTSTATUS status;
2690 status = unmarshall_sec_desc(mem_ctx,
2691 (uint8 *)values[0]->bv_val,
2692 values[0]->bv_len, sd);
2693 if (!NT_STATUS_IS_OK(status)) {
2694 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
2695 nt_errstr(status)));
2696 ret = false;
2700 ldap_value_free_len(values);
2701 return ret;
2705 * in order to support usernames longer than 21 characters we need to
2706 * use both the sAMAccountName and the userPrincipalName attributes
2707 * It seems that not all users have the userPrincipalName attribute set
2709 * @param ads connection to ads server
2710 * @param mem_ctx TALLOC_CTX for allocating sid array
2711 * @param msg Results of search
2712 * @return the username
2714 char *ads_pull_username(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2715 LDAPMessage *msg)
2717 #if 0 /* JERRY */
2718 char *ret, *p;
2720 /* lookup_name() only works on the sAMAccountName to
2721 returning the username portion of userPrincipalName
2722 breaks winbindd_getpwnam() */
2724 ret = ads_pull_string(ads, mem_ctx, msg, "userPrincipalName");
2725 if (ret && (p = strchr_m(ret, '@'))) {
2726 *p = 0;
2727 return ret;
2729 #endif
2730 return ads_pull_string(ads, mem_ctx, msg, "sAMAccountName");
2735 * find the update serial number - this is the core of the ldap cache
2736 * @param ads connection to ads server
2737 * @param ads connection to ADS server
2738 * @param usn Pointer to retrieved update serial number
2739 * @return status of search
2741 ADS_STATUS ads_USN(ADS_STRUCT *ads, uint32 *usn)
2743 const char *attrs[] = {"highestCommittedUSN", NULL};
2744 ADS_STATUS status;
2745 LDAPMessage *res;
2747 status = ads_do_search_retry(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2748 if (!ADS_ERR_OK(status))
2749 return status;
2751 if (ads_count_replies(ads, res) != 1) {
2752 ads_msgfree(ads, res);
2753 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2756 if (!ads_pull_uint32(ads, res, "highestCommittedUSN", usn)) {
2757 ads_msgfree(ads, res);
2758 return ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
2761 ads_msgfree(ads, res);
2762 return ADS_SUCCESS;
2765 /* parse a ADS timestring - typical string is
2766 '20020917091222.0Z0' which means 09:12.22 17th September
2767 2002, timezone 0 */
2768 static time_t ads_parse_time(const char *str)
2770 struct tm tm;
2772 ZERO_STRUCT(tm);
2774 if (sscanf(str, "%4d%2d%2d%2d%2d%2d",
2775 &tm.tm_year, &tm.tm_mon, &tm.tm_mday,
2776 &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) {
2777 return 0;
2779 tm.tm_year -= 1900;
2780 tm.tm_mon -= 1;
2782 return timegm(&tm);
2785 /********************************************************************
2786 ********************************************************************/
2788 ADS_STATUS ads_current_time(ADS_STRUCT *ads)
2790 const char *attrs[] = {"currentTime", NULL};
2791 ADS_STATUS status;
2792 LDAPMessage *res;
2793 char *timestr;
2794 TALLOC_CTX *ctx;
2795 ADS_STRUCT *ads_s = ads;
2797 if (!(ctx = talloc_init("ads_current_time"))) {
2798 return ADS_ERROR(LDAP_NO_MEMORY);
2801 /* establish a new ldap tcp session if necessary */
2803 if ( !ads->ldap.ld ) {
2804 if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup,
2805 ads->server.ldap_server )) == NULL )
2807 goto done;
2809 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
2810 status = ads_connect( ads_s );
2811 if ( !ADS_ERR_OK(status))
2812 goto done;
2815 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2816 if (!ADS_ERR_OK(status)) {
2817 goto done;
2820 timestr = ads_pull_string(ads_s, ctx, res, "currentTime");
2821 if (!timestr) {
2822 ads_msgfree(ads_s, res);
2823 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2824 goto done;
2827 /* but save the time and offset in the original ADS_STRUCT */
2829 ads->config.current_time = ads_parse_time(timestr);
2831 if (ads->config.current_time != 0) {
2832 ads->auth.time_offset = ads->config.current_time - time(NULL);
2833 DEBUG(4,("time offset is %d seconds\n", ads->auth.time_offset));
2836 ads_msgfree(ads, res);
2838 status = ADS_SUCCESS;
2840 done:
2841 /* free any temporary ads connections */
2842 if ( ads_s != ads ) {
2843 ads_destroy( &ads_s );
2845 talloc_destroy(ctx);
2847 return status;
2850 /********************************************************************
2851 ********************************************************************/
2853 ADS_STATUS ads_domain_func_level(ADS_STRUCT *ads, uint32 *val)
2855 const char *attrs[] = {"domainFunctionality", NULL};
2856 ADS_STATUS status;
2857 LDAPMessage *res;
2858 ADS_STRUCT *ads_s = ads;
2860 *val = DS_DOMAIN_FUNCTION_2000;
2862 /* establish a new ldap tcp session if necessary */
2864 if ( !ads->ldap.ld ) {
2865 if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup,
2866 ads->server.ldap_server )) == NULL )
2868 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
2869 goto done;
2871 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
2872 status = ads_connect( ads_s );
2873 if ( !ADS_ERR_OK(status))
2874 goto done;
2877 /* If the attribute does not exist assume it is a Windows 2000
2878 functional domain */
2880 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2881 if (!ADS_ERR_OK(status)) {
2882 if ( status.err.rc == LDAP_NO_SUCH_ATTRIBUTE ) {
2883 status = ADS_SUCCESS;
2885 goto done;
2888 if ( !ads_pull_uint32(ads_s, res, "domainFunctionality", val) ) {
2889 DEBUG(5,("ads_domain_func_level: Failed to pull the domainFunctionality attribute.\n"));
2891 DEBUG(3,("ads_domain_func_level: %d\n", *val));
2894 ads_msgfree(ads, res);
2896 done:
2897 /* free any temporary ads connections */
2898 if ( ads_s != ads ) {
2899 ads_destroy( &ads_s );
2902 return status;
2906 * find the domain sid for our domain
2907 * @param ads connection to ads server
2908 * @param sid Pointer to domain sid
2909 * @return status of search
2911 ADS_STATUS ads_domain_sid(ADS_STRUCT *ads, DOM_SID *sid)
2913 const char *attrs[] = {"objectSid", NULL};
2914 LDAPMessage *res;
2915 ADS_STATUS rc;
2917 rc = ads_do_search_retry(ads, ads->config.bind_path, LDAP_SCOPE_BASE, "(objectclass=*)",
2918 attrs, &res);
2919 if (!ADS_ERR_OK(rc)) return rc;
2920 if (!ads_pull_sid(ads, res, "objectSid", sid)) {
2921 ads_msgfree(ads, res);
2922 return ADS_ERROR_SYSTEM(ENOENT);
2924 ads_msgfree(ads, res);
2926 return ADS_SUCCESS;
2930 * find our site name
2931 * @param ads connection to ads server
2932 * @param mem_ctx Pointer to talloc context
2933 * @param site_name Pointer to the sitename
2934 * @return status of search
2936 ADS_STATUS ads_site_dn(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char **site_name)
2938 ADS_STATUS status;
2939 LDAPMessage *res;
2940 const char *dn, *service_name;
2941 const char *attrs[] = { "dsServiceName", NULL };
2943 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2944 if (!ADS_ERR_OK(status)) {
2945 return status;
2948 service_name = ads_pull_string(ads, mem_ctx, res, "dsServiceName");
2949 if (service_name == NULL) {
2950 ads_msgfree(ads, res);
2951 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2954 ads_msgfree(ads, res);
2956 /* go up three levels */
2957 dn = ads_parent_dn(ads_parent_dn(ads_parent_dn(service_name)));
2958 if (dn == NULL) {
2959 return ADS_ERROR(LDAP_NO_MEMORY);
2962 *site_name = talloc_strdup(mem_ctx, dn);
2963 if (*site_name == NULL) {
2964 return ADS_ERROR(LDAP_NO_MEMORY);
2967 return status;
2969 dsServiceName: CN=NTDS Settings,CN=W2K3DC,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,DC=ber,DC=suse,DC=de
2974 * find the site dn where a machine resides
2975 * @param ads connection to ads server
2976 * @param mem_ctx Pointer to talloc context
2977 * @param computer_name name of the machine
2978 * @param site_name Pointer to the sitename
2979 * @return status of search
2981 ADS_STATUS ads_site_dn_for_machine(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char *computer_name, const char **site_dn)
2983 ADS_STATUS status;
2984 LDAPMessage *res;
2985 const char *parent, *filter;
2986 char *config_context = NULL;
2987 char *dn;
2989 /* shortcut a query */
2990 if (strequal(computer_name, ads->config.ldap_server_name)) {
2991 return ads_site_dn(ads, mem_ctx, site_dn);
2994 status = ads_config_path(ads, mem_ctx, &config_context);
2995 if (!ADS_ERR_OK(status)) {
2996 return status;
2999 filter = talloc_asprintf(mem_ctx, "(cn=%s)", computer_name);
3000 if (filter == NULL) {
3001 return ADS_ERROR(LDAP_NO_MEMORY);
3004 status = ads_do_search(ads, config_context, LDAP_SCOPE_SUBTREE,
3005 filter, NULL, &res);
3006 if (!ADS_ERR_OK(status)) {
3007 return status;
3010 if (ads_count_replies(ads, res) != 1) {
3011 ads_msgfree(ads, res);
3012 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
3015 dn = ads_get_dn(ads, res);
3016 if (dn == NULL) {
3017 ads_msgfree(ads, res);
3018 return ADS_ERROR(LDAP_NO_MEMORY);
3021 /* go up three levels */
3022 parent = ads_parent_dn(ads_parent_dn(ads_parent_dn(dn)));
3023 if (parent == NULL) {
3024 ads_msgfree(ads, res);
3025 ads_memfree(ads, dn);
3026 return ADS_ERROR(LDAP_NO_MEMORY);
3029 *site_dn = talloc_strdup(mem_ctx, parent);
3030 if (*site_dn == NULL) {
3031 ads_msgfree(ads, res);
3032 ads_memfree(ads, dn);
3033 return ADS_ERROR(LDAP_NO_MEMORY);
3036 ads_memfree(ads, dn);
3037 ads_msgfree(ads, res);
3039 return status;
3043 * get the upn suffixes for a domain
3044 * @param ads connection to ads server
3045 * @param mem_ctx Pointer to talloc context
3046 * @param suffixes Pointer to an array of suffixes
3047 * @param num_suffixes Pointer to the number of suffixes
3048 * @return status of search
3050 ADS_STATUS ads_upn_suffixes(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, char ***suffixes, size_t *num_suffixes)
3052 ADS_STATUS status;
3053 LDAPMessage *res;
3054 const char *base;
3055 char *config_context = NULL;
3056 const char *attrs[] = { "uPNSuffixes", NULL };
3058 status = ads_config_path(ads, mem_ctx, &config_context);
3059 if (!ADS_ERR_OK(status)) {
3060 return status;
3063 base = talloc_asprintf(mem_ctx, "cn=Partitions,%s", config_context);
3064 if (base == NULL) {
3065 return ADS_ERROR(LDAP_NO_MEMORY);
3068 status = ads_search_dn(ads, &res, base, attrs);
3069 if (!ADS_ERR_OK(status)) {
3070 return status;
3073 if (ads_count_replies(ads, res) != 1) {
3074 ads_msgfree(ads, res);
3075 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
3078 (*suffixes) = ads_pull_strings(ads, mem_ctx, res, "uPNSuffixes", num_suffixes);
3079 if ((*suffixes) == NULL) {
3080 ads_msgfree(ads, res);
3081 return ADS_ERROR(LDAP_NO_MEMORY);
3084 ads_msgfree(ads, res);
3086 return status;
3090 * get the joinable ous for a domain
3091 * @param ads connection to ads server
3092 * @param mem_ctx Pointer to talloc context
3093 * @param ous Pointer to an array of ous
3094 * @param num_ous Pointer to the number of ous
3095 * @return status of search
3097 ADS_STATUS ads_get_joinable_ous(ADS_STRUCT *ads,
3098 TALLOC_CTX *mem_ctx,
3099 char ***ous,
3100 size_t *num_ous)
3102 ADS_STATUS status;
3103 LDAPMessage *res = NULL;
3104 LDAPMessage *msg = NULL;
3105 const char *attrs[] = { "dn", NULL };
3106 int count = 0;
3108 status = ads_search(ads, &res,
3109 "(|(objectClass=domain)(objectclass=organizationalUnit))",
3110 attrs);
3111 if (!ADS_ERR_OK(status)) {
3112 return status;
3115 count = ads_count_replies(ads, res);
3116 if (count < 1) {
3117 ads_msgfree(ads, res);
3118 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3121 for (msg = ads_first_entry(ads, res); msg;
3122 msg = ads_next_entry(ads, msg)) {
3124 char *dn = NULL;
3126 dn = ads_get_dn(ads, msg);
3127 if (!dn) {
3128 ads_msgfree(ads, res);
3129 return ADS_ERROR(LDAP_NO_MEMORY);
3132 if (!add_string_to_array(mem_ctx, dn,
3133 (const char ***)ous,
3134 (int *)num_ous)) {
3135 ads_memfree(ads, dn);
3136 ads_msgfree(ads, res);
3137 return ADS_ERROR(LDAP_NO_MEMORY);
3140 ads_memfree(ads, dn);
3143 ads_msgfree(ads, res);
3145 return status;
3150 * pull a DOM_SID from an extended dn string
3151 * @param mem_ctx TALLOC_CTX
3152 * @param extended_dn string
3153 * @param flags string type of extended_dn
3154 * @param sid pointer to a DOM_SID
3155 * @return NT_STATUS_OK on success,
3156 * NT_INVALID_PARAMETER on error,
3157 * NT_STATUS_NOT_FOUND if no SID present
3159 ADS_STATUS ads_get_sid_from_extended_dn(TALLOC_CTX *mem_ctx,
3160 const char *extended_dn,
3161 enum ads_extended_dn_flags flags,
3162 DOM_SID *sid)
3164 char *p, *q, *dn;
3166 if (!extended_dn) {
3167 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3170 /* otherwise extended_dn gets stripped off */
3171 if ((dn = talloc_strdup(mem_ctx, extended_dn)) == NULL) {
3172 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3175 * ADS_EXTENDED_DN_HEX_STRING:
3176 * <GUID=238e1963cb390f4bb032ba0105525a29>;<SID=010500000000000515000000bb68c8fd6b61b427572eb04556040000>;CN=gd,OU=berlin,OU=suse,DC=ber,DC=suse,DC=de
3178 * ADS_EXTENDED_DN_STRING (only with w2k3):
3179 * <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
3181 * Object with no SID, such as an Exchange Public Folder
3182 * <GUID=28907fb4bdf6854993e7f0a10b504e7c>;CN=public,CN=Microsoft Exchange System Objects,DC=sd2k3ms,DC=west,DC=isilon,DC=com
3185 p = strchr(dn, ';');
3186 if (!p) {
3187 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3190 if (strncmp(p, ";<SID=", strlen(";<SID=")) != 0) {
3191 DEBUG(5,("No SID present in extended dn\n"));
3192 return ADS_ERROR_NT(NT_STATUS_NOT_FOUND);
3195 p += strlen(";<SID=");
3197 q = strchr(p, '>');
3198 if (!q) {
3199 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3202 *q = '\0';
3204 DEBUG(100,("ads_get_sid_from_extended_dn: sid string is %s\n", p));
3206 switch (flags) {
3208 case ADS_EXTENDED_DN_STRING:
3209 if (!string_to_sid(sid, p)) {
3210 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3212 break;
3213 case ADS_EXTENDED_DN_HEX_STRING: {
3214 fstring buf;
3215 size_t buf_len;
3217 buf_len = strhex_to_str(buf, sizeof(buf), p, strlen(p));
3218 if (buf_len == 0) {
3219 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3222 if (!sid_parse(buf, buf_len, sid)) {
3223 DEBUG(10,("failed to parse sid\n"));
3224 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3226 break;
3228 default:
3229 DEBUG(10,("unknown extended dn format\n"));
3230 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3233 return ADS_ERROR_NT(NT_STATUS_OK);
3237 * pull an array of DOM_SIDs from a ADS result
3238 * @param ads connection to ads server
3239 * @param mem_ctx TALLOC_CTX for allocating sid array
3240 * @param msg Results of search
3241 * @param field Attribute to retrieve
3242 * @param flags string type of extended_dn
3243 * @param sids pointer to sid array to allocate
3244 * @return the count of SIDs pulled
3246 int ads_pull_sids_from_extendeddn(ADS_STRUCT *ads,
3247 TALLOC_CTX *mem_ctx,
3248 LDAPMessage *msg,
3249 const char *field,
3250 enum ads_extended_dn_flags flags,
3251 DOM_SID **sids)
3253 int i;
3254 ADS_STATUS rc;
3255 size_t dn_count, ret_count = 0;
3256 char **dn_strings;
3258 if ((dn_strings = ads_pull_strings(ads, mem_ctx, msg, field,
3259 &dn_count)) == NULL) {
3260 return 0;
3263 (*sids) = TALLOC_ZERO_ARRAY(mem_ctx, DOM_SID, dn_count + 1);
3264 if (!(*sids)) {
3265 TALLOC_FREE(dn_strings);
3266 return 0;
3269 for (i=0; i<dn_count; i++) {
3270 rc = ads_get_sid_from_extended_dn(mem_ctx, dn_strings[i],
3271 flags, &(*sids)[i]);
3272 if (!ADS_ERR_OK(rc)) {
3273 if (NT_STATUS_EQUAL(ads_ntstatus(rc),
3274 NT_STATUS_NOT_FOUND)) {
3275 continue;
3277 else {
3278 TALLOC_FREE(*sids);
3279 TALLOC_FREE(dn_strings);
3280 return 0;
3283 ret_count++;
3286 TALLOC_FREE(dn_strings);
3288 return ret_count;
3291 /********************************************************************
3292 ********************************************************************/
3294 char* ads_get_dnshostname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3296 LDAPMessage *res = NULL;
3297 ADS_STATUS status;
3298 int count = 0;
3299 char *name = NULL;
3301 status = ads_find_machine_acct(ads, &res, global_myname());
3302 if (!ADS_ERR_OK(status)) {
3303 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
3304 global_myname()));
3305 goto out;
3308 if ( (count = ads_count_replies(ads, res)) != 1 ) {
3309 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
3310 goto out;
3313 if ( (name = ads_pull_string(ads, ctx, res, "dNSHostName")) == NULL ) {
3314 DEBUG(0,("ads_get_dnshostname: No dNSHostName attribute!\n"));
3317 out:
3318 ads_msgfree(ads, res);
3320 return name;
3323 /********************************************************************
3324 ********************************************************************/
3326 char* ads_get_upn( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3328 LDAPMessage *res = NULL;
3329 ADS_STATUS status;
3330 int count = 0;
3331 char *name = NULL;
3333 status = ads_find_machine_acct(ads, &res, machine_name);
3334 if (!ADS_ERR_OK(status)) {
3335 DEBUG(0,("ads_get_upn: Failed to find account for %s\n",
3336 global_myname()));
3337 goto out;
3340 if ( (count = ads_count_replies(ads, res)) != 1 ) {
3341 DEBUG(1,("ads_get_upn: %d entries returned!\n", count));
3342 goto out;
3345 if ( (name = ads_pull_string(ads, ctx, res, "userPrincipalName")) == NULL ) {
3346 DEBUG(2,("ads_get_upn: No userPrincipalName attribute!\n"));
3349 out:
3350 ads_msgfree(ads, res);
3352 return name;
3355 /********************************************************************
3356 ********************************************************************/
3358 char* ads_get_samaccountname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3360 LDAPMessage *res = NULL;
3361 ADS_STATUS status;
3362 int count = 0;
3363 char *name = NULL;
3365 status = ads_find_machine_acct(ads, &res, global_myname());
3366 if (!ADS_ERR_OK(status)) {
3367 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
3368 global_myname()));
3369 goto out;
3372 if ( (count = ads_count_replies(ads, res)) != 1 ) {
3373 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
3374 goto out;
3377 if ( (name = ads_pull_string(ads, ctx, res, "sAMAccountName")) == NULL ) {
3378 DEBUG(0,("ads_get_dnshostname: No sAMAccountName attribute!\n"));
3381 out:
3382 ads_msgfree(ads, res);
3384 return name;
3387 #if 0
3389 SAVED CODE - we used to join via ldap - remember how we did this. JRA.
3392 * Join a machine to a realm
3393 * Creates the machine account and sets the machine password
3394 * @param ads connection to ads server
3395 * @param machine name of host to add
3396 * @param org_unit Organizational unit to place machine in
3397 * @return status of join
3399 ADS_STATUS ads_join_realm(ADS_STRUCT *ads, const char *machine_name,
3400 uint32 account_type, const char *org_unit)
3402 ADS_STATUS status;
3403 LDAPMessage *res = NULL;
3404 char *machine;
3406 /* machine name must be lowercase */
3407 machine = SMB_STRDUP(machine_name);
3408 strlower_m(machine);
3411 status = ads_find_machine_acct(ads, (void **)&res, machine);
3412 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
3413 DEBUG(0, ("Host account for %s already exists - deleting old account\n", machine));
3414 status = ads_leave_realm(ads, machine);
3415 if (!ADS_ERR_OK(status)) {
3416 DEBUG(0, ("Failed to delete host '%s' from the '%s' realm.\n",
3417 machine, ads->config.realm));
3418 return status;
3422 status = ads_add_machine_acct(ads, machine, account_type, org_unit);
3423 if (!ADS_ERR_OK(status)) {
3424 DEBUG(0, ("ads_join_realm: ads_add_machine_acct failed (%s): %s\n", machine, ads_errstr(status)));
3425 SAFE_FREE(machine);
3426 return status;
3429 status = ads_find_machine_acct(ads, (void **)(void *)&res, machine);
3430 if (!ADS_ERR_OK(status)) {
3431 DEBUG(0, ("ads_join_realm: Host account test failed for machine %s\n", machine));
3432 SAFE_FREE(machine);
3433 return status;
3436 SAFE_FREE(machine);
3437 ads_msgfree(ads, res);
3439 return status;
3441 #endif
3444 * Delete a machine from the realm
3445 * @param ads connection to ads server
3446 * @param hostname Machine to remove
3447 * @return status of delete
3449 ADS_STATUS ads_leave_realm(ADS_STRUCT *ads, const char *hostname)
3451 ADS_STATUS status;
3452 void *msg;
3453 LDAPMessage *res;
3454 char *hostnameDN, *host;
3455 int rc;
3456 LDAPControl ldap_control;
3457 LDAPControl * pldap_control[2] = {NULL, NULL};
3459 pldap_control[0] = &ldap_control;
3460 memset(&ldap_control, 0, sizeof(LDAPControl));
3461 ldap_control.ldctl_oid = (char *)LDAP_SERVER_TREE_DELETE_OID;
3463 /* hostname must be lowercase */
3464 host = SMB_STRDUP(hostname);
3465 strlower_m(host);
3467 status = ads_find_machine_acct(ads, &res, host);
3468 if (!ADS_ERR_OK(status)) {
3469 DEBUG(0, ("Host account for %s does not exist.\n", host));
3470 SAFE_FREE(host);
3471 return status;
3474 msg = ads_first_entry(ads, res);
3475 if (!msg) {
3476 SAFE_FREE(host);
3477 return ADS_ERROR_SYSTEM(ENOENT);
3480 hostnameDN = ads_get_dn(ads, (LDAPMessage *)msg);
3482 rc = ldap_delete_ext_s(ads->ldap.ld, hostnameDN, pldap_control, NULL);
3483 if (rc) {
3484 DEBUG(3,("ldap_delete_ext_s failed with error code %d\n", rc));
3485 }else {
3486 DEBUG(3,("ldap_delete_ext_s succeeded with error code %d\n", rc));
3489 if (rc != LDAP_SUCCESS) {
3490 const char *attrs[] = { "cn", NULL };
3491 LDAPMessage *msg_sub;
3493 /* we only search with scope ONE, we do not expect any further
3494 * objects to be created deeper */
3496 status = ads_do_search_retry(ads, hostnameDN,
3497 LDAP_SCOPE_ONELEVEL,
3498 "(objectclass=*)", attrs, &res);
3500 if (!ADS_ERR_OK(status)) {
3501 SAFE_FREE(host);
3502 ads_memfree(ads, hostnameDN);
3503 return status;
3506 for (msg_sub = ads_first_entry(ads, res); msg_sub;
3507 msg_sub = ads_next_entry(ads, msg_sub)) {
3509 char *dn = NULL;
3511 if ((dn = ads_get_dn(ads, msg_sub)) == NULL) {
3512 SAFE_FREE(host);
3513 ads_memfree(ads, hostnameDN);
3514 return ADS_ERROR(LDAP_NO_MEMORY);
3517 status = ads_del_dn(ads, dn);
3518 if (!ADS_ERR_OK(status)) {
3519 DEBUG(3,("failed to delete dn %s: %s\n", dn, ads_errstr(status)));
3520 SAFE_FREE(host);
3521 ads_memfree(ads, dn);
3522 ads_memfree(ads, hostnameDN);
3523 return status;
3526 ads_memfree(ads, dn);
3529 /* there should be no subordinate objects anymore */
3530 status = ads_do_search_retry(ads, hostnameDN,
3531 LDAP_SCOPE_ONELEVEL,
3532 "(objectclass=*)", attrs, &res);
3534 if (!ADS_ERR_OK(status) || ( (ads_count_replies(ads, res)) > 0 ) ) {
3535 SAFE_FREE(host);
3536 ads_memfree(ads, hostnameDN);
3537 return status;
3540 /* delete hostnameDN now */
3541 status = ads_del_dn(ads, hostnameDN);
3542 if (!ADS_ERR_OK(status)) {
3543 SAFE_FREE(host);
3544 DEBUG(3,("failed to delete dn %s: %s\n", hostnameDN, ads_errstr(status)));
3545 ads_memfree(ads, hostnameDN);
3546 return status;
3550 ads_memfree(ads, hostnameDN);
3552 status = ads_find_machine_acct(ads, &res, host);
3553 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
3554 DEBUG(3, ("Failed to remove host account.\n"));
3555 SAFE_FREE(host);
3556 return status;
3559 SAFE_FREE(host);
3560 return status;
3564 * pull all token-sids from an LDAP dn
3565 * @param ads connection to ads server
3566 * @param mem_ctx TALLOC_CTX for allocating sid array
3567 * @param dn of LDAP object
3568 * @param user_sid pointer to DOM_SID (objectSid)
3569 * @param primary_group_sid pointer to DOM_SID (self composed)
3570 * @param sids pointer to sid array to allocate
3571 * @param num_sids counter of SIDs pulled
3572 * @return status of token query
3574 ADS_STATUS ads_get_tokensids(ADS_STRUCT *ads,
3575 TALLOC_CTX *mem_ctx,
3576 const char *dn,
3577 DOM_SID *user_sid,
3578 DOM_SID *primary_group_sid,
3579 DOM_SID **sids,
3580 size_t *num_sids)
3582 ADS_STATUS status;
3583 LDAPMessage *res = NULL;
3584 int count = 0;
3585 size_t tmp_num_sids;
3586 DOM_SID *tmp_sids;
3587 DOM_SID tmp_user_sid;
3588 DOM_SID tmp_primary_group_sid;
3589 uint32 pgid;
3590 const char *attrs[] = {
3591 "objectSid",
3592 "tokenGroups",
3593 "primaryGroupID",
3594 NULL
3597 status = ads_search_retry_dn(ads, &res, dn, attrs);
3598 if (!ADS_ERR_OK(status)) {
3599 return status;
3602 count = ads_count_replies(ads, res);
3603 if (count != 1) {
3604 ads_msgfree(ads, res);
3605 return ADS_ERROR_LDAP(LDAP_NO_SUCH_OBJECT);
3608 if (!ads_pull_sid(ads, res, "objectSid", &tmp_user_sid)) {
3609 ads_msgfree(ads, res);
3610 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3613 if (!ads_pull_uint32(ads, res, "primaryGroupID", &pgid)) {
3614 ads_msgfree(ads, res);
3615 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3619 /* hack to compose the primary group sid without knowing the
3620 * domsid */
3622 DOM_SID domsid;
3623 uint32 dummy_rid;
3625 sid_copy(&domsid, &tmp_user_sid);
3627 if (!sid_split_rid(&domsid, &dummy_rid)) {
3628 ads_msgfree(ads, res);
3629 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3632 if (!sid_compose(&tmp_primary_group_sid, &domsid, pgid)) {
3633 ads_msgfree(ads, res);
3634 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3638 tmp_num_sids = ads_pull_sids(ads, mem_ctx, res, "tokenGroups", &tmp_sids);
3640 if (tmp_num_sids == 0 || !tmp_sids) {
3641 ads_msgfree(ads, res);
3642 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3645 if (num_sids) {
3646 *num_sids = tmp_num_sids;
3649 if (sids) {
3650 *sids = tmp_sids;
3653 if (user_sid) {
3654 *user_sid = tmp_user_sid;
3657 if (primary_group_sid) {
3658 *primary_group_sid = tmp_primary_group_sid;
3661 DEBUG(10,("ads_get_tokensids: returned %d sids\n", (int)tmp_num_sids + 2));
3663 ads_msgfree(ads, res);
3664 return ADS_ERROR_LDAP(LDAP_SUCCESS);
3668 * Find a sAMAccoutName in LDAP
3669 * @param ads connection to ads server
3670 * @param mem_ctx TALLOC_CTX for allocating sid array
3671 * @param samaccountname to search
3672 * @param uac_ret uint32 pointer userAccountControl attribute value
3673 * @param dn_ret pointer to dn
3674 * @return status of token query
3676 ADS_STATUS ads_find_samaccount(ADS_STRUCT *ads,
3677 TALLOC_CTX *mem_ctx,
3678 const char *samaccountname,
3679 uint32 *uac_ret,
3680 const char **dn_ret)
3682 ADS_STATUS status;
3683 const char *attrs[] = { "userAccountControl", NULL };
3684 const char *filter;
3685 LDAPMessage *res = NULL;
3686 char *dn = NULL;
3687 uint32 uac = 0;
3689 filter = talloc_asprintf(mem_ctx, "(&(objectclass=user)(sAMAccountName=%s))",
3690 samaccountname);
3691 if (filter == NULL) {
3692 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
3693 goto out;
3696 status = ads_do_search_all(ads, ads->config.bind_path,
3697 LDAP_SCOPE_SUBTREE,
3698 filter, attrs, &res);
3700 if (!ADS_ERR_OK(status)) {
3701 goto out;
3704 if (ads_count_replies(ads, res) != 1) {
3705 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3706 goto out;
3709 dn = ads_get_dn(ads, res);
3710 if (dn == NULL) {
3711 status = ADS_ERROR(LDAP_NO_MEMORY);
3712 goto out;
3715 if (!ads_pull_uint32(ads, res, "userAccountControl", &uac)) {
3716 status = ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
3717 goto out;
3720 if (uac_ret) {
3721 *uac_ret = uac;
3724 if (dn_ret) {
3725 *dn_ret = talloc_strdup(mem_ctx, dn);
3726 if (!*dn_ret) {
3727 status = ADS_ERROR(LDAP_NO_MEMORY);
3728 goto out;
3731 out:
3732 ads_memfree(ads, dn);
3733 ads_msgfree(ads, res);
3735 return status;
3739 * find our configuration path
3740 * @param ads connection to ads server
3741 * @param mem_ctx Pointer to talloc context
3742 * @param config_path Pointer to the config path
3743 * @return status of search
3745 ADS_STATUS ads_config_path(ADS_STRUCT *ads,
3746 TALLOC_CTX *mem_ctx,
3747 char **config_path)
3749 ADS_STATUS status;
3750 LDAPMessage *res = NULL;
3751 const char *config_context = NULL;
3752 const char *attrs[] = { "configurationNamingContext", NULL };
3754 status = ads_do_search(ads, "", LDAP_SCOPE_BASE,
3755 "(objectclass=*)", attrs, &res);
3756 if (!ADS_ERR_OK(status)) {
3757 return status;
3760 config_context = ads_pull_string(ads, mem_ctx, res,
3761 "configurationNamingContext");
3762 ads_msgfree(ads, res);
3763 if (!config_context) {
3764 return ADS_ERROR(LDAP_NO_MEMORY);
3767 if (config_path) {
3768 *config_path = talloc_strdup(mem_ctx, config_context);
3769 if (!*config_path) {
3770 return ADS_ERROR(LDAP_NO_MEMORY);
3774 return ADS_ERROR(LDAP_SUCCESS);
3778 * find the displayName of an extended right
3779 * @param ads connection to ads server
3780 * @param config_path The config path
3781 * @param mem_ctx Pointer to talloc context
3782 * @param GUID struct of the rightsGUID
3783 * @return status of search
3785 const char *ads_get_extended_right_name_by_guid(ADS_STRUCT *ads,
3786 const char *config_path,
3787 TALLOC_CTX *mem_ctx,
3788 const struct GUID *rights_guid)
3790 ADS_STATUS rc;
3791 LDAPMessage *res = NULL;
3792 char *expr = NULL;
3793 const char *attrs[] = { "displayName", NULL };
3794 const char *result = NULL;
3795 const char *path;
3797 if (!ads || !mem_ctx || !rights_guid) {
3798 goto done;
3801 expr = talloc_asprintf(mem_ctx, "(rightsGuid=%s)",
3802 GUID_string(mem_ctx, rights_guid));
3803 if (!expr) {
3804 goto done;
3807 path = talloc_asprintf(mem_ctx, "cn=Extended-Rights,%s", config_path);
3808 if (!path) {
3809 goto done;
3812 rc = ads_do_search_retry(ads, path, LDAP_SCOPE_SUBTREE,
3813 expr, attrs, &res);
3814 if (!ADS_ERR_OK(rc)) {
3815 goto done;
3818 if (ads_count_replies(ads, res) != 1) {
3819 goto done;
3822 result = ads_pull_string(ads, mem_ctx, res, "displayName");
3824 done:
3825 ads_msgfree(ads, res);
3826 return result;
3831 * verify or build and verify an account ou
3832 * @param mem_ctx Pointer to talloc context
3833 * @param ads connection to ads server
3834 * @param account_ou
3835 * @return status of search
3838 ADS_STATUS ads_check_ou_dn(TALLOC_CTX *mem_ctx,
3839 ADS_STRUCT *ads,
3840 const char **account_ou)
3842 struct ldb_dn *name_dn = NULL;
3843 const char *name = NULL;
3844 char *ou_string = NULL;
3846 name_dn = ldb_dn_explode(mem_ctx, *account_ou);
3847 if (name_dn) {
3848 return ADS_SUCCESS;
3851 ou_string = ads_ou_string(ads, *account_ou);
3852 if (!ou_string) {
3853 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX);
3856 name = talloc_asprintf(mem_ctx, "%s,%s", ou_string,
3857 ads->config.bind_path);
3858 SAFE_FREE(ou_string);
3859 if (!name) {
3860 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3863 name_dn = ldb_dn_explode(mem_ctx, name);
3864 if (!name_dn) {
3865 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX);
3868 *account_ou = talloc_strdup(mem_ctx, name);
3869 if (!*account_ou) {
3870 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3873 return ADS_SUCCESS;
3876 #endif