Add listen mode

Close %ejwI9899mEhcIezmWQUKrubKHEfNaFpkmVVG6xOpUsY=.sha256
main
cel 5 years ago
parent cfb1d40d73
commit 14900624cc

@ -31,9 +31,13 @@ uninstall:
$(DESTDIR)$(BINDIR)/$(BIN) \
$(DESTDIR)$(MANDIR)/man1/$(BIN).1
test-shs1:
@# %lzzcAZlM21slUIoiH4yd/wgDnXu8raNLvwqjxqrU06k=.sha256
shs1testclient ./test-shs-inner.sh $(SHS1_TEST_SEED)
test-shs1: test-shs1-client test-shs1-server
test-shs1-client:
shs1testclient ./test-shs-client-inner.sh $(SHS1_TEST_SEED)
test-shs1-server:
shs1testserver ./test-shs-server-inner.sh $(SHS1_TEST_SEED)
clean:
@rm -vf $(BIN)

@ -30,6 +30,7 @@
|
.Fl 6
.Oc
.Op Fl d
|
.Op Fl u Ar socket_path
.Oc
@ -56,8 +57,7 @@ Raw mode. Disables stdin line buffering/editing and echoing. Implies
.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
Test using shs1-testsuite protocol. Instead of connecting to a peer and calling a RPC method, 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
@ -74,6 +74,8 @@ options have no effect and output a warning if used.
Connect to server over IPv4 only.
.It Fl 6
Connect to server over IPv6 only.
.It Fl d
Listen for an incoming connection instead of making an outgoing connection.
.It Fl c Ar cap
Capability key for secret-handshake. Default is SSB's capability key,
.Li 1KHLiKZvAvjbY1ziZEHMXawbCEIM6qwjCDm3VYRan/s= .
@ -90,7 +92,13 @@ 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.
private key file or the
.Fl K
option. In listen mode (
.Fl d
), if
.Fl k
is specified, only a connection from the given key is accepted; otherwise a connection from any key is accepted.
.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

