2 Unix SMB/CIFS implementation.
3 ads (active directory) utility library
4 Copyright (C) Andrew Tridgell 2001
5 Copyright (C) Remus Koos 2001
6 Copyright (C) Jim McDonough <jmcd@us.ibm.com> 2002
7 Copyright (C) Guenther Deschner 2005
8 Copyright (C) Gerald Carter 2006
10 This program is free software; you can redistribute it and/or modify
11 it under the terms of the GNU General Public License as published by
12 the Free Software Foundation; either version 3 of the License, or
13 (at your option) any later version.
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License for more details.
20 You should have received a copy of the GNU General Public License
21 along with this program. If not, see <http://www.gnu.org/licenses/>.
26 #include "libads/sitename_cache.h"
27 #include "libads/cldap.h"
28 #include "libads/netlogon_ping.h"
29 #include "../lib/tsocket/tsocket.h"
30 #include "../lib/addns/dnsquery.h"
31 #include "../libds/common/flags.h"
33 #include "../libcli/security/security.h"
34 #include "../librpc/gen_ndr/netlogon.h"
35 #include "lib/param/loadparm.h"
36 #include "libsmb/namequery.h"
37 #include "../librpc/gen_ndr/ndr_ads.h"
38 #include "auth/credentials/credentials.h"
45 * @brief basic ldap client-side routines for ads server communications
47 * The routines contained here should do the necessary ldap calls for
50 * Important note: attribute names passed into ads_ routines must
51 * already be in UTF-8 format. We do not convert them because in almost
52 * all cases, they are just ascii (which is represented with the same
53 * codepoints in UTF-8). This may have to change at some point
57 #define LDAP_SERVER_TREE_DELETE_OID "1.2.840.113556.1.4.805"
59 static SIG_ATOMIC_T gotalarm
;
61 /***************************************************************
62 Signal function to tell us we timed out.
63 ****************************************************************/
65 static void gotalarm_sig(int signum
)
70 LDAP
*ldap_open_with_timeout(const char *server
,
71 struct sockaddr_storage
*ss
,
72 int port
, unsigned int to
)
78 DEBUG(10, ("Opening connection to LDAP server '%s:%d', timeout "
79 "%u seconds\n", server
, port
, to
));
84 CatchSignal(SIGALRM
, gotalarm_sig
);
86 /* End setup timeout. */
89 if ( strchr_m(server
, ':') ) {
91 uri
= talloc_asprintf(talloc_tos(), "ldap://[%s]:%u", server
, port
);
94 uri
= talloc_asprintf(talloc_tos(), "ldap://%s:%u", server
, port
);
100 #ifdef HAVE_LDAP_INIT_FD
103 NTSTATUS status
= NT_STATUS_UNSUCCESSFUL
;
104 unsigned timeout_ms
= 1000 * to
;
106 status
= open_socket_out(ss
, port
, timeout_ms
, &fd
);
107 if (!NT_STATUS_IS_OK(status
)) {
108 DEBUG(3, ("open_socket_out: failed to open socket\n"));
112 /* define LDAP_PROTO_TCP from openldap.h if required */
113 #ifndef LDAP_PROTO_TCP
114 #define LDAP_PROTO_TCP 1
116 ldap_err
= ldap_init_fd(fd
, LDAP_PROTO_TCP
, uri
, &ldp
);
118 #elif defined(HAVE_LDAP_INITIALIZE)
119 ldap_err
= ldap_initialize(&ldp
, uri
);
121 ldp
= ldap_open(server
, port
);
123 ldap_err
= LDAP_SUCCESS
;
125 ldap_err
= LDAP_OTHER
;
128 if (ldap_err
!= LDAP_SUCCESS
) {
129 DEBUG(2,("Could not initialize connection for LDAP server '%s': %s\n",
130 uri
, ldap_err2string(ldap_err
)));
132 DEBUG(10, ("Initialized connection for LDAP server '%s'\n", uri
));
136 /* Teardown timeout. */
138 CatchSignal(SIGALRM
, SIG_IGN
);
144 static int ldap_search_with_timeout(LDAP
*ld
,
145 LDAP_CONST
char *base
,
147 LDAP_CONST
char *filter
,
150 LDAPControl
**sctrls
,
151 LDAPControl
**cctrls
,
155 int to
= lp_ldap_timeout();
156 struct timeval timeout
;
157 struct timeval
*timeout_ptr
= NULL
;
160 DBG_DEBUG("ldap_search: base => [%s], filter => [%s], scope => [%d]\n",
165 /* Setup timeout for the ldap_search_ext_s call - local and remote. */
171 timeout_ptr
= &timeout
;
173 /* Setup alarm timeout. */
174 CatchSignal(SIGALRM
, gotalarm_sig
);
175 /* Make the alarm time one second beyond
176 the timeout we're setting for the
177 remote search timeout, to allow that
178 to fire in preference. */
180 /* End setup timeout. */
184 result
= ldap_search_ext_s(ld
, base
, scope
, filter
, attrs
,
185 attrsonly
, sctrls
, cctrls
, timeout_ptr
,
189 /* Teardown alarm timeout. */
190 CatchSignal(SIGALRM
, SIG_IGN
);
195 return LDAP_TIMELIMIT_EXCEEDED
;
198 * A bug in OpenLDAP means ldap_search_ext_s can return
199 * LDAP_SUCCESS but with a NULL res pointer. Cope with
200 * this. See bug #6279 for details. JRA.
204 return LDAP_TIMELIMIT_EXCEEDED
;
210 /**********************************************
211 Do client and server sitename match ?
212 **********************************************/
214 bool ads_sitename_match(ADS_STRUCT
*ads
)
216 if (ads
->config
.server_site_name
== NULL
&&
217 ads
->config
.client_site_name
== NULL
) {
218 DEBUG(10,("ads_sitename_match: both null\n"));
221 if (ads
->config
.server_site_name
&&
222 ads
->config
.client_site_name
&&
223 strequal(ads
->config
.server_site_name
,
224 ads
->config
.client_site_name
)) {
225 DEBUG(10,("ads_sitename_match: name %s match\n", ads
->config
.server_site_name
));
228 DEBUG(10,("ads_sitename_match: no match between server: %s and client: %s\n",
229 ads
->config
.server_site_name
? ads
->config
.server_site_name
: "NULL",
230 ads
->config
.client_site_name
? ads
->config
.client_site_name
: "NULL"));
234 /**********************************************
235 Is this the closest DC ?
236 **********************************************/
238 bool ads_closest_dc(ADS_STRUCT
*ads
)
240 if (ads
->config
.flags
& NBT_SERVER_CLOSEST
) {
241 DEBUG(10,("ads_closest_dc: NBT_SERVER_CLOSEST flag set\n"));
245 /* not sure if this can ever happen */
246 if (ads_sitename_match(ads
)) {
247 DEBUG(10,("ads_closest_dc: NBT_SERVER_CLOSEST flag not set but sites match\n"));
251 if (ads
->config
.client_site_name
== NULL
) {
252 DEBUG(10,("ads_closest_dc: client belongs to no site\n"));
256 DEBUG(10,("ads_closest_dc: %s is not the closest DC\n",
257 ads
->config
.ldap_server_name
));
262 static bool ads_fill_cldap_reply(ADS_STRUCT
*ads
,
264 const struct sockaddr_storage
*ss
,
265 const struct NETLOGON_SAM_LOGON_RESPONSE_EX
*cldap_reply
)
267 TALLOC_CTX
*frame
= talloc_stackframe();
269 char addr
[INET6_ADDRSTRLEN
];
273 print_sockaddr(addr
, sizeof(addr
), ss
);
275 /* Check the CLDAP reply flags */
277 /* Fill in the ads->config values */
279 ADS_TALLOC_CONST_FREE(ads
->config
.workgroup
);
280 ADS_TALLOC_CONST_FREE(ads
->config
.realm
);
281 ADS_TALLOC_CONST_FREE(ads
->config
.bind_path
);
282 ADS_TALLOC_CONST_FREE(ads
->config
.ldap_server_name
);
283 ADS_TALLOC_CONST_FREE(ads
->config
.server_site_name
);
284 ADS_TALLOC_CONST_FREE(ads
->config
.client_site_name
);
286 ads
->config
.ldap_server_name
= talloc_strdup(ads
,
287 cldap_reply
->pdc_dns_name
);
288 if (ads
->config
.ldap_server_name
== NULL
) {
289 DBG_WARNING("Out of memory\n");
294 ads
->config
.workgroup
= talloc_strdup(ads
, cldap_reply
->domain_name
);
295 if (ads
->config
.workgroup
== NULL
) {
296 DBG_WARNING("Out of memory\n");
301 ads
->config
.realm
= talloc_asprintf_strupper_m(ads
,
303 cldap_reply
->dns_domain
);
304 if (ads
->config
.realm
== NULL
) {
305 DBG_WARNING("Out of memory\n");
310 status
= ads_build_dn(ads
->config
.realm
, ads
, &dn
);
311 if (!ADS_ERR_OK(status
)) {
312 DBG_DEBUG("Failed to build bind path: %s\n",
317 ads
->config
.bind_path
= dn
;
319 if (*cldap_reply
->server_site
) {
320 ads
->config
.server_site_name
=
321 talloc_strdup(ads
, cldap_reply
->server_site
);
322 if (ads
->config
.server_site_name
== NULL
) {
323 DBG_WARNING("Out of memory\n");
329 if (*cldap_reply
->client_site
) {
330 ads
->config
.client_site_name
=
331 talloc_strdup(ads
, cldap_reply
->client_site
);
332 if (ads
->config
.client_site_name
== NULL
) {
333 DBG_WARNING("Out of memory\n");
339 ads
->ldap
.port
= gc
? LDAP_GC_PORT
: LDAP_PORT
;
342 /* Store our site name. */
343 sitename_store(cldap_reply
->domain_name
, cldap_reply
->client_site
);
344 sitename_store(cldap_reply
->dns_domain
, cldap_reply
->client_site
);
346 /* Leave this until last so that the flags are not clobbered */
347 ads
->config
.flags
= cldap_reply
->server_type
;
358 try a connection to a given ldap server, returning True and setting the servers IP
359 in the ads struct if successful
361 static bool ads_try_connect(ADS_STRUCT
*ads
, bool gc
,
362 struct sockaddr_storage
*ss
)
364 struct NETLOGON_SAM_LOGON_RESPONSE_EX cldap_reply
= {};
365 TALLOC_CTX
*frame
= talloc_stackframe();
367 char addr
[INET6_ADDRSTRLEN
] = { 0, };
374 print_sockaddr(addr
, sizeof(addr
), ss
);
376 DBG_INFO("ads_try_connect: sending CLDAP request to %s (realm: %s)\n",
377 addr
, ads
->server
.realm
);
379 ok
= ads_cldap_netlogon_5(frame
,
382 ads
->config
.flags
| DS_ONLY_LDAP_NEEDED
,
385 DBG_NOTICE("ads_cldap_netlogon_5(%s, %s) failed.\n",
386 addr
, ads
->server
.realm
);
391 ok
= ads_fill_cldap_reply(ads
, gc
, ss
, &cldap_reply
);
393 DBG_NOTICE("ads_fill_cldap_reply(%s, %s) failed.\n",
394 addr
, ads
->server
.realm
);
403 /**********************************************************************
404 send a cldap ping to list of servers, one at a time, until one of
405 them answers it's an ldap server. Record success in the ADS_STRUCT.
406 Take note of and update negative connection cache.
407 **********************************************************************/
409 static NTSTATUS
cldap_ping_list(ADS_STRUCT
*ads
,
411 struct samba_sockaddr
*sa_list
,
414 TALLOC_CTX
*frame
= talloc_stackframe();
415 struct timeval endtime
= timeval_current_ofs(MAX(3,lp_ldap_timeout()/2), 0);
416 uint32_t nt_version
= NETLOGON_NT_VERSION_5
| NETLOGON_NT_VERSION_5EX
;
417 struct tsocket_address
**ts_list
= NULL
;
418 struct samba_sockaddr
**req_sa_list
= NULL
;
419 struct netlogon_samlogon_response
**responses
= NULL
;
420 size_t num_requests
= 0;
426 ts_list
= talloc_zero_array(frame
,
427 struct tsocket_address
*,
429 if (ts_list
== NULL
) {
431 return NT_STATUS_NO_MEMORY
;
434 req_sa_list
= talloc_zero_array(frame
,
435 struct samba_sockaddr
*,
437 if (req_sa_list
== NULL
) {
439 return NT_STATUS_NO_MEMORY
;
444 * The retry loop is bound by the timeout
449 for (i
= 0; i
< count
; i
++) {
450 char server
[INET6_ADDRSTRLEN
];
453 if (is_zero_addr(&sa_list
[i
].u
.ss
)) {
457 print_sockaddr(server
, sizeof(server
), &sa_list
[i
].u
.ss
);
459 status
= check_negative_conn_cache(domain
, server
);
460 if (!NT_STATUS_IS_OK(status
)) {
464 ret
= tsocket_address_inet_from_strings(ts_list
, "ip",
466 &ts_list
[num_requests
]);
468 status
= map_nt_error_from_unix(errno
);
469 DBG_WARNING("Failed to create tsocket_address for %s - %s\n",
470 server
, nt_errstr(status
));
475 req_sa_list
[num_requests
] = &sa_list
[i
];
479 DBG_DEBUG("Try to create %zu netlogon connections for domain '%s' "
480 "(provided count of addresses was %zu).\n",
485 if (num_requests
== 0) {
486 status
= NT_STATUS_NO_LOGON_SERVERS
;
487 DBG_WARNING("domain[%s] num_requests[%zu] for count[%zu] - %s\n",
488 domain
, num_requests
, count
, nt_errstr(status
));
493 status
= netlogon_pings(frame
, /* mem_ctx */
494 lp_client_netlogon_ping_protocol(), /* proto */
495 ts_list
, /* servers */
496 num_requests
, /* num_servers */
497 (struct netlogon_ping_filter
){
498 .ntversion
= nt_version
,
499 .domain
= ads
->server
.realm
,
501 .required_flags
= ads
->config
.flags
|
505 endtime
, /* timeout */
507 if (!NT_STATUS_IS_OK(status
)) {
508 DBG_WARNING("netlogon_pings(realm=%s, num_requests=%zu) "
509 "for count[%zu] - %s\n",
514 return NT_STATUS_NO_LOGON_SERVERS
;
517 for (i
= 0; i
< num_requests
; i
++) {
518 struct NETLOGON_SAM_LOGON_RESPONSE_EX
*cldap_reply
= NULL
;
519 char server
[INET6_ADDRSTRLEN
];
521 if (responses
[i
] == NULL
) {
525 print_sockaddr(server
, sizeof(server
), &req_sa_list
[i
]->u
.ss
);
527 if (responses
[i
]->ntver
!= NETLOGON_NT_VERSION_5EX
) {
528 DBG_NOTICE("realm=[%s] nt_version mismatch: 0x%08x for %s\n",
530 responses
[i
]->ntver
, server
);
534 cldap_reply
= &responses
[i
]->data
.nt5_ex
;
536 /* Returns ok only if it matches the correct server type */
537 ok
= ads_fill_cldap_reply(ads
,
539 &req_sa_list
[i
]->u
.ss
,
542 DBG_DEBUG("realm[%s]: selected %s => %s\n",
544 server
, cldap_reply
->pdc_dns_name
);
545 if (CHECK_DEBUGLVL(DBGLVL_DEBUG
)) {
546 NDR_PRINT_DEBUG(NETLOGON_SAM_LOGON_RESPONSE_EX
,
553 DBG_NOTICE("realm[%s] server %s %s - not usable\n",
555 server
, cldap_reply
->pdc_dns_name
);
556 if (CHECK_DEBUGLVL(DBGLVL_NOTICE
)) {
557 NDR_PRINT_DEBUG(NETLOGON_SAM_LOGON_RESPONSE_EX
,
560 add_failed_connection_entry(domain
, server
,
561 NT_STATUS_CLIENT_SERVER_PARAMETERS_INVALID
);
568 expired
= timeval_expired(&endtime
);
574 /* keep track of failures as all were not suitable */
575 for (i
= 0; i
< num_requests
; i
++) {
576 char server
[INET6_ADDRSTRLEN
];
578 print_sockaddr(server
, sizeof(server
), &req_sa_list
[i
]->u
.ss
);
580 add_failed_connection_entry(domain
, server
,
581 NT_STATUS_UNSUCCESSFUL
);
584 status
= NT_STATUS_NO_LOGON_SERVERS
;
585 DBG_WARNING("realm[%s] no valid response "
586 "num_requests[%zu] for count[%zu] - %s\n",
588 num_requests
, count
, nt_errstr(status
));
590 return NT_STATUS_NO_LOGON_SERVERS
;
593 /***************************************************************************
594 resolve a name and perform an "ldap ping" using NetBIOS and related methods
595 ****************************************************************************/
597 static NTSTATUS
resolve_and_ping_netbios(ADS_STRUCT
*ads
,
598 const char *domain
, const char *realm
)
602 struct samba_sockaddr
*sa_list
= NULL
;
605 DEBUG(6, ("resolve_and_ping_netbios: (cldap) looking for domain '%s'\n",
608 status
= get_sorted_dc_list(talloc_tos(),
614 if (!NT_STATUS_IS_OK(status
)) {
618 /* remove servers which are known to be dead based on
619 the corresponding DNS method */
621 for (i
= 0; i
< count
; ++i
) {
622 char server
[INET6_ADDRSTRLEN
];
624 print_sockaddr(server
, sizeof(server
), &sa_list
[i
].u
.ss
);
627 check_negative_conn_cache(realm
, server
))) {
628 /* Ensure we add the workgroup name for this
629 IP address as negative too. */
630 add_failed_connection_entry(
632 NT_STATUS_UNSUCCESSFUL
);
637 status
= cldap_ping_list(ads
, domain
, sa_list
, count
);
639 TALLOC_FREE(sa_list
);
645 /**********************************************************************
646 resolve a name and perform an "ldap ping" using DNS
647 **********************************************************************/
649 static NTSTATUS
resolve_and_ping_dns(ADS_STRUCT
*ads
, const char *sitename
,
653 struct samba_sockaddr
*sa_list
= NULL
;
656 DEBUG(6, ("resolve_and_ping_dns: (cldap) looking for realm '%s'\n",
659 status
= get_sorted_dc_list(talloc_tos(),
665 if (!NT_STATUS_IS_OK(status
)) {
666 TALLOC_FREE(sa_list
);
670 status
= cldap_ping_list(ads
, realm
, sa_list
, count
);
672 TALLOC_FREE(sa_list
);
677 /**********************************************************************
678 Try to find an AD dc using our internal name resolution routines
679 Try the realm first and then the workgroup name if netbios is not
681 **********************************************************************/
683 static NTSTATUS
ads_find_dc(ADS_STRUCT
*ads
)
685 const char *c_domain
= "";
687 bool use_own_domain
= False
;
688 char *sitename
= NULL
;
689 NTSTATUS status
= NT_STATUS_UNSUCCESSFUL
;
692 /* if the realm and workgroup are both empty, assume they are ours */
695 c_realm
= ads
->server
.realm
;
701 /* special case where no realm and no workgroup means our own */
702 if ( !ads
->server
.workgroup
|| !*ads
->server
.workgroup
) {
703 use_own_domain
= True
;
704 c_realm
= lp_realm();
708 if (!lp_disable_netbios()) {
709 if (use_own_domain
) {
710 c_domain
= lp_workgroup();
712 c_domain
= ads
->server
.workgroup
;
713 if (!*c_realm
&& (!c_domain
|| !*c_domain
)) {
714 c_domain
= lp_workgroup();
723 if (!*c_realm
&& !*c_domain
) {
724 DEBUG(0, ("ads_find_dc: no realm or workgroup! Don't know "
726 return NT_STATUS_INVALID_PARAMETER
; /* rather need MISSING_PARAMETER ... */
730 * In case of LDAP we use get_dc_name() as that
731 * creates the custom krb5.conf file
733 if (ads
->auth
.flags
& ADS_AUTH_GENERATE_KRB5_CONFIG
) {
735 struct sockaddr_storage ip_out
;
737 DEBUG(6, ("ads_find_dc: (ldap) looking for realm '%s'"
738 " and falling back to domain '%s'\n",
741 ok
= get_dc_name(c_domain
, c_realm
, srv_name
, &ip_out
);
743 if (is_zero_addr(&ip_out
)) {
744 return NT_STATUS_NO_LOGON_SERVERS
;
748 * we call ads_try_connect() to fill in the
749 * ads->config details
751 ok
= ads_try_connect(ads
, false, &ip_out
);
757 return NT_STATUS_NO_LOGON_SERVERS
;
761 sitename
= sitename_fetch(talloc_tos(), c_realm
);
762 status
= resolve_and_ping_dns(ads
, sitename
, c_realm
);
764 if (NT_STATUS_IS_OK(status
)) {
765 TALLOC_FREE(sitename
);
769 /* In case we failed to contact one of our closest DC on our
771 * need to try to find another DC, retry with a site-less SRV
776 DEBUG(3, ("ads_find_dc: failed to find a valid DC on "
777 "our site (%s), Trying to find another DC "
778 "for realm '%s' (domain '%s')\n",
779 sitename
, c_realm
, c_domain
));
780 namecache_delete(c_realm
, 0x1C);
782 resolve_and_ping_dns(ads
, NULL
, c_realm
);
784 if (NT_STATUS_IS_OK(status
)) {
785 TALLOC_FREE(sitename
);
790 TALLOC_FREE(sitename
);
793 /* try netbios as fallback - if permitted,
794 or if configuration specifically requests it */
797 DEBUG(3, ("ads_find_dc: falling back to netbios "
798 "name resolution for domain '%s' (realm '%s')\n",
802 status
= resolve_and_ping_netbios(ads
, c_domain
, c_realm
);
803 if (NT_STATUS_IS_OK(status
)) {
808 DEBUG(1, ("ads_find_dc: "
809 "name resolution for realm '%s' (domain '%s') failed: %s\n",
810 c_realm
, c_domain
, nt_errstr(status
)));
815 * Connect to the LDAP server
816 * @param ads Pointer to an existing ADS_STRUCT
817 * @return status of connection
819 static ADS_STATUS
ads_connect_internal(ADS_STRUCT
*ads
,
820 struct cli_credentials
*creds
)
822 int version
= LDAP_VERSION3
;
825 char addr
[INET6_ADDRSTRLEN
];
826 struct sockaddr_storage existing_ss
;
828 bool start_tls
= false;
830 zero_sockaddr(&existing_ss
);
832 if (!(ads
->auth
.flags
& ADS_AUTH_NO_BIND
)) {
833 SMB_ASSERT(creds
!= NULL
);
836 if (ads
->auth
.flags
& ADS_AUTH_ANON_BIND
) {
838 * Simple anonyous binds are only
839 * allowed for anonymous credentials
841 SMB_ASSERT(cli_credentials_is_anonymous(creds
));
844 if (!(ads
->auth
.flags
& (ADS_AUTH_NO_BIND
|ADS_AUTH_ANON_BIND
))) {
845 ads
->auth
.flags
|= ADS_AUTH_GENERATE_KRB5_CONFIG
;
849 * ads_connect can be passed in a reused ADS_STRUCT
850 * with an existing non-zero ads->ldap.ss IP address
851 * that was stored by going through ads_find_dc()
852 * if ads->server.ldap_server was NULL.
854 * If ads->server.ldap_server is still NULL but
855 * the target address isn't the zero address, then
856 * store that address off off before zeroing out
857 * ads->ldap so we don't keep doing multiple calls
858 * to ads_find_dc() in the reuse case.
860 * If a caller wants a clean ADS_STRUCT they
861 * will TALLOC_FREE it and allocate a new one
862 * by calling ads_init(), which ensures
863 * ads->ldap.ss is a properly zero'ed out valid IP
866 if (ads
->server
.ldap_server
== NULL
&& !is_zero_addr(&ads
->ldap
.ss
)) {
867 /* Save off the address we previously found by ads_find_dc(). */
868 existing_ss
= ads
->ldap
.ss
;
872 ZERO_STRUCT(ads
->ldap_tls_data
);
873 ZERO_STRUCT(ads
->ldap_wrap_data
);
874 ads
->ldap
.last_attempt
= time_mono(NULL
);
875 ads
->ldap_wrap_data
.wrap_type
= ADS_SASLWRAP_TYPE_PLAIN
;
877 /* try with a user specified server */
879 if (DEBUGLEVEL
>= 11) {
880 char *s
= NDR_PRINT_STRUCT_STRING(talloc_tos(), ads_struct
, ads
);
881 DEBUG(11,("ads_connect: entering\n"));
882 DEBUGADD(11,("%s\n", s
));
886 if (ads
->server
.ldap_server
) {
888 struct sockaddr_storage ss
;
890 DBG_DEBUG("Resolving name of LDAP server '%s'.\n",
891 ads
->server
.ldap_server
);
892 ok
= resolve_name(ads
->server
.ldap_server
, &ss
, 0x20, true);
894 DEBUG(5,("ads_connect: unable to resolve name %s\n",
895 ads
->server
.ldap_server
));
896 status
= ADS_ERROR_NT(NT_STATUS_NOT_FOUND
);
900 if (is_zero_addr(&ss
)) {
901 status
= ADS_ERROR_NT(NT_STATUS_NOT_FOUND
);
905 ok
= ads_try_connect(ads
, ads
->server
.gc
, &ss
);
910 /* The choice of which GC use is handled one level up in
911 ads_connect_gc(). If we continue on from here with
912 ads_find_dc() we will get GC searches on port 389 which
913 doesn't work. --jerry */
915 if (ads
->server
.gc
== true) {
916 return ADS_ERROR(LDAP_OPERATIONS_ERROR
);
919 if (ads
->server
.no_fallback
) {
920 status
= ADS_ERROR_NT(NT_STATUS_NOT_FOUND
);
925 if (!is_zero_addr(&existing_ss
)) {
926 /* We saved off who we should talk to. */
927 bool ok
= ads_try_connect(ads
,
934 * Keep trying to find a server and fall through
935 * into ads_find_dc() again.
937 DBG_DEBUG("Failed to connect to DC via LDAP server IP address, "
938 "trying to find another DC.\n");
941 ntstatus
= ads_find_dc(ads
);
942 if (NT_STATUS_IS_OK(ntstatus
)) {
946 status
= ADS_ERROR_NT(ntstatus
);
951 print_sockaddr(addr
, sizeof(addr
), &ads
->ldap
.ss
);
952 DEBUG(3,("Successfully contacted LDAP server %s\n", addr
));
954 if (!ads
->auth
.kdc_server
) {
955 print_sockaddr(addr
, sizeof(addr
), &ads
->ldap
.ss
);
956 ads
->auth
.kdc_server
= talloc_strdup(ads
, addr
);
957 if (ads
->auth
.kdc_server
== NULL
) {
958 status
= ADS_ERROR_NT(NT_STATUS_NO_MEMORY
);
963 /* If the caller() requested no LDAP bind, then we are done */
965 if (ads
->auth
.flags
& ADS_AUTH_NO_BIND
) {
966 status
= ADS_SUCCESS
;
970 ads
->ldap_tls_data
.mem_ctx
= talloc_init("ads LDAP TLS connection memory");
971 if (!ads
->ldap_tls_data
.mem_ctx
) {
972 status
= ADS_ERROR_NT(NT_STATUS_NO_MEMORY
);
976 ads
->ldap_wrap_data
.mem_ctx
= talloc_init("ads LDAP connection memory");
977 if (!ads
->ldap_wrap_data
.mem_ctx
) {
978 status
= ADS_ERROR_NT(NT_STATUS_NO_MEMORY
);
982 /* Otherwise setup the TCP LDAP session */
984 if (ads
->auth
.flags
& ADS_AUTH_SASL_LDAPS
) {
986 ads
->ldap
.port
= 636;
987 } else if (ads
->auth
.flags
& ADS_AUTH_SASL_STARTTLS
) {
990 ads
->ldap
.port
= 389;
992 ads
->ldap
.port
= 389;
995 ads
->ldap
.ld
= ldap_open_with_timeout(ads
->config
.ldap_server_name
,
997 ads
->ldap
.port
, lp_ldap_timeout());
998 if (ads
->ldap
.ld
== NULL
) {
999 status
= ADS_ERROR(LDAP_OPERATIONS_ERROR
);
1002 DEBUG(3,("Connected to LDAP server %s\n", ads
->config
.ldap_server_name
));
1004 ldap_set_option(ads
->ldap
.ld
, LDAP_OPT_PROTOCOL_VERSION
, &version
);
1007 unsigned int to
= lp_ldap_connection_timeout();
1008 struct berval
*rspdata
= NULL
;
1009 char *rspoid
= NULL
;
1015 CatchSignal(SIGALRM
, gotalarm_sig
);
1017 /* End setup timeout. */
1020 rc
= ldap_extended_operation_s(ads
->ldap
.ld
,
1021 LDAP_EXOP_START_TLS
,
1027 if (gotalarm
!= 0 && rc
== LDAP_SUCCESS
) {
1032 /* Teardown timeout. */
1034 CatchSignal(SIGALRM
, SIG_IGN
);
1037 if (rspoid
!= NULL
) {
1038 ldap_memfree(rspoid
);
1041 if (rspdata
!= NULL
) {
1042 ber_bvfree(rspdata
);
1045 if (rc
!= LDAP_SUCCESS
) {
1046 status
= ADS_ERROR_LDAP(rc
);
1052 unsigned int to
= lp_ldap_connection_timeout();
1057 CatchSignal(SIGALRM
, gotalarm_sig
);
1059 /* End setup timeout. */
1062 status
= ads_setup_tls_wrapping(&ads
->ldap_tls_data
,
1064 ads
->config
.ldap_server_name
);
1067 /* Teardown timeout. */
1069 CatchSignal(SIGALRM
, SIG_IGN
);
1072 if ( !ADS_ERR_OK(status
) ) {
1077 /* cache the successful connection for workgroup and realm */
1078 if (ads_closest_dc(ads
)) {
1079 saf_store( ads
->server
.workgroup
, ads
->config
.ldap_server_name
);
1080 saf_store( ads
->server
.realm
, ads
->config
.ldap_server_name
);
1083 /* fill in the current time and offsets */
1085 status
= ads_current_time( ads
);
1086 if ( !ADS_ERR_OK(status
) ) {
1090 /* Now do the bind */
1092 if (ads
->auth
.flags
& ADS_AUTH_ANON_BIND
) {
1093 status
= ADS_ERROR(ldap_simple_bind_s(ads
->ldap
.ld
, NULL
, NULL
));
1097 status
= ads_sasl_bind(ads
, creds
);
1100 if (DEBUGLEVEL
>= 11) {
1101 char *s
= NDR_PRINT_STRUCT_STRING(talloc_tos(), ads_struct
, ads
);
1102 DEBUG(11,("ads_connect: leaving with: %s\n",
1103 ads_errstr(status
)));
1104 DEBUGADD(11,("%s\n", s
));
1112 * Connect to the LDAP server using without a bind
1113 * and without a tcp connection at all
1115 * @param ads Pointer to an existing ADS_STRUCT
1116 * @return status of connection
1118 ADS_STATUS
ads_connect_cldap_only(ADS_STRUCT
*ads
)
1120 ads
->auth
.flags
|= ADS_AUTH_NO_BIND
;
1121 return ads_connect_internal(ads
, NULL
);
1125 * Connect to the LDAP server
1126 * @param ads Pointer to an existing ADS_STRUCT
1127 * @return status of connection
1129 ADS_STATUS
ads_connect_creds(ADS_STRUCT
*ads
, struct cli_credentials
*creds
)
1131 SMB_ASSERT(creds
!= NULL
);
1134 * We allow upgrades from
1135 * ADS_AUTH_NO_BIND if credentials
1138 ads
->auth
.flags
&= ~ADS_AUTH_NO_BIND
;
1141 * We allow upgrades from ADS_AUTH_ANON_BIND,
1142 * as we don't want to use simple binds with
1143 * non-anon credentials
1145 if (!cli_credentials_is_anonymous(creds
)) {
1146 ads
->auth
.flags
&= ~ADS_AUTH_ANON_BIND
;
1149 return ads_connect_internal(ads
, creds
);
1153 * Connect to the LDAP server using anonymous credentials
1154 * using a simple bind without username/password
1156 * @param ads Pointer to an existing ADS_STRUCT
1157 * @return status of connection
1159 ADS_STATUS
ads_connect_simple_anon(ADS_STRUCT
*ads
)
1161 TALLOC_CTX
*frame
= talloc_stackframe();
1162 struct cli_credentials
*creds
= NULL
;
1165 creds
= cli_credentials_init_anon(frame
);
1166 if (creds
== NULL
) {
1168 return ADS_ERROR_SYSTEM(errno
);
1171 ads
->auth
.flags
|= ADS_AUTH_ANON_BIND
;
1172 status
= ads_connect_creds(ads
, creds
);
1178 * Connect to the LDAP server using the machine account
1179 * @param ads Pointer to an existing ADS_STRUCT
1180 * @return status of connection
1182 ADS_STATUS
ads_connect_machine(ADS_STRUCT
*ads
)
1184 TALLOC_CTX
*frame
= talloc_stackframe();
1185 struct cli_credentials
*creds
= NULL
;
1189 ntstatus
= pdb_get_trust_credentials(ads
->server
.workgroup
,
1193 if (!NT_STATUS_IS_OK(ntstatus
)) {
1195 return ADS_ERROR_NT(ntstatus
);
1198 status
= ads_connect_creds(ads
, creds
);
1204 * Zero out the internal ads->ldap struct and initialize the address to zero IP.
1205 * @param ads Pointer to an existing ADS_STRUCT
1207 * Sets the ads->ldap.ss to a valid
1208 * zero ip address that can be detected by
1209 * our is_zero_addr() function. Otherwise
1210 * it is left as AF_UNSPEC (0).
1212 void ads_zero_ldap(ADS_STRUCT
*ads
)
1214 ZERO_STRUCT(ads
->ldap
);
1216 * Initialize the sockaddr_storage so we can use
1217 * sockaddr test functions against it.
1219 zero_sockaddr(&ads
->ldap
.ss
);
1223 * Disconnect the LDAP server
1224 * @param ads Pointer to an existing ADS_STRUCT
1226 void ads_disconnect(ADS_STRUCT
*ads
)
1229 ldap_unbind(ads
->ldap
.ld
);
1230 ads
->ldap
.ld
= NULL
;
1232 if (ads
->ldap_tls_data
.mem_ctx
) {
1233 talloc_free(ads
->ldap_tls_data
.mem_ctx
);
1235 if (ads
->ldap_wrap_data
.wrap_ops
&&
1236 ads
->ldap_wrap_data
.wrap_ops
->disconnect
) {
1237 ads
->ldap_wrap_data
.wrap_ops
->disconnect(&ads
->ldap_wrap_data
);
1239 if (ads
->ldap_wrap_data
.mem_ctx
) {
1240 talloc_free(ads
->ldap_wrap_data
.mem_ctx
);
1243 ZERO_STRUCT(ads
->ldap_tls_data
);
1244 ZERO_STRUCT(ads
->ldap_wrap_data
);
1248 Duplicate a struct berval into talloc'ed memory
1250 static struct berval
*dup_berval(TALLOC_CTX
*ctx
, const struct berval
*in_val
)
1252 struct berval
*value
;
1254 if (!in_val
) return NULL
;
1256 value
= talloc_zero(ctx
, struct berval
);
1259 if (in_val
->bv_len
== 0) return value
;
1261 value
->bv_len
= in_val
->bv_len
;
1262 value
->bv_val
= (char *)talloc_memdup(ctx
, in_val
->bv_val
,
1268 Make a values list out of an array of (struct berval *)
1270 static struct berval
**ads_dup_values(TALLOC_CTX
*ctx
,
1271 const struct berval
**in_vals
)
1273 struct berval
**values
;
1276 if (!in_vals
) return NULL
;
1277 for (i
=0; in_vals
[i
]; i
++)
1278 ; /* count values */
1279 values
= talloc_zero_array(ctx
, struct berval
*, i
+1);
1280 if (!values
) return NULL
;
1282 for (i
=0; in_vals
[i
]; i
++) {
1283 values
[i
] = dup_berval(ctx
, in_vals
[i
]);
1289 UTF8-encode a values list out of an array of (char *)
1291 static char **ads_push_strvals(TALLOC_CTX
*ctx
, const char **in_vals
)
1297 if (!in_vals
) return NULL
;
1298 for (i
=0; in_vals
[i
]; i
++)
1299 ; /* count values */
1300 values
= talloc_zero_array(ctx
, char *, i
+1);
1301 if (!values
) return NULL
;
1303 for (i
=0; in_vals
[i
]; i
++) {
1304 if (!push_utf8_talloc(ctx
, &values
[i
], in_vals
[i
], &size
)) {
1305 TALLOC_FREE(values
);
1313 Pull a (char *) array out of a UTF8-encoded values list
1315 static char **ads_pull_strvals(TALLOC_CTX
*ctx
, const char **in_vals
)
1319 size_t converted_size
;
1321 if (!in_vals
) return NULL
;
1322 for (i
=0; in_vals
[i
]; i
++)
1323 ; /* count values */
1324 values
= talloc_zero_array(ctx
, char *, i
+1);
1325 if (!values
) return NULL
;
1327 for (i
=0; in_vals
[i
]; i
++) {
1328 if (!pull_utf8_talloc(ctx
, &values
[i
], in_vals
[i
],
1330 DEBUG(0,("ads_pull_strvals: pull_utf8_talloc failed: "
1331 "%s\n", strerror(errno
)));
1338 * Do a search with paged results. cookie must be null on the first
1339 * call, and then returned on each subsequent call. It will be null
1340 * again when the entire search is complete
1341 * @param ads connection to ads server
1342 * @param bind_path Base dn for the search
1343 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1344 * @param expr Search expression - specified in local charset
1345 * @param attrs Attributes to retrieve - specified in utf8 or ascii
1346 * @param res ** which will contain results - free res* with ads_msgfree()
1347 * @param count Number of entries retrieved on this page
1348 * @param cookie The paged results cookie to be returned on subsequent calls
1349 * @return status of search
1351 static ADS_STATUS
ads_do_paged_search_args(ADS_STRUCT
*ads
,
1352 const char *bind_path
,
1353 int scope
, const char *expr
,
1354 const char **attrs
, void *args
,
1356 int *count
, struct berval
**cookie
)
1359 char *utf8_expr
, *utf8_path
, **search_attrs
= NULL
;
1360 size_t converted_size
;
1361 LDAPControl PagedResults
, NoReferrals
, ExternalCtrl
, *controls
[4], **rcontrols
;
1362 BerElement
*cookie_be
= NULL
;
1363 struct berval
*cookie_bv
= NULL
;
1364 BerElement
*ext_be
= NULL
;
1365 struct berval
*ext_bv
= NULL
;
1368 ads_control
*external_control
= (ads_control
*) args
;
1372 if (!(ctx
= talloc_init("ads_do_paged_search_args")))
1373 return ADS_ERROR(LDAP_NO_MEMORY
);
1375 /* 0 means the conversion worked but the result was empty
1376 so we only fail if it's -1. In any case, it always
1377 at least nulls out the dest */
1378 if (!push_utf8_talloc(ctx
, &utf8_expr
, expr
, &converted_size
) ||
1379 !push_utf8_talloc(ctx
, &utf8_path
, bind_path
, &converted_size
))
1381 rc
= LDAP_NO_MEMORY
;
1385 if (!attrs
|| !(*attrs
))
1386 search_attrs
= NULL
;
1388 /* This would be the utf8-encoded version...*/
1389 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
1390 if (!(search_attrs
= str_list_copy(talloc_tos(), attrs
))) {
1391 rc
= LDAP_NO_MEMORY
;
1396 /* Paged results only available on ldap v3 or later */
1397 ldap_get_option(ads
->ldap
.ld
, LDAP_OPT_PROTOCOL_VERSION
, &version
);
1398 if (version
< LDAP_VERSION3
) {
1399 rc
= LDAP_NOT_SUPPORTED
;
1403 cookie_be
= ber_alloc_t(LBER_USE_DER
);
1405 ber_printf(cookie_be
, "{iO}", (ber_int_t
) ads
->config
.ldap_page_size
, *cookie
);
1406 ber_bvfree(*cookie
); /* don't need it from last time */
1409 ber_printf(cookie_be
, "{io}", (ber_int_t
) ads
->config
.ldap_page_size
, "", 0);
1411 ber_flatten(cookie_be
, &cookie_bv
);
1412 PagedResults
.ldctl_oid
= discard_const_p(char, ADS_PAGE_CTL_OID
);
1413 PagedResults
.ldctl_iscritical
= (char) 1;
1414 PagedResults
.ldctl_value
.bv_len
= cookie_bv
->bv_len
;
1415 PagedResults
.ldctl_value
.bv_val
= cookie_bv
->bv_val
;
1417 NoReferrals
.ldctl_oid
= discard_const_p(char, ADS_NO_REFERRALS_OID
);
1418 NoReferrals
.ldctl_iscritical
= (char) 0;
1419 NoReferrals
.ldctl_value
.bv_len
= 0;
1420 NoReferrals
.ldctl_value
.bv_val
= discard_const_p(char, "");
1422 if (external_control
&&
1423 (strequal(external_control
->control
, ADS_EXTENDED_DN_OID
) ||
1424 strequal(external_control
->control
, ADS_SD_FLAGS_OID
))) {
1426 ExternalCtrl
.ldctl_oid
= discard_const_p(char, external_control
->control
);
1427 ExternalCtrl
.ldctl_iscritical
= (char) external_control
->critical
;
1429 /* win2k does not accept a ldctl_value being passed in */
1431 if (external_control
->val
!= 0) {
1433 if ((ext_be
= ber_alloc_t(LBER_USE_DER
)) == NULL
) {
1434 rc
= LDAP_NO_MEMORY
;
1438 if ((ber_printf(ext_be
, "{i}", (ber_int_t
) external_control
->val
)) == -1) {
1439 rc
= LDAP_NO_MEMORY
;
1442 if ((ber_flatten(ext_be
, &ext_bv
)) == -1) {
1443 rc
= LDAP_NO_MEMORY
;
1447 ExternalCtrl
.ldctl_value
.bv_len
= ext_bv
->bv_len
;
1448 ExternalCtrl
.ldctl_value
.bv_val
= ext_bv
->bv_val
;
1451 ExternalCtrl
.ldctl_value
.bv_len
= 0;
1452 ExternalCtrl
.ldctl_value
.bv_val
= NULL
;
1455 controls
[0] = &NoReferrals
;
1456 controls
[1] = &PagedResults
;
1457 controls
[2] = &ExternalCtrl
;
1461 controls
[0] = &NoReferrals
;
1462 controls
[1] = &PagedResults
;
1466 /* we need to disable referrals as the openldap libs don't
1467 handle them and paged results at the same time. Using them
1468 together results in the result record containing the server
1469 page control being removed from the result list (tridge/jmcd)
1471 leaving this in despite the control that says don't generate
1472 referrals, in case the server doesn't support it (jmcd)
1474 ldap_set_option(ads
->ldap
.ld
, LDAP_OPT_REFERRALS
, LDAP_OPT_OFF
);
1476 rc
= ldap_search_with_timeout(ads
->ldap
.ld
, utf8_path
, scope
, utf8_expr
,
1477 search_attrs
, 0, controls
,
1478 NULL
, LDAP_NO_LIMIT
,
1479 (LDAPMessage
**)res
);
1481 ber_free(cookie_be
, 1);
1482 ber_bvfree(cookie_bv
);
1485 DEBUG(3,("ads_do_paged_search_args: ldap_search_with_timeout(%s) -> %s\n", expr
,
1486 ldap_err2string(rc
)));
1487 if (rc
== LDAP_OTHER
) {
1491 ret
= ldap_parse_result(ads
->ldap
.ld
,
1499 if (ret
== LDAP_SUCCESS
) {
1500 DEBUG(3, ("ldap_search_with_timeout(%s) "
1501 "error: %s\n", expr
, ldap_errmsg
));
1502 ldap_memfree(ldap_errmsg
);
1508 rc
= ldap_parse_result(ads
->ldap
.ld
, *res
, NULL
, NULL
, NULL
,
1509 NULL
, &rcontrols
, 0);
1515 for (i
=0; rcontrols
[i
]; i
++) {
1516 if (strcmp(ADS_PAGE_CTL_OID
, rcontrols
[i
]->ldctl_oid
) == 0) {
1517 cookie_be
= ber_init(&rcontrols
[i
]->ldctl_value
);
1518 ber_scanf(cookie_be
,"{iO}", (ber_int_t
*) count
,
1520 /* the berval is the cookie, but must be freed when
1522 if (cookie_bv
->bv_len
) /* still more to do */
1523 *cookie
=ber_bvdup(cookie_bv
);
1526 ber_bvfree(cookie_bv
);
1527 ber_free(cookie_be
, 1);
1531 ldap_controls_free(rcontrols
);
1534 talloc_destroy(ctx
);
1537 ber_free(ext_be
, 1);
1544 if (rc
!= LDAP_SUCCESS
&& *res
!= NULL
) {
1545 ads_msgfree(ads
, *res
);
1549 /* if/when we decide to utf8-encode attrs, take out this next line */
1550 TALLOC_FREE(search_attrs
);
1552 return ADS_ERROR(rc
);
1555 static ADS_STATUS
ads_do_paged_search(ADS_STRUCT
*ads
, const char *bind_path
,
1556 int scope
, const char *expr
,
1557 const char **attrs
, LDAPMessage
**res
,
1558 int *count
, struct berval
**cookie
)
1560 return ads_do_paged_search_args(ads
, bind_path
, scope
, expr
, attrs
, NULL
, res
, count
, cookie
);
1565 * Get all results for a search. This uses ads_do_paged_search() to return
1566 * all entries in a large search.
1567 * @param ads connection to ads server
1568 * @param bind_path Base dn for the search
1569 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1570 * @param expr Search expression
1571 * @param attrs Attributes to retrieve
1572 * @param res ** which will contain results - free res* with ads_msgfree()
1573 * @return status of search
1575 ADS_STATUS
ads_do_search_all_args(ADS_STRUCT
*ads
, const char *bind_path
,
1576 int scope
, const char *expr
,
1577 const char **attrs
, void *args
,
1580 struct berval
*cookie
= NULL
;
1585 status
= ads_do_paged_search_args(ads
, bind_path
, scope
, expr
, attrs
, args
, res
,
1588 if (!ADS_ERR_OK(status
))
1591 #ifdef HAVE_LDAP_ADD_RESULT_ENTRY
1593 LDAPMessage
*res2
= NULL
;
1594 LDAPMessage
*msg
, *next
;
1596 status
= ads_do_paged_search_args(ads
, bind_path
, scope
, expr
,
1597 attrs
, args
, &res2
, &count
, &cookie
);
1598 if (!ADS_ERR_OK(status
)) {
1602 /* this relies on the way that ldap_add_result_entry() works internally. I hope
1603 that this works on all ldap libs, but I have only tested with openldap */
1604 for (msg
= ads_first_message(ads
, res2
); msg
; msg
= next
) {
1605 next
= ads_next_message(ads
, msg
);
1606 ldap_add_result_entry((LDAPMessage
**)res
, msg
);
1608 /* note that we do not free res2, as the memory is now
1609 part of the main returned list */
1612 DEBUG(0, ("no ldap_add_result_entry() support in LDAP libs!\n"));
1613 status
= ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL
);
1619 ADS_STATUS
ads_do_search_all(ADS_STRUCT
*ads
, const char *bind_path
,
1620 int scope
, const char *expr
,
1621 const char **attrs
, LDAPMessage
**res
)
1623 return ads_do_search_all_args(ads
, bind_path
, scope
, expr
, attrs
, NULL
, res
);
1626 ADS_STATUS
ads_do_search_all_sd_flags(ADS_STRUCT
*ads
, const char *bind_path
,
1627 int scope
, const char *expr
,
1628 const char **attrs
, uint32_t sd_flags
,
1633 args
.control
= ADS_SD_FLAGS_OID
;
1634 args
.val
= sd_flags
;
1635 args
.critical
= True
;
1637 return ads_do_search_all_args(ads
, bind_path
, scope
, expr
, attrs
, &args
, res
);
1642 * Run a function on all results for a search. Uses ads_do_paged_search() and
1643 * runs the function as each page is returned, using ads_process_results()
1644 * @param ads connection to ads server
1645 * @param bind_path Base dn for the search
1646 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1647 * @param expr Search expression - specified in local charset
1648 * @param attrs Attributes to retrieve - specified in UTF-8 or ascii
1649 * @param fn Function which takes attr name, values list, and data_area
1650 * @param data_area Pointer which is passed to function on each call
1651 * @return status of search
1653 ADS_STATUS
ads_do_search_all_fn(ADS_STRUCT
*ads
, const char *bind_path
,
1654 int scope
, const char *expr
, const char **attrs
,
1655 bool (*fn
)(ADS_STRUCT
*, char *, void **, void *),
1658 struct berval
*cookie
= NULL
;
1663 status
= ads_do_paged_search(ads
, bind_path
, scope
, expr
, attrs
, &res
,
1666 if (!ADS_ERR_OK(status
)) return status
;
1668 ads_process_results(ads
, res
, fn
, data_area
);
1669 ads_msgfree(ads
, res
);
1672 status
= ads_do_paged_search(ads
, bind_path
, scope
, expr
, attrs
,
1673 &res
, &count
, &cookie
);
1675 if (!ADS_ERR_OK(status
)) break;
1677 ads_process_results(ads
, res
, fn
, data_area
);
1678 ads_msgfree(ads
, res
);
1685 * Do a search with a timeout.
1686 * @param ads connection to ads server
1687 * @param bind_path Base dn for the search
1688 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1689 * @param expr Search expression
1690 * @param attrs Attributes to retrieve
1691 * @param res ** which will contain results - free res* with ads_msgfree()
1692 * @return status of search
1694 ADS_STATUS
ads_do_search(ADS_STRUCT
*ads
, const char *bind_path
, int scope
,
1696 const char **attrs
, LDAPMessage
**res
)
1699 char *utf8_expr
, *utf8_path
, **search_attrs
= NULL
;
1700 size_t converted_size
;
1704 if (!(ctx
= talloc_init("ads_do_search"))) {
1705 DEBUG(1,("ads_do_search: talloc_init() failed!\n"));
1706 return ADS_ERROR(LDAP_NO_MEMORY
);
1709 /* 0 means the conversion worked but the result was empty
1710 so we only fail if it's negative. In any case, it always
1711 at least nulls out the dest */
1712 if (!push_utf8_talloc(ctx
, &utf8_expr
, expr
, &converted_size
) ||
1713 !push_utf8_talloc(ctx
, &utf8_path
, bind_path
, &converted_size
))
1715 DEBUG(1,("ads_do_search: push_utf8_talloc() failed!\n"));
1716 rc
= LDAP_NO_MEMORY
;
1720 if (!attrs
|| !(*attrs
))
1721 search_attrs
= NULL
;
1723 /* This would be the utf8-encoded version...*/
1724 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
1725 if (!(search_attrs
= str_list_copy(talloc_tos(), attrs
)))
1727 DEBUG(1,("ads_do_search: str_list_copy() failed!\n"));
1728 rc
= LDAP_NO_MEMORY
;
1733 /* see the note in ads_do_paged_search - we *must* disable referrals */
1734 ldap_set_option(ads
->ldap
.ld
, LDAP_OPT_REFERRALS
, LDAP_OPT_OFF
);
1736 rc
= ldap_search_with_timeout(ads
->ldap
.ld
, utf8_path
, scope
, utf8_expr
,
1737 search_attrs
, 0, NULL
, NULL
,
1739 (LDAPMessage
**)res
);
1741 if (rc
== LDAP_SIZELIMIT_EXCEEDED
) {
1742 DEBUG(3,("Warning! sizelimit exceeded in ldap. Truncating.\n"));
1747 talloc_destroy(ctx
);
1748 /* if/when we decide to utf8-encode attrs, take out this next line */
1749 TALLOC_FREE(search_attrs
);
1750 return ADS_ERROR(rc
);
1753 * Do a general ADS search
1754 * @param ads connection to ads server
1755 * @param res ** which will contain results - free res* with ads_msgfree()
1756 * @param expr Search expression
1757 * @param attrs Attributes to retrieve
1758 * @return status of search
1760 ADS_STATUS
ads_search(ADS_STRUCT
*ads
, LDAPMessage
**res
,
1761 const char *expr
, const char **attrs
)
1763 return ads_do_search(ads
, ads
->config
.bind_path
, LDAP_SCOPE_SUBTREE
,
1768 * Do a search on a specific DistinguishedName
1769 * @param ads connection to ads server
1770 * @param res ** which will contain results - free res* with ads_msgfree()
1771 * @param dn DistinguishedName to search
1772 * @param attrs Attributes to retrieve
1773 * @return status of search
1775 ADS_STATUS
ads_search_dn(ADS_STRUCT
*ads
, LDAPMessage
**res
,
1776 const char *dn
, const char **attrs
)
1778 return ads_do_search(ads
, dn
, LDAP_SCOPE_BASE
, "(objectclass=*)",
1783 * Free up memory from a ads_search
1784 * @param ads connection to ads server
1785 * @param msg Search results to free
1787 void ads_msgfree(ADS_STRUCT
*ads
, LDAPMessage
*msg
)
1794 * Get a dn from search results
1795 * @param ads connection to ads server
1796 * @param msg Search result
1799 char *ads_get_dn(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
, LDAPMessage
*msg
)
1801 char *utf8_dn
, *unix_dn
;
1802 size_t converted_size
;
1804 utf8_dn
= ldap_get_dn(ads
->ldap
.ld
, msg
);
1807 DEBUG (5, ("ads_get_dn: ldap_get_dn failed\n"));
1811 if (!pull_utf8_talloc(mem_ctx
, &unix_dn
, utf8_dn
, &converted_size
)) {
1812 DEBUG(0,("ads_get_dn: string conversion failure utf8 [%s]\n",
1816 ldap_memfree(utf8_dn
);
1821 * Get the parent from a dn
1822 * @param dn the dn to return the parent from
1823 * @return parent dn string
1825 char *ads_parent_dn(const char *dn
)
1833 p
= strchr(dn
, ',');
1843 * Find a machine account given a hostname
1844 * @param ads connection to ads server
1845 * @param res ** which will contain results - free res* with ads_msgfree()
1846 * @param host Hostname to search for
1847 * @return status of search
1849 ADS_STATUS
ads_find_machine_acct(ADS_STRUCT
*ads
, LDAPMessage
**res
,
1850 const char *machine
)
1854 const char *attrs
[] = {
1855 /* This is how Windows checks for machine accounts */
1858 "userAccountControl",
1860 "ServicePrincipalName",
1861 "userPrincipalName",
1863 /* Additional attributes Samba checks */
1864 "msDS-KeyVersionNumber",
1865 "msDS-AdditionalDnsHostName",
1866 "msDS-SupportedEncryptionTypes",
1867 "nTSecurityDescriptor",
1872 TALLOC_CTX
*frame
= talloc_stackframe();
1876 /* the easiest way to find a machine account anywhere in the tree
1877 is to look for hostname$ */
1878 expr
= talloc_asprintf(frame
, "(samAccountName=%s$)", machine
);
1880 status
= ADS_ERROR_NT(NT_STATUS_NO_MEMORY
);
1884 status
= ads_search(ads
, res
, expr
, attrs
);
1885 if (ADS_ERR_OK(status
)) {
1886 if (ads_count_replies(ads
, *res
) != 1) {
1887 status
= ADS_ERROR_LDAP(LDAP_NO_SUCH_OBJECT
);
1897 * Initialize a list of mods to be used in a modify request
1898 * @param ctx An initialized TALLOC_CTX
1899 * @return allocated ADS_MODLIST
1901 ADS_MODLIST
ads_init_mods(TALLOC_CTX
*ctx
)
1903 #define ADS_MODLIST_ALLOC_SIZE 10
1906 if ((mods
= talloc_zero_array(ctx
, LDAPMod
*, ADS_MODLIST_ALLOC_SIZE
+ 1)))
1907 /* -1 is safety to make sure we don't go over the end.
1908 need to reset it to NULL before doing ldap modify */
1909 mods
[ADS_MODLIST_ALLOC_SIZE
] = (LDAPMod
*) -1;
1911 return (ADS_MODLIST
)mods
;
1916 add an attribute to the list, with values list already constructed
1918 static ADS_STATUS
ads_modlist_add(TALLOC_CTX
*ctx
, ADS_MODLIST
*mods
,
1919 int mod_op
, const char *name
,
1920 const void *_invals
)
1923 LDAPMod
**modlist
= (LDAPMod
**) *mods
;
1924 struct berval
**ber_values
= NULL
;
1925 char **char_values
= NULL
;
1928 mod_op
= LDAP_MOD_DELETE
;
1930 if (mod_op
& LDAP_MOD_BVALUES
) {
1931 const struct berval
**b
;
1932 b
= discard_const_p(const struct berval
*, _invals
);
1933 ber_values
= ads_dup_values(ctx
, b
);
1936 c
= discard_const_p(const char *, _invals
);
1937 char_values
= ads_push_strvals(ctx
, c
);
1941 /* find the first empty slot */
1942 for (curmod
=0; modlist
[curmod
] && modlist
[curmod
] != (LDAPMod
*) -1;
1944 if (modlist
[curmod
] == (LDAPMod
*) -1) {
1945 if (!(modlist
= talloc_realloc(ctx
, modlist
, LDAPMod
*,
1946 curmod
+ADS_MODLIST_ALLOC_SIZE
+1)))
1947 return ADS_ERROR(LDAP_NO_MEMORY
);
1948 memset(&modlist
[curmod
], 0,
1949 ADS_MODLIST_ALLOC_SIZE
*sizeof(LDAPMod
*));
1950 modlist
[curmod
+ADS_MODLIST_ALLOC_SIZE
] = (LDAPMod
*) -1;
1951 *mods
= (ADS_MODLIST
)modlist
;
1954 if (!(modlist
[curmod
] = talloc_zero(ctx
, LDAPMod
)))
1955 return ADS_ERROR(LDAP_NO_MEMORY
);
1956 modlist
[curmod
]->mod_type
= talloc_strdup(ctx
, name
);
1957 if (mod_op
& LDAP_MOD_BVALUES
) {
1958 modlist
[curmod
]->mod_bvalues
= ber_values
;
1959 } else if (mod_op
& LDAP_MOD_DELETE
) {
1960 modlist
[curmod
]->mod_values
= NULL
;
1962 modlist
[curmod
]->mod_values
= char_values
;
1965 modlist
[curmod
]->mod_op
= mod_op
;
1966 return ADS_ERROR(LDAP_SUCCESS
);
1970 * Add a single string value to a mod list
1971 * @param ctx An initialized TALLOC_CTX
1972 * @param mods An initialized ADS_MODLIST
1973 * @param name The attribute name to add
1974 * @param val The value to add - NULL means DELETE
1975 * @return ADS STATUS indicating success of add
1977 ADS_STATUS
ads_mod_str(TALLOC_CTX
*ctx
, ADS_MODLIST
*mods
,
1978 const char *name
, const char *val
)
1980 const char *values
[2];
1986 return ads_modlist_add(ctx
, mods
, LDAP_MOD_DELETE
, name
, NULL
);
1987 return ads_modlist_add(ctx
, mods
, LDAP_MOD_REPLACE
, name
, values
);
1991 * Add an array of string values to a mod list
1992 * @param ctx An initialized TALLOC_CTX
1993 * @param mods An initialized ADS_MODLIST
1994 * @param name The attribute name to add
1995 * @param vals The array of string values to add - NULL means DELETE
1996 * @return ADS STATUS indicating success of add
1998 ADS_STATUS
ads_mod_strlist(TALLOC_CTX
*ctx
, ADS_MODLIST
*mods
,
1999 const char *name
, const char **vals
)
2002 return ads_modlist_add(ctx
, mods
, LDAP_MOD_DELETE
, name
, NULL
);
2003 return ads_modlist_add(ctx
, mods
, LDAP_MOD_REPLACE
,
2004 name
, (const void **) vals
);
2008 * Add a single ber-encoded value to a mod list
2009 * @param ctx An initialized TALLOC_CTX
2010 * @param mods An initialized ADS_MODLIST
2011 * @param name The attribute name to add
2012 * @param val The value to add - NULL means DELETE
2013 * @return ADS STATUS indicating success of add
2015 static ADS_STATUS
ads_mod_ber(TALLOC_CTX
*ctx
, ADS_MODLIST
*mods
,
2016 const char *name
, const struct berval
*val
)
2018 const struct berval
*values
[2];
2023 return ads_modlist_add(ctx
, mods
, LDAP_MOD_DELETE
, name
, NULL
);
2024 return ads_modlist_add(ctx
, mods
, LDAP_MOD_REPLACE
|LDAP_MOD_BVALUES
,
2025 name
, (const void *) values
);
2028 static void ads_print_error(int ret
, LDAP
*ld
)
2031 char *ld_error
= NULL
;
2032 ldap_get_option(ld
, LDAP_OPT_ERROR_STRING
, &ld_error
);
2033 DBG_ERR("AD LDAP ERROR: %d (%s): %s\n",
2035 ldap_err2string(ret
),
2037 SAFE_FREE(ld_error
);
2042 * Perform an ldap modify
2043 * @param ads connection to ads server
2044 * @param mod_dn DistinguishedName to modify
2045 * @param mods list of modifications to perform
2046 * @return status of modify
2048 ADS_STATUS
ads_gen_mod(ADS_STRUCT
*ads
, const char *mod_dn
, ADS_MODLIST mods
)
2051 char *utf8_dn
= NULL
;
2052 size_t converted_size
;
2054 this control is needed to modify that contains a currently
2055 non-existent attribute (but allowable for the object) to run
2057 LDAPControl PermitModify
= {
2058 discard_const_p(char, ADS_PERMIT_MODIFY_OID
),
2061 LDAPControl
*controls
[2];
2063 DBG_INFO("AD LDAP: Modifying %s\n", mod_dn
);
2065 controls
[0] = &PermitModify
;
2068 if (!push_utf8_talloc(talloc_tos(), &utf8_dn
, mod_dn
, &converted_size
)) {
2069 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY
);
2072 /* find the end of the list, marked by NULL or -1 */
2073 for(i
=0;(mods
[i
]!=0)&&(mods
[i
]!=(LDAPMod
*) -1);i
++);
2074 /* make sure the end of the list is NULL */
2076 ret
= ldap_modify_ext_s(ads
->ldap
.ld
, utf8_dn
,
2077 (LDAPMod
**) mods
, controls
, NULL
);
2078 ads_print_error(ret
, ads
->ldap
.ld
);
2079 TALLOC_FREE(utf8_dn
);
2080 return ADS_ERROR(ret
);
2084 * Perform an ldap add
2085 * @param ads connection to ads server
2086 * @param new_dn DistinguishedName to add
2087 * @param mods list of attributes and values for DN
2088 * @return status of add
2090 ADS_STATUS
ads_gen_add(ADS_STRUCT
*ads
, const char *new_dn
, ADS_MODLIST mods
)
2093 char *utf8_dn
= NULL
;
2094 size_t converted_size
;
2096 DBG_INFO("AD LDAP: Adding %s\n", new_dn
);
2098 if (!push_utf8_talloc(talloc_tos(), &utf8_dn
, new_dn
, &converted_size
)) {
2099 DEBUG(1, ("ads_gen_add: push_utf8_talloc failed!\n"));
2100 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY
);
2103 /* find the end of the list, marked by NULL or -1 */
2104 for(i
=0;(mods
[i
]!=0)&&(mods
[i
]!=(LDAPMod
*) -1);i
++);
2105 /* make sure the end of the list is NULL */
2108 ret
= ldap_add_ext_s(ads
->ldap
.ld
, utf8_dn
, (LDAPMod
**)mods
, NULL
, NULL
);
2109 ads_print_error(ret
, ads
->ldap
.ld
);
2110 TALLOC_FREE(utf8_dn
);
2111 return ADS_ERROR(ret
);
2115 * Delete a DistinguishedName
2116 * @param ads connection to ads server
2117 * @param new_dn DistinguishedName to delete
2118 * @return status of delete
2120 ADS_STATUS
ads_del_dn(ADS_STRUCT
*ads
, char *del_dn
)
2123 char *utf8_dn
= NULL
;
2124 size_t converted_size
;
2125 if (!push_utf8_talloc(talloc_tos(), &utf8_dn
, del_dn
, &converted_size
)) {
2126 DEBUG(1, ("ads_del_dn: push_utf8_talloc failed!\n"));
2127 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY
);
2130 DBG_INFO("AD LDAP: Deleting %s\n", del_dn
);
2132 ret
= ldap_delete_s(ads
->ldap
.ld
, utf8_dn
);
2133 ads_print_error(ret
, ads
->ldap
.ld
);
2134 TALLOC_FREE(utf8_dn
);
2135 return ADS_ERROR(ret
);
2139 * Build an org unit string
2140 * if org unit is Computers or blank then assume a container, otherwise
2141 * assume a / separated list of organisational units.
2142 * jmcd: '\' is now used for escapes so certain chars can be in the ou (e.g. #)
2143 * @param ads connection to ads server
2144 * @param org_unit Organizational unit
2145 * @return org unit string - caller must free
2147 char *ads_ou_string(ADS_STRUCT
*ads
, const char *org_unit
)
2153 if (!org_unit
|| !*org_unit
) {
2155 ret
= ads_default_ou_string(ads
, DS_GUID_COMPUTERS_CONTAINER
);
2157 /* samba4 might not yet respond to a wellknownobject-query */
2158 return ret
? ret
: SMB_STRDUP("cn=Computers");
2161 if (strequal(org_unit
, "Computers")) {
2162 return SMB_STRDUP("cn=Computers");
2165 /* jmcd: removed "\\" from the separation chars, because it is
2166 needed as an escape for chars like '#' which are valid in an
2168 status
= ads_build_path(org_unit
, "/", "ou=", 1, &dn
);
2169 if (!ADS_ERR_OK(status
)) {
2177 * Get a org unit string for a well-known GUID
2178 * @param ads connection to ads server
2179 * @param wknguid Well known GUID
2180 * @return org unit string - caller must free
2182 char *ads_default_ou_string(ADS_STRUCT
*ads
, const char *wknguid
)
2185 LDAPMessage
*res
= NULL
;
2186 char *base
, *wkn_dn
= NULL
, *ret
= NULL
, **wkn_dn_exp
= NULL
,
2187 **bind_dn_exp
= NULL
;
2188 const char *attrs
[] = {"distinguishedName", NULL
};
2189 int new_ln
, wkn_ln
, bind_ln
, i
;
2191 if (wknguid
== NULL
) {
2195 if (asprintf(&base
, "<WKGUID=%s,%s>", wknguid
, ads
->config
.bind_path
) == -1) {
2196 DEBUG(1, ("asprintf failed!\n"));
2200 status
= ads_search_dn(ads
, &res
, base
, attrs
);
2201 if (!ADS_ERR_OK(status
)) {
2202 DEBUG(1,("Failed while searching for: %s\n", base
));
2206 if (ads_count_replies(ads
, res
) != 1) {
2210 /* substitute the bind-path from the well-known-guid-search result */
2211 wkn_dn
= ads_get_dn(ads
, talloc_tos(), res
);
2216 wkn_dn_exp
= ldap_explode_dn(wkn_dn
, 0);
2221 bind_dn_exp
= ldap_explode_dn(ads
->config
.bind_path
, 0);
2226 for (wkn_ln
=0; wkn_dn_exp
[wkn_ln
]; wkn_ln
++)
2228 for (bind_ln
=0; bind_dn_exp
[bind_ln
]; bind_ln
++)
2231 new_ln
= wkn_ln
- bind_ln
;
2233 ret
= SMB_STRDUP(wkn_dn_exp
[0]);
2238 for (i
=1; i
< new_ln
; i
++) {
2241 if (asprintf(&s
, "%s,%s", ret
, wkn_dn_exp
[i
]) == -1) {
2247 ret
= SMB_STRDUP(s
);
2256 ads_msgfree(ads
, res
);
2257 TALLOC_FREE(wkn_dn
);
2259 ldap_value_free(wkn_dn_exp
);
2262 ldap_value_free(bind_dn_exp
);
2269 * Adds (appends) an item to an attribute array, rather then
2270 * replacing the whole list
2271 * @param ctx An initialized TALLOC_CTX
2272 * @param mods An initialized ADS_MODLIST
2273 * @param name name of the ldap attribute to append to
2274 * @param vals an array of values to add
2275 * @return status of addition
2278 ADS_STATUS
ads_add_strlist(TALLOC_CTX
*ctx
, ADS_MODLIST
*mods
,
2279 const char *name
, const char **vals
)
2281 return ads_modlist_add(ctx
, mods
, LDAP_MOD_ADD
, name
,
2282 (const void *) vals
);
2286 * This clears out all registered spn's for a given hostname
2287 * @param ads An initialized ADS_STRUCT
2288 * @param machine_name the NetBIOS name of the computer.
2289 * @return 0 upon success, non-zero otherwise.
2292 ADS_STATUS
ads_clear_service_principal_names(ADS_STRUCT
*ads
, const char *machine_name
)
2295 LDAPMessage
*res
= NULL
;
2297 const char *servicePrincipalName
[1] = {NULL
};
2299 char *dn_string
= NULL
;
2301 ret
= ads_find_machine_acct(ads
, &res
, machine_name
);
2302 if (!ADS_ERR_OK(ret
)) {
2303 DEBUG(5,("ads_clear_service_principal_names: WARNING: Host Account for %s not found... skipping operation.\n", machine_name
));
2304 DEBUG(5,("ads_clear_service_principal_names: WARNING: Service Principals for %s have NOT been cleared.\n", machine_name
));
2305 ads_msgfree(ads
, res
);
2309 DEBUG(5,("ads_clear_service_principal_names: Host account for %s found\n", machine_name
));
2310 ctx
= talloc_init("ads_clear_service_principal_names");
2312 ads_msgfree(ads
, res
);
2313 return ADS_ERROR(LDAP_NO_MEMORY
);
2316 if (!(mods
= ads_init_mods(ctx
))) {
2317 talloc_destroy(ctx
);
2318 ads_msgfree(ads
, res
);
2319 return ADS_ERROR(LDAP_NO_MEMORY
);
2321 ret
= ads_mod_strlist(ctx
, &mods
, "servicePrincipalName", servicePrincipalName
);
2322 if (!ADS_ERR_OK(ret
)) {
2323 DEBUG(1,("ads_clear_service_principal_names: Error creating strlist.\n"));
2324 ads_msgfree(ads
, res
);
2325 talloc_destroy(ctx
);
2328 dn_string
= ads_get_dn(ads
, talloc_tos(), res
);
2330 talloc_destroy(ctx
);
2331 ads_msgfree(ads
, res
);
2332 return ADS_ERROR(LDAP_NO_MEMORY
);
2334 ret
= ads_gen_mod(ads
, dn_string
, mods
);
2335 TALLOC_FREE(dn_string
);
2336 if (!ADS_ERR_OK(ret
)) {
2337 DEBUG(1,("ads_clear_service_principal_names: Error: Updating Service Principals for machine %s in LDAP\n",
2339 ads_msgfree(ads
, res
);
2340 talloc_destroy(ctx
);
2344 ads_msgfree(ads
, res
);
2345 talloc_destroy(ctx
);
2350 * @brief Search for an element in a string array.
2352 * @param[in] el_array The string array to search.
2354 * @param[in] num_el The number of elements in the string array.
2356 * @param[in] el The string to search.
2358 * @return True if found, false if not.
2360 bool ads_element_in_array(const char **el_array
, size_t num_el
, const char *el
)
2364 if (el_array
== NULL
|| num_el
== 0 || el
== NULL
) {
2368 for (i
= 0; i
< num_el
&& el_array
[i
] != NULL
; i
++) {
2371 cmp
= strcasecmp_m(el_array
[i
], el
);
2381 * @brief This gets the service principal names of an existing computer account.
2383 * @param[in] mem_ctx The memory context to use to allocate the spn array.
2385 * @param[in] ads The ADS context to use.
2387 * @param[in] machine_name The NetBIOS name of the computer, which is used to
2388 * identify the computer account.
2390 * @param[in] spn_array A pointer to store the array for SPNs.
2392 * @param[in] num_spns The number of principals stored in the array.
2394 * @return 0 on success, or a ADS error if a failure occurred.
2396 ADS_STATUS
ads_get_service_principal_names(TALLOC_CTX
*mem_ctx
,
2398 const char *machine_name
,
2403 LDAPMessage
*res
= NULL
;
2406 status
= ads_find_machine_acct(ads
,
2409 if (!ADS_ERR_OK(status
)) {
2410 DEBUG(1,("Host Account for %s not found... skipping operation.\n",
2415 count
= ads_count_replies(ads
, res
);
2417 status
= ADS_ERROR(LDAP_NO_SUCH_OBJECT
);
2421 *spn_array
= ads_pull_strings(ads
,
2424 "servicePrincipalName",
2426 if (*spn_array
== NULL
) {
2427 DEBUG(1, ("Host account for %s does not have service principal "
2430 status
= ADS_ERROR(LDAP_NO_SUCH_OBJECT
);
2435 ads_msgfree(ads
, res
);
2441 * This adds a service principal name to an existing computer account
2442 * (found by hostname) in AD.
2443 * @param ads An initialized ADS_STRUCT
2444 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
2445 * @param spns An array or strings for the service principals to add,
2446 * i.e. 'cifs/machine_name', 'http/machine.full.domain.com' etc.
2447 * @return 0 upon success, or non-zero if a failure occurs
2450 ADS_STATUS
ads_add_service_principal_names(ADS_STRUCT
*ads
,
2451 const char *machine_name
,
2456 LDAPMessage
*res
= NULL
;
2458 char *dn_string
= NULL
;
2459 const char **servicePrincipalName
= spns
;
2461 ret
= ads_find_machine_acct(ads
, &res
, machine_name
);
2462 if (!ADS_ERR_OK(ret
)) {
2463 DEBUG(1,("ads_add_service_principal_name: WARNING: Host Account for %s not found... skipping operation.\n",
2465 DEBUG(1,("ads_add_service_principal_name: WARNING: Service Principals have NOT been added.\n"));
2466 ads_msgfree(ads
, res
);
2470 DEBUG(1,("ads_add_service_principal_name: Host account for %s found\n", machine_name
));
2471 if (!(ctx
= talloc_init("ads_add_service_principal_name"))) {
2472 ads_msgfree(ads
, res
);
2473 return ADS_ERROR(LDAP_NO_MEMORY
);
2476 DEBUG(5,("ads_add_service_principal_name: INFO: "
2477 "Adding %s to host %s\n",
2478 spns
[0] ? "N/A" : spns
[0], machine_name
));
2481 DEBUG(5,("ads_add_service_principal_name: INFO: "
2482 "Adding %s to host %s\n",
2483 spns
[1] ? "N/A" : spns
[1], machine_name
));
2485 if ( (mods
= ads_init_mods(ctx
)) == NULL
) {
2486 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
2490 ret
= ads_add_strlist(ctx
,
2492 "servicePrincipalName",
2493 servicePrincipalName
);
2494 if (!ADS_ERR_OK(ret
)) {
2495 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
2499 if ( (dn_string
= ads_get_dn(ads
, ctx
, res
)) == NULL
) {
2500 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
2504 ret
= ads_gen_mod(ads
, dn_string
, mods
);
2505 if (!ADS_ERR_OK(ret
)) {
2506 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
2512 ads_msgfree(ads
, res
);
2516 static uint32_t ads_get_acct_ctrl(ADS_STRUCT
*ads
,
2519 uint32_t acct_ctrl
= 0;
2522 ok
= ads_pull_uint32(ads
, msg
, "userAccountControl", &acct_ctrl
);
2530 static ADS_STATUS
ads_change_machine_acct(ADS_STRUCT
*ads
,
2532 const struct berval
*machine_pw_val
)
2536 TALLOC_CTX
*frame
= talloc_stackframe();
2537 uint32_t acct_control
;
2538 char *control_str
= NULL
;
2539 const char *attrs
[] = {
2543 LDAPMessage
*res
= NULL
;
2546 dn
= ads_get_dn(ads
, frame
, msg
);
2548 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
2552 acct_control
= ads_get_acct_ctrl(ads
, msg
);
2553 if (acct_control
== 0) {
2554 ret
= ADS_ERROR(LDAP_NO_RESULTS_RETURNED
);
2559 * Changing the password, disables the account. So we need to change the
2560 * userAccountControl flags to enable it again.
2562 mods
= ads_init_mods(frame
);
2564 ret
= ADS_ERROR_LDAP(LDAP_NO_MEMORY
);
2568 ads_mod_ber(frame
, &mods
, "unicodePwd", machine_pw_val
);
2570 ret
= ads_gen_mod(ads
, dn
, mods
);
2571 if (!ADS_ERR_OK(ret
)) {
2577 * To activate the account, we need to disable and enable it.
2579 acct_control
|= UF_ACCOUNTDISABLE
;
2581 control_str
= talloc_asprintf(frame
, "%u", acct_control
);
2582 if (control_str
== NULL
) {
2583 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
2587 mods
= ads_init_mods(frame
);
2589 ret
= ADS_ERROR_LDAP(LDAP_NO_MEMORY
);
2593 ads_mod_str(frame
, &mods
, "userAccountControl", control_str
);
2595 ret
= ads_gen_mod(ads
, dn
, mods
);
2596 if (!ADS_ERR_OK(ret
)) {
2600 TALLOC_FREE(control_str
);
2603 * Enable the account again.
2605 acct_control
&= ~UF_ACCOUNTDISABLE
;
2607 control_str
= talloc_asprintf(frame
, "%u", acct_control
);
2608 if (control_str
== NULL
) {
2609 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
2613 mods
= ads_init_mods(frame
);
2615 ret
= ADS_ERROR_LDAP(LDAP_NO_MEMORY
);
2619 ads_mod_str(frame
, &mods
, "userAccountControl", control_str
);
2621 ret
= ads_gen_mod(ads
, dn
, mods
);
2622 if (!ADS_ERR_OK(ret
)) {
2626 TALLOC_FREE(control_str
);
2628 ret
= ads_search_dn(ads
, &res
, dn
, attrs
);
2629 ads_msgfree(ads
, res
);
2638 * adds a machine account to the ADS server
2639 * @param ads An initialized ADS_STRUCT
2640 * @param machine_name - the NetBIOS machine name of this account.
2641 * @param account_type A number indicating the type of account to create
2642 * @param org_unit The LDAP path in which to place this account
2643 * @return 0 upon success, or non-zero otherwise
2646 ADS_STATUS
ads_create_machine_acct(ADS_STRUCT
*ads
,
2647 const char *machine_name
,
2648 const char *machine_password
,
2649 const char *org_unit
,
2650 uint32_t etype_list
,
2651 const char *dns_domain_name
)
2654 char *samAccountName
= NULL
;
2655 char *controlstr
= NULL
;
2656 TALLOC_CTX
*ctx
= NULL
;
2658 char *machine_escaped
= NULL
;
2659 char *dns_hostname
= NULL
;
2660 char *new_dn
= NULL
;
2661 char *utf8_pw
= NULL
;
2662 size_t utf8_pw_len
= 0;
2663 char *utf16_pw
= NULL
;
2664 size_t utf16_pw_len
= 0;
2665 struct berval machine_pw_val
;
2667 const char **spn_array
= NULL
;
2668 size_t num_spns
= 0;
2669 const char *spn_prefix
[] = {
2671 "RestrictedKrbHost",
2674 LDAPMessage
*res
= NULL
;
2675 uint32_t acct_control
= UF_WORKSTATION_TRUST_ACCOUNT
;
2677 ctx
= talloc_init("ads_add_machine_acct");
2679 return ADS_ERROR(LDAP_NO_MEMORY
);
2682 machine_escaped
= escape_rdn_val_string_alloc(machine_name
);
2683 if (machine_escaped
== NULL
) {
2684 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
2688 utf8_pw
= talloc_asprintf(ctx
, "\"%s\"", machine_password
);
2689 if (utf8_pw
== NULL
) {
2690 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
2693 utf8_pw_len
= strlen(utf8_pw
);
2695 ok
= convert_string_talloc(ctx
,
2696 CH_UTF8
, CH_UTF16MUNGED
,
2697 utf8_pw
, utf8_pw_len
,
2698 (void *)&utf16_pw
, &utf16_pw_len
);
2700 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
2704 machine_pw_val
= (struct berval
) {
2706 .bv_len
= utf16_pw_len
,
2709 /* Check if the machine account already exists. */
2710 ret
= ads_find_machine_acct(ads
, &res
, machine_escaped
);
2711 if (ADS_ERR_OK(ret
)) {
2712 /* Change the machine account password */
2713 ret
= ads_change_machine_acct(ads
, res
, &machine_pw_val
);
2714 ads_msgfree(ads
, res
);
2718 ads_msgfree(ads
, res
);
2720 new_dn
= talloc_asprintf(ctx
, "cn=%s,%s", machine_escaped
, org_unit
);
2721 if (new_dn
== NULL
) {
2722 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
2726 /* Create machine account */
2728 samAccountName
= talloc_asprintf(ctx
, "%s$", machine_name
);
2729 if (samAccountName
== NULL
) {
2730 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
2734 dns_hostname
= talloc_asprintf(ctx
,
2738 if (dns_hostname
== NULL
) {
2739 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
2743 /* Add dns_hostname SPNs */
2744 for (i
= 0; i
< ARRAY_SIZE(spn_prefix
); i
++) {
2745 char *spn
= talloc_asprintf(ctx
,
2750 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
2754 ok
= add_string_to_array(ctx
,
2759 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
2764 /* Add machine_name SPNs */
2765 for (i
= 0; i
< ARRAY_SIZE(spn_prefix
); i
++) {
2766 char *spn
= talloc_asprintf(ctx
,
2771 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
2775 ok
= add_string_to_array(ctx
,
2780 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
2785 /* Make sure to NULL terminate the array */
2786 spn_array
= talloc_realloc(ctx
, spn_array
, const char *, num_spns
+ 1);
2787 if (spn_array
== NULL
) {
2788 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
2791 spn_array
[num_spns
] = NULL
;
2793 controlstr
= talloc_asprintf(ctx
, "%u", acct_control
);
2794 if (controlstr
== NULL
) {
2795 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
2799 mods
= ads_init_mods(ctx
);
2801 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
2805 ads_mod_str(ctx
, &mods
, "objectClass", "Computer");
2806 ads_mod_str(ctx
, &mods
, "SamAccountName", samAccountName
);
2807 ads_mod_str(ctx
, &mods
, "userAccountControl", controlstr
);
2808 ads_mod_str(ctx
, &mods
, "DnsHostName", dns_hostname
);
2809 ads_mod_strlist(ctx
, &mods
, "ServicePrincipalName", spn_array
);
2810 ads_mod_ber(ctx
, &mods
, "unicodePwd", &machine_pw_val
);
2812 ret
= ads_gen_add(ads
, new_dn
, mods
);
2815 SAFE_FREE(machine_escaped
);
2816 talloc_destroy(ctx
);
2822 * move a machine account to another OU on the ADS server
2823 * @param ads - An initialized ADS_STRUCT
2824 * @param machine_name - the NetBIOS machine name of this account.
2825 * @param org_unit - The LDAP path in which to place this account
2826 * @param moved - whether we moved the machine account (optional)
2827 * @return 0 upon success, or non-zero otherwise
2830 ADS_STATUS
ads_move_machine_acct(ADS_STRUCT
*ads
, const char *machine_name
,
2831 const char *org_unit
, bool *moved
)
2835 LDAPMessage
*res
= NULL
;
2836 char *filter
= NULL
;
2837 char *computer_dn
= NULL
;
2839 char *computer_rdn
= NULL
;
2840 bool need_move
= False
;
2842 if (asprintf(&filter
, "(samAccountName=%s$)", machine_name
) == -1) {
2843 rc
= ADS_ERROR(LDAP_NO_MEMORY
);
2847 /* Find pre-existing machine */
2848 rc
= ads_search(ads
, &res
, filter
, NULL
);
2849 if (!ADS_ERR_OK(rc
)) {
2853 computer_dn
= ads_get_dn(ads
, talloc_tos(), res
);
2855 rc
= ADS_ERROR(LDAP_NO_MEMORY
);
2859 parent_dn
= ads_parent_dn(computer_dn
);
2860 if (strequal(parent_dn
, org_unit
)) {
2866 if (asprintf(&computer_rdn
, "CN=%s", machine_name
) == -1) {
2867 rc
= ADS_ERROR(LDAP_NO_MEMORY
);
2871 ldap_status
= ldap_rename_s(ads
->ldap
.ld
, computer_dn
, computer_rdn
,
2872 org_unit
, 1, NULL
, NULL
);
2873 rc
= ADS_ERROR(ldap_status
);
2876 ads_msgfree(ads
, res
);
2878 TALLOC_FREE(computer_dn
);
2879 SAFE_FREE(computer_rdn
);
2881 if (!ADS_ERR_OK(rc
)) {
2893 dump a binary result from ldap
2895 static void dump_binary(ADS_STRUCT
*ads
, const char *field
, struct berval
**values
)
2898 for (i
=0; values
[i
]; i
++) {
2900 printf("%s: ", field
);
2901 for (j
=0; j
<values
[i
]->bv_len
; j
++) {
2902 printf("%02X", (unsigned char)values
[i
]->bv_val
[j
]);
2908 static void dump_guid(ADS_STRUCT
*ads
, const char *field
, struct berval
**values
)
2911 for (i
=0; values
[i
]; i
++) {
2913 DATA_BLOB in
= data_blob_const(values
[i
]->bv_val
, values
[i
]->bv_len
);
2916 status
= GUID_from_ndr_blob(&in
, &guid
);
2917 if (NT_STATUS_IS_OK(status
)) {
2918 printf("%s: %s\n", field
, GUID_string(talloc_tos(), &guid
));
2920 printf("%s: INVALID GUID\n", field
);
2926 dump a sid result from ldap
2928 static void dump_sid(ADS_STRUCT
*ads
, const char *field
, struct berval
**values
)
2931 for (i
=0; values
[i
]; i
++) {
2934 struct dom_sid_buf tmp
;
2935 ret
= sid_parse((const uint8_t *)values
[i
]->bv_val
,
2936 values
[i
]->bv_len
, &sid
);
2940 printf("%s: %s\n", field
, dom_sid_str_buf(&sid
, &tmp
));
2945 dump ntSecurityDescriptor
2947 static void dump_sd(ADS_STRUCT
*ads
, const char *filed
, struct berval
**values
)
2949 TALLOC_CTX
*frame
= talloc_stackframe();
2950 struct security_descriptor
*psd
;
2953 status
= unmarshall_sec_desc(talloc_tos(), (uint8_t *)values
[0]->bv_val
,
2954 values
[0]->bv_len
, &psd
);
2955 if (!NT_STATUS_IS_OK(status
)) {
2956 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
2957 nt_errstr(status
)));
2963 ads_disp_sd(ads
, talloc_tos(), psd
);
2970 dump a string result from ldap
2972 static void dump_string(const char *field
, char **values
)
2975 for (i
=0; values
[i
]; i
++) {
2976 printf("%s: %s\n", field
, values
[i
]);
2981 dump a field from LDAP on stdout
2985 static bool ads_dump_field(ADS_STRUCT
*ads
, char *field
, void **values
, void *data_area
)
2990 void (*handler
)(ADS_STRUCT
*, const char *, struct berval
**);
2992 {"objectGUID", False
, dump_guid
},
2993 {"netbootGUID", False
, dump_guid
},
2994 {"nTSecurityDescriptor", False
, dump_sd
},
2995 {"dnsRecord", False
, dump_binary
},
2996 {"objectSid", False
, dump_sid
},
2997 {"securityIdentifier", False
, dump_sid
},
2998 {"tokenGroups", False
, dump_sid
},
2999 {"tokenGroupsNoGCAcceptable", False
, dump_sid
},
3000 {"tokengroupsGlobalandUniversal", False
, dump_sid
},
3001 {"mS-DS-CreatorSID", False
, dump_sid
},
3002 {"msExchMailboxGuid", False
, dump_guid
},
3003 {"msDS-TrustForestTrustInfo", False
, dump_binary
},
3008 if (!field
) { /* must be end of an entry */
3013 for (i
=0; handlers
[i
].name
; i
++) {
3014 if (strcasecmp_m(handlers
[i
].name
, field
) == 0) {
3015 if (!values
) /* first time, indicate string or not */
3016 return handlers
[i
].string
;
3017 handlers
[i
].handler(ads
, field
, (struct berval
**) values
);
3021 if (!handlers
[i
].name
) {
3022 if (!values
) /* first time, indicate string conversion */
3024 dump_string(field
, (char **)values
);
3030 * Dump a result from LDAP on stdout
3031 * used for debugging
3032 * @param ads connection to ads server
3033 * @param res Results to dump
3036 void ads_dump(ADS_STRUCT
*ads
, LDAPMessage
*res
)
3038 ads_process_results(ads
, res
, ads_dump_field
, NULL
);
3042 * Walk through results, calling a function for each entry found.
3043 * The function receives a field name, a berval * array of values,
3044 * and a data area passed through from the start. The function is
3045 * called once with null for field and values at the end of each
3047 * @param ads connection to ads server
3048 * @param res Results to process
3049 * @param fn Function for processing each result
3050 * @param data_area user-defined area to pass to function
3052 void ads_process_results(ADS_STRUCT
*ads
, LDAPMessage
*res
,
3053 bool (*fn
)(ADS_STRUCT
*, char *, void **, void *),
3058 size_t converted_size
;
3060 if (!(ctx
= talloc_init("ads_process_results")))
3063 for (msg
= ads_first_entry(ads
, res
); msg
;
3064 msg
= ads_next_entry(ads
, msg
)) {
3068 for (utf8_field
=ldap_first_attribute(ads
->ldap
.ld
,
3069 (LDAPMessage
*)msg
,&b
);
3071 utf8_field
=ldap_next_attribute(ads
->ldap
.ld
,
3072 (LDAPMessage
*)msg
,b
)) {
3073 struct berval
**ber_vals
;
3079 if (!pull_utf8_talloc(ctx
, &field
, utf8_field
,
3082 DEBUG(0,("ads_process_results: "
3083 "pull_utf8_talloc failed: %s\n",
3087 string
= fn(ads
, field
, NULL
, data_area
);
3092 utf8_vals
= ldap_get_values(ads
->ldap
.ld
,
3093 (LDAPMessage
*)msg
, field
);
3094 p
= discard_const_p(const char *, utf8_vals
);
3095 str_vals
= ads_pull_strvals(ctx
, p
);
3096 fn(ads
, field
, (void **) str_vals
, data_area
);
3097 ldap_value_free(utf8_vals
);
3099 ber_vals
= ldap_get_values_len(ads
->ldap
.ld
,
3100 (LDAPMessage
*)msg
, field
);
3101 fn(ads
, field
, (void **) ber_vals
, data_area
);
3103 ldap_value_free_len(ber_vals
);
3105 ldap_memfree(utf8_field
);
3108 talloc_free_children(ctx
);
3109 fn(ads
, NULL
, NULL
, data_area
); /* completed an entry */
3112 talloc_destroy(ctx
);
3116 * count how many replies are in a LDAPMessage
3117 * @param ads connection to ads server
3118 * @param res Results to count
3119 * @return number of replies
3121 int ads_count_replies(ADS_STRUCT
*ads
, void *res
)
3123 return ldap_count_entries(ads
->ldap
.ld
, (LDAPMessage
*)res
);
3127 * pull the first entry from a ADS result
3128 * @param ads connection to ads server
3129 * @param res Results of search
3130 * @return first entry from result
3132 LDAPMessage
*ads_first_entry(ADS_STRUCT
*ads
, LDAPMessage
*res
)
3134 return ldap_first_entry(ads
->ldap
.ld
, res
);
3138 * pull the next entry from a ADS result
3139 * @param ads connection to ads server
3140 * @param res Results of search
3141 * @return next entry from result
3143 LDAPMessage
*ads_next_entry(ADS_STRUCT
*ads
, LDAPMessage
*res
)
3145 return ldap_next_entry(ads
->ldap
.ld
, res
);
3149 * pull the first message from a ADS result
3150 * @param ads connection to ads server
3151 * @param res Results of search
3152 * @return first message from result
3154 LDAPMessage
*ads_first_message(ADS_STRUCT
*ads
, LDAPMessage
*res
)
3156 return ldap_first_message(ads
->ldap
.ld
, res
);
3160 * pull the next message from a ADS result
3161 * @param ads connection to ads server
3162 * @param res Results of search
3163 * @return next message from result
3165 LDAPMessage
*ads_next_message(ADS_STRUCT
*ads
, LDAPMessage
*res
)
3167 return ldap_next_message(ads
->ldap
.ld
, res
);
3171 * pull a single string from a ADS result
3172 * @param ads connection to ads server
3173 * @param mem_ctx TALLOC_CTX to use for allocating result string
3174 * @param msg Results of search
3175 * @param field Attribute to retrieve
3176 * @return Result string in talloc context
3178 char *ads_pull_string(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
, LDAPMessage
*msg
,
3184 size_t converted_size
;
3186 values
= ldap_get_values(ads
->ldap
.ld
, msg
, field
);
3190 if (values
[0] && pull_utf8_talloc(mem_ctx
, &ux_string
, values
[0],
3195 ldap_value_free(values
);
3200 * pull an array of strings from a ADS result
3201 * @param ads connection to ads server
3202 * @param mem_ctx TALLOC_CTX to use for allocating result string
3203 * @param msg Results of search
3204 * @param field Attribute to retrieve
3205 * @return Result strings in talloc context
3207 char **ads_pull_strings(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
,
3208 LDAPMessage
*msg
, const char *field
,
3213 size_t i
, converted_size
;
3215 values
= ldap_get_values(ads
->ldap
.ld
, msg
, field
);
3219 *num_values
= ldap_count_values(values
);
3221 ret
= talloc_array(mem_ctx
, char *, *num_values
+ 1);
3223 ldap_value_free(values
);
3227 for (i
=0;i
<*num_values
;i
++) {
3228 if (!pull_utf8_talloc(mem_ctx
, &ret
[i
], values
[i
],
3231 ldap_value_free(values
);
3237 ldap_value_free(values
);
3242 * pull an array of strings from a ADS result
3243 * (handle large multivalue attributes with range retrieval)
3244 * @param ads connection to ads server
3245 * @param mem_ctx TALLOC_CTX to use for allocating result string
3246 * @param msg Results of search
3247 * @param field Attribute to retrieve
3248 * @param current_strings strings returned by a previous call to this function
3249 * @param next_attribute The next query should ask for this attribute
3250 * @param num_values How many values did we get this time?
3251 * @param more_values Are there more values to get?
3252 * @return Result strings in talloc context
3254 char **ads_pull_strings_range(ADS_STRUCT
*ads
,
3255 TALLOC_CTX
*mem_ctx
,
3256 LDAPMessage
*msg
, const char *field
,
3257 char **current_strings
,
3258 const char **next_attribute
,
3259 size_t *num_strings
,
3263 char *expected_range_attrib
, *range_attr
= NULL
;
3264 BerElement
*ptr
= NULL
;
3267 size_t num_new_strings
;
3268 unsigned long int range_start
;
3269 unsigned long int range_end
;
3271 /* we might have been given the whole lot anyway */
3272 if ((strings
= ads_pull_strings(ads
, mem_ctx
, msg
, field
, num_strings
))) {
3273 *more_strings
= False
;
3277 expected_range_attrib
= talloc_asprintf(mem_ctx
, "%s;Range=", field
);
3279 /* look for Range result */
3280 for (attr
= ldap_first_attribute(ads
->ldap
.ld
, (LDAPMessage
*)msg
, &ptr
);
3282 attr
= ldap_next_attribute(ads
->ldap
.ld
, (LDAPMessage
*)msg
, ptr
)) {
3283 /* we ignore the fact that this is utf8, as all attributes are ascii... */
3284 if (strnequal(attr
, expected_range_attrib
, strlen(expected_range_attrib
))) {
3292 /* nothing here - this field is just empty */
3293 *more_strings
= False
;
3297 if (sscanf(&range_attr
[strlen(expected_range_attrib
)], "%lu-%lu",
3298 &range_start
, &range_end
) == 2) {
3299 *more_strings
= True
;
3301 if (sscanf(&range_attr
[strlen(expected_range_attrib
)], "%lu-*",
3302 &range_start
) == 1) {
3303 *more_strings
= False
;
3305 DEBUG(1, ("ads_pull_strings_range: Cannot parse Range attribute (%s)\n",
3307 ldap_memfree(range_attr
);
3308 *more_strings
= False
;
3313 if ((*num_strings
) != range_start
) {
3314 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) doesn't start at %u, but at %lu"
3315 " - aborting range retrieval\n",
3316 range_attr
, (unsigned int)(*num_strings
) + 1, range_start
));
3317 ldap_memfree(range_attr
);
3318 *more_strings
= False
;
3322 new_strings
= ads_pull_strings(ads
, mem_ctx
, msg
, range_attr
, &num_new_strings
);
3324 if (*more_strings
&& ((*num_strings
+ num_new_strings
) != (range_end
+ 1))) {
3325 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) tells us we have %lu "
3326 "strings in this bunch, but we only got %lu - aborting range retrieval\n",
3327 range_attr
, (unsigned long int)range_end
- range_start
+ 1,
3328 (unsigned long int)num_new_strings
));
3329 ldap_memfree(range_attr
);
3330 *more_strings
= False
;
3334 strings
= talloc_realloc(mem_ctx
, current_strings
, char *,
3335 *num_strings
+ num_new_strings
);
3337 if (strings
== NULL
) {
3338 ldap_memfree(range_attr
);
3339 *more_strings
= False
;
3343 if (new_strings
&& num_new_strings
) {
3344 memcpy(&strings
[*num_strings
], new_strings
,
3345 sizeof(*new_strings
) * num_new_strings
);
3348 (*num_strings
) += num_new_strings
;
3350 if (*more_strings
) {
3351 *next_attribute
= talloc_asprintf(mem_ctx
,
3356 if (!*next_attribute
) {
3357 DEBUG(1, ("talloc_asprintf for next attribute failed!\n"));
3358 ldap_memfree(range_attr
);
3359 *more_strings
= False
;
3364 ldap_memfree(range_attr
);
3370 * pull a single uint32_t from a ADS result
3371 * @param ads connection to ads server
3372 * @param msg Results of search
3373 * @param field Attribute to retrieve
3374 * @param v Pointer to int to store result
3375 * @return boolean indicating success
3377 bool ads_pull_uint32(ADS_STRUCT
*ads
, LDAPMessage
*msg
, const char *field
,
3382 values
= ldap_get_values(ads
->ldap
.ld
, msg
, field
);
3386 ldap_value_free(values
);
3390 *v
= atoi(values
[0]);
3391 ldap_value_free(values
);
3396 * pull a single objectGUID from an ADS result
3397 * @param ads connection to ADS server
3398 * @param msg results of search
3399 * @param guid 37-byte area to receive text guid
3400 * @return boolean indicating success
3402 bool ads_pull_guid(ADS_STRUCT
*ads
, LDAPMessage
*msg
, struct GUID
*guid
)
3407 if (!smbldap_talloc_single_blob(talloc_tos(), ads
->ldap
.ld
, msg
, "objectGUID",
3412 status
= GUID_from_ndr_blob(&blob
, guid
);
3413 talloc_free(blob
.data
);
3414 return NT_STATUS_IS_OK(status
);
3419 * pull a single struct dom_sid from a ADS result
3420 * @param ads connection to ads server
3421 * @param msg Results of search
3422 * @param field Attribute to retrieve
3423 * @param sid Pointer to sid to store result
3424 * @return boolean indicating success
3426 bool ads_pull_sid(ADS_STRUCT
*ads
, LDAPMessage
*msg
, const char *field
,
3427 struct dom_sid
*sid
)
3429 return smbldap_pull_sid(ads
->ldap
.ld
, msg
, field
, sid
);
3433 * pull an array of struct dom_sids from a ADS result
3434 * @param ads connection to ads server
3435 * @param mem_ctx TALLOC_CTX for allocating sid array
3436 * @param msg Results of search
3437 * @param field Attribute to retrieve
3438 * @param sids pointer to sid array to allocate
3439 * @return the count of SIDs pulled
3441 int ads_pull_sids(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
,
3442 LDAPMessage
*msg
, const char *field
, struct dom_sid
**sids
)
3444 struct berval
**values
;
3447 values
= ldap_get_values_len(ads
->ldap
.ld
, msg
, field
);
3452 for (i
=0; values
[i
]; i
++)
3456 (*sids
) = talloc_array(mem_ctx
, struct dom_sid
, i
);
3458 ldap_value_free_len(values
);
3466 for (i
=0; values
[i
]; i
++) {
3468 ret
= sid_parse((const uint8_t *)values
[i
]->bv_val
,
3469 values
[i
]->bv_len
, &(*sids
)[count
]);
3471 struct dom_sid_buf buf
;
3472 DBG_DEBUG("pulling SID: %s\n",
3473 dom_sid_str_buf(&(*sids
)[count
], &buf
));
3478 ldap_value_free_len(values
);
3483 * pull a struct security_descriptor from a ADS result
3484 * @param ads connection to ads server
3485 * @param mem_ctx TALLOC_CTX for allocating sid array
3486 * @param msg Results of search
3487 * @param field Attribute to retrieve
3488 * @param sd Pointer to *struct security_descriptor to store result (talloc()ed)
3489 * @return boolean indicating success
3491 bool ads_pull_sd(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
,
3492 LDAPMessage
*msg
, const char *field
,
3493 struct security_descriptor
**sd
)
3495 struct berval
**values
;
3498 values
= ldap_get_values_len(ads
->ldap
.ld
, msg
, field
);
3500 if (!values
) return false;
3504 status
= unmarshall_sec_desc(mem_ctx
,
3505 (uint8_t *)values
[0]->bv_val
,
3506 values
[0]->bv_len
, sd
);
3507 if (!NT_STATUS_IS_OK(status
)) {
3508 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
3509 nt_errstr(status
)));
3514 ldap_value_free_len(values
);
3519 * in order to support usernames longer than 21 characters we need to
3520 * use both the sAMAccountName and the userPrincipalName attributes
3521 * It seems that not all users have the userPrincipalName attribute set
3523 * @param ads connection to ads server
3524 * @param mem_ctx TALLOC_CTX for allocating sid array
3525 * @param msg Results of search
3526 * @return the username
3528 char *ads_pull_username(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
,
3534 /* lookup_name() only works on the sAMAccountName to
3535 returning the username portion of userPrincipalName
3536 breaks winbindd_getpwnam() */
3538 ret
= ads_pull_string(ads
, mem_ctx
, msg
, "userPrincipalName");
3539 if (ret
&& (p
= strchr_m(ret
, '@'))) {
3544 return ads_pull_string(ads
, mem_ctx
, msg
, "sAMAccountName");
3549 * find the update serial number - this is the core of the ldap cache
3550 * @param ads connection to ads server
3551 * @param ads connection to ADS server
3552 * @param usn Pointer to retrieved update serial number
3553 * @return status of search
3555 ADS_STATUS
ads_USN(ADS_STRUCT
*ads
, uint32_t *usn
)
3557 const char *attrs
[] = {"highestCommittedUSN", NULL
};
3561 status
= ads_do_search_retry(ads
, "", LDAP_SCOPE_BASE
, "(objectclass=*)", attrs
, &res
);
3562 if (!ADS_ERR_OK(status
))
3565 if (ads_count_replies(ads
, res
) != 1) {
3566 ads_msgfree(ads
, res
);
3567 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED
);
3570 if (!ads_pull_uint32(ads
, res
, "highestCommittedUSN", usn
)) {
3571 ads_msgfree(ads
, res
);
3572 return ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE
);
3575 ads_msgfree(ads
, res
);
3579 /* parse a ADS timestring - typical string is
3580 '20020917091222.0Z0' which means 09:12.22 17th September
3582 static time_t ads_parse_time(const char *str
)
3588 if (sscanf(str
, "%4d%2d%2d%2d%2d%2d",
3589 &tm
.tm_year
, &tm
.tm_mon
, &tm
.tm_mday
,
3590 &tm
.tm_hour
, &tm
.tm_min
, &tm
.tm_sec
) != 6) {
3599 /********************************************************************
3600 ********************************************************************/
3602 ADS_STATUS
ads_current_time(ADS_STRUCT
*ads
)
3604 const char *attrs
[] = {"currentTime", NULL
};
3608 TALLOC_CTX
*tmp_ctx
= talloc_stackframe();
3609 ADS_STRUCT
*ads_s
= ads
;
3611 /* establish a new ldap tcp session if necessary */
3613 if ( !ads
->ldap
.ld
) {
3615 * ADS_STRUCT may be being reused after a
3616 * DC lookup, so ads->ldap.ss may already have a
3617 * good address. If not, re-initialize the passed-in
3618 * ADS_STRUCT with the given server.XXXX parameters.
3620 * Note that this doesn't depend on
3621 * ads->server.ldap_server != NULL,
3622 * as the case where ads->server.ldap_server==NULL and
3623 * ads->ldap.ss != zero_address is precisely the DC
3624 * lookup case where ads->ldap.ss was found by going
3625 * through ads_find_dc() again we want to avoid repeating.
3627 if (is_zero_addr(&ads
->ldap
.ss
)) {
3628 ads_s
= ads_init(tmp_ctx
,
3630 ads
->server
.workgroup
,
3631 ads
->server
.ldap_server
,
3633 if (ads_s
== NULL
) {
3634 status
= ADS_ERROR(LDAP_NO_MEMORY
);
3640 * Reset ads->config.flags as it can contain the flags
3641 * returned by the previous CLDAP ping when reusing the struct.
3643 ads_s
->config
.flags
= 0;
3645 status
= ads_connect_simple_anon(ads_s
);
3646 if ( !ADS_ERR_OK(status
))
3650 status
= ads_do_search(ads_s
, "", LDAP_SCOPE_BASE
, "(objectclass=*)", attrs
, &res
);
3651 if (!ADS_ERR_OK(status
)) {
3655 timestr
= ads_pull_string(ads_s
, tmp_ctx
, res
, "currentTime");
3657 ads_msgfree(ads_s
, res
);
3658 status
= ADS_ERROR(LDAP_NO_RESULTS_RETURNED
);
3662 /* but save the time and offset in the original ADS_STRUCT */
3664 ads
->config
.current_time
= ads_parse_time(timestr
);
3666 if (ads
->config
.current_time
!= 0) {
3667 ads
->config
.time_offset
= ads
->config
.current_time
- time(NULL
);
3668 DBG_INFO("server time offset is %d seconds\n",
3669 ads
->config
.time_offset
);
3671 ads
->config
.time_offset
= 0;
3674 DBG_INFO("server time offset is %d seconds\n",
3675 ads
->config
.time_offset
);
3677 ads_msgfree(ads
, res
);
3679 status
= ADS_SUCCESS
;
3682 TALLOC_FREE(tmp_ctx
);
3687 /********************************************************************
3688 ********************************************************************/
3690 ADS_STATUS
ads_domain_func_level(ADS_STRUCT
*ads
, uint32_t *val
)
3692 TALLOC_CTX
*tmp_ctx
= talloc_stackframe();
3693 const char *attrs
[] = {"domainFunctionality", NULL
};
3696 ADS_STRUCT
*ads_s
= ads
;
3698 *val
= DS_DOMAIN_FUNCTION_2000
;
3700 /* establish a new ldap tcp session if necessary */
3702 if ( !ads
->ldap
.ld
) {
3704 * ADS_STRUCT may be being reused after a
3705 * DC lookup, so ads->ldap.ss may already have a
3706 * good address. If not, re-initialize the passed-in
3707 * ADS_STRUCT with the given server.XXXX parameters.
3709 * Note that this doesn't depend on
3710 * ads->server.ldap_server != NULL,
3711 * as the case where ads->server.ldap_server==NULL and
3712 * ads->ldap.ss != zero_address is precisely the DC
3713 * lookup case where ads->ldap.ss was found by going
3714 * through ads_find_dc() again we want to avoid repeating.
3716 if (is_zero_addr(&ads
->ldap
.ss
)) {
3717 ads_s
= ads_init(tmp_ctx
,
3719 ads
->server
.workgroup
,
3720 ads
->server
.ldap_server
,
3722 if (ads_s
== NULL
) {
3723 status
= ADS_ERROR_NT(NT_STATUS_NO_MEMORY
);
3729 * Reset ads->config.flags as it can contain the flags
3730 * returned by the previous CLDAP ping when reusing the struct.
3732 ads_s
->config
.flags
= 0;
3734 status
= ads_connect_simple_anon(ads_s
);
3735 if ( !ADS_ERR_OK(status
))
3739 /* If the attribute does not exist assume it is a Windows 2000
3740 functional domain */
3742 status
= ads_do_search(ads_s
, "", LDAP_SCOPE_BASE
, "(objectclass=*)", attrs
, &res
);
3743 if (!ADS_ERR_OK(status
)) {
3744 if ( status
.err
.rc
== LDAP_NO_SUCH_ATTRIBUTE
) {
3745 status
= ADS_SUCCESS
;
3750 if ( !ads_pull_uint32(ads_s
, res
, "domainFunctionality", val
) ) {
3751 DEBUG(5,("ads_domain_func_level: Failed to pull the domainFunctionality attribute.\n"));
3753 DEBUG(3,("ads_domain_func_level: %d\n", *val
));
3756 ads_msgfree(ads_s
, res
);
3759 TALLOC_FREE(tmp_ctx
);
3765 * find the domain sid for our domain
3766 * @param ads connection to ads server
3767 * @param sid Pointer to domain sid
3768 * @return status of search
3770 ADS_STATUS
ads_domain_sid(ADS_STRUCT
*ads
, struct dom_sid
*sid
)
3772 const char *attrs
[] = {"objectSid", NULL
};
3776 rc
= ads_do_search_retry(ads
, ads
->config
.bind_path
, LDAP_SCOPE_BASE
, "(objectclass=*)",
3778 if (!ADS_ERR_OK(rc
)) return rc
;
3779 if (!ads_pull_sid(ads
, res
, "objectSid", sid
)) {
3780 ads_msgfree(ads
, res
);
3781 return ADS_ERROR_SYSTEM(ENOENT
);
3783 ads_msgfree(ads
, res
);
3789 * find our site name
3790 * @param ads connection to ads server
3791 * @param mem_ctx Pointer to talloc context
3792 * @param site_name Pointer to the sitename
3793 * @return status of search
3795 ADS_STATUS
ads_site_dn(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
, const char **site_name
)
3799 const char *dn
, *service_name
;
3800 const char *attrs
[] = { "dsServiceName", NULL
};
3802 status
= ads_do_search(ads
, "", LDAP_SCOPE_BASE
, "(objectclass=*)", attrs
, &res
);
3803 if (!ADS_ERR_OK(status
)) {
3807 service_name
= ads_pull_string(ads
, mem_ctx
, res
, "dsServiceName");
3808 if (service_name
== NULL
) {
3809 ads_msgfree(ads
, res
);
3810 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED
);
3813 ads_msgfree(ads
, res
);
3815 /* go up three levels */
3816 dn
= ads_parent_dn(ads_parent_dn(ads_parent_dn(service_name
)));
3818 return ADS_ERROR(LDAP_NO_MEMORY
);
3821 *site_name
= talloc_strdup(mem_ctx
, dn
);
3822 if (*site_name
== NULL
) {
3823 return ADS_ERROR(LDAP_NO_MEMORY
);
3828 dsServiceName: CN=NTDS Settings,CN=W2K3DC,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,DC=ber,DC=suse,DC=de
3833 * find the site dn where a machine resides
3834 * @param ads connection to ads server
3835 * @param mem_ctx Pointer to talloc context
3836 * @param computer_name name of the machine
3837 * @param site_name Pointer to the sitename
3838 * @return status of search
3840 ADS_STATUS
ads_site_dn_for_machine(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
, const char *computer_name
, const char **site_dn
)
3844 const char *parent
, *filter
;
3845 char *config_context
= NULL
;
3848 /* shortcut a query */
3849 if (strequal(computer_name
, ads
->config
.ldap_server_name
)) {
3850 return ads_site_dn(ads
, mem_ctx
, site_dn
);
3853 status
= ads_config_path(ads
, mem_ctx
, &config_context
);
3854 if (!ADS_ERR_OK(status
)) {
3858 filter
= talloc_asprintf(mem_ctx
, "(cn=%s)", computer_name
);
3859 if (filter
== NULL
) {
3860 return ADS_ERROR(LDAP_NO_MEMORY
);
3863 status
= ads_do_search(ads
, config_context
, LDAP_SCOPE_SUBTREE
,
3864 filter
, NULL
, &res
);
3865 if (!ADS_ERR_OK(status
)) {
3869 if (ads_count_replies(ads
, res
) != 1) {
3870 ads_msgfree(ads
, res
);
3871 return ADS_ERROR(LDAP_NO_SUCH_OBJECT
);
3874 dn
= ads_get_dn(ads
, mem_ctx
, res
);
3876 ads_msgfree(ads
, res
);
3877 return ADS_ERROR(LDAP_NO_MEMORY
);
3880 /* go up three levels */
3881 parent
= ads_parent_dn(ads_parent_dn(ads_parent_dn(dn
)));
3882 if (parent
== NULL
) {
3883 ads_msgfree(ads
, res
);
3885 return ADS_ERROR(LDAP_NO_MEMORY
);
3888 *site_dn
= talloc_strdup(mem_ctx
, parent
);
3889 if (*site_dn
== NULL
) {
3890 ads_msgfree(ads
, res
);
3892 return ADS_ERROR(LDAP_NO_MEMORY
);
3896 ads_msgfree(ads
, res
);
3902 * get the upn suffixes for a domain
3903 * @param ads connection to ads server
3904 * @param mem_ctx Pointer to talloc context
3905 * @param suffixes Pointer to an array of suffixes
3906 * @param num_suffixes Pointer to the number of suffixes
3907 * @return status of search
3909 ADS_STATUS
ads_upn_suffixes(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
, char ***suffixes
, size_t *num_suffixes
)
3914 char *config_context
= NULL
;
3915 const char *attrs
[] = { "uPNSuffixes", NULL
};
3917 status
= ads_config_path(ads
, mem_ctx
, &config_context
);
3918 if (!ADS_ERR_OK(status
)) {
3922 base
= talloc_asprintf(mem_ctx
, "cn=Partitions,%s", config_context
);
3924 return ADS_ERROR(LDAP_NO_MEMORY
);
3927 status
= ads_search_dn(ads
, &res
, base
, attrs
);
3928 if (!ADS_ERR_OK(status
)) {
3932 if (ads_count_replies(ads
, res
) != 1) {
3933 ads_msgfree(ads
, res
);
3934 return ADS_ERROR(LDAP_NO_SUCH_OBJECT
);
3937 (*suffixes
) = ads_pull_strings(ads
, mem_ctx
, res
, "uPNSuffixes", num_suffixes
);
3938 if ((*suffixes
) == NULL
) {
3939 ads_msgfree(ads
, res
);
3940 return ADS_ERROR(LDAP_NO_MEMORY
);
3943 ads_msgfree(ads
, res
);
3949 * get the joinable ous for a domain
3950 * @param ads connection to ads server
3951 * @param mem_ctx Pointer to talloc context
3952 * @param ous Pointer to an array of ous
3953 * @param num_ous Pointer to the number of ous
3954 * @return status of search
3956 ADS_STATUS
ads_get_joinable_ous(ADS_STRUCT
*ads
,
3957 TALLOC_CTX
*mem_ctx
,
3962 LDAPMessage
*res
= NULL
;
3963 LDAPMessage
*msg
= NULL
;
3964 const char *attrs
[] = { "dn", NULL
};
3967 status
= ads_search(ads
, &res
,
3968 "(|(objectClass=domain)(objectclass=organizationalUnit))",
3970 if (!ADS_ERR_OK(status
)) {
3974 count
= ads_count_replies(ads
, res
);
3976 ads_msgfree(ads
, res
);
3977 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED
);
3980 for (msg
= ads_first_entry(ads
, res
); msg
;
3981 msg
= ads_next_entry(ads
, msg
)) {
3982 const char **p
= discard_const_p(const char *, *ous
);
3985 dn
= ads_get_dn(ads
, talloc_tos(), msg
);
3987 ads_msgfree(ads
, res
);
3988 return ADS_ERROR(LDAP_NO_MEMORY
);
3991 if (!add_string_to_array(mem_ctx
, dn
, &p
, num_ous
)) {
3993 ads_msgfree(ads
, res
);
3994 return ADS_ERROR(LDAP_NO_MEMORY
);
3998 *ous
= discard_const_p(char *, p
);
4001 ads_msgfree(ads
, res
);
4008 * pull a struct dom_sid from an extended dn string
4009 * @param mem_ctx TALLOC_CTX
4010 * @param extended_dn string
4011 * @param flags string type of extended_dn
4012 * @param sid pointer to a struct dom_sid
4013 * @return NT_STATUS_OK on success,
4014 * NT_INVALID_PARAMETER on error,
4015 * NT_STATUS_NOT_FOUND if no SID present
4017 ADS_STATUS
ads_get_sid_from_extended_dn(TALLOC_CTX
*mem_ctx
,
4018 const char *extended_dn
,
4019 enum ads_extended_dn_flags flags
,
4020 struct dom_sid
*sid
)
4025 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER
);
4028 /* otherwise extended_dn gets stripped off */
4029 if ((dn
= talloc_strdup(mem_ctx
, extended_dn
)) == NULL
) {
4030 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER
);
4033 * ADS_EXTENDED_DN_HEX_STRING:
4034 * <GUID=238e1963cb390f4bb032ba0105525a29>;<SID=010500000000000515000000bb68c8fd6b61b427572eb04556040000>;CN=gd,OU=berlin,OU=suse,DC=ber,DC=suse,DC=de
4036 * ADS_EXTENDED_DN_STRING (only with w2k3):
4037 * <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
4039 * Object with no SID, such as an Exchange Public Folder
4040 * <GUID=28907fb4bdf6854993e7f0a10b504e7c>;CN=public,CN=Microsoft Exchange System Objects,DC=sd2k3ms,DC=west,DC=isilon,DC=com
4043 p
= strchr(dn
, ';');
4045 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER
);
4048 if (strncmp(p
, ";<SID=", strlen(";<SID=")) != 0) {
4049 DEBUG(5,("No SID present in extended dn\n"));
4050 return ADS_ERROR_NT(NT_STATUS_NOT_FOUND
);
4053 p
+= strlen(";<SID=");
4057 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER
);
4062 DEBUG(100,("ads_get_sid_from_extended_dn: sid string is %s\n", p
));
4066 case ADS_EXTENDED_DN_STRING
:
4067 if (!string_to_sid(sid
, p
)) {
4068 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER
);
4071 case ADS_EXTENDED_DN_HEX_STRING
: {
4076 buf_len
= strhex_to_str(buf
, sizeof(buf
), p
, strlen(p
));
4078 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER
);
4081 ret
= sid_parse((const uint8_t *)buf
, buf_len
, sid
);
4083 DEBUG(10,("failed to parse sid\n"));
4084 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER
);
4089 DEBUG(10,("unknown extended dn format\n"));
4090 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER
);
4093 return ADS_ERROR_NT(NT_STATUS_OK
);
4096 /********************************************************************
4097 ********************************************************************/
4099 char* ads_get_upn( ADS_STRUCT
*ads
, TALLOC_CTX
*ctx
, const char *machine_name
)
4101 LDAPMessage
*res
= NULL
;
4106 status
= ads_find_machine_acct(ads
, &res
, machine_name
);
4107 if (!ADS_ERR_OK(status
)) {
4108 DEBUG(0,("ads_get_upn: Failed to find account for %s\n",
4109 lp_netbios_name()));
4113 if ( (count
= ads_count_replies(ads
, res
)) != 1 ) {
4114 DEBUG(1,("ads_get_upn: %d entries returned!\n", count
));
4118 if ( (name
= ads_pull_string(ads
, ctx
, res
, "userPrincipalName")) == NULL
) {
4119 DEBUG(2,("ads_get_upn: No userPrincipalName attribute!\n"));
4123 ads_msgfree(ads
, res
);
4130 SAVED CODE
- we used to join via ldap
- remember how we did
this. JRA
.
4133 * Join a machine to a realm
4134 * Creates the machine account and sets the machine password
4135 * @param ads connection to ads server
4136 * @param machine name of host to add
4137 * @param org_unit Organizational unit to place machine in
4138 * @return status of join
4140 ADS_STATUS
ads_join_realm(ADS_STRUCT
*ads
, const char *machine_name
,
4141 uint32_t account_type
, const char *org_unit
)
4144 LDAPMessage
*res
= NULL
;
4147 /* machine name must be lowercase */
4148 machine
= SMB_STRDUP(machine_name
);
4149 strlower_m(machine
);
4152 status = ads_find_machine_acct(ads, (void **)&res, machine);
4153 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
4154 DEBUG(0, ("Host account for %s already exists - deleting old account\n", machine));
4155 status = ads_leave_realm(ads, machine);
4156 if (!ADS_ERR_OK(status)) {
4157 DEBUG(0, ("Failed to delete host '%s' from the '%s' realm.\n",
4158 machine, ads->config.realm));
4163 status
= ads_add_machine_acct(ads
, machine
, account_type
, org_unit
);
4164 if (!ADS_ERR_OK(status
)) {
4165 DEBUG(0, ("ads_join_realm: ads_add_machine_acct failed (%s): %s\n", machine
, ads_errstr(status
)));
4170 status
= ads_find_machine_acct(ads
, (void **)(void *)&res
, machine
);
4171 if (!ADS_ERR_OK(status
)) {
4172 DEBUG(0, ("ads_join_realm: Host account test failed for machine %s\n", machine
));
4178 ads_msgfree(ads
, res
);
4185 * Delete a machine from the realm
4186 * @param ads connection to ads server
4187 * @param hostname Machine to remove
4188 * @return status of delete
4190 ADS_STATUS
ads_leave_realm(ADS_STRUCT
*ads
, const char *hostname
)
4195 char *hostnameDN
, *host
;
4197 LDAPControl ldap_control
;
4198 LDAPControl
* pldap_control
[2] = {NULL
, NULL
};
4200 pldap_control
[0] = &ldap_control
;
4201 memset(&ldap_control
, 0, sizeof(LDAPControl
));
4202 ldap_control
.ldctl_oid
= discard_const_p(char, LDAP_SERVER_TREE_DELETE_OID
);
4204 /* hostname must be lowercase */
4205 host
= SMB_STRDUP(hostname
);
4206 if (!strlower_m(host
)) {
4208 return ADS_ERROR_SYSTEM(EINVAL
);
4211 status
= ads_find_machine_acct(ads
, &res
, host
);
4212 if (!ADS_ERR_OK(status
)) {
4213 DEBUG(0, ("Host account for %s does not exist.\n", host
));
4218 msg
= ads_first_entry(ads
, res
);
4221 return ADS_ERROR_SYSTEM(ENOENT
);
4224 hostnameDN
= ads_get_dn(ads
, talloc_tos(), (LDAPMessage
*)msg
);
4225 if (hostnameDN
== NULL
) {
4227 return ADS_ERROR_SYSTEM(ENOENT
);
4230 rc
= ldap_delete_ext_s(ads
->ldap
.ld
, hostnameDN
, pldap_control
, NULL
);
4232 DEBUG(3,("ldap_delete_ext_s failed with error code %d\n", rc
));
4234 DEBUG(3,("ldap_delete_ext_s succeeded with error code %d\n", rc
));
4237 if (rc
!= LDAP_SUCCESS
) {
4238 const char *attrs
[] = { "cn", NULL
};
4239 LDAPMessage
*msg_sub
;
4241 /* we only search with scope ONE, we do not expect any further
4242 * objects to be created deeper */
4244 status
= ads_do_search_retry(ads
, hostnameDN
,
4245 LDAP_SCOPE_ONELEVEL
,
4246 "(objectclass=*)", attrs
, &res
);
4248 if (!ADS_ERR_OK(status
)) {
4250 TALLOC_FREE(hostnameDN
);
4254 for (msg_sub
= ads_first_entry(ads
, res
); msg_sub
;
4255 msg_sub
= ads_next_entry(ads
, msg_sub
)) {
4259 if ((dn
= ads_get_dn(ads
, talloc_tos(), msg_sub
)) == NULL
) {
4261 TALLOC_FREE(hostnameDN
);
4262 return ADS_ERROR(LDAP_NO_MEMORY
);
4265 status
= ads_del_dn(ads
, dn
);
4266 if (!ADS_ERR_OK(status
)) {
4267 DEBUG(3,("failed to delete dn %s: %s\n", dn
, ads_errstr(status
)));
4270 TALLOC_FREE(hostnameDN
);
4277 /* there should be no subordinate objects anymore */
4278 status
= ads_do_search_retry(ads
, hostnameDN
,
4279 LDAP_SCOPE_ONELEVEL
,
4280 "(objectclass=*)", attrs
, &res
);
4282 if (!ADS_ERR_OK(status
) || ( (ads_count_replies(ads
, res
)) > 0 ) ) {
4284 TALLOC_FREE(hostnameDN
);
4288 /* delete hostnameDN now */
4289 status
= ads_del_dn(ads
, hostnameDN
);
4290 if (!ADS_ERR_OK(status
)) {
4292 DEBUG(3,("failed to delete dn %s: %s\n", hostnameDN
, ads_errstr(status
)));
4293 TALLOC_FREE(hostnameDN
);
4298 TALLOC_FREE(hostnameDN
);
4300 status
= ads_find_machine_acct(ads
, &res
, host
);
4301 if ((status
.error_type
== ENUM_ADS_ERROR_LDAP
) &&
4302 (status
.err
.rc
!= LDAP_NO_SUCH_OBJECT
)) {
4303 DEBUG(3, ("Failed to remove host account.\n"));
4313 * pull all token-sids from an LDAP dn
4314 * @param ads connection to ads server
4315 * @param mem_ctx TALLOC_CTX for allocating sid array
4316 * @param dn of LDAP object
4317 * @param user_sid pointer to struct dom_sid (objectSid)
4318 * @param primary_group_sid pointer to struct dom_sid (self composed)
4319 * @param sids pointer to sid array to allocate
4320 * @param num_sids counter of SIDs pulled
4321 * @return status of token query
4323 ADS_STATUS
ads_get_tokensids(ADS_STRUCT
*ads
,
4324 TALLOC_CTX
*mem_ctx
,
4326 struct dom_sid
*user_sid
,
4327 struct dom_sid
*primary_group_sid
,
4328 struct dom_sid
**sids
,
4332 LDAPMessage
*res
= NULL
;
4334 size_t tmp_num_sids
;
4335 struct dom_sid
*tmp_sids
;
4336 struct dom_sid tmp_user_sid
;
4337 struct dom_sid tmp_primary_group_sid
;
4339 const char *attrs
[] = {
4346 status
= ads_search_retry_dn(ads
, &res
, dn
, attrs
);
4347 if (!ADS_ERR_OK(status
)) {
4351 count
= ads_count_replies(ads
, res
);
4353 ads_msgfree(ads
, res
);
4354 return ADS_ERROR_LDAP(LDAP_NO_SUCH_OBJECT
);
4357 if (!ads_pull_sid(ads
, res
, "objectSid", &tmp_user_sid
)) {
4358 ads_msgfree(ads
, res
);
4359 return ADS_ERROR_LDAP(LDAP_NO_MEMORY
);
4362 if (!ads_pull_uint32(ads
, res
, "primaryGroupID", &pgid
)) {
4363 ads_msgfree(ads
, res
);
4364 return ADS_ERROR_LDAP(LDAP_NO_MEMORY
);
4368 /* hack to compose the primary group sid without knowing the
4371 struct dom_sid domsid
;
4373 sid_copy(&domsid
, &tmp_user_sid
);
4375 if (!sid_split_rid(&domsid
, NULL
)) {
4376 ads_msgfree(ads
, res
);
4377 return ADS_ERROR_LDAP(LDAP_NO_MEMORY
);
4380 if (!sid_compose(&tmp_primary_group_sid
, &domsid
, pgid
)) {
4381 ads_msgfree(ads
, res
);
4382 return ADS_ERROR_LDAP(LDAP_NO_MEMORY
);
4386 tmp_num_sids
= ads_pull_sids(ads
, mem_ctx
, res
, "tokenGroups", &tmp_sids
);
4388 if (tmp_num_sids
== 0 || !tmp_sids
) {
4389 ads_msgfree(ads
, res
);
4390 return ADS_ERROR_LDAP(LDAP_NO_MEMORY
);
4394 *num_sids
= tmp_num_sids
;
4402 *user_sid
= tmp_user_sid
;
4405 if (primary_group_sid
) {
4406 *primary_group_sid
= tmp_primary_group_sid
;
4409 DEBUG(10,("ads_get_tokensids: returned %d sids\n", (int)tmp_num_sids
+ 2));
4411 ads_msgfree(ads
, res
);
4412 return ADS_ERROR_LDAP(LDAP_SUCCESS
);
4416 * Find a sAMAccountName in LDAP
4417 * @param ads connection to ads server
4418 * @param mem_ctx TALLOC_CTX for allocating sid array
4419 * @param samaccountname to search
4420 * @param uac_ret uint32_t pointer userAccountControl attribute value
4421 * @param dn_ret pointer to dn
4422 * @return status of token query
4424 ADS_STATUS
ads_find_samaccount(ADS_STRUCT
*ads
,
4425 TALLOC_CTX
*mem_ctx
,
4426 const char *samaccountname
,
4428 const char **dn_ret
)
4431 const char *attrs
[] = { "userAccountControl", NULL
};
4433 LDAPMessage
*res
= NULL
;
4437 filter
= talloc_asprintf(mem_ctx
, "(&(objectclass=user)(sAMAccountName=%s))",
4439 if (filter
== NULL
) {
4440 status
= ADS_ERROR_NT(NT_STATUS_NO_MEMORY
);
4444 status
= ads_do_search_all(ads
, ads
->config
.bind_path
,
4446 filter
, attrs
, &res
);
4448 if (!ADS_ERR_OK(status
)) {
4452 if (ads_count_replies(ads
, res
) != 1) {
4453 status
= ADS_ERROR(LDAP_NO_RESULTS_RETURNED
);
4457 dn
= ads_get_dn(ads
, talloc_tos(), res
);
4459 status
= ADS_ERROR(LDAP_NO_MEMORY
);
4463 if (!ads_pull_uint32(ads
, res
, "userAccountControl", &uac
)) {
4464 status
= ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE
);
4473 *dn_ret
= talloc_strdup(mem_ctx
, dn
);
4475 status
= ADS_ERROR(LDAP_NO_MEMORY
);
4481 ads_msgfree(ads
, res
);
4487 * find our configuration path
4488 * @param ads connection to ads server
4489 * @param mem_ctx Pointer to talloc context
4490 * @param config_path Pointer to the config path
4491 * @return status of search
4493 ADS_STATUS
ads_config_path(ADS_STRUCT
*ads
,
4494 TALLOC_CTX
*mem_ctx
,
4498 LDAPMessage
*res
= NULL
;
4499 const char *config_context
= NULL
;
4500 const char *attrs
[] = { "configurationNamingContext", NULL
};
4502 status
= ads_do_search(ads
, "", LDAP_SCOPE_BASE
,
4503 "(objectclass=*)", attrs
, &res
);
4504 if (!ADS_ERR_OK(status
)) {
4508 config_context
= ads_pull_string(ads
, mem_ctx
, res
,
4509 "configurationNamingContext");
4510 ads_msgfree(ads
, res
);
4511 if (!config_context
) {
4512 return ADS_ERROR(LDAP_NO_MEMORY
);
4516 *config_path
= talloc_strdup(mem_ctx
, config_context
);
4517 if (!*config_path
) {
4518 return ADS_ERROR(LDAP_NO_MEMORY
);
4522 return ADS_ERROR(LDAP_SUCCESS
);
4526 * find the displayName of an extended right
4527 * @param ads connection to ads server
4528 * @param config_path The config path
4529 * @param mem_ctx Pointer to talloc context
4530 * @param GUID struct of the rightsGUID
4531 * @return status of search
4533 const char *ads_get_extended_right_name_by_guid(ADS_STRUCT
*ads
,
4534 const char *config_path
,
4535 TALLOC_CTX
*mem_ctx
,
4536 const struct GUID
*rights_guid
)
4539 LDAPMessage
*res
= NULL
;
4541 const char *attrs
[] = { "displayName", NULL
};
4542 const char *result
= NULL
;
4545 if (!ads
|| !mem_ctx
|| !rights_guid
) {
4549 expr
= talloc_asprintf(mem_ctx
, "(rightsGuid=%s)",
4550 GUID_string(mem_ctx
, rights_guid
));
4555 path
= talloc_asprintf(mem_ctx
, "cn=Extended-Rights,%s", config_path
);
4560 rc
= ads_do_search_retry(ads
, path
, LDAP_SCOPE_SUBTREE
,
4562 if (!ADS_ERR_OK(rc
)) {
4566 if (ads_count_replies(ads
, res
) != 1) {
4570 result
= ads_pull_string(ads
, mem_ctx
, res
, "displayName");
4573 ads_msgfree(ads
, res
);
4578 * verify or build and verify an account ou
4579 * @param mem_ctx Pointer to talloc context
4580 * @param ads connection to ads server
4582 * @return status of search
4585 ADS_STATUS
ads_check_ou_dn(TALLOC_CTX
*mem_ctx
,
4587 const char **account_ou
)
4593 if (account_ou
== NULL
) {
4594 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER
);
4597 if (*account_ou
!= NULL
) {
4598 exploded_dn
= ldap_explode_dn(*account_ou
, 0);
4600 ldap_value_free(exploded_dn
);
4605 ou_string
= ads_ou_string(ads
, *account_ou
);
4607 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX
);
4610 name
= talloc_asprintf(mem_ctx
, "%s,%s", ou_string
,
4611 ads
->config
.bind_path
);
4612 SAFE_FREE(ou_string
);
4615 return ADS_ERROR_LDAP(LDAP_NO_MEMORY
);
4618 exploded_dn
= ldap_explode_dn(name
, 0);
4620 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX
);
4622 ldap_value_free(exploded_dn
);