2 Unix SMB/CIFS implementation.
3 ads (active directory) utility library
4 Copyright (C) Andrew Tridgell 2001
5 Copyright (C) Remus Koos 2001
6 Copyright (C) Jim McDonough <jmcd@us.ibm.com> 2002
7 Copyright (C) Guenther Deschner 2005
8 Copyright (C) Gerald Carter 2006
10 This program is free software; you can redistribute it and/or modify
11 it under the terms of the GNU General Public License as published by
12 the Free Software Foundation; either version 3 of the License, or
13 (at your option) any later version.
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License for more details.
20 You should have received a copy of the GNU General Public License
21 along with this program. If not, see <http://www.gnu.org/licenses/>.
26 #include "libads/sitename_cache.h"
27 #include "libads/cldap.h"
28 #include "../lib/tsocket/tsocket.h"
29 #include "../lib/addns/dnsquery.h"
30 #include "../libds/common/flags.h"
32 #include "../libcli/security/security.h"
33 #include "../librpc/gen_ndr/netlogon.h"
34 #include "lib/param/loadparm.h"
35 #include "libsmb/namequery.h"
36 #include "../librpc/gen_ndr/ndr_ads.h"
37 #include "auth/credentials/credentials.h"
44 * @brief basic ldap client-side routines for ads server communications
46 * The routines contained here should do the necessary ldap calls for
49 * Important note: attribute names passed into ads_ routines must
50 * already be in UTF-8 format. We do not convert them because in almost
51 * all cases, they are just ascii (which is represented with the same
52 * codepoints in UTF-8). This may have to change at some point
56 #define LDAP_SERVER_TREE_DELETE_OID "1.2.840.113556.1.4.805"
58 static SIG_ATOMIC_T gotalarm
;
60 /***************************************************************
61 Signal function to tell us we timed out.
62 ****************************************************************/
64 static void gotalarm_sig(int signum
)
69 LDAP
*ldap_open_with_timeout(const char *server
,
70 struct sockaddr_storage
*ss
,
71 int port
, unsigned int to
)
77 DEBUG(10, ("Opening connection to LDAP server '%s:%d', timeout "
78 "%u seconds\n", server
, port
, to
));
83 CatchSignal(SIGALRM
, gotalarm_sig
);
85 /* End setup timeout. */
88 if ( strchr_m(server
, ':') ) {
90 uri
= talloc_asprintf(talloc_tos(), "ldap://[%s]:%u", server
, port
);
93 uri
= talloc_asprintf(talloc_tos(), "ldap://%s:%u", server
, port
);
99 #ifdef HAVE_LDAP_INIT_FD
102 NTSTATUS status
= NT_STATUS_UNSUCCESSFUL
;
103 unsigned timeout_ms
= 1000 * to
;
105 status
= open_socket_out(ss
, port
, timeout_ms
, &fd
);
106 if (!NT_STATUS_IS_OK(status
)) {
107 DEBUG(3, ("open_socket_out: failed to open socket\n"));
111 /* define LDAP_PROTO_TCP from openldap.h if required */
112 #ifndef LDAP_PROTO_TCP
113 #define LDAP_PROTO_TCP 1
115 ldap_err
= ldap_init_fd(fd
, LDAP_PROTO_TCP
, uri
, &ldp
);
117 #elif defined(HAVE_LDAP_INITIALIZE)
118 ldap_err
= ldap_initialize(&ldp
, uri
);
120 ldp
= ldap_open(server
, port
);
122 ldap_err
= LDAP_SUCCESS
;
124 ldap_err
= LDAP_OTHER
;
127 if (ldap_err
!= LDAP_SUCCESS
) {
128 DEBUG(2,("Could not initialize connection for LDAP server '%s': %s\n",
129 uri
, ldap_err2string(ldap_err
)));
131 DEBUG(10, ("Initialized connection for LDAP server '%s'\n", uri
));
135 /* Teardown timeout. */
137 CatchSignal(SIGALRM
, SIG_IGN
);
143 static int ldap_search_with_timeout(LDAP
*ld
,
144 LDAP_CONST
char *base
,
146 LDAP_CONST
char *filter
,
149 LDAPControl
**sctrls
,
150 LDAPControl
**cctrls
,
154 int to
= lp_ldap_timeout();
155 struct timeval timeout
;
156 struct timeval
*timeout_ptr
= NULL
;
159 DBG_DEBUG("ldap_search: base => [%s], filter => [%s], scope => [%d]\n",
164 /* Setup timeout for the ldap_search_ext_s call - local and remote. */
170 timeout_ptr
= &timeout
;
172 /* Setup alarm timeout. */
173 CatchSignal(SIGALRM
, gotalarm_sig
);
174 /* Make the alarm time one second beyond
175 the timeout we're setting for the
176 remote search timeout, to allow that
177 to fire in preference. */
179 /* End setup timeout. */
183 result
= ldap_search_ext_s(ld
, base
, scope
, filter
, attrs
,
184 attrsonly
, sctrls
, cctrls
, timeout_ptr
,
188 /* Teardown alarm timeout. */
189 CatchSignal(SIGALRM
, SIG_IGN
);
194 return LDAP_TIMELIMIT_EXCEEDED
;
197 * A bug in OpenLDAP means ldap_search_ext_s can return
198 * LDAP_SUCCESS but with a NULL res pointer. Cope with
199 * this. See bug #6279 for details. JRA.
203 return LDAP_TIMELIMIT_EXCEEDED
;
209 /**********************************************
210 Do client and server sitename match ?
211 **********************************************/
213 bool ads_sitename_match(ADS_STRUCT
*ads
)
215 if (ads
->config
.server_site_name
== NULL
&&
216 ads
->config
.client_site_name
== NULL
) {
217 DEBUG(10,("ads_sitename_match: both null\n"));
220 if (ads
->config
.server_site_name
&&
221 ads
->config
.client_site_name
&&
222 strequal(ads
->config
.server_site_name
,
223 ads
->config
.client_site_name
)) {
224 DEBUG(10,("ads_sitename_match: name %s match\n", ads
->config
.server_site_name
));
227 DEBUG(10,("ads_sitename_match: no match between server: %s and client: %s\n",
228 ads
->config
.server_site_name
? ads
->config
.server_site_name
: "NULL",
229 ads
->config
.client_site_name
? ads
->config
.client_site_name
: "NULL"));
233 /**********************************************
234 Is this the closest DC ?
235 **********************************************/
237 bool ads_closest_dc(ADS_STRUCT
*ads
)
239 if (ads
->config
.flags
& NBT_SERVER_CLOSEST
) {
240 DEBUG(10,("ads_closest_dc: NBT_SERVER_CLOSEST flag set\n"));
244 /* not sure if this can ever happen */
245 if (ads_sitename_match(ads
)) {
246 DEBUG(10,("ads_closest_dc: NBT_SERVER_CLOSEST flag not set but sites match\n"));
250 if (ads
->config
.client_site_name
== NULL
) {
251 DEBUG(10,("ads_closest_dc: client belongs to no site\n"));
255 DEBUG(10,("ads_closest_dc: %s is not the closest DC\n",
256 ads
->config
.ldap_server_name
));
261 static bool ads_fill_cldap_reply(ADS_STRUCT
*ads
,
263 const struct sockaddr_storage
*ss
,
264 const struct NETLOGON_SAM_LOGON_RESPONSE_EX
*cldap_reply
)
266 TALLOC_CTX
*frame
= talloc_stackframe();
268 char addr
[INET6_ADDRSTRLEN
];
272 print_sockaddr(addr
, sizeof(addr
), ss
);
274 /* Check the CLDAP reply flags */
276 if (!(cldap_reply
->server_type
& NBT_SERVER_LDAP
)) {
277 DBG_WARNING("%s's CLDAP reply says it is not an LDAP server!\n",
283 /* Fill in the ads->config values */
285 ADS_TALLOC_CONST_FREE(ads
->config
.workgroup
);
286 ADS_TALLOC_CONST_FREE(ads
->config
.realm
);
287 ADS_TALLOC_CONST_FREE(ads
->config
.bind_path
);
288 ADS_TALLOC_CONST_FREE(ads
->config
.ldap_server_name
);
289 ADS_TALLOC_CONST_FREE(ads
->config
.server_site_name
);
290 ADS_TALLOC_CONST_FREE(ads
->config
.client_site_name
);
292 if (!check_cldap_reply_required_flags(cldap_reply
->server_type
,
293 ads
->config
.flags
)) {
298 ads
->config
.ldap_server_name
= talloc_strdup(ads
,
299 cldap_reply
->pdc_dns_name
);
300 if (ads
->config
.ldap_server_name
== NULL
) {
301 DBG_WARNING("Out of memory\n");
306 ads
->config
.workgroup
= talloc_strdup(ads
, cldap_reply
->domain_name
);
307 if (ads
->config
.workgroup
== NULL
) {
308 DBG_WARNING("Out of memory\n");
313 ads
->config
.realm
= talloc_asprintf_strupper_m(ads
,
315 cldap_reply
->dns_domain
);
316 if (ads
->config
.realm
== NULL
) {
317 DBG_WARNING("Out of memory\n");
322 status
= ads_build_dn(ads
->config
.realm
, ads
, &dn
);
323 if (!ADS_ERR_OK(status
)) {
324 DBG_DEBUG("Failed to build bind path: %s\n",
329 ads
->config
.bind_path
= dn
;
331 if (*cldap_reply
->server_site
) {
332 ads
->config
.server_site_name
=
333 talloc_strdup(ads
, cldap_reply
->server_site
);
334 if (ads
->config
.server_site_name
== NULL
) {
335 DBG_WARNING("Out of memory\n");
341 if (*cldap_reply
->client_site
) {
342 ads
->config
.client_site_name
=
343 talloc_strdup(ads
, cldap_reply
->client_site
);
344 if (ads
->config
.client_site_name
== NULL
) {
345 DBG_WARNING("Out of memory\n");
351 ads
->ldap
.port
= gc
? LDAP_GC_PORT
: LDAP_PORT
;
354 /* Store our site name. */
355 sitename_store(cldap_reply
->domain_name
, cldap_reply
->client_site
);
356 sitename_store(cldap_reply
->dns_domain
, cldap_reply
->client_site
);
358 /* Leave this until last so that the flags are not clobbered */
359 ads
->config
.flags
= cldap_reply
->server_type
;
370 try a connection to a given ldap server, returning True and setting the servers IP
371 in the ads struct if successful
373 static bool ads_try_connect(ADS_STRUCT
*ads
, bool gc
,
374 struct sockaddr_storage
*ss
)
376 struct NETLOGON_SAM_LOGON_RESPONSE_EX cldap_reply
= {};
377 TALLOC_CTX
*frame
= talloc_stackframe();
379 char addr
[INET6_ADDRSTRLEN
] = { 0, };
386 print_sockaddr(addr
, sizeof(addr
), ss
);
388 DBG_INFO("ads_try_connect: sending CLDAP request to %s (realm: %s)\n",
389 addr
, ads
->server
.realm
);
391 ok
= ads_cldap_netlogon_5(frame
, ss
, ads
->server
.realm
, &cldap_reply
);
393 DBG_NOTICE("ads_cldap_netlogon_5(%s, %s) failed.\n",
394 addr
, ads
->server
.realm
);
399 ok
= ads_fill_cldap_reply(ads
, gc
, ss
, &cldap_reply
);
401 DBG_NOTICE("ads_fill_cldap_reply(%s, %s) failed.\n",
402 addr
, ads
->server
.realm
);
411 /**********************************************************************
412 send a cldap ping to list of servers, one at a time, until one of
413 them answers it's an ldap server. Record success in the ADS_STRUCT.
414 Take note of and update negative connection cache.
415 **********************************************************************/
417 static NTSTATUS
cldap_ping_list(ADS_STRUCT
*ads
,
419 struct samba_sockaddr
*sa_list
,
422 TALLOC_CTX
*frame
= talloc_stackframe();
423 struct timeval endtime
= timeval_current_ofs(MAX(3,lp_ldap_timeout()/2), 0);
424 uint32_t nt_version
= NETLOGON_NT_VERSION_5
| NETLOGON_NT_VERSION_5EX
;
425 struct tsocket_address
**ts_list
= NULL
;
426 const struct tsocket_address
* const *ts_list_const
= 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 ts_list_const
= (const struct tsocket_address
* const *)ts_list
;
504 status
= cldap_multi_netlogon(frame
,
505 ts_list_const
, num_requests
,
506 ads
->server
.realm
, NULL
,
508 1, endtime
, &responses
);
509 if (!NT_STATUS_IS_OK(status
)) {
510 DBG_WARNING("cldap_multi_netlogon(realm=%s, num_requests=%zu) "
511 "for count[%zu] - %s\n",
516 return NT_STATUS_NO_LOGON_SERVERS
;
519 for (i
= 0; i
< num_requests
; i
++) {
520 struct NETLOGON_SAM_LOGON_RESPONSE_EX
*cldap_reply
= NULL
;
521 char server
[INET6_ADDRSTRLEN
];
523 if (responses
[i
] == NULL
) {
527 print_sockaddr(server
, sizeof(server
), &req_sa_list
[i
]->u
.ss
);
529 if (responses
[i
]->ntver
!= NETLOGON_NT_VERSION_5EX
) {
530 DBG_NOTICE("realm=[%s] nt_version mismatch: 0x%08x for %s\n",
532 responses
[i
]->ntver
, server
);
536 cldap_reply
= &responses
[i
]->data
.nt5_ex
;
538 /* Returns ok only if it matches the correct server type */
539 ok
= ads_fill_cldap_reply(ads
,
541 &req_sa_list
[i
]->u
.ss
,
544 DBG_DEBUG("realm[%s]: selected %s => %s\n",
546 server
, cldap_reply
->pdc_dns_name
);
547 if (CHECK_DEBUGLVL(DBGLVL_DEBUG
)) {
548 NDR_PRINT_DEBUG(NETLOGON_SAM_LOGON_RESPONSE_EX
,
555 DBG_NOTICE("realm[%s] server %s %s - not usable\n",
557 server
, cldap_reply
->pdc_dns_name
);
558 if (CHECK_DEBUGLVL(DBGLVL_NOTICE
)) {
559 NDR_PRINT_DEBUG(NETLOGON_SAM_LOGON_RESPONSE_EX
,
562 add_failed_connection_entry(domain
, server
,
563 NT_STATUS_CLIENT_SERVER_PARAMETERS_INVALID
);
570 expired
= timeval_expired(&endtime
);
576 /* keep track of failures as all were not suitable */
577 for (i
= 0; i
< num_requests
; i
++) {
578 char server
[INET6_ADDRSTRLEN
];
580 print_sockaddr(server
, sizeof(server
), &req_sa_list
[i
]->u
.ss
);
582 add_failed_connection_entry(domain
, server
,
583 NT_STATUS_UNSUCCESSFUL
);
586 status
= NT_STATUS_NO_LOGON_SERVERS
;
587 DBG_WARNING("realm[%s] no valid response "
588 "num_requests[%zu] for count[%zu] - %s\n",
590 num_requests
, count
, nt_errstr(status
));
592 return NT_STATUS_NO_LOGON_SERVERS
;
595 /***************************************************************************
596 resolve a name and perform an "ldap ping" using NetBIOS and related methods
597 ****************************************************************************/
599 static NTSTATUS
resolve_and_ping_netbios(ADS_STRUCT
*ads
,
600 const char *domain
, const char *realm
)
604 struct samba_sockaddr
*sa_list
= NULL
;
607 DEBUG(6, ("resolve_and_ping_netbios: (cldap) looking for domain '%s'\n",
610 status
= get_sorted_dc_list(talloc_tos(),
616 if (!NT_STATUS_IS_OK(status
)) {
620 /* remove servers which are known to be dead based on
621 the corresponding DNS method */
623 for (i
= 0; i
< count
; ++i
) {
624 char server
[INET6_ADDRSTRLEN
];
626 print_sockaddr(server
, sizeof(server
), &sa_list
[i
].u
.ss
);
629 check_negative_conn_cache(realm
, server
))) {
630 /* Ensure we add the workgroup name for this
631 IP address as negative too. */
632 add_failed_connection_entry(
634 NT_STATUS_UNSUCCESSFUL
);
639 status
= cldap_ping_list(ads
, domain
, sa_list
, count
);
641 TALLOC_FREE(sa_list
);
647 /**********************************************************************
648 resolve a name and perform an "ldap ping" using DNS
649 **********************************************************************/
651 static NTSTATUS
resolve_and_ping_dns(ADS_STRUCT
*ads
, const char *sitename
,
655 struct samba_sockaddr
*sa_list
= NULL
;
658 DEBUG(6, ("resolve_and_ping_dns: (cldap) looking for realm '%s'\n",
661 status
= get_sorted_dc_list(talloc_tos(),
667 if (!NT_STATUS_IS_OK(status
)) {
668 TALLOC_FREE(sa_list
);
672 status
= cldap_ping_list(ads
, realm
, sa_list
, count
);
674 TALLOC_FREE(sa_list
);
679 /**********************************************************************
680 Try to find an AD dc using our internal name resolution routines
681 Try the realm first and then the workgroup name if netbios is not
683 **********************************************************************/
685 static NTSTATUS
ads_find_dc(ADS_STRUCT
*ads
)
687 const char *c_domain
= "";
689 bool use_own_domain
= False
;
690 char *sitename
= NULL
;
691 NTSTATUS status
= NT_STATUS_UNSUCCESSFUL
;
694 /* if the realm and workgroup are both empty, assume they are ours */
697 c_realm
= ads
->server
.realm
;
703 /* special case where no realm and no workgroup means our own */
704 if ( !ads
->server
.workgroup
|| !*ads
->server
.workgroup
) {
705 use_own_domain
= True
;
706 c_realm
= lp_realm();
710 if (!lp_disable_netbios()) {
711 if (use_own_domain
) {
712 c_domain
= lp_workgroup();
714 c_domain
= ads
->server
.workgroup
;
715 if (!*c_realm
&& (!c_domain
|| !*c_domain
)) {
716 c_domain
= lp_workgroup();
725 if (!*c_realm
&& !*c_domain
) {
726 DEBUG(0, ("ads_find_dc: no realm or workgroup! Don't know "
728 return NT_STATUS_INVALID_PARAMETER
; /* rather need MISSING_PARAMETER ... */
732 * In case of LDAP we use get_dc_name() as that
733 * creates the custom krb5.conf file
735 if (ads
->auth
.flags
& ADS_AUTH_GENERATE_KRB5_CONFIG
) {
737 struct sockaddr_storage ip_out
;
739 DEBUG(6, ("ads_find_dc: (ldap) looking for realm '%s'"
740 " and falling back to domain '%s'\n",
743 ok
= get_dc_name(c_domain
, c_realm
, srv_name
, &ip_out
);
745 if (is_zero_addr(&ip_out
)) {
746 return NT_STATUS_NO_LOGON_SERVERS
;
750 * we call ads_try_connect() to fill in the
751 * ads->config details
753 ok
= ads_try_connect(ads
, false, &ip_out
);
759 return NT_STATUS_NO_LOGON_SERVERS
;
763 sitename
= sitename_fetch(talloc_tos(), c_realm
);
764 status
= resolve_and_ping_dns(ads
, sitename
, c_realm
);
766 if (NT_STATUS_IS_OK(status
)) {
767 TALLOC_FREE(sitename
);
771 /* In case we failed to contact one of our closest DC on our
773 * need to try to find another DC, retry with a site-less SRV
778 DEBUG(3, ("ads_find_dc: failed to find a valid DC on "
779 "our site (%s), Trying to find another DC "
780 "for realm '%s' (domain '%s')\n",
781 sitename
, c_realm
, c_domain
));
782 namecache_delete(c_realm
, 0x1C);
784 resolve_and_ping_dns(ads
, NULL
, c_realm
);
786 if (NT_STATUS_IS_OK(status
)) {
787 TALLOC_FREE(sitename
);
792 TALLOC_FREE(sitename
);
795 /* try netbios as fallback - if permitted,
796 or if configuration specifically requests it */
799 DEBUG(3, ("ads_find_dc: falling back to netbios "
800 "name resolution for domain '%s' (realm '%s')\n",
804 status
= resolve_and_ping_netbios(ads
, c_domain
, c_realm
);
805 if (NT_STATUS_IS_OK(status
)) {
810 DEBUG(1, ("ads_find_dc: "
811 "name resolution for realm '%s' (domain '%s') failed: %s\n",
812 c_realm
, c_domain
, nt_errstr(status
)));
817 * Connect to the LDAP server
818 * @param ads Pointer to an existing ADS_STRUCT
819 * @return status of connection
821 static ADS_STATUS
ads_connect_internal(ADS_STRUCT
*ads
,
822 struct cli_credentials
*creds
)
824 int version
= LDAP_VERSION3
;
827 char addr
[INET6_ADDRSTRLEN
];
828 struct sockaddr_storage existing_ss
;
830 bool start_tls
= false;
832 zero_sockaddr(&existing_ss
);
834 if (!(ads
->auth
.flags
& ADS_AUTH_NO_BIND
)) {
835 SMB_ASSERT(creds
!= NULL
);
838 if (ads
->auth
.flags
& ADS_AUTH_ANON_BIND
) {
840 * Simple anonyous binds are only
841 * allowed for anonymous credentials
843 SMB_ASSERT(cli_credentials_is_anonymous(creds
));
846 if (!(ads
->auth
.flags
& (ADS_AUTH_NO_BIND
|ADS_AUTH_ANON_BIND
))) {
847 ads
->auth
.flags
|= ADS_AUTH_GENERATE_KRB5_CONFIG
;
851 * ads_connect can be passed in a reused ADS_STRUCT
852 * with an existing non-zero ads->ldap.ss IP address
853 * that was stored by going through ads_find_dc()
854 * if ads->server.ldap_server was NULL.
856 * If ads->server.ldap_server is still NULL but
857 * the target address isn't the zero address, then
858 * store that address off off before zeroing out
859 * ads->ldap so we don't keep doing multiple calls
860 * to ads_find_dc() in the reuse case.
862 * If a caller wants a clean ADS_STRUCT they
863 * will TALLOC_FREE it and allocate a new one
864 * by calling ads_init(), which ensures
865 * ads->ldap.ss is a properly zero'ed out valid IP
868 if (ads
->server
.ldap_server
== NULL
&& !is_zero_addr(&ads
->ldap
.ss
)) {
869 /* Save off the address we previously found by ads_find_dc(). */
870 existing_ss
= ads
->ldap
.ss
;
874 ZERO_STRUCT(ads
->ldap_tls_data
);
875 ZERO_STRUCT(ads
->ldap_wrap_data
);
876 ads
->ldap
.last_attempt
= time_mono(NULL
);
877 ads
->ldap_wrap_data
.wrap_type
= ADS_SASLWRAP_TYPE_PLAIN
;
879 /* try with a user specified server */
881 if (DEBUGLEVEL
>= 11) {
882 char *s
= NDR_PRINT_STRUCT_STRING(talloc_tos(), ads_struct
, ads
);
883 DEBUG(11,("ads_connect: entering\n"));
884 DEBUGADD(11,("%s\n", s
));
888 if (ads
->server
.ldap_server
) {
890 struct sockaddr_storage ss
;
892 DBG_DEBUG("Resolving name of LDAP server '%s'.\n",
893 ads
->server
.ldap_server
);
894 ok
= resolve_name(ads
->server
.ldap_server
, &ss
, 0x20, true);
896 DEBUG(5,("ads_connect: unable to resolve name %s\n",
897 ads
->server
.ldap_server
));
898 status
= ADS_ERROR_NT(NT_STATUS_NOT_FOUND
);
902 if (is_zero_addr(&ss
)) {
903 status
= ADS_ERROR_NT(NT_STATUS_NOT_FOUND
);
907 ok
= ads_try_connect(ads
, ads
->server
.gc
, &ss
);
912 /* The choice of which GC use is handled one level up in
913 ads_connect_gc(). If we continue on from here with
914 ads_find_dc() we will get GC searches on port 389 which
915 doesn't work. --jerry */
917 if (ads
->server
.gc
== true) {
918 return ADS_ERROR(LDAP_OPERATIONS_ERROR
);
921 if (ads
->server
.no_fallback
) {
922 status
= ADS_ERROR_NT(NT_STATUS_NOT_FOUND
);
927 if (!is_zero_addr(&existing_ss
)) {
928 /* We saved off who we should talk to. */
929 bool ok
= ads_try_connect(ads
,
936 * Keep trying to find a server and fall through
937 * into ads_find_dc() again.
939 DBG_DEBUG("Failed to connect to DC via LDAP server IP address, "
940 "trying to find another DC.\n");
943 ntstatus
= ads_find_dc(ads
);
944 if (NT_STATUS_IS_OK(ntstatus
)) {
948 status
= ADS_ERROR_NT(ntstatus
);
953 print_sockaddr(addr
, sizeof(addr
), &ads
->ldap
.ss
);
954 DEBUG(3,("Successfully contacted LDAP server %s\n", addr
));
956 if (!ads
->auth
.kdc_server
) {
957 print_sockaddr(addr
, sizeof(addr
), &ads
->ldap
.ss
);
958 ads
->auth
.kdc_server
= talloc_strdup(ads
, addr
);
959 if (ads
->auth
.kdc_server
== NULL
) {
960 status
= ADS_ERROR_NT(NT_STATUS_NO_MEMORY
);
965 /* If the caller() requested no LDAP bind, then we are done */
967 if (ads
->auth
.flags
& ADS_AUTH_NO_BIND
) {
968 status
= ADS_SUCCESS
;
972 ads
->ldap_tls_data
.mem_ctx
= talloc_init("ads LDAP TLS connection memory");
973 if (!ads
->ldap_tls_data
.mem_ctx
) {
974 status
= ADS_ERROR_NT(NT_STATUS_NO_MEMORY
);
978 ads
->ldap_wrap_data
.mem_ctx
= talloc_init("ads LDAP connection memory");
979 if (!ads
->ldap_wrap_data
.mem_ctx
) {
980 status
= ADS_ERROR_NT(NT_STATUS_NO_MEMORY
);
984 /* Otherwise setup the TCP LDAP session */
986 if (ads
->auth
.flags
& ADS_AUTH_SASL_LDAPS
) {
988 ads
->ldap
.port
= 636;
989 } else if (ads
->auth
.flags
& ADS_AUTH_SASL_STARTTLS
) {
992 ads
->ldap
.port
= 389;
994 ads
->ldap
.port
= 389;
997 ads
->ldap
.ld
= ldap_open_with_timeout(ads
->config
.ldap_server_name
,
999 ads
->ldap
.port
, lp_ldap_timeout());
1000 if (ads
->ldap
.ld
== NULL
) {
1001 status
= ADS_ERROR(LDAP_OPERATIONS_ERROR
);
1004 DEBUG(3,("Connected to LDAP server %s\n", ads
->config
.ldap_server_name
));
1006 ldap_set_option(ads
->ldap
.ld
, LDAP_OPT_PROTOCOL_VERSION
, &version
);
1009 unsigned int to
= lp_ldap_connection_timeout();
1010 struct berval
*rspdata
= NULL
;
1011 char *rspoid
= NULL
;
1017 CatchSignal(SIGALRM
, gotalarm_sig
);
1019 /* End setup timeout. */
1022 rc
= ldap_extended_operation_s(ads
->ldap
.ld
,
1023 LDAP_EXOP_START_TLS
,
1029 if (gotalarm
!= 0 && rc
== LDAP_SUCCESS
) {
1034 /* Teardown timeout. */
1036 CatchSignal(SIGALRM
, SIG_IGN
);
1039 if (rspoid
!= NULL
) {
1040 ldap_memfree(rspoid
);
1043 if (rspdata
!= NULL
) {
1044 ber_bvfree(rspdata
);
1047 if (rc
!= LDAP_SUCCESS
) {
1048 status
= ADS_ERROR_LDAP(rc
);
1054 unsigned int to
= lp_ldap_connection_timeout();
1059 CatchSignal(SIGALRM
, gotalarm_sig
);
1061 /* End setup timeout. */
1064 status
= ads_setup_tls_wrapping(&ads
->ldap_tls_data
,
1066 ads
->config
.ldap_server_name
);
1069 /* Teardown timeout. */
1071 CatchSignal(SIGALRM
, SIG_IGN
);
1074 if ( !ADS_ERR_OK(status
) ) {
1079 /* cache the successful connection for workgroup and realm */
1080 if (ads_closest_dc(ads
)) {
1081 saf_store( ads
->server
.workgroup
, ads
->config
.ldap_server_name
);
1082 saf_store( ads
->server
.realm
, ads
->config
.ldap_server_name
);
1085 /* fill in the current time and offsets */
1087 status
= ads_current_time( ads
);
1088 if ( !ADS_ERR_OK(status
) ) {
1092 /* Now do the bind */
1094 if (ads
->auth
.flags
& ADS_AUTH_ANON_BIND
) {
1095 status
= ADS_ERROR(ldap_simple_bind_s(ads
->ldap
.ld
, NULL
, NULL
));
1099 status
= ads_sasl_bind(ads
, creds
);
1102 if (DEBUGLEVEL
>= 11) {
1103 char *s
= NDR_PRINT_STRUCT_STRING(talloc_tos(), ads_struct
, ads
);
1104 DEBUG(11,("ads_connect: leaving with: %s\n",
1105 ads_errstr(status
)));
1106 DEBUGADD(11,("%s\n", s
));
1114 * Connect to the LDAP server using without a bind
1115 * and without a tcp connection at all
1117 * @param ads Pointer to an existing ADS_STRUCT
1118 * @return status of connection
1120 ADS_STATUS
ads_connect_cldap_only(ADS_STRUCT
*ads
)
1122 ads
->auth
.flags
|= ADS_AUTH_NO_BIND
;
1123 return ads_connect_internal(ads
, NULL
);
1127 * Connect to the LDAP server
1128 * @param ads Pointer to an existing ADS_STRUCT
1129 * @return status of connection
1131 ADS_STATUS
ads_connect_creds(ADS_STRUCT
*ads
, struct cli_credentials
*creds
)
1133 SMB_ASSERT(creds
!= NULL
);
1136 * We allow upgrades from
1137 * ADS_AUTH_NO_BIND if credentials
1140 ads
->auth
.flags
&= ~ADS_AUTH_NO_BIND
;
1143 * We allow upgrades from ADS_AUTH_ANON_BIND,
1144 * as we don't want to use simple binds with
1145 * non-anon credentials
1147 if (!cli_credentials_is_anonymous(creds
)) {
1148 ads
->auth
.flags
&= ~ADS_AUTH_ANON_BIND
;
1151 return ads_connect_internal(ads
, creds
);
1155 * Connect to the LDAP server using anonymous credentials
1156 * using a simple bind without username/password
1158 * @param ads Pointer to an existing ADS_STRUCT
1159 * @return status of connection
1161 ADS_STATUS
ads_connect_simple_anon(ADS_STRUCT
*ads
)
1163 TALLOC_CTX
*frame
= talloc_stackframe();
1164 struct cli_credentials
*creds
= NULL
;
1167 creds
= cli_credentials_init_anon(frame
);
1168 if (creds
== NULL
) {
1170 return ADS_ERROR_SYSTEM(errno
);
1173 ads
->auth
.flags
|= ADS_AUTH_ANON_BIND
;
1174 status
= ads_connect_creds(ads
, creds
);
1180 * Connect to the LDAP server using the machine account
1181 * @param ads Pointer to an existing ADS_STRUCT
1182 * @return status of connection
1184 ADS_STATUS
ads_connect_machine(ADS_STRUCT
*ads
)
1186 TALLOC_CTX
*frame
= talloc_stackframe();
1187 struct cli_credentials
*creds
= NULL
;
1191 ntstatus
= pdb_get_trust_credentials(ads
->server
.workgroup
,
1195 if (!NT_STATUS_IS_OK(ntstatus
)) {
1197 return ADS_ERROR_NT(ntstatus
);
1200 status
= ads_connect_creds(ads
, creds
);
1206 * Zero out the internal ads->ldap struct and initialize the address to zero IP.
1207 * @param ads Pointer to an existing ADS_STRUCT
1209 * Sets the ads->ldap.ss to a valid
1210 * zero ip address that can be detected by
1211 * our is_zero_addr() function. Otherwise
1212 * it is left as AF_UNSPEC (0).
1214 void ads_zero_ldap(ADS_STRUCT
*ads
)
1216 ZERO_STRUCT(ads
->ldap
);
1218 * Initialize the sockaddr_storage so we can use
1219 * sockaddr test functions against it.
1221 zero_sockaddr(&ads
->ldap
.ss
);
1225 * Disconnect the LDAP server
1226 * @param ads Pointer to an existing ADS_STRUCT
1228 void ads_disconnect(ADS_STRUCT
*ads
)
1231 ldap_unbind(ads
->ldap
.ld
);
1232 ads
->ldap
.ld
= NULL
;
1234 if (ads
->ldap_tls_data
.mem_ctx
) {
1235 talloc_free(ads
->ldap_tls_data
.mem_ctx
);
1237 if (ads
->ldap_wrap_data
.wrap_ops
&&
1238 ads
->ldap_wrap_data
.wrap_ops
->disconnect
) {
1239 ads
->ldap_wrap_data
.wrap_ops
->disconnect(&ads
->ldap_wrap_data
);
1241 if (ads
->ldap_wrap_data
.mem_ctx
) {
1242 talloc_free(ads
->ldap_wrap_data
.mem_ctx
);
1245 ZERO_STRUCT(ads
->ldap_tls_data
);
1246 ZERO_STRUCT(ads
->ldap_wrap_data
);
1250 Duplicate a struct berval into talloc'ed memory
1252 static struct berval
*dup_berval(TALLOC_CTX
*ctx
, const struct berval
*in_val
)
1254 struct berval
*value
;
1256 if (!in_val
) return NULL
;
1258 value
= talloc_zero(ctx
, struct berval
);
1261 if (in_val
->bv_len
== 0) return value
;
1263 value
->bv_len
= in_val
->bv_len
;
1264 value
->bv_val
= (char *)talloc_memdup(ctx
, in_val
->bv_val
,
1270 Make a values list out of an array of (struct berval *)
1272 static struct berval
**ads_dup_values(TALLOC_CTX
*ctx
,
1273 const struct berval
**in_vals
)
1275 struct berval
**values
;
1278 if (!in_vals
) return NULL
;
1279 for (i
=0; in_vals
[i
]; i
++)
1280 ; /* count values */
1281 values
= talloc_zero_array(ctx
, struct berval
*, i
+1);
1282 if (!values
) return NULL
;
1284 for (i
=0; in_vals
[i
]; i
++) {
1285 values
[i
] = dup_berval(ctx
, in_vals
[i
]);
1291 UTF8-encode a values list out of an array of (char *)
1293 static char **ads_push_strvals(TALLOC_CTX
*ctx
, const char **in_vals
)
1299 if (!in_vals
) return NULL
;
1300 for (i
=0; in_vals
[i
]; i
++)
1301 ; /* count values */
1302 values
= talloc_zero_array(ctx
, char *, i
+1);
1303 if (!values
) return NULL
;
1305 for (i
=0; in_vals
[i
]; i
++) {
1306 if (!push_utf8_talloc(ctx
, &values
[i
], in_vals
[i
], &size
)) {
1307 TALLOC_FREE(values
);
1315 Pull a (char *) array out of a UTF8-encoded values list
1317 static char **ads_pull_strvals(TALLOC_CTX
*ctx
, const char **in_vals
)
1321 size_t converted_size
;
1323 if (!in_vals
) return NULL
;
1324 for (i
=0; in_vals
[i
]; i
++)
1325 ; /* count values */
1326 values
= talloc_zero_array(ctx
, char *, i
+1);
1327 if (!values
) return NULL
;
1329 for (i
=0; in_vals
[i
]; i
++) {
1330 if (!pull_utf8_talloc(ctx
, &values
[i
], in_vals
[i
],
1332 DEBUG(0,("ads_pull_strvals: pull_utf8_talloc failed: "
1333 "%s\n", strerror(errno
)));
1340 * Do a search with paged results. cookie must be null on the first
1341 * call, and then returned on each subsequent call. It will be null
1342 * again when the entire search is complete
1343 * @param ads connection to ads server
1344 * @param bind_path Base dn for the search
1345 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1346 * @param expr Search expression - specified in local charset
1347 * @param attrs Attributes to retrieve - specified in utf8 or ascii
1348 * @param res ** which will contain results - free res* with ads_msgfree()
1349 * @param count Number of entries retrieved on this page
1350 * @param cookie The paged results cookie to be returned on subsequent calls
1351 * @return status of search
1353 static ADS_STATUS
ads_do_paged_search_args(ADS_STRUCT
*ads
,
1354 const char *bind_path
,
1355 int scope
, const char *expr
,
1356 const char **attrs
, void *args
,
1358 int *count
, struct berval
**cookie
)
1361 char *utf8_expr
, *utf8_path
, **search_attrs
= NULL
;
1362 size_t converted_size
;
1363 LDAPControl PagedResults
, NoReferrals
, ExternalCtrl
, *controls
[4], **rcontrols
;
1364 BerElement
*cookie_be
= NULL
;
1365 struct berval
*cookie_bv
= NULL
;
1366 BerElement
*ext_be
= NULL
;
1367 struct berval
*ext_bv
= NULL
;
1370 ads_control
*external_control
= (ads_control
*) args
;
1374 if (!(ctx
= talloc_init("ads_do_paged_search_args")))
1375 return ADS_ERROR(LDAP_NO_MEMORY
);
1377 /* 0 means the conversion worked but the result was empty
1378 so we only fail if it's -1. In any case, it always
1379 at least nulls out the dest */
1380 if (!push_utf8_talloc(ctx
, &utf8_expr
, expr
, &converted_size
) ||
1381 !push_utf8_talloc(ctx
, &utf8_path
, bind_path
, &converted_size
))
1383 rc
= LDAP_NO_MEMORY
;
1387 if (!attrs
|| !(*attrs
))
1388 search_attrs
= NULL
;
1390 /* This would be the utf8-encoded version...*/
1391 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
1392 if (!(search_attrs
= str_list_copy(talloc_tos(), attrs
))) {
1393 rc
= LDAP_NO_MEMORY
;
1398 /* Paged results only available on ldap v3 or later */
1399 ldap_get_option(ads
->ldap
.ld
, LDAP_OPT_PROTOCOL_VERSION
, &version
);
1400 if (version
< LDAP_VERSION3
) {
1401 rc
= LDAP_NOT_SUPPORTED
;
1405 cookie_be
= ber_alloc_t(LBER_USE_DER
);
1407 ber_printf(cookie_be
, "{iO}", (ber_int_t
) ads
->config
.ldap_page_size
, *cookie
);
1408 ber_bvfree(*cookie
); /* don't need it from last time */
1411 ber_printf(cookie_be
, "{io}", (ber_int_t
) ads
->config
.ldap_page_size
, "", 0);
1413 ber_flatten(cookie_be
, &cookie_bv
);
1414 PagedResults
.ldctl_oid
= discard_const_p(char, ADS_PAGE_CTL_OID
);
1415 PagedResults
.ldctl_iscritical
= (char) 1;
1416 PagedResults
.ldctl_value
.bv_len
= cookie_bv
->bv_len
;
1417 PagedResults
.ldctl_value
.bv_val
= cookie_bv
->bv_val
;
1419 NoReferrals
.ldctl_oid
= discard_const_p(char, ADS_NO_REFERRALS_OID
);
1420 NoReferrals
.ldctl_iscritical
= (char) 0;
1421 NoReferrals
.ldctl_value
.bv_len
= 0;
1422 NoReferrals
.ldctl_value
.bv_val
= discard_const_p(char, "");
1424 if (external_control
&&
1425 (strequal(external_control
->control
, ADS_EXTENDED_DN_OID
) ||
1426 strequal(external_control
->control
, ADS_SD_FLAGS_OID
))) {
1428 ExternalCtrl
.ldctl_oid
= discard_const_p(char, external_control
->control
);
1429 ExternalCtrl
.ldctl_iscritical
= (char) external_control
->critical
;
1431 /* win2k does not accept a ldctl_value being passed in */
1433 if (external_control
->val
!= 0) {
1435 if ((ext_be
= ber_alloc_t(LBER_USE_DER
)) == NULL
) {
1436 rc
= LDAP_NO_MEMORY
;
1440 if ((ber_printf(ext_be
, "{i}", (ber_int_t
) external_control
->val
)) == -1) {
1441 rc
= LDAP_NO_MEMORY
;
1444 if ((ber_flatten(ext_be
, &ext_bv
)) == -1) {
1445 rc
= LDAP_NO_MEMORY
;
1449 ExternalCtrl
.ldctl_value
.bv_len
= ext_bv
->bv_len
;
1450 ExternalCtrl
.ldctl_value
.bv_val
= ext_bv
->bv_val
;
1453 ExternalCtrl
.ldctl_value
.bv_len
= 0;
1454 ExternalCtrl
.ldctl_value
.bv_val
= NULL
;
1457 controls
[0] = &NoReferrals
;
1458 controls
[1] = &PagedResults
;
1459 controls
[2] = &ExternalCtrl
;
1463 controls
[0] = &NoReferrals
;
1464 controls
[1] = &PagedResults
;
1468 /* we need to disable referrals as the openldap libs don't
1469 handle them and paged results at the same time. Using them
1470 together results in the result record containing the server
1471 page control being removed from the result list (tridge/jmcd)
1473 leaving this in despite the control that says don't generate
1474 referrals, in case the server doesn't support it (jmcd)
1476 ldap_set_option(ads
->ldap
.ld
, LDAP_OPT_REFERRALS
, LDAP_OPT_OFF
);
1478 rc
= ldap_search_with_timeout(ads
->ldap
.ld
, utf8_path
, scope
, utf8_expr
,
1479 search_attrs
, 0, controls
,
1480 NULL
, LDAP_NO_LIMIT
,
1481 (LDAPMessage
**)res
);
1483 ber_free(cookie_be
, 1);
1484 ber_bvfree(cookie_bv
);
1487 DEBUG(3,("ads_do_paged_search_args: ldap_search_with_timeout(%s) -> %s\n", expr
,
1488 ldap_err2string(rc
)));
1489 if (rc
== LDAP_OTHER
) {
1493 ret
= ldap_parse_result(ads
->ldap
.ld
,
1501 if (ret
== LDAP_SUCCESS
) {
1502 DEBUG(3, ("ldap_search_with_timeout(%s) "
1503 "error: %s\n", expr
, ldap_errmsg
));
1504 ldap_memfree(ldap_errmsg
);
1510 rc
= ldap_parse_result(ads
->ldap
.ld
, *res
, NULL
, NULL
, NULL
,
1511 NULL
, &rcontrols
, 0);
1517 for (i
=0; rcontrols
[i
]; i
++) {
1518 if (strcmp(ADS_PAGE_CTL_OID
, rcontrols
[i
]->ldctl_oid
) == 0) {
1519 cookie_be
= ber_init(&rcontrols
[i
]->ldctl_value
);
1520 ber_scanf(cookie_be
,"{iO}", (ber_int_t
*) count
,
1522 /* the berval is the cookie, but must be freed when
1524 if (cookie_bv
->bv_len
) /* still more to do */
1525 *cookie
=ber_bvdup(cookie_bv
);
1528 ber_bvfree(cookie_bv
);
1529 ber_free(cookie_be
, 1);
1533 ldap_controls_free(rcontrols
);
1536 talloc_destroy(ctx
);
1539 ber_free(ext_be
, 1);
1546 if (rc
!= LDAP_SUCCESS
&& *res
!= NULL
) {
1547 ads_msgfree(ads
, *res
);
1551 /* if/when we decide to utf8-encode attrs, take out this next line */
1552 TALLOC_FREE(search_attrs
);
1554 return ADS_ERROR(rc
);
1557 static ADS_STATUS
ads_do_paged_search(ADS_STRUCT
*ads
, const char *bind_path
,
1558 int scope
, const char *expr
,
1559 const char **attrs
, LDAPMessage
**res
,
1560 int *count
, struct berval
**cookie
)
1562 return ads_do_paged_search_args(ads
, bind_path
, scope
, expr
, attrs
, NULL
, res
, count
, cookie
);
1567 * Get all results for a search. This uses ads_do_paged_search() to return
1568 * all entries in a large search.
1569 * @param ads connection to ads server
1570 * @param bind_path Base dn for the search
1571 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1572 * @param expr Search expression
1573 * @param attrs Attributes to retrieve
1574 * @param res ** which will contain results - free res* with ads_msgfree()
1575 * @return status of search
1577 ADS_STATUS
ads_do_search_all_args(ADS_STRUCT
*ads
, const char *bind_path
,
1578 int scope
, const char *expr
,
1579 const char **attrs
, void *args
,
1582 struct berval
*cookie
= NULL
;
1587 status
= ads_do_paged_search_args(ads
, bind_path
, scope
, expr
, attrs
, args
, res
,
1590 if (!ADS_ERR_OK(status
))
1593 #ifdef HAVE_LDAP_ADD_RESULT_ENTRY
1595 LDAPMessage
*res2
= NULL
;
1596 LDAPMessage
*msg
, *next
;
1598 status
= ads_do_paged_search_args(ads
, bind_path
, scope
, expr
,
1599 attrs
, args
, &res2
, &count
, &cookie
);
1600 if (!ADS_ERR_OK(status
)) {
1604 /* this relies on the way that ldap_add_result_entry() works internally. I hope
1605 that this works on all ldap libs, but I have only tested with openldap */
1606 for (msg
= ads_first_message(ads
, res2
); msg
; msg
= next
) {
1607 next
= ads_next_message(ads
, msg
);
1608 ldap_add_result_entry((LDAPMessage
**)res
, msg
);
1610 /* note that we do not free res2, as the memory is now
1611 part of the main returned list */
1614 DEBUG(0, ("no ldap_add_result_entry() support in LDAP libs!\n"));
1615 status
= ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL
);
1621 ADS_STATUS
ads_do_search_all(ADS_STRUCT
*ads
, const char *bind_path
,
1622 int scope
, const char *expr
,
1623 const char **attrs
, LDAPMessage
**res
)
1625 return ads_do_search_all_args(ads
, bind_path
, scope
, expr
, attrs
, NULL
, res
);
1628 ADS_STATUS
ads_do_search_all_sd_flags(ADS_STRUCT
*ads
, const char *bind_path
,
1629 int scope
, const char *expr
,
1630 const char **attrs
, uint32_t sd_flags
,
1635 args
.control
= ADS_SD_FLAGS_OID
;
1636 args
.val
= sd_flags
;
1637 args
.critical
= True
;
1639 return ads_do_search_all_args(ads
, bind_path
, scope
, expr
, attrs
, &args
, res
);
1644 * Run a function on all results for a search. Uses ads_do_paged_search() and
1645 * runs the function as each page is returned, using ads_process_results()
1646 * @param ads connection to ads server
1647 * @param bind_path Base dn for the search
1648 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1649 * @param expr Search expression - specified in local charset
1650 * @param attrs Attributes to retrieve - specified in UTF-8 or ascii
1651 * @param fn Function which takes attr name, values list, and data_area
1652 * @param data_area Pointer which is passed to function on each call
1653 * @return status of search
1655 ADS_STATUS
ads_do_search_all_fn(ADS_STRUCT
*ads
, const char *bind_path
,
1656 int scope
, const char *expr
, const char **attrs
,
1657 bool (*fn
)(ADS_STRUCT
*, char *, void **, void *),
1660 struct berval
*cookie
= NULL
;
1665 status
= ads_do_paged_search(ads
, bind_path
, scope
, expr
, attrs
, &res
,
1668 if (!ADS_ERR_OK(status
)) return status
;
1670 ads_process_results(ads
, res
, fn
, data_area
);
1671 ads_msgfree(ads
, res
);
1674 status
= ads_do_paged_search(ads
, bind_path
, scope
, expr
, attrs
,
1675 &res
, &count
, &cookie
);
1677 if (!ADS_ERR_OK(status
)) break;
1679 ads_process_results(ads
, res
, fn
, data_area
);
1680 ads_msgfree(ads
, res
);
1687 * Do a search with a timeout.
1688 * @param ads connection to ads server
1689 * @param bind_path Base dn for the search
1690 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1691 * @param expr Search expression
1692 * @param attrs Attributes to retrieve
1693 * @param res ** which will contain results - free res* with ads_msgfree()
1694 * @return status of search
1696 ADS_STATUS
ads_do_search(ADS_STRUCT
*ads
, const char *bind_path
, int scope
,
1698 const char **attrs
, LDAPMessage
**res
)
1701 char *utf8_expr
, *utf8_path
, **search_attrs
= NULL
;
1702 size_t converted_size
;
1706 if (!(ctx
= talloc_init("ads_do_search"))) {
1707 DEBUG(1,("ads_do_search: talloc_init() failed!\n"));
1708 return ADS_ERROR(LDAP_NO_MEMORY
);
1711 /* 0 means the conversion worked but the result was empty
1712 so we only fail if it's negative. In any case, it always
1713 at least nulls out the dest */
1714 if (!push_utf8_talloc(ctx
, &utf8_expr
, expr
, &converted_size
) ||
1715 !push_utf8_talloc(ctx
, &utf8_path
, bind_path
, &converted_size
))
1717 DEBUG(1,("ads_do_search: push_utf8_talloc() failed!\n"));
1718 rc
= LDAP_NO_MEMORY
;
1722 if (!attrs
|| !(*attrs
))
1723 search_attrs
= NULL
;
1725 /* This would be the utf8-encoded version...*/
1726 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
1727 if (!(search_attrs
= str_list_copy(talloc_tos(), attrs
)))
1729 DEBUG(1,("ads_do_search: str_list_copy() failed!\n"));
1730 rc
= LDAP_NO_MEMORY
;
1735 /* see the note in ads_do_paged_search - we *must* disable referrals */
1736 ldap_set_option(ads
->ldap
.ld
, LDAP_OPT_REFERRALS
, LDAP_OPT_OFF
);
1738 rc
= ldap_search_with_timeout(ads
->ldap
.ld
, utf8_path
, scope
, utf8_expr
,
1739 search_attrs
, 0, NULL
, NULL
,
1741 (LDAPMessage
**)res
);
1743 if (rc
== LDAP_SIZELIMIT_EXCEEDED
) {
1744 DEBUG(3,("Warning! sizelimit exceeded in ldap. Truncating.\n"));
1749 talloc_destroy(ctx
);
1750 /* if/when we decide to utf8-encode attrs, take out this next line */
1751 TALLOC_FREE(search_attrs
);
1752 return ADS_ERROR(rc
);
1755 * Do a general ADS search
1756 * @param ads connection to ads server
1757 * @param res ** which will contain results - free res* with ads_msgfree()
1758 * @param expr Search expression
1759 * @param attrs Attributes to retrieve
1760 * @return status of search
1762 ADS_STATUS
ads_search(ADS_STRUCT
*ads
, LDAPMessage
**res
,
1763 const char *expr
, const char **attrs
)
1765 return ads_do_search(ads
, ads
->config
.bind_path
, LDAP_SCOPE_SUBTREE
,
1770 * Do a search on a specific DistinguishedName
1771 * @param ads connection to ads server
1772 * @param res ** which will contain results - free res* with ads_msgfree()
1773 * @param dn DistinguishedName to search
1774 * @param attrs Attributes to retrieve
1775 * @return status of search
1777 ADS_STATUS
ads_search_dn(ADS_STRUCT
*ads
, LDAPMessage
**res
,
1778 const char *dn
, const char **attrs
)
1780 return ads_do_search(ads
, dn
, LDAP_SCOPE_BASE
, "(objectclass=*)",
1785 * Free up memory from a ads_search
1786 * @param ads connection to ads server
1787 * @param msg Search results to free
1789 void ads_msgfree(ADS_STRUCT
*ads
, LDAPMessage
*msg
)
1796 * Get a dn from search results
1797 * @param ads connection to ads server
1798 * @param msg Search result
1801 char *ads_get_dn(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
, LDAPMessage
*msg
)
1803 char *utf8_dn
, *unix_dn
;
1804 size_t converted_size
;
1806 utf8_dn
= ldap_get_dn(ads
->ldap
.ld
, msg
);
1809 DEBUG (5, ("ads_get_dn: ldap_get_dn failed\n"));
1813 if (!pull_utf8_talloc(mem_ctx
, &unix_dn
, utf8_dn
, &converted_size
)) {
1814 DEBUG(0,("ads_get_dn: string conversion failure utf8 [%s]\n",
1818 ldap_memfree(utf8_dn
);
1823 * Get the parent from a dn
1824 * @param dn the dn to return the parent from
1825 * @return parent dn string
1827 char *ads_parent_dn(const char *dn
)
1835 p
= strchr(dn
, ',');
1845 * Find a machine account given a hostname
1846 * @param ads connection to ads server
1847 * @param res ** which will contain results - free res* with ads_msgfree()
1848 * @param host Hostname to search for
1849 * @return status of search
1851 ADS_STATUS
ads_find_machine_acct(ADS_STRUCT
*ads
, LDAPMessage
**res
,
1852 const char *machine
)
1856 const char *attrs
[] = {
1857 /* This is how Windows checks for machine accounts */
1860 "userAccountControl",
1862 "ServicePrincipalName",
1863 "userPrincipalName",
1865 /* Additional attributes Samba checks */
1866 "msDS-AdditionalDnsHostName",
1867 "msDS-SupportedEncryptionTypes",
1868 "nTSecurityDescriptor",
1873 TALLOC_CTX
*frame
= talloc_stackframe();
1877 /* the easiest way to find a machine account anywhere in the tree
1878 is to look for hostname$ */
1879 expr
= talloc_asprintf(frame
, "(samAccountName=%s$)", machine
);
1881 status
= ADS_ERROR_NT(NT_STATUS_NO_MEMORY
);
1885 status
= ads_search(ads
, res
, expr
, attrs
);
1886 if (ADS_ERR_OK(status
)) {
1887 if (ads_count_replies(ads
, *res
) != 1) {
1888 status
= ADS_ERROR_LDAP(LDAP_NO_SUCH_OBJECT
);
1898 * Initialize a list of mods to be used in a modify request
1899 * @param ctx An initialized TALLOC_CTX
1900 * @return allocated ADS_MODLIST
1902 ADS_MODLIST
ads_init_mods(TALLOC_CTX
*ctx
)
1904 #define ADS_MODLIST_ALLOC_SIZE 10
1907 if ((mods
= talloc_zero_array(ctx
, LDAPMod
*, ADS_MODLIST_ALLOC_SIZE
+ 1)))
1908 /* -1 is safety to make sure we don't go over the end.
1909 need to reset it to NULL before doing ldap modify */
1910 mods
[ADS_MODLIST_ALLOC_SIZE
] = (LDAPMod
*) -1;
1912 return (ADS_MODLIST
)mods
;
1917 add an attribute to the list, with values list already constructed
1919 static ADS_STATUS
ads_modlist_add(TALLOC_CTX
*ctx
, ADS_MODLIST
*mods
,
1920 int mod_op
, const char *name
,
1921 const void *_invals
)
1924 LDAPMod
**modlist
= (LDAPMod
**) *mods
;
1925 struct berval
**ber_values
= NULL
;
1926 char **char_values
= NULL
;
1929 mod_op
= LDAP_MOD_DELETE
;
1931 if (mod_op
& LDAP_MOD_BVALUES
) {
1932 const struct berval
**b
;
1933 b
= discard_const_p(const struct berval
*, _invals
);
1934 ber_values
= ads_dup_values(ctx
, b
);
1937 c
= discard_const_p(const char *, _invals
);
1938 char_values
= ads_push_strvals(ctx
, c
);
1942 /* find the first empty slot */
1943 for (curmod
=0; modlist
[curmod
] && modlist
[curmod
] != (LDAPMod
*) -1;
1945 if (modlist
[curmod
] == (LDAPMod
*) -1) {
1946 if (!(modlist
= talloc_realloc(ctx
, modlist
, LDAPMod
*,
1947 curmod
+ADS_MODLIST_ALLOC_SIZE
+1)))
1948 return ADS_ERROR(LDAP_NO_MEMORY
);
1949 memset(&modlist
[curmod
], 0,
1950 ADS_MODLIST_ALLOC_SIZE
*sizeof(LDAPMod
*));
1951 modlist
[curmod
+ADS_MODLIST_ALLOC_SIZE
] = (LDAPMod
*) -1;
1952 *mods
= (ADS_MODLIST
)modlist
;
1955 if (!(modlist
[curmod
] = talloc_zero(ctx
, LDAPMod
)))
1956 return ADS_ERROR(LDAP_NO_MEMORY
);
1957 modlist
[curmod
]->mod_type
= talloc_strdup(ctx
, name
);
1958 if (mod_op
& LDAP_MOD_BVALUES
) {
1959 modlist
[curmod
]->mod_bvalues
= ber_values
;
1960 } else if (mod_op
& LDAP_MOD_DELETE
) {
1961 modlist
[curmod
]->mod_values
= NULL
;
1963 modlist
[curmod
]->mod_values
= char_values
;
1966 modlist
[curmod
]->mod_op
= mod_op
;
1967 return ADS_ERROR(LDAP_SUCCESS
);
1971 * Add a single string value to a mod list
1972 * @param ctx An initialized TALLOC_CTX
1973 * @param mods An initialized ADS_MODLIST
1974 * @param name The attribute name to add
1975 * @param val The value to add - NULL means DELETE
1976 * @return ADS STATUS indicating success of add
1978 ADS_STATUS
ads_mod_str(TALLOC_CTX
*ctx
, ADS_MODLIST
*mods
,
1979 const char *name
, const char *val
)
1981 const char *values
[2];
1987 return ads_modlist_add(ctx
, mods
, LDAP_MOD_DELETE
, name
, NULL
);
1988 return ads_modlist_add(ctx
, mods
, LDAP_MOD_REPLACE
, name
, values
);
1992 * Add an array of string values to a mod list
1993 * @param ctx An initialized TALLOC_CTX
1994 * @param mods An initialized ADS_MODLIST
1995 * @param name The attribute name to add
1996 * @param vals The array of string values to add - NULL means DELETE
1997 * @return ADS STATUS indicating success of add
1999 ADS_STATUS
ads_mod_strlist(TALLOC_CTX
*ctx
, ADS_MODLIST
*mods
,
2000 const char *name
, const char **vals
)
2003 return ads_modlist_add(ctx
, mods
, LDAP_MOD_DELETE
, name
, NULL
);
2004 return ads_modlist_add(ctx
, mods
, LDAP_MOD_REPLACE
,
2005 name
, (const void **) vals
);
2009 * Add a single ber-encoded value to a mod list
2010 * @param ctx An initialized TALLOC_CTX
2011 * @param mods An initialized ADS_MODLIST
2012 * @param name The attribute name to add
2013 * @param val The value to add - NULL means DELETE
2014 * @return ADS STATUS indicating success of add
2016 static ADS_STATUS
ads_mod_ber(TALLOC_CTX
*ctx
, ADS_MODLIST
*mods
,
2017 const char *name
, const struct berval
*val
)
2019 const struct berval
*values
[2];
2024 return ads_modlist_add(ctx
, mods
, LDAP_MOD_DELETE
, name
, NULL
);
2025 return ads_modlist_add(ctx
, mods
, LDAP_MOD_REPLACE
|LDAP_MOD_BVALUES
,
2026 name
, (const void *) values
);
2029 static void ads_print_error(int ret
, LDAP
*ld
)
2032 char *ld_error
= NULL
;
2033 ldap_get_option(ld
, LDAP_OPT_ERROR_STRING
, &ld_error
);
2034 DBG_ERR("AD LDAP ERROR: %d (%s): %s\n",
2036 ldap_err2string(ret
),
2038 SAFE_FREE(ld_error
);
2043 * Perform an ldap modify
2044 * @param ads connection to ads server
2045 * @param mod_dn DistinguishedName to modify
2046 * @param mods list of modifications to perform
2047 * @return status of modify
2049 ADS_STATUS
ads_gen_mod(ADS_STRUCT
*ads
, const char *mod_dn
, ADS_MODLIST mods
)
2052 char *utf8_dn
= NULL
;
2053 size_t converted_size
;
2055 this control is needed to modify that contains a currently
2056 non-existent attribute (but allowable for the object) to run
2058 LDAPControl PermitModify
= {
2059 discard_const_p(char, ADS_PERMIT_MODIFY_OID
),
2062 LDAPControl
*controls
[2];
2064 DBG_INFO("AD LDAP: Modifying %s\n", mod_dn
);
2066 controls
[0] = &PermitModify
;
2069 if (!push_utf8_talloc(talloc_tos(), &utf8_dn
, mod_dn
, &converted_size
)) {
2070 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY
);
2073 /* find the end of the list, marked by NULL or -1 */
2074 for(i
=0;(mods
[i
]!=0)&&(mods
[i
]!=(LDAPMod
*) -1);i
++);
2075 /* make sure the end of the list is NULL */
2077 ret
= ldap_modify_ext_s(ads
->ldap
.ld
, utf8_dn
,
2078 (LDAPMod
**) mods
, controls
, NULL
);
2079 ads_print_error(ret
, ads
->ldap
.ld
);
2080 TALLOC_FREE(utf8_dn
);
2081 return ADS_ERROR(ret
);
2085 * Perform an ldap add
2086 * @param ads connection to ads server
2087 * @param new_dn DistinguishedName to add
2088 * @param mods list of attributes and values for DN
2089 * @return status of add
2091 ADS_STATUS
ads_gen_add(ADS_STRUCT
*ads
, const char *new_dn
, ADS_MODLIST mods
)
2094 char *utf8_dn
= NULL
;
2095 size_t converted_size
;
2097 DBG_INFO("AD LDAP: Adding %s\n", new_dn
);
2099 if (!push_utf8_talloc(talloc_tos(), &utf8_dn
, new_dn
, &converted_size
)) {
2100 DEBUG(1, ("ads_gen_add: push_utf8_talloc failed!\n"));
2101 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY
);
2104 /* find the end of the list, marked by NULL or -1 */
2105 for(i
=0;(mods
[i
]!=0)&&(mods
[i
]!=(LDAPMod
*) -1);i
++);
2106 /* make sure the end of the list is NULL */
2109 ret
= ldap_add_ext_s(ads
->ldap
.ld
, utf8_dn
, (LDAPMod
**)mods
, NULL
, NULL
);
2110 ads_print_error(ret
, ads
->ldap
.ld
);
2111 TALLOC_FREE(utf8_dn
);
2112 return ADS_ERROR(ret
);
2116 * Delete a DistinguishedName
2117 * @param ads connection to ads server
2118 * @param new_dn DistinguishedName to delete
2119 * @return status of delete
2121 ADS_STATUS
ads_del_dn(ADS_STRUCT
*ads
, char *del_dn
)
2124 char *utf8_dn
= NULL
;
2125 size_t converted_size
;
2126 if (!push_utf8_talloc(talloc_tos(), &utf8_dn
, del_dn
, &converted_size
)) {
2127 DEBUG(1, ("ads_del_dn: push_utf8_talloc failed!\n"));
2128 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY
);
2131 DBG_INFO("AD LDAP: Deleting %s\n", del_dn
);
2133 ret
= ldap_delete_s(ads
->ldap
.ld
, utf8_dn
);
2134 ads_print_error(ret
, ads
->ldap
.ld
);
2135 TALLOC_FREE(utf8_dn
);
2136 return ADS_ERROR(ret
);
2140 * Build an org unit string
2141 * if org unit is Computers or blank then assume a container, otherwise
2142 * assume a / separated list of organisational units.
2143 * jmcd: '\' is now used for escapes so certain chars can be in the ou (e.g. #)
2144 * @param ads connection to ads server
2145 * @param org_unit Organizational unit
2146 * @return org unit string - caller must free
2148 char *ads_ou_string(ADS_STRUCT
*ads
, const char *org_unit
)
2154 if (!org_unit
|| !*org_unit
) {
2156 ret
= ads_default_ou_string(ads
, DS_GUID_COMPUTERS_CONTAINER
);
2158 /* samba4 might not yet respond to a wellknownobject-query */
2159 return ret
? ret
: SMB_STRDUP("cn=Computers");
2162 if (strequal(org_unit
, "Computers")) {
2163 return SMB_STRDUP("cn=Computers");
2166 /* jmcd: removed "\\" from the separation chars, because it is
2167 needed as an escape for chars like '#' which are valid in an
2169 status
= ads_build_path(org_unit
, "/", "ou=", 1, &dn
);
2170 if (!ADS_ERR_OK(status
)) {
2178 * Get a org unit string for a well-known GUID
2179 * @param ads connection to ads server
2180 * @param wknguid Well known GUID
2181 * @return org unit string - caller must free
2183 char *ads_default_ou_string(ADS_STRUCT
*ads
, const char *wknguid
)
2186 LDAPMessage
*res
= NULL
;
2187 char *base
, *wkn_dn
= NULL
, *ret
= NULL
, **wkn_dn_exp
= NULL
,
2188 **bind_dn_exp
= NULL
;
2189 const char *attrs
[] = {"distinguishedName", NULL
};
2190 int new_ln
, wkn_ln
, bind_ln
, i
;
2192 if (wknguid
== NULL
) {
2196 if (asprintf(&base
, "<WKGUID=%s,%s>", wknguid
, ads
->config
.bind_path
) == -1) {
2197 DEBUG(1, ("asprintf failed!\n"));
2201 status
= ads_search_dn(ads
, &res
, base
, attrs
);
2202 if (!ADS_ERR_OK(status
)) {
2203 DEBUG(1,("Failed while searching for: %s\n", base
));
2207 if (ads_count_replies(ads
, res
) != 1) {
2211 /* substitute the bind-path from the well-known-guid-search result */
2212 wkn_dn
= ads_get_dn(ads
, talloc_tos(), res
);
2217 wkn_dn_exp
= ldap_explode_dn(wkn_dn
, 0);
2222 bind_dn_exp
= ldap_explode_dn(ads
->config
.bind_path
, 0);
2227 for (wkn_ln
=0; wkn_dn_exp
[wkn_ln
]; wkn_ln
++)
2229 for (bind_ln
=0; bind_dn_exp
[bind_ln
]; bind_ln
++)
2232 new_ln
= wkn_ln
- bind_ln
;
2234 ret
= SMB_STRDUP(wkn_dn_exp
[0]);
2239 for (i
=1; i
< new_ln
; i
++) {
2242 if (asprintf(&s
, "%s,%s", ret
, wkn_dn_exp
[i
]) == -1) {
2248 ret
= SMB_STRDUP(s
);
2257 ads_msgfree(ads
, res
);
2258 TALLOC_FREE(wkn_dn
);
2260 ldap_value_free(wkn_dn_exp
);
2263 ldap_value_free(bind_dn_exp
);
2270 * Adds (appends) an item to an attribute array, rather then
2271 * replacing the whole list
2272 * @param ctx An initialized TALLOC_CTX
2273 * @param mods An initialized ADS_MODLIST
2274 * @param name name of the ldap attribute to append to
2275 * @param vals an array of values to add
2276 * @return status of addition
2279 ADS_STATUS
ads_add_strlist(TALLOC_CTX
*ctx
, ADS_MODLIST
*mods
,
2280 const char *name
, const char **vals
)
2282 return ads_modlist_add(ctx
, mods
, LDAP_MOD_ADD
, name
,
2283 (const void *) vals
);
2287 * Determines the an account's current KVNO via an LDAP lookup
2288 * @param ads An initialized ADS_STRUCT
2289 * @param account_name the NT samaccountname.
2290 * @return the kvno for the account, or -1 in case of a failure.
2293 uint32_t ads_get_kvno(ADS_STRUCT
*ads
, const char *account_name
)
2295 LDAPMessage
*res
= NULL
;
2296 uint32_t kvno
= (uint32_t)-1; /* -1 indicates a failure */
2298 const char *attrs
[] = {"msDS-KeyVersionNumber", NULL
};
2299 char *dn_string
= NULL
;
2302 DEBUG(5,("ads_get_kvno: Searching for account %s\n", account_name
));
2303 if (asprintf(&filter
, "(samAccountName=%s)", account_name
) == -1) {
2306 ret
= ads_search(ads
, &res
, filter
, attrs
);
2308 if (!ADS_ERR_OK(ret
) || (ads_count_replies(ads
, res
) != 1)) {
2309 DEBUG(1,("ads_get_kvno: Account for %s not found.\n", account_name
));
2310 ads_msgfree(ads
, res
);
2314 dn_string
= ads_get_dn(ads
, talloc_tos(), res
);
2316 DEBUG(0,("ads_get_kvno: out of memory.\n"));
2317 ads_msgfree(ads
, res
);
2320 DEBUG(5,("ads_get_kvno: Using: %s\n", dn_string
));
2321 TALLOC_FREE(dn_string
);
2323 /* ---------------------------------------------------------
2324 * 0 is returned as a default KVNO from this point on...
2325 * This is done because Windows 2000 does not support key
2326 * version numbers. Chances are that a failure in the next
2327 * step is simply due to Windows 2000 being used for a
2328 * domain controller. */
2331 if (!ads_pull_uint32(ads
, res
, "msDS-KeyVersionNumber", &kvno
)) {
2332 DEBUG(3,("ads_get_kvno: Error Determining KVNO!\n"));
2333 DEBUG(3,("ads_get_kvno: Windows 2000 does not support KVNO's, so this may be normal.\n"));
2334 ads_msgfree(ads
, res
);
2339 DEBUG(5,("ads_get_kvno: Looked Up KVNO of: %d\n", kvno
));
2340 ads_msgfree(ads
, res
);
2345 * Determines the computer account's current KVNO via an LDAP lookup
2346 * @param ads An initialized ADS_STRUCT
2347 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
2348 * @return the kvno for the computer account, or -1 in case of a failure.
2351 uint32_t ads_get_machine_kvno(ADS_STRUCT
*ads
, const char *machine_name
)
2353 char *computer_account
= NULL
;
2356 if (asprintf(&computer_account
, "%s$", machine_name
) < 0) {
2360 kvno
= ads_get_kvno(ads
, computer_account
);
2361 free(computer_account
);
2367 * This clears out all registered spn's for a given hostname
2368 * @param ads An initialized ADS_STRUCT
2369 * @param machine_name the NetBIOS name of the computer.
2370 * @return 0 upon success, non-zero otherwise.
2373 ADS_STATUS
ads_clear_service_principal_names(ADS_STRUCT
*ads
, const char *machine_name
)
2376 LDAPMessage
*res
= NULL
;
2378 const char *servicePrincipalName
[1] = {NULL
};
2380 char *dn_string
= NULL
;
2382 ret
= ads_find_machine_acct(ads
, &res
, machine_name
);
2383 if (!ADS_ERR_OK(ret
)) {
2384 DEBUG(5,("ads_clear_service_principal_names: WARNING: Host Account for %s not found... skipping operation.\n", machine_name
));
2385 DEBUG(5,("ads_clear_service_principal_names: WARNING: Service Principals for %s have NOT been cleared.\n", machine_name
));
2386 ads_msgfree(ads
, res
);
2390 DEBUG(5,("ads_clear_service_principal_names: Host account for %s found\n", machine_name
));
2391 ctx
= talloc_init("ads_clear_service_principal_names");
2393 ads_msgfree(ads
, res
);
2394 return ADS_ERROR(LDAP_NO_MEMORY
);
2397 if (!(mods
= ads_init_mods(ctx
))) {
2398 talloc_destroy(ctx
);
2399 ads_msgfree(ads
, res
);
2400 return ADS_ERROR(LDAP_NO_MEMORY
);
2402 ret
= ads_mod_strlist(ctx
, &mods
, "servicePrincipalName", servicePrincipalName
);
2403 if (!ADS_ERR_OK(ret
)) {
2404 DEBUG(1,("ads_clear_service_principal_names: Error creating strlist.\n"));
2405 ads_msgfree(ads
, res
);
2406 talloc_destroy(ctx
);
2409 dn_string
= ads_get_dn(ads
, talloc_tos(), res
);
2411 talloc_destroy(ctx
);
2412 ads_msgfree(ads
, res
);
2413 return ADS_ERROR(LDAP_NO_MEMORY
);
2415 ret
= ads_gen_mod(ads
, dn_string
, mods
);
2416 TALLOC_FREE(dn_string
);
2417 if (!ADS_ERR_OK(ret
)) {
2418 DEBUG(1,("ads_clear_service_principal_names: Error: Updating Service Principals for machine %s in LDAP\n",
2420 ads_msgfree(ads
, res
);
2421 talloc_destroy(ctx
);
2425 ads_msgfree(ads
, res
);
2426 talloc_destroy(ctx
);
2431 * @brief Search for an element in a string array.
2433 * @param[in] el_array The string array to search.
2435 * @param[in] num_el The number of elements in the string array.
2437 * @param[in] el The string to search.
2439 * @return True if found, false if not.
2441 bool ads_element_in_array(const char **el_array
, size_t num_el
, const char *el
)
2445 if (el_array
== NULL
|| num_el
== 0 || el
== NULL
) {
2449 for (i
= 0; i
< num_el
&& el_array
[i
] != NULL
; i
++) {
2452 cmp
= strcasecmp_m(el_array
[i
], el
);
2462 * @brief This gets the service principal names of an existing computer account.
2464 * @param[in] mem_ctx The memory context to use to allocate the spn array.
2466 * @param[in] ads The ADS context to use.
2468 * @param[in] machine_name The NetBIOS name of the computer, which is used to
2469 * identify the computer account.
2471 * @param[in] spn_array A pointer to store the array for SPNs.
2473 * @param[in] num_spns The number of principals stored in the array.
2475 * @return 0 on success, or a ADS error if a failure occurred.
2477 ADS_STATUS
ads_get_service_principal_names(TALLOC_CTX
*mem_ctx
,
2479 const char *machine_name
,
2484 LDAPMessage
*res
= NULL
;
2487 status
= ads_find_machine_acct(ads
,
2490 if (!ADS_ERR_OK(status
)) {
2491 DEBUG(1,("Host Account for %s not found... skipping operation.\n",
2496 count
= ads_count_replies(ads
, res
);
2498 status
= ADS_ERROR(LDAP_NO_SUCH_OBJECT
);
2502 *spn_array
= ads_pull_strings(ads
,
2505 "servicePrincipalName",
2507 if (*spn_array
== NULL
) {
2508 DEBUG(1, ("Host account for %s does not have service principal "
2511 status
= ADS_ERROR(LDAP_NO_SUCH_OBJECT
);
2516 ads_msgfree(ads
, res
);
2522 * This adds a service principal name to an existing computer account
2523 * (found by hostname) in AD.
2524 * @param ads An initialized ADS_STRUCT
2525 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
2526 * @param spns An array or strings for the service principals to add,
2527 * i.e. 'cifs/machine_name', 'http/machine.full.domain.com' etc.
2528 * @return 0 upon success, or non-zero if a failure occurs
2531 ADS_STATUS
ads_add_service_principal_names(ADS_STRUCT
*ads
,
2532 const char *machine_name
,
2537 LDAPMessage
*res
= NULL
;
2539 char *dn_string
= NULL
;
2540 const char **servicePrincipalName
= spns
;
2542 ret
= ads_find_machine_acct(ads
, &res
, machine_name
);
2543 if (!ADS_ERR_OK(ret
)) {
2544 DEBUG(1,("ads_add_service_principal_name: WARNING: Host Account for %s not found... skipping operation.\n",
2546 DEBUG(1,("ads_add_service_principal_name: WARNING: Service Principals have NOT been added.\n"));
2547 ads_msgfree(ads
, res
);
2551 DEBUG(1,("ads_add_service_principal_name: Host account for %s found\n", machine_name
));
2552 if (!(ctx
= talloc_init("ads_add_service_principal_name"))) {
2553 ads_msgfree(ads
, res
);
2554 return ADS_ERROR(LDAP_NO_MEMORY
);
2557 DEBUG(5,("ads_add_service_principal_name: INFO: "
2558 "Adding %s to host %s\n",
2559 spns
[0] ? "N/A" : spns
[0], machine_name
));
2562 DEBUG(5,("ads_add_service_principal_name: INFO: "
2563 "Adding %s to host %s\n",
2564 spns
[1] ? "N/A" : spns
[1], machine_name
));
2566 if ( (mods
= ads_init_mods(ctx
)) == NULL
) {
2567 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
2571 ret
= ads_add_strlist(ctx
,
2573 "servicePrincipalName",
2574 servicePrincipalName
);
2575 if (!ADS_ERR_OK(ret
)) {
2576 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
2580 if ( (dn_string
= ads_get_dn(ads
, ctx
, res
)) == NULL
) {
2581 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
2585 ret
= ads_gen_mod(ads
, dn_string
, mods
);
2586 if (!ADS_ERR_OK(ret
)) {
2587 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
2593 ads_msgfree(ads
, res
);
2597 static uint32_t ads_get_acct_ctrl(ADS_STRUCT
*ads
,
2600 uint32_t acct_ctrl
= 0;
2603 ok
= ads_pull_uint32(ads
, msg
, "userAccountControl", &acct_ctrl
);
2611 static ADS_STATUS
ads_change_machine_acct(ADS_STRUCT
*ads
,
2613 const struct berval
*machine_pw_val
)
2617 TALLOC_CTX
*frame
= talloc_stackframe();
2618 uint32_t acct_control
;
2619 char *control_str
= NULL
;
2620 const char *attrs
[] = {
2624 LDAPMessage
*res
= NULL
;
2627 dn
= ads_get_dn(ads
, frame
, msg
);
2629 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
2633 acct_control
= ads_get_acct_ctrl(ads
, msg
);
2634 if (acct_control
== 0) {
2635 ret
= ADS_ERROR(LDAP_NO_RESULTS_RETURNED
);
2640 * Changing the password, disables the account. So we need to change the
2641 * userAccountControl flags to enable it again.
2643 mods
= ads_init_mods(frame
);
2645 ret
= ADS_ERROR_LDAP(LDAP_NO_MEMORY
);
2649 ads_mod_ber(frame
, &mods
, "unicodePwd", machine_pw_val
);
2651 ret
= ads_gen_mod(ads
, dn
, mods
);
2652 if (!ADS_ERR_OK(ret
)) {
2658 * To activate the account, we need to disable and enable it.
2660 acct_control
|= UF_ACCOUNTDISABLE
;
2662 control_str
= talloc_asprintf(frame
, "%u", acct_control
);
2663 if (control_str
== NULL
) {
2664 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
2668 mods
= ads_init_mods(frame
);
2670 ret
= ADS_ERROR_LDAP(LDAP_NO_MEMORY
);
2674 ads_mod_str(frame
, &mods
, "userAccountControl", control_str
);
2676 ret
= ads_gen_mod(ads
, dn
, mods
);
2677 if (!ADS_ERR_OK(ret
)) {
2681 TALLOC_FREE(control_str
);
2684 * Enable the account again.
2686 acct_control
&= ~UF_ACCOUNTDISABLE
;
2688 control_str
= talloc_asprintf(frame
, "%u", acct_control
);
2689 if (control_str
== NULL
) {
2690 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
2694 mods
= ads_init_mods(frame
);
2696 ret
= ADS_ERROR_LDAP(LDAP_NO_MEMORY
);
2700 ads_mod_str(frame
, &mods
, "userAccountControl", control_str
);
2702 ret
= ads_gen_mod(ads
, dn
, mods
);
2703 if (!ADS_ERR_OK(ret
)) {
2707 TALLOC_FREE(control_str
);
2709 ret
= ads_search_dn(ads
, &res
, dn
, attrs
);
2710 ads_msgfree(ads
, res
);
2719 * adds a machine account to the ADS server
2720 * @param ads An initialized ADS_STRUCT
2721 * @param machine_name - the NetBIOS machine name of this account.
2722 * @param account_type A number indicating the type of account to create
2723 * @param org_unit The LDAP path in which to place this account
2724 * @return 0 upon success, or non-zero otherwise
2727 ADS_STATUS
ads_create_machine_acct(ADS_STRUCT
*ads
,
2728 const char *machine_name
,
2729 const char *machine_password
,
2730 const char *org_unit
,
2731 uint32_t etype_list
,
2732 const char *dns_domain_name
)
2735 char *samAccountName
= NULL
;
2736 char *controlstr
= NULL
;
2737 TALLOC_CTX
*ctx
= NULL
;
2739 char *machine_escaped
= NULL
;
2740 char *dns_hostname
= NULL
;
2741 char *new_dn
= NULL
;
2742 char *utf8_pw
= NULL
;
2743 size_t utf8_pw_len
= 0;
2744 char *utf16_pw
= NULL
;
2745 size_t utf16_pw_len
= 0;
2746 struct berval machine_pw_val
;
2748 const char **spn_array
= NULL
;
2749 size_t num_spns
= 0;
2750 const char *spn_prefix
[] = {
2752 "RestrictedKrbHost",
2755 LDAPMessage
*res
= NULL
;
2756 uint32_t acct_control
= UF_WORKSTATION_TRUST_ACCOUNT
;
2758 ctx
= talloc_init("ads_add_machine_acct");
2760 return ADS_ERROR(LDAP_NO_MEMORY
);
2763 machine_escaped
= escape_rdn_val_string_alloc(machine_name
);
2764 if (machine_escaped
== NULL
) {
2765 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
2769 utf8_pw
= talloc_asprintf(ctx
, "\"%s\"", machine_password
);
2770 if (utf8_pw
== NULL
) {
2771 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
2774 utf8_pw_len
= strlen(utf8_pw
);
2776 ok
= convert_string_talloc(ctx
,
2777 CH_UTF8
, CH_UTF16MUNGED
,
2778 utf8_pw
, utf8_pw_len
,
2779 (void *)&utf16_pw
, &utf16_pw_len
);
2781 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
2785 machine_pw_val
= (struct berval
) {
2787 .bv_len
= utf16_pw_len
,
2790 /* Check if the machine account already exists. */
2791 ret
= ads_find_machine_acct(ads
, &res
, machine_escaped
);
2792 if (ADS_ERR_OK(ret
)) {
2793 /* Change the machine account password */
2794 ret
= ads_change_machine_acct(ads
, res
, &machine_pw_val
);
2795 ads_msgfree(ads
, res
);
2799 ads_msgfree(ads
, res
);
2801 new_dn
= talloc_asprintf(ctx
, "cn=%s,%s", machine_escaped
, org_unit
);
2802 if (new_dn
== NULL
) {
2803 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
2807 /* Create machine account */
2809 samAccountName
= talloc_asprintf(ctx
, "%s$", machine_name
);
2810 if (samAccountName
== NULL
) {
2811 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
2815 dns_hostname
= talloc_asprintf(ctx
,
2819 if (dns_hostname
== NULL
) {
2820 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
2824 /* Add dns_hostname SPNs */
2825 for (i
= 0; i
< ARRAY_SIZE(spn_prefix
); i
++) {
2826 char *spn
= talloc_asprintf(ctx
,
2831 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
2835 ok
= add_string_to_array(ctx
,
2840 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
2845 /* Add machine_name SPNs */
2846 for (i
= 0; i
< ARRAY_SIZE(spn_prefix
); i
++) {
2847 char *spn
= talloc_asprintf(ctx
,
2852 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
2856 ok
= add_string_to_array(ctx
,
2861 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
2866 /* Make sure to NULL terminate the array */
2867 spn_array
= talloc_realloc(ctx
, spn_array
, const char *, num_spns
+ 1);
2868 if (spn_array
== NULL
) {
2869 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
2872 spn_array
[num_spns
] = NULL
;
2874 controlstr
= talloc_asprintf(ctx
, "%u", acct_control
);
2875 if (controlstr
== NULL
) {
2876 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
2880 mods
= ads_init_mods(ctx
);
2882 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
2886 ads_mod_str(ctx
, &mods
, "objectClass", "Computer");
2887 ads_mod_str(ctx
, &mods
, "SamAccountName", samAccountName
);
2888 ads_mod_str(ctx
, &mods
, "userAccountControl", controlstr
);
2889 ads_mod_str(ctx
, &mods
, "DnsHostName", dns_hostname
);
2890 ads_mod_strlist(ctx
, &mods
, "ServicePrincipalName", spn_array
);
2891 ads_mod_ber(ctx
, &mods
, "unicodePwd", &machine_pw_val
);
2893 ret
= ads_gen_add(ads
, new_dn
, mods
);
2896 SAFE_FREE(machine_escaped
);
2897 talloc_destroy(ctx
);
2903 * move a machine account to another OU on the ADS server
2904 * @param ads - An initialized ADS_STRUCT
2905 * @param machine_name - the NetBIOS machine name of this account.
2906 * @param org_unit - The LDAP path in which to place this account
2907 * @param moved - whether we moved the machine account (optional)
2908 * @return 0 upon success, or non-zero otherwise
2911 ADS_STATUS
ads_move_machine_acct(ADS_STRUCT
*ads
, const char *machine_name
,
2912 const char *org_unit
, bool *moved
)
2916 LDAPMessage
*res
= NULL
;
2917 char *filter
= NULL
;
2918 char *computer_dn
= NULL
;
2920 char *computer_rdn
= NULL
;
2921 bool need_move
= False
;
2923 if (asprintf(&filter
, "(samAccountName=%s$)", machine_name
) == -1) {
2924 rc
= ADS_ERROR(LDAP_NO_MEMORY
);
2928 /* Find pre-existing machine */
2929 rc
= ads_search(ads
, &res
, filter
, NULL
);
2930 if (!ADS_ERR_OK(rc
)) {
2934 computer_dn
= ads_get_dn(ads
, talloc_tos(), res
);
2936 rc
= ADS_ERROR(LDAP_NO_MEMORY
);
2940 parent_dn
= ads_parent_dn(computer_dn
);
2941 if (strequal(parent_dn
, org_unit
)) {
2947 if (asprintf(&computer_rdn
, "CN=%s", machine_name
) == -1) {
2948 rc
= ADS_ERROR(LDAP_NO_MEMORY
);
2952 ldap_status
= ldap_rename_s(ads
->ldap
.ld
, computer_dn
, computer_rdn
,
2953 org_unit
, 1, NULL
, NULL
);
2954 rc
= ADS_ERROR(ldap_status
);
2957 ads_msgfree(ads
, res
);
2959 TALLOC_FREE(computer_dn
);
2960 SAFE_FREE(computer_rdn
);
2962 if (!ADS_ERR_OK(rc
)) {
2974 dump a binary result from ldap
2976 static void dump_binary(ADS_STRUCT
*ads
, const char *field
, struct berval
**values
)
2979 for (i
=0; values
[i
]; i
++) {
2981 printf("%s: ", field
);
2982 for (j
=0; j
<values
[i
]->bv_len
; j
++) {
2983 printf("%02X", (unsigned char)values
[i
]->bv_val
[j
]);
2989 static void dump_guid(ADS_STRUCT
*ads
, const char *field
, struct berval
**values
)
2992 for (i
=0; values
[i
]; i
++) {
2994 DATA_BLOB in
= data_blob_const(values
[i
]->bv_val
, values
[i
]->bv_len
);
2997 status
= GUID_from_ndr_blob(&in
, &guid
);
2998 if (NT_STATUS_IS_OK(status
)) {
2999 printf("%s: %s\n", field
, GUID_string(talloc_tos(), &guid
));
3001 printf("%s: INVALID GUID\n", field
);
3007 dump a sid result from ldap
3009 static void dump_sid(ADS_STRUCT
*ads
, const char *field
, struct berval
**values
)
3012 for (i
=0; values
[i
]; i
++) {
3015 struct dom_sid_buf tmp
;
3016 ret
= sid_parse((const uint8_t *)values
[i
]->bv_val
,
3017 values
[i
]->bv_len
, &sid
);
3021 printf("%s: %s\n", field
, dom_sid_str_buf(&sid
, &tmp
));
3026 dump ntSecurityDescriptor
3028 static void dump_sd(ADS_STRUCT
*ads
, const char *filed
, struct berval
**values
)
3030 TALLOC_CTX
*frame
= talloc_stackframe();
3031 struct security_descriptor
*psd
;
3034 status
= unmarshall_sec_desc(talloc_tos(), (uint8_t *)values
[0]->bv_val
,
3035 values
[0]->bv_len
, &psd
);
3036 if (!NT_STATUS_IS_OK(status
)) {
3037 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
3038 nt_errstr(status
)));
3044 ads_disp_sd(ads
, talloc_tos(), psd
);
3051 dump a string result from ldap
3053 static void dump_string(const char *field
, char **values
)
3056 for (i
=0; values
[i
]; i
++) {
3057 printf("%s: %s\n", field
, values
[i
]);
3062 dump a field from LDAP on stdout
3066 static bool ads_dump_field(ADS_STRUCT
*ads
, char *field
, void **values
, void *data_area
)
3071 void (*handler
)(ADS_STRUCT
*, const char *, struct berval
**);
3073 {"objectGUID", False
, dump_guid
},
3074 {"netbootGUID", False
, dump_guid
},
3075 {"nTSecurityDescriptor", False
, dump_sd
},
3076 {"dnsRecord", False
, dump_binary
},
3077 {"objectSid", False
, dump_sid
},
3078 {"securityIdentifier", False
, dump_sid
},
3079 {"tokenGroups", False
, dump_sid
},
3080 {"tokenGroupsNoGCAcceptable", False
, dump_sid
},
3081 {"tokengroupsGlobalandUniversal", False
, dump_sid
},
3082 {"mS-DS-CreatorSID", False
, dump_sid
},
3083 {"msExchMailboxGuid", False
, dump_guid
},
3084 {"msDS-TrustForestTrustInfo", False
, dump_binary
},
3089 if (!field
) { /* must be end of an entry */
3094 for (i
=0; handlers
[i
].name
; i
++) {
3095 if (strcasecmp_m(handlers
[i
].name
, field
) == 0) {
3096 if (!values
) /* first time, indicate string or not */
3097 return handlers
[i
].string
;
3098 handlers
[i
].handler(ads
, field
, (struct berval
**) values
);
3102 if (!handlers
[i
].name
) {
3103 if (!values
) /* first time, indicate string conversion */
3105 dump_string(field
, (char **)values
);
3111 * Dump a result from LDAP on stdout
3112 * used for debugging
3113 * @param ads connection to ads server
3114 * @param res Results to dump
3117 void ads_dump(ADS_STRUCT
*ads
, LDAPMessage
*res
)
3119 ads_process_results(ads
, res
, ads_dump_field
, NULL
);
3123 * Walk through results, calling a function for each entry found.
3124 * The function receives a field name, a berval * array of values,
3125 * and a data area passed through from the start. The function is
3126 * called once with null for field and values at the end of each
3128 * @param ads connection to ads server
3129 * @param res Results to process
3130 * @param fn Function for processing each result
3131 * @param data_area user-defined area to pass to function
3133 void ads_process_results(ADS_STRUCT
*ads
, LDAPMessage
*res
,
3134 bool (*fn
)(ADS_STRUCT
*, char *, void **, void *),
3139 size_t converted_size
;
3141 if (!(ctx
= talloc_init("ads_process_results")))
3144 for (msg
= ads_first_entry(ads
, res
); msg
;
3145 msg
= ads_next_entry(ads
, msg
)) {
3149 for (utf8_field
=ldap_first_attribute(ads
->ldap
.ld
,
3150 (LDAPMessage
*)msg
,&b
);
3152 utf8_field
=ldap_next_attribute(ads
->ldap
.ld
,
3153 (LDAPMessage
*)msg
,b
)) {
3154 struct berval
**ber_vals
;
3160 if (!pull_utf8_talloc(ctx
, &field
, utf8_field
,
3163 DEBUG(0,("ads_process_results: "
3164 "pull_utf8_talloc failed: %s\n",
3168 string
= fn(ads
, field
, NULL
, data_area
);
3173 utf8_vals
= ldap_get_values(ads
->ldap
.ld
,
3174 (LDAPMessage
*)msg
, field
);
3175 p
= discard_const_p(const char *, utf8_vals
);
3176 str_vals
= ads_pull_strvals(ctx
, p
);
3177 fn(ads
, field
, (void **) str_vals
, data_area
);
3178 ldap_value_free(utf8_vals
);
3180 ber_vals
= ldap_get_values_len(ads
->ldap
.ld
,
3181 (LDAPMessage
*)msg
, field
);
3182 fn(ads
, field
, (void **) ber_vals
, data_area
);
3184 ldap_value_free_len(ber_vals
);
3186 ldap_memfree(utf8_field
);
3189 talloc_free_children(ctx
);
3190 fn(ads
, NULL
, NULL
, data_area
); /* completed an entry */
3193 talloc_destroy(ctx
);
3197 * count how many replies are in a LDAPMessage
3198 * @param ads connection to ads server
3199 * @param res Results to count
3200 * @return number of replies
3202 int ads_count_replies(ADS_STRUCT
*ads
, void *res
)
3204 return ldap_count_entries(ads
->ldap
.ld
, (LDAPMessage
*)res
);
3208 * pull the first entry from a ADS result
3209 * @param ads connection to ads server
3210 * @param res Results of search
3211 * @return first entry from result
3213 LDAPMessage
*ads_first_entry(ADS_STRUCT
*ads
, LDAPMessage
*res
)
3215 return ldap_first_entry(ads
->ldap
.ld
, res
);
3219 * pull the next entry from a ADS result
3220 * @param ads connection to ads server
3221 * @param res Results of search
3222 * @return next entry from result
3224 LDAPMessage
*ads_next_entry(ADS_STRUCT
*ads
, LDAPMessage
*res
)
3226 return ldap_next_entry(ads
->ldap
.ld
, res
);
3230 * pull the first message from a ADS result
3231 * @param ads connection to ads server
3232 * @param res Results of search
3233 * @return first message from result
3235 LDAPMessage
*ads_first_message(ADS_STRUCT
*ads
, LDAPMessage
*res
)
3237 return ldap_first_message(ads
->ldap
.ld
, res
);
3241 * pull the next message from a ADS result
3242 * @param ads connection to ads server
3243 * @param res Results of search
3244 * @return next message from result
3246 LDAPMessage
*ads_next_message(ADS_STRUCT
*ads
, LDAPMessage
*res
)
3248 return ldap_next_message(ads
->ldap
.ld
, res
);
3252 * pull a single string from a ADS result
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 * @return Result string in talloc context
3259 char *ads_pull_string(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
, LDAPMessage
*msg
,
3265 size_t converted_size
;
3267 values
= ldap_get_values(ads
->ldap
.ld
, msg
, field
);
3271 if (values
[0] && pull_utf8_talloc(mem_ctx
, &ux_string
, values
[0],
3276 ldap_value_free(values
);
3281 * pull an array of strings from a ADS result
3282 * @param ads connection to ads server
3283 * @param mem_ctx TALLOC_CTX to use for allocating result string
3284 * @param msg Results of search
3285 * @param field Attribute to retrieve
3286 * @return Result strings in talloc context
3288 char **ads_pull_strings(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
,
3289 LDAPMessage
*msg
, const char *field
,
3294 size_t i
, converted_size
;
3296 values
= ldap_get_values(ads
->ldap
.ld
, msg
, field
);
3300 *num_values
= ldap_count_values(values
);
3302 ret
= talloc_array(mem_ctx
, char *, *num_values
+ 1);
3304 ldap_value_free(values
);
3308 for (i
=0;i
<*num_values
;i
++) {
3309 if (!pull_utf8_talloc(mem_ctx
, &ret
[i
], values
[i
],
3312 ldap_value_free(values
);
3318 ldap_value_free(values
);
3323 * pull an array of strings from a ADS result
3324 * (handle large multivalue attributes with range retrieval)
3325 * @param ads connection to ads server
3326 * @param mem_ctx TALLOC_CTX to use for allocating result string
3327 * @param msg Results of search
3328 * @param field Attribute to retrieve
3329 * @param current_strings strings returned by a previous call to this function
3330 * @param next_attribute The next query should ask for this attribute
3331 * @param num_values How many values did we get this time?
3332 * @param more_values Are there more values to get?
3333 * @return Result strings in talloc context
3335 char **ads_pull_strings_range(ADS_STRUCT
*ads
,
3336 TALLOC_CTX
*mem_ctx
,
3337 LDAPMessage
*msg
, const char *field
,
3338 char **current_strings
,
3339 const char **next_attribute
,
3340 size_t *num_strings
,
3344 char *expected_range_attrib
, *range_attr
= NULL
;
3345 BerElement
*ptr
= NULL
;
3348 size_t num_new_strings
;
3349 unsigned long int range_start
;
3350 unsigned long int range_end
;
3352 /* we might have been given the whole lot anyway */
3353 if ((strings
= ads_pull_strings(ads
, mem_ctx
, msg
, field
, num_strings
))) {
3354 *more_strings
= False
;
3358 expected_range_attrib
= talloc_asprintf(mem_ctx
, "%s;Range=", field
);
3360 /* look for Range result */
3361 for (attr
= ldap_first_attribute(ads
->ldap
.ld
, (LDAPMessage
*)msg
, &ptr
);
3363 attr
= ldap_next_attribute(ads
->ldap
.ld
, (LDAPMessage
*)msg
, ptr
)) {
3364 /* we ignore the fact that this is utf8, as all attributes are ascii... */
3365 if (strnequal(attr
, expected_range_attrib
, strlen(expected_range_attrib
))) {
3373 /* nothing here - this field is just empty */
3374 *more_strings
= False
;
3378 if (sscanf(&range_attr
[strlen(expected_range_attrib
)], "%lu-%lu",
3379 &range_start
, &range_end
) == 2) {
3380 *more_strings
= True
;
3382 if (sscanf(&range_attr
[strlen(expected_range_attrib
)], "%lu-*",
3383 &range_start
) == 1) {
3384 *more_strings
= False
;
3386 DEBUG(1, ("ads_pull_strings_range: Cannot parse Range attribute (%s)\n",
3388 ldap_memfree(range_attr
);
3389 *more_strings
= False
;
3394 if ((*num_strings
) != range_start
) {
3395 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) doesn't start at %u, but at %lu"
3396 " - aborting range retrieval\n",
3397 range_attr
, (unsigned int)(*num_strings
) + 1, range_start
));
3398 ldap_memfree(range_attr
);
3399 *more_strings
= False
;
3403 new_strings
= ads_pull_strings(ads
, mem_ctx
, msg
, range_attr
, &num_new_strings
);
3405 if (*more_strings
&& ((*num_strings
+ num_new_strings
) != (range_end
+ 1))) {
3406 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) tells us we have %lu "
3407 "strings in this bunch, but we only got %lu - aborting range retrieval\n",
3408 range_attr
, (unsigned long int)range_end
- range_start
+ 1,
3409 (unsigned long int)num_new_strings
));
3410 ldap_memfree(range_attr
);
3411 *more_strings
= False
;
3415 strings
= talloc_realloc(mem_ctx
, current_strings
, char *,
3416 *num_strings
+ num_new_strings
);
3418 if (strings
== NULL
) {
3419 ldap_memfree(range_attr
);
3420 *more_strings
= False
;
3424 if (new_strings
&& num_new_strings
) {
3425 memcpy(&strings
[*num_strings
], new_strings
,
3426 sizeof(*new_strings
) * num_new_strings
);
3429 (*num_strings
) += num_new_strings
;
3431 if (*more_strings
) {
3432 *next_attribute
= talloc_asprintf(mem_ctx
,
3437 if (!*next_attribute
) {
3438 DEBUG(1, ("talloc_asprintf for next attribute failed!\n"));
3439 ldap_memfree(range_attr
);
3440 *more_strings
= False
;
3445 ldap_memfree(range_attr
);
3451 * pull a single uint32_t from a ADS result
3452 * @param ads connection to ads server
3453 * @param msg Results of search
3454 * @param field Attribute to retrieve
3455 * @param v Pointer to int to store result
3456 * @return boolean indicating success
3458 bool ads_pull_uint32(ADS_STRUCT
*ads
, LDAPMessage
*msg
, const char *field
,
3463 values
= ldap_get_values(ads
->ldap
.ld
, msg
, field
);
3467 ldap_value_free(values
);
3471 *v
= atoi(values
[0]);
3472 ldap_value_free(values
);
3477 * pull a single objectGUID from an ADS result
3478 * @param ads connection to ADS server
3479 * @param msg results of search
3480 * @param guid 37-byte area to receive text guid
3481 * @return boolean indicating success
3483 bool ads_pull_guid(ADS_STRUCT
*ads
, LDAPMessage
*msg
, struct GUID
*guid
)
3488 if (!smbldap_talloc_single_blob(talloc_tos(), ads
->ldap
.ld
, msg
, "objectGUID",
3493 status
= GUID_from_ndr_blob(&blob
, guid
);
3494 talloc_free(blob
.data
);
3495 return NT_STATUS_IS_OK(status
);
3500 * pull a single struct dom_sid from a ADS result
3501 * @param ads connection to ads server
3502 * @param msg Results of search
3503 * @param field Attribute to retrieve
3504 * @param sid Pointer to sid to store result
3505 * @return boolean indicating success
3507 bool ads_pull_sid(ADS_STRUCT
*ads
, LDAPMessage
*msg
, const char *field
,
3508 struct dom_sid
*sid
)
3510 return smbldap_pull_sid(ads
->ldap
.ld
, msg
, field
, sid
);
3514 * pull an array of struct dom_sids from a ADS result
3515 * @param ads connection to ads server
3516 * @param mem_ctx TALLOC_CTX for allocating sid array
3517 * @param msg Results of search
3518 * @param field Attribute to retrieve
3519 * @param sids pointer to sid array to allocate
3520 * @return the count of SIDs pulled
3522 int ads_pull_sids(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
,
3523 LDAPMessage
*msg
, const char *field
, struct dom_sid
**sids
)
3525 struct berval
**values
;
3528 values
= ldap_get_values_len(ads
->ldap
.ld
, msg
, field
);
3533 for (i
=0; values
[i
]; i
++)
3537 (*sids
) = talloc_array(mem_ctx
, struct dom_sid
, i
);
3539 ldap_value_free_len(values
);
3547 for (i
=0; values
[i
]; i
++) {
3549 ret
= sid_parse((const uint8_t *)values
[i
]->bv_val
,
3550 values
[i
]->bv_len
, &(*sids
)[count
]);
3552 struct dom_sid_buf buf
;
3553 DBG_DEBUG("pulling SID: %s\n",
3554 dom_sid_str_buf(&(*sids
)[count
], &buf
));
3559 ldap_value_free_len(values
);
3564 * pull a struct security_descriptor from a ADS result
3565 * @param ads connection to ads server
3566 * @param mem_ctx TALLOC_CTX for allocating sid array
3567 * @param msg Results of search
3568 * @param field Attribute to retrieve
3569 * @param sd Pointer to *struct security_descriptor to store result (talloc()ed)
3570 * @return boolean indicating success
3572 bool ads_pull_sd(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
,
3573 LDAPMessage
*msg
, const char *field
,
3574 struct security_descriptor
**sd
)
3576 struct berval
**values
;
3579 values
= ldap_get_values_len(ads
->ldap
.ld
, msg
, field
);
3581 if (!values
) return false;
3585 status
= unmarshall_sec_desc(mem_ctx
,
3586 (uint8_t *)values
[0]->bv_val
,
3587 values
[0]->bv_len
, sd
);
3588 if (!NT_STATUS_IS_OK(status
)) {
3589 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
3590 nt_errstr(status
)));
3595 ldap_value_free_len(values
);
3600 * in order to support usernames longer than 21 characters we need to
3601 * use both the sAMAccountName and the userPrincipalName attributes
3602 * It seems that not all users have the userPrincipalName attribute set
3604 * @param ads connection to ads server
3605 * @param mem_ctx TALLOC_CTX for allocating sid array
3606 * @param msg Results of search
3607 * @return the username
3609 char *ads_pull_username(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
,
3615 /* lookup_name() only works on the sAMAccountName to
3616 returning the username portion of userPrincipalName
3617 breaks winbindd_getpwnam() */
3619 ret
= ads_pull_string(ads
, mem_ctx
, msg
, "userPrincipalName");
3620 if (ret
&& (p
= strchr_m(ret
, '@'))) {
3625 return ads_pull_string(ads
, mem_ctx
, msg
, "sAMAccountName");
3630 * find the update serial number - this is the core of the ldap cache
3631 * @param ads connection to ads server
3632 * @param ads connection to ADS server
3633 * @param usn Pointer to retrieved update serial number
3634 * @return status of search
3636 ADS_STATUS
ads_USN(ADS_STRUCT
*ads
, uint32_t *usn
)
3638 const char *attrs
[] = {"highestCommittedUSN", NULL
};
3642 status
= ads_do_search_retry(ads
, "", LDAP_SCOPE_BASE
, "(objectclass=*)", attrs
, &res
);
3643 if (!ADS_ERR_OK(status
))
3646 if (ads_count_replies(ads
, res
) != 1) {
3647 ads_msgfree(ads
, res
);
3648 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED
);
3651 if (!ads_pull_uint32(ads
, res
, "highestCommittedUSN", usn
)) {
3652 ads_msgfree(ads
, res
);
3653 return ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE
);
3656 ads_msgfree(ads
, res
);
3660 /* parse a ADS timestring - typical string is
3661 '20020917091222.0Z0' which means 09:12.22 17th September
3663 static time_t ads_parse_time(const char *str
)
3669 if (sscanf(str
, "%4d%2d%2d%2d%2d%2d",
3670 &tm
.tm_year
, &tm
.tm_mon
, &tm
.tm_mday
,
3671 &tm
.tm_hour
, &tm
.tm_min
, &tm
.tm_sec
) != 6) {
3680 /********************************************************************
3681 ********************************************************************/
3683 ADS_STATUS
ads_current_time(ADS_STRUCT
*ads
)
3685 const char *attrs
[] = {"currentTime", NULL
};
3689 TALLOC_CTX
*tmp_ctx
= talloc_stackframe();
3690 ADS_STRUCT
*ads_s
= ads
;
3692 /* establish a new ldap tcp session if necessary */
3694 if ( !ads
->ldap
.ld
) {
3696 * ADS_STRUCT may be being reused after a
3697 * DC lookup, so ads->ldap.ss may already have a
3698 * good address. If not, re-initialize the passed-in
3699 * ADS_STRUCT with the given server.XXXX parameters.
3701 * Note that this doesn't depend on
3702 * ads->server.ldap_server != NULL,
3703 * as the case where ads->server.ldap_server==NULL and
3704 * ads->ldap.ss != zero_address is precisely the DC
3705 * lookup case where ads->ldap.ss was found by going
3706 * through ads_find_dc() again we want to avoid repeating.
3708 if (is_zero_addr(&ads
->ldap
.ss
)) {
3709 ads_s
= ads_init(tmp_ctx
,
3711 ads
->server
.workgroup
,
3712 ads
->server
.ldap_server
,
3714 if (ads_s
== NULL
) {
3715 status
= ADS_ERROR(LDAP_NO_MEMORY
);
3721 * Reset ads->config.flags as it can contain the flags
3722 * returned by the previous CLDAP ping when reusing the struct.
3724 ads_s
->config
.flags
= 0;
3726 status
= ads_connect_simple_anon(ads_s
);
3727 if ( !ADS_ERR_OK(status
))
3731 status
= ads_do_search(ads_s
, "", LDAP_SCOPE_BASE
, "(objectclass=*)", attrs
, &res
);
3732 if (!ADS_ERR_OK(status
)) {
3736 timestr
= ads_pull_string(ads_s
, tmp_ctx
, res
, "currentTime");
3738 ads_msgfree(ads_s
, res
);
3739 status
= ADS_ERROR(LDAP_NO_RESULTS_RETURNED
);
3743 /* but save the time and offset in the original ADS_STRUCT */
3745 ads
->config
.current_time
= ads_parse_time(timestr
);
3747 if (ads
->config
.current_time
!= 0) {
3748 ads
->config
.time_offset
= ads
->config
.current_time
- time(NULL
);
3749 DBG_INFO("server time offset is %d seconds\n",
3750 ads
->config
.time_offset
);
3752 ads
->config
.time_offset
= 0;
3755 DBG_INFO("server time offset is %d seconds\n",
3756 ads
->config
.time_offset
);
3758 ads_msgfree(ads
, res
);
3760 status
= ADS_SUCCESS
;
3763 TALLOC_FREE(tmp_ctx
);
3768 /********************************************************************
3769 ********************************************************************/
3771 ADS_STATUS
ads_domain_func_level(ADS_STRUCT
*ads
, uint32_t *val
)
3773 TALLOC_CTX
*tmp_ctx
= talloc_stackframe();
3774 const char *attrs
[] = {"domainFunctionality", NULL
};
3777 ADS_STRUCT
*ads_s
= ads
;
3779 *val
= DS_DOMAIN_FUNCTION_2000
;
3781 /* establish a new ldap tcp session if necessary */
3783 if ( !ads
->ldap
.ld
) {
3785 * ADS_STRUCT may be being reused after a
3786 * DC lookup, so ads->ldap.ss may already have a
3787 * good address. If not, re-initialize the passed-in
3788 * ADS_STRUCT with the given server.XXXX parameters.
3790 * Note that this doesn't depend on
3791 * ads->server.ldap_server != NULL,
3792 * as the case where ads->server.ldap_server==NULL and
3793 * ads->ldap.ss != zero_address is precisely the DC
3794 * lookup case where ads->ldap.ss was found by going
3795 * through ads_find_dc() again we want to avoid repeating.
3797 if (is_zero_addr(&ads
->ldap
.ss
)) {
3798 ads_s
= ads_init(tmp_ctx
,
3800 ads
->server
.workgroup
,
3801 ads
->server
.ldap_server
,
3803 if (ads_s
== NULL
) {
3804 status
= ADS_ERROR_NT(NT_STATUS_NO_MEMORY
);
3810 * Reset ads->config.flags as it can contain the flags
3811 * returned by the previous CLDAP ping when reusing the struct.
3813 ads_s
->config
.flags
= 0;
3815 status
= ads_connect_simple_anon(ads_s
);
3816 if ( !ADS_ERR_OK(status
))
3820 /* If the attribute does not exist assume it is a Windows 2000
3821 functional domain */
3823 status
= ads_do_search(ads_s
, "", LDAP_SCOPE_BASE
, "(objectclass=*)", attrs
, &res
);
3824 if (!ADS_ERR_OK(status
)) {
3825 if ( status
.err
.rc
== LDAP_NO_SUCH_ATTRIBUTE
) {
3826 status
= ADS_SUCCESS
;
3831 if ( !ads_pull_uint32(ads_s
, res
, "domainFunctionality", val
) ) {
3832 DEBUG(5,("ads_domain_func_level: Failed to pull the domainFunctionality attribute.\n"));
3834 DEBUG(3,("ads_domain_func_level: %d\n", *val
));
3837 ads_msgfree(ads_s
, res
);
3840 TALLOC_FREE(tmp_ctx
);
3846 * find the domain sid for our domain
3847 * @param ads connection to ads server
3848 * @param sid Pointer to domain sid
3849 * @return status of search
3851 ADS_STATUS
ads_domain_sid(ADS_STRUCT
*ads
, struct dom_sid
*sid
)
3853 const char *attrs
[] = {"objectSid", NULL
};
3857 rc
= ads_do_search_retry(ads
, ads
->config
.bind_path
, LDAP_SCOPE_BASE
, "(objectclass=*)",
3859 if (!ADS_ERR_OK(rc
)) return rc
;
3860 if (!ads_pull_sid(ads
, res
, "objectSid", sid
)) {
3861 ads_msgfree(ads
, res
);
3862 return ADS_ERROR_SYSTEM(ENOENT
);
3864 ads_msgfree(ads
, res
);
3870 * find our site name
3871 * @param ads connection to ads server
3872 * @param mem_ctx Pointer to talloc context
3873 * @param site_name Pointer to the sitename
3874 * @return status of search
3876 ADS_STATUS
ads_site_dn(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
, const char **site_name
)
3880 const char *dn
, *service_name
;
3881 const char *attrs
[] = { "dsServiceName", NULL
};
3883 status
= ads_do_search(ads
, "", LDAP_SCOPE_BASE
, "(objectclass=*)", attrs
, &res
);
3884 if (!ADS_ERR_OK(status
)) {
3888 service_name
= ads_pull_string(ads
, mem_ctx
, res
, "dsServiceName");
3889 if (service_name
== NULL
) {
3890 ads_msgfree(ads
, res
);
3891 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED
);
3894 ads_msgfree(ads
, res
);
3896 /* go up three levels */
3897 dn
= ads_parent_dn(ads_parent_dn(ads_parent_dn(service_name
)));
3899 return ADS_ERROR(LDAP_NO_MEMORY
);
3902 *site_name
= talloc_strdup(mem_ctx
, dn
);
3903 if (*site_name
== NULL
) {
3904 return ADS_ERROR(LDAP_NO_MEMORY
);
3909 dsServiceName: CN=NTDS Settings,CN=W2K3DC,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,DC=ber,DC=suse,DC=de
3914 * find the site dn where a machine resides
3915 * @param ads connection to ads server
3916 * @param mem_ctx Pointer to talloc context
3917 * @param computer_name name of the machine
3918 * @param site_name Pointer to the sitename
3919 * @return status of search
3921 ADS_STATUS
ads_site_dn_for_machine(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
, const char *computer_name
, const char **site_dn
)
3925 const char *parent
, *filter
;
3926 char *config_context
= NULL
;
3929 /* shortcut a query */
3930 if (strequal(computer_name
, ads
->config
.ldap_server_name
)) {
3931 return ads_site_dn(ads
, mem_ctx
, site_dn
);
3934 status
= ads_config_path(ads
, mem_ctx
, &config_context
);
3935 if (!ADS_ERR_OK(status
)) {
3939 filter
= talloc_asprintf(mem_ctx
, "(cn=%s)", computer_name
);
3940 if (filter
== NULL
) {
3941 return ADS_ERROR(LDAP_NO_MEMORY
);
3944 status
= ads_do_search(ads
, config_context
, LDAP_SCOPE_SUBTREE
,
3945 filter
, NULL
, &res
);
3946 if (!ADS_ERR_OK(status
)) {
3950 if (ads_count_replies(ads
, res
) != 1) {
3951 ads_msgfree(ads
, res
);
3952 return ADS_ERROR(LDAP_NO_SUCH_OBJECT
);
3955 dn
= ads_get_dn(ads
, mem_ctx
, res
);
3957 ads_msgfree(ads
, res
);
3958 return ADS_ERROR(LDAP_NO_MEMORY
);
3961 /* go up three levels */
3962 parent
= ads_parent_dn(ads_parent_dn(ads_parent_dn(dn
)));
3963 if (parent
== NULL
) {
3964 ads_msgfree(ads
, res
);
3966 return ADS_ERROR(LDAP_NO_MEMORY
);
3969 *site_dn
= talloc_strdup(mem_ctx
, parent
);
3970 if (*site_dn
== NULL
) {
3971 ads_msgfree(ads
, res
);
3973 return ADS_ERROR(LDAP_NO_MEMORY
);
3977 ads_msgfree(ads
, res
);
3983 * get the upn suffixes for a domain
3984 * @param ads connection to ads server
3985 * @param mem_ctx Pointer to talloc context
3986 * @param suffixes Pointer to an array of suffixes
3987 * @param num_suffixes Pointer to the number of suffixes
3988 * @return status of search
3990 ADS_STATUS
ads_upn_suffixes(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
, char ***suffixes
, size_t *num_suffixes
)
3995 char *config_context
= NULL
;
3996 const char *attrs
[] = { "uPNSuffixes", NULL
};
3998 status
= ads_config_path(ads
, mem_ctx
, &config_context
);
3999 if (!ADS_ERR_OK(status
)) {
4003 base
= talloc_asprintf(mem_ctx
, "cn=Partitions,%s", config_context
);
4005 return ADS_ERROR(LDAP_NO_MEMORY
);
4008 status
= ads_search_dn(ads
, &res
, base
, attrs
);
4009 if (!ADS_ERR_OK(status
)) {
4013 if (ads_count_replies(ads
, res
) != 1) {
4014 ads_msgfree(ads
, res
);
4015 return ADS_ERROR(LDAP_NO_SUCH_OBJECT
);
4018 (*suffixes
) = ads_pull_strings(ads
, mem_ctx
, res
, "uPNSuffixes", num_suffixes
);
4019 if ((*suffixes
) == NULL
) {
4020 ads_msgfree(ads
, res
);
4021 return ADS_ERROR(LDAP_NO_MEMORY
);
4024 ads_msgfree(ads
, res
);
4030 * get the joinable ous for a domain
4031 * @param ads connection to ads server
4032 * @param mem_ctx Pointer to talloc context
4033 * @param ous Pointer to an array of ous
4034 * @param num_ous Pointer to the number of ous
4035 * @return status of search
4037 ADS_STATUS
ads_get_joinable_ous(ADS_STRUCT
*ads
,
4038 TALLOC_CTX
*mem_ctx
,
4043 LDAPMessage
*res
= NULL
;
4044 LDAPMessage
*msg
= NULL
;
4045 const char *attrs
[] = { "dn", NULL
};
4048 status
= ads_search(ads
, &res
,
4049 "(|(objectClass=domain)(objectclass=organizationalUnit))",
4051 if (!ADS_ERR_OK(status
)) {
4055 count
= ads_count_replies(ads
, res
);
4057 ads_msgfree(ads
, res
);
4058 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED
);
4061 for (msg
= ads_first_entry(ads
, res
); msg
;
4062 msg
= ads_next_entry(ads
, msg
)) {
4063 const char **p
= discard_const_p(const char *, *ous
);
4066 dn
= ads_get_dn(ads
, talloc_tos(), msg
);
4068 ads_msgfree(ads
, res
);
4069 return ADS_ERROR(LDAP_NO_MEMORY
);
4072 if (!add_string_to_array(mem_ctx
, dn
, &p
, num_ous
)) {
4074 ads_msgfree(ads
, res
);
4075 return ADS_ERROR(LDAP_NO_MEMORY
);
4079 *ous
= discard_const_p(char *, p
);
4082 ads_msgfree(ads
, res
);
4089 * pull a struct dom_sid from an extended dn string
4090 * @param mem_ctx TALLOC_CTX
4091 * @param extended_dn string
4092 * @param flags string type of extended_dn
4093 * @param sid pointer to a struct dom_sid
4094 * @return NT_STATUS_OK on success,
4095 * NT_INVALID_PARAMETER on error,
4096 * NT_STATUS_NOT_FOUND if no SID present
4098 ADS_STATUS
ads_get_sid_from_extended_dn(TALLOC_CTX
*mem_ctx
,
4099 const char *extended_dn
,
4100 enum ads_extended_dn_flags flags
,
4101 struct dom_sid
*sid
)
4106 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER
);
4109 /* otherwise extended_dn gets stripped off */
4110 if ((dn
= talloc_strdup(mem_ctx
, extended_dn
)) == NULL
) {
4111 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER
);
4114 * ADS_EXTENDED_DN_HEX_STRING:
4115 * <GUID=238e1963cb390f4bb032ba0105525a29>;<SID=010500000000000515000000bb68c8fd6b61b427572eb04556040000>;CN=gd,OU=berlin,OU=suse,DC=ber,DC=suse,DC=de
4117 * ADS_EXTENDED_DN_STRING (only with w2k3):
4118 * <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
4120 * Object with no SID, such as an Exchange Public Folder
4121 * <GUID=28907fb4bdf6854993e7f0a10b504e7c>;CN=public,CN=Microsoft Exchange System Objects,DC=sd2k3ms,DC=west,DC=isilon,DC=com
4124 p
= strchr(dn
, ';');
4126 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER
);
4129 if (strncmp(p
, ";<SID=", strlen(";<SID=")) != 0) {
4130 DEBUG(5,("No SID present in extended dn\n"));
4131 return ADS_ERROR_NT(NT_STATUS_NOT_FOUND
);
4134 p
+= strlen(";<SID=");
4138 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER
);
4143 DEBUG(100,("ads_get_sid_from_extended_dn: sid string is %s\n", p
));
4147 case ADS_EXTENDED_DN_STRING
:
4148 if (!string_to_sid(sid
, p
)) {
4149 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER
);
4152 case ADS_EXTENDED_DN_HEX_STRING
: {
4157 buf_len
= strhex_to_str(buf
, sizeof(buf
), p
, strlen(p
));
4159 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER
);
4162 ret
= sid_parse((const uint8_t *)buf
, buf_len
, sid
);
4164 DEBUG(10,("failed to parse sid\n"));
4165 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER
);
4170 DEBUG(10,("unknown extended dn format\n"));
4171 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER
);
4174 return ADS_ERROR_NT(NT_STATUS_OK
);
4177 /********************************************************************
4178 ********************************************************************/
4180 char* ads_get_dnshostname( ADS_STRUCT
*ads
, TALLOC_CTX
*ctx
, const char *machine_name
)
4182 LDAPMessage
*res
= NULL
;
4187 status
= ads_find_machine_acct(ads
, &res
, machine_name
);
4188 if (!ADS_ERR_OK(status
)) {
4189 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
4190 lp_netbios_name()));
4194 if ( (count
= ads_count_replies(ads
, res
)) != 1 ) {
4195 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count
));
4199 if ( (name
= ads_pull_string(ads
, ctx
, res
, "dNSHostName")) == NULL
) {
4200 DEBUG(0,("ads_get_dnshostname: No dNSHostName attribute!\n"));
4204 ads_msgfree(ads
, res
);
4209 /********************************************************************
4210 ********************************************************************/
4212 static char **get_addl_hosts(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
,
4213 LDAPMessage
*msg
, size_t *num_values
)
4215 const char *field
= "msDS-AdditionalDnsHostName";
4216 struct berval
**values
= NULL
;
4218 size_t i
, converted_size
;
4221 * Windows DC implicitly adds a short name for each FQDN added to
4222 * msDS-AdditionalDnsHostName, but it comes with a strange binary
4223 * suffix "\0$" which we should ignore (see bug #14406).
4226 values
= ldap_get_values_len(ads
->ldap
.ld
, msg
, field
);
4227 if (values
== NULL
) {
4231 *num_values
= ldap_count_values_len(values
);
4233 ret
= talloc_array(mem_ctx
, char *, *num_values
+ 1);
4235 ldap_value_free_len(values
);
4239 for (i
= 0; i
< *num_values
; i
++) {
4241 if (!convert_string_talloc(mem_ctx
, CH_UTF8
, CH_UNIX
,
4243 strnlen(values
[i
]->bv_val
,
4245 &ret
[i
], &converted_size
)) {
4246 ldap_value_free_len(values
);
4252 ldap_value_free_len(values
);
4256 ADS_STATUS
ads_get_additional_dns_hostnames(TALLOC_CTX
*mem_ctx
,
4258 const char *machine_name
,
4259 char ***hostnames_array
,
4260 size_t *num_hostnames
)
4263 LDAPMessage
*res
= NULL
;
4266 status
= ads_find_machine_acct(ads
,
4269 if (!ADS_ERR_OK(status
)) {
4270 DEBUG(1,("Host Account for %s not found... skipping operation.\n",
4275 count
= ads_count_replies(ads
, res
);
4277 status
= ADS_ERROR(LDAP_NO_SUCH_OBJECT
);
4281 *hostnames_array
= get_addl_hosts(ads
, mem_ctx
, res
, num_hostnames
);
4282 if (*hostnames_array
== NULL
) {
4283 DEBUG(1, ("Host account for %s does not have msDS-AdditionalDnsHostName.\n",
4285 status
= ADS_ERROR(LDAP_NO_SUCH_OBJECT
);
4290 ads_msgfree(ads
, res
);
4295 /********************************************************************
4296 ********************************************************************/
4298 char* ads_get_upn( ADS_STRUCT
*ads
, TALLOC_CTX
*ctx
, const char *machine_name
)
4300 LDAPMessage
*res
= NULL
;
4305 status
= ads_find_machine_acct(ads
, &res
, machine_name
);
4306 if (!ADS_ERR_OK(status
)) {
4307 DEBUG(0,("ads_get_upn: Failed to find account for %s\n",
4308 lp_netbios_name()));
4312 if ( (count
= ads_count_replies(ads
, res
)) != 1 ) {
4313 DEBUG(1,("ads_get_upn: %d entries returned!\n", count
));
4317 if ( (name
= ads_pull_string(ads
, ctx
, res
, "userPrincipalName")) == NULL
) {
4318 DEBUG(2,("ads_get_upn: No userPrincipalName attribute!\n"));
4322 ads_msgfree(ads
, res
);
4327 /********************************************************************
4328 ********************************************************************/
4330 bool ads_has_samaccountname( ADS_STRUCT
*ads
, TALLOC_CTX
*ctx
, const char *machine_name
)
4332 LDAPMessage
*res
= NULL
;
4338 status
= ads_find_machine_acct(ads
, &res
, machine_name
);
4339 if (!ADS_ERR_OK(status
)) {
4340 DEBUG(0,("ads_has_samaccountname: Failed to find account for %s\n",
4341 lp_netbios_name()));
4345 if ( (count
= ads_count_replies(ads
, res
)) != 1 ) {
4346 DEBUG(1,("ads_has_samaccountname: %d entries returned!\n", count
));
4350 if ( (name
= ads_pull_string(ads
, ctx
, res
, "sAMAccountName")) == NULL
) {
4351 DEBUG(0,("ads_has_samaccountname: No sAMAccountName attribute!\n"));
4355 ads_msgfree(ads
, res
);
4357 ok
= (strlen(name
) > 0);
4365 SAVED CODE
- we used to join via ldap
- remember how we did
this. JRA
.
4368 * Join a machine to a realm
4369 * Creates the machine account and sets the machine password
4370 * @param ads connection to ads server
4371 * @param machine name of host to add
4372 * @param org_unit Organizational unit to place machine in
4373 * @return status of join
4375 ADS_STATUS
ads_join_realm(ADS_STRUCT
*ads
, const char *machine_name
,
4376 uint32_t account_type
, const char *org_unit
)
4379 LDAPMessage
*res
= NULL
;
4382 /* machine name must be lowercase */
4383 machine
= SMB_STRDUP(machine_name
);
4384 strlower_m(machine
);
4387 status = ads_find_machine_acct(ads, (void **)&res, machine);
4388 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
4389 DEBUG(0, ("Host account for %s already exists - deleting old account\n", machine));
4390 status = ads_leave_realm(ads, machine);
4391 if (!ADS_ERR_OK(status)) {
4392 DEBUG(0, ("Failed to delete host '%s' from the '%s' realm.\n",
4393 machine, ads->config.realm));
4398 status
= ads_add_machine_acct(ads
, machine
, account_type
, org_unit
);
4399 if (!ADS_ERR_OK(status
)) {
4400 DEBUG(0, ("ads_join_realm: ads_add_machine_acct failed (%s): %s\n", machine
, ads_errstr(status
)));
4405 status
= ads_find_machine_acct(ads
, (void **)(void *)&res
, machine
);
4406 if (!ADS_ERR_OK(status
)) {
4407 DEBUG(0, ("ads_join_realm: Host account test failed for machine %s\n", machine
));
4413 ads_msgfree(ads
, res
);
4420 * Delete a machine from the realm
4421 * @param ads connection to ads server
4422 * @param hostname Machine to remove
4423 * @return status of delete
4425 ADS_STATUS
ads_leave_realm(ADS_STRUCT
*ads
, const char *hostname
)
4430 char *hostnameDN
, *host
;
4432 LDAPControl ldap_control
;
4433 LDAPControl
* pldap_control
[2] = {NULL
, NULL
};
4435 pldap_control
[0] = &ldap_control
;
4436 memset(&ldap_control
, 0, sizeof(LDAPControl
));
4437 ldap_control
.ldctl_oid
= discard_const_p(char, LDAP_SERVER_TREE_DELETE_OID
);
4439 /* hostname must be lowercase */
4440 host
= SMB_STRDUP(hostname
);
4441 if (!strlower_m(host
)) {
4443 return ADS_ERROR_SYSTEM(EINVAL
);
4446 status
= ads_find_machine_acct(ads
, &res
, host
);
4447 if (!ADS_ERR_OK(status
)) {
4448 DEBUG(0, ("Host account for %s does not exist.\n", host
));
4453 msg
= ads_first_entry(ads
, res
);
4456 return ADS_ERROR_SYSTEM(ENOENT
);
4459 hostnameDN
= ads_get_dn(ads
, talloc_tos(), (LDAPMessage
*)msg
);
4460 if (hostnameDN
== NULL
) {
4462 return ADS_ERROR_SYSTEM(ENOENT
);
4465 rc
= ldap_delete_ext_s(ads
->ldap
.ld
, hostnameDN
, pldap_control
, NULL
);
4467 DEBUG(3,("ldap_delete_ext_s failed with error code %d\n", rc
));
4469 DEBUG(3,("ldap_delete_ext_s succeeded with error code %d\n", rc
));
4472 if (rc
!= LDAP_SUCCESS
) {
4473 const char *attrs
[] = { "cn", NULL
};
4474 LDAPMessage
*msg_sub
;
4476 /* we only search with scope ONE, we do not expect any further
4477 * objects to be created deeper */
4479 status
= ads_do_search_retry(ads
, hostnameDN
,
4480 LDAP_SCOPE_ONELEVEL
,
4481 "(objectclass=*)", attrs
, &res
);
4483 if (!ADS_ERR_OK(status
)) {
4485 TALLOC_FREE(hostnameDN
);
4489 for (msg_sub
= ads_first_entry(ads
, res
); msg_sub
;
4490 msg_sub
= ads_next_entry(ads
, msg_sub
)) {
4494 if ((dn
= ads_get_dn(ads
, talloc_tos(), msg_sub
)) == NULL
) {
4496 TALLOC_FREE(hostnameDN
);
4497 return ADS_ERROR(LDAP_NO_MEMORY
);
4500 status
= ads_del_dn(ads
, dn
);
4501 if (!ADS_ERR_OK(status
)) {
4502 DEBUG(3,("failed to delete dn %s: %s\n", dn
, ads_errstr(status
)));
4505 TALLOC_FREE(hostnameDN
);
4512 /* there should be no subordinate objects anymore */
4513 status
= ads_do_search_retry(ads
, hostnameDN
,
4514 LDAP_SCOPE_ONELEVEL
,
4515 "(objectclass=*)", attrs
, &res
);
4517 if (!ADS_ERR_OK(status
) || ( (ads_count_replies(ads
, res
)) > 0 ) ) {
4519 TALLOC_FREE(hostnameDN
);
4523 /* delete hostnameDN now */
4524 status
= ads_del_dn(ads
, hostnameDN
);
4525 if (!ADS_ERR_OK(status
)) {
4527 DEBUG(3,("failed to delete dn %s: %s\n", hostnameDN
, ads_errstr(status
)));
4528 TALLOC_FREE(hostnameDN
);
4533 TALLOC_FREE(hostnameDN
);
4535 status
= ads_find_machine_acct(ads
, &res
, host
);
4536 if ((status
.error_type
== ENUM_ADS_ERROR_LDAP
) &&
4537 (status
.err
.rc
!= LDAP_NO_SUCH_OBJECT
)) {
4538 DEBUG(3, ("Failed to remove host account.\n"));
4548 * pull all token-sids from an LDAP dn
4549 * @param ads connection to ads server
4550 * @param mem_ctx TALLOC_CTX for allocating sid array
4551 * @param dn of LDAP object
4552 * @param user_sid pointer to struct dom_sid (objectSid)
4553 * @param primary_group_sid pointer to struct dom_sid (self composed)
4554 * @param sids pointer to sid array to allocate
4555 * @param num_sids counter of SIDs pulled
4556 * @return status of token query
4558 ADS_STATUS
ads_get_tokensids(ADS_STRUCT
*ads
,
4559 TALLOC_CTX
*mem_ctx
,
4561 struct dom_sid
*user_sid
,
4562 struct dom_sid
*primary_group_sid
,
4563 struct dom_sid
**sids
,
4567 LDAPMessage
*res
= NULL
;
4569 size_t tmp_num_sids
;
4570 struct dom_sid
*tmp_sids
;
4571 struct dom_sid tmp_user_sid
;
4572 struct dom_sid tmp_primary_group_sid
;
4574 const char *attrs
[] = {
4581 status
= ads_search_retry_dn(ads
, &res
, dn
, attrs
);
4582 if (!ADS_ERR_OK(status
)) {
4586 count
= ads_count_replies(ads
, res
);
4588 ads_msgfree(ads
, res
);
4589 return ADS_ERROR_LDAP(LDAP_NO_SUCH_OBJECT
);
4592 if (!ads_pull_sid(ads
, res
, "objectSid", &tmp_user_sid
)) {
4593 ads_msgfree(ads
, res
);
4594 return ADS_ERROR_LDAP(LDAP_NO_MEMORY
);
4597 if (!ads_pull_uint32(ads
, res
, "primaryGroupID", &pgid
)) {
4598 ads_msgfree(ads
, res
);
4599 return ADS_ERROR_LDAP(LDAP_NO_MEMORY
);
4603 /* hack to compose the primary group sid without knowing the
4606 struct dom_sid domsid
;
4608 sid_copy(&domsid
, &tmp_user_sid
);
4610 if (!sid_split_rid(&domsid
, NULL
)) {
4611 ads_msgfree(ads
, res
);
4612 return ADS_ERROR_LDAP(LDAP_NO_MEMORY
);
4615 if (!sid_compose(&tmp_primary_group_sid
, &domsid
, pgid
)) {
4616 ads_msgfree(ads
, res
);
4617 return ADS_ERROR_LDAP(LDAP_NO_MEMORY
);
4621 tmp_num_sids
= ads_pull_sids(ads
, mem_ctx
, res
, "tokenGroups", &tmp_sids
);
4623 if (tmp_num_sids
== 0 || !tmp_sids
) {
4624 ads_msgfree(ads
, res
);
4625 return ADS_ERROR_LDAP(LDAP_NO_MEMORY
);
4629 *num_sids
= tmp_num_sids
;
4637 *user_sid
= tmp_user_sid
;
4640 if (primary_group_sid
) {
4641 *primary_group_sid
= tmp_primary_group_sid
;
4644 DEBUG(10,("ads_get_tokensids: returned %d sids\n", (int)tmp_num_sids
+ 2));
4646 ads_msgfree(ads
, res
);
4647 return ADS_ERROR_LDAP(LDAP_SUCCESS
);
4651 * Find a sAMAccountName in LDAP
4652 * @param ads connection to ads server
4653 * @param mem_ctx TALLOC_CTX for allocating sid array
4654 * @param samaccountname to search
4655 * @param uac_ret uint32_t pointer userAccountControl attribute value
4656 * @param dn_ret pointer to dn
4657 * @return status of token query
4659 ADS_STATUS
ads_find_samaccount(ADS_STRUCT
*ads
,
4660 TALLOC_CTX
*mem_ctx
,
4661 const char *samaccountname
,
4663 const char **dn_ret
)
4666 const char *attrs
[] = { "userAccountControl", NULL
};
4668 LDAPMessage
*res
= NULL
;
4672 filter
= talloc_asprintf(mem_ctx
, "(&(objectclass=user)(sAMAccountName=%s))",
4674 if (filter
== NULL
) {
4675 status
= ADS_ERROR_NT(NT_STATUS_NO_MEMORY
);
4679 status
= ads_do_search_all(ads
, ads
->config
.bind_path
,
4681 filter
, attrs
, &res
);
4683 if (!ADS_ERR_OK(status
)) {
4687 if (ads_count_replies(ads
, res
) != 1) {
4688 status
= ADS_ERROR(LDAP_NO_RESULTS_RETURNED
);
4692 dn
= ads_get_dn(ads
, talloc_tos(), res
);
4694 status
= ADS_ERROR(LDAP_NO_MEMORY
);
4698 if (!ads_pull_uint32(ads
, res
, "userAccountControl", &uac
)) {
4699 status
= ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE
);
4708 *dn_ret
= talloc_strdup(mem_ctx
, dn
);
4710 status
= ADS_ERROR(LDAP_NO_MEMORY
);
4716 ads_msgfree(ads
, res
);
4722 * find our configuration path
4723 * @param ads connection to ads server
4724 * @param mem_ctx Pointer to talloc context
4725 * @param config_path Pointer to the config path
4726 * @return status of search
4728 ADS_STATUS
ads_config_path(ADS_STRUCT
*ads
,
4729 TALLOC_CTX
*mem_ctx
,
4733 LDAPMessage
*res
= NULL
;
4734 const char *config_context
= NULL
;
4735 const char *attrs
[] = { "configurationNamingContext", NULL
};
4737 status
= ads_do_search(ads
, "", LDAP_SCOPE_BASE
,
4738 "(objectclass=*)", attrs
, &res
);
4739 if (!ADS_ERR_OK(status
)) {
4743 config_context
= ads_pull_string(ads
, mem_ctx
, res
,
4744 "configurationNamingContext");
4745 ads_msgfree(ads
, res
);
4746 if (!config_context
) {
4747 return ADS_ERROR(LDAP_NO_MEMORY
);
4751 *config_path
= talloc_strdup(mem_ctx
, config_context
);
4752 if (!*config_path
) {
4753 return ADS_ERROR(LDAP_NO_MEMORY
);
4757 return ADS_ERROR(LDAP_SUCCESS
);
4761 * find the displayName of an extended right
4762 * @param ads connection to ads server
4763 * @param config_path The config path
4764 * @param mem_ctx Pointer to talloc context
4765 * @param GUID struct of the rightsGUID
4766 * @return status of search
4768 const char *ads_get_extended_right_name_by_guid(ADS_STRUCT
*ads
,
4769 const char *config_path
,
4770 TALLOC_CTX
*mem_ctx
,
4771 const struct GUID
*rights_guid
)
4774 LDAPMessage
*res
= NULL
;
4776 const char *attrs
[] = { "displayName", NULL
};
4777 const char *result
= NULL
;
4780 if (!ads
|| !mem_ctx
|| !rights_guid
) {
4784 expr
= talloc_asprintf(mem_ctx
, "(rightsGuid=%s)",
4785 GUID_string(mem_ctx
, rights_guid
));
4790 path
= talloc_asprintf(mem_ctx
, "cn=Extended-Rights,%s", config_path
);
4795 rc
= ads_do_search_retry(ads
, path
, LDAP_SCOPE_SUBTREE
,
4797 if (!ADS_ERR_OK(rc
)) {
4801 if (ads_count_replies(ads
, res
) != 1) {
4805 result
= ads_pull_string(ads
, mem_ctx
, res
, "displayName");
4808 ads_msgfree(ads
, res
);
4813 * verify or build and verify an account ou
4814 * @param mem_ctx Pointer to talloc context
4815 * @param ads connection to ads server
4817 * @return status of search
4820 ADS_STATUS
ads_check_ou_dn(TALLOC_CTX
*mem_ctx
,
4822 const char **account_ou
)
4828 if (account_ou
== NULL
) {
4829 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER
);
4832 if (*account_ou
!= NULL
) {
4833 exploded_dn
= ldap_explode_dn(*account_ou
, 0);
4835 ldap_value_free(exploded_dn
);
4840 ou_string
= ads_ou_string(ads
, *account_ou
);
4842 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX
);
4845 name
= talloc_asprintf(mem_ctx
, "%s,%s", ou_string
,
4846 ads
->config
.bind_path
);
4847 SAFE_FREE(ou_string
);
4850 return ADS_ERROR_LDAP(LDAP_NO_MEMORY
);
4853 exploded_dn
= ldap_explode_dn(name
, 0);
4855 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX
);
4857 ldap_value_free(exploded_dn
);