Skip to content

Commit

Permalink
Malcolm Smith's patch to support CHAP (digest-based) authentication
Browse files Browse the repository at this point in the history
when talking to SOCKS 5 proxies. Configures itself transparently (if
the proxy offers CHAP it will use it, otherwise it falls back to
ordinary cleartext passwords).

[originally from svn r4517]
  • Loading branch information
sgtatham committed Aug 30, 2004
1 parent e2cd7e4 commit 3af7d33
Show file tree
Hide file tree
Showing 8 changed files with 336 additions and 48 deletions.
39 changes: 23 additions & 16 deletions Recipe
Expand Up @@ -207,19 +207,26 @@ CHARSET = sbcsdat slookup sbcs utf8 toucs fromucs xenc mimeenc macenc localenc
LIBS = advapi32.lib user32.lib gdi32.lib comctl32.lib comdlg32.lib
+ shell32.lib winmm.lib imm32.lib winspool.lib

# Network backend sets. This also brings in the relevant attachment
# to proxy.c depending on whether we're crypto-avoidant or not.
BE_ALL = be_all cproxy
BE_NOSSH = be_nossh nocproxy
BE_SSH = be_none cproxy
BE_NONE = be_none nocproxy

# ------------------------------------------------------------
# Definitions of actual programs. The program name, followed by a
# colon, followed by a list of objects. Also in the list may be the
# keywords [G] for Windows GUI app, [C] for Console app, [X] for
# X/GTK Unix app, [U] for command-line Unix app, [M] for Macintosh app.

putty : [G] GUITERM NONSSH WINSSH be_all WINMISC win_res.res LIBS
puttytel : [G] GUITERM NONSSH be_nossh WINMISC win_res.res LIBS
plink : [C] plink console NONSSH WINSSH be_all logging WINMISC
putty : [G] GUITERM NONSSH WINSSH BE_ALL WINMISC win_res.res LIBS
puttytel : [G] GUITERM NONSSH BE_NOSSH WINMISC win_res.res LIBS
plink : [C] plink console NONSSH WINSSH BE_ALL logging WINMISC
+ plink.res LIBS
pscp : [C] scp winsftp console WINSSH be_none SFTP wildcard WINMISC
pscp : [C] scp winsftp console WINSSH BE_SSH SFTP wildcard WINMISC
+ scp.res LIBS
psftp : [C] psftp winsftp console WINSSH be_none SFTP WINMISC scp.res LIBS
psftp : [C] psftp winsftp console WINSSH BE_SSH SFTP WINMISC scp.res LIBS

pageant : [G] pageant sshrsa sshpubk sshdes sshbn sshmd5 version tree234
+ misc sshaes sshsha pageantc sshdss sshsh512 winutils winmisc
Expand All @@ -229,28 +236,28 @@ puttygen : [G] puttygen sshrsag sshdssg sshprime sshdes sshbn sshmd5 version
+ sshrand noise sshsha winstore misc winctrls sshrsa sshdss winmisc
+ sshpubk sshaes sshsh512 import winutils puttygen.res tree234 LIBS

pterm : [X] UXTERM uxmisc misc ldisc settings pty uxsel be_none uxstore
pterm : [X] UXTERM uxmisc misc ldisc settings pty uxsel BE_NONE uxstore
+ signal CHARSET cmdline ptermm version
putty : [X] UXTERM uxmisc misc ldisc settings pty uxsel be_all uxstore
putty : [X] UXTERM uxmisc misc ldisc settings pty uxsel BE_ALL uxstore
+ signal CHARSET uxputty NONSSH UXSSH UXMISC ux_x11
puttytel : [X] UXTERM uxmisc misc ldisc settings pty uxsel be_nossh uxstore
+ signal CHARSET uxputty NONSSH UXMISC
puttytel : [X] UXTERM uxmisc misc ldisc settings pty uxsel BE_NOSSH
+ uxstore signal CHARSET uxputty NONSSH UXMISC

plink : [U] uxplink uxcons NONSSH UXSSH be_all logging UXMISC signal ux_x11
plink : [U] uxplink uxcons NONSSH UXSSH BE_ALL logging UXMISC signal ux_x11

