summaryrefslogtreecommitdiff
path: root/nacl/curvecp/curvecpserver.c
diff options
context:
space:
mode:
Diffstat (limited to 'nacl/curvecp/curvecpserver.c')
-rw-r--r--nacl/curvecp/curvecpserver.c497
1 files changed, 497 insertions, 0 deletions
diff --git a/nacl/curvecp/curvecpserver.c b/nacl/curvecp/curvecpserver.c
new file mode 100644
index 00000000..82cc6670
--- /dev/null
+++ b/nacl/curvecp/curvecpserver.c
@@ -0,0 +1,497 @@
1#include <signal.h>
2#include <stdlib.h>
3#include <sys/types.h>
4#include <sys/stat.h>
5#include <fcntl.h>
6#include <unistd.h>
7#include <poll.h>
8#include "e.h"
9#include "die.h"
10#include "byte.h"
11#include "open.h"
12#include "load.h"
13#include "socket.h"
14#include "uint64_pack.h"
15#include "uint64_unpack.h"
16#include "writeall.h"
17#include "nanoseconds.h"
18#include "safenonce.h"
19#include "nameparse.h"
20#include "hexparse.h"
21#include "portparse.h"
22#include "randommod.h"
23
24#include "randombytes.h"
25#include "crypto_box.h"
26#include "crypto_secretbox.h"
27#if crypto_box_PUBLICKEYBYTES != 32
28error!
29#endif
30#if crypto_box_NONCEBYTES != 24
31error!
32#endif
33#if crypto_box_BOXZEROBYTES != 16
34error!
35#endif
36#if crypto_box_ZEROBYTES != 32
37error!
38#endif
39#if crypto_box_BEFORENMBYTES != 32
40error!
41#endif
42#if crypto_secretbox_KEYBYTES != 32
43error!
44#endif
45#if crypto_secretbox_NONCEBYTES != 24
46error!
47#endif
48#if crypto_secretbox_BOXZEROBYTES != 16
49error!
50#endif
51#if crypto_secretbox_ZEROBYTES != 32
52error!
53#endif
54
55int flagverbose;
56
57#define USAGE "\
58curvecpserver: how to use:\n\
59curvecpserver: -q (optional): no error messages\n\
60curvecpserver: -Q (optional): print error messages (default)\n\
61curvecpserver: -v (optional): print extra information\n\
62curvecpserver: -c n (optional): allow at most n clients at once (default 100)\n\
63curvecpserver: sname: server's name\n\
64curvecpserver: keydir: use this public-key directory\n\
65curvecpserver: ip: server's IP address\n\
66curvecpserver: port: server's UDP port\n\
67curvecpserver: ext: server's extension\n\
68curvecpserver: prog: run this server\n\
69"
70
71void die_usage(const char *s)
72{
73 if (s) die_4(100,USAGE,"curvecpserver: fatal: ",s,"\n");
74 die_1(100,USAGE);
75}
76
77void die_fatal(const char *trouble,const char *d,const char *fn)
78{
79 if (!flagverbose) die_0(111);
80 if (d) {
81 if (fn) die_9(111,"curvecpserver: fatal: ",trouble," ",d,"/",fn,": ",e_str(errno),"\n");
82 die_7(111,"curvecpserver: fatal: ",trouble," ",d,": ",e_str(errno),"\n");
83 }
84 die_5(111,"curvecpserver: fatal: ",trouble,": ",e_str(errno),"\n");
85}
86
87int ipparse(unsigned char *y,const char *x)
88{
89 long long j;
90 long long k;
91 long long d;
92
93 for (k = 0;k < 4;++k) y[k] = 0;
94 for (k = 0;k < 4;++k) {
95 d = 0;
96 for (j = 0;j < 3 && x[j] >= '0' && x[j] <= '9';++j) d = d * 10 + (x[j] - '0');
97 if (j == 0) return 0;
98 x += j;
99 if (k >= 0 && k < 4) y[k] = d;
100 if (k < 3) {
101 if (*x != '.') return 0;
102 ++x;
103 }
104 }
105 if (*x) return 0;
106 return 1;
107}
108
109int maxparse(long long *y,const char *x)
110{
111 long long d;
112 long long j;
113
114 d = 0;
115 for (j = 0;j < 9 && x[j] >= '0' && x[j] <= '9';++j) d = d * 10 + (x[j] - '0');
116 if (x[j]) return 0;
117 if (d < 1) return 0;
118 if (d > 65535) return 0;
119 *y = d;
120 return 1;
121}
122
123/* cookies: */
124long long nextminute;
125unsigned char minutekey[32];
126unsigned char lastminutekey[32];
127
128/* routing to the server: */
129unsigned char serverip[4];
130unsigned char serverport[2];
131unsigned char serverextension[16];
132int udpfd = -1;
133
134/* server security: */
135char *keydir = 0;
136unsigned char servername[256];
137unsigned char serverlongtermsk[32];
138unsigned char servershorttermpk[32];
139unsigned char servershorttermsk[32];
140
141/* routing to the client: */
142unsigned char clientextension[16];
143
144/* client security: */
145unsigned char clientlongtermpk[32];
146unsigned char clientshorttermpk[32];
147
148/* shared secrets: */
149unsigned char clientshortserverlong[32];
150unsigned char clientshortservershort[32];
151unsigned char clientlongserverlong[32];
152
153unsigned char allzero[128] = {0};
154
155unsigned char nonce[24];
156unsigned char text[2048];
157
158unsigned char packetip[4];
159unsigned char packetport[2];
160unsigned char packet[4096];
161crypto_uint64 packetnonce;
162
163#define MESSAGELEN 1104
164
165struct activeclient {
166 unsigned char clientshorttermpk[32];
167 unsigned char clientshortservershort[32];
168 crypto_uint64 receivednonce;
169 crypto_uint64 sentnonce;
170 long long messagelen;
171 pid_t child;
172 int tochild;
173 int fromchild;
174 unsigned char clientextension[16];
175 unsigned char clientip[4];
176 unsigned char clientport[2];
177 unsigned char message[MESSAGELEN];
178} ;
179
180const char *strmaxactiveclients = "100";
181long long maxactiveclients = 0;
182long long numactiveclients = 0;
183struct activeclient *activeclients = 0;
184struct pollfd *p;
185
186int fdwd = -1;
187
188int pi0[2];
189int pi1[2];
190
191unsigned char childbuf[4096];
192long long childbuflen = 0;
193unsigned char childmessage[2048];
194long long childmessagelen = 0;
195
196int main(int argc,char **argv)
197{
198 long long r;
199 long long i;
200 long long k;
201
202 signal(SIGPIPE,SIG_IGN);
203 signal(SIGCHLD,SIG_IGN);
204
205 if (!argv[0]) die_usage(0);
206 for (;;) {
207 char *x;
208 if (!argv[1]) break;
209 if (argv[1][0] != '-') break;
210 x = *++argv;
211 if (x[0] == '-' && x[1] == 0) break;
212 if (x[0] == '-' && x[1] == '-' && x[2] == 0) break;
213 while (*++x) {
214 if (*x == 'q') { flagverbose = 0; continue; }
215 if (*x == 'Q') { flagverbose = 1; continue; }
216 if (*x == 'v') { if (flagverbose == 2) flagverbose = 3; else flagverbose = 2; continue; }
217 if (*x == 'c') {
218 if (x[1]) { strmaxactiveclients = x + 1; break; }
219 if (argv[1]) { strmaxactiveclients = *++argv; break; }
220 }
221 die_usage(0);
222 }
223 }
224 if (!maxparse(&maxactiveclients,strmaxactiveclients)) die_usage("concurrency must be between 1 and 65535");
225 if (!nameparse(servername,*++argv)) die_usage("sname must be at most 255 bytes, at most 63 bytes between dots");
226 keydir = *++argv; if (!keydir) die_usage("missing keydir");
227 if (!ipparse(serverip,*++argv)) die_usage("ip must be an IPv4 address");
228 if (!portparse(serverport,*++argv)) die_usage("port must be an integer between 0 and 65535");
229 if (!hexparse(serverextension,16,*++argv)) die_usage("ext must be exactly 32 hex characters");
230 if (!*++argv) die_usage("missing prog");
231
232 for (;;) {
233 r = open_read("/dev/null");
234 if (r == -1) die_fatal("unable to open /dev/null",0,0);
235 if (r > 9) { close(r); break; }
236 }
237
238 activeclients = malloc(maxactiveclients * sizeof(struct activeclient));
239 if (!activeclients) die_fatal("unable to create activeclients array",0,0);
240 randombytes((void *) activeclients,maxactiveclients * sizeof(struct activeclient));
241 for (i = 0;i < maxactiveclients;++i) {
242 activeclients[i].child = -1;
243 activeclients[i].tochild = -1;
244 activeclients[i].fromchild = -1;
245 activeclients[i].receivednonce = 0;
246 activeclients[i].sentnonce = randommod(281474976710656LL);
247 }
248
249 p = malloc((1 + maxactiveclients) * sizeof(struct pollfd));
250 if (!p) die_fatal("unable to create poll array",0,0);
251
252 fdwd = open_cwd();
253 if (fdwd == -1) die_fatal("unable to open current directory",0,0);
254
255 if (chdir(keydir) == -1) die_fatal("unable to chdir to",keydir,0);
256 if (load(".expertsonly/secretkey",serverlongtermsk,sizeof serverlongtermsk) == -1) die_fatal("unable to read secret key from",keydir,0);
257
258 udpfd = socket_udp();
259 if (udpfd == -1) die_fatal("unable to create socket",0,0);
260 if (socket_bind(udpfd,serverip,serverport) == -1) die_fatal("unable to bind socket",0,0);
261
262 randombytes(minutekey,sizeof minutekey);
263 randombytes(lastminutekey,sizeof lastminutekey);
264 nextminute = nanoseconds() + 60000000000ULL;
265
266 for (;;) {
267 long long timeout = nextminute - nanoseconds();
268 if (timeout <= 0) {
269 timeout = 60000000000ULL;
270 byte_copy(lastminutekey,sizeof lastminutekey,minutekey);
271 randombytes(minutekey,sizeof minutekey);
272 nextminute = nanoseconds() + timeout;
273 randombytes(packet,sizeof packet);
274 randombytes(packetip,sizeof packetip);
275 randombytes(packetport,sizeof packetport);
276 randombytes(clientshorttermpk,sizeof clientshorttermpk);
277 randombytes(clientshortserverlong,sizeof clientshortserverlong);
278 randombytes(nonce,sizeof nonce);
279 randombytes(text,sizeof text);
280 randombytes(childbuf,sizeof childbuf);
281 randombytes(childmessage,sizeof childmessage);
282 randombytes(servershorttermpk,sizeof servershorttermpk);
283 randombytes(servershorttermsk,sizeof servershorttermsk);
284 }
285
286 for (i = 0;i < numactiveclients;++i) {
287 p[i].fd = activeclients[i].fromchild;
288 p[i].events = POLLIN;
289 }
290 p[numactiveclients].fd = udpfd;
291 p[numactiveclients].events = POLLIN;
292 if (poll(p,1 + numactiveclients,timeout / 1000000 + 1) < 0) continue;
293
294 do { /* try receiving a packet: */
295 if (!p[numactiveclients].revents) break;
296 r = socket_recv(udpfd,packet,sizeof packet,packetip,packetport);
297 if (r < 80) break;
298 if (r > 1184) break;
299 if (r & 15) break;
300 if (!(byte_isequal(packet,7,"QvnQ5Xl") & byte_isequal(packet + 8,16,serverextension))) break;
301 byte_copy(clientextension,16,packet + 24);
302 if (packet[7] == 'H') { /* Hello packet: */
303 if (r != 224) break;
304 byte_copy(clientshorttermpk,32,packet + 40);
305 crypto_box_beforenm(clientshortserverlong,clientshorttermpk,serverlongtermsk);
306 byte_copy(nonce,16,"CurveCP-client-H");
307 byte_copy(nonce + 16,8,packet + 136);
308 byte_zero(text,16);
309 byte_copy(text + 16,80,packet + 144);
310 if (crypto_box_open_afternm(text,text,96,nonce,clientshortserverlong)) break;
311
312 /* send Cookie packet: */
313
314 crypto_box_keypair(servershorttermpk,servershorttermsk);
315 byte_zero(text + 64,32);
316 byte_copy(text + 96,32,clientshorttermpk);
317 byte_copy(text + 128,32,servershorttermsk);
318 byte_copy(nonce,8,"minute-k");
319 if (safenonce(nonce + 8,1) == -1) die_fatal("nonce-generation disaster",0,0);
320 crypto_secretbox(text + 64,text + 64,96,nonce,minutekey);
321 byte_copy(text + 64,16,nonce + 8);
322
323 byte_zero(text,32);
324 byte_copy(text + 32,32,servershorttermpk);
325 byte_copy(nonce,8,"CurveCPK"); /* reusing the other 16 bytes */
326 crypto_box_afternm(text,text,160,nonce,clientshortserverlong);
327
328 byte_copy(packet,8,"RL3aNMXK");
329 byte_copy(packet + 8,16,clientextension);
330 byte_copy(packet + 24,16,serverextension);
331 byte_copy(packet + 40,16,nonce + 8);
332 byte_copy(packet + 56,144,text + 16);
333
334 socket_send(udpfd,packet,200,packetip,packetport);
335 }
336 if (packet[7] == 'I') { /* Initiate packet: */
337 if (r < 560) break;
338 for (i = 0;i < numactiveclients;++i) /* XXX use better data structure */
339 if (byte_isequal(activeclients[i].clientshorttermpk,32,packet + 40))
340 break;
341 if (i < numactiveclients) {
342 packetnonce = uint64_unpack(packet + 168);
343 if (packetnonce <= activeclients[i].receivednonce) break;
344 byte_copy(nonce,16,"CurveCP-client-I");
345 byte_copy(nonce + 16,8,packet + 168);
346 byte_zero(text,16);
347 byte_copy(text + 16,r - 176,packet + 176);
348 if (crypto_box_open_afternm(text,text,r - 160,nonce,activeclients[i].clientshortservershort)) break;
349
350 /* XXX: update clientip, clientextension; but not if client has spoken recently */
351 activeclients[i].receivednonce = packetnonce;
352 text[383] = (r - 544) >> 4;
353 if (writeall(activeclients[i].tochild,text + 383,r - 543) == -1)
354 ; /* child is gone; will see eof later */
355 break;
356 }
357 if (i == maxactiveclients) break;
358
359 byte_copy(nonce,8,"minute-k");
360 byte_copy(nonce + 8,16,packet + 72);
361 byte_zero(text,16);
362 byte_copy(text + 16,80,packet + 88);
363 if (crypto_secretbox_open(text,text,96,nonce,minutekey)) {
364 byte_zero(text,16);
365 byte_copy(text + 16,80,packet + 88);
366 if (crypto_secretbox_open(text,text,96,nonce,lastminutekey)) break;
367 }
368 if (!byte_isequal(packet + 40,32,text + 32)) break;
369 byte_copy(servershorttermsk,32,text + 64);
370 byte_copy(clientshorttermpk,32,packet + 40);
371 crypto_box_beforenm(clientshortservershort,clientshorttermpk,servershorttermsk);
372
373 byte_copy(nonce,16,"CurveCP-client-I");
374 byte_copy(nonce + 16,8,packet + 168);
375 byte_zero(text,16);
376 byte_copy(text + 16,r - 176,packet + 176);
377 if (crypto_box_open_afternm(text,text,r - 160,nonce,clientshortservershort)) break;
378
379 if (!byte_isequal(text + 128,256,servername)) break;
380
381 /* XXX skip if client authentication is not desired: */
382 byte_copy(clientlongtermpk,32,text + 32);
383 /* XXX impose policy limitations on clients: known, maxconn */
384 /* XXX for known clients, retrieve shared secret from cache: */
385 crypto_box_beforenm(clientlongserverlong,clientlongtermpk,serverlongtermsk);
386 byte_copy(nonce,8,"CurveCPV");
387 byte_copy(nonce + 8,16,text + 64);
388 byte_zero(text + 64,16);
389 if (crypto_box_open_afternm(text + 64,text + 64,64,nonce,clientlongserverlong)) break;
390 if (!byte_isequal(text + 96,32,clientshorttermpk)) break;
391
392 if (open_pipe(pi0) == -1) break; /* XXX: error message */
393 if (open_pipe(pi1) == -1) { close(pi0[0]); close(pi0[1]); break; } /* XXX: error message */
394
395 activeclients[i].child = fork();
396 if (activeclients[i].child == -1) {
397 close(pi0[0]); close(pi0[1]);
398 close(pi1[0]); close(pi1[1]);
399 break; /* XXX: error message */
400 }
401 if (activeclients[i].child == 0) {
402 if (fchdir(fdwd) == -1) die_fatal("unable to chdir to original directory",0,0);
403 close(8);
404 if (dup(pi0[0]) != 8) die_fatal("unable to dup",0,0);
405 close(9);
406 if (dup(pi1[1]) != 9) die_fatal("unable to dup",0,0);
407 /* XXX: set up environment variables */
408 signal(SIGPIPE,SIG_DFL);
409 signal(SIGCHLD,SIG_DFL);
410 execvp(*argv,argv);
411 die_fatal("unable to run",*argv,0);
412 }
413
414 activeclients[i].tochild = pi0[1]; close(pi0[0]);
415 activeclients[i].fromchild = pi1[0]; close(pi1[1]);
416 activeclients[i].messagelen = 0;
417 byte_copy(activeclients[i].clientshorttermpk,32,clientshorttermpk);
418 byte_copy(activeclients[i].clientshortservershort,32,clientshortservershort);
419 activeclients[i].receivednonce = uint64_unpack(packet + 168);
420 byte_copy(activeclients[i].clientextension,16,clientextension);
421 byte_copy(activeclients[i].clientip,4,packetip);
422 byte_copy(activeclients[i].clientport,2,packetport);
423 ++numactiveclients;
424
425 text[383] = (r - 544) >> 4;
426 if (writeall(activeclients[i].tochild,text + 383,r - 543) == -1)
427 ; /* child is gone; will see eof later */
428 }
429 if (packet[7] == 'M') { /* Message packet: */
430 if (r < 112) break;
431 for (i = 0;i < numactiveclients;++i) /* XXX use better data structure */
432 if (byte_isequal(activeclients[i].clientshorttermpk,32,packet + 40))
433 break;
434 if (i < numactiveclients) {
435 packetnonce = uint64_unpack(packet + 72);
436 if (packetnonce <= activeclients[i].receivednonce) break;
437 byte_copy(nonce,16,"CurveCP-client-M");
438 byte_copy(nonce + 16,8,packet + 72);
439 byte_zero(text,16);
440 byte_copy(text + 16,r - 80,packet + 80);
441 if (crypto_box_open_afternm(text,text,r - 64,nonce,activeclients[i].clientshortservershort)) break;
442
443 /* XXX: update clientip, clientextension */
444 activeclients[i].receivednonce = packetnonce;
445 text[31] = (r - 96) >> 4;
446 if (writeall(activeclients[i].tochild,text + 31,r - 95) == -1)
447 ; /* child is gone; will see eof later */
448 break;
449 }
450 }
451 } while (0);
452
453 for (i = numactiveclients - 1;i >= 0;--i) {
454 do {
455 if (!p[i].revents) break;
456 r = read(activeclients[i].fromchild,childbuf,sizeof childbuf);
457 if (r == -1) if (errno == EINTR || errno == EWOULDBLOCK || errno == EAGAIN) break;
458 if (r <= 0) goto endconnection;
459 childbuflen = r;
460 for (k = 0;k < childbuflen;++k) {
461 r = activeclients[i].messagelen;
462 if (r < 0) goto endconnection;
463 if (r >= MESSAGELEN) goto endconnection;
464 activeclients[i].message[r] = childbuf[k];
465 if (r == 0) if (childbuf[k] & 128) goto endconnection;
466 activeclients[i].messagelen = r + 1;
467 if (r == 16 * (unsigned long long) activeclients[i].message[0]) {
468 if (r < 16) goto endconnection;
469 if (r > 1088) goto endconnection;
470 byte_copy(nonce,16,"CurveCP-server-M");
471 uint64_pack(nonce + 16,++activeclients[i].sentnonce);
472 byte_zero(text,32);
473 byte_copy(text + 32,r,activeclients[i].message + 1);
474 crypto_box_afternm(text,text,r + 32,nonce,activeclients[i].clientshortservershort);
475 byte_copy(packet,8,"RL3aNMXM");
476 byte_copy(packet + 8,16,clientextension);
477 byte_copy(packet + 24,16,serverextension);
478 byte_copy(packet + 40,8,nonce + 16);
479 byte_copy(packet + 48,r + 16,text + 16);
480 socket_send(udpfd,packet,r + 64,activeclients[i].clientip,activeclients[i].clientport);
481 activeclients[i].messagelen = 0;
482 }
483 }
484 break;
485
486 endconnection:
487
488 /* XXX: cache cookie if it's recent */
489 close(activeclients[i].fromchild); activeclients[i].fromchild = -1;
490 close(activeclients[i].tochild); activeclients[i].tochild = -1;
491 --numactiveclients;
492 activeclients[i] = activeclients[numactiveclients];
493 randombytes((void *) &activeclients[numactiveclients],sizeof(struct activeclient));
494 } while (0);
495 }
496 }
497}