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
& NBT_SERVER_CLOSEST
) {
155 DEBUG(10,("ads_closest_dc: NBT_SERVER_CLOSEST flag set\n"));
159 /* not sure if this can ever happen */
160 if (ads_sitename_match(ads
)) {
161 DEBUG(10,("ads_closest_dc: NBT_SERVER_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 nbt_cldap_netlogon_5 cldap_reply
;
180 TALLOC_CTX
*mem_ctx
= NULL
;
183 if (!server
|| !*server
) {
187 DEBUG(5,("ads_try_connect: sending CLDAP request to %s (realm: %s)\n",
188 server
, ads
->server
.realm
));
190 mem_ctx
= talloc_init("ads_try_connect");
192 DEBUG(0,("out of memory\n"));
196 /* this copes with inet_ntoa brokenness */
198 srv
= SMB_STRDUP(server
);
200 ZERO_STRUCT( cldap_reply
);
202 if ( !ads_cldap_netlogon_5(mem_ctx
, srv
, ads
->server
.realm
, &cldap_reply
) ) {
203 DEBUG(3,("ads_try_connect: CLDAP request %s failed.\n", srv
));
208 /* Check the CLDAP reply flags */
210 if ( !(cldap_reply
.server_type
& NBT_SERVER_LDAP
) ) {
211 DEBUG(1,("ads_try_connect: %s's CLDAP reply says it is not an LDAP server!\n",
217 /* Fill in the ads->config values */
219 SAFE_FREE(ads
->config
.realm
);
220 SAFE_FREE(ads
->config
.bind_path
);
221 SAFE_FREE(ads
->config
.ldap_server_name
);
222 SAFE_FREE(ads
->config
.server_site_name
);
223 SAFE_FREE(ads
->config
.client_site_name
);
224 SAFE_FREE(ads
->server
.workgroup
);
226 ads
->config
.flags
= cldap_reply
.server_type
;
227 ads
->config
.ldap_server_name
= SMB_STRDUP(cldap_reply
.pdc_dns_name
);
228 ads
->config
.realm
= SMB_STRDUP(cldap_reply
.dns_domain
);
229 strupper_m(ads
->config
.realm
);
230 ads
->config
.bind_path
= ads_build_dn(ads
->config
.realm
);
231 if (*cldap_reply
.server_site
) {
232 ads
->config
.server_site_name
=
233 SMB_STRDUP(cldap_reply
.server_site
);
235 if (*cldap_reply
.client_site
) {
236 ads
->config
.client_site_name
=
237 SMB_STRDUP(cldap_reply
.client_site
);
239 ads
->server
.workgroup
= SMB_STRDUP(cldap_reply
.domain
);
241 ads
->ldap
.port
= LDAP_PORT
;
242 if (!interpret_string_addr(&ads
->ldap
.ss
, srv
, 0)) {
243 DEBUG(1,("ads_try_connect: unable to convert %s "
250 /* Store our site name. */
251 sitename_store( cldap_reply
.domain
, cldap_reply
.client_site
);
252 sitename_store( cldap_reply
.dns_domain
, cldap_reply
.client_site
);
257 TALLOC_FREE(mem_ctx
);
262 /**********************************************************************
263 Try to find an AD dc using our internal name resolution routines
264 Try the realm first and then then workgroup name if netbios is not
266 **********************************************************************/
268 static NTSTATUS
ads_find_dc(ADS_STRUCT
*ads
)
272 struct ip_service
*ip_list
;
274 bool got_realm
= False
;
275 bool use_own_domain
= False
;
277 NTSTATUS status
= NT_STATUS_UNSUCCESSFUL
;
279 /* if the realm and workgroup are both empty, assume they are ours */
282 c_realm
= ads
->server
.realm
;
284 if ( !c_realm
|| !*c_realm
) {
285 /* special case where no realm and no workgroup means our own */
286 if ( !ads
->server
.workgroup
|| !*ads
->server
.workgroup
) {
287 use_own_domain
= True
;
288 c_realm
= lp_realm();
292 if (c_realm
&& *c_realm
)
295 /* we need to try once with the realm name and fallback to the
296 netbios domain name if we fail (if netbios has not been disabled */
298 if ( !got_realm
&& !lp_disable_netbios() ) {
299 c_realm
= ads
->server
.workgroup
;
300 if (!c_realm
|| !*c_realm
) {
301 if ( use_own_domain
)
302 c_realm
= lp_workgroup();
305 if ( !c_realm
|| !*c_realm
) {
306 DEBUG(0,("ads_find_dc: no realm or workgroup! Don't know what to do\n"));
307 return NT_STATUS_INVALID_PARAMETER
; /* rather need MISSING_PARAMETER ... */
313 sitename
= sitename_fetch(realm
);
317 DEBUG(6,("ads_find_dc: looking for %s '%s'\n",
318 (got_realm
? "realm" : "domain"), realm
));
320 status
= get_sorted_dc_list(realm
, sitename
, &ip_list
, &count
, got_realm
);
321 if (!NT_STATUS_IS_OK(status
)) {
322 /* fall back to netbios if we can */
323 if ( got_realm
&& !lp_disable_netbios() ) {
332 /* if we fail this loop, then giveup since all the IP addresses returned were dead */
333 for ( i
=0; i
<count
; i
++ ) {
334 char server
[INET6_ADDRSTRLEN
];
336 print_sockaddr(server
, sizeof(server
), &ip_list
[i
].ss
);
338 if ( !NT_STATUS_IS_OK(check_negative_conn_cache(realm
, server
)) )
342 /* realm in this case is a workgroup name. We need
343 to ignore any IP addresses in the negative connection
344 cache that match ip addresses returned in the ad realm
345 case. It sucks that I have to reproduce the logic above... */
346 c_realm
= ads
->server
.realm
;
347 if ( !c_realm
|| !*c_realm
) {
348 if ( !ads
->server
.workgroup
|| !*ads
->server
.workgroup
) {
349 c_realm
= lp_realm();
352 if (c_realm
&& *c_realm
&&
353 !NT_STATUS_IS_OK(check_negative_conn_cache(c_realm
, server
))) {
354 /* Ensure we add the workgroup name for this
355 IP address as negative too. */
356 add_failed_connection_entry( realm
, server
, NT_STATUS_UNSUCCESSFUL
);
361 if ( ads_try_connect(ads
, server
) ) {
367 /* keep track of failures */
368 add_failed_connection_entry( realm
, server
, NT_STATUS_UNSUCCESSFUL
);
373 /* In case we failed to contact one of our closest DC on our site we
374 * need to try to find another DC, retry with a site-less SRV DNS query
378 DEBUG(1,("ads_find_dc: failed to find a valid DC on our site (%s), "
379 "trying to find another DC\n", sitename
));
381 namecache_delete(realm
, 0x1C);
385 return NT_STATUS_NO_LOGON_SERVERS
;
390 * Connect to the LDAP server
391 * @param ads Pointer to an existing ADS_STRUCT
392 * @return status of connection
394 ADS_STATUS
ads_connect(ADS_STRUCT
*ads
)
396 int version
= LDAP_VERSION3
;
399 char addr
[INET6_ADDRSTRLEN
];
401 ZERO_STRUCT(ads
->ldap
);
402 ads
->ldap
.last_attempt
= time(NULL
);
403 ads
->ldap
.wrap_type
= ADS_SASLWRAP_TYPE_PLAIN
;
405 /* try with a user specified server */
407 if (DEBUGLEVEL
>= 11) {
408 char *s
= NDR_PRINT_STRUCT_STRING(talloc_tos(), ads_struct
, ads
);
409 DEBUG(11,("ads_connect: entering\n"));
410 DEBUGADD(11,("%s\n", s
));
414 if (ads
->server
.ldap_server
&&
415 ads_try_connect(ads
, ads
->server
.ldap_server
)) {
419 ntstatus
= ads_find_dc(ads
);
420 if (NT_STATUS_IS_OK(ntstatus
)) {
424 status
= ADS_ERROR_NT(ntstatus
);
429 print_sockaddr(addr
, sizeof(addr
), &ads
->ldap
.ss
);
430 DEBUG(3,("Successfully contacted LDAP server %s\n", addr
));
432 if (!ads
->auth
.user_name
) {
433 /* Must use the userPrincipalName value here or sAMAccountName
434 and not servicePrincipalName; found by Guenther Deschner */
436 asprintf(&ads
->auth
.user_name
, "%s$", global_myname() );
439 if (!ads
->auth
.realm
) {
440 ads
->auth
.realm
= SMB_STRDUP(ads
->config
.realm
);
443 if (!ads
->auth
.kdc_server
) {
444 print_sockaddr(addr
, sizeof(addr
), &ads
->ldap
.ss
);
445 ads
->auth
.kdc_server
= SMB_STRDUP(addr
);
449 /* this is a really nasty hack to avoid ADS DNS problems. It needs a patch
450 to MIT kerberos to work (tridge) */
453 asprintf(&env
, "KRB5_KDC_ADDRESS_%s", ads
->config
.realm
);
454 setenv(env
, ads
->auth
.kdc_server
, 1);
459 /* If the caller() requested no LDAP bind, then we are done */
461 if (ads
->auth
.flags
& ADS_AUTH_NO_BIND
) {
462 status
= ADS_SUCCESS
;
466 ads
->ldap
.mem_ctx
= talloc_init("ads LDAP connection memory");
467 if (!ads
->ldap
.mem_ctx
) {
468 status
= ADS_ERROR_NT(NT_STATUS_NO_MEMORY
);
472 /* Otherwise setup the TCP LDAP session */
474 ads
->ldap
.ld
= ldap_open_with_timeout(ads
->config
.ldap_server_name
,
475 LDAP_PORT
, lp_ldap_timeout());
476 if (ads
->ldap
.ld
== NULL
) {
477 status
= ADS_ERROR(LDAP_OPERATIONS_ERROR
);
480 DEBUG(3,("Connected to LDAP server %s\n", ads
->config
.ldap_server_name
));
482 /* cache the successful connection for workgroup and realm */
483 if (ads_closest_dc(ads
)) {
484 print_sockaddr(addr
, sizeof(addr
), &ads
->ldap
.ss
);
485 saf_store( ads
->server
.workgroup
, addr
);
486 saf_store( ads
->server
.realm
, addr
);
489 ldap_set_option(ads
->ldap
.ld
, LDAP_OPT_PROTOCOL_VERSION
, &version
);
491 status
= ADS_ERROR(smb_ldap_start_tls(ads
->ldap
.ld
, version
));
492 if (!ADS_ERR_OK(status
)) {
496 /* fill in the current time and offsets */
498 status
= ads_current_time( ads
);
499 if ( !ADS_ERR_OK(status
) ) {
503 /* Now do the bind */
505 if (ads
->auth
.flags
& ADS_AUTH_ANON_BIND
) {
506 status
= ADS_ERROR(ldap_simple_bind_s(ads
->ldap
.ld
, NULL
, NULL
));
510 if (ads
->auth
.flags
& ADS_AUTH_SIMPLE_BIND
) {
511 status
= ADS_ERROR(ldap_simple_bind_s(ads
->ldap
.ld
, ads
->auth
.user_name
, ads
->auth
.password
));
515 status
= ads_sasl_bind(ads
);
518 if (DEBUGLEVEL
>= 11) {
519 char *s
= NDR_PRINT_STRUCT_STRING(talloc_tos(), ads_struct
, ads
);
520 DEBUG(11,("ads_connect: leaving with: %s\n",
521 ads_errstr(status
)));
522 DEBUGADD(11,("%s\n", s
));
530 * Disconnect the LDAP server
531 * @param ads Pointer to an existing ADS_STRUCT
533 void ads_disconnect(ADS_STRUCT
*ads
)
536 ldap_unbind(ads
->ldap
.ld
);
539 if (ads
->ldap
.wrap_ops
&& ads
->ldap
.wrap_ops
->disconnect
) {
540 ads
->ldap
.wrap_ops
->disconnect(ads
);
542 if (ads
->ldap
.mem_ctx
) {
543 talloc_free(ads
->ldap
.mem_ctx
);
545 ZERO_STRUCT(ads
->ldap
);
549 Duplicate a struct berval into talloc'ed memory
551 static struct berval
*dup_berval(TALLOC_CTX
*ctx
, const struct berval
*in_val
)
553 struct berval
*value
;
555 if (!in_val
) return NULL
;
557 value
= TALLOC_ZERO_P(ctx
, struct berval
);
560 if (in_val
->bv_len
== 0) return value
;
562 value
->bv_len
= in_val
->bv_len
;
563 value
->bv_val
= (char *)TALLOC_MEMDUP(ctx
, in_val
->bv_val
,
569 Make a values list out of an array of (struct berval *)
571 static struct berval
**ads_dup_values(TALLOC_CTX
*ctx
,
572 const struct berval
**in_vals
)
574 struct berval
**values
;
577 if (!in_vals
) return NULL
;
578 for (i
=0; in_vals
[i
]; i
++)
580 values
= TALLOC_ZERO_ARRAY(ctx
, struct berval
*, i
+1);
581 if (!values
) return NULL
;
583 for (i
=0; in_vals
[i
]; i
++) {
584 values
[i
] = dup_berval(ctx
, in_vals
[i
]);
590 UTF8-encode a values list out of an array of (char *)
592 static char **ads_push_strvals(TALLOC_CTX
*ctx
, const char **in_vals
)
598 if (!in_vals
) return NULL
;
599 for (i
=0; in_vals
[i
]; i
++)
601 values
= TALLOC_ZERO_ARRAY(ctx
, char *, i
+1);
602 if (!values
) return NULL
;
604 for (i
=0; in_vals
[i
]; i
++) {
605 if (!push_utf8_talloc(ctx
, &values
[i
], in_vals
[i
], &size
)) {
614 Pull a (char *) array out of a UTF8-encoded values list
616 static char **ads_pull_strvals(TALLOC_CTX
*ctx
, const char **in_vals
)
620 size_t converted_size
;
622 if (!in_vals
) return NULL
;
623 for (i
=0; in_vals
[i
]; i
++)
625 values
= TALLOC_ZERO_ARRAY(ctx
, char *, i
+1);
626 if (!values
) return NULL
;
628 for (i
=0; in_vals
[i
]; i
++) {
629 if (!pull_utf8_talloc(ctx
, &values
[i
], in_vals
[i
],
631 DEBUG(0,("ads_pull_strvals: pull_utf8_talloc failed: "
632 "%s", strerror(errno
)));
639 * Do a search with paged results. cookie must be null on the first
640 * call, and then returned on each subsequent call. It will be null
641 * again when the entire search is complete
642 * @param ads connection to ads server
643 * @param bind_path Base dn for the search
644 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
645 * @param expr Search expression - specified in local charset
646 * @param attrs Attributes to retrieve - specified in utf8 or ascii
647 * @param res ** which will contain results - free res* with ads_msgfree()
648 * @param count Number of entries retrieved on this page
649 * @param cookie The paged results cookie to be returned on subsequent calls
650 * @return status of search
652 static ADS_STATUS
ads_do_paged_search_args(ADS_STRUCT
*ads
,
653 const char *bind_path
,
654 int scope
, const char *expr
,
655 const char **attrs
, void *args
,
657 int *count
, struct berval
**cookie
)
660 char *utf8_expr
, *utf8_path
, **search_attrs
;
661 size_t converted_size
;
662 LDAPControl PagedResults
, NoReferrals
, ExternalCtrl
, *controls
[4], **rcontrols
;
663 BerElement
*cookie_be
= NULL
;
664 struct berval
*cookie_bv
= NULL
;
665 BerElement
*ext_be
= NULL
;
666 struct berval
*ext_bv
= NULL
;
669 ads_control
*external_control
= (ads_control
*) args
;
673 if (!(ctx
= talloc_init("ads_do_paged_search_args")))
674 return ADS_ERROR(LDAP_NO_MEMORY
);
676 /* 0 means the conversion worked but the result was empty
677 so we only fail if it's -1. In any case, it always
678 at least nulls out the dest */
679 if (!push_utf8_talloc(ctx
, &utf8_expr
, expr
, &converted_size
) ||
680 !push_utf8_talloc(ctx
, &utf8_path
, bind_path
, &converted_size
))
686 if (!attrs
|| !(*attrs
))
689 /* This would be the utf8-encoded version...*/
690 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
691 if (!(str_list_copy(talloc_tos(), &search_attrs
, attrs
))) {
697 /* Paged results only available on ldap v3 or later */
698 ldap_get_option(ads
->ldap
.ld
, LDAP_OPT_PROTOCOL_VERSION
, &version
);
699 if (version
< LDAP_VERSION3
) {
700 rc
= LDAP_NOT_SUPPORTED
;
704 cookie_be
= ber_alloc_t(LBER_USE_DER
);
706 ber_printf(cookie_be
, "{iO}", (ber_int_t
) 1000, *cookie
);
707 ber_bvfree(*cookie
); /* don't need it from last time */
710 ber_printf(cookie_be
, "{io}", (ber_int_t
) 1000, "", 0);
712 ber_flatten(cookie_be
, &cookie_bv
);
713 PagedResults
.ldctl_oid
= CONST_DISCARD(char *, ADS_PAGE_CTL_OID
);
714 PagedResults
.ldctl_iscritical
= (char) 1;
715 PagedResults
.ldctl_value
.bv_len
= cookie_bv
->bv_len
;
716 PagedResults
.ldctl_value
.bv_val
= cookie_bv
->bv_val
;
718 NoReferrals
.ldctl_oid
= CONST_DISCARD(char *, ADS_NO_REFERRALS_OID
);
719 NoReferrals
.ldctl_iscritical
= (char) 0;
720 NoReferrals
.ldctl_value
.bv_len
= 0;
721 NoReferrals
.ldctl_value
.bv_val
= CONST_DISCARD(char *, "");
723 if (external_control
&&
724 (strequal(external_control
->control
, ADS_EXTENDED_DN_OID
) ||
725 strequal(external_control
->control
, ADS_SD_FLAGS_OID
))) {
727 ExternalCtrl
.ldctl_oid
= CONST_DISCARD(char *, external_control
->control
);
728 ExternalCtrl
.ldctl_iscritical
= (char) external_control
->critical
;
730 /* win2k does not accept a ldctl_value beeing passed in */
732 if (external_control
->val
!= 0) {
734 if ((ext_be
= ber_alloc_t(LBER_USE_DER
)) == NULL
) {
739 if ((ber_printf(ext_be
, "{i}", (ber_int_t
) external_control
->val
)) == -1) {
743 if ((ber_flatten(ext_be
, &ext_bv
)) == -1) {
748 ExternalCtrl
.ldctl_value
.bv_len
= ext_bv
->bv_len
;
749 ExternalCtrl
.ldctl_value
.bv_val
= ext_bv
->bv_val
;
752 ExternalCtrl
.ldctl_value
.bv_len
= 0;
753 ExternalCtrl
.ldctl_value
.bv_val
= NULL
;
756 controls
[0] = &NoReferrals
;
757 controls
[1] = &PagedResults
;
758 controls
[2] = &ExternalCtrl
;
762 controls
[0] = &NoReferrals
;
763 controls
[1] = &PagedResults
;
767 /* we need to disable referrals as the openldap libs don't
768 handle them and paged results at the same time. Using them
769 together results in the result record containing the server
770 page control being removed from the result list (tridge/jmcd)
772 leaving this in despite the control that says don't generate
773 referrals, in case the server doesn't support it (jmcd)
775 ldap_set_option(ads
->ldap
.ld
, LDAP_OPT_REFERRALS
, LDAP_OPT_OFF
);
777 rc
= ldap_search_with_timeout(ads
->ldap
.ld
, utf8_path
, scope
, utf8_expr
,
778 search_attrs
, 0, controls
,
780 (LDAPMessage
**)res
);
782 ber_free(cookie_be
, 1);
783 ber_bvfree(cookie_bv
);
786 DEBUG(3,("ads_do_paged_search_args: ldap_search_with_timeout(%s) -> %s\n", expr
,
787 ldap_err2string(rc
)));
791 rc
= ldap_parse_result(ads
->ldap
.ld
, *res
, NULL
, NULL
, NULL
,
792 NULL
, &rcontrols
, 0);
798 for (i
=0; rcontrols
[i
]; i
++) {
799 if (strcmp(ADS_PAGE_CTL_OID
, rcontrols
[i
]->ldctl_oid
) == 0) {
800 cookie_be
= ber_init(&rcontrols
[i
]->ldctl_value
);
801 ber_scanf(cookie_be
,"{iO}", (ber_int_t
*) count
,
803 /* the berval is the cookie, but must be freed when
805 if (cookie_bv
->bv_len
) /* still more to do */
806 *cookie
=ber_bvdup(cookie_bv
);
809 ber_bvfree(cookie_bv
);
810 ber_free(cookie_be
, 1);
814 ldap_controls_free(rcontrols
);
827 /* if/when we decide to utf8-encode attrs, take out this next line */
828 TALLOC_FREE(search_attrs
);
830 return ADS_ERROR(rc
);
833 static ADS_STATUS
ads_do_paged_search(ADS_STRUCT
*ads
, const char *bind_path
,
834 int scope
, const char *expr
,
835 const char **attrs
, LDAPMessage
**res
,
836 int *count
, struct berval
**cookie
)
838 return ads_do_paged_search_args(ads
, bind_path
, scope
, expr
, attrs
, NULL
, res
, count
, cookie
);
843 * Get all results for a search. This uses ads_do_paged_search() to return
844 * all entries in a large search.
845 * @param ads connection to ads server
846 * @param bind_path Base dn for the search
847 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
848 * @param expr Search expression
849 * @param attrs Attributes to retrieve
850 * @param res ** which will contain results - free res* with ads_msgfree()
851 * @return status of search
853 ADS_STATUS
ads_do_search_all_args(ADS_STRUCT
*ads
, const char *bind_path
,
854 int scope
, const char *expr
,
855 const char **attrs
, void *args
,
858 struct berval
*cookie
= NULL
;
863 status
= ads_do_paged_search_args(ads
, bind_path
, scope
, expr
, attrs
, args
, res
,
866 if (!ADS_ERR_OK(status
))
869 #ifdef HAVE_LDAP_ADD_RESULT_ENTRY
871 LDAPMessage
*res2
= NULL
;
873 LDAPMessage
*msg
, *next
;
875 status2
= ads_do_paged_search_args(ads
, bind_path
, scope
, expr
,
876 attrs
, args
, &res2
, &count
, &cookie
);
878 if (!ADS_ERR_OK(status2
)) break;
880 /* this relies on the way that ldap_add_result_entry() works internally. I hope
881 that this works on all ldap libs, but I have only tested with openldap */
882 for (msg
= ads_first_message(ads
, res2
); msg
; msg
= next
) {
883 next
= ads_next_message(ads
, msg
);
884 ldap_add_result_entry((LDAPMessage
**)res
, msg
);
886 /* note that we do not free res2, as the memory is now
887 part of the main returned list */
890 DEBUG(0, ("no ldap_add_result_entry() support in LDAP libs!\n"));
891 status
= ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL
);
897 ADS_STATUS
ads_do_search_all(ADS_STRUCT
*ads
, const char *bind_path
,
898 int scope
, const char *expr
,
899 const char **attrs
, LDAPMessage
**res
)
901 return ads_do_search_all_args(ads
, bind_path
, scope
, expr
, attrs
, NULL
, res
);
904 ADS_STATUS
ads_do_search_all_sd_flags(ADS_STRUCT
*ads
, const char *bind_path
,
905 int scope
, const char *expr
,
906 const char **attrs
, uint32 sd_flags
,
911 args
.control
= ADS_SD_FLAGS_OID
;
913 args
.critical
= True
;
915 return ads_do_search_all_args(ads
, bind_path
, scope
, expr
, attrs
, &args
, res
);
920 * Run a function on all results for a search. Uses ads_do_paged_search() and
921 * runs the function as each page is returned, using ads_process_results()
922 * @param ads connection to ads server
923 * @param bind_path Base dn for the search
924 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
925 * @param expr Search expression - specified in local charset
926 * @param attrs Attributes to retrieve - specified in UTF-8 or ascii
927 * @param fn Function which takes attr name, values list, and data_area
928 * @param data_area Pointer which is passed to function on each call
929 * @return status of search
931 ADS_STATUS
ads_do_search_all_fn(ADS_STRUCT
*ads
, const char *bind_path
,
932 int scope
, const char *expr
, const char **attrs
,
933 bool (*fn
)(ADS_STRUCT
*, char *, void **, void *),
936 struct berval
*cookie
= NULL
;
941 status
= ads_do_paged_search(ads
, bind_path
, scope
, expr
, attrs
, &res
,
944 if (!ADS_ERR_OK(status
)) return status
;
946 ads_process_results(ads
, res
, fn
, data_area
);
947 ads_msgfree(ads
, res
);
950 status
= ads_do_paged_search(ads
, bind_path
, scope
, expr
, attrs
,
951 &res
, &count
, &cookie
);
953 if (!ADS_ERR_OK(status
)) break;
955 ads_process_results(ads
, res
, fn
, data_area
);
956 ads_msgfree(ads
, res
);
963 * Do a search with a timeout.
964 * @param ads connection to ads server
965 * @param bind_path Base dn for the search
966 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
967 * @param expr Search expression
968 * @param attrs Attributes to retrieve
969 * @param res ** which will contain results - free res* with ads_msgfree()
970 * @return status of search
972 ADS_STATUS
ads_do_search(ADS_STRUCT
*ads
, const char *bind_path
, int scope
,
974 const char **attrs
, LDAPMessage
**res
)
977 char *utf8_expr
, *utf8_path
, **search_attrs
= NULL
;
978 size_t converted_size
;
982 if (!(ctx
= talloc_init("ads_do_search"))) {
983 DEBUG(1,("ads_do_search: talloc_init() failed!"));
984 return ADS_ERROR(LDAP_NO_MEMORY
);
987 /* 0 means the conversion worked but the result was empty
988 so we only fail if it's negative. In any case, it always
989 at least nulls out the dest */
990 if (!push_utf8_talloc(ctx
, &utf8_expr
, expr
, &converted_size
) ||
991 !push_utf8_talloc(ctx
, &utf8_path
, bind_path
, &converted_size
))
993 DEBUG(1,("ads_do_search: push_utf8_talloc() failed!"));
998 if (!attrs
|| !(*attrs
))
1001 /* This would be the utf8-encoded version...*/
1002 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
1003 if (!(str_list_copy(talloc_tos(), &search_attrs
, attrs
)))
1005 DEBUG(1,("ads_do_search: str_list_copy() failed!"));
1006 rc
= LDAP_NO_MEMORY
;
1011 /* see the note in ads_do_paged_search - we *must* disable referrals */
1012 ldap_set_option(ads
->ldap
.ld
, LDAP_OPT_REFERRALS
, LDAP_OPT_OFF
);
1014 rc
= ldap_search_with_timeout(ads
->ldap
.ld
, utf8_path
, scope
, utf8_expr
,
1015 search_attrs
, 0, NULL
, NULL
,
1017 (LDAPMessage
**)res
);
1019 if (rc
== LDAP_SIZELIMIT_EXCEEDED
) {
1020 DEBUG(3,("Warning! sizelimit exceeded in ldap. Truncating.\n"));
1025 talloc_destroy(ctx
);
1026 /* if/when we decide to utf8-encode attrs, take out this next line */
1027 TALLOC_FREE(search_attrs
);
1028 return ADS_ERROR(rc
);
1031 * Do a general ADS search
1032 * @param ads connection to ads server
1033 * @param res ** which will contain results - free res* with ads_msgfree()
1034 * @param expr Search expression
1035 * @param attrs Attributes to retrieve
1036 * @return status of search
1038 ADS_STATUS
ads_search(ADS_STRUCT
*ads
, LDAPMessage
**res
,
1039 const char *expr
, const char **attrs
)
1041 return ads_do_search(ads
, ads
->config
.bind_path
, LDAP_SCOPE_SUBTREE
,
1046 * Do a search on a specific DistinguishedName
1047 * @param ads connection to ads server
1048 * @param res ** which will contain results - free res* with ads_msgfree()
1049 * @param dn DistinguishName to search
1050 * @param attrs Attributes to retrieve
1051 * @return status of search
1053 ADS_STATUS
ads_search_dn(ADS_STRUCT
*ads
, LDAPMessage
**res
,
1054 const char *dn
, const char **attrs
)
1056 return ads_do_search(ads
, dn
, LDAP_SCOPE_BASE
, "(objectclass=*)",
1061 * Free up memory from a ads_search
1062 * @param ads connection to ads server
1063 * @param msg Search results to free
1065 void ads_msgfree(ADS_STRUCT
*ads
, LDAPMessage
*msg
)
1072 * Free up memory from various ads requests
1073 * @param ads connection to ads server
1074 * @param mem Area to free
1076 void ads_memfree(ADS_STRUCT
*ads
, void *mem
)
1082 * Get a dn from search results
1083 * @param ads connection to ads server
1084 * @param msg Search result
1087 char *ads_get_dn(ADS_STRUCT
*ads
, LDAPMessage
*msg
)
1089 char *utf8_dn
, *unix_dn
;
1090 size_t converted_size
;
1092 utf8_dn
= ldap_get_dn(ads
->ldap
.ld
, msg
);
1095 DEBUG (5, ("ads_get_dn: ldap_get_dn failed\n"));
1099 if (!pull_utf8_allocate(&unix_dn
, utf8_dn
, &converted_size
)) {
1100 DEBUG(0,("ads_get_dn: string conversion failure utf8 [%s]\n",
1104 ldap_memfree(utf8_dn
);
1109 * Get the parent from a dn
1110 * @param dn the dn to return the parent from
1111 * @return parent dn string
1113 char *ads_parent_dn(const char *dn
)
1121 p
= strchr(dn
, ',');
1131 * Find a machine account given a hostname
1132 * @param ads connection to ads server
1133 * @param res ** which will contain results - free res* with ads_msgfree()
1134 * @param host Hostname to search for
1135 * @return status of search
1137 ADS_STATUS
ads_find_machine_acct(ADS_STRUCT
*ads
, LDAPMessage
**res
,
1138 const char *machine
)
1142 const char *attrs
[] = {"*", "nTSecurityDescriptor", NULL
};
1146 /* the easiest way to find a machine account anywhere in the tree
1147 is to look for hostname$ */
1148 if (asprintf(&expr
, "(samAccountName=%s$)", machine
) == -1) {
1149 DEBUG(1, ("asprintf failed!\n"));
1150 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY
);
1153 status
= ads_search(ads
, res
, expr
, attrs
);
1159 * Initialize a list of mods to be used in a modify request
1160 * @param ctx An initialized TALLOC_CTX
1161 * @return allocated ADS_MODLIST
1163 ADS_MODLIST
ads_init_mods(TALLOC_CTX
*ctx
)
1165 #define ADS_MODLIST_ALLOC_SIZE 10
1168 if ((mods
= TALLOC_ZERO_ARRAY(ctx
, LDAPMod
*, ADS_MODLIST_ALLOC_SIZE
+ 1)))
1169 /* -1 is safety to make sure we don't go over the end.
1170 need to reset it to NULL before doing ldap modify */
1171 mods
[ADS_MODLIST_ALLOC_SIZE
] = (LDAPMod
*) -1;
1173 return (ADS_MODLIST
)mods
;
1178 add an attribute to the list, with values list already constructed
1180 static ADS_STATUS
ads_modlist_add(TALLOC_CTX
*ctx
, ADS_MODLIST
*mods
,
1181 int mod_op
, const char *name
,
1182 const void *_invals
)
1184 const void **invals
= (const void **)_invals
;
1186 LDAPMod
**modlist
= (LDAPMod
**) *mods
;
1187 struct berval
**ber_values
= NULL
;
1188 char **char_values
= NULL
;
1191 mod_op
= LDAP_MOD_DELETE
;
1193 if (mod_op
& LDAP_MOD_BVALUES
)
1194 ber_values
= ads_dup_values(ctx
,
1195 (const struct berval
**)invals
);
1197 char_values
= ads_push_strvals(ctx
,
1198 (const char **) invals
);
1201 /* find the first empty slot */
1202 for (curmod
=0; modlist
[curmod
] && modlist
[curmod
] != (LDAPMod
*) -1;
1204 if (modlist
[curmod
] == (LDAPMod
*) -1) {
1205 if (!(modlist
= TALLOC_REALLOC_ARRAY(ctx
, modlist
, LDAPMod
*,
1206 curmod
+ADS_MODLIST_ALLOC_SIZE
+1)))
1207 return ADS_ERROR(LDAP_NO_MEMORY
);
1208 memset(&modlist
[curmod
], 0,
1209 ADS_MODLIST_ALLOC_SIZE
*sizeof(LDAPMod
*));
1210 modlist
[curmod
+ADS_MODLIST_ALLOC_SIZE
] = (LDAPMod
*) -1;
1211 *mods
= (ADS_MODLIST
)modlist
;
1214 if (!(modlist
[curmod
] = TALLOC_ZERO_P(ctx
, LDAPMod
)))
1215 return ADS_ERROR(LDAP_NO_MEMORY
);
1216 modlist
[curmod
]->mod_type
= talloc_strdup(ctx
, name
);
1217 if (mod_op
& LDAP_MOD_BVALUES
) {
1218 modlist
[curmod
]->mod_bvalues
= ber_values
;
1219 } else if (mod_op
& LDAP_MOD_DELETE
) {
1220 modlist
[curmod
]->mod_values
= NULL
;
1222 modlist
[curmod
]->mod_values
= char_values
;
1225 modlist
[curmod
]->mod_op
= mod_op
;
1226 return ADS_ERROR(LDAP_SUCCESS
);
1230 * Add a single string value to a mod list
1231 * @param ctx An initialized TALLOC_CTX
1232 * @param mods An initialized ADS_MODLIST
1233 * @param name The attribute name to add
1234 * @param val The value to add - NULL means DELETE
1235 * @return ADS STATUS indicating success of add
1237 ADS_STATUS
ads_mod_str(TALLOC_CTX
*ctx
, ADS_MODLIST
*mods
,
1238 const char *name
, const char *val
)
1240 const char *values
[2];
1246 return ads_modlist_add(ctx
, mods
, LDAP_MOD_DELETE
, name
, NULL
);
1247 return ads_modlist_add(ctx
, mods
, LDAP_MOD_REPLACE
, name
, values
);
1251 * Add an array of string values to a mod list
1252 * @param ctx An initialized TALLOC_CTX
1253 * @param mods An initialized ADS_MODLIST
1254 * @param name The attribute name to add
1255 * @param vals The array of string values to add - NULL means DELETE
1256 * @return ADS STATUS indicating success of add
1258 ADS_STATUS
ads_mod_strlist(TALLOC_CTX
*ctx
, ADS_MODLIST
*mods
,
1259 const char *name
, const char **vals
)
1262 return ads_modlist_add(ctx
, mods
, LDAP_MOD_DELETE
, name
, NULL
);
1263 return ads_modlist_add(ctx
, mods
, LDAP_MOD_REPLACE
,
1264 name
, (const void **) vals
);
1269 * Add a single ber-encoded value to a mod list
1270 * @param ctx An initialized TALLOC_CTX
1271 * @param mods An initialized ADS_MODLIST
1272 * @param name The attribute name to add
1273 * @param val The value to add - NULL means DELETE
1274 * @return ADS STATUS indicating success of add
1276 static ADS_STATUS
ads_mod_ber(TALLOC_CTX
*ctx
, ADS_MODLIST
*mods
,
1277 const char *name
, const struct berval
*val
)
1279 const struct berval
*values
[2];
1284 return ads_modlist_add(ctx
, mods
, LDAP_MOD_DELETE
, name
, NULL
);
1285 return ads_modlist_add(ctx
, mods
, LDAP_MOD_REPLACE
|LDAP_MOD_BVALUES
,
1286 name
, (const void **) values
);
1291 * Perform an ldap modify
1292 * @param ads connection to ads server
1293 * @param mod_dn DistinguishedName to modify
1294 * @param mods list of modifications to perform
1295 * @return status of modify
1297 ADS_STATUS
ads_gen_mod(ADS_STRUCT
*ads
, const char *mod_dn
, ADS_MODLIST mods
)
1300 char *utf8_dn
= NULL
;
1301 size_t converted_size
;
1303 this control is needed to modify that contains a currently
1304 non-existent attribute (but allowable for the object) to run
1306 LDAPControl PermitModify
= {
1307 CONST_DISCARD(char *, ADS_PERMIT_MODIFY_OID
),
1310 LDAPControl
*controls
[2];
1312 controls
[0] = &PermitModify
;
1315 if (!push_utf8_allocate(&utf8_dn
, mod_dn
, &converted_size
)) {
1316 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY
);
1319 /* find the end of the list, marked by NULL or -1 */
1320 for(i
=0;(mods
[i
]!=0)&&(mods
[i
]!=(LDAPMod
*) -1);i
++);
1321 /* make sure the end of the list is NULL */
1323 ret
= ldap_modify_ext_s(ads
->ldap
.ld
, utf8_dn
,
1324 (LDAPMod
**) mods
, controls
, NULL
);
1326 return ADS_ERROR(ret
);
1330 * Perform an ldap add
1331 * @param ads connection to ads server
1332 * @param new_dn DistinguishedName to add
1333 * @param mods list of attributes and values for DN
1334 * @return status of add
1336 ADS_STATUS
ads_gen_add(ADS_STRUCT
*ads
, const char *new_dn
, ADS_MODLIST mods
)
1339 char *utf8_dn
= NULL
;
1340 size_t converted_size
;
1342 if (!push_utf8_allocate(&utf8_dn
, new_dn
, &converted_size
)) {
1343 DEBUG(1, ("ads_gen_add: push_utf8_allocate failed!"));
1344 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY
);
1347 /* find the end of the list, marked by NULL or -1 */
1348 for(i
=0;(mods
[i
]!=0)&&(mods
[i
]!=(LDAPMod
*) -1);i
++);
1349 /* make sure the end of the list is NULL */
1352 ret
= ldap_add_s(ads
->ldap
.ld
, utf8_dn
, (LDAPMod
**)mods
);
1354 return ADS_ERROR(ret
);
1358 * Delete a DistinguishedName
1359 * @param ads connection to ads server
1360 * @param new_dn DistinguishedName to delete
1361 * @return status of delete
1363 ADS_STATUS
ads_del_dn(ADS_STRUCT
*ads
, char *del_dn
)
1366 char *utf8_dn
= NULL
;
1367 size_t converted_size
;
1368 if (!push_utf8_allocate(&utf8_dn
, del_dn
, &converted_size
)) {
1369 DEBUG(1, ("ads_del_dn: push_utf8_allocate failed!"));
1370 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY
);
1373 ret
= ldap_delete_s(ads
->ldap
.ld
, utf8_dn
);
1375 return ADS_ERROR(ret
);
1379 * Build an org unit string
1380 * if org unit is Computers or blank then assume a container, otherwise
1381 * assume a / separated list of organisational units.
1382 * jmcd: '\' is now used for escapes so certain chars can be in the ou (e.g. #)
1383 * @param ads connection to ads server
1384 * @param org_unit Organizational unit
1385 * @return org unit string - caller must free
1387 char *ads_ou_string(ADS_STRUCT
*ads
, const char *org_unit
)
1391 if (!org_unit
|| !*org_unit
) {
1393 ret
= ads_default_ou_string(ads
, WELL_KNOWN_GUID_COMPUTERS
);
1395 /* samba4 might not yet respond to a wellknownobject-query */
1396 return ret
? ret
: SMB_STRDUP("cn=Computers");
1399 if (strequal(org_unit
, "Computers")) {
1400 return SMB_STRDUP("cn=Computers");
1403 /* jmcd: removed "\\" from the separation chars, because it is
1404 needed as an escape for chars like '#' which are valid in an
1406 return ads_build_path(org_unit
, "/", "ou=", 1);
1410 * Get a org unit string for a well-known GUID
1411 * @param ads connection to ads server
1412 * @param wknguid Well known GUID
1413 * @return org unit string - caller must free
1415 char *ads_default_ou_string(ADS_STRUCT
*ads
, const char *wknguid
)
1418 LDAPMessage
*res
= NULL
;
1419 char *base
, *wkn_dn
= NULL
, *ret
= NULL
, **wkn_dn_exp
= NULL
,
1420 **bind_dn_exp
= NULL
;
1421 const char *attrs
[] = {"distinguishedName", NULL
};
1422 int new_ln
, wkn_ln
, bind_ln
, i
;
1424 if (wknguid
== NULL
) {
1428 if (asprintf(&base
, "<WKGUID=%s,%s>", wknguid
, ads
->config
.bind_path
) == -1) {
1429 DEBUG(1, ("asprintf failed!\n"));
1433 status
= ads_search_dn(ads
, &res
, base
, attrs
);
1434 if (!ADS_ERR_OK(status
)) {
1435 DEBUG(1,("Failed while searching for: %s\n", base
));
1439 if (ads_count_replies(ads
, res
) != 1) {
1443 /* substitute the bind-path from the well-known-guid-search result */
1444 wkn_dn
= ads_get_dn(ads
, res
);
1449 wkn_dn_exp
= ldap_explode_dn(wkn_dn
, 0);
1454 bind_dn_exp
= ldap_explode_dn(ads
->config
.bind_path
, 0);
1459 for (wkn_ln
=0; wkn_dn_exp
[wkn_ln
]; wkn_ln
++)
1461 for (bind_ln
=0; bind_dn_exp
[bind_ln
]; bind_ln
++)
1464 new_ln
= wkn_ln
- bind_ln
;
1466 ret
= SMB_STRDUP(wkn_dn_exp
[0]);
1471 for (i
=1; i
< new_ln
; i
++) {
1474 if (asprintf(&s
, "%s,%s", ret
, wkn_dn_exp
[i
]) == -1) {
1480 ret
= SMB_STRDUP(s
);
1489 ads_msgfree(ads
, res
);
1490 ads_memfree(ads
, wkn_dn
);
1492 ldap_value_free(wkn_dn_exp
);
1495 ldap_value_free(bind_dn_exp
);
1502 * Adds (appends) an item to an attribute array, rather then
1503 * replacing the whole list
1504 * @param ctx An initialized TALLOC_CTX
1505 * @param mods An initialized ADS_MODLIST
1506 * @param name name of the ldap attribute to append to
1507 * @param vals an array of values to add
1508 * @return status of addition
1511 ADS_STATUS
ads_add_strlist(TALLOC_CTX
*ctx
, ADS_MODLIST
*mods
,
1512 const char *name
, const char **vals
)
1514 return ads_modlist_add(ctx
, mods
, LDAP_MOD_ADD
, name
,
1515 (const void *) vals
);
1519 * Determines the an account's current KVNO via an LDAP lookup
1520 * @param ads An initialized ADS_STRUCT
1521 * @param account_name the NT samaccountname.
1522 * @return the kvno for the account, or -1 in case of a failure.
1525 uint32
ads_get_kvno(ADS_STRUCT
*ads
, const char *account_name
)
1527 LDAPMessage
*res
= NULL
;
1528 uint32 kvno
= (uint32
)-1; /* -1 indicates a failure */
1530 const char *attrs
[] = {"msDS-KeyVersionNumber", NULL
};
1531 char *dn_string
= NULL
;
1532 ADS_STATUS ret
= ADS_ERROR(LDAP_SUCCESS
);
1534 DEBUG(5,("ads_get_kvno: Searching for account %s\n", account_name
));
1535 if (asprintf(&filter
, "(samAccountName=%s)", account_name
) == -1) {
1538 ret
= ads_search(ads
, &res
, filter
, attrs
);
1540 if (!ADS_ERR_OK(ret
) || (ads_count_replies(ads
, res
) != 1)) {
1541 DEBUG(1,("ads_get_kvno: Account for %s not found.\n", account_name
));
1542 ads_msgfree(ads
, res
);
1546 dn_string
= ads_get_dn(ads
, res
);
1548 DEBUG(0,("ads_get_kvno: out of memory.\n"));
1549 ads_msgfree(ads
, res
);
1552 DEBUG(5,("ads_get_kvno: Using: %s\n", dn_string
));
1553 ads_memfree(ads
, dn_string
);
1555 /* ---------------------------------------------------------
1556 * 0 is returned as a default KVNO from this point on...
1557 * This is done because Windows 2000 does not support key
1558 * version numbers. Chances are that a failure in the next
1559 * step is simply due to Windows 2000 being used for a
1560 * domain controller. */
1563 if (!ads_pull_uint32(ads
, res
, "msDS-KeyVersionNumber", &kvno
)) {
1564 DEBUG(3,("ads_get_kvno: Error Determining KVNO!\n"));
1565 DEBUG(3,("ads_get_kvno: Windows 2000 does not support KVNO's, so this may be normal.\n"));
1566 ads_msgfree(ads
, res
);
1571 DEBUG(5,("ads_get_kvno: Looked Up KVNO of: %d\n", kvno
));
1572 ads_msgfree(ads
, res
);
1577 * Determines the computer account's current KVNO via an LDAP lookup
1578 * @param ads An initialized ADS_STRUCT
1579 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1580 * @return the kvno for the computer account, or -1 in case of a failure.
1583 uint32_t ads_get_machine_kvno(ADS_STRUCT
*ads
, const char *machine_name
)
1585 char *computer_account
= NULL
;
1588 if (asprintf(&computer_account
, "%s$", machine_name
) < 0) {
1592 kvno
= ads_get_kvno(ads
, computer_account
);
1593 free(computer_account
);
1599 * This clears out all registered spn's for a given hostname
1600 * @param ads An initilaized ADS_STRUCT
1601 * @param machine_name the NetBIOS name of the computer.
1602 * @return 0 upon success, non-zero otherwise.
1605 ADS_STATUS
ads_clear_service_principal_names(ADS_STRUCT
*ads
, const char *machine_name
)
1608 LDAPMessage
*res
= NULL
;
1610 const char *servicePrincipalName
[1] = {NULL
};
1611 ADS_STATUS ret
= ADS_ERROR(LDAP_SUCCESS
);
1612 char *dn_string
= NULL
;
1614 ret
= ads_find_machine_acct(ads
, &res
, machine_name
);
1615 if (!ADS_ERR_OK(ret
) || ads_count_replies(ads
, res
) != 1) {
1616 DEBUG(5,("ads_clear_service_principal_names: WARNING: Host Account for %s not found... skipping operation.\n", machine_name
));
1617 DEBUG(5,("ads_clear_service_principal_names: WARNING: Service Principals for %s have NOT been cleared.\n", machine_name
));
1618 ads_msgfree(ads
, res
);
1619 return ADS_ERROR(LDAP_NO_SUCH_OBJECT
);
1622 DEBUG(5,("ads_clear_service_principal_names: Host account for %s found\n", machine_name
));
1623 ctx
= talloc_init("ads_clear_service_principal_names");
1625 ads_msgfree(ads
, res
);
1626 return ADS_ERROR(LDAP_NO_MEMORY
);
1629 if (!(mods
= ads_init_mods(ctx
))) {
1630 talloc_destroy(ctx
);
1631 ads_msgfree(ads
, res
);
1632 return ADS_ERROR(LDAP_NO_MEMORY
);
1634 ret
= ads_mod_strlist(ctx
, &mods
, "servicePrincipalName", servicePrincipalName
);
1635 if (!ADS_ERR_OK(ret
)) {
1636 DEBUG(1,("ads_clear_service_principal_names: Error creating strlist.\n"));
1637 ads_msgfree(ads
, res
);
1638 talloc_destroy(ctx
);
1641 dn_string
= ads_get_dn(ads
, res
);
1643 talloc_destroy(ctx
);
1644 ads_msgfree(ads
, res
);
1645 return ADS_ERROR(LDAP_NO_MEMORY
);
1647 ret
= ads_gen_mod(ads
, dn_string
, mods
);
1648 ads_memfree(ads
,dn_string
);
1649 if (!ADS_ERR_OK(ret
)) {
1650 DEBUG(1,("ads_clear_service_principal_names: Error: Updating Service Principals for machine %s in LDAP\n",
1652 ads_msgfree(ads
, res
);
1653 talloc_destroy(ctx
);
1657 ads_msgfree(ads
, res
);
1658 talloc_destroy(ctx
);
1663 * This adds a service principal name to an existing computer account
1664 * (found by hostname) in AD.
1665 * @param ads An initialized ADS_STRUCT
1666 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1667 * @param my_fqdn The fully qualified DNS name of the machine
1668 * @param spn A string of the service principal to add, i.e. 'host'
1669 * @return 0 upon sucess, or non-zero if a failure occurs
1672 ADS_STATUS
ads_add_service_principal_name(ADS_STRUCT
*ads
, const char *machine_name
,
1673 const char *my_fqdn
, const char *spn
)
1677 LDAPMessage
*res
= NULL
;
1680 char *dn_string
= NULL
;
1681 const char *servicePrincipalName
[3] = {NULL
, NULL
, NULL
};
1683 ret
= ads_find_machine_acct(ads
, &res
, machine_name
);
1684 if (!ADS_ERR_OK(ret
) || ads_count_replies(ads
, res
) != 1) {
1685 DEBUG(1,("ads_add_service_principal_name: WARNING: Host Account for %s not found... skipping operation.\n",
1687 DEBUG(1,("ads_add_service_principal_name: WARNING: Service Principal '%s/%s@%s' has NOT been added.\n",
1688 spn
, machine_name
, ads
->config
.realm
));
1689 ads_msgfree(ads
, res
);
1690 return ADS_ERROR(LDAP_NO_SUCH_OBJECT
);
1693 DEBUG(1,("ads_add_service_principal_name: Host account for %s found\n", machine_name
));
1694 if (!(ctx
= talloc_init("ads_add_service_principal_name"))) {
1695 ads_msgfree(ads
, res
);
1696 return ADS_ERROR(LDAP_NO_MEMORY
);
1699 /* add short name spn */
1701 if ( (psp1
= talloc_asprintf(ctx
, "%s/%s", spn
, machine_name
)) == NULL
) {
1702 talloc_destroy(ctx
);
1703 ads_msgfree(ads
, res
);
1704 return ADS_ERROR(LDAP_NO_MEMORY
);
1707 strlower_m(&psp1
[strlen(spn
)]);
1708 servicePrincipalName
[0] = psp1
;
1710 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n",
1711 psp1
, machine_name
));
1714 /* add fully qualified spn */
1716 if ( (psp2
= talloc_asprintf(ctx
, "%s/%s", spn
, my_fqdn
)) == NULL
) {
1717 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
1721 strlower_m(&psp2
[strlen(spn
)]);
1722 servicePrincipalName
[1] = psp2
;
1724 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n",
1725 psp2
, machine_name
));
1727 if ( (mods
= ads_init_mods(ctx
)) == NULL
) {
1728 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
1732 ret
= ads_add_strlist(ctx
, &mods
, "servicePrincipalName", servicePrincipalName
);
1733 if (!ADS_ERR_OK(ret
)) {
1734 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
1738 if ( (dn_string
= ads_get_dn(ads
, res
)) == NULL
) {
1739 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
1743 ret
= ads_gen_mod(ads
, dn_string
, mods
);
1744 ads_memfree(ads
,dn_string
);
1745 if (!ADS_ERR_OK(ret
)) {
1746 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
1752 ads_msgfree(ads
, res
);
1757 * adds a machine account to the ADS server
1758 * @param ads An intialized ADS_STRUCT
1759 * @param machine_name - the NetBIOS machine name of this account.
1760 * @param account_type A number indicating the type of account to create
1761 * @param org_unit The LDAP path in which to place this account
1762 * @return 0 upon success, or non-zero otherwise
1765 ADS_STATUS
ads_create_machine_acct(ADS_STRUCT
*ads
, const char *machine_name
,
1766 const char *org_unit
)
1769 char *samAccountName
, *controlstr
;
1772 char *machine_escaped
= NULL
;
1774 const char *objectClass
[] = {"top", "person", "organizationalPerson",
1775 "user", "computer", NULL
};
1776 LDAPMessage
*res
= NULL
;
1777 uint32 acct_control
= ( UF_WORKSTATION_TRUST_ACCOUNT
|\
1778 UF_DONT_EXPIRE_PASSWD
|\
1779 UF_ACCOUNTDISABLE
);
1781 if (!(ctx
= talloc_init("ads_add_machine_acct")))
1782 return ADS_ERROR(LDAP_NO_MEMORY
);
1784 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
1786 machine_escaped
= escape_rdn_val_string_alloc(machine_name
);
1787 if (!machine_escaped
) {
1791 new_dn
= talloc_asprintf(ctx
, "cn=%s,%s", machine_escaped
, org_unit
);
1792 samAccountName
= talloc_asprintf(ctx
, "%s$", machine_name
);
1794 if ( !new_dn
|| !samAccountName
) {
1798 #ifndef ENCTYPE_ARCFOUR_HMAC
1799 acct_control
|= UF_USE_DES_KEY_ONLY
;
1802 if (!(controlstr
= talloc_asprintf(ctx
, "%u", acct_control
))) {
1806 if (!(mods
= ads_init_mods(ctx
))) {
1810 ads_mod_str(ctx
, &mods
, "cn", machine_name
);
1811 ads_mod_str(ctx
, &mods
, "sAMAccountName", samAccountName
);
1812 ads_mod_strlist(ctx
, &mods
, "objectClass", objectClass
);
1813 ads_mod_str(ctx
, &mods
, "userAccountControl", controlstr
);
1815 ret
= ads_gen_add(ads
, new_dn
, mods
);
1818 SAFE_FREE(machine_escaped
);
1819 ads_msgfree(ads
, res
);
1820 talloc_destroy(ctx
);
1826 * move a machine account to another OU on the ADS server
1827 * @param ads - An intialized ADS_STRUCT
1828 * @param machine_name - the NetBIOS machine name of this account.
1829 * @param org_unit - The LDAP path in which to place this account
1830 * @param moved - whether we moved the machine account (optional)
1831 * @return 0 upon success, or non-zero otherwise
1834 ADS_STATUS
ads_move_machine_acct(ADS_STRUCT
*ads
, const char *machine_name
,
1835 const char *org_unit
, bool *moved
)
1839 LDAPMessage
*res
= NULL
;
1840 char *filter
= NULL
;
1841 char *computer_dn
= NULL
;
1843 char *computer_rdn
= NULL
;
1844 bool need_move
= False
;
1846 if (asprintf(&filter
, "(samAccountName=%s$)", machine_name
) == -1) {
1847 rc
= ADS_ERROR(LDAP_NO_MEMORY
);
1851 /* Find pre-existing machine */
1852 rc
= ads_search(ads
, &res
, filter
, NULL
);
1853 if (!ADS_ERR_OK(rc
)) {
1857 computer_dn
= ads_get_dn(ads
, res
);
1859 rc
= ADS_ERROR(LDAP_NO_MEMORY
);
1863 parent_dn
= ads_parent_dn(computer_dn
);
1864 if (strequal(parent_dn
, org_unit
)) {
1870 if (asprintf(&computer_rdn
, "CN=%s", machine_name
) == -1) {
1871 rc
= ADS_ERROR(LDAP_NO_MEMORY
);
1875 ldap_status
= ldap_rename_s(ads
->ldap
.ld
, computer_dn
, computer_rdn
,
1876 org_unit
, 1, NULL
, NULL
);
1877 rc
= ADS_ERROR(ldap_status
);
1880 ads_msgfree(ads
, res
);
1882 SAFE_FREE(computer_dn
);
1883 SAFE_FREE(computer_rdn
);
1885 if (!ADS_ERR_OK(rc
)) {
1897 dump a binary result from ldap
1899 static void dump_binary(ADS_STRUCT
*ads
, const char *field
, struct berval
**values
)
1902 for (i
=0; values
[i
]; i
++) {
1903 printf("%s: ", field
);
1904 for (j
=0; j
<values
[i
]->bv_len
; j
++) {
1905 printf("%02X", (unsigned char)values
[i
]->bv_val
[j
]);
1911 static void dump_guid(ADS_STRUCT
*ads
, const char *field
, struct berval
**values
)
1914 for (i
=0; values
[i
]; i
++) {
1919 memcpy(guid
.info
, values
[i
]->bv_val
, sizeof(guid
.info
));
1920 smb_uuid_unpack(guid
, &tmp
);
1921 printf("%s: %s\n", field
, smb_uuid_string(talloc_tos(), tmp
));
1926 dump a sid result from ldap
1928 static void dump_sid(ADS_STRUCT
*ads
, const char *field
, struct berval
**values
)
1931 for (i
=0; values
[i
]; i
++) {
1934 sid_parse(values
[i
]->bv_val
, values
[i
]->bv_len
, &sid
);
1935 printf("%s: %s\n", field
, sid_to_fstring(tmp
, &sid
));
1940 dump ntSecurityDescriptor
1942 static void dump_sd(ADS_STRUCT
*ads
, const char *filed
, struct berval
**values
)
1944 TALLOC_CTX
*frame
= talloc_stackframe();
1945 struct security_descriptor
*psd
;
1948 status
= unmarshall_sec_desc(talloc_tos(), (uint8
*)values
[0]->bv_val
,
1949 values
[0]->bv_len
, &psd
);
1950 if (!NT_STATUS_IS_OK(status
)) {
1951 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
1952 nt_errstr(status
)));
1958 ads_disp_sd(ads
, talloc_tos(), psd
);
1965 dump a string result from ldap
1967 static void dump_string(const char *field
, char **values
)
1970 for (i
=0; values
[i
]; i
++) {
1971 printf("%s: %s\n", field
, values
[i
]);
1976 dump a field from LDAP on stdout
1980 static bool ads_dump_field(ADS_STRUCT
*ads
, char *field
, void **values
, void *data_area
)
1985 void (*handler
)(ADS_STRUCT
*, const char *, struct berval
**);
1987 {"objectGUID", False
, dump_guid
},
1988 {"netbootGUID", False
, dump_guid
},
1989 {"nTSecurityDescriptor", False
, dump_sd
},
1990 {"dnsRecord", False
, dump_binary
},
1991 {"objectSid", False
, dump_sid
},
1992 {"tokenGroups", False
, dump_sid
},
1993 {"tokenGroupsNoGCAcceptable", False
, dump_sid
},
1994 {"tokengroupsGlobalandUniversal", False
, dump_sid
},
1995 {"mS-DS-CreatorSID", False
, dump_sid
},
1996 {"msExchMailboxGuid", False
, dump_guid
},
2001 if (!field
) { /* must be end of an entry */
2006 for (i
=0; handlers
[i
].name
; i
++) {
2007 if (StrCaseCmp(handlers
[i
].name
, field
) == 0) {
2008 if (!values
) /* first time, indicate string or not */
2009 return handlers
[i
].string
;
2010 handlers
[i
].handler(ads
, field
, (struct berval
**) values
);
2014 if (!handlers
[i
].name
) {
2015 if (!values
) /* first time, indicate string conversion */
2017 dump_string(field
, (char **)values
);
2023 * Dump a result from LDAP on stdout
2024 * used for debugging
2025 * @param ads connection to ads server
2026 * @param res Results to dump
2029 void ads_dump(ADS_STRUCT
*ads
, LDAPMessage
*res
)
2031 ads_process_results(ads
, res
, ads_dump_field
, NULL
);
2035 * Walk through results, calling a function for each entry found.
2036 * The function receives a field name, a berval * array of values,
2037 * and a data area passed through from the start. The function is
2038 * called once with null for field and values at the end of each
2040 * @param ads connection to ads server
2041 * @param res Results to process
2042 * @param fn Function for processing each result
2043 * @param data_area user-defined area to pass to function
2045 void ads_process_results(ADS_STRUCT
*ads
, LDAPMessage
*res
,
2046 bool (*fn
)(ADS_STRUCT
*, char *, void **, void *),
2051 size_t converted_size
;
2053 if (!(ctx
= talloc_init("ads_process_results")))
2056 for (msg
= ads_first_entry(ads
, res
); msg
;
2057 msg
= ads_next_entry(ads
, msg
)) {
2061 for (utf8_field
=ldap_first_attribute(ads
->ldap
.ld
,
2062 (LDAPMessage
*)msg
,&b
);
2064 utf8_field
=ldap_next_attribute(ads
->ldap
.ld
,
2065 (LDAPMessage
*)msg
,b
)) {
2066 struct berval
**ber_vals
;
2067 char **str_vals
, **utf8_vals
;
2071 if (!pull_utf8_talloc(ctx
, &field
, utf8_field
,
2074 DEBUG(0,("ads_process_results: "
2075 "pull_utf8_talloc failed: %s",
2079 string
= fn(ads
, field
, NULL
, data_area
);
2082 utf8_vals
= ldap_get_values(ads
->ldap
.ld
,
2083 (LDAPMessage
*)msg
, field
);
2084 str_vals
= ads_pull_strvals(ctx
,
2085 (const char **) utf8_vals
);
2086 fn(ads
, field
, (void **) str_vals
, data_area
);
2087 ldap_value_free(utf8_vals
);
2089 ber_vals
= ldap_get_values_len(ads
->ldap
.ld
,
2090 (LDAPMessage
*)msg
, field
);
2091 fn(ads
, field
, (void **) ber_vals
, data_area
);
2093 ldap_value_free_len(ber_vals
);
2095 ldap_memfree(utf8_field
);
2098 talloc_free_children(ctx
);
2099 fn(ads
, NULL
, NULL
, data_area
); /* completed an entry */
2102 talloc_destroy(ctx
);
2106 * count how many replies are in a LDAPMessage
2107 * @param ads connection to ads server
2108 * @param res Results to count
2109 * @return number of replies
2111 int ads_count_replies(ADS_STRUCT
*ads
, void *res
)
2113 return ldap_count_entries(ads
->ldap
.ld
, (LDAPMessage
*)res
);
2117 * pull the first entry from a ADS result
2118 * @param ads connection to ads server
2119 * @param res Results of search
2120 * @return first entry from result
2122 LDAPMessage
*ads_first_entry(ADS_STRUCT
*ads
, LDAPMessage
*res
)
2124 return ldap_first_entry(ads
->ldap
.ld
, res
);
2128 * pull the next entry from a ADS result
2129 * @param ads connection to ads server
2130 * @param res Results of search
2131 * @return next entry from result
2133 LDAPMessage
*ads_next_entry(ADS_STRUCT
*ads
, LDAPMessage
*res
)
2135 return ldap_next_entry(ads
->ldap
.ld
, res
);
2139 * pull the first message from a ADS result
2140 * @param ads connection to ads server
2141 * @param res Results of search
2142 * @return first message from result
2144 LDAPMessage
*ads_first_message(ADS_STRUCT
*ads
, LDAPMessage
*res
)
2146 return ldap_first_message(ads
->ldap
.ld
, res
);
2150 * pull the next message from a ADS result
2151 * @param ads connection to ads server
2152 * @param res Results of search
2153 * @return next message from result
2155 LDAPMessage
*ads_next_message(ADS_STRUCT
*ads
, LDAPMessage
*res
)
2157 return ldap_next_message(ads
->ldap
.ld
, res
);
2161 * pull a single string from a ADS result
2162 * @param ads connection to ads server
2163 * @param mem_ctx TALLOC_CTX to use for allocating result string
2164 * @param msg Results of search
2165 * @param field Attribute to retrieve
2166 * @return Result string in talloc context
2168 char *ads_pull_string(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
, LDAPMessage
*msg
,
2174 size_t converted_size
;
2176 values
= ldap_get_values(ads
->ldap
.ld
, msg
, field
);
2180 if (values
[0] && pull_utf8_talloc(mem_ctx
, &ux_string
, values
[0],
2185 ldap_value_free(values
);
2190 * pull an array of strings from a ADS result
2191 * @param ads connection to ads server
2192 * @param mem_ctx TALLOC_CTX to use for allocating result string
2193 * @param msg Results of search
2194 * @param field Attribute to retrieve
2195 * @return Result strings in talloc context
2197 char **ads_pull_strings(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
,
2198 LDAPMessage
*msg
, const char *field
,
2204 size_t converted_size
;
2206 values
= ldap_get_values(ads
->ldap
.ld
, msg
, field
);
2210 *num_values
= ldap_count_values(values
);
2212 ret
= TALLOC_ARRAY(mem_ctx
, char *, *num_values
+ 1);
2214 ldap_value_free(values
);
2218 for (i
=0;i
<*num_values
;i
++) {
2219 if (!pull_utf8_talloc(mem_ctx
, &ret
[i
], values
[i
],
2222 ldap_value_free(values
);
2228 ldap_value_free(values
);
2233 * pull an array of strings from a ADS result
2234 * (handle large multivalue attributes with range retrieval)
2235 * @param ads connection to ads server
2236 * @param mem_ctx TALLOC_CTX to use for allocating result string
2237 * @param msg Results of search
2238 * @param field Attribute to retrieve
2239 * @param current_strings strings returned by a previous call to this function
2240 * @param next_attribute The next query should ask for this attribute
2241 * @param num_values How many values did we get this time?
2242 * @param more_values Are there more values to get?
2243 * @return Result strings in talloc context
2245 char **ads_pull_strings_range(ADS_STRUCT
*ads
,
2246 TALLOC_CTX
*mem_ctx
,
2247 LDAPMessage
*msg
, const char *field
,
2248 char **current_strings
,
2249 const char **next_attribute
,
2250 size_t *num_strings
,
2254 char *expected_range_attrib
, *range_attr
;
2255 BerElement
*ptr
= NULL
;
2258 size_t num_new_strings
;
2259 unsigned long int range_start
;
2260 unsigned long int range_end
;
2262 /* we might have been given the whole lot anyway */
2263 if ((strings
= ads_pull_strings(ads
, mem_ctx
, msg
, field
, num_strings
))) {
2264 *more_strings
= False
;
2268 expected_range_attrib
= talloc_asprintf(mem_ctx
, "%s;Range=", field
);
2270 /* look for Range result */
2271 for (attr
= ldap_first_attribute(ads
->ldap
.ld
, (LDAPMessage
*)msg
, &ptr
);
2273 attr
= ldap_next_attribute(ads
->ldap
.ld
, (LDAPMessage
*)msg
, ptr
)) {
2274 /* we ignore the fact that this is utf8, as all attributes are ascii... */
2275 if (strnequal(attr
, expected_range_attrib
, strlen(expected_range_attrib
))) {
2283 /* nothing here - this field is just empty */
2284 *more_strings
= False
;
2288 if (sscanf(&range_attr
[strlen(expected_range_attrib
)], "%lu-%lu",
2289 &range_start
, &range_end
) == 2) {
2290 *more_strings
= True
;
2292 if (sscanf(&range_attr
[strlen(expected_range_attrib
)], "%lu-*",
2293 &range_start
) == 1) {
2294 *more_strings
= False
;
2296 DEBUG(1, ("ads_pull_strings_range: Cannot parse Range attriubte (%s)\n",
2298 ldap_memfree(range_attr
);
2299 *more_strings
= False
;
2304 if ((*num_strings
) != range_start
) {
2305 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) doesn't start at %u, but at %lu"
2306 " - aborting range retreival\n",
2307 range_attr
, (unsigned int)(*num_strings
) + 1, range_start
));
2308 ldap_memfree(range_attr
);
2309 *more_strings
= False
;
2313 new_strings
= ads_pull_strings(ads
, mem_ctx
, msg
, range_attr
, &num_new_strings
);
2315 if (*more_strings
&& ((*num_strings
+ num_new_strings
) != (range_end
+ 1))) {
2316 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) tells us we have %lu "
2317 "strings in this bunch, but we only got %lu - aborting range retreival\n",
2318 range_attr
, (unsigned long int)range_end
- range_start
+ 1,
2319 (unsigned long int)num_new_strings
));
2320 ldap_memfree(range_attr
);
2321 *more_strings
= False
;
2325 strings
= TALLOC_REALLOC_ARRAY(mem_ctx
, current_strings
, char *,
2326 *num_strings
+ num_new_strings
);
2328 if (strings
== NULL
) {
2329 ldap_memfree(range_attr
);
2330 *more_strings
= False
;
2334 if (new_strings
&& num_new_strings
) {
2335 memcpy(&strings
[*num_strings
], new_strings
,
2336 sizeof(*new_strings
) * num_new_strings
);
2339 (*num_strings
) += num_new_strings
;
2341 if (*more_strings
) {
2342 *next_attribute
= talloc_asprintf(mem_ctx
,
2347 if (!*next_attribute
) {
2348 DEBUG(1, ("talloc_asprintf for next attribute failed!\n"));
2349 ldap_memfree(range_attr
);
2350 *more_strings
= False
;
2355 ldap_memfree(range_attr
);
2361 * pull a single uint32 from a ADS result
2362 * @param ads connection to ads server
2363 * @param msg Results of search
2364 * @param field Attribute to retrieve
2365 * @param v Pointer to int to store result
2366 * @return boolean inidicating success
2368 bool ads_pull_uint32(ADS_STRUCT
*ads
, LDAPMessage
*msg
, const char *field
,
2373 values
= ldap_get_values(ads
->ldap
.ld
, msg
, field
);
2377 ldap_value_free(values
);
2381 *v
= atoi(values
[0]);
2382 ldap_value_free(values
);
2387 * pull a single objectGUID from an ADS result
2388 * @param ads connection to ADS server
2389 * @param msg results of search
2390 * @param guid 37-byte area to receive text guid
2391 * @return boolean indicating success
2393 bool ads_pull_guid(ADS_STRUCT
*ads
, LDAPMessage
*msg
, struct GUID
*guid
)
2396 UUID_FLAT flat_guid
;
2398 values
= ldap_get_values(ads
->ldap
.ld
, msg
, "objectGUID");
2403 memcpy(&flat_guid
.info
, values
[0], sizeof(UUID_FLAT
));
2404 smb_uuid_unpack(flat_guid
, guid
);
2405 ldap_value_free(values
);
2408 ldap_value_free(values
);
2415 * pull a single DOM_SID from a ADS result
2416 * @param ads connection to ads server
2417 * @param msg Results of search
2418 * @param field Attribute to retrieve
2419 * @param sid Pointer to sid to store result
2420 * @return boolean inidicating success
2422 bool ads_pull_sid(ADS_STRUCT
*ads
, LDAPMessage
*msg
, const char *field
,
2425 struct berval
**values
;
2428 values
= ldap_get_values_len(ads
->ldap
.ld
, msg
, field
);
2434 ret
= sid_parse(values
[0]->bv_val
, values
[0]->bv_len
, sid
);
2436 ldap_value_free_len(values
);
2441 * pull an array of DOM_SIDs from a ADS result
2442 * @param ads connection to ads server
2443 * @param mem_ctx TALLOC_CTX for allocating sid array
2444 * @param msg Results of search
2445 * @param field Attribute to retrieve
2446 * @param sids pointer to sid array to allocate
2447 * @return the count of SIDs pulled
2449 int ads_pull_sids(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
,
2450 LDAPMessage
*msg
, const char *field
, DOM_SID
**sids
)
2452 struct berval
**values
;
2456 values
= ldap_get_values_len(ads
->ldap
.ld
, msg
, field
);
2461 for (i
=0; values
[i
]; i
++)
2465 (*sids
) = TALLOC_ARRAY(mem_ctx
, DOM_SID
, i
);
2467 ldap_value_free_len(values
);
2475 for (i
=0; values
[i
]; i
++) {
2476 ret
= sid_parse(values
[i
]->bv_val
, values
[i
]->bv_len
, &(*sids
)[count
]);
2478 DEBUG(10, ("pulling SID: %s\n",
2479 sid_string_dbg(&(*sids
)[count
])));
2484 ldap_value_free_len(values
);
2489 * pull a SEC_DESC from a ADS result
2490 * @param ads connection to ads server
2491 * @param mem_ctx TALLOC_CTX for allocating sid array
2492 * @param msg Results of search
2493 * @param field Attribute to retrieve
2494 * @param sd Pointer to *SEC_DESC to store result (talloc()ed)
2495 * @return boolean inidicating success
2497 bool ads_pull_sd(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
,
2498 LDAPMessage
*msg
, const char *field
, SEC_DESC
**sd
)
2500 struct berval
**values
;
2503 values
= ldap_get_values_len(ads
->ldap
.ld
, msg
, field
);
2505 if (!values
) return false;
2509 status
= unmarshall_sec_desc(mem_ctx
,
2510 (uint8
*)values
[0]->bv_val
,
2511 values
[0]->bv_len
, sd
);
2512 if (!NT_STATUS_IS_OK(status
)) {
2513 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
2514 nt_errstr(status
)));
2519 ldap_value_free_len(values
);
2524 * in order to support usernames longer than 21 characters we need to
2525 * use both the sAMAccountName and the userPrincipalName attributes
2526 * It seems that not all users have the userPrincipalName attribute set
2528 * @param ads connection to ads server
2529 * @param mem_ctx TALLOC_CTX for allocating sid array
2530 * @param msg Results of search
2531 * @return the username
2533 char *ads_pull_username(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
,
2539 /* lookup_name() only works on the sAMAccountName to
2540 returning the username portion of userPrincipalName
2541 breaks winbindd_getpwnam() */
2543 ret
= ads_pull_string(ads
, mem_ctx
, msg
, "userPrincipalName");
2544 if (ret
&& (p
= strchr_m(ret
, '@'))) {
2549 return ads_pull_string(ads
, mem_ctx
, msg
, "sAMAccountName");
2554 * find the update serial number - this is the core of the ldap cache
2555 * @param ads connection to ads server
2556 * @param ads connection to ADS server
2557 * @param usn Pointer to retrieved update serial number
2558 * @return status of search
2560 ADS_STATUS
ads_USN(ADS_STRUCT
*ads
, uint32
*usn
)
2562 const char *attrs
[] = {"highestCommittedUSN", NULL
};
2566 status
= ads_do_search_retry(ads
, "", LDAP_SCOPE_BASE
, "(objectclass=*)", attrs
, &res
);
2567 if (!ADS_ERR_OK(status
))
2570 if (ads_count_replies(ads
, res
) != 1) {
2571 ads_msgfree(ads
, res
);
2572 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED
);
2575 if (!ads_pull_uint32(ads
, res
, "highestCommittedUSN", usn
)) {
2576 ads_msgfree(ads
, res
);
2577 return ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE
);
2580 ads_msgfree(ads
, res
);
2584 /* parse a ADS timestring - typical string is
2585 '20020917091222.0Z0' which means 09:12.22 17th September
2587 static time_t ads_parse_time(const char *str
)
2593 if (sscanf(str
, "%4d%2d%2d%2d%2d%2d",
2594 &tm
.tm_year
, &tm
.tm_mon
, &tm
.tm_mday
,
2595 &tm
.tm_hour
, &tm
.tm_min
, &tm
.tm_sec
) != 6) {
2604 /********************************************************************
2605 ********************************************************************/
2607 ADS_STATUS
ads_current_time(ADS_STRUCT
*ads
)
2609 const char *attrs
[] = {"currentTime", NULL
};
2614 ADS_STRUCT
*ads_s
= ads
;
2616 if (!(ctx
= talloc_init("ads_current_time"))) {
2617 return ADS_ERROR(LDAP_NO_MEMORY
);
2620 /* establish a new ldap tcp session if necessary */
2622 if ( !ads
->ldap
.ld
) {
2623 if ( (ads_s
= ads_init( ads
->server
.realm
, ads
->server
.workgroup
,
2624 ads
->server
.ldap_server
)) == NULL
)
2628 ads_s
->auth
.flags
= ADS_AUTH_ANON_BIND
;
2629 status
= ads_connect( ads_s
);
2630 if ( !ADS_ERR_OK(status
))
2634 status
= ads_do_search(ads_s
, "", LDAP_SCOPE_BASE
, "(objectclass=*)", attrs
, &res
);
2635 if (!ADS_ERR_OK(status
)) {
2639 timestr
= ads_pull_string(ads_s
, ctx
, res
, "currentTime");
2641 ads_msgfree(ads_s
, res
);
2642 status
= ADS_ERROR(LDAP_NO_RESULTS_RETURNED
);
2646 /* but save the time and offset in the original ADS_STRUCT */
2648 ads
->config
.current_time
= ads_parse_time(timestr
);
2650 if (ads
->config
.current_time
!= 0) {
2651 ads
->auth
.time_offset
= ads
->config
.current_time
- time(NULL
);
2652 DEBUG(4,("time offset is %d seconds\n", ads
->auth
.time_offset
));
2655 ads_msgfree(ads
, res
);
2657 status
= ADS_SUCCESS
;
2660 /* free any temporary ads connections */
2661 if ( ads_s
!= ads
) {
2662 ads_destroy( &ads_s
);
2664 talloc_destroy(ctx
);
2669 /********************************************************************
2670 ********************************************************************/
2672 ADS_STATUS
ads_domain_func_level(ADS_STRUCT
*ads
, uint32
*val
)
2674 const char *attrs
[] = {"domainFunctionality", NULL
};
2677 ADS_STRUCT
*ads_s
= ads
;
2679 *val
= DS_DOMAIN_FUNCTION_2000
;
2681 /* establish a new ldap tcp session if necessary */
2683 if ( !ads
->ldap
.ld
) {
2684 if ( (ads_s
= ads_init( ads
->server
.realm
, ads
->server
.workgroup
,
2685 ads
->server
.ldap_server
)) == NULL
)
2689 ads_s
->auth
.flags
= ADS_AUTH_ANON_BIND
;
2690 status
= ads_connect( ads_s
);
2691 if ( !ADS_ERR_OK(status
))
2695 /* If the attribute does not exist assume it is a Windows 2000
2696 functional domain */
2698 status
= ads_do_search(ads_s
, "", LDAP_SCOPE_BASE
, "(objectclass=*)", attrs
, &res
);
2699 if (!ADS_ERR_OK(status
)) {
2700 if ( status
.err
.rc
== LDAP_NO_SUCH_ATTRIBUTE
) {
2701 status
= ADS_SUCCESS
;
2706 if ( !ads_pull_uint32(ads_s
, res
, "domainFunctionality", val
) ) {
2707 DEBUG(5,("ads_domain_func_level: Failed to pull the domainFunctionality attribute.\n"));
2709 DEBUG(3,("ads_domain_func_level: %d\n", *val
));
2712 ads_msgfree(ads
, res
);
2715 /* free any temporary ads connections */
2716 if ( ads_s
!= ads
) {
2717 ads_destroy( &ads_s
);
2724 * find the domain sid for our domain
2725 * @param ads connection to ads server
2726 * @param sid Pointer to domain sid
2727 * @return status of search
2729 ADS_STATUS
ads_domain_sid(ADS_STRUCT
*ads
, DOM_SID
*sid
)
2731 const char *attrs
[] = {"objectSid", NULL
};
2735 rc
= ads_do_search_retry(ads
, ads
->config
.bind_path
, LDAP_SCOPE_BASE
, "(objectclass=*)",
2737 if (!ADS_ERR_OK(rc
)) return rc
;
2738 if (!ads_pull_sid(ads
, res
, "objectSid", sid
)) {
2739 ads_msgfree(ads
, res
);
2740 return ADS_ERROR_SYSTEM(ENOENT
);
2742 ads_msgfree(ads
, res
);
2748 * find our site name
2749 * @param ads connection to ads server
2750 * @param mem_ctx Pointer to talloc context
2751 * @param site_name Pointer to the sitename
2752 * @return status of search
2754 ADS_STATUS
ads_site_dn(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
, const char **site_name
)
2758 const char *dn
, *service_name
;
2759 const char *attrs
[] = { "dsServiceName", NULL
};
2761 status
= ads_do_search(ads
, "", LDAP_SCOPE_BASE
, "(objectclass=*)", attrs
, &res
);
2762 if (!ADS_ERR_OK(status
)) {
2766 service_name
= ads_pull_string(ads
, mem_ctx
, res
, "dsServiceName");
2767 if (service_name
== NULL
) {
2768 ads_msgfree(ads
, res
);
2769 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED
);
2772 ads_msgfree(ads
, res
);
2774 /* go up three levels */
2775 dn
= ads_parent_dn(ads_parent_dn(ads_parent_dn(service_name
)));
2777 return ADS_ERROR(LDAP_NO_MEMORY
);
2780 *site_name
= talloc_strdup(mem_ctx
, dn
);
2781 if (*site_name
== NULL
) {
2782 return ADS_ERROR(LDAP_NO_MEMORY
);
2787 dsServiceName: CN=NTDS Settings,CN=W2K3DC,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,DC=ber,DC=suse,DC=de
2792 * find the site dn where a machine resides
2793 * @param ads connection to ads server
2794 * @param mem_ctx Pointer to talloc context
2795 * @param computer_name name of the machine
2796 * @param site_name Pointer to the sitename
2797 * @return status of search
2799 ADS_STATUS
ads_site_dn_for_machine(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
, const char *computer_name
, const char **site_dn
)
2803 const char *parent
, *filter
;
2804 char *config_context
= NULL
;
2807 /* shortcut a query */
2808 if (strequal(computer_name
, ads
->config
.ldap_server_name
)) {
2809 return ads_site_dn(ads
, mem_ctx
, site_dn
);
2812 status
= ads_config_path(ads
, mem_ctx
, &config_context
);
2813 if (!ADS_ERR_OK(status
)) {
2817 filter
= talloc_asprintf(mem_ctx
, "(cn=%s)", computer_name
);
2818 if (filter
== NULL
) {
2819 return ADS_ERROR(LDAP_NO_MEMORY
);
2822 status
= ads_do_search(ads
, config_context
, LDAP_SCOPE_SUBTREE
,
2823 filter
, NULL
, &res
);
2824 if (!ADS_ERR_OK(status
)) {
2828 if (ads_count_replies(ads
, res
) != 1) {
2829 ads_msgfree(ads
, res
);
2830 return ADS_ERROR(LDAP_NO_SUCH_OBJECT
);
2833 dn
= ads_get_dn(ads
, res
);
2835 ads_msgfree(ads
, res
);
2836 return ADS_ERROR(LDAP_NO_MEMORY
);
2839 /* go up three levels */
2840 parent
= ads_parent_dn(ads_parent_dn(ads_parent_dn(dn
)));
2841 if (parent
== NULL
) {
2842 ads_msgfree(ads
, res
);
2843 ads_memfree(ads
, dn
);
2844 return ADS_ERROR(LDAP_NO_MEMORY
);
2847 *site_dn
= talloc_strdup(mem_ctx
, parent
);
2848 if (*site_dn
== NULL
) {
2849 ads_msgfree(ads
, res
);
2850 ads_memfree(ads
, dn
);
2851 return ADS_ERROR(LDAP_NO_MEMORY
);
2854 ads_memfree(ads
, dn
);
2855 ads_msgfree(ads
, res
);
2861 * get the upn suffixes for a domain
2862 * @param ads connection to ads server
2863 * @param mem_ctx Pointer to talloc context
2864 * @param suffixes Pointer to an array of suffixes
2865 * @param num_suffixes Pointer to the number of suffixes
2866 * @return status of search
2868 ADS_STATUS
ads_upn_suffixes(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
, char ***suffixes
, size_t *num_suffixes
)
2873 char *config_context
= NULL
;
2874 const char *attrs
[] = { "uPNSuffixes", NULL
};
2876 status
= ads_config_path(ads
, mem_ctx
, &config_context
);
2877 if (!ADS_ERR_OK(status
)) {
2881 base
= talloc_asprintf(mem_ctx
, "cn=Partitions,%s", config_context
);
2883 return ADS_ERROR(LDAP_NO_MEMORY
);
2886 status
= ads_search_dn(ads
, &res
, base
, attrs
);
2887 if (!ADS_ERR_OK(status
)) {
2891 if (ads_count_replies(ads
, res
) != 1) {
2892 ads_msgfree(ads
, res
);
2893 return ADS_ERROR(LDAP_NO_SUCH_OBJECT
);
2896 (*suffixes
) = ads_pull_strings(ads
, mem_ctx
, res
, "uPNSuffixes", num_suffixes
);
2897 if ((*suffixes
) == NULL
) {
2898 ads_msgfree(ads
, res
);
2899 return ADS_ERROR(LDAP_NO_MEMORY
);
2902 ads_msgfree(ads
, res
);
2908 * get the joinable ous for a domain
2909 * @param ads connection to ads server
2910 * @param mem_ctx Pointer to talloc context
2911 * @param ous Pointer to an array of ous
2912 * @param num_ous Pointer to the number of ous
2913 * @return status of search
2915 ADS_STATUS
ads_get_joinable_ous(ADS_STRUCT
*ads
,
2916 TALLOC_CTX
*mem_ctx
,
2921 LDAPMessage
*res
= NULL
;
2922 LDAPMessage
*msg
= NULL
;
2923 const char *attrs
[] = { "dn", NULL
};
2926 status
= ads_search(ads
, &res
,
2927 "(|(objectClass=domain)(objectclass=organizationalUnit))",
2929 if (!ADS_ERR_OK(status
)) {
2933 count
= ads_count_replies(ads
, res
);
2935 ads_msgfree(ads
, res
);
2936 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED
);
2939 for (msg
= ads_first_entry(ads
, res
); msg
;
2940 msg
= ads_next_entry(ads
, msg
)) {
2944 dn
= ads_get_dn(ads
, msg
);
2946 ads_msgfree(ads
, res
);
2947 return ADS_ERROR(LDAP_NO_MEMORY
);
2950 if (!add_string_to_array(mem_ctx
, dn
,
2951 (const char ***)ous
,
2953 ads_memfree(ads
, dn
);
2954 ads_msgfree(ads
, res
);
2955 return ADS_ERROR(LDAP_NO_MEMORY
);
2958 ads_memfree(ads
, dn
);
2961 ads_msgfree(ads
, res
);
2968 * pull a DOM_SID from an extended dn string
2969 * @param mem_ctx TALLOC_CTX
2970 * @param extended_dn string
2971 * @param flags string type of extended_dn
2972 * @param sid pointer to a DOM_SID
2973 * @return boolean inidicating success
2975 bool ads_get_sid_from_extended_dn(TALLOC_CTX
*mem_ctx
,
2976 const char *extended_dn
,
2977 enum ads_extended_dn_flags flags
,
2986 /* otherwise extended_dn gets stripped off */
2987 if ((dn
= talloc_strdup(mem_ctx
, extended_dn
)) == NULL
) {
2991 * ADS_EXTENDED_DN_HEX_STRING:
2992 * <GUID=238e1963cb390f4bb032ba0105525a29>;<SID=010500000000000515000000bb68c8fd6b61b427572eb04556040000>;CN=gd,OU=berlin,OU=suse,DC=ber,DC=suse,DC=de
2994 * ADS_EXTENDED_DN_STRING (only with w2k3):
2995 <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
2998 p
= strchr(dn
, ';');
3003 if (strncmp(p
, ";<SID=", strlen(";<SID=")) != 0) {
3007 p
+= strlen(";<SID=");
3016 DEBUG(100,("ads_get_sid_from_extended_dn: sid string is %s\n", p
));
3020 case ADS_EXTENDED_DN_STRING
:
3021 if (!string_to_sid(sid
, p
)) {
3025 case ADS_EXTENDED_DN_HEX_STRING
: {
3029 buf_len
= strhex_to_str(buf
, sizeof(buf
), p
, strlen(p
));
3034 if (!sid_parse(buf
, buf_len
, sid
)) {
3035 DEBUG(10,("failed to parse sid\n"));
3041 DEBUG(10,("unknown extended dn format\n"));
3049 * pull an array of DOM_SIDs from a ADS result
3050 * @param ads connection to ads server
3051 * @param mem_ctx TALLOC_CTX for allocating sid array
3052 * @param msg Results of search
3053 * @param field Attribute to retrieve
3054 * @param flags string type of extended_dn
3055 * @param sids pointer to sid array to allocate
3056 * @return the count of SIDs pulled
3058 int ads_pull_sids_from_extendeddn(ADS_STRUCT
*ads
,
3059 TALLOC_CTX
*mem_ctx
,
3062 enum ads_extended_dn_flags flags
,
3069 if ((dn_strings
= ads_pull_strings(ads
, mem_ctx
, msg
, field
,
3070 &dn_count
)) == NULL
) {
3074 (*sids
) = TALLOC_ZERO_ARRAY(mem_ctx
, DOM_SID
, dn_count
+ 1);
3076 TALLOC_FREE(dn_strings
);
3080 for (i
=0; i
<dn_count
; i
++) {
3082 if (!ads_get_sid_from_extended_dn(mem_ctx
, dn_strings
[i
],
3083 flags
, &(*sids
)[i
])) {
3085 TALLOC_FREE(dn_strings
);
3090 TALLOC_FREE(dn_strings
);
3095 /********************************************************************
3096 ********************************************************************/
3098 char* ads_get_dnshostname( ADS_STRUCT
*ads
, TALLOC_CTX
*ctx
, const char *machine_name
)
3100 LDAPMessage
*res
= NULL
;
3105 status
= ads_find_machine_acct(ads
, &res
, global_myname());
3106 if (!ADS_ERR_OK(status
)) {
3107 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
3112 if ( (count
= ads_count_replies(ads
, res
)) != 1 ) {
3113 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count
));
3117 if ( (name
= ads_pull_string(ads
, ctx
, res
, "dNSHostName")) == NULL
) {
3118 DEBUG(0,("ads_get_dnshostname: No dNSHostName attribute!\n"));
3122 ads_msgfree(ads
, res
);
3127 /********************************************************************
3128 ********************************************************************/
3130 char* ads_get_upn( ADS_STRUCT
*ads
, TALLOC_CTX
*ctx
, const char *machine_name
)
3132 LDAPMessage
*res
= NULL
;
3137 status
= ads_find_machine_acct(ads
, &res
, machine_name
);
3138 if (!ADS_ERR_OK(status
)) {
3139 DEBUG(0,("ads_get_upn: Failed to find account for %s\n",
3144 if ( (count
= ads_count_replies(ads
, res
)) != 1 ) {
3145 DEBUG(1,("ads_get_upn: %d entries returned!\n", count
));
3149 if ( (name
= ads_pull_string(ads
, ctx
, res
, "userPrincipalName")) == NULL
) {
3150 DEBUG(2,("ads_get_upn: No userPrincipalName attribute!\n"));
3154 ads_msgfree(ads
, res
);
3159 /********************************************************************
3160 ********************************************************************/
3162 char* ads_get_samaccountname( ADS_STRUCT
*ads
, TALLOC_CTX
*ctx
, const char *machine_name
)
3164 LDAPMessage
*res
= NULL
;
3169 status
= ads_find_machine_acct(ads
, &res
, global_myname());
3170 if (!ADS_ERR_OK(status
)) {
3171 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
3176 if ( (count
= ads_count_replies(ads
, res
)) != 1 ) {
3177 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count
));
3181 if ( (name
= ads_pull_string(ads
, ctx
, res
, "sAMAccountName")) == NULL
) {
3182 DEBUG(0,("ads_get_dnshostname: No sAMAccountName attribute!\n"));
3186 ads_msgfree(ads
, res
);
3193 SAVED CODE
- we used to join via ldap
- remember how we did
this. JRA
.
3196 * Join a machine to a realm
3197 * Creates the machine account and sets the machine password
3198 * @param ads connection to ads server
3199 * @param machine name of host to add
3200 * @param org_unit Organizational unit to place machine in
3201 * @return status of join
3203 ADS_STATUS
ads_join_realm(ADS_STRUCT
*ads
, const char *machine_name
,
3204 uint32 account_type
, const char *org_unit
)
3207 LDAPMessage
*res
= NULL
;
3210 /* machine name must be lowercase */
3211 machine
= SMB_STRDUP(machine_name
);
3212 strlower_m(machine
);
3215 status = ads_find_machine_acct(ads, (void **)&res, machine);
3216 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
3217 DEBUG(0, ("Host account for %s already exists - deleting old account\n", machine));
3218 status = ads_leave_realm(ads, machine);
3219 if (!ADS_ERR_OK(status)) {
3220 DEBUG(0, ("Failed to delete host '%s' from the '%s' realm.\n",
3221 machine, ads->config.realm));
3226 status
= ads_add_machine_acct(ads
, machine
, account_type
, org_unit
);
3227 if (!ADS_ERR_OK(status
)) {
3228 DEBUG(0, ("ads_join_realm: ads_add_machine_acct failed (%s): %s\n", machine
, ads_errstr(status
)));
3233 status
= ads_find_machine_acct(ads
, (void **)(void *)&res
, machine
);
3234 if (!ADS_ERR_OK(status
)) {
3235 DEBUG(0, ("ads_join_realm: Host account test failed for machine %s\n", machine
));
3241 ads_msgfree(ads
, res
);
3248 * Delete a machine from the realm
3249 * @param ads connection to ads server
3250 * @param hostname Machine to remove
3251 * @return status of delete
3253 ADS_STATUS
ads_leave_realm(ADS_STRUCT
*ads
, const char *hostname
)
3258 char *hostnameDN
, *host
;
3260 LDAPControl ldap_control
;
3261 LDAPControl
* pldap_control
[2] = {NULL
, NULL
};
3263 pldap_control
[0] = &ldap_control
;
3264 memset(&ldap_control
, 0, sizeof(LDAPControl
));
3265 ldap_control
.ldctl_oid
= (char *)LDAP_SERVER_TREE_DELETE_OID
;
3267 /* hostname must be lowercase */
3268 host
= SMB_STRDUP(hostname
);
3271 status
= ads_find_machine_acct(ads
, &res
, host
);
3272 if (!ADS_ERR_OK(status
)) {
3273 DEBUG(0, ("Host account for %s does not exist.\n", host
));
3278 msg
= ads_first_entry(ads
, res
);
3281 return ADS_ERROR_SYSTEM(ENOENT
);
3284 hostnameDN
= ads_get_dn(ads
, (LDAPMessage
*)msg
);
3286 rc
= ldap_delete_ext_s(ads
->ldap
.ld
, hostnameDN
, pldap_control
, NULL
);
3288 DEBUG(3,("ldap_delete_ext_s failed with error code %d\n", rc
));
3290 DEBUG(3,("ldap_delete_ext_s succeeded with error code %d\n", rc
));
3293 if (rc
!= LDAP_SUCCESS
) {
3294 const char *attrs
[] = { "cn", NULL
};
3295 LDAPMessage
*msg_sub
;
3297 /* we only search with scope ONE, we do not expect any further
3298 * objects to be created deeper */
3300 status
= ads_do_search_retry(ads
, hostnameDN
,
3301 LDAP_SCOPE_ONELEVEL
,
3302 "(objectclass=*)", attrs
, &res
);
3304 if (!ADS_ERR_OK(status
)) {
3306 ads_memfree(ads
, hostnameDN
);
3310 for (msg_sub
= ads_first_entry(ads
, res
); msg_sub
;
3311 msg_sub
= ads_next_entry(ads
, msg_sub
)) {
3315 if ((dn
= ads_get_dn(ads
, msg_sub
)) == NULL
) {
3317 ads_memfree(ads
, hostnameDN
);
3318 return ADS_ERROR(LDAP_NO_MEMORY
);
3321 status
= ads_del_dn(ads
, dn
);
3322 if (!ADS_ERR_OK(status
)) {
3323 DEBUG(3,("failed to delete dn %s: %s\n", dn
, ads_errstr(status
)));
3325 ads_memfree(ads
, dn
);
3326 ads_memfree(ads
, hostnameDN
);
3330 ads_memfree(ads
, dn
);
3333 /* there should be no subordinate objects anymore */
3334 status
= ads_do_search_retry(ads
, hostnameDN
,
3335 LDAP_SCOPE_ONELEVEL
,
3336 "(objectclass=*)", attrs
, &res
);
3338 if (!ADS_ERR_OK(status
) || ( (ads_count_replies(ads
, res
)) > 0 ) ) {
3340 ads_memfree(ads
, hostnameDN
);
3344 /* delete hostnameDN now */
3345 status
= ads_del_dn(ads
, hostnameDN
);
3346 if (!ADS_ERR_OK(status
)) {
3348 DEBUG(3,("failed to delete dn %s: %s\n", hostnameDN
, ads_errstr(status
)));
3349 ads_memfree(ads
, hostnameDN
);
3354 ads_memfree(ads
, hostnameDN
);
3356 status
= ads_find_machine_acct(ads
, &res
, host
);
3357 if (ADS_ERR_OK(status
) && ads_count_replies(ads
, res
) == 1) {
3358 DEBUG(3, ("Failed to remove host account.\n"));
3368 * pull all token-sids from an LDAP dn
3369 * @param ads connection to ads server
3370 * @param mem_ctx TALLOC_CTX for allocating sid array
3371 * @param dn of LDAP object
3372 * @param user_sid pointer to DOM_SID (objectSid)
3373 * @param primary_group_sid pointer to DOM_SID (self composed)
3374 * @param sids pointer to sid array to allocate
3375 * @param num_sids counter of SIDs pulled
3376 * @return status of token query
3378 ADS_STATUS
ads_get_tokensids(ADS_STRUCT
*ads
,
3379 TALLOC_CTX
*mem_ctx
,
3382 DOM_SID
*primary_group_sid
,
3387 LDAPMessage
*res
= NULL
;
3389 size_t tmp_num_sids
;
3391 DOM_SID tmp_user_sid
;
3392 DOM_SID tmp_primary_group_sid
;
3394 const char *attrs
[] = {
3401 status
= ads_search_retry_dn(ads
, &res
, dn
, attrs
);
3402 if (!ADS_ERR_OK(status
)) {
3406 count
= ads_count_replies(ads
, res
);
3408 ads_msgfree(ads
, res
);
3409 return ADS_ERROR_LDAP(LDAP_NO_SUCH_OBJECT
);
3412 if (!ads_pull_sid(ads
, res
, "objectSid", &tmp_user_sid
)) {
3413 ads_msgfree(ads
, res
);
3414 return ADS_ERROR_LDAP(LDAP_NO_MEMORY
);
3417 if (!ads_pull_uint32(ads
, res
, "primaryGroupID", &pgid
)) {
3418 ads_msgfree(ads
, res
);
3419 return ADS_ERROR_LDAP(LDAP_NO_MEMORY
);
3423 /* hack to compose the primary group sid without knowing the
3429 sid_copy(&domsid
, &tmp_user_sid
);
3431 if (!sid_split_rid(&domsid
, &dummy_rid
)) {
3432 ads_msgfree(ads
, res
);
3433 return ADS_ERROR_LDAP(LDAP_NO_MEMORY
);
3436 if (!sid_compose(&tmp_primary_group_sid
, &domsid
, pgid
)) {
3437 ads_msgfree(ads
, res
);
3438 return ADS_ERROR_LDAP(LDAP_NO_MEMORY
);
3442 tmp_num_sids
= ads_pull_sids(ads
, mem_ctx
, res
, "tokenGroups", &tmp_sids
);
3444 if (tmp_num_sids
== 0 || !tmp_sids
) {
3445 ads_msgfree(ads
, res
);
3446 return ADS_ERROR_LDAP(LDAP_NO_MEMORY
);
3450 *num_sids
= tmp_num_sids
;
3458 *user_sid
= tmp_user_sid
;
3461 if (primary_group_sid
) {
3462 *primary_group_sid
= tmp_primary_group_sid
;
3465 DEBUG(10,("ads_get_tokensids: returned %d sids\n", (int)tmp_num_sids
+ 2));
3467 ads_msgfree(ads
, res
);
3468 return ADS_ERROR_LDAP(LDAP_SUCCESS
);
3472 * Find a sAMAccoutName in LDAP
3473 * @param ads connection to ads server
3474 * @param mem_ctx TALLOC_CTX for allocating sid array
3475 * @param samaccountname to search
3476 * @param uac_ret uint32 pointer userAccountControl attribute value
3477 * @param dn_ret pointer to dn
3478 * @return status of token query
3480 ADS_STATUS
ads_find_samaccount(ADS_STRUCT
*ads
,
3481 TALLOC_CTX
*mem_ctx
,
3482 const char *samaccountname
,
3484 const char **dn_ret
)
3487 const char *attrs
[] = { "userAccountControl", NULL
};
3489 LDAPMessage
*res
= NULL
;
3493 filter
= talloc_asprintf(mem_ctx
, "(&(objectclass=user)(sAMAccountName=%s))",
3495 if (filter
== NULL
) {
3496 status
= ADS_ERROR_NT(NT_STATUS_NO_MEMORY
);
3500 status
= ads_do_search_all(ads
, ads
->config
.bind_path
,
3502 filter
, attrs
, &res
);
3504 if (!ADS_ERR_OK(status
)) {
3508 if (ads_count_replies(ads
, res
) != 1) {
3509 status
= ADS_ERROR(LDAP_NO_RESULTS_RETURNED
);
3513 dn
= ads_get_dn(ads
, res
);
3515 status
= ADS_ERROR(LDAP_NO_MEMORY
);
3519 if (!ads_pull_uint32(ads
, res
, "userAccountControl", &uac
)) {
3520 status
= ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE
);
3529 *dn_ret
= talloc_strdup(mem_ctx
, dn
);
3531 status
= ADS_ERROR(LDAP_NO_MEMORY
);
3536 ads_memfree(ads
, dn
);
3537 ads_msgfree(ads
, res
);
3543 * find our configuration path
3544 * @param ads connection to ads server
3545 * @param mem_ctx Pointer to talloc context
3546 * @param config_path Pointer to the config path
3547 * @return status of search
3549 ADS_STATUS
ads_config_path(ADS_STRUCT
*ads
,
3550 TALLOC_CTX
*mem_ctx
,
3554 LDAPMessage
*res
= NULL
;
3555 const char *config_context
= NULL
;
3556 const char *attrs
[] = { "configurationNamingContext", NULL
};
3558 status
= ads_do_search(ads
, "", LDAP_SCOPE_BASE
,
3559 "(objectclass=*)", attrs
, &res
);
3560 if (!ADS_ERR_OK(status
)) {
3564 config_context
= ads_pull_string(ads
, mem_ctx
, res
,
3565 "configurationNamingContext");
3566 ads_msgfree(ads
, res
);
3567 if (!config_context
) {
3568 return ADS_ERROR(LDAP_NO_MEMORY
);
3572 *config_path
= talloc_strdup(mem_ctx
, config_context
);
3573 if (!*config_path
) {
3574 return ADS_ERROR(LDAP_NO_MEMORY
);
3578 return ADS_ERROR(LDAP_SUCCESS
);
3582 * find the displayName of an extended right
3583 * @param ads connection to ads server
3584 * @param config_path The config path
3585 * @param mem_ctx Pointer to talloc context
3586 * @param GUID struct of the rightsGUID
3587 * @return status of search
3589 const char *ads_get_extended_right_name_by_guid(ADS_STRUCT
*ads
,
3590 const char *config_path
,
3591 TALLOC_CTX
*mem_ctx
,
3592 const struct GUID
*rights_guid
)
3595 LDAPMessage
*res
= NULL
;
3597 const char *attrs
[] = { "displayName", NULL
};
3598 const char *result
= NULL
;
3601 if (!ads
|| !mem_ctx
|| !rights_guid
) {
3605 expr
= talloc_asprintf(mem_ctx
, "(rightsGuid=%s)",
3606 smb_uuid_string(mem_ctx
, *rights_guid
));
3611 path
= talloc_asprintf(mem_ctx
, "cn=Extended-Rights,%s", config_path
);
3616 rc
= ads_do_search_retry(ads
, path
, LDAP_SCOPE_SUBTREE
,
3618 if (!ADS_ERR_OK(rc
)) {
3622 if (ads_count_replies(ads
, res
) != 1) {
3626 result
= ads_pull_string(ads
, mem_ctx
, res
, "displayName");
3629 ads_msgfree(ads
, res
);
3635 * verify or build and verify an account ou
3636 * @param mem_ctx Pointer to talloc context
3637 * @param ads connection to ads server
3639 * @return status of search
3642 ADS_STATUS
ads_check_ou_dn(TALLOC_CTX
*mem_ctx
,
3644 const char **account_ou
)
3646 struct ldb_dn
*name_dn
= NULL
;
3647 const char *name
= NULL
;
3648 char *ou_string
= NULL
;
3650 name_dn
= ldb_dn_explode(mem_ctx
, *account_ou
);
3655 ou_string
= ads_ou_string(ads
, *account_ou
);
3657 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX
);
3660 name
= talloc_asprintf(mem_ctx
, "%s,%s", ou_string
,
3661 ads
->config
.bind_path
);
3662 SAFE_FREE(ou_string
);
3664 return ADS_ERROR_LDAP(LDAP_NO_MEMORY
);
3667 name_dn
= ldb_dn_explode(mem_ctx
, name
);
3669 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX
);
3672 *account_ou
= talloc_strdup(mem_ctx
, name
);
3674 return ADS_ERROR_LDAP(LDAP_NO_MEMORY
);