Annotation of src/usr.bin/ssh/sshsig.c, Revision 1.28
1.28 ! djm 1: /* $OpenBSD: sshsig.c,v 1.27 2022/01/05 04:02:42 djm Exp $ */
1.1 djm 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 <stdio.h>
19: #include <stdlib.h>
20: #include <stdarg.h>
21: #include <errno.h>
22: #include <string.h>
23: #include <unistd.h>
24:
25: #include "authfd.h"
26: #include "authfile.h"
27: #include "log.h"
28: #include "misc.h"
29: #include "sshbuf.h"
30: #include "sshsig.h"
31: #include "ssherr.h"
32: #include "sshkey.h"
33: #include "match.h"
34: #include "digest.h"
35:
36: #define SIG_VERSION 0x01
37: #define MAGIC_PREAMBLE "SSHSIG"
38: #define MAGIC_PREAMBLE_LEN (sizeof(MAGIC_PREAMBLE) - 1)
39: #define BEGIN_SIGNATURE "-----BEGIN SSH SIGNATURE-----\n"
40: #define END_SIGNATURE "-----END SSH SIGNATURE-----"
41: #define RSA_SIGN_ALG "rsa-sha2-512" /* XXX maybe make configurable */
42: #define RSA_SIGN_ALLOWED "rsa-sha2-512,rsa-sha2-256"
43: #define HASHALG_DEFAULT "sha512" /* XXX maybe make configurable */
44: #define HASHALG_ALLOWED "sha256,sha512"
45:
46: int
47: sshsig_armor(const struct sshbuf *blob, struct sshbuf **out)
48: {
49: struct sshbuf *buf = NULL;
50: int r = SSH_ERR_INTERNAL_ERROR;
51:
52: *out = NULL;
53:
54: if ((buf = sshbuf_new()) == NULL) {
1.18 djm 55: error_f("sshbuf_new failed");
1.1 djm 56: r = SSH_ERR_ALLOC_FAIL;
57: goto out;
58: }
59:
60: if ((r = sshbuf_put(buf, BEGIN_SIGNATURE,
61: sizeof(BEGIN_SIGNATURE)-1)) != 0) {
1.18 djm 62: error_fr(r, "sshbuf_putf");
1.1 djm 63: goto out;
64: }
65:
66: if ((r = sshbuf_dtob64(blob, buf, 1)) != 0) {
1.18 djm 67: error_fr(r, "base64 encode signature");
1.1 djm 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) {
1.18 djm 74: error_fr(r, "sshbuf_put");
1.1 djm 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:
86: int
87: sshsig_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) {
1.18 djm 96: error_f("sshbuf_fromb failed");
1.1 djm 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) {
1.18 djm 107: error_fr(r, "consume");
1.1 djm 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) {
1.18 djm 118: error_fr(r, "consume");
1.1 djm 119: goto done;
120: }
121:
122: if ((b64 = sshbuf_dup_string(sbuf)) == NULL) {
1.18 djm 123: error_f("sshbuf_dup_string failed");
1.1 djm 124: r = SSH_ERR_ALLOC_FAIL;
125: goto done;
126: }
127:
128: if ((buf = sshbuf_new()) == NULL) {
1.18 djm 129: error_f("sshbuf_new() failed");
1.1 djm 130: r = SSH_ERR_ALLOC_FAIL;
131: goto done;
132: }
133:
134: if ((r = sshbuf_b64tod(buf, b64)) != 0) {
1.18 djm 135: error_fr(r, "decode base64");
1.1 djm 136: goto done;
137: }
138:
139: /* success */
140: *out = buf;
141: r = 0;
142: buf = NULL; /* transferred */
143: done:
144: sshbuf_free(buf);
145: sshbuf_free(sbuf);
146: free(b64);
147: return r;
148: }
149:
150: static int
151: sshsig_wrap_sign(struct sshkey *key, const char *hashalg,
1.16 djm 152: const char *sk_provider, const char *sk_pin, const struct sshbuf *h_message,
1.7 djm 153: const char *sig_namespace, struct sshbuf **out,
154: sshsig_signer *signer, void *signer_ctx)
1.1 djm 155: {
156: int r;
157: size_t slen = 0;
158: u_char *sig = NULL;
159: struct sshbuf *blob = NULL;
160: struct sshbuf *tosign = NULL;
161: const char *sign_alg = NULL;
162:
163: if ((tosign = sshbuf_new()) == NULL ||
164: (blob = sshbuf_new()) == NULL) {
1.18 djm 165: error_f("sshbuf_new failed");
1.1 djm 166: r = SSH_ERR_ALLOC_FAIL;
167: goto done;
168: }
169:
170: if ((r = sshbuf_put(tosign, MAGIC_PREAMBLE, MAGIC_PREAMBLE_LEN)) != 0 ||
171: (r = sshbuf_put_cstring(tosign, sig_namespace)) != 0 ||
172: (r = sshbuf_put_string(tosign, NULL, 0)) != 0 || /* reserved */
173: (r = sshbuf_put_cstring(tosign, hashalg)) != 0 ||
1.6 djm 174: (r = sshbuf_put_stringb(tosign, h_message)) != 0) {
1.18 djm 175: error_fr(r, "assemble message to sign");
1.1 djm 176: goto done;
177: }
178:
179: /* If using RSA keys then default to a good signature algorithm */
180: if (sshkey_type_plain(key->type) == KEY_RSA)
181: sign_alg = RSA_SIGN_ALG;
182:
183: if (signer != NULL) {
184: if ((r = signer(key, &sig, &slen,
185: sshbuf_ptr(tosign), sshbuf_len(tosign),
1.16 djm 186: sign_alg, sk_provider, sk_pin, 0, signer_ctx)) != 0) {
1.18 djm 187: error_r(r, "Couldn't sign message (signer)");
1.1 djm 188: goto done;
189: }
190: } else {
191: if ((r = sshkey_sign(key, &sig, &slen,
192: sshbuf_ptr(tosign), sshbuf_len(tosign),
1.16 djm 193: sign_alg, sk_provider, sk_pin, 0)) != 0) {
1.18 djm 194: error_r(r, "Couldn't sign message");
1.1 djm 195: goto done;
196: }
197: }
198:
199: if ((r = sshbuf_put(blob, MAGIC_PREAMBLE, MAGIC_PREAMBLE_LEN)) != 0 ||
200: (r = sshbuf_put_u32(blob, SIG_VERSION)) != 0 ||
201: (r = sshkey_puts(key, blob)) != 0 ||
202: (r = sshbuf_put_cstring(blob, sig_namespace)) != 0 ||
203: (r = sshbuf_put_string(blob, NULL, 0)) != 0 || /* reserved */
204: (r = sshbuf_put_cstring(blob, hashalg)) != 0 ||
205: (r = sshbuf_put_string(blob, sig, slen)) != 0) {
1.18 djm 206: error_fr(r, "assemble signature object");
1.1 djm 207: goto done;
208: }
209:
1.12 markus 210: if (out != NULL) {
211: *out = blob;
212: blob = NULL;
213: }
1.1 djm 214: r = 0;
215: done:
216: free(sig);
217: sshbuf_free(blob);
218: sshbuf_free(tosign);
219: return r;
220: }
221:
222: /* Check preamble and version. */
223: static int
224: sshsig_parse_preamble(struct sshbuf *buf)
225: {
226: int r = SSH_ERR_INTERNAL_ERROR;
227: uint32_t sversion;
228:
229: if ((r = sshbuf_cmp(buf, 0, MAGIC_PREAMBLE, MAGIC_PREAMBLE_LEN)) != 0 ||
230: (r = sshbuf_consume(buf, (sizeof(MAGIC_PREAMBLE)-1))) != 0 ||
231: (r = sshbuf_get_u32(buf, &sversion)) != 0) {
232: error("Couldn't verify signature: invalid format");
233: return r;
234: }
235:
1.2 djm 236: if (sversion > SIG_VERSION) {
1.1 djm 237: error("Signature version %lu is larger than supported "
238: "version %u", (unsigned long)sversion, SIG_VERSION);
239: return SSH_ERR_INVALID_FORMAT;
240: }
241: return 0;
242: }
243:
244: static int
245: sshsig_check_hashalg(const char *hashalg)
246: {
1.2 djm 247: if (hashalg == NULL ||
248: match_pattern_list(hashalg, HASHALG_ALLOWED, 0) == 1)
1.1 djm 249: return 0;
1.18 djm 250: error_f("unsupported hash algorithm \"%.100s\"", hashalg);
1.1 djm 251: return SSH_ERR_SIGN_ALG_UNSUPPORTED;
252: }
253:
254: static int
255: sshsig_peek_hashalg(struct sshbuf *signature, char **hashalgp)
256: {
257: struct sshbuf *buf = NULL;
258: char *hashalg = NULL;
259: int r = SSH_ERR_INTERNAL_ERROR;
260:
261: if (hashalgp != NULL)
262: *hashalgp = NULL;
263: if ((buf = sshbuf_fromb(signature)) == NULL)
264: return SSH_ERR_ALLOC_FAIL;
265: if ((r = sshsig_parse_preamble(buf)) != 0)
266: goto done;
267: if ((r = sshbuf_get_string_direct(buf, NULL, NULL)) != 0 ||
268: (r = sshbuf_get_string_direct(buf, NULL, NULL)) != 0 ||
269: (r = sshbuf_get_string(buf, NULL, NULL)) != 0 ||
270: (r = sshbuf_get_cstring(buf, &hashalg, NULL)) != 0 ||
271: (r = sshbuf_get_string_direct(buf, NULL, NULL)) != 0) {
1.18 djm 272: error_fr(r, "parse signature object");
1.1 djm 273: goto done;
274: }
275:
276: /* success */
277: r = 0;
278: *hashalgp = hashalg;
279: hashalg = NULL;
280: done:
281: free(hashalg);
282: sshbuf_free(buf);
283: return r;
284: }
285:
286: static int
287: sshsig_wrap_verify(struct sshbuf *signature, const char *hashalg,
288: const struct sshbuf *h_message, const char *expect_namespace,
1.8 djm 289: struct sshkey **sign_keyp, struct sshkey_sig_details **sig_details)
1.1 djm 290: {
291: int r = SSH_ERR_INTERNAL_ERROR;
292: struct sshbuf *buf = NULL, *toverify = NULL;
293: struct sshkey *key = NULL;
294: const u_char *sig;
295: char *got_namespace = NULL, *sigtype = NULL, *sig_hashalg = NULL;
296: size_t siglen;
297:
1.18 djm 298: debug_f("verify message length %zu", sshbuf_len(h_message));
1.8 djm 299: if (sig_details != NULL)
300: *sig_details = NULL;
1.1 djm 301: if (sign_keyp != NULL)
302: *sign_keyp = NULL;
303:
304: if ((toverify = sshbuf_new()) == NULL) {
1.18 djm 305: error_f("sshbuf_new failed");
1.1 djm 306: r = SSH_ERR_ALLOC_FAIL;
307: goto done;
308: }
309: if ((r = sshbuf_put(toverify, MAGIC_PREAMBLE,
310: MAGIC_PREAMBLE_LEN)) != 0 ||
311: (r = sshbuf_put_cstring(toverify, expect_namespace)) != 0 ||
312: (r = sshbuf_put_string(toverify, NULL, 0)) != 0 || /* reserved */
313: (r = sshbuf_put_cstring(toverify, hashalg)) != 0 ||
1.6 djm 314: (r = sshbuf_put_stringb(toverify, h_message)) != 0) {
1.18 djm 315: error_fr(r, "assemble message to verify");
1.1 djm 316: goto done;
317: }
318:
319: if ((r = sshsig_parse_preamble(signature)) != 0)
320: goto done;
321:
322: if ((r = sshkey_froms(signature, &key)) != 0 ||
323: (r = sshbuf_get_cstring(signature, &got_namespace, NULL)) != 0 ||
324: (r = sshbuf_get_string(signature, NULL, NULL)) != 0 ||
325: (r = sshbuf_get_cstring(signature, &sig_hashalg, NULL)) != 0 ||
326: (r = sshbuf_get_string_direct(signature, &sig, &siglen)) != 0) {
1.18 djm 327: error_fr(r, "parse signature object");
1.1 djm 328: goto done;
329: }
330:
331: if (sshbuf_len(signature) != 0) {
332: error("Signature contains trailing data");
333: r = SSH_ERR_INVALID_FORMAT;
334: goto done;
335: }
336:
337: if (strcmp(expect_namespace, got_namespace) != 0) {
338: error("Couldn't verify signature: namespace does not match");
1.18 djm 339: debug_f("expected namespace \"%s\" received \"%s\"",
340: expect_namespace, got_namespace);
1.1 djm 341: r = SSH_ERR_SIGNATURE_INVALID;
342: goto done;
343: }
344: if (strcmp(hashalg, sig_hashalg) != 0) {
345: error("Couldn't verify signature: hash algorithm mismatch");
1.18 djm 346: debug_f("expected algorithm \"%s\" received \"%s\"",
347: hashalg, sig_hashalg);
1.1 djm 348: r = SSH_ERR_SIGNATURE_INVALID;
349: goto done;
350: }
351: /* Ensure that RSA keys use an acceptable signature algorithm */
352: if (sshkey_type_plain(key->type) == KEY_RSA) {
353: if ((r = sshkey_get_sigtype(sig, siglen, &sigtype)) != 0) {
1.18 djm 354: error_r(r, "Couldn't verify signature: unable to get "
355: "signature type");
1.1 djm 356: goto done;
357: }
358: if (match_pattern_list(sigtype, RSA_SIGN_ALLOWED, 0) != 1) {
359: error("Couldn't verify signature: unsupported RSA "
360: "signature algorithm %s", sigtype);
361: r = SSH_ERR_SIGN_ALG_UNSUPPORTED;
362: goto done;
363: }
364: }
365: if ((r = sshkey_verify(key, sig, siglen, sshbuf_ptr(toverify),
1.8 djm 366: sshbuf_len(toverify), NULL, 0, sig_details)) != 0) {
1.18 djm 367: error_r(r, "Signature verification failed");
1.1 djm 368: goto done;
369: }
370:
371: /* success */
372: r = 0;
373: if (sign_keyp != NULL) {
374: *sign_keyp = key;
375: key = NULL; /* transferred */
376: }
377: done:
378: free(got_namespace);
379: free(sigtype);
380: free(sig_hashalg);
381: sshbuf_free(buf);
382: sshbuf_free(toverify);
383: sshkey_free(key);
384: return r;
385: }
386:
1.2 djm 387: static int
388: hash_buffer(const struct sshbuf *m, const char *hashalg, struct sshbuf **bp)
1.1 djm 389: {
1.2 djm 390: char *hex, hash[SSH_DIGEST_MAX_LENGTH];
391: int alg, r = SSH_ERR_INTERNAL_ERROR;
1.1 djm 392: struct sshbuf *b = NULL;
393:
1.2 djm 394: *bp = NULL;
395: memset(hash, 0, sizeof(hash));
1.1 djm 396:
397: if ((r = sshsig_check_hashalg(hashalg)) != 0)
398: return r;
399: if ((alg = ssh_digest_alg_by_name(hashalg)) == -1) {
1.18 djm 400: error_f("can't look up hash algorithm %s", hashalg);
1.1 djm 401: return SSH_ERR_INTERNAL_ERROR;
402: }
1.2 djm 403: if ((r = ssh_digest_buffer(alg, m, hash, sizeof(hash))) != 0) {
1.18 djm 404: error_fr(r, "ssh_digest_buffer");
1.1 djm 405: return r;
406: }
1.2 djm 407: if ((hex = tohex(hash, ssh_digest_bytes(alg))) != NULL) {
1.18 djm 408: debug3_f("final hash: %s", hex);
1.2 djm 409: freezero(hex, strlen(hex));
410: }
411: if ((b = sshbuf_new()) == NULL) {
1.1 djm 412: r = SSH_ERR_ALLOC_FAIL;
413: goto out;
414: }
1.2 djm 415: if ((r = sshbuf_put(b, hash, ssh_digest_bytes(alg))) != 0) {
1.18 djm 416: error_fr(r, "sshbuf_put");
1.2 djm 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));
1.12 markus 426: return r;
1.2 djm 427: }
428:
429: int
1.16 djm 430: sshsig_signb(struct sshkey *key, const char *hashalg,
431: const char *sk_provider, const char *sk_pin,
1.2 djm 432: const struct sshbuf *message, const char *sig_namespace,
433: struct sshbuf **out, sshsig_signer *signer, void *signer_ctx)
434: {
435: struct sshbuf *b = NULL;
436: int r = SSH_ERR_INTERNAL_ERROR;
437:
438: if (hashalg == NULL)
439: hashalg = HASHALG_DEFAULT;
440: if (out != NULL)
441: *out = NULL;
442: if ((r = hash_buffer(message, hashalg, &b)) != 0) {
1.18 djm 443: error_fr(r, "hash buffer");
1.2 djm 444: goto out;
445: }
1.16 djm 446: if ((r = sshsig_wrap_sign(key, hashalg, sk_provider, sk_pin, b,
1.7 djm 447: sig_namespace, out, signer, signer_ctx)) != 0)
1.1 djm 448: goto out;
449: /* success */
450: r = 0;
451: out:
452: sshbuf_free(b);
453: return r;
454: }
455:
456: int
1.2 djm 457: sshsig_verifyb(struct sshbuf *signature, const struct sshbuf *message,
1.8 djm 458: const char *expect_namespace, struct sshkey **sign_keyp,
459: struct sshkey_sig_details **sig_details)
1.1 djm 460: {
461: struct sshbuf *b = NULL;
1.2 djm 462: int r = SSH_ERR_INTERNAL_ERROR;
1.1 djm 463: char *hashalg = NULL;
464:
1.8 djm 465: if (sig_details != NULL)
466: *sig_details = NULL;
1.1 djm 467: if (sign_keyp != NULL)
468: *sign_keyp = NULL;
469: if ((r = sshsig_peek_hashalg(signature, &hashalg)) != 0)
470: return r;
1.18 djm 471: debug_f("signature made with hash \"%s\"", hashalg);
1.2 djm 472: if ((r = hash_buffer(message, hashalg, &b)) != 0) {
1.18 djm 473: error_fr(r, "hash buffer");
1.1 djm 474: goto out;
475: }
476: if ((r = sshsig_wrap_verify(signature, hashalg, b, expect_namespace,
1.8 djm 477: sign_keyp, sig_details)) != 0)
1.1 djm 478: goto out;
479: /* success */
480: r = 0;
481: out:
482: sshbuf_free(b);
483: free(hashalg);
484: return r;
485: }
486:
487: static int
1.2 djm 488: hash_file(int fd, const char *hashalg, struct sshbuf **bp)
1.1 djm 489: {
1.2 djm 490: char *hex, rbuf[8192], hash[SSH_DIGEST_MAX_LENGTH];
1.1 djm 491: ssize_t n, total = 0;
492: struct ssh_digest_ctx *ctx;
1.2 djm 493: int alg, oerrno, r = SSH_ERR_INTERNAL_ERROR;
494: struct sshbuf *b = NULL;
495:
496: *bp = NULL;
497: memset(hash, 0, sizeof(hash));
1.1 djm 498:
1.2 djm 499: if ((r = sshsig_check_hashalg(hashalg)) != 0)
500: return r;
501: if ((alg = ssh_digest_alg_by_name(hashalg)) == -1) {
1.18 djm 502: error_f("can't look up hash algorithm %s", hashalg);
1.2 djm 503: return SSH_ERR_INTERNAL_ERROR;
504: }
505: if ((ctx = ssh_digest_start(alg)) == NULL) {
1.18 djm 506: error_f("ssh_digest_start failed");
1.1 djm 507: return SSH_ERR_INTERNAL_ERROR;
508: }
509: for (;;) {
510: if ((n = read(fd, rbuf, sizeof(rbuf))) == -1) {
511: if (errno == EINTR || errno == EAGAIN)
512: continue;
513: oerrno = errno;
1.18 djm 514: error_f("read: %s", strerror(errno));
1.1 djm 515: ssh_digest_free(ctx);
516: errno = oerrno;
1.2 djm 517: r = SSH_ERR_SYSTEM_ERROR;
518: goto out;
1.1 djm 519: } else if (n == 0) {
1.18 djm 520: debug2_f("hashed %zu bytes", total);
1.1 djm 521: break; /* EOF */
522: }
523: total += (size_t)n;
524: if ((r = ssh_digest_update(ctx, rbuf, (size_t)n)) != 0) {
1.18 djm 525: error_fr(r, "ssh_digest_update");
1.2 djm 526: goto out;
1.1 djm 527: }
528: }
1.2 djm 529: if ((r = ssh_digest_final(ctx, hash, sizeof(hash))) != 0) {
1.18 djm 530: error_fr(r, "ssh_digest_final");
1.2 djm 531: goto out;
1.1 djm 532: }
1.2 djm 533: if ((hex = tohex(hash, ssh_digest_bytes(alg))) != NULL) {
1.18 djm 534: debug3_f("final hash: %s", hex);
1.1 djm 535: freezero(hex, strlen(hex));
536: }
1.2 djm 537: if ((b = sshbuf_new()) == NULL) {
538: r = SSH_ERR_ALLOC_FAIL;
539: goto out;
540: }
541: if ((r = sshbuf_put(b, hash, ssh_digest_bytes(alg))) != 0) {
1.18 djm 542: error_fr(r, "sshbuf_put");
1.2 djm 543: goto out;
544: }
545: *bp = b;
546: b = NULL; /* transferred */
1.1 djm 547: /* success */
1.2 djm 548: r = 0;
549: out:
550: sshbuf_free(b);
1.1 djm 551: ssh_digest_free(ctx);
1.2 djm 552: explicit_bzero(hash, sizeof(hash));
1.12 markus 553: return r;
1.1 djm 554: }
555:
556: int
1.16 djm 557: sshsig_sign_fd(struct sshkey *key, const char *hashalg,
558: const char *sk_provider, const char *sk_pin,
1.1 djm 559: int fd, const char *sig_namespace, struct sshbuf **out,
560: sshsig_signer *signer, void *signer_ctx)
561: {
562: struct sshbuf *b = NULL;
1.2 djm 563: int r = SSH_ERR_INTERNAL_ERROR;
1.1 djm 564:
1.2 djm 565: if (hashalg == NULL)
566: hashalg = HASHALG_DEFAULT;
1.1 djm 567: if (out != NULL)
568: *out = NULL;
1.2 djm 569: if ((r = hash_file(fd, hashalg, &b)) != 0) {
1.18 djm 570: error_fr(r, "hash_file");
1.1 djm 571: return r;
572: }
1.16 djm 573: if ((r = sshsig_wrap_sign(key, hashalg, sk_provider, sk_pin, b,
1.7 djm 574: sig_namespace, out, signer, signer_ctx)) != 0)
1.1 djm 575: goto out;
576: /* success */
577: r = 0;
578: out:
579: sshbuf_free(b);
580: return r;
581: }
582:
583: int
584: sshsig_verify_fd(struct sshbuf *signature, int fd,
1.8 djm 585: const char *expect_namespace, struct sshkey **sign_keyp,
586: struct sshkey_sig_details **sig_details)
1.1 djm 587: {
588: struct sshbuf *b = NULL;
1.2 djm 589: int r = SSH_ERR_INTERNAL_ERROR;
1.1 djm 590: char *hashalg = NULL;
591:
1.8 djm 592: if (sig_details != NULL)
593: *sig_details = NULL;
1.1 djm 594: if (sign_keyp != NULL)
595: *sign_keyp = NULL;
596: if ((r = sshsig_peek_hashalg(signature, &hashalg)) != 0)
597: return r;
1.18 djm 598: debug_f("signature made with hash \"%s\"", hashalg);
1.2 djm 599: if ((r = hash_file(fd, hashalg, &b)) != 0) {
1.18 djm 600: error_fr(r, "hash_file");
1.1 djm 601: goto out;
602: }
603: if ((r = sshsig_wrap_verify(signature, hashalg, b, expect_namespace,
1.8 djm 604: sign_keyp, sig_details)) != 0)
1.1 djm 605: goto out;
606: /* success */
607: r = 0;
608: out:
609: sshbuf_free(b);
610: free(hashalg);
611: return r;
612: }
613:
1.4 djm 614: struct sshsigopt {
1.1 djm 615: int ca;
616: char *namespaces;
1.21 djm 617: uint64_t valid_after, valid_before;
1.1 djm 618: };
619:
1.4 djm 620: struct sshsigopt *
621: sshsigopt_parse(const char *opts, const char *path, u_long linenum,
1.1 djm 622: const char **errstrp)
623: {
1.4 djm 624: struct sshsigopt *ret;
1.1 djm 625: int r;
1.21 djm 626: char *opt;
1.1 djm 627: const char *errstr = NULL;
628:
629: if ((ret = calloc(1, sizeof(*ret))) == NULL)
630: return NULL;
631: if (opts == NULL || *opts == '\0')
632: return ret; /* Empty options yields empty options :) */
633:
634: while (*opts && *opts != ' ' && *opts != '\t') {
635: /* flag options */
636: if ((r = opt_flag("cert-authority", 0, &opts)) != -1) {
637: ret->ca = 1;
638: } else if (opt_match(&opts, "namespaces")) {
639: if (ret->namespaces != NULL) {
640: errstr = "multiple \"namespaces\" clauses";
641: goto fail;
642: }
643: ret->namespaces = opt_dequote(&opts, &errstr);
644: if (ret->namespaces == NULL)
645: goto fail;
1.21 djm 646: } else if (opt_match(&opts, "valid-after")) {
647: if (ret->valid_after != 0) {
648: errstr = "multiple \"valid-after\" clauses";
649: goto fail;
650: }
651: if ((opt = opt_dequote(&opts, &errstr)) == NULL)
652: goto fail;
653: if (parse_absolute_time(opt, &ret->valid_after) != 0 ||
654: ret->valid_after == 0) {
655: free(opt);
656: errstr = "invalid \"valid-after\" time";
657: goto fail;
658: }
659: free(opt);
660: } else if (opt_match(&opts, "valid-before")) {
661: if (ret->valid_before != 0) {
662: errstr = "multiple \"valid-before\" clauses";
663: goto fail;
664: }
665: if ((opt = opt_dequote(&opts, &errstr)) == NULL)
666: goto fail;
667: if (parse_absolute_time(opt, &ret->valid_before) != 0 ||
668: ret->valid_before == 0) {
669: free(opt);
670: errstr = "invalid \"valid-before\" time";
671: goto fail;
672: }
673: free(opt);
1.1 djm 674: }
675: /*
676: * Skip the comma, and move to the next option
677: * (or break out if there are no more).
678: */
679: if (*opts == '\0' || *opts == ' ' || *opts == '\t')
680: break; /* End of options. */
681: /* Anything other than a comma is an unknown option */
682: if (*opts != ',') {
683: errstr = "unknown key option";
684: goto fail;
685: }
686: opts++;
687: if (*opts == '\0') {
688: errstr = "unexpected end-of-options";
689: goto fail;
690: }
691: }
1.21 djm 692: /* final consistency check */
693: if (ret->valid_after != 0 && ret->valid_before != 0 &&
694: ret->valid_before <= ret->valid_after) {
695: errstr = "\"valid-before\" time is before \"valid-after\"";
696: goto fail;
697: }
1.1 djm 698: /* success */
699: return ret;
700: fail:
701: if (errstrp != NULL)
702: *errstrp = errstr;
1.5 djm 703: sshsigopt_free(ret);
1.1 djm 704: return NULL;
705: }
706:
1.4 djm 707: void
708: sshsigopt_free(struct sshsigopt *opts)
1.1 djm 709: {
710: if (opts == NULL)
711: return;
712: free(opts->namespaces);
713: free(opts);
714: }
715:
716: static int
1.9 djm 717: parse_principals_key_and_options(const char *path, u_long linenum, char *line,
718: const char *required_principal, char **principalsp, struct sshkey **keyp,
719: struct sshsigopt **sigoptsp)
1.1 djm 720: {
1.9 djm 721: char *opts = NULL, *tmp, *cp, *principals = NULL;
1.1 djm 722: const char *reason = NULL;
1.4 djm 723: struct sshsigopt *sigopts = NULL;
1.9 djm 724: struct sshkey *key = NULL;
725: int r = SSH_ERR_INTERNAL_ERROR;
1.1 djm 726:
1.9 djm 727: if (principalsp != NULL)
728: *principalsp = NULL;
729: if (sigoptsp != NULL)
730: *sigoptsp = NULL;
731: if (keyp != NULL)
732: *keyp = NULL;
1.1 djm 733:
734: cp = line;
735: cp = cp + strspn(cp, " \t"); /* skip leading whitespace */
736: if (*cp == '#' || *cp == '\0')
1.9 djm 737: return SSH_ERR_KEY_NOT_FOUND; /* blank or all-comment line */
738:
739: /* format: identity[,identity...] [option[,option...]] key */
740: if ((tmp = strdelimw(&cp)) == NULL) {
1.1 djm 741: error("%s:%lu: invalid line", path, linenum);
1.9 djm 742: r = SSH_ERR_INVALID_FORMAT;
743: goto out;
1.1 djm 744: }
1.9 djm 745: if ((principals = strdup(tmp)) == NULL) {
1.18 djm 746: error_f("strdup failed");
1.9 djm 747: r = SSH_ERR_ALLOC_FAIL;
748: goto out;
749: }
750: /*
751: * Bail out early if we're looking for a particular principal and this
752: * line does not list it.
753: */
754: if (required_principal != NULL) {
755: if (match_pattern_list(required_principal,
756: principals, 0) != 1) {
757: /* principal didn't match */
758: r = SSH_ERR_KEY_NOT_FOUND;
759: goto out;
760: }
1.18 djm 761: debug_f("%s:%lu: matched principal \"%s\"",
762: path, linenum, required_principal);
1.1 djm 763: }
764:
1.9 djm 765: if ((key = sshkey_new(KEY_UNSPEC)) == NULL) {
1.18 djm 766: error_f("sshkey_new failed");
1.9 djm 767: r = SSH_ERR_ALLOC_FAIL;
768: goto out;
769: }
770: if (sshkey_read(key, &cp) != 0) {
1.1 djm 771: /* no key? Check for options */
772: opts = cp;
773: if (sshkey_advance_past_options(&cp) != 0) {
1.9 djm 774: error("%s:%lu: invalid options", path, linenum);
775: r = SSH_ERR_INVALID_FORMAT;
776: goto out;
1.1 djm 777: }
778: *cp++ = '\0';
779: skip_space(&cp);
1.9 djm 780: if (sshkey_read(key, &cp) != 0) {
781: error("%s:%lu: invalid key", path, linenum);
782: r = SSH_ERR_INVALID_FORMAT;
783: goto out;
1.1 djm 784: }
785: }
786: debug3("%s:%lu: options %s", path, linenum, opts == NULL ? "" : opts);
1.4 djm 787: if ((sigopts = sshsigopt_parse(opts, path, linenum, &reason)) == NULL) {
1.1 djm 788: error("%s:%lu: bad options: %s", path, linenum, reason);
1.9 djm 789: r = SSH_ERR_INVALID_FORMAT;
790: goto out;
791: }
792: /* success */
793: if (principalsp != NULL) {
794: *principalsp = principals;
795: principals = NULL; /* transferred */
796: }
797: if (sigoptsp != NULL) {
798: *sigoptsp = sigopts;
799: sigopts = NULL; /* transferred */
800: }
801: if (keyp != NULL) {
802: *keyp = key;
803: key = NULL; /* transferred */
804: }
805: r = 0;
806: out:
807: free(principals);
808: sshsigopt_free(sigopts);
809: sshkey_free(key);
810: return r;
811: }
812:
813: static int
1.22 djm 814: cert_filter_principals(const char *path, u_long linenum,
815: char **principalsp, const struct sshkey *cert, uint64_t verify_time)
816: {
817: char *cp, *oprincipals, *principals;
818: const char *reason;
819: struct sshbuf *nprincipals;
820: int r = SSH_ERR_INTERNAL_ERROR, success = 0;
1.28 ! djm 821: u_int i;
1.22 djm 822:
823: oprincipals = principals = *principalsp;
824: *principalsp = NULL;
825:
826: if ((nprincipals = sshbuf_new()) == NULL) {
827: r = SSH_ERR_ALLOC_FAIL;
828: goto out;
829: }
830:
831: while ((cp = strsep(&principals, ",")) != NULL && *cp != '\0') {
1.28 ! djm 832: /* Check certificate validity */
1.22 djm 833: if ((r = sshkey_cert_check_authority(cert, 0, 1, 0,
1.28 ! djm 834: verify_time, NULL, &reason)) != 0) {
1.22 djm 835: debug("%s:%lu: principal \"%s\" not authorized: %s",
836: path, linenum, cp, reason);
837: continue;
838: }
1.28 ! djm 839: /* Return all matching principal names from the cert */
! 840: for (i = 0; i < cert->cert->nprincipals; i++) {
! 841: if (match_pattern(cert->cert->principals[i], cp)) {
! 842: if ((r = sshbuf_putf(nprincipals, "%s%s",
! 843: sshbuf_len(nprincipals) != 0 ? "," : "",
! 844: cert->cert->principals[i])) != 0) {
! 845: error_f("buffer error");
! 846: goto out;
! 847: }
! 848: }
1.22 djm 849: }
850: }
851: if (sshbuf_len(nprincipals) == 0) {
852: error("%s:%lu: no valid principals found", path, linenum);
853: r = SSH_ERR_KEY_CERT_INVALID;
854: goto out;
855: }
856: if ((principals = sshbuf_dup_string(nprincipals)) == NULL) {
857: error_f("buffer error");
858: goto out;
859: }
860: /* success */
861: success = 1;
862: *principalsp = principals;
863: out:
864: sshbuf_free(nprincipals);
865: free(oprincipals);
866: return success ? 0 : r;
867: }
868:
869: static int
1.9 djm 870: check_allowed_keys_line(const char *path, u_long linenum, char *line,
871: const struct sshkey *sign_key, const char *principal,
1.23 djm 872: const char *sig_namespace, uint64_t verify_time, char **principalsp)
1.9 djm 873: {
874: struct sshkey *found_key = NULL;
1.23 djm 875: char *principals = NULL;
1.21 djm 876: int r, success = 0;
1.9 djm 877: const char *reason = NULL;
878: struct sshsigopt *sigopts = NULL;
1.21 djm 879: char tvalid[64], tverify[64];
1.9 djm 880:
1.23 djm 881: if (principalsp != NULL)
882: *principalsp = NULL;
883:
1.9 djm 884: /* Parse the line */
885: if ((r = parse_principals_key_and_options(path, linenum, line,
1.23 djm 886: principal, &principals, &found_key, &sigopts)) != 0) {
1.9 djm 887: /* error already logged */
1.1 djm 888: goto done;
889: }
890:
891: if (!sigopts->ca && sshkey_equal(found_key, sign_key)) {
892: /* Exact match of key */
1.21 djm 893: debug("%s:%lu: matched key", path, linenum);
1.1 djm 894: } else if (sigopts->ca && sshkey_is_cert(sign_key) &&
895: sshkey_equal_public(sign_key->cert->signature_key, found_key)) {
1.23 djm 896: if (principal) {
897: /* Match certificate CA key with specified principal */
898: if ((r = sshkey_cert_check_authority(sign_key, 0, 1, 0,
899: verify_time, principal, &reason)) != 0) {
900: error("%s:%lu: certificate not authorized: %s",
901: path, linenum, reason);
902: goto done;
903: }
904: debug("%s:%lu: matched certificate CA key",
905: path, linenum);
906: } else {
907: /* No principal specified - find all matching ones */
908: if ((r = cert_filter_principals(path, linenum,
909: &principals, sign_key, verify_time)) != 0) {
910: /* error already displayed */
911: debug_r(r, "%s:%lu: cert_filter_principals",
912: path, linenum);
913: goto done;
914: }
915: debug("%s:%lu: matched certificate CA key",
916: path, linenum);
1.1 djm 917: }
918: } else {
1.21 djm 919: /* Didn't match key */
1.1 djm 920: goto done;
921: }
1.21 djm 922:
923: /* Check whether options preclude the use of this key */
1.27 djm 924: if (sigopts->namespaces != NULL && sig_namespace != NULL &&
1.21 djm 925: match_pattern_list(sig_namespace, sigopts->namespaces, 0) != 1) {
926: error("%s:%lu: key is not permitted for use in signature "
927: "namespace \"%s\"", path, linenum, sig_namespace);
928: goto done;
929: }
930:
931: /* check key time validity */
932: format_absolute_time((uint64_t)verify_time, tverify, sizeof(tverify));
933: if (sigopts->valid_after != 0 &&
934: (uint64_t)verify_time < sigopts->valid_after) {
935: format_absolute_time(sigopts->valid_after,
936: tvalid, sizeof(tvalid));
937: error("%s:%lu: key is not yet valid: "
938: "verify time %s < valid-after %s", path, linenum,
939: tverify, tvalid);
940: goto done;
941: }
942: if (sigopts->valid_before != 0 &&
943: (uint64_t)verify_time > sigopts->valid_before) {
944: format_absolute_time(sigopts->valid_before,
945: tvalid, sizeof(tvalid));
946: error("%s:%lu: key has expired: "
947: "verify time %s > valid-before %s", path, linenum,
948: tverify, tvalid);
949: goto done;
950: }
951: success = 1;
952:
1.1 djm 953: done:
1.23 djm 954: if (success && principalsp != NULL) {
955: *principalsp = principals;
956: principals = NULL; /* transferred */
957: }
958: free(principals);
1.1 djm 959: sshkey_free(found_key);
1.4 djm 960: sshsigopt_free(sigopts);
1.21 djm 961: return success ? 0 : SSH_ERR_KEY_NOT_FOUND;
1.1 djm 962: }
963:
964: int
965: sshsig_check_allowed_keys(const char *path, const struct sshkey *sign_key,
1.21 djm 966: const char *principal, const char *sig_namespace, uint64_t verify_time)
1.1 djm 967: {
968: FILE *f = NULL;
969: char *line = NULL;
970: size_t linesize = 0;
971: u_long linenum = 0;
1.12 markus 972: int r = SSH_ERR_INTERNAL_ERROR, oerrno;
1.1 djm 973:
974: /* Check key and principal against file */
975: if ((f = fopen(path, "r")) == NULL) {
976: oerrno = errno;
977: error("Unable to open allowed keys file \"%s\": %s",
978: path, strerror(errno));
979: errno = oerrno;
980: return SSH_ERR_SYSTEM_ERROR;
981: }
982:
983: while (getline(&line, &linesize, f) != -1) {
984: linenum++;
985: r = check_allowed_keys_line(path, linenum, line, sign_key,
1.23 djm 986: principal, sig_namespace, verify_time, NULL);
1.2 djm 987: free(line);
988: line = NULL;
1.20 dtucker 989: linesize = 0;
1.1 djm 990: if (r == SSH_ERR_KEY_NOT_FOUND)
991: continue;
992: else if (r == 0) {
993: /* success */
994: fclose(f);
995: return 0;
996: } else
997: break;
998: }
999: /* Either we hit an error parsing or we simply didn't find the key */
1000: fclose(f);
1001: free(line);
1002: return r == 0 ? SSH_ERR_KEY_NOT_FOUND : r;
1.11 djm 1003: }
1004:
1.10 djm 1005: int
1.11 djm 1006: sshsig_find_principals(const char *path, const struct sshkey *sign_key,
1.21 djm 1007: uint64_t verify_time, char **principals)
1.10 djm 1008: {
1009: FILE *f = NULL;
1010: char *line = NULL;
1011: size_t linesize = 0;
1012: u_long linenum = 0;
1.14 markus 1013: int r = SSH_ERR_INTERNAL_ERROR, oerrno;
1.10 djm 1014:
1015: if ((f = fopen(path, "r")) == NULL) {
1016: oerrno = errno;
1017: error("Unable to open allowed keys file \"%s\": %s",
1018: path, strerror(errno));
1019: errno = oerrno;
1020: return SSH_ERR_SYSTEM_ERROR;
1021: }
1022:
1.26 djm 1023: r = SSH_ERR_KEY_NOT_FOUND;
1.10 djm 1024: while (getline(&line, &linesize, f) != -1) {
1025: linenum++;
1.23 djm 1026: r = check_allowed_keys_line(path, linenum, line,
1027: sign_key, NULL, NULL, verify_time, principals);
1.10 djm 1028: free(line);
1029: line = NULL;
1.20 dtucker 1030: linesize = 0;
1.10 djm 1031: if (r == SSH_ERR_KEY_NOT_FOUND)
1032: continue;
1033: else if (r == 0) {
1034: /* success */
1035: fclose(f);
1036: return 0;
1037: } else
1038: break;
1039: }
1040: free(line);
1041: /* Either we hit an error parsing or we simply didn't find the key */
1042: if (ferror(f) != 0) {
1043: oerrno = errno;
1044: fclose(f);
1045: error("Unable to read allowed keys file \"%s\": %s",
1046: path, strerror(errno));
1047: errno = oerrno;
1048: return SSH_ERR_SYSTEM_ERROR;
1049: }
1050: fclose(f);
1051: return r == 0 ? SSH_ERR_KEY_NOT_FOUND : r;
1.24 djm 1052: }
1053:
1054: int
1055: sshsig_match_principals(const char *path, const char *principal,
1056: char ***principalsp, size_t *nprincipalsp)
1057: {
1058: FILE *f = NULL;
1059: char *found, *line = NULL, **principals = NULL, **tmp;
1060: size_t i, nprincipals = 0, linesize = 0;
1061: u_long linenum = 0;
1.25 djm 1062: int oerrno = 0, r, ret = 0;
1.24 djm 1063:
1064: if (principalsp != NULL)
1065: *principalsp = NULL;
1066: if (nprincipalsp != NULL)
1067: *nprincipalsp = 0;
1068:
1069: /* Check key and principal against file */
1070: if ((f = fopen(path, "r")) == NULL) {
1071: oerrno = errno;
1072: error("Unable to open allowed keys file \"%s\": %s",
1073: path, strerror(errno));
1074: errno = oerrno;
1075: return SSH_ERR_SYSTEM_ERROR;
1076: }
1077:
1078: while (getline(&line, &linesize, f) != -1) {
1079: linenum++;
1080: /* Parse the line */
1081: if ((r = parse_principals_key_and_options(path, linenum, line,
1082: principal, &found, NULL, NULL)) != 0) {
1083: if (r == SSH_ERR_KEY_NOT_FOUND)
1084: continue;
1085: ret = r;
1086: oerrno = errno;
1087: break; /* unexpected error */
1088: }
1089: if ((tmp = recallocarray(principals, nprincipals,
1090: nprincipals + 1, sizeof(*principals))) == NULL) {
1091: ret = SSH_ERR_ALLOC_FAIL;
1092: free(found);
1093: break;
1094: }
1095: principals = tmp;
1096: principals[nprincipals++] = found; /* transferred */
1097: free(line);
1098: line = NULL;
1099: linesize = 0;
1100: }
1101: fclose(f);
1102:
1103: if (ret == 0) {
1104: if (nprincipals == 0)
1105: ret = SSH_ERR_KEY_NOT_FOUND;
1106: if (principalsp != NULL) {
1107: *principalsp = principals;
1108: principals = NULL; /* transferred */
1109: }
1110: if (nprincipalsp != 0) {
1111: *nprincipalsp = nprincipals;
1112: nprincipals = 0;
1113: }
1114: }
1115:
1116: for (i = 0; i < nprincipals; i++)
1117: free(principals[i]);
1118: free(principals);
1119:
1120: errno = oerrno;
1121: return ret;
1.10 djm 1122: }
1123:
1124: int
1125: sshsig_get_pubkey(struct sshbuf *signature, struct sshkey **pubkey)
1126: {
1127: struct sshkey *pk = NULL;
1128: int r = SSH_ERR_SIGNATURE_INVALID;
1129:
1.13 markus 1130: if (pubkey == NULL)
1131: return SSH_ERR_INTERNAL_ERROR;
1.10 djm 1132: if ((r = sshsig_parse_preamble(signature)) != 0)
1133: return r;
1134: if ((r = sshkey_froms(signature, &pk)) != 0)
1135: return r;
1136:
1137: *pubkey = pk;
1138: pk = NULL;
1139: return 0;
1140: }