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