source: branches/samba-3.3.x/source/libads/ldap.c@ 715

Last change on this file since 715 was 491, checked in by Herwig Bauernfeind, 15 years ago

Update Samba 3.3 to 3.3.14 (security update)

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