2 Unix SMB/CIFS implementation.
3 ads (active directory) utility library
4 Copyright (C) Andrew Tridgell 2001
5 Copyright (C) Remus Koos 2001
6 Copyright (C) Jim McDonough <jmcd@us.ibm.com> 2002
7 Copyright (C) Guenther Deschner 2005
8 Copyright (C) Gerald Carter 2006
10 This program is free software; you can redistribute it and/or modify
11 it under the terms of the GNU General Public License as published by
12 the Free Software Foundation; either version 3 of the License, or
13 (at your option) any later version.
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License for more details.
20 You should have received a copy of the GNU General Public License
21 along with this program. If not, see <http://www.gnu.org/licenses/>.
30 * @brief basic ldap client-side routines for ads server communications
32 * The routines contained here should do the necessary ldap calls for
35 * Important note: attribute names passed into ads_ routines must
36 * already be in UTF-8 format. We do not convert them because in almost
37 * all cases, they are just ascii (which is represented with the same
38 * codepoints in UTF-8). This may have to change at some point
42 #define LDAP_SERVER_TREE_DELETE_OID "1.2.840.113556.1.4.805"
44 static SIG_ATOMIC_T gotalarm
;
46 /***************************************************************
47 Signal function to tell us we timed out.
48 ****************************************************************/
50 static void gotalarm_sig(void)
55 LDAP
*ldap_open_with_timeout(const char *server
, int port
, unsigned int to
)
61 CatchSignal(SIGALRM
, SIGNAL_CAST gotalarm_sig
);
63 /* End setup timeout. */
65 ldp
= ldap_open(server
, port
);
68 DEBUG(2,("Could not open LDAP connection to %s:%d: %s\n",
69 server
, port
, strerror(errno
)));
72 /* Teardown timeout. */
73 CatchSignal(SIGALRM
, SIGNAL_CAST SIG_IGN
);
79 static int ldap_search_with_timeout(LDAP
*ld
,
80 LDAP_CONST
char *base
,
82 LDAP_CONST
char *filter
,
90 struct timeval timeout
;
93 /* Setup timeout for the ldap_search_ext_s call - local and remote. */
94 timeout
.tv_sec
= lp_ldap_timeout();
97 /* Setup alarm timeout.... Do we need both of these ? JRA. */
99 CatchSignal(SIGALRM
, SIGNAL_CAST gotalarm_sig
);
100 alarm(lp_ldap_timeout());
101 /* End setup timeout. */
103 result
= ldap_search_ext_s(ld
, base
, scope
, filter
, attrs
,
104 attrsonly
, sctrls
, cctrls
, &timeout
,
107 /* Teardown timeout. */
108 CatchSignal(SIGALRM
, SIGNAL_CAST SIG_IGN
);
112 return LDAP_TIMELIMIT_EXCEEDED
;
117 /**********************************************
118 Do client and server sitename match ?
119 **********************************************/
121 BOOL
ads_sitename_match(ADS_STRUCT
*ads
)
123 if (ads
->config
.server_site_name
== NULL
&&
124 ads
->config
.client_site_name
== NULL
) {
125 DEBUG(10,("ads_sitename_match: both null\n"));
128 if (ads
->config
.server_site_name
&&
129 ads
->config
.client_site_name
&&
130 strequal(ads
->config
.server_site_name
,
131 ads
->config
.client_site_name
)) {
132 DEBUG(10,("ads_sitename_match: name %s match\n", ads
->config
.server_site_name
));
135 DEBUG(10,("ads_sitename_match: no match between server: %s and client: %s\n",
136 ads
->config
.server_site_name
? ads
->config
.server_site_name
: "NULL",
137 ads
->config
.client_site_name
? ads
->config
.client_site_name
: "NULL"));
141 /**********************************************
142 Is this the closest DC ?
143 **********************************************/
145 BOOL
ads_closest_dc(ADS_STRUCT
*ads
)
147 if (ads
->config
.flags
& ADS_CLOSEST
) {
148 DEBUG(10,("ads_closest_dc: ADS_CLOSEST flag set\n"));
152 /* not sure if this can ever happen */
153 if (ads_sitename_match(ads
)) {
154 DEBUG(10,("ads_closest_dc: ADS_CLOSEST flag not set but sites match\n"));
158 DEBUG(10,("ads_closest_dc: %s is not the closest DC\n",
159 ads
->config
.ldap_server_name
));
166 try a connection to a given ldap server, returning True and setting the servers IP
167 in the ads struct if successful
169 BOOL
ads_try_connect(ADS_STRUCT
*ads
, const char *server
)
172 struct cldap_netlogon_reply cldap_reply
;
174 if (!server
|| !*server
) {
178 DEBUG(5,("ads_try_connect: sending CLDAP request to %s (realm: %s)\n",
179 server
, ads
->server
.realm
));
181 /* this copes with inet_ntoa brokenness */
183 srv
= SMB_STRDUP(server
);
185 ZERO_STRUCT( cldap_reply
);
187 if ( !ads_cldap_netlogon( srv
, ads
->server
.realm
, &cldap_reply
) ) {
188 DEBUG(3,("ads_try_connect: CLDAP request %s failed.\n", srv
));
193 /* Check the CLDAP reply flags */
195 if ( !(cldap_reply
.flags
& ADS_LDAP
) ) {
196 DEBUG(1,("ads_try_connect: %s's CLDAP reply says it is not an LDAP server!\n",
202 /* Fill in the ads->config values */
204 SAFE_FREE(ads
->config
.realm
);
205 SAFE_FREE(ads
->config
.bind_path
);
206 SAFE_FREE(ads
->config
.ldap_server_name
);
207 SAFE_FREE(ads
->config
.server_site_name
);
208 SAFE_FREE(ads
->config
.client_site_name
);
209 SAFE_FREE(ads
->server
.workgroup
);
211 ads
->config
.flags
= cldap_reply
.flags
;
212 ads
->config
.ldap_server_name
= SMB_STRDUP(cldap_reply
.hostname
);
213 strupper_m(cldap_reply
.domain
);
214 ads
->config
.realm
= SMB_STRDUP(cldap_reply
.domain
);
215 ads
->config
.bind_path
= ads_build_dn(ads
->config
.realm
);
216 if (*cldap_reply
.server_site_name
) {
217 ads
->config
.server_site_name
=
218 SMB_STRDUP(cldap_reply
.server_site_name
);
220 if (*cldap_reply
.client_site_name
) {
221 ads
->config
.client_site_name
=
222 SMB_STRDUP(cldap_reply
.client_site_name
);
225 ads
->server
.workgroup
= SMB_STRDUP(cldap_reply
.netbios_domain
);
227 ads
->ldap
.port
= LDAP_PORT
;
228 ads
->ldap
.ip
= *interpret_addr2(srv
);
231 /* Store our site name. */
232 sitename_store( cldap_reply
.domain
, cldap_reply
.client_site_name
);
237 /**********************************************************************
238 Try to find an AD dc using our internal name resolution routines
239 Try the realm first and then then workgroup name if netbios is not
241 **********************************************************************/
243 static NTSTATUS
ads_find_dc(ADS_STRUCT
*ads
)
247 struct ip_service
*ip_list
;
249 BOOL got_realm
= False
;
250 BOOL use_own_domain
= False
;
252 NTSTATUS status
= NT_STATUS_UNSUCCESSFUL
;
254 /* if the realm and workgroup are both empty, assume they are ours */
257 c_realm
= ads
->server
.realm
;
259 if ( !c_realm
|| !*c_realm
) {
260 /* special case where no realm and no workgroup means our own */
261 if ( !ads
->server
.workgroup
|| !*ads
->server
.workgroup
) {
262 use_own_domain
= True
;
263 c_realm
= lp_realm();
267 if (c_realm
&& *c_realm
)
270 /* we need to try once with the realm name and fallback to the
271 netbios domain name if we fail (if netbios has not been disabled */
273 if ( !got_realm
&& !lp_disable_netbios() ) {
274 c_realm
= ads
->server
.workgroup
;
275 if (!c_realm
|| !*c_realm
) {
276 if ( use_own_domain
)
277 c_realm
= lp_workgroup();
280 if ( !c_realm
|| !*c_realm
) {
281 DEBUG(0,("ads_find_dc: no realm or workgroup! Don't know what to do\n"));
282 return NT_STATUS_INVALID_PARAMETER
; /* rather need MISSING_PARAMETER ... */
286 pstrcpy( realm
, c_realm
);
288 sitename
= sitename_fetch(realm
);
292 DEBUG(6,("ads_find_dc: looking for %s '%s'\n",
293 (got_realm
? "realm" : "domain"), realm
));
295 status
= get_sorted_dc_list(realm
, sitename
, &ip_list
, &count
, got_realm
);
296 if (!NT_STATUS_IS_OK(status
)) {
297 /* fall back to netbios if we can */
298 if ( got_realm
&& !lp_disable_netbios() ) {
307 /* if we fail this loop, then giveup since all the IP addresses returned were dead */
308 for ( i
=0; i
<count
; i
++ ) {
311 fstrcpy( server
, inet_ntoa(ip_list
[i
].ip
) );
313 if ( !NT_STATUS_IS_OK(check_negative_conn_cache(realm
, server
)) )
317 /* realm in this case is a workgroup name. We need
318 to ignore any IP addresses in the negative connection
319 cache that match ip addresses returned in the ad realm
320 case. It sucks that I have to reproduce the logic above... */
321 c_realm
= ads
->server
.realm
;
322 if ( !c_realm
|| !*c_realm
) {
323 if ( !ads
->server
.workgroup
|| !*ads
->server
.workgroup
) {
324 c_realm
= lp_realm();
327 if (c_realm
&& *c_realm
&&
328 !NT_STATUS_IS_OK(check_negative_conn_cache(c_realm
, server
))) {
329 /* Ensure we add the workgroup name for this
330 IP address as negative too. */
331 add_failed_connection_entry( realm
, server
, NT_STATUS_UNSUCCESSFUL
);
336 if ( ads_try_connect(ads
, server
) ) {
342 /* keep track of failures */
343 add_failed_connection_entry( realm
, server
, NT_STATUS_UNSUCCESSFUL
);
348 /* In case we failed to contact one of our closest DC on our site we
349 * need to try to find another DC, retry with a site-less SRV DNS query
353 DEBUG(1,("ads_find_dc: failed to find a valid DC on our site (%s), "
354 "trying to find another DC\n", sitename
));
356 namecache_delete(realm
, 0x1C);
360 return NT_STATUS_NO_LOGON_SERVERS
;
365 * Connect to the LDAP server
366 * @param ads Pointer to an existing ADS_STRUCT
367 * @return status of connection
369 ADS_STATUS
ads_connect(ADS_STRUCT
*ads
)
371 int version
= LDAP_VERSION3
;
375 ZERO_STRUCT(ads
->ldap
);
376 ads
->ldap
.last_attempt
= time(NULL
);
377 ads
->ldap
.wrap_type
= ADS_SASLWRAP_TYPE_PLAIN
;
379 /* try with a user specified server */
381 if (ads
->server
.ldap_server
&&
382 ads_try_connect(ads
, ads
->server
.ldap_server
)) {
386 ntstatus
= ads_find_dc(ads
);
387 if (NT_STATUS_IS_OK(ntstatus
)) {
391 return ADS_ERROR_NT(ntstatus
);
394 DEBUG(3,("Connected to LDAP server %s\n", inet_ntoa(ads
->ldap
.ip
)));
396 if (!ads
->auth
.user_name
) {
397 /* Must use the userPrincipalName value here or sAMAccountName
398 and not servicePrincipalName; found by Guenther Deschner */
400 asprintf(&ads
->auth
.user_name
, "%s$", global_myname() );
403 if (!ads
->auth
.realm
) {
404 ads
->auth
.realm
= SMB_STRDUP(ads
->config
.realm
);
407 if (!ads
->auth
.kdc_server
) {
408 ads
->auth
.kdc_server
= SMB_STRDUP(inet_ntoa(ads
->ldap
.ip
));
412 /* this is a really nasty hack to avoid ADS DNS problems. It needs a patch
413 to MIT kerberos to work (tridge) */
416 asprintf(&env
, "KRB5_KDC_ADDRESS_%s", ads
->config
.realm
);
417 setenv(env
, ads
->auth
.kdc_server
, 1);
422 /* If the caller() requested no LDAP bind, then we are done */
424 if (ads
->auth
.flags
& ADS_AUTH_NO_BIND
) {
428 ads
->ldap
.mem_ctx
= talloc_init("ads LDAP connection memory");
429 if (!ads
->ldap
.mem_ctx
) {
430 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY
);
433 /* Otherwise setup the TCP LDAP session */
435 if ( (ads
->ldap
.ld
= ldap_open_with_timeout(ads
->config
.ldap_server_name
,
436 LDAP_PORT
, lp_ldap_timeout())) == NULL
)
438 return ADS_ERROR(LDAP_OPERATIONS_ERROR
);
441 /* cache the successful connection for workgroup and realm */
442 if (ads_closest_dc(ads
)) {
443 saf_store( ads
->server
.workgroup
, inet_ntoa(ads
->ldap
.ip
));
444 saf_store( ads
->server
.realm
, inet_ntoa(ads
->ldap
.ip
));
447 ldap_set_option(ads
->ldap
.ld
, LDAP_OPT_PROTOCOL_VERSION
, &version
);
449 status
= ADS_ERROR(smb_ldap_start_tls(ads
->ldap
.ld
, version
));
450 if (!ADS_ERR_OK(status
)) {
454 /* fill in the current time and offsets */
456 status
= ads_current_time( ads
);
457 if ( !ADS_ERR_OK(status
) ) {
461 /* Now do the bind */
463 if (ads
->auth
.flags
& ADS_AUTH_ANON_BIND
) {
464 return ADS_ERROR(ldap_simple_bind_s( ads
->ldap
.ld
, NULL
, NULL
));
467 if (ads
->auth
.flags
& ADS_AUTH_SIMPLE_BIND
) {
468 return ADS_ERROR(ldap_simple_bind_s( ads
->ldap
.ld
, ads
->auth
.user_name
, ads
->auth
.password
));
471 return ads_sasl_bind(ads
);
475 * Disconnect the LDAP server
476 * @param ads Pointer to an existing ADS_STRUCT
478 void ads_disconnect(ADS_STRUCT
*ads
)
481 ldap_unbind(ads
->ldap
.ld
);
484 if (ads
->ldap
.wrap_ops
&& ads
->ldap
.wrap_ops
->disconnect
) {
485 ads
->ldap
.wrap_ops
->disconnect(ads
);
487 if (ads
->ldap
.mem_ctx
) {
488 talloc_free(ads
->ldap
.mem_ctx
);
490 ZERO_STRUCT(ads
->ldap
);
494 Duplicate a struct berval into talloc'ed memory
496 static struct berval
*dup_berval(TALLOC_CTX
*ctx
, const struct berval
*in_val
)
498 struct berval
*value
;
500 if (!in_val
) return NULL
;
502 value
= TALLOC_ZERO_P(ctx
, struct berval
);
505 if (in_val
->bv_len
== 0) return value
;
507 value
->bv_len
= in_val
->bv_len
;
508 value
->bv_val
= (char *)TALLOC_MEMDUP(ctx
, in_val
->bv_val
,
514 Make a values list out of an array of (struct berval *)
516 static struct berval
**ads_dup_values(TALLOC_CTX
*ctx
,
517 const struct berval
**in_vals
)
519 struct berval
**values
;
522 if (!in_vals
) return NULL
;
523 for (i
=0; in_vals
[i
]; i
++)
525 values
= TALLOC_ZERO_ARRAY(ctx
, struct berval
*, i
+1);
526 if (!values
) return NULL
;
528 for (i
=0; in_vals
[i
]; i
++) {
529 values
[i
] = dup_berval(ctx
, in_vals
[i
]);
535 UTF8-encode a values list out of an array of (char *)
537 static char **ads_push_strvals(TALLOC_CTX
*ctx
, const char **in_vals
)
542 if (!in_vals
) return NULL
;
543 for (i
=0; in_vals
[i
]; i
++)
545 values
= TALLOC_ZERO_ARRAY(ctx
, char *, i
+1);
546 if (!values
) return NULL
;
548 for (i
=0; in_vals
[i
]; i
++) {
549 push_utf8_talloc(ctx
, &values
[i
], in_vals
[i
]);
555 Pull a (char *) array out of a UTF8-encoded values list
557 static char **ads_pull_strvals(TALLOC_CTX
*ctx
, const char **in_vals
)
562 if (!in_vals
) return NULL
;
563 for (i
=0; in_vals
[i
]; i
++)
565 values
= TALLOC_ZERO_ARRAY(ctx
, char *, i
+1);
566 if (!values
) return NULL
;
568 for (i
=0; in_vals
[i
]; i
++) {
569 pull_utf8_talloc(ctx
, &values
[i
], in_vals
[i
]);
575 * Do a search with paged results. cookie must be null on the first
576 * call, and then returned on each subsequent call. It will be null
577 * again when the entire search is complete
578 * @param ads connection to ads server
579 * @param bind_path Base dn for the search
580 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
581 * @param expr Search expression - specified in local charset
582 * @param attrs Attributes to retrieve - specified in utf8 or ascii
583 * @param res ** which will contain results - free res* with ads_msgfree()
584 * @param count Number of entries retrieved on this page
585 * @param cookie The paged results cookie to be returned on subsequent calls
586 * @return status of search
588 static ADS_STATUS
ads_do_paged_search_args(ADS_STRUCT
*ads
,
589 const char *bind_path
,
590 int scope
, const char *expr
,
591 const char **attrs
, void *args
,
593 int *count
, struct berval
**cookie
)
596 char *utf8_expr
, *utf8_path
, **search_attrs
;
597 LDAPControl PagedResults
, NoReferrals
, ExternalCtrl
, *controls
[4], **rcontrols
;
598 BerElement
*cookie_be
= NULL
;
599 struct berval
*cookie_bv
= NULL
;
600 BerElement
*ext_be
= NULL
;
601 struct berval
*ext_bv
= NULL
;
604 ads_control
*external_control
= (ads_control
*) args
;
608 if (!(ctx
= talloc_init("ads_do_paged_search_args")))
609 return ADS_ERROR(LDAP_NO_MEMORY
);
611 /* 0 means the conversion worked but the result was empty
612 so we only fail if it's -1. In any case, it always
613 at least nulls out the dest */
614 if ((push_utf8_talloc(ctx
, &utf8_expr
, expr
) == (size_t)-1) ||
615 (push_utf8_talloc(ctx
, &utf8_path
, bind_path
) == (size_t)-1)) {
620 if (!attrs
|| !(*attrs
))
623 /* This would be the utf8-encoded version...*/
624 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
625 if (!(str_list_copy(&search_attrs
, attrs
))) {
631 /* Paged results only available on ldap v3 or later */
632 ldap_get_option(ads
->ldap
.ld
, LDAP_OPT_PROTOCOL_VERSION
, &version
);
633 if (version
< LDAP_VERSION3
) {
634 rc
= LDAP_NOT_SUPPORTED
;
638 cookie_be
= ber_alloc_t(LBER_USE_DER
);
640 ber_printf(cookie_be
, "{iO}", (ber_int_t
) 1000, *cookie
);
641 ber_bvfree(*cookie
); /* don't need it from last time */
644 ber_printf(cookie_be
, "{io}", (ber_int_t
) 1000, "", 0);
646 ber_flatten(cookie_be
, &cookie_bv
);
647 PagedResults
.ldctl_oid
= CONST_DISCARD(char *, ADS_PAGE_CTL_OID
);
648 PagedResults
.ldctl_iscritical
= (char) 1;
649 PagedResults
.ldctl_value
.bv_len
= cookie_bv
->bv_len
;
650 PagedResults
.ldctl_value
.bv_val
= cookie_bv
->bv_val
;
652 NoReferrals
.ldctl_oid
= CONST_DISCARD(char *, ADS_NO_REFERRALS_OID
);
653 NoReferrals
.ldctl_iscritical
= (char) 0;
654 NoReferrals
.ldctl_value
.bv_len
= 0;
655 NoReferrals
.ldctl_value
.bv_val
= CONST_DISCARD(char *, "");
657 if (external_control
&&
658 (strequal(external_control
->control
, ADS_EXTENDED_DN_OID
) ||
659 strequal(external_control
->control
, ADS_SD_FLAGS_OID
))) {
661 ExternalCtrl
.ldctl_oid
= CONST_DISCARD(char *, external_control
->control
);
662 ExternalCtrl
.ldctl_iscritical
= (char) external_control
->critical
;
664 /* win2k does not accept a ldctl_value beeing passed in */
666 if (external_control
->val
!= 0) {
668 if ((ext_be
= ber_alloc_t(LBER_USE_DER
)) == NULL
) {
673 if ((ber_printf(ext_be
, "{i}", (ber_int_t
) external_control
->val
)) == -1) {
677 if ((ber_flatten(ext_be
, &ext_bv
)) == -1) {
682 ExternalCtrl
.ldctl_value
.bv_len
= ext_bv
->bv_len
;
683 ExternalCtrl
.ldctl_value
.bv_val
= ext_bv
->bv_val
;
686 ExternalCtrl
.ldctl_value
.bv_len
= 0;
687 ExternalCtrl
.ldctl_value
.bv_val
= NULL
;
690 controls
[0] = &NoReferrals
;
691 controls
[1] = &PagedResults
;
692 controls
[2] = &ExternalCtrl
;
696 controls
[0] = &NoReferrals
;
697 controls
[1] = &PagedResults
;
701 /* we need to disable referrals as the openldap libs don't
702 handle them and paged results at the same time. Using them
703 together results in the result record containing the server
704 page control being removed from the result list (tridge/jmcd)
706 leaving this in despite the control that says don't generate
707 referrals, in case the server doesn't support it (jmcd)
709 ldap_set_option(ads
->ldap
.ld
, LDAP_OPT_REFERRALS
, LDAP_OPT_OFF
);
711 rc
= ldap_search_with_timeout(ads
->ldap
.ld
, utf8_path
, scope
, utf8_expr
,
712 search_attrs
, 0, controls
,
714 (LDAPMessage
**)res
);
716 ber_free(cookie_be
, 1);
717 ber_bvfree(cookie_bv
);
720 DEBUG(3,("ads_do_paged_search_args: ldap_search_with_timeout(%s) -> %s\n", expr
,
721 ldap_err2string(rc
)));
725 rc
= ldap_parse_result(ads
->ldap
.ld
, *res
, NULL
, NULL
, NULL
,
726 NULL
, &rcontrols
, 0);
732 for (i
=0; rcontrols
[i
]; i
++) {
733 if (strcmp(ADS_PAGE_CTL_OID
, rcontrols
[i
]->ldctl_oid
) == 0) {
734 cookie_be
= ber_init(&rcontrols
[i
]->ldctl_value
);
735 ber_scanf(cookie_be
,"{iO}", (ber_int_t
*) count
,
737 /* the berval is the cookie, but must be freed when
739 if (cookie_bv
->bv_len
) /* still more to do */
740 *cookie
=ber_bvdup(cookie_bv
);
743 ber_bvfree(cookie_bv
);
744 ber_free(cookie_be
, 1);
748 ldap_controls_free(rcontrols
);
761 /* if/when we decide to utf8-encode attrs, take out this next line */
762 str_list_free(&search_attrs
);
764 return ADS_ERROR(rc
);
767 static ADS_STATUS
ads_do_paged_search(ADS_STRUCT
*ads
, const char *bind_path
,
768 int scope
, const char *expr
,
769 const char **attrs
, LDAPMessage
**res
,
770 int *count
, struct berval
**cookie
)
772 return ads_do_paged_search_args(ads
, bind_path
, scope
, expr
, attrs
, NULL
, res
, count
, cookie
);
777 * Get all results for a search. This uses ads_do_paged_search() to return
778 * all entries in a large search.
779 * @param ads connection to ads server
780 * @param bind_path Base dn for the search
781 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
782 * @param expr Search expression
783 * @param attrs Attributes to retrieve
784 * @param res ** which will contain results - free res* with ads_msgfree()
785 * @return status of search
787 ADS_STATUS
ads_do_search_all_args(ADS_STRUCT
*ads
, const char *bind_path
,
788 int scope
, const char *expr
,
789 const char **attrs
, void *args
,
792 struct berval
*cookie
= NULL
;
797 status
= ads_do_paged_search_args(ads
, bind_path
, scope
, expr
, attrs
, args
, res
,
800 if (!ADS_ERR_OK(status
))
803 #ifdef HAVE_LDAP_ADD_RESULT_ENTRY
805 LDAPMessage
*res2
= NULL
;
807 LDAPMessage
*msg
, *next
;
809 status2
= ads_do_paged_search_args(ads
, bind_path
, scope
, expr
,
810 attrs
, args
, &res2
, &count
, &cookie
);
812 if (!ADS_ERR_OK(status2
)) break;
814 /* this relies on the way that ldap_add_result_entry() works internally. I hope
815 that this works on all ldap libs, but I have only tested with openldap */
816 for (msg
= ads_first_entry(ads
, res2
); msg
; msg
= next
) {
817 next
= ads_next_entry(ads
, msg
);
818 ldap_add_result_entry((LDAPMessage
**)res
, msg
);
820 /* note that we do not free res2, as the memory is now
821 part of the main returned list */
824 DEBUG(0, ("no ldap_add_result_entry() support in LDAP libs!\n"));
825 status
= ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL
);
831 ADS_STATUS
ads_do_search_all(ADS_STRUCT
*ads
, const char *bind_path
,
832 int scope
, const char *expr
,
833 const char **attrs
, LDAPMessage
**res
)
835 return ads_do_search_all_args(ads
, bind_path
, scope
, expr
, attrs
, NULL
, res
);
838 ADS_STATUS
ads_do_search_all_sd_flags(ADS_STRUCT
*ads
, const char *bind_path
,
839 int scope
, const char *expr
,
840 const char **attrs
, uint32 sd_flags
,
845 args
.control
= ADS_SD_FLAGS_OID
;
847 args
.critical
= True
;
849 return ads_do_search_all_args(ads
, bind_path
, scope
, expr
, attrs
, &args
, res
);
854 * Run a function on all results for a search. Uses ads_do_paged_search() and
855 * runs the function as each page is returned, using ads_process_results()
856 * @param ads connection to ads server
857 * @param bind_path Base dn for the search
858 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
859 * @param expr Search expression - specified in local charset
860 * @param attrs Attributes to retrieve - specified in UTF-8 or ascii
861 * @param fn Function which takes attr name, values list, and data_area
862 * @param data_area Pointer which is passed to function on each call
863 * @return status of search
865 ADS_STATUS
ads_do_search_all_fn(ADS_STRUCT
*ads
, const char *bind_path
,
866 int scope
, const char *expr
, const char **attrs
,
867 BOOL(*fn
)(ADS_STRUCT
*, char *, void **, void *),
870 struct berval
*cookie
= NULL
;
875 status
= ads_do_paged_search(ads
, bind_path
, scope
, expr
, attrs
, &res
,
878 if (!ADS_ERR_OK(status
)) return status
;
880 ads_process_results(ads
, res
, fn
, data_area
);
881 ads_msgfree(ads
, res
);
884 status
= ads_do_paged_search(ads
, bind_path
, scope
, expr
, attrs
,
885 &res
, &count
, &cookie
);
887 if (!ADS_ERR_OK(status
)) break;
889 ads_process_results(ads
, res
, fn
, data_area
);
890 ads_msgfree(ads
, res
);
897 * Do a search with a timeout.
898 * @param ads connection to ads server
899 * @param bind_path Base dn for the search
900 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
901 * @param expr Search expression
902 * @param attrs Attributes to retrieve
903 * @param res ** which will contain results - free res* with ads_msgfree()
904 * @return status of search
906 ADS_STATUS
ads_do_search(ADS_STRUCT
*ads
, const char *bind_path
, int scope
,
908 const char **attrs
, LDAPMessage
**res
)
911 char *utf8_expr
, *utf8_path
, **search_attrs
= NULL
;
915 if (!(ctx
= talloc_init("ads_do_search"))) {
916 DEBUG(1,("ads_do_search: talloc_init() failed!"));
917 return ADS_ERROR(LDAP_NO_MEMORY
);
920 /* 0 means the conversion worked but the result was empty
921 so we only fail if it's negative. In any case, it always
922 at least nulls out the dest */
923 if ((push_utf8_talloc(ctx
, &utf8_expr
, expr
) == (size_t)-1) ||
924 (push_utf8_talloc(ctx
, &utf8_path
, bind_path
) == (size_t)-1)) {
925 DEBUG(1,("ads_do_search: push_utf8_talloc() failed!"));
930 if (!attrs
|| !(*attrs
))
933 /* This would be the utf8-encoded version...*/
934 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
935 if (!(str_list_copy(&search_attrs
, attrs
)))
937 DEBUG(1,("ads_do_search: str_list_copy() failed!"));
943 /* see the note in ads_do_paged_search - we *must* disable referrals */
944 ldap_set_option(ads
->ldap
.ld
, LDAP_OPT_REFERRALS
, LDAP_OPT_OFF
);
946 rc
= ldap_search_with_timeout(ads
->ldap
.ld
, utf8_path
, scope
, utf8_expr
,
947 search_attrs
, 0, NULL
, NULL
,
949 (LDAPMessage
**)res
);
951 if (rc
== LDAP_SIZELIMIT_EXCEEDED
) {
952 DEBUG(3,("Warning! sizelimit exceeded in ldap. Truncating.\n"));
958 /* if/when we decide to utf8-encode attrs, take out this next line */
959 str_list_free(&search_attrs
);
960 return ADS_ERROR(rc
);
963 * Do a general ADS search
964 * @param ads connection to ads server
965 * @param res ** which will contain results - free res* with ads_msgfree()
966 * @param expr Search expression
967 * @param attrs Attributes to retrieve
968 * @return status of search
970 ADS_STATUS
ads_search(ADS_STRUCT
*ads
, LDAPMessage
**res
,
971 const char *expr
, const char **attrs
)
973 return ads_do_search(ads
, ads
->config
.bind_path
, LDAP_SCOPE_SUBTREE
,
978 * Do a search on a specific DistinguishedName
979 * @param ads connection to ads server
980 * @param res ** which will contain results - free res* with ads_msgfree()
981 * @param dn DistinguishName to search
982 * @param attrs Attributes to retrieve
983 * @return status of search
985 ADS_STATUS
ads_search_dn(ADS_STRUCT
*ads
, LDAPMessage
**res
,
986 const char *dn
, const char **attrs
)
988 return ads_do_search(ads
, dn
, LDAP_SCOPE_BASE
, "(objectclass=*)",
993 * Free up memory from a ads_search
994 * @param ads connection to ads server
995 * @param msg Search results to free
997 void ads_msgfree(ADS_STRUCT
*ads
, LDAPMessage
*msg
)
1004 * Free up memory from various ads requests
1005 * @param ads connection to ads server
1006 * @param mem Area to free
1008 void ads_memfree(ADS_STRUCT
*ads
, void *mem
)
1014 * Get a dn from search results
1015 * @param ads connection to ads server
1016 * @param msg Search result
1019 char *ads_get_dn(ADS_STRUCT
*ads
, LDAPMessage
*msg
)
1021 char *utf8_dn
, *unix_dn
;
1023 utf8_dn
= ldap_get_dn(ads
->ldap
.ld
, msg
);
1026 DEBUG (5, ("ads_get_dn: ldap_get_dn failed\n"));
1030 if (pull_utf8_allocate(&unix_dn
, utf8_dn
) == (size_t)-1) {
1031 DEBUG(0,("ads_get_dn: string conversion failure utf8 [%s]\n",
1035 ldap_memfree(utf8_dn
);
1040 * Get the parent from a dn
1041 * @param dn the dn to return the parent from
1042 * @return parent dn string
1044 char *ads_parent_dn(const char *dn
)
1052 p
= strchr(dn
, ',');
1062 * Find a machine account given a hostname
1063 * @param ads connection to ads server
1064 * @param res ** which will contain results - free res* with ads_msgfree()
1065 * @param host Hostname to search for
1066 * @return status of search
1068 ADS_STATUS
ads_find_machine_acct(ADS_STRUCT
*ads
, LDAPMessage
**res
,
1069 const char *machine
)
1073 const char *attrs
[] = {"*", "nTSecurityDescriptor", NULL
};
1077 /* the easiest way to find a machine account anywhere in the tree
1078 is to look for hostname$ */
1079 if (asprintf(&expr
, "(samAccountName=%s$)", machine
) == -1) {
1080 DEBUG(1, ("asprintf failed!\n"));
1081 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY
);
1084 status
= ads_search(ads
, res
, expr
, attrs
);
1090 * Initialize a list of mods to be used in a modify request
1091 * @param ctx An initialized TALLOC_CTX
1092 * @return allocated ADS_MODLIST
1094 ADS_MODLIST
ads_init_mods(TALLOC_CTX
*ctx
)
1096 #define ADS_MODLIST_ALLOC_SIZE 10
1099 if ((mods
= TALLOC_ZERO_ARRAY(ctx
, LDAPMod
*, ADS_MODLIST_ALLOC_SIZE
+ 1)))
1100 /* -1 is safety to make sure we don't go over the end.
1101 need to reset it to NULL before doing ldap modify */
1102 mods
[ADS_MODLIST_ALLOC_SIZE
] = (LDAPMod
*) -1;
1104 return (ADS_MODLIST
)mods
;
1109 add an attribute to the list, with values list already constructed
1111 static ADS_STATUS
ads_modlist_add(TALLOC_CTX
*ctx
, ADS_MODLIST
*mods
,
1112 int mod_op
, const char *name
,
1113 const void *_invals
)
1115 const void **invals
= (const void **)_invals
;
1117 LDAPMod
**modlist
= (LDAPMod
**) *mods
;
1118 struct berval
**ber_values
= NULL
;
1119 char **char_values
= NULL
;
1122 mod_op
= LDAP_MOD_DELETE
;
1124 if (mod_op
& LDAP_MOD_BVALUES
)
1125 ber_values
= ads_dup_values(ctx
,
1126 (const struct berval
**)invals
);
1128 char_values
= ads_push_strvals(ctx
,
1129 (const char **) invals
);
1132 /* find the first empty slot */
1133 for (curmod
=0; modlist
[curmod
] && modlist
[curmod
] != (LDAPMod
*) -1;
1135 if (modlist
[curmod
] == (LDAPMod
*) -1) {
1136 if (!(modlist
= TALLOC_REALLOC_ARRAY(ctx
, modlist
, LDAPMod
*,
1137 curmod
+ADS_MODLIST_ALLOC_SIZE
+1)))
1138 return ADS_ERROR(LDAP_NO_MEMORY
);
1139 memset(&modlist
[curmod
], 0,
1140 ADS_MODLIST_ALLOC_SIZE
*sizeof(LDAPMod
*));
1141 modlist
[curmod
+ADS_MODLIST_ALLOC_SIZE
] = (LDAPMod
*) -1;
1142 *mods
= (ADS_MODLIST
)modlist
;
1145 if (!(modlist
[curmod
] = TALLOC_ZERO_P(ctx
, LDAPMod
)))
1146 return ADS_ERROR(LDAP_NO_MEMORY
);
1147 modlist
[curmod
]->mod_type
= talloc_strdup(ctx
, name
);
1148 if (mod_op
& LDAP_MOD_BVALUES
) {
1149 modlist
[curmod
]->mod_bvalues
= ber_values
;
1150 } else if (mod_op
& LDAP_MOD_DELETE
) {
1151 modlist
[curmod
]->mod_values
= NULL
;
1153 modlist
[curmod
]->mod_values
= char_values
;
1156 modlist
[curmod
]->mod_op
= mod_op
;
1157 return ADS_ERROR(LDAP_SUCCESS
);
1161 * Add a single string value to a mod list
1162 * @param ctx An initialized TALLOC_CTX
1163 * @param mods An initialized ADS_MODLIST
1164 * @param name The attribute name to add
1165 * @param val The value to add - NULL means DELETE
1166 * @return ADS STATUS indicating success of add
1168 ADS_STATUS
ads_mod_str(TALLOC_CTX
*ctx
, ADS_MODLIST
*mods
,
1169 const char *name
, const char *val
)
1171 const char *values
[2];
1177 return ads_modlist_add(ctx
, mods
, LDAP_MOD_DELETE
, name
, NULL
);
1178 return ads_modlist_add(ctx
, mods
, LDAP_MOD_REPLACE
, name
, values
);
1182 * Add an array of string values to a mod list
1183 * @param ctx An initialized TALLOC_CTX
1184 * @param mods An initialized ADS_MODLIST
1185 * @param name The attribute name to add
1186 * @param vals The array of string values to add - NULL means DELETE
1187 * @return ADS STATUS indicating success of add
1189 ADS_STATUS
ads_mod_strlist(TALLOC_CTX
*ctx
, ADS_MODLIST
*mods
,
1190 const char *name
, const char **vals
)
1193 return ads_modlist_add(ctx
, mods
, LDAP_MOD_DELETE
, name
, NULL
);
1194 return ads_modlist_add(ctx
, mods
, LDAP_MOD_REPLACE
,
1195 name
, (const void **) vals
);
1200 * Add a single ber-encoded value to a mod list
1201 * @param ctx An initialized TALLOC_CTX
1202 * @param mods An initialized ADS_MODLIST
1203 * @param name The attribute name to add
1204 * @param val The value to add - NULL means DELETE
1205 * @return ADS STATUS indicating success of add
1207 static ADS_STATUS
ads_mod_ber(TALLOC_CTX
*ctx
, ADS_MODLIST
*mods
,
1208 const char *name
, const struct berval
*val
)
1210 const struct berval
*values
[2];
1215 return ads_modlist_add(ctx
, mods
, LDAP_MOD_DELETE
, name
, NULL
);
1216 return ads_modlist_add(ctx
, mods
, LDAP_MOD_REPLACE
|LDAP_MOD_BVALUES
,
1217 name
, (const void **) values
);
1222 * Perform an ldap modify
1223 * @param ads connection to ads server
1224 * @param mod_dn DistinguishedName to modify
1225 * @param mods list of modifications to perform
1226 * @return status of modify
1228 ADS_STATUS
ads_gen_mod(ADS_STRUCT
*ads
, const char *mod_dn
, ADS_MODLIST mods
)
1231 char *utf8_dn
= NULL
;
1233 this control is needed to modify that contains a currently
1234 non-existent attribute (but allowable for the object) to run
1236 LDAPControl PermitModify
= {
1237 CONST_DISCARD(char *, ADS_PERMIT_MODIFY_OID
),
1240 LDAPControl
*controls
[2];
1242 controls
[0] = &PermitModify
;
1245 if (push_utf8_allocate(&utf8_dn
, mod_dn
) == -1) {
1246 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY
);
1249 /* find the end of the list, marked by NULL or -1 */
1250 for(i
=0;(mods
[i
]!=0)&&(mods
[i
]!=(LDAPMod
*) -1);i
++);
1251 /* make sure the end of the list is NULL */
1253 ret
= ldap_modify_ext_s(ads
->ldap
.ld
, utf8_dn
,
1254 (LDAPMod
**) mods
, controls
, NULL
);
1256 return ADS_ERROR(ret
);
1260 * Perform an ldap add
1261 * @param ads connection to ads server
1262 * @param new_dn DistinguishedName to add
1263 * @param mods list of attributes and values for DN
1264 * @return status of add
1266 ADS_STATUS
ads_gen_add(ADS_STRUCT
*ads
, const char *new_dn
, ADS_MODLIST mods
)
1269 char *utf8_dn
= NULL
;
1271 if (push_utf8_allocate(&utf8_dn
, new_dn
) == -1) {
1272 DEBUG(1, ("ads_gen_add: push_utf8_allocate failed!"));
1273 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY
);
1276 /* find the end of the list, marked by NULL or -1 */
1277 for(i
=0;(mods
[i
]!=0)&&(mods
[i
]!=(LDAPMod
*) -1);i
++);
1278 /* make sure the end of the list is NULL */
1281 ret
= ldap_add_s(ads
->ldap
.ld
, utf8_dn
, (LDAPMod
**)mods
);
1283 return ADS_ERROR(ret
);
1287 * Delete a DistinguishedName
1288 * @param ads connection to ads server
1289 * @param new_dn DistinguishedName to delete
1290 * @return status of delete
1292 ADS_STATUS
ads_del_dn(ADS_STRUCT
*ads
, char *del_dn
)
1295 char *utf8_dn
= NULL
;
1296 if (push_utf8_allocate(&utf8_dn
, del_dn
) == -1) {
1297 DEBUG(1, ("ads_del_dn: push_utf8_allocate failed!"));
1298 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY
);
1301 ret
= ldap_delete_s(ads
->ldap
.ld
, utf8_dn
);
1303 return ADS_ERROR(ret
);
1307 * Build an org unit string
1308 * if org unit is Computers or blank then assume a container, otherwise
1309 * assume a / separated list of organisational units.
1310 * jmcd: '\' is now used for escapes so certain chars can be in the ou (e.g. #)
1311 * @param ads connection to ads server
1312 * @param org_unit Organizational unit
1313 * @return org unit string - caller must free
1315 char *ads_ou_string(ADS_STRUCT
*ads
, const char *org_unit
)
1319 if (!org_unit
|| !*org_unit
) {
1321 ret
= ads_default_ou_string(ads
, WELL_KNOWN_GUID_COMPUTERS
);
1323 /* samba4 might not yet respond to a wellknownobject-query */
1324 return ret
? ret
: SMB_STRDUP("cn=Computers");
1327 if (strequal(org_unit
, "Computers")) {
1328 return SMB_STRDUP("cn=Computers");
1331 /* jmcd: removed "\\" from the separation chars, because it is
1332 needed as an escape for chars like '#' which are valid in an
1334 return ads_build_path(org_unit
, "/", "ou=", 1);
1338 * Get a org unit string for a well-known GUID
1339 * @param ads connection to ads server
1340 * @param wknguid Well known GUID
1341 * @return org unit string - caller must free
1343 char *ads_default_ou_string(ADS_STRUCT
*ads
, const char *wknguid
)
1346 LDAPMessage
*res
= NULL
;
1347 char *base
, *wkn_dn
= NULL
, *ret
= NULL
, **wkn_dn_exp
= NULL
,
1348 **bind_dn_exp
= NULL
;
1349 const char *attrs
[] = {"distinguishedName", NULL
};
1350 int new_ln
, wkn_ln
, bind_ln
, i
;
1352 if (wknguid
== NULL
) {
1356 if (asprintf(&base
, "<WKGUID=%s,%s>", wknguid
, ads
->config
.bind_path
) == -1) {
1357 DEBUG(1, ("asprintf failed!\n"));
1361 status
= ads_search_dn(ads
, &res
, base
, attrs
);
1362 if (!ADS_ERR_OK(status
)) {
1363 DEBUG(1,("Failed while searching for: %s\n", base
));
1367 if (ads_count_replies(ads
, res
) != 1) {
1371 /* substitute the bind-path from the well-known-guid-search result */
1372 wkn_dn
= ads_get_dn(ads
, res
);
1377 wkn_dn_exp
= ldap_explode_dn(wkn_dn
, 0);
1382 bind_dn_exp
= ldap_explode_dn(ads
->config
.bind_path
, 0);
1387 for (wkn_ln
=0; wkn_dn_exp
[wkn_ln
]; wkn_ln
++)
1389 for (bind_ln
=0; bind_dn_exp
[bind_ln
]; bind_ln
++)
1392 new_ln
= wkn_ln
- bind_ln
;
1394 ret
= SMB_STRDUP(wkn_dn_exp
[0]);
1399 for (i
=1; i
< new_ln
; i
++) {
1402 if (asprintf(&s
, "%s,%s", ret
, wkn_dn_exp
[i
]) == -1) {
1408 ret
= SMB_STRDUP(s
);
1417 ads_msgfree(ads
, res
);
1418 ads_memfree(ads
, wkn_dn
);
1420 ldap_value_free(wkn_dn_exp
);
1423 ldap_value_free(bind_dn_exp
);
1430 * Adds (appends) an item to an attribute array, rather then
1431 * replacing the whole list
1432 * @param ctx An initialized TALLOC_CTX
1433 * @param mods An initialized ADS_MODLIST
1434 * @param name name of the ldap attribute to append to
1435 * @param vals an array of values to add
1436 * @return status of addition
1439 ADS_STATUS
ads_add_strlist(TALLOC_CTX
*ctx
, ADS_MODLIST
*mods
,
1440 const char *name
, const char **vals
)
1442 return ads_modlist_add(ctx
, mods
, LDAP_MOD_ADD
, name
,
1443 (const void *) vals
);
1447 * Determines the computer account's current KVNO via an LDAP lookup
1448 * @param ads An initialized ADS_STRUCT
1449 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1450 * @return the kvno for the computer account, or -1 in case of a failure.
1453 uint32
ads_get_kvno(ADS_STRUCT
*ads
, const char *machine_name
)
1455 LDAPMessage
*res
= NULL
;
1456 uint32 kvno
= (uint32
)-1; /* -1 indicates a failure */
1458 const char *attrs
[] = {"msDS-KeyVersionNumber", NULL
};
1459 char *dn_string
= NULL
;
1460 ADS_STATUS ret
= ADS_ERROR(LDAP_SUCCESS
);
1462 DEBUG(5,("ads_get_kvno: Searching for host %s\n", machine_name
));
1463 if (asprintf(&filter
, "(samAccountName=%s$)", machine_name
) == -1) {
1466 ret
= ads_search(ads
, &res
, filter
, attrs
);
1468 if (!ADS_ERR_OK(ret
) && ads_count_replies(ads
, res
)) {
1469 DEBUG(1,("ads_get_kvno: Computer Account For %s not found.\n", machine_name
));
1470 ads_msgfree(ads
, res
);
1474 dn_string
= ads_get_dn(ads
, res
);
1476 DEBUG(0,("ads_get_kvno: out of memory.\n"));
1477 ads_msgfree(ads
, res
);
1480 DEBUG(5,("ads_get_kvno: Using: %s\n", dn_string
));
1481 ads_memfree(ads
, dn_string
);
1483 /* ---------------------------------------------------------
1484 * 0 is returned as a default KVNO from this point on...
1485 * This is done because Windows 2000 does not support key
1486 * version numbers. Chances are that a failure in the next
1487 * step is simply due to Windows 2000 being used for a
1488 * domain controller. */
1491 if (!ads_pull_uint32(ads
, res
, "msDS-KeyVersionNumber", &kvno
)) {
1492 DEBUG(3,("ads_get_kvno: Error Determining KVNO!\n"));
1493 DEBUG(3,("ads_get_kvno: Windows 2000 does not support KVNO's, so this may be normal.\n"));
1494 ads_msgfree(ads
, res
);
1499 DEBUG(5,("ads_get_kvno: Looked Up KVNO of: %d\n", kvno
));
1500 ads_msgfree(ads
, res
);
1505 * This clears out all registered spn's for a given hostname
1506 * @param ads An initilaized ADS_STRUCT
1507 * @param machine_name the NetBIOS name of the computer.
1508 * @return 0 upon success, non-zero otherwise.
1511 ADS_STATUS
ads_clear_service_principal_names(ADS_STRUCT
*ads
, const char *machine_name
)
1514 LDAPMessage
*res
= NULL
;
1516 const char *servicePrincipalName
[1] = {NULL
};
1517 ADS_STATUS ret
= ADS_ERROR(LDAP_SUCCESS
);
1518 char *dn_string
= NULL
;
1520 ret
= ads_find_machine_acct(ads
, &res
, machine_name
);
1521 if (!ADS_ERR_OK(ret
) || ads_count_replies(ads
, res
) != 1) {
1522 DEBUG(5,("ads_clear_service_principal_names: WARNING: Host Account for %s not found... skipping operation.\n", machine_name
));
1523 DEBUG(5,("ads_clear_service_principal_names: WARNING: Service Principals for %s have NOT been cleared.\n", machine_name
));
1524 ads_msgfree(ads
, res
);
1525 return ADS_ERROR(LDAP_NO_SUCH_OBJECT
);
1528 DEBUG(5,("ads_clear_service_principal_names: Host account for %s found\n", machine_name
));
1529 ctx
= talloc_init("ads_clear_service_principal_names");
1531 ads_msgfree(ads
, res
);
1532 return ADS_ERROR(LDAP_NO_MEMORY
);
1535 if (!(mods
= ads_init_mods(ctx
))) {
1536 talloc_destroy(ctx
);
1537 ads_msgfree(ads
, res
);
1538 return ADS_ERROR(LDAP_NO_MEMORY
);
1540 ret
= ads_mod_strlist(ctx
, &mods
, "servicePrincipalName", servicePrincipalName
);
1541 if (!ADS_ERR_OK(ret
)) {
1542 DEBUG(1,("ads_clear_service_principal_names: Error creating strlist.\n"));
1543 ads_msgfree(ads
, res
);
1544 talloc_destroy(ctx
);
1547 dn_string
= ads_get_dn(ads
, res
);
1549 talloc_destroy(ctx
);
1550 ads_msgfree(ads
, res
);
1551 return ADS_ERROR(LDAP_NO_MEMORY
);
1553 ret
= ads_gen_mod(ads
, dn_string
, mods
);
1554 ads_memfree(ads
,dn_string
);
1555 if (!ADS_ERR_OK(ret
)) {
1556 DEBUG(1,("ads_clear_service_principal_names: Error: Updating Service Principals for machine %s in LDAP\n",
1558 ads_msgfree(ads
, res
);
1559 talloc_destroy(ctx
);
1563 ads_msgfree(ads
, res
);
1564 talloc_destroy(ctx
);
1569 * This adds a service principal name to an existing computer account
1570 * (found by hostname) in AD.
1571 * @param ads An initialized ADS_STRUCT
1572 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1573 * @param my_fqdn The fully qualified DNS name of the machine
1574 * @param spn A string of the service principal to add, i.e. 'host'
1575 * @return 0 upon sucess, or non-zero if a failure occurs
1578 ADS_STATUS
ads_add_service_principal_name(ADS_STRUCT
*ads
, const char *machine_name
,
1579 const char *my_fqdn
, const char *spn
)
1583 LDAPMessage
*res
= NULL
;
1586 char *dn_string
= NULL
;
1587 const char *servicePrincipalName
[3] = {NULL
, NULL
, NULL
};
1589 ret
= ads_find_machine_acct(ads
, &res
, machine_name
);
1590 if (!ADS_ERR_OK(ret
) || ads_count_replies(ads
, res
) != 1) {
1591 DEBUG(1,("ads_add_service_principal_name: WARNING: Host Account for %s not found... skipping operation.\n",
1593 DEBUG(1,("ads_add_service_principal_name: WARNING: Service Principal '%s/%s@%s' has NOT been added.\n",
1594 spn
, machine_name
, ads
->config
.realm
));
1595 ads_msgfree(ads
, res
);
1596 return ADS_ERROR(LDAP_NO_SUCH_OBJECT
);
1599 DEBUG(1,("ads_add_service_principal_name: Host account for %s found\n", machine_name
));
1600 if (!(ctx
= talloc_init("ads_add_service_principal_name"))) {
1601 ads_msgfree(ads
, res
);
1602 return ADS_ERROR(LDAP_NO_MEMORY
);
1605 /* add short name spn */
1607 if ( (psp1
= talloc_asprintf(ctx
, "%s/%s", spn
, machine_name
)) == NULL
) {
1608 talloc_destroy(ctx
);
1609 ads_msgfree(ads
, res
);
1610 return ADS_ERROR(LDAP_NO_MEMORY
);
1613 strlower_m(&psp1
[strlen(spn
)]);
1614 servicePrincipalName
[0] = psp1
;
1616 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n",
1617 psp1
, machine_name
));
1620 /* add fully qualified spn */
1622 if ( (psp2
= talloc_asprintf(ctx
, "%s/%s", spn
, my_fqdn
)) == NULL
) {
1623 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
1627 strlower_m(&psp2
[strlen(spn
)]);
1628 servicePrincipalName
[1] = psp2
;
1630 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n",
1631 psp2
, machine_name
));
1633 if ( (mods
= ads_init_mods(ctx
)) == NULL
) {
1634 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
1638 ret
= ads_add_strlist(ctx
, &mods
, "servicePrincipalName", servicePrincipalName
);
1639 if (!ADS_ERR_OK(ret
)) {
1640 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
1644 if ( (dn_string
= ads_get_dn(ads
, res
)) == NULL
) {
1645 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
1649 ret
= ads_gen_mod(ads
, dn_string
, mods
);
1650 ads_memfree(ads
,dn_string
);
1651 if (!ADS_ERR_OK(ret
)) {
1652 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
1658 ads_msgfree(ads
, res
);
1663 * adds a machine account to the ADS server
1664 * @param ads An intialized ADS_STRUCT
1665 * @param machine_name - the NetBIOS machine name of this account.
1666 * @param account_type A number indicating the type of account to create
1667 * @param org_unit The LDAP path in which to place this account
1668 * @return 0 upon success, or non-zero otherwise
1671 ADS_STATUS
ads_create_machine_acct(ADS_STRUCT
*ads
, const char *machine_name
,
1672 const char *org_unit
)
1675 char *samAccountName
, *controlstr
;
1678 char *machine_escaped
= NULL
;
1680 const char *objectClass
[] = {"top", "person", "organizationalPerson",
1681 "user", "computer", NULL
};
1682 LDAPMessage
*res
= NULL
;
1683 uint32 acct_control
= ( UF_WORKSTATION_TRUST_ACCOUNT
|\
1684 UF_DONT_EXPIRE_PASSWD
|\
1685 UF_ACCOUNTDISABLE
);
1687 if (!(ctx
= talloc_init("ads_add_machine_acct")))
1688 return ADS_ERROR(LDAP_NO_MEMORY
);
1690 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
1692 machine_escaped
= escape_rdn_val_string_alloc(machine_name
);
1693 if (!machine_escaped
) {
1697 new_dn
= talloc_asprintf(ctx
, "cn=%s,%s", machine_escaped
, org_unit
);
1698 samAccountName
= talloc_asprintf(ctx
, "%s$", machine_name
);
1700 if ( !new_dn
|| !samAccountName
) {
1704 #ifndef ENCTYPE_ARCFOUR_HMAC
1705 acct_control
|= UF_USE_DES_KEY_ONLY
;
1708 if (!(controlstr
= talloc_asprintf(ctx
, "%u", acct_control
))) {
1712 if (!(mods
= ads_init_mods(ctx
))) {
1716 ads_mod_str(ctx
, &mods
, "cn", machine_name
);
1717 ads_mod_str(ctx
, &mods
, "sAMAccountName", samAccountName
);
1718 ads_mod_strlist(ctx
, &mods
, "objectClass", objectClass
);
1719 ads_mod_str(ctx
, &mods
, "userAccountControl", controlstr
);
1721 ret
= ads_gen_add(ads
, new_dn
, mods
);
1724 SAFE_FREE(machine_escaped
);
1725 ads_msgfree(ads
, res
);
1726 talloc_destroy(ctx
);
1732 * move a machine account to another OU on the ADS server
1733 * @param ads - An intialized ADS_STRUCT
1734 * @param machine_name - the NetBIOS machine name of this account.
1735 * @param org_unit - The LDAP path in which to place this account
1736 * @param moved - whether we moved the machine account (optional)
1737 * @return 0 upon success, or non-zero otherwise
1740 ADS_STATUS
ads_move_machine_acct(ADS_STRUCT
*ads
, const char *machine_name
,
1741 const char *org_unit
, BOOL
*moved
)
1745 LDAPMessage
*res
= NULL
;
1746 char *filter
= NULL
;
1747 char *computer_dn
= NULL
;
1749 char *computer_rdn
= NULL
;
1750 BOOL need_move
= False
;
1752 if (asprintf(&filter
, "(samAccountName=%s$)", machine_name
) == -1) {
1753 rc
= ADS_ERROR(LDAP_NO_MEMORY
);
1757 /* Find pre-existing machine */
1758 rc
= ads_search(ads
, &res
, filter
, NULL
);
1759 if (!ADS_ERR_OK(rc
)) {
1763 computer_dn
= ads_get_dn(ads
, res
);
1765 rc
= ADS_ERROR(LDAP_NO_MEMORY
);
1769 parent_dn
= ads_parent_dn(computer_dn
);
1770 if (strequal(parent_dn
, org_unit
)) {
1776 if (asprintf(&computer_rdn
, "CN=%s", machine_name
) == -1) {
1777 rc
= ADS_ERROR(LDAP_NO_MEMORY
);
1781 ldap_status
= ldap_rename_s(ads
->ldap
.ld
, computer_dn
, computer_rdn
,
1782 org_unit
, 1, NULL
, NULL
);
1783 rc
= ADS_ERROR(ldap_status
);
1786 ads_msgfree(ads
, res
);
1788 SAFE_FREE(computer_dn
);
1789 SAFE_FREE(computer_rdn
);
1791 if (!ADS_ERR_OK(rc
)) {
1803 dump a binary result from ldap
1805 static void dump_binary(ADS_STRUCT
*ads
, const char *field
, struct berval
**values
)
1808 for (i
=0; values
[i
]; i
++) {
1809 printf("%s: ", field
);
1810 for (j
=0; j
<values
[i
]->bv_len
; j
++) {
1811 printf("%02X", (unsigned char)values
[i
]->bv_val
[j
]);
1817 static void dump_guid(ADS_STRUCT
*ads
, const char *field
, struct berval
**values
)
1821 for (i
=0; values
[i
]; i
++) {
1822 memcpy(guid
.info
, values
[i
]->bv_val
, sizeof(guid
.info
));
1823 printf("%s: %s\n", field
,
1824 smb_uuid_string_static(smb_uuid_unpack_static(guid
)));
1829 dump a sid result from ldap
1831 static void dump_sid(ADS_STRUCT
*ads
, const char *field
, struct berval
**values
)
1834 for (i
=0; values
[i
]; i
++) {
1836 sid_parse(values
[i
]->bv_val
, values
[i
]->bv_len
, &sid
);
1837 printf("%s: %s\n", field
, sid_string_static(&sid
));
1842 dump ntSecurityDescriptor
1844 static void dump_sd(ADS_STRUCT
*ads
, const char *filed
, struct berval
**values
)
1849 TALLOC_CTX
*ctx
= 0;
1851 if (!(ctx
= talloc_init("sec_io_desc")))
1855 prs_init(&ps
, values
[0]->bv_len
, ctx
, UNMARSHALL
);
1856 prs_copy_data_in(&ps
, values
[0]->bv_val
, values
[0]->bv_len
);
1857 prs_set_offset(&ps
,0);
1860 if (!sec_io_desc("sd", &psd
, &ps
, 1)) {
1862 talloc_destroy(ctx
);
1866 ads_disp_sd(ads
, ctx
, psd
);
1870 talloc_destroy(ctx
);
1874 dump a string result from ldap
1876 static void dump_string(const char *field
, char **values
)
1879 for (i
=0; values
[i
]; i
++) {
1880 printf("%s: %s\n", field
, values
[i
]);
1885 dump a field from LDAP on stdout
1889 static BOOL
ads_dump_field(ADS_STRUCT
*ads
, char *field
, void **values
, void *data_area
)
1894 void (*handler
)(ADS_STRUCT
*, const char *, struct berval
**);
1896 {"objectGUID", False
, dump_guid
},
1897 {"netbootGUID", False
, dump_guid
},
1898 {"nTSecurityDescriptor", False
, dump_sd
},
1899 {"dnsRecord", False
, dump_binary
},
1900 {"objectSid", False
, dump_sid
},
1901 {"tokenGroups", False
, dump_sid
},
1902 {"tokenGroupsNoGCAcceptable", False
, dump_sid
},
1903 {"tokengroupsGlobalandUniversal", False
, dump_sid
},
1904 {"mS-DS-CreatorSID", False
, dump_sid
},
1905 {"msExchMailboxGuid", False
, dump_guid
},
1910 if (!field
) { /* must be end of an entry */
1915 for (i
=0; handlers
[i
].name
; i
++) {
1916 if (StrCaseCmp(handlers
[i
].name
, field
) == 0) {
1917 if (!values
) /* first time, indicate string or not */
1918 return handlers
[i
].string
;
1919 handlers
[i
].handler(ads
, field
, (struct berval
**) values
);
1923 if (!handlers
[i
].name
) {
1924 if (!values
) /* first time, indicate string conversion */
1926 dump_string(field
, (char **)values
);
1932 * Dump a result from LDAP on stdout
1933 * used for debugging
1934 * @param ads connection to ads server
1935 * @param res Results to dump
1938 void ads_dump(ADS_STRUCT
*ads
, LDAPMessage
*res
)
1940 ads_process_results(ads
, res
, ads_dump_field
, NULL
);
1944 * Walk through results, calling a function for each entry found.
1945 * The function receives a field name, a berval * array of values,
1946 * and a data area passed through from the start. The function is
1947 * called once with null for field and values at the end of each
1949 * @param ads connection to ads server
1950 * @param res Results to process
1951 * @param fn Function for processing each result
1952 * @param data_area user-defined area to pass to function
1954 void ads_process_results(ADS_STRUCT
*ads
, LDAPMessage
*res
,
1955 BOOL(*fn
)(ADS_STRUCT
*, char *, void **, void *),
1961 if (!(ctx
= talloc_init("ads_process_results")))
1964 for (msg
= ads_first_entry(ads
, res
); msg
;
1965 msg
= ads_next_entry(ads
, msg
)) {
1969 for (utf8_field
=ldap_first_attribute(ads
->ldap
.ld
,
1970 (LDAPMessage
*)msg
,&b
);
1972 utf8_field
=ldap_next_attribute(ads
->ldap
.ld
,
1973 (LDAPMessage
*)msg
,b
)) {
1974 struct berval
**ber_vals
;
1975 char **str_vals
, **utf8_vals
;
1979 pull_utf8_talloc(ctx
, &field
, utf8_field
);
1980 string
= fn(ads
, field
, NULL
, data_area
);
1983 utf8_vals
= ldap_get_values(ads
->ldap
.ld
,
1984 (LDAPMessage
*)msg
, field
);
1985 str_vals
= ads_pull_strvals(ctx
,
1986 (const char **) utf8_vals
);
1987 fn(ads
, field
, (void **) str_vals
, data_area
);
1988 ldap_value_free(utf8_vals
);
1990 ber_vals
= ldap_get_values_len(ads
->ldap
.ld
,
1991 (LDAPMessage
*)msg
, field
);
1992 fn(ads
, field
, (void **) ber_vals
, data_area
);
1994 ldap_value_free_len(ber_vals
);
1996 ldap_memfree(utf8_field
);
1999 talloc_free_children(ctx
);
2000 fn(ads
, NULL
, NULL
, data_area
); /* completed an entry */
2003 talloc_destroy(ctx
);
2007 * count how many replies are in a LDAPMessage
2008 * @param ads connection to ads server
2009 * @param res Results to count
2010 * @return number of replies
2012 int ads_count_replies(ADS_STRUCT
*ads
, void *res
)
2014 return ldap_count_entries(ads
->ldap
.ld
, (LDAPMessage
*)res
);
2018 * pull the first entry from a ADS result
2019 * @param ads connection to ads server
2020 * @param res Results of search
2021 * @return first entry from result
2023 LDAPMessage
*ads_first_entry(ADS_STRUCT
*ads
, LDAPMessage
*res
)
2025 return ldap_first_entry(ads
->ldap
.ld
, res
);
2029 * pull the next entry from a ADS result
2030 * @param ads connection to ads server
2031 * @param res Results of search
2032 * @return next entry from result
2034 LDAPMessage
*ads_next_entry(ADS_STRUCT
*ads
, LDAPMessage
*res
)
2036 return ldap_next_entry(ads
->ldap
.ld
, res
);
2040 * pull a single string from a ADS result
2041 * @param ads connection to ads server
2042 * @param mem_ctx TALLOC_CTX to use for allocating result string
2043 * @param msg Results of search
2044 * @param field Attribute to retrieve
2045 * @return Result string in talloc context
2047 char *ads_pull_string(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
, LDAPMessage
*msg
,
2055 values
= ldap_get_values(ads
->ldap
.ld
, msg
, field
);
2060 rc
= pull_utf8_talloc(mem_ctx
, &ux_string
,
2062 if (rc
!= (size_t)-1)
2066 ldap_value_free(values
);
2071 * pull an array of strings from a ADS result
2072 * @param ads connection to ads server
2073 * @param mem_ctx TALLOC_CTX to use for allocating result string
2074 * @param msg Results of search
2075 * @param field Attribute to retrieve
2076 * @return Result strings in talloc context
2078 char **ads_pull_strings(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
,
2079 LDAPMessage
*msg
, const char *field
,
2086 values
= ldap_get_values(ads
->ldap
.ld
, msg
, field
);
2090 *num_values
= ldap_count_values(values
);
2092 ret
= TALLOC_ARRAY(mem_ctx
, char *, *num_values
+ 1);
2094 ldap_value_free(values
);
2098 for (i
=0;i
<*num_values
;i
++) {
2099 if (pull_utf8_talloc(mem_ctx
, &ret
[i
], values
[i
]) == -1) {
2100 ldap_value_free(values
);
2106 ldap_value_free(values
);
2111 * pull an array of strings from a ADS result
2112 * (handle large multivalue attributes with range retrieval)
2113 * @param ads connection to ads server
2114 * @param mem_ctx TALLOC_CTX to use for allocating result string
2115 * @param msg Results of search
2116 * @param field Attribute to retrieve
2117 * @param current_strings strings returned by a previous call to this function
2118 * @param next_attribute The next query should ask for this attribute
2119 * @param num_values How many values did we get this time?
2120 * @param more_values Are there more values to get?
2121 * @return Result strings in talloc context
2123 char **ads_pull_strings_range(ADS_STRUCT
*ads
,
2124 TALLOC_CTX
*mem_ctx
,
2125 LDAPMessage
*msg
, const char *field
,
2126 char **current_strings
,
2127 const char **next_attribute
,
2128 size_t *num_strings
,
2132 char *expected_range_attrib
, *range_attr
;
2133 BerElement
*ptr
= NULL
;
2136 size_t num_new_strings
;
2137 unsigned long int range_start
;
2138 unsigned long int range_end
;
2140 /* we might have been given the whole lot anyway */
2141 if ((strings
= ads_pull_strings(ads
, mem_ctx
, msg
, field
, num_strings
))) {
2142 *more_strings
= False
;
2146 expected_range_attrib
= talloc_asprintf(mem_ctx
, "%s;Range=", field
);
2148 /* look for Range result */
2149 for (attr
= ldap_first_attribute(ads
->ldap
.ld
, (LDAPMessage
*)msg
, &ptr
);
2151 attr
= ldap_next_attribute(ads
->ldap
.ld
, (LDAPMessage
*)msg
, ptr
)) {
2152 /* we ignore the fact that this is utf8, as all attributes are ascii... */
2153 if (strnequal(attr
, expected_range_attrib
, strlen(expected_range_attrib
))) {
2161 /* nothing here - this field is just empty */
2162 *more_strings
= False
;
2166 if (sscanf(&range_attr
[strlen(expected_range_attrib
)], "%lu-%lu",
2167 &range_start
, &range_end
) == 2) {
2168 *more_strings
= True
;
2170 if (sscanf(&range_attr
[strlen(expected_range_attrib
)], "%lu-*",
2171 &range_start
) == 1) {
2172 *more_strings
= False
;
2174 DEBUG(1, ("ads_pull_strings_range: Cannot parse Range attriubte (%s)\n",
2176 ldap_memfree(range_attr
);
2177 *more_strings
= False
;
2182 if ((*num_strings
) != range_start
) {
2183 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) doesn't start at %u, but at %lu"
2184 " - aborting range retreival\n",
2185 range_attr
, (unsigned int)(*num_strings
) + 1, range_start
));
2186 ldap_memfree(range_attr
);
2187 *more_strings
= False
;
2191 new_strings
= ads_pull_strings(ads
, mem_ctx
, msg
, range_attr
, &num_new_strings
);
2193 if (*more_strings
&& ((*num_strings
+ num_new_strings
) != (range_end
+ 1))) {
2194 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) tells us we have %lu "
2195 "strings in this bunch, but we only got %lu - aborting range retreival\n",
2196 range_attr
, (unsigned long int)range_end
- range_start
+ 1,
2197 (unsigned long int)num_new_strings
));
2198 ldap_memfree(range_attr
);
2199 *more_strings
= False
;
2203 strings
= TALLOC_REALLOC_ARRAY(mem_ctx
, current_strings
, char *,
2204 *num_strings
+ num_new_strings
);
2206 if (strings
== NULL
) {
2207 ldap_memfree(range_attr
);
2208 *more_strings
= False
;
2212 if (new_strings
&& num_new_strings
) {
2213 memcpy(&strings
[*num_strings
], new_strings
,
2214 sizeof(*new_strings
) * num_new_strings
);
2217 (*num_strings
) += num_new_strings
;
2219 if (*more_strings
) {
2220 *next_attribute
= talloc_asprintf(mem_ctx
,
2225 if (!*next_attribute
) {
2226 DEBUG(1, ("talloc_asprintf for next attribute failed!\n"));
2227 ldap_memfree(range_attr
);
2228 *more_strings
= False
;
2233 ldap_memfree(range_attr
);
2239 * pull a single uint32 from a ADS result
2240 * @param ads connection to ads server
2241 * @param msg Results of search
2242 * @param field Attribute to retrieve
2243 * @param v Pointer to int to store result
2244 * @return boolean inidicating success
2246 BOOL
ads_pull_uint32(ADS_STRUCT
*ads
, LDAPMessage
*msg
, const char *field
,
2251 values
= ldap_get_values(ads
->ldap
.ld
, msg
, field
);
2255 ldap_value_free(values
);
2259 *v
= atoi(values
[0]);
2260 ldap_value_free(values
);
2265 * pull a single objectGUID from an ADS result
2266 * @param ads connection to ADS server
2267 * @param msg results of search
2268 * @param guid 37-byte area to receive text guid
2269 * @return boolean indicating success
2271 BOOL
ads_pull_guid(ADS_STRUCT
*ads
, LDAPMessage
*msg
, struct GUID
*guid
)
2274 UUID_FLAT flat_guid
;
2276 values
= ldap_get_values(ads
->ldap
.ld
, msg
, "objectGUID");
2281 memcpy(&flat_guid
.info
, values
[0], sizeof(UUID_FLAT
));
2282 smb_uuid_unpack(flat_guid
, guid
);
2283 ldap_value_free(values
);
2286 ldap_value_free(values
);
2293 * pull a single DOM_SID from a ADS result
2294 * @param ads connection to ads server
2295 * @param msg Results of search
2296 * @param field Attribute to retrieve
2297 * @param sid Pointer to sid to store result
2298 * @return boolean inidicating success
2300 BOOL
ads_pull_sid(ADS_STRUCT
*ads
, LDAPMessage
*msg
, const char *field
,
2303 struct berval
**values
;
2306 values
= ldap_get_values_len(ads
->ldap
.ld
, msg
, field
);
2312 ret
= sid_parse(values
[0]->bv_val
, values
[0]->bv_len
, sid
);
2314 ldap_value_free_len(values
);
2319 * pull an array of DOM_SIDs from a ADS result
2320 * @param ads connection to ads server
2321 * @param mem_ctx TALLOC_CTX for allocating sid array
2322 * @param msg Results of search
2323 * @param field Attribute to retrieve
2324 * @param sids pointer to sid array to allocate
2325 * @return the count of SIDs pulled
2327 int ads_pull_sids(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
,
2328 LDAPMessage
*msg
, const char *field
, DOM_SID
**sids
)
2330 struct berval
**values
;
2334 values
= ldap_get_values_len(ads
->ldap
.ld
, msg
, field
);
2339 for (i
=0; values
[i
]; i
++)
2343 (*sids
) = TALLOC_ARRAY(mem_ctx
, DOM_SID
, i
);
2345 ldap_value_free_len(values
);
2353 for (i
=0; values
[i
]; i
++) {
2354 ret
= sid_parse(values
[i
]->bv_val
, values
[i
]->bv_len
, &(*sids
)[count
]);
2357 DEBUG(10, ("pulling SID: %s\n", sid_to_string(sid
, &(*sids
)[count
])));
2362 ldap_value_free_len(values
);
2367 * pull a SEC_DESC from a ADS result
2368 * @param ads connection to ads server
2369 * @param mem_ctx TALLOC_CTX for allocating sid array
2370 * @param msg Results of search
2371 * @param field Attribute to retrieve
2372 * @param sd Pointer to *SEC_DESC to store result (talloc()ed)
2373 * @return boolean inidicating success
2375 BOOL
ads_pull_sd(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
,
2376 LDAPMessage
*msg
, const char *field
, SEC_DESC
**sd
)
2378 struct berval
**values
;
2381 values
= ldap_get_values_len(ads
->ldap
.ld
, msg
, field
);
2383 if (!values
) return False
;
2387 prs_init(&ps
, values
[0]->bv_len
, mem_ctx
, UNMARSHALL
);
2388 prs_copy_data_in(&ps
, values
[0]->bv_val
, values
[0]->bv_len
);
2389 prs_set_offset(&ps
,0);
2391 ret
= sec_io_desc("sd", sd
, &ps
, 1);
2395 ldap_value_free_len(values
);
2400 * in order to support usernames longer than 21 characters we need to
2401 * use both the sAMAccountName and the userPrincipalName attributes
2402 * It seems that not all users have the userPrincipalName attribute set
2404 * @param ads connection to ads server
2405 * @param mem_ctx TALLOC_CTX for allocating sid array
2406 * @param msg Results of search
2407 * @return the username
2409 char *ads_pull_username(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
,
2415 /* lookup_name() only works on the sAMAccountName to
2416 returning the username portion of userPrincipalName
2417 breaks winbindd_getpwnam() */
2419 ret
= ads_pull_string(ads
, mem_ctx
, msg
, "userPrincipalName");
2420 if (ret
&& (p
= strchr_m(ret
, '@'))) {
2425 return ads_pull_string(ads
, mem_ctx
, msg
, "sAMAccountName");
2430 * find the update serial number - this is the core of the ldap cache
2431 * @param ads connection to ads server
2432 * @param ads connection to ADS server
2433 * @param usn Pointer to retrieved update serial number
2434 * @return status of search
2436 ADS_STATUS
ads_USN(ADS_STRUCT
*ads
, uint32
*usn
)
2438 const char *attrs
[] = {"highestCommittedUSN", NULL
};
2442 status
= ads_do_search_retry(ads
, "", LDAP_SCOPE_BASE
, "(objectclass=*)", attrs
, &res
);
2443 if (!ADS_ERR_OK(status
))
2446 if (ads_count_replies(ads
, res
) != 1) {
2447 ads_msgfree(ads
, res
);
2448 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED
);
2451 if (!ads_pull_uint32(ads
, res
, "highestCommittedUSN", usn
)) {
2452 ads_msgfree(ads
, res
);
2453 return ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE
);
2456 ads_msgfree(ads
, res
);
2460 /* parse a ADS timestring - typical string is
2461 '20020917091222.0Z0' which means 09:12.22 17th September
2463 static time_t ads_parse_time(const char *str
)
2469 if (sscanf(str
, "%4d%2d%2d%2d%2d%2d",
2470 &tm
.tm_year
, &tm
.tm_mon
, &tm
.tm_mday
,
2471 &tm
.tm_hour
, &tm
.tm_min
, &tm
.tm_sec
) != 6) {
2480 /********************************************************************
2481 ********************************************************************/
2483 ADS_STATUS
ads_current_time(ADS_STRUCT
*ads
)
2485 const char *attrs
[] = {"currentTime", NULL
};
2490 ADS_STRUCT
*ads_s
= ads
;
2492 if (!(ctx
= talloc_init("ads_current_time"))) {
2493 return ADS_ERROR(LDAP_NO_MEMORY
);
2496 /* establish a new ldap tcp session if necessary */
2498 if ( !ads
->ldap
.ld
) {
2499 if ( (ads_s
= ads_init( ads
->server
.realm
, ads
->server
.workgroup
,
2500 ads
->server
.ldap_server
)) == NULL
)
2504 ads_s
->auth
.flags
= ADS_AUTH_ANON_BIND
;
2505 status
= ads_connect( ads_s
);
2506 if ( !ADS_ERR_OK(status
))
2510 status
= ads_do_search(ads_s
, "", LDAP_SCOPE_BASE
, "(objectclass=*)", attrs
, &res
);
2511 if (!ADS_ERR_OK(status
)) {
2515 timestr
= ads_pull_string(ads_s
, ctx
, res
, "currentTime");
2517 ads_msgfree(ads_s
, res
);
2518 status
= ADS_ERROR(LDAP_NO_RESULTS_RETURNED
);
2522 /* but save the time and offset in the original ADS_STRUCT */
2524 ads
->config
.current_time
= ads_parse_time(timestr
);
2526 if (ads
->config
.current_time
!= 0) {
2527 ads
->auth
.time_offset
= ads
->config
.current_time
- time(NULL
);
2528 DEBUG(4,("time offset is %d seconds\n", ads
->auth
.time_offset
));
2531 ads_msgfree(ads
, res
);
2533 status
= ADS_SUCCESS
;
2536 /* free any temporary ads connections */
2537 if ( ads_s
!= ads
) {
2538 ads_destroy( &ads_s
);
2540 talloc_destroy(ctx
);
2545 /********************************************************************
2546 ********************************************************************/
2548 ADS_STATUS
ads_domain_func_level(ADS_STRUCT
*ads
, uint32
*val
)
2550 const char *attrs
[] = {"domainFunctionality", NULL
};
2553 ADS_STRUCT
*ads_s
= ads
;
2555 *val
= DS_DOMAIN_FUNCTION_2000
;
2557 /* establish a new ldap tcp session if necessary */
2559 if ( !ads
->ldap
.ld
) {
2560 if ( (ads_s
= ads_init( ads
->server
.realm
, ads
->server
.workgroup
,
2561 ads
->server
.ldap_server
)) == NULL
)
2565 ads_s
->auth
.flags
= ADS_AUTH_ANON_BIND
;
2566 status
= ads_connect( ads_s
);
2567 if ( !ADS_ERR_OK(status
))
2571 /* If the attribute does not exist assume it is a Windows 2000
2572 functional domain */
2574 status
= ads_do_search(ads_s
, "", LDAP_SCOPE_BASE
, "(objectclass=*)", attrs
, &res
);
2575 if (!ADS_ERR_OK(status
)) {
2576 if ( status
.err
.rc
== LDAP_NO_SUCH_ATTRIBUTE
) {
2577 status
= ADS_SUCCESS
;
2582 if ( !ads_pull_uint32(ads_s
, res
, "domainFunctionality", val
) ) {
2583 DEBUG(5,("ads_domain_func_level: Failed to pull the domainFunctionality attribute.\n"));
2585 DEBUG(3,("ads_domain_func_level: %d\n", *val
));
2588 ads_msgfree(ads
, res
);
2591 /* free any temporary ads connections */
2592 if ( ads_s
!= ads
) {
2593 ads_destroy( &ads_s
);
2600 * find the domain sid for our domain
2601 * @param ads connection to ads server
2602 * @param sid Pointer to domain sid
2603 * @return status of search
2605 ADS_STATUS
ads_domain_sid(ADS_STRUCT
*ads
, DOM_SID
*sid
)
2607 const char *attrs
[] = {"objectSid", NULL
};
2611 rc
= ads_do_search_retry(ads
, ads
->config
.bind_path
, LDAP_SCOPE_BASE
, "(objectclass=*)",
2613 if (!ADS_ERR_OK(rc
)) return rc
;
2614 if (!ads_pull_sid(ads
, res
, "objectSid", sid
)) {
2615 ads_msgfree(ads
, res
);
2616 return ADS_ERROR_SYSTEM(ENOENT
);
2618 ads_msgfree(ads
, res
);
2624 * find our site name
2625 * @param ads connection to ads server
2626 * @param mem_ctx Pointer to talloc context
2627 * @param site_name Pointer to the sitename
2628 * @return status of search
2630 ADS_STATUS
ads_site_dn(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
, const char **site_name
)
2634 const char *dn
, *service_name
;
2635 const char *attrs
[] = { "dsServiceName", NULL
};
2637 status
= ads_do_search(ads
, "", LDAP_SCOPE_BASE
, "(objectclass=*)", attrs
, &res
);
2638 if (!ADS_ERR_OK(status
)) {
2642 service_name
= ads_pull_string(ads
, mem_ctx
, res
, "dsServiceName");
2643 if (service_name
== NULL
) {
2644 ads_msgfree(ads
, res
);
2645 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED
);
2648 ads_msgfree(ads
, res
);
2650 /* go up three levels */
2651 dn
= ads_parent_dn(ads_parent_dn(ads_parent_dn(service_name
)));
2653 return ADS_ERROR(LDAP_NO_MEMORY
);
2656 *site_name
= talloc_strdup(mem_ctx
, dn
);
2657 if (*site_name
== NULL
) {
2658 return ADS_ERROR(LDAP_NO_MEMORY
);
2663 dsServiceName: CN=NTDS Settings,CN=W2K3DC,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,DC=ber,DC=suse,DC=de
2668 * find the site dn where a machine resides
2669 * @param ads connection to ads server
2670 * @param mem_ctx Pointer to talloc context
2671 * @param computer_name name of the machine
2672 * @param site_name Pointer to the sitename
2673 * @return status of search
2675 ADS_STATUS
ads_site_dn_for_machine(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
, const char *computer_name
, const char **site_dn
)
2679 const char *parent
, *filter
;
2680 char *config_context
= NULL
;
2683 /* shortcut a query */
2684 if (strequal(computer_name
, ads
->config
.ldap_server_name
)) {
2685 return ads_site_dn(ads
, mem_ctx
, site_dn
);
2688 status
= ads_config_path(ads
, mem_ctx
, &config_context
);
2689 if (!ADS_ERR_OK(status
)) {
2693 filter
= talloc_asprintf(mem_ctx
, "(cn=%s)", computer_name
);
2694 if (filter
== NULL
) {
2695 return ADS_ERROR(LDAP_NO_MEMORY
);
2698 status
= ads_do_search(ads
, config_context
, LDAP_SCOPE_SUBTREE
,
2699 filter
, NULL
, &res
);
2700 if (!ADS_ERR_OK(status
)) {
2704 if (ads_count_replies(ads
, res
) != 1) {
2705 ads_msgfree(ads
, res
);
2706 return ADS_ERROR(LDAP_NO_SUCH_OBJECT
);
2709 dn
= ads_get_dn(ads
, res
);
2711 ads_msgfree(ads
, res
);
2712 return ADS_ERROR(LDAP_NO_MEMORY
);
2715 /* go up three levels */
2716 parent
= ads_parent_dn(ads_parent_dn(ads_parent_dn(dn
)));
2717 if (parent
== NULL
) {
2718 ads_msgfree(ads
, res
);
2719 ads_memfree(ads
, dn
);
2720 return ADS_ERROR(LDAP_NO_MEMORY
);
2723 *site_dn
= talloc_strdup(mem_ctx
, parent
);
2724 if (*site_dn
== NULL
) {
2725 ads_msgfree(ads
, res
);
2726 ads_memfree(ads
, dn
);
2727 return ADS_ERROR(LDAP_NO_MEMORY
);
2730 ads_memfree(ads
, dn
);
2731 ads_msgfree(ads
, res
);
2737 * get the upn suffixes for a domain
2738 * @param ads connection to ads server
2739 * @param mem_ctx Pointer to talloc context
2740 * @param suffixes Pointer to an array of suffixes
2741 * @param num_suffixes Pointer to the number of suffixes
2742 * @return status of search
2744 ADS_STATUS
ads_upn_suffixes(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
, char ***suffixes
, size_t *num_suffixes
)
2749 char *config_context
= NULL
;
2750 const char *attrs
[] = { "uPNSuffixes", NULL
};
2752 status
= ads_config_path(ads
, mem_ctx
, &config_context
);
2753 if (!ADS_ERR_OK(status
)) {
2757 base
= talloc_asprintf(mem_ctx
, "cn=Partitions,%s", config_context
);
2759 return ADS_ERROR(LDAP_NO_MEMORY
);
2762 status
= ads_search_dn(ads
, &res
, base
, attrs
);
2763 if (!ADS_ERR_OK(status
)) {
2767 if (ads_count_replies(ads
, res
) != 1) {
2768 ads_msgfree(ads
, res
);
2769 return ADS_ERROR(LDAP_NO_SUCH_OBJECT
);
2772 (*suffixes
) = ads_pull_strings(ads
, mem_ctx
, res
, "uPNSuffixes", num_suffixes
);
2773 if ((*suffixes
) == NULL
) {
2774 ads_msgfree(ads
, res
);
2775 return ADS_ERROR(LDAP_NO_MEMORY
);
2778 ads_msgfree(ads
, res
);
2784 * pull a DOM_SID from an extended dn string
2785 * @param mem_ctx TALLOC_CTX
2786 * @param extended_dn string
2787 * @param flags string type of extended_dn
2788 * @param sid pointer to a DOM_SID
2789 * @return boolean inidicating success
2791 BOOL
ads_get_sid_from_extended_dn(TALLOC_CTX
*mem_ctx
,
2792 const char *extended_dn
,
2793 enum ads_extended_dn_flags flags
,
2802 /* otherwise extended_dn gets stripped off */
2803 if ((dn
= talloc_strdup(mem_ctx
, extended_dn
)) == NULL
) {
2807 * ADS_EXTENDED_DN_HEX_STRING:
2808 * <GUID=238e1963cb390f4bb032ba0105525a29>;<SID=010500000000000515000000bb68c8fd6b61b427572eb04556040000>;CN=gd,OU=berlin,OU=suse,DC=ber,DC=suse,DC=de
2810 * ADS_EXTENDED_DN_STRING (only with w2k3):
2811 <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
2814 p
= strchr(dn
, ';');
2819 if (strncmp(p
, ";<SID=", strlen(";<SID=")) != 0) {
2823 p
+= strlen(";<SID=");
2832 DEBUG(100,("ads_get_sid_from_extended_dn: sid string is %s\n", p
));
2836 case ADS_EXTENDED_DN_STRING
:
2837 if (!string_to_sid(sid
, p
)) {
2841 case ADS_EXTENDED_DN_HEX_STRING
: {
2845 buf_len
= strhex_to_str(buf
, strlen(p
), p
);
2850 if (!sid_parse(buf
, buf_len
, sid
)) {
2851 DEBUG(10,("failed to parse sid\n"));
2857 DEBUG(10,("unknown extended dn format\n"));
2865 * pull an array of DOM_SIDs from a ADS result
2866 * @param ads connection to ads server
2867 * @param mem_ctx TALLOC_CTX for allocating sid array
2868 * @param msg Results of search
2869 * @param field Attribute to retrieve
2870 * @param flags string type of extended_dn
2871 * @param sids pointer to sid array to allocate
2872 * @return the count of SIDs pulled
2874 int ads_pull_sids_from_extendeddn(ADS_STRUCT
*ads
,
2875 TALLOC_CTX
*mem_ctx
,
2878 enum ads_extended_dn_flags flags
,
2885 if ((dn_strings
= ads_pull_strings(ads
, mem_ctx
, msg
, field
,
2886 &dn_count
)) == NULL
) {
2890 (*sids
) = TALLOC_ZERO_ARRAY(mem_ctx
, DOM_SID
, dn_count
+ 1);
2892 TALLOC_FREE(dn_strings
);
2896 for (i
=0; i
<dn_count
; i
++) {
2898 if (!ads_get_sid_from_extended_dn(mem_ctx
, dn_strings
[i
],
2899 flags
, &(*sids
)[i
])) {
2901 TALLOC_FREE(dn_strings
);
2906 TALLOC_FREE(dn_strings
);
2911 /********************************************************************
2912 ********************************************************************/
2914 char* ads_get_dnshostname( ADS_STRUCT
*ads
, TALLOC_CTX
*ctx
, const char *machine_name
)
2916 LDAPMessage
*res
= NULL
;
2921 status
= ads_find_machine_acct(ads
, &res
, global_myname());
2922 if (!ADS_ERR_OK(status
)) {
2923 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
2928 if ( (count
= ads_count_replies(ads
, res
)) != 1 ) {
2929 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count
));
2933 if ( (name
= ads_pull_string(ads
, ctx
, res
, "dNSHostName")) == NULL
) {
2934 DEBUG(0,("ads_get_dnshostname: No dNSHostName attribute!\n"));
2938 ads_msgfree(ads
, res
);
2943 /********************************************************************
2944 ********************************************************************/
2946 char* ads_get_upn( ADS_STRUCT
*ads
, TALLOC_CTX
*ctx
, const char *machine_name
)
2948 LDAPMessage
*res
= NULL
;
2953 status
= ads_find_machine_acct(ads
, &res
, global_myname());
2954 if (!ADS_ERR_OK(status
)) {
2955 DEBUG(0,("ads_get_upn: Failed to find account for %s\n",
2960 if ( (count
= ads_count_replies(ads
, res
)) != 1 ) {
2961 DEBUG(1,("ads_get_upn: %d entries returned!\n", count
));
2965 if ( (name
= ads_pull_string(ads
, ctx
, res
, "userPrincipalName")) == NULL
) {
2966 DEBUG(2,("ads_get_upn: No userPrincipalName attribute!\n"));
2970 ads_msgfree(ads
, res
);
2975 /********************************************************************
2976 ********************************************************************/
2978 char* ads_get_samaccountname( ADS_STRUCT
*ads
, TALLOC_CTX
*ctx
, const char *machine_name
)
2980 LDAPMessage
*res
= NULL
;
2985 status
= ads_find_machine_acct(ads
, &res
, global_myname());
2986 if (!ADS_ERR_OK(status
)) {
2987 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
2992 if ( (count
= ads_count_replies(ads
, res
)) != 1 ) {
2993 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count
));
2997 if ( (name
= ads_pull_string(ads
, ctx
, res
, "sAMAccountName")) == NULL
) {
2998 DEBUG(0,("ads_get_dnshostname: No sAMAccountName attribute!\n"));
3002 ads_msgfree(ads
, res
);
3009 SAVED CODE
- we used to join via ldap
- remember how we did
this. JRA
.
3012 * Join a machine to a realm
3013 * Creates the machine account and sets the machine password
3014 * @param ads connection to ads server
3015 * @param machine name of host to add
3016 * @param org_unit Organizational unit to place machine in
3017 * @return status of join
3019 ADS_STATUS
ads_join_realm(ADS_STRUCT
*ads
, const char *machine_name
,
3020 uint32 account_type
, const char *org_unit
)
3023 LDAPMessage
*res
= NULL
;
3026 /* machine name must be lowercase */
3027 machine
= SMB_STRDUP(machine_name
);
3028 strlower_m(machine
);
3031 status = ads_find_machine_acct(ads, (void **)&res, machine);
3032 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
3033 DEBUG(0, ("Host account for %s already exists - deleting old account\n", machine));
3034 status = ads_leave_realm(ads, machine);
3035 if (!ADS_ERR_OK(status)) {
3036 DEBUG(0, ("Failed to delete host '%s' from the '%s' realm.\n",
3037 machine, ads->config.realm));
3042 status
= ads_add_machine_acct(ads
, machine
, account_type
, org_unit
);
3043 if (!ADS_ERR_OK(status
)) {
3044 DEBUG(0, ("ads_join_realm: ads_add_machine_acct failed (%s): %s\n", machine
, ads_errstr(status
)));
3049 status
= ads_find_machine_acct(ads
, (void **)(void *)&res
, machine
);
3050 if (!ADS_ERR_OK(status
)) {
3051 DEBUG(0, ("ads_join_realm: Host account test failed for machine %s\n", machine
));
3057 ads_msgfree(ads
, res
);
3064 * Delete a machine from the realm
3065 * @param ads connection to ads server
3066 * @param hostname Machine to remove
3067 * @return status of delete
3069 ADS_STATUS
ads_leave_realm(ADS_STRUCT
*ads
, const char *hostname
)
3074 char *hostnameDN
, *host
;
3076 LDAPControl ldap_control
;
3077 LDAPControl
* pldap_control
[2] = {NULL
, NULL
};
3079 pldap_control
[0] = &ldap_control
;
3080 memset(&ldap_control
, 0, sizeof(LDAPControl
));
3081 ldap_control
.ldctl_oid
= (char *)LDAP_SERVER_TREE_DELETE_OID
;
3083 /* hostname must be lowercase */
3084 host
= SMB_STRDUP(hostname
);
3087 status
= ads_find_machine_acct(ads
, &res
, host
);
3088 if (!ADS_ERR_OK(status
)) {
3089 DEBUG(0, ("Host account for %s does not exist.\n", host
));
3094 msg
= ads_first_entry(ads
, res
);
3097 return ADS_ERROR_SYSTEM(ENOENT
);
3100 hostnameDN
= ads_get_dn(ads
, (LDAPMessage
*)msg
);
3102 rc
= ldap_delete_ext_s(ads
->ldap
.ld
, hostnameDN
, pldap_control
, NULL
);
3104 DEBUG(3,("ldap_delete_ext_s failed with error code %d\n", rc
));
3106 DEBUG(3,("ldap_delete_ext_s succeeded with error code %d\n", rc
));
3109 if (rc
!= LDAP_SUCCESS
) {
3110 const char *attrs
[] = { "cn", NULL
};
3111 LDAPMessage
*msg_sub
;
3113 /* we only search with scope ONE, we do not expect any further
3114 * objects to be created deeper */
3116 status
= ads_do_search_retry(ads
, hostnameDN
,
3117 LDAP_SCOPE_ONELEVEL
,
3118 "(objectclass=*)", attrs
, &res
);
3120 if (!ADS_ERR_OK(status
)) {
3122 ads_memfree(ads
, hostnameDN
);
3126 for (msg_sub
= ads_first_entry(ads
, res
); msg_sub
;
3127 msg_sub
= ads_next_entry(ads
, msg_sub
)) {
3131 if ((dn
= ads_get_dn(ads
, msg_sub
)) == NULL
) {
3133 ads_memfree(ads
, hostnameDN
);
3134 return ADS_ERROR(LDAP_NO_MEMORY
);
3137 status
= ads_del_dn(ads
, dn
);
3138 if (!ADS_ERR_OK(status
)) {
3139 DEBUG(3,("failed to delete dn %s: %s\n", dn
, ads_errstr(status
)));
3141 ads_memfree(ads
, dn
);
3142 ads_memfree(ads
, hostnameDN
);
3146 ads_memfree(ads
, dn
);
3149 /* there should be no subordinate objects anymore */
3150 status
= ads_do_search_retry(ads
, hostnameDN
,
3151 LDAP_SCOPE_ONELEVEL
,
3152 "(objectclass=*)", attrs
, &res
);
3154 if (!ADS_ERR_OK(status
) || ( (ads_count_replies(ads
, res
)) > 0 ) ) {
3156 ads_memfree(ads
, hostnameDN
);
3160 /* delete hostnameDN now */
3161 status
= ads_del_dn(ads
, hostnameDN
);
3162 if (!ADS_ERR_OK(status
)) {
3164 DEBUG(3,("failed to delete dn %s: %s\n", hostnameDN
, ads_errstr(status
)));
3165 ads_memfree(ads
, hostnameDN
);
3170 ads_memfree(ads
, hostnameDN
);
3172 status
= ads_find_machine_acct(ads
, &res
, host
);
3173 if (ADS_ERR_OK(status
) && ads_count_replies(ads
, res
) == 1) {
3174 DEBUG(3, ("Failed to remove host account.\n"));
3184 * pull all token-sids from an LDAP dn
3185 * @param ads connection to ads server
3186 * @param mem_ctx TALLOC_CTX for allocating sid array
3187 * @param dn of LDAP object
3188 * @param user_sid pointer to DOM_SID (objectSid)
3189 * @param primary_group_sid pointer to DOM_SID (self composed)
3190 * @param sids pointer to sid array to allocate
3191 * @param num_sids counter of SIDs pulled
3192 * @return status of token query
3194 ADS_STATUS
ads_get_tokensids(ADS_STRUCT
*ads
,
3195 TALLOC_CTX
*mem_ctx
,
3198 DOM_SID
*primary_group_sid
,
3203 LDAPMessage
*res
= NULL
;
3205 size_t tmp_num_sids
;
3207 DOM_SID tmp_user_sid
;
3208 DOM_SID tmp_primary_group_sid
;
3210 const char *attrs
[] = {
3217 status
= ads_search_retry_dn(ads
, &res
, dn
, attrs
);
3218 if (!ADS_ERR_OK(status
)) {
3222 count
= ads_count_replies(ads
, res
);
3224 ads_msgfree(ads
, res
);
3225 return ADS_ERROR_LDAP(LDAP_NO_SUCH_OBJECT
);
3228 if (!ads_pull_sid(ads
, res
, "objectSid", &tmp_user_sid
)) {
3229 ads_msgfree(ads
, res
);
3230 return ADS_ERROR_LDAP(LDAP_NO_MEMORY
);
3233 if (!ads_pull_uint32(ads
, res
, "primaryGroupID", &pgid
)) {
3234 ads_msgfree(ads
, res
);
3235 return ADS_ERROR_LDAP(LDAP_NO_MEMORY
);
3239 /* hack to compose the primary group sid without knowing the
3245 sid_copy(&domsid
, &tmp_user_sid
);
3247 if (!sid_split_rid(&domsid
, &dummy_rid
)) {
3248 ads_msgfree(ads
, res
);
3249 return ADS_ERROR_LDAP(LDAP_NO_MEMORY
);
3252 if (!sid_compose(&tmp_primary_group_sid
, &domsid
, pgid
)) {
3253 ads_msgfree(ads
, res
);
3254 return ADS_ERROR_LDAP(LDAP_NO_MEMORY
);
3258 tmp_num_sids
= ads_pull_sids(ads
, mem_ctx
, res
, "tokenGroups", &tmp_sids
);
3260 if (tmp_num_sids
== 0 || !tmp_sids
) {
3261 ads_msgfree(ads
, res
);
3262 return ADS_ERROR_LDAP(LDAP_NO_MEMORY
);
3266 *num_sids
= tmp_num_sids
;
3274 *user_sid
= tmp_user_sid
;
3277 if (primary_group_sid
) {
3278 *primary_group_sid
= tmp_primary_group_sid
;
3281 DEBUG(10,("ads_get_tokensids: returned %d sids\n", (int)tmp_num_sids
+ 2));
3283 ads_msgfree(ads
, res
);
3284 return ADS_ERROR_LDAP(LDAP_SUCCESS
);
3288 * Find a sAMAccoutName in LDAP
3289 * @param ads connection to ads server
3290 * @param mem_ctx TALLOC_CTX for allocating sid array
3291 * @param samaccountname to search
3292 * @param uac_ret uint32 pointer userAccountControl attribute value
3293 * @param dn_ret pointer to dn
3294 * @return status of token query
3296 ADS_STATUS
ads_find_samaccount(ADS_STRUCT
*ads
,
3297 TALLOC_CTX
*mem_ctx
,
3298 const char *samaccountname
,
3300 const char **dn_ret
)
3303 const char *attrs
[] = { "userAccountControl", NULL
};
3305 LDAPMessage
*res
= NULL
;
3309 filter
= talloc_asprintf(mem_ctx
, "(&(objectclass=user)(sAMAccountName=%s))",
3311 if (filter
== NULL
) {
3315 status
= ads_do_search_all(ads
, ads
->config
.bind_path
,
3317 filter
, attrs
, &res
);
3319 if (!ADS_ERR_OK(status
)) {
3323 if (ads_count_replies(ads
, res
) != 1) {
3324 status
= ADS_ERROR(LDAP_NO_RESULTS_RETURNED
);
3328 dn
= ads_get_dn(ads
, res
);
3330 status
= ADS_ERROR(LDAP_NO_MEMORY
);
3334 if (!ads_pull_uint32(ads
, res
, "userAccountControl", &uac
)) {
3335 status
= ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE
);
3344 *dn_ret
= talloc_strdup(mem_ctx
, dn
);
3346 status
= ADS_ERROR(LDAP_NO_MEMORY
);
3351 ads_memfree(ads
, dn
);
3352 ads_msgfree(ads
, res
);
3358 * find our configuration path
3359 * @param ads connection to ads server
3360 * @param mem_ctx Pointer to talloc context
3361 * @param config_path Pointer to the config path
3362 * @return status of search
3364 ADS_STATUS
ads_config_path(ADS_STRUCT
*ads
,
3365 TALLOC_CTX
*mem_ctx
,
3369 LDAPMessage
*res
= NULL
;
3370 const char *config_context
= NULL
;
3371 const char *attrs
[] = { "configurationNamingContext", NULL
};
3373 status
= ads_do_search(ads
, "", LDAP_SCOPE_BASE
,
3374 "(objectclass=*)", attrs
, &res
);
3375 if (!ADS_ERR_OK(status
)) {
3379 config_context
= ads_pull_string(ads
, mem_ctx
, res
,
3380 "configurationNamingContext");
3381 ads_msgfree(ads
, res
);
3382 if (!config_context
) {
3383 return ADS_ERROR(LDAP_NO_MEMORY
);
3387 *config_path
= talloc_strdup(mem_ctx
, config_context
);
3388 if (!*config_path
) {
3389 return ADS_ERROR(LDAP_NO_MEMORY
);
3393 return ADS_ERROR(LDAP_SUCCESS
);
3397 * find the displayName of an extended right
3398 * @param ads connection to ads server
3399 * @param config_path The config path
3400 * @param mem_ctx Pointer to talloc context
3401 * @param GUID struct of the rightsGUID
3402 * @return status of search
3404 const char *ads_get_extended_right_name_by_guid(ADS_STRUCT
*ads
,
3405 const char *config_path
,
3406 TALLOC_CTX
*mem_ctx
,
3407 const struct GUID
*rights_guid
)
3410 LDAPMessage
*res
= NULL
;
3412 const char *attrs
[] = { "displayName", NULL
};
3413 const char *result
= NULL
;
3416 if (!ads
|| !mem_ctx
|| !rights_guid
) {
3420 expr
= talloc_asprintf(mem_ctx
, "(rightsGuid=%s)",
3421 smb_uuid_string_static(*rights_guid
));
3426 path
= talloc_asprintf(mem_ctx
, "cn=Extended-Rights,%s", config_path
);
3431 rc
= ads_do_search_retry(ads
, path
, LDAP_SCOPE_SUBTREE
,
3433 if (!ADS_ERR_OK(rc
)) {
3437 if (ads_count_replies(ads
, res
) != 1) {
3441 result
= ads_pull_string(ads
, mem_ctx
, res
, "displayName");
3444 ads_msgfree(ads
, res
);