Skip to content

Commit 5ba1143

Browse files
committed
tools/rtrclient: implement ssh password auth
1 parent d3da6a1 commit 5ba1143

File tree

2 files changed

+117
-9
lines changed

2 files changed

+117
-9
lines changed

tools/rtrclient.1

+12-2
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ rtrclient \- rtr rpki client
2424
.IR HOST
2525
.IR PORT
2626
.IR USERNAME
27-
.IR PRIVATE_KEY
27+
(\fIPRIVATE_KEY\fR|\fIPASSWORD\fR)
2828
[\fIHOST_KEY\fR]
2929
.SH DESCRIPTION
3030
\fBrtrclient\fR connects to an RPKI/RTR cache server and prints prefix, origin AS, and router key updates.
@@ -33,7 +33,8 @@ The amount is not limited and different transport types can be mixed arbitrarily
3333
.LP
3434
For \fBtcp\fR you must specify the \fIHOST\fR and \fIPORT\fR.
3535
.LP
36-
For \fBssh\fR you must specify the \fIHOST\fR, \fIPORT\fR, \fIUSERNAME\fR and a file containing the \fIPRIVATE_KEY\fR.
36+
For \fBssh\fR you must specify the \fIHOST\fR, \fIPORT\fR, \fIUSERNAME\fR and a file containing the \fIPRIVATE_KEY\fR or a \fIPASSWORD\fR.
37+
By default the rtrclient will try to guess which of the two was entered. If you want to explicitly specify this see \fB-w\fR and \fB-s\fR.
3738
You may specify a file containing a list of \fIHOST_KEY\fRs, in the well known
3839
.B SSH_KNOWN_HOSTS
3940
file format. See \fIsshd(8)\fR for details.
@@ -53,6 +54,7 @@ Print information about router key updates
5354
\fB-p\fR
5455
.RS 4
5556
Print information about prefix and origin AS updates
57+
.RE
5658
\fB-s\fR
5759
.RS 4
5860
Print information about connection status updates
@@ -72,6 +74,14 @@ Print available templates and exit. Prints specified templated, when used with -
7274
\fB-o\fR
7375
.RS 4
7476
Output file for export
77+
.RE
78+
\fB-w\fR
79+
.RS 4
80+
force ssh authentication information to be interpreted as a password
81+
.RE
82+
\fB-s\fR
83+
.RS 4
84+
force ssh authentication information to be interpreted as a private key
7585
.SH TEMPLATES
7686
Templates can be used to export ROA information in a custom format. They are written in the \fBmustache\fR(\fIhttps://mustache.github.io/\fR) templating language.
7787

tools/rtrclient.c

+105-7
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,20 @@
2929
#include <sys/types.h>
3030
#include <unistd.h>
3131

32+
#ifdef RTRLIB_HAVE_LIBSSH
33+
// constants for utf8 validation
34+
static const uint8_t UTF8_ONE_BYTE_PREFIX = 0b00000000;
35+
static const uint8_t UTF8_ONE_BYTE_MASK = 0b10000000;
36+
static const uint8_t UTF8_TWO_BYTE_PREFIX = 0b11000000;
37+
static const uint8_t UTF8_TWO_BYTE_MASK = 0b11100000;
38+
static const uint8_t UTF8_THREE_BYTE_PREFIX = 0b11100000;
39+
static const uint8_t UTF8_THREE_BYTE_MASK = 0b11110000;
40+
static const uint8_t UTF8_FOUR_BYTE_PREFIX = 0b11110000;
41+
static const uint8_t UTF8_FOUR_BYTE_MASK = 0b11111000;
42+
static const uint8_t UTF8_SUBSEQUENT_BYTE_PREFIX = 0b10000000;
43+
static const uint8_t UTF8_SUBSEQUENT_BYTE_MASK = 0b11000000;
44+
#endif
45+
3246
__attribute__((format(printf, 1, 2), noreturn)) static void print_error_exit(const char *fmt, ...);
3347

3448
static bool is_readable_file(const char *str);
@@ -53,6 +67,9 @@ struct socket_config {
5367
char *ssh_username;
5468
char *ssh_private_key;
5569
char *ssh_host_key;
70+
char *ssh_password;
71+
bool force_password;
72+
bool force_key;
5673
#endif
5774
};
5875

@@ -230,6 +247,44 @@ static bool is_readable_file(const char *str)
230247
return is_file(str);
231248
}
232249

