s3-utils/net_rpc_printer.c: print more info on write error
[Samba/gebeck_regimport.git] / source3 / libads / ldap.c
blob128c133760069a575cbf90b046df1f21bb37043b
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 "ads.h"
26 #include "libads/sitename_cache.h"
27 #include "libads/cldap.h"
28 #include "libads/dns.h"
29 #include "../libds/common/flags.h"
30 #include "smbldap.h"
31 #include "../libcli/security/security.h"
33 #ifdef HAVE_LDAP
35 /**
36 * @file ldap.c
37 * @brief basic ldap client-side routines for ads server communications
39 * The routines contained here should do the necessary ldap calls for
40 * ads setups.
42 * Important note: attribute names passed into ads_ routines must
43 * already be in UTF-8 format. We do not convert them because in almost
44 * all cases, they are just ascii (which is represented with the same
45 * codepoints in UTF-8). This may have to change at some point
46 **/
49 #define LDAP_SERVER_TREE_DELETE_OID "1.2.840.113556.1.4.805"
51 static SIG_ATOMIC_T gotalarm;
53 /***************************************************************
54 Signal function to tell us we timed out.
55 ****************************************************************/
57 static void gotalarm_sig(int signum)
59 gotalarm = 1;
62 LDAP *ldap_open_with_timeout(const char *server,
63 struct sockaddr_storage *ss,
64 int port, unsigned int to)
66 LDAP *ldp = NULL;
68 DEBUG(10, ("Opening connection to LDAP server '%s:%d', timeout "
69 "%u seconds\n", server, port, to));
71 #if defined(HAVE_LDAP_INIT_FD) && defined(SOCKET_WRAPPER)
72 /* Only use this private LDAP function if we are in make test,
73 * as this is the best way to get the emulated TCP socket into
74 * OpenLDAP */
75 if (socket_wrapper_dir() != NULL) {
76 int fd, ldap_err;
77 NTSTATUS status;
78 char *uri;
80 status = open_socket_out(ss, port, to, &fd);
82 if (!NT_STATUS_IS_OK(status)) {
83 return NULL;
86 #ifndef LDAP_PROTO_TCP
87 #define LDAP_PROTO_TCP 1
88 #endif
89 uri = talloc_asprintf(talloc_tos(), "ldap://%s:%u", server, port);
90 if (uri == NULL) {
91 return NULL;
93 ldap_err = ldap_init_fd(fd, LDAP_PROTO_TCP, uri, &ldp);
94 talloc_free(uri);
96 if (ldap_err != LDAP_SUCCESS) {
97 return NULL;
99 return ldp;
101 #endif
103 /* Setup timeout */
104 gotalarm = 0;
105 CatchSignal(SIGALRM, gotalarm_sig);
106 alarm(to);
107 /* End setup timeout. */
109 ldp = ldap_open(server, port);
111 if (ldp == NULL) {
112 DEBUG(2,("Could not open connection to LDAP server %s:%d: %s\n",
113 server, port, strerror(errno)));
114 } else {
115 DEBUG(10, ("Connected to LDAP server '%s:%d'\n", server, port));
118 /* Teardown timeout. */
119 CatchSignal(SIGALRM, SIG_IGN);
120 alarm(0);
122 return ldp;
125 static int ldap_search_with_timeout(LDAP *ld,
126 LDAP_CONST char *base,
127 int scope,
128 LDAP_CONST char *filter,
129 char **attrs,
130 int attrsonly,
131 LDAPControl **sctrls,
132 LDAPControl **cctrls,
133 int sizelimit,
134 LDAPMessage **res )
136 struct timeval timeout;
137 int result;
139 /* Setup timeout for the ldap_search_ext_s call - local and remote. */
140 timeout.tv_sec = lp_ldap_timeout();
141 timeout.tv_usec = 0;
143 /* Setup alarm timeout.... Do we need both of these ? JRA. */
144 gotalarm = 0;
145 CatchSignal(SIGALRM, gotalarm_sig);
146 alarm(lp_ldap_timeout());
147 /* End setup timeout. */
149 result = ldap_search_ext_s(ld, base, scope, filter, attrs,
150 attrsonly, sctrls, cctrls, &timeout,
151 sizelimit, res);
153 /* Teardown timeout. */
154 CatchSignal(SIGALRM, SIG_IGN);
155 alarm(0);
157 if (gotalarm != 0)
158 return LDAP_TIMELIMIT_EXCEEDED;
161 * A bug in OpenLDAP means ldap_search_ext_s can return
162 * LDAP_SUCCESS but with a NULL res pointer. Cope with
163 * this. See bug #6279 for details. JRA.
166 if (*res == NULL) {
167 return LDAP_TIMELIMIT_EXCEEDED;
170 return result;
173 /**********************************************
174 Do client and server sitename match ?
175 **********************************************/
177 bool ads_sitename_match(ADS_STRUCT *ads)
179 if (ads->config.server_site_name == NULL &&
180 ads->config.client_site_name == NULL ) {
181 DEBUG(10,("ads_sitename_match: both null\n"));
182 return True;
184 if (ads->config.server_site_name &&
185 ads->config.client_site_name &&
186 strequal(ads->config.server_site_name,
187 ads->config.client_site_name)) {
188 DEBUG(10,("ads_sitename_match: name %s match\n", ads->config.server_site_name));
189 return True;
191 DEBUG(10,("ads_sitename_match: no match between server: %s and client: %s\n",
192 ads->config.server_site_name ? ads->config.server_site_name : "NULL",
193 ads->config.client_site_name ? ads->config.client_site_name : "NULL"));
194 return False;
197 /**********************************************
198 Is this the closest DC ?
199 **********************************************/
201 bool ads_closest_dc(ADS_STRUCT *ads)
203 if (ads->config.flags & NBT_SERVER_CLOSEST) {
204 DEBUG(10,("ads_closest_dc: NBT_SERVER_CLOSEST flag set\n"));
205 return True;
208 /* not sure if this can ever happen */
209 if (ads_sitename_match(ads)) {
210 DEBUG(10,("ads_closest_dc: NBT_SERVER_CLOSEST flag not set but sites match\n"));
211 return True;
214 if (ads->config.client_site_name == NULL) {
215 DEBUG(10,("ads_closest_dc: client belongs to no site\n"));
216 return True;
219 DEBUG(10,("ads_closest_dc: %s is not the closest DC\n",
220 ads->config.ldap_server_name));
222 return False;
227 try a connection to a given ldap server, returning True and setting the servers IP
228 in the ads struct if successful
230 static bool ads_try_connect(ADS_STRUCT *ads, const char *server, bool gc)
232 struct NETLOGON_SAM_LOGON_RESPONSE_EX cldap_reply;
233 TALLOC_CTX *frame = talloc_stackframe();
234 bool ret = false;
235 struct sockaddr_storage ss;
236 char addr[INET6_ADDRSTRLEN];
238 if (!server || !*server) {
239 TALLOC_FREE(frame);
240 return False;
243 if (!resolve_name(server, &ss, 0x20, true)) {
244 DEBUG(5,("ads_try_connect: unable to resolve name %s\n",
245 server ));
246 TALLOC_FREE(frame);
247 return false;
249 print_sockaddr(addr, sizeof(addr), &ss);
251 DEBUG(5,("ads_try_connect: sending CLDAP request to %s (realm: %s)\n",
252 addr, ads->server.realm));
254 ZERO_STRUCT( cldap_reply );
256 if ( !ads_cldap_netlogon_5(frame, &ss, ads->server.realm, &cldap_reply ) ) {
257 DEBUG(3,("ads_try_connect: CLDAP request %s failed.\n", addr));
258 ret = false;
259 goto out;
262 /* Check the CLDAP reply flags */
264 if ( !(cldap_reply.server_type & NBT_SERVER_LDAP) ) {
265 DEBUG(1,("ads_try_connect: %s's CLDAP reply says it is not an LDAP server!\n",
266 addr));
267 ret = false;
268 goto out;
271 /* Fill in the ads->config values */
273 SAFE_FREE(ads->config.realm);
274 SAFE_FREE(ads->config.bind_path);
275 SAFE_FREE(ads->config.ldap_server_name);
276 SAFE_FREE(ads->config.server_site_name);
277 SAFE_FREE(ads->config.client_site_name);
278 SAFE_FREE(ads->server.workgroup);
280 ads->config.flags = cldap_reply.server_type;
281 ads->config.ldap_server_name = SMB_STRDUP(cldap_reply.pdc_dns_name);
282 ads->config.realm = SMB_STRDUP(cldap_reply.dns_domain);
283 strupper_m(ads->config.realm);
284 ads->config.bind_path = ads_build_dn(ads->config.realm);
285 if (*cldap_reply.server_site) {
286 ads->config.server_site_name =
287 SMB_STRDUP(cldap_reply.server_site);
289 if (*cldap_reply.client_site) {
290 ads->config.client_site_name =
291 SMB_STRDUP(cldap_reply.client_site);
293 ads->server.workgroup = SMB_STRDUP(cldap_reply.domain_name);
295 ads->ldap.port = gc ? LDAP_GC_PORT : LDAP_PORT;
296 ads->ldap.ss = ss;
298 /* Store our site name. */
299 sitename_store( cldap_reply.domain_name, cldap_reply.client_site);
300 sitename_store( cldap_reply.dns_domain, cldap_reply.client_site);
302 ret = true;
304 out:
306 TALLOC_FREE(frame);
307 return ret;
310 /**********************************************************************
311 Try to find an AD dc using our internal name resolution routines
312 Try the realm first and then then workgroup name if netbios is not
313 disabled
314 **********************************************************************/
316 static NTSTATUS ads_find_dc(ADS_STRUCT *ads)
318 const char *c_domain;
319 const char *c_realm;
320 int count, i=0;
321 struct ip_service *ip_list;
322 const char *realm;
323 const char *domain;
324 bool got_realm = False;
325 bool use_own_domain = False;
326 char *sitename;
327 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
329 /* if the realm and workgroup are both empty, assume they are ours */
331 /* realm */
332 c_realm = ads->server.realm;
334 if ( !c_realm || !*c_realm ) {
335 /* special case where no realm and no workgroup means our own */
336 if ( !ads->server.workgroup || !*ads->server.workgroup ) {
337 use_own_domain = True;
338 c_realm = lp_realm();
342 if (c_realm && *c_realm)
343 got_realm = True;
345 /* we need to try once with the realm name and fallback to the
346 netbios domain name if we fail (if netbios has not been disabled */
348 if ( !got_realm && !lp_disable_netbios() ) {
349 c_realm = ads->server.workgroup;
350 if (!c_realm || !*c_realm) {
351 if ( use_own_domain )
352 c_realm = lp_workgroup();
356 if ( !c_realm || !*c_realm ) {
357 DEBUG(0,("ads_find_dc: no realm or workgroup! Don't know what to do\n"));
358 return NT_STATUS_INVALID_PARAMETER; /* rather need MISSING_PARAMETER ... */
361 if ( use_own_domain ) {
362 c_domain = lp_workgroup();
363 } else {
364 c_domain = ads->server.workgroup;
367 realm = c_realm;
368 domain = c_domain;
371 * In case of LDAP we use get_dc_name() as that
372 * creates the custom krb5.conf file
374 if (!(ads->auth.flags & ADS_AUTH_NO_BIND)) {
375 fstring srv_name;
376 struct sockaddr_storage ip_out;
378 DEBUG(6,("ads_find_dc: (ldap) looking for %s '%s'\n",
379 (got_realm ? "realm" : "domain"), realm));
381 if (get_dc_name(domain, realm, srv_name, &ip_out)) {
383 * we call ads_try_connect() to fill in the
384 * ads->config details
386 if (ads_try_connect(ads, srv_name, false)) {
387 return NT_STATUS_OK;
391 return NT_STATUS_NO_LOGON_SERVERS;
394 sitename = sitename_fetch(realm);
396 again:
398 DEBUG(6,("ads_find_dc: (cldap) looking for %s '%s'\n",
399 (got_realm ? "realm" : "domain"), realm));
401 status = get_sorted_dc_list(realm, sitename, &ip_list, &count, got_realm);
402 if (!NT_STATUS_IS_OK(status)) {
403 /* fall back to netbios if we can */
404 if ( got_realm && !lp_disable_netbios() ) {
405 got_realm = False;
406 goto again;
409 SAFE_FREE(sitename);
410 return status;
413 /* if we fail this loop, then giveup since all the IP addresses returned were dead */
414 for ( i=0; i<count; i++ ) {
415 char server[INET6_ADDRSTRLEN];
417 print_sockaddr(server, sizeof(server), &ip_list[i].ss);
419 if ( !NT_STATUS_IS_OK(check_negative_conn_cache(realm, server)) )
420 continue;
422 if (!got_realm) {
423 /* realm in this case is a workgroup name. We need
424 to ignore any IP addresses in the negative connection
425 cache that match ip addresses returned in the ad realm
426 case. It sucks that I have to reproduce the logic above... */
427 c_realm = ads->server.realm;
428 if ( !c_realm || !*c_realm ) {
429 if ( !ads->server.workgroup || !*ads->server.workgroup ) {
430 c_realm = lp_realm();
433 if (c_realm && *c_realm &&
434 !NT_STATUS_IS_OK(check_negative_conn_cache(c_realm, server))) {
435 /* Ensure we add the workgroup name for this
436 IP address as negative too. */
437 add_failed_connection_entry( realm, server, NT_STATUS_UNSUCCESSFUL );
438 continue;
442 if ( ads_try_connect(ads, server, false) ) {
443 SAFE_FREE(ip_list);
444 SAFE_FREE(sitename);
445 return NT_STATUS_OK;
448 /* keep track of failures */
449 add_failed_connection_entry( realm, server, NT_STATUS_UNSUCCESSFUL );
452 SAFE_FREE(ip_list);
454 /* In case we failed to contact one of our closest DC on our site we
455 * need to try to find another DC, retry with a site-less SRV DNS query
456 * - Guenther */
458 if (sitename) {
459 DEBUG(1,("ads_find_dc: failed to find a valid DC on our site (%s), "
460 "trying to find another DC\n", sitename));
461 SAFE_FREE(sitename);
462 namecache_delete(realm, 0x1C);
463 goto again;
466 return NT_STATUS_NO_LOGON_SERVERS;
469 /*********************************************************************
470 *********************************************************************/
472 static NTSTATUS ads_lookup_site(void)
474 ADS_STRUCT *ads = NULL;
475 ADS_STATUS ads_status;
476 NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
478 ads = ads_init(lp_realm(), NULL, NULL);
479 if (!ads) {
480 return NT_STATUS_NO_MEMORY;
483 /* The NO_BIND here will find a DC and set the client site
484 but not establish the TCP connection */
486 ads->auth.flags = ADS_AUTH_NO_BIND;
487 ads_status = ads_connect(ads);
488 if (!ADS_ERR_OK(ads_status)) {
489 DEBUG(4, ("ads_lookup_site: ads_connect to our realm failed! (%s)\n",
490 ads_errstr(ads_status)));
492 nt_status = ads_ntstatus(ads_status);
494 if (ads) {
495 ads_destroy(&ads);
498 return nt_status;
501 /*********************************************************************
502 *********************************************************************/
504 static const char* host_dns_domain(const char *fqdn)
506 const char *p = fqdn;
508 /* go to next char following '.' */
510 if ((p = strchr_m(fqdn, '.')) != NULL) {
511 p++;
514 return p;
519 * Connect to the Global Catalog server
520 * @param ads Pointer to an existing ADS_STRUCT
521 * @return status of connection
523 * Simple wrapper around ads_connect() that fills in the
524 * GC ldap server information
527 ADS_STATUS ads_connect_gc(ADS_STRUCT *ads)
529 TALLOC_CTX *frame = talloc_stackframe();
530 struct dns_rr_srv *gcs_list;
531 int num_gcs;
532 const char *realm = ads->server.realm;
533 NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
534 ADS_STATUS ads_status = ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
535 int i;
536 bool done = false;
537 char *sitename = NULL;
539 if (!realm)
540 realm = lp_realm();
542 if ((sitename = sitename_fetch(realm)) == NULL) {
543 ads_lookup_site();
544 sitename = sitename_fetch(realm);
547 do {
548 /* We try once with a sitename and once without
549 (unless we don't have a sitename and then we're
550 done */
552 if (sitename == NULL)
553 done = true;
555 nt_status = ads_dns_query_gcs(frame, realm, sitename,
556 &gcs_list, &num_gcs);
558 SAFE_FREE(sitename);
560 if (!NT_STATUS_IS_OK(nt_status)) {
561 ads_status = ADS_ERROR_NT(nt_status);
562 goto done;
565 /* Loop until we get a successful connection or have gone
566 through them all. When connecting a GC server, make sure that
567 the realm is the server's DNS name and not the forest root */
569 for (i=0; i<num_gcs; i++) {
570 ads->server.gc = true;
571 ads->server.ldap_server = SMB_STRDUP(gcs_list[i].hostname);
572 ads->server.realm = SMB_STRDUP(host_dns_domain(ads->server.ldap_server));
573 ads_status = ads_connect(ads);
574 if (ADS_ERR_OK(ads_status)) {
575 /* Reset the bind_dn to "". A Global Catalog server
576 may host multiple domain trees in a forest.
577 Windows 2003 GC server will accept "" as the search
578 path to imply search all domain trees in the forest */
580 SAFE_FREE(ads->config.bind_path);
581 ads->config.bind_path = SMB_STRDUP("");
584 goto done;
586 SAFE_FREE(ads->server.ldap_server);
587 SAFE_FREE(ads->server.realm);
590 TALLOC_FREE(gcs_list);
591 num_gcs = 0;
592 } while (!done);
594 done:
595 SAFE_FREE(sitename);
596 talloc_destroy(frame);
598 return ads_status;
603 * Connect to the LDAP server
604 * @param ads Pointer to an existing ADS_STRUCT
605 * @return status of connection
607 ADS_STATUS ads_connect(ADS_STRUCT *ads)
609 int version = LDAP_VERSION3;
610 ADS_STATUS status;
611 NTSTATUS ntstatus;
612 char addr[INET6_ADDRSTRLEN];
614 ZERO_STRUCT(ads->ldap);
615 ads->ldap.last_attempt = time_mono(NULL);
616 ads->ldap.wrap_type = ADS_SASLWRAP_TYPE_PLAIN;
618 /* try with a user specified server */
620 if (DEBUGLEVEL >= 11) {
621 char *s = NDR_PRINT_STRUCT_STRING(talloc_tos(), ads_struct, ads);
622 DEBUG(11,("ads_connect: entering\n"));
623 DEBUGADD(11,("%s\n", s));
624 TALLOC_FREE(s);
627 if (ads->server.ldap_server)
629 if (ads_try_connect(ads, ads->server.ldap_server, ads->server.gc)) {
630 goto got_connection;
633 /* The choice of which GC use is handled one level up in
634 ads_connect_gc(). If we continue on from here with
635 ads_find_dc() we will get GC searches on port 389 which
636 doesn't work. --jerry */
638 if (ads->server.gc == true) {
639 return ADS_ERROR(LDAP_OPERATIONS_ERROR);
643 ntstatus = ads_find_dc(ads);
644 if (NT_STATUS_IS_OK(ntstatus)) {
645 goto got_connection;
648 status = ADS_ERROR_NT(ntstatus);
649 goto out;
651 got_connection:
653 print_sockaddr(addr, sizeof(addr), &ads->ldap.ss);
654 DEBUG(3,("Successfully contacted LDAP server %s\n", addr));
656 if (!ads->auth.user_name) {
657 /* Must use the userPrincipalName value here or sAMAccountName
658 and not servicePrincipalName; found by Guenther Deschner */
660 if (asprintf(&ads->auth.user_name, "%s$", lp_netbios_name() ) == -1) {
661 DEBUG(0,("ads_connect: asprintf fail.\n"));
662 ads->auth.user_name = NULL;
666 if (!ads->auth.realm) {
667 ads->auth.realm = SMB_STRDUP(ads->config.realm);
670 if (!ads->auth.kdc_server) {
671 print_sockaddr(addr, sizeof(addr), &ads->ldap.ss);
672 ads->auth.kdc_server = SMB_STRDUP(addr);
675 /* If the caller() requested no LDAP bind, then we are done */
677 if (ads->auth.flags & ADS_AUTH_NO_BIND) {
678 status = ADS_SUCCESS;
679 goto out;
682 ads->ldap.mem_ctx = talloc_init("ads LDAP connection memory");
683 if (!ads->ldap.mem_ctx) {
684 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
685 goto out;
688 /* Otherwise setup the TCP LDAP session */
690 ads->ldap.ld = ldap_open_with_timeout(ads->config.ldap_server_name,
691 &ads->ldap.ss,
692 ads->ldap.port, lp_ldap_timeout());
693 if (ads->ldap.ld == NULL) {
694 status = ADS_ERROR(LDAP_OPERATIONS_ERROR);
695 goto out;
697 DEBUG(3,("Connected to LDAP server %s\n", ads->config.ldap_server_name));
699 /* cache the successful connection for workgroup and realm */
700 if (ads_closest_dc(ads)) {
701 saf_store( ads->server.workgroup, ads->config.ldap_server_name);
702 saf_store( ads->server.realm, ads->config.ldap_server_name);
705 ldap_set_option(ads->ldap.ld, LDAP_OPT_PROTOCOL_VERSION, &version);
707 if ( lp_ldap_ssl_ads() ) {
708 status = ADS_ERROR(smb_ldap_start_tls(ads->ldap.ld, version));
709 if (!ADS_ERR_OK(status)) {
710 goto out;
714 /* fill in the current time and offsets */
716 status = ads_current_time( ads );
717 if ( !ADS_ERR_OK(status) ) {
718 goto out;
721 /* Now do the bind */
723 if (ads->auth.flags & ADS_AUTH_ANON_BIND) {
724 status = ADS_ERROR(ldap_simple_bind_s(ads->ldap.ld, NULL, NULL));
725 goto out;
728 if (ads->auth.flags & ADS_AUTH_SIMPLE_BIND) {
729 status = ADS_ERROR(ldap_simple_bind_s(ads->ldap.ld, ads->auth.user_name, ads->auth.password));
730 goto out;
733 status = ads_sasl_bind(ads);
735 out:
736 if (DEBUGLEVEL >= 11) {
737 char *s = NDR_PRINT_STRUCT_STRING(talloc_tos(), ads_struct, ads);
738 DEBUG(11,("ads_connect: leaving with: %s\n",
739 ads_errstr(status)));
740 DEBUGADD(11,("%s\n", s));
741 TALLOC_FREE(s);
744 return status;
748 * Connect to the LDAP server using given credentials
749 * @param ads Pointer to an existing ADS_STRUCT
750 * @return status of connection
752 ADS_STATUS ads_connect_user_creds(ADS_STRUCT *ads)
754 ads->auth.flags |= ADS_AUTH_USER_CREDS;
756 return ads_connect(ads);
760 * Disconnect the LDAP server
761 * @param ads Pointer to an existing ADS_STRUCT
763 void ads_disconnect(ADS_STRUCT *ads)
765 if (ads->ldap.ld) {
766 ldap_unbind(ads->ldap.ld);
767 ads->ldap.ld = NULL;
769 if (ads->ldap.wrap_ops && ads->ldap.wrap_ops->disconnect) {
770 ads->ldap.wrap_ops->disconnect(ads);
772 if (ads->ldap.mem_ctx) {
773 talloc_free(ads->ldap.mem_ctx);
775 ZERO_STRUCT(ads->ldap);
779 Duplicate a struct berval into talloc'ed memory
781 static struct berval *dup_berval(TALLOC_CTX *ctx, const struct berval *in_val)
783 struct berval *value;
785 if (!in_val) return NULL;
787 value = talloc_zero(ctx, struct berval);
788 if (value == NULL)
789 return NULL;
790 if (in_val->bv_len == 0) return value;
792 value->bv_len = in_val->bv_len;
793 value->bv_val = (char *)talloc_memdup(ctx, in_val->bv_val,
794 in_val->bv_len);
795 return value;
799 Make a values list out of an array of (struct berval *)
801 static struct berval **ads_dup_values(TALLOC_CTX *ctx,
802 const struct berval **in_vals)
804 struct berval **values;
805 int i;
807 if (!in_vals) return NULL;
808 for (i=0; in_vals[i]; i++)
809 ; /* count values */
810 values = talloc_zero_array(ctx, struct berval *, i+1);
811 if (!values) return NULL;
813 for (i=0; in_vals[i]; i++) {
814 values[i] = dup_berval(ctx, in_vals[i]);
816 return values;
820 UTF8-encode a values list out of an array of (char *)
822 static char **ads_push_strvals(TALLOC_CTX *ctx, const char **in_vals)
824 char **values;
825 int i;
826 size_t size;
828 if (!in_vals) return NULL;
829 for (i=0; in_vals[i]; i++)
830 ; /* count values */
831 values = talloc_zero_array(ctx, char *, i+1);
832 if (!values) return NULL;
834 for (i=0; in_vals[i]; i++) {
835 if (!push_utf8_talloc(ctx, &values[i], in_vals[i], &size)) {
836 TALLOC_FREE(values);
837 return NULL;
840 return values;
844 Pull a (char *) array out of a UTF8-encoded values list
846 static char **ads_pull_strvals(TALLOC_CTX *ctx, const char **in_vals)
848 char **values;
849 int i;
850 size_t converted_size;
852 if (!in_vals) return NULL;
853 for (i=0; in_vals[i]; i++)
854 ; /* count values */
855 values = talloc_zero_array(ctx, char *, i+1);
856 if (!values) return NULL;
858 for (i=0; in_vals[i]; i++) {
859 if (!pull_utf8_talloc(ctx, &values[i], in_vals[i],
860 &converted_size)) {
861 DEBUG(0,("ads_pull_strvals: pull_utf8_talloc failed: "
862 "%s", strerror(errno)));
865 return values;
869 * Do a search with paged results. cookie must be null on the first
870 * call, and then returned on each subsequent call. It will be null
871 * again when the entire search is complete
872 * @param ads connection to ads server
873 * @param bind_path Base dn for the search
874 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
875 * @param expr Search expression - specified in local charset
876 * @param attrs Attributes to retrieve - specified in utf8 or ascii
877 * @param res ** which will contain results - free res* with ads_msgfree()
878 * @param count Number of entries retrieved on this page
879 * @param cookie The paged results cookie to be returned on subsequent calls
880 * @return status of search
882 static ADS_STATUS ads_do_paged_search_args(ADS_STRUCT *ads,
883 const char *bind_path,
884 int scope, const char *expr,
885 const char **attrs, void *args,
886 LDAPMessage **res,
887 int *count, struct berval **cookie)
889 int rc, i, version;
890 char *utf8_expr, *utf8_path, **search_attrs = NULL;
891 size_t converted_size;
892 LDAPControl PagedResults, NoReferrals, ExternalCtrl, *controls[4], **rcontrols;
893 BerElement *cookie_be = NULL;
894 struct berval *cookie_bv= NULL;
895 BerElement *ext_be = NULL;
896 struct berval *ext_bv= NULL;
898 TALLOC_CTX *ctx;
899 ads_control *external_control = (ads_control *) args;
901 *res = NULL;
903 if (!(ctx = talloc_init("ads_do_paged_search_args")))
904 return ADS_ERROR(LDAP_NO_MEMORY);
906 /* 0 means the conversion worked but the result was empty
907 so we only fail if it's -1. In any case, it always
908 at least nulls out the dest */
909 if (!push_utf8_talloc(ctx, &utf8_expr, expr, &converted_size) ||
910 !push_utf8_talloc(ctx, &utf8_path, bind_path, &converted_size))
912 rc = LDAP_NO_MEMORY;
913 goto done;
916 if (!attrs || !(*attrs))
917 search_attrs = NULL;
918 else {
919 /* This would be the utf8-encoded version...*/
920 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
921 if (!(search_attrs = str_list_copy(talloc_tos(), attrs))) {
922 rc = LDAP_NO_MEMORY;
923 goto done;
927 /* Paged results only available on ldap v3 or later */
928 ldap_get_option(ads->ldap.ld, LDAP_OPT_PROTOCOL_VERSION, &version);
929 if (version < LDAP_VERSION3) {
930 rc = LDAP_NOT_SUPPORTED;
931 goto done;
934 cookie_be = ber_alloc_t(LBER_USE_DER);
935 if (*cookie) {
936 ber_printf(cookie_be, "{iO}", (ber_int_t) 1000, *cookie);
937 ber_bvfree(*cookie); /* don't need it from last time */
938 *cookie = NULL;
939 } else {
940 ber_printf(cookie_be, "{io}", (ber_int_t) 1000, "", 0);
942 ber_flatten(cookie_be, &cookie_bv);
943 PagedResults.ldctl_oid = discard_const_p(char, ADS_PAGE_CTL_OID);
944 PagedResults.ldctl_iscritical = (char) 1;
945 PagedResults.ldctl_value.bv_len = cookie_bv->bv_len;
946 PagedResults.ldctl_value.bv_val = cookie_bv->bv_val;
948 NoReferrals.ldctl_oid = discard_const_p(char, ADS_NO_REFERRALS_OID);
949 NoReferrals.ldctl_iscritical = (char) 0;
950 NoReferrals.ldctl_value.bv_len = 0;
951 NoReferrals.ldctl_value.bv_val = discard_const_p(char, "");
953 if (external_control &&
954 (strequal(external_control->control, ADS_EXTENDED_DN_OID) ||
955 strequal(external_control->control, ADS_SD_FLAGS_OID))) {
957 ExternalCtrl.ldctl_oid = discard_const_p(char, external_control->control);
958 ExternalCtrl.ldctl_iscritical = (char) external_control->critical;
960 /* win2k does not accept a ldctl_value beeing passed in */
962 if (external_control->val != 0) {
964 if ((ext_be = ber_alloc_t(LBER_USE_DER)) == NULL ) {
965 rc = LDAP_NO_MEMORY;
966 goto done;
969 if ((ber_printf(ext_be, "{i}", (ber_int_t) external_control->val)) == -1) {
970 rc = LDAP_NO_MEMORY;
971 goto done;
973 if ((ber_flatten(ext_be, &ext_bv)) == -1) {
974 rc = LDAP_NO_MEMORY;
975 goto done;
978 ExternalCtrl.ldctl_value.bv_len = ext_bv->bv_len;
979 ExternalCtrl.ldctl_value.bv_val = ext_bv->bv_val;
981 } else {
982 ExternalCtrl.ldctl_value.bv_len = 0;
983 ExternalCtrl.ldctl_value.bv_val = NULL;
986 controls[0] = &NoReferrals;
987 controls[1] = &PagedResults;
988 controls[2] = &ExternalCtrl;
989 controls[3] = NULL;
991 } else {
992 controls[0] = &NoReferrals;
993 controls[1] = &PagedResults;
994 controls[2] = NULL;
997 /* we need to disable referrals as the openldap libs don't
998 handle them and paged results at the same time. Using them
999 together results in the result record containing the server
1000 page control being removed from the result list (tridge/jmcd)
1002 leaving this in despite the control that says don't generate
1003 referrals, in case the server doesn't support it (jmcd)
1005 ldap_set_option(ads->ldap.ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
1007 rc = ldap_search_with_timeout(ads->ldap.ld, utf8_path, scope, utf8_expr,
1008 search_attrs, 0, controls,
1009 NULL, LDAP_NO_LIMIT,
1010 (LDAPMessage **)res);
1012 ber_free(cookie_be, 1);
1013 ber_bvfree(cookie_bv);
1015 if (rc) {
1016 DEBUG(3,("ads_do_paged_search_args: ldap_search_with_timeout(%s) -> %s\n", expr,
1017 ldap_err2string(rc)));
1018 goto done;
1021 rc = ldap_parse_result(ads->ldap.ld, *res, NULL, NULL, NULL,
1022 NULL, &rcontrols, 0);
1024 if (!rcontrols) {
1025 goto done;
1028 for (i=0; rcontrols[i]; i++) {
1029 if (strcmp(ADS_PAGE_CTL_OID, rcontrols[i]->ldctl_oid) == 0) {
1030 cookie_be = ber_init(&rcontrols[i]->ldctl_value);
1031 ber_scanf(cookie_be,"{iO}", (ber_int_t *) count,
1032 &cookie_bv);
1033 /* the berval is the cookie, but must be freed when
1034 it is all done */
1035 if (cookie_bv->bv_len) /* still more to do */
1036 *cookie=ber_bvdup(cookie_bv);
1037 else
1038 *cookie=NULL;
1039 ber_bvfree(cookie_bv);
1040 ber_free(cookie_be, 1);
1041 break;
1044 ldap_controls_free(rcontrols);
1046 done:
1047 talloc_destroy(ctx);
1049 if (ext_be) {
1050 ber_free(ext_be, 1);
1053 if (ext_bv) {
1054 ber_bvfree(ext_bv);
1057 /* if/when we decide to utf8-encode attrs, take out this next line */
1058 TALLOC_FREE(search_attrs);
1060 return ADS_ERROR(rc);
1063 static ADS_STATUS ads_do_paged_search(ADS_STRUCT *ads, const char *bind_path,
1064 int scope, const char *expr,
1065 const char **attrs, LDAPMessage **res,
1066 int *count, struct berval **cookie)
1068 return ads_do_paged_search_args(ads, bind_path, scope, expr, attrs, NULL, res, count, cookie);
1073 * Get all results for a search. This uses ads_do_paged_search() to return
1074 * all entries in a large search.
1075 * @param ads connection to ads server
1076 * @param bind_path Base dn for the search
1077 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1078 * @param expr Search expression
1079 * @param attrs Attributes to retrieve
1080 * @param res ** which will contain results - free res* with ads_msgfree()
1081 * @return status of search
1083 ADS_STATUS ads_do_search_all_args(ADS_STRUCT *ads, const char *bind_path,
1084 int scope, const char *expr,
1085 const char **attrs, void *args,
1086 LDAPMessage **res)
1088 struct berval *cookie = NULL;
1089 int count = 0;
1090 ADS_STATUS status;
1092 *res = NULL;
1093 status = ads_do_paged_search_args(ads, bind_path, scope, expr, attrs, args, res,
1094 &count, &cookie);
1096 if (!ADS_ERR_OK(status))
1097 return status;
1099 #ifdef HAVE_LDAP_ADD_RESULT_ENTRY
1100 while (cookie) {
1101 LDAPMessage *res2 = NULL;
1102 ADS_STATUS status2;
1103 LDAPMessage *msg, *next;
1105 status2 = ads_do_paged_search_args(ads, bind_path, scope, expr,
1106 attrs, args, &res2, &count, &cookie);
1108 if (!ADS_ERR_OK(status2)) break;
1110 /* this relies on the way that ldap_add_result_entry() works internally. I hope
1111 that this works on all ldap libs, but I have only tested with openldap */
1112 for (msg = ads_first_message(ads, res2); msg; msg = next) {
1113 next = ads_next_message(ads, msg);
1114 ldap_add_result_entry((LDAPMessage **)res, msg);
1116 /* note that we do not free res2, as the memory is now
1117 part of the main returned list */
1119 #else
1120 DEBUG(0, ("no ldap_add_result_entry() support in LDAP libs!\n"));
1121 status = ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
1122 #endif
1124 return status;
1127 ADS_STATUS ads_do_search_all(ADS_STRUCT *ads, const char *bind_path,
1128 int scope, const char *expr,
1129 const char **attrs, LDAPMessage **res)
1131 return ads_do_search_all_args(ads, bind_path, scope, expr, attrs, NULL, res);
1134 ADS_STATUS ads_do_search_all_sd_flags(ADS_STRUCT *ads, const char *bind_path,
1135 int scope, const char *expr,
1136 const char **attrs, uint32 sd_flags,
1137 LDAPMessage **res)
1139 ads_control args;
1141 args.control = ADS_SD_FLAGS_OID;
1142 args.val = sd_flags;
1143 args.critical = True;
1145 return ads_do_search_all_args(ads, bind_path, scope, expr, attrs, &args, res);
1150 * Run a function on all results for a search. Uses ads_do_paged_search() and
1151 * runs the function as each page is returned, using ads_process_results()
1152 * @param ads connection to ads server
1153 * @param bind_path Base dn for the search
1154 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1155 * @param expr Search expression - specified in local charset
1156 * @param attrs Attributes to retrieve - specified in UTF-8 or ascii
1157 * @param fn Function which takes attr name, values list, and data_area
1158 * @param data_area Pointer which is passed to function on each call
1159 * @return status of search
1161 ADS_STATUS ads_do_search_all_fn(ADS_STRUCT *ads, const char *bind_path,
1162 int scope, const char *expr, const char **attrs,
1163 bool (*fn)(ADS_STRUCT *, char *, void **, void *),
1164 void *data_area)
1166 struct berval *cookie = NULL;
1167 int count = 0;
1168 ADS_STATUS status;
1169 LDAPMessage *res;
1171 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs, &res,
1172 &count, &cookie);
1174 if (!ADS_ERR_OK(status)) return status;
1176 ads_process_results(ads, res, fn, data_area);
1177 ads_msgfree(ads, res);
1179 while (cookie) {
1180 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs,
1181 &res, &count, &cookie);
1183 if (!ADS_ERR_OK(status)) break;
1185 ads_process_results(ads, res, fn, data_area);
1186 ads_msgfree(ads, res);
1189 return status;
1193 * Do a search with a timeout.
1194 * @param ads connection to ads server
1195 * @param bind_path Base dn for the search
1196 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1197 * @param expr Search expression
1198 * @param attrs Attributes to retrieve
1199 * @param res ** which will contain results - free res* with ads_msgfree()
1200 * @return status of search
1202 ADS_STATUS ads_do_search(ADS_STRUCT *ads, const char *bind_path, int scope,
1203 const char *expr,
1204 const char **attrs, LDAPMessage **res)
1206 int rc;
1207 char *utf8_expr, *utf8_path, **search_attrs = NULL;
1208 size_t converted_size;
1209 TALLOC_CTX *ctx;
1211 *res = NULL;
1212 if (!(ctx = talloc_init("ads_do_search"))) {
1213 DEBUG(1,("ads_do_search: talloc_init() failed!"));
1214 return ADS_ERROR(LDAP_NO_MEMORY);
1217 /* 0 means the conversion worked but the result was empty
1218 so we only fail if it's negative. In any case, it always
1219 at least nulls out the dest */
1220 if (!push_utf8_talloc(ctx, &utf8_expr, expr, &converted_size) ||
1221 !push_utf8_talloc(ctx, &utf8_path, bind_path, &converted_size))
1223 DEBUG(1,("ads_do_search: push_utf8_talloc() failed!"));
1224 rc = LDAP_NO_MEMORY;
1225 goto done;
1228 if (!attrs || !(*attrs))
1229 search_attrs = NULL;
1230 else {
1231 /* This would be the utf8-encoded version...*/
1232 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
1233 if (!(search_attrs = str_list_copy(talloc_tos(), attrs)))
1235 DEBUG(1,("ads_do_search: str_list_copy() failed!"));
1236 rc = LDAP_NO_MEMORY;
1237 goto done;
1241 /* see the note in ads_do_paged_search - we *must* disable referrals */
1242 ldap_set_option(ads->ldap.ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
1244 rc = ldap_search_with_timeout(ads->ldap.ld, utf8_path, scope, utf8_expr,
1245 search_attrs, 0, NULL, NULL,
1246 LDAP_NO_LIMIT,
1247 (LDAPMessage **)res);
1249 if (rc == LDAP_SIZELIMIT_EXCEEDED) {
1250 DEBUG(3,("Warning! sizelimit exceeded in ldap. Truncating.\n"));
1251 rc = 0;
1254 done:
1255 talloc_destroy(ctx);
1256 /* if/when we decide to utf8-encode attrs, take out this next line */
1257 TALLOC_FREE(search_attrs);
1258 return ADS_ERROR(rc);
1261 * Do a general ADS search
1262 * @param ads connection to ads server
1263 * @param res ** which will contain results - free res* with ads_msgfree()
1264 * @param expr Search expression
1265 * @param attrs Attributes to retrieve
1266 * @return status of search
1268 ADS_STATUS ads_search(ADS_STRUCT *ads, LDAPMessage **res,
1269 const char *expr, const char **attrs)
1271 return ads_do_search(ads, ads->config.bind_path, LDAP_SCOPE_SUBTREE,
1272 expr, attrs, res);
1276 * Do a search on a specific DistinguishedName
1277 * @param ads connection to ads server
1278 * @param res ** which will contain results - free res* with ads_msgfree()
1279 * @param dn DistinguishName to search
1280 * @param attrs Attributes to retrieve
1281 * @return status of search
1283 ADS_STATUS ads_search_dn(ADS_STRUCT *ads, LDAPMessage **res,
1284 const char *dn, const char **attrs)
1286 return ads_do_search(ads, dn, LDAP_SCOPE_BASE, "(objectclass=*)",
1287 attrs, res);
1291 * Free up memory from a ads_search
1292 * @param ads connection to ads server
1293 * @param msg Search results to free
1295 void ads_msgfree(ADS_STRUCT *ads, LDAPMessage *msg)
1297 if (!msg) return;
1298 ldap_msgfree(msg);
1302 * Get a dn from search results
1303 * @param ads connection to ads server
1304 * @param msg Search result
1305 * @return dn string
1307 char *ads_get_dn(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, LDAPMessage *msg)
1309 char *utf8_dn, *unix_dn;
1310 size_t converted_size;
1312 utf8_dn = ldap_get_dn(ads->ldap.ld, msg);
1314 if (!utf8_dn) {
1315 DEBUG (5, ("ads_get_dn: ldap_get_dn failed\n"));
1316 return NULL;
1319 if (!pull_utf8_talloc(mem_ctx, &unix_dn, utf8_dn, &converted_size)) {
1320 DEBUG(0,("ads_get_dn: string conversion failure utf8 [%s]\n",
1321 utf8_dn ));
1322 return NULL;
1324 ldap_memfree(utf8_dn);
1325 return unix_dn;
1329 * Get the parent from a dn
1330 * @param dn the dn to return the parent from
1331 * @return parent dn string
1333 char *ads_parent_dn(const char *dn)
1335 char *p;
1337 if (dn == NULL) {
1338 return NULL;
1341 p = strchr(dn, ',');
1343 if (p == NULL) {
1344 return NULL;
1347 return p+1;
1351 * Find a machine account given a hostname
1352 * @param ads connection to ads server
1353 * @param res ** which will contain results - free res* with ads_msgfree()
1354 * @param host Hostname to search for
1355 * @return status of search
1357 ADS_STATUS ads_find_machine_acct(ADS_STRUCT *ads, LDAPMessage **res,
1358 const char *machine)
1360 ADS_STATUS status;
1361 char *expr;
1362 const char *attrs[] = {"*", "nTSecurityDescriptor", NULL};
1364 *res = NULL;
1366 /* the easiest way to find a machine account anywhere in the tree
1367 is to look for hostname$ */
1368 if (asprintf(&expr, "(samAccountName=%s$)", machine) == -1) {
1369 DEBUG(1, ("asprintf failed!\n"));
1370 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1373 status = ads_search(ads, res, expr, attrs);
1374 SAFE_FREE(expr);
1375 return status;
1379 * Initialize a list of mods to be used in a modify request
1380 * @param ctx An initialized TALLOC_CTX
1381 * @return allocated ADS_MODLIST
1383 ADS_MODLIST ads_init_mods(TALLOC_CTX *ctx)
1385 #define ADS_MODLIST_ALLOC_SIZE 10
1386 LDAPMod **mods;
1388 if ((mods = talloc_zero_array(ctx, LDAPMod *, ADS_MODLIST_ALLOC_SIZE + 1)))
1389 /* -1 is safety to make sure we don't go over the end.
1390 need to reset it to NULL before doing ldap modify */
1391 mods[ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1393 return (ADS_MODLIST)mods;
1398 add an attribute to the list, with values list already constructed
1400 static ADS_STATUS ads_modlist_add(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1401 int mod_op, const char *name,
1402 const void *_invals)
1404 const void **invals = (const void **)_invals;
1405 int curmod;
1406 LDAPMod **modlist = (LDAPMod **) *mods;
1407 struct berval **ber_values = NULL;
1408 char **char_values = NULL;
1410 if (!invals) {
1411 mod_op = LDAP_MOD_DELETE;
1412 } else {
1413 if (mod_op & LDAP_MOD_BVALUES)
1414 ber_values = ads_dup_values(ctx,
1415 (const struct berval **)invals);
1416 else
1417 char_values = ads_push_strvals(ctx,
1418 (const char **) invals);
1421 /* find the first empty slot */
1422 for (curmod=0; modlist[curmod] && modlist[curmod] != (LDAPMod *) -1;
1423 curmod++);
1424 if (modlist[curmod] == (LDAPMod *) -1) {
1425 if (!(modlist = talloc_realloc(ctx, modlist, LDAPMod *,
1426 curmod+ADS_MODLIST_ALLOC_SIZE+1)))
1427 return ADS_ERROR(LDAP_NO_MEMORY);
1428 memset(&modlist[curmod], 0,
1429 ADS_MODLIST_ALLOC_SIZE*sizeof(LDAPMod *));
1430 modlist[curmod+ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1431 *mods = (ADS_MODLIST)modlist;
1434 if (!(modlist[curmod] = talloc_zero(ctx, LDAPMod)))
1435 return ADS_ERROR(LDAP_NO_MEMORY);
1436 modlist[curmod]->mod_type = talloc_strdup(ctx, name);
1437 if (mod_op & LDAP_MOD_BVALUES) {
1438 modlist[curmod]->mod_bvalues = ber_values;
1439 } else if (mod_op & LDAP_MOD_DELETE) {
1440 modlist[curmod]->mod_values = NULL;
1441 } else {
1442 modlist[curmod]->mod_values = char_values;
1445 modlist[curmod]->mod_op = mod_op;
1446 return ADS_ERROR(LDAP_SUCCESS);
1450 * Add a single string value to a mod list
1451 * @param ctx An initialized TALLOC_CTX
1452 * @param mods An initialized ADS_MODLIST
1453 * @param name The attribute name to add
1454 * @param val The value to add - NULL means DELETE
1455 * @return ADS STATUS indicating success of add
1457 ADS_STATUS ads_mod_str(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1458 const char *name, const char *val)
1460 const char *values[2];
1462 values[0] = val;
1463 values[1] = NULL;
1465 if (!val)
1466 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1467 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE, name, values);
1471 * Add an array of string values to a mod list
1472 * @param ctx An initialized TALLOC_CTX
1473 * @param mods An initialized ADS_MODLIST
1474 * @param name The attribute name to add
1475 * @param vals The array of string values to add - NULL means DELETE
1476 * @return ADS STATUS indicating success of add
1478 ADS_STATUS ads_mod_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1479 const char *name, const char **vals)
1481 if (!vals)
1482 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1483 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE,
1484 name, (const void **) vals);
1487 #if 0
1489 * Add a single ber-encoded value to a mod list
1490 * @param ctx An initialized TALLOC_CTX
1491 * @param mods An initialized ADS_MODLIST
1492 * @param name The attribute name to add
1493 * @param val The value to add - NULL means DELETE
1494 * @return ADS STATUS indicating success of add
1496 static ADS_STATUS ads_mod_ber(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1497 const char *name, const struct berval *val)
1499 const struct berval *values[2];
1501 values[0] = val;
1502 values[1] = NULL;
1503 if (!val)
1504 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1505 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE|LDAP_MOD_BVALUES,
1506 name, (const void **) values);
1508 #endif
1511 * Perform an ldap modify
1512 * @param ads connection to ads server
1513 * @param mod_dn DistinguishedName to modify
1514 * @param mods list of modifications to perform
1515 * @return status of modify
1517 ADS_STATUS ads_gen_mod(ADS_STRUCT *ads, const char *mod_dn, ADS_MODLIST mods)
1519 int ret,i;
1520 char *utf8_dn = NULL;
1521 size_t converted_size;
1523 this control is needed to modify that contains a currently
1524 non-existent attribute (but allowable for the object) to run
1526 LDAPControl PermitModify = {
1527 discard_const_p(char, ADS_PERMIT_MODIFY_OID),
1528 {0, NULL},
1529 (char) 1};
1530 LDAPControl *controls[2];
1532 controls[0] = &PermitModify;
1533 controls[1] = NULL;
1535 if (!push_utf8_talloc(talloc_tos(), &utf8_dn, mod_dn, &converted_size)) {
1536 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1539 /* find the end of the list, marked by NULL or -1 */
1540 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1541 /* make sure the end of the list is NULL */
1542 mods[i] = NULL;
1543 ret = ldap_modify_ext_s(ads->ldap.ld, utf8_dn,
1544 (LDAPMod **) mods, controls, NULL);
1545 TALLOC_FREE(utf8_dn);
1546 return ADS_ERROR(ret);
1550 * Perform an ldap add
1551 * @param ads connection to ads server
1552 * @param new_dn DistinguishedName to add
1553 * @param mods list of attributes and values for DN
1554 * @return status of add
1556 ADS_STATUS ads_gen_add(ADS_STRUCT *ads, const char *new_dn, ADS_MODLIST mods)
1558 int ret, i;
1559 char *utf8_dn = NULL;
1560 size_t converted_size;
1562 if (!push_utf8_talloc(talloc_tos(), &utf8_dn, new_dn, &converted_size)) {
1563 DEBUG(1, ("ads_gen_add: push_utf8_talloc failed!"));
1564 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1567 /* find the end of the list, marked by NULL or -1 */
1568 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1569 /* make sure the end of the list is NULL */
1570 mods[i] = NULL;
1572 ret = ldap_add_s(ads->ldap.ld, utf8_dn, (LDAPMod**)mods);
1573 TALLOC_FREE(utf8_dn);
1574 return ADS_ERROR(ret);
1578 * Delete a DistinguishedName
1579 * @param ads connection to ads server
1580 * @param new_dn DistinguishedName to delete
1581 * @return status of delete
1583 ADS_STATUS ads_del_dn(ADS_STRUCT *ads, char *del_dn)
1585 int ret;
1586 char *utf8_dn = NULL;
1587 size_t converted_size;
1588 if (!push_utf8_talloc(talloc_tos(), &utf8_dn, del_dn, &converted_size)) {
1589 DEBUG(1, ("ads_del_dn: push_utf8_talloc failed!"));
1590 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1593 ret = ldap_delete_s(ads->ldap.ld, utf8_dn);
1594 TALLOC_FREE(utf8_dn);
1595 return ADS_ERROR(ret);
1599 * Build an org unit string
1600 * if org unit is Computers or blank then assume a container, otherwise
1601 * assume a / separated list of organisational units.
1602 * jmcd: '\' is now used for escapes so certain chars can be in the ou (e.g. #)
1603 * @param ads connection to ads server
1604 * @param org_unit Organizational unit
1605 * @return org unit string - caller must free
1607 char *ads_ou_string(ADS_STRUCT *ads, const char *org_unit)
1609 char *ret = NULL;
1611 if (!org_unit || !*org_unit) {
1613 ret = ads_default_ou_string(ads, DS_GUID_COMPUTERS_CONTAINER);
1615 /* samba4 might not yet respond to a wellknownobject-query */
1616 return ret ? ret : SMB_STRDUP("cn=Computers");
1619 if (strequal(org_unit, "Computers")) {
1620 return SMB_STRDUP("cn=Computers");
1623 /* jmcd: removed "\\" from the separation chars, because it is
1624 needed as an escape for chars like '#' which are valid in an
1625 OU name */
1626 return ads_build_path(org_unit, "/", "ou=", 1);
1630 * Get a org unit string for a well-known GUID
1631 * @param ads connection to ads server
1632 * @param wknguid Well known GUID
1633 * @return org unit string - caller must free
1635 char *ads_default_ou_string(ADS_STRUCT *ads, const char *wknguid)
1637 ADS_STATUS status;
1638 LDAPMessage *res = NULL;
1639 char *base, *wkn_dn = NULL, *ret = NULL, **wkn_dn_exp = NULL,
1640 **bind_dn_exp = NULL;
1641 const char *attrs[] = {"distinguishedName", NULL};
1642 int new_ln, wkn_ln, bind_ln, i;
1644 if (wknguid == NULL) {
1645 return NULL;
1648 if (asprintf(&base, "<WKGUID=%s,%s>", wknguid, ads->config.bind_path ) == -1) {
1649 DEBUG(1, ("asprintf failed!\n"));
1650 return NULL;
1653 status = ads_search_dn(ads, &res, base, attrs);
1654 if (!ADS_ERR_OK(status)) {
1655 DEBUG(1,("Failed while searching for: %s\n", base));
1656 goto out;
1659 if (ads_count_replies(ads, res) != 1) {
1660 goto out;
1663 /* substitute the bind-path from the well-known-guid-search result */
1664 wkn_dn = ads_get_dn(ads, talloc_tos(), res);
1665 if (!wkn_dn) {
1666 goto out;
1669 wkn_dn_exp = ldap_explode_dn(wkn_dn, 0);
1670 if (!wkn_dn_exp) {
1671 goto out;
1674 bind_dn_exp = ldap_explode_dn(ads->config.bind_path, 0);
1675 if (!bind_dn_exp) {
1676 goto out;
1679 for (wkn_ln=0; wkn_dn_exp[wkn_ln]; wkn_ln++)
1681 for (bind_ln=0; bind_dn_exp[bind_ln]; bind_ln++)
1684 new_ln = wkn_ln - bind_ln;
1686 ret = SMB_STRDUP(wkn_dn_exp[0]);
1687 if (!ret) {
1688 goto out;
1691 for (i=1; i < new_ln; i++) {
1692 char *s = NULL;
1694 if (asprintf(&s, "%s,%s", ret, wkn_dn_exp[i]) == -1) {
1695 SAFE_FREE(ret);
1696 goto out;
1699 SAFE_FREE(ret);
1700 ret = SMB_STRDUP(s);
1701 free(s);
1702 if (!ret) {
1703 goto out;
1707 out:
1708 SAFE_FREE(base);
1709 ads_msgfree(ads, res);
1710 TALLOC_FREE(wkn_dn);
1711 if (wkn_dn_exp) {
1712 ldap_value_free(wkn_dn_exp);
1714 if (bind_dn_exp) {
1715 ldap_value_free(bind_dn_exp);
1718 return ret;
1722 * Adds (appends) an item to an attribute array, rather then
1723 * replacing the whole list
1724 * @param ctx An initialized TALLOC_CTX
1725 * @param mods An initialized ADS_MODLIST
1726 * @param name name of the ldap attribute to append to
1727 * @param vals an array of values to add
1728 * @return status of addition
1731 ADS_STATUS ads_add_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1732 const char *name, const char **vals)
1734 return ads_modlist_add(ctx, mods, LDAP_MOD_ADD, name,
1735 (const void *) vals);
1739 * Determines the an account's current KVNO via an LDAP lookup
1740 * @param ads An initialized ADS_STRUCT
1741 * @param account_name the NT samaccountname.
1742 * @return the kvno for the account, or -1 in case of a failure.
1745 uint32 ads_get_kvno(ADS_STRUCT *ads, const char *account_name)
1747 LDAPMessage *res = NULL;
1748 uint32 kvno = (uint32)-1; /* -1 indicates a failure */
1749 char *filter;
1750 const char *attrs[] = {"msDS-KeyVersionNumber", NULL};
1751 char *dn_string = NULL;
1752 ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS);
1754 DEBUG(5,("ads_get_kvno: Searching for account %s\n", account_name));
1755 if (asprintf(&filter, "(samAccountName=%s)", account_name) == -1) {
1756 return kvno;
1758 ret = ads_search(ads, &res, filter, attrs);
1759 SAFE_FREE(filter);
1760 if (!ADS_ERR_OK(ret) || (ads_count_replies(ads, res) != 1)) {
1761 DEBUG(1,("ads_get_kvno: Account for %s not found.\n", account_name));
1762 ads_msgfree(ads, res);
1763 return kvno;
1766 dn_string = ads_get_dn(ads, talloc_tos(), res);
1767 if (!dn_string) {
1768 DEBUG(0,("ads_get_kvno: out of memory.\n"));
1769 ads_msgfree(ads, res);
1770 return kvno;
1772 DEBUG(5,("ads_get_kvno: Using: %s\n", dn_string));
1773 TALLOC_FREE(dn_string);
1775 /* ---------------------------------------------------------
1776 * 0 is returned as a default KVNO from this point on...
1777 * This is done because Windows 2000 does not support key
1778 * version numbers. Chances are that a failure in the next
1779 * step is simply due to Windows 2000 being used for a
1780 * domain controller. */
1781 kvno = 0;
1783 if (!ads_pull_uint32(ads, res, "msDS-KeyVersionNumber", &kvno)) {
1784 DEBUG(3,("ads_get_kvno: Error Determining KVNO!\n"));
1785 DEBUG(3,("ads_get_kvno: Windows 2000 does not support KVNO's, so this may be normal.\n"));
1786 ads_msgfree(ads, res);
1787 return kvno;
1790 /* Success */
1791 DEBUG(5,("ads_get_kvno: Looked Up KVNO of: %d\n", kvno));
1792 ads_msgfree(ads, res);
1793 return kvno;
1797 * Determines the computer account's current KVNO via an LDAP lookup
1798 * @param ads An initialized ADS_STRUCT
1799 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1800 * @return the kvno for the computer account, or -1 in case of a failure.
1803 uint32_t ads_get_machine_kvno(ADS_STRUCT *ads, const char *machine_name)
1805 char *computer_account = NULL;
1806 uint32_t kvno = -1;
1808 if (asprintf(&computer_account, "%s$", machine_name) < 0) {
1809 return kvno;
1812 kvno = ads_get_kvno(ads, computer_account);
1813 free(computer_account);
1815 return kvno;
1819 * This clears out all registered spn's for a given hostname
1820 * @param ads An initilaized ADS_STRUCT
1821 * @param machine_name the NetBIOS name of the computer.
1822 * @return 0 upon success, non-zero otherwise.
1825 ADS_STATUS ads_clear_service_principal_names(ADS_STRUCT *ads, const char *machine_name)
1827 TALLOC_CTX *ctx;
1828 LDAPMessage *res = NULL;
1829 ADS_MODLIST mods;
1830 const char *servicePrincipalName[1] = {NULL};
1831 ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS);
1832 char *dn_string = NULL;
1834 ret = ads_find_machine_acct(ads, &res, machine_name);
1835 if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
1836 DEBUG(5,("ads_clear_service_principal_names: WARNING: Host Account for %s not found... skipping operation.\n", machine_name));
1837 DEBUG(5,("ads_clear_service_principal_names: WARNING: Service Principals for %s have NOT been cleared.\n", machine_name));
1838 ads_msgfree(ads, res);
1839 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1842 DEBUG(5,("ads_clear_service_principal_names: Host account for %s found\n", machine_name));
1843 ctx = talloc_init("ads_clear_service_principal_names");
1844 if (!ctx) {
1845 ads_msgfree(ads, res);
1846 return ADS_ERROR(LDAP_NO_MEMORY);
1849 if (!(mods = ads_init_mods(ctx))) {
1850 talloc_destroy(ctx);
1851 ads_msgfree(ads, res);
1852 return ADS_ERROR(LDAP_NO_MEMORY);
1854 ret = ads_mod_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1855 if (!ADS_ERR_OK(ret)) {
1856 DEBUG(1,("ads_clear_service_principal_names: Error creating strlist.\n"));
1857 ads_msgfree(ads, res);
1858 talloc_destroy(ctx);
1859 return ret;
1861 dn_string = ads_get_dn(ads, talloc_tos(), res);
1862 if (!dn_string) {
1863 talloc_destroy(ctx);
1864 ads_msgfree(ads, res);
1865 return ADS_ERROR(LDAP_NO_MEMORY);
1867 ret = ads_gen_mod(ads, dn_string, mods);
1868 TALLOC_FREE(dn_string);
1869 if (!ADS_ERR_OK(ret)) {
1870 DEBUG(1,("ads_clear_service_principal_names: Error: Updating Service Principals for machine %s in LDAP\n",
1871 machine_name));
1872 ads_msgfree(ads, res);
1873 talloc_destroy(ctx);
1874 return ret;
1877 ads_msgfree(ads, res);
1878 talloc_destroy(ctx);
1879 return ret;
1883 * This adds a service principal name to an existing computer account
1884 * (found by hostname) in AD.
1885 * @param ads An initialized ADS_STRUCT
1886 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1887 * @param my_fqdn The fully qualified DNS name of the machine
1888 * @param spn A string of the service principal to add, i.e. 'host'
1889 * @return 0 upon sucess, or non-zero if a failure occurs
1892 ADS_STATUS ads_add_service_principal_name(ADS_STRUCT *ads, const char *machine_name,
1893 const char *my_fqdn, const char *spn)
1895 ADS_STATUS ret;
1896 TALLOC_CTX *ctx;
1897 LDAPMessage *res = NULL;
1898 char *psp1, *psp2;
1899 ADS_MODLIST mods;
1900 char *dn_string = NULL;
1901 const char *servicePrincipalName[3] = {NULL, NULL, NULL};
1903 ret = ads_find_machine_acct(ads, &res, machine_name);
1904 if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
1905 DEBUG(1,("ads_add_service_principal_name: WARNING: Host Account for %s not found... skipping operation.\n",
1906 machine_name));
1907 DEBUG(1,("ads_add_service_principal_name: WARNING: Service Principal '%s/%s@%s' has NOT been added.\n",
1908 spn, machine_name, ads->config.realm));
1909 ads_msgfree(ads, res);
1910 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1913 DEBUG(1,("ads_add_service_principal_name: Host account for %s found\n", machine_name));
1914 if (!(ctx = talloc_init("ads_add_service_principal_name"))) {
1915 ads_msgfree(ads, res);
1916 return ADS_ERROR(LDAP_NO_MEMORY);
1919 /* add short name spn */
1921 if ( (psp1 = talloc_asprintf(ctx, "%s/%s", spn, machine_name)) == NULL ) {
1922 talloc_destroy(ctx);
1923 ads_msgfree(ads, res);
1924 return ADS_ERROR(LDAP_NO_MEMORY);
1926 strupper_m(psp1);
1927 strlower_m(&psp1[strlen(spn)]);
1928 servicePrincipalName[0] = psp1;
1930 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n",
1931 psp1, machine_name));
1934 /* add fully qualified spn */
1936 if ( (psp2 = talloc_asprintf(ctx, "%s/%s", spn, my_fqdn)) == NULL ) {
1937 ret = ADS_ERROR(LDAP_NO_MEMORY);
1938 goto out;
1940 strupper_m(psp2);
1941 strlower_m(&psp2[strlen(spn)]);
1942 servicePrincipalName[1] = psp2;
1944 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n",
1945 psp2, machine_name));
1947 if ( (mods = ads_init_mods(ctx)) == NULL ) {
1948 ret = ADS_ERROR(LDAP_NO_MEMORY);
1949 goto out;
1952 ret = ads_add_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1953 if (!ADS_ERR_OK(ret)) {
1954 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
1955 goto out;
1958 if ( (dn_string = ads_get_dn(ads, ctx, res)) == NULL ) {
1959 ret = ADS_ERROR(LDAP_NO_MEMORY);
1960 goto out;
1963 ret = ads_gen_mod(ads, dn_string, mods);
1964 if (!ADS_ERR_OK(ret)) {
1965 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
1966 goto out;
1969 out:
1970 TALLOC_FREE( ctx );
1971 ads_msgfree(ads, res);
1972 return ret;
1976 * adds a machine account to the ADS server
1977 * @param ads An intialized ADS_STRUCT
1978 * @param machine_name - the NetBIOS machine name of this account.
1979 * @param account_type A number indicating the type of account to create
1980 * @param org_unit The LDAP path in which to place this account
1981 * @return 0 upon success, or non-zero otherwise
1984 ADS_STATUS ads_create_machine_acct(ADS_STRUCT *ads, const char *machine_name,
1985 const char *org_unit)
1987 ADS_STATUS ret;
1988 char *samAccountName, *controlstr;
1989 TALLOC_CTX *ctx;
1990 ADS_MODLIST mods;
1991 char *machine_escaped = NULL;
1992 char *new_dn;
1993 const char *objectClass[] = {"top", "person", "organizationalPerson",
1994 "user", "computer", NULL};
1995 LDAPMessage *res = NULL;
1996 uint32 acct_control = ( UF_WORKSTATION_TRUST_ACCOUNT |\
1997 UF_DONT_EXPIRE_PASSWD |\
1998 UF_ACCOUNTDISABLE );
2000 if (!(ctx = talloc_init("ads_add_machine_acct")))
2001 return ADS_ERROR(LDAP_NO_MEMORY);
2003 ret = ADS_ERROR(LDAP_NO_MEMORY);
2005 machine_escaped = escape_rdn_val_string_alloc(machine_name);
2006 if (!machine_escaped) {
2007 goto done;
2010 new_dn = talloc_asprintf(ctx, "cn=%s,%s", machine_escaped, org_unit);
2011 samAccountName = talloc_asprintf(ctx, "%s$", machine_name);
2013 if ( !new_dn || !samAccountName ) {
2014 goto done;
2017 #ifndef ENCTYPE_ARCFOUR_HMAC
2018 acct_control |= UF_USE_DES_KEY_ONLY;
2019 #endif
2021 if (!(controlstr = talloc_asprintf(ctx, "%u", acct_control))) {
2022 goto done;
2025 if (!(mods = ads_init_mods(ctx))) {
2026 goto done;
2029 ads_mod_str(ctx, &mods, "cn", machine_name);
2030 ads_mod_str(ctx, &mods, "sAMAccountName", samAccountName);
2031 ads_mod_strlist(ctx, &mods, "objectClass", objectClass);
2032 ads_mod_str(ctx, &mods, "userAccountControl", controlstr);
2034 ret = ads_gen_add(ads, new_dn, mods);
2036 done:
2037 SAFE_FREE(machine_escaped);
2038 ads_msgfree(ads, res);
2039 talloc_destroy(ctx);
2041 return ret;
2045 * move a machine account to another OU on the ADS server
2046 * @param ads - An intialized ADS_STRUCT
2047 * @param machine_name - the NetBIOS machine name of this account.
2048 * @param org_unit - The LDAP path in which to place this account
2049 * @param moved - whether we moved the machine account (optional)
2050 * @return 0 upon success, or non-zero otherwise
2053 ADS_STATUS ads_move_machine_acct(ADS_STRUCT *ads, const char *machine_name,
2054 const char *org_unit, bool *moved)
2056 ADS_STATUS rc;
2057 int ldap_status;
2058 LDAPMessage *res = NULL;
2059 char *filter = NULL;
2060 char *computer_dn = NULL;
2061 char *parent_dn;
2062 char *computer_rdn = NULL;
2063 bool need_move = False;
2065 if (asprintf(&filter, "(samAccountName=%s$)", machine_name) == -1) {
2066 rc = ADS_ERROR(LDAP_NO_MEMORY);
2067 goto done;
2070 /* Find pre-existing machine */
2071 rc = ads_search(ads, &res, filter, NULL);
2072 if (!ADS_ERR_OK(rc)) {
2073 goto done;
2076 computer_dn = ads_get_dn(ads, talloc_tos(), res);
2077 if (!computer_dn) {
2078 rc = ADS_ERROR(LDAP_NO_MEMORY);
2079 goto done;
2082 parent_dn = ads_parent_dn(computer_dn);
2083 if (strequal(parent_dn, org_unit)) {
2084 goto done;
2087 need_move = True;
2089 if (asprintf(&computer_rdn, "CN=%s", machine_name) == -1) {
2090 rc = ADS_ERROR(LDAP_NO_MEMORY);
2091 goto done;
2094 ldap_status = ldap_rename_s(ads->ldap.ld, computer_dn, computer_rdn,
2095 org_unit, 1, NULL, NULL);
2096 rc = ADS_ERROR(ldap_status);
2098 done:
2099 ads_msgfree(ads, res);
2100 SAFE_FREE(filter);
2101 TALLOC_FREE(computer_dn);
2102 SAFE_FREE(computer_rdn);
2104 if (!ADS_ERR_OK(rc)) {
2105 need_move = False;
2108 if (moved) {
2109 *moved = need_move;
2112 return rc;
2116 dump a binary result from ldap
2118 static void dump_binary(ADS_STRUCT *ads, const char *field, struct berval **values)
2120 int i, j;
2121 for (i=0; values[i]; i++) {
2122 printf("%s: ", field);
2123 for (j=0; j<values[i]->bv_len; j++) {
2124 printf("%02X", (unsigned char)values[i]->bv_val[j]);
2126 printf("\n");
2130 static void dump_guid(ADS_STRUCT *ads, const char *field, struct berval **values)
2132 int i;
2133 for (i=0; values[i]; i++) {
2134 NTSTATUS status;
2135 DATA_BLOB in = data_blob_const(values[i]->bv_val, values[i]->bv_len);
2136 struct GUID guid;
2138 status = GUID_from_ndr_blob(&in, &guid);
2139 if (NT_STATUS_IS_OK(status)) {
2140 printf("%s: %s\n", field, GUID_string(talloc_tos(), &guid));
2141 } else {
2142 printf("%s: INVALID GUID\n", field);
2148 dump a sid result from ldap
2150 static void dump_sid(ADS_STRUCT *ads, const char *field, struct berval **values)
2152 int i;
2153 for (i=0; values[i]; i++) {
2154 struct dom_sid sid;
2155 fstring tmp;
2156 if (!sid_parse(values[i]->bv_val, values[i]->bv_len, &sid)) {
2157 return;
2159 printf("%s: %s\n", field, sid_to_fstring(tmp, &sid));
2164 dump ntSecurityDescriptor
2166 static void dump_sd(ADS_STRUCT *ads, const char *filed, struct berval **values)
2168 TALLOC_CTX *frame = talloc_stackframe();
2169 struct security_descriptor *psd;
2170 NTSTATUS status;
2172 status = unmarshall_sec_desc(talloc_tos(), (uint8 *)values[0]->bv_val,
2173 values[0]->bv_len, &psd);
2174 if (!NT_STATUS_IS_OK(status)) {
2175 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
2176 nt_errstr(status)));
2177 TALLOC_FREE(frame);
2178 return;
2181 if (psd) {
2182 ads_disp_sd(ads, talloc_tos(), psd);
2185 TALLOC_FREE(frame);
2189 dump a string result from ldap
2191 static void dump_string(const char *field, char **values)
2193 int i;
2194 for (i=0; values[i]; i++) {
2195 printf("%s: %s\n", field, values[i]);
2200 dump a field from LDAP on stdout
2201 used for debugging
2204 static bool ads_dump_field(ADS_STRUCT *ads, char *field, void **values, void *data_area)
2206 const struct {
2207 const char *name;
2208 bool string;
2209 void (*handler)(ADS_STRUCT *, const char *, struct berval **);
2210 } handlers[] = {
2211 {"objectGUID", False, dump_guid},
2212 {"netbootGUID", False, dump_guid},
2213 {"nTSecurityDescriptor", False, dump_sd},
2214 {"dnsRecord", False, dump_binary},
2215 {"objectSid", False, dump_sid},
2216 {"tokenGroups", False, dump_sid},
2217 {"tokenGroupsNoGCAcceptable", False, dump_sid},
2218 {"tokengroupsGlobalandUniversal", False, dump_sid},
2219 {"mS-DS-CreatorSID", False, dump_sid},
2220 {"msExchMailboxGuid", False, dump_guid},
2221 {NULL, True, NULL}
2223 int i;
2225 if (!field) { /* must be end of an entry */
2226 printf("\n");
2227 return False;
2230 for (i=0; handlers[i].name; i++) {
2231 if (strcasecmp_m(handlers[i].name, field) == 0) {
2232 if (!values) /* first time, indicate string or not */
2233 return handlers[i].string;
2234 handlers[i].handler(ads, field, (struct berval **) values);
2235 break;
2238 if (!handlers[i].name) {
2239 if (!values) /* first time, indicate string conversion */
2240 return True;
2241 dump_string(field, (char **)values);
2243 return False;
2247 * Dump a result from LDAP on stdout
2248 * used for debugging
2249 * @param ads connection to ads server
2250 * @param res Results to dump
2253 void ads_dump(ADS_STRUCT *ads, LDAPMessage *res)
2255 ads_process_results(ads, res, ads_dump_field, NULL);
2259 * Walk through results, calling a function for each entry found.
2260 * The function receives a field name, a berval * array of values,
2261 * and a data area passed through from the start. The function is
2262 * called once with null for field and values at the end of each
2263 * entry.
2264 * @param ads connection to ads server
2265 * @param res Results to process
2266 * @param fn Function for processing each result
2267 * @param data_area user-defined area to pass to function
2269 void ads_process_results(ADS_STRUCT *ads, LDAPMessage *res,
2270 bool (*fn)(ADS_STRUCT *, char *, void **, void *),
2271 void *data_area)
2273 LDAPMessage *msg;
2274 TALLOC_CTX *ctx;
2275 size_t converted_size;
2277 if (!(ctx = talloc_init("ads_process_results")))
2278 return;
2280 for (msg = ads_first_entry(ads, res); msg;
2281 msg = ads_next_entry(ads, msg)) {
2282 char *utf8_field;
2283 BerElement *b;
2285 for (utf8_field=ldap_first_attribute(ads->ldap.ld,
2286 (LDAPMessage *)msg,&b);
2287 utf8_field;
2288 utf8_field=ldap_next_attribute(ads->ldap.ld,
2289 (LDAPMessage *)msg,b)) {
2290 struct berval **ber_vals;
2291 char **str_vals, **utf8_vals;
2292 char *field;
2293 bool string;
2295 if (!pull_utf8_talloc(ctx, &field, utf8_field,
2296 &converted_size))
2298 DEBUG(0,("ads_process_results: "
2299 "pull_utf8_talloc failed: %s",
2300 strerror(errno)));
2303 string = fn(ads, field, NULL, data_area);
2305 if (string) {
2306 utf8_vals = ldap_get_values(ads->ldap.ld,
2307 (LDAPMessage *)msg, field);
2308 str_vals = ads_pull_strvals(ctx,
2309 (const char **) utf8_vals);
2310 fn(ads, field, (void **) str_vals, data_area);
2311 ldap_value_free(utf8_vals);
2312 } else {
2313 ber_vals = ldap_get_values_len(ads->ldap.ld,
2314 (LDAPMessage *)msg, field);
2315 fn(ads, field, (void **) ber_vals, data_area);
2317 ldap_value_free_len(ber_vals);
2319 ldap_memfree(utf8_field);
2321 ber_free(b, 0);
2322 talloc_free_children(ctx);
2323 fn(ads, NULL, NULL, data_area); /* completed an entry */
2326 talloc_destroy(ctx);
2330 * count how many replies are in a LDAPMessage
2331 * @param ads connection to ads server
2332 * @param res Results to count
2333 * @return number of replies
2335 int ads_count_replies(ADS_STRUCT *ads, void *res)
2337 return ldap_count_entries(ads->ldap.ld, (LDAPMessage *)res);
2341 * pull the first entry from a ADS result
2342 * @param ads connection to ads server
2343 * @param res Results of search
2344 * @return first entry from result
2346 LDAPMessage *ads_first_entry(ADS_STRUCT *ads, LDAPMessage *res)
2348 return ldap_first_entry(ads->ldap.ld, res);
2352 * pull the next entry from a ADS result
2353 * @param ads connection to ads server
2354 * @param res Results of search
2355 * @return next entry from result
2357 LDAPMessage *ads_next_entry(ADS_STRUCT *ads, LDAPMessage *res)
2359 return ldap_next_entry(ads->ldap.ld, res);
2363 * pull the first message from a ADS result
2364 * @param ads connection to ads server
2365 * @param res Results of search
2366 * @return first message from result
2368 LDAPMessage *ads_first_message(ADS_STRUCT *ads, LDAPMessage *res)
2370 return ldap_first_message(ads->ldap.ld, res);
2374 * pull the next message from a ADS result
2375 * @param ads connection to ads server
2376 * @param res Results of search
2377 * @return next message from result
2379 LDAPMessage *ads_next_message(ADS_STRUCT *ads, LDAPMessage *res)
2381 return ldap_next_message(ads->ldap.ld, res);
2385 * pull a single string from a ADS result
2386 * @param ads connection to ads server
2387 * @param mem_ctx TALLOC_CTX to use for allocating result string
2388 * @param msg Results of search
2389 * @param field Attribute to retrieve
2390 * @return Result string in talloc context
2392 char *ads_pull_string(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, LDAPMessage *msg,
2393 const char *field)
2395 char **values;
2396 char *ret = NULL;
2397 char *ux_string;
2398 size_t converted_size;
2400 values = ldap_get_values(ads->ldap.ld, msg, field);
2401 if (!values)
2402 return NULL;
2404 if (values[0] && pull_utf8_talloc(mem_ctx, &ux_string, values[0],
2405 &converted_size))
2407 ret = ux_string;
2409 ldap_value_free(values);
2410 return ret;
2414 * pull an array of strings from a ADS result
2415 * @param ads connection to ads server
2416 * @param mem_ctx TALLOC_CTX to use for allocating result string
2417 * @param msg Results of search
2418 * @param field Attribute to retrieve
2419 * @return Result strings in talloc context
2421 char **ads_pull_strings(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2422 LDAPMessage *msg, const char *field,
2423 size_t *num_values)
2425 char **values;
2426 char **ret = NULL;
2427 int i;
2428 size_t converted_size;
2430 values = ldap_get_values(ads->ldap.ld, msg, field);
2431 if (!values)
2432 return NULL;
2434 *num_values = ldap_count_values(values);
2436 ret = talloc_array(mem_ctx, char *, *num_values + 1);
2437 if (!ret) {
2438 ldap_value_free(values);
2439 return NULL;
2442 for (i=0;i<*num_values;i++) {
2443 if (!pull_utf8_talloc(mem_ctx, &ret[i], values[i],
2444 &converted_size))
2446 ldap_value_free(values);
2447 return NULL;
2450 ret[i] = NULL;
2452 ldap_value_free(values);
2453 return ret;
2457 * pull an array of strings from a ADS result
2458 * (handle large multivalue attributes with range retrieval)
2459 * @param ads connection to ads server
2460 * @param mem_ctx TALLOC_CTX to use for allocating result string
2461 * @param msg Results of search
2462 * @param field Attribute to retrieve
2463 * @param current_strings strings returned by a previous call to this function
2464 * @param next_attribute The next query should ask for this attribute
2465 * @param num_values How many values did we get this time?
2466 * @param more_values Are there more values to get?
2467 * @return Result strings in talloc context
2469 char **ads_pull_strings_range(ADS_STRUCT *ads,
2470 TALLOC_CTX *mem_ctx,
2471 LDAPMessage *msg, const char *field,
2472 char **current_strings,
2473 const char **next_attribute,
2474 size_t *num_strings,
2475 bool *more_strings)
2477 char *attr;
2478 char *expected_range_attrib, *range_attr;
2479 BerElement *ptr = NULL;
2480 char **strings;
2481 char **new_strings;
2482 size_t num_new_strings;
2483 unsigned long int range_start;
2484 unsigned long int range_end;
2486 /* we might have been given the whole lot anyway */
2487 if ((strings = ads_pull_strings(ads, mem_ctx, msg, field, num_strings))) {
2488 *more_strings = False;
2489 return strings;
2492 expected_range_attrib = talloc_asprintf(mem_ctx, "%s;Range=", field);
2494 /* look for Range result */
2495 for (attr = ldap_first_attribute(ads->ldap.ld, (LDAPMessage *)msg, &ptr);
2496 attr;
2497 attr = ldap_next_attribute(ads->ldap.ld, (LDAPMessage *)msg, ptr)) {
2498 /* we ignore the fact that this is utf8, as all attributes are ascii... */
2499 if (strnequal(attr, expected_range_attrib, strlen(expected_range_attrib))) {
2500 range_attr = attr;
2501 break;
2503 ldap_memfree(attr);
2505 if (!attr) {
2506 ber_free(ptr, 0);
2507 /* nothing here - this field is just empty */
2508 *more_strings = False;
2509 return NULL;
2512 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-%lu",
2513 &range_start, &range_end) == 2) {
2514 *more_strings = True;
2515 } else {
2516 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-*",
2517 &range_start) == 1) {
2518 *more_strings = False;
2519 } else {
2520 DEBUG(1, ("ads_pull_strings_range: Cannot parse Range attriubte (%s)\n",
2521 range_attr));
2522 ldap_memfree(range_attr);
2523 *more_strings = False;
2524 return NULL;
2528 if ((*num_strings) != range_start) {
2529 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) doesn't start at %u, but at %lu"
2530 " - aborting range retreival\n",
2531 range_attr, (unsigned int)(*num_strings) + 1, range_start));
2532 ldap_memfree(range_attr);
2533 *more_strings = False;
2534 return NULL;
2537 new_strings = ads_pull_strings(ads, mem_ctx, msg, range_attr, &num_new_strings);
2539 if (*more_strings && ((*num_strings + num_new_strings) != (range_end + 1))) {
2540 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) tells us we have %lu "
2541 "strings in this bunch, but we only got %lu - aborting range retreival\n",
2542 range_attr, (unsigned long int)range_end - range_start + 1,
2543 (unsigned long int)num_new_strings));
2544 ldap_memfree(range_attr);
2545 *more_strings = False;
2546 return NULL;
2549 strings = talloc_realloc(mem_ctx, current_strings, char *,
2550 *num_strings + num_new_strings);
2552 if (strings == NULL) {
2553 ldap_memfree(range_attr);
2554 *more_strings = False;
2555 return NULL;
2558 if (new_strings && num_new_strings) {
2559 memcpy(&strings[*num_strings], new_strings,
2560 sizeof(*new_strings) * num_new_strings);
2563 (*num_strings) += num_new_strings;
2565 if (*more_strings) {
2566 *next_attribute = talloc_asprintf(mem_ctx,
2567 "%s;range=%d-*",
2568 field,
2569 (int)*num_strings);
2571 if (!*next_attribute) {
2572 DEBUG(1, ("talloc_asprintf for next attribute failed!\n"));
2573 ldap_memfree(range_attr);
2574 *more_strings = False;
2575 return NULL;
2579 ldap_memfree(range_attr);
2581 return strings;
2585 * pull a single uint32 from a ADS result
2586 * @param ads connection to ads server
2587 * @param msg Results of search
2588 * @param field Attribute to retrieve
2589 * @param v Pointer to int to store result
2590 * @return boolean inidicating success
2592 bool ads_pull_uint32(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
2593 uint32 *v)
2595 char **values;
2597 values = ldap_get_values(ads->ldap.ld, msg, field);
2598 if (!values)
2599 return False;
2600 if (!values[0]) {
2601 ldap_value_free(values);
2602 return False;
2605 *v = atoi(values[0]);
2606 ldap_value_free(values);
2607 return True;
2611 * pull a single objectGUID from an ADS result
2612 * @param ads connection to ADS server
2613 * @param msg results of search
2614 * @param guid 37-byte area to receive text guid
2615 * @return boolean indicating success
2617 bool ads_pull_guid(ADS_STRUCT *ads, LDAPMessage *msg, struct GUID *guid)
2619 DATA_BLOB blob;
2620 NTSTATUS status;
2622 if (!smbldap_talloc_single_blob(talloc_tos(), ads->ldap.ld, msg, "objectGUID",
2623 &blob)) {
2624 return false;
2627 status = GUID_from_ndr_blob(&blob, guid);
2628 talloc_free(blob.data);
2629 return NT_STATUS_IS_OK(status);
2634 * pull a single struct dom_sid from a ADS result
2635 * @param ads connection to ads server
2636 * @param msg Results of search
2637 * @param field Attribute to retrieve
2638 * @param sid Pointer to sid to store result
2639 * @return boolean inidicating success
2641 bool ads_pull_sid(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
2642 struct dom_sid *sid)
2644 return smbldap_pull_sid(ads->ldap.ld, msg, field, sid);
2648 * pull an array of struct dom_sids from a ADS result
2649 * @param ads connection to ads server
2650 * @param mem_ctx TALLOC_CTX for allocating sid array
2651 * @param msg Results of search
2652 * @param field Attribute to retrieve
2653 * @param sids pointer to sid array to allocate
2654 * @return the count of SIDs pulled
2656 int ads_pull_sids(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2657 LDAPMessage *msg, const char *field, struct dom_sid **sids)
2659 struct berval **values;
2660 bool ret;
2661 int count, i;
2663 values = ldap_get_values_len(ads->ldap.ld, msg, field);
2665 if (!values)
2666 return 0;
2668 for (i=0; values[i]; i++)
2669 /* nop */ ;
2671 if (i) {
2672 (*sids) = talloc_array(mem_ctx, struct dom_sid, i);
2673 if (!(*sids)) {
2674 ldap_value_free_len(values);
2675 return 0;
2677 } else {
2678 (*sids) = NULL;
2681 count = 0;
2682 for (i=0; values[i]; i++) {
2683 ret = sid_parse(values[i]->bv_val, values[i]->bv_len, &(*sids)[count]);
2684 if (ret) {
2685 DEBUG(10, ("pulling SID: %s\n",
2686 sid_string_dbg(&(*sids)[count])));
2687 count++;
2691 ldap_value_free_len(values);
2692 return count;
2696 * pull a struct security_descriptor from a ADS result
2697 * @param ads connection to ads server
2698 * @param mem_ctx TALLOC_CTX for allocating sid array
2699 * @param msg Results of search
2700 * @param field Attribute to retrieve
2701 * @param sd Pointer to *struct security_descriptor to store result (talloc()ed)
2702 * @return boolean inidicating success
2704 bool ads_pull_sd(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2705 LDAPMessage *msg, const char *field,
2706 struct security_descriptor **sd)
2708 struct berval **values;
2709 bool ret = true;
2711 values = ldap_get_values_len(ads->ldap.ld, msg, field);
2713 if (!values) return false;
2715 if (values[0]) {
2716 NTSTATUS status;
2717 status = unmarshall_sec_desc(mem_ctx,
2718 (uint8 *)values[0]->bv_val,
2719 values[0]->bv_len, sd);
2720 if (!NT_STATUS_IS_OK(status)) {
2721 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
2722 nt_errstr(status)));
2723 ret = false;
2727 ldap_value_free_len(values);
2728 return ret;
2732 * in order to support usernames longer than 21 characters we need to
2733 * use both the sAMAccountName and the userPrincipalName attributes
2734 * It seems that not all users have the userPrincipalName attribute set
2736 * @param ads connection to ads server
2737 * @param mem_ctx TALLOC_CTX for allocating sid array
2738 * @param msg Results of search
2739 * @return the username
2741 char *ads_pull_username(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2742 LDAPMessage *msg)
2744 #if 0 /* JERRY */
2745 char *ret, *p;
2747 /* lookup_name() only works on the sAMAccountName to
2748 returning the username portion of userPrincipalName
2749 breaks winbindd_getpwnam() */
2751 ret = ads_pull_string(ads, mem_ctx, msg, "userPrincipalName");
2752 if (ret && (p = strchr_m(ret, '@'))) {
2753 *p = 0;
2754 return ret;
2756 #endif
2757 return ads_pull_string(ads, mem_ctx, msg, "sAMAccountName");
2762 * find the update serial number - this is the core of the ldap cache
2763 * @param ads connection to ads server
2764 * @param ads connection to ADS server
2765 * @param usn Pointer to retrieved update serial number
2766 * @return status of search
2768 ADS_STATUS ads_USN(ADS_STRUCT *ads, uint32 *usn)
2770 const char *attrs[] = {"highestCommittedUSN", NULL};
2771 ADS_STATUS status;
2772 LDAPMessage *res;
2774 status = ads_do_search_retry(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2775 if (!ADS_ERR_OK(status))
2776 return status;
2778 if (ads_count_replies(ads, res) != 1) {
2779 ads_msgfree(ads, res);
2780 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2783 if (!ads_pull_uint32(ads, res, "highestCommittedUSN", usn)) {
2784 ads_msgfree(ads, res);
2785 return ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
2788 ads_msgfree(ads, res);
2789 return ADS_SUCCESS;
2792 /* parse a ADS timestring - typical string is
2793 '20020917091222.0Z0' which means 09:12.22 17th September
2794 2002, timezone 0 */
2795 static time_t ads_parse_time(const char *str)
2797 struct tm tm;
2799 ZERO_STRUCT(tm);
2801 if (sscanf(str, "%4d%2d%2d%2d%2d%2d",
2802 &tm.tm_year, &tm.tm_mon, &tm.tm_mday,
2803 &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) {
2804 return 0;
2806 tm.tm_year -= 1900;
2807 tm.tm_mon -= 1;
2809 return timegm(&tm);
2812 /********************************************************************
2813 ********************************************************************/
2815 ADS_STATUS ads_current_time(ADS_STRUCT *ads)
2817 const char *attrs[] = {"currentTime", NULL};
2818 ADS_STATUS status;
2819 LDAPMessage *res;
2820 char *timestr;
2821 TALLOC_CTX *ctx;
2822 ADS_STRUCT *ads_s = ads;
2824 if (!(ctx = talloc_init("ads_current_time"))) {
2825 return ADS_ERROR(LDAP_NO_MEMORY);
2828 /* establish a new ldap tcp session if necessary */
2830 if ( !ads->ldap.ld ) {
2831 if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup,
2832 ads->server.ldap_server )) == NULL )
2834 goto done;
2836 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
2837 status = ads_connect( ads_s );
2838 if ( !ADS_ERR_OK(status))
2839 goto done;
2842 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2843 if (!ADS_ERR_OK(status)) {
2844 goto done;
2847 timestr = ads_pull_string(ads_s, ctx, res, "currentTime");
2848 if (!timestr) {
2849 ads_msgfree(ads_s, res);
2850 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2851 goto done;
2854 /* but save the time and offset in the original ADS_STRUCT */
2856 ads->config.current_time = ads_parse_time(timestr);
2858 if (ads->config.current_time != 0) {
2859 ads->auth.time_offset = ads->config.current_time - time(NULL);
2860 DEBUG(4,("KDC time offset is %d seconds\n", ads->auth.time_offset));
2863 ads_msgfree(ads, res);
2865 status = ADS_SUCCESS;
2867 done:
2868 /* free any temporary ads connections */
2869 if ( ads_s != ads ) {
2870 ads_destroy( &ads_s );
2872 talloc_destroy(ctx);
2874 return status;
2877 /********************************************************************
2878 ********************************************************************/
2880 ADS_STATUS ads_domain_func_level(ADS_STRUCT *ads, uint32 *val)
2882 const char *attrs[] = {"domainFunctionality", NULL};
2883 ADS_STATUS status;
2884 LDAPMessage *res;
2885 ADS_STRUCT *ads_s = ads;
2887 *val = DS_DOMAIN_FUNCTION_2000;
2889 /* establish a new ldap tcp session if necessary */
2891 if ( !ads->ldap.ld ) {
2892 if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup,
2893 ads->server.ldap_server )) == NULL )
2895 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
2896 goto done;
2898 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
2899 status = ads_connect( ads_s );
2900 if ( !ADS_ERR_OK(status))
2901 goto done;
2904 /* If the attribute does not exist assume it is a Windows 2000
2905 functional domain */
2907 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2908 if (!ADS_ERR_OK(status)) {
2909 if ( status.err.rc == LDAP_NO_SUCH_ATTRIBUTE ) {
2910 status = ADS_SUCCESS;
2912 goto done;
2915 if ( !ads_pull_uint32(ads_s, res, "domainFunctionality", val) ) {
2916 DEBUG(5,("ads_domain_func_level: Failed to pull the domainFunctionality attribute.\n"));
2918 DEBUG(3,("ads_domain_func_level: %d\n", *val));
2921 ads_msgfree(ads, res);
2923 done:
2924 /* free any temporary ads connections */
2925 if ( ads_s != ads ) {
2926 ads_destroy( &ads_s );
2929 return status;
2933 * find the domain sid for our domain
2934 * @param ads connection to ads server
2935 * @param sid Pointer to domain sid
2936 * @return status of search
2938 ADS_STATUS ads_domain_sid(ADS_STRUCT *ads, struct dom_sid *sid)
2940 const char *attrs[] = {"objectSid", NULL};
2941 LDAPMessage *res;
2942 ADS_STATUS rc;
2944 rc = ads_do_search_retry(ads, ads->config.bind_path, LDAP_SCOPE_BASE, "(objectclass=*)",
2945 attrs, &res);
2946 if (!ADS_ERR_OK(rc)) return rc;
2947 if (!ads_pull_sid(ads, res, "objectSid", sid)) {
2948 ads_msgfree(ads, res);
2949 return ADS_ERROR_SYSTEM(ENOENT);
2951 ads_msgfree(ads, res);
2953 return ADS_SUCCESS;
2957 * find our site name
2958 * @param ads connection to ads server
2959 * @param mem_ctx Pointer to talloc context
2960 * @param site_name Pointer to the sitename
2961 * @return status of search
2963 ADS_STATUS ads_site_dn(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char **site_name)
2965 ADS_STATUS status;
2966 LDAPMessage *res;
2967 const char *dn, *service_name;
2968 const char *attrs[] = { "dsServiceName", NULL };
2970 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2971 if (!ADS_ERR_OK(status)) {
2972 return status;
2975 service_name = ads_pull_string(ads, mem_ctx, res, "dsServiceName");
2976 if (service_name == NULL) {
2977 ads_msgfree(ads, res);
2978 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2981 ads_msgfree(ads, res);
2983 /* go up three levels */
2984 dn = ads_parent_dn(ads_parent_dn(ads_parent_dn(service_name)));
2985 if (dn == NULL) {
2986 return ADS_ERROR(LDAP_NO_MEMORY);
2989 *site_name = talloc_strdup(mem_ctx, dn);
2990 if (*site_name == NULL) {
2991 return ADS_ERROR(LDAP_NO_MEMORY);
2994 return status;
2996 dsServiceName: CN=NTDS Settings,CN=W2K3DC,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,DC=ber,DC=suse,DC=de
3001 * find the site dn where a machine resides
3002 * @param ads connection to ads server
3003 * @param mem_ctx Pointer to talloc context
3004 * @param computer_name name of the machine
3005 * @param site_name Pointer to the sitename
3006 * @return status of search
3008 ADS_STATUS ads_site_dn_for_machine(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char *computer_name, const char **site_dn)
3010 ADS_STATUS status;
3011 LDAPMessage *res;
3012 const char *parent, *filter;
3013 char *config_context = NULL;
3014 char *dn;
3016 /* shortcut a query */
3017 if (strequal(computer_name, ads->config.ldap_server_name)) {
3018 return ads_site_dn(ads, mem_ctx, site_dn);
3021 status = ads_config_path(ads, mem_ctx, &config_context);
3022 if (!ADS_ERR_OK(status)) {
3023 return status;
3026 filter = talloc_asprintf(mem_ctx, "(cn=%s)", computer_name);
3027 if (filter == NULL) {
3028 return ADS_ERROR(LDAP_NO_MEMORY);
3031 status = ads_do_search(ads, config_context, LDAP_SCOPE_SUBTREE,
3032 filter, NULL, &res);
3033 if (!ADS_ERR_OK(status)) {
3034 return status;
3037 if (ads_count_replies(ads, res) != 1) {
3038 ads_msgfree(ads, res);
3039 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
3042 dn = ads_get_dn(ads, mem_ctx, res);
3043 if (dn == NULL) {
3044 ads_msgfree(ads, res);
3045 return ADS_ERROR(LDAP_NO_MEMORY);
3048 /* go up three levels */
3049 parent = ads_parent_dn(ads_parent_dn(ads_parent_dn(dn)));
3050 if (parent == NULL) {
3051 ads_msgfree(ads, res);
3052 TALLOC_FREE(dn);
3053 return ADS_ERROR(LDAP_NO_MEMORY);
3056 *site_dn = talloc_strdup(mem_ctx, parent);
3057 if (*site_dn == NULL) {
3058 ads_msgfree(ads, res);
3059 TALLOC_FREE(dn);
3060 return ADS_ERROR(LDAP_NO_MEMORY);
3063 TALLOC_FREE(dn);
3064 ads_msgfree(ads, res);
3066 return status;
3070 * get the upn suffixes for a domain
3071 * @param ads connection to ads server
3072 * @param mem_ctx Pointer to talloc context
3073 * @param suffixes Pointer to an array of suffixes
3074 * @param num_suffixes Pointer to the number of suffixes
3075 * @return status of search
3077 ADS_STATUS ads_upn_suffixes(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, char ***suffixes, size_t *num_suffixes)
3079 ADS_STATUS status;
3080 LDAPMessage *res;
3081 const char *base;
3082 char *config_context = NULL;
3083 const char *attrs[] = { "uPNSuffixes", NULL };
3085 status = ads_config_path(ads, mem_ctx, &config_context);
3086 if (!ADS_ERR_OK(status)) {
3087 return status;
3090 base = talloc_asprintf(mem_ctx, "cn=Partitions,%s", config_context);
3091 if (base == NULL) {
3092 return ADS_ERROR(LDAP_NO_MEMORY);
3095 status = ads_search_dn(ads, &res, base, attrs);
3096 if (!ADS_ERR_OK(status)) {
3097 return status;
3100 if (ads_count_replies(ads, res) != 1) {
3101 ads_msgfree(ads, res);
3102 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
3105 (*suffixes) = ads_pull_strings(ads, mem_ctx, res, "uPNSuffixes", num_suffixes);
3106 if ((*suffixes) == NULL) {
3107 ads_msgfree(ads, res);
3108 return ADS_ERROR(LDAP_NO_MEMORY);
3111 ads_msgfree(ads, res);
3113 return status;
3117 * get the joinable ous for a domain
3118 * @param ads connection to ads server
3119 * @param mem_ctx Pointer to talloc context
3120 * @param ous Pointer to an array of ous
3121 * @param num_ous Pointer to the number of ous
3122 * @return status of search
3124 ADS_STATUS ads_get_joinable_ous(ADS_STRUCT *ads,
3125 TALLOC_CTX *mem_ctx,
3126 char ***ous,
3127 size_t *num_ous)
3129 ADS_STATUS status;
3130 LDAPMessage *res = NULL;
3131 LDAPMessage *msg = NULL;
3132 const char *attrs[] = { "dn", NULL };
3133 int count = 0;
3135 status = ads_search(ads, &res,
3136 "(|(objectClass=domain)(objectclass=organizationalUnit))",
3137 attrs);
3138 if (!ADS_ERR_OK(status)) {
3139 return status;
3142 count = ads_count_replies(ads, res);
3143 if (count < 1) {
3144 ads_msgfree(ads, res);
3145 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3148 for (msg = ads_first_entry(ads, res); msg;
3149 msg = ads_next_entry(ads, msg)) {
3151 char *dn = NULL;
3153 dn = ads_get_dn(ads, talloc_tos(), msg);
3154 if (!dn) {
3155 ads_msgfree(ads, res);
3156 return ADS_ERROR(LDAP_NO_MEMORY);
3159 if (!add_string_to_array(mem_ctx, dn,
3160 (const char ***)ous,
3161 (int *)num_ous)) {
3162 TALLOC_FREE(dn);
3163 ads_msgfree(ads, res);
3164 return ADS_ERROR(LDAP_NO_MEMORY);
3167 TALLOC_FREE(dn);
3170 ads_msgfree(ads, res);
3172 return status;
3177 * pull a struct dom_sid from an extended dn string
3178 * @param mem_ctx TALLOC_CTX
3179 * @param extended_dn string
3180 * @param flags string type of extended_dn
3181 * @param sid pointer to a struct dom_sid
3182 * @return NT_STATUS_OK on success,
3183 * NT_INVALID_PARAMETER on error,
3184 * NT_STATUS_NOT_FOUND if no SID present
3186 ADS_STATUS ads_get_sid_from_extended_dn(TALLOC_CTX *mem_ctx,
3187 const char *extended_dn,
3188 enum ads_extended_dn_flags flags,
3189 struct dom_sid *sid)
3191 char *p, *q, *dn;
3193 if (!extended_dn) {
3194 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3197 /* otherwise extended_dn gets stripped off */
3198 if ((dn = talloc_strdup(mem_ctx, extended_dn)) == NULL) {
3199 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3202 * ADS_EXTENDED_DN_HEX_STRING:
3203 * <GUID=238e1963cb390f4bb032ba0105525a29>;<SID=010500000000000515000000bb68c8fd6b61b427572eb04556040000>;CN=gd,OU=berlin,OU=suse,DC=ber,DC=suse,DC=de
3205 * ADS_EXTENDED_DN_STRING (only with w2k3):
3206 * <GUID=63198e23-39cb-4b0f-b032-ba0105525a29>;<SID=S-1-5-21-4257769659-666132843-1169174103-1110>;CN=gd,OU=berlin,OU=suse,DC=ber,DC=suse,DC=de
3208 * Object with no SID, such as an Exchange Public Folder
3209 * <GUID=28907fb4bdf6854993e7f0a10b504e7c>;CN=public,CN=Microsoft Exchange System Objects,DC=sd2k3ms,DC=west,DC=isilon,DC=com
3212 p = strchr(dn, ';');
3213 if (!p) {
3214 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3217 if (strncmp(p, ";<SID=", strlen(";<SID=")) != 0) {
3218 DEBUG(5,("No SID present in extended dn\n"));
3219 return ADS_ERROR_NT(NT_STATUS_NOT_FOUND);
3222 p += strlen(";<SID=");
3224 q = strchr(p, '>');
3225 if (!q) {
3226 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3229 *q = '\0';
3231 DEBUG(100,("ads_get_sid_from_extended_dn: sid string is %s\n", p));
3233 switch (flags) {
3235 case ADS_EXTENDED_DN_STRING:
3236 if (!string_to_sid(sid, p)) {
3237 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3239 break;
3240 case ADS_EXTENDED_DN_HEX_STRING: {
3241 fstring buf;
3242 size_t buf_len;
3244 buf_len = strhex_to_str(buf, sizeof(buf), p, strlen(p));
3245 if (buf_len == 0) {
3246 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3249 if (!sid_parse(buf, buf_len, sid)) {
3250 DEBUG(10,("failed to parse sid\n"));
3251 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3253 break;
3255 default:
3256 DEBUG(10,("unknown extended dn format\n"));
3257 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3260 return ADS_ERROR_NT(NT_STATUS_OK);
3264 * pull an array of struct dom_sids from a ADS result
3265 * @param ads connection to ads server
3266 * @param mem_ctx TALLOC_CTX for allocating sid array
3267 * @param msg Results of search
3268 * @param field Attribute to retrieve
3269 * @param flags string type of extended_dn
3270 * @param sids pointer to sid array to allocate
3271 * @return the count of SIDs pulled
3273 int ads_pull_sids_from_extendeddn(ADS_STRUCT *ads,
3274 TALLOC_CTX *mem_ctx,
3275 LDAPMessage *msg,
3276 const char *field,
3277 enum ads_extended_dn_flags flags,
3278 struct dom_sid **sids)
3280 int i;
3281 ADS_STATUS rc;
3282 size_t dn_count, ret_count = 0;
3283 char **dn_strings;
3285 if ((dn_strings = ads_pull_strings(ads, mem_ctx, msg, field,
3286 &dn_count)) == NULL) {
3287 return 0;
3290 (*sids) = talloc_zero_array(mem_ctx, struct dom_sid, dn_count + 1);
3291 if (!(*sids)) {
3292 TALLOC_FREE(dn_strings);
3293 return 0;
3296 for (i=0; i<dn_count; i++) {
3297 rc = ads_get_sid_from_extended_dn(mem_ctx, dn_strings[i],
3298 flags, &(*sids)[i]);
3299 if (!ADS_ERR_OK(rc)) {
3300 if (NT_STATUS_EQUAL(ads_ntstatus(rc),
3301 NT_STATUS_NOT_FOUND)) {
3302 continue;
3304 else {
3305 TALLOC_FREE(*sids);
3306 TALLOC_FREE(dn_strings);
3307 return 0;
3310 ret_count++;
3313 TALLOC_FREE(dn_strings);
3315 return ret_count;
3318 /********************************************************************
3319 ********************************************************************/
3321 char* ads_get_dnshostname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3323 LDAPMessage *res = NULL;
3324 ADS_STATUS status;
3325 int count = 0;
3326 char *name = NULL;
3328 status = ads_find_machine_acct(ads, &res, lp_netbios_name());
3329 if (!ADS_ERR_OK(status)) {
3330 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
3331 lp_netbios_name()));
3332 goto out;
3335 if ( (count = ads_count_replies(ads, res)) != 1 ) {
3336 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
3337 goto out;
3340 if ( (name = ads_pull_string(ads, ctx, res, "dNSHostName")) == NULL ) {
3341 DEBUG(0,("ads_get_dnshostname: No dNSHostName attribute!\n"));
3344 out:
3345 ads_msgfree(ads, res);
3347 return name;
3350 /********************************************************************
3351 ********************************************************************/
3353 char* ads_get_upn( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3355 LDAPMessage *res = NULL;
3356 ADS_STATUS status;
3357 int count = 0;
3358 char *name = NULL;
3360 status = ads_find_machine_acct(ads, &res, machine_name);
3361 if (!ADS_ERR_OK(status)) {
3362 DEBUG(0,("ads_get_upn: Failed to find account for %s\n",
3363 lp_netbios_name()));
3364 goto out;
3367 if ( (count = ads_count_replies(ads, res)) != 1 ) {
3368 DEBUG(1,("ads_get_upn: %d entries returned!\n", count));
3369 goto out;
3372 if ( (name = ads_pull_string(ads, ctx, res, "userPrincipalName")) == NULL ) {
3373 DEBUG(2,("ads_get_upn: No userPrincipalName attribute!\n"));
3376 out:
3377 ads_msgfree(ads, res);
3379 return name;
3382 /********************************************************************
3383 ********************************************************************/
3385 char* ads_get_samaccountname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3387 LDAPMessage *res = NULL;
3388 ADS_STATUS status;
3389 int count = 0;
3390 char *name = NULL;
3392 status = ads_find_machine_acct(ads, &res, lp_netbios_name());
3393 if (!ADS_ERR_OK(status)) {
3394 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
3395 lp_netbios_name()));
3396 goto out;
3399 if ( (count = ads_count_replies(ads, res)) != 1 ) {
3400 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
3401 goto out;
3404 if ( (name = ads_pull_string(ads, ctx, res, "sAMAccountName")) == NULL ) {
3405 DEBUG(0,("ads_get_dnshostname: No sAMAccountName attribute!\n"));
3408 out:
3409 ads_msgfree(ads, res);
3411 return name;
3414 #if 0
3416 SAVED CODE - we used to join via ldap - remember how we did this. JRA.
3419 * Join a machine to a realm
3420 * Creates the machine account and sets the machine password
3421 * @param ads connection to ads server
3422 * @param machine name of host to add
3423 * @param org_unit Organizational unit to place machine in
3424 * @return status of join
3426 ADS_STATUS ads_join_realm(ADS_STRUCT *ads, const char *machine_name,
3427 uint32 account_type, const char *org_unit)
3429 ADS_STATUS status;
3430 LDAPMessage *res = NULL;
3431 char *machine;
3433 /* machine name must be lowercase */
3434 machine = SMB_STRDUP(machine_name);
3435 strlower_m(machine);
3438 status = ads_find_machine_acct(ads, (void **)&res, machine);
3439 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
3440 DEBUG(0, ("Host account for %s already exists - deleting old account\n", machine));
3441 status = ads_leave_realm(ads, machine);
3442 if (!ADS_ERR_OK(status)) {
3443 DEBUG(0, ("Failed to delete host '%s' from the '%s' realm.\n",
3444 machine, ads->config.realm));
3445 return status;
3449 status = ads_add_machine_acct(ads, machine, account_type, org_unit);
3450 if (!ADS_ERR_OK(status)) {
3451 DEBUG(0, ("ads_join_realm: ads_add_machine_acct failed (%s): %s\n", machine, ads_errstr(status)));
3452 SAFE_FREE(machine);
3453 return status;
3456 status = ads_find_machine_acct(ads, (void **)(void *)&res, machine);
3457 if (!ADS_ERR_OK(status)) {
3458 DEBUG(0, ("ads_join_realm: Host account test failed for machine %s\n", machine));
3459 SAFE_FREE(machine);
3460 return status;
3463 SAFE_FREE(machine);
3464 ads_msgfree(ads, res);
3466 return status;
3468 #endif
3471 * Delete a machine from the realm
3472 * @param ads connection to ads server
3473 * @param hostname Machine to remove
3474 * @return status of delete
3476 ADS_STATUS ads_leave_realm(ADS_STRUCT *ads, const char *hostname)
3478 ADS_STATUS status;
3479 void *msg;
3480 LDAPMessage *res;
3481 char *hostnameDN, *host;
3482 int rc;
3483 LDAPControl ldap_control;
3484 LDAPControl * pldap_control[2] = {NULL, NULL};
3486 pldap_control[0] = &ldap_control;
3487 memset(&ldap_control, 0, sizeof(LDAPControl));
3488 ldap_control.ldctl_oid = discard_const_p(char, LDAP_SERVER_TREE_DELETE_OID);
3490 /* hostname must be lowercase */
3491 host = SMB_STRDUP(hostname);
3492 strlower_m(host);
3494 status = ads_find_machine_acct(ads, &res, host);
3495 if (!ADS_ERR_OK(status)) {
3496 DEBUG(0, ("Host account for %s does not exist.\n", host));
3497 SAFE_FREE(host);
3498 return status;
3501 msg = ads_first_entry(ads, res);
3502 if (!msg) {
3503 SAFE_FREE(host);
3504 return ADS_ERROR_SYSTEM(ENOENT);
3507 hostnameDN = ads_get_dn(ads, talloc_tos(), (LDAPMessage *)msg);
3508 if (hostnameDN == NULL) {
3509 SAFE_FREE(host);
3510 return ADS_ERROR_SYSTEM(ENOENT);
3513 rc = ldap_delete_ext_s(ads->ldap.ld, hostnameDN, pldap_control, NULL);
3514 if (rc) {
3515 DEBUG(3,("ldap_delete_ext_s failed with error code %d\n", rc));
3516 }else {
3517 DEBUG(3,("ldap_delete_ext_s succeeded with error code %d\n", rc));
3520 if (rc != LDAP_SUCCESS) {
3521 const char *attrs[] = { "cn", NULL };
3522 LDAPMessage *msg_sub;
3524 /* we only search with scope ONE, we do not expect any further
3525 * objects to be created deeper */
3527 status = ads_do_search_retry(ads, hostnameDN,
3528 LDAP_SCOPE_ONELEVEL,
3529 "(objectclass=*)", attrs, &res);
3531 if (!ADS_ERR_OK(status)) {
3532 SAFE_FREE(host);
3533 TALLOC_FREE(hostnameDN);
3534 return status;
3537 for (msg_sub = ads_first_entry(ads, res); msg_sub;
3538 msg_sub = ads_next_entry(ads, msg_sub)) {
3540 char *dn = NULL;
3542 if ((dn = ads_get_dn(ads, talloc_tos(), msg_sub)) == NULL) {
3543 SAFE_FREE(host);
3544 TALLOC_FREE(hostnameDN);
3545 return ADS_ERROR(LDAP_NO_MEMORY);
3548 status = ads_del_dn(ads, dn);
3549 if (!ADS_ERR_OK(status)) {
3550 DEBUG(3,("failed to delete dn %s: %s\n", dn, ads_errstr(status)));
3551 SAFE_FREE(host);
3552 TALLOC_FREE(dn);
3553 TALLOC_FREE(hostnameDN);
3554 return status;
3557 TALLOC_FREE(dn);
3560 /* there should be no subordinate objects anymore */
3561 status = ads_do_search_retry(ads, hostnameDN,
3562 LDAP_SCOPE_ONELEVEL,
3563 "(objectclass=*)", attrs, &res);
3565 if (!ADS_ERR_OK(status) || ( (ads_count_replies(ads, res)) > 0 ) ) {
3566 SAFE_FREE(host);
3567 TALLOC_FREE(hostnameDN);
3568 return status;
3571 /* delete hostnameDN now */
3572 status = ads_del_dn(ads, hostnameDN);
3573 if (!ADS_ERR_OK(status)) {
3574 SAFE_FREE(host);
3575 DEBUG(3,("failed to delete dn %s: %s\n", hostnameDN, ads_errstr(status)));
3576 TALLOC_FREE(hostnameDN);
3577 return status;
3581 TALLOC_FREE(hostnameDN);
3583 status = ads_find_machine_acct(ads, &res, host);
3584 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
3585 DEBUG(3, ("Failed to remove host account.\n"));
3586 SAFE_FREE(host);
3587 return status;
3590 SAFE_FREE(host);
3591 return status;
3595 * pull all token-sids from an LDAP dn
3596 * @param ads connection to ads server
3597 * @param mem_ctx TALLOC_CTX for allocating sid array
3598 * @param dn of LDAP object
3599 * @param user_sid pointer to struct dom_sid (objectSid)
3600 * @param primary_group_sid pointer to struct dom_sid (self composed)
3601 * @param sids pointer to sid array to allocate
3602 * @param num_sids counter of SIDs pulled
3603 * @return status of token query
3605 ADS_STATUS ads_get_tokensids(ADS_STRUCT *ads,
3606 TALLOC_CTX *mem_ctx,
3607 const char *dn,
3608 struct dom_sid *user_sid,
3609 struct dom_sid *primary_group_sid,
3610 struct dom_sid **sids,
3611 size_t *num_sids)
3613 ADS_STATUS status;
3614 LDAPMessage *res = NULL;
3615 int count = 0;
3616 size_t tmp_num_sids;
3617 struct dom_sid *tmp_sids;
3618 struct dom_sid tmp_user_sid;
3619 struct dom_sid tmp_primary_group_sid;
3620 uint32 pgid;
3621 const char *attrs[] = {
3622 "objectSid",
3623 "tokenGroups",
3624 "primaryGroupID",
3625 NULL
3628 status = ads_search_retry_dn(ads, &res, dn, attrs);
3629 if (!ADS_ERR_OK(status)) {
3630 return status;
3633 count = ads_count_replies(ads, res);
3634 if (count != 1) {
3635 ads_msgfree(ads, res);
3636 return ADS_ERROR_LDAP(LDAP_NO_SUCH_OBJECT);
3639 if (!ads_pull_sid(ads, res, "objectSid", &tmp_user_sid)) {
3640 ads_msgfree(ads, res);
3641 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3644 if (!ads_pull_uint32(ads, res, "primaryGroupID", &pgid)) {
3645 ads_msgfree(ads, res);
3646 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3650 /* hack to compose the primary group sid without knowing the
3651 * domsid */
3653 struct dom_sid domsid;
3655 sid_copy(&domsid, &tmp_user_sid);
3657 if (!sid_split_rid(&domsid, NULL)) {
3658 ads_msgfree(ads, res);
3659 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3662 if (!sid_compose(&tmp_primary_group_sid, &domsid, pgid)) {
3663 ads_msgfree(ads, res);
3664 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3668 tmp_num_sids = ads_pull_sids(ads, mem_ctx, res, "tokenGroups", &tmp_sids);
3670 if (tmp_num_sids == 0 || !tmp_sids) {
3671 ads_msgfree(ads, res);
3672 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3675 if (num_sids) {
3676 *num_sids = tmp_num_sids;
3679 if (sids) {
3680 *sids = tmp_sids;
3683 if (user_sid) {
3684 *user_sid = tmp_user_sid;
3687 if (primary_group_sid) {
3688 *primary_group_sid = tmp_primary_group_sid;
3691 DEBUG(10,("ads_get_tokensids: returned %d sids\n", (int)tmp_num_sids + 2));
3693 ads_msgfree(ads, res);
3694 return ADS_ERROR_LDAP(LDAP_SUCCESS);
3698 * Find a sAMAccoutName in LDAP
3699 * @param ads connection to ads server
3700 * @param mem_ctx TALLOC_CTX for allocating sid array
3701 * @param samaccountname to search
3702 * @param uac_ret uint32 pointer userAccountControl attribute value
3703 * @param dn_ret pointer to dn
3704 * @return status of token query
3706 ADS_STATUS ads_find_samaccount(ADS_STRUCT *ads,
3707 TALLOC_CTX *mem_ctx,
3708 const char *samaccountname,
3709 uint32 *uac_ret,
3710 const char **dn_ret)
3712 ADS_STATUS status;
3713 const char *attrs[] = { "userAccountControl", NULL };
3714 const char *filter;
3715 LDAPMessage *res = NULL;
3716 char *dn = NULL;
3717 uint32 uac = 0;
3719 filter = talloc_asprintf(mem_ctx, "(&(objectclass=user)(sAMAccountName=%s))",
3720 samaccountname);
3721 if (filter == NULL) {
3722 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
3723 goto out;
3726 status = ads_do_search_all(ads, ads->config.bind_path,
3727 LDAP_SCOPE_SUBTREE,
3728 filter, attrs, &res);
3730 if (!ADS_ERR_OK(status)) {
3731 goto out;
3734 if (ads_count_replies(ads, res) != 1) {
3735 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3736 goto out;
3739 dn = ads_get_dn(ads, talloc_tos(), res);
3740 if (dn == NULL) {
3741 status = ADS_ERROR(LDAP_NO_MEMORY);
3742 goto out;
3745 if (!ads_pull_uint32(ads, res, "userAccountControl", &uac)) {
3746 status = ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
3747 goto out;
3750 if (uac_ret) {
3751 *uac_ret = uac;
3754 if (dn_ret) {
3755 *dn_ret = talloc_strdup(mem_ctx, dn);
3756 if (!*dn_ret) {
3757 status = ADS_ERROR(LDAP_NO_MEMORY);
3758 goto out;
3761 out:
3762 TALLOC_FREE(dn);
3763 ads_msgfree(ads, res);
3765 return status;
3769 * find our configuration path
3770 * @param ads connection to ads server
3771 * @param mem_ctx Pointer to talloc context
3772 * @param config_path Pointer to the config path
3773 * @return status of search
3775 ADS_STATUS ads_config_path(ADS_STRUCT *ads,
3776 TALLOC_CTX *mem_ctx,
3777 char **config_path)
3779 ADS_STATUS status;
3780 LDAPMessage *res = NULL;
3781 const char *config_context = NULL;
3782 const char *attrs[] = { "configurationNamingContext", NULL };
3784 status = ads_do_search(ads, "", LDAP_SCOPE_BASE,
3785 "(objectclass=*)", attrs, &res);
3786 if (!ADS_ERR_OK(status)) {
3787 return status;
3790 config_context = ads_pull_string(ads, mem_ctx, res,
3791 "configurationNamingContext");
3792 ads_msgfree(ads, res);
3793 if (!config_context) {
3794 return ADS_ERROR(LDAP_NO_MEMORY);
3797 if (config_path) {
3798 *config_path = talloc_strdup(mem_ctx, config_context);
3799 if (!*config_path) {
3800 return ADS_ERROR(LDAP_NO_MEMORY);
3804 return ADS_ERROR(LDAP_SUCCESS);
3808 * find the displayName of an extended right
3809 * @param ads connection to ads server
3810 * @param config_path The config path
3811 * @param mem_ctx Pointer to talloc context
3812 * @param GUID struct of the rightsGUID
3813 * @return status of search
3815 const char *ads_get_extended_right_name_by_guid(ADS_STRUCT *ads,
3816 const char *config_path,
3817 TALLOC_CTX *mem_ctx,
3818 const struct GUID *rights_guid)
3820 ADS_STATUS rc;
3821 LDAPMessage *res = NULL;
3822 char *expr = NULL;
3823 const char *attrs[] = { "displayName", NULL };
3824 const char *result = NULL;
3825 const char *path;
3827 if (!ads || !mem_ctx || !rights_guid) {
3828 goto done;
3831 expr = talloc_asprintf(mem_ctx, "(rightsGuid=%s)",
3832 GUID_string(mem_ctx, rights_guid));
3833 if (!expr) {
3834 goto done;
3837 path = talloc_asprintf(mem_ctx, "cn=Extended-Rights,%s", config_path);
3838 if (!path) {
3839 goto done;
3842 rc = ads_do_search_retry(ads, path, LDAP_SCOPE_SUBTREE,
3843 expr, attrs, &res);
3844 if (!ADS_ERR_OK(rc)) {
3845 goto done;
3848 if (ads_count_replies(ads, res) != 1) {
3849 goto done;
3852 result = ads_pull_string(ads, mem_ctx, res, "displayName");
3854 done:
3855 ads_msgfree(ads, res);
3856 return result;
3860 * verify or build and verify an account ou
3861 * @param mem_ctx Pointer to talloc context
3862 * @param ads connection to ads server
3863 * @param account_ou
3864 * @return status of search
3867 ADS_STATUS ads_check_ou_dn(TALLOC_CTX *mem_ctx,
3868 ADS_STRUCT *ads,
3869 const char **account_ou)
3871 char **exploded_dn;
3872 const char *name;
3873 char *ou_string;
3875 exploded_dn = ldap_explode_dn(*account_ou, 0);
3876 if (exploded_dn) {
3877 ldap_value_free(exploded_dn);
3878 return ADS_SUCCESS;
3881 ou_string = ads_ou_string(ads, *account_ou);
3882 if (!ou_string) {
3883 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX);
3886 name = talloc_asprintf(mem_ctx, "%s,%s", ou_string,
3887 ads->config.bind_path);
3888 SAFE_FREE(ou_string);
3890 if (!name) {
3891 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3894 exploded_dn = ldap_explode_dn(name, 0);
3895 if (!exploded_dn) {
3896 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX);
3898 ldap_value_free(exploded_dn);
3900 *account_ou = name;
3901 return ADS_SUCCESS;
3904 #endif