git ssb

8+

cel / sbotc



Tree: fd953a1e72b4b16e6e5a74bcf2f893dbf1407ce4

Files: fd953a1e72b4b16e6e5a74bcf2f893dbf1407ce4 / sbotc.c

28524 bytesRaw
1/*
2 * sbotc.c
3 * Copyright (c) 2017 Secure Scuttlebutt Consortium
4 *
5 * Usage of the works is permitted provided that this instrument is
6 * retained with the works, so that any entity that uses the works is
7 * notified of this instrument.
8 *
9 * DISCLAIMER: THE WORKS ARE WITHOUT WARRANTY.
10 */
11
12#include <ctype.h>
13#include <err.h>
14#include <errno.h>
15#include <fcntl.h>
16#include <limits.h>
17#include <netdb.h>
18#include <netinet/in.h>
19#include <stdarg.h>
20#include <stdbool.h>
21#include <stdio.h>
22#include <stdlib.h>
23#include <string.h>
24#include <sys/select.h>
25#include <sys/socket.h>
26#include <sys/stat.h>
27#include <unistd.h>
28
29#include <sodium.h>
30
31#include "base64.h"
32#include "jsmn.h"
33
34#define BOXS_MAXLEN 4096
35
36#define write_buf(fd, buf) \
37 write_all(fd, buf, sizeof(buf)-1)
38
39struct boxs_header {
40 uint16_t len;
41 uint8_t mac[16];
42};
43
44struct boxs {
45 int s;
46 unsigned char encrypt_key[32];
47 unsigned char decrypt_key[32];
48 unsigned char nonce1[24];
49 unsigned char nonce2[24];
50 unsigned char rx_nonce[24];
51 unsigned char rx_buf[BOXS_MAXLEN];
52 size_t rx_buf_pos;
53 size_t rx_buf_len;
54};
55
56enum pkt_type {
57 pkt_type_buffer = 0,
58 pkt_type_string = 1,
59 pkt_type_json = 2,
60};
61
62enum pkt_flags {
63 pkt_flags_buffer = 0,
64 pkt_flags_string = 1,
65 pkt_flags_json = 2,
66 pkt_flags_end = 4,
67 pkt_flags_stream = 8,
68};
69
70struct pkt_header {
71 uint32_t len;
72 int32_t req;
73} __attribute__((packed));
74
75enum muxrpc_type {
76 muxrpc_type_async,
77 muxrpc_type_source,
78 muxrpc_type_sink,
79 muxrpc_type_duplex,
80};
81
82enum stream_state {
83 stream_state_open,
84 stream_state_ended_ok,
85 stream_state_ended_error,
86};
87
88static unsigned char zeros[24] = {0};
89
90static const unsigned char ssb_cap[] = {
91 0xd4, 0xa1, 0xcb, 0x88, 0xa6, 0x6f, 0x02, 0xf8,
92 0xdb, 0x63, 0x5c, 0xe2, 0x64, 0x41, 0xcc, 0x5d,
93 0xac, 0x1b, 0x08, 0x42, 0x0c, 0xea, 0xac, 0x23,
94 0x08, 0x39, 0xb7, 0x55, 0x84, 0x5a, 0x9f, 0xfb
95};
96
97static void usage() {
98 fputs("usage: sbotc [-j] [-T] [-a <cap>] [-s <host>] [-p <port>] [-k <key>] [-K <keypair_seed>] \n"
99 " [-t <type>] <method> [<argument>...]\n", stderr);
100 exit(EXIT_FAILURE);
101}
102
103static int tcp_connect(const char *host, const char *port) {
104 struct addrinfo hints;
105 struct addrinfo *result, *rp;
106 int s;
107 int fd;
108 int err;
109
110 memset(&hints, 0, sizeof(hints));
111 hints.ai_family = AF_UNSPEC;
112 hints.ai_protocol = IPPROTO_TCP;
113
114 s = getaddrinfo(host, port, &hints, &result);
115 if (s < 0) errx(1, "unable to resolve host: %s", gai_strerror(s));
116
117 for (rp = result; rp; rp = rp->ai_next) {
118 fd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
119 if (fd < 0) continue;
120 if (connect(fd, rp->ai_addr, rp->ai_addrlen) == 0) break;
121 err = errno;
122 close(fd);
123 errno = err;
124 }
125 if (rp == NULL) fd = -1;
126
127 freeaddrinfo(result);
128 return fd;
129}
130
131static int read_all(int fd, void *buf, size_t count) {
132 ssize_t nbytes;
133 while (count > 0) {
134 nbytes = read(fd, buf, count);
135 if (nbytes == 0) { errno = EPIPE; return -1; }
136 if (nbytes < 0 && errno == EINTR) continue;
137 if (nbytes < 0) return -1;
138 buf += nbytes;
139 count -= nbytes;
140 }
141 return 0;
142}
143
144static int write_all(int fd, const void *buf, size_t count) {
145 ssize_t nbytes;
146 while (count > 0) {
147 nbytes = write(fd, buf, count);
148 if (nbytes < 0 && errno == EINTR) continue;
149 if (nbytes < 0) return -1;
150 buf += nbytes;
151 count -= nbytes;
152 }
153 return 0;
154}
155
156static void shs_connect(int sfd, int infd, int outfd, const unsigned char pubkey[32], const unsigned char seckey[64], const unsigned char appkey[32], const unsigned char server_pubkey[32], struct boxs *bs) {
157 int rc;
158 unsigned char local_app_mac[32], remote_app_mac[32];
159
160 unsigned char kx_pk[32], kx_sk[32];
161 rc = crypto_box_keypair(kx_pk, kx_sk);
162 if (rc < 0) errx(1, "failed to generate auth keypair");
163
164 rc = crypto_auth(local_app_mac, kx_pk, 32, appkey);
165 if (rc < 0) err(1, "failed to generate app mac");
166
167 // send challenge
168 unsigned char buf[64];
169 memcpy(buf, local_app_mac, 32);
170 memcpy(buf+32, kx_pk, 32);
171 rc = write_all(outfd, buf, sizeof(buf));
172 if (rc < 0) err(1, "failed to send challenge");
173
174 // recv challenge
175 unsigned char remote_kx_pk[32];
176 rc = read_all(infd, buf, sizeof(buf));
177 if (rc < 0) err(1, "challenge not accepted");
178 memcpy(remote_app_mac, buf, 32);
179 memcpy(remote_kx_pk, buf+32, 32);
180 rc = crypto_auth_verify(buf, remote_kx_pk, 32, appkey);
181 if (rc < 0) errx(1, "wrong protocol (version?)");
182
183 // send auth
184
185 unsigned char secret[32];
186 rc = crypto_scalarmult(secret, kx_sk, remote_kx_pk);
187 if (rc < 0) errx(1, "failed to derive shared secret");
188
189 unsigned char remote_pk_curve[32];
190 rc = crypto_sign_ed25519_pk_to_curve25519(remote_pk_curve, server_pubkey);
191 if (rc < 0) errx(1, "failed to curvify remote public key");
192
193 unsigned char a_bob[32];
194 rc = crypto_scalarmult(a_bob, kx_sk, remote_pk_curve);
195 if (rc < 0) errx(1, "failed to derive a_bob");
196
197 unsigned char secret2a[96];
198 memcpy(secret2a, appkey, 32);
199 memcpy(secret2a+32, secret, 32);
200 memcpy(secret2a+64, a_bob, 32);
201
202 unsigned char secret2[32];
203 rc = crypto_hash_sha256(secret2, secret2a, sizeof(secret2a));
204 if (rc < 0) errx(1, "failed to hash secret2");
205
206 unsigned char shash[32];
207 rc = crypto_hash_sha256(shash, secret, sizeof(secret));
208 if (rc < 0) errx(1, "failed to hash secret");
209
210 unsigned char signed1[96];
211 memcpy(signed1, appkey, 32);
212 memcpy(signed1+32, server_pubkey, 32);
213 memcpy(signed1+64, shash, 32);
214
215 unsigned char sig[64];
216 rc = crypto_sign_detached(sig, NULL, signed1, sizeof(signed1), seckey);
217 if (rc < 0) errx(1, "failed to sign inner hello");
218
219 unsigned char hello[96];
220 memcpy(hello, sig, 64);
221 memcpy(hello+64, pubkey, 32);
222
223 unsigned char boxed_auth[112];
224 rc = crypto_secretbox_easy(boxed_auth, hello, sizeof(hello), zeros, secret2);
225 if (rc < 0) errx(1, "failed to box hello");
226
227 rc = write_all(outfd, boxed_auth, sizeof(boxed_auth));
228 if (rc < 0) errx(1, "failed to send auth");
229
230 // verify accept
231
232 unsigned char boxed_okay[80];
233 rc = read_all(infd, boxed_okay, sizeof(boxed_okay));
234 if (rc < 0) err(1, "hello not accepted");
235
236 unsigned char local_sk_curve[32];
237 rc = crypto_sign_ed25519_sk_to_curve25519(local_sk_curve, seckey);
238 if (rc < 0) errx(1, "failed to curvify local secret key");
239
240 unsigned char b_alice[32];
241 rc = crypto_scalarmult(b_alice, local_sk_curve, remote_kx_pk);
242 if (rc < 0) errx(1, "failed to derive b_alice");
243
244 unsigned char secret3a[128];
245 memcpy(secret3a, appkey, 32);
246 memcpy(secret3a+32, secret, 32);
247 memcpy(secret3a+64, a_bob, 32);
248 memcpy(secret3a+96, b_alice, 32);
249
250 unsigned char secret3[32];
251 rc = crypto_hash_sha256(secret3, secret3a, sizeof(secret3a));
252 if (rc < 0) errx(1, "failed to hash secret3");
253
254 rc = crypto_secretbox_open_easy(sig, boxed_okay, sizeof(boxed_okay), zeros, secret3);
255 if (rc < 0) errx(1, "failed to unbox the okay");
256
257 unsigned char signed2[160];
258 memcpy(signed2, appkey, 32);
259 memcpy(signed2+32, hello, 96);
260 memcpy(signed2+128, shash, 32);
261
262 rc = crypto_sign_verify_detached(sig, signed2, sizeof(signed2), server_pubkey);
263 if (rc < 0) errx(1, "server not authenticated");
264
265 rc = crypto_hash_sha256(secret, secret3, 32);
266 if (rc < 0) errx(1, "failed to hash secret3");
267
268 unsigned char enc_key_hashed[64];
269 memcpy(enc_key_hashed, secret, 32);
270 memcpy(enc_key_hashed+32, server_pubkey, 32);
271 rc = crypto_hash_sha256(bs->encrypt_key, enc_key_hashed, 64);
272 if (rc < 0) errx(1, "failed to hash the encrypt key");
273
274 unsigned char dec_key_hashed[64];
275 memcpy(dec_key_hashed, secret, 32);
276 memcpy(dec_key_hashed+32, pubkey, 32);
277 rc = crypto_hash_sha256(bs->decrypt_key, dec_key_hashed, 64);
278 if (rc < 0) errx(1, "failed to hash the decrypt key");
279
280 memcpy(bs->nonce1, remote_app_mac, 24);
281 memcpy(bs->nonce2, remote_app_mac, 24);
282 memcpy(bs->rx_nonce, local_app_mac, 24);
283
284 bs->rx_buf_pos = 0;
285 bs->rx_buf_len = 0;
286 bs->s = sfd;
287}
288
289static int pubkey_decode(const char *key_str, unsigned char key[32]) {
290 if (!key_str) { errno = EPROTO; return -1; }
291 if (!*key_str) { errno = EPROTO; return -1; }
292 if (*key_str == '@') key_str++;
293 size_t len = strlen(key_str);
294 if (len == 52 && strcmp(key_str+44, ".ed25519") == 0) {}
295 else if (len != 44) { errno = EMSGSIZE; return -1; }
296 return base64_decode(key_str, 44, key, 32);
297}
298
299static jsmntok_t *json_lookup(const char *buf, jsmntok_t *tok, const char *prop, size_t prop_len) {
300 jsmntok_t *end = tok + tok->size + 1;
301 if (tok->type != JSMN_OBJECT) { errno = EPROTO; return NULL; }
302 tok++;
303 while (tok < end) {
304 if (tok + 1 >= end) { errno = EPROTO; return NULL; }
305 if (tok->type == JSMN_STRING
306 && tok->end - tok->start == (int)prop_len
307 && memcmp(buf + tok->start, prop, prop_len) == 0)
308 return tok + 1;
309 tok += tok->size + 1;
310 end += tok->size;
311 }
312 return NULL;
313}
314
315static ssize_t json_get_value(const char *buf, const char *path, const char **value) {
316 static const int num_tokens = 1024;
317 jsmntok_t tokens[num_tokens], *tok = tokens;
318 jsmn_parser parser;
319
320 jsmn_init(&parser);
321 switch (jsmn_parse(&parser, buf, tokens, num_tokens)) {
322 case JSMN_ERROR_NOMEM: errno = ENOMEM; return -1;
323 case JSMN_ERROR_INVAL: errno = EINVAL; return -1;
324 case JSMN_ERROR_PART: errno = EMSGSIZE; return -1;
325 case JSMN_SUCCESS: break;
326 default: errno = EPROTO; return -1;
327 }
328
329 while (*path) {
330 const char *end = strchr(path, '.');
331 size_t part_len = end ? (size_t)end - (size_t)path : strlen(path);
332 tok = json_lookup(buf, tok, path, part_len);
333 if (!tok) { errno = ENOMSG; return -1; }
334 path += part_len;
335 if (*path == '.') path++;
336 }
337
338 *value = buf + tok->start;
339 return tok->end - tok->start;
340}
341
342static void get_app_dir(char *dir, size_t len) {
343 const char *path, *home, *appname;
344 int rc;
345 path = getenv("ssb_path");
346 if (path) {
347 if (strlen(path) > len) errx(1, "ssb_path too long");
348 strncpy(dir, path, len);
349 return;
350 }
351 home = getenv("HOME");
352 if (!home) home = ".";
353 appname = getenv("ssb_appname");
354 if (!appname) appname = "ssb";
355 rc = snprintf(dir, len, "%s/.%s", home, appname);
356 if (rc < 0) err(1, "failed to get app dir");
357 if ((size_t)rc >= len) errx(1, "path to app dir too long");
358}
359
360static ssize_t read_file(char *buf, size_t len, const char *fmt, ...) {
361 va_list ap;
362 int rc;
363 struct stat st;
364 int fd;
365
366 va_start(ap, fmt);
367 rc = vsnprintf(buf, len, fmt, ap);
368 va_end(ap);
369 if (rc < 0) return -1;
370 if ((size_t)rc >= len) { errno = ENAMETOOLONG; return -1; }
371
372 rc = stat(buf, &st);
373 if (rc < 0) return -1;
374 if (st.st_size > (off_t)(len-1)) { errno = EMSGSIZE; return -1; }
375
376 fd = open(buf, O_RDONLY);
377 if (fd < 0) return -1;
378
379 rc = read_all(fd, buf, st.st_size);
380 if (rc < 0) return -1;
381 buf[st.st_size] = '\0';
382
383 close(fd);
384 return st.st_size;
385}
386
387
388static void read_private_key(const char *dir, unsigned char pk[64]) {
389 ssize_t len;
390 char buf[8192];
391 const char *pk_b64;
392 int rc;
393 ssize_t key_len;
394 char *line;
395
396 len = read_file(buf, sizeof(buf), "%s/secret", dir);
397 if (len < 0) err(1, "failed to read secret file");
398
399 // strip comments
400 for (line = buf; *line; ) {
401 if (*line == '#') while (*line && *line != '\n') *line++ = ' ';
402 else while (*line && *line++ != '\n');
403 }
404
405 key_len = json_get_value(buf, "private", &pk_b64);
406 if (key_len < 0) err(1, "unable to read private key");
407
408 if (key_len > 8 && memcmp(pk_b64 + key_len - 8, ".ed25519", 8) == 0)
409 key_len -= 8;
410 rc = base64_decode(pk_b64, key_len, pk, 64);
411 if (rc < 0) err(1, "unable to decode private key");
412}
413
414static void increment_nonce(uint8_t nonce[24]) {
415 int i;
416 for (i = 23; i >= 0 && nonce[i] == 0xff; i--) nonce[i] = 0;
417 if (i >= 0) nonce[i]++;
418}
419
420static void bs_write_packet(struct boxs *bs, const unsigned char *buf, uint16_t len) {
421 size_t boxed_len = len + 34;
422 unsigned char boxed[boxed_len];
423 increment_nonce(bs->nonce2);
424 int rc = crypto_secretbox_easy(boxed + 18, buf, len, bs->nonce2, bs->encrypt_key);
425 if (rc < 0) errx(1, "failed to box packet data");
426 struct boxs_header header;
427 header.len = htons(len);
428 memcpy(header.mac, boxed + 18, 16);
429 rc = crypto_secretbox_easy(boxed, (unsigned char *)&header, 18, bs->nonce1, bs->encrypt_key);
430 if (rc < 0) errx(1, "failed to box packet header");
431 increment_nonce(bs->nonce1);
432 increment_nonce(bs->nonce1);
433 increment_nonce(bs->nonce2);
434 rc = write_all(bs->s, boxed, boxed_len);
435 if (rc < 0) err(1, "failed to write boxed packet");
436}
437
438static int bs_read_packet(struct boxs *bs, void *buf, size_t *lenp) {
439 unsigned char boxed_header[34];
440 struct boxs_header header;
441 int rc = read_all(bs->s, boxed_header, 34);
442 if (rc < 0 && errno == EPIPE) errx(1, "unexpected end of parent stream");
443 if (rc < 0) err(1, "failed to read boxed packet header");
444 rc = crypto_secretbox_open_easy((unsigned char *)&header, boxed_header, 34, bs->rx_nonce, bs->decrypt_key);
445 if (rc < 0) errx(1, "failed to unbox packet header");
446 increment_nonce(bs->rx_nonce);
447 if (header.len == 0 && !memcmp(header.mac, zeros, 16)) { errno = EPIPE; return -1; }
448 size_t len = ntohs(header.len);
449 if (len > BOXS_MAXLEN) errx(1, "received boxed packet too large");
450 unsigned char boxed_data[len + 16];
451 rc = read_all(bs->s, boxed_data + 16, len);
452 if (rc < 0) err(1, "failed to read boxed packet data");
453 memcpy(boxed_data, header.mac, 16);
454 rc = crypto_secretbox_open_easy(buf, boxed_data, len+16, bs->rx_nonce, bs->decrypt_key);
455 if (rc < 0) errx(1, "failed to unbox packet data");
456 increment_nonce(bs->rx_nonce);
457 *lenp = len;
458 return 0;
459}
460
461static int bs_read(struct boxs *bs, char *buf, size_t len) {
462 size_t remaining;
463 while (len > 0) {
464 remaining = bs->rx_buf_len > len ? len : bs->rx_buf_len;
465 if (buf) memcpy(buf, bs->rx_buf + bs->rx_buf_pos, remaining);
466 bs->rx_buf_len -= remaining;
467 bs->rx_buf_pos += remaining;
468 len -= remaining;
469 buf += remaining;
470 if (len == 0) return 0;
471 if (bs_read_packet(bs, bs->rx_buf, &bs->rx_buf_len) < 0) return -1;
472 bs->rx_buf_pos = 0;
473 }
474 return 0;
475}
476
477static int bs_read_out(struct boxs *bs, int fd, size_t len) {
478 size_t chunk;
479 char buf[4096];
480 int rc;
481 while (len > 0) {
482 chunk = len > sizeof(buf) ? sizeof(buf) : len;
483 rc = bs_read(bs, buf, chunk);
484 if (rc < 0) return -1;
485 rc = write_all(fd, buf, chunk);
486 if (rc < 0) return -1;
487 len -= chunk;
488 }
489 return 0;
490}
491
492static int bs_read_error(struct boxs *bs, int errfd, enum pkt_flags flags, size_t len) {
493 // suppress printing "true" indicating end without error
494 if (flags & pkt_flags_json && len == 4) {
495 char buf[4];
496 if (bs_read(bs, buf, 4) < 0) return -1;
497 if (strncmp(buf, "true", 0) == 0) {
498 return 0;
499 }
500 if (write_all(errfd, buf, 4) < 0) return -1;
501 } else {
502 if (bs_read_out(bs, errfd, len) < 0) return -1;
503 }
504 if (flags & (pkt_flags_json | pkt_flags_string)) {
505 if (write_buf(errfd, "\n") < 0) return -1;
506 }
507 return 1;
508}
509
510static void bs_write(struct boxs *bs, const unsigned char *buf, size_t len) {
511 while (len > 0) {
512 size_t l = len > BOXS_MAXLEN ? BOXS_MAXLEN : len;
513 bs_write_packet(bs, buf, l);
514 len -= l;
515 buf += l;
516 }
517}
518
519static void ps_write(struct boxs *bs, const char *data, size_t len, enum pkt_type type, int req_id, bool stream, bool end) {
520 size_t out_len = 9 + len;
521 unsigned char out_buf[out_len];
522 struct pkt_header header = {htonl(len), htonl(req_id)};
523 out_buf[0] = (stream << 3) | (end << 2) | (type & 3);
524 memcpy(out_buf+1, &header, 8);
525 memcpy(out_buf+9, data, len);
526 bs_write(bs, out_buf, out_len);
527}
528
529static int ps_read_header(struct boxs *bs, size_t *len, int *req_id, enum pkt_flags *flags) {
530 char buf[9];
531 struct pkt_header header;
532 if (bs_read(bs, buf, sizeof(buf)) < 0) return -1;
533 memcpy(&header, buf+1, 8);
534 if (len) *len = ntohl(header.len);
535 if (req_id) *req_id = ntohl(header.req);
536 if (flags) *flags = buf[0];
537 return 0;
538}
539
540static void muxrpc_call(struct boxs *bs, const char *method, const char *argument, enum muxrpc_type type, const char *typestr, int req_id) {
541 char req[33792]; // 32768 max message value size + 1024 extra
542 ssize_t reqlen;
543 bool is_request = type == muxrpc_type_async;
544
545 if (is_request) {
546 reqlen = snprintf(req, sizeof(req),
547 "{\"name\":%s,\"args\":%s}",
548 method, argument);
549 } else {
550 reqlen = snprintf(req, sizeof(req),
551 "{\"name\":%s,\"args\":%s,\"type\":\"%s\"}",
552 method, argument, typestr);
553 }
554 if (reqlen < 0) err(1, "failed to construct request");
555 if ((size_t)reqlen >= sizeof(req)) errx(1, "request too large");
556
557 ps_write(bs, req, reqlen, pkt_type_json, req_id, !is_request, false);
558}
559
560static void ps_reject(struct boxs *bs, size_t len, int32_t req, enum pkt_flags flags) {
561 // ignore the packet. if this is a request, the substream on the other end
562 // will just have to wait until the rpc connection closes.
563 (void)req;
564 (void)flags;
565 write_buf(STDERR_FILENO, "ignoring packet: ");
566 int rc = bs_read_out(bs, STDERR_FILENO, len);
567 if (rc < 0) err(1, "bs_read_out");
568 write_buf(STDERR_FILENO, "\n");
569}
570
571static enum stream_state muxrpc_read_source_1(struct boxs *bs, int outfd, int req_id) {
572 enum pkt_flags flags;
573 size_t len;
574 int32_t req;
575 int rc = ps_read_header(bs, &len, &req, &flags);
576 if (rc < 0) err(1, "ps_read_header");
577 if (req == 0 && len == 0) {
578 warnx("unexpected end of parent stream");
579 return stream_state_ended_error;
580 }
581 if (req != -req_id) {
582 ps_reject(bs, len, req, flags);
583 return stream_state_open;
584 }
585 if (flags & pkt_flags_end) {
586 rc = bs_read_error(bs, STDERR_FILENO, flags, len);
587 if (rc < 0) err(1, "bs_read_error");
588 if (rc == 1) return stream_state_ended_error;
589 return stream_state_ended_ok;
590 }
591 rc = bs_read_out(bs, outfd, len);
592 if (rc < 0) err(1, "bs_read_out");
593 if (flags & (pkt_flags_json | pkt_flags_string)) {
594 rc = write_buf(outfd, "\n");
595 if (rc < 0) err(1, "write_buf");
596 }
597 return stream_state_open;
598}
599
600static int muxrpc_read_source(struct boxs *bs, int outfd, int req_id) {
601 enum stream_state state;
602 while ((state = muxrpc_read_source_1(bs, outfd, req_id)) == stream_state_open);
603 return state == stream_state_ended_ok ? 0 :
604 state == stream_state_ended_error ? 2 : 1;
605}
606
607static int muxrpc_read_async(struct boxs *bs, int outfd, int req_id) {
608 enum pkt_flags flags;
609 size_t len;
610 int32_t req;
611 int rc;
612
613 while (1) {
614 rc = ps_read_header(bs, &len, &req, &flags);
615 if (rc < 0) err(1, "ps_read_header");
616 if (req == -req_id) break;
617 if (req == 0 && len == 0) errx(1, "unexpected end of parent stream");
618 ps_reject(bs, len, req, flags);
619 }
620 if (flags & pkt_flags_end) {
621 rc = bs_read_error(bs, STDERR_FILENO, flags, len);
622 if (rc < 0) err(1, "bs_read_error");
623 if (rc == 1) return 2;
624 return 1;
625 }
626 rc = bs_read_out(bs, outfd, len);
627 if (rc < 0) err(1, "bs_read_out");
628 if (flags & (pkt_flags_json | pkt_flags_string)) {
629 rc = write_buf(outfd, "\n");
630 if (rc < 0) err(1, "write_buf");
631 }
632 return 0;
633}
634
635static enum stream_state muxrpc_write_sink_1(struct boxs *bs, int infd,
636 enum pkt_type ptype, int req_id) {
637 char buf[4096];
638 ssize_t sz = read(infd, buf, sizeof(buf));
639 if (sz < 0) err(1, "read");
640 if (sz == 0) {
641 ps_write(bs, "true", 4, pkt_type_json, req_id, true, true);
642 return stream_state_ended_ok;
643 }
644 ps_write(bs, buf, sz, ptype, req_id, true, false);
645 return stream_state_open;
646}
647
648static enum stream_state muxrpc_write_sink_1_hashed(struct boxs *bs, int infd,
649 crypto_hash_sha256_state *hash_state, int req_id) {
650 int rc;
651 unsigned char buf[4096];
652 ssize_t sz = read(infd, buf, sizeof(buf));
653 if (sz < 0) err(1, "read");
654 if (sz == 0) {
655 ps_write(bs, "true", 4, pkt_type_json, req_id, true, true);
656 return stream_state_ended_ok;
657 }
658 rc = crypto_hash_sha256_update(hash_state, buf, sz);
659 if (rc < 0) errx(1, "hash update failed");
660 ps_write(bs, (char *)buf, sz, pkt_type_buffer, req_id, true, false);
661 return stream_state_open;
662}
663
664static int muxrpc_write_sink(struct boxs *bs, int infd, enum pkt_type ptype, int req_id) {
665 int rc;
666 fd_set rd;
667 int sfd = bs->s;
668 int maxfd = infd > sfd ? infd : sfd;
669 enum stream_state in = stream_state_open;
670 enum stream_state out = stream_state_open;
671
672 while (out == stream_state_open) {
673 FD_ZERO(&rd);
674 if (in == stream_state_open) FD_SET(infd, &rd);
675 if (out == stream_state_open) FD_SET(sfd, &rd);
676 rc = select(maxfd + 1, &rd, 0, 0, NULL);
677 if (rc < 0) err(1, "select");
678 if (FD_ISSET(infd, &rd)) in = muxrpc_write_sink_1(bs, infd, ptype, req_id);
679 if (FD_ISSET(sfd, &rd)) out = muxrpc_read_source_1(bs, -1, req_id);
680 }
681
682 return in == stream_state_ended_ok && out == stream_state_ended_ok ? 0 :
683 in == stream_state_ended_error || out == stream_state_ended_error ? 2 : 1;
684}
685
686static int muxrpc_write_blob_add(struct boxs *bs, int infd, int outfd, int req_id) {
687 int rc;
688 fd_set rd;
689 int sfd = bs->s;
690 int maxfd = infd > sfd ? infd : sfd;
691 enum stream_state in = stream_state_open;
692 enum stream_state out = stream_state_open;
693 crypto_hash_sha256_state hash_state;
694 unsigned char hash[32];
695 char id[54] = "&";
696
697 rc = crypto_hash_sha256_init(&hash_state);
698 if (rc < 0) { errno = EINVAL; return -1; }
699
700 while (out == stream_state_open) {
701 FD_ZERO(&rd);
702 if (in == stream_state_open) FD_SET(infd, &rd);
703 if (out == stream_state_open) FD_SET(sfd, &rd);
704 rc = select(maxfd + 1, &rd, 0, 0, NULL);
705 if (rc < 0) err(1, "select");
706 if (FD_ISSET(infd, &rd)) in = muxrpc_write_sink_1_hashed(bs, infd, &hash_state, req_id);
707 if (FD_ISSET(sfd, &rd)) out = muxrpc_read_source_1(bs, -1, req_id);
708 }
709
710 rc = crypto_hash_sha256_final(&hash_state, hash);
711 if (rc < 0) errx(1, "hash finalize failed");
712
713 rc = base64_encode(hash, 32, id+1, sizeof(id)-1);
714 if (rc < 0) err(1, "encoding hash failed");
715 strcpy(id + 45, ".sha256\n");
716 rc = write_all(outfd, id, sizeof(id)-1);
717 if (rc < 0) err(1, "writing hash failed");
718
719 return in == stream_state_ended_ok && out == stream_state_ended_ok ? 0 :
720 in == stream_state_ended_error || out == stream_state_ended_error ? 2 : 1;
721}
722
723static int muxrpc_duplex(struct boxs *bs, int infd, int outfd, enum pkt_type in_ptype, int req_id) {
724 int rc;
725 fd_set rd;
726 int sfd = bs->s;
727 int maxfd = infd > sfd ? infd : sfd;
728 enum stream_state in = stream_state_open;
729 enum stream_state out = stream_state_open;
730
731 while (out == stream_state_open
732 || (in == stream_state_open && out != stream_state_ended_error)) {
733 FD_ZERO(&rd);
734 if (in == stream_state_open) FD_SET(infd, &rd);
735 if (out == stream_state_open) FD_SET(sfd, &rd);
736 rc = select(maxfd + 1, &rd, 0, 0, NULL);
737 if (rc < 0) err(1, "select");
738 if (FD_ISSET(infd, &rd)) in = muxrpc_write_sink_1(bs, infd, in_ptype, req_id);
739 if (FD_ISSET(sfd, &rd)) out = muxrpc_read_source_1(bs, outfd, req_id);
740 }
741
742 return in == stream_state_ended_ok && out == stream_state_ended_ok ? 0 :
743 in == stream_state_ended_error || out == stream_state_ended_error ? 2 : 1;
744}
745
746static int method_to_json(char *out, size_t outlen, const char *str) {
747 // blobs.get => ["blobs", "get"]
748 size_t i = 0;
749 char c;
750 if (i+2 > outlen) return -1;
751 out[i++] = '[';
752 out[i++] = '"';
753 while ((c = *str++)) {
754 if (c == '.') {
755 if (i+3 > outlen) return -1;
756 out[i++] = '"';
757 out[i++] = ',';
758 out[i++] = '"';
759 } else if (c == '"') {
760 if (i+2 > outlen) return -1;
761 out[i++] = '\\';
762 out[i++] = '"';
763 } else {
764 if (i+1 > outlen) return -1;
765 out[i++] = c;
766 }
767 }
768 if (i+3 > outlen) return -1;
769 out[i++] = '"';
770 out[i++] = ']';
771 out[i++] = '\0';
772 return i;
773}
774
775static int args_to_json_length(int argc, char *argv[]) {
776 int i = 0;
777 int len = 3; // "[]\0"
778 for (i = 0; i < argc; i++)
779 len += strlen(argv[i])+1;
780 return len;
781}
782
783static int args_to_json(char *out, size_t outlen, unsigned int argc, char *argv[]) {
784 size_t i = 0;
785 size_t j;
786 if (i+1 > outlen) return -1;
787 out[i++] = '[';
788 for (j = 0; j < argc; j++) {
789 size_t len = strlen(argv[j]);
790 if (j > 0) out[i++] = ',';
791 if (i+len > outlen) return -1;
792 strncpy(out+i, argv[j], len);
793 i += len;
794 }
795 if (i+2 > outlen) return -1;
796 out[i++] = ']';
797 out[i++] = '\0';
798 return i;
799}
800
801int main(int argc, char *argv[]) {
802 int i, s, infd, outfd, rc;
803 const char *key = NULL;
804 const char *keypair_seed_str = NULL;
805 const char *host = NULL;
806 const char *port = "8008";
807 const char *typestr = NULL, *methodstr;
808 const char *shs_cap_key_str = NULL;
809 size_t argument_len;
810 unsigned char private_key[64];
811 unsigned char public_key[32];
812 unsigned char remote_key[32];
813 unsigned char shs_cap_key[32];
814 enum muxrpc_type type;
815 enum pkt_type ptype = pkt_type_buffer;
816 char method[256];
817 char app_dir[_POSIX_PATH_MAX];
818 ssize_t len;
819 bool test = false;
820
821 get_app_dir(app_dir, sizeof(app_dir));
822
823 char config_buf[8192];
824 len = read_file(config_buf, sizeof(config_buf), "%s/config", app_dir);
825 if (len > 0) {
826 ssize_t host_len = json_get_value(config_buf, "host", &host);
827 ssize_t port_len = json_get_value(config_buf, "port", &port);
828 ssize_t shs_cap_len = json_get_value(config_buf, "caps.shs", &shs_cap_key_str);
829 if (host_len >= 0) ((char *)host)[host_len] = '\0';
830 if (port_len >= 0) ((char *)port)[port_len] = '\0';
831 if (shs_cap_len >= 0) ((char *)shs_cap_key_str)[shs_cap_len] = '\0';
832 } else if (len < 0 && errno != ENOENT) {
833 err(1, "failed to read config");
834 }
835
836 for (i = 1; i < argc && (argv[i][0] == '-'); i++) {
837 switch (argv[i][1]) {
838 case 'c': shs_cap_key_str = argv[++i]; break;
839 case 'j': ptype = pkt_type_json; break;
840 case 'T': test = true; break;
841 case 's': host = argv[++i]; break;
842 case 'k': key = argv[++i]; break;
843 case 'K': keypair_seed_str = argv[++i]; break;
844 case 'p': port = argv[++i]; break;
845 case 't': typestr = argv[++i]; break;
846 default: usage();
847 }
848 }
849 if (i < argc) methodstr = argv[i++]; else if (!test) usage();
850
851 if (shs_cap_key_str) {
852 rc = pubkey_decode(shs_cap_key_str, shs_cap_key);
853 if (rc < 0) err(1, "unable to decode cap key '%s'", shs_cap_key_str);
854 } else {
855 memcpy(shs_cap_key, ssb_cap, 32);
856 }
857
858 argument_len = test ? 0 : args_to_json_length(argc-i, argv+i);
859 char argument[argument_len];
860
861 if (!test) {
862 rc = args_to_json(argument, sizeof(argument), argc-i, argv+i);
863 if (rc < 0) errx(0, "unable to collect arguments");
864
865 char manifest_buf[8192];
866 if (!typestr) {
867 len = read_file(manifest_buf, sizeof(manifest_buf),
868 "%s/manifest.json", app_dir);
869 if (len < 0) err(1, "failed to read manifest file");
870
871 ssize_t type_len = json_get_value(manifest_buf, methodstr, &typestr);
872 if (!typestr && errno == ENOMSG) errx(1,
873 "unable to find method '%s' in manifest", methodstr);
874 if (!typestr) err(1, "unable to read manifest");
875 ((char *)typestr)[type_len] = '\0';
876 }
877 if (strcmp(typestr, "sync") == 0) type = muxrpc_type_async;
878 else if (strcmp(typestr, "async") == 0) type = muxrpc_type_async;
879 else if (strcmp(typestr, "sink") == 0) type = muxrpc_type_sink;
880 else if (strcmp(typestr, "source") == 0) type = muxrpc_type_source;
881 else if (strcmp(typestr, "duplex") == 0) type = muxrpc_type_duplex;
882 else errx(1, "type must be one of <async|sink|source|duplex>");
883
884 rc = method_to_json(method, sizeof(method), methodstr);
885 if (rc < 0) errx(0, "unable to convert method name");
886 }
887
888 if (keypair_seed_str) {
889 unsigned char seed[crypto_sign_SEEDBYTES];
890 unsigned char ed25519_skpk[crypto_sign_ed25519_SECRETKEYBYTES];
891
892 rc = pubkey_decode(keypair_seed_str, ed25519_skpk);
893 if (rc < 0) err(1, "unable to decode private key");
894 rc = crypto_sign_ed25519_sk_to_seed(seed, ed25519_skpk);
895 if (rc < 0) err(1, "unable to convert private key to seed");
896 rc = crypto_sign_seed_keypair(public_key, private_key, seed);
897 if (rc < 0) err(1, "unable to generate keypair from seed");
898 } else {
899 read_private_key(app_dir, private_key);
900 memcpy(public_key, private_key+32, 32);
901 }
902
903 if (key) {
904 rc = pubkey_decode(key, remote_key);
905 if (rc < 0) err(1, "unable to decode remote key '%s'", key);
906 } else {
907 memcpy(remote_key, public_key, 32);
908 }
909
910 if (test) {
911 infd = STDIN_FILENO;
912 outfd = STDOUT_FILENO;
913 s = -1;
914 } else {
915 s = tcp_connect(host, port);
916 if (s < 0) err(1, "tcp_connect");
917 infd = outfd = s;
918 }
919
920 struct boxs bs;
921 shs_connect(s, infd, outfd, public_key, private_key, shs_cap_key, remote_key, &bs);
922
923 if (test) {
924 rc = write_all(outfd, bs.encrypt_key, sizeof(bs.encrypt_key));
925 rc |= write_all(outfd, bs.nonce1, sizeof(bs.nonce1));
926 rc |= write_all(outfd, bs.decrypt_key, sizeof(bs.decrypt_key));
927 rc |= write_all(outfd, bs.rx_nonce, sizeof(bs.rx_nonce));
928 if (rc < 0) err(1, "failed to write handshake result");
929 return 0;
930 }
931
932 muxrpc_call(&bs, method, argument, type, typestr, 1);
933
934 switch (type) {
935 case muxrpc_type_async:
936 rc = muxrpc_read_async(&bs, STDOUT_FILENO, 1);
937 break;
938 case muxrpc_type_source:
939 rc = muxrpc_read_source(&bs, STDOUT_FILENO, 1);
940 break;
941 case muxrpc_type_sink:
942 if (!strcmp(methodstr, "blobs.add")) {
943 rc = muxrpc_write_blob_add(&bs, STDIN_FILENO, STDOUT_FILENO, 1);
944 } else {
945 rc = muxrpc_write_sink(&bs, STDIN_FILENO, ptype, 1);
946 }
947 break;
948 case muxrpc_type_duplex:
949 rc = muxrpc_duplex(&bs, STDIN_FILENO, STDOUT_FILENO, ptype, 1);
950 break;
951 }
952
953 close(s);
954 return rc;
955}
956

Built with git-ssb-web