@ -17,6 +17,7 @@
#include <limits.h>
#include <netdb.h>
#include <netinet/in.h>
#include <signal.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stdio.h>
@ -116,7 +117,7 @@ static void reset_termios() {
static void usage() {
fputs("usage: sbotc [-j] [-T] [-l] [-r] [-e]\n"
" [ -n | [-c <cap>] [-k <key>] [-K <keypair_seed>] ]\n"
" [ [-s <host>] [-p <port>] [ -4 | -6 ] | [-u <socket_path>] ]\n"
" [ [-s <host>] [-p <port>] [ -4 | -6 ] [-d] | [-u <socket_path>] ]\n"
" [ -a | [-t <type>] <method> [<argument>...] ]\n", stderr);
exit(EXIT_FAILURE);
}
@ -156,16 +157,17 @@ static int connect_localhost(const char *port, enum ip_family ip_family) {
return fd;
}
static int tcp_connect(const char *host, const char *port, enum ip_family ip_family) {
static int tcp_connect(const char *host, const char *port, enum ip_family ip_family, bool server) {
struct addrinfo hints;
struct addrinfo *result, *rp;
int s;
int fd;
int err;
int err, rc;
memset(&hints, 0, sizeof(hints));
hints.ai_family = ip_family;
hints.ai_protocol = IPPROTO_TCP;
if (server) hints.ai_flags = AI_PASSIVE;
s = getaddrinfo(host, port, &hints, &result);
if (s < 0) errx(1, "unable to resolve host: %s", gai_strerror(s));
@ -173,7 +175,15 @@ static int tcp_connect(const char *host, const char *port, enum ip_family ip_fam
for (rp = result; rp; rp = rp->ai_next) {
fd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
if (fd < 0) continue;
if (server) {
rc = setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &(int){1}, sizeof(int));
if (rc < 0) goto error;
if (bind(fd, rp->ai_addr, rp->ai_addrlen) < 0) goto error;
if (listen(fd, 1) == 0) break;
} else {
if (connect(fd, rp->ai_addr, rp->ai_addrlen) == 0) break;
}
error:
err = errno;
close(fd);
errno = err;
@ -182,14 +192,31 @@ static int tcp_connect(const char *host, const char *port, enum ip_family ip_fam
freeaddrinfo(result);
if (fd == -1 && errno == ECONNREFUSED && (host == NULL || !strcmp(host, "localhost"))) {
if (!server && fd == -1 && errno == ECONNREFUSED && (host == NULL || !strcmp(host, "localhost"))) {
return connect_localhost(port, ip_family);
}
if (server && fd > -1) {
int client = accept(fd, NULL, NULL);
err = errno;
close(fd);
errno = err;
return client;
}
return fd;
}
static int unix_connect(const char *path) {
static const char *socket_path = NULL;
void cleanup() {
if (socket_path != NULL) {
int rc = unlink(socket_path);
if (rc < 0) warn("unlink");
}
}
static int unix_connect(const char *path, bool server) {
struct sockaddr_un name;
const size_t path_len = strlen(path);
int s, rc;
@ -199,18 +226,38 @@ static int unix_connect(const char *path) {
memset(&name, 0, sizeof(struct sockaddr_un));
name.sun_family = AF_UNIX;
strncpy(name.sun_path, path, sizeof(name.sun_path) - 1);
if (server) {
rc = bind(s, (const struct sockaddr *)&name, sizeof name);
if (rc < 0) return -1;
rc = listen(s, 1);
} else {
rc = connect(s, (const struct sockaddr *)&name, sizeof name);
}
if (rc < 0) return -1;
if (server && rc > -1) {
socket_path = strdup(path);
if (atexit(cleanup) < 0) warn("atexit");
if (signal(SIGINT, exit) == SIG_ERR) warn("signal");
int client = accept(s, NULL, NULL);
int err = errno;
close(s);
errno = err;
return client;
}
return s;
}
static int get_socket_path(char *buf, size_t len, const char *app_dir) {
static int get_socket_path(char *buf, size_t len, const char *app_dir, bool server) {
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 (!server) {
if (rc < 0) return -1;
if (!(st.st_mode & S_IFSOCK)) { errno = EINVAL; return -1; }
}
return 0;
}
@ -249,11 +296,14 @@ static int write_all(int fd, const void *buf, size_t count) {
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) {
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 remote_pubkey[32], struct boxs *bs, bool client, bool specify_client_key) {
int rc;
unsigned char client_pubkey[32];
unsigned char local_app_mac[32], remote_app_mac[32];
unsigned char kx_pk[32], kx_sk[32], remote_kx_pk[32];
unsigned char buf[64];
unsigned char kx_pk[32], kx_sk[32];
if (client) {
rc = crypto_box_keypair(kx_pk, kx_sk);
if (rc < 0) errx(1, "failed to generate auth keypair");
@ -261,14 +311,25 @@ static void shs_connect(int sfd, int infd, int outfd, const unsigned char pubkey
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");
} else {
// recv challenge
rc = read_all(infd, buf, sizeof(buf));
if (rc < 0) err(1, "expected challenge");
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");
}
if (client) {
// 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);
@ -276,94 +337,185 @@ static void shs_connect(int sfd, int infd, int outfd, const unsigned char pubkey
rc = crypto_auth_verify(buf, remote_kx_pk, 32, appkey);
if (rc < 0) errx(1, "wrong protocol (version?)");
// send auth
} else {
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
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");
}
unsigned char remote_pk_curve[32];
unsigned char hello[96];
unsigned char secret2[32];
unsigned char boxed_auth[112];
unsigned char secret[32];
unsigned char a_bob[32];
unsigned char secret2a[96];
unsigned char shash[32];
unsigned char sig[64];
unsigned char signed1[96];
unsigned char local_sk_curve[32];
if (client) {
// send auth
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);
rc = crypto_sign_ed25519_pk_to_curve25519(remote_pk_curve, remote_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+32, remote_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
} else {
// read auth
rc = read_all(infd, boxed_auth, sizeof(boxed_auth));
if (rc < 0) err(1, "expected hello");
rc = crypto_scalarmult(secret, kx_sk, remote_kx_pk);
if (rc < 0) errx(1, "failed to derive shared secret");
rc = crypto_hash_sha256(shash, secret, sizeof(secret));
if (rc < 0) errx(1, "failed to hash secret");
rc = crypto_sign_ed25519_sk_to_curve25519(local_sk_curve, seckey);
if (rc < 0) errx(1, "failed to curvify local secret key");
rc = crypto_scalarmult(a_bob, local_sk_curve, remote_kx_pk);
if (rc < 0) errx(1, "failed to derive a_bob");
memcpy(secret2a, appkey, 32);
memcpy(secret2a+32, secret, 32);
memcpy(secret2a+64, a_bob, 32);
rc = crypto_hash_sha256(secret2, secret2a, sizeof(secret2a));
if (rc < 0) errx(1, "failed to hash secret2");
rc = crypto_secretbox_open_easy(hello, boxed_auth, sizeof(boxed_auth), zeros, secret2);
if (rc < 0) errx(1, "failed to unbox client hello");
memcpy(sig, hello, 64);
memcpy(client_pubkey, hello+64, 32);
memcpy(signed1, appkey, 32);
memcpy(signed1+32, pubkey, 32);
memcpy(signed1+64, shash, 32);
rc = crypto_sign_verify_detached(sig, signed1, sizeof(signed1), client_pubkey);
if (rc < 0) errx(1, "wrong number");
}
unsigned char boxed_okay[80];
unsigned char b_alice[32];
unsigned char secret3a[128];
unsigned char secret3[32];
unsigned char signed2[160];
if (client) {
// verify accept
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);
rc = crypto_sign_verify_detached(sig, signed2, sizeof(signed2), remote_pubkey);
if (rc < 0) errx(1, "server not authenticated");
} else {
if (specify_client_key && memcmp(client_pubkey, remote_pubkey, 32)) {
errx(1, "unexpected client");
}
// send accept
rc = crypto_sign_ed25519_pk_to_curve25519(remote_pk_curve, client_pubkey);
if (rc < 0) errx(1, "failed to curvify remote public key");
rc = crypto_scalarmult(b_alice, kx_sk, remote_pk_curve);
if (rc < 0) errx(1, "failed to derive b_alice");
memcpy(secret3a, appkey, 32);
memcpy(secret3a+32, secret, 32);
memcpy(secret3a+64, a_bob, 32);
memcpy(secret3a+96, b_alice, 32);
rc = crypto_hash_sha256(secret3, secret3a, sizeof(secret3a));
if (rc < 0) errx(1, "failed to hash secret3");
memcpy(signed2, appkey, 32);
memcpy(signed2+32, hello, 96);
memcpy(signed2+128, shash, 32);
rc = crypto_sign_detached(sig, NULL, signed2, sizeof(signed2), seckey);
if (rc < 0) errx(1, "failed to sign inner accept");
rc = crypto_secretbox_easy(boxed_okay, sig, sizeof(sig), zeros, secret3);
if (rc < 0) errx(1, "failed to box accept");
rc = write_all(outfd, boxed_okay, sizeof(boxed_okay));
if (rc < 0) errx(1, "failed to send accept");
}
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);
memcpy(enc_key_hashed+32, client ? remote_pubkey : client_pubkey, 32);
rc = crypto_hash_sha256(bs->encrypt_key, enc_key_hashed, 64);
if (rc < 0) errx(1, "failed to hash the encrypt key");
@ -1056,6 +1208,7 @@ int main(int argc, char *argv[]) {
bool ipv6_arg = false;
bool passthrough = false;
bool strings = false;
bool daemon = false;
enum ip_family ip_family;
get_app_dir(app_dir, sizeof(app_dir));
@ -1089,6 +1242,7 @@ int main(int argc, char *argv[]) {
case 'n': noauth = true; break;
case '4': ipv4_arg = true; break;
case '6': ipv6_arg = true; break;
case 'd': daemon = true; break;
case 'a': passthrough = true; break;
case 'l': no_newline = true; break;
case 'r': raw = true; no_newline = true; break;
@ -1184,24 +1338,24 @@ int main(int argc, char *argv[]) {
} else if (socket_path) {
if (implied_tcp) errx(1, "-u option conflicts with host/port options");
s = unix_connect(socket_path);
s = unix_connect(socket_path, daemon);
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);
rc = get_socket_path(socket_path_buf, sizeof(socket_path_buf), app_dir, daemon);
if (rc < 0 && noauth) err(1, "get_socket_path");
if (rc < 0) goto do_tcp_connect;
s = unix_connect(socket_path_buf);
if (rc < 0) goto do_tcp;
s = unix_connect(socket_path_buf, daemon);
if (s < 0 && noauth) err(1, "unix_connect");
if (s < 0) goto do_tcp_connect;
if (s < 0) goto do_tcp;
noauth = true;
infd = outfd = s;
} else {
do_tcp_connect:
s = tcp_connect(host, port, ip_family);
do_tcp:
s = tcp_connect(host, port, ip_family, daemon);
if (s < 0) err(1, "tcp_connect");
infd = outfd = s;
}
@ -1212,7 +1366,7 @@ do_tcp_connect:
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);
shs_connect(s, infd, outfd, public_key, private_key, shs_cap_key, remote_key, &bs, !daemon, key_arg);
}
if (test) {

@ -0,0 +1,8 @@
#!/bin/sh
cap_hex=${1?shs cap key}
sk_hex=${2?server secret key}
cap_b64="$(echo -n "$cap_hex" | xxd -r -p | base64)"
sk_b64="$(echo -n "$sk_hex" | xxd -r -p | base64 -w 0)"
exec sbotc -T -d -c "$cap_b64" -K "$sk_b64"
Loading…
Cancel
Save