summaryrefslogtreecommitdiff
path: root/ssh-agent.c
diff options
context:
space:
mode:
Diffstat (limited to 'ssh-agent.c')
-rw-r--r--ssh-agent.c158
1 files changed, 129 insertions, 29 deletions
diff --git a/ssh-agent.c b/ssh-agent.c
index e081413b8..e1fd1f3f6 100644
--- a/ssh-agent.c
+++ b/ssh-agent.c
@@ -1,4 +1,4 @@
1/* $OpenBSD: ssh-agent.c,v 1.257 2020/03/06 18:28:27 markus Exp $ */ 1/* $OpenBSD: ssh-agent.c,v 1.264 2020/09/18 08:16:38 djm Exp $ */
2/* 2/*
3 * Author: Tatu Ylonen <ylo@cs.hut.fi> 3 * Author: Tatu Ylonen <ylo@cs.hut.fi>
4 * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland 4 * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@@ -77,6 +77,7 @@
77 77
78#include "xmalloc.h" 78#include "xmalloc.h"
79#include "ssh.h" 79#include "ssh.h"
80#include "ssh2.h"
80#include "sshbuf.h" 81#include "sshbuf.h"
81#include "sshkey.h" 82#include "sshkey.h"
82#include "authfd.h" 83#include "authfd.h"
@@ -92,8 +93,8 @@
92#include "ssh-pkcs11.h" 93#include "ssh-pkcs11.h"
93#include "sk-api.h" 94#include "sk-api.h"
94 95
95#ifndef DEFAULT_PROVIDER_WHITELIST 96#ifndef DEFAULT_ALLOWED_PROVIDERS
96# define DEFAULT_PROVIDER_WHITELIST "/usr/lib*/*,/usr/local/lib*/*" 97# define DEFAULT_ALLOWED_PROVIDERS "/usr/lib*/*,/usr/local/lib*/*"
97#endif 98#endif
98 99
99/* Maximum accepted message length */ 100/* Maximum accepted message length */
@@ -149,8 +150,8 @@ pid_t cleanup_pid = 0;
149char socket_name[PATH_MAX]; 150char socket_name[PATH_MAX];
150char socket_dir[PATH_MAX]; 151char socket_dir[PATH_MAX];
151 152
152/* PKCS#11/Security key path whitelist */ 153/* Pattern-list of allowed PKCS#11/Security key paths */
153static char *provider_whitelist; 154static char *allowed_providers;
154 155
155/* locking */ 156/* locking */
156#define LOCK_SIZE 32 157#define LOCK_SIZE 32
@@ -167,6 +168,9 @@ static long lifetime = 0;
167 168
168static int fingerprint_hash = SSH_FP_HASH_DEFAULT; 169static int fingerprint_hash = SSH_FP_HASH_DEFAULT;
169 170
171/* Refuse signing of non-SSH messages for web-origin FIDO keys */
172static int restrict_websafe = 1;
173
170static void 174static void
171close_socket(SocketEntry *e) 175close_socket(SocketEntry *e)
172{ 176{
@@ -282,6 +286,80 @@ agent_decode_alg(struct sshkey *key, u_int flags)
282 return NULL; 286 return NULL;
283} 287}
284 288
289/*
290 * This function inspects a message to be signed by a FIDO key that has a
291 * web-like application string (i.e. one that does not begin with "ssh:".
292 * It checks that the message is one of those expected for SSH operations
293 * (pubkey userauth, sshsig, CA key signing) to exclude signing challenges
294 * for the web.
295 */
296static int
297check_websafe_message_contents(struct sshkey *key,
298 const u_char *msg, size_t len)
299{
300 int matched = 0;
301 struct sshbuf *b;
302 u_char m, n;
303 char *cp1 = NULL, *cp2 = NULL;
304 int r;
305 struct sshkey *mkey = NULL;
306
307 if ((b = sshbuf_from(msg, len)) == NULL)
308 fatal("%s: sshbuf_new", __func__);
309
310 /* SSH userauth request */
311 if ((r = sshbuf_get_string_direct(b, NULL, NULL)) == 0 && /* sess_id */
312 (r = sshbuf_get_u8(b, &m)) == 0 && /* SSH2_MSG_USERAUTH_REQUEST */
313 (r = sshbuf_get_cstring(b, NULL, NULL)) == 0 && /* server user */
314 (r = sshbuf_get_cstring(b, &cp1, NULL)) == 0 && /* service */
315 (r = sshbuf_get_cstring(b, &cp2, NULL)) == 0 && /* method */
316 (r = sshbuf_get_u8(b, &n)) == 0 && /* sig-follows */
317 (r = sshbuf_get_cstring(b, NULL, NULL)) == 0 && /* alg */
318 (r = sshkey_froms(b, &mkey)) == 0 && /* key */
319 sshbuf_len(b) == 0) {
320 debug("%s: parsed userauth", __func__);
321 if (m == SSH2_MSG_USERAUTH_REQUEST && n == 1 &&
322 strcmp(cp1, "ssh-connection") == 0 &&
323 strcmp(cp2, "publickey") == 0 &&
324 sshkey_equal(key, mkey)) {
325 debug("%s: well formed userauth", __func__);
326 matched = 1;
327 }
328 }
329 free(cp1);
330 free(cp2);
331 sshkey_free(mkey);
332 sshbuf_free(b);
333 if (matched)
334 return 1;
335
336 if ((b = sshbuf_from(msg, len)) == NULL)
337 fatal("%s: sshbuf_new", __func__);
338 cp1 = cp2 = NULL;
339 mkey = NULL;
340
341 /* SSHSIG */
342 if ((r = sshbuf_cmp(b, 0, "SSHSIG", 6)) == 0 &&
343 (r = sshbuf_consume(b, 6)) == 0 &&
344 (r = sshbuf_get_cstring(b, NULL, NULL)) == 0 && /* namespace */
345 (r = sshbuf_get_string_direct(b, NULL, NULL)) == 0 && /* reserved */
346 (r = sshbuf_get_cstring(b, NULL, NULL)) == 0 && /* hashalg */
347 (r = sshbuf_get_string_direct(b, NULL, NULL)) == 0 && /* H(msg) */
348 sshbuf_len(b) == 0) {
349 debug("%s: parsed sshsig", __func__);
350 matched = 1;
351 }
352
353 sshbuf_free(b);
354 if (matched)
355 return 1;
356
357 /* XXX CA signature operation */
358
359 error("web-origin key attempting to sign non-SSH message");
360 return 0;
361}
362
285/* ssh2 only */ 363/* ssh2 only */
286static void 364static void
287process_sign_request2(SocketEntry *e) 365process_sign_request2(SocketEntry *e)
@@ -314,18 +392,25 @@ process_sign_request2(SocketEntry *e)
314 verbose("%s: user refused key", __func__); 392 verbose("%s: user refused key", __func__);
315 goto send; 393 goto send;
316 } 394 }
317 if (sshkey_is_sk(id->key) && 395 if (sshkey_is_sk(id->key)) {
318 (id->key->sk_flags & SSH_SK_USER_PRESENCE_REQD)) { 396 if (strncmp(id->key->sk_application, "ssh:", 4) != 0 &&
319 if ((fp = sshkey_fingerprint(key, SSH_FP_HASH_DEFAULT, 397 !check_websafe_message_contents(key, data, dlen)) {
320 SSH_FP_DEFAULT)) == NULL) 398 /* error already logged */
321 fatal("%s: fingerprint failed", __func__); 399 goto send;
322 notifier = notify_start(0, 400 }
323 "Confirm user presence for key %s %s", 401 if ((id->key->sk_flags & SSH_SK_USER_PRESENCE_REQD)) {
324 sshkey_type(id->key), fp); 402 if ((fp = sshkey_fingerprint(key, SSH_FP_HASH_DEFAULT,
403 SSH_FP_DEFAULT)) == NULL)
404 fatal("%s: fingerprint failed", __func__);
405 notifier = notify_start(0,
406 "Confirm user presence for key %s %s",
407 sshkey_type(id->key), fp);
408 }
325 } 409 }
410 /* XXX support PIN required FIDO keys */
326 if ((r = sshkey_sign(id->key, &signature, &slen, 411 if ((r = sshkey_sign(id->key, &signature, &slen,
327 data, dlen, agent_decode_alg(key, flags), 412 data, dlen, agent_decode_alg(key, flags),
328 id->sk_provider, compat)) != 0) { 413 id->sk_provider, NULL, compat)) != 0) {
329 error("%s: sshkey_sign: %s", __func__, ssh_err(r)); 414 error("%s: sshkey_sign: %s", __func__, ssh_err(r));
330 goto send; 415 goto send;
331 } 416 }
@@ -528,9 +613,9 @@ process_add_identity(SocketEntry *e)
528 free(sk_provider); 613 free(sk_provider);
529 sk_provider = xstrdup(canonical_provider); 614 sk_provider = xstrdup(canonical_provider);
530 if (match_pattern_list(sk_provider, 615 if (match_pattern_list(sk_provider,
531 provider_whitelist, 0) != 1) { 616 allowed_providers, 0) != 1) {
532 error("Refusing add key: " 617 error("Refusing add key: "
533 "provider %s not whitelisted", sk_provider); 618 "provider %s not allowed", sk_provider);
534 free(sk_provider); 619 free(sk_provider);
535 goto send; 620 goto send;
536 } 621 }
@@ -685,9 +770,9 @@ process_add_smartcard_key(SocketEntry *e)
685 provider, strerror(errno)); 770 provider, strerror(errno));
686 goto send; 771 goto send;
687 } 772 }
688 if (match_pattern_list(canonical_provider, provider_whitelist, 0) != 1) { 773 if (match_pattern_list(canonical_provider, allowed_providers, 0) != 1) {
689 verbose("refusing PKCS#11 add of \"%.100s\": " 774 verbose("refusing PKCS#11 add of \"%.100s\": "
690 "provider not whitelisted", canonical_provider); 775 "provider not allowed", canonical_provider);
691 goto send; 776 goto send;
692 } 777 }
693 debug("%s: add %.100s", __func__, canonical_provider); 778 debug("%s: add %.100s", __func__, canonical_provider);
@@ -767,8 +852,10 @@ send:
767} 852}
768#endif /* ENABLE_PKCS11 */ 853#endif /* ENABLE_PKCS11 */
769 854
770/* dispatch incoming messages */ 855/*
771 856 * dispatch incoming message.
857 * returns 1 on success, 0 for incomplete messages or -1 on error.
858 */
772static int 859static int
773process_message(u_int socknum) 860process_message(u_int socknum)
774{ 861{
@@ -822,7 +909,7 @@ process_message(u_int socknum)
822 /* send a fail message for all other request types */ 909 /* send a fail message for all other request types */
823 send_status(e, 0); 910 send_status(e, 0);
824 } 911 }
825 return 0; 912 return 1;
826 } 913 }
827 914
828 switch (type) { 915 switch (type) {
@@ -866,7 +953,7 @@ process_message(u_int socknum)
866 send_status(e, 0); 953 send_status(e, 0);
867 break; 954 break;
868 } 955 }
869 return 0; 956 return 1;
870} 957}
871 958
872static void 959static void
@@ -957,7 +1044,12 @@ handle_conn_read(u_int socknum)
957 if ((r = sshbuf_put(sockets[socknum].input, buf, len)) != 0) 1044 if ((r = sshbuf_put(sockets[socknum].input, buf, len)) != 0)
958 fatal("%s: buffer error: %s", __func__, ssh_err(r)); 1045 fatal("%s: buffer error: %s", __func__, ssh_err(r));
959 explicit_bzero(buf, sizeof(buf)); 1046 explicit_bzero(buf, sizeof(buf));
960 process_message(socknum); 1047 for (;;) {
1048 if ((r = process_message(socknum)) == -1)
1049 return -1;
1050 else if (r == 0)
1051 break;
1052 }
961 return 0; 1053 return 0;
962} 1054}
963 1055
@@ -1170,7 +1262,9 @@ usage(void)
1170{ 1262{
1171 fprintf(stderr, 1263 fprintf(stderr,
1172 "usage: ssh-agent [-c | -s] [-Dd] [-a bind_address] [-E fingerprint_hash]\n" 1264 "usage: ssh-agent [-c | -s] [-Dd] [-a bind_address] [-E fingerprint_hash]\n"
1173 " [-P provider_whitelist] [-t life] [command [arg ...]]\n" 1265 " [-P allowed_providers] [-t life]\n"
1266 " ssh-agent [-a bind_address] [-E fingerprint_hash] [-P allowed_providers]\n"
1267 " [-t life] command [arg ...]\n"
1174 " ssh-agent [-c | -s] -k\n"); 1268 " ssh-agent [-c | -s] -k\n");
1175 exit(1); 1269 exit(1);
1176} 1270}
@@ -1212,7 +1306,7 @@ main(int ac, char **av)
1212 __progname = ssh_get_progname(av[0]); 1306 __progname = ssh_get_progname(av[0]);
1213 seed_rng(); 1307 seed_rng();
1214 1308
1215 while ((ch = getopt(ac, av, "cDdksE:a:P:t:")) != -1) { 1309 while ((ch = getopt(ac, av, "cDdksE:a:O:P:t:")) != -1) {
1216 switch (ch) { 1310 switch (ch) {
1217 case 'E': 1311 case 'E':
1218 fingerprint_hash = ssh_digest_alg_by_name(optarg); 1312 fingerprint_hash = ssh_digest_alg_by_name(optarg);
@@ -1227,10 +1321,16 @@ main(int ac, char **av)
1227 case 'k': 1321 case 'k':
1228 k_flag++; 1322 k_flag++;
1229 break; 1323 break;
1324 case 'O':
1325 if (strcmp(optarg, "no-restrict-websafe") == 0)
1326 restrict_websafe = 0;
1327 else
1328 fatal("Unknown -O option");
1329 break;
1230 case 'P': 1330 case 'P':
1231 if (provider_whitelist != NULL) 1331 if (allowed_providers != NULL)
1232 fatal("-P option already specified"); 1332 fatal("-P option already specified");
1233 provider_whitelist = xstrdup(optarg); 1333 allowed_providers = xstrdup(optarg);
1234 break; 1334 break;
1235 case 's': 1335 case 's':
1236 if (c_flag) 1336 if (c_flag)
@@ -1266,8 +1366,8 @@ main(int ac, char **av)
1266 if (ac > 0 && (c_flag || k_flag || s_flag || d_flag || D_flag)) 1366 if (ac > 0 && (c_flag || k_flag || s_flag || d_flag || D_flag))
1267 usage(); 1367 usage();
1268 1368
1269 if (provider_whitelist == NULL) 1369 if (allowed_providers == NULL)
1270 provider_whitelist = xstrdup(DEFAULT_PROVIDER_WHITELIST); 1370 allowed_providers = xstrdup(DEFAULT_ALLOWED_PROVIDERS);
1271 1371
1272 if (ac == 0 && !c_flag && !s_flag) { 1372 if (ac == 0 && !c_flag && !s_flag) {
1273 shell = getenv("SHELL"); 1373 shell = getenv("SHELL");