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