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 "../lib/addns/dnsquery.h"
29 #include "../libds/common/flags.h"
31 #include "../libcli/security/security.h"
32 #include "../librpc/gen_ndr/netlogon.h"
33 #include "lib/param/loadparm.h"
39 * @brief basic ldap client-side routines for ads server communications
41 * The routines contained here should do the necessary ldap calls for
44 * Important note: attribute names passed into ads_ routines must
45 * already be in UTF-8 format. We do not convert them because in almost
46 * all cases, they are just ascii (which is represented with the same
47 * codepoints in UTF-8). This may have to change at some point
51 #define LDAP_SERVER_TREE_DELETE_OID "1.2.840.113556.1.4.805"
53 static SIG_ATOMIC_T gotalarm
;
55 /***************************************************************
56 Signal function to tell us we timed out.
57 ****************************************************************/
59 static void gotalarm_sig(int signum
)
64 LDAP
*ldap_open_with_timeout(const char *server
,
65 struct sockaddr_storage
*ss
,
66 int port
, unsigned int to
)
72 DEBUG(10, ("Opening connection to LDAP server '%s:%d', timeout "
73 "%u seconds\n", server
, port
, to
));
78 CatchSignal(SIGALRM
, gotalarm_sig
);
80 /* End setup timeout. */
83 if ( strchr_m(server
, ':') ) {
85 uri
= talloc_asprintf(talloc_tos(), "ldap://[%s]:%u", server
, port
);
88 uri
= talloc_asprintf(talloc_tos(), "ldap://%s:%u", server
, port
);
94 #ifdef HAVE_LDAP_INITIALIZE
95 ldap_err
= ldap_initialize(&ldp
, uri
);
97 ldp
= ldap_open(server
, port
);
99 ldap_err
= LDAP_SUCCESS
;
101 ldap_err
= LDAP_OTHER
;
104 if (ldap_err
!= LDAP_SUCCESS
) {
105 DEBUG(2,("Could not initialize connection for LDAP server '%s': %s\n",
106 uri
, ldap_err2string(ldap_err
)));
108 DEBUG(10, ("Initialized connection for LDAP server '%s'\n", uri
));
112 /* Teardown timeout. */
114 CatchSignal(SIGALRM
, SIG_IGN
);
120 static int ldap_search_with_timeout(LDAP
*ld
,
121 LDAP_CONST
char *base
,
123 LDAP_CONST
char *filter
,
126 LDAPControl
**sctrls
,
127 LDAPControl
**cctrls
,
131 int to
= lp_ldap_timeout();
132 struct timeval timeout
;
133 struct timeval
*timeout_ptr
= NULL
;
136 /* Setup timeout for the ldap_search_ext_s call - local and remote. */
142 timeout_ptr
= &timeout
;
144 /* Setup alarm timeout. */
145 CatchSignal(SIGALRM
, gotalarm_sig
);
146 /* Make the alarm time one second beyond
147 the timout we're setting for the
148 remote search timeout, to allow that
149 to fire in preference. */
151 /* End setup timeout. */
155 result
= ldap_search_ext_s(ld
, base
, scope
, filter
, attrs
,
156 attrsonly
, sctrls
, cctrls
, timeout_ptr
,
160 /* Teardown alarm timeout. */
161 CatchSignal(SIGALRM
, SIG_IGN
);
166 return LDAP_TIMELIMIT_EXCEEDED
;
169 * A bug in OpenLDAP means ldap_search_ext_s can return
170 * LDAP_SUCCESS but with a NULL res pointer. Cope with
171 * this. See bug #6279 for details. JRA.
175 return LDAP_TIMELIMIT_EXCEEDED
;
181 /**********************************************
182 Do client and server sitename match ?
183 **********************************************/
185 bool ads_sitename_match(ADS_STRUCT
*ads
)
187 if (ads
->config
.server_site_name
== NULL
&&
188 ads
->config
.client_site_name
== NULL
) {
189 DEBUG(10,("ads_sitename_match: both null\n"));
192 if (ads
->config
.server_site_name
&&
193 ads
->config
.client_site_name
&&
194 strequal(ads
->config
.server_site_name
,
195 ads
->config
.client_site_name
)) {
196 DEBUG(10,("ads_sitename_match: name %s match\n", ads
->config
.server_site_name
));
199 DEBUG(10,("ads_sitename_match: no match between server: %s and client: %s\n",
200 ads
->config
.server_site_name
? ads
->config
.server_site_name
: "NULL",
201 ads
->config
.client_site_name
? ads
->config
.client_site_name
: "NULL"));
205 /**********************************************
206 Is this the closest DC ?
207 **********************************************/
209 bool ads_closest_dc(ADS_STRUCT
*ads
)
211 if (ads
->config
.flags
& NBT_SERVER_CLOSEST
) {
212 DEBUG(10,("ads_closest_dc: NBT_SERVER_CLOSEST flag set\n"));
216 /* not sure if this can ever happen */
217 if (ads_sitename_match(ads
)) {
218 DEBUG(10,("ads_closest_dc: NBT_SERVER_CLOSEST flag not set but sites match\n"));
222 if (ads
->config
.client_site_name
== NULL
) {
223 DEBUG(10,("ads_closest_dc: client belongs to no site\n"));
227 DEBUG(10,("ads_closest_dc: %s is not the closest DC\n",
228 ads
->config
.ldap_server_name
));
235 try a connection to a given ldap server, returning True and setting the servers IP
236 in the ads struct if successful
238 static bool ads_try_connect(ADS_STRUCT
*ads
, bool gc
,
239 struct sockaddr_storage
*ss
)
241 struct NETLOGON_SAM_LOGON_RESPONSE_EX cldap_reply
;
242 TALLOC_CTX
*frame
= talloc_stackframe();
244 char addr
[INET6_ADDRSTRLEN
];
251 print_sockaddr(addr
, sizeof(addr
), ss
);
253 DEBUG(5,("ads_try_connect: sending CLDAP request to %s (realm: %s)\n",
254 addr
, ads
->server
.realm
));
256 ZERO_STRUCT( cldap_reply
);
258 if ( !ads_cldap_netlogon_5(frame
, ss
, ads
->server
.realm
, &cldap_reply
) ) {
259 DEBUG(3,("ads_try_connect: CLDAP request %s failed.\n", addr
));
264 /* Check the CLDAP reply flags */
266 if ( !(cldap_reply
.server_type
& NBT_SERVER_LDAP
) ) {
267 DEBUG(1,("ads_try_connect: %s's CLDAP reply says it is not an LDAP server!\n",
273 /* Fill in the ads->config values */
275 SAFE_FREE(ads
->config
.realm
);
276 SAFE_FREE(ads
->config
.bind_path
);
277 SAFE_FREE(ads
->config
.ldap_server_name
);
278 SAFE_FREE(ads
->config
.server_site_name
);
279 SAFE_FREE(ads
->config
.client_site_name
);
280 SAFE_FREE(ads
->server
.workgroup
);
282 if (!check_cldap_reply_required_flags(cldap_reply
.server_type
,
283 ads
->config
.flags
)) {
288 ads
->config
.ldap_server_name
= SMB_STRDUP(cldap_reply
.pdc_dns_name
);
289 ads
->config
.realm
= SMB_STRDUP(cldap_reply
.dns_domain
);
290 if (!strupper_m(ads
->config
.realm
)) {
295 ads
->config
.bind_path
= ads_build_dn(ads
->config
.realm
);
296 if (*cldap_reply
.server_site
) {
297 ads
->config
.server_site_name
=
298 SMB_STRDUP(cldap_reply
.server_site
);
300 if (*cldap_reply
.client_site
) {
301 ads
->config
.client_site_name
=
302 SMB_STRDUP(cldap_reply
.client_site
);
304 ads
->server
.workgroup
= SMB_STRDUP(cldap_reply
.domain_name
);
306 ads
->ldap
.port
= gc
? LDAP_GC_PORT
: LDAP_PORT
;
309 /* Store our site name. */
310 sitename_store( cldap_reply
.domain_name
, cldap_reply
.client_site
);
311 sitename_store( cldap_reply
.dns_domain
, cldap_reply
.client_site
);
313 /* Leave this until last so that the flags are not clobbered */
314 ads
->config
.flags
= cldap_reply
.server_type
;
324 /**********************************************************************
325 send a cldap ping to list of servers, one at a time, until one of
326 them answers it's an ldap server. Record success in the ADS_STRUCT.
327 Take note of and update negative connection cache.
328 **********************************************************************/
330 static NTSTATUS
cldap_ping_list(ADS_STRUCT
*ads
,const char *domain
,
331 struct ip_service
*ip_list
, int count
)
336 for (i
= 0; i
< count
; i
++) {
337 char server
[INET6_ADDRSTRLEN
];
339 print_sockaddr(server
, sizeof(server
), &ip_list
[i
].ss
);
341 if (!NT_STATUS_IS_OK(
342 check_negative_conn_cache(domain
, server
)))
345 /* Returns ok only if it matches the correct server type */
346 ok
= ads_try_connect(ads
, false, &ip_list
[i
].ss
);
352 /* keep track of failures */
353 add_failed_connection_entry(domain
, server
,
354 NT_STATUS_UNSUCCESSFUL
);
357 return NT_STATUS_NO_LOGON_SERVERS
;
360 /***************************************************************************
361 resolve a name and perform an "ldap ping" using NetBIOS and related methods
362 ****************************************************************************/
364 static NTSTATUS
resolve_and_ping_netbios(ADS_STRUCT
*ads
,
365 const char *domain
, const char *realm
)
368 struct ip_service
*ip_list
;
369 NTSTATUS status
= NT_STATUS_UNSUCCESSFUL
;
371 DEBUG(6, ("resolve_and_ping_netbios: (cldap) looking for domain '%s'\n",
374 status
= get_sorted_dc_list(domain
, NULL
, &ip_list
, &count
,
376 if (!NT_STATUS_IS_OK(status
)) {
380 /* remove servers which are known to be dead based on
381 the corresponding DNS method */
383 for (i
= 0; i
< count
; ++i
) {
384 char server
[INET6_ADDRSTRLEN
];
386 print_sockaddr(server
, sizeof(server
), &ip_list
[i
].ss
);
389 check_negative_conn_cache(realm
, server
))) {
390 /* Ensure we add the workgroup name for this
391 IP address as negative too. */
392 add_failed_connection_entry(
394 NT_STATUS_UNSUCCESSFUL
);
399 status
= cldap_ping_list(ads
, domain
, ip_list
, count
);
407 /**********************************************************************
408 resolve a name and perform an "ldap ping" using DNS
409 **********************************************************************/
411 static NTSTATUS
resolve_and_ping_dns(ADS_STRUCT
*ads
, const char *sitename
,
415 struct ip_service
*ip_list
;
416 NTSTATUS status
= NT_STATUS_UNSUCCESSFUL
;
418 DEBUG(6, ("resolve_and_ping_dns: (cldap) looking for realm '%s'\n",
421 status
= get_sorted_dc_list(realm
, sitename
, &ip_list
, &count
,
423 if (!NT_STATUS_IS_OK(status
)) {
427 status
= cldap_ping_list(ads
, realm
, ip_list
, count
);
434 /**********************************************************************
435 Try to find an AD dc using our internal name resolution routines
436 Try the realm first and then then workgroup name if netbios is not
438 **********************************************************************/
440 static NTSTATUS
ads_find_dc(ADS_STRUCT
*ads
)
442 const char *c_domain
= "";
444 bool use_own_domain
= False
;
445 char *sitename
= NULL
;
446 NTSTATUS status
= NT_STATUS_UNSUCCESSFUL
;
449 /* if the realm and workgroup are both empty, assume they are ours */
452 c_realm
= ads
->server
.realm
;
458 /* special case where no realm and no workgroup means our own */
459 if ( !ads
->server
.workgroup
|| !*ads
->server
.workgroup
) {
460 use_own_domain
= True
;
461 c_realm
= lp_realm();
465 if (!lp_disable_netbios()) {
466 if (use_own_domain
) {
467 c_domain
= lp_workgroup();
469 c_domain
= ads
->server
.workgroup
;
470 if (!*c_realm
&& (!c_domain
|| !*c_domain
)) {
471 c_domain
= lp_workgroup();
480 if (!*c_realm
&& !*c_domain
) {
481 DEBUG(0, ("ads_find_dc: no realm or workgroup! Don't know "
483 return NT_STATUS_INVALID_PARAMETER
; /* rather need MISSING_PARAMETER ... */
487 * In case of LDAP we use get_dc_name() as that
488 * creates the custom krb5.conf file
490 if (!(ads
->auth
.flags
& ADS_AUTH_NO_BIND
)) {
492 struct sockaddr_storage ip_out
;
494 DEBUG(6, ("ads_find_dc: (ldap) looking for realm '%s'"
495 " and falling back to domain '%s'\n",
498 ok
= get_dc_name(c_domain
, c_realm
, srv_name
, &ip_out
);
501 * we call ads_try_connect() to fill in the
502 * ads->config details
504 ok
= ads_try_connect(ads
, false, &ip_out
);
510 return NT_STATUS_NO_LOGON_SERVERS
;
514 sitename
= sitename_fetch(talloc_tos(), c_realm
);
515 status
= resolve_and_ping_dns(ads
, sitename
, c_realm
);
517 if (NT_STATUS_IS_OK(status
)) {
518 TALLOC_FREE(sitename
);
522 /* In case we failed to contact one of our closest DC on our
524 * need to try to find another DC, retry with a site-less SRV
529 DEBUG(3, ("ads_find_dc: failed to find a valid DC on "
530 "our site (%s), Trying to find another DC "
531 "for realm '%s' (domain '%s')\n",
532 sitename
, c_realm
, c_domain
));
533 namecache_delete(c_realm
, 0x1C);
535 resolve_and_ping_dns(ads
, NULL
, c_realm
);
537 if (NT_STATUS_IS_OK(status
)) {
538 TALLOC_FREE(sitename
);
543 TALLOC_FREE(sitename
);
546 /* try netbios as fallback - if permitted,
547 or if configuration specifically requests it */
550 DEBUG(3, ("ads_find_dc: falling back to netbios "
551 "name resolution for domain '%s' (realm '%s')\n",
555 status
= resolve_and_ping_netbios(ads
, c_domain
, c_realm
);
556 if (NT_STATUS_IS_OK(status
)) {
561 DEBUG(1, ("ads_find_dc: "
562 "name resolution for realm '%s' (domain '%s') failed: %s\n",
563 c_realm
, c_domain
, nt_errstr(status
)));
567 * Connect to the LDAP server
568 * @param ads Pointer to an existing ADS_STRUCT
569 * @return status of connection
571 ADS_STATUS
ads_connect(ADS_STRUCT
*ads
)
573 int version
= LDAP_VERSION3
;
576 char addr
[INET6_ADDRSTRLEN
];
578 ZERO_STRUCT(ads
->ldap
);
579 ZERO_STRUCT(ads
->ldap_wrap_data
);
580 ads
->ldap
.last_attempt
= time_mono(NULL
);
581 ads
->ldap_wrap_data
.wrap_type
= ADS_SASLWRAP_TYPE_PLAIN
;
583 /* try with a user specified server */
585 if (DEBUGLEVEL
>= 11) {
586 char *s
= NDR_PRINT_STRUCT_STRING(talloc_tos(), ads_struct
, ads
);
587 DEBUG(11,("ads_connect: entering\n"));
588 DEBUGADD(11,("%s\n", s
));
592 if (ads
->server
.ldap_server
) {
594 struct sockaddr_storage ss
;
596 ok
= resolve_name(ads
->server
.ldap_server
, &ss
, 0x20, true);
598 DEBUG(5,("ads_connect: unable to resolve name %s\n",
599 ads
->server
.ldap_server
));
600 status
= ADS_ERROR_NT(NT_STATUS_NOT_FOUND
);
603 ok
= ads_try_connect(ads
, ads
->server
.gc
, &ss
);
608 /* The choice of which GC use is handled one level up in
609 ads_connect_gc(). If we continue on from here with
610 ads_find_dc() we will get GC searches on port 389 which
611 doesn't work. --jerry */
613 if (ads
->server
.gc
== true) {
614 return ADS_ERROR(LDAP_OPERATIONS_ERROR
);
617 if (ads
->server
.no_fallback
) {
618 status
= ADS_ERROR_NT(NT_STATUS_NOT_FOUND
);
623 ntstatus
= ads_find_dc(ads
);
624 if (NT_STATUS_IS_OK(ntstatus
)) {
628 status
= ADS_ERROR_NT(ntstatus
);
633 print_sockaddr(addr
, sizeof(addr
), &ads
->ldap
.ss
);
634 DEBUG(3,("Successfully contacted LDAP server %s\n", addr
));
636 if (!ads
->auth
.user_name
) {
637 /* Must use the userPrincipalName value here or sAMAccountName
638 and not servicePrincipalName; found by Guenther Deschner */
640 if (asprintf(&ads
->auth
.user_name
, "%s$", lp_netbios_name() ) == -1) {
641 DEBUG(0,("ads_connect: asprintf fail.\n"));
642 ads
->auth
.user_name
= NULL
;
646 if (!ads
->auth
.realm
) {
647 ads
->auth
.realm
= SMB_STRDUP(ads
->config
.realm
);
650 if (!ads
->auth
.kdc_server
) {
651 print_sockaddr(addr
, sizeof(addr
), &ads
->ldap
.ss
);
652 ads
->auth
.kdc_server
= SMB_STRDUP(addr
);
655 /* If the caller() requested no LDAP bind, then we are done */
657 if (ads
->auth
.flags
& ADS_AUTH_NO_BIND
) {
658 status
= ADS_SUCCESS
;
662 ads
->ldap_wrap_data
.mem_ctx
= talloc_init("ads LDAP connection memory");
663 if (!ads
->ldap_wrap_data
.mem_ctx
) {
664 status
= ADS_ERROR_NT(NT_STATUS_NO_MEMORY
);
668 /* Otherwise setup the TCP LDAP session */
670 ads
->ldap
.ld
= ldap_open_with_timeout(addr
,
672 ads
->ldap
.port
, lp_ldap_timeout());
673 if (ads
->ldap
.ld
== NULL
) {
674 status
= ADS_ERROR(LDAP_OPERATIONS_ERROR
);
677 DEBUG(3,("Connected to LDAP server %s\n", ads
->config
.ldap_server_name
));
679 /* cache the successful connection for workgroup and realm */
680 if (ads_closest_dc(ads
)) {
681 saf_store( ads
->server
.workgroup
, ads
->config
.ldap_server_name
);
682 saf_store( ads
->server
.realm
, ads
->config
.ldap_server_name
);
685 ldap_set_option(ads
->ldap
.ld
, LDAP_OPT_PROTOCOL_VERSION
, &version
);
687 if ( lp_ldap_ssl_ads() ) {
688 status
= ADS_ERROR(smbldap_start_tls(ads
->ldap
.ld
, version
));
689 if (!ADS_ERR_OK(status
)) {
694 /* fill in the current time and offsets */
696 status
= ads_current_time( ads
);
697 if ( !ADS_ERR_OK(status
) ) {
701 /* Now do the bind */
703 if (ads
->auth
.flags
& ADS_AUTH_ANON_BIND
) {
704 status
= ADS_ERROR(ldap_simple_bind_s(ads
->ldap
.ld
, NULL
, NULL
));
708 if (ads
->auth
.flags
& ADS_AUTH_SIMPLE_BIND
) {
709 status
= ADS_ERROR(ldap_simple_bind_s(ads
->ldap
.ld
, ads
->auth
.user_name
, ads
->auth
.password
));
713 status
= ads_sasl_bind(ads
);
716 if (DEBUGLEVEL
>= 11) {
717 char *s
= NDR_PRINT_STRUCT_STRING(talloc_tos(), ads_struct
, ads
);
718 DEBUG(11,("ads_connect: leaving with: %s\n",
719 ads_errstr(status
)));
720 DEBUGADD(11,("%s\n", s
));
728 * Connect to the LDAP server using given credentials
729 * @param ads Pointer to an existing ADS_STRUCT
730 * @return status of connection
732 ADS_STATUS
ads_connect_user_creds(ADS_STRUCT
*ads
)
734 ads
->auth
.flags
|= ADS_AUTH_USER_CREDS
;
736 return ads_connect(ads
);
740 * Disconnect the LDAP server
741 * @param ads Pointer to an existing ADS_STRUCT
743 void ads_disconnect(ADS_STRUCT
*ads
)
746 ldap_unbind(ads
->ldap
.ld
);
749 if (ads
->ldap_wrap_data
.wrap_ops
&&
750 ads
->ldap_wrap_data
.wrap_ops
->disconnect
) {
751 ads
->ldap_wrap_data
.wrap_ops
->disconnect(&ads
->ldap_wrap_data
);
753 if (ads
->ldap_wrap_data
.mem_ctx
) {
754 talloc_free(ads
->ldap_wrap_data
.mem_ctx
);
756 ZERO_STRUCT(ads
->ldap
);
757 ZERO_STRUCT(ads
->ldap_wrap_data
);
761 Duplicate a struct berval into talloc'ed memory
763 static struct berval
*dup_berval(TALLOC_CTX
*ctx
, const struct berval
*in_val
)
765 struct berval
*value
;
767 if (!in_val
) return NULL
;
769 value
= talloc_zero(ctx
, struct berval
);
772 if (in_val
->bv_len
== 0) return value
;
774 value
->bv_len
= in_val
->bv_len
;
775 value
->bv_val
= (char *)talloc_memdup(ctx
, in_val
->bv_val
,
781 Make a values list out of an array of (struct berval *)
783 static struct berval
**ads_dup_values(TALLOC_CTX
*ctx
,
784 const struct berval
**in_vals
)
786 struct berval
**values
;
789 if (!in_vals
) return NULL
;
790 for (i
=0; in_vals
[i
]; i
++)
792 values
= talloc_zero_array(ctx
, struct berval
*, i
+1);
793 if (!values
) return NULL
;
795 for (i
=0; in_vals
[i
]; i
++) {
796 values
[i
] = dup_berval(ctx
, in_vals
[i
]);
802 UTF8-encode a values list out of an array of (char *)
804 static char **ads_push_strvals(TALLOC_CTX
*ctx
, const char **in_vals
)
810 if (!in_vals
) return NULL
;
811 for (i
=0; in_vals
[i
]; i
++)
813 values
= talloc_zero_array(ctx
, char *, i
+1);
814 if (!values
) return NULL
;
816 for (i
=0; in_vals
[i
]; i
++) {
817 if (!push_utf8_talloc(ctx
, &values
[i
], in_vals
[i
], &size
)) {
826 Pull a (char *) array out of a UTF8-encoded values list
828 static char **ads_pull_strvals(TALLOC_CTX
*ctx
, const char **in_vals
)
832 size_t converted_size
;
834 if (!in_vals
) return NULL
;
835 for (i
=0; in_vals
[i
]; i
++)
837 values
= talloc_zero_array(ctx
, char *, i
+1);
838 if (!values
) return NULL
;
840 for (i
=0; in_vals
[i
]; i
++) {
841 if (!pull_utf8_talloc(ctx
, &values
[i
], in_vals
[i
],
843 DEBUG(0,("ads_pull_strvals: pull_utf8_talloc failed: "
844 "%s", strerror(errno
)));
851 * Do a search with paged results. cookie must be null on the first
852 * call, and then returned on each subsequent call. It will be null
853 * again when the entire search is complete
854 * @param ads connection to ads server
855 * @param bind_path Base dn for the search
856 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
857 * @param expr Search expression - specified in local charset
858 * @param attrs Attributes to retrieve - specified in utf8 or ascii
859 * @param res ** which will contain results - free res* with ads_msgfree()
860 * @param count Number of entries retrieved on this page
861 * @param cookie The paged results cookie to be returned on subsequent calls
862 * @return status of search
864 static ADS_STATUS
ads_do_paged_search_args(ADS_STRUCT
*ads
,
865 const char *bind_path
,
866 int scope
, const char *expr
,
867 const char **attrs
, void *args
,
869 int *count
, struct berval
**cookie
)
872 char *utf8_expr
, *utf8_path
, **search_attrs
= NULL
;
873 size_t converted_size
;
874 LDAPControl PagedResults
, NoReferrals
, ExternalCtrl
, *controls
[4], **rcontrols
;
875 BerElement
*cookie_be
= NULL
;
876 struct berval
*cookie_bv
= NULL
;
877 BerElement
*ext_be
= NULL
;
878 struct berval
*ext_bv
= NULL
;
881 ads_control
*external_control
= (ads_control
*) args
;
885 if (!(ctx
= talloc_init("ads_do_paged_search_args")))
886 return ADS_ERROR(LDAP_NO_MEMORY
);
888 /* 0 means the conversion worked but the result was empty
889 so we only fail if it's -1. In any case, it always
890 at least nulls out the dest */
891 if (!push_utf8_talloc(ctx
, &utf8_expr
, expr
, &converted_size
) ||
892 !push_utf8_talloc(ctx
, &utf8_path
, bind_path
, &converted_size
))
898 if (!attrs
|| !(*attrs
))
901 /* This would be the utf8-encoded version...*/
902 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
903 if (!(search_attrs
= str_list_copy(talloc_tos(), attrs
))) {
909 /* Paged results only available on ldap v3 or later */
910 ldap_get_option(ads
->ldap
.ld
, LDAP_OPT_PROTOCOL_VERSION
, &version
);
911 if (version
< LDAP_VERSION3
) {
912 rc
= LDAP_NOT_SUPPORTED
;
916 cookie_be
= ber_alloc_t(LBER_USE_DER
);
918 ber_printf(cookie_be
, "{iO}", (ber_int_t
) ads
->config
.ldap_page_size
, *cookie
);
919 ber_bvfree(*cookie
); /* don't need it from last time */
922 ber_printf(cookie_be
, "{io}", (ber_int_t
) ads
->config
.ldap_page_size
, "", 0);
924 ber_flatten(cookie_be
, &cookie_bv
);
925 PagedResults
.ldctl_oid
= discard_const_p(char, ADS_PAGE_CTL_OID
);
926 PagedResults
.ldctl_iscritical
= (char) 1;
927 PagedResults
.ldctl_value
.bv_len
= cookie_bv
->bv_len
;
928 PagedResults
.ldctl_value
.bv_val
= cookie_bv
->bv_val
;
930 NoReferrals
.ldctl_oid
= discard_const_p(char, ADS_NO_REFERRALS_OID
);
931 NoReferrals
.ldctl_iscritical
= (char) 0;
932 NoReferrals
.ldctl_value
.bv_len
= 0;
933 NoReferrals
.ldctl_value
.bv_val
= discard_const_p(char, "");
935 if (external_control
&&
936 (strequal(external_control
->control
, ADS_EXTENDED_DN_OID
) ||
937 strequal(external_control
->control
, ADS_SD_FLAGS_OID
))) {
939 ExternalCtrl
.ldctl_oid
= discard_const_p(char, external_control
->control
);
940 ExternalCtrl
.ldctl_iscritical
= (char) external_control
->critical
;
942 /* win2k does not accept a ldctl_value beeing passed in */
944 if (external_control
->val
!= 0) {
946 if ((ext_be
= ber_alloc_t(LBER_USE_DER
)) == NULL
) {
951 if ((ber_printf(ext_be
, "{i}", (ber_int_t
) external_control
->val
)) == -1) {
955 if ((ber_flatten(ext_be
, &ext_bv
)) == -1) {
960 ExternalCtrl
.ldctl_value
.bv_len
= ext_bv
->bv_len
;
961 ExternalCtrl
.ldctl_value
.bv_val
= ext_bv
->bv_val
;
964 ExternalCtrl
.ldctl_value
.bv_len
= 0;
965 ExternalCtrl
.ldctl_value
.bv_val
= NULL
;
968 controls
[0] = &NoReferrals
;
969 controls
[1] = &PagedResults
;
970 controls
[2] = &ExternalCtrl
;
974 controls
[0] = &NoReferrals
;
975 controls
[1] = &PagedResults
;
979 /* we need to disable referrals as the openldap libs don't
980 handle them and paged results at the same time. Using them
981 together results in the result record containing the server
982 page control being removed from the result list (tridge/jmcd)
984 leaving this in despite the control that says don't generate
985 referrals, in case the server doesn't support it (jmcd)
987 ldap_set_option(ads
->ldap
.ld
, LDAP_OPT_REFERRALS
, LDAP_OPT_OFF
);
989 rc
= ldap_search_with_timeout(ads
->ldap
.ld
, utf8_path
, scope
, utf8_expr
,
990 search_attrs
, 0, controls
,
992 (LDAPMessage
**)res
);
994 ber_free(cookie_be
, 1);
995 ber_bvfree(cookie_bv
);
998 DEBUG(3,("ads_do_paged_search_args: ldap_search_with_timeout(%s) -> %s\n", expr
,
999 ldap_err2string(rc
)));
1000 if (rc
== LDAP_OTHER
) {
1004 ret
= ldap_parse_result(ads
->ldap
.ld
,
1012 if (ret
== LDAP_SUCCESS
) {
1013 DEBUG(3, ("ldap_search_with_timeout(%s) "
1014 "error: %s\n", expr
, ldap_errmsg
));
1015 ldap_memfree(ldap_errmsg
);
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 (rc
!= LDAP_SUCCESS
&& *res
!= NULL
) {
1058 ads_msgfree(ads
, *res
);
1062 /* if/when we decide to utf8-encode attrs, take out this next line */
1063 TALLOC_FREE(search_attrs
);
1065 return ADS_ERROR(rc
);
1068 static ADS_STATUS
ads_do_paged_search(ADS_STRUCT
*ads
, const char *bind_path
,
1069 int scope
, const char *expr
,
1070 const char **attrs
, LDAPMessage
**res
,
1071 int *count
, struct berval
**cookie
)
1073 return ads_do_paged_search_args(ads
, bind_path
, scope
, expr
, attrs
, NULL
, res
, count
, cookie
);
1078 * Get all results for a search. This uses ads_do_paged_search() to return
1079 * all entries in a large search.
1080 * @param ads connection to ads server
1081 * @param bind_path Base dn for the search
1082 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1083 * @param expr Search expression
1084 * @param attrs Attributes to retrieve
1085 * @param res ** which will contain results - free res* with ads_msgfree()
1086 * @return status of search
1088 ADS_STATUS
ads_do_search_all_args(ADS_STRUCT
*ads
, const char *bind_path
,
1089 int scope
, const char *expr
,
1090 const char **attrs
, void *args
,
1093 struct berval
*cookie
= NULL
;
1098 status
= ads_do_paged_search_args(ads
, bind_path
, scope
, expr
, attrs
, args
, res
,
1101 if (!ADS_ERR_OK(status
))
1104 #ifdef HAVE_LDAP_ADD_RESULT_ENTRY
1106 LDAPMessage
*res2
= NULL
;
1107 LDAPMessage
*msg
, *next
;
1109 status
= ads_do_paged_search_args(ads
, bind_path
, scope
, expr
,
1110 attrs
, args
, &res2
, &count
, &cookie
);
1111 if (!ADS_ERR_OK(status
)) {
1115 /* this relies on the way that ldap_add_result_entry() works internally. I hope
1116 that this works on all ldap libs, but I have only tested with openldap */
1117 for (msg
= ads_first_message(ads
, res2
); msg
; msg
= next
) {
1118 next
= ads_next_message(ads
, msg
);
1119 ldap_add_result_entry((LDAPMessage
**)res
, msg
);
1121 /* note that we do not free res2, as the memory is now
1122 part of the main returned list */
1125 DEBUG(0, ("no ldap_add_result_entry() support in LDAP libs!\n"));
1126 status
= ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL
);
1132 ADS_STATUS
ads_do_search_all(ADS_STRUCT
*ads
, const char *bind_path
,
1133 int scope
, const char *expr
,
1134 const char **attrs
, LDAPMessage
**res
)
1136 return ads_do_search_all_args(ads
, bind_path
, scope
, expr
, attrs
, NULL
, res
);
1139 ADS_STATUS
ads_do_search_all_sd_flags(ADS_STRUCT
*ads
, const char *bind_path
,
1140 int scope
, const char *expr
,
1141 const char **attrs
, uint32_t sd_flags
,
1146 args
.control
= ADS_SD_FLAGS_OID
;
1147 args
.val
= sd_flags
;
1148 args
.critical
= True
;
1150 return ads_do_search_all_args(ads
, bind_path
, scope
, expr
, attrs
, &args
, res
);
1155 * Run a function on all results for a search. Uses ads_do_paged_search() and
1156 * runs the function as each page is returned, using ads_process_results()
1157 * @param ads connection to ads server
1158 * @param bind_path Base dn for the search
1159 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1160 * @param expr Search expression - specified in local charset
1161 * @param attrs Attributes to retrieve - specified in UTF-8 or ascii
1162 * @param fn Function which takes attr name, values list, and data_area
1163 * @param data_area Pointer which is passed to function on each call
1164 * @return status of search
1166 ADS_STATUS
ads_do_search_all_fn(ADS_STRUCT
*ads
, const char *bind_path
,
1167 int scope
, const char *expr
, const char **attrs
,
1168 bool (*fn
)(ADS_STRUCT
*, char *, void **, void *),
1171 struct berval
*cookie
= NULL
;
1176 status
= ads_do_paged_search(ads
, bind_path
, scope
, expr
, attrs
, &res
,
1179 if (!ADS_ERR_OK(status
)) return status
;
1181 ads_process_results(ads
, res
, fn
, data_area
);
1182 ads_msgfree(ads
, res
);
1185 status
= ads_do_paged_search(ads
, bind_path
, scope
, expr
, attrs
,
1186 &res
, &count
, &cookie
);
1188 if (!ADS_ERR_OK(status
)) break;
1190 ads_process_results(ads
, res
, fn
, data_area
);
1191 ads_msgfree(ads
, res
);
1198 * Do a search with a timeout.
1199 * @param ads connection to ads server
1200 * @param bind_path Base dn for the search
1201 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1202 * @param expr Search expression
1203 * @param attrs Attributes to retrieve
1204 * @param res ** which will contain results - free res* with ads_msgfree()
1205 * @return status of search
1207 ADS_STATUS
ads_do_search(ADS_STRUCT
*ads
, const char *bind_path
, int scope
,
1209 const char **attrs
, LDAPMessage
**res
)
1212 char *utf8_expr
, *utf8_path
, **search_attrs
= NULL
;
1213 size_t converted_size
;
1217 if (!(ctx
= talloc_init("ads_do_search"))) {
1218 DEBUG(1,("ads_do_search: talloc_init() failed!"));
1219 return ADS_ERROR(LDAP_NO_MEMORY
);
1222 /* 0 means the conversion worked but the result was empty
1223 so we only fail if it's negative. In any case, it always
1224 at least nulls out the dest */
1225 if (!push_utf8_talloc(ctx
, &utf8_expr
, expr
, &converted_size
) ||
1226 !push_utf8_talloc(ctx
, &utf8_path
, bind_path
, &converted_size
))
1228 DEBUG(1,("ads_do_search: push_utf8_talloc() failed!"));
1229 rc
= LDAP_NO_MEMORY
;
1233 if (!attrs
|| !(*attrs
))
1234 search_attrs
= NULL
;
1236 /* This would be the utf8-encoded version...*/
1237 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
1238 if (!(search_attrs
= str_list_copy(talloc_tos(), attrs
)))
1240 DEBUG(1,("ads_do_search: str_list_copy() failed!"));
1241 rc
= LDAP_NO_MEMORY
;
1246 /* see the note in ads_do_paged_search - we *must* disable referrals */
1247 ldap_set_option(ads
->ldap
.ld
, LDAP_OPT_REFERRALS
, LDAP_OPT_OFF
);
1249 rc
= ldap_search_with_timeout(ads
->ldap
.ld
, utf8_path
, scope
, utf8_expr
,
1250 search_attrs
, 0, NULL
, NULL
,
1252 (LDAPMessage
**)res
);
1254 if (rc
== LDAP_SIZELIMIT_EXCEEDED
) {
1255 DEBUG(3,("Warning! sizelimit exceeded in ldap. Truncating.\n"));
1260 talloc_destroy(ctx
);
1261 /* if/when we decide to utf8-encode attrs, take out this next line */
1262 TALLOC_FREE(search_attrs
);
1263 return ADS_ERROR(rc
);
1266 * Do a general ADS search
1267 * @param ads connection to ads server
1268 * @param res ** which will contain results - free res* with ads_msgfree()
1269 * @param expr Search expression
1270 * @param attrs Attributes to retrieve
1271 * @return status of search
1273 ADS_STATUS
ads_search(ADS_STRUCT
*ads
, LDAPMessage
**res
,
1274 const char *expr
, const char **attrs
)
1276 return ads_do_search(ads
, ads
->config
.bind_path
, LDAP_SCOPE_SUBTREE
,
1281 * Do a search on a specific DistinguishedName
1282 * @param ads connection to ads server
1283 * @param res ** which will contain results - free res* with ads_msgfree()
1284 * @param dn DistinguishName to search
1285 * @param attrs Attributes to retrieve
1286 * @return status of search
1288 ADS_STATUS
ads_search_dn(ADS_STRUCT
*ads
, LDAPMessage
**res
,
1289 const char *dn
, const char **attrs
)
1291 return ads_do_search(ads
, dn
, LDAP_SCOPE_BASE
, "(objectclass=*)",
1296 * Free up memory from a ads_search
1297 * @param ads connection to ads server
1298 * @param msg Search results to free
1300 void ads_msgfree(ADS_STRUCT
*ads
, LDAPMessage
*msg
)
1307 * Get a dn from search results
1308 * @param ads connection to ads server
1309 * @param msg Search result
1312 char *ads_get_dn(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
, LDAPMessage
*msg
)
1314 char *utf8_dn
, *unix_dn
;
1315 size_t converted_size
;
1317 utf8_dn
= ldap_get_dn(ads
->ldap
.ld
, msg
);
1320 DEBUG (5, ("ads_get_dn: ldap_get_dn failed\n"));
1324 if (!pull_utf8_talloc(mem_ctx
, &unix_dn
, utf8_dn
, &converted_size
)) {
1325 DEBUG(0,("ads_get_dn: string conversion failure utf8 [%s]\n",
1329 ldap_memfree(utf8_dn
);
1334 * Get the parent from a dn
1335 * @param dn the dn to return the parent from
1336 * @return parent dn string
1338 char *ads_parent_dn(const char *dn
)
1346 p
= strchr(dn
, ',');
1356 * Find a machine account given a hostname
1357 * @param ads connection to ads server
1358 * @param res ** which will contain results - free res* with ads_msgfree()
1359 * @param host Hostname to search for
1360 * @return status of search
1362 ADS_STATUS
ads_find_machine_acct(ADS_STRUCT
*ads
, LDAPMessage
**res
,
1363 const char *machine
)
1367 const char *attrs
[] = {"*", "msDS-SupportedEncryptionTypes", "nTSecurityDescriptor", NULL
};
1371 /* the easiest way to find a machine account anywhere in the tree
1372 is to look for hostname$ */
1373 if (asprintf(&expr
, "(samAccountName=%s$)", machine
) == -1) {
1374 DEBUG(1, ("asprintf failed!\n"));
1375 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY
);
1378 status
= ads_search(ads
, res
, expr
, attrs
);
1384 * Initialize a list of mods to be used in a modify request
1385 * @param ctx An initialized TALLOC_CTX
1386 * @return allocated ADS_MODLIST
1388 ADS_MODLIST
ads_init_mods(TALLOC_CTX
*ctx
)
1390 #define ADS_MODLIST_ALLOC_SIZE 10
1393 if ((mods
= talloc_zero_array(ctx
, LDAPMod
*, ADS_MODLIST_ALLOC_SIZE
+ 1)))
1394 /* -1 is safety to make sure we don't go over the end.
1395 need to reset it to NULL before doing ldap modify */
1396 mods
[ADS_MODLIST_ALLOC_SIZE
] = (LDAPMod
*) -1;
1398 return (ADS_MODLIST
)mods
;
1403 add an attribute to the list, with values list already constructed
1405 static ADS_STATUS
ads_modlist_add(TALLOC_CTX
*ctx
, ADS_MODLIST
*mods
,
1406 int mod_op
, const char *name
,
1407 const void *_invals
)
1410 LDAPMod
**modlist
= (LDAPMod
**) *mods
;
1411 struct berval
**ber_values
= NULL
;
1412 char **char_values
= NULL
;
1415 mod_op
= LDAP_MOD_DELETE
;
1417 if (mod_op
& LDAP_MOD_BVALUES
) {
1418 const struct berval
**b
;
1419 b
= discard_const_p(const struct berval
*, _invals
);
1420 ber_values
= ads_dup_values(ctx
, b
);
1423 c
= discard_const_p(const char *, _invals
);
1424 char_values
= ads_push_strvals(ctx
, c
);
1428 /* find the first empty slot */
1429 for (curmod
=0; modlist
[curmod
] && modlist
[curmod
] != (LDAPMod
*) -1;
1431 if (modlist
[curmod
] == (LDAPMod
*) -1) {
1432 if (!(modlist
= talloc_realloc(ctx
, modlist
, LDAPMod
*,
1433 curmod
+ADS_MODLIST_ALLOC_SIZE
+1)))
1434 return ADS_ERROR(LDAP_NO_MEMORY
);
1435 memset(&modlist
[curmod
], 0,
1436 ADS_MODLIST_ALLOC_SIZE
*sizeof(LDAPMod
*));
1437 modlist
[curmod
+ADS_MODLIST_ALLOC_SIZE
] = (LDAPMod
*) -1;
1438 *mods
= (ADS_MODLIST
)modlist
;
1441 if (!(modlist
[curmod
] = talloc_zero(ctx
, LDAPMod
)))
1442 return ADS_ERROR(LDAP_NO_MEMORY
);
1443 modlist
[curmod
]->mod_type
= talloc_strdup(ctx
, name
);
1444 if (mod_op
& LDAP_MOD_BVALUES
) {
1445 modlist
[curmod
]->mod_bvalues
= ber_values
;
1446 } else if (mod_op
& LDAP_MOD_DELETE
) {
1447 modlist
[curmod
]->mod_values
= NULL
;
1449 modlist
[curmod
]->mod_values
= char_values
;
1452 modlist
[curmod
]->mod_op
= mod_op
;
1453 return ADS_ERROR(LDAP_SUCCESS
);
1457 * Add a single string value to a mod list
1458 * @param ctx An initialized TALLOC_CTX
1459 * @param mods An initialized ADS_MODLIST
1460 * @param name The attribute name to add
1461 * @param val The value to add - NULL means DELETE
1462 * @return ADS STATUS indicating success of add
1464 ADS_STATUS
ads_mod_str(TALLOC_CTX
*ctx
, ADS_MODLIST
*mods
,
1465 const char *name
, const char *val
)
1467 const char *values
[2];
1473 return ads_modlist_add(ctx
, mods
, LDAP_MOD_DELETE
, name
, NULL
);
1474 return ads_modlist_add(ctx
, mods
, LDAP_MOD_REPLACE
, name
, values
);
1478 * Add an array of string values to a mod list
1479 * @param ctx An initialized TALLOC_CTX
1480 * @param mods An initialized ADS_MODLIST
1481 * @param name The attribute name to add
1482 * @param vals The array of string values to add - NULL means DELETE
1483 * @return ADS STATUS indicating success of add
1485 ADS_STATUS
ads_mod_strlist(TALLOC_CTX
*ctx
, ADS_MODLIST
*mods
,
1486 const char *name
, const char **vals
)
1489 return ads_modlist_add(ctx
, mods
, LDAP_MOD_DELETE
, name
, NULL
);
1490 return ads_modlist_add(ctx
, mods
, LDAP_MOD_REPLACE
,
1491 name
, (const void **) vals
);
1496 * Add a single ber-encoded value to a mod list
1497 * @param ctx An initialized TALLOC_CTX
1498 * @param mods An initialized ADS_MODLIST
1499 * @param name The attribute name to add
1500 * @param val The value to add - NULL means DELETE
1501 * @return ADS STATUS indicating success of add
1503 static ADS_STATUS
ads_mod_ber(TALLOC_CTX
*ctx
, ADS_MODLIST
*mods
,
1504 const char *name
, const struct berval
*val
)
1506 const struct berval
*values
[2];
1511 return ads_modlist_add(ctx
, mods
, LDAP_MOD_DELETE
, name
, NULL
);
1512 return ads_modlist_add(ctx
, mods
, LDAP_MOD_REPLACE
|LDAP_MOD_BVALUES
,
1513 name
, (const void **) values
);
1517 static void ads_print_error(int ret
, LDAP
*ld
)
1520 char *ld_error
= NULL
;
1521 ldap_get_option(ld
, LDAP_OPT_ERROR_STRING
, &ld_error
);
1522 DEBUG(10,("AD LDAP failure %d (%s):\n%s\n", ret
,
1523 ldap_err2string(ret
), ld_error
));
1524 SAFE_FREE(ld_error
);
1529 * Perform an ldap modify
1530 * @param ads connection to ads server
1531 * @param mod_dn DistinguishedName to modify
1532 * @param mods list of modifications to perform
1533 * @return status of modify
1535 ADS_STATUS
ads_gen_mod(ADS_STRUCT
*ads
, const char *mod_dn
, ADS_MODLIST mods
)
1538 char *utf8_dn
= NULL
;
1539 size_t converted_size
;
1541 this control is needed to modify that contains a currently
1542 non-existent attribute (but allowable for the object) to run
1544 LDAPControl PermitModify
= {
1545 discard_const_p(char, ADS_PERMIT_MODIFY_OID
),
1548 LDAPControl
*controls
[2];
1550 controls
[0] = &PermitModify
;
1553 if (!push_utf8_talloc(talloc_tos(), &utf8_dn
, mod_dn
, &converted_size
)) {
1554 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY
);
1557 /* find the end of the list, marked by NULL or -1 */
1558 for(i
=0;(mods
[i
]!=0)&&(mods
[i
]!=(LDAPMod
*) -1);i
++);
1559 /* make sure the end of the list is NULL */
1561 ret
= ldap_modify_ext_s(ads
->ldap
.ld
, utf8_dn
,
1562 (LDAPMod
**) mods
, controls
, NULL
);
1563 ads_print_error(ret
, ads
->ldap
.ld
);
1564 TALLOC_FREE(utf8_dn
);
1565 return ADS_ERROR(ret
);
1569 * Perform an ldap add
1570 * @param ads connection to ads server
1571 * @param new_dn DistinguishedName to add
1572 * @param mods list of attributes and values for DN
1573 * @return status of add
1575 ADS_STATUS
ads_gen_add(ADS_STRUCT
*ads
, const char *new_dn
, ADS_MODLIST mods
)
1578 char *utf8_dn
= NULL
;
1579 size_t converted_size
;
1581 if (!push_utf8_talloc(talloc_tos(), &utf8_dn
, new_dn
, &converted_size
)) {
1582 DEBUG(1, ("ads_gen_add: push_utf8_talloc failed!"));
1583 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY
);
1586 /* find the end of the list, marked by NULL or -1 */
1587 for(i
=0;(mods
[i
]!=0)&&(mods
[i
]!=(LDAPMod
*) -1);i
++);
1588 /* make sure the end of the list is NULL */
1591 ret
= ldap_add_s(ads
->ldap
.ld
, utf8_dn
, (LDAPMod
**)mods
);
1592 ads_print_error(ret
, ads
->ldap
.ld
);
1593 TALLOC_FREE(utf8_dn
);
1594 return ADS_ERROR(ret
);
1598 * Delete a DistinguishedName
1599 * @param ads connection to ads server
1600 * @param new_dn DistinguishedName to delete
1601 * @return status of delete
1603 ADS_STATUS
ads_del_dn(ADS_STRUCT
*ads
, char *del_dn
)
1606 char *utf8_dn
= NULL
;
1607 size_t converted_size
;
1608 if (!push_utf8_talloc(talloc_tos(), &utf8_dn
, del_dn
, &converted_size
)) {
1609 DEBUG(1, ("ads_del_dn: push_utf8_talloc failed!"));
1610 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY
);
1613 ret
= ldap_delete_s(ads
->ldap
.ld
, utf8_dn
);
1614 ads_print_error(ret
, ads
->ldap
.ld
);
1615 TALLOC_FREE(utf8_dn
);
1616 return ADS_ERROR(ret
);
1620 * Build an org unit string
1621 * if org unit is Computers or blank then assume a container, otherwise
1622 * assume a / separated list of organisational units.
1623 * jmcd: '\' is now used for escapes so certain chars can be in the ou (e.g. #)
1624 * @param ads connection to ads server
1625 * @param org_unit Organizational unit
1626 * @return org unit string - caller must free
1628 char *ads_ou_string(ADS_STRUCT
*ads
, const char *org_unit
)
1632 if (!org_unit
|| !*org_unit
) {
1634 ret
= ads_default_ou_string(ads
, DS_GUID_COMPUTERS_CONTAINER
);
1636 /* samba4 might not yet respond to a wellknownobject-query */
1637 return ret
? ret
: SMB_STRDUP("cn=Computers");
1640 if (strequal(org_unit
, "Computers")) {
1641 return SMB_STRDUP("cn=Computers");
1644 /* jmcd: removed "\\" from the separation chars, because it is
1645 needed as an escape for chars like '#' which are valid in an
1647 return ads_build_path(org_unit
, "/", "ou=", 1);
1651 * Get a org unit string for a well-known GUID
1652 * @param ads connection to ads server
1653 * @param wknguid Well known GUID
1654 * @return org unit string - caller must free
1656 char *ads_default_ou_string(ADS_STRUCT
*ads
, const char *wknguid
)
1659 LDAPMessage
*res
= NULL
;
1660 char *base
, *wkn_dn
= NULL
, *ret
= NULL
, **wkn_dn_exp
= NULL
,
1661 **bind_dn_exp
= NULL
;
1662 const char *attrs
[] = {"distinguishedName", NULL
};
1663 int new_ln
, wkn_ln
, bind_ln
, i
;
1665 if (wknguid
== NULL
) {
1669 if (asprintf(&base
, "<WKGUID=%s,%s>", wknguid
, ads
->config
.bind_path
) == -1) {
1670 DEBUG(1, ("asprintf failed!\n"));
1674 status
= ads_search_dn(ads
, &res
, base
, attrs
);
1675 if (!ADS_ERR_OK(status
)) {
1676 DEBUG(1,("Failed while searching for: %s\n", base
));
1680 if (ads_count_replies(ads
, res
) != 1) {
1684 /* substitute the bind-path from the well-known-guid-search result */
1685 wkn_dn
= ads_get_dn(ads
, talloc_tos(), res
);
1690 wkn_dn_exp
= ldap_explode_dn(wkn_dn
, 0);
1695 bind_dn_exp
= ldap_explode_dn(ads
->config
.bind_path
, 0);
1700 for (wkn_ln
=0; wkn_dn_exp
[wkn_ln
]; wkn_ln
++)
1702 for (bind_ln
=0; bind_dn_exp
[bind_ln
]; bind_ln
++)
1705 new_ln
= wkn_ln
- bind_ln
;
1707 ret
= SMB_STRDUP(wkn_dn_exp
[0]);
1712 for (i
=1; i
< new_ln
; i
++) {
1715 if (asprintf(&s
, "%s,%s", ret
, wkn_dn_exp
[i
]) == -1) {
1721 ret
= SMB_STRDUP(s
);
1730 ads_msgfree(ads
, res
);
1731 TALLOC_FREE(wkn_dn
);
1733 ldap_value_free(wkn_dn_exp
);
1736 ldap_value_free(bind_dn_exp
);
1743 * Adds (appends) an item to an attribute array, rather then
1744 * replacing the whole list
1745 * @param ctx An initialized TALLOC_CTX
1746 * @param mods An initialized ADS_MODLIST
1747 * @param name name of the ldap attribute to append to
1748 * @param vals an array of values to add
1749 * @return status of addition
1752 ADS_STATUS
ads_add_strlist(TALLOC_CTX
*ctx
, ADS_MODLIST
*mods
,
1753 const char *name
, const char **vals
)
1755 return ads_modlist_add(ctx
, mods
, LDAP_MOD_ADD
, name
,
1756 (const void *) vals
);
1760 * Determines the an account's current KVNO via an LDAP lookup
1761 * @param ads An initialized ADS_STRUCT
1762 * @param account_name the NT samaccountname.
1763 * @return the kvno for the account, or -1 in case of a failure.
1766 uint32_t ads_get_kvno(ADS_STRUCT
*ads
, const char *account_name
)
1768 LDAPMessage
*res
= NULL
;
1769 uint32_t kvno
= (uint32_t)-1; /* -1 indicates a failure */
1771 const char *attrs
[] = {"msDS-KeyVersionNumber", NULL
};
1772 char *dn_string
= NULL
;
1773 ADS_STATUS ret
= ADS_ERROR(LDAP_SUCCESS
);
1775 DEBUG(5,("ads_get_kvno: Searching for account %s\n", account_name
));
1776 if (asprintf(&filter
, "(samAccountName=%s)", account_name
) == -1) {
1779 ret
= ads_search(ads
, &res
, filter
, attrs
);
1781 if (!ADS_ERR_OK(ret
) || (ads_count_replies(ads
, res
) != 1)) {
1782 DEBUG(1,("ads_get_kvno: Account for %s not found.\n", account_name
));
1783 ads_msgfree(ads
, res
);
1787 dn_string
= ads_get_dn(ads
, talloc_tos(), res
);
1789 DEBUG(0,("ads_get_kvno: out of memory.\n"));
1790 ads_msgfree(ads
, res
);
1793 DEBUG(5,("ads_get_kvno: Using: %s\n", dn_string
));
1794 TALLOC_FREE(dn_string
);
1796 /* ---------------------------------------------------------
1797 * 0 is returned as a default KVNO from this point on...
1798 * This is done because Windows 2000 does not support key
1799 * version numbers. Chances are that a failure in the next
1800 * step is simply due to Windows 2000 being used for a
1801 * domain controller. */
1804 if (!ads_pull_uint32(ads
, res
, "msDS-KeyVersionNumber", &kvno
)) {
1805 DEBUG(3,("ads_get_kvno: Error Determining KVNO!\n"));
1806 DEBUG(3,("ads_get_kvno: Windows 2000 does not support KVNO's, so this may be normal.\n"));
1807 ads_msgfree(ads
, res
);
1812 DEBUG(5,("ads_get_kvno: Looked Up KVNO of: %d\n", kvno
));
1813 ads_msgfree(ads
, res
);
1818 * Determines the computer account's current KVNO via an LDAP lookup
1819 * @param ads An initialized ADS_STRUCT
1820 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1821 * @return the kvno for the computer account, or -1 in case of a failure.
1824 uint32_t ads_get_machine_kvno(ADS_STRUCT
*ads
, const char *machine_name
)
1826 char *computer_account
= NULL
;
1829 if (asprintf(&computer_account
, "%s$", machine_name
) < 0) {
1833 kvno
= ads_get_kvno(ads
, computer_account
);
1834 free(computer_account
);
1840 * This clears out all registered spn's for a given hostname
1841 * @param ads An initilaized ADS_STRUCT
1842 * @param machine_name the NetBIOS name of the computer.
1843 * @return 0 upon success, non-zero otherwise.
1846 ADS_STATUS
ads_clear_service_principal_names(ADS_STRUCT
*ads
, const char *machine_name
)
1849 LDAPMessage
*res
= NULL
;
1851 const char *servicePrincipalName
[1] = {NULL
};
1852 ADS_STATUS ret
= ADS_ERROR(LDAP_SUCCESS
);
1853 char *dn_string
= NULL
;
1855 ret
= ads_find_machine_acct(ads
, &res
, machine_name
);
1856 if (!ADS_ERR_OK(ret
) || ads_count_replies(ads
, res
) != 1) {
1857 DEBUG(5,("ads_clear_service_principal_names: WARNING: Host Account for %s not found... skipping operation.\n", machine_name
));
1858 DEBUG(5,("ads_clear_service_principal_names: WARNING: Service Principals for %s have NOT been cleared.\n", machine_name
));
1859 ads_msgfree(ads
, res
);
1860 return ADS_ERROR(LDAP_NO_SUCH_OBJECT
);
1863 DEBUG(5,("ads_clear_service_principal_names: Host account for %s found\n", machine_name
));
1864 ctx
= talloc_init("ads_clear_service_principal_names");
1866 ads_msgfree(ads
, res
);
1867 return ADS_ERROR(LDAP_NO_MEMORY
);
1870 if (!(mods
= ads_init_mods(ctx
))) {
1871 talloc_destroy(ctx
);
1872 ads_msgfree(ads
, res
);
1873 return ADS_ERROR(LDAP_NO_MEMORY
);
1875 ret
= ads_mod_strlist(ctx
, &mods
, "servicePrincipalName", servicePrincipalName
);
1876 if (!ADS_ERR_OK(ret
)) {
1877 DEBUG(1,("ads_clear_service_principal_names: Error creating strlist.\n"));
1878 ads_msgfree(ads
, res
);
1879 talloc_destroy(ctx
);
1882 dn_string
= ads_get_dn(ads
, talloc_tos(), res
);
1884 talloc_destroy(ctx
);
1885 ads_msgfree(ads
, res
);
1886 return ADS_ERROR(LDAP_NO_MEMORY
);
1888 ret
= ads_gen_mod(ads
, dn_string
, mods
);
1889 TALLOC_FREE(dn_string
);
1890 if (!ADS_ERR_OK(ret
)) {
1891 DEBUG(1,("ads_clear_service_principal_names: Error: Updating Service Principals for machine %s in LDAP\n",
1893 ads_msgfree(ads
, res
);
1894 talloc_destroy(ctx
);
1898 ads_msgfree(ads
, res
);
1899 talloc_destroy(ctx
);
1904 * @brief Search for an element in a string array.
1906 * @param[in] el_array The string array to search.
1908 * @param[in] num_el The number of elements in the string array.
1910 * @param[in] el The string to search.
1912 * @return True if found, false if not.
1914 bool ads_element_in_array(const char **el_array
, size_t num_el
, const char *el
)
1918 if (el_array
== NULL
|| num_el
== 0 || el
== NULL
) {
1922 for (i
= 0; i
< num_el
&& el_array
[i
] != NULL
; i
++) {
1925 cmp
= strcasecmp_m(el_array
[i
], el
);
1935 * @brief This gets the service principal names of an existing computer account.
1937 * @param[in] mem_ctx The memory context to use to allocate the spn array.
1939 * @param[in] ads The ADS context to use.
1941 * @param[in] machine_name The NetBIOS name of the computer, which is used to
1942 * identify the computer account.
1944 * @param[in] spn_array A pointer to store the array for SPNs.
1946 * @param[in] num_spns The number of principals stored in the array.
1948 * @return 0 on success, or a ADS error if a failure occurred.
1950 ADS_STATUS
ads_get_service_principal_names(TALLOC_CTX
*mem_ctx
,
1952 const char *machine_name
,
1957 LDAPMessage
*res
= NULL
;
1960 status
= ads_find_machine_acct(ads
,
1963 if (!ADS_ERR_OK(status
)) {
1964 DEBUG(1,("Host Account for %s not found... skipping operation.\n",
1969 count
= ads_count_replies(ads
, res
);
1971 status
= ADS_ERROR(LDAP_NO_SUCH_OBJECT
);
1975 *spn_array
= ads_pull_strings(ads
,
1978 "servicePrincipalName",
1980 if (*spn_array
== NULL
) {
1981 DEBUG(1, ("Host account for %s does not have service principal "
1984 status
= ADS_ERROR(LDAP_NO_SUCH_OBJECT
);
1989 ads_msgfree(ads
, res
);
1995 * This adds a service principal name to an existing computer account
1996 * (found by hostname) in AD.
1997 * @param ads An initialized ADS_STRUCT
1998 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1999 * @param my_fqdn The fully qualified DNS name of the machine
2000 * @param spn A string of the service principal to add, i.e. 'host'
2001 * @return 0 upon sucess, or non-zero if a failure occurs
2004 ADS_STATUS
ads_add_service_principal_name(ADS_STRUCT
*ads
, const char *machine_name
,
2005 const char *my_fqdn
, const char *spn
)
2009 LDAPMessage
*res
= NULL
;
2012 char *dn_string
= NULL
;
2013 const char *servicePrincipalName
[3] = {NULL
, NULL
, NULL
};
2015 ret
= ads_find_machine_acct(ads
, &res
, machine_name
);
2016 if (!ADS_ERR_OK(ret
) || ads_count_replies(ads
, res
) != 1) {
2017 DEBUG(1,("ads_add_service_principal_name: WARNING: Host Account for %s not found... skipping operation.\n",
2019 DEBUG(1,("ads_add_service_principal_name: WARNING: Service Principal '%s/%s@%s' has NOT been added.\n",
2020 spn
, machine_name
, ads
->config
.realm
));
2021 ads_msgfree(ads
, res
);
2022 return ADS_ERROR(LDAP_NO_SUCH_OBJECT
);
2025 DEBUG(1,("ads_add_service_principal_name: Host account for %s found\n", machine_name
));
2026 if (!(ctx
= talloc_init("ads_add_service_principal_name"))) {
2027 ads_msgfree(ads
, res
);
2028 return ADS_ERROR(LDAP_NO_MEMORY
);
2031 /* add short name spn */
2033 if ( (psp1
= talloc_asprintf(ctx
, "%s/%s", spn
, machine_name
)) == NULL
) {
2034 talloc_destroy(ctx
);
2035 ads_msgfree(ads
, res
);
2036 return ADS_ERROR(LDAP_NO_MEMORY
);
2038 if (!strlower_m(&psp1
[strlen(spn
) + 1])) {
2039 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
2042 servicePrincipalName
[0] = psp1
;
2044 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n",
2045 psp1
, machine_name
));
2048 /* add fully qualified spn */
2050 if ( (psp2
= talloc_asprintf(ctx
, "%s/%s", spn
, my_fqdn
)) == NULL
) {
2051 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
2054 if (!strlower_m(&psp2
[strlen(spn
) + 1])) {
2055 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
2058 servicePrincipalName
[1] = psp2
;
2060 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n",
2061 psp2
, machine_name
));
2063 if ( (mods
= ads_init_mods(ctx
)) == NULL
) {
2064 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
2068 ret
= ads_add_strlist(ctx
, &mods
, "servicePrincipalName", servicePrincipalName
);
2069 if (!ADS_ERR_OK(ret
)) {
2070 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
2074 if ( (dn_string
= ads_get_dn(ads
, ctx
, res
)) == NULL
) {
2075 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
2079 ret
= ads_gen_mod(ads
, dn_string
, mods
);
2080 if (!ADS_ERR_OK(ret
)) {
2081 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
2087 ads_msgfree(ads
, res
);
2092 * adds a machine account to the ADS server
2093 * @param ads An intialized ADS_STRUCT
2094 * @param machine_name - the NetBIOS machine name of this account.
2095 * @param account_type A number indicating the type of account to create
2096 * @param org_unit The LDAP path in which to place this account
2097 * @return 0 upon success, or non-zero otherwise
2100 ADS_STATUS
ads_create_machine_acct(ADS_STRUCT
*ads
,
2101 const char *machine_name
,
2102 const char *org_unit
,
2103 uint32_t etype_list
)
2106 char *samAccountName
, *controlstr
;
2109 char *machine_escaped
= NULL
;
2111 const char *objectClass
[] = {"top", "person", "organizationalPerson",
2112 "user", "computer", NULL
};
2113 LDAPMessage
*res
= NULL
;
2114 uint32_t acct_control
= ( UF_WORKSTATION_TRUST_ACCOUNT
|\
2115 UF_DONT_EXPIRE_PASSWD
|\
2116 UF_ACCOUNTDISABLE
);
2117 uint32_t func_level
= 0;
2119 ret
= ads_domain_func_level(ads
, &func_level
);
2120 if (!ADS_ERR_OK(ret
)) {
2124 if (!(ctx
= talloc_init("ads_add_machine_acct")))
2125 return ADS_ERROR(LDAP_NO_MEMORY
);
2127 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
2129 machine_escaped
= escape_rdn_val_string_alloc(machine_name
);
2130 if (!machine_escaped
) {
2134 new_dn
= talloc_asprintf(ctx
, "cn=%s,%s", machine_escaped
, org_unit
);
2135 samAccountName
= talloc_asprintf(ctx
, "%s$", machine_name
);
2137 if ( !new_dn
|| !samAccountName
) {
2141 if (!(controlstr
= talloc_asprintf(ctx
, "%u", acct_control
))) {
2145 if (!(mods
= ads_init_mods(ctx
))) {
2149 ads_mod_str(ctx
, &mods
, "cn", machine_name
);
2150 ads_mod_str(ctx
, &mods
, "sAMAccountName", samAccountName
);
2151 ads_mod_strlist(ctx
, &mods
, "objectClass", objectClass
);
2152 ads_mod_str(ctx
, &mods
, "userAccountControl", controlstr
);
2154 if (func_level
>= DS_DOMAIN_FUNCTION_2008
) {
2155 const char *etype_list_str
;
2157 etype_list_str
= talloc_asprintf(ctx
, "%d", (int)etype_list
);
2158 if (etype_list_str
== NULL
) {
2161 ads_mod_str(ctx
, &mods
, "msDS-SupportedEncryptionTypes",
2165 ret
= ads_gen_add(ads
, new_dn
, mods
);
2168 SAFE_FREE(machine_escaped
);
2169 ads_msgfree(ads
, res
);
2170 talloc_destroy(ctx
);
2176 * move a machine account to another OU on the ADS server
2177 * @param ads - An intialized ADS_STRUCT
2178 * @param machine_name - the NetBIOS machine name of this account.
2179 * @param org_unit - The LDAP path in which to place this account
2180 * @param moved - whether we moved the machine account (optional)
2181 * @return 0 upon success, or non-zero otherwise
2184 ADS_STATUS
ads_move_machine_acct(ADS_STRUCT
*ads
, const char *machine_name
,
2185 const char *org_unit
, bool *moved
)
2189 LDAPMessage
*res
= NULL
;
2190 char *filter
= NULL
;
2191 char *computer_dn
= NULL
;
2193 char *computer_rdn
= NULL
;
2194 bool need_move
= False
;
2196 if (asprintf(&filter
, "(samAccountName=%s$)", machine_name
) == -1) {
2197 rc
= ADS_ERROR(LDAP_NO_MEMORY
);
2201 /* Find pre-existing machine */
2202 rc
= ads_search(ads
, &res
, filter
, NULL
);
2203 if (!ADS_ERR_OK(rc
)) {
2207 computer_dn
= ads_get_dn(ads
, talloc_tos(), res
);
2209 rc
= ADS_ERROR(LDAP_NO_MEMORY
);
2213 parent_dn
= ads_parent_dn(computer_dn
);
2214 if (strequal(parent_dn
, org_unit
)) {
2220 if (asprintf(&computer_rdn
, "CN=%s", machine_name
) == -1) {
2221 rc
= ADS_ERROR(LDAP_NO_MEMORY
);
2225 ldap_status
= ldap_rename_s(ads
->ldap
.ld
, computer_dn
, computer_rdn
,
2226 org_unit
, 1, NULL
, NULL
);
2227 rc
= ADS_ERROR(ldap_status
);
2230 ads_msgfree(ads
, res
);
2232 TALLOC_FREE(computer_dn
);
2233 SAFE_FREE(computer_rdn
);
2235 if (!ADS_ERR_OK(rc
)) {
2247 dump a binary result from ldap
2249 static void dump_binary(ADS_STRUCT
*ads
, const char *field
, struct berval
**values
)
2252 for (i
=0; values
[i
]; i
++) {
2253 printf("%s: ", field
);
2254 for (j
=0; j
<values
[i
]->bv_len
; j
++) {
2255 printf("%02X", (unsigned char)values
[i
]->bv_val
[j
]);
2261 static void dump_guid(ADS_STRUCT
*ads
, const char *field
, struct berval
**values
)
2264 for (i
=0; values
[i
]; i
++) {
2266 DATA_BLOB in
= data_blob_const(values
[i
]->bv_val
, values
[i
]->bv_len
);
2269 status
= GUID_from_ndr_blob(&in
, &guid
);
2270 if (NT_STATUS_IS_OK(status
)) {
2271 printf("%s: %s\n", field
, GUID_string(talloc_tos(), &guid
));
2273 printf("%s: INVALID GUID\n", field
);
2279 dump a sid result from ldap
2281 static void dump_sid(ADS_STRUCT
*ads
, const char *field
, struct berval
**values
)
2284 for (i
=0; values
[i
]; i
++) {
2287 if (!sid_parse((const uint8_t *)values
[i
]->bv_val
,
2288 values
[i
]->bv_len
, &sid
)) {
2291 printf("%s: %s\n", field
, sid_to_fstring(tmp
, &sid
));
2296 dump ntSecurityDescriptor
2298 static void dump_sd(ADS_STRUCT
*ads
, const char *filed
, struct berval
**values
)
2300 TALLOC_CTX
*frame
= talloc_stackframe();
2301 struct security_descriptor
*psd
;
2304 status
= unmarshall_sec_desc(talloc_tos(), (uint8_t *)values
[0]->bv_val
,
2305 values
[0]->bv_len
, &psd
);
2306 if (!NT_STATUS_IS_OK(status
)) {
2307 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
2308 nt_errstr(status
)));
2314 ads_disp_sd(ads
, talloc_tos(), psd
);
2321 dump a string result from ldap
2323 static void dump_string(const char *field
, char **values
)
2326 for (i
=0; values
[i
]; i
++) {
2327 printf("%s: %s\n", field
, values
[i
]);
2332 dump a field from LDAP on stdout
2336 static bool ads_dump_field(ADS_STRUCT
*ads
, char *field
, void **values
, void *data_area
)
2341 void (*handler
)(ADS_STRUCT
*, const char *, struct berval
**);
2343 {"objectGUID", False
, dump_guid
},
2344 {"netbootGUID", False
, dump_guid
},
2345 {"nTSecurityDescriptor", False
, dump_sd
},
2346 {"dnsRecord", False
, dump_binary
},
2347 {"objectSid", False
, dump_sid
},
2348 {"tokenGroups", False
, dump_sid
},
2349 {"tokenGroupsNoGCAcceptable", False
, dump_sid
},
2350 {"tokengroupsGlobalandUniversal", False
, dump_sid
},
2351 {"mS-DS-CreatorSID", False
, dump_sid
},
2352 {"msExchMailboxGuid", False
, dump_guid
},
2357 if (!field
) { /* must be end of an entry */
2362 for (i
=0; handlers
[i
].name
; i
++) {
2363 if (strcasecmp_m(handlers
[i
].name
, field
) == 0) {
2364 if (!values
) /* first time, indicate string or not */
2365 return handlers
[i
].string
;
2366 handlers
[i
].handler(ads
, field
, (struct berval
**) values
);
2370 if (!handlers
[i
].name
) {
2371 if (!values
) /* first time, indicate string conversion */
2373 dump_string(field
, (char **)values
);
2379 * Dump a result from LDAP on stdout
2380 * used for debugging
2381 * @param ads connection to ads server
2382 * @param res Results to dump
2385 void ads_dump(ADS_STRUCT
*ads
, LDAPMessage
*res
)
2387 ads_process_results(ads
, res
, ads_dump_field
, NULL
);
2391 * Walk through results, calling a function for each entry found.
2392 * The function receives a field name, a berval * array of values,
2393 * and a data area passed through from the start. The function is
2394 * called once with null for field and values at the end of each
2396 * @param ads connection to ads server
2397 * @param res Results to process
2398 * @param fn Function for processing each result
2399 * @param data_area user-defined area to pass to function
2401 void ads_process_results(ADS_STRUCT
*ads
, LDAPMessage
*res
,
2402 bool (*fn
)(ADS_STRUCT
*, char *, void **, void *),
2407 size_t converted_size
;
2409 if (!(ctx
= talloc_init("ads_process_results")))
2412 for (msg
= ads_first_entry(ads
, res
); msg
;
2413 msg
= ads_next_entry(ads
, msg
)) {
2417 for (utf8_field
=ldap_first_attribute(ads
->ldap
.ld
,
2418 (LDAPMessage
*)msg
,&b
);
2420 utf8_field
=ldap_next_attribute(ads
->ldap
.ld
,
2421 (LDAPMessage
*)msg
,b
)) {
2422 struct berval
**ber_vals
;
2428 if (!pull_utf8_talloc(ctx
, &field
, utf8_field
,
2431 DEBUG(0,("ads_process_results: "
2432 "pull_utf8_talloc failed: %s",
2436 string
= fn(ads
, field
, NULL
, data_area
);
2441 utf8_vals
= ldap_get_values(ads
->ldap
.ld
,
2442 (LDAPMessage
*)msg
, field
);
2443 p
= discard_const_p(const char *, utf8_vals
);
2444 str_vals
= ads_pull_strvals(ctx
, p
);
2445 fn(ads
, field
, (void **) str_vals
, data_area
);
2446 ldap_value_free(utf8_vals
);
2448 ber_vals
= ldap_get_values_len(ads
->ldap
.ld
,
2449 (LDAPMessage
*)msg
, field
);
2450 fn(ads
, field
, (void **) ber_vals
, data_area
);
2452 ldap_value_free_len(ber_vals
);
2454 ldap_memfree(utf8_field
);
2457 talloc_free_children(ctx
);
2458 fn(ads
, NULL
, NULL
, data_area
); /* completed an entry */
2461 talloc_destroy(ctx
);
2465 * count how many replies are in a LDAPMessage
2466 * @param ads connection to ads server
2467 * @param res Results to count
2468 * @return number of replies
2470 int ads_count_replies(ADS_STRUCT
*ads
, void *res
)
2472 return ldap_count_entries(ads
->ldap
.ld
, (LDAPMessage
*)res
);
2476 * pull the first entry from a ADS result
2477 * @param ads connection to ads server
2478 * @param res Results of search
2479 * @return first entry from result
2481 LDAPMessage
*ads_first_entry(ADS_STRUCT
*ads
, LDAPMessage
*res
)
2483 return ldap_first_entry(ads
->ldap
.ld
, res
);
2487 * pull the next entry from a ADS result
2488 * @param ads connection to ads server
2489 * @param res Results of search
2490 * @return next entry from result
2492 LDAPMessage
*ads_next_entry(ADS_STRUCT
*ads
, LDAPMessage
*res
)
2494 return ldap_next_entry(ads
->ldap
.ld
, res
);
2498 * pull the first message from a ADS result
2499 * @param ads connection to ads server
2500 * @param res Results of search
2501 * @return first message from result
2503 LDAPMessage
*ads_first_message(ADS_STRUCT
*ads
, LDAPMessage
*res
)
2505 return ldap_first_message(ads
->ldap
.ld
, res
);
2509 * pull the next message from a ADS result
2510 * @param ads connection to ads server
2511 * @param res Results of search
2512 * @return next message from result
2514 LDAPMessage
*ads_next_message(ADS_STRUCT
*ads
, LDAPMessage
*res
)
2516 return ldap_next_message(ads
->ldap
.ld
, res
);
2520 * pull a single string from a ADS result
2521 * @param ads connection to ads server
2522 * @param mem_ctx TALLOC_CTX to use for allocating result string
2523 * @param msg Results of search
2524 * @param field Attribute to retrieve
2525 * @return Result string in talloc context
2527 char *ads_pull_string(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
, LDAPMessage
*msg
,
2533 size_t converted_size
;
2535 values
= ldap_get_values(ads
->ldap
.ld
, msg
, field
);
2539 if (values
[0] && pull_utf8_talloc(mem_ctx
, &ux_string
, values
[0],
2544 ldap_value_free(values
);
2549 * pull an array of strings from a ADS result
2550 * @param ads connection to ads server
2551 * @param mem_ctx TALLOC_CTX to use for allocating result string
2552 * @param msg Results of search
2553 * @param field Attribute to retrieve
2554 * @return Result strings in talloc context
2556 char **ads_pull_strings(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
,
2557 LDAPMessage
*msg
, const char *field
,
2563 size_t converted_size
;
2565 values
= ldap_get_values(ads
->ldap
.ld
, msg
, field
);
2569 *num_values
= ldap_count_values(values
);
2571 ret
= talloc_array(mem_ctx
, char *, *num_values
+ 1);
2573 ldap_value_free(values
);
2577 for (i
=0;i
<*num_values
;i
++) {
2578 if (!pull_utf8_talloc(mem_ctx
, &ret
[i
], values
[i
],
2581 ldap_value_free(values
);
2587 ldap_value_free(values
);
2592 * pull an array of strings from a ADS result
2593 * (handle large multivalue attributes with range retrieval)
2594 * @param ads connection to ads server
2595 * @param mem_ctx TALLOC_CTX to use for allocating result string
2596 * @param msg Results of search
2597 * @param field Attribute to retrieve
2598 * @param current_strings strings returned by a previous call to this function
2599 * @param next_attribute The next query should ask for this attribute
2600 * @param num_values How many values did we get this time?
2601 * @param more_values Are there more values to get?
2602 * @return Result strings in talloc context
2604 char **ads_pull_strings_range(ADS_STRUCT
*ads
,
2605 TALLOC_CTX
*mem_ctx
,
2606 LDAPMessage
*msg
, const char *field
,
2607 char **current_strings
,
2608 const char **next_attribute
,
2609 size_t *num_strings
,
2613 char *expected_range_attrib
, *range_attr
;
2614 BerElement
*ptr
= NULL
;
2617 size_t num_new_strings
;
2618 unsigned long int range_start
;
2619 unsigned long int range_end
;
2621 /* we might have been given the whole lot anyway */
2622 if ((strings
= ads_pull_strings(ads
, mem_ctx
, msg
, field
, num_strings
))) {
2623 *more_strings
= False
;
2627 expected_range_attrib
= talloc_asprintf(mem_ctx
, "%s;Range=", field
);
2629 /* look for Range result */
2630 for (attr
= ldap_first_attribute(ads
->ldap
.ld
, (LDAPMessage
*)msg
, &ptr
);
2632 attr
= ldap_next_attribute(ads
->ldap
.ld
, (LDAPMessage
*)msg
, ptr
)) {
2633 /* we ignore the fact that this is utf8, as all attributes are ascii... */
2634 if (strnequal(attr
, expected_range_attrib
, strlen(expected_range_attrib
))) {
2642 /* nothing here - this field is just empty */
2643 *more_strings
= False
;
2647 if (sscanf(&range_attr
[strlen(expected_range_attrib
)], "%lu-%lu",
2648 &range_start
, &range_end
) == 2) {
2649 *more_strings
= True
;
2651 if (sscanf(&range_attr
[strlen(expected_range_attrib
)], "%lu-*",
2652 &range_start
) == 1) {
2653 *more_strings
= False
;
2655 DEBUG(1, ("ads_pull_strings_range: Cannot parse Range attriubte (%s)\n",
2657 ldap_memfree(range_attr
);
2658 *more_strings
= False
;
2663 if ((*num_strings
) != range_start
) {
2664 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) doesn't start at %u, but at %lu"
2665 " - aborting range retreival\n",
2666 range_attr
, (unsigned int)(*num_strings
) + 1, range_start
));
2667 ldap_memfree(range_attr
);
2668 *more_strings
= False
;
2672 new_strings
= ads_pull_strings(ads
, mem_ctx
, msg
, range_attr
, &num_new_strings
);
2674 if (*more_strings
&& ((*num_strings
+ num_new_strings
) != (range_end
+ 1))) {
2675 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) tells us we have %lu "
2676 "strings in this bunch, but we only got %lu - aborting range retreival\n",
2677 range_attr
, (unsigned long int)range_end
- range_start
+ 1,
2678 (unsigned long int)num_new_strings
));
2679 ldap_memfree(range_attr
);
2680 *more_strings
= False
;
2684 strings
= talloc_realloc(mem_ctx
, current_strings
, char *,
2685 *num_strings
+ num_new_strings
);
2687 if (strings
== NULL
) {
2688 ldap_memfree(range_attr
);
2689 *more_strings
= False
;
2693 if (new_strings
&& num_new_strings
) {
2694 memcpy(&strings
[*num_strings
], new_strings
,
2695 sizeof(*new_strings
) * num_new_strings
);
2698 (*num_strings
) += num_new_strings
;
2700 if (*more_strings
) {
2701 *next_attribute
= talloc_asprintf(mem_ctx
,
2706 if (!*next_attribute
) {
2707 DEBUG(1, ("talloc_asprintf for next attribute failed!\n"));
2708 ldap_memfree(range_attr
);
2709 *more_strings
= False
;
2714 ldap_memfree(range_attr
);
2720 * pull a single uint32_t from a ADS result
2721 * @param ads connection to ads server
2722 * @param msg Results of search
2723 * @param field Attribute to retrieve
2724 * @param v Pointer to int to store result
2725 * @return boolean inidicating success
2727 bool ads_pull_uint32(ADS_STRUCT
*ads
, LDAPMessage
*msg
, const char *field
,
2732 values
= ldap_get_values(ads
->ldap
.ld
, msg
, field
);
2736 ldap_value_free(values
);
2740 *v
= atoi(values
[0]);
2741 ldap_value_free(values
);
2746 * pull a single objectGUID from an ADS result
2747 * @param ads connection to ADS server
2748 * @param msg results of search
2749 * @param guid 37-byte area to receive text guid
2750 * @return boolean indicating success
2752 bool ads_pull_guid(ADS_STRUCT
*ads
, LDAPMessage
*msg
, struct GUID
*guid
)
2757 if (!smbldap_talloc_single_blob(talloc_tos(), ads
->ldap
.ld
, msg
, "objectGUID",
2762 status
= GUID_from_ndr_blob(&blob
, guid
);
2763 talloc_free(blob
.data
);
2764 return NT_STATUS_IS_OK(status
);
2769 * pull a single struct dom_sid from a ADS result
2770 * @param ads connection to ads server
2771 * @param msg Results of search
2772 * @param field Attribute to retrieve
2773 * @param sid Pointer to sid to store result
2774 * @return boolean inidicating success
2776 bool ads_pull_sid(ADS_STRUCT
*ads
, LDAPMessage
*msg
, const char *field
,
2777 struct dom_sid
*sid
)
2779 return smbldap_pull_sid(ads
->ldap
.ld
, msg
, field
, sid
);
2783 * pull an array of struct dom_sids from a ADS result
2784 * @param ads connection to ads server
2785 * @param mem_ctx TALLOC_CTX for allocating sid array
2786 * @param msg Results of search
2787 * @param field Attribute to retrieve
2788 * @param sids pointer to sid array to allocate
2789 * @return the count of SIDs pulled
2791 int ads_pull_sids(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
,
2792 LDAPMessage
*msg
, const char *field
, struct dom_sid
**sids
)
2794 struct berval
**values
;
2798 values
= ldap_get_values_len(ads
->ldap
.ld
, msg
, field
);
2803 for (i
=0; values
[i
]; i
++)
2807 (*sids
) = talloc_array(mem_ctx
, struct dom_sid
, i
);
2809 ldap_value_free_len(values
);
2817 for (i
=0; values
[i
]; i
++) {
2818 ret
= sid_parse((const uint8_t *)values
[i
]->bv_val
,
2819 values
[i
]->bv_len
, &(*sids
)[count
]);
2821 DEBUG(10, ("pulling SID: %s\n",
2822 sid_string_dbg(&(*sids
)[count
])));
2827 ldap_value_free_len(values
);
2832 * pull a struct security_descriptor from a ADS result
2833 * @param ads connection to ads server
2834 * @param mem_ctx TALLOC_CTX for allocating sid array
2835 * @param msg Results of search
2836 * @param field Attribute to retrieve
2837 * @param sd Pointer to *struct security_descriptor to store result (talloc()ed)
2838 * @return boolean inidicating success
2840 bool ads_pull_sd(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
,
2841 LDAPMessage
*msg
, const char *field
,
2842 struct security_descriptor
**sd
)
2844 struct berval
**values
;
2847 values
= ldap_get_values_len(ads
->ldap
.ld
, msg
, field
);
2849 if (!values
) return false;
2853 status
= unmarshall_sec_desc(mem_ctx
,
2854 (uint8_t *)values
[0]->bv_val
,
2855 values
[0]->bv_len
, sd
);
2856 if (!NT_STATUS_IS_OK(status
)) {
2857 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
2858 nt_errstr(status
)));
2863 ldap_value_free_len(values
);
2868 * in order to support usernames longer than 21 characters we need to
2869 * use both the sAMAccountName and the userPrincipalName attributes
2870 * It seems that not all users have the userPrincipalName attribute set
2872 * @param ads connection to ads server
2873 * @param mem_ctx TALLOC_CTX for allocating sid array
2874 * @param msg Results of search
2875 * @return the username
2877 char *ads_pull_username(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
,
2883 /* lookup_name() only works on the sAMAccountName to
2884 returning the username portion of userPrincipalName
2885 breaks winbindd_getpwnam() */
2887 ret
= ads_pull_string(ads
, mem_ctx
, msg
, "userPrincipalName");
2888 if (ret
&& (p
= strchr_m(ret
, '@'))) {
2893 return ads_pull_string(ads
, mem_ctx
, msg
, "sAMAccountName");
2898 * find the update serial number - this is the core of the ldap cache
2899 * @param ads connection to ads server
2900 * @param ads connection to ADS server
2901 * @param usn Pointer to retrieved update serial number
2902 * @return status of search
2904 ADS_STATUS
ads_USN(ADS_STRUCT
*ads
, uint32_t *usn
)
2906 const char *attrs
[] = {"highestCommittedUSN", NULL
};
2910 status
= ads_do_search_retry(ads
, "", LDAP_SCOPE_BASE
, "(objectclass=*)", attrs
, &res
);
2911 if (!ADS_ERR_OK(status
))
2914 if (ads_count_replies(ads
, res
) != 1) {
2915 ads_msgfree(ads
, res
);
2916 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED
);
2919 if (!ads_pull_uint32(ads
, res
, "highestCommittedUSN", usn
)) {
2920 ads_msgfree(ads
, res
);
2921 return ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE
);
2924 ads_msgfree(ads
, res
);
2928 /* parse a ADS timestring - typical string is
2929 '20020917091222.0Z0' which means 09:12.22 17th September
2931 static time_t ads_parse_time(const char *str
)
2937 if (sscanf(str
, "%4d%2d%2d%2d%2d%2d",
2938 &tm
.tm_year
, &tm
.tm_mon
, &tm
.tm_mday
,
2939 &tm
.tm_hour
, &tm
.tm_min
, &tm
.tm_sec
) != 6) {
2948 /********************************************************************
2949 ********************************************************************/
2951 ADS_STATUS
ads_current_time(ADS_STRUCT
*ads
)
2953 const char *attrs
[] = {"currentTime", NULL
};
2958 ADS_STRUCT
*ads_s
= ads
;
2960 if (!(ctx
= talloc_init("ads_current_time"))) {
2961 return ADS_ERROR(LDAP_NO_MEMORY
);
2964 /* establish a new ldap tcp session if necessary */
2966 if ( !ads
->ldap
.ld
) {
2967 if ( (ads_s
= ads_init( ads
->server
.realm
, ads
->server
.workgroup
,
2968 ads
->server
.ldap_server
)) == NULL
)
2970 status
= ADS_ERROR(LDAP_NO_MEMORY
);
2973 ads_s
->auth
.flags
= ADS_AUTH_ANON_BIND
;
2974 status
= ads_connect( ads_s
);
2975 if ( !ADS_ERR_OK(status
))
2979 status
= ads_do_search(ads_s
, "", LDAP_SCOPE_BASE
, "(objectclass=*)", attrs
, &res
);
2980 if (!ADS_ERR_OK(status
)) {
2984 timestr
= ads_pull_string(ads_s
, ctx
, res
, "currentTime");
2986 ads_msgfree(ads_s
, res
);
2987 status
= ADS_ERROR(LDAP_NO_RESULTS_RETURNED
);
2991 /* but save the time and offset in the original ADS_STRUCT */
2993 ads
->config
.current_time
= ads_parse_time(timestr
);
2995 if (ads
->config
.current_time
!= 0) {
2996 ads
->auth
.time_offset
= ads
->config
.current_time
- time(NULL
);
2997 DEBUG(4,("KDC time offset is %d seconds\n", ads
->auth
.time_offset
));
3000 ads_msgfree(ads
, res
);
3002 status
= ADS_SUCCESS
;
3005 /* free any temporary ads connections */
3006 if ( ads_s
!= ads
) {
3007 ads_destroy( &ads_s
);
3009 talloc_destroy(ctx
);
3014 /********************************************************************
3015 ********************************************************************/
3017 ADS_STATUS
ads_domain_func_level(ADS_STRUCT
*ads
, uint32_t *val
)
3019 const char *attrs
[] = {"domainFunctionality", NULL
};
3022 ADS_STRUCT
*ads_s
= ads
;
3024 *val
= DS_DOMAIN_FUNCTION_2000
;
3026 /* establish a new ldap tcp session if necessary */
3028 if ( !ads
->ldap
.ld
) {
3029 if ( (ads_s
= ads_init( ads
->server
.realm
, ads
->server
.workgroup
,
3030 ads
->server
.ldap_server
)) == NULL
)
3032 status
= ADS_ERROR_NT(NT_STATUS_NO_MEMORY
);
3035 ads_s
->auth
.flags
= ADS_AUTH_ANON_BIND
;
3036 status
= ads_connect( ads_s
);
3037 if ( !ADS_ERR_OK(status
))
3041 /* If the attribute does not exist assume it is a Windows 2000
3042 functional domain */
3044 status
= ads_do_search(ads_s
, "", LDAP_SCOPE_BASE
, "(objectclass=*)", attrs
, &res
);
3045 if (!ADS_ERR_OK(status
)) {
3046 if ( status
.err
.rc
== LDAP_NO_SUCH_ATTRIBUTE
) {
3047 status
= ADS_SUCCESS
;
3052 if ( !ads_pull_uint32(ads_s
, res
, "domainFunctionality", val
) ) {
3053 DEBUG(5,("ads_domain_func_level: Failed to pull the domainFunctionality attribute.\n"));
3055 DEBUG(3,("ads_domain_func_level: %d\n", *val
));
3058 ads_msgfree(ads
, res
);
3061 /* free any temporary ads connections */
3062 if ( ads_s
!= ads
) {
3063 ads_destroy( &ads_s
);
3070 * find the domain sid for our domain
3071 * @param ads connection to ads server
3072 * @param sid Pointer to domain sid
3073 * @return status of search
3075 ADS_STATUS
ads_domain_sid(ADS_STRUCT
*ads
, struct dom_sid
*sid
)
3077 const char *attrs
[] = {"objectSid", NULL
};
3081 rc
= ads_do_search_retry(ads
, ads
->config
.bind_path
, LDAP_SCOPE_BASE
, "(objectclass=*)",
3083 if (!ADS_ERR_OK(rc
)) return rc
;
3084 if (!ads_pull_sid(ads
, res
, "objectSid", sid
)) {
3085 ads_msgfree(ads
, res
);
3086 return ADS_ERROR_SYSTEM(ENOENT
);
3088 ads_msgfree(ads
, res
);
3094 * find our site name
3095 * @param ads connection to ads server
3096 * @param mem_ctx Pointer to talloc context
3097 * @param site_name Pointer to the sitename
3098 * @return status of search
3100 ADS_STATUS
ads_site_dn(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
, const char **site_name
)
3104 const char *dn
, *service_name
;
3105 const char *attrs
[] = { "dsServiceName", NULL
};
3107 status
= ads_do_search(ads
, "", LDAP_SCOPE_BASE
, "(objectclass=*)", attrs
, &res
);
3108 if (!ADS_ERR_OK(status
)) {
3112 service_name
= ads_pull_string(ads
, mem_ctx
, res
, "dsServiceName");
3113 if (service_name
== NULL
) {
3114 ads_msgfree(ads
, res
);
3115 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED
);
3118 ads_msgfree(ads
, res
);
3120 /* go up three levels */
3121 dn
= ads_parent_dn(ads_parent_dn(ads_parent_dn(service_name
)));
3123 return ADS_ERROR(LDAP_NO_MEMORY
);
3126 *site_name
= talloc_strdup(mem_ctx
, dn
);
3127 if (*site_name
== NULL
) {
3128 return ADS_ERROR(LDAP_NO_MEMORY
);
3133 dsServiceName: CN=NTDS Settings,CN=W2K3DC,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,DC=ber,DC=suse,DC=de
3138 * find the site dn where a machine resides
3139 * @param ads connection to ads server
3140 * @param mem_ctx Pointer to talloc context
3141 * @param computer_name name of the machine
3142 * @param site_name Pointer to the sitename
3143 * @return status of search
3145 ADS_STATUS
ads_site_dn_for_machine(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
, const char *computer_name
, const char **site_dn
)
3149 const char *parent
, *filter
;
3150 char *config_context
= NULL
;
3153 /* shortcut a query */
3154 if (strequal(computer_name
, ads
->config
.ldap_server_name
)) {
3155 return ads_site_dn(ads
, mem_ctx
, site_dn
);
3158 status
= ads_config_path(ads
, mem_ctx
, &config_context
);
3159 if (!ADS_ERR_OK(status
)) {
3163 filter
= talloc_asprintf(mem_ctx
, "(cn=%s)", computer_name
);
3164 if (filter
== NULL
) {
3165 return ADS_ERROR(LDAP_NO_MEMORY
);
3168 status
= ads_do_search(ads
, config_context
, LDAP_SCOPE_SUBTREE
,
3169 filter
, NULL
, &res
);
3170 if (!ADS_ERR_OK(status
)) {
3174 if (ads_count_replies(ads
, res
) != 1) {
3175 ads_msgfree(ads
, res
);
3176 return ADS_ERROR(LDAP_NO_SUCH_OBJECT
);
3179 dn
= ads_get_dn(ads
, mem_ctx
, res
);
3181 ads_msgfree(ads
, res
);
3182 return ADS_ERROR(LDAP_NO_MEMORY
);
3185 /* go up three levels */
3186 parent
= ads_parent_dn(ads_parent_dn(ads_parent_dn(dn
)));
3187 if (parent
== NULL
) {
3188 ads_msgfree(ads
, res
);
3190 return ADS_ERROR(LDAP_NO_MEMORY
);
3193 *site_dn
= talloc_strdup(mem_ctx
, parent
);
3194 if (*site_dn
== NULL
) {
3195 ads_msgfree(ads
, res
);
3197 return ADS_ERROR(LDAP_NO_MEMORY
);
3201 ads_msgfree(ads
, res
);
3207 * get the upn suffixes for a domain
3208 * @param ads connection to ads server
3209 * @param mem_ctx Pointer to talloc context
3210 * @param suffixes Pointer to an array of suffixes
3211 * @param num_suffixes Pointer to the number of suffixes
3212 * @return status of search
3214 ADS_STATUS
ads_upn_suffixes(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
, char ***suffixes
, size_t *num_suffixes
)
3219 char *config_context
= NULL
;
3220 const char *attrs
[] = { "uPNSuffixes", NULL
};
3222 status
= ads_config_path(ads
, mem_ctx
, &config_context
);
3223 if (!ADS_ERR_OK(status
)) {
3227 base
= talloc_asprintf(mem_ctx
, "cn=Partitions,%s", config_context
);
3229 return ADS_ERROR(LDAP_NO_MEMORY
);
3232 status
= ads_search_dn(ads
, &res
, base
, attrs
);
3233 if (!ADS_ERR_OK(status
)) {
3237 if (ads_count_replies(ads
, res
) != 1) {
3238 ads_msgfree(ads
, res
);
3239 return ADS_ERROR(LDAP_NO_SUCH_OBJECT
);
3242 (*suffixes
) = ads_pull_strings(ads
, mem_ctx
, res
, "uPNSuffixes", num_suffixes
);
3243 if ((*suffixes
) == NULL
) {
3244 ads_msgfree(ads
, res
);
3245 return ADS_ERROR(LDAP_NO_MEMORY
);
3248 ads_msgfree(ads
, res
);
3254 * get the joinable ous for a domain
3255 * @param ads connection to ads server
3256 * @param mem_ctx Pointer to talloc context
3257 * @param ous Pointer to an array of ous
3258 * @param num_ous Pointer to the number of ous
3259 * @return status of search
3261 ADS_STATUS
ads_get_joinable_ous(ADS_STRUCT
*ads
,
3262 TALLOC_CTX
*mem_ctx
,
3267 LDAPMessage
*res
= NULL
;
3268 LDAPMessage
*msg
= NULL
;
3269 const char *attrs
[] = { "dn", NULL
};
3272 status
= ads_search(ads
, &res
,
3273 "(|(objectClass=domain)(objectclass=organizationalUnit))",
3275 if (!ADS_ERR_OK(status
)) {
3279 count
= ads_count_replies(ads
, res
);
3281 ads_msgfree(ads
, res
);
3282 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED
);
3285 for (msg
= ads_first_entry(ads
, res
); msg
;
3286 msg
= ads_next_entry(ads
, msg
)) {
3287 const char **p
= discard_const_p(const char *, *ous
);
3290 dn
= ads_get_dn(ads
, talloc_tos(), msg
);
3292 ads_msgfree(ads
, res
);
3293 return ADS_ERROR(LDAP_NO_MEMORY
);
3296 if (!add_string_to_array(mem_ctx
, dn
, &p
, num_ous
)) {
3298 ads_msgfree(ads
, res
);
3299 return ADS_ERROR(LDAP_NO_MEMORY
);
3303 *ous
= discard_const_p(char *, p
);
3306 ads_msgfree(ads
, res
);
3313 * pull a struct dom_sid from an extended dn string
3314 * @param mem_ctx TALLOC_CTX
3315 * @param extended_dn string
3316 * @param flags string type of extended_dn
3317 * @param sid pointer to a struct dom_sid
3318 * @return NT_STATUS_OK on success,
3319 * NT_INVALID_PARAMETER on error,
3320 * NT_STATUS_NOT_FOUND if no SID present
3322 ADS_STATUS
ads_get_sid_from_extended_dn(TALLOC_CTX
*mem_ctx
,
3323 const char *extended_dn
,
3324 enum ads_extended_dn_flags flags
,
3325 struct dom_sid
*sid
)
3330 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER
);
3333 /* otherwise extended_dn gets stripped off */
3334 if ((dn
= talloc_strdup(mem_ctx
, extended_dn
)) == NULL
) {
3335 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER
);
3338 * ADS_EXTENDED_DN_HEX_STRING:
3339 * <GUID=238e1963cb390f4bb032ba0105525a29>;<SID=010500000000000515000000bb68c8fd6b61b427572eb04556040000>;CN=gd,OU=berlin,OU=suse,DC=ber,DC=suse,DC=de
3341 * ADS_EXTENDED_DN_STRING (only with w2k3):
3342 * <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
3344 * Object with no SID, such as an Exchange Public Folder
3345 * <GUID=28907fb4bdf6854993e7f0a10b504e7c>;CN=public,CN=Microsoft Exchange System Objects,DC=sd2k3ms,DC=west,DC=isilon,DC=com
3348 p
= strchr(dn
, ';');
3350 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER
);
3353 if (strncmp(p
, ";<SID=", strlen(";<SID=")) != 0) {
3354 DEBUG(5,("No SID present in extended dn\n"));
3355 return ADS_ERROR_NT(NT_STATUS_NOT_FOUND
);
3358 p
+= strlen(";<SID=");
3362 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER
);
3367 DEBUG(100,("ads_get_sid_from_extended_dn: sid string is %s\n", p
));
3371 case ADS_EXTENDED_DN_STRING
:
3372 if (!string_to_sid(sid
, p
)) {
3373 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER
);
3376 case ADS_EXTENDED_DN_HEX_STRING
: {
3380 buf_len
= strhex_to_str(buf
, sizeof(buf
), p
, strlen(p
));
3382 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER
);
3385 if (!sid_parse((const uint8_t *)buf
, buf_len
, sid
)) {
3386 DEBUG(10,("failed to parse sid\n"));
3387 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER
);
3392 DEBUG(10,("unknown extended dn format\n"));
3393 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER
);
3396 return ADS_ERROR_NT(NT_STATUS_OK
);
3399 /********************************************************************
3400 ********************************************************************/
3402 char* ads_get_dnshostname( ADS_STRUCT
*ads
, TALLOC_CTX
*ctx
, const char *machine_name
)
3404 LDAPMessage
*res
= NULL
;
3409 status
= ads_find_machine_acct(ads
, &res
, lp_netbios_name());
3410 if (!ADS_ERR_OK(status
)) {
3411 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
3412 lp_netbios_name()));
3416 if ( (count
= ads_count_replies(ads
, res
)) != 1 ) {
3417 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count
));
3421 if ( (name
= ads_pull_string(ads
, ctx
, res
, "dNSHostName")) == NULL
) {
3422 DEBUG(0,("ads_get_dnshostname: No dNSHostName attribute!\n"));
3426 ads_msgfree(ads
, res
);
3431 /********************************************************************
3432 ********************************************************************/
3434 char* ads_get_upn( ADS_STRUCT
*ads
, TALLOC_CTX
*ctx
, const char *machine_name
)
3436 LDAPMessage
*res
= NULL
;
3441 status
= ads_find_machine_acct(ads
, &res
, machine_name
);
3442 if (!ADS_ERR_OK(status
)) {
3443 DEBUG(0,("ads_get_upn: Failed to find account for %s\n",
3444 lp_netbios_name()));
3448 if ( (count
= ads_count_replies(ads
, res
)) != 1 ) {
3449 DEBUG(1,("ads_get_upn: %d entries returned!\n", count
));
3453 if ( (name
= ads_pull_string(ads
, ctx
, res
, "userPrincipalName")) == NULL
) {
3454 DEBUG(2,("ads_get_upn: No userPrincipalName attribute!\n"));
3458 ads_msgfree(ads
, res
);
3463 /********************************************************************
3464 ********************************************************************/
3466 char* ads_get_samaccountname( ADS_STRUCT
*ads
, TALLOC_CTX
*ctx
, const char *machine_name
)
3468 LDAPMessage
*res
= NULL
;
3473 status
= ads_find_machine_acct(ads
, &res
, lp_netbios_name());
3474 if (!ADS_ERR_OK(status
)) {
3475 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
3476 lp_netbios_name()));
3480 if ( (count
= ads_count_replies(ads
, res
)) != 1 ) {
3481 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count
));
3485 if ( (name
= ads_pull_string(ads
, ctx
, res
, "sAMAccountName")) == NULL
) {
3486 DEBUG(0,("ads_get_dnshostname: No sAMAccountName attribute!\n"));
3490 ads_msgfree(ads
, res
);
3497 SAVED CODE
- we used to join via ldap
- remember how we did
this. JRA
.
3500 * Join a machine to a realm
3501 * Creates the machine account and sets the machine password
3502 * @param ads connection to ads server
3503 * @param machine name of host to add
3504 * @param org_unit Organizational unit to place machine in
3505 * @return status of join
3507 ADS_STATUS
ads_join_realm(ADS_STRUCT
*ads
, const char *machine_name
,
3508 uint32_t account_type
, const char *org_unit
)
3511 LDAPMessage
*res
= NULL
;
3514 /* machine name must be lowercase */
3515 machine
= SMB_STRDUP(machine_name
);
3516 strlower_m(machine
);
3519 status = ads_find_machine_acct(ads, (void **)&res, machine);
3520 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
3521 DEBUG(0, ("Host account for %s already exists - deleting old account\n", machine));
3522 status = ads_leave_realm(ads, machine);
3523 if (!ADS_ERR_OK(status)) {
3524 DEBUG(0, ("Failed to delete host '%s' from the '%s' realm.\n",
3525 machine, ads->config.realm));
3530 status
= ads_add_machine_acct(ads
, machine
, account_type
, org_unit
);
3531 if (!ADS_ERR_OK(status
)) {
3532 DEBUG(0, ("ads_join_realm: ads_add_machine_acct failed (%s): %s\n", machine
, ads_errstr(status
)));
3537 status
= ads_find_machine_acct(ads
, (void **)(void *)&res
, machine
);
3538 if (!ADS_ERR_OK(status
)) {
3539 DEBUG(0, ("ads_join_realm: Host account test failed for machine %s\n", machine
));
3545 ads_msgfree(ads
, res
);
3552 * Delete a machine from the realm
3553 * @param ads connection to ads server
3554 * @param hostname Machine to remove
3555 * @return status of delete
3557 ADS_STATUS
ads_leave_realm(ADS_STRUCT
*ads
, const char *hostname
)
3562 char *hostnameDN
, *host
;
3564 LDAPControl ldap_control
;
3565 LDAPControl
* pldap_control
[2] = {NULL
, NULL
};
3567 pldap_control
[0] = &ldap_control
;
3568 memset(&ldap_control
, 0, sizeof(LDAPControl
));
3569 ldap_control
.ldctl_oid
= discard_const_p(char, LDAP_SERVER_TREE_DELETE_OID
);
3571 /* hostname must be lowercase */
3572 host
= SMB_STRDUP(hostname
);
3573 if (!strlower_m(host
)) {
3575 return ADS_ERROR_SYSTEM(EINVAL
);
3578 status
= ads_find_machine_acct(ads
, &res
, host
);
3579 if (!ADS_ERR_OK(status
)) {
3580 DEBUG(0, ("Host account for %s does not exist.\n", host
));
3585 msg
= ads_first_entry(ads
, res
);
3588 return ADS_ERROR_SYSTEM(ENOENT
);
3591 hostnameDN
= ads_get_dn(ads
, talloc_tos(), (LDAPMessage
*)msg
);
3592 if (hostnameDN
== NULL
) {
3594 return ADS_ERROR_SYSTEM(ENOENT
);
3597 rc
= ldap_delete_ext_s(ads
->ldap
.ld
, hostnameDN
, pldap_control
, NULL
);
3599 DEBUG(3,("ldap_delete_ext_s failed with error code %d\n", rc
));
3601 DEBUG(3,("ldap_delete_ext_s succeeded with error code %d\n", rc
));
3604 if (rc
!= LDAP_SUCCESS
) {
3605 const char *attrs
[] = { "cn", NULL
};
3606 LDAPMessage
*msg_sub
;
3608 /* we only search with scope ONE, we do not expect any further
3609 * objects to be created deeper */
3611 status
= ads_do_search_retry(ads
, hostnameDN
,
3612 LDAP_SCOPE_ONELEVEL
,
3613 "(objectclass=*)", attrs
, &res
);
3615 if (!ADS_ERR_OK(status
)) {
3617 TALLOC_FREE(hostnameDN
);
3621 for (msg_sub
= ads_first_entry(ads
, res
); msg_sub
;
3622 msg_sub
= ads_next_entry(ads
, msg_sub
)) {
3626 if ((dn
= ads_get_dn(ads
, talloc_tos(), msg_sub
)) == NULL
) {
3628 TALLOC_FREE(hostnameDN
);
3629 return ADS_ERROR(LDAP_NO_MEMORY
);
3632 status
= ads_del_dn(ads
, dn
);
3633 if (!ADS_ERR_OK(status
)) {
3634 DEBUG(3,("failed to delete dn %s: %s\n", dn
, ads_errstr(status
)));
3637 TALLOC_FREE(hostnameDN
);
3644 /* there should be no subordinate objects anymore */
3645 status
= ads_do_search_retry(ads
, hostnameDN
,
3646 LDAP_SCOPE_ONELEVEL
,
3647 "(objectclass=*)", attrs
, &res
);
3649 if (!ADS_ERR_OK(status
) || ( (ads_count_replies(ads
, res
)) > 0 ) ) {
3651 TALLOC_FREE(hostnameDN
);
3655 /* delete hostnameDN now */
3656 status
= ads_del_dn(ads
, hostnameDN
);
3657 if (!ADS_ERR_OK(status
)) {
3659 DEBUG(3,("failed to delete dn %s: %s\n", hostnameDN
, ads_errstr(status
)));
3660 TALLOC_FREE(hostnameDN
);
3665 TALLOC_FREE(hostnameDN
);
3667 status
= ads_find_machine_acct(ads
, &res
, host
);
3668 if (ADS_ERR_OK(status
) && ads_count_replies(ads
, res
) == 1) {
3669 DEBUG(3, ("Failed to remove host account.\n"));
3679 * pull all token-sids from an LDAP dn
3680 * @param ads connection to ads server
3681 * @param mem_ctx TALLOC_CTX for allocating sid array
3682 * @param dn of LDAP object
3683 * @param user_sid pointer to struct dom_sid (objectSid)
3684 * @param primary_group_sid pointer to struct dom_sid (self composed)
3685 * @param sids pointer to sid array to allocate
3686 * @param num_sids counter of SIDs pulled
3687 * @return status of token query
3689 ADS_STATUS
ads_get_tokensids(ADS_STRUCT
*ads
,
3690 TALLOC_CTX
*mem_ctx
,
3692 struct dom_sid
*user_sid
,
3693 struct dom_sid
*primary_group_sid
,
3694 struct dom_sid
**sids
,
3698 LDAPMessage
*res
= NULL
;
3700 size_t tmp_num_sids
;
3701 struct dom_sid
*tmp_sids
;
3702 struct dom_sid tmp_user_sid
;
3703 struct dom_sid tmp_primary_group_sid
;
3705 const char *attrs
[] = {
3712 status
= ads_search_retry_dn(ads
, &res
, dn
, attrs
);
3713 if (!ADS_ERR_OK(status
)) {
3717 count
= ads_count_replies(ads
, res
);
3719 ads_msgfree(ads
, res
);
3720 return ADS_ERROR_LDAP(LDAP_NO_SUCH_OBJECT
);
3723 if (!ads_pull_sid(ads
, res
, "objectSid", &tmp_user_sid
)) {
3724 ads_msgfree(ads
, res
);
3725 return ADS_ERROR_LDAP(LDAP_NO_MEMORY
);
3728 if (!ads_pull_uint32(ads
, res
, "primaryGroupID", &pgid
)) {
3729 ads_msgfree(ads
, res
);
3730 return ADS_ERROR_LDAP(LDAP_NO_MEMORY
);
3734 /* hack to compose the primary group sid without knowing the
3737 struct dom_sid domsid
;
3739 sid_copy(&domsid
, &tmp_user_sid
);
3741 if (!sid_split_rid(&domsid
, NULL
)) {
3742 ads_msgfree(ads
, res
);
3743 return ADS_ERROR_LDAP(LDAP_NO_MEMORY
);
3746 if (!sid_compose(&tmp_primary_group_sid
, &domsid
, pgid
)) {
3747 ads_msgfree(ads
, res
);
3748 return ADS_ERROR_LDAP(LDAP_NO_MEMORY
);
3752 tmp_num_sids
= ads_pull_sids(ads
, mem_ctx
, res
, "tokenGroups", &tmp_sids
);
3754 if (tmp_num_sids
== 0 || !tmp_sids
) {
3755 ads_msgfree(ads
, res
);
3756 return ADS_ERROR_LDAP(LDAP_NO_MEMORY
);
3760 *num_sids
= tmp_num_sids
;
3768 *user_sid
= tmp_user_sid
;
3771 if (primary_group_sid
) {
3772 *primary_group_sid
= tmp_primary_group_sid
;
3775 DEBUG(10,("ads_get_tokensids: returned %d sids\n", (int)tmp_num_sids
+ 2));
3777 ads_msgfree(ads
, res
);
3778 return ADS_ERROR_LDAP(LDAP_SUCCESS
);
3782 * Find a sAMAccoutName in LDAP
3783 * @param ads connection to ads server
3784 * @param mem_ctx TALLOC_CTX for allocating sid array
3785 * @param samaccountname to search
3786 * @param uac_ret uint32_t pointer userAccountControl attribute value
3787 * @param dn_ret pointer to dn
3788 * @return status of token query
3790 ADS_STATUS
ads_find_samaccount(ADS_STRUCT
*ads
,
3791 TALLOC_CTX
*mem_ctx
,
3792 const char *samaccountname
,
3794 const char **dn_ret
)
3797 const char *attrs
[] = { "userAccountControl", NULL
};
3799 LDAPMessage
*res
= NULL
;
3803 filter
= talloc_asprintf(mem_ctx
, "(&(objectclass=user)(sAMAccountName=%s))",
3805 if (filter
== NULL
) {
3806 status
= ADS_ERROR_NT(NT_STATUS_NO_MEMORY
);
3810 status
= ads_do_search_all(ads
, ads
->config
.bind_path
,
3812 filter
, attrs
, &res
);
3814 if (!ADS_ERR_OK(status
)) {
3818 if (ads_count_replies(ads
, res
) != 1) {
3819 status
= ADS_ERROR(LDAP_NO_RESULTS_RETURNED
);
3823 dn
= ads_get_dn(ads
, talloc_tos(), res
);
3825 status
= ADS_ERROR(LDAP_NO_MEMORY
);
3829 if (!ads_pull_uint32(ads
, res
, "userAccountControl", &uac
)) {
3830 status
= ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE
);
3839 *dn_ret
= talloc_strdup(mem_ctx
, dn
);
3841 status
= ADS_ERROR(LDAP_NO_MEMORY
);
3847 ads_msgfree(ads
, res
);
3853 * find our configuration path
3854 * @param ads connection to ads server
3855 * @param mem_ctx Pointer to talloc context
3856 * @param config_path Pointer to the config path
3857 * @return status of search
3859 ADS_STATUS
ads_config_path(ADS_STRUCT
*ads
,
3860 TALLOC_CTX
*mem_ctx
,
3864 LDAPMessage
*res
= NULL
;
3865 const char *config_context
= NULL
;
3866 const char *attrs
[] = { "configurationNamingContext", NULL
};
3868 status
= ads_do_search(ads
, "", LDAP_SCOPE_BASE
,
3869 "(objectclass=*)", attrs
, &res
);
3870 if (!ADS_ERR_OK(status
)) {
3874 config_context
= ads_pull_string(ads
, mem_ctx
, res
,
3875 "configurationNamingContext");
3876 ads_msgfree(ads
, res
);
3877 if (!config_context
) {
3878 return ADS_ERROR(LDAP_NO_MEMORY
);
3882 *config_path
= talloc_strdup(mem_ctx
, config_context
);
3883 if (!*config_path
) {
3884 return ADS_ERROR(LDAP_NO_MEMORY
);
3888 return ADS_ERROR(LDAP_SUCCESS
);
3892 * find the displayName of an extended right
3893 * @param ads connection to ads server
3894 * @param config_path The config path
3895 * @param mem_ctx Pointer to talloc context
3896 * @param GUID struct of the rightsGUID
3897 * @return status of search
3899 const char *ads_get_extended_right_name_by_guid(ADS_STRUCT
*ads
,
3900 const char *config_path
,
3901 TALLOC_CTX
*mem_ctx
,
3902 const struct GUID
*rights_guid
)
3905 LDAPMessage
*res
= NULL
;
3907 const char *attrs
[] = { "displayName", NULL
};
3908 const char *result
= NULL
;
3911 if (!ads
|| !mem_ctx
|| !rights_guid
) {
3915 expr
= talloc_asprintf(mem_ctx
, "(rightsGuid=%s)",
3916 GUID_string(mem_ctx
, rights_guid
));
3921 path
= talloc_asprintf(mem_ctx
, "cn=Extended-Rights,%s", config_path
);
3926 rc
= ads_do_search_retry(ads
, path
, LDAP_SCOPE_SUBTREE
,
3928 if (!ADS_ERR_OK(rc
)) {
3932 if (ads_count_replies(ads
, res
) != 1) {
3936 result
= ads_pull_string(ads
, mem_ctx
, res
, "displayName");
3939 ads_msgfree(ads
, res
);
3944 * verify or build and verify an account ou
3945 * @param mem_ctx Pointer to talloc context
3946 * @param ads connection to ads server
3948 * @return status of search
3951 ADS_STATUS
ads_check_ou_dn(TALLOC_CTX
*mem_ctx
,
3953 const char **account_ou
)
3959 if (account_ou
== NULL
) {
3960 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER
);
3963 if (*account_ou
!= NULL
) {
3964 exploded_dn
= ldap_explode_dn(*account_ou
, 0);
3966 ldap_value_free(exploded_dn
);
3971 ou_string
= ads_ou_string(ads
, *account_ou
);
3973 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX
);
3976 name
= talloc_asprintf(mem_ctx
, "%s,%s", ou_string
,
3977 ads
->config
.bind_path
);
3978 SAFE_FREE(ou_string
);
3981 return ADS_ERROR_LDAP(LDAP_NO_MEMORY
);
3984 exploded_dn
= ldap_explode_dn(name
, 0);
3986 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX
);
3988 ldap_value_free(exploded_dn
);