0
|
1 /* Copyright 2014 Ivo Smits <Ivo@UCIS.nl>. All rights reserved. |
|
2 Redistribution and use in source and binary forms, with or without modification, are |
|
3 permitted provided that the following conditions are met: |
|
4 |
|
5 1. Redistributions of source code must retain the above copyright notice, this list of |
|
6 conditions and the following disclaimer. |
|
7 |
|
8 2. Redistributions in binary form must reproduce the above copyright notice, this list |
|
9 of conditions and the following disclaimer in the documentation and/or other materials |
|
10 provided with the distribution. |
|
11 |
|
12 THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED |
|
13 WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND |
|
14 FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR |
|
15 CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
|
16 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR |
|
17 SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON |
|
18 ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
|
19 NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF |
|
20 ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
|
21 |
|
22 The views and conclusions contained in the software and documentation are those of the |
|
23 authors and should not be interpreted as representing official policies, either expressed |
|
24 or implied, of Ivo Smits.*/ |
|
25 |
|
26 #include <stdio.h> |
|
27 #include <stdbool.h> |
|
28 #include <unistd.h> |
|
29 #include <fcntl.h> |
|
30 #include <string.h> |
|
31 #include <sys/types.h> |
|
32 #include <sodium/crypto_box_curve25519xsalsa20poly1305.h> |
|
33 #include <sodium/crypto_scalarmult_curve25519.h> |
|
34 #include <sodium/crypto_hash_sha512.h> |
|
35 #include "include.h" |
|
36 |
|
37 static unsigned char workbuffer[2000]; |
|
38 |
|
39 static int devurandomfd = -1; |
|
40 static bool randombytes(unsigned char* buffer, int len) { |
|
41 if (devurandomfd == -1) devurandomfd = open("/dev/urandom", O_RDONLY); |
|
42 if (devurandomfd == -1) return errorexit("could not open /dev/urandom"); |
|
43 while (len > 0) { |
|
44 int got = read(devurandomfd, buffer, len); |
|
45 if (got < 0) return errorexitp("could not read from /dev/urandom"); |
|
46 buffer += got; |
|
47 len -= got; |
|
48 } |
|
49 return true; |
|
50 } |
|
51 |
|
52 static void dumphex(char* lbl, unsigned char* buffer, int len) { |
|
53 fprintf(stderr, "%s: ", lbl); |
|
54 for (; len > 0; len--, buffer++) fprintf(stderr, "%02x", *buffer); |
|
55 fprintf(stderr, "\n"); |
|
56 } |
|
57 |
|
58 static void nonceinc(unsigned char* nonce) { |
|
59 int i; |
|
60 for (i = 23; i >= 0 && (++nonce[i] == 0); i--); |
|
61 } |
|
62 |
|
63 static bool send_all(int socket, unsigned char* buffer, int length) { |
|
64 while (length) { |
|
65 int sent = write(socket, buffer, length); |
|
66 if (sent <= 0) return errorexitp("socket write failed"); |
|
67 buffer += sent; |
|
68 length -= sent; |
|
69 } |
|
70 return true; |
|
71 } |
|
72 static bool send_packet(connection_context* context, unsigned char* buffer, int length, bool controlflag) { |
|
73 fprintf(stderr, "Send packet (size=%d type=%d control=%d encrypt=%d)\n", length, buffer[0], controlflag, context->send_encrypted); |
|
74 if (context->send_encrypted) { |
|
75 if (length + 32 > sizeof(workbuffer)) return errorexit("packet too big for encryption buffer"); |
|
76 memmove(workbuffer + 32, buffer, length); |
|
77 memset(workbuffer, 0, 32); |
|
78 crypto_box_curve25519xsalsa20poly1305_afternm(workbuffer, workbuffer, length + 32, context->send_nonce, context->send_key); |
|
79 nonceinc(context->send_nonce); |
|
80 buffer = workbuffer + 16; |
|
81 length += 16; |
|
82 } |
|
83 if (length > 0x7FFF) return errorexit("packet too big for framing"); |
|
84 unsigned char lbuf[2]; |
|
85 length = (length & 0x7FFF) | (controlflag ? 0x8000 : 0); |
|
86 lbuf[0] = (length >> 8) & 0xFF; |
|
87 lbuf[1] = length & 0xFF; |
|
88 if (!send_all(context->send_socket, lbuf, 2)) return false; |
|
89 if (!send_all(context->send_socket, buffer, length & 0x7FFF)) return false; |
|
90 return true; |
|
91 } |
|
92 static bool send_command_ack(connection_context* context, unsigned char code) { |
|
93 workbuffer[0] = code; |
|
94 return send_packet(context, workbuffer, 1, true); |
|
95 } |
|
96 |
|
97 |
|
98 static bool check_tunnel_ready(connection_context* context) { |
|
99 if (context->local_tunnelready) return true; |
|
100 if (context->require_key_authentication) return true; |
|
101 if (context->require_password_authentication) return true; |
|
102 if (context->require_encryption && !context->recv_encrypted) return true; |
|
103 if (!send_command_ack(context, 0)) return false; |
|
104 context->local_tunnelready = true; |
|
105 return true; |
|
106 } |
|
107 |
|
108 static bool process_network_packet(connection_context* context, unsigned char* buffer, int pktlen, int controlflag); |
|
109 bool connection_read(connection_context* context) { |
|
110 int got = read(context->recv_socket, context->recv_buffer + context->recv_offset, sizeof(context->recv_buffer) - context->recv_offset); |
|
111 if (got < 0) return errorexitp("read failure on socket"); |
|
112 context->recv_offset += got; |
|
113 while (context->recv_offset >= 2) { |
|
114 int pktlen = ((unsigned char)context->recv_buffer[0] << 8) | (unsigned char)context->recv_buffer[1]; |
|
115 int controlflag = pktlen & 0x8000; |
|
116 pktlen &= 0x7FFF; |
|
117 if (pktlen > sizeof(context->recv_buffer)) return errorexit("received packet too big for buffer"); |
|
118 if (context->recv_offset < pktlen + 2) break; |
|
119 if (!process_network_packet(context, context->recv_buffer + 2, pktlen, !!controlflag)) return false; |
|
120 context->recv_offset -= 2 + pktlen; |
|
121 memmove(context->recv_buffer, context->recv_buffer + 2 + pktlen, context->recv_offset); |
|
122 } |
|
123 if (!context->local_tunnelready) if (!check_tunnel_ready(context)) return false; |
|
124 return true; |
|
125 } |
|
126 static bool send_start_crypt_auth(connection_context* context, const unsigned char* key) { |
|
127 if (!context->key_updated) return context->startcryptauthsent && !key; |
|
128 if (key) { |
|
129 memcpy(context->local_seckey_next, key, 32); |
|
130 } else { |
|
131 randombytes(context->local_seckey_next, 32); |
|
132 } |
|
133 workbuffer[0] = 1; |
|
134 crypto_scalarmult_curve25519_base(workbuffer + 1, context->local_seckey_next); |
|
135 memcpy(workbuffer + 1 + 32, context->nonce_next, 24); |
|
136 if (!send_packet(context, workbuffer, 1 + 32 + 24, true)) return false; |
|
137 crypto_box_curve25519xsalsa20poly1305_beforenm(context->send_key, context->remote_pubkey, context->local_seckey_next); |
|
138 memcpy(context->send_nonce + 12, context->nonce_next + 12, 12); |
|
139 context->send_encrypted = true; |
|
140 context->startcryptauthsent = true; |
|
141 context->key_updated = false; |
|
142 return true; |
|
143 } |
|
144 static bool begin_update_key(connection_context* context, char* seckey) { |
|
145 if (!context->key_updated) return !seckey; |
|
146 if (seckey) { |
|
147 memcpy(context->local_seckey_next, seckey, 32); |
|
148 } else { |
|
149 randombytes(context->local_seckey_next, 32); |
|
150 } |
|
151 workbuffer[0] = 3; |
|
152 crypto_scalarmult_curve25519_base(workbuffer + 1, context->local_seckey_next); |
|
153 if (!send_packet(context, workbuffer, 1 + 32, true)) return false; |
|
154 crypto_box_curve25519xsalsa20poly1305_beforenm(context->send_key, context->remote_pubkey, context->local_seckey_next); |
|
155 context->send_encrypted = true; |
|
156 context->key_updated = false; |
|
157 return true; |
|
158 } |
|
159 static bool password_hash(unsigned char* output, unsigned char* salt, int saltlength, char* password) { |
|
160 int pwlen = password ? strlen(password) : 0; |
|
161 if (saltlength + pwlen > sizeof(workbuffer)) return errorexit("password and salt too big for buffer"); |
|
162 if (saltlength) memmove(workbuffer, salt, saltlength); |
|
163 if (pwlen) memcpy(workbuffer + saltlength, password, pwlen); |
|
164 crypto_hash_sha512(output, workbuffer, saltlength + pwlen); |
|
165 return true; |
|
166 } |
|
167 static bool process_network_packet(connection_context* context, unsigned char* buffer, int pktlen, int controlflag) { |
|
168 if (context->recv_encrypted) { |
|
169 if (pktlen + 16 > sizeof(workbuffer)) return errorexit("received packet too big for decrypt buffer"); |
|
170 memmove(workbuffer + 16, buffer, pktlen); |
|
171 memset(workbuffer, 0, 16); |
|
172 if (crypto_box_curve25519xsalsa20poly1305_open_afternm(workbuffer, workbuffer, pktlen + 16, context->recv_nonce, context->recv_key)) return false; |
|
173 nonceinc(context->recv_nonce); |
|
174 buffer = workbuffer + 32; |
|
175 pktlen -= 16; |
|
176 } |
|
177 if (controlflag) { |
|
178 fprintf(stderr, "Received packet (size=%d type=%d control=%d encrypt=%d)\n", pktlen, buffer[0], controlflag, context->recv_encrypted); |
|
179 if (pktlen < 1) return errorexit("zero length control packet"); |
|
180 switch (buffer[0]) { |
|
181 case 0: |
|
182 fprintf(stderr, "Control: tunnel ready\n"); |
|
183 context->remote_tunnelready = true; |
|
184 break; |
|
185 case 1: |
|
186 fprintf(stderr, "Control: crypto auth req\n"); |
|
187 if (pktlen < 1 + 32 + 24) return errorexit("short control packet 1"); |
|
188 if (context->require_key_authentication) { |
|
189 if (memcmp(buffer + 1, context->remote_pubkey_expect, 32)) return errorexit("incorrect crypto auth key"); |
|
190 context->require_key_authentication = true; |
|
191 } |
|
192 if (!context->startcryptauthsent) send_start_crypt_auth(context, NULL); |
|
193 if (!send_command_ack(context, 2)) return false; |
|
194 memcpy(context->remote_pubkey, buffer + 1, 32); |
|
195 memcpy(context->send_nonce, buffer + 1 + 32, 12); |
|
196 memcpy(context->recv_nonce + 12, buffer + 1 + 32 + 12, 12); |
|
197 crypto_box_curve25519xsalsa20poly1305_beforenm(context->recv_key, context->remote_pubkey, context->local_seckey_current); |
|
198 crypto_box_curve25519xsalsa20poly1305_beforenm(context->send_key, context->remote_pubkey, context->local_seckey_next); |
|
199 context->recv_encrypted = context->send_encrypted = true; |
|
200 break; |
|
201 case 2: |
|
202 fprintf(stderr, "Control: crypto auth ack\n"); |
|
203 memcpy(context->local_seckey_current, context->local_seckey_next, 32); |
|
204 memcpy(context->recv_nonce, context->nonce_next, 12); |
|
205 crypto_box_curve25519xsalsa20poly1305_beforenm(context->recv_key, context->remote_pubkey, context->local_seckey_current); |
|
206 context->recv_encrypted = true; |
|
207 context->key_updated = true; |
|
208 begin_update_key(context, NULL); |
|
209 break; |
|
210 case 3: |
|
211 fprintf(stderr, "Control: key update req\n"); |
|
212 if (pktlen < 1 + 32) return errorexit("short control packet 3"); |
|
213 if (!send_command_ack(context, 4)) return false; |
|
214 memcpy(context->remote_pubkey, buffer + 1, 32); |
|
215 crypto_box_curve25519xsalsa20poly1305_beforenm(context->recv_key, context->remote_pubkey, context->local_seckey_current); |
|
216 crypto_box_curve25519xsalsa20poly1305_beforenm(context->send_key, context->remote_pubkey, context->local_seckey_next); |
|
217 context->recv_encrypted = context->send_encrypted = true; |
|
218 break; |
|
219 case 4: |
|
220 fprintf(stderr, "Control: key update ack\n"); |
|
221 memcpy(context->local_seckey_current, context->local_seckey_next, 32); |
|
222 crypto_box_curve25519xsalsa20poly1305_beforenm(context->recv_key, context->remote_pubkey, context->local_seckey_current); |
|
223 context->recv_encrypted = true; |
|
224 context->key_updated = true; |
|
225 break; |
|
226 case 5: |
|
227 fprintf(stderr, "Control: password authentication req\n"); |
|
228 unsigned char pwhash[64]; |
|
229 if (!password_hash(pwhash, buffer + 1, pktlen - 1, context->password)) return false; |
|
230 workbuffer[0] = 6; |
|
231 memcpy(workbuffer + 1, pwhash, 64); |
|
232 if (!send_packet(context, workbuffer, 1 + 64, true)) return false; |
|
233 break; |
|
234 case 6: |
|
235 fprintf(stderr, "Control: password authentication ack\n"); |
|
236 if (pktlen < 1 + 64) return errorexit("short control packet 6");; |
|
237 char pwhashcheck[64]; |
|
238 memcpy(pwhashcheck, buffer + 1, 64); |
|
239 if (!password_hash(pwhash, context->nonce_next, 12, context->password)) return false; |
|
240 if (memcmp(pwhash, pwhashcheck, 64)) return errorexit("incorrect password auth");; |
|
241 context->require_password_authentication = false; |
|
242 break; |
|
243 case 7: |
|
244 fprintf(stderr, "Control: disable encryption req\n"); |
|
245 context->recv_encrypted = context->send_encrypted = false; |
|
246 if (!send_command_ack(context, 8)) return false; |
|
247 break; |
|
248 case 8: |
|
249 fprintf(stderr, "Control: disable encryption ack\n"); |
|
250 context->recv_encrypted = false; |
|
251 break; |
|
252 case 9: |
|
253 fprintf(stderr, "Control: echo req\n"); |
|
254 if (pktlen > sizeof(workbuffer)) return errorexit("echo request too big");; |
|
255 memmove(workbuffer, buffer, pktlen); |
|
256 workbuffer[0] = 10; |
|
257 if (!send_packet(context, workbuffer, pktlen, true)) return false; |
|
258 break; |
|
259 case 10: |
|
260 fprintf(stderr, "Control: echo resp\n"); |
|
261 context->pong = true; |
|
262 break; |
|
263 case 13: |
|
264 if (!context->local_tunnelready || !context->remote_tunnelready) return errorexit("received data packet while tunnel not ready");; |
|
265 if (context->tunnel && !tunnel_write_data(context->tunnel, buffer + 1, pktlen - 1)) return false; |
|
266 break; |
|
267 case 11: |
|
268 case 12: |
|
269 case 17: |
|
270 case 81: |
|
271 break; |
|
272 default: |
|
273 fprintf(stderr, "Unknown control type %d\n", buffer[0]); |
|
274 break; |
|
275 } |
|
276 } else { |
|
277 if (!context->local_tunnelready || !context->remote_tunnelready) return errorexit("received data packet while tunnel not ready");; |
|
278 fprintf(stderr, "Tunnel data %d\n", pktlen); |
|
279 if (context->tunnel && !tunnel_write_data(context->tunnel, buffer, pktlen)) return false; |
|
280 } |
|
281 return true; |
|
282 } |
|
283 |
|
284 bool connection_ping(connection_context* context) { |
|
285 if (!context->pong) return false; |
|
286 context->pong = false; |
|
287 send_command_ack(context, 9); |
|
288 return true; |
|
289 } |
|
290 |
|
291 bool connection_init(connection_context* context) { |
|
292 memset(context, 0, sizeof(connection_context)); |
|
293 char localpubkey[32]; |
|
294 crypto_scalarmult_curve25519_base(context->remote_pubkey, context->local_seckey_current); |
|
295 randombytes(context->nonce_next, 24); |
|
296 context->key_updated = true; |
|
297 context->pong = true; |
|
298 return true; |
|
299 } |
|
300 bool connection_init_socket(connection_context* context, const int recvsocket, const int sendsocket) { |
|
301 context->recv_socket = recvsocket; |
|
302 context->send_socket = sendsocket; |
|
303 if (!send_packet(context, (unsigned char*)"QUICKTUN", 8, true)) return false; |
|
304 return true; |
|
305 } |
|
306 bool connection_init_encryption(connection_context* context, const unsigned char* localseckey, const unsigned char* remotepubkey) { |
|
307 if (remotepubkey) { |
|
308 memcpy(context->remote_pubkey_expect, remotepubkey, 32); |
|
309 context->require_key_authentication = true; |
|
310 } |
|
311 context->require_encryption = true; |
|
312 if (!send_start_crypt_auth(context, localseckey)) return false; |
|
313 return true; |
|
314 } |
|
315 bool connection_init_passwordauth(connection_context* context, char* password) { |
|
316 context->password = password; |
|
317 context->require_password_authentication = true; |
|
318 workbuffer[0] = 5; |
|
319 memcpy(workbuffer + 1, context->nonce_next, 12); |
|
320 if (!send_packet(context, workbuffer, 1 + 12, true)) return false; |
|
321 return true; |
|
322 } |
|
323 |
|
324 bool connection_write_data(connection_context* context, unsigned char* buffer, int len) { |
|
325 return send_packet(context, buffer, len, false); |
|
326 } |
|
327 |
|
328 bool connection_init_done(connection_context* context) { |
|
329 return check_tunnel_ready(context); |
|
330 } |
|
331 |
|
332 bool connection_update_key(connection_context* context) { |
|
333 return begin_update_key(context, NULL); |
|
334 } |
|
335 |