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-KeyVersionNumber",
1867 "msDS-AdditionalDnsHostName",
1868 "msDS-SupportedEncryptionTypes",
1869 "nTSecurityDescriptor",
1874 TALLOC_CTX
*frame
= talloc_stackframe();
1878 /* the easiest way to find a machine account anywhere in the tree
1879 is to look for hostname$ */
1880 expr
= talloc_asprintf(frame
, "(samAccountName=%s$)", machine
);
1882 status
= ADS_ERROR_NT(NT_STATUS_NO_MEMORY
);
1886 status
= ads_search(ads
, res
, expr
, attrs
);
1887 if (ADS_ERR_OK(status
)) {
1888 if (ads_count_replies(ads
, *res
) != 1) {
1889 status
= ADS_ERROR_LDAP(LDAP_NO_SUCH_OBJECT
);
1899 * Initialize a list of mods to be used in a modify request
1900 * @param ctx An initialized TALLOC_CTX
1901 * @return allocated ADS_MODLIST
1903 ADS_MODLIST
ads_init_mods(TALLOC_CTX
*ctx
)
1905 #define ADS_MODLIST_ALLOC_SIZE 10
1908 if ((mods
= talloc_zero_array(ctx
, LDAPMod
*, ADS_MODLIST_ALLOC_SIZE
+ 1)))
1909 /* -1 is safety to make sure we don't go over the end.
1910 need to reset it to NULL before doing ldap modify */
1911 mods
[ADS_MODLIST_ALLOC_SIZE
] = (LDAPMod
*) -1;
1913 return (ADS_MODLIST
)mods
;
1918 add an attribute to the list, with values list already constructed
1920 static ADS_STATUS
ads_modlist_add(TALLOC_CTX
*ctx
, ADS_MODLIST
*mods
,
1921 int mod_op
, const char *name
,
1922 const void *_invals
)
1925 LDAPMod
**modlist
= (LDAPMod
**) *mods
;
1926 struct berval
**ber_values
= NULL
;
1927 char **char_values
= NULL
;
1930 mod_op
= LDAP_MOD_DELETE
;
1932 if (mod_op
& LDAP_MOD_BVALUES
) {
1933 const struct berval
**b
;
1934 b
= discard_const_p(const struct berval
*, _invals
);
1935 ber_values
= ads_dup_values(ctx
, b
);
1938 c
= discard_const_p(const char *, _invals
);
1939 char_values
= ads_push_strvals(ctx
, c
);
1943 /* find the first empty slot */
1944 for (curmod
=0; modlist
[curmod
] && modlist
[curmod
] != (LDAPMod
*) -1;
1946 if (modlist
[curmod
] == (LDAPMod
*) -1) {
1947 if (!(modlist
= talloc_realloc(ctx
, modlist
, LDAPMod
*,
1948 curmod
+ADS_MODLIST_ALLOC_SIZE
+1)))
1949 return ADS_ERROR(LDAP_NO_MEMORY
);
1950 memset(&modlist
[curmod
], 0,
1951 ADS_MODLIST_ALLOC_SIZE
*sizeof(LDAPMod
*));
1952 modlist
[curmod
+ADS_MODLIST_ALLOC_SIZE
] = (LDAPMod
*) -1;
1953 *mods
= (ADS_MODLIST
)modlist
;
1956 if (!(modlist
[curmod
] = talloc_zero(ctx
, LDAPMod
)))
1957 return ADS_ERROR(LDAP_NO_MEMORY
);
1958 modlist
[curmod
]->mod_type
= talloc_strdup(ctx
, name
);
1959 if (mod_op
& LDAP_MOD_BVALUES
) {
1960 modlist
[curmod
]->mod_bvalues
= ber_values
;
1961 } else if (mod_op
& LDAP_MOD_DELETE
) {
1962 modlist
[curmod
]->mod_values
= NULL
;
1964 modlist
[curmod
]->mod_values
= char_values
;
1967 modlist
[curmod
]->mod_op
= mod_op
;
1968 return ADS_ERROR(LDAP_SUCCESS
);
1972 * Add a single string value to a mod list
1973 * @param ctx An initialized TALLOC_CTX
1974 * @param mods An initialized ADS_MODLIST
1975 * @param name The attribute name to add
1976 * @param val The value to add - NULL means DELETE
1977 * @return ADS STATUS indicating success of add
1979 ADS_STATUS
ads_mod_str(TALLOC_CTX
*ctx
, ADS_MODLIST
*mods
,
1980 const char *name
, const char *val
)
1982 const char *values
[2];
1988 return ads_modlist_add(ctx
, mods
, LDAP_MOD_DELETE
, name
, NULL
);
1989 return ads_modlist_add(ctx
, mods
, LDAP_MOD_REPLACE
, name
, values
);
1993 * Add an array of string values to a mod list
1994 * @param ctx An initialized TALLOC_CTX
1995 * @param mods An initialized ADS_MODLIST
1996 * @param name The attribute name to add
1997 * @param vals The array of string values to add - NULL means DELETE
1998 * @return ADS STATUS indicating success of add
2000 ADS_STATUS
ads_mod_strlist(TALLOC_CTX
*ctx
, ADS_MODLIST
*mods
,
2001 const char *name
, const char **vals
)
2004 return ads_modlist_add(ctx
, mods
, LDAP_MOD_DELETE
, name
, NULL
);
2005 return ads_modlist_add(ctx
, mods
, LDAP_MOD_REPLACE
,
2006 name
, (const void **) vals
);
2010 * Add a single ber-encoded value to a mod list
2011 * @param ctx An initialized TALLOC_CTX
2012 * @param mods An initialized ADS_MODLIST
2013 * @param name The attribute name to add
2014 * @param val The value to add - NULL means DELETE
2015 * @return ADS STATUS indicating success of add
2017 static ADS_STATUS
ads_mod_ber(TALLOC_CTX
*ctx
, ADS_MODLIST
*mods
,
2018 const char *name
, const struct berval
*val
)
2020 const struct berval
*values
[2];
2025 return ads_modlist_add(ctx
, mods
, LDAP_MOD_DELETE
, name
, NULL
);
2026 return ads_modlist_add(ctx
, mods
, LDAP_MOD_REPLACE
|LDAP_MOD_BVALUES
,
2027 name
, (const void *) values
);
2030 static void ads_print_error(int ret
, LDAP
*ld
)
2033 char *ld_error
= NULL
;
2034 ldap_get_option(ld
, LDAP_OPT_ERROR_STRING
, &ld_error
);
2035 DBG_ERR("AD LDAP ERROR: %d (%s): %s\n",
2037 ldap_err2string(ret
),
2039 SAFE_FREE(ld_error
);
2044 * Perform an ldap modify
2045 * @param ads connection to ads server
2046 * @param mod_dn DistinguishedName to modify
2047 * @param mods list of modifications to perform
2048 * @return status of modify
2050 ADS_STATUS
ads_gen_mod(ADS_STRUCT
*ads
, const char *mod_dn
, ADS_MODLIST mods
)
2053 char *utf8_dn
= NULL
;
2054 size_t converted_size
;
2056 this control is needed to modify that contains a currently
2057 non-existent attribute (but allowable for the object) to run
2059 LDAPControl PermitModify
= {
2060 discard_const_p(char, ADS_PERMIT_MODIFY_OID
),
2063 LDAPControl
*controls
[2];
2065 DBG_INFO("AD LDAP: Modifying %s\n", mod_dn
);
2067 controls
[0] = &PermitModify
;
2070 if (!push_utf8_talloc(talloc_tos(), &utf8_dn
, mod_dn
, &converted_size
)) {
2071 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY
);
2074 /* find the end of the list, marked by NULL or -1 */
2075 for(i
=0;(mods
[i
]!=0)&&(mods
[i
]!=(LDAPMod
*) -1);i
++);
2076 /* make sure the end of the list is NULL */
2078 ret
= ldap_modify_ext_s(ads
->ldap
.ld
, utf8_dn
,
2079 (LDAPMod
**) mods
, controls
, NULL
);
2080 ads_print_error(ret
, ads
->ldap
.ld
);
2081 TALLOC_FREE(utf8_dn
);
2082 return ADS_ERROR(ret
);
2086 * Perform an ldap add
2087 * @param ads connection to ads server
2088 * @param new_dn DistinguishedName to add
2089 * @param mods list of attributes and values for DN
2090 * @return status of add
2092 ADS_STATUS
ads_gen_add(ADS_STRUCT
*ads
, const char *new_dn
, ADS_MODLIST mods
)
2095 char *utf8_dn
= NULL
;
2096 size_t converted_size
;
2098 DBG_INFO("AD LDAP: Adding %s\n", new_dn
);
2100 if (!push_utf8_talloc(talloc_tos(), &utf8_dn
, new_dn
, &converted_size
)) {
2101 DEBUG(1, ("ads_gen_add: push_utf8_talloc failed!\n"));
2102 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY
);
2105 /* find the end of the list, marked by NULL or -1 */
2106 for(i
=0;(mods
[i
]!=0)&&(mods
[i
]!=(LDAPMod
*) -1);i
++);
2107 /* make sure the end of the list is NULL */
2110 ret
= ldap_add_ext_s(ads
->ldap
.ld
, utf8_dn
, (LDAPMod
**)mods
, NULL
, NULL
);
2111 ads_print_error(ret
, ads
->ldap
.ld
);
2112 TALLOC_FREE(utf8_dn
);
2113 return ADS_ERROR(ret
);
2117 * Delete a DistinguishedName
2118 * @param ads connection to ads server
2119 * @param new_dn DistinguishedName to delete
2120 * @return status of delete
2122 ADS_STATUS
ads_del_dn(ADS_STRUCT
*ads
, char *del_dn
)
2125 char *utf8_dn
= NULL
;
2126 size_t converted_size
;
2127 if (!push_utf8_talloc(talloc_tos(), &utf8_dn
, del_dn
, &converted_size
)) {
2128 DEBUG(1, ("ads_del_dn: push_utf8_talloc failed!\n"));
2129 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY
);
2132 DBG_INFO("AD LDAP: Deleting %s\n", del_dn
);
2134 ret
= ldap_delete_s(ads
->ldap
.ld
, utf8_dn
);
2135 ads_print_error(ret
, ads
->ldap
.ld
);
2136 TALLOC_FREE(utf8_dn
);
2137 return ADS_ERROR(ret
);
2141 * Build an org unit string
2142 * if org unit is Computers or blank then assume a container, otherwise
2143 * assume a / separated list of organisational units.
2144 * jmcd: '\' is now used for escapes so certain chars can be in the ou (e.g. #)
2145 * @param ads connection to ads server
2146 * @param org_unit Organizational unit
2147 * @return org unit string - caller must free
2149 char *ads_ou_string(ADS_STRUCT
*ads
, const char *org_unit
)
2155 if (!org_unit
|| !*org_unit
) {
2157 ret
= ads_default_ou_string(ads
, DS_GUID_COMPUTERS_CONTAINER
);
2159 /* samba4 might not yet respond to a wellknownobject-query */
2160 return ret
? ret
: SMB_STRDUP("cn=Computers");
2163 if (strequal(org_unit
, "Computers")) {
2164 return SMB_STRDUP("cn=Computers");
2167 /* jmcd: removed "\\" from the separation chars, because it is
2168 needed as an escape for chars like '#' which are valid in an
2170 status
= ads_build_path(org_unit
, "/", "ou=", 1, &dn
);
2171 if (!ADS_ERR_OK(status
)) {
2179 * Get a org unit string for a well-known GUID
2180 * @param ads connection to ads server
2181 * @param wknguid Well known GUID
2182 * @return org unit string - caller must free
2184 char *ads_default_ou_string(ADS_STRUCT
*ads
, const char *wknguid
)
2187 LDAPMessage
*res
= NULL
;
2188 char *base
, *wkn_dn
= NULL
, *ret
= NULL
, **wkn_dn_exp
= NULL
,
2189 **bind_dn_exp
= NULL
;
2190 const char *attrs
[] = {"distinguishedName", NULL
};
2191 int new_ln
, wkn_ln
, bind_ln
, i
;
2193 if (wknguid
== NULL
) {
2197 if (asprintf(&base
, "<WKGUID=%s,%s>", wknguid
, ads
->config
.bind_path
) == -1) {
2198 DEBUG(1, ("asprintf failed!\n"));
2202 status
= ads_search_dn(ads
, &res
, base
, attrs
);
2203 if (!ADS_ERR_OK(status
)) {
2204 DEBUG(1,("Failed while searching for: %s\n", base
));
2208 if (ads_count_replies(ads
, res
) != 1) {
2212 /* substitute the bind-path from the well-known-guid-search result */
2213 wkn_dn
= ads_get_dn(ads
, talloc_tos(), res
);
2218 wkn_dn_exp
= ldap_explode_dn(wkn_dn
, 0);
2223 bind_dn_exp
= ldap_explode_dn(ads
->config
.bind_path
, 0);
2228 for (wkn_ln
=0; wkn_dn_exp
[wkn_ln
]; wkn_ln
++)
2230 for (bind_ln
=0; bind_dn_exp
[bind_ln
]; bind_ln
++)
2233 new_ln
= wkn_ln
- bind_ln
;
2235 ret
= SMB_STRDUP(wkn_dn_exp
[0]);
2240 for (i
=1; i
< new_ln
; i
++) {
2243 if (asprintf(&s
, "%s,%s", ret
, wkn_dn_exp
[i
]) == -1) {
2249 ret
= SMB_STRDUP(s
);
2258 ads_msgfree(ads
, res
);
2259 TALLOC_FREE(wkn_dn
);
2261 ldap_value_free(wkn_dn_exp
);
2264 ldap_value_free(bind_dn_exp
);
2271 * Adds (appends) an item to an attribute array, rather then
2272 * replacing the whole list
2273 * @param ctx An initialized TALLOC_CTX
2274 * @param mods An initialized ADS_MODLIST
2275 * @param name name of the ldap attribute to append to
2276 * @param vals an array of values to add
2277 * @return status of addition
2280 ADS_STATUS
ads_add_strlist(TALLOC_CTX
*ctx
, ADS_MODLIST
*mods
,
2281 const char *name
, const char **vals
)
2283 return ads_modlist_add(ctx
, mods
, LDAP_MOD_ADD
, name
,
2284 (const void *) vals
);
2288 * This clears out all registered spn's for a given hostname
2289 * @param ads An initialized ADS_STRUCT
2290 * @param machine_name the NetBIOS name of the computer.
2291 * @return 0 upon success, non-zero otherwise.
2294 ADS_STATUS
ads_clear_service_principal_names(ADS_STRUCT
*ads
, const char *machine_name
)
2297 LDAPMessage
*res
= NULL
;
2299 const char *servicePrincipalName
[1] = {NULL
};
2301 char *dn_string
= NULL
;
2303 ret
= ads_find_machine_acct(ads
, &res
, machine_name
);
2304 if (!ADS_ERR_OK(ret
)) {
2305 DEBUG(5,("ads_clear_service_principal_names: WARNING: Host Account for %s not found... skipping operation.\n", machine_name
));
2306 DEBUG(5,("ads_clear_service_principal_names: WARNING: Service Principals for %s have NOT been cleared.\n", machine_name
));
2307 ads_msgfree(ads
, res
);
2311 DEBUG(5,("ads_clear_service_principal_names: Host account for %s found\n", machine_name
));
2312 ctx
= talloc_init("ads_clear_service_principal_names");
2314 ads_msgfree(ads
, res
);
2315 return ADS_ERROR(LDAP_NO_MEMORY
);
2318 if (!(mods
= ads_init_mods(ctx
))) {
2319 talloc_destroy(ctx
);
2320 ads_msgfree(ads
, res
);
2321 return ADS_ERROR(LDAP_NO_MEMORY
);
2323 ret
= ads_mod_strlist(ctx
, &mods
, "servicePrincipalName", servicePrincipalName
);
2324 if (!ADS_ERR_OK(ret
)) {
2325 DEBUG(1,("ads_clear_service_principal_names: Error creating strlist.\n"));
2326 ads_msgfree(ads
, res
);
2327 talloc_destroy(ctx
);
2330 dn_string
= ads_get_dn(ads
, talloc_tos(), res
);
2332 talloc_destroy(ctx
);
2333 ads_msgfree(ads
, res
);
2334 return ADS_ERROR(LDAP_NO_MEMORY
);
2336 ret
= ads_gen_mod(ads
, dn_string
, mods
);
2337 TALLOC_FREE(dn_string
);
2338 if (!ADS_ERR_OK(ret
)) {
2339 DEBUG(1,("ads_clear_service_principal_names: Error: Updating Service Principals for machine %s in LDAP\n",
2341 ads_msgfree(ads
, res
);
2342 talloc_destroy(ctx
);
2346 ads_msgfree(ads
, res
);
2347 talloc_destroy(ctx
);
2352 * @brief Search for an element in a string array.
2354 * @param[in] el_array The string array to search.
2356 * @param[in] num_el The number of elements in the string array.
2358 * @param[in] el The string to search.
2360 * @return True if found, false if not.
2362 bool ads_element_in_array(const char **el_array
, size_t num_el
, const char *el
)
2366 if (el_array
== NULL
|| num_el
== 0 || el
== NULL
) {
2370 for (i
= 0; i
< num_el
&& el_array
[i
] != NULL
; i
++) {
2373 cmp
= strcasecmp_m(el_array
[i
], el
);
2383 * @brief This gets the service principal names of an existing computer account.
2385 * @param[in] mem_ctx The memory context to use to allocate the spn array.
2387 * @param[in] ads The ADS context to use.
2389 * @param[in] machine_name The NetBIOS name of the computer, which is used to
2390 * identify the computer account.
2392 * @param[in] spn_array A pointer to store the array for SPNs.
2394 * @param[in] num_spns The number of principals stored in the array.
2396 * @return 0 on success, or a ADS error if a failure occurred.
2398 ADS_STATUS
ads_get_service_principal_names(TALLOC_CTX
*mem_ctx
,
2400 const char *machine_name
,
2405 LDAPMessage
*res
= NULL
;
2408 status
= ads_find_machine_acct(ads
,
2411 if (!ADS_ERR_OK(status
)) {
2412 DEBUG(1,("Host Account for %s not found... skipping operation.\n",
2417 count
= ads_count_replies(ads
, res
);
2419 status
= ADS_ERROR(LDAP_NO_SUCH_OBJECT
);
2423 *spn_array
= ads_pull_strings(ads
,
2426 "servicePrincipalName",
2428 if (*spn_array
== NULL
) {
2429 DEBUG(1, ("Host account for %s does not have service principal "
2432 status
= ADS_ERROR(LDAP_NO_SUCH_OBJECT
);
2437 ads_msgfree(ads
, res
);
2443 * This adds a service principal name to an existing computer account
2444 * (found by hostname) in AD.
2445 * @param ads An initialized ADS_STRUCT
2446 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
2447 * @param spns An array or strings for the service principals to add,
2448 * i.e. 'cifs/machine_name', 'http/machine.full.domain.com' etc.
2449 * @return 0 upon success, or non-zero if a failure occurs
2452 ADS_STATUS
ads_add_service_principal_names(ADS_STRUCT
*ads
,
2453 const char *machine_name
,
2458 LDAPMessage
*res
= NULL
;
2460 char *dn_string
= NULL
;
2461 const char **servicePrincipalName
= spns
;
2463 ret
= ads_find_machine_acct(ads
, &res
, machine_name
);
2464 if (!ADS_ERR_OK(ret
)) {
2465 DEBUG(1,("ads_add_service_principal_name: WARNING: Host Account for %s not found... skipping operation.\n",
2467 DEBUG(1,("ads_add_service_principal_name: WARNING: Service Principals have NOT been added.\n"));
2468 ads_msgfree(ads
, res
);
2472 DEBUG(1,("ads_add_service_principal_name: Host account for %s found\n", machine_name
));
2473 if (!(ctx
= talloc_init("ads_add_service_principal_name"))) {
2474 ads_msgfree(ads
, res
);
2475 return ADS_ERROR(LDAP_NO_MEMORY
);
2478 DEBUG(5,("ads_add_service_principal_name: INFO: "
2479 "Adding %s to host %s\n",
2480 spns
[0] ? "N/A" : spns
[0], machine_name
));
2483 DEBUG(5,("ads_add_service_principal_name: INFO: "
2484 "Adding %s to host %s\n",
2485 spns
[1] ? "N/A" : spns
[1], machine_name
));
2487 if ( (mods
= ads_init_mods(ctx
)) == NULL
) {
2488 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
2492 ret
= ads_add_strlist(ctx
,
2494 "servicePrincipalName",
2495 servicePrincipalName
);
2496 if (!ADS_ERR_OK(ret
)) {
2497 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
2501 if ( (dn_string
= ads_get_dn(ads
, ctx
, res
)) == NULL
) {
2502 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
2506 ret
= ads_gen_mod(ads
, dn_string
, mods
);
2507 if (!ADS_ERR_OK(ret
)) {
2508 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
2514 ads_msgfree(ads
, res
);
2518 static uint32_t ads_get_acct_ctrl(ADS_STRUCT
*ads
,
2521 uint32_t acct_ctrl
= 0;
2524 ok
= ads_pull_uint32(ads
, msg
, "userAccountControl", &acct_ctrl
);
2532 static ADS_STATUS
ads_change_machine_acct(ADS_STRUCT
*ads
,
2534 const struct berval
*machine_pw_val
)
2538 TALLOC_CTX
*frame
= talloc_stackframe();
2539 uint32_t acct_control
;
2540 char *control_str
= NULL
;
2541 const char *attrs
[] = {
2545 LDAPMessage
*res
= NULL
;
2548 dn
= ads_get_dn(ads
, frame
, msg
);
2550 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
2554 acct_control
= ads_get_acct_ctrl(ads
, msg
);
2555 if (acct_control
== 0) {
2556 ret
= ADS_ERROR(LDAP_NO_RESULTS_RETURNED
);
2561 * Changing the password, disables the account. So we need to change the
2562 * userAccountControl flags to enable it again.
2564 mods
= ads_init_mods(frame
);
2566 ret
= ADS_ERROR_LDAP(LDAP_NO_MEMORY
);
2570 ads_mod_ber(frame
, &mods
, "unicodePwd", machine_pw_val
);
2572 ret
= ads_gen_mod(ads
, dn
, mods
);
2573 if (!ADS_ERR_OK(ret
)) {
2579 * To activate the account, we need to disable and enable it.
2581 acct_control
|= UF_ACCOUNTDISABLE
;
2583 control_str
= talloc_asprintf(frame
, "%u", acct_control
);
2584 if (control_str
== NULL
) {
2585 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
2589 mods
= ads_init_mods(frame
);
2591 ret
= ADS_ERROR_LDAP(LDAP_NO_MEMORY
);
2595 ads_mod_str(frame
, &mods
, "userAccountControl", control_str
);
2597 ret
= ads_gen_mod(ads
, dn
, mods
);
2598 if (!ADS_ERR_OK(ret
)) {
2602 TALLOC_FREE(control_str
);
2605 * Enable the account again.
2607 acct_control
&= ~UF_ACCOUNTDISABLE
;
2609 control_str
= talloc_asprintf(frame
, "%u", acct_control
);
2610 if (control_str
== NULL
) {
2611 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
2615 mods
= ads_init_mods(frame
);
2617 ret
= ADS_ERROR_LDAP(LDAP_NO_MEMORY
);
2621 ads_mod_str(frame
, &mods
, "userAccountControl", control_str
);
2623 ret
= ads_gen_mod(ads
, dn
, mods
);
2624 if (!ADS_ERR_OK(ret
)) {
2628 TALLOC_FREE(control_str
);
2630 ret
= ads_search_dn(ads
, &res
, dn
, attrs
);
2631 ads_msgfree(ads
, res
);
2640 * adds a machine account to the ADS server
2641 * @param ads An initialized ADS_STRUCT
2642 * @param machine_name - the NetBIOS machine name of this account.
2643 * @param account_type A number indicating the type of account to create
2644 * @param org_unit The LDAP path in which to place this account
2645 * @return 0 upon success, or non-zero otherwise
2648 ADS_STATUS
ads_create_machine_acct(ADS_STRUCT
*ads
,
2649 const char *machine_name
,
2650 const char *machine_password
,
2651 const char *org_unit
,
2652 uint32_t etype_list
,
2653 const char *dns_domain_name
)
2656 char *samAccountName
= NULL
;
2657 char *controlstr
= NULL
;
2658 TALLOC_CTX
*ctx
= NULL
;
2660 char *machine_escaped
= NULL
;
2661 char *dns_hostname
= NULL
;
2662 char *new_dn
= NULL
;
2663 char *utf8_pw
= NULL
;
2664 size_t utf8_pw_len
= 0;
2665 char *utf16_pw
= NULL
;
2666 size_t utf16_pw_len
= 0;
2667 struct berval machine_pw_val
;
2669 const char **spn_array
= NULL
;
2670 size_t num_spns
= 0;
2671 const char *spn_prefix
[] = {
2673 "RestrictedKrbHost",
2676 LDAPMessage
*res
= NULL
;
2677 uint32_t acct_control
= UF_WORKSTATION_TRUST_ACCOUNT
;
2679 ctx
= talloc_init("ads_add_machine_acct");
2681 return ADS_ERROR(LDAP_NO_MEMORY
);
2684 machine_escaped
= escape_rdn_val_string_alloc(machine_name
);
2685 if (machine_escaped
== NULL
) {
2686 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
2690 utf8_pw
= talloc_asprintf(ctx
, "\"%s\"", machine_password
);
2691 if (utf8_pw
== NULL
) {
2692 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
2695 utf8_pw_len
= strlen(utf8_pw
);
2697 ok
= convert_string_talloc(ctx
,
2698 CH_UTF8
, CH_UTF16MUNGED
,
2699 utf8_pw
, utf8_pw_len
,
2700 (void *)&utf16_pw
, &utf16_pw_len
);
2702 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
2706 machine_pw_val
= (struct berval
) {
2708 .bv_len
= utf16_pw_len
,
2711 /* Check if the machine account already exists. */
2712 ret
= ads_find_machine_acct(ads
, &res
, machine_escaped
);
2713 if (ADS_ERR_OK(ret
)) {
2714 /* Change the machine account password */
2715 ret
= ads_change_machine_acct(ads
, res
, &machine_pw_val
);
2716 ads_msgfree(ads
, res
);
2720 ads_msgfree(ads
, res
);
2722 new_dn
= talloc_asprintf(ctx
, "cn=%s,%s", machine_escaped
, org_unit
);
2723 if (new_dn
== NULL
) {
2724 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
2728 /* Create machine account */
2730 samAccountName
= talloc_asprintf(ctx
, "%s$", machine_name
);
2731 if (samAccountName
== NULL
) {
2732 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
2736 dns_hostname
= talloc_asprintf(ctx
,
2740 if (dns_hostname
== NULL
) {
2741 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
2745 /* Add dns_hostname SPNs */
2746 for (i
= 0; i
< ARRAY_SIZE(spn_prefix
); i
++) {
2747 char *spn
= talloc_asprintf(ctx
,
2752 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
2756 ok
= add_string_to_array(ctx
,
2761 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
2766 /* Add machine_name SPNs */
2767 for (i
= 0; i
< ARRAY_SIZE(spn_prefix
); i
++) {
2768 char *spn
= talloc_asprintf(ctx
,
2773 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
2777 ok
= add_string_to_array(ctx
,
2782 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
2787 /* Make sure to NULL terminate the array */
2788 spn_array
= talloc_realloc(ctx
, spn_array
, const char *, num_spns
+ 1);
2789 if (spn_array
== NULL
) {
2790 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
2793 spn_array
[num_spns
] = NULL
;
2795 controlstr
= talloc_asprintf(ctx
, "%u", acct_control
);
2796 if (controlstr
== NULL
) {
2797 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
2801 mods
= ads_init_mods(ctx
);
2803 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
2807 ads_mod_str(ctx
, &mods
, "objectClass", "Computer");
2808 ads_mod_str(ctx
, &mods
, "SamAccountName", samAccountName
);
2809 ads_mod_str(ctx
, &mods
, "userAccountControl", controlstr
);
2810 ads_mod_str(ctx
, &mods
, "DnsHostName", dns_hostname
);
2811 ads_mod_strlist(ctx
, &mods
, "ServicePrincipalName", spn_array
);
2812 ads_mod_ber(ctx
, &mods
, "unicodePwd", &machine_pw_val
);
2814 ret
= ads_gen_add(ads
, new_dn
, mods
);
2817 SAFE_FREE(machine_escaped
);
2818 talloc_destroy(ctx
);
2824 * move a machine account to another OU on the ADS server
2825 * @param ads - An initialized ADS_STRUCT
2826 * @param machine_name - the NetBIOS machine name of this account.
2827 * @param org_unit - The LDAP path in which to place this account
2828 * @param moved - whether we moved the machine account (optional)
2829 * @return 0 upon success, or non-zero otherwise
2832 ADS_STATUS
ads_move_machine_acct(ADS_STRUCT
*ads
, const char *machine_name
,
2833 const char *org_unit
, bool *moved
)
2837 LDAPMessage
*res
= NULL
;
2838 char *filter
= NULL
;
2839 char *computer_dn
= NULL
;
2841 char *computer_rdn
= NULL
;
2842 bool need_move
= False
;
2844 if (asprintf(&filter
, "(samAccountName=%s$)", machine_name
) == -1) {
2845 rc
= ADS_ERROR(LDAP_NO_MEMORY
);
2849 /* Find pre-existing machine */
2850 rc
= ads_search(ads
, &res
, filter
, NULL
);
2851 if (!ADS_ERR_OK(rc
)) {
2855 computer_dn
= ads_get_dn(ads
, talloc_tos(), res
);
2857 rc
= ADS_ERROR(LDAP_NO_MEMORY
);
2861 parent_dn
= ads_parent_dn(computer_dn
);
2862 if (strequal(parent_dn
, org_unit
)) {
2868 if (asprintf(&computer_rdn
, "CN=%s", machine_name
) == -1) {
2869 rc
= ADS_ERROR(LDAP_NO_MEMORY
);
2873 ldap_status
= ldap_rename_s(ads
->ldap
.ld
, computer_dn
, computer_rdn
,
2874 org_unit
, 1, NULL
, NULL
);
2875 rc
= ADS_ERROR(ldap_status
);
2878 ads_msgfree(ads
, res
);
2880 TALLOC_FREE(computer_dn
);
2881 SAFE_FREE(computer_rdn
);
2883 if (!ADS_ERR_OK(rc
)) {
2895 dump a binary result from ldap
2897 static void dump_binary(ADS_STRUCT
*ads
, const char *field
, struct berval
**values
)
2900 for (i
=0; values
[i
]; i
++) {
2902 printf("%s: ", field
);
2903 for (j
=0; j
<values
[i
]->bv_len
; j
++) {
2904 printf("%02X", (unsigned char)values
[i
]->bv_val
[j
]);
2910 static void dump_guid(ADS_STRUCT
*ads
, const char *field
, struct berval
**values
)
2913 for (i
=0; values
[i
]; i
++) {
2915 DATA_BLOB in
= data_blob_const(values
[i
]->bv_val
, values
[i
]->bv_len
);
2918 status
= GUID_from_ndr_blob(&in
, &guid
);
2919 if (NT_STATUS_IS_OK(status
)) {
2920 printf("%s: %s\n", field
, GUID_string(talloc_tos(), &guid
));
2922 printf("%s: INVALID GUID\n", field
);
2928 dump a sid result from ldap
2930 static void dump_sid(ADS_STRUCT
*ads
, const char *field
, struct berval
**values
)
2933 for (i
=0; values
[i
]; i
++) {
2936 struct dom_sid_buf tmp
;
2937 ret
= sid_parse((const uint8_t *)values
[i
]->bv_val
,
2938 values
[i
]->bv_len
, &sid
);
2942 printf("%s: %s\n", field
, dom_sid_str_buf(&sid
, &tmp
));
2947 dump ntSecurityDescriptor
2949 static void dump_sd(ADS_STRUCT
*ads
, const char *filed
, struct berval
**values
)
2951 TALLOC_CTX
*frame
= talloc_stackframe();
2952 struct security_descriptor
*psd
;
2955 status
= unmarshall_sec_desc(talloc_tos(), (uint8_t *)values
[0]->bv_val
,
2956 values
[0]->bv_len
, &psd
);
2957 if (!NT_STATUS_IS_OK(status
)) {
2958 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
2959 nt_errstr(status
)));
2965 ads_disp_sd(ads
, talloc_tos(), psd
);
2972 dump a string result from ldap
2974 static void dump_string(const char *field
, char **values
)
2977 for (i
=0; values
[i
]; i
++) {
2978 printf("%s: %s\n", field
, values
[i
]);
2983 dump a field from LDAP on stdout
2987 static bool ads_dump_field(ADS_STRUCT
*ads
, char *field
, void **values
, void *data_area
)
2992 void (*handler
)(ADS_STRUCT
*, const char *, struct berval
**);
2994 {"objectGUID", False
, dump_guid
},
2995 {"netbootGUID", False
, dump_guid
},
2996 {"nTSecurityDescriptor", False
, dump_sd
},
2997 {"dnsRecord", False
, dump_binary
},
2998 {"objectSid", False
, dump_sid
},
2999 {"securityIdentifier", False
, dump_sid
},
3000 {"tokenGroups", False
, dump_sid
},
3001 {"tokenGroupsNoGCAcceptable", False
, dump_sid
},
3002 {"tokengroupsGlobalandUniversal", False
, dump_sid
},
3003 {"mS-DS-CreatorSID", False
, dump_sid
},
3004 {"msExchMailboxGuid", False
, dump_guid
},
3005 {"msDS-TrustForestTrustInfo", False
, dump_binary
},
3010 if (!field
) { /* must be end of an entry */
3015 for (i
=0; handlers
[i
].name
; i
++) {
3016 if (strcasecmp_m(handlers
[i
].name
, field
) == 0) {
3017 if (!values
) /* first time, indicate string or not */
3018 return handlers
[i
].string
;
3019 handlers
[i
].handler(ads
, field
, (struct berval
**) values
);
3023 if (!handlers
[i
].name
) {
3024 if (!values
) /* first time, indicate string conversion */
3026 dump_string(field
, (char **)values
);
3032 * Dump a result from LDAP on stdout
3033 * used for debugging
3034 * @param ads connection to ads server
3035 * @param res Results to dump
3038 void ads_dump(ADS_STRUCT
*ads
, LDAPMessage
*res
)
3040 ads_process_results(ads
, res
, ads_dump_field
, NULL
);
3044 * Walk through results, calling a function for each entry found.
3045 * The function receives a field name, a berval * array of values,
3046 * and a data area passed through from the start. The function is
3047 * called once with null for field and values at the end of each
3049 * @param ads connection to ads server
3050 * @param res Results to process
3051 * @param fn Function for processing each result
3052 * @param data_area user-defined area to pass to function
3054 void ads_process_results(ADS_STRUCT
*ads
, LDAPMessage
*res
,
3055 bool (*fn
)(ADS_STRUCT
*, char *, void **, void *),
3060 size_t converted_size
;
3062 if (!(ctx
= talloc_init("ads_process_results")))
3065 for (msg
= ads_first_entry(ads
, res
); msg
;
3066 msg
= ads_next_entry(ads
, msg
)) {
3070 for (utf8_field
=ldap_first_attribute(ads
->ldap
.ld
,
3071 (LDAPMessage
*)msg
,&b
);
3073 utf8_field
=ldap_next_attribute(ads
->ldap
.ld
,
3074 (LDAPMessage
*)msg
,b
)) {
3075 struct berval
**ber_vals
;
3081 if (!pull_utf8_talloc(ctx
, &field
, utf8_field
,
3084 DEBUG(0,("ads_process_results: "
3085 "pull_utf8_talloc failed: %s\n",
3089 string
= fn(ads
, field
, NULL
, data_area
);
3094 utf8_vals
= ldap_get_values(ads
->ldap
.ld
,
3095 (LDAPMessage
*)msg
, field
);
3096 p
= discard_const_p(const char *, utf8_vals
);
3097 str_vals
= ads_pull_strvals(ctx
, p
);
3098 fn(ads
, field
, (void **) str_vals
, data_area
);
3099 ldap_value_free(utf8_vals
);
3101 ber_vals
= ldap_get_values_len(ads
->ldap
.ld
,
3102 (LDAPMessage
*)msg
, field
);
3103 fn(ads
, field
, (void **) ber_vals
, data_area
);
3105 ldap_value_free_len(ber_vals
);
3107 ldap_memfree(utf8_field
);
3110 talloc_free_children(ctx
);
3111 fn(ads
, NULL
, NULL
, data_area
); /* completed an entry */
3114 talloc_destroy(ctx
);
3118 * count how many replies are in a LDAPMessage
3119 * @param ads connection to ads server
3120 * @param res Results to count
3121 * @return number of replies
3123 int ads_count_replies(ADS_STRUCT
*ads
, void *res
)
3125 return ldap_count_entries(ads
->ldap
.ld
, (LDAPMessage
*)res
);
3129 * pull the first entry from a ADS result
3130 * @param ads connection to ads server
3131 * @param res Results of search
3132 * @return first entry from result
3134 LDAPMessage
*ads_first_entry(ADS_STRUCT
*ads
, LDAPMessage
*res
)
3136 return ldap_first_entry(ads
->ldap
.ld
, res
);
3140 * pull the next entry from a ADS result
3141 * @param ads connection to ads server
3142 * @param res Results of search
3143 * @return next entry from result
3145 LDAPMessage
*ads_next_entry(ADS_STRUCT
*ads
, LDAPMessage
*res
)
3147 return ldap_next_entry(ads
->ldap
.ld
, res
);
3151 * pull the first message from a ADS result
3152 * @param ads connection to ads server
3153 * @param res Results of search
3154 * @return first message from result
3156 LDAPMessage
*ads_first_message(ADS_STRUCT
*ads
, LDAPMessage
*res
)
3158 return ldap_first_message(ads
->ldap
.ld
, res
);
3162 * pull the next message from a ADS result
3163 * @param ads connection to ads server
3164 * @param res Results of search
3165 * @return next message from result
3167 LDAPMessage
*ads_next_message(ADS_STRUCT
*ads
, LDAPMessage
*res
)
3169 return ldap_next_message(ads
->ldap
.ld
, res
);
3173 * pull a single string from a ADS result
3174 * @param ads connection to ads server
3175 * @param mem_ctx TALLOC_CTX to use for allocating result string
3176 * @param msg Results of search
3177 * @param field Attribute to retrieve
3178 * @return Result string in talloc context
3180 char *ads_pull_string(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
, LDAPMessage
*msg
,
3186 size_t converted_size
;
3188 values
= ldap_get_values(ads
->ldap
.ld
, msg
, field
);
3192 if (values
[0] && pull_utf8_talloc(mem_ctx
, &ux_string
, values
[0],
3197 ldap_value_free(values
);
3202 * pull an array of strings from a ADS result
3203 * @param ads connection to ads server
3204 * @param mem_ctx TALLOC_CTX to use for allocating result string
3205 * @param msg Results of search
3206 * @param field Attribute to retrieve
3207 * @return Result strings in talloc context
3209 char **ads_pull_strings(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
,
3210 LDAPMessage
*msg
, const char *field
,
3215 size_t i
, converted_size
;
3217 values
= ldap_get_values(ads
->ldap
.ld
, msg
, field
);
3221 *num_values
= ldap_count_values(values
);
3223 ret
= talloc_array(mem_ctx
, char *, *num_values
+ 1);
3225 ldap_value_free(values
);
3229 for (i
=0;i
<*num_values
;i
++) {
3230 if (!pull_utf8_talloc(mem_ctx
, &ret
[i
], values
[i
],
3233 ldap_value_free(values
);
3239 ldap_value_free(values
);
3244 * pull an array of strings from a ADS result
3245 * (handle large multivalue attributes with range retrieval)
3246 * @param ads connection to ads server
3247 * @param mem_ctx TALLOC_CTX to use for allocating result string
3248 * @param msg Results of search
3249 * @param field Attribute to retrieve
3250 * @param current_strings strings returned by a previous call to this function
3251 * @param next_attribute The next query should ask for this attribute
3252 * @param num_values How many values did we get this time?
3253 * @param more_values Are there more values to get?
3254 * @return Result strings in talloc context
3256 char **ads_pull_strings_range(ADS_STRUCT
*ads
,
3257 TALLOC_CTX
*mem_ctx
,
3258 LDAPMessage
*msg
, const char *field
,
3259 char **current_strings
,
3260 const char **next_attribute
,
3261 size_t *num_strings
,
3265 char *expected_range_attrib
, *range_attr
= NULL
;
3266 BerElement
*ptr
= NULL
;
3269 size_t num_new_strings
;
3270 unsigned long int range_start
;
3271 unsigned long int range_end
;
3273 /* we might have been given the whole lot anyway */
3274 if ((strings
= ads_pull_strings(ads
, mem_ctx
, msg
, field
, num_strings
))) {
3275 *more_strings
= False
;
3279 expected_range_attrib
= talloc_asprintf(mem_ctx
, "%s;Range=", field
);
3281 /* look for Range result */
3282 for (attr
= ldap_first_attribute(ads
->ldap
.ld
, (LDAPMessage
*)msg
, &ptr
);
3284 attr
= ldap_next_attribute(ads
->ldap
.ld
, (LDAPMessage
*)msg
, ptr
)) {
3285 /* we ignore the fact that this is utf8, as all attributes are ascii... */
3286 if (strnequal(attr
, expected_range_attrib
, strlen(expected_range_attrib
))) {
3294 /* nothing here - this field is just empty */
3295 *more_strings
= False
;
3299 if (sscanf(&range_attr
[strlen(expected_range_attrib
)], "%lu-%lu",
3300 &range_start
, &range_end
) == 2) {
3301 *more_strings
= True
;
3303 if (sscanf(&range_attr
[strlen(expected_range_attrib
)], "%lu-*",
3304 &range_start
) == 1) {
3305 *more_strings
= False
;
3307 DEBUG(1, ("ads_pull_strings_range: Cannot parse Range attribute (%s)\n",
3309 ldap_memfree(range_attr
);
3310 *more_strings
= False
;
3315 if ((*num_strings
) != range_start
) {
3316 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) doesn't start at %u, but at %lu"
3317 " - aborting range retrieval\n",
3318 range_attr
, (unsigned int)(*num_strings
) + 1, range_start
));
3319 ldap_memfree(range_attr
);
3320 *more_strings
= False
;
3324 new_strings
= ads_pull_strings(ads
, mem_ctx
, msg
, range_attr
, &num_new_strings
);
3326 if (*more_strings
&& ((*num_strings
+ num_new_strings
) != (range_end
+ 1))) {
3327 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) tells us we have %lu "
3328 "strings in this bunch, but we only got %lu - aborting range retrieval\n",
3329 range_attr
, (unsigned long int)range_end
- range_start
+ 1,
3330 (unsigned long int)num_new_strings
));
3331 ldap_memfree(range_attr
);
3332 *more_strings
= False
;
3336 strings
= talloc_realloc(mem_ctx
, current_strings
, char *,
3337 *num_strings
+ num_new_strings
);
3339 if (strings
== NULL
) {
3340 ldap_memfree(range_attr
);
3341 *more_strings
= False
;
3345 if (new_strings
&& num_new_strings
) {
3346 memcpy(&strings
[*num_strings
], new_strings
,
3347 sizeof(*new_strings
) * num_new_strings
);
3350 (*num_strings
) += num_new_strings
;
3352 if (*more_strings
) {
3353 *next_attribute
= talloc_asprintf(mem_ctx
,
3358 if (!*next_attribute
) {
3359 DEBUG(1, ("talloc_asprintf for next attribute failed!\n"));
3360 ldap_memfree(range_attr
);
3361 *more_strings
= False
;
3366 ldap_memfree(range_attr
);
3372 * pull a single uint32_t from a ADS result
3373 * @param ads connection to ads server
3374 * @param msg Results of search
3375 * @param field Attribute to retrieve
3376 * @param v Pointer to int to store result
3377 * @return boolean indicating success
3379 bool ads_pull_uint32(ADS_STRUCT
*ads
, LDAPMessage
*msg
, const char *field
,
3384 values
= ldap_get_values(ads
->ldap
.ld
, msg
, field
);
3388 ldap_value_free(values
);
3392 *v
= atoi(values
[0]);
3393 ldap_value_free(values
);
3398 * pull a single objectGUID from an ADS result
3399 * @param ads connection to ADS server
3400 * @param msg results of search
3401 * @param guid 37-byte area to receive text guid
3402 * @return boolean indicating success
3404 bool ads_pull_guid(ADS_STRUCT
*ads
, LDAPMessage
*msg
, struct GUID
*guid
)
3409 if (!smbldap_talloc_single_blob(talloc_tos(), ads
->ldap
.ld
, msg
, "objectGUID",
3414 status
= GUID_from_ndr_blob(&blob
, guid
);
3415 talloc_free(blob
.data
);
3416 return NT_STATUS_IS_OK(status
);
3421 * pull a single struct dom_sid from a ADS result
3422 * @param ads connection to ads server
3423 * @param msg Results of search
3424 * @param field Attribute to retrieve
3425 * @param sid Pointer to sid to store result
3426 * @return boolean indicating success
3428 bool ads_pull_sid(ADS_STRUCT
*ads
, LDAPMessage
*msg
, const char *field
,
3429 struct dom_sid
*sid
)
3431 return smbldap_pull_sid(ads
->ldap
.ld
, msg
, field
, sid
);
3435 * pull an array of struct dom_sids from a ADS result
3436 * @param ads connection to ads server
3437 * @param mem_ctx TALLOC_CTX for allocating sid array
3438 * @param msg Results of search
3439 * @param field Attribute to retrieve
3440 * @param sids pointer to sid array to allocate
3441 * @return the count of SIDs pulled
3443 int ads_pull_sids(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
,
3444 LDAPMessage
*msg
, const char *field
, struct dom_sid
**sids
)
3446 struct berval
**values
;
3449 values
= ldap_get_values_len(ads
->ldap
.ld
, msg
, field
);
3454 for (i
=0; values
[i
]; i
++)
3458 (*sids
) = talloc_array(mem_ctx
, struct dom_sid
, i
);
3460 ldap_value_free_len(values
);
3468 for (i
=0; values
[i
]; i
++) {
3470 ret
= sid_parse((const uint8_t *)values
[i
]->bv_val
,
3471 values
[i
]->bv_len
, &(*sids
)[count
]);
3473 struct dom_sid_buf buf
;
3474 DBG_DEBUG("pulling SID: %s\n",
3475 dom_sid_str_buf(&(*sids
)[count
], &buf
));
3480 ldap_value_free_len(values
);
3485 * pull a struct security_descriptor from a ADS result
3486 * @param ads connection to ads server
3487 * @param mem_ctx TALLOC_CTX for allocating sid array
3488 * @param msg Results of search
3489 * @param field Attribute to retrieve
3490 * @param sd Pointer to *struct security_descriptor to store result (talloc()ed)
3491 * @return boolean indicating success
3493 bool ads_pull_sd(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
,
3494 LDAPMessage
*msg
, const char *field
,
3495 struct security_descriptor
**sd
)
3497 struct berval
**values
;
3500 values
= ldap_get_values_len(ads
->ldap
.ld
, msg
, field
);
3502 if (!values
) return false;
3506 status
= unmarshall_sec_desc(mem_ctx
,
3507 (uint8_t *)values
[0]->bv_val
,
3508 values
[0]->bv_len
, sd
);
3509 if (!NT_STATUS_IS_OK(status
)) {
3510 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
3511 nt_errstr(status
)));
3516 ldap_value_free_len(values
);
3521 * in order to support usernames longer than 21 characters we need to
3522 * use both the sAMAccountName and the userPrincipalName attributes
3523 * It seems that not all users have the userPrincipalName attribute set
3525 * @param ads connection to ads server
3526 * @param mem_ctx TALLOC_CTX for allocating sid array
3527 * @param msg Results of search
3528 * @return the username
3530 char *ads_pull_username(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
,
3536 /* lookup_name() only works on the sAMAccountName to
3537 returning the username portion of userPrincipalName
3538 breaks winbindd_getpwnam() */
3540 ret
= ads_pull_string(ads
, mem_ctx
, msg
, "userPrincipalName");
3541 if (ret
&& (p
= strchr_m(ret
, '@'))) {
3546 return ads_pull_string(ads
, mem_ctx
, msg
, "sAMAccountName");
3551 * find the update serial number - this is the core of the ldap cache
3552 * @param ads connection to ads server
3553 * @param ads connection to ADS server
3554 * @param usn Pointer to retrieved update serial number
3555 * @return status of search
3557 ADS_STATUS
ads_USN(ADS_STRUCT
*ads
, uint32_t *usn
)
3559 const char *attrs
[] = {"highestCommittedUSN", NULL
};
3563 status
= ads_do_search_retry(ads
, "", LDAP_SCOPE_BASE
, "(objectclass=*)", attrs
, &res
);
3564 if (!ADS_ERR_OK(status
))
3567 if (ads_count_replies(ads
, res
) != 1) {
3568 ads_msgfree(ads
, res
);
3569 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED
);
3572 if (!ads_pull_uint32(ads
, res
, "highestCommittedUSN", usn
)) {
3573 ads_msgfree(ads
, res
);
3574 return ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE
);
3577 ads_msgfree(ads
, res
);
3581 /* parse a ADS timestring - typical string is
3582 '20020917091222.0Z0' which means 09:12.22 17th September
3584 static time_t ads_parse_time(const char *str
)
3590 if (sscanf(str
, "%4d%2d%2d%2d%2d%2d",
3591 &tm
.tm_year
, &tm
.tm_mon
, &tm
.tm_mday
,
3592 &tm
.tm_hour
, &tm
.tm_min
, &tm
.tm_sec
) != 6) {
3601 /********************************************************************
3602 ********************************************************************/
3604 ADS_STATUS
ads_current_time(ADS_STRUCT
*ads
)
3606 const char *attrs
[] = {"currentTime", NULL
};
3610 TALLOC_CTX
*tmp_ctx
= talloc_stackframe();
3611 ADS_STRUCT
*ads_s
= ads
;
3613 /* establish a new ldap tcp session if necessary */
3615 if ( !ads
->ldap
.ld
) {
3617 * ADS_STRUCT may be being reused after a
3618 * DC lookup, so ads->ldap.ss may already have a
3619 * good address. If not, re-initialize the passed-in
3620 * ADS_STRUCT with the given server.XXXX parameters.
3622 * Note that this doesn't depend on
3623 * ads->server.ldap_server != NULL,
3624 * as the case where ads->server.ldap_server==NULL and
3625 * ads->ldap.ss != zero_address is precisely the DC
3626 * lookup case where ads->ldap.ss was found by going
3627 * through ads_find_dc() again we want to avoid repeating.
3629 if (is_zero_addr(&ads
->ldap
.ss
)) {
3630 ads_s
= ads_init(tmp_ctx
,
3632 ads
->server
.workgroup
,
3633 ads
->server
.ldap_server
,
3635 if (ads_s
== NULL
) {
3636 status
= ADS_ERROR(LDAP_NO_MEMORY
);
3642 * Reset ads->config.flags as it can contain the flags
3643 * returned by the previous CLDAP ping when reusing the struct.
3645 ads_s
->config
.flags
= 0;
3647 status
= ads_connect_simple_anon(ads_s
);
3648 if ( !ADS_ERR_OK(status
))
3652 status
= ads_do_search(ads_s
, "", LDAP_SCOPE_BASE
, "(objectclass=*)", attrs
, &res
);
3653 if (!ADS_ERR_OK(status
)) {
3657 timestr
= ads_pull_string(ads_s
, tmp_ctx
, res
, "currentTime");
3659 ads_msgfree(ads_s
, res
);
3660 status
= ADS_ERROR(LDAP_NO_RESULTS_RETURNED
);
3664 /* but save the time and offset in the original ADS_STRUCT */
3666 ads
->config
.current_time
= ads_parse_time(timestr
);
3668 if (ads
->config
.current_time
!= 0) {
3669 ads
->config
.time_offset
= ads
->config
.current_time
- time(NULL
);
3670 DBG_INFO("server time offset is %d seconds\n",
3671 ads
->config
.time_offset
);
3673 ads
->config
.time_offset
= 0;
3676 DBG_INFO("server time offset is %d seconds\n",
3677 ads
->config
.time_offset
);
3679 ads_msgfree(ads
, res
);
3681 status
= ADS_SUCCESS
;
3684 TALLOC_FREE(tmp_ctx
);
3689 /********************************************************************
3690 ********************************************************************/
3692 ADS_STATUS
ads_domain_func_level(ADS_STRUCT
*ads
, uint32_t *val
)
3694 TALLOC_CTX
*tmp_ctx
= talloc_stackframe();
3695 const char *attrs
[] = {"domainFunctionality", NULL
};
3698 ADS_STRUCT
*ads_s
= ads
;
3700 *val
= DS_DOMAIN_FUNCTION_2000
;
3702 /* establish a new ldap tcp session if necessary */
3704 if ( !ads
->ldap
.ld
) {
3706 * ADS_STRUCT may be being reused after a
3707 * DC lookup, so ads->ldap.ss may already have a
3708 * good address. If not, re-initialize the passed-in
3709 * ADS_STRUCT with the given server.XXXX parameters.
3711 * Note that this doesn't depend on
3712 * ads->server.ldap_server != NULL,
3713 * as the case where ads->server.ldap_server==NULL and
3714 * ads->ldap.ss != zero_address is precisely the DC
3715 * lookup case where ads->ldap.ss was found by going
3716 * through ads_find_dc() again we want to avoid repeating.
3718 if (is_zero_addr(&ads
->ldap
.ss
)) {
3719 ads_s
= ads_init(tmp_ctx
,
3721 ads
->server
.workgroup
,
3722 ads
->server
.ldap_server
,
3724 if (ads_s
== NULL
) {
3725 status
= ADS_ERROR_NT(NT_STATUS_NO_MEMORY
);
3731 * Reset ads->config.flags as it can contain the flags
3732 * returned by the previous CLDAP ping when reusing the struct.
3734 ads_s
->config
.flags
= 0;
3736 status
= ads_connect_simple_anon(ads_s
);
3737 if ( !ADS_ERR_OK(status
))
3741 /* If the attribute does not exist assume it is a Windows 2000
3742 functional domain */
3744 status
= ads_do_search(ads_s
, "", LDAP_SCOPE_BASE
, "(objectclass=*)", attrs
, &res
);
3745 if (!ADS_ERR_OK(status
)) {
3746 if ( status
.err
.rc
== LDAP_NO_SUCH_ATTRIBUTE
) {
3747 status
= ADS_SUCCESS
;
3752 if ( !ads_pull_uint32(ads_s
, res
, "domainFunctionality", val
) ) {
3753 DEBUG(5,("ads_domain_func_level: Failed to pull the domainFunctionality attribute.\n"));
3755 DEBUG(3,("ads_domain_func_level: %d\n", *val
));
3758 ads_msgfree(ads_s
, res
);
3761 TALLOC_FREE(tmp_ctx
);
3767 * find the domain sid for our domain
3768 * @param ads connection to ads server
3769 * @param sid Pointer to domain sid
3770 * @return status of search
3772 ADS_STATUS
ads_domain_sid(ADS_STRUCT
*ads
, struct dom_sid
*sid
)
3774 const char *attrs
[] = {"objectSid", NULL
};
3778 rc
= ads_do_search_retry(ads
, ads
->config
.bind_path
, LDAP_SCOPE_BASE
, "(objectclass=*)",
3780 if (!ADS_ERR_OK(rc
)) return rc
;
3781 if (!ads_pull_sid(ads
, res
, "objectSid", sid
)) {
3782 ads_msgfree(ads
, res
);
3783 return ADS_ERROR_SYSTEM(ENOENT
);
3785 ads_msgfree(ads
, res
);
3791 * find our site name
3792 * @param ads connection to ads server
3793 * @param mem_ctx Pointer to talloc context
3794 * @param site_name Pointer to the sitename
3795 * @return status of search
3797 ADS_STATUS
ads_site_dn(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
, const char **site_name
)
3801 const char *dn
, *service_name
;
3802 const char *attrs
[] = { "dsServiceName", NULL
};
3804 status
= ads_do_search(ads
, "", LDAP_SCOPE_BASE
, "(objectclass=*)", attrs
, &res
);
3805 if (!ADS_ERR_OK(status
)) {
3809 service_name
= ads_pull_string(ads
, mem_ctx
, res
, "dsServiceName");
3810 if (service_name
== NULL
) {
3811 ads_msgfree(ads
, res
);
3812 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED
);
3815 ads_msgfree(ads
, res
);
3817 /* go up three levels */
3818 dn
= ads_parent_dn(ads_parent_dn(ads_parent_dn(service_name
)));
3820 return ADS_ERROR(LDAP_NO_MEMORY
);
3823 *site_name
= talloc_strdup(mem_ctx
, dn
);
3824 if (*site_name
== NULL
) {
3825 return ADS_ERROR(LDAP_NO_MEMORY
);
3830 dsServiceName: CN=NTDS Settings,CN=W2K3DC,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,DC=ber,DC=suse,DC=de
3835 * find the site dn where a machine resides
3836 * @param ads connection to ads server
3837 * @param mem_ctx Pointer to talloc context
3838 * @param computer_name name of the machine
3839 * @param site_name Pointer to the sitename
3840 * @return status of search
3842 ADS_STATUS
ads_site_dn_for_machine(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
, const char *computer_name
, const char **site_dn
)
3846 const char *parent
, *filter
;
3847 char *config_context
= NULL
;
3850 /* shortcut a query */
3851 if (strequal(computer_name
, ads
->config
.ldap_server_name
)) {
3852 return ads_site_dn(ads
, mem_ctx
, site_dn
);
3855 status
= ads_config_path(ads
, mem_ctx
, &config_context
);
3856 if (!ADS_ERR_OK(status
)) {
3860 filter
= talloc_asprintf(mem_ctx
, "(cn=%s)", computer_name
);
3861 if (filter
== NULL
) {
3862 return ADS_ERROR(LDAP_NO_MEMORY
);
3865 status
= ads_do_search(ads
, config_context
, LDAP_SCOPE_SUBTREE
,
3866 filter
, NULL
, &res
);
3867 if (!ADS_ERR_OK(status
)) {
3871 if (ads_count_replies(ads
, res
) != 1) {
3872 ads_msgfree(ads
, res
);
3873 return ADS_ERROR(LDAP_NO_SUCH_OBJECT
);
3876 dn
= ads_get_dn(ads
, mem_ctx
, res
);
3878 ads_msgfree(ads
, res
);
3879 return ADS_ERROR(LDAP_NO_MEMORY
);
3882 /* go up three levels */
3883 parent
= ads_parent_dn(ads_parent_dn(ads_parent_dn(dn
)));
3884 if (parent
== NULL
) {
3885 ads_msgfree(ads
, res
);
3887 return ADS_ERROR(LDAP_NO_MEMORY
);
3890 *site_dn
= talloc_strdup(mem_ctx
, parent
);
3891 if (*site_dn
== NULL
) {
3892 ads_msgfree(ads
, res
);
3894 return ADS_ERROR(LDAP_NO_MEMORY
);
3898 ads_msgfree(ads
, res
);
3904 * get the upn suffixes for a domain
3905 * @param ads connection to ads server
3906 * @param mem_ctx Pointer to talloc context
3907 * @param suffixes Pointer to an array of suffixes
3908 * @param num_suffixes Pointer to the number of suffixes
3909 * @return status of search
3911 ADS_STATUS
ads_upn_suffixes(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
, char ***suffixes
, size_t *num_suffixes
)
3916 char *config_context
= NULL
;
3917 const char *attrs
[] = { "uPNSuffixes", NULL
};
3919 status
= ads_config_path(ads
, mem_ctx
, &config_context
);
3920 if (!ADS_ERR_OK(status
)) {
3924 base
= talloc_asprintf(mem_ctx
, "cn=Partitions,%s", config_context
);
3926 return ADS_ERROR(LDAP_NO_MEMORY
);
3929 status
= ads_search_dn(ads
, &res
, base
, attrs
);
3930 if (!ADS_ERR_OK(status
)) {
3934 if (ads_count_replies(ads
, res
) != 1) {
3935 ads_msgfree(ads
, res
);
3936 return ADS_ERROR(LDAP_NO_SUCH_OBJECT
);
3939 (*suffixes
) = ads_pull_strings(ads
, mem_ctx
, res
, "uPNSuffixes", num_suffixes
);
3940 if ((*suffixes
) == NULL
) {
3941 ads_msgfree(ads
, res
);
3942 return ADS_ERROR(LDAP_NO_MEMORY
);
3945 ads_msgfree(ads
, res
);
3951 * get the joinable ous for a domain
3952 * @param ads connection to ads server
3953 * @param mem_ctx Pointer to talloc context
3954 * @param ous Pointer to an array of ous
3955 * @param num_ous Pointer to the number of ous
3956 * @return status of search
3958 ADS_STATUS
ads_get_joinable_ous(ADS_STRUCT
*ads
,
3959 TALLOC_CTX
*mem_ctx
,
3964 LDAPMessage
*res
= NULL
;
3965 LDAPMessage
*msg
= NULL
;
3966 const char *attrs
[] = { "dn", NULL
};
3969 status
= ads_search(ads
, &res
,
3970 "(|(objectClass=domain)(objectclass=organizationalUnit))",
3972 if (!ADS_ERR_OK(status
)) {
3976 count
= ads_count_replies(ads
, res
);
3978 ads_msgfree(ads
, res
);
3979 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED
);
3982 for (msg
= ads_first_entry(ads
, res
); msg
;
3983 msg
= ads_next_entry(ads
, msg
)) {
3984 const char **p
= discard_const_p(const char *, *ous
);
3987 dn
= ads_get_dn(ads
, talloc_tos(), msg
);
3989 ads_msgfree(ads
, res
);
3990 return ADS_ERROR(LDAP_NO_MEMORY
);
3993 if (!add_string_to_array(mem_ctx
, dn
, &p
, num_ous
)) {
3995 ads_msgfree(ads
, res
);
3996 return ADS_ERROR(LDAP_NO_MEMORY
);
4000 *ous
= discard_const_p(char *, p
);
4003 ads_msgfree(ads
, res
);
4010 * pull a struct dom_sid from an extended dn string
4011 * @param mem_ctx TALLOC_CTX
4012 * @param extended_dn string
4013 * @param flags string type of extended_dn
4014 * @param sid pointer to a struct dom_sid
4015 * @return NT_STATUS_OK on success,
4016 * NT_INVALID_PARAMETER on error,
4017 * NT_STATUS_NOT_FOUND if no SID present
4019 ADS_STATUS
ads_get_sid_from_extended_dn(TALLOC_CTX
*mem_ctx
,
4020 const char *extended_dn
,
4021 enum ads_extended_dn_flags flags
,
4022 struct dom_sid
*sid
)
4027 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER
);
4030 /* otherwise extended_dn gets stripped off */
4031 if ((dn
= talloc_strdup(mem_ctx
, extended_dn
)) == NULL
) {
4032 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER
);
4035 * ADS_EXTENDED_DN_HEX_STRING:
4036 * <GUID=238e1963cb390f4bb032ba0105525a29>;<SID=010500000000000515000000bb68c8fd6b61b427572eb04556040000>;CN=gd,OU=berlin,OU=suse,DC=ber,DC=suse,DC=de
4038 * ADS_EXTENDED_DN_STRING (only with w2k3):
4039 * <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
4041 * Object with no SID, such as an Exchange Public Folder
4042 * <GUID=28907fb4bdf6854993e7f0a10b504e7c>;CN=public,CN=Microsoft Exchange System Objects,DC=sd2k3ms,DC=west,DC=isilon,DC=com
4045 p
= strchr(dn
, ';');
4047 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER
);
4050 if (strncmp(p
, ";<SID=", strlen(";<SID=")) != 0) {
4051 DEBUG(5,("No SID present in extended dn\n"));
4052 return ADS_ERROR_NT(NT_STATUS_NOT_FOUND
);
4055 p
+= strlen(";<SID=");
4059 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER
);
4064 DEBUG(100,("ads_get_sid_from_extended_dn: sid string is %s\n", p
));
4068 case ADS_EXTENDED_DN_STRING
:
4069 if (!string_to_sid(sid
, p
)) {
4070 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER
);
4073 case ADS_EXTENDED_DN_HEX_STRING
: {
4078 buf_len
= strhex_to_str(buf
, sizeof(buf
), p
, strlen(p
));
4080 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER
);
4083 ret
= sid_parse((const uint8_t *)buf
, buf_len
, sid
);
4085 DEBUG(10,("failed to parse sid\n"));
4086 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER
);
4091 DEBUG(10,("unknown extended dn format\n"));
4092 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER
);
4095 return ADS_ERROR_NT(NT_STATUS_OK
);
4098 /********************************************************************
4099 ********************************************************************/
4101 char* ads_get_upn( ADS_STRUCT
*ads
, TALLOC_CTX
*ctx
, const char *machine_name
)
4103 LDAPMessage
*res
= NULL
;
4108 status
= ads_find_machine_acct(ads
, &res
, machine_name
);
4109 if (!ADS_ERR_OK(status
)) {
4110 DEBUG(0,("ads_get_upn: Failed to find account for %s\n",
4111 lp_netbios_name()));
4115 if ( (count
= ads_count_replies(ads
, res
)) != 1 ) {
4116 DEBUG(1,("ads_get_upn: %d entries returned!\n", count
));
4120 if ( (name
= ads_pull_string(ads
, ctx
, res
, "userPrincipalName")) == NULL
) {
4121 DEBUG(2,("ads_get_upn: No userPrincipalName attribute!\n"));
4125 ads_msgfree(ads
, res
);
4132 SAVED CODE
- we used to join via ldap
- remember how we did
this. JRA
.
4135 * Join a machine to a realm
4136 * Creates the machine account and sets the machine password
4137 * @param ads connection to ads server
4138 * @param machine name of host to add
4139 * @param org_unit Organizational unit to place machine in
4140 * @return status of join
4142 ADS_STATUS
ads_join_realm(ADS_STRUCT
*ads
, const char *machine_name
,
4143 uint32_t account_type
, const char *org_unit
)
4146 LDAPMessage
*res
= NULL
;
4149 /* machine name must be lowercase */
4150 machine
= SMB_STRDUP(machine_name
);
4151 strlower_m(machine
);
4154 status = ads_find_machine_acct(ads, (void **)&res, machine);
4155 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
4156 DEBUG(0, ("Host account for %s already exists - deleting old account\n", machine));
4157 status = ads_leave_realm(ads, machine);
4158 if (!ADS_ERR_OK(status)) {
4159 DEBUG(0, ("Failed to delete host '%s' from the '%s' realm.\n",
4160 machine, ads->config.realm));
4165 status
= ads_add_machine_acct(ads
, machine
, account_type
, org_unit
);
4166 if (!ADS_ERR_OK(status
)) {
4167 DEBUG(0, ("ads_join_realm: ads_add_machine_acct failed (%s): %s\n", machine
, ads_errstr(status
)));
4172 status
= ads_find_machine_acct(ads
, (void **)(void *)&res
, machine
);
4173 if (!ADS_ERR_OK(status
)) {
4174 DEBUG(0, ("ads_join_realm: Host account test failed for machine %s\n", machine
));
4180 ads_msgfree(ads
, res
);
4187 * Delete a machine from the realm
4188 * @param ads connection to ads server
4189 * @param hostname Machine to remove
4190 * @return status of delete
4192 ADS_STATUS
ads_leave_realm(ADS_STRUCT
*ads
, const char *hostname
)
4197 char *hostnameDN
, *host
;
4199 LDAPControl ldap_control
;
4200 LDAPControl
* pldap_control
[2] = {NULL
, NULL
};
4202 pldap_control
[0] = &ldap_control
;
4203 memset(&ldap_control
, 0, sizeof(LDAPControl
));
4204 ldap_control
.ldctl_oid
= discard_const_p(char, LDAP_SERVER_TREE_DELETE_OID
);
4206 /* hostname must be lowercase */
4207 host
= SMB_STRDUP(hostname
);
4208 if (!strlower_m(host
)) {
4210 return ADS_ERROR_SYSTEM(EINVAL
);
4213 status
= ads_find_machine_acct(ads
, &res
, host
);
4214 if (!ADS_ERR_OK(status
)) {
4215 DEBUG(0, ("Host account for %s does not exist.\n", host
));
4220 msg
= ads_first_entry(ads
, res
);
4223 return ADS_ERROR_SYSTEM(ENOENT
);
4226 hostnameDN
= ads_get_dn(ads
, talloc_tos(), (LDAPMessage
*)msg
);
4227 if (hostnameDN
== NULL
) {
4229 return ADS_ERROR_SYSTEM(ENOENT
);
4232 rc
= ldap_delete_ext_s(ads
->ldap
.ld
, hostnameDN
, pldap_control
, NULL
);
4234 DEBUG(3,("ldap_delete_ext_s failed with error code %d\n", rc
));
4236 DEBUG(3,("ldap_delete_ext_s succeeded with error code %d\n", rc
));
4239 if (rc
!= LDAP_SUCCESS
) {
4240 const char *attrs
[] = { "cn", NULL
};
4241 LDAPMessage
*msg_sub
;
4243 /* we only search with scope ONE, we do not expect any further
4244 * objects to be created deeper */
4246 status
= ads_do_search_retry(ads
, hostnameDN
,
4247 LDAP_SCOPE_ONELEVEL
,
4248 "(objectclass=*)", attrs
, &res
);
4250 if (!ADS_ERR_OK(status
)) {
4252 TALLOC_FREE(hostnameDN
);
4256 for (msg_sub
= ads_first_entry(ads
, res
); msg_sub
;
4257 msg_sub
= ads_next_entry(ads
, msg_sub
)) {
4261 if ((dn
= ads_get_dn(ads
, talloc_tos(), msg_sub
)) == NULL
) {
4263 TALLOC_FREE(hostnameDN
);
4264 return ADS_ERROR(LDAP_NO_MEMORY
);
4267 status
= ads_del_dn(ads
, dn
);
4268 if (!ADS_ERR_OK(status
)) {
4269 DEBUG(3,("failed to delete dn %s: %s\n", dn
, ads_errstr(status
)));
4272 TALLOC_FREE(hostnameDN
);
4279 /* there should be no subordinate objects anymore */
4280 status
= ads_do_search_retry(ads
, hostnameDN
,
4281 LDAP_SCOPE_ONELEVEL
,
4282 "(objectclass=*)", attrs
, &res
);
4284 if (!ADS_ERR_OK(status
) || ( (ads_count_replies(ads
, res
)) > 0 ) ) {
4286 TALLOC_FREE(hostnameDN
);
4290 /* delete hostnameDN now */
4291 status
= ads_del_dn(ads
, hostnameDN
);
4292 if (!ADS_ERR_OK(status
)) {
4294 DEBUG(3,("failed to delete dn %s: %s\n", hostnameDN
, ads_errstr(status
)));
4295 TALLOC_FREE(hostnameDN
);
4300 TALLOC_FREE(hostnameDN
);
4302 status
= ads_find_machine_acct(ads
, &res
, host
);
4303 if ((status
.error_type
== ENUM_ADS_ERROR_LDAP
) &&
4304 (status
.err
.rc
!= LDAP_NO_SUCH_OBJECT
)) {
4305 DEBUG(3, ("Failed to remove host account.\n"));
4315 * pull all token-sids from an LDAP dn
4316 * @param ads connection to ads server
4317 * @param mem_ctx TALLOC_CTX for allocating sid array
4318 * @param dn of LDAP object
4319 * @param user_sid pointer to struct dom_sid (objectSid)
4320 * @param primary_group_sid pointer to struct dom_sid (self composed)
4321 * @param sids pointer to sid array to allocate
4322 * @param num_sids counter of SIDs pulled
4323 * @return status of token query
4325 ADS_STATUS
ads_get_tokensids(ADS_STRUCT
*ads
,
4326 TALLOC_CTX
*mem_ctx
,
4328 struct dom_sid
*user_sid
,
4329 struct dom_sid
*primary_group_sid
,
4330 struct dom_sid
**sids
,
4334 LDAPMessage
*res
= NULL
;
4336 size_t tmp_num_sids
;
4337 struct dom_sid
*tmp_sids
;
4338 struct dom_sid tmp_user_sid
;
4339 struct dom_sid tmp_primary_group_sid
;
4341 const char *attrs
[] = {
4348 status
= ads_search_retry_dn(ads
, &res
, dn
, attrs
);
4349 if (!ADS_ERR_OK(status
)) {
4353 count
= ads_count_replies(ads
, res
);
4355 ads_msgfree(ads
, res
);
4356 return ADS_ERROR_LDAP(LDAP_NO_SUCH_OBJECT
);
4359 if (!ads_pull_sid(ads
, res
, "objectSid", &tmp_user_sid
)) {
4360 ads_msgfree(ads
, res
);
4361 return ADS_ERROR_LDAP(LDAP_NO_MEMORY
);
4364 if (!ads_pull_uint32(ads
, res
, "primaryGroupID", &pgid
)) {
4365 ads_msgfree(ads
, res
);
4366 return ADS_ERROR_LDAP(LDAP_NO_MEMORY
);
4370 /* hack to compose the primary group sid without knowing the
4373 struct dom_sid domsid
;
4375 sid_copy(&domsid
, &tmp_user_sid
);
4377 if (!sid_split_rid(&domsid
, NULL
)) {
4378 ads_msgfree(ads
, res
);
4379 return ADS_ERROR_LDAP(LDAP_NO_MEMORY
);
4382 if (!sid_compose(&tmp_primary_group_sid
, &domsid
, pgid
)) {
4383 ads_msgfree(ads
, res
);
4384 return ADS_ERROR_LDAP(LDAP_NO_MEMORY
);
4388 tmp_num_sids
= ads_pull_sids(ads
, mem_ctx
, res
, "tokenGroups", &tmp_sids
);
4390 if (tmp_num_sids
== 0 || !tmp_sids
) {
4391 ads_msgfree(ads
, res
);
4392 return ADS_ERROR_LDAP(LDAP_NO_MEMORY
);
4396 *num_sids
= tmp_num_sids
;
4404 *user_sid
= tmp_user_sid
;
4407 if (primary_group_sid
) {
4408 *primary_group_sid
= tmp_primary_group_sid
;
4411 DEBUG(10,("ads_get_tokensids: returned %d sids\n", (int)tmp_num_sids
+ 2));
4413 ads_msgfree(ads
, res
);
4414 return ADS_ERROR_LDAP(LDAP_SUCCESS
);
4418 * Find a sAMAccountName in LDAP
4419 * @param ads connection to ads server
4420 * @param mem_ctx TALLOC_CTX for allocating sid array
4421 * @param samaccountname to search
4422 * @param uac_ret uint32_t pointer userAccountControl attribute value
4423 * @param dn_ret pointer to dn
4424 * @return status of token query
4426 ADS_STATUS
ads_find_samaccount(ADS_STRUCT
*ads
,
4427 TALLOC_CTX
*mem_ctx
,
4428 const char *samaccountname
,
4430 const char **dn_ret
)
4433 const char *attrs
[] = { "userAccountControl", NULL
};
4435 LDAPMessage
*res
= NULL
;
4439 filter
= talloc_asprintf(mem_ctx
, "(&(objectclass=user)(sAMAccountName=%s))",
4441 if (filter
== NULL
) {
4442 status
= ADS_ERROR_NT(NT_STATUS_NO_MEMORY
);
4446 status
= ads_do_search_all(ads
, ads
->config
.bind_path
,
4448 filter
, attrs
, &res
);
4450 if (!ADS_ERR_OK(status
)) {
4454 if (ads_count_replies(ads
, res
) != 1) {
4455 status
= ADS_ERROR(LDAP_NO_RESULTS_RETURNED
);
4459 dn
= ads_get_dn(ads
, talloc_tos(), res
);
4461 status
= ADS_ERROR(LDAP_NO_MEMORY
);
4465 if (!ads_pull_uint32(ads
, res
, "userAccountControl", &uac
)) {
4466 status
= ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE
);
4475 *dn_ret
= talloc_strdup(mem_ctx
, dn
);
4477 status
= ADS_ERROR(LDAP_NO_MEMORY
);
4483 ads_msgfree(ads
, res
);
4489 * find our configuration path
4490 * @param ads connection to ads server
4491 * @param mem_ctx Pointer to talloc context
4492 * @param config_path Pointer to the config path
4493 * @return status of search
4495 ADS_STATUS
ads_config_path(ADS_STRUCT
*ads
,
4496 TALLOC_CTX
*mem_ctx
,
4500 LDAPMessage
*res
= NULL
;
4501 const char *config_context
= NULL
;
4502 const char *attrs
[] = { "configurationNamingContext", NULL
};
4504 status
= ads_do_search(ads
, "", LDAP_SCOPE_BASE
,
4505 "(objectclass=*)", attrs
, &res
);
4506 if (!ADS_ERR_OK(status
)) {
4510 config_context
= ads_pull_string(ads
, mem_ctx
, res
,
4511 "configurationNamingContext");
4512 ads_msgfree(ads
, res
);
4513 if (!config_context
) {
4514 return ADS_ERROR(LDAP_NO_MEMORY
);
4518 *config_path
= talloc_strdup(mem_ctx
, config_context
);
4519 if (!*config_path
) {
4520 return ADS_ERROR(LDAP_NO_MEMORY
);
4524 return ADS_ERROR(LDAP_SUCCESS
);
4528 * find the displayName of an extended right
4529 * @param ads connection to ads server
4530 * @param config_path The config path
4531 * @param mem_ctx Pointer to talloc context
4532 * @param GUID struct of the rightsGUID
4533 * @return status of search
4535 const char *ads_get_extended_right_name_by_guid(ADS_STRUCT
*ads
,
4536 const char *config_path
,
4537 TALLOC_CTX
*mem_ctx
,
4538 const struct GUID
*rights_guid
)
4541 LDAPMessage
*res
= NULL
;
4543 const char *attrs
[] = { "displayName", NULL
};
4544 const char *result
= NULL
;
4547 if (!ads
|| !mem_ctx
|| !rights_guid
) {
4551 expr
= talloc_asprintf(mem_ctx
, "(rightsGuid=%s)",
4552 GUID_string(mem_ctx
, rights_guid
));
4557 path
= talloc_asprintf(mem_ctx
, "cn=Extended-Rights,%s", config_path
);
4562 rc
= ads_do_search_retry(ads
, path
, LDAP_SCOPE_SUBTREE
,
4564 if (!ADS_ERR_OK(rc
)) {
4568 if (ads_count_replies(ads
, res
) != 1) {
4572 result
= ads_pull_string(ads
, mem_ctx
, res
, "displayName");
4575 ads_msgfree(ads
, res
);
4580 * verify or build and verify an account ou
4581 * @param mem_ctx Pointer to talloc context
4582 * @param ads connection to ads server
4584 * @return status of search
4587 ADS_STATUS
ads_check_ou_dn(TALLOC_CTX
*mem_ctx
,
4589 const char **account_ou
)
4595 if (account_ou
== NULL
) {
4596 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER
);
4599 if (*account_ou
!= NULL
) {
4600 exploded_dn
= ldap_explode_dn(*account_ou
, 0);
4602 ldap_value_free(exploded_dn
);
4607 ou_string
= ads_ou_string(ads
, *account_ou
);
4609 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX
);
4612 name
= talloc_asprintf(mem_ctx
, "%s,%s", ou_string
,
4613 ads
->config
.bind_path
);
4614 SAFE_FREE(ou_string
);
4617 return ADS_ERROR_LDAP(LDAP_NO_MEMORY
);
4620 exploded_dn
= ldap_explode_dn(name
, 0);
4622 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX
);
4624 ldap_value_free(exploded_dn
);