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;
119 /**********************************************
120 Do client and server sitename match ?
121 **********************************************/
123 BOOL ads_sitename_match(ADS_STRUCT *ads)
125 if (ads->config.server_site_name == NULL &&
126 ads->config.client_site_name == NULL ) {
127 DEBUG(10,("ads_sitename_match: both null\n"));
130 if (ads->config.server_site_name &&
131 ads->config.client_site_name &&
132 strequal(ads->config.server_site_name,
133 ads->config.client_site_name)) {
134 DEBUG(10,("ads_sitename_match: name %s match\n", ads->config.server_site_name));
137 DEBUG(10,("ads_sitename_match: no match %s %s\n",
138 ads->config.server_site_name ? ads->config.server_site_name : "NULL",
139 ads->config.client_site_name ? ads->config.client_site_name : "NULL"));
145 try a connection to a given ldap server, returning True and setting the servers IP
146 in the ads struct if successful
148 BOOL ads_try_connect(ADS_STRUCT *ads, const char *server )
151 struct cldap_netlogon_reply cldap_reply;
153 if (!server || !*server) {
157 DEBUG(5,("ads_try_connect: sending CLDAP request to %s (realm: %s)\n",
158 server, ads->server.realm));
160 /* this copes with inet_ntoa brokenness */
162 srv = SMB_STRDUP(server);
164 ZERO_STRUCT( cldap_reply );
166 if ( !ads_cldap_netlogon( srv, ads->server.realm, &cldap_reply ) ) {
167 DEBUG(3,("ads_try_connect: CLDAP request %s failed.\n", srv));
172 /* Check the CLDAP reply flags */
174 if ( !(cldap_reply.flags & ADS_LDAP) ) {
175 DEBUG(1,("ads_try_connect: %s's CLDAP reply says it is not an LDAP server!\n",
181 /* Fill in the ads->config values */
183 SAFE_FREE(ads->config.realm);
184 SAFE_FREE(ads->config.bind_path);
185 SAFE_FREE(ads->config.ldap_server_name);
186 SAFE_FREE(ads->config.server_site_name);
187 SAFE_FREE(ads->config.client_site_name);
188 SAFE_FREE(ads->server.workgroup);
190 ads->config.flags = cldap_reply.flags;
191 ads->config.ldap_server_name = SMB_STRDUP(cldap_reply.hostname);
192 strupper_m(cldap_reply.domain);
193 ads->config.realm = SMB_STRDUP(cldap_reply.domain);
194 ads->config.bind_path = ads_build_dn(ads->config.realm);
195 if (*cldap_reply.server_site_name) {
196 ads->config.server_site_name =
197 SMB_STRDUP(cldap_reply.server_site_name);
199 if (*cldap_reply.client_site_name) {
200 ads->config.client_site_name =
201 SMB_STRDUP(cldap_reply.client_site_name);
204 ads->server.workgroup = SMB_STRDUP(cldap_reply.netbios_domain);
206 ads->ldap_port = LDAP_PORT;
207 ads->ldap_ip = *interpret_addr2(srv);
210 /* Store our site name. */
211 sitename_store( cldap_reply.client_site_name );
216 /**********************************************************************
217 Try to find an AD dc using our internal name resolution routines
218 Try the realm first and then then workgroup name if netbios is not
220 **********************************************************************/
222 static NTSTATUS ads_find_dc(ADS_STRUCT *ads)
226 struct ip_service *ip_list;
228 BOOL got_realm = False;
229 BOOL use_own_domain = False;
230 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
232 /* if the realm and workgroup are both empty, assume they are ours */
235 c_realm = ads->server.realm;
237 if ( !c_realm || !*c_realm ) {
238 /* special case where no realm and no workgroup means our own */
239 if ( !ads->server.workgroup || !*ads->server.workgroup ) {
240 use_own_domain = True;
241 c_realm = lp_realm();
245 if (c_realm && *c_realm)
249 /* we need to try once with the realm name and fallback to the
250 netbios domain name if we fail (if netbios has not been disabled */
252 if ( !got_realm && !lp_disable_netbios() ) {
253 c_realm = ads->server.workgroup;
254 if (!c_realm || !*c_realm) {
255 if ( use_own_domain )
256 c_realm = lp_workgroup();
259 if ( !c_realm || !*c_realm ) {
260 DEBUG(0,("ads_find_dc: no realm or workgroup! Don't know what to do\n"));
261 return NT_STATUS_INVALID_PARAMETER; /* rather need MISSING_PARAMETER ... */
265 pstrcpy( realm, c_realm );
267 DEBUG(6,("ads_find_dc: looking for %s '%s'\n",
268 (got_realm ? "realm" : "domain"), realm));
270 status = get_sorted_dc_list(realm, &ip_list, &count, got_realm);
271 if (!NT_STATUS_IS_OK(status)) {
272 /* fall back to netbios if we can */
273 if ( got_realm && !lp_disable_netbios() ) {
281 /* if we fail this loop, then giveup since all the IP addresses returned were dead */
282 for ( i=0; i<count; i++ ) {
285 fstrcpy( server, inet_ntoa(ip_list[i].ip) );
287 if ( !NT_STATUS_IS_OK(check_negative_conn_cache(realm, server)) )
291 /* realm in this case is a workgroup name. We need
292 to ignore any IP addresses in the negative connection
293 cache that match ip addresses returned in the ad realm
294 case. It sucks that I have to reproduce the logic above... */
295 c_realm = ads->server.realm;
296 if ( !c_realm || !*c_realm ) {
297 if ( !ads->server.workgroup || !*ads->server.workgroup ) {
298 c_realm = lp_realm();
301 if (c_realm && *c_realm &&
302 !NT_STATUS_IS_OK(check_negative_conn_cache(c_realm, server))) {
303 /* Ensure we add the workgroup name for this
304 IP address as negative too. */
305 add_failed_connection_entry( realm, server, NT_STATUS_UNSUCCESSFUL );
310 if ( ads_try_connect(ads, server) ) {
315 /* keep track of failures */
316 add_failed_connection_entry( realm, server, NT_STATUS_UNSUCCESSFUL );
321 return NT_STATUS_NO_LOGON_SERVERS;
326 * Connect to the LDAP server
327 * @param ads Pointer to an existing ADS_STRUCT
328 * @return status of connection
330 ADS_STATUS ads_connect(ADS_STRUCT *ads)
332 int version = LDAP_VERSION3;
336 ads->last_attempt = time(NULL);
339 /* try with a user specified server */
341 if (ads->server.ldap_server &&
342 ads_try_connect(ads, ads->server.ldap_server)) {
346 ntstatus = ads_find_dc(ads);
347 if (NT_STATUS_IS_OK(ntstatus)) {
351 return ADS_ERROR_NT(ntstatus);
354 DEBUG(3,("Connected to LDAP server %s\n", inet_ntoa(ads->ldap_ip)));
356 if (!ads->auth.user_name) {
357 /* Must use the userPrincipalName value here or sAMAccountName
358 and not servicePrincipalName; found by Guenther Deschner */
360 asprintf(&ads->auth.user_name, "%s$", global_myname() );
363 if (!ads->auth.realm) {
364 ads->auth.realm = SMB_STRDUP(ads->config.realm);
367 if (!ads->auth.kdc_server) {
368 ads->auth.kdc_server = SMB_STRDUP(inet_ntoa(ads->ldap_ip));
372 /* this is a really nasty hack to avoid ADS DNS problems. It needs a patch
373 to MIT kerberos to work (tridge) */
376 asprintf(&env, "KRB5_KDC_ADDRESS_%s", ads->config.realm);
377 setenv(env, ads->auth.kdc_server, 1);
382 /* If the caller() requested no LDAP bind, then we are done */
384 if (ads->auth.flags & ADS_AUTH_NO_BIND) {
388 /* Otherwise setup the TCP LDAP session */
390 if ( (ads->ld = ldap_open_with_timeout(ads->config.ldap_server_name,
391 LDAP_PORT, lp_ldap_timeout())) == NULL )
393 return ADS_ERROR(LDAP_OPERATIONS_ERROR);
396 /* cache the successful connection */
397 saf_store( ads->server.workgroup, inet_ntoa(ads->ldap_ip));
399 ldap_set_option(ads->ld, LDAP_OPT_PROTOCOL_VERSION, &version);
401 status = ADS_ERROR(smb_ldap_start_tls(ads->ld, version));
402 if (!ADS_ERR_OK(status)) {
406 /* fill in the current time and offsets */
408 status = ads_current_time( ads );
409 if ( !ADS_ERR_OK(status) ) {
413 /* Now do the bind */
415 if (ads->auth.flags & ADS_AUTH_ANON_BIND) {
416 return ADS_ERROR(ldap_simple_bind_s( ads->ld, NULL, NULL));
419 if (ads->auth.flags & ADS_AUTH_SIMPLE_BIND) {
420 return ADS_ERROR(ldap_simple_bind_s( ads->ld, ads->auth.user_name, ads->auth.password));
423 return ads_sasl_bind(ads);
427 Duplicate a struct berval into talloc'ed memory
429 static struct berval *dup_berval(TALLOC_CTX *ctx, const struct berval *in_val)
431 struct berval *value;
433 if (!in_val) return NULL;
435 value = TALLOC_ZERO_P(ctx, struct berval);
438 if (in_val->bv_len == 0) return value;
440 value->bv_len = in_val->bv_len;
441 value->bv_val = TALLOC_MEMDUP(ctx, in_val->bv_val, in_val->bv_len);
446 Make a values list out of an array of (struct berval *)
448 static struct berval **ads_dup_values(TALLOC_CTX *ctx,
449 const struct berval **in_vals)
451 struct berval **values;
454 if (!in_vals) return NULL;
455 for (i=0; in_vals[i]; i++)
457 values = TALLOC_ZERO_ARRAY(ctx, struct berval *, i+1);
458 if (!values) return NULL;
460 for (i=0; in_vals[i]; i++) {
461 values[i] = dup_berval(ctx, in_vals[i]);
467 UTF8-encode a values list out of an array of (char *)
469 static char **ads_push_strvals(TALLOC_CTX *ctx, const char **in_vals)
474 if (!in_vals) return NULL;
475 for (i=0; in_vals[i]; i++)
477 values = TALLOC_ZERO_ARRAY(ctx, char *, i+1);
478 if (!values) return NULL;
480 for (i=0; in_vals[i]; i++) {
481 push_utf8_talloc(ctx, &values[i], in_vals[i]);
487 Pull a (char *) array out of a UTF8-encoded values list
489 static char **ads_pull_strvals(TALLOC_CTX *ctx, const char **in_vals)
494 if (!in_vals) return NULL;
495 for (i=0; in_vals[i]; i++)
497 values = TALLOC_ZERO_ARRAY(ctx, char *, i+1);
498 if (!values) return NULL;
500 for (i=0; in_vals[i]; i++) {
501 pull_utf8_talloc(ctx, &values[i], in_vals[i]);
507 * Do a search with paged results. cookie must be null on the first
508 * call, and then returned on each subsequent call. It will be null
509 * again when the entire search is complete
510 * @param ads connection to ads server
511 * @param bind_path Base dn for the search
512 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
513 * @param expr Search expression - specified in local charset
514 * @param attrs Attributes to retrieve - specified in utf8 or ascii
515 * @param res ** which will contain results - free res* with ads_msgfree()
516 * @param count Number of entries retrieved on this page
517 * @param cookie The paged results cookie to be returned on subsequent calls
518 * @return status of search
520 ADS_STATUS ads_do_paged_search_args(ADS_STRUCT *ads, const char *bind_path,
521 int scope, const char *expr,
522 const char **attrs, void *args, void **res,
523 int *count, void **cookie)
526 char *utf8_expr, *utf8_path, **search_attrs;
527 LDAPControl PagedResults, NoReferrals, ExtendedDn, *controls[4], **rcontrols;
528 BerElement *cookie_be = NULL;
529 struct berval *cookie_bv= NULL;
530 BerElement *extdn_be = NULL;
531 struct berval *extdn_bv= NULL;
534 ads_control *external_control = (ads_control *) args;
538 if (!(ctx = talloc_init("ads_do_paged_search_args")))
539 return ADS_ERROR(LDAP_NO_MEMORY);
541 /* 0 means the conversion worked but the result was empty
542 so we only fail if it's -1. In any case, it always
543 at least nulls out the dest */
544 if ((push_utf8_talloc(ctx, &utf8_expr, expr) == (size_t)-1) ||
545 (push_utf8_talloc(ctx, &utf8_path, bind_path) == (size_t)-1)) {
550 if (!attrs || !(*attrs))
553 /* This would be the utf8-encoded version...*/
554 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
555 if (!(str_list_copy(&search_attrs, attrs))) {
562 /* Paged results only available on ldap v3 or later */
563 ldap_get_option(ads->ld, LDAP_OPT_PROTOCOL_VERSION, &version);
564 if (version < LDAP_VERSION3) {
565 rc = LDAP_NOT_SUPPORTED;
569 cookie_be = ber_alloc_t(LBER_USE_DER);
571 ber_printf(cookie_be, "{iO}", (ber_int_t) 1000, *cookie);
572 ber_bvfree(*cookie); /* don't need it from last time */
575 ber_printf(cookie_be, "{io}", (ber_int_t) 1000, "", 0);
577 ber_flatten(cookie_be, &cookie_bv);
578 PagedResults.ldctl_oid = CONST_DISCARD(char *, ADS_PAGE_CTL_OID);
579 PagedResults.ldctl_iscritical = (char) 1;
580 PagedResults.ldctl_value.bv_len = cookie_bv->bv_len;
581 PagedResults.ldctl_value.bv_val = cookie_bv->bv_val;
583 NoReferrals.ldctl_oid = CONST_DISCARD(char *, ADS_NO_REFERRALS_OID);
584 NoReferrals.ldctl_iscritical = (char) 0;
585 NoReferrals.ldctl_value.bv_len = 0;
586 NoReferrals.ldctl_value.bv_val = CONST_DISCARD(char *, "");
588 if (external_control && strequal(external_control->control, ADS_EXTENDED_DN_OID)) {
590 ExtendedDn.ldctl_oid = CONST_DISCARD(char *, external_control->control);
591 ExtendedDn.ldctl_iscritical = (char) external_control->critical;
593 /* win2k does not accept a ldctl_value beeing passed in */
595 if (external_control->val != 0) {
597 if ((extdn_be = ber_alloc_t(LBER_USE_DER)) == NULL ) {
602 if ((ber_printf(extdn_be, "{i}", (ber_int_t) external_control->val)) == -1) {
606 if ((ber_flatten(extdn_be, &extdn_bv)) == -1) {
611 ExtendedDn.ldctl_value.bv_len = extdn_bv->bv_len;
612 ExtendedDn.ldctl_value.bv_val = extdn_bv->bv_val;
615 ExtendedDn.ldctl_value.bv_len = 0;
616 ExtendedDn.ldctl_value.bv_val = NULL;
619 controls[0] = &NoReferrals;
620 controls[1] = &PagedResults;
621 controls[2] = &ExtendedDn;
625 controls[0] = &NoReferrals;
626 controls[1] = &PagedResults;
630 /* we need to disable referrals as the openldap libs don't
631 handle them and paged results at the same time. Using them
632 together results in the result record containing the server
633 page control being removed from the result list (tridge/jmcd)
635 leaving this in despite the control that says don't generate
636 referrals, in case the server doesn't support it (jmcd)
638 ldap_set_option(ads->ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
640 rc = ldap_search_with_timeout(ads->ld, utf8_path, scope, utf8_expr,
641 search_attrs, 0, controls,
643 (LDAPMessage **)res);
645 ber_free(cookie_be, 1);
646 ber_bvfree(cookie_bv);
649 DEBUG(3,("ads_do_paged_search_args: ldap_search_with_timeout(%s) -> %s\n", expr,
650 ldap_err2string(rc)));
654 rc = ldap_parse_result(ads->ld, *res, NULL, NULL, NULL,
655 NULL, &rcontrols, 0);
661 for (i=0; rcontrols[i]; i++) {
662 if (strcmp(ADS_PAGE_CTL_OID, rcontrols[i]->ldctl_oid) == 0) {
663 cookie_be = ber_init(&rcontrols[i]->ldctl_value);
664 ber_scanf(cookie_be,"{iO}", (ber_int_t *) count,
666 /* the berval is the cookie, but must be freed when
668 if (cookie_bv->bv_len) /* still more to do */
669 *cookie=ber_bvdup(cookie_bv);
672 ber_bvfree(cookie_bv);
673 ber_free(cookie_be, 1);
677 ldap_controls_free(rcontrols);
683 ber_free(extdn_be, 1);
687 ber_bvfree(extdn_bv);
690 /* if/when we decide to utf8-encode attrs, take out this next line */
691 str_list_free(&search_attrs);
693 return ADS_ERROR(rc);
696 ADS_STATUS ads_do_paged_search(ADS_STRUCT *ads, const char *bind_path,
697 int scope, const char *expr,
698 const char **attrs, void **res,
699 int *count, void **cookie)
701 return ads_do_paged_search_args(ads, bind_path, scope, expr, attrs, NULL, res, count, cookie);
706 * Get all results for a search. This uses ads_do_paged_search() to return
707 * all entries in a large search.
708 * @param ads connection to ads server
709 * @param bind_path Base dn for the search
710 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
711 * @param expr Search expression
712 * @param attrs Attributes to retrieve
713 * @param res ** which will contain results - free res* with ads_msgfree()
714 * @return status of search
716 ADS_STATUS ads_do_search_all_args(ADS_STRUCT *ads, const char *bind_path,
717 int scope, const char *expr,
718 const char **attrs, void *args, void **res)
725 status = ads_do_paged_search_args(ads, bind_path, scope, expr, attrs, args, res,
728 if (!ADS_ERR_OK(status))
731 #ifdef HAVE_LDAP_ADD_RESULT_ENTRY
735 LDAPMessage *msg, *next;
737 status2 = ads_do_paged_search_args(ads, bind_path, scope, expr,
738 attrs, args, &res2, &count, &cookie);
740 if (!ADS_ERR_OK(status2)) break;
742 /* this relies on the way that ldap_add_result_entry() works internally. I hope
743 that this works on all ldap libs, but I have only tested with openldap */
744 for (msg = ads_first_entry(ads, res2); msg; msg = next) {
745 next = ads_next_entry(ads, msg);
746 ldap_add_result_entry((LDAPMessage **)res, msg);
748 /* note that we do not free res2, as the memory is now
749 part of the main returned list */
752 DEBUG(0, ("no ldap_add_result_entry() support in LDAP libs!\n"));
753 status = ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
759 ADS_STATUS ads_do_search_all(ADS_STRUCT *ads, const char *bind_path,
760 int scope, const char *expr,
761 const char **attrs, void **res)
763 return ads_do_search_all_args(ads, bind_path, scope, expr, attrs, NULL, res);
767 * Run a function on all results for a search. Uses ads_do_paged_search() and
768 * runs the function as each page is returned, using ads_process_results()
769 * @param ads connection to ads server
770 * @param bind_path Base dn for the search
771 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
772 * @param expr Search expression - specified in local charset
773 * @param attrs Attributes to retrieve - specified in UTF-8 or ascii
774 * @param fn Function which takes attr name, values list, and data_area
775 * @param data_area Pointer which is passed to function on each call
776 * @return status of search
778 ADS_STATUS ads_do_search_all_fn(ADS_STRUCT *ads, const char *bind_path,
779 int scope, const char *expr, const char **attrs,
780 BOOL(*fn)(char *, void **, void *),
788 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs, &res,
791 if (!ADS_ERR_OK(status)) return status;
793 ads_process_results(ads, res, fn, data_area);
794 ads_msgfree(ads, res);
797 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs,
798 &res, &count, &cookie);
800 if (!ADS_ERR_OK(status)) break;
802 ads_process_results(ads, res, fn, data_area);
803 ads_msgfree(ads, res);
810 * Do a search with a timeout.
811 * @param ads connection to ads server
812 * @param bind_path Base dn for the search
813 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
814 * @param expr Search expression
815 * @param attrs Attributes to retrieve
816 * @param res ** which will contain results - free res* with ads_msgfree()
817 * @return status of search
819 ADS_STATUS ads_do_search(ADS_STRUCT *ads, const char *bind_path, int scope,
821 const char **attrs, void **res)
824 char *utf8_expr, *utf8_path, **search_attrs = NULL;
828 if (!(ctx = talloc_init("ads_do_search"))) {
829 DEBUG(1,("ads_do_search: talloc_init() failed!"));
830 return ADS_ERROR(LDAP_NO_MEMORY);
833 /* 0 means the conversion worked but the result was empty
834 so we only fail if it's negative. In any case, it always
835 at least nulls out the dest */
836 if ((push_utf8_talloc(ctx, &utf8_expr, expr) == (size_t)-1) ||
837 (push_utf8_talloc(ctx, &utf8_path, bind_path) == (size_t)-1)) {
838 DEBUG(1,("ads_do_search: push_utf8_talloc() failed!"));
843 if (!attrs || !(*attrs))
846 /* This would be the utf8-encoded version...*/
847 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
848 if (!(str_list_copy(&search_attrs, attrs)))
850 DEBUG(1,("ads_do_search: str_list_copy() failed!"));
856 /* see the note in ads_do_paged_search - we *must* disable referrals */
857 ldap_set_option(ads->ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
859 rc = ldap_search_with_timeout(ads->ld, utf8_path, scope, utf8_expr,
860 search_attrs, 0, NULL, NULL,
862 (LDAPMessage **)res);
864 if (rc == LDAP_SIZELIMIT_EXCEEDED) {
865 DEBUG(3,("Warning! sizelimit exceeded in ldap. Truncating.\n"));
871 /* if/when we decide to utf8-encode attrs, take out this next line */
872 str_list_free(&search_attrs);
873 return ADS_ERROR(rc);
876 * Do a general ADS search
877 * @param ads connection to ads server
878 * @param res ** which will contain results - free res* with ads_msgfree()
879 * @param expr Search expression
880 * @param attrs Attributes to retrieve
881 * @return status of search
883 ADS_STATUS ads_search(ADS_STRUCT *ads, void **res,
887 return ads_do_search(ads, ads->config.bind_path, LDAP_SCOPE_SUBTREE,
892 * Do a search on a specific DistinguishedName
893 * @param ads connection to ads server
894 * @param res ** which will contain results - free res* with ads_msgfree()
895 * @param dn DistinguishName to search
896 * @param attrs Attributes to retrieve
897 * @return status of search
899 ADS_STATUS ads_search_dn(ADS_STRUCT *ads, void *_res,
903 void **res = (void **)_res;
904 return ads_do_search(ads, dn, LDAP_SCOPE_BASE, "(objectclass=*)", attrs, res);
908 * Free up memory from a ads_search
909 * @param ads connection to ads server
910 * @param msg Search results to free
912 void ads_msgfree(ADS_STRUCT *ads, void *msg)
919 * Free up memory from various ads requests
920 * @param ads connection to ads server
921 * @param mem Area to free
923 void ads_memfree(ADS_STRUCT *ads, void *mem)
929 * Get a dn from search results
930 * @param ads connection to ads server
931 * @param msg Search result
934 char *ads_get_dn(ADS_STRUCT *ads, void *msg)
936 char *utf8_dn, *unix_dn;
938 utf8_dn = ldap_get_dn(ads->ld, msg);
941 DEBUG (5, ("ads_get_dn: ldap_get_dn failed\n"));
945 if (pull_utf8_allocate(&unix_dn, utf8_dn) == (size_t)-1) {
946 DEBUG(0,("ads_get_dn: string conversion failure utf8 [%s]\n",
950 ldap_memfree(utf8_dn);
955 * Get a canonical dn from search results
956 * @param ads connection to ads server
957 * @param msg Search result
960 char *ads_get_dn_canonical(ADS_STRUCT *ads, void *msg)
962 #ifdef HAVE_LDAP_DN2AD_CANONICAL
963 return ldap_dn2ad_canonical(ads_get_dn(ads, msg));
970 * Get the parent from a dn
971 * @param dn the dn to return the parent from
972 * @return parent dn string
974 char *ads_parent_dn(const char *dn)
992 * Find a machine account given a hostname
993 * @param ads connection to ads server
994 * @param res ** which will contain results - free res* with ads_msgfree()
995 * @param host Hostname to search for
996 * @return status of search
998 ADS_STATUS ads_find_machine_acct(ADS_STRUCT *ads, void **res, const char *machine)
1002 const char *attrs[] = {"*", "nTSecurityDescriptor", NULL};
1006 /* the easiest way to find a machine account anywhere in the tree
1007 is to look for hostname$ */
1008 if (asprintf(&expr, "(samAccountName=%s$)", machine) == -1) {
1009 DEBUG(1, ("asprintf failed!\n"));
1010 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1013 status = ads_search(ads, res, expr, attrs);
1019 * Initialize a list of mods to be used in a modify request
1020 * @param ctx An initialized TALLOC_CTX
1021 * @return allocated ADS_MODLIST
1023 ADS_MODLIST ads_init_mods(TALLOC_CTX *ctx)
1025 #define ADS_MODLIST_ALLOC_SIZE 10
1028 if ((mods = TALLOC_ZERO_ARRAY(ctx, LDAPMod *, ADS_MODLIST_ALLOC_SIZE + 1)))
1029 /* -1 is safety to make sure we don't go over the end.
1030 need to reset it to NULL before doing ldap modify */
1031 mods[ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1033 return (ADS_MODLIST)mods;
1038 add an attribute to the list, with values list already constructed
1040 static ADS_STATUS ads_modlist_add(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1041 int mod_op, const char *name,
1042 const void *_invals)
1044 const void **invals = (const void **)_invals;
1046 LDAPMod **modlist = (LDAPMod **) *mods;
1047 struct berval **ber_values = NULL;
1048 char **char_values = NULL;
1051 mod_op = LDAP_MOD_DELETE;
1053 if (mod_op & LDAP_MOD_BVALUES)
1054 ber_values = ads_dup_values(ctx,
1055 (const struct berval **)invals);
1057 char_values = ads_push_strvals(ctx,
1058 (const char **) invals);
1061 /* find the first empty slot */
1062 for (curmod=0; modlist[curmod] && modlist[curmod] != (LDAPMod *) -1;
1064 if (modlist[curmod] == (LDAPMod *) -1) {
1065 if (!(modlist = TALLOC_REALLOC_ARRAY(ctx, modlist, LDAPMod *,
1066 curmod+ADS_MODLIST_ALLOC_SIZE+1)))
1067 return ADS_ERROR(LDAP_NO_MEMORY);
1068 memset(&modlist[curmod], 0,
1069 ADS_MODLIST_ALLOC_SIZE*sizeof(LDAPMod *));
1070 modlist[curmod+ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1071 *mods = (ADS_MODLIST)modlist;
1074 if (!(modlist[curmod] = TALLOC_ZERO_P(ctx, LDAPMod)))
1075 return ADS_ERROR(LDAP_NO_MEMORY);
1076 modlist[curmod]->mod_type = talloc_strdup(ctx, name);
1077 if (mod_op & LDAP_MOD_BVALUES) {
1078 modlist[curmod]->mod_bvalues = ber_values;
1079 } else if (mod_op & LDAP_MOD_DELETE) {
1080 modlist[curmod]->mod_values = NULL;
1082 modlist[curmod]->mod_values = char_values;
1085 modlist[curmod]->mod_op = mod_op;
1086 return ADS_ERROR(LDAP_SUCCESS);
1090 * Add a single string value to a mod list
1091 * @param ctx An initialized TALLOC_CTX
1092 * @param mods An initialized ADS_MODLIST
1093 * @param name The attribute name to add
1094 * @param val The value to add - NULL means DELETE
1095 * @return ADS STATUS indicating success of add
1097 ADS_STATUS ads_mod_str(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1098 const char *name, const char *val)
1100 const char *values[2];
1106 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1107 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE, name, values);
1111 * Add an array of string values to a mod list
1112 * @param ctx An initialized TALLOC_CTX
1113 * @param mods An initialized ADS_MODLIST
1114 * @param name The attribute name to add
1115 * @param vals The array of string values to add - NULL means DELETE
1116 * @return ADS STATUS indicating success of add
1118 ADS_STATUS ads_mod_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1119 const char *name, const char **vals)
1122 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1123 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE,
1124 name, (const void **) vals);
1129 * Add a single ber-encoded value to a mod list
1130 * @param ctx An initialized TALLOC_CTX
1131 * @param mods An initialized ADS_MODLIST
1132 * @param name The attribute name to add
1133 * @param val The value to add - NULL means DELETE
1134 * @return ADS STATUS indicating success of add
1136 static ADS_STATUS ads_mod_ber(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1137 const char *name, const struct berval *val)
1139 const struct berval *values[2];
1144 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1145 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE|LDAP_MOD_BVALUES,
1146 name, (const void **) values);
1151 * Perform an ldap modify
1152 * @param ads connection to ads server
1153 * @param mod_dn DistinguishedName to modify
1154 * @param mods list of modifications to perform
1155 * @return status of modify
1157 ADS_STATUS ads_gen_mod(ADS_STRUCT *ads, const char *mod_dn, ADS_MODLIST mods)
1160 char *utf8_dn = NULL;
1162 this control is needed to modify that contains a currently
1163 non-existent attribute (but allowable for the object) to run
1165 LDAPControl PermitModify = {
1166 CONST_DISCARD(char *, ADS_PERMIT_MODIFY_OID),
1169 LDAPControl *controls[2];
1171 controls[0] = &PermitModify;
1174 if (push_utf8_allocate(&utf8_dn, mod_dn) == -1) {
1175 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1178 /* find the end of the list, marked by NULL or -1 */
1179 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1180 /* make sure the end of the list is NULL */
1182 ret = ldap_modify_ext_s(ads->ld, utf8_dn,
1183 (LDAPMod **) mods, controls, NULL);
1185 return ADS_ERROR(ret);
1189 * Perform an ldap add
1190 * @param ads connection to ads server
1191 * @param new_dn DistinguishedName to add
1192 * @param mods list of attributes and values for DN
1193 * @return status of add
1195 ADS_STATUS ads_gen_add(ADS_STRUCT *ads, const char *new_dn, ADS_MODLIST mods)
1198 char *utf8_dn = NULL;
1200 if (push_utf8_allocate(&utf8_dn, new_dn) == -1) {
1201 DEBUG(1, ("ads_gen_add: push_utf8_allocate failed!"));
1202 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1205 /* find the end of the list, marked by NULL or -1 */
1206 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1207 /* make sure the end of the list is NULL */
1210 ret = ldap_add_s(ads->ld, utf8_dn, (LDAPMod**)mods);
1212 return ADS_ERROR(ret);
1216 * Delete a DistinguishedName
1217 * @param ads connection to ads server
1218 * @param new_dn DistinguishedName to delete
1219 * @return status of delete
1221 ADS_STATUS ads_del_dn(ADS_STRUCT *ads, char *del_dn)
1224 char *utf8_dn = NULL;
1225 if (push_utf8_allocate(&utf8_dn, del_dn) == -1) {
1226 DEBUG(1, ("ads_del_dn: push_utf8_allocate failed!"));
1227 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1230 ret = ldap_delete_s(ads->ld, utf8_dn);
1231 return ADS_ERROR(ret);
1235 * Build an org unit string
1236 * if org unit is Computers or blank then assume a container, otherwise
1237 * assume a / separated list of organisational units.
1238 * jmcd: '\' is now used for escapes so certain chars can be in the ou (e.g. #)
1239 * @param ads connection to ads server
1240 * @param org_unit Organizational unit
1241 * @return org unit string - caller must free
1243 char *ads_ou_string(ADS_STRUCT *ads, const char *org_unit)
1247 if (!org_unit || !*org_unit) {
1249 ret = ads_default_ou_string(ads, WELL_KNOWN_GUID_COMPUTERS);
1251 /* samba4 might not yet respond to a wellknownobject-query */
1252 return ret ? ret : SMB_STRDUP("cn=Computers");
1255 if (strequal(org_unit, "Computers")) {
1256 return SMB_STRDUP("cn=Computers");
1259 /* jmcd: removed "\\" from the separation chars, because it is
1260 needed as an escape for chars like '#' which are valid in an
1262 return ads_build_path(org_unit, "/", "ou=", 1);
1266 * Get a org unit string for a well-known GUID
1267 * @param ads connection to ads server
1268 * @param wknguid Well known GUID
1269 * @return org unit string - caller must free
1271 char *ads_default_ou_string(ADS_STRUCT *ads, const char *wknguid)
1275 char *base, *wkn_dn, *ret, **wkn_dn_exp, **bind_dn_exp;
1276 const char *attrs[] = {"distinguishedName", NULL};
1277 int new_ln, wkn_ln, bind_ln, i;
1279 if (wknguid == NULL) {
1283 if (asprintf(&base, "<WKGUID=%s,%s>", wknguid, ads->config.bind_path ) == -1) {
1284 DEBUG(1, ("asprintf failed!\n"));
1288 status = ads_search_dn(ads, &res, base, attrs);
1289 if (!ADS_ERR_OK(status)) {
1290 DEBUG(1,("Failed while searching for: %s\n", base));
1296 if (ads_count_replies(ads, res) != 1) {
1300 /* substitute the bind-path from the well-known-guid-search result */
1301 wkn_dn = ads_get_dn(ads, res);
1302 wkn_dn_exp = ldap_explode_dn(wkn_dn, 0);
1303 bind_dn_exp = ldap_explode_dn(ads->config.bind_path, 0);
1305 for (wkn_ln=0; wkn_dn_exp[wkn_ln]; wkn_ln++)
1307 for (bind_ln=0; bind_dn_exp[bind_ln]; bind_ln++)
1310 new_ln = wkn_ln - bind_ln;
1312 ret = wkn_dn_exp[0];
1314 for (i=1; i < new_ln; i++) {
1316 asprintf(&s, "%s,%s", ret, wkn_dn_exp[i]);
1317 ret = SMB_STRDUP(s);
1321 ads_memfree(ads, wkn_dn);
1322 ldap_value_free(wkn_dn_exp);
1323 ldap_value_free(bind_dn_exp);
1329 * Adds (appends) an item to an attribute array, rather then
1330 * replacing the whole list
1331 * @param ctx An initialized TALLOC_CTX
1332 * @param mods An initialized ADS_MODLIST
1333 * @param name name of the ldap attribute to append to
1334 * @param vals an array of values to add
1335 * @return status of addition
1338 ADS_STATUS ads_add_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1339 const char *name, const char **vals)
1341 return ads_modlist_add(ctx, mods, LDAP_MOD_ADD, name, (const void **) vals);
1345 * Determines the computer account's current KVNO via an LDAP lookup
1346 * @param ads An initialized ADS_STRUCT
1347 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1348 * @return the kvno for the computer account, or -1 in case of a failure.
1351 uint32 ads_get_kvno(ADS_STRUCT *ads, const char *machine_name)
1353 LDAPMessage *res = NULL;
1354 uint32 kvno = (uint32)-1; /* -1 indicates a failure */
1356 const char *attrs[] = {"msDS-KeyVersionNumber", NULL};
1357 char *dn_string = NULL;
1358 ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS);
1360 DEBUG(5,("ads_get_kvno: Searching for host %s\n", machine_name));
1361 if (asprintf(&filter, "(samAccountName=%s$)", machine_name) == -1) {
1364 ret = ads_search(ads, (void**)(void *)&res, filter, attrs);
1366 if (!ADS_ERR_OK(ret) && ads_count_replies(ads, res)) {
1367 DEBUG(1,("ads_get_kvno: Computer Account For %s not found.\n", machine_name));
1368 ads_msgfree(ads, res);
1372 dn_string = ads_get_dn(ads, res);
1374 DEBUG(0,("ads_get_kvno: out of memory.\n"));
1375 ads_msgfree(ads, res);
1378 DEBUG(5,("ads_get_kvno: Using: %s\n", dn_string));
1379 ads_memfree(ads, dn_string);
1381 /* ---------------------------------------------------------
1382 * 0 is returned as a default KVNO from this point on...
1383 * This is done because Windows 2000 does not support key
1384 * version numbers. Chances are that a failure in the next
1385 * step is simply due to Windows 2000 being used for a
1386 * domain controller. */
1389 if (!ads_pull_uint32(ads, res, "msDS-KeyVersionNumber", &kvno)) {
1390 DEBUG(3,("ads_get_kvno: Error Determining KVNO!\n"));
1391 DEBUG(3,("ads_get_kvno: Windows 2000 does not support KVNO's, so this may be normal.\n"));
1392 ads_msgfree(ads, res);
1397 DEBUG(5,("ads_get_kvno: Looked Up KVNO of: %d\n", kvno));
1398 ads_msgfree(ads, res);
1403 * This clears out all registered spn's for a given hostname
1404 * @param ads An initilaized ADS_STRUCT
1405 * @param machine_name the NetBIOS name of the computer.
1406 * @return 0 upon success, non-zero otherwise.
1409 ADS_STATUS ads_clear_service_principal_names(ADS_STRUCT *ads, const char *machine_name)
1412 LDAPMessage *res = NULL;
1414 const char *servicePrincipalName[1] = {NULL};
1415 ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS);
1416 char *dn_string = NULL;
1418 ret = ads_find_machine_acct(ads, (void **)(void *)&res, machine_name);
1419 if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
1420 DEBUG(5,("ads_clear_service_principal_names: WARNING: Host Account for %s not found... skipping operation.\n", machine_name));
1421 DEBUG(5,("ads_clear_service_principal_names: WARNING: Service Principals for %s have NOT been cleared.\n", machine_name));
1422 ads_msgfree(ads, res);
1423 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1426 DEBUG(5,("ads_clear_service_principal_names: Host account for %s found\n", machine_name));
1427 ctx = talloc_init("ads_clear_service_principal_names");
1429 ads_msgfree(ads, res);
1430 return ADS_ERROR(LDAP_NO_MEMORY);
1433 if (!(mods = ads_init_mods(ctx))) {
1434 talloc_destroy(ctx);
1435 ads_msgfree(ads, res);
1436 return ADS_ERROR(LDAP_NO_MEMORY);
1438 ret = ads_mod_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1439 if (!ADS_ERR_OK(ret)) {
1440 DEBUG(1,("ads_clear_service_principal_names: Error creating strlist.\n"));
1441 ads_msgfree(ads, res);
1442 talloc_destroy(ctx);
1445 dn_string = ads_get_dn(ads, res);
1447 talloc_destroy(ctx);
1448 ads_msgfree(ads, res);
1449 return ADS_ERROR(LDAP_NO_MEMORY);
1451 ret = ads_gen_mod(ads, dn_string, mods);
1452 ads_memfree(ads,dn_string);
1453 if (!ADS_ERR_OK(ret)) {
1454 DEBUG(1,("ads_clear_service_principal_names: Error: Updating Service Principals for machine %s in LDAP\n",
1456 ads_msgfree(ads, res);
1457 talloc_destroy(ctx);
1461 ads_msgfree(ads, res);
1462 talloc_destroy(ctx);
1467 * This adds a service principal name to an existing computer account
1468 * (found by hostname) in AD.
1469 * @param ads An initialized ADS_STRUCT
1470 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1471 * @param my_fqdn The fully qualified DNS name of the machine
1472 * @param spn A string of the service principal to add, i.e. 'host'
1473 * @return 0 upon sucess, or non-zero if a failure occurs
1476 ADS_STATUS ads_add_service_principal_name(ADS_STRUCT *ads, const char *machine_name,
1477 const char *my_fqdn, const char *spn)
1481 LDAPMessage *res = NULL;
1484 char *dn_string = NULL;
1485 const char *servicePrincipalName[3] = {NULL, NULL, NULL};
1487 ret = ads_find_machine_acct(ads, (void **)(void *)&res, machine_name);
1488 if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
1489 DEBUG(1,("ads_add_service_principal_name: WARNING: Host Account for %s not found... skipping operation.\n",
1491 DEBUG(1,("ads_add_service_principal_name: WARNING: Service Principal '%s/%s@%s' has NOT been added.\n",
1492 spn, machine_name, ads->config.realm));
1493 ads_msgfree(ads, res);
1494 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1497 DEBUG(1,("ads_add_service_principal_name: Host account for %s found\n", machine_name));
1498 if (!(ctx = talloc_init("ads_add_service_principal_name"))) {
1499 ads_msgfree(ads, res);
1500 return ADS_ERROR(LDAP_NO_MEMORY);
1503 /* add short name spn */
1505 if ( (psp1 = talloc_asprintf(ctx, "%s/%s", spn, machine_name)) == NULL ) {
1506 talloc_destroy(ctx);
1507 ads_msgfree(ads, res);
1508 return ADS_ERROR(LDAP_NO_MEMORY);
1511 strlower_m(&psp1[strlen(spn)]);
1512 servicePrincipalName[0] = psp1;
1514 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n",
1515 psp1, machine_name));
1518 /* add fully qualified spn */
1520 if ( (psp2 = talloc_asprintf(ctx, "%s/%s", spn, my_fqdn)) == NULL ) {
1521 ret = ADS_ERROR(LDAP_NO_MEMORY);
1525 strlower_m(&psp2[strlen(spn)]);
1526 servicePrincipalName[1] = psp2;
1528 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n",
1529 psp2, machine_name));
1531 if ( (mods = ads_init_mods(ctx)) == NULL ) {
1532 ret = ADS_ERROR(LDAP_NO_MEMORY);
1536 ret = ads_add_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1537 if (!ADS_ERR_OK(ret)) {
1538 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
1542 if ( (dn_string = ads_get_dn(ads, res)) == NULL ) {
1543 ret = ADS_ERROR(LDAP_NO_MEMORY);
1547 ret = ads_gen_mod(ads, dn_string, mods);
1548 ads_memfree(ads,dn_string);
1549 if (!ADS_ERR_OK(ret)) {
1550 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
1556 ads_msgfree(ads, res);
1561 * adds a machine account to the ADS server
1562 * @param ads An intialized ADS_STRUCT
1563 * @param machine_name - the NetBIOS machine name of this account.
1564 * @param account_type A number indicating the type of account to create
1565 * @param org_unit The LDAP path in which to place this account
1566 * @return 0 upon success, or non-zero otherwise
1569 ADS_STATUS ads_create_machine_acct(ADS_STRUCT *ads, const char *machine_name,
1570 const char *org_unit)
1573 char *samAccountName, *controlstr;
1577 const char *objectClass[] = {"top", "person", "organizationalPerson",
1578 "user", "computer", NULL};
1579 LDAPMessage *res = NULL;
1580 uint32 acct_control = ( UF_WORKSTATION_TRUST_ACCOUNT |\
1581 UF_DONT_EXPIRE_PASSWD |\
1582 UF_ACCOUNTDISABLE );
1584 if (!(ctx = talloc_init("ads_add_machine_acct")))
1585 return ADS_ERROR(LDAP_NO_MEMORY);
1587 ret = ADS_ERROR(LDAP_NO_MEMORY);
1589 new_dn = talloc_asprintf(ctx, "cn=%s,%s", machine_name, org_unit);
1590 samAccountName = talloc_asprintf(ctx, "%s$", machine_name);
1592 if ( !new_dn || !samAccountName ) {
1596 #ifndef ENCTYPE_ARCFOUR_HMAC
1597 acct_control |= UF_USE_DES_KEY_ONLY;
1600 if (!(controlstr = talloc_asprintf(ctx, "%u", acct_control))) {
1604 if (!(mods = ads_init_mods(ctx))) {
1608 ads_mod_str(ctx, &mods, "cn", machine_name);
1609 ads_mod_str(ctx, &mods, "sAMAccountName", samAccountName);
1610 ads_mod_strlist(ctx, &mods, "objectClass", objectClass);
1611 ads_mod_str(ctx, &mods, "userAccountControl", controlstr);
1613 ret = ads_gen_add(ads, new_dn, mods);
1616 ads_msgfree(ads, res);
1617 talloc_destroy(ctx);
1623 dump a binary result from ldap
1625 static void dump_binary(const char *field, struct berval **values)
1628 for (i=0; values[i]; i++) {
1629 printf("%s: ", field);
1630 for (j=0; j<values[i]->bv_len; j++) {
1631 printf("%02X", (unsigned char)values[i]->bv_val[j]);
1637 static void dump_guid(const char *field, struct berval **values)
1641 for (i=0; values[i]; i++) {
1642 memcpy(guid.info, values[i]->bv_val, sizeof(guid.info));
1643 printf("%s: %s\n", field,
1644 smb_uuid_string_static(smb_uuid_unpack_static(guid)));
1649 dump a sid result from ldap
1651 static void dump_sid(const char *field, struct berval **values)
1654 for (i=0; values[i]; i++) {
1656 sid_parse(values[i]->bv_val, values[i]->bv_len, &sid);
1657 printf("%s: %s\n", field, sid_string_static(&sid));
1662 dump ntSecurityDescriptor
1664 static void dump_sd(const char *filed, struct berval **values)
1669 TALLOC_CTX *ctx = 0;
1671 if (!(ctx = talloc_init("sec_io_desc")))
1675 prs_init(&ps, values[0]->bv_len, ctx, UNMARSHALL);
1676 prs_copy_data_in(&ps, values[0]->bv_val, values[0]->bv_len);
1677 prs_set_offset(&ps,0);
1680 if (!sec_io_desc("sd", &psd, &ps, 1)) {
1682 talloc_destroy(ctx);
1685 if (psd) ads_disp_sd(psd);
1688 talloc_destroy(ctx);
1692 dump a string result from ldap
1694 static void dump_string(const char *field, char **values)
1697 for (i=0; values[i]; i++) {
1698 printf("%s: %s\n", field, values[i]);
1703 dump a field from LDAP on stdout
1707 static BOOL ads_dump_field(char *field, void **values, void *data_area)
1712 void (*handler)(const char *, struct berval **);
1714 {"objectGUID", False, dump_guid},
1715 {"netbootGUID", False, dump_guid},
1716 {"nTSecurityDescriptor", False, dump_sd},
1717 {"dnsRecord", False, dump_binary},
1718 {"objectSid", False, dump_sid},
1719 {"tokenGroups", False, dump_sid},
1720 {"tokenGroupsNoGCAcceptable", False, dump_sid},
1721 {"tokengroupsGlobalandUniversal", False, dump_sid},
1726 if (!field) { /* must be end of an entry */
1731 for (i=0; handlers[i].name; i++) {
1732 if (StrCaseCmp(handlers[i].name, field) == 0) {
1733 if (!values) /* first time, indicate string or not */
1734 return handlers[i].string;
1735 handlers[i].handler(field, (struct berval **) values);
1739 if (!handlers[i].name) {
1740 if (!values) /* first time, indicate string conversion */
1742 dump_string(field, (char **)values);
1748 * Dump a result from LDAP on stdout
1749 * used for debugging
1750 * @param ads connection to ads server
1751 * @param res Results to dump
1754 void ads_dump(ADS_STRUCT *ads, void *res)
1756 ads_process_results(ads, res, ads_dump_field, NULL);
1760 * Walk through results, calling a function for each entry found.
1761 * The function receives a field name, a berval * array of values,
1762 * and a data area passed through from the start. The function is
1763 * called once with null for field and values at the end of each
1765 * @param ads connection to ads server
1766 * @param res Results to process
1767 * @param fn Function for processing each result
1768 * @param data_area user-defined area to pass to function
1770 void ads_process_results(ADS_STRUCT *ads, void *res,
1771 BOOL(*fn)(char *, void **, void *),
1777 if (!(ctx = talloc_init("ads_process_results")))
1780 for (msg = ads_first_entry(ads, res); msg;
1781 msg = ads_next_entry(ads, msg)) {
1785 for (utf8_field=ldap_first_attribute(ads->ld,
1786 (LDAPMessage *)msg,&b);
1788 utf8_field=ldap_next_attribute(ads->ld,
1789 (LDAPMessage *)msg,b)) {
1790 struct berval **ber_vals;
1791 char **str_vals, **utf8_vals;
1795 pull_utf8_talloc(ctx, &field, utf8_field);
1796 string = fn(field, NULL, data_area);
1799 utf8_vals = ldap_get_values(ads->ld,
1800 (LDAPMessage *)msg, field);
1801 str_vals = ads_pull_strvals(ctx,
1802 (const char **) utf8_vals);
1803 fn(field, (void **) str_vals, data_area);
1804 ldap_value_free(utf8_vals);
1806 ber_vals = ldap_get_values_len(ads->ld,
1807 (LDAPMessage *)msg, field);
1808 fn(field, (void **) ber_vals, data_area);
1810 ldap_value_free_len(ber_vals);
1812 ldap_memfree(utf8_field);
1815 talloc_free_children(ctx);
1816 fn(NULL, NULL, data_area); /* completed an entry */
1819 talloc_destroy(ctx);
1823 * count how many replies are in a LDAPMessage
1824 * @param ads connection to ads server
1825 * @param res Results to count
1826 * @return number of replies
1828 int ads_count_replies(ADS_STRUCT *ads, void *res)
1830 return ldap_count_entries(ads->ld, (LDAPMessage *)res);
1834 * pull the first entry from a ADS result
1835 * @param ads connection to ads server
1836 * @param res Results of search
1837 * @return first entry from result
1839 void *ads_first_entry(ADS_STRUCT *ads, void *res)
1841 return (void *)ldap_first_entry(ads->ld, (LDAPMessage *)res);
1845 * pull the next entry from a ADS result
1846 * @param ads connection to ads server
1847 * @param res Results of search
1848 * @return next entry from result
1850 void *ads_next_entry(ADS_STRUCT *ads, void *res)
1852 return (void *)ldap_next_entry(ads->ld, (LDAPMessage *)res);
1856 * pull a single string from a ADS result
1857 * @param ads connection to ads server
1858 * @param mem_ctx TALLOC_CTX to use for allocating result string
1859 * @param msg Results of search
1860 * @param field Attribute to retrieve
1861 * @return Result string in talloc context
1863 char *ads_pull_string(ADS_STRUCT *ads,
1864 TALLOC_CTX *mem_ctx, void *msg, const char *field)
1871 values = ldap_get_values(ads->ld, msg, field);
1876 rc = pull_utf8_talloc(mem_ctx, &ux_string,
1878 if (rc != (size_t)-1)
1882 ldap_value_free(values);
1887 * pull an array of strings from a ADS result
1888 * @param ads connection to ads server
1889 * @param mem_ctx TALLOC_CTX to use for allocating result string
1890 * @param msg Results of search
1891 * @param field Attribute to retrieve
1892 * @return Result strings in talloc context
1894 char **ads_pull_strings(ADS_STRUCT *ads,
1895 TALLOC_CTX *mem_ctx, void *msg, const char *field,
1902 values = ldap_get_values(ads->ld, msg, field);
1906 *num_values = ldap_count_values(values);
1908 ret = TALLOC_ARRAY(mem_ctx, char *, *num_values + 1);
1910 ldap_value_free(values);
1914 for (i=0;i<*num_values;i++) {
1915 if (pull_utf8_talloc(mem_ctx, &ret[i], values[i]) == -1) {
1916 ldap_value_free(values);
1922 ldap_value_free(values);
1927 * pull an array of strings from a ADS result
1928 * (handle large multivalue attributes with range retrieval)
1929 * @param ads connection to ads server
1930 * @param mem_ctx TALLOC_CTX to use for allocating result string
1931 * @param msg Results of search
1932 * @param field Attribute to retrieve
1933 * @param current_strings strings returned by a previous call to this function
1934 * @param next_attribute The next query should ask for this attribute
1935 * @param num_values How many values did we get this time?
1936 * @param more_values Are there more values to get?
1937 * @return Result strings in talloc context
1939 char **ads_pull_strings_range(ADS_STRUCT *ads,
1940 TALLOC_CTX *mem_ctx,
1941 void *msg, const char *field,
1942 char **current_strings,
1943 const char **next_attribute,
1944 size_t *num_strings,
1948 char *expected_range_attrib, *range_attr;
1949 BerElement *ptr = NULL;
1952 size_t num_new_strings;
1953 unsigned long int range_start;
1954 unsigned long int range_end;
1956 /* we might have been given the whole lot anyway */
1957 if ((strings = ads_pull_strings(ads, mem_ctx, msg, field, num_strings))) {
1958 *more_strings = False;
1962 expected_range_attrib = talloc_asprintf(mem_ctx, "%s;Range=", field);
1964 /* look for Range result */
1965 for (attr = ldap_first_attribute(ads->ld, (LDAPMessage *)msg, &ptr);
1967 attr = ldap_next_attribute(ads->ld, (LDAPMessage *)msg, ptr)) {
1968 /* we ignore the fact that this is utf8, as all attributes are ascii... */
1969 if (strnequal(attr, expected_range_attrib, strlen(expected_range_attrib))) {
1977 /* nothing here - this field is just empty */
1978 *more_strings = False;
1982 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-%lu",
1983 &range_start, &range_end) == 2) {
1984 *more_strings = True;
1986 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-*",
1987 &range_start) == 1) {
1988 *more_strings = False;
1990 DEBUG(1, ("ads_pull_strings_range: Cannot parse Range attriubte (%s)\n",
1992 ldap_memfree(range_attr);
1993 *more_strings = False;
1998 if ((*num_strings) != range_start) {
1999 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) doesn't start at %u, but at %lu"
2000 " - aborting range retreival\n",
2001 range_attr, (unsigned int)(*num_strings) + 1, range_start));
2002 ldap_memfree(range_attr);
2003 *more_strings = False;
2007 new_strings = ads_pull_strings(ads, mem_ctx, msg, range_attr, &num_new_strings);
2009 if (*more_strings && ((*num_strings + num_new_strings) != (range_end + 1))) {
2010 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) tells us we have %lu "
2011 "strings in this bunch, but we only got %lu - aborting range retreival\n",
2012 range_attr, (unsigned long int)range_end - range_start + 1,
2013 (unsigned long int)num_new_strings));
2014 ldap_memfree(range_attr);
2015 *more_strings = False;
2019 strings = TALLOC_REALLOC_ARRAY(mem_ctx, current_strings, char *,
2020 *num_strings + num_new_strings);
2022 if (strings == NULL) {
2023 ldap_memfree(range_attr);
2024 *more_strings = False;
2028 if (new_strings && num_new_strings) {
2029 memcpy(&strings[*num_strings], new_strings,
2030 sizeof(*new_strings) * num_new_strings);
2033 (*num_strings) += num_new_strings;
2035 if (*more_strings) {
2036 *next_attribute = talloc_asprintf(mem_ctx,
2041 if (!*next_attribute) {
2042 DEBUG(1, ("talloc_asprintf for next attribute failed!\n"));
2043 ldap_memfree(range_attr);
2044 *more_strings = False;
2049 ldap_memfree(range_attr);
2055 * pull a single uint32 from a ADS result
2056 * @param ads connection to ads server
2057 * @param msg Results of search
2058 * @param field Attribute to retrieve
2059 * @param v Pointer to int to store result
2060 * @return boolean inidicating success
2062 BOOL ads_pull_uint32(ADS_STRUCT *ads,
2063 void *msg, const char *field, uint32 *v)
2067 values = ldap_get_values(ads->ld, msg, field);
2071 ldap_value_free(values);
2075 *v = atoi(values[0]);
2076 ldap_value_free(values);
2081 * pull a single objectGUID from an ADS result
2082 * @param ads connection to ADS server
2083 * @param msg results of search
2084 * @param guid 37-byte area to receive text guid
2085 * @return boolean indicating success
2087 BOOL ads_pull_guid(ADS_STRUCT *ads,
2088 void *msg, struct uuid *guid)
2091 UUID_FLAT flat_guid;
2093 values = ldap_get_values(ads->ld, msg, "objectGUID");
2098 memcpy(&flat_guid.info, values[0], sizeof(UUID_FLAT));
2099 smb_uuid_unpack(flat_guid, guid);
2100 ldap_value_free(values);
2103 ldap_value_free(values);
2110 * pull a single DOM_SID from a ADS result
2111 * @param ads connection to ads server
2112 * @param msg Results of search
2113 * @param field Attribute to retrieve
2114 * @param sid Pointer to sid to store result
2115 * @return boolean inidicating success
2117 BOOL ads_pull_sid(ADS_STRUCT *ads,
2118 void *msg, const char *field, DOM_SID *sid)
2120 struct berval **values;
2123 values = ldap_get_values_len(ads->ld, msg, field);
2129 ret = sid_parse(values[0]->bv_val, values[0]->bv_len, sid);
2131 ldap_value_free_len(values);
2136 * pull an array of DOM_SIDs from a ADS result
2137 * @param ads connection to ads server
2138 * @param mem_ctx TALLOC_CTX for allocating sid array
2139 * @param msg Results of search
2140 * @param field Attribute to retrieve
2141 * @param sids pointer to sid array to allocate
2142 * @return the count of SIDs pulled
2144 int ads_pull_sids(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2145 void *msg, const char *field, DOM_SID **sids)
2147 struct berval **values;
2151 values = ldap_get_values_len(ads->ld, msg, field);
2156 for (i=0; values[i]; i++)
2159 (*sids) = TALLOC_ARRAY(mem_ctx, DOM_SID, i);
2161 ldap_value_free_len(values);
2166 for (i=0; values[i]; i++) {
2167 ret = sid_parse(values[i]->bv_val, values[i]->bv_len, &(*sids)[count]);
2170 DEBUG(10, ("pulling SID: %s\n", sid_to_string(sid, &(*sids)[count])));
2175 ldap_value_free_len(values);
2180 * pull a SEC_DESC from a ADS result
2181 * @param ads connection to ads server
2182 * @param mem_ctx TALLOC_CTX for allocating sid array
2183 * @param msg Results of search
2184 * @param field Attribute to retrieve
2185 * @param sd Pointer to *SEC_DESC to store result (talloc()ed)
2186 * @return boolean inidicating success
2188 BOOL ads_pull_sd(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2189 void *msg, const char *field, SEC_DESC **sd)
2191 struct berval **values;
2195 values = ldap_get_values_len(ads->ld, msg, field);
2197 if (!values) return False;
2200 prs_init(&ps, values[0]->bv_len, mem_ctx, UNMARSHALL);
2201 prs_copy_data_in(&ps, values[0]->bv_val, values[0]->bv_len);
2202 prs_set_offset(&ps,0);
2204 ret = sec_io_desc("sd", sd, &ps, 1);
2207 ldap_value_free_len(values);
2212 * in order to support usernames longer than 21 characters we need to
2213 * use both the sAMAccountName and the userPrincipalName attributes
2214 * It seems that not all users have the userPrincipalName attribute set
2216 * @param ads connection to ads server
2217 * @param mem_ctx TALLOC_CTX for allocating sid array
2218 * @param msg Results of search
2219 * @return the username
2221 char *ads_pull_username(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, void *msg)
2226 /* lookup_name() only works on the sAMAccountName to
2227 returning the username portion of userPrincipalName
2228 breaks winbindd_getpwnam() */
2230 ret = ads_pull_string(ads, mem_ctx, msg, "userPrincipalName");
2231 if (ret && (p = strchr_m(ret, '@'))) {
2236 return ads_pull_string(ads, mem_ctx, msg, "sAMAccountName");
2241 * find the update serial number - this is the core of the ldap cache
2242 * @param ads connection to ads server
2243 * @param ads connection to ADS server
2244 * @param usn Pointer to retrieved update serial number
2245 * @return status of search
2247 ADS_STATUS ads_USN(ADS_STRUCT *ads, uint32 *usn)
2249 const char *attrs[] = {"highestCommittedUSN", NULL};
2253 status = ads_do_search_retry(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2254 if (!ADS_ERR_OK(status))
2257 if (ads_count_replies(ads, res) != 1) {
2258 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2261 ads_pull_uint32(ads, res, "highestCommittedUSN", usn);
2262 ads_msgfree(ads, res);
2266 /* parse a ADS timestring - typical string is
2267 '20020917091222.0Z0' which means 09:12.22 17th September
2269 static time_t ads_parse_time(const char *str)
2275 if (sscanf(str, "%4d%2d%2d%2d%2d%2d",
2276 &tm.tm_year, &tm.tm_mon, &tm.tm_mday,
2277 &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) {
2286 /********************************************************************
2287 ********************************************************************/
2289 ADS_STATUS ads_current_time(ADS_STRUCT *ads)
2291 const char *attrs[] = {"currentTime", NULL};
2296 ADS_STRUCT *ads_s = ads;
2298 if (!(ctx = talloc_init("ads_current_time"))) {
2299 return ADS_ERROR(LDAP_NO_MEMORY);
2302 /* establish a new ldap tcp session if necessary */
2305 if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup,
2306 ads->server.ldap_server )) == NULL )
2310 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
2311 status = ads_connect( ads_s );
2312 if ( !ADS_ERR_OK(status))
2316 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2317 if (!ADS_ERR_OK(status)) {
2321 timestr = ads_pull_string(ads_s, ctx, res, "currentTime");
2323 ads_msgfree(ads, res);
2324 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2328 /* but save the time and offset in the original ADS_STRUCT */
2330 ads->config.current_time = ads_parse_time(timestr);
2332 if (ads->config.current_time != 0) {
2333 ads->auth.time_offset = ads->config.current_time - time(NULL);
2334 DEBUG(4,("time offset is %d seconds\n", ads->auth.time_offset));
2337 ads_msgfree(ads, res);
2339 status = ADS_SUCCESS;
2342 /* free any temporary ads connections */
2343 if ( ads_s != ads ) {
2344 ads_destroy( &ads_s );
2346 talloc_destroy(ctx);
2351 /********************************************************************
2352 ********************************************************************/
2354 ADS_STATUS ads_domain_func_level(ADS_STRUCT *ads, uint32 *val)
2356 const char *attrs[] = {"domainFunctionality", NULL};
2359 ADS_STRUCT *ads_s = ads;
2361 *val = DS_DOMAIN_FUNCTION_2000;
2363 /* establish a new ldap tcp session if necessary */
2366 if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup,
2367 ads->server.ldap_server )) == NULL )
2371 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
2372 status = ads_connect( ads_s );
2373 if ( !ADS_ERR_OK(status))
2377 /* If the attribute does not exist assume it is a Windows 2000
2378 functional domain */
2380 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2381 if (!ADS_ERR_OK(status)) {
2382 if ( status.err.rc == LDAP_NO_SUCH_ATTRIBUTE ) {
2383 status = ADS_SUCCESS;
2388 if ( !ads_pull_uint32(ads_s, res, "domainFunctionality", val) ) {
2389 DEBUG(5,("ads_domain_func_level: Failed to pull the domainFunctionality attribute.\n"));
2391 DEBUG(3,("ads_domain_func_level: %d\n", *val));
2394 ads_msgfree(ads, res);
2397 /* free any temporary ads connections */
2398 if ( ads_s != ads ) {
2399 ads_destroy( &ads_s );
2406 * find the domain sid for our domain
2407 * @param ads connection to ads server
2408 * @param sid Pointer to domain sid
2409 * @return status of search
2411 ADS_STATUS ads_domain_sid(ADS_STRUCT *ads, DOM_SID *sid)
2413 const char *attrs[] = {"objectSid", NULL};
2417 rc = ads_do_search_retry(ads, ads->config.bind_path, LDAP_SCOPE_BASE, "(objectclass=*)",
2419 if (!ADS_ERR_OK(rc)) return rc;
2420 if (!ads_pull_sid(ads, res, "objectSid", sid)) {
2421 ads_msgfree(ads, res);
2422 return ADS_ERROR_SYSTEM(ENOENT);
2424 ads_msgfree(ads, res);
2430 * find our site name
2431 * @param ads connection to ads server
2432 * @param mem_ctx Pointer to talloc context
2433 * @param site_name Pointer to the sitename
2434 * @return status of search
2436 ADS_STATUS ads_site_dn(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char **site_name)
2440 const char *dn, *service_name;
2441 const char *attrs[] = { "dsServiceName", NULL };
2443 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2444 if (!ADS_ERR_OK(status)) {
2448 service_name = ads_pull_string(ads, mem_ctx, res, "dsServiceName");
2449 if (service_name == NULL) {
2450 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2453 /* go up three levels */
2454 dn = ads_parent_dn(ads_parent_dn(ads_parent_dn(service_name)));
2456 return ADS_ERROR(LDAP_NO_MEMORY);
2459 *site_name = talloc_strdup(mem_ctx, dn);
2460 if (*site_name == NULL) {
2461 return ADS_ERROR(LDAP_NO_MEMORY);
2464 ads_msgfree(ads, res);
2468 dsServiceName: CN=NTDS Settings,CN=W2K3DC,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,DC=ber,DC=suse,DC=de
2473 * find the site dn where a machine resides
2474 * @param ads connection to ads server
2475 * @param mem_ctx Pointer to talloc context
2476 * @param computer_name name of the machine
2477 * @param site_name Pointer to the sitename
2478 * @return status of search
2480 ADS_STATUS ads_site_dn_for_machine(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char *computer_name, const char **site_dn)
2484 const char *parent, *config_context, *filter;
2485 const char *attrs[] = { "configurationNamingContext", NULL };
2488 /* shortcut a query */
2489 if (strequal(computer_name, ads->config.ldap_server_name)) {
2490 return ads_site_dn(ads, mem_ctx, site_dn);
2493 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2494 if (!ADS_ERR_OK(status)) {
2498 config_context = ads_pull_string(ads, mem_ctx, res, "configurationNamingContext");
2499 if (config_context == NULL) {
2500 return ADS_ERROR(LDAP_NO_MEMORY);
2503 filter = talloc_asprintf(mem_ctx, "(cn=%s)", computer_name);
2504 if (filter == NULL) {
2505 return ADS_ERROR(LDAP_NO_MEMORY);
2508 status = ads_do_search(ads, config_context, LDAP_SCOPE_SUBTREE, filter, NULL, &res);
2509 if (!ADS_ERR_OK(status)) {
2513 if (ads_count_replies(ads, res) != 1) {
2514 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
2517 dn = ads_get_dn(ads, res);
2519 return ADS_ERROR(LDAP_NO_MEMORY);
2522 /* go up three levels */
2523 parent = ads_parent_dn(ads_parent_dn(ads_parent_dn(dn)));
2524 if (parent == NULL) {
2525 ads_memfree(ads, dn);
2526 return ADS_ERROR(LDAP_NO_MEMORY);
2529 *site_dn = talloc_strdup(mem_ctx, parent);
2530 if (*site_dn == NULL) {
2531 ads_memfree(ads, dn);
2532 ADS_ERROR(LDAP_NO_MEMORY);
2535 ads_memfree(ads, dn);
2536 ads_msgfree(ads, res);
2542 * get the upn suffixes for a domain
2543 * @param ads connection to ads server
2544 * @param mem_ctx Pointer to talloc context
2545 * @param suffixes Pointer to an array of suffixes
2546 * @param site_name Pointer to the number of suffixes
2547 * @return status of search
2549 ADS_STATUS ads_upn_suffixes(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, char **suffixes, size_t *num_suffixes)
2553 const char *config_context, *base;
2554 const char *attrs[] = { "configurationNamingContext", NULL };
2555 const char *attrs2[] = { "uPNSuffixes", NULL };
2557 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2558 if (!ADS_ERR_OK(status)) {
2562 config_context = ads_pull_string(ads, mem_ctx, res, "configurationNamingContext");
2563 if (config_context == NULL) {
2564 return ADS_ERROR(LDAP_NO_MEMORY);
2567 base = talloc_asprintf(mem_ctx, "cn=Partitions,%s", config_context);
2569 return ADS_ERROR(LDAP_NO_MEMORY);
2572 status = ads_search_dn(ads, &res, base, attrs2);
2573 if (!ADS_ERR_OK(status)) {
2577 if (ads_count_replies(ads, res) != 1) {
2578 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
2581 suffixes = ads_pull_strings(ads, mem_ctx, &res, "uPNSuffixes", num_suffixes);
2582 if (suffixes == NULL) {
2583 ads_msgfree(ads, res);
2584 return ADS_ERROR(LDAP_NO_MEMORY);
2587 ads_msgfree(ads, res);
2593 * pull a DOM_SID from an extended dn string
2594 * @param mem_ctx TALLOC_CTX
2595 * @param flags string type of extended_dn
2596 * @param sid pointer to a DOM_SID
2597 * @return boolean inidicating success
2599 BOOL ads_get_sid_from_extended_dn(TALLOC_CTX *mem_ctx,
2601 enum ads_extended_dn_flags flags,
2611 * ADS_EXTENDED_DN_HEX_STRING:
2612 * <GUID=238e1963cb390f4bb032ba0105525a29>;<SID=010500000000000515000000bb68c8fd6b61b427572eb04556040000>;CN=gd,OU=berlin,OU=suse,DC=ber,DC=suse,DC=de
2614 * ADS_EXTENDED_DN_STRING (only with w2k3):
2615 <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
2618 p = strchr(dn, ';');
2623 if (strncmp(p, ";<SID=", strlen(";<SID=")) != 0) {
2627 p += strlen(";<SID=");
2636 DEBUG(100,("ads_get_sid_from_extended_dn: sid string is %s\n", p));
2640 case ADS_EXTENDED_DN_STRING:
2641 if (!string_to_sid(sid, p)) {
2645 case ADS_EXTENDED_DN_HEX_STRING: {
2649 buf_len = strhex_to_str(buf, strlen(p), p);
2654 if (!sid_parse(buf, buf_len, sid)) {
2655 DEBUG(10,("failed to parse sid\n"));
2661 DEBUG(10,("unknown extended dn format\n"));
2669 * pull an array of DOM_SIDs from a ADS result
2670 * @param ads connection to ads server
2671 * @param mem_ctx TALLOC_CTX for allocating sid array
2672 * @param msg Results of search
2673 * @param field Attribute to retrieve
2674 * @param flags string type of extended_dn
2675 * @param sids pointer to sid array to allocate
2676 * @return the count of SIDs pulled
2678 int ads_pull_sids_from_extendeddn(ADS_STRUCT *ads,
2679 TALLOC_CTX *mem_ctx,
2682 enum ads_extended_dn_flags flags,
2689 if ((dn_strings = ads_pull_strings(ads, mem_ctx, msg, field,
2690 &dn_count)) == NULL) {
2694 (*sids) = TALLOC_ZERO_ARRAY(mem_ctx, DOM_SID, dn_count + 1);
2696 TALLOC_FREE(dn_strings);
2700 for (i=0; i<dn_count; i++) {
2702 if (!ads_get_sid_from_extended_dn(mem_ctx, dn_strings[i],
2703 flags, &(*sids)[i])) {
2705 TALLOC_FREE(dn_strings);
2710 TALLOC_FREE(dn_strings);
2715 /********************************************************************
2716 ********************************************************************/
2718 char* ads_get_dnshostname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
2720 LDAPMessage *res = NULL;
2725 status = ads_find_machine_acct(ads, (void **)(void *)&res, global_myname());
2726 if (!ADS_ERR_OK(status)) {
2727 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
2732 if ( (count = ads_count_replies(ads, res)) != 1 ) {
2733 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
2737 if ( (name = ads_pull_string(ads, ctx, res, "dNSHostName")) == NULL ) {
2738 DEBUG(0,("ads_get_dnshostname: No dNSHostName attribute!\n"));
2742 ads_msgfree(ads, res);
2747 /********************************************************************
2748 ********************************************************************/
2750 char* ads_get_upn( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
2752 LDAPMessage *res = NULL;
2757 status = ads_find_machine_acct(ads, (void **)(void *)&res, global_myname());
2758 if (!ADS_ERR_OK(status)) {
2759 DEBUG(0,("ads_get_upn: Failed to find account for %s\n",
2764 if ( (count = ads_count_replies(ads, res)) != 1 ) {
2765 DEBUG(1,("ads_get_upn: %d entries returned!\n", count));
2769 if ( (name = ads_pull_string(ads, ctx, res, "userPrincipalName")) == NULL ) {
2770 DEBUG(2,("ads_get_upn: No userPrincipalName attribute!\n"));
2774 ads_msgfree(ads, res);
2779 /********************************************************************
2780 ********************************************************************/
2782 char* ads_get_samaccountname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
2784 LDAPMessage *res = NULL;
2789 status = ads_find_machine_acct(ads, (void **)(void *)&res, global_myname());
2790 if (!ADS_ERR_OK(status)) {
2791 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
2796 if ( (count = ads_count_replies(ads, res)) != 1 ) {
2797 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
2801 if ( (name = ads_pull_string(ads, ctx, res, "sAMAccountName")) == NULL ) {
2802 DEBUG(0,("ads_get_dnshostname: No sAMAccountName attribute!\n"));
2806 ads_msgfree(ads, res);