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