Now inbound_cap_ls() can enable extensions when a bouncer uses a namespace for the...
[rofl0r-ixchat.git] / src / common / msproxy.c
blob9c85394de2da0bf3bac81e073bcfb27db4b40d44
1 /* X-Chat
2 * Copyright (C) 1998 Peter Zelezny.
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
18 * MS Proxy (ISA server) support is (c) 2006 Pavel Fedin <sonic_amiga@rambler.ru>
19 * based on Dante source code
20 * Copyright (c) 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006
21 * Inferno Nettverk A/S, Norway. All rights reserved.
24 /*#define DEBUG_MSPROXY*/
26 #include <stdio.h>
27 #include <string.h>
28 #include <stdlib.h>
29 #include <unistd.h>
30 #include <fcntl.h>
32 #define WANTSOCKET
33 #define WANTARPA
34 #include "inet.h"
36 #include "xchat.h"
37 #include "network.h"
38 #include "xchatc.h"
39 #include "server.h"
40 #include "msproxy.h"
43 #ifdef USE_MSPROXY
44 #include <ntlm.h>
46 static int
47 send_msprequest(s, state, request, end)
48 int s;
49 struct msproxy_state_t *state;
50 struct msproxy_request_t *request;
51 char *end;
53 ssize_t w;
54 size_t l;
56 request->magic25 = htonl(MSPROXY_VERSION);
57 request->serverack = state->seq_recv;
58 /* don't start incrementing sequence until we are acking packet #2. */
59 request->sequence = (unsigned char)(request->serverack >= 2 ? state->seq_sent + 1 : 0);
61 memcpy(request->RWSP, "RWSP", sizeof(request->RWSP));
63 l = end - (char *)request;
64 /* all requests must be atleast MSPROXY_MINLENGTH it seems. */
65 if (l < MSPROXY_MINLENGTH) {
66 bzero(end, (size_t)(MSPROXY_MINLENGTH - l));
67 l = MSPROXY_MINLENGTH;
70 if ((w = send(s, request, l, 0)) != l) {
71 #ifdef DEBUG_MSPROXY
72 printf ("send_msprequest(): send() failed (%d bytes sent instead of %d\n", w, l);
73 perror ("Error is");
74 #endif
75 return -1;
77 state->seq_sent = request->sequence;
79 return w;
82 static int
83 recv_mspresponse(s, state, response)
84 int s;
85 struct msproxy_state_t *state;
86 struct msproxy_response_t *response;
88 ssize_t r;
90 do {
91 if ((r = recv (s, response, sizeof (*response), 0)) < MSPROXY_MINLENGTH) {
92 #ifdef DEBUG_MSPROXY
93 printf ("recv_mspresponse(): expected to read atleast %d, read %d\n", MSPROXY_MINLENGTH, r);
94 #endif
95 return -1;
97 if (state->seq_recv == 0)
98 break; /* not started incrementing yet. */
99 #ifdef DEBUG_MSPROXY
100 if (response->sequence == state->seq_recv)
101 printf ("seq_recv: %d, dup response, seqnumber: 0x%x\n", state->seq_recv, response->sequence);
102 #endif
103 } while (response->sequence == state->seq_recv);
105 state->seq_recv = response->sequence;
107 return r;
111 traverse_msproxy (int sok, char *serverAddr, int port, struct msproxy_state_t *state, netstore *ns_proxy, int csok4, int csok6, int *csok, char bound)
113 struct msproxy_request_t req;
114 struct msproxy_response_t res;
115 char *data, *p;
116 char hostname[NT_MAXNAMELEN];
117 char ntdomain[NT_MAXNAMELEN];
118 char challenge[8];
119 netstore *ns_client;
120 int clientport;
121 guint32 destaddr;
122 guint32 flags;
124 if (!prefs.proxy_auth || !prefs.proxy_user[0] || !prefs.proxy_pass[0] )
125 return 1;
127 /* MS proxy protocol implementation currently doesn't support IPv6 */
128 destaddr = net_getsockaddr_v4 (ns_proxy);
129 if (!destaddr)
130 return 1;
132 state->seq_recv = 0;
133 state->seq_sent = 0;
135 #ifdef DEBUG_MSPROXY
136 printf ("Connecting to %s:%d via MS proxy\n", serverAddr, port);
137 #endif
139 gethostname (hostname, NT_MAXNAMELEN);
140 p = strchr (hostname, '.');
141 if (p)
142 *p = '\0';
144 bzero (&req, sizeof(req));
145 req.clientid = htonl(0x0a000000); /* Initial client ID is always 0x0a */
146 req.command = htons(MSPROXY_HELLO); /* HELLO command */
147 req.packet.hello.magic5 = htons(0x4b00); /* Fill in magic values */
148 req.packet.hello.magic10 = htons(0x1400);
149 req.packet.hello.magic15 = htons(0x0400);
150 req.packet.hello.magic20 = htons(0x5704);
151 req.packet.hello.magic25 = htons(0x0004);
152 req.packet.hello.magic30 = htons(0x0100);
153 req.packet.hello.magic35 = htons(0x4a02);
154 req.packet.hello.magic40 = htons(0x3000);
155 req.packet.hello.magic45 = htons(0x4400);
156 req.packet.hello.magic50 = htons(0x3900);
157 data = req.packet.hello.data;
158 strcpy (data, prefs.proxy_user); /* Append a username */
159 data += strlen (prefs.proxy_user)+2; /* +2 automatically creates second empty string */
160 strcpy (data, MSPROXY_EXECUTABLE); /* Append an application name */
161 data += strlen (MSPROXY_EXECUTABLE)+1;
162 strcpy (data, hostname); /* Append a hostname */
163 data += strlen (hostname)+1;
165 if (send_msprequest(sok, state, &req, data) == -1)
166 return 1;
168 if (recv_mspresponse(sok, state, &res) == -1)
169 return 1;
171 if (strcmp(res.RWSP, "RWSP") != 0) {
172 #ifdef DEBUG_MSPROXY
173 printf ("Received mailformed packet (no RWSP signature)\n");
174 #endif
175 return 1;
178 if (ntohs(res.command) >> 8 != 0x10) {
179 #ifdef DEBUG_MSPROXY
180 printf ("expected res.command = 10??, is %x", ntohs(res.command));
181 #endif
182 return 1;
185 state->clientid = htonl(rand());
186 state->serverid = res.serverid;
188 #ifdef DEBUG_MSPROXY
189 printf ("clientid: 0x%x, serverid: 0x%0x\n", state->clientid, state->serverid);
190 printf ("packet #2\n");
191 #endif
193 /* almost identical. */
194 req.clientid = state->clientid;
195 req.serverid = state->serverid;
197 if (send_msprequest(sok, state, &req, data) == -1)
198 return 1;
200 if (recv_mspresponse(sok, state, &res) == -1)
201 return 1;
203 if (res.serverid != state->serverid) {
204 #ifdef DEBUG_MSPROXY
205 printf ("expected serverid = 0x%x, is 0x%x\n",state->serverid, res.serverid);
206 #endif
207 return 1;
210 if (res.sequence != 0x01) {
211 #ifdef DEBUG_MSPROXY
212 printf ("expected res.sequence = 0x01, is 0x%x\n", res.sequence);
213 #endif
214 return 1;
217 if (ntohs(res.command) != MSPROXY_USERINFO_ACK) {
218 #ifdef DEBUG_MSPROXY
219 printf ("expected res.command = 0x%x, is 0x%x\n", MSPROXY_USERINFO_ACK, ntohs(res.command));
220 #endif
221 return 1;
224 #ifdef DEBUG_MSPROXY
225 printf ("packet #3\n");
226 #endif
228 bzero(&req, sizeof(req));
229 req.clientid = state->clientid;
230 req.serverid = state->serverid;
231 req.command = htons(MSPROXY_AUTHENTICATE);
232 memcpy(req.packet.auth.NTLMSSP, "NTLMSSP", sizeof("NTLMSSP"));
233 req.packet.auth.bindaddr = htonl(0x02000000);
234 req.packet.auth.msgtype = htonl(0x01000000);
235 /* NTLM flags: 0x80000000 Negotiate LAN Manager key
236 0x10000000 Negotiate sign
237 0x04000000 Request target
238 0x02000000 Negotiate OEM
239 0x00800000 Always sign
240 0x00020000 Negotiate NTLM
242 req.packet.auth.flags = htonl(0x06020000);
244 if (send_msprequest(sok, state, &req, &req.packet.auth.data) == -1)
245 return 1;
247 if (recv_mspresponse(sok, state, &res) == -1)
248 return 1;
250 if (res.serverid != state->serverid) {
251 #ifdef DEBUG_MSPROXY
252 printf ("expected serverid = 0x%x, is 0x%x\n", state->serverid, res.serverid);
253 #endif
254 return 1;
257 if (ntohs(res.command) != MSPROXY_AUTHENTICATE_ACK) {
258 #ifdef DEBUG_MSPROXY
259 printf ("expected res.command = 0x%x, is 0x%x\n", MSPROXY_AUTHENTICATE_ACK, ntohs(res.command));
260 #endif
261 return 1;
264 flags = res.packet.auth.flags & htonl(0x00020000); /* Remember if the server supports NTLM */
265 memcpy(challenge, &res.packet.auth.challenge, sizeof(challenge));
266 memcpy(ntdomain, &res.packet.auth.NTLMSSP[res.packet.auth.target.offset], res.packet.auth.target.len);
267 ntdomain[res.packet.auth.target.len] = 0;
269 #ifdef DEBUG_MSPROXY
270 printf ("ntdomain: \"%s\"\n", ntdomain);
271 printf ("packet #4\n");
272 #endif
274 bzero(&req, sizeof(req));
275 req.clientid = state->clientid;
276 req.serverid = state->serverid;
277 req.command = htons(MSPROXY_AUTHENTICATE_2); /* Authentication response */
278 req.packet.auth2.magic3 = htons(0x0200); /* Something */
279 memcpy(req.packet.auth2.NTLMSSP, "NTLMSSP", sizeof("NTLMSSP")); /* Start of NTLM message */
280 req.packet.auth2.msgtype = htonl(0x03000000); /* Message type 2 */
281 req.packet.auth2.flags = flags | htonl(0x02000000); /* Choose authentication method */
282 data = req.packet.auth2.data;
283 if (flags) {
284 req.packet.auth2.lm_resp.len = 0; /* We are here if NTLM is supported, */
285 req.packet.auth2.lm_resp.alloc = 0; /* Do not fill in insecure LM response */
286 req.packet.auth2.lm_resp.offset = data - req.packet.auth2.NTLMSSP;
287 req.packet.auth2.ntlm_resp.len = 24; /* Fill in NTLM response security buffer */
288 req.packet.auth2.ntlm_resp.alloc = 24;
289 req.packet.auth2.ntlm_resp.offset = data - req.packet.auth2.NTLMSSP;
290 ntlm_smb_nt_encrypt(prefs.proxy_pass, challenge, data); /* Append an NTLM response */
291 data += 24;
292 } else {
293 req.packet.auth2.lm_resp.len = 24; /* Fill in LM response security buffer */
294 req.packet.auth2.lm_resp.alloc = 24;
295 req.packet.auth2.lm_resp.offset = data - req.packet.auth2.NTLMSSP;
296 ntlm_smb_encrypt(prefs.proxy_pass, challenge, data); /* Append an LM response */
297 data += 24;
298 req.packet.auth2.ntlm_resp.len = 0; /* NTLM response is empty */
299 req.packet.auth2.ntlm_resp.alloc = 0;
300 req.packet.auth2.ntlm_resp.offset = data - req.packet.auth2.NTLMSSP;
302 req.packet.auth2.ntdomain_buf.len = strlen(ntdomain); /* Domain name */
303 req.packet.auth2.ntdomain_buf.alloc = req.packet.auth2.ntdomain_buf.len;
304 req.packet.auth2.ntdomain_buf.offset = data - req.packet.auth2.NTLMSSP;
305 strcpy(data, ntdomain);
306 data += req.packet.auth2.ntdomain_buf.len;
307 req.packet.auth2.username_buf.len = strlen(prefs.proxy_user); /* Username */
308 req.packet.auth2.username_buf.alloc = req.packet.auth2.username_buf.len;
309 req.packet.auth2.username_buf.offset = data - req.packet.auth2.NTLMSSP;
310 strcpy(data, prefs.proxy_user);
311 data += req.packet.auth2.username_buf.len;
312 req.packet.auth2.clienthost_buf.len = strlen(hostname); /* Hostname */
313 req.packet.auth2.clienthost_buf.alloc = req.packet.auth2.clienthost_buf.len;
314 req.packet.auth2.clienthost_buf.offset = data - req.packet.auth2.NTLMSSP;
315 strcpy(data, hostname);
316 data += req.packet.auth2.clienthost_buf.len;
317 req.packet.auth2.sessionkey_buf.len = 0; /* Session key (we don't use it) */
318 req.packet.auth2.sessionkey_buf.alloc = 0;
319 req.packet.auth2.sessionkey_buf.offset = data - req.packet.auth2.NTLMSSP;
321 if (send_msprequest(sok, state, &req, data) == -1)
322 return 1;
324 if (recv_mspresponse(sok, state, &res) == -1)
325 return 1;
327 if (res.serverid != state->serverid) {
328 #ifdef DEBUG_MSPROXY
329 printf ("expected res.serverid = 0x%x, is 0x%x\n", state->serverid, res.serverid);
330 #endif
331 return 1;
334 if (res.clientack != 0x01) {
335 #ifdef DEBUG_MSPROXY
336 printf ("expected res.clientack = 0x01, is 0x%x\n", res.clientack);
337 #endif
338 return 1;
341 if (ntohs(res.command) >> 8 != 0x47) {
342 #ifdef DEBUG_MSPROXY
343 printf ("expected res.command = 47??, is 0x%x\n", ntohs(res.command));
344 #endif
345 return 1;
348 if (ntohs(res.command) == MSPROXY_AUTHENTICATE_2_NAK) {
349 #ifdef DEBUG_MSPROXY
350 printf ("Authentication failed\n");
351 #endif
352 return -1;
355 #ifdef DEBUG_MSPROXY
356 printf ("packet #5\n");
357 #endif
359 bzero(&req, sizeof(req));
360 req.clientid = state->clientid;
361 req.serverid = state->serverid;
362 req.command = htons(MSPROXY_CONNECT);
363 req.packet.connect.magic2 = htons(0x0200);
364 req.packet.connect.magic6 = htons(0x0200);
365 req.packet.connect.destport = htons(port);
366 req.packet.connect.destaddr = destaddr;
367 data = req.packet.connect.executable;
368 strcpy(data, MSPROXY_EXECUTABLE);
369 data += strlen(MSPROXY_EXECUTABLE) + 1;
372 * need to tell server what port we will connect from, so we bind our sockets.
374 ns_client = net_store_new ();
375 if (!bound) {
376 net_store_fill_any (ns_client);
377 net_bind(ns_client, csok4, csok6);
378 #ifdef DEBUG_MSPROXY
379 perror ("bind() result");
380 #endif
382 clientport = net_getsockport(csok4, csok6);
383 if (clientport == -1) {
384 #ifdef DEBUG_MSPROXY
385 printf ("Unable to obtain source port\n");
386 #endif
387 return 1;
389 req.packet.connect.srcport = clientport;
391 if (send_msprequest(sok, state, &req, data) == -1)
392 return 1;
394 if (recv_mspresponse(sok, state, &res) == -1)
395 return 1;
397 if (ntohs(res.command) != MSPROXY_CONNECT_ACK) {
398 #ifdef DEBUG_MSPROXY
399 printf ("expected res.command = 0x%x, is 0x%x\n",MSPROXY_CONNECT_ACK, ntohs(res.command));
400 #endif
401 return 1;
404 net_store_fill_v4 (ns_client, res.packet.connect.clientaddr, res.packet.connect.clientport);
406 #ifdef DEBUG_MSPROXY
407 printf ("Connecting...\n");
408 #endif
409 if (net_connect (ns_client, csok4, csok6, csok) != 0) {
410 #ifdef DEBUG_MSPROXY
411 printf ("Failed to connect to port %d\n", htons(res.packet.connect.clientport));
412 #endif
413 net_store_destroy (ns_client);
414 return 1;
416 net_store_destroy (ns_client);
417 #ifdef DEBUG_MSPROXY
418 printf ("packet #6\n");
419 #endif
421 req.clientid = state->clientid;
422 req.serverid = state->serverid;
423 req.command = htons(MSPROXY_USERINFO_ACK);
425 if (send_msprequest(sok, state, &req, req.packet.connack.data) == -1)
426 return 1;
428 return 0;
431 void
432 msproxy_keepalive (void)
434 server *serv;
435 GSList *list = serv_list;
436 struct msproxy_request_t req;
437 struct msproxy_response_t res;
439 while (list)
441 serv = list->data;
442 if (serv->connected && (serv->proxy_sok != -1))
444 #ifdef DEBUG_MSPROXY
445 printf ("sending MS proxy keepalive packet\n");
446 #endif
448 bzero(&req, sizeof(req));
449 req.clientid = serv->msp_state.clientid;
450 req.serverid = serv->msp_state.serverid;
451 req.command = htons(MSPROXY_HELLO);
453 if (send_msprequest(serv->proxy_sok, &serv->msp_state, &req, req.packet.hello.data) == -1)
454 continue;
456 recv_mspresponse(serv->proxy_sok, &serv->msp_state, &res);
458 #ifdef DEBUG_MSPROXY
459 if (ntohs(res.command) != MSPROXY_USERINFO_ACK)
460 printf ("expected res.command = 0x%x, is 0x%x\n", MSPROXY_USERINFO_ACK, ntohs(res.command));
461 #endif
463 list = list->next;
467 #endif