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
+ 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
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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;
+}
* 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 */
* authentication methods:
* 0x00 = no authentication
* 0x01 = GSSAPI
- * 0x02 = username/password
+ * 0x02 = username/password
* 0x03 = CHAP
* 0xff = no acceptable methods
*/
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:
}
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;
}
}
/* 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;
*/
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
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];
void MD5Update(struct MD5Context *context, unsigned char const *buf,
unsigned len);
void MD5Final(unsigned char digest[16], struct MD5Context *context);
+void MD5Simple(void const *p, unsigned len, unsigned char output[16]);
+
+void *hmacmd5_make_context(void);
+void hmacmd5_free_context(void *handle);
+void hmacmd5_key(void *handle, unsigned char const *key, int len);
+void hmacmd5_do_hmac(void *handle, unsigned char const *blk, int len,
+ unsigned char *hmac);
typedef struct {
uint32 h[5];
}
}
+void MD5Simple(void const *p, unsigned len, unsigned char output[16])
+{
+ struct MD5Context s;
+
+ MD5Init(&s);
+ MD5Update(&s, (unsigned char const *)p, len);
+ MD5Final(output, &s);
+}
+
/* ----------------------------------------------------------------------
* The above is the MD5 algorithm itself. Now we implement the
* HMAC wrapper on it.
+ *
+ * Some of these functions are exported directly, because they are
+ * useful elsewhere (SOCKS5 CHAP authentication uses HMAC-MD5).
*/
-static void *md5_make_context(void)
+void *hmacmd5_make_context(void)
{
return snewn(2, struct MD5Context);
}
-static void md5_free_context(void *handle)
+void hmacmd5_free_context(void *handle)
{
sfree(handle);
}
-static void md5_key_internal(void *handle, unsigned char *key, int len)
+void hmacmd5_key(void *handle, unsigned char const *key, int len)
{
struct MD5Context *keys = (struct MD5Context *)handle;
unsigned char foo[64];
memset(foo, 0, 64); /* burn the evidence */
}
-static void md5_key(void *handle, unsigned char *key)
+static void hmacmd5_key_16(void *handle, unsigned char *key)
{
- md5_key_internal(handle, key, 16);
+ hmacmd5_key(handle, key, 16);
}
-static void md5_do_hmac(void *handle, unsigned char *blk, int len,
- unsigned long seq, unsigned char *hmac)
+static void hmacmd5_do_hmac_internal(void *handle,
+ unsigned char const *blk, int len,
+ unsigned char const *blk2, int len2,
+ unsigned char *hmac)
{
struct MD5Context *keys = (struct MD5Context *)handle;
struct MD5Context s;
unsigned char intermediate[16];
- intermediate[0] = (unsigned char) ((seq >> 24) & 0xFF);
- intermediate[1] = (unsigned char) ((seq >> 16) & 0xFF);
- intermediate[2] = (unsigned char) ((seq >> 8) & 0xFF);
- intermediate[3] = (unsigned char) ((seq) & 0xFF);
-
s = keys[0]; /* structure copy */
- MD5Update(&s, intermediate, 4);
MD5Update(&s, blk, len);
+ if (blk2) MD5Update(&s, blk2, len2);
MD5Final(intermediate, &s);
s = keys[1]; /* structure copy */
MD5Update(&s, intermediate, 16);
MD5Final(hmac, &s);
}
-static void md5_generate(void *handle, unsigned char *blk, int len,
- unsigned long seq)
+void hmacmd5_do_hmac(void *handle, unsigned char const *blk, int len,
+ unsigned char *hmac)
+{
+ hmacmd5_do_hmac_internal(handle, blk, len, NULL, 0, hmac);
+}
+
+static void hmacmd5_do_hmac_ssh(void *handle, unsigned char const *blk, int len,
+ unsigned long seq, unsigned char *hmac)
+{
+ unsigned char seqbuf[16];
+
+ seqbuf[0] = (unsigned char) ((seq >> 24) & 0xFF);
+ seqbuf[1] = (unsigned char) ((seq >> 16) & 0xFF);
+ seqbuf[2] = (unsigned char) ((seq >> 8) & 0xFF);
+ seqbuf[3] = (unsigned char) ((seq) & 0xFF);
+
+ hmacmd5_do_hmac_internal(handle, seqbuf, 4, blk, len, hmac);
+}
+
+static void hmacmd5_generate(void *handle, unsigned char *blk, int len,
+ unsigned long seq)
{
- md5_do_hmac(handle, blk, len, seq, blk + len);
+ hmacmd5_do_hmac_ssh(handle, blk, len, seq, blk + len);
}
-static int md5_verify(void *handle, unsigned char *blk, int len,
- unsigned long seq)
+static int hmacmd5_verify(void *handle, unsigned char *blk, int len,
+ unsigned long seq)
{
unsigned char correct[16];
- md5_do_hmac(handle, blk, len, seq, correct);
+ hmacmd5_do_hmac_ssh(handle, blk, len, seq, correct);
return !memcmp(correct, blk + len, 16);
}
const struct ssh_mac ssh_md5 = {
- md5_make_context, md5_free_context, md5_key,
- md5_generate, md5_verify,
+ hmacmd5_make_context, hmacmd5_free_context, hmacmd5_key_16,
+ hmacmd5_generate, hmacmd5_verify,
"hmac-md5",
16
};