250+
#ifdef RTRLIB_HAVE_LIBSSH
251+
static bool is_utf8(const char *str)
252+
{
253+
size_t len = strlen(str);
254+
size_t i = 0;
255+
256+
while (i < len) {
257+
// check if current byte is a single utf8 char
258+
if ((str[i] & UTF8_ONE_BYTE_MASK) == UTF8_ONE_BYTE_PREFIX) {
259+
i += 1;
260+
261+
// check if current byte is the start of a two byte utf8 char and validate subsequent bytes
262+
} else if ((str[i] & UTF8_TWO_BYTE_MASK) == UTF8_TWO_BYTE_PREFIX && i + 1 < len &&
263+
(str[i + 1] & UTF8_SUBSEQUENT_BYTE_MASK) == UTF8_SUBSEQUENT_BYTE_PREFIX) {
264+
i += 2;
265+
266+
// check if current byte is the start of a three byte utf8 char and validate subsequent bytes
267+
} else if ((str[i] & UTF8_THREE_BYTE_MASK) == UTF8_THREE_BYTE_PREFIX && i + 2 < len &&
268+
(str[i + 1] & UTF8_SUBSEQUENT_BYTE_MASK) == UTF8_SUBSEQUENT_BYTE_PREFIX &&
269+
(str[i + 2] & UTF8_SUBSEQUENT_BYTE_MASK) == UTF8_SUBSEQUENT_BYTE_PREFIX) {
270+
i += 3;
271+
272+
// check if current byte is the start of a four byte utf8 char and validate subsequent bytes
273+
} else if ((str[i] & UTF8_FOUR_BYTE_MASK) == UTF8_FOUR_BYTE_PREFIX && i + 3 < len &&
274+
(str[i + 1] & UTF8_SUBSEQUENT_BYTE_MASK) == UTF8_SUBSEQUENT_BYTE_PREFIX &&
275+
(str[i + 2] & UTF8_SUBSEQUENT_BYTE_MASK) == UTF8_SUBSEQUENT_BYTE_PREFIX &&
276+
(str[i + 3] & UTF8_SUBSEQUENT_BYTE_MASK) == UTF8_SUBSEQUENT_BYTE_PREFIX) {
277+
i += 4;
278+
279+
// if none of the conditions matched. The string contains at least one byte that is not valid utf8
280+
} else {
281+
return false;
282+
}
283+
}
284+
return true;
285+
}
286+
#endif
287+
233288
struct exporter_state {
234289
bool roa_section;
235290
tommy_count_t current_roa;
@@ -359,11 +414,16 @@ static void print_usage(char **argv)
359414
printf("\nSocket:\n");
360415
printf(" tcp [-hpkb bindaddr] <host> <port>\n");
361416
#ifdef RTRLIB_HAVE_LIBSSH
362-
printf(" ssh [-hpkb bindaddr] <host> <port> <username> <private_key> [<host_key>]\n");
417+
printf(" ssh [-hpkb bindaddr] <host> <port> <username> (<private_key> | <password>) [<host_key>]\n");
363418
#endif
364419
printf("\nOptions:\n");
365420
printf("-b bindaddr Hostnamne or IP address to connect from\n\n");
366421

422+
#ifdef RTRLIB_HAVE_LIBSSH
423+
printf("-w force ssh authentication information to be interpreted as a password\n");
424+
printf("-r force ssh authentication information to be interpreted as a private key\n\n");
425+
#endif
426+
367427
printf("-k Print information about SPKI updates.\n");
368428
printf("-p Print information about PFX updates.\n");
369429
printf("-s Print information about connection status updates.\n\n");
@@ -536,7 +596,7 @@ static void parse_socket_opts(int argc, char **argv, struct socket_config *confi
536596
{
537597
int opt;
538598

539-
while ((opt = getopt(argc, argv, "+kphb:")) != -1) {
599+
while ((opt = getopt(argc, argv, "+kphwrb:")) != -1) {
540600
switch (opt) {
541601
case 'k':
542602
activate_spki_update_cb = true;
@@ -552,6 +612,22 @@ static void parse_socket_opts(int argc, char **argv, struct socket_config *confi
552612
config->bindaddr = optarg;
553613
break;
554614

615+
#ifdef RTRLIB_HAVE_LIBSSH
616+
case 'w':
617+
if (config->force_key)
618+
print_error_exit("-w and -r are mutually exclusive");
619+
620+
config->force_password = true;
621+
break;
622+
623+
case 'r':
624+
if (config->force_password)
625+
print_error_exit("-w and -r are mutually exclusive");
626+
627+
config->force_key = true;
628+
break;
629+
#endif
630+
555631
default:
556632
print_usage(argv);
557633
exit(EXIT_FAILURE);
@@ -590,7 +666,7 @@ static int parse_cli(int argc, char **argv)
590666
CLI_PARSE_STATE_SOCKET_SSH_HOST,
591667
CLI_PARSE_STATE_SOCKET_SSH_PORT,
592668
CLI_PARSE_STATE_SOCKET_SSH_USERNAME,
593-
CLI_PARSE_STATE_SOCKET_SSH_PRIVATE_KEY,
669+
CLI_PARSE_STATE_SOCKET_SSH_PRIVATE_KEY_PASSWORD,
594670
CLI_PARSE_STATE_SOCKET_SSH_HOST_KEY,
595671
#endif
596672
CLI_PARSE_STATE_END,
@@ -714,15 +790,36 @@ static int parse_cli(int argc, char **argv)
714790
case CLI_PARSE_STATE_SOCKET_SSH_USERNAME:
715791
current_config->ssh_username = argv[optind++];
716792

717-
state = CLI_PARSE_STATE_SOCKET_SSH_PRIVATE_KEY;
793+
state = CLI_PARSE_STATE_SOCKET_SSH_PRIVATE_KEY_PASSWORD;
718794
break;
719795

720-
case CLI_PARSE_STATE_SOCKET_SSH_PRIVATE_KEY:
721-
if (!is_readable_file(argv[optind]))
796+
case CLI_PARSE_STATE_SOCKET_SSH_PRIVATE_KEY_PASSWORD:
797+
if (current_config->force_key && !is_readable_file(argv[optind])) {
722798
print_error_exit("\"%s\" is not a readable file\n", argv[optind]);
723799

724-
current_config->ssh_private_key = argv[optind++];
800+
} else if (!current_config->force_password && !current_config->force_key &&
801+
is_readable_file(argv[optind])) {
802+
current_config->ssh_private_key = argv[optind];
725803

804+
} else if (!current_config->force_password && is_utf8(argv[optind])) {
805+
fprintf(stderr, "\"%s\" does not seem to be a file. Trying password authentication.\n",
806+
argv[optind]);
807+
fprintf(stderr, "Use -r to force key authentication or -w to silence this warning");
808+
current_config->ssh_password = argv[optind];
809+
810+
} else if (current_config->force_password && !is_utf8(argv[optind])) {
811+
print_error_exit("\"%s\" is not a valid utf8 string", argv[optind]);
812+
813+
} else if (current_config->force_password && !current_config->force_key &&
814+
is_utf8(argv[optind])) {
815+
current_config->ssh_password = argv[optind];
816+
817+
} else {
818+
print_error_exit("\"%s\" is neither a readable file nor a valid utf8 string",
819+
argv[optind]);
820+
}
821+
822+
++optind;
726823
state = CLI_PARSE_STATE_SOCKET_SSH_HOST_KEY;
727824
break;
728825

@@ -778,6 +875,7 @@ static void init_sockets(void)
778875
ssh_config.username = config->ssh_username;
779876
ssh_config.client_privkey_path = config->ssh_private_key;
780877
ssh_config.server_hostkey_path = config->ssh_host_key;
878+
ssh_config.password = config->ssh_password;
781879

782880
tr_ssh_init(&ssh_config, &config->tr_socket);
783881
config->socket.tr_socket = &config->tr_socket;

0 commit comments

Comments
 (0)