puttygen : [U] cmdgen sshrsag sshdssg sshprime sshdes sshbn sshmd5 version
+ sshrand uxnoise sshsha misc sshrsa sshdss uxcons uxstore uxmisc
+ sshpubk sshaes sshsh512 import puttygen.res tree234 uxgen

pscp : [U] scp uxsftp uxcons UXSSH be_none SFTP wildcard UXMISC
psftp : [U] psftp uxsftp uxcons UXSSH be_none SFTP UXMISC
pscp : [U] scp uxsftp uxcons UXSSH BE_SSH SFTP wildcard UXMISC
psftp : [U] psftp uxsftp uxcons UXSSH BE_SSH SFTP UXMISC

PuTTY : [M] terminal wcwidth ldiscucs logging be_all mac macdlg macevlog
PuTTY : [M] terminal wcwidth ldiscucs logging BE_ALL mac macdlg macevlog
+ macterm macucs mac_res.rsrc testback NONSSH MACSSH MACMISC CHARSET
+ stricmp vsnprint dialog config macctrls
PuTTYtel : [M] terminal wcwidth ldiscucs logging be_nossh mac macdlg macevlog
+ macterm macucs mac_res.rsrc testback NONSSH MACMISC CHARSET
+ stricmp vsnprint dialog config macctrls
PuTTYtel : [M] terminal wcwidth ldiscucs logging BE_NOSSH mac macdlg
+ macevlog macterm macucs mac_res.rsrc testback NONSSH MACMISC
+ CHARSET stricmp vsnprint dialog config macctrls
PuTTYgen : [M] macpgen sshrsag sshdssg sshprime sshdes sshbn sshmd5 version
+ sshrand macnoise sshsha macstore misc sshrsa sshdss macmisc sshpubk
+ sshaes sshsh512 import macpgen.rsrc macpgkey macabout
190 changes: 190 additions & 0 deletions cproxy.c
@@ -0,0 +1,190 @@
/*
* Routines to do cryptographic interaction with proxies in PuTTY.
* This is in a separate module from proxy.c, so that it can be
* conveniently removed in PuTTYtel by replacing this module with
* the stub version nocproxy.c.
*/

#include <assert.h>
#include <ctype.h>
#include <string.h>

#define DEFINE_PLUG_METHOD_MACROS
#include "putty.h"
#include "ssh.h" /* For MD5 support */
#include "network.h"
#include "proxy.h"

static void hmacmd5_chap(const unsigned char *challenge, int challen,
const char *passwd, unsigned char *response)
{
void *hmacmd5_ctx;
int pwlen;

hmacmd5_ctx = hmacmd5_make_context();

pwlen = strlen(passwd);
if (pwlen>64) {
unsigned char md5buf[16];
MD5Simple(passwd, pwlen, md5buf);
hmacmd5_key(hmacmd5_ctx, md5buf, 16);
} else {
hmacmd5_key(hmacmd5_ctx, passwd, pwlen);
}

hmacmd5_do_hmac(hmacmd5_ctx, challenge, challen, response);
hmacmd5_free_context(hmacmd5_ctx);
}

void proxy_socks5_offerencryptedauth(char *command, int *len)
{
command[*len] = 0x03; /* CHAP */
(*len)++;
}

