2 Unix SMB/CIFS implementation.
3 ads (active directory) utility library
4 Copyright (C) Andrew Tridgell 2001
5 Copyright (C) Remus Koos 2001
6 Copyright (C) Jim McDonough <jmcd@us.ibm.com> 2002
7 Copyright (C) Guenther Deschner 2005
8 Copyright (C) Gerald Carter 2006
10 This program is free software; you can redistribute it and/or modify
11 it under the terms of the GNU General Public License as published by
12 the Free Software Foundation; either version 3 of the License, or
13 (at your option) any later version.
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License for more details.
20 You should have received a copy of the GNU General Public License
21 along with this program. If not, see <http://www.gnu.org/licenses/>.
30 * @brief basic ldap client-side routines for ads server communications
32 * The routines contained here should do the necessary ldap calls for
35 * Important note: attribute names passed into ads_ routines must
36 * already be in UTF-8 format. We do not convert them because in almost
37 * all cases, they are just ascii (which is represented with the same
38 * codepoints in UTF-8). This may have to change at some point
42 #define LDAP_SERVER_TREE_DELETE_OID "1.2.840.113556.1.4.805"
44 static SIG_ATOMIC_T gotalarm;
46 /***************************************************************
47 Signal function to tell us we timed out.
48 ****************************************************************/
50 static void gotalarm_sig(void)
55 LDAP *ldap_open_with_timeout(const char *server, int port, unsigned int to)
61 CatchSignal(SIGALRM, SIGNAL_CAST gotalarm_sig);
63 /* End setup timeout. */
65 ldp = ldap_open(server, port);
68 DEBUG(2,("Could not open LDAP connection to %s:%d: %s\n",
69 server, port, strerror(errno)));
72 /* Teardown timeout. */
73 CatchSignal(SIGALRM, SIGNAL_CAST SIG_IGN);
79 static int ldap_search_with_timeout(LDAP *ld,
80 LDAP_CONST char *base,
82 LDAP_CONST char *filter,
90 struct timeval timeout;
93 /* Setup timeout for the ldap_search_ext_s call - local and remote. */
94 timeout.tv_sec = lp_ldap_timeout();
97 /* Setup alarm timeout.... Do we need both of these ? JRA. */
99 CatchSignal(SIGALRM, SIGNAL_CAST gotalarm_sig);
100 alarm(lp_ldap_timeout());
101 /* End setup timeout. */
103 result = ldap_search_ext_s(ld, base, scope, filter, attrs,
104 attrsonly, sctrls, cctrls, &timeout,
107 /* Teardown timeout. */
108 CatchSignal(SIGALRM, SIGNAL_CAST SIG_IGN);
112 return LDAP_TIMELIMIT_EXCEEDED;
117 /**********************************************
118 Do client and server sitename match ?
119 **********************************************/
121 BOOL ads_sitename_match(ADS_STRUCT *ads)
123 if (ads->config.server_site_name == NULL &&
124 ads->config.client_site_name == NULL ) {
125 DEBUG(10,("ads_sitename_match: both null\n"));
128 if (ads->config.server_site_name &&
129 ads->config.client_site_name &&
130 strequal(ads->config.server_site_name,
131 ads->config.client_site_name)) {
132 DEBUG(10,("ads_sitename_match: name %s match\n", ads->config.server_site_name));
135 DEBUG(10,("ads_sitename_match: no match between server: %s and client: %s\n",
136 ads->config.server_site_name ? ads->config.server_site_name : "NULL",
137 ads->config.client_site_name ? ads->config.client_site_name : "NULL"));
141 /**********************************************
142 Is this the closest DC ?
143 **********************************************/
145 BOOL ads_closest_dc(ADS_STRUCT *ads)
147 if (ads->config.flags & ADS_CLOSEST) {
148 DEBUG(10,("ads_closest_dc: ADS_CLOSEST flag set\n"));
152 /* not sure if this can ever happen */
153 if (ads_sitename_match(ads)) {
154 DEBUG(10,("ads_closest_dc: ADS_CLOSEST flag not set but sites match\n"));
158 DEBUG(10,("ads_closest_dc: %s is not the closest DC\n",
159 ads->config.ldap_server_name));
166 try a connection to a given ldap server, returning True and setting the servers IP
167 in the ads struct if successful
169 BOOL ads_try_connect(ADS_STRUCT *ads, const char *server )
172 struct cldap_netlogon_reply cldap_reply;
174 if (!server || !*server) {
178 DEBUG(5,("ads_try_connect: sending CLDAP request to %s (realm: %s)\n",
179 server, ads->server.realm));
181 /* this copes with inet_ntoa brokenness */
183 srv = SMB_STRDUP(server);
185 ZERO_STRUCT( cldap_reply );
187 if ( !ads_cldap_netlogon( srv, ads->server.realm, &cldap_reply ) ) {
188 DEBUG(3,("ads_try_connect: CLDAP request %s failed.\n", srv));
193 /* Check the CLDAP reply flags */
195 if ( !(cldap_reply.flags & ADS_LDAP) ) {
196 DEBUG(1,("ads_try_connect: %s's CLDAP reply says it is not an LDAP server!\n",
202 /* Fill in the ads->config values */
204 SAFE_FREE(ads->config.realm);
205 SAFE_FREE(ads->config.bind_path);
206 SAFE_FREE(ads->config.ldap_server_name);
207 SAFE_FREE(ads->config.server_site_name);
208 SAFE_FREE(ads->config.client_site_name);
209 SAFE_FREE(ads->server.workgroup);
211 ads->config.flags = cldap_reply.flags;
212 ads->config.ldap_server_name = SMB_STRDUP(cldap_reply.hostname);
213 strupper_m(cldap_reply.domain);
214 ads->config.realm = SMB_STRDUP(cldap_reply.domain);
215 ads->config.bind_path = ads_build_dn(ads->config.realm);
216 if (*cldap_reply.server_site_name) {
217 ads->config.server_site_name =
218 SMB_STRDUP(cldap_reply.server_site_name);
220 if (*cldap_reply.client_site_name) {
221 ads->config.client_site_name =
222 SMB_STRDUP(cldap_reply.client_site_name);
225 ads->server.workgroup = SMB_STRDUP(cldap_reply.netbios_domain);
227 ads->ldap.port = LDAP_PORT;
228 ads->ldap.ip = *interpret_addr2(srv);
231 /* Store our site name. */
232 sitename_store( cldap_reply.domain, cldap_reply.client_site_name );
237 /**********************************************************************
238 Try to find an AD dc using our internal name resolution routines
239 Try the realm first and then then workgroup name if netbios is not
241 **********************************************************************/
243 static NTSTATUS ads_find_dc(ADS_STRUCT *ads)
247 struct ip_service *ip_list;
249 BOOL got_realm = False;
250 BOOL use_own_domain = False;
252 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
254 /* if the realm and workgroup are both empty, assume they are ours */
257 c_realm = ads->server.realm;
259 if ( !c_realm || !*c_realm ) {
260 /* special case where no realm and no workgroup means our own */
261 if ( !ads->server.workgroup || !*ads->server.workgroup ) {
262 use_own_domain = True;
263 c_realm = lp_realm();
267 if (c_realm && *c_realm)
270 /* we need to try once with the realm name and fallback to the
271 netbios domain name if we fail (if netbios has not been disabled */
273 if ( !got_realm && !lp_disable_netbios() ) {
274 c_realm = ads->server.workgroup;
275 if (!c_realm || !*c_realm) {
276 if ( use_own_domain )
277 c_realm = lp_workgroup();
280 if ( !c_realm || !*c_realm ) {
281 DEBUG(0,("ads_find_dc: no realm or workgroup! Don't know what to do\n"));
282 return NT_STATUS_INVALID_PARAMETER; /* rather need MISSING_PARAMETER ... */
286 pstrcpy( realm, c_realm );
288 sitename = sitename_fetch(realm);
292 DEBUG(6,("ads_find_dc: looking for %s '%s'\n",
293 (got_realm ? "realm" : "domain"), realm));
295 status = get_sorted_dc_list(realm, sitename, &ip_list, &count, got_realm);
296 if (!NT_STATUS_IS_OK(status)) {
297 /* fall back to netbios if we can */
298 if ( got_realm && !lp_disable_netbios() ) {
307 /* if we fail this loop, then giveup since all the IP addresses returned were dead */
308 for ( i=0; i<count; i++ ) {
311 fstrcpy( server, inet_ntoa(ip_list[i].ip) );
313 if ( !NT_STATUS_IS_OK(check_negative_conn_cache(realm, server)) )
317 /* realm in this case is a workgroup name. We need
318 to ignore any IP addresses in the negative connection
319 cache that match ip addresses returned in the ad realm
320 case. It sucks that I have to reproduce the logic above... */
321 c_realm = ads->server.realm;
322 if ( !c_realm || !*c_realm ) {
323 if ( !ads->server.workgroup || !*ads->server.workgroup ) {
324 c_realm = lp_realm();
327 if (c_realm && *c_realm &&
328 !NT_STATUS_IS_OK(check_negative_conn_cache(c_realm, server))) {
329 /* Ensure we add the workgroup name for this
330 IP address as negative too. */
331 add_failed_connection_entry( realm, server, NT_STATUS_UNSUCCESSFUL );
336 if ( ads_try_connect(ads, server) ) {
342 /* keep track of failures */
343 add_failed_connection_entry( realm, server, NT_STATUS_UNSUCCESSFUL );
348 /* In case we failed to contact one of our closest DC on our site we
349 * need to try to find another DC, retry with a site-less SRV DNS query
353 DEBUG(1,("ads_find_dc: failed to find a valid DC on our site (%s), "
354 "trying to find another DC\n", sitename));
356 namecache_delete(realm, 0x1C);
360 return NT_STATUS_NO_LOGON_SERVERS;
365 * Connect to the LDAP server
366 * @param ads Pointer to an existing ADS_STRUCT
367 * @return status of connection
369 ADS_STATUS ads_connect(ADS_STRUCT *ads)
371 int version = LDAP_VERSION3;
375 ZERO_STRUCT(ads->ldap);
376 ads->ldap.last_attempt = time(NULL);
377 ads->ldap.wrap_type = ADS_SASLWRAP_TYPE_PLAIN;
379 /* try with a user specified server */
381 if (ads->server.ldap_server &&
382 ads_try_connect(ads, ads->server.ldap_server)) {
386 ntstatus = ads_find_dc(ads);
387 if (NT_STATUS_IS_OK(ntstatus)) {
391 return ADS_ERROR_NT(ntstatus);
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));
412 /* this is a really nasty hack to avoid ADS DNS problems. It needs a patch
413 to MIT kerberos to work (tridge) */
416 asprintf(&env, "KRB5_KDC_ADDRESS_%s", ads->config.realm);
417 setenv(env, ads->auth.kdc_server, 1);
422 /* If the caller() requested no LDAP bind, then we are done */
424 if (ads->auth.flags & ADS_AUTH_NO_BIND) {
428 ads->ldap.mem_ctx = talloc_init("ads LDAP connection memory");
429 if (!ads->ldap.mem_ctx) {
430 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
433 /* Otherwise setup the TCP LDAP session */
435 if ( (ads->ldap.ld = ldap_open_with_timeout(ads->config.ldap_server_name,
436 LDAP_PORT, lp_ldap_timeout())) == NULL )
438 return ADS_ERROR(LDAP_OPERATIONS_ERROR);
441 /* cache the successful connection for workgroup and realm */
442 if (ads_closest_dc(ads)) {
443 saf_store( ads->server.workgroup, inet_ntoa(ads->ldap.ip));
444 saf_store( ads->server.realm, inet_ntoa(ads->ldap.ip));
447 ldap_set_option(ads->ldap.ld, LDAP_OPT_PROTOCOL_VERSION, &version);
449 status = ADS_ERROR(smb_ldap_start_tls(ads->ldap.ld, version));
450 if (!ADS_ERR_OK(status)) {
454 /* fill in the current time and offsets */
456 status = ads_current_time( ads );
457 if ( !ADS_ERR_OK(status) ) {
461 /* Now do the bind */
463 if (ads->auth.flags & ADS_AUTH_ANON_BIND) {
464 return ADS_ERROR(ldap_simple_bind_s( ads->ldap.ld, NULL, NULL));
467 if (ads->auth.flags & ADS_AUTH_SIMPLE_BIND) {
468 return ADS_ERROR(ldap_simple_bind_s( ads->ldap.ld, ads->auth.user_name, ads->auth.password));
471 return ads_sasl_bind(ads);
475 * Disconnect the LDAP server
476 * @param ads Pointer to an existing ADS_STRUCT
478 void ads_disconnect(ADS_STRUCT *ads)
481 ldap_unbind(ads->ldap.ld);
484 if (ads->ldap.wrap_ops && ads->ldap.wrap_ops->disconnect) {
485 ads->ldap.wrap_ops->disconnect(ads);
487 if (ads->ldap.mem_ctx) {
488 talloc_free(ads->ldap.mem_ctx);
490 ZERO_STRUCT(ads->ldap);
494 Duplicate a struct berval into talloc'ed memory
496 static struct berval *dup_berval(TALLOC_CTX *ctx, const struct berval *in_val)
498 struct berval *value;
500 if (!in_val) return NULL;
502 value = TALLOC_ZERO_P(ctx, struct berval);
505 if (in_val->bv_len == 0) return value;
507 value->bv_len = in_val->bv_len;
508 value->bv_val = (char *)TALLOC_MEMDUP(ctx, in_val->bv_val,
514 Make a values list out of an array of (struct berval *)
516 static struct berval **ads_dup_values(TALLOC_CTX *ctx,
517 const struct berval **in_vals)
519 struct berval **values;
522 if (!in_vals) return NULL;
523 for (i=0; in_vals[i]; i++)
525 values = TALLOC_ZERO_ARRAY(ctx, struct berval *, i+1);
526 if (!values) return NULL;
528 for (i=0; in_vals[i]; i++) {
529 values[i] = dup_berval(ctx, in_vals[i]);
535 UTF8-encode a values list out of an array of (char *)
537 static char **ads_push_strvals(TALLOC_CTX *ctx, const char **in_vals)
542 if (!in_vals) return NULL;
543 for (i=0; in_vals[i]; i++)
545 values = TALLOC_ZERO_ARRAY(ctx, char *, i+1);
546 if (!values) return NULL;
548 for (i=0; in_vals[i]; i++) {
549 push_utf8_talloc(ctx, &values[i], in_vals[i]);
555 Pull a (char *) array out of a UTF8-encoded values list
557 static char **ads_pull_strvals(TALLOC_CTX *ctx, const char **in_vals)
562 if (!in_vals) return NULL;
563 for (i=0; in_vals[i]; i++)
565 values = TALLOC_ZERO_ARRAY(ctx, char *, i+1);
566 if (!values) return NULL;
568 for (i=0; in_vals[i]; i++) {
569 pull_utf8_talloc(ctx, &values[i], in_vals[i]);
575 * Do a search with paged results. cookie must be null on the first
576 * call, and then returned on each subsequent call. It will be null
577 * again when the entire search is complete
578 * @param ads connection to ads server
579 * @param bind_path Base dn for the search
580 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
581 * @param expr Search expression - specified in local charset
582 * @param attrs Attributes to retrieve - specified in utf8 or ascii
583 * @param res ** which will contain results - free res* with ads_msgfree()
584 * @param count Number of entries retrieved on this page
585 * @param cookie The paged results cookie to be returned on subsequent calls
586 * @return status of search
588 static ADS_STATUS ads_do_paged_search_args(ADS_STRUCT *ads,
589 const char *bind_path,
590 int scope, const char *expr,
591 const char **attrs, void *args,
593 int *count, struct berval **cookie)
596 char *utf8_expr, *utf8_path, **search_attrs;
597 LDAPControl PagedResults, NoReferrals, ExternalCtrl, *controls[4], **rcontrols;
598 BerElement *cookie_be = NULL;
599 struct berval *cookie_bv= NULL;
600 BerElement *ext_be = NULL;
601 struct berval *ext_bv= NULL;
604 ads_control *external_control = (ads_control *) args;
608 if (!(ctx = talloc_init("ads_do_paged_search_args")))
609 return ADS_ERROR(LDAP_NO_MEMORY);
611 /* 0 means the conversion worked but the result was empty
612 so we only fail if it's -1. In any case, it always
613 at least nulls out the dest */
614 if ((push_utf8_talloc(ctx, &utf8_expr, expr) == (size_t)-1) ||
615 (push_utf8_talloc(ctx, &utf8_path, bind_path) == (size_t)-1)) {
620 if (!attrs || !(*attrs))
623 /* This would be the utf8-encoded version...*/
624 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
625 if (!(str_list_copy(&search_attrs, attrs))) {
631 /* Paged results only available on ldap v3 or later */
632 ldap_get_option(ads->ldap.ld, LDAP_OPT_PROTOCOL_VERSION, &version);
633 if (version < LDAP_VERSION3) {
634 rc = LDAP_NOT_SUPPORTED;
638 cookie_be = ber_alloc_t(LBER_USE_DER);
640 ber_printf(cookie_be, "{iO}", (ber_int_t) 1000, *cookie);
641 ber_bvfree(*cookie); /* don't need it from last time */
644 ber_printf(cookie_be, "{io}", (ber_int_t) 1000, "", 0);
646 ber_flatten(cookie_be, &cookie_bv);
647 PagedResults.ldctl_oid = CONST_DISCARD(char *, ADS_PAGE_CTL_OID);
648 PagedResults.ldctl_iscritical = (char) 1;
649 PagedResults.ldctl_value.bv_len = cookie_bv->bv_len;
650 PagedResults.ldctl_value.bv_val = cookie_bv->bv_val;
652 NoReferrals.ldctl_oid = CONST_DISCARD(char *, ADS_NO_REFERRALS_OID);
653 NoReferrals.ldctl_iscritical = (char) 0;
654 NoReferrals.ldctl_value.bv_len = 0;
655 NoReferrals.ldctl_value.bv_val = CONST_DISCARD(char *, "");
657 if (external_control &&
658 (strequal(external_control->control, ADS_EXTENDED_DN_OID) ||
659 strequal(external_control->control, ADS_SD_FLAGS_OID))) {
661 ExternalCtrl.ldctl_oid = CONST_DISCARD(char *, external_control->control);
662 ExternalCtrl.ldctl_iscritical = (char) external_control->critical;
664 /* win2k does not accept a ldctl_value beeing passed in */
666 if (external_control->val != 0) {
668 if ((ext_be = ber_alloc_t(LBER_USE_DER)) == NULL ) {
673 if ((ber_printf(ext_be, "{i}", (ber_int_t) external_control->val)) == -1) {
677 if ((ber_flatten(ext_be, &ext_bv)) == -1) {
682 ExternalCtrl.ldctl_value.bv_len = ext_bv->bv_len;
683 ExternalCtrl.ldctl_value.bv_val = ext_bv->bv_val;
686 ExternalCtrl.ldctl_value.bv_len = 0;
687 ExternalCtrl.ldctl_value.bv_val = NULL;
690 controls[0] = &NoReferrals;
691 controls[1] = &PagedResults;
692 controls[2] = &ExternalCtrl;
696 controls[0] = &NoReferrals;
697 controls[1] = &PagedResults;
701 /* we need to disable referrals as the openldap libs don't
702 handle them and paged results at the same time. Using them
703 together results in the result record containing the server
704 page control being removed from the result list (tridge/jmcd)
706 leaving this in despite the control that says don't generate
707 referrals, in case the server doesn't support it (jmcd)
709 ldap_set_option(ads->ldap.ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
711 rc = ldap_search_with_timeout(ads->ldap.ld, utf8_path, scope, utf8_expr,
712 search_attrs, 0, controls,
714 (LDAPMessage **)res);
716 ber_free(cookie_be, 1);
717 ber_bvfree(cookie_bv);
720 DEBUG(3,("ads_do_paged_search_args: ldap_search_with_timeout(%s) -> %s\n", expr,
721 ldap_err2string(rc)));
725 rc = ldap_parse_result(ads->ldap.ld, *res, NULL, NULL, NULL,
726 NULL, &rcontrols, 0);
732 for (i=0; rcontrols[i]; i++) {
733 if (strcmp(ADS_PAGE_CTL_OID, rcontrols[i]->ldctl_oid) == 0) {
734 cookie_be = ber_init(&rcontrols[i]->ldctl_value);
735 ber_scanf(cookie_be,"{iO}", (ber_int_t *) count,
737 /* the berval is the cookie, but must be freed when
739 if (cookie_bv->bv_len) /* still more to do */
740 *cookie=ber_bvdup(cookie_bv);
743 ber_bvfree(cookie_bv);
744 ber_free(cookie_be, 1);
748 ldap_controls_free(rcontrols);
761 /* if/when we decide to utf8-encode attrs, take out this next line */
762 str_list_free(&search_attrs);
764 return ADS_ERROR(rc);
767 static ADS_STATUS ads_do_paged_search(ADS_STRUCT *ads, const char *bind_path,
768 int scope, const char *expr,
769 const char **attrs, LDAPMessage **res,
770 int *count, struct berval **cookie)
772 return ads_do_paged_search_args(ads, bind_path, scope, expr, attrs, NULL, res, count, cookie);
777 * Get all results for a search. This uses ads_do_paged_search() to return
778 * all entries in a large search.
779 * @param ads connection to ads server
780 * @param bind_path Base dn for the search
781 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
782 * @param expr Search expression
783 * @param attrs Attributes to retrieve
784 * @param res ** which will contain results - free res* with ads_msgfree()
785 * @return status of search
787 ADS_STATUS ads_do_search_all_args(ADS_STRUCT *ads, const char *bind_path,
788 int scope, const char *expr,
789 const char **attrs, void *args,
792 struct berval *cookie = NULL;
797 status = ads_do_paged_search_args(ads, bind_path, scope, expr, attrs, args, res,
800 if (!ADS_ERR_OK(status))
803 #ifdef HAVE_LDAP_ADD_RESULT_ENTRY
805 LDAPMessage *res2 = NULL;
807 LDAPMessage *msg, *next;
809 status2 = ads_do_paged_search_args(ads, bind_path, scope, expr,
810 attrs, args, &res2, &count, &cookie);
812 if (!ADS_ERR_OK(status2)) break;
814 /* this relies on the way that ldap_add_result_entry() works internally. I hope
815 that this works on all ldap libs, but I have only tested with openldap */
816 for (msg = ads_first_entry(ads, res2); msg; msg = next) {
817 next = ads_next_entry(ads, msg);
818 ldap_add_result_entry((LDAPMessage **)res, msg);
820 /* note that we do not free res2, as the memory is now
821 part of the main returned list */
824 DEBUG(0, ("no ldap_add_result_entry() support in LDAP libs!\n"));
825 status = ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
831 ADS_STATUS ads_do_search_all(ADS_STRUCT *ads, const char *bind_path,
832 int scope, const char *expr,
833 const char **attrs, LDAPMessage **res)
835 return ads_do_search_all_args(ads, bind_path, scope, expr, attrs, NULL, res);
838 ADS_STATUS ads_do_search_all_sd_flags(ADS_STRUCT *ads, const char *bind_path,
839 int scope, const char *expr,
840 const char **attrs, uint32 sd_flags,
845 args.control = ADS_SD_FLAGS_OID;
847 args.critical = True;
849 return ads_do_search_all_args(ads, bind_path, scope, expr, attrs, &args, res);
854 * Run a function on all results for a search. Uses ads_do_paged_search() and
855 * runs the function as each page is returned, using ads_process_results()
856 * @param ads connection to ads server
857 * @param bind_path Base dn for the search
858 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
859 * @param expr Search expression - specified in local charset
860 * @param attrs Attributes to retrieve - specified in UTF-8 or ascii
861 * @param fn Function which takes attr name, values list, and data_area
862 * @param data_area Pointer which is passed to function on each call
863 * @return status of search
865 ADS_STATUS ads_do_search_all_fn(ADS_STRUCT *ads, const char *bind_path,
866 int scope, const char *expr, const char **attrs,
867 BOOL(*fn)(ADS_STRUCT *, char *, void **, void *),
870 struct berval *cookie = NULL;
875 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs, &res,
878 if (!ADS_ERR_OK(status)) return status;
880 ads_process_results(ads, res, fn, data_area);
881 ads_msgfree(ads, res);
884 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs,
885 &res, &count, &cookie);
887 if (!ADS_ERR_OK(status)) break;
889 ads_process_results(ads, res, fn, data_area);
890 ads_msgfree(ads, res);
897 * Do a search with a timeout.
898 * @param ads connection to ads server
899 * @param bind_path Base dn for the search
900 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
901 * @param expr Search expression
902 * @param attrs Attributes to retrieve
903 * @param res ** which will contain results - free res* with ads_msgfree()
904 * @return status of search
906 ADS_STATUS ads_do_search(ADS_STRUCT *ads, const char *bind_path, int scope,
908 const char **attrs, LDAPMessage **res)
911 char *utf8_expr, *utf8_path, **search_attrs = NULL;
915 if (!(ctx = talloc_init("ads_do_search"))) {
916 DEBUG(1,("ads_do_search: talloc_init() failed!"));
917 return ADS_ERROR(LDAP_NO_MEMORY);
920 /* 0 means the conversion worked but the result was empty
921 so we only fail if it's negative. In any case, it always
922 at least nulls out the dest */
923 if ((push_utf8_talloc(ctx, &utf8_expr, expr) == (size_t)-1) ||
924 (push_utf8_talloc(ctx, &utf8_path, bind_path) == (size_t)-1)) {
925 DEBUG(1,("ads_do_search: push_utf8_talloc() failed!"));
930 if (!attrs || !(*attrs))
933 /* This would be the utf8-encoded version...*/
934 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
935 if (!(str_list_copy(&search_attrs, attrs)))
937 DEBUG(1,("ads_do_search: str_list_copy() failed!"));
943 /* see the note in ads_do_paged_search - we *must* disable referrals */
944 ldap_set_option(ads->ldap.ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
946 rc = ldap_search_with_timeout(ads->ldap.ld, utf8_path, scope, utf8_expr,
947 search_attrs, 0, NULL, NULL,
949 (LDAPMessage **)res);
951 if (rc == LDAP_SIZELIMIT_EXCEEDED) {
952 DEBUG(3,("Warning! sizelimit exceeded in ldap. Truncating.\n"));
958 /* if/when we decide to utf8-encode attrs, take out this next line */
959 str_list_free(&search_attrs);
960 return ADS_ERROR(rc);
963 * Do a general ADS search
964 * @param ads connection to ads server
965 * @param res ** which will contain results - free res* with ads_msgfree()
966 * @param expr Search expression
967 * @param attrs Attributes to retrieve
968 * @return status of search
970 ADS_STATUS ads_search(ADS_STRUCT *ads, LDAPMessage **res,
971 const char *expr, const char **attrs)
973 return ads_do_search(ads, ads->config.bind_path, LDAP_SCOPE_SUBTREE,
978 * Do a search on a specific DistinguishedName
979 * @param ads connection to ads server
980 * @param res ** which will contain results - free res* with ads_msgfree()
981 * @param dn DistinguishName to search
982 * @param attrs Attributes to retrieve
983 * @return status of search
985 ADS_STATUS ads_search_dn(ADS_STRUCT *ads, LDAPMessage **res,
986 const char *dn, const char **attrs)
988 return ads_do_search(ads, dn, LDAP_SCOPE_BASE, "(objectclass=*)",
993 * Free up memory from a ads_search
994 * @param ads connection to ads server
995 * @param msg Search results to free
997 void ads_msgfree(ADS_STRUCT *ads, LDAPMessage *msg)
1004 * Free up memory from various ads requests
1005 * @param ads connection to ads server
1006 * @param mem Area to free
1008 void ads_memfree(ADS_STRUCT *ads, void *mem)
1014 * Get a dn from search results
1015 * @param ads connection to ads server
1016 * @param msg Search result
1019 char *ads_get_dn(ADS_STRUCT *ads, LDAPMessage *msg)
1021 char *utf8_dn, *unix_dn;
1023 utf8_dn = ldap_get_dn(ads->ldap.ld, msg);
1026 DEBUG (5, ("ads_get_dn: ldap_get_dn failed\n"));
1030 if (pull_utf8_allocate(&unix_dn, utf8_dn) == (size_t)-1) {
1031 DEBUG(0,("ads_get_dn: string conversion failure utf8 [%s]\n",
1035 ldap_memfree(utf8_dn);
1040 * Get the parent from a dn
1041 * @param dn the dn to return the parent from
1042 * @return parent dn string
1044 char *ads_parent_dn(const char *dn)
1052 p = strchr(dn, ',');
1062 * Find a machine account given a hostname
1063 * @param ads connection to ads server
1064 * @param res ** which will contain results - free res* with ads_msgfree()
1065 * @param host Hostname to search for
1066 * @return status of search
1068 ADS_STATUS ads_find_machine_acct(ADS_STRUCT *ads, LDAPMessage **res,
1069 const char *machine)
1073 const char *attrs[] = {"*", "nTSecurityDescriptor", NULL};
1077 /* the easiest way to find a machine account anywhere in the tree
1078 is to look for hostname$ */
1079 if (asprintf(&expr, "(samAccountName=%s$)", machine) == -1) {
1080 DEBUG(1, ("asprintf failed!\n"));
1081 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1084 status = ads_search(ads, res, expr, attrs);
1090 * Initialize a list of mods to be used in a modify request
1091 * @param ctx An initialized TALLOC_CTX
1092 * @return allocated ADS_MODLIST
1094 ADS_MODLIST ads_init_mods(TALLOC_CTX *ctx)
1096 #define ADS_MODLIST_ALLOC_SIZE 10
1099 if ((mods = TALLOC_ZERO_ARRAY(ctx, LDAPMod *, ADS_MODLIST_ALLOC_SIZE + 1)))
1100 /* -1 is safety to make sure we don't go over the end.
1101 need to reset it to NULL before doing ldap modify */
1102 mods[ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1104 return (ADS_MODLIST)mods;
1109 add an attribute to the list, with values list already constructed
1111 static ADS_STATUS ads_modlist_add(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1112 int mod_op, const char *name,
1113 const void *_invals)
1115 const void **invals = (const void **)_invals;
1117 LDAPMod **modlist = (LDAPMod **) *mods;
1118 struct berval **ber_values = NULL;
1119 char **char_values = NULL;
1122 mod_op = LDAP_MOD_DELETE;
1124 if (mod_op & LDAP_MOD_BVALUES)
1125 ber_values = ads_dup_values(ctx,
1126 (const struct berval **)invals);
1128 char_values = ads_push_strvals(ctx,
1129 (const char **) invals);
1132 /* find the first empty slot */
1133 for (curmod=0; modlist[curmod] && modlist[curmod] != (LDAPMod *) -1;
1135 if (modlist[curmod] == (LDAPMod *) -1) {
1136 if (!(modlist = TALLOC_REALLOC_ARRAY(ctx, modlist, LDAPMod *,
1137 curmod+ADS_MODLIST_ALLOC_SIZE+1)))
1138 return ADS_ERROR(LDAP_NO_MEMORY);
1139 memset(&modlist[curmod], 0,
1140 ADS_MODLIST_ALLOC_SIZE*sizeof(LDAPMod *));
1141 modlist[curmod+ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1142 *mods = (ADS_MODLIST)modlist;
1145 if (!(modlist[curmod] = TALLOC_ZERO_P(ctx, LDAPMod)))
1146 return ADS_ERROR(LDAP_NO_MEMORY);
1147 modlist[curmod]->mod_type = talloc_strdup(ctx, name);
1148 if (mod_op & LDAP_MOD_BVALUES) {
1149 modlist[curmod]->mod_bvalues = ber_values;
1150 } else if (mod_op & LDAP_MOD_DELETE) {
1151 modlist[curmod]->mod_values = NULL;
1153 modlist[curmod]->mod_values = char_values;
1156 modlist[curmod]->mod_op = mod_op;
1157 return ADS_ERROR(LDAP_SUCCESS);
1161 * Add a single string value to a mod list
1162 * @param ctx An initialized TALLOC_CTX
1163 * @param mods An initialized ADS_MODLIST
1164 * @param name The attribute name to add
1165 * @param val The value to add - NULL means DELETE
1166 * @return ADS STATUS indicating success of add
1168 ADS_STATUS ads_mod_str(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1169 const char *name, const char *val)
1171 const char *values[2];
1177 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1178 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE, name, values);
1182 * Add an array of string values to a mod list
1183 * @param ctx An initialized TALLOC_CTX
1184 * @param mods An initialized ADS_MODLIST
1185 * @param name The attribute name to add
1186 * @param vals The array of string values to add - NULL means DELETE
1187 * @return ADS STATUS indicating success of add
1189 ADS_STATUS ads_mod_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1190 const char *name, const char **vals)
1193 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1194 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE,
1195 name, (const void **) vals);
1200 * Add a single ber-encoded value to a mod list
1201 * @param ctx An initialized TALLOC_CTX
1202 * @param mods An initialized ADS_MODLIST
1203 * @param name The attribute name to add
1204 * @param val The value to add - NULL means DELETE
1205 * @return ADS STATUS indicating success of add
1207 static ADS_STATUS ads_mod_ber(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1208 const char *name, const struct berval *val)
1210 const struct berval *values[2];
1215 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1216 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE|LDAP_MOD_BVALUES,
1217 name, (const void **) values);
1222 * Perform an ldap modify
1223 * @param ads connection to ads server
1224 * @param mod_dn DistinguishedName to modify
1225 * @param mods list of modifications to perform
1226 * @return status of modify
1228 ADS_STATUS ads_gen_mod(ADS_STRUCT *ads, const char *mod_dn, ADS_MODLIST mods)
1231 char *utf8_dn = NULL;
1233 this control is needed to modify that contains a currently
1234 non-existent attribute (but allowable for the object) to run
1236 LDAPControl PermitModify = {
1237 CONST_DISCARD(char *, ADS_PERMIT_MODIFY_OID),
1240 LDAPControl *controls[2];
1242 controls[0] = &PermitModify;
1245 if (push_utf8_allocate(&utf8_dn, mod_dn) == -1) {
1246 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1249 /* find the end of the list, marked by NULL or -1 */
1250 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1251 /* make sure the end of the list is NULL */
1253 ret = ldap_modify_ext_s(ads->ldap.ld, utf8_dn,
1254 (LDAPMod **) mods, controls, NULL);
1256 return ADS_ERROR(ret);
1260 * Perform an ldap add
1261 * @param ads connection to ads server
1262 * @param new_dn DistinguishedName to add
1263 * @param mods list of attributes and values for DN
1264 * @return status of add
1266 ADS_STATUS ads_gen_add(ADS_STRUCT *ads, const char *new_dn, ADS_MODLIST mods)
1269 char *utf8_dn = NULL;
1271 if (push_utf8_allocate(&utf8_dn, new_dn) == -1) {
1272 DEBUG(1, ("ads_gen_add: push_utf8_allocate failed!"));
1273 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1276 /* find the end of the list, marked by NULL or -1 */
1277 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1278 /* make sure the end of the list is NULL */
1281 ret = ldap_add_s(ads->ldap.ld, utf8_dn, (LDAPMod**)mods);
1283 return ADS_ERROR(ret);
1287 * Delete a DistinguishedName
1288 * @param ads connection to ads server
1289 * @param new_dn DistinguishedName to delete
1290 * @return status of delete
1292 ADS_STATUS ads_del_dn(ADS_STRUCT *ads, char *del_dn)
1295 char *utf8_dn = NULL;
1296 if (push_utf8_allocate(&utf8_dn, del_dn) == -1) {
1297 DEBUG(1, ("ads_del_dn: push_utf8_allocate failed!"));
1298 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1301 ret = ldap_delete_s(ads->ldap.ld, utf8_dn);
1303 return ADS_ERROR(ret);
1307 * Build an org unit string
1308 * if org unit is Computers or blank then assume a container, otherwise
1309 * assume a / separated list of organisational units.
1310 * jmcd: '\' is now used for escapes so certain chars can be in the ou (e.g. #)
1311 * @param ads connection to ads server
1312 * @param org_unit Organizational unit
1313 * @return org unit string - caller must free
1315 char *ads_ou_string(ADS_STRUCT *ads, const char *org_unit)
1319 if (!org_unit || !*org_unit) {
1321 ret = ads_default_ou_string(ads, WELL_KNOWN_GUID_COMPUTERS);
1323 /* samba4 might not yet respond to a wellknownobject-query */
1324 return ret ? ret : SMB_STRDUP("cn=Computers");
1327 if (strequal(org_unit, "Computers")) {
1328 return SMB_STRDUP("cn=Computers");
1331 /* jmcd: removed "\\" from the separation chars, because it is
1332 needed as an escape for chars like '#' which are valid in an
1334 return ads_build_path(org_unit, "/", "ou=", 1);
1338 * Get a org unit string for a well-known GUID
1339 * @param ads connection to ads server
1340 * @param wknguid Well known GUID
1341 * @return org unit string - caller must free
1343 char *ads_default_ou_string(ADS_STRUCT *ads, const char *wknguid)
1346 LDAPMessage *res = NULL;
1347 char *base, *wkn_dn = NULL, *ret = NULL, **wkn_dn_exp = NULL,
1348 **bind_dn_exp = NULL;
1349 const char *attrs[] = {"distinguishedName", NULL};
1350 int new_ln, wkn_ln, bind_ln, i;
1352 if (wknguid == NULL) {
1356 if (asprintf(&base, "<WKGUID=%s,%s>", wknguid, ads->config.bind_path ) == -1) {
1357 DEBUG(1, ("asprintf failed!\n"));
1361 status = ads_search_dn(ads, &res, base, attrs);
1362 if (!ADS_ERR_OK(status)) {
1363 DEBUG(1,("Failed while searching for: %s\n", base));
1367 if (ads_count_replies(ads, res) != 1) {
1371 /* substitute the bind-path from the well-known-guid-search result */
1372 wkn_dn = ads_get_dn(ads, res);
1377 wkn_dn_exp = ldap_explode_dn(wkn_dn, 0);
1382 bind_dn_exp = ldap_explode_dn(ads->config.bind_path, 0);
1387 for (wkn_ln=0; wkn_dn_exp[wkn_ln]; wkn_ln++)
1389 for (bind_ln=0; bind_dn_exp[bind_ln]; bind_ln++)
1392 new_ln = wkn_ln - bind_ln;
1394 ret = SMB_STRDUP(wkn_dn_exp[0]);
1399 for (i=1; i < new_ln; i++) {
1402 if (asprintf(&s, "%s,%s", ret, wkn_dn_exp[i]) == -1) {
1408 ret = SMB_STRDUP(s);
1417 ads_msgfree(ads, res);
1418 ads_memfree(ads, wkn_dn);
1420 ldap_value_free(wkn_dn_exp);
1423 ldap_value_free(bind_dn_exp);
1430 * Adds (appends) an item to an attribute array, rather then
1431 * replacing the whole list
1432 * @param ctx An initialized TALLOC_CTX
1433 * @param mods An initialized ADS_MODLIST
1434 * @param name name of the ldap attribute to append to
1435 * @param vals an array of values to add
1436 * @return status of addition
1439 ADS_STATUS ads_add_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1440 const char *name, const char **vals)
1442 return ads_modlist_add(ctx, mods, LDAP_MOD_ADD, name,
1443 (const void *) vals);
1447 * Determines the computer account's current KVNO via an LDAP lookup
1448 * @param ads An initialized ADS_STRUCT
1449 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1450 * @return the kvno for the computer account, or -1 in case of a failure.
1453 uint32 ads_get_kvno(ADS_STRUCT *ads, const char *machine_name)
1455 LDAPMessage *res = NULL;
1456 uint32 kvno = (uint32)-1; /* -1 indicates a failure */
1458 const char *attrs[] = {"msDS-KeyVersionNumber", NULL};
1459 char *dn_string = NULL;
1460 ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS);
1462 DEBUG(5,("ads_get_kvno: Searching for host %s\n", machine_name));
1463 if (asprintf(&filter, "(samAccountName=%s$)", machine_name) == -1) {
1466 ret = ads_search(ads, &res, filter, attrs);
1468 if (!ADS_ERR_OK(ret) && ads_count_replies(ads, res)) {
1469 DEBUG(1,("ads_get_kvno: Computer Account For %s not found.\n", machine_name));
1470 ads_msgfree(ads, res);
1474 dn_string = ads_get_dn(ads, res);
1476 DEBUG(0,("ads_get_kvno: out of memory.\n"));
1477 ads_msgfree(ads, res);
1480 DEBUG(5,("ads_get_kvno: Using: %s\n", dn_string));
1481 ads_memfree(ads, dn_string);
1483 /* ---------------------------------------------------------
1484 * 0 is returned as a default KVNO from this point on...
1485 * This is done because Windows 2000 does not support key
1486 * version numbers. Chances are that a failure in the next
1487 * step is simply due to Windows 2000 being used for a
1488 * domain controller. */
1491 if (!ads_pull_uint32(ads, res, "msDS-KeyVersionNumber", &kvno)) {
1492 DEBUG(3,("ads_get_kvno: Error Determining KVNO!\n"));
1493 DEBUG(3,("ads_get_kvno: Windows 2000 does not support KVNO's, so this may be normal.\n"));
1494 ads_msgfree(ads, res);
1499 DEBUG(5,("ads_get_kvno: Looked Up KVNO of: %d\n", kvno));
1500 ads_msgfree(ads, res);
1505 * This clears out all registered spn's for a given hostname
1506 * @param ads An initilaized ADS_STRUCT
1507 * @param machine_name the NetBIOS name of the computer.
1508 * @return 0 upon success, non-zero otherwise.
1511 ADS_STATUS ads_clear_service_principal_names(ADS_STRUCT *ads, const char *machine_name)
1514 LDAPMessage *res = NULL;
1516 const char *servicePrincipalName[1] = {NULL};
1517 ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS);
1518 char *dn_string = NULL;
1520 ret = ads_find_machine_acct(ads, &res, machine_name);
1521 if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
1522 DEBUG(5,("ads_clear_service_principal_names: WARNING: Host Account for %s not found... skipping operation.\n", machine_name));
1523 DEBUG(5,("ads_clear_service_principal_names: WARNING: Service Principals for %s have NOT been cleared.\n", machine_name));
1524 ads_msgfree(ads, res);
1525 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1528 DEBUG(5,("ads_clear_service_principal_names: Host account for %s found\n", machine_name));
1529 ctx = talloc_init("ads_clear_service_principal_names");
1531 ads_msgfree(ads, res);
1532 return ADS_ERROR(LDAP_NO_MEMORY);
1535 if (!(mods = ads_init_mods(ctx))) {
1536 talloc_destroy(ctx);
1537 ads_msgfree(ads, res);
1538 return ADS_ERROR(LDAP_NO_MEMORY);
1540 ret = ads_mod_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1541 if (!ADS_ERR_OK(ret)) {
1542 DEBUG(1,("ads_clear_service_principal_names: Error creating strlist.\n"));
1543 ads_msgfree(ads, res);
1544 talloc_destroy(ctx);
1547 dn_string = ads_get_dn(ads, res);
1549 talloc_destroy(ctx);
1550 ads_msgfree(ads, res);
1551 return ADS_ERROR(LDAP_NO_MEMORY);
1553 ret = ads_gen_mod(ads, dn_string, mods);
1554 ads_memfree(ads,dn_string);
1555 if (!ADS_ERR_OK(ret)) {
1556 DEBUG(1,("ads_clear_service_principal_names: Error: Updating Service Principals for machine %s in LDAP\n",
1558 ads_msgfree(ads, res);
1559 talloc_destroy(ctx);
1563 ads_msgfree(ads, res);
1564 talloc_destroy(ctx);
1569 * This adds a service principal name to an existing computer account
1570 * (found by hostname) in AD.
1571 * @param ads An initialized ADS_STRUCT
1572 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1573 * @param my_fqdn The fully qualified DNS name of the machine
1574 * @param spn A string of the service principal to add, i.e. 'host'
1575 * @return 0 upon sucess, or non-zero if a failure occurs
1578 ADS_STATUS ads_add_service_principal_name(ADS_STRUCT *ads, const char *machine_name,
1579 const char *my_fqdn, const char *spn)
1583 LDAPMessage *res = NULL;
1586 char *dn_string = NULL;
1587 const char *servicePrincipalName[3] = {NULL, NULL, NULL};
1589 ret = ads_find_machine_acct(ads, &res, machine_name);
1590 if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
1591 DEBUG(1,("ads_add_service_principal_name: WARNING: Host Account for %s not found... skipping operation.\n",
1593 DEBUG(1,("ads_add_service_principal_name: WARNING: Service Principal '%s/%s@%s' has NOT been added.\n",
1594 spn, machine_name, ads->config.realm));
1595 ads_msgfree(ads, res);
1596 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1599 DEBUG(1,("ads_add_service_principal_name: Host account for %s found\n", machine_name));
1600 if (!(ctx = talloc_init("ads_add_service_principal_name"))) {
1601 ads_msgfree(ads, res);
1602 return ADS_ERROR(LDAP_NO_MEMORY);
1605 /* add short name spn */
1607 if ( (psp1 = talloc_asprintf(ctx, "%s/%s", spn, machine_name)) == NULL ) {
1608 talloc_destroy(ctx);
1609 ads_msgfree(ads, res);
1610 return ADS_ERROR(LDAP_NO_MEMORY);
1613 strlower_m(&psp1[strlen(spn)]);
1614 servicePrincipalName[0] = psp1;
1616 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n",
1617 psp1, machine_name));
1620 /* add fully qualified spn */
1622 if ( (psp2 = talloc_asprintf(ctx, "%s/%s", spn, my_fqdn)) == NULL ) {
1623 ret = ADS_ERROR(LDAP_NO_MEMORY);
1627 strlower_m(&psp2[strlen(spn)]);
1628 servicePrincipalName[1] = psp2;
1630 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n",
1631 psp2, machine_name));
1633 if ( (mods = ads_init_mods(ctx)) == NULL ) {
1634 ret = ADS_ERROR(LDAP_NO_MEMORY);
1638 ret = ads_add_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1639 if (!ADS_ERR_OK(ret)) {
1640 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
1644 if ( (dn_string = ads_get_dn(ads, res)) == NULL ) {
1645 ret = ADS_ERROR(LDAP_NO_MEMORY);
1649 ret = ads_gen_mod(ads, dn_string, mods);
1650 ads_memfree(ads,dn_string);
1651 if (!ADS_ERR_OK(ret)) {
1652 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
1658 ads_msgfree(ads, res);
1663 * adds a machine account to the ADS server
1664 * @param ads An intialized ADS_STRUCT
1665 * @param machine_name - the NetBIOS machine name of this account.
1666 * @param account_type A number indicating the type of account to create
1667 * @param org_unit The LDAP path in which to place this account
1668 * @return 0 upon success, or non-zero otherwise
1671 ADS_STATUS ads_create_machine_acct(ADS_STRUCT *ads, const char *machine_name,
1672 const char *org_unit)
1675 char *samAccountName, *controlstr;
1678 char *machine_escaped = NULL;
1680 const char *objectClass[] = {"top", "person", "organizationalPerson",
1681 "user", "computer", NULL};
1682 LDAPMessage *res = NULL;
1683 uint32 acct_control = ( UF_WORKSTATION_TRUST_ACCOUNT |\
1684 UF_DONT_EXPIRE_PASSWD |\
1685 UF_ACCOUNTDISABLE );
1687 if (!(ctx = talloc_init("ads_add_machine_acct")))
1688 return ADS_ERROR(LDAP_NO_MEMORY);
1690 ret = ADS_ERROR(LDAP_NO_MEMORY);
1692 machine_escaped = escape_rdn_val_string_alloc(machine_name);
1693 if (!machine_escaped) {
1697 new_dn = talloc_asprintf(ctx, "cn=%s,%s", machine_escaped, org_unit);
1698 samAccountName = talloc_asprintf(ctx, "%s$", machine_name);
1700 if ( !new_dn || !samAccountName ) {
1704 #ifndef ENCTYPE_ARCFOUR_HMAC
1705 acct_control |= UF_USE_DES_KEY_ONLY;
1708 if (!(controlstr = talloc_asprintf(ctx, "%u", acct_control))) {
1712 if (!(mods = ads_init_mods(ctx))) {
1716 ads_mod_str(ctx, &mods, "cn", machine_name);
1717 ads_mod_str(ctx, &mods, "sAMAccountName", samAccountName);
1718 ads_mod_strlist(ctx, &mods, "objectClass", objectClass);
1719 ads_mod_str(ctx, &mods, "userAccountControl", controlstr);
1721 ret = ads_gen_add(ads, new_dn, mods);
1724 SAFE_FREE(machine_escaped);
1725 ads_msgfree(ads, res);
1726 talloc_destroy(ctx);
1732 * move a machine account to another OU on the ADS server
1733 * @param ads - An intialized ADS_STRUCT
1734 * @param machine_name - the NetBIOS machine name of this account.
1735 * @param org_unit - The LDAP path in which to place this account
1736 * @param moved - whether we moved the machine account (optional)
1737 * @return 0 upon success, or non-zero otherwise
1740 ADS_STATUS ads_move_machine_acct(ADS_STRUCT *ads, const char *machine_name,
1741 const char *org_unit, BOOL *moved)
1745 LDAPMessage *res = NULL;
1746 char *filter = NULL;
1747 char *computer_dn = NULL;
1749 char *computer_rdn = NULL;
1750 BOOL need_move = False;
1752 if (asprintf(&filter, "(samAccountName=%s$)", machine_name) == -1) {
1753 rc = ADS_ERROR(LDAP_NO_MEMORY);
1757 /* Find pre-existing machine */
1758 rc = ads_search(ads, &res, filter, NULL);
1759 if (!ADS_ERR_OK(rc)) {
1763 computer_dn = ads_get_dn(ads, res);
1765 rc = ADS_ERROR(LDAP_NO_MEMORY);
1769 parent_dn = ads_parent_dn(computer_dn);
1770 if (strequal(parent_dn, org_unit)) {
1776 if (asprintf(&computer_rdn, "CN=%s", machine_name) == -1) {
1777 rc = ADS_ERROR(LDAP_NO_MEMORY);
1781 ldap_status = ldap_rename_s(ads->ldap.ld, computer_dn, computer_rdn,
1782 org_unit, 1, NULL, NULL);
1783 rc = ADS_ERROR(ldap_status);
1786 ads_msgfree(ads, res);
1788 SAFE_FREE(computer_dn);
1789 SAFE_FREE(computer_rdn);
1791 if (!ADS_ERR_OK(rc)) {
1803 dump a binary result from ldap
1805 static void dump_binary(ADS_STRUCT *ads, const char *field, struct berval **values)
1808 for (i=0; values[i]; i++) {
1809 printf("%s: ", field);
1810 for (j=0; j<values[i]->bv_len; j++) {
1811 printf("%02X", (unsigned char)values[i]->bv_val[j]);
1817 static void dump_guid(ADS_STRUCT *ads, const char *field, struct berval **values)
1821 for (i=0; values[i]; i++) {
1822 memcpy(guid.info, values[i]->bv_val, sizeof(guid.info));
1823 printf("%s: %s\n", field,
1824 smb_uuid_string_static(smb_uuid_unpack_static(guid)));
1829 dump a sid result from ldap
1831 static void dump_sid(ADS_STRUCT *ads, const char *field, struct berval **values)
1834 for (i=0; values[i]; i++) {
1836 sid_parse(values[i]->bv_val, values[i]->bv_len, &sid);
1837 printf("%s: %s\n", field, sid_string_static(&sid));
1842 dump ntSecurityDescriptor
1844 static void dump_sd(ADS_STRUCT *ads, const char *filed, struct berval **values)
1849 TALLOC_CTX *ctx = 0;
1851 if (!(ctx = talloc_init("sec_io_desc")))
1855 prs_init(&ps, values[0]->bv_len, ctx, UNMARSHALL);
1856 prs_copy_data_in(&ps, values[0]->bv_val, values[0]->bv_len);
1857 prs_set_offset(&ps,0);
1860 if (!sec_io_desc("sd", &psd, &ps, 1)) {
1862 talloc_destroy(ctx);
1866 ads_disp_sd(ads, ctx, psd);
1870 talloc_destroy(ctx);
1874 dump a string result from ldap
1876 static void dump_string(const char *field, char **values)
1879 for (i=0; values[i]; i++) {
1880 printf("%s: %s\n", field, values[i]);
1885 dump a field from LDAP on stdout
1889 static BOOL ads_dump_field(ADS_STRUCT *ads, char *field, void **values, void *data_area)
1894 void (*handler)(ADS_STRUCT *, const char *, struct berval **);
1896 {"objectGUID", False, dump_guid},
1897 {"netbootGUID", False, dump_guid},
1898 {"nTSecurityDescriptor", False, dump_sd},
1899 {"dnsRecord", False, dump_binary},
1900 {"objectSid", False, dump_sid},
1901 {"tokenGroups", False, dump_sid},
1902 {"tokenGroupsNoGCAcceptable", False, dump_sid},
1903 {"tokengroupsGlobalandUniversal", False, dump_sid},
1904 {"mS-DS-CreatorSID", False, dump_sid},
1909 if (!field) { /* must be end of an entry */
1914 for (i=0; handlers[i].name; i++) {
1915 if (StrCaseCmp(handlers[i].name, field) == 0) {
1916 if (!values) /* first time, indicate string or not */
1917 return handlers[i].string;
1918 handlers[i].handler(ads, field, (struct berval **) values);
1922 if (!handlers[i].name) {
1923 if (!values) /* first time, indicate string conversion */
1925 dump_string(field, (char **)values);
1931 * Dump a result from LDAP on stdout
1932 * used for debugging
1933 * @param ads connection to ads server
1934 * @param res Results to dump
1937 void ads_dump(ADS_STRUCT *ads, LDAPMessage *res)
1939 ads_process_results(ads, res, ads_dump_field, NULL);
1943 * Walk through results, calling a function for each entry found.
1944 * The function receives a field name, a berval * array of values,
1945 * and a data area passed through from the start. The function is
1946 * called once with null for field and values at the end of each
1948 * @param ads connection to ads server
1949 * @param res Results to process
1950 * @param fn Function for processing each result
1951 * @param data_area user-defined area to pass to function
1953 void ads_process_results(ADS_STRUCT *ads, LDAPMessage *res,
1954 BOOL(*fn)(ADS_STRUCT *, char *, void **, void *),
1960 if (!(ctx = talloc_init("ads_process_results")))
1963 for (msg = ads_first_entry(ads, res); msg;
1964 msg = ads_next_entry(ads, msg)) {
1968 for (utf8_field=ldap_first_attribute(ads->ldap.ld,
1969 (LDAPMessage *)msg,&b);
1971 utf8_field=ldap_next_attribute(ads->ldap.ld,
1972 (LDAPMessage *)msg,b)) {
1973 struct berval **ber_vals;
1974 char **str_vals, **utf8_vals;
1978 pull_utf8_talloc(ctx, &field, utf8_field);
1979 string = fn(ads, field, NULL, data_area);
1982 utf8_vals = ldap_get_values(ads->ldap.ld,
1983 (LDAPMessage *)msg, field);
1984 str_vals = ads_pull_strvals(ctx,
1985 (const char **) utf8_vals);
1986 fn(ads, field, (void **) str_vals, data_area);
1987 ldap_value_free(utf8_vals);
1989 ber_vals = ldap_get_values_len(ads->ldap.ld,
1990 (LDAPMessage *)msg, field);
1991 fn(ads, field, (void **) ber_vals, data_area);
1993 ldap_value_free_len(ber_vals);
1995 ldap_memfree(utf8_field);
1998 talloc_free_children(ctx);
1999 fn(ads, NULL, NULL, data_area); /* completed an entry */
2002 talloc_destroy(ctx);
2006 * count how many replies are in a LDAPMessage
2007 * @param ads connection to ads server
2008 * @param res Results to count
2009 * @return number of replies
2011 int ads_count_replies(ADS_STRUCT *ads, void *res)
2013 return ldap_count_entries(ads->ldap.ld, (LDAPMessage *)res);
2017 * pull the first entry from a ADS result
2018 * @param ads connection to ads server
2019 * @param res Results of search
2020 * @return first entry from result
2022 LDAPMessage *ads_first_entry(ADS_STRUCT *ads, LDAPMessage *res)
2024 return ldap_first_entry(ads->ldap.ld, res);
2028 * pull the next entry from a ADS result
2029 * @param ads connection to ads server
2030 * @param res Results of search
2031 * @return next entry from result
2033 LDAPMessage *ads_next_entry(ADS_STRUCT *ads, LDAPMessage *res)
2035 return ldap_next_entry(ads->ldap.ld, res);
2039 * pull a single string from a ADS result
2040 * @param ads connection to ads server
2041 * @param mem_ctx TALLOC_CTX to use for allocating result string
2042 * @param msg Results of search
2043 * @param field Attribute to retrieve
2044 * @return Result string in talloc context
2046 char *ads_pull_string(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, LDAPMessage *msg,
2054 values = ldap_get_values(ads->ldap.ld, msg, field);
2059 rc = pull_utf8_talloc(mem_ctx, &ux_string,
2061 if (rc != (size_t)-1)
2065 ldap_value_free(values);
2070 * pull an array of strings from a ADS result
2071 * @param ads connection to ads server
2072 * @param mem_ctx TALLOC_CTX to use for allocating result string
2073 * @param msg Results of search
2074 * @param field Attribute to retrieve
2075 * @return Result strings in talloc context
2077 char **ads_pull_strings(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2078 LDAPMessage *msg, const char *field,
2085 values = ldap_get_values(ads->ldap.ld, msg, field);
2089 *num_values = ldap_count_values(values);
2091 ret = TALLOC_ARRAY(mem_ctx, char *, *num_values + 1);
2093 ldap_value_free(values);
2097 for (i=0;i<*num_values;i++) {
2098 if (pull_utf8_talloc(mem_ctx, &ret[i], values[i]) == -1) {
2099 ldap_value_free(values);
2105 ldap_value_free(values);
2110 * pull an array of strings from a ADS result
2111 * (handle large multivalue attributes with range retrieval)
2112 * @param ads connection to ads server
2113 * @param mem_ctx TALLOC_CTX to use for allocating result string
2114 * @param msg Results of search
2115 * @param field Attribute to retrieve
2116 * @param current_strings strings returned by a previous call to this function
2117 * @param next_attribute The next query should ask for this attribute
2118 * @param num_values How many values did we get this time?
2119 * @param more_values Are there more values to get?
2120 * @return Result strings in talloc context
2122 char **ads_pull_strings_range(ADS_STRUCT *ads,
2123 TALLOC_CTX *mem_ctx,
2124 LDAPMessage *msg, const char *field,
2125 char **current_strings,
2126 const char **next_attribute,
2127 size_t *num_strings,
2131 char *expected_range_attrib, *range_attr;
2132 BerElement *ptr = NULL;
2135 size_t num_new_strings;
2136 unsigned long int range_start;
2137 unsigned long int range_end;
2139 /* we might have been given the whole lot anyway */
2140 if ((strings = ads_pull_strings(ads, mem_ctx, msg, field, num_strings))) {
2141 *more_strings = False;
2145 expected_range_attrib = talloc_asprintf(mem_ctx, "%s;Range=", field);
2147 /* look for Range result */
2148 for (attr = ldap_first_attribute(ads->ldap.ld, (LDAPMessage *)msg, &ptr);
2150 attr = ldap_next_attribute(ads->ldap.ld, (LDAPMessage *)msg, ptr)) {
2151 /* we ignore the fact that this is utf8, as all attributes are ascii... */
2152 if (strnequal(attr, expected_range_attrib, strlen(expected_range_attrib))) {
2160 /* nothing here - this field is just empty */
2161 *more_strings = False;
2165 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-%lu",
2166 &range_start, &range_end) == 2) {
2167 *more_strings = True;
2169 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-*",
2170 &range_start) == 1) {
2171 *more_strings = False;
2173 DEBUG(1, ("ads_pull_strings_range: Cannot parse Range attriubte (%s)\n",
2175 ldap_memfree(range_attr);
2176 *more_strings = False;
2181 if ((*num_strings) != range_start) {
2182 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) doesn't start at %u, but at %lu"
2183 " - aborting range retreival\n",
2184 range_attr, (unsigned int)(*num_strings) + 1, range_start));
2185 ldap_memfree(range_attr);
2186 *more_strings = False;
2190 new_strings = ads_pull_strings(ads, mem_ctx, msg, range_attr, &num_new_strings);
2192 if (*more_strings && ((*num_strings + num_new_strings) != (range_end + 1))) {
2193 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) tells us we have %lu "
2194 "strings in this bunch, but we only got %lu - aborting range retreival\n",
2195 range_attr, (unsigned long int)range_end - range_start + 1,
2196 (unsigned long int)num_new_strings));
2197 ldap_memfree(range_attr);
2198 *more_strings = False;
2202 strings = TALLOC_REALLOC_ARRAY(mem_ctx, current_strings, char *,
2203 *num_strings + num_new_strings);
2205 if (strings == NULL) {
2206 ldap_memfree(range_attr);
2207 *more_strings = False;
2211 if (new_strings && num_new_strings) {
2212 memcpy(&strings[*num_strings], new_strings,
2213 sizeof(*new_strings) * num_new_strings);
2216 (*num_strings) += num_new_strings;
2218 if (*more_strings) {
2219 *next_attribute = talloc_asprintf(mem_ctx,
2224 if (!*next_attribute) {
2225 DEBUG(1, ("talloc_asprintf for next attribute failed!\n"));
2226 ldap_memfree(range_attr);
2227 *more_strings = False;
2232 ldap_memfree(range_attr);
2238 * pull a single uint32 from a ADS result
2239 * @param ads connection to ads server
2240 * @param msg Results of search
2241 * @param field Attribute to retrieve
2242 * @param v Pointer to int to store result
2243 * @return boolean inidicating success
2245 BOOL ads_pull_uint32(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
2250 values = ldap_get_values(ads->ldap.ld, msg, field);
2254 ldap_value_free(values);
2258 *v = atoi(values[0]);
2259 ldap_value_free(values);
2264 * pull a single objectGUID from an ADS result
2265 * @param ads connection to ADS server
2266 * @param msg results of search
2267 * @param guid 37-byte area to receive text guid
2268 * @return boolean indicating success
2270 BOOL ads_pull_guid(ADS_STRUCT *ads, LDAPMessage *msg, struct GUID *guid)
2273 UUID_FLAT flat_guid;
2275 values = ldap_get_values(ads->ldap.ld, msg, "objectGUID");
2280 memcpy(&flat_guid.info, values[0], sizeof(UUID_FLAT));
2281 smb_uuid_unpack(flat_guid, guid);
2282 ldap_value_free(values);
2285 ldap_value_free(values);
2292 * pull a single DOM_SID from a ADS result
2293 * @param ads connection to ads server
2294 * @param msg Results of search
2295 * @param field Attribute to retrieve
2296 * @param sid Pointer to sid to store result
2297 * @return boolean inidicating success
2299 BOOL ads_pull_sid(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
2302 struct berval **values;
2305 values = ldap_get_values_len(ads->ldap.ld, msg, field);
2311 ret = sid_parse(values[0]->bv_val, values[0]->bv_len, sid);
2313 ldap_value_free_len(values);
2318 * pull an array of DOM_SIDs from a ADS result
2319 * @param ads connection to ads server
2320 * @param mem_ctx TALLOC_CTX for allocating sid array
2321 * @param msg Results of search
2322 * @param field Attribute to retrieve
2323 * @param sids pointer to sid array to allocate
2324 * @return the count of SIDs pulled
2326 int ads_pull_sids(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2327 LDAPMessage *msg, const char *field, DOM_SID **sids)
2329 struct berval **values;
2333 values = ldap_get_values_len(ads->ldap.ld, msg, field);
2338 for (i=0; values[i]; i++)
2342 (*sids) = TALLOC_ARRAY(mem_ctx, DOM_SID, i);
2344 ldap_value_free_len(values);
2352 for (i=0; values[i]; i++) {
2353 ret = sid_parse(values[i]->bv_val, values[i]->bv_len, &(*sids)[count]);
2356 DEBUG(10, ("pulling SID: %s\n", sid_to_string(sid, &(*sids)[count])));
2361 ldap_value_free_len(values);
2366 * pull a SEC_DESC from a ADS result
2367 * @param ads connection to ads server
2368 * @param mem_ctx TALLOC_CTX for allocating sid array
2369 * @param msg Results of search
2370 * @param field Attribute to retrieve
2371 * @param sd Pointer to *SEC_DESC to store result (talloc()ed)
2372 * @return boolean inidicating success
2374 BOOL ads_pull_sd(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2375 LDAPMessage *msg, const char *field, SEC_DESC **sd)
2377 struct berval **values;
2380 values = ldap_get_values_len(ads->ldap.ld, msg, field);
2382 if (!values) return False;
2386 prs_init(&ps, values[0]->bv_len, mem_ctx, UNMARSHALL);
2387 prs_copy_data_in(&ps, values[0]->bv_val, values[0]->bv_len);
2388 prs_set_offset(&ps,0);
2390 ret = sec_io_desc("sd", sd, &ps, 1);
2394 ldap_value_free_len(values);
2399 * in order to support usernames longer than 21 characters we need to
2400 * use both the sAMAccountName and the userPrincipalName attributes
2401 * It seems that not all users have the userPrincipalName attribute set
2403 * @param ads connection to ads server
2404 * @param mem_ctx TALLOC_CTX for allocating sid array
2405 * @param msg Results of search
2406 * @return the username
2408 char *ads_pull_username(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2414 /* lookup_name() only works on the sAMAccountName to
2415 returning the username portion of userPrincipalName
2416 breaks winbindd_getpwnam() */
2418 ret = ads_pull_string(ads, mem_ctx, msg, "userPrincipalName");
2419 if (ret && (p = strchr_m(ret, '@'))) {
2424 return ads_pull_string(ads, mem_ctx, msg, "sAMAccountName");
2429 * find the update serial number - this is the core of the ldap cache
2430 * @param ads connection to ads server
2431 * @param ads connection to ADS server
2432 * @param usn Pointer to retrieved update serial number
2433 * @return status of search
2435 ADS_STATUS ads_USN(ADS_STRUCT *ads, uint32 *usn)
2437 const char *attrs[] = {"highestCommittedUSN", NULL};
2441 status = ads_do_search_retry(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2442 if (!ADS_ERR_OK(status))
2445 if (ads_count_replies(ads, res) != 1) {
2446 ads_msgfree(ads, res);
2447 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2450 if (!ads_pull_uint32(ads, res, "highestCommittedUSN", usn)) {
2451 ads_msgfree(ads, res);
2452 return ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
2455 ads_msgfree(ads, res);
2459 /* parse a ADS timestring - typical string is
2460 '20020917091222.0Z0' which means 09:12.22 17th September
2462 static time_t ads_parse_time(const char *str)
2468 if (sscanf(str, "%4d%2d%2d%2d%2d%2d",
2469 &tm.tm_year, &tm.tm_mon, &tm.tm_mday,
2470 &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) {
2479 /********************************************************************
2480 ********************************************************************/
2482 ADS_STATUS ads_current_time(ADS_STRUCT *ads)
2484 const char *attrs[] = {"currentTime", NULL};
2489 ADS_STRUCT *ads_s = ads;
2491 if (!(ctx = talloc_init("ads_current_time"))) {
2492 return ADS_ERROR(LDAP_NO_MEMORY);
2495 /* establish a new ldap tcp session if necessary */
2497 if ( !ads->ldap.ld ) {
2498 if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup,
2499 ads->server.ldap_server )) == NULL )
2503 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
2504 status = ads_connect( ads_s );
2505 if ( !ADS_ERR_OK(status))
2509 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2510 if (!ADS_ERR_OK(status)) {
2514 timestr = ads_pull_string(ads_s, ctx, res, "currentTime");
2516 ads_msgfree(ads_s, res);
2517 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2521 /* but save the time and offset in the original ADS_STRUCT */
2523 ads->config.current_time = ads_parse_time(timestr);
2525 if (ads->config.current_time != 0) {
2526 ads->auth.time_offset = ads->config.current_time - time(NULL);
2527 DEBUG(4,("time offset is %d seconds\n", ads->auth.time_offset));
2530 ads_msgfree(ads, res);
2532 status = ADS_SUCCESS;
2535 /* free any temporary ads connections */
2536 if ( ads_s != ads ) {
2537 ads_destroy( &ads_s );
2539 talloc_destroy(ctx);
2544 /********************************************************************
2545 ********************************************************************/
2547 ADS_STATUS ads_domain_func_level(ADS_STRUCT *ads, uint32 *val)
2549 const char *attrs[] = {"domainFunctionality", NULL};
2552 ADS_STRUCT *ads_s = ads;
2554 *val = DS_DOMAIN_FUNCTION_2000;
2556 /* establish a new ldap tcp session if necessary */
2558 if ( !ads->ldap.ld ) {
2559 if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup,
2560 ads->server.ldap_server )) == NULL )
2564 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
2565 status = ads_connect( ads_s );
2566 if ( !ADS_ERR_OK(status))
2570 /* If the attribute does not exist assume it is a Windows 2000
2571 functional domain */
2573 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2574 if (!ADS_ERR_OK(status)) {
2575 if ( status.err.rc == LDAP_NO_SUCH_ATTRIBUTE ) {
2576 status = ADS_SUCCESS;
2581 if ( !ads_pull_uint32(ads_s, res, "domainFunctionality", val) ) {
2582 DEBUG(5,("ads_domain_func_level: Failed to pull the domainFunctionality attribute.\n"));
2584 DEBUG(3,("ads_domain_func_level: %d\n", *val));
2587 ads_msgfree(ads, res);
2590 /* free any temporary ads connections */
2591 if ( ads_s != ads ) {
2592 ads_destroy( &ads_s );
2599 * find the domain sid for our domain
2600 * @param ads connection to ads server
2601 * @param sid Pointer to domain sid
2602 * @return status of search
2604 ADS_STATUS ads_domain_sid(ADS_STRUCT *ads, DOM_SID *sid)
2606 const char *attrs[] = {"objectSid", NULL};
2610 rc = ads_do_search_retry(ads, ads->config.bind_path, LDAP_SCOPE_BASE, "(objectclass=*)",
2612 if (!ADS_ERR_OK(rc)) return rc;
2613 if (!ads_pull_sid(ads, res, "objectSid", sid)) {
2614 ads_msgfree(ads, res);
2615 return ADS_ERROR_SYSTEM(ENOENT);
2617 ads_msgfree(ads, res);
2623 * find our site name
2624 * @param ads connection to ads server
2625 * @param mem_ctx Pointer to talloc context
2626 * @param site_name Pointer to the sitename
2627 * @return status of search
2629 ADS_STATUS ads_site_dn(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char **site_name)
2633 const char *dn, *service_name;
2634 const char *attrs[] = { "dsServiceName", NULL };
2636 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2637 if (!ADS_ERR_OK(status)) {
2641 service_name = ads_pull_string(ads, mem_ctx, res, "dsServiceName");
2642 if (service_name == NULL) {
2643 ads_msgfree(ads, res);
2644 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2647 ads_msgfree(ads, res);
2649 /* go up three levels */
2650 dn = ads_parent_dn(ads_parent_dn(ads_parent_dn(service_name)));
2652 return ADS_ERROR(LDAP_NO_MEMORY);
2655 *site_name = talloc_strdup(mem_ctx, dn);
2656 if (*site_name == NULL) {
2657 return ADS_ERROR(LDAP_NO_MEMORY);
2662 dsServiceName: CN=NTDS Settings,CN=W2K3DC,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,DC=ber,DC=suse,DC=de
2667 * find the site dn where a machine resides
2668 * @param ads connection to ads server
2669 * @param mem_ctx Pointer to talloc context
2670 * @param computer_name name of the machine
2671 * @param site_name Pointer to the sitename
2672 * @return status of search
2674 ADS_STATUS ads_site_dn_for_machine(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char *computer_name, const char **site_dn)
2678 const char *parent, *filter;
2679 char *config_context = NULL;
2682 /* shortcut a query */
2683 if (strequal(computer_name, ads->config.ldap_server_name)) {
2684 return ads_site_dn(ads, mem_ctx, site_dn);
2687 status = ads_config_path(ads, mem_ctx, &config_context);
2688 if (!ADS_ERR_OK(status)) {
2692 filter = talloc_asprintf(mem_ctx, "(cn=%s)", computer_name);
2693 if (filter == NULL) {
2694 return ADS_ERROR(LDAP_NO_MEMORY);
2697 status = ads_do_search(ads, config_context, LDAP_SCOPE_SUBTREE,
2698 filter, NULL, &res);
2699 if (!ADS_ERR_OK(status)) {
2703 if (ads_count_replies(ads, res) != 1) {
2704 ads_msgfree(ads, res);
2705 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
2708 dn = ads_get_dn(ads, res);
2710 ads_msgfree(ads, res);
2711 return ADS_ERROR(LDAP_NO_MEMORY);
2714 /* go up three levels */
2715 parent = ads_parent_dn(ads_parent_dn(ads_parent_dn(dn)));
2716 if (parent == NULL) {
2717 ads_msgfree(ads, res);
2718 ads_memfree(ads, dn);
2719 return ADS_ERROR(LDAP_NO_MEMORY);
2722 *site_dn = talloc_strdup(mem_ctx, parent);
2723 if (*site_dn == NULL) {
2724 ads_msgfree(ads, res);
2725 ads_memfree(ads, dn);
2726 return ADS_ERROR(LDAP_NO_MEMORY);
2729 ads_memfree(ads, dn);
2730 ads_msgfree(ads, res);
2736 * get the upn suffixes for a domain
2737 * @param ads connection to ads server
2738 * @param mem_ctx Pointer to talloc context
2739 * @param suffixes Pointer to an array of suffixes
2740 * @param num_suffixes Pointer to the number of suffixes
2741 * @return status of search
2743 ADS_STATUS ads_upn_suffixes(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, char ***suffixes, size_t *num_suffixes)
2748 char *config_context = NULL;
2749 const char *attrs[] = { "uPNSuffixes", NULL };
2751 status = ads_config_path(ads, mem_ctx, &config_context);
2752 if (!ADS_ERR_OK(status)) {
2756 base = talloc_asprintf(mem_ctx, "cn=Partitions,%s", config_context);
2758 return ADS_ERROR(LDAP_NO_MEMORY);
2761 status = ads_search_dn(ads, &res, base, attrs);
2762 if (!ADS_ERR_OK(status)) {
2766 if (ads_count_replies(ads, res) != 1) {
2767 ads_msgfree(ads, res);
2768 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
2771 (*suffixes) = ads_pull_strings(ads, mem_ctx, res, "uPNSuffixes", num_suffixes);
2772 if ((*suffixes) == NULL) {
2773 ads_msgfree(ads, res);
2774 return ADS_ERROR(LDAP_NO_MEMORY);
2777 ads_msgfree(ads, res);
2783 * pull a DOM_SID from an extended dn string
2784 * @param mem_ctx TALLOC_CTX
2785 * @param extended_dn string
2786 * @param flags string type of extended_dn
2787 * @param sid pointer to a DOM_SID
2788 * @return boolean inidicating success
2790 BOOL ads_get_sid_from_extended_dn(TALLOC_CTX *mem_ctx,
2791 const char *extended_dn,
2792 enum ads_extended_dn_flags flags,
2801 /* otherwise extended_dn gets stripped off */
2802 if ((dn = talloc_strdup(mem_ctx, extended_dn)) == NULL) {
2806 * ADS_EXTENDED_DN_HEX_STRING:
2807 * <GUID=238e1963cb390f4bb032ba0105525a29>;<SID=010500000000000515000000bb68c8fd6b61b427572eb04556040000>;CN=gd,OU=berlin,OU=suse,DC=ber,DC=suse,DC=de
2809 * ADS_EXTENDED_DN_STRING (only with w2k3):
2810 <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
2813 p = strchr(dn, ';');
2818 if (strncmp(p, ";<SID=", strlen(";<SID=")) != 0) {
2822 p += strlen(";<SID=");
2831 DEBUG(100,("ads_get_sid_from_extended_dn: sid string is %s\n", p));
2835 case ADS_EXTENDED_DN_STRING:
2836 if (!string_to_sid(sid, p)) {
2840 case ADS_EXTENDED_DN_HEX_STRING: {
2844 buf_len = strhex_to_str(buf, strlen(p), p);
2849 if (!sid_parse(buf, buf_len, sid)) {
2850 DEBUG(10,("failed to parse sid\n"));
2856 DEBUG(10,("unknown extended dn format\n"));
2864 * pull an array of DOM_SIDs from a ADS result
2865 * @param ads connection to ads server
2866 * @param mem_ctx TALLOC_CTX for allocating sid array
2867 * @param msg Results of search
2868 * @param field Attribute to retrieve
2869 * @param flags string type of extended_dn
2870 * @param sids pointer to sid array to allocate
2871 * @return the count of SIDs pulled
2873 int ads_pull_sids_from_extendeddn(ADS_STRUCT *ads,
2874 TALLOC_CTX *mem_ctx,
2877 enum ads_extended_dn_flags flags,
2884 if ((dn_strings = ads_pull_strings(ads, mem_ctx, msg, field,
2885 &dn_count)) == NULL) {
2889 (*sids) = TALLOC_ZERO_ARRAY(mem_ctx, DOM_SID, dn_count + 1);
2891 TALLOC_FREE(dn_strings);
2895 for (i=0; i<dn_count; i++) {
2897 if (!ads_get_sid_from_extended_dn(mem_ctx, dn_strings[i],
2898 flags, &(*sids)[i])) {
2900 TALLOC_FREE(dn_strings);
2905 TALLOC_FREE(dn_strings);
2910 /********************************************************************
2911 ********************************************************************/
2913 char* ads_get_dnshostname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
2915 LDAPMessage *res = NULL;
2920 status = ads_find_machine_acct(ads, &res, global_myname());
2921 if (!ADS_ERR_OK(status)) {
2922 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
2927 if ( (count = ads_count_replies(ads, res)) != 1 ) {
2928 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
2932 if ( (name = ads_pull_string(ads, ctx, res, "dNSHostName")) == NULL ) {
2933 DEBUG(0,("ads_get_dnshostname: No dNSHostName attribute!\n"));
2937 ads_msgfree(ads, res);
2942 /********************************************************************
2943 ********************************************************************/
2945 char* ads_get_upn( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
2947 LDAPMessage *res = NULL;
2952 status = ads_find_machine_acct(ads, &res, global_myname());
2953 if (!ADS_ERR_OK(status)) {
2954 DEBUG(0,("ads_get_upn: Failed to find account for %s\n",
2959 if ( (count = ads_count_replies(ads, res)) != 1 ) {
2960 DEBUG(1,("ads_get_upn: %d entries returned!\n", count));
2964 if ( (name = ads_pull_string(ads, ctx, res, "userPrincipalName")) == NULL ) {
2965 DEBUG(2,("ads_get_upn: No userPrincipalName attribute!\n"));
2969 ads_msgfree(ads, res);
2974 /********************************************************************
2975 ********************************************************************/
2977 char* ads_get_samaccountname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
2979 LDAPMessage *res = NULL;
2984 status = ads_find_machine_acct(ads, &res, global_myname());
2985 if (!ADS_ERR_OK(status)) {
2986 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
2991 if ( (count = ads_count_replies(ads, res)) != 1 ) {
2992 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
2996 if ( (name = ads_pull_string(ads, ctx, res, "sAMAccountName")) == NULL ) {
2997 DEBUG(0,("ads_get_dnshostname: No sAMAccountName attribute!\n"));
3001 ads_msgfree(ads, res);
3008 SAVED CODE - we used to join via ldap - remember how we did this. JRA.
3011 * Join a machine to a realm
3012 * Creates the machine account and sets the machine password
3013 * @param ads connection to ads server
3014 * @param machine name of host to add
3015 * @param org_unit Organizational unit to place machine in
3016 * @return status of join
3018 ADS_STATUS ads_join_realm(ADS_STRUCT *ads, const char *machine_name,
3019 uint32 account_type, const char *org_unit)
3022 LDAPMessage *res = NULL;
3025 /* machine name must be lowercase */
3026 machine = SMB_STRDUP(machine_name);
3027 strlower_m(machine);
3030 status = ads_find_machine_acct(ads, (void **)&res, machine);
3031 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
3032 DEBUG(0, ("Host account for %s already exists - deleting old account\n", machine));
3033 status = ads_leave_realm(ads, machine);
3034 if (!ADS_ERR_OK(status)) {
3035 DEBUG(0, ("Failed to delete host '%s' from the '%s' realm.\n",
3036 machine, ads->config.realm));
3041 status = ads_add_machine_acct(ads, machine, account_type, org_unit);
3042 if (!ADS_ERR_OK(status)) {
3043 DEBUG(0, ("ads_join_realm: ads_add_machine_acct failed (%s): %s\n", machine, ads_errstr(status)));
3048 status = ads_find_machine_acct(ads, (void **)(void *)&res, machine);
3049 if (!ADS_ERR_OK(status)) {
3050 DEBUG(0, ("ads_join_realm: Host account test failed for machine %s\n", machine));
3056 ads_msgfree(ads, res);
3063 * Delete a machine from the realm
3064 * @param ads connection to ads server
3065 * @param hostname Machine to remove
3066 * @return status of delete
3068 ADS_STATUS ads_leave_realm(ADS_STRUCT *ads, const char *hostname)
3073 char *hostnameDN, *host;
3075 LDAPControl ldap_control;
3076 LDAPControl * pldap_control[2] = {NULL, NULL};
3078 pldap_control[0] = &ldap_control;
3079 memset(&ldap_control, 0, sizeof(LDAPControl));
3080 ldap_control.ldctl_oid = (char *)LDAP_SERVER_TREE_DELETE_OID;
3082 /* hostname must be lowercase */
3083 host = SMB_STRDUP(hostname);
3086 status = ads_find_machine_acct(ads, &res, host);
3087 if (!ADS_ERR_OK(status)) {
3088 DEBUG(0, ("Host account for %s does not exist.\n", host));
3093 msg = ads_first_entry(ads, res);
3096 return ADS_ERROR_SYSTEM(ENOENT);
3099 hostnameDN = ads_get_dn(ads, (LDAPMessage *)msg);
3101 rc = ldap_delete_ext_s(ads->ldap.ld, hostnameDN, pldap_control, NULL);
3103 DEBUG(3,("ldap_delete_ext_s failed with error code %d\n", rc));
3105 DEBUG(3,("ldap_delete_ext_s succeeded with error code %d\n", rc));
3108 if (rc != LDAP_SUCCESS) {
3109 const char *attrs[] = { "cn", NULL };
3110 LDAPMessage *msg_sub;
3112 /* we only search with scope ONE, we do not expect any further
3113 * objects to be created deeper */
3115 status = ads_do_search_retry(ads, hostnameDN,
3116 LDAP_SCOPE_ONELEVEL,
3117 "(objectclass=*)", attrs, &res);
3119 if (!ADS_ERR_OK(status)) {
3121 ads_memfree(ads, hostnameDN);
3125 for (msg_sub = ads_first_entry(ads, res); msg_sub;
3126 msg_sub = ads_next_entry(ads, msg_sub)) {
3130 if ((dn = ads_get_dn(ads, msg_sub)) == NULL) {
3132 ads_memfree(ads, hostnameDN);
3133 return ADS_ERROR(LDAP_NO_MEMORY);
3136 status = ads_del_dn(ads, dn);
3137 if (!ADS_ERR_OK(status)) {
3138 DEBUG(3,("failed to delete dn %s: %s\n", dn, ads_errstr(status)));
3140 ads_memfree(ads, dn);
3141 ads_memfree(ads, hostnameDN);
3145 ads_memfree(ads, dn);
3148 /* there should be no subordinate objects anymore */
3149 status = ads_do_search_retry(ads, hostnameDN,
3150 LDAP_SCOPE_ONELEVEL,
3151 "(objectclass=*)", attrs, &res);
3153 if (!ADS_ERR_OK(status) || ( (ads_count_replies(ads, res)) > 0 ) ) {
3155 ads_memfree(ads, hostnameDN);
3159 /* delete hostnameDN now */
3160 status = ads_del_dn(ads, hostnameDN);
3161 if (!ADS_ERR_OK(status)) {
3163 DEBUG(3,("failed to delete dn %s: %s\n", hostnameDN, ads_errstr(status)));
3164 ads_memfree(ads, hostnameDN);
3169 ads_memfree(ads, hostnameDN);
3171 status = ads_find_machine_acct(ads, &res, host);
3172 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
3173 DEBUG(3, ("Failed to remove host account.\n"));
3183 * pull all token-sids from an LDAP dn
3184 * @param ads connection to ads server
3185 * @param mem_ctx TALLOC_CTX for allocating sid array
3186 * @param dn of LDAP object
3187 * @param user_sid pointer to DOM_SID (objectSid)
3188 * @param primary_group_sid pointer to DOM_SID (self composed)
3189 * @param sids pointer to sid array to allocate
3190 * @param num_sids counter of SIDs pulled
3191 * @return status of token query
3193 ADS_STATUS ads_get_tokensids(ADS_STRUCT *ads,
3194 TALLOC_CTX *mem_ctx,
3197 DOM_SID *primary_group_sid,
3202 LDAPMessage *res = NULL;
3204 size_t tmp_num_sids;
3206 DOM_SID tmp_user_sid;
3207 DOM_SID tmp_primary_group_sid;
3209 const char *attrs[] = {
3216 status = ads_search_retry_dn(ads, &res, dn, attrs);
3217 if (!ADS_ERR_OK(status)) {
3221 count = ads_count_replies(ads, res);
3223 ads_msgfree(ads, res);
3224 return ADS_ERROR_LDAP(LDAP_NO_SUCH_OBJECT);
3227 if (!ads_pull_sid(ads, res, "objectSid", &tmp_user_sid)) {
3228 ads_msgfree(ads, res);
3229 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3232 if (!ads_pull_uint32(ads, res, "primaryGroupID", &pgid)) {
3233 ads_msgfree(ads, res);
3234 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3238 /* hack to compose the primary group sid without knowing the
3244 sid_copy(&domsid, &tmp_user_sid);
3246 if (!sid_split_rid(&domsid, &dummy_rid)) {
3247 ads_msgfree(ads, res);
3248 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3251 if (!sid_compose(&tmp_primary_group_sid, &domsid, pgid)) {
3252 ads_msgfree(ads, res);
3253 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3257 tmp_num_sids = ads_pull_sids(ads, mem_ctx, res, "tokenGroups", &tmp_sids);
3259 if (tmp_num_sids == 0 || !tmp_sids) {
3260 ads_msgfree(ads, res);
3261 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3265 *num_sids = tmp_num_sids;
3273 *user_sid = tmp_user_sid;
3276 if (primary_group_sid) {
3277 *primary_group_sid = tmp_primary_group_sid;
3280 DEBUG(10,("ads_get_tokensids: returned %d sids\n", (int)tmp_num_sids + 2));
3282 ads_msgfree(ads, res);
3283 return ADS_ERROR_LDAP(LDAP_SUCCESS);
3287 * Find a sAMAccoutName in LDAP
3288 * @param ads connection to ads server
3289 * @param mem_ctx TALLOC_CTX for allocating sid array
3290 * @param samaccountname to search
3291 * @param uac_ret uint32 pointer userAccountControl attribute value
3292 * @param dn_ret pointer to dn
3293 * @return status of token query
3295 ADS_STATUS ads_find_samaccount(ADS_STRUCT *ads,
3296 TALLOC_CTX *mem_ctx,
3297 const char *samaccountname,
3299 const char **dn_ret)
3302 const char *attrs[] = { "userAccountControl", NULL };
3304 LDAPMessage *res = NULL;
3308 filter = talloc_asprintf(mem_ctx, "(&(objectclass=user)(sAMAccountName=%s))",
3310 if (filter == NULL) {
3314 status = ads_do_search_all(ads, ads->config.bind_path,
3316 filter, attrs, &res);
3318 if (!ADS_ERR_OK(status)) {
3322 if (ads_count_replies(ads, res) != 1) {
3323 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3327 dn = ads_get_dn(ads, res);
3329 status = ADS_ERROR(LDAP_NO_MEMORY);
3333 if (!ads_pull_uint32(ads, res, "userAccountControl", &uac)) {
3334 status = ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
3343 *dn_ret = talloc_strdup(mem_ctx, dn);
3345 status = ADS_ERROR(LDAP_NO_MEMORY);
3350 ads_memfree(ads, dn);
3351 ads_msgfree(ads, res);
3357 * find our configuration path
3358 * @param ads connection to ads server
3359 * @param mem_ctx Pointer to talloc context
3360 * @param config_path Pointer to the config path
3361 * @return status of search
3363 ADS_STATUS ads_config_path(ADS_STRUCT *ads,
3364 TALLOC_CTX *mem_ctx,
3368 LDAPMessage *res = NULL;
3369 const char *config_context = NULL;
3370 const char *attrs[] = { "configurationNamingContext", NULL };
3372 status = ads_do_search(ads, "", LDAP_SCOPE_BASE,
3373 "(objectclass=*)", attrs, &res);
3374 if (!ADS_ERR_OK(status)) {
3378 config_context = ads_pull_string(ads, mem_ctx, res,
3379 "configurationNamingContext");
3380 ads_msgfree(ads, res);
3381 if (!config_context) {
3382 return ADS_ERROR(LDAP_NO_MEMORY);
3386 *config_path = talloc_strdup(mem_ctx, config_context);
3387 if (!*config_path) {
3388 return ADS_ERROR(LDAP_NO_MEMORY);
3392 return ADS_ERROR(LDAP_SUCCESS);
3396 * find the displayName of an extended right
3397 * @param ads connection to ads server
3398 * @param config_path The config path
3399 * @param mem_ctx Pointer to talloc context
3400 * @param GUID struct of the rightsGUID
3401 * @return status of search
3403 const char *ads_get_extended_right_name_by_guid(ADS_STRUCT *ads,
3404 const char *config_path,
3405 TALLOC_CTX *mem_ctx,
3406 const struct GUID *rights_guid)
3409 LDAPMessage *res = NULL;
3411 const char *attrs[] = { "displayName", NULL };
3412 const char *result = NULL;
3415 if (!ads || !mem_ctx || !rights_guid) {
3419 expr = talloc_asprintf(mem_ctx, "(rightsGuid=%s)",
3420 smb_uuid_string_static(*rights_guid));
3425 path = talloc_asprintf(mem_ctx, "cn=Extended-Rights,%s", config_path);
3430 rc = ads_do_search_retry(ads, path, LDAP_SCOPE_SUBTREE,
3432 if (!ADS_ERR_OK(rc)) {
3436 if (ads_count_replies(ads, res) != 1) {
3440 result = ads_pull_string(ads, mem_ctx, res, "displayName");
3443 ads_msgfree(ads, res);