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/>.
26 #include "libads/sitename_cache.h"
27 #include "libads/cldap.h"
28 #include "libads/dns.h"
29 #include "../libds/common/flags.h"
31 #include "../libcli/security/security.h"
37 * @brief basic ldap client-side routines for ads server communications
39 * The routines contained here should do the necessary ldap calls for
42 * Important note: attribute names passed into ads_ routines must
43 * already be in UTF-8 format. We do not convert them because in almost
44 * all cases, they are just ascii (which is represented with the same
45 * codepoints in UTF-8). This may have to change at some point
49 #define LDAP_SERVER_TREE_DELETE_OID "1.2.840.113556.1.4.805"
51 static SIG_ATOMIC_T gotalarm
;
53 /***************************************************************
54 Signal function to tell us we timed out.
55 ****************************************************************/
57 static void gotalarm_sig(int signum
)
62 LDAP
*ldap_open_with_timeout(const char *server
,
63 struct sockaddr_storage
*ss
,
64 int port
, unsigned int to
)
68 DEBUG(10, ("Opening connection to LDAP server '%s:%d', timeout "
69 "%u seconds\n", server
, port
, to
));
71 #if defined(HAVE_LDAP_INIT_FD) && defined(SOCKET_WRAPPER)
72 /* Only use this private LDAP function if we are in make test,
73 * as this is the best way to get the emulated TCP socket into
75 if (socket_wrapper_dir() != NULL
) {
80 status
= open_socket_out(ss
, port
, to
, &fd
);
82 if (!NT_STATUS_IS_OK(status
)) {
86 #ifndef LDAP_PROTO_TCP
87 #define LDAP_PROTO_TCP 1
89 uri
= talloc_asprintf(talloc_tos(), "ldap://%s:%u", server
, port
);
93 ldap_err
= ldap_init_fd(fd
, LDAP_PROTO_TCP
, uri
, &ldp
);
96 if (ldap_err
!= LDAP_SUCCESS
) {
105 CatchSignal(SIGALRM
, gotalarm_sig
);
107 /* End setup timeout. */
109 ldp
= ldap_open(server
, port
);
112 DEBUG(2,("Could not open connection to LDAP server %s:%d: %s\n",
113 server
, port
, strerror(errno
)));
115 DEBUG(10, ("Connected to LDAP server '%s:%d'\n", server
, port
));
118 /* Teardown timeout. */
119 CatchSignal(SIGALRM
, SIG_IGN
);
125 static int ldap_search_with_timeout(LDAP
*ld
,
126 LDAP_CONST
char *base
,
128 LDAP_CONST
char *filter
,
131 LDAPControl
**sctrls
,
132 LDAPControl
**cctrls
,
136 struct timeval timeout
;
139 /* Setup timeout for the ldap_search_ext_s call - local and remote. */
140 timeout
.tv_sec
= lp_ldap_timeout();
143 /* Setup alarm timeout.... Do we need both of these ? JRA. */
145 CatchSignal(SIGALRM
, gotalarm_sig
);
146 alarm(lp_ldap_timeout());
147 /* End setup timeout. */
149 result
= ldap_search_ext_s(ld
, base
, scope
, filter
, attrs
,
150 attrsonly
, sctrls
, cctrls
, &timeout
,
153 /* Teardown timeout. */
154 CatchSignal(SIGALRM
, SIG_IGN
);
158 return LDAP_TIMELIMIT_EXCEEDED
;
161 * A bug in OpenLDAP means ldap_search_ext_s can return
162 * LDAP_SUCCESS but with a NULL res pointer. Cope with
163 * this. See bug #6279 for details. JRA.
167 return LDAP_TIMELIMIT_EXCEEDED
;
173 /**********************************************
174 Do client and server sitename match ?
175 **********************************************/
177 bool ads_sitename_match(ADS_STRUCT
*ads
)
179 if (ads
->config
.server_site_name
== NULL
&&
180 ads
->config
.client_site_name
== NULL
) {
181 DEBUG(10,("ads_sitename_match: both null\n"));
184 if (ads
->config
.server_site_name
&&
185 ads
->config
.client_site_name
&&
186 strequal(ads
->config
.server_site_name
,
187 ads
->config
.client_site_name
)) {
188 DEBUG(10,("ads_sitename_match: name %s match\n", ads
->config
.server_site_name
));
191 DEBUG(10,("ads_sitename_match: no match between server: %s and client: %s\n",
192 ads
->config
.server_site_name
? ads
->config
.server_site_name
: "NULL",
193 ads
->config
.client_site_name
? ads
->config
.client_site_name
: "NULL"));
197 /**********************************************
198 Is this the closest DC ?
199 **********************************************/
201 bool ads_closest_dc(ADS_STRUCT
*ads
)
203 if (ads
->config
.flags
& NBT_SERVER_CLOSEST
) {
204 DEBUG(10,("ads_closest_dc: NBT_SERVER_CLOSEST flag set\n"));
208 /* not sure if this can ever happen */
209 if (ads_sitename_match(ads
)) {
210 DEBUG(10,("ads_closest_dc: NBT_SERVER_CLOSEST flag not set but sites match\n"));
214 if (ads
->config
.client_site_name
== NULL
) {
215 DEBUG(10,("ads_closest_dc: client belongs to no site\n"));
219 DEBUG(10,("ads_closest_dc: %s is not the closest DC\n",
220 ads
->config
.ldap_server_name
));
227 try a connection to a given ldap server, returning True and setting the servers IP
228 in the ads struct if successful
230 static bool ads_try_connect(ADS_STRUCT
*ads
, const char *server
, bool gc
)
232 struct NETLOGON_SAM_LOGON_RESPONSE_EX cldap_reply
;
233 TALLOC_CTX
*frame
= talloc_stackframe();
235 struct sockaddr_storage ss
;
236 char addr
[INET6_ADDRSTRLEN
];
238 if (!server
|| !*server
) {
243 if (!resolve_name(server
, &ss
, 0x20, true)) {
244 DEBUG(5,("ads_try_connect: unable to resolve name %s\n",
249 print_sockaddr(addr
, sizeof(addr
), &ss
);
251 DEBUG(5,("ads_try_connect: sending CLDAP request to %s (realm: %s)\n",
252 addr
, ads
->server
.realm
));
254 ZERO_STRUCT( cldap_reply
);
256 if ( !ads_cldap_netlogon_5(frame
, &ss
, ads
->server
.realm
, &cldap_reply
) ) {
257 DEBUG(3,("ads_try_connect: CLDAP request %s failed.\n", addr
));
262 /* Check the CLDAP reply flags */
264 if ( !(cldap_reply
.server_type
& NBT_SERVER_LDAP
) ) {
265 DEBUG(1,("ads_try_connect: %s's CLDAP reply says it is not an LDAP server!\n",
271 /* Fill in the ads->config values */
273 SAFE_FREE(ads
->config
.realm
);
274 SAFE_FREE(ads
->config
.bind_path
);
275 SAFE_FREE(ads
->config
.ldap_server_name
);
276 SAFE_FREE(ads
->config
.server_site_name
);
277 SAFE_FREE(ads
->config
.client_site_name
);
278 SAFE_FREE(ads
->server
.workgroup
);
280 ads
->config
.flags
= cldap_reply
.server_type
;
281 ads
->config
.ldap_server_name
= SMB_STRDUP(cldap_reply
.pdc_dns_name
);
282 ads
->config
.realm
= SMB_STRDUP(cldap_reply
.dns_domain
);
283 strupper_m(ads
->config
.realm
);
284 ads
->config
.bind_path
= ads_build_dn(ads
->config
.realm
);
285 if (*cldap_reply
.server_site
) {
286 ads
->config
.server_site_name
=
287 SMB_STRDUP(cldap_reply
.server_site
);
289 if (*cldap_reply
.client_site
) {
290 ads
->config
.client_site_name
=
291 SMB_STRDUP(cldap_reply
.client_site
);
293 ads
->server
.workgroup
= SMB_STRDUP(cldap_reply
.domain_name
);
295 ads
->ldap
.port
= gc
? LDAP_GC_PORT
: LDAP_PORT
;
298 /* Store our site name. */
299 sitename_store( cldap_reply
.domain_name
, cldap_reply
.client_site
);
300 sitename_store( cldap_reply
.dns_domain
, cldap_reply
.client_site
);
310 /**********************************************************************
311 Try to find an AD dc using our internal name resolution routines
312 Try the realm first and then then workgroup name if netbios is not
314 **********************************************************************/
316 static NTSTATUS
ads_find_dc(ADS_STRUCT
*ads
)
318 const char *c_domain
;
321 struct ip_service
*ip_list
;
324 bool got_realm
= False
;
325 bool use_own_domain
= False
;
327 NTSTATUS status
= NT_STATUS_UNSUCCESSFUL
;
329 /* if the realm and workgroup are both empty, assume they are ours */
332 c_realm
= ads
->server
.realm
;
334 if ( !c_realm
|| !*c_realm
) {
335 /* special case where no realm and no workgroup means our own */
336 if ( !ads
->server
.workgroup
|| !*ads
->server
.workgroup
) {
337 use_own_domain
= True
;
338 c_realm
= lp_realm();
342 if (c_realm
&& *c_realm
)
345 /* we need to try once with the realm name and fallback to the
346 netbios domain name if we fail (if netbios has not been disabled */
348 if ( !got_realm
&& !lp_disable_netbios() ) {
349 c_realm
= ads
->server
.workgroup
;
350 if (!c_realm
|| !*c_realm
) {
351 if ( use_own_domain
)
352 c_realm
= lp_workgroup();
356 if ( !c_realm
|| !*c_realm
) {
357 DEBUG(0,("ads_find_dc: no realm or workgroup! Don't know what to do\n"));
358 return NT_STATUS_INVALID_PARAMETER
; /* rather need MISSING_PARAMETER ... */
361 if ( use_own_domain
) {
362 c_domain
= lp_workgroup();
364 c_domain
= ads
->server
.workgroup
;
371 * In case of LDAP we use get_dc_name() as that
372 * creates the custom krb5.conf file
374 if (!(ads
->auth
.flags
& ADS_AUTH_NO_BIND
)) {
376 struct sockaddr_storage ip_out
;
378 DEBUG(6,("ads_find_dc: (ldap) looking for %s '%s'\n",
379 (got_realm
? "realm" : "domain"), realm
));
381 if (get_dc_name(domain
, realm
, srv_name
, &ip_out
)) {
383 * we call ads_try_connect() to fill in the
384 * ads->config details
386 if (ads_try_connect(ads
, srv_name
, false)) {
391 return NT_STATUS_NO_LOGON_SERVERS
;
394 sitename
= sitename_fetch(realm
);
398 DEBUG(6,("ads_find_dc: (cldap) looking for %s '%s'\n",
399 (got_realm
? "realm" : "domain"), realm
));
401 status
= get_sorted_dc_list(realm
, sitename
, &ip_list
, &count
, got_realm
);
402 if (!NT_STATUS_IS_OK(status
)) {
403 /* fall back to netbios if we can */
404 if ( got_realm
&& !lp_disable_netbios() ) {
413 /* if we fail this loop, then giveup since all the IP addresses returned were dead */
414 for ( i
=0; i
<count
; i
++ ) {
415 char server
[INET6_ADDRSTRLEN
];
417 print_sockaddr(server
, sizeof(server
), &ip_list
[i
].ss
);
419 if ( !NT_STATUS_IS_OK(check_negative_conn_cache(realm
, server
)) )
423 /* realm in this case is a workgroup name. We need
424 to ignore any IP addresses in the negative connection
425 cache that match ip addresses returned in the ad realm
426 case. It sucks that I have to reproduce the logic above... */
427 c_realm
= ads
->server
.realm
;
428 if ( !c_realm
|| !*c_realm
) {
429 if ( !ads
->server
.workgroup
|| !*ads
->server
.workgroup
) {
430 c_realm
= lp_realm();
433 if (c_realm
&& *c_realm
&&
434 !NT_STATUS_IS_OK(check_negative_conn_cache(c_realm
, server
))) {
435 /* Ensure we add the workgroup name for this
436 IP address as negative too. */
437 add_failed_connection_entry( realm
, server
, NT_STATUS_UNSUCCESSFUL
);
442 if ( ads_try_connect(ads
, server
, false) ) {
448 /* keep track of failures */
449 add_failed_connection_entry( realm
, server
, NT_STATUS_UNSUCCESSFUL
);
454 /* In case we failed to contact one of our closest DC on our site we
455 * need to try to find another DC, retry with a site-less SRV DNS query
459 DEBUG(1,("ads_find_dc: failed to find a valid DC on our site (%s), "
460 "trying to find another DC\n", sitename
));
462 namecache_delete(realm
, 0x1C);
466 return NT_STATUS_NO_LOGON_SERVERS
;
469 /*********************************************************************
470 *********************************************************************/
472 static NTSTATUS
ads_lookup_site(void)
474 ADS_STRUCT
*ads
= NULL
;
475 ADS_STATUS ads_status
;
476 NTSTATUS nt_status
= NT_STATUS_UNSUCCESSFUL
;
478 ads
= ads_init(lp_realm(), NULL
, NULL
);
480 return NT_STATUS_NO_MEMORY
;
483 /* The NO_BIND here will find a DC and set the client site
484 but not establish the TCP connection */
486 ads
->auth
.flags
= ADS_AUTH_NO_BIND
;
487 ads_status
= ads_connect(ads
);
488 if (!ADS_ERR_OK(ads_status
)) {
489 DEBUG(4, ("ads_lookup_site: ads_connect to our realm failed! (%s)\n",
490 ads_errstr(ads_status
)));
492 nt_status
= ads_ntstatus(ads_status
);
501 /*********************************************************************
502 *********************************************************************/
504 static const char* host_dns_domain(const char *fqdn
)
506 const char *p
= fqdn
;
508 /* go to next char following '.' */
510 if ((p
= strchr_m(fqdn
, '.')) != NULL
) {
519 * Connect to the Global Catalog server
520 * @param ads Pointer to an existing ADS_STRUCT
521 * @return status of connection
523 * Simple wrapper around ads_connect() that fills in the
524 * GC ldap server information
527 ADS_STATUS
ads_connect_gc(ADS_STRUCT
*ads
)
529 TALLOC_CTX
*frame
= talloc_stackframe();
530 struct dns_rr_srv
*gcs_list
;
532 const char *realm
= ads
->server
.realm
;
533 NTSTATUS nt_status
= NT_STATUS_UNSUCCESSFUL
;
534 ADS_STATUS ads_status
= ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL
);
537 char *sitename
= NULL
;
542 if ((sitename
= sitename_fetch(realm
)) == NULL
) {
544 sitename
= sitename_fetch(realm
);
548 /* We try once with a sitename and once without
549 (unless we don't have a sitename and then we're
552 if (sitename
== NULL
)
555 nt_status
= ads_dns_query_gcs(frame
, realm
, sitename
,
556 &gcs_list
, &num_gcs
);
560 if (!NT_STATUS_IS_OK(nt_status
)) {
561 ads_status
= ADS_ERROR_NT(nt_status
);
565 /* Loop until we get a successful connection or have gone
566 through them all. When connecting a GC server, make sure that
567 the realm is the server's DNS name and not the forest root */
569 for (i
=0; i
<num_gcs
; i
++) {
570 ads
->server
.gc
= true;
571 ads
->server
.ldap_server
= SMB_STRDUP(gcs_list
[i
].hostname
);
572 ads
->server
.realm
= SMB_STRDUP(host_dns_domain(ads
->server
.ldap_server
));
573 ads_status
= ads_connect(ads
);
574 if (ADS_ERR_OK(ads_status
)) {
575 /* Reset the bind_dn to "". A Global Catalog server
576 may host multiple domain trees in a forest.
577 Windows 2003 GC server will accept "" as the search
578 path to imply search all domain trees in the forest */
580 SAFE_FREE(ads
->config
.bind_path
);
581 ads
->config
.bind_path
= SMB_STRDUP("");
586 SAFE_FREE(ads
->server
.ldap_server
);
587 SAFE_FREE(ads
->server
.realm
);
590 TALLOC_FREE(gcs_list
);
596 talloc_destroy(frame
);
603 * Connect to the LDAP server
604 * @param ads Pointer to an existing ADS_STRUCT
605 * @return status of connection
607 ADS_STATUS
ads_connect(ADS_STRUCT
*ads
)
609 int version
= LDAP_VERSION3
;
612 char addr
[INET6_ADDRSTRLEN
];
614 ZERO_STRUCT(ads
->ldap
);
615 ads
->ldap
.last_attempt
= time_mono(NULL
);
616 ads
->ldap
.wrap_type
= ADS_SASLWRAP_TYPE_PLAIN
;
618 /* try with a user specified server */
620 if (DEBUGLEVEL
>= 11) {
621 char *s
= NDR_PRINT_STRUCT_STRING(talloc_tos(), ads_struct
, ads
);
622 DEBUG(11,("ads_connect: entering\n"));
623 DEBUGADD(11,("%s\n", s
));
627 if (ads
->server
.ldap_server
)
629 if (ads_try_connect(ads
, ads
->server
.ldap_server
, ads
->server
.gc
)) {
633 /* The choice of which GC use is handled one level up in
634 ads_connect_gc(). If we continue on from here with
635 ads_find_dc() we will get GC searches on port 389 which
636 doesn't work. --jerry */
638 if (ads
->server
.gc
== true) {
639 return ADS_ERROR(LDAP_OPERATIONS_ERROR
);
643 ntstatus
= ads_find_dc(ads
);
644 if (NT_STATUS_IS_OK(ntstatus
)) {
648 status
= ADS_ERROR_NT(ntstatus
);
653 print_sockaddr(addr
, sizeof(addr
), &ads
->ldap
.ss
);
654 DEBUG(3,("Successfully contacted LDAP server %s\n", addr
));
656 if (!ads
->auth
.user_name
) {
657 /* Must use the userPrincipalName value here or sAMAccountName
658 and not servicePrincipalName; found by Guenther Deschner */
660 if (asprintf(&ads
->auth
.user_name
, "%s$", lp_netbios_name() ) == -1) {
661 DEBUG(0,("ads_connect: asprintf fail.\n"));
662 ads
->auth
.user_name
= NULL
;
666 if (!ads
->auth
.realm
) {
667 ads
->auth
.realm
= SMB_STRDUP(ads
->config
.realm
);
670 if (!ads
->auth
.kdc_server
) {
671 print_sockaddr(addr
, sizeof(addr
), &ads
->ldap
.ss
);
672 ads
->auth
.kdc_server
= SMB_STRDUP(addr
);
675 /* If the caller() requested no LDAP bind, then we are done */
677 if (ads
->auth
.flags
& ADS_AUTH_NO_BIND
) {
678 status
= ADS_SUCCESS
;
682 ads
->ldap
.mem_ctx
= talloc_init("ads LDAP connection memory");
683 if (!ads
->ldap
.mem_ctx
) {
684 status
= ADS_ERROR_NT(NT_STATUS_NO_MEMORY
);
688 /* Otherwise setup the TCP LDAP session */
690 ads
->ldap
.ld
= ldap_open_with_timeout(ads
->config
.ldap_server_name
,
692 ads
->ldap
.port
, lp_ldap_timeout());
693 if (ads
->ldap
.ld
== NULL
) {
694 status
= ADS_ERROR(LDAP_OPERATIONS_ERROR
);
697 DEBUG(3,("Connected to LDAP server %s\n", ads
->config
.ldap_server_name
));
699 /* cache the successful connection for workgroup and realm */
700 if (ads_closest_dc(ads
)) {
701 saf_store( ads
->server
.workgroup
, ads
->config
.ldap_server_name
);
702 saf_store( ads
->server
.realm
, ads
->config
.ldap_server_name
);
705 ldap_set_option(ads
->ldap
.ld
, LDAP_OPT_PROTOCOL_VERSION
, &version
);
707 if ( lp_ldap_ssl_ads() ) {
708 status
= ADS_ERROR(smb_ldap_start_tls(ads
->ldap
.ld
, version
));
709 if (!ADS_ERR_OK(status
)) {
714 /* fill in the current time and offsets */
716 status
= ads_current_time( ads
);
717 if ( !ADS_ERR_OK(status
) ) {
721 /* Now do the bind */
723 if (ads
->auth
.flags
& ADS_AUTH_ANON_BIND
) {
724 status
= ADS_ERROR(ldap_simple_bind_s(ads
->ldap
.ld
, NULL
, NULL
));
728 if (ads
->auth
.flags
& ADS_AUTH_SIMPLE_BIND
) {
729 status
= ADS_ERROR(ldap_simple_bind_s(ads
->ldap
.ld
, ads
->auth
.user_name
, ads
->auth
.password
));
733 status
= ads_sasl_bind(ads
);
736 if (DEBUGLEVEL
>= 11) {
737 char *s
= NDR_PRINT_STRUCT_STRING(talloc_tos(), ads_struct
, ads
);
738 DEBUG(11,("ads_connect: leaving with: %s\n",
739 ads_errstr(status
)));
740 DEBUGADD(11,("%s\n", s
));
748 * Connect to the LDAP server using given credentials
749 * @param ads Pointer to an existing ADS_STRUCT
750 * @return status of connection
752 ADS_STATUS
ads_connect_user_creds(ADS_STRUCT
*ads
)
754 ads
->auth
.flags
|= ADS_AUTH_USER_CREDS
;
756 return ads_connect(ads
);
760 * Disconnect the LDAP server
761 * @param ads Pointer to an existing ADS_STRUCT
763 void ads_disconnect(ADS_STRUCT
*ads
)
766 ldap_unbind(ads
->ldap
.ld
);
769 if (ads
->ldap
.wrap_ops
&& ads
->ldap
.wrap_ops
->disconnect
) {
770 ads
->ldap
.wrap_ops
->disconnect(ads
);
772 if (ads
->ldap
.mem_ctx
) {
773 talloc_free(ads
->ldap
.mem_ctx
);
775 ZERO_STRUCT(ads
->ldap
);
779 Duplicate a struct berval into talloc'ed memory
781 static struct berval
*dup_berval(TALLOC_CTX
*ctx
, const struct berval
*in_val
)
783 struct berval
*value
;
785 if (!in_val
) return NULL
;
787 value
= talloc_zero(ctx
, struct berval
);
790 if (in_val
->bv_len
== 0) return value
;
792 value
->bv_len
= in_val
->bv_len
;
793 value
->bv_val
= (char *)talloc_memdup(ctx
, in_val
->bv_val
,
799 Make a values list out of an array of (struct berval *)
801 static struct berval
**ads_dup_values(TALLOC_CTX
*ctx
,
802 const struct berval
**in_vals
)
804 struct berval
**values
;
807 if (!in_vals
) return NULL
;
808 for (i
=0; in_vals
[i
]; i
++)
810 values
= talloc_zero_array(ctx
, struct berval
*, i
+1);
811 if (!values
) return NULL
;
813 for (i
=0; in_vals
[i
]; i
++) {
814 values
[i
] = dup_berval(ctx
, in_vals
[i
]);
820 UTF8-encode a values list out of an array of (char *)
822 static char **ads_push_strvals(TALLOC_CTX
*ctx
, const char **in_vals
)
828 if (!in_vals
) return NULL
;
829 for (i
=0; in_vals
[i
]; i
++)
831 values
= talloc_zero_array(ctx
, char *, i
+1);
832 if (!values
) return NULL
;
834 for (i
=0; in_vals
[i
]; i
++) {
835 if (!push_utf8_talloc(ctx
, &values
[i
], in_vals
[i
], &size
)) {
844 Pull a (char *) array out of a UTF8-encoded values list
846 static char **ads_pull_strvals(TALLOC_CTX
*ctx
, const char **in_vals
)
850 size_t converted_size
;
852 if (!in_vals
) return NULL
;
853 for (i
=0; in_vals
[i
]; i
++)
855 values
= talloc_zero_array(ctx
, char *, i
+1);
856 if (!values
) return NULL
;
858 for (i
=0; in_vals
[i
]; i
++) {
859 if (!pull_utf8_talloc(ctx
, &values
[i
], in_vals
[i
],
861 DEBUG(0,("ads_pull_strvals: pull_utf8_talloc failed: "
862 "%s", strerror(errno
)));
869 * Do a search with paged results. cookie must be null on the first
870 * call, and then returned on each subsequent call. It will be null
871 * again when the entire search is complete
872 * @param ads connection to ads server
873 * @param bind_path Base dn for the search
874 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
875 * @param expr Search expression - specified in local charset
876 * @param attrs Attributes to retrieve - specified in utf8 or ascii
877 * @param res ** which will contain results - free res* with ads_msgfree()
878 * @param count Number of entries retrieved on this page
879 * @param cookie The paged results cookie to be returned on subsequent calls
880 * @return status of search
882 static ADS_STATUS
ads_do_paged_search_args(ADS_STRUCT
*ads
,
883 const char *bind_path
,
884 int scope
, const char *expr
,
885 const char **attrs
, void *args
,
887 int *count
, struct berval
**cookie
)
890 char *utf8_expr
, *utf8_path
, **search_attrs
= NULL
;
891 size_t converted_size
;
892 LDAPControl PagedResults
, NoReferrals
, ExternalCtrl
, *controls
[4], **rcontrols
;
893 BerElement
*cookie_be
= NULL
;
894 struct berval
*cookie_bv
= NULL
;
895 BerElement
*ext_be
= NULL
;
896 struct berval
*ext_bv
= NULL
;
899 ads_control
*external_control
= (ads_control
*) args
;
903 if (!(ctx
= talloc_init("ads_do_paged_search_args")))
904 return ADS_ERROR(LDAP_NO_MEMORY
);
906 /* 0 means the conversion worked but the result was empty
907 so we only fail if it's -1. In any case, it always
908 at least nulls out the dest */
909 if (!push_utf8_talloc(ctx
, &utf8_expr
, expr
, &converted_size
) ||
910 !push_utf8_talloc(ctx
, &utf8_path
, bind_path
, &converted_size
))
916 if (!attrs
|| !(*attrs
))
919 /* This would be the utf8-encoded version...*/
920 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
921 if (!(search_attrs
= str_list_copy(talloc_tos(), attrs
))) {
927 /* Paged results only available on ldap v3 or later */
928 ldap_get_option(ads
->ldap
.ld
, LDAP_OPT_PROTOCOL_VERSION
, &version
);
929 if (version
< LDAP_VERSION3
) {
930 rc
= LDAP_NOT_SUPPORTED
;
934 cookie_be
= ber_alloc_t(LBER_USE_DER
);
936 ber_printf(cookie_be
, "{iO}", (ber_int_t
) 1000, *cookie
);
937 ber_bvfree(*cookie
); /* don't need it from last time */
940 ber_printf(cookie_be
, "{io}", (ber_int_t
) 1000, "", 0);
942 ber_flatten(cookie_be
, &cookie_bv
);
943 PagedResults
.ldctl_oid
= discard_const_p(char, ADS_PAGE_CTL_OID
);
944 PagedResults
.ldctl_iscritical
= (char) 1;
945 PagedResults
.ldctl_value
.bv_len
= cookie_bv
->bv_len
;
946 PagedResults
.ldctl_value
.bv_val
= cookie_bv
->bv_val
;
948 NoReferrals
.ldctl_oid
= discard_const_p(char, ADS_NO_REFERRALS_OID
);
949 NoReferrals
.ldctl_iscritical
= (char) 0;
950 NoReferrals
.ldctl_value
.bv_len
= 0;
951 NoReferrals
.ldctl_value
.bv_val
= discard_const_p(char, "");
953 if (external_control
&&
954 (strequal(external_control
->control
, ADS_EXTENDED_DN_OID
) ||
955 strequal(external_control
->control
, ADS_SD_FLAGS_OID
))) {
957 ExternalCtrl
.ldctl_oid
= discard_const_p(char, external_control
->control
);
958 ExternalCtrl
.ldctl_iscritical
= (char) external_control
->critical
;
960 /* win2k does not accept a ldctl_value beeing passed in */
962 if (external_control
->val
!= 0) {
964 if ((ext_be
= ber_alloc_t(LBER_USE_DER
)) == NULL
) {
969 if ((ber_printf(ext_be
, "{i}", (ber_int_t
) external_control
->val
)) == -1) {
973 if ((ber_flatten(ext_be
, &ext_bv
)) == -1) {
978 ExternalCtrl
.ldctl_value
.bv_len
= ext_bv
->bv_len
;
979 ExternalCtrl
.ldctl_value
.bv_val
= ext_bv
->bv_val
;
982 ExternalCtrl
.ldctl_value
.bv_len
= 0;
983 ExternalCtrl
.ldctl_value
.bv_val
= NULL
;
986 controls
[0] = &NoReferrals
;
987 controls
[1] = &PagedResults
;
988 controls
[2] = &ExternalCtrl
;
992 controls
[0] = &NoReferrals
;
993 controls
[1] = &PagedResults
;
997 /* we need to disable referrals as the openldap libs don't
998 handle them and paged results at the same time. Using them
999 together results in the result record containing the server
1000 page control being removed from the result list (tridge/jmcd)
1002 leaving this in despite the control that says don't generate
1003 referrals, in case the server doesn't support it (jmcd)
1005 ldap_set_option(ads
->ldap
.ld
, LDAP_OPT_REFERRALS
, LDAP_OPT_OFF
);
1007 rc
= ldap_search_with_timeout(ads
->ldap
.ld
, utf8_path
, scope
, utf8_expr
,
1008 search_attrs
, 0, controls
,
1009 NULL
, LDAP_NO_LIMIT
,
1010 (LDAPMessage
**)res
);
1012 ber_free(cookie_be
, 1);
1013 ber_bvfree(cookie_bv
);
1016 DEBUG(3,("ads_do_paged_search_args: ldap_search_with_timeout(%s) -> %s\n", expr
,
1017 ldap_err2string(rc
)));
1021 rc
= ldap_parse_result(ads
->ldap
.ld
, *res
, NULL
, NULL
, NULL
,
1022 NULL
, &rcontrols
, 0);
1028 for (i
=0; rcontrols
[i
]; i
++) {
1029 if (strcmp(ADS_PAGE_CTL_OID
, rcontrols
[i
]->ldctl_oid
) == 0) {
1030 cookie_be
= ber_init(&rcontrols
[i
]->ldctl_value
);
1031 ber_scanf(cookie_be
,"{iO}", (ber_int_t
*) count
,
1033 /* the berval is the cookie, but must be freed when
1035 if (cookie_bv
->bv_len
) /* still more to do */
1036 *cookie
=ber_bvdup(cookie_bv
);
1039 ber_bvfree(cookie_bv
);
1040 ber_free(cookie_be
, 1);
1044 ldap_controls_free(rcontrols
);
1047 talloc_destroy(ctx
);
1050 ber_free(ext_be
, 1);
1057 /* if/when we decide to utf8-encode attrs, take out this next line */
1058 TALLOC_FREE(search_attrs
);
1060 return ADS_ERROR(rc
);
1063 static ADS_STATUS
ads_do_paged_search(ADS_STRUCT
*ads
, const char *bind_path
,
1064 int scope
, const char *expr
,
1065 const char **attrs
, LDAPMessage
**res
,
1066 int *count
, struct berval
**cookie
)
1068 return ads_do_paged_search_args(ads
, bind_path
, scope
, expr
, attrs
, NULL
, res
, count
, cookie
);
1073 * Get all results for a search. This uses ads_do_paged_search() to return
1074 * all entries in a large search.
1075 * @param ads connection to ads server
1076 * @param bind_path Base dn for the search
1077 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1078 * @param expr Search expression
1079 * @param attrs Attributes to retrieve
1080 * @param res ** which will contain results - free res* with ads_msgfree()
1081 * @return status of search
1083 ADS_STATUS
ads_do_search_all_args(ADS_STRUCT
*ads
, const char *bind_path
,
1084 int scope
, const char *expr
,
1085 const char **attrs
, void *args
,
1088 struct berval
*cookie
= NULL
;
1093 status
= ads_do_paged_search_args(ads
, bind_path
, scope
, expr
, attrs
, args
, res
,
1096 if (!ADS_ERR_OK(status
))
1099 #ifdef HAVE_LDAP_ADD_RESULT_ENTRY
1101 LDAPMessage
*res2
= NULL
;
1103 LDAPMessage
*msg
, *next
;
1105 status2
= ads_do_paged_search_args(ads
, bind_path
, scope
, expr
,
1106 attrs
, args
, &res2
, &count
, &cookie
);
1108 if (!ADS_ERR_OK(status2
)) break;
1110 /* this relies on the way that ldap_add_result_entry() works internally. I hope
1111 that this works on all ldap libs, but I have only tested with openldap */
1112 for (msg
= ads_first_message(ads
, res2
); msg
; msg
= next
) {
1113 next
= ads_next_message(ads
, msg
);
1114 ldap_add_result_entry((LDAPMessage
**)res
, msg
);
1116 /* note that we do not free res2, as the memory is now
1117 part of the main returned list */
1120 DEBUG(0, ("no ldap_add_result_entry() support in LDAP libs!\n"));
1121 status
= ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL
);
1127 ADS_STATUS
ads_do_search_all(ADS_STRUCT
*ads
, const char *bind_path
,
1128 int scope
, const char *expr
,
1129 const char **attrs
, LDAPMessage
**res
)
1131 return ads_do_search_all_args(ads
, bind_path
, scope
, expr
, attrs
, NULL
, res
);
1134 ADS_STATUS
ads_do_search_all_sd_flags(ADS_STRUCT
*ads
, const char *bind_path
,
1135 int scope
, const char *expr
,
1136 const char **attrs
, uint32 sd_flags
,
1141 args
.control
= ADS_SD_FLAGS_OID
;
1142 args
.val
= sd_flags
;
1143 args
.critical
= True
;
1145 return ads_do_search_all_args(ads
, bind_path
, scope
, expr
, attrs
, &args
, res
);
1150 * Run a function on all results for a search. Uses ads_do_paged_search() and
1151 * runs the function as each page is returned, using ads_process_results()
1152 * @param ads connection to ads server
1153 * @param bind_path Base dn for the search
1154 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1155 * @param expr Search expression - specified in local charset
1156 * @param attrs Attributes to retrieve - specified in UTF-8 or ascii
1157 * @param fn Function which takes attr name, values list, and data_area
1158 * @param data_area Pointer which is passed to function on each call
1159 * @return status of search
1161 ADS_STATUS
ads_do_search_all_fn(ADS_STRUCT
*ads
, const char *bind_path
,
1162 int scope
, const char *expr
, const char **attrs
,
1163 bool (*fn
)(ADS_STRUCT
*, char *, void **, void *),
1166 struct berval
*cookie
= NULL
;
1171 status
= ads_do_paged_search(ads
, bind_path
, scope
, expr
, attrs
, &res
,
1174 if (!ADS_ERR_OK(status
)) return status
;
1176 ads_process_results(ads
, res
, fn
, data_area
);
1177 ads_msgfree(ads
, res
);
1180 status
= ads_do_paged_search(ads
, bind_path
, scope
, expr
, attrs
,
1181 &res
, &count
, &cookie
);
1183 if (!ADS_ERR_OK(status
)) break;
1185 ads_process_results(ads
, res
, fn
, data_area
);
1186 ads_msgfree(ads
, res
);
1193 * Do a search with a timeout.
1194 * @param ads connection to ads server
1195 * @param bind_path Base dn for the search
1196 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1197 * @param expr Search expression
1198 * @param attrs Attributes to retrieve
1199 * @param res ** which will contain results - free res* with ads_msgfree()
1200 * @return status of search
1202 ADS_STATUS
ads_do_search(ADS_STRUCT
*ads
, const char *bind_path
, int scope
,
1204 const char **attrs
, LDAPMessage
**res
)
1207 char *utf8_expr
, *utf8_path
, **search_attrs
= NULL
;
1208 size_t converted_size
;
1212 if (!(ctx
= talloc_init("ads_do_search"))) {
1213 DEBUG(1,("ads_do_search: talloc_init() failed!"));
1214 return ADS_ERROR(LDAP_NO_MEMORY
);
1217 /* 0 means the conversion worked but the result was empty
1218 so we only fail if it's negative. In any case, it always
1219 at least nulls out the dest */
1220 if (!push_utf8_talloc(ctx
, &utf8_expr
, expr
, &converted_size
) ||
1221 !push_utf8_talloc(ctx
, &utf8_path
, bind_path
, &converted_size
))
1223 DEBUG(1,("ads_do_search: push_utf8_talloc() failed!"));
1224 rc
= LDAP_NO_MEMORY
;
1228 if (!attrs
|| !(*attrs
))
1229 search_attrs
= NULL
;
1231 /* This would be the utf8-encoded version...*/
1232 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
1233 if (!(search_attrs
= str_list_copy(talloc_tos(), attrs
)))
1235 DEBUG(1,("ads_do_search: str_list_copy() failed!"));
1236 rc
= LDAP_NO_MEMORY
;
1241 /* see the note in ads_do_paged_search - we *must* disable referrals */
1242 ldap_set_option(ads
->ldap
.ld
, LDAP_OPT_REFERRALS
, LDAP_OPT_OFF
);
1244 rc
= ldap_search_with_timeout(ads
->ldap
.ld
, utf8_path
, scope
, utf8_expr
,
1245 search_attrs
, 0, NULL
, NULL
,
1247 (LDAPMessage
**)res
);
1249 if (rc
== LDAP_SIZELIMIT_EXCEEDED
) {
1250 DEBUG(3,("Warning! sizelimit exceeded in ldap. Truncating.\n"));
1255 talloc_destroy(ctx
);
1256 /* if/when we decide to utf8-encode attrs, take out this next line */
1257 TALLOC_FREE(search_attrs
);
1258 return ADS_ERROR(rc
);
1261 * Do a general ADS search
1262 * @param ads connection to ads server
1263 * @param res ** which will contain results - free res* with ads_msgfree()
1264 * @param expr Search expression
1265 * @param attrs Attributes to retrieve
1266 * @return status of search
1268 ADS_STATUS
ads_search(ADS_STRUCT
*ads
, LDAPMessage
**res
,
1269 const char *expr
, const char **attrs
)
1271 return ads_do_search(ads
, ads
->config
.bind_path
, LDAP_SCOPE_SUBTREE
,
1276 * Do a search on a specific DistinguishedName
1277 * @param ads connection to ads server
1278 * @param res ** which will contain results - free res* with ads_msgfree()
1279 * @param dn DistinguishName to search
1280 * @param attrs Attributes to retrieve
1281 * @return status of search
1283 ADS_STATUS
ads_search_dn(ADS_STRUCT
*ads
, LDAPMessage
**res
,
1284 const char *dn
, const char **attrs
)
1286 return ads_do_search(ads
, dn
, LDAP_SCOPE_BASE
, "(objectclass=*)",
1291 * Free up memory from a ads_search
1292 * @param ads connection to ads server
1293 * @param msg Search results to free
1295 void ads_msgfree(ADS_STRUCT
*ads
, LDAPMessage
*msg
)
1302 * Get a dn from search results
1303 * @param ads connection to ads server
1304 * @param msg Search result
1307 char *ads_get_dn(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
, LDAPMessage
*msg
)
1309 char *utf8_dn
, *unix_dn
;
1310 size_t converted_size
;
1312 utf8_dn
= ldap_get_dn(ads
->ldap
.ld
, msg
);
1315 DEBUG (5, ("ads_get_dn: ldap_get_dn failed\n"));
1319 if (!pull_utf8_talloc(mem_ctx
, &unix_dn
, utf8_dn
, &converted_size
)) {
1320 DEBUG(0,("ads_get_dn: string conversion failure utf8 [%s]\n",
1324 ldap_memfree(utf8_dn
);
1329 * Get the parent from a dn
1330 * @param dn the dn to return the parent from
1331 * @return parent dn string
1333 char *ads_parent_dn(const char *dn
)
1341 p
= strchr(dn
, ',');
1351 * Find a machine account given a hostname
1352 * @param ads connection to ads server
1353 * @param res ** which will contain results - free res* with ads_msgfree()
1354 * @param host Hostname to search for
1355 * @return status of search
1357 ADS_STATUS
ads_find_machine_acct(ADS_STRUCT
*ads
, LDAPMessage
**res
,
1358 const char *machine
)
1362 const char *attrs
[] = {"*", "nTSecurityDescriptor", NULL
};
1366 /* the easiest way to find a machine account anywhere in the tree
1367 is to look for hostname$ */
1368 if (asprintf(&expr
, "(samAccountName=%s$)", machine
) == -1) {
1369 DEBUG(1, ("asprintf failed!\n"));
1370 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY
);
1373 status
= ads_search(ads
, res
, expr
, attrs
);
1379 * Initialize a list of mods to be used in a modify request
1380 * @param ctx An initialized TALLOC_CTX
1381 * @return allocated ADS_MODLIST
1383 ADS_MODLIST
ads_init_mods(TALLOC_CTX
*ctx
)
1385 #define ADS_MODLIST_ALLOC_SIZE 10
1388 if ((mods
= talloc_zero_array(ctx
, LDAPMod
*, ADS_MODLIST_ALLOC_SIZE
+ 1)))
1389 /* -1 is safety to make sure we don't go over the end.
1390 need to reset it to NULL before doing ldap modify */
1391 mods
[ADS_MODLIST_ALLOC_SIZE
] = (LDAPMod
*) -1;
1393 return (ADS_MODLIST
)mods
;
1398 add an attribute to the list, with values list already constructed
1400 static ADS_STATUS
ads_modlist_add(TALLOC_CTX
*ctx
, ADS_MODLIST
*mods
,
1401 int mod_op
, const char *name
,
1402 const void *_invals
)
1404 const void **invals
= (const void **)_invals
;
1406 LDAPMod
**modlist
= (LDAPMod
**) *mods
;
1407 struct berval
**ber_values
= NULL
;
1408 char **char_values
= NULL
;
1411 mod_op
= LDAP_MOD_DELETE
;
1413 if (mod_op
& LDAP_MOD_BVALUES
)
1414 ber_values
= ads_dup_values(ctx
,
1415 (const struct berval
**)invals
);
1417 char_values
= ads_push_strvals(ctx
,
1418 (const char **) invals
);
1421 /* find the first empty slot */
1422 for (curmod
=0; modlist
[curmod
] && modlist
[curmod
] != (LDAPMod
*) -1;
1424 if (modlist
[curmod
] == (LDAPMod
*) -1) {
1425 if (!(modlist
= talloc_realloc(ctx
, modlist
, LDAPMod
*,
1426 curmod
+ADS_MODLIST_ALLOC_SIZE
+1)))
1427 return ADS_ERROR(LDAP_NO_MEMORY
);
1428 memset(&modlist
[curmod
], 0,
1429 ADS_MODLIST_ALLOC_SIZE
*sizeof(LDAPMod
*));
1430 modlist
[curmod
+ADS_MODLIST_ALLOC_SIZE
] = (LDAPMod
*) -1;
1431 *mods
= (ADS_MODLIST
)modlist
;
1434 if (!(modlist
[curmod
] = talloc_zero(ctx
, LDAPMod
)))
1435 return ADS_ERROR(LDAP_NO_MEMORY
);
1436 modlist
[curmod
]->mod_type
= talloc_strdup(ctx
, name
);
1437 if (mod_op
& LDAP_MOD_BVALUES
) {
1438 modlist
[curmod
]->mod_bvalues
= ber_values
;
1439 } else if (mod_op
& LDAP_MOD_DELETE
) {
1440 modlist
[curmod
]->mod_values
= NULL
;
1442 modlist
[curmod
]->mod_values
= char_values
;
1445 modlist
[curmod
]->mod_op
= mod_op
;
1446 return ADS_ERROR(LDAP_SUCCESS
);
1450 * Add a single string value to a mod list
1451 * @param ctx An initialized TALLOC_CTX
1452 * @param mods An initialized ADS_MODLIST
1453 * @param name The attribute name to add
1454 * @param val The value to add - NULL means DELETE
1455 * @return ADS STATUS indicating success of add
1457 ADS_STATUS
ads_mod_str(TALLOC_CTX
*ctx
, ADS_MODLIST
*mods
,
1458 const char *name
, const char *val
)
1460 const char *values
[2];
1466 return ads_modlist_add(ctx
, mods
, LDAP_MOD_DELETE
, name
, NULL
);
1467 return ads_modlist_add(ctx
, mods
, LDAP_MOD_REPLACE
, name
, values
);
1471 * Add an array of string values to a mod list
1472 * @param ctx An initialized TALLOC_CTX
1473 * @param mods An initialized ADS_MODLIST
1474 * @param name The attribute name to add
1475 * @param vals The array of string values to add - NULL means DELETE
1476 * @return ADS STATUS indicating success of add
1478 ADS_STATUS
ads_mod_strlist(TALLOC_CTX
*ctx
, ADS_MODLIST
*mods
,
1479 const char *name
, const char **vals
)
1482 return ads_modlist_add(ctx
, mods
, LDAP_MOD_DELETE
, name
, NULL
);
1483 return ads_modlist_add(ctx
, mods
, LDAP_MOD_REPLACE
,
1484 name
, (const void **) vals
);
1489 * Add a single ber-encoded value to a mod list
1490 * @param ctx An initialized TALLOC_CTX
1491 * @param mods An initialized ADS_MODLIST
1492 * @param name The attribute name to add
1493 * @param val The value to add - NULL means DELETE
1494 * @return ADS STATUS indicating success of add
1496 static ADS_STATUS
ads_mod_ber(TALLOC_CTX
*ctx
, ADS_MODLIST
*mods
,
1497 const char *name
, const struct berval
*val
)
1499 const struct berval
*values
[2];
1504 return ads_modlist_add(ctx
, mods
, LDAP_MOD_DELETE
, name
, NULL
);
1505 return ads_modlist_add(ctx
, mods
, LDAP_MOD_REPLACE
|LDAP_MOD_BVALUES
,
1506 name
, (const void **) values
);
1511 * Perform an ldap modify
1512 * @param ads connection to ads server
1513 * @param mod_dn DistinguishedName to modify
1514 * @param mods list of modifications to perform
1515 * @return status of modify
1517 ADS_STATUS
ads_gen_mod(ADS_STRUCT
*ads
, const char *mod_dn
, ADS_MODLIST mods
)
1520 char *utf8_dn
= NULL
;
1521 size_t converted_size
;
1523 this control is needed to modify that contains a currently
1524 non-existent attribute (but allowable for the object) to run
1526 LDAPControl PermitModify
= {
1527 discard_const_p(char, ADS_PERMIT_MODIFY_OID
),
1530 LDAPControl
*controls
[2];
1532 controls
[0] = &PermitModify
;
1535 if (!push_utf8_talloc(talloc_tos(), &utf8_dn
, mod_dn
, &converted_size
)) {
1536 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY
);
1539 /* find the end of the list, marked by NULL or -1 */
1540 for(i
=0;(mods
[i
]!=0)&&(mods
[i
]!=(LDAPMod
*) -1);i
++);
1541 /* make sure the end of the list is NULL */
1543 ret
= ldap_modify_ext_s(ads
->ldap
.ld
, utf8_dn
,
1544 (LDAPMod
**) mods
, controls
, NULL
);
1545 TALLOC_FREE(utf8_dn
);
1546 return ADS_ERROR(ret
);
1550 * Perform an ldap add
1551 * @param ads connection to ads server
1552 * @param new_dn DistinguishedName to add
1553 * @param mods list of attributes and values for DN
1554 * @return status of add
1556 ADS_STATUS
ads_gen_add(ADS_STRUCT
*ads
, const char *new_dn
, ADS_MODLIST mods
)
1559 char *utf8_dn
= NULL
;
1560 size_t converted_size
;
1562 if (!push_utf8_talloc(talloc_tos(), &utf8_dn
, new_dn
, &converted_size
)) {
1563 DEBUG(1, ("ads_gen_add: push_utf8_talloc failed!"));
1564 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY
);
1567 /* find the end of the list, marked by NULL or -1 */
1568 for(i
=0;(mods
[i
]!=0)&&(mods
[i
]!=(LDAPMod
*) -1);i
++);
1569 /* make sure the end of the list is NULL */
1572 ret
= ldap_add_s(ads
->ldap
.ld
, utf8_dn
, (LDAPMod
**)mods
);
1573 TALLOC_FREE(utf8_dn
);
1574 return ADS_ERROR(ret
);
1578 * Delete a DistinguishedName
1579 * @param ads connection to ads server
1580 * @param new_dn DistinguishedName to delete
1581 * @return status of delete
1583 ADS_STATUS
ads_del_dn(ADS_STRUCT
*ads
, char *del_dn
)
1586 char *utf8_dn
= NULL
;
1587 size_t converted_size
;
1588 if (!push_utf8_talloc(talloc_tos(), &utf8_dn
, del_dn
, &converted_size
)) {
1589 DEBUG(1, ("ads_del_dn: push_utf8_talloc failed!"));
1590 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY
);
1593 ret
= ldap_delete_s(ads
->ldap
.ld
, utf8_dn
);
1594 TALLOC_FREE(utf8_dn
);
1595 return ADS_ERROR(ret
);
1599 * Build an org unit string
1600 * if org unit is Computers or blank then assume a container, otherwise
1601 * assume a / separated list of organisational units.
1602 * jmcd: '\' is now used for escapes so certain chars can be in the ou (e.g. #)
1603 * @param ads connection to ads server
1604 * @param org_unit Organizational unit
1605 * @return org unit string - caller must free
1607 char *ads_ou_string(ADS_STRUCT
*ads
, const char *org_unit
)
1611 if (!org_unit
|| !*org_unit
) {
1613 ret
= ads_default_ou_string(ads
, DS_GUID_COMPUTERS_CONTAINER
);
1615 /* samba4 might not yet respond to a wellknownobject-query */
1616 return ret
? ret
: SMB_STRDUP("cn=Computers");
1619 if (strequal(org_unit
, "Computers")) {
1620 return SMB_STRDUP("cn=Computers");
1623 /* jmcd: removed "\\" from the separation chars, because it is
1624 needed as an escape for chars like '#' which are valid in an
1626 return ads_build_path(org_unit
, "/", "ou=", 1);
1630 * Get a org unit string for a well-known GUID
1631 * @param ads connection to ads server
1632 * @param wknguid Well known GUID
1633 * @return org unit string - caller must free
1635 char *ads_default_ou_string(ADS_STRUCT
*ads
, const char *wknguid
)
1638 LDAPMessage
*res
= NULL
;
1639 char *base
, *wkn_dn
= NULL
, *ret
= NULL
, **wkn_dn_exp
= NULL
,
1640 **bind_dn_exp
= NULL
;
1641 const char *attrs
[] = {"distinguishedName", NULL
};
1642 int new_ln
, wkn_ln
, bind_ln
, i
;
1644 if (wknguid
== NULL
) {
1648 if (asprintf(&base
, "<WKGUID=%s,%s>", wknguid
, ads
->config
.bind_path
) == -1) {
1649 DEBUG(1, ("asprintf failed!\n"));
1653 status
= ads_search_dn(ads
, &res
, base
, attrs
);
1654 if (!ADS_ERR_OK(status
)) {
1655 DEBUG(1,("Failed while searching for: %s\n", base
));
1659 if (ads_count_replies(ads
, res
) != 1) {
1663 /* substitute the bind-path from the well-known-guid-search result */
1664 wkn_dn
= ads_get_dn(ads
, talloc_tos(), res
);
1669 wkn_dn_exp
= ldap_explode_dn(wkn_dn
, 0);
1674 bind_dn_exp
= ldap_explode_dn(ads
->config
.bind_path
, 0);
1679 for (wkn_ln
=0; wkn_dn_exp
[wkn_ln
]; wkn_ln
++)
1681 for (bind_ln
=0; bind_dn_exp
[bind_ln
]; bind_ln
++)
1684 new_ln
= wkn_ln
- bind_ln
;
1686 ret
= SMB_STRDUP(wkn_dn_exp
[0]);
1691 for (i
=1; i
< new_ln
; i
++) {
1694 if (asprintf(&s
, "%s,%s", ret
, wkn_dn_exp
[i
]) == -1) {
1700 ret
= SMB_STRDUP(s
);
1709 ads_msgfree(ads
, res
);
1710 TALLOC_FREE(wkn_dn
);
1712 ldap_value_free(wkn_dn_exp
);
1715 ldap_value_free(bind_dn_exp
);
1722 * Adds (appends) an item to an attribute array, rather then
1723 * replacing the whole list
1724 * @param ctx An initialized TALLOC_CTX
1725 * @param mods An initialized ADS_MODLIST
1726 * @param name name of the ldap attribute to append to
1727 * @param vals an array of values to add
1728 * @return status of addition
1731 ADS_STATUS
ads_add_strlist(TALLOC_CTX
*ctx
, ADS_MODLIST
*mods
,
1732 const char *name
, const char **vals
)
1734 return ads_modlist_add(ctx
, mods
, LDAP_MOD_ADD
, name
,
1735 (const void *) vals
);
1739 * Determines the an account's current KVNO via an LDAP lookup
1740 * @param ads An initialized ADS_STRUCT
1741 * @param account_name the NT samaccountname.
1742 * @return the kvno for the account, or -1 in case of a failure.
1745 uint32
ads_get_kvno(ADS_STRUCT
*ads
, const char *account_name
)
1747 LDAPMessage
*res
= NULL
;
1748 uint32 kvno
= (uint32
)-1; /* -1 indicates a failure */
1750 const char *attrs
[] = {"msDS-KeyVersionNumber", NULL
};
1751 char *dn_string
= NULL
;
1752 ADS_STATUS ret
= ADS_ERROR(LDAP_SUCCESS
);
1754 DEBUG(5,("ads_get_kvno: Searching for account %s\n", account_name
));
1755 if (asprintf(&filter
, "(samAccountName=%s)", account_name
) == -1) {
1758 ret
= ads_search(ads
, &res
, filter
, attrs
);
1760 if (!ADS_ERR_OK(ret
) || (ads_count_replies(ads
, res
) != 1)) {
1761 DEBUG(1,("ads_get_kvno: Account for %s not found.\n", account_name
));
1762 ads_msgfree(ads
, res
);
1766 dn_string
= ads_get_dn(ads
, talloc_tos(), res
);
1768 DEBUG(0,("ads_get_kvno: out of memory.\n"));
1769 ads_msgfree(ads
, res
);
1772 DEBUG(5,("ads_get_kvno: Using: %s\n", dn_string
));
1773 TALLOC_FREE(dn_string
);
1775 /* ---------------------------------------------------------
1776 * 0 is returned as a default KVNO from this point on...
1777 * This is done because Windows 2000 does not support key
1778 * version numbers. Chances are that a failure in the next
1779 * step is simply due to Windows 2000 being used for a
1780 * domain controller. */
1783 if (!ads_pull_uint32(ads
, res
, "msDS-KeyVersionNumber", &kvno
)) {
1784 DEBUG(3,("ads_get_kvno: Error Determining KVNO!\n"));
1785 DEBUG(3,("ads_get_kvno: Windows 2000 does not support KVNO's, so this may be normal.\n"));
1786 ads_msgfree(ads
, res
);
1791 DEBUG(5,("ads_get_kvno: Looked Up KVNO of: %d\n", kvno
));
1792 ads_msgfree(ads
, res
);
1797 * Determines the computer account's current KVNO via an LDAP lookup
1798 * @param ads An initialized ADS_STRUCT
1799 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1800 * @return the kvno for the computer account, or -1 in case of a failure.
1803 uint32_t ads_get_machine_kvno(ADS_STRUCT
*ads
, const char *machine_name
)
1805 char *computer_account
= NULL
;
1808 if (asprintf(&computer_account
, "%s$", machine_name
) < 0) {
1812 kvno
= ads_get_kvno(ads
, computer_account
);
1813 free(computer_account
);
1819 * This clears out all registered spn's for a given hostname
1820 * @param ads An initilaized ADS_STRUCT
1821 * @param machine_name the NetBIOS name of the computer.
1822 * @return 0 upon success, non-zero otherwise.
1825 ADS_STATUS
ads_clear_service_principal_names(ADS_STRUCT
*ads
, const char *machine_name
)
1828 LDAPMessage
*res
= NULL
;
1830 const char *servicePrincipalName
[1] = {NULL
};
1831 ADS_STATUS ret
= ADS_ERROR(LDAP_SUCCESS
);
1832 char *dn_string
= NULL
;
1834 ret
= ads_find_machine_acct(ads
, &res
, machine_name
);
1835 if (!ADS_ERR_OK(ret
) || ads_count_replies(ads
, res
) != 1) {
1836 DEBUG(5,("ads_clear_service_principal_names: WARNING: Host Account for %s not found... skipping operation.\n", machine_name
));
1837 DEBUG(5,("ads_clear_service_principal_names: WARNING: Service Principals for %s have NOT been cleared.\n", machine_name
));
1838 ads_msgfree(ads
, res
);
1839 return ADS_ERROR(LDAP_NO_SUCH_OBJECT
);
1842 DEBUG(5,("ads_clear_service_principal_names: Host account for %s found\n", machine_name
));
1843 ctx
= talloc_init("ads_clear_service_principal_names");
1845 ads_msgfree(ads
, res
);
1846 return ADS_ERROR(LDAP_NO_MEMORY
);
1849 if (!(mods
= ads_init_mods(ctx
))) {
1850 talloc_destroy(ctx
);
1851 ads_msgfree(ads
, res
);
1852 return ADS_ERROR(LDAP_NO_MEMORY
);
1854 ret
= ads_mod_strlist(ctx
, &mods
, "servicePrincipalName", servicePrincipalName
);
1855 if (!ADS_ERR_OK(ret
)) {
1856 DEBUG(1,("ads_clear_service_principal_names: Error creating strlist.\n"));
1857 ads_msgfree(ads
, res
);
1858 talloc_destroy(ctx
);
1861 dn_string
= ads_get_dn(ads
, talloc_tos(), res
);
1863 talloc_destroy(ctx
);
1864 ads_msgfree(ads
, res
);
1865 return ADS_ERROR(LDAP_NO_MEMORY
);
1867 ret
= ads_gen_mod(ads
, dn_string
, mods
);
1868 TALLOC_FREE(dn_string
);
1869 if (!ADS_ERR_OK(ret
)) {
1870 DEBUG(1,("ads_clear_service_principal_names: Error: Updating Service Principals for machine %s in LDAP\n",
1872 ads_msgfree(ads
, res
);
1873 talloc_destroy(ctx
);
1877 ads_msgfree(ads
, res
);
1878 talloc_destroy(ctx
);
1883 * This adds a service principal name to an existing computer account
1884 * (found by hostname) in AD.
1885 * @param ads An initialized ADS_STRUCT
1886 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1887 * @param my_fqdn The fully qualified DNS name of the machine
1888 * @param spn A string of the service principal to add, i.e. 'host'
1889 * @return 0 upon sucess, or non-zero if a failure occurs
1892 ADS_STATUS
ads_add_service_principal_name(ADS_STRUCT
*ads
, const char *machine_name
,
1893 const char *my_fqdn
, const char *spn
)
1897 LDAPMessage
*res
= NULL
;
1900 char *dn_string
= NULL
;
1901 const char *servicePrincipalName
[3] = {NULL
, NULL
, NULL
};
1903 ret
= ads_find_machine_acct(ads
, &res
, machine_name
);
1904 if (!ADS_ERR_OK(ret
) || ads_count_replies(ads
, res
) != 1) {
1905 DEBUG(1,("ads_add_service_principal_name: WARNING: Host Account for %s not found... skipping operation.\n",
1907 DEBUG(1,("ads_add_service_principal_name: WARNING: Service Principal '%s/%s@%s' has NOT been added.\n",
1908 spn
, machine_name
, ads
->config
.realm
));
1909 ads_msgfree(ads
, res
);
1910 return ADS_ERROR(LDAP_NO_SUCH_OBJECT
);
1913 DEBUG(1,("ads_add_service_principal_name: Host account for %s found\n", machine_name
));
1914 if (!(ctx
= talloc_init("ads_add_service_principal_name"))) {
1915 ads_msgfree(ads
, res
);
1916 return ADS_ERROR(LDAP_NO_MEMORY
);
1919 /* add short name spn */
1921 if ( (psp1
= talloc_asprintf(ctx
, "%s/%s", spn
, machine_name
)) == NULL
) {
1922 talloc_destroy(ctx
);
1923 ads_msgfree(ads
, res
);
1924 return ADS_ERROR(LDAP_NO_MEMORY
);
1927 strlower_m(&psp1
[strlen(spn
)]);
1928 servicePrincipalName
[0] = psp1
;
1930 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n",
1931 psp1
, machine_name
));
1934 /* add fully qualified spn */
1936 if ( (psp2
= talloc_asprintf(ctx
, "%s/%s", spn
, my_fqdn
)) == NULL
) {
1937 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
1941 strlower_m(&psp2
[strlen(spn
)]);
1942 servicePrincipalName
[1] = psp2
;
1944 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n",
1945 psp2
, machine_name
));
1947 if ( (mods
= ads_init_mods(ctx
)) == NULL
) {
1948 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
1952 ret
= ads_add_strlist(ctx
, &mods
, "servicePrincipalName", servicePrincipalName
);
1953 if (!ADS_ERR_OK(ret
)) {
1954 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
1958 if ( (dn_string
= ads_get_dn(ads
, ctx
, res
)) == NULL
) {
1959 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
1963 ret
= ads_gen_mod(ads
, dn_string
, mods
);
1964 if (!ADS_ERR_OK(ret
)) {
1965 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
1971 ads_msgfree(ads
, res
);
1976 * adds a machine account to the ADS server
1977 * @param ads An intialized ADS_STRUCT
1978 * @param machine_name - the NetBIOS machine name of this account.
1979 * @param account_type A number indicating the type of account to create
1980 * @param org_unit The LDAP path in which to place this account
1981 * @return 0 upon success, or non-zero otherwise
1984 ADS_STATUS
ads_create_machine_acct(ADS_STRUCT
*ads
, const char *machine_name
,
1985 const char *org_unit
)
1988 char *samAccountName
, *controlstr
;
1991 char *machine_escaped
= NULL
;
1993 const char *objectClass
[] = {"top", "person", "organizationalPerson",
1994 "user", "computer", NULL
};
1995 LDAPMessage
*res
= NULL
;
1996 uint32 acct_control
= ( UF_WORKSTATION_TRUST_ACCOUNT
|\
1997 UF_DONT_EXPIRE_PASSWD
|\
1998 UF_ACCOUNTDISABLE
);
2000 if (!(ctx
= talloc_init("ads_add_machine_acct")))
2001 return ADS_ERROR(LDAP_NO_MEMORY
);
2003 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
2005 machine_escaped
= escape_rdn_val_string_alloc(machine_name
);
2006 if (!machine_escaped
) {
2010 new_dn
= talloc_asprintf(ctx
, "cn=%s,%s", machine_escaped
, org_unit
);
2011 samAccountName
= talloc_asprintf(ctx
, "%s$", machine_name
);
2013 if ( !new_dn
|| !samAccountName
) {
2017 #ifndef ENCTYPE_ARCFOUR_HMAC
2018 acct_control
|= UF_USE_DES_KEY_ONLY
;
2021 if (!(controlstr
= talloc_asprintf(ctx
, "%u", acct_control
))) {
2025 if (!(mods
= ads_init_mods(ctx
))) {
2029 ads_mod_str(ctx
, &mods
, "cn", machine_name
);
2030 ads_mod_str(ctx
, &mods
, "sAMAccountName", samAccountName
);
2031 ads_mod_strlist(ctx
, &mods
, "objectClass", objectClass
);
2032 ads_mod_str(ctx
, &mods
, "userAccountControl", controlstr
);
2034 ret
= ads_gen_add(ads
, new_dn
, mods
);
2037 SAFE_FREE(machine_escaped
);
2038 ads_msgfree(ads
, res
);
2039 talloc_destroy(ctx
);
2045 * move a machine account to another OU on the ADS server
2046 * @param ads - An intialized ADS_STRUCT
2047 * @param machine_name - the NetBIOS machine name of this account.
2048 * @param org_unit - The LDAP path in which to place this account
2049 * @param moved - whether we moved the machine account (optional)
2050 * @return 0 upon success, or non-zero otherwise
2053 ADS_STATUS
ads_move_machine_acct(ADS_STRUCT
*ads
, const char *machine_name
,
2054 const char *org_unit
, bool *moved
)
2058 LDAPMessage
*res
= NULL
;
2059 char *filter
= NULL
;
2060 char *computer_dn
= NULL
;
2062 char *computer_rdn
= NULL
;
2063 bool need_move
= False
;
2065 if (asprintf(&filter
, "(samAccountName=%s$)", machine_name
) == -1) {
2066 rc
= ADS_ERROR(LDAP_NO_MEMORY
);
2070 /* Find pre-existing machine */
2071 rc
= ads_search(ads
, &res
, filter
, NULL
);
2072 if (!ADS_ERR_OK(rc
)) {
2076 computer_dn
= ads_get_dn(ads
, talloc_tos(), res
);
2078 rc
= ADS_ERROR(LDAP_NO_MEMORY
);
2082 parent_dn
= ads_parent_dn(computer_dn
);
2083 if (strequal(parent_dn
, org_unit
)) {
2089 if (asprintf(&computer_rdn
, "CN=%s", machine_name
) == -1) {
2090 rc
= ADS_ERROR(LDAP_NO_MEMORY
);
2094 ldap_status
= ldap_rename_s(ads
->ldap
.ld
, computer_dn
, computer_rdn
,
2095 org_unit
, 1, NULL
, NULL
);
2096 rc
= ADS_ERROR(ldap_status
);
2099 ads_msgfree(ads
, res
);
2101 TALLOC_FREE(computer_dn
);
2102 SAFE_FREE(computer_rdn
);
2104 if (!ADS_ERR_OK(rc
)) {
2116 dump a binary result from ldap
2118 static void dump_binary(ADS_STRUCT
*ads
, const char *field
, struct berval
**values
)
2121 for (i
=0; values
[i
]; i
++) {
2122 printf("%s: ", field
);
2123 for (j
=0; j
<values
[i
]->bv_len
; j
++) {
2124 printf("%02X", (unsigned char)values
[i
]->bv_val
[j
]);
2130 static void dump_guid(ADS_STRUCT
*ads
, const char *field
, struct berval
**values
)
2133 for (i
=0; values
[i
]; i
++) {
2135 DATA_BLOB in
= data_blob_const(values
[i
]->bv_val
, values
[i
]->bv_len
);
2138 status
= GUID_from_ndr_blob(&in
, &guid
);
2139 if (NT_STATUS_IS_OK(status
)) {
2140 printf("%s: %s\n", field
, GUID_string(talloc_tos(), &guid
));
2142 printf("%s: INVALID GUID\n", field
);
2148 dump a sid result from ldap
2150 static void dump_sid(ADS_STRUCT
*ads
, const char *field
, struct berval
**values
)
2153 for (i
=0; values
[i
]; i
++) {
2156 if (!sid_parse(values
[i
]->bv_val
, values
[i
]->bv_len
, &sid
)) {
2159 printf("%s: %s\n", field
, sid_to_fstring(tmp
, &sid
));
2164 dump ntSecurityDescriptor
2166 static void dump_sd(ADS_STRUCT
*ads
, const char *filed
, struct berval
**values
)
2168 TALLOC_CTX
*frame
= talloc_stackframe();
2169 struct security_descriptor
*psd
;
2172 status
= unmarshall_sec_desc(talloc_tos(), (uint8
*)values
[0]->bv_val
,
2173 values
[0]->bv_len
, &psd
);
2174 if (!NT_STATUS_IS_OK(status
)) {
2175 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
2176 nt_errstr(status
)));
2182 ads_disp_sd(ads
, talloc_tos(), psd
);
2189 dump a string result from ldap
2191 static void dump_string(const char *field
, char **values
)
2194 for (i
=0; values
[i
]; i
++) {
2195 printf("%s: %s\n", field
, values
[i
]);
2200 dump a field from LDAP on stdout
2204 static bool ads_dump_field(ADS_STRUCT
*ads
, char *field
, void **values
, void *data_area
)
2209 void (*handler
)(ADS_STRUCT
*, const char *, struct berval
**);
2211 {"objectGUID", False
, dump_guid
},
2212 {"netbootGUID", False
, dump_guid
},
2213 {"nTSecurityDescriptor", False
, dump_sd
},
2214 {"dnsRecord", False
, dump_binary
},
2215 {"objectSid", False
, dump_sid
},
2216 {"tokenGroups", False
, dump_sid
},
2217 {"tokenGroupsNoGCAcceptable", False
, dump_sid
},
2218 {"tokengroupsGlobalandUniversal", False
, dump_sid
},
2219 {"mS-DS-CreatorSID", False
, dump_sid
},
2220 {"msExchMailboxGuid", False
, dump_guid
},
2225 if (!field
) { /* must be end of an entry */
2230 for (i
=0; handlers
[i
].name
; i
++) {
2231 if (strcasecmp_m(handlers
[i
].name
, field
) == 0) {
2232 if (!values
) /* first time, indicate string or not */
2233 return handlers
[i
].string
;
2234 handlers
[i
].handler(ads
, field
, (struct berval
**) values
);
2238 if (!handlers
[i
].name
) {
2239 if (!values
) /* first time, indicate string conversion */
2241 dump_string(field
, (char **)values
);
2247 * Dump a result from LDAP on stdout
2248 * used for debugging
2249 * @param ads connection to ads server
2250 * @param res Results to dump
2253 void ads_dump(ADS_STRUCT
*ads
, LDAPMessage
*res
)
2255 ads_process_results(ads
, res
, ads_dump_field
, NULL
);
2259 * Walk through results, calling a function for each entry found.
2260 * The function receives a field name, a berval * array of values,
2261 * and a data area passed through from the start. The function is
2262 * called once with null for field and values at the end of each
2264 * @param ads connection to ads server
2265 * @param res Results to process
2266 * @param fn Function for processing each result
2267 * @param data_area user-defined area to pass to function
2269 void ads_process_results(ADS_STRUCT
*ads
, LDAPMessage
*res
,
2270 bool (*fn
)(ADS_STRUCT
*, char *, void **, void *),
2275 size_t converted_size
;
2277 if (!(ctx
= talloc_init("ads_process_results")))
2280 for (msg
= ads_first_entry(ads
, res
); msg
;
2281 msg
= ads_next_entry(ads
, msg
)) {
2285 for (utf8_field
=ldap_first_attribute(ads
->ldap
.ld
,
2286 (LDAPMessage
*)msg
,&b
);
2288 utf8_field
=ldap_next_attribute(ads
->ldap
.ld
,
2289 (LDAPMessage
*)msg
,b
)) {
2290 struct berval
**ber_vals
;
2291 char **str_vals
, **utf8_vals
;
2295 if (!pull_utf8_talloc(ctx
, &field
, utf8_field
,
2298 DEBUG(0,("ads_process_results: "
2299 "pull_utf8_talloc failed: %s",
2303 string
= fn(ads
, field
, NULL
, data_area
);
2306 utf8_vals
= ldap_get_values(ads
->ldap
.ld
,
2307 (LDAPMessage
*)msg
, field
);
2308 str_vals
= ads_pull_strvals(ctx
,
2309 (const char **) utf8_vals
);
2310 fn(ads
, field
, (void **) str_vals
, data_area
);
2311 ldap_value_free(utf8_vals
);
2313 ber_vals
= ldap_get_values_len(ads
->ldap
.ld
,
2314 (LDAPMessage
*)msg
, field
);
2315 fn(ads
, field
, (void **) ber_vals
, data_area
);
2317 ldap_value_free_len(ber_vals
);
2319 ldap_memfree(utf8_field
);
2322 talloc_free_children(ctx
);
2323 fn(ads
, NULL
, NULL
, data_area
); /* completed an entry */
2326 talloc_destroy(ctx
);
2330 * count how many replies are in a LDAPMessage
2331 * @param ads connection to ads server
2332 * @param res Results to count
2333 * @return number of replies
2335 int ads_count_replies(ADS_STRUCT
*ads
, void *res
)
2337 return ldap_count_entries(ads
->ldap
.ld
, (LDAPMessage
*)res
);
2341 * pull the first entry from a ADS result
2342 * @param ads connection to ads server
2343 * @param res Results of search
2344 * @return first entry from result
2346 LDAPMessage
*ads_first_entry(ADS_STRUCT
*ads
, LDAPMessage
*res
)
2348 return ldap_first_entry(ads
->ldap
.ld
, res
);
2352 * pull the next entry from a ADS result
2353 * @param ads connection to ads server
2354 * @param res Results of search
2355 * @return next entry from result
2357 LDAPMessage
*ads_next_entry(ADS_STRUCT
*ads
, LDAPMessage
*res
)
2359 return ldap_next_entry(ads
->ldap
.ld
, res
);
2363 * pull the first message from a ADS result
2364 * @param ads connection to ads server
2365 * @param res Results of search
2366 * @return first message from result
2368 LDAPMessage
*ads_first_message(ADS_STRUCT
*ads
, LDAPMessage
*res
)
2370 return ldap_first_message(ads
->ldap
.ld
, res
);
2374 * pull the next message from a ADS result
2375 * @param ads connection to ads server
2376 * @param res Results of search
2377 * @return next message from result
2379 LDAPMessage
*ads_next_message(ADS_STRUCT
*ads
, LDAPMessage
*res
)
2381 return ldap_next_message(ads
->ldap
.ld
, res
);
2385 * pull a single string from a ADS result
2386 * @param ads connection to ads server
2387 * @param mem_ctx TALLOC_CTX to use for allocating result string
2388 * @param msg Results of search
2389 * @param field Attribute to retrieve
2390 * @return Result string in talloc context
2392 char *ads_pull_string(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
, LDAPMessage
*msg
,
2398 size_t converted_size
;
2400 values
= ldap_get_values(ads
->ldap
.ld
, msg
, field
);
2404 if (values
[0] && pull_utf8_talloc(mem_ctx
, &ux_string
, values
[0],
2409 ldap_value_free(values
);
2414 * pull an array of strings from a ADS result
2415 * @param ads connection to ads server
2416 * @param mem_ctx TALLOC_CTX to use for allocating result string
2417 * @param msg Results of search
2418 * @param field Attribute to retrieve
2419 * @return Result strings in talloc context
2421 char **ads_pull_strings(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
,
2422 LDAPMessage
*msg
, const char *field
,
2428 size_t converted_size
;
2430 values
= ldap_get_values(ads
->ldap
.ld
, msg
, field
);
2434 *num_values
= ldap_count_values(values
);
2436 ret
= talloc_array(mem_ctx
, char *, *num_values
+ 1);
2438 ldap_value_free(values
);
2442 for (i
=0;i
<*num_values
;i
++) {
2443 if (!pull_utf8_talloc(mem_ctx
, &ret
[i
], values
[i
],
2446 ldap_value_free(values
);
2452 ldap_value_free(values
);
2457 * pull an array of strings from a ADS result
2458 * (handle large multivalue attributes with range retrieval)
2459 * @param ads connection to ads server
2460 * @param mem_ctx TALLOC_CTX to use for allocating result string
2461 * @param msg Results of search
2462 * @param field Attribute to retrieve
2463 * @param current_strings strings returned by a previous call to this function
2464 * @param next_attribute The next query should ask for this attribute
2465 * @param num_values How many values did we get this time?
2466 * @param more_values Are there more values to get?
2467 * @return Result strings in talloc context
2469 char **ads_pull_strings_range(ADS_STRUCT
*ads
,
2470 TALLOC_CTX
*mem_ctx
,
2471 LDAPMessage
*msg
, const char *field
,
2472 char **current_strings
,
2473 const char **next_attribute
,
2474 size_t *num_strings
,
2478 char *expected_range_attrib
, *range_attr
;
2479 BerElement
*ptr
= NULL
;
2482 size_t num_new_strings
;
2483 unsigned long int range_start
;
2484 unsigned long int range_end
;
2486 /* we might have been given the whole lot anyway */
2487 if ((strings
= ads_pull_strings(ads
, mem_ctx
, msg
, field
, num_strings
))) {
2488 *more_strings
= False
;
2492 expected_range_attrib
= talloc_asprintf(mem_ctx
, "%s;Range=", field
);
2494 /* look for Range result */
2495 for (attr
= ldap_first_attribute(ads
->ldap
.ld
, (LDAPMessage
*)msg
, &ptr
);
2497 attr
= ldap_next_attribute(ads
->ldap
.ld
, (LDAPMessage
*)msg
, ptr
)) {
2498 /* we ignore the fact that this is utf8, as all attributes are ascii... */
2499 if (strnequal(attr
, expected_range_attrib
, strlen(expected_range_attrib
))) {
2507 /* nothing here - this field is just empty */
2508 *more_strings
= False
;
2512 if (sscanf(&range_attr
[strlen(expected_range_attrib
)], "%lu-%lu",
2513 &range_start
, &range_end
) == 2) {
2514 *more_strings
= True
;
2516 if (sscanf(&range_attr
[strlen(expected_range_attrib
)], "%lu-*",
2517 &range_start
) == 1) {
2518 *more_strings
= False
;
2520 DEBUG(1, ("ads_pull_strings_range: Cannot parse Range attriubte (%s)\n",
2522 ldap_memfree(range_attr
);
2523 *more_strings
= False
;
2528 if ((*num_strings
) != range_start
) {
2529 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) doesn't start at %u, but at %lu"
2530 " - aborting range retreival\n",
2531 range_attr
, (unsigned int)(*num_strings
) + 1, range_start
));
2532 ldap_memfree(range_attr
);
2533 *more_strings
= False
;
2537 new_strings
= ads_pull_strings(ads
, mem_ctx
, msg
, range_attr
, &num_new_strings
);
2539 if (*more_strings
&& ((*num_strings
+ num_new_strings
) != (range_end
+ 1))) {
2540 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) tells us we have %lu "
2541 "strings in this bunch, but we only got %lu - aborting range retreival\n",
2542 range_attr
, (unsigned long int)range_end
- range_start
+ 1,
2543 (unsigned long int)num_new_strings
));
2544 ldap_memfree(range_attr
);
2545 *more_strings
= False
;
2549 strings
= talloc_realloc(mem_ctx
, current_strings
, char *,
2550 *num_strings
+ num_new_strings
);
2552 if (strings
== NULL
) {
2553 ldap_memfree(range_attr
);
2554 *more_strings
= False
;
2558 if (new_strings
&& num_new_strings
) {
2559 memcpy(&strings
[*num_strings
], new_strings
,
2560 sizeof(*new_strings
) * num_new_strings
);
2563 (*num_strings
) += num_new_strings
;
2565 if (*more_strings
) {
2566 *next_attribute
= talloc_asprintf(mem_ctx
,
2571 if (!*next_attribute
) {
2572 DEBUG(1, ("talloc_asprintf for next attribute failed!\n"));
2573 ldap_memfree(range_attr
);
2574 *more_strings
= False
;
2579 ldap_memfree(range_attr
);
2585 * pull a single uint32 from a ADS result
2586 * @param ads connection to ads server
2587 * @param msg Results of search
2588 * @param field Attribute to retrieve
2589 * @param v Pointer to int to store result
2590 * @return boolean inidicating success
2592 bool ads_pull_uint32(ADS_STRUCT
*ads
, LDAPMessage
*msg
, const char *field
,
2597 values
= ldap_get_values(ads
->ldap
.ld
, msg
, field
);
2601 ldap_value_free(values
);
2605 *v
= atoi(values
[0]);
2606 ldap_value_free(values
);
2611 * pull a single objectGUID from an ADS result
2612 * @param ads connection to ADS server
2613 * @param msg results of search
2614 * @param guid 37-byte area to receive text guid
2615 * @return boolean indicating success
2617 bool ads_pull_guid(ADS_STRUCT
*ads
, LDAPMessage
*msg
, struct GUID
*guid
)
2622 if (!smbldap_talloc_single_blob(talloc_tos(), ads
->ldap
.ld
, msg
, "objectGUID",
2627 status
= GUID_from_ndr_blob(&blob
, guid
);
2628 talloc_free(blob
.data
);
2629 return NT_STATUS_IS_OK(status
);
2634 * pull a single struct dom_sid from a ADS result
2635 * @param ads connection to ads server
2636 * @param msg Results of search
2637 * @param field Attribute to retrieve
2638 * @param sid Pointer to sid to store result
2639 * @return boolean inidicating success
2641 bool ads_pull_sid(ADS_STRUCT
*ads
, LDAPMessage
*msg
, const char *field
,
2642 struct dom_sid
*sid
)
2644 return smbldap_pull_sid(ads
->ldap
.ld
, msg
, field
, sid
);
2648 * pull an array of struct dom_sids from a ADS result
2649 * @param ads connection to ads server
2650 * @param mem_ctx TALLOC_CTX for allocating sid array
2651 * @param msg Results of search
2652 * @param field Attribute to retrieve
2653 * @param sids pointer to sid array to allocate
2654 * @return the count of SIDs pulled
2656 int ads_pull_sids(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
,
2657 LDAPMessage
*msg
, const char *field
, struct dom_sid
**sids
)
2659 struct berval
**values
;
2663 values
= ldap_get_values_len(ads
->ldap
.ld
, msg
, field
);
2668 for (i
=0; values
[i
]; i
++)
2672 (*sids
) = talloc_array(mem_ctx
, struct dom_sid
, i
);
2674 ldap_value_free_len(values
);
2682 for (i
=0; values
[i
]; i
++) {
2683 ret
= sid_parse(values
[i
]->bv_val
, values
[i
]->bv_len
, &(*sids
)[count
]);
2685 DEBUG(10, ("pulling SID: %s\n",
2686 sid_string_dbg(&(*sids
)[count
])));
2691 ldap_value_free_len(values
);
2696 * pull a struct security_descriptor from a ADS result
2697 * @param ads connection to ads server
2698 * @param mem_ctx TALLOC_CTX for allocating sid array
2699 * @param msg Results of search
2700 * @param field Attribute to retrieve
2701 * @param sd Pointer to *struct security_descriptor to store result (talloc()ed)
2702 * @return boolean inidicating success
2704 bool ads_pull_sd(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
,
2705 LDAPMessage
*msg
, const char *field
,
2706 struct security_descriptor
**sd
)
2708 struct berval
**values
;
2711 values
= ldap_get_values_len(ads
->ldap
.ld
, msg
, field
);
2713 if (!values
) return false;
2717 status
= unmarshall_sec_desc(mem_ctx
,
2718 (uint8
*)values
[0]->bv_val
,
2719 values
[0]->bv_len
, sd
);
2720 if (!NT_STATUS_IS_OK(status
)) {
2721 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
2722 nt_errstr(status
)));
2727 ldap_value_free_len(values
);
2732 * in order to support usernames longer than 21 characters we need to
2733 * use both the sAMAccountName and the userPrincipalName attributes
2734 * It seems that not all users have the userPrincipalName attribute set
2736 * @param ads connection to ads server
2737 * @param mem_ctx TALLOC_CTX for allocating sid array
2738 * @param msg Results of search
2739 * @return the username
2741 char *ads_pull_username(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
,
2747 /* lookup_name() only works on the sAMAccountName to
2748 returning the username portion of userPrincipalName
2749 breaks winbindd_getpwnam() */
2751 ret
= ads_pull_string(ads
, mem_ctx
, msg
, "userPrincipalName");
2752 if (ret
&& (p
= strchr_m(ret
, '@'))) {
2757 return ads_pull_string(ads
, mem_ctx
, msg
, "sAMAccountName");
2762 * find the update serial number - this is the core of the ldap cache
2763 * @param ads connection to ads server
2764 * @param ads connection to ADS server
2765 * @param usn Pointer to retrieved update serial number
2766 * @return status of search
2768 ADS_STATUS
ads_USN(ADS_STRUCT
*ads
, uint32
*usn
)
2770 const char *attrs
[] = {"highestCommittedUSN", NULL
};
2774 status
= ads_do_search_retry(ads
, "", LDAP_SCOPE_BASE
, "(objectclass=*)", attrs
, &res
);
2775 if (!ADS_ERR_OK(status
))
2778 if (ads_count_replies(ads
, res
) != 1) {
2779 ads_msgfree(ads
, res
);
2780 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED
);
2783 if (!ads_pull_uint32(ads
, res
, "highestCommittedUSN", usn
)) {
2784 ads_msgfree(ads
, res
);
2785 return ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE
);
2788 ads_msgfree(ads
, res
);
2792 /* parse a ADS timestring - typical string is
2793 '20020917091222.0Z0' which means 09:12.22 17th September
2795 static time_t ads_parse_time(const char *str
)
2801 if (sscanf(str
, "%4d%2d%2d%2d%2d%2d",
2802 &tm
.tm_year
, &tm
.tm_mon
, &tm
.tm_mday
,
2803 &tm
.tm_hour
, &tm
.tm_min
, &tm
.tm_sec
) != 6) {
2812 /********************************************************************
2813 ********************************************************************/
2815 ADS_STATUS
ads_current_time(ADS_STRUCT
*ads
)
2817 const char *attrs
[] = {"currentTime", NULL
};
2822 ADS_STRUCT
*ads_s
= ads
;
2824 if (!(ctx
= talloc_init("ads_current_time"))) {
2825 return ADS_ERROR(LDAP_NO_MEMORY
);
2828 /* establish a new ldap tcp session if necessary */
2830 if ( !ads
->ldap
.ld
) {
2831 if ( (ads_s
= ads_init( ads
->server
.realm
, ads
->server
.workgroup
,
2832 ads
->server
.ldap_server
)) == NULL
)
2836 ads_s
->auth
.flags
= ADS_AUTH_ANON_BIND
;
2837 status
= ads_connect( ads_s
);
2838 if ( !ADS_ERR_OK(status
))
2842 status
= ads_do_search(ads_s
, "", LDAP_SCOPE_BASE
, "(objectclass=*)", attrs
, &res
);
2843 if (!ADS_ERR_OK(status
)) {
2847 timestr
= ads_pull_string(ads_s
, ctx
, res
, "currentTime");
2849 ads_msgfree(ads_s
, res
);
2850 status
= ADS_ERROR(LDAP_NO_RESULTS_RETURNED
);
2854 /* but save the time and offset in the original ADS_STRUCT */
2856 ads
->config
.current_time
= ads_parse_time(timestr
);
2858 if (ads
->config
.current_time
!= 0) {
2859 ads
->auth
.time_offset
= ads
->config
.current_time
- time(NULL
);
2860 DEBUG(4,("KDC time offset is %d seconds\n", ads
->auth
.time_offset
));
2863 ads_msgfree(ads
, res
);
2865 status
= ADS_SUCCESS
;
2868 /* free any temporary ads connections */
2869 if ( ads_s
!= ads
) {
2870 ads_destroy( &ads_s
);
2872 talloc_destroy(ctx
);
2877 /********************************************************************
2878 ********************************************************************/
2880 ADS_STATUS
ads_domain_func_level(ADS_STRUCT
*ads
, uint32
*val
)
2882 const char *attrs
[] = {"domainFunctionality", NULL
};
2885 ADS_STRUCT
*ads_s
= ads
;
2887 *val
= DS_DOMAIN_FUNCTION_2000
;
2889 /* establish a new ldap tcp session if necessary */
2891 if ( !ads
->ldap
.ld
) {
2892 if ( (ads_s
= ads_init( ads
->server
.realm
, ads
->server
.workgroup
,
2893 ads
->server
.ldap_server
)) == NULL
)
2895 status
= ADS_ERROR_NT(NT_STATUS_NO_MEMORY
);
2898 ads_s
->auth
.flags
= ADS_AUTH_ANON_BIND
;
2899 status
= ads_connect( ads_s
);
2900 if ( !ADS_ERR_OK(status
))
2904 /* If the attribute does not exist assume it is a Windows 2000
2905 functional domain */
2907 status
= ads_do_search(ads_s
, "", LDAP_SCOPE_BASE
, "(objectclass=*)", attrs
, &res
);
2908 if (!ADS_ERR_OK(status
)) {
2909 if ( status
.err
.rc
== LDAP_NO_SUCH_ATTRIBUTE
) {
2910 status
= ADS_SUCCESS
;
2915 if ( !ads_pull_uint32(ads_s
, res
, "domainFunctionality", val
) ) {
2916 DEBUG(5,("ads_domain_func_level: Failed to pull the domainFunctionality attribute.\n"));
2918 DEBUG(3,("ads_domain_func_level: %d\n", *val
));
2921 ads_msgfree(ads
, res
);
2924 /* free any temporary ads connections */
2925 if ( ads_s
!= ads
) {
2926 ads_destroy( &ads_s
);
2933 * find the domain sid for our domain
2934 * @param ads connection to ads server
2935 * @param sid Pointer to domain sid
2936 * @return status of search
2938 ADS_STATUS
ads_domain_sid(ADS_STRUCT
*ads
, struct dom_sid
*sid
)
2940 const char *attrs
[] = {"objectSid", NULL
};
2944 rc
= ads_do_search_retry(ads
, ads
->config
.bind_path
, LDAP_SCOPE_BASE
, "(objectclass=*)",
2946 if (!ADS_ERR_OK(rc
)) return rc
;
2947 if (!ads_pull_sid(ads
, res
, "objectSid", sid
)) {
2948 ads_msgfree(ads
, res
);
2949 return ADS_ERROR_SYSTEM(ENOENT
);
2951 ads_msgfree(ads
, res
);
2957 * find our site name
2958 * @param ads connection to ads server
2959 * @param mem_ctx Pointer to talloc context
2960 * @param site_name Pointer to the sitename
2961 * @return status of search
2963 ADS_STATUS
ads_site_dn(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
, const char **site_name
)
2967 const char *dn
, *service_name
;
2968 const char *attrs
[] = { "dsServiceName", NULL
};
2970 status
= ads_do_search(ads
, "", LDAP_SCOPE_BASE
, "(objectclass=*)", attrs
, &res
);
2971 if (!ADS_ERR_OK(status
)) {
2975 service_name
= ads_pull_string(ads
, mem_ctx
, res
, "dsServiceName");
2976 if (service_name
== NULL
) {
2977 ads_msgfree(ads
, res
);
2978 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED
);
2981 ads_msgfree(ads
, res
);
2983 /* go up three levels */
2984 dn
= ads_parent_dn(ads_parent_dn(ads_parent_dn(service_name
)));
2986 return ADS_ERROR(LDAP_NO_MEMORY
);
2989 *site_name
= talloc_strdup(mem_ctx
, dn
);
2990 if (*site_name
== NULL
) {
2991 return ADS_ERROR(LDAP_NO_MEMORY
);
2996 dsServiceName: CN=NTDS Settings,CN=W2K3DC,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,DC=ber,DC=suse,DC=de
3001 * find the site dn where a machine resides
3002 * @param ads connection to ads server
3003 * @param mem_ctx Pointer to talloc context
3004 * @param computer_name name of the machine
3005 * @param site_name Pointer to the sitename
3006 * @return status of search
3008 ADS_STATUS
ads_site_dn_for_machine(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
, const char *computer_name
, const char **site_dn
)
3012 const char *parent
, *filter
;
3013 char *config_context
= NULL
;
3016 /* shortcut a query */
3017 if (strequal(computer_name
, ads
->config
.ldap_server_name
)) {
3018 return ads_site_dn(ads
, mem_ctx
, site_dn
);
3021 status
= ads_config_path(ads
, mem_ctx
, &config_context
);
3022 if (!ADS_ERR_OK(status
)) {
3026 filter
= talloc_asprintf(mem_ctx
, "(cn=%s)", computer_name
);
3027 if (filter
== NULL
) {
3028 return ADS_ERROR(LDAP_NO_MEMORY
);
3031 status
= ads_do_search(ads
, config_context
, LDAP_SCOPE_SUBTREE
,
3032 filter
, NULL
, &res
);
3033 if (!ADS_ERR_OK(status
)) {
3037 if (ads_count_replies(ads
, res
) != 1) {
3038 ads_msgfree(ads
, res
);
3039 return ADS_ERROR(LDAP_NO_SUCH_OBJECT
);
3042 dn
= ads_get_dn(ads
, mem_ctx
, res
);
3044 ads_msgfree(ads
, res
);
3045 return ADS_ERROR(LDAP_NO_MEMORY
);
3048 /* go up three levels */
3049 parent
= ads_parent_dn(ads_parent_dn(ads_parent_dn(dn
)));
3050 if (parent
== NULL
) {
3051 ads_msgfree(ads
, res
);
3053 return ADS_ERROR(LDAP_NO_MEMORY
);
3056 *site_dn
= talloc_strdup(mem_ctx
, parent
);
3057 if (*site_dn
== NULL
) {
3058 ads_msgfree(ads
, res
);
3060 return ADS_ERROR(LDAP_NO_MEMORY
);
3064 ads_msgfree(ads
, res
);
3070 * get the upn suffixes for a domain
3071 * @param ads connection to ads server
3072 * @param mem_ctx Pointer to talloc context
3073 * @param suffixes Pointer to an array of suffixes
3074 * @param num_suffixes Pointer to the number of suffixes
3075 * @return status of search
3077 ADS_STATUS
ads_upn_suffixes(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
, char ***suffixes
, size_t *num_suffixes
)
3082 char *config_context
= NULL
;
3083 const char *attrs
[] = { "uPNSuffixes", NULL
};
3085 status
= ads_config_path(ads
, mem_ctx
, &config_context
);
3086 if (!ADS_ERR_OK(status
)) {
3090 base
= talloc_asprintf(mem_ctx
, "cn=Partitions,%s", config_context
);
3092 return ADS_ERROR(LDAP_NO_MEMORY
);
3095 status
= ads_search_dn(ads
, &res
, base
, attrs
);
3096 if (!ADS_ERR_OK(status
)) {
3100 if (ads_count_replies(ads
, res
) != 1) {
3101 ads_msgfree(ads
, res
);
3102 return ADS_ERROR(LDAP_NO_SUCH_OBJECT
);
3105 (*suffixes
) = ads_pull_strings(ads
, mem_ctx
, res
, "uPNSuffixes", num_suffixes
);
3106 if ((*suffixes
) == NULL
) {
3107 ads_msgfree(ads
, res
);
3108 return ADS_ERROR(LDAP_NO_MEMORY
);
3111 ads_msgfree(ads
, res
);
3117 * get the joinable ous for a domain
3118 * @param ads connection to ads server
3119 * @param mem_ctx Pointer to talloc context
3120 * @param ous Pointer to an array of ous
3121 * @param num_ous Pointer to the number of ous
3122 * @return status of search
3124 ADS_STATUS
ads_get_joinable_ous(ADS_STRUCT
*ads
,
3125 TALLOC_CTX
*mem_ctx
,
3130 LDAPMessage
*res
= NULL
;
3131 LDAPMessage
*msg
= NULL
;
3132 const char *attrs
[] = { "dn", NULL
};
3135 status
= ads_search(ads
, &res
,
3136 "(|(objectClass=domain)(objectclass=organizationalUnit))",
3138 if (!ADS_ERR_OK(status
)) {
3142 count
= ads_count_replies(ads
, res
);
3144 ads_msgfree(ads
, res
);
3145 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED
);
3148 for (msg
= ads_first_entry(ads
, res
); msg
;
3149 msg
= ads_next_entry(ads
, msg
)) {
3153 dn
= ads_get_dn(ads
, talloc_tos(), msg
);
3155 ads_msgfree(ads
, res
);
3156 return ADS_ERROR(LDAP_NO_MEMORY
);
3159 if (!add_string_to_array(mem_ctx
, dn
,
3160 (const char ***)ous
,
3163 ads_msgfree(ads
, res
);
3164 return ADS_ERROR(LDAP_NO_MEMORY
);
3170 ads_msgfree(ads
, res
);
3177 * pull a struct dom_sid from an extended dn string
3178 * @param mem_ctx TALLOC_CTX
3179 * @param extended_dn string
3180 * @param flags string type of extended_dn
3181 * @param sid pointer to a struct dom_sid
3182 * @return NT_STATUS_OK on success,
3183 * NT_INVALID_PARAMETER on error,
3184 * NT_STATUS_NOT_FOUND if no SID present
3186 ADS_STATUS
ads_get_sid_from_extended_dn(TALLOC_CTX
*mem_ctx
,
3187 const char *extended_dn
,
3188 enum ads_extended_dn_flags flags
,
3189 struct dom_sid
*sid
)
3194 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER
);
3197 /* otherwise extended_dn gets stripped off */
3198 if ((dn
= talloc_strdup(mem_ctx
, extended_dn
)) == NULL
) {
3199 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER
);
3202 * ADS_EXTENDED_DN_HEX_STRING:
3203 * <GUID=238e1963cb390f4bb032ba0105525a29>;<SID=010500000000000515000000bb68c8fd6b61b427572eb04556040000>;CN=gd,OU=berlin,OU=suse,DC=ber,DC=suse,DC=de
3205 * ADS_EXTENDED_DN_STRING (only with w2k3):
3206 * <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
3208 * Object with no SID, such as an Exchange Public Folder
3209 * <GUID=28907fb4bdf6854993e7f0a10b504e7c>;CN=public,CN=Microsoft Exchange System Objects,DC=sd2k3ms,DC=west,DC=isilon,DC=com
3212 p
= strchr(dn
, ';');
3214 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER
);
3217 if (strncmp(p
, ";<SID=", strlen(";<SID=")) != 0) {
3218 DEBUG(5,("No SID present in extended dn\n"));
3219 return ADS_ERROR_NT(NT_STATUS_NOT_FOUND
);
3222 p
+= strlen(";<SID=");
3226 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER
);
3231 DEBUG(100,("ads_get_sid_from_extended_dn: sid string is %s\n", p
));
3235 case ADS_EXTENDED_DN_STRING
:
3236 if (!string_to_sid(sid
, p
)) {
3237 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER
);
3240 case ADS_EXTENDED_DN_HEX_STRING
: {
3244 buf_len
= strhex_to_str(buf
, sizeof(buf
), p
, strlen(p
));
3246 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER
);
3249 if (!sid_parse(buf
, buf_len
, sid
)) {
3250 DEBUG(10,("failed to parse sid\n"));
3251 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER
);
3256 DEBUG(10,("unknown extended dn format\n"));
3257 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER
);
3260 return ADS_ERROR_NT(NT_STATUS_OK
);
3264 * pull an array of struct dom_sids from a ADS result
3265 * @param ads connection to ads server
3266 * @param mem_ctx TALLOC_CTX for allocating sid array
3267 * @param msg Results of search
3268 * @param field Attribute to retrieve
3269 * @param flags string type of extended_dn
3270 * @param sids pointer to sid array to allocate
3271 * @return the count of SIDs pulled
3273 int ads_pull_sids_from_extendeddn(ADS_STRUCT
*ads
,
3274 TALLOC_CTX
*mem_ctx
,
3277 enum ads_extended_dn_flags flags
,
3278 struct dom_sid
**sids
)
3282 size_t dn_count
, ret_count
= 0;
3285 if ((dn_strings
= ads_pull_strings(ads
, mem_ctx
, msg
, field
,
3286 &dn_count
)) == NULL
) {
3290 (*sids
) = talloc_zero_array(mem_ctx
, struct dom_sid
, dn_count
+ 1);
3292 TALLOC_FREE(dn_strings
);
3296 for (i
=0; i
<dn_count
; i
++) {
3297 rc
= ads_get_sid_from_extended_dn(mem_ctx
, dn_strings
[i
],
3298 flags
, &(*sids
)[i
]);
3299 if (!ADS_ERR_OK(rc
)) {
3300 if (NT_STATUS_EQUAL(ads_ntstatus(rc
),
3301 NT_STATUS_NOT_FOUND
)) {
3306 TALLOC_FREE(dn_strings
);
3313 TALLOC_FREE(dn_strings
);
3318 /********************************************************************
3319 ********************************************************************/
3321 char* ads_get_dnshostname( ADS_STRUCT
*ads
, TALLOC_CTX
*ctx
, const char *machine_name
)
3323 LDAPMessage
*res
= NULL
;
3328 status
= ads_find_machine_acct(ads
, &res
, lp_netbios_name());
3329 if (!ADS_ERR_OK(status
)) {
3330 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
3331 lp_netbios_name()));
3335 if ( (count
= ads_count_replies(ads
, res
)) != 1 ) {
3336 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count
));
3340 if ( (name
= ads_pull_string(ads
, ctx
, res
, "dNSHostName")) == NULL
) {
3341 DEBUG(0,("ads_get_dnshostname: No dNSHostName attribute!\n"));
3345 ads_msgfree(ads
, res
);
3350 /********************************************************************
3351 ********************************************************************/
3353 char* ads_get_upn( ADS_STRUCT
*ads
, TALLOC_CTX
*ctx
, const char *machine_name
)
3355 LDAPMessage
*res
= NULL
;
3360 status
= ads_find_machine_acct(ads
, &res
, machine_name
);
3361 if (!ADS_ERR_OK(status
)) {
3362 DEBUG(0,("ads_get_upn: Failed to find account for %s\n",
3363 lp_netbios_name()));
3367 if ( (count
= ads_count_replies(ads
, res
)) != 1 ) {
3368 DEBUG(1,("ads_get_upn: %d entries returned!\n", count
));
3372 if ( (name
= ads_pull_string(ads
, ctx
, res
, "userPrincipalName")) == NULL
) {
3373 DEBUG(2,("ads_get_upn: No userPrincipalName attribute!\n"));
3377 ads_msgfree(ads
, res
);
3382 /********************************************************************
3383 ********************************************************************/
3385 char* ads_get_samaccountname( ADS_STRUCT
*ads
, TALLOC_CTX
*ctx
, const char *machine_name
)
3387 LDAPMessage
*res
= NULL
;
3392 status
= ads_find_machine_acct(ads
, &res
, lp_netbios_name());
3393 if (!ADS_ERR_OK(status
)) {
3394 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
3395 lp_netbios_name()));
3399 if ( (count
= ads_count_replies(ads
, res
)) != 1 ) {
3400 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count
));
3404 if ( (name
= ads_pull_string(ads
, ctx
, res
, "sAMAccountName")) == NULL
) {
3405 DEBUG(0,("ads_get_dnshostname: No sAMAccountName attribute!\n"));
3409 ads_msgfree(ads
, res
);
3416 SAVED CODE
- we used to join via ldap
- remember how we did
this. JRA
.
3419 * Join a machine to a realm
3420 * Creates the machine account and sets the machine password
3421 * @param ads connection to ads server
3422 * @param machine name of host to add
3423 * @param org_unit Organizational unit to place machine in
3424 * @return status of join
3426 ADS_STATUS
ads_join_realm(ADS_STRUCT
*ads
, const char *machine_name
,
3427 uint32 account_type
, const char *org_unit
)
3430 LDAPMessage
*res
= NULL
;
3433 /* machine name must be lowercase */
3434 machine
= SMB_STRDUP(machine_name
);
3435 strlower_m(machine
);
3438 status = ads_find_machine_acct(ads, (void **)&res, machine);
3439 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
3440 DEBUG(0, ("Host account for %s already exists - deleting old account\n", machine));
3441 status = ads_leave_realm(ads, machine);
3442 if (!ADS_ERR_OK(status)) {
3443 DEBUG(0, ("Failed to delete host '%s' from the '%s' realm.\n",
3444 machine, ads->config.realm));
3449 status
= ads_add_machine_acct(ads
, machine
, account_type
, org_unit
);
3450 if (!ADS_ERR_OK(status
)) {
3451 DEBUG(0, ("ads_join_realm: ads_add_machine_acct failed (%s): %s\n", machine
, ads_errstr(status
)));
3456 status
= ads_find_machine_acct(ads
, (void **)(void *)&res
, machine
);
3457 if (!ADS_ERR_OK(status
)) {
3458 DEBUG(0, ("ads_join_realm: Host account test failed for machine %s\n", machine
));
3464 ads_msgfree(ads
, res
);
3471 * Delete a machine from the realm
3472 * @param ads connection to ads server
3473 * @param hostname Machine to remove
3474 * @return status of delete
3476 ADS_STATUS
ads_leave_realm(ADS_STRUCT
*ads
, const char *hostname
)
3481 char *hostnameDN
, *host
;
3483 LDAPControl ldap_control
;
3484 LDAPControl
* pldap_control
[2] = {NULL
, NULL
};
3486 pldap_control
[0] = &ldap_control
;
3487 memset(&ldap_control
, 0, sizeof(LDAPControl
));
3488 ldap_control
.ldctl_oid
= discard_const_p(char, LDAP_SERVER_TREE_DELETE_OID
);
3490 /* hostname must be lowercase */
3491 host
= SMB_STRDUP(hostname
);
3494 status
= ads_find_machine_acct(ads
, &res
, host
);
3495 if (!ADS_ERR_OK(status
)) {
3496 DEBUG(0, ("Host account for %s does not exist.\n", host
));
3501 msg
= ads_first_entry(ads
, res
);
3504 return ADS_ERROR_SYSTEM(ENOENT
);
3507 hostnameDN
= ads_get_dn(ads
, talloc_tos(), (LDAPMessage
*)msg
);
3508 if (hostnameDN
== NULL
) {
3510 return ADS_ERROR_SYSTEM(ENOENT
);
3513 rc
= ldap_delete_ext_s(ads
->ldap
.ld
, hostnameDN
, pldap_control
, NULL
);
3515 DEBUG(3,("ldap_delete_ext_s failed with error code %d\n", rc
));
3517 DEBUG(3,("ldap_delete_ext_s succeeded with error code %d\n", rc
));
3520 if (rc
!= LDAP_SUCCESS
) {
3521 const char *attrs
[] = { "cn", NULL
};
3522 LDAPMessage
*msg_sub
;
3524 /* we only search with scope ONE, we do not expect any further
3525 * objects to be created deeper */
3527 status
= ads_do_search_retry(ads
, hostnameDN
,
3528 LDAP_SCOPE_ONELEVEL
,
3529 "(objectclass=*)", attrs
, &res
);
3531 if (!ADS_ERR_OK(status
)) {
3533 TALLOC_FREE(hostnameDN
);
3537 for (msg_sub
= ads_first_entry(ads
, res
); msg_sub
;
3538 msg_sub
= ads_next_entry(ads
, msg_sub
)) {
3542 if ((dn
= ads_get_dn(ads
, talloc_tos(), msg_sub
)) == NULL
) {
3544 TALLOC_FREE(hostnameDN
);
3545 return ADS_ERROR(LDAP_NO_MEMORY
);
3548 status
= ads_del_dn(ads
, dn
);
3549 if (!ADS_ERR_OK(status
)) {
3550 DEBUG(3,("failed to delete dn %s: %s\n", dn
, ads_errstr(status
)));
3553 TALLOC_FREE(hostnameDN
);
3560 /* there should be no subordinate objects anymore */
3561 status
= ads_do_search_retry(ads
, hostnameDN
,
3562 LDAP_SCOPE_ONELEVEL
,
3563 "(objectclass=*)", attrs
, &res
);
3565 if (!ADS_ERR_OK(status
) || ( (ads_count_replies(ads
, res
)) > 0 ) ) {
3567 TALLOC_FREE(hostnameDN
);
3571 /* delete hostnameDN now */
3572 status
= ads_del_dn(ads
, hostnameDN
);
3573 if (!ADS_ERR_OK(status
)) {
3575 DEBUG(3,("failed to delete dn %s: %s\n", hostnameDN
, ads_errstr(status
)));
3576 TALLOC_FREE(hostnameDN
);
3581 TALLOC_FREE(hostnameDN
);
3583 status
= ads_find_machine_acct(ads
, &res
, host
);
3584 if (ADS_ERR_OK(status
) && ads_count_replies(ads
, res
) == 1) {
3585 DEBUG(3, ("Failed to remove host account.\n"));
3595 * pull all token-sids from an LDAP dn
3596 * @param ads connection to ads server
3597 * @param mem_ctx TALLOC_CTX for allocating sid array
3598 * @param dn of LDAP object
3599 * @param user_sid pointer to struct dom_sid (objectSid)
3600 * @param primary_group_sid pointer to struct dom_sid (self composed)
3601 * @param sids pointer to sid array to allocate
3602 * @param num_sids counter of SIDs pulled
3603 * @return status of token query
3605 ADS_STATUS
ads_get_tokensids(ADS_STRUCT
*ads
,
3606 TALLOC_CTX
*mem_ctx
,
3608 struct dom_sid
*user_sid
,
3609 struct dom_sid
*primary_group_sid
,
3610 struct dom_sid
**sids
,
3614 LDAPMessage
*res
= NULL
;
3616 size_t tmp_num_sids
;
3617 struct dom_sid
*tmp_sids
;
3618 struct dom_sid tmp_user_sid
;
3619 struct dom_sid tmp_primary_group_sid
;
3621 const char *attrs
[] = {
3628 status
= ads_search_retry_dn(ads
, &res
, dn
, attrs
);
3629 if (!ADS_ERR_OK(status
)) {
3633 count
= ads_count_replies(ads
, res
);
3635 ads_msgfree(ads
, res
);
3636 return ADS_ERROR_LDAP(LDAP_NO_SUCH_OBJECT
);
3639 if (!ads_pull_sid(ads
, res
, "objectSid", &tmp_user_sid
)) {
3640 ads_msgfree(ads
, res
);
3641 return ADS_ERROR_LDAP(LDAP_NO_MEMORY
);
3644 if (!ads_pull_uint32(ads
, res
, "primaryGroupID", &pgid
)) {
3645 ads_msgfree(ads
, res
);
3646 return ADS_ERROR_LDAP(LDAP_NO_MEMORY
);
3650 /* hack to compose the primary group sid without knowing the
3653 struct dom_sid domsid
;
3655 sid_copy(&domsid
, &tmp_user_sid
);
3657 if (!sid_split_rid(&domsid
, NULL
)) {
3658 ads_msgfree(ads
, res
);
3659 return ADS_ERROR_LDAP(LDAP_NO_MEMORY
);
3662 if (!sid_compose(&tmp_primary_group_sid
, &domsid
, pgid
)) {
3663 ads_msgfree(ads
, res
);
3664 return ADS_ERROR_LDAP(LDAP_NO_MEMORY
);
3668 tmp_num_sids
= ads_pull_sids(ads
, mem_ctx
, res
, "tokenGroups", &tmp_sids
);
3670 if (tmp_num_sids
== 0 || !tmp_sids
) {
3671 ads_msgfree(ads
, res
);
3672 return ADS_ERROR_LDAP(LDAP_NO_MEMORY
);
3676 *num_sids
= tmp_num_sids
;
3684 *user_sid
= tmp_user_sid
;
3687 if (primary_group_sid
) {
3688 *primary_group_sid
= tmp_primary_group_sid
;
3691 DEBUG(10,("ads_get_tokensids: returned %d sids\n", (int)tmp_num_sids
+ 2));
3693 ads_msgfree(ads
, res
);
3694 return ADS_ERROR_LDAP(LDAP_SUCCESS
);
3698 * Find a sAMAccoutName in LDAP
3699 * @param ads connection to ads server
3700 * @param mem_ctx TALLOC_CTX for allocating sid array
3701 * @param samaccountname to search
3702 * @param uac_ret uint32 pointer userAccountControl attribute value
3703 * @param dn_ret pointer to dn
3704 * @return status of token query
3706 ADS_STATUS
ads_find_samaccount(ADS_STRUCT
*ads
,
3707 TALLOC_CTX
*mem_ctx
,
3708 const char *samaccountname
,
3710 const char **dn_ret
)
3713 const char *attrs
[] = { "userAccountControl", NULL
};
3715 LDAPMessage
*res
= NULL
;
3719 filter
= talloc_asprintf(mem_ctx
, "(&(objectclass=user)(sAMAccountName=%s))",
3721 if (filter
== NULL
) {
3722 status
= ADS_ERROR_NT(NT_STATUS_NO_MEMORY
);
3726 status
= ads_do_search_all(ads
, ads
->config
.bind_path
,
3728 filter
, attrs
, &res
);
3730 if (!ADS_ERR_OK(status
)) {
3734 if (ads_count_replies(ads
, res
) != 1) {
3735 status
= ADS_ERROR(LDAP_NO_RESULTS_RETURNED
);
3739 dn
= ads_get_dn(ads
, talloc_tos(), res
);
3741 status
= ADS_ERROR(LDAP_NO_MEMORY
);
3745 if (!ads_pull_uint32(ads
, res
, "userAccountControl", &uac
)) {
3746 status
= ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE
);
3755 *dn_ret
= talloc_strdup(mem_ctx
, dn
);
3757 status
= ADS_ERROR(LDAP_NO_MEMORY
);
3763 ads_msgfree(ads
, res
);
3769 * find our configuration path
3770 * @param ads connection to ads server
3771 * @param mem_ctx Pointer to talloc context
3772 * @param config_path Pointer to the config path
3773 * @return status of search
3775 ADS_STATUS
ads_config_path(ADS_STRUCT
*ads
,
3776 TALLOC_CTX
*mem_ctx
,
3780 LDAPMessage
*res
= NULL
;
3781 const char *config_context
= NULL
;
3782 const char *attrs
[] = { "configurationNamingContext", NULL
};
3784 status
= ads_do_search(ads
, "", LDAP_SCOPE_BASE
,
3785 "(objectclass=*)", attrs
, &res
);
3786 if (!ADS_ERR_OK(status
)) {
3790 config_context
= ads_pull_string(ads
, mem_ctx
, res
,
3791 "configurationNamingContext");
3792 ads_msgfree(ads
, res
);
3793 if (!config_context
) {
3794 return ADS_ERROR(LDAP_NO_MEMORY
);
3798 *config_path
= talloc_strdup(mem_ctx
, config_context
);
3799 if (!*config_path
) {
3800 return ADS_ERROR(LDAP_NO_MEMORY
);
3804 return ADS_ERROR(LDAP_SUCCESS
);
3808 * find the displayName of an extended right
3809 * @param ads connection to ads server
3810 * @param config_path The config path
3811 * @param mem_ctx Pointer to talloc context
3812 * @param GUID struct of the rightsGUID
3813 * @return status of search
3815 const char *ads_get_extended_right_name_by_guid(ADS_STRUCT
*ads
,
3816 const char *config_path
,
3817 TALLOC_CTX
*mem_ctx
,
3818 const struct GUID
*rights_guid
)
3821 LDAPMessage
*res
= NULL
;
3823 const char *attrs
[] = { "displayName", NULL
};
3824 const char *result
= NULL
;
3827 if (!ads
|| !mem_ctx
|| !rights_guid
) {
3831 expr
= talloc_asprintf(mem_ctx
, "(rightsGuid=%s)",
3832 GUID_string(mem_ctx
, rights_guid
));
3837 path
= talloc_asprintf(mem_ctx
, "cn=Extended-Rights,%s", config_path
);
3842 rc
= ads_do_search_retry(ads
, path
, LDAP_SCOPE_SUBTREE
,
3844 if (!ADS_ERR_OK(rc
)) {
3848 if (ads_count_replies(ads
, res
) != 1) {
3852 result
= ads_pull_string(ads
, mem_ctx
, res
, "displayName");
3855 ads_msgfree(ads
, res
);
3860 * verify or build and verify an account ou
3861 * @param mem_ctx Pointer to talloc context
3862 * @param ads connection to ads server
3864 * @return status of search
3867 ADS_STATUS
ads_check_ou_dn(TALLOC_CTX
*mem_ctx
,
3869 const char **account_ou
)
3875 exploded_dn
= ldap_explode_dn(*account_ou
, 0);
3877 ldap_value_free(exploded_dn
);
3881 ou_string
= ads_ou_string(ads
, *account_ou
);
3883 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX
);
3886 name
= talloc_asprintf(mem_ctx
, "%s,%s", ou_string
,
3887 ads
->config
.bind_path
);
3888 SAFE_FREE(ou_string
);
3891 return ADS_ERROR_LDAP(LDAP_NO_MEMORY
);
3894 exploded_dn
= ldap_explode_dn(name
, 0);
3896 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX
);
3898 ldap_value_free(exploded_dn
);