Annotation of src/usr.bin/ssh/sshsig.c, Revision 1.16
1.1 djm 1: /*
2: * Copyright (c) 2019 Google LLC
3: *
4: * Permission to use, copy, modify, and distribute this software for any
5: * purpose with or without fee is hereby granted, provided that the above
6: * copyright notice and this permission notice appear in all copies.
7: *
8: * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9: * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10: * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11: * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12: * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13: * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14: * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15: */
16:
17: #include <stdio.h>
18: #include <stdlib.h>
19: #include <stdarg.h>
20: #include <errno.h>
21: #include <string.h>
22: #include <unistd.h>
23:
24: #include "authfd.h"
25: #include "authfile.h"
26: #include "log.h"
27: #include "misc.h"
28: #include "sshbuf.h"
29: #include "sshsig.h"
30: #include "ssherr.h"
31: #include "sshkey.h"
32: #include "match.h"
33: #include "digest.h"
34:
35: #define SIG_VERSION 0x01
36: #define MAGIC_PREAMBLE "SSHSIG"
37: #define MAGIC_PREAMBLE_LEN (sizeof(MAGIC_PREAMBLE) - 1)
38: #define BEGIN_SIGNATURE "-----BEGIN SSH SIGNATURE-----\n"
39: #define END_SIGNATURE "-----END SSH SIGNATURE-----"
40: #define RSA_SIGN_ALG "rsa-sha2-512" /* XXX maybe make configurable */
41: #define RSA_SIGN_ALLOWED "rsa-sha2-512,rsa-sha2-256"
42: #define HASHALG_DEFAULT "sha512" /* XXX maybe make configurable */
43: #define HASHALG_ALLOWED "sha256,sha512"
44:
45: int
46: sshsig_armor(const struct sshbuf *blob, struct sshbuf **out)
47: {
48: struct sshbuf *buf = NULL;
49: int r = SSH_ERR_INTERNAL_ERROR;
50:
51: *out = NULL;
52:
53: if ((buf = sshbuf_new()) == NULL) {
54: error("%s: sshbuf_new failed", __func__);
55: r = SSH_ERR_ALLOC_FAIL;
56: goto out;
57: }
58:
59: if ((r = sshbuf_put(buf, BEGIN_SIGNATURE,
60: sizeof(BEGIN_SIGNATURE)-1)) != 0) {
61: error("%s: sshbuf_putf failed: %s", __func__, ssh_err(r));
62: goto out;
63: }
64:
65: if ((r = sshbuf_dtob64(blob, buf, 1)) != 0) {
66: error("%s: Couldn't base64 encode signature blob: %s",
67: __func__, ssh_err(r));
68: goto out;
69: }
70:
71: if ((r = sshbuf_put(buf, END_SIGNATURE,
72: sizeof(END_SIGNATURE)-1)) != 0 ||
73: (r = sshbuf_put_u8(buf, '\n')) != 0) {
74: error("%s: sshbuf_put failed: %s", __func__, ssh_err(r));
75: goto out;
76: }
77: /* success */
78: *out = buf;
79: buf = NULL; /* transferred */
80: r = 0;
81: out:
82: sshbuf_free(buf);
83: return r;
84: }
85:
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) {
96: error("%s: sshbuf_fromb failed", __func__);
97: return SSH_ERR_ALLOC_FAIL;
98: }
99:
100: if ((r = sshbuf_cmp(sbuf, 0,
101: BEGIN_SIGNATURE, sizeof(BEGIN_SIGNATURE)-1)) != 0) {
102: error("Couldn't parse signature: missing header");
103: goto done;
104: }
105:
106: if ((r = sshbuf_consume(sbuf, sizeof(BEGIN_SIGNATURE)-1)) != 0) {
107: error("%s: sshbuf_consume failed: %s", __func__, ssh_err(r));
108: goto done;
109: }
110:
111: if ((r = sshbuf_find(sbuf, 0, "\n" END_SIGNATURE,
112: sizeof("\n" END_SIGNATURE)-1, &eoffset)) != 0) {
113: error("Couldn't parse signature: missing footer");
114: goto done;
115: }
116:
117: if ((r = sshbuf_consume_end(sbuf, sshbuf_len(sbuf)-eoffset)) != 0) {
118: error("%s: sshbuf_consume failed: %s", __func__, ssh_err(r));
119: goto done;
120: }
121:
122: if ((b64 = sshbuf_dup_string(sbuf)) == NULL) {
123: error("%s: sshbuf_dup_string failed", __func__);
124: r = SSH_ERR_ALLOC_FAIL;
125: goto done;
126: }
127:
128: if ((buf = sshbuf_new()) == NULL) {
129: error("%s: sshbuf_new() failed", __func__);
130: r = SSH_ERR_ALLOC_FAIL;
131: goto done;
132: }
133:
134: if ((r = sshbuf_b64tod(buf, b64)) != 0) {
1.3 naddy 135: error("Couldn't decode signature: %s", ssh_err(r));
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) {
165: error("%s: sshbuf_new failed", __func__);
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.1 djm 175: error("Couldn't construct message to sign: %s", ssh_err(r));
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.1 djm 187: error("Couldn't sign message: %s", ssh_err(r));
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.1 djm 194: error("Couldn't sign message: %s", ssh_err(r));
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) {
206: error("Couldn't populate blob: %s", ssh_err(r));
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;
250: error("%s: unsupported hash algorithm \"%.100s\"", __func__, hashalg);
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) {
272: error("Couldn't parse signature blob: %s", ssh_err(r));
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.2 djm 298: debug("%s: verify message length %zu", __func__, 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) {
305: error("%s: sshbuf_new failed", __func__);
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.1 djm 315: error("Couldn't construct message to verify: %s", ssh_err(r));
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) {
327: error("Couldn't parse signature blob: %s", ssh_err(r));
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");
339: debug("%s: expected namespace \"%s\" received \"%s\"",
340: __func__, expect_namespace, got_namespace);
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");
346: debug("%s: expected algorithm \"%s\" received \"%s\"",
347: __func__, hashalg, sig_hashalg);
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) {
354: error("Couldn't verify signature: unable to get "
355: "signature type: %s", ssh_err(r));
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.1 djm 367: error("Signature verification failed: %s", ssh_err(r));
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) {
400: error("%s: can't look up hash algorithm %s",
1.2 djm 401: __func__, hashalg);
1.1 djm 402: return SSH_ERR_INTERNAL_ERROR;
403: }
1.2 djm 404: if ((r = ssh_digest_buffer(alg, m, hash, sizeof(hash))) != 0) {
1.1 djm 405: error("%s: ssh_digest_buffer failed: %s", __func__, ssh_err(r));
406: return r;
407: }
1.2 djm 408: if ((hex = tohex(hash, ssh_digest_bytes(alg))) != NULL) {
409: debug3("%s: final hash: %s", __func__, hex);
410: freezero(hex, strlen(hex));
411: }
412: if ((b = sshbuf_new()) == NULL) {
1.1 djm 413: r = SSH_ERR_ALLOC_FAIL;
414: goto out;
415: }
1.2 djm 416: if ((r = sshbuf_put(b, hash, ssh_digest_bytes(alg))) != 0) {
417: error("%s: sshbuf_put: %s", __func__, ssh_err(r));
418: goto out;
419: }
420: *bp = b;
421: b = NULL; /* transferred */
422: /* success */
423: r = 0;
424: out:
425: sshbuf_free(b);
426: explicit_bzero(hash, sizeof(hash));
1.12 markus 427: return r;
1.2 djm 428: }
429:
430: int
1.16 ! djm 431: sshsig_signb(struct sshkey *key, const char *hashalg,
! 432: const char *sk_provider, const char *sk_pin,
1.2 djm 433: const struct sshbuf *message, const char *sig_namespace,
434: struct sshbuf **out, sshsig_signer *signer, void *signer_ctx)
435: {
436: struct sshbuf *b = NULL;
437: int r = SSH_ERR_INTERNAL_ERROR;
438:
439: if (hashalg == NULL)
440: hashalg = HASHALG_DEFAULT;
441: if (out != NULL)
442: *out = NULL;
443: if ((r = hash_buffer(message, hashalg, &b)) != 0) {
444: error("%s: hash_buffer failed: %s", __func__, ssh_err(r));
445: goto out;
446: }
1.16 ! djm 447: if ((r = sshsig_wrap_sign(key, hashalg, sk_provider, sk_pin, b,
1.7 djm 448: sig_namespace, out, signer, signer_ctx)) != 0)
1.1 djm 449: goto out;
450: /* success */
451: r = 0;
452: out:
453: sshbuf_free(b);
454: return r;
455: }
456:
457: int
1.2 djm 458: sshsig_verifyb(struct sshbuf *signature, const struct sshbuf *message,
1.8 djm 459: const char *expect_namespace, struct sshkey **sign_keyp,
460: struct sshkey_sig_details **sig_details)
1.1 djm 461: {
462: struct sshbuf *b = NULL;
1.2 djm 463: int r = SSH_ERR_INTERNAL_ERROR;
1.1 djm 464: char *hashalg = NULL;
465:
1.8 djm 466: if (sig_details != NULL)
467: *sig_details = NULL;
1.1 djm 468: if (sign_keyp != NULL)
469: *sign_keyp = NULL;
470: if ((r = sshsig_peek_hashalg(signature, &hashalg)) != 0)
471: return r;
1.2 djm 472: debug("%s: signature made with hash \"%s\"", __func__, hashalg);
473: if ((r = hash_buffer(message, hashalg, &b)) != 0) {
474: error("%s: hash_buffer failed: %s", __func__, ssh_err(r));
1.1 djm 475: goto out;
476: }
477: if ((r = sshsig_wrap_verify(signature, hashalg, b, expect_namespace,
1.8 djm 478: sign_keyp, sig_details)) != 0)
1.1 djm 479: goto out;
480: /* success */
481: r = 0;
482: out:
483: sshbuf_free(b);
484: free(hashalg);
485: return r;
486: }
487:
488: static int
1.2 djm 489: hash_file(int fd, const char *hashalg, struct sshbuf **bp)
1.1 djm 490: {
1.2 djm 491: char *hex, rbuf[8192], hash[SSH_DIGEST_MAX_LENGTH];
1.1 djm 492: ssize_t n, total = 0;
493: struct ssh_digest_ctx *ctx;
1.2 djm 494: int alg, oerrno, r = SSH_ERR_INTERNAL_ERROR;
495: struct sshbuf *b = NULL;
496:
497: *bp = NULL;
498: memset(hash, 0, sizeof(hash));
1.1 djm 499:
1.2 djm 500: if ((r = sshsig_check_hashalg(hashalg)) != 0)
501: return r;
502: if ((alg = ssh_digest_alg_by_name(hashalg)) == -1) {
503: error("%s: can't look up hash algorithm %s",
504: __func__, hashalg);
505: return SSH_ERR_INTERNAL_ERROR;
506: }
507: if ((ctx = ssh_digest_start(alg)) == NULL) {
1.1 djm 508: error("%s: ssh_digest_start failed", __func__);
509: return SSH_ERR_INTERNAL_ERROR;
510: }
511: for (;;) {
512: if ((n = read(fd, rbuf, sizeof(rbuf))) == -1) {
513: if (errno == EINTR || errno == EAGAIN)
514: continue;
515: oerrno = errno;
516: error("%s: read: %s", __func__, strerror(errno));
517: ssh_digest_free(ctx);
518: errno = oerrno;
1.2 djm 519: r = SSH_ERR_SYSTEM_ERROR;
520: goto out;
1.1 djm 521: } else if (n == 0) {
522: debug2("%s: hashed %zu bytes", __func__, total);
523: break; /* EOF */
524: }
525: total += (size_t)n;
526: if ((r = ssh_digest_update(ctx, rbuf, (size_t)n)) != 0) {
527: error("%s: ssh_digest_update: %s",
528: __func__, ssh_err(r));
1.2 djm 529: goto out;
1.1 djm 530: }
531: }
1.2 djm 532: if ((r = ssh_digest_final(ctx, hash, sizeof(hash))) != 0) {
1.1 djm 533: error("%s: ssh_digest_final: %s", __func__, ssh_err(r));
1.2 djm 534: goto out;
1.1 djm 535: }
1.2 djm 536: if ((hex = tohex(hash, ssh_digest_bytes(alg))) != NULL) {
1.1 djm 537: debug3("%s: final hash: %s", __func__, hex);
538: freezero(hex, strlen(hex));
539: }
1.2 djm 540: if ((b = sshbuf_new()) == NULL) {
541: r = SSH_ERR_ALLOC_FAIL;
542: goto out;
543: }
544: if ((r = sshbuf_put(b, hash, ssh_digest_bytes(alg))) != 0) {
545: error("%s: sshbuf_put: %s", __func__, ssh_err(r));
546: goto out;
547: }
548: *bp = b;
549: b = NULL; /* transferred */
1.1 djm 550: /* success */
1.2 djm 551: r = 0;
552: out:
553: sshbuf_free(b);
1.1 djm 554: ssh_digest_free(ctx);
1.2 djm 555: explicit_bzero(hash, sizeof(hash));
1.12 markus 556: return r;
1.1 djm 557: }
558:
559: int
1.16 ! djm 560: sshsig_sign_fd(struct sshkey *key, const char *hashalg,
! 561: const char *sk_provider, const char *sk_pin,
1.1 djm 562: int fd, const char *sig_namespace, struct sshbuf **out,
563: sshsig_signer *signer, void *signer_ctx)
564: {
565: struct sshbuf *b = NULL;
1.2 djm 566: int r = SSH_ERR_INTERNAL_ERROR;
1.1 djm 567:
1.2 djm 568: if (hashalg == NULL)
569: hashalg = HASHALG_DEFAULT;
1.1 djm 570: if (out != NULL)
571: *out = NULL;
1.2 djm 572: if ((r = hash_file(fd, hashalg, &b)) != 0) {
1.1 djm 573: error("%s: hash_file failed: %s", __func__, ssh_err(r));
574: return r;
575: }
1.16 ! djm 576: if ((r = sshsig_wrap_sign(key, hashalg, sk_provider, sk_pin, b,
1.7 djm 577: sig_namespace, out, signer, signer_ctx)) != 0)
1.1 djm 578: goto out;
579: /* success */
580: r = 0;
581: out:
582: sshbuf_free(b);
583: return r;
584: }
585:
586: int
587: sshsig_verify_fd(struct sshbuf *signature, int fd,
1.8 djm 588: const char *expect_namespace, struct sshkey **sign_keyp,
589: struct sshkey_sig_details **sig_details)
1.1 djm 590: {
591: struct sshbuf *b = NULL;
1.2 djm 592: int r = SSH_ERR_INTERNAL_ERROR;
1.1 djm 593: char *hashalg = NULL;
594:
1.8 djm 595: if (sig_details != NULL)
596: *sig_details = NULL;
1.1 djm 597: if (sign_keyp != NULL)
598: *sign_keyp = NULL;
599: if ((r = sshsig_peek_hashalg(signature, &hashalg)) != 0)
600: return r;
1.2 djm 601: debug("%s: signature made with hash \"%s\"", __func__, hashalg);
602: if ((r = hash_file(fd, hashalg, &b)) != 0) {
1.1 djm 603: error("%s: hash_file failed: %s", __func__, ssh_err(r));
604: goto out;
605: }
606: if ((r = sshsig_wrap_verify(signature, hashalg, b, expect_namespace,
1.8 djm 607: sign_keyp, sig_details)) != 0)
1.1 djm 608: goto out;
609: /* success */
610: r = 0;
611: out:
612: sshbuf_free(b);
613: free(hashalg);
614: return r;
615: }
616:
1.4 djm 617: struct sshsigopt {
1.1 djm 618: int ca;
619: char *namespaces;
620: };
621:
1.4 djm 622: struct sshsigopt *
623: sshsigopt_parse(const char *opts, const char *path, u_long linenum,
1.1 djm 624: const char **errstrp)
625: {
1.4 djm 626: struct sshsigopt *ret;
1.1 djm 627: int r;
628: const char *errstr = NULL;
629:
630: if ((ret = calloc(1, sizeof(*ret))) == NULL)
631: return NULL;
632: if (opts == NULL || *opts == '\0')
633: return ret; /* Empty options yields empty options :) */
634:
635: while (*opts && *opts != ' ' && *opts != '\t') {
636: /* flag options */
637: if ((r = opt_flag("cert-authority", 0, &opts)) != -1) {
638: ret->ca = 1;
639: } else if (opt_match(&opts, "namespaces")) {
640: if (ret->namespaces != NULL) {
641: errstr = "multiple \"namespaces\" clauses";
642: goto fail;
643: }
644: ret->namespaces = opt_dequote(&opts, &errstr);
645: if (ret->namespaces == NULL)
646: goto fail;
647: }
648: /*
649: * Skip the comma, and move to the next option
650: * (or break out if there are no more).
651: */
652: if (*opts == '\0' || *opts == ' ' || *opts == '\t')
653: break; /* End of options. */
654: /* Anything other than a comma is an unknown option */
655: if (*opts != ',') {
656: errstr = "unknown key option";
657: goto fail;
658: }
659: opts++;
660: if (*opts == '\0') {
661: errstr = "unexpected end-of-options";
662: goto fail;
663: }
664: }
665: /* success */
666: return ret;
667: fail:
668: if (errstrp != NULL)
669: *errstrp = errstr;
1.5 djm 670: sshsigopt_free(ret);
1.1 djm 671: return NULL;
672: }
673:
1.4 djm 674: void
675: sshsigopt_free(struct sshsigopt *opts)
1.1 djm 676: {
677: if (opts == NULL)
678: return;
679: free(opts->namespaces);
680: free(opts);
681: }
682:
683: static int
1.9 djm 684: parse_principals_key_and_options(const char *path, u_long linenum, char *line,
685: const char *required_principal, char **principalsp, struct sshkey **keyp,
686: struct sshsigopt **sigoptsp)
1.1 djm 687: {
1.9 djm 688: char *opts = NULL, *tmp, *cp, *principals = NULL;
1.1 djm 689: const char *reason = NULL;
1.4 djm 690: struct sshsigopt *sigopts = NULL;
1.9 djm 691: struct sshkey *key = NULL;
692: int r = SSH_ERR_INTERNAL_ERROR;
1.1 djm 693:
1.9 djm 694: if (principalsp != NULL)
695: *principalsp = NULL;
696: if (sigoptsp != NULL)
697: *sigoptsp = NULL;
698: if (keyp != NULL)
699: *keyp = NULL;
1.1 djm 700:
701: cp = line;
702: cp = cp + strspn(cp, " \t"); /* skip leading whitespace */
703: if (*cp == '#' || *cp == '\0')
1.9 djm 704: return SSH_ERR_KEY_NOT_FOUND; /* blank or all-comment line */
705:
706: /* format: identity[,identity...] [option[,option...]] key */
707: if ((tmp = strdelimw(&cp)) == NULL) {
1.1 djm 708: error("%s:%lu: invalid line", path, linenum);
1.9 djm 709: r = SSH_ERR_INVALID_FORMAT;
710: goto out;
1.1 djm 711: }
1.9 djm 712: if ((principals = strdup(tmp)) == NULL) {
713: error("%s: strdup failed", __func__);
714: r = SSH_ERR_ALLOC_FAIL;
715: goto out;
716: }
717: /*
718: * Bail out early if we're looking for a particular principal and this
719: * line does not list it.
720: */
721: if (required_principal != NULL) {
722: if (match_pattern_list(required_principal,
723: principals, 0) != 1) {
724: /* principal didn't match */
725: r = SSH_ERR_KEY_NOT_FOUND;
726: goto out;
727: }
728: debug("%s: %s:%lu: matched principal \"%s\"",
729: __func__, path, linenum, required_principal);
1.1 djm 730: }
731:
1.9 djm 732: if ((key = sshkey_new(KEY_UNSPEC)) == NULL) {
733: error("%s: sshkey_new failed", __func__);
734: r = SSH_ERR_ALLOC_FAIL;
735: goto out;
736: }
737: if (sshkey_read(key, &cp) != 0) {
1.1 djm 738: /* no key? Check for options */
739: opts = cp;
740: if (sshkey_advance_past_options(&cp) != 0) {
1.9 djm 741: error("%s:%lu: invalid options", path, linenum);
742: r = SSH_ERR_INVALID_FORMAT;
743: goto out;
1.1 djm 744: }
745: *cp++ = '\0';
746: skip_space(&cp);
1.9 djm 747: if (sshkey_read(key, &cp) != 0) {
748: error("%s:%lu: invalid key", path, linenum);
749: r = SSH_ERR_INVALID_FORMAT;
750: goto out;
1.1 djm 751: }
752: }
753: debug3("%s:%lu: options %s", path, linenum, opts == NULL ? "" : opts);
1.4 djm 754: if ((sigopts = sshsigopt_parse(opts, path, linenum, &reason)) == NULL) {
1.1 djm 755: error("%s:%lu: bad options: %s", path, linenum, reason);
1.9 djm 756: r = SSH_ERR_INVALID_FORMAT;
757: goto out;
758: }
759: /* success */
760: if (principalsp != NULL) {
761: *principalsp = principals;
762: principals = NULL; /* transferred */
763: }
764: if (sigoptsp != NULL) {
765: *sigoptsp = sigopts;
766: sigopts = NULL; /* transferred */
767: }
768: if (keyp != NULL) {
769: *keyp = key;
770: key = NULL; /* transferred */
771: }
772: r = 0;
773: out:
774: free(principals);
775: sshsigopt_free(sigopts);
776: sshkey_free(key);
777: return r;
778: }
779:
780: static int
781: check_allowed_keys_line(const char *path, u_long linenum, char *line,
782: const struct sshkey *sign_key, const char *principal,
783: const char *sig_namespace)
784: {
785: struct sshkey *found_key = NULL;
786: int r, found = 0;
787: const char *reason = NULL;
788: struct sshsigopt *sigopts = NULL;
789:
790: /* Parse the line */
791: if ((r = parse_principals_key_and_options(path, linenum, line,
792: principal, NULL, &found_key, &sigopts)) != 0) {
793: /* error already logged */
1.1 djm 794: goto done;
795: }
796:
797: /* Check whether options preclude the use of this key */
798: if (sigopts->namespaces != NULL &&
799: match_pattern_list(sig_namespace, sigopts->namespaces, 0) != 1) {
800: error("%s:%lu: key is not permitted for use in signature "
801: "namespace \"%s\"", path, linenum, sig_namespace);
802: goto done;
803: }
804:
805: if (!sigopts->ca && sshkey_equal(found_key, sign_key)) {
806: /* Exact match of key */
807: debug("%s:%lu: matched key and principal", path, linenum);
808: /* success */
809: found = 1;
810: } else if (sigopts->ca && sshkey_is_cert(sign_key) &&
811: sshkey_equal_public(sign_key->cert->signature_key, found_key)) {
812: /* Match of certificate's CA key */
813: if ((r = sshkey_cert_check_authority(sign_key, 0, 1,
814: principal, &reason)) != 0) {
815: error("%s:%lu: certificate not authorized: %s",
816: path, linenum, reason);
817: goto done;
818: }
819: debug("%s:%lu: matched certificate CA key", path, linenum);
820: /* success */
821: found = 1;
822: } else {
823: /* Principal matched but key didn't */
824: goto done;
825: }
826: done:
827: sshkey_free(found_key);
1.4 djm 828: sshsigopt_free(sigopts);
1.1 djm 829: return found ? 0 : SSH_ERR_KEY_NOT_FOUND;
830: }
831:
832: int
833: sshsig_check_allowed_keys(const char *path, const struct sshkey *sign_key,
834: const char *principal, const char *sig_namespace)
835: {
836: FILE *f = NULL;
837: char *line = NULL;
838: size_t linesize = 0;
839: u_long linenum = 0;
1.12 markus 840: int r = SSH_ERR_INTERNAL_ERROR, oerrno;
1.1 djm 841:
842: /* Check key and principal against file */
843: if ((f = fopen(path, "r")) == NULL) {
844: oerrno = errno;
845: error("Unable to open allowed keys file \"%s\": %s",
846: path, strerror(errno));
847: errno = oerrno;
848: return SSH_ERR_SYSTEM_ERROR;
849: }
850:
851: while (getline(&line, &linesize, f) != -1) {
852: linenum++;
853: r = check_allowed_keys_line(path, linenum, line, sign_key,
854: principal, sig_namespace);
1.2 djm 855: free(line);
856: line = NULL;
1.1 djm 857: if (r == SSH_ERR_KEY_NOT_FOUND)
858: continue;
859: else if (r == 0) {
860: /* success */
861: fclose(f);
862: return 0;
863: } else
864: break;
865: }
866: /* Either we hit an error parsing or we simply didn't find the key */
867: fclose(f);
868: free(line);
869: return r == 0 ? SSH_ERR_KEY_NOT_FOUND : r;
870: }
1.10 djm 871:
872: static int
1.11 djm 873: cert_filter_principals(const char *path, u_long linenum,
874: char **principalsp, const struct sshkey *cert)
875: {
876: char *cp, *oprincipals, *principals;
877: const char *reason;
878: struct sshbuf *nprincipals;
879: int r = SSH_ERR_INTERNAL_ERROR, success = 0;
880:
881: oprincipals = principals = *principalsp;
882: *principalsp = NULL;
883:
1.15 markus 884: if ((nprincipals = sshbuf_new()) == NULL) {
885: r = SSH_ERR_ALLOC_FAIL;
886: goto out;
887: }
1.11 djm 888:
889: while ((cp = strsep(&principals, ",")) != NULL && *cp != '\0') {
890: if (strcspn(cp, "!?*") != strlen(cp)) {
891: debug("%s:%lu: principal \"%s\" not authorized: "
892: "contains wildcards", path, linenum, cp);
893: continue;
894: }
895: /* Check against principals list in certificate */
896: if ((r = sshkey_cert_check_authority(cert, 0, 1,
897: cp, &reason)) != 0) {
898: debug("%s:%lu: principal \"%s\" not authorized: %s",
899: path, linenum, cp, reason);
900: continue;
901: }
902: if ((r = sshbuf_putf(nprincipals, "%s%s",
903: sshbuf_len(nprincipals) != 0 ? "," : "", cp)) != 0) {
904: error("%s: buffer error", __func__);
905: goto out;
906: }
907: }
908: if (sshbuf_len(nprincipals) == 0) {
909: error("%s:%lu: no valid principals found", path, linenum);
910: r = SSH_ERR_KEY_CERT_INVALID;
911: goto out;
912: }
913: if ((principals = sshbuf_dup_string(nprincipals)) == NULL) {
914: error("%s: buffer error", __func__);
915: goto out;
916: }
917: /* success */
918: success = 1;
919: *principalsp = principals;
920: out:
921: sshbuf_free(nprincipals);
922: free(oprincipals);
923: return success ? 0 : r;
924: }
925:
926: static int
927: get_matching_principals_from_line(const char *path, u_long linenum, char *line,
1.10 djm 928: const struct sshkey *sign_key, char **principalsp)
929: {
930: struct sshkey *found_key = NULL;
931: char *principals = NULL;
932: int r, found = 0;
933: struct sshsigopt *sigopts = NULL;
934:
935: if (principalsp != NULL)
936: *principalsp = NULL;
937:
938: /* Parse the line */
939: if ((r = parse_principals_key_and_options(path, linenum, line,
940: NULL, &principals, &found_key, &sigopts)) != 0) {
941: /* error already logged */
942: goto done;
943: }
944:
945: if (!sigopts->ca && sshkey_equal(found_key, sign_key)) {
946: /* Exact match of key */
947: debug("%s:%lu: matched key", path, linenum);
948: /* success */
949: found = 1;
950: } else if (sigopts->ca && sshkey_is_cert(sign_key) &&
951: sshkey_equal_public(sign_key->cert->signature_key, found_key)) {
1.11 djm 952: /* Remove principals listed in file but not allowed by cert */
953: if ((r = cert_filter_principals(path, linenum,
954: &principals, sign_key)) != 0) {
955: /* error already displayed */
956: debug("%s:%lu: cert_filter_principals: %s",
957: path, linenum, ssh_err(r));
1.10 djm 958: goto done;
959: }
960: debug("%s:%lu: matched certificate CA key", path, linenum);
961: /* success */
962: found = 1;
963: } else {
964: /* Key didn't match */
965: goto done;
966: }
967: done:
1.13 markus 968: if (found && principalsp != NULL) {
1.10 djm 969: *principalsp = principals;
970: principals = NULL; /* transferred */
971: }
972: free(principals);
973: sshkey_free(found_key);
974: sshsigopt_free(sigopts);
975: return found ? 0 : SSH_ERR_KEY_NOT_FOUND;
976: }
977:
978: int
1.11 djm 979: sshsig_find_principals(const char *path, const struct sshkey *sign_key,
980: char **principals)
1.10 djm 981: {
982: FILE *f = NULL;
983: char *line = NULL;
984: size_t linesize = 0;
985: u_long linenum = 0;
1.14 markus 986: int r = SSH_ERR_INTERNAL_ERROR, oerrno;
1.10 djm 987:
988: if ((f = fopen(path, "r")) == NULL) {
989: oerrno = errno;
990: error("Unable to open allowed keys file \"%s\": %s",
991: path, strerror(errno));
992: errno = oerrno;
993: return SSH_ERR_SYSTEM_ERROR;
994: }
995:
996: while (getline(&line, &linesize, f) != -1) {
997: linenum++;
1.11 djm 998: r = get_matching_principals_from_line(path, linenum, line,
999: sign_key, principals);
1.10 djm 1000: free(line);
1001: line = NULL;
1002: if (r == SSH_ERR_KEY_NOT_FOUND)
1003: continue;
1004: else if (r == 0) {
1005: /* success */
1006: fclose(f);
1007: return 0;
1008: } else
1009: break;
1010: }
1011: free(line);
1012: /* Either we hit an error parsing or we simply didn't find the key */
1013: if (ferror(f) != 0) {
1014: oerrno = errno;
1015: fclose(f);
1016: error("Unable to read allowed keys file \"%s\": %s",
1017: path, strerror(errno));
1018: errno = oerrno;
1019: return SSH_ERR_SYSTEM_ERROR;
1020: }
1021: fclose(f);
1022: return r == 0 ? SSH_ERR_KEY_NOT_FOUND : r;
1023: }
1024:
1025: int
1026: sshsig_get_pubkey(struct sshbuf *signature, struct sshkey **pubkey)
1027: {
1028: struct sshkey *pk = NULL;
1029: int r = SSH_ERR_SIGNATURE_INVALID;
1030:
1.13 markus 1031: if (pubkey == NULL)
1032: return SSH_ERR_INTERNAL_ERROR;
1.10 djm 1033: if ((r = sshsig_parse_preamble(signature)) != 0)
1034: return r;
1035: if ((r = sshkey_froms(signature, &pk)) != 0)
1036: return r;
1037:
1038: *pubkey = pk;
1039: pk = NULL;
1040: return 0;
1041: }