Minor comment updates ...
[Samba/gebeck_regimport.git] / source3 / utils / net_ads_cldap.c
blob1903172cf75d8ac8790f83e3d04cddf96c473cde
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)
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.
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.
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.
22 #include "includes.h"
23 #include "../utils/net.h"
25 #ifdef HAVE_ADS
27 #define MAX_DNS_LABEL 255 + 1
29 struct cldap_netlogon_reply {
30 uint32 type;
31 uint32 flags;
32 UUID_FLAT guid;
34 char forest[MAX_DNS_LABEL];
35 char domain[MAX_DNS_LABEL];
36 char hostname[MAX_DNS_LABEL];
38 char netbios_domain[MAX_DNS_LABEL];
39 char netbios_hostname[MAX_DNS_LABEL];
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];
46 uint32 version;
47 uint16 lmnt_token;
48 uint16 lm20_token;
52 These seem to be strings as described in RFC1035 4.1.4 and can be:
54 - a sequence of labels ending in a zero octet
55 - a pointer
56 - a sequence of labels ending with a pointer
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.
63 A pointer consists of a 14 bit offset from the beginning of the data.
65 struct ptr {
66 unsigned ident:2; // must be 11
67 unsigned offset:14; // from the beginning of data
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.
74 static unsigned pull_netlogon_string(char *ret, const char *ptr,
75 const char *data)
77 char *pret = ret;
78 int followed_ptr = 0;
79 unsigned ret_len = 0;
81 memset(pret, 0, MAX_DNS_LABEL);
82 do {
83 if ((*ptr & 0xc0) == 0xc0) {
84 uint16 len;
86 if (!followed_ptr) {
87 ret_len += 2;
88 followed_ptr = 1;
90 len = ((ptr[0] & 0x3f) << 8) | ptr[1];
91 ptr = data + len;
92 } else if (*ptr) {
93 uint8 len = (uint8)*(ptr++);
95 if ((pret - ret + len + 1) >= MAX_DNS_LABEL) {
96 d_printf("DC returning too long DNS name\n");
97 return 0;
100 if (pret != ret) {
101 *pret = '.';
102 pret++;
104 memcpy(pret, ptr, len);
105 pret += len;
106 ptr += len;
108 if (!followed_ptr) {
109 ret_len += (len + 1);
112 } while (*ptr);
114 return followed_ptr ? ret_len : ret_len + 1;
118 do a cldap netlogon query
120 static int send_cldap_netlogon(int sock, const char *domain,
121 const char *hostname, unsigned ntversion)
123 ASN1_DATA data;
124 char ntver[4];
125 #ifdef CLDAP_USER_QUERY
126 char aac[4];
128 SIVAL(aac, 0, 0x00000180);
129 #endif
130 SIVAL(ntver, 0, ntversion);
132 memset(&data, 0, sizeof(data));
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));
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);
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);
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);
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
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);
172 asn1_pop_tag(&data);
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);
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;
186 if (write(sock, data.data, data.length) != (ssize_t)data.length) {
187 d_printf("failed to send cldap query (%s)\n", strerror(errno));
190 asn1_free(&data);
192 return 0;
197 receive a cldap netlogon reply
199 static int recv_cldap_netlogon(int sock, struct cldap_netlogon_reply *reply)
201 int ret;
202 ASN1_DATA data;
203 DATA_BLOB blob;
204 DATA_BLOB os1, os2, os3;
205 uint32 i1;
206 char *p;
208 blob = data_blob(NULL, 8192);
210 ret = read(sock, blob.data, blob.length);
212 if (ret <= 0) {
213 d_printf("no reply received to cldap netlogon\n");
214 return -1;
216 blob.length = ret;
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);
234 if (data.has_error) {
235 d_printf("Failed to parse cldap reply\n");
236 return -1;
239 p = (char *)os3.data;
241 reply->type = IVAL(p, 0); p += 4;
242 reply->flags = IVAL(p, 0); p += 4;
244 memcpy(&reply->guid.info, p, UUID_FLAT_SIZE);
245 p += UUID_FLAT_SIZE;
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);
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;
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);
263 reply->version = IVAL(p, 0);
264 reply->lmnt_token = SVAL(p, 4);
265 reply->lm20_token = SVAL(p, 6);
267 data_blob_free(&os1);
268 data_blob_free(&os2);
269 data_blob_free(&os3);
270 data_blob_free(&blob);
272 return 0;
276 do a cldap netlogon query
278 int ads_cldap_netlogon(ADS_STRUCT *ads)
280 int sock;
281 int ret;
282 struct cldap_netlogon_reply reply;
284 sock = open_udp_socket(inet_ntoa(ads->ldap_ip), ads->ldap_port);
285 if (sock == -1) {
286 d_printf("Failed to open udp socket to %s:%u\n",
287 inet_ntoa(ads->ldap_ip),
288 ads->ldap_port);
289 return -1;
293 ret = send_cldap_netlogon(sock, ads->config.realm, global_myname(), 6);
294 if (ret != 0) {
295 return ret;
297 ret = recv_cldap_netlogon(sock, &reply);
298 close(sock);
300 if (ret == -1) {
301 return -1;
304 d_printf("Information for Domain Controller: %s\n\n",
305 ads->config.ldap_server_name);
307 d_printf("Response Type: ");
308 switch (reply.type) {
309 case SAMLOGON_AD_UNK_R:
310 d_printf("SAMLOGON\n");
311 break;
312 case SAMLOGON_AD_R:
313 d_printf("SAMLOGON_USER\n");
314 break;
315 default:
316 d_printf("0x%x\n", reply.type);
317 break;
319 d_printf("GUID: %s\n",
320 smb_uuid_string_static(smb_uuid_unpack_static(reply.guid)));
321 d_printf("Flags:\n"
322 "\tIs a PDC: %s\n"
323 "\tIs a GC of the forest: %s\n"
324 "\tIs an LDAP server: %s\n"
325 "\tSupports DS: %s\n"
326 "\tIs running a KDC: %s\n"
327 "\tIs running time services: %s\n"
328 "\tIs the closest DC: %s\n"
329 "\tIs writable: %s\n"
330 "\tHas a hardware clock: %s\n"
331 "\tIs a non-domain NC serviced by LDAP server: %s\n",
332 (reply.flags & ADS_PDC) ? "yes" : "no",
333 (reply.flags & ADS_GC) ? "yes" : "no",
334 (reply.flags & ADS_LDAP) ? "yes" : "no",
335 (reply.flags & ADS_DS) ? "yes" : "no",
336 (reply.flags & ADS_KDC) ? "yes" : "no",
337 (reply.flags & ADS_TIMESERV) ? "yes" : "no",
338 (reply.flags & ADS_CLOSEST) ? "yes" : "no",
339 (reply.flags & ADS_WRITABLE) ? "yes" : "no",
340 (reply.flags & ADS_GOOD_TIMESERV) ? "yes" : "no",
341 (reply.flags & ADS_NDNC) ? "yes" : "no");
343 printf("Forest:\t\t\t%s\n", reply.forest);
344 printf("Domain:\t\t\t%s\n", reply.domain);
345 printf("Domain Controller:\t%s\n", reply.hostname);
347 printf("Pre-Win2k Domain:\t%s\n", reply.netbios_domain);
348 printf("Pre-Win2k Hostname:\t%s\n", reply.netbios_hostname);
350 if (*reply.unk) printf("Unk:\t\t\t%s\n", reply.unk);
351 if (*reply.user_name) printf("User name:\t%s\n", reply.user_name);
353 printf("Site Name:\t\t%s\n", reply.site_name);
354 printf("Site Name (2):\t\t%s\n", reply.site_name_2);
356 d_printf("NT Version: %d\n", reply.version);
357 d_printf("LMNT Token: %.2x\n", reply.lmnt_token);
358 d_printf("LM20 Token: %.2x\n", reply.lm20_token);
360 return ret;
364 #endif