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
;
122 return LDAP_TIMELIMIT_EXCEEDED
;
128 /**********************************************
129 Do client and server sitename match ?
130 **********************************************/
132 bool ads_sitename_match(ADS_STRUCT
*ads
)
134 if (ads
->config
.server_site_name
== NULL
&&
135 ads
->config
.client_site_name
== NULL
) {
136 DEBUG(10,("ads_sitename_match: both null\n"));
139 if (ads
->config
.server_site_name
&&
140 ads
->config
.client_site_name
&&
141 strequal(ads
->config
.server_site_name
,
142 ads
->config
.client_site_name
)) {
143 DEBUG(10,("ads_sitename_match: name %s match\n", ads
->config
.server_site_name
));
146 DEBUG(10,("ads_sitename_match: no match between server: %s and client: %s\n",
147 ads
->config
.server_site_name
? ads
->config
.server_site_name
: "NULL",
148 ads
->config
.client_site_name
? ads
->config
.client_site_name
: "NULL"));
152 /**********************************************
153 Is this the closest DC ?
154 **********************************************/
156 bool ads_closest_dc(ADS_STRUCT
*ads
)
158 if (ads
->config
.flags
& NBT_SERVER_CLOSEST
) {
159 DEBUG(10,("ads_closest_dc: NBT_SERVER_CLOSEST flag set\n"));
163 /* not sure if this can ever happen */
164 if (ads_sitename_match(ads
)) {
165 DEBUG(10,("ads_closest_dc: NBT_SERVER_CLOSEST flag not set but sites match\n"));
169 if (ads
->config
.client_site_name
== NULL
) {
170 DEBUG(10,("ads_closest_dc: client belongs to no site\n"));
174 DEBUG(10,("ads_closest_dc: %s is not the closest DC\n",
175 ads
->config
.ldap_server_name
));
182 try a connection to a given ldap server, returning True and setting the servers IP
183 in the ads struct if successful
185 bool ads_try_connect(ADS_STRUCT
*ads
, const char *server
)
188 struct nbt_cldap_netlogon_5 cldap_reply
;
189 TALLOC_CTX
*mem_ctx
= NULL
;
192 if (!server
|| !*server
) {
196 DEBUG(5,("ads_try_connect: sending CLDAP request to %s (realm: %s)\n",
197 server
, ads
->server
.realm
));
199 mem_ctx
= talloc_init("ads_try_connect");
201 DEBUG(0,("out of memory\n"));
205 /* this copes with inet_ntoa brokenness */
207 srv
= SMB_STRDUP(server
);
209 ZERO_STRUCT( cldap_reply
);
211 if ( !ads_cldap_netlogon_5(mem_ctx
, srv
, ads
->server
.realm
, &cldap_reply
) ) {
212 DEBUG(3,("ads_try_connect: CLDAP request %s failed.\n", srv
));
217 /* Check the CLDAP reply flags */
219 if ( !(cldap_reply
.server_type
& NBT_SERVER_LDAP
) ) {
220 DEBUG(1,("ads_try_connect: %s's CLDAP reply says it is not an LDAP server!\n",
226 /* Fill in the ads->config values */
228 SAFE_FREE(ads
->config
.realm
);
229 SAFE_FREE(ads
->config
.bind_path
);
230 SAFE_FREE(ads
->config
.ldap_server_name
);
231 SAFE_FREE(ads
->config
.server_site_name
);
232 SAFE_FREE(ads
->config
.client_site_name
);
233 SAFE_FREE(ads
->server
.workgroup
);
235 ads
->config
.flags
= cldap_reply
.server_type
;
236 ads
->config
.ldap_server_name
= SMB_STRDUP(cldap_reply
.pdc_dns_name
);
237 ads
->config
.realm
= SMB_STRDUP(cldap_reply
.dns_domain
);
238 strupper_m(ads
->config
.realm
);
239 ads
->config
.bind_path
= ads_build_dn(ads
->config
.realm
);
240 if (*cldap_reply
.server_site
) {
241 ads
->config
.server_site_name
=
242 SMB_STRDUP(cldap_reply
.server_site
);
244 if (*cldap_reply
.client_site
) {
245 ads
->config
.client_site_name
=
246 SMB_STRDUP(cldap_reply
.client_site
);
248 ads
->server
.workgroup
= SMB_STRDUP(cldap_reply
.domain
);
250 ads
->ldap
.port
= LDAP_PORT
;
251 if (!interpret_string_addr(&ads
->ldap
.ss
, srv
, 0)) {
252 DEBUG(1,("ads_try_connect: unable to convert %s "
259 /* Store our site name. */
260 sitename_store( cldap_reply
.domain
, cldap_reply
.client_site
);
261 sitename_store( cldap_reply
.dns_domain
, cldap_reply
.client_site
);
266 TALLOC_FREE(mem_ctx
);
271 /**********************************************************************
272 Try to find an AD dc using our internal name resolution routines
273 Try the realm first and then then workgroup name if netbios is not
275 **********************************************************************/
277 static NTSTATUS
ads_find_dc(ADS_STRUCT
*ads
)
279 const char *c_domain
;
282 struct ip_service
*ip_list
;
285 bool got_realm
= False
;
286 bool use_own_domain
= False
;
288 NTSTATUS status
= NT_STATUS_UNSUCCESSFUL
;
290 /* if the realm and workgroup are both empty, assume they are ours */
293 c_realm
= ads
->server
.realm
;
295 if ( !c_realm
|| !*c_realm
) {
296 /* special case where no realm and no workgroup means our own */
297 if ( !ads
->server
.workgroup
|| !*ads
->server
.workgroup
) {
298 use_own_domain
= True
;
299 c_realm
= lp_realm();
303 if (c_realm
&& *c_realm
)
308 /* we need to try once with the realm name and fallback to the
309 netbios domain name if we fail (if netbios has not been disabled */
311 if ( !got_realm
&& !lp_disable_netbios() ) {
312 c_realm
= ads
->server
.workgroup
;
313 if (!c_realm
|| !*c_realm
) {
314 if ( use_own_domain
)
315 c_realm
= lp_workgroup();
319 if ( !c_realm
|| !*c_realm
) {
320 DEBUG(0,("ads_find_dc: no realm or workgroup! Don't know what to do\n"));
321 return NT_STATUS_INVALID_PARAMETER
; /* rather need MISSING_PARAMETER ... */
324 if ( use_own_domain
) {
325 c_domain
= lp_workgroup();
327 c_domain
= ads
->server
.workgroup
;
334 * In case of LDAP we use get_dc_name() as that
335 * creates the custom krb5.conf file
337 if (!(ads
->auth
.flags
& ADS_AUTH_NO_BIND
)) {
339 struct sockaddr_storage ip_out
;
341 DEBUG(6,("ads_find_dc: (ldap) looking for %s '%s'\n",
342 (got_realm
? "realm" : "domain"), realm
));
344 if (get_dc_name(domain
, realm
, srv_name
, &ip_out
)) {
346 * we call ads_try_connect() to fill in the
347 * ads->config details
349 if (ads_try_connect(ads
, srv_name
)) {
354 return NT_STATUS_NO_LOGON_SERVERS
;
357 sitename
= sitename_fetch(realm
);
359 DEBUG(6,("ads_find_dc: (cldap) looking for %s '%s'\n",
360 (got_realm
? "realm" : "domain"), realm
));
362 status
= get_sorted_dc_list(realm
, sitename
, &ip_list
, &count
, got_realm
);
363 if (!NT_STATUS_IS_OK(status
)) {
364 /* fall back to netbios if we can */
365 if ( got_realm
&& !lp_disable_netbios() ) {
374 /* if we fail this loop, then giveup since all the IP addresses returned were dead */
375 for ( i
=0; i
<count
; i
++ ) {
376 char server
[INET6_ADDRSTRLEN
];
378 print_sockaddr(server
, sizeof(server
), &ip_list
[i
].ss
);
380 if ( !NT_STATUS_IS_OK(check_negative_conn_cache(realm
, server
)) )
384 /* realm in this case is a workgroup name. We need
385 to ignore any IP addresses in the negative connection
386 cache that match ip addresses returned in the ad realm
387 case. It sucks that I have to reproduce the logic above... */
388 c_realm
= ads
->server
.realm
;
389 if ( !c_realm
|| !*c_realm
) {
390 if ( !ads
->server
.workgroup
|| !*ads
->server
.workgroup
) {
391 c_realm
= lp_realm();
394 if (c_realm
&& *c_realm
&&
395 !NT_STATUS_IS_OK(check_negative_conn_cache(c_realm
, server
))) {
396 /* Ensure we add the workgroup name for this
397 IP address as negative too. */
398 add_failed_connection_entry( realm
, server
, NT_STATUS_UNSUCCESSFUL
);
403 if ( ads_try_connect(ads
, server
) ) {
409 /* keep track of failures */
410 add_failed_connection_entry( realm
, server
, NT_STATUS_UNSUCCESSFUL
);
415 /* In case we failed to contact one of our closest DC on our site we
416 * need to try to find another DC, retry with a site-less SRV DNS query
420 DEBUG(1,("ads_find_dc: failed to find a valid DC on our site (%s), "
421 "trying to find another DC\n", sitename
));
423 namecache_delete(realm
, 0x1C);
427 return NT_STATUS_NO_LOGON_SERVERS
;
432 * Connect to the LDAP server
433 * @param ads Pointer to an existing ADS_STRUCT
434 * @return status of connection
436 ADS_STATUS
ads_connect(ADS_STRUCT
*ads
)
438 int version
= LDAP_VERSION3
;
441 char addr
[INET6_ADDRSTRLEN
];
443 ZERO_STRUCT(ads
->ldap
);
444 ads
->ldap
.last_attempt
= time(NULL
);
445 ads
->ldap
.wrap_type
= ADS_SASLWRAP_TYPE_PLAIN
;
447 /* try with a user specified server */
449 if (DEBUGLEVEL
>= 11) {
450 char *s
= NDR_PRINT_STRUCT_STRING(talloc_tos(), ads_struct
, ads
);
451 DEBUG(11,("ads_connect: entering\n"));
452 DEBUGADD(11,("%s\n", s
));
456 if (ads
->server
.ldap_server
&&
457 ads_try_connect(ads
, ads
->server
.ldap_server
)) {
461 ntstatus
= ads_find_dc(ads
);
462 if (NT_STATUS_IS_OK(ntstatus
)) {
466 status
= ADS_ERROR_NT(ntstatus
);
471 print_sockaddr(addr
, sizeof(addr
), &ads
->ldap
.ss
);
472 DEBUG(3,("Successfully contacted LDAP server %s\n", addr
));
474 if (!ads
->auth
.user_name
) {
475 /* Must use the userPrincipalName value here or sAMAccountName
476 and not servicePrincipalName; found by Guenther Deschner */
478 asprintf(&ads
->auth
.user_name
, "%s$", global_myname() );
481 if (!ads
->auth
.realm
) {
482 ads
->auth
.realm
= SMB_STRDUP(ads
->config
.realm
);
485 if (!ads
->auth
.kdc_server
) {
486 print_sockaddr(addr
, sizeof(addr
), &ads
->ldap
.ss
);
487 ads
->auth
.kdc_server
= SMB_STRDUP(addr
);
491 /* this is a really nasty hack to avoid ADS DNS problems. It needs a patch
492 to MIT kerberos to work (tridge) */
495 asprintf(&env
, "KRB5_KDC_ADDRESS_%s", ads
->config
.realm
);
496 setenv(env
, ads
->auth
.kdc_server
, 1);
501 /* If the caller() requested no LDAP bind, then we are done */
503 if (ads
->auth
.flags
& ADS_AUTH_NO_BIND
) {
504 status
= ADS_SUCCESS
;
508 ads
->ldap
.mem_ctx
= talloc_init("ads LDAP connection memory");
509 if (!ads
->ldap
.mem_ctx
) {
510 status
= ADS_ERROR_NT(NT_STATUS_NO_MEMORY
);
514 /* Otherwise setup the TCP LDAP session */
516 ads
->ldap
.ld
= ldap_open_with_timeout(ads
->config
.ldap_server_name
,
517 LDAP_PORT
, lp_ldap_timeout());
518 if (ads
->ldap
.ld
== NULL
) {
519 status
= ADS_ERROR(LDAP_OPERATIONS_ERROR
);
522 DEBUG(3,("Connected to LDAP server %s\n", ads
->config
.ldap_server_name
));
524 /* cache the successful connection for workgroup and realm */
525 if (ads_closest_dc(ads
)) {
526 saf_store( ads
->server
.workgroup
, ads
->config
.ldap_server_name
);
527 saf_store( ads
->server
.realm
, ads
->config
.ldap_server_name
);
530 ldap_set_option(ads
->ldap
.ld
, LDAP_OPT_PROTOCOL_VERSION
, &version
);
532 status
= ADS_ERROR(smb_ldap_start_tls(ads
->ldap
.ld
, version
));
533 if (!ADS_ERR_OK(status
)) {
537 /* fill in the current time and offsets */
539 status
= ads_current_time( ads
);
540 if ( !ADS_ERR_OK(status
) ) {
544 /* Now do the bind */
546 if (ads
->auth
.flags
& ADS_AUTH_ANON_BIND
) {
547 status
= ADS_ERROR(ldap_simple_bind_s(ads
->ldap
.ld
, NULL
, NULL
));
551 if (ads
->auth
.flags
& ADS_AUTH_SIMPLE_BIND
) {
552 status
= ADS_ERROR(ldap_simple_bind_s(ads
->ldap
.ld
, ads
->auth
.user_name
, ads
->auth
.password
));
556 status
= ads_sasl_bind(ads
);
559 if (DEBUGLEVEL
>= 11) {
560 char *s
= NDR_PRINT_STRUCT_STRING(talloc_tos(), ads_struct
, ads
);
561 DEBUG(11,("ads_connect: leaving with: %s\n",
562 ads_errstr(status
)));
563 DEBUGADD(11,("%s\n", s
));
571 * Disconnect the LDAP server
572 * @param ads Pointer to an existing ADS_STRUCT
574 void ads_disconnect(ADS_STRUCT
*ads
)
577 ldap_unbind(ads
->ldap
.ld
);
580 if (ads
->ldap
.wrap_ops
&& ads
->ldap
.wrap_ops
->disconnect
) {
581 ads
->ldap
.wrap_ops
->disconnect(ads
);
583 if (ads
->ldap
.mem_ctx
) {
584 talloc_free(ads
->ldap
.mem_ctx
);
586 ZERO_STRUCT(ads
->ldap
);
590 Duplicate a struct berval into talloc'ed memory
592 static struct berval
*dup_berval(TALLOC_CTX
*ctx
, const struct berval
*in_val
)
594 struct berval
*value
;
596 if (!in_val
) return NULL
;
598 value
= TALLOC_ZERO_P(ctx
, struct berval
);
601 if (in_val
->bv_len
== 0) return value
;
603 value
->bv_len
= in_val
->bv_len
;
604 value
->bv_val
= (char *)TALLOC_MEMDUP(ctx
, in_val
->bv_val
,
610 Make a values list out of an array of (struct berval *)
612 static struct berval
**ads_dup_values(TALLOC_CTX
*ctx
,
613 const struct berval
**in_vals
)
615 struct berval
**values
;
618 if (!in_vals
) return NULL
;
619 for (i
=0; in_vals
[i
]; i
++)
621 values
= TALLOC_ZERO_ARRAY(ctx
, struct berval
*, i
+1);
622 if (!values
) return NULL
;
624 for (i
=0; in_vals
[i
]; i
++) {
625 values
[i
] = dup_berval(ctx
, in_vals
[i
]);
631 UTF8-encode a values list out of an array of (char *)
633 static char **ads_push_strvals(TALLOC_CTX
*ctx
, const char **in_vals
)
638 if (!in_vals
) return NULL
;
639 for (i
=0; in_vals
[i
]; i
++)
641 values
= TALLOC_ZERO_ARRAY(ctx
, char *, i
+1);
642 if (!values
) return NULL
;
644 for (i
=0; in_vals
[i
]; i
++) {
645 if (push_utf8_talloc(ctx
, &values
[i
], in_vals
[i
]) == (size_t) -1) {
654 Pull a (char *) array out of a UTF8-encoded values list
656 static char **ads_pull_strvals(TALLOC_CTX
*ctx
, const char **in_vals
)
661 if (!in_vals
) return NULL
;
662 for (i
=0; in_vals
[i
]; i
++)
664 values
= TALLOC_ZERO_ARRAY(ctx
, char *, i
+1);
665 if (!values
) return NULL
;
667 for (i
=0; in_vals
[i
]; i
++) {
668 pull_utf8_talloc(ctx
, &values
[i
], in_vals
[i
]);
674 * Do a search with paged results. cookie must be null on the first
675 * call, and then returned on each subsequent call. It will be null
676 * again when the entire search is complete
677 * @param ads connection to ads server
678 * @param bind_path Base dn for the search
679 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
680 * @param expr Search expression - specified in local charset
681 * @param attrs Attributes to retrieve - specified in utf8 or ascii
682 * @param res ** which will contain results - free res* with ads_msgfree()
683 * @param count Number of entries retrieved on this page
684 * @param cookie The paged results cookie to be returned on subsequent calls
685 * @return status of search
687 static ADS_STATUS
ads_do_paged_search_args(ADS_STRUCT
*ads
,
688 const char *bind_path
,
689 int scope
, const char *expr
,
690 const char **attrs
, void *args
,
692 int *count
, struct berval
**cookie
)
695 char *utf8_expr
, *utf8_path
, **search_attrs
;
696 LDAPControl PagedResults
, NoReferrals
, ExternalCtrl
, *controls
[4], **rcontrols
;
697 BerElement
*cookie_be
= NULL
;
698 struct berval
*cookie_bv
= NULL
;
699 BerElement
*ext_be
= NULL
;
700 struct berval
*ext_bv
= NULL
;
703 ads_control
*external_control
= (ads_control
*) args
;
707 if (!(ctx
= talloc_init("ads_do_paged_search_args")))
708 return ADS_ERROR(LDAP_NO_MEMORY
);
710 /* 0 means the conversion worked but the result was empty
711 so we only fail if it's -1. In any case, it always
712 at least nulls out the dest */
713 if ((push_utf8_talloc(ctx
, &utf8_expr
, expr
) == (size_t)-1) ||
714 (push_utf8_talloc(ctx
, &utf8_path
, bind_path
) == (size_t)-1)) {
719 if (!attrs
|| !(*attrs
))
722 /* This would be the utf8-encoded version...*/
723 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
724 if (!(str_list_copy(talloc_tos(), &search_attrs
, attrs
))) {
730 /* Paged results only available on ldap v3 or later */
731 ldap_get_option(ads
->ldap
.ld
, LDAP_OPT_PROTOCOL_VERSION
, &version
);
732 if (version
< LDAP_VERSION3
) {
733 rc
= LDAP_NOT_SUPPORTED
;
737 cookie_be
= ber_alloc_t(LBER_USE_DER
);
739 ber_printf(cookie_be
, "{iO}", (ber_int_t
) 1000, *cookie
);
740 ber_bvfree(*cookie
); /* don't need it from last time */
743 ber_printf(cookie_be
, "{io}", (ber_int_t
) 1000, "", 0);
745 ber_flatten(cookie_be
, &cookie_bv
);
746 PagedResults
.ldctl_oid
= CONST_DISCARD(char *, ADS_PAGE_CTL_OID
);
747 PagedResults
.ldctl_iscritical
= (char) 1;
748 PagedResults
.ldctl_value
.bv_len
= cookie_bv
->bv_len
;
749 PagedResults
.ldctl_value
.bv_val
= cookie_bv
->bv_val
;
751 NoReferrals
.ldctl_oid
= CONST_DISCARD(char *, ADS_NO_REFERRALS_OID
);
752 NoReferrals
.ldctl_iscritical
= (char) 0;
753 NoReferrals
.ldctl_value
.bv_len
= 0;
754 NoReferrals
.ldctl_value
.bv_val
= CONST_DISCARD(char *, "");
756 if (external_control
&&
757 (strequal(external_control
->control
, ADS_EXTENDED_DN_OID
) ||
758 strequal(external_control
->control
, ADS_SD_FLAGS_OID
))) {
760 ExternalCtrl
.ldctl_oid
= CONST_DISCARD(char *, external_control
->control
);
761 ExternalCtrl
.ldctl_iscritical
= (char) external_control
->critical
;
763 /* win2k does not accept a ldctl_value beeing passed in */
765 if (external_control
->val
!= 0) {
767 if ((ext_be
= ber_alloc_t(LBER_USE_DER
)) == NULL
) {
772 if ((ber_printf(ext_be
, "{i}", (ber_int_t
) external_control
->val
)) == -1) {
776 if ((ber_flatten(ext_be
, &ext_bv
)) == -1) {
781 ExternalCtrl
.ldctl_value
.bv_len
= ext_bv
->bv_len
;
782 ExternalCtrl
.ldctl_value
.bv_val
= ext_bv
->bv_val
;
785 ExternalCtrl
.ldctl_value
.bv_len
= 0;
786 ExternalCtrl
.ldctl_value
.bv_val
= NULL
;
789 controls
[0] = &NoReferrals
;
790 controls
[1] = &PagedResults
;
791 controls
[2] = &ExternalCtrl
;
795 controls
[0] = &NoReferrals
;
796 controls
[1] = &PagedResults
;
800 /* we need to disable referrals as the openldap libs don't
801 handle them and paged results at the same time. Using them
802 together results in the result record containing the server
803 page control being removed from the result list (tridge/jmcd)
805 leaving this in despite the control that says don't generate
806 referrals, in case the server doesn't support it (jmcd)
808 ldap_set_option(ads
->ldap
.ld
, LDAP_OPT_REFERRALS
, LDAP_OPT_OFF
);
810 rc
= ldap_search_with_timeout(ads
->ldap
.ld
, utf8_path
, scope
, utf8_expr
,
811 search_attrs
, 0, controls
,
813 (LDAPMessage
**)res
);
815 ber_free(cookie_be
, 1);
816 ber_bvfree(cookie_bv
);
819 DEBUG(3,("ads_do_paged_search_args: ldap_search_with_timeout(%s) -> %s\n", expr
,
820 ldap_err2string(rc
)));
824 rc
= ldap_parse_result(ads
->ldap
.ld
, *res
, NULL
, NULL
, NULL
,
825 NULL
, &rcontrols
, 0);
831 for (i
=0; rcontrols
[i
]; i
++) {
832 if (strcmp(ADS_PAGE_CTL_OID
, rcontrols
[i
]->ldctl_oid
) == 0) {
833 cookie_be
= ber_init(&rcontrols
[i
]->ldctl_value
);
834 ber_scanf(cookie_be
,"{iO}", (ber_int_t
*) count
,
836 /* the berval is the cookie, but must be freed when
838 if (cookie_bv
->bv_len
) /* still more to do */
839 *cookie
=ber_bvdup(cookie_bv
);
842 ber_bvfree(cookie_bv
);
843 ber_free(cookie_be
, 1);
847 ldap_controls_free(rcontrols
);
860 /* if/when we decide to utf8-encode attrs, take out this next line */
861 TALLOC_FREE(search_attrs
);
863 return ADS_ERROR(rc
);
866 static ADS_STATUS
ads_do_paged_search(ADS_STRUCT
*ads
, const char *bind_path
,
867 int scope
, const char *expr
,
868 const char **attrs
, LDAPMessage
**res
,
869 int *count
, struct berval
**cookie
)
871 return ads_do_paged_search_args(ads
, bind_path
, scope
, expr
, attrs
, NULL
, res
, count
, cookie
);
876 * Get all results for a search. This uses ads_do_paged_search() to return
877 * all entries in a large search.
878 * @param ads connection to ads server
879 * @param bind_path Base dn for the search
880 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
881 * @param expr Search expression
882 * @param attrs Attributes to retrieve
883 * @param res ** which will contain results - free res* with ads_msgfree()
884 * @return status of search
886 ADS_STATUS
ads_do_search_all_args(ADS_STRUCT
*ads
, const char *bind_path
,
887 int scope
, const char *expr
,
888 const char **attrs
, void *args
,
891 struct berval
*cookie
= NULL
;
896 status
= ads_do_paged_search_args(ads
, bind_path
, scope
, expr
, attrs
, args
, res
,
899 if (!ADS_ERR_OK(status
))
902 #ifdef HAVE_LDAP_ADD_RESULT_ENTRY
904 LDAPMessage
*res2
= NULL
;
906 LDAPMessage
*msg
, *next
;
908 status2
= ads_do_paged_search_args(ads
, bind_path
, scope
, expr
,
909 attrs
, args
, &res2
, &count
, &cookie
);
911 if (!ADS_ERR_OK(status2
)) break;
913 /* this relies on the way that ldap_add_result_entry() works internally. I hope
914 that this works on all ldap libs, but I have only tested with openldap */
915 for (msg
= ads_first_message(ads
, res2
); msg
; msg
= next
) {
916 next
= ads_next_message(ads
, msg
);
917 ldap_add_result_entry((LDAPMessage
**)res
, msg
);
919 /* note that we do not free res2, as the memory is now
920 part of the main returned list */
923 DEBUG(0, ("no ldap_add_result_entry() support in LDAP libs!\n"));
924 status
= ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL
);
930 ADS_STATUS
ads_do_search_all(ADS_STRUCT
*ads
, const char *bind_path
,
931 int scope
, const char *expr
,
932 const char **attrs
, LDAPMessage
**res
)
934 return ads_do_search_all_args(ads
, bind_path
, scope
, expr
, attrs
, NULL
, res
);
937 ADS_STATUS
ads_do_search_all_sd_flags(ADS_STRUCT
*ads
, const char *bind_path
,
938 int scope
, const char *expr
,
939 const char **attrs
, uint32 sd_flags
,
944 args
.control
= ADS_SD_FLAGS_OID
;
946 args
.critical
= True
;
948 return ads_do_search_all_args(ads
, bind_path
, scope
, expr
, attrs
, &args
, res
);
953 * Run a function on all results for a search. Uses ads_do_paged_search() and
954 * runs the function as each page is returned, using ads_process_results()
955 * @param ads connection to ads server
956 * @param bind_path Base dn for the search
957 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
958 * @param expr Search expression - specified in local charset
959 * @param attrs Attributes to retrieve - specified in UTF-8 or ascii
960 * @param fn Function which takes attr name, values list, and data_area
961 * @param data_area Pointer which is passed to function on each call
962 * @return status of search
964 ADS_STATUS
ads_do_search_all_fn(ADS_STRUCT
*ads
, const char *bind_path
,
965 int scope
, const char *expr
, const char **attrs
,
966 bool (*fn
)(ADS_STRUCT
*, char *, void **, void *),
969 struct berval
*cookie
= NULL
;
974 status
= ads_do_paged_search(ads
, bind_path
, scope
, expr
, attrs
, &res
,
977 if (!ADS_ERR_OK(status
)) return status
;
979 ads_process_results(ads
, res
, fn
, data_area
);
980 ads_msgfree(ads
, res
);
983 status
= ads_do_paged_search(ads
, bind_path
, scope
, expr
, attrs
,
984 &res
, &count
, &cookie
);
986 if (!ADS_ERR_OK(status
)) break;
988 ads_process_results(ads
, res
, fn
, data_area
);
989 ads_msgfree(ads
, res
);
996 * Do a search with a timeout.
997 * @param ads connection to ads server
998 * @param bind_path Base dn for the search
999 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1000 * @param expr Search expression
1001 * @param attrs Attributes to retrieve
1002 * @param res ** which will contain results - free res* with ads_msgfree()
1003 * @return status of search
1005 ADS_STATUS
ads_do_search(ADS_STRUCT
*ads
, const char *bind_path
, int scope
,
1007 const char **attrs
, LDAPMessage
**res
)
1010 char *utf8_expr
, *utf8_path
, **search_attrs
= NULL
;
1014 if (!(ctx
= talloc_init("ads_do_search"))) {
1015 DEBUG(1,("ads_do_search: talloc_init() failed!"));
1016 return ADS_ERROR(LDAP_NO_MEMORY
);
1019 /* 0 means the conversion worked but the result was empty
1020 so we only fail if it's negative. In any case, it always
1021 at least nulls out the dest */
1022 if ((push_utf8_talloc(ctx
, &utf8_expr
, expr
) == (size_t)-1) ||
1023 (push_utf8_talloc(ctx
, &utf8_path
, bind_path
) == (size_t)-1)) {
1024 DEBUG(1,("ads_do_search: push_utf8_talloc() failed!"));
1025 rc
= LDAP_NO_MEMORY
;
1029 if (!attrs
|| !(*attrs
))
1030 search_attrs
= NULL
;
1032 /* This would be the utf8-encoded version...*/
1033 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
1034 if (!(str_list_copy(talloc_tos(), &search_attrs
, attrs
)))
1036 DEBUG(1,("ads_do_search: str_list_copy() failed!"));
1037 rc
= LDAP_NO_MEMORY
;
1042 /* see the note in ads_do_paged_search - we *must* disable referrals */
1043 ldap_set_option(ads
->ldap
.ld
, LDAP_OPT_REFERRALS
, LDAP_OPT_OFF
);
1045 rc
= ldap_search_with_timeout(ads
->ldap
.ld
, utf8_path
, scope
, utf8_expr
,
1046 search_attrs
, 0, NULL
, NULL
,
1048 (LDAPMessage
**)res
);
1050 if (rc
== LDAP_SIZELIMIT_EXCEEDED
) {
1051 DEBUG(3,("Warning! sizelimit exceeded in ldap. Truncating.\n"));
1056 talloc_destroy(ctx
);
1057 /* if/when we decide to utf8-encode attrs, take out this next line */
1058 TALLOC_FREE(search_attrs
);
1059 return ADS_ERROR(rc
);
1062 * Do a general ADS search
1063 * @param ads connection to ads server
1064 * @param res ** which will contain results - free res* with ads_msgfree()
1065 * @param expr Search expression
1066 * @param attrs Attributes to retrieve
1067 * @return status of search
1069 ADS_STATUS
ads_search(ADS_STRUCT
*ads
, LDAPMessage
**res
,
1070 const char *expr
, const char **attrs
)
1072 return ads_do_search(ads
, ads
->config
.bind_path
, LDAP_SCOPE_SUBTREE
,
1077 * Do a search on a specific DistinguishedName
1078 * @param ads connection to ads server
1079 * @param res ** which will contain results - free res* with ads_msgfree()
1080 * @param dn DistinguishName to search
1081 * @param attrs Attributes to retrieve
1082 * @return status of search
1084 ADS_STATUS
ads_search_dn(ADS_STRUCT
*ads
, LDAPMessage
**res
,
1085 const char *dn
, const char **attrs
)
1087 return ads_do_search(ads
, dn
, LDAP_SCOPE_BASE
, "(objectclass=*)",
1092 * Free up memory from a ads_search
1093 * @param ads connection to ads server
1094 * @param msg Search results to free
1096 void ads_msgfree(ADS_STRUCT
*ads
, LDAPMessage
*msg
)
1103 * Free up memory from various ads requests
1104 * @param ads connection to ads server
1105 * @param mem Area to free
1107 void ads_memfree(ADS_STRUCT
*ads
, void *mem
)
1113 * Get a dn from search results
1114 * @param ads connection to ads server
1115 * @param msg Search result
1118 char *ads_get_dn(ADS_STRUCT
*ads
, LDAPMessage
*msg
)
1120 char *utf8_dn
, *unix_dn
;
1122 utf8_dn
= ldap_get_dn(ads
->ldap
.ld
, msg
);
1125 DEBUG (5, ("ads_get_dn: ldap_get_dn failed\n"));
1129 if (pull_utf8_allocate(&unix_dn
, utf8_dn
) == (size_t)-1) {
1130 DEBUG(0,("ads_get_dn: string conversion failure utf8 [%s]\n",
1134 ldap_memfree(utf8_dn
);
1139 * Get the parent from a dn
1140 * @param dn the dn to return the parent from
1141 * @return parent dn string
1143 char *ads_parent_dn(const char *dn
)
1151 p
= strchr(dn
, ',');
1161 * Find a machine account given a hostname
1162 * @param ads connection to ads server
1163 * @param res ** which will contain results - free res* with ads_msgfree()
1164 * @param host Hostname to search for
1165 * @return status of search
1167 ADS_STATUS
ads_find_machine_acct(ADS_STRUCT
*ads
, LDAPMessage
**res
,
1168 const char *machine
)
1172 const char *attrs
[] = {"*", "nTSecurityDescriptor", NULL
};
1176 /* the easiest way to find a machine account anywhere in the tree
1177 is to look for hostname$ */
1178 if (asprintf(&expr
, "(samAccountName=%s$)", machine
) == -1) {
1179 DEBUG(1, ("asprintf failed!\n"));
1180 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY
);
1183 status
= ads_search(ads
, res
, expr
, attrs
);
1189 * Initialize a list of mods to be used in a modify request
1190 * @param ctx An initialized TALLOC_CTX
1191 * @return allocated ADS_MODLIST
1193 ADS_MODLIST
ads_init_mods(TALLOC_CTX
*ctx
)
1195 #define ADS_MODLIST_ALLOC_SIZE 10
1198 if ((mods
= TALLOC_ZERO_ARRAY(ctx
, LDAPMod
*, ADS_MODLIST_ALLOC_SIZE
+ 1)))
1199 /* -1 is safety to make sure we don't go over the end.
1200 need to reset it to NULL before doing ldap modify */
1201 mods
[ADS_MODLIST_ALLOC_SIZE
] = (LDAPMod
*) -1;
1203 return (ADS_MODLIST
)mods
;
1208 add an attribute to the list, with values list already constructed
1210 static ADS_STATUS
ads_modlist_add(TALLOC_CTX
*ctx
, ADS_MODLIST
*mods
,
1211 int mod_op
, const char *name
,
1212 const void *_invals
)
1214 const void **invals
= (const void **)_invals
;
1216 LDAPMod
**modlist
= (LDAPMod
**) *mods
;
1217 struct berval
**ber_values
= NULL
;
1218 char **char_values
= NULL
;
1221 mod_op
= LDAP_MOD_DELETE
;
1223 if (mod_op
& LDAP_MOD_BVALUES
)
1224 ber_values
= ads_dup_values(ctx
,
1225 (const struct berval
**)invals
);
1227 char_values
= ads_push_strvals(ctx
,
1228 (const char **) invals
);
1231 /* find the first empty slot */
1232 for (curmod
=0; modlist
[curmod
] && modlist
[curmod
] != (LDAPMod
*) -1;
1234 if (modlist
[curmod
] == (LDAPMod
*) -1) {
1235 if (!(modlist
= TALLOC_REALLOC_ARRAY(ctx
, modlist
, LDAPMod
*,
1236 curmod
+ADS_MODLIST_ALLOC_SIZE
+1)))
1237 return ADS_ERROR(LDAP_NO_MEMORY
);
1238 memset(&modlist
[curmod
], 0,
1239 ADS_MODLIST_ALLOC_SIZE
*sizeof(LDAPMod
*));
1240 modlist
[curmod
+ADS_MODLIST_ALLOC_SIZE
] = (LDAPMod
*) -1;
1241 *mods
= (ADS_MODLIST
)modlist
;
1244 if (!(modlist
[curmod
] = TALLOC_ZERO_P(ctx
, LDAPMod
)))
1245 return ADS_ERROR(LDAP_NO_MEMORY
);
1246 modlist
[curmod
]->mod_type
= talloc_strdup(ctx
, name
);
1247 if (mod_op
& LDAP_MOD_BVALUES
) {
1248 modlist
[curmod
]->mod_bvalues
= ber_values
;
1249 } else if (mod_op
& LDAP_MOD_DELETE
) {
1250 modlist
[curmod
]->mod_values
= NULL
;
1252 modlist
[curmod
]->mod_values
= char_values
;
1255 modlist
[curmod
]->mod_op
= mod_op
;
1256 return ADS_ERROR(LDAP_SUCCESS
);
1260 * Add a single string value to a mod list
1261 * @param ctx An initialized TALLOC_CTX
1262 * @param mods An initialized ADS_MODLIST
1263 * @param name The attribute name to add
1264 * @param val The value to add - NULL means DELETE
1265 * @return ADS STATUS indicating success of add
1267 ADS_STATUS
ads_mod_str(TALLOC_CTX
*ctx
, ADS_MODLIST
*mods
,
1268 const char *name
, const char *val
)
1270 const char *values
[2];
1276 return ads_modlist_add(ctx
, mods
, LDAP_MOD_DELETE
, name
, NULL
);
1277 return ads_modlist_add(ctx
, mods
, LDAP_MOD_REPLACE
, name
, values
);
1281 * Add an array of string values to a mod list
1282 * @param ctx An initialized TALLOC_CTX
1283 * @param mods An initialized ADS_MODLIST
1284 * @param name The attribute name to add
1285 * @param vals The array of string values to add - NULL means DELETE
1286 * @return ADS STATUS indicating success of add
1288 ADS_STATUS
ads_mod_strlist(TALLOC_CTX
*ctx
, ADS_MODLIST
*mods
,
1289 const char *name
, const char **vals
)
1292 return ads_modlist_add(ctx
, mods
, LDAP_MOD_DELETE
, name
, NULL
);
1293 return ads_modlist_add(ctx
, mods
, LDAP_MOD_REPLACE
,
1294 name
, (const void **) vals
);
1299 * Add a single ber-encoded value to a mod list
1300 * @param ctx An initialized TALLOC_CTX
1301 * @param mods An initialized ADS_MODLIST
1302 * @param name The attribute name to add
1303 * @param val The value to add - NULL means DELETE
1304 * @return ADS STATUS indicating success of add
1306 static ADS_STATUS
ads_mod_ber(TALLOC_CTX
*ctx
, ADS_MODLIST
*mods
,
1307 const char *name
, const struct berval
*val
)
1309 const struct berval
*values
[2];
1314 return ads_modlist_add(ctx
, mods
, LDAP_MOD_DELETE
, name
, NULL
);
1315 return ads_modlist_add(ctx
, mods
, LDAP_MOD_REPLACE
|LDAP_MOD_BVALUES
,
1316 name
, (const void **) values
);
1321 * Perform an ldap modify
1322 * @param ads connection to ads server
1323 * @param mod_dn DistinguishedName to modify
1324 * @param mods list of modifications to perform
1325 * @return status of modify
1327 ADS_STATUS
ads_gen_mod(ADS_STRUCT
*ads
, const char *mod_dn
, ADS_MODLIST mods
)
1330 char *utf8_dn
= NULL
;
1332 this control is needed to modify that contains a currently
1333 non-existent attribute (but allowable for the object) to run
1335 LDAPControl PermitModify
= {
1336 CONST_DISCARD(char *, ADS_PERMIT_MODIFY_OID
),
1339 LDAPControl
*controls
[2];
1341 controls
[0] = &PermitModify
;
1344 if (push_utf8_allocate(&utf8_dn
, mod_dn
) == -1) {
1345 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY
);
1348 /* find the end of the list, marked by NULL or -1 */
1349 for(i
=0;(mods
[i
]!=0)&&(mods
[i
]!=(LDAPMod
*) -1);i
++);
1350 /* make sure the end of the list is NULL */
1352 ret
= ldap_modify_ext_s(ads
->ldap
.ld
, utf8_dn
,
1353 (LDAPMod
**) mods
, controls
, NULL
);
1355 return ADS_ERROR(ret
);
1359 * Perform an ldap add
1360 * @param ads connection to ads server
1361 * @param new_dn DistinguishedName to add
1362 * @param mods list of attributes and values for DN
1363 * @return status of add
1365 ADS_STATUS
ads_gen_add(ADS_STRUCT
*ads
, const char *new_dn
, ADS_MODLIST mods
)
1368 char *utf8_dn
= NULL
;
1370 if (push_utf8_allocate(&utf8_dn
, new_dn
) == -1) {
1371 DEBUG(1, ("ads_gen_add: push_utf8_allocate failed!"));
1372 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY
);
1375 /* find the end of the list, marked by NULL or -1 */
1376 for(i
=0;(mods
[i
]!=0)&&(mods
[i
]!=(LDAPMod
*) -1);i
++);
1377 /* make sure the end of the list is NULL */
1380 ret
= ldap_add_s(ads
->ldap
.ld
, utf8_dn
, (LDAPMod
**)mods
);
1382 return ADS_ERROR(ret
);
1386 * Delete a DistinguishedName
1387 * @param ads connection to ads server
1388 * @param new_dn DistinguishedName to delete
1389 * @return status of delete
1391 ADS_STATUS
ads_del_dn(ADS_STRUCT
*ads
, char *del_dn
)
1394 char *utf8_dn
= NULL
;
1395 if (push_utf8_allocate(&utf8_dn
, del_dn
) == -1) {
1396 DEBUG(1, ("ads_del_dn: push_utf8_allocate failed!"));
1397 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY
);
1400 ret
= ldap_delete_s(ads
->ldap
.ld
, utf8_dn
);
1402 return ADS_ERROR(ret
);
1406 * Build an org unit string
1407 * if org unit is Computers or blank then assume a container, otherwise
1408 * assume a / separated list of organisational units.
1409 * jmcd: '\' is now used for escapes so certain chars can be in the ou (e.g. #)
1410 * @param ads connection to ads server
1411 * @param org_unit Organizational unit
1412 * @return org unit string - caller must free
1414 char *ads_ou_string(ADS_STRUCT
*ads
, const char *org_unit
)
1418 if (!org_unit
|| !*org_unit
) {
1420 ret
= ads_default_ou_string(ads
, WELL_KNOWN_GUID_COMPUTERS
);
1422 /* samba4 might not yet respond to a wellknownobject-query */
1423 return ret
? ret
: SMB_STRDUP("cn=Computers");
1426 if (strequal(org_unit
, "Computers")) {
1427 return SMB_STRDUP("cn=Computers");
1430 /* jmcd: removed "\\" from the separation chars, because it is
1431 needed as an escape for chars like '#' which are valid in an
1433 return ads_build_path(org_unit
, "/", "ou=", 1);
1437 * Get a org unit string for a well-known GUID
1438 * @param ads connection to ads server
1439 * @param wknguid Well known GUID
1440 * @return org unit string - caller must free
1442 char *ads_default_ou_string(ADS_STRUCT
*ads
, const char *wknguid
)
1445 LDAPMessage
*res
= NULL
;
1446 char *base
, *wkn_dn
= NULL
, *ret
= NULL
, **wkn_dn_exp
= NULL
,
1447 **bind_dn_exp
= NULL
;
1448 const char *attrs
[] = {"distinguishedName", NULL
};
1449 int new_ln
, wkn_ln
, bind_ln
, i
;
1451 if (wknguid
== NULL
) {
1455 if (asprintf(&base
, "<WKGUID=%s,%s>", wknguid
, ads
->config
.bind_path
) == -1) {
1456 DEBUG(1, ("asprintf failed!\n"));
1460 status
= ads_search_dn(ads
, &res
, base
, attrs
);
1461 if (!ADS_ERR_OK(status
)) {
1462 DEBUG(1,("Failed while searching for: %s\n", base
));
1466 if (ads_count_replies(ads
, res
) != 1) {
1470 /* substitute the bind-path from the well-known-guid-search result */
1471 wkn_dn
= ads_get_dn(ads
, res
);
1476 wkn_dn_exp
= ldap_explode_dn(wkn_dn
, 0);
1481 bind_dn_exp
= ldap_explode_dn(ads
->config
.bind_path
, 0);
1486 for (wkn_ln
=0; wkn_dn_exp
[wkn_ln
]; wkn_ln
++)
1488 for (bind_ln
=0; bind_dn_exp
[bind_ln
]; bind_ln
++)
1491 new_ln
= wkn_ln
- bind_ln
;
1493 ret
= SMB_STRDUP(wkn_dn_exp
[0]);
1498 for (i
=1; i
< new_ln
; i
++) {
1501 if (asprintf(&s
, "%s,%s", ret
, wkn_dn_exp
[i
]) == -1) {
1507 ret
= SMB_STRDUP(s
);
1516 ads_msgfree(ads
, res
);
1517 ads_memfree(ads
, wkn_dn
);
1519 ldap_value_free(wkn_dn_exp
);
1522 ldap_value_free(bind_dn_exp
);
1529 * Adds (appends) an item to an attribute array, rather then
1530 * replacing the whole list
1531 * @param ctx An initialized TALLOC_CTX
1532 * @param mods An initialized ADS_MODLIST
1533 * @param name name of the ldap attribute to append to
1534 * @param vals an array of values to add
1535 * @return status of addition
1538 ADS_STATUS
ads_add_strlist(TALLOC_CTX
*ctx
, ADS_MODLIST
*mods
,
1539 const char *name
, const char **vals
)
1541 return ads_modlist_add(ctx
, mods
, LDAP_MOD_ADD
, name
,
1542 (const void *) vals
);
1546 * Determines the computer account's current KVNO via an LDAP lookup
1547 * @param ads An initialized ADS_STRUCT
1548 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1549 * @return the kvno for the computer account, or -1 in case of a failure.
1552 uint32
ads_get_kvno(ADS_STRUCT
*ads
, const char *machine_name
)
1554 LDAPMessage
*res
= NULL
;
1555 uint32 kvno
= (uint32
)-1; /* -1 indicates a failure */
1557 const char *attrs
[] = {"msDS-KeyVersionNumber", NULL
};
1558 char *dn_string
= NULL
;
1559 ADS_STATUS ret
= ADS_ERROR(LDAP_SUCCESS
);
1561 DEBUG(5,("ads_get_kvno: Searching for host %s\n", machine_name
));
1562 if (asprintf(&filter
, "(samAccountName=%s$)", machine_name
) == -1) {
1565 ret
= ads_search(ads
, &res
, filter
, attrs
);
1567 if (!ADS_ERR_OK(ret
) && ads_count_replies(ads
, res
)) {
1568 DEBUG(1,("ads_get_kvno: Computer Account For %s not found.\n", machine_name
));
1569 ads_msgfree(ads
, res
);
1573 dn_string
= ads_get_dn(ads
, res
);
1575 DEBUG(0,("ads_get_kvno: out of memory.\n"));
1576 ads_msgfree(ads
, res
);
1579 DEBUG(5,("ads_get_kvno: Using: %s\n", dn_string
));
1580 ads_memfree(ads
, dn_string
);
1582 /* ---------------------------------------------------------
1583 * 0 is returned as a default KVNO from this point on...
1584 * This is done because Windows 2000 does not support key
1585 * version numbers. Chances are that a failure in the next
1586 * step is simply due to Windows 2000 being used for a
1587 * domain controller. */
1590 if (!ads_pull_uint32(ads
, res
, "msDS-KeyVersionNumber", &kvno
)) {
1591 DEBUG(3,("ads_get_kvno: Error Determining KVNO!\n"));
1592 DEBUG(3,("ads_get_kvno: Windows 2000 does not support KVNO's, so this may be normal.\n"));
1593 ads_msgfree(ads
, res
);
1598 DEBUG(5,("ads_get_kvno: Looked Up KVNO of: %d\n", kvno
));
1599 ads_msgfree(ads
, res
);
1604 * This clears out all registered spn's for a given hostname
1605 * @param ads An initilaized ADS_STRUCT
1606 * @param machine_name the NetBIOS name of the computer.
1607 * @return 0 upon success, non-zero otherwise.
1610 ADS_STATUS
ads_clear_service_principal_names(ADS_STRUCT
*ads
, const char *machine_name
)
1613 LDAPMessage
*res
= NULL
;
1615 const char *servicePrincipalName
[1] = {NULL
};
1616 ADS_STATUS ret
= ADS_ERROR(LDAP_SUCCESS
);
1617 char *dn_string
= NULL
;
1619 ret
= ads_find_machine_acct(ads
, &res
, machine_name
);
1620 if (!ADS_ERR_OK(ret
) || ads_count_replies(ads
, res
) != 1) {
1621 DEBUG(5,("ads_clear_service_principal_names: WARNING: Host Account for %s not found... skipping operation.\n", machine_name
));
1622 DEBUG(5,("ads_clear_service_principal_names: WARNING: Service Principals for %s have NOT been cleared.\n", machine_name
));
1623 ads_msgfree(ads
, res
);
1624 return ADS_ERROR(LDAP_NO_SUCH_OBJECT
);
1627 DEBUG(5,("ads_clear_service_principal_names: Host account for %s found\n", machine_name
));
1628 ctx
= talloc_init("ads_clear_service_principal_names");
1630 ads_msgfree(ads
, res
);
1631 return ADS_ERROR(LDAP_NO_MEMORY
);
1634 if (!(mods
= ads_init_mods(ctx
))) {
1635 talloc_destroy(ctx
);
1636 ads_msgfree(ads
, res
);
1637 return ADS_ERROR(LDAP_NO_MEMORY
);
1639 ret
= ads_mod_strlist(ctx
, &mods
, "servicePrincipalName", servicePrincipalName
);
1640 if (!ADS_ERR_OK(ret
)) {
1641 DEBUG(1,("ads_clear_service_principal_names: Error creating strlist.\n"));
1642 ads_msgfree(ads
, res
);
1643 talloc_destroy(ctx
);
1646 dn_string
= ads_get_dn(ads
, res
);
1648 talloc_destroy(ctx
);
1649 ads_msgfree(ads
, res
);
1650 return ADS_ERROR(LDAP_NO_MEMORY
);
1652 ret
= ads_gen_mod(ads
, dn_string
, mods
);
1653 ads_memfree(ads
,dn_string
);
1654 if (!ADS_ERR_OK(ret
)) {
1655 DEBUG(1,("ads_clear_service_principal_names: Error: Updating Service Principals for machine %s in LDAP\n",
1657 ads_msgfree(ads
, res
);
1658 talloc_destroy(ctx
);
1662 ads_msgfree(ads
, res
);
1663 talloc_destroy(ctx
);
1668 * This adds a service principal name to an existing computer account
1669 * (found by hostname) in AD.
1670 * @param ads An initialized ADS_STRUCT
1671 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1672 * @param my_fqdn The fully qualified DNS name of the machine
1673 * @param spn A string of the service principal to add, i.e. 'host'
1674 * @return 0 upon sucess, or non-zero if a failure occurs
1677 ADS_STATUS
ads_add_service_principal_name(ADS_STRUCT
*ads
, const char *machine_name
,
1678 const char *my_fqdn
, const char *spn
)
1682 LDAPMessage
*res
= NULL
;
1685 char *dn_string
= NULL
;
1686 const char *servicePrincipalName
[3] = {NULL
, NULL
, NULL
};
1688 ret
= ads_find_machine_acct(ads
, &res
, machine_name
);
1689 if (!ADS_ERR_OK(ret
) || ads_count_replies(ads
, res
) != 1) {
1690 DEBUG(1,("ads_add_service_principal_name: WARNING: Host Account for %s not found... skipping operation.\n",
1692 DEBUG(1,("ads_add_service_principal_name: WARNING: Service Principal '%s/%s@%s' has NOT been added.\n",
1693 spn
, machine_name
, ads
->config
.realm
));
1694 ads_msgfree(ads
, res
);
1695 return ADS_ERROR(LDAP_NO_SUCH_OBJECT
);
1698 DEBUG(1,("ads_add_service_principal_name: Host account for %s found\n", machine_name
));
1699 if (!(ctx
= talloc_init("ads_add_service_principal_name"))) {
1700 ads_msgfree(ads
, res
);
1701 return ADS_ERROR(LDAP_NO_MEMORY
);
1704 /* add short name spn */
1706 if ( (psp1
= talloc_asprintf(ctx
, "%s/%s", spn
, machine_name
)) == NULL
) {
1707 talloc_destroy(ctx
);
1708 ads_msgfree(ads
, res
);
1709 return ADS_ERROR(LDAP_NO_MEMORY
);
1712 strlower_m(&psp1
[strlen(spn
)]);
1713 servicePrincipalName
[0] = psp1
;
1715 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n",
1716 psp1
, machine_name
));
1719 /* add fully qualified spn */
1721 if ( (psp2
= talloc_asprintf(ctx
, "%s/%s", spn
, my_fqdn
)) == NULL
) {
1722 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
1726 strlower_m(&psp2
[strlen(spn
)]);
1727 servicePrincipalName
[1] = psp2
;
1729 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n",
1730 psp2
, machine_name
));
1732 if ( (mods
= ads_init_mods(ctx
)) == NULL
) {
1733 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
1737 ret
= ads_add_strlist(ctx
, &mods
, "servicePrincipalName", servicePrincipalName
);
1738 if (!ADS_ERR_OK(ret
)) {
1739 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
1743 if ( (dn_string
= ads_get_dn(ads
, res
)) == NULL
) {
1744 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
1748 ret
= ads_gen_mod(ads
, dn_string
, mods
);
1749 ads_memfree(ads
,dn_string
);
1750 if (!ADS_ERR_OK(ret
)) {
1751 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
1757 ads_msgfree(ads
, res
);
1762 * adds a machine account to the ADS server
1763 * @param ads An intialized ADS_STRUCT
1764 * @param machine_name - the NetBIOS machine name of this account.
1765 * @param account_type A number indicating the type of account to create
1766 * @param org_unit The LDAP path in which to place this account
1767 * @return 0 upon success, or non-zero otherwise
1770 ADS_STATUS
ads_create_machine_acct(ADS_STRUCT
*ads
, const char *machine_name
,
1771 const char *org_unit
)
1774 char *samAccountName
, *controlstr
;
1777 char *machine_escaped
= NULL
;
1779 const char *objectClass
[] = {"top", "person", "organizationalPerson",
1780 "user", "computer", NULL
};
1781 LDAPMessage
*res
= NULL
;
1782 uint32 acct_control
= ( UF_WORKSTATION_TRUST_ACCOUNT
|\
1783 UF_DONT_EXPIRE_PASSWD
|\
1784 UF_ACCOUNTDISABLE
);
1786 if (!(ctx
= talloc_init("ads_add_machine_acct")))
1787 return ADS_ERROR(LDAP_NO_MEMORY
);
1789 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
1791 machine_escaped
= escape_rdn_val_string_alloc(machine_name
);
1792 if (!machine_escaped
) {
1796 new_dn
= talloc_asprintf(ctx
, "cn=%s,%s", machine_escaped
, org_unit
);
1797 samAccountName
= talloc_asprintf(ctx
, "%s$", machine_name
);
1799 if ( !new_dn
|| !samAccountName
) {
1803 #ifndef ENCTYPE_ARCFOUR_HMAC
1804 acct_control
|= UF_USE_DES_KEY_ONLY
;
1807 if (!(controlstr
= talloc_asprintf(ctx
, "%u", acct_control
))) {
1811 if (!(mods
= ads_init_mods(ctx
))) {
1815 ads_mod_str(ctx
, &mods
, "cn", machine_name
);
1816 ads_mod_str(ctx
, &mods
, "sAMAccountName", samAccountName
);
1817 ads_mod_strlist(ctx
, &mods
, "objectClass", objectClass
);
1818 ads_mod_str(ctx
, &mods
, "userAccountControl", controlstr
);
1820 ret
= ads_gen_add(ads
, new_dn
, mods
);
1823 SAFE_FREE(machine_escaped
);
1824 ads_msgfree(ads
, res
);
1825 talloc_destroy(ctx
);
1831 * move a machine account to another OU on the ADS server
1832 * @param ads - An intialized ADS_STRUCT
1833 * @param machine_name - the NetBIOS machine name of this account.
1834 * @param org_unit - The LDAP path in which to place this account
1835 * @param moved - whether we moved the machine account (optional)
1836 * @return 0 upon success, or non-zero otherwise
1839 ADS_STATUS
ads_move_machine_acct(ADS_STRUCT
*ads
, const char *machine_name
,
1840 const char *org_unit
, bool *moved
)
1844 LDAPMessage
*res
= NULL
;
1845 char *filter
= NULL
;
1846 char *computer_dn
= NULL
;
1848 char *computer_rdn
= NULL
;
1849 bool need_move
= False
;
1851 if (asprintf(&filter
, "(samAccountName=%s$)", machine_name
) == -1) {
1852 rc
= ADS_ERROR(LDAP_NO_MEMORY
);
1856 /* Find pre-existing machine */
1857 rc
= ads_search(ads
, &res
, filter
, NULL
);
1858 if (!ADS_ERR_OK(rc
)) {
1862 computer_dn
= ads_get_dn(ads
, res
);
1864 rc
= ADS_ERROR(LDAP_NO_MEMORY
);
1868 parent_dn
= ads_parent_dn(computer_dn
);
1869 if (strequal(parent_dn
, org_unit
)) {
1875 if (asprintf(&computer_rdn
, "CN=%s", machine_name
) == -1) {
1876 rc
= ADS_ERROR(LDAP_NO_MEMORY
);
1880 ldap_status
= ldap_rename_s(ads
->ldap
.ld
, computer_dn
, computer_rdn
,
1881 org_unit
, 1, NULL
, NULL
);
1882 rc
= ADS_ERROR(ldap_status
);
1885 ads_msgfree(ads
, res
);
1887 SAFE_FREE(computer_dn
);
1888 SAFE_FREE(computer_rdn
);
1890 if (!ADS_ERR_OK(rc
)) {
1902 dump a binary result from ldap
1904 static void dump_binary(ADS_STRUCT
*ads
, const char *field
, struct berval
**values
)
1907 for (i
=0; values
[i
]; i
++) {
1908 printf("%s: ", field
);
1909 for (j
=0; j
<values
[i
]->bv_len
; j
++) {
1910 printf("%02X", (unsigned char)values
[i
]->bv_val
[j
]);
1916 static void dump_guid(ADS_STRUCT
*ads
, const char *field
, struct berval
**values
)
1919 for (i
=0; values
[i
]; i
++) {
1924 memcpy(guid
.info
, values
[i
]->bv_val
, sizeof(guid
.info
));
1925 smb_uuid_unpack(guid
, &tmp
);
1926 printf("%s: %s\n", field
, smb_uuid_string(talloc_tos(), tmp
));
1931 dump a sid result from ldap
1933 static void dump_sid(ADS_STRUCT
*ads
, const char *field
, struct berval
**values
)
1936 for (i
=0; values
[i
]; i
++) {
1939 sid_parse(values
[i
]->bv_val
, values
[i
]->bv_len
, &sid
);
1940 printf("%s: %s\n", field
, sid_to_fstring(tmp
, &sid
));
1945 dump ntSecurityDescriptor
1947 static void dump_sd(ADS_STRUCT
*ads
, const char *filed
, struct berval
**values
)
1949 TALLOC_CTX
*frame
= talloc_stackframe();
1950 struct security_descriptor
*psd
;
1953 status
= unmarshall_sec_desc(talloc_tos(), (uint8
*)values
[0]->bv_val
,
1954 values
[0]->bv_len
, &psd
);
1955 if (!NT_STATUS_IS_OK(status
)) {
1956 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
1957 nt_errstr(status
)));
1963 ads_disp_sd(ads
, talloc_tos(), psd
);
1970 dump a string result from ldap
1972 static void dump_string(const char *field
, char **values
)
1975 for (i
=0; values
[i
]; i
++) {
1976 printf("%s: %s\n", field
, values
[i
]);
1981 dump a field from LDAP on stdout
1985 static bool ads_dump_field(ADS_STRUCT
*ads
, char *field
, void **values
, void *data_area
)
1990 void (*handler
)(ADS_STRUCT
*, const char *, struct berval
**);
1992 {"objectGUID", False
, dump_guid
},
1993 {"netbootGUID", False
, dump_guid
},
1994 {"nTSecurityDescriptor", False
, dump_sd
},
1995 {"dnsRecord", False
, dump_binary
},
1996 {"objectSid", False
, dump_sid
},
1997 {"tokenGroups", False
, dump_sid
},
1998 {"tokenGroupsNoGCAcceptable", False
, dump_sid
},
1999 {"tokengroupsGlobalandUniversal", False
, dump_sid
},
2000 {"mS-DS-CreatorSID", False
, dump_sid
},
2001 {"msExchMailboxGuid", False
, dump_guid
},
2006 if (!field
) { /* must be end of an entry */
2011 for (i
=0; handlers
[i
].name
; i
++) {
2012 if (StrCaseCmp(handlers
[i
].name
, field
) == 0) {
2013 if (!values
) /* first time, indicate string or not */
2014 return handlers
[i
].string
;
2015 handlers
[i
].handler(ads
, field
, (struct berval
**) values
);
2019 if (!handlers
[i
].name
) {
2020 if (!values
) /* first time, indicate string conversion */
2022 dump_string(field
, (char **)values
);
2028 * Dump a result from LDAP on stdout
2029 * used for debugging
2030 * @param ads connection to ads server
2031 * @param res Results to dump
2034 void ads_dump(ADS_STRUCT
*ads
, LDAPMessage
*res
)
2036 ads_process_results(ads
, res
, ads_dump_field
, NULL
);
2040 * Walk through results, calling a function for each entry found.
2041 * The function receives a field name, a berval * array of values,
2042 * and a data area passed through from the start. The function is
2043 * called once with null for field and values at the end of each
2045 * @param ads connection to ads server
2046 * @param res Results to process
2047 * @param fn Function for processing each result
2048 * @param data_area user-defined area to pass to function
2050 void ads_process_results(ADS_STRUCT
*ads
, LDAPMessage
*res
,
2051 bool (*fn
)(ADS_STRUCT
*, char *, void **, void *),
2057 if (!(ctx
= talloc_init("ads_process_results")))
2060 for (msg
= ads_first_entry(ads
, res
); msg
;
2061 msg
= ads_next_entry(ads
, msg
)) {
2065 for (utf8_field
=ldap_first_attribute(ads
->ldap
.ld
,
2066 (LDAPMessage
*)msg
,&b
);
2068 utf8_field
=ldap_next_attribute(ads
->ldap
.ld
,
2069 (LDAPMessage
*)msg
,b
)) {
2070 struct berval
**ber_vals
;
2071 char **str_vals
, **utf8_vals
;
2075 pull_utf8_talloc(ctx
, &field
, utf8_field
);
2076 string
= fn(ads
, field
, NULL
, data_area
);
2079 utf8_vals
= ldap_get_values(ads
->ldap
.ld
,
2080 (LDAPMessage
*)msg
, field
);
2081 str_vals
= ads_pull_strvals(ctx
,
2082 (const char **) utf8_vals
);
2083 fn(ads
, field
, (void **) str_vals
, data_area
);
2084 ldap_value_free(utf8_vals
);
2086 ber_vals
= ldap_get_values_len(ads
->ldap
.ld
,
2087 (LDAPMessage
*)msg
, field
);
2088 fn(ads
, field
, (void **) ber_vals
, data_area
);
2090 ldap_value_free_len(ber_vals
);
2092 ldap_memfree(utf8_field
);
2095 talloc_free_children(ctx
);
2096 fn(ads
, NULL
, NULL
, data_area
); /* completed an entry */
2099 talloc_destroy(ctx
);
2103 * count how many replies are in a LDAPMessage
2104 * @param ads connection to ads server
2105 * @param res Results to count
2106 * @return number of replies
2108 int ads_count_replies(ADS_STRUCT
*ads
, void *res
)
2110 return ldap_count_entries(ads
->ldap
.ld
, (LDAPMessage
*)res
);
2114 * pull the first entry from a ADS result
2115 * @param ads connection to ads server
2116 * @param res Results of search
2117 * @return first entry from result
2119 LDAPMessage
*ads_first_entry(ADS_STRUCT
*ads
, LDAPMessage
*res
)
2121 return ldap_first_entry(ads
->ldap
.ld
, res
);
2125 * pull the next entry from a ADS result
2126 * @param ads connection to ads server
2127 * @param res Results of search
2128 * @return next entry from result
2130 LDAPMessage
*ads_next_entry(ADS_STRUCT
*ads
, LDAPMessage
*res
)
2132 return ldap_next_entry(ads
->ldap
.ld
, res
);
2136 * pull the first message from a ADS result
2137 * @param ads connection to ads server
2138 * @param res Results of search
2139 * @return first message from result
2141 LDAPMessage
*ads_first_message(ADS_STRUCT
*ads
, LDAPMessage
*res
)
2143 return ldap_first_message(ads
->ldap
.ld
, res
);
2147 * pull the next message from a ADS result
2148 * @param ads connection to ads server
2149 * @param res Results of search
2150 * @return next message from result
2152 LDAPMessage
*ads_next_message(ADS_STRUCT
*ads
, LDAPMessage
*res
)
2154 return ldap_next_message(ads
->ldap
.ld
, res
);
2158 * pull a single string from a ADS result
2159 * @param ads connection to ads server
2160 * @param mem_ctx TALLOC_CTX to use for allocating result string
2161 * @param msg Results of search
2162 * @param field Attribute to retrieve
2163 * @return Result string in talloc context
2165 char *ads_pull_string(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
, LDAPMessage
*msg
,
2173 values
= ldap_get_values(ads
->ldap
.ld
, msg
, field
);
2178 rc
= pull_utf8_talloc(mem_ctx
, &ux_string
,
2180 if (rc
!= (size_t)-1)
2184 ldap_value_free(values
);
2189 * pull an array of strings from a ADS result
2190 * @param ads connection to ads server
2191 * @param mem_ctx TALLOC_CTX to use for allocating result string
2192 * @param msg Results of search
2193 * @param field Attribute to retrieve
2194 * @return Result strings in talloc context
2196 char **ads_pull_strings(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
,
2197 LDAPMessage
*msg
, const char *field
,
2204 values
= ldap_get_values(ads
->ldap
.ld
, msg
, field
);
2208 *num_values
= ldap_count_values(values
);
2210 ret
= TALLOC_ARRAY(mem_ctx
, char *, *num_values
+ 1);
2212 ldap_value_free(values
);
2216 for (i
=0;i
<*num_values
;i
++) {
2217 if (pull_utf8_talloc(mem_ctx
, &ret
[i
], values
[i
]) == -1) {
2218 ldap_value_free(values
);
2224 ldap_value_free(values
);
2229 * pull an array of strings from a ADS result
2230 * (handle large multivalue attributes with range retrieval)
2231 * @param ads connection to ads server
2232 * @param mem_ctx TALLOC_CTX to use for allocating result string
2233 * @param msg Results of search
2234 * @param field Attribute to retrieve
2235 * @param current_strings strings returned by a previous call to this function
2236 * @param next_attribute The next query should ask for this attribute
2237 * @param num_values How many values did we get this time?
2238 * @param more_values Are there more values to get?
2239 * @return Result strings in talloc context
2241 char **ads_pull_strings_range(ADS_STRUCT
*ads
,
2242 TALLOC_CTX
*mem_ctx
,
2243 LDAPMessage
*msg
, const char *field
,
2244 char **current_strings
,
2245 const char **next_attribute
,
2246 size_t *num_strings
,
2250 char *expected_range_attrib
, *range_attr
;
2251 BerElement
*ptr
= NULL
;
2254 size_t num_new_strings
;
2255 unsigned long int range_start
;
2256 unsigned long int range_end
;
2258 /* we might have been given the whole lot anyway */
2259 if ((strings
= ads_pull_strings(ads
, mem_ctx
, msg
, field
, num_strings
))) {
2260 *more_strings
= False
;
2264 expected_range_attrib
= talloc_asprintf(mem_ctx
, "%s;Range=", field
);
2266 /* look for Range result */
2267 for (attr
= ldap_first_attribute(ads
->ldap
.ld
, (LDAPMessage
*)msg
, &ptr
);
2269 attr
= ldap_next_attribute(ads
->ldap
.ld
, (LDAPMessage
*)msg
, ptr
)) {
2270 /* we ignore the fact that this is utf8, as all attributes are ascii... */
2271 if (strnequal(attr
, expected_range_attrib
, strlen(expected_range_attrib
))) {
2279 /* nothing here - this field is just empty */
2280 *more_strings
= False
;
2284 if (sscanf(&range_attr
[strlen(expected_range_attrib
)], "%lu-%lu",
2285 &range_start
, &range_end
) == 2) {
2286 *more_strings
= True
;
2288 if (sscanf(&range_attr
[strlen(expected_range_attrib
)], "%lu-*",
2289 &range_start
) == 1) {
2290 *more_strings
= False
;
2292 DEBUG(1, ("ads_pull_strings_range: Cannot parse Range attriubte (%s)\n",
2294 ldap_memfree(range_attr
);
2295 *more_strings
= False
;
2300 if ((*num_strings
) != range_start
) {
2301 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) doesn't start at %u, but at %lu"
2302 " - aborting range retreival\n",
2303 range_attr
, (unsigned int)(*num_strings
) + 1, range_start
));
2304 ldap_memfree(range_attr
);
2305 *more_strings
= False
;
2309 new_strings
= ads_pull_strings(ads
, mem_ctx
, msg
, range_attr
, &num_new_strings
);
2311 if (*more_strings
&& ((*num_strings
+ num_new_strings
) != (range_end
+ 1))) {
2312 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) tells us we have %lu "
2313 "strings in this bunch, but we only got %lu - aborting range retreival\n",
2314 range_attr
, (unsigned long int)range_end
- range_start
+ 1,
2315 (unsigned long int)num_new_strings
));
2316 ldap_memfree(range_attr
);
2317 *more_strings
= False
;
2321 strings
= TALLOC_REALLOC_ARRAY(mem_ctx
, current_strings
, char *,
2322 *num_strings
+ num_new_strings
);
2324 if (strings
== NULL
) {
2325 ldap_memfree(range_attr
);
2326 *more_strings
= False
;
2330 if (new_strings
&& num_new_strings
) {
2331 memcpy(&strings
[*num_strings
], new_strings
,
2332 sizeof(*new_strings
) * num_new_strings
);
2335 (*num_strings
) += num_new_strings
;
2337 if (*more_strings
) {
2338 *next_attribute
= talloc_asprintf(mem_ctx
,
2343 if (!*next_attribute
) {
2344 DEBUG(1, ("talloc_asprintf for next attribute failed!\n"));
2345 ldap_memfree(range_attr
);
2346 *more_strings
= False
;
2351 ldap_memfree(range_attr
);
2357 * pull a single uint32 from a ADS result
2358 * @param ads connection to ads server
2359 * @param msg Results of search
2360 * @param field Attribute to retrieve
2361 * @param v Pointer to int to store result
2362 * @return boolean inidicating success
2364 bool ads_pull_uint32(ADS_STRUCT
*ads
, LDAPMessage
*msg
, const char *field
,
2369 values
= ldap_get_values(ads
->ldap
.ld
, msg
, field
);
2373 ldap_value_free(values
);
2377 *v
= atoi(values
[0]);
2378 ldap_value_free(values
);
2383 * pull a single objectGUID from an ADS result
2384 * @param ads connection to ADS server
2385 * @param msg results of search
2386 * @param guid 37-byte area to receive text guid
2387 * @return boolean indicating success
2389 bool ads_pull_guid(ADS_STRUCT
*ads
, LDAPMessage
*msg
, struct GUID
*guid
)
2392 UUID_FLAT flat_guid
;
2394 values
= ldap_get_values(ads
->ldap
.ld
, msg
, "objectGUID");
2399 memcpy(&flat_guid
.info
, values
[0], sizeof(UUID_FLAT
));
2400 smb_uuid_unpack(flat_guid
, guid
);
2401 ldap_value_free(values
);
2404 ldap_value_free(values
);
2411 * pull a single DOM_SID from a ADS result
2412 * @param ads connection to ads server
2413 * @param msg Results of search
2414 * @param field Attribute to retrieve
2415 * @param sid Pointer to sid to store result
2416 * @return boolean inidicating success
2418 bool ads_pull_sid(ADS_STRUCT
*ads
, LDAPMessage
*msg
, const char *field
,
2421 struct berval
**values
;
2424 values
= ldap_get_values_len(ads
->ldap
.ld
, msg
, field
);
2430 ret
= sid_parse(values
[0]->bv_val
, values
[0]->bv_len
, sid
);
2432 ldap_value_free_len(values
);
2437 * pull an array of DOM_SIDs from a ADS result
2438 * @param ads connection to ads server
2439 * @param mem_ctx TALLOC_CTX for allocating sid array
2440 * @param msg Results of search
2441 * @param field Attribute to retrieve
2442 * @param sids pointer to sid array to allocate
2443 * @return the count of SIDs pulled
2445 int ads_pull_sids(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
,
2446 LDAPMessage
*msg
, const char *field
, DOM_SID
**sids
)
2448 struct berval
**values
;
2452 values
= ldap_get_values_len(ads
->ldap
.ld
, msg
, field
);
2457 for (i
=0; values
[i
]; i
++)
2461 (*sids
) = TALLOC_ARRAY(mem_ctx
, DOM_SID
, i
);
2463 ldap_value_free_len(values
);
2471 for (i
=0; values
[i
]; i
++) {
2472 ret
= sid_parse(values
[i
]->bv_val
, values
[i
]->bv_len
, &(*sids
)[count
]);
2474 DEBUG(10, ("pulling SID: %s\n",
2475 sid_string_dbg(&(*sids
)[count
])));
2480 ldap_value_free_len(values
);
2485 * pull a SEC_DESC from a ADS result
2486 * @param ads connection to ads server
2487 * @param mem_ctx TALLOC_CTX for allocating sid array
2488 * @param msg Results of search
2489 * @param field Attribute to retrieve
2490 * @param sd Pointer to *SEC_DESC to store result (talloc()ed)
2491 * @return boolean inidicating success
2493 bool ads_pull_sd(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
,
2494 LDAPMessage
*msg
, const char *field
, SEC_DESC
**sd
)
2496 struct berval
**values
;
2499 values
= ldap_get_values_len(ads
->ldap
.ld
, msg
, field
);
2501 if (!values
) return false;
2505 status
= unmarshall_sec_desc(mem_ctx
,
2506 (uint8
*)values
[0]->bv_val
,
2507 values
[0]->bv_len
, sd
);
2508 if (!NT_STATUS_IS_OK(status
)) {
2509 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
2510 nt_errstr(status
)));
2515 ldap_value_free_len(values
);
2520 * in order to support usernames longer than 21 characters we need to
2521 * use both the sAMAccountName and the userPrincipalName attributes
2522 * It seems that not all users have the userPrincipalName attribute set
2524 * @param ads connection to ads server
2525 * @param mem_ctx TALLOC_CTX for allocating sid array
2526 * @param msg Results of search
2527 * @return the username
2529 char *ads_pull_username(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
,
2535 /* lookup_name() only works on the sAMAccountName to
2536 returning the username portion of userPrincipalName
2537 breaks winbindd_getpwnam() */
2539 ret
= ads_pull_string(ads
, mem_ctx
, msg
, "userPrincipalName");
2540 if (ret
&& (p
= strchr_m(ret
, '@'))) {
2545 return ads_pull_string(ads
, mem_ctx
, msg
, "sAMAccountName");
2550 * find the update serial number - this is the core of the ldap cache
2551 * @param ads connection to ads server
2552 * @param ads connection to ADS server
2553 * @param usn Pointer to retrieved update serial number
2554 * @return status of search
2556 ADS_STATUS
ads_USN(ADS_STRUCT
*ads
, uint32
*usn
)
2558 const char *attrs
[] = {"highestCommittedUSN", NULL
};
2562 status
= ads_do_search_retry(ads
, "", LDAP_SCOPE_BASE
, "(objectclass=*)", attrs
, &res
);
2563 if (!ADS_ERR_OK(status
))
2566 if (ads_count_replies(ads
, res
) != 1) {
2567 ads_msgfree(ads
, res
);
2568 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED
);
2571 if (!ads_pull_uint32(ads
, res
, "highestCommittedUSN", usn
)) {
2572 ads_msgfree(ads
, res
);
2573 return ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE
);
2576 ads_msgfree(ads
, res
);
2580 /* parse a ADS timestring - typical string is
2581 '20020917091222.0Z0' which means 09:12.22 17th September
2583 static time_t ads_parse_time(const char *str
)
2589 if (sscanf(str
, "%4d%2d%2d%2d%2d%2d",
2590 &tm
.tm_year
, &tm
.tm_mon
, &tm
.tm_mday
,
2591 &tm
.tm_hour
, &tm
.tm_min
, &tm
.tm_sec
) != 6) {
2600 /********************************************************************
2601 ********************************************************************/
2603 ADS_STATUS
ads_current_time(ADS_STRUCT
*ads
)
2605 const char *attrs
[] = {"currentTime", NULL
};
2610 ADS_STRUCT
*ads_s
= ads
;
2612 if (!(ctx
= talloc_init("ads_current_time"))) {
2613 return ADS_ERROR(LDAP_NO_MEMORY
);
2616 /* establish a new ldap tcp session if necessary */
2618 if ( !ads
->ldap
.ld
) {
2619 if ( (ads_s
= ads_init( ads
->server
.realm
, ads
->server
.workgroup
,
2620 ads
->server
.ldap_server
)) == NULL
)
2624 ads_s
->auth
.flags
= ADS_AUTH_ANON_BIND
;
2625 status
= ads_connect( ads_s
);
2626 if ( !ADS_ERR_OK(status
))
2630 status
= ads_do_search(ads_s
, "", LDAP_SCOPE_BASE
, "(objectclass=*)", attrs
, &res
);
2631 if (!ADS_ERR_OK(status
)) {
2635 timestr
= ads_pull_string(ads_s
, ctx
, res
, "currentTime");
2637 ads_msgfree(ads_s
, res
);
2638 status
= ADS_ERROR(LDAP_NO_RESULTS_RETURNED
);
2642 /* but save the time and offset in the original ADS_STRUCT */
2644 ads
->config
.current_time
= ads_parse_time(timestr
);
2646 if (ads
->config
.current_time
!= 0) {
2647 ads
->auth
.time_offset
= ads
->config
.current_time
- time(NULL
);
2648 DEBUG(4,("time offset is %d seconds\n", ads
->auth
.time_offset
));
2651 ads_msgfree(ads
, res
);
2653 status
= ADS_SUCCESS
;
2656 /* free any temporary ads connections */
2657 if ( ads_s
!= ads
) {
2658 ads_destroy( &ads_s
);
2660 talloc_destroy(ctx
);
2665 /********************************************************************
2666 ********************************************************************/
2668 ADS_STATUS
ads_domain_func_level(ADS_STRUCT
*ads
, uint32
*val
)
2670 const char *attrs
[] = {"domainFunctionality", NULL
};
2673 ADS_STRUCT
*ads_s
= ads
;
2675 *val
= DS_DOMAIN_FUNCTION_2000
;
2677 /* establish a new ldap tcp session if necessary */
2679 if ( !ads
->ldap
.ld
) {
2680 if ( (ads_s
= ads_init( ads
->server
.realm
, ads
->server
.workgroup
,
2681 ads
->server
.ldap_server
)) == NULL
)
2683 status
= ADS_ERROR_NT(NT_STATUS_NO_MEMORY
);
2686 ads_s
->auth
.flags
= ADS_AUTH_ANON_BIND
;
2687 status
= ads_connect( ads_s
);
2688 if ( !ADS_ERR_OK(status
))
2692 /* If the attribute does not exist assume it is a Windows 2000
2693 functional domain */
2695 status
= ads_do_search(ads_s
, "", LDAP_SCOPE_BASE
, "(objectclass=*)", attrs
, &res
);
2696 if (!ADS_ERR_OK(status
)) {
2697 if ( status
.err
.rc
== LDAP_NO_SUCH_ATTRIBUTE
) {
2698 status
= ADS_SUCCESS
;
2703 if ( !ads_pull_uint32(ads_s
, res
, "domainFunctionality", val
) ) {
2704 DEBUG(5,("ads_domain_func_level: Failed to pull the domainFunctionality attribute.\n"));
2706 DEBUG(3,("ads_domain_func_level: %d\n", *val
));
2709 ads_msgfree(ads
, res
);
2712 /* free any temporary ads connections */
2713 if ( ads_s
!= ads
) {
2714 ads_destroy( &ads_s
);
2721 * find the domain sid for our domain
2722 * @param ads connection to ads server
2723 * @param sid Pointer to domain sid
2724 * @return status of search
2726 ADS_STATUS
ads_domain_sid(ADS_STRUCT
*ads
, DOM_SID
*sid
)
2728 const char *attrs
[] = {"objectSid", NULL
};
2732 rc
= ads_do_search_retry(ads
, ads
->config
.bind_path
, LDAP_SCOPE_BASE
, "(objectclass=*)",
2734 if (!ADS_ERR_OK(rc
)) return rc
;
2735 if (!ads_pull_sid(ads
, res
, "objectSid", sid
)) {
2736 ads_msgfree(ads
, res
);
2737 return ADS_ERROR_SYSTEM(ENOENT
);
2739 ads_msgfree(ads
, res
);
2745 * find our site name
2746 * @param ads connection to ads server
2747 * @param mem_ctx Pointer to talloc context
2748 * @param site_name Pointer to the sitename
2749 * @return status of search
2751 ADS_STATUS
ads_site_dn(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
, const char **site_name
)
2755 const char *dn
, *service_name
;
2756 const char *attrs
[] = { "dsServiceName", NULL
};
2758 status
= ads_do_search(ads
, "", LDAP_SCOPE_BASE
, "(objectclass=*)", attrs
, &res
);
2759 if (!ADS_ERR_OK(status
)) {
2763 service_name
= ads_pull_string(ads
, mem_ctx
, res
, "dsServiceName");
2764 if (service_name
== NULL
) {
2765 ads_msgfree(ads
, res
);
2766 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED
);
2769 ads_msgfree(ads
, res
);
2771 /* go up three levels */
2772 dn
= ads_parent_dn(ads_parent_dn(ads_parent_dn(service_name
)));
2774 return ADS_ERROR(LDAP_NO_MEMORY
);
2777 *site_name
= talloc_strdup(mem_ctx
, dn
);
2778 if (*site_name
== NULL
) {
2779 return ADS_ERROR(LDAP_NO_MEMORY
);
2784 dsServiceName: CN=NTDS Settings,CN=W2K3DC,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,DC=ber,DC=suse,DC=de
2789 * find the site dn where a machine resides
2790 * @param ads connection to ads server
2791 * @param mem_ctx Pointer to talloc context
2792 * @param computer_name name of the machine
2793 * @param site_name Pointer to the sitename
2794 * @return status of search
2796 ADS_STATUS
ads_site_dn_for_machine(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
, const char *computer_name
, const char **site_dn
)
2800 const char *parent
, *filter
;
2801 char *config_context
= NULL
;
2804 /* shortcut a query */
2805 if (strequal(computer_name
, ads
->config
.ldap_server_name
)) {
2806 return ads_site_dn(ads
, mem_ctx
, site_dn
);
2809 status
= ads_config_path(ads
, mem_ctx
, &config_context
);
2810 if (!ADS_ERR_OK(status
)) {
2814 filter
= talloc_asprintf(mem_ctx
, "(cn=%s)", computer_name
);
2815 if (filter
== NULL
) {
2816 return ADS_ERROR(LDAP_NO_MEMORY
);
2819 status
= ads_do_search(ads
, config_context
, LDAP_SCOPE_SUBTREE
,
2820 filter
, NULL
, &res
);
2821 if (!ADS_ERR_OK(status
)) {
2825 if (ads_count_replies(ads
, res
) != 1) {
2826 ads_msgfree(ads
, res
);
2827 return ADS_ERROR(LDAP_NO_SUCH_OBJECT
);
2830 dn
= ads_get_dn(ads
, res
);
2832 ads_msgfree(ads
, res
);
2833 return ADS_ERROR(LDAP_NO_MEMORY
);
2836 /* go up three levels */
2837 parent
= ads_parent_dn(ads_parent_dn(ads_parent_dn(dn
)));
2838 if (parent
== NULL
) {
2839 ads_msgfree(ads
, res
);
2840 ads_memfree(ads
, dn
);
2841 return ADS_ERROR(LDAP_NO_MEMORY
);
2844 *site_dn
= talloc_strdup(mem_ctx
, parent
);
2845 if (*site_dn
== NULL
) {
2846 ads_msgfree(ads
, res
);
2847 ads_memfree(ads
, dn
);
2848 return ADS_ERROR(LDAP_NO_MEMORY
);
2851 ads_memfree(ads
, dn
);
2852 ads_msgfree(ads
, res
);
2858 * get the upn suffixes for a domain
2859 * @param ads connection to ads server
2860 * @param mem_ctx Pointer to talloc context
2861 * @param suffixes Pointer to an array of suffixes
2862 * @param num_suffixes Pointer to the number of suffixes
2863 * @return status of search
2865 ADS_STATUS
ads_upn_suffixes(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
, char ***suffixes
, size_t *num_suffixes
)
2870 char *config_context
= NULL
;
2871 const char *attrs
[] = { "uPNSuffixes", NULL
};
2873 status
= ads_config_path(ads
, mem_ctx
, &config_context
);
2874 if (!ADS_ERR_OK(status
)) {
2878 base
= talloc_asprintf(mem_ctx
, "cn=Partitions,%s", config_context
);
2880 return ADS_ERROR(LDAP_NO_MEMORY
);
2883 status
= ads_search_dn(ads
, &res
, base
, attrs
);
2884 if (!ADS_ERR_OK(status
)) {
2888 if (ads_count_replies(ads
, res
) != 1) {
2889 ads_msgfree(ads
, res
);
2890 return ADS_ERROR(LDAP_NO_SUCH_OBJECT
);
2893 (*suffixes
) = ads_pull_strings(ads
, mem_ctx
, res
, "uPNSuffixes", num_suffixes
);
2894 if ((*suffixes
) == NULL
) {
2895 ads_msgfree(ads
, res
);
2896 return ADS_ERROR(LDAP_NO_MEMORY
);
2899 ads_msgfree(ads
, res
);
2905 * get the joinable ous for a domain
2906 * @param ads connection to ads server
2907 * @param mem_ctx Pointer to talloc context
2908 * @param ous Pointer to an array of ous
2909 * @param num_ous Pointer to the number of ous
2910 * @return status of search
2912 ADS_STATUS
ads_get_joinable_ous(ADS_STRUCT
*ads
,
2913 TALLOC_CTX
*mem_ctx
,
2918 LDAPMessage
*res
= NULL
;
2919 LDAPMessage
*msg
= NULL
;
2920 const char *attrs
[] = { "dn", NULL
};
2923 status
= ads_search(ads
, &res
,
2924 "(|(objectClass=domain)(objectclass=organizationalUnit))",
2926 if (!ADS_ERR_OK(status
)) {
2930 count
= ads_count_replies(ads
, res
);
2932 ads_msgfree(ads
, res
);
2933 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED
);
2936 for (msg
= ads_first_entry(ads
, res
); msg
;
2937 msg
= ads_next_entry(ads
, msg
)) {
2941 dn
= ads_get_dn(ads
, msg
);
2943 ads_msgfree(ads
, res
);
2944 return ADS_ERROR(LDAP_NO_MEMORY
);
2947 if (!add_string_to_array(mem_ctx
, dn
,
2948 (const char ***)ous
,
2950 ads_memfree(ads
, dn
);
2951 ads_msgfree(ads
, res
);
2952 return ADS_ERROR(LDAP_NO_MEMORY
);
2955 ads_memfree(ads
, dn
);
2958 ads_msgfree(ads
, res
);
2965 * pull a DOM_SID from an extended dn string
2966 * @param mem_ctx TALLOC_CTX
2967 * @param extended_dn string
2968 * @param flags string type of extended_dn
2969 * @param sid pointer to a DOM_SID
2970 * @return NT_STATUS_OK on success,
2971 * NT_INVALID_PARAMETER on error,
2972 * NT_STATUS_NOT_FOUND if no SID present
2974 ADS_STATUS
ads_get_sid_from_extended_dn(TALLOC_CTX
*mem_ctx
,
2975 const char *extended_dn
,
2976 enum ads_extended_dn_flags flags
,
2982 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER
);
2985 /* otherwise extended_dn gets stripped off */
2986 if ((dn
= talloc_strdup(mem_ctx
, extended_dn
)) == NULL
) {
2987 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER
);
2990 * ADS_EXTENDED_DN_HEX_STRING:
2991 * <GUID=238e1963cb390f4bb032ba0105525a29>;<SID=010500000000000515000000bb68c8fd6b61b427572eb04556040000>;CN=gd,OU=berlin,OU=suse,DC=ber,DC=suse,DC=de
2993 * ADS_EXTENDED_DN_STRING (only with w2k3):
2994 * <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
2996 * Object with no SID, such as an Exchange Public Folder
2997 * <GUID=28907fb4bdf6854993e7f0a10b504e7c>;CN=public,CN=Microsoft Exchange System Objects,DC=sd2k3ms,DC=west,DC=isilon,DC=com
3000 p
= strchr(dn
, ';');
3002 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER
);
3005 if (strncmp(p
, ";<SID=", strlen(";<SID=")) != 0) {
3006 DEBUG(5,("No SID present in extended dn\n"));
3007 return ADS_ERROR_NT(NT_STATUS_NOT_FOUND
);
3010 p
+= strlen(";<SID=");
3014 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER
);
3019 DEBUG(100,("ads_get_sid_from_extended_dn: sid string is %s\n", p
));
3023 case ADS_EXTENDED_DN_STRING
:
3024 if (!string_to_sid(sid
, p
)) {
3025 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER
);
3028 case ADS_EXTENDED_DN_HEX_STRING
: {
3032 buf_len
= strhex_to_str(buf
, sizeof(buf
), p
, strlen(p
));
3034 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER
);
3037 if (!sid_parse(buf
, buf_len
, sid
)) {
3038 DEBUG(10,("failed to parse sid\n"));
3039 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER
);
3044 DEBUG(10,("unknown extended dn format\n"));
3045 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER
);
3048 return ADS_ERROR_NT(NT_STATUS_OK
);
3052 * pull an array of DOM_SIDs from a ADS result
3053 * @param ads connection to ads server
3054 * @param mem_ctx TALLOC_CTX for allocating sid array
3055 * @param msg Results of search
3056 * @param field Attribute to retrieve
3057 * @param flags string type of extended_dn
3058 * @param sids pointer to sid array to allocate
3059 * @return the count of SIDs pulled
3061 int ads_pull_sids_from_extendeddn(ADS_STRUCT
*ads
,
3062 TALLOC_CTX
*mem_ctx
,
3065 enum ads_extended_dn_flags flags
,
3070 size_t dn_count
, ret_count
= 0;
3073 if ((dn_strings
= ads_pull_strings(ads
, mem_ctx
, msg
, field
,
3074 &dn_count
)) == NULL
) {
3078 (*sids
) = TALLOC_ZERO_ARRAY(mem_ctx
, DOM_SID
, dn_count
+ 1);
3080 TALLOC_FREE(dn_strings
);
3084 for (i
=0; i
<dn_count
; i
++) {
3085 rc
= ads_get_sid_from_extended_dn(mem_ctx
, dn_strings
[i
],
3086 flags
, &(*sids
)[i
]);
3087 if (!ADS_ERR_OK(rc
)) {
3088 if (NT_STATUS_EQUAL(ads_ntstatus(rc
),
3089 NT_STATUS_NOT_FOUND
)) {
3094 TALLOC_FREE(dn_strings
);
3101 TALLOC_FREE(dn_strings
);
3106 /********************************************************************
3107 ********************************************************************/
3109 char* ads_get_dnshostname( ADS_STRUCT
*ads
, TALLOC_CTX
*ctx
, const char *machine_name
)
3111 LDAPMessage
*res
= NULL
;
3116 status
= ads_find_machine_acct(ads
, &res
, global_myname());
3117 if (!ADS_ERR_OK(status
)) {
3118 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
3123 if ( (count
= ads_count_replies(ads
, res
)) != 1 ) {
3124 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count
));
3128 if ( (name
= ads_pull_string(ads
, ctx
, res
, "dNSHostName")) == NULL
) {
3129 DEBUG(0,("ads_get_dnshostname: No dNSHostName attribute!\n"));
3133 ads_msgfree(ads
, res
);
3138 /********************************************************************
3139 ********************************************************************/
3141 char* ads_get_upn( ADS_STRUCT
*ads
, TALLOC_CTX
*ctx
, const char *machine_name
)
3143 LDAPMessage
*res
= NULL
;
3148 status
= ads_find_machine_acct(ads
, &res
, machine_name
);
3149 if (!ADS_ERR_OK(status
)) {
3150 DEBUG(0,("ads_get_upn: Failed to find account for %s\n",
3155 if ( (count
= ads_count_replies(ads
, res
)) != 1 ) {
3156 DEBUG(1,("ads_get_upn: %d entries returned!\n", count
));
3160 if ( (name
= ads_pull_string(ads
, ctx
, res
, "userPrincipalName")) == NULL
) {
3161 DEBUG(2,("ads_get_upn: No userPrincipalName attribute!\n"));
3165 ads_msgfree(ads
, res
);
3170 /********************************************************************
3171 ********************************************************************/
3173 char* ads_get_samaccountname( ADS_STRUCT
*ads
, TALLOC_CTX
*ctx
, const char *machine_name
)
3175 LDAPMessage
*res
= NULL
;
3180 status
= ads_find_machine_acct(ads
, &res
, global_myname());
3181 if (!ADS_ERR_OK(status
)) {
3182 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
3187 if ( (count
= ads_count_replies(ads
, res
)) != 1 ) {
3188 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count
));
3192 if ( (name
= ads_pull_string(ads
, ctx
, res
, "sAMAccountName")) == NULL
) {
3193 DEBUG(0,("ads_get_dnshostname: No sAMAccountName attribute!\n"));
3197 ads_msgfree(ads
, res
);
3204 SAVED CODE
- we used to join via ldap
- remember how we did
this. JRA
.
3207 * Join a machine to a realm
3208 * Creates the machine account and sets the machine password
3209 * @param ads connection to ads server
3210 * @param machine name of host to add
3211 * @param org_unit Organizational unit to place machine in
3212 * @return status of join
3214 ADS_STATUS
ads_join_realm(ADS_STRUCT
*ads
, const char *machine_name
,
3215 uint32 account_type
, const char *org_unit
)
3218 LDAPMessage
*res
= NULL
;
3221 /* machine name must be lowercase */
3222 machine
= SMB_STRDUP(machine_name
);
3223 strlower_m(machine
);
3226 status = ads_find_machine_acct(ads, (void **)&res, machine);
3227 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
3228 DEBUG(0, ("Host account for %s already exists - deleting old account\n", machine));
3229 status = ads_leave_realm(ads, machine);
3230 if (!ADS_ERR_OK(status)) {
3231 DEBUG(0, ("Failed to delete host '%s' from the '%s' realm.\n",
3232 machine, ads->config.realm));
3237 status
= ads_add_machine_acct(ads
, machine
, account_type
, org_unit
);
3238 if (!ADS_ERR_OK(status
)) {
3239 DEBUG(0, ("ads_join_realm: ads_add_machine_acct failed (%s): %s\n", machine
, ads_errstr(status
)));
3244 status
= ads_find_machine_acct(ads
, (void **)(void *)&res
, machine
);
3245 if (!ADS_ERR_OK(status
)) {
3246 DEBUG(0, ("ads_join_realm: Host account test failed for machine %s\n", machine
));
3252 ads_msgfree(ads
, res
);
3259 * Delete a machine from the realm
3260 * @param ads connection to ads server
3261 * @param hostname Machine to remove
3262 * @return status of delete
3264 ADS_STATUS
ads_leave_realm(ADS_STRUCT
*ads
, const char *hostname
)
3269 char *hostnameDN
, *host
;
3271 LDAPControl ldap_control
;
3272 LDAPControl
* pldap_control
[2] = {NULL
, NULL
};
3274 pldap_control
[0] = &ldap_control
;
3275 memset(&ldap_control
, 0, sizeof(LDAPControl
));
3276 ldap_control
.ldctl_oid
= (char *)LDAP_SERVER_TREE_DELETE_OID
;
3278 /* hostname must be lowercase */
3279 host
= SMB_STRDUP(hostname
);
3282 status
= ads_find_machine_acct(ads
, &res
, host
);
3283 if (!ADS_ERR_OK(status
)) {
3284 DEBUG(0, ("Host account for %s does not exist.\n", host
));
3289 msg
= ads_first_entry(ads
, res
);
3292 return ADS_ERROR_SYSTEM(ENOENT
);
3295 hostnameDN
= ads_get_dn(ads
, (LDAPMessage
*)msg
);
3297 rc
= ldap_delete_ext_s(ads
->ldap
.ld
, hostnameDN
, pldap_control
, NULL
);
3299 DEBUG(3,("ldap_delete_ext_s failed with error code %d\n", rc
));
3301 DEBUG(3,("ldap_delete_ext_s succeeded with error code %d\n", rc
));
3304 if (rc
!= LDAP_SUCCESS
) {
3305 const char *attrs
[] = { "cn", NULL
};
3306 LDAPMessage
*msg_sub
;
3308 /* we only search with scope ONE, we do not expect any further
3309 * objects to be created deeper */
3311 status
= ads_do_search_retry(ads
, hostnameDN
,
3312 LDAP_SCOPE_ONELEVEL
,
3313 "(objectclass=*)", attrs
, &res
);
3315 if (!ADS_ERR_OK(status
)) {
3317 ads_memfree(ads
, hostnameDN
);
3321 for (msg_sub
= ads_first_entry(ads
, res
); msg_sub
;
3322 msg_sub
= ads_next_entry(ads
, msg_sub
)) {
3326 if ((dn
= ads_get_dn(ads
, msg_sub
)) == NULL
) {
3328 ads_memfree(ads
, hostnameDN
);
3329 return ADS_ERROR(LDAP_NO_MEMORY
);
3332 status
= ads_del_dn(ads
, dn
);
3333 if (!ADS_ERR_OK(status
)) {
3334 DEBUG(3,("failed to delete dn %s: %s\n", dn
, ads_errstr(status
)));
3336 ads_memfree(ads
, dn
);
3337 ads_memfree(ads
, hostnameDN
);
3341 ads_memfree(ads
, dn
);
3344 /* there should be no subordinate objects anymore */
3345 status
= ads_do_search_retry(ads
, hostnameDN
,
3346 LDAP_SCOPE_ONELEVEL
,
3347 "(objectclass=*)", attrs
, &res
);
3349 if (!ADS_ERR_OK(status
) || ( (ads_count_replies(ads
, res
)) > 0 ) ) {
3351 ads_memfree(ads
, hostnameDN
);
3355 /* delete hostnameDN now */
3356 status
= ads_del_dn(ads
, hostnameDN
);
3357 if (!ADS_ERR_OK(status
)) {
3359 DEBUG(3,("failed to delete dn %s: %s\n", hostnameDN
, ads_errstr(status
)));
3360 ads_memfree(ads
, hostnameDN
);
3365 ads_memfree(ads
, hostnameDN
);
3367 status
= ads_find_machine_acct(ads
, &res
, host
);
3368 if (ADS_ERR_OK(status
) && ads_count_replies(ads
, res
) == 1) {
3369 DEBUG(3, ("Failed to remove host account.\n"));
3379 * pull all token-sids from an LDAP dn
3380 * @param ads connection to ads server
3381 * @param mem_ctx TALLOC_CTX for allocating sid array
3382 * @param dn of LDAP object
3383 * @param user_sid pointer to DOM_SID (objectSid)
3384 * @param primary_group_sid pointer to DOM_SID (self composed)
3385 * @param sids pointer to sid array to allocate
3386 * @param num_sids counter of SIDs pulled
3387 * @return status of token query
3389 ADS_STATUS
ads_get_tokensids(ADS_STRUCT
*ads
,
3390 TALLOC_CTX
*mem_ctx
,
3393 DOM_SID
*primary_group_sid
,
3398 LDAPMessage
*res
= NULL
;
3400 size_t tmp_num_sids
;
3402 DOM_SID tmp_user_sid
;
3403 DOM_SID tmp_primary_group_sid
;
3405 const char *attrs
[] = {
3412 status
= ads_search_retry_dn(ads
, &res
, dn
, attrs
);
3413 if (!ADS_ERR_OK(status
)) {
3417 count
= ads_count_replies(ads
, res
);
3419 ads_msgfree(ads
, res
);
3420 return ADS_ERROR_LDAP(LDAP_NO_SUCH_OBJECT
);
3423 if (!ads_pull_sid(ads
, res
, "objectSid", &tmp_user_sid
)) {
3424 ads_msgfree(ads
, res
);
3425 return ADS_ERROR_LDAP(LDAP_NO_MEMORY
);
3428 if (!ads_pull_uint32(ads
, res
, "primaryGroupID", &pgid
)) {
3429 ads_msgfree(ads
, res
);
3430 return ADS_ERROR_LDAP(LDAP_NO_MEMORY
);
3434 /* hack to compose the primary group sid without knowing the
3440 sid_copy(&domsid
, &tmp_user_sid
);
3442 if (!sid_split_rid(&domsid
, &dummy_rid
)) {
3443 ads_msgfree(ads
, res
);
3444 return ADS_ERROR_LDAP(LDAP_NO_MEMORY
);
3447 if (!sid_compose(&tmp_primary_group_sid
, &domsid
, pgid
)) {
3448 ads_msgfree(ads
, res
);
3449 return ADS_ERROR_LDAP(LDAP_NO_MEMORY
);
3453 tmp_num_sids
= ads_pull_sids(ads
, mem_ctx
, res
, "tokenGroups", &tmp_sids
);
3455 if (tmp_num_sids
== 0 || !tmp_sids
) {
3456 ads_msgfree(ads
, res
);
3457 return ADS_ERROR_LDAP(LDAP_NO_MEMORY
);
3461 *num_sids
= tmp_num_sids
;
3469 *user_sid
= tmp_user_sid
;
3472 if (primary_group_sid
) {
3473 *primary_group_sid
= tmp_primary_group_sid
;
3476 DEBUG(10,("ads_get_tokensids: returned %d sids\n", (int)tmp_num_sids
+ 2));
3478 ads_msgfree(ads
, res
);
3479 return ADS_ERROR_LDAP(LDAP_SUCCESS
);
3483 * Find a sAMAccoutName in LDAP
3484 * @param ads connection to ads server
3485 * @param mem_ctx TALLOC_CTX for allocating sid array
3486 * @param samaccountname to search
3487 * @param uac_ret uint32 pointer userAccountControl attribute value
3488 * @param dn_ret pointer to dn
3489 * @return status of token query
3491 ADS_STATUS
ads_find_samaccount(ADS_STRUCT
*ads
,
3492 TALLOC_CTX
*mem_ctx
,
3493 const char *samaccountname
,
3495 const char **dn_ret
)
3498 const char *attrs
[] = { "userAccountControl", NULL
};
3500 LDAPMessage
*res
= NULL
;
3504 filter
= talloc_asprintf(mem_ctx
, "(&(objectclass=user)(sAMAccountName=%s))",
3506 if (filter
== NULL
) {
3507 status
= ADS_ERROR_NT(NT_STATUS_NO_MEMORY
);
3511 status
= ads_do_search_all(ads
, ads
->config
.bind_path
,
3513 filter
, attrs
, &res
);
3515 if (!ADS_ERR_OK(status
)) {
3519 if (ads_count_replies(ads
, res
) != 1) {
3520 status
= ADS_ERROR(LDAP_NO_RESULTS_RETURNED
);
3524 dn
= ads_get_dn(ads
, res
);
3526 status
= ADS_ERROR(LDAP_NO_MEMORY
);
3530 if (!ads_pull_uint32(ads
, res
, "userAccountControl", &uac
)) {
3531 status
= ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE
);
3540 *dn_ret
= talloc_strdup(mem_ctx
, dn
);
3542 status
= ADS_ERROR(LDAP_NO_MEMORY
);
3547 ads_memfree(ads
, dn
);
3548 ads_msgfree(ads
, res
);
3554 * find our configuration path
3555 * @param ads connection to ads server
3556 * @param mem_ctx Pointer to talloc context
3557 * @param config_path Pointer to the config path
3558 * @return status of search
3560 ADS_STATUS
ads_config_path(ADS_STRUCT
*ads
,
3561 TALLOC_CTX
*mem_ctx
,
3565 LDAPMessage
*res
= NULL
;
3566 const char *config_context
= NULL
;
3567 const char *attrs
[] = { "configurationNamingContext", NULL
};
3569 status
= ads_do_search(ads
, "", LDAP_SCOPE_BASE
,
3570 "(objectclass=*)", attrs
, &res
);
3571 if (!ADS_ERR_OK(status
)) {
3575 config_context
= ads_pull_string(ads
, mem_ctx
, res
,
3576 "configurationNamingContext");
3577 ads_msgfree(ads
, res
);
3578 if (!config_context
) {
3579 return ADS_ERROR(LDAP_NO_MEMORY
);
3583 *config_path
= talloc_strdup(mem_ctx
, config_context
);
3584 if (!*config_path
) {
3585 return ADS_ERROR(LDAP_NO_MEMORY
);
3589 return ADS_ERROR(LDAP_SUCCESS
);
3593 * find the displayName of an extended right
3594 * @param ads connection to ads server
3595 * @param config_path The config path
3596 * @param mem_ctx Pointer to talloc context
3597 * @param GUID struct of the rightsGUID
3598 * @return status of search
3600 const char *ads_get_extended_right_name_by_guid(ADS_STRUCT
*ads
,
3601 const char *config_path
,
3602 TALLOC_CTX
*mem_ctx
,
3603 const struct GUID
*rights_guid
)
3606 LDAPMessage
*res
= NULL
;
3608 const char *attrs
[] = { "displayName", NULL
};
3609 const char *result
= NULL
;
3612 if (!ads
|| !mem_ctx
|| !rights_guid
) {
3616 expr
= talloc_asprintf(mem_ctx
, "(rightsGuid=%s)",
3617 smb_uuid_string(mem_ctx
, *rights_guid
));
3622 path
= talloc_asprintf(mem_ctx
, "cn=Extended-Rights,%s", config_path
);
3627 rc
= ads_do_search_retry(ads
, path
, LDAP_SCOPE_SUBTREE
,
3629 if (!ADS_ERR_OK(rc
)) {
3633 if (ads_count_replies(ads
, res
) != 1) {
3637 result
= ads_pull_string(ads
, mem_ctx
, res
, "displayName");
3640 ads_msgfree(ads
, res
);
3646 * verify or build and verify an account ou
3647 * @param mem_ctx Pointer to talloc context
3648 * @param ads connection to ads server
3650 * @return status of search
3653 ADS_STATUS
ads_check_ou_dn(TALLOC_CTX
*mem_ctx
,
3655 const char **account_ou
)
3657 struct ldb_dn
*name_dn
= NULL
;
3658 const char *name
= NULL
;
3659 char *ou_string
= NULL
;
3661 name_dn
= ldb_dn_explode(mem_ctx
, *account_ou
);
3666 ou_string
= ads_ou_string(ads
, *account_ou
);
3668 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX
);
3671 name
= talloc_asprintf(mem_ctx
, "%s,%s", ou_string
,
3672 ads
->config
.bind_path
);
3673 SAFE_FREE(ou_string
);
3675 return ADS_ERROR_LDAP(LDAP_NO_MEMORY
);
3678 name_dn
= ldb_dn_explode(mem_ctx
, name
);
3680 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX
);
3683 *account_ou
= talloc_strdup(mem_ctx
, name
);
3685 return ADS_ERROR_LDAP(LDAP_NO_MEMORY
);