diff --git a/README.md b/README.md index 7cbe6f6b..bdd46a6f 100644 --- a/README.md +++ b/README.md @@ -258,6 +258,11 @@ Usage [-u] enable udprelay mode, TPROXY is required in redir mode + [-U] enable UDP relay and disable TCP relay, + not available in local mode + + [-A] enable onetime authentication + [-L :] specify destination server address and port for local port forwarding, only available in tunnel mode diff --git a/shadowsocks-libev.8 b/shadowsocks-libev.8 index 252e6470..2bfe08e6 100644 --- a/shadowsocks-libev.8 +++ b/shadowsocks-libev.8 @@ -96,6 +96,9 @@ Setup the name servers for libudns. The default server is fetched from .B \-u Enable UDP relay. .TP +.B \-A +Enable onetime authentication. +.TP .B \-v Enable verbose mode. .TP diff --git a/src/encrypt.c b/src/encrypt.c index fd404c07..b337817c 100644 --- a/src/encrypt.c +++ b/src/encrypt.c @@ -75,6 +75,7 @@ static uint8_t *enc_table; static uint8_t *dec_table; +static uint8_t auth_key[ONETIMEAUTH_KEYBYTES]; static uint8_t enc_key[MAX_KEY_LENGTH]; static int enc_key_len; static int enc_iv_len; @@ -1026,6 +1027,14 @@ static int cipher_context_update(cipher_ctx_t *ctx, uint8_t *output, int *olen, #endif } +int ss_onetimeauth(char *auth, char *msg, int msg_len) { + return crypto_onetimeauth((uint8_t *)auth, (uint8_t *)msg, msg_len, auth_key); +} + +int ss_onetimeauth_verify(char *auth, char *msg, int msg_len) { + return crypto_onetimeauth_verify((uint8_t *)auth, (uint8_t *)msg, msg_len, auth_key); +} + char * ss_encrypt_all(int buf_size, char *plaintext, ssize_t *len, int method) { if (method > TABLE) { @@ -1446,6 +1455,7 @@ int enc_init(const char *pass, const char *method) } else { enc_key_init(m, pass); } + crypto_generichash(auth_key, ONETIMEAUTH_KEYBYTES, (uint8_t *)pass, strlen(pass), NULL, 0); return m; } diff --git a/src/encrypt.h b/src/encrypt.h index df20c3a5..9ee37527 100644 --- a/src/encrypt.h +++ b/src/encrypt.h @@ -140,6 +140,9 @@ typedef struct { #define SALSA20 15 #define CHACHA20 16 +#define ONETIMEAUTH_BYTES 16U +#define ONETIMEAUTH_KEYBYTES 32U + #define min(a, b) (((a) < (b)) ? (a) : (b)) #define max(a, b) (((a) > (b)) ? (a) : (b)) @@ -161,4 +164,7 @@ int enc_get_iv_len(void); void cipher_context_release(cipher_ctx_t *evp); unsigned char *enc_md5(const unsigned char *d, size_t n, unsigned char *md); +int ss_onetimeauth(char *auth, char *msg, int msg_len); +int ss_onetimeauth_verify(char *auth, char *msg, int msg_len); + #endif // _ENCRYPT_H diff --git a/src/local.c b/src/local.c index c0388949..6ca85baa 100644 --- a/src/local.c +++ b/src/local.c @@ -94,6 +94,8 @@ static int nofile = 0; #endif #endif +static int auth = 0; + static void server_recv_cb(EV_P_ ev_io *w, int revents); static void server_send_cb(EV_P_ ev_io *w, int revents); static void remote_recv_cb(EV_P_ ev_io *w, int revents); @@ -470,6 +472,12 @@ static void server_recv_cb(EV_P_ ev_io *w, int revents) if (!remote->direct) { memcpy(remote->buf, ss_addr_to_send, addr_len); + + if (auth) { + ss_onetimeauth(remote->buf + addr_len, ss_addr_to_send, addr_len); + addr_len += ONETIMEAUTH_BYTES; + } + if (r > 0) { memcpy(remote->buf + addr_len, buf, r); } @@ -921,10 +929,10 @@ int main(int argc, char **argv) USE_TTY(); #ifdef ANDROID - while ((c = getopt_long(argc, argv, "f:s:p:l:k:t:m:i:c:b:a:uvV", + while ((c = getopt_long(argc, argv, "f:s:p:l:k:t:m:i:c:b:a:uvVA", long_options, &option_index)) != -1) { #else - while ((c = getopt_long(argc, argv, "f:s:p:l:k:t:m:i:c:b:a:uv", + while ((c = getopt_long(argc, argv, "f:s:p:l:k:t:m:i:c:b:a:uvA", long_options, &option_index)) != -1) { #endif switch (c) { @@ -979,6 +987,10 @@ int main(int argc, char **argv) case 'v': verbose = 1; break; + case 'A': + auth = 1; + LOGI("onetime authentication enabled"); + break; #ifdef ANDROID case 'V': vpn = 1; diff --git a/src/redir.c b/src/redir.c index 3fa2aee9..0fcfd0c3 100644 --- a/src/redir.c +++ b/src/redir.c @@ -83,6 +83,7 @@ static void close_and_free_server(EV_P_ struct server *server); int verbose = 0; static int mode = TCP_ONLY; +static int auth = 0; int getdestaddr(int fd, struct sockaddr_storage *destaddr) { @@ -376,6 +377,11 @@ static void remote_send_cb(EV_P_ ev_io *w, int revents) return; } + if (auth) { + ss_onetimeauth(ss_addr_to_send + addr_len, ss_addr_to_send, addr_len); + addr_len += ONETIMEAUTH_BYTES; + } + int s = send(remote->fd, ss_addr_to_send, addr_len, 0); free(ss_addr_to_send); @@ -614,7 +620,7 @@ int main(int argc, char **argv) opterr = 0; - while ((c = getopt(argc, argv, "f:s:p:l:k:t:m:c:b:a:uUv")) != -1) { + while ((c = getopt(argc, argv, "f:s:p:l:k:t:m:c:b:a:uUvA")) != -1) { switch (c) { case 's': if (remote_num < MAX_REMOTE_NUM) { @@ -659,6 +665,10 @@ int main(int argc, char **argv) case 'v': verbose = 1; break; + case 'A': + auth = 1; + LOGI("onetime authentication enabled"); + break; } } diff --git a/src/server.c b/src/server.c index 3c7e29a2..c10b8665 100644 --- a/src/server.c +++ b/src/server.c @@ -109,6 +109,7 @@ int verbose = 0; static int acl = 0; static int mode = TCP_ONLY; +static int auth = 0; static int fast_open = 0; #ifdef HAVE_SETRLIMIT @@ -639,6 +640,16 @@ static void server_recv_cb(EV_P_ ev_io *w, int revents) offset += 2; + if (auth) { + if (ss_onetimeauth_verify(server->buf + offset, server->buf, offset)) { + LOGE("authentication error %d", atyp); + report_addr(server->fd); + close_and_free_server(EV_A_ server); + return; + }; + offset += ONETIMEAUTH_BYTES; + } + if (verbose) { LOGI("connect to: %s:%d", host, ntohs(port)); } @@ -1193,7 +1204,7 @@ int main(int argc, char **argv) USE_TTY(); - while ((c = getopt_long(argc, argv, "f:s:p:l:k:t:m:c:i:d:a:uUv", + while ((c = getopt_long(argc, argv, "f:s:p:l:k:t:m:c:i:d:a:uUvA", long_options, &option_index)) != -1) { switch (c) { case 0: @@ -1250,6 +1261,10 @@ int main(int argc, char **argv) case 'v': verbose = 1; break; + case 'A': + auth = 1; + LOGI("onetime authentication enabled"); + break; } } diff --git a/src/tunnel.c b/src/tunnel.c index ee8b781a..d44a0e00 100644 --- a/src/tunnel.c +++ b/src/tunnel.c @@ -91,6 +91,7 @@ int vpn = 0; int verbose = 0; static int mode = TCP_ONLY; +static int auth = 0; #ifndef __MINGW32__ static int setnonblocking(int fd) @@ -418,6 +419,11 @@ static void remote_send_cb(EV_P_ ev_io *w, int revents) return; } + if (auth) { + ss_onetimeauth(ss_addr_to_send + addr_len, ss_addr_to_send, addr_len); + addr_len += ONETIMEAUTH_BYTES; + } + int s = send(remote->fd, ss_addr_to_send, addr_len, 0); free(ss_addr_to_send); @@ -668,9 +674,9 @@ int main(int argc, char **argv) USE_TTY(); #ifdef ANDROID - while ((c = getopt(argc, argv, "f:s:p:l:k:t:m:i:c:b:L:a:uUvV")) != -1) { + while ((c = getopt(argc, argv, "f:s:p:l:k:t:m:i:c:b:L:a:uUvVA")) != -1) { #else - while ((c = getopt(argc, argv, "f:s:p:l:k:t:m:i:c:b:L:a:uUv")) != -1) { + while ((c = getopt(argc, argv, "f:s:p:l:k:t:m:i:c:b:L:a:uUvA")) != -1) { #endif switch (c) { case 's': @@ -722,6 +728,10 @@ int main(int argc, char **argv) case 'v': verbose = 1; break; + case 'A': + auth = 1; + LOGI("onetime authentication enabled"); + break; #ifdef ANDROID case 'V': vpn = 1; diff --git a/src/utils.c b/src/utils.c index a78b1891..fd277d2c 100644 --- a/src/utils.c +++ b/src/utils.c @@ -247,6 +247,9 @@ void usage() printf( " not available in local mode\n"); printf("\n"); + printf( + " [-A] enable onetime authentication\n"); + printf("\n"); printf( " [-L :] specify destination server address and port\n"); printf(