diff options
author | djm@openbsd.org <djm@openbsd.org> | 2019-12-13 19:11:14 +0000 |
---|---|---|
committer | Damien Miller <djm@mindrot.org> | 2019-12-14 07:20:28 +1100 |
commit | 611073fb40ecaf4ac65094e403edea3a08deb700 (patch) | |
tree | 5a7f0d7e4bd49f3326dd9eba6913f880e18632be /ssh-sk-helper.c | |
parent | 612b1dd1ec91ffb1e01f58cca0c6eb1d47bf4423 (diff) |
upstream: perform security key enrollment via ssh-sk-helper too.
This means that ssh-keygen no longer needs to link against ssh-sk-helper, and
only ssh-sk-helper needs libfido2 and /dev/uhid* access;
feedback & ok markus@
OpenBSD-Commit-ID: 9464233fab95708d2ff059f8bee29c0d1f270800
Diffstat (limited to 'ssh-sk-helper.c')
-rw-r--r-- | ssh-sk-helper.c | 160 |
1 files changed, 121 insertions, 39 deletions
diff --git a/ssh-sk-helper.c b/ssh-sk-helper.c index 0acb8d172..eade26e3d 100644 --- a/ssh-sk-helper.c +++ b/ssh-sk-helper.c | |||
@@ -1,4 +1,4 @@ | |||
1 | /* $OpenBSD: ssh-sk-helper.c,v 1.3 2019/11/12 19:33:08 markus Exp $ */ | 1 | /* $OpenBSD: ssh-sk-helper.c,v 1.4 2019/12/13 19:11:14 djm Exp $ */ |
2 | /* | 2 | /* |
3 | * Copyright (c) 2019 Google LLC | 3 | * Copyright (c) 2019 Google LLC |
4 | * | 4 | * |
@@ -18,20 +18,16 @@ | |||
18 | /* | 18 | /* |
19 | * This is a tiny program used to isolate the address space used for | 19 | * This is a tiny program used to isolate the address space used for |
20 | * security key middleware signing operations from ssh-agent. It is similar | 20 | * security key middleware signing operations from ssh-agent. It is similar |
21 | * to ssh-pkcs11-helper.c but considerably simpler as the signing operation | 21 | * to ssh-pkcs11-helper.c but considerably simpler as the operations for |
22 | * for this case are stateless. | 22 | * security keys are stateless. |
23 | * | 23 | * |
24 | * It receives a signing request (key, provider, message, flags) from | 24 | * Please crank SSH_SK_HELPER_VERSION in sshkey.h for any incompatible |
25 | * stdin, attempts to perform a signature using the security key provider | 25 | * protocol changes. |
26 | * and returns the resultant signature via stdout. | ||
27 | * | ||
28 | * In the future, this program might gain additional functions to support | ||
29 | * FIDO2 tokens such as enumerating resident keys. When this happens it will | ||
30 | * be necessary to crank SSH_SK_HELPER_VERSION below. | ||
31 | */ | 26 | */ |
32 | 27 | ||
33 | #include "includes.h" | 28 | #include "includes.h" |
34 | 29 | ||
30 | #include <limits.h> | ||
35 | #include <stdarg.h> | 31 | #include <stdarg.h> |
36 | #include <stdio.h> | 32 | #include <stdio.h> |
37 | #include <stdlib.h> | 33 | #include <stdlib.h> |
@@ -54,19 +50,112 @@ | |||
54 | #ifdef ENABLE_SK | 50 | #ifdef ENABLE_SK |
55 | extern char *__progname; | 51 | extern char *__progname; |
56 | 52 | ||
57 | int | 53 | static struct sshbuf * |
58 | main(int argc, char **argv) | 54 | process_sign(struct sshbuf *req) |
59 | { | 55 | { |
60 | SyslogFacility log_facility = SYSLOG_FACILITY_AUTH; | 56 | int r = SSH_ERR_INTERNAL_ERROR; |
61 | LogLevel log_level = SYSLOG_LEVEL_ERROR; | 57 | struct sshbuf *resp, *kbuf; |
62 | struct sshbuf *req, *resp, *kbuf; | ||
63 | struct sshkey *key; | 58 | struct sshkey *key; |
64 | uint32_t compat; | 59 | uint32_t compat; |
65 | const u_char *message; | 60 | const u_char *message; |
66 | u_char version, *sig; | 61 | u_char *sig; |
67 | size_t msglen, siglen; | 62 | size_t msglen, siglen; |
68 | char *provider; | 63 | char *provider; |
64 | |||
65 | if ((r = sshbuf_froms(req, &kbuf)) != 0 || | ||
66 | (r = sshbuf_get_cstring(req, &provider, NULL)) != 0 || | ||
67 | (r = sshbuf_get_string_direct(req, &message, &msglen)) != 0 || | ||
68 | (r = sshbuf_get_cstring(req, NULL, NULL)) != 0 || /* alg */ | ||
69 | (r = sshbuf_get_u32(req, &compat)) != 0) | ||
70 | fatal("%s: buffer error: %s", __progname, ssh_err(r)); | ||
71 | if (sshbuf_len(req) != 0) | ||
72 | fatal("%s: trailing data in request", __progname); | ||
73 | |||
74 | if ((r = sshkey_private_deserialize(kbuf, &key)) != 0) | ||
75 | fatal("Unable to parse private key: %s", ssh_err(r)); | ||
76 | if (!sshkey_is_sk(key)) | ||
77 | fatal("Unsupported key type %s", sshkey_ssh_name(key)); | ||
78 | |||
79 | debug("%s: ready to sign with key %s, provider %s: " | ||
80 | "msg len %zu, compat 0x%lx", __progname, sshkey_type(key), | ||
81 | provider, msglen, (u_long)compat); | ||
82 | |||
83 | if ((r = sshsk_sign(provider, key, &sig, &siglen, | ||
84 | message, msglen, compat)) != 0) | ||
85 | fatal("Signing failed: %s", ssh_err(r)); | ||
86 | |||
87 | if ((resp = sshbuf_new()) == NULL) | ||
88 | fatal("%s: sshbuf_new failed", __progname); | ||
89 | |||
90 | if ((r = sshbuf_put_string(resp, sig, siglen)) != 0) | ||
91 | fatal("%s: buffer error: %s", __progname, ssh_err(r)); | ||
92 | |||
93 | sshbuf_free(kbuf); | ||
94 | free(provider); | ||
95 | |||
96 | return resp; | ||
97 | } | ||
98 | |||
99 | static struct sshbuf * | ||
100 | process_enroll(struct sshbuf *req) | ||
101 | { | ||
102 | int r; | ||
103 | u_int type; | ||
104 | char *provider; | ||
105 | char *application; | ||
106 | uint8_t flags; | ||
107 | struct sshbuf *challenge, *attest, *kbuf, *resp; | ||
108 | struct sshkey *key; | ||
109 | |||
110 | if ((resp = sshbuf_new()) == NULL || | ||
111 | (attest = sshbuf_new()) == NULL || | ||
112 | (kbuf = sshbuf_new()) == NULL) | ||
113 | fatal("%s: sshbuf_new failed", __progname); | ||
114 | |||
115 | if ((r = sshbuf_get_u32(req, &type)) != 0 || | ||
116 | (r = sshbuf_get_cstring(req, &provider, NULL)) != 0 || | ||
117 | (r = sshbuf_get_cstring(req, &application, NULL)) != 0 || | ||
118 | (r = sshbuf_get_u8(req, &flags)) != 0 || | ||
119 | (r = sshbuf_froms(req, &challenge)) != 0) | ||
120 | fatal("%s: buffer error: %s", __progname, ssh_err(r)); | ||
121 | if (sshbuf_len(req) != 0) | ||
122 | fatal("%s: trailing data in request", __progname); | ||
123 | |||
124 | if (type > INT_MAX) | ||
125 | fatal("%s: bad type %u", __progname, type); | ||
126 | if (sshbuf_len(challenge) == 0) { | ||
127 | sshbuf_free(challenge); | ||
128 | challenge = NULL; | ||
129 | } | ||
130 | |||
131 | if ((r = sshsk_enroll((int)type, provider, application, flags, | ||
132 | challenge, &key, attest)) != 0) | ||
133 | fatal("%s: sshsk_enroll failed: %s", __progname, ssh_err(r)); | ||
134 | |||
135 | if ((r = sshkey_private_serialize(key, kbuf)) != 0) | ||
136 | fatal("%s: serialize private key: %s", __progname, ssh_err(r)); | ||
137 | if ((r = sshbuf_put_stringb(resp, kbuf)) != 0 || | ||
138 | (r = sshbuf_put_stringb(resp, attest)) != 0) | ||
139 | fatal("%s: buffer error: %s", __progname, ssh_err(r)); | ||
140 | |||
141 | sshkey_free(key); | ||
142 | sshbuf_free(kbuf); | ||
143 | sshbuf_free(attest); | ||
144 | sshbuf_free(challenge); | ||
145 | free(provider); | ||
146 | free(application); | ||
147 | |||
148 | return resp; | ||
149 | } | ||
150 | |||
151 | int | ||
152 | main(int argc, char **argv) | ||
153 | { | ||
154 | SyslogFacility log_facility = SYSLOG_FACILITY_AUTH; | ||
155 | LogLevel log_level = SYSLOG_LEVEL_ERROR; | ||
69 | int in, out, ch, r, log_stderr = 0; | 156 | int in, out, ch, r, log_stderr = 0; |
157 | u_int rtype; | ||
158 | uint8_t version; | ||
70 | 159 | ||
71 | sanitise_stdfd(); | 160 | sanitise_stdfd(); |
72 | log_init(__progname, log_level, log_facility, log_stderr); | 161 | log_init(__progname, log_level, log_facility, log_stderr); |
@@ -98,7 +187,7 @@ main(int argc, char **argv) | |||
98 | close(STDOUT_FILENO); | 187 | close(STDOUT_FILENO); |
99 | sanitise_stdfd(); /* resets to /dev/null */ | 188 | sanitise_stdfd(); /* resets to /dev/null */ |
100 | 189 | ||
101 | if ((req = sshbuf_new()) == NULL || (resp = sshbuf_new()) == NULL) | 190 | if ((req = sshbuf_new()) == NULL) |
102 | fatal("%s: sshbuf_new failed", __progname); | 191 | fatal("%s: sshbuf_new failed", __progname); |
103 | if (ssh_msg_recv(in, req) < 0) | 192 | if (ssh_msg_recv(in, req) < 0) |
104 | fatal("ssh_msg_recv failed"); | 193 | fatal("ssh_msg_recv failed"); |
@@ -111,33 +200,26 @@ main(int argc, char **argv) | |||
111 | fatal("unsupported version: received %d, expected %d", | 200 | fatal("unsupported version: received %d, expected %d", |
112 | version, SSH_SK_HELPER_VERSION); | 201 | version, SSH_SK_HELPER_VERSION); |
113 | } | 202 | } |
114 | if ((r = sshbuf_froms(req, &kbuf)) != 0 || | ||
115 | (r = sshkey_private_deserialize(kbuf, &key)) != 0) | ||
116 | fatal("Unable to parse key: %s", ssh_err(r)); | ||
117 | if (!sshkey_is_sk(key)) | ||
118 | fatal("Unsupported key type %s", sshkey_ssh_name(key)); | ||
119 | 203 | ||
120 | if ((r = sshbuf_get_cstring(req, &provider, NULL)) != 0 || | 204 | if ((r = sshbuf_get_u32(req, &rtype)) != 0) |
121 | (r = sshbuf_get_string_direct(req, &message, &msglen)) != 0 || | ||
122 | (r = sshbuf_get_u32(req, &compat)) != 0) | ||
123 | fatal("%s: buffer error: %s", __progname, ssh_err(r)); | 205 | fatal("%s: buffer error: %s", __progname, ssh_err(r)); |
124 | if (sshbuf_len(req) != 0) | ||
125 | fatal("%s: trailing data in request", __progname); | ||
126 | |||
127 | debug("%s: ready to sign with key %s, provider %s: " | ||
128 | "msg len %zu, compat 0x%lx", __progname, sshkey_type(key), | ||
129 | provider, msglen, (u_long)compat); | ||
130 | |||
131 | if ((r = sshsk_sign(provider, key, &sig, &siglen, | ||
132 | message, msglen, compat)) != 0) | ||
133 | fatal("Signing failed: %s", ssh_err(r)); | ||
134 | 206 | ||
135 | /* send reply */ | 207 | switch (rtype) { |
136 | if ((r = sshbuf_put_string(resp, sig, siglen)) != 0) | 208 | case SSH_SK_HELPER_SIGN: |
137 | fatal("%s: buffer error: %s", __progname, ssh_err(r)); | 209 | resp = process_sign(req); |
210 | break; | ||
211 | case SSH_SK_HELPER_ENROLL: | ||
212 | resp = process_enroll(req); | ||
213 | break; | ||
214 | default: | ||
215 | fatal("%s: unsupported request type %u", __progname, rtype); | ||
216 | } | ||
217 | sshbuf_free(req); | ||
138 | debug("%s: reply len %zu", __progname, sshbuf_len(resp)); | 218 | debug("%s: reply len %zu", __progname, sshbuf_len(resp)); |
219 | |||
139 | if (ssh_msg_send(out, SSH_SK_HELPER_VERSION, resp) == -1) | 220 | if (ssh_msg_send(out, SSH_SK_HELPER_VERSION, resp) == -1) |
140 | fatal("ssh_msg_send failed"); | 221 | fatal("ssh_msg_send failed"); |
222 | sshbuf_free(resp); | ||
141 | close(out); | 223 | close(out); |
142 | 224 | ||
143 | return (0); | 225 | return (0); |