s3/libads: Change "ldap ssl:ads" parameter to "ldap ssl ads".
[Samba.git] / source3 / libads / ldap.c
blobedbd69392c7a2d0f42d1463c0a3490e036ab9709
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)
586 if (ads_try_connect(ads, ads->server.ldap_server, ads->server.gc)) {
587 goto got_connection;
590 /* The choice of which GC use is handled one level up in
591 ads_connect_gc(). If we continue on from here with
592 ads_find_dc() we will get GC searches on port 389 which
593 doesn't work. --jerry */
595 if (ads->server.gc == true) {
596 return ADS_ERROR(LDAP_OPERATIONS_ERROR);
600 ntstatus = ads_find_dc(ads);
601 if (NT_STATUS_IS_OK(ntstatus)) {
602 goto got_connection;
605 status = ADS_ERROR_NT(ntstatus);
606 goto out;
608 got_connection:
610 print_sockaddr(addr, sizeof(addr), &ads->ldap.ss);
611 DEBUG(3,("Successfully contacted LDAP server %s\n", addr));
613 if (!ads->auth.user_name) {
614 /* Must use the userPrincipalName value here or sAMAccountName
615 and not servicePrincipalName; found by Guenther Deschner */
617 if (asprintf(&ads->auth.user_name, "%s$", global_myname() ) == -1) {
618 DEBUG(0,("ads_connect: asprintf fail.\n"));
619 ads->auth.user_name = NULL;
623 if (!ads->auth.realm) {
624 ads->auth.realm = SMB_STRDUP(ads->config.realm);
627 if (!ads->auth.kdc_server) {
628 print_sockaddr(addr, sizeof(addr), &ads->ldap.ss);
629 ads->auth.kdc_server = SMB_STRDUP(addr);
632 #if KRB5_DNS_HACK
633 /* this is a really nasty hack to avoid ADS DNS problems. It needs a patch
634 to MIT kerberos to work (tridge) */
636 char *env = NULL;
637 if (asprintf(&env, "KRB5_KDC_ADDRESS_%s", ads->config.realm) > 0) {
638 setenv(env, ads->auth.kdc_server, 1);
639 free(env);
642 #endif
644 /* If the caller() requested no LDAP bind, then we are done */
646 if (ads->auth.flags & ADS_AUTH_NO_BIND) {
647 status = ADS_SUCCESS;
648 goto out;
651 ads->ldap.mem_ctx = talloc_init("ads LDAP connection memory");
652 if (!ads->ldap.mem_ctx) {
653 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
654 goto out;
657 /* Otherwise setup the TCP LDAP session */
659 ads->ldap.ld = ldap_open_with_timeout(ads->config.ldap_server_name,
660 ads->ldap.port, lp_ldap_timeout());
661 if (ads->ldap.ld == NULL) {
662 status = ADS_ERROR(LDAP_OPERATIONS_ERROR);
663 goto out;
665 DEBUG(3,("Connected to LDAP server %s\n", ads->config.ldap_server_name));
667 /* cache the successful connection for workgroup and realm */
668 if (ads_closest_dc(ads)) {
669 saf_store( ads->server.workgroup, ads->config.ldap_server_name);
670 saf_store( ads->server.realm, ads->config.ldap_server_name);
673 ldap_set_option(ads->ldap.ld, LDAP_OPT_PROTOCOL_VERSION, &version);
675 if ( lp_ldap_ssl_ads() ) {
676 status = ADS_ERROR(smb_ldap_start_tls(ads->ldap.ld, version));
677 if (!ADS_ERR_OK(status)) {
678 goto out;
682 /* fill in the current time and offsets */
684 status = ads_current_time( ads );
685 if ( !ADS_ERR_OK(status) ) {
686 goto out;
689 /* Now do the bind */
691 if (ads->auth.flags & ADS_AUTH_ANON_BIND) {
692 status = ADS_ERROR(ldap_simple_bind_s(ads->ldap.ld, NULL, NULL));
693 goto out;
696 if (ads->auth.flags & ADS_AUTH_SIMPLE_BIND) {
697 status = ADS_ERROR(ldap_simple_bind_s(ads->ldap.ld, ads->auth.user_name, ads->auth.password));
698 goto out;
701 status = ads_sasl_bind(ads);
703 out:
704 if (DEBUGLEVEL >= 11) {
705 char *s = NDR_PRINT_STRUCT_STRING(talloc_tos(), ads_struct, ads);
706 DEBUG(11,("ads_connect: leaving with: %s\n",
707 ads_errstr(status)));
708 DEBUGADD(11,("%s\n", s));
709 TALLOC_FREE(s);
712 return status;
716 * Connect to the LDAP server using given credentials
717 * @param ads Pointer to an existing ADS_STRUCT
718 * @return status of connection
720 ADS_STATUS ads_connect_user_creds(ADS_STRUCT *ads)
722 ads->auth.flags |= ADS_AUTH_USER_CREDS;
724 return ads_connect(ads);
728 * Disconnect the LDAP server
729 * @param ads Pointer to an existing ADS_STRUCT
731 void ads_disconnect(ADS_STRUCT *ads)
733 if (ads->ldap.ld) {
734 ldap_unbind(ads->ldap.ld);
735 ads->ldap.ld = NULL;
737 if (ads->ldap.wrap_ops && ads->ldap.wrap_ops->disconnect) {
738 ads->ldap.wrap_ops->disconnect(ads);
740 if (ads->ldap.mem_ctx) {
741 talloc_free(ads->ldap.mem_ctx);
743 ZERO_STRUCT(ads->ldap);
747 Duplicate a struct berval into talloc'ed memory
749 static struct berval *dup_berval(TALLOC_CTX *ctx, const struct berval *in_val)
751 struct berval *value;
753 if (!in_val) return NULL;
755 value = TALLOC_ZERO_P(ctx, struct berval);
756 if (value == NULL)
757 return NULL;
758 if (in_val->bv_len == 0) return value;
760 value->bv_len = in_val->bv_len;
761 value->bv_val = (char *)TALLOC_MEMDUP(ctx, in_val->bv_val,
762 in_val->bv_len);
763 return value;
767 Make a values list out of an array of (struct berval *)
769 static struct berval **ads_dup_values(TALLOC_CTX *ctx,
770 const struct berval **in_vals)
772 struct berval **values;
773 int i;
775 if (!in_vals) return NULL;
776 for (i=0; in_vals[i]; i++)
777 ; /* count values */
778 values = TALLOC_ZERO_ARRAY(ctx, struct berval *, i+1);
779 if (!values) return NULL;
781 for (i=0; in_vals[i]; i++) {
782 values[i] = dup_berval(ctx, in_vals[i]);
784 return values;
788 UTF8-encode a values list out of an array of (char *)
790 static char **ads_push_strvals(TALLOC_CTX *ctx, const char **in_vals)
792 char **values;
793 int i;
794 size_t size;
796 if (!in_vals) return NULL;
797 for (i=0; in_vals[i]; i++)
798 ; /* count values */
799 values = TALLOC_ZERO_ARRAY(ctx, char *, i+1);
800 if (!values) return NULL;
802 for (i=0; in_vals[i]; i++) {
803 if (!push_utf8_talloc(ctx, &values[i], in_vals[i], &size)) {
804 TALLOC_FREE(values);
805 return NULL;
808 return values;
812 Pull a (char *) array out of a UTF8-encoded values list
814 static char **ads_pull_strvals(TALLOC_CTX *ctx, const char **in_vals)
816 char **values;
817 int i;
818 size_t converted_size;
820 if (!in_vals) return NULL;
821 for (i=0; in_vals[i]; i++)
822 ; /* count values */
823 values = TALLOC_ZERO_ARRAY(ctx, char *, i+1);
824 if (!values) return NULL;
826 for (i=0; in_vals[i]; i++) {
827 if (!pull_utf8_talloc(ctx, &values[i], in_vals[i],
828 &converted_size)) {
829 DEBUG(0,("ads_pull_strvals: pull_utf8_talloc failed: "
830 "%s", strerror(errno)));
833 return values;
837 * Do a search with paged results. cookie must be null on the first
838 * call, and then returned on each subsequent call. It will be null
839 * again when the entire search is complete
840 * @param ads connection to ads server
841 * @param bind_path Base dn for the search
842 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
843 * @param expr Search expression - specified in local charset
844 * @param attrs Attributes to retrieve - specified in utf8 or ascii
845 * @param res ** which will contain results - free res* with ads_msgfree()
846 * @param count Number of entries retrieved on this page
847 * @param cookie The paged results cookie to be returned on subsequent calls
848 * @return status of search
850 static ADS_STATUS ads_do_paged_search_args(ADS_STRUCT *ads,
851 const char *bind_path,
852 int scope, const char *expr,
853 const char **attrs, void *args,
854 LDAPMessage **res,
855 int *count, struct berval **cookie)
857 int rc, i, version;
858 char *utf8_expr, *utf8_path, **search_attrs = NULL;
859 size_t converted_size;
860 LDAPControl PagedResults, NoReferrals, ExternalCtrl, *controls[4], **rcontrols;
861 BerElement *cookie_be = NULL;
862 struct berval *cookie_bv= NULL;
863 BerElement *ext_be = NULL;
864 struct berval *ext_bv= NULL;
866 TALLOC_CTX *ctx;
867 ads_control *external_control = (ads_control *) args;
869 *res = NULL;
871 if (!(ctx = talloc_init("ads_do_paged_search_args")))
872 return ADS_ERROR(LDAP_NO_MEMORY);
874 /* 0 means the conversion worked but the result was empty
875 so we only fail if it's -1. In any case, it always
876 at least nulls out the dest */
877 if (!push_utf8_talloc(ctx, &utf8_expr, expr, &converted_size) ||
878 !push_utf8_talloc(ctx, &utf8_path, bind_path, &converted_size))
880 rc = LDAP_NO_MEMORY;
881 goto done;
884 if (!attrs || !(*attrs))
885 search_attrs = NULL;
886 else {
887 /* This would be the utf8-encoded version...*/
888 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
889 if (!(search_attrs = str_list_copy(talloc_tos(), attrs))) {
890 rc = LDAP_NO_MEMORY;
891 goto done;
895 /* Paged results only available on ldap v3 or later */
896 ldap_get_option(ads->ldap.ld, LDAP_OPT_PROTOCOL_VERSION, &version);
897 if (version < LDAP_VERSION3) {
898 rc = LDAP_NOT_SUPPORTED;
899 goto done;
902 cookie_be = ber_alloc_t(LBER_USE_DER);
903 if (*cookie) {
904 ber_printf(cookie_be, "{iO}", (ber_int_t) 1000, *cookie);
905 ber_bvfree(*cookie); /* don't need it from last time */
906 *cookie = NULL;
907 } else {
908 ber_printf(cookie_be, "{io}", (ber_int_t) 1000, "", 0);
910 ber_flatten(cookie_be, &cookie_bv);
911 PagedResults.ldctl_oid = CONST_DISCARD(char *, ADS_PAGE_CTL_OID);
912 PagedResults.ldctl_iscritical = (char) 1;
913 PagedResults.ldctl_value.bv_len = cookie_bv->bv_len;
914 PagedResults.ldctl_value.bv_val = cookie_bv->bv_val;
916 NoReferrals.ldctl_oid = CONST_DISCARD(char *, ADS_NO_REFERRALS_OID);
917 NoReferrals.ldctl_iscritical = (char) 0;
918 NoReferrals.ldctl_value.bv_len = 0;
919 NoReferrals.ldctl_value.bv_val = CONST_DISCARD(char *, "");
921 if (external_control &&
922 (strequal(external_control->control, ADS_EXTENDED_DN_OID) ||
923 strequal(external_control->control, ADS_SD_FLAGS_OID))) {
925 ExternalCtrl.ldctl_oid = CONST_DISCARD(char *, external_control->control);
926 ExternalCtrl.ldctl_iscritical = (char) external_control->critical;
928 /* win2k does not accept a ldctl_value beeing passed in */
930 if (external_control->val != 0) {
932 if ((ext_be = ber_alloc_t(LBER_USE_DER)) == NULL ) {
933 rc = LDAP_NO_MEMORY;
934 goto done;
937 if ((ber_printf(ext_be, "{i}", (ber_int_t) external_control->val)) == -1) {
938 rc = LDAP_NO_MEMORY;
939 goto done;
941 if ((ber_flatten(ext_be, &ext_bv)) == -1) {
942 rc = LDAP_NO_MEMORY;
943 goto done;
946 ExternalCtrl.ldctl_value.bv_len = ext_bv->bv_len;
947 ExternalCtrl.ldctl_value.bv_val = ext_bv->bv_val;
949 } else {
950 ExternalCtrl.ldctl_value.bv_len = 0;
951 ExternalCtrl.ldctl_value.bv_val = NULL;
954 controls[0] = &NoReferrals;
955 controls[1] = &PagedResults;
956 controls[2] = &ExternalCtrl;
957 controls[3] = NULL;
959 } else {
960 controls[0] = &NoReferrals;
961 controls[1] = &PagedResults;
962 controls[2] = NULL;
965 /* we need to disable referrals as the openldap libs don't
966 handle them and paged results at the same time. Using them
967 together results in the result record containing the server
968 page control being removed from the result list (tridge/jmcd)
970 leaving this in despite the control that says don't generate
971 referrals, in case the server doesn't support it (jmcd)
973 ldap_set_option(ads->ldap.ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
975 rc = ldap_search_with_timeout(ads->ldap.ld, utf8_path, scope, utf8_expr,
976 search_attrs, 0, controls,
977 NULL, LDAP_NO_LIMIT,
978 (LDAPMessage **)res);
980 ber_free(cookie_be, 1);
981 ber_bvfree(cookie_bv);
983 if (rc) {
984 DEBUG(3,("ads_do_paged_search_args: ldap_search_with_timeout(%s) -> %s\n", expr,
985 ldap_err2string(rc)));
986 goto done;
989 rc = ldap_parse_result(ads->ldap.ld, *res, NULL, NULL, NULL,
990 NULL, &rcontrols, 0);
992 if (!rcontrols) {
993 goto done;
996 for (i=0; rcontrols[i]; i++) {
997 if (strcmp(ADS_PAGE_CTL_OID, rcontrols[i]->ldctl_oid) == 0) {
998 cookie_be = ber_init(&rcontrols[i]->ldctl_value);
999 ber_scanf(cookie_be,"{iO}", (ber_int_t *) count,
1000 &cookie_bv);
1001 /* the berval is the cookie, but must be freed when
1002 it is all done */
1003 if (cookie_bv->bv_len) /* still more to do */
1004 *cookie=ber_bvdup(cookie_bv);
1005 else
1006 *cookie=NULL;
1007 ber_bvfree(cookie_bv);
1008 ber_free(cookie_be, 1);
1009 break;
1012 ldap_controls_free(rcontrols);
1014 done:
1015 talloc_destroy(ctx);
1017 if (ext_be) {
1018 ber_free(ext_be, 1);
1021 if (ext_bv) {
1022 ber_bvfree(ext_bv);
1025 /* if/when we decide to utf8-encode attrs, take out this next line */
1026 TALLOC_FREE(search_attrs);
1028 return ADS_ERROR(rc);
1031 static ADS_STATUS ads_do_paged_search(ADS_STRUCT *ads, const char *bind_path,
1032 int scope, const char *expr,
1033 const char **attrs, LDAPMessage **res,
1034 int *count, struct berval **cookie)
1036 return ads_do_paged_search_args(ads, bind_path, scope, expr, attrs, NULL, res, count, cookie);
1041 * Get all results for a search. This uses ads_do_paged_search() to return
1042 * all entries in a large search.
1043 * @param ads connection to ads server
1044 * @param bind_path Base dn for the search
1045 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1046 * @param expr Search expression
1047 * @param attrs Attributes to retrieve
1048 * @param res ** which will contain results - free res* with ads_msgfree()
1049 * @return status of search
1051 ADS_STATUS ads_do_search_all_args(ADS_STRUCT *ads, const char *bind_path,
1052 int scope, const char *expr,
1053 const char **attrs, void *args,
1054 LDAPMessage **res)
1056 struct berval *cookie = NULL;
1057 int count = 0;
1058 ADS_STATUS status;
1060 *res = NULL;
1061 status = ads_do_paged_search_args(ads, bind_path, scope, expr, attrs, args, res,
1062 &count, &cookie);
1064 if (!ADS_ERR_OK(status))
1065 return status;
1067 #ifdef HAVE_LDAP_ADD_RESULT_ENTRY
1068 while (cookie) {
1069 LDAPMessage *res2 = NULL;
1070 ADS_STATUS status2;
1071 LDAPMessage *msg, *next;
1073 status2 = ads_do_paged_search_args(ads, bind_path, scope, expr,
1074 attrs, args, &res2, &count, &cookie);
1076 if (!ADS_ERR_OK(status2)) break;
1078 /* this relies on the way that ldap_add_result_entry() works internally. I hope
1079 that this works on all ldap libs, but I have only tested with openldap */
1080 for (msg = ads_first_message(ads, res2); msg; msg = next) {
1081 next = ads_next_message(ads, msg);
1082 ldap_add_result_entry((LDAPMessage **)res, msg);
1084 /* note that we do not free res2, as the memory is now
1085 part of the main returned list */
1087 #else
1088 DEBUG(0, ("no ldap_add_result_entry() support in LDAP libs!\n"));
1089 status = ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
1090 #endif
1092 return status;
1095 ADS_STATUS ads_do_search_all(ADS_STRUCT *ads, const char *bind_path,
1096 int scope, const char *expr,
1097 const char **attrs, LDAPMessage **res)
1099 return ads_do_search_all_args(ads, bind_path, scope, expr, attrs, NULL, res);
1102 ADS_STATUS ads_do_search_all_sd_flags(ADS_STRUCT *ads, const char *bind_path,
1103 int scope, const char *expr,
1104 const char **attrs, uint32 sd_flags,
1105 LDAPMessage **res)
1107 ads_control args;
1109 args.control = ADS_SD_FLAGS_OID;
1110 args.val = sd_flags;
1111 args.critical = True;
1113 return ads_do_search_all_args(ads, bind_path, scope, expr, attrs, &args, res);
1118 * Run a function on all results for a search. Uses ads_do_paged_search() and
1119 * runs the function as each page is returned, using ads_process_results()
1120 * @param ads connection to ads server
1121 * @param bind_path Base dn for the search
1122 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1123 * @param expr Search expression - specified in local charset
1124 * @param attrs Attributes to retrieve - specified in UTF-8 or ascii
1125 * @param fn Function which takes attr name, values list, and data_area
1126 * @param data_area Pointer which is passed to function on each call
1127 * @return status of search
1129 ADS_STATUS ads_do_search_all_fn(ADS_STRUCT *ads, const char *bind_path,
1130 int scope, const char *expr, const char **attrs,
1131 bool (*fn)(ADS_STRUCT *, char *, void **, void *),
1132 void *data_area)
1134 struct berval *cookie = NULL;
1135 int count = 0;
1136 ADS_STATUS status;
1137 LDAPMessage *res;
1139 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs, &res,
1140 &count, &cookie);
1142 if (!ADS_ERR_OK(status)) return status;
1144 ads_process_results(ads, res, fn, data_area);
1145 ads_msgfree(ads, res);
1147 while (cookie) {
1148 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs,
1149 &res, &count, &cookie);
1151 if (!ADS_ERR_OK(status)) break;
1153 ads_process_results(ads, res, fn, data_area);
1154 ads_msgfree(ads, res);
1157 return status;
1161 * Do a search with a timeout.
1162 * @param ads connection to ads server
1163 * @param bind_path Base dn for the search
1164 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1165 * @param expr Search expression
1166 * @param attrs Attributes to retrieve
1167 * @param res ** which will contain results - free res* with ads_msgfree()
1168 * @return status of search
1170 ADS_STATUS ads_do_search(ADS_STRUCT *ads, const char *bind_path, int scope,
1171 const char *expr,
1172 const char **attrs, LDAPMessage **res)
1174 int rc;
1175 char *utf8_expr, *utf8_path, **search_attrs = NULL;
1176 size_t converted_size;
1177 TALLOC_CTX *ctx;
1179 *res = NULL;
1180 if (!(ctx = talloc_init("ads_do_search"))) {
1181 DEBUG(1,("ads_do_search: talloc_init() failed!"));
1182 return ADS_ERROR(LDAP_NO_MEMORY);
1185 /* 0 means the conversion worked but the result was empty
1186 so we only fail if it's negative. In any case, it always
1187 at least nulls out the dest */
1188 if (!push_utf8_talloc(ctx, &utf8_expr, expr, &converted_size) ||
1189 !push_utf8_talloc(ctx, &utf8_path, bind_path, &converted_size))
1191 DEBUG(1,("ads_do_search: push_utf8_talloc() failed!"));
1192 rc = LDAP_NO_MEMORY;
1193 goto done;
1196 if (!attrs || !(*attrs))
1197 search_attrs = NULL;
1198 else {
1199 /* This would be the utf8-encoded version...*/
1200 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
1201 if (!(search_attrs = str_list_copy(talloc_tos(), attrs)))
1203 DEBUG(1,("ads_do_search: str_list_copy() failed!"));
1204 rc = LDAP_NO_MEMORY;
1205 goto done;
1209 /* see the note in ads_do_paged_search - we *must* disable referrals */
1210 ldap_set_option(ads->ldap.ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
1212 rc = ldap_search_with_timeout(ads->ldap.ld, utf8_path, scope, utf8_expr,
1213 search_attrs, 0, NULL, NULL,
1214 LDAP_NO_LIMIT,
1215 (LDAPMessage **)res);
1217 if (rc == LDAP_SIZELIMIT_EXCEEDED) {
1218 DEBUG(3,("Warning! sizelimit exceeded in ldap. Truncating.\n"));
1219 rc = 0;
1222 done:
1223 talloc_destroy(ctx);
1224 /* if/when we decide to utf8-encode attrs, take out this next line */
1225 TALLOC_FREE(search_attrs);
1226 return ADS_ERROR(rc);
1229 * Do a general ADS search
1230 * @param ads connection to ads server
1231 * @param res ** which will contain results - free res* with ads_msgfree()
1232 * @param expr Search expression
1233 * @param attrs Attributes to retrieve
1234 * @return status of search
1236 ADS_STATUS ads_search(ADS_STRUCT *ads, LDAPMessage **res,
1237 const char *expr, const char **attrs)
1239 return ads_do_search(ads, ads->config.bind_path, LDAP_SCOPE_SUBTREE,
1240 expr, attrs, res);
1244 * Do a search on a specific DistinguishedName
1245 * @param ads connection to ads server
1246 * @param res ** which will contain results - free res* with ads_msgfree()
1247 * @param dn DistinguishName to search
1248 * @param attrs Attributes to retrieve
1249 * @return status of search
1251 ADS_STATUS ads_search_dn(ADS_STRUCT *ads, LDAPMessage **res,
1252 const char *dn, const char **attrs)
1254 return ads_do_search(ads, dn, LDAP_SCOPE_BASE, "(objectclass=*)",
1255 attrs, res);
1259 * Free up memory from a ads_search
1260 * @param ads connection to ads server
1261 * @param msg Search results to free
1263 void ads_msgfree(ADS_STRUCT *ads, LDAPMessage *msg)
1265 if (!msg) return;
1266 ldap_msgfree(msg);
1270 * Free up memory from various ads requests
1271 * @param ads connection to ads server
1272 * @param mem Area to free
1274 void ads_memfree(ADS_STRUCT *ads, void *mem)
1276 SAFE_FREE(mem);
1280 * Get a dn from search results
1281 * @param ads connection to ads server
1282 * @param msg Search result
1283 * @return dn string
1285 char *ads_get_dn(ADS_STRUCT *ads, LDAPMessage *msg)
1287 char *utf8_dn, *unix_dn;
1288 size_t converted_size;
1290 utf8_dn = ldap_get_dn(ads->ldap.ld, msg);
1292 if (!utf8_dn) {
1293 DEBUG (5, ("ads_get_dn: ldap_get_dn failed\n"));
1294 return NULL;
1297 if (!pull_utf8_allocate(&unix_dn, utf8_dn, &converted_size)) {
1298 DEBUG(0,("ads_get_dn: string conversion failure utf8 [%s]\n",
1299 utf8_dn ));
1300 return NULL;
1302 ldap_memfree(utf8_dn);
1303 return unix_dn;
1307 * Get the parent from a dn
1308 * @param dn the dn to return the parent from
1309 * @return parent dn string
1311 char *ads_parent_dn(const char *dn)
1313 char *p;
1315 if (dn == NULL) {
1316 return NULL;
1319 p = strchr(dn, ',');
1321 if (p == NULL) {
1322 return NULL;
1325 return p+1;
1329 * Find a machine account given a hostname
1330 * @param ads connection to ads server
1331 * @param res ** which will contain results - free res* with ads_msgfree()
1332 * @param host Hostname to search for
1333 * @return status of search
1335 ADS_STATUS ads_find_machine_acct(ADS_STRUCT *ads, LDAPMessage **res,
1336 const char *machine)
1338 ADS_STATUS status;
1339 char *expr;
1340 const char *attrs[] = {"*", "nTSecurityDescriptor", NULL};
1342 *res = NULL;
1344 /* the easiest way to find a machine account anywhere in the tree
1345 is to look for hostname$ */
1346 if (asprintf(&expr, "(samAccountName=%s$)", machine) == -1) {
1347 DEBUG(1, ("asprintf failed!\n"));
1348 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1351 status = ads_search(ads, res, expr, attrs);
1352 SAFE_FREE(expr);
1353 return status;
1357 * Initialize a list of mods to be used in a modify request
1358 * @param ctx An initialized TALLOC_CTX
1359 * @return allocated ADS_MODLIST
1361 ADS_MODLIST ads_init_mods(TALLOC_CTX *ctx)
1363 #define ADS_MODLIST_ALLOC_SIZE 10
1364 LDAPMod **mods;
1366 if ((mods = TALLOC_ZERO_ARRAY(ctx, LDAPMod *, ADS_MODLIST_ALLOC_SIZE + 1)))
1367 /* -1 is safety to make sure we don't go over the end.
1368 need to reset it to NULL before doing ldap modify */
1369 mods[ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1371 return (ADS_MODLIST)mods;
1376 add an attribute to the list, with values list already constructed
1378 static ADS_STATUS ads_modlist_add(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1379 int mod_op, const char *name,
1380 const void *_invals)
1382 const void **invals = (const void **)_invals;
1383 int curmod;
1384 LDAPMod **modlist = (LDAPMod **) *mods;
1385 struct berval **ber_values = NULL;
1386 char **char_values = NULL;
1388 if (!invals) {
1389 mod_op = LDAP_MOD_DELETE;
1390 } else {
1391 if (mod_op & LDAP_MOD_BVALUES)
1392 ber_values = ads_dup_values(ctx,
1393 (const struct berval **)invals);
1394 else
1395 char_values = ads_push_strvals(ctx,
1396 (const char **) invals);
1399 /* find the first empty slot */
1400 for (curmod=0; modlist[curmod] && modlist[curmod] != (LDAPMod *) -1;
1401 curmod++);
1402 if (modlist[curmod] == (LDAPMod *) -1) {
1403 if (!(modlist = TALLOC_REALLOC_ARRAY(ctx, modlist, LDAPMod *,
1404 curmod+ADS_MODLIST_ALLOC_SIZE+1)))
1405 return ADS_ERROR(LDAP_NO_MEMORY);
1406 memset(&modlist[curmod], 0,
1407 ADS_MODLIST_ALLOC_SIZE*sizeof(LDAPMod *));
1408 modlist[curmod+ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1409 *mods = (ADS_MODLIST)modlist;
1412 if (!(modlist[curmod] = TALLOC_ZERO_P(ctx, LDAPMod)))
1413 return ADS_ERROR(LDAP_NO_MEMORY);
1414 modlist[curmod]->mod_type = talloc_strdup(ctx, name);
1415 if (mod_op & LDAP_MOD_BVALUES) {
1416 modlist[curmod]->mod_bvalues = ber_values;
1417 } else if (mod_op & LDAP_MOD_DELETE) {
1418 modlist[curmod]->mod_values = NULL;
1419 } else {
1420 modlist[curmod]->mod_values = char_values;
1423 modlist[curmod]->mod_op = mod_op;
1424 return ADS_ERROR(LDAP_SUCCESS);
1428 * Add a single string value to a mod list
1429 * @param ctx An initialized TALLOC_CTX
1430 * @param mods An initialized ADS_MODLIST
1431 * @param name The attribute name to add
1432 * @param val The value to add - NULL means DELETE
1433 * @return ADS STATUS indicating success of add
1435 ADS_STATUS ads_mod_str(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1436 const char *name, const char *val)
1438 const char *values[2];
1440 values[0] = val;
1441 values[1] = NULL;
1443 if (!val)
1444 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1445 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE, name, values);
1449 * Add an array of string values to a mod list
1450 * @param ctx An initialized TALLOC_CTX
1451 * @param mods An initialized ADS_MODLIST
1452 * @param name The attribute name to add
1453 * @param vals The array of string values to add - NULL means DELETE
1454 * @return ADS STATUS indicating success of add
1456 ADS_STATUS ads_mod_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1457 const char *name, const char **vals)
1459 if (!vals)
1460 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1461 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE,
1462 name, (const void **) vals);
1465 #if 0
1467 * Add a single ber-encoded value to a mod list
1468 * @param ctx An initialized TALLOC_CTX
1469 * @param mods An initialized ADS_MODLIST
1470 * @param name The attribute name to add
1471 * @param val The value to add - NULL means DELETE
1472 * @return ADS STATUS indicating success of add
1474 static ADS_STATUS ads_mod_ber(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1475 const char *name, const struct berval *val)
1477 const struct berval *values[2];
1479 values[0] = val;
1480 values[1] = NULL;
1481 if (!val)
1482 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1483 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE|LDAP_MOD_BVALUES,
1484 name, (const void **) values);
1486 #endif
1489 * Perform an ldap modify
1490 * @param ads connection to ads server
1491 * @param mod_dn DistinguishedName to modify
1492 * @param mods list of modifications to perform
1493 * @return status of modify
1495 ADS_STATUS ads_gen_mod(ADS_STRUCT *ads, const char *mod_dn, ADS_MODLIST mods)
1497 int ret,i;
1498 char *utf8_dn = NULL;
1499 size_t converted_size;
1501 this control is needed to modify that contains a currently
1502 non-existent attribute (but allowable for the object) to run
1504 LDAPControl PermitModify = {
1505 CONST_DISCARD(char *, ADS_PERMIT_MODIFY_OID),
1506 {0, NULL},
1507 (char) 1};
1508 LDAPControl *controls[2];
1510 controls[0] = &PermitModify;
1511 controls[1] = NULL;
1513 if (!push_utf8_allocate(&utf8_dn, mod_dn, &converted_size)) {
1514 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1517 /* find the end of the list, marked by NULL or -1 */
1518 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1519 /* make sure the end of the list is NULL */
1520 mods[i] = NULL;
1521 ret = ldap_modify_ext_s(ads->ldap.ld, utf8_dn,
1522 (LDAPMod **) mods, controls, NULL);
1523 SAFE_FREE(utf8_dn);
1524 return ADS_ERROR(ret);
1528 * Perform an ldap add
1529 * @param ads connection to ads server
1530 * @param new_dn DistinguishedName to add
1531 * @param mods list of attributes and values for DN
1532 * @return status of add
1534 ADS_STATUS ads_gen_add(ADS_STRUCT *ads, const char *new_dn, ADS_MODLIST mods)
1536 int ret, i;
1537 char *utf8_dn = NULL;
1538 size_t converted_size;
1540 if (!push_utf8_allocate(&utf8_dn, new_dn, &converted_size)) {
1541 DEBUG(1, ("ads_gen_add: push_utf8_allocate failed!"));
1542 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1545 /* find the end of the list, marked by NULL or -1 */
1546 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1547 /* make sure the end of the list is NULL */
1548 mods[i] = NULL;
1550 ret = ldap_add_s(ads->ldap.ld, utf8_dn, (LDAPMod**)mods);
1551 SAFE_FREE(utf8_dn);
1552 return ADS_ERROR(ret);
1556 * Delete a DistinguishedName
1557 * @param ads connection to ads server
1558 * @param new_dn DistinguishedName to delete
1559 * @return status of delete
1561 ADS_STATUS ads_del_dn(ADS_STRUCT *ads, char *del_dn)
1563 int ret;
1564 char *utf8_dn = NULL;
1565 size_t converted_size;
1566 if (!push_utf8_allocate(&utf8_dn, del_dn, &converted_size)) {
1567 DEBUG(1, ("ads_del_dn: push_utf8_allocate failed!"));
1568 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1571 ret = ldap_delete_s(ads->ldap.ld, utf8_dn);
1572 SAFE_FREE(utf8_dn);
1573 return ADS_ERROR(ret);
1577 * Build an org unit string
1578 * if org unit is Computers or blank then assume a container, otherwise
1579 * assume a / separated list of organisational units.
1580 * jmcd: '\' is now used for escapes so certain chars can be in the ou (e.g. #)
1581 * @param ads connection to ads server
1582 * @param org_unit Organizational unit
1583 * @return org unit string - caller must free
1585 char *ads_ou_string(ADS_STRUCT *ads, const char *org_unit)
1587 char *ret = NULL;
1589 if (!org_unit || !*org_unit) {
1591 ret = ads_default_ou_string(ads, WELL_KNOWN_GUID_COMPUTERS);
1593 /* samba4 might not yet respond to a wellknownobject-query */
1594 return ret ? ret : SMB_STRDUP("cn=Computers");
1597 if (strequal(org_unit, "Computers")) {
1598 return SMB_STRDUP("cn=Computers");
1601 /* jmcd: removed "\\" from the separation chars, because it is
1602 needed as an escape for chars like '#' which are valid in an
1603 OU name */
1604 return ads_build_path(org_unit, "/", "ou=", 1);
1608 * Get a org unit string for a well-known GUID
1609 * @param ads connection to ads server
1610 * @param wknguid Well known GUID
1611 * @return org unit string - caller must free
1613 char *ads_default_ou_string(ADS_STRUCT *ads, const char *wknguid)
1615 ADS_STATUS status;
1616 LDAPMessage *res = NULL;
1617 char *base, *wkn_dn = NULL, *ret = NULL, **wkn_dn_exp = NULL,
1618 **bind_dn_exp = NULL;
1619 const char *attrs[] = {"distinguishedName", NULL};
1620 int new_ln, wkn_ln, bind_ln, i;
1622 if (wknguid == NULL) {
1623 return NULL;
1626 if (asprintf(&base, "<WKGUID=%s,%s>", wknguid, ads->config.bind_path ) == -1) {
1627 DEBUG(1, ("asprintf failed!\n"));
1628 return NULL;
1631 status = ads_search_dn(ads, &res, base, attrs);
1632 if (!ADS_ERR_OK(status)) {
1633 DEBUG(1,("Failed while searching for: %s\n", base));
1634 goto out;
1637 if (ads_count_replies(ads, res) != 1) {
1638 goto out;
1641 /* substitute the bind-path from the well-known-guid-search result */
1642 wkn_dn = ads_get_dn(ads, res);
1643 if (!wkn_dn) {
1644 goto out;
1647 wkn_dn_exp = ldap_explode_dn(wkn_dn, 0);
1648 if (!wkn_dn_exp) {
1649 goto out;
1652 bind_dn_exp = ldap_explode_dn(ads->config.bind_path, 0);
1653 if (!bind_dn_exp) {
1654 goto out;
1657 for (wkn_ln=0; wkn_dn_exp[wkn_ln]; wkn_ln++)
1659 for (bind_ln=0; bind_dn_exp[bind_ln]; bind_ln++)
1662 new_ln = wkn_ln - bind_ln;
1664 ret = SMB_STRDUP(wkn_dn_exp[0]);
1665 if (!ret) {
1666 goto out;
1669 for (i=1; i < new_ln; i++) {
1670 char *s = NULL;
1672 if (asprintf(&s, "%s,%s", ret, wkn_dn_exp[i]) == -1) {
1673 SAFE_FREE(ret);
1674 goto out;
1677 SAFE_FREE(ret);
1678 ret = SMB_STRDUP(s);
1679 free(s);
1680 if (!ret) {
1681 goto out;
1685 out:
1686 SAFE_FREE(base);
1687 ads_msgfree(ads, res);
1688 ads_memfree(ads, wkn_dn);
1689 if (wkn_dn_exp) {
1690 ldap_value_free(wkn_dn_exp);
1692 if (bind_dn_exp) {
1693 ldap_value_free(bind_dn_exp);
1696 return ret;
1700 * Adds (appends) an item to an attribute array, rather then
1701 * replacing the whole list
1702 * @param ctx An initialized TALLOC_CTX
1703 * @param mods An initialized ADS_MODLIST
1704 * @param name name of the ldap attribute to append to
1705 * @param vals an array of values to add
1706 * @return status of addition
1709 ADS_STATUS ads_add_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1710 const char *name, const char **vals)
1712 return ads_modlist_add(ctx, mods, LDAP_MOD_ADD, name,
1713 (const void *) vals);
1717 * Determines the an account's current KVNO via an LDAP lookup
1718 * @param ads An initialized ADS_STRUCT
1719 * @param account_name the NT samaccountname.
1720 * @return the kvno for the account, or -1 in case of a failure.
1723 uint32 ads_get_kvno(ADS_STRUCT *ads, const char *account_name)
1725 LDAPMessage *res = NULL;
1726 uint32 kvno = (uint32)-1; /* -1 indicates a failure */
1727 char *filter;
1728 const char *attrs[] = {"msDS-KeyVersionNumber", NULL};
1729 char *dn_string = NULL;
1730 ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS);
1732 DEBUG(5,("ads_get_kvno: Searching for account %s\n", account_name));
1733 if (asprintf(&filter, "(samAccountName=%s)", account_name) == -1) {
1734 return kvno;
1736 ret = ads_search(ads, &res, filter, attrs);
1737 SAFE_FREE(filter);
1738 if (!ADS_ERR_OK(ret) || (ads_count_replies(ads, res) != 1)) {
1739 DEBUG(1,("ads_get_kvno: Account for %s not found.\n", account_name));
1740 ads_msgfree(ads, res);
1741 return kvno;
1744 dn_string = ads_get_dn(ads, res);
1745 if (!dn_string) {
1746 DEBUG(0,("ads_get_kvno: out of memory.\n"));
1747 ads_msgfree(ads, res);
1748 return kvno;
1750 DEBUG(5,("ads_get_kvno: Using: %s\n", dn_string));
1751 ads_memfree(ads, dn_string);
1753 /* ---------------------------------------------------------
1754 * 0 is returned as a default KVNO from this point on...
1755 * This is done because Windows 2000 does not support key
1756 * version numbers. Chances are that a failure in the next
1757 * step is simply due to Windows 2000 being used for a
1758 * domain controller. */
1759 kvno = 0;
1761 if (!ads_pull_uint32(ads, res, "msDS-KeyVersionNumber", &kvno)) {
1762 DEBUG(3,("ads_get_kvno: Error Determining KVNO!\n"));
1763 DEBUG(3,("ads_get_kvno: Windows 2000 does not support KVNO's, so this may be normal.\n"));
1764 ads_msgfree(ads, res);
1765 return kvno;
1768 /* Success */
1769 DEBUG(5,("ads_get_kvno: Looked Up KVNO of: %d\n", kvno));
1770 ads_msgfree(ads, res);
1771 return kvno;
1775 * Determines the computer account's current KVNO via an LDAP lookup
1776 * @param ads An initialized ADS_STRUCT
1777 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1778 * @return the kvno for the computer account, or -1 in case of a failure.
1781 uint32_t ads_get_machine_kvno(ADS_STRUCT *ads, const char *machine_name)
1783 char *computer_account = NULL;
1784 uint32_t kvno = -1;
1786 if (asprintf(&computer_account, "%s$", machine_name) < 0) {
1787 return kvno;
1790 kvno = ads_get_kvno(ads, computer_account);
1791 free(computer_account);
1793 return kvno;
1797 * This clears out all registered spn's for a given hostname
1798 * @param ads An initilaized ADS_STRUCT
1799 * @param machine_name the NetBIOS name of the computer.
1800 * @return 0 upon success, non-zero otherwise.
1803 ADS_STATUS ads_clear_service_principal_names(ADS_STRUCT *ads, const char *machine_name)
1805 TALLOC_CTX *ctx;
1806 LDAPMessage *res = NULL;
1807 ADS_MODLIST mods;
1808 const char *servicePrincipalName[1] = {NULL};
1809 ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS);
1810 char *dn_string = NULL;
1812 ret = ads_find_machine_acct(ads, &res, machine_name);
1813 if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
1814 DEBUG(5,("ads_clear_service_principal_names: WARNING: Host Account for %s not found... skipping operation.\n", machine_name));
1815 DEBUG(5,("ads_clear_service_principal_names: WARNING: Service Principals for %s have NOT been cleared.\n", machine_name));
1816 ads_msgfree(ads, res);
1817 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1820 DEBUG(5,("ads_clear_service_principal_names: Host account for %s found\n", machine_name));
1821 ctx = talloc_init("ads_clear_service_principal_names");
1822 if (!ctx) {
1823 ads_msgfree(ads, res);
1824 return ADS_ERROR(LDAP_NO_MEMORY);
1827 if (!(mods = ads_init_mods(ctx))) {
1828 talloc_destroy(ctx);
1829 ads_msgfree(ads, res);
1830 return ADS_ERROR(LDAP_NO_MEMORY);
1832 ret = ads_mod_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1833 if (!ADS_ERR_OK(ret)) {
1834 DEBUG(1,("ads_clear_service_principal_names: Error creating strlist.\n"));
1835 ads_msgfree(ads, res);
1836 talloc_destroy(ctx);
1837 return ret;
1839 dn_string = ads_get_dn(ads, res);
1840 if (!dn_string) {
1841 talloc_destroy(ctx);
1842 ads_msgfree(ads, res);
1843 return ADS_ERROR(LDAP_NO_MEMORY);
1845 ret = ads_gen_mod(ads, dn_string, mods);
1846 ads_memfree(ads,dn_string);
1847 if (!ADS_ERR_OK(ret)) {
1848 DEBUG(1,("ads_clear_service_principal_names: Error: Updating Service Principals for machine %s in LDAP\n",
1849 machine_name));
1850 ads_msgfree(ads, res);
1851 talloc_destroy(ctx);
1852 return ret;
1855 ads_msgfree(ads, res);
1856 talloc_destroy(ctx);
1857 return ret;
1861 * This adds a service principal name to an existing computer account
1862 * (found by hostname) in AD.
1863 * @param ads An initialized ADS_STRUCT
1864 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1865 * @param my_fqdn The fully qualified DNS name of the machine
1866 * @param spn A string of the service principal to add, i.e. 'host'
1867 * @return 0 upon sucess, or non-zero if a failure occurs
1870 ADS_STATUS ads_add_service_principal_name(ADS_STRUCT *ads, const char *machine_name,
1871 const char *my_fqdn, const char *spn)
1873 ADS_STATUS ret;
1874 TALLOC_CTX *ctx;
1875 LDAPMessage *res = NULL;
1876 char *psp1, *psp2;
1877 ADS_MODLIST mods;
1878 char *dn_string = NULL;
1879 const char *servicePrincipalName[3] = {NULL, NULL, NULL};
1881 ret = ads_find_machine_acct(ads, &res, machine_name);
1882 if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
1883 DEBUG(1,("ads_add_service_principal_name: WARNING: Host Account for %s not found... skipping operation.\n",
1884 machine_name));
1885 DEBUG(1,("ads_add_service_principal_name: WARNING: Service Principal '%s/%s@%s' has NOT been added.\n",
1886 spn, machine_name, ads->config.realm));
1887 ads_msgfree(ads, res);
1888 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1891 DEBUG(1,("ads_add_service_principal_name: Host account for %s found\n", machine_name));
1892 if (!(ctx = talloc_init("ads_add_service_principal_name"))) {
1893 ads_msgfree(ads, res);
1894 return ADS_ERROR(LDAP_NO_MEMORY);
1897 /* add short name spn */
1899 if ( (psp1 = talloc_asprintf(ctx, "%s/%s", spn, machine_name)) == NULL ) {
1900 talloc_destroy(ctx);
1901 ads_msgfree(ads, res);
1902 return ADS_ERROR(LDAP_NO_MEMORY);
1904 strupper_m(psp1);
1905 strlower_m(&psp1[strlen(spn)]);
1906 servicePrincipalName[0] = psp1;
1908 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n",
1909 psp1, machine_name));
1912 /* add fully qualified spn */
1914 if ( (psp2 = talloc_asprintf(ctx, "%s/%s", spn, my_fqdn)) == NULL ) {
1915 ret = ADS_ERROR(LDAP_NO_MEMORY);
1916 goto out;
1918 strupper_m(psp2);
1919 strlower_m(&psp2[strlen(spn)]);
1920 servicePrincipalName[1] = psp2;
1922 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n",
1923 psp2, machine_name));
1925 if ( (mods = ads_init_mods(ctx)) == NULL ) {
1926 ret = ADS_ERROR(LDAP_NO_MEMORY);
1927 goto out;
1930 ret = ads_add_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1931 if (!ADS_ERR_OK(ret)) {
1932 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
1933 goto out;
1936 if ( (dn_string = ads_get_dn(ads, res)) == NULL ) {
1937 ret = ADS_ERROR(LDAP_NO_MEMORY);
1938 goto out;
1941 ret = ads_gen_mod(ads, dn_string, mods);
1942 ads_memfree(ads,dn_string);
1943 if (!ADS_ERR_OK(ret)) {
1944 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
1945 goto out;
1948 out:
1949 TALLOC_FREE( ctx );
1950 ads_msgfree(ads, res);
1951 return ret;
1955 * adds a machine account to the ADS server
1956 * @param ads An intialized ADS_STRUCT
1957 * @param machine_name - the NetBIOS machine name of this account.
1958 * @param account_type A number indicating the type of account to create
1959 * @param org_unit The LDAP path in which to place this account
1960 * @return 0 upon success, or non-zero otherwise
1963 ADS_STATUS ads_create_machine_acct(ADS_STRUCT *ads, const char *machine_name,
1964 const char *org_unit)
1966 ADS_STATUS ret;
1967 char *samAccountName, *controlstr;
1968 TALLOC_CTX *ctx;
1969 ADS_MODLIST mods;
1970 char *machine_escaped = NULL;
1971 char *new_dn;
1972 const char *objectClass[] = {"top", "person", "organizationalPerson",
1973 "user", "computer", NULL};
1974 LDAPMessage *res = NULL;
1975 uint32 acct_control = ( UF_WORKSTATION_TRUST_ACCOUNT |\
1976 UF_DONT_EXPIRE_PASSWD |\
1977 UF_ACCOUNTDISABLE );
1979 if (!(ctx = talloc_init("ads_add_machine_acct")))
1980 return ADS_ERROR(LDAP_NO_MEMORY);
1982 ret = ADS_ERROR(LDAP_NO_MEMORY);
1984 machine_escaped = escape_rdn_val_string_alloc(machine_name);
1985 if (!machine_escaped) {
1986 goto done;
1989 new_dn = talloc_asprintf(ctx, "cn=%s,%s", machine_escaped, org_unit);
1990 samAccountName = talloc_asprintf(ctx, "%s$", machine_name);
1992 if ( !new_dn || !samAccountName ) {
1993 goto done;
1996 #ifndef ENCTYPE_ARCFOUR_HMAC
1997 acct_control |= UF_USE_DES_KEY_ONLY;
1998 #endif
2000 if (!(controlstr = talloc_asprintf(ctx, "%u", acct_control))) {
2001 goto done;
2004 if (!(mods = ads_init_mods(ctx))) {
2005 goto done;
2008 ads_mod_str(ctx, &mods, "cn", machine_name);
2009 ads_mod_str(ctx, &mods, "sAMAccountName", samAccountName);
2010 ads_mod_strlist(ctx, &mods, "objectClass", objectClass);
2011 ads_mod_str(ctx, &mods, "userAccountControl", controlstr);
2013 ret = ads_gen_add(ads, new_dn, mods);
2015 done:
2016 SAFE_FREE(machine_escaped);
2017 ads_msgfree(ads, res);
2018 talloc_destroy(ctx);
2020 return ret;
2024 * move a machine account to another OU on the ADS server
2025 * @param ads - An intialized ADS_STRUCT
2026 * @param machine_name - the NetBIOS machine name of this account.
2027 * @param org_unit - The LDAP path in which to place this account
2028 * @param moved - whether we moved the machine account (optional)
2029 * @return 0 upon success, or non-zero otherwise
2032 ADS_STATUS ads_move_machine_acct(ADS_STRUCT *ads, const char *machine_name,
2033 const char *org_unit, bool *moved)
2035 ADS_STATUS rc;
2036 int ldap_status;
2037 LDAPMessage *res = NULL;
2038 char *filter = NULL;
2039 char *computer_dn = NULL;
2040 char *parent_dn;
2041 char *computer_rdn = NULL;
2042 bool need_move = False;
2044 if (asprintf(&filter, "(samAccountName=%s$)", machine_name) == -1) {
2045 rc = ADS_ERROR(LDAP_NO_MEMORY);
2046 goto done;
2049 /* Find pre-existing machine */
2050 rc = ads_search(ads, &res, filter, NULL);
2051 if (!ADS_ERR_OK(rc)) {
2052 goto done;
2055 computer_dn = ads_get_dn(ads, res);
2056 if (!computer_dn) {
2057 rc = ADS_ERROR(LDAP_NO_MEMORY);
2058 goto done;
2061 parent_dn = ads_parent_dn(computer_dn);
2062 if (strequal(parent_dn, org_unit)) {
2063 goto done;
2066 need_move = True;
2068 if (asprintf(&computer_rdn, "CN=%s", machine_name) == -1) {
2069 rc = ADS_ERROR(LDAP_NO_MEMORY);
2070 goto done;
2073 ldap_status = ldap_rename_s(ads->ldap.ld, computer_dn, computer_rdn,
2074 org_unit, 1, NULL, NULL);
2075 rc = ADS_ERROR(ldap_status);
2077 done:
2078 ads_msgfree(ads, res);
2079 SAFE_FREE(filter);
2080 SAFE_FREE(computer_dn);
2081 SAFE_FREE(computer_rdn);
2083 if (!ADS_ERR_OK(rc)) {
2084 need_move = False;
2087 if (moved) {
2088 *moved = need_move;
2091 return rc;
2095 dump a binary result from ldap
2097 static void dump_binary(ADS_STRUCT *ads, const char *field, struct berval **values)
2099 int i, j;
2100 for (i=0; values[i]; i++) {
2101 printf("%s: ", field);
2102 for (j=0; j<values[i]->bv_len; j++) {
2103 printf("%02X", (unsigned char)values[i]->bv_val[j]);
2105 printf("\n");
2109 static void dump_guid(ADS_STRUCT *ads, const char *field, struct berval **values)
2111 int i;
2112 for (i=0; values[i]; i++) {
2114 UUID_FLAT guid;
2115 struct GUID tmp;
2117 memcpy(guid.info, values[i]->bv_val, sizeof(guid.info));
2118 smb_uuid_unpack(guid, &tmp);
2119 printf("%s: %s\n", field, GUID_string(talloc_tos(), &tmp));
2124 dump a sid result from ldap
2126 static void dump_sid(ADS_STRUCT *ads, const char *field, struct berval **values)
2128 int i;
2129 for (i=0; values[i]; i++) {
2130 DOM_SID sid;
2131 fstring tmp;
2132 sid_parse(values[i]->bv_val, values[i]->bv_len, &sid);
2133 printf("%s: %s\n", field, sid_to_fstring(tmp, &sid));
2138 dump ntSecurityDescriptor
2140 static void dump_sd(ADS_STRUCT *ads, const char *filed, struct berval **values)
2142 TALLOC_CTX *frame = talloc_stackframe();
2143 struct security_descriptor *psd;
2144 NTSTATUS status;
2146 status = unmarshall_sec_desc(talloc_tos(), (uint8 *)values[0]->bv_val,
2147 values[0]->bv_len, &psd);
2148 if (!NT_STATUS_IS_OK(status)) {
2149 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
2150 nt_errstr(status)));
2151 TALLOC_FREE(frame);
2152 return;
2155 if (psd) {
2156 ads_disp_sd(ads, talloc_tos(), psd);
2159 TALLOC_FREE(frame);
2163 dump a string result from ldap
2165 static void dump_string(const char *field, char **values)
2167 int i;
2168 for (i=0; values[i]; i++) {
2169 printf("%s: %s\n", field, values[i]);
2174 dump a field from LDAP on stdout
2175 used for debugging
2178 static bool ads_dump_field(ADS_STRUCT *ads, char *field, void **values, void *data_area)
2180 const struct {
2181 const char *name;
2182 bool string;
2183 void (*handler)(ADS_STRUCT *, const char *, struct berval **);
2184 } handlers[] = {
2185 {"objectGUID", False, dump_guid},
2186 {"netbootGUID", False, dump_guid},
2187 {"nTSecurityDescriptor", False, dump_sd},
2188 {"dnsRecord", False, dump_binary},
2189 {"objectSid", False, dump_sid},
2190 {"tokenGroups", False, dump_sid},
2191 {"tokenGroupsNoGCAcceptable", False, dump_sid},
2192 {"tokengroupsGlobalandUniversal", False, dump_sid},
2193 {"mS-DS-CreatorSID", False, dump_sid},
2194 {"msExchMailboxGuid", False, dump_guid},
2195 {NULL, True, NULL}
2197 int i;
2199 if (!field) { /* must be end of an entry */
2200 printf("\n");
2201 return False;
2204 for (i=0; handlers[i].name; i++) {
2205 if (StrCaseCmp(handlers[i].name, field) == 0) {
2206 if (!values) /* first time, indicate string or not */
2207 return handlers[i].string;
2208 handlers[i].handler(ads, field, (struct berval **) values);
2209 break;
2212 if (!handlers[i].name) {
2213 if (!values) /* first time, indicate string conversion */
2214 return True;
2215 dump_string(field, (char **)values);
2217 return False;
2221 * Dump a result from LDAP on stdout
2222 * used for debugging
2223 * @param ads connection to ads server
2224 * @param res Results to dump
2227 void ads_dump(ADS_STRUCT *ads, LDAPMessage *res)
2229 ads_process_results(ads, res, ads_dump_field, NULL);
2233 * Walk through results, calling a function for each entry found.
2234 * The function receives a field name, a berval * array of values,
2235 * and a data area passed through from the start. The function is
2236 * called once with null for field and values at the end of each
2237 * entry.
2238 * @param ads connection to ads server
2239 * @param res Results to process
2240 * @param fn Function for processing each result
2241 * @param data_area user-defined area to pass to function
2243 void ads_process_results(ADS_STRUCT *ads, LDAPMessage *res,
2244 bool (*fn)(ADS_STRUCT *, char *, void **, void *),
2245 void *data_area)
2247 LDAPMessage *msg;
2248 TALLOC_CTX *ctx;
2249 size_t converted_size;
2251 if (!(ctx = talloc_init("ads_process_results")))
2252 return;
2254 for (msg = ads_first_entry(ads, res); msg;
2255 msg = ads_next_entry(ads, msg)) {
2256 char *utf8_field;
2257 BerElement *b;
2259 for (utf8_field=ldap_first_attribute(ads->ldap.ld,
2260 (LDAPMessage *)msg,&b);
2261 utf8_field;
2262 utf8_field=ldap_next_attribute(ads->ldap.ld,
2263 (LDAPMessage *)msg,b)) {
2264 struct berval **ber_vals;
2265 char **str_vals, **utf8_vals;
2266 char *field;
2267 bool string;
2269 if (!pull_utf8_talloc(ctx, &field, utf8_field,
2270 &converted_size))
2272 DEBUG(0,("ads_process_results: "
2273 "pull_utf8_talloc failed: %s",
2274 strerror(errno)));
2277 string = fn(ads, field, NULL, data_area);
2279 if (string) {
2280 utf8_vals = ldap_get_values(ads->ldap.ld,
2281 (LDAPMessage *)msg, field);
2282 str_vals = ads_pull_strvals(ctx,
2283 (const char **) utf8_vals);
2284 fn(ads, field, (void **) str_vals, data_area);
2285 ldap_value_free(utf8_vals);
2286 } else {
2287 ber_vals = ldap_get_values_len(ads->ldap.ld,
2288 (LDAPMessage *)msg, field);
2289 fn(ads, field, (void **) ber_vals, data_area);
2291 ldap_value_free_len(ber_vals);
2293 ldap_memfree(utf8_field);
2295 ber_free(b, 0);
2296 talloc_free_children(ctx);
2297 fn(ads, NULL, NULL, data_area); /* completed an entry */
2300 talloc_destroy(ctx);
2304 * count how many replies are in a LDAPMessage
2305 * @param ads connection to ads server
2306 * @param res Results to count
2307 * @return number of replies
2309 int ads_count_replies(ADS_STRUCT *ads, void *res)
2311 return ldap_count_entries(ads->ldap.ld, (LDAPMessage *)res);
2315 * pull the first entry from a ADS result
2316 * @param ads connection to ads server
2317 * @param res Results of search
2318 * @return first entry from result
2320 LDAPMessage *ads_first_entry(ADS_STRUCT *ads, LDAPMessage *res)
2322 return ldap_first_entry(ads->ldap.ld, res);
2326 * pull the next entry from a ADS result
2327 * @param ads connection to ads server
2328 * @param res Results of search
2329 * @return next entry from result
2331 LDAPMessage *ads_next_entry(ADS_STRUCT *ads, LDAPMessage *res)
2333 return ldap_next_entry(ads->ldap.ld, res);
2337 * pull the first message from a ADS result
2338 * @param ads connection to ads server
2339 * @param res Results of search
2340 * @return first message from result
2342 LDAPMessage *ads_first_message(ADS_STRUCT *ads, LDAPMessage *res)
2344 return ldap_first_message(ads->ldap.ld, res);
2348 * pull the next message from a ADS result
2349 * @param ads connection to ads server
2350 * @param res Results of search
2351 * @return next message from result
2353 LDAPMessage *ads_next_message(ADS_STRUCT *ads, LDAPMessage *res)
2355 return ldap_next_message(ads->ldap.ld, res);
2359 * pull a single string from a ADS result
2360 * @param ads connection to ads server
2361 * @param mem_ctx TALLOC_CTX to use for allocating result string
2362 * @param msg Results of search
2363 * @param field Attribute to retrieve
2364 * @return Result string in talloc context
2366 char *ads_pull_string(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, LDAPMessage *msg,
2367 const char *field)
2369 char **values;
2370 char *ret = NULL;
2371 char *ux_string;
2372 size_t converted_size;
2374 values = ldap_get_values(ads->ldap.ld, msg, field);
2375 if (!values)
2376 return NULL;
2378 if (values[0] && pull_utf8_talloc(mem_ctx, &ux_string, values[0],
2379 &converted_size))
2381 ret = ux_string;
2383 ldap_value_free(values);
2384 return ret;
2388 * pull an array of strings from a ADS result
2389 * @param ads connection to ads server
2390 * @param mem_ctx TALLOC_CTX to use for allocating result string
2391 * @param msg Results of search
2392 * @param field Attribute to retrieve
2393 * @return Result strings in talloc context
2395 char **ads_pull_strings(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2396 LDAPMessage *msg, const char *field,
2397 size_t *num_values)
2399 char **values;
2400 char **ret = NULL;
2401 int i;
2402 size_t converted_size;
2404 values = ldap_get_values(ads->ldap.ld, msg, field);
2405 if (!values)
2406 return NULL;
2408 *num_values = ldap_count_values(values);
2410 ret = TALLOC_ARRAY(mem_ctx, char *, *num_values + 1);
2411 if (!ret) {
2412 ldap_value_free(values);
2413 return NULL;
2416 for (i=0;i<*num_values;i++) {
2417 if (!pull_utf8_talloc(mem_ctx, &ret[i], values[i],
2418 &converted_size))
2420 ldap_value_free(values);
2421 return NULL;
2424 ret[i] = NULL;
2426 ldap_value_free(values);
2427 return ret;
2431 * pull an array of strings from a ADS result
2432 * (handle large multivalue attributes with range retrieval)
2433 * @param ads connection to ads server
2434 * @param mem_ctx TALLOC_CTX to use for allocating result string
2435 * @param msg Results of search
2436 * @param field Attribute to retrieve
2437 * @param current_strings strings returned by a previous call to this function
2438 * @param next_attribute The next query should ask for this attribute
2439 * @param num_values How many values did we get this time?
2440 * @param more_values Are there more values to get?
2441 * @return Result strings in talloc context
2443 char **ads_pull_strings_range(ADS_STRUCT *ads,
2444 TALLOC_CTX *mem_ctx,
2445 LDAPMessage *msg, const char *field,
2446 char **current_strings,
2447 const char **next_attribute,
2448 size_t *num_strings,
2449 bool *more_strings)
2451 char *attr;
2452 char *expected_range_attrib, *range_attr;
2453 BerElement *ptr = NULL;
2454 char **strings;
2455 char **new_strings;
2456 size_t num_new_strings;
2457 unsigned long int range_start;
2458 unsigned long int range_end;
2460 /* we might have been given the whole lot anyway */
2461 if ((strings = ads_pull_strings(ads, mem_ctx, msg, field, num_strings))) {
2462 *more_strings = False;
2463 return strings;
2466 expected_range_attrib = talloc_asprintf(mem_ctx, "%s;Range=", field);
2468 /* look for Range result */
2469 for (attr = ldap_first_attribute(ads->ldap.ld, (LDAPMessage *)msg, &ptr);
2470 attr;
2471 attr = ldap_next_attribute(ads->ldap.ld, (LDAPMessage *)msg, ptr)) {
2472 /* we ignore the fact that this is utf8, as all attributes are ascii... */
2473 if (strnequal(attr, expected_range_attrib, strlen(expected_range_attrib))) {
2474 range_attr = attr;
2475 break;
2477 ldap_memfree(attr);
2479 if (!attr) {
2480 ber_free(ptr, 0);
2481 /* nothing here - this field is just empty */
2482 *more_strings = False;
2483 return NULL;
2486 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-%lu",
2487 &range_start, &range_end) == 2) {
2488 *more_strings = True;
2489 } else {
2490 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-*",
2491 &range_start) == 1) {
2492 *more_strings = False;
2493 } else {
2494 DEBUG(1, ("ads_pull_strings_range: Cannot parse Range attriubte (%s)\n",
2495 range_attr));
2496 ldap_memfree(range_attr);
2497 *more_strings = False;
2498 return NULL;
2502 if ((*num_strings) != range_start) {
2503 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) doesn't start at %u, but at %lu"
2504 " - aborting range retreival\n",
2505 range_attr, (unsigned int)(*num_strings) + 1, range_start));
2506 ldap_memfree(range_attr);
2507 *more_strings = False;
2508 return NULL;
2511 new_strings = ads_pull_strings(ads, mem_ctx, msg, range_attr, &num_new_strings);
2513 if (*more_strings && ((*num_strings + num_new_strings) != (range_end + 1))) {
2514 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) tells us we have %lu "
2515 "strings in this bunch, but we only got %lu - aborting range retreival\n",
2516 range_attr, (unsigned long int)range_end - range_start + 1,
2517 (unsigned long int)num_new_strings));
2518 ldap_memfree(range_attr);
2519 *more_strings = False;
2520 return NULL;
2523 strings = TALLOC_REALLOC_ARRAY(mem_ctx, current_strings, char *,
2524 *num_strings + num_new_strings);
2526 if (strings == NULL) {
2527 ldap_memfree(range_attr);
2528 *more_strings = False;
2529 return NULL;
2532 if (new_strings && num_new_strings) {
2533 memcpy(&strings[*num_strings], new_strings,
2534 sizeof(*new_strings) * num_new_strings);
2537 (*num_strings) += num_new_strings;
2539 if (*more_strings) {
2540 *next_attribute = talloc_asprintf(mem_ctx,
2541 "%s;range=%d-*",
2542 field,
2543 (int)*num_strings);
2545 if (!*next_attribute) {
2546 DEBUG(1, ("talloc_asprintf for next attribute failed!\n"));
2547 ldap_memfree(range_attr);
2548 *more_strings = False;
2549 return NULL;
2553 ldap_memfree(range_attr);
2555 return strings;
2559 * pull a single uint32 from a ADS result
2560 * @param ads connection to ads server
2561 * @param msg Results of search
2562 * @param field Attribute to retrieve
2563 * @param v Pointer to int to store result
2564 * @return boolean inidicating success
2566 bool ads_pull_uint32(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
2567 uint32 *v)
2569 char **values;
2571 values = ldap_get_values(ads->ldap.ld, msg, field);
2572 if (!values)
2573 return False;
2574 if (!values[0]) {
2575 ldap_value_free(values);
2576 return False;
2579 *v = atoi(values[0]);
2580 ldap_value_free(values);
2581 return True;
2585 * pull a single objectGUID from an ADS result
2586 * @param ads connection to ADS server
2587 * @param msg results of search
2588 * @param guid 37-byte area to receive text guid
2589 * @return boolean indicating success
2591 bool ads_pull_guid(ADS_STRUCT *ads, LDAPMessage *msg, struct GUID *guid)
2593 char **values;
2594 UUID_FLAT flat_guid;
2596 values = ldap_get_values(ads->ldap.ld, msg, "objectGUID");
2597 if (!values)
2598 return False;
2600 if (values[0]) {
2601 memcpy(&flat_guid.info, values[0], sizeof(UUID_FLAT));
2602 smb_uuid_unpack(flat_guid, guid);
2603 ldap_value_free(values);
2604 return True;
2606 ldap_value_free(values);
2607 return False;
2613 * pull a single DOM_SID from a ADS result
2614 * @param ads connection to ads server
2615 * @param msg Results of search
2616 * @param field Attribute to retrieve
2617 * @param sid Pointer to sid to store result
2618 * @return boolean inidicating success
2620 bool ads_pull_sid(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
2621 DOM_SID *sid)
2623 struct berval **values;
2624 bool ret = False;
2626 values = ldap_get_values_len(ads->ldap.ld, msg, field);
2628 if (!values)
2629 return False;
2631 if (values[0])
2632 ret = sid_parse(values[0]->bv_val, values[0]->bv_len, sid);
2634 ldap_value_free_len(values);
2635 return ret;
2639 * pull an array of DOM_SIDs from a ADS result
2640 * @param ads connection to ads server
2641 * @param mem_ctx TALLOC_CTX for allocating sid array
2642 * @param msg Results of search
2643 * @param field Attribute to retrieve
2644 * @param sids pointer to sid array to allocate
2645 * @return the count of SIDs pulled
2647 int ads_pull_sids(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2648 LDAPMessage *msg, const char *field, DOM_SID **sids)
2650 struct berval **values;
2651 bool ret;
2652 int count, i;
2654 values = ldap_get_values_len(ads->ldap.ld, msg, field);
2656 if (!values)
2657 return 0;
2659 for (i=0; values[i]; i++)
2660 /* nop */ ;
2662 if (i) {
2663 (*sids) = TALLOC_ARRAY(mem_ctx, DOM_SID, i);
2664 if (!(*sids)) {
2665 ldap_value_free_len(values);
2666 return 0;
2668 } else {
2669 (*sids) = NULL;
2672 count = 0;
2673 for (i=0; values[i]; i++) {
2674 ret = sid_parse(values[i]->bv_val, values[i]->bv_len, &(*sids)[count]);
2675 if (ret) {
2676 DEBUG(10, ("pulling SID: %s\n",
2677 sid_string_dbg(&(*sids)[count])));
2678 count++;
2682 ldap_value_free_len(values);
2683 return count;
2687 * pull a SEC_DESC from a ADS result
2688 * @param ads connection to ads server
2689 * @param mem_ctx TALLOC_CTX for allocating sid array
2690 * @param msg Results of search
2691 * @param field Attribute to retrieve
2692 * @param sd Pointer to *SEC_DESC to store result (talloc()ed)
2693 * @return boolean inidicating success
2695 bool ads_pull_sd(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2696 LDAPMessage *msg, const char *field, SEC_DESC **sd)
2698 struct berval **values;
2699 bool ret = true;
2701 values = ldap_get_values_len(ads->ldap.ld, msg, field);
2703 if (!values) return false;
2705 if (values[0]) {
2706 NTSTATUS status;
2707 status = unmarshall_sec_desc(mem_ctx,
2708 (uint8 *)values[0]->bv_val,
2709 values[0]->bv_len, sd);
2710 if (!NT_STATUS_IS_OK(status)) {
2711 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
2712 nt_errstr(status)));
2713 ret = false;
2717 ldap_value_free_len(values);
2718 return ret;
2722 * in order to support usernames longer than 21 characters we need to
2723 * use both the sAMAccountName and the userPrincipalName attributes
2724 * It seems that not all users have the userPrincipalName attribute set
2726 * @param ads connection to ads server
2727 * @param mem_ctx TALLOC_CTX for allocating sid array
2728 * @param msg Results of search
2729 * @return the username
2731 char *ads_pull_username(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2732 LDAPMessage *msg)
2734 #if 0 /* JERRY */
2735 char *ret, *p;
2737 /* lookup_name() only works on the sAMAccountName to
2738 returning the username portion of userPrincipalName
2739 breaks winbindd_getpwnam() */
2741 ret = ads_pull_string(ads, mem_ctx, msg, "userPrincipalName");
2742 if (ret && (p = strchr_m(ret, '@'))) {
2743 *p = 0;
2744 return ret;
2746 #endif
2747 return ads_pull_string(ads, mem_ctx, msg, "sAMAccountName");
2752 * find the update serial number - this is the core of the ldap cache
2753 * @param ads connection to ads server
2754 * @param ads connection to ADS server
2755 * @param usn Pointer to retrieved update serial number
2756 * @return status of search
2758 ADS_STATUS ads_USN(ADS_STRUCT *ads, uint32 *usn)
2760 const char *attrs[] = {"highestCommittedUSN", NULL};
2761 ADS_STATUS status;
2762 LDAPMessage *res;
2764 status = ads_do_search_retry(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2765 if (!ADS_ERR_OK(status))
2766 return status;
2768 if (ads_count_replies(ads, res) != 1) {
2769 ads_msgfree(ads, res);
2770 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2773 if (!ads_pull_uint32(ads, res, "highestCommittedUSN", usn)) {
2774 ads_msgfree(ads, res);
2775 return ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
2778 ads_msgfree(ads, res);
2779 return ADS_SUCCESS;
2782 /* parse a ADS timestring - typical string is
2783 '20020917091222.0Z0' which means 09:12.22 17th September
2784 2002, timezone 0 */
2785 static time_t ads_parse_time(const char *str)
2787 struct tm tm;
2789 ZERO_STRUCT(tm);
2791 if (sscanf(str, "%4d%2d%2d%2d%2d%2d",
2792 &tm.tm_year, &tm.tm_mon, &tm.tm_mday,
2793 &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) {
2794 return 0;
2796 tm.tm_year -= 1900;
2797 tm.tm_mon -= 1;
2799 return timegm(&tm);
2802 /********************************************************************
2803 ********************************************************************/
2805 ADS_STATUS ads_current_time(ADS_STRUCT *ads)
2807 const char *attrs[] = {"currentTime", NULL};
2808 ADS_STATUS status;
2809 LDAPMessage *res;
2810 char *timestr;
2811 TALLOC_CTX *ctx;
2812 ADS_STRUCT *ads_s = ads;
2814 if (!(ctx = talloc_init("ads_current_time"))) {
2815 return ADS_ERROR(LDAP_NO_MEMORY);
2818 /* establish a new ldap tcp session if necessary */
2820 if ( !ads->ldap.ld ) {
2821 if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup,
2822 ads->server.ldap_server )) == NULL )
2824 goto done;
2826 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
2827 status = ads_connect( ads_s );
2828 if ( !ADS_ERR_OK(status))
2829 goto done;
2832 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2833 if (!ADS_ERR_OK(status)) {
2834 goto done;
2837 timestr = ads_pull_string(ads_s, ctx, res, "currentTime");
2838 if (!timestr) {
2839 ads_msgfree(ads_s, res);
2840 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2841 goto done;
2844 /* but save the time and offset in the original ADS_STRUCT */
2846 ads->config.current_time = ads_parse_time(timestr);
2848 if (ads->config.current_time != 0) {
2849 ads->auth.time_offset = ads->config.current_time - time(NULL);
2850 DEBUG(4,("time offset is %d seconds\n", ads->auth.time_offset));
2853 ads_msgfree(ads, res);
2855 status = ADS_SUCCESS;
2857 done:
2858 /* free any temporary ads connections */
2859 if ( ads_s != ads ) {
2860 ads_destroy( &ads_s );
2862 talloc_destroy(ctx);
2864 return status;
2867 /********************************************************************
2868 ********************************************************************/
2870 ADS_STATUS ads_domain_func_level(ADS_STRUCT *ads, uint32 *val)
2872 const char *attrs[] = {"domainFunctionality", NULL};
2873 ADS_STATUS status;
2874 LDAPMessage *res;
2875 ADS_STRUCT *ads_s = ads;
2877 *val = DS_DOMAIN_FUNCTION_2000;
2879 /* establish a new ldap tcp session if necessary */
2881 if ( !ads->ldap.ld ) {
2882 if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup,
2883 ads->server.ldap_server )) == NULL )
2885 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
2886 goto done;
2888 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
2889 status = ads_connect( ads_s );
2890 if ( !ADS_ERR_OK(status))
2891 goto done;
2894 /* If the attribute does not exist assume it is a Windows 2000
2895 functional domain */
2897 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2898 if (!ADS_ERR_OK(status)) {
2899 if ( status.err.rc == LDAP_NO_SUCH_ATTRIBUTE ) {
2900 status = ADS_SUCCESS;
2902 goto done;
2905 if ( !ads_pull_uint32(ads_s, res, "domainFunctionality", val) ) {
2906 DEBUG(5,("ads_domain_func_level: Failed to pull the domainFunctionality attribute.\n"));
2908 DEBUG(3,("ads_domain_func_level: %d\n", *val));
2911 ads_msgfree(ads, res);
2913 done:
2914 /* free any temporary ads connections */
2915 if ( ads_s != ads ) {
2916 ads_destroy( &ads_s );
2919 return status;
2923 * find the domain sid for our domain
2924 * @param ads connection to ads server
2925 * @param sid Pointer to domain sid
2926 * @return status of search
2928 ADS_STATUS ads_domain_sid(ADS_STRUCT *ads, DOM_SID *sid)
2930 const char *attrs[] = {"objectSid", NULL};
2931 LDAPMessage *res;
2932 ADS_STATUS rc;
2934 rc = ads_do_search_retry(ads, ads->config.bind_path, LDAP_SCOPE_BASE, "(objectclass=*)",
2935 attrs, &res);
2936 if (!ADS_ERR_OK(rc)) return rc;
2937 if (!ads_pull_sid(ads, res, "objectSid", sid)) {
2938 ads_msgfree(ads, res);
2939 return ADS_ERROR_SYSTEM(ENOENT);
2941 ads_msgfree(ads, res);
2943 return ADS_SUCCESS;
2947 * find our site name
2948 * @param ads connection to ads server
2949 * @param mem_ctx Pointer to talloc context
2950 * @param site_name Pointer to the sitename
2951 * @return status of search
2953 ADS_STATUS ads_site_dn(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char **site_name)
2955 ADS_STATUS status;
2956 LDAPMessage *res;
2957 const char *dn, *service_name;
2958 const char *attrs[] = { "dsServiceName", NULL };
2960 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2961 if (!ADS_ERR_OK(status)) {
2962 return status;
2965 service_name = ads_pull_string(ads, mem_ctx, res, "dsServiceName");
2966 if (service_name == NULL) {
2967 ads_msgfree(ads, res);
2968 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2971 ads_msgfree(ads, res);
2973 /* go up three levels */
2974 dn = ads_parent_dn(ads_parent_dn(ads_parent_dn(service_name)));
2975 if (dn == NULL) {
2976 return ADS_ERROR(LDAP_NO_MEMORY);
2979 *site_name = talloc_strdup(mem_ctx, dn);
2980 if (*site_name == NULL) {
2981 return ADS_ERROR(LDAP_NO_MEMORY);
2984 return status;
2986 dsServiceName: CN=NTDS Settings,CN=W2K3DC,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,DC=ber,DC=suse,DC=de
2991 * find the site dn where a machine resides
2992 * @param ads connection to ads server
2993 * @param mem_ctx Pointer to talloc context
2994 * @param computer_name name of the machine
2995 * @param site_name Pointer to the sitename
2996 * @return status of search
2998 ADS_STATUS ads_site_dn_for_machine(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char *computer_name, const char **site_dn)
3000 ADS_STATUS status;
3001 LDAPMessage *res;
3002 const char *parent, *filter;
3003 char *config_context = NULL;
3004 char *dn;
3006 /* shortcut a query */
3007 if (strequal(computer_name, ads->config.ldap_server_name)) {
3008 return ads_site_dn(ads, mem_ctx, site_dn);
3011 status = ads_config_path(ads, mem_ctx, &config_context);
3012 if (!ADS_ERR_OK(status)) {
3013 return status;
3016 filter = talloc_asprintf(mem_ctx, "(cn=%s)", computer_name);
3017 if (filter == NULL) {
3018 return ADS_ERROR(LDAP_NO_MEMORY);
3021 status = ads_do_search(ads, config_context, LDAP_SCOPE_SUBTREE,
3022 filter, NULL, &res);
3023 if (!ADS_ERR_OK(status)) {
3024 return status;
3027 if (ads_count_replies(ads, res) != 1) {
3028 ads_msgfree(ads, res);
3029 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
3032 dn = ads_get_dn(ads, res);
3033 if (dn == NULL) {
3034 ads_msgfree(ads, res);
3035 return ADS_ERROR(LDAP_NO_MEMORY);
3038 /* go up three levels */
3039 parent = ads_parent_dn(ads_parent_dn(ads_parent_dn(dn)));
3040 if (parent == NULL) {
3041 ads_msgfree(ads, res);
3042 ads_memfree(ads, dn);
3043 return ADS_ERROR(LDAP_NO_MEMORY);
3046 *site_dn = talloc_strdup(mem_ctx, parent);
3047 if (*site_dn == NULL) {
3048 ads_msgfree(ads, res);
3049 ads_memfree(ads, dn);
3050 return ADS_ERROR(LDAP_NO_MEMORY);
3053 ads_memfree(ads, dn);
3054 ads_msgfree(ads, res);
3056 return status;
3060 * get the upn suffixes for a domain
3061 * @param ads connection to ads server
3062 * @param mem_ctx Pointer to talloc context
3063 * @param suffixes Pointer to an array of suffixes
3064 * @param num_suffixes Pointer to the number of suffixes
3065 * @return status of search
3067 ADS_STATUS ads_upn_suffixes(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, char ***suffixes, size_t *num_suffixes)
3069 ADS_STATUS status;
3070 LDAPMessage *res;
3071 const char *base;
3072 char *config_context = NULL;
3073 const char *attrs[] = { "uPNSuffixes", NULL };
3075 status = ads_config_path(ads, mem_ctx, &config_context);
3076 if (!ADS_ERR_OK(status)) {
3077 return status;
3080 base = talloc_asprintf(mem_ctx, "cn=Partitions,%s", config_context);
3081 if (base == NULL) {
3082 return ADS_ERROR(LDAP_NO_MEMORY);
3085 status = ads_search_dn(ads, &res, base, attrs);
3086 if (!ADS_ERR_OK(status)) {
3087 return status;
3090 if (ads_count_replies(ads, res) != 1) {
3091 ads_msgfree(ads, res);
3092 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
3095 (*suffixes) = ads_pull_strings(ads, mem_ctx, res, "uPNSuffixes", num_suffixes);
3096 if ((*suffixes) == NULL) {
3097 ads_msgfree(ads, res);
3098 return ADS_ERROR(LDAP_NO_MEMORY);
3101 ads_msgfree(ads, res);
3103 return status;
3107 * get the joinable ous for a domain
3108 * @param ads connection to ads server
3109 * @param mem_ctx Pointer to talloc context
3110 * @param ous Pointer to an array of ous
3111 * @param num_ous Pointer to the number of ous
3112 * @return status of search
3114 ADS_STATUS ads_get_joinable_ous(ADS_STRUCT *ads,
3115 TALLOC_CTX *mem_ctx,
3116 char ***ous,
3117 size_t *num_ous)
3119 ADS_STATUS status;
3120 LDAPMessage *res = NULL;
3121 LDAPMessage *msg = NULL;
3122 const char *attrs[] = { "dn", NULL };
3123 int count = 0;
3125 status = ads_search(ads, &res,
3126 "(|(objectClass=domain)(objectclass=organizationalUnit))",
3127 attrs);
3128 if (!ADS_ERR_OK(status)) {
3129 return status;
3132 count = ads_count_replies(ads, res);
3133 if (count < 1) {
3134 ads_msgfree(ads, res);
3135 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3138 for (msg = ads_first_entry(ads, res); msg;
3139 msg = ads_next_entry(ads, msg)) {
3141 char *dn = NULL;
3143 dn = ads_get_dn(ads, msg);
3144 if (!dn) {
3145 ads_msgfree(ads, res);
3146 return ADS_ERROR(LDAP_NO_MEMORY);
3149 if (!add_string_to_array(mem_ctx, dn,
3150 (const char ***)ous,
3151 (int *)num_ous)) {
3152 ads_memfree(ads, dn);
3153 ads_msgfree(ads, res);
3154 return ADS_ERROR(LDAP_NO_MEMORY);
3157 ads_memfree(ads, dn);
3160 ads_msgfree(ads, res);
3162 return status;
3167 * pull a DOM_SID from an extended dn string
3168 * @param mem_ctx TALLOC_CTX
3169 * @param extended_dn string
3170 * @param flags string type of extended_dn
3171 * @param sid pointer to a DOM_SID
3172 * @return NT_STATUS_OK on success,
3173 * NT_INVALID_PARAMETER on error,
3174 * NT_STATUS_NOT_FOUND if no SID present
3176 ADS_STATUS ads_get_sid_from_extended_dn(TALLOC_CTX *mem_ctx,
3177 const char *extended_dn,
3178 enum ads_extended_dn_flags flags,
3179 DOM_SID *sid)
3181 char *p, *q, *dn;
3183 if (!extended_dn) {
3184 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3187 /* otherwise extended_dn gets stripped off */
3188 if ((dn = talloc_strdup(mem_ctx, extended_dn)) == NULL) {
3189 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3192 * ADS_EXTENDED_DN_HEX_STRING:
3193 * <GUID=238e1963cb390f4bb032ba0105525a29>;<SID=010500000000000515000000bb68c8fd6b61b427572eb04556040000>;CN=gd,OU=berlin,OU=suse,DC=ber,DC=suse,DC=de
3195 * ADS_EXTENDED_DN_STRING (only with w2k3):
3196 * <GUID=63198e23-39cb-4b0f-b032-ba0105525a29>;<SID=S-1-5-21-4257769659-666132843-1169174103-1110>;CN=gd,OU=berlin,OU=suse,DC=ber,DC=suse,DC=de
3198 * Object with no SID, such as an Exchange Public Folder
3199 * <GUID=28907fb4bdf6854993e7f0a10b504e7c>;CN=public,CN=Microsoft Exchange System Objects,DC=sd2k3ms,DC=west,DC=isilon,DC=com
3202 p = strchr(dn, ';');
3203 if (!p) {
3204 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3207 if (strncmp(p, ";<SID=", strlen(";<SID=")) != 0) {
3208 DEBUG(5,("No SID present in extended dn\n"));
3209 return ADS_ERROR_NT(NT_STATUS_NOT_FOUND);
3212 p += strlen(";<SID=");
3214 q = strchr(p, '>');
3215 if (!q) {
3216 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3219 *q = '\0';
3221 DEBUG(100,("ads_get_sid_from_extended_dn: sid string is %s\n", p));
3223 switch (flags) {
3225 case ADS_EXTENDED_DN_STRING:
3226 if (!string_to_sid(sid, p)) {
3227 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3229 break;
3230 case ADS_EXTENDED_DN_HEX_STRING: {
3231 fstring buf;
3232 size_t buf_len;
3234 buf_len = strhex_to_str(buf, sizeof(buf), p, strlen(p));
3235 if (buf_len == 0) {
3236 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3239 if (!sid_parse(buf, buf_len, sid)) {
3240 DEBUG(10,("failed to parse sid\n"));
3241 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3243 break;
3245 default:
3246 DEBUG(10,("unknown extended dn format\n"));
3247 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3250 return ADS_ERROR_NT(NT_STATUS_OK);
3254 * pull an array of DOM_SIDs from a ADS result
3255 * @param ads connection to ads server
3256 * @param mem_ctx TALLOC_CTX for allocating sid array
3257 * @param msg Results of search
3258 * @param field Attribute to retrieve
3259 * @param flags string type of extended_dn
3260 * @param sids pointer to sid array to allocate
3261 * @return the count of SIDs pulled
3263 int ads_pull_sids_from_extendeddn(ADS_STRUCT *ads,
3264 TALLOC_CTX *mem_ctx,
3265 LDAPMessage *msg,
3266 const char *field,
3267 enum ads_extended_dn_flags flags,
3268 DOM_SID **sids)
3270 int i;
3271 ADS_STATUS rc;
3272 size_t dn_count, ret_count = 0;
3273 char **dn_strings;
3275 if ((dn_strings = ads_pull_strings(ads, mem_ctx, msg, field,
3276 &dn_count)) == NULL) {
3277 return 0;
3280 (*sids) = TALLOC_ZERO_ARRAY(mem_ctx, DOM_SID, dn_count + 1);
3281 if (!(*sids)) {
3282 TALLOC_FREE(dn_strings);
3283 return 0;
3286 for (i=0; i<dn_count; i++) {
3287 rc = ads_get_sid_from_extended_dn(mem_ctx, dn_strings[i],
3288 flags, &(*sids)[i]);
3289 if (!ADS_ERR_OK(rc)) {
3290 if (NT_STATUS_EQUAL(ads_ntstatus(rc),
3291 NT_STATUS_NOT_FOUND)) {
3292 continue;
3294 else {
3295 TALLOC_FREE(*sids);
3296 TALLOC_FREE(dn_strings);
3297 return 0;
3300 ret_count++;
3303 TALLOC_FREE(dn_strings);
3305 return ret_count;
3308 /********************************************************************
3309 ********************************************************************/
3311 char* ads_get_dnshostname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3313 LDAPMessage *res = NULL;
3314 ADS_STATUS status;
3315 int count = 0;
3316 char *name = NULL;
3318 status = ads_find_machine_acct(ads, &res, global_myname());
3319 if (!ADS_ERR_OK(status)) {
3320 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
3321 global_myname()));
3322 goto out;
3325 if ( (count = ads_count_replies(ads, res)) != 1 ) {
3326 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
3327 goto out;
3330 if ( (name = ads_pull_string(ads, ctx, res, "dNSHostName")) == NULL ) {
3331 DEBUG(0,("ads_get_dnshostname: No dNSHostName attribute!\n"));
3334 out:
3335 ads_msgfree(ads, res);
3337 return name;
3340 /********************************************************************
3341 ********************************************************************/
3343 char* ads_get_upn( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3345 LDAPMessage *res = NULL;
3346 ADS_STATUS status;
3347 int count = 0;
3348 char *name = NULL;
3350 status = ads_find_machine_acct(ads, &res, machine_name);
3351 if (!ADS_ERR_OK(status)) {
3352 DEBUG(0,("ads_get_upn: Failed to find account for %s\n",
3353 global_myname()));
3354 goto out;
3357 if ( (count = ads_count_replies(ads, res)) != 1 ) {
3358 DEBUG(1,("ads_get_upn: %d entries returned!\n", count));
3359 goto out;
3362 if ( (name = ads_pull_string(ads, ctx, res, "userPrincipalName")) == NULL ) {
3363 DEBUG(2,("ads_get_upn: No userPrincipalName attribute!\n"));
3366 out:
3367 ads_msgfree(ads, res);
3369 return name;
3372 /********************************************************************
3373 ********************************************************************/
3375 char* ads_get_samaccountname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3377 LDAPMessage *res = NULL;
3378 ADS_STATUS status;
3379 int count = 0;
3380 char *name = NULL;
3382 status = ads_find_machine_acct(ads, &res, global_myname());
3383 if (!ADS_ERR_OK(status)) {
3384 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
3385 global_myname()));
3386 goto out;
3389 if ( (count = ads_count_replies(ads, res)) != 1 ) {
3390 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
3391 goto out;
3394 if ( (name = ads_pull_string(ads, ctx, res, "sAMAccountName")) == NULL ) {
3395 DEBUG(0,("ads_get_dnshostname: No sAMAccountName attribute!\n"));
3398 out:
3399 ads_msgfree(ads, res);
3401 return name;
3404 #if 0
3406 SAVED CODE - we used to join via ldap - remember how we did this. JRA.
3409 * Join a machine to a realm
3410 * Creates the machine account and sets the machine password
3411 * @param ads connection to ads server
3412 * @param machine name of host to add
3413 * @param org_unit Organizational unit to place machine in
3414 * @return status of join
3416 ADS_STATUS ads_join_realm(ADS_STRUCT *ads, const char *machine_name,
3417 uint32 account_type, const char *org_unit)
3419 ADS_STATUS status;
3420 LDAPMessage *res = NULL;
3421 char *machine;
3423 /* machine name must be lowercase */
3424 machine = SMB_STRDUP(machine_name);
3425 strlower_m(machine);
3428 status = ads_find_machine_acct(ads, (void **)&res, machine);
3429 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
3430 DEBUG(0, ("Host account for %s already exists - deleting old account\n", machine));
3431 status = ads_leave_realm(ads, machine);
3432 if (!ADS_ERR_OK(status)) {
3433 DEBUG(0, ("Failed to delete host '%s' from the '%s' realm.\n",
3434 machine, ads->config.realm));
3435 return status;
3439 status = ads_add_machine_acct(ads, machine, account_type, org_unit);
3440 if (!ADS_ERR_OK(status)) {
3441 DEBUG(0, ("ads_join_realm: ads_add_machine_acct failed (%s): %s\n", machine, ads_errstr(status)));
3442 SAFE_FREE(machine);
3443 return status;
3446 status = ads_find_machine_acct(ads, (void **)(void *)&res, machine);
3447 if (!ADS_ERR_OK(status)) {
3448 DEBUG(0, ("ads_join_realm: Host account test failed for machine %s\n", machine));
3449 SAFE_FREE(machine);
3450 return status;
3453 SAFE_FREE(machine);
3454 ads_msgfree(ads, res);
3456 return status;
3458 #endif
3461 * Delete a machine from the realm
3462 * @param ads connection to ads server
3463 * @param hostname Machine to remove
3464 * @return status of delete
3466 ADS_STATUS ads_leave_realm(ADS_STRUCT *ads, const char *hostname)
3468 ADS_STATUS status;
3469 void *msg;
3470 LDAPMessage *res;
3471 char *hostnameDN, *host;
3472 int rc;
3473 LDAPControl ldap_control;
3474 LDAPControl * pldap_control[2] = {NULL, NULL};
3476 pldap_control[0] = &ldap_control;
3477 memset(&ldap_control, 0, sizeof(LDAPControl));
3478 ldap_control.ldctl_oid = (char *)LDAP_SERVER_TREE_DELETE_OID;
3480 /* hostname must be lowercase */
3481 host = SMB_STRDUP(hostname);
3482 strlower_m(host);
3484 status = ads_find_machine_acct(ads, &res, host);
3485 if (!ADS_ERR_OK(status)) {
3486 DEBUG(0, ("Host account for %s does not exist.\n", host));
3487 SAFE_FREE(host);
3488 return status;
3491 msg = ads_first_entry(ads, res);
3492 if (!msg) {
3493 SAFE_FREE(host);
3494 return ADS_ERROR_SYSTEM(ENOENT);
3497 hostnameDN = ads_get_dn(ads, (LDAPMessage *)msg);
3499 rc = ldap_delete_ext_s(ads->ldap.ld, hostnameDN, pldap_control, NULL);
3500 if (rc) {
3501 DEBUG(3,("ldap_delete_ext_s failed with error code %d\n", rc));
3502 }else {
3503 DEBUG(3,("ldap_delete_ext_s succeeded with error code %d\n", rc));
3506 if (rc != LDAP_SUCCESS) {
3507 const char *attrs[] = { "cn", NULL };
3508 LDAPMessage *msg_sub;
3510 /* we only search with scope ONE, we do not expect any further
3511 * objects to be created deeper */
3513 status = ads_do_search_retry(ads, hostnameDN,
3514 LDAP_SCOPE_ONELEVEL,
3515 "(objectclass=*)", attrs, &res);
3517 if (!ADS_ERR_OK(status)) {
3518 SAFE_FREE(host);
3519 ads_memfree(ads, hostnameDN);
3520 return status;
3523 for (msg_sub = ads_first_entry(ads, res); msg_sub;
3524 msg_sub = ads_next_entry(ads, msg_sub)) {
3526 char *dn = NULL;
3528 if ((dn = ads_get_dn(ads, msg_sub)) == NULL) {
3529 SAFE_FREE(host);
3530 ads_memfree(ads, hostnameDN);
3531 return ADS_ERROR(LDAP_NO_MEMORY);
3534 status = ads_del_dn(ads, dn);
3535 if (!ADS_ERR_OK(status)) {
3536 DEBUG(3,("failed to delete dn %s: %s\n", dn, ads_errstr(status)));
3537 SAFE_FREE(host);
3538 ads_memfree(ads, dn);
3539 ads_memfree(ads, hostnameDN);
3540 return status;
3543 ads_memfree(ads, dn);
3546 /* there should be no subordinate objects anymore */
3547 status = ads_do_search_retry(ads, hostnameDN,
3548 LDAP_SCOPE_ONELEVEL,
3549 "(objectclass=*)", attrs, &res);
3551 if (!ADS_ERR_OK(status) || ( (ads_count_replies(ads, res)) > 0 ) ) {
3552 SAFE_FREE(host);
3553 ads_memfree(ads, hostnameDN);
3554 return status;
3557 /* delete hostnameDN now */
3558 status = ads_del_dn(ads, hostnameDN);
3559 if (!ADS_ERR_OK(status)) {
3560 SAFE_FREE(host);
3561 DEBUG(3,("failed to delete dn %s: %s\n", hostnameDN, ads_errstr(status)));
3562 ads_memfree(ads, hostnameDN);
3563 return status;
3567 ads_memfree(ads, hostnameDN);
3569 status = ads_find_machine_acct(ads, &res, host);
3570 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
3571 DEBUG(3, ("Failed to remove host account.\n"));
3572 SAFE_FREE(host);
3573 return status;
3576 SAFE_FREE(host);
3577 return status;
3581 * pull all token-sids from an LDAP dn
3582 * @param ads connection to ads server
3583 * @param mem_ctx TALLOC_CTX for allocating sid array
3584 * @param dn of LDAP object
3585 * @param user_sid pointer to DOM_SID (objectSid)
3586 * @param primary_group_sid pointer to DOM_SID (self composed)
3587 * @param sids pointer to sid array to allocate
3588 * @param num_sids counter of SIDs pulled
3589 * @return status of token query
3591 ADS_STATUS ads_get_tokensids(ADS_STRUCT *ads,
3592 TALLOC_CTX *mem_ctx,
3593 const char *dn,
3594 DOM_SID *user_sid,
3595 DOM_SID *primary_group_sid,
3596 DOM_SID **sids,
3597 size_t *num_sids)
3599 ADS_STATUS status;
3600 LDAPMessage *res = NULL;
3601 int count = 0;
3602 size_t tmp_num_sids;
3603 DOM_SID *tmp_sids;
3604 DOM_SID tmp_user_sid;
3605 DOM_SID tmp_primary_group_sid;
3606 uint32 pgid;
3607 const char *attrs[] = {
3608 "objectSid",
3609 "tokenGroups",
3610 "primaryGroupID",
3611 NULL
3614 status = ads_search_retry_dn(ads, &res, dn, attrs);
3615 if (!ADS_ERR_OK(status)) {
3616 return status;
3619 count = ads_count_replies(ads, res);
3620 if (count != 1) {
3621 ads_msgfree(ads, res);
3622 return ADS_ERROR_LDAP(LDAP_NO_SUCH_OBJECT);
3625 if (!ads_pull_sid(ads, res, "objectSid", &tmp_user_sid)) {
3626 ads_msgfree(ads, res);
3627 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3630 if (!ads_pull_uint32(ads, res, "primaryGroupID", &pgid)) {
3631 ads_msgfree(ads, res);
3632 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3636 /* hack to compose the primary group sid without knowing the
3637 * domsid */
3639 DOM_SID domsid;
3640 uint32 dummy_rid;
3642 sid_copy(&domsid, &tmp_user_sid);
3644 if (!sid_split_rid(&domsid, &dummy_rid)) {
3645 ads_msgfree(ads, res);
3646 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3649 if (!sid_compose(&tmp_primary_group_sid, &domsid, pgid)) {
3650 ads_msgfree(ads, res);
3651 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3655 tmp_num_sids = ads_pull_sids(ads, mem_ctx, res, "tokenGroups", &tmp_sids);
3657 if (tmp_num_sids == 0 || !tmp_sids) {
3658 ads_msgfree(ads, res);
3659 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3662 if (num_sids) {
3663 *num_sids = tmp_num_sids;
3666 if (sids) {
3667 *sids = tmp_sids;
3670 if (user_sid) {
3671 *user_sid = tmp_user_sid;
3674 if (primary_group_sid) {
3675 *primary_group_sid = tmp_primary_group_sid;
3678 DEBUG(10,("ads_get_tokensids: returned %d sids\n", (int)tmp_num_sids + 2));
3680 ads_msgfree(ads, res);
3681 return ADS_ERROR_LDAP(LDAP_SUCCESS);
3685 * Find a sAMAccoutName in LDAP
3686 * @param ads connection to ads server
3687 * @param mem_ctx TALLOC_CTX for allocating sid array
3688 * @param samaccountname to search
3689 * @param uac_ret uint32 pointer userAccountControl attribute value
3690 * @param dn_ret pointer to dn
3691 * @return status of token query
3693 ADS_STATUS ads_find_samaccount(ADS_STRUCT *ads,
3694 TALLOC_CTX *mem_ctx,
3695 const char *samaccountname,
3696 uint32 *uac_ret,
3697 const char **dn_ret)
3699 ADS_STATUS status;
3700 const char *attrs[] = { "userAccountControl", NULL };
3701 const char *filter;
3702 LDAPMessage *res = NULL;
3703 char *dn = NULL;
3704 uint32 uac = 0;
3706 filter = talloc_asprintf(mem_ctx, "(&(objectclass=user)(sAMAccountName=%s))",
3707 samaccountname);
3708 if (filter == NULL) {
3709 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
3710 goto out;
3713 status = ads_do_search_all(ads, ads->config.bind_path,
3714 LDAP_SCOPE_SUBTREE,
3715 filter, attrs, &res);
3717 if (!ADS_ERR_OK(status)) {
3718 goto out;
3721 if (ads_count_replies(ads, res) != 1) {
3722 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3723 goto out;
3726 dn = ads_get_dn(ads, res);
3727 if (dn == NULL) {
3728 status = ADS_ERROR(LDAP_NO_MEMORY);
3729 goto out;
3732 if (!ads_pull_uint32(ads, res, "userAccountControl", &uac)) {
3733 status = ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
3734 goto out;
3737 if (uac_ret) {
3738 *uac_ret = uac;
3741 if (dn_ret) {
3742 *dn_ret = talloc_strdup(mem_ctx, dn);
3743 if (!*dn_ret) {
3744 status = ADS_ERROR(LDAP_NO_MEMORY);
3745 goto out;
3748 out:
3749 ads_memfree(ads, dn);
3750 ads_msgfree(ads, res);
3752 return status;
3756 * find our configuration path
3757 * @param ads connection to ads server
3758 * @param mem_ctx Pointer to talloc context
3759 * @param config_path Pointer to the config path
3760 * @return status of search
3762 ADS_STATUS ads_config_path(ADS_STRUCT *ads,
3763 TALLOC_CTX *mem_ctx,
3764 char **config_path)
3766 ADS_STATUS status;
3767 LDAPMessage *res = NULL;
3768 const char *config_context = NULL;
3769 const char *attrs[] = { "configurationNamingContext", NULL };
3771 status = ads_do_search(ads, "", LDAP_SCOPE_BASE,
3772 "(objectclass=*)", attrs, &res);
3773 if (!ADS_ERR_OK(status)) {
3774 return status;
3777 config_context = ads_pull_string(ads, mem_ctx, res,
3778 "configurationNamingContext");
3779 ads_msgfree(ads, res);
3780 if (!config_context) {
3781 return ADS_ERROR(LDAP_NO_MEMORY);
3784 if (config_path) {
3785 *config_path = talloc_strdup(mem_ctx, config_context);
3786 if (!*config_path) {
3787 return ADS_ERROR(LDAP_NO_MEMORY);
3791 return ADS_ERROR(LDAP_SUCCESS);
3795 * find the displayName of an extended right
3796 * @param ads connection to ads server
3797 * @param config_path The config path
3798 * @param mem_ctx Pointer to talloc context
3799 * @param GUID struct of the rightsGUID
3800 * @return status of search
3802 const char *ads_get_extended_right_name_by_guid(ADS_STRUCT *ads,
3803 const char *config_path,
3804 TALLOC_CTX *mem_ctx,
3805 const struct GUID *rights_guid)
3807 ADS_STATUS rc;
3808 LDAPMessage *res = NULL;
3809 char *expr = NULL;
3810 const char *attrs[] = { "displayName", NULL };
3811 const char *result = NULL;
3812 const char *path;
3814 if (!ads || !mem_ctx || !rights_guid) {
3815 goto done;
3818 expr = talloc_asprintf(mem_ctx, "(rightsGuid=%s)",
3819 GUID_string(mem_ctx, rights_guid));
3820 if (!expr) {
3821 goto done;
3824 path = talloc_asprintf(mem_ctx, "cn=Extended-Rights,%s", config_path);
3825 if (!path) {
3826 goto done;
3829 rc = ads_do_search_retry(ads, path, LDAP_SCOPE_SUBTREE,
3830 expr, attrs, &res);
3831 if (!ADS_ERR_OK(rc)) {
3832 goto done;
3835 if (ads_count_replies(ads, res) != 1) {
3836 goto done;
3839 result = ads_pull_string(ads, mem_ctx, res, "displayName");
3841 done:
3842 ads_msgfree(ads, res);
3843 return result;
3848 * verify or build and verify an account ou
3849 * @param mem_ctx Pointer to talloc context
3850 * @param ads connection to ads server
3851 * @param account_ou
3852 * @return status of search
3855 ADS_STATUS ads_check_ou_dn(TALLOC_CTX *mem_ctx,
3856 ADS_STRUCT *ads,
3857 const char **account_ou)
3859 struct ldb_dn *name_dn = NULL;
3860 const char *name = NULL;
3861 char *ou_string = NULL;
3863 name_dn = ldb_dn_explode(mem_ctx, *account_ou);
3864 if (name_dn) {
3865 return ADS_SUCCESS;
3868 ou_string = ads_ou_string(ads, *account_ou);
3869 if (!ou_string) {
3870 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX);
3873 name = talloc_asprintf(mem_ctx, "%s,%s", ou_string,
3874 ads->config.bind_path);
3875 SAFE_FREE(ou_string);
3876 if (!name) {
3877 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3880 name_dn = ldb_dn_explode(mem_ctx, name);
3881 if (!name_dn) {
3882 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX);
3885 *account_ou = talloc_strdup(mem_ctx, name);
3886 if (!*account_ou) {
3887 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3890 return ADS_SUCCESS;
3893 #endif