Sebastian Kuschel reports that pfd_closing can be called for a socket
[u/mdw/putty] / cproxy.c
CommitLineData
f33ba69e 1/*
2 * Routines to do cryptographic interaction with proxies in PuTTY.
3 * This is in a separate module from proxy.c, so that it can be
4 * conveniently removed in PuTTYtel by replacing this module with
5 * the stub version nocproxy.c.
6 */
7
8#include <assert.h>
9#include <ctype.h>
10#include <string.h>
11
12#define DEFINE_PLUG_METHOD_MACROS
13#include "putty.h"
14#include "ssh.h" /* For MD5 support */
15#include "network.h"
16#include "proxy.h"
17
18static void hmacmd5_chap(const unsigned char *challenge, int challen,
19 const char *passwd, unsigned char *response)
20{
21 void *hmacmd5_ctx;
22 int pwlen;
23
24 hmacmd5_ctx = hmacmd5_make_context();
25
26 pwlen = strlen(passwd);
27 if (pwlen>64) {
28 unsigned char md5buf[16];
29 MD5Simple(passwd, pwlen, md5buf);
30 hmacmd5_key(hmacmd5_ctx, md5buf, 16);
31 } else {
32 hmacmd5_key(hmacmd5_ctx, passwd, pwlen);
33 }
34
35 hmacmd5_do_hmac(hmacmd5_ctx, challenge, challen, response);
36 hmacmd5_free_context(hmacmd5_ctx);
37}
38
39void proxy_socks5_offerencryptedauth(char *command, int *len)
40{
41 command[*len] = 0x03; /* CHAP */
42 (*len)++;
43}
44
45int proxy_socks5_handlechap (Proxy_Socket p)
46{
47
48 /* CHAP authentication reply format:
49 * version number (1 bytes) = 1
50 * number of commands (1 byte)
51 *
52 * For each command:
53 * command identifier (1 byte)
54 * data length (1 byte)
55 */
56 unsigned char data[260];
57 unsigned char outbuf[20];
58
59 while(p->chap_num_attributes == 0 ||
60 p->chap_num_attributes_processed < p->chap_num_attributes) {
61 if (p->chap_num_attributes == 0 ||
62 p->chap_current_attribute == -1) {
63 /* CHAP normally reads in two bytes, either at the
64 * beginning or for each attribute/value pair. But if
65 * we're waiting for the value's data, we might not want
66 * to read 2 bytes.
67 */
68
69 if (bufchain_size(&p->pending_input_data) < 2)
70 return 1; /* not got anything yet */
71
72 /* get the response */
73 bufchain_fetch(&p->pending_input_data, data, 2);
74 bufchain_consume(&p->pending_input_data, 2);
75 }
76
77 if (p->chap_num_attributes == 0) {
78 /* If there are no attributes, this is our first msg
79 * with the server, where we negotiate version and
80 * number of attributes
81 */
82 if (data[0] != 0x01) {
83 plug_closing(p->plug, "Proxy error: SOCKS proxy wants"
84 " a different CHAP version",
85 PROXY_ERROR_GENERAL, 0);
86 return 1;
87 }
88 if (data[1] == 0x00) {
89 plug_closing(p->plug, "Proxy error: SOCKS proxy won't"
90 " negotiate CHAP with us",
91 PROXY_ERROR_GENERAL, 0);
92 return 1;
93 }
94 p->chap_num_attributes = data[1];
95 } else {
96 if (p->chap_current_attribute == -1) {
97 /* We have to read in each attribute/value pair -
98 * those we don't understand can be ignored, but
99 * there are a few we'll need to handle.
100 */
101 p->chap_current_attribute = data[0];
102 p->chap_current_datalen = data[1];
103 }
104 if (bufchain_size(&p->pending_input_data) <
105 p->chap_current_datalen)
106 return 1; /* not got everything yet */
107
108 /* get the response */
109 bufchain_fetch(&p->pending_input_data, data,
110 p->chap_current_datalen);
111
112 bufchain_consume(&p->pending_input_data,
113 p->chap_current_datalen);
114
115 switch (p->chap_current_attribute) {
116 case 0x00:
117 /* Successful authentication */
118 if (data[0] == 0x00)
119 p->state = 2;
120 else {
121 plug_closing(p->plug, "Proxy error: SOCKS proxy"
122 " refused CHAP authentication",
123 PROXY_ERROR_GENERAL, 0);
124 return 1;
125 }
126 break;
127 case 0x03:
128 outbuf[0] = 0x01; /* Version */
129 outbuf[1] = 0x01; /* One attribute */
130 outbuf[2] = 0x04; /* Response */
131 outbuf[3] = 0x10; /* Length */
132 hmacmd5_chap(data, p->chap_current_datalen,
4a693cfc 133 conf_get_str(p->conf, CONF_proxy_password),
134 &outbuf[4]);
11a60120 135 sk_write(p->sub_socket, (char *)outbuf, 20);
f33ba69e 136 break;
137 case 0x11:
138 /* Chose a protocol */
139 if (data[0] != 0x85) {
140 plug_closing(p->plug, "Proxy error: Server chose "
141 "CHAP of other than HMAC-MD5 but we "
142 "didn't offer it!",
143 PROXY_ERROR_GENERAL, 0);
144 return 1;
145 }
146 break;
147 }
148 p->chap_current_attribute = -1;
149 p->chap_num_attributes_processed++;
150 }
151 if (p->state == 8 &&
152 p->chap_num_attributes_processed >= p->chap_num_attributes) {
153 p->chap_num_attributes = 0;
154 p->chap_num_attributes_processed = 0;
155 p->chap_current_datalen = 0;
156 }
157 }
158 return 0;
159}
160
161int proxy_socks5_selectchap(Proxy_Socket p)
162{
4a693cfc 163 char *username = conf_get_str(p->conf, CONF_proxy_username);
164 char *password = conf_get_str(p->conf, CONF_proxy_password);
165 if (username[0] || password[0]) {
f33ba69e 166 char chapbuf[514];
167 int ulen;
168 chapbuf[0] = '\x01'; /* Version */
169 chapbuf[1] = '\x02'; /* Number of attributes sent */
170 chapbuf[2] = '\x11'; /* First attribute - algorithms list */
171 chapbuf[3] = '\x01'; /* Only one CHAP algorithm */
172 chapbuf[4] = '\x85'; /* ...and it's HMAC-MD5, the core one */
173 chapbuf[5] = '\x02'; /* Second attribute - username */
174
4a693cfc 175 ulen = strlen(username);
f33ba69e 176 if (ulen > 255) ulen = 255; if (ulen < 1) ulen = 1;
177
178 chapbuf[6] = ulen;
4a693cfc 179 memcpy(chapbuf+7, username, ulen);
f33ba69e 180
181 sk_write(p->sub_socket, chapbuf, ulen + 7);
182 p->chap_num_attributes = 0;
183 p->chap_num_attributes_processed = 0;
184 p->chap_current_attribute = -1;
185 p->chap_current_datalen = 0;
186
187 p->state = 8;
188 } else
189 plug_closing(p->plug, "Proxy error: Server chose "
190 "CHAP authentication but we didn't offer it!",
191 PROXY_ERROR_GENERAL, 0);
192 return 1;
193}