]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - crypto/openssh/ssh-sk-client.c
contrib/tzdata: import tzdata 2021e
[FreeBSD/FreeBSD.git] / crypto / openssh / ssh-sk-client.c
1 /* $OpenBSD: ssh-sk-client.c,v 1.9 2021/04/03 06:18:41 djm Exp $ */
2 /*
3  * Copyright (c) 2019 Google LLC
4  *
5  * Permission to use, copy, modify, and distribute this software for any
6  * purpose with or without fee is hereby granted, provided that the above
7  * copyright notice and this permission notice appear in all copies.
8  *
9  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16  */
17
18 #include "includes.h"
19
20 #include <sys/types.h>
21 #include <sys/socket.h>
22 #include <sys/wait.h>
23
24 #include <fcntl.h>
25 #include <limits.h>
26 #include <errno.h>
27 #include <signal.h>
28 #include <stdarg.h>
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <unistd.h>
33
34 #include "log.h"
35 #include "ssherr.h"
36 #include "sshbuf.h"
37 #include "sshkey.h"
38 #include "msg.h"
39 #include "digest.h"
40 #include "pathnames.h"
41 #include "ssh-sk.h"
42 #include "misc.h"
43
44 /* #define DEBUG_SK 1 */
45
46 static int
47 start_helper(int *fdp, pid_t *pidp, void (**osigchldp)(int))
48 {
49         void (*osigchld)(int);
50         int oerrno, pair[2];
51         pid_t pid;
52         char *helper, *verbosity = NULL;
53
54         *fdp = -1;
55         *pidp = 0;
56         *osigchldp = SIG_DFL;
57
58         helper = getenv("SSH_SK_HELPER");
59         if (helper == NULL || strlen(helper) == 0)
60                 helper = _PATH_SSH_SK_HELPER;
61         if (access(helper, X_OK) != 0) {
62                 oerrno = errno;
63                 error_f("helper \"%s\" unusable: %s", helper, strerror(errno));
64                 errno = oerrno;
65                 return SSH_ERR_SYSTEM_ERROR;
66         }
67 #ifdef DEBUG_SK
68         verbosity = "-vvv";
69 #endif
70
71         /* Start helper */
72         if (socketpair(AF_UNIX, SOCK_STREAM, 0, pair) == -1) {
73                 error("socketpair: %s", strerror(errno));
74                 return SSH_ERR_SYSTEM_ERROR;
75         }
76         osigchld = ssh_signal(SIGCHLD, SIG_DFL);
77         if ((pid = fork()) == -1) {
78                 oerrno = errno;
79                 error("fork: %s", strerror(errno));
80                 close(pair[0]);
81                 close(pair[1]);
82                 ssh_signal(SIGCHLD, osigchld);
83                 errno = oerrno;
84                 return SSH_ERR_SYSTEM_ERROR;
85         }
86         if (pid == 0) {
87                 if ((dup2(pair[1], STDIN_FILENO) == -1) ||
88                     (dup2(pair[1], STDOUT_FILENO) == -1)) {
89                         error_f("dup2: %s", strerror(errno));
90                         _exit(1);
91                 }
92                 close(pair[0]);
93                 close(pair[1]);
94                 closefrom(STDERR_FILENO + 1);
95                 debug_f("starting %s %s", helper,
96                     verbosity == NULL ? "" : verbosity);
97                 execlp(helper, helper, verbosity, (char *)NULL);
98                 error_f("execlp: %s", strerror(errno));
99                 _exit(1);
100         }
101         close(pair[1]);
102
103         /* success */
104         debug3_f("started pid=%ld", (long)pid);
105         *fdp = pair[0];
106         *pidp = pid;
107         *osigchldp = osigchld;
108         return 0;
109 }
110
111 static int
112 reap_helper(pid_t pid)
113 {
114         int status, oerrno;
115
116         debug3_f("pid=%ld", (long)pid);
117
118         errno = 0;
119         while (waitpid(pid, &status, 0) == -1) {
120                 if (errno == EINTR) {
121                         errno = 0;
122                         continue;
123                 }
124                 oerrno = errno;
125                 error_f("waitpid: %s", strerror(errno));
126                 errno = oerrno;
127                 return SSH_ERR_SYSTEM_ERROR;
128         }
129         if (!WIFEXITED(status)) {
130                 error_f("helper exited abnormally");
131                 return SSH_ERR_AGENT_FAILURE;
132         } else if (WEXITSTATUS(status) != 0) {
133                 error_f("helper exited with non-zero exit status");
134                 return SSH_ERR_AGENT_FAILURE;
135         }
136         return 0;
137 }
138
139 static int
140 client_converse(struct sshbuf *msg, struct sshbuf **respp, u_int type)
141 {
142         int oerrno, fd, r2, ll, r = SSH_ERR_INTERNAL_ERROR;
143         u_int rtype, rerr;
144         pid_t pid;
145         u_char version;
146         void (*osigchld)(int);
147         struct sshbuf *req = NULL, *resp = NULL;
148         *respp = NULL;
149
150         if ((r = start_helper(&fd, &pid, &osigchld)) != 0)
151                 return r;
152
153         if ((req = sshbuf_new()) == NULL || (resp = sshbuf_new()) == NULL) {
154                 r = SSH_ERR_ALLOC_FAIL;
155                 goto out;
156         }
157         /* Request preamble: type, log_on_stderr, log_level */
158         ll = log_level_get();
159         if ((r = sshbuf_put_u32(req, type)) != 0 ||
160             (r = sshbuf_put_u8(req, log_is_on_stderr() != 0)) != 0 ||
161             (r = sshbuf_put_u32(req, ll < 0 ? 0 : ll)) != 0 ||
162             (r = sshbuf_putb(req, msg)) != 0) {
163                 error_fr(r, "compose");
164                 goto out;
165         }
166         if ((r = ssh_msg_send(fd, SSH_SK_HELPER_VERSION, req)) != 0) {
167                 error_fr(r, "send");
168                 goto out;
169         }
170         if ((r = ssh_msg_recv(fd, resp)) != 0) {
171                 error_fr(r, "receive");
172                 goto out;
173         }
174         if ((r = sshbuf_get_u8(resp, &version)) != 0) {
175                 error_fr(r, "parse version");
176                 goto out;
177         }
178         if (version != SSH_SK_HELPER_VERSION) {
179                 error_f("unsupported version: got %u, expected %u",
180                     version, SSH_SK_HELPER_VERSION);
181                 r = SSH_ERR_INVALID_FORMAT;
182                 goto out;
183         }
184         if ((r = sshbuf_get_u32(resp, &rtype)) != 0) {
185                 error_fr(r, "parse message type");
186                 goto out;
187         }
188         if (rtype == SSH_SK_HELPER_ERROR) {
189                 if ((r = sshbuf_get_u32(resp, &rerr)) != 0) {
190                         error_fr(r, "parse");
191                         goto out;
192                 }
193                 debug_f("helper returned error -%u", rerr);
194                 /* OpenSSH error values are negative; encoded as -err on wire */
195                 if (rerr == 0 || rerr >= INT_MAX)
196                         r = SSH_ERR_INTERNAL_ERROR;
197                 else
198                         r = -(int)rerr;
199                 goto out;
200         } else if (rtype != type) {
201                 error_f("helper returned incorrect message type %u, "
202                     "expecting %u", rtype, type);
203                 r = SSH_ERR_INTERNAL_ERROR;
204                 goto out;
205         }
206         /* success */
207         r = 0;
208  out:
209         oerrno = errno;
210         close(fd);
211         if ((r2 = reap_helper(pid)) != 0) {
212                 if (r == 0) {
213                         r = r2;
214                         oerrno = errno;
215                 }
216         }
217         if (r == 0) {
218                 *respp = resp;
219                 resp = NULL;
220         }
221         sshbuf_free(req);
222         sshbuf_free(resp);
223         ssh_signal(SIGCHLD, osigchld);
224         errno = oerrno;
225         return r;
226
227 }
228
229 int
230 sshsk_sign(const char *provider, struct sshkey *key,
231     u_char **sigp, size_t *lenp, const u_char *data, size_t datalen,
232     u_int compat, const char *pin)
233 {
234         int oerrno, r = SSH_ERR_INTERNAL_ERROR;
235         char *fp = NULL;
236         struct sshbuf *kbuf = NULL, *req = NULL, *resp = NULL;
237
238         *sigp = NULL;
239         *lenp = 0;
240
241 #ifndef ENABLE_SK
242         return SSH_ERR_KEY_TYPE_UNKNOWN;
243 #endif
244
245         if ((kbuf = sshbuf_new()) == NULL ||
246             (req = sshbuf_new()) == NULL) {
247                 r = SSH_ERR_ALLOC_FAIL;
248                 goto out;
249         }
250
251         if ((r = sshkey_private_serialize(key, kbuf)) != 0) {
252                 error_fr(r, "encode key");
253                 goto out;
254         }
255         if ((r = sshbuf_put_stringb(req, kbuf)) != 0 ||
256             (r = sshbuf_put_cstring(req, provider)) != 0 ||
257             (r = sshbuf_put_string(req, data, datalen)) != 0 ||
258             (r = sshbuf_put_cstring(req, NULL)) != 0 || /* alg */
259             (r = sshbuf_put_u32(req, compat)) != 0 ||
260             (r = sshbuf_put_cstring(req, pin)) != 0) {
261                 error_fr(r, "compose");
262                 goto out;
263         }
264
265         if ((fp = sshkey_fingerprint(key, SSH_FP_HASH_DEFAULT,
266             SSH_FP_DEFAULT)) == NULL) {
267                 error_f("sshkey_fingerprint failed");
268                 r = SSH_ERR_ALLOC_FAIL;
269                 goto out;
270         }
271         if ((r = client_converse(req, &resp, SSH_SK_HELPER_SIGN)) != 0)
272                 goto out;
273
274         if ((r = sshbuf_get_string(resp, sigp, lenp)) != 0) {
275                 error_fr(r, "parse signature");
276                 r = SSH_ERR_INVALID_FORMAT;
277                 goto out;
278         }
279         if (sshbuf_len(resp) != 0) {
280                 error_f("trailing data in response");
281                 r = SSH_ERR_INVALID_FORMAT;
282                 goto out;
283         }
284         /* success */
285         r = 0;
286  out:
287         oerrno = errno;
288         if (r != 0) {
289                 freezero(*sigp, *lenp);
290                 *sigp = NULL;
291                 *lenp = 0;
292         }
293         sshbuf_free(kbuf);
294         sshbuf_free(req);
295         sshbuf_free(resp);
296         errno = oerrno;
297         return r;
298 }
299
300 int
301 sshsk_enroll(int type, const char *provider_path, const char *device,
302     const char *application, const char *userid, uint8_t flags,
303     const char *pin, struct sshbuf *challenge_buf,
304     struct sshkey **keyp, struct sshbuf *attest)
305 {
306         int oerrno, r = SSH_ERR_INTERNAL_ERROR;
307         struct sshbuf *kbuf = NULL, *abuf = NULL, *req = NULL, *resp = NULL;
308         struct sshkey *key = NULL;
309
310         *keyp = NULL;
311         if (attest != NULL)
312                 sshbuf_reset(attest);
313
314 #ifndef ENABLE_SK
315         return SSH_ERR_KEY_TYPE_UNKNOWN;
316 #endif
317
318         if (type < 0)
319                 return SSH_ERR_INVALID_ARGUMENT;
320
321         if ((abuf = sshbuf_new()) == NULL ||
322             (kbuf = sshbuf_new()) == NULL ||
323             (req = sshbuf_new()) == NULL) {
324                 r = SSH_ERR_ALLOC_FAIL;
325                 goto out;
326         }
327
328         if ((r = sshbuf_put_u32(req, (u_int)type)) != 0 ||
329             (r = sshbuf_put_cstring(req, provider_path)) != 0 ||
330             (r = sshbuf_put_cstring(req, device)) != 0 ||
331             (r = sshbuf_put_cstring(req, application)) != 0 ||
332             (r = sshbuf_put_cstring(req, userid)) != 0 ||
333             (r = sshbuf_put_u8(req, flags)) != 0 ||
334             (r = sshbuf_put_cstring(req, pin)) != 0 ||
335             (r = sshbuf_put_stringb(req, challenge_buf)) != 0) {
336                 error_fr(r, "compose");
337                 goto out;
338         }
339
340         if ((r = client_converse(req, &resp, SSH_SK_HELPER_ENROLL)) != 0)
341                 goto out;
342
343         if ((r = sshbuf_get_stringb(resp, kbuf)) != 0 ||
344             (r = sshbuf_get_stringb(resp, abuf)) != 0) {
345                 error_fr(r, "parse");
346                 r = SSH_ERR_INVALID_FORMAT;
347                 goto out;
348         }
349         if (sshbuf_len(resp) != 0) {
350                 error_f("trailing data in response");
351                 r = SSH_ERR_INVALID_FORMAT;
352                 goto out;
353         }
354         if ((r = sshkey_private_deserialize(kbuf, &key)) != 0) {
355                 error_fr(r, "encode");
356                 goto out;
357         }
358         if (attest != NULL && (r = sshbuf_putb(attest, abuf)) != 0) {
359                 error_fr(r, "encode attestation information");
360                 goto out;
361         }
362
363         /* success */
364         r = 0;
365         *keyp = key;
366         key = NULL;
367  out:
368         oerrno = errno;
369         sshkey_free(key);
370         sshbuf_free(kbuf);
371         sshbuf_free(abuf);
372         sshbuf_free(req);
373         sshbuf_free(resp);
374         errno = oerrno;
375         return r;
376 }
377
378 int
379 sshsk_load_resident(const char *provider_path, const char *device,
380     const char *pin, struct sshkey ***keysp, size_t *nkeysp)
381 {
382         int oerrno, r = SSH_ERR_INTERNAL_ERROR;
383         struct sshbuf *kbuf = NULL, *req = NULL, *resp = NULL;
384         struct sshkey *key = NULL, **keys = NULL, **tmp;
385         size_t i, nkeys = 0;
386
387         *keysp = NULL;
388         *nkeysp = 0;
389
390         if ((resp = sshbuf_new()) == NULL ||
391             (kbuf = sshbuf_new()) == NULL ||
392             (req = sshbuf_new()) == NULL) {
393                 r = SSH_ERR_ALLOC_FAIL;
394                 goto out;
395         }
396
397         if ((r = sshbuf_put_cstring(req, provider_path)) != 0 ||
398             (r = sshbuf_put_cstring(req, device)) != 0 ||
399             (r = sshbuf_put_cstring(req, pin)) != 0) {
400                 error_fr(r, "compose");
401                 goto out;
402         }
403
404         if ((r = client_converse(req, &resp, SSH_SK_HELPER_LOAD_RESIDENT)) != 0)
405                 goto out;
406
407         while (sshbuf_len(resp) != 0) {
408                 /* key, comment */
409                 if ((r = sshbuf_get_stringb(resp, kbuf)) != 0 ||
410                     (r = sshbuf_get_cstring(resp, NULL, NULL)) != 0) {
411                         error_fr(r, "parse signature");
412                         r = SSH_ERR_INVALID_FORMAT;
413                         goto out;
414                 }
415                 if ((r = sshkey_private_deserialize(kbuf, &key)) != 0) {
416                         error_fr(r, "decode key");
417                         goto out;
418                 }
419                 if ((tmp = recallocarray(keys, nkeys, nkeys + 1,
420                     sizeof(*keys))) == NULL) {
421                         error_f("recallocarray keys failed");
422                         goto out;
423                 }
424                 debug_f("keys[%zu]: %s %s", nkeys, sshkey_type(key),
425                     key->sk_application);
426                 keys = tmp;
427                 keys[nkeys++] = key;
428                 key = NULL;
429         }
430
431         /* success */
432         r = 0;
433         *keysp = keys;
434         *nkeysp = nkeys;
435         keys = NULL;
436         nkeys = 0;
437  out:
438         oerrno = errno;
439         for (i = 0; i < nkeys; i++)
440                 sshkey_free(keys[i]);
441         free(keys);
442         sshkey_free(key);
443         sshbuf_free(kbuf);
444         sshbuf_free(req);
445         sshbuf_free(resp);
446         errno = oerrno;
447         return r;
448 }