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"
42 * @brief basic ldap client-side routines for ads server communications
44 * The routines contained here should do the necessary ldap calls for
47 * Important note: attribute names passed into ads_ routines must
48 * already be in UTF-8 format. We do not convert them because in almost
49 * all cases, they are just ascii (which is represented with the same
50 * codepoints in UTF-8). This may have to change at some point
54 #define LDAP_SERVER_TREE_DELETE_OID "1.2.840.113556.1.4.805"
56 static SIG_ATOMIC_T gotalarm
;
58 /***************************************************************
59 Signal function to tell us we timed out.
60 ****************************************************************/
62 static void gotalarm_sig(int signum
)
67 LDAP
*ldap_open_with_timeout(const char *server
,
68 struct sockaddr_storage
*ss
,
69 int port
, unsigned int to
)
75 DEBUG(10, ("Opening connection to LDAP server '%s:%d', timeout "
76 "%u seconds\n", server
, port
, to
));
81 CatchSignal(SIGALRM
, gotalarm_sig
);
83 /* End setup timeout. */
86 if ( strchr_m(server
, ':') ) {
88 uri
= talloc_asprintf(talloc_tos(), "ldap://[%s]:%u", server
, port
);
91 uri
= talloc_asprintf(talloc_tos(), "ldap://%s:%u", server
, port
);
97 #ifdef HAVE_LDAP_INIT_FD
100 NTSTATUS status
= NT_STATUS_UNSUCCESSFUL
;
101 unsigned timeout_ms
= 1000 * to
;
103 status
= open_socket_out(ss
, port
, timeout_ms
, &fd
);
104 if (!NT_STATUS_IS_OK(status
)) {
105 DEBUG(3, ("open_socket_out: failed to open socket\n"));
109 /* define LDAP_PROTO_TCP from openldap.h if required */
110 #ifndef LDAP_PROTO_TCP
111 #define LDAP_PROTO_TCP 1
113 ldap_err
= ldap_init_fd(fd
, LDAP_PROTO_TCP
, uri
, &ldp
);
115 #elif defined(HAVE_LDAP_INITIALIZE)
116 ldap_err
= ldap_initialize(&ldp
, uri
);
118 ldp
= ldap_open(server
, port
);
120 ldap_err
= LDAP_SUCCESS
;
122 ldap_err
= LDAP_OTHER
;
125 if (ldap_err
!= LDAP_SUCCESS
) {
126 DEBUG(2,("Could not initialize connection for LDAP server '%s': %s\n",
127 uri
, ldap_err2string(ldap_err
)));
129 DEBUG(10, ("Initialized connection for LDAP server '%s'\n", uri
));
133 /* Teardown timeout. */
135 CatchSignal(SIGALRM
, SIG_IGN
);
141 static int ldap_search_with_timeout(LDAP
*ld
,
142 LDAP_CONST
char *base
,
144 LDAP_CONST
char *filter
,
147 LDAPControl
**sctrls
,
148 LDAPControl
**cctrls
,
152 int to
= lp_ldap_timeout();
153 struct timeval timeout
;
154 struct timeval
*timeout_ptr
= NULL
;
157 /* Setup timeout for the ldap_search_ext_s call - local and remote. */
163 timeout_ptr
= &timeout
;
165 /* Setup alarm timeout. */
166 CatchSignal(SIGALRM
, gotalarm_sig
);
167 /* Make the alarm time one second beyond
168 the timout we're setting for the
169 remote search timeout, to allow that
170 to fire in preference. */
172 /* End setup timeout. */
176 result
= ldap_search_ext_s(ld
, base
, scope
, filter
, attrs
,
177 attrsonly
, sctrls
, cctrls
, timeout_ptr
,
181 /* Teardown alarm timeout. */
182 CatchSignal(SIGALRM
, SIG_IGN
);
187 return LDAP_TIMELIMIT_EXCEEDED
;
190 * A bug in OpenLDAP means ldap_search_ext_s can return
191 * LDAP_SUCCESS but with a NULL res pointer. Cope with
192 * this. See bug #6279 for details. JRA.
196 return LDAP_TIMELIMIT_EXCEEDED
;
202 /**********************************************
203 Do client and server sitename match ?
204 **********************************************/
206 bool ads_sitename_match(ADS_STRUCT
*ads
)
208 if (ads
->config
.server_site_name
== NULL
&&
209 ads
->config
.client_site_name
== NULL
) {
210 DEBUG(10,("ads_sitename_match: both null\n"));
213 if (ads
->config
.server_site_name
&&
214 ads
->config
.client_site_name
&&
215 strequal(ads
->config
.server_site_name
,
216 ads
->config
.client_site_name
)) {
217 DEBUG(10,("ads_sitename_match: name %s match\n", ads
->config
.server_site_name
));
220 DEBUG(10,("ads_sitename_match: no match between server: %s and client: %s\n",
221 ads
->config
.server_site_name
? ads
->config
.server_site_name
: "NULL",
222 ads
->config
.client_site_name
? ads
->config
.client_site_name
: "NULL"));
226 /**********************************************
227 Is this the closest DC ?
228 **********************************************/
230 bool ads_closest_dc(ADS_STRUCT
*ads
)
232 if (ads
->config
.flags
& NBT_SERVER_CLOSEST
) {
233 DEBUG(10,("ads_closest_dc: NBT_SERVER_CLOSEST flag set\n"));
237 /* not sure if this can ever happen */
238 if (ads_sitename_match(ads
)) {
239 DEBUG(10,("ads_closest_dc: NBT_SERVER_CLOSEST flag not set but sites match\n"));
243 if (ads
->config
.client_site_name
== NULL
) {
244 DEBUG(10,("ads_closest_dc: client belongs to no site\n"));
248 DEBUG(10,("ads_closest_dc: %s is not the closest DC\n",
249 ads
->config
.ldap_server_name
));
254 static bool ads_fill_cldap_reply(ADS_STRUCT
*ads
,
256 const struct sockaddr_storage
*ss
,
257 const struct NETLOGON_SAM_LOGON_RESPONSE_EX
*cldap_reply
)
259 TALLOC_CTX
*frame
= talloc_stackframe();
261 char addr
[INET6_ADDRSTRLEN
];
265 print_sockaddr(addr
, sizeof(addr
), ss
);
267 /* Check the CLDAP reply flags */
269 if (!(cldap_reply
->server_type
& NBT_SERVER_LDAP
)) {
270 DBG_WARNING("%s's CLDAP reply says it is not an LDAP server!\n",
276 /* Fill in the ads->config values */
278 ADS_TALLOC_CONST_FREE(ads
->config
.realm
);
279 ADS_TALLOC_CONST_FREE(ads
->config
.bind_path
);
280 ADS_TALLOC_CONST_FREE(ads
->config
.ldap_server_name
);
281 ADS_TALLOC_CONST_FREE(ads
->config
.server_site_name
);
282 ADS_TALLOC_CONST_FREE(ads
->config
.client_site_name
);
283 ADS_TALLOC_CONST_FREE(ads
->server
.workgroup
);
285 if (!check_cldap_reply_required_flags(cldap_reply
->server_type
,
286 ads
->config
.flags
)) {
291 ads
->config
.ldap_server_name
= talloc_strdup(ads
,
292 cldap_reply
->pdc_dns_name
);
293 if (ads
->config
.ldap_server_name
== NULL
) {
294 DBG_WARNING("Out of memory\n");
299 ads
->config
.realm
= talloc_asprintf_strupper_m(ads
,
301 cldap_reply
->dns_domain
);
302 if (ads
->config
.realm
== NULL
) {
303 DBG_WARNING("Out of memory\n");
308 status
= ads_build_dn(ads
->config
.realm
, ads
, &dn
);
309 if (!ADS_ERR_OK(status
)) {
310 DBG_DEBUG("Failed to build bind path: %s\n",
315 ads
->config
.bind_path
= dn
;
317 if (*cldap_reply
->server_site
) {
318 ads
->config
.server_site_name
=
319 talloc_strdup(ads
, cldap_reply
->server_site
);
320 if (ads
->config
.server_site_name
== NULL
) {
321 DBG_WARNING("Out of memory\n");
327 if (*cldap_reply
->client_site
) {
328 ads
->config
.client_site_name
=
329 talloc_strdup(ads
, cldap_reply
->client_site
);
330 if (ads
->config
.client_site_name
== NULL
) {
331 DBG_WARNING("Out of memory\n");
337 ads
->server
.workgroup
= talloc_strdup(ads
, cldap_reply
->domain_name
);
338 if (ads
->server
.workgroup
== NULL
) {
339 DBG_WARNING("Out of memory\n");
344 ads
->ldap
.port
= gc
? LDAP_GC_PORT
: LDAP_PORT
;
347 /* Store our site name. */
348 sitename_store(cldap_reply
->domain_name
, cldap_reply
->client_site
);
349 sitename_store(cldap_reply
->dns_domain
, cldap_reply
->client_site
);
351 /* Leave this until last so that the flags are not clobbered */
352 ads
->config
.flags
= cldap_reply
->server_type
;
363 try a connection to a given ldap server, returning True and setting the servers IP
364 in the ads struct if successful
366 static bool ads_try_connect(ADS_STRUCT
*ads
, bool gc
,
367 struct sockaddr_storage
*ss
)
369 struct NETLOGON_SAM_LOGON_RESPONSE_EX cldap_reply
= {};
370 TALLOC_CTX
*frame
= talloc_stackframe();
372 char addr
[INET6_ADDRSTRLEN
] = { 0, };
379 print_sockaddr(addr
, sizeof(addr
), ss
);
381 DBG_INFO("ads_try_connect: sending CLDAP request to %s (realm: %s)\n",
382 addr
, ads
->server
.realm
);
384 ok
= ads_cldap_netlogon_5(frame
, ss
, ads
->server
.realm
, &cldap_reply
);
386 DBG_NOTICE("ads_cldap_netlogon_5(%s, %s) failed.\n",
387 addr
, ads
->server
.realm
);
392 ok
= ads_fill_cldap_reply(ads
, gc
, ss
, &cldap_reply
);
394 DBG_NOTICE("ads_fill_cldap_reply(%s, %s) failed.\n",
395 addr
, ads
->server
.realm
);
404 /**********************************************************************
405 send a cldap ping to list of servers, one at a time, until one of
406 them answers it's an ldap server. Record success in the ADS_STRUCT.
407 Take note of and update negative connection cache.
408 **********************************************************************/
410 static NTSTATUS
cldap_ping_list(ADS_STRUCT
*ads
,
412 struct samba_sockaddr
*sa_list
,
415 TALLOC_CTX
*frame
= talloc_stackframe();
416 struct timeval endtime
= timeval_current_ofs(MAX(3,lp_ldap_timeout()/2), 0);
417 uint32_t nt_version
= NETLOGON_NT_VERSION_5
| NETLOGON_NT_VERSION_5EX
;
418 struct tsocket_address
**ts_list
= NULL
;
419 const struct tsocket_address
* const *ts_list_const
= NULL
;
420 struct samba_sockaddr
**req_sa_list
= NULL
;
421 struct netlogon_samlogon_response
**responses
= NULL
;
422 size_t num_requests
= 0;
428 ts_list
= talloc_zero_array(frame
,
429 struct tsocket_address
*,
431 if (ts_list
== NULL
) {
433 return NT_STATUS_NO_MEMORY
;
436 req_sa_list
= talloc_zero_array(frame
,
437 struct samba_sockaddr
*,
439 if (req_sa_list
== NULL
) {
441 return NT_STATUS_NO_MEMORY
;
446 * The retry loop is bound by the timeout
450 for (i
= 0; i
< count
; i
++) {
451 char server
[INET6_ADDRSTRLEN
];
454 if (is_zero_addr(&sa_list
[i
].u
.ss
)) {
458 print_sockaddr(server
, sizeof(server
), &sa_list
[i
].u
.ss
);
460 status
= check_negative_conn_cache(domain
, server
);
461 if (!NT_STATUS_IS_OK(status
)) {
465 ret
= tsocket_address_inet_from_strings(ts_list
, "ip",
467 &ts_list
[num_requests
]);
469 status
= map_nt_error_from_unix(errno
);
470 DBG_WARNING("Failed to create tsocket_address for %s - %s\n",
471 server
, nt_errstr(status
));
476 req_sa_list
[num_requests
] = &sa_list
[i
];
480 if (num_requests
== 0) {
481 status
= NT_STATUS_NO_LOGON_SERVERS
;
482 DBG_WARNING("domain[%s] num_requests[%zu] for count[%zu] - %s\n",
483 domain
, num_requests
, count
, nt_errstr(status
));
488 ts_list_const
= (const struct tsocket_address
* const *)ts_list
;
490 status
= cldap_multi_netlogon(frame
,
491 ts_list_const
, num_requests
,
492 ads
->server
.realm
, NULL
,
494 1, endtime
, &responses
);
495 if (!NT_STATUS_IS_OK(status
)) {
496 DBG_WARNING("cldap_multi_netlogon(realm=%s, num_requests=%zu) "
497 "for count[%zu] - %s\n",
502 return NT_STATUS_NO_LOGON_SERVERS
;
505 for (i
= 0; i
< num_requests
; i
++) {
506 struct NETLOGON_SAM_LOGON_RESPONSE_EX
*cldap_reply
= NULL
;
507 char server
[INET6_ADDRSTRLEN
];
509 if (responses
[i
] == NULL
) {
513 print_sockaddr(server
, sizeof(server
), &req_sa_list
[i
]->u
.ss
);
515 if (responses
[i
]->ntver
!= NETLOGON_NT_VERSION_5EX
) {
516 DBG_NOTICE("realm=[%s] nt_version mismatch: 0x%08x for %s\n",
518 responses
[i
]->ntver
, server
);
522 cldap_reply
= &responses
[i
]->data
.nt5_ex
;
524 /* Returns ok only if it matches the correct server type */
525 ok
= ads_fill_cldap_reply(ads
,
527 &req_sa_list
[i
]->u
.ss
,
530 DBG_DEBUG("realm[%s]: selected %s => %s\n",
532 server
, cldap_reply
->pdc_dns_name
);
533 if (CHECK_DEBUGLVL(DBGLVL_DEBUG
)) {
534 NDR_PRINT_DEBUG(NETLOGON_SAM_LOGON_RESPONSE_EX
,
541 DBG_NOTICE("realm[%s] server %s %s - not usable\n",
543 server
, cldap_reply
->pdc_dns_name
);
544 if (CHECK_DEBUGLVL(DBGLVL_NOTICE
)) {
545 NDR_PRINT_DEBUG(NETLOGON_SAM_LOGON_RESPONSE_EX
,
548 add_failed_connection_entry(domain
, server
,
549 NT_STATUS_CLIENT_SERVER_PARAMETERS_INVALID
);
556 expired
= timeval_expired(&endtime
);
562 /* keep track of failures as all were not suitable */
563 for (i
= 0; i
< num_requests
; i
++) {
564 char server
[INET6_ADDRSTRLEN
];
566 print_sockaddr(server
, sizeof(server
), &req_sa_list
[i
]->u
.ss
);
568 add_failed_connection_entry(domain
, server
,
569 NT_STATUS_UNSUCCESSFUL
);
572 status
= NT_STATUS_NO_LOGON_SERVERS
;
573 DBG_WARNING("realm[%s] no valid response "
574 "num_requests[%zu] for count[%zu] - %s\n",
576 num_requests
, count
, nt_errstr(status
));
578 return NT_STATUS_NO_LOGON_SERVERS
;
581 /***************************************************************************
582 resolve a name and perform an "ldap ping" using NetBIOS and related methods
583 ****************************************************************************/
585 static NTSTATUS
resolve_and_ping_netbios(ADS_STRUCT
*ads
,
586 const char *domain
, const char *realm
)
590 struct samba_sockaddr
*sa_list
= NULL
;
593 DEBUG(6, ("resolve_and_ping_netbios: (cldap) looking for domain '%s'\n",
596 status
= get_sorted_dc_list(talloc_tos(),
602 if (!NT_STATUS_IS_OK(status
)) {
606 /* remove servers which are known to be dead based on
607 the corresponding DNS method */
609 for (i
= 0; i
< count
; ++i
) {
610 char server
[INET6_ADDRSTRLEN
];
612 print_sockaddr(server
, sizeof(server
), &sa_list
[i
].u
.ss
);
615 check_negative_conn_cache(realm
, server
))) {
616 /* Ensure we add the workgroup name for this
617 IP address as negative too. */
618 add_failed_connection_entry(
620 NT_STATUS_UNSUCCESSFUL
);
625 status
= cldap_ping_list(ads
, domain
, sa_list
, count
);
627 TALLOC_FREE(sa_list
);
633 /**********************************************************************
634 resolve a name and perform an "ldap ping" using DNS
635 **********************************************************************/
637 static NTSTATUS
resolve_and_ping_dns(ADS_STRUCT
*ads
, const char *sitename
,
641 struct samba_sockaddr
*sa_list
= NULL
;
644 DEBUG(6, ("resolve_and_ping_dns: (cldap) looking for realm '%s'\n",
647 status
= get_sorted_dc_list(talloc_tos(),
653 if (!NT_STATUS_IS_OK(status
)) {
654 TALLOC_FREE(sa_list
);
658 status
= cldap_ping_list(ads
, realm
, sa_list
, count
);
660 TALLOC_FREE(sa_list
);
665 /**********************************************************************
666 Try to find an AD dc using our internal name resolution routines
667 Try the realm first and then then workgroup name if netbios is not
669 **********************************************************************/
671 static NTSTATUS
ads_find_dc(ADS_STRUCT
*ads
)
673 const char *c_domain
= "";
675 bool use_own_domain
= False
;
676 char *sitename
= NULL
;
677 NTSTATUS status
= NT_STATUS_UNSUCCESSFUL
;
680 /* if the realm and workgroup are both empty, assume they are ours */
683 c_realm
= ads
->server
.realm
;
689 /* special case where no realm and no workgroup means our own */
690 if ( !ads
->server
.workgroup
|| !*ads
->server
.workgroup
) {
691 use_own_domain
= True
;
692 c_realm
= lp_realm();
696 if (!lp_disable_netbios()) {
697 if (use_own_domain
) {
698 c_domain
= lp_workgroup();
700 c_domain
= ads
->server
.workgroup
;
701 if (!*c_realm
&& (!c_domain
|| !*c_domain
)) {
702 c_domain
= lp_workgroup();
711 if (!*c_realm
&& !*c_domain
) {
712 DEBUG(0, ("ads_find_dc: no realm or workgroup! Don't know "
714 return NT_STATUS_INVALID_PARAMETER
; /* rather need MISSING_PARAMETER ... */
718 * In case of LDAP we use get_dc_name() as that
719 * creates the custom krb5.conf file
721 if (!(ads
->auth
.flags
& ADS_AUTH_NO_BIND
)) {
723 struct sockaddr_storage ip_out
;
725 DEBUG(6, ("ads_find_dc: (ldap) looking for realm '%s'"
726 " and falling back to domain '%s'\n",
729 ok
= get_dc_name(c_domain
, c_realm
, srv_name
, &ip_out
);
731 if (is_zero_addr(&ip_out
)) {
732 return NT_STATUS_NO_LOGON_SERVERS
;
736 * we call ads_try_connect() to fill in the
737 * ads->config details
739 ok
= ads_try_connect(ads
, false, &ip_out
);
745 return NT_STATUS_NO_LOGON_SERVERS
;
749 sitename
= sitename_fetch(talloc_tos(), c_realm
);
750 status
= resolve_and_ping_dns(ads
, sitename
, c_realm
);
752 if (NT_STATUS_IS_OK(status
)) {
753 TALLOC_FREE(sitename
);
757 /* In case we failed to contact one of our closest DC on our
759 * need to try to find another DC, retry with a site-less SRV
764 DEBUG(3, ("ads_find_dc: failed to find a valid DC on "
765 "our site (%s), Trying to find another DC "
766 "for realm '%s' (domain '%s')\n",
767 sitename
, c_realm
, c_domain
));
768 namecache_delete(c_realm
, 0x1C);
770 resolve_and_ping_dns(ads
, NULL
, c_realm
);
772 if (NT_STATUS_IS_OK(status
)) {
773 TALLOC_FREE(sitename
);
778 TALLOC_FREE(sitename
);
781 /* try netbios as fallback - if permitted,
782 or if configuration specifically requests it */
785 DEBUG(3, ("ads_find_dc: falling back to netbios "
786 "name resolution for domain '%s' (realm '%s')\n",
790 status
= resolve_and_ping_netbios(ads
, c_domain
, c_realm
);
791 if (NT_STATUS_IS_OK(status
)) {
796 DEBUG(1, ("ads_find_dc: "
797 "name resolution for realm '%s' (domain '%s') failed: %s\n",
798 c_realm
, c_domain
, nt_errstr(status
)));
802 * Connect to the LDAP server
803 * @param ads Pointer to an existing ADS_STRUCT
804 * @return status of connection
806 ADS_STATUS
ads_connect(ADS_STRUCT
*ads
)
808 int version
= LDAP_VERSION3
;
811 char addr
[INET6_ADDRSTRLEN
];
812 struct sockaddr_storage existing_ss
;
814 zero_sockaddr(&existing_ss
);
817 * ads_connect can be passed in a reused ADS_STRUCT
818 * with an existing non-zero ads->ldap.ss IP address
819 * that was stored by going through ads_find_dc()
820 * if ads->server.ldap_server was NULL.
822 * If ads->server.ldap_server is still NULL but
823 * the target address isn't the zero address, then
824 * store that address off off before zeroing out
825 * ads->ldap so we don't keep doing multiple calls
826 * to ads_find_dc() in the reuse case.
828 * If a caller wants a clean ADS_STRUCT they
829 * will TALLOC_FREE it and allocate a new one
830 * by calling ads_init(), which ensures
831 * ads->ldap.ss is a properly zero'ed out valid IP
834 if (ads
->server
.ldap_server
== NULL
&& !is_zero_addr(&ads
->ldap
.ss
)) {
835 /* Save off the address we previously found by ads_find_dc(). */
836 existing_ss
= ads
->ldap
.ss
;
840 ZERO_STRUCT(ads
->ldap_wrap_data
);
841 ads
->ldap
.last_attempt
= time_mono(NULL
);
842 ads
->ldap_wrap_data
.wrap_type
= ADS_SASLWRAP_TYPE_PLAIN
;
844 /* try with a user specified server */
846 if (DEBUGLEVEL
>= 11) {
847 char *s
= NDR_PRINT_STRUCT_STRING(talloc_tos(), ads_struct
, ads
);
848 DEBUG(11,("ads_connect: entering\n"));
849 DEBUGADD(11,("%s\n", s
));
853 if (ads
->server
.ldap_server
) {
855 struct sockaddr_storage ss
;
857 ok
= resolve_name(ads
->server
.ldap_server
, &ss
, 0x20, true);
859 DEBUG(5,("ads_connect: unable to resolve name %s\n",
860 ads
->server
.ldap_server
));
861 status
= ADS_ERROR_NT(NT_STATUS_NOT_FOUND
);
865 if (is_zero_addr(&ss
)) {
866 status
= ADS_ERROR_NT(NT_STATUS_NOT_FOUND
);
870 ok
= ads_try_connect(ads
, ads
->server
.gc
, &ss
);
875 /* The choice of which GC use is handled one level up in
876 ads_connect_gc(). If we continue on from here with
877 ads_find_dc() we will get GC searches on port 389 which
878 doesn't work. --jerry */
880 if (ads
->server
.gc
== true) {
881 return ADS_ERROR(LDAP_OPERATIONS_ERROR
);
884 if (ads
->server
.no_fallback
) {
885 status
= ADS_ERROR_NT(NT_STATUS_NOT_FOUND
);
890 if (!is_zero_addr(&existing_ss
)) {
891 /* We saved off who we should talk to. */
892 bool ok
= ads_try_connect(ads
,
899 * Keep trying to find a server and fall through
900 * into ads_find_dc() again.
904 ntstatus
= ads_find_dc(ads
);
905 if (NT_STATUS_IS_OK(ntstatus
)) {
909 status
= ADS_ERROR_NT(ntstatus
);
914 print_sockaddr(addr
, sizeof(addr
), &ads
->ldap
.ss
);
915 DEBUG(3,("Successfully contacted LDAP server %s\n", addr
));
917 if (!ads
->auth
.user_name
) {
918 /* Must use the userPrincipalName value here or sAMAccountName
919 and not servicePrincipalName; found by Guenther Deschner */
920 ads
->auth
.user_name
= talloc_asprintf(ads
,
923 if (ads
->auth
.user_name
== NULL
) {
924 DBG_ERR("talloc_asprintf failed\n");
925 status
= ADS_ERROR_NT(NT_STATUS_NO_MEMORY
);
930 if (ads
->auth
.realm
== NULL
) {
931 ads
->auth
.realm
= talloc_strdup(ads
, ads
->config
.realm
);
932 if (ads
->auth
.realm
== NULL
) {
933 status
= ADS_ERROR_NT(NT_STATUS_NO_MEMORY
);
938 if (!ads
->auth
.kdc_server
) {
939 print_sockaddr(addr
, sizeof(addr
), &ads
->ldap
.ss
);
940 ads
->auth
.kdc_server
= talloc_strdup(ads
, addr
);
941 if (ads
->auth
.kdc_server
== NULL
) {
942 status
= ADS_ERROR_NT(NT_STATUS_NO_MEMORY
);
947 /* If the caller() requested no LDAP bind, then we are done */
949 if (ads
->auth
.flags
& ADS_AUTH_NO_BIND
) {
950 status
= ADS_SUCCESS
;
954 ads
->ldap_wrap_data
.mem_ctx
= talloc_init("ads LDAP connection memory");
955 if (!ads
->ldap_wrap_data
.mem_ctx
) {
956 status
= ADS_ERROR_NT(NT_STATUS_NO_MEMORY
);
960 /* Otherwise setup the TCP LDAP session */
962 ads
->ldap
.ld
= ldap_open_with_timeout(ads
->config
.ldap_server_name
,
964 ads
->ldap
.port
, lp_ldap_timeout());
965 if (ads
->ldap
.ld
== NULL
) {
966 status
= ADS_ERROR(LDAP_OPERATIONS_ERROR
);
969 DEBUG(3,("Connected to LDAP server %s\n", ads
->config
.ldap_server_name
));
971 /* cache the successful connection for workgroup and realm */
972 if (ads_closest_dc(ads
)) {
973 saf_store( ads
->server
.workgroup
, ads
->config
.ldap_server_name
);
974 saf_store( ads
->server
.realm
, ads
->config
.ldap_server_name
);
977 ldap_set_option(ads
->ldap
.ld
, LDAP_OPT_PROTOCOL_VERSION
, &version
);
979 /* fill in the current time and offsets */
981 status
= ads_current_time( ads
);
982 if ( !ADS_ERR_OK(status
) ) {
986 /* Now do the bind */
988 if (ads
->auth
.flags
& ADS_AUTH_ANON_BIND
) {
989 status
= ADS_ERROR(ldap_simple_bind_s(ads
->ldap
.ld
, NULL
, NULL
));
993 if (ads
->auth
.flags
& ADS_AUTH_SIMPLE_BIND
) {
994 status
= ADS_ERROR(ldap_simple_bind_s(ads
->ldap
.ld
, ads
->auth
.user_name
, ads
->auth
.password
));
998 status
= ads_sasl_bind(ads
);
1001 if (DEBUGLEVEL
>= 11) {
1002 char *s
= NDR_PRINT_STRUCT_STRING(talloc_tos(), ads_struct
, ads
);
1003 DEBUG(11,("ads_connect: leaving with: %s\n",
1004 ads_errstr(status
)));
1005 DEBUGADD(11,("%s\n", s
));
1013 * Connect to the LDAP server using given credentials
1014 * @param ads Pointer to an existing ADS_STRUCT
1015 * @return status of connection
1017 ADS_STATUS
ads_connect_user_creds(ADS_STRUCT
*ads
)
1019 ads
->auth
.flags
|= ADS_AUTH_USER_CREDS
;
1021 return ads_connect(ads
);
1025 * Zero out the internal ads->ldap struct and initialize the address to zero IP.
1026 * @param ads Pointer to an existing ADS_STRUCT
1028 * Sets the ads->ldap.ss to a valid
1029 * zero ip address that can be detected by
1030 * our is_zero_addr() function. Otherwise
1031 * it is left as AF_UNSPEC (0).
1033 void ads_zero_ldap(ADS_STRUCT
*ads
)
1035 ZERO_STRUCT(ads
->ldap
);
1037 * Initialize the sockaddr_storage so we can use
1038 * sockaddr test functions against it.
1040 zero_sockaddr(&ads
->ldap
.ss
);
1044 * Disconnect the LDAP server
1045 * @param ads Pointer to an existing ADS_STRUCT
1047 void ads_disconnect(ADS_STRUCT
*ads
)
1050 ldap_unbind(ads
->ldap
.ld
);
1051 ads
->ldap
.ld
= NULL
;
1053 if (ads
->ldap_wrap_data
.wrap_ops
&&
1054 ads
->ldap_wrap_data
.wrap_ops
->disconnect
) {
1055 ads
->ldap_wrap_data
.wrap_ops
->disconnect(&ads
->ldap_wrap_data
);
1057 if (ads
->ldap_wrap_data
.mem_ctx
) {
1058 talloc_free(ads
->ldap_wrap_data
.mem_ctx
);
1061 ZERO_STRUCT(ads
->ldap_wrap_data
);
1065 Duplicate a struct berval into talloc'ed memory
1067 static struct berval
*dup_berval(TALLOC_CTX
*ctx
, const struct berval
*in_val
)
1069 struct berval
*value
;
1071 if (!in_val
) return NULL
;
1073 value
= talloc_zero(ctx
, struct berval
);
1076 if (in_val
->bv_len
== 0) return value
;
1078 value
->bv_len
= in_val
->bv_len
;
1079 value
->bv_val
= (char *)talloc_memdup(ctx
, in_val
->bv_val
,
1085 Make a values list out of an array of (struct berval *)
1087 static struct berval
**ads_dup_values(TALLOC_CTX
*ctx
,
1088 const struct berval
**in_vals
)
1090 struct berval
**values
;
1093 if (!in_vals
) return NULL
;
1094 for (i
=0; in_vals
[i
]; i
++)
1095 ; /* count values */
1096 values
= talloc_zero_array(ctx
, struct berval
*, i
+1);
1097 if (!values
) return NULL
;
1099 for (i
=0; in_vals
[i
]; i
++) {
1100 values
[i
] = dup_berval(ctx
, in_vals
[i
]);
1106 UTF8-encode a values list out of an array of (char *)
1108 static char **ads_push_strvals(TALLOC_CTX
*ctx
, const char **in_vals
)
1114 if (!in_vals
) return NULL
;
1115 for (i
=0; in_vals
[i
]; i
++)
1116 ; /* count values */
1117 values
= talloc_zero_array(ctx
, char *, i
+1);
1118 if (!values
) return NULL
;
1120 for (i
=0; in_vals
[i
]; i
++) {
1121 if (!push_utf8_talloc(ctx
, &values
[i
], in_vals
[i
], &size
)) {
1122 TALLOC_FREE(values
);
1130 Pull a (char *) array out of a UTF8-encoded values list
1132 static char **ads_pull_strvals(TALLOC_CTX
*ctx
, const char **in_vals
)
1136 size_t converted_size
;
1138 if (!in_vals
) return NULL
;
1139 for (i
=0; in_vals
[i
]; i
++)
1140 ; /* count values */
1141 values
= talloc_zero_array(ctx
, char *, i
+1);
1142 if (!values
) return NULL
;
1144 for (i
=0; in_vals
[i
]; i
++) {
1145 if (!pull_utf8_talloc(ctx
, &values
[i
], in_vals
[i
],
1147 DEBUG(0,("ads_pull_strvals: pull_utf8_talloc failed: "
1148 "%s", strerror(errno
)));
1155 * Do a search with paged results. cookie must be null on the first
1156 * call, and then returned on each subsequent call. It will be null
1157 * again when the entire search is complete
1158 * @param ads connection to ads server
1159 * @param bind_path Base dn for the search
1160 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1161 * @param expr Search expression - specified in local charset
1162 * @param attrs Attributes to retrieve - specified in utf8 or ascii
1163 * @param res ** which will contain results - free res* with ads_msgfree()
1164 * @param count Number of entries retrieved on this page
1165 * @param cookie The paged results cookie to be returned on subsequent calls
1166 * @return status of search
1168 static ADS_STATUS
ads_do_paged_search_args(ADS_STRUCT
*ads
,
1169 const char *bind_path
,
1170 int scope
, const char *expr
,
1171 const char **attrs
, void *args
,
1173 int *count
, struct berval
**cookie
)
1176 char *utf8_expr
, *utf8_path
, **search_attrs
= NULL
;
1177 size_t converted_size
;
1178 LDAPControl PagedResults
, NoReferrals
, ExternalCtrl
, *controls
[4], **rcontrols
;
1179 BerElement
*cookie_be
= NULL
;
1180 struct berval
*cookie_bv
= NULL
;
1181 BerElement
*ext_be
= NULL
;
1182 struct berval
*ext_bv
= NULL
;
1185 ads_control
*external_control
= (ads_control
*) args
;
1189 if (!(ctx
= talloc_init("ads_do_paged_search_args")))
1190 return ADS_ERROR(LDAP_NO_MEMORY
);
1192 /* 0 means the conversion worked but the result was empty
1193 so we only fail if it's -1. In any case, it always
1194 at least nulls out the dest */
1195 if (!push_utf8_talloc(ctx
, &utf8_expr
, expr
, &converted_size
) ||
1196 !push_utf8_talloc(ctx
, &utf8_path
, bind_path
, &converted_size
))
1198 rc
= LDAP_NO_MEMORY
;
1202 if (!attrs
|| !(*attrs
))
1203 search_attrs
= NULL
;
1205 /* This would be the utf8-encoded version...*/
1206 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
1207 if (!(search_attrs
= str_list_copy(talloc_tos(), attrs
))) {
1208 rc
= LDAP_NO_MEMORY
;
1213 /* Paged results only available on ldap v3 or later */
1214 ldap_get_option(ads
->ldap
.ld
, LDAP_OPT_PROTOCOL_VERSION
, &version
);
1215 if (version
< LDAP_VERSION3
) {
1216 rc
= LDAP_NOT_SUPPORTED
;
1220 cookie_be
= ber_alloc_t(LBER_USE_DER
);
1222 ber_printf(cookie_be
, "{iO}", (ber_int_t
) ads
->config
.ldap_page_size
, *cookie
);
1223 ber_bvfree(*cookie
); /* don't need it from last time */
1226 ber_printf(cookie_be
, "{io}", (ber_int_t
) ads
->config
.ldap_page_size
, "", 0);
1228 ber_flatten(cookie_be
, &cookie_bv
);
1229 PagedResults
.ldctl_oid
= discard_const_p(char, ADS_PAGE_CTL_OID
);
1230 PagedResults
.ldctl_iscritical
= (char) 1;
1231 PagedResults
.ldctl_value
.bv_len
= cookie_bv
->bv_len
;
1232 PagedResults
.ldctl_value
.bv_val
= cookie_bv
->bv_val
;
1234 NoReferrals
.ldctl_oid
= discard_const_p(char, ADS_NO_REFERRALS_OID
);
1235 NoReferrals
.ldctl_iscritical
= (char) 0;
1236 NoReferrals
.ldctl_value
.bv_len
= 0;
1237 NoReferrals
.ldctl_value
.bv_val
= discard_const_p(char, "");
1239 if (external_control
&&
1240 (strequal(external_control
->control
, ADS_EXTENDED_DN_OID
) ||
1241 strequal(external_control
->control
, ADS_SD_FLAGS_OID
))) {
1243 ExternalCtrl
.ldctl_oid
= discard_const_p(char, external_control
->control
);
1244 ExternalCtrl
.ldctl_iscritical
= (char) external_control
->critical
;
1246 /* win2k does not accept a ldctl_value beeing passed in */
1248 if (external_control
->val
!= 0) {
1250 if ((ext_be
= ber_alloc_t(LBER_USE_DER
)) == NULL
) {
1251 rc
= LDAP_NO_MEMORY
;
1255 if ((ber_printf(ext_be
, "{i}", (ber_int_t
) external_control
->val
)) == -1) {
1256 rc
= LDAP_NO_MEMORY
;
1259 if ((ber_flatten(ext_be
, &ext_bv
)) == -1) {
1260 rc
= LDAP_NO_MEMORY
;
1264 ExternalCtrl
.ldctl_value
.bv_len
= ext_bv
->bv_len
;
1265 ExternalCtrl
.ldctl_value
.bv_val
= ext_bv
->bv_val
;
1268 ExternalCtrl
.ldctl_value
.bv_len
= 0;
1269 ExternalCtrl
.ldctl_value
.bv_val
= NULL
;
1272 controls
[0] = &NoReferrals
;
1273 controls
[1] = &PagedResults
;
1274 controls
[2] = &ExternalCtrl
;
1278 controls
[0] = &NoReferrals
;
1279 controls
[1] = &PagedResults
;
1283 /* we need to disable referrals as the openldap libs don't
1284 handle them and paged results at the same time. Using them
1285 together results in the result record containing the server
1286 page control being removed from the result list (tridge/jmcd)
1288 leaving this in despite the control that says don't generate
1289 referrals, in case the server doesn't support it (jmcd)
1291 ldap_set_option(ads
->ldap
.ld
, LDAP_OPT_REFERRALS
, LDAP_OPT_OFF
);
1293 rc
= ldap_search_with_timeout(ads
->ldap
.ld
, utf8_path
, scope
, utf8_expr
,
1294 search_attrs
, 0, controls
,
1295 NULL
, LDAP_NO_LIMIT
,
1296 (LDAPMessage
**)res
);
1298 ber_free(cookie_be
, 1);
1299 ber_bvfree(cookie_bv
);
1302 DEBUG(3,("ads_do_paged_search_args: ldap_search_with_timeout(%s) -> %s\n", expr
,
1303 ldap_err2string(rc
)));
1304 if (rc
== LDAP_OTHER
) {
1308 ret
= ldap_parse_result(ads
->ldap
.ld
,
1316 if (ret
== LDAP_SUCCESS
) {
1317 DEBUG(3, ("ldap_search_with_timeout(%s) "
1318 "error: %s\n", expr
, ldap_errmsg
));
1319 ldap_memfree(ldap_errmsg
);
1325 rc
= ldap_parse_result(ads
->ldap
.ld
, *res
, NULL
, NULL
, NULL
,
1326 NULL
, &rcontrols
, 0);
1332 for (i
=0; rcontrols
[i
]; i
++) {
1333 if (strcmp(ADS_PAGE_CTL_OID
, rcontrols
[i
]->ldctl_oid
) == 0) {
1334 cookie_be
= ber_init(&rcontrols
[i
]->ldctl_value
);
1335 ber_scanf(cookie_be
,"{iO}", (ber_int_t
*) count
,
1337 /* the berval is the cookie, but must be freed when
1339 if (cookie_bv
->bv_len
) /* still more to do */
1340 *cookie
=ber_bvdup(cookie_bv
);
1343 ber_bvfree(cookie_bv
);
1344 ber_free(cookie_be
, 1);
1348 ldap_controls_free(rcontrols
);
1351 talloc_destroy(ctx
);
1354 ber_free(ext_be
, 1);
1361 if (rc
!= LDAP_SUCCESS
&& *res
!= NULL
) {
1362 ads_msgfree(ads
, *res
);
1366 /* if/when we decide to utf8-encode attrs, take out this next line */
1367 TALLOC_FREE(search_attrs
);
1369 return ADS_ERROR(rc
);
1372 static ADS_STATUS
ads_do_paged_search(ADS_STRUCT
*ads
, const char *bind_path
,
1373 int scope
, const char *expr
,
1374 const char **attrs
, LDAPMessage
**res
,
1375 int *count
, struct berval
**cookie
)
1377 return ads_do_paged_search_args(ads
, bind_path
, scope
, expr
, attrs
, NULL
, res
, count
, cookie
);
1382 * Get all results for a search. This uses ads_do_paged_search() to return
1383 * all entries in a large search.
1384 * @param ads connection to ads server
1385 * @param bind_path Base dn for the search
1386 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1387 * @param expr Search expression
1388 * @param attrs Attributes to retrieve
1389 * @param res ** which will contain results - free res* with ads_msgfree()
1390 * @return status of search
1392 ADS_STATUS
ads_do_search_all_args(ADS_STRUCT
*ads
, const char *bind_path
,
1393 int scope
, const char *expr
,
1394 const char **attrs
, void *args
,
1397 struct berval
*cookie
= NULL
;
1402 status
= ads_do_paged_search_args(ads
, bind_path
, scope
, expr
, attrs
, args
, res
,
1405 if (!ADS_ERR_OK(status
))
1408 #ifdef HAVE_LDAP_ADD_RESULT_ENTRY
1410 LDAPMessage
*res2
= NULL
;
1411 LDAPMessage
*msg
, *next
;
1413 status
= ads_do_paged_search_args(ads
, bind_path
, scope
, expr
,
1414 attrs
, args
, &res2
, &count
, &cookie
);
1415 if (!ADS_ERR_OK(status
)) {
1419 /* this relies on the way that ldap_add_result_entry() works internally. I hope
1420 that this works on all ldap libs, but I have only tested with openldap */
1421 for (msg
= ads_first_message(ads
, res2
); msg
; msg
= next
) {
1422 next
= ads_next_message(ads
, msg
);
1423 ldap_add_result_entry((LDAPMessage
**)res
, msg
);
1425 /* note that we do not free res2, as the memory is now
1426 part of the main returned list */
1429 DEBUG(0, ("no ldap_add_result_entry() support in LDAP libs!\n"));
1430 status
= ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL
);
1436 ADS_STATUS
ads_do_search_all(ADS_STRUCT
*ads
, const char *bind_path
,
1437 int scope
, const char *expr
,
1438 const char **attrs
, LDAPMessage
**res
)
1440 return ads_do_search_all_args(ads
, bind_path
, scope
, expr
, attrs
, NULL
, res
);
1443 ADS_STATUS
ads_do_search_all_sd_flags(ADS_STRUCT
*ads
, const char *bind_path
,
1444 int scope
, const char *expr
,
1445 const char **attrs
, uint32_t sd_flags
,
1450 args
.control
= ADS_SD_FLAGS_OID
;
1451 args
.val
= sd_flags
;
1452 args
.critical
= True
;
1454 return ads_do_search_all_args(ads
, bind_path
, scope
, expr
, attrs
, &args
, res
);
1459 * Run a function on all results for a search. Uses ads_do_paged_search() and
1460 * runs the function as each page is returned, using ads_process_results()
1461 * @param ads connection to ads server
1462 * @param bind_path Base dn for the search
1463 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1464 * @param expr Search expression - specified in local charset
1465 * @param attrs Attributes to retrieve - specified in UTF-8 or ascii
1466 * @param fn Function which takes attr name, values list, and data_area
1467 * @param data_area Pointer which is passed to function on each call
1468 * @return status of search
1470 ADS_STATUS
ads_do_search_all_fn(ADS_STRUCT
*ads
, const char *bind_path
,
1471 int scope
, const char *expr
, const char **attrs
,
1472 bool (*fn
)(ADS_STRUCT
*, char *, void **, void *),
1475 struct berval
*cookie
= NULL
;
1480 status
= ads_do_paged_search(ads
, bind_path
, scope
, expr
, attrs
, &res
,
1483 if (!ADS_ERR_OK(status
)) return status
;
1485 ads_process_results(ads
, res
, fn
, data_area
);
1486 ads_msgfree(ads
, res
);
1489 status
= ads_do_paged_search(ads
, bind_path
, scope
, expr
, attrs
,
1490 &res
, &count
, &cookie
);
1492 if (!ADS_ERR_OK(status
)) break;
1494 ads_process_results(ads
, res
, fn
, data_area
);
1495 ads_msgfree(ads
, res
);
1502 * Do a search with a timeout.
1503 * @param ads connection to ads server
1504 * @param bind_path Base dn for the search
1505 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1506 * @param expr Search expression
1507 * @param attrs Attributes to retrieve
1508 * @param res ** which will contain results - free res* with ads_msgfree()
1509 * @return status of search
1511 ADS_STATUS
ads_do_search(ADS_STRUCT
*ads
, const char *bind_path
, int scope
,
1513 const char **attrs
, LDAPMessage
**res
)
1516 char *utf8_expr
, *utf8_path
, **search_attrs
= NULL
;
1517 size_t converted_size
;
1521 if (!(ctx
= talloc_init("ads_do_search"))) {
1522 DEBUG(1,("ads_do_search: talloc_init() failed!"));
1523 return ADS_ERROR(LDAP_NO_MEMORY
);
1526 /* 0 means the conversion worked but the result was empty
1527 so we only fail if it's negative. In any case, it always
1528 at least nulls out the dest */
1529 if (!push_utf8_talloc(ctx
, &utf8_expr
, expr
, &converted_size
) ||
1530 !push_utf8_talloc(ctx
, &utf8_path
, bind_path
, &converted_size
))
1532 DEBUG(1,("ads_do_search: push_utf8_talloc() failed!"));
1533 rc
= LDAP_NO_MEMORY
;
1537 if (!attrs
|| !(*attrs
))
1538 search_attrs
= NULL
;
1540 /* This would be the utf8-encoded version...*/
1541 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
1542 if (!(search_attrs
= str_list_copy(talloc_tos(), attrs
)))
1544 DEBUG(1,("ads_do_search: str_list_copy() failed!"));
1545 rc
= LDAP_NO_MEMORY
;
1550 /* see the note in ads_do_paged_search - we *must* disable referrals */
1551 ldap_set_option(ads
->ldap
.ld
, LDAP_OPT_REFERRALS
, LDAP_OPT_OFF
);
1553 rc
= ldap_search_with_timeout(ads
->ldap
.ld
, utf8_path
, scope
, utf8_expr
,
1554 search_attrs
, 0, NULL
, NULL
,
1556 (LDAPMessage
**)res
);
1558 if (rc
== LDAP_SIZELIMIT_EXCEEDED
) {
1559 DEBUG(3,("Warning! sizelimit exceeded in ldap. Truncating.\n"));
1564 talloc_destroy(ctx
);
1565 /* if/when we decide to utf8-encode attrs, take out this next line */
1566 TALLOC_FREE(search_attrs
);
1567 return ADS_ERROR(rc
);
1570 * Do a general ADS search
1571 * @param ads connection to ads server
1572 * @param res ** which will contain results - free res* with ads_msgfree()
1573 * @param expr Search expression
1574 * @param attrs Attributes to retrieve
1575 * @return status of search
1577 ADS_STATUS
ads_search(ADS_STRUCT
*ads
, LDAPMessage
**res
,
1578 const char *expr
, const char **attrs
)
1580 return ads_do_search(ads
, ads
->config
.bind_path
, LDAP_SCOPE_SUBTREE
,
1585 * Do a search on a specific DistinguishedName
1586 * @param ads connection to ads server
1587 * @param res ** which will contain results - free res* with ads_msgfree()
1588 * @param dn DistinguishName to search
1589 * @param attrs Attributes to retrieve
1590 * @return status of search
1592 ADS_STATUS
ads_search_dn(ADS_STRUCT
*ads
, LDAPMessage
**res
,
1593 const char *dn
, const char **attrs
)
1595 return ads_do_search(ads
, dn
, LDAP_SCOPE_BASE
, "(objectclass=*)",
1600 * Free up memory from a ads_search
1601 * @param ads connection to ads server
1602 * @param msg Search results to free
1604 void ads_msgfree(ADS_STRUCT
*ads
, LDAPMessage
*msg
)
1611 * Get a dn from search results
1612 * @param ads connection to ads server
1613 * @param msg Search result
1616 char *ads_get_dn(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
, LDAPMessage
*msg
)
1618 char *utf8_dn
, *unix_dn
;
1619 size_t converted_size
;
1621 utf8_dn
= ldap_get_dn(ads
->ldap
.ld
, msg
);
1624 DEBUG (5, ("ads_get_dn: ldap_get_dn failed\n"));
1628 if (!pull_utf8_talloc(mem_ctx
, &unix_dn
, utf8_dn
, &converted_size
)) {
1629 DEBUG(0,("ads_get_dn: string conversion failure utf8 [%s]\n",
1633 ldap_memfree(utf8_dn
);
1638 * Get the parent from a dn
1639 * @param dn the dn to return the parent from
1640 * @return parent dn string
1642 char *ads_parent_dn(const char *dn
)
1650 p
= strchr(dn
, ',');
1660 * Find a machine account given a hostname
1661 * @param ads connection to ads server
1662 * @param res ** which will contain results - free res* with ads_msgfree()
1663 * @param host Hostname to search for
1664 * @return status of search
1666 ADS_STATUS
ads_find_machine_acct(ADS_STRUCT
*ads
, LDAPMessage
**res
,
1667 const char *machine
)
1671 const char *attrs
[] = {
1672 /* This is how Windows checks for machine accounts */
1675 "userAccountControl",
1677 "ServicePrincipalName",
1678 "userPrincipalName",
1681 /* Additional attributes Samba checks */
1682 "msDS-AdditionalDnsHostName",
1683 "msDS-SupportedEncryptionTypes",
1684 "nTSecurityDescriptor",
1689 TALLOC_CTX
*frame
= talloc_stackframe();
1693 /* the easiest way to find a machine account anywhere in the tree
1694 is to look for hostname$ */
1695 expr
= talloc_asprintf(frame
, "(samAccountName=%s$)", machine
);
1697 status
= ADS_ERROR_NT(NT_STATUS_NO_MEMORY
);
1701 status
= ads_search(ads
, res
, expr
, attrs
);
1702 if (ADS_ERR_OK(status
)) {
1703 if (ads_count_replies(ads
, *res
) != 1) {
1704 status
= ADS_ERROR_LDAP(LDAP_NO_SUCH_OBJECT
);
1714 * Initialize a list of mods to be used in a modify request
1715 * @param ctx An initialized TALLOC_CTX
1716 * @return allocated ADS_MODLIST
1718 ADS_MODLIST
ads_init_mods(TALLOC_CTX
*ctx
)
1720 #define ADS_MODLIST_ALLOC_SIZE 10
1723 if ((mods
= talloc_zero_array(ctx
, LDAPMod
*, ADS_MODLIST_ALLOC_SIZE
+ 1)))
1724 /* -1 is safety to make sure we don't go over the end.
1725 need to reset it to NULL before doing ldap modify */
1726 mods
[ADS_MODLIST_ALLOC_SIZE
] = (LDAPMod
*) -1;
1728 return (ADS_MODLIST
)mods
;
1733 add an attribute to the list, with values list already constructed
1735 static ADS_STATUS
ads_modlist_add(TALLOC_CTX
*ctx
, ADS_MODLIST
*mods
,
1736 int mod_op
, const char *name
,
1737 const void *_invals
)
1740 LDAPMod
**modlist
= (LDAPMod
**) *mods
;
1741 struct berval
**ber_values
= NULL
;
1742 char **char_values
= NULL
;
1745 mod_op
= LDAP_MOD_DELETE
;
1747 if (mod_op
& LDAP_MOD_BVALUES
) {
1748 const struct berval
**b
;
1749 b
= discard_const_p(const struct berval
*, _invals
);
1750 ber_values
= ads_dup_values(ctx
, b
);
1753 c
= discard_const_p(const char *, _invals
);
1754 char_values
= ads_push_strvals(ctx
, c
);
1758 /* find the first empty slot */
1759 for (curmod
=0; modlist
[curmod
] && modlist
[curmod
] != (LDAPMod
*) -1;
1761 if (modlist
[curmod
] == (LDAPMod
*) -1) {
1762 if (!(modlist
= talloc_realloc(ctx
, modlist
, LDAPMod
*,
1763 curmod
+ADS_MODLIST_ALLOC_SIZE
+1)))
1764 return ADS_ERROR(LDAP_NO_MEMORY
);
1765 memset(&modlist
[curmod
], 0,
1766 ADS_MODLIST_ALLOC_SIZE
*sizeof(LDAPMod
*));
1767 modlist
[curmod
+ADS_MODLIST_ALLOC_SIZE
] = (LDAPMod
*) -1;
1768 *mods
= (ADS_MODLIST
)modlist
;
1771 if (!(modlist
[curmod
] = talloc_zero(ctx
, LDAPMod
)))
1772 return ADS_ERROR(LDAP_NO_MEMORY
);
1773 modlist
[curmod
]->mod_type
= talloc_strdup(ctx
, name
);
1774 if (mod_op
& LDAP_MOD_BVALUES
) {
1775 modlist
[curmod
]->mod_bvalues
= ber_values
;
1776 } else if (mod_op
& LDAP_MOD_DELETE
) {
1777 modlist
[curmod
]->mod_values
= NULL
;
1779 modlist
[curmod
]->mod_values
= char_values
;
1782 modlist
[curmod
]->mod_op
= mod_op
;
1783 return ADS_ERROR(LDAP_SUCCESS
);
1787 * Add a single string value to a mod list
1788 * @param ctx An initialized TALLOC_CTX
1789 * @param mods An initialized ADS_MODLIST
1790 * @param name The attribute name to add
1791 * @param val The value to add - NULL means DELETE
1792 * @return ADS STATUS indicating success of add
1794 ADS_STATUS
ads_mod_str(TALLOC_CTX
*ctx
, ADS_MODLIST
*mods
,
1795 const char *name
, const char *val
)
1797 const char *values
[2];
1803 return ads_modlist_add(ctx
, mods
, LDAP_MOD_DELETE
, name
, NULL
);
1804 return ads_modlist_add(ctx
, mods
, LDAP_MOD_REPLACE
, name
, values
);
1808 * Add an array of string values to a mod list
1809 * @param ctx An initialized TALLOC_CTX
1810 * @param mods An initialized ADS_MODLIST
1811 * @param name The attribute name to add
1812 * @param vals The array of string values to add - NULL means DELETE
1813 * @return ADS STATUS indicating success of add
1815 ADS_STATUS
ads_mod_strlist(TALLOC_CTX
*ctx
, ADS_MODLIST
*mods
,
1816 const char *name
, const char **vals
)
1819 return ads_modlist_add(ctx
, mods
, LDAP_MOD_DELETE
, name
, NULL
);
1820 return ads_modlist_add(ctx
, mods
, LDAP_MOD_REPLACE
,
1821 name
, (const void **) vals
);
1825 * Add a single ber-encoded value to a mod list
1826 * @param ctx An initialized TALLOC_CTX
1827 * @param mods An initialized ADS_MODLIST
1828 * @param name The attribute name to add
1829 * @param val The value to add - NULL means DELETE
1830 * @return ADS STATUS indicating success of add
1832 static ADS_STATUS
ads_mod_ber(TALLOC_CTX
*ctx
, ADS_MODLIST
*mods
,
1833 const char *name
, const struct berval
*val
)
1835 const struct berval
*values
[2];
1840 return ads_modlist_add(ctx
, mods
, LDAP_MOD_DELETE
, name
, NULL
);
1841 return ads_modlist_add(ctx
, mods
, LDAP_MOD_REPLACE
|LDAP_MOD_BVALUES
,
1842 name
, (const void **) values
);
1845 static void ads_print_error(int ret
, LDAP
*ld
)
1848 char *ld_error
= NULL
;
1849 ldap_get_option(ld
, LDAP_OPT_ERROR_STRING
, &ld_error
);
1850 DBG_ERR("AD LDAP ERROR: %d (%s): %s\n",
1852 ldap_err2string(ret
),
1854 SAFE_FREE(ld_error
);
1859 * Perform an ldap modify
1860 * @param ads connection to ads server
1861 * @param mod_dn DistinguishedName to modify
1862 * @param mods list of modifications to perform
1863 * @return status of modify
1865 ADS_STATUS
ads_gen_mod(ADS_STRUCT
*ads
, const char *mod_dn
, ADS_MODLIST mods
)
1868 char *utf8_dn
= NULL
;
1869 size_t converted_size
;
1871 this control is needed to modify that contains a currently
1872 non-existent attribute (but allowable for the object) to run
1874 LDAPControl PermitModify
= {
1875 discard_const_p(char, ADS_PERMIT_MODIFY_OID
),
1878 LDAPControl
*controls
[2];
1880 DBG_INFO("AD LDAP: Modifying %s\n", mod_dn
);
1882 controls
[0] = &PermitModify
;
1885 if (!push_utf8_talloc(talloc_tos(), &utf8_dn
, mod_dn
, &converted_size
)) {
1886 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY
);
1889 /* find the end of the list, marked by NULL or -1 */
1890 for(i
=0;(mods
[i
]!=0)&&(mods
[i
]!=(LDAPMod
*) -1);i
++);
1891 /* make sure the end of the list is NULL */
1893 ret
= ldap_modify_ext_s(ads
->ldap
.ld
, utf8_dn
,
1894 (LDAPMod
**) mods
, controls
, NULL
);
1895 ads_print_error(ret
, ads
->ldap
.ld
);
1896 TALLOC_FREE(utf8_dn
);
1897 return ADS_ERROR(ret
);
1901 * Perform an ldap add
1902 * @param ads connection to ads server
1903 * @param new_dn DistinguishedName to add
1904 * @param mods list of attributes and values for DN
1905 * @return status of add
1907 ADS_STATUS
ads_gen_add(ADS_STRUCT
*ads
, const char *new_dn
, ADS_MODLIST mods
)
1910 char *utf8_dn
= NULL
;
1911 size_t converted_size
;
1913 DBG_INFO("AD LDAP: Adding %s\n", new_dn
);
1915 if (!push_utf8_talloc(talloc_tos(), &utf8_dn
, new_dn
, &converted_size
)) {
1916 DEBUG(1, ("ads_gen_add: push_utf8_talloc failed!"));
1917 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY
);
1920 /* find the end of the list, marked by NULL or -1 */
1921 for(i
=0;(mods
[i
]!=0)&&(mods
[i
]!=(LDAPMod
*) -1);i
++);
1922 /* make sure the end of the list is NULL */
1925 ret
= ldap_add_ext_s(ads
->ldap
.ld
, utf8_dn
, (LDAPMod
**)mods
, NULL
, NULL
);
1926 ads_print_error(ret
, ads
->ldap
.ld
);
1927 TALLOC_FREE(utf8_dn
);
1928 return ADS_ERROR(ret
);
1932 * Delete a DistinguishedName
1933 * @param ads connection to ads server
1934 * @param new_dn DistinguishedName to delete
1935 * @return status of delete
1937 ADS_STATUS
ads_del_dn(ADS_STRUCT
*ads
, char *del_dn
)
1940 char *utf8_dn
= NULL
;
1941 size_t converted_size
;
1942 if (!push_utf8_talloc(talloc_tos(), &utf8_dn
, del_dn
, &converted_size
)) {
1943 DEBUG(1, ("ads_del_dn: push_utf8_talloc failed!"));
1944 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY
);
1947 DBG_INFO("AD LDAP: Deleting %s\n", del_dn
);
1949 ret
= ldap_delete_s(ads
->ldap
.ld
, utf8_dn
);
1950 ads_print_error(ret
, ads
->ldap
.ld
);
1951 TALLOC_FREE(utf8_dn
);
1952 return ADS_ERROR(ret
);
1956 * Build an org unit string
1957 * if org unit is Computers or blank then assume a container, otherwise
1958 * assume a / separated list of organisational units.
1959 * jmcd: '\' is now used for escapes so certain chars can be in the ou (e.g. #)
1960 * @param ads connection to ads server
1961 * @param org_unit Organizational unit
1962 * @return org unit string - caller must free
1964 char *ads_ou_string(ADS_STRUCT
*ads
, const char *org_unit
)
1970 if (!org_unit
|| !*org_unit
) {
1972 ret
= ads_default_ou_string(ads
, DS_GUID_COMPUTERS_CONTAINER
);
1974 /* samba4 might not yet respond to a wellknownobject-query */
1975 return ret
? ret
: SMB_STRDUP("cn=Computers");
1978 if (strequal(org_unit
, "Computers")) {
1979 return SMB_STRDUP("cn=Computers");
1982 /* jmcd: removed "\\" from the separation chars, because it is
1983 needed as an escape for chars like '#' which are valid in an
1985 status
= ads_build_path(org_unit
, "/", "ou=", 1, &dn
);
1986 if (!ADS_ERR_OK(status
)) {
1994 * Get a org unit string for a well-known GUID
1995 * @param ads connection to ads server
1996 * @param wknguid Well known GUID
1997 * @return org unit string - caller must free
1999 char *ads_default_ou_string(ADS_STRUCT
*ads
, const char *wknguid
)
2002 LDAPMessage
*res
= NULL
;
2003 char *base
, *wkn_dn
= NULL
, *ret
= NULL
, **wkn_dn_exp
= NULL
,
2004 **bind_dn_exp
= NULL
;
2005 const char *attrs
[] = {"distinguishedName", NULL
};
2006 int new_ln
, wkn_ln
, bind_ln
, i
;
2008 if (wknguid
== NULL
) {
2012 if (asprintf(&base
, "<WKGUID=%s,%s>", wknguid
, ads
->config
.bind_path
) == -1) {
2013 DEBUG(1, ("asprintf failed!\n"));
2017 status
= ads_search_dn(ads
, &res
, base
, attrs
);
2018 if (!ADS_ERR_OK(status
)) {
2019 DEBUG(1,("Failed while searching for: %s\n", base
));
2023 if (ads_count_replies(ads
, res
) != 1) {
2027 /* substitute the bind-path from the well-known-guid-search result */
2028 wkn_dn
= ads_get_dn(ads
, talloc_tos(), res
);
2033 wkn_dn_exp
= ldap_explode_dn(wkn_dn
, 0);
2038 bind_dn_exp
= ldap_explode_dn(ads
->config
.bind_path
, 0);
2043 for (wkn_ln
=0; wkn_dn_exp
[wkn_ln
]; wkn_ln
++)
2045 for (bind_ln
=0; bind_dn_exp
[bind_ln
]; bind_ln
++)
2048 new_ln
= wkn_ln
- bind_ln
;
2050 ret
= SMB_STRDUP(wkn_dn_exp
[0]);
2055 for (i
=1; i
< new_ln
; i
++) {
2058 if (asprintf(&s
, "%s,%s", ret
, wkn_dn_exp
[i
]) == -1) {
2064 ret
= SMB_STRDUP(s
);
2073 ads_msgfree(ads
, res
);
2074 TALLOC_FREE(wkn_dn
);
2076 ldap_value_free(wkn_dn_exp
);
2079 ldap_value_free(bind_dn_exp
);
2086 * Adds (appends) an item to an attribute array, rather then
2087 * replacing the whole list
2088 * @param ctx An initialized TALLOC_CTX
2089 * @param mods An initialized ADS_MODLIST
2090 * @param name name of the ldap attribute to append to
2091 * @param vals an array of values to add
2092 * @return status of addition
2095 ADS_STATUS
ads_add_strlist(TALLOC_CTX
*ctx
, ADS_MODLIST
*mods
,
2096 const char *name
, const char **vals
)
2098 return ads_modlist_add(ctx
, mods
, LDAP_MOD_ADD
, name
,
2099 (const void *) vals
);
2103 * Determines the an account's current KVNO via an LDAP lookup
2104 * @param ads An initialized ADS_STRUCT
2105 * @param account_name the NT samaccountname.
2106 * @return the kvno for the account, or -1 in case of a failure.
2109 uint32_t ads_get_kvno(ADS_STRUCT
*ads
, const char *account_name
)
2111 LDAPMessage
*res
= NULL
;
2112 uint32_t kvno
= (uint32_t)-1; /* -1 indicates a failure */
2114 const char *attrs
[] = {"msDS-KeyVersionNumber", NULL
};
2115 char *dn_string
= NULL
;
2118 DEBUG(5,("ads_get_kvno: Searching for account %s\n", account_name
));
2119 if (asprintf(&filter
, "(samAccountName=%s)", account_name
) == -1) {
2122 ret
= ads_search(ads
, &res
, filter
, attrs
);
2124 if (!ADS_ERR_OK(ret
) || (ads_count_replies(ads
, res
) != 1)) {
2125 DEBUG(1,("ads_get_kvno: Account for %s not found.\n", account_name
));
2126 ads_msgfree(ads
, res
);
2130 dn_string
= ads_get_dn(ads
, talloc_tos(), res
);
2132 DEBUG(0,("ads_get_kvno: out of memory.\n"));
2133 ads_msgfree(ads
, res
);
2136 DEBUG(5,("ads_get_kvno: Using: %s\n", dn_string
));
2137 TALLOC_FREE(dn_string
);
2139 /* ---------------------------------------------------------
2140 * 0 is returned as a default KVNO from this point on...
2141 * This is done because Windows 2000 does not support key
2142 * version numbers. Chances are that a failure in the next
2143 * step is simply due to Windows 2000 being used for a
2144 * domain controller. */
2147 if (!ads_pull_uint32(ads
, res
, "msDS-KeyVersionNumber", &kvno
)) {
2148 DEBUG(3,("ads_get_kvno: Error Determining KVNO!\n"));
2149 DEBUG(3,("ads_get_kvno: Windows 2000 does not support KVNO's, so this may be normal.\n"));
2150 ads_msgfree(ads
, res
);
2155 DEBUG(5,("ads_get_kvno: Looked Up KVNO of: %d\n", kvno
));
2156 ads_msgfree(ads
, res
);
2161 * Determines the computer account's current KVNO via an LDAP lookup
2162 * @param ads An initialized ADS_STRUCT
2163 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
2164 * @return the kvno for the computer account, or -1 in case of a failure.
2167 uint32_t ads_get_machine_kvno(ADS_STRUCT
*ads
, const char *machine_name
)
2169 char *computer_account
= NULL
;
2172 if (asprintf(&computer_account
, "%s$", machine_name
) < 0) {
2176 kvno
= ads_get_kvno(ads
, computer_account
);
2177 free(computer_account
);
2183 * This clears out all registered spn's for a given hostname
2184 * @param ads An initilaized ADS_STRUCT
2185 * @param machine_name the NetBIOS name of the computer.
2186 * @return 0 upon success, non-zero otherwise.
2189 ADS_STATUS
ads_clear_service_principal_names(ADS_STRUCT
*ads
, const char *machine_name
)
2192 LDAPMessage
*res
= NULL
;
2194 const char *servicePrincipalName
[1] = {NULL
};
2196 char *dn_string
= NULL
;
2198 ret
= ads_find_machine_acct(ads
, &res
, machine_name
);
2199 if (!ADS_ERR_OK(ret
)) {
2200 DEBUG(5,("ads_clear_service_principal_names: WARNING: Host Account for %s not found... skipping operation.\n", machine_name
));
2201 DEBUG(5,("ads_clear_service_principal_names: WARNING: Service Principals for %s have NOT been cleared.\n", machine_name
));
2202 ads_msgfree(ads
, res
);
2206 DEBUG(5,("ads_clear_service_principal_names: Host account for %s found\n", machine_name
));
2207 ctx
= talloc_init("ads_clear_service_principal_names");
2209 ads_msgfree(ads
, res
);
2210 return ADS_ERROR(LDAP_NO_MEMORY
);
2213 if (!(mods
= ads_init_mods(ctx
))) {
2214 talloc_destroy(ctx
);
2215 ads_msgfree(ads
, res
);
2216 return ADS_ERROR(LDAP_NO_MEMORY
);
2218 ret
= ads_mod_strlist(ctx
, &mods
, "servicePrincipalName", servicePrincipalName
);
2219 if (!ADS_ERR_OK(ret
)) {
2220 DEBUG(1,("ads_clear_service_principal_names: Error creating strlist.\n"));
2221 ads_msgfree(ads
, res
);
2222 talloc_destroy(ctx
);
2225 dn_string
= ads_get_dn(ads
, talloc_tos(), res
);
2227 talloc_destroy(ctx
);
2228 ads_msgfree(ads
, res
);
2229 return ADS_ERROR(LDAP_NO_MEMORY
);
2231 ret
= ads_gen_mod(ads
, dn_string
, mods
);
2232 TALLOC_FREE(dn_string
);
2233 if (!ADS_ERR_OK(ret
)) {
2234 DEBUG(1,("ads_clear_service_principal_names: Error: Updating Service Principals for machine %s in LDAP\n",
2236 ads_msgfree(ads
, res
);
2237 talloc_destroy(ctx
);
2241 ads_msgfree(ads
, res
);
2242 talloc_destroy(ctx
);
2247 * @brief Search for an element in a string array.
2249 * @param[in] el_array The string array to search.
2251 * @param[in] num_el The number of elements in the string array.
2253 * @param[in] el The string to search.
2255 * @return True if found, false if not.
2257 bool ads_element_in_array(const char **el_array
, size_t num_el
, const char *el
)
2261 if (el_array
== NULL
|| num_el
== 0 || el
== NULL
) {
2265 for (i
= 0; i
< num_el
&& el_array
[i
] != NULL
; i
++) {
2268 cmp
= strcasecmp_m(el_array
[i
], el
);
2278 * @brief This gets the service principal names of an existing computer account.
2280 * @param[in] mem_ctx The memory context to use to allocate the spn array.
2282 * @param[in] ads The ADS context to use.
2284 * @param[in] machine_name The NetBIOS name of the computer, which is used to
2285 * identify the computer account.
2287 * @param[in] spn_array A pointer to store the array for SPNs.
2289 * @param[in] num_spns The number of principals stored in the array.
2291 * @return 0 on success, or a ADS error if a failure occurred.
2293 ADS_STATUS
ads_get_service_principal_names(TALLOC_CTX
*mem_ctx
,
2295 const char *machine_name
,
2300 LDAPMessage
*res
= NULL
;
2303 status
= ads_find_machine_acct(ads
,
2306 if (!ADS_ERR_OK(status
)) {
2307 DEBUG(1,("Host Account for %s not found... skipping operation.\n",
2312 count
= ads_count_replies(ads
, res
);
2314 status
= ADS_ERROR(LDAP_NO_SUCH_OBJECT
);
2318 *spn_array
= ads_pull_strings(ads
,
2321 "servicePrincipalName",
2323 if (*spn_array
== NULL
) {
2324 DEBUG(1, ("Host account for %s does not have service principal "
2327 status
= ADS_ERROR(LDAP_NO_SUCH_OBJECT
);
2332 ads_msgfree(ads
, res
);
2338 * This adds a service principal name to an existing computer account
2339 * (found by hostname) in AD.
2340 * @param ads An initialized ADS_STRUCT
2341 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
2342 * @param spns An array or strings for the service principals to add,
2343 * i.e. 'cifs/machine_name', 'http/machine.full.domain.com' etc.
2344 * @return 0 upon sucess, or non-zero if a failure occurs
2347 ADS_STATUS
ads_add_service_principal_names(ADS_STRUCT
*ads
,
2348 const char *machine_name
,
2353 LDAPMessage
*res
= NULL
;
2355 char *dn_string
= NULL
;
2356 const char **servicePrincipalName
= spns
;
2358 ret
= ads_find_machine_acct(ads
, &res
, machine_name
);
2359 if (!ADS_ERR_OK(ret
)) {
2360 DEBUG(1,("ads_add_service_principal_name: WARNING: Host Account for %s not found... skipping operation.\n",
2362 DEBUG(1,("ads_add_service_principal_name: WARNING: Service Principals have NOT been added.\n"));
2363 ads_msgfree(ads
, res
);
2367 DEBUG(1,("ads_add_service_principal_name: Host account for %s found\n", machine_name
));
2368 if (!(ctx
= talloc_init("ads_add_service_principal_name"))) {
2369 ads_msgfree(ads
, res
);
2370 return ADS_ERROR(LDAP_NO_MEMORY
);
2373 DEBUG(5,("ads_add_service_principal_name: INFO: "
2374 "Adding %s to host %s\n",
2375 spns
[0] ? "N/A" : spns
[0], machine_name
));
2378 DEBUG(5,("ads_add_service_principal_name: INFO: "
2379 "Adding %s to host %s\n",
2380 spns
[1] ? "N/A" : spns
[1], machine_name
));
2382 if ( (mods
= ads_init_mods(ctx
)) == NULL
) {
2383 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
2387 ret
= ads_add_strlist(ctx
,
2389 "servicePrincipalName",
2390 servicePrincipalName
);
2391 if (!ADS_ERR_OK(ret
)) {
2392 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
2396 if ( (dn_string
= ads_get_dn(ads
, ctx
, res
)) == NULL
) {
2397 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
2401 ret
= ads_gen_mod(ads
, dn_string
, mods
);
2402 if (!ADS_ERR_OK(ret
)) {
2403 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
2409 ads_msgfree(ads
, res
);
2413 static uint32_t ads_get_acct_ctrl(ADS_STRUCT
*ads
,
2416 uint32_t acct_ctrl
= 0;
2419 ok
= ads_pull_uint32(ads
, msg
, "userAccountControl", &acct_ctrl
);
2427 static ADS_STATUS
ads_change_machine_acct(ADS_STRUCT
*ads
,
2429 const struct berval
*machine_pw_val
)
2433 TALLOC_CTX
*frame
= talloc_stackframe();
2434 uint32_t acct_control
;
2435 char *control_str
= NULL
;
2436 const char *attrs
[] = {
2440 LDAPMessage
*res
= NULL
;
2443 dn
= ads_get_dn(ads
, frame
, msg
);
2445 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
2449 acct_control
= ads_get_acct_ctrl(ads
, msg
);
2450 if (acct_control
== 0) {
2451 ret
= ADS_ERROR(LDAP_NO_RESULTS_RETURNED
);
2456 * Changing the password, disables the account. So we need to change the
2457 * userAccountControl flags to enable it again.
2459 mods
= ads_init_mods(frame
);
2461 ret
= ADS_ERROR_LDAP(LDAP_NO_MEMORY
);
2465 ads_mod_ber(frame
, &mods
, "unicodePwd", machine_pw_val
);
2467 ret
= ads_gen_mod(ads
, dn
, mods
);
2468 if (!ADS_ERR_OK(ret
)) {
2474 * To activate the account, we need to disable and enable it.
2476 acct_control
|= UF_ACCOUNTDISABLE
;
2478 control_str
= talloc_asprintf(frame
, "%u", acct_control
);
2479 if (control_str
== NULL
) {
2480 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
2484 mods
= ads_init_mods(frame
);
2486 ret
= ADS_ERROR_LDAP(LDAP_NO_MEMORY
);
2490 ads_mod_str(frame
, &mods
, "userAccountControl", control_str
);
2492 ret
= ads_gen_mod(ads
, dn
, mods
);
2493 if (!ADS_ERR_OK(ret
)) {
2497 TALLOC_FREE(control_str
);
2500 * Enable the account again.
2502 acct_control
&= ~UF_ACCOUNTDISABLE
;
2504 control_str
= talloc_asprintf(frame
, "%u", acct_control
);
2505 if (control_str
== NULL
) {
2506 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
2510 mods
= ads_init_mods(frame
);
2512 ret
= ADS_ERROR_LDAP(LDAP_NO_MEMORY
);
2516 ads_mod_str(frame
, &mods
, "userAccountControl", control_str
);
2518 ret
= ads_gen_mod(ads
, dn
, mods
);
2519 if (!ADS_ERR_OK(ret
)) {
2523 TALLOC_FREE(control_str
);
2525 ret
= ads_search_dn(ads
, &res
, dn
, attrs
);
2526 ads_msgfree(ads
, res
);
2535 * adds a machine account to the ADS server
2536 * @param ads An intialized ADS_STRUCT
2537 * @param machine_name - the NetBIOS machine name of this account.
2538 * @param account_type A number indicating the type of account to create
2539 * @param org_unit The LDAP path in which to place this account
2540 * @return 0 upon success, or non-zero otherwise
2543 ADS_STATUS
ads_create_machine_acct(ADS_STRUCT
*ads
,
2544 const char *machine_name
,
2545 const char *machine_password
,
2546 const char *org_unit
,
2547 uint32_t etype_list
,
2548 const char *dns_domain_name
)
2551 char *samAccountName
= NULL
;
2552 char *controlstr
= NULL
;
2553 TALLOC_CTX
*ctx
= NULL
;
2555 char *machine_escaped
= NULL
;
2556 char *dns_hostname
= NULL
;
2557 char *new_dn
= NULL
;
2558 char *utf8_pw
= NULL
;
2559 size_t utf8_pw_len
= 0;
2560 char *utf16_pw
= NULL
;
2561 size_t utf16_pw_len
= 0;
2562 struct berval machine_pw_val
;
2564 const char **spn_array
= NULL
;
2565 size_t num_spns
= 0;
2566 const char *spn_prefix
[] = {
2568 "RestrictedKrbHost",
2571 LDAPMessage
*res
= NULL
;
2572 uint32_t acct_control
= UF_WORKSTATION_TRUST_ACCOUNT
;
2574 ctx
= talloc_init("ads_add_machine_acct");
2576 return ADS_ERROR(LDAP_NO_MEMORY
);
2579 machine_escaped
= escape_rdn_val_string_alloc(machine_name
);
2580 if (machine_escaped
== NULL
) {
2581 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
2585 utf8_pw
= talloc_asprintf(ctx
, "\"%s\"", machine_password
);
2586 if (utf8_pw
== NULL
) {
2587 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
2590 utf8_pw_len
= strlen(utf8_pw
);
2592 ok
= convert_string_talloc(ctx
,
2593 CH_UTF8
, CH_UTF16MUNGED
,
2594 utf8_pw
, utf8_pw_len
,
2595 (void *)&utf16_pw
, &utf16_pw_len
);
2597 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
2601 machine_pw_val
= (struct berval
) {
2603 .bv_len
= utf16_pw_len
,
2606 /* Check if the machine account already exists. */
2607 ret
= ads_find_machine_acct(ads
, &res
, machine_escaped
);
2608 if (ADS_ERR_OK(ret
)) {
2609 /* Change the machine account password */
2610 ret
= ads_change_machine_acct(ads
, res
, &machine_pw_val
);
2611 ads_msgfree(ads
, res
);
2615 ads_msgfree(ads
, res
);
2617 new_dn
= talloc_asprintf(ctx
, "cn=%s,%s", machine_escaped
, org_unit
);
2618 if (new_dn
== NULL
) {
2619 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
2623 /* Create machine account */
2625 samAccountName
= talloc_asprintf(ctx
, "%s$", machine_name
);
2626 if (samAccountName
== NULL
) {
2627 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
2631 dns_hostname
= talloc_asprintf(ctx
,
2635 if (dns_hostname
== NULL
) {
2636 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
2640 /* Add dns_hostname SPNs */
2641 for (i
= 0; i
< ARRAY_SIZE(spn_prefix
); i
++) {
2642 char *spn
= talloc_asprintf(ctx
,
2647 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
2651 ok
= add_string_to_array(spn_array
,
2656 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
2661 /* Add machine_name SPNs */
2662 for (i
= 0; i
< ARRAY_SIZE(spn_prefix
); i
++) {
2663 char *spn
= talloc_asprintf(ctx
,
2668 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
2672 ok
= add_string_to_array(spn_array
,
2677 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
2682 /* Make sure to NULL terminate the array */
2683 spn_array
= talloc_realloc(ctx
, spn_array
, const char *, num_spns
+ 1);
2684 if (spn_array
== NULL
) {
2685 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
2688 spn_array
[num_spns
] = NULL
;
2690 controlstr
= talloc_asprintf(ctx
, "%u", acct_control
);
2691 if (controlstr
== NULL
) {
2692 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
2696 mods
= ads_init_mods(ctx
);
2698 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
2702 ads_mod_str(ctx
, &mods
, "objectClass", "Computer");
2703 ads_mod_str(ctx
, &mods
, "SamAccountName", samAccountName
);
2704 ads_mod_str(ctx
, &mods
, "userAccountControl", controlstr
);
2705 ads_mod_str(ctx
, &mods
, "DnsHostName", dns_hostname
);
2706 ads_mod_strlist(ctx
, &mods
, "ServicePrincipalName", spn_array
);
2707 ads_mod_ber(ctx
, &mods
, "unicodePwd", &machine_pw_val
);
2709 ret
= ads_gen_add(ads
, new_dn
, mods
);
2712 SAFE_FREE(machine_escaped
);
2713 talloc_destroy(ctx
);
2719 * move a machine account to another OU on the ADS server
2720 * @param ads - An intialized ADS_STRUCT
2721 * @param machine_name - the NetBIOS machine name of this account.
2722 * @param org_unit - The LDAP path in which to place this account
2723 * @param moved - whether we moved the machine account (optional)
2724 * @return 0 upon success, or non-zero otherwise
2727 ADS_STATUS
ads_move_machine_acct(ADS_STRUCT
*ads
, const char *machine_name
,
2728 const char *org_unit
, bool *moved
)
2732 LDAPMessage
*res
= NULL
;
2733 char *filter
= NULL
;
2734 char *computer_dn
= NULL
;
2736 char *computer_rdn
= NULL
;
2737 bool need_move
= False
;
2739 if (asprintf(&filter
, "(samAccountName=%s$)", machine_name
) == -1) {
2740 rc
= ADS_ERROR(LDAP_NO_MEMORY
);
2744 /* Find pre-existing machine */
2745 rc
= ads_search(ads
, &res
, filter
, NULL
);
2746 if (!ADS_ERR_OK(rc
)) {
2750 computer_dn
= ads_get_dn(ads
, talloc_tos(), res
);
2752 rc
= ADS_ERROR(LDAP_NO_MEMORY
);
2756 parent_dn
= ads_parent_dn(computer_dn
);
2757 if (strequal(parent_dn
, org_unit
)) {
2763 if (asprintf(&computer_rdn
, "CN=%s", machine_name
) == -1) {
2764 rc
= ADS_ERROR(LDAP_NO_MEMORY
);
2768 ldap_status
= ldap_rename_s(ads
->ldap
.ld
, computer_dn
, computer_rdn
,
2769 org_unit
, 1, NULL
, NULL
);
2770 rc
= ADS_ERROR(ldap_status
);
2773 ads_msgfree(ads
, res
);
2775 TALLOC_FREE(computer_dn
);
2776 SAFE_FREE(computer_rdn
);
2778 if (!ADS_ERR_OK(rc
)) {
2790 dump a binary result from ldap
2792 static void dump_binary(ADS_STRUCT
*ads
, const char *field
, struct berval
**values
)
2795 for (i
=0; values
[i
]; i
++) {
2797 printf("%s: ", field
);
2798 for (j
=0; j
<values
[i
]->bv_len
; j
++) {
2799 printf("%02X", (unsigned char)values
[i
]->bv_val
[j
]);
2805 static void dump_guid(ADS_STRUCT
*ads
, const char *field
, struct berval
**values
)
2808 for (i
=0; values
[i
]; i
++) {
2810 DATA_BLOB in
= data_blob_const(values
[i
]->bv_val
, values
[i
]->bv_len
);
2813 status
= GUID_from_ndr_blob(&in
, &guid
);
2814 if (NT_STATUS_IS_OK(status
)) {
2815 printf("%s: %s\n", field
, GUID_string(talloc_tos(), &guid
));
2817 printf("%s: INVALID GUID\n", field
);
2823 dump a sid result from ldap
2825 static void dump_sid(ADS_STRUCT
*ads
, const char *field
, struct berval
**values
)
2828 for (i
=0; values
[i
]; i
++) {
2831 struct dom_sid_buf tmp
;
2832 ret
= sid_parse((const uint8_t *)values
[i
]->bv_val
,
2833 values
[i
]->bv_len
, &sid
);
2837 printf("%s: %s\n", field
, dom_sid_str_buf(&sid
, &tmp
));
2842 dump ntSecurityDescriptor
2844 static void dump_sd(ADS_STRUCT
*ads
, const char *filed
, struct berval
**values
)
2846 TALLOC_CTX
*frame
= talloc_stackframe();
2847 struct security_descriptor
*psd
;
2850 status
= unmarshall_sec_desc(talloc_tos(), (uint8_t *)values
[0]->bv_val
,
2851 values
[0]->bv_len
, &psd
);
2852 if (!NT_STATUS_IS_OK(status
)) {
2853 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
2854 nt_errstr(status
)));
2860 ads_disp_sd(ads
, talloc_tos(), psd
);
2867 dump a string result from ldap
2869 static void dump_string(const char *field
, char **values
)
2872 for (i
=0; values
[i
]; i
++) {
2873 printf("%s: %s\n", field
, values
[i
]);
2878 dump a field from LDAP on stdout
2882 static bool ads_dump_field(ADS_STRUCT
*ads
, char *field
, void **values
, void *data_area
)
2887 void (*handler
)(ADS_STRUCT
*, const char *, struct berval
**);
2889 {"objectGUID", False
, dump_guid
},
2890 {"netbootGUID", False
, dump_guid
},
2891 {"nTSecurityDescriptor", False
, dump_sd
},
2892 {"dnsRecord", False
, dump_binary
},
2893 {"objectSid", False
, dump_sid
},
2894 {"tokenGroups", False
, dump_sid
},
2895 {"tokenGroupsNoGCAcceptable", False
, dump_sid
},
2896 {"tokengroupsGlobalandUniversal", False
, dump_sid
},
2897 {"mS-DS-CreatorSID", False
, dump_sid
},
2898 {"msExchMailboxGuid", False
, dump_guid
},
2903 if (!field
) { /* must be end of an entry */
2908 for (i
=0; handlers
[i
].name
; i
++) {
2909 if (strcasecmp_m(handlers
[i
].name
, field
) == 0) {
2910 if (!values
) /* first time, indicate string or not */
2911 return handlers
[i
].string
;
2912 handlers
[i
].handler(ads
, field
, (struct berval
**) values
);
2916 if (!handlers
[i
].name
) {
2917 if (!values
) /* first time, indicate string conversion */
2919 dump_string(field
, (char **)values
);
2925 * Dump a result from LDAP on stdout
2926 * used for debugging
2927 * @param ads connection to ads server
2928 * @param res Results to dump
2931 void ads_dump(ADS_STRUCT
*ads
, LDAPMessage
*res
)
2933 ads_process_results(ads
, res
, ads_dump_field
, NULL
);
2937 * Walk through results, calling a function for each entry found.
2938 * The function receives a field name, a berval * array of values,
2939 * and a data area passed through from the start. The function is
2940 * called once with null for field and values at the end of each
2942 * @param ads connection to ads server
2943 * @param res Results to process
2944 * @param fn Function for processing each result
2945 * @param data_area user-defined area to pass to function
2947 void ads_process_results(ADS_STRUCT
*ads
, LDAPMessage
*res
,
2948 bool (*fn
)(ADS_STRUCT
*, char *, void **, void *),
2953 size_t converted_size
;
2955 if (!(ctx
= talloc_init("ads_process_results")))
2958 for (msg
= ads_first_entry(ads
, res
); msg
;
2959 msg
= ads_next_entry(ads
, msg
)) {
2963 for (utf8_field
=ldap_first_attribute(ads
->ldap
.ld
,
2964 (LDAPMessage
*)msg
,&b
);
2966 utf8_field
=ldap_next_attribute(ads
->ldap
.ld
,
2967 (LDAPMessage
*)msg
,b
)) {
2968 struct berval
**ber_vals
;
2974 if (!pull_utf8_talloc(ctx
, &field
, utf8_field
,
2977 DEBUG(0,("ads_process_results: "
2978 "pull_utf8_talloc failed: %s",
2982 string
= fn(ads
, field
, NULL
, data_area
);
2987 utf8_vals
= ldap_get_values(ads
->ldap
.ld
,
2988 (LDAPMessage
*)msg
, field
);
2989 p
= discard_const_p(const char *, utf8_vals
);
2990 str_vals
= ads_pull_strvals(ctx
, p
);
2991 fn(ads
, field
, (void **) str_vals
, data_area
);
2992 ldap_value_free(utf8_vals
);
2994 ber_vals
= ldap_get_values_len(ads
->ldap
.ld
,
2995 (LDAPMessage
*)msg
, field
);
2996 fn(ads
, field
, (void **) ber_vals
, data_area
);
2998 ldap_value_free_len(ber_vals
);
3000 ldap_memfree(utf8_field
);
3003 talloc_free_children(ctx
);
3004 fn(ads
, NULL
, NULL
, data_area
); /* completed an entry */
3007 talloc_destroy(ctx
);
3011 * count how many replies are in a LDAPMessage
3012 * @param ads connection to ads server
3013 * @param res Results to count
3014 * @return number of replies
3016 int ads_count_replies(ADS_STRUCT
*ads
, void *res
)
3018 return ldap_count_entries(ads
->ldap
.ld
, (LDAPMessage
*)res
);
3022 * pull the first entry from a ADS result
3023 * @param ads connection to ads server
3024 * @param res Results of search
3025 * @return first entry from result
3027 LDAPMessage
*ads_first_entry(ADS_STRUCT
*ads
, LDAPMessage
*res
)
3029 return ldap_first_entry(ads
->ldap
.ld
, res
);
3033 * pull the next entry from a ADS result
3034 * @param ads connection to ads server
3035 * @param res Results of search
3036 * @return next entry from result
3038 LDAPMessage
*ads_next_entry(ADS_STRUCT
*ads
, LDAPMessage
*res
)
3040 return ldap_next_entry(ads
->ldap
.ld
, res
);
3044 * pull the first message from a ADS result
3045 * @param ads connection to ads server
3046 * @param res Results of search
3047 * @return first message from result
3049 LDAPMessage
*ads_first_message(ADS_STRUCT
*ads
, LDAPMessage
*res
)
3051 return ldap_first_message(ads
->ldap
.ld
, res
);
3055 * pull the next message from a ADS result
3056 * @param ads connection to ads server
3057 * @param res Results of search
3058 * @return next message from result
3060 LDAPMessage
*ads_next_message(ADS_STRUCT
*ads
, LDAPMessage
*res
)
3062 return ldap_next_message(ads
->ldap
.ld
, res
);
3066 * pull a single string from a ADS result
3067 * @param ads connection to ads server
3068 * @param mem_ctx TALLOC_CTX to use for allocating result string
3069 * @param msg Results of search
3070 * @param field Attribute to retrieve
3071 * @return Result string in talloc context
3073 char *ads_pull_string(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
, LDAPMessage
*msg
,
3079 size_t converted_size
;
3081 values
= ldap_get_values(ads
->ldap
.ld
, msg
, field
);
3085 if (values
[0] && pull_utf8_talloc(mem_ctx
, &ux_string
, values
[0],
3090 ldap_value_free(values
);
3095 * pull an array of strings from a ADS result
3096 * @param ads connection to ads server
3097 * @param mem_ctx TALLOC_CTX to use for allocating result string
3098 * @param msg Results of search
3099 * @param field Attribute to retrieve
3100 * @return Result strings in talloc context
3102 char **ads_pull_strings(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
,
3103 LDAPMessage
*msg
, const char *field
,
3108 size_t i
, converted_size
;
3110 values
= ldap_get_values(ads
->ldap
.ld
, msg
, field
);
3114 *num_values
= ldap_count_values(values
);
3116 ret
= talloc_array(mem_ctx
, char *, *num_values
+ 1);
3118 ldap_value_free(values
);
3122 for (i
=0;i
<*num_values
;i
++) {
3123 if (!pull_utf8_talloc(mem_ctx
, &ret
[i
], values
[i
],
3126 ldap_value_free(values
);
3132 ldap_value_free(values
);
3137 * pull an array of strings from a ADS result
3138 * (handle large multivalue attributes with range retrieval)
3139 * @param ads connection to ads server
3140 * @param mem_ctx TALLOC_CTX to use for allocating result string
3141 * @param msg Results of search
3142 * @param field Attribute to retrieve
3143 * @param current_strings strings returned by a previous call to this function
3144 * @param next_attribute The next query should ask for this attribute
3145 * @param num_values How many values did we get this time?
3146 * @param more_values Are there more values to get?
3147 * @return Result strings in talloc context
3149 char **ads_pull_strings_range(ADS_STRUCT
*ads
,
3150 TALLOC_CTX
*mem_ctx
,
3151 LDAPMessage
*msg
, const char *field
,
3152 char **current_strings
,
3153 const char **next_attribute
,
3154 size_t *num_strings
,
3158 char *expected_range_attrib
, *range_attr
;
3159 BerElement
*ptr
= NULL
;
3162 size_t num_new_strings
;
3163 unsigned long int range_start
;
3164 unsigned long int range_end
;
3166 /* we might have been given the whole lot anyway */
3167 if ((strings
= ads_pull_strings(ads
, mem_ctx
, msg
, field
, num_strings
))) {
3168 *more_strings
= False
;
3172 expected_range_attrib
= talloc_asprintf(mem_ctx
, "%s;Range=", field
);
3174 /* look for Range result */
3175 for (attr
= ldap_first_attribute(ads
->ldap
.ld
, (LDAPMessage
*)msg
, &ptr
);
3177 attr
= ldap_next_attribute(ads
->ldap
.ld
, (LDAPMessage
*)msg
, ptr
)) {
3178 /* we ignore the fact that this is utf8, as all attributes are ascii... */
3179 if (strnequal(attr
, expected_range_attrib
, strlen(expected_range_attrib
))) {
3187 /* nothing here - this field is just empty */
3188 *more_strings
= False
;
3192 if (sscanf(&range_attr
[strlen(expected_range_attrib
)], "%lu-%lu",
3193 &range_start
, &range_end
) == 2) {
3194 *more_strings
= True
;
3196 if (sscanf(&range_attr
[strlen(expected_range_attrib
)], "%lu-*",
3197 &range_start
) == 1) {
3198 *more_strings
= False
;
3200 DEBUG(1, ("ads_pull_strings_range: Cannot parse Range attriubte (%s)\n",
3202 ldap_memfree(range_attr
);
3203 *more_strings
= False
;
3208 if ((*num_strings
) != range_start
) {
3209 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) doesn't start at %u, but at %lu"
3210 " - aborting range retreival\n",
3211 range_attr
, (unsigned int)(*num_strings
) + 1, range_start
));
3212 ldap_memfree(range_attr
);
3213 *more_strings
= False
;
3217 new_strings
= ads_pull_strings(ads
, mem_ctx
, msg
, range_attr
, &num_new_strings
);
3219 if (*more_strings
&& ((*num_strings
+ num_new_strings
) != (range_end
+ 1))) {
3220 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) tells us we have %lu "
3221 "strings in this bunch, but we only got %lu - aborting range retreival\n",
3222 range_attr
, (unsigned long int)range_end
- range_start
+ 1,
3223 (unsigned long int)num_new_strings
));
3224 ldap_memfree(range_attr
);
3225 *more_strings
= False
;
3229 strings
= talloc_realloc(mem_ctx
, current_strings
, char *,
3230 *num_strings
+ num_new_strings
);
3232 if (strings
== NULL
) {
3233 ldap_memfree(range_attr
);
3234 *more_strings
= False
;
3238 if (new_strings
&& num_new_strings
) {
3239 memcpy(&strings
[*num_strings
], new_strings
,
3240 sizeof(*new_strings
) * num_new_strings
);
3243 (*num_strings
) += num_new_strings
;
3245 if (*more_strings
) {
3246 *next_attribute
= talloc_asprintf(mem_ctx
,
3251 if (!*next_attribute
) {
3252 DEBUG(1, ("talloc_asprintf for next attribute failed!\n"));
3253 ldap_memfree(range_attr
);
3254 *more_strings
= False
;
3259 ldap_memfree(range_attr
);
3265 * pull a single uint32_t from a ADS result
3266 * @param ads connection to ads server
3267 * @param msg Results of search
3268 * @param field Attribute to retrieve
3269 * @param v Pointer to int to store result
3270 * @return boolean inidicating success
3272 bool ads_pull_uint32(ADS_STRUCT
*ads
, LDAPMessage
*msg
, const char *field
,
3277 values
= ldap_get_values(ads
->ldap
.ld
, msg
, field
);
3281 ldap_value_free(values
);
3285 *v
= atoi(values
[0]);
3286 ldap_value_free(values
);
3291 * pull a single objectGUID from an ADS result
3292 * @param ads connection to ADS server
3293 * @param msg results of search
3294 * @param guid 37-byte area to receive text guid
3295 * @return boolean indicating success
3297 bool ads_pull_guid(ADS_STRUCT
*ads
, LDAPMessage
*msg
, struct GUID
*guid
)
3302 if (!smbldap_talloc_single_blob(talloc_tos(), ads
->ldap
.ld
, msg
, "objectGUID",
3307 status
= GUID_from_ndr_blob(&blob
, guid
);
3308 talloc_free(blob
.data
);
3309 return NT_STATUS_IS_OK(status
);
3314 * pull a single struct dom_sid from a ADS result
3315 * @param ads connection to ads server
3316 * @param msg Results of search
3317 * @param field Attribute to retrieve
3318 * @param sid Pointer to sid to store result
3319 * @return boolean inidicating success
3321 bool ads_pull_sid(ADS_STRUCT
*ads
, LDAPMessage
*msg
, const char *field
,
3322 struct dom_sid
*sid
)
3324 return smbldap_pull_sid(ads
->ldap
.ld
, msg
, field
, sid
);
3328 * pull an array of struct dom_sids from a ADS result
3329 * @param ads connection to ads server
3330 * @param mem_ctx TALLOC_CTX for allocating sid array
3331 * @param msg Results of search
3332 * @param field Attribute to retrieve
3333 * @param sids pointer to sid array to allocate
3334 * @return the count of SIDs pulled
3336 int ads_pull_sids(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
,
3337 LDAPMessage
*msg
, const char *field
, struct dom_sid
**sids
)
3339 struct berval
**values
;
3342 values
= ldap_get_values_len(ads
->ldap
.ld
, msg
, field
);
3347 for (i
=0; values
[i
]; i
++)
3351 (*sids
) = talloc_array(mem_ctx
, struct dom_sid
, i
);
3353 ldap_value_free_len(values
);
3361 for (i
=0; values
[i
]; i
++) {
3363 ret
= sid_parse((const uint8_t *)values
[i
]->bv_val
,
3364 values
[i
]->bv_len
, &(*sids
)[count
]);
3366 struct dom_sid_buf buf
;
3367 DBG_DEBUG("pulling SID: %s\n",
3368 dom_sid_str_buf(&(*sids
)[count
], &buf
));
3373 ldap_value_free_len(values
);
3378 * pull a struct security_descriptor from a ADS result
3379 * @param ads connection to ads server
3380 * @param mem_ctx TALLOC_CTX for allocating sid array
3381 * @param msg Results of search
3382 * @param field Attribute to retrieve
3383 * @param sd Pointer to *struct security_descriptor to store result (talloc()ed)
3384 * @return boolean inidicating success
3386 bool ads_pull_sd(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
,
3387 LDAPMessage
*msg
, const char *field
,
3388 struct security_descriptor
**sd
)
3390 struct berval
**values
;
3393 values
= ldap_get_values_len(ads
->ldap
.ld
, msg
, field
);
3395 if (!values
) return false;
3399 status
= unmarshall_sec_desc(mem_ctx
,
3400 (uint8_t *)values
[0]->bv_val
,
3401 values
[0]->bv_len
, sd
);
3402 if (!NT_STATUS_IS_OK(status
)) {
3403 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
3404 nt_errstr(status
)));
3409 ldap_value_free_len(values
);
3414 * in order to support usernames longer than 21 characters we need to
3415 * use both the sAMAccountName and the userPrincipalName attributes
3416 * It seems that not all users have the userPrincipalName attribute set
3418 * @param ads connection to ads server
3419 * @param mem_ctx TALLOC_CTX for allocating sid array
3420 * @param msg Results of search
3421 * @return the username
3423 char *ads_pull_username(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
,
3429 /* lookup_name() only works on the sAMAccountName to
3430 returning the username portion of userPrincipalName
3431 breaks winbindd_getpwnam() */
3433 ret
= ads_pull_string(ads
, mem_ctx
, msg
, "userPrincipalName");
3434 if (ret
&& (p
= strchr_m(ret
, '@'))) {
3439 return ads_pull_string(ads
, mem_ctx
, msg
, "sAMAccountName");
3444 * find the update serial number - this is the core of the ldap cache
3445 * @param ads connection to ads server
3446 * @param ads connection to ADS server
3447 * @param usn Pointer to retrieved update serial number
3448 * @return status of search
3450 ADS_STATUS
ads_USN(ADS_STRUCT
*ads
, uint32_t *usn
)
3452 const char *attrs
[] = {"highestCommittedUSN", NULL
};
3456 status
= ads_do_search_retry(ads
, "", LDAP_SCOPE_BASE
, "(objectclass=*)", attrs
, &res
);
3457 if (!ADS_ERR_OK(status
))
3460 if (ads_count_replies(ads
, res
) != 1) {
3461 ads_msgfree(ads
, res
);
3462 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED
);
3465 if (!ads_pull_uint32(ads
, res
, "highestCommittedUSN", usn
)) {
3466 ads_msgfree(ads
, res
);
3467 return ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE
);
3470 ads_msgfree(ads
, res
);
3474 /* parse a ADS timestring - typical string is
3475 '20020917091222.0Z0' which means 09:12.22 17th September
3477 static time_t ads_parse_time(const char *str
)
3483 if (sscanf(str
, "%4d%2d%2d%2d%2d%2d",
3484 &tm
.tm_year
, &tm
.tm_mon
, &tm
.tm_mday
,
3485 &tm
.tm_hour
, &tm
.tm_min
, &tm
.tm_sec
) != 6) {
3494 /********************************************************************
3495 ********************************************************************/
3497 ADS_STATUS
ads_current_time(ADS_STRUCT
*ads
)
3499 const char *attrs
[] = {"currentTime", NULL
};
3503 TALLOC_CTX
*tmp_ctx
= talloc_stackframe();
3504 ADS_STRUCT
*ads_s
= ads
;
3506 /* establish a new ldap tcp session if necessary */
3508 if ( !ads
->ldap
.ld
) {
3510 * ADS_STRUCT may be being reused after a
3511 * DC lookup, so ads->ldap.ss may already have a
3512 * good address. If not, re-initialize the passed-in
3513 * ADS_STRUCT with the given server.XXXX parameters.
3515 * Note that this doesn't depend on
3516 * ads->server.ldap_server != NULL,
3517 * as the case where ads->server.ldap_server==NULL and
3518 * ads->ldap.ss != zero_address is precisely the DC
3519 * lookup case where ads->ldap.ss was found by going
3520 * through ads_find_dc() again we want to avoid repeating.
3522 if (is_zero_addr(&ads
->ldap
.ss
)) {
3523 ads_s
= ads_init(tmp_ctx
,
3525 ads
->server
.workgroup
,
3526 ads
->server
.ldap_server
,
3528 if (ads_s
== NULL
) {
3529 status
= ADS_ERROR(LDAP_NO_MEMORY
);
3535 * Reset ads->config.flags as it can contain the flags
3536 * returned by the previous CLDAP ping when reusing the struct.
3538 ads_s
->config
.flags
= 0;
3540 ads_s
->auth
.flags
= ADS_AUTH_ANON_BIND
;
3541 status
= ads_connect( ads_s
);
3542 if ( !ADS_ERR_OK(status
))
3546 status
= ads_do_search(ads_s
, "", LDAP_SCOPE_BASE
, "(objectclass=*)", attrs
, &res
);
3547 if (!ADS_ERR_OK(status
)) {
3551 timestr
= ads_pull_string(ads_s
, tmp_ctx
, res
, "currentTime");
3553 ads_msgfree(ads_s
, res
);
3554 status
= ADS_ERROR(LDAP_NO_RESULTS_RETURNED
);
3558 /* but save the time and offset in the original ADS_STRUCT */
3560 ads
->config
.current_time
= ads_parse_time(timestr
);
3562 if (ads
->config
.current_time
!= 0) {
3563 ads
->auth
.time_offset
= ads
->config
.current_time
- time(NULL
);
3564 DEBUG(4,("KDC time offset is %d seconds\n", ads
->auth
.time_offset
));
3567 ads_msgfree(ads
, res
);
3569 status
= ADS_SUCCESS
;
3572 TALLOC_FREE(tmp_ctx
);
3577 /********************************************************************
3578 ********************************************************************/
3580 ADS_STATUS
ads_domain_func_level(ADS_STRUCT
*ads
, uint32_t *val
)
3582 TALLOC_CTX
*tmp_ctx
= talloc_stackframe();
3583 const char *attrs
[] = {"domainFunctionality", NULL
};
3586 ADS_STRUCT
*ads_s
= ads
;
3588 *val
= DS_DOMAIN_FUNCTION_2000
;
3590 /* establish a new ldap tcp session if necessary */
3592 if ( !ads
->ldap
.ld
) {
3594 * ADS_STRUCT may be being reused after a
3595 * DC lookup, so ads->ldap.ss may already have a
3596 * good address. If not, re-initialize the passed-in
3597 * ADS_STRUCT with the given server.XXXX parameters.
3599 * Note that this doesn't depend on
3600 * ads->server.ldap_server != NULL,
3601 * as the case where ads->server.ldap_server==NULL and
3602 * ads->ldap.ss != zero_address is precisely the DC
3603 * lookup case where ads->ldap.ss was found by going
3604 * through ads_find_dc() again we want to avoid repeating.
3606 if (is_zero_addr(&ads
->ldap
.ss
)) {
3607 ads_s
= ads_init(tmp_ctx
,
3609 ads
->server
.workgroup
,
3610 ads
->server
.ldap_server
,
3612 if (ads_s
== NULL
) {
3613 status
= ADS_ERROR_NT(NT_STATUS_NO_MEMORY
);
3619 * Reset ads->config.flags as it can contain the flags
3620 * returned by the previous CLDAP ping when reusing the struct.
3622 ads_s
->config
.flags
= 0;
3624 ads_s
->auth
.flags
= ADS_AUTH_ANON_BIND
;
3625 status
= ads_connect( ads_s
);
3626 if ( !ADS_ERR_OK(status
))
3630 /* If the attribute does not exist assume it is a Windows 2000
3631 functional domain */
3633 status
= ads_do_search(ads_s
, "", LDAP_SCOPE_BASE
, "(objectclass=*)", attrs
, &res
);
3634 if (!ADS_ERR_OK(status
)) {
3635 if ( status
.err
.rc
== LDAP_NO_SUCH_ATTRIBUTE
) {
3636 status
= ADS_SUCCESS
;
3641 if ( !ads_pull_uint32(ads_s
, res
, "domainFunctionality", val
) ) {
3642 DEBUG(5,("ads_domain_func_level: Failed to pull the domainFunctionality attribute.\n"));
3644 DEBUG(3,("ads_domain_func_level: %d\n", *val
));
3647 ads_msgfree(ads_s
, res
);
3650 TALLOC_FREE(tmp_ctx
);
3656 * find the domain sid for our domain
3657 * @param ads connection to ads server
3658 * @param sid Pointer to domain sid
3659 * @return status of search
3661 ADS_STATUS
ads_domain_sid(ADS_STRUCT
*ads
, struct dom_sid
*sid
)
3663 const char *attrs
[] = {"objectSid", NULL
};
3667 rc
= ads_do_search_retry(ads
, ads
->config
.bind_path
, LDAP_SCOPE_BASE
, "(objectclass=*)",
3669 if (!ADS_ERR_OK(rc
)) return rc
;
3670 if (!ads_pull_sid(ads
, res
, "objectSid", sid
)) {
3671 ads_msgfree(ads
, res
);
3672 return ADS_ERROR_SYSTEM(ENOENT
);
3674 ads_msgfree(ads
, res
);
3680 * find our site name
3681 * @param ads connection to ads server
3682 * @param mem_ctx Pointer to talloc context
3683 * @param site_name Pointer to the sitename
3684 * @return status of search
3686 ADS_STATUS
ads_site_dn(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
, const char **site_name
)
3690 const char *dn
, *service_name
;
3691 const char *attrs
[] = { "dsServiceName", NULL
};
3693 status
= ads_do_search(ads
, "", LDAP_SCOPE_BASE
, "(objectclass=*)", attrs
, &res
);
3694 if (!ADS_ERR_OK(status
)) {
3698 service_name
= ads_pull_string(ads
, mem_ctx
, res
, "dsServiceName");
3699 if (service_name
== NULL
) {
3700 ads_msgfree(ads
, res
);
3701 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED
);
3704 ads_msgfree(ads
, res
);
3706 /* go up three levels */
3707 dn
= ads_parent_dn(ads_parent_dn(ads_parent_dn(service_name
)));
3709 return ADS_ERROR(LDAP_NO_MEMORY
);
3712 *site_name
= talloc_strdup(mem_ctx
, dn
);
3713 if (*site_name
== NULL
) {
3714 return ADS_ERROR(LDAP_NO_MEMORY
);
3719 dsServiceName: CN=NTDS Settings,CN=W2K3DC,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,DC=ber,DC=suse,DC=de
3724 * find the site dn where a machine resides
3725 * @param ads connection to ads server
3726 * @param mem_ctx Pointer to talloc context
3727 * @param computer_name name of the machine
3728 * @param site_name Pointer to the sitename
3729 * @return status of search
3731 ADS_STATUS
ads_site_dn_for_machine(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
, const char *computer_name
, const char **site_dn
)
3735 const char *parent
, *filter
;
3736 char *config_context
= NULL
;
3739 /* shortcut a query */
3740 if (strequal(computer_name
, ads
->config
.ldap_server_name
)) {
3741 return ads_site_dn(ads
, mem_ctx
, site_dn
);
3744 status
= ads_config_path(ads
, mem_ctx
, &config_context
);
3745 if (!ADS_ERR_OK(status
)) {
3749 filter
= talloc_asprintf(mem_ctx
, "(cn=%s)", computer_name
);
3750 if (filter
== NULL
) {
3751 return ADS_ERROR(LDAP_NO_MEMORY
);
3754 status
= ads_do_search(ads
, config_context
, LDAP_SCOPE_SUBTREE
,
3755 filter
, NULL
, &res
);
3756 if (!ADS_ERR_OK(status
)) {
3760 if (ads_count_replies(ads
, res
) != 1) {
3761 ads_msgfree(ads
, res
);
3762 return ADS_ERROR(LDAP_NO_SUCH_OBJECT
);
3765 dn
= ads_get_dn(ads
, mem_ctx
, res
);
3767 ads_msgfree(ads
, res
);
3768 return ADS_ERROR(LDAP_NO_MEMORY
);
3771 /* go up three levels */
3772 parent
= ads_parent_dn(ads_parent_dn(ads_parent_dn(dn
)));
3773 if (parent
== NULL
) {
3774 ads_msgfree(ads
, res
);
3776 return ADS_ERROR(LDAP_NO_MEMORY
);
3779 *site_dn
= talloc_strdup(mem_ctx
, parent
);
3780 if (*site_dn
== NULL
) {
3781 ads_msgfree(ads
, res
);
3783 return ADS_ERROR(LDAP_NO_MEMORY
);
3787 ads_msgfree(ads
, res
);
3793 * get the upn suffixes for a domain
3794 * @param ads connection to ads server
3795 * @param mem_ctx Pointer to talloc context
3796 * @param suffixes Pointer to an array of suffixes
3797 * @param num_suffixes Pointer to the number of suffixes
3798 * @return status of search
3800 ADS_STATUS
ads_upn_suffixes(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
, char ***suffixes
, size_t *num_suffixes
)
3805 char *config_context
= NULL
;
3806 const char *attrs
[] = { "uPNSuffixes", NULL
};
3808 status
= ads_config_path(ads
, mem_ctx
, &config_context
);
3809 if (!ADS_ERR_OK(status
)) {
3813 base
= talloc_asprintf(mem_ctx
, "cn=Partitions,%s", config_context
);
3815 return ADS_ERROR(LDAP_NO_MEMORY
);
3818 status
= ads_search_dn(ads
, &res
, base
, attrs
);
3819 if (!ADS_ERR_OK(status
)) {
3823 if (ads_count_replies(ads
, res
) != 1) {
3824 ads_msgfree(ads
, res
);
3825 return ADS_ERROR(LDAP_NO_SUCH_OBJECT
);
3828 (*suffixes
) = ads_pull_strings(ads
, mem_ctx
, res
, "uPNSuffixes", num_suffixes
);
3829 if ((*suffixes
) == NULL
) {
3830 ads_msgfree(ads
, res
);
3831 return ADS_ERROR(LDAP_NO_MEMORY
);
3834 ads_msgfree(ads
, res
);
3840 * get the joinable ous for a domain
3841 * @param ads connection to ads server
3842 * @param mem_ctx Pointer to talloc context
3843 * @param ous Pointer to an array of ous
3844 * @param num_ous Pointer to the number of ous
3845 * @return status of search
3847 ADS_STATUS
ads_get_joinable_ous(ADS_STRUCT
*ads
,
3848 TALLOC_CTX
*mem_ctx
,
3853 LDAPMessage
*res
= NULL
;
3854 LDAPMessage
*msg
= NULL
;
3855 const char *attrs
[] = { "dn", NULL
};
3858 status
= ads_search(ads
, &res
,
3859 "(|(objectClass=domain)(objectclass=organizationalUnit))",
3861 if (!ADS_ERR_OK(status
)) {
3865 count
= ads_count_replies(ads
, res
);
3867 ads_msgfree(ads
, res
);
3868 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED
);
3871 for (msg
= ads_first_entry(ads
, res
); msg
;
3872 msg
= ads_next_entry(ads
, msg
)) {
3873 const char **p
= discard_const_p(const char *, *ous
);
3876 dn
= ads_get_dn(ads
, talloc_tos(), msg
);
3878 ads_msgfree(ads
, res
);
3879 return ADS_ERROR(LDAP_NO_MEMORY
);
3882 if (!add_string_to_array(mem_ctx
, dn
, &p
, num_ous
)) {
3884 ads_msgfree(ads
, res
);
3885 return ADS_ERROR(LDAP_NO_MEMORY
);
3889 *ous
= discard_const_p(char *, p
);
3892 ads_msgfree(ads
, res
);
3899 * pull a struct dom_sid from an extended dn string
3900 * @param mem_ctx TALLOC_CTX
3901 * @param extended_dn string
3902 * @param flags string type of extended_dn
3903 * @param sid pointer to a struct dom_sid
3904 * @return NT_STATUS_OK on success,
3905 * NT_INVALID_PARAMETER on error,
3906 * NT_STATUS_NOT_FOUND if no SID present
3908 ADS_STATUS
ads_get_sid_from_extended_dn(TALLOC_CTX
*mem_ctx
,
3909 const char *extended_dn
,
3910 enum ads_extended_dn_flags flags
,
3911 struct dom_sid
*sid
)
3916 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER
);
3919 /* otherwise extended_dn gets stripped off */
3920 if ((dn
= talloc_strdup(mem_ctx
, extended_dn
)) == NULL
) {
3921 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER
);
3924 * ADS_EXTENDED_DN_HEX_STRING:
3925 * <GUID=238e1963cb390f4bb032ba0105525a29>;<SID=010500000000000515000000bb68c8fd6b61b427572eb04556040000>;CN=gd,OU=berlin,OU=suse,DC=ber,DC=suse,DC=de
3927 * ADS_EXTENDED_DN_STRING (only with w2k3):
3928 * <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
3930 * Object with no SID, such as an Exchange Public Folder
3931 * <GUID=28907fb4bdf6854993e7f0a10b504e7c>;CN=public,CN=Microsoft Exchange System Objects,DC=sd2k3ms,DC=west,DC=isilon,DC=com
3934 p
= strchr(dn
, ';');
3936 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER
);
3939 if (strncmp(p
, ";<SID=", strlen(";<SID=")) != 0) {
3940 DEBUG(5,("No SID present in extended dn\n"));
3941 return ADS_ERROR_NT(NT_STATUS_NOT_FOUND
);
3944 p
+= strlen(";<SID=");
3948 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER
);
3953 DEBUG(100,("ads_get_sid_from_extended_dn: sid string is %s\n", p
));
3957 case ADS_EXTENDED_DN_STRING
:
3958 if (!string_to_sid(sid
, p
)) {
3959 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER
);
3962 case ADS_EXTENDED_DN_HEX_STRING
: {
3967 buf_len
= strhex_to_str(buf
, sizeof(buf
), p
, strlen(p
));
3969 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER
);
3972 ret
= sid_parse((const uint8_t *)buf
, buf_len
, sid
);
3974 DEBUG(10,("failed to parse sid\n"));
3975 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER
);
3980 DEBUG(10,("unknown extended dn format\n"));
3981 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER
);
3984 return ADS_ERROR_NT(NT_STATUS_OK
);
3987 /********************************************************************
3988 ********************************************************************/
3990 char* ads_get_dnshostname( ADS_STRUCT
*ads
, TALLOC_CTX
*ctx
, const char *machine_name
)
3992 LDAPMessage
*res
= NULL
;
3997 status
= ads_find_machine_acct(ads
, &res
, machine_name
);
3998 if (!ADS_ERR_OK(status
)) {
3999 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
4000 lp_netbios_name()));
4004 if ( (count
= ads_count_replies(ads
, res
)) != 1 ) {
4005 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count
));
4009 if ( (name
= ads_pull_string(ads
, ctx
, res
, "dNSHostName")) == NULL
) {
4010 DEBUG(0,("ads_get_dnshostname: No dNSHostName attribute!\n"));
4014 ads_msgfree(ads
, res
);
4019 /********************************************************************
4020 ********************************************************************/
4022 static char **get_addl_hosts(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
,
4023 LDAPMessage
*msg
, size_t *num_values
)
4025 const char *field
= "msDS-AdditionalDnsHostName";
4026 struct berval
**values
= NULL
;
4028 size_t i
, converted_size
;
4031 * Windows DC implicitly adds a short name for each FQDN added to
4032 * msDS-AdditionalDnsHostName, but it comes with a strage binary
4033 * suffix "\0$" which we should ignore (see bug #14406).
4036 values
= ldap_get_values_len(ads
->ldap
.ld
, msg
, field
);
4037 if (values
== NULL
) {
4041 *num_values
= ldap_count_values_len(values
);
4043 ret
= talloc_array(mem_ctx
, char *, *num_values
+ 1);
4045 ldap_value_free_len(values
);
4049 for (i
= 0; i
< *num_values
; i
++) {
4051 if (!convert_string_talloc(mem_ctx
, CH_UTF8
, CH_UNIX
,
4053 strnlen(values
[i
]->bv_val
,
4055 &ret
[i
], &converted_size
)) {
4056 ldap_value_free_len(values
);
4062 ldap_value_free_len(values
);
4066 ADS_STATUS
ads_get_additional_dns_hostnames(TALLOC_CTX
*mem_ctx
,
4068 const char *machine_name
,
4069 char ***hostnames_array
,
4070 size_t *num_hostnames
)
4073 LDAPMessage
*res
= NULL
;
4076 status
= ads_find_machine_acct(ads
,
4079 if (!ADS_ERR_OK(status
)) {
4080 DEBUG(1,("Host Account for %s not found... skipping operation.\n",
4085 count
= ads_count_replies(ads
, res
);
4087 status
= ADS_ERROR(LDAP_NO_SUCH_OBJECT
);
4091 *hostnames_array
= get_addl_hosts(ads
, mem_ctx
, res
, num_hostnames
);
4092 if (*hostnames_array
== NULL
) {
4093 DEBUG(1, ("Host account for %s does not have msDS-AdditionalDnsHostName.\n",
4095 status
= ADS_ERROR(LDAP_NO_SUCH_OBJECT
);
4100 ads_msgfree(ads
, res
);
4105 /********************************************************************
4106 ********************************************************************/
4108 char* ads_get_upn( ADS_STRUCT
*ads
, TALLOC_CTX
*ctx
, const char *machine_name
)
4110 LDAPMessage
*res
= NULL
;
4115 status
= ads_find_machine_acct(ads
, &res
, machine_name
);
4116 if (!ADS_ERR_OK(status
)) {
4117 DEBUG(0,("ads_get_upn: Failed to find account for %s\n",
4118 lp_netbios_name()));
4122 if ( (count
= ads_count_replies(ads
, res
)) != 1 ) {
4123 DEBUG(1,("ads_get_upn: %d entries returned!\n", count
));
4127 if ( (name
= ads_pull_string(ads
, ctx
, res
, "userPrincipalName")) == NULL
) {
4128 DEBUG(2,("ads_get_upn: No userPrincipalName attribute!\n"));
4132 ads_msgfree(ads
, res
);
4137 /********************************************************************
4138 ********************************************************************/
4140 bool ads_has_samaccountname( ADS_STRUCT
*ads
, TALLOC_CTX
*ctx
, const char *machine_name
)
4142 LDAPMessage
*res
= NULL
;
4148 status
= ads_find_machine_acct(ads
, &res
, machine_name
);
4149 if (!ADS_ERR_OK(status
)) {
4150 DEBUG(0,("ads_has_samaccountname: Failed to find account for %s\n",
4151 lp_netbios_name()));
4155 if ( (count
= ads_count_replies(ads
, res
)) != 1 ) {
4156 DEBUG(1,("ads_has_samaccountname: %d entries returned!\n", count
));
4160 if ( (name
= ads_pull_string(ads
, ctx
, res
, "sAMAccountName")) == NULL
) {
4161 DEBUG(0,("ads_has_samaccountname: No sAMAccountName attribute!\n"));
4165 ads_msgfree(ads
, res
);
4167 ok
= (strlen(name
) > 0);
4175 SAVED CODE
- we used to join via ldap
- remember how we did
this. JRA
.
4178 * Join a machine to a realm
4179 * Creates the machine account and sets the machine password
4180 * @param ads connection to ads server
4181 * @param machine name of host to add
4182 * @param org_unit Organizational unit to place machine in
4183 * @return status of join
4185 ADS_STATUS
ads_join_realm(ADS_STRUCT
*ads
, const char *machine_name
,
4186 uint32_t account_type
, const char *org_unit
)
4189 LDAPMessage
*res
= NULL
;
4192 /* machine name must be lowercase */
4193 machine
= SMB_STRDUP(machine_name
);
4194 strlower_m(machine
);
4197 status = ads_find_machine_acct(ads, (void **)&res, machine);
4198 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
4199 DEBUG(0, ("Host account for %s already exists - deleting old account\n", machine));
4200 status = ads_leave_realm(ads, machine);
4201 if (!ADS_ERR_OK(status)) {
4202 DEBUG(0, ("Failed to delete host '%s' from the '%s' realm.\n",
4203 machine, ads->config.realm));
4208 status
= ads_add_machine_acct(ads
, machine
, account_type
, org_unit
);
4209 if (!ADS_ERR_OK(status
)) {
4210 DEBUG(0, ("ads_join_realm: ads_add_machine_acct failed (%s): %s\n", machine
, ads_errstr(status
)));
4215 status
= ads_find_machine_acct(ads
, (void **)(void *)&res
, machine
);
4216 if (!ADS_ERR_OK(status
)) {
4217 DEBUG(0, ("ads_join_realm: Host account test failed for machine %s\n", machine
));
4223 ads_msgfree(ads
, res
);
4230 * Delete a machine from the realm
4231 * @param ads connection to ads server
4232 * @param hostname Machine to remove
4233 * @return status of delete
4235 ADS_STATUS
ads_leave_realm(ADS_STRUCT
*ads
, const char *hostname
)
4240 char *hostnameDN
, *host
;
4242 LDAPControl ldap_control
;
4243 LDAPControl
* pldap_control
[2] = {NULL
, NULL
};
4245 pldap_control
[0] = &ldap_control
;
4246 memset(&ldap_control
, 0, sizeof(LDAPControl
));
4247 ldap_control
.ldctl_oid
= discard_const_p(char, LDAP_SERVER_TREE_DELETE_OID
);
4249 /* hostname must be lowercase */
4250 host
= SMB_STRDUP(hostname
);
4251 if (!strlower_m(host
)) {
4253 return ADS_ERROR_SYSTEM(EINVAL
);
4256 status
= ads_find_machine_acct(ads
, &res
, host
);
4257 if (!ADS_ERR_OK(status
)) {
4258 DEBUG(0, ("Host account for %s does not exist.\n", host
));
4263 msg
= ads_first_entry(ads
, res
);
4266 return ADS_ERROR_SYSTEM(ENOENT
);
4269 hostnameDN
= ads_get_dn(ads
, talloc_tos(), (LDAPMessage
*)msg
);
4270 if (hostnameDN
== NULL
) {
4272 return ADS_ERROR_SYSTEM(ENOENT
);
4275 rc
= ldap_delete_ext_s(ads
->ldap
.ld
, hostnameDN
, pldap_control
, NULL
);
4277 DEBUG(3,("ldap_delete_ext_s failed with error code %d\n", rc
));
4279 DEBUG(3,("ldap_delete_ext_s succeeded with error code %d\n", rc
));
4282 if (rc
!= LDAP_SUCCESS
) {
4283 const char *attrs
[] = { "cn", NULL
};
4284 LDAPMessage
*msg_sub
;
4286 /* we only search with scope ONE, we do not expect any further
4287 * objects to be created deeper */
4289 status
= ads_do_search_retry(ads
, hostnameDN
,
4290 LDAP_SCOPE_ONELEVEL
,
4291 "(objectclass=*)", attrs
, &res
);
4293 if (!ADS_ERR_OK(status
)) {
4295 TALLOC_FREE(hostnameDN
);
4299 for (msg_sub
= ads_first_entry(ads
, res
); msg_sub
;
4300 msg_sub
= ads_next_entry(ads
, msg_sub
)) {
4304 if ((dn
= ads_get_dn(ads
, talloc_tos(), msg_sub
)) == NULL
) {
4306 TALLOC_FREE(hostnameDN
);
4307 return ADS_ERROR(LDAP_NO_MEMORY
);
4310 status
= ads_del_dn(ads
, dn
);
4311 if (!ADS_ERR_OK(status
)) {
4312 DEBUG(3,("failed to delete dn %s: %s\n", dn
, ads_errstr(status
)));
4315 TALLOC_FREE(hostnameDN
);
4322 /* there should be no subordinate objects anymore */
4323 status
= ads_do_search_retry(ads
, hostnameDN
,
4324 LDAP_SCOPE_ONELEVEL
,
4325 "(objectclass=*)", attrs
, &res
);
4327 if (!ADS_ERR_OK(status
) || ( (ads_count_replies(ads
, res
)) > 0 ) ) {
4329 TALLOC_FREE(hostnameDN
);
4333 /* delete hostnameDN now */
4334 status
= ads_del_dn(ads
, hostnameDN
);
4335 if (!ADS_ERR_OK(status
)) {
4337 DEBUG(3,("failed to delete dn %s: %s\n", hostnameDN
, ads_errstr(status
)));
4338 TALLOC_FREE(hostnameDN
);
4343 TALLOC_FREE(hostnameDN
);
4345 status
= ads_find_machine_acct(ads
, &res
, host
);
4346 if ((status
.error_type
== ENUM_ADS_ERROR_LDAP
) &&
4347 (status
.err
.rc
!= LDAP_NO_SUCH_OBJECT
)) {
4348 DEBUG(3, ("Failed to remove host account.\n"));
4358 * pull all token-sids from an LDAP dn
4359 * @param ads connection to ads server
4360 * @param mem_ctx TALLOC_CTX for allocating sid array
4361 * @param dn of LDAP object
4362 * @param user_sid pointer to struct dom_sid (objectSid)
4363 * @param primary_group_sid pointer to struct dom_sid (self composed)
4364 * @param sids pointer to sid array to allocate
4365 * @param num_sids counter of SIDs pulled
4366 * @return status of token query
4368 ADS_STATUS
ads_get_tokensids(ADS_STRUCT
*ads
,
4369 TALLOC_CTX
*mem_ctx
,
4371 struct dom_sid
*user_sid
,
4372 struct dom_sid
*primary_group_sid
,
4373 struct dom_sid
**sids
,
4377 LDAPMessage
*res
= NULL
;
4379 size_t tmp_num_sids
;
4380 struct dom_sid
*tmp_sids
;
4381 struct dom_sid tmp_user_sid
;
4382 struct dom_sid tmp_primary_group_sid
;
4384 const char *attrs
[] = {
4391 status
= ads_search_retry_dn(ads
, &res
, dn
, attrs
);
4392 if (!ADS_ERR_OK(status
)) {
4396 count
= ads_count_replies(ads
, res
);
4398 ads_msgfree(ads
, res
);
4399 return ADS_ERROR_LDAP(LDAP_NO_SUCH_OBJECT
);
4402 if (!ads_pull_sid(ads
, res
, "objectSid", &tmp_user_sid
)) {
4403 ads_msgfree(ads
, res
);
4404 return ADS_ERROR_LDAP(LDAP_NO_MEMORY
);
4407 if (!ads_pull_uint32(ads
, res
, "primaryGroupID", &pgid
)) {
4408 ads_msgfree(ads
, res
);
4409 return ADS_ERROR_LDAP(LDAP_NO_MEMORY
);
4413 /* hack to compose the primary group sid without knowing the
4416 struct dom_sid domsid
;
4418 sid_copy(&domsid
, &tmp_user_sid
);
4420 if (!sid_split_rid(&domsid
, NULL
)) {
4421 ads_msgfree(ads
, res
);
4422 return ADS_ERROR_LDAP(LDAP_NO_MEMORY
);
4425 if (!sid_compose(&tmp_primary_group_sid
, &domsid
, pgid
)) {
4426 ads_msgfree(ads
, res
);
4427 return ADS_ERROR_LDAP(LDAP_NO_MEMORY
);
4431 tmp_num_sids
= ads_pull_sids(ads
, mem_ctx
, res
, "tokenGroups", &tmp_sids
);
4433 if (tmp_num_sids
== 0 || !tmp_sids
) {
4434 ads_msgfree(ads
, res
);
4435 return ADS_ERROR_LDAP(LDAP_NO_MEMORY
);
4439 *num_sids
= tmp_num_sids
;
4447 *user_sid
= tmp_user_sid
;
4450 if (primary_group_sid
) {
4451 *primary_group_sid
= tmp_primary_group_sid
;
4454 DEBUG(10,("ads_get_tokensids: returned %d sids\n", (int)tmp_num_sids
+ 2));
4456 ads_msgfree(ads
, res
);
4457 return ADS_ERROR_LDAP(LDAP_SUCCESS
);
4461 * Find a sAMAccoutName in LDAP
4462 * @param ads connection to ads server
4463 * @param mem_ctx TALLOC_CTX for allocating sid array
4464 * @param samaccountname to search
4465 * @param uac_ret uint32_t pointer userAccountControl attribute value
4466 * @param dn_ret pointer to dn
4467 * @return status of token query
4469 ADS_STATUS
ads_find_samaccount(ADS_STRUCT
*ads
,
4470 TALLOC_CTX
*mem_ctx
,
4471 const char *samaccountname
,
4473 const char **dn_ret
)
4476 const char *attrs
[] = { "userAccountControl", NULL
};
4478 LDAPMessage
*res
= NULL
;
4482 filter
= talloc_asprintf(mem_ctx
, "(&(objectclass=user)(sAMAccountName=%s))",
4484 if (filter
== NULL
) {
4485 status
= ADS_ERROR_NT(NT_STATUS_NO_MEMORY
);
4489 status
= ads_do_search_all(ads
, ads
->config
.bind_path
,
4491 filter
, attrs
, &res
);
4493 if (!ADS_ERR_OK(status
)) {
4497 if (ads_count_replies(ads
, res
) != 1) {
4498 status
= ADS_ERROR(LDAP_NO_RESULTS_RETURNED
);
4502 dn
= ads_get_dn(ads
, talloc_tos(), res
);
4504 status
= ADS_ERROR(LDAP_NO_MEMORY
);
4508 if (!ads_pull_uint32(ads
, res
, "userAccountControl", &uac
)) {
4509 status
= ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE
);
4518 *dn_ret
= talloc_strdup(mem_ctx
, dn
);
4520 status
= ADS_ERROR(LDAP_NO_MEMORY
);
4526 ads_msgfree(ads
, res
);
4532 * find our configuration path
4533 * @param ads connection to ads server
4534 * @param mem_ctx Pointer to talloc context
4535 * @param config_path Pointer to the config path
4536 * @return status of search
4538 ADS_STATUS
ads_config_path(ADS_STRUCT
*ads
,
4539 TALLOC_CTX
*mem_ctx
,
4543 LDAPMessage
*res
= NULL
;
4544 const char *config_context
= NULL
;
4545 const char *attrs
[] = { "configurationNamingContext", NULL
};
4547 status
= ads_do_search(ads
, "", LDAP_SCOPE_BASE
,
4548 "(objectclass=*)", attrs
, &res
);
4549 if (!ADS_ERR_OK(status
)) {
4553 config_context
= ads_pull_string(ads
, mem_ctx
, res
,
4554 "configurationNamingContext");
4555 ads_msgfree(ads
, res
);
4556 if (!config_context
) {
4557 return ADS_ERROR(LDAP_NO_MEMORY
);
4561 *config_path
= talloc_strdup(mem_ctx
, config_context
);
4562 if (!*config_path
) {
4563 return ADS_ERROR(LDAP_NO_MEMORY
);
4567 return ADS_ERROR(LDAP_SUCCESS
);
4571 * find the displayName of an extended right
4572 * @param ads connection to ads server
4573 * @param config_path The config path
4574 * @param mem_ctx Pointer to talloc context
4575 * @param GUID struct of the rightsGUID
4576 * @return status of search
4578 const char *ads_get_extended_right_name_by_guid(ADS_STRUCT
*ads
,
4579 const char *config_path
,
4580 TALLOC_CTX
*mem_ctx
,
4581 const struct GUID
*rights_guid
)
4584 LDAPMessage
*res
= NULL
;
4586 const char *attrs
[] = { "displayName", NULL
};
4587 const char *result
= NULL
;
4590 if (!ads
|| !mem_ctx
|| !rights_guid
) {
4594 expr
= talloc_asprintf(mem_ctx
, "(rightsGuid=%s)",
4595 GUID_string(mem_ctx
, rights_guid
));
4600 path
= talloc_asprintf(mem_ctx
, "cn=Extended-Rights,%s", config_path
);
4605 rc
= ads_do_search_retry(ads
, path
, LDAP_SCOPE_SUBTREE
,
4607 if (!ADS_ERR_OK(rc
)) {
4611 if (ads_count_replies(ads
, res
) != 1) {
4615 result
= ads_pull_string(ads
, mem_ctx
, res
, "displayName");
4618 ads_msgfree(ads
, res
);
4623 * verify or build and verify an account ou
4624 * @param mem_ctx Pointer to talloc context
4625 * @param ads connection to ads server
4627 * @return status of search
4630 ADS_STATUS
ads_check_ou_dn(TALLOC_CTX
*mem_ctx
,
4632 const char **account_ou
)
4638 if (account_ou
== NULL
) {
4639 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER
);
4642 if (*account_ou
!= NULL
) {
4643 exploded_dn
= ldap_explode_dn(*account_ou
, 0);
4645 ldap_value_free(exploded_dn
);
4650 ou_string
= ads_ou_string(ads
, *account_ou
);
4652 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX
);
4655 name
= talloc_asprintf(mem_ctx
, "%s,%s", ou_string
,
4656 ads
->config
.bind_path
);
4657 SAFE_FREE(ou_string
);
4660 return ADS_ERROR_LDAP(LDAP_NO_MEMORY
);
4663 exploded_dn
= ldap_explode_dn(name
, 0);
4665 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX
);
4667 ldap_value_free(exploded_dn
);