2 Unix SMB/CIFS implementation.
3 ads (active directory) utility library
4 Copyright (C) Andrew Tridgell 2001
5 Copyright (C) Remus Koos 2001
6 Copyright (C) Jim McDonough <jmcd@us.ibm.com> 2002
7 Copyright (C) Guenther Deschner 2005
8 Copyright (C) Gerald Carter 2006
10 This program is free software; you can redistribute it and/or modify
11 it under the terms of the GNU General Public License as published by
12 the Free Software Foundation; either version 3 of the License, or
13 (at your option) any later version.
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License for more details.
20 You should have received a copy of the GNU General Public License
21 along with this program. If not, see <http://www.gnu.org/licenses/>.
26 #include "libads/sitename_cache.h"
27 #include "libads/cldap.h"
28 #include "../lib/addns/dnsquery.h"
29 #include "../libds/common/flags.h"
31 #include "../libcli/security/security.h"
32 #include "../librpc/gen_ndr/netlogon.h"
33 #include "lib/param/loadparm.h"
34 #include "libsmb/namequery.h"
40 * @brief basic ldap client-side routines for ads server communications
42 * The routines contained here should do the necessary ldap calls for
45 * Important note: attribute names passed into ads_ routines must
46 * already be in UTF-8 format. We do not convert them because in almost
47 * all cases, they are just ascii (which is represented with the same
48 * codepoints in UTF-8). This may have to change at some point
52 #define LDAP_SERVER_TREE_DELETE_OID "1.2.840.113556.1.4.805"
54 static SIG_ATOMIC_T gotalarm
;
56 /***************************************************************
57 Signal function to tell us we timed out.
58 ****************************************************************/
60 static void gotalarm_sig(int signum
)
65 LDAP
*ldap_open_with_timeout(const char *server
,
66 struct sockaddr_storage
*ss
,
67 int port
, unsigned int to
)
73 DEBUG(10, ("Opening connection to LDAP server '%s:%d', timeout "
74 "%u seconds\n", server
, port
, to
));
79 CatchSignal(SIGALRM
, gotalarm_sig
);
81 /* End setup timeout. */
84 if ( strchr_m(server
, ':') ) {
86 uri
= talloc_asprintf(talloc_tos(), "ldap://[%s]:%u", server
, port
);
89 uri
= talloc_asprintf(talloc_tos(), "ldap://%s:%u", server
, port
);
95 #ifdef HAVE_LDAP_INIT_FD
98 NTSTATUS status
= NT_STATUS_UNSUCCESSFUL
;
99 unsigned timeout_ms
= 1000 * to
;
101 status
= open_socket_out(ss
, port
, timeout_ms
, &fd
);
102 if (!NT_STATUS_IS_OK(status
)) {
103 DEBUG(3, ("open_socket_out: failed to open socket\n"));
107 /* define LDAP_PROTO_TCP from openldap.h if required */
108 #ifndef LDAP_PROTO_TCP
109 #define LDAP_PROTO_TCP 1
111 ldap_err
= ldap_init_fd(fd
, LDAP_PROTO_TCP
, uri
, &ldp
);
113 #elif defined(HAVE_LDAP_INITIALIZE)
114 ldap_err
= ldap_initialize(&ldp
, uri
);
116 ldp
= ldap_open(server
, port
);
118 ldap_err
= LDAP_SUCCESS
;
120 ldap_err
= LDAP_OTHER
;
123 if (ldap_err
!= LDAP_SUCCESS
) {
124 DEBUG(2,("Could not initialize connection for LDAP server '%s': %s\n",
125 uri
, ldap_err2string(ldap_err
)));
127 DEBUG(10, ("Initialized connection for LDAP server '%s'\n", uri
));
131 /* Teardown timeout. */
133 CatchSignal(SIGALRM
, SIG_IGN
);
139 static int ldap_search_with_timeout(LDAP
*ld
,
140 LDAP_CONST
char *base
,
142 LDAP_CONST
char *filter
,
145 LDAPControl
**sctrls
,
146 LDAPControl
**cctrls
,
150 int to
= lp_ldap_timeout();
151 struct timeval timeout
;
152 struct timeval
*timeout_ptr
= NULL
;
155 /* Setup timeout for the ldap_search_ext_s call - local and remote. */
161 timeout_ptr
= &timeout
;
163 /* Setup alarm timeout. */
164 CatchSignal(SIGALRM
, gotalarm_sig
);
165 /* Make the alarm time one second beyond
166 the timout we're setting for the
167 remote search timeout, to allow that
168 to fire in preference. */
170 /* End setup timeout. */
174 result
= ldap_search_ext_s(ld
, base
, scope
, filter
, attrs
,
175 attrsonly
, sctrls
, cctrls
, timeout_ptr
,
179 /* Teardown alarm timeout. */
180 CatchSignal(SIGALRM
, SIG_IGN
);
185 return LDAP_TIMELIMIT_EXCEEDED
;
188 * A bug in OpenLDAP means ldap_search_ext_s can return
189 * LDAP_SUCCESS but with a NULL res pointer. Cope with
190 * this. See bug #6279 for details. JRA.
194 return LDAP_TIMELIMIT_EXCEEDED
;
200 /**********************************************
201 Do client and server sitename match ?
202 **********************************************/
204 bool ads_sitename_match(ADS_STRUCT
*ads
)
206 if (ads
->config
.server_site_name
== NULL
&&
207 ads
->config
.client_site_name
== NULL
) {
208 DEBUG(10,("ads_sitename_match: both null\n"));
211 if (ads
->config
.server_site_name
&&
212 ads
->config
.client_site_name
&&
213 strequal(ads
->config
.server_site_name
,
214 ads
->config
.client_site_name
)) {
215 DEBUG(10,("ads_sitename_match: name %s match\n", ads
->config
.server_site_name
));
218 DEBUG(10,("ads_sitename_match: no match between server: %s and client: %s\n",
219 ads
->config
.server_site_name
? ads
->config
.server_site_name
: "NULL",
220 ads
->config
.client_site_name
? ads
->config
.client_site_name
: "NULL"));
224 /**********************************************
225 Is this the closest DC ?
226 **********************************************/
228 bool ads_closest_dc(ADS_STRUCT
*ads
)
230 if (ads
->config
.flags
& NBT_SERVER_CLOSEST
) {
231 DEBUG(10,("ads_closest_dc: NBT_SERVER_CLOSEST flag set\n"));
235 /* not sure if this can ever happen */
236 if (ads_sitename_match(ads
)) {
237 DEBUG(10,("ads_closest_dc: NBT_SERVER_CLOSEST flag not set but sites match\n"));
241 if (ads
->config
.client_site_name
== NULL
) {
242 DEBUG(10,("ads_closest_dc: client belongs to no site\n"));
246 DEBUG(10,("ads_closest_dc: %s is not the closest DC\n",
247 ads
->config
.ldap_server_name
));
254 try a connection to a given ldap server, returning True and setting the servers IP
255 in the ads struct if successful
257 static bool ads_try_connect(ADS_STRUCT
*ads
, bool gc
,
258 struct sockaddr_storage
*ss
)
260 struct NETLOGON_SAM_LOGON_RESPONSE_EX cldap_reply
;
261 TALLOC_CTX
*frame
= talloc_stackframe();
263 char addr
[INET6_ADDRSTRLEN
];
270 print_sockaddr(addr
, sizeof(addr
), ss
);
272 DEBUG(5,("ads_try_connect: sending CLDAP request to %s (realm: %s)\n",
273 addr
, ads
->server
.realm
));
275 ZERO_STRUCT( cldap_reply
);
277 if ( !ads_cldap_netlogon_5(frame
, ss
, ads
->server
.realm
, &cldap_reply
) ) {
278 DEBUG(3,("ads_try_connect: CLDAP request %s failed.\n", addr
));
283 /* Check the CLDAP reply flags */
285 if ( !(cldap_reply
.server_type
& NBT_SERVER_LDAP
) ) {
286 DEBUG(1,("ads_try_connect: %s's CLDAP reply says it is not an LDAP server!\n",
292 /* Fill in the ads->config values */
294 SAFE_FREE(ads
->config
.realm
);
295 SAFE_FREE(ads
->config
.bind_path
);
296 SAFE_FREE(ads
->config
.ldap_server_name
);
297 SAFE_FREE(ads
->config
.server_site_name
);
298 SAFE_FREE(ads
->config
.client_site_name
);
299 SAFE_FREE(ads
->server
.workgroup
);
301 if (!check_cldap_reply_required_flags(cldap_reply
.server_type
,
302 ads
->config
.flags
)) {
307 ads
->config
.ldap_server_name
= SMB_STRDUP(cldap_reply
.pdc_dns_name
);
308 ads
->config
.realm
= SMB_STRDUP(cldap_reply
.dns_domain
);
309 if (!strupper_m(ads
->config
.realm
)) {
314 ads
->config
.bind_path
= ads_build_dn(ads
->config
.realm
);
315 if (*cldap_reply
.server_site
) {
316 ads
->config
.server_site_name
=
317 SMB_STRDUP(cldap_reply
.server_site
);
319 if (*cldap_reply
.client_site
) {
320 ads
->config
.client_site_name
=
321 SMB_STRDUP(cldap_reply
.client_site
);
323 ads
->server
.workgroup
= SMB_STRDUP(cldap_reply
.domain_name
);
325 ads
->ldap
.port
= gc
? LDAP_GC_PORT
: LDAP_PORT
;
328 /* Store our site name. */
329 sitename_store( cldap_reply
.domain_name
, cldap_reply
.client_site
);
330 sitename_store( cldap_reply
.dns_domain
, cldap_reply
.client_site
);
332 /* Leave this until last so that the flags are not clobbered */
333 ads
->config
.flags
= cldap_reply
.server_type
;
343 /**********************************************************************
344 send a cldap ping to list of servers, one at a time, until one of
345 them answers it's an ldap server. Record success in the ADS_STRUCT.
346 Take note of and update negative connection cache.
347 **********************************************************************/
349 static NTSTATUS
cldap_ping_list(ADS_STRUCT
*ads
,const char *domain
,
350 struct ip_service
*ip_list
, int count
)
355 for (i
= 0; i
< count
; i
++) {
356 char server
[INET6_ADDRSTRLEN
];
358 print_sockaddr(server
, sizeof(server
), &ip_list
[i
].ss
);
360 if (!NT_STATUS_IS_OK(
361 check_negative_conn_cache(domain
, server
)))
364 /* Returns ok only if it matches the correct server type */
365 ok
= ads_try_connect(ads
, false, &ip_list
[i
].ss
);
371 /* keep track of failures */
372 add_failed_connection_entry(domain
, server
,
373 NT_STATUS_UNSUCCESSFUL
);
376 return NT_STATUS_NO_LOGON_SERVERS
;
379 /***************************************************************************
380 resolve a name and perform an "ldap ping" using NetBIOS and related methods
381 ****************************************************************************/
383 static NTSTATUS
resolve_and_ping_netbios(ADS_STRUCT
*ads
,
384 const char *domain
, const char *realm
)
387 struct ip_service
*ip_list
;
390 DEBUG(6, ("resolve_and_ping_netbios: (cldap) looking for domain '%s'\n",
393 status
= get_sorted_dc_list(domain
, NULL
, &ip_list
, &count
,
395 if (!NT_STATUS_IS_OK(status
)) {
399 /* remove servers which are known to be dead based on
400 the corresponding DNS method */
402 for (i
= 0; i
< count
; ++i
) {
403 char server
[INET6_ADDRSTRLEN
];
405 print_sockaddr(server
, sizeof(server
), &ip_list
[i
].ss
);
408 check_negative_conn_cache(realm
, server
))) {
409 /* Ensure we add the workgroup name for this
410 IP address as negative too. */
411 add_failed_connection_entry(
413 NT_STATUS_UNSUCCESSFUL
);
418 status
= cldap_ping_list(ads
, domain
, ip_list
, count
);
426 /**********************************************************************
427 resolve a name and perform an "ldap ping" using DNS
428 **********************************************************************/
430 static NTSTATUS
resolve_and_ping_dns(ADS_STRUCT
*ads
, const char *sitename
,
434 struct ip_service
*ip_list
= NULL
;
437 DEBUG(6, ("resolve_and_ping_dns: (cldap) looking for realm '%s'\n",
440 status
= get_sorted_dc_list(realm
, sitename
, &ip_list
, &count
,
442 if (!NT_STATUS_IS_OK(status
)) {
447 status
= cldap_ping_list(ads
, realm
, ip_list
, count
);
454 /**********************************************************************
455 Try to find an AD dc using our internal name resolution routines
456 Try the realm first and then then workgroup name if netbios is not
458 **********************************************************************/
460 static NTSTATUS
ads_find_dc(ADS_STRUCT
*ads
)
462 const char *c_domain
= "";
464 bool use_own_domain
= False
;
465 char *sitename
= NULL
;
466 NTSTATUS status
= NT_STATUS_UNSUCCESSFUL
;
469 /* if the realm and workgroup are both empty, assume they are ours */
472 c_realm
= ads
->server
.realm
;
478 /* special case where no realm and no workgroup means our own */
479 if ( !ads
->server
.workgroup
|| !*ads
->server
.workgroup
) {
480 use_own_domain
= True
;
481 c_realm
= lp_realm();
485 if (!lp_disable_netbios()) {
486 if (use_own_domain
) {
487 c_domain
= lp_workgroup();
489 c_domain
= ads
->server
.workgroup
;
490 if (!*c_realm
&& (!c_domain
|| !*c_domain
)) {
491 c_domain
= lp_workgroup();
500 if (!*c_realm
&& !*c_domain
) {
501 DEBUG(0, ("ads_find_dc: no realm or workgroup! Don't know "
503 return NT_STATUS_INVALID_PARAMETER
; /* rather need MISSING_PARAMETER ... */
507 * In case of LDAP we use get_dc_name() as that
508 * creates the custom krb5.conf file
510 if (!(ads
->auth
.flags
& ADS_AUTH_NO_BIND
)) {
512 struct sockaddr_storage ip_out
;
514 DEBUG(6, ("ads_find_dc: (ldap) looking for realm '%s'"
515 " and falling back to domain '%s'\n",
518 ok
= get_dc_name(c_domain
, c_realm
, srv_name
, &ip_out
);
521 * we call ads_try_connect() to fill in the
522 * ads->config details
524 ok
= ads_try_connect(ads
, false, &ip_out
);
530 return NT_STATUS_NO_LOGON_SERVERS
;
534 sitename
= sitename_fetch(talloc_tos(), c_realm
);
535 status
= resolve_and_ping_dns(ads
, sitename
, c_realm
);
537 if (NT_STATUS_IS_OK(status
)) {
538 TALLOC_FREE(sitename
);
542 /* In case we failed to contact one of our closest DC on our
544 * need to try to find another DC, retry with a site-less SRV
549 DEBUG(3, ("ads_find_dc: failed to find a valid DC on "
550 "our site (%s), Trying to find another DC "
551 "for realm '%s' (domain '%s')\n",
552 sitename
, c_realm
, c_domain
));
553 namecache_delete(c_realm
, 0x1C);
555 resolve_and_ping_dns(ads
, NULL
, c_realm
);
557 if (NT_STATUS_IS_OK(status
)) {
558 TALLOC_FREE(sitename
);
563 TALLOC_FREE(sitename
);
566 /* try netbios as fallback - if permitted,
567 or if configuration specifically requests it */
570 DEBUG(3, ("ads_find_dc: falling back to netbios "
571 "name resolution for domain '%s' (realm '%s')\n",
575 status
= resolve_and_ping_netbios(ads
, c_domain
, c_realm
);
576 if (NT_STATUS_IS_OK(status
)) {
581 DEBUG(1, ("ads_find_dc: "
582 "name resolution for realm '%s' (domain '%s') failed: %s\n",
583 c_realm
, c_domain
, nt_errstr(status
)));
587 * Connect to the LDAP server
588 * @param ads Pointer to an existing ADS_STRUCT
589 * @return status of connection
591 ADS_STATUS
ads_connect(ADS_STRUCT
*ads
)
593 int version
= LDAP_VERSION3
;
596 char addr
[INET6_ADDRSTRLEN
];
598 ZERO_STRUCT(ads
->ldap
);
599 ZERO_STRUCT(ads
->ldap_wrap_data
);
600 ads
->ldap
.last_attempt
= time_mono(NULL
);
601 ads
->ldap_wrap_data
.wrap_type
= ADS_SASLWRAP_TYPE_PLAIN
;
603 /* try with a user specified server */
605 if (DEBUGLEVEL
>= 11) {
606 char *s
= NDR_PRINT_STRUCT_STRING(talloc_tos(), ads_struct
, ads
);
607 DEBUG(11,("ads_connect: entering\n"));
608 DEBUGADD(11,("%s\n", s
));
612 if (ads
->server
.ldap_server
) {
614 struct sockaddr_storage ss
;
616 ok
= resolve_name(ads
->server
.ldap_server
, &ss
, 0x20, true);
618 DEBUG(5,("ads_connect: unable to resolve name %s\n",
619 ads
->server
.ldap_server
));
620 status
= ADS_ERROR_NT(NT_STATUS_NOT_FOUND
);
623 ok
= ads_try_connect(ads
, ads
->server
.gc
, &ss
);
628 /* The choice of which GC use is handled one level up in
629 ads_connect_gc(). If we continue on from here with
630 ads_find_dc() we will get GC searches on port 389 which
631 doesn't work. --jerry */
633 if (ads
->server
.gc
== true) {
634 return ADS_ERROR(LDAP_OPERATIONS_ERROR
);
637 if (ads
->server
.no_fallback
) {
638 status
= ADS_ERROR_NT(NT_STATUS_NOT_FOUND
);
643 ntstatus
= ads_find_dc(ads
);
644 if (NT_STATUS_IS_OK(ntstatus
)) {
648 status
= ADS_ERROR_NT(ntstatus
);
653 print_sockaddr(addr
, sizeof(addr
), &ads
->ldap
.ss
);
654 DEBUG(3,("Successfully contacted LDAP server %s\n", addr
));
656 if (!ads
->auth
.user_name
) {
657 /* Must use the userPrincipalName value here or sAMAccountName
658 and not servicePrincipalName; found by Guenther Deschner */
660 if (asprintf(&ads
->auth
.user_name
, "%s$", lp_netbios_name() ) == -1) {
661 DEBUG(0,("ads_connect: asprintf fail.\n"));
662 ads
->auth
.user_name
= NULL
;
666 if (!ads
->auth
.realm
) {
667 ads
->auth
.realm
= SMB_STRDUP(ads
->config
.realm
);
670 if (!ads
->auth
.kdc_server
) {
671 print_sockaddr(addr
, sizeof(addr
), &ads
->ldap
.ss
);
672 ads
->auth
.kdc_server
= SMB_STRDUP(addr
);
675 /* If the caller() requested no LDAP bind, then we are done */
677 if (ads
->auth
.flags
& ADS_AUTH_NO_BIND
) {
678 status
= ADS_SUCCESS
;
682 ads
->ldap_wrap_data
.mem_ctx
= talloc_init("ads LDAP connection memory");
683 if (!ads
->ldap_wrap_data
.mem_ctx
) {
684 status
= ADS_ERROR_NT(NT_STATUS_NO_MEMORY
);
688 /* Otherwise setup the TCP LDAP session */
690 ads
->ldap
.ld
= ldap_open_with_timeout(ads
->config
.ldap_server_name
,
692 ads
->ldap
.port
, lp_ldap_timeout());
693 if (ads
->ldap
.ld
== NULL
) {
694 status
= ADS_ERROR(LDAP_OPERATIONS_ERROR
);
697 DEBUG(3,("Connected to LDAP server %s\n", ads
->config
.ldap_server_name
));
699 /* cache the successful connection for workgroup and realm */
700 if (ads_closest_dc(ads
)) {
701 saf_store( ads
->server
.workgroup
, ads
->config
.ldap_server_name
);
702 saf_store( ads
->server
.realm
, ads
->config
.ldap_server_name
);
705 ldap_set_option(ads
->ldap
.ld
, LDAP_OPT_PROTOCOL_VERSION
, &version
);
707 /* fill in the current time and offsets */
709 status
= ads_current_time( ads
);
710 if ( !ADS_ERR_OK(status
) ) {
714 /* Now do the bind */
716 if (ads
->auth
.flags
& ADS_AUTH_ANON_BIND
) {
717 status
= ADS_ERROR(ldap_simple_bind_s(ads
->ldap
.ld
, NULL
, NULL
));
721 if (ads
->auth
.flags
& ADS_AUTH_SIMPLE_BIND
) {
722 status
= ADS_ERROR(ldap_simple_bind_s(ads
->ldap
.ld
, ads
->auth
.user_name
, ads
->auth
.password
));
726 status
= ads_sasl_bind(ads
);
729 if (DEBUGLEVEL
>= 11) {
730 char *s
= NDR_PRINT_STRUCT_STRING(talloc_tos(), ads_struct
, ads
);
731 DEBUG(11,("ads_connect: leaving with: %s\n",
732 ads_errstr(status
)));
733 DEBUGADD(11,("%s\n", s
));
741 * Connect to the LDAP server using given credentials
742 * @param ads Pointer to an existing ADS_STRUCT
743 * @return status of connection
745 ADS_STATUS
ads_connect_user_creds(ADS_STRUCT
*ads
)
747 ads
->auth
.flags
|= ADS_AUTH_USER_CREDS
;
749 return ads_connect(ads
);
753 * Disconnect the LDAP server
754 * @param ads Pointer to an existing ADS_STRUCT
756 void ads_disconnect(ADS_STRUCT
*ads
)
759 ldap_unbind(ads
->ldap
.ld
);
762 if (ads
->ldap_wrap_data
.wrap_ops
&&
763 ads
->ldap_wrap_data
.wrap_ops
->disconnect
) {
764 ads
->ldap_wrap_data
.wrap_ops
->disconnect(&ads
->ldap_wrap_data
);
766 if (ads
->ldap_wrap_data
.mem_ctx
) {
767 talloc_free(ads
->ldap_wrap_data
.mem_ctx
);
769 ZERO_STRUCT(ads
->ldap
);
770 ZERO_STRUCT(ads
->ldap_wrap_data
);
774 Duplicate a struct berval into talloc'ed memory
776 static struct berval
*dup_berval(TALLOC_CTX
*ctx
, const struct berval
*in_val
)
778 struct berval
*value
;
780 if (!in_val
) return NULL
;
782 value
= talloc_zero(ctx
, struct berval
);
785 if (in_val
->bv_len
== 0) return value
;
787 value
->bv_len
= in_val
->bv_len
;
788 value
->bv_val
= (char *)talloc_memdup(ctx
, in_val
->bv_val
,
794 Make a values list out of an array of (struct berval *)
796 static struct berval
**ads_dup_values(TALLOC_CTX
*ctx
,
797 const struct berval
**in_vals
)
799 struct berval
**values
;
802 if (!in_vals
) return NULL
;
803 for (i
=0; in_vals
[i
]; i
++)
805 values
= talloc_zero_array(ctx
, struct berval
*, i
+1);
806 if (!values
) return NULL
;
808 for (i
=0; in_vals
[i
]; i
++) {
809 values
[i
] = dup_berval(ctx
, in_vals
[i
]);
815 UTF8-encode a values list out of an array of (char *)
817 static char **ads_push_strvals(TALLOC_CTX
*ctx
, const char **in_vals
)
823 if (!in_vals
) return NULL
;
824 for (i
=0; in_vals
[i
]; i
++)
826 values
= talloc_zero_array(ctx
, char *, i
+1);
827 if (!values
) return NULL
;
829 for (i
=0; in_vals
[i
]; i
++) {
830 if (!push_utf8_talloc(ctx
, &values
[i
], in_vals
[i
], &size
)) {
839 Pull a (char *) array out of a UTF8-encoded values list
841 static char **ads_pull_strvals(TALLOC_CTX
*ctx
, const char **in_vals
)
845 size_t converted_size
;
847 if (!in_vals
) return NULL
;
848 for (i
=0; in_vals
[i
]; i
++)
850 values
= talloc_zero_array(ctx
, char *, i
+1);
851 if (!values
) return NULL
;
853 for (i
=0; in_vals
[i
]; i
++) {
854 if (!pull_utf8_talloc(ctx
, &values
[i
], in_vals
[i
],
856 DEBUG(0,("ads_pull_strvals: pull_utf8_talloc failed: "
857 "%s", strerror(errno
)));
864 * Do a search with paged results. cookie must be null on the first
865 * call, and then returned on each subsequent call. It will be null
866 * again when the entire search is complete
867 * @param ads connection to ads server
868 * @param bind_path Base dn for the search
869 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
870 * @param expr Search expression - specified in local charset
871 * @param attrs Attributes to retrieve - specified in utf8 or ascii
872 * @param res ** which will contain results - free res* with ads_msgfree()
873 * @param count Number of entries retrieved on this page
874 * @param cookie The paged results cookie to be returned on subsequent calls
875 * @return status of search
877 static ADS_STATUS
ads_do_paged_search_args(ADS_STRUCT
*ads
,
878 const char *bind_path
,
879 int scope
, const char *expr
,
880 const char **attrs
, void *args
,
882 int *count
, struct berval
**cookie
)
885 char *utf8_expr
, *utf8_path
, **search_attrs
= NULL
;
886 size_t converted_size
;
887 LDAPControl PagedResults
, NoReferrals
, ExternalCtrl
, *controls
[4], **rcontrols
;
888 BerElement
*cookie_be
= NULL
;
889 struct berval
*cookie_bv
= NULL
;
890 BerElement
*ext_be
= NULL
;
891 struct berval
*ext_bv
= NULL
;
894 ads_control
*external_control
= (ads_control
*) args
;
898 if (!(ctx
= talloc_init("ads_do_paged_search_args")))
899 return ADS_ERROR(LDAP_NO_MEMORY
);
901 /* 0 means the conversion worked but the result was empty
902 so we only fail if it's -1. In any case, it always
903 at least nulls out the dest */
904 if (!push_utf8_talloc(ctx
, &utf8_expr
, expr
, &converted_size
) ||
905 !push_utf8_talloc(ctx
, &utf8_path
, bind_path
, &converted_size
))
911 if (!attrs
|| !(*attrs
))
914 /* This would be the utf8-encoded version...*/
915 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
916 if (!(search_attrs
= str_list_copy(talloc_tos(), attrs
))) {
922 /* Paged results only available on ldap v3 or later */
923 ldap_get_option(ads
->ldap
.ld
, LDAP_OPT_PROTOCOL_VERSION
, &version
);
924 if (version
< LDAP_VERSION3
) {
925 rc
= LDAP_NOT_SUPPORTED
;
929 cookie_be
= ber_alloc_t(LBER_USE_DER
);
931 ber_printf(cookie_be
, "{iO}", (ber_int_t
) ads
->config
.ldap_page_size
, *cookie
);
932 ber_bvfree(*cookie
); /* don't need it from last time */
935 ber_printf(cookie_be
, "{io}", (ber_int_t
) ads
->config
.ldap_page_size
, "", 0);
937 ber_flatten(cookie_be
, &cookie_bv
);
938 PagedResults
.ldctl_oid
= discard_const_p(char, ADS_PAGE_CTL_OID
);
939 PagedResults
.ldctl_iscritical
= (char) 1;
940 PagedResults
.ldctl_value
.bv_len
= cookie_bv
->bv_len
;
941 PagedResults
.ldctl_value
.bv_val
= cookie_bv
->bv_val
;
943 NoReferrals
.ldctl_oid
= discard_const_p(char, ADS_NO_REFERRALS_OID
);
944 NoReferrals
.ldctl_iscritical
= (char) 0;
945 NoReferrals
.ldctl_value
.bv_len
= 0;
946 NoReferrals
.ldctl_value
.bv_val
= discard_const_p(char, "");
948 if (external_control
&&
949 (strequal(external_control
->control
, ADS_EXTENDED_DN_OID
) ||
950 strequal(external_control
->control
, ADS_SD_FLAGS_OID
))) {
952 ExternalCtrl
.ldctl_oid
= discard_const_p(char, external_control
->control
);
953 ExternalCtrl
.ldctl_iscritical
= (char) external_control
->critical
;
955 /* win2k does not accept a ldctl_value beeing passed in */
957 if (external_control
->val
!= 0) {
959 if ((ext_be
= ber_alloc_t(LBER_USE_DER
)) == NULL
) {
964 if ((ber_printf(ext_be
, "{i}", (ber_int_t
) external_control
->val
)) == -1) {
968 if ((ber_flatten(ext_be
, &ext_bv
)) == -1) {
973 ExternalCtrl
.ldctl_value
.bv_len
= ext_bv
->bv_len
;
974 ExternalCtrl
.ldctl_value
.bv_val
= ext_bv
->bv_val
;
977 ExternalCtrl
.ldctl_value
.bv_len
= 0;
978 ExternalCtrl
.ldctl_value
.bv_val
= NULL
;
981 controls
[0] = &NoReferrals
;
982 controls
[1] = &PagedResults
;
983 controls
[2] = &ExternalCtrl
;
987 controls
[0] = &NoReferrals
;
988 controls
[1] = &PagedResults
;
992 /* we need to disable referrals as the openldap libs don't
993 handle them and paged results at the same time. Using them
994 together results in the result record containing the server
995 page control being removed from the result list (tridge/jmcd)
997 leaving this in despite the control that says don't generate
998 referrals, in case the server doesn't support it (jmcd)
1000 ldap_set_option(ads
->ldap
.ld
, LDAP_OPT_REFERRALS
, LDAP_OPT_OFF
);
1002 rc
= ldap_search_with_timeout(ads
->ldap
.ld
, utf8_path
, scope
, utf8_expr
,
1003 search_attrs
, 0, controls
,
1004 NULL
, LDAP_NO_LIMIT
,
1005 (LDAPMessage
**)res
);
1007 ber_free(cookie_be
, 1);
1008 ber_bvfree(cookie_bv
);
1011 DEBUG(3,("ads_do_paged_search_args: ldap_search_with_timeout(%s) -> %s\n", expr
,
1012 ldap_err2string(rc
)));
1013 if (rc
== LDAP_OTHER
) {
1017 ret
= ldap_parse_result(ads
->ldap
.ld
,
1025 if (ret
== LDAP_SUCCESS
) {
1026 DEBUG(3, ("ldap_search_with_timeout(%s) "
1027 "error: %s\n", expr
, ldap_errmsg
));
1028 ldap_memfree(ldap_errmsg
);
1034 rc
= ldap_parse_result(ads
->ldap
.ld
, *res
, NULL
, NULL
, NULL
,
1035 NULL
, &rcontrols
, 0);
1041 for (i
=0; rcontrols
[i
]; i
++) {
1042 if (strcmp(ADS_PAGE_CTL_OID
, rcontrols
[i
]->ldctl_oid
) == 0) {
1043 cookie_be
= ber_init(&rcontrols
[i
]->ldctl_value
);
1044 ber_scanf(cookie_be
,"{iO}", (ber_int_t
*) count
,
1046 /* the berval is the cookie, but must be freed when
1048 if (cookie_bv
->bv_len
) /* still more to do */
1049 *cookie
=ber_bvdup(cookie_bv
);
1052 ber_bvfree(cookie_bv
);
1053 ber_free(cookie_be
, 1);
1057 ldap_controls_free(rcontrols
);
1060 talloc_destroy(ctx
);
1063 ber_free(ext_be
, 1);
1070 if (rc
!= LDAP_SUCCESS
&& *res
!= NULL
) {
1071 ads_msgfree(ads
, *res
);
1075 /* if/when we decide to utf8-encode attrs, take out this next line */
1076 TALLOC_FREE(search_attrs
);
1078 return ADS_ERROR(rc
);
1081 static ADS_STATUS
ads_do_paged_search(ADS_STRUCT
*ads
, const char *bind_path
,
1082 int scope
, const char *expr
,
1083 const char **attrs
, LDAPMessage
**res
,
1084 int *count
, struct berval
**cookie
)
1086 return ads_do_paged_search_args(ads
, bind_path
, scope
, expr
, attrs
, NULL
, res
, count
, cookie
);
1091 * Get all results for a search. This uses ads_do_paged_search() to return
1092 * all entries in a large search.
1093 * @param ads connection to ads server
1094 * @param bind_path Base dn for the search
1095 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1096 * @param expr Search expression
1097 * @param attrs Attributes to retrieve
1098 * @param res ** which will contain results - free res* with ads_msgfree()
1099 * @return status of search
1101 ADS_STATUS
ads_do_search_all_args(ADS_STRUCT
*ads
, const char *bind_path
,
1102 int scope
, const char *expr
,
1103 const char **attrs
, void *args
,
1106 struct berval
*cookie
= NULL
;
1111 status
= ads_do_paged_search_args(ads
, bind_path
, scope
, expr
, attrs
, args
, res
,
1114 if (!ADS_ERR_OK(status
))
1117 #ifdef HAVE_LDAP_ADD_RESULT_ENTRY
1119 LDAPMessage
*res2
= NULL
;
1120 LDAPMessage
*msg
, *next
;
1122 status
= ads_do_paged_search_args(ads
, bind_path
, scope
, expr
,
1123 attrs
, args
, &res2
, &count
, &cookie
);
1124 if (!ADS_ERR_OK(status
)) {
1128 /* this relies on the way that ldap_add_result_entry() works internally. I hope
1129 that this works on all ldap libs, but I have only tested with openldap */
1130 for (msg
= ads_first_message(ads
, res2
); msg
; msg
= next
) {
1131 next
= ads_next_message(ads
, msg
);
1132 ldap_add_result_entry((LDAPMessage
**)res
, msg
);
1134 /* note that we do not free res2, as the memory is now
1135 part of the main returned list */
1138 DEBUG(0, ("no ldap_add_result_entry() support in LDAP libs!\n"));
1139 status
= ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL
);
1145 ADS_STATUS
ads_do_search_all(ADS_STRUCT
*ads
, const char *bind_path
,
1146 int scope
, const char *expr
,
1147 const char **attrs
, LDAPMessage
**res
)
1149 return ads_do_search_all_args(ads
, bind_path
, scope
, expr
, attrs
, NULL
, res
);
1152 ADS_STATUS
ads_do_search_all_sd_flags(ADS_STRUCT
*ads
, const char *bind_path
,
1153 int scope
, const char *expr
,
1154 const char **attrs
, uint32_t sd_flags
,
1159 args
.control
= ADS_SD_FLAGS_OID
;
1160 args
.val
= sd_flags
;
1161 args
.critical
= True
;
1163 return ads_do_search_all_args(ads
, bind_path
, scope
, expr
, attrs
, &args
, res
);
1168 * Run a function on all results for a search. Uses ads_do_paged_search() and
1169 * runs the function as each page is returned, using ads_process_results()
1170 * @param ads connection to ads server
1171 * @param bind_path Base dn for the search
1172 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1173 * @param expr Search expression - specified in local charset
1174 * @param attrs Attributes to retrieve - specified in UTF-8 or ascii
1175 * @param fn Function which takes attr name, values list, and data_area
1176 * @param data_area Pointer which is passed to function on each call
1177 * @return status of search
1179 ADS_STATUS
ads_do_search_all_fn(ADS_STRUCT
*ads
, const char *bind_path
,
1180 int scope
, const char *expr
, const char **attrs
,
1181 bool (*fn
)(ADS_STRUCT
*, char *, void **, void *),
1184 struct berval
*cookie
= NULL
;
1189 status
= ads_do_paged_search(ads
, bind_path
, scope
, expr
, attrs
, &res
,
1192 if (!ADS_ERR_OK(status
)) return status
;
1194 ads_process_results(ads
, res
, fn
, data_area
);
1195 ads_msgfree(ads
, res
);
1198 status
= ads_do_paged_search(ads
, bind_path
, scope
, expr
, attrs
,
1199 &res
, &count
, &cookie
);
1201 if (!ADS_ERR_OK(status
)) break;
1203 ads_process_results(ads
, res
, fn
, data_area
);
1204 ads_msgfree(ads
, res
);
1211 * Do a search with a timeout.
1212 * @param ads connection to ads server
1213 * @param bind_path Base dn for the search
1214 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1215 * @param expr Search expression
1216 * @param attrs Attributes to retrieve
1217 * @param res ** which will contain results - free res* with ads_msgfree()
1218 * @return status of search
1220 ADS_STATUS
ads_do_search(ADS_STRUCT
*ads
, const char *bind_path
, int scope
,
1222 const char **attrs
, LDAPMessage
**res
)
1225 char *utf8_expr
, *utf8_path
, **search_attrs
= NULL
;
1226 size_t converted_size
;
1230 if (!(ctx
= talloc_init("ads_do_search"))) {
1231 DEBUG(1,("ads_do_search: talloc_init() failed!"));
1232 return ADS_ERROR(LDAP_NO_MEMORY
);
1235 /* 0 means the conversion worked but the result was empty
1236 so we only fail if it's negative. In any case, it always
1237 at least nulls out the dest */
1238 if (!push_utf8_talloc(ctx
, &utf8_expr
, expr
, &converted_size
) ||
1239 !push_utf8_talloc(ctx
, &utf8_path
, bind_path
, &converted_size
))
1241 DEBUG(1,("ads_do_search: push_utf8_talloc() failed!"));
1242 rc
= LDAP_NO_MEMORY
;
1246 if (!attrs
|| !(*attrs
))
1247 search_attrs
= NULL
;
1249 /* This would be the utf8-encoded version...*/
1250 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
1251 if (!(search_attrs
= str_list_copy(talloc_tos(), attrs
)))
1253 DEBUG(1,("ads_do_search: str_list_copy() failed!"));
1254 rc
= LDAP_NO_MEMORY
;
1259 /* see the note in ads_do_paged_search - we *must* disable referrals */
1260 ldap_set_option(ads
->ldap
.ld
, LDAP_OPT_REFERRALS
, LDAP_OPT_OFF
);
1262 rc
= ldap_search_with_timeout(ads
->ldap
.ld
, utf8_path
, scope
, utf8_expr
,
1263 search_attrs
, 0, NULL
, NULL
,
1265 (LDAPMessage
**)res
);
1267 if (rc
== LDAP_SIZELIMIT_EXCEEDED
) {
1268 DEBUG(3,("Warning! sizelimit exceeded in ldap. Truncating.\n"));
1273 talloc_destroy(ctx
);
1274 /* if/when we decide to utf8-encode attrs, take out this next line */
1275 TALLOC_FREE(search_attrs
);
1276 return ADS_ERROR(rc
);
1279 * Do a general ADS search
1280 * @param ads connection to ads server
1281 * @param res ** which will contain results - free res* with ads_msgfree()
1282 * @param expr Search expression
1283 * @param attrs Attributes to retrieve
1284 * @return status of search
1286 ADS_STATUS
ads_search(ADS_STRUCT
*ads
, LDAPMessage
**res
,
1287 const char *expr
, const char **attrs
)
1289 return ads_do_search(ads
, ads
->config
.bind_path
, LDAP_SCOPE_SUBTREE
,
1294 * Do a search on a specific DistinguishedName
1295 * @param ads connection to ads server
1296 * @param res ** which will contain results - free res* with ads_msgfree()
1297 * @param dn DistinguishName to search
1298 * @param attrs Attributes to retrieve
1299 * @return status of search
1301 ADS_STATUS
ads_search_dn(ADS_STRUCT
*ads
, LDAPMessage
**res
,
1302 const char *dn
, const char **attrs
)
1304 return ads_do_search(ads
, dn
, LDAP_SCOPE_BASE
, "(objectclass=*)",
1309 * Free up memory from a ads_search
1310 * @param ads connection to ads server
1311 * @param msg Search results to free
1313 void ads_msgfree(ADS_STRUCT
*ads
, LDAPMessage
*msg
)
1320 * Get a dn from search results
1321 * @param ads connection to ads server
1322 * @param msg Search result
1325 char *ads_get_dn(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
, LDAPMessage
*msg
)
1327 char *utf8_dn
, *unix_dn
;
1328 size_t converted_size
;
1330 utf8_dn
= ldap_get_dn(ads
->ldap
.ld
, msg
);
1333 DEBUG (5, ("ads_get_dn: ldap_get_dn failed\n"));
1337 if (!pull_utf8_talloc(mem_ctx
, &unix_dn
, utf8_dn
, &converted_size
)) {
1338 DEBUG(0,("ads_get_dn: string conversion failure utf8 [%s]\n",
1342 ldap_memfree(utf8_dn
);
1347 * Get the parent from a dn
1348 * @param dn the dn to return the parent from
1349 * @return parent dn string
1351 char *ads_parent_dn(const char *dn
)
1359 p
= strchr(dn
, ',');
1369 * Find a machine account given a hostname
1370 * @param ads connection to ads server
1371 * @param res ** which will contain results - free res* with ads_msgfree()
1372 * @param host Hostname to search for
1373 * @return status of search
1375 ADS_STATUS
ads_find_machine_acct(ADS_STRUCT
*ads
, LDAPMessage
**res
,
1376 const char *machine
)
1380 const char *attrs
[] = {
1381 /* This is how Windows checks for machine accounts */
1384 "userAccountControl",
1386 "ServicePrincipalName",
1387 "userPrincipalName",
1390 /* Additional attributes Samba checks */
1391 "msDS-AdditionalDnsHostName",
1392 "msDS-SupportedEncryptionTypes",
1393 "nTSecurityDescriptor",
1397 TALLOC_CTX
*frame
= talloc_stackframe();
1401 /* the easiest way to find a machine account anywhere in the tree
1402 is to look for hostname$ */
1403 expr
= talloc_asprintf(frame
, "(samAccountName=%s$)", machine
);
1405 status
= ADS_ERROR_NT(NT_STATUS_NO_MEMORY
);
1409 status
= ads_search(ads
, res
, expr
, attrs
);
1410 if (ADS_ERR_OK(status
)) {
1411 if (ads_count_replies(ads
, *res
) != 1) {
1412 status
= ADS_ERROR_LDAP(LDAP_NO_SUCH_OBJECT
);
1422 * Initialize a list of mods to be used in a modify request
1423 * @param ctx An initialized TALLOC_CTX
1424 * @return allocated ADS_MODLIST
1426 ADS_MODLIST
ads_init_mods(TALLOC_CTX
*ctx
)
1428 #define ADS_MODLIST_ALLOC_SIZE 10
1431 if ((mods
= talloc_zero_array(ctx
, LDAPMod
*, ADS_MODLIST_ALLOC_SIZE
+ 1)))
1432 /* -1 is safety to make sure we don't go over the end.
1433 need to reset it to NULL before doing ldap modify */
1434 mods
[ADS_MODLIST_ALLOC_SIZE
] = (LDAPMod
*) -1;
1436 return (ADS_MODLIST
)mods
;
1441 add an attribute to the list, with values list already constructed
1443 static ADS_STATUS
ads_modlist_add(TALLOC_CTX
*ctx
, ADS_MODLIST
*mods
,
1444 int mod_op
, const char *name
,
1445 const void *_invals
)
1448 LDAPMod
**modlist
= (LDAPMod
**) *mods
;
1449 struct berval
**ber_values
= NULL
;
1450 char **char_values
= NULL
;
1453 mod_op
= LDAP_MOD_DELETE
;
1455 if (mod_op
& LDAP_MOD_BVALUES
) {
1456 const struct berval
**b
;
1457 b
= discard_const_p(const struct berval
*, _invals
);
1458 ber_values
= ads_dup_values(ctx
, b
);
1461 c
= discard_const_p(const char *, _invals
);
1462 char_values
= ads_push_strvals(ctx
, c
);
1466 /* find the first empty slot */
1467 for (curmod
=0; modlist
[curmod
] && modlist
[curmod
] != (LDAPMod
*) -1;
1469 if (modlist
[curmod
] == (LDAPMod
*) -1) {
1470 if (!(modlist
= talloc_realloc(ctx
, modlist
, LDAPMod
*,
1471 curmod
+ADS_MODLIST_ALLOC_SIZE
+1)))
1472 return ADS_ERROR(LDAP_NO_MEMORY
);
1473 memset(&modlist
[curmod
], 0,
1474 ADS_MODLIST_ALLOC_SIZE
*sizeof(LDAPMod
*));
1475 modlist
[curmod
+ADS_MODLIST_ALLOC_SIZE
] = (LDAPMod
*) -1;
1476 *mods
= (ADS_MODLIST
)modlist
;
1479 if (!(modlist
[curmod
] = talloc_zero(ctx
, LDAPMod
)))
1480 return ADS_ERROR(LDAP_NO_MEMORY
);
1481 modlist
[curmod
]->mod_type
= talloc_strdup(ctx
, name
);
1482 if (mod_op
& LDAP_MOD_BVALUES
) {
1483 modlist
[curmod
]->mod_bvalues
= ber_values
;
1484 } else if (mod_op
& LDAP_MOD_DELETE
) {
1485 modlist
[curmod
]->mod_values
= NULL
;
1487 modlist
[curmod
]->mod_values
= char_values
;
1490 modlist
[curmod
]->mod_op
= mod_op
;
1491 return ADS_ERROR(LDAP_SUCCESS
);
1495 * Add a single string value to a mod list
1496 * @param ctx An initialized TALLOC_CTX
1497 * @param mods An initialized ADS_MODLIST
1498 * @param name The attribute name to add
1499 * @param val The value to add - NULL means DELETE
1500 * @return ADS STATUS indicating success of add
1502 ADS_STATUS
ads_mod_str(TALLOC_CTX
*ctx
, ADS_MODLIST
*mods
,
1503 const char *name
, const char *val
)
1505 const char *values
[2];
1511 return ads_modlist_add(ctx
, mods
, LDAP_MOD_DELETE
, name
, NULL
);
1512 return ads_modlist_add(ctx
, mods
, LDAP_MOD_REPLACE
, name
, values
);
1516 * Add an array of string values to a mod list
1517 * @param ctx An initialized TALLOC_CTX
1518 * @param mods An initialized ADS_MODLIST
1519 * @param name The attribute name to add
1520 * @param vals The array of string values to add - NULL means DELETE
1521 * @return ADS STATUS indicating success of add
1523 ADS_STATUS
ads_mod_strlist(TALLOC_CTX
*ctx
, ADS_MODLIST
*mods
,
1524 const char *name
, const char **vals
)
1527 return ads_modlist_add(ctx
, mods
, LDAP_MOD_DELETE
, name
, NULL
);
1528 return ads_modlist_add(ctx
, mods
, LDAP_MOD_REPLACE
,
1529 name
, (const void **) vals
);
1533 * Add a single ber-encoded value to a mod list
1534 * @param ctx An initialized TALLOC_CTX
1535 * @param mods An initialized ADS_MODLIST
1536 * @param name The attribute name to add
1537 * @param val The value to add - NULL means DELETE
1538 * @return ADS STATUS indicating success of add
1540 static ADS_STATUS
ads_mod_ber(TALLOC_CTX
*ctx
, ADS_MODLIST
*mods
,
1541 const char *name
, const struct berval
*val
)
1543 const struct berval
*values
[2];
1548 return ads_modlist_add(ctx
, mods
, LDAP_MOD_DELETE
, name
, NULL
);
1549 return ads_modlist_add(ctx
, mods
, LDAP_MOD_REPLACE
|LDAP_MOD_BVALUES
,
1550 name
, (const void **) values
);
1553 static void ads_print_error(int ret
, LDAP
*ld
)
1556 char *ld_error
= NULL
;
1557 ldap_get_option(ld
, LDAP_OPT_ERROR_STRING
, &ld_error
);
1558 DBG_ERR("AD LDAP ERROR: %d (%s): %s\n",
1560 ldap_err2string(ret
),
1562 SAFE_FREE(ld_error
);
1567 * Perform an ldap modify
1568 * @param ads connection to ads server
1569 * @param mod_dn DistinguishedName to modify
1570 * @param mods list of modifications to perform
1571 * @return status of modify
1573 ADS_STATUS
ads_gen_mod(ADS_STRUCT
*ads
, const char *mod_dn
, ADS_MODLIST mods
)
1576 char *utf8_dn
= NULL
;
1577 size_t converted_size
;
1579 this control is needed to modify that contains a currently
1580 non-existent attribute (but allowable for the object) to run
1582 LDAPControl PermitModify
= {
1583 discard_const_p(char, ADS_PERMIT_MODIFY_OID
),
1586 LDAPControl
*controls
[2];
1588 DBG_INFO("AD LDAP: Modifying %s\n", mod_dn
);
1590 controls
[0] = &PermitModify
;
1593 if (!push_utf8_talloc(talloc_tos(), &utf8_dn
, mod_dn
, &converted_size
)) {
1594 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY
);
1597 /* find the end of the list, marked by NULL or -1 */
1598 for(i
=0;(mods
[i
]!=0)&&(mods
[i
]!=(LDAPMod
*) -1);i
++);
1599 /* make sure the end of the list is NULL */
1601 ret
= ldap_modify_ext_s(ads
->ldap
.ld
, utf8_dn
,
1602 (LDAPMod
**) mods
, controls
, NULL
);
1603 ads_print_error(ret
, ads
->ldap
.ld
);
1604 TALLOC_FREE(utf8_dn
);
1605 return ADS_ERROR(ret
);
1609 * Perform an ldap add
1610 * @param ads connection to ads server
1611 * @param new_dn DistinguishedName to add
1612 * @param mods list of attributes and values for DN
1613 * @return status of add
1615 ADS_STATUS
ads_gen_add(ADS_STRUCT
*ads
, const char *new_dn
, ADS_MODLIST mods
)
1618 char *utf8_dn
= NULL
;
1619 size_t converted_size
;
1621 DBG_INFO("AD LDAP: Adding %s\n", new_dn
);
1623 if (!push_utf8_talloc(talloc_tos(), &utf8_dn
, new_dn
, &converted_size
)) {
1624 DEBUG(1, ("ads_gen_add: push_utf8_talloc failed!"));
1625 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY
);
1628 /* find the end of the list, marked by NULL or -1 */
1629 for(i
=0;(mods
[i
]!=0)&&(mods
[i
]!=(LDAPMod
*) -1);i
++);
1630 /* make sure the end of the list is NULL */
1633 ret
= ldap_add_ext_s(ads
->ldap
.ld
, utf8_dn
, (LDAPMod
**)mods
, NULL
, NULL
);
1634 ads_print_error(ret
, ads
->ldap
.ld
);
1635 TALLOC_FREE(utf8_dn
);
1636 return ADS_ERROR(ret
);
1640 * Delete a DistinguishedName
1641 * @param ads connection to ads server
1642 * @param new_dn DistinguishedName to delete
1643 * @return status of delete
1645 ADS_STATUS
ads_del_dn(ADS_STRUCT
*ads
, char *del_dn
)
1648 char *utf8_dn
= NULL
;
1649 size_t converted_size
;
1650 if (!push_utf8_talloc(talloc_tos(), &utf8_dn
, del_dn
, &converted_size
)) {
1651 DEBUG(1, ("ads_del_dn: push_utf8_talloc failed!"));
1652 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY
);
1655 DBG_INFO("AD LDAP: Deleting %s\n", del_dn
);
1657 ret
= ldap_delete_s(ads
->ldap
.ld
, utf8_dn
);
1658 ads_print_error(ret
, ads
->ldap
.ld
);
1659 TALLOC_FREE(utf8_dn
);
1660 return ADS_ERROR(ret
);
1664 * Build an org unit string
1665 * if org unit is Computers or blank then assume a container, otherwise
1666 * assume a / separated list of organisational units.
1667 * jmcd: '\' is now used for escapes so certain chars can be in the ou (e.g. #)
1668 * @param ads connection to ads server
1669 * @param org_unit Organizational unit
1670 * @return org unit string - caller must free
1672 char *ads_ou_string(ADS_STRUCT
*ads
, const char *org_unit
)
1676 if (!org_unit
|| !*org_unit
) {
1678 ret
= ads_default_ou_string(ads
, DS_GUID_COMPUTERS_CONTAINER
);
1680 /* samba4 might not yet respond to a wellknownobject-query */
1681 return ret
? ret
: SMB_STRDUP("cn=Computers");
1684 if (strequal(org_unit
, "Computers")) {
1685 return SMB_STRDUP("cn=Computers");
1688 /* jmcd: removed "\\" from the separation chars, because it is
1689 needed as an escape for chars like '#' which are valid in an
1691 return ads_build_path(org_unit
, "/", "ou=", 1);
1695 * Get a org unit string for a well-known GUID
1696 * @param ads connection to ads server
1697 * @param wknguid Well known GUID
1698 * @return org unit string - caller must free
1700 char *ads_default_ou_string(ADS_STRUCT
*ads
, const char *wknguid
)
1703 LDAPMessage
*res
= NULL
;
1704 char *base
, *wkn_dn
= NULL
, *ret
= NULL
, **wkn_dn_exp
= NULL
,
1705 **bind_dn_exp
= NULL
;
1706 const char *attrs
[] = {"distinguishedName", NULL
};
1707 int new_ln
, wkn_ln
, bind_ln
, i
;
1709 if (wknguid
== NULL
) {
1713 if (asprintf(&base
, "<WKGUID=%s,%s>", wknguid
, ads
->config
.bind_path
) == -1) {
1714 DEBUG(1, ("asprintf failed!\n"));
1718 status
= ads_search_dn(ads
, &res
, base
, attrs
);
1719 if (!ADS_ERR_OK(status
)) {
1720 DEBUG(1,("Failed while searching for: %s\n", base
));
1724 if (ads_count_replies(ads
, res
) != 1) {
1728 /* substitute the bind-path from the well-known-guid-search result */
1729 wkn_dn
= ads_get_dn(ads
, talloc_tos(), res
);
1734 wkn_dn_exp
= ldap_explode_dn(wkn_dn
, 0);
1739 bind_dn_exp
= ldap_explode_dn(ads
->config
.bind_path
, 0);
1744 for (wkn_ln
=0; wkn_dn_exp
[wkn_ln
]; wkn_ln
++)
1746 for (bind_ln
=0; bind_dn_exp
[bind_ln
]; bind_ln
++)
1749 new_ln
= wkn_ln
- bind_ln
;
1751 ret
= SMB_STRDUP(wkn_dn_exp
[0]);
1756 for (i
=1; i
< new_ln
; i
++) {
1759 if (asprintf(&s
, "%s,%s", ret
, wkn_dn_exp
[i
]) == -1) {
1765 ret
= SMB_STRDUP(s
);
1774 ads_msgfree(ads
, res
);
1775 TALLOC_FREE(wkn_dn
);
1777 ldap_value_free(wkn_dn_exp
);
1780 ldap_value_free(bind_dn_exp
);
1787 * Adds (appends) an item to an attribute array, rather then
1788 * replacing the whole list
1789 * @param ctx An initialized TALLOC_CTX
1790 * @param mods An initialized ADS_MODLIST
1791 * @param name name of the ldap attribute to append to
1792 * @param vals an array of values to add
1793 * @return status of addition
1796 ADS_STATUS
ads_add_strlist(TALLOC_CTX
*ctx
, ADS_MODLIST
*mods
,
1797 const char *name
, const char **vals
)
1799 return ads_modlist_add(ctx
, mods
, LDAP_MOD_ADD
, name
,
1800 (const void *) vals
);
1804 * Determines the an account's current KVNO via an LDAP lookup
1805 * @param ads An initialized ADS_STRUCT
1806 * @param account_name the NT samaccountname.
1807 * @return the kvno for the account, or -1 in case of a failure.
1810 uint32_t ads_get_kvno(ADS_STRUCT
*ads
, const char *account_name
)
1812 LDAPMessage
*res
= NULL
;
1813 uint32_t kvno
= (uint32_t)-1; /* -1 indicates a failure */
1815 const char *attrs
[] = {"msDS-KeyVersionNumber", NULL
};
1816 char *dn_string
= NULL
;
1819 DEBUG(5,("ads_get_kvno: Searching for account %s\n", account_name
));
1820 if (asprintf(&filter
, "(samAccountName=%s)", account_name
) == -1) {
1823 ret
= ads_search(ads
, &res
, filter
, attrs
);
1825 if (!ADS_ERR_OK(ret
) || (ads_count_replies(ads
, res
) != 1)) {
1826 DEBUG(1,("ads_get_kvno: Account for %s not found.\n", account_name
));
1827 ads_msgfree(ads
, res
);
1831 dn_string
= ads_get_dn(ads
, talloc_tos(), res
);
1833 DEBUG(0,("ads_get_kvno: out of memory.\n"));
1834 ads_msgfree(ads
, res
);
1837 DEBUG(5,("ads_get_kvno: Using: %s\n", dn_string
));
1838 TALLOC_FREE(dn_string
);
1840 /* ---------------------------------------------------------
1841 * 0 is returned as a default KVNO from this point on...
1842 * This is done because Windows 2000 does not support key
1843 * version numbers. Chances are that a failure in the next
1844 * step is simply due to Windows 2000 being used for a
1845 * domain controller. */
1848 if (!ads_pull_uint32(ads
, res
, "msDS-KeyVersionNumber", &kvno
)) {
1849 DEBUG(3,("ads_get_kvno: Error Determining KVNO!\n"));
1850 DEBUG(3,("ads_get_kvno: Windows 2000 does not support KVNO's, so this may be normal.\n"));
1851 ads_msgfree(ads
, res
);
1856 DEBUG(5,("ads_get_kvno: Looked Up KVNO of: %d\n", kvno
));
1857 ads_msgfree(ads
, res
);
1862 * Determines the computer account's current KVNO via an LDAP lookup
1863 * @param ads An initialized ADS_STRUCT
1864 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1865 * @return the kvno for the computer account, or -1 in case of a failure.
1868 uint32_t ads_get_machine_kvno(ADS_STRUCT
*ads
, const char *machine_name
)
1870 char *computer_account
= NULL
;
1873 if (asprintf(&computer_account
, "%s$", machine_name
) < 0) {
1877 kvno
= ads_get_kvno(ads
, computer_account
);
1878 free(computer_account
);
1884 * This clears out all registered spn's for a given hostname
1885 * @param ads An initilaized ADS_STRUCT
1886 * @param machine_name the NetBIOS name of the computer.
1887 * @return 0 upon success, non-zero otherwise.
1890 ADS_STATUS
ads_clear_service_principal_names(ADS_STRUCT
*ads
, const char *machine_name
)
1893 LDAPMessage
*res
= NULL
;
1895 const char *servicePrincipalName
[1] = {NULL
};
1897 char *dn_string
= NULL
;
1899 ret
= ads_find_machine_acct(ads
, &res
, machine_name
);
1900 if (!ADS_ERR_OK(ret
)) {
1901 DEBUG(5,("ads_clear_service_principal_names: WARNING: Host Account for %s not found... skipping operation.\n", machine_name
));
1902 DEBUG(5,("ads_clear_service_principal_names: WARNING: Service Principals for %s have NOT been cleared.\n", machine_name
));
1903 ads_msgfree(ads
, res
);
1907 DEBUG(5,("ads_clear_service_principal_names: Host account for %s found\n", machine_name
));
1908 ctx
= talloc_init("ads_clear_service_principal_names");
1910 ads_msgfree(ads
, res
);
1911 return ADS_ERROR(LDAP_NO_MEMORY
);
1914 if (!(mods
= ads_init_mods(ctx
))) {
1915 talloc_destroy(ctx
);
1916 ads_msgfree(ads
, res
);
1917 return ADS_ERROR(LDAP_NO_MEMORY
);
1919 ret
= ads_mod_strlist(ctx
, &mods
, "servicePrincipalName", servicePrincipalName
);
1920 if (!ADS_ERR_OK(ret
)) {
1921 DEBUG(1,("ads_clear_service_principal_names: Error creating strlist.\n"));
1922 ads_msgfree(ads
, res
);
1923 talloc_destroy(ctx
);
1926 dn_string
= ads_get_dn(ads
, talloc_tos(), res
);
1928 talloc_destroy(ctx
);
1929 ads_msgfree(ads
, res
);
1930 return ADS_ERROR(LDAP_NO_MEMORY
);
1932 ret
= ads_gen_mod(ads
, dn_string
, mods
);
1933 TALLOC_FREE(dn_string
);
1934 if (!ADS_ERR_OK(ret
)) {
1935 DEBUG(1,("ads_clear_service_principal_names: Error: Updating Service Principals for machine %s in LDAP\n",
1937 ads_msgfree(ads
, res
);
1938 talloc_destroy(ctx
);
1942 ads_msgfree(ads
, res
);
1943 talloc_destroy(ctx
);
1948 * @brief Search for an element in a string array.
1950 * @param[in] el_array The string array to search.
1952 * @param[in] num_el The number of elements in the string array.
1954 * @param[in] el The string to search.
1956 * @return True if found, false if not.
1958 bool ads_element_in_array(const char **el_array
, size_t num_el
, const char *el
)
1962 if (el_array
== NULL
|| num_el
== 0 || el
== NULL
) {
1966 for (i
= 0; i
< num_el
&& el_array
[i
] != NULL
; i
++) {
1969 cmp
= strcasecmp_m(el_array
[i
], el
);
1979 * @brief This gets the service principal names of an existing computer account.
1981 * @param[in] mem_ctx The memory context to use to allocate the spn array.
1983 * @param[in] ads The ADS context to use.
1985 * @param[in] machine_name The NetBIOS name of the computer, which is used to
1986 * identify the computer account.
1988 * @param[in] spn_array A pointer to store the array for SPNs.
1990 * @param[in] num_spns The number of principals stored in the array.
1992 * @return 0 on success, or a ADS error if a failure occurred.
1994 ADS_STATUS
ads_get_service_principal_names(TALLOC_CTX
*mem_ctx
,
1996 const char *machine_name
,
2001 LDAPMessage
*res
= NULL
;
2004 status
= ads_find_machine_acct(ads
,
2007 if (!ADS_ERR_OK(status
)) {
2008 DEBUG(1,("Host Account for %s not found... skipping operation.\n",
2013 count
= ads_count_replies(ads
, res
);
2015 status
= ADS_ERROR(LDAP_NO_SUCH_OBJECT
);
2019 *spn_array
= ads_pull_strings(ads
,
2022 "servicePrincipalName",
2024 if (*spn_array
== NULL
) {
2025 DEBUG(1, ("Host account for %s does not have service principal "
2028 status
= ADS_ERROR(LDAP_NO_SUCH_OBJECT
);
2033 ads_msgfree(ads
, res
);
2039 * This adds a service principal name to an existing computer account
2040 * (found by hostname) in AD.
2041 * @param ads An initialized ADS_STRUCT
2042 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
2043 * @param spns An array or strings for the service principals to add,
2044 * i.e. 'cifs/machine_name', 'http/machine.full.domain.com' etc.
2045 * @return 0 upon sucess, or non-zero if a failure occurs
2048 ADS_STATUS
ads_add_service_principal_names(ADS_STRUCT
*ads
,
2049 const char *machine_name
,
2054 LDAPMessage
*res
= NULL
;
2056 char *dn_string
= NULL
;
2057 const char **servicePrincipalName
= spns
;
2059 ret
= ads_find_machine_acct(ads
, &res
, machine_name
);
2060 if (!ADS_ERR_OK(ret
)) {
2061 DEBUG(1,("ads_add_service_principal_name: WARNING: Host Account for %s not found... skipping operation.\n",
2063 DEBUG(1,("ads_add_service_principal_name: WARNING: Service Principals have NOT been added.\n"));
2064 ads_msgfree(ads
, res
);
2068 DEBUG(1,("ads_add_service_principal_name: Host account for %s found\n", machine_name
));
2069 if (!(ctx
= talloc_init("ads_add_service_principal_name"))) {
2070 ads_msgfree(ads
, res
);
2071 return ADS_ERROR(LDAP_NO_MEMORY
);
2074 DEBUG(5,("ads_add_service_principal_name: INFO: "
2075 "Adding %s to host %s\n",
2076 spns
[0] ? "N/A" : spns
[0], machine_name
));
2079 DEBUG(5,("ads_add_service_principal_name: INFO: "
2080 "Adding %s to host %s\n",
2081 spns
[1] ? "N/A" : spns
[1], machine_name
));
2083 if ( (mods
= ads_init_mods(ctx
)) == NULL
) {
2084 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
2088 ret
= ads_add_strlist(ctx
,
2090 "servicePrincipalName",
2091 servicePrincipalName
);
2092 if (!ADS_ERR_OK(ret
)) {
2093 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
2097 if ( (dn_string
= ads_get_dn(ads
, ctx
, res
)) == NULL
) {
2098 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
2102 ret
= ads_gen_mod(ads
, dn_string
, mods
);
2103 if (!ADS_ERR_OK(ret
)) {
2104 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
2110 ads_msgfree(ads
, res
);
2114 static uint32_t ads_get_acct_ctrl(ADS_STRUCT
*ads
,
2117 uint32_t acct_ctrl
= 0;
2120 ok
= ads_pull_uint32(ads
, msg
, "userAccountControl", &acct_ctrl
);
2128 static ADS_STATUS
ads_change_machine_acct(ADS_STRUCT
*ads
,
2130 const struct berval
*machine_pw_val
)
2134 TALLOC_CTX
*frame
= talloc_stackframe();
2135 uint32_t acct_control
;
2136 char *control_str
= NULL
;
2137 const char *attrs
[] = {
2141 LDAPMessage
*res
= NULL
;
2144 dn
= ads_get_dn(ads
, frame
, msg
);
2146 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
2150 acct_control
= ads_get_acct_ctrl(ads
, msg
);
2151 if (acct_control
== 0) {
2152 ret
= ADS_ERROR(LDAP_NO_RESULTS_RETURNED
);
2157 * Changing the password, disables the account. So we need to change the
2158 * userAccountControl flags to enable it again.
2160 mods
= ads_init_mods(frame
);
2162 ret
= ADS_ERROR_LDAP(LDAP_NO_MEMORY
);
2166 ads_mod_ber(frame
, &mods
, "unicodePwd", machine_pw_val
);
2168 ret
= ads_gen_mod(ads
, dn
, mods
);
2169 if (!ADS_ERR_OK(ret
)) {
2175 * To activate the account, we need to disable and enable it.
2177 acct_control
|= UF_ACCOUNTDISABLE
;
2179 control_str
= talloc_asprintf(frame
, "%u", acct_control
);
2180 if (control_str
== NULL
) {
2181 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
2185 mods
= ads_init_mods(frame
);
2187 ret
= ADS_ERROR_LDAP(LDAP_NO_MEMORY
);
2191 ads_mod_str(frame
, &mods
, "userAccountControl", control_str
);
2193 ret
= ads_gen_mod(ads
, dn
, mods
);
2194 if (!ADS_ERR_OK(ret
)) {
2198 TALLOC_FREE(control_str
);
2201 * Enable the account again.
2203 acct_control
&= ~UF_ACCOUNTDISABLE
;
2205 control_str
= talloc_asprintf(frame
, "%u", acct_control
);
2206 if (control_str
== NULL
) {
2207 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
2211 mods
= ads_init_mods(frame
);
2213 ret
= ADS_ERROR_LDAP(LDAP_NO_MEMORY
);
2217 ads_mod_str(frame
, &mods
, "userAccountControl", control_str
);
2219 ret
= ads_gen_mod(ads
, dn
, mods
);
2220 if (!ADS_ERR_OK(ret
)) {
2224 TALLOC_FREE(control_str
);
2226 ret
= ads_search_dn(ads
, &res
, dn
, attrs
);
2227 ads_msgfree(ads
, res
);
2236 * adds a machine account to the ADS server
2237 * @param ads An intialized ADS_STRUCT
2238 * @param machine_name - the NetBIOS machine name of this account.
2239 * @param account_type A number indicating the type of account to create
2240 * @param org_unit The LDAP path in which to place this account
2241 * @return 0 upon success, or non-zero otherwise
2244 ADS_STATUS
ads_create_machine_acct(ADS_STRUCT
*ads
,
2245 const char *machine_name
,
2246 const char *machine_password
,
2247 const char *org_unit
,
2248 uint32_t etype_list
,
2249 const char *dns_domain_name
)
2252 char *samAccountName
= NULL
;
2253 char *controlstr
= NULL
;
2254 TALLOC_CTX
*ctx
= NULL
;
2256 char *machine_escaped
= NULL
;
2257 char *dns_hostname
= NULL
;
2258 char *new_dn
= NULL
;
2259 char *utf8_pw
= NULL
;
2260 size_t utf8_pw_len
= 0;
2261 char *utf16_pw
= NULL
;
2262 size_t utf16_pw_len
= 0;
2263 struct berval machine_pw_val
;
2265 const char **spn_array
= NULL
;
2266 size_t num_spns
= 0;
2267 const char *spn_prefix
[] = {
2269 "RestrictedKrbHost",
2272 LDAPMessage
*res
= NULL
;
2273 uint32_t acct_control
= UF_WORKSTATION_TRUST_ACCOUNT
;
2275 ctx
= talloc_init("ads_add_machine_acct");
2277 return ADS_ERROR(LDAP_NO_MEMORY
);
2280 machine_escaped
= escape_rdn_val_string_alloc(machine_name
);
2281 if (machine_escaped
== NULL
) {
2282 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
2286 utf8_pw
= talloc_asprintf(ctx
, "\"%s\"", machine_password
);
2287 if (utf8_pw
== NULL
) {
2288 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
2291 utf8_pw_len
= strlen(utf8_pw
);
2293 ok
= convert_string_talloc(ctx
,
2294 CH_UTF8
, CH_UTF16MUNGED
,
2295 utf8_pw
, utf8_pw_len
,
2296 (void *)&utf16_pw
, &utf16_pw_len
);
2298 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
2302 machine_pw_val
= (struct berval
) {
2304 .bv_len
= utf16_pw_len
,
2307 /* Check if the machine account already exists. */
2308 ret
= ads_find_machine_acct(ads
, &res
, machine_escaped
);
2309 if (ADS_ERR_OK(ret
)) {
2310 /* Change the machine account password */
2311 ret
= ads_change_machine_acct(ads
, res
, &machine_pw_val
);
2312 ads_msgfree(ads
, res
);
2316 ads_msgfree(ads
, res
);
2318 new_dn
= talloc_asprintf(ctx
, "cn=%s,%s", machine_escaped
, org_unit
);
2319 if (new_dn
== NULL
) {
2320 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
2324 /* Create machine account */
2326 samAccountName
= talloc_asprintf(ctx
, "%s$", machine_name
);
2327 if (samAccountName
== NULL
) {
2328 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
2332 dns_hostname
= talloc_asprintf(ctx
,
2336 if (dns_hostname
== NULL
) {
2337 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
2341 /* Add dns_hostname SPNs */
2342 for (i
= 0; i
< ARRAY_SIZE(spn_prefix
); i
++) {
2343 char *spn
= talloc_asprintf(ctx
,
2348 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
2352 ok
= add_string_to_array(spn_array
,
2357 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
2362 /* Add machine_name SPNs */
2363 for (i
= 0; i
< ARRAY_SIZE(spn_prefix
); i
++) {
2364 char *spn
= talloc_asprintf(ctx
,
2369 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
2373 ok
= add_string_to_array(spn_array
,
2378 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
2383 /* Make sure to NULL terminate the array */
2384 spn_array
= talloc_realloc(ctx
, spn_array
, const char *, num_spns
+ 1);
2385 if (spn_array
== NULL
) {
2386 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
2389 spn_array
[num_spns
] = NULL
;
2391 controlstr
= talloc_asprintf(ctx
, "%u", acct_control
);
2392 if (controlstr
== NULL
) {
2393 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
2397 mods
= ads_init_mods(ctx
);
2399 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
2403 ads_mod_str(ctx
, &mods
, "objectClass", "Computer");
2404 ads_mod_str(ctx
, &mods
, "SamAccountName", samAccountName
);
2405 ads_mod_str(ctx
, &mods
, "userAccountControl", controlstr
);
2406 ads_mod_str(ctx
, &mods
, "DnsHostName", dns_hostname
);
2407 ads_mod_strlist(ctx
, &mods
, "ServicePrincipalName", spn_array
);
2408 ads_mod_ber(ctx
, &mods
, "unicodePwd", &machine_pw_val
);
2410 ret
= ads_gen_add(ads
, new_dn
, mods
);
2413 SAFE_FREE(machine_escaped
);
2414 talloc_destroy(ctx
);
2420 * move a machine account to another OU on the ADS server
2421 * @param ads - An intialized ADS_STRUCT
2422 * @param machine_name - the NetBIOS machine name of this account.
2423 * @param org_unit - The LDAP path in which to place this account
2424 * @param moved - whether we moved the machine account (optional)
2425 * @return 0 upon success, or non-zero otherwise
2428 ADS_STATUS
ads_move_machine_acct(ADS_STRUCT
*ads
, const char *machine_name
,
2429 const char *org_unit
, bool *moved
)
2433 LDAPMessage
*res
= NULL
;
2434 char *filter
= NULL
;
2435 char *computer_dn
= NULL
;
2437 char *computer_rdn
= NULL
;
2438 bool need_move
= False
;
2440 if (asprintf(&filter
, "(samAccountName=%s$)", machine_name
) == -1) {
2441 rc
= ADS_ERROR(LDAP_NO_MEMORY
);
2445 /* Find pre-existing machine */
2446 rc
= ads_search(ads
, &res
, filter
, NULL
);
2447 if (!ADS_ERR_OK(rc
)) {
2451 computer_dn
= ads_get_dn(ads
, talloc_tos(), res
);
2453 rc
= ADS_ERROR(LDAP_NO_MEMORY
);
2457 parent_dn
= ads_parent_dn(computer_dn
);
2458 if (strequal(parent_dn
, org_unit
)) {
2464 if (asprintf(&computer_rdn
, "CN=%s", machine_name
) == -1) {
2465 rc
= ADS_ERROR(LDAP_NO_MEMORY
);
2469 ldap_status
= ldap_rename_s(ads
->ldap
.ld
, computer_dn
, computer_rdn
,
2470 org_unit
, 1, NULL
, NULL
);
2471 rc
= ADS_ERROR(ldap_status
);
2474 ads_msgfree(ads
, res
);
2476 TALLOC_FREE(computer_dn
);
2477 SAFE_FREE(computer_rdn
);
2479 if (!ADS_ERR_OK(rc
)) {
2491 dump a binary result from ldap
2493 static void dump_binary(ADS_STRUCT
*ads
, const char *field
, struct berval
**values
)
2496 for (i
=0; values
[i
]; i
++) {
2498 printf("%s: ", field
);
2499 for (j
=0; j
<values
[i
]->bv_len
; j
++) {
2500 printf("%02X", (unsigned char)values
[i
]->bv_val
[j
]);
2506 static void dump_guid(ADS_STRUCT
*ads
, const char *field
, struct berval
**values
)
2509 for (i
=0; values
[i
]; i
++) {
2511 DATA_BLOB in
= data_blob_const(values
[i
]->bv_val
, values
[i
]->bv_len
);
2514 status
= GUID_from_ndr_blob(&in
, &guid
);
2515 if (NT_STATUS_IS_OK(status
)) {
2516 printf("%s: %s\n", field
, GUID_string(talloc_tos(), &guid
));
2518 printf("%s: INVALID GUID\n", field
);
2524 dump a sid result from ldap
2526 static void dump_sid(ADS_STRUCT
*ads
, const char *field
, struct berval
**values
)
2529 for (i
=0; values
[i
]; i
++) {
2532 struct dom_sid_buf tmp
;
2533 ret
= sid_parse((const uint8_t *)values
[i
]->bv_val
,
2534 values
[i
]->bv_len
, &sid
);
2538 printf("%s: %s\n", field
, dom_sid_str_buf(&sid
, &tmp
));
2543 dump ntSecurityDescriptor
2545 static void dump_sd(ADS_STRUCT
*ads
, const char *filed
, struct berval
**values
)
2547 TALLOC_CTX
*frame
= talloc_stackframe();
2548 struct security_descriptor
*psd
;
2551 status
= unmarshall_sec_desc(talloc_tos(), (uint8_t *)values
[0]->bv_val
,
2552 values
[0]->bv_len
, &psd
);
2553 if (!NT_STATUS_IS_OK(status
)) {
2554 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
2555 nt_errstr(status
)));
2561 ads_disp_sd(ads
, talloc_tos(), psd
);
2568 dump a string result from ldap
2570 static void dump_string(const char *field
, char **values
)
2573 for (i
=0; values
[i
]; i
++) {
2574 printf("%s: %s\n", field
, values
[i
]);
2579 dump a field from LDAP on stdout
2583 static bool ads_dump_field(ADS_STRUCT
*ads
, char *field
, void **values
, void *data_area
)
2588 void (*handler
)(ADS_STRUCT
*, const char *, struct berval
**);
2590 {"objectGUID", False
, dump_guid
},
2591 {"netbootGUID", False
, dump_guid
},
2592 {"nTSecurityDescriptor", False
, dump_sd
},
2593 {"dnsRecord", False
, dump_binary
},
2594 {"objectSid", False
, dump_sid
},
2595 {"tokenGroups", False
, dump_sid
},
2596 {"tokenGroupsNoGCAcceptable", False
, dump_sid
},
2597 {"tokengroupsGlobalandUniversal", False
, dump_sid
},
2598 {"mS-DS-CreatorSID", False
, dump_sid
},
2599 {"msExchMailboxGuid", False
, dump_guid
},
2604 if (!field
) { /* must be end of an entry */
2609 for (i
=0; handlers
[i
].name
; i
++) {
2610 if (strcasecmp_m(handlers
[i
].name
, field
) == 0) {
2611 if (!values
) /* first time, indicate string or not */
2612 return handlers
[i
].string
;
2613 handlers
[i
].handler(ads
, field
, (struct berval
**) values
);
2617 if (!handlers
[i
].name
) {
2618 if (!values
) /* first time, indicate string conversion */
2620 dump_string(field
, (char **)values
);
2626 * Dump a result from LDAP on stdout
2627 * used for debugging
2628 * @param ads connection to ads server
2629 * @param res Results to dump
2632 void ads_dump(ADS_STRUCT
*ads
, LDAPMessage
*res
)
2634 ads_process_results(ads
, res
, ads_dump_field
, NULL
);
2638 * Walk through results, calling a function for each entry found.
2639 * The function receives a field name, a berval * array of values,
2640 * and a data area passed through from the start. The function is
2641 * called once with null for field and values at the end of each
2643 * @param ads connection to ads server
2644 * @param res Results to process
2645 * @param fn Function for processing each result
2646 * @param data_area user-defined area to pass to function
2648 void ads_process_results(ADS_STRUCT
*ads
, LDAPMessage
*res
,
2649 bool (*fn
)(ADS_STRUCT
*, char *, void **, void *),
2654 size_t converted_size
;
2656 if (!(ctx
= talloc_init("ads_process_results")))
2659 for (msg
= ads_first_entry(ads
, res
); msg
;
2660 msg
= ads_next_entry(ads
, msg
)) {
2664 for (utf8_field
=ldap_first_attribute(ads
->ldap
.ld
,
2665 (LDAPMessage
*)msg
,&b
);
2667 utf8_field
=ldap_next_attribute(ads
->ldap
.ld
,
2668 (LDAPMessage
*)msg
,b
)) {
2669 struct berval
**ber_vals
;
2675 if (!pull_utf8_talloc(ctx
, &field
, utf8_field
,
2678 DEBUG(0,("ads_process_results: "
2679 "pull_utf8_talloc failed: %s",
2683 string
= fn(ads
, field
, NULL
, data_area
);
2688 utf8_vals
= ldap_get_values(ads
->ldap
.ld
,
2689 (LDAPMessage
*)msg
, field
);
2690 p
= discard_const_p(const char *, utf8_vals
);
2691 str_vals
= ads_pull_strvals(ctx
, p
);
2692 fn(ads
, field
, (void **) str_vals
, data_area
);
2693 ldap_value_free(utf8_vals
);
2695 ber_vals
= ldap_get_values_len(ads
->ldap
.ld
,
2696 (LDAPMessage
*)msg
, field
);
2697 fn(ads
, field
, (void **) ber_vals
, data_area
);
2699 ldap_value_free_len(ber_vals
);
2701 ldap_memfree(utf8_field
);
2704 talloc_free_children(ctx
);
2705 fn(ads
, NULL
, NULL
, data_area
); /* completed an entry */
2708 talloc_destroy(ctx
);
2712 * count how many replies are in a LDAPMessage
2713 * @param ads connection to ads server
2714 * @param res Results to count
2715 * @return number of replies
2717 int ads_count_replies(ADS_STRUCT
*ads
, void *res
)
2719 return ldap_count_entries(ads
->ldap
.ld
, (LDAPMessage
*)res
);
2723 * pull the first entry from a ADS result
2724 * @param ads connection to ads server
2725 * @param res Results of search
2726 * @return first entry from result
2728 LDAPMessage
*ads_first_entry(ADS_STRUCT
*ads
, LDAPMessage
*res
)
2730 return ldap_first_entry(ads
->ldap
.ld
, res
);
2734 * pull the next entry from a ADS result
2735 * @param ads connection to ads server
2736 * @param res Results of search
2737 * @return next entry from result
2739 LDAPMessage
*ads_next_entry(ADS_STRUCT
*ads
, LDAPMessage
*res
)
2741 return ldap_next_entry(ads
->ldap
.ld
, res
);
2745 * pull the first message from a ADS result
2746 * @param ads connection to ads server
2747 * @param res Results of search
2748 * @return first message from result
2750 LDAPMessage
*ads_first_message(ADS_STRUCT
*ads
, LDAPMessage
*res
)
2752 return ldap_first_message(ads
->ldap
.ld
, res
);
2756 * pull the next message from a ADS result
2757 * @param ads connection to ads server
2758 * @param res Results of search
2759 * @return next message from result
2761 LDAPMessage
*ads_next_message(ADS_STRUCT
*ads
, LDAPMessage
*res
)
2763 return ldap_next_message(ads
->ldap
.ld
, res
);
2767 * pull a single string from a ADS result
2768 * @param ads connection to ads server
2769 * @param mem_ctx TALLOC_CTX to use for allocating result string
2770 * @param msg Results of search
2771 * @param field Attribute to retrieve
2772 * @return Result string in talloc context
2774 char *ads_pull_string(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
, LDAPMessage
*msg
,
2780 size_t converted_size
;
2782 values
= ldap_get_values(ads
->ldap
.ld
, msg
, field
);
2786 if (values
[0] && pull_utf8_talloc(mem_ctx
, &ux_string
, values
[0],
2791 ldap_value_free(values
);
2796 * pull an array of strings from a ADS result
2797 * @param ads connection to ads server
2798 * @param mem_ctx TALLOC_CTX to use for allocating result string
2799 * @param msg Results of search
2800 * @param field Attribute to retrieve
2801 * @return Result strings in talloc context
2803 char **ads_pull_strings(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
,
2804 LDAPMessage
*msg
, const char *field
,
2809 size_t i
, converted_size
;
2811 values
= ldap_get_values(ads
->ldap
.ld
, msg
, field
);
2815 *num_values
= ldap_count_values(values
);
2817 ret
= talloc_array(mem_ctx
, char *, *num_values
+ 1);
2819 ldap_value_free(values
);
2823 for (i
=0;i
<*num_values
;i
++) {
2824 if (!pull_utf8_talloc(mem_ctx
, &ret
[i
], values
[i
],
2827 ldap_value_free(values
);
2833 ldap_value_free(values
);
2838 * pull an array of strings from a ADS result
2839 * (handle large multivalue attributes with range retrieval)
2840 * @param ads connection to ads server
2841 * @param mem_ctx TALLOC_CTX to use for allocating result string
2842 * @param msg Results of search
2843 * @param field Attribute to retrieve
2844 * @param current_strings strings returned by a previous call to this function
2845 * @param next_attribute The next query should ask for this attribute
2846 * @param num_values How many values did we get this time?
2847 * @param more_values Are there more values to get?
2848 * @return Result strings in talloc context
2850 char **ads_pull_strings_range(ADS_STRUCT
*ads
,
2851 TALLOC_CTX
*mem_ctx
,
2852 LDAPMessage
*msg
, const char *field
,
2853 char **current_strings
,
2854 const char **next_attribute
,
2855 size_t *num_strings
,
2859 char *expected_range_attrib
, *range_attr
;
2860 BerElement
*ptr
= NULL
;
2863 size_t num_new_strings
;
2864 unsigned long int range_start
;
2865 unsigned long int range_end
;
2867 /* we might have been given the whole lot anyway */
2868 if ((strings
= ads_pull_strings(ads
, mem_ctx
, msg
, field
, num_strings
))) {
2869 *more_strings
= False
;
2873 expected_range_attrib
= talloc_asprintf(mem_ctx
, "%s;Range=", field
);
2875 /* look for Range result */
2876 for (attr
= ldap_first_attribute(ads
->ldap
.ld
, (LDAPMessage
*)msg
, &ptr
);
2878 attr
= ldap_next_attribute(ads
->ldap
.ld
, (LDAPMessage
*)msg
, ptr
)) {
2879 /* we ignore the fact that this is utf8, as all attributes are ascii... */
2880 if (strnequal(attr
, expected_range_attrib
, strlen(expected_range_attrib
))) {
2888 /* nothing here - this field is just empty */
2889 *more_strings
= False
;
2893 if (sscanf(&range_attr
[strlen(expected_range_attrib
)], "%lu-%lu",
2894 &range_start
, &range_end
) == 2) {
2895 *more_strings
= True
;
2897 if (sscanf(&range_attr
[strlen(expected_range_attrib
)], "%lu-*",
2898 &range_start
) == 1) {
2899 *more_strings
= False
;
2901 DEBUG(1, ("ads_pull_strings_range: Cannot parse Range attriubte (%s)\n",
2903 ldap_memfree(range_attr
);
2904 *more_strings
= False
;
2909 if ((*num_strings
) != range_start
) {
2910 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) doesn't start at %u, but at %lu"
2911 " - aborting range retreival\n",
2912 range_attr
, (unsigned int)(*num_strings
) + 1, range_start
));
2913 ldap_memfree(range_attr
);
2914 *more_strings
= False
;
2918 new_strings
= ads_pull_strings(ads
, mem_ctx
, msg
, range_attr
, &num_new_strings
);
2920 if (*more_strings
&& ((*num_strings
+ num_new_strings
) != (range_end
+ 1))) {
2921 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) tells us we have %lu "
2922 "strings in this bunch, but we only got %lu - aborting range retreival\n",
2923 range_attr
, (unsigned long int)range_end
- range_start
+ 1,
2924 (unsigned long int)num_new_strings
));
2925 ldap_memfree(range_attr
);
2926 *more_strings
= False
;
2930 strings
= talloc_realloc(mem_ctx
, current_strings
, char *,
2931 *num_strings
+ num_new_strings
);
2933 if (strings
== NULL
) {
2934 ldap_memfree(range_attr
);
2935 *more_strings
= False
;
2939 if (new_strings
&& num_new_strings
) {
2940 memcpy(&strings
[*num_strings
], new_strings
,
2941 sizeof(*new_strings
) * num_new_strings
);
2944 (*num_strings
) += num_new_strings
;
2946 if (*more_strings
) {
2947 *next_attribute
= talloc_asprintf(mem_ctx
,
2952 if (!*next_attribute
) {
2953 DEBUG(1, ("talloc_asprintf for next attribute failed!\n"));
2954 ldap_memfree(range_attr
);
2955 *more_strings
= False
;
2960 ldap_memfree(range_attr
);
2966 * pull a single uint32_t from a ADS result
2967 * @param ads connection to ads server
2968 * @param msg Results of search
2969 * @param field Attribute to retrieve
2970 * @param v Pointer to int to store result
2971 * @return boolean inidicating success
2973 bool ads_pull_uint32(ADS_STRUCT
*ads
, LDAPMessage
*msg
, const char *field
,
2978 values
= ldap_get_values(ads
->ldap
.ld
, msg
, field
);
2982 ldap_value_free(values
);
2986 *v
= atoi(values
[0]);
2987 ldap_value_free(values
);
2992 * pull a single objectGUID from an ADS result
2993 * @param ads connection to ADS server
2994 * @param msg results of search
2995 * @param guid 37-byte area to receive text guid
2996 * @return boolean indicating success
2998 bool ads_pull_guid(ADS_STRUCT
*ads
, LDAPMessage
*msg
, struct GUID
*guid
)
3003 if (!smbldap_talloc_single_blob(talloc_tos(), ads
->ldap
.ld
, msg
, "objectGUID",
3008 status
= GUID_from_ndr_blob(&blob
, guid
);
3009 talloc_free(blob
.data
);
3010 return NT_STATUS_IS_OK(status
);
3015 * pull a single struct dom_sid from a ADS result
3016 * @param ads connection to ads server
3017 * @param msg Results of search
3018 * @param field Attribute to retrieve
3019 * @param sid Pointer to sid to store result
3020 * @return boolean inidicating success
3022 bool ads_pull_sid(ADS_STRUCT
*ads
, LDAPMessage
*msg
, const char *field
,
3023 struct dom_sid
*sid
)
3025 return smbldap_pull_sid(ads
->ldap
.ld
, msg
, field
, sid
);
3029 * pull an array of struct dom_sids from a ADS result
3030 * @param ads connection to ads server
3031 * @param mem_ctx TALLOC_CTX for allocating sid array
3032 * @param msg Results of search
3033 * @param field Attribute to retrieve
3034 * @param sids pointer to sid array to allocate
3035 * @return the count of SIDs pulled
3037 int ads_pull_sids(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
,
3038 LDAPMessage
*msg
, const char *field
, struct dom_sid
**sids
)
3040 struct berval
**values
;
3043 values
= ldap_get_values_len(ads
->ldap
.ld
, msg
, field
);
3048 for (i
=0; values
[i
]; i
++)
3052 (*sids
) = talloc_array(mem_ctx
, struct dom_sid
, i
);
3054 ldap_value_free_len(values
);
3062 for (i
=0; values
[i
]; i
++) {
3064 ret
= sid_parse((const uint8_t *)values
[i
]->bv_val
,
3065 values
[i
]->bv_len
, &(*sids
)[count
]);
3067 struct dom_sid_buf buf
;
3068 DBG_DEBUG("pulling SID: %s\n",
3069 dom_sid_str_buf(&(*sids
)[count
], &buf
));
3074 ldap_value_free_len(values
);
3079 * pull a struct security_descriptor from a ADS result
3080 * @param ads connection to ads server
3081 * @param mem_ctx TALLOC_CTX for allocating sid array
3082 * @param msg Results of search
3083 * @param field Attribute to retrieve
3084 * @param sd Pointer to *struct security_descriptor to store result (talloc()ed)
3085 * @return boolean inidicating success
3087 bool ads_pull_sd(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
,
3088 LDAPMessage
*msg
, const char *field
,
3089 struct security_descriptor
**sd
)
3091 struct berval
**values
;
3094 values
= ldap_get_values_len(ads
->ldap
.ld
, msg
, field
);
3096 if (!values
) return false;
3100 status
= unmarshall_sec_desc(mem_ctx
,
3101 (uint8_t *)values
[0]->bv_val
,
3102 values
[0]->bv_len
, sd
);
3103 if (!NT_STATUS_IS_OK(status
)) {
3104 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
3105 nt_errstr(status
)));
3110 ldap_value_free_len(values
);
3115 * in order to support usernames longer than 21 characters we need to
3116 * use both the sAMAccountName and the userPrincipalName attributes
3117 * It seems that not all users have the userPrincipalName attribute set
3119 * @param ads connection to ads server
3120 * @param mem_ctx TALLOC_CTX for allocating sid array
3121 * @param msg Results of search
3122 * @return the username
3124 char *ads_pull_username(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
,
3130 /* lookup_name() only works on the sAMAccountName to
3131 returning the username portion of userPrincipalName
3132 breaks winbindd_getpwnam() */
3134 ret
= ads_pull_string(ads
, mem_ctx
, msg
, "userPrincipalName");
3135 if (ret
&& (p
= strchr_m(ret
, '@'))) {
3140 return ads_pull_string(ads
, mem_ctx
, msg
, "sAMAccountName");
3145 * find the update serial number - this is the core of the ldap cache
3146 * @param ads connection to ads server
3147 * @param ads connection to ADS server
3148 * @param usn Pointer to retrieved update serial number
3149 * @return status of search
3151 ADS_STATUS
ads_USN(ADS_STRUCT
*ads
, uint32_t *usn
)
3153 const char *attrs
[] = {"highestCommittedUSN", NULL
};
3157 status
= ads_do_search_retry(ads
, "", LDAP_SCOPE_BASE
, "(objectclass=*)", attrs
, &res
);
3158 if (!ADS_ERR_OK(status
))
3161 if (ads_count_replies(ads
, res
) != 1) {
3162 ads_msgfree(ads
, res
);
3163 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED
);
3166 if (!ads_pull_uint32(ads
, res
, "highestCommittedUSN", usn
)) {
3167 ads_msgfree(ads
, res
);
3168 return ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE
);
3171 ads_msgfree(ads
, res
);
3175 /* parse a ADS timestring - typical string is
3176 '20020917091222.0Z0' which means 09:12.22 17th September
3178 static time_t ads_parse_time(const char *str
)
3184 if (sscanf(str
, "%4d%2d%2d%2d%2d%2d",
3185 &tm
.tm_year
, &tm
.tm_mon
, &tm
.tm_mday
,
3186 &tm
.tm_hour
, &tm
.tm_min
, &tm
.tm_sec
) != 6) {
3195 /********************************************************************
3196 ********************************************************************/
3198 ADS_STATUS
ads_current_time(ADS_STRUCT
*ads
)
3200 const char *attrs
[] = {"currentTime", NULL
};
3205 ADS_STRUCT
*ads_s
= ads
;
3207 if (!(ctx
= talloc_init("ads_current_time"))) {
3208 return ADS_ERROR(LDAP_NO_MEMORY
);
3211 /* establish a new ldap tcp session if necessary */
3213 if ( !ads
->ldap
.ld
) {
3214 if ( (ads_s
= ads_init( ads
->server
.realm
, ads
->server
.workgroup
,
3215 ads
->server
.ldap_server
, ADS_SASL_PLAIN
)) == NULL
)
3217 status
= ADS_ERROR(LDAP_NO_MEMORY
);
3220 ads_s
->auth
.flags
= ADS_AUTH_ANON_BIND
;
3221 status
= ads_connect( ads_s
);
3222 if ( !ADS_ERR_OK(status
))
3226 status
= ads_do_search(ads_s
, "", LDAP_SCOPE_BASE
, "(objectclass=*)", attrs
, &res
);
3227 if (!ADS_ERR_OK(status
)) {
3231 timestr
= ads_pull_string(ads_s
, ctx
, res
, "currentTime");
3233 ads_msgfree(ads_s
, res
);
3234 status
= ADS_ERROR(LDAP_NO_RESULTS_RETURNED
);
3238 /* but save the time and offset in the original ADS_STRUCT */
3240 ads
->config
.current_time
= ads_parse_time(timestr
);
3242 if (ads
->config
.current_time
!= 0) {
3243 ads
->auth
.time_offset
= ads
->config
.current_time
- time(NULL
);
3244 DEBUG(4,("KDC time offset is %d seconds\n", ads
->auth
.time_offset
));
3247 ads_msgfree(ads
, res
);
3249 status
= ADS_SUCCESS
;
3252 /* free any temporary ads connections */
3253 if ( ads_s
!= ads
) {
3254 ads_destroy( &ads_s
);
3256 talloc_destroy(ctx
);
3261 /********************************************************************
3262 ********************************************************************/
3264 ADS_STATUS
ads_domain_func_level(ADS_STRUCT
*ads
, uint32_t *val
)
3266 const char *attrs
[] = {"domainFunctionality", NULL
};
3269 ADS_STRUCT
*ads_s
= ads
;
3271 *val
= DS_DOMAIN_FUNCTION_2000
;
3273 /* establish a new ldap tcp session if necessary */
3275 if ( !ads
->ldap
.ld
) {
3276 if ( (ads_s
= ads_init( ads
->server
.realm
, ads
->server
.workgroup
,
3277 ads
->server
.ldap_server
, ADS_SASL_PLAIN
)) == NULL
)
3279 status
= ADS_ERROR_NT(NT_STATUS_NO_MEMORY
);
3282 ads_s
->auth
.flags
= ADS_AUTH_ANON_BIND
;
3283 status
= ads_connect( ads_s
);
3284 if ( !ADS_ERR_OK(status
))
3288 /* If the attribute does not exist assume it is a Windows 2000
3289 functional domain */
3291 status
= ads_do_search(ads_s
, "", LDAP_SCOPE_BASE
, "(objectclass=*)", attrs
, &res
);
3292 if (!ADS_ERR_OK(status
)) {
3293 if ( status
.err
.rc
== LDAP_NO_SUCH_ATTRIBUTE
) {
3294 status
= ADS_SUCCESS
;
3299 if ( !ads_pull_uint32(ads_s
, res
, "domainFunctionality", val
) ) {
3300 DEBUG(5,("ads_domain_func_level: Failed to pull the domainFunctionality attribute.\n"));
3302 DEBUG(3,("ads_domain_func_level: %d\n", *val
));
3305 ads_msgfree(ads
, res
);
3308 /* free any temporary ads connections */
3309 if ( ads_s
!= ads
) {
3310 ads_destroy( &ads_s
);
3317 * find the domain sid for our domain
3318 * @param ads connection to ads server
3319 * @param sid Pointer to domain sid
3320 * @return status of search
3322 ADS_STATUS
ads_domain_sid(ADS_STRUCT
*ads
, struct dom_sid
*sid
)
3324 const char *attrs
[] = {"objectSid", NULL
};
3328 rc
= ads_do_search_retry(ads
, ads
->config
.bind_path
, LDAP_SCOPE_BASE
, "(objectclass=*)",
3330 if (!ADS_ERR_OK(rc
)) return rc
;
3331 if (!ads_pull_sid(ads
, res
, "objectSid", sid
)) {
3332 ads_msgfree(ads
, res
);
3333 return ADS_ERROR_SYSTEM(ENOENT
);
3335 ads_msgfree(ads
, res
);
3341 * find our site name
3342 * @param ads connection to ads server
3343 * @param mem_ctx Pointer to talloc context
3344 * @param site_name Pointer to the sitename
3345 * @return status of search
3347 ADS_STATUS
ads_site_dn(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
, const char **site_name
)
3351 const char *dn
, *service_name
;
3352 const char *attrs
[] = { "dsServiceName", NULL
};
3354 status
= ads_do_search(ads
, "", LDAP_SCOPE_BASE
, "(objectclass=*)", attrs
, &res
);
3355 if (!ADS_ERR_OK(status
)) {
3359 service_name
= ads_pull_string(ads
, mem_ctx
, res
, "dsServiceName");
3360 if (service_name
== NULL
) {
3361 ads_msgfree(ads
, res
);
3362 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED
);
3365 ads_msgfree(ads
, res
);
3367 /* go up three levels */
3368 dn
= ads_parent_dn(ads_parent_dn(ads_parent_dn(service_name
)));
3370 return ADS_ERROR(LDAP_NO_MEMORY
);
3373 *site_name
= talloc_strdup(mem_ctx
, dn
);
3374 if (*site_name
== NULL
) {
3375 return ADS_ERROR(LDAP_NO_MEMORY
);
3380 dsServiceName: CN=NTDS Settings,CN=W2K3DC,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,DC=ber,DC=suse,DC=de
3385 * find the site dn where a machine resides
3386 * @param ads connection to ads server
3387 * @param mem_ctx Pointer to talloc context
3388 * @param computer_name name of the machine
3389 * @param site_name Pointer to the sitename
3390 * @return status of search
3392 ADS_STATUS
ads_site_dn_for_machine(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
, const char *computer_name
, const char **site_dn
)
3396 const char *parent
, *filter
;
3397 char *config_context
= NULL
;
3400 /* shortcut a query */
3401 if (strequal(computer_name
, ads
->config
.ldap_server_name
)) {
3402 return ads_site_dn(ads
, mem_ctx
, site_dn
);
3405 status
= ads_config_path(ads
, mem_ctx
, &config_context
);
3406 if (!ADS_ERR_OK(status
)) {
3410 filter
= talloc_asprintf(mem_ctx
, "(cn=%s)", computer_name
);
3411 if (filter
== NULL
) {
3412 return ADS_ERROR(LDAP_NO_MEMORY
);
3415 status
= ads_do_search(ads
, config_context
, LDAP_SCOPE_SUBTREE
,
3416 filter
, NULL
, &res
);
3417 if (!ADS_ERR_OK(status
)) {
3421 if (ads_count_replies(ads
, res
) != 1) {
3422 ads_msgfree(ads
, res
);
3423 return ADS_ERROR(LDAP_NO_SUCH_OBJECT
);
3426 dn
= ads_get_dn(ads
, mem_ctx
, res
);
3428 ads_msgfree(ads
, res
);
3429 return ADS_ERROR(LDAP_NO_MEMORY
);
3432 /* go up three levels */
3433 parent
= ads_parent_dn(ads_parent_dn(ads_parent_dn(dn
)));
3434 if (parent
== NULL
) {
3435 ads_msgfree(ads
, res
);
3437 return ADS_ERROR(LDAP_NO_MEMORY
);
3440 *site_dn
= talloc_strdup(mem_ctx
, parent
);
3441 if (*site_dn
== NULL
) {
3442 ads_msgfree(ads
, res
);
3444 return ADS_ERROR(LDAP_NO_MEMORY
);
3448 ads_msgfree(ads
, res
);
3454 * get the upn suffixes for a domain
3455 * @param ads connection to ads server
3456 * @param mem_ctx Pointer to talloc context
3457 * @param suffixes Pointer to an array of suffixes
3458 * @param num_suffixes Pointer to the number of suffixes
3459 * @return status of search
3461 ADS_STATUS
ads_upn_suffixes(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
, char ***suffixes
, size_t *num_suffixes
)
3466 char *config_context
= NULL
;
3467 const char *attrs
[] = { "uPNSuffixes", NULL
};
3469 status
= ads_config_path(ads
, mem_ctx
, &config_context
);
3470 if (!ADS_ERR_OK(status
)) {
3474 base
= talloc_asprintf(mem_ctx
, "cn=Partitions,%s", config_context
);
3476 return ADS_ERROR(LDAP_NO_MEMORY
);
3479 status
= ads_search_dn(ads
, &res
, base
, attrs
);
3480 if (!ADS_ERR_OK(status
)) {
3484 if (ads_count_replies(ads
, res
) != 1) {
3485 ads_msgfree(ads
, res
);
3486 return ADS_ERROR(LDAP_NO_SUCH_OBJECT
);
3489 (*suffixes
) = ads_pull_strings(ads
, mem_ctx
, res
, "uPNSuffixes", num_suffixes
);
3490 if ((*suffixes
) == NULL
) {
3491 ads_msgfree(ads
, res
);
3492 return ADS_ERROR(LDAP_NO_MEMORY
);
3495 ads_msgfree(ads
, res
);
3501 * get the joinable ous for a domain
3502 * @param ads connection to ads server
3503 * @param mem_ctx Pointer to talloc context
3504 * @param ous Pointer to an array of ous
3505 * @param num_ous Pointer to the number of ous
3506 * @return status of search
3508 ADS_STATUS
ads_get_joinable_ous(ADS_STRUCT
*ads
,
3509 TALLOC_CTX
*mem_ctx
,
3514 LDAPMessage
*res
= NULL
;
3515 LDAPMessage
*msg
= NULL
;
3516 const char *attrs
[] = { "dn", NULL
};
3519 status
= ads_search(ads
, &res
,
3520 "(|(objectClass=domain)(objectclass=organizationalUnit))",
3522 if (!ADS_ERR_OK(status
)) {
3526 count
= ads_count_replies(ads
, res
);
3528 ads_msgfree(ads
, res
);
3529 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED
);
3532 for (msg
= ads_first_entry(ads
, res
); msg
;
3533 msg
= ads_next_entry(ads
, msg
)) {
3534 const char **p
= discard_const_p(const char *, *ous
);
3537 dn
= ads_get_dn(ads
, talloc_tos(), msg
);
3539 ads_msgfree(ads
, res
);
3540 return ADS_ERROR(LDAP_NO_MEMORY
);
3543 if (!add_string_to_array(mem_ctx
, dn
, &p
, num_ous
)) {
3545 ads_msgfree(ads
, res
);
3546 return ADS_ERROR(LDAP_NO_MEMORY
);
3550 *ous
= discard_const_p(char *, p
);
3553 ads_msgfree(ads
, res
);
3560 * pull a struct dom_sid from an extended dn string
3561 * @param mem_ctx TALLOC_CTX
3562 * @param extended_dn string
3563 * @param flags string type of extended_dn
3564 * @param sid pointer to a struct dom_sid
3565 * @return NT_STATUS_OK on success,
3566 * NT_INVALID_PARAMETER on error,
3567 * NT_STATUS_NOT_FOUND if no SID present
3569 ADS_STATUS
ads_get_sid_from_extended_dn(TALLOC_CTX
*mem_ctx
,
3570 const char *extended_dn
,
3571 enum ads_extended_dn_flags flags
,
3572 struct dom_sid
*sid
)
3577 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER
);
3580 /* otherwise extended_dn gets stripped off */
3581 if ((dn
= talloc_strdup(mem_ctx
, extended_dn
)) == NULL
) {
3582 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER
);
3585 * ADS_EXTENDED_DN_HEX_STRING:
3586 * <GUID=238e1963cb390f4bb032ba0105525a29>;<SID=010500000000000515000000bb68c8fd6b61b427572eb04556040000>;CN=gd,OU=berlin,OU=suse,DC=ber,DC=suse,DC=de
3588 * ADS_EXTENDED_DN_STRING (only with w2k3):
3589 * <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
3591 * Object with no SID, such as an Exchange Public Folder
3592 * <GUID=28907fb4bdf6854993e7f0a10b504e7c>;CN=public,CN=Microsoft Exchange System Objects,DC=sd2k3ms,DC=west,DC=isilon,DC=com
3595 p
= strchr(dn
, ';');
3597 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER
);
3600 if (strncmp(p
, ";<SID=", strlen(";<SID=")) != 0) {
3601 DEBUG(5,("No SID present in extended dn\n"));
3602 return ADS_ERROR_NT(NT_STATUS_NOT_FOUND
);
3605 p
+= strlen(";<SID=");
3609 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER
);
3614 DEBUG(100,("ads_get_sid_from_extended_dn: sid string is %s\n", p
));
3618 case ADS_EXTENDED_DN_STRING
:
3619 if (!string_to_sid(sid
, p
)) {
3620 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER
);
3623 case ADS_EXTENDED_DN_HEX_STRING
: {
3628 buf_len
= strhex_to_str(buf
, sizeof(buf
), p
, strlen(p
));
3630 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER
);
3633 ret
= sid_parse((const uint8_t *)buf
, buf_len
, sid
);
3635 DEBUG(10,("failed to parse sid\n"));
3636 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER
);
3641 DEBUG(10,("unknown extended dn format\n"));
3642 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER
);
3645 return ADS_ERROR_NT(NT_STATUS_OK
);
3648 /********************************************************************
3649 ********************************************************************/
3651 char* ads_get_dnshostname( ADS_STRUCT
*ads
, TALLOC_CTX
*ctx
, const char *machine_name
)
3653 LDAPMessage
*res
= NULL
;
3658 status
= ads_find_machine_acct(ads
, &res
, machine_name
);
3659 if (!ADS_ERR_OK(status
)) {
3660 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
3661 lp_netbios_name()));
3665 if ( (count
= ads_count_replies(ads
, res
)) != 1 ) {
3666 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count
));
3670 if ( (name
= ads_pull_string(ads
, ctx
, res
, "dNSHostName")) == NULL
) {
3671 DEBUG(0,("ads_get_dnshostname: No dNSHostName attribute!\n"));
3675 ads_msgfree(ads
, res
);
3680 /********************************************************************
3681 ********************************************************************/
3683 static char **get_addl_hosts(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
,
3684 LDAPMessage
*msg
, size_t *num_values
)
3686 const char *field
= "msDS-AdditionalDnsHostName";
3687 struct berval
**values
= NULL
;
3689 size_t i
, converted_size
;
3692 * Windows DC implicitly adds a short name for each FQDN added to
3693 * msDS-AdditionalDnsHostName, but it comes with a strage binary
3694 * suffix "\0$" which we should ignore (see bug #14406).
3697 values
= ldap_get_values_len(ads
->ldap
.ld
, msg
, field
);
3698 if (values
== NULL
) {
3702 *num_values
= ldap_count_values_len(values
);
3704 ret
= talloc_array(mem_ctx
, char *, *num_values
+ 1);
3706 ldap_value_free_len(values
);
3710 for (i
= 0; i
< *num_values
; i
++) {
3712 if (!convert_string_talloc(mem_ctx
, CH_UTF8
, CH_UNIX
,
3714 strnlen(values
[i
]->bv_val
,
3716 &ret
[i
], &converted_size
)) {
3717 ldap_value_free_len(values
);
3723 ldap_value_free_len(values
);
3727 ADS_STATUS
ads_get_additional_dns_hostnames(TALLOC_CTX
*mem_ctx
,
3729 const char *machine_name
,
3730 char ***hostnames_array
,
3731 size_t *num_hostnames
)
3734 LDAPMessage
*res
= NULL
;
3737 status
= ads_find_machine_acct(ads
,
3740 if (!ADS_ERR_OK(status
)) {
3741 DEBUG(1,("Host Account for %s not found... skipping operation.\n",
3746 count
= ads_count_replies(ads
, res
);
3748 status
= ADS_ERROR(LDAP_NO_SUCH_OBJECT
);
3752 *hostnames_array
= get_addl_hosts(ads
, mem_ctx
, res
, num_hostnames
);
3753 if (*hostnames_array
== NULL
) {
3754 DEBUG(1, ("Host account for %s does not have msDS-AdditionalDnsHostName.\n",
3756 status
= ADS_ERROR(LDAP_NO_SUCH_OBJECT
);
3761 ads_msgfree(ads
, res
);
3766 /********************************************************************
3767 ********************************************************************/
3769 char* ads_get_upn( ADS_STRUCT
*ads
, TALLOC_CTX
*ctx
, const char *machine_name
)
3771 LDAPMessage
*res
= NULL
;
3776 status
= ads_find_machine_acct(ads
, &res
, machine_name
);
3777 if (!ADS_ERR_OK(status
)) {
3778 DEBUG(0,("ads_get_upn: Failed to find account for %s\n",
3779 lp_netbios_name()));
3783 if ( (count
= ads_count_replies(ads
, res
)) != 1 ) {
3784 DEBUG(1,("ads_get_upn: %d entries returned!\n", count
));
3788 if ( (name
= ads_pull_string(ads
, ctx
, res
, "userPrincipalName")) == NULL
) {
3789 DEBUG(2,("ads_get_upn: No userPrincipalName attribute!\n"));
3793 ads_msgfree(ads
, res
);
3798 /********************************************************************
3799 ********************************************************************/
3801 bool ads_has_samaccountname( ADS_STRUCT
*ads
, TALLOC_CTX
*ctx
, const char *machine_name
)
3803 LDAPMessage
*res
= NULL
;
3809 status
= ads_find_machine_acct(ads
, &res
, machine_name
);
3810 if (!ADS_ERR_OK(status
)) {
3811 DEBUG(0,("ads_has_samaccountname: Failed to find account for %s\n",
3812 lp_netbios_name()));
3816 if ( (count
= ads_count_replies(ads
, res
)) != 1 ) {
3817 DEBUG(1,("ads_has_samaccountname: %d entries returned!\n", count
));
3821 if ( (name
= ads_pull_string(ads
, ctx
, res
, "sAMAccountName")) == NULL
) {
3822 DEBUG(0,("ads_has_samaccountname: No sAMAccountName attribute!\n"));
3826 ads_msgfree(ads
, res
);
3828 ok
= (strlen(name
) > 0);
3836 SAVED CODE
- we used to join via ldap
- remember how we did
this. JRA
.
3839 * Join a machine to a realm
3840 * Creates the machine account and sets the machine password
3841 * @param ads connection to ads server
3842 * @param machine name of host to add
3843 * @param org_unit Organizational unit to place machine in
3844 * @return status of join
3846 ADS_STATUS
ads_join_realm(ADS_STRUCT
*ads
, const char *machine_name
,
3847 uint32_t account_type
, const char *org_unit
)
3850 LDAPMessage
*res
= NULL
;
3853 /* machine name must be lowercase */
3854 machine
= SMB_STRDUP(machine_name
);
3855 strlower_m(machine
);
3858 status = ads_find_machine_acct(ads, (void **)&res, machine);
3859 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
3860 DEBUG(0, ("Host account for %s already exists - deleting old account\n", machine));
3861 status = ads_leave_realm(ads, machine);
3862 if (!ADS_ERR_OK(status)) {
3863 DEBUG(0, ("Failed to delete host '%s' from the '%s' realm.\n",
3864 machine, ads->config.realm));
3869 status
= ads_add_machine_acct(ads
, machine
, account_type
, org_unit
);
3870 if (!ADS_ERR_OK(status
)) {
3871 DEBUG(0, ("ads_join_realm: ads_add_machine_acct failed (%s): %s\n", machine
, ads_errstr(status
)));
3876 status
= ads_find_machine_acct(ads
, (void **)(void *)&res
, machine
);
3877 if (!ADS_ERR_OK(status
)) {
3878 DEBUG(0, ("ads_join_realm: Host account test failed for machine %s\n", machine
));
3884 ads_msgfree(ads
, res
);
3891 * Delete a machine from the realm
3892 * @param ads connection to ads server
3893 * @param hostname Machine to remove
3894 * @return status of delete
3896 ADS_STATUS
ads_leave_realm(ADS_STRUCT
*ads
, const char *hostname
)
3901 char *hostnameDN
, *host
;
3903 LDAPControl ldap_control
;
3904 LDAPControl
* pldap_control
[2] = {NULL
, NULL
};
3906 pldap_control
[0] = &ldap_control
;
3907 memset(&ldap_control
, 0, sizeof(LDAPControl
));
3908 ldap_control
.ldctl_oid
= discard_const_p(char, LDAP_SERVER_TREE_DELETE_OID
);
3910 /* hostname must be lowercase */
3911 host
= SMB_STRDUP(hostname
);
3912 if (!strlower_m(host
)) {
3914 return ADS_ERROR_SYSTEM(EINVAL
);
3917 status
= ads_find_machine_acct(ads
, &res
, host
);
3918 if (!ADS_ERR_OK(status
)) {
3919 DEBUG(0, ("Host account for %s does not exist.\n", host
));
3924 msg
= ads_first_entry(ads
, res
);
3927 return ADS_ERROR_SYSTEM(ENOENT
);
3930 hostnameDN
= ads_get_dn(ads
, talloc_tos(), (LDAPMessage
*)msg
);
3931 if (hostnameDN
== NULL
) {
3933 return ADS_ERROR_SYSTEM(ENOENT
);
3936 rc
= ldap_delete_ext_s(ads
->ldap
.ld
, hostnameDN
, pldap_control
, NULL
);
3938 DEBUG(3,("ldap_delete_ext_s failed with error code %d\n", rc
));
3940 DEBUG(3,("ldap_delete_ext_s succeeded with error code %d\n", rc
));
3943 if (rc
!= LDAP_SUCCESS
) {
3944 const char *attrs
[] = { "cn", NULL
};
3945 LDAPMessage
*msg_sub
;
3947 /* we only search with scope ONE, we do not expect any further
3948 * objects to be created deeper */
3950 status
= ads_do_search_retry(ads
, hostnameDN
,
3951 LDAP_SCOPE_ONELEVEL
,
3952 "(objectclass=*)", attrs
, &res
);
3954 if (!ADS_ERR_OK(status
)) {
3956 TALLOC_FREE(hostnameDN
);
3960 for (msg_sub
= ads_first_entry(ads
, res
); msg_sub
;
3961 msg_sub
= ads_next_entry(ads
, msg_sub
)) {
3965 if ((dn
= ads_get_dn(ads
, talloc_tos(), msg_sub
)) == NULL
) {
3967 TALLOC_FREE(hostnameDN
);
3968 return ADS_ERROR(LDAP_NO_MEMORY
);
3971 status
= ads_del_dn(ads
, dn
);
3972 if (!ADS_ERR_OK(status
)) {
3973 DEBUG(3,("failed to delete dn %s: %s\n", dn
, ads_errstr(status
)));
3976 TALLOC_FREE(hostnameDN
);
3983 /* there should be no subordinate objects anymore */
3984 status
= ads_do_search_retry(ads
, hostnameDN
,
3985 LDAP_SCOPE_ONELEVEL
,
3986 "(objectclass=*)", attrs
, &res
);
3988 if (!ADS_ERR_OK(status
) || ( (ads_count_replies(ads
, res
)) > 0 ) ) {
3990 TALLOC_FREE(hostnameDN
);
3994 /* delete hostnameDN now */
3995 status
= ads_del_dn(ads
, hostnameDN
);
3996 if (!ADS_ERR_OK(status
)) {
3998 DEBUG(3,("failed to delete dn %s: %s\n", hostnameDN
, ads_errstr(status
)));
3999 TALLOC_FREE(hostnameDN
);
4004 TALLOC_FREE(hostnameDN
);
4006 status
= ads_find_machine_acct(ads
, &res
, host
);
4007 if ((status
.error_type
== ENUM_ADS_ERROR_LDAP
) &&
4008 (status
.err
.rc
!= LDAP_NO_SUCH_OBJECT
)) {
4009 DEBUG(3, ("Failed to remove host account.\n"));
4019 * pull all token-sids from an LDAP dn
4020 * @param ads connection to ads server
4021 * @param mem_ctx TALLOC_CTX for allocating sid array
4022 * @param dn of LDAP object
4023 * @param user_sid pointer to struct dom_sid (objectSid)
4024 * @param primary_group_sid pointer to struct dom_sid (self composed)
4025 * @param sids pointer to sid array to allocate
4026 * @param num_sids counter of SIDs pulled
4027 * @return status of token query
4029 ADS_STATUS
ads_get_tokensids(ADS_STRUCT
*ads
,
4030 TALLOC_CTX
*mem_ctx
,
4032 struct dom_sid
*user_sid
,
4033 struct dom_sid
*primary_group_sid
,
4034 struct dom_sid
**sids
,
4038 LDAPMessage
*res
= NULL
;
4040 size_t tmp_num_sids
;
4041 struct dom_sid
*tmp_sids
;
4042 struct dom_sid tmp_user_sid
;
4043 struct dom_sid tmp_primary_group_sid
;
4045 const char *attrs
[] = {
4052 status
= ads_search_retry_dn(ads
, &res
, dn
, attrs
);
4053 if (!ADS_ERR_OK(status
)) {
4057 count
= ads_count_replies(ads
, res
);
4059 ads_msgfree(ads
, res
);
4060 return ADS_ERROR_LDAP(LDAP_NO_SUCH_OBJECT
);
4063 if (!ads_pull_sid(ads
, res
, "objectSid", &tmp_user_sid
)) {
4064 ads_msgfree(ads
, res
);
4065 return ADS_ERROR_LDAP(LDAP_NO_MEMORY
);
4068 if (!ads_pull_uint32(ads
, res
, "primaryGroupID", &pgid
)) {
4069 ads_msgfree(ads
, res
);
4070 return ADS_ERROR_LDAP(LDAP_NO_MEMORY
);
4074 /* hack to compose the primary group sid without knowing the
4077 struct dom_sid domsid
;
4079 sid_copy(&domsid
, &tmp_user_sid
);
4081 if (!sid_split_rid(&domsid
, NULL
)) {
4082 ads_msgfree(ads
, res
);
4083 return ADS_ERROR_LDAP(LDAP_NO_MEMORY
);
4086 if (!sid_compose(&tmp_primary_group_sid
, &domsid
, pgid
)) {
4087 ads_msgfree(ads
, res
);
4088 return ADS_ERROR_LDAP(LDAP_NO_MEMORY
);
4092 tmp_num_sids
= ads_pull_sids(ads
, mem_ctx
, res
, "tokenGroups", &tmp_sids
);
4094 if (tmp_num_sids
== 0 || !tmp_sids
) {
4095 ads_msgfree(ads
, res
);
4096 return ADS_ERROR_LDAP(LDAP_NO_MEMORY
);
4100 *num_sids
= tmp_num_sids
;
4108 *user_sid
= tmp_user_sid
;
4111 if (primary_group_sid
) {
4112 *primary_group_sid
= tmp_primary_group_sid
;
4115 DEBUG(10,("ads_get_tokensids: returned %d sids\n", (int)tmp_num_sids
+ 2));
4117 ads_msgfree(ads
, res
);
4118 return ADS_ERROR_LDAP(LDAP_SUCCESS
);
4122 * Find a sAMAccoutName in LDAP
4123 * @param ads connection to ads server
4124 * @param mem_ctx TALLOC_CTX for allocating sid array
4125 * @param samaccountname to search
4126 * @param uac_ret uint32_t pointer userAccountControl attribute value
4127 * @param dn_ret pointer to dn
4128 * @return status of token query
4130 ADS_STATUS
ads_find_samaccount(ADS_STRUCT
*ads
,
4131 TALLOC_CTX
*mem_ctx
,
4132 const char *samaccountname
,
4134 const char **dn_ret
)
4137 const char *attrs
[] = { "userAccountControl", NULL
};
4139 LDAPMessage
*res
= NULL
;
4143 filter
= talloc_asprintf(mem_ctx
, "(&(objectclass=user)(sAMAccountName=%s))",
4145 if (filter
== NULL
) {
4146 status
= ADS_ERROR_NT(NT_STATUS_NO_MEMORY
);
4150 status
= ads_do_search_all(ads
, ads
->config
.bind_path
,
4152 filter
, attrs
, &res
);
4154 if (!ADS_ERR_OK(status
)) {
4158 if (ads_count_replies(ads
, res
) != 1) {
4159 status
= ADS_ERROR(LDAP_NO_RESULTS_RETURNED
);
4163 dn
= ads_get_dn(ads
, talloc_tos(), res
);
4165 status
= ADS_ERROR(LDAP_NO_MEMORY
);
4169 if (!ads_pull_uint32(ads
, res
, "userAccountControl", &uac
)) {
4170 status
= ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE
);
4179 *dn_ret
= talloc_strdup(mem_ctx
, dn
);
4181 status
= ADS_ERROR(LDAP_NO_MEMORY
);
4187 ads_msgfree(ads
, res
);
4193 * find our configuration path
4194 * @param ads connection to ads server
4195 * @param mem_ctx Pointer to talloc context
4196 * @param config_path Pointer to the config path
4197 * @return status of search
4199 ADS_STATUS
ads_config_path(ADS_STRUCT
*ads
,
4200 TALLOC_CTX
*mem_ctx
,
4204 LDAPMessage
*res
= NULL
;
4205 const char *config_context
= NULL
;
4206 const char *attrs
[] = { "configurationNamingContext", NULL
};
4208 status
= ads_do_search(ads
, "", LDAP_SCOPE_BASE
,
4209 "(objectclass=*)", attrs
, &res
);
4210 if (!ADS_ERR_OK(status
)) {
4214 config_context
= ads_pull_string(ads
, mem_ctx
, res
,
4215 "configurationNamingContext");
4216 ads_msgfree(ads
, res
);
4217 if (!config_context
) {
4218 return ADS_ERROR(LDAP_NO_MEMORY
);
4222 *config_path
= talloc_strdup(mem_ctx
, config_context
);
4223 if (!*config_path
) {
4224 return ADS_ERROR(LDAP_NO_MEMORY
);
4228 return ADS_ERROR(LDAP_SUCCESS
);
4232 * find the displayName of an extended right
4233 * @param ads connection to ads server
4234 * @param config_path The config path
4235 * @param mem_ctx Pointer to talloc context
4236 * @param GUID struct of the rightsGUID
4237 * @return status of search
4239 const char *ads_get_extended_right_name_by_guid(ADS_STRUCT
*ads
,
4240 const char *config_path
,
4241 TALLOC_CTX
*mem_ctx
,
4242 const struct GUID
*rights_guid
)
4245 LDAPMessage
*res
= NULL
;
4247 const char *attrs
[] = { "displayName", NULL
};
4248 const char *result
= NULL
;
4251 if (!ads
|| !mem_ctx
|| !rights_guid
) {
4255 expr
= talloc_asprintf(mem_ctx
, "(rightsGuid=%s)",
4256 GUID_string(mem_ctx
, rights_guid
));
4261 path
= talloc_asprintf(mem_ctx
, "cn=Extended-Rights,%s", config_path
);
4266 rc
= ads_do_search_retry(ads
, path
, LDAP_SCOPE_SUBTREE
,
4268 if (!ADS_ERR_OK(rc
)) {
4272 if (ads_count_replies(ads
, res
) != 1) {
4276 result
= ads_pull_string(ads
, mem_ctx
, res
, "displayName");
4279 ads_msgfree(ads
, res
);
4284 * verify or build and verify an account ou
4285 * @param mem_ctx Pointer to talloc context
4286 * @param ads connection to ads server
4288 * @return status of search
4291 ADS_STATUS
ads_check_ou_dn(TALLOC_CTX
*mem_ctx
,
4293 const char **account_ou
)
4299 if (account_ou
== NULL
) {
4300 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER
);
4303 if (*account_ou
!= NULL
) {
4304 exploded_dn
= ldap_explode_dn(*account_ou
, 0);
4306 ldap_value_free(exploded_dn
);
4311 ou_string
= ads_ou_string(ads
, *account_ou
);
4313 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX
);
4316 name
= talloc_asprintf(mem_ctx
, "%s,%s", ou_string
,
4317 ads
->config
.bind_path
);
4318 SAFE_FREE(ou_string
);
4321 return ADS_ERROR_LDAP(LDAP_NO_MEMORY
);
4324 exploded_dn
= ldap_explode_dn(name
, 0);
4326 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX
);
4328 ldap_value_free(exploded_dn
);