xref: /freebsd/crypto/openssh/authfile.c (revision 2f513db72b034fd5ef7f080b11be5c711c15186a)
1*2f513db7SEd Maste /* $OpenBSD: authfile.c,v 1.131 2018/09/21 12:20:12 djm Exp $ */
2511b41d2SMark Murray /*
3f7167e0eSDag-Erling Smørgrav  * Copyright (c) 2000, 2013 Markus Friedl.  All rights reserved.
4c2d3a559SKris Kennaway  *
5c2d3a559SKris Kennaway  * Redistribution and use in source and binary forms, with or without
6c2d3a559SKris Kennaway  * modification, are permitted provided that the following conditions
7c2d3a559SKris Kennaway  * are met:
8c2d3a559SKris Kennaway  * 1. Redistributions of source code must retain the above copyright
9c2d3a559SKris Kennaway  *    notice, this list of conditions and the following disclaimer.
10c2d3a559SKris Kennaway  * 2. Redistributions in binary form must reproduce the above copyright
11c2d3a559SKris Kennaway  *    notice, this list of conditions and the following disclaimer in the
12c2d3a559SKris Kennaway  *    documentation and/or other materials provided with the distribution.
13c2d3a559SKris Kennaway  *
14c2d3a559SKris Kennaway  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
15c2d3a559SKris Kennaway  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16c2d3a559SKris Kennaway  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17c2d3a559SKris Kennaway  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
18c2d3a559SKris Kennaway  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
19c2d3a559SKris Kennaway  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
20c2d3a559SKris Kennaway  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
21c2d3a559SKris Kennaway  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22c2d3a559SKris Kennaway  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
23c2d3a559SKris Kennaway  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24511b41d2SMark Murray  */
25511b41d2SMark Murray 
26511b41d2SMark Murray #include "includes.h"
27333ee039SDag-Erling Smørgrav 
28333ee039SDag-Erling Smørgrav #include <sys/types.h>
29333ee039SDag-Erling Smørgrav #include <sys/stat.h>
30333ee039SDag-Erling Smørgrav #include <sys/uio.h>
31511b41d2SMark Murray 
32333ee039SDag-Erling Smørgrav #include <errno.h>
33333ee039SDag-Erling Smørgrav #include <fcntl.h>
34333ee039SDag-Erling Smørgrav #include <stdio.h>
35a0ee8cc6SDag-Erling Smørgrav #include <stdarg.h>
36333ee039SDag-Erling Smørgrav #include <stdlib.h>
37333ee039SDag-Erling Smørgrav #include <string.h>
38333ee039SDag-Erling Smørgrav #include <unistd.h>
39bc5531deSDag-Erling Smørgrav #include <limits.h>
40333ee039SDag-Erling Smørgrav 
41333ee039SDag-Erling Smørgrav #include "cipher.h"
42ca3176e7SBrian Feldman #include "ssh.h"
43ca3176e7SBrian Feldman #include "log.h"
44ca3176e7SBrian Feldman #include "authfile.h"
45aa49c926SDag-Erling Smørgrav #include "misc.h"
46d4ecd108SDag-Erling Smørgrav #include "atomicio.h"
47eccfee6eSDag-Erling Smørgrav #include "sshkey.h"
48a0ee8cc6SDag-Erling Smørgrav #include "sshbuf.h"
49a0ee8cc6SDag-Erling Smørgrav #include "ssherr.h"
50bc5531deSDag-Erling Smørgrav #include "krl.h"
51511b41d2SMark Murray 
52e146993eSDag-Erling Smørgrav #define MAX_KEY_FILE_SIZE	(1024 * 1024)
53e146993eSDag-Erling Smørgrav 
544a421b63SDag-Erling Smørgrav /* Save a key blob to a file */
554a421b63SDag-Erling Smørgrav static int
56a0ee8cc6SDag-Erling Smørgrav sshkey_save_private_blob(struct sshbuf *keybuf, const char *filename)
574a421b63SDag-Erling Smørgrav {
58a0ee8cc6SDag-Erling Smørgrav 	int fd, oerrno;
594a421b63SDag-Erling Smørgrav 
60a0ee8cc6SDag-Erling Smørgrav 	if ((fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0600)) < 0)
61a0ee8cc6SDag-Erling Smørgrav 		return SSH_ERR_SYSTEM_ERROR;
62190cef3dSDag-Erling Smørgrav 	if (atomicio(vwrite, fd, sshbuf_mutable_ptr(keybuf),
63a0ee8cc6SDag-Erling Smørgrav 	    sshbuf_len(keybuf)) != sshbuf_len(keybuf)) {
64a0ee8cc6SDag-Erling Smørgrav 		oerrno = errno;
654a421b63SDag-Erling Smørgrav 		close(fd);
664a421b63SDag-Erling Smørgrav 		unlink(filename);
67a0ee8cc6SDag-Erling Smørgrav 		errno = oerrno;
68a0ee8cc6SDag-Erling Smørgrav 		return SSH_ERR_SYSTEM_ERROR;
694a421b63SDag-Erling Smørgrav 	}
704a421b63SDag-Erling Smørgrav 	close(fd);
714a421b63SDag-Erling Smørgrav 	return 0;
724a421b63SDag-Erling Smørgrav }
734a421b63SDag-Erling Smørgrav 
74e8aafc91SKris Kennaway int
75a0ee8cc6SDag-Erling Smørgrav sshkey_save_private(struct sshkey *key, const char *filename,
76a0ee8cc6SDag-Erling Smørgrav     const char *passphrase, const char *comment,
77a0ee8cc6SDag-Erling Smørgrav     int force_new_format, const char *new_format_cipher, int new_format_rounds)
78e8aafc91SKris Kennaway {
79a0ee8cc6SDag-Erling Smørgrav 	struct sshbuf *keyblob = NULL;
80a0ee8cc6SDag-Erling Smørgrav 	int r;
814a421b63SDag-Erling Smørgrav 
82a0ee8cc6SDag-Erling Smørgrav 	if ((keyblob = sshbuf_new()) == NULL)
83a0ee8cc6SDag-Erling Smørgrav 		return SSH_ERR_ALLOC_FAIL;
84a0ee8cc6SDag-Erling Smørgrav 	if ((r = sshkey_private_to_fileblob(key, keyblob, passphrase, comment,
85a0ee8cc6SDag-Erling Smørgrav 	    force_new_format, new_format_cipher, new_format_rounds)) != 0)
864a421b63SDag-Erling Smørgrav 		goto out;
87a0ee8cc6SDag-Erling Smørgrav 	if ((r = sshkey_save_private_blob(keyblob, filename)) != 0)
884a421b63SDag-Erling Smørgrav 		goto out;
89a0ee8cc6SDag-Erling Smørgrav 	r = 0;
904a421b63SDag-Erling Smørgrav  out:
91a0ee8cc6SDag-Erling Smørgrav 	sshbuf_free(keyblob);
92a0ee8cc6SDag-Erling Smørgrav 	return r;
934a421b63SDag-Erling Smørgrav }
944a421b63SDag-Erling Smørgrav 
95e146993eSDag-Erling Smørgrav /* Load a key from a fd into a buffer */
96e146993eSDag-Erling Smørgrav int
97bc5531deSDag-Erling Smørgrav sshkey_load_file(int fd, struct sshbuf *blob)
984a421b63SDag-Erling Smørgrav {
99e146993eSDag-Erling Smørgrav 	u_char buf[1024];
1004a421b63SDag-Erling Smørgrav 	size_t len;
1014a421b63SDag-Erling Smørgrav 	struct stat st;
1024f52dfbbSDag-Erling Smørgrav 	int r;
1034a421b63SDag-Erling Smørgrav 
104a0ee8cc6SDag-Erling Smørgrav 	if (fstat(fd, &st) < 0)
105a0ee8cc6SDag-Erling Smørgrav 		return SSH_ERR_SYSTEM_ERROR;
106e146993eSDag-Erling Smørgrav 	if ((st.st_mode & (S_IFSOCK|S_IFCHR|S_IFIFO)) == 0 &&
107a0ee8cc6SDag-Erling Smørgrav 	    st.st_size > MAX_KEY_FILE_SIZE)
108a0ee8cc6SDag-Erling Smørgrav 		return SSH_ERR_INVALID_FORMAT;
109e146993eSDag-Erling Smørgrav 	for (;;) {
110e146993eSDag-Erling Smørgrav 		if ((len = atomicio(read, fd, buf, sizeof(buf))) == 0) {
111e146993eSDag-Erling Smørgrav 			if (errno == EPIPE)
112e146993eSDag-Erling Smørgrav 				break;
113a0ee8cc6SDag-Erling Smørgrav 			r = SSH_ERR_SYSTEM_ERROR;
114a0ee8cc6SDag-Erling Smørgrav 			goto out;
1154a421b63SDag-Erling Smørgrav 		}
116a0ee8cc6SDag-Erling Smørgrav 		if ((r = sshbuf_put(blob, buf, len)) != 0)
117a0ee8cc6SDag-Erling Smørgrav 			goto out;
118a0ee8cc6SDag-Erling Smørgrav 		if (sshbuf_len(blob) > MAX_KEY_FILE_SIZE) {
119a0ee8cc6SDag-Erling Smørgrav 			r = SSH_ERR_INVALID_FORMAT;
120a0ee8cc6SDag-Erling Smørgrav 			goto out;
121e146993eSDag-Erling Smørgrav 		}
122e146993eSDag-Erling Smørgrav 	}
123e146993eSDag-Erling Smørgrav 	if ((st.st_mode & (S_IFSOCK|S_IFCHR|S_IFIFO)) == 0 &&
124a0ee8cc6SDag-Erling Smørgrav 	    st.st_size != (off_t)sshbuf_len(blob)) {
125a0ee8cc6SDag-Erling Smørgrav 		r = SSH_ERR_FILE_CHANGED;
126a0ee8cc6SDag-Erling Smørgrav 		goto out;
127a0ee8cc6SDag-Erling Smørgrav 	}
128a0ee8cc6SDag-Erling Smørgrav 	r = 0;
129a0ee8cc6SDag-Erling Smørgrav 
130a0ee8cc6SDag-Erling Smørgrav  out:
131a0ee8cc6SDag-Erling Smørgrav 	explicit_bzero(buf, sizeof(buf));
132a0ee8cc6SDag-Erling Smørgrav 	if (r != 0)
133a0ee8cc6SDag-Erling Smørgrav 		sshbuf_reset(blob);
134a0ee8cc6SDag-Erling Smørgrav 	return r;
135e146993eSDag-Erling Smørgrav }
136e146993eSDag-Erling Smørgrav 
137e8aafc91SKris Kennaway 
138a0ee8cc6SDag-Erling Smørgrav /* XXX remove error() calls from here? */
139a0ee8cc6SDag-Erling Smørgrav int
140a0ee8cc6SDag-Erling Smørgrav sshkey_perm_ok(int fd, const char *filename)
141e8aafc91SKris Kennaway {
142e8aafc91SKris Kennaway 	struct stat st;
143e8aafc91SKris Kennaway 
144af12a3e7SDag-Erling Smørgrav 	if (fstat(fd, &st) < 0)
145a0ee8cc6SDag-Erling Smørgrav 		return SSH_ERR_SYSTEM_ERROR;
146af12a3e7SDag-Erling Smørgrav 	/*
147af12a3e7SDag-Erling Smørgrav 	 * if a key owned by the user is accessed, then we check the
148af12a3e7SDag-Erling Smørgrav 	 * permissions of the file. if the key owned by a different user,
149af12a3e7SDag-Erling Smørgrav 	 * then we don't care.
150af12a3e7SDag-Erling Smørgrav 	 */
151989dd127SDag-Erling Smørgrav #ifdef HAVE_CYGWIN
152989dd127SDag-Erling Smørgrav 	if (check_ntsec(filename))
153989dd127SDag-Erling Smørgrav #endif
154af12a3e7SDag-Erling Smørgrav 	if ((st.st_uid == getuid()) && (st.st_mode & 077) != 0) {
155e8aafc91SKris Kennaway 		error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@");
156e8aafc91SKris Kennaway 		error("@         WARNING: UNPROTECTED PRIVATE KEY FILE!          @");
157e8aafc91SKris Kennaway 		error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@");
158af12a3e7SDag-Erling Smørgrav 		error("Permissions 0%3.3o for '%s' are too open.",
159cf2b5f3bSDag-Erling Smørgrav 		    (u_int)st.st_mode & 0777, filename);
160557f75e5SDag-Erling Smørgrav 		error("It is required that your private key files are NOT accessible by others.");
161ca3176e7SBrian Feldman 		error("This private key will be ignored.");
162a0ee8cc6SDag-Erling Smørgrav 		return SSH_ERR_KEY_BAD_PERMISSIONS;
163a0ee8cc6SDag-Erling Smørgrav 	}
164e8aafc91SKris Kennaway 	return 0;
165e8aafc91SKris Kennaway }
166ca3176e7SBrian Feldman 
167a0ee8cc6SDag-Erling Smørgrav /* XXX kill perm_ok now that we have SSH_ERR_KEY_BAD_PERMISSIONS? */
168a0ee8cc6SDag-Erling Smørgrav int
169a0ee8cc6SDag-Erling Smørgrav sshkey_load_private_type(int type, const char *filename, const char *passphrase,
170a0ee8cc6SDag-Erling Smørgrav     struct sshkey **keyp, char **commentp, int *perm_ok)
1714a421b63SDag-Erling Smørgrav {
172a0ee8cc6SDag-Erling Smørgrav 	int fd, r;
173f7167e0eSDag-Erling Smørgrav 
174076ad2f8SDag-Erling Smørgrav 	if (keyp != NULL)
175a0ee8cc6SDag-Erling Smørgrav 		*keyp = NULL;
176a0ee8cc6SDag-Erling Smørgrav 	if (commentp != NULL)
177a0ee8cc6SDag-Erling Smørgrav 		*commentp = NULL;
1784a421b63SDag-Erling Smørgrav 
179a0ee8cc6SDag-Erling Smørgrav 	if ((fd = open(filename, O_RDONLY)) < 0) {
180b15c8340SDag-Erling Smørgrav 		if (perm_ok != NULL)
181b15c8340SDag-Erling Smørgrav 			*perm_ok = 0;
182a0ee8cc6SDag-Erling Smørgrav 		return SSH_ERR_SYSTEM_ERROR;
183b15c8340SDag-Erling Smørgrav 	}
184a0ee8cc6SDag-Erling Smørgrav 	if (sshkey_perm_ok(fd, filename) != 0) {
185333ee039SDag-Erling Smørgrav 		if (perm_ok != NULL)
186333ee039SDag-Erling Smørgrav 			*perm_ok = 0;
187a0ee8cc6SDag-Erling Smørgrav 		r = SSH_ERR_KEY_BAD_PERMISSIONS;
188a0ee8cc6SDag-Erling Smørgrav 		goto out;
189e8aafc91SKris Kennaway 	}
190333ee039SDag-Erling Smørgrav 	if (perm_ok != NULL)
191333ee039SDag-Erling Smørgrav 		*perm_ok = 1;
1924a421b63SDag-Erling Smørgrav 
193bc5531deSDag-Erling Smørgrav 	r = sshkey_load_private_type_fd(fd, type, passphrase, keyp, commentp);
19447dd1d1bSDag-Erling Smørgrav 	if (r == 0 && keyp && *keyp)
19547dd1d1bSDag-Erling Smørgrav 		r = sshkey_set_filename(*keyp, filename);
196bc5531deSDag-Erling Smørgrav  out:
197bc5531deSDag-Erling Smørgrav 	close(fd);
198bc5531deSDag-Erling Smørgrav 	return r;
199bc5531deSDag-Erling Smørgrav }
200bc5531deSDag-Erling Smørgrav 
201bc5531deSDag-Erling Smørgrav int
202bc5531deSDag-Erling Smørgrav sshkey_load_private_type_fd(int fd, int type, const char *passphrase,
203bc5531deSDag-Erling Smørgrav     struct sshkey **keyp, char **commentp)
204bc5531deSDag-Erling Smørgrav {
205bc5531deSDag-Erling Smørgrav 	struct sshbuf *buffer = NULL;
206bc5531deSDag-Erling Smørgrav 	int r;
207bc5531deSDag-Erling Smørgrav 
208076ad2f8SDag-Erling Smørgrav 	if (keyp != NULL)
209076ad2f8SDag-Erling Smørgrav 		*keyp = NULL;
210a0ee8cc6SDag-Erling Smørgrav 	if ((buffer = sshbuf_new()) == NULL) {
211a0ee8cc6SDag-Erling Smørgrav 		r = SSH_ERR_ALLOC_FAIL;
212a0ee8cc6SDag-Erling Smørgrav 		goto out;
213ca3176e7SBrian Feldman 	}
214bc5531deSDag-Erling Smørgrav 	if ((r = sshkey_load_file(fd, buffer)) != 0 ||
215bc5531deSDag-Erling Smørgrav 	    (r = sshkey_parse_private_fileblob_type(buffer, type,
216bc5531deSDag-Erling Smørgrav 	    passphrase, keyp, commentp)) != 0)
217a0ee8cc6SDag-Erling Smørgrav 		goto out;
218bc5531deSDag-Erling Smørgrav 
219bc5531deSDag-Erling Smørgrav 	/* success */
220a0ee8cc6SDag-Erling Smørgrav 	r = 0;
221a0ee8cc6SDag-Erling Smørgrav  out:
222a0ee8cc6SDag-Erling Smørgrav 	sshbuf_free(buffer);
223a0ee8cc6SDag-Erling Smørgrav 	return r;
2244a421b63SDag-Erling Smørgrav }
225ca3176e7SBrian Feldman 
226a0ee8cc6SDag-Erling Smørgrav /* XXX this is almost identical to sshkey_load_private_type() */
227a0ee8cc6SDag-Erling Smørgrav int
228a0ee8cc6SDag-Erling Smørgrav sshkey_load_private(const char *filename, const char *passphrase,
229a0ee8cc6SDag-Erling Smørgrav     struct sshkey **keyp, char **commentp)
230e146993eSDag-Erling Smørgrav {
231a0ee8cc6SDag-Erling Smørgrav 	struct sshbuf *buffer = NULL;
232a0ee8cc6SDag-Erling Smørgrav 	int r, fd;
233e146993eSDag-Erling Smørgrav 
234076ad2f8SDag-Erling Smørgrav 	if (keyp != NULL)
235a0ee8cc6SDag-Erling Smørgrav 		*keyp = NULL;
236a0ee8cc6SDag-Erling Smørgrav 	if (commentp != NULL)
237a0ee8cc6SDag-Erling Smørgrav 		*commentp = NULL;
238a0ee8cc6SDag-Erling Smørgrav 
239a0ee8cc6SDag-Erling Smørgrav 	if ((fd = open(filename, O_RDONLY)) < 0)
240a0ee8cc6SDag-Erling Smørgrav 		return SSH_ERR_SYSTEM_ERROR;
241a0ee8cc6SDag-Erling Smørgrav 	if (sshkey_perm_ok(fd, filename) != 0) {
242a0ee8cc6SDag-Erling Smørgrav 		r = SSH_ERR_KEY_BAD_PERMISSIONS;
243a0ee8cc6SDag-Erling Smørgrav 		goto out;
244e146993eSDag-Erling Smørgrav 	}
245e146993eSDag-Erling Smørgrav 
246a0ee8cc6SDag-Erling Smørgrav 	if ((buffer = sshbuf_new()) == NULL) {
247a0ee8cc6SDag-Erling Smørgrav 		r = SSH_ERR_ALLOC_FAIL;
248a0ee8cc6SDag-Erling Smørgrav 		goto out;
249b15c8340SDag-Erling Smørgrav 	}
250bc5531deSDag-Erling Smørgrav 	if ((r = sshkey_load_file(fd, buffer)) != 0 ||
251acc1a9efSDag-Erling Smørgrav 	    (r = sshkey_parse_private_fileblob(buffer, passphrase, keyp,
252acc1a9efSDag-Erling Smørgrav 	    commentp)) != 0)
253a0ee8cc6SDag-Erling Smørgrav 		goto out;
25447dd1d1bSDag-Erling Smørgrav 	if (keyp && *keyp &&
25547dd1d1bSDag-Erling Smørgrav 	    (r = sshkey_set_filename(*keyp, filename)) != 0)
25647dd1d1bSDag-Erling Smørgrav 		goto out;
257a0ee8cc6SDag-Erling Smørgrav 	r = 0;
258a0ee8cc6SDag-Erling Smørgrav  out:
259e8aafc91SKris Kennaway 	close(fd);
260a0ee8cc6SDag-Erling Smørgrav 	sshbuf_free(buffer);
261a0ee8cc6SDag-Erling Smørgrav 	return r;
262e8aafc91SKris Kennaway }
263c2d3a559SKris Kennaway 
264af12a3e7SDag-Erling Smørgrav static int
265a0ee8cc6SDag-Erling Smørgrav sshkey_try_load_public(struct sshkey *k, const char *filename, char **commentp)
266c2d3a559SKris Kennaway {
267c2d3a559SKris Kennaway 	FILE *f;
268190cef3dSDag-Erling Smørgrav 	char *line = NULL, *cp;
269190cef3dSDag-Erling Smørgrav 	size_t linesize = 0;
270a0ee8cc6SDag-Erling Smørgrav 	int r;
271c2d3a559SKris Kennaway 
272a0ee8cc6SDag-Erling Smørgrav 	if (commentp != NULL)
273a0ee8cc6SDag-Erling Smørgrav 		*commentp = NULL;
274a0ee8cc6SDag-Erling Smørgrav 	if ((f = fopen(filename, "r")) == NULL)
275a0ee8cc6SDag-Erling Smørgrav 		return SSH_ERR_SYSTEM_ERROR;
276190cef3dSDag-Erling Smørgrav 	while (getline(&line, &linesize, f) != -1) {
277c2d3a559SKris Kennaway 		cp = line;
278c2d3a559SKris Kennaway 		switch (*cp) {
279c2d3a559SKris Kennaway 		case '#':
280c2d3a559SKris Kennaway 		case '\n':
281c2d3a559SKris Kennaway 		case '\0':
282c2d3a559SKris Kennaway 			continue;
283c2d3a559SKris Kennaway 		}
284e146993eSDag-Erling Smørgrav 		/* Abort loading if this looks like a private key */
285a0ee8cc6SDag-Erling Smørgrav 		if (strncmp(cp, "-----BEGIN", 10) == 0 ||
286a0ee8cc6SDag-Erling Smørgrav 		    strcmp(cp, "SSH PRIVATE KEY FILE") == 0)
287e146993eSDag-Erling Smørgrav 			break;
288c2d3a559SKris Kennaway 		/* Skip leading whitespace. */
289c2d3a559SKris Kennaway 		for (; *cp && (*cp == ' ' || *cp == '\t'); cp++)
290c2d3a559SKris Kennaway 			;
291c2d3a559SKris Kennaway 		if (*cp) {
292a0ee8cc6SDag-Erling Smørgrav 			if ((r = sshkey_read(k, &cp)) == 0) {
293e146993eSDag-Erling Smørgrav 				cp[strcspn(cp, "\r\n")] = '\0';
294e146993eSDag-Erling Smørgrav 				if (commentp) {
295a0ee8cc6SDag-Erling Smørgrav 					*commentp = strdup(*cp ?
296e146993eSDag-Erling Smørgrav 					    cp : filename);
297a0ee8cc6SDag-Erling Smørgrav 					if (*commentp == NULL)
298a0ee8cc6SDag-Erling Smørgrav 						r = SSH_ERR_ALLOC_FAIL;
299e146993eSDag-Erling Smørgrav 				}
300190cef3dSDag-Erling Smørgrav 				free(line);
301c2d3a559SKris Kennaway 				fclose(f);
302a0ee8cc6SDag-Erling Smørgrav 				return r;
303c2d3a559SKris Kennaway 			}
304c2d3a559SKris Kennaway 		}
305c2d3a559SKris Kennaway 	}
306190cef3dSDag-Erling Smørgrav 	free(line);
307c2d3a559SKris Kennaway 	fclose(f);
308a0ee8cc6SDag-Erling Smørgrav 	return SSH_ERR_INVALID_FORMAT;
309c2d3a559SKris Kennaway }
310c2d3a559SKris Kennaway 
3114f52dfbbSDag-Erling Smørgrav /* load public key from any pubkey file */
312a0ee8cc6SDag-Erling Smørgrav int
313a0ee8cc6SDag-Erling Smørgrav sshkey_load_public(const char *filename, struct sshkey **keyp, char **commentp)
314c2d3a559SKris Kennaway {
315a0ee8cc6SDag-Erling Smørgrav 	struct sshkey *pub = NULL;
3164f52dfbbSDag-Erling Smørgrav 	char *file = NULL;
3174f52dfbbSDag-Erling Smørgrav 	int r;
318c2d3a559SKris Kennaway 
319a0ee8cc6SDag-Erling Smørgrav 	if (keyp != NULL)
320a0ee8cc6SDag-Erling Smørgrav 		*keyp = NULL;
321a0ee8cc6SDag-Erling Smørgrav 	if (commentp != NULL)
322a0ee8cc6SDag-Erling Smørgrav 		*commentp = NULL;
323a0ee8cc6SDag-Erling Smørgrav 
324a0ee8cc6SDag-Erling Smørgrav 	if ((pub = sshkey_new(KEY_UNSPEC)) == NULL)
325a0ee8cc6SDag-Erling Smørgrav 		return SSH_ERR_ALLOC_FAIL;
326a0ee8cc6SDag-Erling Smørgrav 	if ((r = sshkey_try_load_public(pub, filename, commentp)) == 0) {
3274f52dfbbSDag-Erling Smørgrav 		if (keyp != NULL) {
328a0ee8cc6SDag-Erling Smørgrav 			*keyp = pub;
3294f52dfbbSDag-Erling Smørgrav 			pub = NULL;
3304f52dfbbSDag-Erling Smørgrav 		}
3314f52dfbbSDag-Erling Smørgrav 		r = 0;
3324f52dfbbSDag-Erling Smørgrav 		goto out;
333a0ee8cc6SDag-Erling Smørgrav 	}
334a0ee8cc6SDag-Erling Smørgrav 	sshkey_free(pub);
335a0ee8cc6SDag-Erling Smørgrav 
336a0ee8cc6SDag-Erling Smørgrav 	/* try .pub suffix */
3374f52dfbbSDag-Erling Smørgrav 	if (asprintf(&file, "%s.pub", filename) == -1)
338a0ee8cc6SDag-Erling Smørgrav 		return SSH_ERR_ALLOC_FAIL;
3394f52dfbbSDag-Erling Smørgrav 	if ((pub = sshkey_new(KEY_UNSPEC)) == NULL) {
3404f52dfbbSDag-Erling Smørgrav 		r = SSH_ERR_ALLOC_FAIL;
3414f52dfbbSDag-Erling Smørgrav 		goto out;
342a0ee8cc6SDag-Erling Smørgrav 	}
3434f52dfbbSDag-Erling Smørgrav 	if ((r = sshkey_try_load_public(pub, file, commentp)) == 0) {
3444f52dfbbSDag-Erling Smørgrav 		if (keyp != NULL) {
3454f52dfbbSDag-Erling Smørgrav 			*keyp = pub;
3464f52dfbbSDag-Erling Smørgrav 			pub = NULL;
3474f52dfbbSDag-Erling Smørgrav 		}
3484f52dfbbSDag-Erling Smørgrav 		r = 0;
3494f52dfbbSDag-Erling Smørgrav 	}
3504f52dfbbSDag-Erling Smørgrav  out:
3514f52dfbbSDag-Erling Smørgrav 	free(file);
352a0ee8cc6SDag-Erling Smørgrav 	sshkey_free(pub);
353a0ee8cc6SDag-Erling Smørgrav 	return r;
354c2d3a559SKris Kennaway }
355b15c8340SDag-Erling Smørgrav 
356e2f6069cSDag-Erling Smørgrav /* Load the certificate associated with the named private key */
357a0ee8cc6SDag-Erling Smørgrav int
358a0ee8cc6SDag-Erling Smørgrav sshkey_load_cert(const char *filename, struct sshkey **keyp)
359e2f6069cSDag-Erling Smørgrav {
360a0ee8cc6SDag-Erling Smørgrav 	struct sshkey *pub = NULL;
361a0ee8cc6SDag-Erling Smørgrav 	char *file = NULL;
362a0ee8cc6SDag-Erling Smørgrav 	int r = SSH_ERR_INTERNAL_ERROR;
363e2f6069cSDag-Erling Smørgrav 
364076ad2f8SDag-Erling Smørgrav 	if (keyp != NULL)
365a0ee8cc6SDag-Erling Smørgrav 		*keyp = NULL;
366a0ee8cc6SDag-Erling Smørgrav 
367a0ee8cc6SDag-Erling Smørgrav 	if (asprintf(&file, "%s-cert.pub", filename) == -1)
368a0ee8cc6SDag-Erling Smørgrav 		return SSH_ERR_ALLOC_FAIL;
369a0ee8cc6SDag-Erling Smørgrav 
370a0ee8cc6SDag-Erling Smørgrav 	if ((pub = sshkey_new(KEY_UNSPEC)) == NULL) {
371a0ee8cc6SDag-Erling Smørgrav 		goto out;
372e2f6069cSDag-Erling Smørgrav 	}
373a0ee8cc6SDag-Erling Smørgrav 	if ((r = sshkey_try_load_public(pub, file, NULL)) != 0)
374a0ee8cc6SDag-Erling Smørgrav 		goto out;
375076ad2f8SDag-Erling Smørgrav 	/* success */
376076ad2f8SDag-Erling Smørgrav 	if (keyp != NULL) {
377a0ee8cc6SDag-Erling Smørgrav 		*keyp = pub;
378a0ee8cc6SDag-Erling Smørgrav 		pub = NULL;
379076ad2f8SDag-Erling Smørgrav 	}
380a0ee8cc6SDag-Erling Smørgrav 	r = 0;
381a0ee8cc6SDag-Erling Smørgrav  out:
382e4a9863fSDag-Erling Smørgrav 	free(file);
383a0ee8cc6SDag-Erling Smørgrav 	sshkey_free(pub);
384a0ee8cc6SDag-Erling Smørgrav 	return r;
385e2f6069cSDag-Erling Smørgrav }
386e2f6069cSDag-Erling Smørgrav 
387e2f6069cSDag-Erling Smørgrav /* Load private key and certificate */
388a0ee8cc6SDag-Erling Smørgrav int
389a0ee8cc6SDag-Erling Smørgrav sshkey_load_private_cert(int type, const char *filename, const char *passphrase,
390a0ee8cc6SDag-Erling Smørgrav     struct sshkey **keyp, int *perm_ok)
391e2f6069cSDag-Erling Smørgrav {
392a0ee8cc6SDag-Erling Smørgrav 	struct sshkey *key = NULL, *cert = NULL;
393a0ee8cc6SDag-Erling Smørgrav 	int r;
394a0ee8cc6SDag-Erling Smørgrav 
395076ad2f8SDag-Erling Smørgrav 	if (keyp != NULL)
396a0ee8cc6SDag-Erling Smørgrav 		*keyp = NULL;
397e2f6069cSDag-Erling Smørgrav 
398e2f6069cSDag-Erling Smørgrav 	switch (type) {
399a0ee8cc6SDag-Erling Smørgrav #ifdef WITH_OPENSSL
400e2f6069cSDag-Erling Smørgrav 	case KEY_RSA:
401e2f6069cSDag-Erling Smørgrav 	case KEY_DSA:
4024a421b63SDag-Erling Smørgrav 	case KEY_ECDSA:
403a0ee8cc6SDag-Erling Smørgrav #endif /* WITH_OPENSSL */
404eccfee6eSDag-Erling Smørgrav 	case KEY_ED25519:
40547dd1d1bSDag-Erling Smørgrav 	case KEY_XMSS:
406a0ee8cc6SDag-Erling Smørgrav 	case KEY_UNSPEC:
407e2f6069cSDag-Erling Smørgrav 		break;
408e2f6069cSDag-Erling Smørgrav 	default:
409a0ee8cc6SDag-Erling Smørgrav 		return SSH_ERR_KEY_TYPE_UNKNOWN;
410e2f6069cSDag-Erling Smørgrav 	}
411e2f6069cSDag-Erling Smørgrav 
412a0ee8cc6SDag-Erling Smørgrav 	if ((r = sshkey_load_private_type(type, filename,
413a0ee8cc6SDag-Erling Smørgrav 	    passphrase, &key, NULL, perm_ok)) != 0 ||
414a0ee8cc6SDag-Erling Smørgrav 	    (r = sshkey_load_cert(filename, &cert)) != 0)
415a0ee8cc6SDag-Erling Smørgrav 		goto out;
416e2f6069cSDag-Erling Smørgrav 
417e2f6069cSDag-Erling Smørgrav 	/* Make sure the private key matches the certificate */
418a0ee8cc6SDag-Erling Smørgrav 	if (sshkey_equal_public(key, cert) == 0) {
419a0ee8cc6SDag-Erling Smørgrav 		r = SSH_ERR_KEY_CERT_MISMATCH;
420a0ee8cc6SDag-Erling Smørgrav 		goto out;
421e2f6069cSDag-Erling Smørgrav 	}
422e2f6069cSDag-Erling Smørgrav 
423eccfee6eSDag-Erling Smørgrav 	if ((r = sshkey_to_certified(key)) != 0 ||
424a0ee8cc6SDag-Erling Smørgrav 	    (r = sshkey_cert_copy(cert, key)) != 0)
425a0ee8cc6SDag-Erling Smørgrav 		goto out;
426a0ee8cc6SDag-Erling Smørgrav 	r = 0;
427076ad2f8SDag-Erling Smørgrav 	if (keyp != NULL) {
428a0ee8cc6SDag-Erling Smørgrav 		*keyp = key;
429a0ee8cc6SDag-Erling Smørgrav 		key = NULL;
430076ad2f8SDag-Erling Smørgrav 	}
431a0ee8cc6SDag-Erling Smørgrav  out:
432a0ee8cc6SDag-Erling Smørgrav 	sshkey_free(key);
433a0ee8cc6SDag-Erling Smørgrav 	sshkey_free(cert);
434a0ee8cc6SDag-Erling Smørgrav 	return r;
435e2f6069cSDag-Erling Smørgrav }
436e2f6069cSDag-Erling Smørgrav 
437b15c8340SDag-Erling Smørgrav /*
438a0ee8cc6SDag-Erling Smørgrav  * Returns success if the specified "key" is listed in the file "filename",
439a0ee8cc6SDag-Erling Smørgrav  * SSH_ERR_KEY_NOT_FOUND: if the key is not listed or another error.
440bc5531deSDag-Erling Smørgrav  * If "strict_type" is set then the key type must match exactly,
441b15c8340SDag-Erling Smørgrav  * otherwise a comparison that ignores certficiate data is performed.
442bc5531deSDag-Erling Smørgrav  * If "check_ca" is set and "key" is a certificate, then its CA key is
443bc5531deSDag-Erling Smørgrav  * also checked and sshkey_in_file() will return success if either is found.
444b15c8340SDag-Erling Smørgrav  */
445b15c8340SDag-Erling Smørgrav int
446bc5531deSDag-Erling Smørgrav sshkey_in_file(struct sshkey *key, const char *filename, int strict_type,
447bc5531deSDag-Erling Smørgrav     int check_ca)
448b15c8340SDag-Erling Smørgrav {
449b15c8340SDag-Erling Smørgrav 	FILE *f;
450190cef3dSDag-Erling Smørgrav 	char *line = NULL, *cp;
451190cef3dSDag-Erling Smørgrav 	size_t linesize = 0;
452a0ee8cc6SDag-Erling Smørgrav 	int r = 0;
453a0ee8cc6SDag-Erling Smørgrav 	struct sshkey *pub = NULL;
454190cef3dSDag-Erling Smørgrav 
455a0ee8cc6SDag-Erling Smørgrav 	int (*sshkey_compare)(const struct sshkey *, const struct sshkey *) =
456a0ee8cc6SDag-Erling Smørgrav 	    strict_type ?  sshkey_equal : sshkey_equal_public;
457b15c8340SDag-Erling Smørgrav 
458bc5531deSDag-Erling Smørgrav 	if ((f = fopen(filename, "r")) == NULL)
459a0ee8cc6SDag-Erling Smørgrav 		return SSH_ERR_SYSTEM_ERROR;
460b15c8340SDag-Erling Smørgrav 
461190cef3dSDag-Erling Smørgrav 	while (getline(&line, &linesize, f) != -1) {
462*2f513db7SEd Maste 		sshkey_free(pub);
463*2f513db7SEd Maste 		pub = NULL;
464b15c8340SDag-Erling Smørgrav 		cp = line;
465b15c8340SDag-Erling Smørgrav 
466b15c8340SDag-Erling Smørgrav 		/* Skip leading whitespace. */
467b15c8340SDag-Erling Smørgrav 		for (; *cp && (*cp == ' ' || *cp == '\t'); cp++)
468b15c8340SDag-Erling Smørgrav 			;
469b15c8340SDag-Erling Smørgrav 
470b15c8340SDag-Erling Smørgrav 		/* Skip comments and empty lines */
471b15c8340SDag-Erling Smørgrav 		switch (*cp) {
472b15c8340SDag-Erling Smørgrav 		case '#':
473b15c8340SDag-Erling Smørgrav 		case '\n':
474b15c8340SDag-Erling Smørgrav 		case '\0':
475b15c8340SDag-Erling Smørgrav 			continue;
476b15c8340SDag-Erling Smørgrav 		}
477b15c8340SDag-Erling Smørgrav 
478a0ee8cc6SDag-Erling Smørgrav 		if ((pub = sshkey_new(KEY_UNSPEC)) == NULL) {
479a0ee8cc6SDag-Erling Smørgrav 			r = SSH_ERR_ALLOC_FAIL;
480a0ee8cc6SDag-Erling Smørgrav 			goto out;
481b15c8340SDag-Erling Smørgrav 		}
482*2f513db7SEd Maste 		switch (r = sshkey_read(pub, &cp)) {
483*2f513db7SEd Maste 		case 0:
484*2f513db7SEd Maste 			break;
485*2f513db7SEd Maste 		case SSH_ERR_KEY_LENGTH:
486*2f513db7SEd Maste 			continue;
487*2f513db7SEd Maste 		default:
488a0ee8cc6SDag-Erling Smørgrav 			goto out;
489*2f513db7SEd Maste 		}
490bc5531deSDag-Erling Smørgrav 		if (sshkey_compare(key, pub) ||
491bc5531deSDag-Erling Smørgrav 		    (check_ca && sshkey_is_cert(key) &&
492bc5531deSDag-Erling Smørgrav 		    sshkey_compare(key->cert->signature_key, pub))) {
493a0ee8cc6SDag-Erling Smørgrav 			r = 0;
494a0ee8cc6SDag-Erling Smørgrav 			goto out;
495b15c8340SDag-Erling Smørgrav 		}
496b15c8340SDag-Erling Smørgrav 	}
497a0ee8cc6SDag-Erling Smørgrav 	r = SSH_ERR_KEY_NOT_FOUND;
498a0ee8cc6SDag-Erling Smørgrav  out:
499190cef3dSDag-Erling Smørgrav 	free(line);
500a0ee8cc6SDag-Erling Smørgrav 	sshkey_free(pub);
501b15c8340SDag-Erling Smørgrav 	fclose(f);
502a0ee8cc6SDag-Erling Smørgrav 	return r;
503b15c8340SDag-Erling Smørgrav }
504a0ee8cc6SDag-Erling Smørgrav 
505bc5531deSDag-Erling Smørgrav /*
506bc5531deSDag-Erling Smørgrav  * Checks whether the specified key is revoked, returning 0 if not,
507bc5531deSDag-Erling Smørgrav  * SSH_ERR_KEY_REVOKED if it is or another error code if something
508bc5531deSDag-Erling Smørgrav  * unexpected happened.
509bc5531deSDag-Erling Smørgrav  * This will check both the key and, if it is a certificate, its CA key too.
510bc5531deSDag-Erling Smørgrav  * "revoked_keys_file" may be a KRL or a one-per-line list of public keys.
511bc5531deSDag-Erling Smørgrav  */
512bc5531deSDag-Erling Smørgrav int
513bc5531deSDag-Erling Smørgrav sshkey_check_revoked(struct sshkey *key, const char *revoked_keys_file)
514bc5531deSDag-Erling Smørgrav {
515bc5531deSDag-Erling Smørgrav 	int r;
516bc5531deSDag-Erling Smørgrav 
517bc5531deSDag-Erling Smørgrav 	r = ssh_krl_file_contains_key(revoked_keys_file, key);
518bc5531deSDag-Erling Smørgrav 	/* If this was not a KRL to begin with then continue below */
519bc5531deSDag-Erling Smørgrav 	if (r != SSH_ERR_KRL_BAD_MAGIC)
520bc5531deSDag-Erling Smørgrav 		return r;
521bc5531deSDag-Erling Smørgrav 
522bc5531deSDag-Erling Smørgrav 	/*
523bc5531deSDag-Erling Smørgrav 	 * If the file is not a KRL or we can't handle KRLs then attempt to
524bc5531deSDag-Erling Smørgrav 	 * parse the file as a flat list of keys.
525bc5531deSDag-Erling Smørgrav 	 */
526bc5531deSDag-Erling Smørgrav 	switch ((r = sshkey_in_file(key, revoked_keys_file, 0, 1))) {
527bc5531deSDag-Erling Smørgrav 	case 0:
528bc5531deSDag-Erling Smørgrav 		/* Key found => revoked */
529bc5531deSDag-Erling Smørgrav 		return SSH_ERR_KEY_REVOKED;
530bc5531deSDag-Erling Smørgrav 	case SSH_ERR_KEY_NOT_FOUND:
531bc5531deSDag-Erling Smørgrav 		/* Key not found => not revoked */
532bc5531deSDag-Erling Smørgrav 		return 0;
533bc5531deSDag-Erling Smørgrav 	default:
534bc5531deSDag-Erling Smørgrav 		/* Some other error occurred */
535bc5531deSDag-Erling Smørgrav 		return r;
536bc5531deSDag-Erling Smørgrav 	}
537bc5531deSDag-Erling Smørgrav }
538bc5531deSDag-Erling Smørgrav 
539