aboutsummaryrefslogtreecommitdiff
path: root/ssh
diff options
context:
space:
mode:
authorTom Smeding <tom.smeding@gmail.com>2020-07-12 22:17:12 +0200
committerTom Smeding <tom.smeding@gmail.com>2020-07-12 22:17:12 +0200
commit62af60991cdf72c0023b5ab45e8670dfbdc7f5dd (patch)
treee7865bee28481397e18c1ac65b4a7e5b7782c683 /ssh
parent93967519fac05078bc0edf6b9fbd0aedefe8dca4 (diff)
tomsg_clientlib: Proper hostkey checking
Diffstat (limited to 'ssh')
-rw-r--r--ssh/client.c50
-rw-r--r--ssh/ssh_client.c6
-rw-r--r--ssh/sshnc.h2
-rw-r--r--ssh/tomsg_clientlib.c17
-rw-r--r--ssh/tomsg_clientlib.h12
5 files changed, 71 insertions, 16 deletions
diff --git a/ssh/client.c b/ssh/client.c
index 64f8f57..80e14a4 100644
--- a/ssh/client.c
+++ b/ssh/client.c
@@ -83,6 +83,38 @@ static void splice_scanned(struct readbuffer *rb) {
rb->newline_cursor = 0;
}
+static bool prompt_yn(struct readbuffer *rb, const char *text) {
+ printf("%s [y/n] ", text);
+ fflush(stdout);
+
+ while (true) {
+ struct string_view line = extract_line(rb);
+ if (line.s == NULL) {
+ read_more_data(STDIN_FILENO, rb);
+ continue;
+ }
+
+ splice_scanned(rb);
+
+ if (line.len <= 3) {
+ char buf[4];
+ memcpy(buf, line.s, line.len);
+ buf[line.len] = '\0';
+
+ if (strcmp(buf, "y") == 0 || strcmp(buf, "yes") == 0) {
+ return true;
+ }
+
+ if (strcmp(buf, "n") == 0 || strcmp(buf, "no") == 0) {
+ return false;
+ }
+ }
+
+ printf("Please answer with 'y', 'n', 'yes' or 'no'. [y/n] ");
+ fflush(stdout);
+ }
+}
+
static struct string_view tokenise_greedy(struct string_view *line) {
sv_skip_whitespace(line);
return sv_tokenise_word(line);
@@ -415,6 +447,17 @@ static void handle_event(struct state *state, const struct tomsg_event event) {
}
}
+static bool hostkey_checker(const unsigned char *hash, size_t length, void *stdinbuf_) {
+ struct readbuffer *stdinbuf = stdinbuf_;
+
+ const char *fingerprint = tomsg_print_hash(hash, length);
+
+ printf("Server host key fingerprint: %s\n", fingerprint);
+ return prompt_yn(stdinbuf,
+ "Does this hash match the one given to you by the server administrator, or by a\n"
+ "member that you trust and is already connected to the server?");
+}
+
int main(int argc, char **argv) {
if (argc != 2) {
printf("Usage: %s <hostname:port>\n", argv[0]);
@@ -428,10 +471,13 @@ int main(int argc, char **argv) {
return 1;
}
+ struct readbuffer stdinbuf = readbuffer_new();
+
printf("Connecting...\n");
struct tomsg_client *client;
- enum tomsg_retval ret = tomsg_connect(hostname, port, &client);
+ enum tomsg_retval ret = tomsg_connect(
+ hostname, port, hostkey_checker, &stdinbuf, &client);
if (ret != TOMSG_OK) {
printf("Could not connect: %s\n", tomsg_strerror(ret));
return 1;
@@ -439,8 +485,6 @@ int main(int argc, char **argv) {
printf("Connected.\n");
- struct readbuffer stdinbuf = readbuffer_new();
-
struct state state = (struct state){
.rooms = NULL,
.num_rooms = 0,
diff --git a/ssh/ssh_client.c b/ssh/ssh_client.c
index 3e4a7c6..f09aaac 100644
--- a/ssh/ssh_client.c
+++ b/ssh/ssh_client.c
@@ -10,7 +10,7 @@
static bool prompt_yn(const char *text) {
- printf("%s", text);
+ printf("%s [y/n] ", text);
fflush(stdout);
bool response;
@@ -37,7 +37,7 @@ static bool prompt_yn(const char *text) {
break;
}
- printf("Please answer with 'y', 'n', 'yes' or 'no'. [y/n]");
+ printf("Please answer with 'y', 'n', 'yes' or 'no'. [y/n] ");
fflush(stdout);
}
@@ -51,7 +51,7 @@ static bool hostkey_checker(const unsigned char *hash, size_t length, void *user
bool response = prompt_yn(
"Does this hash match the one given to you by the server administrator, or by a\n"
- "member that you trust and is already connected to the server? [y/n] ");
+ "member that you trust and is already connected to the server?");
if (!response) {
printf("Disconnecting.\n");
}
diff --git a/ssh/sshnc.h b/ssh/sshnc.h
index 1e82075..5ee742a 100644
--- a/ssh/sshnc.h
+++ b/ssh/sshnc.h
@@ -64,7 +64,7 @@ enum sshnc_retval sshnc_connect(
const char *username,
const char *subsystem,
sshnc_hostkey_checker_t checker,
- void *userdata,
+ void *userdata, // for checker
struct sshnc_client **client // output
);
diff --git a/ssh/tomsg_clientlib.c b/ssh/tomsg_clientlib.c
index 9898b66..80a9bf3 100644
--- a/ssh/tomsg_clientlib.c
+++ b/ssh/tomsg_clientlib.c
@@ -34,13 +34,6 @@ struct tomsg_client {
static size_t min_size_t(size_t a, size_t b) { return a < b ? a : b; }
-static bool hostkey_checker(const unsigned char *hash, size_t length, void *userdata) {
- (void)userdata;
- static const char *preload = "SHA256:ppz/McaESpOQy0O3kbaIi1LPZ37/YtrdC+y9102Y0+I";
- const char *fingerprint = sshnc_print_hash(hash, length);
- return strcmp(fingerprint, preload) == 0;
-}
-
static bool hasspacelf(const char *string) {
for (size_t i = 0; string[i]; i++)
if (string[i] == ' ' || string[i] == '\n') return true;
@@ -139,6 +132,10 @@ static void splice_scanned_buffer_part(struct tomsg_client *client) {
client->buffer_newline_cursor = 0;
}
+const char* tomsg_print_hash(const unsigned char *hash, size_t length) {
+ return sshnc_print_hash(hash, length);
+}
+
const char* tomsg_strerror(enum tomsg_retval code) {
switch (code) {
case TOMSG_OK: return "Success";
@@ -190,13 +187,15 @@ static enum tomsg_retval version_negotiation(struct tomsg_client *client) {
}
enum tomsg_retval tomsg_connect(
- const char *hostname, int port, struct tomsg_client **clientp) {
+ const char *hostname, int port,
+ tomsg_hostkey_checker_t checker, void *userdata,
+ struct tomsg_client **clientp) {
// In case we throw an error along the way
*clientp = NULL;
struct sshnc_client *conn;
enum sshnc_retval ret = sshnc_connect(
- hostname, port, "tomsg", "tomsg", hostkey_checker, NULL, &conn);
+ hostname, port, "tomsg", "tomsg", checker, userdata, &conn);
if (ret == SSHNC_ERR_CONNECT) return TOMSG_ERR_CONNECT;
if (ret != SSHNC_OK) return TOMSG_ERR_TRANSPORT;
diff --git a/ssh/tomsg_clientlib.h b/ssh/tomsg_clientlib.h
index 58ad4d4..03f1fcf 100644
--- a/ssh/tomsg_clientlib.h
+++ b/ssh/tomsg_clientlib.h
@@ -30,6 +30,16 @@ enum tomsg_retval {
TOMSG_ERR_MEMORY, // Error allocating memory
};
+// Should return 'true' if the key is trusted, 'false' otherwise. The hash is
+// sha256 in byte form, not yet encoded in hexadecimal or similar. The
+// 'userdata' pointer comes from the 'tomsg_connect' invocation.
+typedef bool (*tomsg_hostkey_checker_t)(
+ const unsigned char *hash, size_t length, void *userdata);
+
+// Convenience function to convert a hash to a human-readable form. Returns a
+// reference to an internal static buffer.
+const char* tomsg_print_hash(const unsigned char *hash, size_t length);
+
// Returns reference to internal static buffer.
const char* tomsg_strerror(enum tomsg_retval code);
@@ -37,6 +47,8 @@ const char* tomsg_strerror(enum tomsg_retval code);
// TOMSG_OK. On error, stores NULL in 'client' and returns an error code.
enum tomsg_retval tomsg_connect(
const char *hostname, int port,
+ tomsg_hostkey_checker_t checker,
+ void *userdata, // for checker
struct tomsg_client **client // output
);