s3-libsmb/clidfs.c: remove cli_nt_error()
[Samba/gebeck_regimport.git] / source3 / libads / ldap.c
blob870d4bc11da8dde00d065f702a1a1ae2ff4d600d
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 if (to) {
104 /* Setup timeout */
105 gotalarm = 0;
106 CatchSignal(SIGALRM, gotalarm_sig);
107 alarm(to);
108 /* End setup timeout. */
111 ldp = ldap_open(server, port);
113 if (ldp == NULL) {
114 DEBUG(2,("Could not open connection to LDAP server %s:%d: %s\n",
115 server, port, strerror(errno)));
116 } else {
117 DEBUG(10, ("Connected to LDAP server '%s:%d'\n", server, port));
120 if (to) {
121 /* Teardown timeout. */
122 alarm(0);
123 CatchSignal(SIGALRM, SIG_IGN);
126 return ldp;
129 static int ldap_search_with_timeout(LDAP *ld,
130 LDAP_CONST char *base,
131 int scope,
132 LDAP_CONST char *filter,
133 char **attrs,
134 int attrsonly,
135 LDAPControl **sctrls,
136 LDAPControl **cctrls,
137 int sizelimit,
138 LDAPMessage **res )
140 int to = lp_ldap_timeout();
141 struct timeval timeout;
142 struct timeval *timeout_ptr = NULL;
143 int result;
145 /* Setup timeout for the ldap_search_ext_s call - local and remote. */
146 gotalarm = 0;
148 if (to) {
149 timeout.tv_sec = to;
150 timeout.tv_usec = 0;
151 timeout_ptr = &timeout;
153 /* Setup alarm timeout. */
154 CatchSignal(SIGALRM, gotalarm_sig);
155 /* Make the alarm time one second beyond
156 the timout we're setting for the
157 remote search timeout, to allow that
158 to fire in preference. */
159 alarm(to+1);
160 /* End setup timeout. */
164 result = ldap_search_ext_s(ld, base, scope, filter, attrs,
165 attrsonly, sctrls, cctrls, timeout_ptr,
166 sizelimit, res);
168 if (to) {
169 /* Teardown alarm timeout. */
170 CatchSignal(SIGALRM, SIG_IGN);
171 alarm(0);
174 if (gotalarm != 0)
175 return LDAP_TIMELIMIT_EXCEEDED;
178 * A bug in OpenLDAP means ldap_search_ext_s can return
179 * LDAP_SUCCESS but with a NULL res pointer. Cope with
180 * this. See bug #6279 for details. JRA.
183 if (*res == NULL) {
184 return LDAP_TIMELIMIT_EXCEEDED;
187 return result;
190 /**********************************************
191 Do client and server sitename match ?
192 **********************************************/
194 bool ads_sitename_match(ADS_STRUCT *ads)
196 if (ads->config.server_site_name == NULL &&
197 ads->config.client_site_name == NULL ) {
198 DEBUG(10,("ads_sitename_match: both null\n"));
199 return True;
201 if (ads->config.server_site_name &&
202 ads->config.client_site_name &&
203 strequal(ads->config.server_site_name,
204 ads->config.client_site_name)) {
205 DEBUG(10,("ads_sitename_match: name %s match\n", ads->config.server_site_name));
206 return True;
208 DEBUG(10,("ads_sitename_match: no match between server: %s and client: %s\n",
209 ads->config.server_site_name ? ads->config.server_site_name : "NULL",
210 ads->config.client_site_name ? ads->config.client_site_name : "NULL"));
211 return False;
214 /**********************************************
215 Is this the closest DC ?
216 **********************************************/
218 bool ads_closest_dc(ADS_STRUCT *ads)
220 if (ads->config.flags & NBT_SERVER_CLOSEST) {
221 DEBUG(10,("ads_closest_dc: NBT_SERVER_CLOSEST flag set\n"));
222 return True;
225 /* not sure if this can ever happen */
226 if (ads_sitename_match(ads)) {
227 DEBUG(10,("ads_closest_dc: NBT_SERVER_CLOSEST flag not set but sites match\n"));
228 return True;
231 if (ads->config.client_site_name == NULL) {
232 DEBUG(10,("ads_closest_dc: client belongs to no site\n"));
233 return True;
236 DEBUG(10,("ads_closest_dc: %s is not the closest DC\n",
237 ads->config.ldap_server_name));
239 return False;
244 try a connection to a given ldap server, returning True and setting the servers IP
245 in the ads struct if successful
247 static bool ads_try_connect(ADS_STRUCT *ads, const char *server, bool gc)
249 struct NETLOGON_SAM_LOGON_RESPONSE_EX cldap_reply;
250 TALLOC_CTX *frame = talloc_stackframe();
251 bool ret = false;
252 struct sockaddr_storage ss;
253 char addr[INET6_ADDRSTRLEN];
255 if (!server || !*server) {
256 TALLOC_FREE(frame);
257 return False;
260 if (!resolve_name(server, &ss, 0x20, true)) {
261 DEBUG(5,("ads_try_connect: unable to resolve name %s\n",
262 server ));
263 TALLOC_FREE(frame);
264 return false;
266 print_sockaddr(addr, sizeof(addr), &ss);
268 DEBUG(5,("ads_try_connect: sending CLDAP request to %s (realm: %s)\n",
269 addr, ads->server.realm));
271 ZERO_STRUCT( cldap_reply );
273 if ( !ads_cldap_netlogon_5(frame, &ss, ads->server.realm, &cldap_reply ) ) {
274 DEBUG(3,("ads_try_connect: CLDAP request %s failed.\n", addr));
275 ret = false;
276 goto out;
279 /* Check the CLDAP reply flags */
281 if ( !(cldap_reply.server_type & NBT_SERVER_LDAP) ) {
282 DEBUG(1,("ads_try_connect: %s's CLDAP reply says it is not an LDAP server!\n",
283 addr));
284 ret = false;
285 goto out;
288 /* Fill in the ads->config values */
290 SAFE_FREE(ads->config.realm);
291 SAFE_FREE(ads->config.bind_path);
292 SAFE_FREE(ads->config.ldap_server_name);
293 SAFE_FREE(ads->config.server_site_name);
294 SAFE_FREE(ads->config.client_site_name);
295 SAFE_FREE(ads->server.workgroup);
297 ads->config.flags = cldap_reply.server_type;
298 ads->config.ldap_server_name = SMB_STRDUP(cldap_reply.pdc_dns_name);
299 ads->config.realm = SMB_STRDUP(cldap_reply.dns_domain);
300 strupper_m(ads->config.realm);
301 ads->config.bind_path = ads_build_dn(ads->config.realm);
302 if (*cldap_reply.server_site) {
303 ads->config.server_site_name =
304 SMB_STRDUP(cldap_reply.server_site);
306 if (*cldap_reply.client_site) {
307 ads->config.client_site_name =
308 SMB_STRDUP(cldap_reply.client_site);
310 ads->server.workgroup = SMB_STRDUP(cldap_reply.domain_name);
312 ads->ldap.port = gc ? LDAP_GC_PORT : LDAP_PORT;
313 ads->ldap.ss = ss;
315 /* Store our site name. */
316 sitename_store( cldap_reply.domain_name, cldap_reply.client_site);
317 sitename_store( cldap_reply.dns_domain, cldap_reply.client_site);
319 ret = true;
321 out:
323 TALLOC_FREE(frame);
324 return ret;
327 /**********************************************************************
328 Try to find an AD dc using our internal name resolution routines
329 Try the realm first and then then workgroup name if netbios is not
330 disabled
331 **********************************************************************/
333 static NTSTATUS ads_find_dc(ADS_STRUCT *ads)
335 const char *c_domain;
336 const char *c_realm;
337 int count, i=0;
338 struct ip_service *ip_list;
339 const char *realm;
340 const char *domain;
341 bool got_realm = False;
342 bool use_own_domain = False;
343 char *sitename;
344 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
346 /* if the realm and workgroup are both empty, assume they are ours */
348 /* realm */
349 c_realm = ads->server.realm;
351 if ( !c_realm || !*c_realm ) {
352 /* special case where no realm and no workgroup means our own */
353 if ( !ads->server.workgroup || !*ads->server.workgroup ) {
354 use_own_domain = True;
355 c_realm = lp_realm();
359 if (c_realm && *c_realm)
360 got_realm = True;
362 /* we need to try once with the realm name and fallback to the
363 netbios domain name if we fail (if netbios has not been disabled */
365 if ( !got_realm && !lp_disable_netbios() ) {
366 c_realm = ads->server.workgroup;
367 if (!c_realm || !*c_realm) {
368 if ( use_own_domain )
369 c_realm = lp_workgroup();
373 if ( !c_realm || !*c_realm ) {
374 DEBUG(1, ("ads_find_dc: no realm or workgroup! Don't know "
375 "what to do\n"));
376 return NT_STATUS_INVALID_PARAMETER; /* rather need MISSING_PARAMETER ... */
379 if ( use_own_domain ) {
380 c_domain = lp_workgroup();
381 } else {
382 c_domain = ads->server.workgroup;
385 realm = c_realm;
386 domain = c_domain;
389 * In case of LDAP we use get_dc_name() as that
390 * creates the custom krb5.conf file
392 if (!(ads->auth.flags & ADS_AUTH_NO_BIND)) {
393 fstring srv_name;
394 struct sockaddr_storage ip_out;
396 DEBUG(6,("ads_find_dc: (ldap) looking for %s '%s'\n",
397 (got_realm ? "realm" : "domain"), realm));
399 if (get_dc_name(domain, realm, srv_name, &ip_out)) {
401 * we call ads_try_connect() to fill in the
402 * ads->config details
404 if (ads_try_connect(ads, srv_name, false)) {
405 return NT_STATUS_OK;
409 return NT_STATUS_NO_LOGON_SERVERS;
412 sitename = sitename_fetch(realm);
414 again:
416 DEBUG(6,("ads_find_dc: (cldap) looking for %s '%s'\n",
417 (got_realm ? "realm" : "domain"), realm));
419 status = get_sorted_dc_list(realm, sitename, &ip_list, &count, got_realm);
420 if (!NT_STATUS_IS_OK(status)) {
421 /* fall back to netbios if we can */
422 if ( got_realm && !lp_disable_netbios() ) {
423 got_realm = False;
424 goto again;
427 SAFE_FREE(sitename);
428 return status;
431 /* if we fail this loop, then giveup since all the IP addresses returned were dead */
432 for ( i=0; i<count; i++ ) {
433 char server[INET6_ADDRSTRLEN];
435 print_sockaddr(server, sizeof(server), &ip_list[i].ss);
437 if ( !NT_STATUS_IS_OK(check_negative_conn_cache(realm, server)) )
438 continue;
440 if (!got_realm) {
441 /* realm in this case is a workgroup name. We need
442 to ignore any IP addresses in the negative connection
443 cache that match ip addresses returned in the ad realm
444 case. It sucks that I have to reproduce the logic above... */
445 c_realm = ads->server.realm;
446 if ( !c_realm || !*c_realm ) {
447 if ( !ads->server.workgroup || !*ads->server.workgroup ) {
448 c_realm = lp_realm();
451 if (c_realm && *c_realm &&
452 !NT_STATUS_IS_OK(check_negative_conn_cache(c_realm, server))) {
453 /* Ensure we add the workgroup name for this
454 IP address as negative too. */
455 add_failed_connection_entry( realm, server, NT_STATUS_UNSUCCESSFUL );
456 continue;
460 if ( ads_try_connect(ads, server, false) ) {
461 SAFE_FREE(ip_list);
462 SAFE_FREE(sitename);
463 return NT_STATUS_OK;
466 /* keep track of failures */
467 add_failed_connection_entry( realm, server, NT_STATUS_UNSUCCESSFUL );
470 SAFE_FREE(ip_list);
472 /* In case we failed to contact one of our closest DC on our site we
473 * need to try to find another DC, retry with a site-less SRV DNS query
474 * - Guenther */
476 if (sitename) {
477 DEBUG(1,("ads_find_dc: failed to find a valid DC on our site (%s), "
478 "trying to find another DC\n", sitename));
479 SAFE_FREE(sitename);
480 namecache_delete(realm, 0x1C);
481 goto again;
484 return NT_STATUS_NO_LOGON_SERVERS;
487 /*********************************************************************
488 *********************************************************************/
490 static NTSTATUS ads_lookup_site(void)
492 ADS_STRUCT *ads = NULL;
493 ADS_STATUS ads_status;
494 NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
496 ads = ads_init(lp_realm(), NULL, NULL);
497 if (!ads) {
498 return NT_STATUS_NO_MEMORY;
501 /* The NO_BIND here will find a DC and set the client site
502 but not establish the TCP connection */
504 ads->auth.flags = ADS_AUTH_NO_BIND;
505 ads_status = ads_connect(ads);
506 if (!ADS_ERR_OK(ads_status)) {
507 DEBUG(4, ("ads_lookup_site: ads_connect to our realm failed! (%s)\n",
508 ads_errstr(ads_status)));
510 nt_status = ads_ntstatus(ads_status);
512 if (ads) {
513 ads_destroy(&ads);
516 return nt_status;
519 /*********************************************************************
520 *********************************************************************/
522 static const char* host_dns_domain(const char *fqdn)
524 const char *p = fqdn;
526 /* go to next char following '.' */
528 if ((p = strchr_m(fqdn, '.')) != NULL) {
529 p++;
532 return p;
537 * Connect to the Global Catalog server
538 * @param ads Pointer to an existing ADS_STRUCT
539 * @return status of connection
541 * Simple wrapper around ads_connect() that fills in the
542 * GC ldap server information
545 ADS_STATUS ads_connect_gc(ADS_STRUCT *ads)
547 TALLOC_CTX *frame = talloc_stackframe();
548 struct dns_rr_srv *gcs_list;
549 int num_gcs;
550 const char *realm = ads->server.realm;
551 NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
552 ADS_STATUS ads_status = ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
553 int i;
554 bool done = false;
555 char *sitename = NULL;
557 if (!realm)
558 realm = lp_realm();
560 if ((sitename = sitename_fetch(realm)) == NULL) {
561 ads_lookup_site();
562 sitename = sitename_fetch(realm);
565 do {
566 /* We try once with a sitename and once without
567 (unless we don't have a sitename and then we're
568 done */
570 if (sitename == NULL)
571 done = true;
573 nt_status = ads_dns_query_gcs(frame, realm, sitename,
574 &gcs_list, &num_gcs);
576 SAFE_FREE(sitename);
578 if (!NT_STATUS_IS_OK(nt_status)) {
579 ads_status = ADS_ERROR_NT(nt_status);
580 goto done;
583 /* Loop until we get a successful connection or have gone
584 through them all. When connecting a GC server, make sure that
585 the realm is the server's DNS name and not the forest root */
587 for (i=0; i<num_gcs; i++) {
588 ads->server.gc = true;
589 ads->server.ldap_server = SMB_STRDUP(gcs_list[i].hostname);
590 ads->server.realm = SMB_STRDUP(host_dns_domain(ads->server.ldap_server));
591 ads_status = ads_connect(ads);
592 if (ADS_ERR_OK(ads_status)) {
593 /* Reset the bind_dn to "". A Global Catalog server
594 may host multiple domain trees in a forest.
595 Windows 2003 GC server will accept "" as the search
596 path to imply search all domain trees in the forest */
598 SAFE_FREE(ads->config.bind_path);
599 ads->config.bind_path = SMB_STRDUP("");
602 goto done;
604 SAFE_FREE(ads->server.ldap_server);
605 SAFE_FREE(ads->server.realm);
608 TALLOC_FREE(gcs_list);
609 num_gcs = 0;
610 } while (!done);
612 done:
613 SAFE_FREE(sitename);
614 talloc_destroy(frame);
616 return ads_status;
621 * Connect to the LDAP server
622 * @param ads Pointer to an existing ADS_STRUCT
623 * @return status of connection
625 ADS_STATUS ads_connect(ADS_STRUCT *ads)
627 int version = LDAP_VERSION3;
628 ADS_STATUS status;
629 NTSTATUS ntstatus;
630 char addr[INET6_ADDRSTRLEN];
632 ZERO_STRUCT(ads->ldap);
633 ads->ldap.last_attempt = time_mono(NULL);
634 ads->ldap.wrap_type = ADS_SASLWRAP_TYPE_PLAIN;
636 /* try with a user specified server */
638 if (DEBUGLEVEL >= 11) {
639 char *s = NDR_PRINT_STRUCT_STRING(talloc_tos(), ads_struct, ads);
640 DEBUG(11,("ads_connect: entering\n"));
641 DEBUGADD(11,("%s\n", s));
642 TALLOC_FREE(s);
645 if (ads->server.ldap_server)
647 if (ads_try_connect(ads, ads->server.ldap_server, ads->server.gc)) {
648 goto got_connection;
651 /* The choice of which GC use is handled one level up in
652 ads_connect_gc(). If we continue on from here with
653 ads_find_dc() we will get GC searches on port 389 which
654 doesn't work. --jerry */
656 if (ads->server.gc == true) {
657 return ADS_ERROR(LDAP_OPERATIONS_ERROR);
661 ntstatus = ads_find_dc(ads);
662 if (NT_STATUS_IS_OK(ntstatus)) {
663 goto got_connection;
666 status = ADS_ERROR_NT(ntstatus);
667 goto out;
669 got_connection:
671 print_sockaddr(addr, sizeof(addr), &ads->ldap.ss);
672 DEBUG(3,("Successfully contacted LDAP server %s\n", addr));
674 if (!ads->auth.user_name) {
675 /* Must use the userPrincipalName value here or sAMAccountName
676 and not servicePrincipalName; found by Guenther Deschner */
678 if (asprintf(&ads->auth.user_name, "%s$", lp_netbios_name() ) == -1) {
679 DEBUG(0,("ads_connect: asprintf fail.\n"));
680 ads->auth.user_name = NULL;
684 if (!ads->auth.realm) {
685 ads->auth.realm = SMB_STRDUP(ads->config.realm);
688 if (!ads->auth.kdc_server) {
689 print_sockaddr(addr, sizeof(addr), &ads->ldap.ss);
690 ads->auth.kdc_server = SMB_STRDUP(addr);
693 /* If the caller() requested no LDAP bind, then we are done */
695 if (ads->auth.flags & ADS_AUTH_NO_BIND) {
696 status = ADS_SUCCESS;
697 goto out;
700 ads->ldap.mem_ctx = talloc_init("ads LDAP connection memory");
701 if (!ads->ldap.mem_ctx) {
702 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
703 goto out;
706 /* Otherwise setup the TCP LDAP session */
708 ads->ldap.ld = ldap_open_with_timeout(ads->config.ldap_server_name,
709 &ads->ldap.ss,
710 ads->ldap.port, lp_ldap_timeout());
711 if (ads->ldap.ld == NULL) {
712 status = ADS_ERROR(LDAP_OPERATIONS_ERROR);
713 goto out;
715 DEBUG(3,("Connected to LDAP server %s\n", ads->config.ldap_server_name));
717 /* cache the successful connection for workgroup and realm */
718 if (ads_closest_dc(ads)) {
719 saf_store( ads->server.workgroup, ads->config.ldap_server_name);
720 saf_store( ads->server.realm, ads->config.ldap_server_name);
723 ldap_set_option(ads->ldap.ld, LDAP_OPT_PROTOCOL_VERSION, &version);
725 if ( lp_ldap_ssl_ads() ) {
726 status = ADS_ERROR(smb_ldap_start_tls(ads->ldap.ld, version));
727 if (!ADS_ERR_OK(status)) {
728 goto out;
732 /* fill in the current time and offsets */
734 status = ads_current_time( ads );
735 if ( !ADS_ERR_OK(status) ) {
736 goto out;
739 /* Now do the bind */
741 if (ads->auth.flags & ADS_AUTH_ANON_BIND) {
742 status = ADS_ERROR(ldap_simple_bind_s(ads->ldap.ld, NULL, NULL));
743 goto out;
746 if (ads->auth.flags & ADS_AUTH_SIMPLE_BIND) {
747 status = ADS_ERROR(ldap_simple_bind_s(ads->ldap.ld, ads->auth.user_name, ads->auth.password));
748 goto out;
751 status = ads_sasl_bind(ads);
753 out:
754 if (DEBUGLEVEL >= 11) {
755 char *s = NDR_PRINT_STRUCT_STRING(talloc_tos(), ads_struct, ads);
756 DEBUG(11,("ads_connect: leaving with: %s\n",
757 ads_errstr(status)));
758 DEBUGADD(11,("%s\n", s));
759 TALLOC_FREE(s);
762 return status;
766 * Connect to the LDAP server using given credentials
767 * @param ads Pointer to an existing ADS_STRUCT
768 * @return status of connection
770 ADS_STATUS ads_connect_user_creds(ADS_STRUCT *ads)
772 ads->auth.flags |= ADS_AUTH_USER_CREDS;
774 return ads_connect(ads);
778 * Disconnect the LDAP server
779 * @param ads Pointer to an existing ADS_STRUCT
781 void ads_disconnect(ADS_STRUCT *ads)
783 if (ads->ldap.ld) {
784 ldap_unbind(ads->ldap.ld);
785 ads->ldap.ld = NULL;
787 if (ads->ldap.wrap_ops && ads->ldap.wrap_ops->disconnect) {
788 ads->ldap.wrap_ops->disconnect(ads);
790 if (ads->ldap.mem_ctx) {
791 talloc_free(ads->ldap.mem_ctx);
793 ZERO_STRUCT(ads->ldap);
797 Duplicate a struct berval into talloc'ed memory
799 static struct berval *dup_berval(TALLOC_CTX *ctx, const struct berval *in_val)
801 struct berval *value;
803 if (!in_val) return NULL;
805 value = talloc_zero(ctx, struct berval);
806 if (value == NULL)
807 return NULL;
808 if (in_val->bv_len == 0) return value;
810 value->bv_len = in_val->bv_len;
811 value->bv_val = (char *)talloc_memdup(ctx, in_val->bv_val,
812 in_val->bv_len);
813 return value;
817 Make a values list out of an array of (struct berval *)
819 static struct berval **ads_dup_values(TALLOC_CTX *ctx,
820 const struct berval **in_vals)
822 struct berval **values;
823 int i;
825 if (!in_vals) return NULL;
826 for (i=0; in_vals[i]; i++)
827 ; /* count values */
828 values = talloc_zero_array(ctx, struct berval *, i+1);
829 if (!values) return NULL;
831 for (i=0; in_vals[i]; i++) {
832 values[i] = dup_berval(ctx, in_vals[i]);
834 return values;
838 UTF8-encode a values list out of an array of (char *)
840 static char **ads_push_strvals(TALLOC_CTX *ctx, const char **in_vals)
842 char **values;
843 int i;
844 size_t size;
846 if (!in_vals) return NULL;
847 for (i=0; in_vals[i]; i++)
848 ; /* count values */
849 values = talloc_zero_array(ctx, char *, i+1);
850 if (!values) return NULL;
852 for (i=0; in_vals[i]; i++) {
853 if (!push_utf8_talloc(ctx, &values[i], in_vals[i], &size)) {
854 TALLOC_FREE(values);
855 return NULL;
858 return values;
862 Pull a (char *) array out of a UTF8-encoded values list
864 static char **ads_pull_strvals(TALLOC_CTX *ctx, const char **in_vals)
866 char **values;
867 int i;
868 size_t converted_size;
870 if (!in_vals) return NULL;
871 for (i=0; in_vals[i]; i++)
872 ; /* count values */
873 values = talloc_zero_array(ctx, char *, i+1);
874 if (!values) return NULL;
876 for (i=0; in_vals[i]; i++) {
877 if (!pull_utf8_talloc(ctx, &values[i], in_vals[i],
878 &converted_size)) {
879 DEBUG(0,("ads_pull_strvals: pull_utf8_talloc failed: "
880 "%s", strerror(errno)));
883 return values;
887 * Do a search with paged results. cookie must be null on the first
888 * call, and then returned on each subsequent call. It will be null
889 * again when the entire search is complete
890 * @param ads connection to ads server
891 * @param bind_path Base dn for the search
892 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
893 * @param expr Search expression - specified in local charset
894 * @param attrs Attributes to retrieve - specified in utf8 or ascii
895 * @param res ** which will contain results - free res* with ads_msgfree()
896 * @param count Number of entries retrieved on this page
897 * @param cookie The paged results cookie to be returned on subsequent calls
898 * @return status of search
900 static ADS_STATUS ads_do_paged_search_args(ADS_STRUCT *ads,
901 const char *bind_path,
902 int scope, const char *expr,
903 const char **attrs, void *args,
904 LDAPMessage **res,
905 int *count, struct berval **cookie)
907 int rc, i, version;
908 char *utf8_expr, *utf8_path, **search_attrs = NULL;
909 size_t converted_size;
910 LDAPControl PagedResults, NoReferrals, ExternalCtrl, *controls[4], **rcontrols;
911 BerElement *cookie_be = NULL;
912 struct berval *cookie_bv= NULL;
913 BerElement *ext_be = NULL;
914 struct berval *ext_bv= NULL;
916 TALLOC_CTX *ctx;
917 ads_control *external_control = (ads_control *) args;
919 *res = NULL;
921 if (!(ctx = talloc_init("ads_do_paged_search_args")))
922 return ADS_ERROR(LDAP_NO_MEMORY);
924 /* 0 means the conversion worked but the result was empty
925 so we only fail if it's -1. In any case, it always
926 at least nulls out the dest */
927 if (!push_utf8_talloc(ctx, &utf8_expr, expr, &converted_size) ||
928 !push_utf8_talloc(ctx, &utf8_path, bind_path, &converted_size))
930 rc = LDAP_NO_MEMORY;
931 goto done;
934 if (!attrs || !(*attrs))
935 search_attrs = NULL;
936 else {
937 /* This would be the utf8-encoded version...*/
938 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
939 if (!(search_attrs = str_list_copy(talloc_tos(), attrs))) {
940 rc = LDAP_NO_MEMORY;
941 goto done;
945 /* Paged results only available on ldap v3 or later */
946 ldap_get_option(ads->ldap.ld, LDAP_OPT_PROTOCOL_VERSION, &version);
947 if (version < LDAP_VERSION3) {
948 rc = LDAP_NOT_SUPPORTED;
949 goto done;
952 cookie_be = ber_alloc_t(LBER_USE_DER);
953 if (*cookie) {
954 ber_printf(cookie_be, "{iO}", (ber_int_t) 1000, *cookie);
955 ber_bvfree(*cookie); /* don't need it from last time */
956 *cookie = NULL;
957 } else {
958 ber_printf(cookie_be, "{io}", (ber_int_t) 1000, "", 0);
960 ber_flatten(cookie_be, &cookie_bv);
961 PagedResults.ldctl_oid = discard_const_p(char, ADS_PAGE_CTL_OID);
962 PagedResults.ldctl_iscritical = (char) 1;
963 PagedResults.ldctl_value.bv_len = cookie_bv->bv_len;
964 PagedResults.ldctl_value.bv_val = cookie_bv->bv_val;
966 NoReferrals.ldctl_oid = discard_const_p(char, ADS_NO_REFERRALS_OID);
967 NoReferrals.ldctl_iscritical = (char) 0;
968 NoReferrals.ldctl_value.bv_len = 0;
969 NoReferrals.ldctl_value.bv_val = discard_const_p(char, "");
971 if (external_control &&
972 (strequal(external_control->control, ADS_EXTENDED_DN_OID) ||
973 strequal(external_control->control, ADS_SD_FLAGS_OID))) {
975 ExternalCtrl.ldctl_oid = discard_const_p(char, external_control->control);
976 ExternalCtrl.ldctl_iscritical = (char) external_control->critical;
978 /* win2k does not accept a ldctl_value beeing passed in */
980 if (external_control->val != 0) {
982 if ((ext_be = ber_alloc_t(LBER_USE_DER)) == NULL ) {
983 rc = LDAP_NO_MEMORY;
984 goto done;
987 if ((ber_printf(ext_be, "{i}", (ber_int_t) external_control->val)) == -1) {
988 rc = LDAP_NO_MEMORY;
989 goto done;
991 if ((ber_flatten(ext_be, &ext_bv)) == -1) {
992 rc = LDAP_NO_MEMORY;
993 goto done;
996 ExternalCtrl.ldctl_value.bv_len = ext_bv->bv_len;
997 ExternalCtrl.ldctl_value.bv_val = ext_bv->bv_val;
999 } else {
1000 ExternalCtrl.ldctl_value.bv_len = 0;
1001 ExternalCtrl.ldctl_value.bv_val = NULL;
1004 controls[0] = &NoReferrals;
1005 controls[1] = &PagedResults;
1006 controls[2] = &ExternalCtrl;
1007 controls[3] = NULL;
1009 } else {
1010 controls[0] = &NoReferrals;
1011 controls[1] = &PagedResults;
1012 controls[2] = NULL;
1015 /* we need to disable referrals as the openldap libs don't
1016 handle them and paged results at the same time. Using them
1017 together results in the result record containing the server
1018 page control being removed from the result list (tridge/jmcd)
1020 leaving this in despite the control that says don't generate
1021 referrals, in case the server doesn't support it (jmcd)
1023 ldap_set_option(ads->ldap.ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
1025 rc = ldap_search_with_timeout(ads->ldap.ld, utf8_path, scope, utf8_expr,
1026 search_attrs, 0, controls,
1027 NULL, LDAP_NO_LIMIT,
1028 (LDAPMessage **)res);
1030 ber_free(cookie_be, 1);
1031 ber_bvfree(cookie_bv);
1033 if (rc) {
1034 DEBUG(3,("ads_do_paged_search_args: ldap_search_with_timeout(%s) -> %s\n", expr,
1035 ldap_err2string(rc)));
1036 goto done;
1039 rc = ldap_parse_result(ads->ldap.ld, *res, NULL, NULL, NULL,
1040 NULL, &rcontrols, 0);
1042 if (!rcontrols) {
1043 goto done;
1046 for (i=0; rcontrols[i]; i++) {
1047 if (strcmp(ADS_PAGE_CTL_OID, rcontrols[i]->ldctl_oid) == 0) {
1048 cookie_be = ber_init(&rcontrols[i]->ldctl_value);
1049 ber_scanf(cookie_be,"{iO}", (ber_int_t *) count,
1050 &cookie_bv);
1051 /* the berval is the cookie, but must be freed when
1052 it is all done */
1053 if (cookie_bv->bv_len) /* still more to do */
1054 *cookie=ber_bvdup(cookie_bv);
1055 else
1056 *cookie=NULL;
1057 ber_bvfree(cookie_bv);
1058 ber_free(cookie_be, 1);
1059 break;
1062 ldap_controls_free(rcontrols);
1064 done:
1065 talloc_destroy(ctx);
1067 if (ext_be) {
1068 ber_free(ext_be, 1);
1071 if (ext_bv) {
1072 ber_bvfree(ext_bv);
1075 /* if/when we decide to utf8-encode attrs, take out this next line */
1076 TALLOC_FREE(search_attrs);
1078 return ADS_ERROR(rc);
1081 static ADS_STATUS ads_do_paged_search(ADS_STRUCT *ads, const char *bind_path,
1082 int scope, const char *expr,
1083 const char **attrs, LDAPMessage **res,
1084 int *count, struct berval **cookie)
1086 return ads_do_paged_search_args(ads, bind_path, scope, expr, attrs, NULL, res, count, cookie);
1091 * Get all results for a search. This uses ads_do_paged_search() to return
1092 * all entries in a large search.
1093 * @param ads connection to ads server
1094 * @param bind_path Base dn for the search
1095 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1096 * @param expr Search expression
1097 * @param attrs Attributes to retrieve
1098 * @param res ** which will contain results - free res* with ads_msgfree()
1099 * @return status of search
1101 ADS_STATUS ads_do_search_all_args(ADS_STRUCT *ads, const char *bind_path,
1102 int scope, const char *expr,
1103 const char **attrs, void *args,
1104 LDAPMessage **res)
1106 struct berval *cookie = NULL;
1107 int count = 0;
1108 ADS_STATUS status;
1110 *res = NULL;
1111 status = ads_do_paged_search_args(ads, bind_path, scope, expr, attrs, args, res,
1112 &count, &cookie);
1114 if (!ADS_ERR_OK(status))
1115 return status;
1117 #ifdef HAVE_LDAP_ADD_RESULT_ENTRY
1118 while (cookie) {
1119 LDAPMessage *res2 = NULL;
1120 ADS_STATUS status2;
1121 LDAPMessage *msg, *next;
1123 status2 = ads_do_paged_search_args(ads, bind_path, scope, expr,
1124 attrs, args, &res2, &count, &cookie);
1126 if (!ADS_ERR_OK(status2)) break;
1128 /* this relies on the way that ldap_add_result_entry() works internally. I hope
1129 that this works on all ldap libs, but I have only tested with openldap */
1130 for (msg = ads_first_message(ads, res2); msg; msg = next) {
1131 next = ads_next_message(ads, msg);
1132 ldap_add_result_entry((LDAPMessage **)res, msg);
1134 /* note that we do not free res2, as the memory is now
1135 part of the main returned list */
1137 #else
1138 DEBUG(0, ("no ldap_add_result_entry() support in LDAP libs!\n"));
1139 status = ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
1140 #endif
1142 return status;
1145 ADS_STATUS ads_do_search_all(ADS_STRUCT *ads, const char *bind_path,
1146 int scope, const char *expr,
1147 const char **attrs, LDAPMessage **res)
1149 return ads_do_search_all_args(ads, bind_path, scope, expr, attrs, NULL, res);
1152 ADS_STATUS ads_do_search_all_sd_flags(ADS_STRUCT *ads, const char *bind_path,
1153 int scope, const char *expr,
1154 const char **attrs, uint32 sd_flags,
1155 LDAPMessage **res)
1157 ads_control args;
1159 args.control = ADS_SD_FLAGS_OID;
1160 args.val = sd_flags;
1161 args.critical = True;
1163 return ads_do_search_all_args(ads, bind_path, scope, expr, attrs, &args, res);
1168 * Run a function on all results for a search. Uses ads_do_paged_search() and
1169 * runs the function as each page is returned, using ads_process_results()
1170 * @param ads connection to ads server
1171 * @param bind_path Base dn for the search
1172 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1173 * @param expr Search expression - specified in local charset
1174 * @param attrs Attributes to retrieve - specified in UTF-8 or ascii
1175 * @param fn Function which takes attr name, values list, and data_area
1176 * @param data_area Pointer which is passed to function on each call
1177 * @return status of search
1179 ADS_STATUS ads_do_search_all_fn(ADS_STRUCT *ads, const char *bind_path,
1180 int scope, const char *expr, const char **attrs,
1181 bool (*fn)(ADS_STRUCT *, char *, void **, void *),
1182 void *data_area)
1184 struct berval *cookie = NULL;
1185 int count = 0;
1186 ADS_STATUS status;
1187 LDAPMessage *res;
1189 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs, &res,
1190 &count, &cookie);
1192 if (!ADS_ERR_OK(status)) return status;
1194 ads_process_results(ads, res, fn, data_area);
1195 ads_msgfree(ads, res);
1197 while (cookie) {
1198 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs,
1199 &res, &count, &cookie);
1201 if (!ADS_ERR_OK(status)) break;
1203 ads_process_results(ads, res, fn, data_area);
1204 ads_msgfree(ads, res);
1207 return status;
1211 * Do a search with a timeout.
1212 * @param ads connection to ads server
1213 * @param bind_path Base dn for the search
1214 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1215 * @param expr Search expression
1216 * @param attrs Attributes to retrieve
1217 * @param res ** which will contain results - free res* with ads_msgfree()
1218 * @return status of search
1220 ADS_STATUS ads_do_search(ADS_STRUCT *ads, const char *bind_path, int scope,
1221 const char *expr,
1222 const char **attrs, LDAPMessage **res)
1224 int rc;
1225 char *utf8_expr, *utf8_path, **search_attrs = NULL;
1226 size_t converted_size;
1227 TALLOC_CTX *ctx;
1229 *res = NULL;
1230 if (!(ctx = talloc_init("ads_do_search"))) {
1231 DEBUG(1,("ads_do_search: talloc_init() failed!"));
1232 return ADS_ERROR(LDAP_NO_MEMORY);
1235 /* 0 means the conversion worked but the result was empty
1236 so we only fail if it's negative. In any case, it always
1237 at least nulls out the dest */
1238 if (!push_utf8_talloc(ctx, &utf8_expr, expr, &converted_size) ||
1239 !push_utf8_talloc(ctx, &utf8_path, bind_path, &converted_size))
1241 DEBUG(1,("ads_do_search: push_utf8_talloc() failed!"));
1242 rc = LDAP_NO_MEMORY;
1243 goto done;
1246 if (!attrs || !(*attrs))
1247 search_attrs = NULL;
1248 else {
1249 /* This would be the utf8-encoded version...*/
1250 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
1251 if (!(search_attrs = str_list_copy(talloc_tos(), attrs)))
1253 DEBUG(1,("ads_do_search: str_list_copy() failed!"));
1254 rc = LDAP_NO_MEMORY;
1255 goto done;
1259 /* see the note in ads_do_paged_search - we *must* disable referrals */
1260 ldap_set_option(ads->ldap.ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
1262 rc = ldap_search_with_timeout(ads->ldap.ld, utf8_path, scope, utf8_expr,
1263 search_attrs, 0, NULL, NULL,
1264 LDAP_NO_LIMIT,
1265 (LDAPMessage **)res);
1267 if (rc == LDAP_SIZELIMIT_EXCEEDED) {
1268 DEBUG(3,("Warning! sizelimit exceeded in ldap. Truncating.\n"));
1269 rc = 0;
1272 done:
1273 talloc_destroy(ctx);
1274 /* if/when we decide to utf8-encode attrs, take out this next line */
1275 TALLOC_FREE(search_attrs);
1276 return ADS_ERROR(rc);
1279 * Do a general ADS search
1280 * @param ads connection to ads server
1281 * @param res ** which will contain results - free res* with ads_msgfree()
1282 * @param expr Search expression
1283 * @param attrs Attributes to retrieve
1284 * @return status of search
1286 ADS_STATUS ads_search(ADS_STRUCT *ads, LDAPMessage **res,
1287 const char *expr, const char **attrs)
1289 return ads_do_search(ads, ads->config.bind_path, LDAP_SCOPE_SUBTREE,
1290 expr, attrs, res);
1294 * Do a search on a specific DistinguishedName
1295 * @param ads connection to ads server
1296 * @param res ** which will contain results - free res* with ads_msgfree()
1297 * @param dn DistinguishName to search
1298 * @param attrs Attributes to retrieve
1299 * @return status of search
1301 ADS_STATUS ads_search_dn(ADS_STRUCT *ads, LDAPMessage **res,
1302 const char *dn, const char **attrs)
1304 return ads_do_search(ads, dn, LDAP_SCOPE_BASE, "(objectclass=*)",
1305 attrs, res);
1309 * Free up memory from a ads_search
1310 * @param ads connection to ads server
1311 * @param msg Search results to free
1313 void ads_msgfree(ADS_STRUCT *ads, LDAPMessage *msg)
1315 if (!msg) return;
1316 ldap_msgfree(msg);
1320 * Get a dn from search results
1321 * @param ads connection to ads server
1322 * @param msg Search result
1323 * @return dn string
1325 char *ads_get_dn(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, LDAPMessage *msg)
1327 char *utf8_dn, *unix_dn;
1328 size_t converted_size;
1330 utf8_dn = ldap_get_dn(ads->ldap.ld, msg);
1332 if (!utf8_dn) {
1333 DEBUG (5, ("ads_get_dn: ldap_get_dn failed\n"));
1334 return NULL;
1337 if (!pull_utf8_talloc(mem_ctx, &unix_dn, utf8_dn, &converted_size)) {
1338 DEBUG(0,("ads_get_dn: string conversion failure utf8 [%s]\n",
1339 utf8_dn ));
1340 return NULL;
1342 ldap_memfree(utf8_dn);
1343 return unix_dn;
1347 * Get the parent from a dn
1348 * @param dn the dn to return the parent from
1349 * @return parent dn string
1351 char *ads_parent_dn(const char *dn)
1353 char *p;
1355 if (dn == NULL) {
1356 return NULL;
1359 p = strchr(dn, ',');
1361 if (p == NULL) {
1362 return NULL;
1365 return p+1;
1369 * Find a machine account given a hostname
1370 * @param ads connection to ads server
1371 * @param res ** which will contain results - free res* with ads_msgfree()
1372 * @param host Hostname to search for
1373 * @return status of search
1375 ADS_STATUS ads_find_machine_acct(ADS_STRUCT *ads, LDAPMessage **res,
1376 const char *machine)
1378 ADS_STATUS status;
1379 char *expr;
1380 const char *attrs[] = {"*", "nTSecurityDescriptor", NULL};
1382 *res = NULL;
1384 /* the easiest way to find a machine account anywhere in the tree
1385 is to look for hostname$ */
1386 if (asprintf(&expr, "(samAccountName=%s$)", machine) == -1) {
1387 DEBUG(1, ("asprintf failed!\n"));
1388 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1391 status = ads_search(ads, res, expr, attrs);
1392 SAFE_FREE(expr);
1393 return status;
1397 * Initialize a list of mods to be used in a modify request
1398 * @param ctx An initialized TALLOC_CTX
1399 * @return allocated ADS_MODLIST
1401 ADS_MODLIST ads_init_mods(TALLOC_CTX *ctx)
1403 #define ADS_MODLIST_ALLOC_SIZE 10
1404 LDAPMod **mods;
1406 if ((mods = talloc_zero_array(ctx, LDAPMod *, ADS_MODLIST_ALLOC_SIZE + 1)))
1407 /* -1 is safety to make sure we don't go over the end.
1408 need to reset it to NULL before doing ldap modify */
1409 mods[ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1411 return (ADS_MODLIST)mods;
1416 add an attribute to the list, with values list already constructed
1418 static ADS_STATUS ads_modlist_add(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1419 int mod_op, const char *name,
1420 const void *_invals)
1422 const void **invals = (const void **)_invals;
1423 int curmod;
1424 LDAPMod **modlist = (LDAPMod **) *mods;
1425 struct berval **ber_values = NULL;
1426 char **char_values = NULL;
1428 if (!invals) {
1429 mod_op = LDAP_MOD_DELETE;
1430 } else {
1431 if (mod_op & LDAP_MOD_BVALUES)
1432 ber_values = ads_dup_values(ctx,
1433 (const struct berval **)invals);
1434 else
1435 char_values = ads_push_strvals(ctx,
1436 (const char **) invals);
1439 /* find the first empty slot */
1440 for (curmod=0; modlist[curmod] && modlist[curmod] != (LDAPMod *) -1;
1441 curmod++);
1442 if (modlist[curmod] == (LDAPMod *) -1) {
1443 if (!(modlist = talloc_realloc(ctx, modlist, LDAPMod *,
1444 curmod+ADS_MODLIST_ALLOC_SIZE+1)))
1445 return ADS_ERROR(LDAP_NO_MEMORY);
1446 memset(&modlist[curmod], 0,
1447 ADS_MODLIST_ALLOC_SIZE*sizeof(LDAPMod *));
1448 modlist[curmod+ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1449 *mods = (ADS_MODLIST)modlist;
1452 if (!(modlist[curmod] = talloc_zero(ctx, LDAPMod)))
1453 return ADS_ERROR(LDAP_NO_MEMORY);
1454 modlist[curmod]->mod_type = talloc_strdup(ctx, name);
1455 if (mod_op & LDAP_MOD_BVALUES) {
1456 modlist[curmod]->mod_bvalues = ber_values;
1457 } else if (mod_op & LDAP_MOD_DELETE) {
1458 modlist[curmod]->mod_values = NULL;
1459 } else {
1460 modlist[curmod]->mod_values = char_values;
1463 modlist[curmod]->mod_op = mod_op;
1464 return ADS_ERROR(LDAP_SUCCESS);
1468 * Add a single string value to a mod list
1469 * @param ctx An initialized TALLOC_CTX
1470 * @param mods An initialized ADS_MODLIST
1471 * @param name The attribute name to add
1472 * @param val The value to add - NULL means DELETE
1473 * @return ADS STATUS indicating success of add
1475 ADS_STATUS ads_mod_str(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1476 const char *name, const char *val)
1478 const char *values[2];
1480 values[0] = val;
1481 values[1] = NULL;
1483 if (!val)
1484 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1485 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE, name, values);
1489 * Add an array of string values 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 vals The array of string values to add - NULL means DELETE
1494 * @return ADS STATUS indicating success of add
1496 ADS_STATUS ads_mod_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1497 const char *name, const char **vals)
1499 if (!vals)
1500 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1501 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE,
1502 name, (const void **) vals);
1505 #if 0
1507 * Add a single ber-encoded value to a mod list
1508 * @param ctx An initialized TALLOC_CTX
1509 * @param mods An initialized ADS_MODLIST
1510 * @param name The attribute name to add
1511 * @param val The value to add - NULL means DELETE
1512 * @return ADS STATUS indicating success of add
1514 static ADS_STATUS ads_mod_ber(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1515 const char *name, const struct berval *val)
1517 const struct berval *values[2];
1519 values[0] = val;
1520 values[1] = NULL;
1521 if (!val)
1522 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1523 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE|LDAP_MOD_BVALUES,
1524 name, (const void **) values);
1526 #endif
1529 * Perform an ldap modify
1530 * @param ads connection to ads server
1531 * @param mod_dn DistinguishedName to modify
1532 * @param mods list of modifications to perform
1533 * @return status of modify
1535 ADS_STATUS ads_gen_mod(ADS_STRUCT *ads, const char *mod_dn, ADS_MODLIST mods)
1537 int ret,i;
1538 char *utf8_dn = NULL;
1539 size_t converted_size;
1541 this control is needed to modify that contains a currently
1542 non-existent attribute (but allowable for the object) to run
1544 LDAPControl PermitModify = {
1545 discard_const_p(char, ADS_PERMIT_MODIFY_OID),
1546 {0, NULL},
1547 (char) 1};
1548 LDAPControl *controls[2];
1550 controls[0] = &PermitModify;
1551 controls[1] = NULL;
1553 if (!push_utf8_talloc(talloc_tos(), &utf8_dn, mod_dn, &converted_size)) {
1554 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1557 /* find the end of the list, marked by NULL or -1 */
1558 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1559 /* make sure the end of the list is NULL */
1560 mods[i] = NULL;
1561 ret = ldap_modify_ext_s(ads->ldap.ld, utf8_dn,
1562 (LDAPMod **) mods, controls, NULL);
1563 TALLOC_FREE(utf8_dn);
1564 return ADS_ERROR(ret);
1568 * Perform an ldap add
1569 * @param ads connection to ads server
1570 * @param new_dn DistinguishedName to add
1571 * @param mods list of attributes and values for DN
1572 * @return status of add
1574 ADS_STATUS ads_gen_add(ADS_STRUCT *ads, const char *new_dn, ADS_MODLIST mods)
1576 int ret, i;
1577 char *utf8_dn = NULL;
1578 size_t converted_size;
1580 if (!push_utf8_talloc(talloc_tos(), &utf8_dn, new_dn, &converted_size)) {
1581 DEBUG(1, ("ads_gen_add: push_utf8_talloc failed!"));
1582 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1585 /* find the end of the list, marked by NULL or -1 */
1586 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1587 /* make sure the end of the list is NULL */
1588 mods[i] = NULL;
1590 ret = ldap_add_s(ads->ldap.ld, utf8_dn, (LDAPMod**)mods);
1591 TALLOC_FREE(utf8_dn);
1592 return ADS_ERROR(ret);
1596 * Delete a DistinguishedName
1597 * @param ads connection to ads server
1598 * @param new_dn DistinguishedName to delete
1599 * @return status of delete
1601 ADS_STATUS ads_del_dn(ADS_STRUCT *ads, char *del_dn)
1603 int ret;
1604 char *utf8_dn = NULL;
1605 size_t converted_size;
1606 if (!push_utf8_talloc(talloc_tos(), &utf8_dn, del_dn, &converted_size)) {
1607 DEBUG(1, ("ads_del_dn: push_utf8_talloc failed!"));
1608 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1611 ret = ldap_delete_s(ads->ldap.ld, utf8_dn);
1612 TALLOC_FREE(utf8_dn);
1613 return ADS_ERROR(ret);
1617 * Build an org unit string
1618 * if org unit is Computers or blank then assume a container, otherwise
1619 * assume a / separated list of organisational units.
1620 * jmcd: '\' is now used for escapes so certain chars can be in the ou (e.g. #)
1621 * @param ads connection to ads server
1622 * @param org_unit Organizational unit
1623 * @return org unit string - caller must free
1625 char *ads_ou_string(ADS_STRUCT *ads, const char *org_unit)
1627 char *ret = NULL;
1629 if (!org_unit || !*org_unit) {
1631 ret = ads_default_ou_string(ads, DS_GUID_COMPUTERS_CONTAINER);
1633 /* samba4 might not yet respond to a wellknownobject-query */
1634 return ret ? ret : SMB_STRDUP("cn=Computers");
1637 if (strequal(org_unit, "Computers")) {
1638 return SMB_STRDUP("cn=Computers");
1641 /* jmcd: removed "\\" from the separation chars, because it is
1642 needed as an escape for chars like '#' which are valid in an
1643 OU name */
1644 return ads_build_path(org_unit, "/", "ou=", 1);
1648 * Get a org unit string for a well-known GUID
1649 * @param ads connection to ads server
1650 * @param wknguid Well known GUID
1651 * @return org unit string - caller must free
1653 char *ads_default_ou_string(ADS_STRUCT *ads, const char *wknguid)
1655 ADS_STATUS status;
1656 LDAPMessage *res = NULL;
1657 char *base, *wkn_dn = NULL, *ret = NULL, **wkn_dn_exp = NULL,
1658 **bind_dn_exp = NULL;
1659 const char *attrs[] = {"distinguishedName", NULL};
1660 int new_ln, wkn_ln, bind_ln, i;
1662 if (wknguid == NULL) {
1663 return NULL;
1666 if (asprintf(&base, "<WKGUID=%s,%s>", wknguid, ads->config.bind_path ) == -1) {
1667 DEBUG(1, ("asprintf failed!\n"));
1668 return NULL;
1671 status = ads_search_dn(ads, &res, base, attrs);
1672 if (!ADS_ERR_OK(status)) {
1673 DEBUG(1,("Failed while searching for: %s\n", base));
1674 goto out;
1677 if (ads_count_replies(ads, res) != 1) {
1678 goto out;
1681 /* substitute the bind-path from the well-known-guid-search result */
1682 wkn_dn = ads_get_dn(ads, talloc_tos(), res);
1683 if (!wkn_dn) {
1684 goto out;
1687 wkn_dn_exp = ldap_explode_dn(wkn_dn, 0);
1688 if (!wkn_dn_exp) {
1689 goto out;
1692 bind_dn_exp = ldap_explode_dn(ads->config.bind_path, 0);
1693 if (!bind_dn_exp) {
1694 goto out;
1697 for (wkn_ln=0; wkn_dn_exp[wkn_ln]; wkn_ln++)
1699 for (bind_ln=0; bind_dn_exp[bind_ln]; bind_ln++)
1702 new_ln = wkn_ln - bind_ln;
1704 ret = SMB_STRDUP(wkn_dn_exp[0]);
1705 if (!ret) {
1706 goto out;
1709 for (i=1; i < new_ln; i++) {
1710 char *s = NULL;
1712 if (asprintf(&s, "%s,%s", ret, wkn_dn_exp[i]) == -1) {
1713 SAFE_FREE(ret);
1714 goto out;
1717 SAFE_FREE(ret);
1718 ret = SMB_STRDUP(s);
1719 free(s);
1720 if (!ret) {
1721 goto out;
1725 out:
1726 SAFE_FREE(base);
1727 ads_msgfree(ads, res);
1728 TALLOC_FREE(wkn_dn);
1729 if (wkn_dn_exp) {
1730 ldap_value_free(wkn_dn_exp);
1732 if (bind_dn_exp) {
1733 ldap_value_free(bind_dn_exp);
1736 return ret;
1740 * Adds (appends) an item to an attribute array, rather then
1741 * replacing the whole list
1742 * @param ctx An initialized TALLOC_CTX
1743 * @param mods An initialized ADS_MODLIST
1744 * @param name name of the ldap attribute to append to
1745 * @param vals an array of values to add
1746 * @return status of addition
1749 ADS_STATUS ads_add_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1750 const char *name, const char **vals)
1752 return ads_modlist_add(ctx, mods, LDAP_MOD_ADD, name,
1753 (const void *) vals);
1757 * Determines the an account's current KVNO via an LDAP lookup
1758 * @param ads An initialized ADS_STRUCT
1759 * @param account_name the NT samaccountname.
1760 * @return the kvno for the account, or -1 in case of a failure.
1763 uint32 ads_get_kvno(ADS_STRUCT *ads, const char *account_name)
1765 LDAPMessage *res = NULL;
1766 uint32 kvno = (uint32)-1; /* -1 indicates a failure */
1767 char *filter;
1768 const char *attrs[] = {"msDS-KeyVersionNumber", NULL};
1769 char *dn_string = NULL;
1770 ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS);
1772 DEBUG(5,("ads_get_kvno: Searching for account %s\n", account_name));
1773 if (asprintf(&filter, "(samAccountName=%s)", account_name) == -1) {
1774 return kvno;
1776 ret = ads_search(ads, &res, filter, attrs);
1777 SAFE_FREE(filter);
1778 if (!ADS_ERR_OK(ret) || (ads_count_replies(ads, res) != 1)) {
1779 DEBUG(1,("ads_get_kvno: Account for %s not found.\n", account_name));
1780 ads_msgfree(ads, res);
1781 return kvno;
1784 dn_string = ads_get_dn(ads, talloc_tos(), res);
1785 if (!dn_string) {
1786 DEBUG(0,("ads_get_kvno: out of memory.\n"));
1787 ads_msgfree(ads, res);
1788 return kvno;
1790 DEBUG(5,("ads_get_kvno: Using: %s\n", dn_string));
1791 TALLOC_FREE(dn_string);
1793 /* ---------------------------------------------------------
1794 * 0 is returned as a default KVNO from this point on...
1795 * This is done because Windows 2000 does not support key
1796 * version numbers. Chances are that a failure in the next
1797 * step is simply due to Windows 2000 being used for a
1798 * domain controller. */
1799 kvno = 0;
1801 if (!ads_pull_uint32(ads, res, "msDS-KeyVersionNumber", &kvno)) {
1802 DEBUG(3,("ads_get_kvno: Error Determining KVNO!\n"));
1803 DEBUG(3,("ads_get_kvno: Windows 2000 does not support KVNO's, so this may be normal.\n"));
1804 ads_msgfree(ads, res);
1805 return kvno;
1808 /* Success */
1809 DEBUG(5,("ads_get_kvno: Looked Up KVNO of: %d\n", kvno));
1810 ads_msgfree(ads, res);
1811 return kvno;
1815 * Determines the computer account's current KVNO via an LDAP lookup
1816 * @param ads An initialized ADS_STRUCT
1817 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1818 * @return the kvno for the computer account, or -1 in case of a failure.
1821 uint32_t ads_get_machine_kvno(ADS_STRUCT *ads, const char *machine_name)
1823 char *computer_account = NULL;
1824 uint32_t kvno = -1;
1826 if (asprintf(&computer_account, "%s$", machine_name) < 0) {
1827 return kvno;
1830 kvno = ads_get_kvno(ads, computer_account);
1831 free(computer_account);
1833 return kvno;
1837 * This clears out all registered spn's for a given hostname
1838 * @param ads An initilaized ADS_STRUCT
1839 * @param machine_name the NetBIOS name of the computer.
1840 * @return 0 upon success, non-zero otherwise.
1843 ADS_STATUS ads_clear_service_principal_names(ADS_STRUCT *ads, const char *machine_name)
1845 TALLOC_CTX *ctx;
1846 LDAPMessage *res = NULL;
1847 ADS_MODLIST mods;
1848 const char *servicePrincipalName[1] = {NULL};
1849 ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS);
1850 char *dn_string = NULL;
1852 ret = ads_find_machine_acct(ads, &res, machine_name);
1853 if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
1854 DEBUG(5,("ads_clear_service_principal_names: WARNING: Host Account for %s not found... skipping operation.\n", machine_name));
1855 DEBUG(5,("ads_clear_service_principal_names: WARNING: Service Principals for %s have NOT been cleared.\n", machine_name));
1856 ads_msgfree(ads, res);
1857 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1860 DEBUG(5,("ads_clear_service_principal_names: Host account for %s found\n", machine_name));
1861 ctx = talloc_init("ads_clear_service_principal_names");
1862 if (!ctx) {
1863 ads_msgfree(ads, res);
1864 return ADS_ERROR(LDAP_NO_MEMORY);
1867 if (!(mods = ads_init_mods(ctx))) {
1868 talloc_destroy(ctx);
1869 ads_msgfree(ads, res);
1870 return ADS_ERROR(LDAP_NO_MEMORY);
1872 ret = ads_mod_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1873 if (!ADS_ERR_OK(ret)) {
1874 DEBUG(1,("ads_clear_service_principal_names: Error creating strlist.\n"));
1875 ads_msgfree(ads, res);
1876 talloc_destroy(ctx);
1877 return ret;
1879 dn_string = ads_get_dn(ads, talloc_tos(), res);
1880 if (!dn_string) {
1881 talloc_destroy(ctx);
1882 ads_msgfree(ads, res);
1883 return ADS_ERROR(LDAP_NO_MEMORY);
1885 ret = ads_gen_mod(ads, dn_string, mods);
1886 TALLOC_FREE(dn_string);
1887 if (!ADS_ERR_OK(ret)) {
1888 DEBUG(1,("ads_clear_service_principal_names: Error: Updating Service Principals for machine %s in LDAP\n",
1889 machine_name));
1890 ads_msgfree(ads, res);
1891 talloc_destroy(ctx);
1892 return ret;
1895 ads_msgfree(ads, res);
1896 talloc_destroy(ctx);
1897 return ret;
1901 * This adds a service principal name to an existing computer account
1902 * (found by hostname) in AD.
1903 * @param ads An initialized ADS_STRUCT
1904 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1905 * @param my_fqdn The fully qualified DNS name of the machine
1906 * @param spn A string of the service principal to add, i.e. 'host'
1907 * @return 0 upon sucess, or non-zero if a failure occurs
1910 ADS_STATUS ads_add_service_principal_name(ADS_STRUCT *ads, const char *machine_name,
1911 const char *my_fqdn, const char *spn)
1913 ADS_STATUS ret;
1914 TALLOC_CTX *ctx;
1915 LDAPMessage *res = NULL;
1916 char *psp1, *psp2;
1917 ADS_MODLIST mods;
1918 char *dn_string = NULL;
1919 const char *servicePrincipalName[3] = {NULL, NULL, NULL};
1921 ret = ads_find_machine_acct(ads, &res, machine_name);
1922 if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
1923 DEBUG(1,("ads_add_service_principal_name: WARNING: Host Account for %s not found... skipping operation.\n",
1924 machine_name));
1925 DEBUG(1,("ads_add_service_principal_name: WARNING: Service Principal '%s/%s@%s' has NOT been added.\n",
1926 spn, machine_name, ads->config.realm));
1927 ads_msgfree(ads, res);
1928 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1931 DEBUG(1,("ads_add_service_principal_name: Host account for %s found\n", machine_name));
1932 if (!(ctx = talloc_init("ads_add_service_principal_name"))) {
1933 ads_msgfree(ads, res);
1934 return ADS_ERROR(LDAP_NO_MEMORY);
1937 /* add short name spn */
1939 if ( (psp1 = talloc_asprintf(ctx, "%s/%s", spn, machine_name)) == NULL ) {
1940 talloc_destroy(ctx);
1941 ads_msgfree(ads, res);
1942 return ADS_ERROR(LDAP_NO_MEMORY);
1944 strupper_m(psp1);
1945 strlower_m(&psp1[strlen(spn)]);
1946 servicePrincipalName[0] = psp1;
1948 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n",
1949 psp1, machine_name));
1952 /* add fully qualified spn */
1954 if ( (psp2 = talloc_asprintf(ctx, "%s/%s", spn, my_fqdn)) == NULL ) {
1955 ret = ADS_ERROR(LDAP_NO_MEMORY);
1956 goto out;
1958 strupper_m(psp2);
1959 strlower_m(&psp2[strlen(spn)]);
1960 servicePrincipalName[1] = psp2;
1962 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n",
1963 psp2, machine_name));
1965 if ( (mods = ads_init_mods(ctx)) == NULL ) {
1966 ret = ADS_ERROR(LDAP_NO_MEMORY);
1967 goto out;
1970 ret = ads_add_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1971 if (!ADS_ERR_OK(ret)) {
1972 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
1973 goto out;
1976 if ( (dn_string = ads_get_dn(ads, ctx, res)) == NULL ) {
1977 ret = ADS_ERROR(LDAP_NO_MEMORY);
1978 goto out;
1981 ret = ads_gen_mod(ads, dn_string, mods);
1982 if (!ADS_ERR_OK(ret)) {
1983 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
1984 goto out;
1987 out:
1988 TALLOC_FREE( ctx );
1989 ads_msgfree(ads, res);
1990 return ret;
1994 * adds a machine account to the ADS server
1995 * @param ads An intialized ADS_STRUCT
1996 * @param machine_name - the NetBIOS machine name of this account.
1997 * @param account_type A number indicating the type of account to create
1998 * @param org_unit The LDAP path in which to place this account
1999 * @return 0 upon success, or non-zero otherwise
2002 ADS_STATUS ads_create_machine_acct(ADS_STRUCT *ads, const char *machine_name,
2003 const char *org_unit)
2005 ADS_STATUS ret;
2006 char *samAccountName, *controlstr;
2007 TALLOC_CTX *ctx;
2008 ADS_MODLIST mods;
2009 char *machine_escaped = NULL;
2010 char *new_dn;
2011 const char *objectClass[] = {"top", "person", "organizationalPerson",
2012 "user", "computer", NULL};
2013 LDAPMessage *res = NULL;
2014 uint32 acct_control = ( UF_WORKSTATION_TRUST_ACCOUNT |\
2015 UF_DONT_EXPIRE_PASSWD |\
2016 UF_ACCOUNTDISABLE );
2018 if (!(ctx = talloc_init("ads_add_machine_acct")))
2019 return ADS_ERROR(LDAP_NO_MEMORY);
2021 ret = ADS_ERROR(LDAP_NO_MEMORY);
2023 machine_escaped = escape_rdn_val_string_alloc(machine_name);
2024 if (!machine_escaped) {
2025 goto done;
2028 new_dn = talloc_asprintf(ctx, "cn=%s,%s", machine_escaped, org_unit);
2029 samAccountName = talloc_asprintf(ctx, "%s$", machine_name);
2031 if ( !new_dn || !samAccountName ) {
2032 goto done;
2035 #ifndef ENCTYPE_ARCFOUR_HMAC
2036 acct_control |= UF_USE_DES_KEY_ONLY;
2037 #endif
2039 if (!(controlstr = talloc_asprintf(ctx, "%u", acct_control))) {
2040 goto done;
2043 if (!(mods = ads_init_mods(ctx))) {
2044 goto done;
2047 ads_mod_str(ctx, &mods, "cn", machine_name);
2048 ads_mod_str(ctx, &mods, "sAMAccountName", samAccountName);
2049 ads_mod_strlist(ctx, &mods, "objectClass", objectClass);
2050 ads_mod_str(ctx, &mods, "userAccountControl", controlstr);
2052 ret = ads_gen_add(ads, new_dn, mods);
2054 done:
2055 SAFE_FREE(machine_escaped);
2056 ads_msgfree(ads, res);
2057 talloc_destroy(ctx);
2059 return ret;
2063 * move a machine account to another OU on the ADS server
2064 * @param ads - An intialized ADS_STRUCT
2065 * @param machine_name - the NetBIOS machine name of this account.
2066 * @param org_unit - The LDAP path in which to place this account
2067 * @param moved - whether we moved the machine account (optional)
2068 * @return 0 upon success, or non-zero otherwise
2071 ADS_STATUS ads_move_machine_acct(ADS_STRUCT *ads, const char *machine_name,
2072 const char *org_unit, bool *moved)
2074 ADS_STATUS rc;
2075 int ldap_status;
2076 LDAPMessage *res = NULL;
2077 char *filter = NULL;
2078 char *computer_dn = NULL;
2079 char *parent_dn;
2080 char *computer_rdn = NULL;
2081 bool need_move = False;
2083 if (asprintf(&filter, "(samAccountName=%s$)", machine_name) == -1) {
2084 rc = ADS_ERROR(LDAP_NO_MEMORY);
2085 goto done;
2088 /* Find pre-existing machine */
2089 rc = ads_search(ads, &res, filter, NULL);
2090 if (!ADS_ERR_OK(rc)) {
2091 goto done;
2094 computer_dn = ads_get_dn(ads, talloc_tos(), res);
2095 if (!computer_dn) {
2096 rc = ADS_ERROR(LDAP_NO_MEMORY);
2097 goto done;
2100 parent_dn = ads_parent_dn(computer_dn);
2101 if (strequal(parent_dn, org_unit)) {
2102 goto done;
2105 need_move = True;
2107 if (asprintf(&computer_rdn, "CN=%s", machine_name) == -1) {
2108 rc = ADS_ERROR(LDAP_NO_MEMORY);
2109 goto done;
2112 ldap_status = ldap_rename_s(ads->ldap.ld, computer_dn, computer_rdn,
2113 org_unit, 1, NULL, NULL);
2114 rc = ADS_ERROR(ldap_status);
2116 done:
2117 ads_msgfree(ads, res);
2118 SAFE_FREE(filter);
2119 TALLOC_FREE(computer_dn);
2120 SAFE_FREE(computer_rdn);
2122 if (!ADS_ERR_OK(rc)) {
2123 need_move = False;
2126 if (moved) {
2127 *moved = need_move;
2130 return rc;
2134 dump a binary result from ldap
2136 static void dump_binary(ADS_STRUCT *ads, const char *field, struct berval **values)
2138 int i, j;
2139 for (i=0; values[i]; i++) {
2140 printf("%s: ", field);
2141 for (j=0; j<values[i]->bv_len; j++) {
2142 printf("%02X", (unsigned char)values[i]->bv_val[j]);
2144 printf("\n");
2148 static void dump_guid(ADS_STRUCT *ads, const char *field, struct berval **values)
2150 int i;
2151 for (i=0; values[i]; i++) {
2152 NTSTATUS status;
2153 DATA_BLOB in = data_blob_const(values[i]->bv_val, values[i]->bv_len);
2154 struct GUID guid;
2156 status = GUID_from_ndr_blob(&in, &guid);
2157 if (NT_STATUS_IS_OK(status)) {
2158 printf("%s: %s\n", field, GUID_string(talloc_tos(), &guid));
2159 } else {
2160 printf("%s: INVALID GUID\n", field);
2166 dump a sid result from ldap
2168 static void dump_sid(ADS_STRUCT *ads, const char *field, struct berval **values)
2170 int i;
2171 for (i=0; values[i]; i++) {
2172 struct dom_sid sid;
2173 fstring tmp;
2174 if (!sid_parse(values[i]->bv_val, values[i]->bv_len, &sid)) {
2175 return;
2177 printf("%s: %s\n", field, sid_to_fstring(tmp, &sid));
2182 dump ntSecurityDescriptor
2184 static void dump_sd(ADS_STRUCT *ads, const char *filed, struct berval **values)
2186 TALLOC_CTX *frame = talloc_stackframe();
2187 struct security_descriptor *psd;
2188 NTSTATUS status;
2190 status = unmarshall_sec_desc(talloc_tos(), (uint8 *)values[0]->bv_val,
2191 values[0]->bv_len, &psd);
2192 if (!NT_STATUS_IS_OK(status)) {
2193 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
2194 nt_errstr(status)));
2195 TALLOC_FREE(frame);
2196 return;
2199 if (psd) {
2200 ads_disp_sd(ads, talloc_tos(), psd);
2203 TALLOC_FREE(frame);
2207 dump a string result from ldap
2209 static void dump_string(const char *field, char **values)
2211 int i;
2212 for (i=0; values[i]; i++) {
2213 printf("%s: %s\n", field, values[i]);
2218 dump a field from LDAP on stdout
2219 used for debugging
2222 static bool ads_dump_field(ADS_STRUCT *ads, char *field, void **values, void *data_area)
2224 const struct {
2225 const char *name;
2226 bool string;
2227 void (*handler)(ADS_STRUCT *, const char *, struct berval **);
2228 } handlers[] = {
2229 {"objectGUID", False, dump_guid},
2230 {"netbootGUID", False, dump_guid},
2231 {"nTSecurityDescriptor", False, dump_sd},
2232 {"dnsRecord", False, dump_binary},
2233 {"objectSid", False, dump_sid},
2234 {"tokenGroups", False, dump_sid},
2235 {"tokenGroupsNoGCAcceptable", False, dump_sid},
2236 {"tokengroupsGlobalandUniversal", False, dump_sid},
2237 {"mS-DS-CreatorSID", False, dump_sid},
2238 {"msExchMailboxGuid", False, dump_guid},
2239 {NULL, True, NULL}
2241 int i;
2243 if (!field) { /* must be end of an entry */
2244 printf("\n");
2245 return False;
2248 for (i=0; handlers[i].name; i++) {
2249 if (strcasecmp_m(handlers[i].name, field) == 0) {
2250 if (!values) /* first time, indicate string or not */
2251 return handlers[i].string;
2252 handlers[i].handler(ads, field, (struct berval **) values);
2253 break;
2256 if (!handlers[i].name) {
2257 if (!values) /* first time, indicate string conversion */
2258 return True;
2259 dump_string(field, (char **)values);
2261 return False;
2265 * Dump a result from LDAP on stdout
2266 * used for debugging
2267 * @param ads connection to ads server
2268 * @param res Results to dump
2271 void ads_dump(ADS_STRUCT *ads, LDAPMessage *res)
2273 ads_process_results(ads, res, ads_dump_field, NULL);
2277 * Walk through results, calling a function for each entry found.
2278 * The function receives a field name, a berval * array of values,
2279 * and a data area passed through from the start. The function is
2280 * called once with null for field and values at the end of each
2281 * entry.
2282 * @param ads connection to ads server
2283 * @param res Results to process
2284 * @param fn Function for processing each result
2285 * @param data_area user-defined area to pass to function
2287 void ads_process_results(ADS_STRUCT *ads, LDAPMessage *res,
2288 bool (*fn)(ADS_STRUCT *, char *, void **, void *),
2289 void *data_area)
2291 LDAPMessage *msg;
2292 TALLOC_CTX *ctx;
2293 size_t converted_size;
2295 if (!(ctx = talloc_init("ads_process_results")))
2296 return;
2298 for (msg = ads_first_entry(ads, res); msg;
2299 msg = ads_next_entry(ads, msg)) {
2300 char *utf8_field;
2301 BerElement *b;
2303 for (utf8_field=ldap_first_attribute(ads->ldap.ld,
2304 (LDAPMessage *)msg,&b);
2305 utf8_field;
2306 utf8_field=ldap_next_attribute(ads->ldap.ld,
2307 (LDAPMessage *)msg,b)) {
2308 struct berval **ber_vals;
2309 char **str_vals, **utf8_vals;
2310 char *field;
2311 bool string;
2313 if (!pull_utf8_talloc(ctx, &field, utf8_field,
2314 &converted_size))
2316 DEBUG(0,("ads_process_results: "
2317 "pull_utf8_talloc failed: %s",
2318 strerror(errno)));
2321 string = fn(ads, field, NULL, data_area);
2323 if (string) {
2324 utf8_vals = ldap_get_values(ads->ldap.ld,
2325 (LDAPMessage *)msg, field);
2326 str_vals = ads_pull_strvals(ctx,
2327 (const char **) utf8_vals);
2328 fn(ads, field, (void **) str_vals, data_area);
2329 ldap_value_free(utf8_vals);
2330 } else {
2331 ber_vals = ldap_get_values_len(ads->ldap.ld,
2332 (LDAPMessage *)msg, field);
2333 fn(ads, field, (void **) ber_vals, data_area);
2335 ldap_value_free_len(ber_vals);
2337 ldap_memfree(utf8_field);
2339 ber_free(b, 0);
2340 talloc_free_children(ctx);
2341 fn(ads, NULL, NULL, data_area); /* completed an entry */
2344 talloc_destroy(ctx);
2348 * count how many replies are in a LDAPMessage
2349 * @param ads connection to ads server
2350 * @param res Results to count
2351 * @return number of replies
2353 int ads_count_replies(ADS_STRUCT *ads, void *res)
2355 return ldap_count_entries(ads->ldap.ld, (LDAPMessage *)res);
2359 * pull the first entry from a ADS result
2360 * @param ads connection to ads server
2361 * @param res Results of search
2362 * @return first entry from result
2364 LDAPMessage *ads_first_entry(ADS_STRUCT *ads, LDAPMessage *res)
2366 return ldap_first_entry(ads->ldap.ld, res);
2370 * pull the next entry from a ADS result
2371 * @param ads connection to ads server
2372 * @param res Results of search
2373 * @return next entry from result
2375 LDAPMessage *ads_next_entry(ADS_STRUCT *ads, LDAPMessage *res)
2377 return ldap_next_entry(ads->ldap.ld, res);
2381 * pull the first message from a ADS result
2382 * @param ads connection to ads server
2383 * @param res Results of search
2384 * @return first message from result
2386 LDAPMessage *ads_first_message(ADS_STRUCT *ads, LDAPMessage *res)
2388 return ldap_first_message(ads->ldap.ld, res);
2392 * pull the next message from a ADS result
2393 * @param ads connection to ads server
2394 * @param res Results of search
2395 * @return next message from result
2397 LDAPMessage *ads_next_message(ADS_STRUCT *ads, LDAPMessage *res)
2399 return ldap_next_message(ads->ldap.ld, res);
2403 * pull a single string from a ADS result
2404 * @param ads connection to ads server
2405 * @param mem_ctx TALLOC_CTX to use for allocating result string
2406 * @param msg Results of search
2407 * @param field Attribute to retrieve
2408 * @return Result string in talloc context
2410 char *ads_pull_string(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, LDAPMessage *msg,
2411 const char *field)
2413 char **values;
2414 char *ret = NULL;
2415 char *ux_string;
2416 size_t converted_size;
2418 values = ldap_get_values(ads->ldap.ld, msg, field);
2419 if (!values)
2420 return NULL;
2422 if (values[0] && pull_utf8_talloc(mem_ctx, &ux_string, values[0],
2423 &converted_size))
2425 ret = ux_string;
2427 ldap_value_free(values);
2428 return ret;
2432 * pull an array of strings from a ADS result
2433 * @param ads connection to ads server
2434 * @param mem_ctx TALLOC_CTX to use for allocating result string
2435 * @param msg Results of search
2436 * @param field Attribute to retrieve
2437 * @return Result strings in talloc context
2439 char **ads_pull_strings(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2440 LDAPMessage *msg, const char *field,
2441 size_t *num_values)
2443 char **values;
2444 char **ret = NULL;
2445 int i;
2446 size_t converted_size;
2448 values = ldap_get_values(ads->ldap.ld, msg, field);
2449 if (!values)
2450 return NULL;
2452 *num_values = ldap_count_values(values);
2454 ret = talloc_array(mem_ctx, char *, *num_values + 1);
2455 if (!ret) {
2456 ldap_value_free(values);
2457 return NULL;
2460 for (i=0;i<*num_values;i++) {
2461 if (!pull_utf8_talloc(mem_ctx, &ret[i], values[i],
2462 &converted_size))
2464 ldap_value_free(values);
2465 return NULL;
2468 ret[i] = NULL;
2470 ldap_value_free(values);
2471 return ret;
2475 * pull an array of strings from a ADS result
2476 * (handle large multivalue attributes with range retrieval)
2477 * @param ads connection to ads server
2478 * @param mem_ctx TALLOC_CTX to use for allocating result string
2479 * @param msg Results of search
2480 * @param field Attribute to retrieve
2481 * @param current_strings strings returned by a previous call to this function
2482 * @param next_attribute The next query should ask for this attribute
2483 * @param num_values How many values did we get this time?
2484 * @param more_values Are there more values to get?
2485 * @return Result strings in talloc context
2487 char **ads_pull_strings_range(ADS_STRUCT *ads,
2488 TALLOC_CTX *mem_ctx,
2489 LDAPMessage *msg, const char *field,
2490 char **current_strings,
2491 const char **next_attribute,
2492 size_t *num_strings,
2493 bool *more_strings)
2495 char *attr;
2496 char *expected_range_attrib, *range_attr;
2497 BerElement *ptr = NULL;
2498 char **strings;
2499 char **new_strings;
2500 size_t num_new_strings;
2501 unsigned long int range_start;
2502 unsigned long int range_end;
2504 /* we might have been given the whole lot anyway */
2505 if ((strings = ads_pull_strings(ads, mem_ctx, msg, field, num_strings))) {
2506 *more_strings = False;
2507 return strings;
2510 expected_range_attrib = talloc_asprintf(mem_ctx, "%s;Range=", field);
2512 /* look for Range result */
2513 for (attr = ldap_first_attribute(ads->ldap.ld, (LDAPMessage *)msg, &ptr);
2514 attr;
2515 attr = ldap_next_attribute(ads->ldap.ld, (LDAPMessage *)msg, ptr)) {
2516 /* we ignore the fact that this is utf8, as all attributes are ascii... */
2517 if (strnequal(attr, expected_range_attrib, strlen(expected_range_attrib))) {
2518 range_attr = attr;
2519 break;
2521 ldap_memfree(attr);
2523 if (!attr) {
2524 ber_free(ptr, 0);
2525 /* nothing here - this field is just empty */
2526 *more_strings = False;
2527 return NULL;
2530 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-%lu",
2531 &range_start, &range_end) == 2) {
2532 *more_strings = True;
2533 } else {
2534 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-*",
2535 &range_start) == 1) {
2536 *more_strings = False;
2537 } else {
2538 DEBUG(1, ("ads_pull_strings_range: Cannot parse Range attriubte (%s)\n",
2539 range_attr));
2540 ldap_memfree(range_attr);
2541 *more_strings = False;
2542 return NULL;
2546 if ((*num_strings) != range_start) {
2547 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) doesn't start at %u, but at %lu"
2548 " - aborting range retreival\n",
2549 range_attr, (unsigned int)(*num_strings) + 1, range_start));
2550 ldap_memfree(range_attr);
2551 *more_strings = False;
2552 return NULL;
2555 new_strings = ads_pull_strings(ads, mem_ctx, msg, range_attr, &num_new_strings);
2557 if (*more_strings && ((*num_strings + num_new_strings) != (range_end + 1))) {
2558 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) tells us we have %lu "
2559 "strings in this bunch, but we only got %lu - aborting range retreival\n",
2560 range_attr, (unsigned long int)range_end - range_start + 1,
2561 (unsigned long int)num_new_strings));
2562 ldap_memfree(range_attr);
2563 *more_strings = False;
2564 return NULL;
2567 strings = talloc_realloc(mem_ctx, current_strings, char *,
2568 *num_strings + num_new_strings);
2570 if (strings == NULL) {
2571 ldap_memfree(range_attr);
2572 *more_strings = False;
2573 return NULL;
2576 if (new_strings && num_new_strings) {
2577 memcpy(&strings[*num_strings], new_strings,
2578 sizeof(*new_strings) * num_new_strings);
2581 (*num_strings) += num_new_strings;
2583 if (*more_strings) {
2584 *next_attribute = talloc_asprintf(mem_ctx,
2585 "%s;range=%d-*",
2586 field,
2587 (int)*num_strings);
2589 if (!*next_attribute) {
2590 DEBUG(1, ("talloc_asprintf for next attribute failed!\n"));
2591 ldap_memfree(range_attr);
2592 *more_strings = False;
2593 return NULL;
2597 ldap_memfree(range_attr);
2599 return strings;
2603 * pull a single uint32 from a ADS result
2604 * @param ads connection to ads server
2605 * @param msg Results of search
2606 * @param field Attribute to retrieve
2607 * @param v Pointer to int to store result
2608 * @return boolean inidicating success
2610 bool ads_pull_uint32(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
2611 uint32 *v)
2613 char **values;
2615 values = ldap_get_values(ads->ldap.ld, msg, field);
2616 if (!values)
2617 return False;
2618 if (!values[0]) {
2619 ldap_value_free(values);
2620 return False;
2623 *v = atoi(values[0]);
2624 ldap_value_free(values);
2625 return True;
2629 * pull a single objectGUID from an ADS result
2630 * @param ads connection to ADS server
2631 * @param msg results of search
2632 * @param guid 37-byte area to receive text guid
2633 * @return boolean indicating success
2635 bool ads_pull_guid(ADS_STRUCT *ads, LDAPMessage *msg, struct GUID *guid)
2637 DATA_BLOB blob;
2638 NTSTATUS status;
2640 if (!smbldap_talloc_single_blob(talloc_tos(), ads->ldap.ld, msg, "objectGUID",
2641 &blob)) {
2642 return false;
2645 status = GUID_from_ndr_blob(&blob, guid);
2646 talloc_free(blob.data);
2647 return NT_STATUS_IS_OK(status);
2652 * pull a single struct dom_sid from a ADS result
2653 * @param ads connection to ads server
2654 * @param msg Results of search
2655 * @param field Attribute to retrieve
2656 * @param sid Pointer to sid to store result
2657 * @return boolean inidicating success
2659 bool ads_pull_sid(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
2660 struct dom_sid *sid)
2662 return smbldap_pull_sid(ads->ldap.ld, msg, field, sid);
2666 * pull an array of struct dom_sids from a ADS result
2667 * @param ads connection to ads server
2668 * @param mem_ctx TALLOC_CTX for allocating sid array
2669 * @param msg Results of search
2670 * @param field Attribute to retrieve
2671 * @param sids pointer to sid array to allocate
2672 * @return the count of SIDs pulled
2674 int ads_pull_sids(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2675 LDAPMessage *msg, const char *field, struct dom_sid **sids)
2677 struct berval **values;
2678 bool ret;
2679 int count, i;
2681 values = ldap_get_values_len(ads->ldap.ld, msg, field);
2683 if (!values)
2684 return 0;
2686 for (i=0; values[i]; i++)
2687 /* nop */ ;
2689 if (i) {
2690 (*sids) = talloc_array(mem_ctx, struct dom_sid, i);
2691 if (!(*sids)) {
2692 ldap_value_free_len(values);
2693 return 0;
2695 } else {
2696 (*sids) = NULL;
2699 count = 0;
2700 for (i=0; values[i]; i++) {
2701 ret = sid_parse(values[i]->bv_val, values[i]->bv_len, &(*sids)[count]);
2702 if (ret) {
2703 DEBUG(10, ("pulling SID: %s\n",
2704 sid_string_dbg(&(*sids)[count])));
2705 count++;
2709 ldap_value_free_len(values);
2710 return count;
2714 * pull a struct security_descriptor from a ADS result
2715 * @param ads connection to ads server
2716 * @param mem_ctx TALLOC_CTX for allocating sid array
2717 * @param msg Results of search
2718 * @param field Attribute to retrieve
2719 * @param sd Pointer to *struct security_descriptor to store result (talloc()ed)
2720 * @return boolean inidicating success
2722 bool ads_pull_sd(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2723 LDAPMessage *msg, const char *field,
2724 struct security_descriptor **sd)
2726 struct berval **values;
2727 bool ret = true;
2729 values = ldap_get_values_len(ads->ldap.ld, msg, field);
2731 if (!values) return false;
2733 if (values[0]) {
2734 NTSTATUS status;
2735 status = unmarshall_sec_desc(mem_ctx,
2736 (uint8 *)values[0]->bv_val,
2737 values[0]->bv_len, sd);
2738 if (!NT_STATUS_IS_OK(status)) {
2739 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
2740 nt_errstr(status)));
2741 ret = false;
2745 ldap_value_free_len(values);
2746 return ret;
2750 * in order to support usernames longer than 21 characters we need to
2751 * use both the sAMAccountName and the userPrincipalName attributes
2752 * It seems that not all users have the userPrincipalName attribute set
2754 * @param ads connection to ads server
2755 * @param mem_ctx TALLOC_CTX for allocating sid array
2756 * @param msg Results of search
2757 * @return the username
2759 char *ads_pull_username(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2760 LDAPMessage *msg)
2762 #if 0 /* JERRY */
2763 char *ret, *p;
2765 /* lookup_name() only works on the sAMAccountName to
2766 returning the username portion of userPrincipalName
2767 breaks winbindd_getpwnam() */
2769 ret = ads_pull_string(ads, mem_ctx, msg, "userPrincipalName");
2770 if (ret && (p = strchr_m(ret, '@'))) {
2771 *p = 0;
2772 return ret;
2774 #endif
2775 return ads_pull_string(ads, mem_ctx, msg, "sAMAccountName");
2780 * find the update serial number - this is the core of the ldap cache
2781 * @param ads connection to ads server
2782 * @param ads connection to ADS server
2783 * @param usn Pointer to retrieved update serial number
2784 * @return status of search
2786 ADS_STATUS ads_USN(ADS_STRUCT *ads, uint32 *usn)
2788 const char *attrs[] = {"highestCommittedUSN", NULL};
2789 ADS_STATUS status;
2790 LDAPMessage *res;
2792 status = ads_do_search_retry(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2793 if (!ADS_ERR_OK(status))
2794 return status;
2796 if (ads_count_replies(ads, res) != 1) {
2797 ads_msgfree(ads, res);
2798 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2801 if (!ads_pull_uint32(ads, res, "highestCommittedUSN", usn)) {
2802 ads_msgfree(ads, res);
2803 return ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
2806 ads_msgfree(ads, res);
2807 return ADS_SUCCESS;
2810 /* parse a ADS timestring - typical string is
2811 '20020917091222.0Z0' which means 09:12.22 17th September
2812 2002, timezone 0 */
2813 static time_t ads_parse_time(const char *str)
2815 struct tm tm;
2817 ZERO_STRUCT(tm);
2819 if (sscanf(str, "%4d%2d%2d%2d%2d%2d",
2820 &tm.tm_year, &tm.tm_mon, &tm.tm_mday,
2821 &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) {
2822 return 0;
2824 tm.tm_year -= 1900;
2825 tm.tm_mon -= 1;
2827 return timegm(&tm);
2830 /********************************************************************
2831 ********************************************************************/
2833 ADS_STATUS ads_current_time(ADS_STRUCT *ads)
2835 const char *attrs[] = {"currentTime", NULL};
2836 ADS_STATUS status;
2837 LDAPMessage *res;
2838 char *timestr;
2839 TALLOC_CTX *ctx;
2840 ADS_STRUCT *ads_s = ads;
2842 if (!(ctx = talloc_init("ads_current_time"))) {
2843 return ADS_ERROR(LDAP_NO_MEMORY);
2846 /* establish a new ldap tcp session if necessary */
2848 if ( !ads->ldap.ld ) {
2849 if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup,
2850 ads->server.ldap_server )) == NULL )
2852 goto done;
2854 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
2855 status = ads_connect( ads_s );
2856 if ( !ADS_ERR_OK(status))
2857 goto done;
2860 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2861 if (!ADS_ERR_OK(status)) {
2862 goto done;
2865 timestr = ads_pull_string(ads_s, ctx, res, "currentTime");
2866 if (!timestr) {
2867 ads_msgfree(ads_s, res);
2868 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2869 goto done;
2872 /* but save the time and offset in the original ADS_STRUCT */
2874 ads->config.current_time = ads_parse_time(timestr);
2876 if (ads->config.current_time != 0) {
2877 ads->auth.time_offset = ads->config.current_time - time(NULL);
2878 DEBUG(4,("KDC time offset is %d seconds\n", ads->auth.time_offset));
2881 ads_msgfree(ads, res);
2883 status = ADS_SUCCESS;
2885 done:
2886 /* free any temporary ads connections */
2887 if ( ads_s != ads ) {
2888 ads_destroy( &ads_s );
2890 talloc_destroy(ctx);
2892 return status;
2895 /********************************************************************
2896 ********************************************************************/
2898 ADS_STATUS ads_domain_func_level(ADS_STRUCT *ads, uint32 *val)
2900 const char *attrs[] = {"domainFunctionality", NULL};
2901 ADS_STATUS status;
2902 LDAPMessage *res;
2903 ADS_STRUCT *ads_s = ads;
2905 *val = DS_DOMAIN_FUNCTION_2000;
2907 /* establish a new ldap tcp session if necessary */
2909 if ( !ads->ldap.ld ) {
2910 if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup,
2911 ads->server.ldap_server )) == NULL )
2913 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
2914 goto done;
2916 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
2917 status = ads_connect( ads_s );
2918 if ( !ADS_ERR_OK(status))
2919 goto done;
2922 /* If the attribute does not exist assume it is a Windows 2000
2923 functional domain */
2925 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2926 if (!ADS_ERR_OK(status)) {
2927 if ( status.err.rc == LDAP_NO_SUCH_ATTRIBUTE ) {
2928 status = ADS_SUCCESS;
2930 goto done;
2933 if ( !ads_pull_uint32(ads_s, res, "domainFunctionality", val) ) {
2934 DEBUG(5,("ads_domain_func_level: Failed to pull the domainFunctionality attribute.\n"));
2936 DEBUG(3,("ads_domain_func_level: %d\n", *val));
2939 ads_msgfree(ads, res);
2941 done:
2942 /* free any temporary ads connections */
2943 if ( ads_s != ads ) {
2944 ads_destroy( &ads_s );
2947 return status;
2951 * find the domain sid for our domain
2952 * @param ads connection to ads server
2953 * @param sid Pointer to domain sid
2954 * @return status of search
2956 ADS_STATUS ads_domain_sid(ADS_STRUCT *ads, struct dom_sid *sid)
2958 const char *attrs[] = {"objectSid", NULL};
2959 LDAPMessage *res;
2960 ADS_STATUS rc;
2962 rc = ads_do_search_retry(ads, ads->config.bind_path, LDAP_SCOPE_BASE, "(objectclass=*)",
2963 attrs, &res);
2964 if (!ADS_ERR_OK(rc)) return rc;
2965 if (!ads_pull_sid(ads, res, "objectSid", sid)) {
2966 ads_msgfree(ads, res);
2967 return ADS_ERROR_SYSTEM(ENOENT);
2969 ads_msgfree(ads, res);
2971 return ADS_SUCCESS;
2975 * find our site name
2976 * @param ads connection to ads server
2977 * @param mem_ctx Pointer to talloc context
2978 * @param site_name Pointer to the sitename
2979 * @return status of search
2981 ADS_STATUS ads_site_dn(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char **site_name)
2983 ADS_STATUS status;
2984 LDAPMessage *res;
2985 const char *dn, *service_name;
2986 const char *attrs[] = { "dsServiceName", NULL };
2988 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2989 if (!ADS_ERR_OK(status)) {
2990 return status;
2993 service_name = ads_pull_string(ads, mem_ctx, res, "dsServiceName");
2994 if (service_name == NULL) {
2995 ads_msgfree(ads, res);
2996 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2999 ads_msgfree(ads, res);
3001 /* go up three levels */
3002 dn = ads_parent_dn(ads_parent_dn(ads_parent_dn(service_name)));
3003 if (dn == NULL) {
3004 return ADS_ERROR(LDAP_NO_MEMORY);
3007 *site_name = talloc_strdup(mem_ctx, dn);
3008 if (*site_name == NULL) {
3009 return ADS_ERROR(LDAP_NO_MEMORY);
3012 return status;
3014 dsServiceName: CN=NTDS Settings,CN=W2K3DC,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,DC=ber,DC=suse,DC=de
3019 * find the site dn where a machine resides
3020 * @param ads connection to ads server
3021 * @param mem_ctx Pointer to talloc context
3022 * @param computer_name name of the machine
3023 * @param site_name Pointer to the sitename
3024 * @return status of search
3026 ADS_STATUS ads_site_dn_for_machine(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char *computer_name, const char **site_dn)
3028 ADS_STATUS status;
3029 LDAPMessage *res;
3030 const char *parent, *filter;
3031 char *config_context = NULL;
3032 char *dn;
3034 /* shortcut a query */
3035 if (strequal(computer_name, ads->config.ldap_server_name)) {
3036 return ads_site_dn(ads, mem_ctx, site_dn);
3039 status = ads_config_path(ads, mem_ctx, &config_context);
3040 if (!ADS_ERR_OK(status)) {
3041 return status;
3044 filter = talloc_asprintf(mem_ctx, "(cn=%s)", computer_name);
3045 if (filter == NULL) {
3046 return ADS_ERROR(LDAP_NO_MEMORY);
3049 status = ads_do_search(ads, config_context, LDAP_SCOPE_SUBTREE,
3050 filter, NULL, &res);
3051 if (!ADS_ERR_OK(status)) {
3052 return status;
3055 if (ads_count_replies(ads, res) != 1) {
3056 ads_msgfree(ads, res);
3057 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
3060 dn = ads_get_dn(ads, mem_ctx, res);
3061 if (dn == NULL) {
3062 ads_msgfree(ads, res);
3063 return ADS_ERROR(LDAP_NO_MEMORY);
3066 /* go up three levels */
3067 parent = ads_parent_dn(ads_parent_dn(ads_parent_dn(dn)));
3068 if (parent == NULL) {
3069 ads_msgfree(ads, res);
3070 TALLOC_FREE(dn);
3071 return ADS_ERROR(LDAP_NO_MEMORY);
3074 *site_dn = talloc_strdup(mem_ctx, parent);
3075 if (*site_dn == NULL) {
3076 ads_msgfree(ads, res);
3077 TALLOC_FREE(dn);
3078 return ADS_ERROR(LDAP_NO_MEMORY);
3081 TALLOC_FREE(dn);
3082 ads_msgfree(ads, res);
3084 return status;
3088 * get the upn suffixes for a domain
3089 * @param ads connection to ads server
3090 * @param mem_ctx Pointer to talloc context
3091 * @param suffixes Pointer to an array of suffixes
3092 * @param num_suffixes Pointer to the number of suffixes
3093 * @return status of search
3095 ADS_STATUS ads_upn_suffixes(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, char ***suffixes, size_t *num_suffixes)
3097 ADS_STATUS status;
3098 LDAPMessage *res;
3099 const char *base;
3100 char *config_context = NULL;
3101 const char *attrs[] = { "uPNSuffixes", NULL };
3103 status = ads_config_path(ads, mem_ctx, &config_context);
3104 if (!ADS_ERR_OK(status)) {
3105 return status;
3108 base = talloc_asprintf(mem_ctx, "cn=Partitions,%s", config_context);
3109 if (base == NULL) {
3110 return ADS_ERROR(LDAP_NO_MEMORY);
3113 status = ads_search_dn(ads, &res, base, attrs);
3114 if (!ADS_ERR_OK(status)) {
3115 return status;
3118 if (ads_count_replies(ads, res) != 1) {
3119 ads_msgfree(ads, res);
3120 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
3123 (*suffixes) = ads_pull_strings(ads, mem_ctx, res, "uPNSuffixes", num_suffixes);
3124 if ((*suffixes) == NULL) {
3125 ads_msgfree(ads, res);
3126 return ADS_ERROR(LDAP_NO_MEMORY);
3129 ads_msgfree(ads, res);
3131 return status;
3135 * get the joinable ous for a domain
3136 * @param ads connection to ads server
3137 * @param mem_ctx Pointer to talloc context
3138 * @param ous Pointer to an array of ous
3139 * @param num_ous Pointer to the number of ous
3140 * @return status of search
3142 ADS_STATUS ads_get_joinable_ous(ADS_STRUCT *ads,
3143 TALLOC_CTX *mem_ctx,
3144 char ***ous,
3145 size_t *num_ous)
3147 ADS_STATUS status;
3148 LDAPMessage *res = NULL;
3149 LDAPMessage *msg = NULL;
3150 const char *attrs[] = { "dn", NULL };
3151 int count = 0;
3153 status = ads_search(ads, &res,
3154 "(|(objectClass=domain)(objectclass=organizationalUnit))",
3155 attrs);
3156 if (!ADS_ERR_OK(status)) {
3157 return status;
3160 count = ads_count_replies(ads, res);
3161 if (count < 1) {
3162 ads_msgfree(ads, res);
3163 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3166 for (msg = ads_first_entry(ads, res); msg;
3167 msg = ads_next_entry(ads, msg)) {
3169 char *dn = NULL;
3171 dn = ads_get_dn(ads, talloc_tos(), msg);
3172 if (!dn) {
3173 ads_msgfree(ads, res);
3174 return ADS_ERROR(LDAP_NO_MEMORY);
3177 if (!add_string_to_array(mem_ctx, dn,
3178 (const char ***)ous,
3179 (int *)num_ous)) {
3180 TALLOC_FREE(dn);
3181 ads_msgfree(ads, res);
3182 return ADS_ERROR(LDAP_NO_MEMORY);
3185 TALLOC_FREE(dn);
3188 ads_msgfree(ads, res);
3190 return status;
3195 * pull a struct dom_sid from an extended dn string
3196 * @param mem_ctx TALLOC_CTX
3197 * @param extended_dn string
3198 * @param flags string type of extended_dn
3199 * @param sid pointer to a struct dom_sid
3200 * @return NT_STATUS_OK on success,
3201 * NT_INVALID_PARAMETER on error,
3202 * NT_STATUS_NOT_FOUND if no SID present
3204 ADS_STATUS ads_get_sid_from_extended_dn(TALLOC_CTX *mem_ctx,
3205 const char *extended_dn,
3206 enum ads_extended_dn_flags flags,
3207 struct dom_sid *sid)
3209 char *p, *q, *dn;
3211 if (!extended_dn) {
3212 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3215 /* otherwise extended_dn gets stripped off */
3216 if ((dn = talloc_strdup(mem_ctx, extended_dn)) == NULL) {
3217 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3220 * ADS_EXTENDED_DN_HEX_STRING:
3221 * <GUID=238e1963cb390f4bb032ba0105525a29>;<SID=010500000000000515000000bb68c8fd6b61b427572eb04556040000>;CN=gd,OU=berlin,OU=suse,DC=ber,DC=suse,DC=de
3223 * ADS_EXTENDED_DN_STRING (only with w2k3):
3224 * <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
3226 * Object with no SID, such as an Exchange Public Folder
3227 * <GUID=28907fb4bdf6854993e7f0a10b504e7c>;CN=public,CN=Microsoft Exchange System Objects,DC=sd2k3ms,DC=west,DC=isilon,DC=com
3230 p = strchr(dn, ';');
3231 if (!p) {
3232 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3235 if (strncmp(p, ";<SID=", strlen(";<SID=")) != 0) {
3236 DEBUG(5,("No SID present in extended dn\n"));
3237 return ADS_ERROR_NT(NT_STATUS_NOT_FOUND);
3240 p += strlen(";<SID=");
3242 q = strchr(p, '>');
3243 if (!q) {
3244 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3247 *q = '\0';
3249 DEBUG(100,("ads_get_sid_from_extended_dn: sid string is %s\n", p));
3251 switch (flags) {
3253 case ADS_EXTENDED_DN_STRING:
3254 if (!string_to_sid(sid, p)) {
3255 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3257 break;
3258 case ADS_EXTENDED_DN_HEX_STRING: {
3259 fstring buf;
3260 size_t buf_len;
3262 buf_len = strhex_to_str(buf, sizeof(buf), p, strlen(p));
3263 if (buf_len == 0) {
3264 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3267 if (!sid_parse(buf, buf_len, sid)) {
3268 DEBUG(10,("failed to parse sid\n"));
3269 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3271 break;
3273 default:
3274 DEBUG(10,("unknown extended dn format\n"));
3275 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3278 return ADS_ERROR_NT(NT_STATUS_OK);
3282 * pull an array of struct dom_sids from a ADS result
3283 * @param ads connection to ads server
3284 * @param mem_ctx TALLOC_CTX for allocating sid array
3285 * @param msg Results of search
3286 * @param field Attribute to retrieve
3287 * @param flags string type of extended_dn
3288 * @param sids pointer to sid array to allocate
3289 * @return the count of SIDs pulled
3291 int ads_pull_sids_from_extendeddn(ADS_STRUCT *ads,
3292 TALLOC_CTX *mem_ctx,
3293 LDAPMessage *msg,
3294 const char *field,
3295 enum ads_extended_dn_flags flags,
3296 struct dom_sid **sids)
3298 int i;
3299 ADS_STATUS rc;
3300 size_t dn_count, ret_count = 0;
3301 char **dn_strings;
3303 if ((dn_strings = ads_pull_strings(ads, mem_ctx, msg, field,
3304 &dn_count)) == NULL) {
3305 return 0;
3308 (*sids) = talloc_zero_array(mem_ctx, struct dom_sid, dn_count + 1);
3309 if (!(*sids)) {
3310 TALLOC_FREE(dn_strings);
3311 return 0;
3314 for (i=0; i<dn_count; i++) {
3315 rc = ads_get_sid_from_extended_dn(mem_ctx, dn_strings[i],
3316 flags, &(*sids)[i]);
3317 if (!ADS_ERR_OK(rc)) {
3318 if (NT_STATUS_EQUAL(ads_ntstatus(rc),
3319 NT_STATUS_NOT_FOUND)) {
3320 continue;
3322 else {
3323 TALLOC_FREE(*sids);
3324 TALLOC_FREE(dn_strings);
3325 return 0;
3328 ret_count++;
3331 TALLOC_FREE(dn_strings);
3333 return ret_count;
3336 /********************************************************************
3337 ********************************************************************/
3339 char* ads_get_dnshostname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3341 LDAPMessage *res = NULL;
3342 ADS_STATUS status;
3343 int count = 0;
3344 char *name = NULL;
3346 status = ads_find_machine_acct(ads, &res, lp_netbios_name());
3347 if (!ADS_ERR_OK(status)) {
3348 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
3349 lp_netbios_name()));
3350 goto out;
3353 if ( (count = ads_count_replies(ads, res)) != 1 ) {
3354 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
3355 goto out;
3358 if ( (name = ads_pull_string(ads, ctx, res, "dNSHostName")) == NULL ) {
3359 DEBUG(0,("ads_get_dnshostname: No dNSHostName attribute!\n"));
3362 out:
3363 ads_msgfree(ads, res);
3365 return name;
3368 /********************************************************************
3369 ********************************************************************/
3371 char* ads_get_upn( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3373 LDAPMessage *res = NULL;
3374 ADS_STATUS status;
3375 int count = 0;
3376 char *name = NULL;
3378 status = ads_find_machine_acct(ads, &res, machine_name);
3379 if (!ADS_ERR_OK(status)) {
3380 DEBUG(0,("ads_get_upn: Failed to find account for %s\n",
3381 lp_netbios_name()));
3382 goto out;
3385 if ( (count = ads_count_replies(ads, res)) != 1 ) {
3386 DEBUG(1,("ads_get_upn: %d entries returned!\n", count));
3387 goto out;
3390 if ( (name = ads_pull_string(ads, ctx, res, "userPrincipalName")) == NULL ) {
3391 DEBUG(2,("ads_get_upn: No userPrincipalName attribute!\n"));
3394 out:
3395 ads_msgfree(ads, res);
3397 return name;
3400 /********************************************************************
3401 ********************************************************************/
3403 char* ads_get_samaccountname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3405 LDAPMessage *res = NULL;
3406 ADS_STATUS status;
3407 int count = 0;
3408 char *name = NULL;
3410 status = ads_find_machine_acct(ads, &res, lp_netbios_name());
3411 if (!ADS_ERR_OK(status)) {
3412 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
3413 lp_netbios_name()));
3414 goto out;
3417 if ( (count = ads_count_replies(ads, res)) != 1 ) {
3418 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
3419 goto out;
3422 if ( (name = ads_pull_string(ads, ctx, res, "sAMAccountName")) == NULL ) {
3423 DEBUG(0,("ads_get_dnshostname: No sAMAccountName attribute!\n"));
3426 out:
3427 ads_msgfree(ads, res);
3429 return name;
3432 #if 0
3434 SAVED CODE - we used to join via ldap - remember how we did this. JRA.
3437 * Join a machine to a realm
3438 * Creates the machine account and sets the machine password
3439 * @param ads connection to ads server
3440 * @param machine name of host to add
3441 * @param org_unit Organizational unit to place machine in
3442 * @return status of join
3444 ADS_STATUS ads_join_realm(ADS_STRUCT *ads, const char *machine_name,
3445 uint32 account_type, const char *org_unit)
3447 ADS_STATUS status;
3448 LDAPMessage *res = NULL;
3449 char *machine;
3451 /* machine name must be lowercase */
3452 machine = SMB_STRDUP(machine_name);
3453 strlower_m(machine);
3456 status = ads_find_machine_acct(ads, (void **)&res, machine);
3457 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
3458 DEBUG(0, ("Host account for %s already exists - deleting old account\n", machine));
3459 status = ads_leave_realm(ads, machine);
3460 if (!ADS_ERR_OK(status)) {
3461 DEBUG(0, ("Failed to delete host '%s' from the '%s' realm.\n",
3462 machine, ads->config.realm));
3463 return status;
3467 status = ads_add_machine_acct(ads, machine, account_type, org_unit);
3468 if (!ADS_ERR_OK(status)) {
3469 DEBUG(0, ("ads_join_realm: ads_add_machine_acct failed (%s): %s\n", machine, ads_errstr(status)));
3470 SAFE_FREE(machine);
3471 return status;
3474 status = ads_find_machine_acct(ads, (void **)(void *)&res, machine);
3475 if (!ADS_ERR_OK(status)) {
3476 DEBUG(0, ("ads_join_realm: Host account test failed for machine %s\n", machine));
3477 SAFE_FREE(machine);
3478 return status;
3481 SAFE_FREE(machine);
3482 ads_msgfree(ads, res);
3484 return status;
3486 #endif
3489 * Delete a machine from the realm
3490 * @param ads connection to ads server
3491 * @param hostname Machine to remove
3492 * @return status of delete
3494 ADS_STATUS ads_leave_realm(ADS_STRUCT *ads, const char *hostname)
3496 ADS_STATUS status;
3497 void *msg;
3498 LDAPMessage *res;
3499 char *hostnameDN, *host;
3500 int rc;
3501 LDAPControl ldap_control;
3502 LDAPControl * pldap_control[2] = {NULL, NULL};
3504 pldap_control[0] = &ldap_control;
3505 memset(&ldap_control, 0, sizeof(LDAPControl));
3506 ldap_control.ldctl_oid = discard_const_p(char, LDAP_SERVER_TREE_DELETE_OID);
3508 /* hostname must be lowercase */
3509 host = SMB_STRDUP(hostname);
3510 strlower_m(host);
3512 status = ads_find_machine_acct(ads, &res, host);
3513 if (!ADS_ERR_OK(status)) {
3514 DEBUG(0, ("Host account for %s does not exist.\n", host));
3515 SAFE_FREE(host);
3516 return status;
3519 msg = ads_first_entry(ads, res);
3520 if (!msg) {
3521 SAFE_FREE(host);
3522 return ADS_ERROR_SYSTEM(ENOENT);
3525 hostnameDN = ads_get_dn(ads, talloc_tos(), (LDAPMessage *)msg);
3526 if (hostnameDN == NULL) {
3527 SAFE_FREE(host);
3528 return ADS_ERROR_SYSTEM(ENOENT);
3531 rc = ldap_delete_ext_s(ads->ldap.ld, hostnameDN, pldap_control, NULL);
3532 if (rc) {
3533 DEBUG(3,("ldap_delete_ext_s failed with error code %d\n", rc));
3534 }else {
3535 DEBUG(3,("ldap_delete_ext_s succeeded with error code %d\n", rc));
3538 if (rc != LDAP_SUCCESS) {
3539 const char *attrs[] = { "cn", NULL };
3540 LDAPMessage *msg_sub;
3542 /* we only search with scope ONE, we do not expect any further
3543 * objects to be created deeper */
3545 status = ads_do_search_retry(ads, hostnameDN,
3546 LDAP_SCOPE_ONELEVEL,
3547 "(objectclass=*)", attrs, &res);
3549 if (!ADS_ERR_OK(status)) {
3550 SAFE_FREE(host);
3551 TALLOC_FREE(hostnameDN);
3552 return status;
3555 for (msg_sub = ads_first_entry(ads, res); msg_sub;
3556 msg_sub = ads_next_entry(ads, msg_sub)) {
3558 char *dn = NULL;
3560 if ((dn = ads_get_dn(ads, talloc_tos(), msg_sub)) == NULL) {
3561 SAFE_FREE(host);
3562 TALLOC_FREE(hostnameDN);
3563 return ADS_ERROR(LDAP_NO_MEMORY);
3566 status = ads_del_dn(ads, dn);
3567 if (!ADS_ERR_OK(status)) {
3568 DEBUG(3,("failed to delete dn %s: %s\n", dn, ads_errstr(status)));
3569 SAFE_FREE(host);
3570 TALLOC_FREE(dn);
3571 TALLOC_FREE(hostnameDN);
3572 return status;
3575 TALLOC_FREE(dn);
3578 /* there should be no subordinate objects anymore */
3579 status = ads_do_search_retry(ads, hostnameDN,
3580 LDAP_SCOPE_ONELEVEL,
3581 "(objectclass=*)", attrs, &res);
3583 if (!ADS_ERR_OK(status) || ( (ads_count_replies(ads, res)) > 0 ) ) {
3584 SAFE_FREE(host);
3585 TALLOC_FREE(hostnameDN);
3586 return status;
3589 /* delete hostnameDN now */
3590 status = ads_del_dn(ads, hostnameDN);
3591 if (!ADS_ERR_OK(status)) {
3592 SAFE_FREE(host);
3593 DEBUG(3,("failed to delete dn %s: %s\n", hostnameDN, ads_errstr(status)));
3594 TALLOC_FREE(hostnameDN);
3595 return status;
3599 TALLOC_FREE(hostnameDN);
3601 status = ads_find_machine_acct(ads, &res, host);
3602 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
3603 DEBUG(3, ("Failed to remove host account.\n"));
3604 SAFE_FREE(host);
3605 return status;
3608 SAFE_FREE(host);
3609 return status;
3613 * pull all token-sids from an LDAP dn
3614 * @param ads connection to ads server
3615 * @param mem_ctx TALLOC_CTX for allocating sid array
3616 * @param dn of LDAP object
3617 * @param user_sid pointer to struct dom_sid (objectSid)
3618 * @param primary_group_sid pointer to struct dom_sid (self composed)
3619 * @param sids pointer to sid array to allocate
3620 * @param num_sids counter of SIDs pulled
3621 * @return status of token query
3623 ADS_STATUS ads_get_tokensids(ADS_STRUCT *ads,
3624 TALLOC_CTX *mem_ctx,
3625 const char *dn,
3626 struct dom_sid *user_sid,
3627 struct dom_sid *primary_group_sid,
3628 struct dom_sid **sids,
3629 size_t *num_sids)
3631 ADS_STATUS status;
3632 LDAPMessage *res = NULL;
3633 int count = 0;
3634 size_t tmp_num_sids;
3635 struct dom_sid *tmp_sids;
3636 struct dom_sid tmp_user_sid;
3637 struct dom_sid tmp_primary_group_sid;
3638 uint32 pgid;
3639 const char *attrs[] = {
3640 "objectSid",
3641 "tokenGroups",
3642 "primaryGroupID",
3643 NULL
3646 status = ads_search_retry_dn(ads, &res, dn, attrs);
3647 if (!ADS_ERR_OK(status)) {
3648 return status;
3651 count = ads_count_replies(ads, res);
3652 if (count != 1) {
3653 ads_msgfree(ads, res);
3654 return ADS_ERROR_LDAP(LDAP_NO_SUCH_OBJECT);
3657 if (!ads_pull_sid(ads, res, "objectSid", &tmp_user_sid)) {
3658 ads_msgfree(ads, res);
3659 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3662 if (!ads_pull_uint32(ads, res, "primaryGroupID", &pgid)) {
3663 ads_msgfree(ads, res);
3664 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3668 /* hack to compose the primary group sid without knowing the
3669 * domsid */
3671 struct dom_sid domsid;
3673 sid_copy(&domsid, &tmp_user_sid);
3675 if (!sid_split_rid(&domsid, NULL)) {
3676 ads_msgfree(ads, res);
3677 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3680 if (!sid_compose(&tmp_primary_group_sid, &domsid, pgid)) {
3681 ads_msgfree(ads, res);
3682 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3686 tmp_num_sids = ads_pull_sids(ads, mem_ctx, res, "tokenGroups", &tmp_sids);
3688 if (tmp_num_sids == 0 || !tmp_sids) {
3689 ads_msgfree(ads, res);
3690 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3693 if (num_sids) {
3694 *num_sids = tmp_num_sids;
3697 if (sids) {
3698 *sids = tmp_sids;
3701 if (user_sid) {
3702 *user_sid = tmp_user_sid;
3705 if (primary_group_sid) {
3706 *primary_group_sid = tmp_primary_group_sid;
3709 DEBUG(10,("ads_get_tokensids: returned %d sids\n", (int)tmp_num_sids + 2));
3711 ads_msgfree(ads, res);
3712 return ADS_ERROR_LDAP(LDAP_SUCCESS);
3716 * Find a sAMAccoutName in LDAP
3717 * @param ads connection to ads server
3718 * @param mem_ctx TALLOC_CTX for allocating sid array
3719 * @param samaccountname to search
3720 * @param uac_ret uint32 pointer userAccountControl attribute value
3721 * @param dn_ret pointer to dn
3722 * @return status of token query
3724 ADS_STATUS ads_find_samaccount(ADS_STRUCT *ads,
3725 TALLOC_CTX *mem_ctx,
3726 const char *samaccountname,
3727 uint32 *uac_ret,
3728 const char **dn_ret)
3730 ADS_STATUS status;
3731 const char *attrs[] = { "userAccountControl", NULL };
3732 const char *filter;
3733 LDAPMessage *res = NULL;
3734 char *dn = NULL;
3735 uint32 uac = 0;
3737 filter = talloc_asprintf(mem_ctx, "(&(objectclass=user)(sAMAccountName=%s))",
3738 samaccountname);
3739 if (filter == NULL) {
3740 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
3741 goto out;
3744 status = ads_do_search_all(ads, ads->config.bind_path,
3745 LDAP_SCOPE_SUBTREE,
3746 filter, attrs, &res);
3748 if (!ADS_ERR_OK(status)) {
3749 goto out;
3752 if (ads_count_replies(ads, res) != 1) {
3753 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3754 goto out;
3757 dn = ads_get_dn(ads, talloc_tos(), res);
3758 if (dn == NULL) {
3759 status = ADS_ERROR(LDAP_NO_MEMORY);
3760 goto out;
3763 if (!ads_pull_uint32(ads, res, "userAccountControl", &uac)) {
3764 status = ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
3765 goto out;
3768 if (uac_ret) {
3769 *uac_ret = uac;
3772 if (dn_ret) {
3773 *dn_ret = talloc_strdup(mem_ctx, dn);
3774 if (!*dn_ret) {
3775 status = ADS_ERROR(LDAP_NO_MEMORY);
3776 goto out;
3779 out:
3780 TALLOC_FREE(dn);
3781 ads_msgfree(ads, res);
3783 return status;
3787 * find our configuration path
3788 * @param ads connection to ads server
3789 * @param mem_ctx Pointer to talloc context
3790 * @param config_path Pointer to the config path
3791 * @return status of search
3793 ADS_STATUS ads_config_path(ADS_STRUCT *ads,
3794 TALLOC_CTX *mem_ctx,
3795 char **config_path)
3797 ADS_STATUS status;
3798 LDAPMessage *res = NULL;
3799 const char *config_context = NULL;
3800 const char *attrs[] = { "configurationNamingContext", NULL };
3802 status = ads_do_search(ads, "", LDAP_SCOPE_BASE,
3803 "(objectclass=*)", attrs, &res);
3804 if (!ADS_ERR_OK(status)) {
3805 return status;
3808 config_context = ads_pull_string(ads, mem_ctx, res,
3809 "configurationNamingContext");
3810 ads_msgfree(ads, res);
3811 if (!config_context) {
3812 return ADS_ERROR(LDAP_NO_MEMORY);
3815 if (config_path) {
3816 *config_path = talloc_strdup(mem_ctx, config_context);
3817 if (!*config_path) {
3818 return ADS_ERROR(LDAP_NO_MEMORY);
3822 return ADS_ERROR(LDAP_SUCCESS);
3826 * find the displayName of an extended right
3827 * @param ads connection to ads server
3828 * @param config_path The config path
3829 * @param mem_ctx Pointer to talloc context
3830 * @param GUID struct of the rightsGUID
3831 * @return status of search
3833 const char *ads_get_extended_right_name_by_guid(ADS_STRUCT *ads,
3834 const char *config_path,
3835 TALLOC_CTX *mem_ctx,
3836 const struct GUID *rights_guid)
3838 ADS_STATUS rc;
3839 LDAPMessage *res = NULL;
3840 char *expr = NULL;
3841 const char *attrs[] = { "displayName", NULL };
3842 const char *result = NULL;
3843 const char *path;
3845 if (!ads || !mem_ctx || !rights_guid) {
3846 goto done;
3849 expr = talloc_asprintf(mem_ctx, "(rightsGuid=%s)",
3850 GUID_string(mem_ctx, rights_guid));
3851 if (!expr) {
3852 goto done;
3855 path = talloc_asprintf(mem_ctx, "cn=Extended-Rights,%s", config_path);
3856 if (!path) {
3857 goto done;
3860 rc = ads_do_search_retry(ads, path, LDAP_SCOPE_SUBTREE,
3861 expr, attrs, &res);
3862 if (!ADS_ERR_OK(rc)) {
3863 goto done;
3866 if (ads_count_replies(ads, res) != 1) {
3867 goto done;
3870 result = ads_pull_string(ads, mem_ctx, res, "displayName");
3872 done:
3873 ads_msgfree(ads, res);
3874 return result;
3878 * verify or build and verify an account ou
3879 * @param mem_ctx Pointer to talloc context
3880 * @param ads connection to ads server
3881 * @param account_ou
3882 * @return status of search
3885 ADS_STATUS ads_check_ou_dn(TALLOC_CTX *mem_ctx,
3886 ADS_STRUCT *ads,
3887 const char **account_ou)
3889 char **exploded_dn;
3890 const char *name;
3891 char *ou_string;
3893 exploded_dn = ldap_explode_dn(*account_ou, 0);
3894 if (exploded_dn) {
3895 ldap_value_free(exploded_dn);
3896 return ADS_SUCCESS;
3899 ou_string = ads_ou_string(ads, *account_ou);
3900 if (!ou_string) {
3901 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX);
3904 name = talloc_asprintf(mem_ctx, "%s,%s", ou_string,
3905 ads->config.bind_path);
3906 SAFE_FREE(ou_string);
3908 if (!name) {
3909 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3912 exploded_dn = ldap_explode_dn(name, 0);
3913 if (!exploded_dn) {
3914 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX);
3916 ldap_value_free(exploded_dn);
3918 *account_ou = name;
3919 return ADS_SUCCESS;
3922 #endif