tmate-io_tmate-ssh-server/tmate-auth-keys.c
2019-11-09 23:28:07 -05:00

226 lines
4.6 KiB
C

#include <sys/socket.h>
#include <sys/un.h>
#include "tmate.h"
static void reset_and_enable_authorized_keys(void)
{
ssh_key *keys = tmate_session->authorized_keys;
if (keys) {
for (ssh_key *k = keys; *k; k++)
ssh_key_free(*k);
free(keys);
}
keys = xreallocarray(NULL, sizeof(ssh_key), 1);
keys[0] = NULL;
tmate_session->authorized_keys = keys;
}
static ssh_key import_ssh_pubkey64(const char *_keystr)
{
/* key is formatted as "type base64_key" */
char * const keystr = xstrdup(_keystr);
char *s = keystr;
ssh_key ret = NULL;
char *key_type = strsep(&s, " ");
char *key_content = strsep(&s, " ");
if (!key_content)
goto out;
enum ssh_keytypes_e type = ssh_key_type_from_name(key_type);
if (type == SSH_KEYTYPE_UNKNOWN)
goto out;
if (ssh_pki_import_pubkey_base64(key_content, type, &ret) != SSH_OK) {
ret = NULL;
goto out;
}
out:
free(keystr);
return ret;
}
int get_num_authorized_keys(ssh_key *keys)
{
if (!keys)
return 0;
int count = 0;
for (ssh_key *k = keys; *k; k++)
count++;
return count;
}
static void append_authorized_key(const char *keystr)
{
if (!tmate_session->authorized_keys)
reset_and_enable_authorized_keys();
ssh_key pkey = import_ssh_pubkey64(keystr);
if (!pkey)
return;
ssh_key *keys = tmate_session->authorized_keys;
int count = get_num_authorized_keys(keys);
keys = xreallocarray(keys, sizeof(ssh_key), count+2);
tmate_session->authorized_keys = keys;
keys[count++] = pkey;
keys[count] = NULL;
}
static void tmate_set(char *key, char *value)
{
if (!strcmp(key, "authorized_keys"))
append_authorized_key(value);
}
void tmate_hook_set_option_auth(const char *name, const char *val)
{
if (!strcmp(name, "tmate-authorized-keys")) {
reset_and_enable_authorized_keys();
} else if (!strcmp(name, "tmate-set")) {
char *key_value = xstrdup(val);
char *s = key_value;
char *key = strsep(&s, "=");
char *value = s;
if (value)
tmate_set(key, value);
free(key_value);
}
}
bool tmate_allow_auth(const char *pubkey)
{
/*
* Note that we don't accept connections on the tmux socket until we
* get the tmate ready message.
*/
if (!tmate_session->authorized_keys)
return true;
if (!pubkey)
return false;
ssh_key client_pkey = import_ssh_pubkey64(pubkey);
if (!client_pkey)
return false;
bool ret = false;
for (ssh_key *k = tmate_session->authorized_keys; *k; k++) {
if (!ssh_key_cmp(client_pkey, *k, SSH_KEY_CMP_PUBLIC)) {
ret = true;
break;
}
}
ssh_key_free(client_pkey);
return ret;
}
static int write_all(int fd, const char *buf, size_t len)
{
for (size_t i = 0; i < len;) {
size_t ret = write(fd, buf+i, len-i);
if (ret <= 0)
return -1;
i += ret;
}
return 0;
}
static int read_all(int fd, char *buf, size_t len)
{
for (size_t i = 0; i < len;) {
size_t ret = read(fd, buf+i, len-i);
if (ret <= 0)
return -1;
i += ret;
}
return 0;
}
/*
* The following is executed in the context of the SSH server
*/
bool would_tmate_session_allow_auth(const char *token, const char *pubkey)
{
/*
* The existance of this function is a bit unpleasant:
* In order to have the right SSH public key from the SSH client,
* we need to ask the tmate session for a match. Denying the key
* to the SSH client will make it cycle through its keys.
* We briefly connect to the session to get an answer.
*
* Note that the client will get reauthenticated later (see
* server-client.c when identifying the client).
*/
int sock_fd = -1;
int ret = true;
if (tmate_validate_session_token(token) < 0)
goto out;
char *sock_path = get_socket_path(token);
struct sockaddr_un sa;
memset(&sa, 0, sizeof(sa));
sa.sun_family = AF_UNIX;
size_t size = strlcpy(sa.sun_path, sock_path, sizeof(sa.sun_path));
free(sock_path);
if (size >= sizeof sa.sun_path)
goto out;
sock_fd = socket(AF_UNIX, SOCK_STREAM, 0);
if (sock_fd < 0)
goto out;
if (connect(sock_fd, (struct sockaddr *)&sa, sizeof sa) == -1)
goto out;
struct imsg_hdr hdr = {
.type = pubkey ? MSG_IDENTIFY_TMATE_AUTH_PUBKEY :
MSG_IDENTIFY_TMATE_AUTH_NONE,
.len = IMSG_HEADER_SIZE + (pubkey ? strlen(pubkey)+1 : 0),
.flags = 0,
.peerid = PROTOCOL_VERSION,
.pid = -1,
};
if (write_all(sock_fd, (void*)&hdr, sizeof(hdr)) < 0)
goto out;
if (pubkey) {
if (write_all(sock_fd, pubkey, strlen(pubkey)+1) < 0)
goto out;
}
struct {
struct imsg_hdr hdr;
bool allow;
} __packed recv_msg;
if (read_all(sock_fd, (void*)&recv_msg, sizeof(recv_msg)) < 0)
goto out;
if (recv_msg.hdr.type == MSG_TMATE_AUTH_STATUS &&
recv_msg.hdr.len == sizeof(recv_msg))
ret = recv_msg.allow;
tmate_debug("(preauth) allow=%d", ret);
out:
if (sock_fd != -1)
close(sock_fd);
return ret;
}