1 #include <signal.h>
2 #include <sys/types.h>
3 #include <sys/stat.h>
4 #include <sys/wait.h>
5 #include <fcntl.h>
6 #include <poll.h>
7 #include <unistd.h>
8 #include "e.h"
9 #include "die.h"
10 #include "load.h"
11 #include "open.h"
12 #include "byte.h"
13 #include "socket.h"
14 #include "uint64_pack.h"
15 #include "uint64_unpack.h"
16 #include "nanoseconds.h"
17 #include "hexparse.h"
18 #include "nameparse.h"
19 #include "portparse.h"
20 #include "writeall.h"
21 #include "safenonce.h"
22 #include "randommod.h"
23 
24 long long recent = 0;
25 
26 #define NUMIP 8
27 long long hellowait[NUMIP] = {
28    1000000000
29 ,  1500000000
30 ,  2250000000
31 ,  3375000000
32 ,  5062500000
33 ,  7593750000
34 , 11390625000
35 , 17085937500
36 } ;
37 
38 #include "crypto_box.h"
39 #include "randombytes.h"
40 #if crypto_box_PUBLICKEYBYTES != 32
41 error!
42 #endif
43 #if crypto_box_NONCEBYTES != 24
44 error!
45 #endif
46 #if crypto_box_BOXZEROBYTES != 16
47 error!
48 #endif
49 #if crypto_box_ZEROBYTES != 32
50 error!
51 #endif
52 #if crypto_box_BEFORENMBYTES != 32
53 error!
54 #endif
55 
56 int flagverbose = 1;
57 
58 #define USAGE "\
59 curvecpclient: how to use:\n\
60 curvecpclient:   -q (optional): no error messages\n\
61 curvecpclient:   -Q (optional): print error messages (default)\n\
62 curvecpclient:   -v (optional): print extra information\n\
63 curvecpclient:   -c keydir (optional): use this public-key directory\n\
64 curvecpclient:   sname: server's name\n\
65 curvecpclient:   pk: server's public key\n\
66 curvecpclient:   ip: server's IP address\n\
67 curvecpclient:   port: server's UDP port\n\
68 curvecpclient:   ext: server's extension\n\
69 curvecpclient:   prog: run this client\n\
70 "
71 
die_usage(const char * s)72 void die_usage(const char *s)
73 {
74   if (s) die_4(100,USAGE,"curvecpclient: fatal: ",s,"\n");
75   die_1(100,USAGE);
76 }
77 
die_fatal(const char * trouble,const char * d,const char * fn)78 void die_fatal(const char *trouble,const char *d,const char *fn)
79 {
80   /* XXX: clean up? OS can do it much more reliably */
81   if (!flagverbose) die_0(111);
82   if (d) {
83     if (fn) die_9(111,"curvecpclient: fatal: ",trouble," ",d,"/",fn,": ",e_str(errno),"\n");
84     die_7(111,"curvecpclient: fatal: ",trouble," ",d,": ",e_str(errno),"\n");
85   }
86   if (errno) die_5(111,"curvecpclient: fatal: ",trouble,": ",e_str(errno),"\n");
87   die_3(111,"curvecpclient: fatal: ",trouble,"\n");
88 }
89 
multiipparse(unsigned char * y,const char * x)90 int multiipparse(unsigned char *y,const char *x)
91 {
92   long long pos;
93   long long pos2;
94   long long ynum;
95   long long ypos;
96   long long j;
97   long long k;
98   long long d;
99   for (j = 0;j < 4 * NUMIP;++j) y[j] = 0;
100   ynum = 0;
101   while (ynum < 1000) {
102     ++ynum;
103     ypos = randommod(ynum);
104     for (k = 0;k < 4;++k) {
105       pos = ypos * 4 + k;
106       pos2 = (ynum - 1) * 4 + k;
107       if (pos >= 0 && pos < 4 * NUMIP && pos2 >= 0 && pos2 < 4 * NUMIP) y[pos2] = y[pos];
108       d = 0;
109       for (j = 0;j < 3 && x[j] >= '0' && x[j] <= '9';++j) d = d * 10 + (x[j] - '0');
110       if (j == 0) return 0;
111       x += j;
112       if (pos >= 0 && pos < 4 * NUMIP) y[pos] = d;
113       if (k < 3) {
114         if (*x != '.') return 0;
115         ++x;
116       }
117     }
118     if (!*x) break;
119     if (*x != ',') return 0;
120     ++x;
121   }
122   /* if fewer than 8 IP addresses, cycle through them: */
123   pos = 0;
124   pos2 = ynum * 4;
125   while (pos2 < 4 * NUMIP) {
126     if (pos >= 0 && pos < 4 * NUMIP && pos2 >= 0 && pos2 < 4 * NUMIP) y[pos2] = y[pos];
127     ++pos2;
128     ++pos;
129   }
130   return 1;
131 }
132 
133 
134 /* routing to the client: */
135 unsigned char clientextension[16];
136 long long clientextensionloadtime = 0;
137 int udpfd = -1;
138 
clientextension_init(void)139 void clientextension_init(void)
140 {
141   if (recent >= clientextensionloadtime) {
142     clientextensionloadtime = recent + 30000000000LL;
143     if (load("/etc/curvecpextension",clientextension,16) == -1)
144       if (errno == ENOENT || errno == ENAMETOOLONG)
145         byte_zero(clientextension,16);
146   }
147 }
148 
149 
150 /* client security: */
151 char *keydir = 0;
152 unsigned char clientlongtermpk[32];
153 unsigned char clientlongtermsk[32];
154 unsigned char clientshorttermpk[32];
155 unsigned char clientshorttermsk[32];
156 crypto_uint64 clientshorttermnonce;
157 unsigned char vouch[64];
158 
clientshorttermnonce_update(void)159 void clientshorttermnonce_update(void)
160 {
161   ++clientshorttermnonce;
162   if (clientshorttermnonce) return;
163   errno = EPROTO;
164   die_fatal("nonce space expired",0,0);
165 }
166 
167 /* routing to the server: */
168 unsigned char serverip[4 * NUMIP];
169 unsigned char serverport[2];
170 unsigned char serverextension[16];
171 
172 /* server security: */
173 unsigned char servername[256];
174 unsigned char serverlongtermpk[32];
175 unsigned char servershorttermpk[32];
176 unsigned char servercookie[96];
177 
178 /* shared secrets: */
179 unsigned char clientshortserverlong[32];
180 unsigned char clientshortservershort[32];
181 unsigned char clientlongserverlong[32];
182 
183 unsigned char allzero[128] = {0};
184 
185 unsigned char nonce[24];
186 unsigned char text[2048];
187 
188 unsigned char packet[4096];
189 unsigned char packetip[4];
190 unsigned char packetport[2];
191 crypto_uint64 packetnonce;
192 int flagreceivedmessage = 0;
193 crypto_uint64 receivednonce = 0;
194 
195 struct pollfd p[3];
196 
197 int fdwd = -1;
198 
199 int tochild[2] = {-1,-1};
200 int fromchild[2] = {-1,-1};
201 pid_t child = -1;
202 int childstatus = 0;
203 
204 unsigned char childbuf[4096];
205 long long childbuflen = 0;
206 unsigned char childmessage[2048];
207 long long childmessagelen = 0;
208 
main(int argc,char ** argv)209 int main(int argc,char **argv)
210 {
211   long long hellopackets;
212   long long r;
213   long long nextaction;
214 
215   signal(SIGPIPE,SIG_IGN);
216 
217   if (!argv[0]) die_usage(0);
218   for (;;) {
219     char *x;
220     if (!argv[1]) break;
221     if (argv[1][0] != '-') break;
222     x = *++argv;
223     if (x[0] == '-' && x[1] == 0) break;
224     if (x[0] == '-' && x[1] == '-' && x[2] == 0) break;
225     while (*++x) {
226       if (*x == 'q') { flagverbose = 0; continue; }
227       if (*x == 'Q') { flagverbose = 1; continue; }
228       if (*x == 'v') { if (flagverbose == 2) flagverbose = 3; else flagverbose = 2; continue; }
229       if (*x == 'c') {
230         if (x[1]) { keydir = x + 1; break; }
231         if (argv[1]) { keydir = *++argv; break; }
232       }
233       die_usage(0);
234     }
235   }
236   if (!nameparse(servername,*++argv)) die_usage("sname must be at most 255 bytes, at most 63 bytes between dots");
237   if (!hexparse(serverlongtermpk,32,*++argv)) die_usage("pk must be exactly 64 hex characters");
238   if (!multiipparse(serverip,*++argv)) die_usage("ip must be a comma-separated series of IPv4 addresses");
239   if (!portparse(serverport,*++argv)) die_usage("port must be an integer between 0 and 65535");
240   if (!hexparse(serverextension,16,*++argv)) die_usage("ext must be exactly 32 hex characters");
241   if (!*++argv) die_usage("missing prog");
242 
243   for (;;) {
244     r = open_read("/dev/null");
245     if (r == -1) die_fatal("unable to open /dev/null",0,0);
246     if (r > 9) { close(r); break; }
247   }
248 
249   if (keydir) {
250     fdwd = open_cwd();
251     if (fdwd == -1) die_fatal("unable to open current working directory",0,0);
252     if (chdir(keydir) == -1) die_fatal("unable to change to directory",keydir,0);
253     if (load("publickey",clientlongtermpk,sizeof clientlongtermpk) == -1) die_fatal("unable to read public key from",keydir,0);
254     if (load(".expertsonly/secretkey",clientlongtermsk,sizeof clientlongtermsk) == -1) die_fatal("unable to read secret key from",keydir,0);
255   } else {
256     crypto_box_keypair(clientlongtermpk,clientlongtermsk);
257   }
258 
259   crypto_box_keypair(clientshorttermpk,clientshorttermsk);
260   clientshorttermnonce = randommod(281474976710656LL);
261   crypto_box_beforenm(clientshortserverlong,serverlongtermpk,clientshorttermsk);
262   crypto_box_beforenm(clientlongserverlong,serverlongtermpk,clientlongtermsk);
263 
264   udpfd = socket_udp();
265   if (udpfd == -1) die_fatal("unable to create socket",0,0);
266 
267   for (hellopackets = 0;hellopackets < NUMIP;++hellopackets) {
268     recent = nanoseconds();
269 
270     /* send a Hello packet: */
271 
272     clientextension_init();
273 
274     clientshorttermnonce_update();
275     byte_copy(nonce,16,"CurveCP-client-H");
276     uint64_pack(nonce + 16,clientshorttermnonce);
277 
278     byte_copy(packet,8,"QvnQ5XlH");
279     byte_copy(packet + 8,16,serverextension);
280     byte_copy(packet + 24,16,clientextension);
281     byte_copy(packet + 40,32,clientshorttermpk);
282     byte_copy(packet + 72,64,allzero);
283     byte_copy(packet + 136,8,nonce + 16);
284     crypto_box_afternm(text,allzero,96,nonce,clientshortserverlong);
285     byte_copy(packet + 144,80,text + 16);
286 
287     socket_send(udpfd,packet,224,serverip + 4 * hellopackets,serverport);
288 
289     nextaction = recent + hellowait[hellopackets] + randommod(hellowait[hellopackets]);
290 
291     for (;;) {
292       long long timeout = nextaction - recent;
293       if (timeout <= 0) break;
294       p[0].fd = udpfd;
295       p[0].events = POLLIN;
296       if (poll(p,1,timeout / 1000000 + 1) < 0) p[0].revents = 0;
297 
298       do { /* try receiving a Cookie packet: */
299         if (!p[0].revents) break;
300         r = socket_recv(udpfd,packet,sizeof packet,packetip,packetport);
301         if (r != 200) break;
302         if (!(byte_isequal(packetip,4,serverip + 4 * hellopackets) &
303               byte_isequal(packetport,2,serverport) &
304               byte_isequal(packet,8,"RL3aNMXK") &
305               byte_isequal(packet + 8,16,clientextension) &
306               byte_isequal(packet + 24,16,serverextension)
307            )) break;
308         byte_copy(nonce,8,"CurveCPK");
309         byte_copy(nonce + 8,16,packet + 40);
310         byte_zero(text,16);
311         byte_copy(text + 16,144,packet + 56);
312         if (crypto_box_open_afternm(text,text,160,nonce,clientshortserverlong)) break;
313         byte_copy(servershorttermpk,32,text + 32);
314         byte_copy(servercookie,96,text + 64);
315         byte_copy(serverip,4,serverip + 4 * hellopackets);
316         goto receivedcookie;
317       } while (0);
318 
319       recent = nanoseconds();
320     }
321   }
322 
323   errno = ETIMEDOUT; die_fatal("no response from server",0,0);
324 
325   receivedcookie:
326 
327   crypto_box_beforenm(clientshortservershort,servershorttermpk,clientshorttermsk);
328 
329   byte_copy(nonce,8,"CurveCPV");
330   if (keydir) {
331     if (safenonce(nonce + 8,0) == -1) die_fatal("nonce-generation disaster",0,0);
332   } else {
333     randombytes(nonce + 8,16);
334   }
335 
336   byte_zero(text,32);
337   byte_copy(text + 32,32,clientshorttermpk);
338   crypto_box_afternm(text,text,64,nonce,clientlongserverlong);
339   byte_copy(vouch,16,nonce + 8);
340   byte_copy(vouch + 16,48,text + 16);
341 
342   /* server is responding, so start child: */
343 
344   if (open_pipe(tochild) == -1) die_fatal("unable to create pipe",0,0);
345   if (open_pipe(fromchild) == -1) die_fatal("unable to create pipe",0,0);
346 
347   child = fork();
348   if (child == -1) die_fatal("unable to fork",0,0);
349   if (child == 0) {
350     if (keydir) if (fchdir(fdwd) == -1) die_fatal("unable to chdir to original directory",0,0);
351     close(8);
352     if (dup(tochild[0]) != 8) die_fatal("unable to dup",0,0);
353     close(9);
354     if (dup(fromchild[1]) != 9) die_fatal("unable to dup",0,0);
355     /* XXX: set up environment variables */
356     signal(SIGPIPE,SIG_DFL);
357     execvp(*argv,argv);
358     die_fatal("unable to run",*argv,0);
359   }
360 
361   close(fromchild[1]);
362   close(tochild[0]);
363 
364 
365   for (;;) {
366     p[0].fd = udpfd;
367     p[0].events = POLLIN;
368     p[1].fd = fromchild[0];
369     p[1].events = POLLIN;
370 
371     if (poll(p,2,-1) < 0) {
372       p[0].revents = 0;
373       p[1].revents = 0;
374     }
375 
376     do { /* try receiving a Message packet: */
377       if (!p[0].revents) break;
378       r = socket_recv(udpfd,packet,sizeof packet,packetip,packetport);
379       if (r < 80) break;
380       if (r > 1152) break;
381       if (r & 15) break;
382       packetnonce = uint64_unpack(packet + 40);
383       if (flagreceivedmessage && packetnonce <= receivednonce) break;
384       if (!(byte_isequal(packetip,4,serverip + 4 * hellopackets) &
385             byte_isequal(packetport,2,serverport) &
386             byte_isequal(packet,8,"RL3aNMXM") &
387             byte_isequal(packet + 8,16,clientextension) &
388             byte_isequal(packet + 24,16,serverextension)
389          )) break;
390       byte_copy(nonce,16,"CurveCP-server-M");
391       byte_copy(nonce + 16,8,packet + 40);
392       byte_zero(text,16);
393       byte_copy(text + 16,r - 48,packet + 48);
394       if (crypto_box_open_afternm(text,text,r - 32,nonce,clientshortservershort)) break;
395 
396       if (!flagreceivedmessage) {
397         flagreceivedmessage = 1;
398 	randombytes(clientlongtermpk,sizeof clientlongtermpk);
399 	randombytes(vouch,sizeof vouch);
400 	randombytes(servername,sizeof servername);
401 	randombytes(servercookie,sizeof servercookie);
402       }
403 
404       receivednonce = packetnonce;
405       text[31] = (r - 64) >> 4;
406       /* child is responsible for reading all data immediately, so we won't block: */
407       if (writeall(tochild[1],text + 31,r - 63) == -1) goto done;
408     } while (0);
409 
410     do { /* try receiving data from child: */
411       long long i;
412       if (!p[1].revents) break;
413       r = read(fromchild[0],childbuf,sizeof childbuf);
414       if (r == -1) if (errno == EINTR || errno == EWOULDBLOCK || errno == EAGAIN) break;
415       if (r <= 0) goto done;
416       childbuflen = r;
417       for (i = 0;i < childbuflen;++i) {
418 	if (childmessagelen < 0) goto done;
419 	if (childmessagelen >= sizeof childmessage) goto done;
420         childmessage[childmessagelen++] = childbuf[i];
421 	if (childmessage[0] & 128) goto done;
422 	if (childmessagelen == 1 + 16 * (unsigned long long) childmessage[0]) {
423 	  clientextension_init();
424 	  clientshorttermnonce_update();
425           uint64_pack(nonce + 16,clientshorttermnonce);
426 	  if (flagreceivedmessage) {
427 	    r = childmessagelen - 1;
428 	    if (r < 16) goto done;
429 	    if (r > 1088) goto done;
430             byte_copy(nonce,16,"CurveCP-client-M");
431 	    byte_zero(text,32);
432 	    byte_copy(text + 32,r,childmessage + 1);
433 	    crypto_box_afternm(text,text,r + 32,nonce,clientshortservershort);
434 	    byte_copy(packet,8,"QvnQ5XlM");
435 	    byte_copy(packet + 8,16,serverextension);
436 	    byte_copy(packet + 24,16,clientextension);
437 	    byte_copy(packet + 40,32,clientshorttermpk);
438 	    byte_copy(packet + 72,8,nonce + 16);
439 	    byte_copy(packet + 80,r + 16,text + 16);
440             socket_send(udpfd,packet,r + 96,serverip,serverport);
441 	  } else {
442 	    r = childmessagelen - 1;
443 	    if (r < 16) goto done;
444 	    if (r > 640) goto done;
445 	    byte_copy(nonce,16,"CurveCP-client-I");
446 	    byte_zero(text,32);
447 	    byte_copy(text + 32,32,clientlongtermpk);
448 	    byte_copy(text + 64,64,vouch);
449 	    byte_copy(text + 128,256,servername);
450 	    byte_copy(text + 384,r,childmessage + 1);
451 	    crypto_box_afternm(text,text,r + 384,nonce,clientshortservershort);
452 	    byte_copy(packet,8,"QvnQ5XlI");
453 	    byte_copy(packet + 8,16,serverextension);
454 	    byte_copy(packet + 24,16,clientextension);
455 	    byte_copy(packet + 40,32,clientshorttermpk);
456 	    byte_copy(packet + 72,96,servercookie);
457 	    byte_copy(packet + 168,8,nonce + 16);
458 	    byte_copy(packet + 176,r + 368,text + 16);
459             socket_send(udpfd,packet,r + 544,serverip,serverport);
460 	  }
461 	  childmessagelen = 0;
462 	}
463       }
464     } while (0);
465   }
466 
467 
468   done:
469 
470   do {
471     r = waitpid(child,&childstatus,0);
472   } while (r == -1 && errno == EINTR);
473 
474   if (!WIFEXITED(childstatus)) { errno = 0; die_fatal("process killed by signal",0,0); }
475   return WEXITSTATUS(childstatus);
476 }
477