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/tsocket/tsocket.h"
29 #include "../lib/addns/dnsquery.h"
30 #include "../libds/common/flags.h"
32 #include "../libcli/security/security.h"
33 #include "../librpc/gen_ndr/netlogon.h"
34 #include "lib/param/loadparm.h"
35 #include "libsmb/namequery.h"
36 #include "../librpc/gen_ndr/ndr_ads.h"
37 #include "auth/credentials/credentials.h"
43 * @brief basic ldap client-side routines for ads server communications
45 * The routines contained here should do the necessary ldap calls for
48 * Important note: attribute names passed into ads_ routines must
49 * already be in UTF-8 format. We do not convert them because in almost
50 * all cases, they are just ascii (which is represented with the same
51 * codepoints in UTF-8). This may have to change at some point
55 #define LDAP_SERVER_TREE_DELETE_OID "1.2.840.113556.1.4.805"
57 static SIG_ATOMIC_T gotalarm
;
59 /***************************************************************
60 Signal function to tell us we timed out.
61 ****************************************************************/
63 static void gotalarm_sig(int signum
)
68 LDAP
*ldap_open_with_timeout(const char *server
,
69 struct sockaddr_storage
*ss
,
70 int port
, unsigned int to
)
76 DEBUG(10, ("Opening connection to LDAP server '%s:%d', timeout "
77 "%u seconds\n", server
, port
, to
));
82 CatchSignal(SIGALRM
, gotalarm_sig
);
84 /* End setup timeout. */
87 if ( strchr_m(server
, ':') ) {
89 uri
= talloc_asprintf(talloc_tos(), "ldap://[%s]:%u", server
, port
);
92 uri
= talloc_asprintf(talloc_tos(), "ldap://%s:%u", server
, port
);
98 #ifdef HAVE_LDAP_INIT_FD
101 NTSTATUS status
= NT_STATUS_UNSUCCESSFUL
;
102 unsigned timeout_ms
= 1000 * to
;
104 status
= open_socket_out(ss
, port
, timeout_ms
, &fd
);
105 if (!NT_STATUS_IS_OK(status
)) {
106 DEBUG(3, ("open_socket_out: failed to open socket\n"));
110 /* define LDAP_PROTO_TCP from openldap.h if required */
111 #ifndef LDAP_PROTO_TCP
112 #define LDAP_PROTO_TCP 1
114 ldap_err
= ldap_init_fd(fd
, LDAP_PROTO_TCP
, uri
, &ldp
);
116 #elif defined(HAVE_LDAP_INITIALIZE)
117 ldap_err
= ldap_initialize(&ldp
, uri
);
119 ldp
= ldap_open(server
, port
);
121 ldap_err
= LDAP_SUCCESS
;
123 ldap_err
= LDAP_OTHER
;
126 if (ldap_err
!= LDAP_SUCCESS
) {
127 DEBUG(2,("Could not initialize connection for LDAP server '%s': %s\n",
128 uri
, ldap_err2string(ldap_err
)));
130 DEBUG(10, ("Initialized connection for LDAP server '%s'\n", uri
));
134 /* Teardown timeout. */
136 CatchSignal(SIGALRM
, SIG_IGN
);
142 static int ldap_search_with_timeout(LDAP
*ld
,
143 LDAP_CONST
char *base
,
145 LDAP_CONST
char *filter
,
148 LDAPControl
**sctrls
,
149 LDAPControl
**cctrls
,
153 int to
= lp_ldap_timeout();
154 struct timeval timeout
;
155 struct timeval
*timeout_ptr
= NULL
;
158 DBG_DEBUG("ldap_search: base => [%s], filter => [%s], scope => [%d]\n",
163 /* Setup timeout for the ldap_search_ext_s call - local and remote. */
169 timeout_ptr
= &timeout
;
171 /* Setup alarm timeout. */
172 CatchSignal(SIGALRM
, gotalarm_sig
);
173 /* Make the alarm time one second beyond
174 the timeout we're setting for the
175 remote search timeout, to allow that
176 to fire in preference. */
178 /* End setup timeout. */
182 result
= ldap_search_ext_s(ld
, base
, scope
, filter
, attrs
,
183 attrsonly
, sctrls
, cctrls
, timeout_ptr
,
187 /* Teardown alarm timeout. */
188 CatchSignal(SIGALRM
, SIG_IGN
);
193 return LDAP_TIMELIMIT_EXCEEDED
;
196 * A bug in OpenLDAP means ldap_search_ext_s can return
197 * LDAP_SUCCESS but with a NULL res pointer. Cope with
198 * this. See bug #6279 for details. JRA.
202 return LDAP_TIMELIMIT_EXCEEDED
;
208 /**********************************************
209 Do client and server sitename match ?
210 **********************************************/
212 bool ads_sitename_match(ADS_STRUCT
*ads
)
214 if (ads
->config
.server_site_name
== NULL
&&
215 ads
->config
.client_site_name
== NULL
) {
216 DEBUG(10,("ads_sitename_match: both null\n"));
219 if (ads
->config
.server_site_name
&&
220 ads
->config
.client_site_name
&&
221 strequal(ads
->config
.server_site_name
,
222 ads
->config
.client_site_name
)) {
223 DEBUG(10,("ads_sitename_match: name %s match\n", ads
->config
.server_site_name
));
226 DEBUG(10,("ads_sitename_match: no match between server: %s and client: %s\n",
227 ads
->config
.server_site_name
? ads
->config
.server_site_name
: "NULL",
228 ads
->config
.client_site_name
? ads
->config
.client_site_name
: "NULL"));
232 /**********************************************
233 Is this the closest DC ?
234 **********************************************/
236 bool ads_closest_dc(ADS_STRUCT
*ads
)
238 if (ads
->config
.flags
& NBT_SERVER_CLOSEST
) {
239 DEBUG(10,("ads_closest_dc: NBT_SERVER_CLOSEST flag set\n"));
243 /* not sure if this can ever happen */
244 if (ads_sitename_match(ads
)) {
245 DEBUG(10,("ads_closest_dc: NBT_SERVER_CLOSEST flag not set but sites match\n"));
249 if (ads
->config
.client_site_name
== NULL
) {
250 DEBUG(10,("ads_closest_dc: client belongs to no site\n"));
254 DEBUG(10,("ads_closest_dc: %s is not the closest DC\n",
255 ads
->config
.ldap_server_name
));
260 static bool ads_fill_cldap_reply(ADS_STRUCT
*ads
,
262 const struct sockaddr_storage
*ss
,
263 const struct NETLOGON_SAM_LOGON_RESPONSE_EX
*cldap_reply
)
265 TALLOC_CTX
*frame
= talloc_stackframe();
267 char addr
[INET6_ADDRSTRLEN
];
271 print_sockaddr(addr
, sizeof(addr
), ss
);
273 /* Check the CLDAP reply flags */
275 if (!(cldap_reply
->server_type
& NBT_SERVER_LDAP
)) {
276 DBG_WARNING("%s's CLDAP reply says it is not an LDAP server!\n",
282 /* Fill in the ads->config values */
284 ADS_TALLOC_CONST_FREE(ads
->config
.workgroup
);
285 ADS_TALLOC_CONST_FREE(ads
->config
.realm
);
286 ADS_TALLOC_CONST_FREE(ads
->config
.bind_path
);
287 ADS_TALLOC_CONST_FREE(ads
->config
.ldap_server_name
);
288 ADS_TALLOC_CONST_FREE(ads
->config
.server_site_name
);
289 ADS_TALLOC_CONST_FREE(ads
->config
.client_site_name
);
291 if (!check_cldap_reply_required_flags(cldap_reply
->server_type
,
292 ads
->config
.flags
)) {
297 ads
->config
.ldap_server_name
= talloc_strdup(ads
,
298 cldap_reply
->pdc_dns_name
);
299 if (ads
->config
.ldap_server_name
== NULL
) {
300 DBG_WARNING("Out of memory\n");
305 ads
->config
.workgroup
= talloc_strdup(ads
, cldap_reply
->domain_name
);
306 if (ads
->config
.workgroup
== NULL
) {
307 DBG_WARNING("Out of memory\n");
312 ads
->config
.realm
= talloc_asprintf_strupper_m(ads
,
314 cldap_reply
->dns_domain
);
315 if (ads
->config
.realm
== NULL
) {
316 DBG_WARNING("Out of memory\n");
321 status
= ads_build_dn(ads
->config
.realm
, ads
, &dn
);
322 if (!ADS_ERR_OK(status
)) {
323 DBG_DEBUG("Failed to build bind path: %s\n",
328 ads
->config
.bind_path
= dn
;
330 if (*cldap_reply
->server_site
) {
331 ads
->config
.server_site_name
=
332 talloc_strdup(ads
, cldap_reply
->server_site
);
333 if (ads
->config
.server_site_name
== NULL
) {
334 DBG_WARNING("Out of memory\n");
340 if (*cldap_reply
->client_site
) {
341 ads
->config
.client_site_name
=
342 talloc_strdup(ads
, cldap_reply
->client_site
);
343 if (ads
->config
.client_site_name
== NULL
) {
344 DBG_WARNING("Out of memory\n");
350 ads
->ldap
.port
= gc
? LDAP_GC_PORT
: LDAP_PORT
;
353 /* Store our site name. */
354 sitename_store(cldap_reply
->domain_name
, cldap_reply
->client_site
);
355 sitename_store(cldap_reply
->dns_domain
, cldap_reply
->client_site
);
357 /* Leave this until last so that the flags are not clobbered */
358 ads
->config
.flags
= cldap_reply
->server_type
;
369 try a connection to a given ldap server, returning True and setting the servers IP
370 in the ads struct if successful
372 static bool ads_try_connect(ADS_STRUCT
*ads
, bool gc
,
373 struct sockaddr_storage
*ss
)
375 struct NETLOGON_SAM_LOGON_RESPONSE_EX cldap_reply
= {};
376 TALLOC_CTX
*frame
= talloc_stackframe();
378 char addr
[INET6_ADDRSTRLEN
] = { 0, };
385 print_sockaddr(addr
, sizeof(addr
), ss
);
387 DBG_INFO("ads_try_connect: sending CLDAP request to %s (realm: %s)\n",
388 addr
, ads
->server
.realm
);
390 ok
= ads_cldap_netlogon_5(frame
, ss
, ads
->server
.realm
, &cldap_reply
);
392 DBG_NOTICE("ads_cldap_netlogon_5(%s, %s) failed.\n",
393 addr
, ads
->server
.realm
);
398 ok
= ads_fill_cldap_reply(ads
, gc
, ss
, &cldap_reply
);
400 DBG_NOTICE("ads_fill_cldap_reply(%s, %s) failed.\n",
401 addr
, ads
->server
.realm
);
410 /**********************************************************************
411 send a cldap ping to list of servers, one at a time, until one of
412 them answers it's an ldap server. Record success in the ADS_STRUCT.
413 Take note of and update negative connection cache.
414 **********************************************************************/
416 static NTSTATUS
cldap_ping_list(ADS_STRUCT
*ads
,
418 struct samba_sockaddr
*sa_list
,
421 TALLOC_CTX
*frame
= talloc_stackframe();
422 struct timeval endtime
= timeval_current_ofs(MAX(3,lp_ldap_timeout()/2), 0);
423 uint32_t nt_version
= NETLOGON_NT_VERSION_5
| NETLOGON_NT_VERSION_5EX
;
424 struct tsocket_address
**ts_list
= NULL
;
425 const struct tsocket_address
* const *ts_list_const
= NULL
;
426 struct samba_sockaddr
**req_sa_list
= NULL
;
427 struct netlogon_samlogon_response
**responses
= NULL
;
428 size_t num_requests
= 0;
434 ts_list
= talloc_zero_array(frame
,
435 struct tsocket_address
*,
437 if (ts_list
== NULL
) {
439 return NT_STATUS_NO_MEMORY
;
442 req_sa_list
= talloc_zero_array(frame
,
443 struct samba_sockaddr
*,
445 if (req_sa_list
== NULL
) {
447 return NT_STATUS_NO_MEMORY
;
452 * The retry loop is bound by the timeout
457 for (i
= 0; i
< count
; i
++) {
458 char server
[INET6_ADDRSTRLEN
];
461 if (is_zero_addr(&sa_list
[i
].u
.ss
)) {
465 print_sockaddr(server
, sizeof(server
), &sa_list
[i
].u
.ss
);
467 status
= check_negative_conn_cache(domain
, server
);
468 if (!NT_STATUS_IS_OK(status
)) {
472 ret
= tsocket_address_inet_from_strings(ts_list
, "ip",
474 &ts_list
[num_requests
]);
476 status
= map_nt_error_from_unix(errno
);
477 DBG_WARNING("Failed to create tsocket_address for %s - %s\n",
478 server
, nt_errstr(status
));
483 req_sa_list
[num_requests
] = &sa_list
[i
];
487 DBG_DEBUG("Try to create %zu netlogon connections for domain '%s' "
488 "(provided count of addresses was %zu).\n",
493 if (num_requests
== 0) {
494 status
= NT_STATUS_NO_LOGON_SERVERS
;
495 DBG_WARNING("domain[%s] num_requests[%zu] for count[%zu] - %s\n",
496 domain
, num_requests
, count
, nt_errstr(status
));
501 ts_list_const
= (const struct tsocket_address
* const *)ts_list
;
503 status
= cldap_multi_netlogon(frame
,
504 ts_list_const
, num_requests
,
505 ads
->server
.realm
, NULL
,
507 1, endtime
, &responses
);
508 if (!NT_STATUS_IS_OK(status
)) {
509 DBG_WARNING("cldap_multi_netlogon(realm=%s, num_requests=%zu) "
510 "for count[%zu] - %s\n",
515 return NT_STATUS_NO_LOGON_SERVERS
;
518 for (i
= 0; i
< num_requests
; i
++) {
519 struct NETLOGON_SAM_LOGON_RESPONSE_EX
*cldap_reply
= NULL
;
520 char server
[INET6_ADDRSTRLEN
];
522 if (responses
[i
] == NULL
) {
526 print_sockaddr(server
, sizeof(server
), &req_sa_list
[i
]->u
.ss
);
528 if (responses
[i
]->ntver
!= NETLOGON_NT_VERSION_5EX
) {
529 DBG_NOTICE("realm=[%s] nt_version mismatch: 0x%08x for %s\n",
531 responses
[i
]->ntver
, server
);
535 cldap_reply
= &responses
[i
]->data
.nt5_ex
;
537 /* Returns ok only if it matches the correct server type */
538 ok
= ads_fill_cldap_reply(ads
,
540 &req_sa_list
[i
]->u
.ss
,
543 DBG_DEBUG("realm[%s]: selected %s => %s\n",
545 server
, cldap_reply
->pdc_dns_name
);
546 if (CHECK_DEBUGLVL(DBGLVL_DEBUG
)) {
547 NDR_PRINT_DEBUG(NETLOGON_SAM_LOGON_RESPONSE_EX
,
554 DBG_NOTICE("realm[%s] server %s %s - not usable\n",
556 server
, cldap_reply
->pdc_dns_name
);
557 if (CHECK_DEBUGLVL(DBGLVL_NOTICE
)) {
558 NDR_PRINT_DEBUG(NETLOGON_SAM_LOGON_RESPONSE_EX
,
561 add_failed_connection_entry(domain
, server
,
562 NT_STATUS_CLIENT_SERVER_PARAMETERS_INVALID
);
569 expired
= timeval_expired(&endtime
);
575 /* keep track of failures as all were not suitable */
576 for (i
= 0; i
< num_requests
; i
++) {
577 char server
[INET6_ADDRSTRLEN
];
579 print_sockaddr(server
, sizeof(server
), &req_sa_list
[i
]->u
.ss
);
581 add_failed_connection_entry(domain
, server
,
582 NT_STATUS_UNSUCCESSFUL
);
585 status
= NT_STATUS_NO_LOGON_SERVERS
;
586 DBG_WARNING("realm[%s] no valid response "
587 "num_requests[%zu] for count[%zu] - %s\n",
589 num_requests
, count
, nt_errstr(status
));
591 return NT_STATUS_NO_LOGON_SERVERS
;
594 /***************************************************************************
595 resolve a name and perform an "ldap ping" using NetBIOS and related methods
596 ****************************************************************************/
598 static NTSTATUS
resolve_and_ping_netbios(ADS_STRUCT
*ads
,
599 const char *domain
, const char *realm
)
603 struct samba_sockaddr
*sa_list
= NULL
;
606 DEBUG(6, ("resolve_and_ping_netbios: (cldap) looking for domain '%s'\n",
609 status
= get_sorted_dc_list(talloc_tos(),
615 if (!NT_STATUS_IS_OK(status
)) {
619 /* remove servers which are known to be dead based on
620 the corresponding DNS method */
622 for (i
= 0; i
< count
; ++i
) {
623 char server
[INET6_ADDRSTRLEN
];
625 print_sockaddr(server
, sizeof(server
), &sa_list
[i
].u
.ss
);
628 check_negative_conn_cache(realm
, server
))) {
629 /* Ensure we add the workgroup name for this
630 IP address as negative too. */
631 add_failed_connection_entry(
633 NT_STATUS_UNSUCCESSFUL
);
638 status
= cldap_ping_list(ads
, domain
, sa_list
, count
);
640 TALLOC_FREE(sa_list
);
646 /**********************************************************************
647 resolve a name and perform an "ldap ping" using DNS
648 **********************************************************************/
650 static NTSTATUS
resolve_and_ping_dns(ADS_STRUCT
*ads
, const char *sitename
,
654 struct samba_sockaddr
*sa_list
= NULL
;
657 DEBUG(6, ("resolve_and_ping_dns: (cldap) looking for realm '%s'\n",
660 status
= get_sorted_dc_list(talloc_tos(),
666 if (!NT_STATUS_IS_OK(status
)) {
667 TALLOC_FREE(sa_list
);
671 status
= cldap_ping_list(ads
, realm
, sa_list
, count
);
673 TALLOC_FREE(sa_list
);
678 /**********************************************************************
679 Try to find an AD dc using our internal name resolution routines
680 Try the realm first and then the workgroup name if netbios is not
682 **********************************************************************/
684 static NTSTATUS
ads_find_dc(ADS_STRUCT
*ads
)
686 const char *c_domain
= "";
688 bool use_own_domain
= False
;
689 char *sitename
= NULL
;
690 NTSTATUS status
= NT_STATUS_UNSUCCESSFUL
;
693 /* if the realm and workgroup are both empty, assume they are ours */
696 c_realm
= ads
->server
.realm
;
702 /* special case where no realm and no workgroup means our own */
703 if ( !ads
->server
.workgroup
|| !*ads
->server
.workgroup
) {
704 use_own_domain
= True
;
705 c_realm
= lp_realm();
709 if (!lp_disable_netbios()) {
710 if (use_own_domain
) {
711 c_domain
= lp_workgroup();
713 c_domain
= ads
->server
.workgroup
;
714 if (!*c_realm
&& (!c_domain
|| !*c_domain
)) {
715 c_domain
= lp_workgroup();
724 if (!*c_realm
&& !*c_domain
) {
725 DEBUG(0, ("ads_find_dc: no realm or workgroup! Don't know "
727 return NT_STATUS_INVALID_PARAMETER
; /* rather need MISSING_PARAMETER ... */
731 * In case of LDAP we use get_dc_name() as that
732 * creates the custom krb5.conf file
734 if (ads
->auth
.flags
& ADS_AUTH_GENERATE_KRB5_CONFIG
) {
736 struct sockaddr_storage ip_out
;
738 DEBUG(6, ("ads_find_dc: (ldap) looking for realm '%s'"
739 " and falling back to domain '%s'\n",
742 ok
= get_dc_name(c_domain
, c_realm
, srv_name
, &ip_out
);
744 if (is_zero_addr(&ip_out
)) {
745 return NT_STATUS_NO_LOGON_SERVERS
;
749 * we call ads_try_connect() to fill in the
750 * ads->config details
752 ok
= ads_try_connect(ads
, false, &ip_out
);
758 return NT_STATUS_NO_LOGON_SERVERS
;
762 sitename
= sitename_fetch(talloc_tos(), c_realm
);
763 status
= resolve_and_ping_dns(ads
, sitename
, c_realm
);
765 if (NT_STATUS_IS_OK(status
)) {
766 TALLOC_FREE(sitename
);
770 /* In case we failed to contact one of our closest DC on our
772 * need to try to find another DC, retry with a site-less SRV
777 DEBUG(3, ("ads_find_dc: failed to find a valid DC on "
778 "our site (%s), Trying to find another DC "
779 "for realm '%s' (domain '%s')\n",
780 sitename
, c_realm
, c_domain
));
781 namecache_delete(c_realm
, 0x1C);
783 resolve_and_ping_dns(ads
, NULL
, c_realm
);
785 if (NT_STATUS_IS_OK(status
)) {
786 TALLOC_FREE(sitename
);
791 TALLOC_FREE(sitename
);
794 /* try netbios as fallback - if permitted,
795 or if configuration specifically requests it */
798 DEBUG(3, ("ads_find_dc: falling back to netbios "
799 "name resolution for domain '%s' (realm '%s')\n",
803 status
= resolve_and_ping_netbios(ads
, c_domain
, c_realm
);
804 if (NT_STATUS_IS_OK(status
)) {
809 DEBUG(1, ("ads_find_dc: "
810 "name resolution for realm '%s' (domain '%s') failed: %s\n",
811 c_realm
, c_domain
, nt_errstr(status
)));
816 * Connect to the LDAP server
817 * @param ads Pointer to an existing ADS_STRUCT
818 * @return status of connection
820 static ADS_STATUS
ads_connect_internal(ADS_STRUCT
*ads
,
821 struct cli_credentials
*creds
)
823 int version
= LDAP_VERSION3
;
826 char addr
[INET6_ADDRSTRLEN
];
827 struct sockaddr_storage existing_ss
;
829 bool start_tls
= false;
831 zero_sockaddr(&existing_ss
);
833 if (!(ads
->auth
.flags
& ADS_AUTH_NO_BIND
)) {
834 SMB_ASSERT(creds
!= NULL
);
837 if (ads
->auth
.flags
& ADS_AUTH_ANON_BIND
) {
839 * Simple anonyous binds are only
840 * allowed for anonymous credentials
842 SMB_ASSERT(cli_credentials_is_anonymous(creds
));
845 if (!(ads
->auth
.flags
& (ADS_AUTH_NO_BIND
|ADS_AUTH_ANON_BIND
))) {
846 ads
->auth
.flags
|= ADS_AUTH_GENERATE_KRB5_CONFIG
;
850 * ads_connect can be passed in a reused ADS_STRUCT
851 * with an existing non-zero ads->ldap.ss IP address
852 * that was stored by going through ads_find_dc()
853 * if ads->server.ldap_server was NULL.
855 * If ads->server.ldap_server is still NULL but
856 * the target address isn't the zero address, then
857 * store that address off off before zeroing out
858 * ads->ldap so we don't keep doing multiple calls
859 * to ads_find_dc() in the reuse case.
861 * If a caller wants a clean ADS_STRUCT they
862 * will TALLOC_FREE it and allocate a new one
863 * by calling ads_init(), which ensures
864 * ads->ldap.ss is a properly zero'ed out valid IP
867 if (ads
->server
.ldap_server
== NULL
&& !is_zero_addr(&ads
->ldap
.ss
)) {
868 /* Save off the address we previously found by ads_find_dc(). */
869 existing_ss
= ads
->ldap
.ss
;
873 ZERO_STRUCT(ads
->ldap_tls_data
);
874 ZERO_STRUCT(ads
->ldap_wrap_data
);
875 ads
->ldap
.last_attempt
= time_mono(NULL
);
876 ads
->ldap_wrap_data
.wrap_type
= ADS_SASLWRAP_TYPE_PLAIN
;
878 /* try with a user specified server */
880 if (DEBUGLEVEL
>= 11) {
881 char *s
= NDR_PRINT_STRUCT_STRING(talloc_tos(), ads_struct
, ads
);
882 DEBUG(11,("ads_connect: entering\n"));
883 DEBUGADD(11,("%s\n", s
));
887 if (ads
->server
.ldap_server
) {
889 struct sockaddr_storage ss
;
891 DBG_DEBUG("Resolving name of LDAP server '%s'.\n",
892 ads
->server
.ldap_server
);
893 ok
= resolve_name(ads
->server
.ldap_server
, &ss
, 0x20, true);
895 DEBUG(5,("ads_connect: unable to resolve name %s\n",
896 ads
->server
.ldap_server
));
897 status
= ADS_ERROR_NT(NT_STATUS_NOT_FOUND
);
901 if (is_zero_addr(&ss
)) {
902 status
= ADS_ERROR_NT(NT_STATUS_NOT_FOUND
);
906 ok
= ads_try_connect(ads
, ads
->server
.gc
, &ss
);
911 /* The choice of which GC use is handled one level up in
912 ads_connect_gc(). If we continue on from here with
913 ads_find_dc() we will get GC searches on port 389 which
914 doesn't work. --jerry */
916 if (ads
->server
.gc
== true) {
917 return ADS_ERROR(LDAP_OPERATIONS_ERROR
);
920 if (ads
->server
.no_fallback
) {
921 status
= ADS_ERROR_NT(NT_STATUS_NOT_FOUND
);
926 if (!is_zero_addr(&existing_ss
)) {
927 /* We saved off who we should talk to. */
928 bool ok
= ads_try_connect(ads
,
935 * Keep trying to find a server and fall through
936 * into ads_find_dc() again.
938 DBG_DEBUG("Failed to connect to DC via LDAP server IP address, "
939 "trying to find another DC.\n");
942 ntstatus
= ads_find_dc(ads
);
943 if (NT_STATUS_IS_OK(ntstatus
)) {
947 status
= ADS_ERROR_NT(ntstatus
);
952 print_sockaddr(addr
, sizeof(addr
), &ads
->ldap
.ss
);
953 DEBUG(3,("Successfully contacted LDAP server %s\n", addr
));
955 if (!ads
->auth
.kdc_server
) {
956 print_sockaddr(addr
, sizeof(addr
), &ads
->ldap
.ss
);
957 ads
->auth
.kdc_server
= talloc_strdup(ads
, addr
);
958 if (ads
->auth
.kdc_server
== NULL
) {
959 status
= ADS_ERROR_NT(NT_STATUS_NO_MEMORY
);
964 /* If the caller() requested no LDAP bind, then we are done */
966 if (ads
->auth
.flags
& ADS_AUTH_NO_BIND
) {
967 status
= ADS_SUCCESS
;
971 ads
->ldap_tls_data
.mem_ctx
= talloc_init("ads LDAP TLS connection memory");
972 if (!ads
->ldap_tls_data
.mem_ctx
) {
973 status
= ADS_ERROR_NT(NT_STATUS_NO_MEMORY
);
977 ads
->ldap_wrap_data
.mem_ctx
= talloc_init("ads LDAP connection memory");
978 if (!ads
->ldap_wrap_data
.mem_ctx
) {
979 status
= ADS_ERROR_NT(NT_STATUS_NO_MEMORY
);
983 /* Otherwise setup the TCP LDAP session */
985 if (ads
->auth
.flags
& ADS_AUTH_SASL_LDAPS
) {
987 ads
->ldap
.port
= 636;
988 } else if (ads
->auth
.flags
& ADS_AUTH_SASL_STARTTLS
) {
991 ads
->ldap
.port
= 389;
993 ads
->ldap
.port
= 389;
996 ads
->ldap
.ld
= ldap_open_with_timeout(ads
->config
.ldap_server_name
,
998 ads
->ldap
.port
, lp_ldap_timeout());
999 if (ads
->ldap
.ld
== NULL
) {
1000 status
= ADS_ERROR(LDAP_OPERATIONS_ERROR
);
1003 DEBUG(3,("Connected to LDAP server %s\n", ads
->config
.ldap_server_name
));
1005 ldap_set_option(ads
->ldap
.ld
, LDAP_OPT_PROTOCOL_VERSION
, &version
);
1008 unsigned int to
= lp_ldap_connection_timeout();
1009 struct berval
*rspdata
= NULL
;
1010 char *rspoid
= NULL
;
1016 CatchSignal(SIGALRM
, gotalarm_sig
);
1018 /* End setup timeout. */
1021 rc
= ldap_extended_operation_s(ads
->ldap
.ld
,
1022 LDAP_EXOP_START_TLS
,
1028 if (gotalarm
!= 0 && rc
== LDAP_SUCCESS
) {
1033 /* Teardown timeout. */
1035 CatchSignal(SIGALRM
, SIG_IGN
);
1038 if (rspoid
!= NULL
) {
1039 ldap_memfree(rspoid
);
1042 if (rspdata
!= NULL
) {
1043 ber_bvfree(rspdata
);
1046 if (rc
!= LDAP_SUCCESS
) {
1047 status
= ADS_ERROR_LDAP(rc
);
1053 unsigned int to
= lp_ldap_connection_timeout();
1058 CatchSignal(SIGALRM
, gotalarm_sig
);
1060 /* End setup timeout. */
1063 status
= ads_setup_tls_wrapping(&ads
->ldap_tls_data
,
1065 ads
->config
.ldap_server_name
);
1068 /* Teardown timeout. */
1070 CatchSignal(SIGALRM
, SIG_IGN
);
1073 if ( !ADS_ERR_OK(status
) ) {
1078 /* cache the successful connection for workgroup and realm */
1079 if (ads_closest_dc(ads
)) {
1080 saf_store( ads
->server
.workgroup
, ads
->config
.ldap_server_name
);
1081 saf_store( ads
->server
.realm
, ads
->config
.ldap_server_name
);
1084 /* fill in the current time and offsets */
1086 status
= ads_current_time( ads
);
1087 if ( !ADS_ERR_OK(status
) ) {
1091 /* Now do the bind */
1093 if (ads
->auth
.flags
& ADS_AUTH_ANON_BIND
) {
1094 status
= ADS_ERROR(ldap_simple_bind_s(ads
->ldap
.ld
, NULL
, NULL
));
1098 status
= ads_sasl_bind(ads
, creds
);
1101 if (DEBUGLEVEL
>= 11) {
1102 char *s
= NDR_PRINT_STRUCT_STRING(talloc_tos(), ads_struct
, ads
);
1103 DEBUG(11,("ads_connect: leaving with: %s\n",
1104 ads_errstr(status
)));
1105 DEBUGADD(11,("%s\n", s
));
1113 * Connect to the LDAP server using without a bind
1114 * and without a tcp connection at all
1116 * @param ads Pointer to an existing ADS_STRUCT
1117 * @return status of connection
1119 ADS_STATUS
ads_connect_cldap_only(ADS_STRUCT
*ads
)
1121 ads
->auth
.flags
|= ADS_AUTH_NO_BIND
;
1122 return ads_connect_internal(ads
, NULL
);
1126 * Connect to the LDAP server
1127 * @param ads Pointer to an existing ADS_STRUCT
1128 * @return status of connection
1130 ADS_STATUS
ads_connect(ADS_STRUCT
*ads
)
1132 TALLOC_CTX
*frame
= talloc_stackframe();
1133 struct cli_credentials
*creds
= NULL
;
1137 ntstatus
= ads_legacy_creds(ads
, frame
, &creds
);
1138 if (!NT_STATUS_IS_OK(ntstatus
)) {
1140 return ADS_ERROR_NT(ntstatus
);
1143 status
= ads_connect_internal(ads
, creds
);
1149 * Connect to the LDAP server using given credentials
1150 * @param ads Pointer to an existing ADS_STRUCT
1151 * @return status of connection
1153 ADS_STATUS
ads_connect_user_creds(ADS_STRUCT
*ads
)
1155 ads
->auth
.flags
|= ADS_AUTH_USER_CREDS
;
1157 return ads_connect(ads
);
1161 * Zero out the internal ads->ldap struct and initialize the address to zero IP.
1162 * @param ads Pointer to an existing ADS_STRUCT
1164 * Sets the ads->ldap.ss to a valid
1165 * zero ip address that can be detected by
1166 * our is_zero_addr() function. Otherwise
1167 * it is left as AF_UNSPEC (0).
1169 void ads_zero_ldap(ADS_STRUCT
*ads
)
1171 ZERO_STRUCT(ads
->ldap
);
1173 * Initialize the sockaddr_storage so we can use
1174 * sockaddr test functions against it.
1176 zero_sockaddr(&ads
->ldap
.ss
);
1180 * Disconnect the LDAP server
1181 * @param ads Pointer to an existing ADS_STRUCT
1183 void ads_disconnect(ADS_STRUCT
*ads
)
1186 ldap_unbind(ads
->ldap
.ld
);
1187 ads
->ldap
.ld
= NULL
;
1189 if (ads
->ldap_tls_data
.mem_ctx
) {
1190 talloc_free(ads
->ldap_tls_data
.mem_ctx
);
1192 if (ads
->ldap_wrap_data
.wrap_ops
&&
1193 ads
->ldap_wrap_data
.wrap_ops
->disconnect
) {
1194 ads
->ldap_wrap_data
.wrap_ops
->disconnect(&ads
->ldap_wrap_data
);
1196 if (ads
->ldap_wrap_data
.mem_ctx
) {
1197 talloc_free(ads
->ldap_wrap_data
.mem_ctx
);
1200 ZERO_STRUCT(ads
->ldap_tls_data
);
1201 ZERO_STRUCT(ads
->ldap_wrap_data
);
1205 Duplicate a struct berval into talloc'ed memory
1207 static struct berval
*dup_berval(TALLOC_CTX
*ctx
, const struct berval
*in_val
)
1209 struct berval
*value
;
1211 if (!in_val
) return NULL
;
1213 value
= talloc_zero(ctx
, struct berval
);
1216 if (in_val
->bv_len
== 0) return value
;
1218 value
->bv_len
= in_val
->bv_len
;
1219 value
->bv_val
= (char *)talloc_memdup(ctx
, in_val
->bv_val
,
1225 Make a values list out of an array of (struct berval *)
1227 static struct berval
**ads_dup_values(TALLOC_CTX
*ctx
,
1228 const struct berval
**in_vals
)
1230 struct berval
**values
;
1233 if (!in_vals
) return NULL
;
1234 for (i
=0; in_vals
[i
]; i
++)
1235 ; /* count values */
1236 values
= talloc_zero_array(ctx
, struct berval
*, i
+1);
1237 if (!values
) return NULL
;
1239 for (i
=0; in_vals
[i
]; i
++) {
1240 values
[i
] = dup_berval(ctx
, in_vals
[i
]);
1246 UTF8-encode a values list out of an array of (char *)
1248 static char **ads_push_strvals(TALLOC_CTX
*ctx
, const char **in_vals
)
1254 if (!in_vals
) return NULL
;
1255 for (i
=0; in_vals
[i
]; i
++)
1256 ; /* count values */
1257 values
= talloc_zero_array(ctx
, char *, i
+1);
1258 if (!values
) return NULL
;
1260 for (i
=0; in_vals
[i
]; i
++) {
1261 if (!push_utf8_talloc(ctx
, &values
[i
], in_vals
[i
], &size
)) {
1262 TALLOC_FREE(values
);
1270 Pull a (char *) array out of a UTF8-encoded values list
1272 static char **ads_pull_strvals(TALLOC_CTX
*ctx
, const char **in_vals
)
1276 size_t converted_size
;
1278 if (!in_vals
) return NULL
;
1279 for (i
=0; in_vals
[i
]; i
++)
1280 ; /* count values */
1281 values
= talloc_zero_array(ctx
, char *, i
+1);
1282 if (!values
) return NULL
;
1284 for (i
=0; in_vals
[i
]; i
++) {
1285 if (!pull_utf8_talloc(ctx
, &values
[i
], in_vals
[i
],
1287 DEBUG(0,("ads_pull_strvals: pull_utf8_talloc failed: "
1288 "%s\n", strerror(errno
)));
1295 * Do a search with paged results. cookie must be null on the first
1296 * call, and then returned on each subsequent call. It will be null
1297 * again when the entire search is complete
1298 * @param ads connection to ads server
1299 * @param bind_path Base dn for the search
1300 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1301 * @param expr Search expression - specified in local charset
1302 * @param attrs Attributes to retrieve - specified in utf8 or ascii
1303 * @param res ** which will contain results - free res* with ads_msgfree()
1304 * @param count Number of entries retrieved on this page
1305 * @param cookie The paged results cookie to be returned on subsequent calls
1306 * @return status of search
1308 static ADS_STATUS
ads_do_paged_search_args(ADS_STRUCT
*ads
,
1309 const char *bind_path
,
1310 int scope
, const char *expr
,
1311 const char **attrs
, void *args
,
1313 int *count
, struct berval
**cookie
)
1316 char *utf8_expr
, *utf8_path
, **search_attrs
= NULL
;
1317 size_t converted_size
;
1318 LDAPControl PagedResults
, NoReferrals
, ExternalCtrl
, *controls
[4], **rcontrols
;
1319 BerElement
*cookie_be
= NULL
;
1320 struct berval
*cookie_bv
= NULL
;
1321 BerElement
*ext_be
= NULL
;
1322 struct berval
*ext_bv
= NULL
;
1325 ads_control
*external_control
= (ads_control
*) args
;
1329 if (!(ctx
= talloc_init("ads_do_paged_search_args")))
1330 return ADS_ERROR(LDAP_NO_MEMORY
);
1332 /* 0 means the conversion worked but the result was empty
1333 so we only fail if it's -1. In any case, it always
1334 at least nulls out the dest */
1335 if (!push_utf8_talloc(ctx
, &utf8_expr
, expr
, &converted_size
) ||
1336 !push_utf8_talloc(ctx
, &utf8_path
, bind_path
, &converted_size
))
1338 rc
= LDAP_NO_MEMORY
;
1342 if (!attrs
|| !(*attrs
))
1343 search_attrs
= NULL
;
1345 /* This would be the utf8-encoded version...*/
1346 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
1347 if (!(search_attrs
= str_list_copy(talloc_tos(), attrs
))) {
1348 rc
= LDAP_NO_MEMORY
;
1353 /* Paged results only available on ldap v3 or later */
1354 ldap_get_option(ads
->ldap
.ld
, LDAP_OPT_PROTOCOL_VERSION
, &version
);
1355 if (version
< LDAP_VERSION3
) {
1356 rc
= LDAP_NOT_SUPPORTED
;
1360 cookie_be
= ber_alloc_t(LBER_USE_DER
);
1362 ber_printf(cookie_be
, "{iO}", (ber_int_t
) ads
->config
.ldap_page_size
, *cookie
);
1363 ber_bvfree(*cookie
); /* don't need it from last time */
1366 ber_printf(cookie_be
, "{io}", (ber_int_t
) ads
->config
.ldap_page_size
, "", 0);
1368 ber_flatten(cookie_be
, &cookie_bv
);
1369 PagedResults
.ldctl_oid
= discard_const_p(char, ADS_PAGE_CTL_OID
);
1370 PagedResults
.ldctl_iscritical
= (char) 1;
1371 PagedResults
.ldctl_value
.bv_len
= cookie_bv
->bv_len
;
1372 PagedResults
.ldctl_value
.bv_val
= cookie_bv
->bv_val
;
1374 NoReferrals
.ldctl_oid
= discard_const_p(char, ADS_NO_REFERRALS_OID
);
1375 NoReferrals
.ldctl_iscritical
= (char) 0;
1376 NoReferrals
.ldctl_value
.bv_len
= 0;
1377 NoReferrals
.ldctl_value
.bv_val
= discard_const_p(char, "");
1379 if (external_control
&&
1380 (strequal(external_control
->control
, ADS_EXTENDED_DN_OID
) ||
1381 strequal(external_control
->control
, ADS_SD_FLAGS_OID
))) {
1383 ExternalCtrl
.ldctl_oid
= discard_const_p(char, external_control
->control
);
1384 ExternalCtrl
.ldctl_iscritical
= (char) external_control
->critical
;
1386 /* win2k does not accept a ldctl_value being passed in */
1388 if (external_control
->val
!= 0) {
1390 if ((ext_be
= ber_alloc_t(LBER_USE_DER
)) == NULL
) {
1391 rc
= LDAP_NO_MEMORY
;
1395 if ((ber_printf(ext_be
, "{i}", (ber_int_t
) external_control
->val
)) == -1) {
1396 rc
= LDAP_NO_MEMORY
;
1399 if ((ber_flatten(ext_be
, &ext_bv
)) == -1) {
1400 rc
= LDAP_NO_MEMORY
;
1404 ExternalCtrl
.ldctl_value
.bv_len
= ext_bv
->bv_len
;
1405 ExternalCtrl
.ldctl_value
.bv_val
= ext_bv
->bv_val
;
1408 ExternalCtrl
.ldctl_value
.bv_len
= 0;
1409 ExternalCtrl
.ldctl_value
.bv_val
= NULL
;
1412 controls
[0] = &NoReferrals
;
1413 controls
[1] = &PagedResults
;
1414 controls
[2] = &ExternalCtrl
;
1418 controls
[0] = &NoReferrals
;
1419 controls
[1] = &PagedResults
;
1423 /* we need to disable referrals as the openldap libs don't
1424 handle them and paged results at the same time. Using them
1425 together results in the result record containing the server
1426 page control being removed from the result list (tridge/jmcd)
1428 leaving this in despite the control that says don't generate
1429 referrals, in case the server doesn't support it (jmcd)
1431 ldap_set_option(ads
->ldap
.ld
, LDAP_OPT_REFERRALS
, LDAP_OPT_OFF
);
1433 rc
= ldap_search_with_timeout(ads
->ldap
.ld
, utf8_path
, scope
, utf8_expr
,
1434 search_attrs
, 0, controls
,
1435 NULL
, LDAP_NO_LIMIT
,
1436 (LDAPMessage
**)res
);
1438 ber_free(cookie_be
, 1);
1439 ber_bvfree(cookie_bv
);
1442 DEBUG(3,("ads_do_paged_search_args: ldap_search_with_timeout(%s) -> %s\n", expr
,
1443 ldap_err2string(rc
)));
1444 if (rc
== LDAP_OTHER
) {
1448 ret
= ldap_parse_result(ads
->ldap
.ld
,
1456 if (ret
== LDAP_SUCCESS
) {
1457 DEBUG(3, ("ldap_search_with_timeout(%s) "
1458 "error: %s\n", expr
, ldap_errmsg
));
1459 ldap_memfree(ldap_errmsg
);
1465 rc
= ldap_parse_result(ads
->ldap
.ld
, *res
, NULL
, NULL
, NULL
,
1466 NULL
, &rcontrols
, 0);
1472 for (i
=0; rcontrols
[i
]; i
++) {
1473 if (strcmp(ADS_PAGE_CTL_OID
, rcontrols
[i
]->ldctl_oid
) == 0) {
1474 cookie_be
= ber_init(&rcontrols
[i
]->ldctl_value
);
1475 ber_scanf(cookie_be
,"{iO}", (ber_int_t
*) count
,
1477 /* the berval is the cookie, but must be freed when
1479 if (cookie_bv
->bv_len
) /* still more to do */
1480 *cookie
=ber_bvdup(cookie_bv
);
1483 ber_bvfree(cookie_bv
);
1484 ber_free(cookie_be
, 1);
1488 ldap_controls_free(rcontrols
);
1491 talloc_destroy(ctx
);
1494 ber_free(ext_be
, 1);
1501 if (rc
!= LDAP_SUCCESS
&& *res
!= NULL
) {
1502 ads_msgfree(ads
, *res
);
1506 /* if/when we decide to utf8-encode attrs, take out this next line */
1507 TALLOC_FREE(search_attrs
);
1509 return ADS_ERROR(rc
);
1512 static ADS_STATUS
ads_do_paged_search(ADS_STRUCT
*ads
, const char *bind_path
,
1513 int scope
, const char *expr
,
1514 const char **attrs
, LDAPMessage
**res
,
1515 int *count
, struct berval
**cookie
)
1517 return ads_do_paged_search_args(ads
, bind_path
, scope
, expr
, attrs
, NULL
, res
, count
, cookie
);
1522 * Get all results for a search. This uses ads_do_paged_search() to return
1523 * all entries in a large search.
1524 * @param ads connection to ads server
1525 * @param bind_path Base dn for the search
1526 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1527 * @param expr Search expression
1528 * @param attrs Attributes to retrieve
1529 * @param res ** which will contain results - free res* with ads_msgfree()
1530 * @return status of search
1532 ADS_STATUS
ads_do_search_all_args(ADS_STRUCT
*ads
, const char *bind_path
,
1533 int scope
, const char *expr
,
1534 const char **attrs
, void *args
,
1537 struct berval
*cookie
= NULL
;
1542 status
= ads_do_paged_search_args(ads
, bind_path
, scope
, expr
, attrs
, args
, res
,
1545 if (!ADS_ERR_OK(status
))
1548 #ifdef HAVE_LDAP_ADD_RESULT_ENTRY
1550 LDAPMessage
*res2
= NULL
;
1551 LDAPMessage
*msg
, *next
;
1553 status
= ads_do_paged_search_args(ads
, bind_path
, scope
, expr
,
1554 attrs
, args
, &res2
, &count
, &cookie
);
1555 if (!ADS_ERR_OK(status
)) {
1559 /* this relies on the way that ldap_add_result_entry() works internally. I hope
1560 that this works on all ldap libs, but I have only tested with openldap */
1561 for (msg
= ads_first_message(ads
, res2
); msg
; msg
= next
) {
1562 next
= ads_next_message(ads
, msg
);
1563 ldap_add_result_entry((LDAPMessage
**)res
, msg
);
1565 /* note that we do not free res2, as the memory is now
1566 part of the main returned list */
1569 DEBUG(0, ("no ldap_add_result_entry() support in LDAP libs!\n"));
1570 status
= ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL
);
1576 ADS_STATUS
ads_do_search_all(ADS_STRUCT
*ads
, const char *bind_path
,
1577 int scope
, const char *expr
,
1578 const char **attrs
, LDAPMessage
**res
)
1580 return ads_do_search_all_args(ads
, bind_path
, scope
, expr
, attrs
, NULL
, res
);
1583 ADS_STATUS
ads_do_search_all_sd_flags(ADS_STRUCT
*ads
, const char *bind_path
,
1584 int scope
, const char *expr
,
1585 const char **attrs
, uint32_t sd_flags
,
1590 args
.control
= ADS_SD_FLAGS_OID
;
1591 args
.val
= sd_flags
;
1592 args
.critical
= True
;
1594 return ads_do_search_all_args(ads
, bind_path
, scope
, expr
, attrs
, &args
, res
);
1599 * Run a function on all results for a search. Uses ads_do_paged_search() and
1600 * runs the function as each page is returned, using ads_process_results()
1601 * @param ads connection to ads server
1602 * @param bind_path Base dn for the search
1603 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1604 * @param expr Search expression - specified in local charset
1605 * @param attrs Attributes to retrieve - specified in UTF-8 or ascii
1606 * @param fn Function which takes attr name, values list, and data_area
1607 * @param data_area Pointer which is passed to function on each call
1608 * @return status of search
1610 ADS_STATUS
ads_do_search_all_fn(ADS_STRUCT
*ads
, const char *bind_path
,
1611 int scope
, const char *expr
, const char **attrs
,
1612 bool (*fn
)(ADS_STRUCT
*, char *, void **, void *),
1615 struct berval
*cookie
= NULL
;
1620 status
= ads_do_paged_search(ads
, bind_path
, scope
, expr
, attrs
, &res
,
1623 if (!ADS_ERR_OK(status
)) return status
;
1625 ads_process_results(ads
, res
, fn
, data_area
);
1626 ads_msgfree(ads
, res
);
1629 status
= ads_do_paged_search(ads
, bind_path
, scope
, expr
, attrs
,
1630 &res
, &count
, &cookie
);
1632 if (!ADS_ERR_OK(status
)) break;
1634 ads_process_results(ads
, res
, fn
, data_area
);
1635 ads_msgfree(ads
, res
);
1642 * Do a search with a timeout.
1643 * @param ads connection to ads server
1644 * @param bind_path Base dn for the search
1645 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1646 * @param expr Search expression
1647 * @param attrs Attributes to retrieve
1648 * @param res ** which will contain results - free res* with ads_msgfree()
1649 * @return status of search
1651 ADS_STATUS
ads_do_search(ADS_STRUCT
*ads
, const char *bind_path
, int scope
,
1653 const char **attrs
, LDAPMessage
**res
)
1656 char *utf8_expr
, *utf8_path
, **search_attrs
= NULL
;
1657 size_t converted_size
;
1661 if (!(ctx
= talloc_init("ads_do_search"))) {
1662 DEBUG(1,("ads_do_search: talloc_init() failed!\n"));
1663 return ADS_ERROR(LDAP_NO_MEMORY
);
1666 /* 0 means the conversion worked but the result was empty
1667 so we only fail if it's negative. In any case, it always
1668 at least nulls out the dest */
1669 if (!push_utf8_talloc(ctx
, &utf8_expr
, expr
, &converted_size
) ||
1670 !push_utf8_talloc(ctx
, &utf8_path
, bind_path
, &converted_size
))
1672 DEBUG(1,("ads_do_search: push_utf8_talloc() failed!\n"));
1673 rc
= LDAP_NO_MEMORY
;
1677 if (!attrs
|| !(*attrs
))
1678 search_attrs
= NULL
;
1680 /* This would be the utf8-encoded version...*/
1681 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
1682 if (!(search_attrs
= str_list_copy(talloc_tos(), attrs
)))
1684 DEBUG(1,("ads_do_search: str_list_copy() failed!\n"));
1685 rc
= LDAP_NO_MEMORY
;
1690 /* see the note in ads_do_paged_search - we *must* disable referrals */
1691 ldap_set_option(ads
->ldap
.ld
, LDAP_OPT_REFERRALS
, LDAP_OPT_OFF
);
1693 rc
= ldap_search_with_timeout(ads
->ldap
.ld
, utf8_path
, scope
, utf8_expr
,
1694 search_attrs
, 0, NULL
, NULL
,
1696 (LDAPMessage
**)res
);
1698 if (rc
== LDAP_SIZELIMIT_EXCEEDED
) {
1699 DEBUG(3,("Warning! sizelimit exceeded in ldap. Truncating.\n"));
1704 talloc_destroy(ctx
);
1705 /* if/when we decide to utf8-encode attrs, take out this next line */
1706 TALLOC_FREE(search_attrs
);
1707 return ADS_ERROR(rc
);
1710 * Do a general ADS search
1711 * @param ads connection to ads server
1712 * @param res ** which will contain results - free res* with ads_msgfree()
1713 * @param expr Search expression
1714 * @param attrs Attributes to retrieve
1715 * @return status of search
1717 ADS_STATUS
ads_search(ADS_STRUCT
*ads
, LDAPMessage
**res
,
1718 const char *expr
, const char **attrs
)
1720 return ads_do_search(ads
, ads
->config
.bind_path
, LDAP_SCOPE_SUBTREE
,
1725 * Do a search on a specific DistinguishedName
1726 * @param ads connection to ads server
1727 * @param res ** which will contain results - free res* with ads_msgfree()
1728 * @param dn DistinguishedName to search
1729 * @param attrs Attributes to retrieve
1730 * @return status of search
1732 ADS_STATUS
ads_search_dn(ADS_STRUCT
*ads
, LDAPMessage
**res
,
1733 const char *dn
, const char **attrs
)
1735 return ads_do_search(ads
, dn
, LDAP_SCOPE_BASE
, "(objectclass=*)",
1740 * Free up memory from a ads_search
1741 * @param ads connection to ads server
1742 * @param msg Search results to free
1744 void ads_msgfree(ADS_STRUCT
*ads
, LDAPMessage
*msg
)
1751 * Get a dn from search results
1752 * @param ads connection to ads server
1753 * @param msg Search result
1756 char *ads_get_dn(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
, LDAPMessage
*msg
)
1758 char *utf8_dn
, *unix_dn
;
1759 size_t converted_size
;
1761 utf8_dn
= ldap_get_dn(ads
->ldap
.ld
, msg
);
1764 DEBUG (5, ("ads_get_dn: ldap_get_dn failed\n"));
1768 if (!pull_utf8_talloc(mem_ctx
, &unix_dn
, utf8_dn
, &converted_size
)) {
1769 DEBUG(0,("ads_get_dn: string conversion failure utf8 [%s]\n",
1773 ldap_memfree(utf8_dn
);
1778 * Get the parent from a dn
1779 * @param dn the dn to return the parent from
1780 * @return parent dn string
1782 char *ads_parent_dn(const char *dn
)
1790 p
= strchr(dn
, ',');
1800 * Find a machine account given a hostname
1801 * @param ads connection to ads server
1802 * @param res ** which will contain results - free res* with ads_msgfree()
1803 * @param host Hostname to search for
1804 * @return status of search
1806 ADS_STATUS
ads_find_machine_acct(ADS_STRUCT
*ads
, LDAPMessage
**res
,
1807 const char *machine
)
1811 const char *attrs
[] = {
1812 /* This is how Windows checks for machine accounts */
1815 "userAccountControl",
1817 "ServicePrincipalName",
1818 "userPrincipalName",
1820 /* Additional attributes Samba checks */
1821 "msDS-AdditionalDnsHostName",
1822 "msDS-SupportedEncryptionTypes",
1823 "nTSecurityDescriptor",
1828 TALLOC_CTX
*frame
= talloc_stackframe();
1832 /* the easiest way to find a machine account anywhere in the tree
1833 is to look for hostname$ */
1834 expr
= talloc_asprintf(frame
, "(samAccountName=%s$)", machine
);
1836 status
= ADS_ERROR_NT(NT_STATUS_NO_MEMORY
);
1840 status
= ads_search(ads
, res
, expr
, attrs
);
1841 if (ADS_ERR_OK(status
)) {
1842 if (ads_count_replies(ads
, *res
) != 1) {
1843 status
= ADS_ERROR_LDAP(LDAP_NO_SUCH_OBJECT
);
1853 * Initialize a list of mods to be used in a modify request
1854 * @param ctx An initialized TALLOC_CTX
1855 * @return allocated ADS_MODLIST
1857 ADS_MODLIST
ads_init_mods(TALLOC_CTX
*ctx
)
1859 #define ADS_MODLIST_ALLOC_SIZE 10
1862 if ((mods
= talloc_zero_array(ctx
, LDAPMod
*, ADS_MODLIST_ALLOC_SIZE
+ 1)))
1863 /* -1 is safety to make sure we don't go over the end.
1864 need to reset it to NULL before doing ldap modify */
1865 mods
[ADS_MODLIST_ALLOC_SIZE
] = (LDAPMod
*) -1;
1867 return (ADS_MODLIST
)mods
;
1872 add an attribute to the list, with values list already constructed
1874 static ADS_STATUS
ads_modlist_add(TALLOC_CTX
*ctx
, ADS_MODLIST
*mods
,
1875 int mod_op
, const char *name
,
1876 const void *_invals
)
1879 LDAPMod
**modlist
= (LDAPMod
**) *mods
;
1880 struct berval
**ber_values
= NULL
;
1881 char **char_values
= NULL
;
1884 mod_op
= LDAP_MOD_DELETE
;
1886 if (mod_op
& LDAP_MOD_BVALUES
) {
1887 const struct berval
**b
;
1888 b
= discard_const_p(const struct berval
*, _invals
);
1889 ber_values
= ads_dup_values(ctx
, b
);
1892 c
= discard_const_p(const char *, _invals
);
1893 char_values
= ads_push_strvals(ctx
, c
);
1897 /* find the first empty slot */
1898 for (curmod
=0; modlist
[curmod
] && modlist
[curmod
] != (LDAPMod
*) -1;
1900 if (modlist
[curmod
] == (LDAPMod
*) -1) {
1901 if (!(modlist
= talloc_realloc(ctx
, modlist
, LDAPMod
*,
1902 curmod
+ADS_MODLIST_ALLOC_SIZE
+1)))
1903 return ADS_ERROR(LDAP_NO_MEMORY
);
1904 memset(&modlist
[curmod
], 0,
1905 ADS_MODLIST_ALLOC_SIZE
*sizeof(LDAPMod
*));
1906 modlist
[curmod
+ADS_MODLIST_ALLOC_SIZE
] = (LDAPMod
*) -1;
1907 *mods
= (ADS_MODLIST
)modlist
;
1910 if (!(modlist
[curmod
] = talloc_zero(ctx
, LDAPMod
)))
1911 return ADS_ERROR(LDAP_NO_MEMORY
);
1912 modlist
[curmod
]->mod_type
= talloc_strdup(ctx
, name
);
1913 if (mod_op
& LDAP_MOD_BVALUES
) {
1914 modlist
[curmod
]->mod_bvalues
= ber_values
;
1915 } else if (mod_op
& LDAP_MOD_DELETE
) {
1916 modlist
[curmod
]->mod_values
= NULL
;
1918 modlist
[curmod
]->mod_values
= char_values
;
1921 modlist
[curmod
]->mod_op
= mod_op
;
1922 return ADS_ERROR(LDAP_SUCCESS
);
1926 * Add a single string value to a mod list
1927 * @param ctx An initialized TALLOC_CTX
1928 * @param mods An initialized ADS_MODLIST
1929 * @param name The attribute name to add
1930 * @param val The value to add - NULL means DELETE
1931 * @return ADS STATUS indicating success of add
1933 ADS_STATUS
ads_mod_str(TALLOC_CTX
*ctx
, ADS_MODLIST
*mods
,
1934 const char *name
, const char *val
)
1936 const char *values
[2];
1942 return ads_modlist_add(ctx
, mods
, LDAP_MOD_DELETE
, name
, NULL
);
1943 return ads_modlist_add(ctx
, mods
, LDAP_MOD_REPLACE
, name
, values
);
1947 * Add an array of string values to a mod list
1948 * @param ctx An initialized TALLOC_CTX
1949 * @param mods An initialized ADS_MODLIST
1950 * @param name The attribute name to add
1951 * @param vals The array of string values to add - NULL means DELETE
1952 * @return ADS STATUS indicating success of add
1954 ADS_STATUS
ads_mod_strlist(TALLOC_CTX
*ctx
, ADS_MODLIST
*mods
,
1955 const char *name
, const char **vals
)
1958 return ads_modlist_add(ctx
, mods
, LDAP_MOD_DELETE
, name
, NULL
);
1959 return ads_modlist_add(ctx
, mods
, LDAP_MOD_REPLACE
,
1960 name
, (const void **) vals
);
1964 * Add a single ber-encoded value to a mod list
1965 * @param ctx An initialized TALLOC_CTX
1966 * @param mods An initialized ADS_MODLIST
1967 * @param name The attribute name to add
1968 * @param val The value to add - NULL means DELETE
1969 * @return ADS STATUS indicating success of add
1971 static ADS_STATUS
ads_mod_ber(TALLOC_CTX
*ctx
, ADS_MODLIST
*mods
,
1972 const char *name
, const struct berval
*val
)
1974 const struct berval
*values
[2];
1979 return ads_modlist_add(ctx
, mods
, LDAP_MOD_DELETE
, name
, NULL
);
1980 return ads_modlist_add(ctx
, mods
, LDAP_MOD_REPLACE
|LDAP_MOD_BVALUES
,
1981 name
, (const void **) values
);
1984 static void ads_print_error(int ret
, LDAP
*ld
)
1987 char *ld_error
= NULL
;
1988 ldap_get_option(ld
, LDAP_OPT_ERROR_STRING
, &ld_error
);
1989 DBG_ERR("AD LDAP ERROR: %d (%s): %s\n",
1991 ldap_err2string(ret
),
1993 SAFE_FREE(ld_error
);
1998 * Perform an ldap modify
1999 * @param ads connection to ads server
2000 * @param mod_dn DistinguishedName to modify
2001 * @param mods list of modifications to perform
2002 * @return status of modify
2004 ADS_STATUS
ads_gen_mod(ADS_STRUCT
*ads
, const char *mod_dn
, ADS_MODLIST mods
)
2007 char *utf8_dn
= NULL
;
2008 size_t converted_size
;
2010 this control is needed to modify that contains a currently
2011 non-existent attribute (but allowable for the object) to run
2013 LDAPControl PermitModify
= {
2014 discard_const_p(char, ADS_PERMIT_MODIFY_OID
),
2017 LDAPControl
*controls
[2];
2019 DBG_INFO("AD LDAP: Modifying %s\n", mod_dn
);
2021 controls
[0] = &PermitModify
;
2024 if (!push_utf8_talloc(talloc_tos(), &utf8_dn
, mod_dn
, &converted_size
)) {
2025 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY
);
2028 /* find the end of the list, marked by NULL or -1 */
2029 for(i
=0;(mods
[i
]!=0)&&(mods
[i
]!=(LDAPMod
*) -1);i
++);
2030 /* make sure the end of the list is NULL */
2032 ret
= ldap_modify_ext_s(ads
->ldap
.ld
, utf8_dn
,
2033 (LDAPMod
**) mods
, controls
, NULL
);
2034 ads_print_error(ret
, ads
->ldap
.ld
);
2035 TALLOC_FREE(utf8_dn
);
2036 return ADS_ERROR(ret
);
2040 * Perform an ldap add
2041 * @param ads connection to ads server
2042 * @param new_dn DistinguishedName to add
2043 * @param mods list of attributes and values for DN
2044 * @return status of add
2046 ADS_STATUS
ads_gen_add(ADS_STRUCT
*ads
, const char *new_dn
, ADS_MODLIST mods
)
2049 char *utf8_dn
= NULL
;
2050 size_t converted_size
;
2052 DBG_INFO("AD LDAP: Adding %s\n", new_dn
);
2054 if (!push_utf8_talloc(talloc_tos(), &utf8_dn
, new_dn
, &converted_size
)) {
2055 DEBUG(1, ("ads_gen_add: push_utf8_talloc failed!\n"));
2056 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY
);
2059 /* find the end of the list, marked by NULL or -1 */
2060 for(i
=0;(mods
[i
]!=0)&&(mods
[i
]!=(LDAPMod
*) -1);i
++);
2061 /* make sure the end of the list is NULL */
2064 ret
= ldap_add_ext_s(ads
->ldap
.ld
, utf8_dn
, (LDAPMod
**)mods
, NULL
, NULL
);
2065 ads_print_error(ret
, ads
->ldap
.ld
);
2066 TALLOC_FREE(utf8_dn
);
2067 return ADS_ERROR(ret
);
2071 * Delete a DistinguishedName
2072 * @param ads connection to ads server
2073 * @param new_dn DistinguishedName to delete
2074 * @return status of delete
2076 ADS_STATUS
ads_del_dn(ADS_STRUCT
*ads
, char *del_dn
)
2079 char *utf8_dn
= NULL
;
2080 size_t converted_size
;
2081 if (!push_utf8_talloc(talloc_tos(), &utf8_dn
, del_dn
, &converted_size
)) {
2082 DEBUG(1, ("ads_del_dn: push_utf8_talloc failed!\n"));
2083 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY
);
2086 DBG_INFO("AD LDAP: Deleting %s\n", del_dn
);
2088 ret
= ldap_delete_s(ads
->ldap
.ld
, utf8_dn
);
2089 ads_print_error(ret
, ads
->ldap
.ld
);
2090 TALLOC_FREE(utf8_dn
);
2091 return ADS_ERROR(ret
);
2095 * Build an org unit string
2096 * if org unit is Computers or blank then assume a container, otherwise
2097 * assume a / separated list of organisational units.
2098 * jmcd: '\' is now used for escapes so certain chars can be in the ou (e.g. #)
2099 * @param ads connection to ads server
2100 * @param org_unit Organizational unit
2101 * @return org unit string - caller must free
2103 char *ads_ou_string(ADS_STRUCT
*ads
, const char *org_unit
)
2109 if (!org_unit
|| !*org_unit
) {
2111 ret
= ads_default_ou_string(ads
, DS_GUID_COMPUTERS_CONTAINER
);
2113 /* samba4 might not yet respond to a wellknownobject-query */
2114 return ret
? ret
: SMB_STRDUP("cn=Computers");
2117 if (strequal(org_unit
, "Computers")) {
2118 return SMB_STRDUP("cn=Computers");
2121 /* jmcd: removed "\\" from the separation chars, because it is
2122 needed as an escape for chars like '#' which are valid in an
2124 status
= ads_build_path(org_unit
, "/", "ou=", 1, &dn
);
2125 if (!ADS_ERR_OK(status
)) {
2133 * Get a org unit string for a well-known GUID
2134 * @param ads connection to ads server
2135 * @param wknguid Well known GUID
2136 * @return org unit string - caller must free
2138 char *ads_default_ou_string(ADS_STRUCT
*ads
, const char *wknguid
)
2141 LDAPMessage
*res
= NULL
;
2142 char *base
, *wkn_dn
= NULL
, *ret
= NULL
, **wkn_dn_exp
= NULL
,
2143 **bind_dn_exp
= NULL
;
2144 const char *attrs
[] = {"distinguishedName", NULL
};
2145 int new_ln
, wkn_ln
, bind_ln
, i
;
2147 if (wknguid
== NULL
) {
2151 if (asprintf(&base
, "<WKGUID=%s,%s>", wknguid
, ads
->config
.bind_path
) == -1) {
2152 DEBUG(1, ("asprintf failed!\n"));
2156 status
= ads_search_dn(ads
, &res
, base
, attrs
);
2157 if (!ADS_ERR_OK(status
)) {
2158 DEBUG(1,("Failed while searching for: %s\n", base
));
2162 if (ads_count_replies(ads
, res
) != 1) {
2166 /* substitute the bind-path from the well-known-guid-search result */
2167 wkn_dn
= ads_get_dn(ads
, talloc_tos(), res
);
2172 wkn_dn_exp
= ldap_explode_dn(wkn_dn
, 0);
2177 bind_dn_exp
= ldap_explode_dn(ads
->config
.bind_path
, 0);
2182 for (wkn_ln
=0; wkn_dn_exp
[wkn_ln
]; wkn_ln
++)
2184 for (bind_ln
=0; bind_dn_exp
[bind_ln
]; bind_ln
++)
2187 new_ln
= wkn_ln
- bind_ln
;
2189 ret
= SMB_STRDUP(wkn_dn_exp
[0]);
2194 for (i
=1; i
< new_ln
; i
++) {
2197 if (asprintf(&s
, "%s,%s", ret
, wkn_dn_exp
[i
]) == -1) {
2203 ret
= SMB_STRDUP(s
);
2212 ads_msgfree(ads
, res
);
2213 TALLOC_FREE(wkn_dn
);
2215 ldap_value_free(wkn_dn_exp
);
2218 ldap_value_free(bind_dn_exp
);
2225 * Adds (appends) an item to an attribute array, rather then
2226 * replacing the whole list
2227 * @param ctx An initialized TALLOC_CTX
2228 * @param mods An initialized ADS_MODLIST
2229 * @param name name of the ldap attribute to append to
2230 * @param vals an array of values to add
2231 * @return status of addition
2234 ADS_STATUS
ads_add_strlist(TALLOC_CTX
*ctx
, ADS_MODLIST
*mods
,
2235 const char *name
, const char **vals
)
2237 return ads_modlist_add(ctx
, mods
, LDAP_MOD_ADD
, name
,
2238 (const void *) vals
);
2242 * Determines the an account's current KVNO via an LDAP lookup
2243 * @param ads An initialized ADS_STRUCT
2244 * @param account_name the NT samaccountname.
2245 * @return the kvno for the account, or -1 in case of a failure.
2248 uint32_t ads_get_kvno(ADS_STRUCT
*ads
, const char *account_name
)
2250 LDAPMessage
*res
= NULL
;
2251 uint32_t kvno
= (uint32_t)-1; /* -1 indicates a failure */
2253 const char *attrs
[] = {"msDS-KeyVersionNumber", NULL
};
2254 char *dn_string
= NULL
;
2257 DEBUG(5,("ads_get_kvno: Searching for account %s\n", account_name
));
2258 if (asprintf(&filter
, "(samAccountName=%s)", account_name
) == -1) {
2261 ret
= ads_search(ads
, &res
, filter
, attrs
);
2263 if (!ADS_ERR_OK(ret
) || (ads_count_replies(ads
, res
) != 1)) {
2264 DEBUG(1,("ads_get_kvno: Account for %s not found.\n", account_name
));
2265 ads_msgfree(ads
, res
);
2269 dn_string
= ads_get_dn(ads
, talloc_tos(), res
);
2271 DEBUG(0,("ads_get_kvno: out of memory.\n"));
2272 ads_msgfree(ads
, res
);
2275 DEBUG(5,("ads_get_kvno: Using: %s\n", dn_string
));
2276 TALLOC_FREE(dn_string
);
2278 /* ---------------------------------------------------------
2279 * 0 is returned as a default KVNO from this point on...
2280 * This is done because Windows 2000 does not support key
2281 * version numbers. Chances are that a failure in the next
2282 * step is simply due to Windows 2000 being used for a
2283 * domain controller. */
2286 if (!ads_pull_uint32(ads
, res
, "msDS-KeyVersionNumber", &kvno
)) {
2287 DEBUG(3,("ads_get_kvno: Error Determining KVNO!\n"));
2288 DEBUG(3,("ads_get_kvno: Windows 2000 does not support KVNO's, so this may be normal.\n"));
2289 ads_msgfree(ads
, res
);
2294 DEBUG(5,("ads_get_kvno: Looked Up KVNO of: %d\n", kvno
));
2295 ads_msgfree(ads
, res
);
2300 * Determines the computer account's current KVNO via an LDAP lookup
2301 * @param ads An initialized ADS_STRUCT
2302 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
2303 * @return the kvno for the computer account, or -1 in case of a failure.
2306 uint32_t ads_get_machine_kvno(ADS_STRUCT
*ads
, const char *machine_name
)
2308 char *computer_account
= NULL
;
2311 if (asprintf(&computer_account
, "%s$", machine_name
) < 0) {
2315 kvno
= ads_get_kvno(ads
, computer_account
);
2316 free(computer_account
);
2322 * This clears out all registered spn's for a given hostname
2323 * @param ads An initialized ADS_STRUCT
2324 * @param machine_name the NetBIOS name of the computer.
2325 * @return 0 upon success, non-zero otherwise.
2328 ADS_STATUS
ads_clear_service_principal_names(ADS_STRUCT
*ads
, const char *machine_name
)
2331 LDAPMessage
*res
= NULL
;
2333 const char *servicePrincipalName
[1] = {NULL
};
2335 char *dn_string
= NULL
;
2337 ret
= ads_find_machine_acct(ads
, &res
, machine_name
);
2338 if (!ADS_ERR_OK(ret
)) {
2339 DEBUG(5,("ads_clear_service_principal_names: WARNING: Host Account for %s not found... skipping operation.\n", machine_name
));
2340 DEBUG(5,("ads_clear_service_principal_names: WARNING: Service Principals for %s have NOT been cleared.\n", machine_name
));
2341 ads_msgfree(ads
, res
);
2345 DEBUG(5,("ads_clear_service_principal_names: Host account for %s found\n", machine_name
));
2346 ctx
= talloc_init("ads_clear_service_principal_names");
2348 ads_msgfree(ads
, res
);
2349 return ADS_ERROR(LDAP_NO_MEMORY
);
2352 if (!(mods
= ads_init_mods(ctx
))) {
2353 talloc_destroy(ctx
);
2354 ads_msgfree(ads
, res
);
2355 return ADS_ERROR(LDAP_NO_MEMORY
);
2357 ret
= ads_mod_strlist(ctx
, &mods
, "servicePrincipalName", servicePrincipalName
);
2358 if (!ADS_ERR_OK(ret
)) {
2359 DEBUG(1,("ads_clear_service_principal_names: Error creating strlist.\n"));
2360 ads_msgfree(ads
, res
);
2361 talloc_destroy(ctx
);
2364 dn_string
= ads_get_dn(ads
, talloc_tos(), res
);
2366 talloc_destroy(ctx
);
2367 ads_msgfree(ads
, res
);
2368 return ADS_ERROR(LDAP_NO_MEMORY
);
2370 ret
= ads_gen_mod(ads
, dn_string
, mods
);
2371 TALLOC_FREE(dn_string
);
2372 if (!ADS_ERR_OK(ret
)) {
2373 DEBUG(1,("ads_clear_service_principal_names: Error: Updating Service Principals for machine %s in LDAP\n",
2375 ads_msgfree(ads
, res
);
2376 talloc_destroy(ctx
);
2380 ads_msgfree(ads
, res
);
2381 talloc_destroy(ctx
);
2386 * @brief Search for an element in a string array.
2388 * @param[in] el_array The string array to search.
2390 * @param[in] num_el The number of elements in the string array.
2392 * @param[in] el The string to search.
2394 * @return True if found, false if not.
2396 bool ads_element_in_array(const char **el_array
, size_t num_el
, const char *el
)
2400 if (el_array
== NULL
|| num_el
== 0 || el
== NULL
) {
2404 for (i
= 0; i
< num_el
&& el_array
[i
] != NULL
; i
++) {
2407 cmp
= strcasecmp_m(el_array
[i
], el
);
2417 * @brief This gets the service principal names of an existing computer account.
2419 * @param[in] mem_ctx The memory context to use to allocate the spn array.
2421 * @param[in] ads The ADS context to use.
2423 * @param[in] machine_name The NetBIOS name of the computer, which is used to
2424 * identify the computer account.
2426 * @param[in] spn_array A pointer to store the array for SPNs.
2428 * @param[in] num_spns The number of principals stored in the array.
2430 * @return 0 on success, or a ADS error if a failure occurred.
2432 ADS_STATUS
ads_get_service_principal_names(TALLOC_CTX
*mem_ctx
,
2434 const char *machine_name
,
2439 LDAPMessage
*res
= NULL
;
2442 status
= ads_find_machine_acct(ads
,
2445 if (!ADS_ERR_OK(status
)) {
2446 DEBUG(1,("Host Account for %s not found... skipping operation.\n",
2451 count
= ads_count_replies(ads
, res
);
2453 status
= ADS_ERROR(LDAP_NO_SUCH_OBJECT
);
2457 *spn_array
= ads_pull_strings(ads
,
2460 "servicePrincipalName",
2462 if (*spn_array
== NULL
) {
2463 DEBUG(1, ("Host account for %s does not have service principal "
2466 status
= ADS_ERROR(LDAP_NO_SUCH_OBJECT
);
2471 ads_msgfree(ads
, res
);
2477 * This adds a service principal name to an existing computer account
2478 * (found by hostname) in AD.
2479 * @param ads An initialized ADS_STRUCT
2480 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
2481 * @param spns An array or strings for the service principals to add,
2482 * i.e. 'cifs/machine_name', 'http/machine.full.domain.com' etc.
2483 * @return 0 upon success, or non-zero if a failure occurs
2486 ADS_STATUS
ads_add_service_principal_names(ADS_STRUCT
*ads
,
2487 const char *machine_name
,
2492 LDAPMessage
*res
= NULL
;
2494 char *dn_string
= NULL
;
2495 const char **servicePrincipalName
= spns
;
2497 ret
= ads_find_machine_acct(ads
, &res
, machine_name
);
2498 if (!ADS_ERR_OK(ret
)) {
2499 DEBUG(1,("ads_add_service_principal_name: WARNING: Host Account for %s not found... skipping operation.\n",
2501 DEBUG(1,("ads_add_service_principal_name: WARNING: Service Principals have NOT been added.\n"));
2502 ads_msgfree(ads
, res
);
2506 DEBUG(1,("ads_add_service_principal_name: Host account for %s found\n", machine_name
));
2507 if (!(ctx
= talloc_init("ads_add_service_principal_name"))) {
2508 ads_msgfree(ads
, res
);
2509 return ADS_ERROR(LDAP_NO_MEMORY
);
2512 DEBUG(5,("ads_add_service_principal_name: INFO: "
2513 "Adding %s to host %s\n",
2514 spns
[0] ? "N/A" : spns
[0], machine_name
));
2517 DEBUG(5,("ads_add_service_principal_name: INFO: "
2518 "Adding %s to host %s\n",
2519 spns
[1] ? "N/A" : spns
[1], machine_name
));
2521 if ( (mods
= ads_init_mods(ctx
)) == NULL
) {
2522 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
2526 ret
= ads_add_strlist(ctx
,
2528 "servicePrincipalName",
2529 servicePrincipalName
);
2530 if (!ADS_ERR_OK(ret
)) {
2531 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
2535 if ( (dn_string
= ads_get_dn(ads
, ctx
, res
)) == NULL
) {
2536 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
2540 ret
= ads_gen_mod(ads
, dn_string
, mods
);
2541 if (!ADS_ERR_OK(ret
)) {
2542 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
2548 ads_msgfree(ads
, res
);
2552 static uint32_t ads_get_acct_ctrl(ADS_STRUCT
*ads
,
2555 uint32_t acct_ctrl
= 0;
2558 ok
= ads_pull_uint32(ads
, msg
, "userAccountControl", &acct_ctrl
);
2566 static ADS_STATUS
ads_change_machine_acct(ADS_STRUCT
*ads
,
2568 const struct berval
*machine_pw_val
)
2572 TALLOC_CTX
*frame
= talloc_stackframe();
2573 uint32_t acct_control
;
2574 char *control_str
= NULL
;
2575 const char *attrs
[] = {
2579 LDAPMessage
*res
= NULL
;
2582 dn
= ads_get_dn(ads
, frame
, msg
);
2584 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
2588 acct_control
= ads_get_acct_ctrl(ads
, msg
);
2589 if (acct_control
== 0) {
2590 ret
= ADS_ERROR(LDAP_NO_RESULTS_RETURNED
);
2595 * Changing the password, disables the account. So we need to change the
2596 * userAccountControl flags to enable it again.
2598 mods
= ads_init_mods(frame
);
2600 ret
= ADS_ERROR_LDAP(LDAP_NO_MEMORY
);
2604 ads_mod_ber(frame
, &mods
, "unicodePwd", machine_pw_val
);
2606 ret
= ads_gen_mod(ads
, dn
, mods
);
2607 if (!ADS_ERR_OK(ret
)) {
2613 * To activate the account, we need to disable and enable it.
2615 acct_control
|= UF_ACCOUNTDISABLE
;
2617 control_str
= talloc_asprintf(frame
, "%u", acct_control
);
2618 if (control_str
== NULL
) {
2619 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
2623 mods
= ads_init_mods(frame
);
2625 ret
= ADS_ERROR_LDAP(LDAP_NO_MEMORY
);
2629 ads_mod_str(frame
, &mods
, "userAccountControl", control_str
);
2631 ret
= ads_gen_mod(ads
, dn
, mods
);
2632 if (!ADS_ERR_OK(ret
)) {
2636 TALLOC_FREE(control_str
);
2639 * Enable the account again.
2641 acct_control
&= ~UF_ACCOUNTDISABLE
;
2643 control_str
= talloc_asprintf(frame
, "%u", acct_control
);
2644 if (control_str
== NULL
) {
2645 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
2649 mods
= ads_init_mods(frame
);
2651 ret
= ADS_ERROR_LDAP(LDAP_NO_MEMORY
);
2655 ads_mod_str(frame
, &mods
, "userAccountControl", control_str
);
2657 ret
= ads_gen_mod(ads
, dn
, mods
);
2658 if (!ADS_ERR_OK(ret
)) {
2662 TALLOC_FREE(control_str
);
2664 ret
= ads_search_dn(ads
, &res
, dn
, attrs
);
2665 ads_msgfree(ads
, res
);
2674 * adds a machine account to the ADS server
2675 * @param ads An initialized ADS_STRUCT
2676 * @param machine_name - the NetBIOS machine name of this account.
2677 * @param account_type A number indicating the type of account to create
2678 * @param org_unit The LDAP path in which to place this account
2679 * @return 0 upon success, or non-zero otherwise
2682 ADS_STATUS
ads_create_machine_acct(ADS_STRUCT
*ads
,
2683 const char *machine_name
,
2684 const char *machine_password
,
2685 const char *org_unit
,
2686 uint32_t etype_list
,
2687 const char *dns_domain_name
)
2690 char *samAccountName
= NULL
;
2691 char *controlstr
= NULL
;
2692 TALLOC_CTX
*ctx
= NULL
;
2694 char *machine_escaped
= NULL
;
2695 char *dns_hostname
= NULL
;
2696 char *new_dn
= NULL
;
2697 char *utf8_pw
= NULL
;
2698 size_t utf8_pw_len
= 0;
2699 char *utf16_pw
= NULL
;
2700 size_t utf16_pw_len
= 0;
2701 struct berval machine_pw_val
;
2703 const char **spn_array
= NULL
;
2704 size_t num_spns
= 0;
2705 const char *spn_prefix
[] = {
2707 "RestrictedKrbHost",
2710 LDAPMessage
*res
= NULL
;
2711 uint32_t acct_control
= UF_WORKSTATION_TRUST_ACCOUNT
;
2713 ctx
= talloc_init("ads_add_machine_acct");
2715 return ADS_ERROR(LDAP_NO_MEMORY
);
2718 machine_escaped
= escape_rdn_val_string_alloc(machine_name
);
2719 if (machine_escaped
== NULL
) {
2720 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
2724 utf8_pw
= talloc_asprintf(ctx
, "\"%s\"", machine_password
);
2725 if (utf8_pw
== NULL
) {
2726 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
2729 utf8_pw_len
= strlen(utf8_pw
);
2731 ok
= convert_string_talloc(ctx
,
2732 CH_UTF8
, CH_UTF16MUNGED
,
2733 utf8_pw
, utf8_pw_len
,
2734 (void *)&utf16_pw
, &utf16_pw_len
);
2736 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
2740 machine_pw_val
= (struct berval
) {
2742 .bv_len
= utf16_pw_len
,
2745 /* Check if the machine account already exists. */
2746 ret
= ads_find_machine_acct(ads
, &res
, machine_escaped
);
2747 if (ADS_ERR_OK(ret
)) {
2748 /* Change the machine account password */
2749 ret
= ads_change_machine_acct(ads
, res
, &machine_pw_val
);
2750 ads_msgfree(ads
, res
);
2754 ads_msgfree(ads
, res
);
2756 new_dn
= talloc_asprintf(ctx
, "cn=%s,%s", machine_escaped
, org_unit
);
2757 if (new_dn
== NULL
) {
2758 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
2762 /* Create machine account */
2764 samAccountName
= talloc_asprintf(ctx
, "%s$", machine_name
);
2765 if (samAccountName
== NULL
) {
2766 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
2770 dns_hostname
= talloc_asprintf(ctx
,
2774 if (dns_hostname
== NULL
) {
2775 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
2779 /* Add dns_hostname SPNs */
2780 for (i
= 0; i
< ARRAY_SIZE(spn_prefix
); i
++) {
2781 char *spn
= talloc_asprintf(ctx
,
2786 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
2790 ok
= add_string_to_array(ctx
,
2795 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
2800 /* Add machine_name SPNs */
2801 for (i
= 0; i
< ARRAY_SIZE(spn_prefix
); i
++) {
2802 char *spn
= talloc_asprintf(ctx
,
2807 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
2811 ok
= add_string_to_array(ctx
,
2816 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
2821 /* Make sure to NULL terminate the array */
2822 spn_array
= talloc_realloc(ctx
, spn_array
, const char *, num_spns
+ 1);
2823 if (spn_array
== NULL
) {
2824 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
2827 spn_array
[num_spns
] = NULL
;
2829 controlstr
= talloc_asprintf(ctx
, "%u", acct_control
);
2830 if (controlstr
== NULL
) {
2831 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
2835 mods
= ads_init_mods(ctx
);
2837 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
2841 ads_mod_str(ctx
, &mods
, "objectClass", "Computer");
2842 ads_mod_str(ctx
, &mods
, "SamAccountName", samAccountName
);
2843 ads_mod_str(ctx
, &mods
, "userAccountControl", controlstr
);
2844 ads_mod_str(ctx
, &mods
, "DnsHostName", dns_hostname
);
2845 ads_mod_strlist(ctx
, &mods
, "ServicePrincipalName", spn_array
);
2846 ads_mod_ber(ctx
, &mods
, "unicodePwd", &machine_pw_val
);
2848 ret
= ads_gen_add(ads
, new_dn
, mods
);
2851 SAFE_FREE(machine_escaped
);
2852 talloc_destroy(ctx
);
2858 * move a machine account to another OU on the ADS server
2859 * @param ads - An initialized ADS_STRUCT
2860 * @param machine_name - the NetBIOS machine name of this account.
2861 * @param org_unit - The LDAP path in which to place this account
2862 * @param moved - whether we moved the machine account (optional)
2863 * @return 0 upon success, or non-zero otherwise
2866 ADS_STATUS
ads_move_machine_acct(ADS_STRUCT
*ads
, const char *machine_name
,
2867 const char *org_unit
, bool *moved
)
2871 LDAPMessage
*res
= NULL
;
2872 char *filter
= NULL
;
2873 char *computer_dn
= NULL
;
2875 char *computer_rdn
= NULL
;
2876 bool need_move
= False
;
2878 if (asprintf(&filter
, "(samAccountName=%s$)", machine_name
) == -1) {
2879 rc
= ADS_ERROR(LDAP_NO_MEMORY
);
2883 /* Find pre-existing machine */
2884 rc
= ads_search(ads
, &res
, filter
, NULL
);
2885 if (!ADS_ERR_OK(rc
)) {
2889 computer_dn
= ads_get_dn(ads
, talloc_tos(), res
);
2891 rc
= ADS_ERROR(LDAP_NO_MEMORY
);
2895 parent_dn
= ads_parent_dn(computer_dn
);
2896 if (strequal(parent_dn
, org_unit
)) {
2902 if (asprintf(&computer_rdn
, "CN=%s", machine_name
) == -1) {
2903 rc
= ADS_ERROR(LDAP_NO_MEMORY
);
2907 ldap_status
= ldap_rename_s(ads
->ldap
.ld
, computer_dn
, computer_rdn
,
2908 org_unit
, 1, NULL
, NULL
);
2909 rc
= ADS_ERROR(ldap_status
);
2912 ads_msgfree(ads
, res
);
2914 TALLOC_FREE(computer_dn
);
2915 SAFE_FREE(computer_rdn
);
2917 if (!ADS_ERR_OK(rc
)) {
2929 dump a binary result from ldap
2931 static void dump_binary(ADS_STRUCT
*ads
, const char *field
, struct berval
**values
)
2934 for (i
=0; values
[i
]; i
++) {
2936 printf("%s: ", field
);
2937 for (j
=0; j
<values
[i
]->bv_len
; j
++) {
2938 printf("%02X", (unsigned char)values
[i
]->bv_val
[j
]);
2944 static void dump_guid(ADS_STRUCT
*ads
, const char *field
, struct berval
**values
)
2947 for (i
=0; values
[i
]; i
++) {
2949 DATA_BLOB in
= data_blob_const(values
[i
]->bv_val
, values
[i
]->bv_len
);
2952 status
= GUID_from_ndr_blob(&in
, &guid
);
2953 if (NT_STATUS_IS_OK(status
)) {
2954 printf("%s: %s\n", field
, GUID_string(talloc_tos(), &guid
));
2956 printf("%s: INVALID GUID\n", field
);
2962 dump a sid result from ldap
2964 static void dump_sid(ADS_STRUCT
*ads
, const char *field
, struct berval
**values
)
2967 for (i
=0; values
[i
]; i
++) {
2970 struct dom_sid_buf tmp
;
2971 ret
= sid_parse((const uint8_t *)values
[i
]->bv_val
,
2972 values
[i
]->bv_len
, &sid
);
2976 printf("%s: %s\n", field
, dom_sid_str_buf(&sid
, &tmp
));
2981 dump ntSecurityDescriptor
2983 static void dump_sd(ADS_STRUCT
*ads
, const char *filed
, struct berval
**values
)
2985 TALLOC_CTX
*frame
= talloc_stackframe();
2986 struct security_descriptor
*psd
;
2989 status
= unmarshall_sec_desc(talloc_tos(), (uint8_t *)values
[0]->bv_val
,
2990 values
[0]->bv_len
, &psd
);
2991 if (!NT_STATUS_IS_OK(status
)) {
2992 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
2993 nt_errstr(status
)));
2999 ads_disp_sd(ads
, talloc_tos(), psd
);
3006 dump a string result from ldap
3008 static void dump_string(const char *field
, char **values
)
3011 for (i
=0; values
[i
]; i
++) {
3012 printf("%s: %s\n", field
, values
[i
]);
3017 dump a field from LDAP on stdout
3021 static bool ads_dump_field(ADS_STRUCT
*ads
, char *field
, void **values
, void *data_area
)
3026 void (*handler
)(ADS_STRUCT
*, const char *, struct berval
**);
3028 {"objectGUID", False
, dump_guid
},
3029 {"netbootGUID", False
, dump_guid
},
3030 {"nTSecurityDescriptor", False
, dump_sd
},
3031 {"dnsRecord", False
, dump_binary
},
3032 {"objectSid", False
, dump_sid
},
3033 {"securityIdentifier", False
, dump_sid
},
3034 {"tokenGroups", False
, dump_sid
},
3035 {"tokenGroupsNoGCAcceptable", False
, dump_sid
},
3036 {"tokengroupsGlobalandUniversal", False
, dump_sid
},
3037 {"mS-DS-CreatorSID", False
, dump_sid
},
3038 {"msExchMailboxGuid", False
, dump_guid
},
3039 {"msDS-TrustForestTrustInfo", False
, dump_binary
},
3044 if (!field
) { /* must be end of an entry */
3049 for (i
=0; handlers
[i
].name
; i
++) {
3050 if (strcasecmp_m(handlers
[i
].name
, field
) == 0) {
3051 if (!values
) /* first time, indicate string or not */
3052 return handlers
[i
].string
;
3053 handlers
[i
].handler(ads
, field
, (struct berval
**) values
);
3057 if (!handlers
[i
].name
) {
3058 if (!values
) /* first time, indicate string conversion */
3060 dump_string(field
, (char **)values
);
3066 * Dump a result from LDAP on stdout
3067 * used for debugging
3068 * @param ads connection to ads server
3069 * @param res Results to dump
3072 void ads_dump(ADS_STRUCT
*ads
, LDAPMessage
*res
)
3074 ads_process_results(ads
, res
, ads_dump_field
, NULL
);
3078 * Walk through results, calling a function for each entry found.
3079 * The function receives a field name, a berval * array of values,
3080 * and a data area passed through from the start. The function is
3081 * called once with null for field and values at the end of each
3083 * @param ads connection to ads server
3084 * @param res Results to process
3085 * @param fn Function for processing each result
3086 * @param data_area user-defined area to pass to function
3088 void ads_process_results(ADS_STRUCT
*ads
, LDAPMessage
*res
,
3089 bool (*fn
)(ADS_STRUCT
*, char *, void **, void *),
3094 size_t converted_size
;
3096 if (!(ctx
= talloc_init("ads_process_results")))
3099 for (msg
= ads_first_entry(ads
, res
); msg
;
3100 msg
= ads_next_entry(ads
, msg
)) {
3104 for (utf8_field
=ldap_first_attribute(ads
->ldap
.ld
,
3105 (LDAPMessage
*)msg
,&b
);
3107 utf8_field
=ldap_next_attribute(ads
->ldap
.ld
,
3108 (LDAPMessage
*)msg
,b
)) {
3109 struct berval
**ber_vals
;
3115 if (!pull_utf8_talloc(ctx
, &field
, utf8_field
,
3118 DEBUG(0,("ads_process_results: "
3119 "pull_utf8_talloc failed: %s\n",
3123 string
= fn(ads
, field
, NULL
, data_area
);
3128 utf8_vals
= ldap_get_values(ads
->ldap
.ld
,
3129 (LDAPMessage
*)msg
, field
);
3130 p
= discard_const_p(const char *, utf8_vals
);
3131 str_vals
= ads_pull_strvals(ctx
, p
);
3132 fn(ads
, field
, (void **) str_vals
, data_area
);
3133 ldap_value_free(utf8_vals
);
3135 ber_vals
= ldap_get_values_len(ads
->ldap
.ld
,
3136 (LDAPMessage
*)msg
, field
);
3137 fn(ads
, field
, (void **) ber_vals
, data_area
);
3139 ldap_value_free_len(ber_vals
);
3141 ldap_memfree(utf8_field
);
3144 talloc_free_children(ctx
);
3145 fn(ads
, NULL
, NULL
, data_area
); /* completed an entry */
3148 talloc_destroy(ctx
);
3152 * count how many replies are in a LDAPMessage
3153 * @param ads connection to ads server
3154 * @param res Results to count
3155 * @return number of replies
3157 int ads_count_replies(ADS_STRUCT
*ads
, void *res
)
3159 return ldap_count_entries(ads
->ldap
.ld
, (LDAPMessage
*)res
);
3163 * pull the first entry from a ADS result
3164 * @param ads connection to ads server
3165 * @param res Results of search
3166 * @return first entry from result
3168 LDAPMessage
*ads_first_entry(ADS_STRUCT
*ads
, LDAPMessage
*res
)
3170 return ldap_first_entry(ads
->ldap
.ld
, res
);
3174 * pull the next entry from a ADS result
3175 * @param ads connection to ads server
3176 * @param res Results of search
3177 * @return next entry from result
3179 LDAPMessage
*ads_next_entry(ADS_STRUCT
*ads
, LDAPMessage
*res
)
3181 return ldap_next_entry(ads
->ldap
.ld
, res
);
3185 * pull the first message from a ADS result
3186 * @param ads connection to ads server
3187 * @param res Results of search
3188 * @return first message from result
3190 LDAPMessage
*ads_first_message(ADS_STRUCT
*ads
, LDAPMessage
*res
)
3192 return ldap_first_message(ads
->ldap
.ld
, res
);
3196 * pull the next message from a ADS result
3197 * @param ads connection to ads server
3198 * @param res Results of search
3199 * @return next message from result
3201 LDAPMessage
*ads_next_message(ADS_STRUCT
*ads
, LDAPMessage
*res
)
3203 return ldap_next_message(ads
->ldap
.ld
, res
);
3207 * pull a single string from a ADS result
3208 * @param ads connection to ads server
3209 * @param mem_ctx TALLOC_CTX to use for allocating result string
3210 * @param msg Results of search
3211 * @param field Attribute to retrieve
3212 * @return Result string in talloc context
3214 char *ads_pull_string(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
, LDAPMessage
*msg
,
3220 size_t converted_size
;
3222 values
= ldap_get_values(ads
->ldap
.ld
, msg
, field
);
3226 if (values
[0] && pull_utf8_talloc(mem_ctx
, &ux_string
, values
[0],
3231 ldap_value_free(values
);
3236 * pull an array of strings from a ADS result
3237 * @param ads connection to ads server
3238 * @param mem_ctx TALLOC_CTX to use for allocating result string
3239 * @param msg Results of search
3240 * @param field Attribute to retrieve
3241 * @return Result strings in talloc context
3243 char **ads_pull_strings(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
,
3244 LDAPMessage
*msg
, const char *field
,
3249 size_t i
, converted_size
;
3251 values
= ldap_get_values(ads
->ldap
.ld
, msg
, field
);
3255 *num_values
= ldap_count_values(values
);
3257 ret
= talloc_array(mem_ctx
, char *, *num_values
+ 1);
3259 ldap_value_free(values
);
3263 for (i
=0;i
<*num_values
;i
++) {
3264 if (!pull_utf8_talloc(mem_ctx
, &ret
[i
], values
[i
],
3267 ldap_value_free(values
);
3273 ldap_value_free(values
);
3278 * pull an array of strings from a ADS result
3279 * (handle large multivalue attributes with range retrieval)
3280 * @param ads connection to ads server
3281 * @param mem_ctx TALLOC_CTX to use for allocating result string
3282 * @param msg Results of search
3283 * @param field Attribute to retrieve
3284 * @param current_strings strings returned by a previous call to this function
3285 * @param next_attribute The next query should ask for this attribute
3286 * @param num_values How many values did we get this time?
3287 * @param more_values Are there more values to get?
3288 * @return Result strings in talloc context
3290 char **ads_pull_strings_range(ADS_STRUCT
*ads
,
3291 TALLOC_CTX
*mem_ctx
,
3292 LDAPMessage
*msg
, const char *field
,
3293 char **current_strings
,
3294 const char **next_attribute
,
3295 size_t *num_strings
,
3299 char *expected_range_attrib
, *range_attr
= NULL
;
3300 BerElement
*ptr
= NULL
;
3303 size_t num_new_strings
;
3304 unsigned long int range_start
;
3305 unsigned long int range_end
;
3307 /* we might have been given the whole lot anyway */
3308 if ((strings
= ads_pull_strings(ads
, mem_ctx
, msg
, field
, num_strings
))) {
3309 *more_strings
= False
;
3313 expected_range_attrib
= talloc_asprintf(mem_ctx
, "%s;Range=", field
);
3315 /* look for Range result */
3316 for (attr
= ldap_first_attribute(ads
->ldap
.ld
, (LDAPMessage
*)msg
, &ptr
);
3318 attr
= ldap_next_attribute(ads
->ldap
.ld
, (LDAPMessage
*)msg
, ptr
)) {
3319 /* we ignore the fact that this is utf8, as all attributes are ascii... */
3320 if (strnequal(attr
, expected_range_attrib
, strlen(expected_range_attrib
))) {
3328 /* nothing here - this field is just empty */
3329 *more_strings
= False
;
3333 if (sscanf(&range_attr
[strlen(expected_range_attrib
)], "%lu-%lu",
3334 &range_start
, &range_end
) == 2) {
3335 *more_strings
= True
;
3337 if (sscanf(&range_attr
[strlen(expected_range_attrib
)], "%lu-*",
3338 &range_start
) == 1) {
3339 *more_strings
= False
;
3341 DEBUG(1, ("ads_pull_strings_range: Cannot parse Range attribute (%s)\n",
3343 ldap_memfree(range_attr
);
3344 *more_strings
= False
;
3349 if ((*num_strings
) != range_start
) {
3350 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) doesn't start at %u, but at %lu"
3351 " - aborting range retrieval\n",
3352 range_attr
, (unsigned int)(*num_strings
) + 1, range_start
));
3353 ldap_memfree(range_attr
);
3354 *more_strings
= False
;
3358 new_strings
= ads_pull_strings(ads
, mem_ctx
, msg
, range_attr
, &num_new_strings
);
3360 if (*more_strings
&& ((*num_strings
+ num_new_strings
) != (range_end
+ 1))) {
3361 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) tells us we have %lu "
3362 "strings in this bunch, but we only got %lu - aborting range retrieval\n",
3363 range_attr
, (unsigned long int)range_end
- range_start
+ 1,
3364 (unsigned long int)num_new_strings
));
3365 ldap_memfree(range_attr
);
3366 *more_strings
= False
;
3370 strings
= talloc_realloc(mem_ctx
, current_strings
, char *,
3371 *num_strings
+ num_new_strings
);
3373 if (strings
== NULL
) {
3374 ldap_memfree(range_attr
);
3375 *more_strings
= False
;
3379 if (new_strings
&& num_new_strings
) {
3380 memcpy(&strings
[*num_strings
], new_strings
,
3381 sizeof(*new_strings
) * num_new_strings
);
3384 (*num_strings
) += num_new_strings
;
3386 if (*more_strings
) {
3387 *next_attribute
= talloc_asprintf(mem_ctx
,
3392 if (!*next_attribute
) {
3393 DEBUG(1, ("talloc_asprintf for next attribute failed!\n"));
3394 ldap_memfree(range_attr
);
3395 *more_strings
= False
;
3400 ldap_memfree(range_attr
);
3406 * pull a single uint32_t from a ADS result
3407 * @param ads connection to ads server
3408 * @param msg Results of search
3409 * @param field Attribute to retrieve
3410 * @param v Pointer to int to store result
3411 * @return boolean indicating success
3413 bool ads_pull_uint32(ADS_STRUCT
*ads
, LDAPMessage
*msg
, const char *field
,
3418 values
= ldap_get_values(ads
->ldap
.ld
, msg
, field
);
3422 ldap_value_free(values
);
3426 *v
= atoi(values
[0]);
3427 ldap_value_free(values
);
3432 * pull a single objectGUID from an ADS result
3433 * @param ads connection to ADS server
3434 * @param msg results of search
3435 * @param guid 37-byte area to receive text guid
3436 * @return boolean indicating success
3438 bool ads_pull_guid(ADS_STRUCT
*ads
, LDAPMessage
*msg
, struct GUID
*guid
)
3443 if (!smbldap_talloc_single_blob(talloc_tos(), ads
->ldap
.ld
, msg
, "objectGUID",
3448 status
= GUID_from_ndr_blob(&blob
, guid
);
3449 talloc_free(blob
.data
);
3450 return NT_STATUS_IS_OK(status
);
3455 * pull a single struct dom_sid from a ADS result
3456 * @param ads connection to ads server
3457 * @param msg Results of search
3458 * @param field Attribute to retrieve
3459 * @param sid Pointer to sid to store result
3460 * @return boolean indicating success
3462 bool ads_pull_sid(ADS_STRUCT
*ads
, LDAPMessage
*msg
, const char *field
,
3463 struct dom_sid
*sid
)
3465 return smbldap_pull_sid(ads
->ldap
.ld
, msg
, field
, sid
);
3469 * pull an array of struct dom_sids from a ADS result
3470 * @param ads connection to ads server
3471 * @param mem_ctx TALLOC_CTX for allocating sid array
3472 * @param msg Results of search
3473 * @param field Attribute to retrieve
3474 * @param sids pointer to sid array to allocate
3475 * @return the count of SIDs pulled
3477 int ads_pull_sids(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
,
3478 LDAPMessage
*msg
, const char *field
, struct dom_sid
**sids
)
3480 struct berval
**values
;
3483 values
= ldap_get_values_len(ads
->ldap
.ld
, msg
, field
);
3488 for (i
=0; values
[i
]; i
++)
3492 (*sids
) = talloc_array(mem_ctx
, struct dom_sid
, i
);
3494 ldap_value_free_len(values
);
3502 for (i
=0; values
[i
]; i
++) {
3504 ret
= sid_parse((const uint8_t *)values
[i
]->bv_val
,
3505 values
[i
]->bv_len
, &(*sids
)[count
]);
3507 struct dom_sid_buf buf
;
3508 DBG_DEBUG("pulling SID: %s\n",
3509 dom_sid_str_buf(&(*sids
)[count
], &buf
));
3514 ldap_value_free_len(values
);
3519 * pull a struct security_descriptor from a ADS result
3520 * @param ads connection to ads server
3521 * @param mem_ctx TALLOC_CTX for allocating sid array
3522 * @param msg Results of search
3523 * @param field Attribute to retrieve
3524 * @param sd Pointer to *struct security_descriptor to store result (talloc()ed)
3525 * @return boolean indicating success
3527 bool ads_pull_sd(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
,
3528 LDAPMessage
*msg
, const char *field
,
3529 struct security_descriptor
**sd
)
3531 struct berval
**values
;
3534 values
= ldap_get_values_len(ads
->ldap
.ld
, msg
, field
);
3536 if (!values
) return false;
3540 status
= unmarshall_sec_desc(mem_ctx
,
3541 (uint8_t *)values
[0]->bv_val
,
3542 values
[0]->bv_len
, sd
);
3543 if (!NT_STATUS_IS_OK(status
)) {
3544 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
3545 nt_errstr(status
)));
3550 ldap_value_free_len(values
);
3555 * in order to support usernames longer than 21 characters we need to
3556 * use both the sAMAccountName and the userPrincipalName attributes
3557 * It seems that not all users have the userPrincipalName attribute set
3559 * @param ads connection to ads server
3560 * @param mem_ctx TALLOC_CTX for allocating sid array
3561 * @param msg Results of search
3562 * @return the username
3564 char *ads_pull_username(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
,
3570 /* lookup_name() only works on the sAMAccountName to
3571 returning the username portion of userPrincipalName
3572 breaks winbindd_getpwnam() */
3574 ret
= ads_pull_string(ads
, mem_ctx
, msg
, "userPrincipalName");
3575 if (ret
&& (p
= strchr_m(ret
, '@'))) {
3580 return ads_pull_string(ads
, mem_ctx
, msg
, "sAMAccountName");
3585 * find the update serial number - this is the core of the ldap cache
3586 * @param ads connection to ads server
3587 * @param ads connection to ADS server
3588 * @param usn Pointer to retrieved update serial number
3589 * @return status of search
3591 ADS_STATUS
ads_USN(ADS_STRUCT
*ads
, uint32_t *usn
)
3593 const char *attrs
[] = {"highestCommittedUSN", NULL
};
3597 status
= ads_do_search_retry(ads
, "", LDAP_SCOPE_BASE
, "(objectclass=*)", attrs
, &res
);
3598 if (!ADS_ERR_OK(status
))
3601 if (ads_count_replies(ads
, res
) != 1) {
3602 ads_msgfree(ads
, res
);
3603 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED
);
3606 if (!ads_pull_uint32(ads
, res
, "highestCommittedUSN", usn
)) {
3607 ads_msgfree(ads
, res
);
3608 return ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE
);
3611 ads_msgfree(ads
, res
);
3615 /* parse a ADS timestring - typical string is
3616 '20020917091222.0Z0' which means 09:12.22 17th September
3618 static time_t ads_parse_time(const char *str
)
3624 if (sscanf(str
, "%4d%2d%2d%2d%2d%2d",
3625 &tm
.tm_year
, &tm
.tm_mon
, &tm
.tm_mday
,
3626 &tm
.tm_hour
, &tm
.tm_min
, &tm
.tm_sec
) != 6) {
3635 /********************************************************************
3636 ********************************************************************/
3638 ADS_STATUS
ads_current_time(ADS_STRUCT
*ads
)
3640 const char *attrs
[] = {"currentTime", NULL
};
3644 TALLOC_CTX
*tmp_ctx
= talloc_stackframe();
3645 ADS_STRUCT
*ads_s
= ads
;
3647 /* establish a new ldap tcp session if necessary */
3649 if ( !ads
->ldap
.ld
) {
3651 * ADS_STRUCT may be being reused after a
3652 * DC lookup, so ads->ldap.ss may already have a
3653 * good address. If not, re-initialize the passed-in
3654 * ADS_STRUCT with the given server.XXXX parameters.
3656 * Note that this doesn't depend on
3657 * ads->server.ldap_server != NULL,
3658 * as the case where ads->server.ldap_server==NULL and
3659 * ads->ldap.ss != zero_address is precisely the DC
3660 * lookup case where ads->ldap.ss was found by going
3661 * through ads_find_dc() again we want to avoid repeating.
3663 if (is_zero_addr(&ads
->ldap
.ss
)) {
3664 ads_s
= ads_init(tmp_ctx
,
3666 ads
->server
.workgroup
,
3667 ads
->server
.ldap_server
,
3669 if (ads_s
== NULL
) {
3670 status
= ADS_ERROR(LDAP_NO_MEMORY
);
3676 * Reset ads->config.flags as it can contain the flags
3677 * returned by the previous CLDAP ping when reusing the struct.
3679 ads_s
->config
.flags
= 0;
3681 ads_s
->auth
.flags
= ADS_AUTH_ANON_BIND
;
3682 status
= ads_connect( ads_s
);
3683 if ( !ADS_ERR_OK(status
))
3687 status
= ads_do_search(ads_s
, "", LDAP_SCOPE_BASE
, "(objectclass=*)", attrs
, &res
);
3688 if (!ADS_ERR_OK(status
)) {
3692 timestr
= ads_pull_string(ads_s
, tmp_ctx
, res
, "currentTime");
3694 ads_msgfree(ads_s
, res
);
3695 status
= ADS_ERROR(LDAP_NO_RESULTS_RETURNED
);
3699 /* but save the time and offset in the original ADS_STRUCT */
3701 ads
->config
.current_time
= ads_parse_time(timestr
);
3703 if (ads
->config
.current_time
!= 0) {
3704 ads
->config
.time_offset
= ads
->config
.current_time
- time(NULL
);
3705 DBG_INFO("server time offset is %d seconds\n",
3706 ads
->config
.time_offset
);
3708 ads
->config
.time_offset
= 0;
3711 DBG_INFO("server time offset is %d seconds\n",
3712 ads
->config
.time_offset
);
3714 ads_msgfree(ads
, res
);
3716 status
= ADS_SUCCESS
;
3719 TALLOC_FREE(tmp_ctx
);
3724 /********************************************************************
3725 ********************************************************************/
3727 ADS_STATUS
ads_domain_func_level(ADS_STRUCT
*ads
, uint32_t *val
)
3729 TALLOC_CTX
*tmp_ctx
= talloc_stackframe();
3730 const char *attrs
[] = {"domainFunctionality", NULL
};
3733 ADS_STRUCT
*ads_s
= ads
;
3735 *val
= DS_DOMAIN_FUNCTION_2000
;
3737 /* establish a new ldap tcp session if necessary */
3739 if ( !ads
->ldap
.ld
) {
3741 * ADS_STRUCT may be being reused after a
3742 * DC lookup, so ads->ldap.ss may already have a
3743 * good address. If not, re-initialize the passed-in
3744 * ADS_STRUCT with the given server.XXXX parameters.
3746 * Note that this doesn't depend on
3747 * ads->server.ldap_server != NULL,
3748 * as the case where ads->server.ldap_server==NULL and
3749 * ads->ldap.ss != zero_address is precisely the DC
3750 * lookup case where ads->ldap.ss was found by going
3751 * through ads_find_dc() again we want to avoid repeating.
3753 if (is_zero_addr(&ads
->ldap
.ss
)) {
3754 ads_s
= ads_init(tmp_ctx
,
3756 ads
->server
.workgroup
,
3757 ads
->server
.ldap_server
,
3759 if (ads_s
== NULL
) {
3760 status
= ADS_ERROR_NT(NT_STATUS_NO_MEMORY
);
3766 * Reset ads->config.flags as it can contain the flags
3767 * returned by the previous CLDAP ping when reusing the struct.
3769 ads_s
->config
.flags
= 0;
3771 ads_s
->auth
.flags
= ADS_AUTH_ANON_BIND
;
3772 status
= ads_connect( ads_s
);
3773 if ( !ADS_ERR_OK(status
))
3777 /* If the attribute does not exist assume it is a Windows 2000
3778 functional domain */
3780 status
= ads_do_search(ads_s
, "", LDAP_SCOPE_BASE
, "(objectclass=*)", attrs
, &res
);
3781 if (!ADS_ERR_OK(status
)) {
3782 if ( status
.err
.rc
== LDAP_NO_SUCH_ATTRIBUTE
) {
3783 status
= ADS_SUCCESS
;
3788 if ( !ads_pull_uint32(ads_s
, res
, "domainFunctionality", val
) ) {
3789 DEBUG(5,("ads_domain_func_level: Failed to pull the domainFunctionality attribute.\n"));
3791 DEBUG(3,("ads_domain_func_level: %d\n", *val
));
3794 ads_msgfree(ads_s
, res
);
3797 TALLOC_FREE(tmp_ctx
);
3803 * find the domain sid for our domain
3804 * @param ads connection to ads server
3805 * @param sid Pointer to domain sid
3806 * @return status of search
3808 ADS_STATUS
ads_domain_sid(ADS_STRUCT
*ads
, struct dom_sid
*sid
)
3810 const char *attrs
[] = {"objectSid", NULL
};
3814 rc
= ads_do_search_retry(ads
, ads
->config
.bind_path
, LDAP_SCOPE_BASE
, "(objectclass=*)",
3816 if (!ADS_ERR_OK(rc
)) return rc
;
3817 if (!ads_pull_sid(ads
, res
, "objectSid", sid
)) {
3818 ads_msgfree(ads
, res
);
3819 return ADS_ERROR_SYSTEM(ENOENT
);
3821 ads_msgfree(ads
, res
);
3827 * find our site name
3828 * @param ads connection to ads server
3829 * @param mem_ctx Pointer to talloc context
3830 * @param site_name Pointer to the sitename
3831 * @return status of search
3833 ADS_STATUS
ads_site_dn(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
, const char **site_name
)
3837 const char *dn
, *service_name
;
3838 const char *attrs
[] = { "dsServiceName", NULL
};
3840 status
= ads_do_search(ads
, "", LDAP_SCOPE_BASE
, "(objectclass=*)", attrs
, &res
);
3841 if (!ADS_ERR_OK(status
)) {
3845 service_name
= ads_pull_string(ads
, mem_ctx
, res
, "dsServiceName");
3846 if (service_name
== NULL
) {
3847 ads_msgfree(ads
, res
);
3848 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED
);
3851 ads_msgfree(ads
, res
);
3853 /* go up three levels */
3854 dn
= ads_parent_dn(ads_parent_dn(ads_parent_dn(service_name
)));
3856 return ADS_ERROR(LDAP_NO_MEMORY
);
3859 *site_name
= talloc_strdup(mem_ctx
, dn
);
3860 if (*site_name
== NULL
) {
3861 return ADS_ERROR(LDAP_NO_MEMORY
);
3866 dsServiceName: CN=NTDS Settings,CN=W2K3DC,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,DC=ber,DC=suse,DC=de
3871 * find the site dn where a machine resides
3872 * @param ads connection to ads server
3873 * @param mem_ctx Pointer to talloc context
3874 * @param computer_name name of the machine
3875 * @param site_name Pointer to the sitename
3876 * @return status of search
3878 ADS_STATUS
ads_site_dn_for_machine(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
, const char *computer_name
, const char **site_dn
)
3882 const char *parent
, *filter
;
3883 char *config_context
= NULL
;
3886 /* shortcut a query */
3887 if (strequal(computer_name
, ads
->config
.ldap_server_name
)) {
3888 return ads_site_dn(ads
, mem_ctx
, site_dn
);
3891 status
= ads_config_path(ads
, mem_ctx
, &config_context
);
3892 if (!ADS_ERR_OK(status
)) {
3896 filter
= talloc_asprintf(mem_ctx
, "(cn=%s)", computer_name
);
3897 if (filter
== NULL
) {
3898 return ADS_ERROR(LDAP_NO_MEMORY
);
3901 status
= ads_do_search(ads
, config_context
, LDAP_SCOPE_SUBTREE
,
3902 filter
, NULL
, &res
);
3903 if (!ADS_ERR_OK(status
)) {
3907 if (ads_count_replies(ads
, res
) != 1) {
3908 ads_msgfree(ads
, res
);
3909 return ADS_ERROR(LDAP_NO_SUCH_OBJECT
);
3912 dn
= ads_get_dn(ads
, mem_ctx
, res
);
3914 ads_msgfree(ads
, res
);
3915 return ADS_ERROR(LDAP_NO_MEMORY
);
3918 /* go up three levels */
3919 parent
= ads_parent_dn(ads_parent_dn(ads_parent_dn(dn
)));
3920 if (parent
== NULL
) {
3921 ads_msgfree(ads
, res
);
3923 return ADS_ERROR(LDAP_NO_MEMORY
);
3926 *site_dn
= talloc_strdup(mem_ctx
, parent
);
3927 if (*site_dn
== NULL
) {
3928 ads_msgfree(ads
, res
);
3930 return ADS_ERROR(LDAP_NO_MEMORY
);
3934 ads_msgfree(ads
, res
);
3940 * get the upn suffixes for a domain
3941 * @param ads connection to ads server
3942 * @param mem_ctx Pointer to talloc context
3943 * @param suffixes Pointer to an array of suffixes
3944 * @param num_suffixes Pointer to the number of suffixes
3945 * @return status of search
3947 ADS_STATUS
ads_upn_suffixes(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
, char ***suffixes
, size_t *num_suffixes
)
3952 char *config_context
= NULL
;
3953 const char *attrs
[] = { "uPNSuffixes", NULL
};
3955 status
= ads_config_path(ads
, mem_ctx
, &config_context
);
3956 if (!ADS_ERR_OK(status
)) {
3960 base
= talloc_asprintf(mem_ctx
, "cn=Partitions,%s", config_context
);
3962 return ADS_ERROR(LDAP_NO_MEMORY
);
3965 status
= ads_search_dn(ads
, &res
, base
, attrs
);
3966 if (!ADS_ERR_OK(status
)) {
3970 if (ads_count_replies(ads
, res
) != 1) {
3971 ads_msgfree(ads
, res
);
3972 return ADS_ERROR(LDAP_NO_SUCH_OBJECT
);
3975 (*suffixes
) = ads_pull_strings(ads
, mem_ctx
, res
, "uPNSuffixes", num_suffixes
);
3976 if ((*suffixes
) == NULL
) {
3977 ads_msgfree(ads
, res
);
3978 return ADS_ERROR(LDAP_NO_MEMORY
);
3981 ads_msgfree(ads
, res
);
3987 * get the joinable ous for a domain
3988 * @param ads connection to ads server
3989 * @param mem_ctx Pointer to talloc context
3990 * @param ous Pointer to an array of ous
3991 * @param num_ous Pointer to the number of ous
3992 * @return status of search
3994 ADS_STATUS
ads_get_joinable_ous(ADS_STRUCT
*ads
,
3995 TALLOC_CTX
*mem_ctx
,
4000 LDAPMessage
*res
= NULL
;
4001 LDAPMessage
*msg
= NULL
;
4002 const char *attrs
[] = { "dn", NULL
};
4005 status
= ads_search(ads
, &res
,
4006 "(|(objectClass=domain)(objectclass=organizationalUnit))",
4008 if (!ADS_ERR_OK(status
)) {
4012 count
= ads_count_replies(ads
, res
);
4014 ads_msgfree(ads
, res
);
4015 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED
);
4018 for (msg
= ads_first_entry(ads
, res
); msg
;
4019 msg
= ads_next_entry(ads
, msg
)) {
4020 const char **p
= discard_const_p(const char *, *ous
);
4023 dn
= ads_get_dn(ads
, talloc_tos(), msg
);
4025 ads_msgfree(ads
, res
);
4026 return ADS_ERROR(LDAP_NO_MEMORY
);
4029 if (!add_string_to_array(mem_ctx
, dn
, &p
, num_ous
)) {
4031 ads_msgfree(ads
, res
);
4032 return ADS_ERROR(LDAP_NO_MEMORY
);
4036 *ous
= discard_const_p(char *, p
);
4039 ads_msgfree(ads
, res
);
4046 * pull a struct dom_sid from an extended dn string
4047 * @param mem_ctx TALLOC_CTX
4048 * @param extended_dn string
4049 * @param flags string type of extended_dn
4050 * @param sid pointer to a struct dom_sid
4051 * @return NT_STATUS_OK on success,
4052 * NT_INVALID_PARAMETER on error,
4053 * NT_STATUS_NOT_FOUND if no SID present
4055 ADS_STATUS
ads_get_sid_from_extended_dn(TALLOC_CTX
*mem_ctx
,
4056 const char *extended_dn
,
4057 enum ads_extended_dn_flags flags
,
4058 struct dom_sid
*sid
)
4063 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER
);
4066 /* otherwise extended_dn gets stripped off */
4067 if ((dn
= talloc_strdup(mem_ctx
, extended_dn
)) == NULL
) {
4068 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER
);
4071 * ADS_EXTENDED_DN_HEX_STRING:
4072 * <GUID=238e1963cb390f4bb032ba0105525a29>;<SID=010500000000000515000000bb68c8fd6b61b427572eb04556040000>;CN=gd,OU=berlin,OU=suse,DC=ber,DC=suse,DC=de
4074 * ADS_EXTENDED_DN_STRING (only with w2k3):
4075 * <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
4077 * Object with no SID, such as an Exchange Public Folder
4078 * <GUID=28907fb4bdf6854993e7f0a10b504e7c>;CN=public,CN=Microsoft Exchange System Objects,DC=sd2k3ms,DC=west,DC=isilon,DC=com
4081 p
= strchr(dn
, ';');
4083 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER
);
4086 if (strncmp(p
, ";<SID=", strlen(";<SID=")) != 0) {
4087 DEBUG(5,("No SID present in extended dn\n"));
4088 return ADS_ERROR_NT(NT_STATUS_NOT_FOUND
);
4091 p
+= strlen(";<SID=");
4095 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER
);
4100 DEBUG(100,("ads_get_sid_from_extended_dn: sid string is %s\n", p
));
4104 case ADS_EXTENDED_DN_STRING
:
4105 if (!string_to_sid(sid
, p
)) {
4106 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER
);
4109 case ADS_EXTENDED_DN_HEX_STRING
: {
4114 buf_len
= strhex_to_str(buf
, sizeof(buf
), p
, strlen(p
));
4116 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER
);
4119 ret
= sid_parse((const uint8_t *)buf
, buf_len
, sid
);
4121 DEBUG(10,("failed to parse sid\n"));
4122 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER
);
4127 DEBUG(10,("unknown extended dn format\n"));
4128 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER
);
4131 return ADS_ERROR_NT(NT_STATUS_OK
);
4134 /********************************************************************
4135 ********************************************************************/
4137 char* ads_get_dnshostname( ADS_STRUCT
*ads
, TALLOC_CTX
*ctx
, const char *machine_name
)
4139 LDAPMessage
*res
= NULL
;
4144 status
= ads_find_machine_acct(ads
, &res
, machine_name
);
4145 if (!ADS_ERR_OK(status
)) {
4146 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
4147 lp_netbios_name()));
4151 if ( (count
= ads_count_replies(ads
, res
)) != 1 ) {
4152 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count
));
4156 if ( (name
= ads_pull_string(ads
, ctx
, res
, "dNSHostName")) == NULL
) {
4157 DEBUG(0,("ads_get_dnshostname: No dNSHostName attribute!\n"));
4161 ads_msgfree(ads
, res
);
4166 /********************************************************************
4167 ********************************************************************/
4169 static char **get_addl_hosts(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
,
4170 LDAPMessage
*msg
, size_t *num_values
)
4172 const char *field
= "msDS-AdditionalDnsHostName";
4173 struct berval
**values
= NULL
;
4175 size_t i
, converted_size
;
4178 * Windows DC implicitly adds a short name for each FQDN added to
4179 * msDS-AdditionalDnsHostName, but it comes with a strange binary
4180 * suffix "\0$" which we should ignore (see bug #14406).
4183 values
= ldap_get_values_len(ads
->ldap
.ld
, msg
, field
);
4184 if (values
== NULL
) {
4188 *num_values
= ldap_count_values_len(values
);
4190 ret
= talloc_array(mem_ctx
, char *, *num_values
+ 1);
4192 ldap_value_free_len(values
);
4196 for (i
= 0; i
< *num_values
; i
++) {
4198 if (!convert_string_talloc(mem_ctx
, CH_UTF8
, CH_UNIX
,
4200 strnlen(values
[i
]->bv_val
,
4202 &ret
[i
], &converted_size
)) {
4203 ldap_value_free_len(values
);
4209 ldap_value_free_len(values
);
4213 ADS_STATUS
ads_get_additional_dns_hostnames(TALLOC_CTX
*mem_ctx
,
4215 const char *machine_name
,
4216 char ***hostnames_array
,
4217 size_t *num_hostnames
)
4220 LDAPMessage
*res
= NULL
;
4223 status
= ads_find_machine_acct(ads
,
4226 if (!ADS_ERR_OK(status
)) {
4227 DEBUG(1,("Host Account for %s not found... skipping operation.\n",
4232 count
= ads_count_replies(ads
, res
);
4234 status
= ADS_ERROR(LDAP_NO_SUCH_OBJECT
);
4238 *hostnames_array
= get_addl_hosts(ads
, mem_ctx
, res
, num_hostnames
);
4239 if (*hostnames_array
== NULL
) {
4240 DEBUG(1, ("Host account for %s does not have msDS-AdditionalDnsHostName.\n",
4242 status
= ADS_ERROR(LDAP_NO_SUCH_OBJECT
);
4247 ads_msgfree(ads
, res
);
4252 /********************************************************************
4253 ********************************************************************/
4255 char* ads_get_upn( ADS_STRUCT
*ads
, TALLOC_CTX
*ctx
, const char *machine_name
)
4257 LDAPMessage
*res
= NULL
;
4262 status
= ads_find_machine_acct(ads
, &res
, machine_name
);
4263 if (!ADS_ERR_OK(status
)) {
4264 DEBUG(0,("ads_get_upn: Failed to find account for %s\n",
4265 lp_netbios_name()));
4269 if ( (count
= ads_count_replies(ads
, res
)) != 1 ) {
4270 DEBUG(1,("ads_get_upn: %d entries returned!\n", count
));
4274 if ( (name
= ads_pull_string(ads
, ctx
, res
, "userPrincipalName")) == NULL
) {
4275 DEBUG(2,("ads_get_upn: No userPrincipalName attribute!\n"));
4279 ads_msgfree(ads
, res
);
4284 /********************************************************************
4285 ********************************************************************/
4287 bool ads_has_samaccountname( ADS_STRUCT
*ads
, TALLOC_CTX
*ctx
, const char *machine_name
)
4289 LDAPMessage
*res
= NULL
;
4295 status
= ads_find_machine_acct(ads
, &res
, machine_name
);
4296 if (!ADS_ERR_OK(status
)) {
4297 DEBUG(0,("ads_has_samaccountname: Failed to find account for %s\n",
4298 lp_netbios_name()));
4302 if ( (count
= ads_count_replies(ads
, res
)) != 1 ) {
4303 DEBUG(1,("ads_has_samaccountname: %d entries returned!\n", count
));
4307 if ( (name
= ads_pull_string(ads
, ctx
, res
, "sAMAccountName")) == NULL
) {
4308 DEBUG(0,("ads_has_samaccountname: No sAMAccountName attribute!\n"));
4312 ads_msgfree(ads
, res
);
4314 ok
= (strlen(name
) > 0);
4322 SAVED CODE
- we used to join via ldap
- remember how we did
this. JRA
.
4325 * Join a machine to a realm
4326 * Creates the machine account and sets the machine password
4327 * @param ads connection to ads server
4328 * @param machine name of host to add
4329 * @param org_unit Organizational unit to place machine in
4330 * @return status of join
4332 ADS_STATUS
ads_join_realm(ADS_STRUCT
*ads
, const char *machine_name
,
4333 uint32_t account_type
, const char *org_unit
)
4336 LDAPMessage
*res
= NULL
;
4339 /* machine name must be lowercase */
4340 machine
= SMB_STRDUP(machine_name
);
4341 strlower_m(machine
);
4344 status = ads_find_machine_acct(ads, (void **)&res, machine);
4345 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
4346 DEBUG(0, ("Host account for %s already exists - deleting old account\n", machine));
4347 status = ads_leave_realm(ads, machine);
4348 if (!ADS_ERR_OK(status)) {
4349 DEBUG(0, ("Failed to delete host '%s' from the '%s' realm.\n",
4350 machine, ads->config.realm));
4355 status
= ads_add_machine_acct(ads
, machine
, account_type
, org_unit
);
4356 if (!ADS_ERR_OK(status
)) {
4357 DEBUG(0, ("ads_join_realm: ads_add_machine_acct failed (%s): %s\n", machine
, ads_errstr(status
)));
4362 status
= ads_find_machine_acct(ads
, (void **)(void *)&res
, machine
);
4363 if (!ADS_ERR_OK(status
)) {
4364 DEBUG(0, ("ads_join_realm: Host account test failed for machine %s\n", machine
));
4370 ads_msgfree(ads
, res
);
4377 * Delete a machine from the realm
4378 * @param ads connection to ads server
4379 * @param hostname Machine to remove
4380 * @return status of delete
4382 ADS_STATUS
ads_leave_realm(ADS_STRUCT
*ads
, const char *hostname
)
4387 char *hostnameDN
, *host
;
4389 LDAPControl ldap_control
;
4390 LDAPControl
* pldap_control
[2] = {NULL
, NULL
};
4392 pldap_control
[0] = &ldap_control
;
4393 memset(&ldap_control
, 0, sizeof(LDAPControl
));
4394 ldap_control
.ldctl_oid
= discard_const_p(char, LDAP_SERVER_TREE_DELETE_OID
);
4396 /* hostname must be lowercase */
4397 host
= SMB_STRDUP(hostname
);
4398 if (!strlower_m(host
)) {
4400 return ADS_ERROR_SYSTEM(EINVAL
);
4403 status
= ads_find_machine_acct(ads
, &res
, host
);
4404 if (!ADS_ERR_OK(status
)) {
4405 DEBUG(0, ("Host account for %s does not exist.\n", host
));
4410 msg
= ads_first_entry(ads
, res
);
4413 return ADS_ERROR_SYSTEM(ENOENT
);
4416 hostnameDN
= ads_get_dn(ads
, talloc_tos(), (LDAPMessage
*)msg
);
4417 if (hostnameDN
== NULL
) {
4419 return ADS_ERROR_SYSTEM(ENOENT
);
4422 rc
= ldap_delete_ext_s(ads
->ldap
.ld
, hostnameDN
, pldap_control
, NULL
);
4424 DEBUG(3,("ldap_delete_ext_s failed with error code %d\n", rc
));
4426 DEBUG(3,("ldap_delete_ext_s succeeded with error code %d\n", rc
));
4429 if (rc
!= LDAP_SUCCESS
) {
4430 const char *attrs
[] = { "cn", NULL
};
4431 LDAPMessage
*msg_sub
;
4433 /* we only search with scope ONE, we do not expect any further
4434 * objects to be created deeper */
4436 status
= ads_do_search_retry(ads
, hostnameDN
,
4437 LDAP_SCOPE_ONELEVEL
,
4438 "(objectclass=*)", attrs
, &res
);
4440 if (!ADS_ERR_OK(status
)) {
4442 TALLOC_FREE(hostnameDN
);
4446 for (msg_sub
= ads_first_entry(ads
, res
); msg_sub
;
4447 msg_sub
= ads_next_entry(ads
, msg_sub
)) {
4451 if ((dn
= ads_get_dn(ads
, talloc_tos(), msg_sub
)) == NULL
) {
4453 TALLOC_FREE(hostnameDN
);
4454 return ADS_ERROR(LDAP_NO_MEMORY
);
4457 status
= ads_del_dn(ads
, dn
);
4458 if (!ADS_ERR_OK(status
)) {
4459 DEBUG(3,("failed to delete dn %s: %s\n", dn
, ads_errstr(status
)));
4462 TALLOC_FREE(hostnameDN
);
4469 /* there should be no subordinate objects anymore */
4470 status
= ads_do_search_retry(ads
, hostnameDN
,
4471 LDAP_SCOPE_ONELEVEL
,
4472 "(objectclass=*)", attrs
, &res
);
4474 if (!ADS_ERR_OK(status
) || ( (ads_count_replies(ads
, res
)) > 0 ) ) {
4476 TALLOC_FREE(hostnameDN
);
4480 /* delete hostnameDN now */
4481 status
= ads_del_dn(ads
, hostnameDN
);
4482 if (!ADS_ERR_OK(status
)) {
4484 DEBUG(3,("failed to delete dn %s: %s\n", hostnameDN
, ads_errstr(status
)));
4485 TALLOC_FREE(hostnameDN
);
4490 TALLOC_FREE(hostnameDN
);
4492 status
= ads_find_machine_acct(ads
, &res
, host
);
4493 if ((status
.error_type
== ENUM_ADS_ERROR_LDAP
) &&
4494 (status
.err
.rc
!= LDAP_NO_SUCH_OBJECT
)) {
4495 DEBUG(3, ("Failed to remove host account.\n"));
4505 * pull all token-sids from an LDAP dn
4506 * @param ads connection to ads server
4507 * @param mem_ctx TALLOC_CTX for allocating sid array
4508 * @param dn of LDAP object
4509 * @param user_sid pointer to struct dom_sid (objectSid)
4510 * @param primary_group_sid pointer to struct dom_sid (self composed)
4511 * @param sids pointer to sid array to allocate
4512 * @param num_sids counter of SIDs pulled
4513 * @return status of token query
4515 ADS_STATUS
ads_get_tokensids(ADS_STRUCT
*ads
,
4516 TALLOC_CTX
*mem_ctx
,
4518 struct dom_sid
*user_sid
,
4519 struct dom_sid
*primary_group_sid
,
4520 struct dom_sid
**sids
,
4524 LDAPMessage
*res
= NULL
;
4526 size_t tmp_num_sids
;
4527 struct dom_sid
*tmp_sids
;
4528 struct dom_sid tmp_user_sid
;
4529 struct dom_sid tmp_primary_group_sid
;
4531 const char *attrs
[] = {
4538 status
= ads_search_retry_dn(ads
, &res
, dn
, attrs
);
4539 if (!ADS_ERR_OK(status
)) {
4543 count
= ads_count_replies(ads
, res
);
4545 ads_msgfree(ads
, res
);
4546 return ADS_ERROR_LDAP(LDAP_NO_SUCH_OBJECT
);
4549 if (!ads_pull_sid(ads
, res
, "objectSid", &tmp_user_sid
)) {
4550 ads_msgfree(ads
, res
);
4551 return ADS_ERROR_LDAP(LDAP_NO_MEMORY
);
4554 if (!ads_pull_uint32(ads
, res
, "primaryGroupID", &pgid
)) {
4555 ads_msgfree(ads
, res
);
4556 return ADS_ERROR_LDAP(LDAP_NO_MEMORY
);
4560 /* hack to compose the primary group sid without knowing the
4563 struct dom_sid domsid
;
4565 sid_copy(&domsid
, &tmp_user_sid
);
4567 if (!sid_split_rid(&domsid
, NULL
)) {
4568 ads_msgfree(ads
, res
);
4569 return ADS_ERROR_LDAP(LDAP_NO_MEMORY
);
4572 if (!sid_compose(&tmp_primary_group_sid
, &domsid
, pgid
)) {
4573 ads_msgfree(ads
, res
);
4574 return ADS_ERROR_LDAP(LDAP_NO_MEMORY
);
4578 tmp_num_sids
= ads_pull_sids(ads
, mem_ctx
, res
, "tokenGroups", &tmp_sids
);
4580 if (tmp_num_sids
== 0 || !tmp_sids
) {
4581 ads_msgfree(ads
, res
);
4582 return ADS_ERROR_LDAP(LDAP_NO_MEMORY
);
4586 *num_sids
= tmp_num_sids
;
4594 *user_sid
= tmp_user_sid
;
4597 if (primary_group_sid
) {
4598 *primary_group_sid
= tmp_primary_group_sid
;
4601 DEBUG(10,("ads_get_tokensids: returned %d sids\n", (int)tmp_num_sids
+ 2));
4603 ads_msgfree(ads
, res
);
4604 return ADS_ERROR_LDAP(LDAP_SUCCESS
);
4608 * Find a sAMAccountName in LDAP
4609 * @param ads connection to ads server
4610 * @param mem_ctx TALLOC_CTX for allocating sid array
4611 * @param samaccountname to search
4612 * @param uac_ret uint32_t pointer userAccountControl attribute value
4613 * @param dn_ret pointer to dn
4614 * @return status of token query
4616 ADS_STATUS
ads_find_samaccount(ADS_STRUCT
*ads
,
4617 TALLOC_CTX
*mem_ctx
,
4618 const char *samaccountname
,
4620 const char **dn_ret
)
4623 const char *attrs
[] = { "userAccountControl", NULL
};
4625 LDAPMessage
*res
= NULL
;
4629 filter
= talloc_asprintf(mem_ctx
, "(&(objectclass=user)(sAMAccountName=%s))",
4631 if (filter
== NULL
) {
4632 status
= ADS_ERROR_NT(NT_STATUS_NO_MEMORY
);
4636 status
= ads_do_search_all(ads
, ads
->config
.bind_path
,
4638 filter
, attrs
, &res
);
4640 if (!ADS_ERR_OK(status
)) {
4644 if (ads_count_replies(ads
, res
) != 1) {
4645 status
= ADS_ERROR(LDAP_NO_RESULTS_RETURNED
);
4649 dn
= ads_get_dn(ads
, talloc_tos(), res
);
4651 status
= ADS_ERROR(LDAP_NO_MEMORY
);
4655 if (!ads_pull_uint32(ads
, res
, "userAccountControl", &uac
)) {
4656 status
= ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE
);
4665 *dn_ret
= talloc_strdup(mem_ctx
, dn
);
4667 status
= ADS_ERROR(LDAP_NO_MEMORY
);
4673 ads_msgfree(ads
, res
);
4679 * find our configuration path
4680 * @param ads connection to ads server
4681 * @param mem_ctx Pointer to talloc context
4682 * @param config_path Pointer to the config path
4683 * @return status of search
4685 ADS_STATUS
ads_config_path(ADS_STRUCT
*ads
,
4686 TALLOC_CTX
*mem_ctx
,
4690 LDAPMessage
*res
= NULL
;
4691 const char *config_context
= NULL
;
4692 const char *attrs
[] = { "configurationNamingContext", NULL
};
4694 status
= ads_do_search(ads
, "", LDAP_SCOPE_BASE
,
4695 "(objectclass=*)", attrs
, &res
);
4696 if (!ADS_ERR_OK(status
)) {
4700 config_context
= ads_pull_string(ads
, mem_ctx
, res
,
4701 "configurationNamingContext");
4702 ads_msgfree(ads
, res
);
4703 if (!config_context
) {
4704 return ADS_ERROR(LDAP_NO_MEMORY
);
4708 *config_path
= talloc_strdup(mem_ctx
, config_context
);
4709 if (!*config_path
) {
4710 return ADS_ERROR(LDAP_NO_MEMORY
);
4714 return ADS_ERROR(LDAP_SUCCESS
);
4718 * find the displayName of an extended right
4719 * @param ads connection to ads server
4720 * @param config_path The config path
4721 * @param mem_ctx Pointer to talloc context
4722 * @param GUID struct of the rightsGUID
4723 * @return status of search
4725 const char *ads_get_extended_right_name_by_guid(ADS_STRUCT
*ads
,
4726 const char *config_path
,
4727 TALLOC_CTX
*mem_ctx
,
4728 const struct GUID
*rights_guid
)
4731 LDAPMessage
*res
= NULL
;
4733 const char *attrs
[] = { "displayName", NULL
};
4734 const char *result
= NULL
;
4737 if (!ads
|| !mem_ctx
|| !rights_guid
) {
4741 expr
= talloc_asprintf(mem_ctx
, "(rightsGuid=%s)",
4742 GUID_string(mem_ctx
, rights_guid
));
4747 path
= talloc_asprintf(mem_ctx
, "cn=Extended-Rights,%s", config_path
);
4752 rc
= ads_do_search_retry(ads
, path
, LDAP_SCOPE_SUBTREE
,
4754 if (!ADS_ERR_OK(rc
)) {
4758 if (ads_count_replies(ads
, res
) != 1) {
4762 result
= ads_pull_string(ads
, mem_ctx
, res
, "displayName");
4765 ads_msgfree(ads
, res
);
4770 * verify or build and verify an account ou
4771 * @param mem_ctx Pointer to talloc context
4772 * @param ads connection to ads server
4774 * @return status of search
4777 ADS_STATUS
ads_check_ou_dn(TALLOC_CTX
*mem_ctx
,
4779 const char **account_ou
)
4785 if (account_ou
== NULL
) {
4786 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER
);
4789 if (*account_ou
!= NULL
) {
4790 exploded_dn
= ldap_explode_dn(*account_ou
, 0);
4792 ldap_value_free(exploded_dn
);
4797 ou_string
= ads_ou_string(ads
, *account_ou
);
4799 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX
);
4802 name
= talloc_asprintf(mem_ctx
, "%s,%s", ou_string
,
4803 ads
->config
.bind_path
);
4804 SAFE_FREE(ou_string
);
4807 return ADS_ERROR_LDAP(LDAP_NO_MEMORY
);
4810 exploded_dn
= ldap_explode_dn(name
, 0);
4812 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX
);
4814 ldap_value_free(exploded_dn
);