diff --git a/.install/sbotc/Makefile b/.install/sbotc/Makefile deleted file mode 100644 index 3a26f42..0000000 --- a/.install/sbotc/Makefile +++ /dev/null @@ -1,42 +0,0 @@ -BIN = sbotc - -PREFIX = /usr/local -BINDIR = $(PREFIX)/bin -MANDIR = $(PREFIX)/share/man - -CFLAGS = -Wall -Werror -Wextra - -ifdef STATIC - LDLIBS = -l:libsodium.a -else - LDLIBS = -lsodium -endif - -all: $(BIN) - -$(BIN): $(BIN).c base64.c jsmn.c - -install: all - @mkdir -vp $(DESTDIR)$(BINDIR) $(DESTDIR)$(MANDIR)/man1 - @cp -vf $(BIN) $(DESTDIR)$(BINDIR) - @cp -vf $(BIN).1 $(DESTDIR)$(MANDIR)/man1 - -link: all - @mkdir -vp $(DESTDIR)$(BINDIR) $(DESTDIR)$(MANDIR)/man1 - @ln -svf $(shell realpath $(BIN)) $(DESTDIR)$(BINDIR) - @ln -svf $(shell realpath $(BIN).1) $(DESTDIR)$(MANDIR)/man1 - -uninstall: - @rm -vf \ - $(DESTDIR)$(BINDIR)/$(BIN) \ - $(DESTDIR)$(MANDIR)/man1/$(BIN).1 - -test-shs1: - @# %lzzcAZlM21slUIoiH4yd/wgDnXu8raNLvwqjxqrU06k=.sha256 - shs1testclient ./test-shs-inner.sh $(SHS1_TEST_SEED) - -clean: - @rm -vf $(BIN) - -.PHONY: - all install link uninstall test-shs1 clean diff --git a/.install/sbotc/README.md b/.install/sbotc/README.md deleted file mode 100644 index cc3d0dd..0000000 --- a/.install/sbotc/README.md +++ /dev/null @@ -1,31 +0,0 @@ -# sbotc - -A command-line SSB client in C. Use like the `sbot` command (except for `server`/`start`). - -## Install - -Install the dependency, *sodium*. On Debian: `sudo apt-get install libsodium-dev` - -Compile and install the program: - -```sh -make -sudo make install -``` - -## Compile options - -To build a binary statically linked with libsodium, use `make STATIC=1` - -## Usage - -```sh -sbotc [-j] [-T] [-l] [-r] [-e] - [ -n | [-c ] [-k ] [-K ] ] - [ [-s ] [-p ] [ -4 | -6 ] | [-u ] ] - [ -a | [-t ] [...] ] -``` - -Arguments must be explicitly JSON-encoded. - -For more information, see the manual page `sbotc(1)`. diff --git a/.install/sbotc/base64.c b/.install/sbotc/base64.c deleted file mode 100644 index 1ee0395..0000000 --- a/.install/sbotc/base64.c +++ /dev/null @@ -1,118 +0,0 @@ -/* - - This code is public domain software. - -*/ - -#include "base64.h" - -#include -#include -#include - - -// single base64 character conversion -// -static int POS(char c) -{ - if (c>='A' && c<='Z') return c - 'A'; - if (c>='a' && c<='z') return c - 'a' + 26; - if (c>='0' && c<='9') return c - '0' + 52; - if (c == '+') return 62; - if (c == '/') return 63; - if (c == '=') return -1; - return -2; -} - -// base64 decoding -// -// s: base64 string -// str_len size of the base64 string -// data: output buffer for decoded data -// data_len expected size of decoded data -// return: 0 on success, -1 on failure -// -int base64_decode(const char* s, size_t str_len, void *data, size_t data_len) -{ - const char *p, *str_end; - unsigned char *q, *end; - int n[4] = { 0, 0, 0, 0 }; - - if (str_len % 4) { errno = EBADMSG; return -1; } - q = (unsigned char*) data; - end = q + data_len; - str_end = s + str_len; - - for (p = s; p < str_end; ) { - n[0] = POS(*p++); - n[1] = POS(*p++); - n[2] = POS(*p++); - n[3] = POS(*p++); - - if (n[0] == -2 || n[1] == -2 || n[2] == -2 || n[3] == -2) - { errno = EBADMSG; return -1; } - - if (n[0] == -1 || n[1] == -1) - { errno = EBADMSG; return -1; } - - if (n[2] == -1 && n[3] != -1) - { errno = EBADMSG; return -1; } - - if (q >= end) { errno = EMSGSIZE; return -1; } - q[0] = (n[0] << 2) + (n[1] >> 4); - if (n[2] != -1) { - if (q+1 >= end) { errno = EMSGSIZE; return -1; } - q[1] = ((n[1] & 15) << 4) + (n[2] >> 2); - } - if (n[3] != -1) { - if (q+2 >= end) { errno = EMSGSIZE; return -1; } - q[2] = ((n[2] & 3) << 6) + n[3]; - } - q += 3; - } - - return 0; -} - -int base64_encode(const void* buf, size_t size, char *str, size_t out_size) { - static const char base64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; - - char* p = str; - const unsigned char* q = (const unsigned char*) buf; - size_t i = 0; - - if ((size+3)*4/3 + 1 > out_size) { - errno = EMSGSIZE; - return -1; - } - - while (i < size) { - int c = q[i++]; - c *= 256; - if (i < size) - c += q[i]; - i++; - - c *= 256; - if (i < size) - c += q[i]; - i++; - - *p++ = base64[(c & 0x00fc0000) >> 18]; - *p++ = base64[(c & 0x0003f000) >> 12]; - - if (i > size + 1) - *p++ = '='; - else - *p++ = base64[(c & 0x00000fc0) >> 6]; - - if (i > size) - *p++ = '='; - else - *p++ = base64[c & 0x0000003f]; - } - - *p = 0; - - return 0; -} diff --git a/.install/sbotc/base64.h b/.install/sbotc/base64.h deleted file mode 100644 index 8ca4652..0000000 --- a/.install/sbotc/base64.h +++ /dev/null @@ -1,6 +0,0 @@ -#pragma once - -#include - -int base64_encode(const void* buf, size_t size, char *str, size_t out_size); -int base64_decode(const char *s, size_t str_len, void *data, size_t data_len); diff --git a/.install/sbotc/jsmn.c b/.install/sbotc/jsmn.c deleted file mode 100644 index 9fda563..0000000 --- a/.install/sbotc/jsmn.c +++ /dev/null @@ -1,278 +0,0 @@ -/** - * jsmn - * Copyright (c) 2010 Serge A. Zaitsev - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#include - -#include "jsmn.h" - -/** - * Allocates a fresh unused token from the token pull. - */ -static jsmntok_t *jsmn_alloc_token(jsmn_parser *parser, - jsmntok_t *tokens, size_t num_tokens) { - jsmntok_t *tok; - if (parser->toknext >= (int)num_tokens) { - return NULL; - } - tok = &tokens[parser->toknext++]; - tok->start = tok->end = -1; - tok->size = 0; -#ifdef JSMN_PARENT_LINKS - tok->parent = -1; -#endif - return tok; -} - -/** - * Fills token type and boundaries. - */ -static void jsmn_fill_token(jsmntok_t *token, jsmntype_t type, - int start, int end) { - token->type = type; - token->start = start; - token->end = end; - token->size = 0; -} - -/** - * Fills next available token with JSON primitive. - */ -static jsmnerr_t jsmn_parse_primitive(jsmn_parser *parser, const char *js, - jsmntok_t *tokens, size_t num_tokens) { - jsmntok_t *token; - int start; - - start = parser->pos; - - for (; js[parser->pos] != '\0'; parser->pos++) { - switch (js[parser->pos]) { -#ifndef JSMN_STRICT - /* In strict mode primitive must be followed by "," or "}" or "]" */ - case ':': -#endif - case '\t' : case '\r' : case '\n' : case ' ' : - case ',' : case ']' : case '}' : - goto found; - } - if (js[parser->pos] < 32 || js[parser->pos] >= 127) { - parser->pos = start; - return JSMN_ERROR_INVAL; - } - } -#ifdef JSMN_STRICT - /* In strict mode primitive must be followed by a comma/object/array */ - parser->pos = start; - return JSMN_ERROR_PART; -#endif - -found: - token = jsmn_alloc_token(parser, tokens, num_tokens); - if (token == NULL) { - parser->pos = start; - return JSMN_ERROR_NOMEM; - } - jsmn_fill_token(token, JSMN_PRIMITIVE, start, parser->pos); -#ifdef JSMN_PARENT_LINKS - token->parent = parser->toksuper; -#endif - parser->pos--; - return JSMN_SUCCESS; -} - -/** - * Filsl next token with JSON string. - */ -static jsmnerr_t jsmn_parse_string(jsmn_parser *parser, const char *js, - jsmntok_t *tokens, size_t num_tokens) { - jsmntok_t *token; - - int start = parser->pos; - - parser->pos++; - - /* Skip starting quote */ - for (; js[parser->pos] != '\0'; parser->pos++) { - char c = js[parser->pos]; - - /* Quote: end of string */ - if (c == '\"') { - token = jsmn_alloc_token(parser, tokens, num_tokens); - if (token == NULL) { - parser->pos = start; - return JSMN_ERROR_NOMEM; - } - jsmn_fill_token(token, JSMN_STRING, start+1, parser->pos); -#ifdef JSMN_PARENT_LINKS - token->parent = parser->toksuper; -#endif - return JSMN_SUCCESS; - } - - /* Backslash: Quoted symbol expected */ - if (c == '\\') { - parser->pos++; - switch (js[parser->pos]) { - /* Allowed escaped symbols */ - case '\"': case '/' : case '\\' : case 'b' : - case 'f' : case 'r' : case 'n' : case 't' : - break; - /* Allows escaped symbol \uXXXX */ - case 'u': - /* TODO */ - break; - /* Unexpected symbol */ - default: - parser->pos = start; - return JSMN_ERROR_INVAL; - } - } - } - parser->pos = start; - return JSMN_ERROR_PART; -} - -/** - * Parse JSON string and fill tokens. - */ -jsmnerr_t jsmn_parse(jsmn_parser *parser, const char *js, jsmntok_t *tokens, - unsigned int num_tokens) { - jsmnerr_t r; - int i; - jsmntok_t *token; - - for (; js[parser->pos] != '\0'; parser->pos++) { - char c; - jsmntype_t type; - - c = js[parser->pos]; - switch (c) { - case '{': case '[': - token = jsmn_alloc_token(parser, tokens, num_tokens); - if (token == NULL) - return JSMN_ERROR_NOMEM; - if (parser->toksuper != -1) { - tokens[parser->toksuper].size++; -#ifdef JSMN_PARENT_LINKS - token->parent = parser->toksuper; -#endif - } - token->type = (c == '{' ? JSMN_OBJECT : JSMN_ARRAY); - token->start = parser->pos; - parser->toksuper = parser->toknext - 1; - break; - case '}': case ']': - type = (c == '}' ? JSMN_OBJECT : JSMN_ARRAY); -#ifdef JSMN_PARENT_LINKS - if (parser->toknext < 1) { - return JSMN_ERROR_INVAL; - } - token = &tokens[parser->toknext - 1]; - for (;;) { - if (token->start != -1 && token->end == -1) { - if (token->type != type) { - return JSMN_ERROR_INVAL; - } - token->end = parser->pos + 1; - parser->toksuper = token->parent; - break; - } - if (token->parent == -1) { - break; - } - token = &tokens[token->parent]; - } -#else - for (i = parser->toknext - 1; i >= 0; i--) { - token = &tokens[i]; - if (token->start != -1 && token->end == -1) { - if (token->type != type) { - return JSMN_ERROR_INVAL; - } - parser->toksuper = -1; - token->end = parser->pos + 1; - break; - } - } - /* Error if unmatched closing bracket */ - if (i == -1) return JSMN_ERROR_INVAL; - for (; i >= 0; i--) { - token = &tokens[i]; - if (token->start != -1 && token->end == -1) { - parser->toksuper = i; - break; - } - } -#endif - break; - case '\"': - r = jsmn_parse_string(parser, js, tokens, num_tokens); - if (r < 0) return r; - if (parser->toksuper != -1) - tokens[parser->toksuper].size++; - break; - case '\t' : case '\r' : case '\n' : case ':' : case ',': case ' ': - break; -#ifdef JSMN_STRICT - /* In strict mode primitives are: numbers and booleans */ - case '-': case '0': case '1' : case '2': case '3' : case '4': - case '5': case '6': case '7' : case '8': case '9': - case 't': case 'f': case 'n' : -#else - /* In non-strict mode every unquoted value is a primitive */ - default: -#endif - r = jsmn_parse_primitive(parser, js, tokens, num_tokens); - if (r < 0) return r; - if (parser->toksuper != -1) - tokens[parser->toksuper].size++; - break; - -#ifdef JSMN_STRICT - /* Unexpected char in strict mode */ - default: - return JSMN_ERROR_INVAL; -#endif - - } - } - - for (i = parser->toknext - 1; i >= 0; i--) { - /* Unmatched opened object or array */ - if (tokens[i].start != -1 && tokens[i].end == -1) { - return JSMN_ERROR_PART; - } - } - - return JSMN_SUCCESS; -} - -/** - * Creates a new parser based over a given buffer with an array of tokens - * available. - */ -void jsmn_init(jsmn_parser *parser) { - parser->pos = 0; - parser->toknext = 0; - parser->toksuper = -1; -} - diff --git a/.install/sbotc/jsmn.h b/.install/sbotc/jsmn.h deleted file mode 100644 index 03b2c1a..0000000 --- a/.install/sbotc/jsmn.h +++ /dev/null @@ -1,67 +0,0 @@ -#ifndef __JSMN_H_ -#define __JSMN_H_ - -/** - * JSON type identifier. Basic types are: - * o Object - * o Array - * o String - * o Other primitive: number, boolean (true/false) or null - */ -typedef enum { - JSMN_PRIMITIVE = 0, - JSMN_OBJECT = 1, - JSMN_ARRAY = 2, - JSMN_STRING = 3 -} jsmntype_t; - -typedef enum { - /* Not enough tokens were provided */ - JSMN_ERROR_NOMEM = -1, - /* Invalid character inside JSON string */ - JSMN_ERROR_INVAL = -2, - /* The string is not a full JSON packet, more bytes expected */ - JSMN_ERROR_PART = -3, - /* Everything was fine */ - JSMN_SUCCESS = 0 -} jsmnerr_t; - -/** - * JSON token description. - * @param type type (object, array, string etc.) - * @param start start position in JSON data string - * @param end end position in JSON data string - */ -typedef struct { - jsmntype_t type; - int start; - int end; - int size; -#ifdef JSMN_PARENT_LINKS - int parent; -#endif -} jsmntok_t; - -/** - * JSON parser. Contains an array of token blocks available. Also stores - * the string being parsed now and current position in that string - */ -typedef struct { - unsigned int pos; /* offset in the JSON string */ - int toknext; /* next token to allocate */ - int toksuper; /* superior token node, e.g parent object or array */ -} jsmn_parser; - -/** - * Create JSON parser over an array of tokens - */ -void jsmn_init(jsmn_parser *parser); - -/** - * Run JSON parser. It parses a JSON data string into and array of tokens, each describing - * a single JSON object. - */ -jsmnerr_t jsmn_parse(jsmn_parser *parser, const char *js, - jsmntok_t *tokens, unsigned int num_tokens); - -#endif /* __JSMN_H_ */ diff --git a/.install/sbotc/sbotc b/.install/sbotc/sbotc deleted file mode 100755 index 82c420d..0000000 Binary files a/.install/sbotc/sbotc and /dev/null differ diff --git a/.install/sbotc/sbotc.1 b/.install/sbotc/sbotc.1 deleted file mode 100644 index 0a741fc..0000000 --- a/.install/sbotc/sbotc.1 +++ /dev/null @@ -1,190 +0,0 @@ -.Dd 2017-06-03 -.Dt SBOTC 1 -.Os SSBC -.ds REPO ssb://%133ulDgs/oC1DXjoK04vDFy6DgVBB/Zok15YJmuhD5Q=.sha256 -.Sh NAME -.Nm sbotc -.Nd Call a scuttlebot/secret-stack RPC method -.Sh SYNOPSIS -.Nm -.Op Fl j -.Op Fl l -.Op Fl r -.Op Fl T -.Op Fl e -.Op Fl a -. -.Oo -.Fl n -| -.Op Fl c Ar cap -.Op Fl k Ar key -.Op Fl K Ar keypair -.Oc -. -.Oo -.Op Fl s Ar host -.Op Fl p Ar port -.Oo -.Fl 4 -| -.Fl 6 -.Oc -| -.Op Fl u Ar socket_path -.Oc -. -.Oo -.Fl a -| -.Op Fl t Ar type -.Ar method -.Op Ar argument ... -.Oc -.Sh DESCRIPTION -Connect to a scuttlebot/secret-stack server, and call a method on it, with -standard I/O. -.Sh OPTIONS -.Bl -tag -.It Fl j -Send stdin data as JSON. -.It Fl l -Don't output newlines after string or JSON packets. -.It Fl r -Raw mode. Disables stdin line buffering/editing and echoing. Implies -.Fl l . -.It Fl e -Encode arguments as strings, rather than expecting them to be JSON-encoded. -.It Fl T -Test using shs1-testsuite protocol. Instead of connecting to a server and running -a command, connect to stdio. On successful handshake, output concatenation of -the encryption key, encryption nonce, decryption key and decryption nonce. -.It Fl a -Passthrough mode. Instead of making a muxrpc call, pass through the box-stream -to stdio. -.It Fl n -Noauth mode. Skip secret-handshake authentication and box-stream encryption. -This option makes the -.Fl k , -.Fl K , -and -.Fl c -options have no effect and output a warning if used. -.It Fl 4 -Connect to server over IPv4 only. -.It Fl 6 -Connect to server over IPv6 only. -.It Fl c Ar cap -Capability key for secret-handshake. Default is SSB's capability key, -.Li 1KHLiKZvAvjbY1ziZEHMXawbCEIM6qwjCDm3VYRan/s= . -.It Fl s Ar host -The hostname to connect to. Default is localhost. If set to localhost and connection to localhost fails, -.Nm -may attempt to connect to other local interface addresses. -.It Fl p Ar port -The port to connect to. Default is 8008. -.It Fl u Ar socket_path -Unix socket path to connect to, instead of TCP socket. Conflicts with -.Fl p -and -.Fl s . -.It Fl k Ar key -The key to connect to. Default is your public key, as read from your -private key file. -.It Fl K Ar keypair -Private key or private key seed to use for secret-handshake. Default is to use the private key -from your -.Pa ~/.ssb/secret -file or other secret file according to the environmental variables described in -.Sx ENVIRONMENT . -.It Fl t Ar type -The type of method: -.Dq async , -.Dq source , -.Dq sink , -or -.Dq duplex . -Default is to look up the method in -.Pa ~/.ssb/manifest.json . -.It Ar method -Method name. -.It Op Ar argument ... -Arguments to pass to the method call. Each argument must be JSON-encoded, unless the -.Fl e -option is used, in which the arguments are treated as strings. -.El -.Sh ENVIRONMENT -.Bl -tag -.It Ev ssb_appname -Name of the app. Default is -.Dq ssb . -Used to construct the app's directory if -.Ev ssb_path -is not present. -.It Ev ssb_path -Path to the app's directory. Default is to use -.Ev ssb_appname to construct the path as -.Dq ~/. -.El -.Sh FILES -.Bl -tag -width -indent -.It Pa ~/.ssb/secret -Your private key, used for authenticating to the server with the -secret-handshake protocol. -.It Pa ~/.ssb/manifest.json -A map of method names to method types. -.It Pa ~/.ssb/config -JSON file containing key, host, port, and/or SHS cap key to use if the -.Fl s , -.Fl p -or -.Fl c -options are not given, respectively. -.It Pa ~/.ssb/socket -UNIX socket stream file for noauth connections. -If none of the options -.Fl s , -.Fl p , -.Fl u , -.Fl c , -.Fl k , -.Fl K , -.Fl c , -.Fl 4 , -.Fl 6 , -or -.Fl T -are specified, -.Nm -will attempt to connect in noauth mode to this socket file. If the socket file -is not present or the connection fails, -.Nm -will fall back to connecting with TCP and secret-handshake according to the -config file - unless the -.Fl n -option is specified, in which case the command will fail. -.El -.Pp -The base path -.Dq ~/.ssb/ -of these file names may be changed by setting -.Ev ssb_appname -or -.Ev ssb_path . -.Sh EXIT STATUS -.Bl -tag -width Ds -.It 0 -The command completed successfully. -.It 1 -An error occurred. -.It 2 -The command completed with an error. -.El -.Sh AUTHORS -.Nm -was written by -.An cel Aq @f/6sQ6d2CMxRUhLpspgGIulDxDCwYD7DzFzPNr7u5AU=.ed25519 . -.Sh BUGS -.Pp -Please report any bugs by making a post on SSB mentioning the repo, -.Lk \*[REPO] diff --git a/.install/sbotc/sbotc.c b/.install/sbotc/sbotc.c deleted file mode 100644 index 8972a38..0000000 --- a/.install/sbotc/sbotc.c +++ /dev/null @@ -1,1270 +0,0 @@ -/* - * sbotc.c - * Copyright (c) 2017 Secure Scuttlebutt Consortium - * - * Usage of the works is permitted provided that this instrument is - * retained with the works, so that any entity that uses the works is - * notified of this instrument. - * - * DISCLAIMER: THE WORKS ARE WITHOUT WARRANTY. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include "base64.h" -#include "jsmn.h" - -#define BOXS_MAXLEN 4096 - -#define write_buf(fd, buf) \ - write_all(fd, buf, sizeof(buf)-1) - -struct boxs_header { - uint16_t len; - uint8_t mac[16]; -}; - -struct boxs { - int s; - unsigned char encrypt_key[32]; - unsigned char decrypt_key[32]; - unsigned char nonce1[24]; - unsigned char nonce2[24]; - unsigned char rx_nonce[24]; - unsigned char rx_buf[BOXS_MAXLEN]; - size_t rx_buf_pos; - size_t rx_buf_len; - bool noauth; - bool wrote_goodbye; -}; - -enum pkt_type { - pkt_type_buffer = 0, - pkt_type_string = 1, - pkt_type_json = 2, -}; - -enum pkt_flags { - pkt_flags_buffer = 0, - pkt_flags_string = 1, - pkt_flags_json = 2, - pkt_flags_end = 4, - pkt_flags_stream = 8, -}; - -struct pkt_header { - uint32_t len; - int32_t req; -} __attribute__((packed)); - -enum muxrpc_type { - muxrpc_type_async, - muxrpc_type_source, - muxrpc_type_sink, - muxrpc_type_duplex, -}; - -enum stream_state { - stream_state_open, - stream_state_ended_ok, - stream_state_ended_error, -}; - -enum ip_family { - ip_family_ipv4 = AF_INET, - ip_family_ipv6 = AF_INET6, - ip_family_any = AF_UNSPEC -}; - -static unsigned char zeros[24] = {0}; - -static const unsigned char ssb_cap[] = { - 0xd4, 0xa1, 0xcb, 0x88, 0xa6, 0x6f, 0x02, 0xf8, - 0xdb, 0x63, 0x5c, 0xe2, 0x64, 0x41, 0xcc, 0x5d, - 0xac, 0x1b, 0x08, 0x42, 0x0c, 0xea, 0xac, 0x23, - 0x08, 0x39, 0xb7, 0x55, 0x84, 0x5a, 0x9f, 0xfb -}; - -struct termios orig_tc; - -static void reset_termios() { - int rc = tcsetattr(STDIN_FILENO, TCSANOW, &orig_tc); - if (rc < 0) warn("tcsetattr"); -} - -static void usage() { - fputs("usage: sbotc [-j] [-T] [-l] [-r] [-e]\n" - " [ -n | [-c ] [-k ] [-K ] ]\n" - " [ [-s ] [-p ] [ -4 | -6 ] | [-u ] ]\n" - " [ -a | [-t ] [...] ]\n", stderr); - exit(EXIT_FAILURE); -} - -static int connect_localhost(const char *port, enum ip_family ip_family) { - int rc, family, fd, err; - struct ifaddrs *ifaddr, *ifa; - rc = getifaddrs(&ifaddr); - if (rc < 0) return -1; - int port_n = htons(atoi(port)); - - for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) { - if (ifa->ifa_addr == NULL) continue; - family = ifa->ifa_addr->sa_family; - socklen_t addrlen; - if (family == AF_INET) { - if (ip_family != ip_family_ipv4 && ip_family != ip_family_any) continue; - addrlen = sizeof(struct sockaddr_in); - struct sockaddr_in *addr = (struct sockaddr_in *)ifa->ifa_addr; - addr->sin_port = port_n; - } else if (family == AF_INET6) { - if (ip_family != ip_family_ipv6 && ip_family != ip_family_any) continue; - addrlen = sizeof(struct sockaddr_in6); - struct sockaddr_in6 *addr = (struct sockaddr_in6 *)ifa->ifa_addr; - addr->sin6_port = port_n; - } else continue; - fd = socket(family, SOCK_STREAM, IPPROTO_TCP); - if (fd < 0) continue; - if (connect(fd, ifa->ifa_addr, addrlen) == 0) break; - err = errno; - close(fd); - errno = err; - } - if (ifa == NULL) fd = -1; - - freeifaddrs(ifaddr); - return fd; -} - -static int tcp_connect(const char *host, const char *port, enum ip_family ip_family) { - struct addrinfo hints; - struct addrinfo *result, *rp; - int s; - int fd; - int err; - - memset(&hints, 0, sizeof(hints)); - hints.ai_family = ip_family; - hints.ai_protocol = IPPROTO_TCP; - - s = getaddrinfo(host, port, &hints, &result); - if (s < 0) errx(1, "unable to resolve host: %s", gai_strerror(s)); - - for (rp = result; rp; rp = rp->ai_next) { - fd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); - if (fd < 0) continue; - if (connect(fd, rp->ai_addr, rp->ai_addrlen) == 0) break; - err = errno; - close(fd); - errno = err; - } - if (rp == NULL) fd = -1; - - freeaddrinfo(result); - - if (fd == -1 && errno == ECONNREFUSED && (host == NULL || !strcmp(host, "localhost"))) { - return connect_localhost(port, ip_family); - } - - return fd; -} - -static int unix_connect(const char *path) { - struct sockaddr_un name; - const size_t path_len = strlen(path); - int s, rc; - if (path_len >= sizeof(name.sun_path)-1) errx(1, "socket path too long"); - s = socket(AF_UNIX, SOCK_STREAM, 0); - if (s < 0) return -1; - memset(&name, 0, sizeof(struct sockaddr_un)); - name.sun_family = AF_UNIX; - strncpy(name.sun_path, path, sizeof(name.sun_path) - 1); - rc = connect(s, (const struct sockaddr *)&name, sizeof name); - if (rc < 0) return -1; - return s; -} - -static int get_socket_path(char *buf, size_t len, const char *app_dir) { - struct stat st; - int sz = snprintf(buf, len-1, "%s/%s", app_dir, "socket"); - if (sz < 0 || sz >= (int)len-1) err(1, "failed to get socket path"); - int rc = stat(buf, &st); - if (rc < 0) return -1; - if (!(st.st_mode & S_IFSOCK)) { errno = EINVAL; return -1; } - return 0; -} - -static int read_all(int fd, void *buf, size_t count) { - ssize_t nbytes; - while (count > 0) { - nbytes = read(fd, buf, count); - if (nbytes == 0) { errno = EPIPE; return -1; } - if (nbytes < 0 && errno == EINTR) continue; - if (nbytes < 0) return -1; - buf += nbytes; - count -= nbytes; - } - return 0; -} - -static int read_some(int fd, unsigned char *buf, size_t *lenp) { - ssize_t nbytes; - do nbytes = read(fd, buf, *lenp); - while (nbytes < 0 && errno == EINTR); - if (nbytes == 0) { errno = EPIPE; return -1; } - if (nbytes < 0) return -1; - *lenp = nbytes; - return 0; -} - -static int write_all(int fd, const void *buf, size_t count) { - ssize_t nbytes; - while (count > 0) { - nbytes = write(fd, buf, count); - if (nbytes < 0 && errno == EINTR) continue; - if (nbytes < 0) return -1; - buf += nbytes; - count -= nbytes; - } - return 0; -} - -static 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) { - int rc; - unsigned char local_app_mac[32], remote_app_mac[32]; - - unsigned char kx_pk[32], kx_sk[32]; - rc = crypto_box_keypair(kx_pk, kx_sk); - if (rc < 0) errx(1, "failed to generate auth keypair"); - - rc = crypto_auth(local_app_mac, kx_pk, 32, appkey); - if (rc < 0) err(1, "failed to generate app mac"); - - // send challenge - unsigned char buf[64]; - memcpy(buf, local_app_mac, 32); - memcpy(buf+32, kx_pk, 32); - rc = write_all(outfd, buf, sizeof(buf)); - if (rc < 0) err(1, "failed to send challenge"); - - // recv challenge - unsigned char remote_kx_pk[32]; - rc = read_all(infd, buf, sizeof(buf)); - if (rc < 0) err(1, "challenge not accepted"); - memcpy(remote_app_mac, buf, 32); - memcpy(remote_kx_pk, buf+32, 32); - rc = crypto_auth_verify(buf, remote_kx_pk, 32, appkey); - if (rc < 0) errx(1, "wrong protocol (version?)"); - - // send auth - - unsigned char secret[32]; - rc = crypto_scalarmult(secret, kx_sk, remote_kx_pk); - if (rc < 0) errx(1, "failed to derive shared secret"); - - unsigned char remote_pk_curve[32]; - rc = crypto_sign_ed25519_pk_to_curve25519(remote_pk_curve, server_pubkey); - if (rc < 0) errx(1, "failed to curvify remote public key"); - - unsigned char a_bob[32]; - rc = crypto_scalarmult(a_bob, kx_sk, remote_pk_curve); - if (rc < 0) errx(1, "failed to derive a_bob"); - - unsigned char secret2a[96]; - memcpy(secret2a, appkey, 32); - memcpy(secret2a+32, secret, 32); - memcpy(secret2a+64, a_bob, 32); - - unsigned char secret2[32]; - rc = crypto_hash_sha256(secret2, secret2a, sizeof(secret2a)); - if (rc < 0) errx(1, "failed to hash secret2"); - - unsigned char shash[32]; - rc = crypto_hash_sha256(shash, secret, sizeof(secret)); - if (rc < 0) errx(1, "failed to hash secret"); - - unsigned char signed1[96]; - memcpy(signed1, appkey, 32); - memcpy(signed1+32, server_pubkey, 32); - memcpy(signed1+64, shash, 32); - - unsigned char sig[64]; - rc = crypto_sign_detached(sig, NULL, signed1, sizeof(signed1), seckey); - if (rc < 0) errx(1, "failed to sign inner hello"); - - unsigned char hello[96]; - memcpy(hello, sig, 64); - memcpy(hello+64, pubkey, 32); - - unsigned char boxed_auth[112]; - rc = crypto_secretbox_easy(boxed_auth, hello, sizeof(hello), zeros, secret2); - if (rc < 0) errx(1, "failed to box hello"); - - rc = write_all(outfd, boxed_auth, sizeof(boxed_auth)); - if (rc < 0) errx(1, "failed to send auth"); - - // verify accept - - unsigned char boxed_okay[80]; - rc = read_all(infd, boxed_okay, sizeof(boxed_okay)); - if (rc < 0) err(1, "hello not accepted"); - - unsigned char local_sk_curve[32]; - rc = crypto_sign_ed25519_sk_to_curve25519(local_sk_curve, seckey); - if (rc < 0) errx(1, "failed to curvify local secret key"); - - unsigned char b_alice[32]; - rc = crypto_scalarmult(b_alice, local_sk_curve, remote_kx_pk); - if (rc < 0) errx(1, "failed to derive b_alice"); - - unsigned char secret3a[128]; - memcpy(secret3a, appkey, 32); - memcpy(secret3a+32, secret, 32); - memcpy(secret3a+64, a_bob, 32); - memcpy(secret3a+96, b_alice, 32); - - unsigned char secret3[32]; - rc = crypto_hash_sha256(secret3, secret3a, sizeof(secret3a)); - if (rc < 0) errx(1, "failed to hash secret3"); - - rc = crypto_secretbox_open_easy(sig, boxed_okay, sizeof(boxed_okay), zeros, secret3); - if (rc < 0) errx(1, "failed to unbox the okay"); - - unsigned char signed2[160]; - memcpy(signed2, appkey, 32); - memcpy(signed2+32, hello, 96); - memcpy(signed2+128, shash, 32); - - rc = crypto_sign_verify_detached(sig, signed2, sizeof(signed2), server_pubkey); - if (rc < 0) errx(1, "server not authenticated"); - - rc = crypto_hash_sha256(secret, secret3, 32); - if (rc < 0) errx(1, "failed to hash secret3"); - - unsigned char enc_key_hashed[64]; - memcpy(enc_key_hashed, secret, 32); - memcpy(enc_key_hashed+32, server_pubkey, 32); - rc = crypto_hash_sha256(bs->encrypt_key, enc_key_hashed, 64); - if (rc < 0) errx(1, "failed to hash the encrypt key"); - - unsigned char dec_key_hashed[64]; - memcpy(dec_key_hashed, secret, 32); - memcpy(dec_key_hashed+32, pubkey, 32); - rc = crypto_hash_sha256(bs->decrypt_key, dec_key_hashed, 64); - if (rc < 0) errx(1, "failed to hash the decrypt key"); - - memcpy(bs->nonce1, remote_app_mac, 24); - memcpy(bs->nonce2, remote_app_mac, 24); - memcpy(bs->rx_nonce, local_app_mac, 24); - - bs->rx_buf_pos = 0; - bs->rx_buf_len = 0; - bs->s = sfd; - bs->noauth = false; - bs->wrote_goodbye = false; -} - -static int pubkey_decode(const char *key_str, unsigned char key[32]) { - if (!key_str) { errno = EPROTO; return -1; } - if (!*key_str) { errno = EPROTO; return -1; } - if (*key_str == '@') key_str++; - size_t len = strlen(key_str); - if (len == 52 && strcmp(key_str+44, ".ed25519") == 0) {} - else if (len != 44) { errno = EMSGSIZE; return -1; } - return base64_decode(key_str, 44, key, 32); -} - -static int seckey_decode(const char *key_str, unsigned char key[64]) { - if (!key_str) { errno = EPROTO; return -1; } - if (!*key_str) { errno = EPROTO; return -1; } - if (*key_str == '@') key_str++; - size_t len = strlen(key_str); - if (len > 8 && memcmp(key_str + len - 8, ".ed25519", 8) == 0) len -= 8; - return base64_decode(key_str, len, key, 64); -} - -static jsmntok_t *json_lookup(const char *buf, jsmntok_t *tok, const char *prop, size_t prop_len) { - jsmntok_t *end = tok + tok->size + 1; - if (tok->type != JSMN_OBJECT) { errno = EPROTO; return NULL; } - tok++; - while (tok < end) { - if (tok + 1 >= end) { errno = EPROTO; return NULL; } - if (tok->type == JSMN_STRING - && tok->end - tok->start == (int)prop_len - && memcmp(buf + tok->start, prop, prop_len) == 0) - return tok + 1; - tok += tok->size + 1; - end += tok->size; - } - return NULL; -} - -static ssize_t json_get_value(const char *buf, const char *path, const char **value) { - static const int num_tokens = 1024; - jsmntok_t tokens[num_tokens], *tok = tokens; - jsmn_parser parser; - - jsmn_init(&parser); - switch (jsmn_parse(&parser, buf, tokens, num_tokens)) { - case JSMN_ERROR_NOMEM: errno = ENOMEM; return -1; - case JSMN_ERROR_INVAL: errno = EINVAL; return -1; - case JSMN_ERROR_PART: errno = EMSGSIZE; return -1; - case JSMN_SUCCESS: break; - default: errno = EPROTO; return -1; - } - - while (*path) { - const char *end = strchr(path, '.'); - size_t part_len = end ? (size_t)end - (size_t)path : strlen(path); - tok = json_lookup(buf, tok, path, part_len); - if (!tok) { errno = ENOMSG; return -1; } - path += part_len; - if (*path == '.') path++; - } - - *value = buf + tok->start; - return tok->end - tok->start; -} - -static void get_app_dir(char *dir, size_t len) { - const char *path, *home, *appname; - int rc; - path = getenv("ssb_path"); - if (path) { - if (strlen(path) > len) errx(1, "ssb_path too long"); - strncpy(dir, path, len); - return; - } - home = getenv("HOME"); - if (!home) home = "."; - appname = getenv("ssb_appname"); - if (!appname) appname = "ssb"; - rc = snprintf(dir, len, "%s/.%s", home, appname); - if (rc < 0) err(1, "failed to get app dir"); - if ((size_t)rc >= len) errx(1, "path to app dir too long"); -} - -static ssize_t read_file(char *buf, size_t len, const char *fmt, ...) { - va_list ap; - int rc; - struct stat st; - int fd; - - va_start(ap, fmt); - rc = vsnprintf(buf, len, fmt, ap); - va_end(ap); - if (rc < 0) return -1; - if ((size_t)rc >= len) { errno = ENAMETOOLONG; return -1; } - - rc = stat(buf, &st); - if (rc < 0) return -1; - if (st.st_size > (off_t)(len-1)) { errno = EMSGSIZE; return -1; } - - fd = open(buf, O_RDONLY); - if (fd < 0) return -1; - - rc = read_all(fd, buf, st.st_size); - if (rc < 0) return -1; - buf[st.st_size] = '\0'; - - close(fd); - return st.st_size; -} - - -static void read_private_key(const char *dir, unsigned char pk[64]) { - ssize_t len; - char buf[8192]; - const char *pk_b64; - int rc; - ssize_t key_len; - char *line; - - len = read_file(buf, sizeof(buf), "%s/secret", dir); - if (len < 0) err(1, "failed to read secret file"); - - // strip comments - for (line = buf; *line; ) { - if (*line == '#') while (*line && *line != '\n') *line++ = ' '; - else while (*line && *line++ != '\n'); - } - - key_len = json_get_value(buf, "private", &pk_b64); - if (key_len < 0) err(1, "unable to read private key"); - - if (key_len > 8 && memcmp(pk_b64 + key_len - 8, ".ed25519", 8) == 0) - key_len -= 8; - rc = base64_decode(pk_b64, key_len, pk, 64); - if (rc < 0) err(1, "unable to decode private key"); -} - -static void increment_nonce(uint8_t nonce[24]) { - int i; - for (i = 23; i >= 0 && nonce[i] == 0xff; i--) nonce[i] = 0; - if (i >= 0) nonce[i]++; -} - -static void bs_write_end_box(struct boxs *bs) { - unsigned char boxed[34]; - int rc = crypto_secretbox_easy(boxed, zeros, 18, bs->nonce1, bs->encrypt_key); - if (rc < 0) errx(1, "failed to box packet end header"); - increment_nonce(bs->nonce1); - increment_nonce(bs->nonce2); - rc = write_all(bs->s, boxed, 34); - if (rc < 0) err(1, "failed to write boxed end header"); -} - -static void bs_write_packet(struct boxs *bs, const unsigned char *buf, uint16_t len) { - size_t boxed_len = len + 34; - unsigned char boxed[boxed_len]; - increment_nonce(bs->nonce2); - int rc = crypto_secretbox_easy(boxed + 18, buf, len, bs->nonce2, bs->encrypt_key); - if (rc < 0) errx(1, "failed to box packet data"); - struct boxs_header header; - header.len = htons(len); - memcpy(header.mac, boxed + 18, 16); - rc = crypto_secretbox_easy(boxed, (unsigned char *)&header, 18, bs->nonce1, bs->encrypt_key); - if (rc < 0) errx(1, "failed to box packet header"); - increment_nonce(bs->nonce1); - increment_nonce(bs->nonce1); - increment_nonce(bs->nonce2); - rc = write_all(bs->s, boxed, boxed_len); - if (rc < 0) err(1, "failed to write boxed packet"); -} - -static void bs_end(struct boxs *bs) { - if (bs->wrote_goodbye) return; - bs->wrote_goodbye = true; - if (!bs->noauth) { - bs_write_end_box(bs); - } - shutdown(bs->s, SHUT_WR); -} - -static int bs_read_packet(struct boxs *bs, void *buf, size_t *lenp) { - int rc; - if (bs->noauth) { - rc = read_some(bs->s, buf, lenp); - if (rc < 0 && errno == EPIPE) return -1; - if (rc < 0) err(1, "failed to read packet data"); - return 0; - } - unsigned char boxed_header[34]; - struct boxs_header header; - rc = read_all(bs->s, boxed_header, 34); - if (rc < 0 && errno == EPIPE) errx(1, "unexpected end of parent stream"); - if (rc < 0) err(1, "failed to read boxed packet header"); - rc = crypto_secretbox_open_easy((unsigned char *)&header, boxed_header, 34, bs->rx_nonce, bs->decrypt_key); - if (rc < 0) errx(1, "failed to unbox packet header"); - increment_nonce(bs->rx_nonce); - if (header.len == 0 && !memcmp(header.mac, zeros, 16)) { errno = EPIPE; return -1; } - size_t len = ntohs(header.len); - if (len > BOXS_MAXLEN) errx(1, "received boxed packet too large"); - unsigned char boxed_data[len + 16]; - rc = read_all(bs->s, boxed_data + 16, len); - if (rc < 0) err(1, "failed to read boxed packet data"); - memcpy(boxed_data, header.mac, 16); - rc = crypto_secretbox_open_easy(buf, boxed_data, len+16, bs->rx_nonce, bs->decrypt_key); - if (rc < 0) errx(1, "failed to unbox packet data"); - increment_nonce(bs->rx_nonce); - *lenp = len; - return 0; -} - -static int bs_read(struct boxs *bs, char *buf, size_t len) { - if (bs->noauth) { - int rc = read_all(bs->s, buf, len); - if (rc < 0) err(1, "failed to read packet data"); - return 0; - } - size_t remaining; - while (len > 0) { - remaining = bs->rx_buf_len > len ? len : bs->rx_buf_len; - if (buf) memcpy(buf, bs->rx_buf + bs->rx_buf_pos, remaining); - bs->rx_buf_len -= remaining; - bs->rx_buf_pos += remaining; - len -= remaining; - buf += remaining; - if (len == 0) return 0; - if (bs_read_packet(bs, bs->rx_buf, &bs->rx_buf_len) < 0) return -1; - bs->rx_buf_pos = 0; - } - return 0; -} - -static enum stream_state bs_read_out_1(struct boxs *bs, int fd) { - size_t buf[4096]; - size_t len = sizeof(buf); - int rc; - rc = bs_read_packet(bs, buf, &len); - if (rc < 0 && errno == EPIPE) { - bs_end(bs); - return stream_state_ended_ok; - } - if (rc < 0) return stream_state_ended_error; - rc = write_all(fd, buf, len); - if (rc < 0) return stream_state_ended_error; - return stream_state_open; -} - -static int bs_read_out(struct boxs *bs, int fd, size_t len) { - size_t chunk; - char buf[4096]; - int rc; - while (len > 0) { - chunk = len > sizeof(buf) ? sizeof(buf) : len; - rc = bs_read(bs, buf, chunk); - if (rc < 0) return -1; - rc = write_all(fd, buf, chunk); - if (rc < 0) return -1; - len -= chunk; - } - return 0; -} - -static int bs_read_error(struct boxs *bs, int errfd, enum pkt_flags flags, size_t len, bool no_newline) { - // suppress printing "true" indicating end without error - if (flags & pkt_flags_json && len == 4) { - char buf[4]; - if (bs_read(bs, buf, 4) < 0) return -1; - if (strncmp(buf, "true", 4) == 0) { - return 0; - } - if (write_all(errfd, buf, 4) < 0) return -1; - } else { - if (bs_read_out(bs, errfd, len) < 0) return -1; - } - if (flags & (pkt_flags_json | pkt_flags_string) && !no_newline) { - if (write_buf(errfd, "\n") < 0) return -1; - } - return 1; -} - -static void bs_write(struct boxs *bs, const unsigned char *buf, size_t len) { - if (bs->noauth) { - int rc = write_all(bs->s, buf, len); - if (rc < 0) err(1, "failed to write packet"); - return; - } - while (len > 0) { - size_t l = len > BOXS_MAXLEN ? BOXS_MAXLEN : len; - bs_write_packet(bs, buf, l); - len -= l; - buf += l; - } -} - -static enum stream_state bs_write_in_1(struct boxs *bs, int fd) { - unsigned char buf[4096]; - ssize_t sz = read(fd, buf, sizeof(buf)); - if (sz < 0) err(1, "read"); - if (sz == 0) { - bs_end(bs); - return stream_state_ended_ok; - } - bs_write(bs, buf, sz); - return stream_state_open; -} - -static void ps_write(struct boxs *bs, const char *data, size_t len, enum pkt_type type, int req_id, bool stream, bool end) { - size_t out_len = 9 + len; - unsigned char out_buf[out_len]; - struct pkt_header header = {htonl(len), htonl(req_id)}; - out_buf[0] = (stream << 3) | (end << 2) | (type & 3); - memcpy(out_buf+1, &header, 8); - memcpy(out_buf+9, data, len); - bs_write(bs, out_buf, out_len); -} - -static void ps_goodbye(struct boxs *bs) { - if (bs->wrote_goodbye) return; - bs->wrote_goodbye = true; - bs_write(bs, zeros, 9); -} - -static int ps_read_header(struct boxs *bs, size_t *len, int *req_id, enum pkt_flags *flags) { - char buf[9]; - struct pkt_header header; - if (bs_read(bs, buf, sizeof(buf)) < 0) return -1; - memcpy(&header, buf+1, 8); - if (len) *len = ntohl(header.len); - if (req_id) *req_id = ntohl(header.req); - if (flags) *flags = buf[0]; - return 0; -} - -static void muxrpc_call(struct boxs *bs, const char *method, const char *argument, enum muxrpc_type type, const char *typestr, int req_id) { - char req[33792]; // 32768 max message value size + 1024 extra - ssize_t reqlen; - bool is_request = type == muxrpc_type_async; - - if (is_request) { - reqlen = snprintf(req, sizeof(req), - "{\"name\":%s,\"args\":%s}", - method, argument); - } else { - reqlen = snprintf(req, sizeof(req), - "{\"name\":%s,\"args\":%s,\"type\":\"%s\"}", - method, argument, typestr); - } - if (reqlen < 0) err(1, "failed to construct request"); - if ((size_t)reqlen >= sizeof(req)) errx(1, "request too large"); - - ps_write(bs, req, reqlen, pkt_type_json, req_id, !is_request, false); -} - -static int bs_passthrough(struct boxs *bs, int infd, int outfd) { - int rc; - fd_set rd; - int sfd = bs->s; - int maxfd = infd > sfd ? infd : sfd; - enum stream_state in = stream_state_open; - enum stream_state out = stream_state_open; - - while (out == stream_state_open) { - FD_ZERO(&rd); - if (in == stream_state_open) FD_SET(infd, &rd); - if (out == stream_state_open) FD_SET(sfd, &rd); - rc = select(maxfd + 1, &rd, 0, 0, NULL); - if (rc < 0) err(1, "select"); - if (FD_ISSET(infd, &rd)) in = bs_write_in_1(bs, infd); - if (FD_ISSET(sfd, &rd)) out = bs_read_out_1(bs, outfd); - } - - return in != stream_state_ended_error && out == stream_state_ended_ok ? 0 : - in == stream_state_ended_error || out == stream_state_ended_error ? 2 : 1; -} - -static void ps_reject(struct boxs *bs, size_t len, int32_t req, enum pkt_flags flags) { - // ignore the packet. if this is a request, the substream on the other end - // will just have to wait until the rpc connection closes. - (void)req; - (void)flags; - write_buf(STDERR_FILENO, "ignoring packet: "); - int rc = bs_read_out(bs, STDERR_FILENO, len); - if (rc < 0) err(1, "bs_read_out"); - write_buf(STDERR_FILENO, "\n"); -} - -static enum stream_state muxrpc_read_source_1(struct boxs *bs, int outfd, int req_id, bool no_newline) { - enum pkt_flags flags; - size_t len; - int32_t req; - int rc = ps_read_header(bs, &len, &req, &flags); - if (rc < 0) err(1, "ps_read_header"); - if (req == 0 && len == 0) { - if (bs->wrote_goodbye) return stream_state_ended_ok; - warnx("unexpected end of parent stream"); - return stream_state_ended_error; - } - if (req != -req_id) { - ps_reject(bs, len, req, flags); - return stream_state_open; - } - if (flags & pkt_flags_end) { - rc = bs_read_error(bs, STDERR_FILENO, flags, len, no_newline); - if (rc < 0) err(1, "bs_read_error"); - if (rc == 1) return stream_state_ended_error; - return stream_state_ended_ok; - } - rc = bs_read_out(bs, outfd, len); - if (rc < 0) err(1, "bs_read_out"); - if (flags & (pkt_flags_json | pkt_flags_string) && !no_newline) { - rc = write_buf(outfd, "\n"); - if (rc < 0) err(1, "write_buf"); - } - return stream_state_open; -} - -static int muxrpc_read_source(struct boxs *bs, int outfd, int req_id, bool no_newline) { - enum stream_state state; - while ((state = muxrpc_read_source_1(bs, outfd, req_id, no_newline)) == stream_state_open); - return state == stream_state_ended_ok ? 0 : - state == stream_state_ended_error ? 2 : 1; -} - -static int muxrpc_read_async(struct boxs *bs, int outfd, int req_id, bool no_newline) { - enum pkt_flags flags; - size_t len; - int32_t req; - int rc; - - while (1) { - rc = ps_read_header(bs, &len, &req, &flags); - if (rc < 0) err(1, "ps_read_header"); - if (req == -req_id) break; - if (req == 0 && len == 0) errx(1, "unexpected end of parent stream"); - ps_reject(bs, len, req, flags); - } - if (flags & pkt_flags_end) { - rc = bs_read_error(bs, STDERR_FILENO, flags, len, no_newline); - if (rc < 0) err(1, "bs_read_error"); - if (rc == 1) return 2; - return 1; - } - rc = bs_read_out(bs, outfd, len); - if (rc < 0) err(1, "bs_read_out"); - if (flags & (pkt_flags_json | pkt_flags_string) && !no_newline) { - rc = write_buf(outfd, "\n"); - if (rc < 0) err(1, "write_buf"); - } - return 0; -} - -static enum stream_state muxrpc_write_sink_1(struct boxs *bs, int infd, - enum pkt_type ptype, int req_id) { - char buf[4096]; - ssize_t sz = read(infd, buf, sizeof(buf)); - if (sz < 0) err(1, "read"); - if (sz == 0) { - ps_write(bs, "true", 4, pkt_type_json, req_id, true, true); - return stream_state_ended_ok; - } - ps_write(bs, buf, sz, ptype, req_id, true, false); - return stream_state_open; -} - -static enum stream_state muxrpc_write_sink_1_hashed(struct boxs *bs, int infd, - crypto_hash_sha256_state *hash_state, int req_id) { - int rc; - unsigned char buf[4096]; - ssize_t sz = read(infd, buf, sizeof(buf)); - if (sz < 0) err(1, "read"); - if (sz == 0) { - ps_write(bs, "true", 4, pkt_type_json, req_id, true, true); - return stream_state_ended_ok; - } - rc = crypto_hash_sha256_update(hash_state, buf, sz); - if (rc < 0) errx(1, "hash update failed"); - ps_write(bs, (char *)buf, sz, pkt_type_buffer, req_id, true, false); - return stream_state_open; -} - -static int muxrpc_write_sink(struct boxs *bs, int infd, enum pkt_type ptype, int req_id, bool no_newline) { - int rc; - fd_set rd; - int sfd = bs->s; - int maxfd = infd > sfd ? infd : sfd; - enum stream_state in = stream_state_open; - enum stream_state out = stream_state_open; - - while (out == stream_state_open) { - FD_ZERO(&rd); - if (in == stream_state_open) FD_SET(infd, &rd); - if (out == stream_state_open) FD_SET(sfd, &rd); - rc = select(maxfd + 1, &rd, 0, 0, NULL); - if (rc < 0) err(1, "select"); - if (FD_ISSET(infd, &rd)) in = muxrpc_write_sink_1(bs, infd, ptype, req_id); - if (FD_ISSET(sfd, &rd)) out = muxrpc_read_source_1(bs, -1, req_id, no_newline); - } - - return in == stream_state_ended_ok && out == stream_state_ended_ok ? 0 : - in == stream_state_ended_error || out == stream_state_ended_error ? 2 : 1; -} - -static int muxrpc_write_blob_add(struct boxs *bs, int infd, int outfd, int req_id, bool no_newline) { - int rc; - fd_set rd; - int sfd = bs->s; - int maxfd = infd > sfd ? infd : sfd; - enum stream_state in = stream_state_open; - enum stream_state out = stream_state_open; - crypto_hash_sha256_state hash_state; - unsigned char hash[32]; - char id[54] = "&"; - - rc = crypto_hash_sha256_init(&hash_state); - if (rc < 0) { errno = EINVAL; return -1; } - - while (out == stream_state_open) { - FD_ZERO(&rd); - if (in == stream_state_open) FD_SET(infd, &rd); - if (out == stream_state_open) FD_SET(sfd, &rd); - rc = select(maxfd + 1, &rd, 0, 0, NULL); - if (rc < 0) err(1, "select"); - if (FD_ISSET(infd, &rd)) in = muxrpc_write_sink_1_hashed(bs, infd, &hash_state, req_id); - if (FD_ISSET(sfd, &rd)) out = muxrpc_read_source_1(bs, -1, req_id, no_newline); - } - - rc = crypto_hash_sha256_final(&hash_state, hash); - if (rc < 0) errx(1, "hash finalize failed"); - - rc = base64_encode(hash, 32, id+1, sizeof(id)-1); - if (rc < 0) err(1, "encoding hash failed"); - strcpy(id + 45, ".sha256\n"); - rc = write_all(outfd, id, sizeof(id)-1); - if (rc < 0) err(1, "writing hash failed"); - - return in == stream_state_ended_ok && out == stream_state_ended_ok ? 0 : - in == stream_state_ended_error || out == stream_state_ended_error ? 2 : 1; -} - -static int muxrpc_duplex(struct boxs *bs, int infd, int outfd, enum pkt_type in_ptype, int req_id, bool no_newline) { - int rc; - fd_set rd; - int sfd = bs->s; - int maxfd = infd > sfd ? infd : sfd; - enum stream_state in = stream_state_open; - enum stream_state out = stream_state_open; - - while (out == stream_state_open) { - FD_ZERO(&rd); - if (in == stream_state_open) FD_SET(infd, &rd); - if (out == stream_state_open) FD_SET(sfd, &rd); - rc = select(maxfd + 1, &rd, 0, 0, NULL); - if (rc < 0) err(1, "select"); - if (FD_ISSET(infd, &rd)) in = muxrpc_write_sink_1(bs, infd, in_ptype, req_id); - if (FD_ISSET(sfd, &rd)) out = muxrpc_read_source_1(bs, outfd, req_id, no_newline); - } - - return in == stream_state_ended_ok && out == stream_state_ended_ok ? 0 : - in == stream_state_ended_error || out == stream_state_ended_error ? 2 : 1; -} - -static int method_to_json(char *out, size_t outlen, const char *str) { - // blobs.get => ["blobs", "get"] - size_t i = 0; - char c; - if (i+2 > outlen) return -1; - out[i++] = '['; - out[i++] = '"'; - while ((c = *str++)) { - if (c == '.') { - if (i+3 > outlen) return -1; - out[i++] = '"'; - out[i++] = ','; - out[i++] = '"'; - } else if (c == '"') { - if (i+2 > outlen) return -1; - out[i++] = '\\'; - out[i++] = '"'; - } else { - if (i+1 > outlen) return -1; - out[i++] = c; - } - } - if (i+3 > outlen) return -1; - out[i++] = '"'; - out[i++] = ']'; - out[i++] = '\0'; - return i; -} - -static int args_to_json_length(int argc, char *argv[], bool encode_strings) { - int i = 0; - int len = 3; // "[]\0" - for (i = 0; i < argc; i++) { - if (!encode_strings) { - len += strlen(argv[i])+1; - } else { - len += 3; // "\"\"," - char *arg = argv[i], c; - while ((c = *arg++)) switch (c) { - case '"': len += 2; break; - case '\\': len += 2; break; - default: len++; - } - } - } - return len; -} - -static int args_to_json(char *out, size_t outlen, unsigned int argc, char *argv[], bool encode_strings) { - size_t i = 0; - size_t j; - if (i+1 > outlen) return -1; - out[i++] = '['; - for (j = 0; j < argc; j++) { - if (!encode_strings) { - size_t len = strlen(argv[j]); - if (j > 0) out[i++] = ','; - if (i+len > outlen) return -1; - strncpy(out+i, argv[j], len); - i += len; - } else { - char *arg = argv[j]; - char c; - if (j > 0) { - if (i+1 > outlen) return -1; - out[i++] = ','; - } - if (i+1 > outlen) return -1; - out[i++] = '"'; - while ((c = *arg++)) { - if (i+2 > outlen) return -1; - if (c == '"' || c == '\\') out[i++] = '\\'; - out[i++] = c; - } - if (i+1 > outlen) return -1; - out[i++] = '"'; - } - } - if (i+2 > outlen) return -1; - out[i++] = ']'; - out[i++] = '\0'; - return i; -} - -int main(int argc, char *argv[]) { - int i, s, infd, outfd, rc; - const char *key = NULL; - const char *keypair_seed_str = NULL; - const char *host = NULL; - const char *port = "8008"; - const char *typestr = NULL, *methodstr = NULL; - const char *shs_cap_key_str = NULL; - const char *socket_path = NULL; - size_t argument_len; - unsigned char private_key[64]; - unsigned char public_key[32]; - unsigned char remote_key[32]; - unsigned char shs_cap_key[32]; - enum muxrpc_type type; - enum pkt_type ptype = pkt_type_buffer; - char method[256]; - char app_dir[_POSIX_PATH_MAX]; - ssize_t len; - bool test = false; - bool noauth = false; - bool no_newline = false; - bool raw = false; - bool host_arg = false; - bool port_arg = false; - bool key_arg = false; - bool shs_cap_key_str_arg = false; - bool ipv4_arg = false; - bool ipv6_arg = false; - bool passthrough = false; - bool strings = false; - enum ip_family ip_family; - - get_app_dir(app_dir, sizeof(app_dir)); - - char config_buf[8192]; - len = read_file(config_buf, sizeof(config_buf), "%s/config", app_dir); - if (len > 0) { - ssize_t key_len = json_get_value(config_buf, "key", &key); - ssize_t host_len = json_get_value(config_buf, "host", &host); - ssize_t port_len = json_get_value(config_buf, "port", &port); - ssize_t shs_cap_len = json_get_value(config_buf, "caps.shs", &shs_cap_key_str); - if (key_len >= 0) ((char *)key)[key_len] = '\0'; - if (host_len >= 0) ((char *)host)[host_len] = '\0'; - if (port_len >= 0) ((char *)port)[port_len] = '\0'; - if (shs_cap_len >= 0) ((char *)shs_cap_key_str)[shs_cap_len] = '\0'; - } else if (len < 0 && errno != ENOENT) { - err(1, "failed to read config"); - } - - for (i = 1; i < argc && (argv[i][0] == '-'); i++) { - switch (argv[i][1]) { - case 'c': shs_cap_key_str = argv[++i]; shs_cap_key_str_arg = true; break; - case 'j': ptype = pkt_type_json; break; - case 'T': test = true; break; - case 's': host = argv[++i]; host_arg = true; break; - case 'k': key = argv[++i]; key_arg = true; break; - case 'K': keypair_seed_str = argv[++i]; break; - case 'p': port = argv[++i]; port_arg = true; break; - case 'u': socket_path = argv[++i]; break; - case 't': typestr = argv[++i]; break; - case 'n': noauth = true; break; - case '4': ipv4_arg = true; break; - case '6': ipv6_arg = true; break; - case 'a': passthrough = true; break; - case 'l': no_newline = true; break; - case 'r': raw = true; no_newline = true; break; - case 'e': strings = true; break; - default: usage(); - } - } - if (i < argc) methodstr = argv[i++]; - else if (!test && !passthrough) usage(); - - if (ipv4_arg && ipv6_arg) errx(1, "options -4 and -6 conflict"); - ip_family = - ipv4_arg ? ip_family_ipv4 : - ipv6_arg ? ip_family_ipv6 : - ip_family_any; - - if (shs_cap_key_str) { - rc = pubkey_decode(shs_cap_key_str, shs_cap_key); - if (rc < 0) err(1, "unable to decode cap key '%s'", shs_cap_key_str); - } else { - memcpy(shs_cap_key, ssb_cap, 32); - } - - argument_len = test ? 0 : args_to_json_length(argc-i, argv+i, strings); - char argument[argument_len]; - - if (passthrough) { - if (methodstr) errx(1, "-a option conflicts with method"); - if (typestr) errx(1, "-a option conflicts with -t option"); - if (argc-i > 0) errx(1, "-a option conflicts with method arguments"); - if (test) errx(1, "-a option conflicts with -T test"); - - } else if (!test) { - rc = args_to_json(argument, sizeof(argument), argc-i, argv+i, strings); - if (rc < 0) errx(1, "unable to collect arguments"); - - char manifest_buf[8192]; - if (!typestr) { - len = read_file(manifest_buf, sizeof(manifest_buf), - "%s/manifest.json", app_dir); - if (len < 0) err(1, "failed to read manifest file"); - - ssize_t type_len = json_get_value(manifest_buf, methodstr, &typestr); - if (!typestr && errno == ENOMSG) errx(1, - "unable to find method '%s' in manifest", methodstr); - if (!typestr) err(1, "unable to read manifest %s/%s", manifest_buf, methodstr); - ((char *)typestr)[type_len] = '\0'; - } - if (strcmp(typestr, "sync") == 0) type = muxrpc_type_async; - else if (strcmp(typestr, "async") == 0) type = muxrpc_type_async; - else if (strcmp(typestr, "sink") == 0) type = muxrpc_type_sink; - else if (strcmp(typestr, "source") == 0) type = muxrpc_type_source; - else if (strcmp(typestr, "duplex") == 0) type = muxrpc_type_duplex; - else errx(1, "type must be one of "); - - rc = method_to_json(method, sizeof(method), methodstr); - if (rc < 0) errx(0, "unable to convert method name"); - } - - if (keypair_seed_str == NULL) { - read_private_key(app_dir, private_key); - memcpy(public_key, private_key+32, 32); - } else if (strlen(keypair_seed_str) > 55) { - rc = seckey_decode(keypair_seed_str, private_key); - if (rc < 0) err(1, "unable to decode private key"); - memcpy(public_key, private_key+32, 32); - } else if (keypair_seed_str) { - unsigned char seed[crypto_sign_SEEDBYTES]; - unsigned char ed25519_skpk[crypto_sign_ed25519_SECRETKEYBYTES]; - - rc = pubkey_decode(keypair_seed_str, ed25519_skpk); - if (rc < 0) err(1, "unable to decode private key"); - rc = crypto_sign_ed25519_sk_to_seed(seed, ed25519_skpk); - if (rc < 0) err(1, "unable to convert private key to seed"); - rc = crypto_sign_seed_keypair(public_key, private_key, seed); - if (rc < 0) err(1, "unable to generate keypair from seed"); - } - - if (key) { - rc = pubkey_decode(key, remote_key); - if (rc < 0) err(1, "unable to decode remote key '%s'", key); - } else { - memcpy(remote_key, public_key, 32); - } - - bool implied_tcp = host_arg || port_arg || ipv4_arg || ipv6_arg; - bool implied_auth = key_arg || keypair_seed_str || shs_cap_key_str_arg || test; - - if (test) { - infd = STDIN_FILENO; - outfd = STDOUT_FILENO; - s = -1; - - } else if (socket_path) { - if (implied_tcp) errx(1, "-u option conflicts with host/port options"); - s = unix_connect(socket_path); - if (s < 0) err(1, "unix_connect"); - infd = outfd = s; - - } else if (!implied_tcp && !implied_auth) { - char socket_path_buf[_POSIX_PATH_MAX]; - rc = get_socket_path(socket_path_buf, sizeof(socket_path_buf), app_dir); - if (rc < 0 && noauth) err(1, "get_socket_path"); - if (rc < 0) goto do_tcp_connect; - s = unix_connect(socket_path_buf); - if (s < 0 && noauth) err(1, "unix_connect"); - if (s < 0) goto do_tcp_connect; - noauth = true; - infd = outfd = s; - - } else { -do_tcp_connect: - s = tcp_connect(host, port, ip_family); - if (s < 0) err(1, "tcp_connect"); - infd = outfd = s; - } - - struct boxs bs; - if (noauth) { - bs.s = s; - bs.noauth = true; - if (implied_auth) errx(1, "-n option conflicts with -k, -K, -c and -T options."); - } else { - shs_connect(s, infd, outfd, public_key, private_key, shs_cap_key, remote_key, &bs); - } - - if (test) { - rc = write_all(outfd, bs.encrypt_key, sizeof(bs.encrypt_key)); - rc |= write_all(outfd, bs.nonce1, sizeof(bs.nonce1)); - rc |= write_all(outfd, bs.decrypt_key, sizeof(bs.decrypt_key)); - rc |= write_all(outfd, bs.rx_nonce, sizeof(bs.rx_nonce)); - if (rc < 0) err(1, "failed to write handshake result"); - return 0; - } - - if (passthrough) { - rc = bs_passthrough(&bs, STDIN_FILENO, STDOUT_FILENO); - close(s); - return rc; - } - - muxrpc_call(&bs, method, argument, type, typestr, 1); - - if (raw) { - struct termios raw_tc; - rc = tcgetattr(STDIN_FILENO, &orig_tc); - if (rc < 0) warn("tcgetattr"); - raw_tc = orig_tc; - raw_tc.c_lflag &= ~(ICANON | ECHO); - rc = tcsetattr(STDIN_FILENO, TCSANOW, &raw_tc); - if (rc < 0) warn("tcgetattr"); - rc = atexit(reset_termios); - if (rc < 0) warn("atexit"); - } - - switch (type) { - case muxrpc_type_async: - rc = muxrpc_read_async(&bs, STDOUT_FILENO, 1, no_newline); - break; - case muxrpc_type_source: - rc = muxrpc_read_source(&bs, STDOUT_FILENO, 1, no_newline); - break; - case muxrpc_type_sink: - if (!strcmp(methodstr, "blobs.add")) { - rc = muxrpc_write_blob_add(&bs, STDIN_FILENO, STDOUT_FILENO, 1, no_newline); - } else { - rc = muxrpc_write_sink(&bs, STDIN_FILENO, ptype, 1, no_newline); - } - break; - case muxrpc_type_duplex: - rc = muxrpc_duplex(&bs, STDIN_FILENO, STDOUT_FILENO, ptype, 1, no_newline); - break; - } - - ps_goodbye(&bs); - bs_end(&bs); - close(s); - return rc; -} diff --git a/.install/sbotc/test-shs-inner.sh b/.install/sbotc/test-shs-inner.sh deleted file mode 100755 index 622e241..0000000 --- a/.install/sbotc/test-shs-inner.sh +++ /dev/null @@ -1,8 +0,0 @@ -#!/bin/sh -cap_hex=${1?shs cap key} -pk_hex=${2?server public key} - -cap_b64="$(echo -n "$cap_hex" | xxd -r -p | base64)" -pk_b64="$(echo -n "$pk_hex" | xxd -r -p | base64)" - -exec sbotc -T -c "$cap_b64" -k "$pk_b64" diff --git a/.install/templates/ipfs/ipfs b/.install/templates/ipfs/ipfs-initV.sh similarity index 100% rename from .install/templates/ipfs/ipfs rename to .install/templates/ipfs/ipfs-initV.sh diff --git a/git-update.sh b/git-update.sh index f8a21d6..2113c2b 100755 --- a/git-update.sh +++ b/git-update.sh @@ -1,5 +1,6 @@ #!/bin/bash +{ # this ensures the entire script is downloaded # # Check if astroport folder exist [[ -d astroport ]] && rm -rf astroport && echo "astroport folder has been deleted." @@ -27,3 +28,4 @@ echo "Astroport updated!" echo "Astroport ready to go!" exit 0 +} # this ensures the entire script is downloaded # diff --git a/install.sh b/install.sh index fb6b61c..57a1148 100755 --- a/install.sh +++ b/install.sh @@ -55,7 +55,7 @@ $MY_PATH/.install/export_colors.sh # Install IPFS ipfs() { - source .install/ipfs.sh + source .install/ipfs_alone.sh } # Install ScuttleButt source .install/scuttlebutt.sh