int proxy_socks5_handlechap (Proxy_Socket p)
{

/* CHAP authentication reply format:
* version number (1 bytes) = 1
* number of commands (1 byte)
*
* For each command:
* command identifier (1 byte)
* data length (1 byte)
*/
unsigned char data[260];
unsigned char outbuf[20];

while(p->chap_num_attributes == 0 ||
p->chap_num_attributes_processed < p->chap_num_attributes) {
if (p->chap_num_attributes == 0 ||
p->chap_current_attribute == -1) {
/* CHAP normally reads in two bytes, either at the
* beginning or for each attribute/value pair. But if
* we're waiting for the value's data, we might not want
* to read 2 bytes.
*/

if (bufchain_size(&p->pending_input_data) < 2)
return 1; /* not got anything yet */

/* get the response */
bufchain_fetch(&p->pending_input_data, data, 2);
bufchain_consume(&p->pending_input_data, 2);
}

if (p->chap_num_attributes == 0) {
/* If there are no attributes, this is our first msg
* with the server, where we negotiate version and
* number of attributes
*/
if (data[0] != 0x01) {
plug_closing(p->plug, "Proxy error: SOCKS proxy wants"
" a different CHAP version",
PROXY_ERROR_GENERAL, 0);
return 1;
}
if (data[1] == 0x00) {
plug_closing(p->plug, "Proxy error: SOCKS proxy won't"
" negotiate CHAP with us",
PROXY_ERROR_GENERAL, 0);
return 1;
}
p->chap_num_attributes = data[1];
} else {
if (p->chap_current_attribute == -1) {
/* We have to read in each attribute/value pair -
* those we don't understand can be ignored, but
* there are a few we'll need to handle.
*/
p->chap_current_attribute = data[0];
p->chap_current_datalen = data[1];
}
if (bufchain_size(&p->pending_input_data) <
p->chap_current_datalen)
return 1; /* not got everything yet */

/* get the response */
bufchain_fetch(&p->pending_input_data, data,
p->chap_current_datalen);

bufchain_consume(&p->pending_input_data,
p->chap_current_datalen);

switch (p->chap_current_attribute) {
case 0x00:
/* Successful authentication */
if (data[0] == 0x00)
p->state = 2;
else {
plug_closing(p->plug, "Proxy error: SOCKS proxy"
" refused CHAP authentication",
PROXY_ERROR_GENERAL, 0);
return 1;
}
break;
case 0x03:
outbuf[0] = 0x01; /* Version */
outbuf[1] = 0x01; /* One attribute */
outbuf[2] = 0x04; /* Response */
outbuf[3] = 0x10; /* Length */
hmacmd5_chap(data, p->chap_current_datalen,
p->cfg.proxy_password, &outbuf[4]);
sk_write(p->sub_socket, outbuf, 20);
break;
case 0x11:
/* Chose a protocol */
if (data[0] != 0x85) {
plug_closing(p->plug, "Proxy error: Server chose "
"CHAP of other than HMAC-MD5 but we "
"didn't offer it!",
PROXY_ERROR_GENERAL, 0);
return 1;
}
break;
}
p->chap_current_attribute = -1;
p->chap_num_attributes_processed++;
}
if (p->state == 8 &&
p->chap_num_attributes_processed >= p->chap_num_attributes) {
p->chap_num_attributes = 0;
p->chap_num_attributes_processed = 0;
p->chap_current_datalen = 0;
}
}
return 0;
}

int proxy_socks5_selectchap(Proxy_Socket p)
{
if (p->cfg.proxy_username[0] || p->cfg.proxy_password[0]) {
char chapbuf[514];
int ulen;
chapbuf[0] = '\x01'; /* Version */
chapbuf[1] = '\x02'; /* Number of attributes sent */
chapbuf[2] = '\x11'; /* First attribute - algorithms list */
chapbuf[3] = '\x01'; /* Only one CHAP algorithm */
chapbuf[4] = '\x85'; /* ...and it's HMAC-MD5, the core one */
chapbuf[5] = '\x02'; /* Second attribute - username */

ulen = strlen(p->cfg.proxy_username);
if (ulen > 255) ulen = 255; if (ulen < 1) ulen = 1;

chapbuf[6] = ulen;
memcpy(chapbuf+7, p->cfg.proxy_username, ulen);

sk_write(p->sub_socket, chapbuf, ulen + 7);
p->chap_num_attributes = 0;
p->chap_num_attributes_processed = 0;
p->chap_current_attribute = -1;
p->chap_current_datalen = 0;

p->state = 8;
} else
plug_closing(p->plug, "Proxy error: Server chose "
"CHAP authentication but we didn't offer it!",
PROXY_ERROR_GENERAL, 0);
return 1;
}
36 changes: 36 additions & 0 deletions nocproxy.c
@@ -0,0 +1,36 @@
/*
* Routines to refuse to do cryptographic interaction with proxies
* in PuTTY. This is a stub implementation of the same interfaces
* provided by cproxy.c, for use in PuTTYtel.
*/

#include <assert.h>
#include <ctype.h>
#include <string.h>

