OSDN Git Service

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