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 2 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, write to the Free Software
22 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
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
)
62 CatchSignal(SIGALRM
, SIGNAL_CAST gotalarm_sig
);
64 /* End setup timeout. */
66 ldp
= ldap_open(server
, port
);
69 DEBUG(2,("Could not open LDAP connection to %s:%d: %s\n",
70 server
, port
, strerror(errno
)));
73 /* Teardown timeout. */
74 CatchSignal(SIGALRM
, SIGNAL_CAST SIG_IGN
);
80 static int ldap_search_with_timeout(LDAP
*ld
,
81 LDAP_CONST
char *base
,
83 LDAP_CONST
char *filter
,
91 struct timeval timeout
;
94 /* Setup timeout for the ldap_search_ext_s call - local and remote. */
95 timeout
.tv_sec
= lp_ldap_timeout();
98 /* Setup alarm timeout.... Do we need both of these ? JRA. */
100 CatchSignal(SIGALRM
, SIGNAL_CAST gotalarm_sig
);
101 alarm(lp_ldap_timeout());
102 /* End setup timeout. */
104 result
= ldap_search_ext_s(ld
, base
, scope
, filter
, attrs
,
105 attrsonly
, sctrls
, cctrls
, &timeout
,
108 /* Teardown timeout. */
109 CatchSignal(SIGALRM
, SIGNAL_CAST SIG_IGN
);
113 return LDAP_TIMELIMIT_EXCEEDED
;
117 * A bug in OpenLDAP means ldap_search_ext_s can return
118 * LDAP_SUCCESS but with a NULL res pointer. Cope with
119 * this. See bug #6279 for details. JRA.
123 return LDAP_TIMELIMIT_EXCEEDED
;
129 /**********************************************
130 Do client and server sitename match ?
131 **********************************************/
133 BOOL
ads_sitename_match(ADS_STRUCT
*ads
)
135 if (ads
->config
.server_site_name
== NULL
&&
136 ads
->config
.client_site_name
== NULL
) {
137 DEBUG(10,("ads_sitename_match: both null\n"));
140 if (ads
->config
.server_site_name
&&
141 ads
->config
.client_site_name
&&
142 strequal(ads
->config
.server_site_name
,
143 ads
->config
.client_site_name
)) {
144 DEBUG(10,("ads_sitename_match: name %s match\n", ads
->config
.server_site_name
));
147 DEBUG(10,("ads_sitename_match: no match between server: %s and client: %s\n",
148 ads
->config
.server_site_name
? ads
->config
.server_site_name
: "NULL",
149 ads
->config
.client_site_name
? ads
->config
.client_site_name
: "NULL"));
153 /**********************************************
154 Is this the closest DC ?
155 **********************************************/
157 BOOL
ads_closest_dc(ADS_STRUCT
*ads
)
159 if (ads
->config
.flags
& ADS_CLOSEST
) {
160 DEBUG(10,("ads_closest_dc: ADS_CLOSEST flag set\n"));
164 /* not sure if this can ever happen */
165 if (ads_sitename_match(ads
)) {
166 DEBUG(10,("ads_closest_dc: ADS_CLOSEST flag not set but sites match\n"));
170 if (ads
->config
.client_site_name
== NULL
) {
171 DEBUG(10,("ads_closest_dc: client belongs to no site\n"));
175 DEBUG(10,("ads_closest_dc: %s is not the closest DC\n",
176 ads
->config
.ldap_server_name
));
183 try a connection to a given ldap server, returning True and setting the servers IP
184 in the ads struct if successful
186 BOOL
ads_try_connect(ADS_STRUCT
*ads
, const char *server
)
189 struct cldap_netlogon_reply cldap_reply
;
191 if (!server
|| !*server
) {
195 DEBUG(5,("ads_try_connect: sending CLDAP request to %s (realm: %s)\n",
196 server
, ads
->server
.realm
));
198 /* this copes with inet_ntoa brokenness */
200 srv
= SMB_STRDUP(server
);
202 ZERO_STRUCT( cldap_reply
);
204 if ( !ads_cldap_netlogon( srv
, ads
->server
.realm
, &cldap_reply
) ) {
205 DEBUG(3,("ads_try_connect: CLDAP request %s failed.\n", srv
));
210 /* Check the CLDAP reply flags */
212 if ( !(cldap_reply
.flags
& ADS_LDAP
) ) {
213 DEBUG(1,("ads_try_connect: %s's CLDAP reply says it is not an LDAP server!\n",
219 /* Fill in the ads->config values */
221 SAFE_FREE(ads
->config
.realm
);
222 SAFE_FREE(ads
->config
.bind_path
);
223 SAFE_FREE(ads
->config
.ldap_server_name
);
224 SAFE_FREE(ads
->config
.server_site_name
);
225 SAFE_FREE(ads
->config
.client_site_name
);
226 SAFE_FREE(ads
->server
.workgroup
);
228 ads
->config
.flags
= cldap_reply
.flags
;
229 ads
->config
.ldap_server_name
= SMB_STRDUP(cldap_reply
.hostname
);
230 strupper_m(cldap_reply
.domain
);
231 ads
->config
.realm
= SMB_STRDUP(cldap_reply
.domain
);
232 ads
->config
.bind_path
= ads_build_dn(ads
->config
.realm
);
233 if (*cldap_reply
.server_site_name
) {
234 ads
->config
.server_site_name
=
235 SMB_STRDUP(cldap_reply
.server_site_name
);
237 if (*cldap_reply
.client_site_name
) {
238 ads
->config
.client_site_name
=
239 SMB_STRDUP(cldap_reply
.client_site_name
);
242 ads
->server
.workgroup
= SMB_STRDUP(cldap_reply
.netbios_domain
);
244 ads
->ldap_port
= LDAP_PORT
;
245 ads
->ldap_ip
= *interpret_addr2(srv
);
248 /* Store our site name. */
249 sitename_store( cldap_reply
.domain
, cldap_reply
.client_site_name
);
254 /**********************************************************************
255 Try to find an AD dc using our internal name resolution routines
256 Try the realm first and then then workgroup name if netbios is not
258 **********************************************************************/
260 static NTSTATUS
ads_find_dc(ADS_STRUCT
*ads
)
262 const char *c_domain
;
265 struct ip_service
*ip_list
;
268 BOOL got_realm
= False
;
269 BOOL use_own_domain
= False
;
271 NTSTATUS status
= NT_STATUS_UNSUCCESSFUL
;
273 /* if the realm and workgroup are both empty, assume they are ours */
276 c_realm
= ads
->server
.realm
;
278 if ( !c_realm
|| !*c_realm
) {
279 /* special case where no realm and no workgroup means our own */
280 if ( !ads
->server
.workgroup
|| !*ads
->server
.workgroup
) {
281 use_own_domain
= True
;
282 c_realm
= lp_realm();
286 if (c_realm
&& *c_realm
)
291 /* we need to try once with the realm name and fallback to the
292 netbios domain name if we fail (if netbios has not been disabled */
294 if ( !got_realm
&& !lp_disable_netbios() ) {
295 c_realm
= ads
->server
.workgroup
;
296 if (!c_realm
|| !*c_realm
) {
297 if ( use_own_domain
)
298 c_realm
= lp_workgroup();
302 if ( !c_realm
|| !*c_realm
) {
303 DEBUG(0,("ads_find_dc: no realm or workgroup! Don't know what to do\n"));
304 return NT_STATUS_INVALID_PARAMETER
; /* rather need MISSING_PARAMETER ... */
307 if ( use_own_domain
) {
308 c_domain
= lp_workgroup();
310 c_domain
= ads
->server
.workgroup
;
313 pstrcpy( domain
, c_domain
);
314 pstrcpy( realm
, c_realm
);
317 * In case of LDAP we use get_dc_name() as that
318 * creates the custom krb5.conf file
320 if (!(ads
->auth
.flags
& ADS_AUTH_NO_BIND
)) {
322 struct in_addr ip_out
;
324 DEBUG(6,("ads_find_dc: (ldap) looking for %s '%s'\n",
325 (got_realm
? "realm" : "domain"), realm
));
327 if (get_dc_name(domain
, realm
, srv_name
, &ip_out
)) {
329 * we call ads_try_connect() to fill in the
330 * ads->config details
332 if (ads_try_connect(ads
, srv_name
)) {
337 return NT_STATUS_NO_LOGON_SERVERS
;
340 sitename
= sitename_fetch(realm
);
342 DEBUG(6,("ads_find_dc: (cldap) looking for %s '%s'\n",
343 (got_realm
? "realm" : "domain"), realm
));
345 status
= get_sorted_dc_list(realm
, sitename
, &ip_list
, &count
, got_realm
);
346 if (!NT_STATUS_IS_OK(status
)) {
347 /* fall back to netbios if we can */
348 if ( got_realm
&& !lp_disable_netbios() ) {
357 /* if we fail this loop, then giveup since all the IP addresses returned were dead */
358 for ( i
=0; i
<count
; i
++ ) {
361 fstrcpy( server
, inet_ntoa(ip_list
[i
].ip
) );
363 if ( !NT_STATUS_IS_OK(check_negative_conn_cache(realm
, server
)) )
367 /* realm in this case is a workgroup name. We need
368 to ignore any IP addresses in the negative connection
369 cache that match ip addresses returned in the ad realm
370 case. It sucks that I have to reproduce the logic above... */
371 c_realm
= ads
->server
.realm
;
372 if ( !c_realm
|| !*c_realm
) {
373 if ( !ads
->server
.workgroup
|| !*ads
->server
.workgroup
) {
374 c_realm
= lp_realm();
377 if (c_realm
&& *c_realm
&&
378 !NT_STATUS_IS_OK(check_negative_conn_cache(c_realm
, server
))) {
379 /* Ensure we add the workgroup name for this
380 IP address as negative too. */
381 add_failed_connection_entry( realm
, server
, NT_STATUS_UNSUCCESSFUL
);
386 if ( ads_try_connect(ads
, server
) ) {
392 /* keep track of failures */
393 add_failed_connection_entry( realm
, server
, NT_STATUS_UNSUCCESSFUL
);
398 /* In case we failed to contact one of our closest DC on our site we
399 * need to try to find another DC, retry with a site-less SRV DNS query
403 DEBUG(1,("ads_find_dc: failed to find a valid DC on our site (%s), "
404 "trying to find another DC\n", sitename
));
406 namecache_delete(realm
, 0x1C);
410 return NT_STATUS_NO_LOGON_SERVERS
;
415 * Connect to the LDAP server
416 * @param ads Pointer to an existing ADS_STRUCT
417 * @return status of connection
419 ADS_STATUS
ads_connect(ADS_STRUCT
*ads
)
421 int version
= LDAP_VERSION3
;
425 ads
->last_attempt
= time(NULL
);
428 /* try with a user specified server */
430 if (ads
->server
.ldap_server
&&
431 ads_try_connect(ads
, ads
->server
.ldap_server
)) {
435 ntstatus
= ads_find_dc(ads
);
436 if (NT_STATUS_IS_OK(ntstatus
)) {
440 return ADS_ERROR_NT(ntstatus
);
443 DEBUG(3,("Connected to LDAP server %s\n", inet_ntoa(ads
->ldap_ip
)));
445 if (!ads
->auth
.user_name
) {
446 /* Must use the userPrincipalName value here or sAMAccountName
447 and not servicePrincipalName; found by Guenther Deschner */
449 asprintf(&ads
->auth
.user_name
, "%s$", global_myname() );
452 if (!ads
->auth
.realm
) {
453 ads
->auth
.realm
= SMB_STRDUP(ads
->config
.realm
);
456 if (!ads
->auth
.kdc_server
) {
457 ads
->auth
.kdc_server
= SMB_STRDUP(inet_ntoa(ads
->ldap_ip
));
461 /* this is a really nasty hack to avoid ADS DNS problems. It needs a patch
462 to MIT kerberos to work (tridge) */
465 asprintf(&env
, "KRB5_KDC_ADDRESS_%s", ads
->config
.realm
);
466 setenv(env
, ads
->auth
.kdc_server
, 1);
471 /* If the caller() requested no LDAP bind, then we are done */
473 if (ads
->auth
.flags
& ADS_AUTH_NO_BIND
) {
477 /* Otherwise setup the TCP LDAP session */
479 if ( (ads
->ld
= ldap_open_with_timeout(ads
->config
.ldap_server_name
,
480 LDAP_PORT
, lp_ldap_timeout())) == NULL
)
482 return ADS_ERROR(LDAP_OPERATIONS_ERROR
);
485 /* cache the successful connection for workgroup and realm */
486 if (ads_closest_dc(ads
)) {
487 saf_store( ads
->server
.workgroup
, ads
->config
.ldap_server_name
);
488 saf_store( ads
->server
.realm
, ads
->config
.ldap_server_name
);
491 ldap_set_option(ads
->ld
, LDAP_OPT_PROTOCOL_VERSION
, &version
);
493 status
= ADS_ERROR(smb_ldap_start_tls(ads
->ld
, version
));
494 if (!ADS_ERR_OK(status
)) {
498 /* fill in the current time and offsets */
500 status
= ads_current_time( ads
);
501 if ( !ADS_ERR_OK(status
) ) {
505 /* Now do the bind */
507 if (ads
->auth
.flags
& ADS_AUTH_ANON_BIND
) {
508 return ADS_ERROR(ldap_simple_bind_s( ads
->ld
, NULL
, NULL
));
511 if (ads
->auth
.flags
& ADS_AUTH_SIMPLE_BIND
) {
512 return ADS_ERROR(ldap_simple_bind_s( ads
->ld
, ads
->auth
.user_name
, ads
->auth
.password
));
515 return ads_sasl_bind(ads
);
519 Duplicate a struct berval into talloc'ed memory
521 static struct berval
*dup_berval(TALLOC_CTX
*ctx
, const struct berval
*in_val
)
523 struct berval
*value
;
525 if (!in_val
) return NULL
;
527 value
= TALLOC_ZERO_P(ctx
, struct berval
);
530 if (in_val
->bv_len
== 0) return value
;
532 value
->bv_len
= in_val
->bv_len
;
533 value
->bv_val
= (char *)TALLOC_MEMDUP(ctx
, in_val
->bv_val
,
539 Make a values list out of an array of (struct berval *)
541 static struct berval
**ads_dup_values(TALLOC_CTX
*ctx
,
542 const struct berval
**in_vals
)
544 struct berval
**values
;
547 if (!in_vals
) return NULL
;
548 for (i
=0; in_vals
[i
]; i
++)
550 values
= TALLOC_ZERO_ARRAY(ctx
, struct berval
*, i
+1);
551 if (!values
) return NULL
;
553 for (i
=0; in_vals
[i
]; i
++) {
554 values
[i
] = dup_berval(ctx
, in_vals
[i
]);
560 UTF8-encode a values list out of an array of (char *)
562 static char **ads_push_strvals(TALLOC_CTX
*ctx
, const char **in_vals
)
567 if (!in_vals
) return NULL
;
568 for (i
=0; in_vals
[i
]; i
++)
570 values
= TALLOC_ZERO_ARRAY(ctx
, char *, i
+1);
571 if (!values
) return NULL
;
573 for (i
=0; in_vals
[i
]; i
++) {
574 push_utf8_talloc(ctx
, &values
[i
], in_vals
[i
]);
580 Pull a (char *) array out of a UTF8-encoded values list
582 static char **ads_pull_strvals(TALLOC_CTX
*ctx
, const char **in_vals
)
587 if (!in_vals
) return NULL
;
588 for (i
=0; in_vals
[i
]; i
++)
590 values
= TALLOC_ZERO_ARRAY(ctx
, char *, i
+1);
591 if (!values
) return NULL
;
593 for (i
=0; in_vals
[i
]; i
++) {
594 pull_utf8_talloc(ctx
, &values
[i
], in_vals
[i
]);
600 * Do a search with paged results. cookie must be null on the first
601 * call, and then returned on each subsequent call. It will be null
602 * again when the entire search is complete
603 * @param ads connection to ads server
604 * @param bind_path Base dn for the search
605 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
606 * @param expr Search expression - specified in local charset
607 * @param attrs Attributes to retrieve - specified in utf8 or ascii
608 * @param res ** which will contain results - free res* with ads_msgfree()
609 * @param count Number of entries retrieved on this page
610 * @param cookie The paged results cookie to be returned on subsequent calls
611 * @return status of search
613 static ADS_STATUS
ads_do_paged_search_args(ADS_STRUCT
*ads
,
614 const char *bind_path
,
615 int scope
, const char *expr
,
616 const char **attrs
, void *args
,
618 int *count
, struct berval
**cookie
)
621 char *utf8_expr
, *utf8_path
, **search_attrs
;
622 LDAPControl PagedResults
, NoReferrals
, ExtendedDn
, *controls
[4], **rcontrols
;
623 BerElement
*cookie_be
= NULL
;
624 struct berval
*cookie_bv
= NULL
;
625 BerElement
*extdn_be
= NULL
;
626 struct berval
*extdn_bv
= NULL
;
629 ads_control
*external_control
= (ads_control
*) args
;
633 if (!(ctx
= talloc_init("ads_do_paged_search_args")))
634 return ADS_ERROR(LDAP_NO_MEMORY
);
636 /* 0 means the conversion worked but the result was empty
637 so we only fail if it's -1. In any case, it always
638 at least nulls out the dest */
639 if ((push_utf8_talloc(ctx
, &utf8_expr
, expr
) == (size_t)-1) ||
640 (push_utf8_talloc(ctx
, &utf8_path
, bind_path
) == (size_t)-1)) {
645 if (!attrs
|| !(*attrs
))
648 /* This would be the utf8-encoded version...*/
649 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
650 if (!(str_list_copy(&search_attrs
, attrs
))) {
657 /* Paged results only available on ldap v3 or later */
658 ldap_get_option(ads
->ld
, LDAP_OPT_PROTOCOL_VERSION
, &version
);
659 if (version
< LDAP_VERSION3
) {
660 rc
= LDAP_NOT_SUPPORTED
;
664 cookie_be
= ber_alloc_t(LBER_USE_DER
);
666 ber_printf(cookie_be
, "{iO}", (ber_int_t
) 1000, *cookie
);
667 ber_bvfree(*cookie
); /* don't need it from last time */
670 ber_printf(cookie_be
, "{io}", (ber_int_t
) 1000, "", 0);
672 ber_flatten(cookie_be
, &cookie_bv
);
673 PagedResults
.ldctl_oid
= CONST_DISCARD(char *, ADS_PAGE_CTL_OID
);
674 PagedResults
.ldctl_iscritical
= (char) 1;
675 PagedResults
.ldctl_value
.bv_len
= cookie_bv
->bv_len
;
676 PagedResults
.ldctl_value
.bv_val
= cookie_bv
->bv_val
;
678 NoReferrals
.ldctl_oid
= CONST_DISCARD(char *, ADS_NO_REFERRALS_OID
);
679 NoReferrals
.ldctl_iscritical
= (char) 0;
680 NoReferrals
.ldctl_value
.bv_len
= 0;
681 NoReferrals
.ldctl_value
.bv_val
= CONST_DISCARD(char *, "");
683 if (external_control
&& strequal(external_control
->control
, ADS_EXTENDED_DN_OID
)) {
685 ExtendedDn
.ldctl_oid
= CONST_DISCARD(char *, external_control
->control
);
686 ExtendedDn
.ldctl_iscritical
= (char) external_control
->critical
;
688 /* win2k does not accept a ldctl_value beeing passed in */
690 if (external_control
->val
!= 0) {
692 if ((extdn_be
= ber_alloc_t(LBER_USE_DER
)) == NULL
) {
697 if ((ber_printf(extdn_be
, "{i}", (ber_int_t
) external_control
->val
)) == -1) {
701 if ((ber_flatten(extdn_be
, &extdn_bv
)) == -1) {
706 ExtendedDn
.ldctl_value
.bv_len
= extdn_bv
->bv_len
;
707 ExtendedDn
.ldctl_value
.bv_val
= extdn_bv
->bv_val
;
710 ExtendedDn
.ldctl_value
.bv_len
= 0;
711 ExtendedDn
.ldctl_value
.bv_val
= NULL
;
714 controls
[0] = &NoReferrals
;
715 controls
[1] = &PagedResults
;
716 controls
[2] = &ExtendedDn
;
720 controls
[0] = &NoReferrals
;
721 controls
[1] = &PagedResults
;
725 /* we need to disable referrals as the openldap libs don't
726 handle them and paged results at the same time. Using them
727 together results in the result record containing the server
728 page control being removed from the result list (tridge/jmcd)
730 leaving this in despite the control that says don't generate
731 referrals, in case the server doesn't support it (jmcd)
733 ldap_set_option(ads
->ld
, LDAP_OPT_REFERRALS
, LDAP_OPT_OFF
);
735 rc
= ldap_search_with_timeout(ads
->ld
, utf8_path
, scope
, utf8_expr
,
736 search_attrs
, 0, controls
,
738 (LDAPMessage
**)res
);
740 ber_free(cookie_be
, 1);
741 ber_bvfree(cookie_bv
);
744 DEBUG(3,("ads_do_paged_search_args: ldap_search_with_timeout(%s) -> %s\n", expr
,
745 ldap_err2string(rc
)));
749 rc
= ldap_parse_result(ads
->ld
, *res
, NULL
, NULL
, NULL
,
750 NULL
, &rcontrols
, 0);
756 for (i
=0; rcontrols
[i
]; i
++) {
757 if (strcmp(ADS_PAGE_CTL_OID
, rcontrols
[i
]->ldctl_oid
) == 0) {
758 cookie_be
= ber_init(&rcontrols
[i
]->ldctl_value
);
759 ber_scanf(cookie_be
,"{iO}", (ber_int_t
*) count
,
761 /* the berval is the cookie, but must be freed when
763 if (cookie_bv
->bv_len
) /* still more to do */
764 *cookie
=ber_bvdup(cookie_bv
);
767 ber_bvfree(cookie_bv
);
768 ber_free(cookie_be
, 1);
772 ldap_controls_free(rcontrols
);
778 ber_free(extdn_be
, 1);
782 ber_bvfree(extdn_bv
);
785 /* if/when we decide to utf8-encode attrs, take out this next line */
786 str_list_free(&search_attrs
);
788 return ADS_ERROR(rc
);
791 static ADS_STATUS
ads_do_paged_search(ADS_STRUCT
*ads
, const char *bind_path
,
792 int scope
, const char *expr
,
793 const char **attrs
, LDAPMessage
**res
,
794 int *count
, struct berval
**cookie
)
796 return ads_do_paged_search_args(ads
, bind_path
, scope
, expr
, attrs
, NULL
, res
, count
, cookie
);
801 * Get all results for a search. This uses ads_do_paged_search() to return
802 * all entries in a large search.
803 * @param ads connection to ads server
804 * @param bind_path Base dn for the search
805 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
806 * @param expr Search expression
807 * @param attrs Attributes to retrieve
808 * @param res ** which will contain results - free res* with ads_msgfree()
809 * @return status of search
811 ADS_STATUS
ads_do_search_all_args(ADS_STRUCT
*ads
, const char *bind_path
,
812 int scope
, const char *expr
,
813 const char **attrs
, void *args
,
816 struct berval
*cookie
= NULL
;
821 status
= ads_do_paged_search_args(ads
, bind_path
, scope
, expr
, attrs
, args
, res
,
824 if (!ADS_ERR_OK(status
))
827 #ifdef HAVE_LDAP_ADD_RESULT_ENTRY
829 LDAPMessage
*res2
= NULL
;
831 LDAPMessage
*msg
, *next
;
833 status2
= ads_do_paged_search_args(ads
, bind_path
, scope
, expr
,
834 attrs
, args
, &res2
, &count
, &cookie
);
836 if (!ADS_ERR_OK(status2
)) break;
838 /* this relies on the way that ldap_add_result_entry() works internally. I hope
839 that this works on all ldap libs, but I have only tested with openldap */
840 for (msg
= ads_first_message(ads
, res2
); msg
; msg
= next
) {
841 next
= ads_next_message(ads
, msg
);
842 ldap_add_result_entry((LDAPMessage
**)res
, msg
);
844 /* note that we do not free res2, as the memory is now
845 part of the main returned list */
848 DEBUG(0, ("no ldap_add_result_entry() support in LDAP libs!\n"));
849 status
= ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL
);
855 ADS_STATUS
ads_do_search_all(ADS_STRUCT
*ads
, const char *bind_path
,
856 int scope
, const char *expr
,
857 const char **attrs
, LDAPMessage
**res
)
859 return ads_do_search_all_args(ads
, bind_path
, scope
, expr
, attrs
, NULL
, res
);
863 * Run a function on all results for a search. Uses ads_do_paged_search() and
864 * runs the function as each page is returned, using ads_process_results()
865 * @param ads connection to ads server
866 * @param bind_path Base dn for the search
867 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
868 * @param expr Search expression - specified in local charset
869 * @param attrs Attributes to retrieve - specified in UTF-8 or ascii
870 * @param fn Function which takes attr name, values list, and data_area
871 * @param data_area Pointer which is passed to function on each call
872 * @return status of search
874 ADS_STATUS
ads_do_search_all_fn(ADS_STRUCT
*ads
, const char *bind_path
,
875 int scope
, const char *expr
, const char **attrs
,
876 BOOL(*fn
)(char *, void **, void *),
879 struct berval
*cookie
= NULL
;
884 status
= ads_do_paged_search(ads
, bind_path
, scope
, expr
, attrs
, &res
,
887 if (!ADS_ERR_OK(status
)) return status
;
889 ads_process_results(ads
, res
, fn
, data_area
);
890 ads_msgfree(ads
, res
);
893 status
= ads_do_paged_search(ads
, bind_path
, scope
, expr
, attrs
,
894 &res
, &count
, &cookie
);
896 if (!ADS_ERR_OK(status
)) break;
898 ads_process_results(ads
, res
, fn
, data_area
);
899 ads_msgfree(ads
, res
);
906 * Do a search with a timeout.
907 * @param ads connection to ads server
908 * @param bind_path Base dn for the search
909 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
910 * @param expr Search expression
911 * @param attrs Attributes to retrieve
912 * @param res ** which will contain results - free res* with ads_msgfree()
913 * @return status of search
915 ADS_STATUS
ads_do_search(ADS_STRUCT
*ads
, const char *bind_path
, int scope
,
917 const char **attrs
, LDAPMessage
**res
)
920 char *utf8_expr
, *utf8_path
, **search_attrs
= NULL
;
924 if (!(ctx
= talloc_init("ads_do_search"))) {
925 DEBUG(1,("ads_do_search: talloc_init() failed!"));
926 return ADS_ERROR(LDAP_NO_MEMORY
);
929 /* 0 means the conversion worked but the result was empty
930 so we only fail if it's negative. In any case, it always
931 at least nulls out the dest */
932 if ((push_utf8_talloc(ctx
, &utf8_expr
, expr
) == (size_t)-1) ||
933 (push_utf8_talloc(ctx
, &utf8_path
, bind_path
) == (size_t)-1)) {
934 DEBUG(1,("ads_do_search: push_utf8_talloc() failed!"));
939 if (!attrs
|| !(*attrs
))
942 /* This would be the utf8-encoded version...*/
943 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
944 if (!(str_list_copy(&search_attrs
, attrs
)))
946 DEBUG(1,("ads_do_search: str_list_copy() failed!"));
952 /* see the note in ads_do_paged_search - we *must* disable referrals */
953 ldap_set_option(ads
->ld
, LDAP_OPT_REFERRALS
, LDAP_OPT_OFF
);
955 rc
= ldap_search_with_timeout(ads
->ld
, utf8_path
, scope
, utf8_expr
,
956 search_attrs
, 0, NULL
, NULL
,
958 (LDAPMessage
**)res
);
960 if (rc
== LDAP_SIZELIMIT_EXCEEDED
) {
961 DEBUG(3,("Warning! sizelimit exceeded in ldap. Truncating.\n"));
967 /* if/when we decide to utf8-encode attrs, take out this next line */
968 str_list_free(&search_attrs
);
969 return ADS_ERROR(rc
);
972 * Do a general ADS search
973 * @param ads connection to ads server
974 * @param res ** which will contain results - free res* with ads_msgfree()
975 * @param expr Search expression
976 * @param attrs Attributes to retrieve
977 * @return status of search
979 ADS_STATUS
ads_search(ADS_STRUCT
*ads
, LDAPMessage
**res
,
980 const char *expr
, const char **attrs
)
982 return ads_do_search(ads
, ads
->config
.bind_path
, LDAP_SCOPE_SUBTREE
,
987 * Do a search on a specific DistinguishedName
988 * @param ads connection to ads server
989 * @param res ** which will contain results - free res* with ads_msgfree()
990 * @param dn DistinguishName to search
991 * @param attrs Attributes to retrieve
992 * @return status of search
994 ADS_STATUS
ads_search_dn(ADS_STRUCT
*ads
, LDAPMessage
**res
,
995 const char *dn
, const char **attrs
)
997 return ads_do_search(ads
, dn
, LDAP_SCOPE_BASE
, "(objectclass=*)",
1002 * Free up memory from a ads_search
1003 * @param ads connection to ads server
1004 * @param msg Search results to free
1006 void ads_msgfree(ADS_STRUCT
*ads
, LDAPMessage
*msg
)
1013 * Free up memory from various ads requests
1014 * @param ads connection to ads server
1015 * @param mem Area to free
1017 void ads_memfree(ADS_STRUCT
*ads
, void *mem
)
1023 * Get a dn from search results
1024 * @param ads connection to ads server
1025 * @param msg Search result
1028 char *ads_get_dn(ADS_STRUCT
*ads
, LDAPMessage
*msg
)
1030 char *utf8_dn
, *unix_dn
;
1032 utf8_dn
= ldap_get_dn(ads
->ld
, msg
);
1035 DEBUG (5, ("ads_get_dn: ldap_get_dn failed\n"));
1039 if (pull_utf8_allocate(&unix_dn
, utf8_dn
) == (size_t)-1) {
1040 DEBUG(0,("ads_get_dn: string conversion failure utf8 [%s]\n",
1044 ldap_memfree(utf8_dn
);
1049 * Get the parent from a dn
1050 * @param dn the dn to return the parent from
1051 * @return parent dn string
1053 char *ads_parent_dn(const char *dn
)
1061 p
= strchr(dn
, ',');
1071 * Find a machine account given a hostname
1072 * @param ads connection to ads server
1073 * @param res ** which will contain results - free res* with ads_msgfree()
1074 * @param host Hostname to search for
1075 * @return status of search
1077 ADS_STATUS
ads_find_machine_acct(ADS_STRUCT
*ads
, LDAPMessage
**res
,
1078 const char *machine
)
1082 const char *attrs
[] = {"*", "nTSecurityDescriptor", NULL
};
1086 /* the easiest way to find a machine account anywhere in the tree
1087 is to look for hostname$ */
1088 if (asprintf(&expr
, "(samAccountName=%s$)", machine
) == -1) {
1089 DEBUG(1, ("asprintf failed!\n"));
1090 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY
);
1093 status
= ads_search(ads
, res
, expr
, attrs
);
1099 * Initialize a list of mods to be used in a modify request
1100 * @param ctx An initialized TALLOC_CTX
1101 * @return allocated ADS_MODLIST
1103 ADS_MODLIST
ads_init_mods(TALLOC_CTX
*ctx
)
1105 #define ADS_MODLIST_ALLOC_SIZE 10
1108 if ((mods
= TALLOC_ZERO_ARRAY(ctx
, LDAPMod
*, ADS_MODLIST_ALLOC_SIZE
+ 1)))
1109 /* -1 is safety to make sure we don't go over the end.
1110 need to reset it to NULL before doing ldap modify */
1111 mods
[ADS_MODLIST_ALLOC_SIZE
] = (LDAPMod
*) -1;
1113 return (ADS_MODLIST
)mods
;
1118 add an attribute to the list, with values list already constructed
1120 static ADS_STATUS
ads_modlist_add(TALLOC_CTX
*ctx
, ADS_MODLIST
*mods
,
1121 int mod_op
, const char *name
,
1122 const void *_invals
)
1124 const void **invals
= (const void **)_invals
;
1126 LDAPMod
**modlist
= (LDAPMod
**) *mods
;
1127 struct berval
**ber_values
= NULL
;
1128 char **char_values
= NULL
;
1131 mod_op
= LDAP_MOD_DELETE
;
1133 if (mod_op
& LDAP_MOD_BVALUES
)
1134 ber_values
= ads_dup_values(ctx
,
1135 (const struct berval
**)invals
);
1137 char_values
= ads_push_strvals(ctx
,
1138 (const char **) invals
);
1141 /* find the first empty slot */
1142 for (curmod
=0; modlist
[curmod
] && modlist
[curmod
] != (LDAPMod
*) -1;
1144 if (modlist
[curmod
] == (LDAPMod
*) -1) {
1145 if (!(modlist
= TALLOC_REALLOC_ARRAY(ctx
, modlist
, LDAPMod
*,
1146 curmod
+ADS_MODLIST_ALLOC_SIZE
+1)))
1147 return ADS_ERROR(LDAP_NO_MEMORY
);
1148 memset(&modlist
[curmod
], 0,
1149 ADS_MODLIST_ALLOC_SIZE
*sizeof(LDAPMod
*));
1150 modlist
[curmod
+ADS_MODLIST_ALLOC_SIZE
] = (LDAPMod
*) -1;
1151 *mods
= (ADS_MODLIST
)modlist
;
1154 if (!(modlist
[curmod
] = TALLOC_ZERO_P(ctx
, LDAPMod
)))
1155 return ADS_ERROR(LDAP_NO_MEMORY
);
1156 modlist
[curmod
]->mod_type
= talloc_strdup(ctx
, name
);
1157 if (mod_op
& LDAP_MOD_BVALUES
) {
1158 modlist
[curmod
]->mod_bvalues
= ber_values
;
1159 } else if (mod_op
& LDAP_MOD_DELETE
) {
1160 modlist
[curmod
]->mod_values
= NULL
;
1162 modlist
[curmod
]->mod_values
= char_values
;
1165 modlist
[curmod
]->mod_op
= mod_op
;
1166 return ADS_ERROR(LDAP_SUCCESS
);
1170 * Add a single string value to a mod list
1171 * @param ctx An initialized TALLOC_CTX
1172 * @param mods An initialized ADS_MODLIST
1173 * @param name The attribute name to add
1174 * @param val The value to add - NULL means DELETE
1175 * @return ADS STATUS indicating success of add
1177 ADS_STATUS
ads_mod_str(TALLOC_CTX
*ctx
, ADS_MODLIST
*mods
,
1178 const char *name
, const char *val
)
1180 const char *values
[2];
1186 return ads_modlist_add(ctx
, mods
, LDAP_MOD_DELETE
, name
, NULL
);
1187 return ads_modlist_add(ctx
, mods
, LDAP_MOD_REPLACE
, name
, values
);
1191 * Add an array of string values to a mod list
1192 * @param ctx An initialized TALLOC_CTX
1193 * @param mods An initialized ADS_MODLIST
1194 * @param name The attribute name to add
1195 * @param vals The array of string values to add - NULL means DELETE
1196 * @return ADS STATUS indicating success of add
1198 ADS_STATUS
ads_mod_strlist(TALLOC_CTX
*ctx
, ADS_MODLIST
*mods
,
1199 const char *name
, const char **vals
)
1202 return ads_modlist_add(ctx
, mods
, LDAP_MOD_DELETE
, name
, NULL
);
1203 return ads_modlist_add(ctx
, mods
, LDAP_MOD_REPLACE
,
1204 name
, (const void **) vals
);
1209 * Add a single ber-encoded value to a mod list
1210 * @param ctx An initialized TALLOC_CTX
1211 * @param mods An initialized ADS_MODLIST
1212 * @param name The attribute name to add
1213 * @param val The value to add - NULL means DELETE
1214 * @return ADS STATUS indicating success of add
1216 static ADS_STATUS
ads_mod_ber(TALLOC_CTX
*ctx
, ADS_MODLIST
*mods
,
1217 const char *name
, const struct berval
*val
)
1219 const struct berval
*values
[2];
1224 return ads_modlist_add(ctx
, mods
, LDAP_MOD_DELETE
, name
, NULL
);
1225 return ads_modlist_add(ctx
, mods
, LDAP_MOD_REPLACE
|LDAP_MOD_BVALUES
,
1226 name
, (const void **) values
);
1231 * Perform an ldap modify
1232 * @param ads connection to ads server
1233 * @param mod_dn DistinguishedName to modify
1234 * @param mods list of modifications to perform
1235 * @return status of modify
1237 ADS_STATUS
ads_gen_mod(ADS_STRUCT
*ads
, const char *mod_dn
, ADS_MODLIST mods
)
1240 char *utf8_dn
= NULL
;
1242 this control is needed to modify that contains a currently
1243 non-existent attribute (but allowable for the object) to run
1245 LDAPControl PermitModify
= {
1246 CONST_DISCARD(char *, ADS_PERMIT_MODIFY_OID
),
1249 LDAPControl
*controls
[2];
1251 controls
[0] = &PermitModify
;
1254 if (push_utf8_allocate(&utf8_dn
, mod_dn
) == -1) {
1255 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY
);
1258 /* find the end of the list, marked by NULL or -1 */
1259 for(i
=0;(mods
[i
]!=0)&&(mods
[i
]!=(LDAPMod
*) -1);i
++);
1260 /* make sure the end of the list is NULL */
1262 ret
= ldap_modify_ext_s(ads
->ld
, utf8_dn
,
1263 (LDAPMod
**) mods
, controls
, NULL
);
1265 return ADS_ERROR(ret
);
1269 * Perform an ldap add
1270 * @param ads connection to ads server
1271 * @param new_dn DistinguishedName to add
1272 * @param mods list of attributes and values for DN
1273 * @return status of add
1275 ADS_STATUS
ads_gen_add(ADS_STRUCT
*ads
, const char *new_dn
, ADS_MODLIST mods
)
1278 char *utf8_dn
= NULL
;
1280 if (push_utf8_allocate(&utf8_dn
, new_dn
) == -1) {
1281 DEBUG(1, ("ads_gen_add: push_utf8_allocate failed!"));
1282 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY
);
1285 /* find the end of the list, marked by NULL or -1 */
1286 for(i
=0;(mods
[i
]!=0)&&(mods
[i
]!=(LDAPMod
*) -1);i
++);
1287 /* make sure the end of the list is NULL */
1290 ret
= ldap_add_s(ads
->ld
, utf8_dn
, (LDAPMod
**)mods
);
1292 return ADS_ERROR(ret
);
1296 * Delete a DistinguishedName
1297 * @param ads connection to ads server
1298 * @param new_dn DistinguishedName to delete
1299 * @return status of delete
1301 ADS_STATUS
ads_del_dn(ADS_STRUCT
*ads
, char *del_dn
)
1304 char *utf8_dn
= NULL
;
1305 if (push_utf8_allocate(&utf8_dn
, del_dn
) == -1) {
1306 DEBUG(1, ("ads_del_dn: push_utf8_allocate failed!"));
1307 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY
);
1310 ret
= ldap_delete_s(ads
->ld
, utf8_dn
);
1312 return ADS_ERROR(ret
);
1316 * Build an org unit string
1317 * if org unit is Computers or blank then assume a container, otherwise
1318 * assume a / separated list of organisational units.
1319 * jmcd: '\' is now used for escapes so certain chars can be in the ou (e.g. #)
1320 * @param ads connection to ads server
1321 * @param org_unit Organizational unit
1322 * @return org unit string - caller must free
1324 char *ads_ou_string(ADS_STRUCT
*ads
, const char *org_unit
)
1328 if (!org_unit
|| !*org_unit
) {
1330 ret
= ads_default_ou_string(ads
, WELL_KNOWN_GUID_COMPUTERS
);
1332 /* samba4 might not yet respond to a wellknownobject-query */
1333 return ret
? ret
: SMB_STRDUP("cn=Computers");
1336 if (strequal(org_unit
, "Computers")) {
1337 return SMB_STRDUP("cn=Computers");
1340 /* jmcd: removed "\\" from the separation chars, because it is
1341 needed as an escape for chars like '#' which are valid in an
1343 return ads_build_path(org_unit
, "/", "ou=", 1);
1347 * Get a org unit string for a well-known GUID
1348 * @param ads connection to ads server
1349 * @param wknguid Well known GUID
1350 * @return org unit string - caller must free
1352 char *ads_default_ou_string(ADS_STRUCT
*ads
, const char *wknguid
)
1355 LDAPMessage
*res
= NULL
;
1356 char *base
, *wkn_dn
= NULL
, *ret
= NULL
, **wkn_dn_exp
= NULL
,
1357 **bind_dn_exp
= NULL
;
1358 const char *attrs
[] = {"distinguishedName", NULL
};
1359 int new_ln
, wkn_ln
, bind_ln
, i
;
1361 if (wknguid
== NULL
) {
1365 if (asprintf(&base
, "<WKGUID=%s,%s>", wknguid
, ads
->config
.bind_path
) == -1) {
1366 DEBUG(1, ("asprintf failed!\n"));
1370 status
= ads_search_dn(ads
, &res
, base
, attrs
);
1371 if (!ADS_ERR_OK(status
)) {
1372 DEBUG(1,("Failed while searching for: %s\n", base
));
1376 if (ads_count_replies(ads
, res
) != 1) {
1380 /* substitute the bind-path from the well-known-guid-search result */
1381 wkn_dn
= ads_get_dn(ads
, res
);
1386 wkn_dn_exp
= ldap_explode_dn(wkn_dn
, 0);
1391 bind_dn_exp
= ldap_explode_dn(ads
->config
.bind_path
, 0);
1396 for (wkn_ln
=0; wkn_dn_exp
[wkn_ln
]; wkn_ln
++)
1398 for (bind_ln
=0; bind_dn_exp
[bind_ln
]; bind_ln
++)
1401 new_ln
= wkn_ln
- bind_ln
;
1403 ret
= SMB_STRDUP(wkn_dn_exp
[0]);
1408 for (i
=1; i
< new_ln
; i
++) {
1411 if (asprintf(&s
, "%s,%s", ret
, wkn_dn_exp
[i
]) == -1) {
1417 ret
= SMB_STRDUP(s
);
1426 ads_msgfree(ads
, res
);
1427 ads_memfree(ads
, wkn_dn
);
1429 ldap_value_free(wkn_dn_exp
);
1432 ldap_value_free(bind_dn_exp
);
1439 * Adds (appends) an item to an attribute array, rather then
1440 * replacing the whole list
1441 * @param ctx An initialized TALLOC_CTX
1442 * @param mods An initialized ADS_MODLIST
1443 * @param name name of the ldap attribute to append to
1444 * @param vals an array of values to add
1445 * @return status of addition
1448 ADS_STATUS
ads_add_strlist(TALLOC_CTX
*ctx
, ADS_MODLIST
*mods
,
1449 const char *name
, const char **vals
)
1451 return ads_modlist_add(ctx
, mods
, LDAP_MOD_ADD
, name
,
1452 (const void *) vals
);
1456 * Determines the computer account's current KVNO via an LDAP lookup
1457 * @param ads An initialized ADS_STRUCT
1458 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1459 * @return the kvno for the computer account, or -1 in case of a failure.
1462 uint32
ads_get_kvno(ADS_STRUCT
*ads
, const char *machine_name
)
1464 LDAPMessage
*res
= NULL
;
1465 uint32 kvno
= (uint32
)-1; /* -1 indicates a failure */
1467 const char *attrs
[] = {"msDS-KeyVersionNumber", NULL
};
1468 char *dn_string
= NULL
;
1469 ADS_STATUS ret
= ADS_ERROR(LDAP_SUCCESS
);
1471 DEBUG(5,("ads_get_kvno: Searching for host %s\n", machine_name
));
1472 if (asprintf(&filter
, "(samAccountName=%s$)", machine_name
) == -1) {
1475 ret
= ads_search(ads
, &res
, filter
, attrs
);
1477 if (!ADS_ERR_OK(ret
) && ads_count_replies(ads
, res
)) {
1478 DEBUG(1,("ads_get_kvno: Computer Account For %s not found.\n", machine_name
));
1479 ads_msgfree(ads
, res
);
1483 dn_string
= ads_get_dn(ads
, res
);
1485 DEBUG(0,("ads_get_kvno: out of memory.\n"));
1486 ads_msgfree(ads
, res
);
1489 DEBUG(5,("ads_get_kvno: Using: %s\n", dn_string
));
1490 ads_memfree(ads
, dn_string
);
1492 /* ---------------------------------------------------------
1493 * 0 is returned as a default KVNO from this point on...
1494 * This is done because Windows 2000 does not support key
1495 * version numbers. Chances are that a failure in the next
1496 * step is simply due to Windows 2000 being used for a
1497 * domain controller. */
1500 if (!ads_pull_uint32(ads
, res
, "msDS-KeyVersionNumber", &kvno
)) {
1501 DEBUG(3,("ads_get_kvno: Error Determining KVNO!\n"));
1502 DEBUG(3,("ads_get_kvno: Windows 2000 does not support KVNO's, so this may be normal.\n"));
1503 ads_msgfree(ads
, res
);
1508 DEBUG(5,("ads_get_kvno: Looked Up KVNO of: %d\n", kvno
));
1509 ads_msgfree(ads
, res
);
1514 * This clears out all registered spn's for a given hostname
1515 * @param ads An initilaized ADS_STRUCT
1516 * @param machine_name the NetBIOS name of the computer.
1517 * @return 0 upon success, non-zero otherwise.
1520 ADS_STATUS
ads_clear_service_principal_names(ADS_STRUCT
*ads
, const char *machine_name
)
1523 LDAPMessage
*res
= NULL
;
1525 const char *servicePrincipalName
[1] = {NULL
};
1526 ADS_STATUS ret
= ADS_ERROR(LDAP_SUCCESS
);
1527 char *dn_string
= NULL
;
1529 ret
= ads_find_machine_acct(ads
, &res
, machine_name
);
1530 if (!ADS_ERR_OK(ret
) || ads_count_replies(ads
, res
) != 1) {
1531 DEBUG(5,("ads_clear_service_principal_names: WARNING: Host Account for %s not found... skipping operation.\n", machine_name
));
1532 DEBUG(5,("ads_clear_service_principal_names: WARNING: Service Principals for %s have NOT been cleared.\n", machine_name
));
1533 ads_msgfree(ads
, res
);
1534 return ADS_ERROR(LDAP_NO_SUCH_OBJECT
);
1537 DEBUG(5,("ads_clear_service_principal_names: Host account for %s found\n", machine_name
));
1538 ctx
= talloc_init("ads_clear_service_principal_names");
1540 ads_msgfree(ads
, res
);
1541 return ADS_ERROR(LDAP_NO_MEMORY
);
1544 if (!(mods
= ads_init_mods(ctx
))) {
1545 talloc_destroy(ctx
);
1546 ads_msgfree(ads
, res
);
1547 return ADS_ERROR(LDAP_NO_MEMORY
);
1549 ret
= ads_mod_strlist(ctx
, &mods
, "servicePrincipalName", servicePrincipalName
);
1550 if (!ADS_ERR_OK(ret
)) {
1551 DEBUG(1,("ads_clear_service_principal_names: Error creating strlist.\n"));
1552 ads_msgfree(ads
, res
);
1553 talloc_destroy(ctx
);
1556 dn_string
= ads_get_dn(ads
, res
);
1558 talloc_destroy(ctx
);
1559 ads_msgfree(ads
, res
);
1560 return ADS_ERROR(LDAP_NO_MEMORY
);
1562 ret
= ads_gen_mod(ads
, dn_string
, mods
);
1563 ads_memfree(ads
,dn_string
);
1564 if (!ADS_ERR_OK(ret
)) {
1565 DEBUG(1,("ads_clear_service_principal_names: Error: Updating Service Principals for machine %s in LDAP\n",
1567 ads_msgfree(ads
, res
);
1568 talloc_destroy(ctx
);
1572 ads_msgfree(ads
, res
);
1573 talloc_destroy(ctx
);
1578 * This adds a service principal name to an existing computer account
1579 * (found by hostname) in AD.
1580 * @param ads An initialized ADS_STRUCT
1581 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1582 * @param my_fqdn The fully qualified DNS name of the machine
1583 * @param spn A string of the service principal to add, i.e. 'host'
1584 * @return 0 upon sucess, or non-zero if a failure occurs
1587 ADS_STATUS
ads_add_service_principal_name(ADS_STRUCT
*ads
, const char *machine_name
,
1588 const char *my_fqdn
, const char *spn
)
1592 LDAPMessage
*res
= NULL
;
1595 char *dn_string
= NULL
;
1596 const char *servicePrincipalName
[3] = {NULL
, NULL
, NULL
};
1598 ret
= ads_find_machine_acct(ads
, &res
, machine_name
);
1599 if (!ADS_ERR_OK(ret
) || ads_count_replies(ads
, res
) != 1) {
1600 DEBUG(1,("ads_add_service_principal_name: WARNING: Host Account for %s not found... skipping operation.\n",
1602 DEBUG(1,("ads_add_service_principal_name: WARNING: Service Principal '%s/%s@%s' has NOT been added.\n",
1603 spn
, machine_name
, ads
->config
.realm
));
1604 ads_msgfree(ads
, res
);
1605 return ADS_ERROR(LDAP_NO_SUCH_OBJECT
);
1608 DEBUG(1,("ads_add_service_principal_name: Host account for %s found\n", machine_name
));
1609 if (!(ctx
= talloc_init("ads_add_service_principal_name"))) {
1610 ads_msgfree(ads
, res
);
1611 return ADS_ERROR(LDAP_NO_MEMORY
);
1614 /* add short name spn */
1616 if ( (psp1
= talloc_asprintf(ctx
, "%s/%s", spn
, machine_name
)) == NULL
) {
1617 talloc_destroy(ctx
);
1618 ads_msgfree(ads
, res
);
1619 return ADS_ERROR(LDAP_NO_MEMORY
);
1622 strlower_m(&psp1
[strlen(spn
)]);
1623 servicePrincipalName
[0] = psp1
;
1625 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n",
1626 psp1
, machine_name
));
1629 /* add fully qualified spn */
1631 if ( (psp2
= talloc_asprintf(ctx
, "%s/%s", spn
, my_fqdn
)) == NULL
) {
1632 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
1636 strlower_m(&psp2
[strlen(spn
)]);
1637 servicePrincipalName
[1] = psp2
;
1639 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n",
1640 psp2
, machine_name
));
1642 if ( (mods
= ads_init_mods(ctx
)) == NULL
) {
1643 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
1647 ret
= ads_add_strlist(ctx
, &mods
, "servicePrincipalName", servicePrincipalName
);
1648 if (!ADS_ERR_OK(ret
)) {
1649 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
1653 if ( (dn_string
= ads_get_dn(ads
, res
)) == NULL
) {
1654 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
1658 ret
= ads_gen_mod(ads
, dn_string
, mods
);
1659 ads_memfree(ads
,dn_string
);
1660 if (!ADS_ERR_OK(ret
)) {
1661 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
1667 ads_msgfree(ads
, res
);
1672 * adds a machine account to the ADS server
1673 * @param ads An intialized ADS_STRUCT
1674 * @param machine_name - the NetBIOS machine name of this account.
1675 * @param account_type A number indicating the type of account to create
1676 * @param org_unit The LDAP path in which to place this account
1677 * @return 0 upon success, or non-zero otherwise
1680 ADS_STATUS
ads_create_machine_acct(ADS_STRUCT
*ads
, const char *machine_name
,
1681 const char *org_unit
)
1684 char *samAccountName
, *controlstr
;
1687 char *machine_escaped
= NULL
;
1689 const char *objectClass
[] = {"top", "person", "organizationalPerson",
1690 "user", "computer", NULL
};
1691 LDAPMessage
*res
= NULL
;
1692 uint32 acct_control
= ( UF_WORKSTATION_TRUST_ACCOUNT
|\
1693 UF_DONT_EXPIRE_PASSWD
|\
1694 UF_ACCOUNTDISABLE
);
1696 if (!(ctx
= talloc_init("ads_add_machine_acct")))
1697 return ADS_ERROR(LDAP_NO_MEMORY
);
1699 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
1701 machine_escaped
= escape_rdn_val_string_alloc(machine_name
);
1702 if (!machine_escaped
) {
1706 new_dn
= talloc_asprintf(ctx
, "cn=%s,%s", machine_escaped
, org_unit
);
1707 samAccountName
= talloc_asprintf(ctx
, "%s$", machine_name
);
1709 if ( !new_dn
|| !samAccountName
) {
1713 #ifndef ENCTYPE_ARCFOUR_HMAC
1714 acct_control
|= UF_USE_DES_KEY_ONLY
;
1717 if (!(controlstr
= talloc_asprintf(ctx
, "%u", acct_control
))) {
1721 if (!(mods
= ads_init_mods(ctx
))) {
1725 ads_mod_str(ctx
, &mods
, "cn", machine_name
);
1726 ads_mod_str(ctx
, &mods
, "sAMAccountName", samAccountName
);
1727 ads_mod_strlist(ctx
, &mods
, "objectClass", objectClass
);
1728 ads_mod_str(ctx
, &mods
, "userAccountControl", controlstr
);
1730 ret
= ads_gen_add(ads
, new_dn
, mods
);
1733 SAFE_FREE(machine_escaped
);
1734 ads_msgfree(ads
, res
);
1735 talloc_destroy(ctx
);
1741 dump a binary result from ldap
1743 static void dump_binary(const char *field
, struct berval
**values
)
1746 for (i
=0; values
[i
]; i
++) {
1747 printf("%s: ", field
);
1748 for (j
=0; j
<values
[i
]->bv_len
; j
++) {
1749 printf("%02X", (unsigned char)values
[i
]->bv_val
[j
]);
1755 static void dump_guid(const char *field
, struct berval
**values
)
1759 for (i
=0; values
[i
]; i
++) {
1760 memcpy(guid
.info
, values
[i
]->bv_val
, sizeof(guid
.info
));
1761 printf("%s: %s\n", field
,
1762 smb_uuid_string_static(smb_uuid_unpack_static(guid
)));
1767 dump a sid result from ldap
1769 static void dump_sid(const char *field
, struct berval
**values
)
1772 for (i
=0; values
[i
]; i
++) {
1774 sid_parse(values
[i
]->bv_val
, values
[i
]->bv_len
, &sid
);
1775 printf("%s: %s\n", field
, sid_string_static(&sid
));
1780 dump ntSecurityDescriptor
1782 static void dump_sd(const char *filed
, struct berval
**values
)
1787 TALLOC_CTX
*ctx
= 0;
1789 if (!(ctx
= talloc_init("sec_io_desc")))
1793 prs_init(&ps
, values
[0]->bv_len
, ctx
, UNMARSHALL
);
1794 prs_copy_data_in(&ps
, values
[0]->bv_val
, values
[0]->bv_len
);
1795 prs_set_offset(&ps
,0);
1798 if (!sec_io_desc("sd", &psd
, &ps
, 1)) {
1800 talloc_destroy(ctx
);
1803 if (psd
) ads_disp_sd(psd
);
1806 talloc_destroy(ctx
);
1810 dump a string result from ldap
1812 static void dump_string(const char *field
, char **values
)
1815 for (i
=0; values
[i
]; i
++) {
1816 printf("%s: %s\n", field
, values
[i
]);
1821 dump a field from LDAP on stdout
1825 static BOOL
ads_dump_field(char *field
, void **values
, void *data_area
)
1830 void (*handler
)(const char *, struct berval
**);
1832 {"objectGUID", False
, dump_guid
},
1833 {"netbootGUID", False
, dump_guid
},
1834 {"nTSecurityDescriptor", False
, dump_sd
},
1835 {"dnsRecord", False
, dump_binary
},
1836 {"objectSid", False
, dump_sid
},
1837 {"tokenGroups", False
, dump_sid
},
1838 {"tokenGroupsNoGCAcceptable", False
, dump_sid
},
1839 {"tokengroupsGlobalandUniversal", False
, dump_sid
},
1840 {"mS-DS-CreatorSID", False
, dump_sid
},
1845 if (!field
) { /* must be end of an entry */
1850 for (i
=0; handlers
[i
].name
; i
++) {
1851 if (StrCaseCmp(handlers
[i
].name
, field
) == 0) {
1852 if (!values
) /* first time, indicate string or not */
1853 return handlers
[i
].string
;
1854 handlers
[i
].handler(field
, (struct berval
**) values
);
1858 if (!handlers
[i
].name
) {
1859 if (!values
) /* first time, indicate string conversion */
1861 dump_string(field
, (char **)values
);
1867 * Dump a result from LDAP on stdout
1868 * used for debugging
1869 * @param ads connection to ads server
1870 * @param res Results to dump
1873 void ads_dump(ADS_STRUCT
*ads
, LDAPMessage
*res
)
1875 ads_process_results(ads
, res
, ads_dump_field
, NULL
);
1879 * Walk through results, calling a function for each entry found.
1880 * The function receives a field name, a berval * array of values,
1881 * and a data area passed through from the start. The function is
1882 * called once with null for field and values at the end of each
1884 * @param ads connection to ads server
1885 * @param res Results to process
1886 * @param fn Function for processing each result
1887 * @param data_area user-defined area to pass to function
1889 void ads_process_results(ADS_STRUCT
*ads
, LDAPMessage
*res
,
1890 BOOL(*fn
)(char *, void **, void *),
1896 if (!(ctx
= talloc_init("ads_process_results")))
1899 for (msg
= ads_first_entry(ads
, res
); msg
;
1900 msg
= ads_next_entry(ads
, msg
)) {
1904 for (utf8_field
=ldap_first_attribute(ads
->ld
,
1905 (LDAPMessage
*)msg
,&b
);
1907 utf8_field
=ldap_next_attribute(ads
->ld
,
1908 (LDAPMessage
*)msg
,b
)) {
1909 struct berval
**ber_vals
;
1910 char **str_vals
, **utf8_vals
;
1914 pull_utf8_talloc(ctx
, &field
, utf8_field
);
1915 string
= fn(field
, NULL
, data_area
);
1918 utf8_vals
= ldap_get_values(ads
->ld
,
1919 (LDAPMessage
*)msg
, field
);
1920 str_vals
= ads_pull_strvals(ctx
,
1921 (const char **) utf8_vals
);
1922 fn(field
, (void **) str_vals
, data_area
);
1923 ldap_value_free(utf8_vals
);
1925 ber_vals
= ldap_get_values_len(ads
->ld
,
1926 (LDAPMessage
*)msg
, field
);
1927 fn(field
, (void **) ber_vals
, data_area
);
1929 ldap_value_free_len(ber_vals
);
1931 ldap_memfree(utf8_field
);
1934 talloc_free_children(ctx
);
1935 fn(NULL
, NULL
, data_area
); /* completed an entry */
1938 talloc_destroy(ctx
);
1942 * count how many replies are in a LDAPMessage
1943 * @param ads connection to ads server
1944 * @param res Results to count
1945 * @return number of replies
1947 int ads_count_replies(ADS_STRUCT
*ads
, void *res
)
1949 return ldap_count_entries(ads
->ld
, (LDAPMessage
*)res
);
1953 * pull the first entry from a ADS result
1954 * @param ads connection to ads server
1955 * @param res Results of search
1956 * @return first entry from result
1958 LDAPMessage
*ads_first_entry(ADS_STRUCT
*ads
, LDAPMessage
*res
)
1960 return ldap_first_entry(ads
->ld
, res
);
1964 * pull the next entry from a ADS result
1965 * @param ads connection to ads server
1966 * @param res Results of search
1967 * @return next entry from result
1969 LDAPMessage
*ads_next_entry(ADS_STRUCT
*ads
, LDAPMessage
*res
)
1971 return ldap_next_entry(ads
->ld
, res
);
1975 * pull the first message from a ADS result
1976 * @param ads connection to ads server
1977 * @param res Results of search
1978 * @return first message from result
1980 LDAPMessage
*ads_first_message(ADS_STRUCT
*ads
, LDAPMessage
*res
)
1982 return ldap_first_message(ads
->ld
, res
);
1986 * pull the next message from a ADS result
1987 * @param ads connection to ads server
1988 * @param res Results of search
1989 * @return next message from result
1991 LDAPMessage
*ads_next_message(ADS_STRUCT
*ads
, LDAPMessage
*res
)
1993 return ldap_next_message(ads
->ld
, res
);
1997 * pull a single string from a ADS result
1998 * @param ads connection to ads server
1999 * @param mem_ctx TALLOC_CTX to use for allocating result string
2000 * @param msg Results of search
2001 * @param field Attribute to retrieve
2002 * @return Result string in talloc context
2004 char *ads_pull_string(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
, LDAPMessage
*msg
,
2012 values
= ldap_get_values(ads
->ld
, msg
, field
);
2017 rc
= pull_utf8_talloc(mem_ctx
, &ux_string
,
2019 if (rc
!= (size_t)-1)
2023 ldap_value_free(values
);
2028 * pull an array of strings from a ADS result
2029 * @param ads connection to ads server
2030 * @param mem_ctx TALLOC_CTX to use for allocating result string
2031 * @param msg Results of search
2032 * @param field Attribute to retrieve
2033 * @return Result strings in talloc context
2035 char **ads_pull_strings(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
,
2036 LDAPMessage
*msg
, const char *field
,
2043 values
= ldap_get_values(ads
->ld
, msg
, field
);
2047 *num_values
= ldap_count_values(values
);
2049 ret
= TALLOC_ARRAY(mem_ctx
, char *, *num_values
+ 1);
2051 ldap_value_free(values
);
2055 for (i
=0;i
<*num_values
;i
++) {
2056 if (pull_utf8_talloc(mem_ctx
, &ret
[i
], values
[i
]) == -1) {
2057 ldap_value_free(values
);
2063 ldap_value_free(values
);
2068 * pull an array of strings from a ADS result
2069 * (handle large multivalue attributes with range retrieval)
2070 * @param ads connection to ads server
2071 * @param mem_ctx TALLOC_CTX to use for allocating result string
2072 * @param msg Results of search
2073 * @param field Attribute to retrieve
2074 * @param current_strings strings returned by a previous call to this function
2075 * @param next_attribute The next query should ask for this attribute
2076 * @param num_values How many values did we get this time?
2077 * @param more_values Are there more values to get?
2078 * @return Result strings in talloc context
2080 char **ads_pull_strings_range(ADS_STRUCT
*ads
,
2081 TALLOC_CTX
*mem_ctx
,
2082 LDAPMessage
*msg
, const char *field
,
2083 char **current_strings
,
2084 const char **next_attribute
,
2085 size_t *num_strings
,
2089 char *expected_range_attrib
, *range_attr
;
2090 BerElement
*ptr
= NULL
;
2093 size_t num_new_strings
;
2094 unsigned long int range_start
;
2095 unsigned long int range_end
;
2097 /* we might have been given the whole lot anyway */
2098 if ((strings
= ads_pull_strings(ads
, mem_ctx
, msg
, field
, num_strings
))) {
2099 *more_strings
= False
;
2103 expected_range_attrib
= talloc_asprintf(mem_ctx
, "%s;Range=", field
);
2105 /* look for Range result */
2106 for (attr
= ldap_first_attribute(ads
->ld
, (LDAPMessage
*)msg
, &ptr
);
2108 attr
= ldap_next_attribute(ads
->ld
, (LDAPMessage
*)msg
, ptr
)) {
2109 /* we ignore the fact that this is utf8, as all attributes are ascii... */
2110 if (strnequal(attr
, expected_range_attrib
, strlen(expected_range_attrib
))) {
2118 /* nothing here - this field is just empty */
2119 *more_strings
= False
;
2123 if (sscanf(&range_attr
[strlen(expected_range_attrib
)], "%lu-%lu",
2124 &range_start
, &range_end
) == 2) {
2125 *more_strings
= True
;
2127 if (sscanf(&range_attr
[strlen(expected_range_attrib
)], "%lu-*",
2128 &range_start
) == 1) {
2129 *more_strings
= False
;
2131 DEBUG(1, ("ads_pull_strings_range: Cannot parse Range attriubte (%s)\n",
2133 ldap_memfree(range_attr
);
2134 *more_strings
= False
;
2139 if ((*num_strings
) != range_start
) {
2140 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) doesn't start at %u, but at %lu"
2141 " - aborting range retreival\n",
2142 range_attr
, (unsigned int)(*num_strings
) + 1, range_start
));
2143 ldap_memfree(range_attr
);
2144 *more_strings
= False
;
2148 new_strings
= ads_pull_strings(ads
, mem_ctx
, msg
, range_attr
, &num_new_strings
);
2150 if (*more_strings
&& ((*num_strings
+ num_new_strings
) != (range_end
+ 1))) {
2151 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) tells us we have %lu "
2152 "strings in this bunch, but we only got %lu - aborting range retreival\n",
2153 range_attr
, (unsigned long int)range_end
- range_start
+ 1,
2154 (unsigned long int)num_new_strings
));
2155 ldap_memfree(range_attr
);
2156 *more_strings
= False
;
2160 strings
= TALLOC_REALLOC_ARRAY(mem_ctx
, current_strings
, char *,
2161 *num_strings
+ num_new_strings
);
2163 if (strings
== NULL
) {
2164 ldap_memfree(range_attr
);
2165 *more_strings
= False
;
2169 if (new_strings
&& num_new_strings
) {
2170 memcpy(&strings
[*num_strings
], new_strings
,
2171 sizeof(*new_strings
) * num_new_strings
);
2174 (*num_strings
) += num_new_strings
;
2176 if (*more_strings
) {
2177 *next_attribute
= talloc_asprintf(mem_ctx
,
2182 if (!*next_attribute
) {
2183 DEBUG(1, ("talloc_asprintf for next attribute failed!\n"));
2184 ldap_memfree(range_attr
);
2185 *more_strings
= False
;
2190 ldap_memfree(range_attr
);
2196 * pull a single uint32 from a ADS result
2197 * @param ads connection to ads server
2198 * @param msg Results of search
2199 * @param field Attribute to retrieve
2200 * @param v Pointer to int to store result
2201 * @return boolean inidicating success
2203 BOOL
ads_pull_uint32(ADS_STRUCT
*ads
, LDAPMessage
*msg
, const char *field
,
2208 values
= ldap_get_values(ads
->ld
, msg
, field
);
2212 ldap_value_free(values
);
2216 *v
= atoi(values
[0]);
2217 ldap_value_free(values
);
2222 * pull a single objectGUID from an ADS result
2223 * @param ads connection to ADS server
2224 * @param msg results of search
2225 * @param guid 37-byte area to receive text guid
2226 * @return boolean indicating success
2228 BOOL
ads_pull_guid(ADS_STRUCT
*ads
, LDAPMessage
*msg
, struct GUID
*guid
)
2231 UUID_FLAT flat_guid
;
2233 values
= ldap_get_values(ads
->ld
, msg
, "objectGUID");
2238 memcpy(&flat_guid
.info
, values
[0], sizeof(UUID_FLAT
));
2239 smb_uuid_unpack(flat_guid
, guid
);
2240 ldap_value_free(values
);
2243 ldap_value_free(values
);
2250 * pull a single DOM_SID from a ADS result
2251 * @param ads connection to ads server
2252 * @param msg Results of search
2253 * @param field Attribute to retrieve
2254 * @param sid Pointer to sid to store result
2255 * @return boolean inidicating success
2257 BOOL
ads_pull_sid(ADS_STRUCT
*ads
, LDAPMessage
*msg
, const char *field
,
2260 struct berval
**values
;
2263 values
= ldap_get_values_len(ads
->ld
, msg
, field
);
2269 ret
= sid_parse(values
[0]->bv_val
, values
[0]->bv_len
, sid
);
2271 ldap_value_free_len(values
);
2276 * pull an array of DOM_SIDs from a ADS result
2277 * @param ads connection to ads server
2278 * @param mem_ctx TALLOC_CTX for allocating sid array
2279 * @param msg Results of search
2280 * @param field Attribute to retrieve
2281 * @param sids pointer to sid array to allocate
2282 * @return the count of SIDs pulled
2284 int ads_pull_sids(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
,
2285 LDAPMessage
*msg
, const char *field
, DOM_SID
**sids
)
2287 struct berval
**values
;
2291 values
= ldap_get_values_len(ads
->ld
, msg
, field
);
2296 for (i
=0; values
[i
]; i
++)
2300 (*sids
) = TALLOC_ARRAY(mem_ctx
, DOM_SID
, i
);
2302 ldap_value_free_len(values
);
2310 for (i
=0; values
[i
]; i
++) {
2311 ret
= sid_parse(values
[i
]->bv_val
, values
[i
]->bv_len
, &(*sids
)[count
]);
2314 DEBUG(10, ("pulling SID: %s\n", sid_to_string(sid
, &(*sids
)[count
])));
2319 ldap_value_free_len(values
);
2324 * pull a SEC_DESC from a ADS result
2325 * @param ads connection to ads server
2326 * @param mem_ctx TALLOC_CTX for allocating sid array
2327 * @param msg Results of search
2328 * @param field Attribute to retrieve
2329 * @param sd Pointer to *SEC_DESC to store result (talloc()ed)
2330 * @return boolean inidicating success
2332 BOOL
ads_pull_sd(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
,
2333 LDAPMessage
*msg
, const char *field
, SEC_DESC
**sd
)
2335 struct berval
**values
;
2338 values
= ldap_get_values_len(ads
->ld
, msg
, field
);
2340 if (!values
) return False
;
2344 prs_init(&ps
, values
[0]->bv_len
, mem_ctx
, UNMARSHALL
);
2345 prs_copy_data_in(&ps
, values
[0]->bv_val
, values
[0]->bv_len
);
2346 prs_set_offset(&ps
,0);
2348 ret
= sec_io_desc("sd", sd
, &ps
, 1);
2352 ldap_value_free_len(values
);
2357 * in order to support usernames longer than 21 characters we need to
2358 * use both the sAMAccountName and the userPrincipalName attributes
2359 * It seems that not all users have the userPrincipalName attribute set
2361 * @param ads connection to ads server
2362 * @param mem_ctx TALLOC_CTX for allocating sid array
2363 * @param msg Results of search
2364 * @return the username
2366 char *ads_pull_username(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
,
2372 /* lookup_name() only works on the sAMAccountName to
2373 returning the username portion of userPrincipalName
2374 breaks winbindd_getpwnam() */
2376 ret
= ads_pull_string(ads
, mem_ctx
, msg
, "userPrincipalName");
2377 if (ret
&& (p
= strchr_m(ret
, '@'))) {
2382 return ads_pull_string(ads
, mem_ctx
, msg
, "sAMAccountName");
2387 * find the update serial number - this is the core of the ldap cache
2388 * @param ads connection to ads server
2389 * @param ads connection to ADS server
2390 * @param usn Pointer to retrieved update serial number
2391 * @return status of search
2393 ADS_STATUS
ads_USN(ADS_STRUCT
*ads
, uint32
*usn
)
2395 const char *attrs
[] = {"highestCommittedUSN", NULL
};
2399 status
= ads_do_search_retry(ads
, "", LDAP_SCOPE_BASE
, "(objectclass=*)", attrs
, &res
);
2400 if (!ADS_ERR_OK(status
))
2403 if (ads_count_replies(ads
, res
) != 1) {
2404 ads_msgfree(ads
, res
);
2405 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED
);
2408 if (!ads_pull_uint32(ads
, res
, "highestCommittedUSN", usn
)) {
2409 ads_msgfree(ads
, res
);
2410 return ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE
);
2413 ads_msgfree(ads
, res
);
2417 /* parse a ADS timestring - typical string is
2418 '20020917091222.0Z0' which means 09:12.22 17th September
2420 static time_t ads_parse_time(const char *str
)
2426 if (sscanf(str
, "%4d%2d%2d%2d%2d%2d",
2427 &tm
.tm_year
, &tm
.tm_mon
, &tm
.tm_mday
,
2428 &tm
.tm_hour
, &tm
.tm_min
, &tm
.tm_sec
) != 6) {
2437 /********************************************************************
2438 ********************************************************************/
2440 ADS_STATUS
ads_current_time(ADS_STRUCT
*ads
)
2442 const char *attrs
[] = {"currentTime", NULL
};
2447 ADS_STRUCT
*ads_s
= ads
;
2449 if (!(ctx
= talloc_init("ads_current_time"))) {
2450 return ADS_ERROR(LDAP_NO_MEMORY
);
2453 /* establish a new ldap tcp session if necessary */
2456 if ( (ads_s
= ads_init( ads
->server
.realm
, ads
->server
.workgroup
,
2457 ads
->server
.ldap_server
)) == NULL
)
2461 ads_s
->auth
.flags
= ADS_AUTH_ANON_BIND
;
2462 status
= ads_connect( ads_s
);
2463 if ( !ADS_ERR_OK(status
))
2467 status
= ads_do_search(ads_s
, "", LDAP_SCOPE_BASE
, "(objectclass=*)", attrs
, &res
);
2468 if (!ADS_ERR_OK(status
)) {
2472 timestr
= ads_pull_string(ads_s
, ctx
, res
, "currentTime");
2474 ads_msgfree(ads_s
, res
);
2475 status
= ADS_ERROR(LDAP_NO_RESULTS_RETURNED
);
2479 /* but save the time and offset in the original ADS_STRUCT */
2481 ads
->config
.current_time
= ads_parse_time(timestr
);
2483 if (ads
->config
.current_time
!= 0) {
2484 ads
->auth
.time_offset
= ads
->config
.current_time
- time(NULL
);
2485 DEBUG(4,("time offset is %d seconds\n", ads
->auth
.time_offset
));
2488 ads_msgfree(ads
, res
);
2490 status
= ADS_SUCCESS
;
2493 /* free any temporary ads connections */
2494 if ( ads_s
!= ads
) {
2495 ads_destroy( &ads_s
);
2497 talloc_destroy(ctx
);
2502 /********************************************************************
2503 ********************************************************************/
2505 ADS_STATUS
ads_domain_func_level(ADS_STRUCT
*ads
, uint32
*val
)
2507 const char *attrs
[] = {"domainFunctionality", NULL
};
2510 ADS_STRUCT
*ads_s
= ads
;
2512 *val
= DS_DOMAIN_FUNCTION_2000
;
2514 /* establish a new ldap tcp session if necessary */
2517 if ( (ads_s
= ads_init( ads
->server
.realm
, ads
->server
.workgroup
,
2518 ads
->server
.ldap_server
)) == NULL
)
2522 ads_s
->auth
.flags
= ADS_AUTH_ANON_BIND
;
2523 status
= ads_connect( ads_s
);
2524 if ( !ADS_ERR_OK(status
))
2528 /* If the attribute does not exist assume it is a Windows 2000
2529 functional domain */
2531 status
= ads_do_search(ads_s
, "", LDAP_SCOPE_BASE
, "(objectclass=*)", attrs
, &res
);
2532 if (!ADS_ERR_OK(status
)) {
2533 if ( status
.err
.rc
== LDAP_NO_SUCH_ATTRIBUTE
) {
2534 status
= ADS_SUCCESS
;
2539 if ( !ads_pull_uint32(ads_s
, res
, "domainFunctionality", val
) ) {
2540 DEBUG(5,("ads_domain_func_level: Failed to pull the domainFunctionality attribute.\n"));
2542 DEBUG(3,("ads_domain_func_level: %d\n", *val
));
2545 ads_msgfree(ads
, res
);
2548 /* free any temporary ads connections */
2549 if ( ads_s
!= ads
) {
2550 ads_destroy( &ads_s
);
2557 * find the domain sid for our domain
2558 * @param ads connection to ads server
2559 * @param sid Pointer to domain sid
2560 * @return status of search
2562 ADS_STATUS
ads_domain_sid(ADS_STRUCT
*ads
, DOM_SID
*sid
)
2564 const char *attrs
[] = {"objectSid", NULL
};
2568 rc
= ads_do_search_retry(ads
, ads
->config
.bind_path
, LDAP_SCOPE_BASE
, "(objectclass=*)",
2570 if (!ADS_ERR_OK(rc
)) return rc
;
2571 if (!ads_pull_sid(ads
, res
, "objectSid", sid
)) {
2572 ads_msgfree(ads
, res
);
2573 return ADS_ERROR_SYSTEM(ENOENT
);
2575 ads_msgfree(ads
, res
);
2581 * find our site name
2582 * @param ads connection to ads server
2583 * @param mem_ctx Pointer to talloc context
2584 * @param site_name Pointer to the sitename
2585 * @return status of search
2587 ADS_STATUS
ads_site_dn(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
, const char **site_name
)
2591 const char *dn
, *service_name
;
2592 const char *attrs
[] = { "dsServiceName", NULL
};
2594 status
= ads_do_search(ads
, "", LDAP_SCOPE_BASE
, "(objectclass=*)", attrs
, &res
);
2595 if (!ADS_ERR_OK(status
)) {
2599 service_name
= ads_pull_string(ads
, mem_ctx
, res
, "dsServiceName");
2600 if (service_name
== NULL
) {
2601 ads_msgfree(ads
, res
);
2602 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED
);
2605 ads_msgfree(ads
, res
);
2607 /* go up three levels */
2608 dn
= ads_parent_dn(ads_parent_dn(ads_parent_dn(service_name
)));
2610 return ADS_ERROR(LDAP_NO_MEMORY
);
2613 *site_name
= talloc_strdup(mem_ctx
, dn
);
2614 if (*site_name
== NULL
) {
2615 return ADS_ERROR(LDAP_NO_MEMORY
);
2620 dsServiceName: CN=NTDS Settings,CN=W2K3DC,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,DC=ber,DC=suse,DC=de
2625 * find the site dn where a machine resides
2626 * @param ads connection to ads server
2627 * @param mem_ctx Pointer to talloc context
2628 * @param computer_name name of the machine
2629 * @param site_name Pointer to the sitename
2630 * @return status of search
2632 ADS_STATUS
ads_site_dn_for_machine(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
, const char *computer_name
, const char **site_dn
)
2636 const char *parent
, *config_context
, *filter
;
2637 const char *attrs
[] = { "configurationNamingContext", NULL
};
2640 /* shortcut a query */
2641 if (strequal(computer_name
, ads
->config
.ldap_server_name
)) {
2642 return ads_site_dn(ads
, mem_ctx
, site_dn
);
2645 status
= ads_do_search(ads
, "", LDAP_SCOPE_BASE
, "(objectclass=*)", attrs
, &res
);
2646 if (!ADS_ERR_OK(status
)) {
2650 config_context
= ads_pull_string(ads
, mem_ctx
, res
, "configurationNamingContext");
2651 if (config_context
== NULL
) {
2652 ads_msgfree(ads
, res
);
2653 return ADS_ERROR(LDAP_NO_MEMORY
);
2656 filter
= talloc_asprintf(mem_ctx
, "(cn=%s)", computer_name
);
2657 if (filter
== NULL
) {
2658 ads_msgfree(ads
, res
);
2659 return ADS_ERROR(LDAP_NO_MEMORY
);
2662 ads_msgfree(ads
, res
);
2664 status
= ads_do_search(ads
, config_context
, LDAP_SCOPE_SUBTREE
, filter
, NULL
, &res
);
2665 if (!ADS_ERR_OK(status
)) {
2669 if (ads_count_replies(ads
, res
) != 1) {
2670 ads_msgfree(ads
, res
);
2671 return ADS_ERROR(LDAP_NO_SUCH_OBJECT
);
2674 dn
= ads_get_dn(ads
, res
);
2676 ads_msgfree(ads
, res
);
2677 return ADS_ERROR(LDAP_NO_MEMORY
);
2680 /* go up three levels */
2681 parent
= ads_parent_dn(ads_parent_dn(ads_parent_dn(dn
)));
2682 if (parent
== NULL
) {
2683 ads_msgfree(ads
, res
);
2684 ads_memfree(ads
, dn
);
2685 return ADS_ERROR(LDAP_NO_MEMORY
);
2688 *site_dn
= talloc_strdup(mem_ctx
, parent
);
2689 if (*site_dn
== NULL
) {
2690 ads_msgfree(ads
, res
);
2691 ads_memfree(ads
, dn
);
2692 return ADS_ERROR(LDAP_NO_MEMORY
);
2695 ads_memfree(ads
, dn
);
2696 ads_msgfree(ads
, res
);
2702 * get the upn suffixes for a domain
2703 * @param ads connection to ads server
2704 * @param mem_ctx Pointer to talloc context
2705 * @param suffixes Pointer to an array of suffixes
2706 * @param num_suffixes Pointer to the number of suffixes
2707 * @return status of search
2709 ADS_STATUS
ads_upn_suffixes(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
, char ***suffixes
, size_t *num_suffixes
)
2713 const char *config_context
, *base
;
2714 const char *attrs
[] = { "configurationNamingContext", NULL
};
2715 const char *attrs2
[] = { "uPNSuffixes", NULL
};
2717 status
= ads_do_search(ads
, "", LDAP_SCOPE_BASE
, "(objectclass=*)", attrs
, &res
);
2718 if (!ADS_ERR_OK(status
)) {
2722 config_context
= ads_pull_string(ads
, mem_ctx
, res
, "configurationNamingContext");
2723 if (config_context
== NULL
) {
2724 ads_msgfree(ads
, res
);
2725 return ADS_ERROR(LDAP_NO_MEMORY
);
2728 ads_msgfree(ads
, res
);
2730 base
= talloc_asprintf(mem_ctx
, "cn=Partitions,%s", config_context
);
2732 return ADS_ERROR(LDAP_NO_MEMORY
);
2735 status
= ads_search_dn(ads
, &res
, base
, attrs2
);
2736 if (!ADS_ERR_OK(status
)) {
2740 if (ads_count_replies(ads
, res
) != 1) {
2741 return ADS_ERROR(LDAP_NO_SUCH_OBJECT
);
2744 (*suffixes
) = ads_pull_strings(ads
, mem_ctx
, res
, "uPNSuffixes", num_suffixes
);
2745 if ((*suffixes
) == NULL
) {
2746 ads_msgfree(ads
, res
);
2747 return ADS_ERROR(LDAP_NO_MEMORY
);
2750 ads_msgfree(ads
, res
);
2756 * pull a DOM_SID from an extended dn string
2757 * @param mem_ctx TALLOC_CTX
2758 * @param flags string type of extended_dn
2759 * @param sid pointer to a DOM_SID
2760 * @return boolean inidicating success
2762 BOOL
ads_get_sid_from_extended_dn(TALLOC_CTX
*mem_ctx
,
2764 enum ads_extended_dn_flags flags
,
2774 * ADS_EXTENDED_DN_HEX_STRING:
2775 * <GUID=238e1963cb390f4bb032ba0105525a29>;<SID=010500000000000515000000bb68c8fd6b61b427572eb04556040000>;CN=gd,OU=berlin,OU=suse,DC=ber,DC=suse,DC=de
2777 * ADS_EXTENDED_DN_STRING (only with w2k3):
2778 <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
2781 p
= strchr(dn
, ';');
2786 if (strncmp(p
, ";<SID=", strlen(";<SID=")) != 0) {
2790 p
+= strlen(";<SID=");
2799 DEBUG(100,("ads_get_sid_from_extended_dn: sid string is %s\n", p
));
2803 case ADS_EXTENDED_DN_STRING
:
2804 if (!string_to_sid(sid
, p
)) {
2808 case ADS_EXTENDED_DN_HEX_STRING
: {
2812 buf_len
= strhex_to_str(buf
, strlen(p
), p
);
2817 if (!sid_parse(buf
, buf_len
, sid
)) {
2818 DEBUG(10,("failed to parse sid\n"));
2824 DEBUG(10,("unknown extended dn format\n"));
2832 * pull an array of DOM_SIDs from a ADS result
2833 * @param ads connection to ads server
2834 * @param mem_ctx TALLOC_CTX for allocating sid array
2835 * @param msg Results of search
2836 * @param field Attribute to retrieve
2837 * @param flags string type of extended_dn
2838 * @param sids pointer to sid array to allocate
2839 * @return the count of SIDs pulled
2841 int ads_pull_sids_from_extendeddn(ADS_STRUCT
*ads
,
2842 TALLOC_CTX
*mem_ctx
,
2845 enum ads_extended_dn_flags flags
,
2852 if ((dn_strings
= ads_pull_strings(ads
, mem_ctx
, msg
, field
,
2853 &dn_count
)) == NULL
) {
2857 (*sids
) = TALLOC_ZERO_ARRAY(mem_ctx
, DOM_SID
, dn_count
+ 1);
2859 TALLOC_FREE(dn_strings
);
2863 for (i
=0; i
<dn_count
; i
++) {
2865 if (!ads_get_sid_from_extended_dn(mem_ctx
, dn_strings
[i
],
2866 flags
, &(*sids
)[i
])) {
2868 TALLOC_FREE(dn_strings
);
2873 TALLOC_FREE(dn_strings
);
2878 /********************************************************************
2879 ********************************************************************/
2881 char* ads_get_dnshostname( ADS_STRUCT
*ads
, TALLOC_CTX
*ctx
, const char *machine_name
)
2883 LDAPMessage
*res
= NULL
;
2888 status
= ads_find_machine_acct(ads
, &res
, global_myname());
2889 if (!ADS_ERR_OK(status
)) {
2890 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
2895 if ( (count
= ads_count_replies(ads
, res
)) != 1 ) {
2896 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count
));
2900 if ( (name
= ads_pull_string(ads
, ctx
, res
, "dNSHostName")) == NULL
) {
2901 DEBUG(0,("ads_get_dnshostname: No dNSHostName attribute!\n"));
2905 ads_msgfree(ads
, res
);
2910 /********************************************************************
2911 ********************************************************************/
2913 char* ads_get_upn( ADS_STRUCT
*ads
, TALLOC_CTX
*ctx
, const char *machine_name
)
2915 LDAPMessage
*res
= NULL
;
2920 status
= ads_find_machine_acct(ads
, &res
, global_myname());
2921 if (!ADS_ERR_OK(status
)) {
2922 DEBUG(0,("ads_get_upn: Failed to find account for %s\n",
2927 if ( (count
= ads_count_replies(ads
, res
)) != 1 ) {
2928 DEBUG(1,("ads_get_upn: %d entries returned!\n", count
));
2932 if ( (name
= ads_pull_string(ads
, ctx
, res
, "userPrincipalName")) == NULL
) {
2933 DEBUG(2,("ads_get_upn: No userPrincipalName attribute!\n"));
2937 ads_msgfree(ads
, res
);
2942 /********************************************************************
2943 ********************************************************************/
2945 char* ads_get_samaccountname( ADS_STRUCT
*ads
, TALLOC_CTX
*ctx
, const char *machine_name
)
2947 LDAPMessage
*res
= NULL
;
2952 status
= ads_find_machine_acct(ads
, &res
, global_myname());
2953 if (!ADS_ERR_OK(status
)) {
2954 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
2959 if ( (count
= ads_count_replies(ads
, res
)) != 1 ) {
2960 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count
));
2964 if ( (name
= ads_pull_string(ads
, ctx
, res
, "sAMAccountName")) == NULL
) {
2965 DEBUG(0,("ads_get_dnshostname: No sAMAccountName attribute!\n"));
2969 ads_msgfree(ads
, res
);
2976 SAVED CODE
- we used to join via ldap
- remember how we did
this. JRA
.
2979 * Join a machine to a realm
2980 * Creates the machine account and sets the machine password
2981 * @param ads connection to ads server
2982 * @param machine name of host to add
2983 * @param org_unit Organizational unit to place machine in
2984 * @return status of join
2986 ADS_STATUS
ads_join_realm(ADS_STRUCT
*ads
, const char *machine_name
,
2987 uint32 account_type
, const char *org_unit
)
2990 LDAPMessage
*res
= NULL
;
2993 /* machine name must be lowercase */
2994 machine
= SMB_STRDUP(machine_name
);
2995 strlower_m(machine
);
2998 status = ads_find_machine_acct(ads, (void **)&res, machine);
2999 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
3000 DEBUG(0, ("Host account for %s already exists - deleting old account\n", machine));
3001 status = ads_leave_realm(ads, machine);
3002 if (!ADS_ERR_OK(status)) {
3003 DEBUG(0, ("Failed to delete host '%s' from the '%s' realm.\n",
3004 machine, ads->config.realm));
3009 status
= ads_add_machine_acct(ads
, machine
, account_type
, org_unit
);
3010 if (!ADS_ERR_OK(status
)) {
3011 DEBUG(0, ("ads_join_realm: ads_add_machine_acct failed (%s): %s\n", machine
, ads_errstr(status
)));
3016 status
= ads_find_machine_acct(ads
, (void **)(void *)&res
, machine
);
3017 if (!ADS_ERR_OK(status
)) {
3018 DEBUG(0, ("ads_join_realm: Host account test failed for machine %s\n", machine
));
3024 ads_msgfree(ads
, res
);
3031 * Delete a machine from the realm
3032 * @param ads connection to ads server
3033 * @param hostname Machine to remove
3034 * @return status of delete
3036 ADS_STATUS
ads_leave_realm(ADS_STRUCT
*ads
, const char *hostname
)
3041 char *hostnameDN
, *host
;
3043 LDAPControl ldap_control
;
3044 LDAPControl
* pldap_control
[2] = {NULL
, NULL
};
3046 pldap_control
[0] = &ldap_control
;
3047 memset(&ldap_control
, 0, sizeof(LDAPControl
));
3048 ldap_control
.ldctl_oid
= (char *)LDAP_SERVER_TREE_DELETE_OID
;
3050 /* hostname must be lowercase */
3051 host
= SMB_STRDUP(hostname
);
3054 status
= ads_find_machine_acct(ads
, &res
, host
);
3055 if (!ADS_ERR_OK(status
)) {
3056 DEBUG(0, ("Host account for %s does not exist.\n", host
));
3061 msg
= ads_first_entry(ads
, res
);
3064 return ADS_ERROR_SYSTEM(ENOENT
);
3067 hostnameDN
= ads_get_dn(ads
, (LDAPMessage
*)msg
);
3069 rc
= ldap_delete_ext_s(ads
->ld
, hostnameDN
, pldap_control
, NULL
);
3071 DEBUG(3,("ldap_delete_ext_s failed with error code %d\n", rc
));
3073 DEBUG(3,("ldap_delete_ext_s succeeded with error code %d\n", rc
));
3076 if (rc
!= LDAP_SUCCESS
) {
3077 const char *attrs
[] = { "cn", NULL
};
3078 LDAPMessage
*msg_sub
;
3080 /* we only search with scope ONE, we do not expect any further
3081 * objects to be created deeper */
3083 status
= ads_do_search_retry(ads
, hostnameDN
,
3084 LDAP_SCOPE_ONELEVEL
,
3085 "(objectclass=*)", attrs
, &res
);
3087 if (!ADS_ERR_OK(status
)) {
3089 ads_memfree(ads
, hostnameDN
);
3093 for (msg_sub
= ads_first_entry(ads
, res
); msg_sub
;
3094 msg_sub
= ads_next_entry(ads
, msg_sub
)) {
3098 if ((dn
= ads_get_dn(ads
, msg_sub
)) == NULL
) {
3100 ads_memfree(ads
, hostnameDN
);
3101 return ADS_ERROR(LDAP_NO_MEMORY
);
3104 status
= ads_del_dn(ads
, dn
);
3105 if (!ADS_ERR_OK(status
)) {
3106 DEBUG(3,("failed to delete dn %s: %s\n", dn
, ads_errstr(status
)));
3108 ads_memfree(ads
, dn
);
3109 ads_memfree(ads
, hostnameDN
);
3113 ads_memfree(ads
, dn
);
3116 /* there should be no subordinate objects anymore */
3117 status
= ads_do_search_retry(ads
, hostnameDN
,
3118 LDAP_SCOPE_ONELEVEL
,
3119 "(objectclass=*)", attrs
, &res
);
3121 if (!ADS_ERR_OK(status
) || ( (ads_count_replies(ads
, res
)) > 0 ) ) {
3123 ads_memfree(ads
, hostnameDN
);
3127 /* delete hostnameDN now */
3128 status
= ads_del_dn(ads
, hostnameDN
);
3129 if (!ADS_ERR_OK(status
)) {
3131 DEBUG(3,("failed to delete dn %s: %s\n", hostnameDN
, ads_errstr(status
)));
3132 ads_memfree(ads
, hostnameDN
);
3137 ads_memfree(ads
, hostnameDN
);
3139 status
= ads_find_machine_acct(ads
, &res
, host
);
3140 if (ADS_ERR_OK(status
) && ads_count_replies(ads
, res
) == 1) {
3141 DEBUG(3, ("Failed to remove host account.\n"));