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/>.
30 * @brief basic ldap client-side routines for ads server communications
32 * The routines contained here should do the necessary ldap calls for
35 * Important note: attribute names passed into ads_ routines must
36 * already be in UTF-8 format. We do not convert them because in almost
37 * all cases, they are just ascii (which is represented with the same
38 * codepoints in UTF-8). This may have to change at some point
42 #define LDAP_SERVER_TREE_DELETE_OID "1.2.840.113556.1.4.805"
44 static SIG_ATOMIC_T gotalarm;
46 /***************************************************************
47 Signal function to tell us we timed out.
48 ****************************************************************/
50 static void gotalarm_sig(void)
55 LDAP *ldap_open_with_timeout(const char *server, int port, unsigned int to)
61 CatchSignal(SIGALRM, SIGNAL_CAST gotalarm_sig);
63 /* End setup timeout. */
65 ldp = ldap_open(server, port);
68 DEBUG(2,("Could not open LDAP connection to %s:%d: %s\n",
69 server, port, strerror(errno)));
72 /* Teardown timeout. */
73 CatchSignal(SIGALRM, SIGNAL_CAST SIG_IGN);
79 static int ldap_search_with_timeout(LDAP *ld,
80 LDAP_CONST char *base,
82 LDAP_CONST char *filter,
90 struct timeval timeout;
93 /* Setup timeout for the ldap_search_ext_s call - local and remote. */
94 timeout.tv_sec = lp_ldap_timeout();
97 /* Setup alarm timeout.... Do we need both of these ? JRA. */
99 CatchSignal(SIGALRM, SIGNAL_CAST gotalarm_sig);
100 alarm(lp_ldap_timeout());
101 /* End setup timeout. */
103 result = ldap_search_ext_s(ld, base, scope, filter, attrs,
104 attrsonly, sctrls, cctrls, &timeout,
107 /* Teardown timeout. */
108 CatchSignal(SIGALRM, SIGNAL_CAST SIG_IGN);
112 return LDAP_TIMELIMIT_EXCEEDED;
117 /**********************************************
118 Do client and server sitename match ?
119 **********************************************/
121 BOOL ads_sitename_match(ADS_STRUCT *ads)
123 if (ads->config.server_site_name == NULL &&
124 ads->config.client_site_name == NULL ) {
125 DEBUG(10,("ads_sitename_match: both null\n"));
128 if (ads->config.server_site_name &&
129 ads->config.client_site_name &&
130 strequal(ads->config.server_site_name,
131 ads->config.client_site_name)) {
132 DEBUG(10,("ads_sitename_match: name %s match\n", ads->config.server_site_name));
135 DEBUG(10,("ads_sitename_match: no match between server: %s and client: %s\n",
136 ads->config.server_site_name ? ads->config.server_site_name : "NULL",
137 ads->config.client_site_name ? ads->config.client_site_name : "NULL"));
141 /**********************************************
142 Is this the closest DC ?
143 **********************************************/
145 BOOL ads_closest_dc(ADS_STRUCT *ads)
147 if (ads->config.flags & ADS_CLOSEST) {
148 DEBUG(10,("ads_closest_dc: ADS_CLOSEST flag set\n"));
152 /* not sure if this can ever happen */
153 if (ads_sitename_match(ads)) {
154 DEBUG(10,("ads_closest_dc: ADS_CLOSEST flag not set but sites match\n"));
158 DEBUG(10,("ads_closest_dc: %s is not the closest DC\n",
159 ads->config.ldap_server_name));
166 try a connection to a given ldap server, returning True and setting the servers IP
167 in the ads struct if successful
169 BOOL ads_try_connect(ADS_STRUCT *ads, const char *server )
172 struct cldap_netlogon_reply cldap_reply;
174 if (!server || !*server) {
178 DEBUG(5,("ads_try_connect: sending CLDAP request to %s (realm: %s)\n",
179 server, ads->server.realm));
181 /* this copes with inet_ntoa brokenness */
183 srv = SMB_STRDUP(server);
185 ZERO_STRUCT( cldap_reply );
187 if ( !ads_cldap_netlogon( srv, ads->server.realm, &cldap_reply ) ) {
188 DEBUG(3,("ads_try_connect: CLDAP request %s failed.\n", srv));
193 /* Check the CLDAP reply flags */
195 if ( !(cldap_reply.flags & ADS_LDAP) ) {
196 DEBUG(1,("ads_try_connect: %s's CLDAP reply says it is not an LDAP server!\n",
202 /* Fill in the ads->config values */
204 SAFE_FREE(ads->config.realm);
205 SAFE_FREE(ads->config.bind_path);
206 SAFE_FREE(ads->config.ldap_server_name);
207 SAFE_FREE(ads->config.server_site_name);
208 SAFE_FREE(ads->config.client_site_name);
209 SAFE_FREE(ads->server.workgroup);
211 ads->config.flags = cldap_reply.flags;
212 ads->config.ldap_server_name = SMB_STRDUP(cldap_reply.hostname);
213 strupper_m(cldap_reply.domain);
214 ads->config.realm = SMB_STRDUP(cldap_reply.domain);
215 ads->config.bind_path = ads_build_dn(ads->config.realm);
216 if (*cldap_reply.server_site_name) {
217 ads->config.server_site_name =
218 SMB_STRDUP(cldap_reply.server_site_name);
220 if (*cldap_reply.client_site_name) {
221 ads->config.client_site_name =
222 SMB_STRDUP(cldap_reply.client_site_name);
225 ads->server.workgroup = SMB_STRDUP(cldap_reply.netbios_domain);
227 ads->ldap_port = LDAP_PORT;
228 ads->ldap_ip = *interpret_addr2(srv);
231 /* Store our site name. */
232 sitename_store( cldap_reply.domain, cldap_reply.client_site_name );
237 /**********************************************************************
238 Try to find an AD dc using our internal name resolution routines
239 Try the realm first and then then workgroup name if netbios is not
241 **********************************************************************/
243 static NTSTATUS ads_find_dc(ADS_STRUCT *ads)
247 struct ip_service *ip_list;
249 BOOL got_realm = False;
250 BOOL use_own_domain = False;
252 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
254 /* if the realm and workgroup are both empty, assume they are ours */
257 c_realm = ads->server.realm;
259 if ( !c_realm || !*c_realm ) {
260 /* special case where no realm and no workgroup means our own */
261 if ( !ads->server.workgroup || !*ads->server.workgroup ) {
262 use_own_domain = True;
263 c_realm = lp_realm();
267 if (c_realm && *c_realm)
270 /* we need to try once with the realm name and fallback to the
271 netbios domain name if we fail (if netbios has not been disabled */
273 if ( !got_realm && !lp_disable_netbios() ) {
274 c_realm = ads->server.workgroup;
275 if (!c_realm || !*c_realm) {
276 if ( use_own_domain )
277 c_realm = lp_workgroup();
280 if ( !c_realm || !*c_realm ) {
281 DEBUG(0,("ads_find_dc: no realm or workgroup! Don't know what to do\n"));
282 return NT_STATUS_INVALID_PARAMETER; /* rather need MISSING_PARAMETER ... */
286 pstrcpy( realm, c_realm );
288 sitename = sitename_fetch(realm);
292 DEBUG(6,("ads_find_dc: looking for %s '%s'\n",
293 (got_realm ? "realm" : "domain"), realm));
295 status = get_sorted_dc_list(realm, sitename, &ip_list, &count, got_realm);
296 if (!NT_STATUS_IS_OK(status)) {
297 /* fall back to netbios if we can */
298 if ( got_realm && !lp_disable_netbios() ) {
307 /* if we fail this loop, then giveup since all the IP addresses returned were dead */
308 for ( i=0; i<count; i++ ) {
311 fstrcpy( server, inet_ntoa(ip_list[i].ip) );
313 if ( !NT_STATUS_IS_OK(check_negative_conn_cache(realm, server)) )
317 /* realm in this case is a workgroup name. We need
318 to ignore any IP addresses in the negative connection
319 cache that match ip addresses returned in the ad realm
320 case. It sucks that I have to reproduce the logic above... */
321 c_realm = ads->server.realm;
322 if ( !c_realm || !*c_realm ) {
323 if ( !ads->server.workgroup || !*ads->server.workgroup ) {
324 c_realm = lp_realm();
327 if (c_realm && *c_realm &&
328 !NT_STATUS_IS_OK(check_negative_conn_cache(c_realm, server))) {
329 /* Ensure we add the workgroup name for this
330 IP address as negative too. */
331 add_failed_connection_entry( realm, server, NT_STATUS_UNSUCCESSFUL );
336 if ( ads_try_connect(ads, server) ) {
342 /* keep track of failures */
343 add_failed_connection_entry( realm, server, NT_STATUS_UNSUCCESSFUL );
348 /* In case we failed to contact one of our closest DC on our site we
349 * need to try to find another DC, retry with a site-less SRV DNS query
353 DEBUG(1,("ads_find_dc: failed to find a valid DC on our site (%s), "
354 "trying to find another DC\n", sitename));
356 namecache_delete(realm, 0x1C);
360 return NT_STATUS_NO_LOGON_SERVERS;
365 * Connect to the LDAP server
366 * @param ads Pointer to an existing ADS_STRUCT
367 * @return status of connection
369 ADS_STATUS ads_connect(ADS_STRUCT *ads)
371 int version = LDAP_VERSION3;
375 ads->last_attempt = time(NULL);
378 /* try with a user specified server */
380 if (ads->server.ldap_server &&
381 ads_try_connect(ads, ads->server.ldap_server)) {
385 ntstatus = ads_find_dc(ads);
386 if (NT_STATUS_IS_OK(ntstatus)) {
390 return ADS_ERROR_NT(ntstatus);
393 DEBUG(3,("Connected to LDAP server %s\n", inet_ntoa(ads->ldap_ip)));
395 if (!ads->auth.user_name) {
396 /* Must use the userPrincipalName value here or sAMAccountName
397 and not servicePrincipalName; found by Guenther Deschner */
399 asprintf(&ads->auth.user_name, "%s$", global_myname() );
402 if (!ads->auth.realm) {
403 ads->auth.realm = SMB_STRDUP(ads->config.realm);
406 if (!ads->auth.kdc_server) {
407 ads->auth.kdc_server = SMB_STRDUP(inet_ntoa(ads->ldap_ip));
411 /* this is a really nasty hack to avoid ADS DNS problems. It needs a patch
412 to MIT kerberos to work (tridge) */
415 asprintf(&env, "KRB5_KDC_ADDRESS_%s", ads->config.realm);
416 setenv(env, ads->auth.kdc_server, 1);
421 /* If the caller() requested no LDAP bind, then we are done */
423 if (ads->auth.flags & ADS_AUTH_NO_BIND) {
427 /* Otherwise setup the TCP LDAP session */
429 if ( (ads->ld = ldap_open_with_timeout(ads->config.ldap_server_name,
430 LDAP_PORT, lp_ldap_timeout())) == NULL )
432 return ADS_ERROR(LDAP_OPERATIONS_ERROR);
435 /* cache the successful connection for workgroup and realm */
436 if (ads_closest_dc(ads)) {
437 saf_store( ads->server.workgroup, inet_ntoa(ads->ldap_ip));
438 saf_store( ads->server.realm, inet_ntoa(ads->ldap_ip));
441 ldap_set_option(ads->ld, LDAP_OPT_PROTOCOL_VERSION, &version);
443 status = ADS_ERROR(smb_ldap_start_tls(ads->ld, version));
444 if (!ADS_ERR_OK(status)) {
448 /* fill in the current time and offsets */
450 status = ads_current_time( ads );
451 if ( !ADS_ERR_OK(status) ) {
455 /* Now do the bind */
457 if (ads->auth.flags & ADS_AUTH_ANON_BIND) {
458 return ADS_ERROR(ldap_simple_bind_s( ads->ld, NULL, NULL));
461 if (ads->auth.flags & ADS_AUTH_SIMPLE_BIND) {
462 return ADS_ERROR(ldap_simple_bind_s( ads->ld, ads->auth.user_name, ads->auth.password));
465 return ads_sasl_bind(ads);
469 Duplicate a struct berval into talloc'ed memory
471 static struct berval *dup_berval(TALLOC_CTX *ctx, const struct berval *in_val)
473 struct berval *value;
475 if (!in_val) return NULL;
477 value = TALLOC_ZERO_P(ctx, struct berval);
480 if (in_val->bv_len == 0) return value;
482 value->bv_len = in_val->bv_len;
483 value->bv_val = (char *)TALLOC_MEMDUP(ctx, in_val->bv_val,
489 Make a values list out of an array of (struct berval *)
491 static struct berval **ads_dup_values(TALLOC_CTX *ctx,
492 const struct berval **in_vals)
494 struct berval **values;
497 if (!in_vals) return NULL;
498 for (i=0; in_vals[i]; i++)
500 values = TALLOC_ZERO_ARRAY(ctx, struct berval *, i+1);
501 if (!values) return NULL;
503 for (i=0; in_vals[i]; i++) {
504 values[i] = dup_berval(ctx, in_vals[i]);
510 UTF8-encode a values list out of an array of (char *)
512 static char **ads_push_strvals(TALLOC_CTX *ctx, const char **in_vals)
517 if (!in_vals) return NULL;
518 for (i=0; in_vals[i]; i++)
520 values = TALLOC_ZERO_ARRAY(ctx, char *, i+1);
521 if (!values) return NULL;
523 for (i=0; in_vals[i]; i++) {
524 push_utf8_talloc(ctx, &values[i], in_vals[i]);
530 Pull a (char *) array out of a UTF8-encoded values list
532 static char **ads_pull_strvals(TALLOC_CTX *ctx, const char **in_vals)
537 if (!in_vals) return NULL;
538 for (i=0; in_vals[i]; i++)
540 values = TALLOC_ZERO_ARRAY(ctx, char *, i+1);
541 if (!values) return NULL;
543 for (i=0; in_vals[i]; i++) {
544 pull_utf8_talloc(ctx, &values[i], in_vals[i]);
550 * Do a search with paged results. cookie must be null on the first
551 * call, and then returned on each subsequent call. It will be null
552 * again when the entire search is complete
553 * @param ads connection to ads server
554 * @param bind_path Base dn for the search
555 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
556 * @param expr Search expression - specified in local charset
557 * @param attrs Attributes to retrieve - specified in utf8 or ascii
558 * @param res ** which will contain results - free res* with ads_msgfree()
559 * @param count Number of entries retrieved on this page
560 * @param cookie The paged results cookie to be returned on subsequent calls
561 * @return status of search
563 static ADS_STATUS ads_do_paged_search_args(ADS_STRUCT *ads,
564 const char *bind_path,
565 int scope, const char *expr,
566 const char **attrs, void *args,
568 int *count, struct berval **cookie)
571 char *utf8_expr, *utf8_path, **search_attrs;
572 LDAPControl PagedResults, NoReferrals, ExternalCtrl, *controls[4], **rcontrols;
573 BerElement *cookie_be = NULL;
574 struct berval *cookie_bv= NULL;
575 BerElement *ext_be = NULL;
576 struct berval *ext_bv= NULL;
579 ads_control *external_control = (ads_control *) args;
583 if (!(ctx = talloc_init("ads_do_paged_search_args")))
584 return ADS_ERROR(LDAP_NO_MEMORY);
586 /* 0 means the conversion worked but the result was empty
587 so we only fail if it's -1. In any case, it always
588 at least nulls out the dest */
589 if ((push_utf8_talloc(ctx, &utf8_expr, expr) == (size_t)-1) ||
590 (push_utf8_talloc(ctx, &utf8_path, bind_path) == (size_t)-1)) {
595 if (!attrs || !(*attrs))
598 /* This would be the utf8-encoded version...*/
599 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
600 if (!(str_list_copy(&search_attrs, attrs))) {
606 /* Paged results only available on ldap v3 or later */
607 ldap_get_option(ads->ld, LDAP_OPT_PROTOCOL_VERSION, &version);
608 if (version < LDAP_VERSION3) {
609 rc = LDAP_NOT_SUPPORTED;
613 cookie_be = ber_alloc_t(LBER_USE_DER);
615 ber_printf(cookie_be, "{iO}", (ber_int_t) 1000, *cookie);
616 ber_bvfree(*cookie); /* don't need it from last time */
619 ber_printf(cookie_be, "{io}", (ber_int_t) 1000, "", 0);
621 ber_flatten(cookie_be, &cookie_bv);
622 PagedResults.ldctl_oid = CONST_DISCARD(char *, ADS_PAGE_CTL_OID);
623 PagedResults.ldctl_iscritical = (char) 1;
624 PagedResults.ldctl_value.bv_len = cookie_bv->bv_len;
625 PagedResults.ldctl_value.bv_val = cookie_bv->bv_val;
627 NoReferrals.ldctl_oid = CONST_DISCARD(char *, ADS_NO_REFERRALS_OID);
628 NoReferrals.ldctl_iscritical = (char) 0;
629 NoReferrals.ldctl_value.bv_len = 0;
630 NoReferrals.ldctl_value.bv_val = CONST_DISCARD(char *, "");
632 if (external_control &&
633 (strequal(external_control->control, ADS_EXTENDED_DN_OID) ||
634 strequal(external_control->control, ADS_SD_FLAGS_OID))) {
636 ExternalCtrl.ldctl_oid = CONST_DISCARD(char *, external_control->control);
637 ExternalCtrl.ldctl_iscritical = (char) external_control->critical;
639 /* win2k does not accept a ldctl_value beeing passed in */
641 if (external_control->val != 0) {
643 if ((ext_be = ber_alloc_t(LBER_USE_DER)) == NULL ) {
648 if ((ber_printf(ext_be, "{i}", (ber_int_t) external_control->val)) == -1) {
652 if ((ber_flatten(ext_be, &ext_bv)) == -1) {
657 ExternalCtrl.ldctl_value.bv_len = ext_bv->bv_len;
658 ExternalCtrl.ldctl_value.bv_val = ext_bv->bv_val;
661 ExternalCtrl.ldctl_value.bv_len = 0;
662 ExternalCtrl.ldctl_value.bv_val = NULL;
665 controls[0] = &NoReferrals;
666 controls[1] = &PagedResults;
667 controls[2] = &ExternalCtrl;
671 controls[0] = &NoReferrals;
672 controls[1] = &PagedResults;
676 /* we need to disable referrals as the openldap libs don't
677 handle them and paged results at the same time. Using them
678 together results in the result record containing the server
679 page control being removed from the result list (tridge/jmcd)
681 leaving this in despite the control that says don't generate
682 referrals, in case the server doesn't support it (jmcd)
684 ldap_set_option(ads->ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
686 rc = ldap_search_with_timeout(ads->ld, utf8_path, scope, utf8_expr,
687 search_attrs, 0, controls,
689 (LDAPMessage **)res);
691 ber_free(cookie_be, 1);
692 ber_bvfree(cookie_bv);
695 DEBUG(3,("ads_do_paged_search_args: ldap_search_with_timeout(%s) -> %s\n", expr,
696 ldap_err2string(rc)));
700 rc = ldap_parse_result(ads->ld, *res, NULL, NULL, NULL,
701 NULL, &rcontrols, 0);
707 for (i=0; rcontrols[i]; i++) {
708 if (strcmp(ADS_PAGE_CTL_OID, rcontrols[i]->ldctl_oid) == 0) {
709 cookie_be = ber_init(&rcontrols[i]->ldctl_value);
710 ber_scanf(cookie_be,"{iO}", (ber_int_t *) count,
712 /* the berval is the cookie, but must be freed when
714 if (cookie_bv->bv_len) /* still more to do */
715 *cookie=ber_bvdup(cookie_bv);
718 ber_bvfree(cookie_bv);
719 ber_free(cookie_be, 1);
723 ldap_controls_free(rcontrols);
736 /* if/when we decide to utf8-encode attrs, take out this next line */
737 str_list_free(&search_attrs);
739 return ADS_ERROR(rc);
742 static ADS_STATUS ads_do_paged_search(ADS_STRUCT *ads, const char *bind_path,
743 int scope, const char *expr,
744 const char **attrs, LDAPMessage **res,
745 int *count, struct berval **cookie)
747 return ads_do_paged_search_args(ads, bind_path, scope, expr, attrs, NULL, res, count, cookie);
752 * Get all results for a search. This uses ads_do_paged_search() to return
753 * all entries in a large search.
754 * @param ads connection to ads server
755 * @param bind_path Base dn for the search
756 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
757 * @param expr Search expression
758 * @param attrs Attributes to retrieve
759 * @param res ** which will contain results - free res* with ads_msgfree()
760 * @return status of search
762 ADS_STATUS ads_do_search_all_args(ADS_STRUCT *ads, const char *bind_path,
763 int scope, const char *expr,
764 const char **attrs, void *args,
767 struct berval *cookie = NULL;
772 status = ads_do_paged_search_args(ads, bind_path, scope, expr, attrs, args, res,
775 if (!ADS_ERR_OK(status))
778 #ifdef HAVE_LDAP_ADD_RESULT_ENTRY
780 LDAPMessage *res2 = NULL;
782 LDAPMessage *msg, *next;
784 status2 = ads_do_paged_search_args(ads, bind_path, scope, expr,
785 attrs, args, &res2, &count, &cookie);
787 if (!ADS_ERR_OK(status2)) break;
789 /* this relies on the way that ldap_add_result_entry() works internally. I hope
790 that this works on all ldap libs, but I have only tested with openldap */
791 for (msg = ads_first_entry(ads, res2); msg; msg = next) {
792 next = ads_next_entry(ads, msg);
793 ldap_add_result_entry((LDAPMessage **)res, msg);
795 /* note that we do not free res2, as the memory is now
796 part of the main returned list */
799 DEBUG(0, ("no ldap_add_result_entry() support in LDAP libs!\n"));
800 status = ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
806 ADS_STATUS ads_do_search_all(ADS_STRUCT *ads, const char *bind_path,
807 int scope, const char *expr,
808 const char **attrs, LDAPMessage **res)
810 return ads_do_search_all_args(ads, bind_path, scope, expr, attrs, NULL, res);
813 ADS_STATUS ads_do_search_all_sd_flags(ADS_STRUCT *ads, const char *bind_path,
814 int scope, const char *expr,
815 const char **attrs, uint32 sd_flags,
820 args.control = ADS_SD_FLAGS_OID;
822 args.critical = True;
824 return ads_do_search_all_args(ads, bind_path, scope, expr, attrs, &args, res);
829 * Run a function on all results for a search. Uses ads_do_paged_search() and
830 * runs the function as each page is returned, using ads_process_results()
831 * @param ads connection to ads server
832 * @param bind_path Base dn for the search
833 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
834 * @param expr Search expression - specified in local charset
835 * @param attrs Attributes to retrieve - specified in UTF-8 or ascii
836 * @param fn Function which takes attr name, values list, and data_area
837 * @param data_area Pointer which is passed to function on each call
838 * @return status of search
840 ADS_STATUS ads_do_search_all_fn(ADS_STRUCT *ads, const char *bind_path,
841 int scope, const char *expr, const char **attrs,
842 BOOL(*fn)(char *, void **, void *),
845 struct berval *cookie = NULL;
850 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs, &res,
853 if (!ADS_ERR_OK(status)) return status;
855 ads_process_results(ads, res, fn, data_area);
856 ads_msgfree(ads, res);
859 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs,
860 &res, &count, &cookie);
862 if (!ADS_ERR_OK(status)) break;
864 ads_process_results(ads, res, fn, data_area);
865 ads_msgfree(ads, res);
872 * Do a search with a timeout.
873 * @param ads connection to ads server
874 * @param bind_path Base dn for the search
875 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
876 * @param expr Search expression
877 * @param attrs Attributes to retrieve
878 * @param res ** which will contain results - free res* with ads_msgfree()
879 * @return status of search
881 ADS_STATUS ads_do_search(ADS_STRUCT *ads, const char *bind_path, int scope,
883 const char **attrs, LDAPMessage **res)
886 char *utf8_expr, *utf8_path, **search_attrs = NULL;
890 if (!(ctx = talloc_init("ads_do_search"))) {
891 DEBUG(1,("ads_do_search: talloc_init() failed!"));
892 return ADS_ERROR(LDAP_NO_MEMORY);
895 /* 0 means the conversion worked but the result was empty
896 so we only fail if it's negative. In any case, it always
897 at least nulls out the dest */
898 if ((push_utf8_talloc(ctx, &utf8_expr, expr) == (size_t)-1) ||
899 (push_utf8_talloc(ctx, &utf8_path, bind_path) == (size_t)-1)) {
900 DEBUG(1,("ads_do_search: push_utf8_talloc() failed!"));
905 if (!attrs || !(*attrs))
908 /* This would be the utf8-encoded version...*/
909 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
910 if (!(str_list_copy(&search_attrs, attrs)))
912 DEBUG(1,("ads_do_search: str_list_copy() failed!"));
918 /* see the note in ads_do_paged_search - we *must* disable referrals */
919 ldap_set_option(ads->ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
921 rc = ldap_search_with_timeout(ads->ld, utf8_path, scope, utf8_expr,
922 search_attrs, 0, NULL, NULL,
924 (LDAPMessage **)res);
926 if (rc == LDAP_SIZELIMIT_EXCEEDED) {
927 DEBUG(3,("Warning! sizelimit exceeded in ldap. Truncating.\n"));
933 /* if/when we decide to utf8-encode attrs, take out this next line */
934 str_list_free(&search_attrs);
935 return ADS_ERROR(rc);
938 * Do a general ADS search
939 * @param ads connection to ads server
940 * @param res ** which will contain results - free res* with ads_msgfree()
941 * @param expr Search expression
942 * @param attrs Attributes to retrieve
943 * @return status of search
945 ADS_STATUS ads_search(ADS_STRUCT *ads, LDAPMessage **res,
946 const char *expr, const char **attrs)
948 return ads_do_search(ads, ads->config.bind_path, LDAP_SCOPE_SUBTREE,
953 * Do a search on a specific DistinguishedName
954 * @param ads connection to ads server
955 * @param res ** which will contain results - free res* with ads_msgfree()
956 * @param dn DistinguishName to search
957 * @param attrs Attributes to retrieve
958 * @return status of search
960 ADS_STATUS ads_search_dn(ADS_STRUCT *ads, LDAPMessage **res,
961 const char *dn, const char **attrs)
963 return ads_do_search(ads, dn, LDAP_SCOPE_BASE, "(objectclass=*)",
968 * Free up memory from a ads_search
969 * @param ads connection to ads server
970 * @param msg Search results to free
972 void ads_msgfree(ADS_STRUCT *ads, LDAPMessage *msg)
979 * Free up memory from various ads requests
980 * @param ads connection to ads server
981 * @param mem Area to free
983 void ads_memfree(ADS_STRUCT *ads, void *mem)
989 * Get a dn from search results
990 * @param ads connection to ads server
991 * @param msg Search result
994 char *ads_get_dn(ADS_STRUCT *ads, LDAPMessage *msg)
996 char *utf8_dn, *unix_dn;
998 utf8_dn = ldap_get_dn(ads->ld, msg);
1001 DEBUG (5, ("ads_get_dn: ldap_get_dn failed\n"));
1005 if (pull_utf8_allocate(&unix_dn, utf8_dn) == (size_t)-1) {
1006 DEBUG(0,("ads_get_dn: string conversion failure utf8 [%s]\n",
1010 ldap_memfree(utf8_dn);
1015 * Get the parent from a dn
1016 * @param dn the dn to return the parent from
1017 * @return parent dn string
1019 char *ads_parent_dn(const char *dn)
1027 p = strchr(dn, ',');
1037 * Find a machine account given a hostname
1038 * @param ads connection to ads server
1039 * @param res ** which will contain results - free res* with ads_msgfree()
1040 * @param host Hostname to search for
1041 * @return status of search
1043 ADS_STATUS ads_find_machine_acct(ADS_STRUCT *ads, LDAPMessage **res,
1044 const char *machine)
1048 const char *attrs[] = {"*", "nTSecurityDescriptor", NULL};
1052 /* the easiest way to find a machine account anywhere in the tree
1053 is to look for hostname$ */
1054 if (asprintf(&expr, "(samAccountName=%s$)", machine) == -1) {
1055 DEBUG(1, ("asprintf failed!\n"));
1056 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1059 status = ads_search(ads, res, expr, attrs);
1065 * Initialize a list of mods to be used in a modify request
1066 * @param ctx An initialized TALLOC_CTX
1067 * @return allocated ADS_MODLIST
1069 ADS_MODLIST ads_init_mods(TALLOC_CTX *ctx)
1071 #define ADS_MODLIST_ALLOC_SIZE 10
1074 if ((mods = TALLOC_ZERO_ARRAY(ctx, LDAPMod *, ADS_MODLIST_ALLOC_SIZE + 1)))
1075 /* -1 is safety to make sure we don't go over the end.
1076 need to reset it to NULL before doing ldap modify */
1077 mods[ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1079 return (ADS_MODLIST)mods;
1084 add an attribute to the list, with values list already constructed
1086 static ADS_STATUS ads_modlist_add(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1087 int mod_op, const char *name,
1088 const void *_invals)
1090 const void **invals = (const void **)_invals;
1092 LDAPMod **modlist = (LDAPMod **) *mods;
1093 struct berval **ber_values = NULL;
1094 char **char_values = NULL;
1097 mod_op = LDAP_MOD_DELETE;
1099 if (mod_op & LDAP_MOD_BVALUES)
1100 ber_values = ads_dup_values(ctx,
1101 (const struct berval **)invals);
1103 char_values = ads_push_strvals(ctx,
1104 (const char **) invals);
1107 /* find the first empty slot */
1108 for (curmod=0; modlist[curmod] && modlist[curmod] != (LDAPMod *) -1;
1110 if (modlist[curmod] == (LDAPMod *) -1) {
1111 if (!(modlist = TALLOC_REALLOC_ARRAY(ctx, modlist, LDAPMod *,
1112 curmod+ADS_MODLIST_ALLOC_SIZE+1)))
1113 return ADS_ERROR(LDAP_NO_MEMORY);
1114 memset(&modlist[curmod], 0,
1115 ADS_MODLIST_ALLOC_SIZE*sizeof(LDAPMod *));
1116 modlist[curmod+ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1117 *mods = (ADS_MODLIST)modlist;
1120 if (!(modlist[curmod] = TALLOC_ZERO_P(ctx, LDAPMod)))
1121 return ADS_ERROR(LDAP_NO_MEMORY);
1122 modlist[curmod]->mod_type = talloc_strdup(ctx, name);
1123 if (mod_op & LDAP_MOD_BVALUES) {
1124 modlist[curmod]->mod_bvalues = ber_values;
1125 } else if (mod_op & LDAP_MOD_DELETE) {
1126 modlist[curmod]->mod_values = NULL;
1128 modlist[curmod]->mod_values = char_values;
1131 modlist[curmod]->mod_op = mod_op;
1132 return ADS_ERROR(LDAP_SUCCESS);
1136 * Add a single string value to a mod list
1137 * @param ctx An initialized TALLOC_CTX
1138 * @param mods An initialized ADS_MODLIST
1139 * @param name The attribute name to add
1140 * @param val The value to add - NULL means DELETE
1141 * @return ADS STATUS indicating success of add
1143 ADS_STATUS ads_mod_str(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1144 const char *name, const char *val)
1146 const char *values[2];
1152 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1153 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE, name, values);
1157 * Add an array of string values to a mod list
1158 * @param ctx An initialized TALLOC_CTX
1159 * @param mods An initialized ADS_MODLIST
1160 * @param name The attribute name to add
1161 * @param vals The array of string values to add - NULL means DELETE
1162 * @return ADS STATUS indicating success of add
1164 ADS_STATUS ads_mod_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1165 const char *name, const char **vals)
1168 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1169 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE,
1170 name, (const void **) vals);
1175 * Add a single ber-encoded value to a mod list
1176 * @param ctx An initialized TALLOC_CTX
1177 * @param mods An initialized ADS_MODLIST
1178 * @param name The attribute name to add
1179 * @param val The value to add - NULL means DELETE
1180 * @return ADS STATUS indicating success of add
1182 static ADS_STATUS ads_mod_ber(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1183 const char *name, const struct berval *val)
1185 const struct berval *values[2];
1190 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1191 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE|LDAP_MOD_BVALUES,
1192 name, (const void **) values);
1197 * Perform an ldap modify
1198 * @param ads connection to ads server
1199 * @param mod_dn DistinguishedName to modify
1200 * @param mods list of modifications to perform
1201 * @return status of modify
1203 ADS_STATUS ads_gen_mod(ADS_STRUCT *ads, const char *mod_dn, ADS_MODLIST mods)
1206 char *utf8_dn = NULL;
1208 this control is needed to modify that contains a currently
1209 non-existent attribute (but allowable for the object) to run
1211 LDAPControl PermitModify = {
1212 CONST_DISCARD(char *, ADS_PERMIT_MODIFY_OID),
1215 LDAPControl *controls[2];
1217 controls[0] = &PermitModify;
1220 if (push_utf8_allocate(&utf8_dn, mod_dn) == -1) {
1221 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1224 /* find the end of the list, marked by NULL or -1 */
1225 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1226 /* make sure the end of the list is NULL */
1228 ret = ldap_modify_ext_s(ads->ld, utf8_dn,
1229 (LDAPMod **) mods, controls, NULL);
1231 return ADS_ERROR(ret);
1235 * Perform an ldap add
1236 * @param ads connection to ads server
1237 * @param new_dn DistinguishedName to add
1238 * @param mods list of attributes and values for DN
1239 * @return status of add
1241 ADS_STATUS ads_gen_add(ADS_STRUCT *ads, const char *new_dn, ADS_MODLIST mods)
1244 char *utf8_dn = NULL;
1246 if (push_utf8_allocate(&utf8_dn, new_dn) == -1) {
1247 DEBUG(1, ("ads_gen_add: push_utf8_allocate failed!"));
1248 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1251 /* find the end of the list, marked by NULL or -1 */
1252 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1253 /* make sure the end of the list is NULL */
1256 ret = ldap_add_s(ads->ld, utf8_dn, (LDAPMod**)mods);
1258 return ADS_ERROR(ret);
1262 * Delete a DistinguishedName
1263 * @param ads connection to ads server
1264 * @param new_dn DistinguishedName to delete
1265 * @return status of delete
1267 ADS_STATUS ads_del_dn(ADS_STRUCT *ads, char *del_dn)
1270 char *utf8_dn = NULL;
1271 if (push_utf8_allocate(&utf8_dn, del_dn) == -1) {
1272 DEBUG(1, ("ads_del_dn: push_utf8_allocate failed!"));
1273 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1276 ret = ldap_delete_s(ads->ld, utf8_dn);
1278 return ADS_ERROR(ret);
1282 * Build an org unit string
1283 * if org unit is Computers or blank then assume a container, otherwise
1284 * assume a / separated list of organisational units.
1285 * jmcd: '\' is now used for escapes so certain chars can be in the ou (e.g. #)
1286 * @param ads connection to ads server
1287 * @param org_unit Organizational unit
1288 * @return org unit string - caller must free
1290 char *ads_ou_string(ADS_STRUCT *ads, const char *org_unit)
1294 if (!org_unit || !*org_unit) {
1296 ret = ads_default_ou_string(ads, WELL_KNOWN_GUID_COMPUTERS);
1298 /* samba4 might not yet respond to a wellknownobject-query */
1299 return ret ? ret : SMB_STRDUP("cn=Computers");
1302 if (strequal(org_unit, "Computers")) {
1303 return SMB_STRDUP("cn=Computers");
1306 /* jmcd: removed "\\" from the separation chars, because it is
1307 needed as an escape for chars like '#' which are valid in an
1309 return ads_build_path(org_unit, "/", "ou=", 1);
1313 * Get a org unit string for a well-known GUID
1314 * @param ads connection to ads server
1315 * @param wknguid Well known GUID
1316 * @return org unit string - caller must free
1318 char *ads_default_ou_string(ADS_STRUCT *ads, const char *wknguid)
1321 LDAPMessage *res = NULL;
1322 char *base, *wkn_dn = NULL, *ret = NULL, **wkn_dn_exp = NULL,
1323 **bind_dn_exp = NULL;
1324 const char *attrs[] = {"distinguishedName", NULL};
1325 int new_ln, wkn_ln, bind_ln, i;
1327 if (wknguid == NULL) {
1331 if (asprintf(&base, "<WKGUID=%s,%s>", wknguid, ads->config.bind_path ) == -1) {
1332 DEBUG(1, ("asprintf failed!\n"));
1336 status = ads_search_dn(ads, &res, base, attrs);
1337 if (!ADS_ERR_OK(status)) {
1338 DEBUG(1,("Failed while searching for: %s\n", base));
1342 if (ads_count_replies(ads, res) != 1) {
1346 /* substitute the bind-path from the well-known-guid-search result */
1347 wkn_dn = ads_get_dn(ads, res);
1352 wkn_dn_exp = ldap_explode_dn(wkn_dn, 0);
1357 bind_dn_exp = ldap_explode_dn(ads->config.bind_path, 0);
1362 for (wkn_ln=0; wkn_dn_exp[wkn_ln]; wkn_ln++)
1364 for (bind_ln=0; bind_dn_exp[bind_ln]; bind_ln++)
1367 new_ln = wkn_ln - bind_ln;
1369 ret = SMB_STRDUP(wkn_dn_exp[0]);
1374 for (i=1; i < new_ln; i++) {
1377 if (asprintf(&s, "%s,%s", ret, wkn_dn_exp[i]) == -1) {
1383 ret = SMB_STRDUP(s);
1392 ads_msgfree(ads, res);
1393 ads_memfree(ads, wkn_dn);
1395 ldap_value_free(wkn_dn_exp);
1398 ldap_value_free(bind_dn_exp);
1405 * Adds (appends) an item to an attribute array, rather then
1406 * replacing the whole list
1407 * @param ctx An initialized TALLOC_CTX
1408 * @param mods An initialized ADS_MODLIST
1409 * @param name name of the ldap attribute to append to
1410 * @param vals an array of values to add
1411 * @return status of addition
1414 ADS_STATUS ads_add_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1415 const char *name, const char **vals)
1417 return ads_modlist_add(ctx, mods, LDAP_MOD_ADD, name,
1418 (const void *) vals);
1422 * Determines the computer account's current KVNO via an LDAP lookup
1423 * @param ads An initialized ADS_STRUCT
1424 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1425 * @return the kvno for the computer account, or -1 in case of a failure.
1428 uint32 ads_get_kvno(ADS_STRUCT *ads, const char *machine_name)
1430 LDAPMessage *res = NULL;
1431 uint32 kvno = (uint32)-1; /* -1 indicates a failure */
1433 const char *attrs[] = {"msDS-KeyVersionNumber", NULL};
1434 char *dn_string = NULL;
1435 ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS);
1437 DEBUG(5,("ads_get_kvno: Searching for host %s\n", machine_name));
1438 if (asprintf(&filter, "(samAccountName=%s$)", machine_name) == -1) {
1441 ret = ads_search(ads, &res, filter, attrs);
1443 if (!ADS_ERR_OK(ret) && ads_count_replies(ads, res)) {
1444 DEBUG(1,("ads_get_kvno: Computer Account For %s not found.\n", machine_name));
1445 ads_msgfree(ads, res);
1449 dn_string = ads_get_dn(ads, res);
1451 DEBUG(0,("ads_get_kvno: out of memory.\n"));
1452 ads_msgfree(ads, res);
1455 DEBUG(5,("ads_get_kvno: Using: %s\n", dn_string));
1456 ads_memfree(ads, dn_string);
1458 /* ---------------------------------------------------------
1459 * 0 is returned as a default KVNO from this point on...
1460 * This is done because Windows 2000 does not support key
1461 * version numbers. Chances are that a failure in the next
1462 * step is simply due to Windows 2000 being used for a
1463 * domain controller. */
1466 if (!ads_pull_uint32(ads, res, "msDS-KeyVersionNumber", &kvno)) {
1467 DEBUG(3,("ads_get_kvno: Error Determining KVNO!\n"));
1468 DEBUG(3,("ads_get_kvno: Windows 2000 does not support KVNO's, so this may be normal.\n"));
1469 ads_msgfree(ads, res);
1474 DEBUG(5,("ads_get_kvno: Looked Up KVNO of: %d\n", kvno));
1475 ads_msgfree(ads, res);
1480 * This clears out all registered spn's for a given hostname
1481 * @param ads An initilaized ADS_STRUCT
1482 * @param machine_name the NetBIOS name of the computer.
1483 * @return 0 upon success, non-zero otherwise.
1486 ADS_STATUS ads_clear_service_principal_names(ADS_STRUCT *ads, const char *machine_name)
1489 LDAPMessage *res = NULL;
1491 const char *servicePrincipalName[1] = {NULL};
1492 ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS);
1493 char *dn_string = NULL;
1495 ret = ads_find_machine_acct(ads, &res, machine_name);
1496 if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
1497 DEBUG(5,("ads_clear_service_principal_names: WARNING: Host Account for %s not found... skipping operation.\n", machine_name));
1498 DEBUG(5,("ads_clear_service_principal_names: WARNING: Service Principals for %s have NOT been cleared.\n", machine_name));
1499 ads_msgfree(ads, res);
1500 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1503 DEBUG(5,("ads_clear_service_principal_names: Host account for %s found\n", machine_name));
1504 ctx = talloc_init("ads_clear_service_principal_names");
1506 ads_msgfree(ads, res);
1507 return ADS_ERROR(LDAP_NO_MEMORY);
1510 if (!(mods = ads_init_mods(ctx))) {
1511 talloc_destroy(ctx);
1512 ads_msgfree(ads, res);
1513 return ADS_ERROR(LDAP_NO_MEMORY);
1515 ret = ads_mod_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1516 if (!ADS_ERR_OK(ret)) {
1517 DEBUG(1,("ads_clear_service_principal_names: Error creating strlist.\n"));
1518 ads_msgfree(ads, res);
1519 talloc_destroy(ctx);
1522 dn_string = ads_get_dn(ads, res);
1524 talloc_destroy(ctx);
1525 ads_msgfree(ads, res);
1526 return ADS_ERROR(LDAP_NO_MEMORY);
1528 ret = ads_gen_mod(ads, dn_string, mods);
1529 ads_memfree(ads,dn_string);
1530 if (!ADS_ERR_OK(ret)) {
1531 DEBUG(1,("ads_clear_service_principal_names: Error: Updating Service Principals for machine %s in LDAP\n",
1533 ads_msgfree(ads, res);
1534 talloc_destroy(ctx);
1538 ads_msgfree(ads, res);
1539 talloc_destroy(ctx);
1544 * This adds a service principal name to an existing computer account
1545 * (found by hostname) in AD.
1546 * @param ads An initialized ADS_STRUCT
1547 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1548 * @param my_fqdn The fully qualified DNS name of the machine
1549 * @param spn A string of the service principal to add, i.e. 'host'
1550 * @return 0 upon sucess, or non-zero if a failure occurs
1553 ADS_STATUS ads_add_service_principal_name(ADS_STRUCT *ads, const char *machine_name,
1554 const char *my_fqdn, const char *spn)
1558 LDAPMessage *res = NULL;
1561 char *dn_string = NULL;
1562 const char *servicePrincipalName[3] = {NULL, NULL, NULL};
1564 ret = ads_find_machine_acct(ads, &res, machine_name);
1565 if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
1566 DEBUG(1,("ads_add_service_principal_name: WARNING: Host Account for %s not found... skipping operation.\n",
1568 DEBUG(1,("ads_add_service_principal_name: WARNING: Service Principal '%s/%s@%s' has NOT been added.\n",
1569 spn, machine_name, ads->config.realm));
1570 ads_msgfree(ads, res);
1571 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1574 DEBUG(1,("ads_add_service_principal_name: Host account for %s found\n", machine_name));
1575 if (!(ctx = talloc_init("ads_add_service_principal_name"))) {
1576 ads_msgfree(ads, res);
1577 return ADS_ERROR(LDAP_NO_MEMORY);
1580 /* add short name spn */
1582 if ( (psp1 = talloc_asprintf(ctx, "%s/%s", spn, machine_name)) == NULL ) {
1583 talloc_destroy(ctx);
1584 ads_msgfree(ads, res);
1585 return ADS_ERROR(LDAP_NO_MEMORY);
1588 strlower_m(&psp1[strlen(spn)]);
1589 servicePrincipalName[0] = psp1;
1591 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n",
1592 psp1, machine_name));
1595 /* add fully qualified spn */
1597 if ( (psp2 = talloc_asprintf(ctx, "%s/%s", spn, my_fqdn)) == NULL ) {
1598 ret = ADS_ERROR(LDAP_NO_MEMORY);
1602 strlower_m(&psp2[strlen(spn)]);
1603 servicePrincipalName[1] = psp2;
1605 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n",
1606 psp2, machine_name));
1608 if ( (mods = ads_init_mods(ctx)) == NULL ) {
1609 ret = ADS_ERROR(LDAP_NO_MEMORY);
1613 ret = ads_add_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1614 if (!ADS_ERR_OK(ret)) {
1615 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
1619 if ( (dn_string = ads_get_dn(ads, res)) == NULL ) {
1620 ret = ADS_ERROR(LDAP_NO_MEMORY);
1624 ret = ads_gen_mod(ads, dn_string, mods);
1625 ads_memfree(ads,dn_string);
1626 if (!ADS_ERR_OK(ret)) {
1627 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
1633 ads_msgfree(ads, res);
1638 * adds a machine account to the ADS server
1639 * @param ads An intialized ADS_STRUCT
1640 * @param machine_name - the NetBIOS machine name of this account.
1641 * @param account_type A number indicating the type of account to create
1642 * @param org_unit The LDAP path in which to place this account
1643 * @return 0 upon success, or non-zero otherwise
1646 ADS_STATUS ads_create_machine_acct(ADS_STRUCT *ads, const char *machine_name,
1647 const char *org_unit)
1650 char *samAccountName, *controlstr;
1653 char *machine_escaped = NULL;
1655 const char *objectClass[] = {"top", "person", "organizationalPerson",
1656 "user", "computer", NULL};
1657 LDAPMessage *res = NULL;
1658 uint32 acct_control = ( UF_WORKSTATION_TRUST_ACCOUNT |\
1659 UF_DONT_EXPIRE_PASSWD |\
1660 UF_ACCOUNTDISABLE );
1662 if (!(ctx = talloc_init("ads_add_machine_acct")))
1663 return ADS_ERROR(LDAP_NO_MEMORY);
1665 ret = ADS_ERROR(LDAP_NO_MEMORY);
1667 machine_escaped = escape_rdn_val_string_alloc(machine_name);
1668 if (!machine_escaped) {
1672 new_dn = talloc_asprintf(ctx, "cn=%s,%s", machine_escaped, org_unit);
1673 samAccountName = talloc_asprintf(ctx, "%s$", machine_name);
1675 if ( !new_dn || !samAccountName ) {
1679 #ifndef ENCTYPE_ARCFOUR_HMAC
1680 acct_control |= UF_USE_DES_KEY_ONLY;
1683 if (!(controlstr = talloc_asprintf(ctx, "%u", acct_control))) {
1687 if (!(mods = ads_init_mods(ctx))) {
1691 ads_mod_str(ctx, &mods, "cn", machine_name);
1692 ads_mod_str(ctx, &mods, "sAMAccountName", samAccountName);
1693 ads_mod_strlist(ctx, &mods, "objectClass", objectClass);
1694 ads_mod_str(ctx, &mods, "userAccountControl", controlstr);
1696 ret = ads_gen_add(ads, new_dn, mods);
1699 SAFE_FREE(machine_escaped);
1700 ads_msgfree(ads, res);
1701 talloc_destroy(ctx);
1707 * move a machine account to another OU on the ADS server
1708 * @param ads - An intialized ADS_STRUCT
1709 * @param machine_name - the NetBIOS machine name of this account.
1710 * @param org_unit - The LDAP path in which to place this account
1711 * @param moved - whether we moved the machine account (optional)
1712 * @return 0 upon success, or non-zero otherwise
1715 ADS_STATUS ads_move_machine_acct(ADS_STRUCT *ads, const char *machine_name,
1716 const char *org_unit, BOOL *moved)
1720 LDAPMessage *res = NULL;
1721 char *filter = NULL;
1722 char *computer_dn = NULL;
1724 char *computer_rdn = NULL;
1725 BOOL need_move = False;
1727 if (asprintf(&filter, "(samAccountName=%s$)", machine_name) == -1) {
1728 rc = ADS_ERROR(LDAP_NO_MEMORY);
1732 /* Find pre-existing machine */
1733 rc = ads_search(ads, &res, filter, NULL);
1734 if (!ADS_ERR_OK(rc)) {
1738 computer_dn = ads_get_dn(ads, res);
1740 rc = ADS_ERROR(LDAP_NO_MEMORY);
1744 parent_dn = ads_parent_dn(computer_dn);
1745 if (strequal(parent_dn, org_unit)) {
1751 if (asprintf(&computer_rdn, "CN=%s", machine_name) == -1) {
1752 rc = ADS_ERROR(LDAP_NO_MEMORY);
1756 ldap_status = ldap_rename_s(ads->ld, computer_dn, computer_rdn,
1757 org_unit, 1, NULL, NULL);
1758 rc = ADS_ERROR(ldap_status);
1761 ads_msgfree(ads, res);
1763 SAFE_FREE(computer_dn);
1764 SAFE_FREE(computer_rdn);
1766 if (!ADS_ERR_OK(rc)) {
1778 dump a binary result from ldap
1780 static void dump_binary(const char *field, struct berval **values)
1783 for (i=0; values[i]; i++) {
1784 printf("%s: ", field);
1785 for (j=0; j<values[i]->bv_len; j++) {
1786 printf("%02X", (unsigned char)values[i]->bv_val[j]);
1792 static void dump_guid(const char *field, struct berval **values)
1796 for (i=0; values[i]; i++) {
1797 memcpy(guid.info, values[i]->bv_val, sizeof(guid.info));
1798 printf("%s: %s\n", field,
1799 smb_uuid_string_static(smb_uuid_unpack_static(guid)));
1804 dump a sid result from ldap
1806 static void dump_sid(const char *field, struct berval **values)
1809 for (i=0; values[i]; i++) {
1811 sid_parse(values[i]->bv_val, values[i]->bv_len, &sid);
1812 printf("%s: %s\n", field, sid_string_static(&sid));
1817 dump ntSecurityDescriptor
1819 static void dump_sd(const char *filed, struct berval **values)
1824 TALLOC_CTX *ctx = 0;
1826 if (!(ctx = talloc_init("sec_io_desc")))
1830 prs_init(&ps, values[0]->bv_len, ctx, UNMARSHALL);
1831 prs_copy_data_in(&ps, values[0]->bv_val, values[0]->bv_len);
1832 prs_set_offset(&ps,0);
1835 if (!sec_io_desc("sd", &psd, &ps, 1)) {
1837 talloc_destroy(ctx);
1840 if (psd) ads_disp_sd(psd);
1843 talloc_destroy(ctx);
1847 dump a string result from ldap
1849 static void dump_string(const char *field, char **values)
1852 for (i=0; values[i]; i++) {
1853 printf("%s: %s\n", field, values[i]);
1858 dump a field from LDAP on stdout
1862 static BOOL ads_dump_field(char *field, void **values, void *data_area)
1867 void (*handler)(const char *, struct berval **);
1869 {"objectGUID", False, dump_guid},
1870 {"netbootGUID", False, dump_guid},
1871 {"nTSecurityDescriptor", False, dump_sd},
1872 {"dnsRecord", False, dump_binary},
1873 {"objectSid", False, dump_sid},
1874 {"tokenGroups", False, dump_sid},
1875 {"tokenGroupsNoGCAcceptable", False, dump_sid},
1876 {"tokengroupsGlobalandUniversal", False, dump_sid},
1877 {"mS-DS-CreatorSID", False, dump_sid},
1882 if (!field) { /* must be end of an entry */
1887 for (i=0; handlers[i].name; i++) {
1888 if (StrCaseCmp(handlers[i].name, field) == 0) {
1889 if (!values) /* first time, indicate string or not */
1890 return handlers[i].string;
1891 handlers[i].handler(field, (struct berval **) values);
1895 if (!handlers[i].name) {
1896 if (!values) /* first time, indicate string conversion */
1898 dump_string(field, (char **)values);
1904 * Dump a result from LDAP on stdout
1905 * used for debugging
1906 * @param ads connection to ads server
1907 * @param res Results to dump
1910 void ads_dump(ADS_STRUCT *ads, LDAPMessage *res)
1912 ads_process_results(ads, res, ads_dump_field, NULL);
1916 * Walk through results, calling a function for each entry found.
1917 * The function receives a field name, a berval * array of values,
1918 * and a data area passed through from the start. The function is
1919 * called once with null for field and values at the end of each
1921 * @param ads connection to ads server
1922 * @param res Results to process
1923 * @param fn Function for processing each result
1924 * @param data_area user-defined area to pass to function
1926 void ads_process_results(ADS_STRUCT *ads, LDAPMessage *res,
1927 BOOL(*fn)(char *, void **, void *),
1933 if (!(ctx = talloc_init("ads_process_results")))
1936 for (msg = ads_first_entry(ads, res); msg;
1937 msg = ads_next_entry(ads, msg)) {
1941 for (utf8_field=ldap_first_attribute(ads->ld,
1942 (LDAPMessage *)msg,&b);
1944 utf8_field=ldap_next_attribute(ads->ld,
1945 (LDAPMessage *)msg,b)) {
1946 struct berval **ber_vals;
1947 char **str_vals, **utf8_vals;
1951 pull_utf8_talloc(ctx, &field, utf8_field);
1952 string = fn(field, NULL, data_area);
1955 utf8_vals = ldap_get_values(ads->ld,
1956 (LDAPMessage *)msg, field);
1957 str_vals = ads_pull_strvals(ctx,
1958 (const char **) utf8_vals);
1959 fn(field, (void **) str_vals, data_area);
1960 ldap_value_free(utf8_vals);
1962 ber_vals = ldap_get_values_len(ads->ld,
1963 (LDAPMessage *)msg, field);
1964 fn(field, (void **) ber_vals, data_area);
1966 ldap_value_free_len(ber_vals);
1968 ldap_memfree(utf8_field);
1971 talloc_free_children(ctx);
1972 fn(NULL, NULL, data_area); /* completed an entry */
1975 talloc_destroy(ctx);
1979 * count how many replies are in a LDAPMessage
1980 * @param ads connection to ads server
1981 * @param res Results to count
1982 * @return number of replies
1984 int ads_count_replies(ADS_STRUCT *ads, void *res)
1986 return ldap_count_entries(ads->ld, (LDAPMessage *)res);
1990 * pull the first entry from a ADS result
1991 * @param ads connection to ads server
1992 * @param res Results of search
1993 * @return first entry from result
1995 LDAPMessage *ads_first_entry(ADS_STRUCT *ads, LDAPMessage *res)
1997 return ldap_first_entry(ads->ld, res);
2001 * pull the next entry from a ADS result
2002 * @param ads connection to ads server
2003 * @param res Results of search
2004 * @return next entry from result
2006 LDAPMessage *ads_next_entry(ADS_STRUCT *ads, LDAPMessage *res)
2008 return ldap_next_entry(ads->ld, res);
2012 * pull a single string from a ADS result
2013 * @param ads connection to ads server
2014 * @param mem_ctx TALLOC_CTX to use for allocating result string
2015 * @param msg Results of search
2016 * @param field Attribute to retrieve
2017 * @return Result string in talloc context
2019 char *ads_pull_string(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, LDAPMessage *msg,
2027 values = ldap_get_values(ads->ld, msg, field);
2032 rc = pull_utf8_talloc(mem_ctx, &ux_string,
2034 if (rc != (size_t)-1)
2038 ldap_value_free(values);
2043 * pull an array of strings from a ADS result
2044 * @param ads connection to ads server
2045 * @param mem_ctx TALLOC_CTX to use for allocating result string
2046 * @param msg Results of search
2047 * @param field Attribute to retrieve
2048 * @return Result strings in talloc context
2050 char **ads_pull_strings(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2051 LDAPMessage *msg, const char *field,
2058 values = ldap_get_values(ads->ld, msg, field);
2062 *num_values = ldap_count_values(values);
2064 ret = TALLOC_ARRAY(mem_ctx, char *, *num_values + 1);
2066 ldap_value_free(values);
2070 for (i=0;i<*num_values;i++) {
2071 if (pull_utf8_talloc(mem_ctx, &ret[i], values[i]) == -1) {
2072 ldap_value_free(values);
2078 ldap_value_free(values);
2083 * pull an array of strings from a ADS result
2084 * (handle large multivalue attributes with range retrieval)
2085 * @param ads connection to ads server
2086 * @param mem_ctx TALLOC_CTX to use for allocating result string
2087 * @param msg Results of search
2088 * @param field Attribute to retrieve
2089 * @param current_strings strings returned by a previous call to this function
2090 * @param next_attribute The next query should ask for this attribute
2091 * @param num_values How many values did we get this time?
2092 * @param more_values Are there more values to get?
2093 * @return Result strings in talloc context
2095 char **ads_pull_strings_range(ADS_STRUCT *ads,
2096 TALLOC_CTX *mem_ctx,
2097 LDAPMessage *msg, const char *field,
2098 char **current_strings,
2099 const char **next_attribute,
2100 size_t *num_strings,
2104 char *expected_range_attrib, *range_attr;
2105 BerElement *ptr = NULL;
2108 size_t num_new_strings;
2109 unsigned long int range_start;
2110 unsigned long int range_end;
2112 /* we might have been given the whole lot anyway */
2113 if ((strings = ads_pull_strings(ads, mem_ctx, msg, field, num_strings))) {
2114 *more_strings = False;
2118 expected_range_attrib = talloc_asprintf(mem_ctx, "%s;Range=", field);
2120 /* look for Range result */
2121 for (attr = ldap_first_attribute(ads->ld, (LDAPMessage *)msg, &ptr);
2123 attr = ldap_next_attribute(ads->ld, (LDAPMessage *)msg, ptr)) {
2124 /* we ignore the fact that this is utf8, as all attributes are ascii... */
2125 if (strnequal(attr, expected_range_attrib, strlen(expected_range_attrib))) {
2133 /* nothing here - this field is just empty */
2134 *more_strings = False;
2138 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-%lu",
2139 &range_start, &range_end) == 2) {
2140 *more_strings = True;
2142 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-*",
2143 &range_start) == 1) {
2144 *more_strings = False;
2146 DEBUG(1, ("ads_pull_strings_range: Cannot parse Range attriubte (%s)\n",
2148 ldap_memfree(range_attr);
2149 *more_strings = False;
2154 if ((*num_strings) != range_start) {
2155 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) doesn't start at %u, but at %lu"
2156 " - aborting range retreival\n",
2157 range_attr, (unsigned int)(*num_strings) + 1, range_start));
2158 ldap_memfree(range_attr);
2159 *more_strings = False;
2163 new_strings = ads_pull_strings(ads, mem_ctx, msg, range_attr, &num_new_strings);
2165 if (*more_strings && ((*num_strings + num_new_strings) != (range_end + 1))) {
2166 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) tells us we have %lu "
2167 "strings in this bunch, but we only got %lu - aborting range retreival\n",
2168 range_attr, (unsigned long int)range_end - range_start + 1,
2169 (unsigned long int)num_new_strings));
2170 ldap_memfree(range_attr);
2171 *more_strings = False;
2175 strings = TALLOC_REALLOC_ARRAY(mem_ctx, current_strings, char *,
2176 *num_strings + num_new_strings);
2178 if (strings == NULL) {
2179 ldap_memfree(range_attr);
2180 *more_strings = False;
2184 if (new_strings && num_new_strings) {
2185 memcpy(&strings[*num_strings], new_strings,
2186 sizeof(*new_strings) * num_new_strings);
2189 (*num_strings) += num_new_strings;
2191 if (*more_strings) {
2192 *next_attribute = talloc_asprintf(mem_ctx,
2197 if (!*next_attribute) {
2198 DEBUG(1, ("talloc_asprintf for next attribute failed!\n"));
2199 ldap_memfree(range_attr);
2200 *more_strings = False;
2205 ldap_memfree(range_attr);
2211 * pull a single uint32 from a ADS result
2212 * @param ads connection to ads server
2213 * @param msg Results of search
2214 * @param field Attribute to retrieve
2215 * @param v Pointer to int to store result
2216 * @return boolean inidicating success
2218 BOOL ads_pull_uint32(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
2223 values = ldap_get_values(ads->ld, msg, field);
2227 ldap_value_free(values);
2231 *v = atoi(values[0]);
2232 ldap_value_free(values);
2237 * pull a single objectGUID from an ADS result
2238 * @param ads connection to ADS server
2239 * @param msg results of search
2240 * @param guid 37-byte area to receive text guid
2241 * @return boolean indicating success
2243 BOOL ads_pull_guid(ADS_STRUCT *ads, LDAPMessage *msg, struct GUID *guid)
2246 UUID_FLAT flat_guid;
2248 values = ldap_get_values(ads->ld, msg, "objectGUID");
2253 memcpy(&flat_guid.info, values[0], sizeof(UUID_FLAT));
2254 smb_uuid_unpack(flat_guid, guid);
2255 ldap_value_free(values);
2258 ldap_value_free(values);
2265 * pull a single DOM_SID from a ADS result
2266 * @param ads connection to ads server
2267 * @param msg Results of search
2268 * @param field Attribute to retrieve
2269 * @param sid Pointer to sid to store result
2270 * @return boolean inidicating success
2272 BOOL ads_pull_sid(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
2275 struct berval **values;
2278 values = ldap_get_values_len(ads->ld, msg, field);
2284 ret = sid_parse(values[0]->bv_val, values[0]->bv_len, sid);
2286 ldap_value_free_len(values);
2291 * pull an array of DOM_SIDs from a ADS result
2292 * @param ads connection to ads server
2293 * @param mem_ctx TALLOC_CTX for allocating sid array
2294 * @param msg Results of search
2295 * @param field Attribute to retrieve
2296 * @param sids pointer to sid array to allocate
2297 * @return the count of SIDs pulled
2299 int ads_pull_sids(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2300 LDAPMessage *msg, const char *field, DOM_SID **sids)
2302 struct berval **values;
2306 values = ldap_get_values_len(ads->ld, msg, field);
2311 for (i=0; values[i]; i++)
2315 (*sids) = TALLOC_ARRAY(mem_ctx, DOM_SID, i);
2317 ldap_value_free_len(values);
2325 for (i=0; values[i]; i++) {
2326 ret = sid_parse(values[i]->bv_val, values[i]->bv_len, &(*sids)[count]);
2329 DEBUG(10, ("pulling SID: %s\n", sid_to_string(sid, &(*sids)[count])));
2334 ldap_value_free_len(values);
2339 * pull a SEC_DESC from a ADS result
2340 * @param ads connection to ads server
2341 * @param mem_ctx TALLOC_CTX for allocating sid array
2342 * @param msg Results of search
2343 * @param field Attribute to retrieve
2344 * @param sd Pointer to *SEC_DESC to store result (talloc()ed)
2345 * @return boolean inidicating success
2347 BOOL ads_pull_sd(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2348 LDAPMessage *msg, const char *field, SEC_DESC **sd)
2350 struct berval **values;
2353 values = ldap_get_values_len(ads->ld, msg, field);
2355 if (!values) return False;
2359 prs_init(&ps, values[0]->bv_len, mem_ctx, UNMARSHALL);
2360 prs_copy_data_in(&ps, values[0]->bv_val, values[0]->bv_len);
2361 prs_set_offset(&ps,0);
2363 ret = sec_io_desc("sd", sd, &ps, 1);
2367 ldap_value_free_len(values);
2372 * in order to support usernames longer than 21 characters we need to
2373 * use both the sAMAccountName and the userPrincipalName attributes
2374 * It seems that not all users have the userPrincipalName attribute set
2376 * @param ads connection to ads server
2377 * @param mem_ctx TALLOC_CTX for allocating sid array
2378 * @param msg Results of search
2379 * @return the username
2381 char *ads_pull_username(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2387 /* lookup_name() only works on the sAMAccountName to
2388 returning the username portion of userPrincipalName
2389 breaks winbindd_getpwnam() */
2391 ret = ads_pull_string(ads, mem_ctx, msg, "userPrincipalName");
2392 if (ret && (p = strchr_m(ret, '@'))) {
2397 return ads_pull_string(ads, mem_ctx, msg, "sAMAccountName");
2402 * find the update serial number - this is the core of the ldap cache
2403 * @param ads connection to ads server
2404 * @param ads connection to ADS server
2405 * @param usn Pointer to retrieved update serial number
2406 * @return status of search
2408 ADS_STATUS ads_USN(ADS_STRUCT *ads, uint32 *usn)
2410 const char *attrs[] = {"highestCommittedUSN", NULL};
2414 status = ads_do_search_retry(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2415 if (!ADS_ERR_OK(status))
2418 if (ads_count_replies(ads, res) != 1) {
2419 ads_msgfree(ads, res);
2420 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2423 if (!ads_pull_uint32(ads, res, "highestCommittedUSN", usn)) {
2424 ads_msgfree(ads, res);
2425 return ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
2428 ads_msgfree(ads, res);
2432 /* parse a ADS timestring - typical string is
2433 '20020917091222.0Z0' which means 09:12.22 17th September
2435 static time_t ads_parse_time(const char *str)
2441 if (sscanf(str, "%4d%2d%2d%2d%2d%2d",
2442 &tm.tm_year, &tm.tm_mon, &tm.tm_mday,
2443 &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) {
2452 /********************************************************************
2453 ********************************************************************/
2455 ADS_STATUS ads_current_time(ADS_STRUCT *ads)
2457 const char *attrs[] = {"currentTime", NULL};
2462 ADS_STRUCT *ads_s = ads;
2464 if (!(ctx = talloc_init("ads_current_time"))) {
2465 return ADS_ERROR(LDAP_NO_MEMORY);
2468 /* establish a new ldap tcp session if necessary */
2471 if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup,
2472 ads->server.ldap_server )) == NULL )
2476 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
2477 status = ads_connect( ads_s );
2478 if ( !ADS_ERR_OK(status))
2482 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2483 if (!ADS_ERR_OK(status)) {
2487 timestr = ads_pull_string(ads_s, ctx, res, "currentTime");
2489 ads_msgfree(ads_s, res);
2490 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2494 /* but save the time and offset in the original ADS_STRUCT */
2496 ads->config.current_time = ads_parse_time(timestr);
2498 if (ads->config.current_time != 0) {
2499 ads->auth.time_offset = ads->config.current_time - time(NULL);
2500 DEBUG(4,("time offset is %d seconds\n", ads->auth.time_offset));
2503 ads_msgfree(ads, res);
2505 status = ADS_SUCCESS;
2508 /* free any temporary ads connections */
2509 if ( ads_s != ads ) {
2510 ads_destroy( &ads_s );
2512 talloc_destroy(ctx);
2517 /********************************************************************
2518 ********************************************************************/
2520 ADS_STATUS ads_domain_func_level(ADS_STRUCT *ads, uint32 *val)
2522 const char *attrs[] = {"domainFunctionality", NULL};
2525 ADS_STRUCT *ads_s = ads;
2527 *val = DS_DOMAIN_FUNCTION_2000;
2529 /* establish a new ldap tcp session if necessary */
2532 if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup,
2533 ads->server.ldap_server )) == NULL )
2537 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
2538 status = ads_connect( ads_s );
2539 if ( !ADS_ERR_OK(status))
2543 /* If the attribute does not exist assume it is a Windows 2000
2544 functional domain */
2546 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2547 if (!ADS_ERR_OK(status)) {
2548 if ( status.err.rc == LDAP_NO_SUCH_ATTRIBUTE ) {
2549 status = ADS_SUCCESS;
2554 if ( !ads_pull_uint32(ads_s, res, "domainFunctionality", val) ) {
2555 DEBUG(5,("ads_domain_func_level: Failed to pull the domainFunctionality attribute.\n"));
2557 DEBUG(3,("ads_domain_func_level: %d\n", *val));
2560 ads_msgfree(ads, res);
2563 /* free any temporary ads connections */
2564 if ( ads_s != ads ) {
2565 ads_destroy( &ads_s );
2572 * find the domain sid for our domain
2573 * @param ads connection to ads server
2574 * @param sid Pointer to domain sid
2575 * @return status of search
2577 ADS_STATUS ads_domain_sid(ADS_STRUCT *ads, DOM_SID *sid)
2579 const char *attrs[] = {"objectSid", NULL};
2583 rc = ads_do_search_retry(ads, ads->config.bind_path, LDAP_SCOPE_BASE, "(objectclass=*)",
2585 if (!ADS_ERR_OK(rc)) return rc;
2586 if (!ads_pull_sid(ads, res, "objectSid", sid)) {
2587 ads_msgfree(ads, res);
2588 return ADS_ERROR_SYSTEM(ENOENT);
2590 ads_msgfree(ads, res);
2596 * find our site name
2597 * @param ads connection to ads server
2598 * @param mem_ctx Pointer to talloc context
2599 * @param site_name Pointer to the sitename
2600 * @return status of search
2602 ADS_STATUS ads_site_dn(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char **site_name)
2606 const char *dn, *service_name;
2607 const char *attrs[] = { "dsServiceName", NULL };
2609 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2610 if (!ADS_ERR_OK(status)) {
2614 service_name = ads_pull_string(ads, mem_ctx, res, "dsServiceName");
2615 if (service_name == NULL) {
2616 ads_msgfree(ads, res);
2617 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2620 ads_msgfree(ads, res);
2622 /* go up three levels */
2623 dn = ads_parent_dn(ads_parent_dn(ads_parent_dn(service_name)));
2625 return ADS_ERROR(LDAP_NO_MEMORY);
2628 *site_name = talloc_strdup(mem_ctx, dn);
2629 if (*site_name == NULL) {
2630 return ADS_ERROR(LDAP_NO_MEMORY);
2635 dsServiceName: CN=NTDS Settings,CN=W2K3DC,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,DC=ber,DC=suse,DC=de
2640 * find the site dn where a machine resides
2641 * @param ads connection to ads server
2642 * @param mem_ctx Pointer to talloc context
2643 * @param computer_name name of the machine
2644 * @param site_name Pointer to the sitename
2645 * @return status of search
2647 ADS_STATUS ads_site_dn_for_machine(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char *computer_name, const char **site_dn)
2651 const char *parent, *config_context, *filter;
2652 const char *attrs[] = { "configurationNamingContext", NULL };
2655 /* shortcut a query */
2656 if (strequal(computer_name, ads->config.ldap_server_name)) {
2657 return ads_site_dn(ads, mem_ctx, site_dn);
2660 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2661 if (!ADS_ERR_OK(status)) {
2665 config_context = ads_pull_string(ads, mem_ctx, res, "configurationNamingContext");
2666 if (config_context == NULL) {
2667 ads_msgfree(ads, res);
2668 return ADS_ERROR(LDAP_NO_MEMORY);
2671 filter = talloc_asprintf(mem_ctx, "(cn=%s)", computer_name);
2672 if (filter == NULL) {
2673 ads_msgfree(ads, res);
2674 return ADS_ERROR(LDAP_NO_MEMORY);
2677 ads_msgfree(ads, res);
2679 status = ads_do_search(ads, config_context, LDAP_SCOPE_SUBTREE, filter, NULL, &res);
2680 if (!ADS_ERR_OK(status)) {
2684 if (ads_count_replies(ads, res) != 1) {
2685 ads_msgfree(ads, res);
2686 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
2689 dn = ads_get_dn(ads, res);
2691 ads_msgfree(ads, res);
2692 return ADS_ERROR(LDAP_NO_MEMORY);
2695 /* go up three levels */
2696 parent = ads_parent_dn(ads_parent_dn(ads_parent_dn(dn)));
2697 if (parent == NULL) {
2698 ads_msgfree(ads, res);
2699 ads_memfree(ads, dn);
2700 return ADS_ERROR(LDAP_NO_MEMORY);
2703 *site_dn = talloc_strdup(mem_ctx, parent);
2704 if (*site_dn == NULL) {
2705 ads_msgfree(ads, res);
2706 ads_memfree(ads, dn);
2707 ADS_ERROR(LDAP_NO_MEMORY);
2710 ads_memfree(ads, dn);
2711 ads_msgfree(ads, res);
2717 * get the upn suffixes for a domain
2718 * @param ads connection to ads server
2719 * @param mem_ctx Pointer to talloc context
2720 * @param suffixes Pointer to an array of suffixes
2721 * @param num_suffixes Pointer to the number of suffixes
2722 * @return status of search
2724 ADS_STATUS ads_upn_suffixes(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, char ***suffixes, size_t *num_suffixes)
2728 const char *config_context, *base;
2729 const char *attrs[] = { "configurationNamingContext", NULL };
2730 const char *attrs2[] = { "uPNSuffixes", NULL };
2732 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2733 if (!ADS_ERR_OK(status)) {
2737 config_context = ads_pull_string(ads, mem_ctx, res, "configurationNamingContext");
2738 if (config_context == NULL) {
2739 ads_msgfree(ads, res);
2740 return ADS_ERROR(LDAP_NO_MEMORY);
2743 ads_msgfree(ads, res);
2745 base = talloc_asprintf(mem_ctx, "cn=Partitions,%s", config_context);
2747 return ADS_ERROR(LDAP_NO_MEMORY);
2750 status = ads_search_dn(ads, &res, base, attrs2);
2751 if (!ADS_ERR_OK(status)) {
2755 if (ads_count_replies(ads, res) != 1) {
2756 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
2759 (*suffixes) = ads_pull_strings(ads, mem_ctx, res, "uPNSuffixes", num_suffixes);
2760 if ((*suffixes) == NULL) {
2761 ads_msgfree(ads, res);
2762 return ADS_ERROR(LDAP_NO_MEMORY);
2765 ads_msgfree(ads, res);
2771 * pull a DOM_SID from an extended dn string
2772 * @param mem_ctx TALLOC_CTX
2773 * @param extended_dn string
2774 * @param flags string type of extended_dn
2775 * @param sid pointer to a DOM_SID
2776 * @return boolean inidicating success
2778 BOOL ads_get_sid_from_extended_dn(TALLOC_CTX *mem_ctx,
2779 const char *extended_dn,
2780 enum ads_extended_dn_flags flags,
2789 /* otherwise extended_dn gets stripped off */
2790 if ((dn = talloc_strdup(mem_ctx, extended_dn)) == NULL) {
2794 * ADS_EXTENDED_DN_HEX_STRING:
2795 * <GUID=238e1963cb390f4bb032ba0105525a29>;<SID=010500000000000515000000bb68c8fd6b61b427572eb04556040000>;CN=gd,OU=berlin,OU=suse,DC=ber,DC=suse,DC=de
2797 * ADS_EXTENDED_DN_STRING (only with w2k3):
2798 <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
2801 p = strchr(dn, ';');
2806 if (strncmp(p, ";<SID=", strlen(";<SID=")) != 0) {
2810 p += strlen(";<SID=");
2819 DEBUG(100,("ads_get_sid_from_extended_dn: sid string is %s\n", p));
2823 case ADS_EXTENDED_DN_STRING:
2824 if (!string_to_sid(sid, p)) {
2828 case ADS_EXTENDED_DN_HEX_STRING: {
2832 buf_len = strhex_to_str(buf, strlen(p), p);
2837 if (!sid_parse(buf, buf_len, sid)) {
2838 DEBUG(10,("failed to parse sid\n"));
2844 DEBUG(10,("unknown extended dn format\n"));
2852 * pull an array of DOM_SIDs from a ADS result
2853 * @param ads connection to ads server
2854 * @param mem_ctx TALLOC_CTX for allocating sid array
2855 * @param msg Results of search
2856 * @param field Attribute to retrieve
2857 * @param flags string type of extended_dn
2858 * @param sids pointer to sid array to allocate
2859 * @return the count of SIDs pulled
2861 int ads_pull_sids_from_extendeddn(ADS_STRUCT *ads,
2862 TALLOC_CTX *mem_ctx,
2865 enum ads_extended_dn_flags flags,
2872 if ((dn_strings = ads_pull_strings(ads, mem_ctx, msg, field,
2873 &dn_count)) == NULL) {
2877 (*sids) = TALLOC_ZERO_ARRAY(mem_ctx, DOM_SID, dn_count + 1);
2879 TALLOC_FREE(dn_strings);
2883 for (i=0; i<dn_count; i++) {
2885 if (!ads_get_sid_from_extended_dn(mem_ctx, dn_strings[i],
2886 flags, &(*sids)[i])) {
2888 TALLOC_FREE(dn_strings);
2893 TALLOC_FREE(dn_strings);
2898 /********************************************************************
2899 ********************************************************************/
2901 char* ads_get_dnshostname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
2903 LDAPMessage *res = NULL;
2908 status = ads_find_machine_acct(ads, &res, global_myname());
2909 if (!ADS_ERR_OK(status)) {
2910 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
2915 if ( (count = ads_count_replies(ads, res)) != 1 ) {
2916 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
2920 if ( (name = ads_pull_string(ads, ctx, res, "dNSHostName")) == NULL ) {
2921 DEBUG(0,("ads_get_dnshostname: No dNSHostName attribute!\n"));
2925 ads_msgfree(ads, res);
2930 /********************************************************************
2931 ********************************************************************/
2933 char* ads_get_upn( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
2935 LDAPMessage *res = NULL;
2940 status = ads_find_machine_acct(ads, &res, global_myname());
2941 if (!ADS_ERR_OK(status)) {
2942 DEBUG(0,("ads_get_upn: Failed to find account for %s\n",
2947 if ( (count = ads_count_replies(ads, res)) != 1 ) {
2948 DEBUG(1,("ads_get_upn: %d entries returned!\n", count));
2952 if ( (name = ads_pull_string(ads, ctx, res, "userPrincipalName")) == NULL ) {
2953 DEBUG(2,("ads_get_upn: No userPrincipalName attribute!\n"));
2957 ads_msgfree(ads, res);
2962 /********************************************************************
2963 ********************************************************************/
2965 char* ads_get_samaccountname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
2967 LDAPMessage *res = NULL;
2972 status = ads_find_machine_acct(ads, &res, global_myname());
2973 if (!ADS_ERR_OK(status)) {
2974 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
2979 if ( (count = ads_count_replies(ads, res)) != 1 ) {
2980 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
2984 if ( (name = ads_pull_string(ads, ctx, res, "sAMAccountName")) == NULL ) {
2985 DEBUG(0,("ads_get_dnshostname: No sAMAccountName attribute!\n"));
2989 ads_msgfree(ads, res);
2996 SAVED CODE - we used to join via ldap - remember how we did this. JRA.
2999 * Join a machine to a realm
3000 * Creates the machine account and sets the machine password
3001 * @param ads connection to ads server
3002 * @param machine name of host to add
3003 * @param org_unit Organizational unit to place machine in
3004 * @return status of join
3006 ADS_STATUS ads_join_realm(ADS_STRUCT *ads, const char *machine_name,
3007 uint32 account_type, const char *org_unit)
3010 LDAPMessage *res = NULL;
3013 /* machine name must be lowercase */
3014 machine = SMB_STRDUP(machine_name);
3015 strlower_m(machine);
3018 status = ads_find_machine_acct(ads, (void **)&res, machine);
3019 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
3020 DEBUG(0, ("Host account for %s already exists - deleting old account\n", machine));
3021 status = ads_leave_realm(ads, machine);
3022 if (!ADS_ERR_OK(status)) {
3023 DEBUG(0, ("Failed to delete host '%s' from the '%s' realm.\n",
3024 machine, ads->config.realm));
3029 status = ads_add_machine_acct(ads, machine, account_type, org_unit);
3030 if (!ADS_ERR_OK(status)) {
3031 DEBUG(0, ("ads_join_realm: ads_add_machine_acct failed (%s): %s\n", machine, ads_errstr(status)));
3036 status = ads_find_machine_acct(ads, (void **)(void *)&res, machine);
3037 if (!ADS_ERR_OK(status)) {
3038 DEBUG(0, ("ads_join_realm: Host account test failed for machine %s\n", machine));
3044 ads_msgfree(ads, res);
3051 * Delete a machine from the realm
3052 * @param ads connection to ads server
3053 * @param hostname Machine to remove
3054 * @return status of delete
3056 ADS_STATUS ads_leave_realm(ADS_STRUCT *ads, const char *hostname)
3061 char *hostnameDN, *host;
3063 LDAPControl ldap_control;
3064 LDAPControl * pldap_control[2] = {NULL, NULL};
3066 pldap_control[0] = &ldap_control;
3067 memset(&ldap_control, 0, sizeof(LDAPControl));
3068 ldap_control.ldctl_oid = (char *)LDAP_SERVER_TREE_DELETE_OID;
3070 /* hostname must be lowercase */
3071 host = SMB_STRDUP(hostname);
3074 status = ads_find_machine_acct(ads, &res, host);
3075 if (!ADS_ERR_OK(status)) {
3076 DEBUG(0, ("Host account for %s does not exist.\n", host));
3081 msg = ads_first_entry(ads, res);
3084 return ADS_ERROR_SYSTEM(ENOENT);
3087 hostnameDN = ads_get_dn(ads, (LDAPMessage *)msg);
3089 rc = ldap_delete_ext_s(ads->ld, hostnameDN, pldap_control, NULL);
3091 DEBUG(3,("ldap_delete_ext_s failed with error code %d\n", rc));
3093 DEBUG(3,("ldap_delete_ext_s succeeded with error code %d\n", rc));
3096 if (rc != LDAP_SUCCESS) {
3097 const char *attrs[] = { "cn", NULL };
3098 LDAPMessage *msg_sub;
3100 /* we only search with scope ONE, we do not expect any further
3101 * objects to be created deeper */
3103 status = ads_do_search_retry(ads, hostnameDN,
3104 LDAP_SCOPE_ONELEVEL,
3105 "(objectclass=*)", attrs, &res);
3107 if (!ADS_ERR_OK(status)) {
3109 ads_memfree(ads, hostnameDN);
3113 for (msg_sub = ads_first_entry(ads, res); msg_sub;
3114 msg_sub = ads_next_entry(ads, msg_sub)) {
3118 if ((dn = ads_get_dn(ads, msg_sub)) == NULL) {
3120 ads_memfree(ads, hostnameDN);
3121 return ADS_ERROR(LDAP_NO_MEMORY);
3124 status = ads_del_dn(ads, dn);
3125 if (!ADS_ERR_OK(status)) {
3126 DEBUG(3,("failed to delete dn %s: %s\n", dn, ads_errstr(status)));
3128 ads_memfree(ads, dn);
3129 ads_memfree(ads, hostnameDN);
3133 ads_memfree(ads, dn);
3136 /* there should be no subordinate objects anymore */
3137 status = ads_do_search_retry(ads, hostnameDN,
3138 LDAP_SCOPE_ONELEVEL,
3139 "(objectclass=*)", attrs, &res);
3141 if (!ADS_ERR_OK(status) || ( (ads_count_replies(ads, res)) > 0 ) ) {
3143 ads_memfree(ads, hostnameDN);
3147 /* delete hostnameDN now */
3148 status = ads_del_dn(ads, hostnameDN);
3149 if (!ADS_ERR_OK(status)) {
3151 DEBUG(3,("failed to delete dn %s: %s\n", hostnameDN, ads_errstr(status)));
3152 ads_memfree(ads, hostnameDN);
3157 ads_memfree(ads, hostnameDN);
3159 status = ads_find_machine_acct(ads, &res, host);
3160 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
3161 DEBUG(3, ("Failed to remove host account.\n"));
3171 * pull all token-sids from an LDAP dn
3172 * @param ads connection to ads server
3173 * @param mem_ctx TALLOC_CTX for allocating sid array
3174 * @param dn of LDAP object
3175 * @param user_sid pointer to DOM_SID (objectSid)
3176 * @param primary_group_sid pointer to DOM_SID (self composed)
3177 * @param sids pointer to sid array to allocate
3178 * @param num_sids counter of SIDs pulled
3179 * @return status of token query
3181 ADS_STATUS ads_get_tokensids(ADS_STRUCT *ads,
3182 TALLOC_CTX *mem_ctx,
3185 DOM_SID *primary_group_sid,
3190 LDAPMessage *res = NULL;
3192 size_t tmp_num_sids;
3194 DOM_SID tmp_user_sid;
3195 DOM_SID tmp_primary_group_sid;
3197 const char *attrs[] = {
3204 status = ads_search_retry_dn(ads, &res, dn, attrs);
3205 if (!ADS_ERR_OK(status)) {
3209 count = ads_count_replies(ads, res);
3211 ads_msgfree(ads, res);
3212 return ADS_ERROR_LDAP(LDAP_NO_SUCH_OBJECT);
3215 if (!ads_pull_sid(ads, res, "objectSid", &tmp_user_sid)) {
3216 ads_msgfree(ads, res);
3217 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3220 if (!ads_pull_uint32(ads, res, "primaryGroupID", &pgid)) {
3221 ads_msgfree(ads, res);
3222 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3226 /* hack to compose the primary group sid without knowing the
3232 sid_copy(&domsid, &tmp_user_sid);
3234 if (!sid_split_rid(&domsid, &dummy_rid)) {
3235 ads_msgfree(ads, res);
3236 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3239 if (!sid_compose(&tmp_primary_group_sid, &domsid, pgid)) {
3240 ads_msgfree(ads, res);
3241 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3245 tmp_num_sids = ads_pull_sids(ads, mem_ctx, res, "tokenGroups", &tmp_sids);
3247 if (tmp_num_sids == 0 || !tmp_sids) {
3248 ads_msgfree(ads, res);
3249 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3253 *num_sids = tmp_num_sids;
3261 *user_sid = tmp_user_sid;
3264 if (primary_group_sid) {
3265 *primary_group_sid = tmp_primary_group_sid;
3268 DEBUG(10,("ads_get_tokensids: returned %d sids\n", (int)tmp_num_sids + 2));
3270 ads_msgfree(ads, res);
3271 return ADS_ERROR_LDAP(LDAP_SUCCESS);
3274 ADS_STATUS ads_find_samaccount(ADS_STRUCT *ads,
3275 TALLOC_CTX *mem_ctx,
3276 const char *samaccountname,
3278 const char **dn_ret)
3281 const char *attrs[] = { "userAccountControl", NULL };
3283 LDAPMessage *res = NULL;
3287 filter = talloc_asprintf(mem_ctx, "(&(objectclass=user)(sAMAccountName=%s))",
3289 if (filter == NULL) {
3293 status = ads_do_search_all(ads, ads->config.bind_path,
3295 filter, attrs, &res);
3297 if (!ADS_ERR_OK(status)) {
3301 if (ads_count_replies(ads, res) != 1) {
3302 printf("no result\n");
3306 dn = ads_get_dn(ads, res);
3308 status = ADS_ERROR(LDAP_NO_MEMORY);
3312 if (!ads_pull_uint32(ads, res, "userAccountControl", &uac)) {
3313 status = ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
3322 *dn_ret = talloc_strdup(mem_ctx, dn);
3324 status = ADS_ERROR(LDAP_NO_MEMORY);
3329 ads_memfree(ads, dn);
3330 ads_msgfree(ads, res);