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 | |
18 | static 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 | |
39 | void proxy_socks5_offerencryptedauth(char *command, int *len) |
40 | { |
41 | command[*len] = 0x03; /* CHAP */ |
42 | (*len)++; |
43 | } |
44 | |
45 | int 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 | |
161 | int 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 | } |