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/>.
25 #include "lib/ldb/include/includes.h"
31 * @brief basic ldap client-side routines for ads server communications
33 * The routines contained here should do the necessary ldap calls for
36 * Important note: attribute names passed into ads_ routines must
37 * already be in UTF-8 format. We do not convert them because in almost
38 * all cases, they are just ascii (which is represented with the same
39 * codepoints in UTF-8). This may have to change at some point
43 #define LDAP_SERVER_TREE_DELETE_OID "1.2.840.113556.1.4.805"
45 static SIG_ATOMIC_T gotalarm
;
47 /***************************************************************
48 Signal function to tell us we timed out.
49 ****************************************************************/
51 static void gotalarm_sig(void)
56 LDAP
*ldap_open_with_timeout(const char *server
, int port
, unsigned int to
)
61 DEBUG(10, ("Opening connection to LDAP server '%s:%d', timeout "
62 "%u seconds\n", server
, port
, to
));
66 CatchSignal(SIGALRM
, SIGNAL_CAST gotalarm_sig
);
68 /* End setup timeout. */
70 ldp
= ldap_open(server
, port
);
73 DEBUG(2,("Could not open connection to LDAP server %s:%d: %s\n",
74 server
, port
, strerror(errno
)));
76 DEBUG(10, ("Connected to LDAP server '%s:%d'\n", server
, port
));
79 /* Teardown timeout. */
80 CatchSignal(SIGALRM
, SIGNAL_CAST SIG_IGN
);
86 static int ldap_search_with_timeout(LDAP
*ld
,
87 LDAP_CONST
char *base
,
89 LDAP_CONST
char *filter
,
97 struct timeval timeout
;
100 /* Setup timeout for the ldap_search_ext_s call - local and remote. */
101 timeout
.tv_sec
= lp_ldap_timeout();
104 /* Setup alarm timeout.... Do we need both of these ? JRA. */
106 CatchSignal(SIGALRM
, SIGNAL_CAST gotalarm_sig
);
107 alarm(lp_ldap_timeout());
108 /* End setup timeout. */
110 result
= ldap_search_ext_s(ld
, base
, scope
, filter
, attrs
,
111 attrsonly
, sctrls
, cctrls
, &timeout
,
114 /* Teardown timeout. */
115 CatchSignal(SIGALRM
, SIGNAL_CAST SIG_IGN
);
119 return LDAP_TIMELIMIT_EXCEEDED
;
124 /**********************************************
125 Do client and server sitename match ?
126 **********************************************/
128 bool ads_sitename_match(ADS_STRUCT
*ads
)
130 if (ads
->config
.server_site_name
== NULL
&&
131 ads
->config
.client_site_name
== NULL
) {
132 DEBUG(10,("ads_sitename_match: both null\n"));
135 if (ads
->config
.server_site_name
&&
136 ads
->config
.client_site_name
&&
137 strequal(ads
->config
.server_site_name
,
138 ads
->config
.client_site_name
)) {
139 DEBUG(10,("ads_sitename_match: name %s match\n", ads
->config
.server_site_name
));
142 DEBUG(10,("ads_sitename_match: no match between server: %s and client: %s\n",
143 ads
->config
.server_site_name
? ads
->config
.server_site_name
: "NULL",
144 ads
->config
.client_site_name
? ads
->config
.client_site_name
: "NULL"));
148 /**********************************************
149 Is this the closest DC ?
150 **********************************************/
152 bool ads_closest_dc(ADS_STRUCT
*ads
)
154 if (ads
->config
.flags
& NBT_SERVER_CLOSEST
) {
155 DEBUG(10,("ads_closest_dc: NBT_SERVER_CLOSEST flag set\n"));
159 /* not sure if this can ever happen */
160 if (ads_sitename_match(ads
)) {
161 DEBUG(10,("ads_closest_dc: NBT_SERVER_CLOSEST flag not set but sites match\n"));
165 if (ads
->config
.client_site_name
== NULL
) {
166 DEBUG(10,("ads_closest_dc: client belongs to no site\n"));
170 DEBUG(10,("ads_closest_dc: %s is not the closest DC\n",
171 ads
->config
.ldap_server_name
));
178 try a connection to a given ldap server, returning True and setting the servers IP
179 in the ads struct if successful
181 bool ads_try_connect(ADS_STRUCT
*ads
, const char *server
)
184 struct nbt_cldap_netlogon_5 cldap_reply
;
185 TALLOC_CTX
*mem_ctx
= NULL
;
188 if (!server
|| !*server
) {
192 DEBUG(5,("ads_try_connect: sending CLDAP request to %s (realm: %s)\n",
193 server
, ads
->server
.realm
));
195 mem_ctx
= talloc_init("ads_try_connect");
197 DEBUG(0,("out of memory\n"));
201 /* this copes with inet_ntoa brokenness */
203 srv
= SMB_STRDUP(server
);
205 ZERO_STRUCT( cldap_reply
);
207 if ( !ads_cldap_netlogon_5(mem_ctx
, srv
, ads
->server
.realm
, &cldap_reply
) ) {
208 DEBUG(3,("ads_try_connect: CLDAP request %s failed.\n", srv
));
213 /* Check the CLDAP reply flags */
215 if ( !(cldap_reply
.server_type
& NBT_SERVER_LDAP
) ) {
216 DEBUG(1,("ads_try_connect: %s's CLDAP reply says it is not an LDAP server!\n",
222 /* Fill in the ads->config values */
224 SAFE_FREE(ads
->config
.realm
);
225 SAFE_FREE(ads
->config
.bind_path
);
226 SAFE_FREE(ads
->config
.ldap_server_name
);
227 SAFE_FREE(ads
->config
.server_site_name
);
228 SAFE_FREE(ads
->config
.client_site_name
);
229 SAFE_FREE(ads
->server
.workgroup
);
231 ads
->config
.flags
= cldap_reply
.server_type
;
232 ads
->config
.ldap_server_name
= SMB_STRDUP(cldap_reply
.pdc_dns_name
);
233 ads
->config
.realm
= SMB_STRDUP(cldap_reply
.dns_domain
);
234 strupper_m(ads
->config
.realm
);
235 ads
->config
.bind_path
= ads_build_dn(ads
->config
.realm
);
236 if (*cldap_reply
.server_site
) {
237 ads
->config
.server_site_name
=
238 SMB_STRDUP(cldap_reply
.server_site
);
240 if (*cldap_reply
.client_site
) {
241 ads
->config
.client_site_name
=
242 SMB_STRDUP(cldap_reply
.client_site
);
244 ads
->server
.workgroup
= SMB_STRDUP(cldap_reply
.domain
);
246 ads
->ldap
.port
= LDAP_PORT
;
247 if (!interpret_string_addr(&ads
->ldap
.ss
, srv
, 0)) {
248 DEBUG(1,("ads_try_connect: unable to convert %s "
255 /* Store our site name. */
256 sitename_store( cldap_reply
.domain
, cldap_reply
.client_site
);
257 sitename_store( cldap_reply
.dns_domain
, cldap_reply
.client_site
);
262 TALLOC_FREE(mem_ctx
);
267 /**********************************************************************
268 Try to find an AD dc using our internal name resolution routines
269 Try the realm first and then then workgroup name if netbios is not
271 **********************************************************************/
273 static NTSTATUS
ads_find_dc(ADS_STRUCT
*ads
)
275 const char *c_domain
;
278 struct ip_service
*ip_list
;
281 bool got_realm
= False
;
282 bool use_own_domain
= False
;
284 NTSTATUS status
= NT_STATUS_UNSUCCESSFUL
;
286 /* if the realm and workgroup are both empty, assume they are ours */
289 c_realm
= ads
->server
.realm
;
291 if ( !c_realm
|| !*c_realm
) {
292 /* special case where no realm and no workgroup means our own */
293 if ( !ads
->server
.workgroup
|| !*ads
->server
.workgroup
) {
294 use_own_domain
= True
;
295 c_realm
= lp_realm();
299 if (c_realm
&& *c_realm
)
302 /* we need to try once with the realm name and fallback to the
303 netbios domain name if we fail (if netbios has not been disabled */
305 if ( !got_realm
&& !lp_disable_netbios() ) {
306 c_realm
= ads
->server
.workgroup
;
307 if (!c_realm
|| !*c_realm
) {
308 if ( use_own_domain
)
309 c_realm
= lp_workgroup();
313 if ( !c_realm
|| !*c_realm
) {
314 DEBUG(0,("ads_find_dc: no realm or workgroup! Don't know what to do\n"));
315 return NT_STATUS_INVALID_PARAMETER
; /* rather need MISSING_PARAMETER ... */
318 if ( use_own_domain
) {
319 c_domain
= lp_workgroup();
321 c_domain
= ads
->server
.workgroup
;
328 * In case of LDAP we use get_dc_name() as that
329 * creates the custom krb5.conf file
331 if (!(ads
->auth
.flags
& ADS_AUTH_NO_BIND
)) {
333 struct sockaddr_storage ip_out
;
335 DEBUG(6,("ads_find_dc: (ldap) looking for %s '%s'\n",
336 (got_realm
? "realm" : "domain"), realm
));
338 if (get_dc_name(domain
, realm
, srv_name
, &ip_out
)) {
340 * we call ads_try_connect() to fill in the
341 * ads->config details
343 if (ads_try_connect(ads
, srv_name
)) {
348 return NT_STATUS_NO_LOGON_SERVERS
;
351 sitename
= sitename_fetch(realm
);
355 DEBUG(6,("ads_find_dc: (cldap) looking for %s '%s'\n",
356 (got_realm
? "realm" : "domain"), realm
));
358 status
= get_sorted_dc_list(realm
, sitename
, &ip_list
, &count
, got_realm
);
359 if (!NT_STATUS_IS_OK(status
)) {
360 /* fall back to netbios if we can */
361 if ( got_realm
&& !lp_disable_netbios() ) {
370 /* if we fail this loop, then giveup since all the IP addresses returned were dead */
371 for ( i
=0; i
<count
; i
++ ) {
372 char server
[INET6_ADDRSTRLEN
];
374 print_sockaddr(server
, sizeof(server
), &ip_list
[i
].ss
);
376 if ( !NT_STATUS_IS_OK(check_negative_conn_cache(realm
, server
)) )
380 /* realm in this case is a workgroup name. We need
381 to ignore any IP addresses in the negative connection
382 cache that match ip addresses returned in the ad realm
383 case. It sucks that I have to reproduce the logic above... */
384 c_realm
= ads
->server
.realm
;
385 if ( !c_realm
|| !*c_realm
) {
386 if ( !ads
->server
.workgroup
|| !*ads
->server
.workgroup
) {
387 c_realm
= lp_realm();
390 if (c_realm
&& *c_realm
&&
391 !NT_STATUS_IS_OK(check_negative_conn_cache(c_realm
, server
))) {
392 /* Ensure we add the workgroup name for this
393 IP address as negative too. */
394 add_failed_connection_entry( realm
, server
, NT_STATUS_UNSUCCESSFUL
);
399 if ( ads_try_connect(ads
, server
) ) {
405 /* keep track of failures */
406 add_failed_connection_entry( realm
, server
, NT_STATUS_UNSUCCESSFUL
);
411 /* In case we failed to contact one of our closest DC on our site we
412 * need to try to find another DC, retry with a site-less SRV DNS query
416 DEBUG(1,("ads_find_dc: failed to find a valid DC on our site (%s), "
417 "trying to find another DC\n", sitename
));
419 namecache_delete(realm
, 0x1C);
423 return NT_STATUS_NO_LOGON_SERVERS
;
428 * Connect to the LDAP server
429 * @param ads Pointer to an existing ADS_STRUCT
430 * @return status of connection
432 ADS_STATUS
ads_connect(ADS_STRUCT
*ads
)
434 int version
= LDAP_VERSION3
;
437 char addr
[INET6_ADDRSTRLEN
];
439 ZERO_STRUCT(ads
->ldap
);
440 ads
->ldap
.last_attempt
= time(NULL
);
441 ads
->ldap
.wrap_type
= ADS_SASLWRAP_TYPE_PLAIN
;
443 /* try with a user specified server */
445 if (DEBUGLEVEL
>= 11) {
446 char *s
= NDR_PRINT_STRUCT_STRING(talloc_tos(), ads_struct
, ads
);
447 DEBUG(11,("ads_connect: entering\n"));
448 DEBUGADD(11,("%s\n", s
));
452 if (ads
->server
.ldap_server
&&
453 ads_try_connect(ads
, ads
->server
.ldap_server
)) {
457 ntstatus
= ads_find_dc(ads
);
458 if (NT_STATUS_IS_OK(ntstatus
)) {
462 status
= ADS_ERROR_NT(ntstatus
);
467 print_sockaddr(addr
, sizeof(addr
), &ads
->ldap
.ss
);
468 DEBUG(3,("Successfully contacted LDAP server %s\n", addr
));
470 if (!ads
->auth
.user_name
) {
471 /* Must use the userPrincipalName value here or sAMAccountName
472 and not servicePrincipalName; found by Guenther Deschner */
474 asprintf(&ads
->auth
.user_name
, "%s$", global_myname() );
477 if (!ads
->auth
.realm
) {
478 ads
->auth
.realm
= SMB_STRDUP(ads
->config
.realm
);
481 if (!ads
->auth
.kdc_server
) {
482 print_sockaddr(addr
, sizeof(addr
), &ads
->ldap
.ss
);
483 ads
->auth
.kdc_server
= SMB_STRDUP(addr
);
487 /* this is a really nasty hack to avoid ADS DNS problems. It needs a patch
488 to MIT kerberos to work (tridge) */
491 asprintf(&env
, "KRB5_KDC_ADDRESS_%s", ads
->config
.realm
);
492 setenv(env
, ads
->auth
.kdc_server
, 1);
497 /* If the caller() requested no LDAP bind, then we are done */
499 if (ads
->auth
.flags
& ADS_AUTH_NO_BIND
) {
500 status
= ADS_SUCCESS
;
504 ads
->ldap
.mem_ctx
= talloc_init("ads LDAP connection memory");
505 if (!ads
->ldap
.mem_ctx
) {
506 status
= ADS_ERROR_NT(NT_STATUS_NO_MEMORY
);
510 /* Otherwise setup the TCP LDAP session */
512 ads
->ldap
.ld
= ldap_open_with_timeout(ads
->config
.ldap_server_name
,
513 LDAP_PORT
, lp_ldap_timeout());
514 if (ads
->ldap
.ld
== NULL
) {
515 status
= ADS_ERROR(LDAP_OPERATIONS_ERROR
);
518 DEBUG(3,("Connected to LDAP server %s\n", ads
->config
.ldap_server_name
));
520 /* cache the successful connection for workgroup and realm */
521 if (ads_closest_dc(ads
)) {
522 saf_store( ads
->server
.workgroup
, ads
->config
.ldap_server_name
);
523 saf_store( ads
->server
.realm
, ads
->config
.ldap_server_name
);
526 ldap_set_option(ads
->ldap
.ld
, LDAP_OPT_PROTOCOL_VERSION
, &version
);
528 status
= ADS_ERROR(smb_ldap_start_tls(ads
->ldap
.ld
, version
));
529 if (!ADS_ERR_OK(status
)) {
533 /* fill in the current time and offsets */
535 status
= ads_current_time( ads
);
536 if ( !ADS_ERR_OK(status
) ) {
540 /* Now do the bind */
542 if (ads
->auth
.flags
& ADS_AUTH_ANON_BIND
) {
543 status
= ADS_ERROR(ldap_simple_bind_s(ads
->ldap
.ld
, NULL
, NULL
));
547 if (ads
->auth
.flags
& ADS_AUTH_SIMPLE_BIND
) {
548 status
= ADS_ERROR(ldap_simple_bind_s(ads
->ldap
.ld
, ads
->auth
.user_name
, ads
->auth
.password
));
552 status
= ads_sasl_bind(ads
);
555 if (DEBUGLEVEL
>= 11) {
556 char *s
= NDR_PRINT_STRUCT_STRING(talloc_tos(), ads_struct
, ads
);
557 DEBUG(11,("ads_connect: leaving with: %s\n",
558 ads_errstr(status
)));
559 DEBUGADD(11,("%s\n", s
));
567 * Disconnect the LDAP server
568 * @param ads Pointer to an existing ADS_STRUCT
570 void ads_disconnect(ADS_STRUCT
*ads
)
573 ldap_unbind(ads
->ldap
.ld
);
576 if (ads
->ldap
.wrap_ops
&& ads
->ldap
.wrap_ops
->disconnect
) {
577 ads
->ldap
.wrap_ops
->disconnect(ads
);
579 if (ads
->ldap
.mem_ctx
) {
580 talloc_free(ads
->ldap
.mem_ctx
);
582 ZERO_STRUCT(ads
->ldap
);
586 Duplicate a struct berval into talloc'ed memory
588 static struct berval
*dup_berval(TALLOC_CTX
*ctx
, const struct berval
*in_val
)
590 struct berval
*value
;
592 if (!in_val
) return NULL
;
594 value
= TALLOC_ZERO_P(ctx
, struct berval
);
597 if (in_val
->bv_len
== 0) return value
;
599 value
->bv_len
= in_val
->bv_len
;
600 value
->bv_val
= (char *)TALLOC_MEMDUP(ctx
, in_val
->bv_val
,
606 Make a values list out of an array of (struct berval *)
608 static struct berval
**ads_dup_values(TALLOC_CTX
*ctx
,
609 const struct berval
**in_vals
)
611 struct berval
**values
;
614 if (!in_vals
) return NULL
;
615 for (i
=0; in_vals
[i
]; i
++)
617 values
= TALLOC_ZERO_ARRAY(ctx
, struct berval
*, i
+1);
618 if (!values
) return NULL
;
620 for (i
=0; in_vals
[i
]; i
++) {
621 values
[i
] = dup_berval(ctx
, in_vals
[i
]);
627 UTF8-encode a values list out of an array of (char *)
629 static char **ads_push_strvals(TALLOC_CTX
*ctx
, const char **in_vals
)
634 if (!in_vals
) return NULL
;
635 for (i
=0; in_vals
[i
]; i
++)
637 values
= TALLOC_ZERO_ARRAY(ctx
, char *, i
+1);
638 if (!values
) return NULL
;
640 for (i
=0; in_vals
[i
]; i
++) {
641 if (push_utf8_talloc(ctx
, &values
[i
], in_vals
[i
]) == (size_t) -1) {
650 Pull a (char *) array out of a UTF8-encoded values list
652 static char **ads_pull_strvals(TALLOC_CTX
*ctx
, const char **in_vals
)
657 if (!in_vals
) return NULL
;
658 for (i
=0; in_vals
[i
]; i
++)
660 values
= TALLOC_ZERO_ARRAY(ctx
, char *, i
+1);
661 if (!values
) return NULL
;
663 for (i
=0; in_vals
[i
]; i
++) {
664 pull_utf8_talloc(ctx
, &values
[i
], in_vals
[i
]);
670 * Do a search with paged results. cookie must be null on the first
671 * call, and then returned on each subsequent call. It will be null
672 * again when the entire search is complete
673 * @param ads connection to ads server
674 * @param bind_path Base dn for the search
675 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
676 * @param expr Search expression - specified in local charset
677 * @param attrs Attributes to retrieve - specified in utf8 or ascii
678 * @param res ** which will contain results - free res* with ads_msgfree()
679 * @param count Number of entries retrieved on this page
680 * @param cookie The paged results cookie to be returned on subsequent calls
681 * @return status of search
683 static ADS_STATUS
ads_do_paged_search_args(ADS_STRUCT
*ads
,
684 const char *bind_path
,
685 int scope
, const char *expr
,
686 const char **attrs
, void *args
,
688 int *count
, struct berval
**cookie
)
691 char *utf8_expr
, *utf8_path
, **search_attrs
;
692 LDAPControl PagedResults
, NoReferrals
, ExternalCtrl
, *controls
[4], **rcontrols
;
693 BerElement
*cookie_be
= NULL
;
694 struct berval
*cookie_bv
= NULL
;
695 BerElement
*ext_be
= NULL
;
696 struct berval
*ext_bv
= NULL
;
699 ads_control
*external_control
= (ads_control
*) args
;
703 if (!(ctx
= talloc_init("ads_do_paged_search_args")))
704 return ADS_ERROR(LDAP_NO_MEMORY
);
706 /* 0 means the conversion worked but the result was empty
707 so we only fail if it's -1. In any case, it always
708 at least nulls out the dest */
709 if ((push_utf8_talloc(ctx
, &utf8_expr
, expr
) == (size_t)-1) ||
710 (push_utf8_talloc(ctx
, &utf8_path
, bind_path
) == (size_t)-1)) {
715 if (!attrs
|| !(*attrs
))
718 /* This would be the utf8-encoded version...*/
719 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
720 if (!(str_list_copy(talloc_tos(), &search_attrs
, attrs
))) {
726 /* Paged results only available on ldap v3 or later */
727 ldap_get_option(ads
->ldap
.ld
, LDAP_OPT_PROTOCOL_VERSION
, &version
);
728 if (version
< LDAP_VERSION3
) {
729 rc
= LDAP_NOT_SUPPORTED
;
733 cookie_be
= ber_alloc_t(LBER_USE_DER
);
735 ber_printf(cookie_be
, "{iO}", (ber_int_t
) 1000, *cookie
);
736 ber_bvfree(*cookie
); /* don't need it from last time */
739 ber_printf(cookie_be
, "{io}", (ber_int_t
) 1000, "", 0);
741 ber_flatten(cookie_be
, &cookie_bv
);
742 PagedResults
.ldctl_oid
= CONST_DISCARD(char *, ADS_PAGE_CTL_OID
);
743 PagedResults
.ldctl_iscritical
= (char) 1;
744 PagedResults
.ldctl_value
.bv_len
= cookie_bv
->bv_len
;
745 PagedResults
.ldctl_value
.bv_val
= cookie_bv
->bv_val
;
747 NoReferrals
.ldctl_oid
= CONST_DISCARD(char *, ADS_NO_REFERRALS_OID
);
748 NoReferrals
.ldctl_iscritical
= (char) 0;
749 NoReferrals
.ldctl_value
.bv_len
= 0;
750 NoReferrals
.ldctl_value
.bv_val
= CONST_DISCARD(char *, "");
752 if (external_control
&&
753 (strequal(external_control
->control
, ADS_EXTENDED_DN_OID
) ||
754 strequal(external_control
->control
, ADS_SD_FLAGS_OID
))) {
756 ExternalCtrl
.ldctl_oid
= CONST_DISCARD(char *, external_control
->control
);
757 ExternalCtrl
.ldctl_iscritical
= (char) external_control
->critical
;
759 /* win2k does not accept a ldctl_value beeing passed in */
761 if (external_control
->val
!= 0) {
763 if ((ext_be
= ber_alloc_t(LBER_USE_DER
)) == NULL
) {
768 if ((ber_printf(ext_be
, "{i}", (ber_int_t
) external_control
->val
)) == -1) {
772 if ((ber_flatten(ext_be
, &ext_bv
)) == -1) {
777 ExternalCtrl
.ldctl_value
.bv_len
= ext_bv
->bv_len
;
778 ExternalCtrl
.ldctl_value
.bv_val
= ext_bv
->bv_val
;
781 ExternalCtrl
.ldctl_value
.bv_len
= 0;
782 ExternalCtrl
.ldctl_value
.bv_val
= NULL
;
785 controls
[0] = &NoReferrals
;
786 controls
[1] = &PagedResults
;
787 controls
[2] = &ExternalCtrl
;
791 controls
[0] = &NoReferrals
;
792 controls
[1] = &PagedResults
;
796 /* we need to disable referrals as the openldap libs don't
797 handle them and paged results at the same time. Using them
798 together results in the result record containing the server
799 page control being removed from the result list (tridge/jmcd)
801 leaving this in despite the control that says don't generate
802 referrals, in case the server doesn't support it (jmcd)
804 ldap_set_option(ads
->ldap
.ld
, LDAP_OPT_REFERRALS
, LDAP_OPT_OFF
);
806 rc
= ldap_search_with_timeout(ads
->ldap
.ld
, utf8_path
, scope
, utf8_expr
,
807 search_attrs
, 0, controls
,
809 (LDAPMessage
**)res
);
811 ber_free(cookie_be
, 1);
812 ber_bvfree(cookie_bv
);
815 DEBUG(3,("ads_do_paged_search_args: ldap_search_with_timeout(%s) -> %s\n", expr
,
816 ldap_err2string(rc
)));
820 rc
= ldap_parse_result(ads
->ldap
.ld
, *res
, NULL
, NULL
, NULL
,
821 NULL
, &rcontrols
, 0);
827 for (i
=0; rcontrols
[i
]; i
++) {
828 if (strcmp(ADS_PAGE_CTL_OID
, rcontrols
[i
]->ldctl_oid
) == 0) {
829 cookie_be
= ber_init(&rcontrols
[i
]->ldctl_value
);
830 ber_scanf(cookie_be
,"{iO}", (ber_int_t
*) count
,
832 /* the berval is the cookie, but must be freed when
834 if (cookie_bv
->bv_len
) /* still more to do */
835 *cookie
=ber_bvdup(cookie_bv
);
838 ber_bvfree(cookie_bv
);
839 ber_free(cookie_be
, 1);
843 ldap_controls_free(rcontrols
);
856 /* if/when we decide to utf8-encode attrs, take out this next line */
857 TALLOC_FREE(search_attrs
);
859 return ADS_ERROR(rc
);
862 static ADS_STATUS
ads_do_paged_search(ADS_STRUCT
*ads
, const char *bind_path
,
863 int scope
, const char *expr
,
864 const char **attrs
, LDAPMessage
**res
,
865 int *count
, struct berval
**cookie
)
867 return ads_do_paged_search_args(ads
, bind_path
, scope
, expr
, attrs
, NULL
, res
, count
, cookie
);
872 * Get all results for a search. This uses ads_do_paged_search() to return
873 * all entries in a large search.
874 * @param ads connection to ads server
875 * @param bind_path Base dn for the search
876 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
877 * @param expr Search expression
878 * @param attrs Attributes to retrieve
879 * @param res ** which will contain results - free res* with ads_msgfree()
880 * @return status of search
882 ADS_STATUS
ads_do_search_all_args(ADS_STRUCT
*ads
, const char *bind_path
,
883 int scope
, const char *expr
,
884 const char **attrs
, void *args
,
887 struct berval
*cookie
= NULL
;
892 status
= ads_do_paged_search_args(ads
, bind_path
, scope
, expr
, attrs
, args
, res
,
895 if (!ADS_ERR_OK(status
))
898 #ifdef HAVE_LDAP_ADD_RESULT_ENTRY
900 LDAPMessage
*res2
= NULL
;
902 LDAPMessage
*msg
, *next
;
904 status2
= ads_do_paged_search_args(ads
, bind_path
, scope
, expr
,
905 attrs
, args
, &res2
, &count
, &cookie
);
907 if (!ADS_ERR_OK(status2
)) break;
909 /* this relies on the way that ldap_add_result_entry() works internally. I hope
910 that this works on all ldap libs, but I have only tested with openldap */
911 for (msg
= ads_first_message(ads
, res2
); msg
; msg
= next
) {
912 next
= ads_next_message(ads
, msg
);
913 ldap_add_result_entry((LDAPMessage
**)res
, msg
);
915 /* note that we do not free res2, as the memory is now
916 part of the main returned list */
919 DEBUG(0, ("no ldap_add_result_entry() support in LDAP libs!\n"));
920 status
= ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL
);
926 ADS_STATUS
ads_do_search_all(ADS_STRUCT
*ads
, const char *bind_path
,
927 int scope
, const char *expr
,
928 const char **attrs
, LDAPMessage
**res
)
930 return ads_do_search_all_args(ads
, bind_path
, scope
, expr
, attrs
, NULL
, res
);
933 ADS_STATUS
ads_do_search_all_sd_flags(ADS_STRUCT
*ads
, const char *bind_path
,
934 int scope
, const char *expr
,
935 const char **attrs
, uint32 sd_flags
,
940 args
.control
= ADS_SD_FLAGS_OID
;
942 args
.critical
= True
;
944 return ads_do_search_all_args(ads
, bind_path
, scope
, expr
, attrs
, &args
, res
);
949 * Run a function on all results for a search. Uses ads_do_paged_search() and
950 * runs the function as each page is returned, using ads_process_results()
951 * @param ads connection to ads server
952 * @param bind_path Base dn for the search
953 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
954 * @param expr Search expression - specified in local charset
955 * @param attrs Attributes to retrieve - specified in UTF-8 or ascii
956 * @param fn Function which takes attr name, values list, and data_area
957 * @param data_area Pointer which is passed to function on each call
958 * @return status of search
960 ADS_STATUS
ads_do_search_all_fn(ADS_STRUCT
*ads
, const char *bind_path
,
961 int scope
, const char *expr
, const char **attrs
,
962 bool (*fn
)(ADS_STRUCT
*, char *, void **, void *),
965 struct berval
*cookie
= NULL
;
970 status
= ads_do_paged_search(ads
, bind_path
, scope
, expr
, attrs
, &res
,
973 if (!ADS_ERR_OK(status
)) return status
;
975 ads_process_results(ads
, res
, fn
, data_area
);
976 ads_msgfree(ads
, res
);
979 status
= ads_do_paged_search(ads
, bind_path
, scope
, expr
, attrs
,
980 &res
, &count
, &cookie
);
982 if (!ADS_ERR_OK(status
)) break;
984 ads_process_results(ads
, res
, fn
, data_area
);
985 ads_msgfree(ads
, res
);
992 * Do a search with a timeout.
993 * @param ads connection to ads server
994 * @param bind_path Base dn for the search
995 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
996 * @param expr Search expression
997 * @param attrs Attributes to retrieve
998 * @param res ** which will contain results - free res* with ads_msgfree()
999 * @return status of search
1001 ADS_STATUS
ads_do_search(ADS_STRUCT
*ads
, const char *bind_path
, int scope
,
1003 const char **attrs
, LDAPMessage
**res
)
1006 char *utf8_expr
, *utf8_path
, **search_attrs
= NULL
;
1010 if (!(ctx
= talloc_init("ads_do_search"))) {
1011 DEBUG(1,("ads_do_search: talloc_init() failed!"));
1012 return ADS_ERROR(LDAP_NO_MEMORY
);
1015 /* 0 means the conversion worked but the result was empty
1016 so we only fail if it's negative. In any case, it always
1017 at least nulls out the dest */
1018 if ((push_utf8_talloc(ctx
, &utf8_expr
, expr
) == (size_t)-1) ||
1019 (push_utf8_talloc(ctx
, &utf8_path
, bind_path
) == (size_t)-1)) {
1020 DEBUG(1,("ads_do_search: push_utf8_talloc() failed!"));
1021 rc
= LDAP_NO_MEMORY
;
1025 if (!attrs
|| !(*attrs
))
1026 search_attrs
= NULL
;
1028 /* This would be the utf8-encoded version...*/
1029 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
1030 if (!(str_list_copy(talloc_tos(), &search_attrs
, attrs
)))
1032 DEBUG(1,("ads_do_search: str_list_copy() failed!"));
1033 rc
= LDAP_NO_MEMORY
;
1038 /* see the note in ads_do_paged_search - we *must* disable referrals */
1039 ldap_set_option(ads
->ldap
.ld
, LDAP_OPT_REFERRALS
, LDAP_OPT_OFF
);
1041 rc
= ldap_search_with_timeout(ads
->ldap
.ld
, utf8_path
, scope
, utf8_expr
,
1042 search_attrs
, 0, NULL
, NULL
,
1044 (LDAPMessage
**)res
);
1046 if (rc
== LDAP_SIZELIMIT_EXCEEDED
) {
1047 DEBUG(3,("Warning! sizelimit exceeded in ldap. Truncating.\n"));
1052 talloc_destroy(ctx
);
1053 /* if/when we decide to utf8-encode attrs, take out this next line */
1054 TALLOC_FREE(search_attrs
);
1055 return ADS_ERROR(rc
);
1058 * Do a general ADS search
1059 * @param ads connection to ads server
1060 * @param res ** which will contain results - free res* with ads_msgfree()
1061 * @param expr Search expression
1062 * @param attrs Attributes to retrieve
1063 * @return status of search
1065 ADS_STATUS
ads_search(ADS_STRUCT
*ads
, LDAPMessage
**res
,
1066 const char *expr
, const char **attrs
)
1068 return ads_do_search(ads
, ads
->config
.bind_path
, LDAP_SCOPE_SUBTREE
,
1073 * Do a search on a specific DistinguishedName
1074 * @param ads connection to ads server
1075 * @param res ** which will contain results - free res* with ads_msgfree()
1076 * @param dn DistinguishName to search
1077 * @param attrs Attributes to retrieve
1078 * @return status of search
1080 ADS_STATUS
ads_search_dn(ADS_STRUCT
*ads
, LDAPMessage
**res
,
1081 const char *dn
, const char **attrs
)
1083 return ads_do_search(ads
, dn
, LDAP_SCOPE_BASE
, "(objectclass=*)",
1088 * Free up memory from a ads_search
1089 * @param ads connection to ads server
1090 * @param msg Search results to free
1092 void ads_msgfree(ADS_STRUCT
*ads
, LDAPMessage
*msg
)
1099 * Free up memory from various ads requests
1100 * @param ads connection to ads server
1101 * @param mem Area to free
1103 void ads_memfree(ADS_STRUCT
*ads
, void *mem
)
1109 * Get a dn from search results
1110 * @param ads connection to ads server
1111 * @param msg Search result
1114 char *ads_get_dn(ADS_STRUCT
*ads
, LDAPMessage
*msg
)
1116 char *utf8_dn
, *unix_dn
;
1118 utf8_dn
= ldap_get_dn(ads
->ldap
.ld
, msg
);
1121 DEBUG (5, ("ads_get_dn: ldap_get_dn failed\n"));
1125 if (pull_utf8_allocate(&unix_dn
, utf8_dn
) == (size_t)-1) {
1126 DEBUG(0,("ads_get_dn: string conversion failure utf8 [%s]\n",
1130 ldap_memfree(utf8_dn
);
1135 * Get the parent from a dn
1136 * @param dn the dn to return the parent from
1137 * @return parent dn string
1139 char *ads_parent_dn(const char *dn
)
1147 p
= strchr(dn
, ',');
1157 * Find a machine account given a hostname
1158 * @param ads connection to ads server
1159 * @param res ** which will contain results - free res* with ads_msgfree()
1160 * @param host Hostname to search for
1161 * @return status of search
1163 ADS_STATUS
ads_find_machine_acct(ADS_STRUCT
*ads
, LDAPMessage
**res
,
1164 const char *machine
)
1168 const char *attrs
[] = {"*", "nTSecurityDescriptor", NULL
};
1172 /* the easiest way to find a machine account anywhere in the tree
1173 is to look for hostname$ */
1174 if (asprintf(&expr
, "(samAccountName=%s$)", machine
) == -1) {
1175 DEBUG(1, ("asprintf failed!\n"));
1176 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY
);
1179 status
= ads_search(ads
, res
, expr
, attrs
);
1185 * Initialize a list of mods to be used in a modify request
1186 * @param ctx An initialized TALLOC_CTX
1187 * @return allocated ADS_MODLIST
1189 ADS_MODLIST
ads_init_mods(TALLOC_CTX
*ctx
)
1191 #define ADS_MODLIST_ALLOC_SIZE 10
1194 if ((mods
= TALLOC_ZERO_ARRAY(ctx
, LDAPMod
*, ADS_MODLIST_ALLOC_SIZE
+ 1)))
1195 /* -1 is safety to make sure we don't go over the end.
1196 need to reset it to NULL before doing ldap modify */
1197 mods
[ADS_MODLIST_ALLOC_SIZE
] = (LDAPMod
*) -1;
1199 return (ADS_MODLIST
)mods
;
1204 add an attribute to the list, with values list already constructed
1206 static ADS_STATUS
ads_modlist_add(TALLOC_CTX
*ctx
, ADS_MODLIST
*mods
,
1207 int mod_op
, const char *name
,
1208 const void *_invals
)
1210 const void **invals
= (const void **)_invals
;
1212 LDAPMod
**modlist
= (LDAPMod
**) *mods
;
1213 struct berval
**ber_values
= NULL
;
1214 char **char_values
= NULL
;
1217 mod_op
= LDAP_MOD_DELETE
;
1219 if (mod_op
& LDAP_MOD_BVALUES
)
1220 ber_values
= ads_dup_values(ctx
,
1221 (const struct berval
**)invals
);
1223 char_values
= ads_push_strvals(ctx
,
1224 (const char **) invals
);
1227 /* find the first empty slot */
1228 for (curmod
=0; modlist
[curmod
] && modlist
[curmod
] != (LDAPMod
*) -1;
1230 if (modlist
[curmod
] == (LDAPMod
*) -1) {
1231 if (!(modlist
= TALLOC_REALLOC_ARRAY(ctx
, modlist
, LDAPMod
*,
1232 curmod
+ADS_MODLIST_ALLOC_SIZE
+1)))
1233 return ADS_ERROR(LDAP_NO_MEMORY
);
1234 memset(&modlist
[curmod
], 0,
1235 ADS_MODLIST_ALLOC_SIZE
*sizeof(LDAPMod
*));
1236 modlist
[curmod
+ADS_MODLIST_ALLOC_SIZE
] = (LDAPMod
*) -1;
1237 *mods
= (ADS_MODLIST
)modlist
;
1240 if (!(modlist
[curmod
] = TALLOC_ZERO_P(ctx
, LDAPMod
)))
1241 return ADS_ERROR(LDAP_NO_MEMORY
);
1242 modlist
[curmod
]->mod_type
= talloc_strdup(ctx
, name
);
1243 if (mod_op
& LDAP_MOD_BVALUES
) {
1244 modlist
[curmod
]->mod_bvalues
= ber_values
;
1245 } else if (mod_op
& LDAP_MOD_DELETE
) {
1246 modlist
[curmod
]->mod_values
= NULL
;
1248 modlist
[curmod
]->mod_values
= char_values
;
1251 modlist
[curmod
]->mod_op
= mod_op
;
1252 return ADS_ERROR(LDAP_SUCCESS
);
1256 * Add a single string value to a mod list
1257 * @param ctx An initialized TALLOC_CTX
1258 * @param mods An initialized ADS_MODLIST
1259 * @param name The attribute name to add
1260 * @param val The value to add - NULL means DELETE
1261 * @return ADS STATUS indicating success of add
1263 ADS_STATUS
ads_mod_str(TALLOC_CTX
*ctx
, ADS_MODLIST
*mods
,
1264 const char *name
, const char *val
)
1266 const char *values
[2];
1272 return ads_modlist_add(ctx
, mods
, LDAP_MOD_DELETE
, name
, NULL
);
1273 return ads_modlist_add(ctx
, mods
, LDAP_MOD_REPLACE
, name
, values
);
1277 * Add an array of string values to a mod list
1278 * @param ctx An initialized TALLOC_CTX
1279 * @param mods An initialized ADS_MODLIST
1280 * @param name The attribute name to add
1281 * @param vals The array of string values to add - NULL means DELETE
1282 * @return ADS STATUS indicating success of add
1284 ADS_STATUS
ads_mod_strlist(TALLOC_CTX
*ctx
, ADS_MODLIST
*mods
,
1285 const char *name
, const char **vals
)
1288 return ads_modlist_add(ctx
, mods
, LDAP_MOD_DELETE
, name
, NULL
);
1289 return ads_modlist_add(ctx
, mods
, LDAP_MOD_REPLACE
,
1290 name
, (const void **) vals
);
1295 * Add a single ber-encoded value to a mod list
1296 * @param ctx An initialized TALLOC_CTX
1297 * @param mods An initialized ADS_MODLIST
1298 * @param name The attribute name to add
1299 * @param val The value to add - NULL means DELETE
1300 * @return ADS STATUS indicating success of add
1302 static ADS_STATUS
ads_mod_ber(TALLOC_CTX
*ctx
, ADS_MODLIST
*mods
,
1303 const char *name
, const struct berval
*val
)
1305 const struct berval
*values
[2];
1310 return ads_modlist_add(ctx
, mods
, LDAP_MOD_DELETE
, name
, NULL
);
1311 return ads_modlist_add(ctx
, mods
, LDAP_MOD_REPLACE
|LDAP_MOD_BVALUES
,
1312 name
, (const void **) values
);
1317 * Perform an ldap modify
1318 * @param ads connection to ads server
1319 * @param mod_dn DistinguishedName to modify
1320 * @param mods list of modifications to perform
1321 * @return status of modify
1323 ADS_STATUS
ads_gen_mod(ADS_STRUCT
*ads
, const char *mod_dn
, ADS_MODLIST mods
)
1326 char *utf8_dn
= NULL
;
1328 this control is needed to modify that contains a currently
1329 non-existent attribute (but allowable for the object) to run
1331 LDAPControl PermitModify
= {
1332 CONST_DISCARD(char *, ADS_PERMIT_MODIFY_OID
),
1335 LDAPControl
*controls
[2];
1337 controls
[0] = &PermitModify
;
1340 if (push_utf8_allocate(&utf8_dn
, mod_dn
) == -1) {
1341 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY
);
1344 /* find the end of the list, marked by NULL or -1 */
1345 for(i
=0;(mods
[i
]!=0)&&(mods
[i
]!=(LDAPMod
*) -1);i
++);
1346 /* make sure the end of the list is NULL */
1348 ret
= ldap_modify_ext_s(ads
->ldap
.ld
, utf8_dn
,
1349 (LDAPMod
**) mods
, controls
, NULL
);
1351 return ADS_ERROR(ret
);
1355 * Perform an ldap add
1356 * @param ads connection to ads server
1357 * @param new_dn DistinguishedName to add
1358 * @param mods list of attributes and values for DN
1359 * @return status of add
1361 ADS_STATUS
ads_gen_add(ADS_STRUCT
*ads
, const char *new_dn
, ADS_MODLIST mods
)
1364 char *utf8_dn
= NULL
;
1366 if (push_utf8_allocate(&utf8_dn
, new_dn
) == -1) {
1367 DEBUG(1, ("ads_gen_add: push_utf8_allocate failed!"));
1368 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY
);
1371 /* find the end of the list, marked by NULL or -1 */
1372 for(i
=0;(mods
[i
]!=0)&&(mods
[i
]!=(LDAPMod
*) -1);i
++);
1373 /* make sure the end of the list is NULL */
1376 ret
= ldap_add_s(ads
->ldap
.ld
, utf8_dn
, (LDAPMod
**)mods
);
1378 return ADS_ERROR(ret
);
1382 * Delete a DistinguishedName
1383 * @param ads connection to ads server
1384 * @param new_dn DistinguishedName to delete
1385 * @return status of delete
1387 ADS_STATUS
ads_del_dn(ADS_STRUCT
*ads
, char *del_dn
)
1390 char *utf8_dn
= NULL
;
1391 if (push_utf8_allocate(&utf8_dn
, del_dn
) == -1) {
1392 DEBUG(1, ("ads_del_dn: push_utf8_allocate failed!"));
1393 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY
);
1396 ret
= ldap_delete_s(ads
->ldap
.ld
, utf8_dn
);
1398 return ADS_ERROR(ret
);
1402 * Build an org unit string
1403 * if org unit is Computers or blank then assume a container, otherwise
1404 * assume a / separated list of organisational units.
1405 * jmcd: '\' is now used for escapes so certain chars can be in the ou (e.g. #)
1406 * @param ads connection to ads server
1407 * @param org_unit Organizational unit
1408 * @return org unit string - caller must free
1410 char *ads_ou_string(ADS_STRUCT
*ads
, const char *org_unit
)
1414 if (!org_unit
|| !*org_unit
) {
1416 ret
= ads_default_ou_string(ads
, WELL_KNOWN_GUID_COMPUTERS
);
1418 /* samba4 might not yet respond to a wellknownobject-query */
1419 return ret
? ret
: SMB_STRDUP("cn=Computers");
1422 if (strequal(org_unit
, "Computers")) {
1423 return SMB_STRDUP("cn=Computers");
1426 /* jmcd: removed "\\" from the separation chars, because it is
1427 needed as an escape for chars like '#' which are valid in an
1429 return ads_build_path(org_unit
, "/", "ou=", 1);
1433 * Get a org unit string for a well-known GUID
1434 * @param ads connection to ads server
1435 * @param wknguid Well known GUID
1436 * @return org unit string - caller must free
1438 char *ads_default_ou_string(ADS_STRUCT
*ads
, const char *wknguid
)
1441 LDAPMessage
*res
= NULL
;
1442 char *base
, *wkn_dn
= NULL
, *ret
= NULL
, **wkn_dn_exp
= NULL
,
1443 **bind_dn_exp
= NULL
;
1444 const char *attrs
[] = {"distinguishedName", NULL
};
1445 int new_ln
, wkn_ln
, bind_ln
, i
;
1447 if (wknguid
== NULL
) {
1451 if (asprintf(&base
, "<WKGUID=%s,%s>", wknguid
, ads
->config
.bind_path
) == -1) {
1452 DEBUG(1, ("asprintf failed!\n"));
1456 status
= ads_search_dn(ads
, &res
, base
, attrs
);
1457 if (!ADS_ERR_OK(status
)) {
1458 DEBUG(1,("Failed while searching for: %s\n", base
));
1462 if (ads_count_replies(ads
, res
) != 1) {
1466 /* substitute the bind-path from the well-known-guid-search result */
1467 wkn_dn
= ads_get_dn(ads
, res
);
1472 wkn_dn_exp
= ldap_explode_dn(wkn_dn
, 0);
1477 bind_dn_exp
= ldap_explode_dn(ads
->config
.bind_path
, 0);
1482 for (wkn_ln
=0; wkn_dn_exp
[wkn_ln
]; wkn_ln
++)
1484 for (bind_ln
=0; bind_dn_exp
[bind_ln
]; bind_ln
++)
1487 new_ln
= wkn_ln
- bind_ln
;
1489 ret
= SMB_STRDUP(wkn_dn_exp
[0]);
1494 for (i
=1; i
< new_ln
; i
++) {
1497 if (asprintf(&s
, "%s,%s", ret
, wkn_dn_exp
[i
]) == -1) {
1503 ret
= SMB_STRDUP(s
);
1512 ads_msgfree(ads
, res
);
1513 ads_memfree(ads
, wkn_dn
);
1515 ldap_value_free(wkn_dn_exp
);
1518 ldap_value_free(bind_dn_exp
);
1525 * Adds (appends) an item to an attribute array, rather then
1526 * replacing the whole list
1527 * @param ctx An initialized TALLOC_CTX
1528 * @param mods An initialized ADS_MODLIST
1529 * @param name name of the ldap attribute to append to
1530 * @param vals an array of values to add
1531 * @return status of addition
1534 ADS_STATUS
ads_add_strlist(TALLOC_CTX
*ctx
, ADS_MODLIST
*mods
,
1535 const char *name
, const char **vals
)
1537 return ads_modlist_add(ctx
, mods
, LDAP_MOD_ADD
, name
,
1538 (const void *) vals
);
1542 * Determines the computer account's current KVNO via an LDAP lookup
1543 * @param ads An initialized ADS_STRUCT
1544 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1545 * @return the kvno for the computer account, or -1 in case of a failure.
1548 uint32
ads_get_kvno(ADS_STRUCT
*ads
, const char *machine_name
)
1550 LDAPMessage
*res
= NULL
;
1551 uint32 kvno
= (uint32
)-1; /* -1 indicates a failure */
1553 const char *attrs
[] = {"msDS-KeyVersionNumber", NULL
};
1554 char *dn_string
= NULL
;
1555 ADS_STATUS ret
= ADS_ERROR(LDAP_SUCCESS
);
1557 DEBUG(5,("ads_get_kvno: Searching for host %s\n", machine_name
));
1558 if (asprintf(&filter
, "(samAccountName=%s$)", machine_name
) == -1) {
1561 ret
= ads_search(ads
, &res
, filter
, attrs
);
1563 if (!ADS_ERR_OK(ret
) && ads_count_replies(ads
, res
)) {
1564 DEBUG(1,("ads_get_kvno: Computer Account For %s not found.\n", machine_name
));
1565 ads_msgfree(ads
, res
);
1569 dn_string
= ads_get_dn(ads
, res
);
1571 DEBUG(0,("ads_get_kvno: out of memory.\n"));
1572 ads_msgfree(ads
, res
);
1575 DEBUG(5,("ads_get_kvno: Using: %s\n", dn_string
));
1576 ads_memfree(ads
, dn_string
);
1578 /* ---------------------------------------------------------
1579 * 0 is returned as a default KVNO from this point on...
1580 * This is done because Windows 2000 does not support key
1581 * version numbers. Chances are that a failure in the next
1582 * step is simply due to Windows 2000 being used for a
1583 * domain controller. */
1586 if (!ads_pull_uint32(ads
, res
, "msDS-KeyVersionNumber", &kvno
)) {
1587 DEBUG(3,("ads_get_kvno: Error Determining KVNO!\n"));
1588 DEBUG(3,("ads_get_kvno: Windows 2000 does not support KVNO's, so this may be normal.\n"));
1589 ads_msgfree(ads
, res
);
1594 DEBUG(5,("ads_get_kvno: Looked Up KVNO of: %d\n", kvno
));
1595 ads_msgfree(ads
, res
);
1600 * This clears out all registered spn's for a given hostname
1601 * @param ads An initilaized ADS_STRUCT
1602 * @param machine_name the NetBIOS name of the computer.
1603 * @return 0 upon success, non-zero otherwise.
1606 ADS_STATUS
ads_clear_service_principal_names(ADS_STRUCT
*ads
, const char *machine_name
)
1609 LDAPMessage
*res
= NULL
;
1611 const char *servicePrincipalName
[1] = {NULL
};
1612 ADS_STATUS ret
= ADS_ERROR(LDAP_SUCCESS
);
1613 char *dn_string
= NULL
;
1615 ret
= ads_find_machine_acct(ads
, &res
, machine_name
);
1616 if (!ADS_ERR_OK(ret
) || ads_count_replies(ads
, res
) != 1) {
1617 DEBUG(5,("ads_clear_service_principal_names: WARNING: Host Account for %s not found... skipping operation.\n", machine_name
));
1618 DEBUG(5,("ads_clear_service_principal_names: WARNING: Service Principals for %s have NOT been cleared.\n", machine_name
));
1619 ads_msgfree(ads
, res
);
1620 return ADS_ERROR(LDAP_NO_SUCH_OBJECT
);
1623 DEBUG(5,("ads_clear_service_principal_names: Host account for %s found\n", machine_name
));
1624 ctx
= talloc_init("ads_clear_service_principal_names");
1626 ads_msgfree(ads
, res
);
1627 return ADS_ERROR(LDAP_NO_MEMORY
);
1630 if (!(mods
= ads_init_mods(ctx
))) {
1631 talloc_destroy(ctx
);
1632 ads_msgfree(ads
, res
);
1633 return ADS_ERROR(LDAP_NO_MEMORY
);
1635 ret
= ads_mod_strlist(ctx
, &mods
, "servicePrincipalName", servicePrincipalName
);
1636 if (!ADS_ERR_OK(ret
)) {
1637 DEBUG(1,("ads_clear_service_principal_names: Error creating strlist.\n"));
1638 ads_msgfree(ads
, res
);
1639 talloc_destroy(ctx
);
1642 dn_string
= ads_get_dn(ads
, res
);
1644 talloc_destroy(ctx
);
1645 ads_msgfree(ads
, res
);
1646 return ADS_ERROR(LDAP_NO_MEMORY
);
1648 ret
= ads_gen_mod(ads
, dn_string
, mods
);
1649 ads_memfree(ads
,dn_string
);
1650 if (!ADS_ERR_OK(ret
)) {
1651 DEBUG(1,("ads_clear_service_principal_names: Error: Updating Service Principals for machine %s in LDAP\n",
1653 ads_msgfree(ads
, res
);
1654 talloc_destroy(ctx
);
1658 ads_msgfree(ads
, res
);
1659 talloc_destroy(ctx
);
1664 * This adds a service principal name to an existing computer account
1665 * (found by hostname) in AD.
1666 * @param ads An initialized ADS_STRUCT
1667 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1668 * @param my_fqdn The fully qualified DNS name of the machine
1669 * @param spn A string of the service principal to add, i.e. 'host'
1670 * @return 0 upon sucess, or non-zero if a failure occurs
1673 ADS_STATUS
ads_add_service_principal_name(ADS_STRUCT
*ads
, const char *machine_name
,
1674 const char *my_fqdn
, const char *spn
)
1678 LDAPMessage
*res
= NULL
;
1681 char *dn_string
= NULL
;
1682 const char *servicePrincipalName
[3] = {NULL
, NULL
, NULL
};
1684 ret
= ads_find_machine_acct(ads
, &res
, machine_name
);
1685 if (!ADS_ERR_OK(ret
) || ads_count_replies(ads
, res
) != 1) {
1686 DEBUG(1,("ads_add_service_principal_name: WARNING: Host Account for %s not found... skipping operation.\n",
1688 DEBUG(1,("ads_add_service_principal_name: WARNING: Service Principal '%s/%s@%s' has NOT been added.\n",
1689 spn
, machine_name
, ads
->config
.realm
));
1690 ads_msgfree(ads
, res
);
1691 return ADS_ERROR(LDAP_NO_SUCH_OBJECT
);
1694 DEBUG(1,("ads_add_service_principal_name: Host account for %s found\n", machine_name
));
1695 if (!(ctx
= talloc_init("ads_add_service_principal_name"))) {
1696 ads_msgfree(ads
, res
);
1697 return ADS_ERROR(LDAP_NO_MEMORY
);
1700 /* add short name spn */
1702 if ( (psp1
= talloc_asprintf(ctx
, "%s/%s", spn
, machine_name
)) == NULL
) {
1703 talloc_destroy(ctx
);
1704 ads_msgfree(ads
, res
);
1705 return ADS_ERROR(LDAP_NO_MEMORY
);
1708 strlower_m(&psp1
[strlen(spn
)]);
1709 servicePrincipalName
[0] = psp1
;
1711 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n",
1712 psp1
, machine_name
));
1715 /* add fully qualified spn */
1717 if ( (psp2
= talloc_asprintf(ctx
, "%s/%s", spn
, my_fqdn
)) == NULL
) {
1718 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
1722 strlower_m(&psp2
[strlen(spn
)]);
1723 servicePrincipalName
[1] = psp2
;
1725 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n",
1726 psp2
, machine_name
));
1728 if ( (mods
= ads_init_mods(ctx
)) == NULL
) {
1729 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
1733 ret
= ads_add_strlist(ctx
, &mods
, "servicePrincipalName", servicePrincipalName
);
1734 if (!ADS_ERR_OK(ret
)) {
1735 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
1739 if ( (dn_string
= ads_get_dn(ads
, res
)) == NULL
) {
1740 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
1744 ret
= ads_gen_mod(ads
, dn_string
, mods
);
1745 ads_memfree(ads
,dn_string
);
1746 if (!ADS_ERR_OK(ret
)) {
1747 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
1753 ads_msgfree(ads
, res
);
1758 * adds a machine account to the ADS server
1759 * @param ads An intialized ADS_STRUCT
1760 * @param machine_name - the NetBIOS machine name of this account.
1761 * @param account_type A number indicating the type of account to create
1762 * @param org_unit The LDAP path in which to place this account
1763 * @return 0 upon success, or non-zero otherwise
1766 ADS_STATUS
ads_create_machine_acct(ADS_STRUCT
*ads
, const char *machine_name
,
1767 const char *org_unit
)
1770 char *samAccountName
, *controlstr
;
1773 char *machine_escaped
= NULL
;
1775 const char *objectClass
[] = {"top", "person", "organizationalPerson",
1776 "user", "computer", NULL
};
1777 LDAPMessage
*res
= NULL
;
1778 uint32 acct_control
= ( UF_WORKSTATION_TRUST_ACCOUNT
|\
1779 UF_DONT_EXPIRE_PASSWD
|\
1780 UF_ACCOUNTDISABLE
);
1782 if (!(ctx
= talloc_init("ads_add_machine_acct")))
1783 return ADS_ERROR(LDAP_NO_MEMORY
);
1785 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
1787 machine_escaped
= escape_rdn_val_string_alloc(machine_name
);
1788 if (!machine_escaped
) {
1792 new_dn
= talloc_asprintf(ctx
, "cn=%s,%s", machine_escaped
, org_unit
);
1793 samAccountName
= talloc_asprintf(ctx
, "%s$", machine_name
);
1795 if ( !new_dn
|| !samAccountName
) {
1799 #ifndef ENCTYPE_ARCFOUR_HMAC
1800 acct_control
|= UF_USE_DES_KEY_ONLY
;
1803 if (!(controlstr
= talloc_asprintf(ctx
, "%u", acct_control
))) {
1807 if (!(mods
= ads_init_mods(ctx
))) {
1811 ads_mod_str(ctx
, &mods
, "cn", machine_name
);
1812 ads_mod_str(ctx
, &mods
, "sAMAccountName", samAccountName
);
1813 ads_mod_strlist(ctx
, &mods
, "objectClass", objectClass
);
1814 ads_mod_str(ctx
, &mods
, "userAccountControl", controlstr
);
1816 ret
= ads_gen_add(ads
, new_dn
, mods
);
1819 SAFE_FREE(machine_escaped
);
1820 ads_msgfree(ads
, res
);
1821 talloc_destroy(ctx
);
1827 * move a machine account to another OU on the ADS server
1828 * @param ads - An intialized ADS_STRUCT
1829 * @param machine_name - the NetBIOS machine name of this account.
1830 * @param org_unit - The LDAP path in which to place this account
1831 * @param moved - whether we moved the machine account (optional)
1832 * @return 0 upon success, or non-zero otherwise
1835 ADS_STATUS
ads_move_machine_acct(ADS_STRUCT
*ads
, const char *machine_name
,
1836 const char *org_unit
, bool *moved
)
1840 LDAPMessage
*res
= NULL
;
1841 char *filter
= NULL
;
1842 char *computer_dn
= NULL
;
1844 char *computer_rdn
= NULL
;
1845 bool need_move
= False
;
1847 if (asprintf(&filter
, "(samAccountName=%s$)", machine_name
) == -1) {
1848 rc
= ADS_ERROR(LDAP_NO_MEMORY
);
1852 /* Find pre-existing machine */
1853 rc
= ads_search(ads
, &res
, filter
, NULL
);
1854 if (!ADS_ERR_OK(rc
)) {
1858 computer_dn
= ads_get_dn(ads
, res
);
1860 rc
= ADS_ERROR(LDAP_NO_MEMORY
);
1864 parent_dn
= ads_parent_dn(computer_dn
);
1865 if (strequal(parent_dn
, org_unit
)) {
1871 if (asprintf(&computer_rdn
, "CN=%s", machine_name
) == -1) {
1872 rc
= ADS_ERROR(LDAP_NO_MEMORY
);
1876 ldap_status
= ldap_rename_s(ads
->ldap
.ld
, computer_dn
, computer_rdn
,
1877 org_unit
, 1, NULL
, NULL
);
1878 rc
= ADS_ERROR(ldap_status
);
1881 ads_msgfree(ads
, res
);
1883 SAFE_FREE(computer_dn
);
1884 SAFE_FREE(computer_rdn
);
1886 if (!ADS_ERR_OK(rc
)) {
1898 dump a binary result from ldap
1900 static void dump_binary(ADS_STRUCT
*ads
, const char *field
, struct berval
**values
)
1903 for (i
=0; values
[i
]; i
++) {
1904 printf("%s: ", field
);
1905 for (j
=0; j
<values
[i
]->bv_len
; j
++) {
1906 printf("%02X", (unsigned char)values
[i
]->bv_val
[j
]);
1912 static void dump_guid(ADS_STRUCT
*ads
, const char *field
, struct berval
**values
)
1915 for (i
=0; values
[i
]; i
++) {
1920 memcpy(guid
.info
, values
[i
]->bv_val
, sizeof(guid
.info
));
1921 smb_uuid_unpack(guid
, &tmp
);
1922 printf("%s: %s\n", field
, smb_uuid_string(talloc_tos(), tmp
));
1927 dump a sid result from ldap
1929 static void dump_sid(ADS_STRUCT
*ads
, const char *field
, struct berval
**values
)
1932 for (i
=0; values
[i
]; i
++) {
1935 sid_parse(values
[i
]->bv_val
, values
[i
]->bv_len
, &sid
);
1936 printf("%s: %s\n", field
, sid_to_fstring(tmp
, &sid
));
1941 dump ntSecurityDescriptor
1943 static void dump_sd(ADS_STRUCT
*ads
, const char *filed
, struct berval
**values
)
1945 TALLOC_CTX
*frame
= talloc_stackframe();
1946 struct security_descriptor
*psd
;
1949 status
= unmarshall_sec_desc(talloc_tos(), (uint8
*)values
[0]->bv_val
,
1950 values
[0]->bv_len
, &psd
);
1951 if (!NT_STATUS_IS_OK(status
)) {
1952 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
1953 nt_errstr(status
)));
1959 ads_disp_sd(ads
, talloc_tos(), psd
);
1966 dump a string result from ldap
1968 static void dump_string(const char *field
, char **values
)
1971 for (i
=0; values
[i
]; i
++) {
1972 printf("%s: %s\n", field
, values
[i
]);
1977 dump a field from LDAP on stdout
1981 static bool ads_dump_field(ADS_STRUCT
*ads
, char *field
, void **values
, void *data_area
)
1986 void (*handler
)(ADS_STRUCT
*, const char *, struct berval
**);
1988 {"objectGUID", False
, dump_guid
},
1989 {"netbootGUID", False
, dump_guid
},
1990 {"nTSecurityDescriptor", False
, dump_sd
},
1991 {"dnsRecord", False
, dump_binary
},
1992 {"objectSid", False
, dump_sid
},
1993 {"tokenGroups", False
, dump_sid
},
1994 {"tokenGroupsNoGCAcceptable", False
, dump_sid
},
1995 {"tokengroupsGlobalandUniversal", False
, dump_sid
},
1996 {"mS-DS-CreatorSID", False
, dump_sid
},
1997 {"msExchMailboxGuid", False
, dump_guid
},
2002 if (!field
) { /* must be end of an entry */
2007 for (i
=0; handlers
[i
].name
; i
++) {
2008 if (StrCaseCmp(handlers
[i
].name
, field
) == 0) {
2009 if (!values
) /* first time, indicate string or not */
2010 return handlers
[i
].string
;
2011 handlers
[i
].handler(ads
, field
, (struct berval
**) values
);
2015 if (!handlers
[i
].name
) {
2016 if (!values
) /* first time, indicate string conversion */
2018 dump_string(field
, (char **)values
);
2024 * Dump a result from LDAP on stdout
2025 * used for debugging
2026 * @param ads connection to ads server
2027 * @param res Results to dump
2030 void ads_dump(ADS_STRUCT
*ads
, LDAPMessage
*res
)
2032 ads_process_results(ads
, res
, ads_dump_field
, NULL
);
2036 * Walk through results, calling a function for each entry found.
2037 * The function receives a field name, a berval * array of values,
2038 * and a data area passed through from the start. The function is
2039 * called once with null for field and values at the end of each
2041 * @param ads connection to ads server
2042 * @param res Results to process
2043 * @param fn Function for processing each result
2044 * @param data_area user-defined area to pass to function
2046 void ads_process_results(ADS_STRUCT
*ads
, LDAPMessage
*res
,
2047 bool (*fn
)(ADS_STRUCT
*, char *, void **, void *),
2053 if (!(ctx
= talloc_init("ads_process_results")))
2056 for (msg
= ads_first_entry(ads
, res
); msg
;
2057 msg
= ads_next_entry(ads
, msg
)) {
2061 for (utf8_field
=ldap_first_attribute(ads
->ldap
.ld
,
2062 (LDAPMessage
*)msg
,&b
);
2064 utf8_field
=ldap_next_attribute(ads
->ldap
.ld
,
2065 (LDAPMessage
*)msg
,b
)) {
2066 struct berval
**ber_vals
;
2067 char **str_vals
, **utf8_vals
;
2071 pull_utf8_talloc(ctx
, &field
, utf8_field
);
2072 string
= fn(ads
, field
, NULL
, data_area
);
2075 utf8_vals
= ldap_get_values(ads
->ldap
.ld
,
2076 (LDAPMessage
*)msg
, field
);
2077 str_vals
= ads_pull_strvals(ctx
,
2078 (const char **) utf8_vals
);
2079 fn(ads
, field
, (void **) str_vals
, data_area
);
2080 ldap_value_free(utf8_vals
);
2082 ber_vals
= ldap_get_values_len(ads
->ldap
.ld
,
2083 (LDAPMessage
*)msg
, field
);
2084 fn(ads
, field
, (void **) ber_vals
, data_area
);
2086 ldap_value_free_len(ber_vals
);
2088 ldap_memfree(utf8_field
);
2091 talloc_free_children(ctx
);
2092 fn(ads
, NULL
, NULL
, data_area
); /* completed an entry */
2095 talloc_destroy(ctx
);
2099 * count how many replies are in a LDAPMessage
2100 * @param ads connection to ads server
2101 * @param res Results to count
2102 * @return number of replies
2104 int ads_count_replies(ADS_STRUCT
*ads
, void *res
)
2106 return ldap_count_entries(ads
->ldap
.ld
, (LDAPMessage
*)res
);
2110 * pull the first entry from a ADS result
2111 * @param ads connection to ads server
2112 * @param res Results of search
2113 * @return first entry from result
2115 LDAPMessage
*ads_first_entry(ADS_STRUCT
*ads
, LDAPMessage
*res
)
2117 return ldap_first_entry(ads
->ldap
.ld
, res
);
2121 * pull the next entry from a ADS result
2122 * @param ads connection to ads server
2123 * @param res Results of search
2124 * @return next entry from result
2126 LDAPMessage
*ads_next_entry(ADS_STRUCT
*ads
, LDAPMessage
*res
)
2128 return ldap_next_entry(ads
->ldap
.ld
, res
);
2132 * pull the first message from a ADS result
2133 * @param ads connection to ads server
2134 * @param res Results of search
2135 * @return first message from result
2137 LDAPMessage
*ads_first_message(ADS_STRUCT
*ads
, LDAPMessage
*res
)
2139 return ldap_first_message(ads
->ldap
.ld
, res
);
2143 * pull the next message from a ADS result
2144 * @param ads connection to ads server
2145 * @param res Results of search
2146 * @return next message from result
2148 LDAPMessage
*ads_next_message(ADS_STRUCT
*ads
, LDAPMessage
*res
)
2150 return ldap_next_message(ads
->ldap
.ld
, res
);
2154 * pull a single string from a ADS result
2155 * @param ads connection to ads server
2156 * @param mem_ctx TALLOC_CTX to use for allocating result string
2157 * @param msg Results of search
2158 * @param field Attribute to retrieve
2159 * @return Result string in talloc context
2161 char *ads_pull_string(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
, LDAPMessage
*msg
,
2169 values
= ldap_get_values(ads
->ldap
.ld
, msg
, field
);
2174 rc
= pull_utf8_talloc(mem_ctx
, &ux_string
,
2176 if (rc
!= (size_t)-1)
2180 ldap_value_free(values
);
2185 * pull an array of strings from a ADS result
2186 * @param ads connection to ads server
2187 * @param mem_ctx TALLOC_CTX to use for allocating result string
2188 * @param msg Results of search
2189 * @param field Attribute to retrieve
2190 * @return Result strings in talloc context
2192 char **ads_pull_strings(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
,
2193 LDAPMessage
*msg
, const char *field
,
2200 values
= ldap_get_values(ads
->ldap
.ld
, msg
, field
);
2204 *num_values
= ldap_count_values(values
);
2206 ret
= TALLOC_ARRAY(mem_ctx
, char *, *num_values
+ 1);
2208 ldap_value_free(values
);
2212 for (i
=0;i
<*num_values
;i
++) {
2213 if (pull_utf8_talloc(mem_ctx
, &ret
[i
], values
[i
]) == -1) {
2214 ldap_value_free(values
);
2220 ldap_value_free(values
);
2225 * pull an array of strings from a ADS result
2226 * (handle large multivalue attributes with range retrieval)
2227 * @param ads connection to ads server
2228 * @param mem_ctx TALLOC_CTX to use for allocating result string
2229 * @param msg Results of search
2230 * @param field Attribute to retrieve
2231 * @param current_strings strings returned by a previous call to this function
2232 * @param next_attribute The next query should ask for this attribute
2233 * @param num_values How many values did we get this time?
2234 * @param more_values Are there more values to get?
2235 * @return Result strings in talloc context
2237 char **ads_pull_strings_range(ADS_STRUCT
*ads
,
2238 TALLOC_CTX
*mem_ctx
,
2239 LDAPMessage
*msg
, const char *field
,
2240 char **current_strings
,
2241 const char **next_attribute
,
2242 size_t *num_strings
,
2246 char *expected_range_attrib
, *range_attr
;
2247 BerElement
*ptr
= NULL
;
2250 size_t num_new_strings
;
2251 unsigned long int range_start
;
2252 unsigned long int range_end
;
2254 /* we might have been given the whole lot anyway */
2255 if ((strings
= ads_pull_strings(ads
, mem_ctx
, msg
, field
, num_strings
))) {
2256 *more_strings
= False
;
2260 expected_range_attrib
= talloc_asprintf(mem_ctx
, "%s;Range=", field
);
2262 /* look for Range result */
2263 for (attr
= ldap_first_attribute(ads
->ldap
.ld
, (LDAPMessage
*)msg
, &ptr
);
2265 attr
= ldap_next_attribute(ads
->ldap
.ld
, (LDAPMessage
*)msg
, ptr
)) {
2266 /* we ignore the fact that this is utf8, as all attributes are ascii... */
2267 if (strnequal(attr
, expected_range_attrib
, strlen(expected_range_attrib
))) {
2275 /* nothing here - this field is just empty */
2276 *more_strings
= False
;
2280 if (sscanf(&range_attr
[strlen(expected_range_attrib
)], "%lu-%lu",
2281 &range_start
, &range_end
) == 2) {
2282 *more_strings
= True
;
2284 if (sscanf(&range_attr
[strlen(expected_range_attrib
)], "%lu-*",
2285 &range_start
) == 1) {
2286 *more_strings
= False
;
2288 DEBUG(1, ("ads_pull_strings_range: Cannot parse Range attriubte (%s)\n",
2290 ldap_memfree(range_attr
);
2291 *more_strings
= False
;
2296 if ((*num_strings
) != range_start
) {
2297 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) doesn't start at %u, but at %lu"
2298 " - aborting range retreival\n",
2299 range_attr
, (unsigned int)(*num_strings
) + 1, range_start
));
2300 ldap_memfree(range_attr
);
2301 *more_strings
= False
;
2305 new_strings
= ads_pull_strings(ads
, mem_ctx
, msg
, range_attr
, &num_new_strings
);
2307 if (*more_strings
&& ((*num_strings
+ num_new_strings
) != (range_end
+ 1))) {
2308 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) tells us we have %lu "
2309 "strings in this bunch, but we only got %lu - aborting range retreival\n",
2310 range_attr
, (unsigned long int)range_end
- range_start
+ 1,
2311 (unsigned long int)num_new_strings
));
2312 ldap_memfree(range_attr
);
2313 *more_strings
= False
;
2317 strings
= TALLOC_REALLOC_ARRAY(mem_ctx
, current_strings
, char *,
2318 *num_strings
+ num_new_strings
);
2320 if (strings
== NULL
) {
2321 ldap_memfree(range_attr
);
2322 *more_strings
= False
;
2326 if (new_strings
&& num_new_strings
) {
2327 memcpy(&strings
[*num_strings
], new_strings
,
2328 sizeof(*new_strings
) * num_new_strings
);
2331 (*num_strings
) += num_new_strings
;
2333 if (*more_strings
) {
2334 *next_attribute
= talloc_asprintf(mem_ctx
,
2339 if (!*next_attribute
) {
2340 DEBUG(1, ("talloc_asprintf for next attribute failed!\n"));
2341 ldap_memfree(range_attr
);
2342 *more_strings
= False
;
2347 ldap_memfree(range_attr
);
2353 * pull a single uint32 from a ADS result
2354 * @param ads connection to ads server
2355 * @param msg Results of search
2356 * @param field Attribute to retrieve
2357 * @param v Pointer to int to store result
2358 * @return boolean inidicating success
2360 bool ads_pull_uint32(ADS_STRUCT
*ads
, LDAPMessage
*msg
, const char *field
,
2365 values
= ldap_get_values(ads
->ldap
.ld
, msg
, field
);
2369 ldap_value_free(values
);
2373 *v
= atoi(values
[0]);
2374 ldap_value_free(values
);
2379 * pull a single objectGUID from an ADS result
2380 * @param ads connection to ADS server
2381 * @param msg results of search
2382 * @param guid 37-byte area to receive text guid
2383 * @return boolean indicating success
2385 bool ads_pull_guid(ADS_STRUCT
*ads
, LDAPMessage
*msg
, struct GUID
*guid
)
2388 UUID_FLAT flat_guid
;
2390 values
= ldap_get_values(ads
->ldap
.ld
, msg
, "objectGUID");
2395 memcpy(&flat_guid
.info
, values
[0], sizeof(UUID_FLAT
));
2396 smb_uuid_unpack(flat_guid
, guid
);
2397 ldap_value_free(values
);
2400 ldap_value_free(values
);
2407 * pull a single DOM_SID from a ADS result
2408 * @param ads connection to ads server
2409 * @param msg Results of search
2410 * @param field Attribute to retrieve
2411 * @param sid Pointer to sid to store result
2412 * @return boolean inidicating success
2414 bool ads_pull_sid(ADS_STRUCT
*ads
, LDAPMessage
*msg
, const char *field
,
2417 struct berval
**values
;
2420 values
= ldap_get_values_len(ads
->ldap
.ld
, msg
, field
);
2426 ret
= sid_parse(values
[0]->bv_val
, values
[0]->bv_len
, sid
);
2428 ldap_value_free_len(values
);
2433 * pull an array of DOM_SIDs from a ADS result
2434 * @param ads connection to ads server
2435 * @param mem_ctx TALLOC_CTX for allocating sid array
2436 * @param msg Results of search
2437 * @param field Attribute to retrieve
2438 * @param sids pointer to sid array to allocate
2439 * @return the count of SIDs pulled
2441 int ads_pull_sids(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
,
2442 LDAPMessage
*msg
, const char *field
, DOM_SID
**sids
)
2444 struct berval
**values
;
2448 values
= ldap_get_values_len(ads
->ldap
.ld
, msg
, field
);
2453 for (i
=0; values
[i
]; i
++)
2457 (*sids
) = TALLOC_ARRAY(mem_ctx
, DOM_SID
, i
);
2459 ldap_value_free_len(values
);
2467 for (i
=0; values
[i
]; i
++) {
2468 ret
= sid_parse(values
[i
]->bv_val
, values
[i
]->bv_len
, &(*sids
)[count
]);
2470 DEBUG(10, ("pulling SID: %s\n",
2471 sid_string_dbg(&(*sids
)[count
])));
2476 ldap_value_free_len(values
);
2481 * pull a SEC_DESC from a ADS result
2482 * @param ads connection to ads server
2483 * @param mem_ctx TALLOC_CTX for allocating sid array
2484 * @param msg Results of search
2485 * @param field Attribute to retrieve
2486 * @param sd Pointer to *SEC_DESC to store result (talloc()ed)
2487 * @return boolean inidicating success
2489 bool ads_pull_sd(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
,
2490 LDAPMessage
*msg
, const char *field
, SEC_DESC
**sd
)
2492 struct berval
**values
;
2495 values
= ldap_get_values_len(ads
->ldap
.ld
, msg
, field
);
2497 if (!values
) return false;
2501 status
= unmarshall_sec_desc(mem_ctx
,
2502 (uint8
*)values
[0]->bv_val
,
2503 values
[0]->bv_len
, sd
);
2504 if (!NT_STATUS_IS_OK(status
)) {
2505 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
2506 nt_errstr(status
)));
2511 ldap_value_free_len(values
);
2516 * in order to support usernames longer than 21 characters we need to
2517 * use both the sAMAccountName and the userPrincipalName attributes
2518 * It seems that not all users have the userPrincipalName attribute set
2520 * @param ads connection to ads server
2521 * @param mem_ctx TALLOC_CTX for allocating sid array
2522 * @param msg Results of search
2523 * @return the username
2525 char *ads_pull_username(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
,
2531 /* lookup_name() only works on the sAMAccountName to
2532 returning the username portion of userPrincipalName
2533 breaks winbindd_getpwnam() */
2535 ret
= ads_pull_string(ads
, mem_ctx
, msg
, "userPrincipalName");
2536 if (ret
&& (p
= strchr_m(ret
, '@'))) {
2541 return ads_pull_string(ads
, mem_ctx
, msg
, "sAMAccountName");
2546 * find the update serial number - this is the core of the ldap cache
2547 * @param ads connection to ads server
2548 * @param ads connection to ADS server
2549 * @param usn Pointer to retrieved update serial number
2550 * @return status of search
2552 ADS_STATUS
ads_USN(ADS_STRUCT
*ads
, uint32
*usn
)
2554 const char *attrs
[] = {"highestCommittedUSN", NULL
};
2558 status
= ads_do_search_retry(ads
, "", LDAP_SCOPE_BASE
, "(objectclass=*)", attrs
, &res
);
2559 if (!ADS_ERR_OK(status
))
2562 if (ads_count_replies(ads
, res
) != 1) {
2563 ads_msgfree(ads
, res
);
2564 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED
);
2567 if (!ads_pull_uint32(ads
, res
, "highestCommittedUSN", usn
)) {
2568 ads_msgfree(ads
, res
);
2569 return ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE
);
2572 ads_msgfree(ads
, res
);
2576 /* parse a ADS timestring - typical string is
2577 '20020917091222.0Z0' which means 09:12.22 17th September
2579 static time_t ads_parse_time(const char *str
)
2585 if (sscanf(str
, "%4d%2d%2d%2d%2d%2d",
2586 &tm
.tm_year
, &tm
.tm_mon
, &tm
.tm_mday
,
2587 &tm
.tm_hour
, &tm
.tm_min
, &tm
.tm_sec
) != 6) {
2596 /********************************************************************
2597 ********************************************************************/
2599 ADS_STATUS
ads_current_time(ADS_STRUCT
*ads
)
2601 const char *attrs
[] = {"currentTime", NULL
};
2606 ADS_STRUCT
*ads_s
= ads
;
2608 if (!(ctx
= talloc_init("ads_current_time"))) {
2609 return ADS_ERROR(LDAP_NO_MEMORY
);
2612 /* establish a new ldap tcp session if necessary */
2614 if ( !ads
->ldap
.ld
) {
2615 if ( (ads_s
= ads_init( ads
->server
.realm
, ads
->server
.workgroup
,
2616 ads
->server
.ldap_server
)) == NULL
)
2620 ads_s
->auth
.flags
= ADS_AUTH_ANON_BIND
;
2621 status
= ads_connect( ads_s
);
2622 if ( !ADS_ERR_OK(status
))
2626 status
= ads_do_search(ads_s
, "", LDAP_SCOPE_BASE
, "(objectclass=*)", attrs
, &res
);
2627 if (!ADS_ERR_OK(status
)) {
2631 timestr
= ads_pull_string(ads_s
, ctx
, res
, "currentTime");
2633 ads_msgfree(ads_s
, res
);
2634 status
= ADS_ERROR(LDAP_NO_RESULTS_RETURNED
);
2638 /* but save the time and offset in the original ADS_STRUCT */
2640 ads
->config
.current_time
= ads_parse_time(timestr
);
2642 if (ads
->config
.current_time
!= 0) {
2643 ads
->auth
.time_offset
= ads
->config
.current_time
- time(NULL
);
2644 DEBUG(4,("time offset is %d seconds\n", ads
->auth
.time_offset
));
2647 ads_msgfree(ads
, res
);
2649 status
= ADS_SUCCESS
;
2652 /* free any temporary ads connections */
2653 if ( ads_s
!= ads
) {
2654 ads_destroy( &ads_s
);
2656 talloc_destroy(ctx
);
2661 /********************************************************************
2662 ********************************************************************/
2664 ADS_STATUS
ads_domain_func_level(ADS_STRUCT
*ads
, uint32
*val
)
2666 const char *attrs
[] = {"domainFunctionality", NULL
};
2669 ADS_STRUCT
*ads_s
= ads
;
2671 *val
= DS_DOMAIN_FUNCTION_2000
;
2673 /* establish a new ldap tcp session if necessary */
2675 if ( !ads
->ldap
.ld
) {
2676 if ( (ads_s
= ads_init( ads
->server
.realm
, ads
->server
.workgroup
,
2677 ads
->server
.ldap_server
)) == NULL
)
2679 status
= ADS_ERROR_NT(NT_STATUS_NO_MEMORY
);
2682 ads_s
->auth
.flags
= ADS_AUTH_ANON_BIND
;
2683 status
= ads_connect( ads_s
);
2684 if ( !ADS_ERR_OK(status
))
2688 /* If the attribute does not exist assume it is a Windows 2000
2689 functional domain */
2691 status
= ads_do_search(ads_s
, "", LDAP_SCOPE_BASE
, "(objectclass=*)", attrs
, &res
);
2692 if (!ADS_ERR_OK(status
)) {
2693 if ( status
.err
.rc
== LDAP_NO_SUCH_ATTRIBUTE
) {
2694 status
= ADS_SUCCESS
;
2699 if ( !ads_pull_uint32(ads_s
, res
, "domainFunctionality", val
) ) {
2700 DEBUG(5,("ads_domain_func_level: Failed to pull the domainFunctionality attribute.\n"));
2702 DEBUG(3,("ads_domain_func_level: %d\n", *val
));
2705 ads_msgfree(ads
, res
);
2708 /* free any temporary ads connections */
2709 if ( ads_s
!= ads
) {
2710 ads_destroy( &ads_s
);
2717 * find the domain sid for our domain
2718 * @param ads connection to ads server
2719 * @param sid Pointer to domain sid
2720 * @return status of search
2722 ADS_STATUS
ads_domain_sid(ADS_STRUCT
*ads
, DOM_SID
*sid
)
2724 const char *attrs
[] = {"objectSid", NULL
};
2728 rc
= ads_do_search_retry(ads
, ads
->config
.bind_path
, LDAP_SCOPE_BASE
, "(objectclass=*)",
2730 if (!ADS_ERR_OK(rc
)) return rc
;
2731 if (!ads_pull_sid(ads
, res
, "objectSid", sid
)) {
2732 ads_msgfree(ads
, res
);
2733 return ADS_ERROR_SYSTEM(ENOENT
);
2735 ads_msgfree(ads
, res
);
2741 * find our site name
2742 * @param ads connection to ads server
2743 * @param mem_ctx Pointer to talloc context
2744 * @param site_name Pointer to the sitename
2745 * @return status of search
2747 ADS_STATUS
ads_site_dn(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
, const char **site_name
)
2751 const char *dn
, *service_name
;
2752 const char *attrs
[] = { "dsServiceName", NULL
};
2754 status
= ads_do_search(ads
, "", LDAP_SCOPE_BASE
, "(objectclass=*)", attrs
, &res
);
2755 if (!ADS_ERR_OK(status
)) {
2759 service_name
= ads_pull_string(ads
, mem_ctx
, res
, "dsServiceName");
2760 if (service_name
== NULL
) {
2761 ads_msgfree(ads
, res
);
2762 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED
);
2765 ads_msgfree(ads
, res
);
2767 /* go up three levels */
2768 dn
= ads_parent_dn(ads_parent_dn(ads_parent_dn(service_name
)));
2770 return ADS_ERROR(LDAP_NO_MEMORY
);
2773 *site_name
= talloc_strdup(mem_ctx
, dn
);
2774 if (*site_name
== NULL
) {
2775 return ADS_ERROR(LDAP_NO_MEMORY
);
2780 dsServiceName: CN=NTDS Settings,CN=W2K3DC,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,DC=ber,DC=suse,DC=de
2785 * find the site dn where a machine resides
2786 * @param ads connection to ads server
2787 * @param mem_ctx Pointer to talloc context
2788 * @param computer_name name of the machine
2789 * @param site_name Pointer to the sitename
2790 * @return status of search
2792 ADS_STATUS
ads_site_dn_for_machine(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
, const char *computer_name
, const char **site_dn
)
2796 const char *parent
, *filter
;
2797 char *config_context
= NULL
;
2800 /* shortcut a query */
2801 if (strequal(computer_name
, ads
->config
.ldap_server_name
)) {
2802 return ads_site_dn(ads
, mem_ctx
, site_dn
);
2805 status
= ads_config_path(ads
, mem_ctx
, &config_context
);
2806 if (!ADS_ERR_OK(status
)) {
2810 filter
= talloc_asprintf(mem_ctx
, "(cn=%s)", computer_name
);
2811 if (filter
== NULL
) {
2812 return ADS_ERROR(LDAP_NO_MEMORY
);
2815 status
= ads_do_search(ads
, config_context
, LDAP_SCOPE_SUBTREE
,
2816 filter
, NULL
, &res
);
2817 if (!ADS_ERR_OK(status
)) {
2821 if (ads_count_replies(ads
, res
) != 1) {
2822 ads_msgfree(ads
, res
);
2823 return ADS_ERROR(LDAP_NO_SUCH_OBJECT
);
2826 dn
= ads_get_dn(ads
, res
);
2828 ads_msgfree(ads
, res
);
2829 return ADS_ERROR(LDAP_NO_MEMORY
);
2832 /* go up three levels */
2833 parent
= ads_parent_dn(ads_parent_dn(ads_parent_dn(dn
)));
2834 if (parent
== NULL
) {
2835 ads_msgfree(ads
, res
);
2836 ads_memfree(ads
, dn
);
2837 return ADS_ERROR(LDAP_NO_MEMORY
);
2840 *site_dn
= talloc_strdup(mem_ctx
, parent
);
2841 if (*site_dn
== NULL
) {
2842 ads_msgfree(ads
, res
);
2843 ads_memfree(ads
, dn
);
2844 return ADS_ERROR(LDAP_NO_MEMORY
);
2847 ads_memfree(ads
, dn
);
2848 ads_msgfree(ads
, res
);
2854 * get the upn suffixes for a domain
2855 * @param ads connection to ads server
2856 * @param mem_ctx Pointer to talloc context
2857 * @param suffixes Pointer to an array of suffixes
2858 * @param num_suffixes Pointer to the number of suffixes
2859 * @return status of search
2861 ADS_STATUS
ads_upn_suffixes(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
, char ***suffixes
, size_t *num_suffixes
)
2866 char *config_context
= NULL
;
2867 const char *attrs
[] = { "uPNSuffixes", NULL
};
2869 status
= ads_config_path(ads
, mem_ctx
, &config_context
);
2870 if (!ADS_ERR_OK(status
)) {
2874 base
= talloc_asprintf(mem_ctx
, "cn=Partitions,%s", config_context
);
2876 return ADS_ERROR(LDAP_NO_MEMORY
);
2879 status
= ads_search_dn(ads
, &res
, base
, attrs
);
2880 if (!ADS_ERR_OK(status
)) {
2884 if (ads_count_replies(ads
, res
) != 1) {
2885 ads_msgfree(ads
, res
);
2886 return ADS_ERROR(LDAP_NO_SUCH_OBJECT
);
2889 (*suffixes
) = ads_pull_strings(ads
, mem_ctx
, res
, "uPNSuffixes", num_suffixes
);
2890 if ((*suffixes
) == NULL
) {
2891 ads_msgfree(ads
, res
);
2892 return ADS_ERROR(LDAP_NO_MEMORY
);
2895 ads_msgfree(ads
, res
);
2901 * get the joinable ous for a domain
2902 * @param ads connection to ads server
2903 * @param mem_ctx Pointer to talloc context
2904 * @param ous Pointer to an array of ous
2905 * @param num_ous Pointer to the number of ous
2906 * @return status of search
2908 ADS_STATUS
ads_get_joinable_ous(ADS_STRUCT
*ads
,
2909 TALLOC_CTX
*mem_ctx
,
2914 LDAPMessage
*res
= NULL
;
2915 LDAPMessage
*msg
= NULL
;
2916 const char *attrs
[] = { "dn", NULL
};
2919 status
= ads_search(ads
, &res
,
2920 "(|(objectClass=domain)(objectclass=organizationalUnit))",
2922 if (!ADS_ERR_OK(status
)) {
2926 count
= ads_count_replies(ads
, res
);
2928 ads_msgfree(ads
, res
);
2929 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED
);
2932 for (msg
= ads_first_entry(ads
, res
); msg
;
2933 msg
= ads_next_entry(ads
, msg
)) {
2937 dn
= ads_get_dn(ads
, msg
);
2939 ads_msgfree(ads
, res
);
2940 return ADS_ERROR(LDAP_NO_MEMORY
);
2943 if (!add_string_to_array(mem_ctx
, dn
,
2944 (const char ***)ous
,
2946 ads_memfree(ads
, dn
);
2947 ads_msgfree(ads
, res
);
2948 return ADS_ERROR(LDAP_NO_MEMORY
);
2951 ads_memfree(ads
, dn
);
2954 ads_msgfree(ads
, res
);
2961 * pull a DOM_SID from an extended dn string
2962 * @param mem_ctx TALLOC_CTX
2963 * @param extended_dn string
2964 * @param flags string type of extended_dn
2965 * @param sid pointer to a DOM_SID
2966 * @return NT_STATUS_OK on success,
2967 * NT_INVALID_PARAMETER on error,
2968 * NT_STATUS_NOT_FOUND if no SID present
2970 ADS_STATUS
ads_get_sid_from_extended_dn(TALLOC_CTX
*mem_ctx
,
2971 const char *extended_dn
,
2972 enum ads_extended_dn_flags flags
,
2978 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER
);
2981 /* otherwise extended_dn gets stripped off */
2982 if ((dn
= talloc_strdup(mem_ctx
, extended_dn
)) == NULL
) {
2983 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER
);
2986 * ADS_EXTENDED_DN_HEX_STRING:
2987 * <GUID=238e1963cb390f4bb032ba0105525a29>;<SID=010500000000000515000000bb68c8fd6b61b427572eb04556040000>;CN=gd,OU=berlin,OU=suse,DC=ber,DC=suse,DC=de
2989 * ADS_EXTENDED_DN_STRING (only with w2k3):
2990 * <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
2992 * Object with no SID, such as an Exchange Public Folder
2993 * <GUID=28907fb4bdf6854993e7f0a10b504e7c>;CN=public,CN=Microsoft Exchange System Objects,DC=sd2k3ms,DC=west,DC=isilon,DC=com
2996 p
= strchr(dn
, ';');
2998 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER
);
3001 if (strncmp(p
, ";<SID=", strlen(";<SID=")) != 0) {
3002 DEBUG(5,("No SID present in extended dn\n"));
3003 return ADS_ERROR_NT(NT_STATUS_NOT_FOUND
);
3006 p
+= strlen(";<SID=");
3010 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER
);
3015 DEBUG(100,("ads_get_sid_from_extended_dn: sid string is %s\n", p
));
3019 case ADS_EXTENDED_DN_STRING
:
3020 if (!string_to_sid(sid
, p
)) {
3021 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER
);
3024 case ADS_EXTENDED_DN_HEX_STRING
: {
3028 buf_len
= strhex_to_str(buf
, sizeof(buf
), p
, strlen(p
));
3030 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER
);
3033 if (!sid_parse(buf
, buf_len
, sid
)) {
3034 DEBUG(10,("failed to parse sid\n"));
3035 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER
);
3040 DEBUG(10,("unknown extended dn format\n"));
3041 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER
);
3044 return ADS_ERROR_NT(NT_STATUS_OK
);
3048 * pull an array of DOM_SIDs from a ADS result
3049 * @param ads connection to ads server
3050 * @param mem_ctx TALLOC_CTX for allocating sid array
3051 * @param msg Results of search
3052 * @param field Attribute to retrieve
3053 * @param flags string type of extended_dn
3054 * @param sids pointer to sid array to allocate
3055 * @return the count of SIDs pulled
3057 int ads_pull_sids_from_extendeddn(ADS_STRUCT
*ads
,
3058 TALLOC_CTX
*mem_ctx
,
3061 enum ads_extended_dn_flags flags
,
3066 size_t dn_count
, ret_count
= 0;
3069 if ((dn_strings
= ads_pull_strings(ads
, mem_ctx
, msg
, field
,
3070 &dn_count
)) == NULL
) {
3074 (*sids
) = TALLOC_ZERO_ARRAY(mem_ctx
, DOM_SID
, dn_count
+ 1);
3076 TALLOC_FREE(dn_strings
);
3080 for (i
=0; i
<dn_count
; i
++) {
3081 rc
= ads_get_sid_from_extended_dn(mem_ctx
, dn_strings
[i
],
3082 flags
, &(*sids
)[i
]);
3083 if (!ADS_ERR_OK(rc
)) {
3084 if (NT_STATUS_EQUAL(ads_ntstatus(rc
),
3085 NT_STATUS_NOT_FOUND
)) {
3090 TALLOC_FREE(dn_strings
);
3097 TALLOC_FREE(dn_strings
);
3102 /********************************************************************
3103 ********************************************************************/
3105 char* ads_get_dnshostname( ADS_STRUCT
*ads
, TALLOC_CTX
*ctx
, const char *machine_name
)
3107 LDAPMessage
*res
= NULL
;
3112 status
= ads_find_machine_acct(ads
, &res
, global_myname());
3113 if (!ADS_ERR_OK(status
)) {
3114 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
3119 if ( (count
= ads_count_replies(ads
, res
)) != 1 ) {
3120 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count
));
3124 if ( (name
= ads_pull_string(ads
, ctx
, res
, "dNSHostName")) == NULL
) {
3125 DEBUG(0,("ads_get_dnshostname: No dNSHostName attribute!\n"));
3129 ads_msgfree(ads
, res
);
3134 /********************************************************************
3135 ********************************************************************/
3137 char* ads_get_upn( ADS_STRUCT
*ads
, TALLOC_CTX
*ctx
, const char *machine_name
)
3139 LDAPMessage
*res
= NULL
;
3144 status
= ads_find_machine_acct(ads
, &res
, machine_name
);
3145 if (!ADS_ERR_OK(status
)) {
3146 DEBUG(0,("ads_get_upn: Failed to find account for %s\n",
3151 if ( (count
= ads_count_replies(ads
, res
)) != 1 ) {
3152 DEBUG(1,("ads_get_upn: %d entries returned!\n", count
));
3156 if ( (name
= ads_pull_string(ads
, ctx
, res
, "userPrincipalName")) == NULL
) {
3157 DEBUG(2,("ads_get_upn: No userPrincipalName attribute!\n"));
3161 ads_msgfree(ads
, res
);
3166 /********************************************************************
3167 ********************************************************************/
3169 char* ads_get_samaccountname( ADS_STRUCT
*ads
, TALLOC_CTX
*ctx
, const char *machine_name
)
3171 LDAPMessage
*res
= NULL
;
3176 status
= ads_find_machine_acct(ads
, &res
, global_myname());
3177 if (!ADS_ERR_OK(status
)) {
3178 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
3183 if ( (count
= ads_count_replies(ads
, res
)) != 1 ) {
3184 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count
));
3188 if ( (name
= ads_pull_string(ads
, ctx
, res
, "sAMAccountName")) == NULL
) {
3189 DEBUG(0,("ads_get_dnshostname: No sAMAccountName attribute!\n"));
3193 ads_msgfree(ads
, res
);
3200 SAVED CODE
- we used to join via ldap
- remember how we did
this. JRA
.
3203 * Join a machine to a realm
3204 * Creates the machine account and sets the machine password
3205 * @param ads connection to ads server
3206 * @param machine name of host to add
3207 * @param org_unit Organizational unit to place machine in
3208 * @return status of join
3210 ADS_STATUS
ads_join_realm(ADS_STRUCT
*ads
, const char *machine_name
,
3211 uint32 account_type
, const char *org_unit
)
3214 LDAPMessage
*res
= NULL
;
3217 /* machine name must be lowercase */
3218 machine
= SMB_STRDUP(machine_name
);
3219 strlower_m(machine
);
3222 status = ads_find_machine_acct(ads, (void **)&res, machine);
3223 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
3224 DEBUG(0, ("Host account for %s already exists - deleting old account\n", machine));
3225 status = ads_leave_realm(ads, machine);
3226 if (!ADS_ERR_OK(status)) {
3227 DEBUG(0, ("Failed to delete host '%s' from the '%s' realm.\n",
3228 machine, ads->config.realm));
3233 status
= ads_add_machine_acct(ads
, machine
, account_type
, org_unit
);
3234 if (!ADS_ERR_OK(status
)) {
3235 DEBUG(0, ("ads_join_realm: ads_add_machine_acct failed (%s): %s\n", machine
, ads_errstr(status
)));
3240 status
= ads_find_machine_acct(ads
, (void **)(void *)&res
, machine
);
3241 if (!ADS_ERR_OK(status
)) {
3242 DEBUG(0, ("ads_join_realm: Host account test failed for machine %s\n", machine
));
3248 ads_msgfree(ads
, res
);
3255 * Delete a machine from the realm
3256 * @param ads connection to ads server
3257 * @param hostname Machine to remove
3258 * @return status of delete
3260 ADS_STATUS
ads_leave_realm(ADS_STRUCT
*ads
, const char *hostname
)
3265 char *hostnameDN
, *host
;
3267 LDAPControl ldap_control
;
3268 LDAPControl
* pldap_control
[2] = {NULL
, NULL
};
3270 pldap_control
[0] = &ldap_control
;
3271 memset(&ldap_control
, 0, sizeof(LDAPControl
));
3272 ldap_control
.ldctl_oid
= (char *)LDAP_SERVER_TREE_DELETE_OID
;
3274 /* hostname must be lowercase */
3275 host
= SMB_STRDUP(hostname
);
3278 status
= ads_find_machine_acct(ads
, &res
, host
);
3279 if (!ADS_ERR_OK(status
)) {
3280 DEBUG(0, ("Host account for %s does not exist.\n", host
));
3285 msg
= ads_first_entry(ads
, res
);
3288 return ADS_ERROR_SYSTEM(ENOENT
);
3291 hostnameDN
= ads_get_dn(ads
, (LDAPMessage
*)msg
);
3293 rc
= ldap_delete_ext_s(ads
->ldap
.ld
, hostnameDN
, pldap_control
, NULL
);
3295 DEBUG(3,("ldap_delete_ext_s failed with error code %d\n", rc
));
3297 DEBUG(3,("ldap_delete_ext_s succeeded with error code %d\n", rc
));
3300 if (rc
!= LDAP_SUCCESS
) {
3301 const char *attrs
[] = { "cn", NULL
};
3302 LDAPMessage
*msg_sub
;
3304 /* we only search with scope ONE, we do not expect any further
3305 * objects to be created deeper */
3307 status
= ads_do_search_retry(ads
, hostnameDN
,
3308 LDAP_SCOPE_ONELEVEL
,
3309 "(objectclass=*)", attrs
, &res
);
3311 if (!ADS_ERR_OK(status
)) {
3313 ads_memfree(ads
, hostnameDN
);
3317 for (msg_sub
= ads_first_entry(ads
, res
); msg_sub
;
3318 msg_sub
= ads_next_entry(ads
, msg_sub
)) {
3322 if ((dn
= ads_get_dn(ads
, msg_sub
)) == NULL
) {
3324 ads_memfree(ads
, hostnameDN
);
3325 return ADS_ERROR(LDAP_NO_MEMORY
);
3328 status
= ads_del_dn(ads
, dn
);
3329 if (!ADS_ERR_OK(status
)) {
3330 DEBUG(3,("failed to delete dn %s: %s\n", dn
, ads_errstr(status
)));
3332 ads_memfree(ads
, dn
);
3333 ads_memfree(ads
, hostnameDN
);
3337 ads_memfree(ads
, dn
);
3340 /* there should be no subordinate objects anymore */
3341 status
= ads_do_search_retry(ads
, hostnameDN
,
3342 LDAP_SCOPE_ONELEVEL
,
3343 "(objectclass=*)", attrs
, &res
);
3345 if (!ADS_ERR_OK(status
) || ( (ads_count_replies(ads
, res
)) > 0 ) ) {
3347 ads_memfree(ads
, hostnameDN
);
3351 /* delete hostnameDN now */
3352 status
= ads_del_dn(ads
, hostnameDN
);
3353 if (!ADS_ERR_OK(status
)) {
3355 DEBUG(3,("failed to delete dn %s: %s\n", hostnameDN
, ads_errstr(status
)));
3356 ads_memfree(ads
, hostnameDN
);
3361 ads_memfree(ads
, hostnameDN
);
3363 status
= ads_find_machine_acct(ads
, &res
, host
);
3364 if (ADS_ERR_OK(status
) && ads_count_replies(ads
, res
) == 1) {
3365 DEBUG(3, ("Failed to remove host account.\n"));
3375 * pull all token-sids from an LDAP dn
3376 * @param ads connection to ads server
3377 * @param mem_ctx TALLOC_CTX for allocating sid array
3378 * @param dn of LDAP object
3379 * @param user_sid pointer to DOM_SID (objectSid)
3380 * @param primary_group_sid pointer to DOM_SID (self composed)
3381 * @param sids pointer to sid array to allocate
3382 * @param num_sids counter of SIDs pulled
3383 * @return status of token query
3385 ADS_STATUS
ads_get_tokensids(ADS_STRUCT
*ads
,
3386 TALLOC_CTX
*mem_ctx
,
3389 DOM_SID
*primary_group_sid
,
3394 LDAPMessage
*res
= NULL
;
3396 size_t tmp_num_sids
;
3398 DOM_SID tmp_user_sid
;
3399 DOM_SID tmp_primary_group_sid
;
3401 const char *attrs
[] = {
3408 status
= ads_search_retry_dn(ads
, &res
, dn
, attrs
);
3409 if (!ADS_ERR_OK(status
)) {
3413 count
= ads_count_replies(ads
, res
);
3415 ads_msgfree(ads
, res
);
3416 return ADS_ERROR_LDAP(LDAP_NO_SUCH_OBJECT
);
3419 if (!ads_pull_sid(ads
, res
, "objectSid", &tmp_user_sid
)) {
3420 ads_msgfree(ads
, res
);
3421 return ADS_ERROR_LDAP(LDAP_NO_MEMORY
);
3424 if (!ads_pull_uint32(ads
, res
, "primaryGroupID", &pgid
)) {
3425 ads_msgfree(ads
, res
);
3426 return ADS_ERROR_LDAP(LDAP_NO_MEMORY
);
3430 /* hack to compose the primary group sid without knowing the
3436 sid_copy(&domsid
, &tmp_user_sid
);
3438 if (!sid_split_rid(&domsid
, &dummy_rid
)) {
3439 ads_msgfree(ads
, res
);
3440 return ADS_ERROR_LDAP(LDAP_NO_MEMORY
);
3443 if (!sid_compose(&tmp_primary_group_sid
, &domsid
, pgid
)) {
3444 ads_msgfree(ads
, res
);
3445 return ADS_ERROR_LDAP(LDAP_NO_MEMORY
);
3449 tmp_num_sids
= ads_pull_sids(ads
, mem_ctx
, res
, "tokenGroups", &tmp_sids
);
3451 if (tmp_num_sids
== 0 || !tmp_sids
) {
3452 ads_msgfree(ads
, res
);
3453 return ADS_ERROR_LDAP(LDAP_NO_MEMORY
);
3457 *num_sids
= tmp_num_sids
;
3465 *user_sid
= tmp_user_sid
;
3468 if (primary_group_sid
) {
3469 *primary_group_sid
= tmp_primary_group_sid
;
3472 DEBUG(10,("ads_get_tokensids: returned %d sids\n", (int)tmp_num_sids
+ 2));
3474 ads_msgfree(ads
, res
);
3475 return ADS_ERROR_LDAP(LDAP_SUCCESS
);
3479 * Find a sAMAccoutName in LDAP
3480 * @param ads connection to ads server
3481 * @param mem_ctx TALLOC_CTX for allocating sid array
3482 * @param samaccountname to search
3483 * @param uac_ret uint32 pointer userAccountControl attribute value
3484 * @param dn_ret pointer to dn
3485 * @return status of token query
3487 ADS_STATUS
ads_find_samaccount(ADS_STRUCT
*ads
,
3488 TALLOC_CTX
*mem_ctx
,
3489 const char *samaccountname
,
3491 const char **dn_ret
)
3494 const char *attrs
[] = { "userAccountControl", NULL
};
3496 LDAPMessage
*res
= NULL
;
3500 filter
= talloc_asprintf(mem_ctx
, "(&(objectclass=user)(sAMAccountName=%s))",
3502 if (filter
== NULL
) {
3503 status
= ADS_ERROR_NT(NT_STATUS_NO_MEMORY
);
3507 status
= ads_do_search_all(ads
, ads
->config
.bind_path
,
3509 filter
, attrs
, &res
);
3511 if (!ADS_ERR_OK(status
)) {
3515 if (ads_count_replies(ads
, res
) != 1) {
3516 status
= ADS_ERROR(LDAP_NO_RESULTS_RETURNED
);
3520 dn
= ads_get_dn(ads
, res
);
3522 status
= ADS_ERROR(LDAP_NO_MEMORY
);
3526 if (!ads_pull_uint32(ads
, res
, "userAccountControl", &uac
)) {
3527 status
= ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE
);
3536 *dn_ret
= talloc_strdup(mem_ctx
, dn
);
3538 status
= ADS_ERROR(LDAP_NO_MEMORY
);
3543 ads_memfree(ads
, dn
);
3544 ads_msgfree(ads
, res
);
3550 * find our configuration path
3551 * @param ads connection to ads server
3552 * @param mem_ctx Pointer to talloc context
3553 * @param config_path Pointer to the config path
3554 * @return status of search
3556 ADS_STATUS
ads_config_path(ADS_STRUCT
*ads
,
3557 TALLOC_CTX
*mem_ctx
,
3561 LDAPMessage
*res
= NULL
;
3562 const char *config_context
= NULL
;
3563 const char *attrs
[] = { "configurationNamingContext", NULL
};
3565 status
= ads_do_search(ads
, "", LDAP_SCOPE_BASE
,
3566 "(objectclass=*)", attrs
, &res
);
3567 if (!ADS_ERR_OK(status
)) {
3571 config_context
= ads_pull_string(ads
, mem_ctx
, res
,
3572 "configurationNamingContext");
3573 ads_msgfree(ads
, res
);
3574 if (!config_context
) {
3575 return ADS_ERROR(LDAP_NO_MEMORY
);
3579 *config_path
= talloc_strdup(mem_ctx
, config_context
);
3580 if (!*config_path
) {
3581 return ADS_ERROR(LDAP_NO_MEMORY
);
3585 return ADS_ERROR(LDAP_SUCCESS
);
3589 * find the displayName of an extended right
3590 * @param ads connection to ads server
3591 * @param config_path The config path
3592 * @param mem_ctx Pointer to talloc context
3593 * @param GUID struct of the rightsGUID
3594 * @return status of search
3596 const char *ads_get_extended_right_name_by_guid(ADS_STRUCT
*ads
,
3597 const char *config_path
,
3598 TALLOC_CTX
*mem_ctx
,
3599 const struct GUID
*rights_guid
)
3602 LDAPMessage
*res
= NULL
;
3604 const char *attrs
[] = { "displayName", NULL
};
3605 const char *result
= NULL
;
3608 if (!ads
|| !mem_ctx
|| !rights_guid
) {
3612 expr
= talloc_asprintf(mem_ctx
, "(rightsGuid=%s)",
3613 smb_uuid_string(mem_ctx
, *rights_guid
));
3618 path
= talloc_asprintf(mem_ctx
, "cn=Extended-Rights,%s", config_path
);
3623 rc
= ads_do_search_retry(ads
, path
, LDAP_SCOPE_SUBTREE
,
3625 if (!ADS_ERR_OK(rc
)) {
3629 if (ads_count_replies(ads
, res
) != 1) {
3633 result
= ads_pull_string(ads
, mem_ctx
, res
, "displayName");
3636 ads_msgfree(ads
, res
);
3642 * verify or build and verify an account ou
3643 * @param mem_ctx Pointer to talloc context
3644 * @param ads connection to ads server
3646 * @return status of search
3649 ADS_STATUS
ads_check_ou_dn(TALLOC_CTX
*mem_ctx
,
3651 const char **account_ou
)
3653 struct ldb_dn
*name_dn
= NULL
;
3654 const char *name
= NULL
;
3655 char *ou_string
= NULL
;
3657 name_dn
= ldb_dn_explode(mem_ctx
, *account_ou
);
3662 ou_string
= ads_ou_string(ads
, *account_ou
);
3664 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX
);
3667 name
= talloc_asprintf(mem_ctx
, "%s,%s", ou_string
,
3668 ads
->config
.bind_path
);
3669 SAFE_FREE(ou_string
);
3671 return ADS_ERROR_LDAP(LDAP_NO_MEMORY
);
3674 name_dn
= ldb_dn_explode(mem_ctx
, name
);
3676 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX
);
3679 *account_ou
= talloc_strdup(mem_ctx
, name
);
3681 return ADS_ERROR_LDAP(LDAP_NO_MEMORY
);