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