471 lines
14 KiB
C
471 lines
14 KiB
C
/* X-Chat
|
|
* Copyright (C) 1998 Peter Zelezny.
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
|
*
|
|
* MS Proxy (ISA server) support is (c) 2006 Pavel Fedin <sonic_amiga@rambler.ru>
|
|
* based on Dante source code
|
|
* Copyright (c) 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006
|
|
* Inferno Nettverk A/S, Norway. All rights reserved.
|
|
*/
|
|
|
|
/*#define DEBUG_MSPROXY*/
|
|
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
#include <fcntl.h>
|
|
|
|
#ifndef WIN32
|
|
#include <unistd.h>
|
|
#endif
|
|
|
|
#define WANTSOCKET
|
|
#define WANTARPA
|
|
#include "inet.h"
|
|
|
|
#include "hexchat.h"
|
|
#include "network.h"
|
|
#include "hexchatc.h"
|
|
#include "server.h"
|
|
#include "msproxy.h"
|
|
|
|
|
|
#ifdef USE_MSPROXY
|
|
#include <ntlm.h>
|
|
|
|
static int
|
|
send_msprequest(s, state, request, end)
|
|
int s;
|
|
struct msproxy_state_t *state;
|
|
struct msproxy_request_t *request;
|
|
char *end;
|
|
{
|
|
ssize_t w;
|
|
size_t l;
|
|
|
|
request->magic25 = htonl(MSPROXY_VERSION);
|
|
request->serverack = state->seq_recv;
|
|
/* don't start incrementing sequence until we are acking packet #2. */
|
|
request->sequence = (unsigned char)(request->serverack >= 2 ? state->seq_sent + 1 : 0);
|
|
|
|
memcpy(request->RWSP, "RWSP", sizeof(request->RWSP));
|
|
|
|
l = end - (char *)request;
|
|
/* all requests must be atleast MSPROXY_MINLENGTH it seems. */
|
|
if (l < MSPROXY_MINLENGTH) {
|
|
bzero(end, (size_t)(MSPROXY_MINLENGTH - l));
|
|
l = MSPROXY_MINLENGTH;
|
|
}
|
|
|
|
if ((w = send(s, request, l, 0)) != l) {
|
|
#ifdef DEBUG_MSPROXY
|
|
printf ("send_msprequest(): send() failed (%d bytes sent instead of %d\n", w, l);
|
|
perror ("Error is");
|
|
#endif
|
|
return -1;
|
|
}
|
|
state->seq_sent = request->sequence;
|
|
|
|
return w;
|
|
}
|
|
|
|
static int
|
|
recv_mspresponse(s, state, response)
|
|
int s;
|
|
struct msproxy_state_t *state;
|
|
struct msproxy_response_t *response;
|
|
{
|
|
ssize_t r;
|
|
|
|
do {
|
|
if ((r = recv (s, response, sizeof (*response), 0)) < MSPROXY_MINLENGTH) {
|
|
#ifdef DEBUG_MSPROXY
|
|
printf ("recv_mspresponse(): expected to read atleast %d, read %d\n", MSPROXY_MINLENGTH, r);
|
|
#endif
|
|
return -1;
|
|
}
|
|
if (state->seq_recv == 0)
|
|
break; /* not started incrementing yet. */
|
|
#ifdef DEBUG_MSPROXY
|
|
if (response->sequence == state->seq_recv)
|
|
printf ("seq_recv: %d, dup response, seqnumber: 0x%x\n", state->seq_recv, response->sequence);
|
|
#endif
|
|
} while (response->sequence == state->seq_recv);
|
|
|
|
state->seq_recv = response->sequence;
|
|
|
|
return r;
|
|
}
|
|
|
|
int
|
|
traverse_msproxy (int sok, char *serverAddr, int port, struct msproxy_state_t *state, netstore *ns_proxy, int csok4, int csok6, int *csok, char bound)
|
|
{
|
|
struct msproxy_request_t req;
|
|
struct msproxy_response_t res;
|
|
char *data, *p;
|
|
char hostname[NT_MAXNAMELEN];
|
|
char ntdomain[NT_MAXNAMELEN];
|
|
char challenge[8];
|
|
netstore *ns_client;
|
|
int clientport;
|
|
guint32 destaddr;
|
|
guint32 flags;
|
|
|
|
if (!prefs.hex_net_proxy_auth || !prefs.hex_net_proxy_user[0] || !prefs.hex_net_proxy_pass[0] )
|
|
return 1;
|
|
|
|
/* MS proxy protocol implementation currently doesn't support IPv6 */
|
|
destaddr = net_getsockaddr_v4 (ns_proxy);
|
|
if (!destaddr)
|
|
return 1;
|
|
|
|
state->seq_recv = 0;
|
|
state->seq_sent = 0;
|
|
|
|
#ifdef DEBUG_MSPROXY
|
|
printf ("Connecting to %s:%d via MS proxy\n", serverAddr, port);
|
|
#endif
|
|
|
|
gethostname (hostname, NT_MAXNAMELEN);
|
|
p = strchr (hostname, '.');
|
|
if (p)
|
|
*p = '\0';
|
|
|
|
bzero (&req, sizeof(req));
|
|
req.clientid = htonl(0x0a000000); /* Initial client ID is always 0x0a */
|
|
req.command = htons(MSPROXY_HELLO); /* HELLO command */
|
|
req.packet.hello.magic5 = htons(0x4b00); /* Fill in magic values */
|
|
req.packet.hello.magic10 = htons(0x1400);
|
|
req.packet.hello.magic15 = htons(0x0400);
|
|
req.packet.hello.magic20 = htons(0x5704);
|
|
req.packet.hello.magic25 = htons(0x0004);
|
|
req.packet.hello.magic30 = htons(0x0100);
|
|
req.packet.hello.magic35 = htons(0x4a02);
|
|
req.packet.hello.magic40 = htons(0x3000);
|
|
req.packet.hello.magic45 = htons(0x4400);
|
|
req.packet.hello.magic50 = htons(0x3900);
|
|
data = req.packet.hello.data;
|
|
strcpy (data, prefs.hex_net_proxy_user); /* Append a username */
|
|
data += strlen (prefs.hex_net_proxy_user)+2; /* +2 automatically creates second empty string */
|
|
strcpy (data, MSPROXY_EXECUTABLE); /* Append an application name */
|
|
data += strlen (MSPROXY_EXECUTABLE)+1;
|
|
strcpy (data, hostname); /* Append a hostname */
|
|
data += strlen (hostname)+1;
|
|
|
|
if (send_msprequest(sok, state, &req, data) == -1)
|
|
return 1;
|
|
|
|
if (recv_mspresponse(sok, state, &res) == -1)
|
|
return 1;
|
|
|
|
if (strcmp(res.RWSP, "RWSP") != 0) {
|
|
#ifdef DEBUG_MSPROXY
|
|
printf ("Received mailformed packet (no RWSP signature)\n");
|
|
#endif
|
|
return 1;
|
|
}
|
|
|
|
if (ntohs(res.command) >> 8 != 0x10) {
|
|
#ifdef DEBUG_MSPROXY
|
|
printf ("expected res.command = 10??, is %x", ntohs(res.command));
|
|
#endif
|
|
return 1;
|
|
}
|
|
|
|
state->clientid = htonl(rand());
|
|
state->serverid = res.serverid;
|
|
|
|
#ifdef DEBUG_MSPROXY
|
|
printf ("clientid: 0x%x, serverid: 0x%0x\n", state->clientid, state->serverid);
|
|
printf ("packet #2\n");
|
|
#endif
|
|
|
|
/* almost identical. */
|
|
req.clientid = state->clientid;
|
|
req.serverid = state->serverid;
|
|
|
|
if (send_msprequest(sok, state, &req, data) == -1)
|
|
return 1;
|
|
|
|
if (recv_mspresponse(sok, state, &res) == -1)
|
|
return 1;
|
|
|
|
if (res.serverid != state->serverid) {
|
|
#ifdef DEBUG_MSPROXY
|
|
printf ("expected serverid = 0x%x, is 0x%x\n",state->serverid, res.serverid);
|
|
#endif
|
|
return 1;
|
|
}
|
|
|
|
if (res.sequence != 0x01) {
|
|
#ifdef DEBUG_MSPROXY
|
|
printf ("expected res.sequence = 0x01, is 0x%x\n", res.sequence);
|
|
#endif
|
|
return 1;
|
|
}
|
|
|
|
if (ntohs(res.command) != MSPROXY_USERINFO_ACK) {
|
|
#ifdef DEBUG_MSPROXY
|
|
printf ("expected res.command = 0x%x, is 0x%x\n", MSPROXY_USERINFO_ACK, ntohs(res.command));
|
|
#endif
|
|
return 1;
|
|
}
|
|
|
|
#ifdef DEBUG_MSPROXY
|
|
printf ("packet #3\n");
|
|
#endif
|
|
|
|
bzero(&req, sizeof(req));
|
|
req.clientid = state->clientid;
|
|
req.serverid = state->serverid;
|
|
req.command = htons(MSPROXY_AUTHENTICATE);
|
|
memcpy(req.packet.auth.NTLMSSP, "NTLMSSP", sizeof("NTLMSSP"));
|
|
req.packet.auth.bindaddr = htonl(0x02000000);
|
|
req.packet.auth.msgtype = htonl(0x01000000);
|
|
/* NTLM flags: 0x80000000 Negotiate LAN Manager key
|
|
0x10000000 Negotiate sign
|
|
0x04000000 Request target
|
|
0x02000000 Negotiate OEM
|
|
0x00800000 Always sign
|
|
0x00020000 Negotiate NTLM
|
|
*/
|
|
req.packet.auth.flags = htonl(0x06020000);
|
|
|
|
if (send_msprequest(sok, state, &req, &req.packet.auth.data) == -1)
|
|
return 1;
|
|
|
|
if (recv_mspresponse(sok, state, &res) == -1)
|
|
return 1;
|
|
|
|
if (res.serverid != state->serverid) {
|
|
#ifdef DEBUG_MSPROXY
|
|
printf ("expected serverid = 0x%x, is 0x%x\n", state->serverid, res.serverid);
|
|
#endif
|
|
return 1;
|
|
}
|
|
|
|
if (ntohs(res.command) != MSPROXY_AUTHENTICATE_ACK) {
|
|
#ifdef DEBUG_MSPROXY
|
|
printf ("expected res.command = 0x%x, is 0x%x\n", MSPROXY_AUTHENTICATE_ACK, ntohs(res.command));
|
|
#endif
|
|
return 1;
|
|
}
|
|
|
|
flags = res.packet.auth.flags & htonl(0x00020000); /* Remember if the server supports NTLM */
|
|
memcpy(challenge, &res.packet.auth.challenge, sizeof(challenge));
|
|
memcpy(ntdomain, &res.packet.auth.NTLMSSP[res.packet.auth.target.offset], res.packet.auth.target.len);
|
|
ntdomain[res.packet.auth.target.len] = 0;
|
|
|
|
#ifdef DEBUG_MSPROXY
|
|
printf ("ntdomain: \"%s\"\n", ntdomain);
|
|
printf ("packet #4\n");
|
|
#endif
|
|
|
|
bzero(&req, sizeof(req));
|
|
req.clientid = state->clientid;
|
|
req.serverid = state->serverid;
|
|
req.command = htons(MSPROXY_AUTHENTICATE_2); /* Authentication response */
|
|
req.packet.auth2.magic3 = htons(0x0200); /* Something */
|
|
memcpy(req.packet.auth2.NTLMSSP, "NTLMSSP", sizeof("NTLMSSP")); /* Start of NTLM message */
|
|
req.packet.auth2.msgtype = htonl(0x03000000); /* Message type 2 */
|
|
req.packet.auth2.flags = flags | htonl(0x02000000); /* Choose authentication method */
|
|
data = req.packet.auth2.data;
|
|
if (flags) {
|
|
req.packet.auth2.lm_resp.len = 0; /* We are here if NTLM is supported, */
|
|
req.packet.auth2.lm_resp.alloc = 0; /* Do not fill in insecure LM response */
|
|
req.packet.auth2.lm_resp.offset = data - req.packet.auth2.NTLMSSP;
|
|
req.packet.auth2.ntlm_resp.len = 24; /* Fill in NTLM response security buffer */
|
|
req.packet.auth2.ntlm_resp.alloc = 24;
|
|
req.packet.auth2.ntlm_resp.offset = data - req.packet.auth2.NTLMSSP;
|
|
ntlm_smb_nt_encrypt(prefs.hex_net_proxy_pass, challenge, data); /* Append an NTLM response */
|
|
data += 24;
|
|
} else {
|
|
req.packet.auth2.lm_resp.len = 24; /* Fill in LM response security buffer */
|
|
req.packet.auth2.lm_resp.alloc = 24;
|
|
req.packet.auth2.lm_resp.offset = data - req.packet.auth2.NTLMSSP;
|
|
ntlm_smb_encrypt(prefs.hex_net_proxy_pass, challenge, data); /* Append an LM response */
|
|
data += 24;
|
|
req.packet.auth2.ntlm_resp.len = 0; /* NTLM response is empty */
|
|
req.packet.auth2.ntlm_resp.alloc = 0;
|
|
req.packet.auth2.ntlm_resp.offset = data - req.packet.auth2.NTLMSSP;
|
|
}
|
|
req.packet.auth2.ntdomain_buf.len = strlen(ntdomain); /* Domain name */
|
|
req.packet.auth2.ntdomain_buf.alloc = req.packet.auth2.ntdomain_buf.len;
|
|
req.packet.auth2.ntdomain_buf.offset = data - req.packet.auth2.NTLMSSP;
|
|
strcpy(data, ntdomain);
|
|
data += req.packet.auth2.ntdomain_buf.len;
|
|
req.packet.auth2.username_buf.len = strlen(prefs.hex_net_proxy_user); /* Username */
|
|
req.packet.auth2.username_buf.alloc = req.packet.auth2.username_buf.len;
|
|
req.packet.auth2.username_buf.offset = data - req.packet.auth2.NTLMSSP;
|
|
strcpy(data, prefs.hex_net_proxy_user);
|
|
data += req.packet.auth2.username_buf.len;
|
|
req.packet.auth2.clienthost_buf.len = strlen(hostname); /* Hostname */
|
|
req.packet.auth2.clienthost_buf.alloc = req.packet.auth2.clienthost_buf.len;
|
|
req.packet.auth2.clienthost_buf.offset = data - req.packet.auth2.NTLMSSP;
|
|
strcpy(data, hostname);
|
|
data += req.packet.auth2.clienthost_buf.len;
|
|
req.packet.auth2.sessionkey_buf.len = 0; /* Session key (we don't use it) */
|
|
req.packet.auth2.sessionkey_buf.alloc = 0;
|
|
req.packet.auth2.sessionkey_buf.offset = data - req.packet.auth2.NTLMSSP;
|
|
|
|
if (send_msprequest(sok, state, &req, data) == -1)
|
|
return 1;
|
|
|
|
if (recv_mspresponse(sok, state, &res) == -1)
|
|
return 1;
|
|
|
|
if (res.serverid != state->serverid) {
|
|
#ifdef DEBUG_MSPROXY
|
|
printf ("expected res.serverid = 0x%x, is 0x%x\n", state->serverid, res.serverid);
|
|
#endif
|
|
return 1;
|
|
}
|
|
|
|
if (res.clientack != 0x01) {
|
|
#ifdef DEBUG_MSPROXY
|
|
printf ("expected res.clientack = 0x01, is 0x%x\n", res.clientack);
|
|
#endif
|
|
return 1;
|
|
}
|
|
|
|
if (ntohs(res.command) >> 8 != 0x47) {
|
|
#ifdef DEBUG_MSPROXY
|
|
printf ("expected res.command = 47??, is 0x%x\n", ntohs(res.command));
|
|
#endif
|
|
return 1;
|
|
}
|
|
|
|
if (ntohs(res.command) == MSPROXY_AUTHENTICATE_2_NAK) {
|
|
#ifdef DEBUG_MSPROXY
|
|
printf ("Authentication failed\n");
|
|
#endif
|
|
return -1;
|
|
}
|
|
|
|
#ifdef DEBUG_MSPROXY
|
|
printf ("packet #5\n");
|
|
#endif
|
|
|
|
bzero(&req, sizeof(req));
|
|
req.clientid = state->clientid;
|
|
req.serverid = state->serverid;
|
|
req.command = htons(MSPROXY_CONNECT);
|
|
req.packet.connect.magic2 = htons(0x0200);
|
|
req.packet.connect.magic6 = htons(0x0200);
|
|
req.packet.connect.destport = htons(port);
|
|
req.packet.connect.destaddr = destaddr;
|
|
data = req.packet.connect.executable;
|
|
strcpy(data, MSPROXY_EXECUTABLE);
|
|
data += strlen(MSPROXY_EXECUTABLE) + 1;
|
|
|
|
/*
|
|
* need to tell server what port we will connect from, so we bind our sockets.
|
|
*/
|
|
ns_client = net_store_new ();
|
|
if (!bound) {
|
|
net_store_fill_any (ns_client);
|
|
net_bind(ns_client, csok4, csok6);
|
|
#ifdef DEBUG_MSPROXY
|
|
perror ("bind() result");
|
|
#endif
|
|
}
|
|
clientport = net_getsockport(csok4, csok6);
|
|
if (clientport == -1) {
|
|
#ifdef DEBUG_MSPROXY
|
|
printf ("Unable to obtain source port\n");
|
|
#endif
|
|
return 1;
|
|
}
|
|
req.packet.connect.srcport = clientport;
|
|
|
|
if (send_msprequest(sok, state, &req, data) == -1)
|
|
return 1;
|
|
|
|
if (recv_mspresponse(sok, state, &res) == -1)
|
|
return 1;
|
|
|
|
if (ntohs(res.command) != MSPROXY_CONNECT_ACK) {
|
|
#ifdef DEBUG_MSPROXY
|
|
printf ("expected res.command = 0x%x, is 0x%x\n",MSPROXY_CONNECT_ACK, ntohs(res.command));
|
|
#endif
|
|
return 1;
|
|
}
|
|
|
|
net_store_fill_v4 (ns_client, res.packet.connect.clientaddr, res.packet.connect.clientport);
|
|
|
|
#ifdef DEBUG_MSPROXY
|
|
printf ("Connecting...\n");
|
|
#endif
|
|
if (net_connect (ns_client, csok4, csok6, csok) != 0) {
|
|
#ifdef DEBUG_MSPROXY
|
|
printf ("Failed to connect to port %d\n", htons(res.packet.connect.clientport));
|
|
#endif
|
|
net_store_destroy (ns_client);
|
|
return 1;
|
|
}
|
|
net_store_destroy (ns_client);
|
|
#ifdef DEBUG_MSPROXY
|
|
printf ("packet #6\n");
|
|
#endif
|
|
|
|
req.clientid = state->clientid;
|
|
req.serverid = state->serverid;
|
|
req.command = htons(MSPROXY_USERINFO_ACK);
|
|
|
|
if (send_msprequest(sok, state, &req, req.packet.connack.data) == -1)
|
|
return 1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
void
|
|
msproxy_keepalive (void)
|
|
{
|
|
server *serv;
|
|
GSList *list = serv_list;
|
|
struct msproxy_request_t req;
|
|
struct msproxy_response_t res;
|
|
|
|
while (list)
|
|
{
|
|
serv = list->data;
|
|
if (serv->connected && (serv->proxy_sok != -1))
|
|
{
|
|
#ifdef DEBUG_MSPROXY
|
|
printf ("sending MS proxy keepalive packet\n");
|
|
#endif
|
|
|
|
bzero(&req, sizeof(req));
|
|
req.clientid = serv->msp_state.clientid;
|
|
req.serverid = serv->msp_state.serverid;
|
|
req.command = htons(MSPROXY_HELLO);
|
|
|
|
if (send_msprequest(serv->proxy_sok, &serv->msp_state, &req, req.packet.hello.data) == -1)
|
|
continue;
|
|
|
|
recv_mspresponse(serv->proxy_sok, &serv->msp_state, &res);
|
|
|
|
#ifdef DEBUG_MSPROXY
|
|
if (ntohs(res.command) != MSPROXY_USERINFO_ACK)
|
|
printf ("expected res.command = 0x%x, is 0x%x\n", MSPROXY_USERINFO_ACK, ntohs(res.command));
|
|
#endif
|
|
}
|
|
list = list->next;
|
|
}
|
|
}
|
|
|
|
#endif
|