]> asedeno.scripts.mit.edu Git - PuTTY.git/blob - cproxy.c
first pass
[PuTTY.git] / cproxy.c
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(NULL);
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,
133                              conf_get_str(p->conf, CONF_proxy_password),
134                              &outbuf[4]);
135                 sk_write(p->sub_socket, (char *)outbuf, 20);
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 {
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]) {
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
175         ulen = strlen(username);
176         if (ulen > 255) ulen = 255;
177         if (ulen < 1) ulen = 1;
178
179         chapbuf[6] = ulen;
180         memcpy(chapbuf+7, username, ulen);
181
182         sk_write(p->sub_socket, chapbuf, ulen + 7);
183         p->chap_num_attributes = 0;
184         p->chap_num_attributes_processed = 0;
185         p->chap_current_attribute = -1;
186         p->chap_current_datalen = 0;
187
188         p->state = 8;
189     } else 
190         plug_closing(p->plug, "Proxy error: Server chose "
191                      "CHAP authentication but we didn't offer it!",
192                  PROXY_ERROR_GENERAL, 0);
193     return 1;
194 }