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
;
118 /**********************************************
119 Do client and server sitename match ?
120 **********************************************/
122 BOOL
ads_sitename_match(ADS_STRUCT
*ads
)
124 if (ads
->config
.server_site_name
== NULL
&&
125 ads
->config
.client_site_name
== NULL
) {
126 DEBUG(10,("ads_sitename_match: both null\n"));
129 if (ads
->config
.server_site_name
&&
130 ads
->config
.client_site_name
&&
131 strequal(ads
->config
.server_site_name
,
132 ads
->config
.client_site_name
)) {
133 DEBUG(10,("ads_sitename_match: name %s match\n", ads
->config
.server_site_name
));
136 DEBUG(10,("ads_sitename_match: no match between server: %s and client: %s\n",
137 ads
->config
.server_site_name
? ads
->config
.server_site_name
: "NULL",
138 ads
->config
.client_site_name
? ads
->config
.client_site_name
: "NULL"));
143 try a connection to a given ldap server, returning True and setting the servers IP
144 in the ads struct if successful
146 BOOL
ads_try_connect(ADS_STRUCT
*ads
, const char *server
)
149 struct cldap_netlogon_reply cldap_reply
;
151 if (!server
|| !*server
) {
155 DEBUG(5,("ads_try_connect: sending CLDAP request to %s (realm: %s)\n",
156 server
, ads
->server
.realm
));
158 /* this copes with inet_ntoa brokenness */
160 srv
= SMB_STRDUP(server
);
162 ZERO_STRUCT( cldap_reply
);
164 if ( !ads_cldap_netlogon( srv
, ads
->server
.realm
, &cldap_reply
) ) {
165 DEBUG(3,("ads_try_connect: CLDAP request %s failed.\n", srv
));
170 /* Check the CLDAP reply flags */
172 if ( !(cldap_reply
.flags
& ADS_LDAP
) ) {
173 DEBUG(1,("ads_try_connect: %s's CLDAP reply says it is not an LDAP server!\n",
179 /* Fill in the ads->config values */
181 SAFE_FREE(ads
->config
.realm
);
182 SAFE_FREE(ads
->config
.bind_path
);
183 SAFE_FREE(ads
->config
.ldap_server_name
);
184 SAFE_FREE(ads
->config
.server_site_name
);
185 SAFE_FREE(ads
->config
.client_site_name
);
186 SAFE_FREE(ads
->server
.workgroup
);
188 ads
->config
.flags
= cldap_reply
.flags
;
189 ads
->config
.ldap_server_name
= SMB_STRDUP(cldap_reply
.hostname
);
190 strupper_m(cldap_reply
.domain
);
191 ads
->config
.realm
= SMB_STRDUP(cldap_reply
.domain
);
192 ads
->config
.bind_path
= ads_build_dn(ads
->config
.realm
);
193 if (*cldap_reply
.server_site_name
) {
194 ads
->config
.server_site_name
=
195 SMB_STRDUP(cldap_reply
.server_site_name
);
197 if (*cldap_reply
.client_site_name
) {
198 ads
->config
.client_site_name
=
199 SMB_STRDUP(cldap_reply
.client_site_name
);
202 ads
->server
.workgroup
= SMB_STRDUP(cldap_reply
.netbios_domain
);
204 ads
->ldap_port
= LDAP_PORT
;
205 ads
->ldap_ip
= *interpret_addr2(srv
);
208 /* Store our site name. */
209 sitename_store( cldap_reply
.client_site_name
);
214 /**********************************************************************
215 Try to find an AD dc using our internal name resolution routines
216 Try the realm first and then then workgroup name if netbios is not
218 **********************************************************************/
220 static NTSTATUS
ads_find_dc(ADS_STRUCT
*ads
)
224 struct ip_service
*ip_list
;
226 BOOL got_realm
= False
;
227 BOOL use_own_domain
= False
;
228 NTSTATUS status
= NT_STATUS_UNSUCCESSFUL
;
230 /* if the realm and workgroup are both empty, assume they are ours */
233 c_realm
= ads
->server
.realm
;
235 if ( !c_realm
|| !*c_realm
) {
236 /* special case where no realm and no workgroup means our own */
237 if ( !ads
->server
.workgroup
|| !*ads
->server
.workgroup
) {
238 use_own_domain
= True
;
239 c_realm
= lp_realm();
243 if (c_realm
&& *c_realm
)
247 /* we need to try once with the realm name and fallback to the
248 netbios domain name if we fail (if netbios has not been disabled */
250 if ( !got_realm
&& !lp_disable_netbios() ) {
251 c_realm
= ads
->server
.workgroup
;
252 if (!c_realm
|| !*c_realm
) {
253 if ( use_own_domain
)
254 c_realm
= lp_workgroup();
257 if ( !c_realm
|| !*c_realm
) {
258 DEBUG(0,("ads_find_dc: no realm or workgroup! Don't know what to do\n"));
259 return NT_STATUS_INVALID_PARAMETER
; /* rather need MISSING_PARAMETER ... */
263 pstrcpy( realm
, c_realm
);
265 DEBUG(6,("ads_find_dc: looking for %s '%s'\n",
266 (got_realm
? "realm" : "domain"), realm
));
268 status
= get_sorted_dc_list(realm
, &ip_list
, &count
, got_realm
);
269 if (!NT_STATUS_IS_OK(status
)) {
270 /* fall back to netbios if we can */
271 if ( got_realm
&& !lp_disable_netbios() ) {
279 /* if we fail this loop, then giveup since all the IP addresses returned were dead */
280 for ( i
=0; i
<count
; i
++ ) {
283 fstrcpy( server
, inet_ntoa(ip_list
[i
].ip
) );
285 if ( !NT_STATUS_IS_OK(check_negative_conn_cache(realm
, server
)) )
289 /* realm in this case is a workgroup name. We need
290 to ignore any IP addresses in the negative connection
291 cache that match ip addresses returned in the ad realm
292 case. It sucks that I have to reproduce the logic above... */
293 c_realm
= ads
->server
.realm
;
294 if ( !c_realm
|| !*c_realm
) {
295 if ( !ads
->server
.workgroup
|| !*ads
->server
.workgroup
) {
296 c_realm
= lp_realm();
299 if (c_realm
&& *c_realm
&&
300 !NT_STATUS_IS_OK(check_negative_conn_cache(c_realm
, server
))) {
301 /* Ensure we add the workgroup name for this
302 IP address as negative too. */
303 add_failed_connection_entry( realm
, server
, NT_STATUS_UNSUCCESSFUL
);
308 if ( ads_try_connect(ads
, server
) ) {
313 /* keep track of failures */
314 add_failed_connection_entry( realm
, server
, NT_STATUS_UNSUCCESSFUL
);
319 return NT_STATUS_NO_LOGON_SERVERS
;
324 * Connect to the LDAP server
325 * @param ads Pointer to an existing ADS_STRUCT
326 * @return status of connection
328 ADS_STATUS
ads_connect(ADS_STRUCT
*ads
)
330 int version
= LDAP_VERSION3
;
334 ads
->last_attempt
= time(NULL
);
337 /* try with a user specified server */
339 if (ads
->server
.ldap_server
&&
340 ads_try_connect(ads
, ads
->server
.ldap_server
)) {
344 ntstatus
= ads_find_dc(ads
);
345 if (NT_STATUS_IS_OK(ntstatus
)) {
349 return ADS_ERROR_NT(ntstatus
);
352 DEBUG(3,("Connected to LDAP server %s\n", inet_ntoa(ads
->ldap_ip
)));
354 if (!ads
->auth
.user_name
) {
355 /* Must use the userPrincipalName value here or sAMAccountName
356 and not servicePrincipalName; found by Guenther Deschner */
358 asprintf(&ads
->auth
.user_name
, "%s$", global_myname() );
361 if (!ads
->auth
.realm
) {
362 ads
->auth
.realm
= SMB_STRDUP(ads
->config
.realm
);
365 if (!ads
->auth
.kdc_server
) {
366 ads
->auth
.kdc_server
= SMB_STRDUP(inet_ntoa(ads
->ldap_ip
));
370 /* this is a really nasty hack to avoid ADS DNS problems. It needs a patch
371 to MIT kerberos to work (tridge) */
374 asprintf(&env
, "KRB5_KDC_ADDRESS_%s", ads
->config
.realm
);
375 setenv(env
, ads
->auth
.kdc_server
, 1);
380 /* If the caller() requested no LDAP bind, then we are done */
382 if (ads
->auth
.flags
& ADS_AUTH_NO_BIND
) {
386 /* Otherwise setup the TCP LDAP session */
388 if ( (ads
->ld
= ldap_open_with_timeout(ads
->config
.ldap_server_name
,
389 LDAP_PORT
, lp_ldap_timeout())) == NULL
)
391 return ADS_ERROR(LDAP_OPERATIONS_ERROR
);
394 /* cache the successful connection for workgroup and realm */
395 if (ads_sitename_match(ads
)) {
396 saf_store( ads
->server
.workgroup
, inet_ntoa(ads
->ldap_ip
));
397 saf_store( ads
->server
.realm
, inet_ntoa(ads
->ldap_ip
));
400 ldap_set_option(ads
->ld
, LDAP_OPT_PROTOCOL_VERSION
, &version
);
402 status
= ADS_ERROR(smb_ldap_start_tls(ads
->ld
, version
));
403 if (!ADS_ERR_OK(status
)) {
407 /* fill in the current time and offsets */
409 status
= ads_current_time( ads
);
410 if ( !ADS_ERR_OK(status
) ) {
414 /* Now do the bind */
416 if (ads
->auth
.flags
& ADS_AUTH_ANON_BIND
) {
417 return ADS_ERROR(ldap_simple_bind_s( ads
->ld
, NULL
, NULL
));
420 if (ads
->auth
.flags
& ADS_AUTH_SIMPLE_BIND
) {
421 return ADS_ERROR(ldap_simple_bind_s( ads
->ld
, ads
->auth
.user_name
, ads
->auth
.password
));
424 return ads_sasl_bind(ads
);
428 Duplicate a struct berval into talloc'ed memory
430 static struct berval
*dup_berval(TALLOC_CTX
*ctx
, const struct berval
*in_val
)
432 struct berval
*value
;
434 if (!in_val
) return NULL
;
436 value
= TALLOC_ZERO_P(ctx
, struct berval
);
439 if (in_val
->bv_len
== 0) return value
;
441 value
->bv_len
= in_val
->bv_len
;
442 value
->bv_val
= (char *)TALLOC_MEMDUP(ctx
, in_val
->bv_val
,
448 Make a values list out of an array of (struct berval *)
450 static struct berval
**ads_dup_values(TALLOC_CTX
*ctx
,
451 const struct berval
**in_vals
)
453 struct berval
**values
;
456 if (!in_vals
) return NULL
;
457 for (i
=0; in_vals
[i
]; i
++)
459 values
= TALLOC_ZERO_ARRAY(ctx
, struct berval
*, i
+1);
460 if (!values
) return NULL
;
462 for (i
=0; in_vals
[i
]; i
++) {
463 values
[i
] = dup_berval(ctx
, in_vals
[i
]);
469 UTF8-encode a values list out of an array of (char *)
471 static char **ads_push_strvals(TALLOC_CTX
*ctx
, const char **in_vals
)
476 if (!in_vals
) return NULL
;
477 for (i
=0; in_vals
[i
]; i
++)
479 values
= TALLOC_ZERO_ARRAY(ctx
, char *, i
+1);
480 if (!values
) return NULL
;
482 for (i
=0; in_vals
[i
]; i
++) {
483 push_utf8_talloc(ctx
, &values
[i
], in_vals
[i
]);
489 Pull a (char *) array out of a UTF8-encoded values list
491 static char **ads_pull_strvals(TALLOC_CTX
*ctx
, const char **in_vals
)
496 if (!in_vals
) return NULL
;
497 for (i
=0; in_vals
[i
]; i
++)
499 values
= TALLOC_ZERO_ARRAY(ctx
, char *, i
+1);
500 if (!values
) return NULL
;
502 for (i
=0; in_vals
[i
]; i
++) {
503 pull_utf8_talloc(ctx
, &values
[i
], in_vals
[i
]);
509 * Do a search with paged results. cookie must be null on the first
510 * call, and then returned on each subsequent call. It will be null
511 * again when the entire search is complete
512 * @param ads connection to ads server
513 * @param bind_path Base dn for the search
514 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
515 * @param expr Search expression - specified in local charset
516 * @param attrs Attributes to retrieve - specified in utf8 or ascii
517 * @param res ** which will contain results - free res* with ads_msgfree()
518 * @param count Number of entries retrieved on this page
519 * @param cookie The paged results cookie to be returned on subsequent calls
520 * @return status of search
522 static ADS_STATUS
ads_do_paged_search_args(ADS_STRUCT
*ads
,
523 const char *bind_path
,
524 int scope
, const char *expr
,
525 const char **attrs
, void *args
,
527 int *count
, struct berval
**cookie
)
530 char *utf8_expr
, *utf8_path
, **search_attrs
;
531 LDAPControl PagedResults
, NoReferrals
, ExtendedDn
, *controls
[4], **rcontrols
;
532 BerElement
*cookie_be
= NULL
;
533 struct berval
*cookie_bv
= NULL
;
534 BerElement
*extdn_be
= NULL
;
535 struct berval
*extdn_bv
= NULL
;
538 ads_control
*external_control
= (ads_control
*) args
;
542 if (!(ctx
= talloc_init("ads_do_paged_search_args")))
543 return ADS_ERROR(LDAP_NO_MEMORY
);
545 /* 0 means the conversion worked but the result was empty
546 so we only fail if it's -1. In any case, it always
547 at least nulls out the dest */
548 if ((push_utf8_talloc(ctx
, &utf8_expr
, expr
) == (size_t)-1) ||
549 (push_utf8_talloc(ctx
, &utf8_path
, bind_path
) == (size_t)-1)) {
554 if (!attrs
|| !(*attrs
))
557 /* This would be the utf8-encoded version...*/
558 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
559 if (!(str_list_copy(&search_attrs
, attrs
))) {
566 /* Paged results only available on ldap v3 or later */
567 ldap_get_option(ads
->ld
, LDAP_OPT_PROTOCOL_VERSION
, &version
);
568 if (version
< LDAP_VERSION3
) {
569 rc
= LDAP_NOT_SUPPORTED
;
573 cookie_be
= ber_alloc_t(LBER_USE_DER
);
575 ber_printf(cookie_be
, "{iO}", (ber_int_t
) 1000, *cookie
);
576 ber_bvfree(*cookie
); /* don't need it from last time */
579 ber_printf(cookie_be
, "{io}", (ber_int_t
) 1000, "", 0);
581 ber_flatten(cookie_be
, &cookie_bv
);
582 PagedResults
.ldctl_oid
= CONST_DISCARD(char *, ADS_PAGE_CTL_OID
);
583 PagedResults
.ldctl_iscritical
= (char) 1;
584 PagedResults
.ldctl_value
.bv_len
= cookie_bv
->bv_len
;
585 PagedResults
.ldctl_value
.bv_val
= cookie_bv
->bv_val
;
587 NoReferrals
.ldctl_oid
= CONST_DISCARD(char *, ADS_NO_REFERRALS_OID
);
588 NoReferrals
.ldctl_iscritical
= (char) 0;
589 NoReferrals
.ldctl_value
.bv_len
= 0;
590 NoReferrals
.ldctl_value
.bv_val
= CONST_DISCARD(char *, "");
592 if (external_control
&& strequal(external_control
->control
, ADS_EXTENDED_DN_OID
)) {
594 ExtendedDn
.ldctl_oid
= CONST_DISCARD(char *, external_control
->control
);
595 ExtendedDn
.ldctl_iscritical
= (char) external_control
->critical
;
597 /* win2k does not accept a ldctl_value beeing passed in */
599 if (external_control
->val
!= 0) {
601 if ((extdn_be
= ber_alloc_t(LBER_USE_DER
)) == NULL
) {
606 if ((ber_printf(extdn_be
, "{i}", (ber_int_t
) external_control
->val
)) == -1) {
610 if ((ber_flatten(extdn_be
, &extdn_bv
)) == -1) {
615 ExtendedDn
.ldctl_value
.bv_len
= extdn_bv
->bv_len
;
616 ExtendedDn
.ldctl_value
.bv_val
= extdn_bv
->bv_val
;
619 ExtendedDn
.ldctl_value
.bv_len
= 0;
620 ExtendedDn
.ldctl_value
.bv_val
= NULL
;
623 controls
[0] = &NoReferrals
;
624 controls
[1] = &PagedResults
;
625 controls
[2] = &ExtendedDn
;
629 controls
[0] = &NoReferrals
;
630 controls
[1] = &PagedResults
;
634 /* we need to disable referrals as the openldap libs don't
635 handle them and paged results at the same time. Using them
636 together results in the result record containing the server
637 page control being removed from the result list (tridge/jmcd)
639 leaving this in despite the control that says don't generate
640 referrals, in case the server doesn't support it (jmcd)
642 ldap_set_option(ads
->ld
, LDAP_OPT_REFERRALS
, LDAP_OPT_OFF
);
644 rc
= ldap_search_with_timeout(ads
->ld
, utf8_path
, scope
, utf8_expr
,
645 search_attrs
, 0, controls
,
647 (LDAPMessage
**)res
);
649 ber_free(cookie_be
, 1);
650 ber_bvfree(cookie_bv
);
653 DEBUG(3,("ads_do_paged_search_args: ldap_search_with_timeout(%s) -> %s\n", expr
,
654 ldap_err2string(rc
)));
658 rc
= ldap_parse_result(ads
->ld
, *res
, NULL
, NULL
, NULL
,
659 NULL
, &rcontrols
, 0);
665 for (i
=0; rcontrols
[i
]; i
++) {
666 if (strcmp(ADS_PAGE_CTL_OID
, rcontrols
[i
]->ldctl_oid
) == 0) {
667 cookie_be
= ber_init(&rcontrols
[i
]->ldctl_value
);
668 ber_scanf(cookie_be
,"{iO}", (ber_int_t
*) count
,
670 /* the berval is the cookie, but must be freed when
672 if (cookie_bv
->bv_len
) /* still more to do */
673 *cookie
=ber_bvdup(cookie_bv
);
676 ber_bvfree(cookie_bv
);
677 ber_free(cookie_be
, 1);
681 ldap_controls_free(rcontrols
);
687 ber_free(extdn_be
, 1);
691 ber_bvfree(extdn_bv
);
694 /* if/when we decide to utf8-encode attrs, take out this next line */
695 str_list_free(&search_attrs
);
697 return ADS_ERROR(rc
);
700 static ADS_STATUS
ads_do_paged_search(ADS_STRUCT
*ads
, const char *bind_path
,
701 int scope
, const char *expr
,
702 const char **attrs
, LDAPMessage
**res
,
703 int *count
, struct berval
**cookie
)
705 return ads_do_paged_search_args(ads
, bind_path
, scope
, expr
, attrs
, NULL
, res
, count
, cookie
);
710 * Get all results for a search. This uses ads_do_paged_search() to return
711 * all entries in a large search.
712 * @param ads connection to ads server
713 * @param bind_path Base dn for the search
714 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
715 * @param expr Search expression
716 * @param attrs Attributes to retrieve
717 * @param res ** which will contain results - free res* with ads_msgfree()
718 * @return status of search
720 ADS_STATUS
ads_do_search_all_args(ADS_STRUCT
*ads
, const char *bind_path
,
721 int scope
, const char *expr
,
722 const char **attrs
, void *args
,
725 struct berval
*cookie
= NULL
;
730 status
= ads_do_paged_search_args(ads
, bind_path
, scope
, expr
, attrs
, args
, res
,
733 if (!ADS_ERR_OK(status
))
736 #ifdef HAVE_LDAP_ADD_RESULT_ENTRY
738 LDAPMessage
*res2
= NULL
;
740 LDAPMessage
*msg
, *next
;
742 status2
= ads_do_paged_search_args(ads
, bind_path
, scope
, expr
,
743 attrs
, args
, &res2
, &count
, &cookie
);
745 if (!ADS_ERR_OK(status2
)) break;
747 /* this relies on the way that ldap_add_result_entry() works internally. I hope
748 that this works on all ldap libs, but I have only tested with openldap */
749 for (msg
= ads_first_entry(ads
, res2
); msg
; msg
= next
) {
750 next
= ads_next_entry(ads
, msg
);
751 ldap_add_result_entry((LDAPMessage
**)res
, msg
);
753 /* note that we do not free res2, as the memory is now
754 part of the main returned list */
757 DEBUG(0, ("no ldap_add_result_entry() support in LDAP libs!\n"));
758 status
= ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL
);
764 ADS_STATUS
ads_do_search_all(ADS_STRUCT
*ads
, const char *bind_path
,
765 int scope
, const char *expr
,
766 const char **attrs
, LDAPMessage
**res
)
768 return ads_do_search_all_args(ads
, bind_path
, scope
, expr
, attrs
, NULL
, res
);
772 * Run a function on all results for a search. Uses ads_do_paged_search() and
773 * runs the function as each page is returned, using ads_process_results()
774 * @param ads connection to ads server
775 * @param bind_path Base dn for the search
776 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
777 * @param expr Search expression - specified in local charset
778 * @param attrs Attributes to retrieve - specified in UTF-8 or ascii
779 * @param fn Function which takes attr name, values list, and data_area
780 * @param data_area Pointer which is passed to function on each call
781 * @return status of search
783 ADS_STATUS
ads_do_search_all_fn(ADS_STRUCT
*ads
, const char *bind_path
,
784 int scope
, const char *expr
, const char **attrs
,
785 BOOL(*fn
)(char *, void **, void *),
788 struct berval
*cookie
= NULL
;
793 status
= ads_do_paged_search(ads
, bind_path
, scope
, expr
, attrs
, &res
,
796 if (!ADS_ERR_OK(status
)) return status
;
798 ads_process_results(ads
, res
, fn
, data_area
);
799 ads_msgfree(ads
, res
);
802 status
= ads_do_paged_search(ads
, bind_path
, scope
, expr
, attrs
,
803 &res
, &count
, &cookie
);
805 if (!ADS_ERR_OK(status
)) break;
807 ads_process_results(ads
, res
, fn
, data_area
);
808 ads_msgfree(ads
, res
);
815 * Do a search with a timeout.
816 * @param ads connection to ads server
817 * @param bind_path Base dn for the search
818 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
819 * @param expr Search expression
820 * @param attrs Attributes to retrieve
821 * @param res ** which will contain results - free res* with ads_msgfree()
822 * @return status of search
824 ADS_STATUS
ads_do_search(ADS_STRUCT
*ads
, const char *bind_path
, int scope
,
826 const char **attrs
, LDAPMessage
**res
)
829 char *utf8_expr
, *utf8_path
, **search_attrs
= NULL
;
833 if (!(ctx
= talloc_init("ads_do_search"))) {
834 DEBUG(1,("ads_do_search: talloc_init() failed!"));
835 return ADS_ERROR(LDAP_NO_MEMORY
);
838 /* 0 means the conversion worked but the result was empty
839 so we only fail if it's negative. In any case, it always
840 at least nulls out the dest */
841 if ((push_utf8_talloc(ctx
, &utf8_expr
, expr
) == (size_t)-1) ||
842 (push_utf8_talloc(ctx
, &utf8_path
, bind_path
) == (size_t)-1)) {
843 DEBUG(1,("ads_do_search: push_utf8_talloc() failed!"));
848 if (!attrs
|| !(*attrs
))
851 /* This would be the utf8-encoded version...*/
852 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
853 if (!(str_list_copy(&search_attrs
, attrs
)))
855 DEBUG(1,("ads_do_search: str_list_copy() failed!"));
861 /* see the note in ads_do_paged_search - we *must* disable referrals */
862 ldap_set_option(ads
->ld
, LDAP_OPT_REFERRALS
, LDAP_OPT_OFF
);
864 rc
= ldap_search_with_timeout(ads
->ld
, utf8_path
, scope
, utf8_expr
,
865 search_attrs
, 0, NULL
, NULL
,
867 (LDAPMessage
**)res
);
869 if (rc
== LDAP_SIZELIMIT_EXCEEDED
) {
870 DEBUG(3,("Warning! sizelimit exceeded in ldap. Truncating.\n"));
876 /* if/when we decide to utf8-encode attrs, take out this next line */
877 str_list_free(&search_attrs
);
878 return ADS_ERROR(rc
);
881 * Do a general ADS search
882 * @param ads connection to ads server
883 * @param res ** which will contain results - free res* with ads_msgfree()
884 * @param expr Search expression
885 * @param attrs Attributes to retrieve
886 * @return status of search
888 ADS_STATUS
ads_search(ADS_STRUCT
*ads
, LDAPMessage
**res
,
889 const char *expr
, const char **attrs
)
891 return ads_do_search(ads
, ads
->config
.bind_path
, LDAP_SCOPE_SUBTREE
,
896 * Do a search on a specific DistinguishedName
897 * @param ads connection to ads server
898 * @param res ** which will contain results - free res* with ads_msgfree()
899 * @param dn DistinguishName to search
900 * @param attrs Attributes to retrieve
901 * @return status of search
903 ADS_STATUS
ads_search_dn(ADS_STRUCT
*ads
, LDAPMessage
**res
,
904 const char *dn
, const char **attrs
)
906 return ads_do_search(ads
, dn
, LDAP_SCOPE_BASE
, "(objectclass=*)",
911 * Free up memory from a ads_search
912 * @param ads connection to ads server
913 * @param msg Search results to free
915 void ads_msgfree(ADS_STRUCT
*ads
, LDAPMessage
*msg
)
922 * Free up memory from various ads requests
923 * @param ads connection to ads server
924 * @param mem Area to free
926 void ads_memfree(ADS_STRUCT
*ads
, void *mem
)
932 * Get a dn from search results
933 * @param ads connection to ads server
934 * @param msg Search result
937 char *ads_get_dn(ADS_STRUCT
*ads
, LDAPMessage
*msg
)
939 char *utf8_dn
, *unix_dn
;
941 utf8_dn
= ldap_get_dn(ads
->ld
, msg
);
944 DEBUG (5, ("ads_get_dn: ldap_get_dn failed\n"));
948 if (pull_utf8_allocate(&unix_dn
, utf8_dn
) == (size_t)-1) {
949 DEBUG(0,("ads_get_dn: string conversion failure utf8 [%s]\n",
953 ldap_memfree(utf8_dn
);
958 * Get a canonical dn from search results
959 * @param ads connection to ads server
960 * @param msg Search result
963 char *ads_get_dn_canonical(ADS_STRUCT
*ads
, LDAPMessage
*msg
)
965 #ifdef HAVE_LDAP_DN2AD_CANONICAL
966 return ldap_dn2ad_canonical(ads_get_dn(ads
, msg
));
973 * Get the parent from a dn
974 * @param dn the dn to return the parent from
975 * @return parent dn string
977 char *ads_parent_dn(const char *dn
)
995 * Find a machine account given a hostname
996 * @param ads connection to ads server
997 * @param res ** which will contain results - free res* with ads_msgfree()
998 * @param host Hostname to search for
999 * @return status of search
1001 ADS_STATUS
ads_find_machine_acct(ADS_STRUCT
*ads
, LDAPMessage
**res
,
1002 const char *machine
)
1006 const char *attrs
[] = {"*", "nTSecurityDescriptor", NULL
};
1010 /* the easiest way to find a machine account anywhere in the tree
1011 is to look for hostname$ */
1012 if (asprintf(&expr
, "(samAccountName=%s$)", machine
) == -1) {
1013 DEBUG(1, ("asprintf failed!\n"));
1014 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY
);
1017 status
= ads_search(ads
, res
, expr
, attrs
);
1023 * Initialize a list of mods to be used in a modify request
1024 * @param ctx An initialized TALLOC_CTX
1025 * @return allocated ADS_MODLIST
1027 ADS_MODLIST
ads_init_mods(TALLOC_CTX
*ctx
)
1029 #define ADS_MODLIST_ALLOC_SIZE 10
1032 if ((mods
= TALLOC_ZERO_ARRAY(ctx
, LDAPMod
*, ADS_MODLIST_ALLOC_SIZE
+ 1)))
1033 /* -1 is safety to make sure we don't go over the end.
1034 need to reset it to NULL before doing ldap modify */
1035 mods
[ADS_MODLIST_ALLOC_SIZE
] = (LDAPMod
*) -1;
1037 return (ADS_MODLIST
)mods
;
1042 add an attribute to the list, with values list already constructed
1044 static ADS_STATUS
ads_modlist_add(TALLOC_CTX
*ctx
, ADS_MODLIST
*mods
,
1045 int mod_op
, const char *name
,
1046 const void *_invals
)
1048 const void **invals
= (const void **)_invals
;
1050 LDAPMod
**modlist
= (LDAPMod
**) *mods
;
1051 struct berval
**ber_values
= NULL
;
1052 char **char_values
= NULL
;
1055 mod_op
= LDAP_MOD_DELETE
;
1057 if (mod_op
& LDAP_MOD_BVALUES
)
1058 ber_values
= ads_dup_values(ctx
,
1059 (const struct berval
**)invals
);
1061 char_values
= ads_push_strvals(ctx
,
1062 (const char **) invals
);
1065 /* find the first empty slot */
1066 for (curmod
=0; modlist
[curmod
] && modlist
[curmod
] != (LDAPMod
*) -1;
1068 if (modlist
[curmod
] == (LDAPMod
*) -1) {
1069 if (!(modlist
= TALLOC_REALLOC_ARRAY(ctx
, modlist
, LDAPMod
*,
1070 curmod
+ADS_MODLIST_ALLOC_SIZE
+1)))
1071 return ADS_ERROR(LDAP_NO_MEMORY
);
1072 memset(&modlist
[curmod
], 0,
1073 ADS_MODLIST_ALLOC_SIZE
*sizeof(LDAPMod
*));
1074 modlist
[curmod
+ADS_MODLIST_ALLOC_SIZE
] = (LDAPMod
*) -1;
1075 *mods
= (ADS_MODLIST
)modlist
;
1078 if (!(modlist
[curmod
] = TALLOC_ZERO_P(ctx
, LDAPMod
)))
1079 return ADS_ERROR(LDAP_NO_MEMORY
);
1080 modlist
[curmod
]->mod_type
= talloc_strdup(ctx
, name
);
1081 if (mod_op
& LDAP_MOD_BVALUES
) {
1082 modlist
[curmod
]->mod_bvalues
= ber_values
;
1083 } else if (mod_op
& LDAP_MOD_DELETE
) {
1084 modlist
[curmod
]->mod_values
= NULL
;
1086 modlist
[curmod
]->mod_values
= char_values
;
1089 modlist
[curmod
]->mod_op
= mod_op
;
1090 return ADS_ERROR(LDAP_SUCCESS
);
1094 * Add a single string value to a mod list
1095 * @param ctx An initialized TALLOC_CTX
1096 * @param mods An initialized ADS_MODLIST
1097 * @param name The attribute name to add
1098 * @param val The value to add - NULL means DELETE
1099 * @return ADS STATUS indicating success of add
1101 ADS_STATUS
ads_mod_str(TALLOC_CTX
*ctx
, ADS_MODLIST
*mods
,
1102 const char *name
, const char *val
)
1104 const char *values
[2];
1110 return ads_modlist_add(ctx
, mods
, LDAP_MOD_DELETE
, name
, NULL
);
1111 return ads_modlist_add(ctx
, mods
, LDAP_MOD_REPLACE
, name
, values
);
1115 * Add an array of string values to a mod list
1116 * @param ctx An initialized TALLOC_CTX
1117 * @param mods An initialized ADS_MODLIST
1118 * @param name The attribute name to add
1119 * @param vals The array of string values to add - NULL means DELETE
1120 * @return ADS STATUS indicating success of add
1122 ADS_STATUS
ads_mod_strlist(TALLOC_CTX
*ctx
, ADS_MODLIST
*mods
,
1123 const char *name
, const char **vals
)
1126 return ads_modlist_add(ctx
, mods
, LDAP_MOD_DELETE
, name
, NULL
);
1127 return ads_modlist_add(ctx
, mods
, LDAP_MOD_REPLACE
,
1128 name
, (const void **) vals
);
1133 * Add a single ber-encoded value to a mod list
1134 * @param ctx An initialized TALLOC_CTX
1135 * @param mods An initialized ADS_MODLIST
1136 * @param name The attribute name to add
1137 * @param val The value to add - NULL means DELETE
1138 * @return ADS STATUS indicating success of add
1140 static ADS_STATUS
ads_mod_ber(TALLOC_CTX
*ctx
, ADS_MODLIST
*mods
,
1141 const char *name
, const struct berval
*val
)
1143 const struct berval
*values
[2];
1148 return ads_modlist_add(ctx
, mods
, LDAP_MOD_DELETE
, name
, NULL
);
1149 return ads_modlist_add(ctx
, mods
, LDAP_MOD_REPLACE
|LDAP_MOD_BVALUES
,
1150 name
, (const void **) values
);
1155 * Perform an ldap modify
1156 * @param ads connection to ads server
1157 * @param mod_dn DistinguishedName to modify
1158 * @param mods list of modifications to perform
1159 * @return status of modify
1161 ADS_STATUS
ads_gen_mod(ADS_STRUCT
*ads
, const char *mod_dn
, ADS_MODLIST mods
)
1164 char *utf8_dn
= NULL
;
1166 this control is needed to modify that contains a currently
1167 non-existent attribute (but allowable for the object) to run
1169 LDAPControl PermitModify
= {
1170 CONST_DISCARD(char *, ADS_PERMIT_MODIFY_OID
),
1173 LDAPControl
*controls
[2];
1175 controls
[0] = &PermitModify
;
1178 if (push_utf8_allocate(&utf8_dn
, mod_dn
) == -1) {
1179 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY
);
1182 /* find the end of the list, marked by NULL or -1 */
1183 for(i
=0;(mods
[i
]!=0)&&(mods
[i
]!=(LDAPMod
*) -1);i
++);
1184 /* make sure the end of the list is NULL */
1186 ret
= ldap_modify_ext_s(ads
->ld
, utf8_dn
,
1187 (LDAPMod
**) mods
, controls
, NULL
);
1189 return ADS_ERROR(ret
);
1193 * Perform an ldap add
1194 * @param ads connection to ads server
1195 * @param new_dn DistinguishedName to add
1196 * @param mods list of attributes and values for DN
1197 * @return status of add
1199 ADS_STATUS
ads_gen_add(ADS_STRUCT
*ads
, const char *new_dn
, ADS_MODLIST mods
)
1202 char *utf8_dn
= NULL
;
1204 if (push_utf8_allocate(&utf8_dn
, new_dn
) == -1) {
1205 DEBUG(1, ("ads_gen_add: push_utf8_allocate failed!"));
1206 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY
);
1209 /* find the end of the list, marked by NULL or -1 */
1210 for(i
=0;(mods
[i
]!=0)&&(mods
[i
]!=(LDAPMod
*) -1);i
++);
1211 /* make sure the end of the list is NULL */
1214 ret
= ldap_add_s(ads
->ld
, utf8_dn
, (LDAPMod
**)mods
);
1216 return ADS_ERROR(ret
);
1220 * Delete a DistinguishedName
1221 * @param ads connection to ads server
1222 * @param new_dn DistinguishedName to delete
1223 * @return status of delete
1225 ADS_STATUS
ads_del_dn(ADS_STRUCT
*ads
, char *del_dn
)
1228 char *utf8_dn
= NULL
;
1229 if (push_utf8_allocate(&utf8_dn
, del_dn
) == -1) {
1230 DEBUG(1, ("ads_del_dn: push_utf8_allocate failed!"));
1231 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY
);
1234 ret
= ldap_delete_s(ads
->ld
, utf8_dn
);
1236 return ADS_ERROR(ret
);
1240 * Build an org unit string
1241 * if org unit is Computers or blank then assume a container, otherwise
1242 * assume a / separated list of organisational units.
1243 * jmcd: '\' is now used for escapes so certain chars can be in the ou (e.g. #)
1244 * @param ads connection to ads server
1245 * @param org_unit Organizational unit
1246 * @return org unit string - caller must free
1248 char *ads_ou_string(ADS_STRUCT
*ads
, const char *org_unit
)
1252 if (!org_unit
|| !*org_unit
) {
1254 ret
= ads_default_ou_string(ads
, WELL_KNOWN_GUID_COMPUTERS
);
1256 /* samba4 might not yet respond to a wellknownobject-query */
1257 return ret
? ret
: SMB_STRDUP("cn=Computers");
1260 if (strequal(org_unit
, "Computers")) {
1261 return SMB_STRDUP("cn=Computers");
1264 /* jmcd: removed "\\" from the separation chars, because it is
1265 needed as an escape for chars like '#' which are valid in an
1267 return ads_build_path(org_unit
, "/", "ou=", 1);
1271 * Get a org unit string for a well-known GUID
1272 * @param ads connection to ads server
1273 * @param wknguid Well known GUID
1274 * @return org unit string - caller must free
1276 char *ads_default_ou_string(ADS_STRUCT
*ads
, const char *wknguid
)
1279 LDAPMessage
*res
= NULL
;
1280 char *base
, *wkn_dn
, *ret
= NULL
, **wkn_dn_exp
, **bind_dn_exp
;
1281 const char *attrs
[] = {"distinguishedName", NULL
};
1282 int new_ln
, wkn_ln
, bind_ln
, i
;
1284 if (wknguid
== NULL
) {
1288 if (asprintf(&base
, "<WKGUID=%s,%s>", wknguid
, ads
->config
.bind_path
) == -1) {
1289 DEBUG(1, ("asprintf failed!\n"));
1293 status
= ads_search_dn(ads
, &res
, base
, attrs
);
1294 if (!ADS_ERR_OK(status
)) {
1295 DEBUG(1,("Failed while searching for: %s\n", base
));
1299 if (ads_count_replies(ads
, res
) != 1) {
1303 /* substitute the bind-path from the well-known-guid-search result */
1304 wkn_dn
= ads_get_dn(ads
, res
);
1309 wkn_dn_exp
= ldap_explode_dn(wkn_dn
, 0);
1314 bind_dn_exp
= ldap_explode_dn(ads
->config
.bind_path
, 0);
1319 for (wkn_ln
=0; wkn_dn_exp
[wkn_ln
]; wkn_ln
++)
1321 for (bind_ln
=0; bind_dn_exp
[bind_ln
]; bind_ln
++)
1324 new_ln
= wkn_ln
- bind_ln
;
1326 ret
= SMB_STRDUP(wkn_dn_exp
[0]);
1331 for (i
=1; i
< new_ln
; i
++) {
1334 if (asprintf(&s
, "%s,%s", ret
, wkn_dn_exp
[i
]) == -1) {
1340 ret
= SMB_STRDUP(s
);
1349 ads_msgfree(ads
, res
);
1350 ads_memfree(ads
, wkn_dn
);
1352 ldap_value_free(wkn_dn_exp
);
1355 ldap_value_free(bind_dn_exp
);
1362 * Adds (appends) an item to an attribute array, rather then
1363 * replacing the whole list
1364 * @param ctx An initialized TALLOC_CTX
1365 * @param mods An initialized ADS_MODLIST
1366 * @param name name of the ldap attribute to append to
1367 * @param vals an array of values to add
1368 * @return status of addition
1371 ADS_STATUS
ads_add_strlist(TALLOC_CTX
*ctx
, ADS_MODLIST
*mods
,
1372 const char *name
, const char **vals
)
1374 return ads_modlist_add(ctx
, mods
, LDAP_MOD_ADD
, name
,
1375 (const void *) vals
);
1379 * Determines the computer account's current KVNO via an LDAP lookup
1380 * @param ads An initialized ADS_STRUCT
1381 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1382 * @return the kvno for the computer account, or -1 in case of a failure.
1385 uint32
ads_get_kvno(ADS_STRUCT
*ads
, const char *machine_name
)
1387 LDAPMessage
*res
= NULL
;
1388 uint32 kvno
= (uint32
)-1; /* -1 indicates a failure */
1390 const char *attrs
[] = {"msDS-KeyVersionNumber", NULL
};
1391 char *dn_string
= NULL
;
1392 ADS_STATUS ret
= ADS_ERROR(LDAP_SUCCESS
);
1394 DEBUG(5,("ads_get_kvno: Searching for host %s\n", machine_name
));
1395 if (asprintf(&filter
, "(samAccountName=%s$)", machine_name
) == -1) {
1398 ret
= ads_search(ads
, &res
, filter
, attrs
);
1400 if (!ADS_ERR_OK(ret
) && ads_count_replies(ads
, res
)) {
1401 DEBUG(1,("ads_get_kvno: Computer Account For %s not found.\n", machine_name
));
1402 ads_msgfree(ads
, res
);
1406 dn_string
= ads_get_dn(ads
, res
);
1408 DEBUG(0,("ads_get_kvno: out of memory.\n"));
1409 ads_msgfree(ads
, res
);
1412 DEBUG(5,("ads_get_kvno: Using: %s\n", dn_string
));
1413 ads_memfree(ads
, dn_string
);
1415 /* ---------------------------------------------------------
1416 * 0 is returned as a default KVNO from this point on...
1417 * This is done because Windows 2000 does not support key
1418 * version numbers. Chances are that a failure in the next
1419 * step is simply due to Windows 2000 being used for a
1420 * domain controller. */
1423 if (!ads_pull_uint32(ads
, res
, "msDS-KeyVersionNumber", &kvno
)) {
1424 DEBUG(3,("ads_get_kvno: Error Determining KVNO!\n"));
1425 DEBUG(3,("ads_get_kvno: Windows 2000 does not support KVNO's, so this may be normal.\n"));
1426 ads_msgfree(ads
, res
);
1431 DEBUG(5,("ads_get_kvno: Looked Up KVNO of: %d\n", kvno
));
1432 ads_msgfree(ads
, res
);
1437 * This clears out all registered spn's for a given hostname
1438 * @param ads An initilaized ADS_STRUCT
1439 * @param machine_name the NetBIOS name of the computer.
1440 * @return 0 upon success, non-zero otherwise.
1443 ADS_STATUS
ads_clear_service_principal_names(ADS_STRUCT
*ads
, const char *machine_name
)
1446 LDAPMessage
*res
= NULL
;
1448 const char *servicePrincipalName
[1] = {NULL
};
1449 ADS_STATUS ret
= ADS_ERROR(LDAP_SUCCESS
);
1450 char *dn_string
= NULL
;
1452 ret
= ads_find_machine_acct(ads
, &res
, machine_name
);
1453 if (!ADS_ERR_OK(ret
) || ads_count_replies(ads
, res
) != 1) {
1454 DEBUG(5,("ads_clear_service_principal_names: WARNING: Host Account for %s not found... skipping operation.\n", machine_name
));
1455 DEBUG(5,("ads_clear_service_principal_names: WARNING: Service Principals for %s have NOT been cleared.\n", machine_name
));
1456 ads_msgfree(ads
, res
);
1457 return ADS_ERROR(LDAP_NO_SUCH_OBJECT
);
1460 DEBUG(5,("ads_clear_service_principal_names: Host account for %s found\n", machine_name
));
1461 ctx
= talloc_init("ads_clear_service_principal_names");
1463 ads_msgfree(ads
, res
);
1464 return ADS_ERROR(LDAP_NO_MEMORY
);
1467 if (!(mods
= ads_init_mods(ctx
))) {
1468 talloc_destroy(ctx
);
1469 ads_msgfree(ads
, res
);
1470 return ADS_ERROR(LDAP_NO_MEMORY
);
1472 ret
= ads_mod_strlist(ctx
, &mods
, "servicePrincipalName", servicePrincipalName
);
1473 if (!ADS_ERR_OK(ret
)) {
1474 DEBUG(1,("ads_clear_service_principal_names: Error creating strlist.\n"));
1475 ads_msgfree(ads
, res
);
1476 talloc_destroy(ctx
);
1479 dn_string
= ads_get_dn(ads
, res
);
1481 talloc_destroy(ctx
);
1482 ads_msgfree(ads
, res
);
1483 return ADS_ERROR(LDAP_NO_MEMORY
);
1485 ret
= ads_gen_mod(ads
, dn_string
, mods
);
1486 ads_memfree(ads
,dn_string
);
1487 if (!ADS_ERR_OK(ret
)) {
1488 DEBUG(1,("ads_clear_service_principal_names: Error: Updating Service Principals for machine %s in LDAP\n",
1490 ads_msgfree(ads
, res
);
1491 talloc_destroy(ctx
);
1495 ads_msgfree(ads
, res
);
1496 talloc_destroy(ctx
);
1501 * This adds a service principal name to an existing computer account
1502 * (found by hostname) in AD.
1503 * @param ads An initialized ADS_STRUCT
1504 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1505 * @param my_fqdn The fully qualified DNS name of the machine
1506 * @param spn A string of the service principal to add, i.e. 'host'
1507 * @return 0 upon sucess, or non-zero if a failure occurs
1510 ADS_STATUS
ads_add_service_principal_name(ADS_STRUCT
*ads
, const char *machine_name
,
1511 const char *my_fqdn
, const char *spn
)
1515 LDAPMessage
*res
= NULL
;
1518 char *dn_string
= NULL
;
1519 const char *servicePrincipalName
[3] = {NULL
, NULL
, NULL
};
1521 ret
= ads_find_machine_acct(ads
, &res
, machine_name
);
1522 if (!ADS_ERR_OK(ret
) || ads_count_replies(ads
, res
) != 1) {
1523 DEBUG(1,("ads_add_service_principal_name: WARNING: Host Account for %s not found... skipping operation.\n",
1525 DEBUG(1,("ads_add_service_principal_name: WARNING: Service Principal '%s/%s@%s' has NOT been added.\n",
1526 spn
, machine_name
, ads
->config
.realm
));
1527 ads_msgfree(ads
, res
);
1528 return ADS_ERROR(LDAP_NO_SUCH_OBJECT
);
1531 DEBUG(1,("ads_add_service_principal_name: Host account for %s found\n", machine_name
));
1532 if (!(ctx
= talloc_init("ads_add_service_principal_name"))) {
1533 ads_msgfree(ads
, res
);
1534 return ADS_ERROR(LDAP_NO_MEMORY
);
1537 /* add short name spn */
1539 if ( (psp1
= talloc_asprintf(ctx
, "%s/%s", spn
, machine_name
)) == NULL
) {
1540 talloc_destroy(ctx
);
1541 ads_msgfree(ads
, res
);
1542 return ADS_ERROR(LDAP_NO_MEMORY
);
1545 strlower_m(&psp1
[strlen(spn
)]);
1546 servicePrincipalName
[0] = psp1
;
1548 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n",
1549 psp1
, machine_name
));
1552 /* add fully qualified spn */
1554 if ( (psp2
= talloc_asprintf(ctx
, "%s/%s", spn
, my_fqdn
)) == NULL
) {
1555 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
1559 strlower_m(&psp2
[strlen(spn
)]);
1560 servicePrincipalName
[1] = psp2
;
1562 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n",
1563 psp2
, machine_name
));
1565 if ( (mods
= ads_init_mods(ctx
)) == NULL
) {
1566 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
1570 ret
= ads_add_strlist(ctx
, &mods
, "servicePrincipalName", servicePrincipalName
);
1571 if (!ADS_ERR_OK(ret
)) {
1572 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
1576 if ( (dn_string
= ads_get_dn(ads
, res
)) == NULL
) {
1577 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
1581 ret
= ads_gen_mod(ads
, dn_string
, mods
);
1582 ads_memfree(ads
,dn_string
);
1583 if (!ADS_ERR_OK(ret
)) {
1584 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
1590 ads_msgfree(ads
, res
);
1595 * adds a machine account to the ADS server
1596 * @param ads An intialized ADS_STRUCT
1597 * @param machine_name - the NetBIOS machine name of this account.
1598 * @param account_type A number indicating the type of account to create
1599 * @param org_unit The LDAP path in which to place this account
1600 * @return 0 upon success, or non-zero otherwise
1603 ADS_STATUS
ads_create_machine_acct(ADS_STRUCT
*ads
, const char *machine_name
,
1604 const char *org_unit
)
1607 char *samAccountName
, *controlstr
;
1611 const char *objectClass
[] = {"top", "person", "organizationalPerson",
1612 "user", "computer", NULL
};
1613 LDAPMessage
*res
= NULL
;
1614 uint32 acct_control
= ( UF_WORKSTATION_TRUST_ACCOUNT
|\
1615 UF_DONT_EXPIRE_PASSWD
|\
1616 UF_ACCOUNTDISABLE
);
1618 if (!(ctx
= talloc_init("ads_add_machine_acct")))
1619 return ADS_ERROR(LDAP_NO_MEMORY
);
1621 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
1623 new_dn
= talloc_asprintf(ctx
, "cn=%s,%s", machine_name
, org_unit
);
1624 samAccountName
= talloc_asprintf(ctx
, "%s$", machine_name
);
1626 if ( !new_dn
|| !samAccountName
) {
1630 #ifndef ENCTYPE_ARCFOUR_HMAC
1631 acct_control
|= UF_USE_DES_KEY_ONLY
;
1634 if (!(controlstr
= talloc_asprintf(ctx
, "%u", acct_control
))) {
1638 if (!(mods
= ads_init_mods(ctx
))) {
1642 ads_mod_str(ctx
, &mods
, "cn", machine_name
);
1643 ads_mod_str(ctx
, &mods
, "sAMAccountName", samAccountName
);
1644 ads_mod_strlist(ctx
, &mods
, "objectClass", objectClass
);
1645 ads_mod_str(ctx
, &mods
, "userAccountControl", controlstr
);
1647 ret
= ads_gen_add(ads
, new_dn
, mods
);
1650 ads_msgfree(ads
, res
);
1651 talloc_destroy(ctx
);
1657 dump a binary result from ldap
1659 static void dump_binary(const char *field
, struct berval
**values
)
1662 for (i
=0; values
[i
]; i
++) {
1663 printf("%s: ", field
);
1664 for (j
=0; j
<values
[i
]->bv_len
; j
++) {
1665 printf("%02X", (unsigned char)values
[i
]->bv_val
[j
]);
1671 static void dump_guid(const char *field
, struct berval
**values
)
1675 for (i
=0; values
[i
]; i
++) {
1676 memcpy(guid
.info
, values
[i
]->bv_val
, sizeof(guid
.info
));
1677 printf("%s: %s\n", field
,
1678 smb_uuid_string_static(smb_uuid_unpack_static(guid
)));
1683 dump a sid result from ldap
1685 static void dump_sid(const char *field
, struct berval
**values
)
1688 for (i
=0; values
[i
]; i
++) {
1690 sid_parse(values
[i
]->bv_val
, values
[i
]->bv_len
, &sid
);
1691 printf("%s: %s\n", field
, sid_string_static(&sid
));
1696 dump ntSecurityDescriptor
1698 static void dump_sd(const char *filed
, struct berval
**values
)
1703 TALLOC_CTX
*ctx
= 0;
1705 if (!(ctx
= talloc_init("sec_io_desc")))
1709 prs_init(&ps
, values
[0]->bv_len
, ctx
, UNMARSHALL
);
1710 prs_copy_data_in(&ps
, values
[0]->bv_val
, values
[0]->bv_len
);
1711 prs_set_offset(&ps
,0);
1714 if (!sec_io_desc("sd", &psd
, &ps
, 1)) {
1716 talloc_destroy(ctx
);
1719 if (psd
) ads_disp_sd(psd
);
1722 talloc_destroy(ctx
);
1726 dump a string result from ldap
1728 static void dump_string(const char *field
, char **values
)
1731 for (i
=0; values
[i
]; i
++) {
1732 printf("%s: %s\n", field
, values
[i
]);
1737 dump a field from LDAP on stdout
1741 static BOOL
ads_dump_field(char *field
, void **values
, void *data_area
)
1746 void (*handler
)(const char *, struct berval
**);
1748 {"objectGUID", False
, dump_guid
},
1749 {"netbootGUID", False
, dump_guid
},
1750 {"nTSecurityDescriptor", False
, dump_sd
},
1751 {"dnsRecord", False
, dump_binary
},
1752 {"objectSid", False
, dump_sid
},
1753 {"tokenGroups", False
, dump_sid
},
1754 {"tokenGroupsNoGCAcceptable", False
, dump_sid
},
1755 {"tokengroupsGlobalandUniversal", False
, dump_sid
},
1756 {"mS-DS-CreatorSID", False
, dump_sid
},
1761 if (!field
) { /* must be end of an entry */
1766 for (i
=0; handlers
[i
].name
; i
++) {
1767 if (StrCaseCmp(handlers
[i
].name
, field
) == 0) {
1768 if (!values
) /* first time, indicate string or not */
1769 return handlers
[i
].string
;
1770 handlers
[i
].handler(field
, (struct berval
**) values
);
1774 if (!handlers
[i
].name
) {
1775 if (!values
) /* first time, indicate string conversion */
1777 dump_string(field
, (char **)values
);
1783 * Dump a result from LDAP on stdout
1784 * used for debugging
1785 * @param ads connection to ads server
1786 * @param res Results to dump
1789 void ads_dump(ADS_STRUCT
*ads
, LDAPMessage
*res
)
1791 ads_process_results(ads
, res
, ads_dump_field
, NULL
);
1795 * Walk through results, calling a function for each entry found.
1796 * The function receives a field name, a berval * array of values,
1797 * and a data area passed through from the start. The function is
1798 * called once with null for field and values at the end of each
1800 * @param ads connection to ads server
1801 * @param res Results to process
1802 * @param fn Function for processing each result
1803 * @param data_area user-defined area to pass to function
1805 void ads_process_results(ADS_STRUCT
*ads
, LDAPMessage
*res
,
1806 BOOL(*fn
)(char *, void **, void *),
1812 if (!(ctx
= talloc_init("ads_process_results")))
1815 for (msg
= ads_first_entry(ads
, res
); msg
;
1816 msg
= ads_next_entry(ads
, msg
)) {
1820 for (utf8_field
=ldap_first_attribute(ads
->ld
,
1821 (LDAPMessage
*)msg
,&b
);
1823 utf8_field
=ldap_next_attribute(ads
->ld
,
1824 (LDAPMessage
*)msg
,b
)) {
1825 struct berval
**ber_vals
;
1826 char **str_vals
, **utf8_vals
;
1830 pull_utf8_talloc(ctx
, &field
, utf8_field
);
1831 string
= fn(field
, NULL
, data_area
);
1834 utf8_vals
= ldap_get_values(ads
->ld
,
1835 (LDAPMessage
*)msg
, field
);
1836 str_vals
= ads_pull_strvals(ctx
,
1837 (const char **) utf8_vals
);
1838 fn(field
, (void **) str_vals
, data_area
);
1839 ldap_value_free(utf8_vals
);
1841 ber_vals
= ldap_get_values_len(ads
->ld
,
1842 (LDAPMessage
*)msg
, field
);
1843 fn(field
, (void **) ber_vals
, data_area
);
1845 ldap_value_free_len(ber_vals
);
1847 ldap_memfree(utf8_field
);
1850 talloc_free_children(ctx
);
1851 fn(NULL
, NULL
, data_area
); /* completed an entry */
1854 talloc_destroy(ctx
);
1858 * count how many replies are in a LDAPMessage
1859 * @param ads connection to ads server
1860 * @param res Results to count
1861 * @return number of replies
1863 int ads_count_replies(ADS_STRUCT
*ads
, void *res
)
1865 return ldap_count_entries(ads
->ld
, (LDAPMessage
*)res
);
1869 * pull the first entry from a ADS result
1870 * @param ads connection to ads server
1871 * @param res Results of search
1872 * @return first entry from result
1874 LDAPMessage
*ads_first_entry(ADS_STRUCT
*ads
, LDAPMessage
*res
)
1876 return ldap_first_entry(ads
->ld
, res
);
1880 * pull the next entry from a ADS result
1881 * @param ads connection to ads server
1882 * @param res Results of search
1883 * @return next entry from result
1885 LDAPMessage
*ads_next_entry(ADS_STRUCT
*ads
, LDAPMessage
*res
)
1887 return ldap_next_entry(ads
->ld
, res
);
1891 * pull a single string from a ADS result
1892 * @param ads connection to ads server
1893 * @param mem_ctx TALLOC_CTX to use for allocating result string
1894 * @param msg Results of search
1895 * @param field Attribute to retrieve
1896 * @return Result string in talloc context
1898 char *ads_pull_string(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
, LDAPMessage
*msg
,
1906 values
= ldap_get_values(ads
->ld
, msg
, field
);
1911 rc
= pull_utf8_talloc(mem_ctx
, &ux_string
,
1913 if (rc
!= (size_t)-1)
1917 ldap_value_free(values
);
1922 * pull an array of strings from a ADS result
1923 * @param ads connection to ads server
1924 * @param mem_ctx TALLOC_CTX to use for allocating result string
1925 * @param msg Results of search
1926 * @param field Attribute to retrieve
1927 * @return Result strings in talloc context
1929 char **ads_pull_strings(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
,
1930 LDAPMessage
*msg
, const char *field
,
1937 values
= ldap_get_values(ads
->ld
, msg
, field
);
1941 *num_values
= ldap_count_values(values
);
1943 ret
= TALLOC_ARRAY(mem_ctx
, char *, *num_values
+ 1);
1945 ldap_value_free(values
);
1949 for (i
=0;i
<*num_values
;i
++) {
1950 if (pull_utf8_talloc(mem_ctx
, &ret
[i
], values
[i
]) == -1) {
1951 ldap_value_free(values
);
1957 ldap_value_free(values
);
1962 * pull an array of strings from a ADS result
1963 * (handle large multivalue attributes with range retrieval)
1964 * @param ads connection to ads server
1965 * @param mem_ctx TALLOC_CTX to use for allocating result string
1966 * @param msg Results of search
1967 * @param field Attribute to retrieve
1968 * @param current_strings strings returned by a previous call to this function
1969 * @param next_attribute The next query should ask for this attribute
1970 * @param num_values How many values did we get this time?
1971 * @param more_values Are there more values to get?
1972 * @return Result strings in talloc context
1974 char **ads_pull_strings_range(ADS_STRUCT
*ads
,
1975 TALLOC_CTX
*mem_ctx
,
1976 LDAPMessage
*msg
, const char *field
,
1977 char **current_strings
,
1978 const char **next_attribute
,
1979 size_t *num_strings
,
1983 char *expected_range_attrib
, *range_attr
;
1984 BerElement
*ptr
= NULL
;
1987 size_t num_new_strings
;
1988 unsigned long int range_start
;
1989 unsigned long int range_end
;
1991 /* we might have been given the whole lot anyway */
1992 if ((strings
= ads_pull_strings(ads
, mem_ctx
, msg
, field
, num_strings
))) {
1993 *more_strings
= False
;
1997 expected_range_attrib
= talloc_asprintf(mem_ctx
, "%s;Range=", field
);
1999 /* look for Range result */
2000 for (attr
= ldap_first_attribute(ads
->ld
, (LDAPMessage
*)msg
, &ptr
);
2002 attr
= ldap_next_attribute(ads
->ld
, (LDAPMessage
*)msg
, ptr
)) {
2003 /* we ignore the fact that this is utf8, as all attributes are ascii... */
2004 if (strnequal(attr
, expected_range_attrib
, strlen(expected_range_attrib
))) {
2012 /* nothing here - this field is just empty */
2013 *more_strings
= False
;
2017 if (sscanf(&range_attr
[strlen(expected_range_attrib
)], "%lu-%lu",
2018 &range_start
, &range_end
) == 2) {
2019 *more_strings
= True
;
2021 if (sscanf(&range_attr
[strlen(expected_range_attrib
)], "%lu-*",
2022 &range_start
) == 1) {
2023 *more_strings
= False
;
2025 DEBUG(1, ("ads_pull_strings_range: Cannot parse Range attriubte (%s)\n",
2027 ldap_memfree(range_attr
);
2028 *more_strings
= False
;
2033 if ((*num_strings
) != range_start
) {
2034 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) doesn't start at %u, but at %lu"
2035 " - aborting range retreival\n",
2036 range_attr
, (unsigned int)(*num_strings
) + 1, range_start
));
2037 ldap_memfree(range_attr
);
2038 *more_strings
= False
;
2042 new_strings
= ads_pull_strings(ads
, mem_ctx
, msg
, range_attr
, &num_new_strings
);
2044 if (*more_strings
&& ((*num_strings
+ num_new_strings
) != (range_end
+ 1))) {
2045 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) tells us we have %lu "
2046 "strings in this bunch, but we only got %lu - aborting range retreival\n",
2047 range_attr
, (unsigned long int)range_end
- range_start
+ 1,
2048 (unsigned long int)num_new_strings
));
2049 ldap_memfree(range_attr
);
2050 *more_strings
= False
;
2054 strings
= TALLOC_REALLOC_ARRAY(mem_ctx
, current_strings
, char *,
2055 *num_strings
+ num_new_strings
);
2057 if (strings
== NULL
) {
2058 ldap_memfree(range_attr
);
2059 *more_strings
= False
;
2063 if (new_strings
&& num_new_strings
) {
2064 memcpy(&strings
[*num_strings
], new_strings
,
2065 sizeof(*new_strings
) * num_new_strings
);
2068 (*num_strings
) += num_new_strings
;
2070 if (*more_strings
) {
2071 *next_attribute
= talloc_asprintf(mem_ctx
,
2076 if (!*next_attribute
) {
2077 DEBUG(1, ("talloc_asprintf for next attribute failed!\n"));
2078 ldap_memfree(range_attr
);
2079 *more_strings
= False
;
2084 ldap_memfree(range_attr
);
2090 * pull a single uint32 from a ADS result
2091 * @param ads connection to ads server
2092 * @param msg Results of search
2093 * @param field Attribute to retrieve
2094 * @param v Pointer to int to store result
2095 * @return boolean inidicating success
2097 BOOL
ads_pull_uint32(ADS_STRUCT
*ads
, LDAPMessage
*msg
, const char *field
,
2102 values
= ldap_get_values(ads
->ld
, msg
, field
);
2106 ldap_value_free(values
);
2110 *v
= atoi(values
[0]);
2111 ldap_value_free(values
);
2116 * pull a single objectGUID from an ADS result
2117 * @param ads connection to ADS server
2118 * @param msg results of search
2119 * @param guid 37-byte area to receive text guid
2120 * @return boolean indicating success
2122 BOOL
ads_pull_guid(ADS_STRUCT
*ads
, LDAPMessage
*msg
, struct GUID
*guid
)
2125 UUID_FLAT flat_guid
;
2127 values
= ldap_get_values(ads
->ld
, msg
, "objectGUID");
2132 memcpy(&flat_guid
.info
, values
[0], sizeof(UUID_FLAT
));
2133 smb_uuid_unpack(flat_guid
, guid
);
2134 ldap_value_free(values
);
2137 ldap_value_free(values
);
2144 * pull a single DOM_SID from a ADS result
2145 * @param ads connection to ads server
2146 * @param msg Results of search
2147 * @param field Attribute to retrieve
2148 * @param sid Pointer to sid to store result
2149 * @return boolean inidicating success
2151 BOOL
ads_pull_sid(ADS_STRUCT
*ads
, LDAPMessage
*msg
, const char *field
,
2154 struct berval
**values
;
2157 values
= ldap_get_values_len(ads
->ld
, msg
, field
);
2163 ret
= sid_parse(values
[0]->bv_val
, values
[0]->bv_len
, sid
);
2165 ldap_value_free_len(values
);
2170 * pull an array of DOM_SIDs from a ADS result
2171 * @param ads connection to ads server
2172 * @param mem_ctx TALLOC_CTX for allocating sid array
2173 * @param msg Results of search
2174 * @param field Attribute to retrieve
2175 * @param sids pointer to sid array to allocate
2176 * @return the count of SIDs pulled
2178 int ads_pull_sids(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
,
2179 LDAPMessage
*msg
, const char *field
, DOM_SID
**sids
)
2181 struct berval
**values
;
2185 values
= ldap_get_values_len(ads
->ld
, msg
, field
);
2190 for (i
=0; values
[i
]; i
++)
2193 (*sids
) = TALLOC_ARRAY(mem_ctx
, DOM_SID
, i
);
2195 ldap_value_free_len(values
);
2200 for (i
=0; values
[i
]; i
++) {
2201 ret
= sid_parse(values
[i
]->bv_val
, values
[i
]->bv_len
, &(*sids
)[count
]);
2204 DEBUG(10, ("pulling SID: %s\n", sid_to_string(sid
, &(*sids
)[count
])));
2209 ldap_value_free_len(values
);
2214 * pull a SEC_DESC from a ADS result
2215 * @param ads connection to ads server
2216 * @param mem_ctx TALLOC_CTX for allocating sid array
2217 * @param msg Results of search
2218 * @param field Attribute to retrieve
2219 * @param sd Pointer to *SEC_DESC to store result (talloc()ed)
2220 * @return boolean inidicating success
2222 BOOL
ads_pull_sd(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
,
2223 LDAPMessage
*msg
, const char *field
, SEC_DESC
**sd
)
2225 struct berval
**values
;
2229 values
= ldap_get_values_len(ads
->ld
, msg
, field
);
2231 if (!values
) return False
;
2234 prs_init(&ps
, values
[0]->bv_len
, mem_ctx
, UNMARSHALL
);
2235 prs_copy_data_in(&ps
, values
[0]->bv_val
, values
[0]->bv_len
);
2236 prs_set_offset(&ps
,0);
2238 ret
= sec_io_desc("sd", sd
, &ps
, 1);
2241 ldap_value_free_len(values
);
2246 * in order to support usernames longer than 21 characters we need to
2247 * use both the sAMAccountName and the userPrincipalName attributes
2248 * It seems that not all users have the userPrincipalName attribute set
2250 * @param ads connection to ads server
2251 * @param mem_ctx TALLOC_CTX for allocating sid array
2252 * @param msg Results of search
2253 * @return the username
2255 char *ads_pull_username(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
,
2261 /* lookup_name() only works on the sAMAccountName to
2262 returning the username portion of userPrincipalName
2263 breaks winbindd_getpwnam() */
2265 ret
= ads_pull_string(ads
, mem_ctx
, msg
, "userPrincipalName");
2266 if (ret
&& (p
= strchr_m(ret
, '@'))) {
2271 return ads_pull_string(ads
, mem_ctx
, msg
, "sAMAccountName");
2276 * find the update serial number - this is the core of the ldap cache
2277 * @param ads connection to ads server
2278 * @param ads connection to ADS server
2279 * @param usn Pointer to retrieved update serial number
2280 * @return status of search
2282 ADS_STATUS
ads_USN(ADS_STRUCT
*ads
, uint32
*usn
)
2284 const char *attrs
[] = {"highestCommittedUSN", NULL
};
2288 status
= ads_do_search_retry(ads
, "", LDAP_SCOPE_BASE
, "(objectclass=*)", attrs
, &res
);
2289 if (!ADS_ERR_OK(status
))
2292 if (ads_count_replies(ads
, res
) != 1) {
2293 ads_msgfree(ads
, res
);
2294 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED
);
2297 if (!ads_pull_uint32(ads
, res
, "highestCommittedUSN", usn
)) {
2298 ads_msgfree(ads
, res
);
2299 return ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE
);
2302 ads_msgfree(ads
, res
);
2306 /* parse a ADS timestring - typical string is
2307 '20020917091222.0Z0' which means 09:12.22 17th September
2309 static time_t ads_parse_time(const char *str
)
2315 if (sscanf(str
, "%4d%2d%2d%2d%2d%2d",
2316 &tm
.tm_year
, &tm
.tm_mon
, &tm
.tm_mday
,
2317 &tm
.tm_hour
, &tm
.tm_min
, &tm
.tm_sec
) != 6) {
2326 /********************************************************************
2327 ********************************************************************/
2329 ADS_STATUS
ads_current_time(ADS_STRUCT
*ads
)
2331 const char *attrs
[] = {"currentTime", NULL
};
2336 ADS_STRUCT
*ads_s
= ads
;
2338 if (!(ctx
= talloc_init("ads_current_time"))) {
2339 return ADS_ERROR(LDAP_NO_MEMORY
);
2342 /* establish a new ldap tcp session if necessary */
2345 if ( (ads_s
= ads_init( ads
->server
.realm
, ads
->server
.workgroup
,
2346 ads
->server
.ldap_server
)) == NULL
)
2350 ads_s
->auth
.flags
= ADS_AUTH_ANON_BIND
;
2351 status
= ads_connect( ads_s
);
2352 if ( !ADS_ERR_OK(status
))
2356 status
= ads_do_search(ads_s
, "", LDAP_SCOPE_BASE
, "(objectclass=*)", attrs
, &res
);
2357 if (!ADS_ERR_OK(status
)) {
2361 timestr
= ads_pull_string(ads_s
, ctx
, res
, "currentTime");
2363 ads_msgfree(ads_s
, res
);
2364 status
= ADS_ERROR(LDAP_NO_RESULTS_RETURNED
);
2368 /* but save the time and offset in the original ADS_STRUCT */
2370 ads
->config
.current_time
= ads_parse_time(timestr
);
2372 if (ads
->config
.current_time
!= 0) {
2373 ads
->auth
.time_offset
= ads
->config
.current_time
- time(NULL
);
2374 DEBUG(4,("time offset is %d seconds\n", ads
->auth
.time_offset
));
2377 ads_msgfree(ads
, res
);
2379 status
= ADS_SUCCESS
;
2382 /* free any temporary ads connections */
2383 if ( ads_s
!= ads
) {
2384 ads_destroy( &ads_s
);
2386 talloc_destroy(ctx
);
2391 /********************************************************************
2392 ********************************************************************/
2394 ADS_STATUS
ads_domain_func_level(ADS_STRUCT
*ads
, uint32
*val
)
2396 const char *attrs
[] = {"domainFunctionality", NULL
};
2399 ADS_STRUCT
*ads_s
= ads
;
2401 *val
= DS_DOMAIN_FUNCTION_2000
;
2403 /* establish a new ldap tcp session if necessary */
2406 if ( (ads_s
= ads_init( ads
->server
.realm
, ads
->server
.workgroup
,
2407 ads
->server
.ldap_server
)) == NULL
)
2411 ads_s
->auth
.flags
= ADS_AUTH_ANON_BIND
;
2412 status
= ads_connect( ads_s
);
2413 if ( !ADS_ERR_OK(status
))
2417 /* If the attribute does not exist assume it is a Windows 2000
2418 functional domain */
2420 status
= ads_do_search(ads_s
, "", LDAP_SCOPE_BASE
, "(objectclass=*)", attrs
, &res
);
2421 if (!ADS_ERR_OK(status
)) {
2422 if ( status
.err
.rc
== LDAP_NO_SUCH_ATTRIBUTE
) {
2423 status
= ADS_SUCCESS
;
2428 if ( !ads_pull_uint32(ads_s
, res
, "domainFunctionality", val
) ) {
2429 DEBUG(5,("ads_domain_func_level: Failed to pull the domainFunctionality attribute.\n"));
2431 DEBUG(3,("ads_domain_func_level: %d\n", *val
));
2434 ads_msgfree(ads
, res
);
2437 /* free any temporary ads connections */
2438 if ( ads_s
!= ads
) {
2439 ads_destroy( &ads_s
);
2446 * find the domain sid for our domain
2447 * @param ads connection to ads server
2448 * @param sid Pointer to domain sid
2449 * @return status of search
2451 ADS_STATUS
ads_domain_sid(ADS_STRUCT
*ads
, DOM_SID
*sid
)
2453 const char *attrs
[] = {"objectSid", NULL
};
2457 rc
= ads_do_search_retry(ads
, ads
->config
.bind_path
, LDAP_SCOPE_BASE
, "(objectclass=*)",
2459 if (!ADS_ERR_OK(rc
)) return rc
;
2460 if (!ads_pull_sid(ads
, res
, "objectSid", sid
)) {
2461 ads_msgfree(ads
, res
);
2462 return ADS_ERROR_SYSTEM(ENOENT
);
2464 ads_msgfree(ads
, res
);
2470 * find our site name
2471 * @param ads connection to ads server
2472 * @param mem_ctx Pointer to talloc context
2473 * @param site_name Pointer to the sitename
2474 * @return status of search
2476 ADS_STATUS
ads_site_dn(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
, const char **site_name
)
2480 const char *dn
, *service_name
;
2481 const char *attrs
[] = { "dsServiceName", NULL
};
2483 status
= ads_do_search(ads
, "", LDAP_SCOPE_BASE
, "(objectclass=*)", attrs
, &res
);
2484 if (!ADS_ERR_OK(status
)) {
2488 service_name
= ads_pull_string(ads
, mem_ctx
, res
, "dsServiceName");
2489 if (service_name
== NULL
) {
2490 ads_msgfree(ads
, res
);
2491 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED
);
2494 ads_msgfree(ads
, res
);
2496 /* go up three levels */
2497 dn
= ads_parent_dn(ads_parent_dn(ads_parent_dn(service_name
)));
2499 return ADS_ERROR(LDAP_NO_MEMORY
);
2502 *site_name
= talloc_strdup(mem_ctx
, dn
);
2503 if (*site_name
== NULL
) {
2504 return ADS_ERROR(LDAP_NO_MEMORY
);
2509 dsServiceName: CN=NTDS Settings,CN=W2K3DC,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,DC=ber,DC=suse,DC=de
2514 * find the site dn where a machine resides
2515 * @param ads connection to ads server
2516 * @param mem_ctx Pointer to talloc context
2517 * @param computer_name name of the machine
2518 * @param site_name Pointer to the sitename
2519 * @return status of search
2521 ADS_STATUS
ads_site_dn_for_machine(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
, const char *computer_name
, const char **site_dn
)
2525 const char *parent
, *config_context
, *filter
;
2526 const char *attrs
[] = { "configurationNamingContext", NULL
};
2529 /* shortcut a query */
2530 if (strequal(computer_name
, ads
->config
.ldap_server_name
)) {
2531 return ads_site_dn(ads
, mem_ctx
, site_dn
);
2534 status
= ads_do_search(ads
, "", LDAP_SCOPE_BASE
, "(objectclass=*)", attrs
, &res
);
2535 if (!ADS_ERR_OK(status
)) {
2539 config_context
= ads_pull_string(ads
, mem_ctx
, res
, "configurationNamingContext");
2540 if (config_context
== NULL
) {
2541 ads_msgfree(ads
, res
);
2542 return ADS_ERROR(LDAP_NO_MEMORY
);
2545 filter
= talloc_asprintf(mem_ctx
, "(cn=%s)", computer_name
);
2546 if (filter
== NULL
) {
2547 ads_msgfree(ads
, res
);
2548 return ADS_ERROR(LDAP_NO_MEMORY
);
2551 ads_msgfree(ads
, res
);
2553 status
= ads_do_search(ads
, config_context
, LDAP_SCOPE_SUBTREE
, filter
, NULL
, &res
);
2554 if (!ADS_ERR_OK(status
)) {
2558 if (ads_count_replies(ads
, res
) != 1) {
2559 ads_msgfree(ads
, res
);
2560 return ADS_ERROR(LDAP_NO_SUCH_OBJECT
);
2563 dn
= ads_get_dn(ads
, res
);
2565 ads_msgfree(ads
, res
);
2566 return ADS_ERROR(LDAP_NO_MEMORY
);
2569 /* go up three levels */
2570 parent
= ads_parent_dn(ads_parent_dn(ads_parent_dn(dn
)));
2571 if (parent
== NULL
) {
2572 ads_msgfree(ads
, res
);
2573 ads_memfree(ads
, dn
);
2574 return ADS_ERROR(LDAP_NO_MEMORY
);
2577 *site_dn
= talloc_strdup(mem_ctx
, parent
);
2578 if (*site_dn
== NULL
) {
2579 ads_msgfree(ads
, res
);
2580 ads_memfree(ads
, dn
);
2581 ADS_ERROR(LDAP_NO_MEMORY
);
2584 ads_memfree(ads
, dn
);
2585 ads_msgfree(ads
, res
);
2591 * get the upn suffixes for a domain
2592 * @param ads connection to ads server
2593 * @param mem_ctx Pointer to talloc context
2594 * @param suffixes Pointer to an array of suffixes
2595 * @param site_name Pointer to the number of suffixes
2596 * @return status of search
2598 ADS_STATUS
ads_upn_suffixes(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
, char **suffixes
, size_t *num_suffixes
)
2602 const char *config_context
, *base
;
2603 const char *attrs
[] = { "configurationNamingContext", NULL
};
2604 const char *attrs2
[] = { "uPNSuffixes", NULL
};
2606 status
= ads_do_search(ads
, "", LDAP_SCOPE_BASE
, "(objectclass=*)", attrs
, &res
);
2607 if (!ADS_ERR_OK(status
)) {
2611 config_context
= ads_pull_string(ads
, mem_ctx
, res
, "configurationNamingContext");
2612 if (config_context
== NULL
) {
2613 return ADS_ERROR(LDAP_NO_MEMORY
);
2616 base
= talloc_asprintf(mem_ctx
, "cn=Partitions,%s", config_context
);
2618 return ADS_ERROR(LDAP_NO_MEMORY
);
2621 status
= ads_search_dn(ads
, &res
, base
, attrs2
);
2622 if (!ADS_ERR_OK(status
)) {
2626 if (ads_count_replies(ads
, res
) != 1) {
2627 return ADS_ERROR(LDAP_NO_SUCH_OBJECT
);
2630 suffixes
= ads_pull_strings(ads
, mem_ctx
, res
, "uPNSuffixes", num_suffixes
);
2631 if (suffixes
== NULL
) {
2632 ads_msgfree(ads
, res
);
2633 return ADS_ERROR(LDAP_NO_MEMORY
);
2636 ads_msgfree(ads
, res
);
2642 * pull a DOM_SID from an extended dn string
2643 * @param mem_ctx TALLOC_CTX
2644 * @param flags string type of extended_dn
2645 * @param sid pointer to a DOM_SID
2646 * @return boolean inidicating success
2648 BOOL
ads_get_sid_from_extended_dn(TALLOC_CTX
*mem_ctx
,
2650 enum ads_extended_dn_flags flags
,
2660 * ADS_EXTENDED_DN_HEX_STRING:
2661 * <GUID=238e1963cb390f4bb032ba0105525a29>;<SID=010500000000000515000000bb68c8fd6b61b427572eb04556040000>;CN=gd,OU=berlin,OU=suse,DC=ber,DC=suse,DC=de
2663 * ADS_EXTENDED_DN_STRING (only with w2k3):
2664 <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
2667 p
= strchr(dn
, ';');
2672 if (strncmp(p
, ";<SID=", strlen(";<SID=")) != 0) {
2676 p
+= strlen(";<SID=");
2685 DEBUG(100,("ads_get_sid_from_extended_dn: sid string is %s\n", p
));
2689 case ADS_EXTENDED_DN_STRING
:
2690 if (!string_to_sid(sid
, p
)) {
2694 case ADS_EXTENDED_DN_HEX_STRING
: {
2698 buf_len
= strhex_to_str(buf
, strlen(p
), p
);
2703 if (!sid_parse(buf
, buf_len
, sid
)) {
2704 DEBUG(10,("failed to parse sid\n"));
2710 DEBUG(10,("unknown extended dn format\n"));
2718 * pull an array of DOM_SIDs from a ADS result
2719 * @param ads connection to ads server
2720 * @param mem_ctx TALLOC_CTX for allocating sid array
2721 * @param msg Results of search
2722 * @param field Attribute to retrieve
2723 * @param flags string type of extended_dn
2724 * @param sids pointer to sid array to allocate
2725 * @return the count of SIDs pulled
2727 int ads_pull_sids_from_extendeddn(ADS_STRUCT
*ads
,
2728 TALLOC_CTX
*mem_ctx
,
2731 enum ads_extended_dn_flags flags
,
2738 if ((dn_strings
= ads_pull_strings(ads
, mem_ctx
, msg
, field
,
2739 &dn_count
)) == NULL
) {
2743 (*sids
) = TALLOC_ZERO_ARRAY(mem_ctx
, DOM_SID
, dn_count
+ 1);
2745 TALLOC_FREE(dn_strings
);
2749 for (i
=0; i
<dn_count
; i
++) {
2751 if (!ads_get_sid_from_extended_dn(mem_ctx
, dn_strings
[i
],
2752 flags
, &(*sids
)[i
])) {
2754 TALLOC_FREE(dn_strings
);
2759 TALLOC_FREE(dn_strings
);
2764 /********************************************************************
2765 ********************************************************************/
2767 char* ads_get_dnshostname( ADS_STRUCT
*ads
, TALLOC_CTX
*ctx
, const char *machine_name
)
2769 LDAPMessage
*res
= NULL
;
2774 status
= ads_find_machine_acct(ads
, &res
, global_myname());
2775 if (!ADS_ERR_OK(status
)) {
2776 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
2781 if ( (count
= ads_count_replies(ads
, res
)) != 1 ) {
2782 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count
));
2786 if ( (name
= ads_pull_string(ads
, ctx
, res
, "dNSHostName")) == NULL
) {
2787 DEBUG(0,("ads_get_dnshostname: No dNSHostName attribute!\n"));
2791 ads_msgfree(ads
, res
);
2796 /********************************************************************
2797 ********************************************************************/
2799 char* ads_get_upn( ADS_STRUCT
*ads
, TALLOC_CTX
*ctx
, const char *machine_name
)
2801 LDAPMessage
*res
= NULL
;
2806 status
= ads_find_machine_acct(ads
, &res
, global_myname());
2807 if (!ADS_ERR_OK(status
)) {
2808 DEBUG(0,("ads_get_upn: Failed to find account for %s\n",
2813 if ( (count
= ads_count_replies(ads
, res
)) != 1 ) {
2814 DEBUG(1,("ads_get_upn: %d entries returned!\n", count
));
2818 if ( (name
= ads_pull_string(ads
, ctx
, res
, "userPrincipalName")) == NULL
) {
2819 DEBUG(2,("ads_get_upn: No userPrincipalName attribute!\n"));
2823 ads_msgfree(ads
, res
);
2828 /********************************************************************
2829 ********************************************************************/
2831 char* ads_get_samaccountname( ADS_STRUCT
*ads
, TALLOC_CTX
*ctx
, const char *machine_name
)
2833 LDAPMessage
*res
= NULL
;
2838 status
= ads_find_machine_acct(ads
, &res
, global_myname());
2839 if (!ADS_ERR_OK(status
)) {
2840 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
2845 if ( (count
= ads_count_replies(ads
, res
)) != 1 ) {
2846 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count
));
2850 if ( (name
= ads_pull_string(ads
, ctx
, res
, "sAMAccountName")) == NULL
) {
2851 DEBUG(0,("ads_get_dnshostname: No sAMAccountName attribute!\n"));
2855 ads_msgfree(ads
, res
);
2862 SAVED CODE
- we used to join via ldap
- remember how we did
this. JRA
.
2865 * Join a machine to a realm
2866 * Creates the machine account and sets the machine password
2867 * @param ads connection to ads server
2868 * @param machine name of host to add
2869 * @param org_unit Organizational unit to place machine in
2870 * @return status of join
2872 ADS_STATUS
ads_join_realm(ADS_STRUCT
*ads
, const char *machine_name
,
2873 uint32 account_type
, const char *org_unit
)
2876 LDAPMessage
*res
= NULL
;
2879 /* machine name must be lowercase */
2880 machine
= SMB_STRDUP(machine_name
);
2881 strlower_m(machine
);
2884 status = ads_find_machine_acct(ads, (void **)&res, machine);
2885 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
2886 DEBUG(0, ("Host account for %s already exists - deleting old account\n", machine));
2887 status = ads_leave_realm(ads, machine);
2888 if (!ADS_ERR_OK(status)) {
2889 DEBUG(0, ("Failed to delete host '%s' from the '%s' realm.\n",
2890 machine, ads->config.realm));
2895 status
= ads_add_machine_acct(ads
, machine
, account_type
, org_unit
);
2896 if (!ADS_ERR_OK(status
)) {
2897 DEBUG(0, ("ads_join_realm: ads_add_machine_acct failed (%s): %s\n", machine
, ads_errstr(status
)));
2902 status
= ads_find_machine_acct(ads
, (void **)(void *)&res
, machine
);
2903 if (!ADS_ERR_OK(status
)) {
2904 DEBUG(0, ("ads_join_realm: Host account test failed for machine %s\n", machine
));
2910 ads_msgfree(ads
, res
);
2919 * Delete a machine from the realm
2920 * @param ads connection to ads server
2921 * @param hostname Machine to remove
2922 * @return status of delete
2924 ADS_STATUS
ads_leave_realm(ADS_STRUCT
*ads
, const char *hostname
)
2929 char *hostnameDN
, *host
;
2931 LDAPControl ldap_control
;
2932 LDAPControl
* pldap_control
[2] = {NULL
, NULL
};
2934 pldap_control
[0] = &ldap_control
;
2935 memset(&ldap_control
, 0, sizeof(LDAPControl
));
2936 ldap_control
.ldctl_oid
= (char *)LDAP_SERVER_TREE_DELETE_OID
;
2938 /* hostname must be lowercase */
2939 host
= SMB_STRDUP(hostname
);
2942 status
= ads_find_machine_acct(ads
, &res
, host
);
2943 if (!ADS_ERR_OK(status
)) {
2944 DEBUG(0, ("Host account for %s does not exist.\n", host
));
2949 msg
= ads_first_entry(ads
, res
);
2952 return ADS_ERROR_SYSTEM(ENOENT
);
2955 hostnameDN
= ads_get_dn(ads
, (LDAPMessage
*)msg
);
2957 rc
= ldap_delete_ext_s(ads
->ld
, hostnameDN
, pldap_control
, NULL
);
2959 DEBUG(3,("ldap_delete_ext_s failed with error code %d\n", rc
));
2961 DEBUG(3,("ldap_delete_ext_s succeeded with error code %d\n", rc
));
2964 if (rc
!= LDAP_SUCCESS
) {
2965 const char *attrs
[] = { "cn", NULL
};
2966 LDAPMessage
*msg_sub
;
2968 /* we only search with scope ONE, we do not expect any further
2969 * objects to be created deeper */
2971 status
= ads_do_search_retry(ads
, hostnameDN
,
2972 LDAP_SCOPE_ONELEVEL
,
2973 "(objectclass=*)", attrs
, &res
);
2975 if (!ADS_ERR_OK(status
)) {
2977 ads_memfree(ads
, hostnameDN
);
2981 for (msg_sub
= ads_first_entry(ads
, res
); msg_sub
;
2982 msg_sub
= ads_next_entry(ads
, msg_sub
)) {
2986 if ((dn
= ads_get_dn(ads
, msg_sub
)) == NULL
) {
2988 ads_memfree(ads
, hostnameDN
);
2989 return ADS_ERROR(LDAP_NO_MEMORY
);
2992 status
= ads_del_dn(ads
, dn
);
2993 if (!ADS_ERR_OK(status
)) {
2994 DEBUG(3,("failed to delete dn %s: %s\n", dn
, ads_errstr(status
)));
2996 ads_memfree(ads
, dn
);
2997 ads_memfree(ads
, hostnameDN
);
3001 ads_memfree(ads
, dn
);
3004 /* there should be no subordinate objects anymore */
3005 status
= ads_do_search_retry(ads
, hostnameDN
,
3006 LDAP_SCOPE_ONELEVEL
,
3007 "(objectclass=*)", attrs
, &res
);
3009 if (!ADS_ERR_OK(status
) || ( (ads_count_replies(ads
, res
)) > 0 ) ) {
3011 ads_memfree(ads
, hostnameDN
);
3015 /* delete hostnameDN now */
3016 status
= ads_del_dn(ads
, hostnameDN
);
3017 if (!ADS_ERR_OK(status
)) {
3019 DEBUG(3,("failed to delete dn %s: %s\n", hostnameDN
, ads_errstr(status
)));
3020 ads_memfree(ads
, hostnameDN
);
3025 ads_memfree(ads
, hostnameDN
);
3027 status
= ads_find_machine_acct(ads
, &res
, host
);
3028 if (ADS_ERR_OK(status
) && ads_count_replies(ads
, res
) == 1) {
3029 DEBUG(3, ("Failed to remove host account.\n"));