#define DEFINE_PLUG_METHOD_MACROS
#include "putty.h"
#include "network.h"
#include "proxy.h"

void proxy_socks5_offerencryptedauth(char * command, int * len)
{
/* For telnet, don't add any new encrypted authentication routines */
}

int proxy_socks5_handlechap (Proxy_Socket p)
{

plug_closing(p->plug, "Proxy error: Trying to handle a SOCKS5 CHAP request"
" in telnet-only build",
PROXY_ERROR_GENERAL, 0);
return 1;
}

int proxy_socks5_selectchap(Proxy_Socket p)
{
plug_closing(p->plug, "Proxy error: Trying to handle a SOCKS5 CHAP request"
" in telnet-only build",
PROXY_ERROR_GENERAL, 0);
return 1;
}
24 changes: 15 additions & 9 deletions proxy.c
Expand Up @@ -859,15 +859,16 @@ int proxy_socks5_negotiate (Proxy_Socket p, int change)
* 0x03 = CHAP
*/

char command[4];
char command[5];
int len;

command[0] = 5; /* version 5 */
if (p->cfg.proxy_username[0] || p->cfg.proxy_password[0]) {
command[1] = 2; /* two methods supported: */
command[2] = 0x00; /* no authentication */
command[3] = 0x02; /* username/password */
len = 4;
len = 3;
proxy_socks5_offerencryptedauth (command, &len);
command[len++] = 0x02; /* username/password */
command[1] = len - 2; /* Number of methods supported */
} else {
command[1] = 1; /* one methods supported: */
command[2] = 0x00; /* no authentication */
Expand Down Expand Up @@ -923,7 +924,7 @@ int proxy_socks5_negotiate (Proxy_Socket p, int change)
* authentication methods:
* 0x00 = no authentication
* 0x01 = GSSAPI
* 0x02 = username/password
* 0x02 = username/password
* 0x03 = CHAP
* 0xff = no acceptable methods
*/
Expand Down Expand Up @@ -988,6 +989,12 @@ int proxy_socks5_negotiate (Proxy_Socket p, int change)
p->state = 2; /* now proceed as authenticated */
}

if (p->state == 8) {
int ret;
ret = proxy_socks5_handlechap(p);
if (ret) return ret;
}

if (p->state == 2) {

/* request format:
Expand Down Expand Up @@ -1156,10 +1163,9 @@ int proxy_socks5_negotiate (Proxy_Socket p, int change)
}

if (p->state == 6) {
/* TODO: Handle CHAP authentication */
plug_closing(p->plug, "Proxy error: We don't support CHAP authentication",
PROXY_ERROR_GENERAL, 0);
return 1;
int ret;
ret = proxy_socks5_selectchap(p);
if (ret) return ret;
}

}
Expand Down
14 changes: 14 additions & 0 deletions proxy.h
Expand Up @@ -81,6 +81,12 @@ struct Socket_proxy_tag {

/* configuration, used to look up proxy settings */
Config cfg;

/* CHAP transient data */
int chap_num_attributes;
int chap_num_attributes_processed;
int chap_current_attribute;
int chap_current_datalen;
};

typedef struct Plug_proxy_tag * Proxy_Plug;
Expand All @@ -106,4 +112,12 @@ extern int proxy_socks5_negotiate (Proxy_Socket, int);
*/
char *format_telnet_command(SockAddr addr, int port, const Config *cfg);

/*
* These are implemented in cproxy.c or nocproxy.c, depending on
* whether encrypted proxy authentication is available.
*/
extern void proxy_socks5_offerencryptedauth(char *command, int *len);
extern int proxy_socks5_handlechap (Proxy_Socket p);
extern int proxy_socks5_selectchap(Proxy_Socket p);

#endif
4 changes: 2 additions & 2 deletions putty.h
Expand Up @@ -338,8 +338,8 @@ struct config_tag {
int proxy_type;
char proxy_host[512];
int proxy_port;
char proxy_username[32];
char proxy_password[32];
char proxy_username[128];
char proxy_password[128];
char proxy_telnet_command[512];
/* SSH options */
char remote_cmd[512];
Expand Down

0 comments on commit 3af7d33

Please sign in to comment.