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