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 2 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, write to the Free Software
22 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
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)
62 CatchSignal(SIGALRM, SIGNAL_CAST gotalarm_sig);
64 /* End setup timeout. */
66 ldp = ldap_open(server, port);
69 DEBUG(2,("Could not open LDAP connection to %s:%d: %s\n",
70 server, port, strerror(errno)));
73 /* Teardown timeout. */
74 CatchSignal(SIGALRM, SIGNAL_CAST SIG_IGN);
80 static int ldap_search_with_timeout(LDAP *ld,
81 LDAP_CONST char *base,
83 LDAP_CONST char *filter,
91 struct timeval timeout;
94 /* Setup timeout for the ldap_search_ext_s call - local and remote. */
95 timeout.tv_sec = lp_ldap_timeout();
98 /* Setup alarm timeout.... Do we need both of these ? JRA. */
100 CatchSignal(SIGALRM, SIGNAL_CAST gotalarm_sig);
101 alarm(lp_ldap_timeout());
102 /* End setup timeout. */
104 result = ldap_search_ext_s(ld, base, scope, filter, attrs,
105 attrsonly, sctrls, cctrls, &timeout,
108 /* Teardown timeout. */
109 CatchSignal(SIGALRM, SIGNAL_CAST SIG_IGN);
113 return LDAP_TIMELIMIT_EXCEEDED;
118 /**********************************************
119 Do client and server sitename match ?
120 **********************************************/
122 BOOL ads_sitename_match(ADS_STRUCT *ads)
124 if (ads->config.server_site_name == NULL &&
125 ads->config.client_site_name == NULL ) {
126 DEBUG(10,("ads_sitename_match: both null\n"));
129 if (ads->config.server_site_name &&
130 ads->config.client_site_name &&
131 strequal(ads->config.server_site_name,
132 ads->config.client_site_name)) {
133 DEBUG(10,("ads_sitename_match: name %s match\n", ads->config.server_site_name));
136 DEBUG(10,("ads_sitename_match: no match between server: %s and client: %s\n",
137 ads->config.server_site_name ? ads->config.server_site_name : "NULL",
138 ads->config.client_site_name ? ads->config.client_site_name : "NULL"));
142 /**********************************************
143 Is this the closest DC ?
144 **********************************************/
146 BOOL ads_closest_dc(ADS_STRUCT *ads)
148 if (ads->config.flags & ADS_CLOSEST) {
149 DEBUG(10,("ads_closest_dc: ADS_CLOSEST flag set\n"));
153 /* not sure if this can ever happen */
154 if (ads_sitename_match(ads)) {
155 DEBUG(10,("ads_closest_dc: ADS_CLOSEST flag not set but sites match\n"));
159 if (ads->config.client_site_name == NULL) {
160 DEBUG(10,("ads_closest_dc: client belongs to no site\n"));
164 DEBUG(10,("ads_closest_dc: %s is not the closest DC\n",
165 ads->config.ldap_server_name));
172 try a connection to a given ldap server, returning True and setting the servers IP
173 in the ads struct if successful
175 BOOL ads_try_connect(ADS_STRUCT *ads, const char *server )
178 struct cldap_netlogon_reply cldap_reply;
180 if (!server || !*server) {
184 DEBUG(5,("ads_try_connect: sending CLDAP request to %s (realm: %s)\n",
185 server, ads->server.realm));
187 /* this copes with inet_ntoa brokenness */
189 srv = SMB_STRDUP(server);
191 ZERO_STRUCT( cldap_reply );
193 if ( !ads_cldap_netlogon( srv, ads->server.realm, &cldap_reply ) ) {
194 DEBUG(3,("ads_try_connect: CLDAP request %s failed.\n", srv));
199 /* Check the CLDAP reply flags */
201 if ( !(cldap_reply.flags & ADS_LDAP) ) {
202 DEBUG(1,("ads_try_connect: %s's CLDAP reply says it is not an LDAP server!\n",
208 /* Fill in the ads->config values */
210 SAFE_FREE(ads->config.realm);
211 SAFE_FREE(ads->config.bind_path);
212 SAFE_FREE(ads->config.ldap_server_name);
213 SAFE_FREE(ads->config.server_site_name);
214 SAFE_FREE(ads->config.client_site_name);
215 SAFE_FREE(ads->server.workgroup);
217 ads->config.flags = cldap_reply.flags;
218 ads->config.ldap_server_name = SMB_STRDUP(cldap_reply.hostname);
219 strupper_m(cldap_reply.domain);
220 ads->config.realm = SMB_STRDUP(cldap_reply.domain);
221 ads->config.bind_path = ads_build_dn(ads->config.realm);
222 if (*cldap_reply.server_site_name) {
223 ads->config.server_site_name =
224 SMB_STRDUP(cldap_reply.server_site_name);
226 if (*cldap_reply.client_site_name) {
227 ads->config.client_site_name =
228 SMB_STRDUP(cldap_reply.client_site_name);
231 ads->server.workgroup = SMB_STRDUP(cldap_reply.netbios_domain);
233 ads->ldap_port = LDAP_PORT;
234 ads->ldap_ip = *interpret_addr2(srv);
237 /* Store our site name. */
238 sitename_store( cldap_reply.domain, cldap_reply.client_site_name );
243 /**********************************************************************
244 Try to find an AD dc using our internal name resolution routines
245 Try the realm first and then then workgroup name if netbios is not
247 **********************************************************************/
249 static NTSTATUS ads_find_dc(ADS_STRUCT *ads)
251 const char *c_domain;
254 struct ip_service *ip_list;
257 BOOL got_realm = False;
258 BOOL use_own_domain = False;
260 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
262 /* if the realm and workgroup are both empty, assume they are ours */
265 c_realm = ads->server.realm;
267 if ( !c_realm || !*c_realm ) {
268 /* special case where no realm and no workgroup means our own */
269 if ( !ads->server.workgroup || !*ads->server.workgroup ) {
270 use_own_domain = True;
271 c_realm = lp_realm();
275 if (c_realm && *c_realm)
280 /* we need to try once with the realm name and fallback to the
281 netbios domain name if we fail (if netbios has not been disabled */
283 if ( !got_realm && !lp_disable_netbios() ) {
284 c_realm = ads->server.workgroup;
285 if (!c_realm || !*c_realm) {
286 if ( use_own_domain )
287 c_realm = lp_workgroup();
291 if ( !c_realm || !*c_realm ) {
292 DEBUG(0,("ads_find_dc: no realm or workgroup! Don't know what to do\n"));
293 return NT_STATUS_INVALID_PARAMETER; /* rather need MISSING_PARAMETER ... */
296 if ( use_own_domain ) {
297 c_domain = lp_workgroup();
299 c_domain = ads->server.workgroup;
302 pstrcpy( domain, c_domain );
303 pstrcpy( realm, c_realm );
306 * In case of LDAP we use get_dc_name() as that
307 * creates the custom krb5.conf file
309 if (!(ads->auth.flags & ADS_AUTH_NO_BIND)) {
311 struct in_addr ip_out;
313 DEBUG(6,("ads_find_dc: (ldap) looking for %s '%s'\n",
314 (got_realm ? "realm" : "domain"), realm));
316 if (get_dc_name(domain, realm, srv_name, &ip_out)) {
318 * we call ads_try_connect() to fill in the
319 * ads->config details
321 if (ads_try_connect(ads, srv_name)) {
326 return NT_STATUS_NO_LOGON_SERVERS;
329 sitename = sitename_fetch(realm);
331 DEBUG(6,("ads_find_dc: (cldap) looking for %s '%s'\n",
332 (got_realm ? "realm" : "domain"), realm));
334 status = get_sorted_dc_list(realm, sitename, &ip_list, &count, got_realm);
335 if (!NT_STATUS_IS_OK(status)) {
336 /* fall back to netbios if we can */
337 if ( got_realm && !lp_disable_netbios() ) {
346 /* if we fail this loop, then giveup since all the IP addresses returned were dead */
347 for ( i=0; i<count; i++ ) {
350 fstrcpy( server, inet_ntoa(ip_list[i].ip) );
352 if ( !NT_STATUS_IS_OK(check_negative_conn_cache(realm, server)) )
356 /* realm in this case is a workgroup name. We need
357 to ignore any IP addresses in the negative connection
358 cache that match ip addresses returned in the ad realm
359 case. It sucks that I have to reproduce the logic above... */
360 c_realm = ads->server.realm;
361 if ( !c_realm || !*c_realm ) {
362 if ( !ads->server.workgroup || !*ads->server.workgroup ) {
363 c_realm = lp_realm();
366 if (c_realm && *c_realm &&
367 !NT_STATUS_IS_OK(check_negative_conn_cache(c_realm, server))) {
368 /* Ensure we add the workgroup name for this
369 IP address as negative too. */
370 add_failed_connection_entry( realm, server, NT_STATUS_UNSUCCESSFUL );
375 if ( ads_try_connect(ads, server) ) {
381 /* keep track of failures */
382 add_failed_connection_entry( realm, server, NT_STATUS_UNSUCCESSFUL );
387 /* In case we failed to contact one of our closest DC on our site we
388 * need to try to find another DC, retry with a site-less SRV DNS query
392 DEBUG(1,("ads_find_dc: failed to find a valid DC on our site (%s), "
393 "trying to find another DC\n", sitename));
395 namecache_delete(realm, 0x1C);
399 return NT_STATUS_NO_LOGON_SERVERS;
404 * Connect to the LDAP server
405 * @param ads Pointer to an existing ADS_STRUCT
406 * @return status of connection
408 ADS_STATUS ads_connect(ADS_STRUCT *ads)
410 int version = LDAP_VERSION3;
414 ads->last_attempt = time(NULL);
417 /* try with a user specified server */
419 if (ads->server.ldap_server &&
420 ads_try_connect(ads, ads->server.ldap_server)) {
424 ntstatus = ads_find_dc(ads);
425 if (NT_STATUS_IS_OK(ntstatus)) {
429 return ADS_ERROR_NT(ntstatus);
432 DEBUG(3,("Connected to LDAP server %s\n", inet_ntoa(ads->ldap_ip)));
434 if (!ads->auth.user_name) {
435 /* Must use the userPrincipalName value here or sAMAccountName
436 and not servicePrincipalName; found by Guenther Deschner */
438 asprintf(&ads->auth.user_name, "%s$", global_myname() );
441 if (!ads->auth.realm) {
442 ads->auth.realm = SMB_STRDUP(ads->config.realm);
445 if (!ads->auth.kdc_server) {
446 ads->auth.kdc_server = SMB_STRDUP(inet_ntoa(ads->ldap_ip));
450 /* this is a really nasty hack to avoid ADS DNS problems. It needs a patch
451 to MIT kerberos to work (tridge) */
454 asprintf(&env, "KRB5_KDC_ADDRESS_%s", ads->config.realm);
455 setenv(env, ads->auth.kdc_server, 1);
460 /* If the caller() requested no LDAP bind, then we are done */
462 if (ads->auth.flags & ADS_AUTH_NO_BIND) {
466 /* Otherwise setup the TCP LDAP session */
468 if ( (ads->ld = ldap_open_with_timeout(ads->config.ldap_server_name,
469 LDAP_PORT, lp_ldap_timeout())) == NULL )
471 return ADS_ERROR(LDAP_OPERATIONS_ERROR);
474 /* cache the successful connection for workgroup and realm */
475 if (ads_closest_dc(ads)) {
476 saf_store( ads->server.workgroup, ads->config.ldap_server_name);
477 saf_store( ads->server.realm, ads->config.ldap_server_name);
480 ldap_set_option(ads->ld, LDAP_OPT_PROTOCOL_VERSION, &version);
482 status = ADS_ERROR(smb_ldap_start_tls(ads->ld, version));
483 if (!ADS_ERR_OK(status)) {
487 /* fill in the current time and offsets */
489 status = ads_current_time( ads );
490 if ( !ADS_ERR_OK(status) ) {
494 /* Now do the bind */
496 if (ads->auth.flags & ADS_AUTH_ANON_BIND) {
497 return ADS_ERROR(ldap_simple_bind_s( ads->ld, NULL, NULL));
500 if (ads->auth.flags & ADS_AUTH_SIMPLE_BIND) {
501 return ADS_ERROR(ldap_simple_bind_s( ads->ld, ads->auth.user_name, ads->auth.password));
504 return ads_sasl_bind(ads);
508 Duplicate a struct berval into talloc'ed memory
510 static struct berval *dup_berval(TALLOC_CTX *ctx, const struct berval *in_val)
512 struct berval *value;
514 if (!in_val) return NULL;
516 value = TALLOC_ZERO_P(ctx, struct berval);
519 if (in_val->bv_len == 0) return value;
521 value->bv_len = in_val->bv_len;
522 value->bv_val = (char *)TALLOC_MEMDUP(ctx, in_val->bv_val,
528 Make a values list out of an array of (struct berval *)
530 static struct berval **ads_dup_values(TALLOC_CTX *ctx,
531 const struct berval **in_vals)
533 struct berval **values;
536 if (!in_vals) return NULL;
537 for (i=0; in_vals[i]; i++)
539 values = TALLOC_ZERO_ARRAY(ctx, struct berval *, i+1);
540 if (!values) return NULL;
542 for (i=0; in_vals[i]; i++) {
543 values[i] = dup_berval(ctx, in_vals[i]);
549 UTF8-encode a values list out of an array of (char *)
551 static char **ads_push_strvals(TALLOC_CTX *ctx, const char **in_vals)
556 if (!in_vals) return NULL;
557 for (i=0; in_vals[i]; i++)
559 values = TALLOC_ZERO_ARRAY(ctx, char *, i+1);
560 if (!values) return NULL;
562 for (i=0; in_vals[i]; i++) {
563 push_utf8_talloc(ctx, &values[i], in_vals[i]);
569 Pull a (char *) array out of a UTF8-encoded values list
571 static char **ads_pull_strvals(TALLOC_CTX *ctx, const char **in_vals)
576 if (!in_vals) return NULL;
577 for (i=0; in_vals[i]; i++)
579 values = TALLOC_ZERO_ARRAY(ctx, char *, i+1);
580 if (!values) return NULL;
582 for (i=0; in_vals[i]; i++) {
583 pull_utf8_talloc(ctx, &values[i], in_vals[i]);
589 * Do a search with paged results. cookie must be null on the first
590 * call, and then returned on each subsequent call. It will be null
591 * again when the entire search is complete
592 * @param ads connection to ads server
593 * @param bind_path Base dn for the search
594 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
595 * @param expr Search expression - specified in local charset
596 * @param attrs Attributes to retrieve - specified in utf8 or ascii
597 * @param res ** which will contain results - free res* with ads_msgfree()
598 * @param count Number of entries retrieved on this page
599 * @param cookie The paged results cookie to be returned on subsequent calls
600 * @return status of search
602 static ADS_STATUS ads_do_paged_search_args(ADS_STRUCT *ads,
603 const char *bind_path,
604 int scope, const char *expr,
605 const char **attrs, void *args,
607 int *count, struct berval **cookie)
610 char *utf8_expr, *utf8_path, **search_attrs;
611 LDAPControl PagedResults, NoReferrals, ExtendedDn, *controls[4], **rcontrols;
612 BerElement *cookie_be = NULL;
613 struct berval *cookie_bv= NULL;
614 BerElement *extdn_be = NULL;
615 struct berval *extdn_bv= NULL;
618 ads_control *external_control = (ads_control *) args;
622 if (!(ctx = talloc_init("ads_do_paged_search_args")))
623 return ADS_ERROR(LDAP_NO_MEMORY);
625 /* 0 means the conversion worked but the result was empty
626 so we only fail if it's -1. In any case, it always
627 at least nulls out the dest */
628 if ((push_utf8_talloc(ctx, &utf8_expr, expr) == (size_t)-1) ||
629 (push_utf8_talloc(ctx, &utf8_path, bind_path) == (size_t)-1)) {
634 if (!attrs || !(*attrs))
637 /* This would be the utf8-encoded version...*/
638 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
639 if (!(str_list_copy(&search_attrs, attrs))) {
646 /* Paged results only available on ldap v3 or later */
647 ldap_get_option(ads->ld, LDAP_OPT_PROTOCOL_VERSION, &version);
648 if (version < LDAP_VERSION3) {
649 rc = LDAP_NOT_SUPPORTED;
653 cookie_be = ber_alloc_t(LBER_USE_DER);
655 ber_printf(cookie_be, "{iO}", (ber_int_t) 1000, *cookie);
656 ber_bvfree(*cookie); /* don't need it from last time */
659 ber_printf(cookie_be, "{io}", (ber_int_t) 1000, "", 0);
661 ber_flatten(cookie_be, &cookie_bv);
662 PagedResults.ldctl_oid = CONST_DISCARD(char *, ADS_PAGE_CTL_OID);
663 PagedResults.ldctl_iscritical = (char) 1;
664 PagedResults.ldctl_value.bv_len = cookie_bv->bv_len;
665 PagedResults.ldctl_value.bv_val = cookie_bv->bv_val;
667 NoReferrals.ldctl_oid = CONST_DISCARD(char *, ADS_NO_REFERRALS_OID);
668 NoReferrals.ldctl_iscritical = (char) 0;
669 NoReferrals.ldctl_value.bv_len = 0;
670 NoReferrals.ldctl_value.bv_val = CONST_DISCARD(char *, "");
672 if (external_control && strequal(external_control->control, ADS_EXTENDED_DN_OID)) {
674 ExtendedDn.ldctl_oid = CONST_DISCARD(char *, external_control->control);
675 ExtendedDn.ldctl_iscritical = (char) external_control->critical;
677 /* win2k does not accept a ldctl_value beeing passed in */
679 if (external_control->val != 0) {
681 if ((extdn_be = ber_alloc_t(LBER_USE_DER)) == NULL ) {
686 if ((ber_printf(extdn_be, "{i}", (ber_int_t) external_control->val)) == -1) {
690 if ((ber_flatten(extdn_be, &extdn_bv)) == -1) {
695 ExtendedDn.ldctl_value.bv_len = extdn_bv->bv_len;
696 ExtendedDn.ldctl_value.bv_val = extdn_bv->bv_val;
699 ExtendedDn.ldctl_value.bv_len = 0;
700 ExtendedDn.ldctl_value.bv_val = NULL;
703 controls[0] = &NoReferrals;
704 controls[1] = &PagedResults;
705 controls[2] = &ExtendedDn;
709 controls[0] = &NoReferrals;
710 controls[1] = &PagedResults;
714 /* we need to disable referrals as the openldap libs don't
715 handle them and paged results at the same time. Using them
716 together results in the result record containing the server
717 page control being removed from the result list (tridge/jmcd)
719 leaving this in despite the control that says don't generate
720 referrals, in case the server doesn't support it (jmcd)
722 ldap_set_option(ads->ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
724 rc = ldap_search_with_timeout(ads->ld, utf8_path, scope, utf8_expr,
725 search_attrs, 0, controls,
727 (LDAPMessage **)res);
729 ber_free(cookie_be, 1);
730 ber_bvfree(cookie_bv);
733 DEBUG(3,("ads_do_paged_search_args: ldap_search_with_timeout(%s) -> %s\n", expr,
734 ldap_err2string(rc)));
738 rc = ldap_parse_result(ads->ld, *res, NULL, NULL, NULL,
739 NULL, &rcontrols, 0);
745 for (i=0; rcontrols[i]; i++) {
746 if (strcmp(ADS_PAGE_CTL_OID, rcontrols[i]->ldctl_oid) == 0) {
747 cookie_be = ber_init(&rcontrols[i]->ldctl_value);
748 ber_scanf(cookie_be,"{iO}", (ber_int_t *) count,
750 /* the berval is the cookie, but must be freed when
752 if (cookie_bv->bv_len) /* still more to do */
753 *cookie=ber_bvdup(cookie_bv);
756 ber_bvfree(cookie_bv);
757 ber_free(cookie_be, 1);
761 ldap_controls_free(rcontrols);
767 ber_free(extdn_be, 1);
771 ber_bvfree(extdn_bv);
774 /* if/when we decide to utf8-encode attrs, take out this next line */
775 str_list_free(&search_attrs);
777 return ADS_ERROR(rc);
780 static ADS_STATUS ads_do_paged_search(ADS_STRUCT *ads, const char *bind_path,
781 int scope, const char *expr,
782 const char **attrs, LDAPMessage **res,
783 int *count, struct berval **cookie)
785 return ads_do_paged_search_args(ads, bind_path, scope, expr, attrs, NULL, res, count, cookie);
790 * Get all results for a search. This uses ads_do_paged_search() to return
791 * all entries in a large search.
792 * @param ads connection to ads server
793 * @param bind_path Base dn for the search
794 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
795 * @param expr Search expression
796 * @param attrs Attributes to retrieve
797 * @param res ** which will contain results - free res* with ads_msgfree()
798 * @return status of search
800 ADS_STATUS ads_do_search_all_args(ADS_STRUCT *ads, const char *bind_path,
801 int scope, const char *expr,
802 const char **attrs, void *args,
805 struct berval *cookie = NULL;
810 status = ads_do_paged_search_args(ads, bind_path, scope, expr, attrs, args, res,
813 if (!ADS_ERR_OK(status))
816 #ifdef HAVE_LDAP_ADD_RESULT_ENTRY
818 LDAPMessage *res2 = NULL;
820 LDAPMessage *msg, *next;
822 status2 = ads_do_paged_search_args(ads, bind_path, scope, expr,
823 attrs, args, &res2, &count, &cookie);
825 if (!ADS_ERR_OK(status2)) break;
827 /* this relies on the way that ldap_add_result_entry() works internally. I hope
828 that this works on all ldap libs, but I have only tested with openldap */
829 for (msg = ads_first_message(ads, res2); msg; msg = next) {
830 next = ads_next_message(ads, msg);
831 ldap_add_result_entry((LDAPMessage **)res, msg);
833 /* note that we do not free res2, as the memory is now
834 part of the main returned list */
837 DEBUG(0, ("no ldap_add_result_entry() support in LDAP libs!\n"));
838 status = ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
844 ADS_STATUS ads_do_search_all(ADS_STRUCT *ads, const char *bind_path,
845 int scope, const char *expr,
846 const char **attrs, LDAPMessage **res)
848 return ads_do_search_all_args(ads, bind_path, scope, expr, attrs, NULL, res);
852 * Run a function on all results for a search. Uses ads_do_paged_search() and
853 * runs the function as each page is returned, using ads_process_results()
854 * @param ads connection to ads server
855 * @param bind_path Base dn for the search
856 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
857 * @param expr Search expression - specified in local charset
858 * @param attrs Attributes to retrieve - specified in UTF-8 or ascii
859 * @param fn Function which takes attr name, values list, and data_area
860 * @param data_area Pointer which is passed to function on each call
861 * @return status of search
863 ADS_STATUS ads_do_search_all_fn(ADS_STRUCT *ads, const char *bind_path,
864 int scope, const char *expr, const char **attrs,
865 BOOL(*fn)(char *, void **, void *),
868 struct berval *cookie = NULL;
873 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs, &res,
876 if (!ADS_ERR_OK(status)) return status;
878 ads_process_results(ads, res, fn, data_area);
879 ads_msgfree(ads, res);
882 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs,
883 &res, &count, &cookie);
885 if (!ADS_ERR_OK(status)) break;
887 ads_process_results(ads, res, fn, data_area);
888 ads_msgfree(ads, res);
895 * Do a search with a timeout.
896 * @param ads connection to ads server
897 * @param bind_path Base dn for the search
898 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
899 * @param expr Search expression
900 * @param attrs Attributes to retrieve
901 * @param res ** which will contain results - free res* with ads_msgfree()
902 * @return status of search
904 ADS_STATUS ads_do_search(ADS_STRUCT *ads, const char *bind_path, int scope,
906 const char **attrs, LDAPMessage **res)
909 char *utf8_expr, *utf8_path, **search_attrs = NULL;
913 if (!(ctx = talloc_init("ads_do_search"))) {
914 DEBUG(1,("ads_do_search: talloc_init() failed!"));
915 return ADS_ERROR(LDAP_NO_MEMORY);
918 /* 0 means the conversion worked but the result was empty
919 so we only fail if it's negative. In any case, it always
920 at least nulls out the dest */
921 if ((push_utf8_talloc(ctx, &utf8_expr, expr) == (size_t)-1) ||
922 (push_utf8_talloc(ctx, &utf8_path, bind_path) == (size_t)-1)) {
923 DEBUG(1,("ads_do_search: push_utf8_talloc() failed!"));
928 if (!attrs || !(*attrs))
931 /* This would be the utf8-encoded version...*/
932 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
933 if (!(str_list_copy(&search_attrs, attrs)))
935 DEBUG(1,("ads_do_search: str_list_copy() failed!"));
941 /* see the note in ads_do_paged_search - we *must* disable referrals */
942 ldap_set_option(ads->ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
944 rc = ldap_search_with_timeout(ads->ld, utf8_path, scope, utf8_expr,
945 search_attrs, 0, NULL, NULL,
947 (LDAPMessage **)res);
949 if (rc == LDAP_SIZELIMIT_EXCEEDED) {
950 DEBUG(3,("Warning! sizelimit exceeded in ldap. Truncating.\n"));
956 /* if/when we decide to utf8-encode attrs, take out this next line */
957 str_list_free(&search_attrs);
958 return ADS_ERROR(rc);
961 * Do a general ADS search
962 * @param ads connection to ads server
963 * @param res ** which will contain results - free res* with ads_msgfree()
964 * @param expr Search expression
965 * @param attrs Attributes to retrieve
966 * @return status of search
968 ADS_STATUS ads_search(ADS_STRUCT *ads, LDAPMessage **res,
969 const char *expr, const char **attrs)
971 return ads_do_search(ads, ads->config.bind_path, LDAP_SCOPE_SUBTREE,
976 * Do a search on a specific DistinguishedName
977 * @param ads connection to ads server
978 * @param res ** which will contain results - free res* with ads_msgfree()
979 * @param dn DistinguishName to search
980 * @param attrs Attributes to retrieve
981 * @return status of search
983 ADS_STATUS ads_search_dn(ADS_STRUCT *ads, LDAPMessage **res,
984 const char *dn, const char **attrs)
986 return ads_do_search(ads, dn, LDAP_SCOPE_BASE, "(objectclass=*)",
991 * Free up memory from a ads_search
992 * @param ads connection to ads server
993 * @param msg Search results to free
995 void ads_msgfree(ADS_STRUCT *ads, LDAPMessage *msg)
1002 * Free up memory from various ads requests
1003 * @param ads connection to ads server
1004 * @param mem Area to free
1006 void ads_memfree(ADS_STRUCT *ads, void *mem)
1012 * Get a dn from search results
1013 * @param ads connection to ads server
1014 * @param msg Search result
1017 char *ads_get_dn(ADS_STRUCT *ads, LDAPMessage *msg)
1019 char *utf8_dn, *unix_dn;
1021 utf8_dn = ldap_get_dn(ads->ld, msg);
1024 DEBUG (5, ("ads_get_dn: ldap_get_dn failed\n"));
1028 if (pull_utf8_allocate(&unix_dn, utf8_dn) == (size_t)-1) {
1029 DEBUG(0,("ads_get_dn: string conversion failure utf8 [%s]\n",
1033 ldap_memfree(utf8_dn);
1038 * Get the parent from a dn
1039 * @param dn the dn to return the parent from
1040 * @return parent dn string
1042 char *ads_parent_dn(const char *dn)
1050 p = strchr(dn, ',');
1060 * Find a machine account given a hostname
1061 * @param ads connection to ads server
1062 * @param res ** which will contain results - free res* with ads_msgfree()
1063 * @param host Hostname to search for
1064 * @return status of search
1066 ADS_STATUS ads_find_machine_acct(ADS_STRUCT *ads, LDAPMessage **res,
1067 const char *machine)
1071 const char *attrs[] = {"*", "nTSecurityDescriptor", NULL};
1075 /* the easiest way to find a machine account anywhere in the tree
1076 is to look for hostname$ */
1077 if (asprintf(&expr, "(samAccountName=%s$)", machine) == -1) {
1078 DEBUG(1, ("asprintf failed!\n"));
1079 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1082 status = ads_search(ads, res, expr, attrs);
1088 * Initialize a list of mods to be used in a modify request
1089 * @param ctx An initialized TALLOC_CTX
1090 * @return allocated ADS_MODLIST
1092 ADS_MODLIST ads_init_mods(TALLOC_CTX *ctx)
1094 #define ADS_MODLIST_ALLOC_SIZE 10
1097 if ((mods = TALLOC_ZERO_ARRAY(ctx, LDAPMod *, ADS_MODLIST_ALLOC_SIZE + 1)))
1098 /* -1 is safety to make sure we don't go over the end.
1099 need to reset it to NULL before doing ldap modify */
1100 mods[ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1102 return (ADS_MODLIST)mods;
1107 add an attribute to the list, with values list already constructed
1109 static ADS_STATUS ads_modlist_add(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1110 int mod_op, const char *name,
1111 const void *_invals)
1113 const void **invals = (const void **)_invals;
1115 LDAPMod **modlist = (LDAPMod **) *mods;
1116 struct berval **ber_values = NULL;
1117 char **char_values = NULL;
1120 mod_op = LDAP_MOD_DELETE;
1122 if (mod_op & LDAP_MOD_BVALUES)
1123 ber_values = ads_dup_values(ctx,
1124 (const struct berval **)invals);
1126 char_values = ads_push_strvals(ctx,
1127 (const char **) invals);
1130 /* find the first empty slot */
1131 for (curmod=0; modlist[curmod] && modlist[curmod] != (LDAPMod *) -1;
1133 if (modlist[curmod] == (LDAPMod *) -1) {
1134 if (!(modlist = TALLOC_REALLOC_ARRAY(ctx, modlist, LDAPMod *,
1135 curmod+ADS_MODLIST_ALLOC_SIZE+1)))
1136 return ADS_ERROR(LDAP_NO_MEMORY);
1137 memset(&modlist[curmod], 0,
1138 ADS_MODLIST_ALLOC_SIZE*sizeof(LDAPMod *));
1139 modlist[curmod+ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1140 *mods = (ADS_MODLIST)modlist;
1143 if (!(modlist[curmod] = TALLOC_ZERO_P(ctx, LDAPMod)))
1144 return ADS_ERROR(LDAP_NO_MEMORY);
1145 modlist[curmod]->mod_type = talloc_strdup(ctx, name);
1146 if (mod_op & LDAP_MOD_BVALUES) {
1147 modlist[curmod]->mod_bvalues = ber_values;
1148 } else if (mod_op & LDAP_MOD_DELETE) {
1149 modlist[curmod]->mod_values = NULL;
1151 modlist[curmod]->mod_values = char_values;
1154 modlist[curmod]->mod_op = mod_op;
1155 return ADS_ERROR(LDAP_SUCCESS);
1159 * Add a single string value to a mod list
1160 * @param ctx An initialized TALLOC_CTX
1161 * @param mods An initialized ADS_MODLIST
1162 * @param name The attribute name to add
1163 * @param val The value to add - NULL means DELETE
1164 * @return ADS STATUS indicating success of add
1166 ADS_STATUS ads_mod_str(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1167 const char *name, const char *val)
1169 const char *values[2];
1175 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1176 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE, name, values);
1180 * Add an array of string values to a mod list
1181 * @param ctx An initialized TALLOC_CTX
1182 * @param mods An initialized ADS_MODLIST
1183 * @param name The attribute name to add
1184 * @param vals The array of string values to add - NULL means DELETE
1185 * @return ADS STATUS indicating success of add
1187 ADS_STATUS ads_mod_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1188 const char *name, const char **vals)
1191 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1192 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE,
1193 name, (const void **) vals);
1198 * Add a single ber-encoded value to a mod list
1199 * @param ctx An initialized TALLOC_CTX
1200 * @param mods An initialized ADS_MODLIST
1201 * @param name The attribute name to add
1202 * @param val The value to add - NULL means DELETE
1203 * @return ADS STATUS indicating success of add
1205 static ADS_STATUS ads_mod_ber(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1206 const char *name, const struct berval *val)
1208 const struct berval *values[2];
1213 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1214 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE|LDAP_MOD_BVALUES,
1215 name, (const void **) values);
1220 * Perform an ldap modify
1221 * @param ads connection to ads server
1222 * @param mod_dn DistinguishedName to modify
1223 * @param mods list of modifications to perform
1224 * @return status of modify
1226 ADS_STATUS ads_gen_mod(ADS_STRUCT *ads, const char *mod_dn, ADS_MODLIST mods)
1229 char *utf8_dn = NULL;
1231 this control is needed to modify that contains a currently
1232 non-existent attribute (but allowable for the object) to run
1234 LDAPControl PermitModify = {
1235 CONST_DISCARD(char *, ADS_PERMIT_MODIFY_OID),
1238 LDAPControl *controls[2];
1240 controls[0] = &PermitModify;
1243 if (push_utf8_allocate(&utf8_dn, mod_dn) == -1) {
1244 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1247 /* find the end of the list, marked by NULL or -1 */
1248 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1249 /* make sure the end of the list is NULL */
1251 ret = ldap_modify_ext_s(ads->ld, utf8_dn,
1252 (LDAPMod **) mods, controls, NULL);
1254 return ADS_ERROR(ret);
1258 * Perform an ldap add
1259 * @param ads connection to ads server
1260 * @param new_dn DistinguishedName to add
1261 * @param mods list of attributes and values for DN
1262 * @return status of add
1264 ADS_STATUS ads_gen_add(ADS_STRUCT *ads, const char *new_dn, ADS_MODLIST mods)
1267 char *utf8_dn = NULL;
1269 if (push_utf8_allocate(&utf8_dn, new_dn) == -1) {
1270 DEBUG(1, ("ads_gen_add: push_utf8_allocate failed!"));
1271 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1274 /* find the end of the list, marked by NULL or -1 */
1275 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1276 /* make sure the end of the list is NULL */
1279 ret = ldap_add_s(ads->ld, utf8_dn, (LDAPMod**)mods);
1281 return ADS_ERROR(ret);
1285 * Delete a DistinguishedName
1286 * @param ads connection to ads server
1287 * @param new_dn DistinguishedName to delete
1288 * @return status of delete
1290 ADS_STATUS ads_del_dn(ADS_STRUCT *ads, char *del_dn)
1293 char *utf8_dn = NULL;
1294 if (push_utf8_allocate(&utf8_dn, del_dn) == -1) {
1295 DEBUG(1, ("ads_del_dn: push_utf8_allocate failed!"));
1296 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1299 ret = ldap_delete_s(ads->ld, utf8_dn);
1301 return ADS_ERROR(ret);
1305 * Build an org unit string
1306 * if org unit is Computers or blank then assume a container, otherwise
1307 * assume a / separated list of organisational units.
1308 * jmcd: '\' is now used for escapes so certain chars can be in the ou (e.g. #)
1309 * @param ads connection to ads server
1310 * @param org_unit Organizational unit
1311 * @return org unit string - caller must free
1313 char *ads_ou_string(ADS_STRUCT *ads, const char *org_unit)
1317 if (!org_unit || !*org_unit) {
1319 ret = ads_default_ou_string(ads, WELL_KNOWN_GUID_COMPUTERS);
1321 /* samba4 might not yet respond to a wellknownobject-query */
1322 return ret ? ret : SMB_STRDUP("cn=Computers");
1325 if (strequal(org_unit, "Computers")) {
1326 return SMB_STRDUP("cn=Computers");
1329 /* jmcd: removed "\\" from the separation chars, because it is
1330 needed as an escape for chars like '#' which are valid in an
1332 return ads_build_path(org_unit, "/", "ou=", 1);
1336 * Get a org unit string for a well-known GUID
1337 * @param ads connection to ads server
1338 * @param wknguid Well known GUID
1339 * @return org unit string - caller must free
1341 char *ads_default_ou_string(ADS_STRUCT *ads, const char *wknguid)
1344 LDAPMessage *res = NULL;
1345 char *base, *wkn_dn = NULL, *ret = NULL, **wkn_dn_exp = NULL,
1346 **bind_dn_exp = NULL;
1347 const char *attrs[] = {"distinguishedName", NULL};
1348 int new_ln, wkn_ln, bind_ln, i;
1350 if (wknguid == NULL) {
1354 if (asprintf(&base, "<WKGUID=%s,%s>", wknguid, ads->config.bind_path ) == -1) {
1355 DEBUG(1, ("asprintf failed!\n"));
1359 status = ads_search_dn(ads, &res, base, attrs);
1360 if (!ADS_ERR_OK(status)) {
1361 DEBUG(1,("Failed while searching for: %s\n", base));
1365 if (ads_count_replies(ads, res) != 1) {
1369 /* substitute the bind-path from the well-known-guid-search result */
1370 wkn_dn = ads_get_dn(ads, res);
1375 wkn_dn_exp = ldap_explode_dn(wkn_dn, 0);
1380 bind_dn_exp = ldap_explode_dn(ads->config.bind_path, 0);
1385 for (wkn_ln=0; wkn_dn_exp[wkn_ln]; wkn_ln++)
1387 for (bind_ln=0; bind_dn_exp[bind_ln]; bind_ln++)
1390 new_ln = wkn_ln - bind_ln;
1392 ret = SMB_STRDUP(wkn_dn_exp[0]);
1397 for (i=1; i < new_ln; i++) {
1400 if (asprintf(&s, "%s,%s", ret, wkn_dn_exp[i]) == -1) {
1406 ret = SMB_STRDUP(s);
1415 ads_msgfree(ads, res);
1416 ads_memfree(ads, wkn_dn);
1418 ldap_value_free(wkn_dn_exp);
1421 ldap_value_free(bind_dn_exp);
1428 * Adds (appends) an item to an attribute array, rather then
1429 * replacing the whole list
1430 * @param ctx An initialized TALLOC_CTX
1431 * @param mods An initialized ADS_MODLIST
1432 * @param name name of the ldap attribute to append to
1433 * @param vals an array of values to add
1434 * @return status of addition
1437 ADS_STATUS ads_add_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1438 const char *name, const char **vals)
1440 return ads_modlist_add(ctx, mods, LDAP_MOD_ADD, name,
1441 (const void *) vals);
1445 * Determines the computer account's current KVNO via an LDAP lookup
1446 * @param ads An initialized ADS_STRUCT
1447 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1448 * @return the kvno for the computer account, or -1 in case of a failure.
1451 uint32 ads_get_kvno(ADS_STRUCT *ads, const char *machine_name)
1453 LDAPMessage *res = NULL;
1454 uint32 kvno = (uint32)-1; /* -1 indicates a failure */
1456 const char *attrs[] = {"msDS-KeyVersionNumber", NULL};
1457 char *dn_string = NULL;
1458 ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS);
1460 DEBUG(5,("ads_get_kvno: Searching for host %s\n", machine_name));
1461 if (asprintf(&filter, "(samAccountName=%s$)", machine_name) == -1) {
1464 ret = ads_search(ads, &res, filter, attrs);
1466 if (!ADS_ERR_OK(ret) && ads_count_replies(ads, res)) {
1467 DEBUG(1,("ads_get_kvno: Computer Account For %s not found.\n", machine_name));
1468 ads_msgfree(ads, res);
1472 dn_string = ads_get_dn(ads, res);
1474 DEBUG(0,("ads_get_kvno: out of memory.\n"));
1475 ads_msgfree(ads, res);
1478 DEBUG(5,("ads_get_kvno: Using: %s\n", dn_string));
1479 ads_memfree(ads, dn_string);
1481 /* ---------------------------------------------------------
1482 * 0 is returned as a default KVNO from this point on...
1483 * This is done because Windows 2000 does not support key
1484 * version numbers. Chances are that a failure in the next
1485 * step is simply due to Windows 2000 being used for a
1486 * domain controller. */
1489 if (!ads_pull_uint32(ads, res, "msDS-KeyVersionNumber", &kvno)) {
1490 DEBUG(3,("ads_get_kvno: Error Determining KVNO!\n"));
1491 DEBUG(3,("ads_get_kvno: Windows 2000 does not support KVNO's, so this may be normal.\n"));
1492 ads_msgfree(ads, res);
1497 DEBUG(5,("ads_get_kvno: Looked Up KVNO of: %d\n", kvno));
1498 ads_msgfree(ads, res);
1503 * This clears out all registered spn's for a given hostname
1504 * @param ads An initilaized ADS_STRUCT
1505 * @param machine_name the NetBIOS name of the computer.
1506 * @return 0 upon success, non-zero otherwise.
1509 ADS_STATUS ads_clear_service_principal_names(ADS_STRUCT *ads, const char *machine_name)
1512 LDAPMessage *res = NULL;
1514 const char *servicePrincipalName[1] = {NULL};
1515 ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS);
1516 char *dn_string = NULL;
1518 ret = ads_find_machine_acct(ads, &res, machine_name);
1519 if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
1520 DEBUG(5,("ads_clear_service_principal_names: WARNING: Host Account for %s not found... skipping operation.\n", machine_name));
1521 DEBUG(5,("ads_clear_service_principal_names: WARNING: Service Principals for %s have NOT been cleared.\n", machine_name));
1522 ads_msgfree(ads, res);
1523 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1526 DEBUG(5,("ads_clear_service_principal_names: Host account for %s found\n", machine_name));
1527 ctx = talloc_init("ads_clear_service_principal_names");
1529 ads_msgfree(ads, res);
1530 return ADS_ERROR(LDAP_NO_MEMORY);
1533 if (!(mods = ads_init_mods(ctx))) {
1534 talloc_destroy(ctx);
1535 ads_msgfree(ads, res);
1536 return ADS_ERROR(LDAP_NO_MEMORY);
1538 ret = ads_mod_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1539 if (!ADS_ERR_OK(ret)) {
1540 DEBUG(1,("ads_clear_service_principal_names: Error creating strlist.\n"));
1541 ads_msgfree(ads, res);
1542 talloc_destroy(ctx);
1545 dn_string = ads_get_dn(ads, res);
1547 talloc_destroy(ctx);
1548 ads_msgfree(ads, res);
1549 return ADS_ERROR(LDAP_NO_MEMORY);
1551 ret = ads_gen_mod(ads, dn_string, mods);
1552 ads_memfree(ads,dn_string);
1553 if (!ADS_ERR_OK(ret)) {
1554 DEBUG(1,("ads_clear_service_principal_names: Error: Updating Service Principals for machine %s in LDAP\n",
1556 ads_msgfree(ads, res);
1557 talloc_destroy(ctx);
1561 ads_msgfree(ads, res);
1562 talloc_destroy(ctx);
1567 * This adds a service principal name to an existing computer account
1568 * (found by hostname) in AD.
1569 * @param ads An initialized ADS_STRUCT
1570 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1571 * @param my_fqdn The fully qualified DNS name of the machine
1572 * @param spn A string of the service principal to add, i.e. 'host'
1573 * @return 0 upon sucess, or non-zero if a failure occurs
1576 ADS_STATUS ads_add_service_principal_name(ADS_STRUCT *ads, const char *machine_name,
1577 const char *my_fqdn, const char *spn)
1581 LDAPMessage *res = NULL;
1584 char *dn_string = NULL;
1585 const char *servicePrincipalName[3] = {NULL, NULL, NULL};
1587 ret = ads_find_machine_acct(ads, &res, machine_name);
1588 if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
1589 DEBUG(1,("ads_add_service_principal_name: WARNING: Host Account for %s not found... skipping operation.\n",
1591 DEBUG(1,("ads_add_service_principal_name: WARNING: Service Principal '%s/%s@%s' has NOT been added.\n",
1592 spn, machine_name, ads->config.realm));
1593 ads_msgfree(ads, res);
1594 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1597 DEBUG(1,("ads_add_service_principal_name: Host account for %s found\n", machine_name));
1598 if (!(ctx = talloc_init("ads_add_service_principal_name"))) {
1599 ads_msgfree(ads, res);
1600 return ADS_ERROR(LDAP_NO_MEMORY);
1603 /* add short name spn */
1605 if ( (psp1 = talloc_asprintf(ctx, "%s/%s", spn, machine_name)) == NULL ) {
1606 talloc_destroy(ctx);
1607 ads_msgfree(ads, res);
1608 return ADS_ERROR(LDAP_NO_MEMORY);
1611 strlower_m(&psp1[strlen(spn)]);
1612 servicePrincipalName[0] = psp1;
1614 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n",
1615 psp1, machine_name));
1618 /* add fully qualified spn */
1620 if ( (psp2 = talloc_asprintf(ctx, "%s/%s", spn, my_fqdn)) == NULL ) {
1621 ret = ADS_ERROR(LDAP_NO_MEMORY);
1625 strlower_m(&psp2[strlen(spn)]);
1626 servicePrincipalName[1] = psp2;
1628 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n",
1629 psp2, machine_name));
1631 if ( (mods = ads_init_mods(ctx)) == NULL ) {
1632 ret = ADS_ERROR(LDAP_NO_MEMORY);
1636 ret = ads_add_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1637 if (!ADS_ERR_OK(ret)) {
1638 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
1642 if ( (dn_string = ads_get_dn(ads, res)) == NULL ) {
1643 ret = ADS_ERROR(LDAP_NO_MEMORY);
1647 ret = ads_gen_mod(ads, dn_string, mods);
1648 ads_memfree(ads,dn_string);
1649 if (!ADS_ERR_OK(ret)) {
1650 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
1656 ads_msgfree(ads, res);
1661 * adds a machine account to the ADS server
1662 * @param ads An intialized ADS_STRUCT
1663 * @param machine_name - the NetBIOS machine name of this account.
1664 * @param account_type A number indicating the type of account to create
1665 * @param org_unit The LDAP path in which to place this account
1666 * @return 0 upon success, or non-zero otherwise
1669 ADS_STATUS ads_create_machine_acct(ADS_STRUCT *ads, const char *machine_name,
1670 const char *org_unit)
1673 char *samAccountName, *controlstr;
1676 char *machine_escaped = NULL;
1678 const char *objectClass[] = {"top", "person", "organizationalPerson",
1679 "user", "computer", NULL};
1680 LDAPMessage *res = NULL;
1681 uint32 acct_control = ( UF_WORKSTATION_TRUST_ACCOUNT |\
1682 UF_DONT_EXPIRE_PASSWD |\
1683 UF_ACCOUNTDISABLE );
1685 if (!(ctx = talloc_init("ads_add_machine_acct")))
1686 return ADS_ERROR(LDAP_NO_MEMORY);
1688 ret = ADS_ERROR(LDAP_NO_MEMORY);
1690 machine_escaped = escape_rdn_val_string_alloc(machine_name);
1691 if (!machine_escaped) {
1695 new_dn = talloc_asprintf(ctx, "cn=%s,%s", machine_escaped, org_unit);
1696 samAccountName = talloc_asprintf(ctx, "%s$", machine_name);
1698 if ( !new_dn || !samAccountName ) {
1702 #ifndef ENCTYPE_ARCFOUR_HMAC
1703 acct_control |= UF_USE_DES_KEY_ONLY;
1706 if (!(controlstr = talloc_asprintf(ctx, "%u", acct_control))) {
1710 if (!(mods = ads_init_mods(ctx))) {
1714 ads_mod_str(ctx, &mods, "cn", machine_name);
1715 ads_mod_str(ctx, &mods, "sAMAccountName", samAccountName);
1716 ads_mod_strlist(ctx, &mods, "objectClass", objectClass);
1717 ads_mod_str(ctx, &mods, "userAccountControl", controlstr);
1719 ret = ads_gen_add(ads, new_dn, mods);
1722 SAFE_FREE(machine_escaped);
1723 ads_msgfree(ads, res);
1724 talloc_destroy(ctx);
1730 dump a binary result from ldap
1732 static void dump_binary(const char *field, struct berval **values)
1735 for (i=0; values[i]; i++) {
1736 printf("%s: ", field);
1737 for (j=0; j<values[i]->bv_len; j++) {
1738 printf("%02X", (unsigned char)values[i]->bv_val[j]);
1744 static void dump_guid(const char *field, struct berval **values)
1748 for (i=0; values[i]; i++) {
1749 memcpy(guid.info, values[i]->bv_val, sizeof(guid.info));
1750 printf("%s: %s\n", field,
1751 smb_uuid_string_static(smb_uuid_unpack_static(guid)));
1756 dump a sid result from ldap
1758 static void dump_sid(const char *field, struct berval **values)
1761 for (i=0; values[i]; i++) {
1763 sid_parse(values[i]->bv_val, values[i]->bv_len, &sid);
1764 printf("%s: %s\n", field, sid_string_static(&sid));
1769 dump ntSecurityDescriptor
1771 static void dump_sd(const char *filed, struct berval **values)
1776 TALLOC_CTX *ctx = 0;
1778 if (!(ctx = talloc_init("sec_io_desc")))
1782 prs_init(&ps, values[0]->bv_len, ctx, UNMARSHALL);
1783 prs_copy_data_in(&ps, values[0]->bv_val, values[0]->bv_len);
1784 prs_set_offset(&ps,0);
1787 if (!sec_io_desc("sd", &psd, &ps, 1)) {
1789 talloc_destroy(ctx);
1792 if (psd) ads_disp_sd(psd);
1795 talloc_destroy(ctx);
1799 dump a string result from ldap
1801 static void dump_string(const char *field, char **values)
1804 for (i=0; values[i]; i++) {
1805 printf("%s: %s\n", field, values[i]);
1810 dump a field from LDAP on stdout
1814 static BOOL ads_dump_field(char *field, void **values, void *data_area)
1819 void (*handler)(const char *, struct berval **);
1821 {"objectGUID", False, dump_guid},
1822 {"netbootGUID", False, dump_guid},
1823 {"nTSecurityDescriptor", False, dump_sd},
1824 {"dnsRecord", False, dump_binary},
1825 {"objectSid", False, dump_sid},
1826 {"tokenGroups", False, dump_sid},
1827 {"tokenGroupsNoGCAcceptable", False, dump_sid},
1828 {"tokengroupsGlobalandUniversal", False, dump_sid},
1829 {"mS-DS-CreatorSID", False, dump_sid},
1834 if (!field) { /* must be end of an entry */
1839 for (i=0; handlers[i].name; i++) {
1840 if (StrCaseCmp(handlers[i].name, field) == 0) {
1841 if (!values) /* first time, indicate string or not */
1842 return handlers[i].string;
1843 handlers[i].handler(field, (struct berval **) values);
1847 if (!handlers[i].name) {
1848 if (!values) /* first time, indicate string conversion */
1850 dump_string(field, (char **)values);
1856 * Dump a result from LDAP on stdout
1857 * used for debugging
1858 * @param ads connection to ads server
1859 * @param res Results to dump
1862 void ads_dump(ADS_STRUCT *ads, LDAPMessage *res)
1864 ads_process_results(ads, res, ads_dump_field, NULL);
1868 * Walk through results, calling a function for each entry found.
1869 * The function receives a field name, a berval * array of values,
1870 * and a data area passed through from the start. The function is
1871 * called once with null for field and values at the end of each
1873 * @param ads connection to ads server
1874 * @param res Results to process
1875 * @param fn Function for processing each result
1876 * @param data_area user-defined area to pass to function
1878 void ads_process_results(ADS_STRUCT *ads, LDAPMessage *res,
1879 BOOL(*fn)(char *, void **, void *),
1885 if (!(ctx = talloc_init("ads_process_results")))
1888 for (msg = ads_first_entry(ads, res); msg;
1889 msg = ads_next_entry(ads, msg)) {
1893 for (utf8_field=ldap_first_attribute(ads->ld,
1894 (LDAPMessage *)msg,&b);
1896 utf8_field=ldap_next_attribute(ads->ld,
1897 (LDAPMessage *)msg,b)) {
1898 struct berval **ber_vals;
1899 char **str_vals, **utf8_vals;
1903 pull_utf8_talloc(ctx, &field, utf8_field);
1904 string = fn(field, NULL, data_area);
1907 utf8_vals = ldap_get_values(ads->ld,
1908 (LDAPMessage *)msg, field);
1909 str_vals = ads_pull_strvals(ctx,
1910 (const char **) utf8_vals);
1911 fn(field, (void **) str_vals, data_area);
1912 ldap_value_free(utf8_vals);
1914 ber_vals = ldap_get_values_len(ads->ld,
1915 (LDAPMessage *)msg, field);
1916 fn(field, (void **) ber_vals, data_area);
1918 ldap_value_free_len(ber_vals);
1920 ldap_memfree(utf8_field);
1923 talloc_free_children(ctx);
1924 fn(NULL, NULL, data_area); /* completed an entry */
1927 talloc_destroy(ctx);
1931 * count how many replies are in a LDAPMessage
1932 * @param ads connection to ads server
1933 * @param res Results to count
1934 * @return number of replies
1936 int ads_count_replies(ADS_STRUCT *ads, void *res)
1938 return ldap_count_entries(ads->ld, (LDAPMessage *)res);
1942 * pull the first entry from a ADS result
1943 * @param ads connection to ads server
1944 * @param res Results of search
1945 * @return first entry from result
1947 LDAPMessage *ads_first_entry(ADS_STRUCT *ads, LDAPMessage *res)
1949 return ldap_first_entry(ads->ld, res);
1953 * pull the next entry from a ADS result
1954 * @param ads connection to ads server
1955 * @param res Results of search
1956 * @return next entry from result
1958 LDAPMessage *ads_next_entry(ADS_STRUCT *ads, LDAPMessage *res)
1960 return ldap_next_entry(ads->ld, res);
1964 * pull the first message from a ADS result
1965 * @param ads connection to ads server
1966 * @param res Results of search
1967 * @return first message from result
1969 LDAPMessage *ads_first_message(ADS_STRUCT *ads, LDAPMessage *res)
1971 return ldap_first_message(ads->ld, res);
1975 * pull the next message from a ADS result
1976 * @param ads connection to ads server
1977 * @param res Results of search
1978 * @return next message from result
1980 LDAPMessage *ads_next_message(ADS_STRUCT *ads, LDAPMessage *res)
1982 return ldap_next_message(ads->ld, res);
1986 * pull a single string from a ADS result
1987 * @param ads connection to ads server
1988 * @param mem_ctx TALLOC_CTX to use for allocating result string
1989 * @param msg Results of search
1990 * @param field Attribute to retrieve
1991 * @return Result string in talloc context
1993 char *ads_pull_string(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, LDAPMessage *msg,
2001 values = ldap_get_values(ads->ld, msg, field);
2006 rc = pull_utf8_talloc(mem_ctx, &ux_string,
2008 if (rc != (size_t)-1)
2012 ldap_value_free(values);
2017 * pull an array of strings from a ADS result
2018 * @param ads connection to ads server
2019 * @param mem_ctx TALLOC_CTX to use for allocating result string
2020 * @param msg Results of search
2021 * @param field Attribute to retrieve
2022 * @return Result strings in talloc context
2024 char **ads_pull_strings(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2025 LDAPMessage *msg, const char *field,
2032 values = ldap_get_values(ads->ld, msg, field);
2036 *num_values = ldap_count_values(values);
2038 ret = TALLOC_ARRAY(mem_ctx, char *, *num_values + 1);
2040 ldap_value_free(values);
2044 for (i=0;i<*num_values;i++) {
2045 if (pull_utf8_talloc(mem_ctx, &ret[i], values[i]) == -1) {
2046 ldap_value_free(values);
2052 ldap_value_free(values);
2057 * pull an array of strings from a ADS result
2058 * (handle large multivalue attributes with range retrieval)
2059 * @param ads connection to ads server
2060 * @param mem_ctx TALLOC_CTX to use for allocating result string
2061 * @param msg Results of search
2062 * @param field Attribute to retrieve
2063 * @param current_strings strings returned by a previous call to this function
2064 * @param next_attribute The next query should ask for this attribute
2065 * @param num_values How many values did we get this time?
2066 * @param more_values Are there more values to get?
2067 * @return Result strings in talloc context
2069 char **ads_pull_strings_range(ADS_STRUCT *ads,
2070 TALLOC_CTX *mem_ctx,
2071 LDAPMessage *msg, const char *field,
2072 char **current_strings,
2073 const char **next_attribute,
2074 size_t *num_strings,
2078 char *expected_range_attrib, *range_attr;
2079 BerElement *ptr = NULL;
2082 size_t num_new_strings;
2083 unsigned long int range_start;
2084 unsigned long int range_end;
2086 /* we might have been given the whole lot anyway */
2087 if ((strings = ads_pull_strings(ads, mem_ctx, msg, field, num_strings))) {
2088 *more_strings = False;
2092 expected_range_attrib = talloc_asprintf(mem_ctx, "%s;Range=", field);
2094 /* look for Range result */
2095 for (attr = ldap_first_attribute(ads->ld, (LDAPMessage *)msg, &ptr);
2097 attr = ldap_next_attribute(ads->ld, (LDAPMessage *)msg, ptr)) {
2098 /* we ignore the fact that this is utf8, as all attributes are ascii... */
2099 if (strnequal(attr, expected_range_attrib, strlen(expected_range_attrib))) {
2107 /* nothing here - this field is just empty */
2108 *more_strings = False;
2112 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-%lu",
2113 &range_start, &range_end) == 2) {
2114 *more_strings = True;
2116 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-*",
2117 &range_start) == 1) {
2118 *more_strings = False;
2120 DEBUG(1, ("ads_pull_strings_range: Cannot parse Range attriubte (%s)\n",
2122 ldap_memfree(range_attr);
2123 *more_strings = False;
2128 if ((*num_strings) != range_start) {
2129 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) doesn't start at %u, but at %lu"
2130 " - aborting range retreival\n",
2131 range_attr, (unsigned int)(*num_strings) + 1, range_start));
2132 ldap_memfree(range_attr);
2133 *more_strings = False;
2137 new_strings = ads_pull_strings(ads, mem_ctx, msg, range_attr, &num_new_strings);
2139 if (*more_strings && ((*num_strings + num_new_strings) != (range_end + 1))) {
2140 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) tells us we have %lu "
2141 "strings in this bunch, but we only got %lu - aborting range retreival\n",
2142 range_attr, (unsigned long int)range_end - range_start + 1,
2143 (unsigned long int)num_new_strings));
2144 ldap_memfree(range_attr);
2145 *more_strings = False;
2149 strings = TALLOC_REALLOC_ARRAY(mem_ctx, current_strings, char *,
2150 *num_strings + num_new_strings);
2152 if (strings == NULL) {
2153 ldap_memfree(range_attr);
2154 *more_strings = False;
2158 if (new_strings && num_new_strings) {
2159 memcpy(&strings[*num_strings], new_strings,
2160 sizeof(*new_strings) * num_new_strings);
2163 (*num_strings) += num_new_strings;
2165 if (*more_strings) {
2166 *next_attribute = talloc_asprintf(mem_ctx,
2171 if (!*next_attribute) {
2172 DEBUG(1, ("talloc_asprintf for next attribute failed!\n"));
2173 ldap_memfree(range_attr);
2174 *more_strings = False;
2179 ldap_memfree(range_attr);
2185 * pull a single uint32 from a ADS result
2186 * @param ads connection to ads server
2187 * @param msg Results of search
2188 * @param field Attribute to retrieve
2189 * @param v Pointer to int to store result
2190 * @return boolean inidicating success
2192 BOOL ads_pull_uint32(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
2197 values = ldap_get_values(ads->ld, msg, field);
2201 ldap_value_free(values);
2205 *v = atoi(values[0]);
2206 ldap_value_free(values);
2211 * pull a single objectGUID from an ADS result
2212 * @param ads connection to ADS server
2213 * @param msg results of search
2214 * @param guid 37-byte area to receive text guid
2215 * @return boolean indicating success
2217 BOOL ads_pull_guid(ADS_STRUCT *ads, LDAPMessage *msg, struct GUID *guid)
2220 UUID_FLAT flat_guid;
2222 values = ldap_get_values(ads->ld, msg, "objectGUID");
2227 memcpy(&flat_guid.info, values[0], sizeof(UUID_FLAT));
2228 smb_uuid_unpack(flat_guid, guid);
2229 ldap_value_free(values);
2232 ldap_value_free(values);
2239 * pull a single DOM_SID from a ADS result
2240 * @param ads connection to ads server
2241 * @param msg Results of search
2242 * @param field Attribute to retrieve
2243 * @param sid Pointer to sid to store result
2244 * @return boolean inidicating success
2246 BOOL ads_pull_sid(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
2249 struct berval **values;
2252 values = ldap_get_values_len(ads->ld, msg, field);
2258 ret = sid_parse(values[0]->bv_val, values[0]->bv_len, sid);
2260 ldap_value_free_len(values);
2265 * pull an array of DOM_SIDs from a ADS result
2266 * @param ads connection to ads server
2267 * @param mem_ctx TALLOC_CTX for allocating sid array
2268 * @param msg Results of search
2269 * @param field Attribute to retrieve
2270 * @param sids pointer to sid array to allocate
2271 * @return the count of SIDs pulled
2273 int ads_pull_sids(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2274 LDAPMessage *msg, const char *field, DOM_SID **sids)
2276 struct berval **values;
2280 values = ldap_get_values_len(ads->ld, msg, field);
2285 for (i=0; values[i]; i++)
2289 (*sids) = TALLOC_ARRAY(mem_ctx, DOM_SID, i);
2291 ldap_value_free_len(values);
2299 for (i=0; values[i]; i++) {
2300 ret = sid_parse(values[i]->bv_val, values[i]->bv_len, &(*sids)[count]);
2303 DEBUG(10, ("pulling SID: %s\n", sid_to_string(sid, &(*sids)[count])));
2308 ldap_value_free_len(values);
2313 * pull a SEC_DESC from a ADS result
2314 * @param ads connection to ads server
2315 * @param mem_ctx TALLOC_CTX for allocating sid array
2316 * @param msg Results of search
2317 * @param field Attribute to retrieve
2318 * @param sd Pointer to *SEC_DESC to store result (talloc()ed)
2319 * @return boolean inidicating success
2321 BOOL ads_pull_sd(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2322 LDAPMessage *msg, const char *field, SEC_DESC **sd)
2324 struct berval **values;
2327 values = ldap_get_values_len(ads->ld, msg, field);
2329 if (!values) return False;
2333 prs_init(&ps, values[0]->bv_len, mem_ctx, UNMARSHALL);
2334 prs_copy_data_in(&ps, values[0]->bv_val, values[0]->bv_len);
2335 prs_set_offset(&ps,0);
2337 ret = sec_io_desc("sd", sd, &ps, 1);
2341 ldap_value_free_len(values);
2346 * in order to support usernames longer than 21 characters we need to
2347 * use both the sAMAccountName and the userPrincipalName attributes
2348 * It seems that not all users have the userPrincipalName attribute set
2350 * @param ads connection to ads server
2351 * @param mem_ctx TALLOC_CTX for allocating sid array
2352 * @param msg Results of search
2353 * @return the username
2355 char *ads_pull_username(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2361 /* lookup_name() only works on the sAMAccountName to
2362 returning the username portion of userPrincipalName
2363 breaks winbindd_getpwnam() */
2365 ret = ads_pull_string(ads, mem_ctx, msg, "userPrincipalName");
2366 if (ret && (p = strchr_m(ret, '@'))) {
2371 return ads_pull_string(ads, mem_ctx, msg, "sAMAccountName");
2376 * find the update serial number - this is the core of the ldap cache
2377 * @param ads connection to ads server
2378 * @param ads connection to ADS server
2379 * @param usn Pointer to retrieved update serial number
2380 * @return status of search
2382 ADS_STATUS ads_USN(ADS_STRUCT *ads, uint32 *usn)
2384 const char *attrs[] = {"highestCommittedUSN", NULL};
2388 status = ads_do_search_retry(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2389 if (!ADS_ERR_OK(status))
2392 if (ads_count_replies(ads, res) != 1) {
2393 ads_msgfree(ads, res);
2394 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2397 if (!ads_pull_uint32(ads, res, "highestCommittedUSN", usn)) {
2398 ads_msgfree(ads, res);
2399 return ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
2402 ads_msgfree(ads, res);
2406 /* parse a ADS timestring - typical string is
2407 '20020917091222.0Z0' which means 09:12.22 17th September
2409 static time_t ads_parse_time(const char *str)
2415 if (sscanf(str, "%4d%2d%2d%2d%2d%2d",
2416 &tm.tm_year, &tm.tm_mon, &tm.tm_mday,
2417 &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) {
2426 /********************************************************************
2427 ********************************************************************/
2429 ADS_STATUS ads_current_time(ADS_STRUCT *ads)
2431 const char *attrs[] = {"currentTime", NULL};
2436 ADS_STRUCT *ads_s = ads;
2438 if (!(ctx = talloc_init("ads_current_time"))) {
2439 return ADS_ERROR(LDAP_NO_MEMORY);
2442 /* establish a new ldap tcp session if necessary */
2445 if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup,
2446 ads->server.ldap_server )) == NULL )
2450 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
2451 status = ads_connect( ads_s );
2452 if ( !ADS_ERR_OK(status))
2456 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2457 if (!ADS_ERR_OK(status)) {
2461 timestr = ads_pull_string(ads_s, ctx, res, "currentTime");
2463 ads_msgfree(ads_s, res);
2464 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2468 /* but save the time and offset in the original ADS_STRUCT */
2470 ads->config.current_time = ads_parse_time(timestr);
2472 if (ads->config.current_time != 0) {
2473 ads->auth.time_offset = ads->config.current_time - time(NULL);
2474 DEBUG(4,("time offset is %d seconds\n", ads->auth.time_offset));
2477 ads_msgfree(ads, res);
2479 status = ADS_SUCCESS;
2482 /* free any temporary ads connections */
2483 if ( ads_s != ads ) {
2484 ads_destroy( &ads_s );
2486 talloc_destroy(ctx);
2491 /********************************************************************
2492 ********************************************************************/
2494 ADS_STATUS ads_domain_func_level(ADS_STRUCT *ads, uint32 *val)
2496 const char *attrs[] = {"domainFunctionality", NULL};
2499 ADS_STRUCT *ads_s = ads;
2501 *val = DS_DOMAIN_FUNCTION_2000;
2503 /* establish a new ldap tcp session if necessary */
2506 if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup,
2507 ads->server.ldap_server )) == NULL )
2511 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
2512 status = ads_connect( ads_s );
2513 if ( !ADS_ERR_OK(status))
2517 /* If the attribute does not exist assume it is a Windows 2000
2518 functional domain */
2520 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2521 if (!ADS_ERR_OK(status)) {
2522 if ( status.err.rc == LDAP_NO_SUCH_ATTRIBUTE ) {
2523 status = ADS_SUCCESS;
2528 if ( !ads_pull_uint32(ads_s, res, "domainFunctionality", val) ) {
2529 DEBUG(5,("ads_domain_func_level: Failed to pull the domainFunctionality attribute.\n"));
2531 DEBUG(3,("ads_domain_func_level: %d\n", *val));
2534 ads_msgfree(ads, res);
2537 /* free any temporary ads connections */
2538 if ( ads_s != ads ) {
2539 ads_destroy( &ads_s );
2546 * find the domain sid for our domain
2547 * @param ads connection to ads server
2548 * @param sid Pointer to domain sid
2549 * @return status of search
2551 ADS_STATUS ads_domain_sid(ADS_STRUCT *ads, DOM_SID *sid)
2553 const char *attrs[] = {"objectSid", NULL};
2557 rc = ads_do_search_retry(ads, ads->config.bind_path, LDAP_SCOPE_BASE, "(objectclass=*)",
2559 if (!ADS_ERR_OK(rc)) return rc;
2560 if (!ads_pull_sid(ads, res, "objectSid", sid)) {
2561 ads_msgfree(ads, res);
2562 return ADS_ERROR_SYSTEM(ENOENT);
2564 ads_msgfree(ads, res);
2570 * find our site name
2571 * @param ads connection to ads server
2572 * @param mem_ctx Pointer to talloc context
2573 * @param site_name Pointer to the sitename
2574 * @return status of search
2576 ADS_STATUS ads_site_dn(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char **site_name)
2580 const char *dn, *service_name;
2581 const char *attrs[] = { "dsServiceName", NULL };
2583 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2584 if (!ADS_ERR_OK(status)) {
2588 service_name = ads_pull_string(ads, mem_ctx, res, "dsServiceName");
2589 if (service_name == NULL) {
2590 ads_msgfree(ads, res);
2591 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2594 ads_msgfree(ads, res);
2596 /* go up three levels */
2597 dn = ads_parent_dn(ads_parent_dn(ads_parent_dn(service_name)));
2599 return ADS_ERROR(LDAP_NO_MEMORY);
2602 *site_name = talloc_strdup(mem_ctx, dn);
2603 if (*site_name == NULL) {
2604 return ADS_ERROR(LDAP_NO_MEMORY);
2609 dsServiceName: CN=NTDS Settings,CN=W2K3DC,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,DC=ber,DC=suse,DC=de
2614 * find the site dn where a machine resides
2615 * @param ads connection to ads server
2616 * @param mem_ctx Pointer to talloc context
2617 * @param computer_name name of the machine
2618 * @param site_name Pointer to the sitename
2619 * @return status of search
2621 ADS_STATUS ads_site_dn_for_machine(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char *computer_name, const char **site_dn)
2625 const char *parent, *config_context, *filter;
2626 const char *attrs[] = { "configurationNamingContext", NULL };
2629 /* shortcut a query */
2630 if (strequal(computer_name, ads->config.ldap_server_name)) {
2631 return ads_site_dn(ads, mem_ctx, site_dn);
2634 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2635 if (!ADS_ERR_OK(status)) {
2639 config_context = ads_pull_string(ads, mem_ctx, res, "configurationNamingContext");
2640 if (config_context == NULL) {
2641 ads_msgfree(ads, res);
2642 return ADS_ERROR(LDAP_NO_MEMORY);
2645 filter = talloc_asprintf(mem_ctx, "(cn=%s)", computer_name);
2646 if (filter == NULL) {
2647 ads_msgfree(ads, res);
2648 return ADS_ERROR(LDAP_NO_MEMORY);
2651 ads_msgfree(ads, res);
2653 status = ads_do_search(ads, config_context, LDAP_SCOPE_SUBTREE, filter, NULL, &res);
2654 if (!ADS_ERR_OK(status)) {
2658 if (ads_count_replies(ads, res) != 1) {
2659 ads_msgfree(ads, res);
2660 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
2663 dn = ads_get_dn(ads, res);
2665 ads_msgfree(ads, res);
2666 return ADS_ERROR(LDAP_NO_MEMORY);
2669 /* go up three levels */
2670 parent = ads_parent_dn(ads_parent_dn(ads_parent_dn(dn)));
2671 if (parent == NULL) {
2672 ads_msgfree(ads, res);
2673 ads_memfree(ads, dn);
2674 return ADS_ERROR(LDAP_NO_MEMORY);
2677 *site_dn = talloc_strdup(mem_ctx, parent);
2678 if (*site_dn == NULL) {
2679 ads_msgfree(ads, res);
2680 ads_memfree(ads, dn);
2681 return ADS_ERROR(LDAP_NO_MEMORY);
2684 ads_memfree(ads, dn);
2685 ads_msgfree(ads, res);
2691 * get the upn suffixes for a domain
2692 * @param ads connection to ads server
2693 * @param mem_ctx Pointer to talloc context
2694 * @param suffixes Pointer to an array of suffixes
2695 * @param num_suffixes Pointer to the number of suffixes
2696 * @return status of search
2698 ADS_STATUS ads_upn_suffixes(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, char ***suffixes, size_t *num_suffixes)
2702 const char *config_context, *base;
2703 const char *attrs[] = { "configurationNamingContext", NULL };
2704 const char *attrs2[] = { "uPNSuffixes", NULL };
2706 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2707 if (!ADS_ERR_OK(status)) {
2711 config_context = ads_pull_string(ads, mem_ctx, res, "configurationNamingContext");
2712 if (config_context == NULL) {
2713 ads_msgfree(ads, res);
2714 return ADS_ERROR(LDAP_NO_MEMORY);
2717 ads_msgfree(ads, res);
2719 base = talloc_asprintf(mem_ctx, "cn=Partitions,%s", config_context);
2721 return ADS_ERROR(LDAP_NO_MEMORY);
2724 status = ads_search_dn(ads, &res, base, attrs2);
2725 if (!ADS_ERR_OK(status)) {
2729 if (ads_count_replies(ads, res) != 1) {
2730 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
2733 (*suffixes) = ads_pull_strings(ads, mem_ctx, res, "uPNSuffixes", num_suffixes);
2734 if ((*suffixes) == NULL) {
2735 ads_msgfree(ads, res);
2736 return ADS_ERROR(LDAP_NO_MEMORY);
2739 ads_msgfree(ads, res);
2745 * pull a DOM_SID from an extended dn string
2746 * @param mem_ctx TALLOC_CTX
2747 * @param flags string type of extended_dn
2748 * @param sid pointer to a DOM_SID
2749 * @return boolean inidicating success
2751 BOOL ads_get_sid_from_extended_dn(TALLOC_CTX *mem_ctx,
2753 enum ads_extended_dn_flags flags,
2763 * ADS_EXTENDED_DN_HEX_STRING:
2764 * <GUID=238e1963cb390f4bb032ba0105525a29>;<SID=010500000000000515000000bb68c8fd6b61b427572eb04556040000>;CN=gd,OU=berlin,OU=suse,DC=ber,DC=suse,DC=de
2766 * ADS_EXTENDED_DN_STRING (only with w2k3):
2767 <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
2770 p = strchr(dn, ';');
2775 if (strncmp(p, ";<SID=", strlen(";<SID=")) != 0) {
2779 p += strlen(";<SID=");
2788 DEBUG(100,("ads_get_sid_from_extended_dn: sid string is %s\n", p));
2792 case ADS_EXTENDED_DN_STRING:
2793 if (!string_to_sid(sid, p)) {
2797 case ADS_EXTENDED_DN_HEX_STRING: {
2801 buf_len = strhex_to_str(buf, strlen(p), p);
2806 if (!sid_parse(buf, buf_len, sid)) {
2807 DEBUG(10,("failed to parse sid\n"));
2813 DEBUG(10,("unknown extended dn format\n"));
2821 * pull an array of DOM_SIDs from a ADS result
2822 * @param ads connection to ads server
2823 * @param mem_ctx TALLOC_CTX for allocating sid array
2824 * @param msg Results of search
2825 * @param field Attribute to retrieve
2826 * @param flags string type of extended_dn
2827 * @param sids pointer to sid array to allocate
2828 * @return the count of SIDs pulled
2830 int ads_pull_sids_from_extendeddn(ADS_STRUCT *ads,
2831 TALLOC_CTX *mem_ctx,
2834 enum ads_extended_dn_flags flags,
2841 if ((dn_strings = ads_pull_strings(ads, mem_ctx, msg, field,
2842 &dn_count)) == NULL) {
2846 (*sids) = TALLOC_ZERO_ARRAY(mem_ctx, DOM_SID, dn_count + 1);
2848 TALLOC_FREE(dn_strings);
2852 for (i=0; i<dn_count; i++) {
2854 if (!ads_get_sid_from_extended_dn(mem_ctx, dn_strings[i],
2855 flags, &(*sids)[i])) {
2857 TALLOC_FREE(dn_strings);
2862 TALLOC_FREE(dn_strings);
2867 /********************************************************************
2868 ********************************************************************/
2870 char* ads_get_dnshostname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
2872 LDAPMessage *res = NULL;
2877 status = ads_find_machine_acct(ads, &res, global_myname());
2878 if (!ADS_ERR_OK(status)) {
2879 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
2884 if ( (count = ads_count_replies(ads, res)) != 1 ) {
2885 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
2889 if ( (name = ads_pull_string(ads, ctx, res, "dNSHostName")) == NULL ) {
2890 DEBUG(0,("ads_get_dnshostname: No dNSHostName attribute!\n"));
2894 ads_msgfree(ads, res);
2899 /********************************************************************
2900 ********************************************************************/
2902 char* ads_get_upn( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
2904 LDAPMessage *res = NULL;
2909 status = ads_find_machine_acct(ads, &res, global_myname());
2910 if (!ADS_ERR_OK(status)) {
2911 DEBUG(0,("ads_get_upn: Failed to find account for %s\n",
2916 if ( (count = ads_count_replies(ads, res)) != 1 ) {
2917 DEBUG(1,("ads_get_upn: %d entries returned!\n", count));
2921 if ( (name = ads_pull_string(ads, ctx, res, "userPrincipalName")) == NULL ) {
2922 DEBUG(2,("ads_get_upn: No userPrincipalName attribute!\n"));
2926 ads_msgfree(ads, res);
2931 /********************************************************************
2932 ********************************************************************/
2934 char* ads_get_samaccountname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
2936 LDAPMessage *res = NULL;
2941 status = ads_find_machine_acct(ads, &res, global_myname());
2942 if (!ADS_ERR_OK(status)) {
2943 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
2948 if ( (count = ads_count_replies(ads, res)) != 1 ) {
2949 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
2953 if ( (name = ads_pull_string(ads, ctx, res, "sAMAccountName")) == NULL ) {
2954 DEBUG(0,("ads_get_dnshostname: No sAMAccountName attribute!\n"));
2958 ads_msgfree(ads, res);
2965 SAVED CODE - we used to join via ldap - remember how we did this. JRA.
2968 * Join a machine to a realm
2969 * Creates the machine account and sets the machine password
2970 * @param ads connection to ads server
2971 * @param machine name of host to add
2972 * @param org_unit Organizational unit to place machine in
2973 * @return status of join
2975 ADS_STATUS ads_join_realm(ADS_STRUCT *ads, const char *machine_name,
2976 uint32 account_type, const char *org_unit)
2979 LDAPMessage *res = NULL;
2982 /* machine name must be lowercase */
2983 machine = SMB_STRDUP(machine_name);
2984 strlower_m(machine);
2987 status = ads_find_machine_acct(ads, (void **)&res, machine);
2988 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
2989 DEBUG(0, ("Host account for %s already exists - deleting old account\n", machine));
2990 status = ads_leave_realm(ads, machine);
2991 if (!ADS_ERR_OK(status)) {
2992 DEBUG(0, ("Failed to delete host '%s' from the '%s' realm.\n",
2993 machine, ads->config.realm));
2998 status = ads_add_machine_acct(ads, machine, account_type, org_unit);
2999 if (!ADS_ERR_OK(status)) {
3000 DEBUG(0, ("ads_join_realm: ads_add_machine_acct failed (%s): %s\n", machine, ads_errstr(status)));
3005 status = ads_find_machine_acct(ads, (void **)(void *)&res, machine);
3006 if (!ADS_ERR_OK(status)) {
3007 DEBUG(0, ("ads_join_realm: Host account test failed for machine %s\n", machine));
3013 ads_msgfree(ads, res);
3020 * Delete a machine from the realm
3021 * @param ads connection to ads server
3022 * @param hostname Machine to remove
3023 * @return status of delete
3025 ADS_STATUS ads_leave_realm(ADS_STRUCT *ads, const char *hostname)
3030 char *hostnameDN, *host;
3032 LDAPControl ldap_control;
3033 LDAPControl * pldap_control[2] = {NULL, NULL};
3035 pldap_control[0] = &ldap_control;
3036 memset(&ldap_control, 0, sizeof(LDAPControl));
3037 ldap_control.ldctl_oid = (char *)LDAP_SERVER_TREE_DELETE_OID;
3039 /* hostname must be lowercase */
3040 host = SMB_STRDUP(hostname);
3043 status = ads_find_machine_acct(ads, &res, host);
3044 if (!ADS_ERR_OK(status)) {
3045 DEBUG(0, ("Host account for %s does not exist.\n", host));
3050 msg = ads_first_entry(ads, res);
3053 return ADS_ERROR_SYSTEM(ENOENT);
3056 hostnameDN = ads_get_dn(ads, (LDAPMessage *)msg);
3058 rc = ldap_delete_ext_s(ads->ld, hostnameDN, pldap_control, NULL);
3060 DEBUG(3,("ldap_delete_ext_s failed with error code %d\n", rc));
3062 DEBUG(3,("ldap_delete_ext_s succeeded with error code %d\n", rc));
3065 if (rc != LDAP_SUCCESS) {
3066 const char *attrs[] = { "cn", NULL };
3067 LDAPMessage *msg_sub;
3069 /* we only search with scope ONE, we do not expect any further
3070 * objects to be created deeper */
3072 status = ads_do_search_retry(ads, hostnameDN,
3073 LDAP_SCOPE_ONELEVEL,
3074 "(objectclass=*)", attrs, &res);
3076 if (!ADS_ERR_OK(status)) {
3078 ads_memfree(ads, hostnameDN);
3082 for (msg_sub = ads_first_entry(ads, res); msg_sub;
3083 msg_sub = ads_next_entry(ads, msg_sub)) {
3087 if ((dn = ads_get_dn(ads, msg_sub)) == NULL) {
3089 ads_memfree(ads, hostnameDN);
3090 return ADS_ERROR(LDAP_NO_MEMORY);
3093 status = ads_del_dn(ads, dn);
3094 if (!ADS_ERR_OK(status)) {
3095 DEBUG(3,("failed to delete dn %s: %s\n", dn, ads_errstr(status)));
3097 ads_memfree(ads, dn);
3098 ads_memfree(ads, hostnameDN);
3102 ads_memfree(ads, dn);
3105 /* there should be no subordinate objects anymore */
3106 status = ads_do_search_retry(ads, hostnameDN,
3107 LDAP_SCOPE_ONELEVEL,
3108 "(objectclass=*)", attrs, &res);
3110 if (!ADS_ERR_OK(status) || ( (ads_count_replies(ads, res)) > 0 ) ) {
3112 ads_memfree(ads, hostnameDN);
3116 /* delete hostnameDN now */
3117 status = ads_del_dn(ads, hostnameDN);
3118 if (!ADS_ERR_OK(status)) {
3120 DEBUG(3,("failed to delete dn %s: %s\n", hostnameDN, ads_errstr(status)));
3121 ads_memfree(ads, hostnameDN);
3126 ads_memfree(ads, hostnameDN);
3128 status = ads_find_machine_acct(ads, &res, host);
3129 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
3130 DEBUG(3, ("Failed to remove host account.\n"));