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