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