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/>.
25 #include "lib/ldb/include/includes.h"
31 * @brief basic ldap client-side routines for ads server communications
33 * The routines contained here should do the necessary ldap calls for
36 * Important note: attribute names passed into ads_ routines must
37 * already be in UTF-8 format. We do not convert them because in almost
38 * all cases, they are just ascii (which is represented with the same
39 * codepoints in UTF-8). This may have to change at some point
43 #define LDAP_SERVER_TREE_DELETE_OID "1.2.840.113556.1.4.805"
45 static SIG_ATOMIC_T gotalarm;
47 /***************************************************************
48 Signal function to tell us we timed out.
49 ****************************************************************/
51 static void gotalarm_sig(void)
56 LDAP *ldap_open_with_timeout(const char *server, int port, unsigned int to)
61 DEBUG(10, ("Opening connection to LDAP server '%s:%d', timeout "
62 "%u seconds\n", server, port, to));
66 CatchSignal(SIGALRM, SIGNAL_CAST gotalarm_sig);
68 /* End setup timeout. */
70 ldp = ldap_open(server, port);
73 DEBUG(2,("Could not open connection to LDAP server %s:%d: %s\n",
74 server, port, strerror(errno)));
76 DEBUG(10, ("Connected to LDAP server '%s:%d'\n", server, port));
79 /* Teardown timeout. */
80 CatchSignal(SIGALRM, SIGNAL_CAST SIG_IGN);
86 static int ldap_search_with_timeout(LDAP *ld,
87 LDAP_CONST char *base,
89 LDAP_CONST char *filter,
97 struct timeval timeout;
100 /* Setup timeout for the ldap_search_ext_s call - local and remote. */
101 timeout.tv_sec = lp_ldap_timeout();
104 /* Setup alarm timeout.... Do we need both of these ? JRA. */
106 CatchSignal(SIGALRM, SIGNAL_CAST gotalarm_sig);
107 alarm(lp_ldap_timeout());
108 /* End setup timeout. */
110 result = ldap_search_ext_s(ld, base, scope, filter, attrs,
111 attrsonly, sctrls, cctrls, &timeout,
114 /* Teardown timeout. */
115 CatchSignal(SIGALRM, SIGNAL_CAST SIG_IGN);
119 return LDAP_TIMELIMIT_EXCEEDED;
124 /**********************************************
125 Do client and server sitename match ?
126 **********************************************/
128 bool ads_sitename_match(ADS_STRUCT *ads)
130 if (ads->config.server_site_name == NULL &&
131 ads->config.client_site_name == NULL ) {
132 DEBUG(10,("ads_sitename_match: both null\n"));
135 if (ads->config.server_site_name &&
136 ads->config.client_site_name &&
137 strequal(ads->config.server_site_name,
138 ads->config.client_site_name)) {
139 DEBUG(10,("ads_sitename_match: name %s match\n", ads->config.server_site_name));
142 DEBUG(10,("ads_sitename_match: no match between server: %s and client: %s\n",
143 ads->config.server_site_name ? ads->config.server_site_name : "NULL",
144 ads->config.client_site_name ? ads->config.client_site_name : "NULL"));
148 /**********************************************
149 Is this the closest DC ?
150 **********************************************/
152 bool ads_closest_dc(ADS_STRUCT *ads)
154 if (ads->config.flags & NBT_SERVER_CLOSEST) {
155 DEBUG(10,("ads_closest_dc: NBT_SERVER_CLOSEST flag set\n"));
159 /* not sure if this can ever happen */
160 if (ads_sitename_match(ads)) {
161 DEBUG(10,("ads_closest_dc: NBT_SERVER_CLOSEST flag not set but sites match\n"));
165 DEBUG(10,("ads_closest_dc: %s is not the closest DC\n",
166 ads->config.ldap_server_name));
173 try a connection to a given ldap server, returning True and setting the servers IP
174 in the ads struct if successful
176 bool ads_try_connect(ADS_STRUCT *ads, const char *server )
179 struct nbt_cldap_netlogon_5 cldap_reply;
180 TALLOC_CTX *mem_ctx = NULL;
183 if (!server || !*server) {
187 DEBUG(5,("ads_try_connect: sending CLDAP request to %s (realm: %s)\n",
188 server, ads->server.realm));
190 mem_ctx = talloc_init("ads_try_connect");
192 DEBUG(0,("out of memory\n"));
196 /* this copes with inet_ntoa brokenness */
198 srv = SMB_STRDUP(server);
200 ZERO_STRUCT( cldap_reply );
202 if ( !ads_cldap_netlogon_5(mem_ctx, srv, ads->server.realm, &cldap_reply ) ) {
203 DEBUG(3,("ads_try_connect: CLDAP request %s failed.\n", srv));
208 /* Check the CLDAP reply flags */
210 if ( !(cldap_reply.server_type & NBT_SERVER_LDAP) ) {
211 DEBUG(1,("ads_try_connect: %s's CLDAP reply says it is not an LDAP server!\n",
217 /* Fill in the ads->config values */
219 SAFE_FREE(ads->config.realm);
220 SAFE_FREE(ads->config.bind_path);
221 SAFE_FREE(ads->config.ldap_server_name);
222 SAFE_FREE(ads->config.server_site_name);
223 SAFE_FREE(ads->config.client_site_name);
224 SAFE_FREE(ads->server.workgroup);
226 ads->config.flags = cldap_reply.server_type;
227 ads->config.ldap_server_name = SMB_STRDUP(cldap_reply.pdc_dns_name);
228 ads->config.realm = SMB_STRDUP(cldap_reply.dns_domain);
229 strupper_m(ads->config.realm);
230 ads->config.bind_path = ads_build_dn(ads->config.realm);
231 if (*cldap_reply.server_site) {
232 ads->config.server_site_name =
233 SMB_STRDUP(cldap_reply.server_site);
235 if (*cldap_reply.client_site) {
236 ads->config.client_site_name =
237 SMB_STRDUP(cldap_reply.client_site);
239 ads->server.workgroup = SMB_STRDUP(cldap_reply.domain);
241 ads->ldap.port = LDAP_PORT;
242 if (!interpret_string_addr(&ads->ldap.ss, srv, 0)) {
243 DEBUG(1,("ads_try_connect: unable to convert %s "
250 /* Store our site name. */
251 sitename_store( cldap_reply.domain, cldap_reply.client_site);
256 TALLOC_FREE(mem_ctx);
261 /**********************************************************************
262 Try to find an AD dc using our internal name resolution routines
263 Try the realm first and then then workgroup name if netbios is not
265 **********************************************************************/
267 static NTSTATUS ads_find_dc(ADS_STRUCT *ads)
271 struct ip_service *ip_list;
273 bool got_realm = False;
274 bool use_own_domain = False;
276 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
278 /* if the realm and workgroup are both empty, assume they are ours */
281 c_realm = ads->server.realm;
283 if ( !c_realm || !*c_realm ) {
284 /* special case where no realm and no workgroup means our own */
285 if ( !ads->server.workgroup || !*ads->server.workgroup ) {
286 use_own_domain = True;
287 c_realm = lp_realm();
291 if (c_realm && *c_realm)
294 /* we need to try once with the realm name and fallback to the
295 netbios domain name if we fail (if netbios has not been disabled */
297 if ( !got_realm && !lp_disable_netbios() ) {
298 c_realm = ads->server.workgroup;
299 if (!c_realm || !*c_realm) {
300 if ( use_own_domain )
301 c_realm = lp_workgroup();
304 if ( !c_realm || !*c_realm ) {
305 DEBUG(0,("ads_find_dc: no realm or workgroup! Don't know what to do\n"));
306 return NT_STATUS_INVALID_PARAMETER; /* rather need MISSING_PARAMETER ... */
312 sitename = sitename_fetch(realm);
316 DEBUG(6,("ads_find_dc: looking for %s '%s'\n",
317 (got_realm ? "realm" : "domain"), realm));
319 status = get_sorted_dc_list(realm, sitename, &ip_list, &count, got_realm);
320 if (!NT_STATUS_IS_OK(status)) {
321 /* fall back to netbios if we can */
322 if ( got_realm && !lp_disable_netbios() ) {
331 /* if we fail this loop, then giveup since all the IP addresses returned were dead */
332 for ( i=0; i<count; i++ ) {
333 char server[INET6_ADDRSTRLEN];
335 print_sockaddr(server, sizeof(server), &ip_list[i].ss);
337 if ( !NT_STATUS_IS_OK(check_negative_conn_cache(realm, server)) )
341 /* realm in this case is a workgroup name. We need
342 to ignore any IP addresses in the negative connection
343 cache that match ip addresses returned in the ad realm
344 case. It sucks that I have to reproduce the logic above... */
345 c_realm = ads->server.realm;
346 if ( !c_realm || !*c_realm ) {
347 if ( !ads->server.workgroup || !*ads->server.workgroup ) {
348 c_realm = lp_realm();
351 if (c_realm && *c_realm &&
352 !NT_STATUS_IS_OK(check_negative_conn_cache(c_realm, server))) {
353 /* Ensure we add the workgroup name for this
354 IP address as negative too. */
355 add_failed_connection_entry( realm, server, NT_STATUS_UNSUCCESSFUL );
360 if ( ads_try_connect(ads, server) ) {
366 /* keep track of failures */
367 add_failed_connection_entry( realm, server, NT_STATUS_UNSUCCESSFUL );
372 /* In case we failed to contact one of our closest DC on our site we
373 * need to try to find another DC, retry with a site-less SRV DNS query
377 DEBUG(1,("ads_find_dc: failed to find a valid DC on our site (%s), "
378 "trying to find another DC\n", sitename));
380 namecache_delete(realm, 0x1C);
384 return NT_STATUS_NO_LOGON_SERVERS;
389 * Connect to the LDAP server
390 * @param ads Pointer to an existing ADS_STRUCT
391 * @return status of connection
393 ADS_STATUS ads_connect(ADS_STRUCT *ads)
395 int version = LDAP_VERSION3;
398 char addr[INET6_ADDRSTRLEN];
400 ZERO_STRUCT(ads->ldap);
401 ads->ldap.last_attempt = time(NULL);
402 ads->ldap.wrap_type = ADS_SASLWRAP_TYPE_PLAIN;
404 /* try with a user specified server */
406 if (DEBUGLEVEL >= 11) {
407 char *s = NDR_PRINT_STRUCT_STRING(talloc_tos(), ads_struct, ads);
408 DEBUG(11,("ads_connect: entering\n"));
409 DEBUGADD(11,("%s\n", s));
413 if (ads->server.ldap_server &&
414 ads_try_connect(ads, ads->server.ldap_server)) {
418 ntstatus = ads_find_dc(ads);
419 if (NT_STATUS_IS_OK(ntstatus)) {
423 status = ADS_ERROR_NT(ntstatus);
428 print_sockaddr(addr, sizeof(addr), &ads->ldap.ss);
429 DEBUG(3,("Successfully contacted LDAP server %s\n", addr));
431 if (!ads->auth.user_name) {
432 /* Must use the userPrincipalName value here or sAMAccountName
433 and not servicePrincipalName; found by Guenther Deschner */
435 asprintf(&ads->auth.user_name, "%s$", global_myname() );
438 if (!ads->auth.realm) {
439 ads->auth.realm = SMB_STRDUP(ads->config.realm);
442 if (!ads->auth.kdc_server) {
443 print_sockaddr(addr, sizeof(addr), &ads->ldap.ss);
444 ads->auth.kdc_server = SMB_STRDUP(addr);
448 /* this is a really nasty hack to avoid ADS DNS problems. It needs a patch
449 to MIT kerberos to work (tridge) */
452 asprintf(&env, "KRB5_KDC_ADDRESS_%s", ads->config.realm);
453 setenv(env, ads->auth.kdc_server, 1);
458 /* If the caller() requested no LDAP bind, then we are done */
460 if (ads->auth.flags & ADS_AUTH_NO_BIND) {
461 status = ADS_SUCCESS;
465 ads->ldap.mem_ctx = talloc_init("ads LDAP connection memory");
466 if (!ads->ldap.mem_ctx) {
467 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
471 /* Otherwise setup the TCP LDAP session */
473 ads->ldap.ld = ldap_open_with_timeout(ads->config.ldap_server_name,
474 LDAP_PORT, lp_ldap_timeout());
475 if (ads->ldap.ld == NULL) {
476 status = ADS_ERROR(LDAP_OPERATIONS_ERROR);
479 DEBUG(3,("Connected to LDAP server %s\n", ads->config.ldap_server_name));
481 /* cache the successful connection for workgroup and realm */
482 if (ads_closest_dc(ads)) {
483 print_sockaddr(addr, sizeof(addr), &ads->ldap.ss);
484 saf_store( ads->server.workgroup, addr);
485 saf_store( ads->server.realm, addr);
488 ldap_set_option(ads->ldap.ld, LDAP_OPT_PROTOCOL_VERSION, &version);
490 status = ADS_ERROR(smb_ldap_start_tls(ads->ldap.ld, version));
491 if (!ADS_ERR_OK(status)) {
495 /* fill in the current time and offsets */
497 status = ads_current_time( ads );
498 if ( !ADS_ERR_OK(status) ) {
502 /* Now do the bind */
504 if (ads->auth.flags & ADS_AUTH_ANON_BIND) {
505 status = ADS_ERROR(ldap_simple_bind_s(ads->ldap.ld, NULL, NULL));
509 if (ads->auth.flags & ADS_AUTH_SIMPLE_BIND) {
510 status = ADS_ERROR(ldap_simple_bind_s(ads->ldap.ld, ads->auth.user_name, ads->auth.password));
514 status = ads_sasl_bind(ads);
517 if (DEBUGLEVEL >= 11) {
518 char *s = NDR_PRINT_STRUCT_STRING(talloc_tos(), ads_struct, ads);
519 DEBUG(11,("ads_connect: leaving with: %s\n",
520 ads_errstr(status)));
521 DEBUGADD(11,("%s\n", s));
529 * Disconnect the LDAP server
530 * @param ads Pointer to an existing ADS_STRUCT
532 void ads_disconnect(ADS_STRUCT *ads)
535 ldap_unbind(ads->ldap.ld);
538 if (ads->ldap.wrap_ops && ads->ldap.wrap_ops->disconnect) {
539 ads->ldap.wrap_ops->disconnect(ads);
541 if (ads->ldap.mem_ctx) {
542 talloc_free(ads->ldap.mem_ctx);
544 ZERO_STRUCT(ads->ldap);
548 Duplicate a struct berval into talloc'ed memory
550 static struct berval *dup_berval(TALLOC_CTX *ctx, const struct berval *in_val)
552 struct berval *value;
554 if (!in_val) return NULL;
556 value = TALLOC_ZERO_P(ctx, struct berval);
559 if (in_val->bv_len == 0) return value;
561 value->bv_len = in_val->bv_len;
562 value->bv_val = (char *)TALLOC_MEMDUP(ctx, in_val->bv_val,
568 Make a values list out of an array of (struct berval *)
570 static struct berval **ads_dup_values(TALLOC_CTX *ctx,
571 const struct berval **in_vals)
573 struct berval **values;
576 if (!in_vals) return NULL;
577 for (i=0; in_vals[i]; i++)
579 values = TALLOC_ZERO_ARRAY(ctx, struct berval *, i+1);
580 if (!values) return NULL;
582 for (i=0; in_vals[i]; i++) {
583 values[i] = dup_berval(ctx, in_vals[i]);
589 UTF8-encode a values list out of an array of (char *)
591 static char **ads_push_strvals(TALLOC_CTX *ctx, const char **in_vals)
596 if (!in_vals) return NULL;
597 for (i=0; in_vals[i]; i++)
599 values = TALLOC_ZERO_ARRAY(ctx, char *, i+1);
600 if (!values) return NULL;
602 for (i=0; in_vals[i]; i++) {
603 if (push_utf8_talloc(ctx, &values[i], in_vals[i]) == (size_t) -1) {
612 Pull a (char *) array out of a UTF8-encoded values list
614 static char **ads_pull_strvals(TALLOC_CTX *ctx, const char **in_vals)
619 if (!in_vals) return NULL;
620 for (i=0; in_vals[i]; i++)
622 values = TALLOC_ZERO_ARRAY(ctx, char *, i+1);
623 if (!values) return NULL;
625 for (i=0; in_vals[i]; i++) {
626 pull_utf8_talloc(ctx, &values[i], in_vals[i]);
632 * Do a search with paged results. cookie must be null on the first
633 * call, and then returned on each subsequent call. It will be null
634 * again when the entire search is complete
635 * @param ads connection to ads server
636 * @param bind_path Base dn for the search
637 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
638 * @param expr Search expression - specified in local charset
639 * @param attrs Attributes to retrieve - specified in utf8 or ascii
640 * @param res ** which will contain results - free res* with ads_msgfree()
641 * @param count Number of entries retrieved on this page
642 * @param cookie The paged results cookie to be returned on subsequent calls
643 * @return status of search
645 static ADS_STATUS ads_do_paged_search_args(ADS_STRUCT *ads,
646 const char *bind_path,
647 int scope, const char *expr,
648 const char **attrs, void *args,
650 int *count, struct berval **cookie)
653 char *utf8_expr, *utf8_path, **search_attrs;
654 LDAPControl PagedResults, NoReferrals, ExternalCtrl, *controls[4], **rcontrols;
655 BerElement *cookie_be = NULL;
656 struct berval *cookie_bv= NULL;
657 BerElement *ext_be = NULL;
658 struct berval *ext_bv= NULL;
661 ads_control *external_control = (ads_control *) args;
665 if (!(ctx = talloc_init("ads_do_paged_search_args")))
666 return ADS_ERROR(LDAP_NO_MEMORY);
668 /* 0 means the conversion worked but the result was empty
669 so we only fail if it's -1. In any case, it always
670 at least nulls out the dest */
671 if ((push_utf8_talloc(ctx, &utf8_expr, expr) == (size_t)-1) ||
672 (push_utf8_talloc(ctx, &utf8_path, bind_path) == (size_t)-1)) {
677 if (!attrs || !(*attrs))
680 /* This would be the utf8-encoded version...*/
681 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
682 if (!(str_list_copy(talloc_tos(), &search_attrs, attrs))) {
688 /* Paged results only available on ldap v3 or later */
689 ldap_get_option(ads->ldap.ld, LDAP_OPT_PROTOCOL_VERSION, &version);
690 if (version < LDAP_VERSION3) {
691 rc = LDAP_NOT_SUPPORTED;
695 cookie_be = ber_alloc_t(LBER_USE_DER);
697 ber_printf(cookie_be, "{iO}", (ber_int_t) 1000, *cookie);
698 ber_bvfree(*cookie); /* don't need it from last time */
701 ber_printf(cookie_be, "{io}", (ber_int_t) 1000, "", 0);
703 ber_flatten(cookie_be, &cookie_bv);
704 PagedResults.ldctl_oid = CONST_DISCARD(char *, ADS_PAGE_CTL_OID);
705 PagedResults.ldctl_iscritical = (char) 1;
706 PagedResults.ldctl_value.bv_len = cookie_bv->bv_len;
707 PagedResults.ldctl_value.bv_val = cookie_bv->bv_val;
709 NoReferrals.ldctl_oid = CONST_DISCARD(char *, ADS_NO_REFERRALS_OID);
710 NoReferrals.ldctl_iscritical = (char) 0;
711 NoReferrals.ldctl_value.bv_len = 0;
712 NoReferrals.ldctl_value.bv_val = CONST_DISCARD(char *, "");
714 if (external_control &&
715 (strequal(external_control->control, ADS_EXTENDED_DN_OID) ||
716 strequal(external_control->control, ADS_SD_FLAGS_OID))) {
718 ExternalCtrl.ldctl_oid = CONST_DISCARD(char *, external_control->control);
719 ExternalCtrl.ldctl_iscritical = (char) external_control->critical;
721 /* win2k does not accept a ldctl_value beeing passed in */
723 if (external_control->val != 0) {
725 if ((ext_be = ber_alloc_t(LBER_USE_DER)) == NULL ) {
730 if ((ber_printf(ext_be, "{i}", (ber_int_t) external_control->val)) == -1) {
734 if ((ber_flatten(ext_be, &ext_bv)) == -1) {
739 ExternalCtrl.ldctl_value.bv_len = ext_bv->bv_len;
740 ExternalCtrl.ldctl_value.bv_val = ext_bv->bv_val;
743 ExternalCtrl.ldctl_value.bv_len = 0;
744 ExternalCtrl.ldctl_value.bv_val = NULL;
747 controls[0] = &NoReferrals;
748 controls[1] = &PagedResults;
749 controls[2] = &ExternalCtrl;
753 controls[0] = &NoReferrals;
754 controls[1] = &PagedResults;
758 /* we need to disable referrals as the openldap libs don't
759 handle them and paged results at the same time. Using them
760 together results in the result record containing the server
761 page control being removed from the result list (tridge/jmcd)
763 leaving this in despite the control that says don't generate
764 referrals, in case the server doesn't support it (jmcd)
766 ldap_set_option(ads->ldap.ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
768 rc = ldap_search_with_timeout(ads->ldap.ld, utf8_path, scope, utf8_expr,
769 search_attrs, 0, controls,
771 (LDAPMessage **)res);
773 ber_free(cookie_be, 1);
774 ber_bvfree(cookie_bv);
777 DEBUG(3,("ads_do_paged_search_args: ldap_search_with_timeout(%s) -> %s\n", expr,
778 ldap_err2string(rc)));
782 rc = ldap_parse_result(ads->ldap.ld, *res, NULL, NULL, NULL,
783 NULL, &rcontrols, 0);
789 for (i=0; rcontrols[i]; i++) {
790 if (strcmp(ADS_PAGE_CTL_OID, rcontrols[i]->ldctl_oid) == 0) {
791 cookie_be = ber_init(&rcontrols[i]->ldctl_value);
792 ber_scanf(cookie_be,"{iO}", (ber_int_t *) count,
794 /* the berval is the cookie, but must be freed when
796 if (cookie_bv->bv_len) /* still more to do */
797 *cookie=ber_bvdup(cookie_bv);
800 ber_bvfree(cookie_bv);
801 ber_free(cookie_be, 1);
805 ldap_controls_free(rcontrols);
818 /* if/when we decide to utf8-encode attrs, take out this next line */
819 TALLOC_FREE(search_attrs);
821 return ADS_ERROR(rc);
824 static ADS_STATUS ads_do_paged_search(ADS_STRUCT *ads, const char *bind_path,
825 int scope, const char *expr,
826 const char **attrs, LDAPMessage **res,
827 int *count, struct berval **cookie)
829 return ads_do_paged_search_args(ads, bind_path, scope, expr, attrs, NULL, res, count, cookie);
834 * Get all results for a search. This uses ads_do_paged_search() to return
835 * all entries in a large search.
836 * @param ads connection to ads server
837 * @param bind_path Base dn for the search
838 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
839 * @param expr Search expression
840 * @param attrs Attributes to retrieve
841 * @param res ** which will contain results - free res* with ads_msgfree()
842 * @return status of search
844 ADS_STATUS ads_do_search_all_args(ADS_STRUCT *ads, const char *bind_path,
845 int scope, const char *expr,
846 const char **attrs, void *args,
849 struct berval *cookie = NULL;
854 status = ads_do_paged_search_args(ads, bind_path, scope, expr, attrs, args, res,
857 if (!ADS_ERR_OK(status))
860 #ifdef HAVE_LDAP_ADD_RESULT_ENTRY
862 LDAPMessage *res2 = NULL;
864 LDAPMessage *msg, *next;
866 status2 = ads_do_paged_search_args(ads, bind_path, scope, expr,
867 attrs, args, &res2, &count, &cookie);
869 if (!ADS_ERR_OK(status2)) break;
871 /* this relies on the way that ldap_add_result_entry() works internally. I hope
872 that this works on all ldap libs, but I have only tested with openldap */
873 for (msg = ads_first_message(ads, res2); msg; msg = next) {
874 next = ads_next_message(ads, msg);
875 ldap_add_result_entry((LDAPMessage **)res, msg);
877 /* note that we do not free res2, as the memory is now
878 part of the main returned list */
881 DEBUG(0, ("no ldap_add_result_entry() support in LDAP libs!\n"));
882 status = ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
888 ADS_STATUS ads_do_search_all(ADS_STRUCT *ads, const char *bind_path,
889 int scope, const char *expr,
890 const char **attrs, LDAPMessage **res)
892 return ads_do_search_all_args(ads, bind_path, scope, expr, attrs, NULL, res);
895 ADS_STATUS ads_do_search_all_sd_flags(ADS_STRUCT *ads, const char *bind_path,
896 int scope, const char *expr,
897 const char **attrs, uint32 sd_flags,
902 args.control = ADS_SD_FLAGS_OID;
904 args.critical = True;
906 return ads_do_search_all_args(ads, bind_path, scope, expr, attrs, &args, res);
911 * Run a function on all results for a search. Uses ads_do_paged_search() and
912 * runs the function as each page is returned, using ads_process_results()
913 * @param ads connection to ads server
914 * @param bind_path Base dn for the search
915 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
916 * @param expr Search expression - specified in local charset
917 * @param attrs Attributes to retrieve - specified in UTF-8 or ascii
918 * @param fn Function which takes attr name, values list, and data_area
919 * @param data_area Pointer which is passed to function on each call
920 * @return status of search
922 ADS_STATUS ads_do_search_all_fn(ADS_STRUCT *ads, const char *bind_path,
923 int scope, const char *expr, const char **attrs,
924 bool (*fn)(ADS_STRUCT *, char *, void **, void *),
927 struct berval *cookie = NULL;
932 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs, &res,
935 if (!ADS_ERR_OK(status)) return status;
937 ads_process_results(ads, res, fn, data_area);
938 ads_msgfree(ads, res);
941 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs,
942 &res, &count, &cookie);
944 if (!ADS_ERR_OK(status)) break;
946 ads_process_results(ads, res, fn, data_area);
947 ads_msgfree(ads, res);
954 * Do a search with a timeout.
955 * @param ads connection to ads server
956 * @param bind_path Base dn for the search
957 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
958 * @param expr Search expression
959 * @param attrs Attributes to retrieve
960 * @param res ** which will contain results - free res* with ads_msgfree()
961 * @return status of search
963 ADS_STATUS ads_do_search(ADS_STRUCT *ads, const char *bind_path, int scope,
965 const char **attrs, LDAPMessage **res)
968 char *utf8_expr, *utf8_path, **search_attrs = NULL;
972 if (!(ctx = talloc_init("ads_do_search"))) {
973 DEBUG(1,("ads_do_search: talloc_init() failed!"));
974 return ADS_ERROR(LDAP_NO_MEMORY);
977 /* 0 means the conversion worked but the result was empty
978 so we only fail if it's negative. In any case, it always
979 at least nulls out the dest */
980 if ((push_utf8_talloc(ctx, &utf8_expr, expr) == (size_t)-1) ||
981 (push_utf8_talloc(ctx, &utf8_path, bind_path) == (size_t)-1)) {
982 DEBUG(1,("ads_do_search: push_utf8_talloc() failed!"));
987 if (!attrs || !(*attrs))
990 /* This would be the utf8-encoded version...*/
991 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
992 if (!(str_list_copy(talloc_tos(), &search_attrs, attrs)))
994 DEBUG(1,("ads_do_search: str_list_copy() failed!"));
1000 /* see the note in ads_do_paged_search - we *must* disable referrals */
1001 ldap_set_option(ads->ldap.ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
1003 rc = ldap_search_with_timeout(ads->ldap.ld, utf8_path, scope, utf8_expr,
1004 search_attrs, 0, NULL, NULL,
1006 (LDAPMessage **)res);
1008 if (rc == LDAP_SIZELIMIT_EXCEEDED) {
1009 DEBUG(3,("Warning! sizelimit exceeded in ldap. Truncating.\n"));
1014 talloc_destroy(ctx);
1015 /* if/when we decide to utf8-encode attrs, take out this next line */
1016 TALLOC_FREE(search_attrs);
1017 return ADS_ERROR(rc);
1020 * Do a general ADS search
1021 * @param ads connection to ads server
1022 * @param res ** which will contain results - free res* with ads_msgfree()
1023 * @param expr Search expression
1024 * @param attrs Attributes to retrieve
1025 * @return status of search
1027 ADS_STATUS ads_search(ADS_STRUCT *ads, LDAPMessage **res,
1028 const char *expr, const char **attrs)
1030 return ads_do_search(ads, ads->config.bind_path, LDAP_SCOPE_SUBTREE,
1035 * Do a search on a specific DistinguishedName
1036 * @param ads connection to ads server
1037 * @param res ** which will contain results - free res* with ads_msgfree()
1038 * @param dn DistinguishName to search
1039 * @param attrs Attributes to retrieve
1040 * @return status of search
1042 ADS_STATUS ads_search_dn(ADS_STRUCT *ads, LDAPMessage **res,
1043 const char *dn, const char **attrs)
1045 return ads_do_search(ads, dn, LDAP_SCOPE_BASE, "(objectclass=*)",
1050 * Free up memory from a ads_search
1051 * @param ads connection to ads server
1052 * @param msg Search results to free
1054 void ads_msgfree(ADS_STRUCT *ads, LDAPMessage *msg)
1061 * Free up memory from various ads requests
1062 * @param ads connection to ads server
1063 * @param mem Area to free
1065 void ads_memfree(ADS_STRUCT *ads, void *mem)
1071 * Get a dn from search results
1072 * @param ads connection to ads server
1073 * @param msg Search result
1076 char *ads_get_dn(ADS_STRUCT *ads, LDAPMessage *msg)
1078 char *utf8_dn, *unix_dn;
1080 utf8_dn = ldap_get_dn(ads->ldap.ld, msg);
1083 DEBUG (5, ("ads_get_dn: ldap_get_dn failed\n"));
1087 if (pull_utf8_allocate(&unix_dn, utf8_dn) == (size_t)-1) {
1088 DEBUG(0,("ads_get_dn: string conversion failure utf8 [%s]\n",
1092 ldap_memfree(utf8_dn);
1097 * Get the parent from a dn
1098 * @param dn the dn to return the parent from
1099 * @return parent dn string
1101 char *ads_parent_dn(const char *dn)
1109 p = strchr(dn, ',');
1119 * Find a machine account given a hostname
1120 * @param ads connection to ads server
1121 * @param res ** which will contain results - free res* with ads_msgfree()
1122 * @param host Hostname to search for
1123 * @return status of search
1125 ADS_STATUS ads_find_machine_acct(ADS_STRUCT *ads, LDAPMessage **res,
1126 const char *machine)
1130 const char *attrs[] = {"*", "nTSecurityDescriptor", NULL};
1134 /* the easiest way to find a machine account anywhere in the tree
1135 is to look for hostname$ */
1136 if (asprintf(&expr, "(samAccountName=%s$)", machine) == -1) {
1137 DEBUG(1, ("asprintf failed!\n"));
1138 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1141 status = ads_search(ads, res, expr, attrs);
1147 * Initialize a list of mods to be used in a modify request
1148 * @param ctx An initialized TALLOC_CTX
1149 * @return allocated ADS_MODLIST
1151 ADS_MODLIST ads_init_mods(TALLOC_CTX *ctx)
1153 #define ADS_MODLIST_ALLOC_SIZE 10
1156 if ((mods = TALLOC_ZERO_ARRAY(ctx, LDAPMod *, ADS_MODLIST_ALLOC_SIZE + 1)))
1157 /* -1 is safety to make sure we don't go over the end.
1158 need to reset it to NULL before doing ldap modify */
1159 mods[ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1161 return (ADS_MODLIST)mods;
1166 add an attribute to the list, with values list already constructed
1168 static ADS_STATUS ads_modlist_add(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1169 int mod_op, const char *name,
1170 const void *_invals)
1172 const void **invals = (const void **)_invals;
1174 LDAPMod **modlist = (LDAPMod **) *mods;
1175 struct berval **ber_values = NULL;
1176 char **char_values = NULL;
1179 mod_op = LDAP_MOD_DELETE;
1181 if (mod_op & LDAP_MOD_BVALUES)
1182 ber_values = ads_dup_values(ctx,
1183 (const struct berval **)invals);
1185 char_values = ads_push_strvals(ctx,
1186 (const char **) invals);
1189 /* find the first empty slot */
1190 for (curmod=0; modlist[curmod] && modlist[curmod] != (LDAPMod *) -1;
1192 if (modlist[curmod] == (LDAPMod *) -1) {
1193 if (!(modlist = TALLOC_REALLOC_ARRAY(ctx, modlist, LDAPMod *,
1194 curmod+ADS_MODLIST_ALLOC_SIZE+1)))
1195 return ADS_ERROR(LDAP_NO_MEMORY);
1196 memset(&modlist[curmod], 0,
1197 ADS_MODLIST_ALLOC_SIZE*sizeof(LDAPMod *));
1198 modlist[curmod+ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1199 *mods = (ADS_MODLIST)modlist;
1202 if (!(modlist[curmod] = TALLOC_ZERO_P(ctx, LDAPMod)))
1203 return ADS_ERROR(LDAP_NO_MEMORY);
1204 modlist[curmod]->mod_type = talloc_strdup(ctx, name);
1205 if (mod_op & LDAP_MOD_BVALUES) {
1206 modlist[curmod]->mod_bvalues = ber_values;
1207 } else if (mod_op & LDAP_MOD_DELETE) {
1208 modlist[curmod]->mod_values = NULL;
1210 modlist[curmod]->mod_values = char_values;
1213 modlist[curmod]->mod_op = mod_op;
1214 return ADS_ERROR(LDAP_SUCCESS);
1218 * Add a single string value to a mod list
1219 * @param ctx An initialized TALLOC_CTX
1220 * @param mods An initialized ADS_MODLIST
1221 * @param name The attribute name to add
1222 * @param val The value to add - NULL means DELETE
1223 * @return ADS STATUS indicating success of add
1225 ADS_STATUS ads_mod_str(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1226 const char *name, const char *val)
1228 const char *values[2];
1234 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1235 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE, name, values);
1239 * Add an array of string values to a mod list
1240 * @param ctx An initialized TALLOC_CTX
1241 * @param mods An initialized ADS_MODLIST
1242 * @param name The attribute name to add
1243 * @param vals The array of string values to add - NULL means DELETE
1244 * @return ADS STATUS indicating success of add
1246 ADS_STATUS ads_mod_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1247 const char *name, const char **vals)
1250 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1251 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE,
1252 name, (const void **) vals);
1257 * Add a single ber-encoded value to a mod list
1258 * @param ctx An initialized TALLOC_CTX
1259 * @param mods An initialized ADS_MODLIST
1260 * @param name The attribute name to add
1261 * @param val The value to add - NULL means DELETE
1262 * @return ADS STATUS indicating success of add
1264 static ADS_STATUS ads_mod_ber(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1265 const char *name, const struct berval *val)
1267 const struct berval *values[2];
1272 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1273 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE|LDAP_MOD_BVALUES,
1274 name, (const void **) values);
1279 * Perform an ldap modify
1280 * @param ads connection to ads server
1281 * @param mod_dn DistinguishedName to modify
1282 * @param mods list of modifications to perform
1283 * @return status of modify
1285 ADS_STATUS ads_gen_mod(ADS_STRUCT *ads, const char *mod_dn, ADS_MODLIST mods)
1288 char *utf8_dn = NULL;
1290 this control is needed to modify that contains a currently
1291 non-existent attribute (but allowable for the object) to run
1293 LDAPControl PermitModify = {
1294 CONST_DISCARD(char *, ADS_PERMIT_MODIFY_OID),
1297 LDAPControl *controls[2];
1299 controls[0] = &PermitModify;
1302 if (push_utf8_allocate(&utf8_dn, mod_dn) == -1) {
1303 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1306 /* find the end of the list, marked by NULL or -1 */
1307 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1308 /* make sure the end of the list is NULL */
1310 ret = ldap_modify_ext_s(ads->ldap.ld, utf8_dn,
1311 (LDAPMod **) mods, controls, NULL);
1313 return ADS_ERROR(ret);
1317 * Perform an ldap add
1318 * @param ads connection to ads server
1319 * @param new_dn DistinguishedName to add
1320 * @param mods list of attributes and values for DN
1321 * @return status of add
1323 ADS_STATUS ads_gen_add(ADS_STRUCT *ads, const char *new_dn, ADS_MODLIST mods)
1326 char *utf8_dn = NULL;
1328 if (push_utf8_allocate(&utf8_dn, new_dn) == -1) {
1329 DEBUG(1, ("ads_gen_add: push_utf8_allocate failed!"));
1330 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1333 /* find the end of the list, marked by NULL or -1 */
1334 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1335 /* make sure the end of the list is NULL */
1338 ret = ldap_add_s(ads->ldap.ld, utf8_dn, (LDAPMod**)mods);
1340 return ADS_ERROR(ret);
1344 * Delete a DistinguishedName
1345 * @param ads connection to ads server
1346 * @param new_dn DistinguishedName to delete
1347 * @return status of delete
1349 ADS_STATUS ads_del_dn(ADS_STRUCT *ads, char *del_dn)
1352 char *utf8_dn = NULL;
1353 if (push_utf8_allocate(&utf8_dn, del_dn) == -1) {
1354 DEBUG(1, ("ads_del_dn: push_utf8_allocate failed!"));
1355 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1358 ret = ldap_delete_s(ads->ldap.ld, utf8_dn);
1360 return ADS_ERROR(ret);
1364 * Build an org unit string
1365 * if org unit is Computers or blank then assume a container, otherwise
1366 * assume a / separated list of organisational units.
1367 * jmcd: '\' is now used for escapes so certain chars can be in the ou (e.g. #)
1368 * @param ads connection to ads server
1369 * @param org_unit Organizational unit
1370 * @return org unit string - caller must free
1372 char *ads_ou_string(ADS_STRUCT *ads, const char *org_unit)
1376 if (!org_unit || !*org_unit) {
1378 ret = ads_default_ou_string(ads, WELL_KNOWN_GUID_COMPUTERS);
1380 /* samba4 might not yet respond to a wellknownobject-query */
1381 return ret ? ret : SMB_STRDUP("cn=Computers");
1384 if (strequal(org_unit, "Computers")) {
1385 return SMB_STRDUP("cn=Computers");
1388 /* jmcd: removed "\\" from the separation chars, because it is
1389 needed as an escape for chars like '#' which are valid in an
1391 return ads_build_path(org_unit, "/", "ou=", 1);
1395 * Get a org unit string for a well-known GUID
1396 * @param ads connection to ads server
1397 * @param wknguid Well known GUID
1398 * @return org unit string - caller must free
1400 char *ads_default_ou_string(ADS_STRUCT *ads, const char *wknguid)
1403 LDAPMessage *res = NULL;
1404 char *base, *wkn_dn = NULL, *ret = NULL, **wkn_dn_exp = NULL,
1405 **bind_dn_exp = NULL;
1406 const char *attrs[] = {"distinguishedName", NULL};
1407 int new_ln, wkn_ln, bind_ln, i;
1409 if (wknguid == NULL) {
1413 if (asprintf(&base, "<WKGUID=%s,%s>", wknguid, ads->config.bind_path ) == -1) {
1414 DEBUG(1, ("asprintf failed!\n"));
1418 status = ads_search_dn(ads, &res, base, attrs);
1419 if (!ADS_ERR_OK(status)) {
1420 DEBUG(1,("Failed while searching for: %s\n", base));
1424 if (ads_count_replies(ads, res) != 1) {
1428 /* substitute the bind-path from the well-known-guid-search result */
1429 wkn_dn = ads_get_dn(ads, res);
1434 wkn_dn_exp = ldap_explode_dn(wkn_dn, 0);
1439 bind_dn_exp = ldap_explode_dn(ads->config.bind_path, 0);
1444 for (wkn_ln=0; wkn_dn_exp[wkn_ln]; wkn_ln++)
1446 for (bind_ln=0; bind_dn_exp[bind_ln]; bind_ln++)
1449 new_ln = wkn_ln - bind_ln;
1451 ret = SMB_STRDUP(wkn_dn_exp[0]);
1456 for (i=1; i < new_ln; i++) {
1459 if (asprintf(&s, "%s,%s", ret, wkn_dn_exp[i]) == -1) {
1465 ret = SMB_STRDUP(s);
1474 ads_msgfree(ads, res);
1475 ads_memfree(ads, wkn_dn);
1477 ldap_value_free(wkn_dn_exp);
1480 ldap_value_free(bind_dn_exp);
1487 * Adds (appends) an item to an attribute array, rather then
1488 * replacing the whole list
1489 * @param ctx An initialized TALLOC_CTX
1490 * @param mods An initialized ADS_MODLIST
1491 * @param name name of the ldap attribute to append to
1492 * @param vals an array of values to add
1493 * @return status of addition
1496 ADS_STATUS ads_add_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1497 const char *name, const char **vals)
1499 return ads_modlist_add(ctx, mods, LDAP_MOD_ADD, name,
1500 (const void *) vals);
1504 * Determines the computer account's current KVNO via an LDAP lookup
1505 * @param ads An initialized ADS_STRUCT
1506 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1507 * @return the kvno for the computer account, or -1 in case of a failure.
1510 uint32 ads_get_kvno(ADS_STRUCT *ads, const char *machine_name)
1512 LDAPMessage *res = NULL;
1513 uint32 kvno = (uint32)-1; /* -1 indicates a failure */
1515 const char *attrs[] = {"msDS-KeyVersionNumber", NULL};
1516 char *dn_string = NULL;
1517 ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS);
1519 DEBUG(5,("ads_get_kvno: Searching for host %s\n", machine_name));
1520 if (asprintf(&filter, "(samAccountName=%s$)", machine_name) == -1) {
1523 ret = ads_search(ads, &res, filter, attrs);
1525 if (!ADS_ERR_OK(ret) && ads_count_replies(ads, res)) {
1526 DEBUG(1,("ads_get_kvno: Computer Account For %s not found.\n", machine_name));
1527 ads_msgfree(ads, res);
1531 dn_string = ads_get_dn(ads, res);
1533 DEBUG(0,("ads_get_kvno: out of memory.\n"));
1534 ads_msgfree(ads, res);
1537 DEBUG(5,("ads_get_kvno: Using: %s\n", dn_string));
1538 ads_memfree(ads, dn_string);
1540 /* ---------------------------------------------------------
1541 * 0 is returned as a default KVNO from this point on...
1542 * This is done because Windows 2000 does not support key
1543 * version numbers. Chances are that a failure in the next
1544 * step is simply due to Windows 2000 being used for a
1545 * domain controller. */
1548 if (!ads_pull_uint32(ads, res, "msDS-KeyVersionNumber", &kvno)) {
1549 DEBUG(3,("ads_get_kvno: Error Determining KVNO!\n"));
1550 DEBUG(3,("ads_get_kvno: Windows 2000 does not support KVNO's, so this may be normal.\n"));
1551 ads_msgfree(ads, res);
1556 DEBUG(5,("ads_get_kvno: Looked Up KVNO of: %d\n", kvno));
1557 ads_msgfree(ads, res);
1562 * This clears out all registered spn's for a given hostname
1563 * @param ads An initilaized ADS_STRUCT
1564 * @param machine_name the NetBIOS name of the computer.
1565 * @return 0 upon success, non-zero otherwise.
1568 ADS_STATUS ads_clear_service_principal_names(ADS_STRUCT *ads, const char *machine_name)
1571 LDAPMessage *res = NULL;
1573 const char *servicePrincipalName[1] = {NULL};
1574 ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS);
1575 char *dn_string = NULL;
1577 ret = ads_find_machine_acct(ads, &res, machine_name);
1578 if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
1579 DEBUG(5,("ads_clear_service_principal_names: WARNING: Host Account for %s not found... skipping operation.\n", machine_name));
1580 DEBUG(5,("ads_clear_service_principal_names: WARNING: Service Principals for %s have NOT been cleared.\n", machine_name));
1581 ads_msgfree(ads, res);
1582 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1585 DEBUG(5,("ads_clear_service_principal_names: Host account for %s found\n", machine_name));
1586 ctx = talloc_init("ads_clear_service_principal_names");
1588 ads_msgfree(ads, res);
1589 return ADS_ERROR(LDAP_NO_MEMORY);
1592 if (!(mods = ads_init_mods(ctx))) {
1593 talloc_destroy(ctx);
1594 ads_msgfree(ads, res);
1595 return ADS_ERROR(LDAP_NO_MEMORY);
1597 ret = ads_mod_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1598 if (!ADS_ERR_OK(ret)) {
1599 DEBUG(1,("ads_clear_service_principal_names: Error creating strlist.\n"));
1600 ads_msgfree(ads, res);
1601 talloc_destroy(ctx);
1604 dn_string = ads_get_dn(ads, res);
1606 talloc_destroy(ctx);
1607 ads_msgfree(ads, res);
1608 return ADS_ERROR(LDAP_NO_MEMORY);
1610 ret = ads_gen_mod(ads, dn_string, mods);
1611 ads_memfree(ads,dn_string);
1612 if (!ADS_ERR_OK(ret)) {
1613 DEBUG(1,("ads_clear_service_principal_names: Error: Updating Service Principals for machine %s in LDAP\n",
1615 ads_msgfree(ads, res);
1616 talloc_destroy(ctx);
1620 ads_msgfree(ads, res);
1621 talloc_destroy(ctx);
1626 * This adds a service principal name to an existing computer account
1627 * (found by hostname) in AD.
1628 * @param ads An initialized ADS_STRUCT
1629 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1630 * @param my_fqdn The fully qualified DNS name of the machine
1631 * @param spn A string of the service principal to add, i.e. 'host'
1632 * @return 0 upon sucess, or non-zero if a failure occurs
1635 ADS_STATUS ads_add_service_principal_name(ADS_STRUCT *ads, const char *machine_name,
1636 const char *my_fqdn, const char *spn)
1640 LDAPMessage *res = NULL;
1643 char *dn_string = NULL;
1644 const char *servicePrincipalName[3] = {NULL, NULL, NULL};
1646 ret = ads_find_machine_acct(ads, &res, machine_name);
1647 if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
1648 DEBUG(1,("ads_add_service_principal_name: WARNING: Host Account for %s not found... skipping operation.\n",
1650 DEBUG(1,("ads_add_service_principal_name: WARNING: Service Principal '%s/%s@%s' has NOT been added.\n",
1651 spn, machine_name, ads->config.realm));
1652 ads_msgfree(ads, res);
1653 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1656 DEBUG(1,("ads_add_service_principal_name: Host account for %s found\n", machine_name));
1657 if (!(ctx = talloc_init("ads_add_service_principal_name"))) {
1658 ads_msgfree(ads, res);
1659 return ADS_ERROR(LDAP_NO_MEMORY);
1662 /* add short name spn */
1664 if ( (psp1 = talloc_asprintf(ctx, "%s/%s", spn, machine_name)) == NULL ) {
1665 talloc_destroy(ctx);
1666 ads_msgfree(ads, res);
1667 return ADS_ERROR(LDAP_NO_MEMORY);
1670 strlower_m(&psp1[strlen(spn)]);
1671 servicePrincipalName[0] = psp1;
1673 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n",
1674 psp1, machine_name));
1677 /* add fully qualified spn */
1679 if ( (psp2 = talloc_asprintf(ctx, "%s/%s", spn, my_fqdn)) == NULL ) {
1680 ret = ADS_ERROR(LDAP_NO_MEMORY);
1684 strlower_m(&psp2[strlen(spn)]);
1685 servicePrincipalName[1] = psp2;
1687 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n",
1688 psp2, machine_name));
1690 if ( (mods = ads_init_mods(ctx)) == NULL ) {
1691 ret = ADS_ERROR(LDAP_NO_MEMORY);
1695 ret = ads_add_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1696 if (!ADS_ERR_OK(ret)) {
1697 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
1701 if ( (dn_string = ads_get_dn(ads, res)) == NULL ) {
1702 ret = ADS_ERROR(LDAP_NO_MEMORY);
1706 ret = ads_gen_mod(ads, dn_string, mods);
1707 ads_memfree(ads,dn_string);
1708 if (!ADS_ERR_OK(ret)) {
1709 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
1715 ads_msgfree(ads, res);
1720 * adds a machine account to the ADS server
1721 * @param ads An intialized ADS_STRUCT
1722 * @param machine_name - the NetBIOS machine name of this account.
1723 * @param account_type A number indicating the type of account to create
1724 * @param org_unit The LDAP path in which to place this account
1725 * @return 0 upon success, or non-zero otherwise
1728 ADS_STATUS ads_create_machine_acct(ADS_STRUCT *ads, const char *machine_name,
1729 const char *org_unit)
1732 char *samAccountName, *controlstr;
1735 char *machine_escaped = NULL;
1737 const char *objectClass[] = {"top", "person", "organizationalPerson",
1738 "user", "computer", NULL};
1739 LDAPMessage *res = NULL;
1740 uint32 acct_control = ( UF_WORKSTATION_TRUST_ACCOUNT |\
1741 UF_DONT_EXPIRE_PASSWD |\
1742 UF_ACCOUNTDISABLE );
1744 if (!(ctx = talloc_init("ads_add_machine_acct")))
1745 return ADS_ERROR(LDAP_NO_MEMORY);
1747 ret = ADS_ERROR(LDAP_NO_MEMORY);
1749 machine_escaped = escape_rdn_val_string_alloc(machine_name);
1750 if (!machine_escaped) {
1754 new_dn = talloc_asprintf(ctx, "cn=%s,%s", machine_escaped, org_unit);
1755 samAccountName = talloc_asprintf(ctx, "%s$", machine_name);
1757 if ( !new_dn || !samAccountName ) {
1761 #ifndef ENCTYPE_ARCFOUR_HMAC
1762 acct_control |= UF_USE_DES_KEY_ONLY;
1765 if (!(controlstr = talloc_asprintf(ctx, "%u", acct_control))) {
1769 if (!(mods = ads_init_mods(ctx))) {
1773 ads_mod_str(ctx, &mods, "cn", machine_name);
1774 ads_mod_str(ctx, &mods, "sAMAccountName", samAccountName);
1775 ads_mod_strlist(ctx, &mods, "objectClass", objectClass);
1776 ads_mod_str(ctx, &mods, "userAccountControl", controlstr);
1778 ret = ads_gen_add(ads, new_dn, mods);
1781 SAFE_FREE(machine_escaped);
1782 ads_msgfree(ads, res);
1783 talloc_destroy(ctx);
1789 * move a machine account to another OU on the ADS server
1790 * @param ads - An intialized ADS_STRUCT
1791 * @param machine_name - the NetBIOS machine name of this account.
1792 * @param org_unit - The LDAP path in which to place this account
1793 * @param moved - whether we moved the machine account (optional)
1794 * @return 0 upon success, or non-zero otherwise
1797 ADS_STATUS ads_move_machine_acct(ADS_STRUCT *ads, const char *machine_name,
1798 const char *org_unit, bool *moved)
1802 LDAPMessage *res = NULL;
1803 char *filter = NULL;
1804 char *computer_dn = NULL;
1806 char *computer_rdn = NULL;
1807 bool need_move = False;
1809 if (asprintf(&filter, "(samAccountName=%s$)", machine_name) == -1) {
1810 rc = ADS_ERROR(LDAP_NO_MEMORY);
1814 /* Find pre-existing machine */
1815 rc = ads_search(ads, &res, filter, NULL);
1816 if (!ADS_ERR_OK(rc)) {
1820 computer_dn = ads_get_dn(ads, res);
1822 rc = ADS_ERROR(LDAP_NO_MEMORY);
1826 parent_dn = ads_parent_dn(computer_dn);
1827 if (strequal(parent_dn, org_unit)) {
1833 if (asprintf(&computer_rdn, "CN=%s", machine_name) == -1) {
1834 rc = ADS_ERROR(LDAP_NO_MEMORY);
1838 ldap_status = ldap_rename_s(ads->ldap.ld, computer_dn, computer_rdn,
1839 org_unit, 1, NULL, NULL);
1840 rc = ADS_ERROR(ldap_status);
1843 ads_msgfree(ads, res);
1845 SAFE_FREE(computer_dn);
1846 SAFE_FREE(computer_rdn);
1848 if (!ADS_ERR_OK(rc)) {
1860 dump a binary result from ldap
1862 static void dump_binary(ADS_STRUCT *ads, const char *field, struct berval **values)
1865 for (i=0; values[i]; i++) {
1866 printf("%s: ", field);
1867 for (j=0; j<values[i]->bv_len; j++) {
1868 printf("%02X", (unsigned char)values[i]->bv_val[j]);
1874 static void dump_guid(ADS_STRUCT *ads, const char *field, struct berval **values)
1877 for (i=0; values[i]; i++) {
1882 memcpy(guid.info, values[i]->bv_val, sizeof(guid.info));
1883 smb_uuid_unpack(guid, &tmp);
1884 printf("%s: %s\n", field, smb_uuid_string(talloc_tos(), tmp));
1889 dump a sid result from ldap
1891 static void dump_sid(ADS_STRUCT *ads, const char *field, struct berval **values)
1894 for (i=0; values[i]; i++) {
1897 sid_parse(values[i]->bv_val, values[i]->bv_len, &sid);
1898 printf("%s: %s\n", field, sid_to_fstring(tmp, &sid));
1903 dump ntSecurityDescriptor
1905 static void dump_sd(ADS_STRUCT *ads, const char *filed, struct berval **values)
1907 TALLOC_CTX *frame = talloc_stackframe();
1908 struct security_descriptor *psd;
1911 status = unmarshall_sec_desc(talloc_tos(), (uint8 *)values[0]->bv_val,
1912 values[0]->bv_len, &psd);
1913 if (!NT_STATUS_IS_OK(status)) {
1914 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
1915 nt_errstr(status)));
1921 ads_disp_sd(ads, talloc_tos(), psd);
1928 dump a string result from ldap
1930 static void dump_string(const char *field, char **values)
1933 for (i=0; values[i]; i++) {
1934 printf("%s: %s\n", field, values[i]);
1939 dump a field from LDAP on stdout
1943 static bool ads_dump_field(ADS_STRUCT *ads, char *field, void **values, void *data_area)
1948 void (*handler)(ADS_STRUCT *, const char *, struct berval **);
1950 {"objectGUID", False, dump_guid},
1951 {"netbootGUID", False, dump_guid},
1952 {"nTSecurityDescriptor", False, dump_sd},
1953 {"dnsRecord", False, dump_binary},
1954 {"objectSid", False, dump_sid},
1955 {"tokenGroups", False, dump_sid},
1956 {"tokenGroupsNoGCAcceptable", False, dump_sid},
1957 {"tokengroupsGlobalandUniversal", False, dump_sid},
1958 {"mS-DS-CreatorSID", False, dump_sid},
1959 {"msExchMailboxGuid", False, dump_guid},
1964 if (!field) { /* must be end of an entry */
1969 for (i=0; handlers[i].name; i++) {
1970 if (StrCaseCmp(handlers[i].name, field) == 0) {
1971 if (!values) /* first time, indicate string or not */
1972 return handlers[i].string;
1973 handlers[i].handler(ads, field, (struct berval **) values);
1977 if (!handlers[i].name) {
1978 if (!values) /* first time, indicate string conversion */
1980 dump_string(field, (char **)values);
1986 * Dump a result from LDAP on stdout
1987 * used for debugging
1988 * @param ads connection to ads server
1989 * @param res Results to dump
1992 void ads_dump(ADS_STRUCT *ads, LDAPMessage *res)
1994 ads_process_results(ads, res, ads_dump_field, NULL);
1998 * Walk through results, calling a function for each entry found.
1999 * The function receives a field name, a berval * array of values,
2000 * and a data area passed through from the start. The function is
2001 * called once with null for field and values at the end of each
2003 * @param ads connection to ads server
2004 * @param res Results to process
2005 * @param fn Function for processing each result
2006 * @param data_area user-defined area to pass to function
2008 void ads_process_results(ADS_STRUCT *ads, LDAPMessage *res,
2009 bool (*fn)(ADS_STRUCT *, char *, void **, void *),
2015 if (!(ctx = talloc_init("ads_process_results")))
2018 for (msg = ads_first_entry(ads, res); msg;
2019 msg = ads_next_entry(ads, msg)) {
2023 for (utf8_field=ldap_first_attribute(ads->ldap.ld,
2024 (LDAPMessage *)msg,&b);
2026 utf8_field=ldap_next_attribute(ads->ldap.ld,
2027 (LDAPMessage *)msg,b)) {
2028 struct berval **ber_vals;
2029 char **str_vals, **utf8_vals;
2033 pull_utf8_talloc(ctx, &field, utf8_field);
2034 string = fn(ads, field, NULL, data_area);
2037 utf8_vals = ldap_get_values(ads->ldap.ld,
2038 (LDAPMessage *)msg, field);
2039 str_vals = ads_pull_strvals(ctx,
2040 (const char **) utf8_vals);
2041 fn(ads, field, (void **) str_vals, data_area);
2042 ldap_value_free(utf8_vals);
2044 ber_vals = ldap_get_values_len(ads->ldap.ld,
2045 (LDAPMessage *)msg, field);
2046 fn(ads, field, (void **) ber_vals, data_area);
2048 ldap_value_free_len(ber_vals);
2050 ldap_memfree(utf8_field);
2053 talloc_free_children(ctx);
2054 fn(ads, NULL, NULL, data_area); /* completed an entry */
2057 talloc_destroy(ctx);
2061 * count how many replies are in a LDAPMessage
2062 * @param ads connection to ads server
2063 * @param res Results to count
2064 * @return number of replies
2066 int ads_count_replies(ADS_STRUCT *ads, void *res)
2068 return ldap_count_entries(ads->ldap.ld, (LDAPMessage *)res);
2072 * pull the first entry from a ADS result
2073 * @param ads connection to ads server
2074 * @param res Results of search
2075 * @return first entry from result
2077 LDAPMessage *ads_first_entry(ADS_STRUCT *ads, LDAPMessage *res)
2079 return ldap_first_entry(ads->ldap.ld, res);
2083 * pull the next entry from a ADS result
2084 * @param ads connection to ads server
2085 * @param res Results of search
2086 * @return next entry from result
2088 LDAPMessage *ads_next_entry(ADS_STRUCT *ads, LDAPMessage *res)
2090 return ldap_next_entry(ads->ldap.ld, res);
2094 * pull the first message from a ADS result
2095 * @param ads connection to ads server
2096 * @param res Results of search
2097 * @return first message from result
2099 LDAPMessage *ads_first_message(ADS_STRUCT *ads, LDAPMessage *res)
2101 return ldap_first_message(ads->ldap.ld, res);
2105 * pull the next message from a ADS result
2106 * @param ads connection to ads server
2107 * @param res Results of search
2108 * @return next message from result
2110 LDAPMessage *ads_next_message(ADS_STRUCT *ads, LDAPMessage *res)
2112 return ldap_next_message(ads->ldap.ld, res);
2116 * pull a single string from a ADS result
2117 * @param ads connection to ads server
2118 * @param mem_ctx TALLOC_CTX to use for allocating result string
2119 * @param msg Results of search
2120 * @param field Attribute to retrieve
2121 * @return Result string in talloc context
2123 char *ads_pull_string(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, LDAPMessage *msg,
2131 values = ldap_get_values(ads->ldap.ld, msg, field);
2136 rc = pull_utf8_talloc(mem_ctx, &ux_string,
2138 if (rc != (size_t)-1)
2142 ldap_value_free(values);
2147 * pull an array of strings from a ADS result
2148 * @param ads connection to ads server
2149 * @param mem_ctx TALLOC_CTX to use for allocating result string
2150 * @param msg Results of search
2151 * @param field Attribute to retrieve
2152 * @return Result strings in talloc context
2154 char **ads_pull_strings(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2155 LDAPMessage *msg, const char *field,
2162 values = ldap_get_values(ads->ldap.ld, msg, field);
2166 *num_values = ldap_count_values(values);
2168 ret = TALLOC_ARRAY(mem_ctx, char *, *num_values + 1);
2170 ldap_value_free(values);
2174 for (i=0;i<*num_values;i++) {
2175 if (pull_utf8_talloc(mem_ctx, &ret[i], values[i]) == -1) {
2176 ldap_value_free(values);
2182 ldap_value_free(values);
2187 * pull an array of strings from a ADS result
2188 * (handle large multivalue attributes with range retrieval)
2189 * @param ads connection to ads server
2190 * @param mem_ctx TALLOC_CTX to use for allocating result string
2191 * @param msg Results of search
2192 * @param field Attribute to retrieve
2193 * @param current_strings strings returned by a previous call to this function
2194 * @param next_attribute The next query should ask for this attribute
2195 * @param num_values How many values did we get this time?
2196 * @param more_values Are there more values to get?
2197 * @return Result strings in talloc context
2199 char **ads_pull_strings_range(ADS_STRUCT *ads,
2200 TALLOC_CTX *mem_ctx,
2201 LDAPMessage *msg, const char *field,
2202 char **current_strings,
2203 const char **next_attribute,
2204 size_t *num_strings,
2208 char *expected_range_attrib, *range_attr;
2209 BerElement *ptr = NULL;
2212 size_t num_new_strings;
2213 unsigned long int range_start;
2214 unsigned long int range_end;
2216 /* we might have been given the whole lot anyway */
2217 if ((strings = ads_pull_strings(ads, mem_ctx, msg, field, num_strings))) {
2218 *more_strings = False;
2222 expected_range_attrib = talloc_asprintf(mem_ctx, "%s;Range=", field);
2224 /* look for Range result */
2225 for (attr = ldap_first_attribute(ads->ldap.ld, (LDAPMessage *)msg, &ptr);
2227 attr = ldap_next_attribute(ads->ldap.ld, (LDAPMessage *)msg, ptr)) {
2228 /* we ignore the fact that this is utf8, as all attributes are ascii... */
2229 if (strnequal(attr, expected_range_attrib, strlen(expected_range_attrib))) {
2237 /* nothing here - this field is just empty */
2238 *more_strings = False;
2242 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-%lu",
2243 &range_start, &range_end) == 2) {
2244 *more_strings = True;
2246 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-*",
2247 &range_start) == 1) {
2248 *more_strings = False;
2250 DEBUG(1, ("ads_pull_strings_range: Cannot parse Range attriubte (%s)\n",
2252 ldap_memfree(range_attr);
2253 *more_strings = False;
2258 if ((*num_strings) != range_start) {
2259 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) doesn't start at %u, but at %lu"
2260 " - aborting range retreival\n",
2261 range_attr, (unsigned int)(*num_strings) + 1, range_start));
2262 ldap_memfree(range_attr);
2263 *more_strings = False;
2267 new_strings = ads_pull_strings(ads, mem_ctx, msg, range_attr, &num_new_strings);
2269 if (*more_strings && ((*num_strings + num_new_strings) != (range_end + 1))) {
2270 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) tells us we have %lu "
2271 "strings in this bunch, but we only got %lu - aborting range retreival\n",
2272 range_attr, (unsigned long int)range_end - range_start + 1,
2273 (unsigned long int)num_new_strings));
2274 ldap_memfree(range_attr);
2275 *more_strings = False;
2279 strings = TALLOC_REALLOC_ARRAY(mem_ctx, current_strings, char *,
2280 *num_strings + num_new_strings);
2282 if (strings == NULL) {
2283 ldap_memfree(range_attr);
2284 *more_strings = False;
2288 if (new_strings && num_new_strings) {
2289 memcpy(&strings[*num_strings], new_strings,
2290 sizeof(*new_strings) * num_new_strings);
2293 (*num_strings) += num_new_strings;
2295 if (*more_strings) {
2296 *next_attribute = talloc_asprintf(mem_ctx,
2301 if (!*next_attribute) {
2302 DEBUG(1, ("talloc_asprintf for next attribute failed!\n"));
2303 ldap_memfree(range_attr);
2304 *more_strings = False;
2309 ldap_memfree(range_attr);
2315 * pull a single uint32 from a ADS result
2316 * @param ads connection to ads server
2317 * @param msg Results of search
2318 * @param field Attribute to retrieve
2319 * @param v Pointer to int to store result
2320 * @return boolean inidicating success
2322 bool ads_pull_uint32(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
2327 values = ldap_get_values(ads->ldap.ld, msg, field);
2331 ldap_value_free(values);
2335 *v = atoi(values[0]);
2336 ldap_value_free(values);
2341 * pull a single objectGUID from an ADS result
2342 * @param ads connection to ADS server
2343 * @param msg results of search
2344 * @param guid 37-byte area to receive text guid
2345 * @return boolean indicating success
2347 bool ads_pull_guid(ADS_STRUCT *ads, LDAPMessage *msg, struct GUID *guid)
2350 UUID_FLAT flat_guid;
2352 values = ldap_get_values(ads->ldap.ld, msg, "objectGUID");
2357 memcpy(&flat_guid.info, values[0], sizeof(UUID_FLAT));
2358 smb_uuid_unpack(flat_guid, guid);
2359 ldap_value_free(values);
2362 ldap_value_free(values);
2369 * pull a single DOM_SID from a ADS result
2370 * @param ads connection to ads server
2371 * @param msg Results of search
2372 * @param field Attribute to retrieve
2373 * @param sid Pointer to sid to store result
2374 * @return boolean inidicating success
2376 bool ads_pull_sid(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
2379 struct berval **values;
2382 values = ldap_get_values_len(ads->ldap.ld, msg, field);
2388 ret = sid_parse(values[0]->bv_val, values[0]->bv_len, sid);
2390 ldap_value_free_len(values);
2395 * pull an array of DOM_SIDs from a ADS result
2396 * @param ads connection to ads server
2397 * @param mem_ctx TALLOC_CTX for allocating sid array
2398 * @param msg Results of search
2399 * @param field Attribute to retrieve
2400 * @param sids pointer to sid array to allocate
2401 * @return the count of SIDs pulled
2403 int ads_pull_sids(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2404 LDAPMessage *msg, const char *field, DOM_SID **sids)
2406 struct berval **values;
2410 values = ldap_get_values_len(ads->ldap.ld, msg, field);
2415 for (i=0; values[i]; i++)
2419 (*sids) = TALLOC_ARRAY(mem_ctx, DOM_SID, i);
2421 ldap_value_free_len(values);
2429 for (i=0; values[i]; i++) {
2430 ret = sid_parse(values[i]->bv_val, values[i]->bv_len, &(*sids)[count]);
2432 DEBUG(10, ("pulling SID: %s\n",
2433 sid_string_dbg(&(*sids)[count])));
2438 ldap_value_free_len(values);
2443 * pull a SEC_DESC from a ADS result
2444 * @param ads connection to ads server
2445 * @param mem_ctx TALLOC_CTX for allocating sid array
2446 * @param msg Results of search
2447 * @param field Attribute to retrieve
2448 * @param sd Pointer to *SEC_DESC to store result (talloc()ed)
2449 * @return boolean inidicating success
2451 bool ads_pull_sd(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2452 LDAPMessage *msg, const char *field, SEC_DESC **sd)
2454 struct berval **values;
2457 values = ldap_get_values_len(ads->ldap.ld, msg, field);
2459 if (!values) return false;
2463 status = unmarshall_sec_desc(mem_ctx,
2464 (uint8 *)values[0]->bv_val,
2465 values[0]->bv_len, sd);
2466 if (!NT_STATUS_IS_OK(status)) {
2467 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
2468 nt_errstr(status)));
2473 ldap_value_free_len(values);
2478 * in order to support usernames longer than 21 characters we need to
2479 * use both the sAMAccountName and the userPrincipalName attributes
2480 * It seems that not all users have the userPrincipalName attribute set
2482 * @param ads connection to ads server
2483 * @param mem_ctx TALLOC_CTX for allocating sid array
2484 * @param msg Results of search
2485 * @return the username
2487 char *ads_pull_username(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2493 /* lookup_name() only works on the sAMAccountName to
2494 returning the username portion of userPrincipalName
2495 breaks winbindd_getpwnam() */
2497 ret = ads_pull_string(ads, mem_ctx, msg, "userPrincipalName");
2498 if (ret && (p = strchr_m(ret, '@'))) {
2503 return ads_pull_string(ads, mem_ctx, msg, "sAMAccountName");
2508 * find the update serial number - this is the core of the ldap cache
2509 * @param ads connection to ads server
2510 * @param ads connection to ADS server
2511 * @param usn Pointer to retrieved update serial number
2512 * @return status of search
2514 ADS_STATUS ads_USN(ADS_STRUCT *ads, uint32 *usn)
2516 const char *attrs[] = {"highestCommittedUSN", NULL};
2520 status = ads_do_search_retry(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2521 if (!ADS_ERR_OK(status))
2524 if (ads_count_replies(ads, res) != 1) {
2525 ads_msgfree(ads, res);
2526 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2529 if (!ads_pull_uint32(ads, res, "highestCommittedUSN", usn)) {
2530 ads_msgfree(ads, res);
2531 return ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
2534 ads_msgfree(ads, res);
2538 /* parse a ADS timestring - typical string is
2539 '20020917091222.0Z0' which means 09:12.22 17th September
2541 static time_t ads_parse_time(const char *str)
2547 if (sscanf(str, "%4d%2d%2d%2d%2d%2d",
2548 &tm.tm_year, &tm.tm_mon, &tm.tm_mday,
2549 &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) {
2558 /********************************************************************
2559 ********************************************************************/
2561 ADS_STATUS ads_current_time(ADS_STRUCT *ads)
2563 const char *attrs[] = {"currentTime", NULL};
2568 ADS_STRUCT *ads_s = ads;
2570 if (!(ctx = talloc_init("ads_current_time"))) {
2571 return ADS_ERROR(LDAP_NO_MEMORY);
2574 /* establish a new ldap tcp session if necessary */
2576 if ( !ads->ldap.ld ) {
2577 if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup,
2578 ads->server.ldap_server )) == NULL )
2582 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
2583 status = ads_connect( ads_s );
2584 if ( !ADS_ERR_OK(status))
2588 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2589 if (!ADS_ERR_OK(status)) {
2593 timestr = ads_pull_string(ads_s, ctx, res, "currentTime");
2595 ads_msgfree(ads_s, res);
2596 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2600 /* but save the time and offset in the original ADS_STRUCT */
2602 ads->config.current_time = ads_parse_time(timestr);
2604 if (ads->config.current_time != 0) {
2605 ads->auth.time_offset = ads->config.current_time - time(NULL);
2606 DEBUG(4,("time offset is %d seconds\n", ads->auth.time_offset));
2609 ads_msgfree(ads, res);
2611 status = ADS_SUCCESS;
2614 /* free any temporary ads connections */
2615 if ( ads_s != ads ) {
2616 ads_destroy( &ads_s );
2618 talloc_destroy(ctx);
2623 /********************************************************************
2624 ********************************************************************/
2626 ADS_STATUS ads_domain_func_level(ADS_STRUCT *ads, uint32 *val)
2628 const char *attrs[] = {"domainFunctionality", NULL};
2631 ADS_STRUCT *ads_s = ads;
2633 *val = DS_DOMAIN_FUNCTION_2000;
2635 /* establish a new ldap tcp session if necessary */
2637 if ( !ads->ldap.ld ) {
2638 if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup,
2639 ads->server.ldap_server )) == NULL )
2643 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
2644 status = ads_connect( ads_s );
2645 if ( !ADS_ERR_OK(status))
2649 /* If the attribute does not exist assume it is a Windows 2000
2650 functional domain */
2652 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2653 if (!ADS_ERR_OK(status)) {
2654 if ( status.err.rc == LDAP_NO_SUCH_ATTRIBUTE ) {
2655 status = ADS_SUCCESS;
2660 if ( !ads_pull_uint32(ads_s, res, "domainFunctionality", val) ) {
2661 DEBUG(5,("ads_domain_func_level: Failed to pull the domainFunctionality attribute.\n"));
2663 DEBUG(3,("ads_domain_func_level: %d\n", *val));
2666 ads_msgfree(ads, res);
2669 /* free any temporary ads connections */
2670 if ( ads_s != ads ) {
2671 ads_destroy( &ads_s );
2678 * find the domain sid for our domain
2679 * @param ads connection to ads server
2680 * @param sid Pointer to domain sid
2681 * @return status of search
2683 ADS_STATUS ads_domain_sid(ADS_STRUCT *ads, DOM_SID *sid)
2685 const char *attrs[] = {"objectSid", NULL};
2689 rc = ads_do_search_retry(ads, ads->config.bind_path, LDAP_SCOPE_BASE, "(objectclass=*)",
2691 if (!ADS_ERR_OK(rc)) return rc;
2692 if (!ads_pull_sid(ads, res, "objectSid", sid)) {
2693 ads_msgfree(ads, res);
2694 return ADS_ERROR_SYSTEM(ENOENT);
2696 ads_msgfree(ads, res);
2702 * find our site name
2703 * @param ads connection to ads server
2704 * @param mem_ctx Pointer to talloc context
2705 * @param site_name Pointer to the sitename
2706 * @return status of search
2708 ADS_STATUS ads_site_dn(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char **site_name)
2712 const char *dn, *service_name;
2713 const char *attrs[] = { "dsServiceName", NULL };
2715 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2716 if (!ADS_ERR_OK(status)) {
2720 service_name = ads_pull_string(ads, mem_ctx, res, "dsServiceName");
2721 if (service_name == NULL) {
2722 ads_msgfree(ads, res);
2723 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2726 ads_msgfree(ads, res);
2728 /* go up three levels */
2729 dn = ads_parent_dn(ads_parent_dn(ads_parent_dn(service_name)));
2731 return ADS_ERROR(LDAP_NO_MEMORY);
2734 *site_name = talloc_strdup(mem_ctx, dn);
2735 if (*site_name == NULL) {
2736 return ADS_ERROR(LDAP_NO_MEMORY);
2741 dsServiceName: CN=NTDS Settings,CN=W2K3DC,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,DC=ber,DC=suse,DC=de
2746 * find the site dn where a machine resides
2747 * @param ads connection to ads server
2748 * @param mem_ctx Pointer to talloc context
2749 * @param computer_name name of the machine
2750 * @param site_name Pointer to the sitename
2751 * @return status of search
2753 ADS_STATUS ads_site_dn_for_machine(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char *computer_name, const char **site_dn)
2757 const char *parent, *filter;
2758 char *config_context = NULL;
2761 /* shortcut a query */
2762 if (strequal(computer_name, ads->config.ldap_server_name)) {
2763 return ads_site_dn(ads, mem_ctx, site_dn);
2766 status = ads_config_path(ads, mem_ctx, &config_context);
2767 if (!ADS_ERR_OK(status)) {
2771 filter = talloc_asprintf(mem_ctx, "(cn=%s)", computer_name);
2772 if (filter == NULL) {
2773 return ADS_ERROR(LDAP_NO_MEMORY);
2776 status = ads_do_search(ads, config_context, LDAP_SCOPE_SUBTREE,
2777 filter, NULL, &res);
2778 if (!ADS_ERR_OK(status)) {
2782 if (ads_count_replies(ads, res) != 1) {
2783 ads_msgfree(ads, res);
2784 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
2787 dn = ads_get_dn(ads, res);
2789 ads_msgfree(ads, res);
2790 return ADS_ERROR(LDAP_NO_MEMORY);
2793 /* go up three levels */
2794 parent = ads_parent_dn(ads_parent_dn(ads_parent_dn(dn)));
2795 if (parent == NULL) {
2796 ads_msgfree(ads, res);
2797 ads_memfree(ads, dn);
2798 return ADS_ERROR(LDAP_NO_MEMORY);
2801 *site_dn = talloc_strdup(mem_ctx, parent);
2802 if (*site_dn == NULL) {
2803 ads_msgfree(ads, res);
2804 ads_memfree(ads, dn);
2805 return ADS_ERROR(LDAP_NO_MEMORY);
2808 ads_memfree(ads, dn);
2809 ads_msgfree(ads, res);
2815 * get the upn suffixes for a domain
2816 * @param ads connection to ads server
2817 * @param mem_ctx Pointer to talloc context
2818 * @param suffixes Pointer to an array of suffixes
2819 * @param num_suffixes Pointer to the number of suffixes
2820 * @return status of search
2822 ADS_STATUS ads_upn_suffixes(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, char ***suffixes, size_t *num_suffixes)
2827 char *config_context = NULL;
2828 const char *attrs[] = { "uPNSuffixes", NULL };
2830 status = ads_config_path(ads, mem_ctx, &config_context);
2831 if (!ADS_ERR_OK(status)) {
2835 base = talloc_asprintf(mem_ctx, "cn=Partitions,%s", config_context);
2837 return ADS_ERROR(LDAP_NO_MEMORY);
2840 status = ads_search_dn(ads, &res, base, attrs);
2841 if (!ADS_ERR_OK(status)) {
2845 if (ads_count_replies(ads, res) != 1) {
2846 ads_msgfree(ads, res);
2847 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
2850 (*suffixes) = ads_pull_strings(ads, mem_ctx, res, "uPNSuffixes", num_suffixes);
2851 if ((*suffixes) == NULL) {
2852 ads_msgfree(ads, res);
2853 return ADS_ERROR(LDAP_NO_MEMORY);
2856 ads_msgfree(ads, res);
2862 * get the joinable ous for a domain
2863 * @param ads connection to ads server
2864 * @param mem_ctx Pointer to talloc context
2865 * @param ous Pointer to an array of ous
2866 * @param num_ous Pointer to the number of ous
2867 * @return status of search
2869 ADS_STATUS ads_get_joinable_ous(ADS_STRUCT *ads,
2870 TALLOC_CTX *mem_ctx,
2875 LDAPMessage *res = NULL;
2876 LDAPMessage *msg = NULL;
2877 const char *attrs[] = { "dn", NULL };
2880 status = ads_search(ads, &res,
2881 "(|(objectClass=domain)(objectclass=organizationalUnit))",
2883 if (!ADS_ERR_OK(status)) {
2887 count = ads_count_replies(ads, res);
2889 ads_msgfree(ads, res);
2890 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2893 for (msg = ads_first_entry(ads, res); msg;
2894 msg = ads_next_entry(ads, msg)) {
2898 dn = ads_get_dn(ads, msg);
2900 ads_msgfree(ads, res);
2901 return ADS_ERROR(LDAP_NO_MEMORY);
2904 if (!add_string_to_array(mem_ctx, dn,
2905 (const char ***)ous,
2907 ads_memfree(ads, dn);
2908 ads_msgfree(ads, res);
2909 return ADS_ERROR(LDAP_NO_MEMORY);
2912 ads_memfree(ads, dn);
2915 ads_msgfree(ads, res);
2922 * pull a DOM_SID from an extended dn string
2923 * @param mem_ctx TALLOC_CTX
2924 * @param extended_dn string
2925 * @param flags string type of extended_dn
2926 * @param sid pointer to a DOM_SID
2927 * @return boolean inidicating success
2929 bool ads_get_sid_from_extended_dn(TALLOC_CTX *mem_ctx,
2930 const char *extended_dn,
2931 enum ads_extended_dn_flags flags,
2940 /* otherwise extended_dn gets stripped off */
2941 if ((dn = talloc_strdup(mem_ctx, extended_dn)) == NULL) {
2945 * ADS_EXTENDED_DN_HEX_STRING:
2946 * <GUID=238e1963cb390f4bb032ba0105525a29>;<SID=010500000000000515000000bb68c8fd6b61b427572eb04556040000>;CN=gd,OU=berlin,OU=suse,DC=ber,DC=suse,DC=de
2948 * ADS_EXTENDED_DN_STRING (only with w2k3):
2949 <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
2952 p = strchr(dn, ';');
2957 if (strncmp(p, ";<SID=", strlen(";<SID=")) != 0) {
2961 p += strlen(";<SID=");
2970 DEBUG(100,("ads_get_sid_from_extended_dn: sid string is %s\n", p));
2974 case ADS_EXTENDED_DN_STRING:
2975 if (!string_to_sid(sid, p)) {
2979 case ADS_EXTENDED_DN_HEX_STRING: {
2983 buf_len = strhex_to_str(buf, sizeof(buf), p, strlen(p));
2988 if (!sid_parse(buf, buf_len, sid)) {
2989 DEBUG(10,("failed to parse sid\n"));
2995 DEBUG(10,("unknown extended dn format\n"));
3003 * pull an array of DOM_SIDs from a ADS result
3004 * @param ads connection to ads server
3005 * @param mem_ctx TALLOC_CTX for allocating sid array
3006 * @param msg Results of search
3007 * @param field Attribute to retrieve
3008 * @param flags string type of extended_dn
3009 * @param sids pointer to sid array to allocate
3010 * @return the count of SIDs pulled
3012 int ads_pull_sids_from_extendeddn(ADS_STRUCT *ads,
3013 TALLOC_CTX *mem_ctx,
3016 enum ads_extended_dn_flags flags,
3023 if ((dn_strings = ads_pull_strings(ads, mem_ctx, msg, field,
3024 &dn_count)) == NULL) {
3028 (*sids) = TALLOC_ZERO_ARRAY(mem_ctx, DOM_SID, dn_count + 1);
3030 TALLOC_FREE(dn_strings);
3034 for (i=0; i<dn_count; i++) {
3036 if (!ads_get_sid_from_extended_dn(mem_ctx, dn_strings[i],
3037 flags, &(*sids)[i])) {
3039 TALLOC_FREE(dn_strings);
3044 TALLOC_FREE(dn_strings);
3049 /********************************************************************
3050 ********************************************************************/
3052 char* ads_get_dnshostname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3054 LDAPMessage *res = NULL;
3059 status = ads_find_machine_acct(ads, &res, global_myname());
3060 if (!ADS_ERR_OK(status)) {
3061 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
3066 if ( (count = ads_count_replies(ads, res)) != 1 ) {
3067 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
3071 if ( (name = ads_pull_string(ads, ctx, res, "dNSHostName")) == NULL ) {
3072 DEBUG(0,("ads_get_dnshostname: No dNSHostName attribute!\n"));
3076 ads_msgfree(ads, res);
3081 /********************************************************************
3082 ********************************************************************/
3084 char* ads_get_upn( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3086 LDAPMessage *res = NULL;
3091 status = ads_find_machine_acct(ads, &res, machine_name);
3092 if (!ADS_ERR_OK(status)) {
3093 DEBUG(0,("ads_get_upn: Failed to find account for %s\n",
3098 if ( (count = ads_count_replies(ads, res)) != 1 ) {
3099 DEBUG(1,("ads_get_upn: %d entries returned!\n", count));
3103 if ( (name = ads_pull_string(ads, ctx, res, "userPrincipalName")) == NULL ) {
3104 DEBUG(2,("ads_get_upn: No userPrincipalName attribute!\n"));
3108 ads_msgfree(ads, res);
3113 /********************************************************************
3114 ********************************************************************/
3116 char* ads_get_samaccountname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3118 LDAPMessage *res = NULL;
3123 status = ads_find_machine_acct(ads, &res, global_myname());
3124 if (!ADS_ERR_OK(status)) {
3125 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
3130 if ( (count = ads_count_replies(ads, res)) != 1 ) {
3131 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
3135 if ( (name = ads_pull_string(ads, ctx, res, "sAMAccountName")) == NULL ) {
3136 DEBUG(0,("ads_get_dnshostname: No sAMAccountName attribute!\n"));
3140 ads_msgfree(ads, res);
3147 SAVED CODE - we used to join via ldap - remember how we did this. JRA.
3150 * Join a machine to a realm
3151 * Creates the machine account and sets the machine password
3152 * @param ads connection to ads server
3153 * @param machine name of host to add
3154 * @param org_unit Organizational unit to place machine in
3155 * @return status of join
3157 ADS_STATUS ads_join_realm(ADS_STRUCT *ads, const char *machine_name,
3158 uint32 account_type, const char *org_unit)
3161 LDAPMessage *res = NULL;
3164 /* machine name must be lowercase */
3165 machine = SMB_STRDUP(machine_name);
3166 strlower_m(machine);
3169 status = ads_find_machine_acct(ads, (void **)&res, machine);
3170 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
3171 DEBUG(0, ("Host account for %s already exists - deleting old account\n", machine));
3172 status = ads_leave_realm(ads, machine);
3173 if (!ADS_ERR_OK(status)) {
3174 DEBUG(0, ("Failed to delete host '%s' from the '%s' realm.\n",
3175 machine, ads->config.realm));
3180 status = ads_add_machine_acct(ads, machine, account_type, org_unit);
3181 if (!ADS_ERR_OK(status)) {
3182 DEBUG(0, ("ads_join_realm: ads_add_machine_acct failed (%s): %s\n", machine, ads_errstr(status)));
3187 status = ads_find_machine_acct(ads, (void **)(void *)&res, machine);
3188 if (!ADS_ERR_OK(status)) {
3189 DEBUG(0, ("ads_join_realm: Host account test failed for machine %s\n", machine));
3195 ads_msgfree(ads, res);
3202 * Delete a machine from the realm
3203 * @param ads connection to ads server
3204 * @param hostname Machine to remove
3205 * @return status of delete
3207 ADS_STATUS ads_leave_realm(ADS_STRUCT *ads, const char *hostname)
3212 char *hostnameDN, *host;
3214 LDAPControl ldap_control;
3215 LDAPControl * pldap_control[2] = {NULL, NULL};
3217 pldap_control[0] = &ldap_control;
3218 memset(&ldap_control, 0, sizeof(LDAPControl));
3219 ldap_control.ldctl_oid = (char *)LDAP_SERVER_TREE_DELETE_OID;
3221 /* hostname must be lowercase */
3222 host = SMB_STRDUP(hostname);
3225 status = ads_find_machine_acct(ads, &res, host);
3226 if (!ADS_ERR_OK(status)) {
3227 DEBUG(0, ("Host account for %s does not exist.\n", host));
3232 msg = ads_first_entry(ads, res);
3235 return ADS_ERROR_SYSTEM(ENOENT);
3238 hostnameDN = ads_get_dn(ads, (LDAPMessage *)msg);
3240 rc = ldap_delete_ext_s(ads->ldap.ld, hostnameDN, pldap_control, NULL);
3242 DEBUG(3,("ldap_delete_ext_s failed with error code %d\n", rc));
3244 DEBUG(3,("ldap_delete_ext_s succeeded with error code %d\n", rc));
3247 if (rc != LDAP_SUCCESS) {
3248 const char *attrs[] = { "cn", NULL };
3249 LDAPMessage *msg_sub;
3251 /* we only search with scope ONE, we do not expect any further
3252 * objects to be created deeper */
3254 status = ads_do_search_retry(ads, hostnameDN,
3255 LDAP_SCOPE_ONELEVEL,
3256 "(objectclass=*)", attrs, &res);
3258 if (!ADS_ERR_OK(status)) {
3260 ads_memfree(ads, hostnameDN);
3264 for (msg_sub = ads_first_entry(ads, res); msg_sub;
3265 msg_sub = ads_next_entry(ads, msg_sub)) {
3269 if ((dn = ads_get_dn(ads, msg_sub)) == NULL) {
3271 ads_memfree(ads, hostnameDN);
3272 return ADS_ERROR(LDAP_NO_MEMORY);
3275 status = ads_del_dn(ads, dn);
3276 if (!ADS_ERR_OK(status)) {
3277 DEBUG(3,("failed to delete dn %s: %s\n", dn, ads_errstr(status)));
3279 ads_memfree(ads, dn);
3280 ads_memfree(ads, hostnameDN);
3284 ads_memfree(ads, dn);
3287 /* there should be no subordinate objects anymore */
3288 status = ads_do_search_retry(ads, hostnameDN,
3289 LDAP_SCOPE_ONELEVEL,
3290 "(objectclass=*)", attrs, &res);
3292 if (!ADS_ERR_OK(status) || ( (ads_count_replies(ads, res)) > 0 ) ) {
3294 ads_memfree(ads, hostnameDN);
3298 /* delete hostnameDN now */
3299 status = ads_del_dn(ads, hostnameDN);
3300 if (!ADS_ERR_OK(status)) {
3302 DEBUG(3,("failed to delete dn %s: %s\n", hostnameDN, ads_errstr(status)));
3303 ads_memfree(ads, hostnameDN);
3308 ads_memfree(ads, hostnameDN);
3310 status = ads_find_machine_acct(ads, &res, host);
3311 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
3312 DEBUG(3, ("Failed to remove host account.\n"));
3322 * pull all token-sids from an LDAP dn
3323 * @param ads connection to ads server
3324 * @param mem_ctx TALLOC_CTX for allocating sid array
3325 * @param dn of LDAP object
3326 * @param user_sid pointer to DOM_SID (objectSid)
3327 * @param primary_group_sid pointer to DOM_SID (self composed)
3328 * @param sids pointer to sid array to allocate
3329 * @param num_sids counter of SIDs pulled
3330 * @return status of token query
3332 ADS_STATUS ads_get_tokensids(ADS_STRUCT *ads,
3333 TALLOC_CTX *mem_ctx,
3336 DOM_SID *primary_group_sid,
3341 LDAPMessage *res = NULL;
3343 size_t tmp_num_sids;
3345 DOM_SID tmp_user_sid;
3346 DOM_SID tmp_primary_group_sid;
3348 const char *attrs[] = {
3355 status = ads_search_retry_dn(ads, &res, dn, attrs);
3356 if (!ADS_ERR_OK(status)) {
3360 count = ads_count_replies(ads, res);
3362 ads_msgfree(ads, res);
3363 return ADS_ERROR_LDAP(LDAP_NO_SUCH_OBJECT);
3366 if (!ads_pull_sid(ads, res, "objectSid", &tmp_user_sid)) {
3367 ads_msgfree(ads, res);
3368 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3371 if (!ads_pull_uint32(ads, res, "primaryGroupID", &pgid)) {
3372 ads_msgfree(ads, res);
3373 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3377 /* hack to compose the primary group sid without knowing the
3383 sid_copy(&domsid, &tmp_user_sid);
3385 if (!sid_split_rid(&domsid, &dummy_rid)) {
3386 ads_msgfree(ads, res);
3387 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3390 if (!sid_compose(&tmp_primary_group_sid, &domsid, pgid)) {
3391 ads_msgfree(ads, res);
3392 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3396 tmp_num_sids = ads_pull_sids(ads, mem_ctx, res, "tokenGroups", &tmp_sids);
3398 if (tmp_num_sids == 0 || !tmp_sids) {
3399 ads_msgfree(ads, res);
3400 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3404 *num_sids = tmp_num_sids;
3412 *user_sid = tmp_user_sid;
3415 if (primary_group_sid) {
3416 *primary_group_sid = tmp_primary_group_sid;
3419 DEBUG(10,("ads_get_tokensids: returned %d sids\n", (int)tmp_num_sids + 2));
3421 ads_msgfree(ads, res);
3422 return ADS_ERROR_LDAP(LDAP_SUCCESS);
3426 * Find a sAMAccoutName in LDAP
3427 * @param ads connection to ads server
3428 * @param mem_ctx TALLOC_CTX for allocating sid array
3429 * @param samaccountname to search
3430 * @param uac_ret uint32 pointer userAccountControl attribute value
3431 * @param dn_ret pointer to dn
3432 * @return status of token query
3434 ADS_STATUS ads_find_samaccount(ADS_STRUCT *ads,
3435 TALLOC_CTX *mem_ctx,
3436 const char *samaccountname,
3438 const char **dn_ret)
3441 const char *attrs[] = { "userAccountControl", NULL };
3443 LDAPMessage *res = NULL;
3447 filter = talloc_asprintf(mem_ctx, "(&(objectclass=user)(sAMAccountName=%s))",
3449 if (filter == NULL) {
3450 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
3454 status = ads_do_search_all(ads, ads->config.bind_path,
3456 filter, attrs, &res);
3458 if (!ADS_ERR_OK(status)) {
3462 if (ads_count_replies(ads, res) != 1) {
3463 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3467 dn = ads_get_dn(ads, res);
3469 status = ADS_ERROR(LDAP_NO_MEMORY);
3473 if (!ads_pull_uint32(ads, res, "userAccountControl", &uac)) {
3474 status = ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
3483 *dn_ret = talloc_strdup(mem_ctx, dn);
3485 status = ADS_ERROR(LDAP_NO_MEMORY);
3490 ads_memfree(ads, dn);
3491 ads_msgfree(ads, res);
3497 * find our configuration path
3498 * @param ads connection to ads server
3499 * @param mem_ctx Pointer to talloc context
3500 * @param config_path Pointer to the config path
3501 * @return status of search
3503 ADS_STATUS ads_config_path(ADS_STRUCT *ads,
3504 TALLOC_CTX *mem_ctx,
3508 LDAPMessage *res = NULL;
3509 const char *config_context = NULL;
3510 const char *attrs[] = { "configurationNamingContext", NULL };
3512 status = ads_do_search(ads, "", LDAP_SCOPE_BASE,
3513 "(objectclass=*)", attrs, &res);
3514 if (!ADS_ERR_OK(status)) {
3518 config_context = ads_pull_string(ads, mem_ctx, res,
3519 "configurationNamingContext");
3520 ads_msgfree(ads, res);
3521 if (!config_context) {
3522 return ADS_ERROR(LDAP_NO_MEMORY);
3526 *config_path = talloc_strdup(mem_ctx, config_context);
3527 if (!*config_path) {
3528 return ADS_ERROR(LDAP_NO_MEMORY);
3532 return ADS_ERROR(LDAP_SUCCESS);
3536 * find the displayName of an extended right
3537 * @param ads connection to ads server
3538 * @param config_path The config path
3539 * @param mem_ctx Pointer to talloc context
3540 * @param GUID struct of the rightsGUID
3541 * @return status of search
3543 const char *ads_get_extended_right_name_by_guid(ADS_STRUCT *ads,
3544 const char *config_path,
3545 TALLOC_CTX *mem_ctx,
3546 const struct GUID *rights_guid)
3549 LDAPMessage *res = NULL;
3551 const char *attrs[] = { "displayName", NULL };
3552 const char *result = NULL;
3555 if (!ads || !mem_ctx || !rights_guid) {
3559 expr = talloc_asprintf(mem_ctx, "(rightsGuid=%s)",
3560 smb_uuid_string(mem_ctx, *rights_guid));
3565 path = talloc_asprintf(mem_ctx, "cn=Extended-Rights,%s", config_path);
3570 rc = ads_do_search_retry(ads, path, LDAP_SCOPE_SUBTREE,
3572 if (!ADS_ERR_OK(rc)) {
3576 if (ads_count_replies(ads, res) != 1) {
3580 result = ads_pull_string(ads, mem_ctx, res, "displayName");
3583 ads_msgfree(ads, res);
3589 * verify or build and verify an account ou
3590 * @param mem_ctx Pointer to talloc context
3591 * @param ads connection to ads server
3593 * @return status of search
3596 ADS_STATUS ads_check_ou_dn(TALLOC_CTX *mem_ctx,
3598 const char **account_ou)
3600 struct ldb_dn *name_dn = NULL;
3601 const char *name = NULL;
3602 char *ou_string = NULL;
3604 name_dn = ldb_dn_explode(mem_ctx, *account_ou);
3609 ou_string = ads_ou_string(ads, *account_ou);
3611 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX);
3614 name = talloc_asprintf(mem_ctx, "%s,%s", ou_string,
3615 ads->config.bind_path);
3616 SAFE_FREE(ou_string);
3618 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3621 name_dn = ldb_dn_explode(mem_ctx, name);
3623 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX);
3626 *account_ou = talloc_strdup(mem_ctx, name);
3628 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);