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#include "utils/net.h"
24
25#ifdef HAVE_ADS
26
27#define MAX_DNS_LABEL 255 + 1
28
29struct cldap_netlogon_reply {
30	uint32 type;
31	uint32 flags;
32	UUID_FLAT guid;
33
34	char forest[MAX_DNS_LABEL];
35	char domain[MAX_DNS_LABEL];
36	char hostname[MAX_DNS_LABEL];
37
38	char netbios_domain[MAX_DNS_LABEL];
39	char netbios_hostname[MAX_DNS_LABEL];
40
41	char unk[MAX_DNS_LABEL];
42	char user_name[MAX_DNS_LABEL];
43	char site_name[MAX_DNS_LABEL];
44	char site_name_2[MAX_DNS_LABEL];
45
46	uint32 version;
47	uint16 lmnt_token;
48	uint16 lm20_token;
49};
50
51/*
52  These seem to be strings as described in RFC1035 4.1.4 and can be:
53
54   - a sequence of labels ending in a zero octet
55   - a pointer
56   - a sequence of labels ending with a pointer
57
58  A label is a byte where the first two bits must be zero and the remaining
59  bits represent the length of the label followed by the label itself.
60  Therefore, the length of a label is at max 64 bytes.  Under RFC1035, a
61  sequence of labels cannot exceed 255 bytes.
62
63  A pointer consists of a 14 bit offset from the beginning of the data.
64
65  struct ptr {
66    unsigned ident:2; // must be 11
67    unsigned offset:14; // from the beginning of data
68  };
69
70  This is used as a method to compress the packet by eliminated duplicate
71  domain components.  Since a UDP packet should probably be < 512 bytes and a
72  DNS name can be up to 255 bytes, this actually makes a lot of sense.
73*/
74static unsigned pull_netlogon_string(char *ret, const char *ptr,
75				     const char *data)
76{
77	char *pret = ret;
78	int followed_ptr = 0;
79	unsigned ret_len = 0;
80
81	memset(pret, 0, MAX_DNS_LABEL);
82	do {
83		if ((*ptr & 0xc0) == 0xc0) {
84			uint16 len;
85
86			if (!followed_ptr) {
87				ret_len += 2;
88				followed_ptr = 1;
89			}
90			len = ((ptr[0] & 0x3f) << 8) | ptr[1];
91			ptr = data + len;
92		} else if (*ptr) {
93			uint8 len = (uint8)*(ptr++);
94
95			if ((pret - ret + len + 1) >= MAX_DNS_LABEL) {
96				d_printf("DC returning too long DNS name\n");
97				return 0;
98			}
99
100			if (pret != ret) {
101				*pret = '.';
102				pret++;
103			}
104			memcpy(pret, ptr, len);
105			pret += len;
106			ptr += len;
107
108			if (!followed_ptr) {
109				ret_len += (len + 1);
110			}
111		}
112	} while (*ptr);
113
114	return followed_ptr ? ret_len : ret_len + 1;
115}
116
117/*
118  do a cldap netlogon query
119*/
120static int send_cldap_netlogon(int sock, const char *domain,
121			       const char *hostname, unsigned ntversion)
122{
123	ASN1_DATA data;
124	char ntver[4];
125#ifdef CLDAP_USER_QUERY
126	char aac[4];
127
128	SIVAL(aac, 0, 0x00000180);
129#endif
130	SIVAL(ntver, 0, ntversion);
131
132	memset(&data, 0, sizeof(data));
133
134	asn1_push_tag(&data,ASN1_SEQUENCE(0));
135	asn1_write_Integer(&data, 4);
136	asn1_push_tag(&data, ASN1_APPLICATION(3));
137	asn1_write_OctetString(&data, NULL, 0);
138	asn1_write_enumerated(&data, 0);
139	asn1_write_enumerated(&data, 0);
140	asn1_write_Integer(&data, 0);
141	asn1_write_Integer(&data, 0);
142	asn1_write_BOOLEAN2(&data, False);
143	asn1_push_tag(&data, ASN1_CONTEXT(0));
144
145	asn1_push_tag(&data, ASN1_CONTEXT(3));
146	asn1_write_OctetString(&data, "DnsDomain", 9);
147	asn1_write_OctetString(&data, domain, strlen(domain));
148	asn1_pop_tag(&data);
149
150	asn1_push_tag(&data, ASN1_CONTEXT(3));
151	asn1_write_OctetString(&data, "Host", 4);
152	asn1_write_OctetString(&data, hostname, strlen(hostname));
153	asn1_pop_tag(&data);
154
155#ifdef CLDAP_USER_QUERY
156	asn1_push_tag(&data, ASN1_CONTEXT(3));
157	asn1_write_OctetString(&data, "User", 4);
158	asn1_write_OctetString(&data, "SAMBA$", 6);
159	asn1_pop_tag(&data);
160
161	asn1_push_tag(&data, ASN1_CONTEXT(3));
162	asn1_write_OctetString(&data, "AAC", 4);
163	asn1_write_OctetString(&data, aac, 4);
164	asn1_pop_tag(&data);
165#endif
166
167	asn1_push_tag(&data, ASN1_CONTEXT(3));
168	asn1_write_OctetString(&data, "NtVer", 5);
169	asn1_write_OctetString(&data, ntver, 4);
170	asn1_pop_tag(&data);
171
172	asn1_pop_tag(&data);
173
174	asn1_push_tag(&data,ASN1_SEQUENCE(0));
175	asn1_write_OctetString(&data, "NetLogon", 8);
176	asn1_pop_tag(&data);
177	asn1_pop_tag(&data);
178	asn1_pop_tag(&data);
179
180	if (data.has_error) {
181		d_printf("Failed to build cldap netlogon at offset %d\n", (int)data.ofs);
182		asn1_free(&data);
183		return -1;
184	}
185
186	if (write(sock, data.data, data.length) != (ssize_t)data.length) {
187		d_printf("failed to send cldap query (%s)\n", strerror(errno));
188	}
189
190	asn1_free(&data);
191
192	return 0;
193}
194
195
196/*
197  receive a cldap netlogon reply
198*/
199static int recv_cldap_netlogon(int sock, struct cldap_netlogon_reply *reply)
200{
201	int ret;
202	ASN1_DATA data;
203	DATA_BLOB blob;
204	DATA_BLOB os1, os2, os3;
205	uint32 i1;
206	char *p;
207
208	blob = data_blob(NULL, 8192);
209
210	ret = read(sock, blob.data, blob.length);
211
212	if (ret <= 0) {
213		d_printf("no reply received to cldap netlogon\n");
214		return -1;
215	}
216	blob.length = ret;
217
218	asn1_load(&data, blob);
219	asn1_start_tag(&data, ASN1_SEQUENCE(0));
220	asn1_read_Integer(&data, &i1);
221	asn1_start_tag(&data, ASN1_APPLICATION(4));
222	asn1_read_OctetString(&data, &os1);
223	asn1_start_tag(&data, ASN1_SEQUENCE(0));
224	asn1_start_tag(&data, ASN1_SEQUENCE(0));
225	asn1_read_OctetString(&data, &os2);
226	asn1_start_tag(&data, ASN1_SET);
227	asn1_read_OctetString(&data, &os3);
228	asn1_end_tag(&data);
229	asn1_end_tag(&data);
230	asn1_end_tag(&data);
231	asn1_end_tag(&data);
232	asn1_end_tag(&data);
233
234	if (data.has_error) {
235		d_printf("Failed to parse cldap reply\n");
236		return -1;
237	}
238
239	p = (char *)os3.data;
240
241	reply->type = IVAL(p, 0); p += 4;
242	reply->flags = IVAL(p, 0); p += 4;
243
244	memcpy(&reply->guid.info, p, UUID_FLAT_SIZE);
245	p += UUID_FLAT_SIZE;
246
247	p += pull_netlogon_string(reply->forest, p, (const char *)os3.data);
248	p += pull_netlogon_string(reply->domain, p, (const char *)os3.data);
249	p += pull_netlogon_string(reply->hostname, p, (const char *)os3.data);
250	p += pull_netlogon_string(reply->netbios_domain, p, (const char *)os3.data);
251	p += pull_netlogon_string(reply->netbios_hostname, p, (const char *)os3.data);
252	p += pull_netlogon_string(reply->unk, p, (const char *)os3.data);
253
254	if (reply->type == SAMLOGON_AD_R) {
255		p += pull_netlogon_string(reply->user_name, p, (const char *)os3.data);
256	} else {
257		*reply->user_name = 0;
258	}
259
260	p += pull_netlogon_string(reply->site_name, p, (const char *)os3.data);
261	p += pull_netlogon_string(reply->site_name_2, p, (const char *)os3.data);
262
263	reply->version = IVAL(p, 0);
264	reply->lmnt_token = SVAL(p, 4);
265	reply->lm20_token = SVAL(p, 6);
266
267	data_blob_free(&os1);
268	data_blob_free(&os2);
269	data_blob_free(&os3);
270	data_blob_free(&blob);
271
272	return 0;
273}
274
275/*
276  do a cldap netlogon query
277*/
278int ads_cldap_netlogon(ADS_STRUCT *ads)
279{
280	int sock;
281	int ret;
282	struct cldap_netlogon_reply reply;
283	const char *target = opt_host ? opt_host : inet_ntoa(ads->ldap_ip);
284
285	sock = open_udp_socket(target, ads->ldap_port);
286	if (sock == -1) {
287		d_printf("Failed to open udp socket to %s:%u\n",
288			 inet_ntoa(ads->ldap_ip),
289			 ads->ldap_port);
290		return -1;
291
292	}
293
294	ret = send_cldap_netlogon(sock, ads->config.realm, global_myname(), 6);
295	if (ret != 0) {
296		return ret;
297	}
298	ret = recv_cldap_netlogon(sock, &reply);
299	close(sock);
300
301	if (ret == -1) {
302		return -1;
303	}
304
305	d_printf("Information for Domain Controller: %s\n\n",
306		 ads->config.ldap_server_name);
307
308	d_printf("Response Type: ");
309	switch (reply.type) {
310	case SAMLOGON_AD_UNK_R:
311		d_printf("SAMLOGON\n");
312		break;
313	case SAMLOGON_AD_R:
314		d_printf("SAMLOGON_USER\n");
315		break;
316	default:
317		d_printf("0x%x\n", reply.type);
318		break;
319	}
320	d_printf("GUID: %s\n",
321		 smb_uuid_string_static(smb_uuid_unpack_static(reply.guid)));
322	d_printf("Flags:\n"
323		 "\tIs a PDC:                                   %s\n"
324		 "\tIs a GC of the forest:                      %s\n"
325		 "\tIs an LDAP server:                          %s\n"
326		 "\tSupports DS:                                %s\n"
327		 "\tIs running a KDC:                           %s\n"
328		 "\tIs running time services:                   %s\n"
329		 "\tIs the closest DC:                          %s\n"
330		 "\tIs writable:                                %s\n"
331		 "\tHas a hardware clock:                       %s\n"
332		 "\tIs a non-domain NC serviced by LDAP server: %s\n",
333		 (reply.flags & ADS_PDC) ? "yes" : "no",
334		 (reply.flags & ADS_GC) ? "yes" : "no",
335		 (reply.flags & ADS_LDAP) ? "yes" : "no",
336		 (reply.flags & ADS_DS) ? "yes" : "no",
337		 (reply.flags & ADS_KDC) ? "yes" : "no",
338		 (reply.flags & ADS_TIMESERV) ? "yes" : "no",
339		 (reply.flags & ADS_CLOSEST) ? "yes" : "no",
340		 (reply.flags & ADS_WRITABLE) ? "yes" : "no",
341		 (reply.flags & ADS_GOOD_TIMESERV) ? "yes" : "no",
342		 (reply.flags & ADS_NDNC) ? "yes" : "no");
343
344	printf("Forest:\t\t\t%s\n", reply.forest);
345	printf("Domain:\t\t\t%s\n", reply.domain);
346	printf("Domain Controller:\t%s\n", reply.hostname);
347
348	printf("Pre-Win2k Domain:\t%s\n", reply.netbios_domain);
349	printf("Pre-Win2k Hostname:\t%s\n", reply.netbios_hostname);
350
351	if (*reply.unk) printf("Unk:\t\t\t%s\n", reply.unk);
352	if (*reply.user_name) printf("User name:\t%s\n", reply.user_name);
353
354	printf("Site Name:\t\t%s\n", reply.site_name);
355	printf("Site Name (2):\t\t%s\n", reply.site_name_2);
356
357	d_printf("NT Version: %d\n", reply.version);
358	d_printf("LMNT Token: %.2x\n", reply.lmnt_token);
359	d_printf("LM20 Token: %.2x\n", reply.lm20_token);
360
361	return ret;
362}
363
364
365#endif
366