blob: c1f2d803ffdcb748256e9684953113cb86100538 [file] [log] [blame]
djm@openbsd.org2a9c9f72019-09-03 08:34:19 +00001/*
2 * Copyright (c) 2019 Google LLC
3 *
4 * Permission to use, copy, modify, and distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
7 *
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 */
16
17#include <stdio.h>
18#include <stdlib.h>
19#include <stdarg.h>
20#include <errno.h>
21#include <string.h>
22#include <unistd.h>
23
24#include "authfd.h"
25#include "authfile.h"
26#include "log.h"
27#include "misc.h"
28#include "sshbuf.h"
29#include "sshsig.h"
30#include "ssherr.h"
31#include "sshkey.h"
32#include "match.h"
33#include "digest.h"
34
35#define SIG_VERSION 0x01
36#define MAGIC_PREAMBLE "SSHSIG"
37#define MAGIC_PREAMBLE_LEN (sizeof(MAGIC_PREAMBLE) - 1)
38#define BEGIN_SIGNATURE "-----BEGIN SSH SIGNATURE-----\n"
39#define END_SIGNATURE "-----END SSH SIGNATURE-----"
40#define RSA_SIGN_ALG "rsa-sha2-512" /* XXX maybe make configurable */
41#define RSA_SIGN_ALLOWED "rsa-sha2-512,rsa-sha2-256"
42#define HASHALG_DEFAULT "sha512" /* XXX maybe make configurable */
43#define HASHALG_ALLOWED "sha256,sha512"
44
45int
46sshsig_armor(const struct sshbuf *blob, struct sshbuf **out)
47{
48 struct sshbuf *buf = NULL;
49 int r = SSH_ERR_INTERNAL_ERROR;
50
51 *out = NULL;
52
53 if ((buf = sshbuf_new()) == NULL) {
54 error("%s: sshbuf_new failed", __func__);
55 r = SSH_ERR_ALLOC_FAIL;
56 goto out;
57 }
58
59 if ((r = sshbuf_put(buf, BEGIN_SIGNATURE,
60 sizeof(BEGIN_SIGNATURE)-1)) != 0) {
61 error("%s: sshbuf_putf failed: %s", __func__, ssh_err(r));
62 goto out;
63 }
64
65 if ((r = sshbuf_dtob64(blob, buf, 1)) != 0) {
66 error("%s: Couldn't base64 encode signature blob: %s",
67 __func__, ssh_err(r));
68 goto out;
69 }
70
71 if ((r = sshbuf_put(buf, END_SIGNATURE,
72 sizeof(END_SIGNATURE)-1)) != 0 ||
73 (r = sshbuf_put_u8(buf, '\n')) != 0) {
74 error("%s: sshbuf_put failed: %s", __func__, ssh_err(r));
75 goto out;
76 }
77 /* success */
78 *out = buf;
79 buf = NULL; /* transferred */
80 r = 0;
81 out:
82 sshbuf_free(buf);
83 return r;
84}
85
86int
87sshsig_dearmor(struct sshbuf *sig, struct sshbuf **out)
88{
89 int r;
90 size_t eoffset = 0;
91 struct sshbuf *buf = NULL;
92 struct sshbuf *sbuf = NULL;
93 char *b64 = NULL;
94
95 if ((sbuf = sshbuf_fromb(sig)) == NULL) {
96 error("%s: sshbuf_fromb failed", __func__);
97 return SSH_ERR_ALLOC_FAIL;
98 }
99
100 if ((r = sshbuf_cmp(sbuf, 0,
101 BEGIN_SIGNATURE, sizeof(BEGIN_SIGNATURE)-1)) != 0) {
102 error("Couldn't parse signature: missing header");
103 goto done;
104 }
105
106 if ((r = sshbuf_consume(sbuf, sizeof(BEGIN_SIGNATURE)-1)) != 0) {
107 error("%s: sshbuf_consume failed: %s", __func__, ssh_err(r));
108 goto done;
109 }
110
111 if ((r = sshbuf_find(sbuf, 0, "\n" END_SIGNATURE,
112 sizeof("\n" END_SIGNATURE)-1, &eoffset)) != 0) {
113 error("Couldn't parse signature: missing footer");
114 goto done;
115 }
116
117 if ((r = sshbuf_consume_end(sbuf, sshbuf_len(sbuf)-eoffset)) != 0) {
118 error("%s: sshbuf_consume failed: %s", __func__, ssh_err(r));
119 goto done;
120 }
121
122 if ((b64 = sshbuf_dup_string(sbuf)) == NULL) {
123 error("%s: sshbuf_dup_string failed", __func__);
124 r = SSH_ERR_ALLOC_FAIL;
125 goto done;
126 }
127
128 if ((buf = sshbuf_new()) == NULL) {
129 error("%s: sshbuf_new() failed", __func__);
130 r = SSH_ERR_ALLOC_FAIL;
131 goto done;
132 }
133
134 if ((r = sshbuf_b64tod(buf, b64)) != 0) {
135 error("Coundn't decode signature: %s", ssh_err(r));
136 goto done;
137 }
138
139 /* success */
140 *out = buf;
141 r = 0;
142 buf = NULL; /* transferred */
143done:
144 sshbuf_free(buf);
145 sshbuf_free(sbuf);
146 free(b64);
147 return r;
148}
149
150static int
151sshsig_wrap_sign(struct sshkey *key, const char *hashalg,
152 const struct sshbuf *h_message, const char *sig_namespace,
153 struct sshbuf **out, sshsig_signer *signer, void *signer_ctx)
154{
155 int r;
156 size_t slen = 0;
157 u_char *sig = NULL;
158 struct sshbuf *blob = NULL;
159 struct sshbuf *tosign = NULL;
160 const char *sign_alg = NULL;
161
162 if ((tosign = sshbuf_new()) == NULL ||
163 (blob = sshbuf_new()) == NULL) {
164 error("%s: sshbuf_new failed", __func__);
165 r = SSH_ERR_ALLOC_FAIL;
166 goto done;
167 }
168
169 if ((r = sshbuf_put(tosign, MAGIC_PREAMBLE, MAGIC_PREAMBLE_LEN)) != 0 ||
170 (r = sshbuf_put_cstring(tosign, sig_namespace)) != 0 ||
171 (r = sshbuf_put_string(tosign, NULL, 0)) != 0 || /* reserved */
172 (r = sshbuf_put_cstring(tosign, hashalg)) != 0 ||
173 (r = sshbuf_putb(tosign, h_message)) != 0) {
174 error("Couldn't construct message to sign: %s", ssh_err(r));
175 goto done;
176 }
177
178 /* If using RSA keys then default to a good signature algorithm */
179 if (sshkey_type_plain(key->type) == KEY_RSA)
180 sign_alg = RSA_SIGN_ALG;
181
182 if (signer != NULL) {
183 if ((r = signer(key, &sig, &slen,
184 sshbuf_ptr(tosign), sshbuf_len(tosign),
185 sign_alg, 0, signer_ctx)) != 0) {
186 error("Couldn't sign message: %s", ssh_err(r));
187 goto done;
188 }
189 } else {
190 if ((r = sshkey_sign(key, &sig, &slen,
191 sshbuf_ptr(tosign), sshbuf_len(tosign),
192 sign_alg, 0)) != 0) {
193 error("Couldn't sign message: %s", ssh_err(r));
194 goto done;
195 }
196 }
197
198 if ((r = sshbuf_put(blob, MAGIC_PREAMBLE, MAGIC_PREAMBLE_LEN)) != 0 ||
199 (r = sshbuf_put_u32(blob, SIG_VERSION)) != 0 ||
200 (r = sshkey_puts(key, blob)) != 0 ||
201 (r = sshbuf_put_cstring(blob, sig_namespace)) != 0 ||
202 (r = sshbuf_put_string(blob, NULL, 0)) != 0 || /* reserved */
203 (r = sshbuf_put_cstring(blob, hashalg)) != 0 ||
204 (r = sshbuf_put_string(blob, sig, slen)) != 0) {
205 error("Couldn't populate blob: %s", ssh_err(r));
206 goto done;
207 }
208
209 *out = blob;
210 blob = NULL;
211 r = 0;
212done:
213 free(sig);
214 sshbuf_free(blob);
215 sshbuf_free(tosign);
216 return r;
217}
218
219/* Check preamble and version. */
220static int
221sshsig_parse_preamble(struct sshbuf *buf)
222{
223 int r = SSH_ERR_INTERNAL_ERROR;
224 uint32_t sversion;
225
226 if ((r = sshbuf_cmp(buf, 0, MAGIC_PREAMBLE, MAGIC_PREAMBLE_LEN)) != 0 ||
227 (r = sshbuf_consume(buf, (sizeof(MAGIC_PREAMBLE)-1))) != 0 ||
228 (r = sshbuf_get_u32(buf, &sversion)) != 0) {
229 error("Couldn't verify signature: invalid format");
230 return r;
231 }
232
djm@openbsd.orgd637c4a2019-09-03 08:35:27 +0000233 if (sversion > SIG_VERSION) {
djm@openbsd.org2a9c9f72019-09-03 08:34:19 +0000234 error("Signature version %lu is larger than supported "
235 "version %u", (unsigned long)sversion, SIG_VERSION);
236 return SSH_ERR_INVALID_FORMAT;
237 }
238 return 0;
239}
240
241static int
242sshsig_check_hashalg(const char *hashalg)
243{
djm@openbsd.orgd637c4a2019-09-03 08:35:27 +0000244 if (hashalg == NULL ||
245 match_pattern_list(hashalg, HASHALG_ALLOWED, 0) == 1)
djm@openbsd.org2a9c9f72019-09-03 08:34:19 +0000246 return 0;
247 error("%s: unsupported hash algorithm \"%.100s\"", __func__, hashalg);
248 return SSH_ERR_SIGN_ALG_UNSUPPORTED;
249}
250
251static int
252sshsig_peek_hashalg(struct sshbuf *signature, char **hashalgp)
253{
254 struct sshbuf *buf = NULL;
255 char *hashalg = NULL;
256 int r = SSH_ERR_INTERNAL_ERROR;
257
258 if (hashalgp != NULL)
259 *hashalgp = NULL;
260 if ((buf = sshbuf_fromb(signature)) == NULL)
261 return SSH_ERR_ALLOC_FAIL;
262 if ((r = sshsig_parse_preamble(buf)) != 0)
263 goto done;
264 if ((r = sshbuf_get_string_direct(buf, NULL, NULL)) != 0 ||
265 (r = sshbuf_get_string_direct(buf, NULL, NULL)) != 0 ||
266 (r = sshbuf_get_string(buf, NULL, NULL)) != 0 ||
267 (r = sshbuf_get_cstring(buf, &hashalg, NULL)) != 0 ||
268 (r = sshbuf_get_string_direct(buf, NULL, NULL)) != 0) {
269 error("Couldn't parse signature blob: %s", ssh_err(r));
270 goto done;
271 }
djm@openbsd.org2a9c9f72019-09-03 08:34:19 +0000272
273 /* success */
274 r = 0;
275 *hashalgp = hashalg;
276 hashalg = NULL;
277 done:
278 free(hashalg);
279 sshbuf_free(buf);
280 return r;
281}
282
283static int
284sshsig_wrap_verify(struct sshbuf *signature, const char *hashalg,
285 const struct sshbuf *h_message, const char *expect_namespace,
286 struct sshkey **sign_keyp)
287{
288 int r = SSH_ERR_INTERNAL_ERROR;
289 struct sshbuf *buf = NULL, *toverify = NULL;
290 struct sshkey *key = NULL;
291 const u_char *sig;
292 char *got_namespace = NULL, *sigtype = NULL, *sig_hashalg = NULL;
293 size_t siglen;
294
djm@openbsd.orgd637c4a2019-09-03 08:35:27 +0000295 debug("%s: verify message length %zu", __func__, sshbuf_len(h_message));
djm@openbsd.org2a9c9f72019-09-03 08:34:19 +0000296 if (sign_keyp != NULL)
297 *sign_keyp = NULL;
298
299 if ((toverify = sshbuf_new()) == NULL) {
300 error("%s: sshbuf_new failed", __func__);
301 r = SSH_ERR_ALLOC_FAIL;
302 goto done;
303 }
djm@openbsd.org2a9c9f72019-09-03 08:34:19 +0000304 if ((r = sshbuf_put(toverify, MAGIC_PREAMBLE,
305 MAGIC_PREAMBLE_LEN)) != 0 ||
306 (r = sshbuf_put_cstring(toverify, expect_namespace)) != 0 ||
307 (r = sshbuf_put_string(toverify, NULL, 0)) != 0 || /* reserved */
308 (r = sshbuf_put_cstring(toverify, hashalg)) != 0 ||
309 (r = sshbuf_putb(toverify, h_message)) != 0) {
310 error("Couldn't construct message to verify: %s", ssh_err(r));
311 goto done;
312 }
313
314 if ((r = sshsig_parse_preamble(signature)) != 0)
315 goto done;
316
317 if ((r = sshkey_froms(signature, &key)) != 0 ||
318 (r = sshbuf_get_cstring(signature, &got_namespace, NULL)) != 0 ||
319 (r = sshbuf_get_string(signature, NULL, NULL)) != 0 ||
320 (r = sshbuf_get_cstring(signature, &sig_hashalg, NULL)) != 0 ||
321 (r = sshbuf_get_string_direct(signature, &sig, &siglen)) != 0) {
322 error("Couldn't parse signature blob: %s", ssh_err(r));
323 goto done;
324 }
325
326 if (sshbuf_len(signature) != 0) {
327 error("Signature contains trailing data");
328 r = SSH_ERR_INVALID_FORMAT;
329 goto done;
330 }
331
332 if (strcmp(expect_namespace, got_namespace) != 0) {
333 error("Couldn't verify signature: namespace does not match");
334 debug("%s: expected namespace \"%s\" received \"%s\"",
335 __func__, expect_namespace, got_namespace);
336 r = SSH_ERR_SIGNATURE_INVALID;
337 goto done;
338 }
339 if (strcmp(hashalg, sig_hashalg) != 0) {
340 error("Couldn't verify signature: hash algorithm mismatch");
341 debug("%s: expected algorithm \"%s\" received \"%s\"",
342 __func__, hashalg, sig_hashalg);
343 r = SSH_ERR_SIGNATURE_INVALID;
344 goto done;
345 }
346 /* Ensure that RSA keys use an acceptable signature algorithm */
347 if (sshkey_type_plain(key->type) == KEY_RSA) {
348 if ((r = sshkey_get_sigtype(sig, siglen, &sigtype)) != 0) {
349 error("Couldn't verify signature: unable to get "
350 "signature type: %s", ssh_err(r));
351 goto done;
352 }
353 if (match_pattern_list(sigtype, RSA_SIGN_ALLOWED, 0) != 1) {
354 error("Couldn't verify signature: unsupported RSA "
355 "signature algorithm %s", sigtype);
356 r = SSH_ERR_SIGN_ALG_UNSUPPORTED;
357 goto done;
358 }
359 }
360 if ((r = sshkey_verify(key, sig, siglen, sshbuf_ptr(toverify),
361 sshbuf_len(toverify), NULL, 0)) != 0) {
362 error("Signature verification failed: %s", ssh_err(r));
363 goto done;
364 }
365
366 /* success */
367 r = 0;
368 if (sign_keyp != NULL) {
369 *sign_keyp = key;
370 key = NULL; /* transferred */
371 }
372done:
373 free(got_namespace);
374 free(sigtype);
375 free(sig_hashalg);
376 sshbuf_free(buf);
377 sshbuf_free(toverify);
378 sshkey_free(key);
379 return r;
380}
381
djm@openbsd.orgd637c4a2019-09-03 08:35:27 +0000382static int
383hash_buffer(const struct sshbuf *m, const char *hashalg, struct sshbuf **bp)
djm@openbsd.org2a9c9f72019-09-03 08:34:19 +0000384{
djm@openbsd.orgd637c4a2019-09-03 08:35:27 +0000385 char *hex, hash[SSH_DIGEST_MAX_LENGTH];
djm@openbsd.org2a9c9f72019-09-03 08:34:19 +0000386 int alg, r = SSH_ERR_INTERNAL_ERROR;
djm@openbsd.orgd637c4a2019-09-03 08:35:27 +0000387 struct sshbuf *b = NULL;
djm@openbsd.org2a9c9f72019-09-03 08:34:19 +0000388
djm@openbsd.orgd637c4a2019-09-03 08:35:27 +0000389 *bp = NULL;
390 memset(hash, 0, sizeof(hash));
djm@openbsd.org2a9c9f72019-09-03 08:34:19 +0000391
392 if ((r = sshsig_check_hashalg(hashalg)) != 0)
393 return r;
394 if ((alg = ssh_digest_alg_by_name(hashalg)) == -1) {
395 error("%s: can't look up hash algorithm %s",
djm@openbsd.orgd637c4a2019-09-03 08:35:27 +0000396 __func__, hashalg);
djm@openbsd.org2a9c9f72019-09-03 08:34:19 +0000397 return SSH_ERR_INTERNAL_ERROR;
398 }
djm@openbsd.orgd637c4a2019-09-03 08:35:27 +0000399 if ((r = ssh_digest_buffer(alg, m, hash, sizeof(hash))) != 0) {
djm@openbsd.org2a9c9f72019-09-03 08:34:19 +0000400 error("%s: ssh_digest_buffer failed: %s", __func__, ssh_err(r));
401 return r;
402 }
djm@openbsd.orgd637c4a2019-09-03 08:35:27 +0000403 if ((hex = tohex(hash, ssh_digest_bytes(alg))) != NULL) {
404 debug3("%s: final hash: %s", __func__, hex);
405 freezero(hex, strlen(hex));
406 }
407 if ((b = sshbuf_new()) == NULL) {
djm@openbsd.org2a9c9f72019-09-03 08:34:19 +0000408 r = SSH_ERR_ALLOC_FAIL;
409 goto out;
410 }
djm@openbsd.orgd637c4a2019-09-03 08:35:27 +0000411 if ((r = sshbuf_put(b, hash, ssh_digest_bytes(alg))) != 0) {
412 error("%s: sshbuf_put: %s", __func__, ssh_err(r));
413 goto out;
414 }
415 *bp = b;
416 b = NULL; /* transferred */
417 /* success */
418 r = 0;
419 out:
420 sshbuf_free(b);
421 explicit_bzero(hash, sizeof(hash));
422 return 0;
423}
424
425int
426sshsig_signb(struct sshkey *key, const char *hashalg,
427 const struct sshbuf *message, const char *sig_namespace,
428 struct sshbuf **out, sshsig_signer *signer, void *signer_ctx)
429{
430 struct sshbuf *b = NULL;
431 int r = SSH_ERR_INTERNAL_ERROR;
432
433 if (hashalg == NULL)
434 hashalg = HASHALG_DEFAULT;
435 if (out != NULL)
436 *out = NULL;
437 if ((r = hash_buffer(message, hashalg, &b)) != 0) {
438 error("%s: hash_buffer failed: %s", __func__, ssh_err(r));
439 goto out;
440 }
djm@openbsd.org2a9c9f72019-09-03 08:34:19 +0000441 if ((r = sshsig_wrap_sign(key, hashalg, b, sig_namespace, out,
442 signer, signer_ctx)) != 0)
443 goto out;
444 /* success */
445 r = 0;
446 out:
447 sshbuf_free(b);
djm@openbsd.org2a9c9f72019-09-03 08:34:19 +0000448 return r;
449}
450
451int
djm@openbsd.orgd637c4a2019-09-03 08:35:27 +0000452sshsig_verifyb(struct sshbuf *signature, const struct sshbuf *message,
djm@openbsd.org2a9c9f72019-09-03 08:34:19 +0000453 const char *expect_namespace, struct sshkey **sign_keyp)
454{
djm@openbsd.org2a9c9f72019-09-03 08:34:19 +0000455 struct sshbuf *b = NULL;
djm@openbsd.orgd637c4a2019-09-03 08:35:27 +0000456 int r = SSH_ERR_INTERNAL_ERROR;
djm@openbsd.org2a9c9f72019-09-03 08:34:19 +0000457 char *hashalg = NULL;
458
459 if (sign_keyp != NULL)
460 *sign_keyp = NULL;
461
462 if ((r = sshsig_peek_hashalg(signature, &hashalg)) != 0)
463 return r;
djm@openbsd.orgd637c4a2019-09-03 08:35:27 +0000464 debug("%s: signature made with hash \"%s\"", __func__, hashalg);
465 if ((r = hash_buffer(message, hashalg, &b)) != 0) {
466 error("%s: hash_buffer failed: %s", __func__, ssh_err(r));
djm@openbsd.org2a9c9f72019-09-03 08:34:19 +0000467 goto out;
468 }
469 if ((r = sshsig_wrap_verify(signature, hashalg, b, expect_namespace,
470 sign_keyp)) != 0)
471 goto out;
472 /* success */
473 r = 0;
474 out:
475 sshbuf_free(b);
476 free(hashalg);
djm@openbsd.org2a9c9f72019-09-03 08:34:19 +0000477 return r;
478}
479
480static int
djm@openbsd.orgd637c4a2019-09-03 08:35:27 +0000481hash_file(int fd, const char *hashalg, struct sshbuf **bp)
djm@openbsd.org2a9c9f72019-09-03 08:34:19 +0000482{
djm@openbsd.orgd637c4a2019-09-03 08:35:27 +0000483 char *hex, rbuf[8192], hash[SSH_DIGEST_MAX_LENGTH];
djm@openbsd.org2a9c9f72019-09-03 08:34:19 +0000484 ssize_t n, total = 0;
485 struct ssh_digest_ctx *ctx;
djm@openbsd.orgd637c4a2019-09-03 08:35:27 +0000486 int alg, oerrno, r = SSH_ERR_INTERNAL_ERROR;
487 struct sshbuf *b = NULL;
djm@openbsd.org2a9c9f72019-09-03 08:34:19 +0000488
djm@openbsd.orgd637c4a2019-09-03 08:35:27 +0000489 *bp = NULL;
490 memset(hash, 0, sizeof(hash));
491
492 if ((r = sshsig_check_hashalg(hashalg)) != 0)
493 return r;
494 if ((alg = ssh_digest_alg_by_name(hashalg)) == -1) {
495 error("%s: can't look up hash algorithm %s",
496 __func__, hashalg);
497 return SSH_ERR_INTERNAL_ERROR;
498 }
499 if ((ctx = ssh_digest_start(alg)) == NULL) {
djm@openbsd.org2a9c9f72019-09-03 08:34:19 +0000500 error("%s: ssh_digest_start failed", __func__);
501 return SSH_ERR_INTERNAL_ERROR;
502 }
503 for (;;) {
504 if ((n = read(fd, rbuf, sizeof(rbuf))) == -1) {
505 if (errno == EINTR || errno == EAGAIN)
506 continue;
507 oerrno = errno;
508 error("%s: read: %s", __func__, strerror(errno));
509 ssh_digest_free(ctx);
510 errno = oerrno;
djm@openbsd.orgd637c4a2019-09-03 08:35:27 +0000511 r = SSH_ERR_SYSTEM_ERROR;
512 goto out;
djm@openbsd.org2a9c9f72019-09-03 08:34:19 +0000513 } else if (n == 0) {
514 debug2("%s: hashed %zu bytes", __func__, total);
515 break; /* EOF */
516 }
517 total += (size_t)n;
518 if ((r = ssh_digest_update(ctx, rbuf, (size_t)n)) != 0) {
519 error("%s: ssh_digest_update: %s",
520 __func__, ssh_err(r));
djm@openbsd.orgd637c4a2019-09-03 08:35:27 +0000521 goto out;
djm@openbsd.org2a9c9f72019-09-03 08:34:19 +0000522 }
523 }
djm@openbsd.orgd637c4a2019-09-03 08:35:27 +0000524 if ((r = ssh_digest_final(ctx, hash, sizeof(hash))) != 0) {
djm@openbsd.org2a9c9f72019-09-03 08:34:19 +0000525 error("%s: ssh_digest_final: %s", __func__, ssh_err(r));
djm@openbsd.orgd637c4a2019-09-03 08:35:27 +0000526 goto out;
djm@openbsd.org2a9c9f72019-09-03 08:34:19 +0000527 }
djm@openbsd.orgd637c4a2019-09-03 08:35:27 +0000528 if ((hex = tohex(hash, ssh_digest_bytes(alg))) != NULL) {
djm@openbsd.org2a9c9f72019-09-03 08:34:19 +0000529 debug3("%s: final hash: %s", __func__, hex);
530 freezero(hex, strlen(hex));
531 }
djm@openbsd.orgd637c4a2019-09-03 08:35:27 +0000532 if ((b = sshbuf_new()) == NULL) {
533 r = SSH_ERR_ALLOC_FAIL;
534 goto out;
535 }
536 if ((r = sshbuf_put(b, hash, ssh_digest_bytes(alg))) != 0) {
537 error("%s: sshbuf_put: %s", __func__, ssh_err(r));
538 goto out;
539 }
540 *bp = b;
541 b = NULL; /* transferred */
djm@openbsd.org2a9c9f72019-09-03 08:34:19 +0000542 /* success */
djm@openbsd.orgd637c4a2019-09-03 08:35:27 +0000543 r = 0;
544 out:
545 sshbuf_free(b);
djm@openbsd.org2a9c9f72019-09-03 08:34:19 +0000546 ssh_digest_free(ctx);
djm@openbsd.orgd637c4a2019-09-03 08:35:27 +0000547 explicit_bzero(hash, sizeof(hash));
djm@openbsd.org2a9c9f72019-09-03 08:34:19 +0000548 return 0;
549}
550
551int
552sshsig_sign_fd(struct sshkey *key, const char *hashalg,
553 int fd, const char *sig_namespace, struct sshbuf **out,
554 sshsig_signer *signer, void *signer_ctx)
555{
djm@openbsd.org2a9c9f72019-09-03 08:34:19 +0000556 struct sshbuf *b = NULL;
djm@openbsd.orgd637c4a2019-09-03 08:35:27 +0000557 int r = SSH_ERR_INTERNAL_ERROR;
djm@openbsd.org2a9c9f72019-09-03 08:34:19 +0000558
djm@openbsd.orgd637c4a2019-09-03 08:35:27 +0000559 if (hashalg == NULL)
560 hashalg = HASHALG_DEFAULT;
djm@openbsd.org2a9c9f72019-09-03 08:34:19 +0000561 if (out != NULL)
562 *out = NULL;
djm@openbsd.orgd637c4a2019-09-03 08:35:27 +0000563 if ((r = hash_file(fd, hashalg, &b)) != 0) {
djm@openbsd.org2a9c9f72019-09-03 08:34:19 +0000564 error("%s: hash_file failed: %s", __func__, ssh_err(r));
565 return r;
566 }
djm@openbsd.org2a9c9f72019-09-03 08:34:19 +0000567 if ((r = sshsig_wrap_sign(key, hashalg, b, sig_namespace, out,
568 signer, signer_ctx)) != 0)
569 goto out;
570 /* success */
571 r = 0;
572 out:
573 sshbuf_free(b);
djm@openbsd.org2a9c9f72019-09-03 08:34:19 +0000574 return r;
575}
576
577int
578sshsig_verify_fd(struct sshbuf *signature, int fd,
579 const char *expect_namespace, struct sshkey **sign_keyp)
580{
djm@openbsd.org2a9c9f72019-09-03 08:34:19 +0000581 struct sshbuf *b = NULL;
djm@openbsd.orgd637c4a2019-09-03 08:35:27 +0000582 int r = SSH_ERR_INTERNAL_ERROR;
djm@openbsd.org2a9c9f72019-09-03 08:34:19 +0000583 char *hashalg = NULL;
584
585 if (sign_keyp != NULL)
586 *sign_keyp = NULL;
587
588 if ((r = sshsig_peek_hashalg(signature, &hashalg)) != 0)
589 return r;
djm@openbsd.orgd637c4a2019-09-03 08:35:27 +0000590 debug("%s: signature made with hash \"%s\"", __func__, hashalg);
591 if ((r = hash_file(fd, hashalg, &b)) != 0) {
djm@openbsd.org2a9c9f72019-09-03 08:34:19 +0000592 error("%s: hash_file failed: %s", __func__, ssh_err(r));
djm@openbsd.org2a9c9f72019-09-03 08:34:19 +0000593 goto out;
594 }
595 if ((r = sshsig_wrap_verify(signature, hashalg, b, expect_namespace,
596 sign_keyp)) != 0)
597 goto out;
598 /* success */
599 r = 0;
600 out:
601 sshbuf_free(b);
602 free(hashalg);
djm@openbsd.org2a9c9f72019-09-03 08:34:19 +0000603 return r;
604}
605
606struct sigopts {
607 int ca;
608 char *namespaces;
609};
610
611static struct sigopts *
612sigopts_parse(const char *opts, const char *path, u_long linenum,
613 const char **errstrp)
614{
615 struct sigopts *ret;
616 int r;
617 const char *errstr = NULL;
618
619 if ((ret = calloc(1, sizeof(*ret))) == NULL)
620 return NULL;
621 if (opts == NULL || *opts == '\0')
622 return ret; /* Empty options yields empty options :) */
623
624 while (*opts && *opts != ' ' && *opts != '\t') {
625 /* flag options */
626 if ((r = opt_flag("cert-authority", 0, &opts)) != -1) {
627 ret->ca = 1;
628 } else if (opt_match(&opts, "namespaces")) {
629 if (ret->namespaces != NULL) {
630 errstr = "multiple \"namespaces\" clauses";
631 goto fail;
632 }
633 ret->namespaces = opt_dequote(&opts, &errstr);
634 if (ret->namespaces == NULL)
635 goto fail;
636 }
637 /*
638 * Skip the comma, and move to the next option
639 * (or break out if there are no more).
640 */
641 if (*opts == '\0' || *opts == ' ' || *opts == '\t')
642 break; /* End of options. */
643 /* Anything other than a comma is an unknown option */
644 if (*opts != ',') {
645 errstr = "unknown key option";
646 goto fail;
647 }
648 opts++;
649 if (*opts == '\0') {
650 errstr = "unexpected end-of-options";
651 goto fail;
652 }
653 }
654 /* success */
655 return ret;
656 fail:
657 if (errstrp != NULL)
658 *errstrp = errstr;
659 free(ret);
660 return NULL;
661}
662
663static void
664sigopts_free(struct sigopts *opts)
665{
666 if (opts == NULL)
667 return;
668 free(opts->namespaces);
669 free(opts);
670}
671
672static int
673check_allowed_keys_line(const char *path, u_long linenum, char *line,
674 const struct sshkey *sign_key, const char *principal,
675 const char *sig_namespace)
676{
677 struct sshkey *found_key = NULL;
678 char *cp, *opts = NULL, *identities = NULL;
679 int r, found = 0;
680 const char *reason = NULL;
681 struct sigopts *sigopts = NULL;
682
683 if ((found_key = sshkey_new(KEY_UNSPEC)) == NULL) {
684 error("%s: sshkey_new failed", __func__);
685 return SSH_ERR_ALLOC_FAIL;
686 }
687
688 /* format: identity[,identity...] [option[,option...]] key */
689 cp = line;
690 cp = cp + strspn(cp, " \t"); /* skip leading whitespace */
691 if (*cp == '#' || *cp == '\0')
692 goto done;
693 if ((identities = strdelimw(&cp)) == NULL) {
694 error("%s:%lu: invalid line", path, linenum);
695 goto done;
696 }
697 if (match_pattern_list(principal, identities, 0) != 1) {
698 /* principal didn't match */
699 goto done;
700 }
701 debug("%s: %s:%lu: matched principal \"%s\"",
702 __func__, path, linenum, principal);
703
704 if (sshkey_read(found_key, &cp) != 0) {
705 /* no key? Check for options */
706 opts = cp;
707 if (sshkey_advance_past_options(&cp) != 0) {
708 error("%s:%lu: invalid options",
709 path, linenum);
710 goto done;
711 }
712 *cp++ = '\0';
713 skip_space(&cp);
714 if (sshkey_read(found_key, &cp) != 0) {
715 error("%s:%lu: invalid key", path,
716 linenum);
717 goto done;
718 }
719 }
720 debug3("%s:%lu: options %s", path, linenum, opts == NULL ? "" : opts);
721 if ((sigopts = sigopts_parse(opts, path, linenum, &reason)) == NULL) {
722 error("%s:%lu: bad options: %s", path, linenum, reason);
723 goto done;
724 }
725
726 /* Check whether options preclude the use of this key */
727 if (sigopts->namespaces != NULL &&
728 match_pattern_list(sig_namespace, sigopts->namespaces, 0) != 1) {
729 error("%s:%lu: key is not permitted for use in signature "
730 "namespace \"%s\"", path, linenum, sig_namespace);
731 goto done;
732 }
733
734 if (!sigopts->ca && sshkey_equal(found_key, sign_key)) {
735 /* Exact match of key */
736 debug("%s:%lu: matched key and principal", path, linenum);
737 /* success */
738 found = 1;
739 } else if (sigopts->ca && sshkey_is_cert(sign_key) &&
740 sshkey_equal_public(sign_key->cert->signature_key, found_key)) {
741 /* Match of certificate's CA key */
742 if ((r = sshkey_cert_check_authority(sign_key, 0, 1,
743 principal, &reason)) != 0) {
744 error("%s:%lu: certificate not authorized: %s",
745 path, linenum, reason);
746 goto done;
747 }
748 debug("%s:%lu: matched certificate CA key", path, linenum);
749 /* success */
750 found = 1;
751 } else {
752 /* Principal matched but key didn't */
753 goto done;
754 }
755 done:
756 sshkey_free(found_key);
757 sigopts_free(sigopts);
758 return found ? 0 : SSH_ERR_KEY_NOT_FOUND;
759}
760
761int
762sshsig_check_allowed_keys(const char *path, const struct sshkey *sign_key,
763 const char *principal, const char *sig_namespace)
764{
765 FILE *f = NULL;
766 char *line = NULL;
767 size_t linesize = 0;
768 u_long linenum = 0;
769 int r, oerrno;
770
771 /* Check key and principal against file */
772 if ((f = fopen(path, "r")) == NULL) {
773 oerrno = errno;
774 error("Unable to open allowed keys file \"%s\": %s",
775 path, strerror(errno));
776 errno = oerrno;
777 return SSH_ERR_SYSTEM_ERROR;
778 }
779
780 while (getline(&line, &linesize, f) != -1) {
781 linenum++;
782 r = check_allowed_keys_line(path, linenum, line, sign_key,
783 principal, sig_namespace);
djm@openbsd.orgd637c4a2019-09-03 08:35:27 +0000784 free(line);
785 line = NULL;
djm@openbsd.org2a9c9f72019-09-03 08:34:19 +0000786 if (r == SSH_ERR_KEY_NOT_FOUND)
787 continue;
788 else if (r == 0) {
789 /* success */
790 fclose(f);
djm@openbsd.org2a9c9f72019-09-03 08:34:19 +0000791 return 0;
djm@openbsd.org2a9c9f72019-09-03 08:34:19 +0000792 } else
793 break;
794 }
795 /* Either we hit an error parsing or we simply didn't find the key */
796 fclose(f);
797 free(line);
798 return r == 0 ? SSH_ERR_KEY_NOT_FOUND : r;
799}