/src/samba/source3/libads/ldap.c
Line | Count | Source |
1 | | /* |
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 |
9 | | |
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. |
14 | | |
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. |
19 | | |
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/>. |
22 | | */ |
23 | | |
24 | | #include "includes.h" |
25 | | #include "ads.h" |
26 | | #include "libads/sitename_cache.h" |
27 | | #include "libads/cldap.h" |
28 | | #include "libads/netlogon_ping.h" |
29 | | #include "../lib/tsocket/tsocket.h" |
30 | | #include "../lib/addns/dnsquery.h" |
31 | | #include "../libds/common/flags.h" |
32 | | #include "smbldap.h" |
33 | | #include "../libcli/security/security.h" |
34 | | #include "../librpc/gen_ndr/netlogon.h" |
35 | | #include "lib/param/loadparm.h" |
36 | | #include "libsmb/namequery.h" |
37 | | #include "../librpc/gen_ndr/ndr_ads.h" |
38 | | #include "auth/credentials/credentials.h" |
39 | | #include "passdb.h" |
40 | | |
41 | | #ifdef HAVE_LDAP |
42 | | |
43 | | /** |
44 | | * @file ldap.c |
45 | | * @brief basic ldap client-side routines for ads server communications |
46 | | * |
47 | | * The routines contained here should do the necessary ldap calls for |
48 | | * ads setups. |
49 | | * |
50 | | * Important note: attribute names passed into ads_ routines must |
51 | | * already be in UTF-8 format. We do not convert them because in almost |
52 | | * all cases, they are just ascii (which is represented with the same |
53 | | * codepoints in UTF-8). This may have to change at some point |
54 | | **/ |
55 | | |
56 | | |
57 | | #define LDAP_SERVER_TREE_DELETE_OID "1.2.840.113556.1.4.805" |
58 | | |
59 | | static SIG_ATOMIC_T gotalarm; |
60 | | |
61 | | /*************************************************************** |
62 | | Signal function to tell us we timed out. |
63 | | ****************************************************************/ |
64 | | |
65 | | static void gotalarm_sig(int signum) |
66 | 0 | { |
67 | 0 | gotalarm = 1; |
68 | 0 | } |
69 | | |
70 | | LDAP *ldap_open_with_timeout(const char *server, |
71 | | struct sockaddr_storage *ss, |
72 | | int port, unsigned int to) |
73 | 0 | { |
74 | 0 | LDAP *ldp = NULL; |
75 | 0 | int ldap_err; |
76 | 0 | char *uri; |
77 | |
|
78 | 0 | DEBUG(10, ("Opening connection to LDAP server '%s:%d', timeout " |
79 | 0 | "%u seconds\n", server, port, to)); |
80 | |
|
81 | 0 | if (to) { |
82 | | /* Setup timeout */ |
83 | 0 | gotalarm = 0; |
84 | 0 | CatchSignal(SIGALRM, gotalarm_sig); |
85 | 0 | alarm(to); |
86 | | /* End setup timeout. */ |
87 | 0 | } |
88 | |
|
89 | 0 | if ( strchr_m(server, ':') ) { |
90 | | /* IPv6 URI */ |
91 | 0 | uri = talloc_asprintf(talloc_tos(), "ldap://[%s]:%u", server, port); |
92 | 0 | } else { |
93 | | /* IPv4 URI */ |
94 | 0 | uri = talloc_asprintf(talloc_tos(), "ldap://%s:%u", server, port); |
95 | 0 | } |
96 | 0 | if (uri == NULL) { |
97 | 0 | return NULL; |
98 | 0 | } |
99 | | |
100 | 0 | #ifdef HAVE_LDAP_INIT_FD |
101 | 0 | { |
102 | 0 | int fd = -1; |
103 | 0 | NTSTATUS status = NT_STATUS_UNSUCCESSFUL; |
104 | 0 | unsigned timeout_ms = 1000 * to; |
105 | |
|
106 | 0 | status = open_socket_out(ss, port, timeout_ms, &fd); |
107 | 0 | if (!NT_STATUS_IS_OK(status)) { |
108 | 0 | DEBUG(3, ("open_socket_out: failed to open socket\n")); |
109 | 0 | return NULL; |
110 | 0 | } |
111 | | |
112 | | /* define LDAP_PROTO_TCP from openldap.h if required */ |
113 | 0 | #ifndef LDAP_PROTO_TCP |
114 | 0 | #define LDAP_PROTO_TCP 1 |
115 | 0 | #endif |
116 | 0 | ldap_err = ldap_init_fd(fd, LDAP_PROTO_TCP, uri, &ldp); |
117 | 0 | } |
118 | | #elif defined(HAVE_LDAP_INITIALIZE) |
119 | | ldap_err = ldap_initialize(&ldp, uri); |
120 | | #else |
121 | | ldp = ldap_open(server, port); |
122 | | if (ldp != NULL) { |
123 | | ldap_err = LDAP_SUCCESS; |
124 | | } else { |
125 | | ldap_err = LDAP_OTHER; |
126 | | } |
127 | | #endif |
128 | 0 | if (ldap_err != LDAP_SUCCESS) { |
129 | 0 | DEBUG(2,("Could not initialize connection for LDAP server '%s': %s\n", |
130 | 0 | uri, ldap_err2string(ldap_err))); |
131 | 0 | } else { |
132 | 0 | DEBUG(10, ("Initialized connection for LDAP server '%s'\n", uri)); |
133 | 0 | } |
134 | |
|
135 | 0 | if (to) { |
136 | | /* Teardown timeout. */ |
137 | 0 | alarm(0); |
138 | 0 | CatchSignal(SIGALRM, SIG_IGN); |
139 | 0 | } |
140 | |
|
141 | 0 | return ldp; |
142 | 0 | } |
143 | | |
144 | | static int ldap_search_with_timeout(LDAP *ld, |
145 | | LDAP_CONST char *base, |
146 | | int scope, |
147 | | LDAP_CONST char *filter, |
148 | | char **attrs, |
149 | | int attrsonly, |
150 | | LDAPControl **sctrls, |
151 | | LDAPControl **cctrls, |
152 | | int sizelimit, |
153 | | LDAPMessage **res ) |
154 | 0 | { |
155 | 0 | int to = lp_ldap_timeout(); |
156 | 0 | struct timeval timeout; |
157 | 0 | struct timeval *timeout_ptr = NULL; |
158 | 0 | int result; |
159 | |
|
160 | 0 | DBG_DEBUG("ldap_search: base => [%s], filter => [%s], scope => [%d]\n", |
161 | 0 | base, |
162 | 0 | filter, |
163 | 0 | scope); |
164 | | |
165 | | /* Setup timeout for the ldap_search_ext_s call - local and remote. */ |
166 | 0 | gotalarm = 0; |
167 | |
|
168 | 0 | if (to) { |
169 | 0 | timeout.tv_sec = to; |
170 | 0 | timeout.tv_usec = 0; |
171 | 0 | timeout_ptr = &timeout; |
172 | | |
173 | | /* Setup alarm timeout. */ |
174 | 0 | CatchSignal(SIGALRM, gotalarm_sig); |
175 | | /* Make the alarm time one second beyond |
176 | | the timeout we're setting for the |
177 | | remote search timeout, to allow that |
178 | | to fire in preference. */ |
179 | 0 | alarm(to+1); |
180 | | /* End setup timeout. */ |
181 | 0 | } |
182 | | |
183 | |
|
184 | 0 | result = ldap_search_ext_s(ld, base, scope, filter, attrs, |
185 | 0 | attrsonly, sctrls, cctrls, timeout_ptr, |
186 | 0 | sizelimit, res); |
187 | |
|
188 | 0 | if (to) { |
189 | | /* Teardown alarm timeout. */ |
190 | 0 | CatchSignal(SIGALRM, SIG_IGN); |
191 | 0 | alarm(0); |
192 | 0 | } |
193 | |
|
194 | 0 | if (gotalarm != 0) |
195 | 0 | return LDAP_TIMELIMIT_EXCEEDED; |
196 | | |
197 | | /* |
198 | | * A bug in OpenLDAP means ldap_search_ext_s can return |
199 | | * LDAP_SUCCESS but with a NULL res pointer. Cope with |
200 | | * this. See bug #6279 for details. JRA. |
201 | | */ |
202 | | |
203 | 0 | if (*res == NULL) { |
204 | 0 | return LDAP_TIMELIMIT_EXCEEDED; |
205 | 0 | } |
206 | | |
207 | 0 | return result; |
208 | 0 | } |
209 | | |
210 | | /********************************************** |
211 | | Do client and server sitename match ? |
212 | | **********************************************/ |
213 | | |
214 | | bool ads_sitename_match(ADS_STRUCT *ads) |
215 | 0 | { |
216 | 0 | if (ads->config.server_site_name == NULL && |
217 | 0 | ads->config.client_site_name == NULL ) { |
218 | 0 | DEBUG(10,("ads_sitename_match: both null\n")); |
219 | 0 | return True; |
220 | 0 | } |
221 | 0 | if (ads->config.server_site_name && |
222 | 0 | ads->config.client_site_name && |
223 | 0 | strequal(ads->config.server_site_name, |
224 | 0 | ads->config.client_site_name)) { |
225 | 0 | DEBUG(10,("ads_sitename_match: name %s match\n", ads->config.server_site_name)); |
226 | 0 | return True; |
227 | 0 | } |
228 | 0 | DEBUG(10,("ads_sitename_match: no match between server: %s and client: %s\n", |
229 | 0 | ads->config.server_site_name ? ads->config.server_site_name : "NULL", |
230 | 0 | ads->config.client_site_name ? ads->config.client_site_name : "NULL")); |
231 | 0 | return False; |
232 | 0 | } |
233 | | |
234 | | /********************************************** |
235 | | Is this the closest DC ? |
236 | | **********************************************/ |
237 | | |
238 | | bool ads_closest_dc(ADS_STRUCT *ads) |
239 | 0 | { |
240 | 0 | if (ads->config.flags & NBT_SERVER_CLOSEST) { |
241 | 0 | DEBUG(10,("ads_closest_dc: NBT_SERVER_CLOSEST flag set\n")); |
242 | 0 | return True; |
243 | 0 | } |
244 | | |
245 | | /* not sure if this can ever happen */ |
246 | 0 | if (ads_sitename_match(ads)) { |
247 | 0 | DEBUG(10,("ads_closest_dc: NBT_SERVER_CLOSEST flag not set but sites match\n")); |
248 | 0 | return True; |
249 | 0 | } |
250 | | |
251 | 0 | if (ads->config.client_site_name == NULL) { |
252 | 0 | DEBUG(10,("ads_closest_dc: client belongs to no site\n")); |
253 | 0 | return True; |
254 | 0 | } |
255 | | |
256 | 0 | DEBUG(10,("ads_closest_dc: %s is not the closest DC\n", |
257 | 0 | ads->config.ldap_server_name)); |
258 | |
|
259 | 0 | return False; |
260 | 0 | } |
261 | | |
262 | | static bool ads_fill_cldap_reply(ADS_STRUCT *ads, |
263 | | bool gc, |
264 | | const struct sockaddr_storage *ss, |
265 | | const struct NETLOGON_SAM_LOGON_RESPONSE_EX *cldap_reply) |
266 | 0 | { |
267 | 0 | TALLOC_CTX *frame = talloc_stackframe(); |
268 | 0 | bool ret = false; |
269 | 0 | char addr[INET6_ADDRSTRLEN]; |
270 | 0 | ADS_STATUS status; |
271 | 0 | char *dn; |
272 | |
|
273 | 0 | print_sockaddr(addr, sizeof(addr), ss); |
274 | | |
275 | | /* Check the CLDAP reply flags */ |
276 | | |
277 | | /* Fill in the ads->config values */ |
278 | |
|
279 | 0 | ADS_TALLOC_CONST_FREE(ads->config.workgroup); |
280 | 0 | ADS_TALLOC_CONST_FREE(ads->config.realm); |
281 | 0 | ADS_TALLOC_CONST_FREE(ads->config.bind_path); |
282 | 0 | ADS_TALLOC_CONST_FREE(ads->config.ldap_server_name); |
283 | 0 | ADS_TALLOC_CONST_FREE(ads->config.server_site_name); |
284 | 0 | ADS_TALLOC_CONST_FREE(ads->config.client_site_name); |
285 | |
|
286 | 0 | ads->config.ldap_server_name = talloc_strdup(ads, |
287 | 0 | cldap_reply->pdc_dns_name); |
288 | 0 | if (ads->config.ldap_server_name == NULL) { |
289 | 0 | DBG_WARNING("Out of memory\n"); |
290 | 0 | ret = false; |
291 | 0 | goto out; |
292 | 0 | } |
293 | | |
294 | 0 | ads->config.workgroup = talloc_strdup(ads, cldap_reply->domain_name); |
295 | 0 | if (ads->config.workgroup == NULL) { |
296 | 0 | DBG_WARNING("Out of memory\n"); |
297 | 0 | ret = false; |
298 | 0 | goto out; |
299 | 0 | } |
300 | | |
301 | 0 | ads->config.realm = talloc_asprintf_strupper_m(ads, |
302 | 0 | "%s", |
303 | 0 | cldap_reply->dns_domain); |
304 | 0 | if (ads->config.realm == NULL) { |
305 | 0 | DBG_WARNING("Out of memory\n"); |
306 | 0 | ret = false; |
307 | 0 | goto out; |
308 | 0 | } |
309 | | |
310 | 0 | status = ads_build_dn(ads->config.realm, ads, &dn); |
311 | 0 | if (!ADS_ERR_OK(status)) { |
312 | 0 | DBG_DEBUG("Failed to build bind path: %s\n", |
313 | 0 | ads_errstr(status)); |
314 | 0 | ret = false; |
315 | 0 | goto out; |
316 | 0 | } |
317 | 0 | ads->config.bind_path = dn; |
318 | |
|
319 | 0 | if (*cldap_reply->server_site) { |
320 | 0 | ads->config.server_site_name = |
321 | 0 | talloc_strdup(ads, cldap_reply->server_site); |
322 | 0 | if (ads->config.server_site_name == NULL) { |
323 | 0 | DBG_WARNING("Out of memory\n"); |
324 | 0 | ret = false; |
325 | 0 | goto out; |
326 | 0 | } |
327 | 0 | } |
328 | | |
329 | 0 | if (*cldap_reply->client_site) { |
330 | 0 | ads->config.client_site_name = |
331 | 0 | talloc_strdup(ads, cldap_reply->client_site); |
332 | 0 | if (ads->config.client_site_name == NULL) { |
333 | 0 | DBG_WARNING("Out of memory\n"); |
334 | 0 | ret = false; |
335 | 0 | goto out; |
336 | 0 | } |
337 | 0 | } |
338 | | |
339 | 0 | ads->ldap.port = gc ? LDAP_GC_PORT : LDAP_PORT; |
340 | 0 | ads->ldap.ss = *ss; |
341 | | |
342 | | /* Store our site name. */ |
343 | 0 | sitename_store(cldap_reply->domain_name, cldap_reply->client_site); |
344 | 0 | sitename_store(cldap_reply->dns_domain, cldap_reply->client_site); |
345 | | |
346 | | /* Leave this until last so that the flags are not clobbered */ |
347 | 0 | ads->config.flags = cldap_reply->server_type; |
348 | |
|
349 | 0 | ret = true; |
350 | |
|
351 | 0 | out: |
352 | |
|
353 | 0 | TALLOC_FREE(frame); |
354 | 0 | return ret; |
355 | 0 | } |
356 | | |
357 | | /* |
358 | | try a connection to a given ldap server, returning True and setting the servers IP |
359 | | in the ads struct if successful |
360 | | */ |
361 | | static bool ads_try_connect(ADS_STRUCT *ads, bool gc, |
362 | | struct sockaddr_storage *ss) |
363 | 0 | { |
364 | 0 | struct NETLOGON_SAM_LOGON_RESPONSE_EX cldap_reply = {}; |
365 | 0 | TALLOC_CTX *frame = talloc_stackframe(); |
366 | 0 | bool ok; |
367 | 0 | char addr[INET6_ADDRSTRLEN] = { 0, }; |
368 | |
|
369 | 0 | if (ss == NULL) { |
370 | 0 | TALLOC_FREE(frame); |
371 | 0 | return false; |
372 | 0 | } |
373 | | |
374 | 0 | print_sockaddr(addr, sizeof(addr), ss); |
375 | |
|
376 | 0 | DBG_INFO("ads_try_connect: sending CLDAP request to %s (realm: %s)\n", |
377 | 0 | addr, ads->server.realm); |
378 | |
|
379 | 0 | ok = ads_cldap_netlogon_5(frame, |
380 | 0 | ss, |
381 | 0 | ads->server.realm, |
382 | 0 | ads->config.flags | DS_ONLY_LDAP_NEEDED, |
383 | 0 | &cldap_reply); |
384 | 0 | if (!ok) { |
385 | 0 | DBG_NOTICE("ads_cldap_netlogon_5(%s, %s) failed.\n", |
386 | 0 | addr, ads->server.realm); |
387 | 0 | TALLOC_FREE(frame); |
388 | 0 | return false; |
389 | 0 | } |
390 | | |
391 | 0 | ok = ads_fill_cldap_reply(ads, gc, ss, &cldap_reply); |
392 | 0 | if (!ok) { |
393 | 0 | DBG_NOTICE("ads_fill_cldap_reply(%s, %s) failed.\n", |
394 | 0 | addr, ads->server.realm); |
395 | 0 | TALLOC_FREE(frame); |
396 | 0 | return false; |
397 | 0 | } |
398 | | |
399 | 0 | TALLOC_FREE(frame); |
400 | 0 | return true; |
401 | 0 | } |
402 | | |
403 | | /********************************************************************** |
404 | | send a cldap ping to list of servers, one at a time, until one of |
405 | | them answers it's an ldap server. Record success in the ADS_STRUCT. |
406 | | Take note of and update negative connection cache. |
407 | | **********************************************************************/ |
408 | | |
409 | | static NTSTATUS cldap_ping_list(ADS_STRUCT *ads, |
410 | | const char *domain, |
411 | | struct samba_sockaddr *sa_list, |
412 | | size_t count) |
413 | 0 | { |
414 | 0 | TALLOC_CTX *frame = talloc_stackframe(); |
415 | 0 | struct timeval endtime = timeval_current_ofs(MAX(3,lp_ldap_timeout()/2), 0); |
416 | 0 | uint32_t nt_version = NETLOGON_NT_VERSION_5 | NETLOGON_NT_VERSION_5EX; |
417 | 0 | struct tsocket_address **ts_list = NULL; |
418 | 0 | struct samba_sockaddr **req_sa_list = NULL; |
419 | 0 | struct netlogon_samlogon_response **responses = NULL; |
420 | 0 | size_t num_requests = 0; |
421 | 0 | NTSTATUS status; |
422 | 0 | size_t i; |
423 | 0 | bool ok = false; |
424 | 0 | bool retry; |
425 | |
|
426 | 0 | ts_list = talloc_zero_array(frame, |
427 | 0 | struct tsocket_address *, |
428 | 0 | count); |
429 | 0 | if (ts_list == NULL) { |
430 | 0 | TALLOC_FREE(frame); |
431 | 0 | return NT_STATUS_NO_MEMORY; |
432 | 0 | } |
433 | | |
434 | 0 | req_sa_list = talloc_zero_array(frame, |
435 | 0 | struct samba_sockaddr *, |
436 | 0 | count); |
437 | 0 | if (req_sa_list == NULL) { |
438 | 0 | TALLOC_FREE(frame); |
439 | 0 | return NT_STATUS_NO_MEMORY; |
440 | 0 | } |
441 | | |
442 | 0 | again: |
443 | | /* |
444 | | * The retry loop is bound by the timeout |
445 | | */ |
446 | 0 | retry = false; |
447 | 0 | num_requests = 0; |
448 | |
|
449 | 0 | for (i = 0; i < count; i++) { |
450 | 0 | char server[INET6_ADDRSTRLEN]; |
451 | 0 | int ret; |
452 | |
|
453 | 0 | if (is_zero_addr(&sa_list[i].u.ss)) { |
454 | 0 | continue; |
455 | 0 | } |
456 | | |
457 | 0 | print_sockaddr(server, sizeof(server), &sa_list[i].u.ss); |
458 | |
|
459 | 0 | status = check_negative_conn_cache(domain, server); |
460 | 0 | if (!NT_STATUS_IS_OK(status)) { |
461 | 0 | continue; |
462 | 0 | } |
463 | | |
464 | 0 | ret = tsocket_address_inet_from_strings(ts_list, "ip", |
465 | 0 | server, LDAP_PORT, |
466 | 0 | &ts_list[num_requests]); |
467 | 0 | if (ret != 0) { |
468 | 0 | status = map_nt_error_from_unix(errno); |
469 | 0 | DBG_WARNING("Failed to create tsocket_address for %s - %s\n", |
470 | 0 | server, nt_errstr(status)); |
471 | 0 | TALLOC_FREE(frame); |
472 | 0 | return status; |
473 | 0 | } |
474 | | |
475 | 0 | req_sa_list[num_requests] = &sa_list[i]; |
476 | 0 | num_requests += 1; |
477 | 0 | } |
478 | | |
479 | 0 | DBG_DEBUG("Try to create %zu netlogon connections for domain '%s' " |
480 | 0 | "(provided count of addresses was %zu).\n", |
481 | 0 | num_requests, |
482 | 0 | domain, |
483 | 0 | count); |
484 | |
|
485 | 0 | if (num_requests == 0) { |
486 | 0 | status = NT_STATUS_NO_LOGON_SERVERS; |
487 | 0 | DBG_WARNING("domain[%s] num_requests[%zu] for count[%zu] - %s\n", |
488 | 0 | domain, num_requests, count, nt_errstr(status)); |
489 | 0 | TALLOC_FREE(frame); |
490 | 0 | return status; |
491 | 0 | } |
492 | | |
493 | 0 | status = netlogon_pings(frame, /* mem_ctx */ |
494 | 0 | lp_client_netlogon_ping_protocol(), /* proto */ |
495 | 0 | ts_list, /* servers */ |
496 | 0 | num_requests, /* num_servers */ |
497 | 0 | (struct netlogon_ping_filter){ |
498 | 0 | .ntversion = nt_version, |
499 | 0 | .domain = ads->server.realm, |
500 | 0 | .acct_ctrl = -1, |
501 | 0 | .required_flags = ads->config.flags | |
502 | 0 | DS_ONLY_LDAP_NEEDED, |
503 | 0 | }, |
504 | 0 | 1, /* wanted_servers */ |
505 | 0 | endtime, /* timeout */ |
506 | 0 | &responses); |
507 | 0 | if (!NT_STATUS_IS_OK(status)) { |
508 | 0 | DBG_WARNING("netlogon_pings(realm=%s, num_requests=%zu) " |
509 | 0 | "for count[%zu] - %s\n", |
510 | 0 | ads->server.realm, |
511 | 0 | num_requests, count, |
512 | 0 | nt_errstr(status)); |
513 | 0 | TALLOC_FREE(frame); |
514 | 0 | return NT_STATUS_NO_LOGON_SERVERS; |
515 | 0 | } |
516 | | |
517 | 0 | for (i = 0; i < num_requests; i++) { |
518 | 0 | struct NETLOGON_SAM_LOGON_RESPONSE_EX *cldap_reply = NULL; |
519 | 0 | char server[INET6_ADDRSTRLEN]; |
520 | |
|
521 | 0 | print_sockaddr(server, sizeof(server), &req_sa_list[i]->u.ss); |
522 | |
|
523 | 0 | if (responses[i] == NULL) { |
524 | 0 | add_failed_connection_entry( |
525 | 0 | domain, |
526 | 0 | server, |
527 | 0 | NT_STATUS_INVALID_NETWORK_RESPONSE); |
528 | 0 | continue; |
529 | 0 | } |
530 | | |
531 | 0 | if (responses[i]->ntver != NETLOGON_NT_VERSION_5EX) { |
532 | 0 | DBG_NOTICE("realm=[%s] nt_version mismatch: 0x%08x for %s\n", |
533 | 0 | ads->server.realm, |
534 | 0 | responses[i]->ntver, server); |
535 | 0 | add_failed_connection_entry( |
536 | 0 | domain, |
537 | 0 | server, |
538 | 0 | NT_STATUS_INVALID_NETWORK_RESPONSE); |
539 | 0 | continue; |
540 | 0 | } |
541 | | |
542 | 0 | cldap_reply = &responses[i]->data.nt5_ex; |
543 | |
|
544 | 0 | if (cldap_reply->pdc_dns_name != NULL) { |
545 | 0 | status = check_negative_conn_cache( |
546 | 0 | domain, |
547 | 0 | cldap_reply->pdc_dns_name); |
548 | 0 | if (!NT_STATUS_IS_OK(status)) { |
549 | | /* |
550 | | * only use the server if it's not black listed |
551 | | * by name |
552 | | */ |
553 | 0 | DBG_NOTICE("realm=[%s] server=[%s][%s] " |
554 | 0 | "black listed: %s\n", |
555 | 0 | ads->server.realm, |
556 | 0 | server, |
557 | 0 | cldap_reply->pdc_dns_name, |
558 | 0 | nt_errstr(status)); |
559 | | /* propagate blacklisting from name to ip */ |
560 | 0 | add_failed_connection_entry(domain, |
561 | 0 | server, |
562 | 0 | status); |
563 | 0 | retry = true; |
564 | 0 | continue; |
565 | 0 | } |
566 | 0 | } |
567 | | |
568 | | /* Returns ok only if it matches the correct server type */ |
569 | 0 | ok = ads_fill_cldap_reply(ads, |
570 | 0 | false, |
571 | 0 | &req_sa_list[i]->u.ss, |
572 | 0 | cldap_reply); |
573 | 0 | if (ok) { |
574 | 0 | DBG_DEBUG("realm[%s]: selected %s => %s\n", |
575 | 0 | ads->server.realm, |
576 | 0 | server, cldap_reply->pdc_dns_name); |
577 | 0 | if (CHECK_DEBUGLVL(DBGLVL_DEBUG)) { |
578 | 0 | NDR_PRINT_DEBUG(NETLOGON_SAM_LOGON_RESPONSE_EX, |
579 | 0 | cldap_reply); |
580 | 0 | } |
581 | 0 | TALLOC_FREE(frame); |
582 | 0 | return NT_STATUS_OK; |
583 | 0 | } |
584 | | |
585 | 0 | DBG_NOTICE("realm[%s] server %s %s - not usable\n", |
586 | 0 | ads->server.realm, |
587 | 0 | server, cldap_reply->pdc_dns_name); |
588 | 0 | if (CHECK_DEBUGLVL(DBGLVL_NOTICE)) { |
589 | 0 | NDR_PRINT_DEBUG(NETLOGON_SAM_LOGON_RESPONSE_EX, |
590 | 0 | cldap_reply); |
591 | 0 | } |
592 | 0 | add_failed_connection_entry(domain, server, |
593 | 0 | NT_STATUS_CLIENT_SERVER_PARAMETERS_INVALID); |
594 | 0 | retry = true; |
595 | 0 | } |
596 | | |
597 | 0 | if (retry) { |
598 | 0 | bool expired; |
599 | |
|
600 | 0 | expired = timeval_expired(&endtime); |
601 | 0 | if (!expired) { |
602 | 0 | goto again; |
603 | 0 | } |
604 | 0 | } |
605 | | |
606 | 0 | status = NT_STATUS_NO_LOGON_SERVERS; |
607 | 0 | DBG_WARNING("realm[%s] no valid response " |
608 | 0 | "num_requests[%zu] for count[%zu] - %s\n", |
609 | 0 | ads->server.realm, |
610 | 0 | num_requests, count, nt_errstr(status)); |
611 | 0 | TALLOC_FREE(frame); |
612 | 0 | return NT_STATUS_NO_LOGON_SERVERS; |
613 | 0 | } |
614 | | |
615 | | /*************************************************************************** |
616 | | resolve a name and perform an "ldap ping" using NetBIOS and related methods |
617 | | ****************************************************************************/ |
618 | | |
619 | | static NTSTATUS resolve_and_ping_netbios(ADS_STRUCT *ads, |
620 | | const char *domain, const char *realm) |
621 | 0 | { |
622 | 0 | size_t i; |
623 | 0 | size_t count = 0; |
624 | 0 | struct samba_sockaddr *sa_list = NULL; |
625 | 0 | NTSTATUS status; |
626 | |
|
627 | 0 | DEBUG(6, ("resolve_and_ping_netbios: (cldap) looking for domain '%s'\n", |
628 | 0 | domain)); |
629 | |
|
630 | 0 | status = get_sorted_dc_list(talloc_tos(), |
631 | 0 | domain, |
632 | 0 | NULL, |
633 | 0 | &sa_list, |
634 | 0 | &count, |
635 | 0 | false); |
636 | 0 | if (!NT_STATUS_IS_OK(status)) { |
637 | 0 | return status; |
638 | 0 | } |
639 | | |
640 | | /* remove servers which are known to be dead based on |
641 | | the corresponding DNS method */ |
642 | 0 | if (*realm) { |
643 | 0 | for (i = 0; i < count; ++i) { |
644 | 0 | char server[INET6_ADDRSTRLEN]; |
645 | |
|
646 | 0 | print_sockaddr(server, sizeof(server), &sa_list[i].u.ss); |
647 | |
|
648 | 0 | if(!NT_STATUS_IS_OK( |
649 | 0 | check_negative_conn_cache(realm, server))) { |
650 | | /* Ensure we add the workgroup name for this |
651 | | IP address as negative too. */ |
652 | 0 | add_failed_connection_entry( |
653 | 0 | domain, server, |
654 | 0 | NT_STATUS_UNSUCCESSFUL); |
655 | 0 | } |
656 | 0 | } |
657 | 0 | } |
658 | |
|
659 | 0 | status = cldap_ping_list(ads, domain, sa_list, count); |
660 | |
|
661 | 0 | TALLOC_FREE(sa_list); |
662 | |
|
663 | 0 | return status; |
664 | 0 | } |
665 | | |
666 | | |
667 | | /********************************************************************** |
668 | | resolve a name and perform an "ldap ping" using DNS |
669 | | **********************************************************************/ |
670 | | |
671 | | static NTSTATUS resolve_and_ping_dns(ADS_STRUCT *ads, const char *sitename, |
672 | | const char *realm) |
673 | 0 | { |
674 | 0 | size_t count = 0; |
675 | 0 | struct samba_sockaddr *sa_list = NULL; |
676 | 0 | NTSTATUS status; |
677 | |
|
678 | 0 | DEBUG(6, ("resolve_and_ping_dns: (cldap) looking for realm '%s'\n", |
679 | 0 | realm)); |
680 | |
|
681 | 0 | status = get_sorted_dc_list(talloc_tos(), |
682 | 0 | realm, |
683 | 0 | sitename, |
684 | 0 | &sa_list, |
685 | 0 | &count, |
686 | 0 | true); |
687 | 0 | if (!NT_STATUS_IS_OK(status)) { |
688 | 0 | TALLOC_FREE(sa_list); |
689 | 0 | return status; |
690 | 0 | } |
691 | | |
692 | 0 | status = cldap_ping_list(ads, realm, sa_list, count); |
693 | |
|
694 | 0 | TALLOC_FREE(sa_list); |
695 | |
|
696 | 0 | return status; |
697 | 0 | } |
698 | | |
699 | | /********************************************************************** |
700 | | Try to find an AD dc using our internal name resolution routines |
701 | | Try the realm first and then the workgroup name if netbios is not |
702 | | disabled |
703 | | **********************************************************************/ |
704 | | |
705 | | static NTSTATUS ads_find_dc(ADS_STRUCT *ads) |
706 | 0 | { |
707 | 0 | const char *c_domain = ""; |
708 | 0 | const char *c_realm; |
709 | 0 | bool use_own_domain = False; |
710 | 0 | char *sitename = NULL; |
711 | 0 | NTSTATUS status = NT_STATUS_UNSUCCESSFUL; |
712 | 0 | bool ok = false; |
713 | | |
714 | | /* if the realm and workgroup are both empty, assume they are ours */ |
715 | | |
716 | | /* realm */ |
717 | 0 | c_realm = ads->server.realm; |
718 | |
|
719 | 0 | if (c_realm == NULL) |
720 | 0 | c_realm = ""; |
721 | |
|
722 | 0 | if (!*c_realm) { |
723 | | /* special case where no realm and no workgroup means our own */ |
724 | 0 | if ( !ads->server.workgroup || !*ads->server.workgroup ) { |
725 | 0 | use_own_domain = True; |
726 | 0 | c_realm = lp_realm(); |
727 | 0 | } |
728 | 0 | } |
729 | |
|
730 | 0 | if (!lp_disable_netbios()) { |
731 | 0 | if (use_own_domain) { |
732 | 0 | c_domain = lp_workgroup(); |
733 | 0 | } else { |
734 | 0 | c_domain = ads->server.workgroup; |
735 | 0 | if (!*c_realm && (!c_domain || !*c_domain)) { |
736 | 0 | c_domain = lp_workgroup(); |
737 | 0 | } |
738 | 0 | } |
739 | |
|
740 | 0 | if (!c_domain) { |
741 | 0 | c_domain = ""; |
742 | 0 | } |
743 | 0 | } |
744 | |
|
745 | 0 | if (!*c_realm && !*c_domain) { |
746 | 0 | DEBUG(0, ("ads_find_dc: no realm or workgroup! Don't know " |
747 | 0 | "what to do\n")); |
748 | 0 | return NT_STATUS_INVALID_PARAMETER; /* rather need MISSING_PARAMETER ... */ |
749 | 0 | } |
750 | | |
751 | | /* |
752 | | * In case of LDAP we use get_dc_name() as that |
753 | | * creates the custom krb5.conf file |
754 | | */ |
755 | 0 | if (ads->auth.flags & ADS_AUTH_GENERATE_KRB5_CONFIG) { |
756 | 0 | fstring srv_name; |
757 | 0 | struct sockaddr_storage ip_out; |
758 | |
|
759 | 0 | DEBUG(6, ("ads_find_dc: (ldap) looking for realm '%s'" |
760 | 0 | " and falling back to domain '%s'\n", |
761 | 0 | c_realm, c_domain)); |
762 | |
|
763 | 0 | ok = get_dc_name(c_domain, c_realm, srv_name, &ip_out); |
764 | 0 | if (ok) { |
765 | 0 | if (is_zero_addr(&ip_out)) { |
766 | 0 | return NT_STATUS_NO_LOGON_SERVERS; |
767 | 0 | } |
768 | | |
769 | | /* |
770 | | * we call ads_try_connect() to fill in the |
771 | | * ads->config details |
772 | | */ |
773 | 0 | ok = ads_try_connect(ads, false, &ip_out); |
774 | 0 | if (ok) { |
775 | 0 | return NT_STATUS_OK; |
776 | 0 | } |
777 | 0 | } |
778 | | |
779 | 0 | return NT_STATUS_NO_LOGON_SERVERS; |
780 | 0 | } |
781 | | |
782 | 0 | if (*c_realm) { |
783 | 0 | sitename = sitename_fetch(talloc_tos(), c_realm); |
784 | 0 | status = resolve_and_ping_dns(ads, sitename, c_realm); |
785 | |
|
786 | 0 | if (NT_STATUS_IS_OK(status)) { |
787 | 0 | TALLOC_FREE(sitename); |
788 | 0 | return status; |
789 | 0 | } |
790 | | |
791 | | /* In case we failed to contact one of our closest DC on our |
792 | | * site we |
793 | | * need to try to find another DC, retry with a site-less SRV |
794 | | * DNS query |
795 | | * - Guenther */ |
796 | | |
797 | 0 | if (sitename) { |
798 | 0 | DEBUG(3, ("ads_find_dc: failed to find a valid DC on " |
799 | 0 | "our site (%s), Trying to find another DC " |
800 | 0 | "for realm '%s' (domain '%s')\n", |
801 | 0 | sitename, c_realm, c_domain)); |
802 | 0 | namecache_delete(c_realm, 0x1C); |
803 | 0 | status = |
804 | 0 | resolve_and_ping_dns(ads, NULL, c_realm); |
805 | |
|
806 | 0 | if (NT_STATUS_IS_OK(status)) { |
807 | 0 | TALLOC_FREE(sitename); |
808 | 0 | return status; |
809 | 0 | } |
810 | 0 | } |
811 | | |
812 | 0 | TALLOC_FREE(sitename); |
813 | 0 | } |
814 | | |
815 | | /* try netbios as fallback - if permitted, |
816 | | or if configuration specifically requests it */ |
817 | 0 | if (*c_domain) { |
818 | 0 | if (*c_realm) { |
819 | 0 | DEBUG(3, ("ads_find_dc: falling back to netbios " |
820 | 0 | "name resolution for domain '%s' (realm '%s')\n", |
821 | 0 | c_domain, c_realm)); |
822 | 0 | } |
823 | |
|
824 | 0 | status = resolve_and_ping_netbios(ads, c_domain, c_realm); |
825 | 0 | if (NT_STATUS_IS_OK(status)) { |
826 | 0 | return status; |
827 | 0 | } |
828 | 0 | } |
829 | | |
830 | 0 | DEBUG(1, ("ads_find_dc: " |
831 | 0 | "name resolution for realm '%s' (domain '%s') failed: %s\n", |
832 | 0 | c_realm, c_domain, nt_errstr(status))); |
833 | 0 | return status; |
834 | 0 | } |
835 | | |
836 | | /** |
837 | | * Connect to the LDAP server |
838 | | * @param ads Pointer to an existing ADS_STRUCT |
839 | | * @return status of connection |
840 | | **/ |
841 | | static ADS_STATUS ads_connect_internal(ADS_STRUCT *ads, |
842 | | struct cli_credentials *creds) |
843 | 0 | { |
844 | 0 | int version = LDAP_VERSION3; |
845 | 0 | ADS_STATUS status; |
846 | 0 | NTSTATUS ntstatus; |
847 | 0 | char addr[INET6_ADDRSTRLEN]; |
848 | 0 | struct sockaddr_storage existing_ss; |
849 | 0 | bool tls = false; |
850 | 0 | bool start_tls = false; |
851 | |
|
852 | 0 | zero_sockaddr(&existing_ss); |
853 | |
|
854 | 0 | if (!(ads->auth.flags & ADS_AUTH_NO_BIND)) { |
855 | 0 | SMB_ASSERT(creds != NULL); |
856 | 0 | } |
857 | | |
858 | 0 | if (ads->auth.flags & ADS_AUTH_ANON_BIND) { |
859 | | /* |
860 | | * Simple anonyous binds are only |
861 | | * allowed for anonymous credentials |
862 | | */ |
863 | 0 | SMB_ASSERT(cli_credentials_is_anonymous(creds)); |
864 | 0 | } |
865 | | |
866 | 0 | if (!(ads->auth.flags & (ADS_AUTH_NO_BIND|ADS_AUTH_ANON_BIND))) { |
867 | 0 | ads->auth.flags |= ADS_AUTH_GENERATE_KRB5_CONFIG; |
868 | 0 | } |
869 | | |
870 | | /* |
871 | | * ads_connect can be passed in a reused ADS_STRUCT |
872 | | * with an existing non-zero ads->ldap.ss IP address |
873 | | * that was stored by going through ads_find_dc() |
874 | | * if ads->server.ldap_server was NULL. |
875 | | * |
876 | | * If ads->server.ldap_server is still NULL but |
877 | | * the target address isn't the zero address, then |
878 | | * store that address off off before zeroing out |
879 | | * ads->ldap so we don't keep doing multiple calls |
880 | | * to ads_find_dc() in the reuse case. |
881 | | * |
882 | | * If a caller wants a clean ADS_STRUCT they |
883 | | * will TALLOC_FREE it and allocate a new one |
884 | | * by calling ads_init(), which ensures |
885 | | * ads->ldap.ss is a properly zero'ed out valid IP |
886 | | * address. |
887 | | */ |
888 | 0 | if (ads->server.ldap_server == NULL && !is_zero_addr(&ads->ldap.ss)) { |
889 | | /* Save off the address we previously found by ads_find_dc(). */ |
890 | 0 | existing_ss = ads->ldap.ss; |
891 | 0 | } |
892 | |
|
893 | 0 | ads_zero_ldap(ads); |
894 | 0 | ZERO_STRUCT(ads->ldap_tls_data); |
895 | 0 | ZERO_STRUCT(ads->ldap_wrap_data); |
896 | 0 | ads->ldap.last_attempt = time_mono(NULL); |
897 | 0 | ads->ldap_wrap_data.wrap_type = ADS_SASLWRAP_TYPE_PLAIN; |
898 | | |
899 | | /* try with a user specified server */ |
900 | |
|
901 | 0 | if (DEBUGLEVEL >= 11) { |
902 | 0 | char *s = NDR_PRINT_STRUCT_STRING(talloc_tos(), ads_struct, ads); |
903 | 0 | DEBUG(11,("ads_connect: entering\n")); |
904 | 0 | DEBUGADD(11,("%s\n", s)); |
905 | 0 | TALLOC_FREE(s); |
906 | 0 | } |
907 | |
|
908 | 0 | if (ads->server.ldap_server) { |
909 | 0 | bool ok = false; |
910 | 0 | struct sockaddr_storage ss; |
911 | |
|
912 | 0 | DBG_DEBUG("Resolving name of LDAP server '%s'.\n", |
913 | 0 | ads->server.ldap_server); |
914 | 0 | ok = resolve_name(ads->server.ldap_server, &ss, 0x20, true); |
915 | 0 | if (!ok) { |
916 | 0 | DEBUG(5,("ads_connect: unable to resolve name %s\n", |
917 | 0 | ads->server.ldap_server)); |
918 | 0 | status = ADS_ERROR_NT(NT_STATUS_NOT_FOUND); |
919 | 0 | goto out; |
920 | 0 | } |
921 | | |
922 | 0 | if (is_zero_addr(&ss)) { |
923 | 0 | status = ADS_ERROR_NT(NT_STATUS_NOT_FOUND); |
924 | 0 | goto out; |
925 | 0 | } |
926 | | |
927 | 0 | ok = ads_try_connect(ads, ads->server.gc, &ss); |
928 | 0 | if (ok) { |
929 | 0 | goto got_connection; |
930 | 0 | } |
931 | | |
932 | | /* The choice of which GC use is handled one level up in |
933 | | ads_connect_gc(). If we continue on from here with |
934 | | ads_find_dc() we will get GC searches on port 389 which |
935 | | doesn't work. --jerry */ |
936 | | |
937 | 0 | if (ads->server.gc == true) { |
938 | 0 | return ADS_ERROR(LDAP_OPERATIONS_ERROR); |
939 | 0 | } |
940 | | |
941 | 0 | if (ads->server.no_fallback) { |
942 | 0 | status = ADS_ERROR_NT(NT_STATUS_NOT_FOUND); |
943 | 0 | goto out; |
944 | 0 | } |
945 | 0 | } |
946 | | |
947 | 0 | if (!is_zero_addr(&existing_ss)) { |
948 | | /* We saved off who we should talk to. */ |
949 | 0 | bool ok = ads_try_connect(ads, |
950 | 0 | ads->server.gc, |
951 | 0 | &existing_ss); |
952 | 0 | if (ok) { |
953 | 0 | goto got_connection; |
954 | 0 | } |
955 | | /* |
956 | | * Keep trying to find a server and fall through |
957 | | * into ads_find_dc() again. |
958 | | */ |
959 | 0 | DBG_DEBUG("Failed to connect to DC via LDAP server IP address, " |
960 | 0 | "trying to find another DC.\n"); |
961 | 0 | } |
962 | | |
963 | 0 | ntstatus = ads_find_dc(ads); |
964 | 0 | if (NT_STATUS_IS_OK(ntstatus)) { |
965 | 0 | goto got_connection; |
966 | 0 | } |
967 | | |
968 | 0 | status = ADS_ERROR_NT(ntstatus); |
969 | 0 | goto out; |
970 | | |
971 | 0 | got_connection: |
972 | |
|
973 | 0 | print_sockaddr(addr, sizeof(addr), &ads->ldap.ss); |
974 | 0 | DEBUG(3,("Successfully contacted LDAP server %s\n", addr)); |
975 | |
|
976 | 0 | if (!ads->auth.kdc_server) { |
977 | 0 | print_sockaddr(addr, sizeof(addr), &ads->ldap.ss); |
978 | 0 | ads->auth.kdc_server = talloc_strdup(ads, addr); |
979 | 0 | if (ads->auth.kdc_server == NULL) { |
980 | 0 | status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY); |
981 | 0 | goto out; |
982 | 0 | } |
983 | 0 | } |
984 | | |
985 | | /* If the caller() requested no LDAP bind, then we are done */ |
986 | | |
987 | 0 | if (ads->auth.flags & ADS_AUTH_NO_BIND) { |
988 | 0 | status = ADS_SUCCESS; |
989 | 0 | goto out; |
990 | 0 | } |
991 | | |
992 | 0 | ads->ldap_tls_data.mem_ctx = talloc_init("ads LDAP TLS connection memory"); |
993 | 0 | if (!ads->ldap_tls_data.mem_ctx) { |
994 | 0 | status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY); |
995 | 0 | goto out; |
996 | 0 | } |
997 | | |
998 | 0 | ads->ldap_wrap_data.mem_ctx = talloc_init("ads LDAP connection memory"); |
999 | 0 | if (!ads->ldap_wrap_data.mem_ctx) { |
1000 | 0 | status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY); |
1001 | 0 | goto out; |
1002 | 0 | } |
1003 | | |
1004 | | /* Otherwise setup the TCP LDAP session */ |
1005 | | |
1006 | 0 | if (ads->auth.flags & ADS_AUTH_SASL_LDAPS) { |
1007 | 0 | tls = true; |
1008 | 0 | ads->ldap.port = 636; |
1009 | 0 | } else if (ads->auth.flags & ADS_AUTH_SASL_STARTTLS) { |
1010 | 0 | tls = true; |
1011 | 0 | start_tls = true; |
1012 | 0 | ads->ldap.port = 389; |
1013 | 0 | } else { |
1014 | 0 | ads->ldap.port = 389; |
1015 | 0 | } |
1016 | |
|
1017 | 0 | ads->ldap.ld = ldap_open_with_timeout(ads->config.ldap_server_name, |
1018 | 0 | &ads->ldap.ss, |
1019 | 0 | ads->ldap.port, lp_ldap_timeout()); |
1020 | 0 | if (ads->ldap.ld == NULL) { |
1021 | 0 | status = ADS_ERROR(LDAP_OPERATIONS_ERROR); |
1022 | 0 | goto out; |
1023 | 0 | } |
1024 | 0 | DEBUG(3,("Connected to LDAP server %s\n", ads->config.ldap_server_name)); |
1025 | |
|
1026 | 0 | ldap_set_option(ads->ldap.ld, LDAP_OPT_PROTOCOL_VERSION, &version); |
1027 | |
|
1028 | 0 | if (start_tls) { |
1029 | 0 | unsigned int to = lp_ldap_connection_timeout(); |
1030 | 0 | struct berval *rspdata = NULL; |
1031 | 0 | char *rspoid = NULL; |
1032 | 0 | int rc; |
1033 | |
|
1034 | 0 | if (to) { |
1035 | | /* Setup timeout */ |
1036 | 0 | gotalarm = 0; |
1037 | 0 | CatchSignal(SIGALRM, gotalarm_sig); |
1038 | 0 | alarm(to); |
1039 | | /* End setup timeout. */ |
1040 | 0 | } |
1041 | |
|
1042 | 0 | rc = ldap_extended_operation_s(ads->ldap.ld, |
1043 | 0 | LDAP_EXOP_START_TLS, |
1044 | 0 | NULL, |
1045 | 0 | NULL, |
1046 | 0 | NULL, |
1047 | 0 | &rspoid, |
1048 | 0 | &rspdata); |
1049 | 0 | if (gotalarm != 0 && rc == LDAP_SUCCESS) { |
1050 | 0 | rc = LDAP_TIMEOUT; |
1051 | 0 | } |
1052 | |
|
1053 | 0 | if (to) { |
1054 | | /* Teardown timeout. */ |
1055 | 0 | alarm(0); |
1056 | 0 | CatchSignal(SIGALRM, SIG_IGN); |
1057 | 0 | } |
1058 | |
|
1059 | 0 | if (rspoid != NULL) { |
1060 | 0 | ldap_memfree(rspoid); |
1061 | 0 | } |
1062 | |
|
1063 | 0 | if (rspdata != NULL) { |
1064 | 0 | ber_bvfree(rspdata); |
1065 | 0 | } |
1066 | |
|
1067 | 0 | if (rc != LDAP_SUCCESS) { |
1068 | 0 | status = ADS_ERROR_LDAP(rc); |
1069 | 0 | goto out; |
1070 | 0 | } |
1071 | 0 | } |
1072 | | |
1073 | 0 | if (tls) { |
1074 | 0 | unsigned int to = lp_ldap_connection_timeout(); |
1075 | |
|
1076 | 0 | if (to) { |
1077 | | /* Setup timeout */ |
1078 | 0 | gotalarm = 0; |
1079 | 0 | CatchSignal(SIGALRM, gotalarm_sig); |
1080 | 0 | alarm(to); |
1081 | | /* End setup timeout. */ |
1082 | 0 | } |
1083 | |
|
1084 | 0 | status = ads_setup_tls_wrapping(&ads->ldap_tls_data, |
1085 | 0 | ads->ldap.ld, |
1086 | 0 | ads->config.ldap_server_name); |
1087 | |
|
1088 | 0 | if (to) { |
1089 | | /* Teardown timeout. */ |
1090 | 0 | alarm(0); |
1091 | 0 | CatchSignal(SIGALRM, SIG_IGN); |
1092 | 0 | } |
1093 | |
|
1094 | 0 | if ( !ADS_ERR_OK(status) ) { |
1095 | 0 | goto out; |
1096 | 0 | } |
1097 | 0 | } |
1098 | | |
1099 | | /* cache the successful connection for workgroup and realm */ |
1100 | 0 | if (ads_closest_dc(ads)) { |
1101 | 0 | saf_store( ads->server.workgroup, ads->config.ldap_server_name); |
1102 | 0 | saf_store( ads->server.realm, ads->config.ldap_server_name); |
1103 | 0 | } |
1104 | | |
1105 | | /* fill in the current time and offsets */ |
1106 | |
|
1107 | 0 | status = ads_current_time( ads ); |
1108 | 0 | if ( !ADS_ERR_OK(status) ) { |
1109 | 0 | goto out; |
1110 | 0 | } |
1111 | | |
1112 | | /* Now do the bind */ |
1113 | | |
1114 | 0 | if (ads->auth.flags & ADS_AUTH_ANON_BIND) { |
1115 | 0 | status = ADS_ERROR(ldap_simple_bind_s(ads->ldap.ld, NULL, NULL)); |
1116 | 0 | goto out; |
1117 | 0 | } |
1118 | | |
1119 | 0 | status = ads_sasl_bind(ads, creds); |
1120 | |
|
1121 | 0 | out: |
1122 | 0 | if (DEBUGLEVEL >= 11) { |
1123 | 0 | char *s = NDR_PRINT_STRUCT_STRING(talloc_tos(), ads_struct, ads); |
1124 | 0 | DEBUG(11,("ads_connect: leaving with: %s\n", |
1125 | 0 | ads_errstr(status))); |
1126 | 0 | DEBUGADD(11,("%s\n", s)); |
1127 | 0 | TALLOC_FREE(s); |
1128 | 0 | } |
1129 | |
|
1130 | 0 | return status; |
1131 | 0 | } |
1132 | | |
1133 | | /** |
1134 | | * Connect to the LDAP server using without a bind |
1135 | | * and without a tcp connection at all |
1136 | | * |
1137 | | * @param ads Pointer to an existing ADS_STRUCT |
1138 | | * @return status of connection |
1139 | | **/ |
1140 | | ADS_STATUS ads_connect_cldap_only(ADS_STRUCT *ads) |
1141 | 0 | { |
1142 | 0 | ads->auth.flags |= ADS_AUTH_NO_BIND; |
1143 | 0 | return ads_connect_internal(ads, NULL); |
1144 | 0 | } |
1145 | | |
1146 | | /** |
1147 | | * Connect to the LDAP server |
1148 | | * @param ads Pointer to an existing ADS_STRUCT |
1149 | | * @return status of connection |
1150 | | **/ |
1151 | | ADS_STATUS ads_connect_creds(ADS_STRUCT *ads, struct cli_credentials *creds) |
1152 | 0 | { |
1153 | 0 | SMB_ASSERT(creds != NULL); |
1154 | | |
1155 | | /* |
1156 | | * We allow upgrades from |
1157 | | * ADS_AUTH_NO_BIND if credentials |
1158 | | * are specified |
1159 | | */ |
1160 | 0 | ads->auth.flags &= ~ADS_AUTH_NO_BIND; |
1161 | | |
1162 | | /* |
1163 | | * We allow upgrades from ADS_AUTH_ANON_BIND, |
1164 | | * as we don't want to use simple binds with |
1165 | | * non-anon credentials |
1166 | | */ |
1167 | 0 | if (!cli_credentials_is_anonymous(creds)) { |
1168 | 0 | ads->auth.flags &= ~ADS_AUTH_ANON_BIND; |
1169 | 0 | } |
1170 | |
|
1171 | 0 | return ads_connect_internal(ads, creds); |
1172 | 0 | } |
1173 | | |
1174 | | /** |
1175 | | * Connect to the LDAP server using anonymous credentials |
1176 | | * using a simple bind without username/password |
1177 | | * |
1178 | | * @param ads Pointer to an existing ADS_STRUCT |
1179 | | * @return status of connection |
1180 | | **/ |
1181 | | ADS_STATUS ads_connect_simple_anon(ADS_STRUCT *ads) |
1182 | 0 | { |
1183 | 0 | TALLOC_CTX *frame = talloc_stackframe(); |
1184 | 0 | struct cli_credentials *creds = NULL; |
1185 | 0 | ADS_STATUS status; |
1186 | |
|
1187 | 0 | creds = cli_credentials_init_anon(frame); |
1188 | 0 | if (creds == NULL) { |
1189 | 0 | TALLOC_FREE(frame); |
1190 | 0 | return ADS_ERROR_SYSTEM(errno); |
1191 | 0 | } |
1192 | | |
1193 | 0 | ads->auth.flags |= ADS_AUTH_ANON_BIND; |
1194 | 0 | status = ads_connect_creds(ads, creds); |
1195 | 0 | TALLOC_FREE(frame); |
1196 | 0 | return status; |
1197 | 0 | } |
1198 | | |
1199 | | /** |
1200 | | * Connect to the LDAP server using the machine account |
1201 | | * @param ads Pointer to an existing ADS_STRUCT |
1202 | | * @return status of connection |
1203 | | **/ |
1204 | | ADS_STATUS ads_connect_machine(ADS_STRUCT *ads) |
1205 | 0 | { |
1206 | 0 | TALLOC_CTX *frame = talloc_stackframe(); |
1207 | 0 | struct cli_credentials *creds = NULL; |
1208 | 0 | ADS_STATUS status; |
1209 | 0 | NTSTATUS ntstatus; |
1210 | |
|
1211 | 0 | ntstatus = pdb_get_trust_credentials(ads->server.workgroup, |
1212 | 0 | ads->server.realm, |
1213 | 0 | frame, |
1214 | 0 | &creds); |
1215 | 0 | if (!NT_STATUS_IS_OK(ntstatus)) { |
1216 | 0 | TALLOC_FREE(frame); |
1217 | 0 | return ADS_ERROR_NT(ntstatus); |
1218 | 0 | } |
1219 | | |
1220 | 0 | status = ads_connect_creds(ads, creds); |
1221 | 0 | TALLOC_FREE(frame); |
1222 | 0 | return status; |
1223 | 0 | } |
1224 | | |
1225 | | /* |
1226 | | * Zero out the internal ads->ldap struct and initialize the address to zero IP. |
1227 | | * @param ads Pointer to an existing ADS_STRUCT |
1228 | | * |
1229 | | * Sets the ads->ldap.ss to a valid |
1230 | | * zero ip address that can be detected by |
1231 | | * our is_zero_addr() function. Otherwise |
1232 | | * it is left as AF_UNSPEC (0). |
1233 | | **/ |
1234 | | void ads_zero_ldap(ADS_STRUCT *ads) |
1235 | 0 | { |
1236 | 0 | ZERO_STRUCT(ads->ldap); |
1237 | | /* |
1238 | | * Initialize the sockaddr_storage so we can use |
1239 | | * sockaddr test functions against it. |
1240 | | */ |
1241 | 0 | zero_sockaddr(&ads->ldap.ss); |
1242 | 0 | } |
1243 | | |
1244 | | /** |
1245 | | * Disconnect the LDAP server |
1246 | | * @param ads Pointer to an existing ADS_STRUCT |
1247 | | **/ |
1248 | | void ads_disconnect(ADS_STRUCT *ads) |
1249 | 0 | { |
1250 | 0 | if (ads->ldap.ld) { |
1251 | 0 | ldap_unbind(ads->ldap.ld); |
1252 | 0 | ads->ldap.ld = NULL; |
1253 | 0 | } |
1254 | 0 | if (ads->ldap_tls_data.mem_ctx) { |
1255 | 0 | talloc_free(ads->ldap_tls_data.mem_ctx); |
1256 | 0 | } |
1257 | 0 | if (ads->ldap_wrap_data.wrap_ops && |
1258 | 0 | ads->ldap_wrap_data.wrap_ops->disconnect) { |
1259 | 0 | ads->ldap_wrap_data.wrap_ops->disconnect(&ads->ldap_wrap_data); |
1260 | 0 | } |
1261 | 0 | if (ads->ldap_wrap_data.mem_ctx) { |
1262 | 0 | talloc_free(ads->ldap_wrap_data.mem_ctx); |
1263 | 0 | } |
1264 | 0 | ads_zero_ldap(ads); |
1265 | 0 | ZERO_STRUCT(ads->ldap_tls_data); |
1266 | 0 | ZERO_STRUCT(ads->ldap_wrap_data); |
1267 | 0 | } |
1268 | | |
1269 | | /* |
1270 | | Duplicate a struct berval into talloc'ed memory |
1271 | | */ |
1272 | | static struct berval *dup_berval(TALLOC_CTX *ctx, const struct berval *in_val) |
1273 | 0 | { |
1274 | 0 | struct berval *value; |
1275 | |
|
1276 | 0 | if (!in_val) return NULL; |
1277 | | |
1278 | 0 | value = talloc_zero(ctx, struct berval); |
1279 | 0 | if (value == NULL) |
1280 | 0 | return NULL; |
1281 | 0 | if (in_val->bv_len == 0) return value; |
1282 | | |
1283 | 0 | value->bv_len = in_val->bv_len; |
1284 | 0 | value->bv_val = (char *)talloc_memdup(ctx, in_val->bv_val, |
1285 | 0 | in_val->bv_len); |
1286 | 0 | return value; |
1287 | 0 | } |
1288 | | |
1289 | | /* |
1290 | | Make a values list out of an array of (struct berval *) |
1291 | | */ |
1292 | | static struct berval **ads_dup_values(TALLOC_CTX *ctx, |
1293 | | const struct berval **in_vals) |
1294 | 0 | { |
1295 | 0 | struct berval **values; |
1296 | 0 | int i; |
1297 | |
|
1298 | 0 | if (!in_vals) return NULL; |
1299 | 0 | for (i=0; in_vals[i]; i++) |
1300 | 0 | ; /* count values */ |
1301 | 0 | values = talloc_zero_array(ctx, struct berval *, i+1); |
1302 | 0 | if (!values) return NULL; |
1303 | | |
1304 | 0 | for (i=0; in_vals[i]; i++) { |
1305 | 0 | values[i] = dup_berval(ctx, in_vals[i]); |
1306 | 0 | } |
1307 | 0 | return values; |
1308 | 0 | } |
1309 | | |
1310 | | /* |
1311 | | UTF8-encode a values list out of an array of (char *) |
1312 | | */ |
1313 | | static char **ads_push_strvals(TALLOC_CTX *ctx, const char **in_vals) |
1314 | 0 | { |
1315 | 0 | char **values; |
1316 | 0 | int i; |
1317 | 0 | size_t size; |
1318 | |
|
1319 | 0 | if (!in_vals) return NULL; |
1320 | 0 | for (i=0; in_vals[i]; i++) |
1321 | 0 | ; /* count values */ |
1322 | 0 | values = talloc_zero_array(ctx, char *, i+1); |
1323 | 0 | if (!values) return NULL; |
1324 | | |
1325 | 0 | for (i=0; in_vals[i]; i++) { |
1326 | 0 | if (!push_utf8_talloc(ctx, &values[i], in_vals[i], &size)) { |
1327 | 0 | TALLOC_FREE(values); |
1328 | 0 | return NULL; |
1329 | 0 | } |
1330 | 0 | } |
1331 | 0 | return values; |
1332 | 0 | } |
1333 | | |
1334 | | /* |
1335 | | Pull a (char *) array out of a UTF8-encoded values list |
1336 | | */ |
1337 | | static char **ads_pull_strvals(TALLOC_CTX *ctx, const char **in_vals) |
1338 | 0 | { |
1339 | 0 | char **values; |
1340 | 0 | int i; |
1341 | 0 | size_t converted_size; |
1342 | |
|
1343 | 0 | if (!in_vals) return NULL; |
1344 | 0 | for (i=0; in_vals[i]; i++) |
1345 | 0 | ; /* count values */ |
1346 | 0 | values = talloc_zero_array(ctx, char *, i+1); |
1347 | 0 | if (!values) return NULL; |
1348 | | |
1349 | 0 | for (i=0; in_vals[i]; i++) { |
1350 | 0 | if (!pull_utf8_talloc(ctx, &values[i], in_vals[i], |
1351 | 0 | &converted_size)) { |
1352 | 0 | DEBUG(0,("ads_pull_strvals: pull_utf8_talloc failed: " |
1353 | 0 | "%s\n", strerror(errno))); |
1354 | 0 | } |
1355 | 0 | } |
1356 | 0 | return values; |
1357 | 0 | } |
1358 | | |
1359 | | /** |
1360 | | * Do a search with paged results. cookie must be null on the first |
1361 | | * call, and then returned on each subsequent call. It will be null |
1362 | | * again when the entire search is complete |
1363 | | * @param ads connection to ads server |
1364 | | * @param bind_path Base dn for the search |
1365 | | * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE) |
1366 | | * @param expr Search expression - specified in local charset |
1367 | | * @param attrs Attributes to retrieve - specified in utf8 or ascii |
1368 | | * @param res ** which will contain results - free res* with ads_msgfree() |
1369 | | * @param count Number of entries retrieved on this page |
1370 | | * @param cookie The paged results cookie to be returned on subsequent calls |
1371 | | * @return status of search |
1372 | | **/ |
1373 | | static ADS_STATUS ads_do_paged_search_args(ADS_STRUCT *ads, |
1374 | | const char *bind_path, |
1375 | | int scope, const char *expr, |
1376 | | const char **attrs, void *args, |
1377 | | LDAPMessage **res, |
1378 | | int *count, struct berval **cookie) |
1379 | 0 | { |
1380 | 0 | int rc, i, version; |
1381 | 0 | char *utf8_expr, *utf8_path, **search_attrs = NULL; |
1382 | 0 | size_t converted_size; |
1383 | 0 | LDAPControl PagedResults, NoReferrals, ExternalCtrl, *controls[4], **rcontrols; |
1384 | 0 | BerElement *cookie_be = NULL; |
1385 | 0 | struct berval *cookie_bv= NULL; |
1386 | 0 | BerElement *ext_be = NULL; |
1387 | 0 | struct berval *ext_bv= NULL; |
1388 | |
|
1389 | 0 | TALLOC_CTX *ctx; |
1390 | 0 | ads_control *external_control = (ads_control *) args; |
1391 | |
|
1392 | 0 | *res = NULL; |
1393 | |
|
1394 | 0 | if (!(ctx = talloc_init("ads_do_paged_search_args"))) |
1395 | 0 | return ADS_ERROR(LDAP_NO_MEMORY); |
1396 | | |
1397 | | /* 0 means the conversion worked but the result was empty |
1398 | | so we only fail if it's -1. In any case, it always |
1399 | | at least nulls out the dest */ |
1400 | 0 | if (!push_utf8_talloc(ctx, &utf8_expr, expr, &converted_size) || |
1401 | 0 | !push_utf8_talloc(ctx, &utf8_path, bind_path, &converted_size)) |
1402 | 0 | { |
1403 | 0 | rc = LDAP_NO_MEMORY; |
1404 | 0 | goto done; |
1405 | 0 | } |
1406 | | |
1407 | 0 | if (!attrs || !(*attrs)) |
1408 | 0 | search_attrs = NULL; |
1409 | 0 | else { |
1410 | | /* This would be the utf8-encoded version...*/ |
1411 | | /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */ |
1412 | 0 | if (!(search_attrs = str_list_copy(talloc_tos(), attrs))) { |
1413 | 0 | rc = LDAP_NO_MEMORY; |
1414 | 0 | goto done; |
1415 | 0 | } |
1416 | 0 | } |
1417 | | |
1418 | | /* Paged results only available on ldap v3 or later */ |
1419 | 0 | ldap_get_option(ads->ldap.ld, LDAP_OPT_PROTOCOL_VERSION, &version); |
1420 | 0 | if (version < LDAP_VERSION3) { |
1421 | 0 | rc = LDAP_NOT_SUPPORTED; |
1422 | 0 | goto done; |
1423 | 0 | } |
1424 | | |
1425 | 0 | cookie_be = ber_alloc_t(LBER_USE_DER); |
1426 | 0 | if (*cookie) { |
1427 | 0 | ber_printf(cookie_be, "{iO}", (ber_int_t) ads->config.ldap_page_size, *cookie); |
1428 | 0 | ber_bvfree(*cookie); /* don't need it from last time */ |
1429 | 0 | *cookie = NULL; |
1430 | 0 | } else { |
1431 | 0 | ber_printf(cookie_be, "{io}", (ber_int_t) ads->config.ldap_page_size, "", 0); |
1432 | 0 | } |
1433 | 0 | ber_flatten(cookie_be, &cookie_bv); |
1434 | 0 | PagedResults.ldctl_oid = discard_const_p(char, ADS_PAGE_CTL_OID); |
1435 | 0 | PagedResults.ldctl_iscritical = (char) 1; |
1436 | 0 | PagedResults.ldctl_value.bv_len = cookie_bv->bv_len; |
1437 | 0 | PagedResults.ldctl_value.bv_val = cookie_bv->bv_val; |
1438 | |
|
1439 | 0 | NoReferrals.ldctl_oid = discard_const_p(char, ADS_NO_REFERRALS_OID); |
1440 | 0 | NoReferrals.ldctl_iscritical = (char) 0; |
1441 | 0 | NoReferrals.ldctl_value.bv_len = 0; |
1442 | 0 | NoReferrals.ldctl_value.bv_val = discard_const_p(char, ""); |
1443 | |
|
1444 | 0 | if (external_control && |
1445 | 0 | (strequal(external_control->control, ADS_EXTENDED_DN_OID) || |
1446 | 0 | strequal(external_control->control, ADS_SD_FLAGS_OID))) { |
1447 | |
|
1448 | 0 | ExternalCtrl.ldctl_oid = discard_const_p(char, external_control->control); |
1449 | 0 | ExternalCtrl.ldctl_iscritical = (char) external_control->critical; |
1450 | | |
1451 | | /* win2k does not accept a ldctl_value being passed in */ |
1452 | |
|
1453 | 0 | if (external_control->val != 0) { |
1454 | |
|
1455 | 0 | if ((ext_be = ber_alloc_t(LBER_USE_DER)) == NULL ) { |
1456 | 0 | rc = LDAP_NO_MEMORY; |
1457 | 0 | goto done; |
1458 | 0 | } |
1459 | | |
1460 | 0 | if ((ber_printf(ext_be, "{i}", (ber_int_t) external_control->val)) == -1) { |
1461 | 0 | rc = LDAP_NO_MEMORY; |
1462 | 0 | goto done; |
1463 | 0 | } |
1464 | 0 | if ((ber_flatten(ext_be, &ext_bv)) == -1) { |
1465 | 0 | rc = LDAP_NO_MEMORY; |
1466 | 0 | goto done; |
1467 | 0 | } |
1468 | | |
1469 | 0 | ExternalCtrl.ldctl_value.bv_len = ext_bv->bv_len; |
1470 | 0 | ExternalCtrl.ldctl_value.bv_val = ext_bv->bv_val; |
1471 | |
|
1472 | 0 | } else { |
1473 | 0 | ExternalCtrl.ldctl_value.bv_len = 0; |
1474 | 0 | ExternalCtrl.ldctl_value.bv_val = NULL; |
1475 | 0 | } |
1476 | | |
1477 | 0 | controls[0] = &NoReferrals; |
1478 | 0 | controls[1] = &PagedResults; |
1479 | 0 | controls[2] = &ExternalCtrl; |
1480 | 0 | controls[3] = NULL; |
1481 | |
|
1482 | 0 | } else { |
1483 | 0 | controls[0] = &NoReferrals; |
1484 | 0 | controls[1] = &PagedResults; |
1485 | 0 | controls[2] = NULL; |
1486 | 0 | } |
1487 | | |
1488 | | /* we need to disable referrals as the openldap libs don't |
1489 | | handle them and paged results at the same time. Using them |
1490 | | together results in the result record containing the server |
1491 | | page control being removed from the result list (tridge/jmcd) |
1492 | | |
1493 | | leaving this in despite the control that says don't generate |
1494 | | referrals, in case the server doesn't support it (jmcd) |
1495 | | */ |
1496 | 0 | ldap_set_option(ads->ldap.ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF); |
1497 | |
|
1498 | 0 | rc = ldap_search_with_timeout(ads->ldap.ld, utf8_path, scope, utf8_expr, |
1499 | 0 | search_attrs, 0, controls, |
1500 | 0 | NULL, LDAP_NO_LIMIT, |
1501 | 0 | (LDAPMessage **)res); |
1502 | |
|
1503 | 0 | ber_free(cookie_be, 1); |
1504 | 0 | ber_bvfree(cookie_bv); |
1505 | |
|
1506 | 0 | if (rc) { |
1507 | 0 | DEBUG(3,("ads_do_paged_search_args: ldap_search_with_timeout(%s) -> %s\n", expr, |
1508 | 0 | ldap_err2string(rc))); |
1509 | 0 | if (rc == LDAP_OTHER) { |
1510 | 0 | char *ldap_errmsg; |
1511 | 0 | int ret; |
1512 | |
|
1513 | 0 | ret = ldap_parse_result(ads->ldap.ld, |
1514 | 0 | *res, |
1515 | 0 | NULL, |
1516 | 0 | NULL, |
1517 | 0 | &ldap_errmsg, |
1518 | 0 | NULL, |
1519 | 0 | NULL, |
1520 | 0 | 0); |
1521 | 0 | if (ret == LDAP_SUCCESS) { |
1522 | 0 | DEBUG(3, ("ldap_search_with_timeout(%s) " |
1523 | 0 | "error: %s\n", expr, ldap_errmsg)); |
1524 | 0 | ldap_memfree(ldap_errmsg); |
1525 | 0 | } |
1526 | 0 | } |
1527 | 0 | goto done; |
1528 | 0 | } |
1529 | | |
1530 | 0 | rc = ldap_parse_result(ads->ldap.ld, *res, NULL, NULL, NULL, |
1531 | 0 | NULL, &rcontrols, 0); |
1532 | |
|
1533 | 0 | if (!rcontrols) { |
1534 | 0 | goto done; |
1535 | 0 | } |
1536 | | |
1537 | 0 | for (i=0; rcontrols[i]; i++) { |
1538 | 0 | if (strcmp(ADS_PAGE_CTL_OID, rcontrols[i]->ldctl_oid) == 0) { |
1539 | 0 | cookie_be = ber_init(&rcontrols[i]->ldctl_value); |
1540 | 0 | ber_scanf(cookie_be,"{iO}", (ber_int_t *) count, |
1541 | 0 | &cookie_bv); |
1542 | | /* the berval is the cookie, but must be freed when |
1543 | | it is all done */ |
1544 | 0 | if (cookie_bv->bv_len) /* still more to do */ |
1545 | 0 | *cookie=ber_bvdup(cookie_bv); |
1546 | 0 | else |
1547 | 0 | *cookie=NULL; |
1548 | 0 | ber_bvfree(cookie_bv); |
1549 | 0 | ber_free(cookie_be, 1); |
1550 | 0 | break; |
1551 | 0 | } |
1552 | 0 | } |
1553 | 0 | ldap_controls_free(rcontrols); |
1554 | |
|
1555 | 0 | done: |
1556 | 0 | TALLOC_FREE(ctx); |
1557 | |
|
1558 | 0 | if (ext_be) { |
1559 | 0 | ber_free(ext_be, 1); |
1560 | 0 | } |
1561 | |
|
1562 | 0 | if (ext_bv) { |
1563 | 0 | ber_bvfree(ext_bv); |
1564 | 0 | } |
1565 | |
|
1566 | 0 | if (rc != LDAP_SUCCESS && *res != NULL) { |
1567 | 0 | ads_msgfree(ads, *res); |
1568 | 0 | *res = NULL; |
1569 | 0 | } |
1570 | | |
1571 | | /* if/when we decide to utf8-encode attrs, take out this next line */ |
1572 | 0 | TALLOC_FREE(search_attrs); |
1573 | |
|
1574 | 0 | return ADS_ERROR(rc); |
1575 | 0 | } |
1576 | | |
1577 | | static ADS_STATUS ads_do_paged_search(ADS_STRUCT *ads, const char *bind_path, |
1578 | | int scope, const char *expr, |
1579 | | const char **attrs, LDAPMessage **res, |
1580 | | int *count, struct berval **cookie) |
1581 | 0 | { |
1582 | 0 | return ads_do_paged_search_args(ads, bind_path, scope, expr, attrs, NULL, res, count, cookie); |
1583 | 0 | } |
1584 | | |
1585 | | |
1586 | | /** |
1587 | | * Get all results for a search. This uses ads_do_paged_search() to return |
1588 | | * all entries in a large search. |
1589 | | * @param ads connection to ads server |
1590 | | * @param bind_path Base dn for the search |
1591 | | * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE) |
1592 | | * @param expr Search expression |
1593 | | * @param attrs Attributes to retrieve |
1594 | | * @param res ** which will contain results - free res* with ads_msgfree() |
1595 | | * @return status of search |
1596 | | **/ |
1597 | | ADS_STATUS ads_do_search_all_args(ADS_STRUCT *ads, const char *bind_path, |
1598 | | int scope, const char *expr, |
1599 | | const char **attrs, void *args, |
1600 | | LDAPMessage **res) |
1601 | 0 | { |
1602 | 0 | struct berval *cookie = NULL; |
1603 | 0 | int count = 0; |
1604 | 0 | ADS_STATUS status; |
1605 | |
|
1606 | 0 | *res = NULL; |
1607 | 0 | status = ads_do_paged_search_args(ads, bind_path, scope, expr, attrs, args, res, |
1608 | 0 | &count, &cookie); |
1609 | |
|
1610 | 0 | if (!ADS_ERR_OK(status)) |
1611 | 0 | return status; |
1612 | | |
1613 | 0 | #ifdef HAVE_LDAP_ADD_RESULT_ENTRY |
1614 | 0 | while (cookie) { |
1615 | 0 | LDAPMessage *res2 = NULL; |
1616 | 0 | LDAPMessage *msg, *next; |
1617 | |
|
1618 | 0 | status = ads_do_paged_search_args(ads, bind_path, scope, expr, |
1619 | 0 | attrs, args, &res2, &count, &cookie); |
1620 | 0 | if (!ADS_ERR_OK(status)) { |
1621 | 0 | break; |
1622 | 0 | } |
1623 | | |
1624 | | /* this relies on the way that ldap_add_result_entry() works internally. I hope |
1625 | | that this works on all ldap libs, but I have only tested with openldap */ |
1626 | 0 | for (msg = ads_first_message(ads, res2); msg; msg = next) { |
1627 | 0 | next = ads_next_message(ads, msg); |
1628 | 0 | ldap_add_result_entry((LDAPMessage **)res, msg); |
1629 | 0 | } |
1630 | | /* note that we do not free res2, as the memory is now |
1631 | | part of the main returned list */ |
1632 | 0 | } |
1633 | | #else |
1634 | | DEBUG(0, ("no ldap_add_result_entry() support in LDAP libs!\n")); |
1635 | | status = ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL); |
1636 | | #endif |
1637 | |
|
1638 | 0 | return status; |
1639 | 0 | } |
1640 | | |
1641 | | ADS_STATUS ads_do_search_all(ADS_STRUCT *ads, const char *bind_path, |
1642 | | int scope, const char *expr, |
1643 | | const char **attrs, LDAPMessage **res) |
1644 | 0 | { |
1645 | 0 | return ads_do_search_all_args(ads, bind_path, scope, expr, attrs, NULL, res); |
1646 | 0 | } |
1647 | | |
1648 | | ADS_STATUS ads_do_search_all_sd_flags(ADS_STRUCT *ads, const char *bind_path, |
1649 | | int scope, const char *expr, |
1650 | | const char **attrs, uint32_t sd_flags, |
1651 | | LDAPMessage **res) |
1652 | 0 | { |
1653 | 0 | ads_control args; |
1654 | |
|
1655 | 0 | args.control = ADS_SD_FLAGS_OID; |
1656 | 0 | args.val = sd_flags; |
1657 | 0 | args.critical = True; |
1658 | |
|
1659 | 0 | return ads_do_search_all_args(ads, bind_path, scope, expr, attrs, &args, res); |
1660 | 0 | } |
1661 | | |
1662 | | |
1663 | | /** |
1664 | | * Run a function on all results for a search. Uses ads_do_paged_search() and |
1665 | | * runs the function as each page is returned, using ads_process_results() |
1666 | | * @param ads connection to ads server |
1667 | | * @param bind_path Base dn for the search |
1668 | | * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE) |
1669 | | * @param expr Search expression - specified in local charset |
1670 | | * @param attrs Attributes to retrieve - specified in UTF-8 or ascii |
1671 | | * @param fn Function which takes attr name, values list, and data_area |
1672 | | * @param data_area Pointer which is passed to function on each call |
1673 | | * @return status of search |
1674 | | **/ |
1675 | | ADS_STATUS ads_do_search_all_fn(ADS_STRUCT *ads, const char *bind_path, |
1676 | | int scope, const char *expr, const char **attrs, |
1677 | | bool (*fn)(ADS_STRUCT *, char *, void **, void *), |
1678 | | void *data_area) |
1679 | 0 | { |
1680 | 0 | struct berval *cookie = NULL; |
1681 | 0 | int count = 0; |
1682 | 0 | ADS_STATUS status; |
1683 | 0 | LDAPMessage *res; |
1684 | |
|
1685 | 0 | status = ads_do_paged_search(ads, bind_path, scope, expr, attrs, &res, |
1686 | 0 | &count, &cookie); |
1687 | |
|
1688 | 0 | if (!ADS_ERR_OK(status)) return status; |
1689 | | |
1690 | 0 | ads_process_results(ads, res, fn, data_area); |
1691 | 0 | ads_msgfree(ads, res); |
1692 | |
|
1693 | 0 | while (cookie) { |
1694 | 0 | status = ads_do_paged_search(ads, bind_path, scope, expr, attrs, |
1695 | 0 | &res, &count, &cookie); |
1696 | |
|
1697 | 0 | if (!ADS_ERR_OK(status)) break; |
1698 | | |
1699 | 0 | ads_process_results(ads, res, fn, data_area); |
1700 | 0 | ads_msgfree(ads, res); |
1701 | 0 | } |
1702 | |
|
1703 | 0 | return status; |
1704 | 0 | } |
1705 | | |
1706 | | /** |
1707 | | * Do a search with a timeout. |
1708 | | * @param ads connection to ads server |
1709 | | * @param bind_path Base dn for the search |
1710 | | * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE) |
1711 | | * @param expr Search expression |
1712 | | * @param attrs Attributes to retrieve |
1713 | | * @param res ** which will contain results - free res* with ads_msgfree() |
1714 | | * @return status of search |
1715 | | **/ |
1716 | | ADS_STATUS ads_do_search(ADS_STRUCT *ads, const char *bind_path, int scope, |
1717 | | const char *expr, |
1718 | | const char **attrs, LDAPMessage **res) |
1719 | 0 | { |
1720 | 0 | int rc; |
1721 | 0 | char *utf8_expr, *utf8_path, **search_attrs = NULL; |
1722 | 0 | size_t converted_size; |
1723 | 0 | TALLOC_CTX *ctx; |
1724 | |
|
1725 | 0 | *res = NULL; |
1726 | 0 | if (!(ctx = talloc_init("ads_do_search"))) { |
1727 | 0 | DEBUG(1,("ads_do_search: talloc_init() failed!\n")); |
1728 | 0 | return ADS_ERROR(LDAP_NO_MEMORY); |
1729 | 0 | } |
1730 | | |
1731 | | /* 0 means the conversion worked but the result was empty |
1732 | | so we only fail if it's negative. In any case, it always |
1733 | | at least nulls out the dest */ |
1734 | 0 | if (!push_utf8_talloc(ctx, &utf8_expr, expr, &converted_size) || |
1735 | 0 | !push_utf8_talloc(ctx, &utf8_path, bind_path, &converted_size)) |
1736 | 0 | { |
1737 | 0 | DEBUG(1,("ads_do_search: push_utf8_talloc() failed!\n")); |
1738 | 0 | rc = LDAP_NO_MEMORY; |
1739 | 0 | goto done; |
1740 | 0 | } |
1741 | | |
1742 | 0 | if (!attrs || !(*attrs)) |
1743 | 0 | search_attrs = NULL; |
1744 | 0 | else { |
1745 | | /* This would be the utf8-encoded version...*/ |
1746 | | /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */ |
1747 | 0 | if (!(search_attrs = str_list_copy(talloc_tos(), attrs))) |
1748 | 0 | { |
1749 | 0 | DEBUG(1,("ads_do_search: str_list_copy() failed!\n")); |
1750 | 0 | rc = LDAP_NO_MEMORY; |
1751 | 0 | goto done; |
1752 | 0 | } |
1753 | 0 | } |
1754 | | |
1755 | | /* see the note in ads_do_paged_search - we *must* disable referrals */ |
1756 | 0 | ldap_set_option(ads->ldap.ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF); |
1757 | |
|
1758 | 0 | rc = ldap_search_with_timeout(ads->ldap.ld, utf8_path, scope, utf8_expr, |
1759 | 0 | search_attrs, 0, NULL, NULL, |
1760 | 0 | LDAP_NO_LIMIT, |
1761 | 0 | (LDAPMessage **)res); |
1762 | |
|
1763 | 0 | if (rc == LDAP_SIZELIMIT_EXCEEDED) { |
1764 | 0 | DEBUG(3,("Warning! sizelimit exceeded in ldap. Truncating.\n")); |
1765 | 0 | rc = 0; |
1766 | 0 | } |
1767 | |
|
1768 | 0 | done: |
1769 | 0 | TALLOC_FREE(ctx); |
1770 | | /* if/when we decide to utf8-encode attrs, take out this next line */ |
1771 | 0 | TALLOC_FREE(search_attrs); |
1772 | 0 | return ADS_ERROR(rc); |
1773 | 0 | } |
1774 | | /** |
1775 | | * Do a general ADS search |
1776 | | * @param ads connection to ads server |
1777 | | * @param res ** which will contain results - free res* with ads_msgfree() |
1778 | | * @param expr Search expression |
1779 | | * @param attrs Attributes to retrieve |
1780 | | * @return status of search |
1781 | | **/ |
1782 | | ADS_STATUS ads_search(ADS_STRUCT *ads, LDAPMessage **res, |
1783 | | const char *expr, const char **attrs) |
1784 | 0 | { |
1785 | 0 | return ads_do_search(ads, ads->config.bind_path, LDAP_SCOPE_SUBTREE, |
1786 | 0 | expr, attrs, res); |
1787 | 0 | } |
1788 | | |
1789 | | /** |
1790 | | * Do a search on a specific DistinguishedName |
1791 | | * @param ads connection to ads server |
1792 | | * @param res ** which will contain results - free res* with ads_msgfree() |
1793 | | * @param dn DistinguishedName to search |
1794 | | * @param attrs Attributes to retrieve |
1795 | | * @return status of search |
1796 | | **/ |
1797 | | ADS_STATUS ads_search_dn(ADS_STRUCT *ads, LDAPMessage **res, |
1798 | | const char *dn, const char **attrs) |
1799 | 0 | { |
1800 | 0 | return ads_do_search(ads, dn, LDAP_SCOPE_BASE, "(objectclass=*)", |
1801 | 0 | attrs, res); |
1802 | 0 | } |
1803 | | |
1804 | | /** |
1805 | | * Free up memory from a ads_search |
1806 | | * @param ads connection to ads server |
1807 | | * @param msg Search results to free |
1808 | | **/ |
1809 | | void ads_msgfree(ADS_STRUCT *ads, LDAPMessage *msg) |
1810 | 0 | { |
1811 | 0 | if (!msg) return; |
1812 | 0 | ldap_msgfree(msg); |
1813 | 0 | } |
1814 | | |
1815 | | /** |
1816 | | * Get a dn from search results |
1817 | | * @param ads connection to ads server |
1818 | | * @param msg Search result |
1819 | | * @return dn string |
1820 | | **/ |
1821 | | char *ads_get_dn(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, LDAPMessage *msg) |
1822 | 0 | { |
1823 | 0 | char *utf8_dn, *unix_dn; |
1824 | 0 | size_t converted_size; |
1825 | |
|
1826 | 0 | utf8_dn = ldap_get_dn(ads->ldap.ld, msg); |
1827 | |
|
1828 | 0 | if (!utf8_dn) { |
1829 | 0 | DEBUG (5, ("ads_get_dn: ldap_get_dn failed\n")); |
1830 | 0 | return NULL; |
1831 | 0 | } |
1832 | | |
1833 | 0 | if (!pull_utf8_talloc(mem_ctx, &unix_dn, utf8_dn, &converted_size)) { |
1834 | 0 | DEBUG(0,("ads_get_dn: string conversion failure utf8 [%s]\n", |
1835 | 0 | utf8_dn )); |
1836 | 0 | return NULL; |
1837 | 0 | } |
1838 | 0 | ldap_memfree(utf8_dn); |
1839 | 0 | return unix_dn; |
1840 | 0 | } |
1841 | | |
1842 | | /** |
1843 | | * Get the parent from a dn |
1844 | | * @param dn the dn to return the parent from |
1845 | | * @return parent dn string |
1846 | | **/ |
1847 | | char *ads_parent_dn(const char *dn) |
1848 | 0 | { |
1849 | 0 | char *p; |
1850 | |
|
1851 | 0 | if (dn == NULL) { |
1852 | 0 | return NULL; |
1853 | 0 | } |
1854 | | |
1855 | 0 | p = strchr(dn, ','); |
1856 | |
|
1857 | 0 | if (p == NULL) { |
1858 | 0 | return NULL; |
1859 | 0 | } |
1860 | | |
1861 | 0 | return p+1; |
1862 | 0 | } |
1863 | | |
1864 | | /** |
1865 | | * Find a machine account given a hostname |
1866 | | * @param ads connection to ads server |
1867 | | * @param res ** which will contain results - free res* with ads_msgfree() |
1868 | | * @param host Hostname to search for |
1869 | | * @return status of search |
1870 | | **/ |
1871 | | ADS_STATUS ads_find_machine_acct(ADS_STRUCT *ads, LDAPMessage **res, |
1872 | | const char *machine) |
1873 | 0 | { |
1874 | 0 | ADS_STATUS status; |
1875 | 0 | char *expr; |
1876 | 0 | const char *attrs[] = { |
1877 | | /* This is how Windows checks for machine accounts */ |
1878 | 0 | "objectClass", |
1879 | 0 | "sAMAccountName", |
1880 | 0 | "userAccountControl", |
1881 | 0 | "DnsHostName", |
1882 | 0 | "ServicePrincipalName", |
1883 | 0 | "userPrincipalName", |
1884 | | |
1885 | | /* Additional attributes Samba checks */ |
1886 | 0 | "msDS-KeyVersionNumber", |
1887 | 0 | "msDS-AdditionalDnsHostName", |
1888 | 0 | "msDS-SupportedEncryptionTypes", |
1889 | 0 | "nTSecurityDescriptor", |
1890 | 0 | "objectSid", |
1891 | |
|
1892 | 0 | NULL |
1893 | 0 | }; |
1894 | 0 | TALLOC_CTX *frame = talloc_stackframe(); |
1895 | |
|
1896 | 0 | *res = NULL; |
1897 | | |
1898 | | /* the easiest way to find a machine account anywhere in the tree |
1899 | | is to look for hostname$ */ |
1900 | 0 | expr = talloc_asprintf(frame, "(sAMAccountName=%s$)", machine); |
1901 | 0 | if (expr == NULL) { |
1902 | 0 | status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY); |
1903 | 0 | goto done; |
1904 | 0 | } |
1905 | | |
1906 | 0 | status = ads_search(ads, res, expr, attrs); |
1907 | 0 | if (ADS_ERR_OK(status)) { |
1908 | 0 | if (ads_count_replies(ads, *res) != 1) { |
1909 | 0 | status = ADS_ERROR_LDAP(LDAP_NO_SUCH_OBJECT); |
1910 | 0 | } |
1911 | 0 | } |
1912 | |
|
1913 | 0 | done: |
1914 | 0 | TALLOC_FREE(frame); |
1915 | 0 | return status; |
1916 | 0 | } |
1917 | | |
1918 | | /** |
1919 | | * Initialize a list of mods to be used in a modify request |
1920 | | * @param ctx An initialized TALLOC_CTX |
1921 | | * @return allocated ADS_MODLIST |
1922 | | **/ |
1923 | | ADS_MODLIST ads_init_mods(TALLOC_CTX *ctx) |
1924 | 0 | { |
1925 | 0 | #define ADS_MODLIST_ALLOC_SIZE 10 |
1926 | 0 | LDAPMod **mods; |
1927 | |
|
1928 | 0 | if ((mods = talloc_zero_array(ctx, LDAPMod *, ADS_MODLIST_ALLOC_SIZE + 1))) |
1929 | | /* -1 is safety to make sure we don't go over the end. |
1930 | | need to reset it to NULL before doing ldap modify */ |
1931 | 0 | mods[ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1; |
1932 | |
|
1933 | 0 | return (ADS_MODLIST)mods; |
1934 | 0 | } |
1935 | | |
1936 | | |
1937 | | /* |
1938 | | add an attribute to the list, with values list already constructed |
1939 | | */ |
1940 | | static ADS_STATUS ads_modlist_add(TALLOC_CTX *ctx, ADS_MODLIST *mods, |
1941 | | int mod_op, const char *name, |
1942 | | const void *_invals) |
1943 | 0 | { |
1944 | 0 | int curmod; |
1945 | 0 | LDAPMod **modlist = (LDAPMod **) *mods; |
1946 | 0 | struct berval **ber_values = NULL; |
1947 | 0 | char **char_values = NULL; |
1948 | |
|
1949 | 0 | if (!_invals) { |
1950 | 0 | mod_op = LDAP_MOD_DELETE; |
1951 | 0 | } else { |
1952 | 0 | if (mod_op & LDAP_MOD_BVALUES) { |
1953 | 0 | const struct berval **b; |
1954 | 0 | b = discard_const_p(const struct berval *, _invals); |
1955 | 0 | ber_values = ads_dup_values(ctx, b); |
1956 | 0 | } else { |
1957 | 0 | const char **c; |
1958 | 0 | c = discard_const_p(const char *, _invals); |
1959 | 0 | char_values = ads_push_strvals(ctx, c); |
1960 | 0 | } |
1961 | 0 | } |
1962 | | |
1963 | | /* find the first empty slot */ |
1964 | 0 | for (curmod=0; modlist[curmod] && modlist[curmod] != (LDAPMod *) -1; |
1965 | 0 | curmod++); |
1966 | 0 | if (modlist[curmod] == (LDAPMod *) -1) { |
1967 | 0 | if (!(modlist = talloc_realloc(ctx, modlist, LDAPMod *, |
1968 | 0 | curmod+ADS_MODLIST_ALLOC_SIZE+1))) |
1969 | 0 | return ADS_ERROR(LDAP_NO_MEMORY); |
1970 | 0 | memset(&modlist[curmod], 0, |
1971 | 0 | ADS_MODLIST_ALLOC_SIZE*sizeof(LDAPMod *)); |
1972 | 0 | modlist[curmod+ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1; |
1973 | 0 | *mods = (ADS_MODLIST)modlist; |
1974 | 0 | } |
1975 | | |
1976 | 0 | if (!(modlist[curmod] = talloc_zero(ctx, LDAPMod))) |
1977 | 0 | return ADS_ERROR(LDAP_NO_MEMORY); |
1978 | 0 | modlist[curmod]->mod_type = talloc_strdup(ctx, name); |
1979 | 0 | if (mod_op & LDAP_MOD_BVALUES) { |
1980 | 0 | modlist[curmod]->mod_bvalues = ber_values; |
1981 | 0 | } else if (mod_op & LDAP_MOD_DELETE) { |
1982 | 0 | modlist[curmod]->mod_values = NULL; |
1983 | 0 | } else { |
1984 | 0 | modlist[curmod]->mod_values = char_values; |
1985 | 0 | } |
1986 | |
|
1987 | 0 | modlist[curmod]->mod_op = mod_op; |
1988 | 0 | return ADS_ERROR(LDAP_SUCCESS); |
1989 | 0 | } |
1990 | | |
1991 | | /* |
1992 | | dump a ADS_MODSLIST via DEBUG |
1993 | | */ |
1994 | | static void ads_dump_modlist(ADS_MODLIST *mods) |
1995 | 0 | { |
1996 | 0 | LDAPMod **modlist = (LDAPMod **)*mods; |
1997 | 0 | const char *op = NULL; |
1998 | 0 | size_t i, j; |
1999 | 0 | char *buf = NULL; |
2000 | |
|
2001 | 0 | if (mods == NULL || DEBUGLEVEL < DBGLVL_DEBUG) { |
2002 | 0 | return; |
2003 | 0 | } |
2004 | | |
2005 | 0 | buf = talloc_strdup(talloc_tos(), ""); |
2006 | |
|
2007 | 0 | for (i = 0; modlist[i] != NULL; i++) { |
2008 | | |
2009 | | /* only ever used three ops */ |
2010 | |
|
2011 | 0 | switch (modlist[i]->mod_op) { |
2012 | 0 | case LDAP_MOD_DELETE: |
2013 | 0 | op = "LDAP_MOD_DELETE"; |
2014 | 0 | break; |
2015 | 0 | case LDAP_MOD_REPLACE: |
2016 | 0 | op = "LDAP_MOD_REPLACE"; |
2017 | 0 | break; |
2018 | 0 | case LDAP_MOD_REPLACE | LDAP_MOD_BVALUES: |
2019 | 0 | op = "LDAP_MOD_REPLACE | LDAP_MOD_BVALUES"; |
2020 | 0 | break; |
2021 | 0 | default: |
2022 | 0 | op = "unknown"; |
2023 | 0 | break; |
2024 | 0 | } |
2025 | | |
2026 | 0 | talloc_asprintf_addbuf(&buf, "mod[%zu]: mod_op: %s\n", i, op); |
2027 | 0 | talloc_asprintf_addbuf(&buf, |
2028 | 0 | "mod[%zu]: mod_type: %s\n", |
2029 | 0 | i, |
2030 | 0 | modlist[i]->mod_type); |
2031 | |
|
2032 | 0 | if (modlist[i]->mod_op & LDAP_MOD_BVALUES) { |
2033 | 0 | continue; |
2034 | 0 | } |
2035 | | |
2036 | 0 | for (j = 0; modlist[i]->mod_values[j] != NULL; j++) { |
2037 | 0 | talloc_asprintf_addbuf( |
2038 | 0 | &buf, |
2039 | 0 | "mod[%zu]: mod_values[%zu]: %s\n", |
2040 | 0 | i, |
2041 | 0 | j, |
2042 | 0 | modlist[i]->mod_values[j]); |
2043 | 0 | } |
2044 | 0 | } |
2045 | | |
2046 | 0 | if (buf != NULL) { |
2047 | 0 | DBG_DEBUG("%s", buf); |
2048 | 0 | TALLOC_FREE(buf); |
2049 | 0 | } |
2050 | 0 | } |
2051 | | |
2052 | | /** |
2053 | | * Add a single string value to a mod list |
2054 | | * @param ctx An initialized TALLOC_CTX |
2055 | | * @param mods An initialized ADS_MODLIST |
2056 | | * @param name The attribute name to add |
2057 | | * @param val The value to add - NULL means DELETE |
2058 | | * @return ADS STATUS indicating success of add |
2059 | | **/ |
2060 | | ADS_STATUS ads_mod_str(TALLOC_CTX *ctx, ADS_MODLIST *mods, |
2061 | | const char *name, const char *val) |
2062 | 0 | { |
2063 | 0 | const char *values[2]; |
2064 | |
|
2065 | 0 | values[0] = val; |
2066 | 0 | values[1] = NULL; |
2067 | |
|
2068 | 0 | if (!val) |
2069 | 0 | return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL); |
2070 | 0 | return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE, name, values); |
2071 | 0 | } |
2072 | | |
2073 | | /** |
2074 | | * Add an array of string values to a mod list |
2075 | | * @param ctx An initialized TALLOC_CTX |
2076 | | * @param mods An initialized ADS_MODLIST |
2077 | | * @param name The attribute name to add |
2078 | | * @param vals The array of string values to add - NULL means DELETE |
2079 | | * @return ADS STATUS indicating success of add |
2080 | | **/ |
2081 | | ADS_STATUS ads_mod_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods, |
2082 | | const char *name, const char **vals) |
2083 | 0 | { |
2084 | 0 | if (!vals) |
2085 | 0 | return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL); |
2086 | 0 | return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE, |
2087 | 0 | name, (const void **) vals); |
2088 | 0 | } |
2089 | | |
2090 | | /** |
2091 | | * Add a single ber-encoded value to a mod list |
2092 | | * @param ctx An initialized TALLOC_CTX |
2093 | | * @param mods An initialized ADS_MODLIST |
2094 | | * @param name The attribute name to add |
2095 | | * @param val The value to add - NULL means DELETE |
2096 | | * @return ADS STATUS indicating success of add |
2097 | | **/ |
2098 | | static ADS_STATUS ads_mod_ber(TALLOC_CTX *ctx, ADS_MODLIST *mods, |
2099 | | const char *name, const struct berval *val) |
2100 | 0 | { |
2101 | 0 | const struct berval *values[2]; |
2102 | |
|
2103 | 0 | values[0] = val; |
2104 | 0 | values[1] = NULL; |
2105 | 0 | if (!val) |
2106 | 0 | return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL); |
2107 | 0 | return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE|LDAP_MOD_BVALUES, |
2108 | 0 | name, (const void *) values); |
2109 | 0 | } |
2110 | | |
2111 | | static void ads_print_error(int ret, LDAP *ld) |
2112 | 0 | { |
2113 | 0 | if (ret != 0) { |
2114 | 0 | char *ld_error = NULL; |
2115 | 0 | ldap_get_option(ld, LDAP_OPT_ERROR_STRING, &ld_error); |
2116 | 0 | DBG_ERR("AD LDAP ERROR: %d (%s): %s\n", |
2117 | 0 | ret, |
2118 | 0 | ldap_err2string(ret), |
2119 | 0 | ld_error); |
2120 | 0 | SAFE_FREE(ld_error); |
2121 | 0 | } |
2122 | 0 | } |
2123 | | |
2124 | | /** |
2125 | | * Perform an ldap modify |
2126 | | * @param ads connection to ads server |
2127 | | * @param mod_dn DistinguishedName to modify |
2128 | | * @param mods list of modifications to perform |
2129 | | * @return status of modify |
2130 | | **/ |
2131 | | ADS_STATUS ads_gen_mod(ADS_STRUCT *ads, const char *mod_dn, ADS_MODLIST mods) |
2132 | 0 | { |
2133 | 0 | int ret,i; |
2134 | 0 | char *utf8_dn = NULL; |
2135 | 0 | size_t converted_size; |
2136 | | /* |
2137 | | this control is needed to modify that contains a currently |
2138 | | non-existent attribute (but allowable for the object) to run |
2139 | | */ |
2140 | 0 | LDAPControl PermitModify = { |
2141 | 0 | discard_const_p(char, ADS_PERMIT_MODIFY_OID), |
2142 | 0 | {0, NULL}, |
2143 | 0 | (char) 1}; |
2144 | 0 | LDAPControl *controls[2]; |
2145 | |
|
2146 | 0 | DBG_INFO("AD LDAP: Modifying %s\n", mod_dn); |
2147 | |
|
2148 | 0 | controls[0] = &PermitModify; |
2149 | 0 | controls[1] = NULL; |
2150 | |
|
2151 | 0 | if (!push_utf8_talloc(talloc_tos(), &utf8_dn, mod_dn, &converted_size)) { |
2152 | 0 | return ADS_ERROR_NT(NT_STATUS_NO_MEMORY); |
2153 | 0 | } |
2154 | | |
2155 | | /* find the end of the list, marked by NULL or -1 */ |
2156 | 0 | for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++); |
2157 | | /* make sure the end of the list is NULL */ |
2158 | 0 | mods[i] = NULL; |
2159 | |
|
2160 | 0 | ads_dump_modlist(&mods); |
2161 | |
|
2162 | 0 | ret = ldap_modify_ext_s(ads->ldap.ld, utf8_dn, |
2163 | 0 | (LDAPMod **) mods, controls, NULL); |
2164 | 0 | ads_print_error(ret, ads->ldap.ld); |
2165 | 0 | TALLOC_FREE(utf8_dn); |
2166 | 0 | return ADS_ERROR(ret); |
2167 | 0 | } |
2168 | | |
2169 | | /** |
2170 | | * Perform an ldap add |
2171 | | * @param ads connection to ads server |
2172 | | * @param new_dn DistinguishedName to add |
2173 | | * @param mods list of attributes and values for DN |
2174 | | * @return status of add |
2175 | | **/ |
2176 | | ADS_STATUS ads_gen_add(ADS_STRUCT *ads, const char *new_dn, ADS_MODLIST mods) |
2177 | 0 | { |
2178 | 0 | int ret, i; |
2179 | 0 | char *utf8_dn = NULL; |
2180 | 0 | size_t converted_size; |
2181 | |
|
2182 | 0 | DBG_INFO("AD LDAP: Adding %s\n", new_dn); |
2183 | |
|
2184 | 0 | if (!push_utf8_talloc(talloc_tos(), &utf8_dn, new_dn, &converted_size)) { |
2185 | 0 | DEBUG(1, ("ads_gen_add: push_utf8_talloc failed!\n")); |
2186 | 0 | return ADS_ERROR_NT(NT_STATUS_NO_MEMORY); |
2187 | 0 | } |
2188 | | |
2189 | | /* find the end of the list, marked by NULL or -1 */ |
2190 | 0 | for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++); |
2191 | | /* make sure the end of the list is NULL */ |
2192 | 0 | mods[i] = NULL; |
2193 | |
|
2194 | 0 | ads_dump_modlist(&mods); |
2195 | |
|
2196 | 0 | ret = ldap_add_ext_s(ads->ldap.ld, utf8_dn, (LDAPMod**)mods, NULL, NULL); |
2197 | 0 | ads_print_error(ret, ads->ldap.ld); |
2198 | 0 | TALLOC_FREE(utf8_dn); |
2199 | 0 | return ADS_ERROR(ret); |
2200 | 0 | } |
2201 | | |
2202 | | /** |
2203 | | * Delete a DistinguishedName |
2204 | | * @param ads connection to ads server |
2205 | | * @param new_dn DistinguishedName to delete |
2206 | | * @return status of delete |
2207 | | **/ |
2208 | | ADS_STATUS ads_del_dn(ADS_STRUCT *ads, char *del_dn) |
2209 | 0 | { |
2210 | 0 | int ret; |
2211 | 0 | char *utf8_dn = NULL; |
2212 | 0 | size_t converted_size; |
2213 | 0 | if (!push_utf8_talloc(talloc_tos(), &utf8_dn, del_dn, &converted_size)) { |
2214 | 0 | DEBUG(1, ("ads_del_dn: push_utf8_talloc failed!\n")); |
2215 | 0 | return ADS_ERROR_NT(NT_STATUS_NO_MEMORY); |
2216 | 0 | } |
2217 | | |
2218 | 0 | DBG_INFO("AD LDAP: Deleting %s\n", del_dn); |
2219 | |
|
2220 | 0 | ret = ldap_delete_s(ads->ldap.ld, utf8_dn); |
2221 | 0 | ads_print_error(ret, ads->ldap.ld); |
2222 | 0 | TALLOC_FREE(utf8_dn); |
2223 | 0 | return ADS_ERROR(ret); |
2224 | 0 | } |
2225 | | |
2226 | | /** |
2227 | | * Build an org unit string |
2228 | | * if org unit is Computers or blank then assume a container, otherwise |
2229 | | * assume a / separated list of organisational units. |
2230 | | * jmcd: '\' is now used for escapes so certain chars can be in the ou (e.g. #) |
2231 | | * @param ads connection to ads server |
2232 | | * @param org_unit Organizational unit |
2233 | | * @return org unit string - caller must free |
2234 | | **/ |
2235 | | char *ads_ou_string(ADS_STRUCT *ads, const char *org_unit) |
2236 | 0 | { |
2237 | 0 | ADS_STATUS status; |
2238 | 0 | char *ret = NULL; |
2239 | 0 | char *dn = NULL; |
2240 | |
|
2241 | 0 | if (!org_unit || !*org_unit) { |
2242 | |
|
2243 | 0 | ret = ads_default_ou_string(ads, DS_GUID_COMPUTERS_CONTAINER); |
2244 | | |
2245 | | /* samba4 might not yet respond to a wellknownobject-query */ |
2246 | 0 | return ret ? ret : SMB_STRDUP("cn=Computers"); |
2247 | 0 | } |
2248 | | |
2249 | 0 | if (strequal(org_unit, "Computers")) { |
2250 | 0 | return SMB_STRDUP("cn=Computers"); |
2251 | 0 | } |
2252 | | |
2253 | | /* jmcd: removed "\\" from the separation chars, because it is |
2254 | | needed as an escape for chars like '#' which are valid in an |
2255 | | OU name */ |
2256 | 0 | status = ads_build_path(org_unit, "/", "ou=", 1, &dn); |
2257 | 0 | if (!ADS_ERR_OK(status)) { |
2258 | 0 | return NULL; |
2259 | 0 | } |
2260 | | |
2261 | 0 | return dn; |
2262 | 0 | } |
2263 | | |
2264 | | /** |
2265 | | * Get a org unit string for a well-known GUID |
2266 | | * @param ads connection to ads server |
2267 | | * @param wknguid Well known GUID |
2268 | | * @return org unit string - caller must free |
2269 | | **/ |
2270 | | char *ads_default_ou_string(ADS_STRUCT *ads, const char *wknguid) |
2271 | 0 | { |
2272 | 0 | ADS_STATUS status; |
2273 | 0 | LDAPMessage *res = NULL; |
2274 | 0 | char *base, *wkn_dn = NULL, *ret = NULL, **wkn_dn_exp = NULL, |
2275 | 0 | **bind_dn_exp = NULL; |
2276 | 0 | const char *attrs[] = {"distinguishedName", NULL}; |
2277 | 0 | int new_ln, wkn_ln, bind_ln, i; |
2278 | |
|
2279 | 0 | if (wknguid == NULL) { |
2280 | 0 | return NULL; |
2281 | 0 | } |
2282 | | |
2283 | 0 | if (asprintf(&base, "<WKGUID=%s,%s>", wknguid, ads->config.bind_path ) == -1) { |
2284 | 0 | DEBUG(1, ("asprintf failed!\n")); |
2285 | 0 | return NULL; |
2286 | 0 | } |
2287 | | |
2288 | 0 | status = ads_search_dn(ads, &res, base, attrs); |
2289 | 0 | if (!ADS_ERR_OK(status)) { |
2290 | 0 | DEBUG(1,("Failed while searching for: %s\n", base)); |
2291 | 0 | goto out; |
2292 | 0 | } |
2293 | | |
2294 | 0 | if (ads_count_replies(ads, res) != 1) { |
2295 | 0 | goto out; |
2296 | 0 | } |
2297 | | |
2298 | | /* substitute the bind-path from the well-known-guid-search result */ |
2299 | 0 | wkn_dn = ads_get_dn(ads, talloc_tos(), res); |
2300 | 0 | if (!wkn_dn) { |
2301 | 0 | goto out; |
2302 | 0 | } |
2303 | | |
2304 | 0 | wkn_dn_exp = ldap_explode_dn(wkn_dn, 0); |
2305 | 0 | if (!wkn_dn_exp) { |
2306 | 0 | goto out; |
2307 | 0 | } |
2308 | | |
2309 | 0 | bind_dn_exp = ldap_explode_dn(ads->config.bind_path, 0); |
2310 | 0 | if (!bind_dn_exp) { |
2311 | 0 | goto out; |
2312 | 0 | } |
2313 | | |
2314 | 0 | for (wkn_ln=0; wkn_dn_exp[wkn_ln]; wkn_ln++) |
2315 | 0 | ; |
2316 | 0 | for (bind_ln=0; bind_dn_exp[bind_ln]; bind_ln++) |
2317 | 0 | ; |
2318 | |
|
2319 | 0 | new_ln = wkn_ln - bind_ln; |
2320 | |
|
2321 | 0 | ret = SMB_STRDUP(wkn_dn_exp[0]); |
2322 | 0 | if (!ret) { |
2323 | 0 | goto out; |
2324 | 0 | } |
2325 | | |
2326 | 0 | for (i=1; i < new_ln; i++) { |
2327 | 0 | char *s = NULL; |
2328 | |
|
2329 | 0 | if (asprintf(&s, "%s,%s", ret, wkn_dn_exp[i]) == -1) { |
2330 | 0 | SAFE_FREE(ret); |
2331 | 0 | goto out; |
2332 | 0 | } |
2333 | | |
2334 | 0 | SAFE_FREE(ret); |
2335 | 0 | ret = SMB_STRDUP(s); |
2336 | 0 | free(s); |
2337 | 0 | if (!ret) { |
2338 | 0 | goto out; |
2339 | 0 | } |
2340 | 0 | } |
2341 | | |
2342 | 0 | out: |
2343 | 0 | SAFE_FREE(base); |
2344 | 0 | ads_msgfree(ads, res); |
2345 | 0 | TALLOC_FREE(wkn_dn); |
2346 | 0 | if (wkn_dn_exp) { |
2347 | 0 | ldap_value_free(wkn_dn_exp); |
2348 | 0 | } |
2349 | 0 | if (bind_dn_exp) { |
2350 | 0 | ldap_value_free(bind_dn_exp); |
2351 | 0 | } |
2352 | |
|
2353 | 0 | return ret; |
2354 | 0 | } |
2355 | | |
2356 | | /** |
2357 | | * Adds (appends) an item to an attribute array, rather then |
2358 | | * replacing the whole list |
2359 | | * @param ctx An initialized TALLOC_CTX |
2360 | | * @param mods An initialized ADS_MODLIST |
2361 | | * @param name name of the ldap attribute to append to |
2362 | | * @param vals an array of values to add |
2363 | | * @return status of addition |
2364 | | **/ |
2365 | | |
2366 | | ADS_STATUS ads_add_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods, |
2367 | | const char *name, const char **vals) |
2368 | 0 | { |
2369 | 0 | return ads_modlist_add(ctx, mods, LDAP_MOD_ADD, name, |
2370 | 0 | (const void *) vals); |
2371 | 0 | } |
2372 | | |
2373 | | /** |
2374 | | * This clears out all registered spn's for a given hostname |
2375 | | * @param ads An initialized ADS_STRUCT |
2376 | | * @param machine_name the NetBIOS name of the computer. |
2377 | | * @return 0 upon success, non-zero otherwise. |
2378 | | **/ |
2379 | | |
2380 | | ADS_STATUS ads_clear_service_principal_names(ADS_STRUCT *ads, const char *machine_name) |
2381 | 0 | { |
2382 | 0 | TALLOC_CTX *ctx; |
2383 | 0 | LDAPMessage *res = NULL; |
2384 | 0 | ADS_MODLIST mods; |
2385 | 0 | const char *servicePrincipalName[1] = {NULL}; |
2386 | 0 | ADS_STATUS ret; |
2387 | 0 | char *dn_string = NULL; |
2388 | |
|
2389 | 0 | ret = ads_find_machine_acct(ads, &res, machine_name); |
2390 | 0 | if (!ADS_ERR_OK(ret)) { |
2391 | 0 | DEBUG(5,("ads_clear_service_principal_names: WARNING: Host Account for %s not found... skipping operation.\n", machine_name)); |
2392 | 0 | DEBUG(5,("ads_clear_service_principal_names: WARNING: Service Principals for %s have NOT been cleared.\n", machine_name)); |
2393 | 0 | ads_msgfree(ads, res); |
2394 | 0 | return ret; |
2395 | 0 | } |
2396 | | |
2397 | 0 | DEBUG(5,("ads_clear_service_principal_names: Host account for %s found\n", machine_name)); |
2398 | 0 | ctx = talloc_init("ads_clear_service_principal_names"); |
2399 | 0 | if (!ctx) { |
2400 | 0 | ads_msgfree(ads, res); |
2401 | 0 | return ADS_ERROR(LDAP_NO_MEMORY); |
2402 | 0 | } |
2403 | | |
2404 | 0 | if (!(mods = ads_init_mods(ctx))) { |
2405 | 0 | TALLOC_FREE(ctx); |
2406 | 0 | ads_msgfree(ads, res); |
2407 | 0 | return ADS_ERROR(LDAP_NO_MEMORY); |
2408 | 0 | } |
2409 | 0 | ret = ads_mod_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName); |
2410 | 0 | if (!ADS_ERR_OK(ret)) { |
2411 | 0 | DEBUG(1,("ads_clear_service_principal_names: Error creating strlist.\n")); |
2412 | 0 | ads_msgfree(ads, res); |
2413 | 0 | TALLOC_FREE(ctx); |
2414 | 0 | return ret; |
2415 | 0 | } |
2416 | 0 | dn_string = ads_get_dn(ads, talloc_tos(), res); |
2417 | 0 | if (!dn_string) { |
2418 | 0 | TALLOC_FREE(ctx); |
2419 | 0 | ads_msgfree(ads, res); |
2420 | 0 | return ADS_ERROR(LDAP_NO_MEMORY); |
2421 | 0 | } |
2422 | 0 | ret = ads_gen_mod(ads, dn_string, mods); |
2423 | 0 | TALLOC_FREE(dn_string); |
2424 | 0 | if (!ADS_ERR_OK(ret)) { |
2425 | 0 | DEBUG(1,("ads_clear_service_principal_names: Error: Updating Service Principals for machine %s in LDAP\n", |
2426 | 0 | machine_name)); |
2427 | 0 | ads_msgfree(ads, res); |
2428 | 0 | TALLOC_FREE(ctx); |
2429 | 0 | return ret; |
2430 | 0 | } |
2431 | | |
2432 | 0 | ads_msgfree(ads, res); |
2433 | 0 | TALLOC_FREE(ctx); |
2434 | 0 | return ret; |
2435 | 0 | } |
2436 | | |
2437 | | /** |
2438 | | * @brief Search for an element in a string array. |
2439 | | * |
2440 | | * @param[in] el_array The string array to search. |
2441 | | * |
2442 | | * @param[in] num_el The number of elements in the string array. |
2443 | | * |
2444 | | * @param[in] el The string to search. |
2445 | | * |
2446 | | * @return True if found, false if not. |
2447 | | */ |
2448 | | bool ads_element_in_array(const char **el_array, size_t num_el, const char *el) |
2449 | 0 | { |
2450 | 0 | size_t i; |
2451 | |
|
2452 | 0 | if (el_array == NULL || num_el == 0 || el == NULL) { |
2453 | 0 | return false; |
2454 | 0 | } |
2455 | | |
2456 | 0 | for (i = 0; i < num_el && el_array[i] != NULL; i++) { |
2457 | 0 | int cmp; |
2458 | |
|
2459 | 0 | cmp = strcasecmp_m(el_array[i], el); |
2460 | 0 | if (cmp == 0) { |
2461 | 0 | return true; |
2462 | 0 | } |
2463 | 0 | } |
2464 | | |
2465 | 0 | return false; |
2466 | 0 | } |
2467 | | |
2468 | | /** |
2469 | | * @brief This gets the service principal names of an existing computer account. |
2470 | | * |
2471 | | * @param[in] mem_ctx The memory context to use to allocate the spn array. |
2472 | | * |
2473 | | * @param[in] ads The ADS context to use. |
2474 | | * |
2475 | | * @param[in] machine_name The NetBIOS name of the computer, which is used to |
2476 | | * identify the computer account. |
2477 | | * |
2478 | | * @param[in] spn_array A pointer to store the array for SPNs. |
2479 | | * |
2480 | | * @param[in] num_spns The number of principals stored in the array. |
2481 | | * |
2482 | | * @return 0 on success, or a ADS error if a failure occurred. |
2483 | | */ |
2484 | | ADS_STATUS ads_get_service_principal_names(TALLOC_CTX *mem_ctx, |
2485 | | ADS_STRUCT *ads, |
2486 | | const char *machine_name, |
2487 | | char ***spn_array, |
2488 | | size_t *num_spns) |
2489 | 0 | { |
2490 | 0 | ADS_STATUS status; |
2491 | 0 | LDAPMessage *res = NULL; |
2492 | 0 | int count; |
2493 | |
|
2494 | 0 | status = ads_find_machine_acct(ads, |
2495 | 0 | &res, |
2496 | 0 | machine_name); |
2497 | 0 | if (!ADS_ERR_OK(status)) { |
2498 | 0 | DEBUG(1,("Host Account for %s not found... skipping operation.\n", |
2499 | 0 | machine_name)); |
2500 | 0 | return status; |
2501 | 0 | } |
2502 | | |
2503 | 0 | count = ads_count_replies(ads, res); |
2504 | 0 | if (count != 1) { |
2505 | 0 | status = ADS_ERROR(LDAP_NO_SUCH_OBJECT); |
2506 | 0 | goto done; |
2507 | 0 | } |
2508 | | |
2509 | 0 | *spn_array = ads_pull_strings(ads, |
2510 | 0 | mem_ctx, |
2511 | 0 | res, |
2512 | 0 | "servicePrincipalName", |
2513 | 0 | num_spns); |
2514 | 0 | if (*spn_array == NULL) { |
2515 | 0 | DEBUG(1, ("Host account for %s does not have service principal " |
2516 | 0 | "names.\n", |
2517 | 0 | machine_name)); |
2518 | 0 | status = ADS_ERROR(LDAP_NO_SUCH_OBJECT); |
2519 | 0 | goto done; |
2520 | 0 | } |
2521 | | |
2522 | 0 | done: |
2523 | 0 | ads_msgfree(ads, res); |
2524 | |
|
2525 | 0 | return status; |
2526 | 0 | } |
2527 | | |
2528 | | /** |
2529 | | * This adds a service principal name to an existing computer account |
2530 | | * (found by hostname) in AD. |
2531 | | * @param ads An initialized ADS_STRUCT |
2532 | | * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account. |
2533 | | * @param spns An array or strings for the service principals to add, |
2534 | | * i.e. 'cifs/machine_name', 'http/machine.full.domain.com' etc. |
2535 | | * @return 0 upon success, or non-zero if a failure occurs |
2536 | | **/ |
2537 | | |
2538 | | ADS_STATUS ads_add_service_principal_names(ADS_STRUCT *ads, |
2539 | | const char *machine_name, |
2540 | | const char **spns) |
2541 | 0 | { |
2542 | 0 | ADS_STATUS ret; |
2543 | 0 | TALLOC_CTX *ctx; |
2544 | 0 | LDAPMessage *res = NULL; |
2545 | 0 | ADS_MODLIST mods; |
2546 | 0 | char *dn_string = NULL; |
2547 | 0 | const char **servicePrincipalName = spns; |
2548 | |
|
2549 | 0 | ret = ads_find_machine_acct(ads, &res, machine_name); |
2550 | 0 | if (!ADS_ERR_OK(ret)) { |
2551 | 0 | DEBUG(1,("ads_add_service_principal_name: WARNING: Host Account for %s not found... skipping operation.\n", |
2552 | 0 | machine_name)); |
2553 | 0 | DEBUG(1,("ads_add_service_principal_name: WARNING: Service Principals have NOT been added.\n")); |
2554 | 0 | ads_msgfree(ads, res); |
2555 | 0 | return ret; |
2556 | 0 | } |
2557 | | |
2558 | 0 | DEBUG(1,("ads_add_service_principal_name: Host account for %s found\n", machine_name)); |
2559 | 0 | if (!(ctx = talloc_init("ads_add_service_principal_name"))) { |
2560 | 0 | ads_msgfree(ads, res); |
2561 | 0 | return ADS_ERROR(LDAP_NO_MEMORY); |
2562 | 0 | } |
2563 | | |
2564 | 0 | DEBUG(5,("ads_add_service_principal_name: INFO: " |
2565 | 0 | "Adding %s to host %s\n", |
2566 | 0 | spns[0] ? "N/A" : spns[0], machine_name)); |
2567 | | |
2568 | |
|
2569 | 0 | DEBUG(5,("ads_add_service_principal_name: INFO: " |
2570 | 0 | "Adding %s to host %s\n", |
2571 | 0 | spns[1] ? "N/A" : spns[1], machine_name)); |
2572 | |
|
2573 | 0 | if ( (mods = ads_init_mods(ctx)) == NULL ) { |
2574 | 0 | ret = ADS_ERROR(LDAP_NO_MEMORY); |
2575 | 0 | goto out; |
2576 | 0 | } |
2577 | | |
2578 | 0 | ret = ads_add_strlist(ctx, |
2579 | 0 | &mods, |
2580 | 0 | "servicePrincipalName", |
2581 | 0 | servicePrincipalName); |
2582 | 0 | if (!ADS_ERR_OK(ret)) { |
2583 | 0 | DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n")); |
2584 | 0 | goto out; |
2585 | 0 | } |
2586 | | |
2587 | 0 | if ( (dn_string = ads_get_dn(ads, ctx, res)) == NULL ) { |
2588 | 0 | ret = ADS_ERROR(LDAP_NO_MEMORY); |
2589 | 0 | goto out; |
2590 | 0 | } |
2591 | | |
2592 | 0 | ret = ads_gen_mod(ads, dn_string, mods); |
2593 | 0 | if (!ADS_ERR_OK(ret)) { |
2594 | 0 | DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n")); |
2595 | 0 | goto out; |
2596 | 0 | } |
2597 | | |
2598 | 0 | out: |
2599 | 0 | TALLOC_FREE( ctx ); |
2600 | 0 | ads_msgfree(ads, res); |
2601 | 0 | return ret; |
2602 | 0 | } |
2603 | | |
2604 | | static uint32_t ads_get_acct_ctrl(ADS_STRUCT *ads, |
2605 | | LDAPMessage *msg) |
2606 | 0 | { |
2607 | 0 | uint32_t acct_ctrl = 0; |
2608 | 0 | bool ok; |
2609 | |
|
2610 | 0 | ok = ads_pull_uint32(ads, msg, "userAccountControl", &acct_ctrl); |
2611 | 0 | if (!ok) { |
2612 | 0 | return 0; |
2613 | 0 | } |
2614 | | |
2615 | 0 | return acct_ctrl; |
2616 | 0 | } |
2617 | | |
2618 | | static ADS_STATUS ads_change_machine_acct(ADS_STRUCT *ads, |
2619 | | LDAPMessage *msg, |
2620 | | const struct berval *machine_pw_val) |
2621 | 0 | { |
2622 | 0 | ADS_MODLIST mods; |
2623 | 0 | ADS_STATUS ret; |
2624 | 0 | TALLOC_CTX *frame = talloc_stackframe(); |
2625 | 0 | uint32_t acct_control; |
2626 | 0 | char *control_str = NULL; |
2627 | 0 | const char *attrs[] = { |
2628 | 0 | "objectSid", |
2629 | 0 | NULL |
2630 | 0 | }; |
2631 | 0 | LDAPMessage *res = NULL; |
2632 | 0 | char *dn = NULL; |
2633 | |
|
2634 | 0 | dn = ads_get_dn(ads, frame, msg); |
2635 | 0 | if (dn == NULL) { |
2636 | 0 | ret = ADS_ERROR(LDAP_NO_MEMORY); |
2637 | 0 | goto done; |
2638 | 0 | } |
2639 | | |
2640 | 0 | acct_control = ads_get_acct_ctrl(ads, msg); |
2641 | 0 | if (acct_control == 0) { |
2642 | 0 | ret = ADS_ERROR(LDAP_NO_RESULTS_RETURNED); |
2643 | 0 | goto done; |
2644 | 0 | } |
2645 | | |
2646 | | /* |
2647 | | * Changing the password, disables the account. So we need to change the |
2648 | | * userAccountControl flags to enable it again. |
2649 | | */ |
2650 | 0 | mods = ads_init_mods(frame); |
2651 | 0 | if (mods == NULL) { |
2652 | 0 | ret = ADS_ERROR_LDAP(LDAP_NO_MEMORY); |
2653 | 0 | goto done; |
2654 | 0 | } |
2655 | | |
2656 | 0 | ads_mod_ber(frame, &mods, "unicodePwd", machine_pw_val); |
2657 | |
|
2658 | 0 | ret = ads_gen_mod(ads, dn, mods); |
2659 | 0 | if (!ADS_ERR_OK(ret)) { |
2660 | 0 | goto done; |
2661 | 0 | } |
2662 | 0 | TALLOC_FREE(mods); |
2663 | | |
2664 | | /* |
2665 | | * To activate the account, we need to disable and enable it. |
2666 | | */ |
2667 | 0 | acct_control |= UF_ACCOUNTDISABLE; |
2668 | |
|
2669 | 0 | control_str = talloc_asprintf(frame, "%u", acct_control); |
2670 | 0 | if (control_str == NULL) { |
2671 | 0 | ret = ADS_ERROR(LDAP_NO_MEMORY); |
2672 | 0 | goto done; |
2673 | 0 | } |
2674 | | |
2675 | 0 | mods = ads_init_mods(frame); |
2676 | 0 | if (mods == NULL) { |
2677 | 0 | ret = ADS_ERROR_LDAP(LDAP_NO_MEMORY); |
2678 | 0 | goto done; |
2679 | 0 | } |
2680 | | |
2681 | 0 | ads_mod_str(frame, &mods, "userAccountControl", control_str); |
2682 | |
|
2683 | 0 | ret = ads_gen_mod(ads, dn, mods); |
2684 | 0 | if (!ADS_ERR_OK(ret)) { |
2685 | 0 | goto done; |
2686 | 0 | } |
2687 | 0 | TALLOC_FREE(mods); |
2688 | 0 | TALLOC_FREE(control_str); |
2689 | | |
2690 | | /* |
2691 | | * Enable the account again. |
2692 | | */ |
2693 | 0 | acct_control &= ~UF_ACCOUNTDISABLE; |
2694 | |
|
2695 | 0 | control_str = talloc_asprintf(frame, "%u", acct_control); |
2696 | 0 | if (control_str == NULL) { |
2697 | 0 | ret = ADS_ERROR(LDAP_NO_MEMORY); |
2698 | 0 | goto done; |
2699 | 0 | } |
2700 | | |
2701 | 0 | mods = ads_init_mods(frame); |
2702 | 0 | if (mods == NULL) { |
2703 | 0 | ret = ADS_ERROR_LDAP(LDAP_NO_MEMORY); |
2704 | 0 | goto done; |
2705 | 0 | } |
2706 | | |
2707 | 0 | ads_mod_str(frame, &mods, "userAccountControl", control_str); |
2708 | |
|
2709 | 0 | ret = ads_gen_mod(ads, dn, mods); |
2710 | 0 | if (!ADS_ERR_OK(ret)) { |
2711 | 0 | goto done; |
2712 | 0 | } |
2713 | 0 | TALLOC_FREE(mods); |
2714 | 0 | TALLOC_FREE(control_str); |
2715 | |
|
2716 | 0 | ret = ads_search_dn(ads, &res, dn, attrs); |
2717 | 0 | ads_msgfree(ads, res); |
2718 | |
|
2719 | 0 | done: |
2720 | 0 | talloc_free(frame); |
2721 | |
|
2722 | 0 | return ret; |
2723 | 0 | } |
2724 | | |
2725 | | /** |
2726 | | * adds a machine account to the ADS server |
2727 | | * @param ads An initialized ADS_STRUCT |
2728 | | * @param machine_name - the NetBIOS machine name of this account. |
2729 | | * @param account_type A number indicating the type of account to create |
2730 | | * @param org_unit The LDAP path in which to place this account |
2731 | | * @return 0 upon success, or non-zero otherwise |
2732 | | **/ |
2733 | | |
2734 | | ADS_STATUS ads_create_machine_acct(ADS_STRUCT *ads, |
2735 | | const char *machine_name, |
2736 | | const char *machine_password, |
2737 | | const char *org_unit, |
2738 | | uint32_t etype_list, |
2739 | | const char *dns_domain_name) |
2740 | 0 | { |
2741 | 0 | ADS_STATUS ret; |
2742 | 0 | char *samaccountname = NULL; |
2743 | 0 | char *controlstr = NULL; |
2744 | 0 | TALLOC_CTX *ctx = NULL; |
2745 | 0 | ADS_MODLIST mods; |
2746 | 0 | char *machine_escaped = NULL; |
2747 | 0 | char *dns_hostname = NULL; |
2748 | 0 | char *new_dn = NULL; |
2749 | 0 | char *utf8_pw = NULL; |
2750 | 0 | size_t utf8_pw_len = 0; |
2751 | 0 | char *utf16_pw = NULL; |
2752 | 0 | size_t utf16_pw_len = 0; |
2753 | 0 | struct berval machine_pw_val; |
2754 | 0 | bool ok; |
2755 | 0 | const char **spn_array = NULL; |
2756 | 0 | size_t num_spns = 0; |
2757 | 0 | const char *spn_prefix[] = { |
2758 | 0 | "HOST", |
2759 | 0 | "RestrictedKrbHost", |
2760 | 0 | }; |
2761 | 0 | size_t i; |
2762 | 0 | LDAPMessage *res = NULL; |
2763 | 0 | uint32_t acct_control = UF_WORKSTATION_TRUST_ACCOUNT; |
2764 | |
|
2765 | 0 | ctx = talloc_init("ads_add_machine_acct"); |
2766 | 0 | if (ctx == NULL) { |
2767 | 0 | return ADS_ERROR(LDAP_NO_MEMORY); |
2768 | 0 | } |
2769 | | |
2770 | 0 | machine_escaped = escape_rdn_val_string_alloc(machine_name); |
2771 | 0 | if (machine_escaped == NULL) { |
2772 | 0 | ret = ADS_ERROR(LDAP_NO_MEMORY); |
2773 | 0 | goto done; |
2774 | 0 | } |
2775 | | |
2776 | 0 | utf8_pw = talloc_asprintf(ctx, "\"%s\"", machine_password); |
2777 | 0 | if (utf8_pw == NULL) { |
2778 | 0 | ret = ADS_ERROR(LDAP_NO_MEMORY); |
2779 | 0 | goto done; |
2780 | 0 | } |
2781 | 0 | utf8_pw_len = strlen(utf8_pw); |
2782 | |
|
2783 | 0 | ok = convert_string_talloc(ctx, |
2784 | 0 | CH_UTF8, CH_UTF16MUNGED, |
2785 | 0 | utf8_pw, utf8_pw_len, |
2786 | 0 | (void *)&utf16_pw, &utf16_pw_len); |
2787 | 0 | if (!ok) { |
2788 | 0 | ret = ADS_ERROR(LDAP_NO_MEMORY); |
2789 | 0 | goto done; |
2790 | 0 | } |
2791 | | |
2792 | 0 | machine_pw_val = (struct berval) { |
2793 | 0 | .bv_val = utf16_pw, |
2794 | 0 | .bv_len = utf16_pw_len, |
2795 | 0 | }; |
2796 | | |
2797 | | /* Check if the machine account already exists. */ |
2798 | 0 | ret = ads_find_machine_acct(ads, &res, machine_escaped); |
2799 | 0 | if (ADS_ERR_OK(ret)) { |
2800 | | /* Change the machine account password */ |
2801 | 0 | ret = ads_change_machine_acct(ads, res, &machine_pw_val); |
2802 | 0 | ads_msgfree(ads, res); |
2803 | |
|
2804 | 0 | goto done; |
2805 | 0 | } |
2806 | 0 | ads_msgfree(ads, res); |
2807 | |
|
2808 | 0 | new_dn = talloc_asprintf(ctx, "cn=%s,%s", machine_escaped, org_unit); |
2809 | 0 | if (new_dn == NULL) { |
2810 | 0 | ret = ADS_ERROR(LDAP_NO_MEMORY); |
2811 | 0 | goto done; |
2812 | 0 | } |
2813 | | |
2814 | | /* Create machine account */ |
2815 | | |
2816 | 0 | samaccountname = talloc_asprintf(ctx, "%s$", machine_name); |
2817 | 0 | if (samaccountname == NULL) { |
2818 | 0 | ret = ADS_ERROR(LDAP_NO_MEMORY); |
2819 | 0 | goto done; |
2820 | 0 | } |
2821 | | |
2822 | 0 | dns_hostname = talloc_asprintf(ctx, |
2823 | 0 | "%s.%s", |
2824 | 0 | machine_name, |
2825 | 0 | dns_domain_name); |
2826 | 0 | if (dns_hostname == NULL) { |
2827 | 0 | ret = ADS_ERROR(LDAP_NO_MEMORY); |
2828 | 0 | goto done; |
2829 | 0 | } |
2830 | | |
2831 | | /* Add dns_hostname SPNs */ |
2832 | 0 | for (i = 0; i < ARRAY_SIZE(spn_prefix); i++) { |
2833 | 0 | char *spn = talloc_asprintf(ctx, |
2834 | 0 | "%s/%s", |
2835 | 0 | spn_prefix[i], |
2836 | 0 | dns_hostname); |
2837 | 0 | if (spn == NULL) { |
2838 | 0 | ret = ADS_ERROR(LDAP_NO_MEMORY); |
2839 | 0 | goto done; |
2840 | 0 | } |
2841 | | |
2842 | 0 | ok = add_string_to_array(ctx, |
2843 | 0 | spn, |
2844 | 0 | &spn_array, |
2845 | 0 | &num_spns); |
2846 | 0 | if (!ok) { |
2847 | 0 | ret = ADS_ERROR(LDAP_NO_MEMORY); |
2848 | 0 | goto done; |
2849 | 0 | } |
2850 | 0 | } |
2851 | | |
2852 | | /* Add machine_name SPNs */ |
2853 | 0 | for (i = 0; i < ARRAY_SIZE(spn_prefix); i++) { |
2854 | 0 | char *spn = talloc_asprintf(ctx, |
2855 | 0 | "%s/%s", |
2856 | 0 | spn_prefix[i], |
2857 | 0 | machine_name); |
2858 | 0 | if (spn == NULL) { |
2859 | 0 | ret = ADS_ERROR(LDAP_NO_MEMORY); |
2860 | 0 | goto done; |
2861 | 0 | } |
2862 | | |
2863 | 0 | ok = add_string_to_array(ctx, |
2864 | 0 | spn, |
2865 | 0 | &spn_array, |
2866 | 0 | &num_spns); |
2867 | 0 | if (!ok) { |
2868 | 0 | ret = ADS_ERROR(LDAP_NO_MEMORY); |
2869 | 0 | goto done; |
2870 | 0 | } |
2871 | 0 | } |
2872 | | |
2873 | | /* Make sure to NULL terminate the array */ |
2874 | 0 | spn_array = talloc_realloc_zero(ctx, |
2875 | 0 | spn_array, |
2876 | 0 | const char *, |
2877 | 0 | num_spns + 1); |
2878 | 0 | if (spn_array == NULL) { |
2879 | 0 | ret = ADS_ERROR(LDAP_NO_MEMORY); |
2880 | 0 | goto done; |
2881 | 0 | } |
2882 | | |
2883 | 0 | controlstr = talloc_asprintf(ctx, "%u", acct_control); |
2884 | 0 | if (controlstr == NULL) { |
2885 | 0 | ret = ADS_ERROR(LDAP_NO_MEMORY); |
2886 | 0 | goto done; |
2887 | 0 | } |
2888 | | |
2889 | 0 | mods = ads_init_mods(ctx); |
2890 | 0 | if (mods == NULL) { |
2891 | 0 | ret = ADS_ERROR(LDAP_NO_MEMORY); |
2892 | 0 | goto done; |
2893 | 0 | } |
2894 | | |
2895 | 0 | ads_mod_str(ctx, &mods, "objectClass", "Computer"); |
2896 | 0 | ads_mod_str(ctx, &mods, "sAMAccountName", samaccountname); |
2897 | 0 | ads_mod_str(ctx, &mods, "userAccountControl", controlstr); |
2898 | 0 | ads_mod_str(ctx, &mods, "DnsHostName", dns_hostname); |
2899 | 0 | ads_mod_strlist(ctx, &mods, "ServicePrincipalName", spn_array); |
2900 | 0 | ads_mod_ber(ctx, &mods, "unicodePwd", &machine_pw_val); |
2901 | |
|
2902 | 0 | ret = ads_gen_add(ads, new_dn, mods); |
2903 | |
|
2904 | 0 | done: |
2905 | 0 | SAFE_FREE(machine_escaped); |
2906 | 0 | TALLOC_FREE(ctx); |
2907 | |
|
2908 | 0 | return ret; |
2909 | 0 | } |
2910 | | |
2911 | | /** |
2912 | | * move a machine account to another OU on the ADS server |
2913 | | * @param ads - An initialized ADS_STRUCT |
2914 | | * @param machine_name - the NetBIOS machine name of this account. |
2915 | | * @param org_unit - The LDAP path in which to place this account |
2916 | | * @param moved - whether we moved the machine account (optional) |
2917 | | * @return 0 upon success, or non-zero otherwise |
2918 | | **/ |
2919 | | |
2920 | | ADS_STATUS ads_move_machine_acct(ADS_STRUCT *ads, const char *machine_name, |
2921 | | const char *org_unit, bool *moved) |
2922 | 0 | { |
2923 | 0 | ADS_STATUS rc; |
2924 | 0 | int ldap_status; |
2925 | 0 | LDAPMessage *res = NULL; |
2926 | 0 | char *filter = NULL; |
2927 | 0 | char *computer_dn = NULL; |
2928 | 0 | char *parent_dn; |
2929 | 0 | char *computer_rdn = NULL; |
2930 | 0 | bool need_move = False; |
2931 | |
|
2932 | 0 | if (asprintf(&filter, "(sAMAccountName=%s$)", machine_name) == -1) { |
2933 | 0 | rc = ADS_ERROR(LDAP_NO_MEMORY); |
2934 | 0 | goto done; |
2935 | 0 | } |
2936 | | |
2937 | | /* Find pre-existing machine */ |
2938 | 0 | rc = ads_search(ads, &res, filter, NULL); |
2939 | 0 | if (!ADS_ERR_OK(rc)) { |
2940 | 0 | goto done; |
2941 | 0 | } |
2942 | | |
2943 | 0 | computer_dn = ads_get_dn(ads, talloc_tos(), res); |
2944 | 0 | if (!computer_dn) { |
2945 | 0 | rc = ADS_ERROR(LDAP_NO_MEMORY); |
2946 | 0 | goto done; |
2947 | 0 | } |
2948 | | |
2949 | 0 | parent_dn = ads_parent_dn(computer_dn); |
2950 | 0 | if (strequal(parent_dn, org_unit)) { |
2951 | 0 | goto done; |
2952 | 0 | } |
2953 | | |
2954 | 0 | need_move = True; |
2955 | |
|
2956 | 0 | if (asprintf(&computer_rdn, "CN=%s", machine_name) == -1) { |
2957 | 0 | rc = ADS_ERROR(LDAP_NO_MEMORY); |
2958 | 0 | goto done; |
2959 | 0 | } |
2960 | | |
2961 | 0 | ldap_status = ldap_rename_s(ads->ldap.ld, computer_dn, computer_rdn, |
2962 | 0 | org_unit, 1, NULL, NULL); |
2963 | 0 | rc = ADS_ERROR(ldap_status); |
2964 | |
|
2965 | 0 | done: |
2966 | 0 | ads_msgfree(ads, res); |
2967 | 0 | SAFE_FREE(filter); |
2968 | 0 | TALLOC_FREE(computer_dn); |
2969 | 0 | SAFE_FREE(computer_rdn); |
2970 | |
|
2971 | 0 | if (!ADS_ERR_OK(rc)) { |
2972 | 0 | need_move = False; |
2973 | 0 | } |
2974 | |
|
2975 | 0 | if (moved) { |
2976 | 0 | *moved = need_move; |
2977 | 0 | } |
2978 | |
|
2979 | 0 | return rc; |
2980 | 0 | } |
2981 | | |
2982 | | /* |
2983 | | dump a binary result from ldap |
2984 | | */ |
2985 | | static void dump_binary(ADS_STRUCT *ads, const char *field, struct berval **values) |
2986 | 0 | { |
2987 | 0 | size_t i; |
2988 | 0 | for (i=0; values[i]; i++) { |
2989 | 0 | ber_len_t j; |
2990 | 0 | printf("%s: ", field); |
2991 | 0 | for (j=0; j<values[i]->bv_len; j++) { |
2992 | 0 | printf("%02X", (unsigned char)values[i]->bv_val[j]); |
2993 | 0 | } |
2994 | 0 | printf("\n"); |
2995 | 0 | } |
2996 | 0 | } |
2997 | | |
2998 | | static void dump_guid(ADS_STRUCT *ads, const char *field, struct berval **values) |
2999 | 0 | { |
3000 | 0 | int i; |
3001 | 0 | for (i=0; values[i]; i++) { |
3002 | 0 | NTSTATUS status; |
3003 | 0 | DATA_BLOB in = data_blob_const(values[i]->bv_val, values[i]->bv_len); |
3004 | 0 | struct GUID guid; |
3005 | |
|
3006 | 0 | status = GUID_from_ndr_blob(&in, &guid); |
3007 | 0 | if (NT_STATUS_IS_OK(status)) { |
3008 | 0 | printf("%s: %s\n", field, GUID_string(talloc_tos(), &guid)); |
3009 | 0 | } else { |
3010 | 0 | printf("%s: INVALID GUID\n", field); |
3011 | 0 | } |
3012 | 0 | } |
3013 | 0 | } |
3014 | | |
3015 | | /* |
3016 | | dump a sid result from ldap |
3017 | | */ |
3018 | | static void dump_sid(ADS_STRUCT *ads, const char *field, struct berval **values) |
3019 | 0 | { |
3020 | 0 | int i; |
3021 | 0 | for (i=0; values[i]; i++) { |
3022 | 0 | ssize_t ret; |
3023 | 0 | struct dom_sid sid; |
3024 | 0 | struct dom_sid_buf tmp; |
3025 | 0 | ret = sid_parse((const uint8_t *)values[i]->bv_val, |
3026 | 0 | values[i]->bv_len, &sid); |
3027 | 0 | if (ret == -1) { |
3028 | 0 | return; |
3029 | 0 | } |
3030 | 0 | printf("%s: %s\n", field, dom_sid_str_buf(&sid, &tmp)); |
3031 | 0 | } |
3032 | 0 | } |
3033 | | |
3034 | | /* |
3035 | | dump ntSecurityDescriptor |
3036 | | */ |
3037 | | static void dump_sd(ADS_STRUCT *ads, const char *filed, struct berval **values) |
3038 | 0 | { |
3039 | 0 | TALLOC_CTX *frame = talloc_stackframe(); |
3040 | 0 | struct security_descriptor *psd; |
3041 | 0 | NTSTATUS status; |
3042 | |
|
3043 | 0 | status = unmarshall_sec_desc(talloc_tos(), (uint8_t *)values[0]->bv_val, |
3044 | 0 | values[0]->bv_len, &psd); |
3045 | 0 | if (!NT_STATUS_IS_OK(status)) { |
3046 | 0 | DEBUG(0, ("unmarshall_sec_desc failed: %s\n", |
3047 | 0 | nt_errstr(status))); |
3048 | 0 | TALLOC_FREE(frame); |
3049 | 0 | return; |
3050 | 0 | } |
3051 | | |
3052 | 0 | if (psd) { |
3053 | 0 | ads_disp_sd(ads, talloc_tos(), psd); |
3054 | 0 | } |
3055 | |
|
3056 | 0 | TALLOC_FREE(frame); |
3057 | 0 | } |
3058 | | |
3059 | | /* |
3060 | | dump a string result from ldap |
3061 | | */ |
3062 | | static void dump_string(const char *field, char **values) |
3063 | 0 | { |
3064 | 0 | int i; |
3065 | 0 | for (i=0; values[i]; i++) { |
3066 | 0 | printf("%s: %s\n", field, values[i]); |
3067 | 0 | } |
3068 | 0 | } |
3069 | | |
3070 | | /* |
3071 | | dump a field from LDAP on stdout |
3072 | | used for debugging |
3073 | | */ |
3074 | | |
3075 | | static bool ads_dump_field(ADS_STRUCT *ads, char *field, void **values, void *data_area) |
3076 | 0 | { |
3077 | 0 | const struct { |
3078 | 0 | const char *name; |
3079 | 0 | bool string; |
3080 | 0 | void (*handler)(ADS_STRUCT *, const char *, struct berval **); |
3081 | 0 | } handlers[] = { |
3082 | 0 | {"objectGUID", False, dump_guid}, |
3083 | 0 | {"netbootGUID", False, dump_guid}, |
3084 | 0 | {"nTSecurityDescriptor", False, dump_sd}, |
3085 | 0 | {"dnsRecord", False, dump_binary}, |
3086 | 0 | {"objectSid", False, dump_sid}, |
3087 | 0 | {"securityIdentifier", False, dump_sid}, |
3088 | 0 | {"tokenGroups", False, dump_sid}, |
3089 | 0 | {"tokenGroupsNoGCAcceptable", False, dump_sid}, |
3090 | 0 | {"tokengroupsGlobalandUniversal", False, dump_sid}, |
3091 | 0 | {"mS-DS-CreatorSID", False, dump_sid}, |
3092 | 0 | {"msExchMailboxGuid", False, dump_guid}, |
3093 | 0 | {"msDS-TrustForestTrustInfo", False, dump_binary}, |
3094 | 0 | {NULL, True, NULL} |
3095 | 0 | }; |
3096 | 0 | int i; |
3097 | |
|
3098 | 0 | if (!field) { /* must be end of an entry */ |
3099 | 0 | printf("\n"); |
3100 | 0 | return False; |
3101 | 0 | } |
3102 | | |
3103 | 0 | for (i=0; handlers[i].name; i++) { |
3104 | 0 | if (strcasecmp_m(handlers[i].name, field) == 0) { |
3105 | 0 | if (!values) /* first time, indicate string or not */ |
3106 | 0 | return handlers[i].string; |
3107 | 0 | handlers[i].handler(ads, field, (struct berval **) values); |
3108 | 0 | break; |
3109 | 0 | } |
3110 | 0 | } |
3111 | 0 | if (!handlers[i].name) { |
3112 | 0 | if (!values) /* first time, indicate string conversion */ |
3113 | 0 | return True; |
3114 | 0 | dump_string(field, (char **)values); |
3115 | 0 | } |
3116 | 0 | return False; |
3117 | 0 | } |
3118 | | |
3119 | | /** |
3120 | | * Dump a result from LDAP on stdout |
3121 | | * used for debugging |
3122 | | * @param ads connection to ads server |
3123 | | * @param res Results to dump |
3124 | | **/ |
3125 | | |
3126 | | void ads_dump(ADS_STRUCT *ads, LDAPMessage *res) |
3127 | 0 | { |
3128 | 0 | ads_process_results(ads, res, ads_dump_field, NULL); |
3129 | 0 | } |
3130 | | |
3131 | | /** |
3132 | | * Walk through results, calling a function for each entry found. |
3133 | | * The function receives a field name, a berval * array of values, |
3134 | | * and a data area passed through from the start. The function is |
3135 | | * called once with null for field and values at the end of each |
3136 | | * entry. |
3137 | | * @param ads connection to ads server |
3138 | | * @param res Results to process |
3139 | | * @param fn Function for processing each result |
3140 | | * @param data_area user-defined area to pass to function |
3141 | | **/ |
3142 | | void ads_process_results(ADS_STRUCT *ads, LDAPMessage *res, |
3143 | | bool (*fn)(ADS_STRUCT *, char *, void **, void *), |
3144 | | void *data_area) |
3145 | 0 | { |
3146 | 0 | LDAPMessage *msg; |
3147 | 0 | TALLOC_CTX *ctx; |
3148 | 0 | size_t converted_size; |
3149 | |
|
3150 | 0 | if (!(ctx = talloc_init("ads_process_results"))) |
3151 | 0 | return; |
3152 | | |
3153 | 0 | for (msg = ads_first_entry(ads, res); msg; |
3154 | 0 | msg = ads_next_entry(ads, msg)) { |
3155 | 0 | char *utf8_field; |
3156 | 0 | BerElement *b; |
3157 | |
|
3158 | 0 | for (utf8_field=ldap_first_attribute(ads->ldap.ld, |
3159 | 0 | (LDAPMessage *)msg,&b); |
3160 | 0 | utf8_field; |
3161 | 0 | utf8_field=ldap_next_attribute(ads->ldap.ld, |
3162 | 0 | (LDAPMessage *)msg,b)) { |
3163 | 0 | struct berval **ber_vals; |
3164 | 0 | char **str_vals; |
3165 | 0 | char **utf8_vals; |
3166 | 0 | char *field; |
3167 | 0 | bool string; |
3168 | |
|
3169 | 0 | if (!pull_utf8_talloc(ctx, &field, utf8_field, |
3170 | 0 | &converted_size)) |
3171 | 0 | { |
3172 | 0 | DEBUG(0,("ads_process_results: " |
3173 | 0 | "pull_utf8_talloc failed: %s\n", |
3174 | 0 | strerror(errno))); |
3175 | 0 | } |
3176 | |
|
3177 | 0 | string = fn(ads, field, NULL, data_area); |
3178 | |
|
3179 | 0 | if (string) { |
3180 | 0 | const char **p; |
3181 | |
|
3182 | 0 | utf8_vals = ldap_get_values(ads->ldap.ld, |
3183 | 0 | (LDAPMessage *)msg, field); |
3184 | 0 | p = discard_const_p(const char *, utf8_vals); |
3185 | 0 | str_vals = ads_pull_strvals(ctx, p); |
3186 | 0 | fn(ads, field, (void **) str_vals, data_area); |
3187 | 0 | ldap_value_free(utf8_vals); |
3188 | 0 | } else { |
3189 | 0 | ber_vals = ldap_get_values_len(ads->ldap.ld, |
3190 | 0 | (LDAPMessage *)msg, field); |
3191 | 0 | fn(ads, field, (void **) ber_vals, data_area); |
3192 | |
|
3193 | 0 | ldap_value_free_len(ber_vals); |
3194 | 0 | } |
3195 | 0 | ldap_memfree(utf8_field); |
3196 | 0 | } |
3197 | 0 | ber_free(b, 0); |
3198 | 0 | talloc_free_children(ctx); |
3199 | 0 | fn(ads, NULL, NULL, data_area); /* completed an entry */ |
3200 | |
|
3201 | 0 | } |
3202 | 0 | TALLOC_FREE(ctx); |
3203 | 0 | } |
3204 | | |
3205 | | /** |
3206 | | * count how many replies are in a LDAPMessage |
3207 | | * @param ads connection to ads server |
3208 | | * @param res Results to count |
3209 | | * @return number of replies |
3210 | | **/ |
3211 | | int ads_count_replies(ADS_STRUCT *ads, void *res) |
3212 | 0 | { |
3213 | 0 | return ldap_count_entries(ads->ldap.ld, (LDAPMessage *)res); |
3214 | 0 | } |
3215 | | |
3216 | | /** |
3217 | | * pull the first entry from a ADS result |
3218 | | * @param ads connection to ads server |
3219 | | * @param res Results of search |
3220 | | * @return first entry from result |
3221 | | **/ |
3222 | | LDAPMessage *ads_first_entry(ADS_STRUCT *ads, LDAPMessage *res) |
3223 | 0 | { |
3224 | 0 | return ldap_first_entry(ads->ldap.ld, res); |
3225 | 0 | } |
3226 | | |
3227 | | /** |
3228 | | * pull the next entry from a ADS result |
3229 | | * @param ads connection to ads server |
3230 | | * @param res Results of search |
3231 | | * @return next entry from result |
3232 | | **/ |
3233 | | LDAPMessage *ads_next_entry(ADS_STRUCT *ads, LDAPMessage *res) |
3234 | 0 | { |
3235 | 0 | return ldap_next_entry(ads->ldap.ld, res); |
3236 | 0 | } |
3237 | | |
3238 | | /** |
3239 | | * pull the first message from a ADS result |
3240 | | * @param ads connection to ads server |
3241 | | * @param res Results of search |
3242 | | * @return first message from result |
3243 | | **/ |
3244 | | LDAPMessage *ads_first_message(ADS_STRUCT *ads, LDAPMessage *res) |
3245 | 0 | { |
3246 | 0 | return ldap_first_message(ads->ldap.ld, res); |
3247 | 0 | } |
3248 | | |
3249 | | /** |
3250 | | * pull the next message from a ADS result |
3251 | | * @param ads connection to ads server |
3252 | | * @param res Results of search |
3253 | | * @return next message from result |
3254 | | **/ |
3255 | | LDAPMessage *ads_next_message(ADS_STRUCT *ads, LDAPMessage *res) |
3256 | 0 | { |
3257 | 0 | return ldap_next_message(ads->ldap.ld, res); |
3258 | 0 | } |
3259 | | |
3260 | | /** |
3261 | | * pull a single string from a ADS result |
3262 | | * @param ads connection to ads server |
3263 | | * @param mem_ctx TALLOC_CTX to use for allocating result string |
3264 | | * @param msg Results of search |
3265 | | * @param field Attribute to retrieve |
3266 | | * @return Result string in talloc context |
3267 | | **/ |
3268 | | char *ads_pull_string(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, LDAPMessage *msg, |
3269 | | const char *field) |
3270 | 0 | { |
3271 | 0 | char **values; |
3272 | 0 | char *ret = NULL; |
3273 | 0 | char *ux_string; |
3274 | 0 | size_t converted_size; |
3275 | |
|
3276 | 0 | values = ldap_get_values(ads->ldap.ld, msg, field); |
3277 | 0 | if (!values) |
3278 | 0 | return NULL; |
3279 | | |
3280 | 0 | if (values[0] && pull_utf8_talloc(mem_ctx, &ux_string, values[0], |
3281 | 0 | &converted_size)) |
3282 | 0 | { |
3283 | 0 | ret = ux_string; |
3284 | 0 | } |
3285 | 0 | ldap_value_free(values); |
3286 | 0 | return ret; |
3287 | 0 | } |
3288 | | |
3289 | | /** |
3290 | | * pull an array of strings from a ADS result |
3291 | | * @param ads connection to ads server |
3292 | | * @param mem_ctx TALLOC_CTX to use for allocating result string |
3293 | | * @param msg Results of search |
3294 | | * @param field Attribute to retrieve |
3295 | | * @return Result strings in talloc context |
3296 | | **/ |
3297 | | char **ads_pull_strings(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, |
3298 | | LDAPMessage *msg, const char *field, |
3299 | | size_t *num_values) |
3300 | 0 | { |
3301 | 0 | char **values; |
3302 | 0 | char **ret = NULL; |
3303 | 0 | size_t i, converted_size; |
3304 | |
|
3305 | 0 | values = ldap_get_values(ads->ldap.ld, msg, field); |
3306 | 0 | if (!values) |
3307 | 0 | return NULL; |
3308 | | |
3309 | 0 | *num_values = ldap_count_values(values); |
3310 | |
|
3311 | 0 | ret = talloc_array(mem_ctx, char *, *num_values + 1); |
3312 | 0 | if (!ret) { |
3313 | 0 | ldap_value_free(values); |
3314 | 0 | return NULL; |
3315 | 0 | } |
3316 | | |
3317 | 0 | for (i=0;i<*num_values;i++) { |
3318 | 0 | if (!pull_utf8_talloc(mem_ctx, &ret[i], values[i], |
3319 | 0 | &converted_size)) |
3320 | 0 | { |
3321 | 0 | ldap_value_free(values); |
3322 | 0 | return NULL; |
3323 | 0 | } |
3324 | 0 | } |
3325 | 0 | ret[i] = NULL; |
3326 | |
|
3327 | 0 | ldap_value_free(values); |
3328 | 0 | return ret; |
3329 | 0 | } |
3330 | | |
3331 | | /** |
3332 | | * pull an array of strings from a ADS result |
3333 | | * (handle large multivalue attributes with range retrieval) |
3334 | | * @param ads connection to ads server |
3335 | | * @param mem_ctx TALLOC_CTX to use for allocating result string |
3336 | | * @param msg Results of search |
3337 | | * @param field Attribute to retrieve |
3338 | | * @param current_strings strings returned by a previous call to this function |
3339 | | * @param next_attribute The next query should ask for this attribute |
3340 | | * @param num_values How many values did we get this time? |
3341 | | * @param more_values Are there more values to get? |
3342 | | * @return Result strings in talloc context |
3343 | | **/ |
3344 | | char **ads_pull_strings_range(ADS_STRUCT *ads, |
3345 | | TALLOC_CTX *mem_ctx, |
3346 | | LDAPMessage *msg, const char *field, |
3347 | | char **current_strings, |
3348 | | const char **next_attribute, |
3349 | | size_t *num_strings, |
3350 | | bool *more_strings) |
3351 | 0 | { |
3352 | 0 | char *attr; |
3353 | 0 | char *expected_range_attrib, *range_attr = NULL; |
3354 | 0 | BerElement *ptr = NULL; |
3355 | 0 | char **strings; |
3356 | 0 | char **new_strings; |
3357 | 0 | size_t num_new_strings; |
3358 | 0 | unsigned long int range_start; |
3359 | 0 | unsigned long int range_end; |
3360 | | |
3361 | | /* we might have been given the whole lot anyway */ |
3362 | 0 | if ((strings = ads_pull_strings(ads, mem_ctx, msg, field, num_strings))) { |
3363 | 0 | *more_strings = False; |
3364 | 0 | return strings; |
3365 | 0 | } |
3366 | | |
3367 | 0 | expected_range_attrib = talloc_asprintf(mem_ctx, "%s;Range=", field); |
3368 | | |
3369 | | /* look for Range result */ |
3370 | 0 | for (attr = ldap_first_attribute(ads->ldap.ld, (LDAPMessage *)msg, &ptr); |
3371 | 0 | attr; |
3372 | 0 | attr = ldap_next_attribute(ads->ldap.ld, (LDAPMessage *)msg, ptr)) { |
3373 | | /* we ignore the fact that this is utf8, as all attributes are ascii... */ |
3374 | 0 | if (strnequal(attr, expected_range_attrib, strlen(expected_range_attrib))) { |
3375 | 0 | range_attr = attr; |
3376 | 0 | break; |
3377 | 0 | } |
3378 | 0 | ldap_memfree(attr); |
3379 | 0 | } |
3380 | 0 | if (!range_attr) { |
3381 | 0 | ber_free(ptr, 0); |
3382 | | /* nothing here - this field is just empty */ |
3383 | 0 | *more_strings = False; |
3384 | 0 | return NULL; |
3385 | 0 | } |
3386 | | |
3387 | 0 | if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-%lu", |
3388 | 0 | &range_start, &range_end) == 2) { |
3389 | 0 | *more_strings = True; |
3390 | 0 | } else { |
3391 | 0 | if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-*", |
3392 | 0 | &range_start) == 1) { |
3393 | 0 | *more_strings = False; |
3394 | 0 | } else { |
3395 | 0 | DEBUG(1, ("ads_pull_strings_range: Cannot parse Range attribute (%s)\n", |
3396 | 0 | range_attr)); |
3397 | 0 | ldap_memfree(range_attr); |
3398 | 0 | *more_strings = False; |
3399 | 0 | return NULL; |
3400 | 0 | } |
3401 | 0 | } |
3402 | | |
3403 | 0 | if ((*num_strings) != range_start) { |
3404 | 0 | DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) doesn't start at %u, but at %lu" |
3405 | 0 | " - aborting range retrieval\n", |
3406 | 0 | range_attr, (unsigned int)(*num_strings) + 1, range_start)); |
3407 | 0 | ldap_memfree(range_attr); |
3408 | 0 | *more_strings = False; |
3409 | 0 | return NULL; |
3410 | 0 | } |
3411 | | |
3412 | 0 | new_strings = ads_pull_strings(ads, mem_ctx, msg, range_attr, &num_new_strings); |
3413 | |
|
3414 | 0 | if (*more_strings && ((*num_strings + num_new_strings) != (range_end + 1))) { |
3415 | 0 | DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) tells us we have %lu " |
3416 | 0 | "strings in this bunch, but we only got %lu - aborting range retrieval\n", |
3417 | 0 | range_attr, (unsigned long int)range_end - range_start + 1, |
3418 | 0 | (unsigned long int)num_new_strings)); |
3419 | 0 | ldap_memfree(range_attr); |
3420 | 0 | *more_strings = False; |
3421 | 0 | return NULL; |
3422 | 0 | } |
3423 | | |
3424 | 0 | strings = talloc_realloc(mem_ctx, current_strings, char *, |
3425 | 0 | *num_strings + num_new_strings); |
3426 | |
|
3427 | 0 | if (strings == NULL) { |
3428 | 0 | ldap_memfree(range_attr); |
3429 | 0 | *more_strings = False; |
3430 | 0 | return NULL; |
3431 | 0 | } |
3432 | | |
3433 | 0 | if (new_strings && num_new_strings) { |
3434 | 0 | memcpy(&strings[*num_strings], new_strings, |
3435 | 0 | sizeof(*new_strings) * num_new_strings); |
3436 | 0 | } |
3437 | |
|
3438 | 0 | (*num_strings) += num_new_strings; |
3439 | |
|
3440 | 0 | if (*more_strings) { |
3441 | 0 | *next_attribute = talloc_asprintf(mem_ctx, |
3442 | 0 | "%s;range=%d-*", |
3443 | 0 | field, |
3444 | 0 | (int)*num_strings); |
3445 | |
|
3446 | 0 | if (!*next_attribute) { |
3447 | 0 | DEBUG(1, ("talloc_asprintf for next attribute failed!\n")); |
3448 | 0 | ldap_memfree(range_attr); |
3449 | 0 | *more_strings = False; |
3450 | 0 | return NULL; |
3451 | 0 | } |
3452 | 0 | } |
3453 | | |
3454 | 0 | ldap_memfree(range_attr); |
3455 | |
|
3456 | 0 | return strings; |
3457 | 0 | } |
3458 | | |
3459 | | /** |
3460 | | * pull a single uint32_t from a ADS result |
3461 | | * @param ads connection to ads server |
3462 | | * @param msg Results of search |
3463 | | * @param field Attribute to retrieve |
3464 | | * @param v Pointer to int to store result |
3465 | | * @return boolean indicating success |
3466 | | */ |
3467 | | bool ads_pull_uint32(ADS_STRUCT *ads, LDAPMessage *msg, const char *field, |
3468 | | uint32_t *v) |
3469 | 0 | { |
3470 | 0 | char **values; |
3471 | |
|
3472 | 0 | values = ldap_get_values(ads->ldap.ld, msg, field); |
3473 | 0 | if (!values) |
3474 | 0 | return False; |
3475 | 0 | if (!values[0]) { |
3476 | 0 | ldap_value_free(values); |
3477 | 0 | return False; |
3478 | 0 | } |
3479 | | |
3480 | 0 | *v = atoi(values[0]); |
3481 | 0 | ldap_value_free(values); |
3482 | 0 | return True; |
3483 | 0 | } |
3484 | | |
3485 | | /** |
3486 | | * pull a single objectGUID from an ADS result |
3487 | | * @param ads connection to ADS server |
3488 | | * @param msg results of search |
3489 | | * @param guid 37-byte area to receive text guid |
3490 | | * @return boolean indicating success |
3491 | | **/ |
3492 | | bool ads_pull_guid(ADS_STRUCT *ads, LDAPMessage *msg, struct GUID *guid) |
3493 | 0 | { |
3494 | 0 | DATA_BLOB blob; |
3495 | 0 | NTSTATUS status; |
3496 | |
|
3497 | 0 | if (!smbldap_talloc_single_blob(talloc_tos(), ads->ldap.ld, msg, "objectGUID", |
3498 | 0 | &blob)) { |
3499 | 0 | return false; |
3500 | 0 | } |
3501 | | |
3502 | 0 | status = GUID_from_ndr_blob(&blob, guid); |
3503 | 0 | talloc_free(blob.data); |
3504 | 0 | return NT_STATUS_IS_OK(status); |
3505 | 0 | } |
3506 | | |
3507 | | |
3508 | | /** |
3509 | | * pull a single struct dom_sid from a ADS result |
3510 | | * @param ads connection to ads server |
3511 | | * @param msg Results of search |
3512 | | * @param field Attribute to retrieve |
3513 | | * @param sid Pointer to sid to store result |
3514 | | * @return boolean indicating success |
3515 | | */ |
3516 | | bool ads_pull_sid(ADS_STRUCT *ads, LDAPMessage *msg, const char *field, |
3517 | | struct dom_sid *sid) |
3518 | 0 | { |
3519 | 0 | return smbldap_pull_sid(ads->ldap.ld, msg, field, sid); |
3520 | 0 | } |
3521 | | |
3522 | | /** |
3523 | | * pull an array of struct dom_sids from a ADS result |
3524 | | * @param ads connection to ads server |
3525 | | * @param mem_ctx TALLOC_CTX for allocating sid array |
3526 | | * @param msg Results of search |
3527 | | * @param field Attribute to retrieve |
3528 | | * @param sids pointer to sid array to allocate |
3529 | | * @return the count of SIDs pulled |
3530 | | **/ |
3531 | | int ads_pull_sids(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, |
3532 | | LDAPMessage *msg, const char *field, struct dom_sid **sids) |
3533 | 0 | { |
3534 | 0 | struct berval **values; |
3535 | 0 | int count, i; |
3536 | |
|
3537 | 0 | values = ldap_get_values_len(ads->ldap.ld, msg, field); |
3538 | |
|
3539 | 0 | if (!values) |
3540 | 0 | return 0; |
3541 | | |
3542 | 0 | for (i=0; values[i]; i++) |
3543 | 0 | /* nop */ ; |
3544 | |
|
3545 | 0 | if (i) { |
3546 | 0 | (*sids) = talloc_array(mem_ctx, struct dom_sid, i); |
3547 | 0 | if (!(*sids)) { |
3548 | 0 | ldap_value_free_len(values); |
3549 | 0 | return 0; |
3550 | 0 | } |
3551 | 0 | } else { |
3552 | 0 | (*sids) = NULL; |
3553 | 0 | } |
3554 | | |
3555 | 0 | count = 0; |
3556 | 0 | for (i=0; values[i]; i++) { |
3557 | 0 | ssize_t ret; |
3558 | 0 | ret = sid_parse((const uint8_t *)values[i]->bv_val, |
3559 | 0 | values[i]->bv_len, &(*sids)[count]); |
3560 | 0 | if (ret != -1) { |
3561 | 0 | struct dom_sid_buf buf; |
3562 | 0 | DBG_DEBUG("pulling SID: %s\n", |
3563 | 0 | dom_sid_str_buf(&(*sids)[count], &buf)); |
3564 | 0 | count++; |
3565 | 0 | } |
3566 | 0 | } |
3567 | |
|
3568 | 0 | ldap_value_free_len(values); |
3569 | 0 | return count; |
3570 | 0 | } |
3571 | | |
3572 | | /** |
3573 | | * pull a struct security_descriptor from a ADS result |
3574 | | * @param ads connection to ads server |
3575 | | * @param mem_ctx TALLOC_CTX for allocating sid array |
3576 | | * @param msg Results of search |
3577 | | * @param field Attribute to retrieve |
3578 | | * @param sd Pointer to *struct security_descriptor to store result (talloc()ed) |
3579 | | * @return boolean indicating success |
3580 | | */ |
3581 | | bool ads_pull_sd(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, |
3582 | | LDAPMessage *msg, const char *field, |
3583 | | struct security_descriptor **sd) |
3584 | 0 | { |
3585 | 0 | struct berval **values; |
3586 | 0 | bool ret = true; |
3587 | |
|
3588 | 0 | values = ldap_get_values_len(ads->ldap.ld, msg, field); |
3589 | |
|
3590 | 0 | if (!values) return false; |
3591 | | |
3592 | 0 | if (values[0]) { |
3593 | 0 | NTSTATUS status; |
3594 | 0 | status = unmarshall_sec_desc(mem_ctx, |
3595 | 0 | (uint8_t *)values[0]->bv_val, |
3596 | 0 | values[0]->bv_len, sd); |
3597 | 0 | if (!NT_STATUS_IS_OK(status)) { |
3598 | 0 | DEBUG(0, ("unmarshall_sec_desc failed: %s\n", |
3599 | 0 | nt_errstr(status))); |
3600 | 0 | ret = false; |
3601 | 0 | } |
3602 | 0 | } |
3603 | |
|
3604 | 0 | ldap_value_free_len(values); |
3605 | 0 | return ret; |
3606 | 0 | } |
3607 | | |
3608 | | /* |
3609 | | * in order to support usernames longer than 21 characters we need to |
3610 | | * use both the sAMAccountName and the userPrincipalName attributes |
3611 | | * It seems that not all users have the userPrincipalName attribute set |
3612 | | * |
3613 | | * @param ads connection to ads server |
3614 | | * @param mem_ctx TALLOC_CTX for allocating sid array |
3615 | | * @param msg Results of search |
3616 | | * @return the username |
3617 | | */ |
3618 | | char *ads_pull_username(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, |
3619 | | LDAPMessage *msg) |
3620 | 0 | { |
3621 | | #if 0 /* JERRY */ |
3622 | | char *ret, *p; |
3623 | | |
3624 | | /* lookup_name() only works on the sAMAccountName to |
3625 | | returning the username portion of userPrincipalName |
3626 | | breaks winbindd_getpwnam() */ |
3627 | | |
3628 | | ret = ads_pull_string(ads, mem_ctx, msg, "userPrincipalName"); |
3629 | | if (ret && (p = strchr_m(ret, '@'))) { |
3630 | | *p = 0; |
3631 | | return ret; |
3632 | | } |
3633 | | #endif |
3634 | 0 | return ads_pull_string(ads, mem_ctx, msg, "sAMAccountName"); |
3635 | 0 | } |
3636 | | |
3637 | | |
3638 | | /** |
3639 | | * find the update serial number - this is the core of the ldap cache |
3640 | | * @param ads connection to ads server |
3641 | | * @param ads connection to ADS server |
3642 | | * @param usn Pointer to retrieved update serial number |
3643 | | * @return status of search |
3644 | | **/ |
3645 | | ADS_STATUS ads_USN(ADS_STRUCT *ads, uint32_t *usn) |
3646 | 0 | { |
3647 | 0 | const char *attrs[] = {"highestCommittedUSN", NULL}; |
3648 | 0 | ADS_STATUS status; |
3649 | 0 | LDAPMessage *res; |
3650 | |
|
3651 | 0 | status = ads_do_search_retry(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res); |
3652 | 0 | if (!ADS_ERR_OK(status)) |
3653 | 0 | return status; |
3654 | | |
3655 | 0 | if (ads_count_replies(ads, res) != 1) { |
3656 | 0 | ads_msgfree(ads, res); |
3657 | 0 | return ADS_ERROR(LDAP_NO_RESULTS_RETURNED); |
3658 | 0 | } |
3659 | | |
3660 | 0 | if (!ads_pull_uint32(ads, res, "highestCommittedUSN", usn)) { |
3661 | 0 | ads_msgfree(ads, res); |
3662 | 0 | return ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE); |
3663 | 0 | } |
3664 | | |
3665 | 0 | ads_msgfree(ads, res); |
3666 | 0 | return ADS_SUCCESS; |
3667 | 0 | } |
3668 | | |
3669 | | /* parse a ADS timestring - typical string is |
3670 | | '20020917091222.0Z0' which means 09:12.22 17th September |
3671 | | 2002, timezone 0 */ |
3672 | | static time_t ads_parse_time(const char *str) |
3673 | 0 | { |
3674 | 0 | struct tm tm; |
3675 | |
|
3676 | 0 | ZERO_STRUCT(tm); |
3677 | |
|
3678 | 0 | if (sscanf(str, "%4d%2d%2d%2d%2d%2d", |
3679 | 0 | &tm.tm_year, &tm.tm_mon, &tm.tm_mday, |
3680 | 0 | &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) { |
3681 | 0 | return 0; |
3682 | 0 | } |
3683 | 0 | tm.tm_year -= 1900; |
3684 | 0 | tm.tm_mon -= 1; |
3685 | |
|
3686 | 0 | return timegm(&tm); |
3687 | 0 | } |
3688 | | |
3689 | | /******************************************************************** |
3690 | | ********************************************************************/ |
3691 | | |
3692 | | ADS_STATUS ads_current_time(ADS_STRUCT *ads) |
3693 | 0 | { |
3694 | 0 | const char *attrs[] = {"currentTime", NULL}; |
3695 | 0 | ADS_STATUS status; |
3696 | 0 | LDAPMessage *res; |
3697 | 0 | char *timestr; |
3698 | 0 | TALLOC_CTX *tmp_ctx = talloc_stackframe(); |
3699 | 0 | ADS_STRUCT *ads_s = ads; |
3700 | | |
3701 | | /* establish a new ldap tcp session if necessary */ |
3702 | |
|
3703 | 0 | if ( !ads->ldap.ld ) { |
3704 | | /* |
3705 | | * ADS_STRUCT may be being reused after a |
3706 | | * DC lookup, so ads->ldap.ss may already have a |
3707 | | * good address. If not, re-initialize the passed-in |
3708 | | * ADS_STRUCT with the given server.XXXX parameters. |
3709 | | * |
3710 | | * Note that this doesn't depend on |
3711 | | * ads->server.ldap_server != NULL, |
3712 | | * as the case where ads->server.ldap_server==NULL and |
3713 | | * ads->ldap.ss != zero_address is precisely the DC |
3714 | | * lookup case where ads->ldap.ss was found by going |
3715 | | * through ads_find_dc() again we want to avoid repeating. |
3716 | | */ |
3717 | 0 | if (is_zero_addr(&ads->ldap.ss)) { |
3718 | 0 | ads_s = ads_init(tmp_ctx, |
3719 | 0 | ads->server.realm, |
3720 | 0 | ads->server.workgroup, |
3721 | 0 | ads->server.ldap_server, |
3722 | 0 | ADS_SASL_PLAIN ); |
3723 | 0 | if (ads_s == NULL) { |
3724 | 0 | status = ADS_ERROR(LDAP_NO_MEMORY); |
3725 | 0 | goto done; |
3726 | 0 | } |
3727 | 0 | } |
3728 | | |
3729 | | /* |
3730 | | * Reset ads->config.flags as it can contain the flags |
3731 | | * returned by the previous CLDAP ping when reusing the struct. |
3732 | | */ |
3733 | 0 | ads_s->config.flags = 0; |
3734 | |
|
3735 | 0 | status = ads_connect_simple_anon(ads_s); |
3736 | 0 | if ( !ADS_ERR_OK(status)) |
3737 | 0 | goto done; |
3738 | 0 | } |
3739 | | |
3740 | 0 | status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res); |
3741 | 0 | if (!ADS_ERR_OK(status)) { |
3742 | 0 | goto done; |
3743 | 0 | } |
3744 | | |
3745 | 0 | timestr = ads_pull_string(ads_s, tmp_ctx, res, "currentTime"); |
3746 | 0 | if (!timestr) { |
3747 | 0 | ads_msgfree(ads_s, res); |
3748 | 0 | status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED); |
3749 | 0 | goto done; |
3750 | 0 | } |
3751 | | |
3752 | | /* but save the time and offset in the original ADS_STRUCT */ |
3753 | | |
3754 | 0 | ads->config.current_time = ads_parse_time(timestr); |
3755 | |
|
3756 | 0 | if (ads->config.current_time != 0) { |
3757 | 0 | ads->config.time_offset = ads->config.current_time - time(NULL); |
3758 | 0 | DBG_INFO("server time offset is %d seconds\n", |
3759 | 0 | ads->config.time_offset); |
3760 | 0 | } else { |
3761 | 0 | ads->config.time_offset = 0; |
3762 | 0 | } |
3763 | |
|
3764 | 0 | DBG_INFO("server time offset is %d seconds\n", |
3765 | 0 | ads->config.time_offset); |
3766 | |
|
3767 | 0 | ads_msgfree(ads, res); |
3768 | |
|
3769 | 0 | status = ADS_SUCCESS; |
3770 | |
|
3771 | 0 | done: |
3772 | 0 | TALLOC_FREE(tmp_ctx); |
3773 | |
|
3774 | 0 | return status; |
3775 | 0 | } |
3776 | | |
3777 | | /******************************************************************** |
3778 | | ********************************************************************/ |
3779 | | |
3780 | | ADS_STATUS ads_domain_func_level(ADS_STRUCT *ads, uint32_t *val) |
3781 | 0 | { |
3782 | 0 | TALLOC_CTX *tmp_ctx = talloc_stackframe(); |
3783 | 0 | const char *attrs[] = {"domainFunctionality", NULL}; |
3784 | 0 | ADS_STATUS status; |
3785 | 0 | LDAPMessage *res; |
3786 | 0 | ADS_STRUCT *ads_s = ads; |
3787 | |
|
3788 | 0 | *val = DS_DOMAIN_FUNCTION_2000; |
3789 | | |
3790 | | /* establish a new ldap tcp session if necessary */ |
3791 | |
|
3792 | 0 | if ( !ads->ldap.ld ) { |
3793 | | /* |
3794 | | * ADS_STRUCT may be being reused after a |
3795 | | * DC lookup, so ads->ldap.ss may already have a |
3796 | | * good address. If not, re-initialize the passed-in |
3797 | | * ADS_STRUCT with the given server.XXXX parameters. |
3798 | | * |
3799 | | * Note that this doesn't depend on |
3800 | | * ads->server.ldap_server != NULL, |
3801 | | * as the case where ads->server.ldap_server==NULL and |
3802 | | * ads->ldap.ss != zero_address is precisely the DC |
3803 | | * lookup case where ads->ldap.ss was found by going |
3804 | | * through ads_find_dc() again we want to avoid repeating. |
3805 | | */ |
3806 | 0 | if (is_zero_addr(&ads->ldap.ss)) { |
3807 | 0 | ads_s = ads_init(tmp_ctx, |
3808 | 0 | ads->server.realm, |
3809 | 0 | ads->server.workgroup, |
3810 | 0 | ads->server.ldap_server, |
3811 | 0 | ADS_SASL_PLAIN ); |
3812 | 0 | if (ads_s == NULL ) { |
3813 | 0 | status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY); |
3814 | 0 | goto done; |
3815 | 0 | } |
3816 | 0 | } |
3817 | | |
3818 | | /* |
3819 | | * Reset ads->config.flags as it can contain the flags |
3820 | | * returned by the previous CLDAP ping when reusing the struct. |
3821 | | */ |
3822 | 0 | ads_s->config.flags = 0; |
3823 | |
|
3824 | 0 | status = ads_connect_simple_anon(ads_s); |
3825 | 0 | if ( !ADS_ERR_OK(status)) |
3826 | 0 | goto done; |
3827 | 0 | } |
3828 | | |
3829 | | /* If the attribute does not exist assume it is a Windows 2000 |
3830 | | functional domain */ |
3831 | | |
3832 | 0 | status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res); |
3833 | 0 | if (!ADS_ERR_OK(status)) { |
3834 | 0 | if ( status.err.rc == LDAP_NO_SUCH_ATTRIBUTE ) { |
3835 | 0 | status = ADS_SUCCESS; |
3836 | 0 | } |
3837 | 0 | goto done; |
3838 | 0 | } |
3839 | | |
3840 | 0 | if ( !ads_pull_uint32(ads_s, res, "domainFunctionality", val) ) { |
3841 | 0 | DEBUG(5,("ads_domain_func_level: Failed to pull the domainFunctionality attribute.\n")); |
3842 | 0 | } |
3843 | 0 | DEBUG(3,("ads_domain_func_level: %d\n", *val)); |
3844 | | |
3845 | |
|
3846 | 0 | ads_msgfree(ads_s, res); |
3847 | |
|
3848 | 0 | done: |
3849 | 0 | TALLOC_FREE(tmp_ctx); |
3850 | |
|
3851 | 0 | return status; |
3852 | 0 | } |
3853 | | |
3854 | | /** |
3855 | | * find the domain sid for our domain |
3856 | | * @param ads connection to ads server |
3857 | | * @param sid Pointer to domain sid |
3858 | | * @return status of search |
3859 | | **/ |
3860 | | ADS_STATUS ads_domain_sid(ADS_STRUCT *ads, struct dom_sid *sid) |
3861 | 0 | { |
3862 | 0 | const char *attrs[] = {"objectSid", NULL}; |
3863 | 0 | LDAPMessage *res; |
3864 | 0 | ADS_STATUS rc; |
3865 | |
|
3866 | 0 | rc = ads_do_search_retry(ads, ads->config.bind_path, LDAP_SCOPE_BASE, "(objectclass=*)", |
3867 | 0 | attrs, &res); |
3868 | 0 | if (!ADS_ERR_OK(rc)) return rc; |
3869 | 0 | if (!ads_pull_sid(ads, res, "objectSid", sid)) { |
3870 | 0 | ads_msgfree(ads, res); |
3871 | 0 | return ADS_ERROR_SYSTEM(ENOENT); |
3872 | 0 | } |
3873 | 0 | ads_msgfree(ads, res); |
3874 | |
|
3875 | 0 | return ADS_SUCCESS; |
3876 | 0 | } |
3877 | | |
3878 | | /** |
3879 | | * find our site name |
3880 | | * @param ads connection to ads server |
3881 | | * @param mem_ctx Pointer to talloc context |
3882 | | * @param site_name Pointer to the sitename |
3883 | | * @return status of search |
3884 | | **/ |
3885 | | ADS_STATUS ads_site_dn(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char **site_name) |
3886 | 0 | { |
3887 | 0 | ADS_STATUS status; |
3888 | 0 | LDAPMessage *res; |
3889 | 0 | const char *dn, *service_name; |
3890 | 0 | const char *attrs[] = { "dsServiceName", NULL }; |
3891 | |
|
3892 | 0 | status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res); |
3893 | 0 | if (!ADS_ERR_OK(status)) { |
3894 | 0 | return status; |
3895 | 0 | } |
3896 | | |
3897 | 0 | service_name = ads_pull_string(ads, mem_ctx, res, "dsServiceName"); |
3898 | 0 | if (service_name == NULL) { |
3899 | 0 | ads_msgfree(ads, res); |
3900 | 0 | return ADS_ERROR(LDAP_NO_RESULTS_RETURNED); |
3901 | 0 | } |
3902 | | |
3903 | 0 | ads_msgfree(ads, res); |
3904 | | |
3905 | | /* go up three levels */ |
3906 | 0 | dn = ads_parent_dn(ads_parent_dn(ads_parent_dn(service_name))); |
3907 | 0 | if (dn == NULL) { |
3908 | 0 | return ADS_ERROR(LDAP_NO_MEMORY); |
3909 | 0 | } |
3910 | | |
3911 | 0 | *site_name = talloc_strdup(mem_ctx, dn); |
3912 | 0 | if (*site_name == NULL) { |
3913 | 0 | return ADS_ERROR(LDAP_NO_MEMORY); |
3914 | 0 | } |
3915 | | |
3916 | 0 | return status; |
3917 | | /* |
3918 | | dsServiceName: CN=NTDS Settings,CN=W2K3DC,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,DC=ber,DC=suse,DC=de |
3919 | | */ |
3920 | 0 | } |
3921 | | |
3922 | | /** |
3923 | | * find the site dn where a machine resides |
3924 | | * @param ads connection to ads server |
3925 | | * @param mem_ctx Pointer to talloc context |
3926 | | * @param computer_name name of the machine |
3927 | | * @param site_name Pointer to the sitename |
3928 | | * @return status of search |
3929 | | **/ |
3930 | | ADS_STATUS ads_site_dn_for_machine(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char *computer_name, const char **site_dn) |
3931 | 0 | { |
3932 | 0 | ADS_STATUS status; |
3933 | 0 | LDAPMessage *res; |
3934 | 0 | const char *parent, *filter; |
3935 | 0 | char *config_context = NULL; |
3936 | 0 | char *dn; |
3937 | | |
3938 | | /* shortcut a query */ |
3939 | 0 | if (strequal(computer_name, ads->config.ldap_server_name)) { |
3940 | 0 | return ads_site_dn(ads, mem_ctx, site_dn); |
3941 | 0 | } |
3942 | | |
3943 | 0 | status = ads_config_path(ads, mem_ctx, &config_context); |
3944 | 0 | if (!ADS_ERR_OK(status)) { |
3945 | 0 | return status; |
3946 | 0 | } |
3947 | | |
3948 | 0 | filter = talloc_asprintf(mem_ctx, "(cn=%s)", computer_name); |
3949 | 0 | if (filter == NULL) { |
3950 | 0 | return ADS_ERROR(LDAP_NO_MEMORY); |
3951 | 0 | } |
3952 | | |
3953 | 0 | status = ads_do_search(ads, config_context, LDAP_SCOPE_SUBTREE, |
3954 | 0 | filter, NULL, &res); |
3955 | 0 | if (!ADS_ERR_OK(status)) { |
3956 | 0 | return status; |
3957 | 0 | } |
3958 | | |
3959 | 0 | if (ads_count_replies(ads, res) != 1) { |
3960 | 0 | ads_msgfree(ads, res); |
3961 | 0 | return ADS_ERROR(LDAP_NO_SUCH_OBJECT); |
3962 | 0 | } |
3963 | | |
3964 | 0 | dn = ads_get_dn(ads, mem_ctx, res); |
3965 | 0 | if (dn == NULL) { |
3966 | 0 | ads_msgfree(ads, res); |
3967 | 0 | return ADS_ERROR(LDAP_NO_MEMORY); |
3968 | 0 | } |
3969 | | |
3970 | | /* go up three levels */ |
3971 | 0 | parent = ads_parent_dn(ads_parent_dn(ads_parent_dn(dn))); |
3972 | 0 | if (parent == NULL) { |
3973 | 0 | ads_msgfree(ads, res); |
3974 | 0 | TALLOC_FREE(dn); |
3975 | 0 | return ADS_ERROR(LDAP_NO_MEMORY); |
3976 | 0 | } |
3977 | | |
3978 | 0 | *site_dn = talloc_strdup(mem_ctx, parent); |
3979 | 0 | if (*site_dn == NULL) { |
3980 | 0 | ads_msgfree(ads, res); |
3981 | 0 | TALLOC_FREE(dn); |
3982 | 0 | return ADS_ERROR(LDAP_NO_MEMORY); |
3983 | 0 | } |
3984 | | |
3985 | 0 | TALLOC_FREE(dn); |
3986 | 0 | ads_msgfree(ads, res); |
3987 | |
|
3988 | 0 | return status; |
3989 | 0 | } |
3990 | | |
3991 | | /** |
3992 | | * get the upn suffixes for a domain |
3993 | | * @param ads connection to ads server |
3994 | | * @param mem_ctx Pointer to talloc context |
3995 | | * @param suffixes Pointer to an array of suffixes |
3996 | | * @param num_suffixes Pointer to the number of suffixes |
3997 | | * @return status of search |
3998 | | **/ |
3999 | | ADS_STATUS ads_upn_suffixes(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, char ***suffixes, size_t *num_suffixes) |
4000 | 0 | { |
4001 | 0 | ADS_STATUS status; |
4002 | 0 | LDAPMessage *res; |
4003 | 0 | const char *base; |
4004 | 0 | char *config_context = NULL; |
4005 | 0 | const char *attrs[] = { "uPNSuffixes", NULL }; |
4006 | |
|
4007 | 0 | status = ads_config_path(ads, mem_ctx, &config_context); |
4008 | 0 | if (!ADS_ERR_OK(status)) { |
4009 | 0 | return status; |
4010 | 0 | } |
4011 | | |
4012 | 0 | base = talloc_asprintf(mem_ctx, "cn=Partitions,%s", config_context); |
4013 | 0 | if (base == NULL) { |
4014 | 0 | return ADS_ERROR(LDAP_NO_MEMORY); |
4015 | 0 | } |
4016 | | |
4017 | 0 | status = ads_search_dn(ads, &res, base, attrs); |
4018 | 0 | if (!ADS_ERR_OK(status)) { |
4019 | 0 | return status; |
4020 | 0 | } |
4021 | | |
4022 | 0 | if (ads_count_replies(ads, res) != 1) { |
4023 | 0 | ads_msgfree(ads, res); |
4024 | 0 | return ADS_ERROR(LDAP_NO_SUCH_OBJECT); |
4025 | 0 | } |
4026 | | |
4027 | 0 | (*suffixes) = ads_pull_strings(ads, mem_ctx, res, "uPNSuffixes", num_suffixes); |
4028 | 0 | if ((*suffixes) == NULL) { |
4029 | 0 | ads_msgfree(ads, res); |
4030 | 0 | return ADS_ERROR(LDAP_NO_MEMORY); |
4031 | 0 | } |
4032 | | |
4033 | 0 | ads_msgfree(ads, res); |
4034 | |
|
4035 | 0 | return status; |
4036 | 0 | } |
4037 | | |
4038 | | /** |
4039 | | * get the joinable ous for a domain |
4040 | | * @param ads connection to ads server |
4041 | | * @param mem_ctx Pointer to talloc context |
4042 | | * @param ous Pointer to an array of ous |
4043 | | * @param num_ous Pointer to the number of ous |
4044 | | * @return status of search |
4045 | | **/ |
4046 | | ADS_STATUS ads_get_joinable_ous(ADS_STRUCT *ads, |
4047 | | TALLOC_CTX *mem_ctx, |
4048 | | char ***ous, |
4049 | | size_t *num_ous) |
4050 | 0 | { |
4051 | 0 | ADS_STATUS status; |
4052 | 0 | LDAPMessage *res = NULL; |
4053 | 0 | LDAPMessage *msg = NULL; |
4054 | 0 | const char *attrs[] = { "dn", NULL }; |
4055 | 0 | int count = 0; |
4056 | |
|
4057 | 0 | status = ads_search(ads, &res, |
4058 | 0 | "(|(objectClass=domain)(objectclass=organizationalUnit))", |
4059 | 0 | attrs); |
4060 | 0 | if (!ADS_ERR_OK(status)) { |
4061 | 0 | return status; |
4062 | 0 | } |
4063 | | |
4064 | 0 | count = ads_count_replies(ads, res); |
4065 | 0 | if (count < 1) { |
4066 | 0 | ads_msgfree(ads, res); |
4067 | 0 | return ADS_ERROR(LDAP_NO_RESULTS_RETURNED); |
4068 | 0 | } |
4069 | | |
4070 | 0 | for (msg = ads_first_entry(ads, res); msg; |
4071 | 0 | msg = ads_next_entry(ads, msg)) { |
4072 | 0 | const char **p = discard_const_p(const char *, *ous); |
4073 | 0 | char *dn = NULL; |
4074 | |
|
4075 | 0 | dn = ads_get_dn(ads, talloc_tos(), msg); |
4076 | 0 | if (!dn) { |
4077 | 0 | ads_msgfree(ads, res); |
4078 | 0 | return ADS_ERROR(LDAP_NO_MEMORY); |
4079 | 0 | } |
4080 | | |
4081 | 0 | if (!add_string_to_array(mem_ctx, dn, &p, num_ous)) { |
4082 | 0 | TALLOC_FREE(dn); |
4083 | 0 | ads_msgfree(ads, res); |
4084 | 0 | return ADS_ERROR(LDAP_NO_MEMORY); |
4085 | 0 | } |
4086 | | |
4087 | 0 | TALLOC_FREE(dn); |
4088 | 0 | *ous = discard_const_p(char *, p); |
4089 | 0 | } |
4090 | | |
4091 | 0 | ads_msgfree(ads, res); |
4092 | |
|
4093 | 0 | return status; |
4094 | 0 | } |
4095 | | |
4096 | | |
4097 | | /** |
4098 | | * pull a struct dom_sid from an extended dn string |
4099 | | * @param mem_ctx TALLOC_CTX |
4100 | | * @param extended_dn string |
4101 | | * @param flags string type of extended_dn |
4102 | | * @param sid pointer to a struct dom_sid |
4103 | | * @return NT_STATUS_OK on success, |
4104 | | * NT_INVALID_PARAMETER on error, |
4105 | | * NT_STATUS_NOT_FOUND if no SID present |
4106 | | **/ |
4107 | | ADS_STATUS ads_get_sid_from_extended_dn(TALLOC_CTX *mem_ctx, |
4108 | | const char *extended_dn, |
4109 | | enum ads_extended_dn_flags flags, |
4110 | | struct dom_sid *sid) |
4111 | 0 | { |
4112 | 0 | char *p, *q, *dn; |
4113 | |
|
4114 | 0 | if (!extended_dn) { |
4115 | 0 | return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER); |
4116 | 0 | } |
4117 | | |
4118 | | /* otherwise extended_dn gets stripped off */ |
4119 | 0 | if ((dn = talloc_strdup(mem_ctx, extended_dn)) == NULL) { |
4120 | 0 | return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER); |
4121 | 0 | } |
4122 | | /* |
4123 | | * ADS_EXTENDED_DN_HEX_STRING: |
4124 | | * <GUID=238e1963cb390f4bb032ba0105525a29>;<SID=010500000000000515000000bb68c8fd6b61b427572eb04556040000>;CN=gd,OU=berlin,OU=suse,DC=ber,DC=suse,DC=de |
4125 | | * |
4126 | | * ADS_EXTENDED_DN_STRING (only with w2k3): |
4127 | | * <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 |
4128 | | * |
4129 | | * Object with no SID, such as an Exchange Public Folder |
4130 | | * <GUID=28907fb4bdf6854993e7f0a10b504e7c>;CN=public,CN=Microsoft Exchange System Objects,DC=sd2k3ms,DC=west,DC=isilon,DC=com |
4131 | | */ |
4132 | | |
4133 | 0 | p = strchr(dn, ';'); |
4134 | 0 | if (!p) { |
4135 | 0 | return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER); |
4136 | 0 | } |
4137 | | |
4138 | 0 | if (strncmp(p, ";<SID=", strlen(";<SID=")) != 0) { |
4139 | 0 | DEBUG(5,("No SID present in extended dn\n")); |
4140 | 0 | return ADS_ERROR_NT(NT_STATUS_NOT_FOUND); |
4141 | 0 | } |
4142 | | |
4143 | 0 | p += strlen(";<SID="); |
4144 | |
|
4145 | 0 | q = strchr(p, '>'); |
4146 | 0 | if (!q) { |
4147 | 0 | return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER); |
4148 | 0 | } |
4149 | | |
4150 | 0 | *q = '\0'; |
4151 | |
|
4152 | 0 | DEBUG(100,("ads_get_sid_from_extended_dn: sid string is %s\n", p)); |
4153 | |
|
4154 | 0 | switch (flags) { |
4155 | | |
4156 | 0 | case ADS_EXTENDED_DN_STRING: |
4157 | 0 | if (!string_to_sid(sid, p)) { |
4158 | 0 | return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER); |
4159 | 0 | } |
4160 | 0 | break; |
4161 | 0 | case ADS_EXTENDED_DN_HEX_STRING: { |
4162 | 0 | ssize_t ret; |
4163 | 0 | fstring buf; |
4164 | 0 | size_t buf_len; |
4165 | |
|
4166 | 0 | buf_len = strhex_to_str(buf, sizeof(buf), p, strlen(p)); |
4167 | 0 | if (buf_len == 0) { |
4168 | 0 | return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER); |
4169 | 0 | } |
4170 | | |
4171 | 0 | ret = sid_parse((const uint8_t *)buf, buf_len, sid); |
4172 | 0 | if (ret == -1) { |
4173 | 0 | DEBUG(10,("failed to parse sid\n")); |
4174 | 0 | return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER); |
4175 | 0 | } |
4176 | 0 | break; |
4177 | 0 | } |
4178 | 0 | default: |
4179 | 0 | DEBUG(10,("unknown extended dn format\n")); |
4180 | 0 | return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER); |
4181 | 0 | } |
4182 | | |
4183 | 0 | return ADS_ERROR_NT(NT_STATUS_OK); |
4184 | 0 | } |
4185 | | |
4186 | | /******************************************************************** |
4187 | | ********************************************************************/ |
4188 | | |
4189 | | char* ads_get_upn( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name ) |
4190 | 0 | { |
4191 | 0 | LDAPMessage *res = NULL; |
4192 | 0 | ADS_STATUS status; |
4193 | 0 | int count = 0; |
4194 | 0 | char *name = NULL; |
4195 | |
|
4196 | 0 | status = ads_find_machine_acct(ads, &res, machine_name); |
4197 | 0 | if (!ADS_ERR_OK(status)) { |
4198 | 0 | DEBUG(0,("ads_get_upn: Failed to find account for %s\n", |
4199 | 0 | lp_netbios_name())); |
4200 | 0 | goto out; |
4201 | 0 | } |
4202 | | |
4203 | 0 | if ( (count = ads_count_replies(ads, res)) != 1 ) { |
4204 | 0 | DEBUG(1,("ads_get_upn: %d entries returned!\n", count)); |
4205 | 0 | goto out; |
4206 | 0 | } |
4207 | | |
4208 | 0 | if ( (name = ads_pull_string(ads, ctx, res, "userPrincipalName")) == NULL ) { |
4209 | 0 | DEBUG(2,("ads_get_upn: No userPrincipalName attribute!\n")); |
4210 | 0 | } |
4211 | |
|
4212 | 0 | out: |
4213 | 0 | ads_msgfree(ads, res); |
4214 | |
|
4215 | 0 | return name; |
4216 | 0 | } |
4217 | | |
4218 | | #if 0 |
4219 | | |
4220 | | SAVED CODE - we used to join via ldap - remember how we did this. JRA. |
4221 | | |
4222 | | /** |
4223 | | * Join a machine to a realm |
4224 | | * Creates the machine account and sets the machine password |
4225 | | * @param ads connection to ads server |
4226 | | * @param machine name of host to add |
4227 | | * @param org_unit Organizational unit to place machine in |
4228 | | * @return status of join |
4229 | | **/ |
4230 | | ADS_STATUS ads_join_realm(ADS_STRUCT *ads, const char *machine_name, |
4231 | | uint32_t account_type, const char *org_unit) |
4232 | | { |
4233 | | ADS_STATUS status; |
4234 | | LDAPMessage *res = NULL; |
4235 | | char *machine; |
4236 | | |
4237 | | /* machine name must be lowercase */ |
4238 | | machine = SMB_STRDUP(machine_name); |
4239 | | strlower_m(machine); |
4240 | | |
4241 | | /* |
4242 | | status = ads_find_machine_acct(ads, (void **)&res, machine); |
4243 | | if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) { |
4244 | | DEBUG(0, ("Host account for %s already exists - deleting old account\n", machine)); |
4245 | | status = ads_leave_realm(ads, machine); |
4246 | | if (!ADS_ERR_OK(status)) { |
4247 | | DEBUG(0, ("Failed to delete host '%s' from the '%s' realm.\n", |
4248 | | machine, ads->config.realm)); |
4249 | | return status; |
4250 | | } |
4251 | | } |
4252 | | */ |
4253 | | status = ads_add_machine_acct(ads, machine, account_type, org_unit); |
4254 | | if (!ADS_ERR_OK(status)) { |
4255 | | DEBUG(0, ("ads_join_realm: ads_add_machine_acct failed (%s): %s\n", machine, ads_errstr(status))); |
4256 | | SAFE_FREE(machine); |
4257 | | return status; |
4258 | | } |
4259 | | |
4260 | | status = ads_find_machine_acct(ads, (void **)(void *)&res, machine); |
4261 | | if (!ADS_ERR_OK(status)) { |
4262 | | DEBUG(0, ("ads_join_realm: Host account test failed for machine %s\n", machine)); |
4263 | | SAFE_FREE(machine); |
4264 | | return status; |
4265 | | } |
4266 | | |
4267 | | SAFE_FREE(machine); |
4268 | | ads_msgfree(ads, res); |
4269 | | |
4270 | | return status; |
4271 | | } |
4272 | | #endif |
4273 | | |
4274 | | /** |
4275 | | * Delete a machine from the realm |
4276 | | * @param ads connection to ads server |
4277 | | * @param hostname Machine to remove |
4278 | | * @return status of delete |
4279 | | **/ |
4280 | | ADS_STATUS ads_leave_realm(ADS_STRUCT *ads, const char *hostname) |
4281 | 0 | { |
4282 | 0 | ADS_STATUS status; |
4283 | 0 | void *msg; |
4284 | 0 | LDAPMessage *res; |
4285 | 0 | char *hostnameDN, *host; |
4286 | 0 | int rc; |
4287 | 0 | LDAPControl ldap_control; |
4288 | 0 | LDAPControl * pldap_control[2] = {NULL, NULL}; |
4289 | |
|
4290 | 0 | pldap_control[0] = &ldap_control; |
4291 | 0 | memset(&ldap_control, 0, sizeof(LDAPControl)); |
4292 | 0 | ldap_control.ldctl_oid = discard_const_p(char, LDAP_SERVER_TREE_DELETE_OID); |
4293 | | |
4294 | | /* hostname must be lowercase */ |
4295 | 0 | host = SMB_STRDUP(hostname); |
4296 | 0 | if (!strlower_m(host)) { |
4297 | 0 | SAFE_FREE(host); |
4298 | 0 | return ADS_ERROR_SYSTEM(EINVAL); |
4299 | 0 | } |
4300 | | |
4301 | 0 | status = ads_find_machine_acct(ads, &res, host); |
4302 | 0 | if (!ADS_ERR_OK(status)) { |
4303 | 0 | DEBUG(0, ("Host account for %s does not exist.\n", host)); |
4304 | 0 | SAFE_FREE(host); |
4305 | 0 | return status; |
4306 | 0 | } |
4307 | | |
4308 | 0 | msg = ads_first_entry(ads, res); |
4309 | 0 | if (!msg) { |
4310 | 0 | SAFE_FREE(host); |
4311 | 0 | return ADS_ERROR_SYSTEM(ENOENT); |
4312 | 0 | } |
4313 | | |
4314 | 0 | hostnameDN = ads_get_dn(ads, talloc_tos(), (LDAPMessage *)msg); |
4315 | 0 | if (hostnameDN == NULL) { |
4316 | 0 | SAFE_FREE(host); |
4317 | 0 | return ADS_ERROR_SYSTEM(ENOENT); |
4318 | 0 | } |
4319 | | |
4320 | 0 | rc = ldap_delete_ext_s(ads->ldap.ld, hostnameDN, pldap_control, NULL); |
4321 | 0 | if (rc) { |
4322 | 0 | DEBUG(3,("ldap_delete_ext_s failed with error code %d\n", rc)); |
4323 | 0 | }else { |
4324 | 0 | DEBUG(3,("ldap_delete_ext_s succeeded with error code %d\n", rc)); |
4325 | 0 | } |
4326 | |
|
4327 | 0 | if (rc != LDAP_SUCCESS) { |
4328 | 0 | const char *attrs[] = { "cn", NULL }; |
4329 | 0 | LDAPMessage *msg_sub; |
4330 | | |
4331 | | /* we only search with scope ONE, we do not expect any further |
4332 | | * objects to be created deeper */ |
4333 | |
|
4334 | 0 | status = ads_do_search_retry(ads, hostnameDN, |
4335 | 0 | LDAP_SCOPE_ONELEVEL, |
4336 | 0 | "(objectclass=*)", attrs, &res); |
4337 | |
|
4338 | 0 | if (!ADS_ERR_OK(status)) { |
4339 | 0 | SAFE_FREE(host); |
4340 | 0 | TALLOC_FREE(hostnameDN); |
4341 | 0 | return status; |
4342 | 0 | } |
4343 | | |
4344 | 0 | for (msg_sub = ads_first_entry(ads, res); msg_sub; |
4345 | 0 | msg_sub = ads_next_entry(ads, msg_sub)) { |
4346 | |
|
4347 | 0 | char *dn = NULL; |
4348 | |
|
4349 | 0 | if ((dn = ads_get_dn(ads, talloc_tos(), msg_sub)) == NULL) { |
4350 | 0 | SAFE_FREE(host); |
4351 | 0 | TALLOC_FREE(hostnameDN); |
4352 | 0 | return ADS_ERROR(LDAP_NO_MEMORY); |
4353 | 0 | } |
4354 | | |
4355 | 0 | status = ads_del_dn(ads, dn); |
4356 | 0 | if (!ADS_ERR_OK(status)) { |
4357 | 0 | DEBUG(3,("failed to delete dn %s: %s\n", dn, ads_errstr(status))); |
4358 | 0 | SAFE_FREE(host); |
4359 | 0 | TALLOC_FREE(dn); |
4360 | 0 | TALLOC_FREE(hostnameDN); |
4361 | 0 | return status; |
4362 | 0 | } |
4363 | | |
4364 | 0 | TALLOC_FREE(dn); |
4365 | 0 | } |
4366 | | |
4367 | | /* there should be no subordinate objects anymore */ |
4368 | 0 | status = ads_do_search_retry(ads, hostnameDN, |
4369 | 0 | LDAP_SCOPE_ONELEVEL, |
4370 | 0 | "(objectclass=*)", attrs, &res); |
4371 | |
|
4372 | 0 | if (!ADS_ERR_OK(status) || ( (ads_count_replies(ads, res)) > 0 ) ) { |
4373 | 0 | SAFE_FREE(host); |
4374 | 0 | TALLOC_FREE(hostnameDN); |
4375 | 0 | return status; |
4376 | 0 | } |
4377 | | |
4378 | | /* delete hostnameDN now */ |
4379 | 0 | status = ads_del_dn(ads, hostnameDN); |
4380 | 0 | if (!ADS_ERR_OK(status)) { |
4381 | 0 | SAFE_FREE(host); |
4382 | 0 | DEBUG(3,("failed to delete dn %s: %s\n", hostnameDN, ads_errstr(status))); |
4383 | 0 | TALLOC_FREE(hostnameDN); |
4384 | 0 | return status; |
4385 | 0 | } |
4386 | 0 | } |
4387 | | |
4388 | 0 | TALLOC_FREE(hostnameDN); |
4389 | |
|
4390 | 0 | status = ads_find_machine_acct(ads, &res, host); |
4391 | 0 | if ((status.error_type == ENUM_ADS_ERROR_LDAP) && |
4392 | 0 | (status.err.rc != LDAP_NO_SUCH_OBJECT)) { |
4393 | 0 | DEBUG(3, ("Failed to remove host account.\n")); |
4394 | 0 | SAFE_FREE(host); |
4395 | 0 | return status; |
4396 | 0 | } |
4397 | | |
4398 | 0 | SAFE_FREE(host); |
4399 | 0 | return ADS_SUCCESS; |
4400 | 0 | } |
4401 | | |
4402 | | /** |
4403 | | * pull all token-sids from an LDAP dn |
4404 | | * @param ads connection to ads server |
4405 | | * @param mem_ctx TALLOC_CTX for allocating sid array |
4406 | | * @param dn of LDAP object |
4407 | | * @param user_sid pointer to struct dom_sid (objectSid) |
4408 | | * @param primary_group_sid pointer to struct dom_sid (self composed) |
4409 | | * @param sids pointer to sid array to allocate |
4410 | | * @param num_sids counter of SIDs pulled |
4411 | | * @return status of token query |
4412 | | **/ |
4413 | | ADS_STATUS ads_get_tokensids(ADS_STRUCT *ads, |
4414 | | TALLOC_CTX *mem_ctx, |
4415 | | const char *dn, |
4416 | | struct dom_sid *user_sid, |
4417 | | struct dom_sid *primary_group_sid, |
4418 | | struct dom_sid **sids, |
4419 | | size_t *num_sids) |
4420 | 0 | { |
4421 | 0 | ADS_STATUS status; |
4422 | 0 | LDAPMessage *res = NULL; |
4423 | 0 | int count = 0; |
4424 | 0 | size_t tmp_num_sids; |
4425 | 0 | struct dom_sid *tmp_sids; |
4426 | 0 | struct dom_sid tmp_user_sid; |
4427 | 0 | struct dom_sid tmp_primary_group_sid; |
4428 | 0 | uint32_t pgid; |
4429 | 0 | const char *attrs[] = { |
4430 | 0 | "objectSid", |
4431 | 0 | "tokenGroups", |
4432 | 0 | "primaryGroupID", |
4433 | 0 | NULL |
4434 | 0 | }; |
4435 | |
|
4436 | 0 | status = ads_search_retry_dn(ads, &res, dn, attrs); |
4437 | 0 | if (!ADS_ERR_OK(status)) { |
4438 | 0 | return status; |
4439 | 0 | } |
4440 | | |
4441 | 0 | count = ads_count_replies(ads, res); |
4442 | 0 | if (count != 1) { |
4443 | 0 | ads_msgfree(ads, res); |
4444 | 0 | return ADS_ERROR_LDAP(LDAP_NO_SUCH_OBJECT); |
4445 | 0 | } |
4446 | | |
4447 | 0 | if (!ads_pull_sid(ads, res, "objectSid", &tmp_user_sid)) { |
4448 | 0 | ads_msgfree(ads, res); |
4449 | 0 | return ADS_ERROR_LDAP(LDAP_NO_MEMORY); |
4450 | 0 | } |
4451 | | |
4452 | 0 | if (!ads_pull_uint32(ads, res, "primaryGroupID", &pgid)) { |
4453 | 0 | ads_msgfree(ads, res); |
4454 | 0 | return ADS_ERROR_LDAP(LDAP_NO_MEMORY); |
4455 | 0 | } |
4456 | | |
4457 | 0 | { |
4458 | | /* hack to compose the primary group sid without knowing the |
4459 | | * domsid */ |
4460 | |
|
4461 | 0 | struct dom_sid domsid; |
4462 | |
|
4463 | 0 | sid_copy(&domsid, &tmp_user_sid); |
4464 | |
|
4465 | 0 | if (!sid_split_rid(&domsid, NULL)) { |
4466 | 0 | ads_msgfree(ads, res); |
4467 | 0 | return ADS_ERROR_LDAP(LDAP_NO_MEMORY); |
4468 | 0 | } |
4469 | | |
4470 | 0 | if (!sid_compose(&tmp_primary_group_sid, &domsid, pgid)) { |
4471 | 0 | ads_msgfree(ads, res); |
4472 | 0 | return ADS_ERROR_LDAP(LDAP_NO_MEMORY); |
4473 | 0 | } |
4474 | 0 | } |
4475 | | |
4476 | 0 | tmp_num_sids = ads_pull_sids(ads, mem_ctx, res, "tokenGroups", &tmp_sids); |
4477 | |
|
4478 | 0 | if (tmp_num_sids == 0 || !tmp_sids) { |
4479 | 0 | ads_msgfree(ads, res); |
4480 | 0 | return ADS_ERROR_LDAP(LDAP_NO_MEMORY); |
4481 | 0 | } |
4482 | | |
4483 | 0 | if (num_sids) { |
4484 | 0 | *num_sids = tmp_num_sids; |
4485 | 0 | } |
4486 | |
|
4487 | 0 | if (sids) { |
4488 | 0 | *sids = tmp_sids; |
4489 | 0 | } |
4490 | |
|
4491 | 0 | if (user_sid) { |
4492 | 0 | *user_sid = tmp_user_sid; |
4493 | 0 | } |
4494 | |
|
4495 | 0 | if (primary_group_sid) { |
4496 | 0 | *primary_group_sid = tmp_primary_group_sid; |
4497 | 0 | } |
4498 | |
|
4499 | 0 | DEBUG(10,("ads_get_tokensids: returned %d sids\n", (int)tmp_num_sids + 2)); |
4500 | |
|
4501 | 0 | ads_msgfree(ads, res); |
4502 | 0 | return ADS_ERROR_LDAP(LDAP_SUCCESS); |
4503 | 0 | } |
4504 | | |
4505 | | /** |
4506 | | * Find a sAMAccountName in LDAP |
4507 | | * @param ads connection to ads server |
4508 | | * @param mem_ctx TALLOC_CTX for allocating sid array |
4509 | | * @param samaccountname to search |
4510 | | * @param uac_ret uint32_t pointer userAccountControl attribute value |
4511 | | * @param dn_ret pointer to dn |
4512 | | * @return status of token query |
4513 | | **/ |
4514 | | ADS_STATUS ads_find_samaccount(ADS_STRUCT *ads, |
4515 | | TALLOC_CTX *mem_ctx, |
4516 | | const char *samaccountname, |
4517 | | uint32_t *uac_ret, |
4518 | | const char **dn_ret) |
4519 | 0 | { |
4520 | 0 | ADS_STATUS status; |
4521 | 0 | const char *attrs[] = { "userAccountControl", NULL }; |
4522 | 0 | const char *filter; |
4523 | 0 | LDAPMessage *res = NULL; |
4524 | 0 | char *dn = NULL; |
4525 | 0 | uint32_t uac = 0; |
4526 | |
|
4527 | 0 | filter = talloc_asprintf(mem_ctx, "(&(objectclass=user)(sAMAccountName=%s))", |
4528 | 0 | samaccountname); |
4529 | 0 | if (filter == NULL) { |
4530 | 0 | status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY); |
4531 | 0 | goto out; |
4532 | 0 | } |
4533 | | |
4534 | 0 | status = ads_do_search_all(ads, ads->config.bind_path, |
4535 | 0 | LDAP_SCOPE_SUBTREE, |
4536 | 0 | filter, attrs, &res); |
4537 | |
|
4538 | 0 | if (!ADS_ERR_OK(status)) { |
4539 | 0 | goto out; |
4540 | 0 | } |
4541 | | |
4542 | 0 | if (ads_count_replies(ads, res) != 1) { |
4543 | 0 | status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED); |
4544 | 0 | goto out; |
4545 | 0 | } |
4546 | | |
4547 | 0 | dn = ads_get_dn(ads, talloc_tos(), res); |
4548 | 0 | if (dn == NULL) { |
4549 | 0 | status = ADS_ERROR(LDAP_NO_MEMORY); |
4550 | 0 | goto out; |
4551 | 0 | } |
4552 | | |
4553 | 0 | if (!ads_pull_uint32(ads, res, "userAccountControl", &uac)) { |
4554 | 0 | status = ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE); |
4555 | 0 | goto out; |
4556 | 0 | } |
4557 | | |
4558 | 0 | if (uac_ret) { |
4559 | 0 | *uac_ret = uac; |
4560 | 0 | } |
4561 | |
|
4562 | 0 | if (dn_ret) { |
4563 | 0 | *dn_ret = talloc_strdup(mem_ctx, dn); |
4564 | 0 | if (!*dn_ret) { |
4565 | 0 | status = ADS_ERROR(LDAP_NO_MEMORY); |
4566 | 0 | goto out; |
4567 | 0 | } |
4568 | 0 | } |
4569 | 0 | out: |
4570 | 0 | TALLOC_FREE(dn); |
4571 | 0 | ads_msgfree(ads, res); |
4572 | |
|
4573 | 0 | return status; |
4574 | 0 | } |
4575 | | |
4576 | | /** |
4577 | | * find our configuration path |
4578 | | * @param ads connection to ads server |
4579 | | * @param mem_ctx Pointer to talloc context |
4580 | | * @param config_path Pointer to the config path |
4581 | | * @return status of search |
4582 | | **/ |
4583 | | ADS_STATUS ads_config_path(ADS_STRUCT *ads, |
4584 | | TALLOC_CTX *mem_ctx, |
4585 | | char **config_path) |
4586 | 0 | { |
4587 | 0 | ADS_STATUS status; |
4588 | 0 | LDAPMessage *res = NULL; |
4589 | 0 | const char *config_context = NULL; |
4590 | 0 | const char *attrs[] = { "configurationNamingContext", NULL }; |
4591 | |
|
4592 | 0 | status = ads_do_search(ads, "", LDAP_SCOPE_BASE, |
4593 | 0 | "(objectclass=*)", attrs, &res); |
4594 | 0 | if (!ADS_ERR_OK(status)) { |
4595 | 0 | return status; |
4596 | 0 | } |
4597 | | |
4598 | 0 | config_context = ads_pull_string(ads, mem_ctx, res, |
4599 | 0 | "configurationNamingContext"); |
4600 | 0 | ads_msgfree(ads, res); |
4601 | 0 | if (!config_context) { |
4602 | 0 | return ADS_ERROR(LDAP_NO_MEMORY); |
4603 | 0 | } |
4604 | | |
4605 | 0 | if (config_path) { |
4606 | 0 | *config_path = talloc_strdup(mem_ctx, config_context); |
4607 | 0 | if (!*config_path) { |
4608 | 0 | return ADS_ERROR(LDAP_NO_MEMORY); |
4609 | 0 | } |
4610 | 0 | } |
4611 | | |
4612 | 0 | return ADS_ERROR(LDAP_SUCCESS); |
4613 | 0 | } |
4614 | | |
4615 | | /** |
4616 | | * find the displayName of an extended right |
4617 | | * @param ads connection to ads server |
4618 | | * @param config_path The config path |
4619 | | * @param mem_ctx Pointer to talloc context |
4620 | | * @param GUID struct of the rightsGUID |
4621 | | * @return status of search |
4622 | | **/ |
4623 | | const char *ads_get_extended_right_name_by_guid(ADS_STRUCT *ads, |
4624 | | const char *config_path, |
4625 | | TALLOC_CTX *mem_ctx, |
4626 | | const struct GUID *rights_guid) |
4627 | 0 | { |
4628 | 0 | ADS_STATUS rc; |
4629 | 0 | LDAPMessage *res = NULL; |
4630 | 0 | char *expr = NULL; |
4631 | 0 | const char *attrs[] = { "displayName", NULL }; |
4632 | 0 | const char *result = NULL; |
4633 | 0 | const char *path; |
4634 | |
|
4635 | 0 | if (!ads || !mem_ctx || !rights_guid) { |
4636 | 0 | goto done; |
4637 | 0 | } |
4638 | | |
4639 | 0 | expr = talloc_asprintf(mem_ctx, "(rightsGuid=%s)", |
4640 | 0 | GUID_string(mem_ctx, rights_guid)); |
4641 | 0 | if (!expr) { |
4642 | 0 | goto done; |
4643 | 0 | } |
4644 | | |
4645 | 0 | path = talloc_asprintf(mem_ctx, "cn=Extended-Rights,%s", config_path); |
4646 | 0 | if (!path) { |
4647 | 0 | goto done; |
4648 | 0 | } |
4649 | | |
4650 | 0 | rc = ads_do_search_retry(ads, path, LDAP_SCOPE_SUBTREE, |
4651 | 0 | expr, attrs, &res); |
4652 | 0 | if (!ADS_ERR_OK(rc)) { |
4653 | 0 | goto done; |
4654 | 0 | } |
4655 | | |
4656 | 0 | if (ads_count_replies(ads, res) != 1) { |
4657 | 0 | goto done; |
4658 | 0 | } |
4659 | | |
4660 | 0 | result = ads_pull_string(ads, mem_ctx, res, "displayName"); |
4661 | |
|
4662 | 0 | done: |
4663 | 0 | ads_msgfree(ads, res); |
4664 | 0 | return result; |
4665 | 0 | } |
4666 | | |
4667 | | /** |
4668 | | * verify or build and verify an account ou |
4669 | | * @param mem_ctx Pointer to talloc context |
4670 | | * @param ads connection to ads server |
4671 | | * @param account_ou |
4672 | | * @return status of search |
4673 | | **/ |
4674 | | |
4675 | | ADS_STATUS ads_check_ou_dn(TALLOC_CTX *mem_ctx, |
4676 | | ADS_STRUCT *ads, |
4677 | | const char **account_ou) |
4678 | 0 | { |
4679 | 0 | char **exploded_dn; |
4680 | 0 | const char *name; |
4681 | 0 | char *ou_string; |
4682 | |
|
4683 | 0 | if (account_ou == NULL) { |
4684 | 0 | return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER); |
4685 | 0 | } |
4686 | | |
4687 | 0 | if (*account_ou != NULL) { |
4688 | 0 | exploded_dn = ldap_explode_dn(*account_ou, 0); |
4689 | 0 | if (exploded_dn) { |
4690 | 0 | ldap_value_free(exploded_dn); |
4691 | 0 | return ADS_SUCCESS; |
4692 | 0 | } |
4693 | 0 | } |
4694 | | |
4695 | 0 | ou_string = ads_ou_string(ads, *account_ou); |
4696 | 0 | if (!ou_string) { |
4697 | 0 | return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX); |
4698 | 0 | } |
4699 | | |
4700 | 0 | name = talloc_asprintf(mem_ctx, "%s,%s", ou_string, |
4701 | 0 | ads->config.bind_path); |
4702 | 0 | SAFE_FREE(ou_string); |
4703 | |
|
4704 | 0 | if (!name) { |
4705 | 0 | return ADS_ERROR_LDAP(LDAP_NO_MEMORY); |
4706 | 0 | } |
4707 | | |
4708 | 0 | exploded_dn = ldap_explode_dn(name, 0); |
4709 | 0 | if (!exploded_dn) { |
4710 | 0 | return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX); |
4711 | 0 | } |
4712 | 0 | ldap_value_free(exploded_dn); |
4713 | |
|
4714 | 0 | *account_ou = name; |
4715 | 0 | return ADS_SUCCESS; |
4716 | 0 | } |
4717 | | |
4718 | | #endif |