r23772: Add ads_find_samaccount() helper function.
[Samba.git] / source / libads / ldap.c
blob6f0ca3452ca2d8903ae63aafe5a43572dc443604
1 /*
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.
25 #include "includes.h"
27 #ifdef HAVE_LDAP
29 /**
30 * @file ldap.c
31 * @brief basic ldap client-side routines for ads server communications
33 * The routines contained here should do the necessary ldap calls for
34 * ads setups.
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
40 **/
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)
53 gotalarm = 1;
56 LDAP *ldap_open_with_timeout(const char *server, int port, unsigned int to)
58 LDAP *ldp = NULL;
60 /* Setup timeout */
61 gotalarm = 0;
62 CatchSignal(SIGALRM, SIGNAL_CAST gotalarm_sig);
63 alarm(to);
64 /* End setup timeout. */
66 ldp = ldap_open(server, port);
68 if (ldp == NULL) {
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);
75 alarm(0);
77 return ldp;
80 static int ldap_search_with_timeout(LDAP *ld,
81 LDAP_CONST char *base,
82 int scope,
83 LDAP_CONST char *filter,
84 char **attrs,
85 int attrsonly,
86 LDAPControl **sctrls,
87 LDAPControl **cctrls,
88 int sizelimit,
89 LDAPMessage **res )
91 struct timeval timeout;
92 int result;
94 /* Setup timeout for the ldap_search_ext_s call - local and remote. */
95 timeout.tv_sec = lp_ldap_timeout();
96 timeout.tv_usec = 0;
98 /* Setup alarm timeout.... Do we need both of these ? JRA. */
99 gotalarm = 0;
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,
106 sizelimit, res);
108 /* Teardown timeout. */
109 CatchSignal(SIGALRM, SIGNAL_CAST SIG_IGN);
110 alarm(0);
112 if (gotalarm != 0)
113 return LDAP_TIMELIMIT_EXCEEDED;
115 return result;
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"));
127 return True;
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));
134 return True;
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"));
139 return False;
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"));
150 return True;
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"));
156 return True;
159 DEBUG(10,("ads_closest_dc: %s is not the closest DC\n",
160 ads->config.ldap_server_name));
162 return False;
167 try a connection to a given ldap server, returning True and setting the servers IP
168 in the ads struct if successful
170 BOOL ads_try_connect(ADS_STRUCT *ads, const char *server )
172 char *srv;
173 struct cldap_netlogon_reply cldap_reply;
175 if (!server || !*server) {
176 return False;
179 DEBUG(5,("ads_try_connect: sending CLDAP request to %s (realm: %s)\n",
180 server, ads->server.realm));
182 /* this copes with inet_ntoa brokenness */
184 srv = SMB_STRDUP(server);
186 ZERO_STRUCT( cldap_reply );
188 if ( !ads_cldap_netlogon( srv, ads->server.realm, &cldap_reply ) ) {
189 DEBUG(3,("ads_try_connect: CLDAP request %s failed.\n", srv));
190 SAFE_FREE( srv );
191 return False;
194 /* Check the CLDAP reply flags */
196 if ( !(cldap_reply.flags & ADS_LDAP) ) {
197 DEBUG(1,("ads_try_connect: %s's CLDAP reply says it is not an LDAP server!\n",
198 srv));
199 SAFE_FREE( srv );
200 return False;
203 /* Fill in the ads->config values */
205 SAFE_FREE(ads->config.realm);
206 SAFE_FREE(ads->config.bind_path);
207 SAFE_FREE(ads->config.ldap_server_name);
208 SAFE_FREE(ads->config.server_site_name);
209 SAFE_FREE(ads->config.client_site_name);
210 SAFE_FREE(ads->server.workgroup);
212 ads->config.flags = cldap_reply.flags;
213 ads->config.ldap_server_name = SMB_STRDUP(cldap_reply.hostname);
214 strupper_m(cldap_reply.domain);
215 ads->config.realm = SMB_STRDUP(cldap_reply.domain);
216 ads->config.bind_path = ads_build_dn(ads->config.realm);
217 if (*cldap_reply.server_site_name) {
218 ads->config.server_site_name =
219 SMB_STRDUP(cldap_reply.server_site_name);
221 if (*cldap_reply.client_site_name) {
222 ads->config.client_site_name =
223 SMB_STRDUP(cldap_reply.client_site_name);
226 ads->server.workgroup = SMB_STRDUP(cldap_reply.netbios_domain);
228 ads->ldap_port = LDAP_PORT;
229 ads->ldap_ip = *interpret_addr2(srv);
230 SAFE_FREE(srv);
232 /* Store our site name. */
233 sitename_store( cldap_reply.domain, cldap_reply.client_site_name );
235 return True;
238 /**********************************************************************
239 Try to find an AD dc using our internal name resolution routines
240 Try the realm first and then then workgroup name if netbios is not
241 disabled
242 **********************************************************************/
244 static NTSTATUS ads_find_dc(ADS_STRUCT *ads)
246 const char *c_realm;
247 int count, i=0;
248 struct ip_service *ip_list;
249 pstring realm;
250 BOOL got_realm = False;
251 BOOL use_own_domain = False;
252 char *sitename;
253 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
255 /* if the realm and workgroup are both empty, assume they are ours */
257 /* realm */
258 c_realm = ads->server.realm;
260 if ( !c_realm || !*c_realm ) {
261 /* special case where no realm and no workgroup means our own */
262 if ( !ads->server.workgroup || !*ads->server.workgroup ) {
263 use_own_domain = True;
264 c_realm = lp_realm();
268 if (c_realm && *c_realm)
269 got_realm = True;
271 /* we need to try once with the realm name and fallback to the
272 netbios domain name if we fail (if netbios has not been disabled */
274 if ( !got_realm && !lp_disable_netbios() ) {
275 c_realm = ads->server.workgroup;
276 if (!c_realm || !*c_realm) {
277 if ( use_own_domain )
278 c_realm = lp_workgroup();
281 if ( !c_realm || !*c_realm ) {
282 DEBUG(0,("ads_find_dc: no realm or workgroup! Don't know what to do\n"));
283 return NT_STATUS_INVALID_PARAMETER; /* rather need MISSING_PARAMETER ... */
287 pstrcpy( realm, c_realm );
289 sitename = sitename_fetch(realm);
291 again:
293 DEBUG(6,("ads_find_dc: looking for %s '%s'\n",
294 (got_realm ? "realm" : "domain"), realm));
296 status = get_sorted_dc_list(realm, sitename, &ip_list, &count, got_realm);
297 if (!NT_STATUS_IS_OK(status)) {
298 /* fall back to netbios if we can */
299 if ( got_realm && !lp_disable_netbios() ) {
300 got_realm = False;
301 goto again;
304 SAFE_FREE(sitename);
305 return status;
308 /* if we fail this loop, then giveup since all the IP addresses returned were dead */
309 for ( i=0; i<count; i++ ) {
310 fstring server;
312 fstrcpy( server, inet_ntoa(ip_list[i].ip) );
314 if ( !NT_STATUS_IS_OK(check_negative_conn_cache(realm, server)) )
315 continue;
317 if (!got_realm) {
318 /* realm in this case is a workgroup name. We need
319 to ignore any IP addresses in the negative connection
320 cache that match ip addresses returned in the ad realm
321 case. It sucks that I have to reproduce the logic above... */
322 c_realm = ads->server.realm;
323 if ( !c_realm || !*c_realm ) {
324 if ( !ads->server.workgroup || !*ads->server.workgroup ) {
325 c_realm = lp_realm();
328 if (c_realm && *c_realm &&
329 !NT_STATUS_IS_OK(check_negative_conn_cache(c_realm, server))) {
330 /* Ensure we add the workgroup name for this
331 IP address as negative too. */
332 add_failed_connection_entry( realm, server, NT_STATUS_UNSUCCESSFUL );
333 continue;
337 if ( ads_try_connect(ads, server) ) {
338 SAFE_FREE(ip_list);
339 SAFE_FREE(sitename);
340 return NT_STATUS_OK;
343 /* keep track of failures */
344 add_failed_connection_entry( realm, server, NT_STATUS_UNSUCCESSFUL );
347 SAFE_FREE(ip_list);
349 /* In case we failed to contact one of our closest DC on our site we
350 * need to try to find another DC, retry with a site-less SRV DNS query
351 * - Guenther */
353 if (sitename) {
354 DEBUG(1,("ads_find_dc: failed to find a valid DC on our site (%s), "
355 "trying to find another DC\n", sitename));
356 SAFE_FREE(sitename);
357 namecache_delete(realm, 0x1C);
358 goto again;
361 return NT_STATUS_NO_LOGON_SERVERS;
366 * Connect to the LDAP server
367 * @param ads Pointer to an existing ADS_STRUCT
368 * @return status of connection
370 ADS_STATUS ads_connect(ADS_STRUCT *ads)
372 int version = LDAP_VERSION3;
373 ADS_STATUS status;
374 NTSTATUS ntstatus;
376 ads->last_attempt = time(NULL);
377 ads->ld = NULL;
379 /* try with a user specified server */
381 if (ads->server.ldap_server &&
382 ads_try_connect(ads, ads->server.ldap_server)) {
383 goto got_connection;
386 ntstatus = ads_find_dc(ads);
387 if (NT_STATUS_IS_OK(ntstatus)) {
388 goto got_connection;
391 return ADS_ERROR_NT(ntstatus);
393 got_connection:
394 DEBUG(3,("Connected to LDAP server %s\n", inet_ntoa(ads->ldap_ip)));
396 if (!ads->auth.user_name) {
397 /* Must use the userPrincipalName value here or sAMAccountName
398 and not servicePrincipalName; found by Guenther Deschner */
400 asprintf(&ads->auth.user_name, "%s$", global_myname() );
403 if (!ads->auth.realm) {
404 ads->auth.realm = SMB_STRDUP(ads->config.realm);
407 if (!ads->auth.kdc_server) {
408 ads->auth.kdc_server = SMB_STRDUP(inet_ntoa(ads->ldap_ip));
411 #if KRB5_DNS_HACK
412 /* this is a really nasty hack to avoid ADS DNS problems. It needs a patch
413 to MIT kerberos to work (tridge) */
415 char *env;
416 asprintf(&env, "KRB5_KDC_ADDRESS_%s", ads->config.realm);
417 setenv(env, ads->auth.kdc_server, 1);
418 free(env);
420 #endif
422 /* If the caller() requested no LDAP bind, then we are done */
424 if (ads->auth.flags & ADS_AUTH_NO_BIND) {
425 return ADS_SUCCESS;
428 /* Otherwise setup the TCP LDAP session */
430 if ( (ads->ld = ldap_open_with_timeout(ads->config.ldap_server_name,
431 LDAP_PORT, lp_ldap_timeout())) == NULL )
433 return ADS_ERROR(LDAP_OPERATIONS_ERROR);
436 /* cache the successful connection for workgroup and realm */
437 if (ads_closest_dc(ads)) {
438 saf_store( ads->server.workgroup, inet_ntoa(ads->ldap_ip));
439 saf_store( ads->server.realm, inet_ntoa(ads->ldap_ip));
442 ldap_set_option(ads->ld, LDAP_OPT_PROTOCOL_VERSION, &version);
444 status = ADS_ERROR(smb_ldap_start_tls(ads->ld, version));
445 if (!ADS_ERR_OK(status)) {
446 return status;
449 /* fill in the current time and offsets */
451 status = ads_current_time( ads );
452 if ( !ADS_ERR_OK(status) ) {
453 return status;
456 /* Now do the bind */
458 if (ads->auth.flags & ADS_AUTH_ANON_BIND) {
459 return ADS_ERROR(ldap_simple_bind_s( ads->ld, NULL, NULL));
462 if (ads->auth.flags & ADS_AUTH_SIMPLE_BIND) {
463 return ADS_ERROR(ldap_simple_bind_s( ads->ld, ads->auth.user_name, ads->auth.password));
466 return ads_sasl_bind(ads);
470 Duplicate a struct berval into talloc'ed memory
472 static struct berval *dup_berval(TALLOC_CTX *ctx, const struct berval *in_val)
474 struct berval *value;
476 if (!in_val) return NULL;
478 value = TALLOC_ZERO_P(ctx, struct berval);
479 if (value == NULL)
480 return NULL;
481 if (in_val->bv_len == 0) return value;
483 value->bv_len = in_val->bv_len;
484 value->bv_val = (char *)TALLOC_MEMDUP(ctx, in_val->bv_val,
485 in_val->bv_len);
486 return value;
490 Make a values list out of an array of (struct berval *)
492 static struct berval **ads_dup_values(TALLOC_CTX *ctx,
493 const struct berval **in_vals)
495 struct berval **values;
496 int i;
498 if (!in_vals) return NULL;
499 for (i=0; in_vals[i]; i++)
500 ; /* count values */
501 values = TALLOC_ZERO_ARRAY(ctx, struct berval *, i+1);
502 if (!values) return NULL;
504 for (i=0; in_vals[i]; i++) {
505 values[i] = dup_berval(ctx, in_vals[i]);
507 return values;
511 UTF8-encode a values list out of an array of (char *)
513 static char **ads_push_strvals(TALLOC_CTX *ctx, const char **in_vals)
515 char **values;
516 int i;
518 if (!in_vals) return NULL;
519 for (i=0; in_vals[i]; i++)
520 ; /* count values */
521 values = TALLOC_ZERO_ARRAY(ctx, char *, i+1);
522 if (!values) return NULL;
524 for (i=0; in_vals[i]; i++) {
525 push_utf8_talloc(ctx, &values[i], in_vals[i]);
527 return values;
531 Pull a (char *) array out of a UTF8-encoded values list
533 static char **ads_pull_strvals(TALLOC_CTX *ctx, const char **in_vals)
535 char **values;
536 int i;
538 if (!in_vals) return NULL;
539 for (i=0; in_vals[i]; i++)
540 ; /* count values */
541 values = TALLOC_ZERO_ARRAY(ctx, char *, i+1);
542 if (!values) return NULL;
544 for (i=0; in_vals[i]; i++) {
545 pull_utf8_talloc(ctx, &values[i], in_vals[i]);
547 return values;
551 * Do a search with paged results. cookie must be null on the first
552 * call, and then returned on each subsequent call. It will be null
553 * again when the entire search is complete
554 * @param ads connection to ads server
555 * @param bind_path Base dn for the search
556 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
557 * @param expr Search expression - specified in local charset
558 * @param attrs Attributes to retrieve - specified in utf8 or ascii
559 * @param res ** which will contain results - free res* with ads_msgfree()
560 * @param count Number of entries retrieved on this page
561 * @param cookie The paged results cookie to be returned on subsequent calls
562 * @return status of search
564 static ADS_STATUS ads_do_paged_search_args(ADS_STRUCT *ads,
565 const char *bind_path,
566 int scope, const char *expr,
567 const char **attrs, void *args,
568 LDAPMessage **res,
569 int *count, struct berval **cookie)
571 int rc, i, version;
572 char *utf8_expr, *utf8_path, **search_attrs;
573 LDAPControl PagedResults, NoReferrals, ExternalCtrl, *controls[4], **rcontrols;
574 BerElement *cookie_be = NULL;
575 struct berval *cookie_bv= NULL;
576 BerElement *ext_be = NULL;
577 struct berval *ext_bv= NULL;
579 TALLOC_CTX *ctx;
580 ads_control *external_control = (ads_control *) args;
582 *res = NULL;
584 if (!(ctx = talloc_init("ads_do_paged_search_args")))
585 return ADS_ERROR(LDAP_NO_MEMORY);
587 /* 0 means the conversion worked but the result was empty
588 so we only fail if it's -1. In any case, it always
589 at least nulls out the dest */
590 if ((push_utf8_talloc(ctx, &utf8_expr, expr) == (size_t)-1) ||
591 (push_utf8_talloc(ctx, &utf8_path, bind_path) == (size_t)-1)) {
592 rc = LDAP_NO_MEMORY;
593 goto done;
596 if (!attrs || !(*attrs))
597 search_attrs = NULL;
598 else {
599 /* This would be the utf8-encoded version...*/
600 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
601 if (!(str_list_copy(&search_attrs, attrs))) {
602 rc = LDAP_NO_MEMORY;
603 goto done;
607 /* Paged results only available on ldap v3 or later */
608 ldap_get_option(ads->ld, LDAP_OPT_PROTOCOL_VERSION, &version);
609 if (version < LDAP_VERSION3) {
610 rc = LDAP_NOT_SUPPORTED;
611 goto done;
614 cookie_be = ber_alloc_t(LBER_USE_DER);
615 if (*cookie) {
616 ber_printf(cookie_be, "{iO}", (ber_int_t) 1000, *cookie);
617 ber_bvfree(*cookie); /* don't need it from last time */
618 *cookie = NULL;
619 } else {
620 ber_printf(cookie_be, "{io}", (ber_int_t) 1000, "", 0);
622 ber_flatten(cookie_be, &cookie_bv);
623 PagedResults.ldctl_oid = CONST_DISCARD(char *, ADS_PAGE_CTL_OID);
624 PagedResults.ldctl_iscritical = (char) 1;
625 PagedResults.ldctl_value.bv_len = cookie_bv->bv_len;
626 PagedResults.ldctl_value.bv_val = cookie_bv->bv_val;
628 NoReferrals.ldctl_oid = CONST_DISCARD(char *, ADS_NO_REFERRALS_OID);
629 NoReferrals.ldctl_iscritical = (char) 0;
630 NoReferrals.ldctl_value.bv_len = 0;
631 NoReferrals.ldctl_value.bv_val = CONST_DISCARD(char *, "");
633 if (external_control &&
634 (strequal(external_control->control, ADS_EXTENDED_DN_OID) ||
635 strequal(external_control->control, ADS_SD_FLAGS_OID))) {
637 ExternalCtrl.ldctl_oid = CONST_DISCARD(char *, external_control->control);
638 ExternalCtrl.ldctl_iscritical = (char) external_control->critical;
640 /* win2k does not accept a ldctl_value beeing passed in */
642 if (external_control->val != 0) {
644 if ((ext_be = ber_alloc_t(LBER_USE_DER)) == NULL ) {
645 rc = LDAP_NO_MEMORY;
646 goto done;
649 if ((ber_printf(ext_be, "{i}", (ber_int_t) external_control->val)) == -1) {
650 rc = LDAP_NO_MEMORY;
651 goto done;
653 if ((ber_flatten(ext_be, &ext_bv)) == -1) {
654 rc = LDAP_NO_MEMORY;
655 goto done;
658 ExternalCtrl.ldctl_value.bv_len = ext_bv->bv_len;
659 ExternalCtrl.ldctl_value.bv_val = ext_bv->bv_val;
661 } else {
662 ExternalCtrl.ldctl_value.bv_len = 0;
663 ExternalCtrl.ldctl_value.bv_val = NULL;
666 controls[0] = &NoReferrals;
667 controls[1] = &PagedResults;
668 controls[2] = &ExternalCtrl;
669 controls[3] = NULL;
671 } else {
672 controls[0] = &NoReferrals;
673 controls[1] = &PagedResults;
674 controls[2] = NULL;
677 /* we need to disable referrals as the openldap libs don't
678 handle them and paged results at the same time. Using them
679 together results in the result record containing the server
680 page control being removed from the result list (tridge/jmcd)
682 leaving this in despite the control that says don't generate
683 referrals, in case the server doesn't support it (jmcd)
685 ldap_set_option(ads->ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
687 rc = ldap_search_with_timeout(ads->ld, utf8_path, scope, utf8_expr,
688 search_attrs, 0, controls,
689 NULL, LDAP_NO_LIMIT,
690 (LDAPMessage **)res);
692 ber_free(cookie_be, 1);
693 ber_bvfree(cookie_bv);
695 if (rc) {
696 DEBUG(3,("ads_do_paged_search_args: ldap_search_with_timeout(%s) -> %s\n", expr,
697 ldap_err2string(rc)));
698 goto done;
701 rc = ldap_parse_result(ads->ld, *res, NULL, NULL, NULL,
702 NULL, &rcontrols, 0);
704 if (!rcontrols) {
705 goto done;
708 for (i=0; rcontrols[i]; i++) {
709 if (strcmp(ADS_PAGE_CTL_OID, rcontrols[i]->ldctl_oid) == 0) {
710 cookie_be = ber_init(&rcontrols[i]->ldctl_value);
711 ber_scanf(cookie_be,"{iO}", (ber_int_t *) count,
712 &cookie_bv);
713 /* the berval is the cookie, but must be freed when
714 it is all done */
715 if (cookie_bv->bv_len) /* still more to do */
716 *cookie=ber_bvdup(cookie_bv);
717 else
718 *cookie=NULL;
719 ber_bvfree(cookie_bv);
720 ber_free(cookie_be, 1);
721 break;
724 ldap_controls_free(rcontrols);
726 done:
727 talloc_destroy(ctx);
729 if (ext_be) {
730 ber_free(ext_be, 1);
733 if (ext_bv) {
734 ber_bvfree(ext_bv);
737 /* if/when we decide to utf8-encode attrs, take out this next line */
738 str_list_free(&search_attrs);
740 return ADS_ERROR(rc);
743 static ADS_STATUS ads_do_paged_search(ADS_STRUCT *ads, const char *bind_path,
744 int scope, const char *expr,
745 const char **attrs, LDAPMessage **res,
746 int *count, struct berval **cookie)
748 return ads_do_paged_search_args(ads, bind_path, scope, expr, attrs, NULL, res, count, cookie);
753 * Get all results for a search. This uses ads_do_paged_search() to return
754 * all entries in a large search.
755 * @param ads connection to ads server
756 * @param bind_path Base dn for the search
757 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
758 * @param expr Search expression
759 * @param attrs Attributes to retrieve
760 * @param res ** which will contain results - free res* with ads_msgfree()
761 * @return status of search
763 ADS_STATUS ads_do_search_all_args(ADS_STRUCT *ads, const char *bind_path,
764 int scope, const char *expr,
765 const char **attrs, void *args,
766 LDAPMessage **res)
768 struct berval *cookie = NULL;
769 int count = 0;
770 ADS_STATUS status;
772 *res = NULL;
773 status = ads_do_paged_search_args(ads, bind_path, scope, expr, attrs, args, res,
774 &count, &cookie);
776 if (!ADS_ERR_OK(status))
777 return status;
779 #ifdef HAVE_LDAP_ADD_RESULT_ENTRY
780 while (cookie) {
781 LDAPMessage *res2 = NULL;
782 ADS_STATUS status2;
783 LDAPMessage *msg, *next;
785 status2 = ads_do_paged_search_args(ads, bind_path, scope, expr,
786 attrs, args, &res2, &count, &cookie);
788 if (!ADS_ERR_OK(status2)) break;
790 /* this relies on the way that ldap_add_result_entry() works internally. I hope
791 that this works on all ldap libs, but I have only tested with openldap */
792 for (msg = ads_first_entry(ads, res2); msg; msg = next) {
793 next = ads_next_entry(ads, msg);
794 ldap_add_result_entry((LDAPMessage **)res, msg);
796 /* note that we do not free res2, as the memory is now
797 part of the main returned list */
799 #else
800 DEBUG(0, ("no ldap_add_result_entry() support in LDAP libs!\n"));
801 status = ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
802 #endif
804 return status;
807 ADS_STATUS ads_do_search_all(ADS_STRUCT *ads, const char *bind_path,
808 int scope, const char *expr,
809 const char **attrs, LDAPMessage **res)
811 return ads_do_search_all_args(ads, bind_path, scope, expr, attrs, NULL, res);
814 ADS_STATUS ads_do_search_all_sd_flags(ADS_STRUCT *ads, const char *bind_path,
815 int scope, const char *expr,
816 const char **attrs, uint32 sd_flags,
817 LDAPMessage **res)
819 ads_control args;
821 args.control = ADS_SD_FLAGS_OID;
822 args.val = sd_flags;
823 args.critical = True;
825 return ads_do_search_all_args(ads, bind_path, scope, expr, attrs, &args, res);
830 * Run a function on all results for a search. Uses ads_do_paged_search() and
831 * runs the function as each page is returned, using ads_process_results()
832 * @param ads connection to ads server
833 * @param bind_path Base dn for the search
834 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
835 * @param expr Search expression - specified in local charset
836 * @param attrs Attributes to retrieve - specified in UTF-8 or ascii
837 * @param fn Function which takes attr name, values list, and data_area
838 * @param data_area Pointer which is passed to function on each call
839 * @return status of search
841 ADS_STATUS ads_do_search_all_fn(ADS_STRUCT *ads, const char *bind_path,
842 int scope, const char *expr, const char **attrs,
843 BOOL(*fn)(char *, void **, void *),
844 void *data_area)
846 struct berval *cookie = NULL;
847 int count = 0;
848 ADS_STATUS status;
849 LDAPMessage *res;
851 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs, &res,
852 &count, &cookie);
854 if (!ADS_ERR_OK(status)) return status;
856 ads_process_results(ads, res, fn, data_area);
857 ads_msgfree(ads, res);
859 while (cookie) {
860 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs,
861 &res, &count, &cookie);
863 if (!ADS_ERR_OK(status)) break;
865 ads_process_results(ads, res, fn, data_area);
866 ads_msgfree(ads, res);
869 return status;
873 * Do a search with a timeout.
874 * @param ads connection to ads server
875 * @param bind_path Base dn for the search
876 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
877 * @param expr Search expression
878 * @param attrs Attributes to retrieve
879 * @param res ** which will contain results - free res* with ads_msgfree()
880 * @return status of search
882 ADS_STATUS ads_do_search(ADS_STRUCT *ads, const char *bind_path, int scope,
883 const char *expr,
884 const char **attrs, LDAPMessage **res)
886 int rc;
887 char *utf8_expr, *utf8_path, **search_attrs = NULL;
888 TALLOC_CTX *ctx;
890 *res = NULL;
891 if (!(ctx = talloc_init("ads_do_search"))) {
892 DEBUG(1,("ads_do_search: talloc_init() failed!"));
893 return ADS_ERROR(LDAP_NO_MEMORY);
896 /* 0 means the conversion worked but the result was empty
897 so we only fail if it's negative. In any case, it always
898 at least nulls out the dest */
899 if ((push_utf8_talloc(ctx, &utf8_expr, expr) == (size_t)-1) ||
900 (push_utf8_talloc(ctx, &utf8_path, bind_path) == (size_t)-1)) {
901 DEBUG(1,("ads_do_search: push_utf8_talloc() failed!"));
902 rc = LDAP_NO_MEMORY;
903 goto done;
906 if (!attrs || !(*attrs))
907 search_attrs = NULL;
908 else {
909 /* This would be the utf8-encoded version...*/
910 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
911 if (!(str_list_copy(&search_attrs, attrs)))
913 DEBUG(1,("ads_do_search: str_list_copy() failed!"));
914 rc = LDAP_NO_MEMORY;
915 goto done;
919 /* see the note in ads_do_paged_search - we *must* disable referrals */
920 ldap_set_option(ads->ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
922 rc = ldap_search_with_timeout(ads->ld, utf8_path, scope, utf8_expr,
923 search_attrs, 0, NULL, NULL,
924 LDAP_NO_LIMIT,
925 (LDAPMessage **)res);
927 if (rc == LDAP_SIZELIMIT_EXCEEDED) {
928 DEBUG(3,("Warning! sizelimit exceeded in ldap. Truncating.\n"));
929 rc = 0;
932 done:
933 talloc_destroy(ctx);
934 /* if/when we decide to utf8-encode attrs, take out this next line */
935 str_list_free(&search_attrs);
936 return ADS_ERROR(rc);
939 * Do a general ADS search
940 * @param ads connection to ads server
941 * @param res ** which will contain results - free res* with ads_msgfree()
942 * @param expr Search expression
943 * @param attrs Attributes to retrieve
944 * @return status of search
946 ADS_STATUS ads_search(ADS_STRUCT *ads, LDAPMessage **res,
947 const char *expr, const char **attrs)
949 return ads_do_search(ads, ads->config.bind_path, LDAP_SCOPE_SUBTREE,
950 expr, attrs, res);
954 * Do a search on a specific DistinguishedName
955 * @param ads connection to ads server
956 * @param res ** which will contain results - free res* with ads_msgfree()
957 * @param dn DistinguishName to search
958 * @param attrs Attributes to retrieve
959 * @return status of search
961 ADS_STATUS ads_search_dn(ADS_STRUCT *ads, LDAPMessage **res,
962 const char *dn, const char **attrs)
964 return ads_do_search(ads, dn, LDAP_SCOPE_BASE, "(objectclass=*)",
965 attrs, res);
969 * Free up memory from a ads_search
970 * @param ads connection to ads server
971 * @param msg Search results to free
973 void ads_msgfree(ADS_STRUCT *ads, LDAPMessage *msg)
975 if (!msg) return;
976 ldap_msgfree(msg);
980 * Free up memory from various ads requests
981 * @param ads connection to ads server
982 * @param mem Area to free
984 void ads_memfree(ADS_STRUCT *ads, void *mem)
986 SAFE_FREE(mem);
990 * Get a dn from search results
991 * @param ads connection to ads server
992 * @param msg Search result
993 * @return dn string
995 char *ads_get_dn(ADS_STRUCT *ads, LDAPMessage *msg)
997 char *utf8_dn, *unix_dn;
999 utf8_dn = ldap_get_dn(ads->ld, msg);
1001 if (!utf8_dn) {
1002 DEBUG (5, ("ads_get_dn: ldap_get_dn failed\n"));
1003 return NULL;
1006 if (pull_utf8_allocate(&unix_dn, utf8_dn) == (size_t)-1) {
1007 DEBUG(0,("ads_get_dn: string conversion failure utf8 [%s]\n",
1008 utf8_dn ));
1009 return NULL;
1011 ldap_memfree(utf8_dn);
1012 return unix_dn;
1016 * Get the parent from a dn
1017 * @param dn the dn to return the parent from
1018 * @return parent dn string
1020 char *ads_parent_dn(const char *dn)
1022 char *p;
1024 if (dn == NULL) {
1025 return NULL;
1028 p = strchr(dn, ',');
1030 if (p == NULL) {
1031 return NULL;
1034 return p+1;
1038 * Find a machine account given a hostname
1039 * @param ads connection to ads server
1040 * @param res ** which will contain results - free res* with ads_msgfree()
1041 * @param host Hostname to search for
1042 * @return status of search
1044 ADS_STATUS ads_find_machine_acct(ADS_STRUCT *ads, LDAPMessage **res,
1045 const char *machine)
1047 ADS_STATUS status;
1048 char *expr;
1049 const char *attrs[] = {"*", "nTSecurityDescriptor", NULL};
1051 *res = NULL;
1053 /* the easiest way to find a machine account anywhere in the tree
1054 is to look for hostname$ */
1055 if (asprintf(&expr, "(samAccountName=%s$)", machine) == -1) {
1056 DEBUG(1, ("asprintf failed!\n"));
1057 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1060 status = ads_search(ads, res, expr, attrs);
1061 SAFE_FREE(expr);
1062 return status;
1066 * Initialize a list of mods to be used in a modify request
1067 * @param ctx An initialized TALLOC_CTX
1068 * @return allocated ADS_MODLIST
1070 ADS_MODLIST ads_init_mods(TALLOC_CTX *ctx)
1072 #define ADS_MODLIST_ALLOC_SIZE 10
1073 LDAPMod **mods;
1075 if ((mods = TALLOC_ZERO_ARRAY(ctx, LDAPMod *, ADS_MODLIST_ALLOC_SIZE + 1)))
1076 /* -1 is safety to make sure we don't go over the end.
1077 need to reset it to NULL before doing ldap modify */
1078 mods[ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1080 return (ADS_MODLIST)mods;
1085 add an attribute to the list, with values list already constructed
1087 static ADS_STATUS ads_modlist_add(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1088 int mod_op, const char *name,
1089 const void *_invals)
1091 const void **invals = (const void **)_invals;
1092 int curmod;
1093 LDAPMod **modlist = (LDAPMod **) *mods;
1094 struct berval **ber_values = NULL;
1095 char **char_values = NULL;
1097 if (!invals) {
1098 mod_op = LDAP_MOD_DELETE;
1099 } else {
1100 if (mod_op & LDAP_MOD_BVALUES)
1101 ber_values = ads_dup_values(ctx,
1102 (const struct berval **)invals);
1103 else
1104 char_values = ads_push_strvals(ctx,
1105 (const char **) invals);
1108 /* find the first empty slot */
1109 for (curmod=0; modlist[curmod] && modlist[curmod] != (LDAPMod *) -1;
1110 curmod++);
1111 if (modlist[curmod] == (LDAPMod *) -1) {
1112 if (!(modlist = TALLOC_REALLOC_ARRAY(ctx, modlist, LDAPMod *,
1113 curmod+ADS_MODLIST_ALLOC_SIZE+1)))
1114 return ADS_ERROR(LDAP_NO_MEMORY);
1115 memset(&modlist[curmod], 0,
1116 ADS_MODLIST_ALLOC_SIZE*sizeof(LDAPMod *));
1117 modlist[curmod+ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1118 *mods = (ADS_MODLIST)modlist;
1121 if (!(modlist[curmod] = TALLOC_ZERO_P(ctx, LDAPMod)))
1122 return ADS_ERROR(LDAP_NO_MEMORY);
1123 modlist[curmod]->mod_type = talloc_strdup(ctx, name);
1124 if (mod_op & LDAP_MOD_BVALUES) {
1125 modlist[curmod]->mod_bvalues = ber_values;
1126 } else if (mod_op & LDAP_MOD_DELETE) {
1127 modlist[curmod]->mod_values = NULL;
1128 } else {
1129 modlist[curmod]->mod_values = char_values;
1132 modlist[curmod]->mod_op = mod_op;
1133 return ADS_ERROR(LDAP_SUCCESS);
1137 * Add a single string value to a mod list
1138 * @param ctx An initialized TALLOC_CTX
1139 * @param mods An initialized ADS_MODLIST
1140 * @param name The attribute name to add
1141 * @param val The value to add - NULL means DELETE
1142 * @return ADS STATUS indicating success of add
1144 ADS_STATUS ads_mod_str(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1145 const char *name, const char *val)
1147 const char *values[2];
1149 values[0] = val;
1150 values[1] = NULL;
1152 if (!val)
1153 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1154 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE, name, values);
1158 * Add an array of string values to a mod list
1159 * @param ctx An initialized TALLOC_CTX
1160 * @param mods An initialized ADS_MODLIST
1161 * @param name The attribute name to add
1162 * @param vals The array of string values to add - NULL means DELETE
1163 * @return ADS STATUS indicating success of add
1165 ADS_STATUS ads_mod_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1166 const char *name, const char **vals)
1168 if (!vals)
1169 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1170 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE,
1171 name, (const void **) vals);
1174 #if 0
1176 * Add a single ber-encoded value to a mod list
1177 * @param ctx An initialized TALLOC_CTX
1178 * @param mods An initialized ADS_MODLIST
1179 * @param name The attribute name to add
1180 * @param val The value to add - NULL means DELETE
1181 * @return ADS STATUS indicating success of add
1183 static ADS_STATUS ads_mod_ber(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1184 const char *name, const struct berval *val)
1186 const struct berval *values[2];
1188 values[0] = val;
1189 values[1] = NULL;
1190 if (!val)
1191 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1192 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE|LDAP_MOD_BVALUES,
1193 name, (const void **) values);
1195 #endif
1198 * Perform an ldap modify
1199 * @param ads connection to ads server
1200 * @param mod_dn DistinguishedName to modify
1201 * @param mods list of modifications to perform
1202 * @return status of modify
1204 ADS_STATUS ads_gen_mod(ADS_STRUCT *ads, const char *mod_dn, ADS_MODLIST mods)
1206 int ret,i;
1207 char *utf8_dn = NULL;
1209 this control is needed to modify that contains a currently
1210 non-existent attribute (but allowable for the object) to run
1212 LDAPControl PermitModify = {
1213 CONST_DISCARD(char *, ADS_PERMIT_MODIFY_OID),
1214 {0, NULL},
1215 (char) 1};
1216 LDAPControl *controls[2];
1218 controls[0] = &PermitModify;
1219 controls[1] = NULL;
1221 if (push_utf8_allocate(&utf8_dn, mod_dn) == -1) {
1222 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1225 /* find the end of the list, marked by NULL or -1 */
1226 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1227 /* make sure the end of the list is NULL */
1228 mods[i] = NULL;
1229 ret = ldap_modify_ext_s(ads->ld, utf8_dn,
1230 (LDAPMod **) mods, controls, NULL);
1231 SAFE_FREE(utf8_dn);
1232 return ADS_ERROR(ret);
1236 * Perform an ldap add
1237 * @param ads connection to ads server
1238 * @param new_dn DistinguishedName to add
1239 * @param mods list of attributes and values for DN
1240 * @return status of add
1242 ADS_STATUS ads_gen_add(ADS_STRUCT *ads, const char *new_dn, ADS_MODLIST mods)
1244 int ret, i;
1245 char *utf8_dn = NULL;
1247 if (push_utf8_allocate(&utf8_dn, new_dn) == -1) {
1248 DEBUG(1, ("ads_gen_add: push_utf8_allocate failed!"));
1249 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1252 /* find the end of the list, marked by NULL or -1 */
1253 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1254 /* make sure the end of the list is NULL */
1255 mods[i] = NULL;
1257 ret = ldap_add_s(ads->ld, utf8_dn, (LDAPMod**)mods);
1258 SAFE_FREE(utf8_dn);
1259 return ADS_ERROR(ret);
1263 * Delete a DistinguishedName
1264 * @param ads connection to ads server
1265 * @param new_dn DistinguishedName to delete
1266 * @return status of delete
1268 ADS_STATUS ads_del_dn(ADS_STRUCT *ads, char *del_dn)
1270 int ret;
1271 char *utf8_dn = NULL;
1272 if (push_utf8_allocate(&utf8_dn, del_dn) == -1) {
1273 DEBUG(1, ("ads_del_dn: push_utf8_allocate failed!"));
1274 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1277 ret = ldap_delete_s(ads->ld, utf8_dn);
1278 SAFE_FREE(utf8_dn);
1279 return ADS_ERROR(ret);
1283 * Build an org unit string
1284 * if org unit is Computers or blank then assume a container, otherwise
1285 * assume a / separated list of organisational units.
1286 * jmcd: '\' is now used for escapes so certain chars can be in the ou (e.g. #)
1287 * @param ads connection to ads server
1288 * @param org_unit Organizational unit
1289 * @return org unit string - caller must free
1291 char *ads_ou_string(ADS_STRUCT *ads, const char *org_unit)
1293 char *ret = NULL;
1295 if (!org_unit || !*org_unit) {
1297 ret = ads_default_ou_string(ads, WELL_KNOWN_GUID_COMPUTERS);
1299 /* samba4 might not yet respond to a wellknownobject-query */
1300 return ret ? ret : SMB_STRDUP("cn=Computers");
1303 if (strequal(org_unit, "Computers")) {
1304 return SMB_STRDUP("cn=Computers");
1307 /* jmcd: removed "\\" from the separation chars, because it is
1308 needed as an escape for chars like '#' which are valid in an
1309 OU name */
1310 return ads_build_path(org_unit, "/", "ou=", 1);
1314 * Get a org unit string for a well-known GUID
1315 * @param ads connection to ads server
1316 * @param wknguid Well known GUID
1317 * @return org unit string - caller must free
1319 char *ads_default_ou_string(ADS_STRUCT *ads, const char *wknguid)
1321 ADS_STATUS status;
1322 LDAPMessage *res = NULL;
1323 char *base, *wkn_dn = NULL, *ret = NULL, **wkn_dn_exp = NULL,
1324 **bind_dn_exp = NULL;
1325 const char *attrs[] = {"distinguishedName", NULL};
1326 int new_ln, wkn_ln, bind_ln, i;
1328 if (wknguid == NULL) {
1329 return NULL;
1332 if (asprintf(&base, "<WKGUID=%s,%s>", wknguid, ads->config.bind_path ) == -1) {
1333 DEBUG(1, ("asprintf failed!\n"));
1334 return NULL;
1337 status = ads_search_dn(ads, &res, base, attrs);
1338 if (!ADS_ERR_OK(status)) {
1339 DEBUG(1,("Failed while searching for: %s\n", base));
1340 goto out;
1343 if (ads_count_replies(ads, res) != 1) {
1344 goto out;
1347 /* substitute the bind-path from the well-known-guid-search result */
1348 wkn_dn = ads_get_dn(ads, res);
1349 if (!wkn_dn) {
1350 goto out;
1353 wkn_dn_exp = ldap_explode_dn(wkn_dn, 0);
1354 if (!wkn_dn_exp) {
1355 goto out;
1358 bind_dn_exp = ldap_explode_dn(ads->config.bind_path, 0);
1359 if (!bind_dn_exp) {
1360 goto out;
1363 for (wkn_ln=0; wkn_dn_exp[wkn_ln]; wkn_ln++)
1365 for (bind_ln=0; bind_dn_exp[bind_ln]; bind_ln++)
1368 new_ln = wkn_ln - bind_ln;
1370 ret = SMB_STRDUP(wkn_dn_exp[0]);
1371 if (!ret) {
1372 goto out;
1375 for (i=1; i < new_ln; i++) {
1376 char *s = NULL;
1378 if (asprintf(&s, "%s,%s", ret, wkn_dn_exp[i]) == -1) {
1379 SAFE_FREE(ret);
1380 goto out;
1383 SAFE_FREE(ret);
1384 ret = SMB_STRDUP(s);
1385 free(s);
1386 if (!ret) {
1387 goto out;
1391 out:
1392 SAFE_FREE(base);
1393 ads_msgfree(ads, res);
1394 ads_memfree(ads, wkn_dn);
1395 if (wkn_dn_exp) {
1396 ldap_value_free(wkn_dn_exp);
1398 if (bind_dn_exp) {
1399 ldap_value_free(bind_dn_exp);
1402 return ret;
1406 * Adds (appends) an item to an attribute array, rather then
1407 * replacing the whole list
1408 * @param ctx An initialized TALLOC_CTX
1409 * @param mods An initialized ADS_MODLIST
1410 * @param name name of the ldap attribute to append to
1411 * @param vals an array of values to add
1412 * @return status of addition
1415 ADS_STATUS ads_add_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1416 const char *name, const char **vals)
1418 return ads_modlist_add(ctx, mods, LDAP_MOD_ADD, name,
1419 (const void *) vals);
1423 * Determines the computer account's current KVNO via an LDAP lookup
1424 * @param ads An initialized ADS_STRUCT
1425 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1426 * @return the kvno for the computer account, or -1 in case of a failure.
1429 uint32 ads_get_kvno(ADS_STRUCT *ads, const char *machine_name)
1431 LDAPMessage *res = NULL;
1432 uint32 kvno = (uint32)-1; /* -1 indicates a failure */
1433 char *filter;
1434 const char *attrs[] = {"msDS-KeyVersionNumber", NULL};
1435 char *dn_string = NULL;
1436 ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS);
1438 DEBUG(5,("ads_get_kvno: Searching for host %s\n", machine_name));
1439 if (asprintf(&filter, "(samAccountName=%s$)", machine_name) == -1) {
1440 return kvno;
1442 ret = ads_search(ads, &res, filter, attrs);
1443 SAFE_FREE(filter);
1444 if (!ADS_ERR_OK(ret) && ads_count_replies(ads, res)) {
1445 DEBUG(1,("ads_get_kvno: Computer Account For %s not found.\n", machine_name));
1446 ads_msgfree(ads, res);
1447 return kvno;
1450 dn_string = ads_get_dn(ads, res);
1451 if (!dn_string) {
1452 DEBUG(0,("ads_get_kvno: out of memory.\n"));
1453 ads_msgfree(ads, res);
1454 return kvno;
1456 DEBUG(5,("ads_get_kvno: Using: %s\n", dn_string));
1457 ads_memfree(ads, dn_string);
1459 /* ---------------------------------------------------------
1460 * 0 is returned as a default KVNO from this point on...
1461 * This is done because Windows 2000 does not support key
1462 * version numbers. Chances are that a failure in the next
1463 * step is simply due to Windows 2000 being used for a
1464 * domain controller. */
1465 kvno = 0;
1467 if (!ads_pull_uint32(ads, res, "msDS-KeyVersionNumber", &kvno)) {
1468 DEBUG(3,("ads_get_kvno: Error Determining KVNO!\n"));
1469 DEBUG(3,("ads_get_kvno: Windows 2000 does not support KVNO's, so this may be normal.\n"));
1470 ads_msgfree(ads, res);
1471 return kvno;
1474 /* Success */
1475 DEBUG(5,("ads_get_kvno: Looked Up KVNO of: %d\n", kvno));
1476 ads_msgfree(ads, res);
1477 return kvno;
1481 * This clears out all registered spn's for a given hostname
1482 * @param ads An initilaized ADS_STRUCT
1483 * @param machine_name the NetBIOS name of the computer.
1484 * @return 0 upon success, non-zero otherwise.
1487 ADS_STATUS ads_clear_service_principal_names(ADS_STRUCT *ads, const char *machine_name)
1489 TALLOC_CTX *ctx;
1490 LDAPMessage *res = NULL;
1491 ADS_MODLIST mods;
1492 const char *servicePrincipalName[1] = {NULL};
1493 ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS);
1494 char *dn_string = NULL;
1496 ret = ads_find_machine_acct(ads, &res, machine_name);
1497 if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
1498 DEBUG(5,("ads_clear_service_principal_names: WARNING: Host Account for %s not found... skipping operation.\n", machine_name));
1499 DEBUG(5,("ads_clear_service_principal_names: WARNING: Service Principals for %s have NOT been cleared.\n", machine_name));
1500 ads_msgfree(ads, res);
1501 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1504 DEBUG(5,("ads_clear_service_principal_names: Host account for %s found\n", machine_name));
1505 ctx = talloc_init("ads_clear_service_principal_names");
1506 if (!ctx) {
1507 ads_msgfree(ads, res);
1508 return ADS_ERROR(LDAP_NO_MEMORY);
1511 if (!(mods = ads_init_mods(ctx))) {
1512 talloc_destroy(ctx);
1513 ads_msgfree(ads, res);
1514 return ADS_ERROR(LDAP_NO_MEMORY);
1516 ret = ads_mod_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1517 if (!ADS_ERR_OK(ret)) {
1518 DEBUG(1,("ads_clear_service_principal_names: Error creating strlist.\n"));
1519 ads_msgfree(ads, res);
1520 talloc_destroy(ctx);
1521 return ret;
1523 dn_string = ads_get_dn(ads, res);
1524 if (!dn_string) {
1525 talloc_destroy(ctx);
1526 ads_msgfree(ads, res);
1527 return ADS_ERROR(LDAP_NO_MEMORY);
1529 ret = ads_gen_mod(ads, dn_string, mods);
1530 ads_memfree(ads,dn_string);
1531 if (!ADS_ERR_OK(ret)) {
1532 DEBUG(1,("ads_clear_service_principal_names: Error: Updating Service Principals for machine %s in LDAP\n",
1533 machine_name));
1534 ads_msgfree(ads, res);
1535 talloc_destroy(ctx);
1536 return ret;
1539 ads_msgfree(ads, res);
1540 talloc_destroy(ctx);
1541 return ret;
1545 * This adds a service principal name to an existing computer account
1546 * (found by hostname) in AD.
1547 * @param ads An initialized ADS_STRUCT
1548 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1549 * @param my_fqdn The fully qualified DNS name of the machine
1550 * @param spn A string of the service principal to add, i.e. 'host'
1551 * @return 0 upon sucess, or non-zero if a failure occurs
1554 ADS_STATUS ads_add_service_principal_name(ADS_STRUCT *ads, const char *machine_name,
1555 const char *my_fqdn, const char *spn)
1557 ADS_STATUS ret;
1558 TALLOC_CTX *ctx;
1559 LDAPMessage *res = NULL;
1560 char *psp1, *psp2;
1561 ADS_MODLIST mods;
1562 char *dn_string = NULL;
1563 const char *servicePrincipalName[3] = {NULL, NULL, NULL};
1565 ret = ads_find_machine_acct(ads, &res, machine_name);
1566 if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
1567 DEBUG(1,("ads_add_service_principal_name: WARNING: Host Account for %s not found... skipping operation.\n",
1568 machine_name));
1569 DEBUG(1,("ads_add_service_principal_name: WARNING: Service Principal '%s/%s@%s' has NOT been added.\n",
1570 spn, machine_name, ads->config.realm));
1571 ads_msgfree(ads, res);
1572 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1575 DEBUG(1,("ads_add_service_principal_name: Host account for %s found\n", machine_name));
1576 if (!(ctx = talloc_init("ads_add_service_principal_name"))) {
1577 ads_msgfree(ads, res);
1578 return ADS_ERROR(LDAP_NO_MEMORY);
1581 /* add short name spn */
1583 if ( (psp1 = talloc_asprintf(ctx, "%s/%s", spn, machine_name)) == NULL ) {
1584 talloc_destroy(ctx);
1585 ads_msgfree(ads, res);
1586 return ADS_ERROR(LDAP_NO_MEMORY);
1588 strupper_m(psp1);
1589 strlower_m(&psp1[strlen(spn)]);
1590 servicePrincipalName[0] = psp1;
1592 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n",
1593 psp1, machine_name));
1596 /* add fully qualified spn */
1598 if ( (psp2 = talloc_asprintf(ctx, "%s/%s", spn, my_fqdn)) == NULL ) {
1599 ret = ADS_ERROR(LDAP_NO_MEMORY);
1600 goto out;
1602 strupper_m(psp2);
1603 strlower_m(&psp2[strlen(spn)]);
1604 servicePrincipalName[1] = psp2;
1606 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n",
1607 psp2, machine_name));
1609 if ( (mods = ads_init_mods(ctx)) == NULL ) {
1610 ret = ADS_ERROR(LDAP_NO_MEMORY);
1611 goto out;
1614 ret = ads_add_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1615 if (!ADS_ERR_OK(ret)) {
1616 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
1617 goto out;
1620 if ( (dn_string = ads_get_dn(ads, res)) == NULL ) {
1621 ret = ADS_ERROR(LDAP_NO_MEMORY);
1622 goto out;
1625 ret = ads_gen_mod(ads, dn_string, mods);
1626 ads_memfree(ads,dn_string);
1627 if (!ADS_ERR_OK(ret)) {
1628 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
1629 goto out;
1632 out:
1633 TALLOC_FREE( ctx );
1634 ads_msgfree(ads, res);
1635 return ret;
1639 * adds a machine account to the ADS server
1640 * @param ads An intialized ADS_STRUCT
1641 * @param machine_name - the NetBIOS machine name of this account.
1642 * @param account_type A number indicating the type of account to create
1643 * @param org_unit The LDAP path in which to place this account
1644 * @return 0 upon success, or non-zero otherwise
1647 ADS_STATUS ads_create_machine_acct(ADS_STRUCT *ads, const char *machine_name,
1648 const char *org_unit)
1650 ADS_STATUS ret;
1651 char *samAccountName, *controlstr;
1652 TALLOC_CTX *ctx;
1653 ADS_MODLIST mods;
1654 char *machine_escaped = NULL;
1655 char *new_dn;
1656 const char *objectClass[] = {"top", "person", "organizationalPerson",
1657 "user", "computer", NULL};
1658 LDAPMessage *res = NULL;
1659 uint32 acct_control = ( UF_WORKSTATION_TRUST_ACCOUNT |\
1660 UF_DONT_EXPIRE_PASSWD |\
1661 UF_ACCOUNTDISABLE );
1663 if (!(ctx = talloc_init("ads_add_machine_acct")))
1664 return ADS_ERROR(LDAP_NO_MEMORY);
1666 ret = ADS_ERROR(LDAP_NO_MEMORY);
1668 machine_escaped = escape_rdn_val_string_alloc(machine_name);
1669 if (!machine_escaped) {
1670 goto done;
1673 new_dn = talloc_asprintf(ctx, "cn=%s,%s", machine_escaped, org_unit);
1674 samAccountName = talloc_asprintf(ctx, "%s$", machine_name);
1676 if ( !new_dn || !samAccountName ) {
1677 goto done;
1680 #ifndef ENCTYPE_ARCFOUR_HMAC
1681 acct_control |= UF_USE_DES_KEY_ONLY;
1682 #endif
1684 if (!(controlstr = talloc_asprintf(ctx, "%u", acct_control))) {
1685 goto done;
1688 if (!(mods = ads_init_mods(ctx))) {
1689 goto done;
1692 ads_mod_str(ctx, &mods, "cn", machine_name);
1693 ads_mod_str(ctx, &mods, "sAMAccountName", samAccountName);
1694 ads_mod_strlist(ctx, &mods, "objectClass", objectClass);
1695 ads_mod_str(ctx, &mods, "userAccountControl", controlstr);
1697 ret = ads_gen_add(ads, new_dn, mods);
1699 done:
1700 SAFE_FREE(machine_escaped);
1701 ads_msgfree(ads, res);
1702 talloc_destroy(ctx);
1704 return ret;
1708 * move a machine account to another OU on the ADS server
1709 * @param ads - An intialized ADS_STRUCT
1710 * @param machine_name - the NetBIOS machine name of this account.
1711 * @param org_unit - The LDAP path in which to place this account
1712 * @param moved - whether we moved the machine account (optional)
1713 * @return 0 upon success, or non-zero otherwise
1716 ADS_STATUS ads_move_machine_acct(ADS_STRUCT *ads, const char *machine_name,
1717 const char *org_unit, BOOL *moved)
1719 ADS_STATUS rc;
1720 int ldap_status;
1721 LDAPMessage *res = NULL;
1722 char *filter = NULL;
1723 char *computer_dn = NULL;
1724 char *parent_dn;
1725 char *computer_rdn = NULL;
1726 BOOL need_move = False;
1728 if (asprintf(&filter, "(samAccountName=%s$)", machine_name) == -1) {
1729 rc = ADS_ERROR(LDAP_NO_MEMORY);
1730 goto done;
1733 /* Find pre-existing machine */
1734 rc = ads_search(ads, &res, filter, NULL);
1735 if (!ADS_ERR_OK(rc)) {
1736 goto done;
1739 computer_dn = ads_get_dn(ads, res);
1740 if (!computer_dn) {
1741 rc = ADS_ERROR(LDAP_NO_MEMORY);
1742 goto done;
1745 parent_dn = ads_parent_dn(computer_dn);
1746 if (strequal(parent_dn, org_unit)) {
1747 goto done;
1750 need_move = True;
1752 if (asprintf(&computer_rdn, "CN=%s", machine_name) == -1) {
1753 rc = ADS_ERROR(LDAP_NO_MEMORY);
1754 goto done;
1757 ldap_status = ldap_rename_s(ads->ld, computer_dn, computer_rdn,
1758 org_unit, 1, NULL, NULL);
1759 rc = ADS_ERROR(ldap_status);
1761 done:
1762 ads_msgfree(ads, res);
1763 SAFE_FREE(filter);
1764 SAFE_FREE(computer_dn);
1765 SAFE_FREE(computer_rdn);
1767 if (!ADS_ERR_OK(rc)) {
1768 need_move = False;
1771 if (moved) {
1772 *moved = need_move;
1775 return rc;
1779 dump a binary result from ldap
1781 static void dump_binary(const char *field, struct berval **values)
1783 int i, j;
1784 for (i=0; values[i]; i++) {
1785 printf("%s: ", field);
1786 for (j=0; j<values[i]->bv_len; j++) {
1787 printf("%02X", (unsigned char)values[i]->bv_val[j]);
1789 printf("\n");
1793 static void dump_guid(const char *field, struct berval **values)
1795 int i;
1796 UUID_FLAT guid;
1797 for (i=0; values[i]; i++) {
1798 memcpy(guid.info, values[i]->bv_val, sizeof(guid.info));
1799 printf("%s: %s\n", field,
1800 smb_uuid_string_static(smb_uuid_unpack_static(guid)));
1805 dump a sid result from ldap
1807 static void dump_sid(const char *field, struct berval **values)
1809 int i;
1810 for (i=0; values[i]; i++) {
1811 DOM_SID sid;
1812 sid_parse(values[i]->bv_val, values[i]->bv_len, &sid);
1813 printf("%s: %s\n", field, sid_string_static(&sid));
1818 dump ntSecurityDescriptor
1820 static void dump_sd(const char *filed, struct berval **values)
1822 prs_struct ps;
1824 SEC_DESC *psd = 0;
1825 TALLOC_CTX *ctx = 0;
1827 if (!(ctx = talloc_init("sec_io_desc")))
1828 return;
1830 /* prepare data */
1831 prs_init(&ps, values[0]->bv_len, ctx, UNMARSHALL);
1832 prs_copy_data_in(&ps, values[0]->bv_val, values[0]->bv_len);
1833 prs_set_offset(&ps,0);
1835 /* parse secdesc */
1836 if (!sec_io_desc("sd", &psd, &ps, 1)) {
1837 prs_mem_free(&ps);
1838 talloc_destroy(ctx);
1839 return;
1841 if (psd) ads_disp_sd(psd);
1843 prs_mem_free(&ps);
1844 talloc_destroy(ctx);
1848 dump a string result from ldap
1850 static void dump_string(const char *field, char **values)
1852 int i;
1853 for (i=0; values[i]; i++) {
1854 printf("%s: %s\n", field, values[i]);
1859 dump a field from LDAP on stdout
1860 used for debugging
1863 static BOOL ads_dump_field(char *field, void **values, void *data_area)
1865 const struct {
1866 const char *name;
1867 BOOL string;
1868 void (*handler)(const char *, struct berval **);
1869 } handlers[] = {
1870 {"objectGUID", False, dump_guid},
1871 {"netbootGUID", False, dump_guid},
1872 {"nTSecurityDescriptor", False, dump_sd},
1873 {"dnsRecord", False, dump_binary},
1874 {"objectSid", False, dump_sid},
1875 {"tokenGroups", False, dump_sid},
1876 {"tokenGroupsNoGCAcceptable", False, dump_sid},
1877 {"tokengroupsGlobalandUniversal", False, dump_sid},
1878 {"mS-DS-CreatorSID", False, dump_sid},
1879 {NULL, True, NULL}
1881 int i;
1883 if (!field) { /* must be end of an entry */
1884 printf("\n");
1885 return False;
1888 for (i=0; handlers[i].name; i++) {
1889 if (StrCaseCmp(handlers[i].name, field) == 0) {
1890 if (!values) /* first time, indicate string or not */
1891 return handlers[i].string;
1892 handlers[i].handler(field, (struct berval **) values);
1893 break;
1896 if (!handlers[i].name) {
1897 if (!values) /* first time, indicate string conversion */
1898 return True;
1899 dump_string(field, (char **)values);
1901 return False;
1905 * Dump a result from LDAP on stdout
1906 * used for debugging
1907 * @param ads connection to ads server
1908 * @param res Results to dump
1911 void ads_dump(ADS_STRUCT *ads, LDAPMessage *res)
1913 ads_process_results(ads, res, ads_dump_field, NULL);
1917 * Walk through results, calling a function for each entry found.
1918 * The function receives a field name, a berval * array of values,
1919 * and a data area passed through from the start. The function is
1920 * called once with null for field and values at the end of each
1921 * entry.
1922 * @param ads connection to ads server
1923 * @param res Results to process
1924 * @param fn Function for processing each result
1925 * @param data_area user-defined area to pass to function
1927 void ads_process_results(ADS_STRUCT *ads, LDAPMessage *res,
1928 BOOL(*fn)(char *, void **, void *),
1929 void *data_area)
1931 LDAPMessage *msg;
1932 TALLOC_CTX *ctx;
1934 if (!(ctx = talloc_init("ads_process_results")))
1935 return;
1937 for (msg = ads_first_entry(ads, res); msg;
1938 msg = ads_next_entry(ads, msg)) {
1939 char *utf8_field;
1940 BerElement *b;
1942 for (utf8_field=ldap_first_attribute(ads->ld,
1943 (LDAPMessage *)msg,&b);
1944 utf8_field;
1945 utf8_field=ldap_next_attribute(ads->ld,
1946 (LDAPMessage *)msg,b)) {
1947 struct berval **ber_vals;
1948 char **str_vals, **utf8_vals;
1949 char *field;
1950 BOOL string;
1952 pull_utf8_talloc(ctx, &field, utf8_field);
1953 string = fn(field, NULL, data_area);
1955 if (string) {
1956 utf8_vals = ldap_get_values(ads->ld,
1957 (LDAPMessage *)msg, field);
1958 str_vals = ads_pull_strvals(ctx,
1959 (const char **) utf8_vals);
1960 fn(field, (void **) str_vals, data_area);
1961 ldap_value_free(utf8_vals);
1962 } else {
1963 ber_vals = ldap_get_values_len(ads->ld,
1964 (LDAPMessage *)msg, field);
1965 fn(field, (void **) ber_vals, data_area);
1967 ldap_value_free_len(ber_vals);
1969 ldap_memfree(utf8_field);
1971 ber_free(b, 0);
1972 talloc_free_children(ctx);
1973 fn(NULL, NULL, data_area); /* completed an entry */
1976 talloc_destroy(ctx);
1980 * count how many replies are in a LDAPMessage
1981 * @param ads connection to ads server
1982 * @param res Results to count
1983 * @return number of replies
1985 int ads_count_replies(ADS_STRUCT *ads, void *res)
1987 return ldap_count_entries(ads->ld, (LDAPMessage *)res);
1991 * pull the first entry from a ADS result
1992 * @param ads connection to ads server
1993 * @param res Results of search
1994 * @return first entry from result
1996 LDAPMessage *ads_first_entry(ADS_STRUCT *ads, LDAPMessage *res)
1998 return ldap_first_entry(ads->ld, res);
2002 * pull the next entry from a ADS result
2003 * @param ads connection to ads server
2004 * @param res Results of search
2005 * @return next entry from result
2007 LDAPMessage *ads_next_entry(ADS_STRUCT *ads, LDAPMessage *res)
2009 return ldap_next_entry(ads->ld, res);
2013 * pull a single string from a ADS result
2014 * @param ads connection to ads server
2015 * @param mem_ctx TALLOC_CTX to use for allocating result string
2016 * @param msg Results of search
2017 * @param field Attribute to retrieve
2018 * @return Result string in talloc context
2020 char *ads_pull_string(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, LDAPMessage *msg,
2021 const char *field)
2023 char **values;
2024 char *ret = NULL;
2025 char *ux_string;
2026 size_t rc;
2028 values = ldap_get_values(ads->ld, msg, field);
2029 if (!values)
2030 return NULL;
2032 if (values[0]) {
2033 rc = pull_utf8_talloc(mem_ctx, &ux_string,
2034 values[0]);
2035 if (rc != (size_t)-1)
2036 ret = ux_string;
2039 ldap_value_free(values);
2040 return ret;
2044 * pull an array of strings from a ADS result
2045 * @param ads connection to ads server
2046 * @param mem_ctx TALLOC_CTX to use for allocating result string
2047 * @param msg Results of search
2048 * @param field Attribute to retrieve
2049 * @return Result strings in talloc context
2051 char **ads_pull_strings(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2052 LDAPMessage *msg, const char *field,
2053 size_t *num_values)
2055 char **values;
2056 char **ret = NULL;
2057 int i;
2059 values = ldap_get_values(ads->ld, msg, field);
2060 if (!values)
2061 return NULL;
2063 *num_values = ldap_count_values(values);
2065 ret = TALLOC_ARRAY(mem_ctx, char *, *num_values + 1);
2066 if (!ret) {
2067 ldap_value_free(values);
2068 return NULL;
2071 for (i=0;i<*num_values;i++) {
2072 if (pull_utf8_talloc(mem_ctx, &ret[i], values[i]) == -1) {
2073 ldap_value_free(values);
2074 return NULL;
2077 ret[i] = NULL;
2079 ldap_value_free(values);
2080 return ret;
2084 * pull an array of strings from a ADS result
2085 * (handle large multivalue attributes with range retrieval)
2086 * @param ads connection to ads server
2087 * @param mem_ctx TALLOC_CTX to use for allocating result string
2088 * @param msg Results of search
2089 * @param field Attribute to retrieve
2090 * @param current_strings strings returned by a previous call to this function
2091 * @param next_attribute The next query should ask for this attribute
2092 * @param num_values How many values did we get this time?
2093 * @param more_values Are there more values to get?
2094 * @return Result strings in talloc context
2096 char **ads_pull_strings_range(ADS_STRUCT *ads,
2097 TALLOC_CTX *mem_ctx,
2098 LDAPMessage *msg, const char *field,
2099 char **current_strings,
2100 const char **next_attribute,
2101 size_t *num_strings,
2102 BOOL *more_strings)
2104 char *attr;
2105 char *expected_range_attrib, *range_attr;
2106 BerElement *ptr = NULL;
2107 char **strings;
2108 char **new_strings;
2109 size_t num_new_strings;
2110 unsigned long int range_start;
2111 unsigned long int range_end;
2113 /* we might have been given the whole lot anyway */
2114 if ((strings = ads_pull_strings(ads, mem_ctx, msg, field, num_strings))) {
2115 *more_strings = False;
2116 return strings;
2119 expected_range_attrib = talloc_asprintf(mem_ctx, "%s;Range=", field);
2121 /* look for Range result */
2122 for (attr = ldap_first_attribute(ads->ld, (LDAPMessage *)msg, &ptr);
2123 attr;
2124 attr = ldap_next_attribute(ads->ld, (LDAPMessage *)msg, ptr)) {
2125 /* we ignore the fact that this is utf8, as all attributes are ascii... */
2126 if (strnequal(attr, expected_range_attrib, strlen(expected_range_attrib))) {
2127 range_attr = attr;
2128 break;
2130 ldap_memfree(attr);
2132 if (!attr) {
2133 ber_free(ptr, 0);
2134 /* nothing here - this field is just empty */
2135 *more_strings = False;
2136 return NULL;
2139 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-%lu",
2140 &range_start, &range_end) == 2) {
2141 *more_strings = True;
2142 } else {
2143 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-*",
2144 &range_start) == 1) {
2145 *more_strings = False;
2146 } else {
2147 DEBUG(1, ("ads_pull_strings_range: Cannot parse Range attriubte (%s)\n",
2148 range_attr));
2149 ldap_memfree(range_attr);
2150 *more_strings = False;
2151 return NULL;
2155 if ((*num_strings) != range_start) {
2156 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) doesn't start at %u, but at %lu"
2157 " - aborting range retreival\n",
2158 range_attr, (unsigned int)(*num_strings) + 1, range_start));
2159 ldap_memfree(range_attr);
2160 *more_strings = False;
2161 return NULL;
2164 new_strings = ads_pull_strings(ads, mem_ctx, msg, range_attr, &num_new_strings);
2166 if (*more_strings && ((*num_strings + num_new_strings) != (range_end + 1))) {
2167 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) tells us we have %lu "
2168 "strings in this bunch, but we only got %lu - aborting range retreival\n",
2169 range_attr, (unsigned long int)range_end - range_start + 1,
2170 (unsigned long int)num_new_strings));
2171 ldap_memfree(range_attr);
2172 *more_strings = False;
2173 return NULL;
2176 strings = TALLOC_REALLOC_ARRAY(mem_ctx, current_strings, char *,
2177 *num_strings + num_new_strings);
2179 if (strings == NULL) {
2180 ldap_memfree(range_attr);
2181 *more_strings = False;
2182 return NULL;
2185 if (new_strings && num_new_strings) {
2186 memcpy(&strings[*num_strings], new_strings,
2187 sizeof(*new_strings) * num_new_strings);
2190 (*num_strings) += num_new_strings;
2192 if (*more_strings) {
2193 *next_attribute = talloc_asprintf(mem_ctx,
2194 "%s;range=%d-*",
2195 field,
2196 (int)*num_strings);
2198 if (!*next_attribute) {
2199 DEBUG(1, ("talloc_asprintf for next attribute failed!\n"));
2200 ldap_memfree(range_attr);
2201 *more_strings = False;
2202 return NULL;
2206 ldap_memfree(range_attr);
2208 return strings;
2212 * pull a single uint32 from a ADS result
2213 * @param ads connection to ads server
2214 * @param msg Results of search
2215 * @param field Attribute to retrieve
2216 * @param v Pointer to int to store result
2217 * @return boolean inidicating success
2219 BOOL ads_pull_uint32(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
2220 uint32 *v)
2222 char **values;
2224 values = ldap_get_values(ads->ld, msg, field);
2225 if (!values)
2226 return False;
2227 if (!values[0]) {
2228 ldap_value_free(values);
2229 return False;
2232 *v = atoi(values[0]);
2233 ldap_value_free(values);
2234 return True;
2238 * pull a single objectGUID from an ADS result
2239 * @param ads connection to ADS server
2240 * @param msg results of search
2241 * @param guid 37-byte area to receive text guid
2242 * @return boolean indicating success
2244 BOOL ads_pull_guid(ADS_STRUCT *ads, LDAPMessage *msg, struct GUID *guid)
2246 char **values;
2247 UUID_FLAT flat_guid;
2249 values = ldap_get_values(ads->ld, msg, "objectGUID");
2250 if (!values)
2251 return False;
2253 if (values[0]) {
2254 memcpy(&flat_guid.info, values[0], sizeof(UUID_FLAT));
2255 smb_uuid_unpack(flat_guid, guid);
2256 ldap_value_free(values);
2257 return True;
2259 ldap_value_free(values);
2260 return False;
2266 * pull a single DOM_SID from a ADS result
2267 * @param ads connection to ads server
2268 * @param msg Results of search
2269 * @param field Attribute to retrieve
2270 * @param sid Pointer to sid to store result
2271 * @return boolean inidicating success
2273 BOOL ads_pull_sid(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
2274 DOM_SID *sid)
2276 struct berval **values;
2277 BOOL ret = False;
2279 values = ldap_get_values_len(ads->ld, msg, field);
2281 if (!values)
2282 return False;
2284 if (values[0])
2285 ret = sid_parse(values[0]->bv_val, values[0]->bv_len, sid);
2287 ldap_value_free_len(values);
2288 return ret;
2292 * pull an array of DOM_SIDs from a ADS result
2293 * @param ads connection to ads server
2294 * @param mem_ctx TALLOC_CTX for allocating sid array
2295 * @param msg Results of search
2296 * @param field Attribute to retrieve
2297 * @param sids pointer to sid array to allocate
2298 * @return the count of SIDs pulled
2300 int ads_pull_sids(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2301 LDAPMessage *msg, const char *field, DOM_SID **sids)
2303 struct berval **values;
2304 BOOL ret;
2305 int count, i;
2307 values = ldap_get_values_len(ads->ld, msg, field);
2309 if (!values)
2310 return 0;
2312 for (i=0; values[i]; i++)
2313 /* nop */ ;
2315 if (i) {
2316 (*sids) = TALLOC_ARRAY(mem_ctx, DOM_SID, i);
2317 if (!(*sids)) {
2318 ldap_value_free_len(values);
2319 return 0;
2321 } else {
2322 (*sids) = NULL;
2325 count = 0;
2326 for (i=0; values[i]; i++) {
2327 ret = sid_parse(values[i]->bv_val, values[i]->bv_len, &(*sids)[count]);
2328 if (ret) {
2329 fstring sid;
2330 DEBUG(10, ("pulling SID: %s\n", sid_to_string(sid, &(*sids)[count])));
2331 count++;
2335 ldap_value_free_len(values);
2336 return count;
2340 * pull a SEC_DESC from a ADS result
2341 * @param ads connection to ads server
2342 * @param mem_ctx TALLOC_CTX for allocating sid array
2343 * @param msg Results of search
2344 * @param field Attribute to retrieve
2345 * @param sd Pointer to *SEC_DESC to store result (talloc()ed)
2346 * @return boolean inidicating success
2348 BOOL ads_pull_sd(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2349 LDAPMessage *msg, const char *field, SEC_DESC **sd)
2351 struct berval **values;
2352 BOOL ret = False;
2354 values = ldap_get_values_len(ads->ld, msg, field);
2356 if (!values) return False;
2358 if (values[0]) {
2359 prs_struct ps;
2360 prs_init(&ps, values[0]->bv_len, mem_ctx, UNMARSHALL);
2361 prs_copy_data_in(&ps, values[0]->bv_val, values[0]->bv_len);
2362 prs_set_offset(&ps,0);
2364 ret = sec_io_desc("sd", sd, &ps, 1);
2365 prs_mem_free(&ps);
2368 ldap_value_free_len(values);
2369 return ret;
2373 * in order to support usernames longer than 21 characters we need to
2374 * use both the sAMAccountName and the userPrincipalName attributes
2375 * It seems that not all users have the userPrincipalName attribute set
2377 * @param ads connection to ads server
2378 * @param mem_ctx TALLOC_CTX for allocating sid array
2379 * @param msg Results of search
2380 * @return the username
2382 char *ads_pull_username(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2383 LDAPMessage *msg)
2385 #if 0 /* JERRY */
2386 char *ret, *p;
2388 /* lookup_name() only works on the sAMAccountName to
2389 returning the username portion of userPrincipalName
2390 breaks winbindd_getpwnam() */
2392 ret = ads_pull_string(ads, mem_ctx, msg, "userPrincipalName");
2393 if (ret && (p = strchr_m(ret, '@'))) {
2394 *p = 0;
2395 return ret;
2397 #endif
2398 return ads_pull_string(ads, mem_ctx, msg, "sAMAccountName");
2403 * find the update serial number - this is the core of the ldap cache
2404 * @param ads connection to ads server
2405 * @param ads connection to ADS server
2406 * @param usn Pointer to retrieved update serial number
2407 * @return status of search
2409 ADS_STATUS ads_USN(ADS_STRUCT *ads, uint32 *usn)
2411 const char *attrs[] = {"highestCommittedUSN", NULL};
2412 ADS_STATUS status;
2413 LDAPMessage *res;
2415 status = ads_do_search_retry(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2416 if (!ADS_ERR_OK(status))
2417 return status;
2419 if (ads_count_replies(ads, res) != 1) {
2420 ads_msgfree(ads, res);
2421 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2424 if (!ads_pull_uint32(ads, res, "highestCommittedUSN", usn)) {
2425 ads_msgfree(ads, res);
2426 return ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
2429 ads_msgfree(ads, res);
2430 return ADS_SUCCESS;
2433 /* parse a ADS timestring - typical string is
2434 '20020917091222.0Z0' which means 09:12.22 17th September
2435 2002, timezone 0 */
2436 static time_t ads_parse_time(const char *str)
2438 struct tm tm;
2440 ZERO_STRUCT(tm);
2442 if (sscanf(str, "%4d%2d%2d%2d%2d%2d",
2443 &tm.tm_year, &tm.tm_mon, &tm.tm_mday,
2444 &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) {
2445 return 0;
2447 tm.tm_year -= 1900;
2448 tm.tm_mon -= 1;
2450 return timegm(&tm);
2453 /********************************************************************
2454 ********************************************************************/
2456 ADS_STATUS ads_current_time(ADS_STRUCT *ads)
2458 const char *attrs[] = {"currentTime", NULL};
2459 ADS_STATUS status;
2460 LDAPMessage *res;
2461 char *timestr;
2462 TALLOC_CTX *ctx;
2463 ADS_STRUCT *ads_s = ads;
2465 if (!(ctx = talloc_init("ads_current_time"))) {
2466 return ADS_ERROR(LDAP_NO_MEMORY);
2469 /* establish a new ldap tcp session if necessary */
2471 if ( !ads->ld ) {
2472 if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup,
2473 ads->server.ldap_server )) == NULL )
2475 goto done;
2477 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
2478 status = ads_connect( ads_s );
2479 if ( !ADS_ERR_OK(status))
2480 goto done;
2483 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2484 if (!ADS_ERR_OK(status)) {
2485 goto done;
2488 timestr = ads_pull_string(ads_s, ctx, res, "currentTime");
2489 if (!timestr) {
2490 ads_msgfree(ads_s, res);
2491 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2492 goto done;
2495 /* but save the time and offset in the original ADS_STRUCT */
2497 ads->config.current_time = ads_parse_time(timestr);
2499 if (ads->config.current_time != 0) {
2500 ads->auth.time_offset = ads->config.current_time - time(NULL);
2501 DEBUG(4,("time offset is %d seconds\n", ads->auth.time_offset));
2504 ads_msgfree(ads, res);
2506 status = ADS_SUCCESS;
2508 done:
2509 /* free any temporary ads connections */
2510 if ( ads_s != ads ) {
2511 ads_destroy( &ads_s );
2513 talloc_destroy(ctx);
2515 return status;
2518 /********************************************************************
2519 ********************************************************************/
2521 ADS_STATUS ads_domain_func_level(ADS_STRUCT *ads, uint32 *val)
2523 const char *attrs[] = {"domainFunctionality", NULL};
2524 ADS_STATUS status;
2525 LDAPMessage *res;
2526 ADS_STRUCT *ads_s = ads;
2528 *val = DS_DOMAIN_FUNCTION_2000;
2530 /* establish a new ldap tcp session if necessary */
2532 if ( !ads->ld ) {
2533 if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup,
2534 ads->server.ldap_server )) == NULL )
2536 goto done;
2538 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
2539 status = ads_connect( ads_s );
2540 if ( !ADS_ERR_OK(status))
2541 goto done;
2544 /* If the attribute does not exist assume it is a Windows 2000
2545 functional domain */
2547 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2548 if (!ADS_ERR_OK(status)) {
2549 if ( status.err.rc == LDAP_NO_SUCH_ATTRIBUTE ) {
2550 status = ADS_SUCCESS;
2552 goto done;
2555 if ( !ads_pull_uint32(ads_s, res, "domainFunctionality", val) ) {
2556 DEBUG(5,("ads_domain_func_level: Failed to pull the domainFunctionality attribute.\n"));
2558 DEBUG(3,("ads_domain_func_level: %d\n", *val));
2561 ads_msgfree(ads, res);
2563 done:
2564 /* free any temporary ads connections */
2565 if ( ads_s != ads ) {
2566 ads_destroy( &ads_s );
2569 return status;
2573 * find the domain sid for our domain
2574 * @param ads connection to ads server
2575 * @param sid Pointer to domain sid
2576 * @return status of search
2578 ADS_STATUS ads_domain_sid(ADS_STRUCT *ads, DOM_SID *sid)
2580 const char *attrs[] = {"objectSid", NULL};
2581 LDAPMessage *res;
2582 ADS_STATUS rc;
2584 rc = ads_do_search_retry(ads, ads->config.bind_path, LDAP_SCOPE_BASE, "(objectclass=*)",
2585 attrs, &res);
2586 if (!ADS_ERR_OK(rc)) return rc;
2587 if (!ads_pull_sid(ads, res, "objectSid", sid)) {
2588 ads_msgfree(ads, res);
2589 return ADS_ERROR_SYSTEM(ENOENT);
2591 ads_msgfree(ads, res);
2593 return ADS_SUCCESS;
2597 * find our site name
2598 * @param ads connection to ads server
2599 * @param mem_ctx Pointer to talloc context
2600 * @param site_name Pointer to the sitename
2601 * @return status of search
2603 ADS_STATUS ads_site_dn(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char **site_name)
2605 ADS_STATUS status;
2606 LDAPMessage *res;
2607 const char *dn, *service_name;
2608 const char *attrs[] = { "dsServiceName", NULL };
2610 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2611 if (!ADS_ERR_OK(status)) {
2612 return status;
2615 service_name = ads_pull_string(ads, mem_ctx, res, "dsServiceName");
2616 if (service_name == NULL) {
2617 ads_msgfree(ads, res);
2618 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2621 ads_msgfree(ads, res);
2623 /* go up three levels */
2624 dn = ads_parent_dn(ads_parent_dn(ads_parent_dn(service_name)));
2625 if (dn == NULL) {
2626 return ADS_ERROR(LDAP_NO_MEMORY);
2629 *site_name = talloc_strdup(mem_ctx, dn);
2630 if (*site_name == NULL) {
2631 return ADS_ERROR(LDAP_NO_MEMORY);
2634 return status;
2636 dsServiceName: CN=NTDS Settings,CN=W2K3DC,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,DC=ber,DC=suse,DC=de
2641 * find the site dn where a machine resides
2642 * @param ads connection to ads server
2643 * @param mem_ctx Pointer to talloc context
2644 * @param computer_name name of the machine
2645 * @param site_name Pointer to the sitename
2646 * @return status of search
2648 ADS_STATUS ads_site_dn_for_machine(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char *computer_name, const char **site_dn)
2650 ADS_STATUS status;
2651 LDAPMessage *res;
2652 const char *parent, *config_context, *filter;
2653 const char *attrs[] = { "configurationNamingContext", NULL };
2654 char *dn;
2656 /* shortcut a query */
2657 if (strequal(computer_name, ads->config.ldap_server_name)) {
2658 return ads_site_dn(ads, mem_ctx, site_dn);
2661 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2662 if (!ADS_ERR_OK(status)) {
2663 return status;
2666 config_context = ads_pull_string(ads, mem_ctx, res, "configurationNamingContext");
2667 if (config_context == NULL) {
2668 ads_msgfree(ads, res);
2669 return ADS_ERROR(LDAP_NO_MEMORY);
2672 filter = talloc_asprintf(mem_ctx, "(cn=%s)", computer_name);
2673 if (filter == NULL) {
2674 ads_msgfree(ads, res);
2675 return ADS_ERROR(LDAP_NO_MEMORY);
2678 ads_msgfree(ads, res);
2680 status = ads_do_search(ads, config_context, LDAP_SCOPE_SUBTREE, filter, NULL, &res);
2681 if (!ADS_ERR_OK(status)) {
2682 return status;
2685 if (ads_count_replies(ads, res) != 1) {
2686 ads_msgfree(ads, res);
2687 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
2690 dn = ads_get_dn(ads, res);
2691 if (dn == NULL) {
2692 ads_msgfree(ads, res);
2693 return ADS_ERROR(LDAP_NO_MEMORY);
2696 /* go up three levels */
2697 parent = ads_parent_dn(ads_parent_dn(ads_parent_dn(dn)));
2698 if (parent == NULL) {
2699 ads_msgfree(ads, res);
2700 ads_memfree(ads, dn);
2701 return ADS_ERROR(LDAP_NO_MEMORY);
2704 *site_dn = talloc_strdup(mem_ctx, parent);
2705 if (*site_dn == NULL) {
2706 ads_msgfree(ads, res);
2707 ads_memfree(ads, dn);
2708 ADS_ERROR(LDAP_NO_MEMORY);
2711 ads_memfree(ads, dn);
2712 ads_msgfree(ads, res);
2714 return status;
2718 * get the upn suffixes for a domain
2719 * @param ads connection to ads server
2720 * @param mem_ctx Pointer to talloc context
2721 * @param suffixes Pointer to an array of suffixes
2722 * @param num_suffixes Pointer to the number of suffixes
2723 * @return status of search
2725 ADS_STATUS ads_upn_suffixes(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, char ***suffixes, size_t *num_suffixes)
2727 ADS_STATUS status;
2728 LDAPMessage *res;
2729 const char *config_context, *base;
2730 const char *attrs[] = { "configurationNamingContext", NULL };
2731 const char *attrs2[] = { "uPNSuffixes", NULL };
2733 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2734 if (!ADS_ERR_OK(status)) {
2735 return status;
2738 config_context = ads_pull_string(ads, mem_ctx, res, "configurationNamingContext");
2739 if (config_context == NULL) {
2740 ads_msgfree(ads, res);
2741 return ADS_ERROR(LDAP_NO_MEMORY);
2744 ads_msgfree(ads, res);
2746 base = talloc_asprintf(mem_ctx, "cn=Partitions,%s", config_context);
2747 if (base == NULL) {
2748 return ADS_ERROR(LDAP_NO_MEMORY);
2751 status = ads_search_dn(ads, &res, base, attrs2);
2752 if (!ADS_ERR_OK(status)) {
2753 return status;
2756 if (ads_count_replies(ads, res) != 1) {
2757 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
2760 (*suffixes) = ads_pull_strings(ads, mem_ctx, res, "uPNSuffixes", num_suffixes);
2761 if ((*suffixes) == NULL) {
2762 ads_msgfree(ads, res);
2763 return ADS_ERROR(LDAP_NO_MEMORY);
2766 ads_msgfree(ads, res);
2768 return status;
2772 * pull a DOM_SID from an extended dn string
2773 * @param mem_ctx TALLOC_CTX
2774 * @param extended_dn string
2775 * @param flags string type of extended_dn
2776 * @param sid pointer to a DOM_SID
2777 * @return boolean inidicating success
2779 BOOL ads_get_sid_from_extended_dn(TALLOC_CTX *mem_ctx,
2780 const char *extended_dn,
2781 enum ads_extended_dn_flags flags,
2782 DOM_SID *sid)
2784 char *p, *q, *dn;
2786 if (!extended_dn) {
2787 return False;
2790 /* otherwise extended_dn gets stripped off */
2791 if ((dn = talloc_strdup(mem_ctx, extended_dn)) == NULL) {
2792 return False;
2795 * ADS_EXTENDED_DN_HEX_STRING:
2796 * <GUID=238e1963cb390f4bb032ba0105525a29>;<SID=010500000000000515000000bb68c8fd6b61b427572eb04556040000>;CN=gd,OU=berlin,OU=suse,DC=ber,DC=suse,DC=de
2798 * ADS_EXTENDED_DN_STRING (only with w2k3):
2799 <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
2802 p = strchr(dn, ';');
2803 if (!p) {
2804 return False;
2807 if (strncmp(p, ";<SID=", strlen(";<SID=")) != 0) {
2808 return False;
2811 p += strlen(";<SID=");
2813 q = strchr(p, '>');
2814 if (!q) {
2815 return False;
2818 *q = '\0';
2820 DEBUG(100,("ads_get_sid_from_extended_dn: sid string is %s\n", p));
2822 switch (flags) {
2824 case ADS_EXTENDED_DN_STRING:
2825 if (!string_to_sid(sid, p)) {
2826 return False;
2828 break;
2829 case ADS_EXTENDED_DN_HEX_STRING: {
2830 pstring buf;
2831 size_t buf_len;
2833 buf_len = strhex_to_str(buf, strlen(p), p);
2834 if (buf_len == 0) {
2835 return False;
2838 if (!sid_parse(buf, buf_len, sid)) {
2839 DEBUG(10,("failed to parse sid\n"));
2840 return False;
2842 break;
2844 default:
2845 DEBUG(10,("unknown extended dn format\n"));
2846 return False;
2849 return True;
2853 * pull an array of DOM_SIDs from a ADS result
2854 * @param ads connection to ads server
2855 * @param mem_ctx TALLOC_CTX for allocating sid array
2856 * @param msg Results of search
2857 * @param field Attribute to retrieve
2858 * @param flags string type of extended_dn
2859 * @param sids pointer to sid array to allocate
2860 * @return the count of SIDs pulled
2862 int ads_pull_sids_from_extendeddn(ADS_STRUCT *ads,
2863 TALLOC_CTX *mem_ctx,
2864 LDAPMessage *msg,
2865 const char *field,
2866 enum ads_extended_dn_flags flags,
2867 DOM_SID **sids)
2869 int i;
2870 size_t dn_count;
2871 char **dn_strings;
2873 if ((dn_strings = ads_pull_strings(ads, mem_ctx, msg, field,
2874 &dn_count)) == NULL) {
2875 return 0;
2878 (*sids) = TALLOC_ZERO_ARRAY(mem_ctx, DOM_SID, dn_count + 1);
2879 if (!(*sids)) {
2880 TALLOC_FREE(dn_strings);
2881 return 0;
2884 for (i=0; i<dn_count; i++) {
2886 if (!ads_get_sid_from_extended_dn(mem_ctx, dn_strings[i],
2887 flags, &(*sids)[i])) {
2888 TALLOC_FREE(*sids);
2889 TALLOC_FREE(dn_strings);
2890 return 0;
2894 TALLOC_FREE(dn_strings);
2896 return dn_count;
2899 /********************************************************************
2900 ********************************************************************/
2902 char* ads_get_dnshostname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
2904 LDAPMessage *res = NULL;
2905 ADS_STATUS status;
2906 int count = 0;
2907 char *name = NULL;
2909 status = ads_find_machine_acct(ads, &res, global_myname());
2910 if (!ADS_ERR_OK(status)) {
2911 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
2912 global_myname()));
2913 goto out;
2916 if ( (count = ads_count_replies(ads, res)) != 1 ) {
2917 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
2918 goto out;
2921 if ( (name = ads_pull_string(ads, ctx, res, "dNSHostName")) == NULL ) {
2922 DEBUG(0,("ads_get_dnshostname: No dNSHostName attribute!\n"));
2925 out:
2926 ads_msgfree(ads, res);
2928 return name;
2931 /********************************************************************
2932 ********************************************************************/
2934 char* ads_get_upn( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
2936 LDAPMessage *res = NULL;
2937 ADS_STATUS status;
2938 int count = 0;
2939 char *name = NULL;
2941 status = ads_find_machine_acct(ads, &res, global_myname());
2942 if (!ADS_ERR_OK(status)) {
2943 DEBUG(0,("ads_get_upn: Failed to find account for %s\n",
2944 global_myname()));
2945 goto out;
2948 if ( (count = ads_count_replies(ads, res)) != 1 ) {
2949 DEBUG(1,("ads_get_upn: %d entries returned!\n", count));
2950 goto out;
2953 if ( (name = ads_pull_string(ads, ctx, res, "userPrincipalName")) == NULL ) {
2954 DEBUG(2,("ads_get_upn: No userPrincipalName attribute!\n"));
2957 out:
2958 ads_msgfree(ads, res);
2960 return name;
2963 /********************************************************************
2964 ********************************************************************/
2966 char* ads_get_samaccountname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
2968 LDAPMessage *res = NULL;
2969 ADS_STATUS status;
2970 int count = 0;
2971 char *name = NULL;
2973 status = ads_find_machine_acct(ads, &res, global_myname());
2974 if (!ADS_ERR_OK(status)) {
2975 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
2976 global_myname()));
2977 goto out;
2980 if ( (count = ads_count_replies(ads, res)) != 1 ) {
2981 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
2982 goto out;
2985 if ( (name = ads_pull_string(ads, ctx, res, "sAMAccountName")) == NULL ) {
2986 DEBUG(0,("ads_get_dnshostname: No sAMAccountName attribute!\n"));
2989 out:
2990 ads_msgfree(ads, res);
2992 return name;
2995 #if 0
2997 SAVED CODE - we used to join via ldap - remember how we did this. JRA.
3000 * Join a machine to a realm
3001 * Creates the machine account and sets the machine password
3002 * @param ads connection to ads server
3003 * @param machine name of host to add
3004 * @param org_unit Organizational unit to place machine in
3005 * @return status of join
3007 ADS_STATUS ads_join_realm(ADS_STRUCT *ads, const char *machine_name,
3008 uint32 account_type, const char *org_unit)
3010 ADS_STATUS status;
3011 LDAPMessage *res = NULL;
3012 char *machine;
3014 /* machine name must be lowercase */
3015 machine = SMB_STRDUP(machine_name);
3016 strlower_m(machine);
3019 status = ads_find_machine_acct(ads, (void **)&res, machine);
3020 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
3021 DEBUG(0, ("Host account for %s already exists - deleting old account\n", machine));
3022 status = ads_leave_realm(ads, machine);
3023 if (!ADS_ERR_OK(status)) {
3024 DEBUG(0, ("Failed to delete host '%s' from the '%s' realm.\n",
3025 machine, ads->config.realm));
3026 return status;
3030 status = ads_add_machine_acct(ads, machine, account_type, org_unit);
3031 if (!ADS_ERR_OK(status)) {
3032 DEBUG(0, ("ads_join_realm: ads_add_machine_acct failed (%s): %s\n", machine, ads_errstr(status)));
3033 SAFE_FREE(machine);
3034 return status;
3037 status = ads_find_machine_acct(ads, (void **)(void *)&res, machine);
3038 if (!ADS_ERR_OK(status)) {
3039 DEBUG(0, ("ads_join_realm: Host account test failed for machine %s\n", machine));
3040 SAFE_FREE(machine);
3041 return status;
3044 SAFE_FREE(machine);
3045 ads_msgfree(ads, res);
3047 return status;
3049 #endif
3052 * Delete a machine from the realm
3053 * @param ads connection to ads server
3054 * @param hostname Machine to remove
3055 * @return status of delete
3057 ADS_STATUS ads_leave_realm(ADS_STRUCT *ads, const char *hostname)
3059 ADS_STATUS status;
3060 void *msg;
3061 LDAPMessage *res;
3062 char *hostnameDN, *host;
3063 int rc;
3064 LDAPControl ldap_control;
3065 LDAPControl * pldap_control[2] = {NULL, NULL};
3067 pldap_control[0] = &ldap_control;
3068 memset(&ldap_control, 0, sizeof(LDAPControl));
3069 ldap_control.ldctl_oid = (char *)LDAP_SERVER_TREE_DELETE_OID;
3071 /* hostname must be lowercase */
3072 host = SMB_STRDUP(hostname);
3073 strlower_m(host);
3075 status = ads_find_machine_acct(ads, &res, host);
3076 if (!ADS_ERR_OK(status)) {
3077 DEBUG(0, ("Host account for %s does not exist.\n", host));
3078 SAFE_FREE(host);
3079 return status;
3082 msg = ads_first_entry(ads, res);
3083 if (!msg) {
3084 SAFE_FREE(host);
3085 return ADS_ERROR_SYSTEM(ENOENT);
3088 hostnameDN = ads_get_dn(ads, (LDAPMessage *)msg);
3090 rc = ldap_delete_ext_s(ads->ld, hostnameDN, pldap_control, NULL);
3091 if (rc) {
3092 DEBUG(3,("ldap_delete_ext_s failed with error code %d\n", rc));
3093 }else {
3094 DEBUG(3,("ldap_delete_ext_s succeeded with error code %d\n", rc));
3097 if (rc != LDAP_SUCCESS) {
3098 const char *attrs[] = { "cn", NULL };
3099 LDAPMessage *msg_sub;
3101 /* we only search with scope ONE, we do not expect any further
3102 * objects to be created deeper */
3104 status = ads_do_search_retry(ads, hostnameDN,
3105 LDAP_SCOPE_ONELEVEL,
3106 "(objectclass=*)", attrs, &res);
3108 if (!ADS_ERR_OK(status)) {
3109 SAFE_FREE(host);
3110 ads_memfree(ads, hostnameDN);
3111 return status;
3114 for (msg_sub = ads_first_entry(ads, res); msg_sub;
3115 msg_sub = ads_next_entry(ads, msg_sub)) {
3117 char *dn = NULL;
3119 if ((dn = ads_get_dn(ads, msg_sub)) == NULL) {
3120 SAFE_FREE(host);
3121 ads_memfree(ads, hostnameDN);
3122 return ADS_ERROR(LDAP_NO_MEMORY);
3125 status = ads_del_dn(ads, dn);
3126 if (!ADS_ERR_OK(status)) {
3127 DEBUG(3,("failed to delete dn %s: %s\n", dn, ads_errstr(status)));
3128 SAFE_FREE(host);
3129 ads_memfree(ads, dn);
3130 ads_memfree(ads, hostnameDN);
3131 return status;
3134 ads_memfree(ads, dn);
3137 /* there should be no subordinate objects anymore */
3138 status = ads_do_search_retry(ads, hostnameDN,
3139 LDAP_SCOPE_ONELEVEL,
3140 "(objectclass=*)", attrs, &res);
3142 if (!ADS_ERR_OK(status) || ( (ads_count_replies(ads, res)) > 0 ) ) {
3143 SAFE_FREE(host);
3144 ads_memfree(ads, hostnameDN);
3145 return status;
3148 /* delete hostnameDN now */
3149 status = ads_del_dn(ads, hostnameDN);
3150 if (!ADS_ERR_OK(status)) {
3151 SAFE_FREE(host);
3152 DEBUG(3,("failed to delete dn %s: %s\n", hostnameDN, ads_errstr(status)));
3153 ads_memfree(ads, hostnameDN);
3154 return status;
3158 ads_memfree(ads, hostnameDN);
3160 status = ads_find_machine_acct(ads, &res, host);
3161 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
3162 DEBUG(3, ("Failed to remove host account.\n"));
3163 SAFE_FREE(host);
3164 return status;
3167 SAFE_FREE(host);
3168 return status;
3172 * pull all token-sids from an LDAP dn
3173 * @param ads connection to ads server
3174 * @param mem_ctx TALLOC_CTX for allocating sid array
3175 * @param dn of LDAP object
3176 * @param user_sid pointer to DOM_SID (objectSid)
3177 * @param primary_group_sid pointer to DOM_SID (self composed)
3178 * @param sids pointer to sid array to allocate
3179 * @param num_sids counter of SIDs pulled
3180 * @return status of token query
3182 ADS_STATUS ads_get_tokensids(ADS_STRUCT *ads,
3183 TALLOC_CTX *mem_ctx,
3184 const char *dn,
3185 DOM_SID *user_sid,
3186 DOM_SID *primary_group_sid,
3187 DOM_SID **sids,
3188 size_t *num_sids)
3190 ADS_STATUS status;
3191 LDAPMessage *res = NULL;
3192 int count = 0;
3193 size_t tmp_num_sids;
3194 DOM_SID *tmp_sids;
3195 DOM_SID tmp_user_sid;
3196 DOM_SID tmp_primary_group_sid;
3197 uint32 pgid;
3198 const char *attrs[] = {
3199 "objectSid",
3200 "tokenGroups",
3201 "primaryGroupID",
3202 NULL
3205 status = ads_search_retry_dn(ads, &res, dn, attrs);
3206 if (!ADS_ERR_OK(status)) {
3207 return status;
3210 count = ads_count_replies(ads, res);
3211 if (count != 1) {
3212 ads_msgfree(ads, res);
3213 return ADS_ERROR_LDAP(LDAP_NO_SUCH_OBJECT);
3216 if (!ads_pull_sid(ads, res, "objectSid", &tmp_user_sid)) {
3217 ads_msgfree(ads, res);
3218 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3221 if (!ads_pull_uint32(ads, res, "primaryGroupID", &pgid)) {
3222 ads_msgfree(ads, res);
3223 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3227 /* hack to compose the primary group sid without knowing the
3228 * domsid */
3230 DOM_SID domsid;
3231 uint32 dummy_rid;
3233 sid_copy(&domsid, &tmp_user_sid);
3235 if (!sid_split_rid(&domsid, &dummy_rid)) {
3236 ads_msgfree(ads, res);
3237 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3240 if (!sid_compose(&tmp_primary_group_sid, &domsid, pgid)) {
3241 ads_msgfree(ads, res);
3242 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3246 tmp_num_sids = ads_pull_sids(ads, mem_ctx, res, "tokenGroups", &tmp_sids);
3248 if (tmp_num_sids == 0 || !tmp_sids) {
3249 ads_msgfree(ads, res);
3250 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3253 if (num_sids) {
3254 *num_sids = tmp_num_sids;
3257 if (sids) {
3258 *sids = tmp_sids;
3261 if (user_sid) {
3262 *user_sid = tmp_user_sid;
3265 if (primary_group_sid) {
3266 *primary_group_sid = tmp_primary_group_sid;
3269 DEBUG(10,("ads_get_tokensids: returned %d sids\n", (int)tmp_num_sids + 2));
3271 ads_msgfree(ads, res);
3272 return ADS_ERROR_LDAP(LDAP_SUCCESS);
3275 ADS_STATUS ads_find_samaccount(ADS_STRUCT *ads,
3276 TALLOC_CTX *mem_ctx,
3277 const char *samaccountname,
3278 uint32 *uac_ret,
3279 const char **dn_ret)
3281 ADS_STATUS status;
3282 const char *attrs[] = { "userAccountControl", NULL };
3283 const char *filter;
3284 LDAPMessage *res = NULL;
3285 char *dn = NULL;
3286 uint32 uac = 0;
3288 filter = talloc_asprintf(mem_ctx, "(&(objectclass=user)(sAMAccountName=%s))",
3289 samaccountname);
3290 if (filter == NULL) {
3291 goto out;
3294 status = ads_do_search_all(ads, ads->config.bind_path,
3295 LDAP_SCOPE_SUBTREE,
3296 filter, attrs, &res);
3298 if (!ADS_ERR_OK(status)) {
3299 goto out;
3302 if (ads_count_replies(ads, res) != 1) {
3303 printf("no result\n");
3304 goto out;
3307 dn = ads_get_dn(ads, res);
3308 if (dn == NULL) {
3309 status = ADS_ERROR(LDAP_NO_MEMORY);
3310 goto out;
3313 if (!ads_pull_uint32(ads, res, "userAccountControl", &uac)) {
3314 status = ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
3315 goto out;
3318 if (uac_ret) {
3319 *uac_ret = uac;
3322 if (dn_ret) {
3323 *dn_ret = talloc_strdup(mem_ctx, dn);
3324 if (!*dn_ret) {
3325 status = ADS_ERROR(LDAP_NO_MEMORY);
3326 goto out;
3329 out:
3330 ads_memfree(ads, dn);
3331 ads_msgfree(ads, res);
3333 return status;
3335 #endif