1/*
2   Samba Unix/Linux SMB client library
3   net ads cldap functions
4   Copyright (C) 2001 Andrew Tridgell (tridge@samba.org)
5   Copyright (C) 2003 Jim McDonough (jmcd@us.ibm.com)
6
7   This program is free software; you can redistribute it and/or modify
8   it under the terms of the GNU General Public License as published by
9   the Free Software Foundation; either version 2 of the License, or
10   (at your option) any later version.
11
12   This program is distributed in the hope that it will be useful,
13   but WITHOUT ANY WARRANTY; without even the implied warranty of
14   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15   GNU General Public License for more details.
16
17   You should have received a copy of the GNU General Public License
18   along with this program; if not, write to the Free Software
19   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20*/
21
22#include "includes.h"
23
24/*
25  These seem to be strings as described in RFC1035 4.1.4 and can be:
26
27   - a sequence of labels ending in a zero octet
28   - a pointer
29   - a sequence of labels ending with a pointer
30
31  A label is a byte where the first two bits must be zero and the remaining
32  bits represent the length of the label followed by the label itself.
33  Therefore, the length of a label is at max 64 bytes.  Under RFC1035, a
34  sequence of labels cannot exceed 255 bytes.
35
36  A pointer consists of a 14 bit offset from the beginning of the data.
37
38  struct ptr {
39    unsigned ident:2; // must be 11
40    unsigned offset:14; // from the beginning of data
41  };
42
43  This is used as a method to compress the packet by eliminated duplicate
44  domain components.  Since a UDP packet should probably be < 512 bytes and a
45  DNS name can be up to 255 bytes, this actually makes a lot of sense.
46*/
47static unsigned pull_netlogon_string(char *ret, const char *ptr,
48				     const char *data)
49{
50	char *pret = ret;
51	int followed_ptr = 0;
52	unsigned ret_len = 0;
53
54	memset(pret, 0, MAX_DNS_LABEL);
55	do {
56		if ((*ptr & 0xc0) == 0xc0) {
57			uint16 len;
58
59			if (!followed_ptr) {
60				ret_len += 2;
61				followed_ptr = 1;
62			}
63			len = ((ptr[0] & 0x3f) << 8) | ptr[1];
64			ptr = data + len;
65		} else if (*ptr) {
66			uint8 len = (uint8)*(ptr++);
67
68			if ((pret - ret + len + 1) >= MAX_DNS_LABEL) {
69				DEBUG(1,("DC returning too long DNS name\n"));
70				return 0;
71			}
72
73			if (pret != ret) {
74				*pret = '.';
75				pret++;
76			}
77			memcpy(pret, ptr, len);
78			pret += len;
79			ptr += len;
80
81			if (!followed_ptr) {
82				ret_len += (len + 1);
83			}
84		}
85	} while (*ptr);
86
87	return followed_ptr ? ret_len : ret_len + 1;
88}
89
90/*
91  do a cldap netlogon query
92*/
93static int send_cldap_netlogon(int sock, const char *domain,
94			       const char *hostname, unsigned ntversion)
95{
96	ASN1_DATA data;
97	char ntver[4];
98#ifdef CLDAP_USER_QUERY
99	char aac[4];
100
101	SIVAL(aac, 0, 0x00000180);
102#endif
103	SIVAL(ntver, 0, ntversion);
104
105	memset(&data, 0, sizeof(data));
106
107	asn1_push_tag(&data,ASN1_SEQUENCE(0));
108	asn1_write_Integer(&data, 4);
109	asn1_push_tag(&data, ASN1_APPLICATION(3));
110	asn1_write_OctetString(&data, NULL, 0);
111	asn1_write_enumerated(&data, 0);
112	asn1_write_enumerated(&data, 0);
113	asn1_write_Integer(&data, 0);
114	asn1_write_Integer(&data, 0);
115	asn1_write_BOOLEAN2(&data, False);
116	asn1_push_tag(&data, ASN1_CONTEXT(0));
117
118	if (domain) {
119		asn1_push_tag(&data, ASN1_CONTEXT(3));
120		asn1_write_OctetString(&data, "DnsDomain", 9);
121		asn1_write_OctetString(&data, domain, strlen(domain));
122		asn1_pop_tag(&data);
123	}
124
125	asn1_push_tag(&data, ASN1_CONTEXT(3));
126	asn1_write_OctetString(&data, "Host", 4);
127	asn1_write_OctetString(&data, hostname, strlen(hostname));
128	asn1_pop_tag(&data);
129
130#ifdef CLDAP_USER_QUERY
131	asn1_push_tag(&data, ASN1_CONTEXT(3));
132	asn1_write_OctetString(&data, "User", 4);
133	asn1_write_OctetString(&data, "SAMBA$", 6);
134	asn1_pop_tag(&data);
135
136	asn1_push_tag(&data, ASN1_CONTEXT(3));
137	asn1_write_OctetString(&data, "AAC", 4);
138	asn1_write_OctetString(&data, aac, 4);
139	asn1_pop_tag(&data);
140#endif
141
142	asn1_push_tag(&data, ASN1_CONTEXT(3));
143	asn1_write_OctetString(&data, "NtVer", 5);
144	asn1_write_OctetString(&data, ntver, 4);
145	asn1_pop_tag(&data);
146
147	asn1_pop_tag(&data);
148
149	asn1_push_tag(&data,ASN1_SEQUENCE(0));
150	asn1_write_OctetString(&data, "NetLogon", 8);
151	asn1_pop_tag(&data);
152	asn1_pop_tag(&data);
153	asn1_pop_tag(&data);
154
155	if (data.has_error) {
156		DEBUG(2,("Failed to build cldap netlogon at offset %d\n", (int)data.ofs));
157		asn1_free(&data);
158		return -1;
159	}
160
161	if (write(sock, data.data, data.length) != (ssize_t)data.length) {
162		DEBUG(2,("failed to send cldap query (%s)\n", strerror(errno)));
163		asn1_free(&data);
164		return -1;
165	}
166
167	asn1_free(&data);
168
169	return 0;
170}
171
172static SIG_ATOMIC_T gotalarm;
173
174/***************************************************************
175 Signal function to tell us we timed out.
176****************************************************************/
177
178static void gotalarm_sig(void)
179{
180	gotalarm = 1;
181}
182
183/*
184  receive a cldap netlogon reply
185*/
186static int recv_cldap_netlogon(int sock, struct cldap_netlogon_reply *reply)
187{
188	int ret;
189	ASN1_DATA data;
190	DATA_BLOB blob = data_blob(NULL, 0);
191	DATA_BLOB os1 = data_blob(NULL, 0);
192	DATA_BLOB os2 = data_blob(NULL, 0);
193	DATA_BLOB os3 = data_blob(NULL, 0);
194	int i1;
195	/* half the time of a regular ldap timeout, not less than 3 seconds. */
196	unsigned int al_secs = MAX(3,lp_ldap_timeout()/2);
197	char *p;
198
199	blob = data_blob(NULL, 8192);
200	if (blob.data == NULL) {
201		DEBUG(1, ("data_blob failed\n"));
202		errno = ENOMEM;
203		return -1;
204	}
205
206	/* Setup timeout */
207	gotalarm = 0;
208	CatchSignal(SIGALRM, SIGNAL_CAST gotalarm_sig);
209	alarm(al_secs);
210	/* End setup timeout. */
211
212	ret = read(sock, blob.data, blob.length);
213
214	/* Teardown timeout. */
215	CatchSignal(SIGALRM, SIGNAL_CAST SIG_IGN);
216	alarm(0);
217
218	if (ret <= 0) {
219		DEBUG(1,("no reply received to cldap netlogon\n"));
220		data_blob_free(&blob);
221		return -1;
222	}
223	blob.length = ret;
224
225	asn1_load(&data, blob);
226	asn1_start_tag(&data, ASN1_SEQUENCE(0));
227	asn1_read_Integer(&data, &i1);
228	asn1_start_tag(&data, ASN1_APPLICATION(4));
229	asn1_read_OctetString(&data, &os1);
230	asn1_start_tag(&data, ASN1_SEQUENCE(0));
231	asn1_start_tag(&data, ASN1_SEQUENCE(0));
232	asn1_read_OctetString(&data, &os2);
233	asn1_start_tag(&data, ASN1_SET);
234	asn1_read_OctetString(&data, &os3);
235	asn1_end_tag(&data);
236	asn1_end_tag(&data);
237	asn1_end_tag(&data);
238	asn1_end_tag(&data);
239	asn1_end_tag(&data);
240
241	if (data.has_error) {
242		data_blob_free(&blob);
243		data_blob_free(&os1);
244		data_blob_free(&os2);
245		data_blob_free(&os3);
246		asn1_free(&data);
247		DEBUG(1,("Failed to parse cldap reply\n"));
248		return -1;
249	}
250
251	p = (char *)os3.data;
252
253	reply->type = IVAL(p, 0); p += 4;
254	reply->flags = IVAL(p, 0); p += 4;
255
256	memcpy(&reply->guid.info, p, UUID_FLAT_SIZE);
257	p += UUID_FLAT_SIZE;
258
259	p += pull_netlogon_string(reply->forest, p, (const char *)os3.data);
260	p += pull_netlogon_string(reply->domain, p, (const char *)os3.data);
261	p += pull_netlogon_string(reply->hostname, p, (const char *)os3.data);
262	p += pull_netlogon_string(reply->netbios_domain, p, (const char *)os3.data);
263	p += pull_netlogon_string(reply->netbios_hostname, p, (const char *)os3.data);
264	p += pull_netlogon_string(reply->unk, p, (const char *)os3.data);
265
266	if (reply->type == SAMLOGON_AD_R) {
267		p += pull_netlogon_string(reply->user_name, p, (const char *)os3.data);
268	} else {
269		*reply->user_name = 0;
270	}
271
272	p += pull_netlogon_string(reply->server_site_name, p, (const char *)os3.data);
273	p += pull_netlogon_string(reply->client_site_name, p, (const char *)os3.data);
274
275	reply->version = IVAL(p, 0);
276	reply->lmnt_token = SVAL(p, 4);
277	reply->lm20_token = SVAL(p, 6);
278
279	data_blob_free(&os1);
280	data_blob_free(&os2);
281	data_blob_free(&os3);
282	data_blob_free(&blob);
283
284	asn1_free(&data);
285
286	return 0;
287}
288
289/*******************************************************************
290  do a cldap netlogon query.  Always 389/udp
291*******************************************************************/
292
293BOOL ads_cldap_netlogon(const char *server, const char *realm,  struct cldap_netlogon_reply *reply)
294{
295	int sock;
296	int ret;
297
298	sock = open_udp_socket(server, LDAP_PORT );
299	if (sock == -1) {
300		DEBUG(2,("ads_cldap_netlogon: Failed to open udp socket to %s\n",
301			 server));
302		return False;
303	}
304
305	ret = send_cldap_netlogon(sock, realm, global_myname(), 6);
306	if (ret != 0) {
307		close(sock);
308		return False;
309	}
310	ret = recv_cldap_netlogon(sock, reply);
311	close(sock);
312
313	if (ret == -1) {
314		return False;
315	}
316
317	return True;
318}
319