Improve proxy server support
[tinc] / src / proxy.c
1 #include "system.h"
2
3 #include "logger.h"
4 #include "proxy.h"
5
6 typedef enum socks5_auth_method_t {
7         AUTH_ANONYMOUS = 0,
8         AUTH_PASSWORD = 2,
9         AUTH_FAILED = 0xFF,
10 } socks5_auth_method_t;
11
12 // SOCKS 4 constants (https://en.wikipedia.org/wiki/SOCKS#SOCKS4)
13 static const uint8_t SOCKS4_CMD_CONN = 1;
14 static const uint8_t SOCKS4_REPLY_VERSION = 0;
15 static const uint8_t SOCKS4_STATUS_OK = 0x5A;
16 static const uint8_t SOCKS4_VERSION = 4;
17
18 // SOCKS 5 constants (https://en.wikipedia.org/wiki/SOCKS#SOCKS5)
19 typedef enum socks5_addr_type_t {
20         SOCKS5_IPV4 = 1,
21         SOCKS5_IPV6 = 4,
22 } socks5_addr_type_t;
23
24 static const uint8_t SOCKS5_AUTH_METHOD_NONE = 0;
25 static const uint8_t SOCKS5_AUTH_METHOD_PASSWORD = 2;
26 static const uint8_t SOCKS5_AUTH_OK = 0;
27 static const uint8_t SOCKS5_AUTH_VERSION = 1;
28 static const uint8_t SOCKS5_COMMAND_CONN = 1;
29 static const uint8_t SOCKS5_STATUS_OK = 0;
30 static const uint8_t SOCKS5_VERSION = 5;
31
32 static void log_proxy_grant(bool granted) {
33         if(granted) {
34                 logger(DEBUG_CONNECTIONS, LOG_DEBUG, "Proxy request granted");
35         } else {
36                 logger(DEBUG_CONNECTIONS, LOG_ERR, "Proxy request rejected");
37         }
38 }
39
40 static void log_short_response(void) {
41         logger(DEBUG_CONNECTIONS, LOG_ERR, "Received short response from proxy");
42 }
43
44 static bool check_socks4_resp(const socks4_response_t *resp, size_t len) {
45         if(len < sizeof(socks4_response_t)) {
46                 log_short_response();
47                 return false;
48         }
49
50         if(resp->version != SOCKS4_REPLY_VERSION) {
51                 logger(DEBUG_CONNECTIONS, LOG_ERR, "Bad response from SOCKS4 proxy");
52                 return false;
53         }
54
55         bool granted = resp->status == SOCKS4_STATUS_OK;
56         log_proxy_grant(granted);
57         return granted;
58 }
59
60 static bool socks5_check_result(const socks5_conn_resp_t *re, size_t len) {
61         size_t addrlen;
62
63         switch((socks5_addr_type_t)re->addr_type) {
64         case SOCKS5_IPV4:
65                 addrlen = sizeof(socks5_ipv4_t);
66                 break;
67
68         case SOCKS5_IPV6:
69                 addrlen = sizeof(socks5_ipv6_t);
70                 break;
71
72         default:
73                 logger(DEBUG_CONNECTIONS, LOG_ERR, "Unsupported address type 0x%x from proxy server", re->addr_type);
74                 return false;
75         }
76
77         if(len < addrlen) {
78                 logger(DEBUG_CONNECTIONS, LOG_ERR, "Received short address from proxy server");
79                 return false;
80         }
81
82         if(re->socks_version != SOCKS5_VERSION) {
83                 logger(DEBUG_CONNECTIONS, LOG_ERR, "Invalid response from proxy server");
84                 return false;
85         }
86
87         bool granted = re->conn_status == SOCKS5_STATUS_OK;
88         log_proxy_grant(granted);
89         return granted;
90 }
91
92 static bool check_socks5_resp(const socks5_resp_t *resp, size_t len) {
93         if(len < sizeof(socks5_server_choice_t)) {
94                 log_short_response();
95                 return false;
96         }
97
98         len -= sizeof(socks5_server_choice_t);
99
100         if(resp->choice.socks_version != SOCKS5_VERSION) {
101                 logger(DEBUG_CONNECTIONS, LOG_ERR, "Invalid response from proxy server");
102                 return false;
103         }
104
105         switch((socks5_auth_method_t) resp->choice.auth_method) {
106         case AUTH_ANONYMOUS:
107                 if(len < sizeof(socks5_conn_resp_t)) {
108                         log_short_response();
109                         return false;
110                 } else {
111                         return socks5_check_result(&resp->anon, len - sizeof(socks5_conn_resp_t));
112                 }
113
114         case AUTH_PASSWORD: {
115                 size_t header_len = sizeof(socks5_auth_status_t) + sizeof(socks5_conn_resp_t);
116
117                 if(len < header_len) {
118                         log_short_response();
119                         return false;
120                 }
121
122                 if(resp->pass.status.auth_version != SOCKS5_AUTH_VERSION) {
123                         logger(DEBUG_CONNECTIONS, LOG_ERR, "Invalid proxy authentication protocol version");
124                         return false;
125                 }
126
127                 if(resp->pass.status.auth_status != SOCKS5_AUTH_OK) {
128                         logger(DEBUG_CONNECTIONS, LOG_ERR, "Proxy authentication failed");
129                         return false;
130                 }
131
132                 return socks5_check_result(&resp->pass.resp, len - header_len);
133         }
134
135         case AUTH_FAILED:
136                 logger(DEBUG_CONNECTIONS, LOG_ERR, "Proxy request rejected: unsuitable authentication method");
137                 return false;
138
139         default:
140                 logger(DEBUG_CONNECTIONS, LOG_ERR, "Unsupported authentication method");
141                 return false;
142         }
143 }
144
145 bool check_socks_resp(proxytype_t type, const void *buf, size_t len) {
146         if(type == PROXY_SOCKS4) {
147                 return check_socks4_resp(buf, len);
148         } else if(type == PROXY_SOCKS5) {
149                 return check_socks5_resp(buf, len);
150         } else {
151                 return false;
152         }
153 }
154
155 static size_t create_socks4_req(socks4_request_t *req, const sockaddr_t *sa) {
156         if(sa->sa.sa_family != AF_INET) {
157                 logger(DEBUG_ALWAYS, LOG_ERR, "Cannot connect to an IPv6 host through a SOCKS 4 proxy!");
158                 return 0;
159         }
160
161         req->version = SOCKS4_VERSION;
162         req->command = SOCKS4_CMD_CONN;
163         req->dstport = sa->in.sin_port;
164         req->dstip = sa->in.sin_addr;
165
166         if(proxyuser) {
167                 strcpy(req->id, proxyuser);
168         } else {
169                 req->id[0] = '\0';
170         }
171
172         return sizeof(socks4_response_t);
173 }
174
175 static size_t create_socks5_req(void *buf, const sockaddr_t *sa) {
176         uint16_t family = sa->sa.sa_family;
177
178         if(family != AF_INET && family != AF_INET6) {
179                 logger(DEBUG_ALWAYS, LOG_ERR, "Address family %x not supported for SOCKS 5 proxies!", family);
180                 return 0;
181         }
182
183         socks5_greet_t *req = buf;
184         req->version = SOCKS5_VERSION;
185         req->nmethods = 1; // only one auth method is supported
186
187         size_t resplen = sizeof(socks5_server_choice_t);
188         uint8_t *auth = (uint8_t *)buf + sizeof(socks5_greet_t);
189
190         if(proxyuser && proxypass) {
191                 req->authmethod = SOCKS5_AUTH_METHOD_PASSWORD;
192
193                 // field  | VER | IDLEN |  ID   | PWLEN |   PW  |
194                 // bytes  |  1  |   1   | 1-255 |   1   | 1-255 |
195
196                 // Assign the first field (auth protocol version)
197                 *auth++ = SOCKS5_AUTH_VERSION;
198
199                 size_t userlen = strlen(proxyuser);
200                 size_t passlen = strlen(proxypass);
201
202                 // Assign the username length, and copy the username
203                 *auth++ = userlen;
204                 memcpy(auth, proxyuser, userlen);
205                 auth += userlen;
206
207                 // Do the same for password
208                 *auth++ = passlen;
209                 memcpy(auth, proxypass, passlen);
210                 auth += passlen;
211
212                 resplen += sizeof(socks5_auth_status_t);
213         } else {
214                 req->authmethod = SOCKS5_AUTH_METHOD_NONE;
215         }
216
217         socks5_conn_req_t *conn = (socks5_conn_req_t *) auth;
218         conn->header.version = SOCKS5_VERSION;
219         conn->header.command = SOCKS5_COMMAND_CONN;
220         conn->header.reserved = 0;
221
222         resplen += sizeof(socks5_conn_resp_t);
223
224         if(family == AF_INET) {
225                 conn->header.addr_type = SOCKS5_IPV4;
226                 conn->dst.ipv4.addr = sa->in.sin_addr;
227                 conn->dst.ipv4.port = sa->in.sin_port;
228                 resplen += sizeof(socks5_ipv4_t);
229         } else {
230                 conn->header.addr_type = SOCKS5_IPV6;
231                 conn->dst.ipv6.addr = sa->in6.sin6_addr;
232                 conn->dst.ipv6.port = sa->in6.sin6_port;
233                 resplen += sizeof(socks5_ipv6_t);
234         }
235
236         return resplen;
237 }
238
239 size_t socks_req_len(proxytype_t type, const sockaddr_t *sa) {
240         uint16_t family = sa->sa.sa_family;
241
242         if(type == PROXY_SOCKS4) {
243                 if(family != AF_INET) {
244                         logger(DEBUG_CONNECTIONS, LOG_ERR, "SOCKS 4 only supports IPv4 addresses");
245                         return 0;
246                 }
247
248                 size_t userlen_size = 1;
249                 size_t userlen = proxyuser ? strlen(proxyuser) : 0;
250                 return sizeof(socks4_request_t) + userlen_size + userlen;
251         }
252
253         if(type == PROXY_SOCKS5) {
254                 if(family != AF_INET && family != AF_INET6) {
255                         logger(DEBUG_CONNECTIONS, LOG_ERR, "SOCKS 5 only supports IPv4 and IPv6");
256                         return 0;
257                 }
258
259                 size_t len = sizeof(socks5_greet_t) +
260                              sizeof(socks5_conn_hdr_t) +
261                              (family == AF_INET
262                               ? sizeof(socks5_ipv4_t)
263                               : sizeof(socks5_ipv6_t));
264
265                 if(proxyuser && proxypass) {
266                         // version, userlen, user, passlen, pass
267                         len += 1 + 1 + strlen(proxyuser) + 1 + strlen(proxypass);
268                 }
269
270                 return len;
271         }
272
273         logger(DEBUG_CONNECTIONS, LOG_ERR, "Bad proxy type 0x%x", type);
274         return 0;
275 }
276
277 size_t create_socks_req(proxytype_t type, void *buf, const sockaddr_t *sa) {
278         if(type == PROXY_SOCKS4) {
279                 return create_socks4_req(buf, sa);
280         } else if(type == PROXY_SOCKS5) {
281                 return create_socks5_req(buf, sa);
282         } else {
283                 abort();
284         }
285 }