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 if (!(cldap_reply
->server_type
& NBT_SERVER_LDAP
)) {
278 DBG_WARNING("%s's CLDAP reply says it is not an LDAP server!\n",
284 /* Fill in the ads->config values */
286 ADS_TALLOC_CONST_FREE(ads
->config
.workgroup
);
287 ADS_TALLOC_CONST_FREE(ads
->config
.realm
);
288 ADS_TALLOC_CONST_FREE(ads
->config
.bind_path
);
289 ADS_TALLOC_CONST_FREE(ads
->config
.ldap_server_name
);
290 ADS_TALLOC_CONST_FREE(ads
->config
.server_site_name
);
291 ADS_TALLOC_CONST_FREE(ads
->config
.client_site_name
);
293 if (!check_cldap_reply_required_flags(cldap_reply
->server_type
,
294 ads
->config
.flags
)) {
299 ads
->config
.ldap_server_name
= talloc_strdup(ads
,
300 cldap_reply
->pdc_dns_name
);
301 if (ads
->config
.ldap_server_name
== NULL
) {
302 DBG_WARNING("Out of memory\n");
307 ads
->config
.workgroup
= talloc_strdup(ads
, cldap_reply
->domain_name
);
308 if (ads
->config
.workgroup
== NULL
) {
309 DBG_WARNING("Out of memory\n");
314 ads
->config
.realm
= talloc_asprintf_strupper_m(ads
,
316 cldap_reply
->dns_domain
);
317 if (ads
->config
.realm
== NULL
) {
318 DBG_WARNING("Out of memory\n");
323 status
= ads_build_dn(ads
->config
.realm
, ads
, &dn
);
324 if (!ADS_ERR_OK(status
)) {
325 DBG_DEBUG("Failed to build bind path: %s\n",
330 ads
->config
.bind_path
= dn
;
332 if (*cldap_reply
->server_site
) {
333 ads
->config
.server_site_name
=
334 talloc_strdup(ads
, cldap_reply
->server_site
);
335 if (ads
->config
.server_site_name
== NULL
) {
336 DBG_WARNING("Out of memory\n");
342 if (*cldap_reply
->client_site
) {
343 ads
->config
.client_site_name
=
344 talloc_strdup(ads
, cldap_reply
->client_site
);
345 if (ads
->config
.client_site_name
== NULL
) {
346 DBG_WARNING("Out of memory\n");
352 ads
->ldap
.port
= gc
? LDAP_GC_PORT
: LDAP_PORT
;
355 /* Store our site name. */
356 sitename_store(cldap_reply
->domain_name
, cldap_reply
->client_site
);
357 sitename_store(cldap_reply
->dns_domain
, cldap_reply
->client_site
);
359 /* Leave this until last so that the flags are not clobbered */
360 ads
->config
.flags
= cldap_reply
->server_type
;
371 try a connection to a given ldap server, returning True and setting the servers IP
372 in the ads struct if successful
374 static bool ads_try_connect(ADS_STRUCT
*ads
, bool gc
,
375 struct sockaddr_storage
*ss
)
377 struct NETLOGON_SAM_LOGON_RESPONSE_EX cldap_reply
= {};
378 TALLOC_CTX
*frame
= talloc_stackframe();
380 char addr
[INET6_ADDRSTRLEN
] = { 0, };
387 print_sockaddr(addr
, sizeof(addr
), ss
);
389 DBG_INFO("ads_try_connect: sending CLDAP request to %s (realm: %s)\n",
390 addr
, ads
->server
.realm
);
392 ok
= ads_cldap_netlogon_5(frame
, ss
, ads
->server
.realm
, &cldap_reply
);
394 DBG_NOTICE("ads_cldap_netlogon_5(%s, %s) failed.\n",
395 addr
, ads
->server
.realm
);
400 ok
= ads_fill_cldap_reply(ads
, gc
, ss
, &cldap_reply
);
402 DBG_NOTICE("ads_fill_cldap_reply(%s, %s) failed.\n",
403 addr
, ads
->server
.realm
);
412 /**********************************************************************
413 send a cldap ping to list of servers, one at a time, until one of
414 them answers it's an ldap server. Record success in the ADS_STRUCT.
415 Take note of and update negative connection cache.
416 **********************************************************************/
418 static NTSTATUS
cldap_ping_list(ADS_STRUCT
*ads
,
420 struct samba_sockaddr
*sa_list
,
423 TALLOC_CTX
*frame
= talloc_stackframe();
424 struct timeval endtime
= timeval_current_ofs(MAX(3,lp_ldap_timeout()/2), 0);
425 uint32_t nt_version
= NETLOGON_NT_VERSION_5
| NETLOGON_NT_VERSION_5EX
;
426 struct tsocket_address
**ts_list
= NULL
;
427 struct samba_sockaddr
**req_sa_list
= NULL
;
428 struct netlogon_samlogon_response
**responses
= NULL
;
429 size_t num_requests
= 0;
435 ts_list
= talloc_zero_array(frame
,
436 struct tsocket_address
*,
438 if (ts_list
== NULL
) {
440 return NT_STATUS_NO_MEMORY
;
443 req_sa_list
= talloc_zero_array(frame
,
444 struct samba_sockaddr
*,
446 if (req_sa_list
== NULL
) {
448 return NT_STATUS_NO_MEMORY
;
453 * The retry loop is bound by the timeout
458 for (i
= 0; i
< count
; i
++) {
459 char server
[INET6_ADDRSTRLEN
];
462 if (is_zero_addr(&sa_list
[i
].u
.ss
)) {
466 print_sockaddr(server
, sizeof(server
), &sa_list
[i
].u
.ss
);
468 status
= check_negative_conn_cache(domain
, server
);
469 if (!NT_STATUS_IS_OK(status
)) {
473 ret
= tsocket_address_inet_from_strings(ts_list
, "ip",
475 &ts_list
[num_requests
]);
477 status
= map_nt_error_from_unix(errno
);
478 DBG_WARNING("Failed to create tsocket_address for %s - %s\n",
479 server
, nt_errstr(status
));
484 req_sa_list
[num_requests
] = &sa_list
[i
];
488 DBG_DEBUG("Try to create %zu netlogon connections for domain '%s' "
489 "(provided count of addresses was %zu).\n",
494 if (num_requests
== 0) {
495 status
= NT_STATUS_NO_LOGON_SERVERS
;
496 DBG_WARNING("domain[%s] num_requests[%zu] for count[%zu] - %s\n",
497 domain
, num_requests
, count
, nt_errstr(status
));
502 status
= netlogon_pings(frame
, /* mem_ctx */
503 lp_client_netlogon_ping_protocol(), /* proto */
504 ts_list
, /* servers */
505 num_requests
, /* num_servers */
506 (struct netlogon_ping_filter
){
507 .ntversion
= nt_version
,
508 .domain
= ads
->server
.realm
,
510 .required_flags
= ads
->config
.flags
|
514 endtime
, /* timeout */
516 if (!NT_STATUS_IS_OK(status
)) {
517 DBG_WARNING("netlogon_pings(realm=%s, num_requests=%zu) "
518 "for count[%zu] - %s\n",
523 return NT_STATUS_NO_LOGON_SERVERS
;
526 for (i
= 0; i
< num_requests
; i
++) {
527 struct NETLOGON_SAM_LOGON_RESPONSE_EX
*cldap_reply
= NULL
;
528 char server
[INET6_ADDRSTRLEN
];
530 if (responses
[i
] == NULL
) {
534 print_sockaddr(server
, sizeof(server
), &req_sa_list
[i
]->u
.ss
);
536 if (responses
[i
]->ntver
!= NETLOGON_NT_VERSION_5EX
) {
537 DBG_NOTICE("realm=[%s] nt_version mismatch: 0x%08x for %s\n",
539 responses
[i
]->ntver
, server
);
543 cldap_reply
= &responses
[i
]->data
.nt5_ex
;
545 /* Returns ok only if it matches the correct server type */
546 ok
= ads_fill_cldap_reply(ads
,
548 &req_sa_list
[i
]->u
.ss
,
551 DBG_DEBUG("realm[%s]: selected %s => %s\n",
553 server
, cldap_reply
->pdc_dns_name
);
554 if (CHECK_DEBUGLVL(DBGLVL_DEBUG
)) {
555 NDR_PRINT_DEBUG(NETLOGON_SAM_LOGON_RESPONSE_EX
,
562 DBG_NOTICE("realm[%s] server %s %s - not usable\n",
564 server
, cldap_reply
->pdc_dns_name
);
565 if (CHECK_DEBUGLVL(DBGLVL_NOTICE
)) {
566 NDR_PRINT_DEBUG(NETLOGON_SAM_LOGON_RESPONSE_EX
,
569 add_failed_connection_entry(domain
, server
,
570 NT_STATUS_CLIENT_SERVER_PARAMETERS_INVALID
);
577 expired
= timeval_expired(&endtime
);
583 /* keep track of failures as all were not suitable */
584 for (i
= 0; i
< num_requests
; i
++) {
585 char server
[INET6_ADDRSTRLEN
];
587 print_sockaddr(server
, sizeof(server
), &req_sa_list
[i
]->u
.ss
);
589 add_failed_connection_entry(domain
, server
,
590 NT_STATUS_UNSUCCESSFUL
);
593 status
= NT_STATUS_NO_LOGON_SERVERS
;
594 DBG_WARNING("realm[%s] no valid response "
595 "num_requests[%zu] for count[%zu] - %s\n",
597 num_requests
, count
, nt_errstr(status
));
599 return NT_STATUS_NO_LOGON_SERVERS
;
602 /***************************************************************************
603 resolve a name and perform an "ldap ping" using NetBIOS and related methods
604 ****************************************************************************/
606 static NTSTATUS
resolve_and_ping_netbios(ADS_STRUCT
*ads
,
607 const char *domain
, const char *realm
)
611 struct samba_sockaddr
*sa_list
= NULL
;
614 DEBUG(6, ("resolve_and_ping_netbios: (cldap) looking for domain '%s'\n",
617 status
= get_sorted_dc_list(talloc_tos(),
623 if (!NT_STATUS_IS_OK(status
)) {
627 /* remove servers which are known to be dead based on
628 the corresponding DNS method */
630 for (i
= 0; i
< count
; ++i
) {
631 char server
[INET6_ADDRSTRLEN
];
633 print_sockaddr(server
, sizeof(server
), &sa_list
[i
].u
.ss
);
636 check_negative_conn_cache(realm
, server
))) {
637 /* Ensure we add the workgroup name for this
638 IP address as negative too. */
639 add_failed_connection_entry(
641 NT_STATUS_UNSUCCESSFUL
);
646 status
= cldap_ping_list(ads
, domain
, sa_list
, count
);
648 TALLOC_FREE(sa_list
);
654 /**********************************************************************
655 resolve a name and perform an "ldap ping" using DNS
656 **********************************************************************/
658 static NTSTATUS
resolve_and_ping_dns(ADS_STRUCT
*ads
, const char *sitename
,
662 struct samba_sockaddr
*sa_list
= NULL
;
665 DEBUG(6, ("resolve_and_ping_dns: (cldap) looking for realm '%s'\n",
668 status
= get_sorted_dc_list(talloc_tos(),
674 if (!NT_STATUS_IS_OK(status
)) {
675 TALLOC_FREE(sa_list
);
679 status
= cldap_ping_list(ads
, realm
, sa_list
, count
);
681 TALLOC_FREE(sa_list
);
686 /**********************************************************************
687 Try to find an AD dc using our internal name resolution routines
688 Try the realm first and then the workgroup name if netbios is not
690 **********************************************************************/
692 static NTSTATUS
ads_find_dc(ADS_STRUCT
*ads
)
694 const char *c_domain
= "";
696 bool use_own_domain
= False
;
697 char *sitename
= NULL
;
698 NTSTATUS status
= NT_STATUS_UNSUCCESSFUL
;
701 /* if the realm and workgroup are both empty, assume they are ours */
704 c_realm
= ads
->server
.realm
;
710 /* special case where no realm and no workgroup means our own */
711 if ( !ads
->server
.workgroup
|| !*ads
->server
.workgroup
) {
712 use_own_domain
= True
;
713 c_realm
= lp_realm();
717 if (!lp_disable_netbios()) {
718 if (use_own_domain
) {
719 c_domain
= lp_workgroup();
721 c_domain
= ads
->server
.workgroup
;
722 if (!*c_realm
&& (!c_domain
|| !*c_domain
)) {
723 c_domain
= lp_workgroup();
732 if (!*c_realm
&& !*c_domain
) {
733 DEBUG(0, ("ads_find_dc: no realm or workgroup! Don't know "
735 return NT_STATUS_INVALID_PARAMETER
; /* rather need MISSING_PARAMETER ... */
739 * In case of LDAP we use get_dc_name() as that
740 * creates the custom krb5.conf file
742 if (ads
->auth
.flags
& ADS_AUTH_GENERATE_KRB5_CONFIG
) {
744 struct sockaddr_storage ip_out
;
746 DEBUG(6, ("ads_find_dc: (ldap) looking for realm '%s'"
747 " and falling back to domain '%s'\n",
750 ok
= get_dc_name(c_domain
, c_realm
, srv_name
, &ip_out
);
752 if (is_zero_addr(&ip_out
)) {
753 return NT_STATUS_NO_LOGON_SERVERS
;
757 * we call ads_try_connect() to fill in the
758 * ads->config details
760 ok
= ads_try_connect(ads
, false, &ip_out
);
766 return NT_STATUS_NO_LOGON_SERVERS
;
770 sitename
= sitename_fetch(talloc_tos(), c_realm
);
771 status
= resolve_and_ping_dns(ads
, sitename
, c_realm
);
773 if (NT_STATUS_IS_OK(status
)) {
774 TALLOC_FREE(sitename
);
778 /* In case we failed to contact one of our closest DC on our
780 * need to try to find another DC, retry with a site-less SRV
785 DEBUG(3, ("ads_find_dc: failed to find a valid DC on "
786 "our site (%s), Trying to find another DC "
787 "for realm '%s' (domain '%s')\n",
788 sitename
, c_realm
, c_domain
));
789 namecache_delete(c_realm
, 0x1C);
791 resolve_and_ping_dns(ads
, NULL
, c_realm
);
793 if (NT_STATUS_IS_OK(status
)) {
794 TALLOC_FREE(sitename
);
799 TALLOC_FREE(sitename
);
802 /* try netbios as fallback - if permitted,
803 or if configuration specifically requests it */
806 DEBUG(3, ("ads_find_dc: falling back to netbios "
807 "name resolution for domain '%s' (realm '%s')\n",
811 status
= resolve_and_ping_netbios(ads
, c_domain
, c_realm
);
812 if (NT_STATUS_IS_OK(status
)) {
817 DEBUG(1, ("ads_find_dc: "
818 "name resolution for realm '%s' (domain '%s') failed: %s\n",
819 c_realm
, c_domain
, nt_errstr(status
)));
824 * Connect to the LDAP server
825 * @param ads Pointer to an existing ADS_STRUCT
826 * @return status of connection
828 static ADS_STATUS
ads_connect_internal(ADS_STRUCT
*ads
,
829 struct cli_credentials
*creds
)
831 int version
= LDAP_VERSION3
;
834 char addr
[INET6_ADDRSTRLEN
];
835 struct sockaddr_storage existing_ss
;
837 bool start_tls
= false;
839 zero_sockaddr(&existing_ss
);
841 if (!(ads
->auth
.flags
& ADS_AUTH_NO_BIND
)) {
842 SMB_ASSERT(creds
!= NULL
);
845 if (ads
->auth
.flags
& ADS_AUTH_ANON_BIND
) {
847 * Simple anonyous binds are only
848 * allowed for anonymous credentials
850 SMB_ASSERT(cli_credentials_is_anonymous(creds
));
853 if (!(ads
->auth
.flags
& (ADS_AUTH_NO_BIND
|ADS_AUTH_ANON_BIND
))) {
854 ads
->auth
.flags
|= ADS_AUTH_GENERATE_KRB5_CONFIG
;
858 * ads_connect can be passed in a reused ADS_STRUCT
859 * with an existing non-zero ads->ldap.ss IP address
860 * that was stored by going through ads_find_dc()
861 * if ads->server.ldap_server was NULL.
863 * If ads->server.ldap_server is still NULL but
864 * the target address isn't the zero address, then
865 * store that address off off before zeroing out
866 * ads->ldap so we don't keep doing multiple calls
867 * to ads_find_dc() in the reuse case.
869 * If a caller wants a clean ADS_STRUCT they
870 * will TALLOC_FREE it and allocate a new one
871 * by calling ads_init(), which ensures
872 * ads->ldap.ss is a properly zero'ed out valid IP
875 if (ads
->server
.ldap_server
== NULL
&& !is_zero_addr(&ads
->ldap
.ss
)) {
876 /* Save off the address we previously found by ads_find_dc(). */
877 existing_ss
= ads
->ldap
.ss
;
881 ZERO_STRUCT(ads
->ldap_tls_data
);
882 ZERO_STRUCT(ads
->ldap_wrap_data
);
883 ads
->ldap
.last_attempt
= time_mono(NULL
);
884 ads
->ldap_wrap_data
.wrap_type
= ADS_SASLWRAP_TYPE_PLAIN
;
886 /* try with a user specified server */
888 if (DEBUGLEVEL
>= 11) {
889 char *s
= NDR_PRINT_STRUCT_STRING(talloc_tos(), ads_struct
, ads
);
890 DEBUG(11,("ads_connect: entering\n"));
891 DEBUGADD(11,("%s\n", s
));
895 if (ads
->server
.ldap_server
) {
897 struct sockaddr_storage ss
;
899 DBG_DEBUG("Resolving name of LDAP server '%s'.\n",
900 ads
->server
.ldap_server
);
901 ok
= resolve_name(ads
->server
.ldap_server
, &ss
, 0x20, true);
903 DEBUG(5,("ads_connect: unable to resolve name %s\n",
904 ads
->server
.ldap_server
));
905 status
= ADS_ERROR_NT(NT_STATUS_NOT_FOUND
);
909 if (is_zero_addr(&ss
)) {
910 status
= ADS_ERROR_NT(NT_STATUS_NOT_FOUND
);
914 ok
= ads_try_connect(ads
, ads
->server
.gc
, &ss
);
919 /* The choice of which GC use is handled one level up in
920 ads_connect_gc(). If we continue on from here with
921 ads_find_dc() we will get GC searches on port 389 which
922 doesn't work. --jerry */
924 if (ads
->server
.gc
== true) {
925 return ADS_ERROR(LDAP_OPERATIONS_ERROR
);
928 if (ads
->server
.no_fallback
) {
929 status
= ADS_ERROR_NT(NT_STATUS_NOT_FOUND
);
934 if (!is_zero_addr(&existing_ss
)) {
935 /* We saved off who we should talk to. */
936 bool ok
= ads_try_connect(ads
,
943 * Keep trying to find a server and fall through
944 * into ads_find_dc() again.
946 DBG_DEBUG("Failed to connect to DC via LDAP server IP address, "
947 "trying to find another DC.\n");
950 ntstatus
= ads_find_dc(ads
);
951 if (NT_STATUS_IS_OK(ntstatus
)) {
955 status
= ADS_ERROR_NT(ntstatus
);
960 print_sockaddr(addr
, sizeof(addr
), &ads
->ldap
.ss
);
961 DEBUG(3,("Successfully contacted LDAP server %s\n", addr
));
963 if (!ads
->auth
.kdc_server
) {
964 print_sockaddr(addr
, sizeof(addr
), &ads
->ldap
.ss
);
965 ads
->auth
.kdc_server
= talloc_strdup(ads
, addr
);
966 if (ads
->auth
.kdc_server
== NULL
) {
967 status
= ADS_ERROR_NT(NT_STATUS_NO_MEMORY
);
972 /* If the caller() requested no LDAP bind, then we are done */
974 if (ads
->auth
.flags
& ADS_AUTH_NO_BIND
) {
975 status
= ADS_SUCCESS
;
979 ads
->ldap_tls_data
.mem_ctx
= talloc_init("ads LDAP TLS connection memory");
980 if (!ads
->ldap_tls_data
.mem_ctx
) {
981 status
= ADS_ERROR_NT(NT_STATUS_NO_MEMORY
);
985 ads
->ldap_wrap_data
.mem_ctx
= talloc_init("ads LDAP connection memory");
986 if (!ads
->ldap_wrap_data
.mem_ctx
) {
987 status
= ADS_ERROR_NT(NT_STATUS_NO_MEMORY
);
991 /* Otherwise setup the TCP LDAP session */
993 if (ads
->auth
.flags
& ADS_AUTH_SASL_LDAPS
) {
995 ads
->ldap
.port
= 636;
996 } else if (ads
->auth
.flags
& ADS_AUTH_SASL_STARTTLS
) {
999 ads
->ldap
.port
= 389;
1001 ads
->ldap
.port
= 389;
1004 ads
->ldap
.ld
= ldap_open_with_timeout(ads
->config
.ldap_server_name
,
1006 ads
->ldap
.port
, lp_ldap_timeout());
1007 if (ads
->ldap
.ld
== NULL
) {
1008 status
= ADS_ERROR(LDAP_OPERATIONS_ERROR
);
1011 DEBUG(3,("Connected to LDAP server %s\n", ads
->config
.ldap_server_name
));
1013 ldap_set_option(ads
->ldap
.ld
, LDAP_OPT_PROTOCOL_VERSION
, &version
);
1016 unsigned int to
= lp_ldap_connection_timeout();
1017 struct berval
*rspdata
= NULL
;
1018 char *rspoid
= NULL
;
1024 CatchSignal(SIGALRM
, gotalarm_sig
);
1026 /* End setup timeout. */
1029 rc
= ldap_extended_operation_s(ads
->ldap
.ld
,
1030 LDAP_EXOP_START_TLS
,
1036 if (gotalarm
!= 0 && rc
== LDAP_SUCCESS
) {
1041 /* Teardown timeout. */
1043 CatchSignal(SIGALRM
, SIG_IGN
);
1046 if (rspoid
!= NULL
) {
1047 ldap_memfree(rspoid
);
1050 if (rspdata
!= NULL
) {
1051 ber_bvfree(rspdata
);
1054 if (rc
!= LDAP_SUCCESS
) {
1055 status
= ADS_ERROR_LDAP(rc
);
1061 unsigned int to
= lp_ldap_connection_timeout();
1066 CatchSignal(SIGALRM
, gotalarm_sig
);
1068 /* End setup timeout. */
1071 status
= ads_setup_tls_wrapping(&ads
->ldap_tls_data
,
1073 ads
->config
.ldap_server_name
);
1076 /* Teardown timeout. */
1078 CatchSignal(SIGALRM
, SIG_IGN
);
1081 if ( !ADS_ERR_OK(status
) ) {
1086 /* cache the successful connection for workgroup and realm */
1087 if (ads_closest_dc(ads
)) {
1088 saf_store( ads
->server
.workgroup
, ads
->config
.ldap_server_name
);
1089 saf_store( ads
->server
.realm
, ads
->config
.ldap_server_name
);
1092 /* fill in the current time and offsets */
1094 status
= ads_current_time( ads
);
1095 if ( !ADS_ERR_OK(status
) ) {
1099 /* Now do the bind */
1101 if (ads
->auth
.flags
& ADS_AUTH_ANON_BIND
) {
1102 status
= ADS_ERROR(ldap_simple_bind_s(ads
->ldap
.ld
, NULL
, NULL
));
1106 status
= ads_sasl_bind(ads
, creds
);
1109 if (DEBUGLEVEL
>= 11) {
1110 char *s
= NDR_PRINT_STRUCT_STRING(talloc_tos(), ads_struct
, ads
);
1111 DEBUG(11,("ads_connect: leaving with: %s\n",
1112 ads_errstr(status
)));
1113 DEBUGADD(11,("%s\n", s
));
1121 * Connect to the LDAP server using without a bind
1122 * and without a tcp connection at all
1124 * @param ads Pointer to an existing ADS_STRUCT
1125 * @return status of connection
1127 ADS_STATUS
ads_connect_cldap_only(ADS_STRUCT
*ads
)
1129 ads
->auth
.flags
|= ADS_AUTH_NO_BIND
;
1130 return ads_connect_internal(ads
, NULL
);
1134 * Connect to the LDAP server
1135 * @param ads Pointer to an existing ADS_STRUCT
1136 * @return status of connection
1138 ADS_STATUS
ads_connect_creds(ADS_STRUCT
*ads
, struct cli_credentials
*creds
)
1140 SMB_ASSERT(creds
!= NULL
);
1143 * We allow upgrades from
1144 * ADS_AUTH_NO_BIND if credentials
1147 ads
->auth
.flags
&= ~ADS_AUTH_NO_BIND
;
1150 * We allow upgrades from ADS_AUTH_ANON_BIND,
1151 * as we don't want to use simple binds with
1152 * non-anon credentials
1154 if (!cli_credentials_is_anonymous(creds
)) {
1155 ads
->auth
.flags
&= ~ADS_AUTH_ANON_BIND
;
1158 return ads_connect_internal(ads
, creds
);
1162 * Connect to the LDAP server using anonymous credentials
1163 * using a simple bind without username/password
1165 * @param ads Pointer to an existing ADS_STRUCT
1166 * @return status of connection
1168 ADS_STATUS
ads_connect_simple_anon(ADS_STRUCT
*ads
)
1170 TALLOC_CTX
*frame
= talloc_stackframe();
1171 struct cli_credentials
*creds
= NULL
;
1174 creds
= cli_credentials_init_anon(frame
);
1175 if (creds
== NULL
) {
1177 return ADS_ERROR_SYSTEM(errno
);
1180 ads
->auth
.flags
|= ADS_AUTH_ANON_BIND
;
1181 status
= ads_connect_creds(ads
, creds
);
1187 * Connect to the LDAP server using the machine account
1188 * @param ads Pointer to an existing ADS_STRUCT
1189 * @return status of connection
1191 ADS_STATUS
ads_connect_machine(ADS_STRUCT
*ads
)
1193 TALLOC_CTX
*frame
= talloc_stackframe();
1194 struct cli_credentials
*creds
= NULL
;
1198 ntstatus
= pdb_get_trust_credentials(ads
->server
.workgroup
,
1202 if (!NT_STATUS_IS_OK(ntstatus
)) {
1204 return ADS_ERROR_NT(ntstatus
);
1207 status
= ads_connect_creds(ads
, creds
);
1213 * Zero out the internal ads->ldap struct and initialize the address to zero IP.
1214 * @param ads Pointer to an existing ADS_STRUCT
1216 * Sets the ads->ldap.ss to a valid
1217 * zero ip address that can be detected by
1218 * our is_zero_addr() function. Otherwise
1219 * it is left as AF_UNSPEC (0).
1221 void ads_zero_ldap(ADS_STRUCT
*ads
)
1223 ZERO_STRUCT(ads
->ldap
);
1225 * Initialize the sockaddr_storage so we can use
1226 * sockaddr test functions against it.
1228 zero_sockaddr(&ads
->ldap
.ss
);
1232 * Disconnect the LDAP server
1233 * @param ads Pointer to an existing ADS_STRUCT
1235 void ads_disconnect(ADS_STRUCT
*ads
)
1238 ldap_unbind(ads
->ldap
.ld
);
1239 ads
->ldap
.ld
= NULL
;
1241 if (ads
->ldap_tls_data
.mem_ctx
) {
1242 talloc_free(ads
->ldap_tls_data
.mem_ctx
);
1244 if (ads
->ldap_wrap_data
.wrap_ops
&&
1245 ads
->ldap_wrap_data
.wrap_ops
->disconnect
) {
1246 ads
->ldap_wrap_data
.wrap_ops
->disconnect(&ads
->ldap_wrap_data
);
1248 if (ads
->ldap_wrap_data
.mem_ctx
) {
1249 talloc_free(ads
->ldap_wrap_data
.mem_ctx
);
1252 ZERO_STRUCT(ads
->ldap_tls_data
);
1253 ZERO_STRUCT(ads
->ldap_wrap_data
);
1257 Duplicate a struct berval into talloc'ed memory
1259 static struct berval
*dup_berval(TALLOC_CTX
*ctx
, const struct berval
*in_val
)
1261 struct berval
*value
;
1263 if (!in_val
) return NULL
;
1265 value
= talloc_zero(ctx
, struct berval
);
1268 if (in_val
->bv_len
== 0) return value
;
1270 value
->bv_len
= in_val
->bv_len
;
1271 value
->bv_val
= (char *)talloc_memdup(ctx
, in_val
->bv_val
,
1277 Make a values list out of an array of (struct berval *)
1279 static struct berval
**ads_dup_values(TALLOC_CTX
*ctx
,
1280 const struct berval
**in_vals
)
1282 struct berval
**values
;
1285 if (!in_vals
) return NULL
;
1286 for (i
=0; in_vals
[i
]; i
++)
1287 ; /* count values */
1288 values
= talloc_zero_array(ctx
, struct berval
*, i
+1);
1289 if (!values
) return NULL
;
1291 for (i
=0; in_vals
[i
]; i
++) {
1292 values
[i
] = dup_berval(ctx
, in_vals
[i
]);
1298 UTF8-encode a values list out of an array of (char *)
1300 static char **ads_push_strvals(TALLOC_CTX
*ctx
, const char **in_vals
)
1306 if (!in_vals
) return NULL
;
1307 for (i
=0; in_vals
[i
]; i
++)
1308 ; /* count values */
1309 values
= talloc_zero_array(ctx
, char *, i
+1);
1310 if (!values
) return NULL
;
1312 for (i
=0; in_vals
[i
]; i
++) {
1313 if (!push_utf8_talloc(ctx
, &values
[i
], in_vals
[i
], &size
)) {
1314 TALLOC_FREE(values
);
1322 Pull a (char *) array out of a UTF8-encoded values list
1324 static char **ads_pull_strvals(TALLOC_CTX
*ctx
, const char **in_vals
)
1328 size_t converted_size
;
1330 if (!in_vals
) return NULL
;
1331 for (i
=0; in_vals
[i
]; i
++)
1332 ; /* count values */
1333 values
= talloc_zero_array(ctx
, char *, i
+1);
1334 if (!values
) return NULL
;
1336 for (i
=0; in_vals
[i
]; i
++) {
1337 if (!pull_utf8_talloc(ctx
, &values
[i
], in_vals
[i
],
1339 DEBUG(0,("ads_pull_strvals: pull_utf8_talloc failed: "
1340 "%s\n", strerror(errno
)));
1347 * Do a search with paged results. cookie must be null on the first
1348 * call, and then returned on each subsequent call. It will be null
1349 * again when the entire search is complete
1350 * @param ads connection to ads server
1351 * @param bind_path Base dn for the search
1352 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1353 * @param expr Search expression - specified in local charset
1354 * @param attrs Attributes to retrieve - specified in utf8 or ascii
1355 * @param res ** which will contain results - free res* with ads_msgfree()
1356 * @param count Number of entries retrieved on this page
1357 * @param cookie The paged results cookie to be returned on subsequent calls
1358 * @return status of search
1360 static ADS_STATUS
ads_do_paged_search_args(ADS_STRUCT
*ads
,
1361 const char *bind_path
,
1362 int scope
, const char *expr
,
1363 const char **attrs
, void *args
,
1365 int *count
, struct berval
**cookie
)
1368 char *utf8_expr
, *utf8_path
, **search_attrs
= NULL
;
1369 size_t converted_size
;
1370 LDAPControl PagedResults
, NoReferrals
, ExternalCtrl
, *controls
[4], **rcontrols
;
1371 BerElement
*cookie_be
= NULL
;
1372 struct berval
*cookie_bv
= NULL
;
1373 BerElement
*ext_be
= NULL
;
1374 struct berval
*ext_bv
= NULL
;
1377 ads_control
*external_control
= (ads_control
*) args
;
1381 if (!(ctx
= talloc_init("ads_do_paged_search_args")))
1382 return ADS_ERROR(LDAP_NO_MEMORY
);
1384 /* 0 means the conversion worked but the result was empty
1385 so we only fail if it's -1. In any case, it always
1386 at least nulls out the dest */
1387 if (!push_utf8_talloc(ctx
, &utf8_expr
, expr
, &converted_size
) ||
1388 !push_utf8_talloc(ctx
, &utf8_path
, bind_path
, &converted_size
))
1390 rc
= LDAP_NO_MEMORY
;
1394 if (!attrs
|| !(*attrs
))
1395 search_attrs
= NULL
;
1397 /* This would be the utf8-encoded version...*/
1398 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
1399 if (!(search_attrs
= str_list_copy(talloc_tos(), attrs
))) {
1400 rc
= LDAP_NO_MEMORY
;
1405 /* Paged results only available on ldap v3 or later */
1406 ldap_get_option(ads
->ldap
.ld
, LDAP_OPT_PROTOCOL_VERSION
, &version
);
1407 if (version
< LDAP_VERSION3
) {
1408 rc
= LDAP_NOT_SUPPORTED
;
1412 cookie_be
= ber_alloc_t(LBER_USE_DER
);
1414 ber_printf(cookie_be
, "{iO}", (ber_int_t
) ads
->config
.ldap_page_size
, *cookie
);
1415 ber_bvfree(*cookie
); /* don't need it from last time */
1418 ber_printf(cookie_be
, "{io}", (ber_int_t
) ads
->config
.ldap_page_size
, "", 0);
1420 ber_flatten(cookie_be
, &cookie_bv
);
1421 PagedResults
.ldctl_oid
= discard_const_p(char, ADS_PAGE_CTL_OID
);
1422 PagedResults
.ldctl_iscritical
= (char) 1;
1423 PagedResults
.ldctl_value
.bv_len
= cookie_bv
->bv_len
;
1424 PagedResults
.ldctl_value
.bv_val
= cookie_bv
->bv_val
;
1426 NoReferrals
.ldctl_oid
= discard_const_p(char, ADS_NO_REFERRALS_OID
);
1427 NoReferrals
.ldctl_iscritical
= (char) 0;
1428 NoReferrals
.ldctl_value
.bv_len
= 0;
1429 NoReferrals
.ldctl_value
.bv_val
= discard_const_p(char, "");
1431 if (external_control
&&
1432 (strequal(external_control
->control
, ADS_EXTENDED_DN_OID
) ||
1433 strequal(external_control
->control
, ADS_SD_FLAGS_OID
))) {
1435 ExternalCtrl
.ldctl_oid
= discard_const_p(char, external_control
->control
);
1436 ExternalCtrl
.ldctl_iscritical
= (char) external_control
->critical
;
1438 /* win2k does not accept a ldctl_value being passed in */
1440 if (external_control
->val
!= 0) {
1442 if ((ext_be
= ber_alloc_t(LBER_USE_DER
)) == NULL
) {
1443 rc
= LDAP_NO_MEMORY
;
1447 if ((ber_printf(ext_be
, "{i}", (ber_int_t
) external_control
->val
)) == -1) {
1448 rc
= LDAP_NO_MEMORY
;
1451 if ((ber_flatten(ext_be
, &ext_bv
)) == -1) {
1452 rc
= LDAP_NO_MEMORY
;
1456 ExternalCtrl
.ldctl_value
.bv_len
= ext_bv
->bv_len
;
1457 ExternalCtrl
.ldctl_value
.bv_val
= ext_bv
->bv_val
;
1460 ExternalCtrl
.ldctl_value
.bv_len
= 0;
1461 ExternalCtrl
.ldctl_value
.bv_val
= NULL
;
1464 controls
[0] = &NoReferrals
;
1465 controls
[1] = &PagedResults
;
1466 controls
[2] = &ExternalCtrl
;
1470 controls
[0] = &NoReferrals
;
1471 controls
[1] = &PagedResults
;
1475 /* we need to disable referrals as the openldap libs don't
1476 handle them and paged results at the same time. Using them
1477 together results in the result record containing the server
1478 page control being removed from the result list (tridge/jmcd)
1480 leaving this in despite the control that says don't generate
1481 referrals, in case the server doesn't support it (jmcd)
1483 ldap_set_option(ads
->ldap
.ld
, LDAP_OPT_REFERRALS
, LDAP_OPT_OFF
);
1485 rc
= ldap_search_with_timeout(ads
->ldap
.ld
, utf8_path
, scope
, utf8_expr
,
1486 search_attrs
, 0, controls
,
1487 NULL
, LDAP_NO_LIMIT
,
1488 (LDAPMessage
**)res
);
1490 ber_free(cookie_be
, 1);
1491 ber_bvfree(cookie_bv
);
1494 DEBUG(3,("ads_do_paged_search_args: ldap_search_with_timeout(%s) -> %s\n", expr
,
1495 ldap_err2string(rc
)));
1496 if (rc
== LDAP_OTHER
) {
1500 ret
= ldap_parse_result(ads
->ldap
.ld
,
1508 if (ret
== LDAP_SUCCESS
) {
1509 DEBUG(3, ("ldap_search_with_timeout(%s) "
1510 "error: %s\n", expr
, ldap_errmsg
));
1511 ldap_memfree(ldap_errmsg
);
1517 rc
= ldap_parse_result(ads
->ldap
.ld
, *res
, NULL
, NULL
, NULL
,
1518 NULL
, &rcontrols
, 0);
1524 for (i
=0; rcontrols
[i
]; i
++) {
1525 if (strcmp(ADS_PAGE_CTL_OID
, rcontrols
[i
]->ldctl_oid
) == 0) {
1526 cookie_be
= ber_init(&rcontrols
[i
]->ldctl_value
);
1527 ber_scanf(cookie_be
,"{iO}", (ber_int_t
*) count
,
1529 /* the berval is the cookie, but must be freed when
1531 if (cookie_bv
->bv_len
) /* still more to do */
1532 *cookie
=ber_bvdup(cookie_bv
);
1535 ber_bvfree(cookie_bv
);
1536 ber_free(cookie_be
, 1);
1540 ldap_controls_free(rcontrols
);
1543 talloc_destroy(ctx
);
1546 ber_free(ext_be
, 1);
1553 if (rc
!= LDAP_SUCCESS
&& *res
!= NULL
) {
1554 ads_msgfree(ads
, *res
);
1558 /* if/when we decide to utf8-encode attrs, take out this next line */
1559 TALLOC_FREE(search_attrs
);
1561 return ADS_ERROR(rc
);
1564 static ADS_STATUS
ads_do_paged_search(ADS_STRUCT
*ads
, const char *bind_path
,
1565 int scope
, const char *expr
,
1566 const char **attrs
, LDAPMessage
**res
,
1567 int *count
, struct berval
**cookie
)
1569 return ads_do_paged_search_args(ads
, bind_path
, scope
, expr
, attrs
, NULL
, res
, count
, cookie
);
1574 * Get all results for a search. This uses ads_do_paged_search() to return
1575 * all entries in a large search.
1576 * @param ads connection to ads server
1577 * @param bind_path Base dn for the search
1578 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1579 * @param expr Search expression
1580 * @param attrs Attributes to retrieve
1581 * @param res ** which will contain results - free res* with ads_msgfree()
1582 * @return status of search
1584 ADS_STATUS
ads_do_search_all_args(ADS_STRUCT
*ads
, const char *bind_path
,
1585 int scope
, const char *expr
,
1586 const char **attrs
, void *args
,
1589 struct berval
*cookie
= NULL
;
1594 status
= ads_do_paged_search_args(ads
, bind_path
, scope
, expr
, attrs
, args
, res
,
1597 if (!ADS_ERR_OK(status
))
1600 #ifdef HAVE_LDAP_ADD_RESULT_ENTRY
1602 LDAPMessage
*res2
= NULL
;
1603 LDAPMessage
*msg
, *next
;
1605 status
= ads_do_paged_search_args(ads
, bind_path
, scope
, expr
,
1606 attrs
, args
, &res2
, &count
, &cookie
);
1607 if (!ADS_ERR_OK(status
)) {
1611 /* this relies on the way that ldap_add_result_entry() works internally. I hope
1612 that this works on all ldap libs, but I have only tested with openldap */
1613 for (msg
= ads_first_message(ads
, res2
); msg
; msg
= next
) {
1614 next
= ads_next_message(ads
, msg
);
1615 ldap_add_result_entry((LDAPMessage
**)res
, msg
);
1617 /* note that we do not free res2, as the memory is now
1618 part of the main returned list */
1621 DEBUG(0, ("no ldap_add_result_entry() support in LDAP libs!\n"));
1622 status
= ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL
);
1628 ADS_STATUS
ads_do_search_all(ADS_STRUCT
*ads
, const char *bind_path
,
1629 int scope
, const char *expr
,
1630 const char **attrs
, LDAPMessage
**res
)
1632 return ads_do_search_all_args(ads
, bind_path
, scope
, expr
, attrs
, NULL
, res
);
1635 ADS_STATUS
ads_do_search_all_sd_flags(ADS_STRUCT
*ads
, const char *bind_path
,
1636 int scope
, const char *expr
,
1637 const char **attrs
, uint32_t sd_flags
,
1642 args
.control
= ADS_SD_FLAGS_OID
;
1643 args
.val
= sd_flags
;
1644 args
.critical
= True
;
1646 return ads_do_search_all_args(ads
, bind_path
, scope
, expr
, attrs
, &args
, res
);
1651 * Run a function on all results for a search. Uses ads_do_paged_search() and
1652 * runs the function as each page is returned, using ads_process_results()
1653 * @param ads connection to ads server
1654 * @param bind_path Base dn for the search
1655 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1656 * @param expr Search expression - specified in local charset
1657 * @param attrs Attributes to retrieve - specified in UTF-8 or ascii
1658 * @param fn Function which takes attr name, values list, and data_area
1659 * @param data_area Pointer which is passed to function on each call
1660 * @return status of search
1662 ADS_STATUS
ads_do_search_all_fn(ADS_STRUCT
*ads
, const char *bind_path
,
1663 int scope
, const char *expr
, const char **attrs
,
1664 bool (*fn
)(ADS_STRUCT
*, char *, void **, void *),
1667 struct berval
*cookie
= NULL
;
1672 status
= ads_do_paged_search(ads
, bind_path
, scope
, expr
, attrs
, &res
,
1675 if (!ADS_ERR_OK(status
)) return status
;
1677 ads_process_results(ads
, res
, fn
, data_area
);
1678 ads_msgfree(ads
, res
);
1681 status
= ads_do_paged_search(ads
, bind_path
, scope
, expr
, attrs
,
1682 &res
, &count
, &cookie
);
1684 if (!ADS_ERR_OK(status
)) break;
1686 ads_process_results(ads
, res
, fn
, data_area
);
1687 ads_msgfree(ads
, res
);
1694 * Do a search with a timeout.
1695 * @param ads connection to ads server
1696 * @param bind_path Base dn for the search
1697 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1698 * @param expr Search expression
1699 * @param attrs Attributes to retrieve
1700 * @param res ** which will contain results - free res* with ads_msgfree()
1701 * @return status of search
1703 ADS_STATUS
ads_do_search(ADS_STRUCT
*ads
, const char *bind_path
, int scope
,
1705 const char **attrs
, LDAPMessage
**res
)
1708 char *utf8_expr
, *utf8_path
, **search_attrs
= NULL
;
1709 size_t converted_size
;
1713 if (!(ctx
= talloc_init("ads_do_search"))) {
1714 DEBUG(1,("ads_do_search: talloc_init() failed!\n"));
1715 return ADS_ERROR(LDAP_NO_MEMORY
);
1718 /* 0 means the conversion worked but the result was empty
1719 so we only fail if it's negative. In any case, it always
1720 at least nulls out the dest */
1721 if (!push_utf8_talloc(ctx
, &utf8_expr
, expr
, &converted_size
) ||
1722 !push_utf8_talloc(ctx
, &utf8_path
, bind_path
, &converted_size
))
1724 DEBUG(1,("ads_do_search: push_utf8_talloc() failed!\n"));
1725 rc
= LDAP_NO_MEMORY
;
1729 if (!attrs
|| !(*attrs
))
1730 search_attrs
= NULL
;
1732 /* This would be the utf8-encoded version...*/
1733 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
1734 if (!(search_attrs
= str_list_copy(talloc_tos(), attrs
)))
1736 DEBUG(1,("ads_do_search: str_list_copy() failed!\n"));
1737 rc
= LDAP_NO_MEMORY
;
1742 /* see the note in ads_do_paged_search - we *must* disable referrals */
1743 ldap_set_option(ads
->ldap
.ld
, LDAP_OPT_REFERRALS
, LDAP_OPT_OFF
);
1745 rc
= ldap_search_with_timeout(ads
->ldap
.ld
, utf8_path
, scope
, utf8_expr
,
1746 search_attrs
, 0, NULL
, NULL
,
1748 (LDAPMessage
**)res
);
1750 if (rc
== LDAP_SIZELIMIT_EXCEEDED
) {
1751 DEBUG(3,("Warning! sizelimit exceeded in ldap. Truncating.\n"));
1756 talloc_destroy(ctx
);
1757 /* if/when we decide to utf8-encode attrs, take out this next line */
1758 TALLOC_FREE(search_attrs
);
1759 return ADS_ERROR(rc
);
1762 * Do a general ADS search
1763 * @param ads connection to ads server
1764 * @param res ** which will contain results - free res* with ads_msgfree()
1765 * @param expr Search expression
1766 * @param attrs Attributes to retrieve
1767 * @return status of search
1769 ADS_STATUS
ads_search(ADS_STRUCT
*ads
, LDAPMessage
**res
,
1770 const char *expr
, const char **attrs
)
1772 return ads_do_search(ads
, ads
->config
.bind_path
, LDAP_SCOPE_SUBTREE
,
1777 * Do a search on a specific DistinguishedName
1778 * @param ads connection to ads server
1779 * @param res ** which will contain results - free res* with ads_msgfree()
1780 * @param dn DistinguishedName to search
1781 * @param attrs Attributes to retrieve
1782 * @return status of search
1784 ADS_STATUS
ads_search_dn(ADS_STRUCT
*ads
, LDAPMessage
**res
,
1785 const char *dn
, const char **attrs
)
1787 return ads_do_search(ads
, dn
, LDAP_SCOPE_BASE
, "(objectclass=*)",
1792 * Free up memory from a ads_search
1793 * @param ads connection to ads server
1794 * @param msg Search results to free
1796 void ads_msgfree(ADS_STRUCT
*ads
, LDAPMessage
*msg
)
1803 * Get a dn from search results
1804 * @param ads connection to ads server
1805 * @param msg Search result
1808 char *ads_get_dn(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
, LDAPMessage
*msg
)
1810 char *utf8_dn
, *unix_dn
;
1811 size_t converted_size
;
1813 utf8_dn
= ldap_get_dn(ads
->ldap
.ld
, msg
);
1816 DEBUG (5, ("ads_get_dn: ldap_get_dn failed\n"));
1820 if (!pull_utf8_talloc(mem_ctx
, &unix_dn
, utf8_dn
, &converted_size
)) {
1821 DEBUG(0,("ads_get_dn: string conversion failure utf8 [%s]\n",
1825 ldap_memfree(utf8_dn
);
1830 * Get the parent from a dn
1831 * @param dn the dn to return the parent from
1832 * @return parent dn string
1834 char *ads_parent_dn(const char *dn
)
1842 p
= strchr(dn
, ',');
1852 * Find a machine account given a hostname
1853 * @param ads connection to ads server
1854 * @param res ** which will contain results - free res* with ads_msgfree()
1855 * @param host Hostname to search for
1856 * @return status of search
1858 ADS_STATUS
ads_find_machine_acct(ADS_STRUCT
*ads
, LDAPMessage
**res
,
1859 const char *machine
)
1863 const char *attrs
[] = {
1864 /* This is how Windows checks for machine accounts */
1867 "userAccountControl",
1869 "ServicePrincipalName",
1870 "userPrincipalName",
1872 /* Additional attributes Samba checks */
1873 "msDS-KeyVersionNumber",
1874 "msDS-AdditionalDnsHostName",
1875 "msDS-SupportedEncryptionTypes",
1876 "nTSecurityDescriptor",
1881 TALLOC_CTX
*frame
= talloc_stackframe();
1885 /* the easiest way to find a machine account anywhere in the tree
1886 is to look for hostname$ */
1887 expr
= talloc_asprintf(frame
, "(samAccountName=%s$)", machine
);
1889 status
= ADS_ERROR_NT(NT_STATUS_NO_MEMORY
);
1893 status
= ads_search(ads
, res
, expr
, attrs
);
1894 if (ADS_ERR_OK(status
)) {
1895 if (ads_count_replies(ads
, *res
) != 1) {
1896 status
= ADS_ERROR_LDAP(LDAP_NO_SUCH_OBJECT
);
1906 * Initialize a list of mods to be used in a modify request
1907 * @param ctx An initialized TALLOC_CTX
1908 * @return allocated ADS_MODLIST
1910 ADS_MODLIST
ads_init_mods(TALLOC_CTX
*ctx
)
1912 #define ADS_MODLIST_ALLOC_SIZE 10
1915 if ((mods
= talloc_zero_array(ctx
, LDAPMod
*, ADS_MODLIST_ALLOC_SIZE
+ 1)))
1916 /* -1 is safety to make sure we don't go over the end.
1917 need to reset it to NULL before doing ldap modify */
1918 mods
[ADS_MODLIST_ALLOC_SIZE
] = (LDAPMod
*) -1;
1920 return (ADS_MODLIST
)mods
;
1925 add an attribute to the list, with values list already constructed
1927 static ADS_STATUS
ads_modlist_add(TALLOC_CTX
*ctx
, ADS_MODLIST
*mods
,
1928 int mod_op
, const char *name
,
1929 const void *_invals
)
1932 LDAPMod
**modlist
= (LDAPMod
**) *mods
;
1933 struct berval
**ber_values
= NULL
;
1934 char **char_values
= NULL
;
1937 mod_op
= LDAP_MOD_DELETE
;
1939 if (mod_op
& LDAP_MOD_BVALUES
) {
1940 const struct berval
**b
;
1941 b
= discard_const_p(const struct berval
*, _invals
);
1942 ber_values
= ads_dup_values(ctx
, b
);
1945 c
= discard_const_p(const char *, _invals
);
1946 char_values
= ads_push_strvals(ctx
, c
);
1950 /* find the first empty slot */
1951 for (curmod
=0; modlist
[curmod
] && modlist
[curmod
] != (LDAPMod
*) -1;
1953 if (modlist
[curmod
] == (LDAPMod
*) -1) {
1954 if (!(modlist
= talloc_realloc(ctx
, modlist
, LDAPMod
*,
1955 curmod
+ADS_MODLIST_ALLOC_SIZE
+1)))
1956 return ADS_ERROR(LDAP_NO_MEMORY
);
1957 memset(&modlist
[curmod
], 0,
1958 ADS_MODLIST_ALLOC_SIZE
*sizeof(LDAPMod
*));
1959 modlist
[curmod
+ADS_MODLIST_ALLOC_SIZE
] = (LDAPMod
*) -1;
1960 *mods
= (ADS_MODLIST
)modlist
;
1963 if (!(modlist
[curmod
] = talloc_zero(ctx
, LDAPMod
)))
1964 return ADS_ERROR(LDAP_NO_MEMORY
);
1965 modlist
[curmod
]->mod_type
= talloc_strdup(ctx
, name
);
1966 if (mod_op
& LDAP_MOD_BVALUES
) {
1967 modlist
[curmod
]->mod_bvalues
= ber_values
;
1968 } else if (mod_op
& LDAP_MOD_DELETE
) {
1969 modlist
[curmod
]->mod_values
= NULL
;
1971 modlist
[curmod
]->mod_values
= char_values
;
1974 modlist
[curmod
]->mod_op
= mod_op
;
1975 return ADS_ERROR(LDAP_SUCCESS
);
1979 * Add a single string value to a mod list
1980 * @param ctx An initialized TALLOC_CTX
1981 * @param mods An initialized ADS_MODLIST
1982 * @param name The attribute name to add
1983 * @param val The value to add - NULL means DELETE
1984 * @return ADS STATUS indicating success of add
1986 ADS_STATUS
ads_mod_str(TALLOC_CTX
*ctx
, ADS_MODLIST
*mods
,
1987 const char *name
, const char *val
)
1989 const char *values
[2];
1995 return ads_modlist_add(ctx
, mods
, LDAP_MOD_DELETE
, name
, NULL
);
1996 return ads_modlist_add(ctx
, mods
, LDAP_MOD_REPLACE
, name
, values
);
2000 * Add an array of string values to a mod list
2001 * @param ctx An initialized TALLOC_CTX
2002 * @param mods An initialized ADS_MODLIST
2003 * @param name The attribute name to add
2004 * @param vals The array of string values to add - NULL means DELETE
2005 * @return ADS STATUS indicating success of add
2007 ADS_STATUS
ads_mod_strlist(TALLOC_CTX
*ctx
, ADS_MODLIST
*mods
,
2008 const char *name
, const char **vals
)
2011 return ads_modlist_add(ctx
, mods
, LDAP_MOD_DELETE
, name
, NULL
);
2012 return ads_modlist_add(ctx
, mods
, LDAP_MOD_REPLACE
,
2013 name
, (const void **) vals
);
2017 * Add a single ber-encoded value to a mod list
2018 * @param ctx An initialized TALLOC_CTX
2019 * @param mods An initialized ADS_MODLIST
2020 * @param name The attribute name to add
2021 * @param val The value to add - NULL means DELETE
2022 * @return ADS STATUS indicating success of add
2024 static ADS_STATUS
ads_mod_ber(TALLOC_CTX
*ctx
, ADS_MODLIST
*mods
,
2025 const char *name
, const struct berval
*val
)
2027 const struct berval
*values
[2];
2032 return ads_modlist_add(ctx
, mods
, LDAP_MOD_DELETE
, name
, NULL
);
2033 return ads_modlist_add(ctx
, mods
, LDAP_MOD_REPLACE
|LDAP_MOD_BVALUES
,
2034 name
, (const void *) values
);
2037 static void ads_print_error(int ret
, LDAP
*ld
)
2040 char *ld_error
= NULL
;
2041 ldap_get_option(ld
, LDAP_OPT_ERROR_STRING
, &ld_error
);
2042 DBG_ERR("AD LDAP ERROR: %d (%s): %s\n",
2044 ldap_err2string(ret
),
2046 SAFE_FREE(ld_error
);
2051 * Perform an ldap modify
2052 * @param ads connection to ads server
2053 * @param mod_dn DistinguishedName to modify
2054 * @param mods list of modifications to perform
2055 * @return status of modify
2057 ADS_STATUS
ads_gen_mod(ADS_STRUCT
*ads
, const char *mod_dn
, ADS_MODLIST mods
)
2060 char *utf8_dn
= NULL
;
2061 size_t converted_size
;
2063 this control is needed to modify that contains a currently
2064 non-existent attribute (but allowable for the object) to run
2066 LDAPControl PermitModify
= {
2067 discard_const_p(char, ADS_PERMIT_MODIFY_OID
),
2070 LDAPControl
*controls
[2];
2072 DBG_INFO("AD LDAP: Modifying %s\n", mod_dn
);
2074 controls
[0] = &PermitModify
;
2077 if (!push_utf8_talloc(talloc_tos(), &utf8_dn
, mod_dn
, &converted_size
)) {
2078 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY
);
2081 /* find the end of the list, marked by NULL or -1 */
2082 for(i
=0;(mods
[i
]!=0)&&(mods
[i
]!=(LDAPMod
*) -1);i
++);
2083 /* make sure the end of the list is NULL */
2085 ret
= ldap_modify_ext_s(ads
->ldap
.ld
, utf8_dn
,
2086 (LDAPMod
**) mods
, controls
, NULL
);
2087 ads_print_error(ret
, ads
->ldap
.ld
);
2088 TALLOC_FREE(utf8_dn
);
2089 return ADS_ERROR(ret
);
2093 * Perform an ldap add
2094 * @param ads connection to ads server
2095 * @param new_dn DistinguishedName to add
2096 * @param mods list of attributes and values for DN
2097 * @return status of add
2099 ADS_STATUS
ads_gen_add(ADS_STRUCT
*ads
, const char *new_dn
, ADS_MODLIST mods
)
2102 char *utf8_dn
= NULL
;
2103 size_t converted_size
;
2105 DBG_INFO("AD LDAP: Adding %s\n", new_dn
);
2107 if (!push_utf8_talloc(talloc_tos(), &utf8_dn
, new_dn
, &converted_size
)) {
2108 DEBUG(1, ("ads_gen_add: push_utf8_talloc failed!\n"));
2109 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY
);
2112 /* find the end of the list, marked by NULL or -1 */
2113 for(i
=0;(mods
[i
]!=0)&&(mods
[i
]!=(LDAPMod
*) -1);i
++);
2114 /* make sure the end of the list is NULL */
2117 ret
= ldap_add_ext_s(ads
->ldap
.ld
, utf8_dn
, (LDAPMod
**)mods
, NULL
, NULL
);
2118 ads_print_error(ret
, ads
->ldap
.ld
);
2119 TALLOC_FREE(utf8_dn
);
2120 return ADS_ERROR(ret
);
2124 * Delete a DistinguishedName
2125 * @param ads connection to ads server
2126 * @param new_dn DistinguishedName to delete
2127 * @return status of delete
2129 ADS_STATUS
ads_del_dn(ADS_STRUCT
*ads
, char *del_dn
)
2132 char *utf8_dn
= NULL
;
2133 size_t converted_size
;
2134 if (!push_utf8_talloc(talloc_tos(), &utf8_dn
, del_dn
, &converted_size
)) {
2135 DEBUG(1, ("ads_del_dn: push_utf8_talloc failed!\n"));
2136 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY
);
2139 DBG_INFO("AD LDAP: Deleting %s\n", del_dn
);
2141 ret
= ldap_delete_s(ads
->ldap
.ld
, utf8_dn
);
2142 ads_print_error(ret
, ads
->ldap
.ld
);
2143 TALLOC_FREE(utf8_dn
);
2144 return ADS_ERROR(ret
);
2148 * Build an org unit string
2149 * if org unit is Computers or blank then assume a container, otherwise
2150 * assume a / separated list of organisational units.
2151 * jmcd: '\' is now used for escapes so certain chars can be in the ou (e.g. #)
2152 * @param ads connection to ads server
2153 * @param org_unit Organizational unit
2154 * @return org unit string - caller must free
2156 char *ads_ou_string(ADS_STRUCT
*ads
, const char *org_unit
)
2162 if (!org_unit
|| !*org_unit
) {
2164 ret
= ads_default_ou_string(ads
, DS_GUID_COMPUTERS_CONTAINER
);
2166 /* samba4 might not yet respond to a wellknownobject-query */
2167 return ret
? ret
: SMB_STRDUP("cn=Computers");
2170 if (strequal(org_unit
, "Computers")) {
2171 return SMB_STRDUP("cn=Computers");
2174 /* jmcd: removed "\\" from the separation chars, because it is
2175 needed as an escape for chars like '#' which are valid in an
2177 status
= ads_build_path(org_unit
, "/", "ou=", 1, &dn
);
2178 if (!ADS_ERR_OK(status
)) {
2186 * Get a org unit string for a well-known GUID
2187 * @param ads connection to ads server
2188 * @param wknguid Well known GUID
2189 * @return org unit string - caller must free
2191 char *ads_default_ou_string(ADS_STRUCT
*ads
, const char *wknguid
)
2194 LDAPMessage
*res
= NULL
;
2195 char *base
, *wkn_dn
= NULL
, *ret
= NULL
, **wkn_dn_exp
= NULL
,
2196 **bind_dn_exp
= NULL
;
2197 const char *attrs
[] = {"distinguishedName", NULL
};
2198 int new_ln
, wkn_ln
, bind_ln
, i
;
2200 if (wknguid
== NULL
) {
2204 if (asprintf(&base
, "<WKGUID=%s,%s>", wknguid
, ads
->config
.bind_path
) == -1) {
2205 DEBUG(1, ("asprintf failed!\n"));
2209 status
= ads_search_dn(ads
, &res
, base
, attrs
);
2210 if (!ADS_ERR_OK(status
)) {
2211 DEBUG(1,("Failed while searching for: %s\n", base
));
2215 if (ads_count_replies(ads
, res
) != 1) {
2219 /* substitute the bind-path from the well-known-guid-search result */
2220 wkn_dn
= ads_get_dn(ads
, talloc_tos(), res
);
2225 wkn_dn_exp
= ldap_explode_dn(wkn_dn
, 0);
2230 bind_dn_exp
= ldap_explode_dn(ads
->config
.bind_path
, 0);
2235 for (wkn_ln
=0; wkn_dn_exp
[wkn_ln
]; wkn_ln
++)
2237 for (bind_ln
=0; bind_dn_exp
[bind_ln
]; bind_ln
++)
2240 new_ln
= wkn_ln
- bind_ln
;
2242 ret
= SMB_STRDUP(wkn_dn_exp
[0]);
2247 for (i
=1; i
< new_ln
; i
++) {
2250 if (asprintf(&s
, "%s,%s", ret
, wkn_dn_exp
[i
]) == -1) {
2256 ret
= SMB_STRDUP(s
);
2265 ads_msgfree(ads
, res
);
2266 TALLOC_FREE(wkn_dn
);
2268 ldap_value_free(wkn_dn_exp
);
2271 ldap_value_free(bind_dn_exp
);
2278 * Adds (appends) an item to an attribute array, rather then
2279 * replacing the whole list
2280 * @param ctx An initialized TALLOC_CTX
2281 * @param mods An initialized ADS_MODLIST
2282 * @param name name of the ldap attribute to append to
2283 * @param vals an array of values to add
2284 * @return status of addition
2287 ADS_STATUS
ads_add_strlist(TALLOC_CTX
*ctx
, ADS_MODLIST
*mods
,
2288 const char *name
, const char **vals
)
2290 return ads_modlist_add(ctx
, mods
, LDAP_MOD_ADD
, name
,
2291 (const void *) vals
);
2295 * This clears out all registered spn's for a given hostname
2296 * @param ads An initialized ADS_STRUCT
2297 * @param machine_name the NetBIOS name of the computer.
2298 * @return 0 upon success, non-zero otherwise.
2301 ADS_STATUS
ads_clear_service_principal_names(ADS_STRUCT
*ads
, const char *machine_name
)
2304 LDAPMessage
*res
= NULL
;
2306 const char *servicePrincipalName
[1] = {NULL
};
2308 char *dn_string
= NULL
;
2310 ret
= ads_find_machine_acct(ads
, &res
, machine_name
);
2311 if (!ADS_ERR_OK(ret
)) {
2312 DEBUG(5,("ads_clear_service_principal_names: WARNING: Host Account for %s not found... skipping operation.\n", machine_name
));
2313 DEBUG(5,("ads_clear_service_principal_names: WARNING: Service Principals for %s have NOT been cleared.\n", machine_name
));
2314 ads_msgfree(ads
, res
);
2318 DEBUG(5,("ads_clear_service_principal_names: Host account for %s found\n", machine_name
));
2319 ctx
= talloc_init("ads_clear_service_principal_names");
2321 ads_msgfree(ads
, res
);
2322 return ADS_ERROR(LDAP_NO_MEMORY
);
2325 if (!(mods
= ads_init_mods(ctx
))) {
2326 talloc_destroy(ctx
);
2327 ads_msgfree(ads
, res
);
2328 return ADS_ERROR(LDAP_NO_MEMORY
);
2330 ret
= ads_mod_strlist(ctx
, &mods
, "servicePrincipalName", servicePrincipalName
);
2331 if (!ADS_ERR_OK(ret
)) {
2332 DEBUG(1,("ads_clear_service_principal_names: Error creating strlist.\n"));
2333 ads_msgfree(ads
, res
);
2334 talloc_destroy(ctx
);
2337 dn_string
= ads_get_dn(ads
, talloc_tos(), res
);
2339 talloc_destroy(ctx
);
2340 ads_msgfree(ads
, res
);
2341 return ADS_ERROR(LDAP_NO_MEMORY
);
2343 ret
= ads_gen_mod(ads
, dn_string
, mods
);
2344 TALLOC_FREE(dn_string
);
2345 if (!ADS_ERR_OK(ret
)) {
2346 DEBUG(1,("ads_clear_service_principal_names: Error: Updating Service Principals for machine %s in LDAP\n",
2348 ads_msgfree(ads
, res
);
2349 talloc_destroy(ctx
);
2353 ads_msgfree(ads
, res
);
2354 talloc_destroy(ctx
);
2359 * @brief Search for an element in a string array.
2361 * @param[in] el_array The string array to search.
2363 * @param[in] num_el The number of elements in the string array.
2365 * @param[in] el The string to search.
2367 * @return True if found, false if not.
2369 bool ads_element_in_array(const char **el_array
, size_t num_el
, const char *el
)
2373 if (el_array
== NULL
|| num_el
== 0 || el
== NULL
) {
2377 for (i
= 0; i
< num_el
&& el_array
[i
] != NULL
; i
++) {
2380 cmp
= strcasecmp_m(el_array
[i
], el
);
2390 * @brief This gets the service principal names of an existing computer account.
2392 * @param[in] mem_ctx The memory context to use to allocate the spn array.
2394 * @param[in] ads The ADS context to use.
2396 * @param[in] machine_name The NetBIOS name of the computer, which is used to
2397 * identify the computer account.
2399 * @param[in] spn_array A pointer to store the array for SPNs.
2401 * @param[in] num_spns The number of principals stored in the array.
2403 * @return 0 on success, or a ADS error if a failure occurred.
2405 ADS_STATUS
ads_get_service_principal_names(TALLOC_CTX
*mem_ctx
,
2407 const char *machine_name
,
2412 LDAPMessage
*res
= NULL
;
2415 status
= ads_find_machine_acct(ads
,
2418 if (!ADS_ERR_OK(status
)) {
2419 DEBUG(1,("Host Account for %s not found... skipping operation.\n",
2424 count
= ads_count_replies(ads
, res
);
2426 status
= ADS_ERROR(LDAP_NO_SUCH_OBJECT
);
2430 *spn_array
= ads_pull_strings(ads
,
2433 "servicePrincipalName",
2435 if (*spn_array
== NULL
) {
2436 DEBUG(1, ("Host account for %s does not have service principal "
2439 status
= ADS_ERROR(LDAP_NO_SUCH_OBJECT
);
2444 ads_msgfree(ads
, res
);
2450 * This adds a service principal name to an existing computer account
2451 * (found by hostname) in AD.
2452 * @param ads An initialized ADS_STRUCT
2453 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
2454 * @param spns An array or strings for the service principals to add,
2455 * i.e. 'cifs/machine_name', 'http/machine.full.domain.com' etc.
2456 * @return 0 upon success, or non-zero if a failure occurs
2459 ADS_STATUS
ads_add_service_principal_names(ADS_STRUCT
*ads
,
2460 const char *machine_name
,
2465 LDAPMessage
*res
= NULL
;
2467 char *dn_string
= NULL
;
2468 const char **servicePrincipalName
= spns
;
2470 ret
= ads_find_machine_acct(ads
, &res
, machine_name
);
2471 if (!ADS_ERR_OK(ret
)) {
2472 DEBUG(1,("ads_add_service_principal_name: WARNING: Host Account for %s not found... skipping operation.\n",
2474 DEBUG(1,("ads_add_service_principal_name: WARNING: Service Principals have NOT been added.\n"));
2475 ads_msgfree(ads
, res
);
2479 DEBUG(1,("ads_add_service_principal_name: Host account for %s found\n", machine_name
));
2480 if (!(ctx
= talloc_init("ads_add_service_principal_name"))) {
2481 ads_msgfree(ads
, res
);
2482 return ADS_ERROR(LDAP_NO_MEMORY
);
2485 DEBUG(5,("ads_add_service_principal_name: INFO: "
2486 "Adding %s to host %s\n",
2487 spns
[0] ? "N/A" : spns
[0], machine_name
));
2490 DEBUG(5,("ads_add_service_principal_name: INFO: "
2491 "Adding %s to host %s\n",
2492 spns
[1] ? "N/A" : spns
[1], machine_name
));
2494 if ( (mods
= ads_init_mods(ctx
)) == NULL
) {
2495 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
2499 ret
= ads_add_strlist(ctx
,
2501 "servicePrincipalName",
2502 servicePrincipalName
);
2503 if (!ADS_ERR_OK(ret
)) {
2504 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
2508 if ( (dn_string
= ads_get_dn(ads
, ctx
, res
)) == NULL
) {
2509 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
2513 ret
= ads_gen_mod(ads
, dn_string
, mods
);
2514 if (!ADS_ERR_OK(ret
)) {
2515 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
2521 ads_msgfree(ads
, res
);
2525 static uint32_t ads_get_acct_ctrl(ADS_STRUCT
*ads
,
2528 uint32_t acct_ctrl
= 0;
2531 ok
= ads_pull_uint32(ads
, msg
, "userAccountControl", &acct_ctrl
);
2539 static ADS_STATUS
ads_change_machine_acct(ADS_STRUCT
*ads
,
2541 const struct berval
*machine_pw_val
)
2545 TALLOC_CTX
*frame
= talloc_stackframe();
2546 uint32_t acct_control
;
2547 char *control_str
= NULL
;
2548 const char *attrs
[] = {
2552 LDAPMessage
*res
= NULL
;
2555 dn
= ads_get_dn(ads
, frame
, msg
);
2557 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
2561 acct_control
= ads_get_acct_ctrl(ads
, msg
);
2562 if (acct_control
== 0) {
2563 ret
= ADS_ERROR(LDAP_NO_RESULTS_RETURNED
);
2568 * Changing the password, disables the account. So we need to change the
2569 * userAccountControl flags to enable it again.
2571 mods
= ads_init_mods(frame
);
2573 ret
= ADS_ERROR_LDAP(LDAP_NO_MEMORY
);
2577 ads_mod_ber(frame
, &mods
, "unicodePwd", machine_pw_val
);
2579 ret
= ads_gen_mod(ads
, dn
, mods
);
2580 if (!ADS_ERR_OK(ret
)) {
2586 * To activate the account, we need to disable and enable it.
2588 acct_control
|= UF_ACCOUNTDISABLE
;
2590 control_str
= talloc_asprintf(frame
, "%u", acct_control
);
2591 if (control_str
== NULL
) {
2592 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
2596 mods
= ads_init_mods(frame
);
2598 ret
= ADS_ERROR_LDAP(LDAP_NO_MEMORY
);
2602 ads_mod_str(frame
, &mods
, "userAccountControl", control_str
);
2604 ret
= ads_gen_mod(ads
, dn
, mods
);
2605 if (!ADS_ERR_OK(ret
)) {
2609 TALLOC_FREE(control_str
);
2612 * Enable the account again.
2614 acct_control
&= ~UF_ACCOUNTDISABLE
;
2616 control_str
= talloc_asprintf(frame
, "%u", acct_control
);
2617 if (control_str
== NULL
) {
2618 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
2622 mods
= ads_init_mods(frame
);
2624 ret
= ADS_ERROR_LDAP(LDAP_NO_MEMORY
);
2628 ads_mod_str(frame
, &mods
, "userAccountControl", control_str
);
2630 ret
= ads_gen_mod(ads
, dn
, mods
);
2631 if (!ADS_ERR_OK(ret
)) {
2635 TALLOC_FREE(control_str
);
2637 ret
= ads_search_dn(ads
, &res
, dn
, attrs
);
2638 ads_msgfree(ads
, res
);
2647 * adds a machine account to the ADS server
2648 * @param ads An initialized ADS_STRUCT
2649 * @param machine_name - the NetBIOS machine name of this account.
2650 * @param account_type A number indicating the type of account to create
2651 * @param org_unit The LDAP path in which to place this account
2652 * @return 0 upon success, or non-zero otherwise
2655 ADS_STATUS
ads_create_machine_acct(ADS_STRUCT
*ads
,
2656 const char *machine_name
,
2657 const char *machine_password
,
2658 const char *org_unit
,
2659 uint32_t etype_list
,
2660 const char *dns_domain_name
)
2663 char *samAccountName
= NULL
;
2664 char *controlstr
= NULL
;
2665 TALLOC_CTX
*ctx
= NULL
;
2667 char *machine_escaped
= NULL
;
2668 char *dns_hostname
= NULL
;
2669 char *new_dn
= NULL
;
2670 char *utf8_pw
= NULL
;
2671 size_t utf8_pw_len
= 0;
2672 char *utf16_pw
= NULL
;
2673 size_t utf16_pw_len
= 0;
2674 struct berval machine_pw_val
;
2676 const char **spn_array
= NULL
;
2677 size_t num_spns
= 0;
2678 const char *spn_prefix
[] = {
2680 "RestrictedKrbHost",
2683 LDAPMessage
*res
= NULL
;
2684 uint32_t acct_control
= UF_WORKSTATION_TRUST_ACCOUNT
;
2686 ctx
= talloc_init("ads_add_machine_acct");
2688 return ADS_ERROR(LDAP_NO_MEMORY
);
2691 machine_escaped
= escape_rdn_val_string_alloc(machine_name
);
2692 if (machine_escaped
== NULL
) {
2693 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
2697 utf8_pw
= talloc_asprintf(ctx
, "\"%s\"", machine_password
);
2698 if (utf8_pw
== NULL
) {
2699 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
2702 utf8_pw_len
= strlen(utf8_pw
);
2704 ok
= convert_string_talloc(ctx
,
2705 CH_UTF8
, CH_UTF16MUNGED
,
2706 utf8_pw
, utf8_pw_len
,
2707 (void *)&utf16_pw
, &utf16_pw_len
);
2709 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
2713 machine_pw_val
= (struct berval
) {
2715 .bv_len
= utf16_pw_len
,
2718 /* Check if the machine account already exists. */
2719 ret
= ads_find_machine_acct(ads
, &res
, machine_escaped
);
2720 if (ADS_ERR_OK(ret
)) {
2721 /* Change the machine account password */
2722 ret
= ads_change_machine_acct(ads
, res
, &machine_pw_val
);
2723 ads_msgfree(ads
, res
);
2727 ads_msgfree(ads
, res
);
2729 new_dn
= talloc_asprintf(ctx
, "cn=%s,%s", machine_escaped
, org_unit
);
2730 if (new_dn
== NULL
) {
2731 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
2735 /* Create machine account */
2737 samAccountName
= talloc_asprintf(ctx
, "%s$", machine_name
);
2738 if (samAccountName
== NULL
) {
2739 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
2743 dns_hostname
= talloc_asprintf(ctx
,
2747 if (dns_hostname
== NULL
) {
2748 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
2752 /* Add dns_hostname SPNs */
2753 for (i
= 0; i
< ARRAY_SIZE(spn_prefix
); i
++) {
2754 char *spn
= talloc_asprintf(ctx
,
2759 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
2763 ok
= add_string_to_array(ctx
,
2768 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
2773 /* Add machine_name SPNs */
2774 for (i
= 0; i
< ARRAY_SIZE(spn_prefix
); i
++) {
2775 char *spn
= talloc_asprintf(ctx
,
2780 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
2784 ok
= add_string_to_array(ctx
,
2789 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
2794 /* Make sure to NULL terminate the array */
2795 spn_array
= talloc_realloc(ctx
, spn_array
, const char *, num_spns
+ 1);
2796 if (spn_array
== NULL
) {
2797 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
2800 spn_array
[num_spns
] = NULL
;
2802 controlstr
= talloc_asprintf(ctx
, "%u", acct_control
);
2803 if (controlstr
== NULL
) {
2804 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
2808 mods
= ads_init_mods(ctx
);
2810 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
2814 ads_mod_str(ctx
, &mods
, "objectClass", "Computer");
2815 ads_mod_str(ctx
, &mods
, "SamAccountName", samAccountName
);
2816 ads_mod_str(ctx
, &mods
, "userAccountControl", controlstr
);
2817 ads_mod_str(ctx
, &mods
, "DnsHostName", dns_hostname
);
2818 ads_mod_strlist(ctx
, &mods
, "ServicePrincipalName", spn_array
);
2819 ads_mod_ber(ctx
, &mods
, "unicodePwd", &machine_pw_val
);
2821 ret
= ads_gen_add(ads
, new_dn
, mods
);
2824 SAFE_FREE(machine_escaped
);
2825 talloc_destroy(ctx
);
2831 * move a machine account to another OU on the ADS server
2832 * @param ads - An initialized ADS_STRUCT
2833 * @param machine_name - the NetBIOS machine name of this account.
2834 * @param org_unit - The LDAP path in which to place this account
2835 * @param moved - whether we moved the machine account (optional)
2836 * @return 0 upon success, or non-zero otherwise
2839 ADS_STATUS
ads_move_machine_acct(ADS_STRUCT
*ads
, const char *machine_name
,
2840 const char *org_unit
, bool *moved
)
2844 LDAPMessage
*res
= NULL
;
2845 char *filter
= NULL
;
2846 char *computer_dn
= NULL
;
2848 char *computer_rdn
= NULL
;
2849 bool need_move
= False
;
2851 if (asprintf(&filter
, "(samAccountName=%s$)", machine_name
) == -1) {
2852 rc
= ADS_ERROR(LDAP_NO_MEMORY
);
2856 /* Find pre-existing machine */
2857 rc
= ads_search(ads
, &res
, filter
, NULL
);
2858 if (!ADS_ERR_OK(rc
)) {
2862 computer_dn
= ads_get_dn(ads
, talloc_tos(), res
);
2864 rc
= ADS_ERROR(LDAP_NO_MEMORY
);
2868 parent_dn
= ads_parent_dn(computer_dn
);
2869 if (strequal(parent_dn
, org_unit
)) {
2875 if (asprintf(&computer_rdn
, "CN=%s", machine_name
) == -1) {
2876 rc
= ADS_ERROR(LDAP_NO_MEMORY
);
2880 ldap_status
= ldap_rename_s(ads
->ldap
.ld
, computer_dn
, computer_rdn
,
2881 org_unit
, 1, NULL
, NULL
);
2882 rc
= ADS_ERROR(ldap_status
);
2885 ads_msgfree(ads
, res
);
2887 TALLOC_FREE(computer_dn
);
2888 SAFE_FREE(computer_rdn
);
2890 if (!ADS_ERR_OK(rc
)) {
2902 dump a binary result from ldap
2904 static void dump_binary(ADS_STRUCT
*ads
, const char *field
, struct berval
**values
)
2907 for (i
=0; values
[i
]; i
++) {
2909 printf("%s: ", field
);
2910 for (j
=0; j
<values
[i
]->bv_len
; j
++) {
2911 printf("%02X", (unsigned char)values
[i
]->bv_val
[j
]);
2917 static void dump_guid(ADS_STRUCT
*ads
, const char *field
, struct berval
**values
)
2920 for (i
=0; values
[i
]; i
++) {
2922 DATA_BLOB in
= data_blob_const(values
[i
]->bv_val
, values
[i
]->bv_len
);
2925 status
= GUID_from_ndr_blob(&in
, &guid
);
2926 if (NT_STATUS_IS_OK(status
)) {
2927 printf("%s: %s\n", field
, GUID_string(talloc_tos(), &guid
));
2929 printf("%s: INVALID GUID\n", field
);
2935 dump a sid result from ldap
2937 static void dump_sid(ADS_STRUCT
*ads
, const char *field
, struct berval
**values
)
2940 for (i
=0; values
[i
]; i
++) {
2943 struct dom_sid_buf tmp
;
2944 ret
= sid_parse((const uint8_t *)values
[i
]->bv_val
,
2945 values
[i
]->bv_len
, &sid
);
2949 printf("%s: %s\n", field
, dom_sid_str_buf(&sid
, &tmp
));
2954 dump ntSecurityDescriptor
2956 static void dump_sd(ADS_STRUCT
*ads
, const char *filed
, struct berval
**values
)
2958 TALLOC_CTX
*frame
= talloc_stackframe();
2959 struct security_descriptor
*psd
;
2962 status
= unmarshall_sec_desc(talloc_tos(), (uint8_t *)values
[0]->bv_val
,
2963 values
[0]->bv_len
, &psd
);
2964 if (!NT_STATUS_IS_OK(status
)) {
2965 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
2966 nt_errstr(status
)));
2972 ads_disp_sd(ads
, talloc_tos(), psd
);
2979 dump a string result from ldap
2981 static void dump_string(const char *field
, char **values
)
2984 for (i
=0; values
[i
]; i
++) {
2985 printf("%s: %s\n", field
, values
[i
]);
2990 dump a field from LDAP on stdout
2994 static bool ads_dump_field(ADS_STRUCT
*ads
, char *field
, void **values
, void *data_area
)
2999 void (*handler
)(ADS_STRUCT
*, const char *, struct berval
**);
3001 {"objectGUID", False
, dump_guid
},
3002 {"netbootGUID", False
, dump_guid
},
3003 {"nTSecurityDescriptor", False
, dump_sd
},
3004 {"dnsRecord", False
, dump_binary
},
3005 {"objectSid", False
, dump_sid
},
3006 {"securityIdentifier", False
, dump_sid
},
3007 {"tokenGroups", False
, dump_sid
},
3008 {"tokenGroupsNoGCAcceptable", False
, dump_sid
},
3009 {"tokengroupsGlobalandUniversal", False
, dump_sid
},
3010 {"mS-DS-CreatorSID", False
, dump_sid
},
3011 {"msExchMailboxGuid", False
, dump_guid
},
3012 {"msDS-TrustForestTrustInfo", False
, dump_binary
},
3017 if (!field
) { /* must be end of an entry */
3022 for (i
=0; handlers
[i
].name
; i
++) {
3023 if (strcasecmp_m(handlers
[i
].name
, field
) == 0) {
3024 if (!values
) /* first time, indicate string or not */
3025 return handlers
[i
].string
;
3026 handlers
[i
].handler(ads
, field
, (struct berval
**) values
);
3030 if (!handlers
[i
].name
) {
3031 if (!values
) /* first time, indicate string conversion */
3033 dump_string(field
, (char **)values
);
3039 * Dump a result from LDAP on stdout
3040 * used for debugging
3041 * @param ads connection to ads server
3042 * @param res Results to dump
3045 void ads_dump(ADS_STRUCT
*ads
, LDAPMessage
*res
)
3047 ads_process_results(ads
, res
, ads_dump_field
, NULL
);
3051 * Walk through results, calling a function for each entry found.
3052 * The function receives a field name, a berval * array of values,
3053 * and a data area passed through from the start. The function is
3054 * called once with null for field and values at the end of each
3056 * @param ads connection to ads server
3057 * @param res Results to process
3058 * @param fn Function for processing each result
3059 * @param data_area user-defined area to pass to function
3061 void ads_process_results(ADS_STRUCT
*ads
, LDAPMessage
*res
,
3062 bool (*fn
)(ADS_STRUCT
*, char *, void **, void *),
3067 size_t converted_size
;
3069 if (!(ctx
= talloc_init("ads_process_results")))
3072 for (msg
= ads_first_entry(ads
, res
); msg
;
3073 msg
= ads_next_entry(ads
, msg
)) {
3077 for (utf8_field
=ldap_first_attribute(ads
->ldap
.ld
,
3078 (LDAPMessage
*)msg
,&b
);
3080 utf8_field
=ldap_next_attribute(ads
->ldap
.ld
,
3081 (LDAPMessage
*)msg
,b
)) {
3082 struct berval
**ber_vals
;
3088 if (!pull_utf8_talloc(ctx
, &field
, utf8_field
,
3091 DEBUG(0,("ads_process_results: "
3092 "pull_utf8_talloc failed: %s\n",
3096 string
= fn(ads
, field
, NULL
, data_area
);
3101 utf8_vals
= ldap_get_values(ads
->ldap
.ld
,
3102 (LDAPMessage
*)msg
, field
);
3103 p
= discard_const_p(const char *, utf8_vals
);
3104 str_vals
= ads_pull_strvals(ctx
, p
);
3105 fn(ads
, field
, (void **) str_vals
, data_area
);
3106 ldap_value_free(utf8_vals
);
3108 ber_vals
= ldap_get_values_len(ads
->ldap
.ld
,
3109 (LDAPMessage
*)msg
, field
);
3110 fn(ads
, field
, (void **) ber_vals
, data_area
);
3112 ldap_value_free_len(ber_vals
);
3114 ldap_memfree(utf8_field
);
3117 talloc_free_children(ctx
);
3118 fn(ads
, NULL
, NULL
, data_area
); /* completed an entry */
3121 talloc_destroy(ctx
);
3125 * count how many replies are in a LDAPMessage
3126 * @param ads connection to ads server
3127 * @param res Results to count
3128 * @return number of replies
3130 int ads_count_replies(ADS_STRUCT
*ads
, void *res
)
3132 return ldap_count_entries(ads
->ldap
.ld
, (LDAPMessage
*)res
);
3136 * pull the first entry from a ADS result
3137 * @param ads connection to ads server
3138 * @param res Results of search
3139 * @return first entry from result
3141 LDAPMessage
*ads_first_entry(ADS_STRUCT
*ads
, LDAPMessage
*res
)
3143 return ldap_first_entry(ads
->ldap
.ld
, res
);
3147 * pull the next entry from a ADS result
3148 * @param ads connection to ads server
3149 * @param res Results of search
3150 * @return next entry from result
3152 LDAPMessage
*ads_next_entry(ADS_STRUCT
*ads
, LDAPMessage
*res
)
3154 return ldap_next_entry(ads
->ldap
.ld
, res
);
3158 * pull the first message from a ADS result
3159 * @param ads connection to ads server
3160 * @param res Results of search
3161 * @return first message from result
3163 LDAPMessage
*ads_first_message(ADS_STRUCT
*ads
, LDAPMessage
*res
)
3165 return ldap_first_message(ads
->ldap
.ld
, res
);
3169 * pull the next message from a ADS result
3170 * @param ads connection to ads server
3171 * @param res Results of search
3172 * @return next message from result
3174 LDAPMessage
*ads_next_message(ADS_STRUCT
*ads
, LDAPMessage
*res
)
3176 return ldap_next_message(ads
->ldap
.ld
, res
);
3180 * pull a single string from a ADS result
3181 * @param ads connection to ads server
3182 * @param mem_ctx TALLOC_CTX to use for allocating result string
3183 * @param msg Results of search
3184 * @param field Attribute to retrieve
3185 * @return Result string in talloc context
3187 char *ads_pull_string(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
, LDAPMessage
*msg
,
3193 size_t converted_size
;
3195 values
= ldap_get_values(ads
->ldap
.ld
, msg
, field
);
3199 if (values
[0] && pull_utf8_talloc(mem_ctx
, &ux_string
, values
[0],
3204 ldap_value_free(values
);
3209 * pull an array of strings from a ADS result
3210 * @param ads connection to ads server
3211 * @param mem_ctx TALLOC_CTX to use for allocating result string
3212 * @param msg Results of search
3213 * @param field Attribute to retrieve
3214 * @return Result strings in talloc context
3216 char **ads_pull_strings(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
,
3217 LDAPMessage
*msg
, const char *field
,
3222 size_t i
, converted_size
;
3224 values
= ldap_get_values(ads
->ldap
.ld
, msg
, field
);
3228 *num_values
= ldap_count_values(values
);
3230 ret
= talloc_array(mem_ctx
, char *, *num_values
+ 1);
3232 ldap_value_free(values
);
3236 for (i
=0;i
<*num_values
;i
++) {
3237 if (!pull_utf8_talloc(mem_ctx
, &ret
[i
], values
[i
],
3240 ldap_value_free(values
);
3246 ldap_value_free(values
);
3251 * pull an array of strings from a ADS result
3252 * (handle large multivalue attributes with range retrieval)
3253 * @param ads connection to ads server
3254 * @param mem_ctx TALLOC_CTX to use for allocating result string
3255 * @param msg Results of search
3256 * @param field Attribute to retrieve
3257 * @param current_strings strings returned by a previous call to this function
3258 * @param next_attribute The next query should ask for this attribute
3259 * @param num_values How many values did we get this time?
3260 * @param more_values Are there more values to get?
3261 * @return Result strings in talloc context
3263 char **ads_pull_strings_range(ADS_STRUCT
*ads
,
3264 TALLOC_CTX
*mem_ctx
,
3265 LDAPMessage
*msg
, const char *field
,
3266 char **current_strings
,
3267 const char **next_attribute
,
3268 size_t *num_strings
,
3272 char *expected_range_attrib
, *range_attr
= NULL
;
3273 BerElement
*ptr
= NULL
;
3276 size_t num_new_strings
;
3277 unsigned long int range_start
;
3278 unsigned long int range_end
;
3280 /* we might have been given the whole lot anyway */
3281 if ((strings
= ads_pull_strings(ads
, mem_ctx
, msg
, field
, num_strings
))) {
3282 *more_strings
= False
;
3286 expected_range_attrib
= talloc_asprintf(mem_ctx
, "%s;Range=", field
);
3288 /* look for Range result */
3289 for (attr
= ldap_first_attribute(ads
->ldap
.ld
, (LDAPMessage
*)msg
, &ptr
);
3291 attr
= ldap_next_attribute(ads
->ldap
.ld
, (LDAPMessage
*)msg
, ptr
)) {
3292 /* we ignore the fact that this is utf8, as all attributes are ascii... */
3293 if (strnequal(attr
, expected_range_attrib
, strlen(expected_range_attrib
))) {
3301 /* nothing here - this field is just empty */
3302 *more_strings
= False
;
3306 if (sscanf(&range_attr
[strlen(expected_range_attrib
)], "%lu-%lu",
3307 &range_start
, &range_end
) == 2) {
3308 *more_strings
= True
;
3310 if (sscanf(&range_attr
[strlen(expected_range_attrib
)], "%lu-*",
3311 &range_start
) == 1) {
3312 *more_strings
= False
;
3314 DEBUG(1, ("ads_pull_strings_range: Cannot parse Range attribute (%s)\n",
3316 ldap_memfree(range_attr
);
3317 *more_strings
= False
;
3322 if ((*num_strings
) != range_start
) {
3323 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) doesn't start at %u, but at %lu"
3324 " - aborting range retrieval\n",
3325 range_attr
, (unsigned int)(*num_strings
) + 1, range_start
));
3326 ldap_memfree(range_attr
);
3327 *more_strings
= False
;
3331 new_strings
= ads_pull_strings(ads
, mem_ctx
, msg
, range_attr
, &num_new_strings
);
3333 if (*more_strings
&& ((*num_strings
+ num_new_strings
) != (range_end
+ 1))) {
3334 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) tells us we have %lu "
3335 "strings in this bunch, but we only got %lu - aborting range retrieval\n",
3336 range_attr
, (unsigned long int)range_end
- range_start
+ 1,
3337 (unsigned long int)num_new_strings
));
3338 ldap_memfree(range_attr
);
3339 *more_strings
= False
;
3343 strings
= talloc_realloc(mem_ctx
, current_strings
, char *,
3344 *num_strings
+ num_new_strings
);
3346 if (strings
== NULL
) {
3347 ldap_memfree(range_attr
);
3348 *more_strings
= False
;
3352 if (new_strings
&& num_new_strings
) {
3353 memcpy(&strings
[*num_strings
], new_strings
,
3354 sizeof(*new_strings
) * num_new_strings
);
3357 (*num_strings
) += num_new_strings
;
3359 if (*more_strings
) {
3360 *next_attribute
= talloc_asprintf(mem_ctx
,
3365 if (!*next_attribute
) {
3366 DEBUG(1, ("talloc_asprintf for next attribute failed!\n"));
3367 ldap_memfree(range_attr
);
3368 *more_strings
= False
;
3373 ldap_memfree(range_attr
);
3379 * pull a single uint32_t from a ADS result
3380 * @param ads connection to ads server
3381 * @param msg Results of search
3382 * @param field Attribute to retrieve
3383 * @param v Pointer to int to store result
3384 * @return boolean indicating success
3386 bool ads_pull_uint32(ADS_STRUCT
*ads
, LDAPMessage
*msg
, const char *field
,
3391 values
= ldap_get_values(ads
->ldap
.ld
, msg
, field
);
3395 ldap_value_free(values
);
3399 *v
= atoi(values
[0]);
3400 ldap_value_free(values
);
3405 * pull a single objectGUID from an ADS result
3406 * @param ads connection to ADS server
3407 * @param msg results of search
3408 * @param guid 37-byte area to receive text guid
3409 * @return boolean indicating success
3411 bool ads_pull_guid(ADS_STRUCT
*ads
, LDAPMessage
*msg
, struct GUID
*guid
)
3416 if (!smbldap_talloc_single_blob(talloc_tos(), ads
->ldap
.ld
, msg
, "objectGUID",
3421 status
= GUID_from_ndr_blob(&blob
, guid
);
3422 talloc_free(blob
.data
);
3423 return NT_STATUS_IS_OK(status
);
3428 * pull a single struct dom_sid from a ADS result
3429 * @param ads connection to ads server
3430 * @param msg Results of search
3431 * @param field Attribute to retrieve
3432 * @param sid Pointer to sid to store result
3433 * @return boolean indicating success
3435 bool ads_pull_sid(ADS_STRUCT
*ads
, LDAPMessage
*msg
, const char *field
,
3436 struct dom_sid
*sid
)
3438 return smbldap_pull_sid(ads
->ldap
.ld
, msg
, field
, sid
);
3442 * pull an array of struct dom_sids from a ADS result
3443 * @param ads connection to ads server
3444 * @param mem_ctx TALLOC_CTX for allocating sid array
3445 * @param msg Results of search
3446 * @param field Attribute to retrieve
3447 * @param sids pointer to sid array to allocate
3448 * @return the count of SIDs pulled
3450 int ads_pull_sids(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
,
3451 LDAPMessage
*msg
, const char *field
, struct dom_sid
**sids
)
3453 struct berval
**values
;
3456 values
= ldap_get_values_len(ads
->ldap
.ld
, msg
, field
);
3461 for (i
=0; values
[i
]; i
++)
3465 (*sids
) = talloc_array(mem_ctx
, struct dom_sid
, i
);
3467 ldap_value_free_len(values
);
3475 for (i
=0; values
[i
]; i
++) {
3477 ret
= sid_parse((const uint8_t *)values
[i
]->bv_val
,
3478 values
[i
]->bv_len
, &(*sids
)[count
]);
3480 struct dom_sid_buf buf
;
3481 DBG_DEBUG("pulling SID: %s\n",
3482 dom_sid_str_buf(&(*sids
)[count
], &buf
));
3487 ldap_value_free_len(values
);
3492 * pull a struct security_descriptor from a ADS result
3493 * @param ads connection to ads server
3494 * @param mem_ctx TALLOC_CTX for allocating sid array
3495 * @param msg Results of search
3496 * @param field Attribute to retrieve
3497 * @param sd Pointer to *struct security_descriptor to store result (talloc()ed)
3498 * @return boolean indicating success
3500 bool ads_pull_sd(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
,
3501 LDAPMessage
*msg
, const char *field
,
3502 struct security_descriptor
**sd
)
3504 struct berval
**values
;
3507 values
= ldap_get_values_len(ads
->ldap
.ld
, msg
, field
);
3509 if (!values
) return false;
3513 status
= unmarshall_sec_desc(mem_ctx
,
3514 (uint8_t *)values
[0]->bv_val
,
3515 values
[0]->bv_len
, sd
);
3516 if (!NT_STATUS_IS_OK(status
)) {
3517 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
3518 nt_errstr(status
)));
3523 ldap_value_free_len(values
);
3528 * in order to support usernames longer than 21 characters we need to
3529 * use both the sAMAccountName and the userPrincipalName attributes
3530 * It seems that not all users have the userPrincipalName attribute set
3532 * @param ads connection to ads server
3533 * @param mem_ctx TALLOC_CTX for allocating sid array
3534 * @param msg Results of search
3535 * @return the username
3537 char *ads_pull_username(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
,
3543 /* lookup_name() only works on the sAMAccountName to
3544 returning the username portion of userPrincipalName
3545 breaks winbindd_getpwnam() */
3547 ret
= ads_pull_string(ads
, mem_ctx
, msg
, "userPrincipalName");
3548 if (ret
&& (p
= strchr_m(ret
, '@'))) {
3553 return ads_pull_string(ads
, mem_ctx
, msg
, "sAMAccountName");
3558 * find the update serial number - this is the core of the ldap cache
3559 * @param ads connection to ads server
3560 * @param ads connection to ADS server
3561 * @param usn Pointer to retrieved update serial number
3562 * @return status of search
3564 ADS_STATUS
ads_USN(ADS_STRUCT
*ads
, uint32_t *usn
)
3566 const char *attrs
[] = {"highestCommittedUSN", NULL
};
3570 status
= ads_do_search_retry(ads
, "", LDAP_SCOPE_BASE
, "(objectclass=*)", attrs
, &res
);
3571 if (!ADS_ERR_OK(status
))
3574 if (ads_count_replies(ads
, res
) != 1) {
3575 ads_msgfree(ads
, res
);
3576 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED
);
3579 if (!ads_pull_uint32(ads
, res
, "highestCommittedUSN", usn
)) {
3580 ads_msgfree(ads
, res
);
3581 return ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE
);
3584 ads_msgfree(ads
, res
);
3588 /* parse a ADS timestring - typical string is
3589 '20020917091222.0Z0' which means 09:12.22 17th September
3591 static time_t ads_parse_time(const char *str
)
3597 if (sscanf(str
, "%4d%2d%2d%2d%2d%2d",
3598 &tm
.tm_year
, &tm
.tm_mon
, &tm
.tm_mday
,
3599 &tm
.tm_hour
, &tm
.tm_min
, &tm
.tm_sec
) != 6) {
3608 /********************************************************************
3609 ********************************************************************/
3611 ADS_STATUS
ads_current_time(ADS_STRUCT
*ads
)
3613 const char *attrs
[] = {"currentTime", NULL
};
3617 TALLOC_CTX
*tmp_ctx
= talloc_stackframe();
3618 ADS_STRUCT
*ads_s
= ads
;
3620 /* establish a new ldap tcp session if necessary */
3622 if ( !ads
->ldap
.ld
) {
3624 * ADS_STRUCT may be being reused after a
3625 * DC lookup, so ads->ldap.ss may already have a
3626 * good address. If not, re-initialize the passed-in
3627 * ADS_STRUCT with the given server.XXXX parameters.
3629 * Note that this doesn't depend on
3630 * ads->server.ldap_server != NULL,
3631 * as the case where ads->server.ldap_server==NULL and
3632 * ads->ldap.ss != zero_address is precisely the DC
3633 * lookup case where ads->ldap.ss was found by going
3634 * through ads_find_dc() again we want to avoid repeating.
3636 if (is_zero_addr(&ads
->ldap
.ss
)) {
3637 ads_s
= ads_init(tmp_ctx
,
3639 ads
->server
.workgroup
,
3640 ads
->server
.ldap_server
,
3642 if (ads_s
== NULL
) {
3643 status
= ADS_ERROR(LDAP_NO_MEMORY
);
3649 * Reset ads->config.flags as it can contain the flags
3650 * returned by the previous CLDAP ping when reusing the struct.
3652 ads_s
->config
.flags
= 0;
3654 status
= ads_connect_simple_anon(ads_s
);
3655 if ( !ADS_ERR_OK(status
))
3659 status
= ads_do_search(ads_s
, "", LDAP_SCOPE_BASE
, "(objectclass=*)", attrs
, &res
);
3660 if (!ADS_ERR_OK(status
)) {
3664 timestr
= ads_pull_string(ads_s
, tmp_ctx
, res
, "currentTime");
3666 ads_msgfree(ads_s
, res
);
3667 status
= ADS_ERROR(LDAP_NO_RESULTS_RETURNED
);
3671 /* but save the time and offset in the original ADS_STRUCT */
3673 ads
->config
.current_time
= ads_parse_time(timestr
);
3675 if (ads
->config
.current_time
!= 0) {
3676 ads
->config
.time_offset
= ads
->config
.current_time
- time(NULL
);
3677 DBG_INFO("server time offset is %d seconds\n",
3678 ads
->config
.time_offset
);
3680 ads
->config
.time_offset
= 0;
3683 DBG_INFO("server time offset is %d seconds\n",
3684 ads
->config
.time_offset
);
3686 ads_msgfree(ads
, res
);
3688 status
= ADS_SUCCESS
;
3691 TALLOC_FREE(tmp_ctx
);
3696 /********************************************************************
3697 ********************************************************************/
3699 ADS_STATUS
ads_domain_func_level(ADS_STRUCT
*ads
, uint32_t *val
)
3701 TALLOC_CTX
*tmp_ctx
= talloc_stackframe();
3702 const char *attrs
[] = {"domainFunctionality", NULL
};
3705 ADS_STRUCT
*ads_s
= ads
;
3707 *val
= DS_DOMAIN_FUNCTION_2000
;
3709 /* establish a new ldap tcp session if necessary */
3711 if ( !ads
->ldap
.ld
) {
3713 * ADS_STRUCT may be being reused after a
3714 * DC lookup, so ads->ldap.ss may already have a
3715 * good address. If not, re-initialize the passed-in
3716 * ADS_STRUCT with the given server.XXXX parameters.
3718 * Note that this doesn't depend on
3719 * ads->server.ldap_server != NULL,
3720 * as the case where ads->server.ldap_server==NULL and
3721 * ads->ldap.ss != zero_address is precisely the DC
3722 * lookup case where ads->ldap.ss was found by going
3723 * through ads_find_dc() again we want to avoid repeating.
3725 if (is_zero_addr(&ads
->ldap
.ss
)) {
3726 ads_s
= ads_init(tmp_ctx
,
3728 ads
->server
.workgroup
,
3729 ads
->server
.ldap_server
,
3731 if (ads_s
== NULL
) {
3732 status
= ADS_ERROR_NT(NT_STATUS_NO_MEMORY
);
3738 * Reset ads->config.flags as it can contain the flags
3739 * returned by the previous CLDAP ping when reusing the struct.
3741 ads_s
->config
.flags
= 0;
3743 status
= ads_connect_simple_anon(ads_s
);
3744 if ( !ADS_ERR_OK(status
))
3748 /* If the attribute does not exist assume it is a Windows 2000
3749 functional domain */
3751 status
= ads_do_search(ads_s
, "", LDAP_SCOPE_BASE
, "(objectclass=*)", attrs
, &res
);
3752 if (!ADS_ERR_OK(status
)) {
3753 if ( status
.err
.rc
== LDAP_NO_SUCH_ATTRIBUTE
) {
3754 status
= ADS_SUCCESS
;
3759 if ( !ads_pull_uint32(ads_s
, res
, "domainFunctionality", val
) ) {
3760 DEBUG(5,("ads_domain_func_level: Failed to pull the domainFunctionality attribute.\n"));
3762 DEBUG(3,("ads_domain_func_level: %d\n", *val
));
3765 ads_msgfree(ads_s
, res
);
3768 TALLOC_FREE(tmp_ctx
);
3774 * find the domain sid for our domain
3775 * @param ads connection to ads server
3776 * @param sid Pointer to domain sid
3777 * @return status of search
3779 ADS_STATUS
ads_domain_sid(ADS_STRUCT
*ads
, struct dom_sid
*sid
)
3781 const char *attrs
[] = {"objectSid", NULL
};
3785 rc
= ads_do_search_retry(ads
, ads
->config
.bind_path
, LDAP_SCOPE_BASE
, "(objectclass=*)",
3787 if (!ADS_ERR_OK(rc
)) return rc
;
3788 if (!ads_pull_sid(ads
, res
, "objectSid", sid
)) {
3789 ads_msgfree(ads
, res
);
3790 return ADS_ERROR_SYSTEM(ENOENT
);
3792 ads_msgfree(ads
, res
);
3798 * find our site name
3799 * @param ads connection to ads server
3800 * @param mem_ctx Pointer to talloc context
3801 * @param site_name Pointer to the sitename
3802 * @return status of search
3804 ADS_STATUS
ads_site_dn(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
, const char **site_name
)
3808 const char *dn
, *service_name
;
3809 const char *attrs
[] = { "dsServiceName", NULL
};
3811 status
= ads_do_search(ads
, "", LDAP_SCOPE_BASE
, "(objectclass=*)", attrs
, &res
);
3812 if (!ADS_ERR_OK(status
)) {
3816 service_name
= ads_pull_string(ads
, mem_ctx
, res
, "dsServiceName");
3817 if (service_name
== NULL
) {
3818 ads_msgfree(ads
, res
);
3819 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED
);
3822 ads_msgfree(ads
, res
);
3824 /* go up three levels */
3825 dn
= ads_parent_dn(ads_parent_dn(ads_parent_dn(service_name
)));
3827 return ADS_ERROR(LDAP_NO_MEMORY
);
3830 *site_name
= talloc_strdup(mem_ctx
, dn
);
3831 if (*site_name
== NULL
) {
3832 return ADS_ERROR(LDAP_NO_MEMORY
);
3837 dsServiceName: CN=NTDS Settings,CN=W2K3DC,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,DC=ber,DC=suse,DC=de
3842 * find the site dn where a machine resides
3843 * @param ads connection to ads server
3844 * @param mem_ctx Pointer to talloc context
3845 * @param computer_name name of the machine
3846 * @param site_name Pointer to the sitename
3847 * @return status of search
3849 ADS_STATUS
ads_site_dn_for_machine(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
, const char *computer_name
, const char **site_dn
)
3853 const char *parent
, *filter
;
3854 char *config_context
= NULL
;
3857 /* shortcut a query */
3858 if (strequal(computer_name
, ads
->config
.ldap_server_name
)) {
3859 return ads_site_dn(ads
, mem_ctx
, site_dn
);
3862 status
= ads_config_path(ads
, mem_ctx
, &config_context
);
3863 if (!ADS_ERR_OK(status
)) {
3867 filter
= talloc_asprintf(mem_ctx
, "(cn=%s)", computer_name
);
3868 if (filter
== NULL
) {
3869 return ADS_ERROR(LDAP_NO_MEMORY
);
3872 status
= ads_do_search(ads
, config_context
, LDAP_SCOPE_SUBTREE
,
3873 filter
, NULL
, &res
);
3874 if (!ADS_ERR_OK(status
)) {
3878 if (ads_count_replies(ads
, res
) != 1) {
3879 ads_msgfree(ads
, res
);
3880 return ADS_ERROR(LDAP_NO_SUCH_OBJECT
);
3883 dn
= ads_get_dn(ads
, mem_ctx
, res
);
3885 ads_msgfree(ads
, res
);
3886 return ADS_ERROR(LDAP_NO_MEMORY
);
3889 /* go up three levels */
3890 parent
= ads_parent_dn(ads_parent_dn(ads_parent_dn(dn
)));
3891 if (parent
== NULL
) {
3892 ads_msgfree(ads
, res
);
3894 return ADS_ERROR(LDAP_NO_MEMORY
);
3897 *site_dn
= talloc_strdup(mem_ctx
, parent
);
3898 if (*site_dn
== NULL
) {
3899 ads_msgfree(ads
, res
);
3901 return ADS_ERROR(LDAP_NO_MEMORY
);
3905 ads_msgfree(ads
, res
);
3911 * get the upn suffixes for a domain
3912 * @param ads connection to ads server
3913 * @param mem_ctx Pointer to talloc context
3914 * @param suffixes Pointer to an array of suffixes
3915 * @param num_suffixes Pointer to the number of suffixes
3916 * @return status of search
3918 ADS_STATUS
ads_upn_suffixes(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
, char ***suffixes
, size_t *num_suffixes
)
3923 char *config_context
= NULL
;
3924 const char *attrs
[] = { "uPNSuffixes", NULL
};
3926 status
= ads_config_path(ads
, mem_ctx
, &config_context
);
3927 if (!ADS_ERR_OK(status
)) {
3931 base
= talloc_asprintf(mem_ctx
, "cn=Partitions,%s", config_context
);
3933 return ADS_ERROR(LDAP_NO_MEMORY
);
3936 status
= ads_search_dn(ads
, &res
, base
, attrs
);
3937 if (!ADS_ERR_OK(status
)) {
3941 if (ads_count_replies(ads
, res
) != 1) {
3942 ads_msgfree(ads
, res
);
3943 return ADS_ERROR(LDAP_NO_SUCH_OBJECT
);
3946 (*suffixes
) = ads_pull_strings(ads
, mem_ctx
, res
, "uPNSuffixes", num_suffixes
);
3947 if ((*suffixes
) == NULL
) {
3948 ads_msgfree(ads
, res
);
3949 return ADS_ERROR(LDAP_NO_MEMORY
);
3952 ads_msgfree(ads
, res
);
3958 * get the joinable ous for a domain
3959 * @param ads connection to ads server
3960 * @param mem_ctx Pointer to talloc context
3961 * @param ous Pointer to an array of ous
3962 * @param num_ous Pointer to the number of ous
3963 * @return status of search
3965 ADS_STATUS
ads_get_joinable_ous(ADS_STRUCT
*ads
,
3966 TALLOC_CTX
*mem_ctx
,
3971 LDAPMessage
*res
= NULL
;
3972 LDAPMessage
*msg
= NULL
;
3973 const char *attrs
[] = { "dn", NULL
};
3976 status
= ads_search(ads
, &res
,
3977 "(|(objectClass=domain)(objectclass=organizationalUnit))",
3979 if (!ADS_ERR_OK(status
)) {
3983 count
= ads_count_replies(ads
, res
);
3985 ads_msgfree(ads
, res
);
3986 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED
);
3989 for (msg
= ads_first_entry(ads
, res
); msg
;
3990 msg
= ads_next_entry(ads
, msg
)) {
3991 const char **p
= discard_const_p(const char *, *ous
);
3994 dn
= ads_get_dn(ads
, talloc_tos(), msg
);
3996 ads_msgfree(ads
, res
);
3997 return ADS_ERROR(LDAP_NO_MEMORY
);
4000 if (!add_string_to_array(mem_ctx
, dn
, &p
, num_ous
)) {
4002 ads_msgfree(ads
, res
);
4003 return ADS_ERROR(LDAP_NO_MEMORY
);
4007 *ous
= discard_const_p(char *, p
);
4010 ads_msgfree(ads
, res
);
4017 * pull a struct dom_sid from an extended dn string
4018 * @param mem_ctx TALLOC_CTX
4019 * @param extended_dn string
4020 * @param flags string type of extended_dn
4021 * @param sid pointer to a struct dom_sid
4022 * @return NT_STATUS_OK on success,
4023 * NT_INVALID_PARAMETER on error,
4024 * NT_STATUS_NOT_FOUND if no SID present
4026 ADS_STATUS
ads_get_sid_from_extended_dn(TALLOC_CTX
*mem_ctx
,
4027 const char *extended_dn
,
4028 enum ads_extended_dn_flags flags
,
4029 struct dom_sid
*sid
)
4034 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER
);
4037 /* otherwise extended_dn gets stripped off */
4038 if ((dn
= talloc_strdup(mem_ctx
, extended_dn
)) == NULL
) {
4039 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER
);
4042 * ADS_EXTENDED_DN_HEX_STRING:
4043 * <GUID=238e1963cb390f4bb032ba0105525a29>;<SID=010500000000000515000000bb68c8fd6b61b427572eb04556040000>;CN=gd,OU=berlin,OU=suse,DC=ber,DC=suse,DC=de
4045 * ADS_EXTENDED_DN_STRING (only with w2k3):
4046 * <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
4048 * Object with no SID, such as an Exchange Public Folder
4049 * <GUID=28907fb4bdf6854993e7f0a10b504e7c>;CN=public,CN=Microsoft Exchange System Objects,DC=sd2k3ms,DC=west,DC=isilon,DC=com
4052 p
= strchr(dn
, ';');
4054 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER
);
4057 if (strncmp(p
, ";<SID=", strlen(";<SID=")) != 0) {
4058 DEBUG(5,("No SID present in extended dn\n"));
4059 return ADS_ERROR_NT(NT_STATUS_NOT_FOUND
);
4062 p
+= strlen(";<SID=");
4066 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER
);
4071 DEBUG(100,("ads_get_sid_from_extended_dn: sid string is %s\n", p
));
4075 case ADS_EXTENDED_DN_STRING
:
4076 if (!string_to_sid(sid
, p
)) {
4077 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER
);
4080 case ADS_EXTENDED_DN_HEX_STRING
: {
4085 buf_len
= strhex_to_str(buf
, sizeof(buf
), p
, strlen(p
));
4087 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER
);
4090 ret
= sid_parse((const uint8_t *)buf
, buf_len
, sid
);
4092 DEBUG(10,("failed to parse sid\n"));
4093 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER
);
4098 DEBUG(10,("unknown extended dn format\n"));
4099 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER
);
4102 return ADS_ERROR_NT(NT_STATUS_OK
);
4105 /********************************************************************
4106 ********************************************************************/
4108 char* ads_get_upn( ADS_STRUCT
*ads
, TALLOC_CTX
*ctx
, const char *machine_name
)
4110 LDAPMessage
*res
= NULL
;
4115 status
= ads_find_machine_acct(ads
, &res
, machine_name
);
4116 if (!ADS_ERR_OK(status
)) {
4117 DEBUG(0,("ads_get_upn: Failed to find account for %s\n",
4118 lp_netbios_name()));
4122 if ( (count
= ads_count_replies(ads
, res
)) != 1 ) {
4123 DEBUG(1,("ads_get_upn: %d entries returned!\n", count
));
4127 if ( (name
= ads_pull_string(ads
, ctx
, res
, "userPrincipalName")) == NULL
) {
4128 DEBUG(2,("ads_get_upn: No userPrincipalName attribute!\n"));
4132 ads_msgfree(ads
, res
);
4139 SAVED CODE
- we used to join via ldap
- remember how we did
this. JRA
.
4142 * Join a machine to a realm
4143 * Creates the machine account and sets the machine password
4144 * @param ads connection to ads server
4145 * @param machine name of host to add
4146 * @param org_unit Organizational unit to place machine in
4147 * @return status of join
4149 ADS_STATUS
ads_join_realm(ADS_STRUCT
*ads
, const char *machine_name
,
4150 uint32_t account_type
, const char *org_unit
)
4153 LDAPMessage
*res
= NULL
;
4156 /* machine name must be lowercase */
4157 machine
= SMB_STRDUP(machine_name
);
4158 strlower_m(machine
);
4161 status = ads_find_machine_acct(ads, (void **)&res, machine);
4162 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
4163 DEBUG(0, ("Host account for %s already exists - deleting old account\n", machine));
4164 status = ads_leave_realm(ads, machine);
4165 if (!ADS_ERR_OK(status)) {
4166 DEBUG(0, ("Failed to delete host '%s' from the '%s' realm.\n",
4167 machine, ads->config.realm));
4172 status
= ads_add_machine_acct(ads
, machine
, account_type
, org_unit
);
4173 if (!ADS_ERR_OK(status
)) {
4174 DEBUG(0, ("ads_join_realm: ads_add_machine_acct failed (%s): %s\n", machine
, ads_errstr(status
)));
4179 status
= ads_find_machine_acct(ads
, (void **)(void *)&res
, machine
);
4180 if (!ADS_ERR_OK(status
)) {
4181 DEBUG(0, ("ads_join_realm: Host account test failed for machine %s\n", machine
));
4187 ads_msgfree(ads
, res
);
4194 * Delete a machine from the realm
4195 * @param ads connection to ads server
4196 * @param hostname Machine to remove
4197 * @return status of delete
4199 ADS_STATUS
ads_leave_realm(ADS_STRUCT
*ads
, const char *hostname
)
4204 char *hostnameDN
, *host
;
4206 LDAPControl ldap_control
;
4207 LDAPControl
* pldap_control
[2] = {NULL
, NULL
};
4209 pldap_control
[0] = &ldap_control
;
4210 memset(&ldap_control
, 0, sizeof(LDAPControl
));
4211 ldap_control
.ldctl_oid
= discard_const_p(char, LDAP_SERVER_TREE_DELETE_OID
);
4213 /* hostname must be lowercase */
4214 host
= SMB_STRDUP(hostname
);
4215 if (!strlower_m(host
)) {
4217 return ADS_ERROR_SYSTEM(EINVAL
);
4220 status
= ads_find_machine_acct(ads
, &res
, host
);
4221 if (!ADS_ERR_OK(status
)) {
4222 DEBUG(0, ("Host account for %s does not exist.\n", host
));
4227 msg
= ads_first_entry(ads
, res
);
4230 return ADS_ERROR_SYSTEM(ENOENT
);
4233 hostnameDN
= ads_get_dn(ads
, talloc_tos(), (LDAPMessage
*)msg
);
4234 if (hostnameDN
== NULL
) {
4236 return ADS_ERROR_SYSTEM(ENOENT
);
4239 rc
= ldap_delete_ext_s(ads
->ldap
.ld
, hostnameDN
, pldap_control
, NULL
);
4241 DEBUG(3,("ldap_delete_ext_s failed with error code %d\n", rc
));
4243 DEBUG(3,("ldap_delete_ext_s succeeded with error code %d\n", rc
));
4246 if (rc
!= LDAP_SUCCESS
) {
4247 const char *attrs
[] = { "cn", NULL
};
4248 LDAPMessage
*msg_sub
;
4250 /* we only search with scope ONE, we do not expect any further
4251 * objects to be created deeper */
4253 status
= ads_do_search_retry(ads
, hostnameDN
,
4254 LDAP_SCOPE_ONELEVEL
,
4255 "(objectclass=*)", attrs
, &res
);
4257 if (!ADS_ERR_OK(status
)) {
4259 TALLOC_FREE(hostnameDN
);
4263 for (msg_sub
= ads_first_entry(ads
, res
); msg_sub
;
4264 msg_sub
= ads_next_entry(ads
, msg_sub
)) {
4268 if ((dn
= ads_get_dn(ads
, talloc_tos(), msg_sub
)) == NULL
) {
4270 TALLOC_FREE(hostnameDN
);
4271 return ADS_ERROR(LDAP_NO_MEMORY
);
4274 status
= ads_del_dn(ads
, dn
);
4275 if (!ADS_ERR_OK(status
)) {
4276 DEBUG(3,("failed to delete dn %s: %s\n", dn
, ads_errstr(status
)));
4279 TALLOC_FREE(hostnameDN
);
4286 /* there should be no subordinate objects anymore */
4287 status
= ads_do_search_retry(ads
, hostnameDN
,
4288 LDAP_SCOPE_ONELEVEL
,
4289 "(objectclass=*)", attrs
, &res
);
4291 if (!ADS_ERR_OK(status
) || ( (ads_count_replies(ads
, res
)) > 0 ) ) {
4293 TALLOC_FREE(hostnameDN
);
4297 /* delete hostnameDN now */
4298 status
= ads_del_dn(ads
, hostnameDN
);
4299 if (!ADS_ERR_OK(status
)) {
4301 DEBUG(3,("failed to delete dn %s: %s\n", hostnameDN
, ads_errstr(status
)));
4302 TALLOC_FREE(hostnameDN
);
4307 TALLOC_FREE(hostnameDN
);
4309 status
= ads_find_machine_acct(ads
, &res
, host
);
4310 if ((status
.error_type
== ENUM_ADS_ERROR_LDAP
) &&
4311 (status
.err
.rc
!= LDAP_NO_SUCH_OBJECT
)) {
4312 DEBUG(3, ("Failed to remove host account.\n"));
4322 * pull all token-sids from an LDAP dn
4323 * @param ads connection to ads server
4324 * @param mem_ctx TALLOC_CTX for allocating sid array
4325 * @param dn of LDAP object
4326 * @param user_sid pointer to struct dom_sid (objectSid)
4327 * @param primary_group_sid pointer to struct dom_sid (self composed)
4328 * @param sids pointer to sid array to allocate
4329 * @param num_sids counter of SIDs pulled
4330 * @return status of token query
4332 ADS_STATUS
ads_get_tokensids(ADS_STRUCT
*ads
,
4333 TALLOC_CTX
*mem_ctx
,
4335 struct dom_sid
*user_sid
,
4336 struct dom_sid
*primary_group_sid
,
4337 struct dom_sid
**sids
,
4341 LDAPMessage
*res
= NULL
;
4343 size_t tmp_num_sids
;
4344 struct dom_sid
*tmp_sids
;
4345 struct dom_sid tmp_user_sid
;
4346 struct dom_sid tmp_primary_group_sid
;
4348 const char *attrs
[] = {
4355 status
= ads_search_retry_dn(ads
, &res
, dn
, attrs
);
4356 if (!ADS_ERR_OK(status
)) {
4360 count
= ads_count_replies(ads
, res
);
4362 ads_msgfree(ads
, res
);
4363 return ADS_ERROR_LDAP(LDAP_NO_SUCH_OBJECT
);
4366 if (!ads_pull_sid(ads
, res
, "objectSid", &tmp_user_sid
)) {
4367 ads_msgfree(ads
, res
);
4368 return ADS_ERROR_LDAP(LDAP_NO_MEMORY
);
4371 if (!ads_pull_uint32(ads
, res
, "primaryGroupID", &pgid
)) {
4372 ads_msgfree(ads
, res
);
4373 return ADS_ERROR_LDAP(LDAP_NO_MEMORY
);
4377 /* hack to compose the primary group sid without knowing the
4380 struct dom_sid domsid
;
4382 sid_copy(&domsid
, &tmp_user_sid
);
4384 if (!sid_split_rid(&domsid
, NULL
)) {
4385 ads_msgfree(ads
, res
);
4386 return ADS_ERROR_LDAP(LDAP_NO_MEMORY
);
4389 if (!sid_compose(&tmp_primary_group_sid
, &domsid
, pgid
)) {
4390 ads_msgfree(ads
, res
);
4391 return ADS_ERROR_LDAP(LDAP_NO_MEMORY
);
4395 tmp_num_sids
= ads_pull_sids(ads
, mem_ctx
, res
, "tokenGroups", &tmp_sids
);
4397 if (tmp_num_sids
== 0 || !tmp_sids
) {
4398 ads_msgfree(ads
, res
);
4399 return ADS_ERROR_LDAP(LDAP_NO_MEMORY
);
4403 *num_sids
= tmp_num_sids
;
4411 *user_sid
= tmp_user_sid
;
4414 if (primary_group_sid
) {
4415 *primary_group_sid
= tmp_primary_group_sid
;
4418 DEBUG(10,("ads_get_tokensids: returned %d sids\n", (int)tmp_num_sids
+ 2));
4420 ads_msgfree(ads
, res
);
4421 return ADS_ERROR_LDAP(LDAP_SUCCESS
);
4425 * Find a sAMAccountName in LDAP
4426 * @param ads connection to ads server
4427 * @param mem_ctx TALLOC_CTX for allocating sid array
4428 * @param samaccountname to search
4429 * @param uac_ret uint32_t pointer userAccountControl attribute value
4430 * @param dn_ret pointer to dn
4431 * @return status of token query
4433 ADS_STATUS
ads_find_samaccount(ADS_STRUCT
*ads
,
4434 TALLOC_CTX
*mem_ctx
,
4435 const char *samaccountname
,
4437 const char **dn_ret
)
4440 const char *attrs
[] = { "userAccountControl", NULL
};
4442 LDAPMessage
*res
= NULL
;
4446 filter
= talloc_asprintf(mem_ctx
, "(&(objectclass=user)(sAMAccountName=%s))",
4448 if (filter
== NULL
) {
4449 status
= ADS_ERROR_NT(NT_STATUS_NO_MEMORY
);
4453 status
= ads_do_search_all(ads
, ads
->config
.bind_path
,
4455 filter
, attrs
, &res
);
4457 if (!ADS_ERR_OK(status
)) {
4461 if (ads_count_replies(ads
, res
) != 1) {
4462 status
= ADS_ERROR(LDAP_NO_RESULTS_RETURNED
);
4466 dn
= ads_get_dn(ads
, talloc_tos(), res
);
4468 status
= ADS_ERROR(LDAP_NO_MEMORY
);
4472 if (!ads_pull_uint32(ads
, res
, "userAccountControl", &uac
)) {
4473 status
= ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE
);
4482 *dn_ret
= talloc_strdup(mem_ctx
, dn
);
4484 status
= ADS_ERROR(LDAP_NO_MEMORY
);
4490 ads_msgfree(ads
, res
);
4496 * find our configuration path
4497 * @param ads connection to ads server
4498 * @param mem_ctx Pointer to talloc context
4499 * @param config_path Pointer to the config path
4500 * @return status of search
4502 ADS_STATUS
ads_config_path(ADS_STRUCT
*ads
,
4503 TALLOC_CTX
*mem_ctx
,
4507 LDAPMessage
*res
= NULL
;
4508 const char *config_context
= NULL
;
4509 const char *attrs
[] = { "configurationNamingContext", NULL
};
4511 status
= ads_do_search(ads
, "", LDAP_SCOPE_BASE
,
4512 "(objectclass=*)", attrs
, &res
);
4513 if (!ADS_ERR_OK(status
)) {
4517 config_context
= ads_pull_string(ads
, mem_ctx
, res
,
4518 "configurationNamingContext");
4519 ads_msgfree(ads
, res
);
4520 if (!config_context
) {
4521 return ADS_ERROR(LDAP_NO_MEMORY
);
4525 *config_path
= talloc_strdup(mem_ctx
, config_context
);
4526 if (!*config_path
) {
4527 return ADS_ERROR(LDAP_NO_MEMORY
);
4531 return ADS_ERROR(LDAP_SUCCESS
);
4535 * find the displayName of an extended right
4536 * @param ads connection to ads server
4537 * @param config_path The config path
4538 * @param mem_ctx Pointer to talloc context
4539 * @param GUID struct of the rightsGUID
4540 * @return status of search
4542 const char *ads_get_extended_right_name_by_guid(ADS_STRUCT
*ads
,
4543 const char *config_path
,
4544 TALLOC_CTX
*mem_ctx
,
4545 const struct GUID
*rights_guid
)
4548 LDAPMessage
*res
= NULL
;
4550 const char *attrs
[] = { "displayName", NULL
};
4551 const char *result
= NULL
;
4554 if (!ads
|| !mem_ctx
|| !rights_guid
) {
4558 expr
= talloc_asprintf(mem_ctx
, "(rightsGuid=%s)",
4559 GUID_string(mem_ctx
, rights_guid
));
4564 path
= talloc_asprintf(mem_ctx
, "cn=Extended-Rights,%s", config_path
);
4569 rc
= ads_do_search_retry(ads
, path
, LDAP_SCOPE_SUBTREE
,
4571 if (!ADS_ERR_OK(rc
)) {
4575 if (ads_count_replies(ads
, res
) != 1) {
4579 result
= ads_pull_string(ads
, mem_ctx
, res
, "displayName");
4582 ads_msgfree(ads
, res
);
4587 * verify or build and verify an account ou
4588 * @param mem_ctx Pointer to talloc context
4589 * @param ads connection to ads server
4591 * @return status of search
4594 ADS_STATUS
ads_check_ou_dn(TALLOC_CTX
*mem_ctx
,
4596 const char **account_ou
)
4602 if (account_ou
== NULL
) {
4603 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER
);
4606 if (*account_ou
!= NULL
) {
4607 exploded_dn
= ldap_explode_dn(*account_ou
, 0);
4609 ldap_value_free(exploded_dn
);
4614 ou_string
= ads_ou_string(ads
, *account_ou
);
4616 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX
);
4619 name
= talloc_asprintf(mem_ctx
, "%s,%s", ou_string
,
4620 ads
->config
.bind_path
);
4621 SAFE_FREE(ou_string
);
4624 return ADS_ERROR_LDAP(LDAP_NO_MEMORY
);
4627 exploded_dn
= ldap_explode_dn(name
, 0);
4629 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX
);
4631 ldap_value_free(exploded_dn
);