Annotation of src/usr.bin/ssh/sshsig.c, Revision 1.5
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,
152: const struct sshbuf *h_message, const char *sig_namespace,
153: struct sshbuf **out, sshsig_signer *signer, void *signer_ctx)
154: {
155: int r;
156: size_t slen = 0;
157: u_char *sig = NULL;
158: struct sshbuf *blob = NULL;
159: struct sshbuf *tosign = NULL;
160: const char *sign_alg = NULL;
161:
162: if ((tosign = sshbuf_new()) == NULL ||
163: (blob = sshbuf_new()) == NULL) {
164: error("%s: sshbuf_new failed", __func__);
165: r = SSH_ERR_ALLOC_FAIL;
166: goto done;
167: }
168:
169: if ((r = sshbuf_put(tosign, MAGIC_PREAMBLE, MAGIC_PREAMBLE_LEN)) != 0 ||
170: (r = sshbuf_put_cstring(tosign, sig_namespace)) != 0 ||
171: (r = sshbuf_put_string(tosign, NULL, 0)) != 0 || /* reserved */
172: (r = sshbuf_put_cstring(tosign, hashalg)) != 0 ||
173: (r = sshbuf_putb(tosign, h_message)) != 0) {
174: error("Couldn't construct message to sign: %s", ssh_err(r));
175: goto done;
176: }
177:
178: /* If using RSA keys then default to a good signature algorithm */
179: if (sshkey_type_plain(key->type) == KEY_RSA)
180: sign_alg = RSA_SIGN_ALG;
181:
182: if (signer != NULL) {
183: if ((r = signer(key, &sig, &slen,
184: sshbuf_ptr(tosign), sshbuf_len(tosign),
185: sign_alg, 0, signer_ctx)) != 0) {
186: error("Couldn't sign message: %s", ssh_err(r));
187: goto done;
188: }
189: } else {
190: if ((r = sshkey_sign(key, &sig, &slen,
191: sshbuf_ptr(tosign), sshbuf_len(tosign),
192: sign_alg, 0)) != 0) {
193: error("Couldn't sign message: %s", ssh_err(r));
194: goto done;
195: }
196: }
197:
198: if ((r = sshbuf_put(blob, MAGIC_PREAMBLE, MAGIC_PREAMBLE_LEN)) != 0 ||
199: (r = sshbuf_put_u32(blob, SIG_VERSION)) != 0 ||
200: (r = sshkey_puts(key, blob)) != 0 ||
201: (r = sshbuf_put_cstring(blob, sig_namespace)) != 0 ||
202: (r = sshbuf_put_string(blob, NULL, 0)) != 0 || /* reserved */
203: (r = sshbuf_put_cstring(blob, hashalg)) != 0 ||
204: (r = sshbuf_put_string(blob, sig, slen)) != 0) {
205: error("Couldn't populate blob: %s", ssh_err(r));
206: goto done;
207: }
208:
209: *out = blob;
210: blob = NULL;
211: r = 0;
212: done:
213: free(sig);
214: sshbuf_free(blob);
215: sshbuf_free(tosign);
216: return r;
217: }
218:
219: /* Check preamble and version. */
220: static int
221: sshsig_parse_preamble(struct sshbuf *buf)
222: {
223: int r = SSH_ERR_INTERNAL_ERROR;
224: uint32_t sversion;
225:
226: if ((r = sshbuf_cmp(buf, 0, MAGIC_PREAMBLE, MAGIC_PREAMBLE_LEN)) != 0 ||
227: (r = sshbuf_consume(buf, (sizeof(MAGIC_PREAMBLE)-1))) != 0 ||
228: (r = sshbuf_get_u32(buf, &sversion)) != 0) {
229: error("Couldn't verify signature: invalid format");
230: return r;
231: }
232:
1.2 djm 233: if (sversion > SIG_VERSION) {
1.1 djm 234: error("Signature version %lu is larger than supported "
235: "version %u", (unsigned long)sversion, SIG_VERSION);
236: return SSH_ERR_INVALID_FORMAT;
237: }
238: return 0;
239: }
240:
241: static int
242: sshsig_check_hashalg(const char *hashalg)
243: {
1.2 djm 244: if (hashalg == NULL ||
245: match_pattern_list(hashalg, HASHALG_ALLOWED, 0) == 1)
1.1 djm 246: return 0;
247: error("%s: unsupported hash algorithm \"%.100s\"", __func__, hashalg);
248: return SSH_ERR_SIGN_ALG_UNSUPPORTED;
249: }
250:
251: static int
252: sshsig_peek_hashalg(struct sshbuf *signature, char **hashalgp)
253: {
254: struct sshbuf *buf = NULL;
255: char *hashalg = NULL;
256: int r = SSH_ERR_INTERNAL_ERROR;
257:
258: if (hashalgp != NULL)
259: *hashalgp = NULL;
260: if ((buf = sshbuf_fromb(signature)) == NULL)
261: return SSH_ERR_ALLOC_FAIL;
262: if ((r = sshsig_parse_preamble(buf)) != 0)
263: goto done;
264: if ((r = sshbuf_get_string_direct(buf, NULL, NULL)) != 0 ||
265: (r = sshbuf_get_string_direct(buf, NULL, NULL)) != 0 ||
266: (r = sshbuf_get_string(buf, NULL, NULL)) != 0 ||
267: (r = sshbuf_get_cstring(buf, &hashalg, NULL)) != 0 ||
268: (r = sshbuf_get_string_direct(buf, NULL, NULL)) != 0) {
269: error("Couldn't parse signature blob: %s", ssh_err(r));
270: goto done;
271: }
272:
273: /* success */
274: r = 0;
275: *hashalgp = hashalg;
276: hashalg = NULL;
277: done:
278: free(hashalg);
279: sshbuf_free(buf);
280: return r;
281: }
282:
283: static int
284: sshsig_wrap_verify(struct sshbuf *signature, const char *hashalg,
285: const struct sshbuf *h_message, const char *expect_namespace,
286: struct sshkey **sign_keyp)
287: {
288: int r = SSH_ERR_INTERNAL_ERROR;
289: struct sshbuf *buf = NULL, *toverify = NULL;
290: struct sshkey *key = NULL;
291: const u_char *sig;
292: char *got_namespace = NULL, *sigtype = NULL, *sig_hashalg = NULL;
293: size_t siglen;
294:
1.2 djm 295: debug("%s: verify message length %zu", __func__, sshbuf_len(h_message));
1.1 djm 296: if (sign_keyp != NULL)
297: *sign_keyp = NULL;
298:
299: if ((toverify = sshbuf_new()) == NULL) {
300: error("%s: sshbuf_new failed", __func__);
301: r = SSH_ERR_ALLOC_FAIL;
302: goto done;
303: }
304: if ((r = sshbuf_put(toverify, MAGIC_PREAMBLE,
305: MAGIC_PREAMBLE_LEN)) != 0 ||
306: (r = sshbuf_put_cstring(toverify, expect_namespace)) != 0 ||
307: (r = sshbuf_put_string(toverify, NULL, 0)) != 0 || /* reserved */
308: (r = sshbuf_put_cstring(toverify, hashalg)) != 0 ||
309: (r = sshbuf_putb(toverify, h_message)) != 0) {
310: error("Couldn't construct message to verify: %s", ssh_err(r));
311: goto done;
312: }
313:
314: if ((r = sshsig_parse_preamble(signature)) != 0)
315: goto done;
316:
317: if ((r = sshkey_froms(signature, &key)) != 0 ||
318: (r = sshbuf_get_cstring(signature, &got_namespace, NULL)) != 0 ||
319: (r = sshbuf_get_string(signature, NULL, NULL)) != 0 ||
320: (r = sshbuf_get_cstring(signature, &sig_hashalg, NULL)) != 0 ||
321: (r = sshbuf_get_string_direct(signature, &sig, &siglen)) != 0) {
322: error("Couldn't parse signature blob: %s", ssh_err(r));
323: goto done;
324: }
325:
326: if (sshbuf_len(signature) != 0) {
327: error("Signature contains trailing data");
328: r = SSH_ERR_INVALID_FORMAT;
329: goto done;
330: }
331:
332: if (strcmp(expect_namespace, got_namespace) != 0) {
333: error("Couldn't verify signature: namespace does not match");
334: debug("%s: expected namespace \"%s\" received \"%s\"",
335: __func__, expect_namespace, got_namespace);
336: r = SSH_ERR_SIGNATURE_INVALID;
337: goto done;
338: }
339: if (strcmp(hashalg, sig_hashalg) != 0) {
340: error("Couldn't verify signature: hash algorithm mismatch");
341: debug("%s: expected algorithm \"%s\" received \"%s\"",
342: __func__, hashalg, sig_hashalg);
343: r = SSH_ERR_SIGNATURE_INVALID;
344: goto done;
345: }
346: /* Ensure that RSA keys use an acceptable signature algorithm */
347: if (sshkey_type_plain(key->type) == KEY_RSA) {
348: if ((r = sshkey_get_sigtype(sig, siglen, &sigtype)) != 0) {
349: error("Couldn't verify signature: unable to get "
350: "signature type: %s", ssh_err(r));
351: goto done;
352: }
353: if (match_pattern_list(sigtype, RSA_SIGN_ALLOWED, 0) != 1) {
354: error("Couldn't verify signature: unsupported RSA "
355: "signature algorithm %s", sigtype);
356: r = SSH_ERR_SIGN_ALG_UNSUPPORTED;
357: goto done;
358: }
359: }
360: if ((r = sshkey_verify(key, sig, siglen, sshbuf_ptr(toverify),
361: sshbuf_len(toverify), NULL, 0)) != 0) {
362: error("Signature verification failed: %s", ssh_err(r));
363: goto done;
364: }
365:
366: /* success */
367: r = 0;
368: if (sign_keyp != NULL) {
369: *sign_keyp = key;
370: key = NULL; /* transferred */
371: }
372: done:
373: free(got_namespace);
374: free(sigtype);
375: free(sig_hashalg);
376: sshbuf_free(buf);
377: sshbuf_free(toverify);
378: sshkey_free(key);
379: return r;
380: }
381:
1.2 djm 382: static int
383: hash_buffer(const struct sshbuf *m, const char *hashalg, struct sshbuf **bp)
1.1 djm 384: {
1.2 djm 385: char *hex, hash[SSH_DIGEST_MAX_LENGTH];
386: int alg, r = SSH_ERR_INTERNAL_ERROR;
1.1 djm 387: struct sshbuf *b = NULL;
388:
1.2 djm 389: *bp = NULL;
390: memset(hash, 0, sizeof(hash));
1.1 djm 391:
392: if ((r = sshsig_check_hashalg(hashalg)) != 0)
393: return r;
394: if ((alg = ssh_digest_alg_by_name(hashalg)) == -1) {
395: error("%s: can't look up hash algorithm %s",
1.2 djm 396: __func__, hashalg);
1.1 djm 397: return SSH_ERR_INTERNAL_ERROR;
398: }
1.2 djm 399: if ((r = ssh_digest_buffer(alg, m, hash, sizeof(hash))) != 0) {
1.1 djm 400: error("%s: ssh_digest_buffer failed: %s", __func__, ssh_err(r));
401: return r;
402: }
1.2 djm 403: if ((hex = tohex(hash, ssh_digest_bytes(alg))) != NULL) {
404: debug3("%s: final hash: %s", __func__, hex);
405: freezero(hex, strlen(hex));
406: }
407: if ((b = sshbuf_new()) == NULL) {
1.1 djm 408: r = SSH_ERR_ALLOC_FAIL;
409: goto out;
410: }
1.2 djm 411: if ((r = sshbuf_put(b, hash, ssh_digest_bytes(alg))) != 0) {
412: error("%s: sshbuf_put: %s", __func__, ssh_err(r));
413: goto out;
414: }
415: *bp = b;
416: b = NULL; /* transferred */
417: /* success */
418: r = 0;
419: out:
420: sshbuf_free(b);
421: explicit_bzero(hash, sizeof(hash));
422: return 0;
423: }
424:
425: int
426: sshsig_signb(struct sshkey *key, const char *hashalg,
427: const struct sshbuf *message, const char *sig_namespace,
428: struct sshbuf **out, sshsig_signer *signer, void *signer_ctx)
429: {
430: struct sshbuf *b = NULL;
431: int r = SSH_ERR_INTERNAL_ERROR;
432:
433: if (hashalg == NULL)
434: hashalg = HASHALG_DEFAULT;
435: if (out != NULL)
436: *out = NULL;
437: if ((r = hash_buffer(message, hashalg, &b)) != 0) {
438: error("%s: hash_buffer failed: %s", __func__, ssh_err(r));
439: goto out;
440: }
1.1 djm 441: if ((r = sshsig_wrap_sign(key, hashalg, b, sig_namespace, out,
442: signer, signer_ctx)) != 0)
443: goto out;
444: /* success */
445: r = 0;
446: out:
447: sshbuf_free(b);
448: return r;
449: }
450:
451: int
1.2 djm 452: sshsig_verifyb(struct sshbuf *signature, const struct sshbuf *message,
1.1 djm 453: const char *expect_namespace, struct sshkey **sign_keyp)
454: {
455: struct sshbuf *b = NULL;
1.2 djm 456: int r = SSH_ERR_INTERNAL_ERROR;
1.1 djm 457: char *hashalg = NULL;
458:
459: if (sign_keyp != NULL)
460: *sign_keyp = NULL;
461:
462: if ((r = sshsig_peek_hashalg(signature, &hashalg)) != 0)
463: return r;
1.2 djm 464: debug("%s: signature made with hash \"%s\"", __func__, hashalg);
465: if ((r = hash_buffer(message, hashalg, &b)) != 0) {
466: error("%s: hash_buffer failed: %s", __func__, ssh_err(r));
1.1 djm 467: goto out;
468: }
469: if ((r = sshsig_wrap_verify(signature, hashalg, b, expect_namespace,
470: sign_keyp)) != 0)
471: goto out;
472: /* success */
473: r = 0;
474: out:
475: sshbuf_free(b);
476: free(hashalg);
477: return r;
478: }
479:
480: static int
1.2 djm 481: hash_file(int fd, const char *hashalg, struct sshbuf **bp)
1.1 djm 482: {
1.2 djm 483: char *hex, rbuf[8192], hash[SSH_DIGEST_MAX_LENGTH];
1.1 djm 484: ssize_t n, total = 0;
485: struct ssh_digest_ctx *ctx;
1.2 djm 486: int alg, oerrno, r = SSH_ERR_INTERNAL_ERROR;
487: struct sshbuf *b = NULL;
488:
489: *bp = NULL;
490: memset(hash, 0, sizeof(hash));
1.1 djm 491:
1.2 djm 492: if ((r = sshsig_check_hashalg(hashalg)) != 0)
493: return r;
494: if ((alg = ssh_digest_alg_by_name(hashalg)) == -1) {
495: error("%s: can't look up hash algorithm %s",
496: __func__, hashalg);
497: return SSH_ERR_INTERNAL_ERROR;
498: }
499: if ((ctx = ssh_digest_start(alg)) == NULL) {
1.1 djm 500: error("%s: ssh_digest_start failed", __func__);
501: return SSH_ERR_INTERNAL_ERROR;
502: }
503: for (;;) {
504: if ((n = read(fd, rbuf, sizeof(rbuf))) == -1) {
505: if (errno == EINTR || errno == EAGAIN)
506: continue;
507: oerrno = errno;
508: error("%s: read: %s", __func__, strerror(errno));
509: ssh_digest_free(ctx);
510: errno = oerrno;
1.2 djm 511: r = SSH_ERR_SYSTEM_ERROR;
512: goto out;
1.1 djm 513: } else if (n == 0) {
514: debug2("%s: hashed %zu bytes", __func__, total);
515: break; /* EOF */
516: }
517: total += (size_t)n;
518: if ((r = ssh_digest_update(ctx, rbuf, (size_t)n)) != 0) {
519: error("%s: ssh_digest_update: %s",
520: __func__, ssh_err(r));
1.2 djm 521: goto out;
1.1 djm 522: }
523: }
1.2 djm 524: if ((r = ssh_digest_final(ctx, hash, sizeof(hash))) != 0) {
1.1 djm 525: error("%s: ssh_digest_final: %s", __func__, ssh_err(r));
1.2 djm 526: goto out;
1.1 djm 527: }
1.2 djm 528: if ((hex = tohex(hash, ssh_digest_bytes(alg))) != NULL) {
1.1 djm 529: debug3("%s: final hash: %s", __func__, hex);
530: freezero(hex, strlen(hex));
531: }
1.2 djm 532: if ((b = sshbuf_new()) == NULL) {
533: r = SSH_ERR_ALLOC_FAIL;
534: goto out;
535: }
536: if ((r = sshbuf_put(b, hash, ssh_digest_bytes(alg))) != 0) {
537: error("%s: sshbuf_put: %s", __func__, ssh_err(r));
538: goto out;
539: }
540: *bp = b;
541: b = NULL; /* transferred */
1.1 djm 542: /* success */
1.2 djm 543: r = 0;
544: out:
545: sshbuf_free(b);
1.1 djm 546: ssh_digest_free(ctx);
1.2 djm 547: explicit_bzero(hash, sizeof(hash));
1.1 djm 548: return 0;
549: }
550:
551: int
552: sshsig_sign_fd(struct sshkey *key, const char *hashalg,
553: int fd, const char *sig_namespace, struct sshbuf **out,
554: sshsig_signer *signer, void *signer_ctx)
555: {
556: struct sshbuf *b = NULL;
1.2 djm 557: int r = SSH_ERR_INTERNAL_ERROR;
1.1 djm 558:
1.2 djm 559: if (hashalg == NULL)
560: hashalg = HASHALG_DEFAULT;
1.1 djm 561: if (out != NULL)
562: *out = NULL;
1.2 djm 563: if ((r = hash_file(fd, hashalg, &b)) != 0) {
1.1 djm 564: error("%s: hash_file failed: %s", __func__, ssh_err(r));
565: return r;
566: }
567: if ((r = sshsig_wrap_sign(key, hashalg, b, sig_namespace, out,
568: signer, signer_ctx)) != 0)
569: goto out;
570: /* success */
571: r = 0;
572: out:
573: sshbuf_free(b);
574: return r;
575: }
576:
577: int
578: sshsig_verify_fd(struct sshbuf *signature, int fd,
579: const char *expect_namespace, struct sshkey **sign_keyp)
580: {
581: struct sshbuf *b = NULL;
1.2 djm 582: int r = SSH_ERR_INTERNAL_ERROR;
1.1 djm 583: char *hashalg = NULL;
584:
585: if (sign_keyp != NULL)
586: *sign_keyp = NULL;
587:
588: if ((r = sshsig_peek_hashalg(signature, &hashalg)) != 0)
589: return r;
1.2 djm 590: debug("%s: signature made with hash \"%s\"", __func__, hashalg);
591: if ((r = hash_file(fd, hashalg, &b)) != 0) {
1.1 djm 592: error("%s: hash_file failed: %s", __func__, ssh_err(r));
593: goto out;
594: }
595: if ((r = sshsig_wrap_verify(signature, hashalg, b, expect_namespace,
596: sign_keyp)) != 0)
597: goto out;
598: /* success */
599: r = 0;
600: out:
601: sshbuf_free(b);
602: free(hashalg);
603: return r;
604: }
605:
1.4 djm 606: struct sshsigopt {
1.1 djm 607: int ca;
608: char *namespaces;
609: };
610:
1.4 djm 611: struct sshsigopt *
612: sshsigopt_parse(const char *opts, const char *path, u_long linenum,
1.1 djm 613: const char **errstrp)
614: {
1.4 djm 615: struct sshsigopt *ret;
1.1 djm 616: int r;
617: const char *errstr = NULL;
618:
619: if ((ret = calloc(1, sizeof(*ret))) == NULL)
620: return NULL;
621: if (opts == NULL || *opts == '\0')
622: return ret; /* Empty options yields empty options :) */
623:
624: while (*opts && *opts != ' ' && *opts != '\t') {
625: /* flag options */
626: if ((r = opt_flag("cert-authority", 0, &opts)) != -1) {
627: ret->ca = 1;
628: } else if (opt_match(&opts, "namespaces")) {
629: if (ret->namespaces != NULL) {
630: errstr = "multiple \"namespaces\" clauses";
631: goto fail;
632: }
633: ret->namespaces = opt_dequote(&opts, &errstr);
634: if (ret->namespaces == NULL)
635: goto fail;
636: }
637: /*
638: * Skip the comma, and move to the next option
639: * (or break out if there are no more).
640: */
641: if (*opts == '\0' || *opts == ' ' || *opts == '\t')
642: break; /* End of options. */
643: /* Anything other than a comma is an unknown option */
644: if (*opts != ',') {
645: errstr = "unknown key option";
646: goto fail;
647: }
648: opts++;
649: if (*opts == '\0') {
650: errstr = "unexpected end-of-options";
651: goto fail;
652: }
653: }
654: /* success */
655: return ret;
656: fail:
657: if (errstrp != NULL)
658: *errstrp = errstr;
1.5 ! djm 659: sshsigopt_free(ret);
1.1 djm 660: return NULL;
661: }
662:
1.4 djm 663: void
664: sshsigopt_free(struct sshsigopt *opts)
1.1 djm 665: {
666: if (opts == NULL)
667: return;
668: free(opts->namespaces);
669: free(opts);
670: }
671:
672: static int
673: check_allowed_keys_line(const char *path, u_long linenum, char *line,
674: const struct sshkey *sign_key, const char *principal,
675: const char *sig_namespace)
676: {
677: struct sshkey *found_key = NULL;
678: char *cp, *opts = NULL, *identities = NULL;
679: int r, found = 0;
680: const char *reason = NULL;
1.4 djm 681: struct sshsigopt *sigopts = NULL;
1.1 djm 682:
683: if ((found_key = sshkey_new(KEY_UNSPEC)) == NULL) {
684: error("%s: sshkey_new failed", __func__);
685: return SSH_ERR_ALLOC_FAIL;
686: }
687:
688: /* format: identity[,identity...] [option[,option...]] key */
689: cp = line;
690: cp = cp + strspn(cp, " \t"); /* skip leading whitespace */
691: if (*cp == '#' || *cp == '\0')
692: goto done;
693: if ((identities = strdelimw(&cp)) == NULL) {
694: error("%s:%lu: invalid line", path, linenum);
695: goto done;
696: }
697: if (match_pattern_list(principal, identities, 0) != 1) {
698: /* principal didn't match */
699: goto done;
700: }
701: debug("%s: %s:%lu: matched principal \"%s\"",
702: __func__, path, linenum, principal);
703:
704: if (sshkey_read(found_key, &cp) != 0) {
705: /* no key? Check for options */
706: opts = cp;
707: if (sshkey_advance_past_options(&cp) != 0) {
708: error("%s:%lu: invalid options",
709: path, linenum);
710: goto done;
711: }
712: *cp++ = '\0';
713: skip_space(&cp);
714: if (sshkey_read(found_key, &cp) != 0) {
715: error("%s:%lu: invalid key", path,
716: linenum);
717: goto done;
718: }
719: }
720: debug3("%s:%lu: options %s", path, linenum, opts == NULL ? "" : opts);
1.4 djm 721: if ((sigopts = sshsigopt_parse(opts, path, linenum, &reason)) == NULL) {
1.1 djm 722: error("%s:%lu: bad options: %s", path, linenum, reason);
723: goto done;
724: }
725:
726: /* Check whether options preclude the use of this key */
727: if (sigopts->namespaces != NULL &&
728: match_pattern_list(sig_namespace, sigopts->namespaces, 0) != 1) {
729: error("%s:%lu: key is not permitted for use in signature "
730: "namespace \"%s\"", path, linenum, sig_namespace);
731: goto done;
732: }
733:
734: if (!sigopts->ca && sshkey_equal(found_key, sign_key)) {
735: /* Exact match of key */
736: debug("%s:%lu: matched key and principal", path, linenum);
737: /* success */
738: found = 1;
739: } else if (sigopts->ca && sshkey_is_cert(sign_key) &&
740: sshkey_equal_public(sign_key->cert->signature_key, found_key)) {
741: /* Match of certificate's CA key */
742: if ((r = sshkey_cert_check_authority(sign_key, 0, 1,
743: principal, &reason)) != 0) {
744: error("%s:%lu: certificate not authorized: %s",
745: path, linenum, reason);
746: goto done;
747: }
748: debug("%s:%lu: matched certificate CA key", path, linenum);
749: /* success */
750: found = 1;
751: } else {
752: /* Principal matched but key didn't */
753: goto done;
754: }
755: done:
756: sshkey_free(found_key);
1.4 djm 757: sshsigopt_free(sigopts);
1.1 djm 758: return found ? 0 : SSH_ERR_KEY_NOT_FOUND;
759: }
760:
761: int
762: sshsig_check_allowed_keys(const char *path, const struct sshkey *sign_key,
763: const char *principal, const char *sig_namespace)
764: {
765: FILE *f = NULL;
766: char *line = NULL;
767: size_t linesize = 0;
768: u_long linenum = 0;
769: int r, oerrno;
770:
771: /* Check key and principal against file */
772: if ((f = fopen(path, "r")) == NULL) {
773: oerrno = errno;
774: error("Unable to open allowed keys file \"%s\": %s",
775: path, strerror(errno));
776: errno = oerrno;
777: return SSH_ERR_SYSTEM_ERROR;
778: }
779:
780: while (getline(&line, &linesize, f) != -1) {
781: linenum++;
782: r = check_allowed_keys_line(path, linenum, line, sign_key,
783: principal, sig_namespace);
1.2 djm 784: free(line);
785: line = NULL;
1.1 djm 786: if (r == SSH_ERR_KEY_NOT_FOUND)
787: continue;
788: else if (r == 0) {
789: /* success */
790: fclose(f);
791: return 0;
792: } else
793: break;
794: }
795: /* Either we hit an error parsing or we simply didn't find the key */
796: fclose(f);
797: free(line);
798: return r == 0 ? SSH_ERR_KEY_NOT_FOUND : r;
799: }