diff options
author | djm@openbsd.org <djm@openbsd.org> | 2019-12-13 19:09:10 +0000 |
---|---|---|
committer | Damien Miller <djm@mindrot.org> | 2019-12-14 07:17:44 +1100 |
commit | b52ec0ba3983859514aa7b57d6100fa9759fe696 (patch) | |
tree | 262c9a2ebc4e6972594f45ea75fc15e8ae3bfefb /ssh-agent.c | |
parent | c33d46868c3d88e04a92610cdb429094aeeb5847 (diff) |
upstream: use ssh-sk-helper for all security key signing operations
This extracts and refactors the client interface for ssh-sk-helper
from ssh-agent and generalises it for use by the other programs.
This means that most OpenSSH tools no longer need to link against
libfido2 or directly interact with /dev/uhid*
requested by, feedback and ok markus@
OpenBSD-Commit-ID: 1abcd3aea9a7460eccfbf8ca154cdfa62f1dc93f
Diffstat (limited to 'ssh-agent.c')
-rw-r--r-- | ssh-agent.c | 160 |
1 files changed, 20 insertions, 140 deletions
diff --git a/ssh-agent.c b/ssh-agent.c index 1d4b779f6..09d12dc3f 100644 --- a/ssh-agent.c +++ b/ssh-agent.c | |||
@@ -1,4 +1,4 @@ | |||
1 | /* $OpenBSD: ssh-agent.c,v 1.250 2019/11/19 16:02:32 jmc Exp $ */ | 1 | /* $OpenBSD: ssh-agent.c,v 1.251 2019/12/13 19:09:10 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 |
@@ -90,7 +90,7 @@ | |||
90 | #include "ssherr.h" | 90 | #include "ssherr.h" |
91 | #include "pathnames.h" | 91 | #include "pathnames.h" |
92 | #include "ssh-pkcs11.h" | 92 | #include "ssh-pkcs11.h" |
93 | #include "ssh-sk.h" | 93 | #include "sk-api.h" |
94 | 94 | ||
95 | #ifndef DEFAULT_PROVIDER_WHITELIST | 95 | #ifndef DEFAULT_PROVIDER_WHITELIST |
96 | # define DEFAULT_PROVIDER_WHITELIST "/usr/lib*/*,/usr/local/lib*/*" | 96 | # define DEFAULT_PROVIDER_WHITELIST "/usr/lib*/*,/usr/local/lib*/*" |
@@ -282,130 +282,6 @@ agent_decode_alg(struct sshkey *key, u_int flags) | |||
282 | return NULL; | 282 | return NULL; |
283 | } | 283 | } |
284 | 284 | ||
285 | static int | ||
286 | provider_sign(const char *provider, struct sshkey *key, | ||
287 | u_char **sigp, size_t *lenp, | ||
288 | const u_char *data, size_t datalen, | ||
289 | const char *alg, u_int compat) | ||
290 | { | ||
291 | int status, pair[2], r = SSH_ERR_INTERNAL_ERROR; | ||
292 | pid_t pid; | ||
293 | char *helper, *verbosity = NULL, *fp = NULL; | ||
294 | struct sshbuf *kbuf, *req, *resp; | ||
295 | u_char version; | ||
296 | struct notifier_ctx *notifier = NULL; | ||
297 | |||
298 | debug3("%s: start for provider %s", __func__, provider); | ||
299 | |||
300 | *sigp = NULL; | ||
301 | *lenp = 0; | ||
302 | |||
303 | helper = getenv("SSH_SK_HELPER"); | ||
304 | if (helper == NULL || strlen(helper) == 0) | ||
305 | helper = _PATH_SSH_SK_HELPER; | ||
306 | if (log_level_get() >= SYSLOG_LEVEL_DEBUG1) | ||
307 | verbosity = "-vvv"; | ||
308 | |||
309 | /* Start helper */ | ||
310 | if (socketpair(AF_UNIX, SOCK_STREAM, 0, pair) == -1) { | ||
311 | error("socketpair: %s", strerror(errno)); | ||
312 | return SSH_ERR_SYSTEM_ERROR; | ||
313 | } | ||
314 | if ((pid = fork()) == -1) { | ||
315 | error("fork: %s", strerror(errno)); | ||
316 | close(pair[0]); | ||
317 | close(pair[1]); | ||
318 | return SSH_ERR_SYSTEM_ERROR; | ||
319 | } | ||
320 | if (pid == 0) { | ||
321 | if ((dup2(pair[1], STDIN_FILENO) == -1) || | ||
322 | (dup2(pair[1], STDOUT_FILENO) == -1)) | ||
323 | fatal("%s: dup2: %s", __func__, ssh_err(r)); | ||
324 | close(pair[0]); | ||
325 | close(pair[1]); | ||
326 | closefrom(STDERR_FILENO + 1); | ||
327 | debug("%s: starting %s %s", __func__, helper, | ||
328 | verbosity == NULL ? "" : verbosity); | ||
329 | execlp(helper, helper, verbosity, (char *)NULL); | ||
330 | fatal("%s: execlp: %s", __func__, strerror(errno)); | ||
331 | } | ||
332 | close(pair[1]); | ||
333 | |||
334 | if ((kbuf = sshbuf_new()) == NULL || | ||
335 | (req = sshbuf_new()) == NULL || | ||
336 | (resp = sshbuf_new()) == NULL) | ||
337 | fatal("%s: sshbuf_new failed", __func__); | ||
338 | |||
339 | if ((r = sshkey_private_serialize(key, kbuf)) != 0 || | ||
340 | (r = sshbuf_put_stringb(req, kbuf)) != 0 || | ||
341 | (r = sshbuf_put_cstring(req, provider)) != 0 || | ||
342 | (r = sshbuf_put_string(req, data, datalen)) != 0 || | ||
343 | (r = sshbuf_put_u32(req, compat)) != 0) | ||
344 | fatal("%s: compose: %s", __func__, ssh_err(r)); | ||
345 | |||
346 | if ((fp = sshkey_fingerprint(key, SSH_FP_HASH_DEFAULT, | ||
347 | SSH_FP_DEFAULT)) == NULL) | ||
348 | fatal("%s: sshkey_fingerprint failed", __func__); | ||
349 | notifier = notify_start(0, | ||
350 | "Confirm user presence for key %s %s", sshkey_type(key), fp); | ||
351 | |||
352 | if ((r = ssh_msg_send(pair[0], SSH_SK_HELPER_VERSION, req)) != 0) { | ||
353 | error("%s: send: %s", __func__, ssh_err(r)); | ||
354 | goto out; | ||
355 | } | ||
356 | if ((r = ssh_msg_recv(pair[0], resp)) != 0) { | ||
357 | error("%s: receive: %s", __func__, ssh_err(r)); | ||
358 | goto out; | ||
359 | } | ||
360 | if ((r = sshbuf_get_u8(resp, &version)) != 0) { | ||
361 | error("%s: parse version: %s", __func__, ssh_err(r)); | ||
362 | goto out; | ||
363 | } | ||
364 | if (version != SSH_SK_HELPER_VERSION) { | ||
365 | error("%s: unsupported version: got %u, expected %u", | ||
366 | __func__, version, SSH_SK_HELPER_VERSION); | ||
367 | r = SSH_ERR_INVALID_FORMAT; | ||
368 | goto out; | ||
369 | } | ||
370 | if ((r = sshbuf_get_string(resp, sigp, lenp)) != 0) { | ||
371 | error("%s: parse signature: %s", __func__, ssh_err(r)); | ||
372 | r = SSH_ERR_INVALID_FORMAT; | ||
373 | goto out; | ||
374 | } | ||
375 | if (sshbuf_len(resp) != 0) { | ||
376 | error("%s: trailing data in response", __func__); | ||
377 | r = SSH_ERR_INVALID_FORMAT; | ||
378 | goto out; | ||
379 | } | ||
380 | /* success */ | ||
381 | r = 0; | ||
382 | out: | ||
383 | while (waitpid(pid, &status, 0) == -1) { | ||
384 | if (errno != EINTR) | ||
385 | fatal("%s: waitpid: %s", __func__, ssh_err(r)); | ||
386 | } | ||
387 | notify_complete(notifier); | ||
388 | if (!WIFEXITED(status)) { | ||
389 | error("%s: helper %s exited abnormally", __func__, helper); | ||
390 | if (r == 0) | ||
391 | r = SSH_ERR_SYSTEM_ERROR; | ||
392 | } else if (WEXITSTATUS(status) != 0) { | ||
393 | error("%s: helper %s exited with non-zero exit status", | ||
394 | __func__, helper); | ||
395 | if (r == 0) | ||
396 | r = SSH_ERR_SYSTEM_ERROR; | ||
397 | } | ||
398 | if (r != 0) { | ||
399 | freezero(*sigp, *lenp); | ||
400 | *sigp = NULL; | ||
401 | *lenp = 0; | ||
402 | } | ||
403 | sshbuf_free(kbuf); | ||
404 | sshbuf_free(req); | ||
405 | sshbuf_free(resp); | ||
406 | return r; | ||
407 | } | ||
408 | |||
409 | /* ssh2 only */ | 285 | /* ssh2 only */ |
410 | static void | 286 | static void |
411 | process_sign_request2(SocketEntry *e) | 287 | process_sign_request2(SocketEntry *e) |
@@ -415,9 +291,11 @@ process_sign_request2(SocketEntry *e) | |||
415 | size_t dlen, slen = 0; | 291 | size_t dlen, slen = 0; |
416 | u_int compat = 0, flags; | 292 | u_int compat = 0, flags; |
417 | int r, ok = -1; | 293 | int r, ok = -1; |
294 | char *fp = NULL; | ||
418 | struct sshbuf *msg; | 295 | struct sshbuf *msg; |
419 | struct sshkey *key = NULL; | 296 | struct sshkey *key = NULL; |
420 | struct identity *id; | 297 | struct identity *id; |
298 | struct notifier_ctx *notifier = NULL; | ||
421 | 299 | ||
422 | if ((msg = sshbuf_new()) == NULL) | 300 | if ((msg = sshbuf_new()) == NULL) |
423 | fatal("%s: sshbuf_new failed", __func__); | 301 | fatal("%s: sshbuf_new failed", __func__); |
@@ -436,25 +314,27 @@ process_sign_request2(SocketEntry *e) | |||
436 | verbose("%s: user refused key", __func__); | 314 | verbose("%s: user refused key", __func__); |
437 | goto send; | 315 | goto send; |
438 | } | 316 | } |
439 | if (id->sk_provider != NULL) { | 317 | if (sshkey_is_sk(id->key) && |
440 | if ((r = provider_sign(id->sk_provider, id->key, &signature, | 318 | (id->key->sk_flags & SSH_SK_USER_PRESENCE_REQD)) { |
441 | &slen, data, dlen, agent_decode_alg(key, flags), | 319 | if ((fp = sshkey_fingerprint(key, SSH_FP_HASH_DEFAULT, |
442 | compat)) != 0) { | 320 | SSH_FP_DEFAULT)) == NULL) |
443 | error("%s: sign: %s", __func__, ssh_err(r)); | 321 | fatal("%s: fingerprint failed", __func__); |
444 | goto send; | 322 | notifier = notify_start(0, |
445 | } | 323 | "Confirm user presence for key %s %s", |
446 | } else { | 324 | sshkey_type(id->key), fp); |
447 | if ((r = sshkey_sign(id->key, &signature, &slen, | 325 | } |
448 | data, dlen, agent_decode_alg(key, flags), | 326 | if ((r = sshkey_sign(id->key, &signature, &slen, |
449 | NULL, compat)) != 0) { | 327 | data, dlen, agent_decode_alg(key, flags), |
450 | error("%s: sshkey_sign: %s", __func__, ssh_err(r)); | 328 | id->sk_provider, compat)) != 0) { |
451 | goto send; | 329 | error("%s: sshkey_sign: %s", __func__, ssh_err(r)); |
452 | } | 330 | goto send; |
453 | } | 331 | } |
454 | /* Success */ | 332 | /* Success */ |
455 | ok = 0; | 333 | ok = 0; |
456 | send: | 334 | send: |
335 | notify_complete(notifier); | ||
457 | sshkey_free(key); | 336 | sshkey_free(key); |
337 | free(fp); | ||
458 | if (ok == 0) { | 338 | if (ok == 0) { |
459 | if ((r = sshbuf_put_u8(msg, SSH2_AGENT_SIGN_RESPONSE)) != 0 || | 339 | if ((r = sshbuf_put_u8(msg, SSH2_AGENT_SIGN_RESPONSE)) != 0 || |
460 | (r = sshbuf_put_string(msg, signature, slen)) != 0) | 340 | (r = sshbuf_put_string(msg, signature, slen)) != 0) |