r23779: Change from v2 or later to v3 or later.
[metze/samba/wip.git] / source3 / libads / cldap.c
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 3 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 */
47 static 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 */
93 static 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
172 static SIG_ATOMIC_T gotalarm;
173                                                                                                                    
174 /***************************************************************
175  Signal function to tell us we timed out.
176 ****************************************************************/
177                                                                                                                    
178 static void gotalarm_sig(void)
179 {
180         gotalarm = 1;
181 }
182                                                                                                                    
183 /*
184   receive a cldap netlogon reply
185 */
186 static 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;
191         DATA_BLOB os1 = data_blob_null;
192         DATA_BLOB os2 = data_blob_null;
193         DATA_BLOB os3 = data_blob_null;
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
293 BOOL 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 }