known hosts functionality (check/verify/save server pub keys)

This commit is contained in:
cy384 2020-10-10 19:45:59 -04:00
parent b51a26a924
commit 2a31b1092c
4 changed files with 220 additions and 1 deletions

View File

@ -87,6 +87,10 @@
#define DLOG_PREFERENCES 140 #define DLOG_PREFERENCES 140
#define DITL_PREFERENCES 140 #define DITL_PREFERENCES 140
/* dialog for known host check */
#define DLOG_NEW_HOST 141
#define DITL_NEW_HOST 141
/* controls for preferences dialog */ /* controls for preferences dialog */
#define CNTL_PREF_FG_COLOR 128 #define CNTL_PREF_FG_COLOR 128
#define CNTL_PREF_BG_COLOR 129 #define CNTL_PREF_BG_COLOR 129

185
ssheven.c
View File

@ -1131,6 +1131,181 @@ int intro_dialog(void)
} }
} }
// returns base64 sha256 hash of key as a malloc'd pascal string
char* host_hash(void)
{
size_t length = 0;
char* human_readable = malloc(64);
memset(human_readable, 0, 64);
const char* host_key_hash = NULL;
host_key_hash = libssh2_hostkey_hash(ssh_con.session, LIBSSH2_HOSTKEY_HASH_SHA256);
mbedtls_base64_encode((unsigned char*)human_readable+1, 64, &length, (unsigned const char*)host_key_hash, 32);
human_readable[0] = (unsigned char)length;
return human_readable;
}
char* known_hosts_full_path(int* found)
{
int ok = 1;
short foundVRefNum = 0;
long foundDirID = 0;
FSSpec known_hosts_file;
*found = 0;
OSType pref_type = 'SH7p';
OSType creator_type = 'SSH7';
// find the preferences folder on the system disk, create folder if needed
OSErr e = FindFolder(kOnSystemDisk, kPreferencesFolderType, kCreateFolder, &foundVRefNum, &foundDirID);
if (e != noErr) ok = 0;
// make an FSSpec for the new file we want to make
if (ok)
{
e = FSMakeFSSpec(foundVRefNum, foundDirID, "\pknown_hosts", &known_hosts_file);
// if the file exists, we found it else make an empty one
if (e == noErr) *found = 1;
else if (e == noErr) e = FSpCreate(&known_hosts_file, creator_type, pref_type, smSystemScript);
if (e != noErr) ok = 0;
}
Handle full_path_handle;
int path_length;
FSpPathFromLocation(&known_hosts_file, &path_length, &full_path_handle);
char* full_path = malloc(path_length+1);
strncpy(full_path, (char*)(*full_path_handle), path_length+1);
DisposeHandle(full_path_handle);
return full_path;
}
int known_hosts(void)
{
int safe_to_connect = 1;
int recognized_key = 0;
int known_hosts_file_exists = 0;
char* known_hosts_file_path = known_hosts_full_path(&known_hosts_file_exists);
char* hash_string = NULL;
size_t key_len = 0;
int key_type = 0;
const char* host_key = libssh2_session_hostkey(ssh_con.session, &key_len, &key_type);
LIBSSH2_KNOWNHOSTS* known_hosts = libssh2_knownhost_init(ssh_con.session);
if (known_hosts_file_exists)
{
// load known hosts file
printf_i("Loading known hosts... ");
int e = libssh2_knownhost_readfile(known_hosts, known_hosts_file_path, LIBSSH2_KNOWNHOST_FILE_OPENSSH);
if (e >= 0)
{
printf_i("got %d.\r\n", e);
}
else
{
printf_i("failed: %s\r\n", libssh2_error_string(e));
}
}
else
{
printf_i("No known hosts file found.\r\n");
}
if (safe_to_connect)
{
// our hostname string includes the port, I guess that's okay?
int e = libssh2_knownhost_check(known_hosts, prefs.hostname+1, host_key, key_len, key_type, NULL);
switch (e)
{
case LIBSSH2_KNOWNHOST_CHECK_FAILURE:
printf_i("Failed to check known hosts against server public key!\r\n");
safe_to_connect = 0;
break;
case LIBSSH2_KNOWNHOST_CHECK_NOTFOUND:
printf_i("No matching host found.\r\n");
break;
case LIBSSH2_KNOWNHOST_CHECK_MATCH:
printf_i("Host and key match found in known hosts.\r\n");
recognized_key = 1;
break;
case LIBSSH2_KNOWNHOST_CHECK_MISMATCH:
printf_i("Host found in known hosts but key doesn't match!\r\n");
safe_to_connect = 0;
break;
}
}
hash_string = host_hash();
//printf_i("Host key hash (SHA256): %s\r\n", hash_string+1);
// ask the user to confirm if we're seeing a new host+key combo
if (safe_to_connect && !recognized_key)
{
// ask the user if the key is OK
DialogPtr dlg = GetNewDialog(DLOG_NEW_HOST, 0, (WindowPtr)-1);
DialogItemType type;
Handle itemH;
Rect box;
// draw default button indicator around the connect button
GetDialogItem(dlg, 2, &type, &itemH, &box);
SetDialogItem(dlg, 2, type, (Handle)NewUserItemUPP(&ButtonFrameProc), &box);
// write the hash string into the dialog window
ControlHandle hash_text_box;
GetDialogItem(dlg, 4, &type, &itemH, &box);
hash_text_box = (ControlHandle)itemH;
SetDialogItemText((Handle)hash_text_box, (ConstStr255Param)hash_string);
// let the modalmanager do everything
// stop on reject or accept
short item;
do {
ModalDialog(NULL, &item);
} while(item != 1 && item != 5);
// reject if user hit reject
if (item == 1)
{
safe_to_connect = 0;
}
DisposeDialog(dlg);
FlushEvents(everyEvent, -1);
printf_i("Saving host and key... ");
int save_type = (key_type == LIBSSH2_HOSTKEY_TYPE_RSA ? LIBSSH2_KNOWNHOST_KEY_SSHRSA : LIBSSH2_KNOWNHOST_KEY_SSHDSS);
int e = libssh2_knownhost_addc(known_hosts, prefs.hostname+1, NULL, host_key, key_len, NULL, 0, LIBSSH2_KNOWNHOST_TYPE_PLAIN | LIBSSH2_KNOWNHOST_KEYENC_RAW | save_type, NULL);
if (e != 0) printf_i("failed to add to known hosts: %s\r\n", libssh2_error_string(e));
else
{
e = libssh2_knownhost_writefile(known_hosts, known_hosts_file_path, LIBSSH2_KNOWNHOST_FILE_OPENSSH);
if (e != 0) printf_i("failed to save known hosts file: %s\r\n", libssh2_error_string(e));
else printf_i("done.\r\n");
}
}
free(hash_string);
libssh2_knownhost_free(known_hosts);
free(known_hosts_file_path);
return safe_to_connect;
}
void* read_thread(void* arg) void* read_thread(void* arg)
{ {
int ok = 1; int ok = 1;
@ -1144,10 +1319,18 @@ void* read_thread(void* arg)
return 0; return 0;
} }
// connect and log in // connect
ok = init_connection(prefs.hostname+1); ok = init_connection(prefs.hostname+1);
YieldToAnyThread(); YieldToAnyThread();
// check the server pub key vs. known hosts
if (ok)
{
ok = known_hosts();
if (!ok) printf_i("Rejected server public key!\r\n");
}
// actually log in
if (ok) if (ok)
{ {
printf_i("Authenticating... "); YieldToAnyThread(); printf_i("Authenticating... "); YieldToAnyThread();

View File

@ -38,6 +38,8 @@
#include <stdio.h> #include <stdio.h>
#include <mbedtls/base64.h>
// sinful globals // sinful globals
struct ssheven_console struct ssheven_console
{ {

View File

@ -339,6 +339,36 @@ resource 'ALRT' (ALRT_FILE_FAIL, purgeable) {
alertPositionMainScreen alertPositionMainScreen
}; };
resource 'DLOG' (DLOG_NEW_HOST) {
{ 40, 30, 140, 480 },
dBoxProc,
visible,
noGoAway,
0,
DLOG_NEW_HOST,
"",
centerMainScreen
};
resource 'DITL' (DITL_NEW_HOST) {
{
{ 70, 355, 90, 440 },
Button { enabled, "Reject" };
{ 70, 355, 90, 440 },
UserItem { enabled };
{ 10, 70, 30, 400 },
StaticText { enabled, "Unrecognized server public key. SHA256 hash:" };
{ 30, 70, 50, 440 },
StaticText { enabled, "01234567890123456789012345678901234567890123456789" };
{ 70, 10, 90, 90 },
Button { enabled, "Accept" };
}
};
resource 'SIZE' (-1) { resource 'SIZE' (-1) {
reserved, reserved,
acceptSuspendResumeEvents, acceptSuspendResumeEvents,