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);
252 sitename_store( cldap_reply.dns_domain, cldap_reply.client_site);
257 TALLOC_FREE(mem_ctx);
262 /**********************************************************************
263 Try to find an AD dc using our internal name resolution routines
264 Try the realm first and then then workgroup name if netbios is not
266 **********************************************************************/
268 static NTSTATUS ads_find_dc(ADS_STRUCT *ads)
272 struct ip_service *ip_list;
274 bool got_realm = False;
275 bool use_own_domain = False;
277 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
279 /* if the realm and workgroup are both empty, assume they are ours */
282 c_realm = ads->server.realm;
284 if ( !c_realm || !*c_realm ) {
285 /* special case where no realm and no workgroup means our own */
286 if ( !ads->server.workgroup || !*ads->server.workgroup ) {
287 use_own_domain = True;
288 c_realm = lp_realm();
292 if (c_realm && *c_realm)
295 /* we need to try once with the realm name and fallback to the
296 netbios domain name if we fail (if netbios has not been disabled */
298 if ( !got_realm && !lp_disable_netbios() ) {
299 c_realm = ads->server.workgroup;
300 if (!c_realm || !*c_realm) {
301 if ( use_own_domain )
302 c_realm = lp_workgroup();
305 if ( !c_realm || !*c_realm ) {
306 DEBUG(0,("ads_find_dc: no realm or workgroup! Don't know what to do\n"));
307 return NT_STATUS_INVALID_PARAMETER; /* rather need MISSING_PARAMETER ... */
313 sitename = sitename_fetch(realm);
317 DEBUG(6,("ads_find_dc: looking for %s '%s'\n",
318 (got_realm ? "realm" : "domain"), realm));
320 status = get_sorted_dc_list(realm, sitename, &ip_list, &count, got_realm);
321 if (!NT_STATUS_IS_OK(status)) {
322 /* fall back to netbios if we can */
323 if ( got_realm && !lp_disable_netbios() ) {
332 /* if we fail this loop, then giveup since all the IP addresses returned were dead */
333 for ( i=0; i<count; i++ ) {
334 char server[INET6_ADDRSTRLEN];
336 print_sockaddr(server, sizeof(server), &ip_list[i].ss);
338 if ( !NT_STATUS_IS_OK(check_negative_conn_cache(realm, server)) )
342 /* realm in this case is a workgroup name. We need
343 to ignore any IP addresses in the negative connection
344 cache that match ip addresses returned in the ad realm
345 case. It sucks that I have to reproduce the logic above... */
346 c_realm = ads->server.realm;
347 if ( !c_realm || !*c_realm ) {
348 if ( !ads->server.workgroup || !*ads->server.workgroup ) {
349 c_realm = lp_realm();
352 if (c_realm && *c_realm &&
353 !NT_STATUS_IS_OK(check_negative_conn_cache(c_realm, server))) {
354 /* Ensure we add the workgroup name for this
355 IP address as negative too. */
356 add_failed_connection_entry( realm, server, NT_STATUS_UNSUCCESSFUL );
361 if ( ads_try_connect(ads, server) ) {
367 /* keep track of failures */
368 add_failed_connection_entry( realm, server, NT_STATUS_UNSUCCESSFUL );
373 /* In case we failed to contact one of our closest DC on our site we
374 * need to try to find another DC, retry with a site-less SRV DNS query
378 DEBUG(1,("ads_find_dc: failed to find a valid DC on our site (%s), "
379 "trying to find another DC\n", sitename));
381 namecache_delete(realm, 0x1C);
385 return NT_STATUS_NO_LOGON_SERVERS;
390 * Connect to the LDAP server
391 * @param ads Pointer to an existing ADS_STRUCT
392 * @return status of connection
394 ADS_STATUS ads_connect(ADS_STRUCT *ads)
396 int version = LDAP_VERSION3;
399 char addr[INET6_ADDRSTRLEN];
401 ZERO_STRUCT(ads->ldap);
402 ads->ldap.last_attempt = time(NULL);
403 ads->ldap.wrap_type = ADS_SASLWRAP_TYPE_PLAIN;
405 /* try with a user specified server */
407 if (DEBUGLEVEL >= 11) {
408 char *s = NDR_PRINT_STRUCT_STRING(talloc_tos(), ads_struct, ads);
409 DEBUG(11,("ads_connect: entering\n"));
410 DEBUGADD(11,("%s\n", s));
414 if (ads->server.ldap_server &&
415 ads_try_connect(ads, ads->server.ldap_server)) {
419 ntstatus = ads_find_dc(ads);
420 if (NT_STATUS_IS_OK(ntstatus)) {
424 status = ADS_ERROR_NT(ntstatus);
429 print_sockaddr(addr, sizeof(addr), &ads->ldap.ss);
430 DEBUG(3,("Successfully contacted LDAP server %s\n", addr));
432 if (!ads->auth.user_name) {
433 /* Must use the userPrincipalName value here or sAMAccountName
434 and not servicePrincipalName; found by Guenther Deschner */
436 asprintf(&ads->auth.user_name, "%s$", global_myname() );
439 if (!ads->auth.realm) {
440 ads->auth.realm = SMB_STRDUP(ads->config.realm);
443 if (!ads->auth.kdc_server) {
444 print_sockaddr(addr, sizeof(addr), &ads->ldap.ss);
445 ads->auth.kdc_server = SMB_STRDUP(addr);
449 /* this is a really nasty hack to avoid ADS DNS problems. It needs a patch
450 to MIT kerberos to work (tridge) */
453 asprintf(&env, "KRB5_KDC_ADDRESS_%s", ads->config.realm);
454 setenv(env, ads->auth.kdc_server, 1);
459 /* If the caller() requested no LDAP bind, then we are done */
461 if (ads->auth.flags & ADS_AUTH_NO_BIND) {
462 status = ADS_SUCCESS;
466 ads->ldap.mem_ctx = talloc_init("ads LDAP connection memory");
467 if (!ads->ldap.mem_ctx) {
468 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
472 /* Otherwise setup the TCP LDAP session */
474 ads->ldap.ld = ldap_open_with_timeout(ads->config.ldap_server_name,
475 LDAP_PORT, lp_ldap_timeout());
476 if (ads->ldap.ld == NULL) {
477 status = ADS_ERROR(LDAP_OPERATIONS_ERROR);
480 DEBUG(3,("Connected to LDAP server %s\n", ads->config.ldap_server_name));
482 /* cache the successful connection for workgroup and realm */
483 if (ads_closest_dc(ads)) {
484 print_sockaddr(addr, sizeof(addr), &ads->ldap.ss);
485 saf_store( ads->server.workgroup, addr);
486 saf_store( ads->server.realm, addr);
489 ldap_set_option(ads->ldap.ld, LDAP_OPT_PROTOCOL_VERSION, &version);
491 status = ADS_ERROR(smb_ldap_start_tls(ads->ldap.ld, version));
492 if (!ADS_ERR_OK(status)) {
496 /* fill in the current time and offsets */
498 status = ads_current_time( ads );
499 if ( !ADS_ERR_OK(status) ) {
503 /* Now do the bind */
505 if (ads->auth.flags & ADS_AUTH_ANON_BIND) {
506 status = ADS_ERROR(ldap_simple_bind_s(ads->ldap.ld, NULL, NULL));
510 if (ads->auth.flags & ADS_AUTH_SIMPLE_BIND) {
511 status = ADS_ERROR(ldap_simple_bind_s(ads->ldap.ld, ads->auth.user_name, ads->auth.password));
515 status = ads_sasl_bind(ads);
518 if (DEBUGLEVEL >= 11) {
519 char *s = NDR_PRINT_STRUCT_STRING(talloc_tos(), ads_struct, ads);
520 DEBUG(11,("ads_connect: leaving with: %s\n",
521 ads_errstr(status)));
522 DEBUGADD(11,("%s\n", s));
530 * Disconnect the LDAP server
531 * @param ads Pointer to an existing ADS_STRUCT
533 void ads_disconnect(ADS_STRUCT *ads)
536 ldap_unbind(ads->ldap.ld);
539 if (ads->ldap.wrap_ops && ads->ldap.wrap_ops->disconnect) {
540 ads->ldap.wrap_ops->disconnect(ads);
542 if (ads->ldap.mem_ctx) {
543 talloc_free(ads->ldap.mem_ctx);
545 ZERO_STRUCT(ads->ldap);
549 Duplicate a struct berval into talloc'ed memory
551 static struct berval *dup_berval(TALLOC_CTX *ctx, const struct berval *in_val)
553 struct berval *value;
555 if (!in_val) return NULL;
557 value = TALLOC_ZERO_P(ctx, struct berval);
560 if (in_val->bv_len == 0) return value;
562 value->bv_len = in_val->bv_len;
563 value->bv_val = (char *)TALLOC_MEMDUP(ctx, in_val->bv_val,
569 Make a values list out of an array of (struct berval *)
571 static struct berval **ads_dup_values(TALLOC_CTX *ctx,
572 const struct berval **in_vals)
574 struct berval **values;
577 if (!in_vals) return NULL;
578 for (i=0; in_vals[i]; i++)
580 values = TALLOC_ZERO_ARRAY(ctx, struct berval *, i+1);
581 if (!values) return NULL;
583 for (i=0; in_vals[i]; i++) {
584 values[i] = dup_berval(ctx, in_vals[i]);
590 UTF8-encode a values list out of an array of (char *)
592 static char **ads_push_strvals(TALLOC_CTX *ctx, const char **in_vals)
598 if (!in_vals) return NULL;
599 for (i=0; in_vals[i]; i++)
601 values = TALLOC_ZERO_ARRAY(ctx, char *, i+1);
602 if (!values) return NULL;
604 for (i=0; in_vals[i]; i++) {
605 if (!push_utf8_talloc(ctx, &values[i], in_vals[i], &size)) {
614 Pull a (char *) array out of a UTF8-encoded values list
616 static char **ads_pull_strvals(TALLOC_CTX *ctx, const char **in_vals)
620 size_t converted_size;
622 if (!in_vals) return NULL;
623 for (i=0; in_vals[i]; i++)
625 values = TALLOC_ZERO_ARRAY(ctx, char *, i+1);
626 if (!values) return NULL;
628 for (i=0; in_vals[i]; i++) {
629 if (!pull_utf8_talloc(ctx, &values[i], in_vals[i],
631 DEBUG(0,("ads_pull_strvals: pull_utf8_talloc failed: "
632 "%s", strerror(errno)));
639 * Do a search with paged results. cookie must be null on the first
640 * call, and then returned on each subsequent call. It will be null
641 * again when the entire search is complete
642 * @param ads connection to ads server
643 * @param bind_path Base dn for the search
644 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
645 * @param expr Search expression - specified in local charset
646 * @param attrs Attributes to retrieve - specified in utf8 or ascii
647 * @param res ** which will contain results - free res* with ads_msgfree()
648 * @param count Number of entries retrieved on this page
649 * @param cookie The paged results cookie to be returned on subsequent calls
650 * @return status of search
652 static ADS_STATUS ads_do_paged_search_args(ADS_STRUCT *ads,
653 const char *bind_path,
654 int scope, const char *expr,
655 const char **attrs, void *args,
657 int *count, struct berval **cookie)
660 char *utf8_expr, *utf8_path, **search_attrs;
661 size_t converted_size;
662 LDAPControl PagedResults, NoReferrals, ExternalCtrl, *controls[4], **rcontrols;
663 BerElement *cookie_be = NULL;
664 struct berval *cookie_bv= NULL;
665 BerElement *ext_be = NULL;
666 struct berval *ext_bv= NULL;
669 ads_control *external_control = (ads_control *) args;
673 if (!(ctx = talloc_init("ads_do_paged_search_args")))
674 return ADS_ERROR(LDAP_NO_MEMORY);
676 /* 0 means the conversion worked but the result was empty
677 so we only fail if it's -1. In any case, it always
678 at least nulls out the dest */
679 if (!push_utf8_talloc(ctx, &utf8_expr, expr, &converted_size) ||
680 !push_utf8_talloc(ctx, &utf8_path, bind_path, &converted_size))
686 if (!attrs || !(*attrs))
689 /* This would be the utf8-encoded version...*/
690 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
691 if (!(str_list_copy(talloc_tos(), &search_attrs, attrs))) {
697 /* Paged results only available on ldap v3 or later */
698 ldap_get_option(ads->ldap.ld, LDAP_OPT_PROTOCOL_VERSION, &version);
699 if (version < LDAP_VERSION3) {
700 rc = LDAP_NOT_SUPPORTED;
704 cookie_be = ber_alloc_t(LBER_USE_DER);
706 ber_printf(cookie_be, "{iO}", (ber_int_t) 1000, *cookie);
707 ber_bvfree(*cookie); /* don't need it from last time */
710 ber_printf(cookie_be, "{io}", (ber_int_t) 1000, "", 0);
712 ber_flatten(cookie_be, &cookie_bv);
713 PagedResults.ldctl_oid = CONST_DISCARD(char *, ADS_PAGE_CTL_OID);
714 PagedResults.ldctl_iscritical = (char) 1;
715 PagedResults.ldctl_value.bv_len = cookie_bv->bv_len;
716 PagedResults.ldctl_value.bv_val = cookie_bv->bv_val;
718 NoReferrals.ldctl_oid = CONST_DISCARD(char *, ADS_NO_REFERRALS_OID);
719 NoReferrals.ldctl_iscritical = (char) 0;
720 NoReferrals.ldctl_value.bv_len = 0;
721 NoReferrals.ldctl_value.bv_val = CONST_DISCARD(char *, "");
723 if (external_control &&
724 (strequal(external_control->control, ADS_EXTENDED_DN_OID) ||
725 strequal(external_control->control, ADS_SD_FLAGS_OID))) {
727 ExternalCtrl.ldctl_oid = CONST_DISCARD(char *, external_control->control);
728 ExternalCtrl.ldctl_iscritical = (char) external_control->critical;
730 /* win2k does not accept a ldctl_value beeing passed in */
732 if (external_control->val != 0) {
734 if ((ext_be = ber_alloc_t(LBER_USE_DER)) == NULL ) {
739 if ((ber_printf(ext_be, "{i}", (ber_int_t) external_control->val)) == -1) {
743 if ((ber_flatten(ext_be, &ext_bv)) == -1) {
748 ExternalCtrl.ldctl_value.bv_len = ext_bv->bv_len;
749 ExternalCtrl.ldctl_value.bv_val = ext_bv->bv_val;
752 ExternalCtrl.ldctl_value.bv_len = 0;
753 ExternalCtrl.ldctl_value.bv_val = NULL;
756 controls[0] = &NoReferrals;
757 controls[1] = &PagedResults;
758 controls[2] = &ExternalCtrl;
762 controls[0] = &NoReferrals;
763 controls[1] = &PagedResults;
767 /* we need to disable referrals as the openldap libs don't
768 handle them and paged results at the same time. Using them
769 together results in the result record containing the server
770 page control being removed from the result list (tridge/jmcd)
772 leaving this in despite the control that says don't generate
773 referrals, in case the server doesn't support it (jmcd)
775 ldap_set_option(ads->ldap.ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
777 rc = ldap_search_with_timeout(ads->ldap.ld, utf8_path, scope, utf8_expr,
778 search_attrs, 0, controls,
780 (LDAPMessage **)res);
782 ber_free(cookie_be, 1);
783 ber_bvfree(cookie_bv);
786 DEBUG(3,("ads_do_paged_search_args: ldap_search_with_timeout(%s) -> %s\n", expr,
787 ldap_err2string(rc)));
791 rc = ldap_parse_result(ads->ldap.ld, *res, NULL, NULL, NULL,
792 NULL, &rcontrols, 0);
798 for (i=0; rcontrols[i]; i++) {
799 if (strcmp(ADS_PAGE_CTL_OID, rcontrols[i]->ldctl_oid) == 0) {
800 cookie_be = ber_init(&rcontrols[i]->ldctl_value);
801 ber_scanf(cookie_be,"{iO}", (ber_int_t *) count,
803 /* the berval is the cookie, but must be freed when
805 if (cookie_bv->bv_len) /* still more to do */
806 *cookie=ber_bvdup(cookie_bv);
809 ber_bvfree(cookie_bv);
810 ber_free(cookie_be, 1);
814 ldap_controls_free(rcontrols);
827 /* if/when we decide to utf8-encode attrs, take out this next line */
828 TALLOC_FREE(search_attrs);
830 return ADS_ERROR(rc);
833 static ADS_STATUS ads_do_paged_search(ADS_STRUCT *ads, const char *bind_path,
834 int scope, const char *expr,
835 const char **attrs, LDAPMessage **res,
836 int *count, struct berval **cookie)
838 return ads_do_paged_search_args(ads, bind_path, scope, expr, attrs, NULL, res, count, cookie);
843 * Get all results for a search. This uses ads_do_paged_search() to return
844 * all entries in a large search.
845 * @param ads connection to ads server
846 * @param bind_path Base dn for the search
847 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
848 * @param expr Search expression
849 * @param attrs Attributes to retrieve
850 * @param res ** which will contain results - free res* with ads_msgfree()
851 * @return status of search
853 ADS_STATUS ads_do_search_all_args(ADS_STRUCT *ads, const char *bind_path,
854 int scope, const char *expr,
855 const char **attrs, void *args,
858 struct berval *cookie = NULL;
863 status = ads_do_paged_search_args(ads, bind_path, scope, expr, attrs, args, res,
866 if (!ADS_ERR_OK(status))
869 #ifdef HAVE_LDAP_ADD_RESULT_ENTRY
871 LDAPMessage *res2 = NULL;
873 LDAPMessage *msg, *next;
875 status2 = ads_do_paged_search_args(ads, bind_path, scope, expr,
876 attrs, args, &res2, &count, &cookie);
878 if (!ADS_ERR_OK(status2)) break;
880 /* this relies on the way that ldap_add_result_entry() works internally. I hope
881 that this works on all ldap libs, but I have only tested with openldap */
882 for (msg = ads_first_message(ads, res2); msg; msg = next) {
883 next = ads_next_message(ads, msg);
884 ldap_add_result_entry((LDAPMessage **)res, msg);
886 /* note that we do not free res2, as the memory is now
887 part of the main returned list */
890 DEBUG(0, ("no ldap_add_result_entry() support in LDAP libs!\n"));
891 status = ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
897 ADS_STATUS ads_do_search_all(ADS_STRUCT *ads, const char *bind_path,
898 int scope, const char *expr,
899 const char **attrs, LDAPMessage **res)
901 return ads_do_search_all_args(ads, bind_path, scope, expr, attrs, NULL, res);
904 ADS_STATUS ads_do_search_all_sd_flags(ADS_STRUCT *ads, const char *bind_path,
905 int scope, const char *expr,
906 const char **attrs, uint32 sd_flags,
911 args.control = ADS_SD_FLAGS_OID;
913 args.critical = True;
915 return ads_do_search_all_args(ads, bind_path, scope, expr, attrs, &args, res);
920 * Run a function on all results for a search. Uses ads_do_paged_search() and
921 * runs the function as each page is returned, using ads_process_results()
922 * @param ads connection to ads server
923 * @param bind_path Base dn for the search
924 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
925 * @param expr Search expression - specified in local charset
926 * @param attrs Attributes to retrieve - specified in UTF-8 or ascii
927 * @param fn Function which takes attr name, values list, and data_area
928 * @param data_area Pointer which is passed to function on each call
929 * @return status of search
931 ADS_STATUS ads_do_search_all_fn(ADS_STRUCT *ads, const char *bind_path,
932 int scope, const char *expr, const char **attrs,
933 bool (*fn)(ADS_STRUCT *, char *, void **, void *),
936 struct berval *cookie = NULL;
941 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs, &res,
944 if (!ADS_ERR_OK(status)) return status;
946 ads_process_results(ads, res, fn, data_area);
947 ads_msgfree(ads, res);
950 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs,
951 &res, &count, &cookie);
953 if (!ADS_ERR_OK(status)) break;
955 ads_process_results(ads, res, fn, data_area);
956 ads_msgfree(ads, res);
963 * Do a search with a timeout.
964 * @param ads connection to ads server
965 * @param bind_path Base dn for the search
966 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
967 * @param expr Search expression
968 * @param attrs Attributes to retrieve
969 * @param res ** which will contain results - free res* with ads_msgfree()
970 * @return status of search
972 ADS_STATUS ads_do_search(ADS_STRUCT *ads, const char *bind_path, int scope,
974 const char **attrs, LDAPMessage **res)
977 char *utf8_expr, *utf8_path, **search_attrs = NULL;
978 size_t converted_size;
982 if (!(ctx = talloc_init("ads_do_search"))) {
983 DEBUG(1,("ads_do_search: talloc_init() failed!"));
984 return ADS_ERROR(LDAP_NO_MEMORY);
987 /* 0 means the conversion worked but the result was empty
988 so we only fail if it's negative. In any case, it always
989 at least nulls out the dest */
990 if (!push_utf8_talloc(ctx, &utf8_expr, expr, &converted_size) ||
991 !push_utf8_talloc(ctx, &utf8_path, bind_path, &converted_size))
993 DEBUG(1,("ads_do_search: push_utf8_talloc() failed!"));
998 if (!attrs || !(*attrs))
1001 /* This would be the utf8-encoded version...*/
1002 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
1003 if (!(str_list_copy(talloc_tos(), &search_attrs, attrs)))
1005 DEBUG(1,("ads_do_search: str_list_copy() failed!"));
1006 rc = LDAP_NO_MEMORY;
1011 /* see the note in ads_do_paged_search - we *must* disable referrals */
1012 ldap_set_option(ads->ldap.ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
1014 rc = ldap_search_with_timeout(ads->ldap.ld, utf8_path, scope, utf8_expr,
1015 search_attrs, 0, NULL, NULL,
1017 (LDAPMessage **)res);
1019 if (rc == LDAP_SIZELIMIT_EXCEEDED) {
1020 DEBUG(3,("Warning! sizelimit exceeded in ldap. Truncating.\n"));
1025 talloc_destroy(ctx);
1026 /* if/when we decide to utf8-encode attrs, take out this next line */
1027 TALLOC_FREE(search_attrs);
1028 return ADS_ERROR(rc);
1031 * Do a general ADS search
1032 * @param ads connection to ads server
1033 * @param res ** which will contain results - free res* with ads_msgfree()
1034 * @param expr Search expression
1035 * @param attrs Attributes to retrieve
1036 * @return status of search
1038 ADS_STATUS ads_search(ADS_STRUCT *ads, LDAPMessage **res,
1039 const char *expr, const char **attrs)
1041 return ads_do_search(ads, ads->config.bind_path, LDAP_SCOPE_SUBTREE,
1046 * Do a search on a specific DistinguishedName
1047 * @param ads connection to ads server
1048 * @param res ** which will contain results - free res* with ads_msgfree()
1049 * @param dn DistinguishName to search
1050 * @param attrs Attributes to retrieve
1051 * @return status of search
1053 ADS_STATUS ads_search_dn(ADS_STRUCT *ads, LDAPMessage **res,
1054 const char *dn, const char **attrs)
1056 return ads_do_search(ads, dn, LDAP_SCOPE_BASE, "(objectclass=*)",
1061 * Free up memory from a ads_search
1062 * @param ads connection to ads server
1063 * @param msg Search results to free
1065 void ads_msgfree(ADS_STRUCT *ads, LDAPMessage *msg)
1072 * Free up memory from various ads requests
1073 * @param ads connection to ads server
1074 * @param mem Area to free
1076 void ads_memfree(ADS_STRUCT *ads, void *mem)
1082 * Get a dn from search results
1083 * @param ads connection to ads server
1084 * @param msg Search result
1087 char *ads_get_dn(ADS_STRUCT *ads, LDAPMessage *msg)
1089 char *utf8_dn, *unix_dn;
1090 size_t converted_size;
1092 utf8_dn = ldap_get_dn(ads->ldap.ld, msg);
1095 DEBUG (5, ("ads_get_dn: ldap_get_dn failed\n"));
1099 if (!pull_utf8_allocate(&unix_dn, utf8_dn, &converted_size)) {
1100 DEBUG(0,("ads_get_dn: string conversion failure utf8 [%s]\n",
1104 ldap_memfree(utf8_dn);
1109 * Get the parent from a dn
1110 * @param dn the dn to return the parent from
1111 * @return parent dn string
1113 char *ads_parent_dn(const char *dn)
1121 p = strchr(dn, ',');
1131 * Find a machine account given a hostname
1132 * @param ads connection to ads server
1133 * @param res ** which will contain results - free res* with ads_msgfree()
1134 * @param host Hostname to search for
1135 * @return status of search
1137 ADS_STATUS ads_find_machine_acct(ADS_STRUCT *ads, LDAPMessage **res,
1138 const char *machine)
1142 const char *attrs[] = {"*", "nTSecurityDescriptor", NULL};
1146 /* the easiest way to find a machine account anywhere in the tree
1147 is to look for hostname$ */
1148 if (asprintf(&expr, "(samAccountName=%s$)", machine) == -1) {
1149 DEBUG(1, ("asprintf failed!\n"));
1150 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1153 status = ads_search(ads, res, expr, attrs);
1159 * Initialize a list of mods to be used in a modify request
1160 * @param ctx An initialized TALLOC_CTX
1161 * @return allocated ADS_MODLIST
1163 ADS_MODLIST ads_init_mods(TALLOC_CTX *ctx)
1165 #define ADS_MODLIST_ALLOC_SIZE 10
1168 if ((mods = TALLOC_ZERO_ARRAY(ctx, LDAPMod *, ADS_MODLIST_ALLOC_SIZE + 1)))
1169 /* -1 is safety to make sure we don't go over the end.
1170 need to reset it to NULL before doing ldap modify */
1171 mods[ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1173 return (ADS_MODLIST)mods;
1178 add an attribute to the list, with values list already constructed
1180 static ADS_STATUS ads_modlist_add(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1181 int mod_op, const char *name,
1182 const void *_invals)
1184 const void **invals = (const void **)_invals;
1186 LDAPMod **modlist = (LDAPMod **) *mods;
1187 struct berval **ber_values = NULL;
1188 char **char_values = NULL;
1191 mod_op = LDAP_MOD_DELETE;
1193 if (mod_op & LDAP_MOD_BVALUES)
1194 ber_values = ads_dup_values(ctx,
1195 (const struct berval **)invals);
1197 char_values = ads_push_strvals(ctx,
1198 (const char **) invals);
1201 /* find the first empty slot */
1202 for (curmod=0; modlist[curmod] && modlist[curmod] != (LDAPMod *) -1;
1204 if (modlist[curmod] == (LDAPMod *) -1) {
1205 if (!(modlist = TALLOC_REALLOC_ARRAY(ctx, modlist, LDAPMod *,
1206 curmod+ADS_MODLIST_ALLOC_SIZE+1)))
1207 return ADS_ERROR(LDAP_NO_MEMORY);
1208 memset(&modlist[curmod], 0,
1209 ADS_MODLIST_ALLOC_SIZE*sizeof(LDAPMod *));
1210 modlist[curmod+ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1211 *mods = (ADS_MODLIST)modlist;
1214 if (!(modlist[curmod] = TALLOC_ZERO_P(ctx, LDAPMod)))
1215 return ADS_ERROR(LDAP_NO_MEMORY);
1216 modlist[curmod]->mod_type = talloc_strdup(ctx, name);
1217 if (mod_op & LDAP_MOD_BVALUES) {
1218 modlist[curmod]->mod_bvalues = ber_values;
1219 } else if (mod_op & LDAP_MOD_DELETE) {
1220 modlist[curmod]->mod_values = NULL;
1222 modlist[curmod]->mod_values = char_values;
1225 modlist[curmod]->mod_op = mod_op;
1226 return ADS_ERROR(LDAP_SUCCESS);
1230 * Add a single string value to a mod list
1231 * @param ctx An initialized TALLOC_CTX
1232 * @param mods An initialized ADS_MODLIST
1233 * @param name The attribute name to add
1234 * @param val The value to add - NULL means DELETE
1235 * @return ADS STATUS indicating success of add
1237 ADS_STATUS ads_mod_str(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1238 const char *name, const char *val)
1240 const char *values[2];
1246 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1247 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE, name, values);
1251 * Add an array of string values to a mod list
1252 * @param ctx An initialized TALLOC_CTX
1253 * @param mods An initialized ADS_MODLIST
1254 * @param name The attribute name to add
1255 * @param vals The array of string values to add - NULL means DELETE
1256 * @return ADS STATUS indicating success of add
1258 ADS_STATUS ads_mod_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1259 const char *name, const char **vals)
1262 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1263 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE,
1264 name, (const void **) vals);
1269 * Add a single ber-encoded value to a mod list
1270 * @param ctx An initialized TALLOC_CTX
1271 * @param mods An initialized ADS_MODLIST
1272 * @param name The attribute name to add
1273 * @param val The value to add - NULL means DELETE
1274 * @return ADS STATUS indicating success of add
1276 static ADS_STATUS ads_mod_ber(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1277 const char *name, const struct berval *val)
1279 const struct berval *values[2];
1284 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1285 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE|LDAP_MOD_BVALUES,
1286 name, (const void **) values);
1291 * Perform an ldap modify
1292 * @param ads connection to ads server
1293 * @param mod_dn DistinguishedName to modify
1294 * @param mods list of modifications to perform
1295 * @return status of modify
1297 ADS_STATUS ads_gen_mod(ADS_STRUCT *ads, const char *mod_dn, ADS_MODLIST mods)
1300 char *utf8_dn = NULL;
1301 size_t converted_size;
1303 this control is needed to modify that contains a currently
1304 non-existent attribute (but allowable for the object) to run
1306 LDAPControl PermitModify = {
1307 CONST_DISCARD(char *, ADS_PERMIT_MODIFY_OID),
1310 LDAPControl *controls[2];
1312 controls[0] = &PermitModify;
1315 if (!push_utf8_allocate(&utf8_dn, mod_dn, &converted_size)) {
1316 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1319 /* find the end of the list, marked by NULL or -1 */
1320 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1321 /* make sure the end of the list is NULL */
1323 ret = ldap_modify_ext_s(ads->ldap.ld, utf8_dn,
1324 (LDAPMod **) mods, controls, NULL);
1326 return ADS_ERROR(ret);
1330 * Perform an ldap add
1331 * @param ads connection to ads server
1332 * @param new_dn DistinguishedName to add
1333 * @param mods list of attributes and values for DN
1334 * @return status of add
1336 ADS_STATUS ads_gen_add(ADS_STRUCT *ads, const char *new_dn, ADS_MODLIST mods)
1339 char *utf8_dn = NULL;
1340 size_t converted_size;
1342 if (!push_utf8_allocate(&utf8_dn, new_dn, &converted_size)) {
1343 DEBUG(1, ("ads_gen_add: push_utf8_allocate failed!"));
1344 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1347 /* find the end of the list, marked by NULL or -1 */
1348 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1349 /* make sure the end of the list is NULL */
1352 ret = ldap_add_s(ads->ldap.ld, utf8_dn, (LDAPMod**)mods);
1354 return ADS_ERROR(ret);
1358 * Delete a DistinguishedName
1359 * @param ads connection to ads server
1360 * @param new_dn DistinguishedName to delete
1361 * @return status of delete
1363 ADS_STATUS ads_del_dn(ADS_STRUCT *ads, char *del_dn)
1366 char *utf8_dn = NULL;
1367 size_t converted_size;
1368 if (!push_utf8_allocate(&utf8_dn, del_dn, &converted_size)) {
1369 DEBUG(1, ("ads_del_dn: push_utf8_allocate failed!"));
1370 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1373 ret = ldap_delete_s(ads->ldap.ld, utf8_dn);
1375 return ADS_ERROR(ret);
1379 * Build an org unit string
1380 * if org unit is Computers or blank then assume a container, otherwise
1381 * assume a / separated list of organisational units.
1382 * jmcd: '\' is now used for escapes so certain chars can be in the ou (e.g. #)
1383 * @param ads connection to ads server
1384 * @param org_unit Organizational unit
1385 * @return org unit string - caller must free
1387 char *ads_ou_string(ADS_STRUCT *ads, const char *org_unit)
1391 if (!org_unit || !*org_unit) {
1393 ret = ads_default_ou_string(ads, WELL_KNOWN_GUID_COMPUTERS);
1395 /* samba4 might not yet respond to a wellknownobject-query */
1396 return ret ? ret : SMB_STRDUP("cn=Computers");
1399 if (strequal(org_unit, "Computers")) {
1400 return SMB_STRDUP("cn=Computers");
1403 /* jmcd: removed "\\" from the separation chars, because it is
1404 needed as an escape for chars like '#' which are valid in an
1406 return ads_build_path(org_unit, "/", "ou=", 1);
1410 * Get a org unit string for a well-known GUID
1411 * @param ads connection to ads server
1412 * @param wknguid Well known GUID
1413 * @return org unit string - caller must free
1415 char *ads_default_ou_string(ADS_STRUCT *ads, const char *wknguid)
1418 LDAPMessage *res = NULL;
1419 char *base, *wkn_dn = NULL, *ret = NULL, **wkn_dn_exp = NULL,
1420 **bind_dn_exp = NULL;
1421 const char *attrs[] = {"distinguishedName", NULL};
1422 int new_ln, wkn_ln, bind_ln, i;
1424 if (wknguid == NULL) {
1428 if (asprintf(&base, "<WKGUID=%s,%s>", wknguid, ads->config.bind_path ) == -1) {
1429 DEBUG(1, ("asprintf failed!\n"));
1433 status = ads_search_dn(ads, &res, base, attrs);
1434 if (!ADS_ERR_OK(status)) {
1435 DEBUG(1,("Failed while searching for: %s\n", base));
1439 if (ads_count_replies(ads, res) != 1) {
1443 /* substitute the bind-path from the well-known-guid-search result */
1444 wkn_dn = ads_get_dn(ads, res);
1449 wkn_dn_exp = ldap_explode_dn(wkn_dn, 0);
1454 bind_dn_exp = ldap_explode_dn(ads->config.bind_path, 0);
1459 for (wkn_ln=0; wkn_dn_exp[wkn_ln]; wkn_ln++)
1461 for (bind_ln=0; bind_dn_exp[bind_ln]; bind_ln++)
1464 new_ln = wkn_ln - bind_ln;
1466 ret = SMB_STRDUP(wkn_dn_exp[0]);
1471 for (i=1; i < new_ln; i++) {
1474 if (asprintf(&s, "%s,%s", ret, wkn_dn_exp[i]) == -1) {
1480 ret = SMB_STRDUP(s);
1489 ads_msgfree(ads, res);
1490 ads_memfree(ads, wkn_dn);
1492 ldap_value_free(wkn_dn_exp);
1495 ldap_value_free(bind_dn_exp);
1502 * Adds (appends) an item to an attribute array, rather then
1503 * replacing the whole list
1504 * @param ctx An initialized TALLOC_CTX
1505 * @param mods An initialized ADS_MODLIST
1506 * @param name name of the ldap attribute to append to
1507 * @param vals an array of values to add
1508 * @return status of addition
1511 ADS_STATUS ads_add_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1512 const char *name, const char **vals)
1514 return ads_modlist_add(ctx, mods, LDAP_MOD_ADD, name,
1515 (const void *) vals);
1519 * Determines the computer account's current KVNO via an LDAP lookup
1520 * @param ads An initialized ADS_STRUCT
1521 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1522 * @return the kvno for the computer account, or -1 in case of a failure.
1525 uint32 ads_get_kvno(ADS_STRUCT *ads, const char *machine_name)
1527 LDAPMessage *res = NULL;
1528 uint32 kvno = (uint32)-1; /* -1 indicates a failure */
1530 const char *attrs[] = {"msDS-KeyVersionNumber", NULL};
1531 char *dn_string = NULL;
1532 ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS);
1534 DEBUG(5,("ads_get_kvno: Searching for host %s\n", machine_name));
1535 if (asprintf(&filter, "(samAccountName=%s$)", machine_name) == -1) {
1538 ret = ads_search(ads, &res, filter, attrs);
1540 if (!ADS_ERR_OK(ret) && ads_count_replies(ads, res)) {
1541 DEBUG(1,("ads_get_kvno: Computer Account For %s not found.\n", machine_name));
1542 ads_msgfree(ads, res);
1546 dn_string = ads_get_dn(ads, res);
1548 DEBUG(0,("ads_get_kvno: out of memory.\n"));
1549 ads_msgfree(ads, res);
1552 DEBUG(5,("ads_get_kvno: Using: %s\n", dn_string));
1553 ads_memfree(ads, dn_string);
1555 /* ---------------------------------------------------------
1556 * 0 is returned as a default KVNO from this point on...
1557 * This is done because Windows 2000 does not support key
1558 * version numbers. Chances are that a failure in the next
1559 * step is simply due to Windows 2000 being used for a
1560 * domain controller. */
1563 if (!ads_pull_uint32(ads, res, "msDS-KeyVersionNumber", &kvno)) {
1564 DEBUG(3,("ads_get_kvno: Error Determining KVNO!\n"));
1565 DEBUG(3,("ads_get_kvno: Windows 2000 does not support KVNO's, so this may be normal.\n"));
1566 ads_msgfree(ads, res);
1571 DEBUG(5,("ads_get_kvno: Looked Up KVNO of: %d\n", kvno));
1572 ads_msgfree(ads, res);
1577 * This clears out all registered spn's for a given hostname
1578 * @param ads An initilaized ADS_STRUCT
1579 * @param machine_name the NetBIOS name of the computer.
1580 * @return 0 upon success, non-zero otherwise.
1583 ADS_STATUS ads_clear_service_principal_names(ADS_STRUCT *ads, const char *machine_name)
1586 LDAPMessage *res = NULL;
1588 const char *servicePrincipalName[1] = {NULL};
1589 ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS);
1590 char *dn_string = NULL;
1592 ret = ads_find_machine_acct(ads, &res, machine_name);
1593 if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
1594 DEBUG(5,("ads_clear_service_principal_names: WARNING: Host Account for %s not found... skipping operation.\n", machine_name));
1595 DEBUG(5,("ads_clear_service_principal_names: WARNING: Service Principals for %s have NOT been cleared.\n", machine_name));
1596 ads_msgfree(ads, res);
1597 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1600 DEBUG(5,("ads_clear_service_principal_names: Host account for %s found\n", machine_name));
1601 ctx = talloc_init("ads_clear_service_principal_names");
1603 ads_msgfree(ads, res);
1604 return ADS_ERROR(LDAP_NO_MEMORY);
1607 if (!(mods = ads_init_mods(ctx))) {
1608 talloc_destroy(ctx);
1609 ads_msgfree(ads, res);
1610 return ADS_ERROR(LDAP_NO_MEMORY);
1612 ret = ads_mod_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1613 if (!ADS_ERR_OK(ret)) {
1614 DEBUG(1,("ads_clear_service_principal_names: Error creating strlist.\n"));
1615 ads_msgfree(ads, res);
1616 talloc_destroy(ctx);
1619 dn_string = ads_get_dn(ads, res);
1621 talloc_destroy(ctx);
1622 ads_msgfree(ads, res);
1623 return ADS_ERROR(LDAP_NO_MEMORY);
1625 ret = ads_gen_mod(ads, dn_string, mods);
1626 ads_memfree(ads,dn_string);
1627 if (!ADS_ERR_OK(ret)) {
1628 DEBUG(1,("ads_clear_service_principal_names: Error: Updating Service Principals for machine %s in LDAP\n",
1630 ads_msgfree(ads, res);
1631 talloc_destroy(ctx);
1635 ads_msgfree(ads, res);
1636 talloc_destroy(ctx);
1641 * This adds a service principal name to an existing computer account
1642 * (found by hostname) in AD.
1643 * @param ads An initialized ADS_STRUCT
1644 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1645 * @param my_fqdn The fully qualified DNS name of the machine
1646 * @param spn A string of the service principal to add, i.e. 'host'
1647 * @return 0 upon sucess, or non-zero if a failure occurs
1650 ADS_STATUS ads_add_service_principal_name(ADS_STRUCT *ads, const char *machine_name,
1651 const char *my_fqdn, const char *spn)
1655 LDAPMessage *res = NULL;
1658 char *dn_string = NULL;
1659 const char *servicePrincipalName[3] = {NULL, NULL, NULL};
1661 ret = ads_find_machine_acct(ads, &res, machine_name);
1662 if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
1663 DEBUG(1,("ads_add_service_principal_name: WARNING: Host Account for %s not found... skipping operation.\n",
1665 DEBUG(1,("ads_add_service_principal_name: WARNING: Service Principal '%s/%s@%s' has NOT been added.\n",
1666 spn, machine_name, ads->config.realm));
1667 ads_msgfree(ads, res);
1668 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1671 DEBUG(1,("ads_add_service_principal_name: Host account for %s found\n", machine_name));
1672 if (!(ctx = talloc_init("ads_add_service_principal_name"))) {
1673 ads_msgfree(ads, res);
1674 return ADS_ERROR(LDAP_NO_MEMORY);
1677 /* add short name spn */
1679 if ( (psp1 = talloc_asprintf(ctx, "%s/%s", spn, machine_name)) == NULL ) {
1680 talloc_destroy(ctx);
1681 ads_msgfree(ads, res);
1682 return ADS_ERROR(LDAP_NO_MEMORY);
1685 strlower_m(&psp1[strlen(spn)]);
1686 servicePrincipalName[0] = psp1;
1688 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n",
1689 psp1, machine_name));
1692 /* add fully qualified spn */
1694 if ( (psp2 = talloc_asprintf(ctx, "%s/%s", spn, my_fqdn)) == NULL ) {
1695 ret = ADS_ERROR(LDAP_NO_MEMORY);
1699 strlower_m(&psp2[strlen(spn)]);
1700 servicePrincipalName[1] = psp2;
1702 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n",
1703 psp2, machine_name));
1705 if ( (mods = ads_init_mods(ctx)) == NULL ) {
1706 ret = ADS_ERROR(LDAP_NO_MEMORY);
1710 ret = ads_add_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1711 if (!ADS_ERR_OK(ret)) {
1712 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
1716 if ( (dn_string = ads_get_dn(ads, res)) == NULL ) {
1717 ret = ADS_ERROR(LDAP_NO_MEMORY);
1721 ret = ads_gen_mod(ads, dn_string, mods);
1722 ads_memfree(ads,dn_string);
1723 if (!ADS_ERR_OK(ret)) {
1724 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
1730 ads_msgfree(ads, res);
1735 * adds a machine account to the ADS server
1736 * @param ads An intialized ADS_STRUCT
1737 * @param machine_name - the NetBIOS machine name of this account.
1738 * @param account_type A number indicating the type of account to create
1739 * @param org_unit The LDAP path in which to place this account
1740 * @return 0 upon success, or non-zero otherwise
1743 ADS_STATUS ads_create_machine_acct(ADS_STRUCT *ads, const char *machine_name,
1744 const char *org_unit)
1747 char *samAccountName, *controlstr;
1750 char *machine_escaped = NULL;
1752 const char *objectClass[] = {"top", "person", "organizationalPerson",
1753 "user", "computer", NULL};
1754 LDAPMessage *res = NULL;
1755 uint32 acct_control = ( UF_WORKSTATION_TRUST_ACCOUNT |\
1756 UF_DONT_EXPIRE_PASSWD |\
1757 UF_ACCOUNTDISABLE );
1759 if (!(ctx = talloc_init("ads_add_machine_acct")))
1760 return ADS_ERROR(LDAP_NO_MEMORY);
1762 ret = ADS_ERROR(LDAP_NO_MEMORY);
1764 machine_escaped = escape_rdn_val_string_alloc(machine_name);
1765 if (!machine_escaped) {
1769 new_dn = talloc_asprintf(ctx, "cn=%s,%s", machine_escaped, org_unit);
1770 samAccountName = talloc_asprintf(ctx, "%s$", machine_name);
1772 if ( !new_dn || !samAccountName ) {
1776 #ifndef ENCTYPE_ARCFOUR_HMAC
1777 acct_control |= UF_USE_DES_KEY_ONLY;
1780 if (!(controlstr = talloc_asprintf(ctx, "%u", acct_control))) {
1784 if (!(mods = ads_init_mods(ctx))) {
1788 ads_mod_str(ctx, &mods, "cn", machine_name);
1789 ads_mod_str(ctx, &mods, "sAMAccountName", samAccountName);
1790 ads_mod_strlist(ctx, &mods, "objectClass", objectClass);
1791 ads_mod_str(ctx, &mods, "userAccountControl", controlstr);
1793 ret = ads_gen_add(ads, new_dn, mods);
1796 SAFE_FREE(machine_escaped);
1797 ads_msgfree(ads, res);
1798 talloc_destroy(ctx);
1804 * move a machine account to another OU on the ADS server
1805 * @param ads - An intialized ADS_STRUCT
1806 * @param machine_name - the NetBIOS machine name of this account.
1807 * @param org_unit - The LDAP path in which to place this account
1808 * @param moved - whether we moved the machine account (optional)
1809 * @return 0 upon success, or non-zero otherwise
1812 ADS_STATUS ads_move_machine_acct(ADS_STRUCT *ads, const char *machine_name,
1813 const char *org_unit, bool *moved)
1817 LDAPMessage *res = NULL;
1818 char *filter = NULL;
1819 char *computer_dn = NULL;
1821 char *computer_rdn = NULL;
1822 bool need_move = False;
1824 if (asprintf(&filter, "(samAccountName=%s$)", machine_name) == -1) {
1825 rc = ADS_ERROR(LDAP_NO_MEMORY);
1829 /* Find pre-existing machine */
1830 rc = ads_search(ads, &res, filter, NULL);
1831 if (!ADS_ERR_OK(rc)) {
1835 computer_dn = ads_get_dn(ads, res);
1837 rc = ADS_ERROR(LDAP_NO_MEMORY);
1841 parent_dn = ads_parent_dn(computer_dn);
1842 if (strequal(parent_dn, org_unit)) {
1848 if (asprintf(&computer_rdn, "CN=%s", machine_name) == -1) {
1849 rc = ADS_ERROR(LDAP_NO_MEMORY);
1853 ldap_status = ldap_rename_s(ads->ldap.ld, computer_dn, computer_rdn,
1854 org_unit, 1, NULL, NULL);
1855 rc = ADS_ERROR(ldap_status);
1858 ads_msgfree(ads, res);
1860 SAFE_FREE(computer_dn);
1861 SAFE_FREE(computer_rdn);
1863 if (!ADS_ERR_OK(rc)) {
1875 dump a binary result from ldap
1877 static void dump_binary(ADS_STRUCT *ads, const char *field, struct berval **values)
1880 for (i=0; values[i]; i++) {
1881 printf("%s: ", field);
1882 for (j=0; j<values[i]->bv_len; j++) {
1883 printf("%02X", (unsigned char)values[i]->bv_val[j]);
1889 static void dump_guid(ADS_STRUCT *ads, const char *field, struct berval **values)
1892 for (i=0; values[i]; i++) {
1897 memcpy(guid.info, values[i]->bv_val, sizeof(guid.info));
1898 smb_uuid_unpack(guid, &tmp);
1899 printf("%s: %s\n", field, smb_uuid_string(talloc_tos(), tmp));
1904 dump a sid result from ldap
1906 static void dump_sid(ADS_STRUCT *ads, const char *field, struct berval **values)
1909 for (i=0; values[i]; i++) {
1912 sid_parse(values[i]->bv_val, values[i]->bv_len, &sid);
1913 printf("%s: %s\n", field, sid_to_fstring(tmp, &sid));
1918 dump ntSecurityDescriptor
1920 static void dump_sd(ADS_STRUCT *ads, const char *filed, struct berval **values)
1922 TALLOC_CTX *frame = talloc_stackframe();
1923 struct security_descriptor *psd;
1926 status = unmarshall_sec_desc(talloc_tos(), (uint8 *)values[0]->bv_val,
1927 values[0]->bv_len, &psd);
1928 if (!NT_STATUS_IS_OK(status)) {
1929 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
1930 nt_errstr(status)));
1936 ads_disp_sd(ads, talloc_tos(), psd);
1943 dump a string result from ldap
1945 static void dump_string(const char *field, char **values)
1948 for (i=0; values[i]; i++) {
1949 printf("%s: %s\n", field, values[i]);
1954 dump a field from LDAP on stdout
1958 static bool ads_dump_field(ADS_STRUCT *ads, char *field, void **values, void *data_area)
1963 void (*handler)(ADS_STRUCT *, const char *, struct berval **);
1965 {"objectGUID", False, dump_guid},
1966 {"netbootGUID", False, dump_guid},
1967 {"nTSecurityDescriptor", False, dump_sd},
1968 {"dnsRecord", False, dump_binary},
1969 {"objectSid", False, dump_sid},
1970 {"tokenGroups", False, dump_sid},
1971 {"tokenGroupsNoGCAcceptable", False, dump_sid},
1972 {"tokengroupsGlobalandUniversal", False, dump_sid},
1973 {"mS-DS-CreatorSID", False, dump_sid},
1974 {"msExchMailboxGuid", False, dump_guid},
1979 if (!field) { /* must be end of an entry */
1984 for (i=0; handlers[i].name; i++) {
1985 if (StrCaseCmp(handlers[i].name, field) == 0) {
1986 if (!values) /* first time, indicate string or not */
1987 return handlers[i].string;
1988 handlers[i].handler(ads, field, (struct berval **) values);
1992 if (!handlers[i].name) {
1993 if (!values) /* first time, indicate string conversion */
1995 dump_string(field, (char **)values);
2001 * Dump a result from LDAP on stdout
2002 * used for debugging
2003 * @param ads connection to ads server
2004 * @param res Results to dump
2007 void ads_dump(ADS_STRUCT *ads, LDAPMessage *res)
2009 ads_process_results(ads, res, ads_dump_field, NULL);
2013 * Walk through results, calling a function for each entry found.
2014 * The function receives a field name, a berval * array of values,
2015 * and a data area passed through from the start. The function is
2016 * called once with null for field and values at the end of each
2018 * @param ads connection to ads server
2019 * @param res Results to process
2020 * @param fn Function for processing each result
2021 * @param data_area user-defined area to pass to function
2023 void ads_process_results(ADS_STRUCT *ads, LDAPMessage *res,
2024 bool (*fn)(ADS_STRUCT *, char *, void **, void *),
2029 size_t converted_size;
2031 if (!(ctx = talloc_init("ads_process_results")))
2034 for (msg = ads_first_entry(ads, res); msg;
2035 msg = ads_next_entry(ads, msg)) {
2039 for (utf8_field=ldap_first_attribute(ads->ldap.ld,
2040 (LDAPMessage *)msg,&b);
2042 utf8_field=ldap_next_attribute(ads->ldap.ld,
2043 (LDAPMessage *)msg,b)) {
2044 struct berval **ber_vals;
2045 char **str_vals, **utf8_vals;
2049 if (!pull_utf8_talloc(ctx, &field, utf8_field,
2052 DEBUG(0,("ads_process_results: "
2053 "pull_utf8_talloc failed: %s",
2057 string = fn(ads, field, NULL, data_area);
2060 utf8_vals = ldap_get_values(ads->ldap.ld,
2061 (LDAPMessage *)msg, field);
2062 str_vals = ads_pull_strvals(ctx,
2063 (const char **) utf8_vals);
2064 fn(ads, field, (void **) str_vals, data_area);
2065 ldap_value_free(utf8_vals);
2067 ber_vals = ldap_get_values_len(ads->ldap.ld,
2068 (LDAPMessage *)msg, field);
2069 fn(ads, field, (void **) ber_vals, data_area);
2071 ldap_value_free_len(ber_vals);
2073 ldap_memfree(utf8_field);
2076 talloc_free_children(ctx);
2077 fn(ads, NULL, NULL, data_area); /* completed an entry */
2080 talloc_destroy(ctx);
2084 * count how many replies are in a LDAPMessage
2085 * @param ads connection to ads server
2086 * @param res Results to count
2087 * @return number of replies
2089 int ads_count_replies(ADS_STRUCT *ads, void *res)
2091 return ldap_count_entries(ads->ldap.ld, (LDAPMessage *)res);
2095 * pull the first entry from a ADS result
2096 * @param ads connection to ads server
2097 * @param res Results of search
2098 * @return first entry from result
2100 LDAPMessage *ads_first_entry(ADS_STRUCT *ads, LDAPMessage *res)
2102 return ldap_first_entry(ads->ldap.ld, res);
2106 * pull the next entry from a ADS result
2107 * @param ads connection to ads server
2108 * @param res Results of search
2109 * @return next entry from result
2111 LDAPMessage *ads_next_entry(ADS_STRUCT *ads, LDAPMessage *res)
2113 return ldap_next_entry(ads->ldap.ld, res);
2117 * pull the first message from a ADS result
2118 * @param ads connection to ads server
2119 * @param res Results of search
2120 * @return first message from result
2122 LDAPMessage *ads_first_message(ADS_STRUCT *ads, LDAPMessage *res)
2124 return ldap_first_message(ads->ldap.ld, res);
2128 * pull the next message from a ADS result
2129 * @param ads connection to ads server
2130 * @param res Results of search
2131 * @return next message from result
2133 LDAPMessage *ads_next_message(ADS_STRUCT *ads, LDAPMessage *res)
2135 return ldap_next_message(ads->ldap.ld, res);
2139 * pull a single string from a ADS result
2140 * @param ads connection to ads server
2141 * @param mem_ctx TALLOC_CTX to use for allocating result string
2142 * @param msg Results of search
2143 * @param field Attribute to retrieve
2144 * @return Result string in talloc context
2146 char *ads_pull_string(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, LDAPMessage *msg,
2152 size_t converted_size;
2154 values = ldap_get_values(ads->ldap.ld, msg, field);
2158 if (values[0] && pull_utf8_talloc(mem_ctx, &ux_string, values[0],
2163 ldap_value_free(values);
2168 * pull an array of strings from a ADS result
2169 * @param ads connection to ads server
2170 * @param mem_ctx TALLOC_CTX to use for allocating result string
2171 * @param msg Results of search
2172 * @param field Attribute to retrieve
2173 * @return Result strings in talloc context
2175 char **ads_pull_strings(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2176 LDAPMessage *msg, const char *field,
2182 size_t converted_size;
2184 values = ldap_get_values(ads->ldap.ld, msg, field);
2188 *num_values = ldap_count_values(values);
2190 ret = TALLOC_ARRAY(mem_ctx, char *, *num_values + 1);
2192 ldap_value_free(values);
2196 for (i=0;i<*num_values;i++) {
2197 if (!pull_utf8_talloc(mem_ctx, &ret[i], values[i],
2200 ldap_value_free(values);
2206 ldap_value_free(values);
2211 * pull an array of strings from a ADS result
2212 * (handle large multivalue attributes with range retrieval)
2213 * @param ads connection to ads server
2214 * @param mem_ctx TALLOC_CTX to use for allocating result string
2215 * @param msg Results of search
2216 * @param field Attribute to retrieve
2217 * @param current_strings strings returned by a previous call to this function
2218 * @param next_attribute The next query should ask for this attribute
2219 * @param num_values How many values did we get this time?
2220 * @param more_values Are there more values to get?
2221 * @return Result strings in talloc context
2223 char **ads_pull_strings_range(ADS_STRUCT *ads,
2224 TALLOC_CTX *mem_ctx,
2225 LDAPMessage *msg, const char *field,
2226 char **current_strings,
2227 const char **next_attribute,
2228 size_t *num_strings,
2232 char *expected_range_attrib, *range_attr;
2233 BerElement *ptr = NULL;
2236 size_t num_new_strings;
2237 unsigned long int range_start;
2238 unsigned long int range_end;
2240 /* we might have been given the whole lot anyway */
2241 if ((strings = ads_pull_strings(ads, mem_ctx, msg, field, num_strings))) {
2242 *more_strings = False;
2246 expected_range_attrib = talloc_asprintf(mem_ctx, "%s;Range=", field);
2248 /* look for Range result */
2249 for (attr = ldap_first_attribute(ads->ldap.ld, (LDAPMessage *)msg, &ptr);
2251 attr = ldap_next_attribute(ads->ldap.ld, (LDAPMessage *)msg, ptr)) {
2252 /* we ignore the fact that this is utf8, as all attributes are ascii... */
2253 if (strnequal(attr, expected_range_attrib, strlen(expected_range_attrib))) {
2261 /* nothing here - this field is just empty */
2262 *more_strings = False;
2266 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-%lu",
2267 &range_start, &range_end) == 2) {
2268 *more_strings = True;
2270 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-*",
2271 &range_start) == 1) {
2272 *more_strings = False;
2274 DEBUG(1, ("ads_pull_strings_range: Cannot parse Range attriubte (%s)\n",
2276 ldap_memfree(range_attr);
2277 *more_strings = False;
2282 if ((*num_strings) != range_start) {
2283 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) doesn't start at %u, but at %lu"
2284 " - aborting range retreival\n",
2285 range_attr, (unsigned int)(*num_strings) + 1, range_start));
2286 ldap_memfree(range_attr);
2287 *more_strings = False;
2291 new_strings = ads_pull_strings(ads, mem_ctx, msg, range_attr, &num_new_strings);
2293 if (*more_strings && ((*num_strings + num_new_strings) != (range_end + 1))) {
2294 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) tells us we have %lu "
2295 "strings in this bunch, but we only got %lu - aborting range retreival\n",
2296 range_attr, (unsigned long int)range_end - range_start + 1,
2297 (unsigned long int)num_new_strings));
2298 ldap_memfree(range_attr);
2299 *more_strings = False;
2303 strings = TALLOC_REALLOC_ARRAY(mem_ctx, current_strings, char *,
2304 *num_strings + num_new_strings);
2306 if (strings == NULL) {
2307 ldap_memfree(range_attr);
2308 *more_strings = False;
2312 if (new_strings && num_new_strings) {
2313 memcpy(&strings[*num_strings], new_strings,
2314 sizeof(*new_strings) * num_new_strings);
2317 (*num_strings) += num_new_strings;
2319 if (*more_strings) {
2320 *next_attribute = talloc_asprintf(mem_ctx,
2325 if (!*next_attribute) {
2326 DEBUG(1, ("talloc_asprintf for next attribute failed!\n"));
2327 ldap_memfree(range_attr);
2328 *more_strings = False;
2333 ldap_memfree(range_attr);
2339 * pull a single uint32 from a ADS result
2340 * @param ads connection to ads server
2341 * @param msg Results of search
2342 * @param field Attribute to retrieve
2343 * @param v Pointer to int to store result
2344 * @return boolean inidicating success
2346 bool ads_pull_uint32(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
2351 values = ldap_get_values(ads->ldap.ld, msg, field);
2355 ldap_value_free(values);
2359 *v = atoi(values[0]);
2360 ldap_value_free(values);
2365 * pull a single objectGUID from an ADS result
2366 * @param ads connection to ADS server
2367 * @param msg results of search
2368 * @param guid 37-byte area to receive text guid
2369 * @return boolean indicating success
2371 bool ads_pull_guid(ADS_STRUCT *ads, LDAPMessage *msg, struct GUID *guid)
2374 UUID_FLAT flat_guid;
2376 values = ldap_get_values(ads->ldap.ld, msg, "objectGUID");
2381 memcpy(&flat_guid.info, values[0], sizeof(UUID_FLAT));
2382 smb_uuid_unpack(flat_guid, guid);
2383 ldap_value_free(values);
2386 ldap_value_free(values);
2393 * pull a single DOM_SID from a ADS result
2394 * @param ads connection to ads server
2395 * @param msg Results of search
2396 * @param field Attribute to retrieve
2397 * @param sid Pointer to sid to store result
2398 * @return boolean inidicating success
2400 bool ads_pull_sid(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
2403 struct berval **values;
2406 values = ldap_get_values_len(ads->ldap.ld, msg, field);
2412 ret = sid_parse(values[0]->bv_val, values[0]->bv_len, sid);
2414 ldap_value_free_len(values);
2419 * pull an array of DOM_SIDs from a ADS result
2420 * @param ads connection to ads server
2421 * @param mem_ctx TALLOC_CTX for allocating sid array
2422 * @param msg Results of search
2423 * @param field Attribute to retrieve
2424 * @param sids pointer to sid array to allocate
2425 * @return the count of SIDs pulled
2427 int ads_pull_sids(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2428 LDAPMessage *msg, const char *field, DOM_SID **sids)
2430 struct berval **values;
2434 values = ldap_get_values_len(ads->ldap.ld, msg, field);
2439 for (i=0; values[i]; i++)
2443 (*sids) = TALLOC_ARRAY(mem_ctx, DOM_SID, i);
2445 ldap_value_free_len(values);
2453 for (i=0; values[i]; i++) {
2454 ret = sid_parse(values[i]->bv_val, values[i]->bv_len, &(*sids)[count]);
2456 DEBUG(10, ("pulling SID: %s\n",
2457 sid_string_dbg(&(*sids)[count])));
2462 ldap_value_free_len(values);
2467 * pull a SEC_DESC from a ADS result
2468 * @param ads connection to ads server
2469 * @param mem_ctx TALLOC_CTX for allocating sid array
2470 * @param msg Results of search
2471 * @param field Attribute to retrieve
2472 * @param sd Pointer to *SEC_DESC to store result (talloc()ed)
2473 * @return boolean inidicating success
2475 bool ads_pull_sd(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2476 LDAPMessage *msg, const char *field, SEC_DESC **sd)
2478 struct berval **values;
2481 values = ldap_get_values_len(ads->ldap.ld, msg, field);
2483 if (!values) return false;
2487 status = unmarshall_sec_desc(mem_ctx,
2488 (uint8 *)values[0]->bv_val,
2489 values[0]->bv_len, sd);
2490 if (!NT_STATUS_IS_OK(status)) {
2491 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
2492 nt_errstr(status)));
2497 ldap_value_free_len(values);
2502 * in order to support usernames longer than 21 characters we need to
2503 * use both the sAMAccountName and the userPrincipalName attributes
2504 * It seems that not all users have the userPrincipalName attribute set
2506 * @param ads connection to ads server
2507 * @param mem_ctx TALLOC_CTX for allocating sid array
2508 * @param msg Results of search
2509 * @return the username
2511 char *ads_pull_username(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2517 /* lookup_name() only works on the sAMAccountName to
2518 returning the username portion of userPrincipalName
2519 breaks winbindd_getpwnam() */
2521 ret = ads_pull_string(ads, mem_ctx, msg, "userPrincipalName");
2522 if (ret && (p = strchr_m(ret, '@'))) {
2527 return ads_pull_string(ads, mem_ctx, msg, "sAMAccountName");
2532 * find the update serial number - this is the core of the ldap cache
2533 * @param ads connection to ads server
2534 * @param ads connection to ADS server
2535 * @param usn Pointer to retrieved update serial number
2536 * @return status of search
2538 ADS_STATUS ads_USN(ADS_STRUCT *ads, uint32 *usn)
2540 const char *attrs[] = {"highestCommittedUSN", NULL};
2544 status = ads_do_search_retry(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2545 if (!ADS_ERR_OK(status))
2548 if (ads_count_replies(ads, res) != 1) {
2549 ads_msgfree(ads, res);
2550 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2553 if (!ads_pull_uint32(ads, res, "highestCommittedUSN", usn)) {
2554 ads_msgfree(ads, res);
2555 return ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
2558 ads_msgfree(ads, res);
2562 /* parse a ADS timestring - typical string is
2563 '20020917091222.0Z0' which means 09:12.22 17th September
2565 static time_t ads_parse_time(const char *str)
2571 if (sscanf(str, "%4d%2d%2d%2d%2d%2d",
2572 &tm.tm_year, &tm.tm_mon, &tm.tm_mday,
2573 &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) {
2582 /********************************************************************
2583 ********************************************************************/
2585 ADS_STATUS ads_current_time(ADS_STRUCT *ads)
2587 const char *attrs[] = {"currentTime", NULL};
2592 ADS_STRUCT *ads_s = ads;
2594 if (!(ctx = talloc_init("ads_current_time"))) {
2595 return ADS_ERROR(LDAP_NO_MEMORY);
2598 /* establish a new ldap tcp session if necessary */
2600 if ( !ads->ldap.ld ) {
2601 if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup,
2602 ads->server.ldap_server )) == NULL )
2606 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
2607 status = ads_connect( ads_s );
2608 if ( !ADS_ERR_OK(status))
2612 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2613 if (!ADS_ERR_OK(status)) {
2617 timestr = ads_pull_string(ads_s, ctx, res, "currentTime");
2619 ads_msgfree(ads_s, res);
2620 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2624 /* but save the time and offset in the original ADS_STRUCT */
2626 ads->config.current_time = ads_parse_time(timestr);
2628 if (ads->config.current_time != 0) {
2629 ads->auth.time_offset = ads->config.current_time - time(NULL);
2630 DEBUG(4,("time offset is %d seconds\n", ads->auth.time_offset));
2633 ads_msgfree(ads, res);
2635 status = ADS_SUCCESS;
2638 /* free any temporary ads connections */
2639 if ( ads_s != ads ) {
2640 ads_destroy( &ads_s );
2642 talloc_destroy(ctx);
2647 /********************************************************************
2648 ********************************************************************/
2650 ADS_STATUS ads_domain_func_level(ADS_STRUCT *ads, uint32 *val)
2652 const char *attrs[] = {"domainFunctionality", NULL};
2655 ADS_STRUCT *ads_s = ads;
2657 *val = DS_DOMAIN_FUNCTION_2000;
2659 /* establish a new ldap tcp session if necessary */
2661 if ( !ads->ldap.ld ) {
2662 if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup,
2663 ads->server.ldap_server )) == NULL )
2667 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
2668 status = ads_connect( ads_s );
2669 if ( !ADS_ERR_OK(status))
2673 /* If the attribute does not exist assume it is a Windows 2000
2674 functional domain */
2676 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2677 if (!ADS_ERR_OK(status)) {
2678 if ( status.err.rc == LDAP_NO_SUCH_ATTRIBUTE ) {
2679 status = ADS_SUCCESS;
2684 if ( !ads_pull_uint32(ads_s, res, "domainFunctionality", val) ) {
2685 DEBUG(5,("ads_domain_func_level: Failed to pull the domainFunctionality attribute.\n"));
2687 DEBUG(3,("ads_domain_func_level: %d\n", *val));
2690 ads_msgfree(ads, res);
2693 /* free any temporary ads connections */
2694 if ( ads_s != ads ) {
2695 ads_destroy( &ads_s );
2702 * find the domain sid for our domain
2703 * @param ads connection to ads server
2704 * @param sid Pointer to domain sid
2705 * @return status of search
2707 ADS_STATUS ads_domain_sid(ADS_STRUCT *ads, DOM_SID *sid)
2709 const char *attrs[] = {"objectSid", NULL};
2713 rc = ads_do_search_retry(ads, ads->config.bind_path, LDAP_SCOPE_BASE, "(objectclass=*)",
2715 if (!ADS_ERR_OK(rc)) return rc;
2716 if (!ads_pull_sid(ads, res, "objectSid", sid)) {
2717 ads_msgfree(ads, res);
2718 return ADS_ERROR_SYSTEM(ENOENT);
2720 ads_msgfree(ads, res);
2726 * find our site name
2727 * @param ads connection to ads server
2728 * @param mem_ctx Pointer to talloc context
2729 * @param site_name Pointer to the sitename
2730 * @return status of search
2732 ADS_STATUS ads_site_dn(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char **site_name)
2736 const char *dn, *service_name;
2737 const char *attrs[] = { "dsServiceName", NULL };
2739 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2740 if (!ADS_ERR_OK(status)) {
2744 service_name = ads_pull_string(ads, mem_ctx, res, "dsServiceName");
2745 if (service_name == NULL) {
2746 ads_msgfree(ads, res);
2747 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2750 ads_msgfree(ads, res);
2752 /* go up three levels */
2753 dn = ads_parent_dn(ads_parent_dn(ads_parent_dn(service_name)));
2755 return ADS_ERROR(LDAP_NO_MEMORY);
2758 *site_name = talloc_strdup(mem_ctx, dn);
2759 if (*site_name == NULL) {
2760 return ADS_ERROR(LDAP_NO_MEMORY);
2765 dsServiceName: CN=NTDS Settings,CN=W2K3DC,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,DC=ber,DC=suse,DC=de
2770 * find the site dn where a machine resides
2771 * @param ads connection to ads server
2772 * @param mem_ctx Pointer to talloc context
2773 * @param computer_name name of the machine
2774 * @param site_name Pointer to the sitename
2775 * @return status of search
2777 ADS_STATUS ads_site_dn_for_machine(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char *computer_name, const char **site_dn)
2781 const char *parent, *filter;
2782 char *config_context = NULL;
2785 /* shortcut a query */
2786 if (strequal(computer_name, ads->config.ldap_server_name)) {
2787 return ads_site_dn(ads, mem_ctx, site_dn);
2790 status = ads_config_path(ads, mem_ctx, &config_context);
2791 if (!ADS_ERR_OK(status)) {
2795 filter = talloc_asprintf(mem_ctx, "(cn=%s)", computer_name);
2796 if (filter == NULL) {
2797 return ADS_ERROR(LDAP_NO_MEMORY);
2800 status = ads_do_search(ads, config_context, LDAP_SCOPE_SUBTREE,
2801 filter, NULL, &res);
2802 if (!ADS_ERR_OK(status)) {
2806 if (ads_count_replies(ads, res) != 1) {
2807 ads_msgfree(ads, res);
2808 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
2811 dn = ads_get_dn(ads, res);
2813 ads_msgfree(ads, res);
2814 return ADS_ERROR(LDAP_NO_MEMORY);
2817 /* go up three levels */
2818 parent = ads_parent_dn(ads_parent_dn(ads_parent_dn(dn)));
2819 if (parent == NULL) {
2820 ads_msgfree(ads, res);
2821 ads_memfree(ads, dn);
2822 return ADS_ERROR(LDAP_NO_MEMORY);
2825 *site_dn = talloc_strdup(mem_ctx, parent);
2826 if (*site_dn == NULL) {
2827 ads_msgfree(ads, res);
2828 ads_memfree(ads, dn);
2829 return ADS_ERROR(LDAP_NO_MEMORY);
2832 ads_memfree(ads, dn);
2833 ads_msgfree(ads, res);
2839 * get the upn suffixes for a domain
2840 * @param ads connection to ads server
2841 * @param mem_ctx Pointer to talloc context
2842 * @param suffixes Pointer to an array of suffixes
2843 * @param num_suffixes Pointer to the number of suffixes
2844 * @return status of search
2846 ADS_STATUS ads_upn_suffixes(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, char ***suffixes, size_t *num_suffixes)
2851 char *config_context = NULL;
2852 const char *attrs[] = { "uPNSuffixes", NULL };
2854 status = ads_config_path(ads, mem_ctx, &config_context);
2855 if (!ADS_ERR_OK(status)) {
2859 base = talloc_asprintf(mem_ctx, "cn=Partitions,%s", config_context);
2861 return ADS_ERROR(LDAP_NO_MEMORY);
2864 status = ads_search_dn(ads, &res, base, attrs);
2865 if (!ADS_ERR_OK(status)) {
2869 if (ads_count_replies(ads, res) != 1) {
2870 ads_msgfree(ads, res);
2871 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
2874 (*suffixes) = ads_pull_strings(ads, mem_ctx, res, "uPNSuffixes", num_suffixes);
2875 if ((*suffixes) == NULL) {
2876 ads_msgfree(ads, res);
2877 return ADS_ERROR(LDAP_NO_MEMORY);
2880 ads_msgfree(ads, res);
2886 * get the joinable ous for a domain
2887 * @param ads connection to ads server
2888 * @param mem_ctx Pointer to talloc context
2889 * @param ous Pointer to an array of ous
2890 * @param num_ous Pointer to the number of ous
2891 * @return status of search
2893 ADS_STATUS ads_get_joinable_ous(ADS_STRUCT *ads,
2894 TALLOC_CTX *mem_ctx,
2899 LDAPMessage *res = NULL;
2900 LDAPMessage *msg = NULL;
2901 const char *attrs[] = { "dn", NULL };
2904 status = ads_search(ads, &res,
2905 "(|(objectClass=domain)(objectclass=organizationalUnit))",
2907 if (!ADS_ERR_OK(status)) {
2911 count = ads_count_replies(ads, res);
2913 ads_msgfree(ads, res);
2914 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2917 for (msg = ads_first_entry(ads, res); msg;
2918 msg = ads_next_entry(ads, msg)) {
2922 dn = ads_get_dn(ads, msg);
2924 ads_msgfree(ads, res);
2925 return ADS_ERROR(LDAP_NO_MEMORY);
2928 if (!add_string_to_array(mem_ctx, dn,
2929 (const char ***)ous,
2931 ads_memfree(ads, dn);
2932 ads_msgfree(ads, res);
2933 return ADS_ERROR(LDAP_NO_MEMORY);
2936 ads_memfree(ads, dn);
2939 ads_msgfree(ads, res);
2946 * pull a DOM_SID from an extended dn string
2947 * @param mem_ctx TALLOC_CTX
2948 * @param extended_dn string
2949 * @param flags string type of extended_dn
2950 * @param sid pointer to a DOM_SID
2951 * @return boolean inidicating success
2953 bool ads_get_sid_from_extended_dn(TALLOC_CTX *mem_ctx,
2954 const char *extended_dn,
2955 enum ads_extended_dn_flags flags,
2964 /* otherwise extended_dn gets stripped off */
2965 if ((dn = talloc_strdup(mem_ctx, extended_dn)) == NULL) {
2969 * ADS_EXTENDED_DN_HEX_STRING:
2970 * <GUID=238e1963cb390f4bb032ba0105525a29>;<SID=010500000000000515000000bb68c8fd6b61b427572eb04556040000>;CN=gd,OU=berlin,OU=suse,DC=ber,DC=suse,DC=de
2972 * ADS_EXTENDED_DN_STRING (only with w2k3):
2973 <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
2976 p = strchr(dn, ';');
2981 if (strncmp(p, ";<SID=", strlen(";<SID=")) != 0) {
2985 p += strlen(";<SID=");
2994 DEBUG(100,("ads_get_sid_from_extended_dn: sid string is %s\n", p));
2998 case ADS_EXTENDED_DN_STRING:
2999 if (!string_to_sid(sid, p)) {
3003 case ADS_EXTENDED_DN_HEX_STRING: {
3007 buf_len = strhex_to_str(buf, sizeof(buf), p, strlen(p));
3012 if (!sid_parse(buf, buf_len, sid)) {
3013 DEBUG(10,("failed to parse sid\n"));
3019 DEBUG(10,("unknown extended dn format\n"));
3027 * pull an array of DOM_SIDs from a ADS result
3028 * @param ads connection to ads server
3029 * @param mem_ctx TALLOC_CTX for allocating sid array
3030 * @param msg Results of search
3031 * @param field Attribute to retrieve
3032 * @param flags string type of extended_dn
3033 * @param sids pointer to sid array to allocate
3034 * @return the count of SIDs pulled
3036 int ads_pull_sids_from_extendeddn(ADS_STRUCT *ads,
3037 TALLOC_CTX *mem_ctx,
3040 enum ads_extended_dn_flags flags,
3047 if ((dn_strings = ads_pull_strings(ads, mem_ctx, msg, field,
3048 &dn_count)) == NULL) {
3052 (*sids) = TALLOC_ZERO_ARRAY(mem_ctx, DOM_SID, dn_count + 1);
3054 TALLOC_FREE(dn_strings);
3058 for (i=0; i<dn_count; i++) {
3060 if (!ads_get_sid_from_extended_dn(mem_ctx, dn_strings[i],
3061 flags, &(*sids)[i])) {
3063 TALLOC_FREE(dn_strings);
3068 TALLOC_FREE(dn_strings);
3073 /********************************************************************
3074 ********************************************************************/
3076 char* ads_get_dnshostname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3078 LDAPMessage *res = NULL;
3083 status = ads_find_machine_acct(ads, &res, global_myname());
3084 if (!ADS_ERR_OK(status)) {
3085 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
3090 if ( (count = ads_count_replies(ads, res)) != 1 ) {
3091 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
3095 if ( (name = ads_pull_string(ads, ctx, res, "dNSHostName")) == NULL ) {
3096 DEBUG(0,("ads_get_dnshostname: No dNSHostName attribute!\n"));
3100 ads_msgfree(ads, res);
3105 /********************************************************************
3106 ********************************************************************/
3108 char* ads_get_upn( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3110 LDAPMessage *res = NULL;
3115 status = ads_find_machine_acct(ads, &res, machine_name);
3116 if (!ADS_ERR_OK(status)) {
3117 DEBUG(0,("ads_get_upn: Failed to find account for %s\n",
3122 if ( (count = ads_count_replies(ads, res)) != 1 ) {
3123 DEBUG(1,("ads_get_upn: %d entries returned!\n", count));
3127 if ( (name = ads_pull_string(ads, ctx, res, "userPrincipalName")) == NULL ) {
3128 DEBUG(2,("ads_get_upn: No userPrincipalName attribute!\n"));
3132 ads_msgfree(ads, res);
3137 /********************************************************************
3138 ********************************************************************/
3140 char* ads_get_samaccountname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3142 LDAPMessage *res = NULL;
3147 status = ads_find_machine_acct(ads, &res, global_myname());
3148 if (!ADS_ERR_OK(status)) {
3149 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
3154 if ( (count = ads_count_replies(ads, res)) != 1 ) {
3155 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
3159 if ( (name = ads_pull_string(ads, ctx, res, "sAMAccountName")) == NULL ) {
3160 DEBUG(0,("ads_get_dnshostname: No sAMAccountName attribute!\n"));
3164 ads_msgfree(ads, res);
3171 SAVED CODE - we used to join via ldap - remember how we did this. JRA.
3174 * Join a machine to a realm
3175 * Creates the machine account and sets the machine password
3176 * @param ads connection to ads server
3177 * @param machine name of host to add
3178 * @param org_unit Organizational unit to place machine in
3179 * @return status of join
3181 ADS_STATUS ads_join_realm(ADS_STRUCT *ads, const char *machine_name,
3182 uint32 account_type, const char *org_unit)
3185 LDAPMessage *res = NULL;
3188 /* machine name must be lowercase */
3189 machine = SMB_STRDUP(machine_name);
3190 strlower_m(machine);
3193 status = ads_find_machine_acct(ads, (void **)&res, machine);
3194 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
3195 DEBUG(0, ("Host account for %s already exists - deleting old account\n", machine));
3196 status = ads_leave_realm(ads, machine);
3197 if (!ADS_ERR_OK(status)) {
3198 DEBUG(0, ("Failed to delete host '%s' from the '%s' realm.\n",
3199 machine, ads->config.realm));
3204 status = ads_add_machine_acct(ads, machine, account_type, org_unit);
3205 if (!ADS_ERR_OK(status)) {
3206 DEBUG(0, ("ads_join_realm: ads_add_machine_acct failed (%s): %s\n", machine, ads_errstr(status)));
3211 status = ads_find_machine_acct(ads, (void **)(void *)&res, machine);
3212 if (!ADS_ERR_OK(status)) {
3213 DEBUG(0, ("ads_join_realm: Host account test failed for machine %s\n", machine));
3219 ads_msgfree(ads, res);
3226 * Delete a machine from the realm
3227 * @param ads connection to ads server
3228 * @param hostname Machine to remove
3229 * @return status of delete
3231 ADS_STATUS ads_leave_realm(ADS_STRUCT *ads, const char *hostname)
3236 char *hostnameDN, *host;
3238 LDAPControl ldap_control;
3239 LDAPControl * pldap_control[2] = {NULL, NULL};
3241 pldap_control[0] = &ldap_control;
3242 memset(&ldap_control, 0, sizeof(LDAPControl));
3243 ldap_control.ldctl_oid = (char *)LDAP_SERVER_TREE_DELETE_OID;
3245 /* hostname must be lowercase */
3246 host = SMB_STRDUP(hostname);
3249 status = ads_find_machine_acct(ads, &res, host);
3250 if (!ADS_ERR_OK(status)) {
3251 DEBUG(0, ("Host account for %s does not exist.\n", host));
3256 msg = ads_first_entry(ads, res);
3259 return ADS_ERROR_SYSTEM(ENOENT);
3262 hostnameDN = ads_get_dn(ads, (LDAPMessage *)msg);
3264 rc = ldap_delete_ext_s(ads->ldap.ld, hostnameDN, pldap_control, NULL);
3266 DEBUG(3,("ldap_delete_ext_s failed with error code %d\n", rc));
3268 DEBUG(3,("ldap_delete_ext_s succeeded with error code %d\n", rc));
3271 if (rc != LDAP_SUCCESS) {
3272 const char *attrs[] = { "cn", NULL };
3273 LDAPMessage *msg_sub;
3275 /* we only search with scope ONE, we do not expect any further
3276 * objects to be created deeper */
3278 status = ads_do_search_retry(ads, hostnameDN,
3279 LDAP_SCOPE_ONELEVEL,
3280 "(objectclass=*)", attrs, &res);
3282 if (!ADS_ERR_OK(status)) {
3284 ads_memfree(ads, hostnameDN);
3288 for (msg_sub = ads_first_entry(ads, res); msg_sub;
3289 msg_sub = ads_next_entry(ads, msg_sub)) {
3293 if ((dn = ads_get_dn(ads, msg_sub)) == NULL) {
3295 ads_memfree(ads, hostnameDN);
3296 return ADS_ERROR(LDAP_NO_MEMORY);
3299 status = ads_del_dn(ads, dn);
3300 if (!ADS_ERR_OK(status)) {
3301 DEBUG(3,("failed to delete dn %s: %s\n", dn, ads_errstr(status)));
3303 ads_memfree(ads, dn);
3304 ads_memfree(ads, hostnameDN);
3308 ads_memfree(ads, dn);
3311 /* there should be no subordinate objects anymore */
3312 status = ads_do_search_retry(ads, hostnameDN,
3313 LDAP_SCOPE_ONELEVEL,
3314 "(objectclass=*)", attrs, &res);
3316 if (!ADS_ERR_OK(status) || ( (ads_count_replies(ads, res)) > 0 ) ) {
3318 ads_memfree(ads, hostnameDN);
3322 /* delete hostnameDN now */
3323 status = ads_del_dn(ads, hostnameDN);
3324 if (!ADS_ERR_OK(status)) {
3326 DEBUG(3,("failed to delete dn %s: %s\n", hostnameDN, ads_errstr(status)));
3327 ads_memfree(ads, hostnameDN);
3332 ads_memfree(ads, hostnameDN);
3334 status = ads_find_machine_acct(ads, &res, host);
3335 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
3336 DEBUG(3, ("Failed to remove host account.\n"));
3346 * pull all token-sids from an LDAP dn
3347 * @param ads connection to ads server
3348 * @param mem_ctx TALLOC_CTX for allocating sid array
3349 * @param dn of LDAP object
3350 * @param user_sid pointer to DOM_SID (objectSid)
3351 * @param primary_group_sid pointer to DOM_SID (self composed)
3352 * @param sids pointer to sid array to allocate
3353 * @param num_sids counter of SIDs pulled
3354 * @return status of token query
3356 ADS_STATUS ads_get_tokensids(ADS_STRUCT *ads,
3357 TALLOC_CTX *mem_ctx,
3360 DOM_SID *primary_group_sid,
3365 LDAPMessage *res = NULL;
3367 size_t tmp_num_sids;
3369 DOM_SID tmp_user_sid;
3370 DOM_SID tmp_primary_group_sid;
3372 const char *attrs[] = {
3379 status = ads_search_retry_dn(ads, &res, dn, attrs);
3380 if (!ADS_ERR_OK(status)) {
3384 count = ads_count_replies(ads, res);
3386 ads_msgfree(ads, res);
3387 return ADS_ERROR_LDAP(LDAP_NO_SUCH_OBJECT);
3390 if (!ads_pull_sid(ads, res, "objectSid", &tmp_user_sid)) {
3391 ads_msgfree(ads, res);
3392 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3395 if (!ads_pull_uint32(ads, res, "primaryGroupID", &pgid)) {
3396 ads_msgfree(ads, res);
3397 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3401 /* hack to compose the primary group sid without knowing the
3407 sid_copy(&domsid, &tmp_user_sid);
3409 if (!sid_split_rid(&domsid, &dummy_rid)) {
3410 ads_msgfree(ads, res);
3411 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3414 if (!sid_compose(&tmp_primary_group_sid, &domsid, pgid)) {
3415 ads_msgfree(ads, res);
3416 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3420 tmp_num_sids = ads_pull_sids(ads, mem_ctx, res, "tokenGroups", &tmp_sids);
3422 if (tmp_num_sids == 0 || !tmp_sids) {
3423 ads_msgfree(ads, res);
3424 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3428 *num_sids = tmp_num_sids;
3436 *user_sid = tmp_user_sid;
3439 if (primary_group_sid) {
3440 *primary_group_sid = tmp_primary_group_sid;
3443 DEBUG(10,("ads_get_tokensids: returned %d sids\n", (int)tmp_num_sids + 2));
3445 ads_msgfree(ads, res);
3446 return ADS_ERROR_LDAP(LDAP_SUCCESS);
3450 * Find a sAMAccoutName in LDAP
3451 * @param ads connection to ads server
3452 * @param mem_ctx TALLOC_CTX for allocating sid array
3453 * @param samaccountname to search
3454 * @param uac_ret uint32 pointer userAccountControl attribute value
3455 * @param dn_ret pointer to dn
3456 * @return status of token query
3458 ADS_STATUS ads_find_samaccount(ADS_STRUCT *ads,
3459 TALLOC_CTX *mem_ctx,
3460 const char *samaccountname,
3462 const char **dn_ret)
3465 const char *attrs[] = { "userAccountControl", NULL };
3467 LDAPMessage *res = NULL;
3471 filter = talloc_asprintf(mem_ctx, "(&(objectclass=user)(sAMAccountName=%s))",
3473 if (filter == NULL) {
3474 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
3478 status = ads_do_search_all(ads, ads->config.bind_path,
3480 filter, attrs, &res);
3482 if (!ADS_ERR_OK(status)) {
3486 if (ads_count_replies(ads, res) != 1) {
3487 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3491 dn = ads_get_dn(ads, res);
3493 status = ADS_ERROR(LDAP_NO_MEMORY);
3497 if (!ads_pull_uint32(ads, res, "userAccountControl", &uac)) {
3498 status = ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
3507 *dn_ret = talloc_strdup(mem_ctx, dn);
3509 status = ADS_ERROR(LDAP_NO_MEMORY);
3514 ads_memfree(ads, dn);
3515 ads_msgfree(ads, res);
3521 * find our configuration path
3522 * @param ads connection to ads server
3523 * @param mem_ctx Pointer to talloc context
3524 * @param config_path Pointer to the config path
3525 * @return status of search
3527 ADS_STATUS ads_config_path(ADS_STRUCT *ads,
3528 TALLOC_CTX *mem_ctx,
3532 LDAPMessage *res = NULL;
3533 const char *config_context = NULL;
3534 const char *attrs[] = { "configurationNamingContext", NULL };
3536 status = ads_do_search(ads, "", LDAP_SCOPE_BASE,
3537 "(objectclass=*)", attrs, &res);
3538 if (!ADS_ERR_OK(status)) {
3542 config_context = ads_pull_string(ads, mem_ctx, res,
3543 "configurationNamingContext");
3544 ads_msgfree(ads, res);
3545 if (!config_context) {
3546 return ADS_ERROR(LDAP_NO_MEMORY);
3550 *config_path = talloc_strdup(mem_ctx, config_context);
3551 if (!*config_path) {
3552 return ADS_ERROR(LDAP_NO_MEMORY);
3556 return ADS_ERROR(LDAP_SUCCESS);
3560 * find the displayName of an extended right
3561 * @param ads connection to ads server
3562 * @param config_path The config path
3563 * @param mem_ctx Pointer to talloc context
3564 * @param GUID struct of the rightsGUID
3565 * @return status of search
3567 const char *ads_get_extended_right_name_by_guid(ADS_STRUCT *ads,
3568 const char *config_path,
3569 TALLOC_CTX *mem_ctx,
3570 const struct GUID *rights_guid)
3573 LDAPMessage *res = NULL;
3575 const char *attrs[] = { "displayName", NULL };
3576 const char *result = NULL;
3579 if (!ads || !mem_ctx || !rights_guid) {
3583 expr = talloc_asprintf(mem_ctx, "(rightsGuid=%s)",
3584 smb_uuid_string(mem_ctx, *rights_guid));
3589 path = talloc_asprintf(mem_ctx, "cn=Extended-Rights,%s", config_path);
3594 rc = ads_do_search_retry(ads, path, LDAP_SCOPE_SUBTREE,
3596 if (!ADS_ERR_OK(rc)) {
3600 if (ads_count_replies(ads, res) != 1) {
3604 result = ads_pull_string(ads, mem_ctx, res, "displayName");
3607 ads_msgfree(ads, res);
3613 * verify or build and verify an account ou
3614 * @param mem_ctx Pointer to talloc context
3615 * @param ads connection to ads server
3617 * @return status of search
3620 ADS_STATUS ads_check_ou_dn(TALLOC_CTX *mem_ctx,
3622 const char **account_ou)
3624 struct ldb_dn *name_dn = NULL;
3625 const char *name = NULL;
3626 char *ou_string = NULL;
3628 name_dn = ldb_dn_explode(mem_ctx, *account_ou);
3633 ou_string = ads_ou_string(ads, *account_ou);
3635 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX);
3638 name = talloc_asprintf(mem_ctx, "%s,%s", ou_string,
3639 ads->config.bind_path);
3640 SAFE_FREE(ou_string);
3642 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3645 name_dn = ldb_dn_explode(mem_ctx, name);
3647 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX);
3650 *account_ou = talloc_strdup(mem_ctx, name);
3652 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);