xref: /freebsd/crypto/openssh/authfile.c (revision 4d3fc8b0570b29fb0d6ee9525f104d52176ff0d4)
1*4d3fc8b0SEd Maste /* $OpenBSD: authfile.c,v 1.144 2023/03/14 07:26:25 dtucker 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
sshkey_save_private_blob(struct sshbuf * keybuf,const char * filename)56a0ee8cc6SDag-Erling Smørgrav sshkey_save_private_blob(struct sshbuf *keybuf, const char *filename)
574a421b63SDag-Erling Smørgrav {
5819261079SEd Maste 	int r;
5919261079SEd Maste 	mode_t omask;
604a421b63SDag-Erling Smørgrav 
6119261079SEd Maste 	omask = umask(077);
6219261079SEd Maste 	r = sshbuf_write_file(filename, keybuf);
6319261079SEd Maste 	umask(omask);
6419261079SEd Maste 	return r;
654a421b63SDag-Erling Smørgrav }
664a421b63SDag-Erling Smørgrav 
67e8aafc91SKris Kennaway int
sshkey_save_private(struct sshkey * key,const char * filename,const char * passphrase,const char * comment,int format,const char * openssh_format_cipher,int openssh_format_rounds)68a0ee8cc6SDag-Erling Smørgrav sshkey_save_private(struct sshkey *key, const char *filename,
69a0ee8cc6SDag-Erling Smørgrav     const char *passphrase, const char *comment,
7019261079SEd Maste     int format, const char *openssh_format_cipher, int openssh_format_rounds)
71e8aafc91SKris Kennaway {
72a0ee8cc6SDag-Erling Smørgrav 	struct sshbuf *keyblob = NULL;
73a0ee8cc6SDag-Erling Smørgrav 	int r;
744a421b63SDag-Erling Smørgrav 
75a0ee8cc6SDag-Erling Smørgrav 	if ((keyblob = sshbuf_new()) == NULL)
76a0ee8cc6SDag-Erling Smørgrav 		return SSH_ERR_ALLOC_FAIL;
77a0ee8cc6SDag-Erling Smørgrav 	if ((r = sshkey_private_to_fileblob(key, keyblob, passphrase, comment,
7819261079SEd Maste 	    format, openssh_format_cipher, openssh_format_rounds)) != 0)
794a421b63SDag-Erling Smørgrav 		goto out;
80a0ee8cc6SDag-Erling Smørgrav 	if ((r = sshkey_save_private_blob(keyblob, filename)) != 0)
814a421b63SDag-Erling Smørgrav 		goto out;
82a0ee8cc6SDag-Erling Smørgrav 	r = 0;
834a421b63SDag-Erling Smørgrav  out:
84a0ee8cc6SDag-Erling Smørgrav 	sshbuf_free(keyblob);
85a0ee8cc6SDag-Erling Smørgrav 	return r;
864a421b63SDag-Erling Smørgrav }
874a421b63SDag-Erling Smørgrav 
88a0ee8cc6SDag-Erling Smørgrav /* XXX remove error() calls from here? */
89a0ee8cc6SDag-Erling Smørgrav int
sshkey_perm_ok(int fd,const char * filename)90a0ee8cc6SDag-Erling Smørgrav sshkey_perm_ok(int fd, const char *filename)
91e8aafc91SKris Kennaway {
92e8aafc91SKris Kennaway 	struct stat st;
93e8aafc91SKris Kennaway 
9419261079SEd Maste 	if (fstat(fd, &st) == -1)
95a0ee8cc6SDag-Erling Smørgrav 		return SSH_ERR_SYSTEM_ERROR;
96af12a3e7SDag-Erling Smørgrav 	/*
97af12a3e7SDag-Erling Smørgrav 	 * if a key owned by the user is accessed, then we check the
98af12a3e7SDag-Erling Smørgrav 	 * permissions of the file. if the key owned by a different user,
99af12a3e7SDag-Erling Smørgrav 	 * then we don't care.
100af12a3e7SDag-Erling Smørgrav 	 */
101989dd127SDag-Erling Smørgrav #ifdef HAVE_CYGWIN
102989dd127SDag-Erling Smørgrav 	if (check_ntsec(filename))
103989dd127SDag-Erling Smørgrav #endif
104af12a3e7SDag-Erling Smørgrav 	if ((st.st_uid == getuid()) && (st.st_mode & 077) != 0) {
105e8aafc91SKris Kennaway 		error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@");
106e8aafc91SKris Kennaway 		error("@         WARNING: UNPROTECTED PRIVATE KEY FILE!          @");
107e8aafc91SKris Kennaway 		error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@");
108af12a3e7SDag-Erling Smørgrav 		error("Permissions 0%3.3o for '%s' are too open.",
109cf2b5f3bSDag-Erling Smørgrav 		    (u_int)st.st_mode & 0777, filename);
110557f75e5SDag-Erling Smørgrav 		error("It is required that your private key files are NOT accessible by others.");
111ca3176e7SBrian Feldman 		error("This private key will be ignored.");
112a0ee8cc6SDag-Erling Smørgrav 		return SSH_ERR_KEY_BAD_PERMISSIONS;
113a0ee8cc6SDag-Erling Smørgrav 	}
114e8aafc91SKris Kennaway 	return 0;
115e8aafc91SKris Kennaway }
116ca3176e7SBrian Feldman 
117a0ee8cc6SDag-Erling Smørgrav int
sshkey_load_private_type(int type,const char * filename,const char * passphrase,struct sshkey ** keyp,char ** commentp)118a0ee8cc6SDag-Erling Smørgrav sshkey_load_private_type(int type, const char *filename, const char *passphrase,
11919261079SEd Maste     struct sshkey **keyp, char **commentp)
1204a421b63SDag-Erling Smørgrav {
121a0ee8cc6SDag-Erling Smørgrav 	int fd, r;
122f7167e0eSDag-Erling Smørgrav 
123076ad2f8SDag-Erling Smørgrav 	if (keyp != NULL)
124a0ee8cc6SDag-Erling Smørgrav 		*keyp = NULL;
125a0ee8cc6SDag-Erling Smørgrav 	if (commentp != NULL)
126a0ee8cc6SDag-Erling Smørgrav 		*commentp = NULL;
1274a421b63SDag-Erling Smørgrav 
12819261079SEd Maste 	if ((fd = open(filename, O_RDONLY)) == -1)
129a0ee8cc6SDag-Erling Smørgrav 		return SSH_ERR_SYSTEM_ERROR;
13019261079SEd Maste 
13119261079SEd Maste 	r = sshkey_perm_ok(fd, filename);
13219261079SEd Maste 	if (r != 0)
133a0ee8cc6SDag-Erling Smørgrav 		goto out;
1344a421b63SDag-Erling Smørgrav 
135bc5531deSDag-Erling Smørgrav 	r = sshkey_load_private_type_fd(fd, type, passphrase, keyp, commentp);
13647dd1d1bSDag-Erling Smørgrav 	if (r == 0 && keyp && *keyp)
13747dd1d1bSDag-Erling Smørgrav 		r = sshkey_set_filename(*keyp, filename);
138bc5531deSDag-Erling Smørgrav  out:
139bc5531deSDag-Erling Smørgrav 	close(fd);
140bc5531deSDag-Erling Smørgrav 	return r;
141bc5531deSDag-Erling Smørgrav }
142bc5531deSDag-Erling Smørgrav 
143bc5531deSDag-Erling Smørgrav int
sshkey_load_private(const char * filename,const char * passphrase,struct sshkey ** keyp,char ** commentp)14419261079SEd Maste sshkey_load_private(const char *filename, const char *passphrase,
14519261079SEd Maste     struct sshkey **keyp, char **commentp)
14619261079SEd Maste {
14719261079SEd Maste 	return sshkey_load_private_type(KEY_UNSPEC, filename, passphrase,
14819261079SEd Maste 	    keyp, commentp);
14919261079SEd Maste }
15019261079SEd Maste 
15119261079SEd Maste int
sshkey_load_private_type_fd(int fd,int type,const char * passphrase,struct sshkey ** keyp,char ** commentp)152bc5531deSDag-Erling Smørgrav sshkey_load_private_type_fd(int fd, int type, const char *passphrase,
153bc5531deSDag-Erling Smørgrav     struct sshkey **keyp, char **commentp)
154bc5531deSDag-Erling Smørgrav {
155bc5531deSDag-Erling Smørgrav 	struct sshbuf *buffer = NULL;
156bc5531deSDag-Erling Smørgrav 	int r;
157bc5531deSDag-Erling Smørgrav 
158076ad2f8SDag-Erling Smørgrav 	if (keyp != NULL)
159076ad2f8SDag-Erling Smørgrav 		*keyp = NULL;
16019261079SEd Maste 	if ((r = sshbuf_load_fd(fd, &buffer)) != 0 ||
161bc5531deSDag-Erling Smørgrav 	    (r = sshkey_parse_private_fileblob_type(buffer, type,
162bc5531deSDag-Erling Smørgrav 	    passphrase, keyp, commentp)) != 0)
163a0ee8cc6SDag-Erling Smørgrav 		goto out;
164bc5531deSDag-Erling Smørgrav 
165bc5531deSDag-Erling Smørgrav 	/* success */
166a0ee8cc6SDag-Erling Smørgrav 	r = 0;
167a0ee8cc6SDag-Erling Smørgrav  out:
168a0ee8cc6SDag-Erling Smørgrav 	sshbuf_free(buffer);
169a0ee8cc6SDag-Erling Smørgrav 	return r;
1704a421b63SDag-Erling Smørgrav }
171ca3176e7SBrian Feldman 
17219261079SEd Maste /* Load a pubkey from the unencrypted envelope of a new-format private key */
17319261079SEd Maste static int
sshkey_load_pubkey_from_private(const char * filename,struct sshkey ** pubkeyp)17419261079SEd Maste sshkey_load_pubkey_from_private(const char *filename, struct sshkey **pubkeyp)
175e146993eSDag-Erling Smørgrav {
176a0ee8cc6SDag-Erling Smørgrav 	struct sshbuf *buffer = NULL;
17719261079SEd Maste 	struct sshkey *pubkey = NULL;
178a0ee8cc6SDag-Erling Smørgrav 	int r, fd;
179e146993eSDag-Erling Smørgrav 
18019261079SEd Maste 	if (pubkeyp != NULL)
18119261079SEd Maste 		*pubkeyp = NULL;
182a0ee8cc6SDag-Erling Smørgrav 
18319261079SEd Maste 	if ((fd = open(filename, O_RDONLY)) == -1)
184a0ee8cc6SDag-Erling Smørgrav 		return SSH_ERR_SYSTEM_ERROR;
18519261079SEd Maste 	if ((r = sshbuf_load_fd(fd, &buffer)) != 0 ||
18619261079SEd Maste 	    (r = sshkey_parse_pubkey_from_private_fileblob_type(buffer,
18719261079SEd Maste 	    KEY_UNSPEC, &pubkey)) != 0)
188a0ee8cc6SDag-Erling Smørgrav 		goto out;
18919261079SEd Maste 	if ((r = sshkey_set_filename(pubkey, filename)) != 0)
19019261079SEd Maste 		goto out;
19119261079SEd Maste 	/* success */
19219261079SEd Maste 	if (pubkeyp != NULL) {
19319261079SEd Maste 		*pubkeyp = pubkey;
19419261079SEd Maste 		pubkey = NULL;
195e146993eSDag-Erling Smørgrav 	}
196a0ee8cc6SDag-Erling Smørgrav 	r = 0;
197a0ee8cc6SDag-Erling Smørgrav  out:
198e8aafc91SKris Kennaway 	close(fd);
199a0ee8cc6SDag-Erling Smørgrav 	sshbuf_free(buffer);
20019261079SEd Maste 	sshkey_free(pubkey);
201a0ee8cc6SDag-Erling Smørgrav 	return r;
202e8aafc91SKris Kennaway }
203c2d3a559SKris Kennaway 
204af12a3e7SDag-Erling Smørgrav static int
sshkey_try_load_public(struct sshkey ** kp,const char * filename,char ** commentp)20519261079SEd Maste sshkey_try_load_public(struct sshkey **kp, const char *filename,
20619261079SEd Maste     char **commentp)
207c2d3a559SKris Kennaway {
208c2d3a559SKris Kennaway 	FILE *f;
209190cef3dSDag-Erling Smørgrav 	char *line = NULL, *cp;
210190cef3dSDag-Erling Smørgrav 	size_t linesize = 0;
211a0ee8cc6SDag-Erling Smørgrav 	int r;
21219261079SEd Maste 	struct sshkey *k = NULL;
213c2d3a559SKris Kennaway 
214*4d3fc8b0SEd Maste 	if (kp == NULL)
215*4d3fc8b0SEd Maste 		return SSH_ERR_INVALID_ARGUMENT;
21619261079SEd Maste 	*kp = NULL;
217a0ee8cc6SDag-Erling Smørgrav 	if (commentp != NULL)
218a0ee8cc6SDag-Erling Smørgrav 		*commentp = NULL;
219a0ee8cc6SDag-Erling Smørgrav 	if ((f = fopen(filename, "r")) == NULL)
220a0ee8cc6SDag-Erling Smørgrav 		return SSH_ERR_SYSTEM_ERROR;
22119261079SEd Maste 	if ((k = sshkey_new(KEY_UNSPEC)) == NULL) {
22219261079SEd Maste 		fclose(f);
22319261079SEd Maste 		return SSH_ERR_ALLOC_FAIL;
22419261079SEd Maste 	}
225190cef3dSDag-Erling Smørgrav 	while (getline(&line, &linesize, f) != -1) {
226c2d3a559SKris Kennaway 		cp = line;
227c2d3a559SKris Kennaway 		switch (*cp) {
228c2d3a559SKris Kennaway 		case '#':
229c2d3a559SKris Kennaway 		case '\n':
230c2d3a559SKris Kennaway 		case '\0':
231c2d3a559SKris Kennaway 			continue;
232c2d3a559SKris Kennaway 		}
233e146993eSDag-Erling Smørgrav 		/* Abort loading if this looks like a private key */
234a0ee8cc6SDag-Erling Smørgrav 		if (strncmp(cp, "-----BEGIN", 10) == 0 ||
235a0ee8cc6SDag-Erling Smørgrav 		    strcmp(cp, "SSH PRIVATE KEY FILE") == 0)
236e146993eSDag-Erling Smørgrav 			break;
237c2d3a559SKris Kennaway 		/* Skip leading whitespace. */
238c2d3a559SKris Kennaway 		for (; *cp && (*cp == ' ' || *cp == '\t'); cp++)
239c2d3a559SKris Kennaway 			;
240c2d3a559SKris Kennaway 		if (*cp) {
241a0ee8cc6SDag-Erling Smørgrav 			if ((r = sshkey_read(k, &cp)) == 0) {
242e146993eSDag-Erling Smørgrav 				cp[strcspn(cp, "\r\n")] = '\0';
243e146993eSDag-Erling Smørgrav 				if (commentp) {
244a0ee8cc6SDag-Erling Smørgrav 					*commentp = strdup(*cp ?
245e146993eSDag-Erling Smørgrav 					    cp : filename);
246a0ee8cc6SDag-Erling Smørgrav 					if (*commentp == NULL)
247a0ee8cc6SDag-Erling Smørgrav 						r = SSH_ERR_ALLOC_FAIL;
248e146993eSDag-Erling Smørgrav 				}
24919261079SEd Maste 				/* success */
25019261079SEd Maste 				*kp = k;
251190cef3dSDag-Erling Smørgrav 				free(line);
252c2d3a559SKris Kennaway 				fclose(f);
253a0ee8cc6SDag-Erling Smørgrav 				return r;
254c2d3a559SKris Kennaway 			}
255c2d3a559SKris Kennaway 		}
256c2d3a559SKris Kennaway 	}
25719261079SEd Maste 	free(k);
258190cef3dSDag-Erling Smørgrav 	free(line);
259c2d3a559SKris Kennaway 	fclose(f);
260a0ee8cc6SDag-Erling Smørgrav 	return SSH_ERR_INVALID_FORMAT;
261c2d3a559SKris Kennaway }
262c2d3a559SKris Kennaway 
2634f52dfbbSDag-Erling Smørgrav /* load public key from any pubkey file */
264a0ee8cc6SDag-Erling Smørgrav int
sshkey_load_public(const char * filename,struct sshkey ** keyp,char ** commentp)265a0ee8cc6SDag-Erling Smørgrav sshkey_load_public(const char *filename, struct sshkey **keyp, char **commentp)
266c2d3a559SKris Kennaway {
26719261079SEd Maste 	char *pubfile = NULL;
26819261079SEd Maste 	int r, oerrno;
269c2d3a559SKris Kennaway 
270a0ee8cc6SDag-Erling Smørgrav 	if (keyp != NULL)
271a0ee8cc6SDag-Erling Smørgrav 		*keyp = NULL;
272a0ee8cc6SDag-Erling Smørgrav 	if (commentp != NULL)
273a0ee8cc6SDag-Erling Smørgrav 		*commentp = NULL;
274a0ee8cc6SDag-Erling Smørgrav 
27519261079SEd Maste 	if ((r = sshkey_try_load_public(keyp, filename, commentp)) == 0)
2764f52dfbbSDag-Erling Smørgrav 		goto out;
277a0ee8cc6SDag-Erling Smørgrav 
278a0ee8cc6SDag-Erling Smørgrav 	/* try .pub suffix */
27919261079SEd Maste 	if (asprintf(&pubfile, "%s.pub", filename) == -1)
280a0ee8cc6SDag-Erling Smørgrav 		return SSH_ERR_ALLOC_FAIL;
28119261079SEd Maste 	if ((r = sshkey_try_load_public(keyp, pubfile, commentp)) == 0)
2824f52dfbbSDag-Erling Smørgrav 		goto out;
28319261079SEd Maste 
28419261079SEd Maste 	/* finally, try to extract public key from private key file */
28519261079SEd Maste 	if ((r = sshkey_load_pubkey_from_private(filename, keyp)) == 0)
28619261079SEd Maste 		goto out;
28719261079SEd Maste 
28819261079SEd Maste 	/* Pretend we couldn't find the key */
28919261079SEd Maste 	r = SSH_ERR_SYSTEM_ERROR;
29019261079SEd Maste 	errno = ENOENT;
29119261079SEd Maste 
2924f52dfbbSDag-Erling Smørgrav  out:
29319261079SEd Maste 	oerrno = errno;
29419261079SEd Maste 	free(pubfile);
29519261079SEd Maste 	errno = oerrno;
296a0ee8cc6SDag-Erling Smørgrav 	return r;
297c2d3a559SKris Kennaway }
298b15c8340SDag-Erling Smørgrav 
299e2f6069cSDag-Erling Smørgrav /* Load the certificate associated with the named private key */
300a0ee8cc6SDag-Erling Smørgrav int
sshkey_load_cert(const char * filename,struct sshkey ** keyp)301a0ee8cc6SDag-Erling Smørgrav sshkey_load_cert(const char *filename, struct sshkey **keyp)
302e2f6069cSDag-Erling Smørgrav {
303a0ee8cc6SDag-Erling Smørgrav 	struct sshkey *pub = NULL;
304a0ee8cc6SDag-Erling Smørgrav 	char *file = NULL;
305a0ee8cc6SDag-Erling Smørgrav 	int r = SSH_ERR_INTERNAL_ERROR;
306e2f6069cSDag-Erling Smørgrav 
307076ad2f8SDag-Erling Smørgrav 	if (keyp != NULL)
308a0ee8cc6SDag-Erling Smørgrav 		*keyp = NULL;
309a0ee8cc6SDag-Erling Smørgrav 
310a0ee8cc6SDag-Erling Smørgrav 	if (asprintf(&file, "%s-cert.pub", filename) == -1)
311a0ee8cc6SDag-Erling Smørgrav 		return SSH_ERR_ALLOC_FAIL;
312a0ee8cc6SDag-Erling Smørgrav 
31319261079SEd Maste 	r = sshkey_try_load_public(keyp, file, NULL);
314e4a9863fSDag-Erling Smørgrav 	free(file);
315a0ee8cc6SDag-Erling Smørgrav 	sshkey_free(pub);
316a0ee8cc6SDag-Erling Smørgrav 	return r;
317e2f6069cSDag-Erling Smørgrav }
318e2f6069cSDag-Erling Smørgrav 
319e2f6069cSDag-Erling Smørgrav /* Load private key and certificate */
320a0ee8cc6SDag-Erling Smørgrav int
sshkey_load_private_cert(int type,const char * filename,const char * passphrase,struct sshkey ** keyp)321a0ee8cc6SDag-Erling Smørgrav sshkey_load_private_cert(int type, const char *filename, const char *passphrase,
32219261079SEd Maste     struct sshkey **keyp)
323e2f6069cSDag-Erling Smørgrav {
324a0ee8cc6SDag-Erling Smørgrav 	struct sshkey *key = NULL, *cert = NULL;
325a0ee8cc6SDag-Erling Smørgrav 	int r;
326a0ee8cc6SDag-Erling Smørgrav 
327076ad2f8SDag-Erling Smørgrav 	if (keyp != NULL)
328a0ee8cc6SDag-Erling Smørgrav 		*keyp = NULL;
329e2f6069cSDag-Erling Smørgrav 
330e2f6069cSDag-Erling Smørgrav 	switch (type) {
331a0ee8cc6SDag-Erling Smørgrav #ifdef WITH_OPENSSL
332e2f6069cSDag-Erling Smørgrav 	case KEY_RSA:
333e2f6069cSDag-Erling Smørgrav 	case KEY_DSA:
3344a421b63SDag-Erling Smørgrav 	case KEY_ECDSA:
335a0ee8cc6SDag-Erling Smørgrav #endif /* WITH_OPENSSL */
336eccfee6eSDag-Erling Smørgrav 	case KEY_ED25519:
33747dd1d1bSDag-Erling Smørgrav 	case KEY_XMSS:
338a0ee8cc6SDag-Erling Smørgrav 	case KEY_UNSPEC:
339e2f6069cSDag-Erling Smørgrav 		break;
340e2f6069cSDag-Erling Smørgrav 	default:
341a0ee8cc6SDag-Erling Smørgrav 		return SSH_ERR_KEY_TYPE_UNKNOWN;
342e2f6069cSDag-Erling Smørgrav 	}
343e2f6069cSDag-Erling Smørgrav 
344a0ee8cc6SDag-Erling Smørgrav 	if ((r = sshkey_load_private_type(type, filename,
34519261079SEd Maste 	    passphrase, &key, NULL)) != 0 ||
346a0ee8cc6SDag-Erling Smørgrav 	    (r = sshkey_load_cert(filename, &cert)) != 0)
347a0ee8cc6SDag-Erling Smørgrav 		goto out;
348e2f6069cSDag-Erling Smørgrav 
349e2f6069cSDag-Erling Smørgrav 	/* Make sure the private key matches the certificate */
350a0ee8cc6SDag-Erling Smørgrav 	if (sshkey_equal_public(key, cert) == 0) {
351a0ee8cc6SDag-Erling Smørgrav 		r = SSH_ERR_KEY_CERT_MISMATCH;
352a0ee8cc6SDag-Erling Smørgrav 		goto out;
353e2f6069cSDag-Erling Smørgrav 	}
354e2f6069cSDag-Erling Smørgrav 
355eccfee6eSDag-Erling Smørgrav 	if ((r = sshkey_to_certified(key)) != 0 ||
356a0ee8cc6SDag-Erling Smørgrav 	    (r = sshkey_cert_copy(cert, key)) != 0)
357a0ee8cc6SDag-Erling Smørgrav 		goto out;
358a0ee8cc6SDag-Erling Smørgrav 	r = 0;
359076ad2f8SDag-Erling Smørgrav 	if (keyp != NULL) {
360a0ee8cc6SDag-Erling Smørgrav 		*keyp = key;
361a0ee8cc6SDag-Erling Smørgrav 		key = NULL;
362076ad2f8SDag-Erling Smørgrav 	}
363a0ee8cc6SDag-Erling Smørgrav  out:
364a0ee8cc6SDag-Erling Smørgrav 	sshkey_free(key);
365a0ee8cc6SDag-Erling Smørgrav 	sshkey_free(cert);
366a0ee8cc6SDag-Erling Smørgrav 	return r;
367e2f6069cSDag-Erling Smørgrav }
368e2f6069cSDag-Erling Smørgrav 
369b15c8340SDag-Erling Smørgrav /*
370a0ee8cc6SDag-Erling Smørgrav  * Returns success if the specified "key" is listed in the file "filename",
371a0ee8cc6SDag-Erling Smørgrav  * SSH_ERR_KEY_NOT_FOUND: if the key is not listed or another error.
372bc5531deSDag-Erling Smørgrav  * If "strict_type" is set then the key type must match exactly,
3731323ec57SEd Maste  * otherwise a comparison that ignores certificate data is performed.
374bc5531deSDag-Erling Smørgrav  * If "check_ca" is set and "key" is a certificate, then its CA key is
375bc5531deSDag-Erling Smørgrav  * also checked and sshkey_in_file() will return success if either is found.
376b15c8340SDag-Erling Smørgrav  */
377b15c8340SDag-Erling Smørgrav int
sshkey_in_file(struct sshkey * key,const char * filename,int strict_type,int check_ca)378bc5531deSDag-Erling Smørgrav sshkey_in_file(struct sshkey *key, const char *filename, int strict_type,
379bc5531deSDag-Erling Smørgrav     int check_ca)
380b15c8340SDag-Erling Smørgrav {
381b15c8340SDag-Erling Smørgrav 	FILE *f;
382190cef3dSDag-Erling Smørgrav 	char *line = NULL, *cp;
383190cef3dSDag-Erling Smørgrav 	size_t linesize = 0;
384a0ee8cc6SDag-Erling Smørgrav 	int r = 0;
385a0ee8cc6SDag-Erling Smørgrav 	struct sshkey *pub = NULL;
386190cef3dSDag-Erling Smørgrav 
387a0ee8cc6SDag-Erling Smørgrav 	int (*sshkey_compare)(const struct sshkey *, const struct sshkey *) =
388a0ee8cc6SDag-Erling Smørgrav 	    strict_type ?  sshkey_equal : sshkey_equal_public;
389b15c8340SDag-Erling Smørgrav 
390bc5531deSDag-Erling Smørgrav 	if ((f = fopen(filename, "r")) == NULL)
391a0ee8cc6SDag-Erling Smørgrav 		return SSH_ERR_SYSTEM_ERROR;
392b15c8340SDag-Erling Smørgrav 
393190cef3dSDag-Erling Smørgrav 	while (getline(&line, &linesize, f) != -1) {
3942f513db7SEd Maste 		sshkey_free(pub);
3952f513db7SEd Maste 		pub = NULL;
396b15c8340SDag-Erling Smørgrav 		cp = line;
397b15c8340SDag-Erling Smørgrav 
398b15c8340SDag-Erling Smørgrav 		/* Skip leading whitespace. */
399b15c8340SDag-Erling Smørgrav 		for (; *cp && (*cp == ' ' || *cp == '\t'); cp++)
400b15c8340SDag-Erling Smørgrav 			;
401b15c8340SDag-Erling Smørgrav 
402b15c8340SDag-Erling Smørgrav 		/* Skip comments and empty lines */
403b15c8340SDag-Erling Smørgrav 		switch (*cp) {
404b15c8340SDag-Erling Smørgrav 		case '#':
405b15c8340SDag-Erling Smørgrav 		case '\n':
406b15c8340SDag-Erling Smørgrav 		case '\0':
407b15c8340SDag-Erling Smørgrav 			continue;
408b15c8340SDag-Erling Smørgrav 		}
409b15c8340SDag-Erling Smørgrav 
410a0ee8cc6SDag-Erling Smørgrav 		if ((pub = sshkey_new(KEY_UNSPEC)) == NULL) {
411a0ee8cc6SDag-Erling Smørgrav 			r = SSH_ERR_ALLOC_FAIL;
412a0ee8cc6SDag-Erling Smørgrav 			goto out;
413b15c8340SDag-Erling Smørgrav 		}
4142f513db7SEd Maste 		switch (r = sshkey_read(pub, &cp)) {
4152f513db7SEd Maste 		case 0:
4162f513db7SEd Maste 			break;
4172f513db7SEd Maste 		case SSH_ERR_KEY_LENGTH:
4182f513db7SEd Maste 			continue;
4192f513db7SEd Maste 		default:
420a0ee8cc6SDag-Erling Smørgrav 			goto out;
4212f513db7SEd Maste 		}
422bc5531deSDag-Erling Smørgrav 		if (sshkey_compare(key, pub) ||
423bc5531deSDag-Erling Smørgrav 		    (check_ca && sshkey_is_cert(key) &&
424bc5531deSDag-Erling Smørgrav 		    sshkey_compare(key->cert->signature_key, pub))) {
425a0ee8cc6SDag-Erling Smørgrav 			r = 0;
426a0ee8cc6SDag-Erling Smørgrav 			goto out;
427b15c8340SDag-Erling Smørgrav 		}
428b15c8340SDag-Erling Smørgrav 	}
429a0ee8cc6SDag-Erling Smørgrav 	r = SSH_ERR_KEY_NOT_FOUND;
430a0ee8cc6SDag-Erling Smørgrav  out:
431190cef3dSDag-Erling Smørgrav 	free(line);
432a0ee8cc6SDag-Erling Smørgrav 	sshkey_free(pub);
433b15c8340SDag-Erling Smørgrav 	fclose(f);
434a0ee8cc6SDag-Erling Smørgrav 	return r;
435b15c8340SDag-Erling Smørgrav }
436a0ee8cc6SDag-Erling Smørgrav 
437bc5531deSDag-Erling Smørgrav /*
438bc5531deSDag-Erling Smørgrav  * Checks whether the specified key is revoked, returning 0 if not,
439bc5531deSDag-Erling Smørgrav  * SSH_ERR_KEY_REVOKED if it is or another error code if something
440bc5531deSDag-Erling Smørgrav  * unexpected happened.
441bc5531deSDag-Erling Smørgrav  * This will check both the key and, if it is a certificate, its CA key too.
442bc5531deSDag-Erling Smørgrav  * "revoked_keys_file" may be a KRL or a one-per-line list of public keys.
443bc5531deSDag-Erling Smørgrav  */
444bc5531deSDag-Erling Smørgrav int
sshkey_check_revoked(struct sshkey * key,const char * revoked_keys_file)445bc5531deSDag-Erling Smørgrav sshkey_check_revoked(struct sshkey *key, const char *revoked_keys_file)
446bc5531deSDag-Erling Smørgrav {
447bc5531deSDag-Erling Smørgrav 	int r;
448bc5531deSDag-Erling Smørgrav 
449bc5531deSDag-Erling Smørgrav 	r = ssh_krl_file_contains_key(revoked_keys_file, key);
450bc5531deSDag-Erling Smørgrav 	/* If this was not a KRL to begin with then continue below */
451bc5531deSDag-Erling Smørgrav 	if (r != SSH_ERR_KRL_BAD_MAGIC)
452bc5531deSDag-Erling Smørgrav 		return r;
453bc5531deSDag-Erling Smørgrav 
454bc5531deSDag-Erling Smørgrav 	/*
455bc5531deSDag-Erling Smørgrav 	 * If the file is not a KRL or we can't handle KRLs then attempt to
456bc5531deSDag-Erling Smørgrav 	 * parse the file as a flat list of keys.
457bc5531deSDag-Erling Smørgrav 	 */
458bc5531deSDag-Erling Smørgrav 	switch ((r = sshkey_in_file(key, revoked_keys_file, 0, 1))) {
459bc5531deSDag-Erling Smørgrav 	case 0:
460bc5531deSDag-Erling Smørgrav 		/* Key found => revoked */
461bc5531deSDag-Erling Smørgrav 		return SSH_ERR_KEY_REVOKED;
462bc5531deSDag-Erling Smørgrav 	case SSH_ERR_KEY_NOT_FOUND:
463bc5531deSDag-Erling Smørgrav 		/* Key not found => not revoked */
464bc5531deSDag-Erling Smørgrav 		return 0;
465bc5531deSDag-Erling Smørgrav 	default:
466bc5531deSDag-Erling Smørgrav 		/* Some other error occurred */
467bc5531deSDag-Erling Smørgrav 		return r;
468bc5531deSDag-Erling Smørgrav 	}
469bc5531deSDag-Erling Smørgrav }
470bc5531deSDag-Erling Smørgrav 
47119261079SEd Maste /*
47219261079SEd Maste  * Advanced *cpp past the end of key options, defined as the first unquoted
47319261079SEd Maste  * whitespace character. Returns 0 on success or -1 on failure (e.g.
47419261079SEd Maste  * unterminated quotes).
47519261079SEd Maste  */
47619261079SEd Maste int
sshkey_advance_past_options(char ** cpp)47719261079SEd Maste sshkey_advance_past_options(char **cpp)
47819261079SEd Maste {
47919261079SEd Maste 	char *cp = *cpp;
48019261079SEd Maste 	int quoted = 0;
48119261079SEd Maste 
48219261079SEd Maste 	for (; *cp && (quoted || (*cp != ' ' && *cp != '\t')); cp++) {
48319261079SEd Maste 		if (*cp == '\\' && cp[1] == '"')
48419261079SEd Maste 			cp++;	/* Skip both */
48519261079SEd Maste 		else if (*cp == '"')
48619261079SEd Maste 			quoted = !quoted;
48719261079SEd Maste 	}
48819261079SEd Maste 	*cpp = cp;
48919261079SEd Maste 	/* return failure for unterminated quotes */
49019261079SEd Maste 	return (*cp == '\0' && quoted) ? -1 : 0;
49119261079SEd Maste }
49219261079SEd Maste 
49319261079SEd Maste /* Save a public key */
49419261079SEd Maste int
sshkey_save_public(const struct sshkey * key,const char * path,const char * comment)49519261079SEd Maste sshkey_save_public(const struct sshkey *key, const char *path,
49619261079SEd Maste     const char *comment)
49719261079SEd Maste {
49819261079SEd Maste 	int fd, oerrno;
49919261079SEd Maste 	FILE *f = NULL;
50019261079SEd Maste 	int r = SSH_ERR_INTERNAL_ERROR;
50119261079SEd Maste 
50219261079SEd Maste 	if ((fd = open(path, O_WRONLY|O_CREAT|O_TRUNC, 0644)) == -1)
50319261079SEd Maste 		return SSH_ERR_SYSTEM_ERROR;
50419261079SEd Maste 	if ((f = fdopen(fd, "w")) == NULL) {
50519261079SEd Maste 		r = SSH_ERR_SYSTEM_ERROR;
50638a52bd3SEd Maste 		close(fd);
50719261079SEd Maste 		goto fail;
50819261079SEd Maste 	}
50919261079SEd Maste 	if ((r = sshkey_write(key, f)) != 0)
51019261079SEd Maste 		goto fail;
51119261079SEd Maste 	fprintf(f, " %s\n", comment);
51238a52bd3SEd Maste 	if (ferror(f)) {
51319261079SEd Maste 		r = SSH_ERR_SYSTEM_ERROR;
51438a52bd3SEd Maste 		goto fail;
51538a52bd3SEd Maste 	}
51638a52bd3SEd Maste 	if (fclose(f) != 0) {
51738a52bd3SEd Maste 		r = SSH_ERR_SYSTEM_ERROR;
51838a52bd3SEd Maste 		f = NULL;
51919261079SEd Maste  fail:
52038a52bd3SEd Maste 		if (f != NULL) {
52119261079SEd Maste 			oerrno = errno;
52219261079SEd Maste 			fclose(f);
52319261079SEd Maste 			errno = oerrno;
52438a52bd3SEd Maste 		}
52519261079SEd Maste 		return r;
52619261079SEd Maste 	}
52719261079SEd Maste 	return 0;
52819261079SEd Maste }
529