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/>.
25 #include "lib/ldb/include/includes.h"
31 * @brief basic ldap client-side routines for ads server communications
33 * The routines contained here should do the necessary ldap calls for
36 * Important note: attribute names passed into ads_ routines must
37 * already be in UTF-8 format. We do not convert them because in almost
38 * all cases, they are just ascii (which is represented with the same
39 * codepoints in UTF-8). This may have to change at some point
43 #define LDAP_SERVER_TREE_DELETE_OID "1.2.840.113556.1.4.805"
45 static SIG_ATOMIC_T gotalarm
;
47 /***************************************************************
48 Signal function to tell us we timed out.
49 ****************************************************************/
51 static void gotalarm_sig(void)
56 LDAP
*ldap_open_with_timeout(const char *server
, int port
, unsigned int to
)
61 DEBUG(10, ("Opening connection to LDAP server '%s:%d', timeout "
62 "%u seconds\n", server
, port
, to
));
66 CatchSignal(SIGALRM
, SIGNAL_CAST gotalarm_sig
);
68 /* End setup timeout. */
70 ldp
= ldap_open(server
, port
);
73 DEBUG(2,("Could not open connection to LDAP server %s:%d: %s\n",
74 server
, port
, strerror(errno
)));
76 DEBUG(10, ("Connected to LDAP server '%s:%d'\n", server
, port
));
79 /* Teardown timeout. */
80 CatchSignal(SIGALRM
, SIGNAL_CAST SIG_IGN
);
86 static int ldap_search_with_timeout(LDAP
*ld
,
87 LDAP_CONST
char *base
,
89 LDAP_CONST
char *filter
,
97 struct timeval timeout
;
100 /* Setup timeout for the ldap_search_ext_s call - local and remote. */
101 timeout
.tv_sec
= lp_ldap_timeout();
104 /* Setup alarm timeout.... Do we need both of these ? JRA. */
106 CatchSignal(SIGALRM
, SIGNAL_CAST gotalarm_sig
);
107 alarm(lp_ldap_timeout());
108 /* End setup timeout. */
110 result
= ldap_search_ext_s(ld
, base
, scope
, filter
, attrs
,
111 attrsonly
, sctrls
, cctrls
, &timeout
,
114 /* Teardown timeout. */
115 CatchSignal(SIGALRM
, SIGNAL_CAST SIG_IGN
);
119 return LDAP_TIMELIMIT_EXCEEDED
;
124 /**********************************************
125 Do client and server sitename match ?
126 **********************************************/
128 bool ads_sitename_match(ADS_STRUCT
*ads
)
130 if (ads
->config
.server_site_name
== NULL
&&
131 ads
->config
.client_site_name
== NULL
) {
132 DEBUG(10,("ads_sitename_match: both null\n"));
135 if (ads
->config
.server_site_name
&&
136 ads
->config
.client_site_name
&&
137 strequal(ads
->config
.server_site_name
,
138 ads
->config
.client_site_name
)) {
139 DEBUG(10,("ads_sitename_match: name %s match\n", ads
->config
.server_site_name
));
142 DEBUG(10,("ads_sitename_match: no match between server: %s and client: %s\n",
143 ads
->config
.server_site_name
? ads
->config
.server_site_name
: "NULL",
144 ads
->config
.client_site_name
? ads
->config
.client_site_name
: "NULL"));
148 /**********************************************
149 Is this the closest DC ?
150 **********************************************/
152 bool ads_closest_dc(ADS_STRUCT
*ads
)
154 if (ads
->config
.flags
& ADS_CLOSEST
) {
155 DEBUG(10,("ads_closest_dc: ADS_CLOSEST flag set\n"));
159 /* not sure if this can ever happen */
160 if (ads_sitename_match(ads
)) {
161 DEBUG(10,("ads_closest_dc: ADS_CLOSEST flag not set but sites match\n"));
165 DEBUG(10,("ads_closest_dc: %s is not the closest DC\n",
166 ads
->config
.ldap_server_name
));
173 try a connection to a given ldap server, returning True and setting the servers IP
174 in the ads struct if successful
176 bool ads_try_connect(ADS_STRUCT
*ads
, const char *server
)
179 struct cldap_netlogon_reply cldap_reply
;
181 if (!server
|| !*server
) {
185 DEBUG(5,("ads_try_connect: sending CLDAP request to %s (realm: %s)\n",
186 server
, ads
->server
.realm
));
188 /* this copes with inet_ntoa brokenness */
190 srv
= SMB_STRDUP(server
);
192 ZERO_STRUCT( cldap_reply
);
194 if ( !ads_cldap_netlogon( srv
, ads
->server
.realm
, &cldap_reply
) ) {
195 DEBUG(3,("ads_try_connect: CLDAP request %s failed.\n", srv
));
200 /* Check the CLDAP reply flags */
202 if ( !(cldap_reply
.flags
& ADS_LDAP
) ) {
203 DEBUG(1,("ads_try_connect: %s's CLDAP reply says it is not an LDAP server!\n",
209 /* Fill in the ads->config values */
211 SAFE_FREE(ads
->config
.realm
);
212 SAFE_FREE(ads
->config
.bind_path
);
213 SAFE_FREE(ads
->config
.ldap_server_name
);
214 SAFE_FREE(ads
->config
.server_site_name
);
215 SAFE_FREE(ads
->config
.client_site_name
);
216 SAFE_FREE(ads
->server
.workgroup
);
218 ads
->config
.flags
= cldap_reply
.flags
;
219 ads
->config
.ldap_server_name
= SMB_STRDUP(cldap_reply
.hostname
);
220 strupper_m(cldap_reply
.domain
);
221 ads
->config
.realm
= SMB_STRDUP(cldap_reply
.domain
);
222 ads
->config
.bind_path
= ads_build_dn(ads
->config
.realm
);
223 if (*cldap_reply
.server_site_name
) {
224 ads
->config
.server_site_name
=
225 SMB_STRDUP(cldap_reply
.server_site_name
);
227 if (*cldap_reply
.client_site_name
) {
228 ads
->config
.client_site_name
=
229 SMB_STRDUP(cldap_reply
.client_site_name
);
231 ads
->server
.workgroup
= SMB_STRDUP(cldap_reply
.netbios_domain
);
233 ads
->ldap
.port
= LDAP_PORT
;
234 if (!interpret_string_addr(&ads
->ldap
.ss
, srv
, 0)) {
235 DEBUG(1,("ads_try_connect: unable to convert %s "
244 /* Store our site name. */
245 sitename_store( cldap_reply
.domain
, cldap_reply
.client_site_name
);
250 /**********************************************************************
251 Try to find an AD dc using our internal name resolution routines
252 Try the realm first and then then workgroup name if netbios is not
254 **********************************************************************/
256 static NTSTATUS
ads_find_dc(ADS_STRUCT
*ads
)
260 struct ip_service
*ip_list
;
262 bool got_realm
= False
;
263 bool use_own_domain
= False
;
265 NTSTATUS status
= NT_STATUS_UNSUCCESSFUL
;
267 /* if the realm and workgroup are both empty, assume they are ours */
270 c_realm
= ads
->server
.realm
;
272 if ( !c_realm
|| !*c_realm
) {
273 /* special case where no realm and no workgroup means our own */
274 if ( !ads
->server
.workgroup
|| !*ads
->server
.workgroup
) {
275 use_own_domain
= True
;
276 c_realm
= lp_realm();
280 if (c_realm
&& *c_realm
)
283 /* we need to try once with the realm name and fallback to the
284 netbios domain name if we fail (if netbios has not been disabled */
286 if ( !got_realm
&& !lp_disable_netbios() ) {
287 c_realm
= ads
->server
.workgroup
;
288 if (!c_realm
|| !*c_realm
) {
289 if ( use_own_domain
)
290 c_realm
= lp_workgroup();
293 if ( !c_realm
|| !*c_realm
) {
294 DEBUG(0,("ads_find_dc: no realm or workgroup! Don't know what to do\n"));
295 return NT_STATUS_INVALID_PARAMETER
; /* rather need MISSING_PARAMETER ... */
301 sitename
= sitename_fetch(realm
);
305 DEBUG(6,("ads_find_dc: looking for %s '%s'\n",
306 (got_realm
? "realm" : "domain"), realm
));
308 status
= get_sorted_dc_list(realm
, sitename
, &ip_list
, &count
, got_realm
);
309 if (!NT_STATUS_IS_OK(status
)) {
310 /* fall back to netbios if we can */
311 if ( got_realm
&& !lp_disable_netbios() ) {
320 /* if we fail this loop, then giveup since all the IP addresses returned were dead */
321 for ( i
=0; i
<count
; i
++ ) {
322 char server
[INET6_ADDRSTRLEN
];
324 print_sockaddr(server
, sizeof(server
), &ip_list
[i
].ss
);
326 if ( !NT_STATUS_IS_OK(check_negative_conn_cache(realm
, server
)) )
330 /* realm in this case is a workgroup name. We need
331 to ignore any IP addresses in the negative connection
332 cache that match ip addresses returned in the ad realm
333 case. It sucks that I have to reproduce the logic above... */
334 c_realm
= ads
->server
.realm
;
335 if ( !c_realm
|| !*c_realm
) {
336 if ( !ads
->server
.workgroup
|| !*ads
->server
.workgroup
) {
337 c_realm
= lp_realm();
340 if (c_realm
&& *c_realm
&&
341 !NT_STATUS_IS_OK(check_negative_conn_cache(c_realm
, server
))) {
342 /* Ensure we add the workgroup name for this
343 IP address as negative too. */
344 add_failed_connection_entry( realm
, server
, NT_STATUS_UNSUCCESSFUL
);
349 if ( ads_try_connect(ads
, server
) ) {
355 /* keep track of failures */
356 add_failed_connection_entry( realm
, server
, NT_STATUS_UNSUCCESSFUL
);
361 /* In case we failed to contact one of our closest DC on our site we
362 * need to try to find another DC, retry with a site-less SRV DNS query
366 DEBUG(1,("ads_find_dc: failed to find a valid DC on our site (%s), "
367 "trying to find another DC\n", sitename
));
369 namecache_delete(realm
, 0x1C);
373 return NT_STATUS_NO_LOGON_SERVERS
;
378 * Connect to the LDAP server
379 * @param ads Pointer to an existing ADS_STRUCT
380 * @return status of connection
382 ADS_STATUS
ads_connect(ADS_STRUCT
*ads
)
384 int version
= LDAP_VERSION3
;
387 char addr
[INET6_ADDRSTRLEN
];
389 ZERO_STRUCT(ads
->ldap
);
390 ads
->ldap
.last_attempt
= time(NULL
);
391 ads
->ldap
.wrap_type
= ADS_SASLWRAP_TYPE_PLAIN
;
393 /* try with a user specified server */
395 if (DEBUGLEVEL
>= 11) {
396 char *s
= NDR_PRINT_STRUCT_STRING(talloc_tos(), ads_struct
, ads
);
397 DEBUG(11,("ads_connect: entering\n"));
398 DEBUGADD(11,("%s\n", s
));
402 if (ads
->server
.ldap_server
&&
403 ads_try_connect(ads
, ads
->server
.ldap_server
)) {
407 ntstatus
= ads_find_dc(ads
);
408 if (NT_STATUS_IS_OK(ntstatus
)) {
412 status
= ADS_ERROR_NT(ntstatus
);
417 print_sockaddr(addr
, sizeof(addr
), &ads
->ldap
.ss
);
418 DEBUG(3,("Successfully contacted LDAP server %s\n", addr
));
420 if (!ads
->auth
.user_name
) {
421 /* Must use the userPrincipalName value here or sAMAccountName
422 and not servicePrincipalName; found by Guenther Deschner */
424 asprintf(&ads
->auth
.user_name
, "%s$", global_myname() );
427 if (!ads
->auth
.realm
) {
428 ads
->auth
.realm
= SMB_STRDUP(ads
->config
.realm
);
431 if (!ads
->auth
.kdc_server
) {
432 print_sockaddr(addr
, sizeof(addr
), &ads
->ldap
.ss
);
433 ads
->auth
.kdc_server
= SMB_STRDUP(addr
);
437 /* this is a really nasty hack to avoid ADS DNS problems. It needs a patch
438 to MIT kerberos to work (tridge) */
441 asprintf(&env
, "KRB5_KDC_ADDRESS_%s", ads
->config
.realm
);
442 setenv(env
, ads
->auth
.kdc_server
, 1);
447 /* If the caller() requested no LDAP bind, then we are done */
449 if (ads
->auth
.flags
& ADS_AUTH_NO_BIND
) {
450 status
= ADS_SUCCESS
;
454 ads
->ldap
.mem_ctx
= talloc_init("ads LDAP connection memory");
455 if (!ads
->ldap
.mem_ctx
) {
456 status
= ADS_ERROR_NT(NT_STATUS_NO_MEMORY
);
460 /* Otherwise setup the TCP LDAP session */
462 ads
->ldap
.ld
= ldap_open_with_timeout(ads
->config
.ldap_server_name
,
463 LDAP_PORT
, lp_ldap_timeout());
464 if (ads
->ldap
.ld
== NULL
) {
465 status
= ADS_ERROR(LDAP_OPERATIONS_ERROR
);
468 DEBUG(3,("Connected to LDAP server %s\n", ads
->config
.ldap_server_name
));
470 /* cache the successful connection for workgroup and realm */
471 if (ads_closest_dc(ads
)) {
472 print_sockaddr(addr
, sizeof(addr
), &ads
->ldap
.ss
);
473 saf_store( ads
->server
.workgroup
, addr
);
474 saf_store( ads
->server
.realm
, addr
);
477 ldap_set_option(ads
->ldap
.ld
, LDAP_OPT_PROTOCOL_VERSION
, &version
);
479 status
= ADS_ERROR(smb_ldap_start_tls(ads
->ldap
.ld
, version
));
480 if (!ADS_ERR_OK(status
)) {
484 /* fill in the current time and offsets */
486 status
= ads_current_time( ads
);
487 if ( !ADS_ERR_OK(status
) ) {
491 /* Now do the bind */
493 if (ads
->auth
.flags
& ADS_AUTH_ANON_BIND
) {
494 status
= ADS_ERROR(ldap_simple_bind_s(ads
->ldap
.ld
, NULL
, NULL
));
498 if (ads
->auth
.flags
& ADS_AUTH_SIMPLE_BIND
) {
499 status
= ADS_ERROR(ldap_simple_bind_s(ads
->ldap
.ld
, ads
->auth
.user_name
, ads
->auth
.password
));
503 status
= ads_sasl_bind(ads
);
506 if (DEBUGLEVEL
>= 11) {
507 char *s
= NDR_PRINT_STRUCT_STRING(talloc_tos(), ads_struct
, ads
);
508 DEBUG(11,("ads_connect: leaving with: %s\n",
509 ads_errstr(status
)));
510 DEBUGADD(11,("%s\n", s
));
518 * Disconnect the LDAP server
519 * @param ads Pointer to an existing ADS_STRUCT
521 void ads_disconnect(ADS_STRUCT
*ads
)
524 ldap_unbind(ads
->ldap
.ld
);
527 if (ads
->ldap
.wrap_ops
&& ads
->ldap
.wrap_ops
->disconnect
) {
528 ads
->ldap
.wrap_ops
->disconnect(ads
);
530 if (ads
->ldap
.mem_ctx
) {
531 talloc_free(ads
->ldap
.mem_ctx
);
533 ZERO_STRUCT(ads
->ldap
);
537 Duplicate a struct berval into talloc'ed memory
539 static struct berval
*dup_berval(TALLOC_CTX
*ctx
, const struct berval
*in_val
)
541 struct berval
*value
;
543 if (!in_val
) return NULL
;
545 value
= TALLOC_ZERO_P(ctx
, struct berval
);
548 if (in_val
->bv_len
== 0) return value
;
550 value
->bv_len
= in_val
->bv_len
;
551 value
->bv_val
= (char *)TALLOC_MEMDUP(ctx
, in_val
->bv_val
,
557 Make a values list out of an array of (struct berval *)
559 static struct berval
**ads_dup_values(TALLOC_CTX
*ctx
,
560 const struct berval
**in_vals
)
562 struct berval
**values
;
565 if (!in_vals
) return NULL
;
566 for (i
=0; in_vals
[i
]; i
++)
568 values
= TALLOC_ZERO_ARRAY(ctx
, struct berval
*, i
+1);
569 if (!values
) return NULL
;
571 for (i
=0; in_vals
[i
]; i
++) {
572 values
[i
] = dup_berval(ctx
, in_vals
[i
]);
578 UTF8-encode a values list out of an array of (char *)
580 static char **ads_push_strvals(TALLOC_CTX
*ctx
, const char **in_vals
)
585 if (!in_vals
) return NULL
;
586 for (i
=0; in_vals
[i
]; i
++)
588 values
= TALLOC_ZERO_ARRAY(ctx
, char *, i
+1);
589 if (!values
) return NULL
;
591 for (i
=0; in_vals
[i
]; i
++) {
592 if (push_utf8_talloc(ctx
, &values
[i
], in_vals
[i
]) == (size_t) -1) {
601 Pull a (char *) array out of a UTF8-encoded values list
603 static char **ads_pull_strvals(TALLOC_CTX
*ctx
, const char **in_vals
)
608 if (!in_vals
) return NULL
;
609 for (i
=0; in_vals
[i
]; i
++)
611 values
= TALLOC_ZERO_ARRAY(ctx
, char *, i
+1);
612 if (!values
) return NULL
;
614 for (i
=0; in_vals
[i
]; i
++) {
615 pull_utf8_talloc(ctx
, &values
[i
], in_vals
[i
]);
621 * Do a search with paged results. cookie must be null on the first
622 * call, and then returned on each subsequent call. It will be null
623 * again when the entire search is complete
624 * @param ads connection to ads server
625 * @param bind_path Base dn for the search
626 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
627 * @param expr Search expression - specified in local charset
628 * @param attrs Attributes to retrieve - specified in utf8 or ascii
629 * @param res ** which will contain results - free res* with ads_msgfree()
630 * @param count Number of entries retrieved on this page
631 * @param cookie The paged results cookie to be returned on subsequent calls
632 * @return status of search
634 static ADS_STATUS
ads_do_paged_search_args(ADS_STRUCT
*ads
,
635 const char *bind_path
,
636 int scope
, const char *expr
,
637 const char **attrs
, void *args
,
639 int *count
, struct berval
**cookie
)
642 char *utf8_expr
, *utf8_path
, **search_attrs
;
643 LDAPControl PagedResults
, NoReferrals
, ExternalCtrl
, *controls
[4], **rcontrols
;
644 BerElement
*cookie_be
= NULL
;
645 struct berval
*cookie_bv
= NULL
;
646 BerElement
*ext_be
= NULL
;
647 struct berval
*ext_bv
= NULL
;
650 ads_control
*external_control
= (ads_control
*) args
;
654 if (!(ctx
= talloc_init("ads_do_paged_search_args")))
655 return ADS_ERROR(LDAP_NO_MEMORY
);
657 /* 0 means the conversion worked but the result was empty
658 so we only fail if it's -1. In any case, it always
659 at least nulls out the dest */
660 if ((push_utf8_talloc(ctx
, &utf8_expr
, expr
) == (size_t)-1) ||
661 (push_utf8_talloc(ctx
, &utf8_path
, bind_path
) == (size_t)-1)) {
666 if (!attrs
|| !(*attrs
))
669 /* This would be the utf8-encoded version...*/
670 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
671 if (!(str_list_copy(talloc_tos(), &search_attrs
, attrs
))) {
677 /* Paged results only available on ldap v3 or later */
678 ldap_get_option(ads
->ldap
.ld
, LDAP_OPT_PROTOCOL_VERSION
, &version
);
679 if (version
< LDAP_VERSION3
) {
680 rc
= LDAP_NOT_SUPPORTED
;
684 cookie_be
= ber_alloc_t(LBER_USE_DER
);
686 ber_printf(cookie_be
, "{iO}", (ber_int_t
) 1000, *cookie
);
687 ber_bvfree(*cookie
); /* don't need it from last time */
690 ber_printf(cookie_be
, "{io}", (ber_int_t
) 1000, "", 0);
692 ber_flatten(cookie_be
, &cookie_bv
);
693 PagedResults
.ldctl_oid
= CONST_DISCARD(char *, ADS_PAGE_CTL_OID
);
694 PagedResults
.ldctl_iscritical
= (char) 1;
695 PagedResults
.ldctl_value
.bv_len
= cookie_bv
->bv_len
;
696 PagedResults
.ldctl_value
.bv_val
= cookie_bv
->bv_val
;
698 NoReferrals
.ldctl_oid
= CONST_DISCARD(char *, ADS_NO_REFERRALS_OID
);
699 NoReferrals
.ldctl_iscritical
= (char) 0;
700 NoReferrals
.ldctl_value
.bv_len
= 0;
701 NoReferrals
.ldctl_value
.bv_val
= CONST_DISCARD(char *, "");
703 if (external_control
&&
704 (strequal(external_control
->control
, ADS_EXTENDED_DN_OID
) ||
705 strequal(external_control
->control
, ADS_SD_FLAGS_OID
))) {
707 ExternalCtrl
.ldctl_oid
= CONST_DISCARD(char *, external_control
->control
);
708 ExternalCtrl
.ldctl_iscritical
= (char) external_control
->critical
;
710 /* win2k does not accept a ldctl_value beeing passed in */
712 if (external_control
->val
!= 0) {
714 if ((ext_be
= ber_alloc_t(LBER_USE_DER
)) == NULL
) {
719 if ((ber_printf(ext_be
, "{i}", (ber_int_t
) external_control
->val
)) == -1) {
723 if ((ber_flatten(ext_be
, &ext_bv
)) == -1) {
728 ExternalCtrl
.ldctl_value
.bv_len
= ext_bv
->bv_len
;
729 ExternalCtrl
.ldctl_value
.bv_val
= ext_bv
->bv_val
;
732 ExternalCtrl
.ldctl_value
.bv_len
= 0;
733 ExternalCtrl
.ldctl_value
.bv_val
= NULL
;
736 controls
[0] = &NoReferrals
;
737 controls
[1] = &PagedResults
;
738 controls
[2] = &ExternalCtrl
;
742 controls
[0] = &NoReferrals
;
743 controls
[1] = &PagedResults
;
747 /* we need to disable referrals as the openldap libs don't
748 handle them and paged results at the same time. Using them
749 together results in the result record containing the server
750 page control being removed from the result list (tridge/jmcd)
752 leaving this in despite the control that says don't generate
753 referrals, in case the server doesn't support it (jmcd)
755 ldap_set_option(ads
->ldap
.ld
, LDAP_OPT_REFERRALS
, LDAP_OPT_OFF
);
757 rc
= ldap_search_with_timeout(ads
->ldap
.ld
, utf8_path
, scope
, utf8_expr
,
758 search_attrs
, 0, controls
,
760 (LDAPMessage
**)res
);
762 ber_free(cookie_be
, 1);
763 ber_bvfree(cookie_bv
);
766 DEBUG(3,("ads_do_paged_search_args: ldap_search_with_timeout(%s) -> %s\n", expr
,
767 ldap_err2string(rc
)));
771 rc
= ldap_parse_result(ads
->ldap
.ld
, *res
, NULL
, NULL
, NULL
,
772 NULL
, &rcontrols
, 0);
778 for (i
=0; rcontrols
[i
]; i
++) {
779 if (strcmp(ADS_PAGE_CTL_OID
, rcontrols
[i
]->ldctl_oid
) == 0) {
780 cookie_be
= ber_init(&rcontrols
[i
]->ldctl_value
);
781 ber_scanf(cookie_be
,"{iO}", (ber_int_t
*) count
,
783 /* the berval is the cookie, but must be freed when
785 if (cookie_bv
->bv_len
) /* still more to do */
786 *cookie
=ber_bvdup(cookie_bv
);
789 ber_bvfree(cookie_bv
);
790 ber_free(cookie_be
, 1);
794 ldap_controls_free(rcontrols
);
807 /* if/when we decide to utf8-encode attrs, take out this next line */
808 TALLOC_FREE(search_attrs
);
810 return ADS_ERROR(rc
);
813 static ADS_STATUS
ads_do_paged_search(ADS_STRUCT
*ads
, const char *bind_path
,
814 int scope
, const char *expr
,
815 const char **attrs
, LDAPMessage
**res
,
816 int *count
, struct berval
**cookie
)
818 return ads_do_paged_search_args(ads
, bind_path
, scope
, expr
, attrs
, NULL
, res
, count
, cookie
);
823 * Get all results for a search. This uses ads_do_paged_search() to return
824 * all entries in a large search.
825 * @param ads connection to ads server
826 * @param bind_path Base dn for the search
827 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
828 * @param expr Search expression
829 * @param attrs Attributes to retrieve
830 * @param res ** which will contain results - free res* with ads_msgfree()
831 * @return status of search
833 ADS_STATUS
ads_do_search_all_args(ADS_STRUCT
*ads
, const char *bind_path
,
834 int scope
, const char *expr
,
835 const char **attrs
, void *args
,
838 struct berval
*cookie
= NULL
;
843 status
= ads_do_paged_search_args(ads
, bind_path
, scope
, expr
, attrs
, args
, res
,
846 if (!ADS_ERR_OK(status
))
849 #ifdef HAVE_LDAP_ADD_RESULT_ENTRY
851 LDAPMessage
*res2
= NULL
;
853 LDAPMessage
*msg
, *next
;
855 status2
= ads_do_paged_search_args(ads
, bind_path
, scope
, expr
,
856 attrs
, args
, &res2
, &count
, &cookie
);
858 if (!ADS_ERR_OK(status2
)) break;
860 /* this relies on the way that ldap_add_result_entry() works internally. I hope
861 that this works on all ldap libs, but I have only tested with openldap */
862 for (msg
= ads_first_entry(ads
, res2
); msg
; msg
= next
) {
863 next
= ads_next_entry(ads
, msg
);
864 ldap_add_result_entry((LDAPMessage
**)res
, msg
);
866 /* note that we do not free res2, as the memory is now
867 part of the main returned list */
870 DEBUG(0, ("no ldap_add_result_entry() support in LDAP libs!\n"));
871 status
= ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL
);
877 ADS_STATUS
ads_do_search_all(ADS_STRUCT
*ads
, const char *bind_path
,
878 int scope
, const char *expr
,
879 const char **attrs
, LDAPMessage
**res
)
881 return ads_do_search_all_args(ads
, bind_path
, scope
, expr
, attrs
, NULL
, res
);
884 ADS_STATUS
ads_do_search_all_sd_flags(ADS_STRUCT
*ads
, const char *bind_path
,
885 int scope
, const char *expr
,
886 const char **attrs
, uint32 sd_flags
,
891 args
.control
= ADS_SD_FLAGS_OID
;
893 args
.critical
= True
;
895 return ads_do_search_all_args(ads
, bind_path
, scope
, expr
, attrs
, &args
, res
);
900 * Run a function on all results for a search. Uses ads_do_paged_search() and
901 * runs the function as each page is returned, using ads_process_results()
902 * @param ads connection to ads server
903 * @param bind_path Base dn for the search
904 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
905 * @param expr Search expression - specified in local charset
906 * @param attrs Attributes to retrieve - specified in UTF-8 or ascii
907 * @param fn Function which takes attr name, values list, and data_area
908 * @param data_area Pointer which is passed to function on each call
909 * @return status of search
911 ADS_STATUS
ads_do_search_all_fn(ADS_STRUCT
*ads
, const char *bind_path
,
912 int scope
, const char *expr
, const char **attrs
,
913 bool (*fn
)(ADS_STRUCT
*, char *, void **, void *),
916 struct berval
*cookie
= NULL
;
921 status
= ads_do_paged_search(ads
, bind_path
, scope
, expr
, attrs
, &res
,
924 if (!ADS_ERR_OK(status
)) return status
;
926 ads_process_results(ads
, res
, fn
, data_area
);
927 ads_msgfree(ads
, res
);
930 status
= ads_do_paged_search(ads
, bind_path
, scope
, expr
, attrs
,
931 &res
, &count
, &cookie
);
933 if (!ADS_ERR_OK(status
)) break;
935 ads_process_results(ads
, res
, fn
, data_area
);
936 ads_msgfree(ads
, res
);
943 * Do a search with a timeout.
944 * @param ads connection to ads server
945 * @param bind_path Base dn for the search
946 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
947 * @param expr Search expression
948 * @param attrs Attributes to retrieve
949 * @param res ** which will contain results - free res* with ads_msgfree()
950 * @return status of search
952 ADS_STATUS
ads_do_search(ADS_STRUCT
*ads
, const char *bind_path
, int scope
,
954 const char **attrs
, LDAPMessage
**res
)
957 char *utf8_expr
, *utf8_path
, **search_attrs
= NULL
;
961 if (!(ctx
= talloc_init("ads_do_search"))) {
962 DEBUG(1,("ads_do_search: talloc_init() failed!"));
963 return ADS_ERROR(LDAP_NO_MEMORY
);
966 /* 0 means the conversion worked but the result was empty
967 so we only fail if it's negative. In any case, it always
968 at least nulls out the dest */
969 if ((push_utf8_talloc(ctx
, &utf8_expr
, expr
) == (size_t)-1) ||
970 (push_utf8_talloc(ctx
, &utf8_path
, bind_path
) == (size_t)-1)) {
971 DEBUG(1,("ads_do_search: push_utf8_talloc() failed!"));
976 if (!attrs
|| !(*attrs
))
979 /* This would be the utf8-encoded version...*/
980 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
981 if (!(str_list_copy(talloc_tos(), &search_attrs
, attrs
)))
983 DEBUG(1,("ads_do_search: str_list_copy() failed!"));
989 /* see the note in ads_do_paged_search - we *must* disable referrals */
990 ldap_set_option(ads
->ldap
.ld
, LDAP_OPT_REFERRALS
, LDAP_OPT_OFF
);
992 rc
= ldap_search_with_timeout(ads
->ldap
.ld
, utf8_path
, scope
, utf8_expr
,
993 search_attrs
, 0, NULL
, NULL
,
995 (LDAPMessage
**)res
);
997 if (rc
== LDAP_SIZELIMIT_EXCEEDED
) {
998 DEBUG(3,("Warning! sizelimit exceeded in ldap. Truncating.\n"));
1003 talloc_destroy(ctx
);
1004 /* if/when we decide to utf8-encode attrs, take out this next line */
1005 TALLOC_FREE(search_attrs
);
1006 return ADS_ERROR(rc
);
1009 * Do a general ADS search
1010 * @param ads connection to ads server
1011 * @param res ** which will contain results - free res* with ads_msgfree()
1012 * @param expr Search expression
1013 * @param attrs Attributes to retrieve
1014 * @return status of search
1016 ADS_STATUS
ads_search(ADS_STRUCT
*ads
, LDAPMessage
**res
,
1017 const char *expr
, const char **attrs
)
1019 return ads_do_search(ads
, ads
->config
.bind_path
, LDAP_SCOPE_SUBTREE
,
1024 * Do a search on a specific DistinguishedName
1025 * @param ads connection to ads server
1026 * @param res ** which will contain results - free res* with ads_msgfree()
1027 * @param dn DistinguishName to search
1028 * @param attrs Attributes to retrieve
1029 * @return status of search
1031 ADS_STATUS
ads_search_dn(ADS_STRUCT
*ads
, LDAPMessage
**res
,
1032 const char *dn
, const char **attrs
)
1034 return ads_do_search(ads
, dn
, LDAP_SCOPE_BASE
, "(objectclass=*)",
1039 * Free up memory from a ads_search
1040 * @param ads connection to ads server
1041 * @param msg Search results to free
1043 void ads_msgfree(ADS_STRUCT
*ads
, LDAPMessage
*msg
)
1050 * Free up memory from various ads requests
1051 * @param ads connection to ads server
1052 * @param mem Area to free
1054 void ads_memfree(ADS_STRUCT
*ads
, void *mem
)
1060 * Get a dn from search results
1061 * @param ads connection to ads server
1062 * @param msg Search result
1065 char *ads_get_dn(ADS_STRUCT
*ads
, LDAPMessage
*msg
)
1067 char *utf8_dn
, *unix_dn
;
1069 utf8_dn
= ldap_get_dn(ads
->ldap
.ld
, msg
);
1072 DEBUG (5, ("ads_get_dn: ldap_get_dn failed\n"));
1076 if (pull_utf8_allocate(&unix_dn
, utf8_dn
) == (size_t)-1) {
1077 DEBUG(0,("ads_get_dn: string conversion failure utf8 [%s]\n",
1081 ldap_memfree(utf8_dn
);
1086 * Get the parent from a dn
1087 * @param dn the dn to return the parent from
1088 * @return parent dn string
1090 char *ads_parent_dn(const char *dn
)
1098 p
= strchr(dn
, ',');
1108 * Find a machine account given a hostname
1109 * @param ads connection to ads server
1110 * @param res ** which will contain results - free res* with ads_msgfree()
1111 * @param host Hostname to search for
1112 * @return status of search
1114 ADS_STATUS
ads_find_machine_acct(ADS_STRUCT
*ads
, LDAPMessage
**res
,
1115 const char *machine
)
1119 const char *attrs
[] = {"*", "nTSecurityDescriptor", NULL
};
1123 /* the easiest way to find a machine account anywhere in the tree
1124 is to look for hostname$ */
1125 if (asprintf(&expr
, "(samAccountName=%s$)", machine
) == -1) {
1126 DEBUG(1, ("asprintf failed!\n"));
1127 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY
);
1130 status
= ads_search(ads
, res
, expr
, attrs
);
1136 * Initialize a list of mods to be used in a modify request
1137 * @param ctx An initialized TALLOC_CTX
1138 * @return allocated ADS_MODLIST
1140 ADS_MODLIST
ads_init_mods(TALLOC_CTX
*ctx
)
1142 #define ADS_MODLIST_ALLOC_SIZE 10
1145 if ((mods
= TALLOC_ZERO_ARRAY(ctx
, LDAPMod
*, ADS_MODLIST_ALLOC_SIZE
+ 1)))
1146 /* -1 is safety to make sure we don't go over the end.
1147 need to reset it to NULL before doing ldap modify */
1148 mods
[ADS_MODLIST_ALLOC_SIZE
] = (LDAPMod
*) -1;
1150 return (ADS_MODLIST
)mods
;
1155 add an attribute to the list, with values list already constructed
1157 static ADS_STATUS
ads_modlist_add(TALLOC_CTX
*ctx
, ADS_MODLIST
*mods
,
1158 int mod_op
, const char *name
,
1159 const void *_invals
)
1161 const void **invals
= (const void **)_invals
;
1163 LDAPMod
**modlist
= (LDAPMod
**) *mods
;
1164 struct berval
**ber_values
= NULL
;
1165 char **char_values
= NULL
;
1168 mod_op
= LDAP_MOD_DELETE
;
1170 if (mod_op
& LDAP_MOD_BVALUES
)
1171 ber_values
= ads_dup_values(ctx
,
1172 (const struct berval
**)invals
);
1174 char_values
= ads_push_strvals(ctx
,
1175 (const char **) invals
);
1178 /* find the first empty slot */
1179 for (curmod
=0; modlist
[curmod
] && modlist
[curmod
] != (LDAPMod
*) -1;
1181 if (modlist
[curmod
] == (LDAPMod
*) -1) {
1182 if (!(modlist
= TALLOC_REALLOC_ARRAY(ctx
, modlist
, LDAPMod
*,
1183 curmod
+ADS_MODLIST_ALLOC_SIZE
+1)))
1184 return ADS_ERROR(LDAP_NO_MEMORY
);
1185 memset(&modlist
[curmod
], 0,
1186 ADS_MODLIST_ALLOC_SIZE
*sizeof(LDAPMod
*));
1187 modlist
[curmod
+ADS_MODLIST_ALLOC_SIZE
] = (LDAPMod
*) -1;
1188 *mods
= (ADS_MODLIST
)modlist
;
1191 if (!(modlist
[curmod
] = TALLOC_ZERO_P(ctx
, LDAPMod
)))
1192 return ADS_ERROR(LDAP_NO_MEMORY
);
1193 modlist
[curmod
]->mod_type
= talloc_strdup(ctx
, name
);
1194 if (mod_op
& LDAP_MOD_BVALUES
) {
1195 modlist
[curmod
]->mod_bvalues
= ber_values
;
1196 } else if (mod_op
& LDAP_MOD_DELETE
) {
1197 modlist
[curmod
]->mod_values
= NULL
;
1199 modlist
[curmod
]->mod_values
= char_values
;
1202 modlist
[curmod
]->mod_op
= mod_op
;
1203 return ADS_ERROR(LDAP_SUCCESS
);
1207 * Add a single string value to a mod list
1208 * @param ctx An initialized TALLOC_CTX
1209 * @param mods An initialized ADS_MODLIST
1210 * @param name The attribute name to add
1211 * @param val The value to add - NULL means DELETE
1212 * @return ADS STATUS indicating success of add
1214 ADS_STATUS
ads_mod_str(TALLOC_CTX
*ctx
, ADS_MODLIST
*mods
,
1215 const char *name
, const char *val
)
1217 const char *values
[2];
1223 return ads_modlist_add(ctx
, mods
, LDAP_MOD_DELETE
, name
, NULL
);
1224 return ads_modlist_add(ctx
, mods
, LDAP_MOD_REPLACE
, name
, values
);
1228 * Add an array of string values to a mod list
1229 * @param ctx An initialized TALLOC_CTX
1230 * @param mods An initialized ADS_MODLIST
1231 * @param name The attribute name to add
1232 * @param vals The array of string values to add - NULL means DELETE
1233 * @return ADS STATUS indicating success of add
1235 ADS_STATUS
ads_mod_strlist(TALLOC_CTX
*ctx
, ADS_MODLIST
*mods
,
1236 const char *name
, const char **vals
)
1239 return ads_modlist_add(ctx
, mods
, LDAP_MOD_DELETE
, name
, NULL
);
1240 return ads_modlist_add(ctx
, mods
, LDAP_MOD_REPLACE
,
1241 name
, (const void **) vals
);
1246 * Add a single ber-encoded value to a mod list
1247 * @param ctx An initialized TALLOC_CTX
1248 * @param mods An initialized ADS_MODLIST
1249 * @param name The attribute name to add
1250 * @param val The value to add - NULL means DELETE
1251 * @return ADS STATUS indicating success of add
1253 static ADS_STATUS
ads_mod_ber(TALLOC_CTX
*ctx
, ADS_MODLIST
*mods
,
1254 const char *name
, const struct berval
*val
)
1256 const struct berval
*values
[2];
1261 return ads_modlist_add(ctx
, mods
, LDAP_MOD_DELETE
, name
, NULL
);
1262 return ads_modlist_add(ctx
, mods
, LDAP_MOD_REPLACE
|LDAP_MOD_BVALUES
,
1263 name
, (const void **) values
);
1268 * Perform an ldap modify
1269 * @param ads connection to ads server
1270 * @param mod_dn DistinguishedName to modify
1271 * @param mods list of modifications to perform
1272 * @return status of modify
1274 ADS_STATUS
ads_gen_mod(ADS_STRUCT
*ads
, const char *mod_dn
, ADS_MODLIST mods
)
1277 char *utf8_dn
= NULL
;
1279 this control is needed to modify that contains a currently
1280 non-existent attribute (but allowable for the object) to run
1282 LDAPControl PermitModify
= {
1283 CONST_DISCARD(char *, ADS_PERMIT_MODIFY_OID
),
1286 LDAPControl
*controls
[2];
1288 controls
[0] = &PermitModify
;
1291 if (push_utf8_allocate(&utf8_dn
, mod_dn
) == -1) {
1292 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY
);
1295 /* find the end of the list, marked by NULL or -1 */
1296 for(i
=0;(mods
[i
]!=0)&&(mods
[i
]!=(LDAPMod
*) -1);i
++);
1297 /* make sure the end of the list is NULL */
1299 ret
= ldap_modify_ext_s(ads
->ldap
.ld
, utf8_dn
,
1300 (LDAPMod
**) mods
, controls
, NULL
);
1302 return ADS_ERROR(ret
);
1306 * Perform an ldap add
1307 * @param ads connection to ads server
1308 * @param new_dn DistinguishedName to add
1309 * @param mods list of attributes and values for DN
1310 * @return status of add
1312 ADS_STATUS
ads_gen_add(ADS_STRUCT
*ads
, const char *new_dn
, ADS_MODLIST mods
)
1315 char *utf8_dn
= NULL
;
1317 if (push_utf8_allocate(&utf8_dn
, new_dn
) == -1) {
1318 DEBUG(1, ("ads_gen_add: push_utf8_allocate failed!"));
1319 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY
);
1322 /* find the end of the list, marked by NULL or -1 */
1323 for(i
=0;(mods
[i
]!=0)&&(mods
[i
]!=(LDAPMod
*) -1);i
++);
1324 /* make sure the end of the list is NULL */
1327 ret
= ldap_add_s(ads
->ldap
.ld
, utf8_dn
, (LDAPMod
**)mods
);
1329 return ADS_ERROR(ret
);
1333 * Delete a DistinguishedName
1334 * @param ads connection to ads server
1335 * @param new_dn DistinguishedName to delete
1336 * @return status of delete
1338 ADS_STATUS
ads_del_dn(ADS_STRUCT
*ads
, char *del_dn
)
1341 char *utf8_dn
= NULL
;
1342 if (push_utf8_allocate(&utf8_dn
, del_dn
) == -1) {
1343 DEBUG(1, ("ads_del_dn: push_utf8_allocate failed!"));
1344 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY
);
1347 ret
= ldap_delete_s(ads
->ldap
.ld
, utf8_dn
);
1349 return ADS_ERROR(ret
);
1353 * Build an org unit string
1354 * if org unit is Computers or blank then assume a container, otherwise
1355 * assume a / separated list of organisational units.
1356 * jmcd: '\' is now used for escapes so certain chars can be in the ou (e.g. #)
1357 * @param ads connection to ads server
1358 * @param org_unit Organizational unit
1359 * @return org unit string - caller must free
1361 char *ads_ou_string(ADS_STRUCT
*ads
, const char *org_unit
)
1365 if (!org_unit
|| !*org_unit
) {
1367 ret
= ads_default_ou_string(ads
, WELL_KNOWN_GUID_COMPUTERS
);
1369 /* samba4 might not yet respond to a wellknownobject-query */
1370 return ret
? ret
: SMB_STRDUP("cn=Computers");
1373 if (strequal(org_unit
, "Computers")) {
1374 return SMB_STRDUP("cn=Computers");
1377 /* jmcd: removed "\\" from the separation chars, because it is
1378 needed as an escape for chars like '#' which are valid in an
1380 return ads_build_path(org_unit
, "/", "ou=", 1);
1384 * Get a org unit string for a well-known GUID
1385 * @param ads connection to ads server
1386 * @param wknguid Well known GUID
1387 * @return org unit string - caller must free
1389 char *ads_default_ou_string(ADS_STRUCT
*ads
, const char *wknguid
)
1392 LDAPMessage
*res
= NULL
;
1393 char *base
, *wkn_dn
= NULL
, *ret
= NULL
, **wkn_dn_exp
= NULL
,
1394 **bind_dn_exp
= NULL
;
1395 const char *attrs
[] = {"distinguishedName", NULL
};
1396 int new_ln
, wkn_ln
, bind_ln
, i
;
1398 if (wknguid
== NULL
) {
1402 if (asprintf(&base
, "<WKGUID=%s,%s>", wknguid
, ads
->config
.bind_path
) == -1) {
1403 DEBUG(1, ("asprintf failed!\n"));
1407 status
= ads_search_dn(ads
, &res
, base
, attrs
);
1408 if (!ADS_ERR_OK(status
)) {
1409 DEBUG(1,("Failed while searching for: %s\n", base
));
1413 if (ads_count_replies(ads
, res
) != 1) {
1417 /* substitute the bind-path from the well-known-guid-search result */
1418 wkn_dn
= ads_get_dn(ads
, res
);
1423 wkn_dn_exp
= ldap_explode_dn(wkn_dn
, 0);
1428 bind_dn_exp
= ldap_explode_dn(ads
->config
.bind_path
, 0);
1433 for (wkn_ln
=0; wkn_dn_exp
[wkn_ln
]; wkn_ln
++)
1435 for (bind_ln
=0; bind_dn_exp
[bind_ln
]; bind_ln
++)
1438 new_ln
= wkn_ln
- bind_ln
;
1440 ret
= SMB_STRDUP(wkn_dn_exp
[0]);
1445 for (i
=1; i
< new_ln
; i
++) {
1448 if (asprintf(&s
, "%s,%s", ret
, wkn_dn_exp
[i
]) == -1) {
1454 ret
= SMB_STRDUP(s
);
1463 ads_msgfree(ads
, res
);
1464 ads_memfree(ads
, wkn_dn
);
1466 ldap_value_free(wkn_dn_exp
);
1469 ldap_value_free(bind_dn_exp
);
1476 * Adds (appends) an item to an attribute array, rather then
1477 * replacing the whole list
1478 * @param ctx An initialized TALLOC_CTX
1479 * @param mods An initialized ADS_MODLIST
1480 * @param name name of the ldap attribute to append to
1481 * @param vals an array of values to add
1482 * @return status of addition
1485 ADS_STATUS
ads_add_strlist(TALLOC_CTX
*ctx
, ADS_MODLIST
*mods
,
1486 const char *name
, const char **vals
)
1488 return ads_modlist_add(ctx
, mods
, LDAP_MOD_ADD
, name
,
1489 (const void *) vals
);
1493 * Determines the computer account's current KVNO via an LDAP lookup
1494 * @param ads An initialized ADS_STRUCT
1495 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1496 * @return the kvno for the computer account, or -1 in case of a failure.
1499 uint32
ads_get_kvno(ADS_STRUCT
*ads
, const char *machine_name
)
1501 LDAPMessage
*res
= NULL
;
1502 uint32 kvno
= (uint32
)-1; /* -1 indicates a failure */
1504 const char *attrs
[] = {"msDS-KeyVersionNumber", NULL
};
1505 char *dn_string
= NULL
;
1506 ADS_STATUS ret
= ADS_ERROR(LDAP_SUCCESS
);
1508 DEBUG(5,("ads_get_kvno: Searching for host %s\n", machine_name
));
1509 if (asprintf(&filter
, "(samAccountName=%s$)", machine_name
) == -1) {
1512 ret
= ads_search(ads
, &res
, filter
, attrs
);
1514 if (!ADS_ERR_OK(ret
) && ads_count_replies(ads
, res
)) {
1515 DEBUG(1,("ads_get_kvno: Computer Account For %s not found.\n", machine_name
));
1516 ads_msgfree(ads
, res
);
1520 dn_string
= ads_get_dn(ads
, res
);
1522 DEBUG(0,("ads_get_kvno: out of memory.\n"));
1523 ads_msgfree(ads
, res
);
1526 DEBUG(5,("ads_get_kvno: Using: %s\n", dn_string
));
1527 ads_memfree(ads
, dn_string
);
1529 /* ---------------------------------------------------------
1530 * 0 is returned as a default KVNO from this point on...
1531 * This is done because Windows 2000 does not support key
1532 * version numbers. Chances are that a failure in the next
1533 * step is simply due to Windows 2000 being used for a
1534 * domain controller. */
1537 if (!ads_pull_uint32(ads
, res
, "msDS-KeyVersionNumber", &kvno
)) {
1538 DEBUG(3,("ads_get_kvno: Error Determining KVNO!\n"));
1539 DEBUG(3,("ads_get_kvno: Windows 2000 does not support KVNO's, so this may be normal.\n"));
1540 ads_msgfree(ads
, res
);
1545 DEBUG(5,("ads_get_kvno: Looked Up KVNO of: %d\n", kvno
));
1546 ads_msgfree(ads
, res
);
1551 * This clears out all registered spn's for a given hostname
1552 * @param ads An initilaized ADS_STRUCT
1553 * @param machine_name the NetBIOS name of the computer.
1554 * @return 0 upon success, non-zero otherwise.
1557 ADS_STATUS
ads_clear_service_principal_names(ADS_STRUCT
*ads
, const char *machine_name
)
1560 LDAPMessage
*res
= NULL
;
1562 const char *servicePrincipalName
[1] = {NULL
};
1563 ADS_STATUS ret
= ADS_ERROR(LDAP_SUCCESS
);
1564 char *dn_string
= NULL
;
1566 ret
= ads_find_machine_acct(ads
, &res
, machine_name
);
1567 if (!ADS_ERR_OK(ret
) || ads_count_replies(ads
, res
) != 1) {
1568 DEBUG(5,("ads_clear_service_principal_names: WARNING: Host Account for %s not found... skipping operation.\n", machine_name
));
1569 DEBUG(5,("ads_clear_service_principal_names: WARNING: Service Principals for %s have NOT been cleared.\n", machine_name
));
1570 ads_msgfree(ads
, res
);
1571 return ADS_ERROR(LDAP_NO_SUCH_OBJECT
);
1574 DEBUG(5,("ads_clear_service_principal_names: Host account for %s found\n", machine_name
));
1575 ctx
= talloc_init("ads_clear_service_principal_names");
1577 ads_msgfree(ads
, res
);
1578 return ADS_ERROR(LDAP_NO_MEMORY
);
1581 if (!(mods
= ads_init_mods(ctx
))) {
1582 talloc_destroy(ctx
);
1583 ads_msgfree(ads
, res
);
1584 return ADS_ERROR(LDAP_NO_MEMORY
);
1586 ret
= ads_mod_strlist(ctx
, &mods
, "servicePrincipalName", servicePrincipalName
);
1587 if (!ADS_ERR_OK(ret
)) {
1588 DEBUG(1,("ads_clear_service_principal_names: Error creating strlist.\n"));
1589 ads_msgfree(ads
, res
);
1590 talloc_destroy(ctx
);
1593 dn_string
= ads_get_dn(ads
, res
);
1595 talloc_destroy(ctx
);
1596 ads_msgfree(ads
, res
);
1597 return ADS_ERROR(LDAP_NO_MEMORY
);
1599 ret
= ads_gen_mod(ads
, dn_string
, mods
);
1600 ads_memfree(ads
,dn_string
);
1601 if (!ADS_ERR_OK(ret
)) {
1602 DEBUG(1,("ads_clear_service_principal_names: Error: Updating Service Principals for machine %s in LDAP\n",
1604 ads_msgfree(ads
, res
);
1605 talloc_destroy(ctx
);
1609 ads_msgfree(ads
, res
);
1610 talloc_destroy(ctx
);
1615 * This adds a service principal name to an existing computer account
1616 * (found by hostname) in AD.
1617 * @param ads An initialized ADS_STRUCT
1618 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1619 * @param my_fqdn The fully qualified DNS name of the machine
1620 * @param spn A string of the service principal to add, i.e. 'host'
1621 * @return 0 upon sucess, or non-zero if a failure occurs
1624 ADS_STATUS
ads_add_service_principal_name(ADS_STRUCT
*ads
, const char *machine_name
,
1625 const char *my_fqdn
, const char *spn
)
1629 LDAPMessage
*res
= NULL
;
1632 char *dn_string
= NULL
;
1633 const char *servicePrincipalName
[3] = {NULL
, NULL
, NULL
};
1635 ret
= ads_find_machine_acct(ads
, &res
, machine_name
);
1636 if (!ADS_ERR_OK(ret
) || ads_count_replies(ads
, res
) != 1) {
1637 DEBUG(1,("ads_add_service_principal_name: WARNING: Host Account for %s not found... skipping operation.\n",
1639 DEBUG(1,("ads_add_service_principal_name: WARNING: Service Principal '%s/%s@%s' has NOT been added.\n",
1640 spn
, machine_name
, ads
->config
.realm
));
1641 ads_msgfree(ads
, res
);
1642 return ADS_ERROR(LDAP_NO_SUCH_OBJECT
);
1645 DEBUG(1,("ads_add_service_principal_name: Host account for %s found\n", machine_name
));
1646 if (!(ctx
= talloc_init("ads_add_service_principal_name"))) {
1647 ads_msgfree(ads
, res
);
1648 return ADS_ERROR(LDAP_NO_MEMORY
);
1651 /* add short name spn */
1653 if ( (psp1
= talloc_asprintf(ctx
, "%s/%s", spn
, machine_name
)) == NULL
) {
1654 talloc_destroy(ctx
);
1655 ads_msgfree(ads
, res
);
1656 return ADS_ERROR(LDAP_NO_MEMORY
);
1659 strlower_m(&psp1
[strlen(spn
)]);
1660 servicePrincipalName
[0] = psp1
;
1662 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n",
1663 psp1
, machine_name
));
1666 /* add fully qualified spn */
1668 if ( (psp2
= talloc_asprintf(ctx
, "%s/%s", spn
, my_fqdn
)) == NULL
) {
1669 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
1673 strlower_m(&psp2
[strlen(spn
)]);
1674 servicePrincipalName
[1] = psp2
;
1676 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n",
1677 psp2
, machine_name
));
1679 if ( (mods
= ads_init_mods(ctx
)) == NULL
) {
1680 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
1684 ret
= ads_add_strlist(ctx
, &mods
, "servicePrincipalName", servicePrincipalName
);
1685 if (!ADS_ERR_OK(ret
)) {
1686 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
1690 if ( (dn_string
= ads_get_dn(ads
, res
)) == NULL
) {
1691 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
1695 ret
= ads_gen_mod(ads
, dn_string
, mods
);
1696 ads_memfree(ads
,dn_string
);
1697 if (!ADS_ERR_OK(ret
)) {
1698 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
1704 ads_msgfree(ads
, res
);
1709 * adds a machine account to the ADS server
1710 * @param ads An intialized ADS_STRUCT
1711 * @param machine_name - the NetBIOS machine name of this account.
1712 * @param account_type A number indicating the type of account to create
1713 * @param org_unit The LDAP path in which to place this account
1714 * @return 0 upon success, or non-zero otherwise
1717 ADS_STATUS
ads_create_machine_acct(ADS_STRUCT
*ads
, const char *machine_name
,
1718 const char *org_unit
)
1721 char *samAccountName
, *controlstr
;
1724 char *machine_escaped
= NULL
;
1726 const char *objectClass
[] = {"top", "person", "organizationalPerson",
1727 "user", "computer", NULL
};
1728 LDAPMessage
*res
= NULL
;
1729 uint32 acct_control
= ( UF_WORKSTATION_TRUST_ACCOUNT
|\
1730 UF_DONT_EXPIRE_PASSWD
|\
1731 UF_ACCOUNTDISABLE
);
1733 if (!(ctx
= talloc_init("ads_add_machine_acct")))
1734 return ADS_ERROR(LDAP_NO_MEMORY
);
1736 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
1738 machine_escaped
= escape_rdn_val_string_alloc(machine_name
);
1739 if (!machine_escaped
) {
1743 new_dn
= talloc_asprintf(ctx
, "cn=%s,%s", machine_escaped
, org_unit
);
1744 samAccountName
= talloc_asprintf(ctx
, "%s$", machine_name
);
1746 if ( !new_dn
|| !samAccountName
) {
1750 #ifndef ENCTYPE_ARCFOUR_HMAC
1751 acct_control
|= UF_USE_DES_KEY_ONLY
;
1754 if (!(controlstr
= talloc_asprintf(ctx
, "%u", acct_control
))) {
1758 if (!(mods
= ads_init_mods(ctx
))) {
1762 ads_mod_str(ctx
, &mods
, "cn", machine_name
);
1763 ads_mod_str(ctx
, &mods
, "sAMAccountName", samAccountName
);
1764 ads_mod_strlist(ctx
, &mods
, "objectClass", objectClass
);
1765 ads_mod_str(ctx
, &mods
, "userAccountControl", controlstr
);
1767 ret
= ads_gen_add(ads
, new_dn
, mods
);
1770 SAFE_FREE(machine_escaped
);
1771 ads_msgfree(ads
, res
);
1772 talloc_destroy(ctx
);
1778 * move a machine account to another OU on the ADS server
1779 * @param ads - An intialized ADS_STRUCT
1780 * @param machine_name - the NetBIOS machine name of this account.
1781 * @param org_unit - The LDAP path in which to place this account
1782 * @param moved - whether we moved the machine account (optional)
1783 * @return 0 upon success, or non-zero otherwise
1786 ADS_STATUS
ads_move_machine_acct(ADS_STRUCT
*ads
, const char *machine_name
,
1787 const char *org_unit
, bool *moved
)
1791 LDAPMessage
*res
= NULL
;
1792 char *filter
= NULL
;
1793 char *computer_dn
= NULL
;
1795 char *computer_rdn
= NULL
;
1796 bool need_move
= False
;
1798 if (asprintf(&filter
, "(samAccountName=%s$)", machine_name
) == -1) {
1799 rc
= ADS_ERROR(LDAP_NO_MEMORY
);
1803 /* Find pre-existing machine */
1804 rc
= ads_search(ads
, &res
, filter
, NULL
);
1805 if (!ADS_ERR_OK(rc
)) {
1809 computer_dn
= ads_get_dn(ads
, res
);
1811 rc
= ADS_ERROR(LDAP_NO_MEMORY
);
1815 parent_dn
= ads_parent_dn(computer_dn
);
1816 if (strequal(parent_dn
, org_unit
)) {
1822 if (asprintf(&computer_rdn
, "CN=%s", machine_name
) == -1) {
1823 rc
= ADS_ERROR(LDAP_NO_MEMORY
);
1827 ldap_status
= ldap_rename_s(ads
->ldap
.ld
, computer_dn
, computer_rdn
,
1828 org_unit
, 1, NULL
, NULL
);
1829 rc
= ADS_ERROR(ldap_status
);
1832 ads_msgfree(ads
, res
);
1834 SAFE_FREE(computer_dn
);
1835 SAFE_FREE(computer_rdn
);
1837 if (!ADS_ERR_OK(rc
)) {
1849 dump a binary result from ldap
1851 static void dump_binary(ADS_STRUCT
*ads
, const char *field
, struct berval
**values
)
1854 for (i
=0; values
[i
]; i
++) {
1855 printf("%s: ", field
);
1856 for (j
=0; j
<values
[i
]->bv_len
; j
++) {
1857 printf("%02X", (unsigned char)values
[i
]->bv_val
[j
]);
1863 static void dump_guid(ADS_STRUCT
*ads
, const char *field
, struct berval
**values
)
1866 for (i
=0; values
[i
]; i
++) {
1871 memcpy(guid
.info
, values
[i
]->bv_val
, sizeof(guid
.info
));
1872 smb_uuid_unpack(guid
, &tmp
);
1873 printf("%s: %s\n", field
, smb_uuid_string(talloc_tos(), tmp
));
1878 dump a sid result from ldap
1880 static void dump_sid(ADS_STRUCT
*ads
, const char *field
, struct berval
**values
)
1883 for (i
=0; values
[i
]; i
++) {
1886 sid_parse(values
[i
]->bv_val
, values
[i
]->bv_len
, &sid
);
1887 printf("%s: %s\n", field
, sid_to_fstring(tmp
, &sid
));
1892 dump ntSecurityDescriptor
1894 static void dump_sd(ADS_STRUCT
*ads
, const char *filed
, struct berval
**values
)
1896 TALLOC_CTX
*frame
= talloc_stackframe();
1897 struct security_descriptor
*psd
;
1900 status
= unmarshall_sec_desc(talloc_tos(), (uint8
*)values
[0]->bv_val
,
1901 values
[0]->bv_len
, &psd
);
1902 if (!NT_STATUS_IS_OK(status
)) {
1903 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
1904 nt_errstr(status
)));
1910 ads_disp_sd(ads
, talloc_tos(), psd
);
1917 dump a string result from ldap
1919 static void dump_string(const char *field
, char **values
)
1922 for (i
=0; values
[i
]; i
++) {
1923 printf("%s: %s\n", field
, values
[i
]);
1928 dump a field from LDAP on stdout
1932 static bool ads_dump_field(ADS_STRUCT
*ads
, char *field
, void **values
, void *data_area
)
1937 void (*handler
)(ADS_STRUCT
*, const char *, struct berval
**);
1939 {"objectGUID", False
, dump_guid
},
1940 {"netbootGUID", False
, dump_guid
},
1941 {"nTSecurityDescriptor", False
, dump_sd
},
1942 {"dnsRecord", False
, dump_binary
},
1943 {"objectSid", False
, dump_sid
},
1944 {"tokenGroups", False
, dump_sid
},
1945 {"tokenGroupsNoGCAcceptable", False
, dump_sid
},
1946 {"tokengroupsGlobalandUniversal", False
, dump_sid
},
1947 {"mS-DS-CreatorSID", False
, dump_sid
},
1948 {"msExchMailboxGuid", False
, dump_guid
},
1953 if (!field
) { /* must be end of an entry */
1958 for (i
=0; handlers
[i
].name
; i
++) {
1959 if (StrCaseCmp(handlers
[i
].name
, field
) == 0) {
1960 if (!values
) /* first time, indicate string or not */
1961 return handlers
[i
].string
;
1962 handlers
[i
].handler(ads
, field
, (struct berval
**) values
);
1966 if (!handlers
[i
].name
) {
1967 if (!values
) /* first time, indicate string conversion */
1969 dump_string(field
, (char **)values
);
1975 * Dump a result from LDAP on stdout
1976 * used for debugging
1977 * @param ads connection to ads server
1978 * @param res Results to dump
1981 void ads_dump(ADS_STRUCT
*ads
, LDAPMessage
*res
)
1983 ads_process_results(ads
, res
, ads_dump_field
, NULL
);
1987 * Walk through results, calling a function for each entry found.
1988 * The function receives a field name, a berval * array of values,
1989 * and a data area passed through from the start. The function is
1990 * called once with null for field and values at the end of each
1992 * @param ads connection to ads server
1993 * @param res Results to process
1994 * @param fn Function for processing each result
1995 * @param data_area user-defined area to pass to function
1997 void ads_process_results(ADS_STRUCT
*ads
, LDAPMessage
*res
,
1998 bool (*fn
)(ADS_STRUCT
*, char *, void **, void *),
2004 if (!(ctx
= talloc_init("ads_process_results")))
2007 for (msg
= ads_first_entry(ads
, res
); msg
;
2008 msg
= ads_next_entry(ads
, msg
)) {
2012 for (utf8_field
=ldap_first_attribute(ads
->ldap
.ld
,
2013 (LDAPMessage
*)msg
,&b
);
2015 utf8_field
=ldap_next_attribute(ads
->ldap
.ld
,
2016 (LDAPMessage
*)msg
,b
)) {
2017 struct berval
**ber_vals
;
2018 char **str_vals
, **utf8_vals
;
2022 pull_utf8_talloc(ctx
, &field
, utf8_field
);
2023 string
= fn(ads
, field
, NULL
, data_area
);
2026 utf8_vals
= ldap_get_values(ads
->ldap
.ld
,
2027 (LDAPMessage
*)msg
, field
);
2028 str_vals
= ads_pull_strvals(ctx
,
2029 (const char **) utf8_vals
);
2030 fn(ads
, field
, (void **) str_vals
, data_area
);
2031 ldap_value_free(utf8_vals
);
2033 ber_vals
= ldap_get_values_len(ads
->ldap
.ld
,
2034 (LDAPMessage
*)msg
, field
);
2035 fn(ads
, field
, (void **) ber_vals
, data_area
);
2037 ldap_value_free_len(ber_vals
);
2039 ldap_memfree(utf8_field
);
2042 talloc_free_children(ctx
);
2043 fn(ads
, NULL
, NULL
, data_area
); /* completed an entry */
2046 talloc_destroy(ctx
);
2050 * count how many replies are in a LDAPMessage
2051 * @param ads connection to ads server
2052 * @param res Results to count
2053 * @return number of replies
2055 int ads_count_replies(ADS_STRUCT
*ads
, void *res
)
2057 return ldap_count_entries(ads
->ldap
.ld
, (LDAPMessage
*)res
);
2061 * pull the first entry from a ADS result
2062 * @param ads connection to ads server
2063 * @param res Results of search
2064 * @return first entry from result
2066 LDAPMessage
*ads_first_entry(ADS_STRUCT
*ads
, LDAPMessage
*res
)
2068 return ldap_first_entry(ads
->ldap
.ld
, res
);
2072 * pull the next entry from a ADS result
2073 * @param ads connection to ads server
2074 * @param res Results of search
2075 * @return next entry from result
2077 LDAPMessage
*ads_next_entry(ADS_STRUCT
*ads
, LDAPMessage
*res
)
2079 return ldap_next_entry(ads
->ldap
.ld
, res
);
2083 * pull a single string from a ADS result
2084 * @param ads connection to ads server
2085 * @param mem_ctx TALLOC_CTX to use for allocating result string
2086 * @param msg Results of search
2087 * @param field Attribute to retrieve
2088 * @return Result string in talloc context
2090 char *ads_pull_string(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
, LDAPMessage
*msg
,
2098 values
= ldap_get_values(ads
->ldap
.ld
, msg
, field
);
2103 rc
= pull_utf8_talloc(mem_ctx
, &ux_string
,
2105 if (rc
!= (size_t)-1)
2109 ldap_value_free(values
);
2114 * pull an array of strings from a ADS result
2115 * @param ads connection to ads server
2116 * @param mem_ctx TALLOC_CTX to use for allocating result string
2117 * @param msg Results of search
2118 * @param field Attribute to retrieve
2119 * @return Result strings in talloc context
2121 char **ads_pull_strings(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
,
2122 LDAPMessage
*msg
, const char *field
,
2129 values
= ldap_get_values(ads
->ldap
.ld
, msg
, field
);
2133 *num_values
= ldap_count_values(values
);
2135 ret
= TALLOC_ARRAY(mem_ctx
, char *, *num_values
+ 1);
2137 ldap_value_free(values
);
2141 for (i
=0;i
<*num_values
;i
++) {
2142 if (pull_utf8_talloc(mem_ctx
, &ret
[i
], values
[i
]) == -1) {
2143 ldap_value_free(values
);
2149 ldap_value_free(values
);
2154 * pull an array of strings from a ADS result
2155 * (handle large multivalue attributes with range retrieval)
2156 * @param ads connection to ads server
2157 * @param mem_ctx TALLOC_CTX to use for allocating result string
2158 * @param msg Results of search
2159 * @param field Attribute to retrieve
2160 * @param current_strings strings returned by a previous call to this function
2161 * @param next_attribute The next query should ask for this attribute
2162 * @param num_values How many values did we get this time?
2163 * @param more_values Are there more values to get?
2164 * @return Result strings in talloc context
2166 char **ads_pull_strings_range(ADS_STRUCT
*ads
,
2167 TALLOC_CTX
*mem_ctx
,
2168 LDAPMessage
*msg
, const char *field
,
2169 char **current_strings
,
2170 const char **next_attribute
,
2171 size_t *num_strings
,
2175 char *expected_range_attrib
, *range_attr
;
2176 BerElement
*ptr
= NULL
;
2179 size_t num_new_strings
;
2180 unsigned long int range_start
;
2181 unsigned long int range_end
;
2183 /* we might have been given the whole lot anyway */
2184 if ((strings
= ads_pull_strings(ads
, mem_ctx
, msg
, field
, num_strings
))) {
2185 *more_strings
= False
;
2189 expected_range_attrib
= talloc_asprintf(mem_ctx
, "%s;Range=", field
);
2191 /* look for Range result */
2192 for (attr
= ldap_first_attribute(ads
->ldap
.ld
, (LDAPMessage
*)msg
, &ptr
);
2194 attr
= ldap_next_attribute(ads
->ldap
.ld
, (LDAPMessage
*)msg
, ptr
)) {
2195 /* we ignore the fact that this is utf8, as all attributes are ascii... */
2196 if (strnequal(attr
, expected_range_attrib
, strlen(expected_range_attrib
))) {
2204 /* nothing here - this field is just empty */
2205 *more_strings
= False
;
2209 if (sscanf(&range_attr
[strlen(expected_range_attrib
)], "%lu-%lu",
2210 &range_start
, &range_end
) == 2) {
2211 *more_strings
= True
;
2213 if (sscanf(&range_attr
[strlen(expected_range_attrib
)], "%lu-*",
2214 &range_start
) == 1) {
2215 *more_strings
= False
;
2217 DEBUG(1, ("ads_pull_strings_range: Cannot parse Range attriubte (%s)\n",
2219 ldap_memfree(range_attr
);
2220 *more_strings
= False
;
2225 if ((*num_strings
) != range_start
) {
2226 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) doesn't start at %u, but at %lu"
2227 " - aborting range retreival\n",
2228 range_attr
, (unsigned int)(*num_strings
) + 1, range_start
));
2229 ldap_memfree(range_attr
);
2230 *more_strings
= False
;
2234 new_strings
= ads_pull_strings(ads
, mem_ctx
, msg
, range_attr
, &num_new_strings
);
2236 if (*more_strings
&& ((*num_strings
+ num_new_strings
) != (range_end
+ 1))) {
2237 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) tells us we have %lu "
2238 "strings in this bunch, but we only got %lu - aborting range retreival\n",
2239 range_attr
, (unsigned long int)range_end
- range_start
+ 1,
2240 (unsigned long int)num_new_strings
));
2241 ldap_memfree(range_attr
);
2242 *more_strings
= False
;
2246 strings
= TALLOC_REALLOC_ARRAY(mem_ctx
, current_strings
, char *,
2247 *num_strings
+ num_new_strings
);
2249 if (strings
== NULL
) {
2250 ldap_memfree(range_attr
);
2251 *more_strings
= False
;
2255 if (new_strings
&& num_new_strings
) {
2256 memcpy(&strings
[*num_strings
], new_strings
,
2257 sizeof(*new_strings
) * num_new_strings
);
2260 (*num_strings
) += num_new_strings
;
2262 if (*more_strings
) {
2263 *next_attribute
= talloc_asprintf(mem_ctx
,
2268 if (!*next_attribute
) {
2269 DEBUG(1, ("talloc_asprintf for next attribute failed!\n"));
2270 ldap_memfree(range_attr
);
2271 *more_strings
= False
;
2276 ldap_memfree(range_attr
);
2282 * pull a single uint32 from a ADS result
2283 * @param ads connection to ads server
2284 * @param msg Results of search
2285 * @param field Attribute to retrieve
2286 * @param v Pointer to int to store result
2287 * @return boolean inidicating success
2289 bool ads_pull_uint32(ADS_STRUCT
*ads
, LDAPMessage
*msg
, const char *field
,
2294 values
= ldap_get_values(ads
->ldap
.ld
, msg
, field
);
2298 ldap_value_free(values
);
2302 *v
= atoi(values
[0]);
2303 ldap_value_free(values
);
2308 * pull a single objectGUID from an ADS result
2309 * @param ads connection to ADS server
2310 * @param msg results of search
2311 * @param guid 37-byte area to receive text guid
2312 * @return boolean indicating success
2314 bool ads_pull_guid(ADS_STRUCT
*ads
, LDAPMessage
*msg
, struct GUID
*guid
)
2317 UUID_FLAT flat_guid
;
2319 values
= ldap_get_values(ads
->ldap
.ld
, msg
, "objectGUID");
2324 memcpy(&flat_guid
.info
, values
[0], sizeof(UUID_FLAT
));
2325 smb_uuid_unpack(flat_guid
, guid
);
2326 ldap_value_free(values
);
2329 ldap_value_free(values
);
2336 * pull a single DOM_SID from a ADS result
2337 * @param ads connection to ads server
2338 * @param msg Results of search
2339 * @param field Attribute to retrieve
2340 * @param sid Pointer to sid to store result
2341 * @return boolean inidicating success
2343 bool ads_pull_sid(ADS_STRUCT
*ads
, LDAPMessage
*msg
, const char *field
,
2346 struct berval
**values
;
2349 values
= ldap_get_values_len(ads
->ldap
.ld
, msg
, field
);
2355 ret
= sid_parse(values
[0]->bv_val
, values
[0]->bv_len
, sid
);
2357 ldap_value_free_len(values
);
2362 * pull an array of DOM_SIDs from a ADS result
2363 * @param ads connection to ads server
2364 * @param mem_ctx TALLOC_CTX for allocating sid array
2365 * @param msg Results of search
2366 * @param field Attribute to retrieve
2367 * @param sids pointer to sid array to allocate
2368 * @return the count of SIDs pulled
2370 int ads_pull_sids(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
,
2371 LDAPMessage
*msg
, const char *field
, DOM_SID
**sids
)
2373 struct berval
**values
;
2377 values
= ldap_get_values_len(ads
->ldap
.ld
, msg
, field
);
2382 for (i
=0; values
[i
]; i
++)
2386 (*sids
) = TALLOC_ARRAY(mem_ctx
, DOM_SID
, i
);
2388 ldap_value_free_len(values
);
2396 for (i
=0; values
[i
]; i
++) {
2397 ret
= sid_parse(values
[i
]->bv_val
, values
[i
]->bv_len
, &(*sids
)[count
]);
2399 DEBUG(10, ("pulling SID: %s\n",
2400 sid_string_dbg(&(*sids
)[count
])));
2405 ldap_value_free_len(values
);
2410 * pull a SEC_DESC from a ADS result
2411 * @param ads connection to ads server
2412 * @param mem_ctx TALLOC_CTX for allocating sid array
2413 * @param msg Results of search
2414 * @param field Attribute to retrieve
2415 * @param sd Pointer to *SEC_DESC to store result (talloc()ed)
2416 * @return boolean inidicating success
2418 bool ads_pull_sd(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
,
2419 LDAPMessage
*msg
, const char *field
, SEC_DESC
**sd
)
2421 struct berval
**values
;
2424 values
= ldap_get_values_len(ads
->ldap
.ld
, msg
, field
);
2426 if (!values
) return false;
2430 status
= unmarshall_sec_desc(mem_ctx
,
2431 (uint8
*)values
[0]->bv_val
,
2432 values
[0]->bv_len
, sd
);
2433 if (!NT_STATUS_IS_OK(status
)) {
2434 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
2435 nt_errstr(status
)));
2440 ldap_value_free_len(values
);
2445 * in order to support usernames longer than 21 characters we need to
2446 * use both the sAMAccountName and the userPrincipalName attributes
2447 * It seems that not all users have the userPrincipalName attribute set
2449 * @param ads connection to ads server
2450 * @param mem_ctx TALLOC_CTX for allocating sid array
2451 * @param msg Results of search
2452 * @return the username
2454 char *ads_pull_username(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
,
2460 /* lookup_name() only works on the sAMAccountName to
2461 returning the username portion of userPrincipalName
2462 breaks winbindd_getpwnam() */
2464 ret
= ads_pull_string(ads
, mem_ctx
, msg
, "userPrincipalName");
2465 if (ret
&& (p
= strchr_m(ret
, '@'))) {
2470 return ads_pull_string(ads
, mem_ctx
, msg
, "sAMAccountName");
2475 * find the update serial number - this is the core of the ldap cache
2476 * @param ads connection to ads server
2477 * @param ads connection to ADS server
2478 * @param usn Pointer to retrieved update serial number
2479 * @return status of search
2481 ADS_STATUS
ads_USN(ADS_STRUCT
*ads
, uint32
*usn
)
2483 const char *attrs
[] = {"highestCommittedUSN", NULL
};
2487 status
= ads_do_search_retry(ads
, "", LDAP_SCOPE_BASE
, "(objectclass=*)", attrs
, &res
);
2488 if (!ADS_ERR_OK(status
))
2491 if (ads_count_replies(ads
, res
) != 1) {
2492 ads_msgfree(ads
, res
);
2493 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED
);
2496 if (!ads_pull_uint32(ads
, res
, "highestCommittedUSN", usn
)) {
2497 ads_msgfree(ads
, res
);
2498 return ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE
);
2501 ads_msgfree(ads
, res
);
2505 /* parse a ADS timestring - typical string is
2506 '20020917091222.0Z0' which means 09:12.22 17th September
2508 static time_t ads_parse_time(const char *str
)
2514 if (sscanf(str
, "%4d%2d%2d%2d%2d%2d",
2515 &tm
.tm_year
, &tm
.tm_mon
, &tm
.tm_mday
,
2516 &tm
.tm_hour
, &tm
.tm_min
, &tm
.tm_sec
) != 6) {
2525 /********************************************************************
2526 ********************************************************************/
2528 ADS_STATUS
ads_current_time(ADS_STRUCT
*ads
)
2530 const char *attrs
[] = {"currentTime", NULL
};
2535 ADS_STRUCT
*ads_s
= ads
;
2537 if (!(ctx
= talloc_init("ads_current_time"))) {
2538 return ADS_ERROR(LDAP_NO_MEMORY
);
2541 /* establish a new ldap tcp session if necessary */
2543 if ( !ads
->ldap
.ld
) {
2544 if ( (ads_s
= ads_init( ads
->server
.realm
, ads
->server
.workgroup
,
2545 ads
->server
.ldap_server
)) == NULL
)
2549 ads_s
->auth
.flags
= ADS_AUTH_ANON_BIND
;
2550 status
= ads_connect( ads_s
);
2551 if ( !ADS_ERR_OK(status
))
2555 status
= ads_do_search(ads_s
, "", LDAP_SCOPE_BASE
, "(objectclass=*)", attrs
, &res
);
2556 if (!ADS_ERR_OK(status
)) {
2560 timestr
= ads_pull_string(ads_s
, ctx
, res
, "currentTime");
2562 ads_msgfree(ads_s
, res
);
2563 status
= ADS_ERROR(LDAP_NO_RESULTS_RETURNED
);
2567 /* but save the time and offset in the original ADS_STRUCT */
2569 ads
->config
.current_time
= ads_parse_time(timestr
);
2571 if (ads
->config
.current_time
!= 0) {
2572 ads
->auth
.time_offset
= ads
->config
.current_time
- time(NULL
);
2573 DEBUG(4,("time offset is %d seconds\n", ads
->auth
.time_offset
));
2576 ads_msgfree(ads
, res
);
2578 status
= ADS_SUCCESS
;
2581 /* free any temporary ads connections */
2582 if ( ads_s
!= ads
) {
2583 ads_destroy( &ads_s
);
2585 talloc_destroy(ctx
);
2590 /********************************************************************
2591 ********************************************************************/
2593 ADS_STATUS
ads_domain_func_level(ADS_STRUCT
*ads
, uint32
*val
)
2595 const char *attrs
[] = {"domainFunctionality", NULL
};
2598 ADS_STRUCT
*ads_s
= ads
;
2600 *val
= DS_DOMAIN_FUNCTION_2000
;
2602 /* establish a new ldap tcp session if necessary */
2604 if ( !ads
->ldap
.ld
) {
2605 if ( (ads_s
= ads_init( ads
->server
.realm
, ads
->server
.workgroup
,
2606 ads
->server
.ldap_server
)) == NULL
)
2610 ads_s
->auth
.flags
= ADS_AUTH_ANON_BIND
;
2611 status
= ads_connect( ads_s
);
2612 if ( !ADS_ERR_OK(status
))
2616 /* If the attribute does not exist assume it is a Windows 2000
2617 functional domain */
2619 status
= ads_do_search(ads_s
, "", LDAP_SCOPE_BASE
, "(objectclass=*)", attrs
, &res
);
2620 if (!ADS_ERR_OK(status
)) {
2621 if ( status
.err
.rc
== LDAP_NO_SUCH_ATTRIBUTE
) {
2622 status
= ADS_SUCCESS
;
2627 if ( !ads_pull_uint32(ads_s
, res
, "domainFunctionality", val
) ) {
2628 DEBUG(5,("ads_domain_func_level: Failed to pull the domainFunctionality attribute.\n"));
2630 DEBUG(3,("ads_domain_func_level: %d\n", *val
));
2633 ads_msgfree(ads
, res
);
2636 /* free any temporary ads connections */
2637 if ( ads_s
!= ads
) {
2638 ads_destroy( &ads_s
);
2645 * find the domain sid for our domain
2646 * @param ads connection to ads server
2647 * @param sid Pointer to domain sid
2648 * @return status of search
2650 ADS_STATUS
ads_domain_sid(ADS_STRUCT
*ads
, DOM_SID
*sid
)
2652 const char *attrs
[] = {"objectSid", NULL
};
2656 rc
= ads_do_search_retry(ads
, ads
->config
.bind_path
, LDAP_SCOPE_BASE
, "(objectclass=*)",
2658 if (!ADS_ERR_OK(rc
)) return rc
;
2659 if (!ads_pull_sid(ads
, res
, "objectSid", sid
)) {
2660 ads_msgfree(ads
, res
);
2661 return ADS_ERROR_SYSTEM(ENOENT
);
2663 ads_msgfree(ads
, res
);
2669 * find our site name
2670 * @param ads connection to ads server
2671 * @param mem_ctx Pointer to talloc context
2672 * @param site_name Pointer to the sitename
2673 * @return status of search
2675 ADS_STATUS
ads_site_dn(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
, const char **site_name
)
2679 const char *dn
, *service_name
;
2680 const char *attrs
[] = { "dsServiceName", NULL
};
2682 status
= ads_do_search(ads
, "", LDAP_SCOPE_BASE
, "(objectclass=*)", attrs
, &res
);
2683 if (!ADS_ERR_OK(status
)) {
2687 service_name
= ads_pull_string(ads
, mem_ctx
, res
, "dsServiceName");
2688 if (service_name
== NULL
) {
2689 ads_msgfree(ads
, res
);
2690 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED
);
2693 ads_msgfree(ads
, res
);
2695 /* go up three levels */
2696 dn
= ads_parent_dn(ads_parent_dn(ads_parent_dn(service_name
)));
2698 return ADS_ERROR(LDAP_NO_MEMORY
);
2701 *site_name
= talloc_strdup(mem_ctx
, dn
);
2702 if (*site_name
== NULL
) {
2703 return ADS_ERROR(LDAP_NO_MEMORY
);
2708 dsServiceName: CN=NTDS Settings,CN=W2K3DC,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,DC=ber,DC=suse,DC=de
2713 * find the site dn where a machine resides
2714 * @param ads connection to ads server
2715 * @param mem_ctx Pointer to talloc context
2716 * @param computer_name name of the machine
2717 * @param site_name Pointer to the sitename
2718 * @return status of search
2720 ADS_STATUS
ads_site_dn_for_machine(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
, const char *computer_name
, const char **site_dn
)
2724 const char *parent
, *filter
;
2725 char *config_context
= NULL
;
2728 /* shortcut a query */
2729 if (strequal(computer_name
, ads
->config
.ldap_server_name
)) {
2730 return ads_site_dn(ads
, mem_ctx
, site_dn
);
2733 status
= ads_config_path(ads
, mem_ctx
, &config_context
);
2734 if (!ADS_ERR_OK(status
)) {
2738 filter
= talloc_asprintf(mem_ctx
, "(cn=%s)", computer_name
);
2739 if (filter
== NULL
) {
2740 return ADS_ERROR(LDAP_NO_MEMORY
);
2743 status
= ads_do_search(ads
, config_context
, LDAP_SCOPE_SUBTREE
,
2744 filter
, NULL
, &res
);
2745 if (!ADS_ERR_OK(status
)) {
2749 if (ads_count_replies(ads
, res
) != 1) {
2750 ads_msgfree(ads
, res
);
2751 return ADS_ERROR(LDAP_NO_SUCH_OBJECT
);
2754 dn
= ads_get_dn(ads
, res
);
2756 ads_msgfree(ads
, res
);
2757 return ADS_ERROR(LDAP_NO_MEMORY
);
2760 /* go up three levels */
2761 parent
= ads_parent_dn(ads_parent_dn(ads_parent_dn(dn
)));
2762 if (parent
== NULL
) {
2763 ads_msgfree(ads
, res
);
2764 ads_memfree(ads
, dn
);
2765 return ADS_ERROR(LDAP_NO_MEMORY
);
2768 *site_dn
= talloc_strdup(mem_ctx
, parent
);
2769 if (*site_dn
== NULL
) {
2770 ads_msgfree(ads
, res
);
2771 ads_memfree(ads
, dn
);
2772 return ADS_ERROR(LDAP_NO_MEMORY
);
2775 ads_memfree(ads
, dn
);
2776 ads_msgfree(ads
, res
);
2782 * get the upn suffixes for a domain
2783 * @param ads connection to ads server
2784 * @param mem_ctx Pointer to talloc context
2785 * @param suffixes Pointer to an array of suffixes
2786 * @param num_suffixes Pointer to the number of suffixes
2787 * @return status of search
2789 ADS_STATUS
ads_upn_suffixes(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
, char ***suffixes
, size_t *num_suffixes
)
2794 char *config_context
= NULL
;
2795 const char *attrs
[] = { "uPNSuffixes", NULL
};
2797 status
= ads_config_path(ads
, mem_ctx
, &config_context
);
2798 if (!ADS_ERR_OK(status
)) {
2802 base
= talloc_asprintf(mem_ctx
, "cn=Partitions,%s", config_context
);
2804 return ADS_ERROR(LDAP_NO_MEMORY
);
2807 status
= ads_search_dn(ads
, &res
, base
, attrs
);
2808 if (!ADS_ERR_OK(status
)) {
2812 if (ads_count_replies(ads
, res
) != 1) {
2813 ads_msgfree(ads
, res
);
2814 return ADS_ERROR(LDAP_NO_SUCH_OBJECT
);
2817 (*suffixes
) = ads_pull_strings(ads
, mem_ctx
, res
, "uPNSuffixes", num_suffixes
);
2818 if ((*suffixes
) == NULL
) {
2819 ads_msgfree(ads
, res
);
2820 return ADS_ERROR(LDAP_NO_MEMORY
);
2823 ads_msgfree(ads
, res
);
2829 * get the joinable ous for a domain
2830 * @param ads connection to ads server
2831 * @param mem_ctx Pointer to talloc context
2832 * @param ous Pointer to an array of ous
2833 * @param num_ous Pointer to the number of ous
2834 * @return status of search
2836 ADS_STATUS
ads_get_joinable_ous(ADS_STRUCT
*ads
,
2837 TALLOC_CTX
*mem_ctx
,
2842 LDAPMessage
*res
= NULL
;
2843 LDAPMessage
*msg
= NULL
;
2844 const char *attrs
[] = { "dn", NULL
};
2847 status
= ads_search(ads
, &res
,
2848 "(|(objectClass=domain)(objectclass=organizationalUnit))",
2850 if (!ADS_ERR_OK(status
)) {
2854 count
= ads_count_replies(ads
, res
);
2856 ads_msgfree(ads
, res
);
2857 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED
);
2860 for (msg
= ads_first_entry(ads
, res
); msg
;
2861 msg
= ads_next_entry(ads
, msg
)) {
2865 dn
= ads_get_dn(ads
, msg
);
2867 ads_msgfree(ads
, res
);
2868 return ADS_ERROR(LDAP_NO_MEMORY
);
2871 if (!add_string_to_array(mem_ctx
, dn
,
2872 (const char ***)ous
,
2874 ads_memfree(ads
, dn
);
2875 ads_msgfree(ads
, res
);
2876 return ADS_ERROR(LDAP_NO_MEMORY
);
2879 ads_memfree(ads
, dn
);
2882 ads_msgfree(ads
, res
);
2889 * pull a DOM_SID from an extended dn string
2890 * @param mem_ctx TALLOC_CTX
2891 * @param extended_dn string
2892 * @param flags string type of extended_dn
2893 * @param sid pointer to a DOM_SID
2894 * @return boolean inidicating success
2896 bool ads_get_sid_from_extended_dn(TALLOC_CTX
*mem_ctx
,
2897 const char *extended_dn
,
2898 enum ads_extended_dn_flags flags
,
2907 /* otherwise extended_dn gets stripped off */
2908 if ((dn
= talloc_strdup(mem_ctx
, extended_dn
)) == NULL
) {
2912 * ADS_EXTENDED_DN_HEX_STRING:
2913 * <GUID=238e1963cb390f4bb032ba0105525a29>;<SID=010500000000000515000000bb68c8fd6b61b427572eb04556040000>;CN=gd,OU=berlin,OU=suse,DC=ber,DC=suse,DC=de
2915 * ADS_EXTENDED_DN_STRING (only with w2k3):
2916 <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
2919 p
= strchr(dn
, ';');
2924 if (strncmp(p
, ";<SID=", strlen(";<SID=")) != 0) {
2928 p
+= strlen(";<SID=");
2937 DEBUG(100,("ads_get_sid_from_extended_dn: sid string is %s\n", p
));
2941 case ADS_EXTENDED_DN_STRING
:
2942 if (!string_to_sid(sid
, p
)) {
2946 case ADS_EXTENDED_DN_HEX_STRING
: {
2950 buf_len
= strhex_to_str(buf
, sizeof(buf
), p
, strlen(p
));
2955 if (!sid_parse(buf
, buf_len
, sid
)) {
2956 DEBUG(10,("failed to parse sid\n"));
2962 DEBUG(10,("unknown extended dn format\n"));
2970 * pull an array of DOM_SIDs from a ADS result
2971 * @param ads connection to ads server
2972 * @param mem_ctx TALLOC_CTX for allocating sid array
2973 * @param msg Results of search
2974 * @param field Attribute to retrieve
2975 * @param flags string type of extended_dn
2976 * @param sids pointer to sid array to allocate
2977 * @return the count of SIDs pulled
2979 int ads_pull_sids_from_extendeddn(ADS_STRUCT
*ads
,
2980 TALLOC_CTX
*mem_ctx
,
2983 enum ads_extended_dn_flags flags
,
2990 if ((dn_strings
= ads_pull_strings(ads
, mem_ctx
, msg
, field
,
2991 &dn_count
)) == NULL
) {
2995 (*sids
) = TALLOC_ZERO_ARRAY(mem_ctx
, DOM_SID
, dn_count
+ 1);
2997 TALLOC_FREE(dn_strings
);
3001 for (i
=0; i
<dn_count
; i
++) {
3003 if (!ads_get_sid_from_extended_dn(mem_ctx
, dn_strings
[i
],
3004 flags
, &(*sids
)[i
])) {
3006 TALLOC_FREE(dn_strings
);
3011 TALLOC_FREE(dn_strings
);
3016 /********************************************************************
3017 ********************************************************************/
3019 char* ads_get_dnshostname( ADS_STRUCT
*ads
, TALLOC_CTX
*ctx
, const char *machine_name
)
3021 LDAPMessage
*res
= NULL
;
3026 status
= ads_find_machine_acct(ads
, &res
, global_myname());
3027 if (!ADS_ERR_OK(status
)) {
3028 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
3033 if ( (count
= ads_count_replies(ads
, res
)) != 1 ) {
3034 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count
));
3038 if ( (name
= ads_pull_string(ads
, ctx
, res
, "dNSHostName")) == NULL
) {
3039 DEBUG(0,("ads_get_dnshostname: No dNSHostName attribute!\n"));
3043 ads_msgfree(ads
, res
);
3048 /********************************************************************
3049 ********************************************************************/
3051 char* ads_get_upn( ADS_STRUCT
*ads
, TALLOC_CTX
*ctx
, const char *machine_name
)
3053 LDAPMessage
*res
= NULL
;
3058 status
= ads_find_machine_acct(ads
, &res
, machine_name
);
3059 if (!ADS_ERR_OK(status
)) {
3060 DEBUG(0,("ads_get_upn: Failed to find account for %s\n",
3065 if ( (count
= ads_count_replies(ads
, res
)) != 1 ) {
3066 DEBUG(1,("ads_get_upn: %d entries returned!\n", count
));
3070 if ( (name
= ads_pull_string(ads
, ctx
, res
, "userPrincipalName")) == NULL
) {
3071 DEBUG(2,("ads_get_upn: No userPrincipalName attribute!\n"));
3075 ads_msgfree(ads
, res
);
3080 /********************************************************************
3081 ********************************************************************/
3083 char* ads_get_samaccountname( ADS_STRUCT
*ads
, TALLOC_CTX
*ctx
, const char *machine_name
)
3085 LDAPMessage
*res
= NULL
;
3090 status
= ads_find_machine_acct(ads
, &res
, global_myname());
3091 if (!ADS_ERR_OK(status
)) {
3092 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
3097 if ( (count
= ads_count_replies(ads
, res
)) != 1 ) {
3098 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count
));
3102 if ( (name
= ads_pull_string(ads
, ctx
, res
, "sAMAccountName")) == NULL
) {
3103 DEBUG(0,("ads_get_dnshostname: No sAMAccountName attribute!\n"));
3107 ads_msgfree(ads
, res
);
3114 SAVED CODE
- we used to join via ldap
- remember how we did
this. JRA
.
3117 * Join a machine to a realm
3118 * Creates the machine account and sets the machine password
3119 * @param ads connection to ads server
3120 * @param machine name of host to add
3121 * @param org_unit Organizational unit to place machine in
3122 * @return status of join
3124 ADS_STATUS
ads_join_realm(ADS_STRUCT
*ads
, const char *machine_name
,
3125 uint32 account_type
, const char *org_unit
)
3128 LDAPMessage
*res
= NULL
;
3131 /* machine name must be lowercase */
3132 machine
= SMB_STRDUP(machine_name
);
3133 strlower_m(machine
);
3136 status = ads_find_machine_acct(ads, (void **)&res, machine);
3137 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
3138 DEBUG(0, ("Host account for %s already exists - deleting old account\n", machine));
3139 status = ads_leave_realm(ads, machine);
3140 if (!ADS_ERR_OK(status)) {
3141 DEBUG(0, ("Failed to delete host '%s' from the '%s' realm.\n",
3142 machine, ads->config.realm));
3147 status
= ads_add_machine_acct(ads
, machine
, account_type
, org_unit
);
3148 if (!ADS_ERR_OK(status
)) {
3149 DEBUG(0, ("ads_join_realm: ads_add_machine_acct failed (%s): %s\n", machine
, ads_errstr(status
)));
3154 status
= ads_find_machine_acct(ads
, (void **)(void *)&res
, machine
);
3155 if (!ADS_ERR_OK(status
)) {
3156 DEBUG(0, ("ads_join_realm: Host account test failed for machine %s\n", machine
));
3162 ads_msgfree(ads
, res
);
3169 * Delete a machine from the realm
3170 * @param ads connection to ads server
3171 * @param hostname Machine to remove
3172 * @return status of delete
3174 ADS_STATUS
ads_leave_realm(ADS_STRUCT
*ads
, const char *hostname
)
3179 char *hostnameDN
, *host
;
3181 LDAPControl ldap_control
;
3182 LDAPControl
* pldap_control
[2] = {NULL
, NULL
};
3184 pldap_control
[0] = &ldap_control
;
3185 memset(&ldap_control
, 0, sizeof(LDAPControl
));
3186 ldap_control
.ldctl_oid
= (char *)LDAP_SERVER_TREE_DELETE_OID
;
3188 /* hostname must be lowercase */
3189 host
= SMB_STRDUP(hostname
);
3192 status
= ads_find_machine_acct(ads
, &res
, host
);
3193 if (!ADS_ERR_OK(status
)) {
3194 DEBUG(0, ("Host account for %s does not exist.\n", host
));
3199 msg
= ads_first_entry(ads
, res
);
3202 return ADS_ERROR_SYSTEM(ENOENT
);
3205 hostnameDN
= ads_get_dn(ads
, (LDAPMessage
*)msg
);
3207 rc
= ldap_delete_ext_s(ads
->ldap
.ld
, hostnameDN
, pldap_control
, NULL
);
3209 DEBUG(3,("ldap_delete_ext_s failed with error code %d\n", rc
));
3211 DEBUG(3,("ldap_delete_ext_s succeeded with error code %d\n", rc
));
3214 if (rc
!= LDAP_SUCCESS
) {
3215 const char *attrs
[] = { "cn", NULL
};
3216 LDAPMessage
*msg_sub
;
3218 /* we only search with scope ONE, we do not expect any further
3219 * objects to be created deeper */
3221 status
= ads_do_search_retry(ads
, hostnameDN
,
3222 LDAP_SCOPE_ONELEVEL
,
3223 "(objectclass=*)", attrs
, &res
);
3225 if (!ADS_ERR_OK(status
)) {
3227 ads_memfree(ads
, hostnameDN
);
3231 for (msg_sub
= ads_first_entry(ads
, res
); msg_sub
;
3232 msg_sub
= ads_next_entry(ads
, msg_sub
)) {
3236 if ((dn
= ads_get_dn(ads
, msg_sub
)) == NULL
) {
3238 ads_memfree(ads
, hostnameDN
);
3239 return ADS_ERROR(LDAP_NO_MEMORY
);
3242 status
= ads_del_dn(ads
, dn
);
3243 if (!ADS_ERR_OK(status
)) {
3244 DEBUG(3,("failed to delete dn %s: %s\n", dn
, ads_errstr(status
)));
3246 ads_memfree(ads
, dn
);
3247 ads_memfree(ads
, hostnameDN
);
3251 ads_memfree(ads
, dn
);
3254 /* there should be no subordinate objects anymore */
3255 status
= ads_do_search_retry(ads
, hostnameDN
,
3256 LDAP_SCOPE_ONELEVEL
,
3257 "(objectclass=*)", attrs
, &res
);
3259 if (!ADS_ERR_OK(status
) || ( (ads_count_replies(ads
, res
)) > 0 ) ) {
3261 ads_memfree(ads
, hostnameDN
);
3265 /* delete hostnameDN now */
3266 status
= ads_del_dn(ads
, hostnameDN
);
3267 if (!ADS_ERR_OK(status
)) {
3269 DEBUG(3,("failed to delete dn %s: %s\n", hostnameDN
, ads_errstr(status
)));
3270 ads_memfree(ads
, hostnameDN
);
3275 ads_memfree(ads
, hostnameDN
);
3277 status
= ads_find_machine_acct(ads
, &res
, host
);
3278 if (ADS_ERR_OK(status
) && ads_count_replies(ads
, res
) == 1) {
3279 DEBUG(3, ("Failed to remove host account.\n"));
3289 * pull all token-sids from an LDAP dn
3290 * @param ads connection to ads server
3291 * @param mem_ctx TALLOC_CTX for allocating sid array
3292 * @param dn of LDAP object
3293 * @param user_sid pointer to DOM_SID (objectSid)
3294 * @param primary_group_sid pointer to DOM_SID (self composed)
3295 * @param sids pointer to sid array to allocate
3296 * @param num_sids counter of SIDs pulled
3297 * @return status of token query
3299 ADS_STATUS
ads_get_tokensids(ADS_STRUCT
*ads
,
3300 TALLOC_CTX
*mem_ctx
,
3303 DOM_SID
*primary_group_sid
,
3308 LDAPMessage
*res
= NULL
;
3310 size_t tmp_num_sids
;
3312 DOM_SID tmp_user_sid
;
3313 DOM_SID tmp_primary_group_sid
;
3315 const char *attrs
[] = {
3322 status
= ads_search_retry_dn(ads
, &res
, dn
, attrs
);
3323 if (!ADS_ERR_OK(status
)) {
3327 count
= ads_count_replies(ads
, res
);
3329 ads_msgfree(ads
, res
);
3330 return ADS_ERROR_LDAP(LDAP_NO_SUCH_OBJECT
);
3333 if (!ads_pull_sid(ads
, res
, "objectSid", &tmp_user_sid
)) {
3334 ads_msgfree(ads
, res
);
3335 return ADS_ERROR_LDAP(LDAP_NO_MEMORY
);
3338 if (!ads_pull_uint32(ads
, res
, "primaryGroupID", &pgid
)) {
3339 ads_msgfree(ads
, res
);
3340 return ADS_ERROR_LDAP(LDAP_NO_MEMORY
);
3344 /* hack to compose the primary group sid without knowing the
3350 sid_copy(&domsid
, &tmp_user_sid
);
3352 if (!sid_split_rid(&domsid
, &dummy_rid
)) {
3353 ads_msgfree(ads
, res
);
3354 return ADS_ERROR_LDAP(LDAP_NO_MEMORY
);
3357 if (!sid_compose(&tmp_primary_group_sid
, &domsid
, pgid
)) {
3358 ads_msgfree(ads
, res
);
3359 return ADS_ERROR_LDAP(LDAP_NO_MEMORY
);
3363 tmp_num_sids
= ads_pull_sids(ads
, mem_ctx
, res
, "tokenGroups", &tmp_sids
);
3365 if (tmp_num_sids
== 0 || !tmp_sids
) {
3366 ads_msgfree(ads
, res
);
3367 return ADS_ERROR_LDAP(LDAP_NO_MEMORY
);
3371 *num_sids
= tmp_num_sids
;
3379 *user_sid
= tmp_user_sid
;
3382 if (primary_group_sid
) {
3383 *primary_group_sid
= tmp_primary_group_sid
;
3386 DEBUG(10,("ads_get_tokensids: returned %d sids\n", (int)tmp_num_sids
+ 2));
3388 ads_msgfree(ads
, res
);
3389 return ADS_ERROR_LDAP(LDAP_SUCCESS
);
3393 * Find a sAMAccoutName in LDAP
3394 * @param ads connection to ads server
3395 * @param mem_ctx TALLOC_CTX for allocating sid array
3396 * @param samaccountname to search
3397 * @param uac_ret uint32 pointer userAccountControl attribute value
3398 * @param dn_ret pointer to dn
3399 * @return status of token query
3401 ADS_STATUS
ads_find_samaccount(ADS_STRUCT
*ads
,
3402 TALLOC_CTX
*mem_ctx
,
3403 const char *samaccountname
,
3405 const char **dn_ret
)
3408 const char *attrs
[] = { "userAccountControl", NULL
};
3410 LDAPMessage
*res
= NULL
;
3414 filter
= talloc_asprintf(mem_ctx
, "(&(objectclass=user)(sAMAccountName=%s))",
3416 if (filter
== NULL
) {
3417 status
= ADS_ERROR_NT(NT_STATUS_NO_MEMORY
);
3421 status
= ads_do_search_all(ads
, ads
->config
.bind_path
,
3423 filter
, attrs
, &res
);
3425 if (!ADS_ERR_OK(status
)) {
3429 if (ads_count_replies(ads
, res
) != 1) {
3430 status
= ADS_ERROR(LDAP_NO_RESULTS_RETURNED
);
3434 dn
= ads_get_dn(ads
, res
);
3436 status
= ADS_ERROR(LDAP_NO_MEMORY
);
3440 if (!ads_pull_uint32(ads
, res
, "userAccountControl", &uac
)) {
3441 status
= ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE
);
3450 *dn_ret
= talloc_strdup(mem_ctx
, dn
);
3452 status
= ADS_ERROR(LDAP_NO_MEMORY
);
3457 ads_memfree(ads
, dn
);
3458 ads_msgfree(ads
, res
);
3464 * find our configuration path
3465 * @param ads connection to ads server
3466 * @param mem_ctx Pointer to talloc context
3467 * @param config_path Pointer to the config path
3468 * @return status of search
3470 ADS_STATUS
ads_config_path(ADS_STRUCT
*ads
,
3471 TALLOC_CTX
*mem_ctx
,
3475 LDAPMessage
*res
= NULL
;
3476 const char *config_context
= NULL
;
3477 const char *attrs
[] = { "configurationNamingContext", NULL
};
3479 status
= ads_do_search(ads
, "", LDAP_SCOPE_BASE
,
3480 "(objectclass=*)", attrs
, &res
);
3481 if (!ADS_ERR_OK(status
)) {
3485 config_context
= ads_pull_string(ads
, mem_ctx
, res
,
3486 "configurationNamingContext");
3487 ads_msgfree(ads
, res
);
3488 if (!config_context
) {
3489 return ADS_ERROR(LDAP_NO_MEMORY
);
3493 *config_path
= talloc_strdup(mem_ctx
, config_context
);
3494 if (!*config_path
) {
3495 return ADS_ERROR(LDAP_NO_MEMORY
);
3499 return ADS_ERROR(LDAP_SUCCESS
);
3503 * find the displayName of an extended right
3504 * @param ads connection to ads server
3505 * @param config_path The config path
3506 * @param mem_ctx Pointer to talloc context
3507 * @param GUID struct of the rightsGUID
3508 * @return status of search
3510 const char *ads_get_extended_right_name_by_guid(ADS_STRUCT
*ads
,
3511 const char *config_path
,
3512 TALLOC_CTX
*mem_ctx
,
3513 const struct GUID
*rights_guid
)
3516 LDAPMessage
*res
= NULL
;
3518 const char *attrs
[] = { "displayName", NULL
};
3519 const char *result
= NULL
;
3522 if (!ads
|| !mem_ctx
|| !rights_guid
) {
3526 expr
= talloc_asprintf(mem_ctx
, "(rightsGuid=%s)",
3527 smb_uuid_string(mem_ctx
, *rights_guid
));
3532 path
= talloc_asprintf(mem_ctx
, "cn=Extended-Rights,%s", config_path
);
3537 rc
= ads_do_search_retry(ads
, path
, LDAP_SCOPE_SUBTREE
,
3539 if (!ADS_ERR_OK(rc
)) {
3543 if (ads_count_replies(ads
, res
) != 1) {
3547 result
= ads_pull_string(ads
, mem_ctx
, res
, "displayName");
3550 ads_msgfree(ads
, res
);
3556 * verify or build and verify an account ou
3557 * @param mem_ctx Pointer to talloc context
3558 * @param ads connection to ads server
3560 * @return status of search
3563 ADS_STATUS
ads_check_ou_dn(TALLOC_CTX
*mem_ctx
,
3565 const char *account_ou
)
3567 struct ldb_dn
*name_dn
= NULL
;
3568 const char *name
= NULL
;
3569 char *ou_string
= NULL
;
3571 name_dn
= ldb_dn_explode(mem_ctx
, account_ou
);
3576 ou_string
= ads_ou_string(ads
, account_ou
);
3578 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX
);
3581 name
= talloc_asprintf(mem_ctx
, "%s,%s", ou_string
,
3582 ads
->config
.bind_path
);
3583 SAFE_FREE(ou_string
);
3585 return ADS_ERROR_LDAP(LDAP_NO_MEMORY
);
3588 name_dn
= ldb_dn_explode(mem_ctx
, name
);
3590 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX
);
3593 account_ou
= talloc_strdup(mem_ctx
, name
);
3595 return ADS_ERROR_LDAP(LDAP_NO_MEMORY
);