2 Unix SMB/CIFS implementation.
3 ads (active directory) utility library
4 Copyright (C) Andrew Tridgell 2001
5 Copyright (C) Remus Koos 2001
6 Copyright (C) Jim McDonough <jmcd@us.ibm.com> 2002
7 Copyright (C) Guenther Deschner 2005
8 Copyright (C) Gerald Carter 2006
10 This program is free software; you can redistribute it and/or modify
11 it under the terms of the GNU General Public License as published by
12 the Free Software Foundation; either version 3 of the License, or
13 (at your option) any later version.
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License for more details.
20 You should have received a copy of the GNU General Public License
21 along with this program. If not, see <http://www.gnu.org/licenses/>.
26 #include "libads/sitename_cache.h"
27 #include "libads/cldap.h"
28 #include "libads/dns.h"
29 #include "../libds/common/flags.h"
31 #include "../libcli/security/security.h"
37 * @brief basic ldap client-side routines for ads server communications
39 * The routines contained here should do the necessary ldap calls for
42 * Important note: attribute names passed into ads_ routines must
43 * already be in UTF-8 format. We do not convert them because in almost
44 * all cases, they are just ascii (which is represented with the same
45 * codepoints in UTF-8). This may have to change at some point
49 #define LDAP_SERVER_TREE_DELETE_OID "1.2.840.113556.1.4.805"
51 static SIG_ATOMIC_T gotalarm
;
53 /***************************************************************
54 Signal function to tell us we timed out.
55 ****************************************************************/
57 static void gotalarm_sig(int signum
)
62 LDAP
*ldap_open_with_timeout(const char *server
,
63 struct sockaddr_storage
*ss
,
64 int port
, unsigned int to
)
68 DEBUG(10, ("Opening connection to LDAP server '%s:%d', timeout "
69 "%u seconds\n", server
, port
, to
));
71 #if defined(HAVE_LDAP_INIT_FD) && defined(SOCKET_WRAPPER)
72 /* Only use this private LDAP function if we are in make test,
73 * as this is the best way to get the emulated TCP socket into
75 if (socket_wrapper_dir() != NULL
) {
80 status
= open_socket_out(ss
, port
, to
, &fd
);
82 if (!NT_STATUS_IS_OK(status
)) {
86 #ifndef LDAP_PROTO_TCP
87 #define LDAP_PROTO_TCP 1
89 uri
= talloc_asprintf(talloc_tos(), "ldap://%s:%u", server
, port
);
93 ldap_err
= ldap_init_fd(fd
, LDAP_PROTO_TCP
, uri
, &ldp
);
96 if (ldap_err
!= LDAP_SUCCESS
) {
106 CatchSignal(SIGALRM
, gotalarm_sig
);
108 /* End setup timeout. */
111 ldp
= ldap_open(server
, port
);
114 DEBUG(2,("Could not open connection to LDAP server %s:%d: %s\n",
115 server
, port
, strerror(errno
)));
117 DEBUG(10, ("Connected to LDAP server '%s:%d'\n", server
, port
));
121 /* Teardown timeout. */
123 CatchSignal(SIGALRM
, SIG_IGN
);
129 static int ldap_search_with_timeout(LDAP
*ld
,
130 LDAP_CONST
char *base
,
132 LDAP_CONST
char *filter
,
135 LDAPControl
**sctrls
,
136 LDAPControl
**cctrls
,
140 int to
= lp_ldap_timeout();
141 struct timeval timeout
;
142 struct timeval
*timeout_ptr
= NULL
;
145 /* Setup timeout for the ldap_search_ext_s call - local and remote. */
151 timeout_ptr
= &timeout
;
153 /* Setup alarm timeout. */
154 CatchSignal(SIGALRM
, gotalarm_sig
);
155 /* Make the alarm time one second beyond
156 the timout we're setting for the
157 remote search timeout, to allow that
158 to fire in preference. */
160 /* End setup timeout. */
164 result
= ldap_search_ext_s(ld
, base
, scope
, filter
, attrs
,
165 attrsonly
, sctrls
, cctrls
, timeout_ptr
,
169 /* Teardown alarm timeout. */
170 CatchSignal(SIGALRM
, SIG_IGN
);
175 return LDAP_TIMELIMIT_EXCEEDED
;
178 * A bug in OpenLDAP means ldap_search_ext_s can return
179 * LDAP_SUCCESS but with a NULL res pointer. Cope with
180 * this. See bug #6279 for details. JRA.
184 return LDAP_TIMELIMIT_EXCEEDED
;
190 /**********************************************
191 Do client and server sitename match ?
192 **********************************************/
194 bool ads_sitename_match(ADS_STRUCT
*ads
)
196 if (ads
->config
.server_site_name
== NULL
&&
197 ads
->config
.client_site_name
== NULL
) {
198 DEBUG(10,("ads_sitename_match: both null\n"));
201 if (ads
->config
.server_site_name
&&
202 ads
->config
.client_site_name
&&
203 strequal(ads
->config
.server_site_name
,
204 ads
->config
.client_site_name
)) {
205 DEBUG(10,("ads_sitename_match: name %s match\n", ads
->config
.server_site_name
));
208 DEBUG(10,("ads_sitename_match: no match between server: %s and client: %s\n",
209 ads
->config
.server_site_name
? ads
->config
.server_site_name
: "NULL",
210 ads
->config
.client_site_name
? ads
->config
.client_site_name
: "NULL"));
214 /**********************************************
215 Is this the closest DC ?
216 **********************************************/
218 bool ads_closest_dc(ADS_STRUCT
*ads
)
220 if (ads
->config
.flags
& NBT_SERVER_CLOSEST
) {
221 DEBUG(10,("ads_closest_dc: NBT_SERVER_CLOSEST flag set\n"));
225 /* not sure if this can ever happen */
226 if (ads_sitename_match(ads
)) {
227 DEBUG(10,("ads_closest_dc: NBT_SERVER_CLOSEST flag not set but sites match\n"));
231 if (ads
->config
.client_site_name
== NULL
) {
232 DEBUG(10,("ads_closest_dc: client belongs to no site\n"));
236 DEBUG(10,("ads_closest_dc: %s is not the closest DC\n",
237 ads
->config
.ldap_server_name
));
244 try a connection to a given ldap server, returning True and setting the servers IP
245 in the ads struct if successful
247 static bool ads_try_connect(ADS_STRUCT
*ads
, const char *server
, bool gc
)
249 struct NETLOGON_SAM_LOGON_RESPONSE_EX cldap_reply
;
250 TALLOC_CTX
*frame
= talloc_stackframe();
252 struct sockaddr_storage ss
;
253 char addr
[INET6_ADDRSTRLEN
];
255 if (!server
|| !*server
) {
260 if (!resolve_name(server
, &ss
, 0x20, true)) {
261 DEBUG(5,("ads_try_connect: unable to resolve name %s\n",
266 print_sockaddr(addr
, sizeof(addr
), &ss
);
268 DEBUG(5,("ads_try_connect: sending CLDAP request to %s (realm: %s)\n",
269 addr
, ads
->server
.realm
));
271 ZERO_STRUCT( cldap_reply
);
273 if ( !ads_cldap_netlogon_5(frame
, &ss
, ads
->server
.realm
, &cldap_reply
) ) {
274 DEBUG(3,("ads_try_connect: CLDAP request %s failed.\n", addr
));
279 /* Check the CLDAP reply flags */
281 if ( !(cldap_reply
.server_type
& NBT_SERVER_LDAP
) ) {
282 DEBUG(1,("ads_try_connect: %s's CLDAP reply says it is not an LDAP server!\n",
288 /* Fill in the ads->config values */
290 SAFE_FREE(ads
->config
.realm
);
291 SAFE_FREE(ads
->config
.bind_path
);
292 SAFE_FREE(ads
->config
.ldap_server_name
);
293 SAFE_FREE(ads
->config
.server_site_name
);
294 SAFE_FREE(ads
->config
.client_site_name
);
295 SAFE_FREE(ads
->server
.workgroup
);
297 ads
->config
.flags
= cldap_reply
.server_type
;
298 ads
->config
.ldap_server_name
= SMB_STRDUP(cldap_reply
.pdc_dns_name
);
299 ads
->config
.realm
= SMB_STRDUP(cldap_reply
.dns_domain
);
300 strupper_m(ads
->config
.realm
);
301 ads
->config
.bind_path
= ads_build_dn(ads
->config
.realm
);
302 if (*cldap_reply
.server_site
) {
303 ads
->config
.server_site_name
=
304 SMB_STRDUP(cldap_reply
.server_site
);
306 if (*cldap_reply
.client_site
) {
307 ads
->config
.client_site_name
=
308 SMB_STRDUP(cldap_reply
.client_site
);
310 ads
->server
.workgroup
= SMB_STRDUP(cldap_reply
.domain_name
);
312 ads
->ldap
.port
= gc
? LDAP_GC_PORT
: LDAP_PORT
;
315 /* Store our site name. */
316 sitename_store( cldap_reply
.domain_name
, cldap_reply
.client_site
);
317 sitename_store( cldap_reply
.dns_domain
, cldap_reply
.client_site
);
327 /**********************************************************************
328 Try to find an AD dc using our internal name resolution routines
329 Try the realm first and then then workgroup name if netbios is not
331 **********************************************************************/
333 static NTSTATUS
ads_find_dc(ADS_STRUCT
*ads
)
335 const char *c_domain
;
338 struct ip_service
*ip_list
;
341 bool got_realm
= False
;
342 bool use_own_domain
= False
;
344 NTSTATUS status
= NT_STATUS_UNSUCCESSFUL
;
346 /* if the realm and workgroup are both empty, assume they are ours */
349 c_realm
= ads
->server
.realm
;
351 if ( !c_realm
|| !*c_realm
) {
352 /* special case where no realm and no workgroup means our own */
353 if ( !ads
->server
.workgroup
|| !*ads
->server
.workgroup
) {
354 use_own_domain
= True
;
355 c_realm
= lp_realm();
359 if (c_realm
&& *c_realm
)
362 /* we need to try once with the realm name and fallback to the
363 netbios domain name if we fail (if netbios has not been disabled */
365 if ( !got_realm
&& !lp_disable_netbios() ) {
366 c_realm
= ads
->server
.workgroup
;
367 if (!c_realm
|| !*c_realm
) {
368 if ( use_own_domain
)
369 c_realm
= lp_workgroup();
373 if ( !c_realm
|| !*c_realm
) {
374 DEBUG(1, ("ads_find_dc: no realm or workgroup! Don't know "
376 return NT_STATUS_INVALID_PARAMETER
; /* rather need MISSING_PARAMETER ... */
379 if ( use_own_domain
) {
380 c_domain
= lp_workgroup();
382 c_domain
= ads
->server
.workgroup
;
389 * In case of LDAP we use get_dc_name() as that
390 * creates the custom krb5.conf file
392 if (!(ads
->auth
.flags
& ADS_AUTH_NO_BIND
)) {
394 struct sockaddr_storage ip_out
;
396 DEBUG(6,("ads_find_dc: (ldap) looking for %s '%s'\n",
397 (got_realm
? "realm" : "domain"), realm
));
399 if (get_dc_name(domain
, realm
, srv_name
, &ip_out
)) {
401 * we call ads_try_connect() to fill in the
402 * ads->config details
404 if (ads_try_connect(ads
, srv_name
, false)) {
409 return NT_STATUS_NO_LOGON_SERVERS
;
412 sitename
= sitename_fetch(realm
);
416 DEBUG(6,("ads_find_dc: (cldap) looking for %s '%s'\n",
417 (got_realm
? "realm" : "domain"), realm
));
419 status
= get_sorted_dc_list(realm
, sitename
, &ip_list
, &count
, got_realm
);
420 if (!NT_STATUS_IS_OK(status
)) {
421 /* fall back to netbios if we can */
422 if ( got_realm
&& !lp_disable_netbios() ) {
431 /* if we fail this loop, then giveup since all the IP addresses returned were dead */
432 for ( i
=0; i
<count
; i
++ ) {
433 char server
[INET6_ADDRSTRLEN
];
435 print_sockaddr(server
, sizeof(server
), &ip_list
[i
].ss
);
437 if ( !NT_STATUS_IS_OK(check_negative_conn_cache(realm
, server
)) )
441 /* realm in this case is a workgroup name. We need
442 to ignore any IP addresses in the negative connection
443 cache that match ip addresses returned in the ad realm
444 case. It sucks that I have to reproduce the logic above... */
445 c_realm
= ads
->server
.realm
;
446 if ( !c_realm
|| !*c_realm
) {
447 if ( !ads
->server
.workgroup
|| !*ads
->server
.workgroup
) {
448 c_realm
= lp_realm();
451 if (c_realm
&& *c_realm
&&
452 !NT_STATUS_IS_OK(check_negative_conn_cache(c_realm
, server
))) {
453 /* Ensure we add the workgroup name for this
454 IP address as negative too. */
455 add_failed_connection_entry( realm
, server
, NT_STATUS_UNSUCCESSFUL
);
460 if ( ads_try_connect(ads
, server
, false) ) {
466 /* keep track of failures */
467 add_failed_connection_entry( realm
, server
, NT_STATUS_UNSUCCESSFUL
);
472 /* In case we failed to contact one of our closest DC on our site we
473 * need to try to find another DC, retry with a site-less SRV DNS query
477 DEBUG(1,("ads_find_dc: failed to find a valid DC on our site (%s), "
478 "trying to find another DC\n", sitename
));
480 namecache_delete(realm
, 0x1C);
484 return NT_STATUS_NO_LOGON_SERVERS
;
487 /*********************************************************************
488 *********************************************************************/
490 static NTSTATUS
ads_lookup_site(void)
492 ADS_STRUCT
*ads
= NULL
;
493 ADS_STATUS ads_status
;
494 NTSTATUS nt_status
= NT_STATUS_UNSUCCESSFUL
;
496 ads
= ads_init(lp_realm(), NULL
, NULL
);
498 return NT_STATUS_NO_MEMORY
;
501 /* The NO_BIND here will find a DC and set the client site
502 but not establish the TCP connection */
504 ads
->auth
.flags
= ADS_AUTH_NO_BIND
;
505 ads_status
= ads_connect(ads
);
506 if (!ADS_ERR_OK(ads_status
)) {
507 DEBUG(4, ("ads_lookup_site: ads_connect to our realm failed! (%s)\n",
508 ads_errstr(ads_status
)));
510 nt_status
= ads_ntstatus(ads_status
);
519 /*********************************************************************
520 *********************************************************************/
522 static const char* host_dns_domain(const char *fqdn
)
524 const char *p
= fqdn
;
526 /* go to next char following '.' */
528 if ((p
= strchr_m(fqdn
, '.')) != NULL
) {
537 * Connect to the Global Catalog server
538 * @param ads Pointer to an existing ADS_STRUCT
539 * @return status of connection
541 * Simple wrapper around ads_connect() that fills in the
542 * GC ldap server information
545 ADS_STATUS
ads_connect_gc(ADS_STRUCT
*ads
)
547 TALLOC_CTX
*frame
= talloc_stackframe();
548 struct dns_rr_srv
*gcs_list
;
550 const char *realm
= ads
->server
.realm
;
551 NTSTATUS nt_status
= NT_STATUS_UNSUCCESSFUL
;
552 ADS_STATUS ads_status
= ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL
);
555 char *sitename
= NULL
;
556 const char *dns_hosts_file
;
561 if ((sitename
= sitename_fetch(realm
)) == NULL
) {
563 sitename
= sitename_fetch(realm
);
566 dns_hosts_file
= lp_parm_const_string(-1, "resolv", "host file", NULL
);
568 /* We try once with a sitename and once without
569 (unless we don't have a sitename and then we're
572 if (sitename
== NULL
)
575 nt_status
= ads_dns_query_gcs(frame
, dns_hosts_file
,
577 &gcs_list
, &num_gcs
);
581 if (!NT_STATUS_IS_OK(nt_status
)) {
582 ads_status
= ADS_ERROR_NT(nt_status
);
586 /* Loop until we get a successful connection or have gone
587 through them all. When connecting a GC server, make sure that
588 the realm is the server's DNS name and not the forest root */
590 for (i
=0; i
<num_gcs
; i
++) {
591 ads
->server
.gc
= true;
592 ads
->server
.ldap_server
= SMB_STRDUP(gcs_list
[i
].hostname
);
593 ads
->server
.realm
= SMB_STRDUP(host_dns_domain(ads
->server
.ldap_server
));
594 ads_status
= ads_connect(ads
);
595 if (ADS_ERR_OK(ads_status
)) {
596 /* Reset the bind_dn to "". A Global Catalog server
597 may host multiple domain trees in a forest.
598 Windows 2003 GC server will accept "" as the search
599 path to imply search all domain trees in the forest */
601 SAFE_FREE(ads
->config
.bind_path
);
602 ads
->config
.bind_path
= SMB_STRDUP("");
607 SAFE_FREE(ads
->server
.ldap_server
);
608 SAFE_FREE(ads
->server
.realm
);
611 TALLOC_FREE(gcs_list
);
617 talloc_destroy(frame
);
624 * Connect to the LDAP server
625 * @param ads Pointer to an existing ADS_STRUCT
626 * @return status of connection
628 ADS_STATUS
ads_connect(ADS_STRUCT
*ads
)
630 int version
= LDAP_VERSION3
;
633 char addr
[INET6_ADDRSTRLEN
];
635 ZERO_STRUCT(ads
->ldap
);
636 ads
->ldap
.last_attempt
= time_mono(NULL
);
637 ads
->ldap
.wrap_type
= ADS_SASLWRAP_TYPE_PLAIN
;
639 /* try with a user specified server */
641 if (DEBUGLEVEL
>= 11) {
642 char *s
= NDR_PRINT_STRUCT_STRING(talloc_tos(), ads_struct
, ads
);
643 DEBUG(11,("ads_connect: entering\n"));
644 DEBUGADD(11,("%s\n", s
));
648 if (ads
->server
.ldap_server
)
650 if (ads_try_connect(ads
, ads
->server
.ldap_server
, ads
->server
.gc
)) {
654 /* The choice of which GC use is handled one level up in
655 ads_connect_gc(). If we continue on from here with
656 ads_find_dc() we will get GC searches on port 389 which
657 doesn't work. --jerry */
659 if (ads
->server
.gc
== true) {
660 return ADS_ERROR(LDAP_OPERATIONS_ERROR
);
664 ntstatus
= ads_find_dc(ads
);
665 if (NT_STATUS_IS_OK(ntstatus
)) {
669 status
= ADS_ERROR_NT(ntstatus
);
674 print_sockaddr(addr
, sizeof(addr
), &ads
->ldap
.ss
);
675 DEBUG(3,("Successfully contacted LDAP server %s\n", addr
));
677 if (!ads
->auth
.user_name
) {
678 /* Must use the userPrincipalName value here or sAMAccountName
679 and not servicePrincipalName; found by Guenther Deschner */
681 if (asprintf(&ads
->auth
.user_name
, "%s$", lp_netbios_name() ) == -1) {
682 DEBUG(0,("ads_connect: asprintf fail.\n"));
683 ads
->auth
.user_name
= NULL
;
687 if (!ads
->auth
.realm
) {
688 ads
->auth
.realm
= SMB_STRDUP(ads
->config
.realm
);
691 if (!ads
->auth
.kdc_server
) {
692 print_sockaddr(addr
, sizeof(addr
), &ads
->ldap
.ss
);
693 ads
->auth
.kdc_server
= SMB_STRDUP(addr
);
696 /* If the caller() requested no LDAP bind, then we are done */
698 if (ads
->auth
.flags
& ADS_AUTH_NO_BIND
) {
699 status
= ADS_SUCCESS
;
703 ads
->ldap
.mem_ctx
= talloc_init("ads LDAP connection memory");
704 if (!ads
->ldap
.mem_ctx
) {
705 status
= ADS_ERROR_NT(NT_STATUS_NO_MEMORY
);
709 /* Otherwise setup the TCP LDAP session */
711 ads
->ldap
.ld
= ldap_open_with_timeout(ads
->config
.ldap_server_name
,
713 ads
->ldap
.port
, lp_ldap_timeout());
714 if (ads
->ldap
.ld
== NULL
) {
715 status
= ADS_ERROR(LDAP_OPERATIONS_ERROR
);
718 DEBUG(3,("Connected to LDAP server %s\n", ads
->config
.ldap_server_name
));
720 /* cache the successful connection for workgroup and realm */
721 if (ads_closest_dc(ads
)) {
722 saf_store( ads
->server
.workgroup
, ads
->config
.ldap_server_name
);
723 saf_store( ads
->server
.realm
, ads
->config
.ldap_server_name
);
726 ldap_set_option(ads
->ldap
.ld
, LDAP_OPT_PROTOCOL_VERSION
, &version
);
728 if ( lp_ldap_ssl_ads() ) {
729 status
= ADS_ERROR(smb_ldap_start_tls(ads
->ldap
.ld
, version
));
730 if (!ADS_ERR_OK(status
)) {
735 /* fill in the current time and offsets */
737 status
= ads_current_time( ads
);
738 if ( !ADS_ERR_OK(status
) ) {
742 /* Now do the bind */
744 if (ads
->auth
.flags
& ADS_AUTH_ANON_BIND
) {
745 status
= ADS_ERROR(ldap_simple_bind_s(ads
->ldap
.ld
, NULL
, NULL
));
749 if (ads
->auth
.flags
& ADS_AUTH_SIMPLE_BIND
) {
750 status
= ADS_ERROR(ldap_simple_bind_s(ads
->ldap
.ld
, ads
->auth
.user_name
, ads
->auth
.password
));
754 status
= ads_sasl_bind(ads
);
757 if (DEBUGLEVEL
>= 11) {
758 char *s
= NDR_PRINT_STRUCT_STRING(talloc_tos(), ads_struct
, ads
);
759 DEBUG(11,("ads_connect: leaving with: %s\n",
760 ads_errstr(status
)));
761 DEBUGADD(11,("%s\n", s
));
769 * Connect to the LDAP server using given credentials
770 * @param ads Pointer to an existing ADS_STRUCT
771 * @return status of connection
773 ADS_STATUS
ads_connect_user_creds(ADS_STRUCT
*ads
)
775 ads
->auth
.flags
|= ADS_AUTH_USER_CREDS
;
777 return ads_connect(ads
);
781 * Disconnect the LDAP server
782 * @param ads Pointer to an existing ADS_STRUCT
784 void ads_disconnect(ADS_STRUCT
*ads
)
787 ldap_unbind(ads
->ldap
.ld
);
790 if (ads
->ldap
.wrap_ops
&& ads
->ldap
.wrap_ops
->disconnect
) {
791 ads
->ldap
.wrap_ops
->disconnect(ads
);
793 if (ads
->ldap
.mem_ctx
) {
794 talloc_free(ads
->ldap
.mem_ctx
);
796 ZERO_STRUCT(ads
->ldap
);
800 Duplicate a struct berval into talloc'ed memory
802 static struct berval
*dup_berval(TALLOC_CTX
*ctx
, const struct berval
*in_val
)
804 struct berval
*value
;
806 if (!in_val
) return NULL
;
808 value
= talloc_zero(ctx
, struct berval
);
811 if (in_val
->bv_len
== 0) return value
;
813 value
->bv_len
= in_val
->bv_len
;
814 value
->bv_val
= (char *)talloc_memdup(ctx
, in_val
->bv_val
,
820 Make a values list out of an array of (struct berval *)
822 static struct berval
**ads_dup_values(TALLOC_CTX
*ctx
,
823 const struct berval
**in_vals
)
825 struct berval
**values
;
828 if (!in_vals
) return NULL
;
829 for (i
=0; in_vals
[i
]; i
++)
831 values
= talloc_zero_array(ctx
, struct berval
*, i
+1);
832 if (!values
) return NULL
;
834 for (i
=0; in_vals
[i
]; i
++) {
835 values
[i
] = dup_berval(ctx
, in_vals
[i
]);
841 UTF8-encode a values list out of an array of (char *)
843 static char **ads_push_strvals(TALLOC_CTX
*ctx
, const char **in_vals
)
849 if (!in_vals
) return NULL
;
850 for (i
=0; in_vals
[i
]; i
++)
852 values
= talloc_zero_array(ctx
, char *, i
+1);
853 if (!values
) return NULL
;
855 for (i
=0; in_vals
[i
]; i
++) {
856 if (!push_utf8_talloc(ctx
, &values
[i
], in_vals
[i
], &size
)) {
865 Pull a (char *) array out of a UTF8-encoded values list
867 static char **ads_pull_strvals(TALLOC_CTX
*ctx
, const char **in_vals
)
871 size_t converted_size
;
873 if (!in_vals
) return NULL
;
874 for (i
=0; in_vals
[i
]; i
++)
876 values
= talloc_zero_array(ctx
, char *, i
+1);
877 if (!values
) return NULL
;
879 for (i
=0; in_vals
[i
]; i
++) {
880 if (!pull_utf8_talloc(ctx
, &values
[i
], in_vals
[i
],
882 DEBUG(0,("ads_pull_strvals: pull_utf8_talloc failed: "
883 "%s", strerror(errno
)));
890 * Do a search with paged results. cookie must be null on the first
891 * call, and then returned on each subsequent call. It will be null
892 * again when the entire search is complete
893 * @param ads connection to ads server
894 * @param bind_path Base dn for the search
895 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
896 * @param expr Search expression - specified in local charset
897 * @param attrs Attributes to retrieve - specified in utf8 or ascii
898 * @param res ** which will contain results - free res* with ads_msgfree()
899 * @param count Number of entries retrieved on this page
900 * @param cookie The paged results cookie to be returned on subsequent calls
901 * @return status of search
903 static ADS_STATUS
ads_do_paged_search_args(ADS_STRUCT
*ads
,
904 const char *bind_path
,
905 int scope
, const char *expr
,
906 const char **attrs
, void *args
,
908 int *count
, struct berval
**cookie
)
911 char *utf8_expr
, *utf8_path
, **search_attrs
= NULL
;
912 size_t converted_size
;
913 LDAPControl PagedResults
, NoReferrals
, ExternalCtrl
, *controls
[4], **rcontrols
;
914 BerElement
*cookie_be
= NULL
;
915 struct berval
*cookie_bv
= NULL
;
916 BerElement
*ext_be
= NULL
;
917 struct berval
*ext_bv
= NULL
;
920 ads_control
*external_control
= (ads_control
*) args
;
924 if (!(ctx
= talloc_init("ads_do_paged_search_args")))
925 return ADS_ERROR(LDAP_NO_MEMORY
);
927 /* 0 means the conversion worked but the result was empty
928 so we only fail if it's -1. In any case, it always
929 at least nulls out the dest */
930 if (!push_utf8_talloc(ctx
, &utf8_expr
, expr
, &converted_size
) ||
931 !push_utf8_talloc(ctx
, &utf8_path
, bind_path
, &converted_size
))
937 if (!attrs
|| !(*attrs
))
940 /* This would be the utf8-encoded version...*/
941 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
942 if (!(search_attrs
= str_list_copy(talloc_tos(), attrs
))) {
948 /* Paged results only available on ldap v3 or later */
949 ldap_get_option(ads
->ldap
.ld
, LDAP_OPT_PROTOCOL_VERSION
, &version
);
950 if (version
< LDAP_VERSION3
) {
951 rc
= LDAP_NOT_SUPPORTED
;
955 cookie_be
= ber_alloc_t(LBER_USE_DER
);
957 ber_printf(cookie_be
, "{iO}", (ber_int_t
) 1000, *cookie
);
958 ber_bvfree(*cookie
); /* don't need it from last time */
961 ber_printf(cookie_be
, "{io}", (ber_int_t
) 1000, "", 0);
963 ber_flatten(cookie_be
, &cookie_bv
);
964 PagedResults
.ldctl_oid
= discard_const_p(char, ADS_PAGE_CTL_OID
);
965 PagedResults
.ldctl_iscritical
= (char) 1;
966 PagedResults
.ldctl_value
.bv_len
= cookie_bv
->bv_len
;
967 PagedResults
.ldctl_value
.bv_val
= cookie_bv
->bv_val
;
969 NoReferrals
.ldctl_oid
= discard_const_p(char, ADS_NO_REFERRALS_OID
);
970 NoReferrals
.ldctl_iscritical
= (char) 0;
971 NoReferrals
.ldctl_value
.bv_len
= 0;
972 NoReferrals
.ldctl_value
.bv_val
= discard_const_p(char, "");
974 if (external_control
&&
975 (strequal(external_control
->control
, ADS_EXTENDED_DN_OID
) ||
976 strequal(external_control
->control
, ADS_SD_FLAGS_OID
))) {
978 ExternalCtrl
.ldctl_oid
= discard_const_p(char, external_control
->control
);
979 ExternalCtrl
.ldctl_iscritical
= (char) external_control
->critical
;
981 /* win2k does not accept a ldctl_value beeing passed in */
983 if (external_control
->val
!= 0) {
985 if ((ext_be
= ber_alloc_t(LBER_USE_DER
)) == NULL
) {
990 if ((ber_printf(ext_be
, "{i}", (ber_int_t
) external_control
->val
)) == -1) {
994 if ((ber_flatten(ext_be
, &ext_bv
)) == -1) {
999 ExternalCtrl
.ldctl_value
.bv_len
= ext_bv
->bv_len
;
1000 ExternalCtrl
.ldctl_value
.bv_val
= ext_bv
->bv_val
;
1003 ExternalCtrl
.ldctl_value
.bv_len
= 0;
1004 ExternalCtrl
.ldctl_value
.bv_val
= NULL
;
1007 controls
[0] = &NoReferrals
;
1008 controls
[1] = &PagedResults
;
1009 controls
[2] = &ExternalCtrl
;
1013 controls
[0] = &NoReferrals
;
1014 controls
[1] = &PagedResults
;
1018 /* we need to disable referrals as the openldap libs don't
1019 handle them and paged results at the same time. Using them
1020 together results in the result record containing the server
1021 page control being removed from the result list (tridge/jmcd)
1023 leaving this in despite the control that says don't generate
1024 referrals, in case the server doesn't support it (jmcd)
1026 ldap_set_option(ads
->ldap
.ld
, LDAP_OPT_REFERRALS
, LDAP_OPT_OFF
);
1028 rc
= ldap_search_with_timeout(ads
->ldap
.ld
, utf8_path
, scope
, utf8_expr
,
1029 search_attrs
, 0, controls
,
1030 NULL
, LDAP_NO_LIMIT
,
1031 (LDAPMessage
**)res
);
1033 ber_free(cookie_be
, 1);
1034 ber_bvfree(cookie_bv
);
1037 DEBUG(3,("ads_do_paged_search_args: ldap_search_with_timeout(%s) -> %s\n", expr
,
1038 ldap_err2string(rc
)));
1042 rc
= ldap_parse_result(ads
->ldap
.ld
, *res
, NULL
, NULL
, NULL
,
1043 NULL
, &rcontrols
, 0);
1049 for (i
=0; rcontrols
[i
]; i
++) {
1050 if (strcmp(ADS_PAGE_CTL_OID
, rcontrols
[i
]->ldctl_oid
) == 0) {
1051 cookie_be
= ber_init(&rcontrols
[i
]->ldctl_value
);
1052 ber_scanf(cookie_be
,"{iO}", (ber_int_t
*) count
,
1054 /* the berval is the cookie, but must be freed when
1056 if (cookie_bv
->bv_len
) /* still more to do */
1057 *cookie
=ber_bvdup(cookie_bv
);
1060 ber_bvfree(cookie_bv
);
1061 ber_free(cookie_be
, 1);
1065 ldap_controls_free(rcontrols
);
1068 talloc_destroy(ctx
);
1071 ber_free(ext_be
, 1);
1078 /* if/when we decide to utf8-encode attrs, take out this next line */
1079 TALLOC_FREE(search_attrs
);
1081 return ADS_ERROR(rc
);
1084 static ADS_STATUS
ads_do_paged_search(ADS_STRUCT
*ads
, const char *bind_path
,
1085 int scope
, const char *expr
,
1086 const char **attrs
, LDAPMessage
**res
,
1087 int *count
, struct berval
**cookie
)
1089 return ads_do_paged_search_args(ads
, bind_path
, scope
, expr
, attrs
, NULL
, res
, count
, cookie
);
1094 * Get all results for a search. This uses ads_do_paged_search() to return
1095 * all entries in a large search.
1096 * @param ads connection to ads server
1097 * @param bind_path Base dn for the search
1098 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1099 * @param expr Search expression
1100 * @param attrs Attributes to retrieve
1101 * @param res ** which will contain results - free res* with ads_msgfree()
1102 * @return status of search
1104 ADS_STATUS
ads_do_search_all_args(ADS_STRUCT
*ads
, const char *bind_path
,
1105 int scope
, const char *expr
,
1106 const char **attrs
, void *args
,
1109 struct berval
*cookie
= NULL
;
1114 status
= ads_do_paged_search_args(ads
, bind_path
, scope
, expr
, attrs
, args
, res
,
1117 if (!ADS_ERR_OK(status
))
1120 #ifdef HAVE_LDAP_ADD_RESULT_ENTRY
1122 LDAPMessage
*res2
= NULL
;
1124 LDAPMessage
*msg
, *next
;
1126 status2
= ads_do_paged_search_args(ads
, bind_path
, scope
, expr
,
1127 attrs
, args
, &res2
, &count
, &cookie
);
1129 if (!ADS_ERR_OK(status2
)) break;
1131 /* this relies on the way that ldap_add_result_entry() works internally. I hope
1132 that this works on all ldap libs, but I have only tested with openldap */
1133 for (msg
= ads_first_message(ads
, res2
); msg
; msg
= next
) {
1134 next
= ads_next_message(ads
, msg
);
1135 ldap_add_result_entry((LDAPMessage
**)res
, msg
);
1137 /* note that we do not free res2, as the memory is now
1138 part of the main returned list */
1141 DEBUG(0, ("no ldap_add_result_entry() support in LDAP libs!\n"));
1142 status
= ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL
);
1148 ADS_STATUS
ads_do_search_all(ADS_STRUCT
*ads
, const char *bind_path
,
1149 int scope
, const char *expr
,
1150 const char **attrs
, LDAPMessage
**res
)
1152 return ads_do_search_all_args(ads
, bind_path
, scope
, expr
, attrs
, NULL
, res
);
1155 ADS_STATUS
ads_do_search_all_sd_flags(ADS_STRUCT
*ads
, const char *bind_path
,
1156 int scope
, const char *expr
,
1157 const char **attrs
, uint32 sd_flags
,
1162 args
.control
= ADS_SD_FLAGS_OID
;
1163 args
.val
= sd_flags
;
1164 args
.critical
= True
;
1166 return ads_do_search_all_args(ads
, bind_path
, scope
, expr
, attrs
, &args
, res
);
1171 * Run a function on all results for a search. Uses ads_do_paged_search() and
1172 * runs the function as each page is returned, using ads_process_results()
1173 * @param ads connection to ads server
1174 * @param bind_path Base dn for the search
1175 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1176 * @param expr Search expression - specified in local charset
1177 * @param attrs Attributes to retrieve - specified in UTF-8 or ascii
1178 * @param fn Function which takes attr name, values list, and data_area
1179 * @param data_area Pointer which is passed to function on each call
1180 * @return status of search
1182 ADS_STATUS
ads_do_search_all_fn(ADS_STRUCT
*ads
, const char *bind_path
,
1183 int scope
, const char *expr
, const char **attrs
,
1184 bool (*fn
)(ADS_STRUCT
*, char *, void **, void *),
1187 struct berval
*cookie
= NULL
;
1192 status
= ads_do_paged_search(ads
, bind_path
, scope
, expr
, attrs
, &res
,
1195 if (!ADS_ERR_OK(status
)) return status
;
1197 ads_process_results(ads
, res
, fn
, data_area
);
1198 ads_msgfree(ads
, res
);
1201 status
= ads_do_paged_search(ads
, bind_path
, scope
, expr
, attrs
,
1202 &res
, &count
, &cookie
);
1204 if (!ADS_ERR_OK(status
)) break;
1206 ads_process_results(ads
, res
, fn
, data_area
);
1207 ads_msgfree(ads
, res
);
1214 * Do a search with a timeout.
1215 * @param ads connection to ads server
1216 * @param bind_path Base dn for the search
1217 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1218 * @param expr Search expression
1219 * @param attrs Attributes to retrieve
1220 * @param res ** which will contain results - free res* with ads_msgfree()
1221 * @return status of search
1223 ADS_STATUS
ads_do_search(ADS_STRUCT
*ads
, const char *bind_path
, int scope
,
1225 const char **attrs
, LDAPMessage
**res
)
1228 char *utf8_expr
, *utf8_path
, **search_attrs
= NULL
;
1229 size_t converted_size
;
1233 if (!(ctx
= talloc_init("ads_do_search"))) {
1234 DEBUG(1,("ads_do_search: talloc_init() failed!"));
1235 return ADS_ERROR(LDAP_NO_MEMORY
);
1238 /* 0 means the conversion worked but the result was empty
1239 so we only fail if it's negative. In any case, it always
1240 at least nulls out the dest */
1241 if (!push_utf8_talloc(ctx
, &utf8_expr
, expr
, &converted_size
) ||
1242 !push_utf8_talloc(ctx
, &utf8_path
, bind_path
, &converted_size
))
1244 DEBUG(1,("ads_do_search: push_utf8_talloc() failed!"));
1245 rc
= LDAP_NO_MEMORY
;
1249 if (!attrs
|| !(*attrs
))
1250 search_attrs
= NULL
;
1252 /* This would be the utf8-encoded version...*/
1253 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
1254 if (!(search_attrs
= str_list_copy(talloc_tos(), attrs
)))
1256 DEBUG(1,("ads_do_search: str_list_copy() failed!"));
1257 rc
= LDAP_NO_MEMORY
;
1262 /* see the note in ads_do_paged_search - we *must* disable referrals */
1263 ldap_set_option(ads
->ldap
.ld
, LDAP_OPT_REFERRALS
, LDAP_OPT_OFF
);
1265 rc
= ldap_search_with_timeout(ads
->ldap
.ld
, utf8_path
, scope
, utf8_expr
,
1266 search_attrs
, 0, NULL
, NULL
,
1268 (LDAPMessage
**)res
);
1270 if (rc
== LDAP_SIZELIMIT_EXCEEDED
) {
1271 DEBUG(3,("Warning! sizelimit exceeded in ldap. Truncating.\n"));
1276 talloc_destroy(ctx
);
1277 /* if/when we decide to utf8-encode attrs, take out this next line */
1278 TALLOC_FREE(search_attrs
);
1279 return ADS_ERROR(rc
);
1282 * Do a general ADS search
1283 * @param ads connection to ads server
1284 * @param res ** which will contain results - free res* with ads_msgfree()
1285 * @param expr Search expression
1286 * @param attrs Attributes to retrieve
1287 * @return status of search
1289 ADS_STATUS
ads_search(ADS_STRUCT
*ads
, LDAPMessage
**res
,
1290 const char *expr
, const char **attrs
)
1292 return ads_do_search(ads
, ads
->config
.bind_path
, LDAP_SCOPE_SUBTREE
,
1297 * Do a search on a specific DistinguishedName
1298 * @param ads connection to ads server
1299 * @param res ** which will contain results - free res* with ads_msgfree()
1300 * @param dn DistinguishName to search
1301 * @param attrs Attributes to retrieve
1302 * @return status of search
1304 ADS_STATUS
ads_search_dn(ADS_STRUCT
*ads
, LDAPMessage
**res
,
1305 const char *dn
, const char **attrs
)
1307 return ads_do_search(ads
, dn
, LDAP_SCOPE_BASE
, "(objectclass=*)",
1312 * Free up memory from a ads_search
1313 * @param ads connection to ads server
1314 * @param msg Search results to free
1316 void ads_msgfree(ADS_STRUCT
*ads
, LDAPMessage
*msg
)
1323 * Get a dn from search results
1324 * @param ads connection to ads server
1325 * @param msg Search result
1328 char *ads_get_dn(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
, LDAPMessage
*msg
)
1330 char *utf8_dn
, *unix_dn
;
1331 size_t converted_size
;
1333 utf8_dn
= ldap_get_dn(ads
->ldap
.ld
, msg
);
1336 DEBUG (5, ("ads_get_dn: ldap_get_dn failed\n"));
1340 if (!pull_utf8_talloc(mem_ctx
, &unix_dn
, utf8_dn
, &converted_size
)) {
1341 DEBUG(0,("ads_get_dn: string conversion failure utf8 [%s]\n",
1345 ldap_memfree(utf8_dn
);
1350 * Get the parent from a dn
1351 * @param dn the dn to return the parent from
1352 * @return parent dn string
1354 char *ads_parent_dn(const char *dn
)
1362 p
= strchr(dn
, ',');
1372 * Find a machine account given a hostname
1373 * @param ads connection to ads server
1374 * @param res ** which will contain results - free res* with ads_msgfree()
1375 * @param host Hostname to search for
1376 * @return status of search
1378 ADS_STATUS
ads_find_machine_acct(ADS_STRUCT
*ads
, LDAPMessage
**res
,
1379 const char *machine
)
1383 const char *attrs
[] = {"*", "nTSecurityDescriptor", NULL
};
1387 /* the easiest way to find a machine account anywhere in the tree
1388 is to look for hostname$ */
1389 if (asprintf(&expr
, "(samAccountName=%s$)", machine
) == -1) {
1390 DEBUG(1, ("asprintf failed!\n"));
1391 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY
);
1394 status
= ads_search(ads
, res
, expr
, attrs
);
1400 * Initialize a list of mods to be used in a modify request
1401 * @param ctx An initialized TALLOC_CTX
1402 * @return allocated ADS_MODLIST
1404 ADS_MODLIST
ads_init_mods(TALLOC_CTX
*ctx
)
1406 #define ADS_MODLIST_ALLOC_SIZE 10
1409 if ((mods
= talloc_zero_array(ctx
, LDAPMod
*, ADS_MODLIST_ALLOC_SIZE
+ 1)))
1410 /* -1 is safety to make sure we don't go over the end.
1411 need to reset it to NULL before doing ldap modify */
1412 mods
[ADS_MODLIST_ALLOC_SIZE
] = (LDAPMod
*) -1;
1414 return (ADS_MODLIST
)mods
;
1419 add an attribute to the list, with values list already constructed
1421 static ADS_STATUS
ads_modlist_add(TALLOC_CTX
*ctx
, ADS_MODLIST
*mods
,
1422 int mod_op
, const char *name
,
1423 const void *_invals
)
1425 const void **invals
= (const void **)_invals
;
1427 LDAPMod
**modlist
= (LDAPMod
**) *mods
;
1428 struct berval
**ber_values
= NULL
;
1429 char **char_values
= NULL
;
1432 mod_op
= LDAP_MOD_DELETE
;
1434 if (mod_op
& LDAP_MOD_BVALUES
)
1435 ber_values
= ads_dup_values(ctx
,
1436 (const struct berval
**)invals
);
1438 char_values
= ads_push_strvals(ctx
,
1439 (const char **) invals
);
1442 /* find the first empty slot */
1443 for (curmod
=0; modlist
[curmod
] && modlist
[curmod
] != (LDAPMod
*) -1;
1445 if (modlist
[curmod
] == (LDAPMod
*) -1) {
1446 if (!(modlist
= talloc_realloc(ctx
, modlist
, LDAPMod
*,
1447 curmod
+ADS_MODLIST_ALLOC_SIZE
+1)))
1448 return ADS_ERROR(LDAP_NO_MEMORY
);
1449 memset(&modlist
[curmod
], 0,
1450 ADS_MODLIST_ALLOC_SIZE
*sizeof(LDAPMod
*));
1451 modlist
[curmod
+ADS_MODLIST_ALLOC_SIZE
] = (LDAPMod
*) -1;
1452 *mods
= (ADS_MODLIST
)modlist
;
1455 if (!(modlist
[curmod
] = talloc_zero(ctx
, LDAPMod
)))
1456 return ADS_ERROR(LDAP_NO_MEMORY
);
1457 modlist
[curmod
]->mod_type
= talloc_strdup(ctx
, name
);
1458 if (mod_op
& LDAP_MOD_BVALUES
) {
1459 modlist
[curmod
]->mod_bvalues
= ber_values
;
1460 } else if (mod_op
& LDAP_MOD_DELETE
) {
1461 modlist
[curmod
]->mod_values
= NULL
;
1463 modlist
[curmod
]->mod_values
= char_values
;
1466 modlist
[curmod
]->mod_op
= mod_op
;
1467 return ADS_ERROR(LDAP_SUCCESS
);
1471 * Add a single string value to a mod list
1472 * @param ctx An initialized TALLOC_CTX
1473 * @param mods An initialized ADS_MODLIST
1474 * @param name The attribute name to add
1475 * @param val The value to add - NULL means DELETE
1476 * @return ADS STATUS indicating success of add
1478 ADS_STATUS
ads_mod_str(TALLOC_CTX
*ctx
, ADS_MODLIST
*mods
,
1479 const char *name
, const char *val
)
1481 const char *values
[2];
1487 return ads_modlist_add(ctx
, mods
, LDAP_MOD_DELETE
, name
, NULL
);
1488 return ads_modlist_add(ctx
, mods
, LDAP_MOD_REPLACE
, name
, values
);
1492 * Add an array of string values to a mod list
1493 * @param ctx An initialized TALLOC_CTX
1494 * @param mods An initialized ADS_MODLIST
1495 * @param name The attribute name to add
1496 * @param vals The array of string values to add - NULL means DELETE
1497 * @return ADS STATUS indicating success of add
1499 ADS_STATUS
ads_mod_strlist(TALLOC_CTX
*ctx
, ADS_MODLIST
*mods
,
1500 const char *name
, const char **vals
)
1503 return ads_modlist_add(ctx
, mods
, LDAP_MOD_DELETE
, name
, NULL
);
1504 return ads_modlist_add(ctx
, mods
, LDAP_MOD_REPLACE
,
1505 name
, (const void **) vals
);
1510 * Add a single ber-encoded value to a mod list
1511 * @param ctx An initialized TALLOC_CTX
1512 * @param mods An initialized ADS_MODLIST
1513 * @param name The attribute name to add
1514 * @param val The value to add - NULL means DELETE
1515 * @return ADS STATUS indicating success of add
1517 static ADS_STATUS
ads_mod_ber(TALLOC_CTX
*ctx
, ADS_MODLIST
*mods
,
1518 const char *name
, const struct berval
*val
)
1520 const struct berval
*values
[2];
1525 return ads_modlist_add(ctx
, mods
, LDAP_MOD_DELETE
, name
, NULL
);
1526 return ads_modlist_add(ctx
, mods
, LDAP_MOD_REPLACE
|LDAP_MOD_BVALUES
,
1527 name
, (const void **) values
);
1532 * Perform an ldap modify
1533 * @param ads connection to ads server
1534 * @param mod_dn DistinguishedName to modify
1535 * @param mods list of modifications to perform
1536 * @return status of modify
1538 ADS_STATUS
ads_gen_mod(ADS_STRUCT
*ads
, const char *mod_dn
, ADS_MODLIST mods
)
1541 char *utf8_dn
= NULL
;
1542 size_t converted_size
;
1544 this control is needed to modify that contains a currently
1545 non-existent attribute (but allowable for the object) to run
1547 LDAPControl PermitModify
= {
1548 discard_const_p(char, ADS_PERMIT_MODIFY_OID
),
1551 LDAPControl
*controls
[2];
1553 controls
[0] = &PermitModify
;
1556 if (!push_utf8_talloc(talloc_tos(), &utf8_dn
, mod_dn
, &converted_size
)) {
1557 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY
);
1560 /* find the end of the list, marked by NULL or -1 */
1561 for(i
=0;(mods
[i
]!=0)&&(mods
[i
]!=(LDAPMod
*) -1);i
++);
1562 /* make sure the end of the list is NULL */
1564 ret
= ldap_modify_ext_s(ads
->ldap
.ld
, utf8_dn
,
1565 (LDAPMod
**) mods
, controls
, NULL
);
1566 TALLOC_FREE(utf8_dn
);
1567 return ADS_ERROR(ret
);
1571 * Perform an ldap add
1572 * @param ads connection to ads server
1573 * @param new_dn DistinguishedName to add
1574 * @param mods list of attributes and values for DN
1575 * @return status of add
1577 ADS_STATUS
ads_gen_add(ADS_STRUCT
*ads
, const char *new_dn
, ADS_MODLIST mods
)
1580 char *utf8_dn
= NULL
;
1581 size_t converted_size
;
1583 if (!push_utf8_talloc(talloc_tos(), &utf8_dn
, new_dn
, &converted_size
)) {
1584 DEBUG(1, ("ads_gen_add: push_utf8_talloc failed!"));
1585 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY
);
1588 /* find the end of the list, marked by NULL or -1 */
1589 for(i
=0;(mods
[i
]!=0)&&(mods
[i
]!=(LDAPMod
*) -1);i
++);
1590 /* make sure the end of the list is NULL */
1593 ret
= ldap_add_s(ads
->ldap
.ld
, utf8_dn
, (LDAPMod
**)mods
);
1594 TALLOC_FREE(utf8_dn
);
1595 return ADS_ERROR(ret
);
1599 * Delete a DistinguishedName
1600 * @param ads connection to ads server
1601 * @param new_dn DistinguishedName to delete
1602 * @return status of delete
1604 ADS_STATUS
ads_del_dn(ADS_STRUCT
*ads
, char *del_dn
)
1607 char *utf8_dn
= NULL
;
1608 size_t converted_size
;
1609 if (!push_utf8_talloc(talloc_tos(), &utf8_dn
, del_dn
, &converted_size
)) {
1610 DEBUG(1, ("ads_del_dn: push_utf8_talloc failed!"));
1611 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY
);
1614 ret
= ldap_delete_s(ads
->ldap
.ld
, utf8_dn
);
1615 TALLOC_FREE(utf8_dn
);
1616 return ADS_ERROR(ret
);
1620 * Build an org unit string
1621 * if org unit is Computers or blank then assume a container, otherwise
1622 * assume a / separated list of organisational units.
1623 * jmcd: '\' is now used for escapes so certain chars can be in the ou (e.g. #)
1624 * @param ads connection to ads server
1625 * @param org_unit Organizational unit
1626 * @return org unit string - caller must free
1628 char *ads_ou_string(ADS_STRUCT
*ads
, const char *org_unit
)
1632 if (!org_unit
|| !*org_unit
) {
1634 ret
= ads_default_ou_string(ads
, DS_GUID_COMPUTERS_CONTAINER
);
1636 /* samba4 might not yet respond to a wellknownobject-query */
1637 return ret
? ret
: SMB_STRDUP("cn=Computers");
1640 if (strequal(org_unit
, "Computers")) {
1641 return SMB_STRDUP("cn=Computers");
1644 /* jmcd: removed "\\" from the separation chars, because it is
1645 needed as an escape for chars like '#' which are valid in an
1647 return ads_build_path(org_unit
, "/", "ou=", 1);
1651 * Get a org unit string for a well-known GUID
1652 * @param ads connection to ads server
1653 * @param wknguid Well known GUID
1654 * @return org unit string - caller must free
1656 char *ads_default_ou_string(ADS_STRUCT
*ads
, const char *wknguid
)
1659 LDAPMessage
*res
= NULL
;
1660 char *base
, *wkn_dn
= NULL
, *ret
= NULL
, **wkn_dn_exp
= NULL
,
1661 **bind_dn_exp
= NULL
;
1662 const char *attrs
[] = {"distinguishedName", NULL
};
1663 int new_ln
, wkn_ln
, bind_ln
, i
;
1665 if (wknguid
== NULL
) {
1669 if (asprintf(&base
, "<WKGUID=%s,%s>", wknguid
, ads
->config
.bind_path
) == -1) {
1670 DEBUG(1, ("asprintf failed!\n"));
1674 status
= ads_search_dn(ads
, &res
, base
, attrs
);
1675 if (!ADS_ERR_OK(status
)) {
1676 DEBUG(1,("Failed while searching for: %s\n", base
));
1680 if (ads_count_replies(ads
, res
) != 1) {
1684 /* substitute the bind-path from the well-known-guid-search result */
1685 wkn_dn
= ads_get_dn(ads
, talloc_tos(), res
);
1690 wkn_dn_exp
= ldap_explode_dn(wkn_dn
, 0);
1695 bind_dn_exp
= ldap_explode_dn(ads
->config
.bind_path
, 0);
1700 for (wkn_ln
=0; wkn_dn_exp
[wkn_ln
]; wkn_ln
++)
1702 for (bind_ln
=0; bind_dn_exp
[bind_ln
]; bind_ln
++)
1705 new_ln
= wkn_ln
- bind_ln
;
1707 ret
= SMB_STRDUP(wkn_dn_exp
[0]);
1712 for (i
=1; i
< new_ln
; i
++) {
1715 if (asprintf(&s
, "%s,%s", ret
, wkn_dn_exp
[i
]) == -1) {
1721 ret
= SMB_STRDUP(s
);
1730 ads_msgfree(ads
, res
);
1731 TALLOC_FREE(wkn_dn
);
1733 ldap_value_free(wkn_dn_exp
);
1736 ldap_value_free(bind_dn_exp
);
1743 * Adds (appends) an item to an attribute array, rather then
1744 * replacing the whole list
1745 * @param ctx An initialized TALLOC_CTX
1746 * @param mods An initialized ADS_MODLIST
1747 * @param name name of the ldap attribute to append to
1748 * @param vals an array of values to add
1749 * @return status of addition
1752 ADS_STATUS
ads_add_strlist(TALLOC_CTX
*ctx
, ADS_MODLIST
*mods
,
1753 const char *name
, const char **vals
)
1755 return ads_modlist_add(ctx
, mods
, LDAP_MOD_ADD
, name
,
1756 (const void *) vals
);
1760 * Determines the an account's current KVNO via an LDAP lookup
1761 * @param ads An initialized ADS_STRUCT
1762 * @param account_name the NT samaccountname.
1763 * @return the kvno for the account, or -1 in case of a failure.
1766 uint32
ads_get_kvno(ADS_STRUCT
*ads
, const char *account_name
)
1768 LDAPMessage
*res
= NULL
;
1769 uint32 kvno
= (uint32
)-1; /* -1 indicates a failure */
1771 const char *attrs
[] = {"msDS-KeyVersionNumber", NULL
};
1772 char *dn_string
= NULL
;
1773 ADS_STATUS ret
= ADS_ERROR(LDAP_SUCCESS
);
1775 DEBUG(5,("ads_get_kvno: Searching for account %s\n", account_name
));
1776 if (asprintf(&filter
, "(samAccountName=%s)", account_name
) == -1) {
1779 ret
= ads_search(ads
, &res
, filter
, attrs
);
1781 if (!ADS_ERR_OK(ret
) || (ads_count_replies(ads
, res
) != 1)) {
1782 DEBUG(1,("ads_get_kvno: Account for %s not found.\n", account_name
));
1783 ads_msgfree(ads
, res
);
1787 dn_string
= ads_get_dn(ads
, talloc_tos(), res
);
1789 DEBUG(0,("ads_get_kvno: out of memory.\n"));
1790 ads_msgfree(ads
, res
);
1793 DEBUG(5,("ads_get_kvno: Using: %s\n", dn_string
));
1794 TALLOC_FREE(dn_string
);
1796 /* ---------------------------------------------------------
1797 * 0 is returned as a default KVNO from this point on...
1798 * This is done because Windows 2000 does not support key
1799 * version numbers. Chances are that a failure in the next
1800 * step is simply due to Windows 2000 being used for a
1801 * domain controller. */
1804 if (!ads_pull_uint32(ads
, res
, "msDS-KeyVersionNumber", &kvno
)) {
1805 DEBUG(3,("ads_get_kvno: Error Determining KVNO!\n"));
1806 DEBUG(3,("ads_get_kvno: Windows 2000 does not support KVNO's, so this may be normal.\n"));
1807 ads_msgfree(ads
, res
);
1812 DEBUG(5,("ads_get_kvno: Looked Up KVNO of: %d\n", kvno
));
1813 ads_msgfree(ads
, res
);
1818 * Determines the computer account's current KVNO via an LDAP lookup
1819 * @param ads An initialized ADS_STRUCT
1820 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1821 * @return the kvno for the computer account, or -1 in case of a failure.
1824 uint32_t ads_get_machine_kvno(ADS_STRUCT
*ads
, const char *machine_name
)
1826 char *computer_account
= NULL
;
1829 if (asprintf(&computer_account
, "%s$", machine_name
) < 0) {
1833 kvno
= ads_get_kvno(ads
, computer_account
);
1834 free(computer_account
);
1840 * This clears out all registered spn's for a given hostname
1841 * @param ads An initilaized ADS_STRUCT
1842 * @param machine_name the NetBIOS name of the computer.
1843 * @return 0 upon success, non-zero otherwise.
1846 ADS_STATUS
ads_clear_service_principal_names(ADS_STRUCT
*ads
, const char *machine_name
)
1849 LDAPMessage
*res
= NULL
;
1851 const char *servicePrincipalName
[1] = {NULL
};
1852 ADS_STATUS ret
= ADS_ERROR(LDAP_SUCCESS
);
1853 char *dn_string
= NULL
;
1855 ret
= ads_find_machine_acct(ads
, &res
, machine_name
);
1856 if (!ADS_ERR_OK(ret
) || ads_count_replies(ads
, res
) != 1) {
1857 DEBUG(5,("ads_clear_service_principal_names: WARNING: Host Account for %s not found... skipping operation.\n", machine_name
));
1858 DEBUG(5,("ads_clear_service_principal_names: WARNING: Service Principals for %s have NOT been cleared.\n", machine_name
));
1859 ads_msgfree(ads
, res
);
1860 return ADS_ERROR(LDAP_NO_SUCH_OBJECT
);
1863 DEBUG(5,("ads_clear_service_principal_names: Host account for %s found\n", machine_name
));
1864 ctx
= talloc_init("ads_clear_service_principal_names");
1866 ads_msgfree(ads
, res
);
1867 return ADS_ERROR(LDAP_NO_MEMORY
);
1870 if (!(mods
= ads_init_mods(ctx
))) {
1871 talloc_destroy(ctx
);
1872 ads_msgfree(ads
, res
);
1873 return ADS_ERROR(LDAP_NO_MEMORY
);
1875 ret
= ads_mod_strlist(ctx
, &mods
, "servicePrincipalName", servicePrincipalName
);
1876 if (!ADS_ERR_OK(ret
)) {
1877 DEBUG(1,("ads_clear_service_principal_names: Error creating strlist.\n"));
1878 ads_msgfree(ads
, res
);
1879 talloc_destroy(ctx
);
1882 dn_string
= ads_get_dn(ads
, talloc_tos(), res
);
1884 talloc_destroy(ctx
);
1885 ads_msgfree(ads
, res
);
1886 return ADS_ERROR(LDAP_NO_MEMORY
);
1888 ret
= ads_gen_mod(ads
, dn_string
, mods
);
1889 TALLOC_FREE(dn_string
);
1890 if (!ADS_ERR_OK(ret
)) {
1891 DEBUG(1,("ads_clear_service_principal_names: Error: Updating Service Principals for machine %s in LDAP\n",
1893 ads_msgfree(ads
, res
);
1894 talloc_destroy(ctx
);
1898 ads_msgfree(ads
, res
);
1899 talloc_destroy(ctx
);
1904 * This adds a service principal name to an existing computer account
1905 * (found by hostname) in AD.
1906 * @param ads An initialized ADS_STRUCT
1907 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1908 * @param my_fqdn The fully qualified DNS name of the machine
1909 * @param spn A string of the service principal to add, i.e. 'host'
1910 * @return 0 upon sucess, or non-zero if a failure occurs
1913 ADS_STATUS
ads_add_service_principal_name(ADS_STRUCT
*ads
, const char *machine_name
,
1914 const char *my_fqdn
, const char *spn
)
1918 LDAPMessage
*res
= NULL
;
1921 char *dn_string
= NULL
;
1922 const char *servicePrincipalName
[3] = {NULL
, NULL
, NULL
};
1924 ret
= ads_find_machine_acct(ads
, &res
, machine_name
);
1925 if (!ADS_ERR_OK(ret
) || ads_count_replies(ads
, res
) != 1) {
1926 DEBUG(1,("ads_add_service_principal_name: WARNING: Host Account for %s not found... skipping operation.\n",
1928 DEBUG(1,("ads_add_service_principal_name: WARNING: Service Principal '%s/%s@%s' has NOT been added.\n",
1929 spn
, machine_name
, ads
->config
.realm
));
1930 ads_msgfree(ads
, res
);
1931 return ADS_ERROR(LDAP_NO_SUCH_OBJECT
);
1934 DEBUG(1,("ads_add_service_principal_name: Host account for %s found\n", machine_name
));
1935 if (!(ctx
= talloc_init("ads_add_service_principal_name"))) {
1936 ads_msgfree(ads
, res
);
1937 return ADS_ERROR(LDAP_NO_MEMORY
);
1940 /* add short name spn */
1942 if ( (psp1
= talloc_asprintf(ctx
, "%s/%s", spn
, machine_name
)) == NULL
) {
1943 talloc_destroy(ctx
);
1944 ads_msgfree(ads
, res
);
1945 return ADS_ERROR(LDAP_NO_MEMORY
);
1948 strlower_m(&psp1
[strlen(spn
)]);
1949 servicePrincipalName
[0] = psp1
;
1951 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n",
1952 psp1
, machine_name
));
1955 /* add fully qualified spn */
1957 if ( (psp2
= talloc_asprintf(ctx
, "%s/%s", spn
, my_fqdn
)) == NULL
) {
1958 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
1962 strlower_m(&psp2
[strlen(spn
)]);
1963 servicePrincipalName
[1] = psp2
;
1965 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n",
1966 psp2
, machine_name
));
1968 if ( (mods
= ads_init_mods(ctx
)) == NULL
) {
1969 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
1973 ret
= ads_add_strlist(ctx
, &mods
, "servicePrincipalName", servicePrincipalName
);
1974 if (!ADS_ERR_OK(ret
)) {
1975 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
1979 if ( (dn_string
= ads_get_dn(ads
, ctx
, res
)) == NULL
) {
1980 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
1984 ret
= ads_gen_mod(ads
, dn_string
, mods
);
1985 if (!ADS_ERR_OK(ret
)) {
1986 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
1992 ads_msgfree(ads
, res
);
1997 * adds a machine account to the ADS server
1998 * @param ads An intialized ADS_STRUCT
1999 * @param machine_name - the NetBIOS machine name of this account.
2000 * @param account_type A number indicating the type of account to create
2001 * @param org_unit The LDAP path in which to place this account
2002 * @return 0 upon success, or non-zero otherwise
2005 ADS_STATUS
ads_create_machine_acct(ADS_STRUCT
*ads
, const char *machine_name
,
2006 const char *org_unit
)
2009 char *samAccountName
, *controlstr
;
2012 char *machine_escaped
= NULL
;
2014 const char *objectClass
[] = {"top", "person", "organizationalPerson",
2015 "user", "computer", NULL
};
2016 LDAPMessage
*res
= NULL
;
2017 uint32 acct_control
= ( UF_WORKSTATION_TRUST_ACCOUNT
|\
2018 UF_DONT_EXPIRE_PASSWD
|\
2019 UF_ACCOUNTDISABLE
);
2021 if (!(ctx
= talloc_init("ads_add_machine_acct")))
2022 return ADS_ERROR(LDAP_NO_MEMORY
);
2024 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
2026 machine_escaped
= escape_rdn_val_string_alloc(machine_name
);
2027 if (!machine_escaped
) {
2031 new_dn
= talloc_asprintf(ctx
, "cn=%s,%s", machine_escaped
, org_unit
);
2032 samAccountName
= talloc_asprintf(ctx
, "%s$", machine_name
);
2034 if ( !new_dn
|| !samAccountName
) {
2038 #ifndef ENCTYPE_ARCFOUR_HMAC
2039 acct_control
|= UF_USE_DES_KEY_ONLY
;
2042 if (!(controlstr
= talloc_asprintf(ctx
, "%u", acct_control
))) {
2046 if (!(mods
= ads_init_mods(ctx
))) {
2050 ads_mod_str(ctx
, &mods
, "cn", machine_name
);
2051 ads_mod_str(ctx
, &mods
, "sAMAccountName", samAccountName
);
2052 ads_mod_strlist(ctx
, &mods
, "objectClass", objectClass
);
2053 ads_mod_str(ctx
, &mods
, "userAccountControl", controlstr
);
2055 ret
= ads_gen_add(ads
, new_dn
, mods
);
2058 SAFE_FREE(machine_escaped
);
2059 ads_msgfree(ads
, res
);
2060 talloc_destroy(ctx
);
2066 * move a machine account to another OU on the ADS server
2067 * @param ads - An intialized ADS_STRUCT
2068 * @param machine_name - the NetBIOS machine name of this account.
2069 * @param org_unit - The LDAP path in which to place this account
2070 * @param moved - whether we moved the machine account (optional)
2071 * @return 0 upon success, or non-zero otherwise
2074 ADS_STATUS
ads_move_machine_acct(ADS_STRUCT
*ads
, const char *machine_name
,
2075 const char *org_unit
, bool *moved
)
2079 LDAPMessage
*res
= NULL
;
2080 char *filter
= NULL
;
2081 char *computer_dn
= NULL
;
2083 char *computer_rdn
= NULL
;
2084 bool need_move
= False
;
2086 if (asprintf(&filter
, "(samAccountName=%s$)", machine_name
) == -1) {
2087 rc
= ADS_ERROR(LDAP_NO_MEMORY
);
2091 /* Find pre-existing machine */
2092 rc
= ads_search(ads
, &res
, filter
, NULL
);
2093 if (!ADS_ERR_OK(rc
)) {
2097 computer_dn
= ads_get_dn(ads
, talloc_tos(), res
);
2099 rc
= ADS_ERROR(LDAP_NO_MEMORY
);
2103 parent_dn
= ads_parent_dn(computer_dn
);
2104 if (strequal(parent_dn
, org_unit
)) {
2110 if (asprintf(&computer_rdn
, "CN=%s", machine_name
) == -1) {
2111 rc
= ADS_ERROR(LDAP_NO_MEMORY
);
2115 ldap_status
= ldap_rename_s(ads
->ldap
.ld
, computer_dn
, computer_rdn
,
2116 org_unit
, 1, NULL
, NULL
);
2117 rc
= ADS_ERROR(ldap_status
);
2120 ads_msgfree(ads
, res
);
2122 TALLOC_FREE(computer_dn
);
2123 SAFE_FREE(computer_rdn
);
2125 if (!ADS_ERR_OK(rc
)) {
2137 dump a binary result from ldap
2139 static void dump_binary(ADS_STRUCT
*ads
, const char *field
, struct berval
**values
)
2142 for (i
=0; values
[i
]; i
++) {
2143 printf("%s: ", field
);
2144 for (j
=0; j
<values
[i
]->bv_len
; j
++) {
2145 printf("%02X", (unsigned char)values
[i
]->bv_val
[j
]);
2151 static void dump_guid(ADS_STRUCT
*ads
, const char *field
, struct berval
**values
)
2154 for (i
=0; values
[i
]; i
++) {
2156 DATA_BLOB in
= data_blob_const(values
[i
]->bv_val
, values
[i
]->bv_len
);
2159 status
= GUID_from_ndr_blob(&in
, &guid
);
2160 if (NT_STATUS_IS_OK(status
)) {
2161 printf("%s: %s\n", field
, GUID_string(talloc_tos(), &guid
));
2163 printf("%s: INVALID GUID\n", field
);
2169 dump a sid result from ldap
2171 static void dump_sid(ADS_STRUCT
*ads
, const char *field
, struct berval
**values
)
2174 for (i
=0; values
[i
]; i
++) {
2177 if (!sid_parse(values
[i
]->bv_val
, values
[i
]->bv_len
, &sid
)) {
2180 printf("%s: %s\n", field
, sid_to_fstring(tmp
, &sid
));
2185 dump ntSecurityDescriptor
2187 static void dump_sd(ADS_STRUCT
*ads
, const char *filed
, struct berval
**values
)
2189 TALLOC_CTX
*frame
= talloc_stackframe();
2190 struct security_descriptor
*psd
;
2193 status
= unmarshall_sec_desc(talloc_tos(), (uint8
*)values
[0]->bv_val
,
2194 values
[0]->bv_len
, &psd
);
2195 if (!NT_STATUS_IS_OK(status
)) {
2196 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
2197 nt_errstr(status
)));
2203 ads_disp_sd(ads
, talloc_tos(), psd
);
2210 dump a string result from ldap
2212 static void dump_string(const char *field
, char **values
)
2215 for (i
=0; values
[i
]; i
++) {
2216 printf("%s: %s\n", field
, values
[i
]);
2221 dump a field from LDAP on stdout
2225 static bool ads_dump_field(ADS_STRUCT
*ads
, char *field
, void **values
, void *data_area
)
2230 void (*handler
)(ADS_STRUCT
*, const char *, struct berval
**);
2232 {"objectGUID", False
, dump_guid
},
2233 {"netbootGUID", False
, dump_guid
},
2234 {"nTSecurityDescriptor", False
, dump_sd
},
2235 {"dnsRecord", False
, dump_binary
},
2236 {"objectSid", False
, dump_sid
},
2237 {"tokenGroups", False
, dump_sid
},
2238 {"tokenGroupsNoGCAcceptable", False
, dump_sid
},
2239 {"tokengroupsGlobalandUniversal", False
, dump_sid
},
2240 {"mS-DS-CreatorSID", False
, dump_sid
},
2241 {"msExchMailboxGuid", False
, dump_guid
},
2246 if (!field
) { /* must be end of an entry */
2251 for (i
=0; handlers
[i
].name
; i
++) {
2252 if (strcasecmp_m(handlers
[i
].name
, field
) == 0) {
2253 if (!values
) /* first time, indicate string or not */
2254 return handlers
[i
].string
;
2255 handlers
[i
].handler(ads
, field
, (struct berval
**) values
);
2259 if (!handlers
[i
].name
) {
2260 if (!values
) /* first time, indicate string conversion */
2262 dump_string(field
, (char **)values
);
2268 * Dump a result from LDAP on stdout
2269 * used for debugging
2270 * @param ads connection to ads server
2271 * @param res Results to dump
2274 void ads_dump(ADS_STRUCT
*ads
, LDAPMessage
*res
)
2276 ads_process_results(ads
, res
, ads_dump_field
, NULL
);
2280 * Walk through results, calling a function for each entry found.
2281 * The function receives a field name, a berval * array of values,
2282 * and a data area passed through from the start. The function is
2283 * called once with null for field and values at the end of each
2285 * @param ads connection to ads server
2286 * @param res Results to process
2287 * @param fn Function for processing each result
2288 * @param data_area user-defined area to pass to function
2290 void ads_process_results(ADS_STRUCT
*ads
, LDAPMessage
*res
,
2291 bool (*fn
)(ADS_STRUCT
*, char *, void **, void *),
2296 size_t converted_size
;
2298 if (!(ctx
= talloc_init("ads_process_results")))
2301 for (msg
= ads_first_entry(ads
, res
); msg
;
2302 msg
= ads_next_entry(ads
, msg
)) {
2306 for (utf8_field
=ldap_first_attribute(ads
->ldap
.ld
,
2307 (LDAPMessage
*)msg
,&b
);
2309 utf8_field
=ldap_next_attribute(ads
->ldap
.ld
,
2310 (LDAPMessage
*)msg
,b
)) {
2311 struct berval
**ber_vals
;
2312 char **str_vals
, **utf8_vals
;
2316 if (!pull_utf8_talloc(ctx
, &field
, utf8_field
,
2319 DEBUG(0,("ads_process_results: "
2320 "pull_utf8_talloc failed: %s",
2324 string
= fn(ads
, field
, NULL
, data_area
);
2327 utf8_vals
= ldap_get_values(ads
->ldap
.ld
,
2328 (LDAPMessage
*)msg
, field
);
2329 str_vals
= ads_pull_strvals(ctx
,
2330 (const char **) utf8_vals
);
2331 fn(ads
, field
, (void **) str_vals
, data_area
);
2332 ldap_value_free(utf8_vals
);
2334 ber_vals
= ldap_get_values_len(ads
->ldap
.ld
,
2335 (LDAPMessage
*)msg
, field
);
2336 fn(ads
, field
, (void **) ber_vals
, data_area
);
2338 ldap_value_free_len(ber_vals
);
2340 ldap_memfree(utf8_field
);
2343 talloc_free_children(ctx
);
2344 fn(ads
, NULL
, NULL
, data_area
); /* completed an entry */
2347 talloc_destroy(ctx
);
2351 * count how many replies are in a LDAPMessage
2352 * @param ads connection to ads server
2353 * @param res Results to count
2354 * @return number of replies
2356 int ads_count_replies(ADS_STRUCT
*ads
, void *res
)
2358 return ldap_count_entries(ads
->ldap
.ld
, (LDAPMessage
*)res
);
2362 * pull the first entry from a ADS result
2363 * @param ads connection to ads server
2364 * @param res Results of search
2365 * @return first entry from result
2367 LDAPMessage
*ads_first_entry(ADS_STRUCT
*ads
, LDAPMessage
*res
)
2369 return ldap_first_entry(ads
->ldap
.ld
, res
);
2373 * pull the next entry from a ADS result
2374 * @param ads connection to ads server
2375 * @param res Results of search
2376 * @return next entry from result
2378 LDAPMessage
*ads_next_entry(ADS_STRUCT
*ads
, LDAPMessage
*res
)
2380 return ldap_next_entry(ads
->ldap
.ld
, res
);
2384 * pull the first message from a ADS result
2385 * @param ads connection to ads server
2386 * @param res Results of search
2387 * @return first message from result
2389 LDAPMessage
*ads_first_message(ADS_STRUCT
*ads
, LDAPMessage
*res
)
2391 return ldap_first_message(ads
->ldap
.ld
, res
);
2395 * pull the next message from a ADS result
2396 * @param ads connection to ads server
2397 * @param res Results of search
2398 * @return next message from result
2400 LDAPMessage
*ads_next_message(ADS_STRUCT
*ads
, LDAPMessage
*res
)
2402 return ldap_next_message(ads
->ldap
.ld
, res
);
2406 * pull a single string from a ADS result
2407 * @param ads connection to ads server
2408 * @param mem_ctx TALLOC_CTX to use for allocating result string
2409 * @param msg Results of search
2410 * @param field Attribute to retrieve
2411 * @return Result string in talloc context
2413 char *ads_pull_string(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
, LDAPMessage
*msg
,
2419 size_t converted_size
;
2421 values
= ldap_get_values(ads
->ldap
.ld
, msg
, field
);
2425 if (values
[0] && pull_utf8_talloc(mem_ctx
, &ux_string
, values
[0],
2430 ldap_value_free(values
);
2435 * pull an array of strings from a ADS result
2436 * @param ads connection to ads server
2437 * @param mem_ctx TALLOC_CTX to use for allocating result string
2438 * @param msg Results of search
2439 * @param field Attribute to retrieve
2440 * @return Result strings in talloc context
2442 char **ads_pull_strings(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
,
2443 LDAPMessage
*msg
, const char *field
,
2449 size_t converted_size
;
2451 values
= ldap_get_values(ads
->ldap
.ld
, msg
, field
);
2455 *num_values
= ldap_count_values(values
);
2457 ret
= talloc_array(mem_ctx
, char *, *num_values
+ 1);
2459 ldap_value_free(values
);
2463 for (i
=0;i
<*num_values
;i
++) {
2464 if (!pull_utf8_talloc(mem_ctx
, &ret
[i
], values
[i
],
2467 ldap_value_free(values
);
2473 ldap_value_free(values
);
2478 * pull an array of strings from a ADS result
2479 * (handle large multivalue attributes with range retrieval)
2480 * @param ads connection to ads server
2481 * @param mem_ctx TALLOC_CTX to use for allocating result string
2482 * @param msg Results of search
2483 * @param field Attribute to retrieve
2484 * @param current_strings strings returned by a previous call to this function
2485 * @param next_attribute The next query should ask for this attribute
2486 * @param num_values How many values did we get this time?
2487 * @param more_values Are there more values to get?
2488 * @return Result strings in talloc context
2490 char **ads_pull_strings_range(ADS_STRUCT
*ads
,
2491 TALLOC_CTX
*mem_ctx
,
2492 LDAPMessage
*msg
, const char *field
,
2493 char **current_strings
,
2494 const char **next_attribute
,
2495 size_t *num_strings
,
2499 char *expected_range_attrib
, *range_attr
;
2500 BerElement
*ptr
= NULL
;
2503 size_t num_new_strings
;
2504 unsigned long int range_start
;
2505 unsigned long int range_end
;
2507 /* we might have been given the whole lot anyway */
2508 if ((strings
= ads_pull_strings(ads
, mem_ctx
, msg
, field
, num_strings
))) {
2509 *more_strings
= False
;
2513 expected_range_attrib
= talloc_asprintf(mem_ctx
, "%s;Range=", field
);
2515 /* look for Range result */
2516 for (attr
= ldap_first_attribute(ads
->ldap
.ld
, (LDAPMessage
*)msg
, &ptr
);
2518 attr
= ldap_next_attribute(ads
->ldap
.ld
, (LDAPMessage
*)msg
, ptr
)) {
2519 /* we ignore the fact that this is utf8, as all attributes are ascii... */
2520 if (strnequal(attr
, expected_range_attrib
, strlen(expected_range_attrib
))) {
2528 /* nothing here - this field is just empty */
2529 *more_strings
= False
;
2533 if (sscanf(&range_attr
[strlen(expected_range_attrib
)], "%lu-%lu",
2534 &range_start
, &range_end
) == 2) {
2535 *more_strings
= True
;
2537 if (sscanf(&range_attr
[strlen(expected_range_attrib
)], "%lu-*",
2538 &range_start
) == 1) {
2539 *more_strings
= False
;
2541 DEBUG(1, ("ads_pull_strings_range: Cannot parse Range attriubte (%s)\n",
2543 ldap_memfree(range_attr
);
2544 *more_strings
= False
;
2549 if ((*num_strings
) != range_start
) {
2550 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) doesn't start at %u, but at %lu"
2551 " - aborting range retreival\n",
2552 range_attr
, (unsigned int)(*num_strings
) + 1, range_start
));
2553 ldap_memfree(range_attr
);
2554 *more_strings
= False
;
2558 new_strings
= ads_pull_strings(ads
, mem_ctx
, msg
, range_attr
, &num_new_strings
);
2560 if (*more_strings
&& ((*num_strings
+ num_new_strings
) != (range_end
+ 1))) {
2561 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) tells us we have %lu "
2562 "strings in this bunch, but we only got %lu - aborting range retreival\n",
2563 range_attr
, (unsigned long int)range_end
- range_start
+ 1,
2564 (unsigned long int)num_new_strings
));
2565 ldap_memfree(range_attr
);
2566 *more_strings
= False
;
2570 strings
= talloc_realloc(mem_ctx
, current_strings
, char *,
2571 *num_strings
+ num_new_strings
);
2573 if (strings
== NULL
) {
2574 ldap_memfree(range_attr
);
2575 *more_strings
= False
;
2579 if (new_strings
&& num_new_strings
) {
2580 memcpy(&strings
[*num_strings
], new_strings
,
2581 sizeof(*new_strings
) * num_new_strings
);
2584 (*num_strings
) += num_new_strings
;
2586 if (*more_strings
) {
2587 *next_attribute
= talloc_asprintf(mem_ctx
,
2592 if (!*next_attribute
) {
2593 DEBUG(1, ("talloc_asprintf for next attribute failed!\n"));
2594 ldap_memfree(range_attr
);
2595 *more_strings
= False
;
2600 ldap_memfree(range_attr
);
2606 * pull a single uint32 from a ADS result
2607 * @param ads connection to ads server
2608 * @param msg Results of search
2609 * @param field Attribute to retrieve
2610 * @param v Pointer to int to store result
2611 * @return boolean inidicating success
2613 bool ads_pull_uint32(ADS_STRUCT
*ads
, LDAPMessage
*msg
, const char *field
,
2618 values
= ldap_get_values(ads
->ldap
.ld
, msg
, field
);
2622 ldap_value_free(values
);
2626 *v
= atoi(values
[0]);
2627 ldap_value_free(values
);
2632 * pull a single objectGUID from an ADS result
2633 * @param ads connection to ADS server
2634 * @param msg results of search
2635 * @param guid 37-byte area to receive text guid
2636 * @return boolean indicating success
2638 bool ads_pull_guid(ADS_STRUCT
*ads
, LDAPMessage
*msg
, struct GUID
*guid
)
2643 if (!smbldap_talloc_single_blob(talloc_tos(), ads
->ldap
.ld
, msg
, "objectGUID",
2648 status
= GUID_from_ndr_blob(&blob
, guid
);
2649 talloc_free(blob
.data
);
2650 return NT_STATUS_IS_OK(status
);
2655 * pull a single struct dom_sid from a ADS result
2656 * @param ads connection to ads server
2657 * @param msg Results of search
2658 * @param field Attribute to retrieve
2659 * @param sid Pointer to sid to store result
2660 * @return boolean inidicating success
2662 bool ads_pull_sid(ADS_STRUCT
*ads
, LDAPMessage
*msg
, const char *field
,
2663 struct dom_sid
*sid
)
2665 return smbldap_pull_sid(ads
->ldap
.ld
, msg
, field
, sid
);
2669 * pull an array of struct dom_sids from a ADS result
2670 * @param ads connection to ads server
2671 * @param mem_ctx TALLOC_CTX for allocating sid array
2672 * @param msg Results of search
2673 * @param field Attribute to retrieve
2674 * @param sids pointer to sid array to allocate
2675 * @return the count of SIDs pulled
2677 int ads_pull_sids(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
,
2678 LDAPMessage
*msg
, const char *field
, struct dom_sid
**sids
)
2680 struct berval
**values
;
2684 values
= ldap_get_values_len(ads
->ldap
.ld
, msg
, field
);
2689 for (i
=0; values
[i
]; i
++)
2693 (*sids
) = talloc_array(mem_ctx
, struct dom_sid
, i
);
2695 ldap_value_free_len(values
);
2703 for (i
=0; values
[i
]; i
++) {
2704 ret
= sid_parse(values
[i
]->bv_val
, values
[i
]->bv_len
, &(*sids
)[count
]);
2706 DEBUG(10, ("pulling SID: %s\n",
2707 sid_string_dbg(&(*sids
)[count
])));
2712 ldap_value_free_len(values
);
2717 * pull a struct security_descriptor from a ADS result
2718 * @param ads connection to ads server
2719 * @param mem_ctx TALLOC_CTX for allocating sid array
2720 * @param msg Results of search
2721 * @param field Attribute to retrieve
2722 * @param sd Pointer to *struct security_descriptor to store result (talloc()ed)
2723 * @return boolean inidicating success
2725 bool ads_pull_sd(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
,
2726 LDAPMessage
*msg
, const char *field
,
2727 struct security_descriptor
**sd
)
2729 struct berval
**values
;
2732 values
= ldap_get_values_len(ads
->ldap
.ld
, msg
, field
);
2734 if (!values
) return false;
2738 status
= unmarshall_sec_desc(mem_ctx
,
2739 (uint8
*)values
[0]->bv_val
,
2740 values
[0]->bv_len
, sd
);
2741 if (!NT_STATUS_IS_OK(status
)) {
2742 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
2743 nt_errstr(status
)));
2748 ldap_value_free_len(values
);
2753 * in order to support usernames longer than 21 characters we need to
2754 * use both the sAMAccountName and the userPrincipalName attributes
2755 * It seems that not all users have the userPrincipalName attribute set
2757 * @param ads connection to ads server
2758 * @param mem_ctx TALLOC_CTX for allocating sid array
2759 * @param msg Results of search
2760 * @return the username
2762 char *ads_pull_username(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
,
2768 /* lookup_name() only works on the sAMAccountName to
2769 returning the username portion of userPrincipalName
2770 breaks winbindd_getpwnam() */
2772 ret
= ads_pull_string(ads
, mem_ctx
, msg
, "userPrincipalName");
2773 if (ret
&& (p
= strchr_m(ret
, '@'))) {
2778 return ads_pull_string(ads
, mem_ctx
, msg
, "sAMAccountName");
2783 * find the update serial number - this is the core of the ldap cache
2784 * @param ads connection to ads server
2785 * @param ads connection to ADS server
2786 * @param usn Pointer to retrieved update serial number
2787 * @return status of search
2789 ADS_STATUS
ads_USN(ADS_STRUCT
*ads
, uint32
*usn
)
2791 const char *attrs
[] = {"highestCommittedUSN", NULL
};
2795 status
= ads_do_search_retry(ads
, "", LDAP_SCOPE_BASE
, "(objectclass=*)", attrs
, &res
);
2796 if (!ADS_ERR_OK(status
))
2799 if (ads_count_replies(ads
, res
) != 1) {
2800 ads_msgfree(ads
, res
);
2801 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED
);
2804 if (!ads_pull_uint32(ads
, res
, "highestCommittedUSN", usn
)) {
2805 ads_msgfree(ads
, res
);
2806 return ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE
);
2809 ads_msgfree(ads
, res
);
2813 /* parse a ADS timestring - typical string is
2814 '20020917091222.0Z0' which means 09:12.22 17th September
2816 static time_t ads_parse_time(const char *str
)
2822 if (sscanf(str
, "%4d%2d%2d%2d%2d%2d",
2823 &tm
.tm_year
, &tm
.tm_mon
, &tm
.tm_mday
,
2824 &tm
.tm_hour
, &tm
.tm_min
, &tm
.tm_sec
) != 6) {
2833 /********************************************************************
2834 ********************************************************************/
2836 ADS_STATUS
ads_current_time(ADS_STRUCT
*ads
)
2838 const char *attrs
[] = {"currentTime", NULL
};
2843 ADS_STRUCT
*ads_s
= ads
;
2845 if (!(ctx
= talloc_init("ads_current_time"))) {
2846 return ADS_ERROR(LDAP_NO_MEMORY
);
2849 /* establish a new ldap tcp session if necessary */
2851 if ( !ads
->ldap
.ld
) {
2852 if ( (ads_s
= ads_init( ads
->server
.realm
, ads
->server
.workgroup
,
2853 ads
->server
.ldap_server
)) == NULL
)
2857 ads_s
->auth
.flags
= ADS_AUTH_ANON_BIND
;
2858 status
= ads_connect( ads_s
);
2859 if ( !ADS_ERR_OK(status
))
2863 status
= ads_do_search(ads_s
, "", LDAP_SCOPE_BASE
, "(objectclass=*)", attrs
, &res
);
2864 if (!ADS_ERR_OK(status
)) {
2868 timestr
= ads_pull_string(ads_s
, ctx
, res
, "currentTime");
2870 ads_msgfree(ads_s
, res
);
2871 status
= ADS_ERROR(LDAP_NO_RESULTS_RETURNED
);
2875 /* but save the time and offset in the original ADS_STRUCT */
2877 ads
->config
.current_time
= ads_parse_time(timestr
);
2879 if (ads
->config
.current_time
!= 0) {
2880 ads
->auth
.time_offset
= ads
->config
.current_time
- time(NULL
);
2881 DEBUG(4,("KDC time offset is %d seconds\n", ads
->auth
.time_offset
));
2884 ads_msgfree(ads
, res
);
2886 status
= ADS_SUCCESS
;
2889 /* free any temporary ads connections */
2890 if ( ads_s
!= ads
) {
2891 ads_destroy( &ads_s
);
2893 talloc_destroy(ctx
);
2898 /********************************************************************
2899 ********************************************************************/
2901 ADS_STATUS
ads_domain_func_level(ADS_STRUCT
*ads
, uint32
*val
)
2903 const char *attrs
[] = {"domainFunctionality", NULL
};
2906 ADS_STRUCT
*ads_s
= ads
;
2908 *val
= DS_DOMAIN_FUNCTION_2000
;
2910 /* establish a new ldap tcp session if necessary */
2912 if ( !ads
->ldap
.ld
) {
2913 if ( (ads_s
= ads_init( ads
->server
.realm
, ads
->server
.workgroup
,
2914 ads
->server
.ldap_server
)) == NULL
)
2916 status
= ADS_ERROR_NT(NT_STATUS_NO_MEMORY
);
2919 ads_s
->auth
.flags
= ADS_AUTH_ANON_BIND
;
2920 status
= ads_connect( ads_s
);
2921 if ( !ADS_ERR_OK(status
))
2925 /* If the attribute does not exist assume it is a Windows 2000
2926 functional domain */
2928 status
= ads_do_search(ads_s
, "", LDAP_SCOPE_BASE
, "(objectclass=*)", attrs
, &res
);
2929 if (!ADS_ERR_OK(status
)) {
2930 if ( status
.err
.rc
== LDAP_NO_SUCH_ATTRIBUTE
) {
2931 status
= ADS_SUCCESS
;
2936 if ( !ads_pull_uint32(ads_s
, res
, "domainFunctionality", val
) ) {
2937 DEBUG(5,("ads_domain_func_level: Failed to pull the domainFunctionality attribute.\n"));
2939 DEBUG(3,("ads_domain_func_level: %d\n", *val
));
2942 ads_msgfree(ads
, res
);
2945 /* free any temporary ads connections */
2946 if ( ads_s
!= ads
) {
2947 ads_destroy( &ads_s
);
2954 * find the domain sid for our domain
2955 * @param ads connection to ads server
2956 * @param sid Pointer to domain sid
2957 * @return status of search
2959 ADS_STATUS
ads_domain_sid(ADS_STRUCT
*ads
, struct dom_sid
*sid
)
2961 const char *attrs
[] = {"objectSid", NULL
};
2965 rc
= ads_do_search_retry(ads
, ads
->config
.bind_path
, LDAP_SCOPE_BASE
, "(objectclass=*)",
2967 if (!ADS_ERR_OK(rc
)) return rc
;
2968 if (!ads_pull_sid(ads
, res
, "objectSid", sid
)) {
2969 ads_msgfree(ads
, res
);
2970 return ADS_ERROR_SYSTEM(ENOENT
);
2972 ads_msgfree(ads
, res
);
2978 * find our site name
2979 * @param ads connection to ads server
2980 * @param mem_ctx Pointer to talloc context
2981 * @param site_name Pointer to the sitename
2982 * @return status of search
2984 ADS_STATUS
ads_site_dn(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
, const char **site_name
)
2988 const char *dn
, *service_name
;
2989 const char *attrs
[] = { "dsServiceName", NULL
};
2991 status
= ads_do_search(ads
, "", LDAP_SCOPE_BASE
, "(objectclass=*)", attrs
, &res
);
2992 if (!ADS_ERR_OK(status
)) {
2996 service_name
= ads_pull_string(ads
, mem_ctx
, res
, "dsServiceName");
2997 if (service_name
== NULL
) {
2998 ads_msgfree(ads
, res
);
2999 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED
);
3002 ads_msgfree(ads
, res
);
3004 /* go up three levels */
3005 dn
= ads_parent_dn(ads_parent_dn(ads_parent_dn(service_name
)));
3007 return ADS_ERROR(LDAP_NO_MEMORY
);
3010 *site_name
= talloc_strdup(mem_ctx
, dn
);
3011 if (*site_name
== NULL
) {
3012 return ADS_ERROR(LDAP_NO_MEMORY
);
3017 dsServiceName: CN=NTDS Settings,CN=W2K3DC,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,DC=ber,DC=suse,DC=de
3022 * find the site dn where a machine resides
3023 * @param ads connection to ads server
3024 * @param mem_ctx Pointer to talloc context
3025 * @param computer_name name of the machine
3026 * @param site_name Pointer to the sitename
3027 * @return status of search
3029 ADS_STATUS
ads_site_dn_for_machine(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
, const char *computer_name
, const char **site_dn
)
3033 const char *parent
, *filter
;
3034 char *config_context
= NULL
;
3037 /* shortcut a query */
3038 if (strequal(computer_name
, ads
->config
.ldap_server_name
)) {
3039 return ads_site_dn(ads
, mem_ctx
, site_dn
);
3042 status
= ads_config_path(ads
, mem_ctx
, &config_context
);
3043 if (!ADS_ERR_OK(status
)) {
3047 filter
= talloc_asprintf(mem_ctx
, "(cn=%s)", computer_name
);
3048 if (filter
== NULL
) {
3049 return ADS_ERROR(LDAP_NO_MEMORY
);
3052 status
= ads_do_search(ads
, config_context
, LDAP_SCOPE_SUBTREE
,
3053 filter
, NULL
, &res
);
3054 if (!ADS_ERR_OK(status
)) {
3058 if (ads_count_replies(ads
, res
) != 1) {
3059 ads_msgfree(ads
, res
);
3060 return ADS_ERROR(LDAP_NO_SUCH_OBJECT
);
3063 dn
= ads_get_dn(ads
, mem_ctx
, res
);
3065 ads_msgfree(ads
, res
);
3066 return ADS_ERROR(LDAP_NO_MEMORY
);
3069 /* go up three levels */
3070 parent
= ads_parent_dn(ads_parent_dn(ads_parent_dn(dn
)));
3071 if (parent
== NULL
) {
3072 ads_msgfree(ads
, res
);
3074 return ADS_ERROR(LDAP_NO_MEMORY
);
3077 *site_dn
= talloc_strdup(mem_ctx
, parent
);
3078 if (*site_dn
== NULL
) {
3079 ads_msgfree(ads
, res
);
3081 return ADS_ERROR(LDAP_NO_MEMORY
);
3085 ads_msgfree(ads
, res
);
3091 * get the upn suffixes for a domain
3092 * @param ads connection to ads server
3093 * @param mem_ctx Pointer to talloc context
3094 * @param suffixes Pointer to an array of suffixes
3095 * @param num_suffixes Pointer to the number of suffixes
3096 * @return status of search
3098 ADS_STATUS
ads_upn_suffixes(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
, char ***suffixes
, size_t *num_suffixes
)
3103 char *config_context
= NULL
;
3104 const char *attrs
[] = { "uPNSuffixes", NULL
};
3106 status
= ads_config_path(ads
, mem_ctx
, &config_context
);
3107 if (!ADS_ERR_OK(status
)) {
3111 base
= talloc_asprintf(mem_ctx
, "cn=Partitions,%s", config_context
);
3113 return ADS_ERROR(LDAP_NO_MEMORY
);
3116 status
= ads_search_dn(ads
, &res
, base
, attrs
);
3117 if (!ADS_ERR_OK(status
)) {
3121 if (ads_count_replies(ads
, res
) != 1) {
3122 ads_msgfree(ads
, res
);
3123 return ADS_ERROR(LDAP_NO_SUCH_OBJECT
);
3126 (*suffixes
) = ads_pull_strings(ads
, mem_ctx
, res
, "uPNSuffixes", num_suffixes
);
3127 if ((*suffixes
) == NULL
) {
3128 ads_msgfree(ads
, res
);
3129 return ADS_ERROR(LDAP_NO_MEMORY
);
3132 ads_msgfree(ads
, res
);
3138 * get the joinable ous for a domain
3139 * @param ads connection to ads server
3140 * @param mem_ctx Pointer to talloc context
3141 * @param ous Pointer to an array of ous
3142 * @param num_ous Pointer to the number of ous
3143 * @return status of search
3145 ADS_STATUS
ads_get_joinable_ous(ADS_STRUCT
*ads
,
3146 TALLOC_CTX
*mem_ctx
,
3151 LDAPMessage
*res
= NULL
;
3152 LDAPMessage
*msg
= NULL
;
3153 const char *attrs
[] = { "dn", NULL
};
3156 status
= ads_search(ads
, &res
,
3157 "(|(objectClass=domain)(objectclass=organizationalUnit))",
3159 if (!ADS_ERR_OK(status
)) {
3163 count
= ads_count_replies(ads
, res
);
3165 ads_msgfree(ads
, res
);
3166 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED
);
3169 for (msg
= ads_first_entry(ads
, res
); msg
;
3170 msg
= ads_next_entry(ads
, msg
)) {
3174 dn
= ads_get_dn(ads
, talloc_tos(), msg
);
3176 ads_msgfree(ads
, res
);
3177 return ADS_ERROR(LDAP_NO_MEMORY
);
3180 if (!add_string_to_array(mem_ctx
, dn
,
3181 (const char ***)ous
,
3184 ads_msgfree(ads
, res
);
3185 return ADS_ERROR(LDAP_NO_MEMORY
);
3191 ads_msgfree(ads
, res
);
3198 * pull a struct dom_sid from an extended dn string
3199 * @param mem_ctx TALLOC_CTX
3200 * @param extended_dn string
3201 * @param flags string type of extended_dn
3202 * @param sid pointer to a struct dom_sid
3203 * @return NT_STATUS_OK on success,
3204 * NT_INVALID_PARAMETER on error,
3205 * NT_STATUS_NOT_FOUND if no SID present
3207 ADS_STATUS
ads_get_sid_from_extended_dn(TALLOC_CTX
*mem_ctx
,
3208 const char *extended_dn
,
3209 enum ads_extended_dn_flags flags
,
3210 struct dom_sid
*sid
)
3215 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER
);
3218 /* otherwise extended_dn gets stripped off */
3219 if ((dn
= talloc_strdup(mem_ctx
, extended_dn
)) == NULL
) {
3220 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER
);
3223 * ADS_EXTENDED_DN_HEX_STRING:
3224 * <GUID=238e1963cb390f4bb032ba0105525a29>;<SID=010500000000000515000000bb68c8fd6b61b427572eb04556040000>;CN=gd,OU=berlin,OU=suse,DC=ber,DC=suse,DC=de
3226 * ADS_EXTENDED_DN_STRING (only with w2k3):
3227 * <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
3229 * Object with no SID, such as an Exchange Public Folder
3230 * <GUID=28907fb4bdf6854993e7f0a10b504e7c>;CN=public,CN=Microsoft Exchange System Objects,DC=sd2k3ms,DC=west,DC=isilon,DC=com
3233 p
= strchr(dn
, ';');
3235 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER
);
3238 if (strncmp(p
, ";<SID=", strlen(";<SID=")) != 0) {
3239 DEBUG(5,("No SID present in extended dn\n"));
3240 return ADS_ERROR_NT(NT_STATUS_NOT_FOUND
);
3243 p
+= strlen(";<SID=");
3247 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER
);
3252 DEBUG(100,("ads_get_sid_from_extended_dn: sid string is %s\n", p
));
3256 case ADS_EXTENDED_DN_STRING
:
3257 if (!string_to_sid(sid
, p
)) {
3258 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER
);
3261 case ADS_EXTENDED_DN_HEX_STRING
: {
3265 buf_len
= strhex_to_str(buf
, sizeof(buf
), p
, strlen(p
));
3267 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER
);
3270 if (!sid_parse(buf
, buf_len
, sid
)) {
3271 DEBUG(10,("failed to parse sid\n"));
3272 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER
);
3277 DEBUG(10,("unknown extended dn format\n"));
3278 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER
);
3281 return ADS_ERROR_NT(NT_STATUS_OK
);
3284 /********************************************************************
3285 ********************************************************************/
3287 char* ads_get_dnshostname( ADS_STRUCT
*ads
, TALLOC_CTX
*ctx
, const char *machine_name
)
3289 LDAPMessage
*res
= NULL
;
3294 status
= ads_find_machine_acct(ads
, &res
, lp_netbios_name());
3295 if (!ADS_ERR_OK(status
)) {
3296 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
3297 lp_netbios_name()));
3301 if ( (count
= ads_count_replies(ads
, res
)) != 1 ) {
3302 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count
));
3306 if ( (name
= ads_pull_string(ads
, ctx
, res
, "dNSHostName")) == NULL
) {
3307 DEBUG(0,("ads_get_dnshostname: No dNSHostName attribute!\n"));
3311 ads_msgfree(ads
, res
);
3316 /********************************************************************
3317 ********************************************************************/
3319 char* ads_get_upn( ADS_STRUCT
*ads
, TALLOC_CTX
*ctx
, const char *machine_name
)
3321 LDAPMessage
*res
= NULL
;
3326 status
= ads_find_machine_acct(ads
, &res
, machine_name
);
3327 if (!ADS_ERR_OK(status
)) {
3328 DEBUG(0,("ads_get_upn: Failed to find account for %s\n",
3329 lp_netbios_name()));
3333 if ( (count
= ads_count_replies(ads
, res
)) != 1 ) {
3334 DEBUG(1,("ads_get_upn: %d entries returned!\n", count
));
3338 if ( (name
= ads_pull_string(ads
, ctx
, res
, "userPrincipalName")) == NULL
) {
3339 DEBUG(2,("ads_get_upn: No userPrincipalName attribute!\n"));
3343 ads_msgfree(ads
, res
);
3348 /********************************************************************
3349 ********************************************************************/
3351 char* ads_get_samaccountname( ADS_STRUCT
*ads
, TALLOC_CTX
*ctx
, const char *machine_name
)
3353 LDAPMessage
*res
= NULL
;
3358 status
= ads_find_machine_acct(ads
, &res
, lp_netbios_name());
3359 if (!ADS_ERR_OK(status
)) {
3360 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
3361 lp_netbios_name()));
3365 if ( (count
= ads_count_replies(ads
, res
)) != 1 ) {
3366 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count
));
3370 if ( (name
= ads_pull_string(ads
, ctx
, res
, "sAMAccountName")) == NULL
) {
3371 DEBUG(0,("ads_get_dnshostname: No sAMAccountName attribute!\n"));
3375 ads_msgfree(ads
, res
);
3382 SAVED CODE
- we used to join via ldap
- remember how we did
this. JRA
.
3385 * Join a machine to a realm
3386 * Creates the machine account and sets the machine password
3387 * @param ads connection to ads server
3388 * @param machine name of host to add
3389 * @param org_unit Organizational unit to place machine in
3390 * @return status of join
3392 ADS_STATUS
ads_join_realm(ADS_STRUCT
*ads
, const char *machine_name
,
3393 uint32 account_type
, const char *org_unit
)
3396 LDAPMessage
*res
= NULL
;
3399 /* machine name must be lowercase */
3400 machine
= SMB_STRDUP(machine_name
);
3401 strlower_m(machine
);
3404 status = ads_find_machine_acct(ads, (void **)&res, machine);
3405 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
3406 DEBUG(0, ("Host account for %s already exists - deleting old account\n", machine));
3407 status = ads_leave_realm(ads, machine);
3408 if (!ADS_ERR_OK(status)) {
3409 DEBUG(0, ("Failed to delete host '%s' from the '%s' realm.\n",
3410 machine, ads->config.realm));
3415 status
= ads_add_machine_acct(ads
, machine
, account_type
, org_unit
);
3416 if (!ADS_ERR_OK(status
)) {
3417 DEBUG(0, ("ads_join_realm: ads_add_machine_acct failed (%s): %s\n", machine
, ads_errstr(status
)));
3422 status
= ads_find_machine_acct(ads
, (void **)(void *)&res
, machine
);
3423 if (!ADS_ERR_OK(status
)) {
3424 DEBUG(0, ("ads_join_realm: Host account test failed for machine %s\n", machine
));
3430 ads_msgfree(ads
, res
);
3437 * Delete a machine from the realm
3438 * @param ads connection to ads server
3439 * @param hostname Machine to remove
3440 * @return status of delete
3442 ADS_STATUS
ads_leave_realm(ADS_STRUCT
*ads
, const char *hostname
)
3447 char *hostnameDN
, *host
;
3449 LDAPControl ldap_control
;
3450 LDAPControl
* pldap_control
[2] = {NULL
, NULL
};
3452 pldap_control
[0] = &ldap_control
;
3453 memset(&ldap_control
, 0, sizeof(LDAPControl
));
3454 ldap_control
.ldctl_oid
= discard_const_p(char, LDAP_SERVER_TREE_DELETE_OID
);
3456 /* hostname must be lowercase */
3457 host
= SMB_STRDUP(hostname
);
3460 status
= ads_find_machine_acct(ads
, &res
, host
);
3461 if (!ADS_ERR_OK(status
)) {
3462 DEBUG(0, ("Host account for %s does not exist.\n", host
));
3467 msg
= ads_first_entry(ads
, res
);
3470 return ADS_ERROR_SYSTEM(ENOENT
);
3473 hostnameDN
= ads_get_dn(ads
, talloc_tos(), (LDAPMessage
*)msg
);
3474 if (hostnameDN
== NULL
) {
3476 return ADS_ERROR_SYSTEM(ENOENT
);
3479 rc
= ldap_delete_ext_s(ads
->ldap
.ld
, hostnameDN
, pldap_control
, NULL
);
3481 DEBUG(3,("ldap_delete_ext_s failed with error code %d\n", rc
));
3483 DEBUG(3,("ldap_delete_ext_s succeeded with error code %d\n", rc
));
3486 if (rc
!= LDAP_SUCCESS
) {
3487 const char *attrs
[] = { "cn", NULL
};
3488 LDAPMessage
*msg_sub
;
3490 /* we only search with scope ONE, we do not expect any further
3491 * objects to be created deeper */
3493 status
= ads_do_search_retry(ads
, hostnameDN
,
3494 LDAP_SCOPE_ONELEVEL
,
3495 "(objectclass=*)", attrs
, &res
);
3497 if (!ADS_ERR_OK(status
)) {
3499 TALLOC_FREE(hostnameDN
);
3503 for (msg_sub
= ads_first_entry(ads
, res
); msg_sub
;
3504 msg_sub
= ads_next_entry(ads
, msg_sub
)) {
3508 if ((dn
= ads_get_dn(ads
, talloc_tos(), msg_sub
)) == NULL
) {
3510 TALLOC_FREE(hostnameDN
);
3511 return ADS_ERROR(LDAP_NO_MEMORY
);
3514 status
= ads_del_dn(ads
, dn
);
3515 if (!ADS_ERR_OK(status
)) {
3516 DEBUG(3,("failed to delete dn %s: %s\n", dn
, ads_errstr(status
)));
3519 TALLOC_FREE(hostnameDN
);
3526 /* there should be no subordinate objects anymore */
3527 status
= ads_do_search_retry(ads
, hostnameDN
,
3528 LDAP_SCOPE_ONELEVEL
,
3529 "(objectclass=*)", attrs
, &res
);
3531 if (!ADS_ERR_OK(status
) || ( (ads_count_replies(ads
, res
)) > 0 ) ) {
3533 TALLOC_FREE(hostnameDN
);
3537 /* delete hostnameDN now */
3538 status
= ads_del_dn(ads
, hostnameDN
);
3539 if (!ADS_ERR_OK(status
)) {
3541 DEBUG(3,("failed to delete dn %s: %s\n", hostnameDN
, ads_errstr(status
)));
3542 TALLOC_FREE(hostnameDN
);
3547 TALLOC_FREE(hostnameDN
);
3549 status
= ads_find_machine_acct(ads
, &res
, host
);
3550 if (ADS_ERR_OK(status
) && ads_count_replies(ads
, res
) == 1) {
3551 DEBUG(3, ("Failed to remove host account.\n"));
3561 * pull all token-sids from an LDAP dn
3562 * @param ads connection to ads server
3563 * @param mem_ctx TALLOC_CTX for allocating sid array
3564 * @param dn of LDAP object
3565 * @param user_sid pointer to struct dom_sid (objectSid)
3566 * @param primary_group_sid pointer to struct dom_sid (self composed)
3567 * @param sids pointer to sid array to allocate
3568 * @param num_sids counter of SIDs pulled
3569 * @return status of token query
3571 ADS_STATUS
ads_get_tokensids(ADS_STRUCT
*ads
,
3572 TALLOC_CTX
*mem_ctx
,
3574 struct dom_sid
*user_sid
,
3575 struct dom_sid
*primary_group_sid
,
3576 struct dom_sid
**sids
,
3580 LDAPMessage
*res
= NULL
;
3582 size_t tmp_num_sids
;
3583 struct dom_sid
*tmp_sids
;
3584 struct dom_sid tmp_user_sid
;
3585 struct dom_sid tmp_primary_group_sid
;
3587 const char *attrs
[] = {
3594 status
= ads_search_retry_dn(ads
, &res
, dn
, attrs
);
3595 if (!ADS_ERR_OK(status
)) {
3599 count
= ads_count_replies(ads
, res
);
3601 ads_msgfree(ads
, res
);
3602 return ADS_ERROR_LDAP(LDAP_NO_SUCH_OBJECT
);
3605 if (!ads_pull_sid(ads
, res
, "objectSid", &tmp_user_sid
)) {
3606 ads_msgfree(ads
, res
);
3607 return ADS_ERROR_LDAP(LDAP_NO_MEMORY
);
3610 if (!ads_pull_uint32(ads
, res
, "primaryGroupID", &pgid
)) {
3611 ads_msgfree(ads
, res
);
3612 return ADS_ERROR_LDAP(LDAP_NO_MEMORY
);
3616 /* hack to compose the primary group sid without knowing the
3619 struct dom_sid domsid
;
3621 sid_copy(&domsid
, &tmp_user_sid
);
3623 if (!sid_split_rid(&domsid
, NULL
)) {
3624 ads_msgfree(ads
, res
);
3625 return ADS_ERROR_LDAP(LDAP_NO_MEMORY
);
3628 if (!sid_compose(&tmp_primary_group_sid
, &domsid
, pgid
)) {
3629 ads_msgfree(ads
, res
);
3630 return ADS_ERROR_LDAP(LDAP_NO_MEMORY
);
3634 tmp_num_sids
= ads_pull_sids(ads
, mem_ctx
, res
, "tokenGroups", &tmp_sids
);
3636 if (tmp_num_sids
== 0 || !tmp_sids
) {
3637 ads_msgfree(ads
, res
);
3638 return ADS_ERROR_LDAP(LDAP_NO_MEMORY
);
3642 *num_sids
= tmp_num_sids
;
3650 *user_sid
= tmp_user_sid
;
3653 if (primary_group_sid
) {
3654 *primary_group_sid
= tmp_primary_group_sid
;
3657 DEBUG(10,("ads_get_tokensids: returned %d sids\n", (int)tmp_num_sids
+ 2));
3659 ads_msgfree(ads
, res
);
3660 return ADS_ERROR_LDAP(LDAP_SUCCESS
);
3664 * Find a sAMAccoutName in LDAP
3665 * @param ads connection to ads server
3666 * @param mem_ctx TALLOC_CTX for allocating sid array
3667 * @param samaccountname to search
3668 * @param uac_ret uint32 pointer userAccountControl attribute value
3669 * @param dn_ret pointer to dn
3670 * @return status of token query
3672 ADS_STATUS
ads_find_samaccount(ADS_STRUCT
*ads
,
3673 TALLOC_CTX
*mem_ctx
,
3674 const char *samaccountname
,
3676 const char **dn_ret
)
3679 const char *attrs
[] = { "userAccountControl", NULL
};
3681 LDAPMessage
*res
= NULL
;
3685 filter
= talloc_asprintf(mem_ctx
, "(&(objectclass=user)(sAMAccountName=%s))",
3687 if (filter
== NULL
) {
3688 status
= ADS_ERROR_NT(NT_STATUS_NO_MEMORY
);
3692 status
= ads_do_search_all(ads
, ads
->config
.bind_path
,
3694 filter
, attrs
, &res
);
3696 if (!ADS_ERR_OK(status
)) {
3700 if (ads_count_replies(ads
, res
) != 1) {
3701 status
= ADS_ERROR(LDAP_NO_RESULTS_RETURNED
);
3705 dn
= ads_get_dn(ads
, talloc_tos(), res
);
3707 status
= ADS_ERROR(LDAP_NO_MEMORY
);
3711 if (!ads_pull_uint32(ads
, res
, "userAccountControl", &uac
)) {
3712 status
= ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE
);
3721 *dn_ret
= talloc_strdup(mem_ctx
, dn
);
3723 status
= ADS_ERROR(LDAP_NO_MEMORY
);
3729 ads_msgfree(ads
, res
);
3735 * find our configuration path
3736 * @param ads connection to ads server
3737 * @param mem_ctx Pointer to talloc context
3738 * @param config_path Pointer to the config path
3739 * @return status of search
3741 ADS_STATUS
ads_config_path(ADS_STRUCT
*ads
,
3742 TALLOC_CTX
*mem_ctx
,
3746 LDAPMessage
*res
= NULL
;
3747 const char *config_context
= NULL
;
3748 const char *attrs
[] = { "configurationNamingContext", NULL
};
3750 status
= ads_do_search(ads
, "", LDAP_SCOPE_BASE
,
3751 "(objectclass=*)", attrs
, &res
);
3752 if (!ADS_ERR_OK(status
)) {
3756 config_context
= ads_pull_string(ads
, mem_ctx
, res
,
3757 "configurationNamingContext");
3758 ads_msgfree(ads
, res
);
3759 if (!config_context
) {
3760 return ADS_ERROR(LDAP_NO_MEMORY
);
3764 *config_path
= talloc_strdup(mem_ctx
, config_context
);
3765 if (!*config_path
) {
3766 return ADS_ERROR(LDAP_NO_MEMORY
);
3770 return ADS_ERROR(LDAP_SUCCESS
);
3774 * find the displayName of an extended right
3775 * @param ads connection to ads server
3776 * @param config_path The config path
3777 * @param mem_ctx Pointer to talloc context
3778 * @param GUID struct of the rightsGUID
3779 * @return status of search
3781 const char *ads_get_extended_right_name_by_guid(ADS_STRUCT
*ads
,
3782 const char *config_path
,
3783 TALLOC_CTX
*mem_ctx
,
3784 const struct GUID
*rights_guid
)
3787 LDAPMessage
*res
= NULL
;
3789 const char *attrs
[] = { "displayName", NULL
};
3790 const char *result
= NULL
;
3793 if (!ads
|| !mem_ctx
|| !rights_guid
) {
3797 expr
= talloc_asprintf(mem_ctx
, "(rightsGuid=%s)",
3798 GUID_string(mem_ctx
, rights_guid
));
3803 path
= talloc_asprintf(mem_ctx
, "cn=Extended-Rights,%s", config_path
);
3808 rc
= ads_do_search_retry(ads
, path
, LDAP_SCOPE_SUBTREE
,
3810 if (!ADS_ERR_OK(rc
)) {
3814 if (ads_count_replies(ads
, res
) != 1) {
3818 result
= ads_pull_string(ads
, mem_ctx
, res
, "displayName");
3821 ads_msgfree(ads
, res
);
3826 * verify or build and verify an account ou
3827 * @param mem_ctx Pointer to talloc context
3828 * @param ads connection to ads server
3830 * @return status of search
3833 ADS_STATUS
ads_check_ou_dn(TALLOC_CTX
*mem_ctx
,
3835 const char **account_ou
)
3841 exploded_dn
= ldap_explode_dn(*account_ou
, 0);
3843 ldap_value_free(exploded_dn
);
3847 ou_string
= ads_ou_string(ads
, *account_ou
);
3849 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX
);
3852 name
= talloc_asprintf(mem_ctx
, "%s,%s", ou_string
,
3853 ads
->config
.bind_path
);
3854 SAFE_FREE(ou_string
);
3857 return ADS_ERROR_LDAP(LDAP_NO_MEMORY
);
3860 exploded_dn
= ldap_explode_dn(name
, 0);
3862 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX
);
3864 ldap_value_free(exploded_dn
);