1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225
|
#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;
}
|