1323124Sdes/* $OpenBSD: authfile.c,v 1.121 2016/04/09 12:39:30 djm Exp $ */
257429Smarkm/*
3262566Sdes * Copyright (c) 2000, 2013 Markus Friedl.  All rights reserved.
465674Skris *
565674Skris * Redistribution and use in source and binary forms, with or without
665674Skris * modification, are permitted provided that the following conditions
765674Skris * are met:
865674Skris * 1. Redistributions of source code must retain the above copyright
965674Skris *    notice, this list of conditions and the following disclaimer.
1065674Skris * 2. Redistributions in binary form must reproduce the above copyright
1165674Skris *    notice, this list of conditions and the following disclaimer in the
1265674Skris *    documentation and/or other materials provided with the distribution.
1365674Skris *
1465674Skris * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
1565674Skris * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
1665674Skris * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
1765674Skris * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
1865674Skris * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
1965674Skris * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
2065674Skris * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
2165674Skris * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2265674Skris * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
2365674Skris * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2457429Smarkm */
2557429Smarkm
2657429Smarkm#include "includes.h"
2757429Smarkm
28162856Sdes#include <sys/types.h>
29162856Sdes#include <sys/stat.h>
30162856Sdes#include <sys/uio.h>
31162856Sdes
32162856Sdes#include <errno.h>
33162856Sdes#include <fcntl.h>
34295367Sdes#include <stdio.h>
35162856Sdes#include <stdarg.h>
36162856Sdes#include <stdlib.h>
37162856Sdes#include <string.h>
38162856Sdes#include <unistd.h>
39295367Sdes#include <limits.h>
40162856Sdes
4176262Sgreen#include "cipher.h"
4257429Smarkm#include "ssh.h"
4376262Sgreen#include "log.h"
4476262Sgreen#include "authfile.h"
4592559Sdes#include "rsa.h"
46147005Sdes#include "misc.h"
47149753Sdes#include "atomicio.h"
48295367Sdes#include "sshkey.h"
49295367Sdes#include "sshbuf.h"
50295367Sdes#include "ssherr.h"
51295367Sdes#include "krl.h"
5257429Smarkm
53226046Sdes#define MAX_KEY_FILE_SIZE	(1024 * 1024)
54226046Sdes
55221420Sdes/* Save a key blob to a file */
56221420Sdesstatic int
57295367Sdessshkey_save_private_blob(struct sshbuf *keybuf, const char *filename)
58221420Sdes{
59295367Sdes	int fd, oerrno;
60221420Sdes
61295367Sdes	if ((fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0600)) < 0)
62295367Sdes		return SSH_ERR_SYSTEM_ERROR;
63295367Sdes	if (atomicio(vwrite, fd, (u_char *)sshbuf_ptr(keybuf),
64295367Sdes	    sshbuf_len(keybuf)) != sshbuf_len(keybuf)) {
65295367Sdes		oerrno = errno;
66221420Sdes		close(fd);
67221420Sdes		unlink(filename);
68295367Sdes		errno = oerrno;
69295367Sdes		return SSH_ERR_SYSTEM_ERROR;
70221420Sdes	}
71221420Sdes	close(fd);
72295367Sdes	return 0;
73221420Sdes}
74221420Sdes
75221420Sdesint
76295367Sdessshkey_save_private(struct sshkey *key, const char *filename,
77295367Sdes    const char *passphrase, const char *comment,
78295367Sdes    int force_new_format, const char *new_format_cipher, int new_format_rounds)
79221420Sdes{
80295367Sdes	struct sshbuf *keyblob = NULL;
81295367Sdes	int r;
82221420Sdes
83295367Sdes	if ((keyblob = sshbuf_new()) == NULL)
84295367Sdes		return SSH_ERR_ALLOC_FAIL;
85295367Sdes	if ((r = sshkey_private_to_fileblob(key, keyblob, passphrase, comment,
86295367Sdes	    force_new_format, new_format_cipher, new_format_rounds)) != 0)
87221420Sdes		goto out;
88295367Sdes	if ((r = sshkey_save_private_blob(keyblob, filename)) != 0)
89221420Sdes		goto out;
90295367Sdes	r = 0;
91221420Sdes out:
92295367Sdes	sshbuf_free(keyblob);
93295367Sdes	return r;
94221420Sdes}
95221420Sdes
96226046Sdes/* Load a key from a fd into a buffer */
97226046Sdesint
98295367Sdessshkey_load_file(int fd, struct sshbuf *blob)
99221420Sdes{
100226046Sdes	u_char buf[1024];
101221420Sdes	size_t len;
102113911Sdes	struct stat st;
103295367Sdes	int r;
10457429Smarkm
105295367Sdes	if (fstat(fd, &st) < 0)
106295367Sdes		return SSH_ERR_SYSTEM_ERROR;
107226046Sdes	if ((st.st_mode & (S_IFSOCK|S_IFCHR|S_IFIFO)) == 0 &&
108295367Sdes	    st.st_size > MAX_KEY_FILE_SIZE)
109295367Sdes		return SSH_ERR_INVALID_FORMAT;
110226046Sdes	for (;;) {
111226046Sdes		if ((len = atomicio(read, fd, buf, sizeof(buf))) == 0) {
112226046Sdes			if (errno == EPIPE)
113226046Sdes				break;
114295367Sdes			r = SSH_ERR_SYSTEM_ERROR;
115295367Sdes			goto out;
116226046Sdes		}
117295367Sdes		if ((r = sshbuf_put(blob, buf, len)) != 0)
118295367Sdes			goto out;
119295367Sdes		if (sshbuf_len(blob) > MAX_KEY_FILE_SIZE) {
120295367Sdes			r = SSH_ERR_INVALID_FORMAT;
121295367Sdes			goto out;
122226046Sdes		}
123226046Sdes	}
124226046Sdes	if ((st.st_mode & (S_IFSOCK|S_IFCHR|S_IFIFO)) == 0 &&
125295367Sdes	    st.st_size != (off_t)sshbuf_len(blob)) {
126295367Sdes		r = SSH_ERR_FILE_CHANGED;
127295367Sdes		goto out;
12857429Smarkm	}
129295367Sdes	r = 0;
130226046Sdes
131295367Sdes out:
132295367Sdes	explicit_bzero(buf, sizeof(buf));
133295367Sdes	if (r != 0)
134295367Sdes		sshbuf_reset(blob);
135295367Sdes	return r;
136221420Sdes}
13757429Smarkm
138295367Sdes#ifdef WITH_SSH1
139221420Sdes/*
140221420Sdes * Loads the public part of the ssh v1 key file.  Returns NULL if an error was
141221420Sdes * encountered (the file does not exist or is not readable), and the key
142221420Sdes * otherwise.
143221420Sdes */
144295367Sdesstatic int
145295367Sdessshkey_load_public_rsa1(int fd, struct sshkey **keyp, char **commentp)
146221420Sdes{
147295367Sdes	struct sshbuf *b = NULL;
148295367Sdes	int r;
149221420Sdes
150323124Sdes	if (keyp != NULL)
151323124Sdes		*keyp = NULL;
152255767Sdes	if (commentp != NULL)
153295367Sdes		*commentp = NULL;
15460576Skris
155295367Sdes	if ((b = sshbuf_new()) == NULL)
156295367Sdes		return SSH_ERR_ALLOC_FAIL;
157295367Sdes	if ((r = sshkey_load_file(fd, b)) != 0)
158295367Sdes		goto out;
159295367Sdes	if ((r = sshkey_parse_public_rsa1_fileblob(b, keyp, commentp)) != 0)
160295367Sdes		goto out;
161295367Sdes	r = 0;
162295367Sdes out:
163295367Sdes	sshbuf_free(b);
164295367Sdes	return r;
16560576Skris}
166295367Sdes#endif /* WITH_SSH1 */
16760576Skris
168295367Sdes/* XXX remove error() calls from here? */
169162856Sdesint
170295367Sdessshkey_perm_ok(int fd, const char *filename)
17160576Skris{
17260576Skris	struct stat st;
17360576Skris
17492559Sdes	if (fstat(fd, &st) < 0)
175295367Sdes		return SSH_ERR_SYSTEM_ERROR;
17692559Sdes	/*
17792559Sdes	 * if a key owned by the user is accessed, then we check the
17892559Sdes	 * permissions of the file. if the key owned by a different user,
17992559Sdes	 * then we don't care.
18092559Sdes	 */
18198941Sdes#ifdef HAVE_CYGWIN
18298941Sdes	if (check_ntsec(filename))
18398941Sdes#endif
18492559Sdes	if ((st.st_uid == getuid()) && (st.st_mode & 077) != 0) {
18560576Skris		error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@");
18660576Skris		error("@         WARNING: UNPROTECTED PRIVATE KEY FILE!          @");
18760576Skris		error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@");
18892559Sdes		error("Permissions 0%3.3o for '%s' are too open.",
189124211Sdes		    (u_int)st.st_mode & 0777, filename);
190226046Sdes		error("It is required that your private key files are NOT accessible by others.");
19176262Sgreen		error("This private key will be ignored.");
192295367Sdes		return SSH_ERR_KEY_BAD_PERMISSIONS;
19360576Skris	}
194295367Sdes	return 0;
19576262Sgreen}
19676262Sgreen
197295367Sdes/* XXX kill perm_ok now that we have SSH_ERR_KEY_BAD_PERMISSIONS? */
198295367Sdesint
199295367Sdessshkey_load_private_type(int type, const char *filename, const char *passphrase,
200295367Sdes    struct sshkey **keyp, char **commentp, int *perm_ok)
201221420Sdes{
202295367Sdes	int fd, r;
203262566Sdes
204323124Sdes	if (keyp != NULL)
205323124Sdes		*keyp = NULL;
206295367Sdes	if (commentp != NULL)
207295367Sdes		*commentp = NULL;
208221420Sdes
209295367Sdes	if ((fd = open(filename, O_RDONLY)) < 0) {
210204917Sdes		if (perm_ok != NULL)
211204917Sdes			*perm_ok = 0;
212295367Sdes		return SSH_ERR_SYSTEM_ERROR;
213204917Sdes	}
214295367Sdes	if (sshkey_perm_ok(fd, filename) != 0) {
215162856Sdes		if (perm_ok != NULL)
216162856Sdes			*perm_ok = 0;
217295367Sdes		r = SSH_ERR_KEY_BAD_PERMISSIONS;
218295367Sdes		goto out;
21976262Sgreen	}
220162856Sdes	if (perm_ok != NULL)
221162856Sdes		*perm_ok = 1;
222221420Sdes
223295367Sdes	r = sshkey_load_private_type_fd(fd, type, passphrase, keyp, commentp);
224295367Sdes out:
225221420Sdes	close(fd);
226295367Sdes	return r;
22760576Skris}
22865674Skris
229295367Sdesint
230295367Sdessshkey_load_private_type_fd(int fd, int type, const char *passphrase,
231295367Sdes    struct sshkey **keyp, char **commentp)
232226046Sdes{
233295367Sdes	struct sshbuf *buffer = NULL;
234295367Sdes	int r;
235226046Sdes
236323124Sdes	if (keyp != NULL)
237323124Sdes		*keyp = NULL;
238295367Sdes	if ((buffer = sshbuf_new()) == NULL) {
239295367Sdes		r = SSH_ERR_ALLOC_FAIL;
240295367Sdes		goto out;
241226046Sdes	}
242295367Sdes	if ((r = sshkey_load_file(fd, buffer)) != 0 ||
243295367Sdes	    (r = sshkey_parse_private_fileblob_type(buffer, type,
244295367Sdes	    passphrase, keyp, commentp)) != 0)
245295367Sdes		goto out;
246295367Sdes
247295367Sdes	/* success */
248295367Sdes	r = 0;
249295367Sdes out:
250296781Sdes	sshbuf_free(buffer);
251295367Sdes	return r;
252226046Sdes}
253226046Sdes
254295367Sdes/* XXX this is almost identical to sshkey_load_private_type() */
255295367Sdesint
256295367Sdessshkey_load_private(const char *filename, const char *passphrase,
257295367Sdes    struct sshkey **keyp, char **commentp)
25876262Sgreen{
259295367Sdes	struct sshbuf *buffer = NULL;
260295367Sdes	int r, fd;
26176262Sgreen
262323124Sdes	if (keyp != NULL)
263323124Sdes		*keyp = NULL;
264295367Sdes	if (commentp != NULL)
265295367Sdes		*commentp = NULL;
266295367Sdes
267295367Sdes	if ((fd = open(filename, O_RDONLY)) < 0)
268295367Sdes		return SSH_ERR_SYSTEM_ERROR;
269295367Sdes	if (sshkey_perm_ok(fd, filename) != 0) {
270295367Sdes		r = SSH_ERR_KEY_BAD_PERMISSIONS;
271295367Sdes		goto out;
272204917Sdes	}
273221420Sdes
274295367Sdes	if ((buffer = sshbuf_new()) == NULL) {
275295367Sdes		r = SSH_ERR_ALLOC_FAIL;
276295367Sdes		goto out;
277221420Sdes	}
278295367Sdes	if ((r = sshkey_load_file(fd, buffer)) != 0 ||
279296781Sdes	    (r = sshkey_parse_private_fileblob(buffer, passphrase, keyp,
280296781Sdes	    commentp)) != 0)
281295367Sdes		goto out;
282295367Sdes	r = 0;
283295367Sdes out:
284221420Sdes	close(fd);
285296781Sdes	sshbuf_free(buffer);
286295367Sdes	return r;
28776262Sgreen}
28876262Sgreen
28992559Sdesstatic int
290295367Sdessshkey_try_load_public(struct sshkey *k, const char *filename, char **commentp)
29165674Skris{
29265674Skris	FILE *f;
293147005Sdes	char line[SSH_MAX_PUBKEY_BYTES];
29465674Skris	char *cp;
295147005Sdes	u_long linenum = 0;
296295367Sdes	int r;
29765674Skris
298295367Sdes	if (commentp != NULL)
299295367Sdes		*commentp = NULL;
300295367Sdes	if ((f = fopen(filename, "r")) == NULL)
301295367Sdes		return SSH_ERR_SYSTEM_ERROR;
302295367Sdes	while (read_keyfile_line(f, filename, line, sizeof(line),
303295367Sdes		    &linenum) != -1) {
304295367Sdes		cp = line;
305295367Sdes		switch (*cp) {
306295367Sdes		case '#':
307295367Sdes		case '\n':
308295367Sdes		case '\0':
309295367Sdes			continue;
310295367Sdes		}
311295367Sdes		/* Abort loading if this looks like a private key */
312295367Sdes		if (strncmp(cp, "-----BEGIN", 10) == 0 ||
313295367Sdes		    strcmp(cp, "SSH PRIVATE KEY FILE") == 0)
314295367Sdes			break;
315295367Sdes		/* Skip leading whitespace. */
316295367Sdes		for (; *cp && (*cp == ' ' || *cp == '\t'); cp++)
317295367Sdes			;
318295367Sdes		if (*cp) {
319295367Sdes			if ((r = sshkey_read(k, &cp)) == 0) {
320295367Sdes				cp[strcspn(cp, "\r\n")] = '\0';
321295367Sdes				if (commentp) {
322295367Sdes					*commentp = strdup(*cp ?
323295367Sdes					    cp : filename);
324295367Sdes					if (*commentp == NULL)
325295367Sdes						r = SSH_ERR_ALLOC_FAIL;
32665674Skris				}
327295367Sdes				fclose(f);
328295367Sdes				return r;
32965674Skris			}
33065674Skris		}
33165674Skris	}
332295367Sdes	fclose(f);
333295367Sdes	return SSH_ERR_INVALID_FORMAT;
33465674Skris}
33565674Skris
33676262Sgreen/* load public key from ssh v1 private or any pubkey file */
337295367Sdesint
338295367Sdessshkey_load_public(const char *filename, struct sshkey **keyp, char **commentp)
33965674Skris{
340295367Sdes	struct sshkey *pub = NULL;
341295367Sdes	char file[PATH_MAX];
342295367Sdes	int r, fd;
34365674Skris
344295367Sdes	if (keyp != NULL)
345295367Sdes		*keyp = NULL;
346295367Sdes	if (commentp != NULL)
347295367Sdes		*commentp = NULL;
348295367Sdes
349295367Sdes	/* XXX should load file once and attempt to parse each format */
350295367Sdes
351295367Sdes	if ((fd = open(filename, O_RDONLY)) < 0)
352295367Sdes		goto skip;
353295367Sdes#ifdef WITH_SSH1
354124211Sdes	/* try rsa1 private key */
355295367Sdes	r = sshkey_load_public_rsa1(fd, keyp, commentp);
356295367Sdes	close(fd);
357295367Sdes	switch (r) {
358295367Sdes	case SSH_ERR_INTERNAL_ERROR:
359295367Sdes	case SSH_ERR_ALLOC_FAIL:
360295367Sdes	case SSH_ERR_INVALID_ARGUMENT:
361295367Sdes	case SSH_ERR_SYSTEM_ERROR:
362295367Sdes	case 0:
363295367Sdes		return r;
364295367Sdes	}
365295367Sdes#else /* WITH_SSH1 */
366295367Sdes	close(fd);
367295367Sdes#endif /* WITH_SSH1 */
368124211Sdes
369295367Sdes	/* try ssh2 public key */
370295367Sdes	if ((pub = sshkey_new(KEY_UNSPEC)) == NULL)
371295367Sdes		return SSH_ERR_ALLOC_FAIL;
372295367Sdes	if ((r = sshkey_try_load_public(pub, filename, commentp)) == 0) {
373295367Sdes		if (keyp != NULL)
374295367Sdes			*keyp = pub;
375295367Sdes		return 0;
376295367Sdes	}
377295367Sdes	sshkey_free(pub);
378295367Sdes
379295367Sdes#ifdef WITH_SSH1
380124211Sdes	/* try rsa1 public key */
381295367Sdes	if ((pub = sshkey_new(KEY_RSA1)) == NULL)
382295367Sdes		return SSH_ERR_ALLOC_FAIL;
383295367Sdes	if ((r = sshkey_try_load_public(pub, filename, commentp)) == 0) {
384295367Sdes		if (keyp != NULL)
385295367Sdes			*keyp = pub;
386295367Sdes		return 0;
387295367Sdes	}
388295367Sdes	sshkey_free(pub);
389295367Sdes#endif /* WITH_SSH1 */
390124211Sdes
391295367Sdes skip:
392295367Sdes	/* try .pub suffix */
393295367Sdes	if ((pub = sshkey_new(KEY_UNSPEC)) == NULL)
394295367Sdes		return SSH_ERR_ALLOC_FAIL;
395295367Sdes	r = SSH_ERR_ALLOC_FAIL;	/* in case strlcpy or strlcat fail */
39676262Sgreen	if ((strlcpy(file, filename, sizeof file) < sizeof(file)) &&
39776262Sgreen	    (strlcat(file, ".pub", sizeof file) < sizeof(file)) &&
398295367Sdes	    (r = sshkey_try_load_public(pub, file, commentp)) == 0) {
399295367Sdes		if (keyp != NULL)
400295367Sdes			*keyp = pub;
401295367Sdes		return 0;
402295367Sdes	}
403295367Sdes	sshkey_free(pub);
404295367Sdes
405295367Sdes	return r;
40665674Skris}
407204917Sdes
408215116Sdes/* Load the certificate associated with the named private key */
409295367Sdesint
410295367Sdessshkey_load_cert(const char *filename, struct sshkey **keyp)
411215116Sdes{
412295367Sdes	struct sshkey *pub = NULL;
413295367Sdes	char *file = NULL;
414295367Sdes	int r = SSH_ERR_INTERNAL_ERROR;
415215116Sdes
416323124Sdes	if (keyp != NULL)
417323124Sdes		*keyp = NULL;
418295367Sdes
419295367Sdes	if (asprintf(&file, "%s-cert.pub", filename) == -1)
420295367Sdes		return SSH_ERR_ALLOC_FAIL;
421295367Sdes
422295367Sdes	if ((pub = sshkey_new(KEY_UNSPEC)) == NULL) {
423295367Sdes		goto out;
424295367Sdes	}
425295367Sdes	if ((r = sshkey_try_load_public(pub, file, NULL)) != 0)
426295367Sdes		goto out;
427323124Sdes	/* success */
428323124Sdes	if (keyp != NULL) {
429323124Sdes		*keyp = pub;
430323124Sdes		pub = NULL;
431323124Sdes	}
432295367Sdes	r = 0;
433295367Sdes out:
434296781Sdes	free(file);
435296781Sdes	sshkey_free(pub);
436295367Sdes	return r;
437215116Sdes}
438215116Sdes
439215116Sdes/* Load private key and certificate */
440295367Sdesint
441295367Sdessshkey_load_private_cert(int type, const char *filename, const char *passphrase,
442295367Sdes    struct sshkey **keyp, int *perm_ok)
443215116Sdes{
444295367Sdes	struct sshkey *key = NULL, *cert = NULL;
445295367Sdes	int r;
446215116Sdes
447323124Sdes	if (keyp != NULL)
448323124Sdes		*keyp = NULL;
449295367Sdes
450215116Sdes	switch (type) {
451295367Sdes#ifdef WITH_OPENSSL
452215116Sdes	case KEY_RSA:
453215116Sdes	case KEY_DSA:
454221420Sdes	case KEY_ECDSA:
455295367Sdes#endif /* WITH_OPENSSL */
456262566Sdes	case KEY_ED25519:
457295367Sdes	case KEY_UNSPEC:
458215116Sdes		break;
459215116Sdes	default:
460295367Sdes		return SSH_ERR_KEY_TYPE_UNKNOWN;
461215116Sdes	}
462215116Sdes
463295367Sdes	if ((r = sshkey_load_private_type(type, filename,
464295367Sdes	    passphrase, &key, NULL, perm_ok)) != 0 ||
465295367Sdes	    (r = sshkey_load_cert(filename, &cert)) != 0)
466295367Sdes		goto out;
467215116Sdes
468215116Sdes	/* Make sure the private key matches the certificate */
469295367Sdes	if (sshkey_equal_public(key, cert) == 0) {
470295367Sdes		r = SSH_ERR_KEY_CERT_MISMATCH;
471295367Sdes		goto out;
472215116Sdes	}
473215116Sdes
474295367Sdes	if ((r = sshkey_to_certified(key)) != 0 ||
475295367Sdes	    (r = sshkey_cert_copy(cert, key)) != 0)
476295367Sdes		goto out;
477295367Sdes	r = 0;
478323124Sdes	if (keyp != NULL) {
479323124Sdes		*keyp = key;
480323124Sdes		key = NULL;
481323124Sdes	}
482295367Sdes out:
483296781Sdes	sshkey_free(key);
484296781Sdes	sshkey_free(cert);
485295367Sdes	return r;
486215116Sdes}
487215116Sdes
488204917Sdes/*
489295367Sdes * Returns success if the specified "key" is listed in the file "filename",
490295367Sdes * SSH_ERR_KEY_NOT_FOUND: if the key is not listed or another error.
491295367Sdes * If "strict_type" is set then the key type must match exactly,
492204917Sdes * otherwise a comparison that ignores certficiate data is performed.
493295367Sdes * If "check_ca" is set and "key" is a certificate, then its CA key is
494295367Sdes * also checked and sshkey_in_file() will return success if either is found.
495204917Sdes */
496204917Sdesint
497295367Sdessshkey_in_file(struct sshkey *key, const char *filename, int strict_type,
498295367Sdes    int check_ca)
499204917Sdes{
500204917Sdes	FILE *f;
501204917Sdes	char line[SSH_MAX_PUBKEY_BYTES];
502204917Sdes	char *cp;
503204917Sdes	u_long linenum = 0;
504295367Sdes	int r = 0;
505295367Sdes	struct sshkey *pub = NULL;
506295367Sdes	int (*sshkey_compare)(const struct sshkey *, const struct sshkey *) =
507295367Sdes	    strict_type ?  sshkey_equal : sshkey_equal_public;
508204917Sdes
509295367Sdes	if ((f = fopen(filename, "r")) == NULL)
510295367Sdes		return SSH_ERR_SYSTEM_ERROR;
511204917Sdes
512204917Sdes	while (read_keyfile_line(f, filename, line, sizeof(line),
513295367Sdes	    &linenum) != -1) {
514204917Sdes		cp = line;
515204917Sdes
516204917Sdes		/* Skip leading whitespace. */
517204917Sdes		for (; *cp && (*cp == ' ' || *cp == '\t'); cp++)
518204917Sdes			;
519204917Sdes
520204917Sdes		/* Skip comments and empty lines */
521204917Sdes		switch (*cp) {
522204917Sdes		case '#':
523204917Sdes		case '\n':
524204917Sdes		case '\0':
525204917Sdes			continue;
526204917Sdes		}
527204917Sdes
528295367Sdes		if ((pub = sshkey_new(KEY_UNSPEC)) == NULL) {
529295367Sdes			r = SSH_ERR_ALLOC_FAIL;
530295367Sdes			goto out;
531204917Sdes		}
532295367Sdes		if ((r = sshkey_read(pub, &cp)) != 0)
533295367Sdes			goto out;
534295367Sdes		if (sshkey_compare(key, pub) ||
535295367Sdes		    (check_ca && sshkey_is_cert(key) &&
536295367Sdes		    sshkey_compare(key->cert->signature_key, pub))) {
537295367Sdes			r = 0;
538295367Sdes			goto out;
539204917Sdes		}
540295367Sdes		sshkey_free(pub);
541295367Sdes		pub = NULL;
542204917Sdes	}
543295367Sdes	r = SSH_ERR_KEY_NOT_FOUND;
544295367Sdes out:
545296781Sdes	sshkey_free(pub);
546204917Sdes	fclose(f);
547295367Sdes	return r;
548204917Sdes}
549295367Sdes
550295367Sdes/*
551295367Sdes * Checks whether the specified key is revoked, returning 0 if not,
552295367Sdes * SSH_ERR_KEY_REVOKED if it is or another error code if something
553295367Sdes * unexpected happened.
554295367Sdes * This will check both the key and, if it is a certificate, its CA key too.
555295367Sdes * "revoked_keys_file" may be a KRL or a one-per-line list of public keys.
556295367Sdes */
557295367Sdesint
558295367Sdessshkey_check_revoked(struct sshkey *key, const char *revoked_keys_file)
559295367Sdes{
560295367Sdes	int r;
561295367Sdes
562295367Sdes	r = ssh_krl_file_contains_key(revoked_keys_file, key);
563295367Sdes	/* If this was not a KRL to begin with then continue below */
564295367Sdes	if (r != SSH_ERR_KRL_BAD_MAGIC)
565295367Sdes		return r;
566295367Sdes
567295367Sdes	/*
568295367Sdes	 * If the file is not a KRL or we can't handle KRLs then attempt to
569295367Sdes	 * parse the file as a flat list of keys.
570295367Sdes	 */
571295367Sdes	switch ((r = sshkey_in_file(key, revoked_keys_file, 0, 1))) {
572295367Sdes	case 0:
573295367Sdes		/* Key found => revoked */
574295367Sdes		return SSH_ERR_KEY_REVOKED;
575295367Sdes	case SSH_ERR_KEY_NOT_FOUND:
576295367Sdes		/* Key not found => not revoked */
577295367Sdes		return 0;
578295367Sdes	default:
579295367Sdes		/* Some other error occurred */
580295367Sdes		return r;
581295367Sdes	}
582295367Sdes}
583295367Sdes
584