xref: /dflybsd-src/crypto/openssh/authfile.c (revision ba1276acd1c8c22d225b1bcf370a14c878644f44)
1*ba1276acSMatthew Dillon /* $OpenBSD: authfile.c,v 1.144 2023/03/14 07:26:25 dtucker Exp $ */
218de8d7fSPeter Avalos /*
336e94dc5SPeter Avalos  * Copyright (c) 2000, 2013 Markus Friedl.  All rights reserved.
418de8d7fSPeter Avalos  *
518de8d7fSPeter Avalos  * Redistribution and use in source and binary forms, with or without
618de8d7fSPeter Avalos  * modification, are permitted provided that the following conditions
718de8d7fSPeter Avalos  * are met:
818de8d7fSPeter Avalos  * 1. Redistributions of source code must retain the above copyright
918de8d7fSPeter Avalos  *    notice, this list of conditions and the following disclaimer.
1018de8d7fSPeter Avalos  * 2. Redistributions in binary form must reproduce the above copyright
1118de8d7fSPeter Avalos  *    notice, this list of conditions and the following disclaimer in the
1218de8d7fSPeter Avalos  *    documentation and/or other materials provided with the distribution.
1318de8d7fSPeter Avalos  *
1418de8d7fSPeter Avalos  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
1518de8d7fSPeter Avalos  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
1618de8d7fSPeter Avalos  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
1718de8d7fSPeter Avalos  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
1818de8d7fSPeter Avalos  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
1918de8d7fSPeter Avalos  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
2018de8d7fSPeter Avalos  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
2118de8d7fSPeter Avalos  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2218de8d7fSPeter Avalos  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
2318de8d7fSPeter Avalos  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2418de8d7fSPeter Avalos  */
2518de8d7fSPeter Avalos 
2618de8d7fSPeter Avalos #include "includes.h"
2718de8d7fSPeter Avalos 
2818de8d7fSPeter Avalos #include <sys/types.h>
2918de8d7fSPeter Avalos #include <sys/stat.h>
3018de8d7fSPeter Avalos #include <sys/uio.h>
3118de8d7fSPeter Avalos 
3218de8d7fSPeter Avalos #include <errno.h>
3318de8d7fSPeter Avalos #include <fcntl.h>
3418de8d7fSPeter Avalos #include <stdio.h>
3536e94dc5SPeter Avalos #include <stdarg.h>
3618de8d7fSPeter Avalos #include <stdlib.h>
3718de8d7fSPeter Avalos #include <string.h>
3818de8d7fSPeter Avalos #include <unistd.h>
39e9778795SPeter Avalos #include <limits.h>
4018de8d7fSPeter Avalos 
4118de8d7fSPeter Avalos #include "cipher.h"
4218de8d7fSPeter Avalos #include "ssh.h"
4318de8d7fSPeter Avalos #include "log.h"
4418de8d7fSPeter Avalos #include "authfile.h"
4518de8d7fSPeter Avalos #include "misc.h"
4618de8d7fSPeter Avalos #include "atomicio.h"
47e9778795SPeter Avalos #include "sshkey.h"
4836e94dc5SPeter Avalos #include "sshbuf.h"
4936e94dc5SPeter Avalos #include "ssherr.h"
50e9778795SPeter Avalos #include "krl.h"
5118de8d7fSPeter Avalos 
521c188a7fSPeter Avalos #define MAX_KEY_FILE_SIZE	(1024 * 1024)
531c188a7fSPeter Avalos 
549f304aafSPeter Avalos /* Save a key blob to a file */
559f304aafSPeter Avalos static int
sshkey_save_private_blob(struct sshbuf * keybuf,const char * filename)5636e94dc5SPeter Avalos sshkey_save_private_blob(struct sshbuf *keybuf, const char *filename)
579f304aafSPeter Avalos {
580cbfa66cSDaniel Fojt 	int r;
590cbfa66cSDaniel Fojt 	mode_t omask;
609f304aafSPeter Avalos 
610cbfa66cSDaniel Fojt 	omask = umask(077);
620cbfa66cSDaniel Fojt 	r = sshbuf_write_file(filename, keybuf);
630cbfa66cSDaniel Fojt 	umask(omask);
640cbfa66cSDaniel Fojt 	return r;
659f304aafSPeter Avalos }
669f304aafSPeter Avalos 
6718de8d7fSPeter Avalos 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)6836e94dc5SPeter Avalos sshkey_save_private(struct sshkey *key, const char *filename,
6936e94dc5SPeter Avalos     const char *passphrase, const char *comment,
700cbfa66cSDaniel Fojt     int format, const char *openssh_format_cipher, int openssh_format_rounds)
7118de8d7fSPeter Avalos {
7236e94dc5SPeter Avalos 	struct sshbuf *keyblob = NULL;
7336e94dc5SPeter Avalos 	int r;
749f304aafSPeter Avalos 
7536e94dc5SPeter Avalos 	if ((keyblob = sshbuf_new()) == NULL)
7636e94dc5SPeter Avalos 		return SSH_ERR_ALLOC_FAIL;
7736e94dc5SPeter Avalos 	if ((r = sshkey_private_to_fileblob(key, keyblob, passphrase, comment,
780cbfa66cSDaniel Fojt 	    format, openssh_format_cipher, openssh_format_rounds)) != 0)
799f304aafSPeter Avalos 		goto out;
8036e94dc5SPeter Avalos 	if ((r = sshkey_save_private_blob(keyblob, filename)) != 0)
819f304aafSPeter Avalos 		goto out;
8236e94dc5SPeter Avalos 	r = 0;
839f304aafSPeter Avalos  out:
8436e94dc5SPeter Avalos 	sshbuf_free(keyblob);
8536e94dc5SPeter Avalos 	return r;
869f304aafSPeter Avalos }
879f304aafSPeter Avalos 
8836e94dc5SPeter Avalos /* XXX remove error() calls from here? */
8936e94dc5SPeter Avalos int
sshkey_perm_ok(int fd,const char * filename)9036e94dc5SPeter Avalos sshkey_perm_ok(int fd, const char *filename)
9118de8d7fSPeter Avalos {
9218de8d7fSPeter Avalos 	struct stat st;
9318de8d7fSPeter Avalos 
940cbfa66cSDaniel Fojt 	if (fstat(fd, &st) == -1)
9536e94dc5SPeter Avalos 		return SSH_ERR_SYSTEM_ERROR;
9618de8d7fSPeter Avalos 	/*
9718de8d7fSPeter Avalos 	 * if a key owned by the user is accessed, then we check the
9818de8d7fSPeter Avalos 	 * permissions of the file. if the key owned by a different user,
9918de8d7fSPeter Avalos 	 * then we don't care.
10018de8d7fSPeter Avalos 	 */
10118de8d7fSPeter Avalos #ifdef HAVE_CYGWIN
10218de8d7fSPeter Avalos 	if (check_ntsec(filename))
10318de8d7fSPeter Avalos #endif
10418de8d7fSPeter Avalos 	if ((st.st_uid == getuid()) && (st.st_mode & 077) != 0) {
10518de8d7fSPeter Avalos 		error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@");
10618de8d7fSPeter Avalos 		error("@         WARNING: UNPROTECTED PRIVATE KEY FILE!          @");
10718de8d7fSPeter Avalos 		error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@");
10818de8d7fSPeter Avalos 		error("Permissions 0%3.3o for '%s' are too open.",
10918de8d7fSPeter Avalos 		    (u_int)st.st_mode & 0777, filename);
110e9778795SPeter Avalos 		error("It is required that your private key files are NOT accessible by others.");
11118de8d7fSPeter Avalos 		error("This private key will be ignored.");
11236e94dc5SPeter Avalos 		return SSH_ERR_KEY_BAD_PERMISSIONS;
11336e94dc5SPeter Avalos 	}
11418de8d7fSPeter Avalos 	return 0;
11518de8d7fSPeter Avalos }
11618de8d7fSPeter Avalos 
11736e94dc5SPeter Avalos int
sshkey_load_private_type(int type,const char * filename,const char * passphrase,struct sshkey ** keyp,char ** commentp)11836e94dc5SPeter Avalos sshkey_load_private_type(int type, const char *filename, const char *passphrase,
1190cbfa66cSDaniel Fojt     struct sshkey **keyp, char **commentp)
1209f304aafSPeter Avalos {
12136e94dc5SPeter Avalos 	int fd, r;
1229f304aafSPeter Avalos 
123e9778795SPeter Avalos 	if (keyp != NULL)
12436e94dc5SPeter Avalos 		*keyp = NULL;
12536e94dc5SPeter Avalos 	if (commentp != NULL)
12636e94dc5SPeter Avalos 		*commentp = NULL;
12718de8d7fSPeter Avalos 
1280cbfa66cSDaniel Fojt 	if ((fd = open(filename, O_RDONLY)) == -1)
12936e94dc5SPeter Avalos 		return SSH_ERR_SYSTEM_ERROR;
1300cbfa66cSDaniel Fojt 
1310cbfa66cSDaniel Fojt 	r = sshkey_perm_ok(fd, filename);
1320cbfa66cSDaniel Fojt 	if (r != 0)
13336e94dc5SPeter Avalos 		goto out;
1349f304aafSPeter Avalos 
135e9778795SPeter Avalos 	r = sshkey_load_private_type_fd(fd, type, passphrase, keyp, commentp);
136664f4763Szrj 	if (r == 0 && keyp && *keyp)
137664f4763Szrj 		r = sshkey_set_filename(*keyp, filename);
138e9778795SPeter Avalos  out:
139e9778795SPeter Avalos 	close(fd);
140e9778795SPeter Avalos 	return r;
141e9778795SPeter Avalos }
142e9778795SPeter Avalos 
143e9778795SPeter Avalos int
sshkey_load_private(const char * filename,const char * passphrase,struct sshkey ** keyp,char ** commentp)1440cbfa66cSDaniel Fojt sshkey_load_private(const char *filename, const char *passphrase,
1450cbfa66cSDaniel Fojt     struct sshkey **keyp, char **commentp)
1460cbfa66cSDaniel Fojt {
1470cbfa66cSDaniel Fojt 	return sshkey_load_private_type(KEY_UNSPEC, filename, passphrase,
1480cbfa66cSDaniel Fojt 	    keyp, commentp);
1490cbfa66cSDaniel Fojt }
1500cbfa66cSDaniel Fojt 
1510cbfa66cSDaniel Fojt int
sshkey_load_private_type_fd(int fd,int type,const char * passphrase,struct sshkey ** keyp,char ** commentp)152e9778795SPeter Avalos sshkey_load_private_type_fd(int fd, int type, const char *passphrase,
153e9778795SPeter Avalos     struct sshkey **keyp, char **commentp)
154e9778795SPeter Avalos {
155e9778795SPeter Avalos 	struct sshbuf *buffer = NULL;
156e9778795SPeter Avalos 	int r;
157e9778795SPeter Avalos 
158e9778795SPeter Avalos 	if (keyp != NULL)
159e9778795SPeter Avalos 		*keyp = NULL;
1600cbfa66cSDaniel Fojt 	if ((r = sshbuf_load_fd(fd, &buffer)) != 0 ||
161e9778795SPeter Avalos 	    (r = sshkey_parse_private_fileblob_type(buffer, type,
162e9778795SPeter Avalos 	    passphrase, keyp, commentp)) != 0)
16336e94dc5SPeter Avalos 		goto out;
164e9778795SPeter Avalos 
165e9778795SPeter Avalos 	/* success */
16636e94dc5SPeter Avalos 	r = 0;
16736e94dc5SPeter Avalos  out:
16836e94dc5SPeter Avalos 	sshbuf_free(buffer);
16936e94dc5SPeter Avalos 	return r;
1709f304aafSPeter Avalos }
17118de8d7fSPeter Avalos 
1720cbfa66cSDaniel Fojt /* Load a pubkey from the unencrypted envelope of a new-format private key */
1730cbfa66cSDaniel Fojt static int
sshkey_load_pubkey_from_private(const char * filename,struct sshkey ** pubkeyp)1740cbfa66cSDaniel Fojt sshkey_load_pubkey_from_private(const char *filename, struct sshkey **pubkeyp)
1751c188a7fSPeter Avalos {
17636e94dc5SPeter Avalos 	struct sshbuf *buffer = NULL;
1770cbfa66cSDaniel Fojt 	struct sshkey *pubkey = NULL;
17836e94dc5SPeter Avalos 	int r, fd;
1791c188a7fSPeter Avalos 
1800cbfa66cSDaniel Fojt 	if (pubkeyp != NULL)
1810cbfa66cSDaniel Fojt 		*pubkeyp = NULL;
18236e94dc5SPeter Avalos 
1830cbfa66cSDaniel Fojt 	if ((fd = open(filename, O_RDONLY)) == -1)
18436e94dc5SPeter Avalos 		return SSH_ERR_SYSTEM_ERROR;
1850cbfa66cSDaniel Fojt 	if ((r = sshbuf_load_fd(fd, &buffer)) != 0 ||
1860cbfa66cSDaniel Fojt 	    (r = sshkey_parse_pubkey_from_private_fileblob_type(buffer,
1870cbfa66cSDaniel Fojt 	    KEY_UNSPEC, &pubkey)) != 0)
18836e94dc5SPeter Avalos 		goto out;
1890cbfa66cSDaniel Fojt 	if ((r = sshkey_set_filename(pubkey, filename)) != 0)
1900cbfa66cSDaniel Fojt 		goto out;
1910cbfa66cSDaniel Fojt 	/* success */
1920cbfa66cSDaniel Fojt 	if (pubkeyp != NULL) {
1930cbfa66cSDaniel Fojt 		*pubkeyp = pubkey;
1940cbfa66cSDaniel Fojt 		pubkey = NULL;
1951c188a7fSPeter Avalos 	}
19636e94dc5SPeter Avalos 	r = 0;
19736e94dc5SPeter Avalos  out:
19818de8d7fSPeter Avalos 	close(fd);
19936e94dc5SPeter Avalos 	sshbuf_free(buffer);
2000cbfa66cSDaniel Fojt 	sshkey_free(pubkey);
20136e94dc5SPeter Avalos 	return r;
20218de8d7fSPeter Avalos }
20318de8d7fSPeter Avalos 
20418de8d7fSPeter Avalos static int
sshkey_try_load_public(struct sshkey ** kp,const char * filename,char ** commentp)2050cbfa66cSDaniel Fojt sshkey_try_load_public(struct sshkey **kp, const char *filename,
2060cbfa66cSDaniel Fojt     char **commentp)
20718de8d7fSPeter Avalos {
20818de8d7fSPeter Avalos 	FILE *f;
209664f4763Szrj 	char *line = NULL, *cp;
210664f4763Szrj 	size_t linesize = 0;
21136e94dc5SPeter Avalos 	int r;
2120cbfa66cSDaniel Fojt 	struct sshkey *k = NULL;
21318de8d7fSPeter Avalos 
214*ba1276acSMatthew Dillon 	if (kp == NULL)
215*ba1276acSMatthew Dillon 		return SSH_ERR_INVALID_ARGUMENT;
2160cbfa66cSDaniel Fojt 	*kp = NULL;
21736e94dc5SPeter Avalos 	if (commentp != NULL)
21836e94dc5SPeter Avalos 		*commentp = NULL;
21936e94dc5SPeter Avalos 	if ((f = fopen(filename, "r")) == NULL)
22036e94dc5SPeter Avalos 		return SSH_ERR_SYSTEM_ERROR;
2210cbfa66cSDaniel Fojt 	if ((k = sshkey_new(KEY_UNSPEC)) == NULL) {
2220cbfa66cSDaniel Fojt 		fclose(f);
2230cbfa66cSDaniel Fojt 		return SSH_ERR_ALLOC_FAIL;
2240cbfa66cSDaniel Fojt 	}
225664f4763Szrj 	while (getline(&line, &linesize, f) != -1) {
22618de8d7fSPeter Avalos 		cp = line;
22718de8d7fSPeter Avalos 		switch (*cp) {
22818de8d7fSPeter Avalos 		case '#':
22918de8d7fSPeter Avalos 		case '\n':
23018de8d7fSPeter Avalos 		case '\0':
23118de8d7fSPeter Avalos 			continue;
23218de8d7fSPeter Avalos 		}
2331c188a7fSPeter Avalos 		/* Abort loading if this looks like a private key */
23436e94dc5SPeter Avalos 		if (strncmp(cp, "-----BEGIN", 10) == 0 ||
23536e94dc5SPeter Avalos 		    strcmp(cp, "SSH PRIVATE KEY FILE") == 0)
2361c188a7fSPeter Avalos 			break;
23718de8d7fSPeter Avalos 		/* Skip leading whitespace. */
23818de8d7fSPeter Avalos 		for (; *cp && (*cp == ' ' || *cp == '\t'); cp++)
23918de8d7fSPeter Avalos 			;
24018de8d7fSPeter Avalos 		if (*cp) {
24136e94dc5SPeter Avalos 			if ((r = sshkey_read(k, &cp)) == 0) {
2421c188a7fSPeter Avalos 				cp[strcspn(cp, "\r\n")] = '\0';
2431c188a7fSPeter Avalos 				if (commentp) {
24436e94dc5SPeter Avalos 					*commentp = strdup(*cp ?
2451c188a7fSPeter Avalos 					    cp : filename);
24636e94dc5SPeter Avalos 					if (*commentp == NULL)
24736e94dc5SPeter Avalos 						r = SSH_ERR_ALLOC_FAIL;
2481c188a7fSPeter Avalos 				}
2490cbfa66cSDaniel Fojt 				/* success */
2500cbfa66cSDaniel Fojt 				*kp = k;
251664f4763Szrj 				free(line);
25218de8d7fSPeter Avalos 				fclose(f);
25336e94dc5SPeter Avalos 				return r;
25418de8d7fSPeter Avalos 			}
25518de8d7fSPeter Avalos 		}
25618de8d7fSPeter Avalos 	}
2570cbfa66cSDaniel Fojt 	free(k);
258664f4763Szrj 	free(line);
25918de8d7fSPeter Avalos 	fclose(f);
26036e94dc5SPeter Avalos 	return SSH_ERR_INVALID_FORMAT;
26118de8d7fSPeter Avalos }
26218de8d7fSPeter Avalos 
263ce74bacaSMatthew Dillon /* load public key from any pubkey file */
26436e94dc5SPeter Avalos int
sshkey_load_public(const char * filename,struct sshkey ** keyp,char ** commentp)26536e94dc5SPeter Avalos sshkey_load_public(const char *filename, struct sshkey **keyp, char **commentp)
26618de8d7fSPeter Avalos {
2670cbfa66cSDaniel Fojt 	char *pubfile = NULL;
26850a69bb5SSascha Wildner 	int r, oerrno;
26918de8d7fSPeter Avalos 
27036e94dc5SPeter Avalos 	if (keyp != NULL)
27136e94dc5SPeter Avalos 		*keyp = NULL;
27236e94dc5SPeter Avalos 	if (commentp != NULL)
27336e94dc5SPeter Avalos 		*commentp = NULL;
27436e94dc5SPeter Avalos 
2750cbfa66cSDaniel Fojt 	if ((r = sshkey_try_load_public(keyp, filename, commentp)) == 0)
276ce74bacaSMatthew Dillon 		goto out;
27736e94dc5SPeter Avalos 
27836e94dc5SPeter Avalos 	/* try .pub suffix */
2790cbfa66cSDaniel Fojt 	if (asprintf(&pubfile, "%s.pub", filename) == -1)
28036e94dc5SPeter Avalos 		return SSH_ERR_ALLOC_FAIL;
2810cbfa66cSDaniel Fojt 	if ((r = sshkey_try_load_public(keyp, pubfile, commentp)) == 0)
282ce74bacaSMatthew Dillon 		goto out;
2830cbfa66cSDaniel Fojt 
2840cbfa66cSDaniel Fojt 	/* finally, try to extract public key from private key file */
2850cbfa66cSDaniel Fojt 	if ((r = sshkey_load_pubkey_from_private(filename, keyp)) == 0)
2860cbfa66cSDaniel Fojt 		goto out;
2870cbfa66cSDaniel Fojt 
28850a69bb5SSascha Wildner 	/* Pretend we couldn't find the key */
28950a69bb5SSascha Wildner 	r = SSH_ERR_SYSTEM_ERROR;
29050a69bb5SSascha Wildner 	errno = ENOENT;
29150a69bb5SSascha Wildner 
292ce74bacaSMatthew Dillon  out:
29350a69bb5SSascha Wildner 	oerrno = errno;
2940cbfa66cSDaniel Fojt 	free(pubfile);
29550a69bb5SSascha Wildner 	errno = oerrno;
29636e94dc5SPeter Avalos 	return r;
29718de8d7fSPeter Avalos }
298856ea928SPeter Avalos 
299856ea928SPeter Avalos /* Load the certificate associated with the named private key */
30036e94dc5SPeter Avalos int
sshkey_load_cert(const char * filename,struct sshkey ** keyp)30136e94dc5SPeter Avalos sshkey_load_cert(const char *filename, struct sshkey **keyp)
302856ea928SPeter Avalos {
30336e94dc5SPeter Avalos 	struct sshkey *pub = NULL;
30436e94dc5SPeter Avalos 	char *file = NULL;
30536e94dc5SPeter Avalos 	int r = SSH_ERR_INTERNAL_ERROR;
306856ea928SPeter Avalos 
307e9778795SPeter Avalos 	if (keyp != NULL)
30836e94dc5SPeter Avalos 		*keyp = NULL;
30936e94dc5SPeter Avalos 
31036e94dc5SPeter Avalos 	if (asprintf(&file, "%s-cert.pub", filename) == -1)
31136e94dc5SPeter Avalos 		return SSH_ERR_ALLOC_FAIL;
31236e94dc5SPeter Avalos 
3130cbfa66cSDaniel Fojt 	r = sshkey_try_load_public(keyp, file, NULL);
31436e94dc5SPeter Avalos 	free(file);
31536e94dc5SPeter Avalos 	sshkey_free(pub);
31636e94dc5SPeter Avalos 	return r;
317856ea928SPeter Avalos }
318856ea928SPeter Avalos 
319856ea928SPeter Avalos /* Load private key and certificate */
32036e94dc5SPeter Avalos int
sshkey_load_private_cert(int type,const char * filename,const char * passphrase,struct sshkey ** keyp)32136e94dc5SPeter Avalos sshkey_load_private_cert(int type, const char *filename, const char *passphrase,
3220cbfa66cSDaniel Fojt     struct sshkey **keyp)
323856ea928SPeter Avalos {
32436e94dc5SPeter Avalos 	struct sshkey *key = NULL, *cert = NULL;
32536e94dc5SPeter Avalos 	int r;
32636e94dc5SPeter Avalos 
327e9778795SPeter Avalos 	if (keyp != NULL)
32836e94dc5SPeter Avalos 		*keyp = NULL;
329856ea928SPeter Avalos 
330856ea928SPeter Avalos 	switch (type) {
33136e94dc5SPeter Avalos #ifdef WITH_OPENSSL
332856ea928SPeter Avalos 	case KEY_RSA:
333856ea928SPeter Avalos 	case KEY_DSA:
3349f304aafSPeter Avalos 	case KEY_ECDSA:
33536e94dc5SPeter Avalos #endif /* WITH_OPENSSL */
336e9778795SPeter Avalos 	case KEY_ED25519:
337664f4763Szrj 	case KEY_XMSS:
33836e94dc5SPeter Avalos 	case KEY_UNSPEC:
339856ea928SPeter Avalos 		break;
340856ea928SPeter Avalos 	default:
34136e94dc5SPeter Avalos 		return SSH_ERR_KEY_TYPE_UNKNOWN;
342856ea928SPeter Avalos 	}
343856ea928SPeter Avalos 
34436e94dc5SPeter Avalos 	if ((r = sshkey_load_private_type(type, filename,
3450cbfa66cSDaniel Fojt 	    passphrase, &key, NULL)) != 0 ||
34636e94dc5SPeter Avalos 	    (r = sshkey_load_cert(filename, &cert)) != 0)
34736e94dc5SPeter Avalos 		goto out;
348856ea928SPeter Avalos 
349856ea928SPeter Avalos 	/* Make sure the private key matches the certificate */
35036e94dc5SPeter Avalos 	if (sshkey_equal_public(key, cert) == 0) {
35136e94dc5SPeter Avalos 		r = SSH_ERR_KEY_CERT_MISMATCH;
35236e94dc5SPeter Avalos 		goto out;
353856ea928SPeter Avalos 	}
354856ea928SPeter Avalos 
355e9778795SPeter Avalos 	if ((r = sshkey_to_certified(key)) != 0 ||
35636e94dc5SPeter Avalos 	    (r = sshkey_cert_copy(cert, key)) != 0)
35736e94dc5SPeter Avalos 		goto out;
35836e94dc5SPeter Avalos 	r = 0;
359e9778795SPeter Avalos 	if (keyp != NULL) {
36036e94dc5SPeter Avalos 		*keyp = key;
36136e94dc5SPeter Avalos 		key = NULL;
362e9778795SPeter Avalos 	}
36336e94dc5SPeter Avalos  out:
36436e94dc5SPeter Avalos 	sshkey_free(key);
36536e94dc5SPeter Avalos 	sshkey_free(cert);
36636e94dc5SPeter Avalos 	return r;
367856ea928SPeter Avalos }
368856ea928SPeter Avalos 
369856ea928SPeter Avalos /*
37036e94dc5SPeter Avalos  * Returns success if the specified "key" is listed in the file "filename",
37136e94dc5SPeter Avalos  * SSH_ERR_KEY_NOT_FOUND: if the key is not listed or another error.
372e9778795SPeter Avalos  * If "strict_type" is set then the key type must match exactly,
373ee116499SAntonio Huete Jimenez  * otherwise a comparison that ignores certificate data is performed.
374e9778795SPeter Avalos  * If "check_ca" is set and "key" is a certificate, then its CA key is
375e9778795SPeter Avalos  * also checked and sshkey_in_file() will return success if either is found.
376856ea928SPeter Avalos  */
377856ea928SPeter Avalos int
sshkey_in_file(struct sshkey * key,const char * filename,int strict_type,int check_ca)378e9778795SPeter Avalos sshkey_in_file(struct sshkey *key, const char *filename, int strict_type,
379e9778795SPeter Avalos     int check_ca)
380856ea928SPeter Avalos {
381856ea928SPeter Avalos 	FILE *f;
382664f4763Szrj 	char *line = NULL, *cp;
383664f4763Szrj 	size_t linesize = 0;
38436e94dc5SPeter Avalos 	int r = 0;
38536e94dc5SPeter Avalos 	struct sshkey *pub = NULL;
386664f4763Szrj 
38736e94dc5SPeter Avalos 	int (*sshkey_compare)(const struct sshkey *, const struct sshkey *) =
38836e94dc5SPeter Avalos 	    strict_type ?  sshkey_equal : sshkey_equal_public;
389856ea928SPeter Avalos 
390e9778795SPeter Avalos 	if ((f = fopen(filename, "r")) == NULL)
39136e94dc5SPeter Avalos 		return SSH_ERR_SYSTEM_ERROR;
392856ea928SPeter Avalos 
393664f4763Szrj 	while (getline(&line, &linesize, f) != -1) {
394664f4763Szrj 		sshkey_free(pub);
395664f4763Szrj 		pub = NULL;
396856ea928SPeter Avalos 		cp = line;
397856ea928SPeter Avalos 
398856ea928SPeter Avalos 		/* Skip leading whitespace. */
399856ea928SPeter Avalos 		for (; *cp && (*cp == ' ' || *cp == '\t'); cp++)
400856ea928SPeter Avalos 			;
401856ea928SPeter Avalos 
402856ea928SPeter Avalos 		/* Skip comments and empty lines */
403856ea928SPeter Avalos 		switch (*cp) {
404856ea928SPeter Avalos 		case '#':
405856ea928SPeter Avalos 		case '\n':
406856ea928SPeter Avalos 		case '\0':
407856ea928SPeter Avalos 			continue;
408856ea928SPeter Avalos 		}
409856ea928SPeter Avalos 
41036e94dc5SPeter Avalos 		if ((pub = sshkey_new(KEY_UNSPEC)) == NULL) {
41136e94dc5SPeter Avalos 			r = SSH_ERR_ALLOC_FAIL;
41236e94dc5SPeter Avalos 			goto out;
413856ea928SPeter Avalos 		}
414664f4763Szrj 		switch (r = sshkey_read(pub, &cp)) {
415664f4763Szrj 		case 0:
416664f4763Szrj 			break;
417664f4763Szrj 		case SSH_ERR_KEY_LENGTH:
418664f4763Szrj 			continue;
419664f4763Szrj 		default:
42036e94dc5SPeter Avalos 			goto out;
421664f4763Szrj 		}
422e9778795SPeter Avalos 		if (sshkey_compare(key, pub) ||
423e9778795SPeter Avalos 		    (check_ca && sshkey_is_cert(key) &&
424e9778795SPeter Avalos 		    sshkey_compare(key->cert->signature_key, pub))) {
42536e94dc5SPeter Avalos 			r = 0;
42636e94dc5SPeter Avalos 			goto out;
427856ea928SPeter Avalos 		}
428856ea928SPeter Avalos 	}
42936e94dc5SPeter Avalos 	r = SSH_ERR_KEY_NOT_FOUND;
43036e94dc5SPeter Avalos  out:
431664f4763Szrj 	free(line);
43236e94dc5SPeter Avalos 	sshkey_free(pub);
433856ea928SPeter Avalos 	fclose(f);
43436e94dc5SPeter Avalos 	return r;
435856ea928SPeter Avalos }
436856ea928SPeter Avalos 
437e9778795SPeter Avalos /*
438e9778795SPeter Avalos  * Checks whether the specified key is revoked, returning 0 if not,
439e9778795SPeter Avalos  * SSH_ERR_KEY_REVOKED if it is or another error code if something
440e9778795SPeter Avalos  * unexpected happened.
441e9778795SPeter Avalos  * This will check both the key and, if it is a certificate, its CA key too.
442e9778795SPeter Avalos  * "revoked_keys_file" may be a KRL or a one-per-line list of public keys.
443e9778795SPeter Avalos  */
444e9778795SPeter Avalos int
sshkey_check_revoked(struct sshkey * key,const char * revoked_keys_file)445e9778795SPeter Avalos sshkey_check_revoked(struct sshkey *key, const char *revoked_keys_file)
446e9778795SPeter Avalos {
447e9778795SPeter Avalos 	int r;
448e9778795SPeter Avalos 
449e9778795SPeter Avalos 	r = ssh_krl_file_contains_key(revoked_keys_file, key);
450e9778795SPeter Avalos 	/* If this was not a KRL to begin with then continue below */
451e9778795SPeter Avalos 	if (r != SSH_ERR_KRL_BAD_MAGIC)
452e9778795SPeter Avalos 		return r;
453e9778795SPeter Avalos 
454e9778795SPeter Avalos 	/*
455e9778795SPeter Avalos 	 * If the file is not a KRL or we can't handle KRLs then attempt to
456e9778795SPeter Avalos 	 * parse the file as a flat list of keys.
457e9778795SPeter Avalos 	 */
458e9778795SPeter Avalos 	switch ((r = sshkey_in_file(key, revoked_keys_file, 0, 1))) {
459e9778795SPeter Avalos 	case 0:
460e9778795SPeter Avalos 		/* Key found => revoked */
461e9778795SPeter Avalos 		return SSH_ERR_KEY_REVOKED;
462e9778795SPeter Avalos 	case SSH_ERR_KEY_NOT_FOUND:
463e9778795SPeter Avalos 		/* Key not found => not revoked */
464e9778795SPeter Avalos 		return 0;
465e9778795SPeter Avalos 	default:
466e9778795SPeter Avalos 		/* Some other error occurred */
467e9778795SPeter Avalos 		return r;
468e9778795SPeter Avalos 	}
469e9778795SPeter Avalos }
470e9778795SPeter Avalos 
4710cbfa66cSDaniel Fojt /*
4720cbfa66cSDaniel Fojt  * Advanced *cpp past the end of key options, defined as the first unquoted
4730cbfa66cSDaniel Fojt  * whitespace character. Returns 0 on success or -1 on failure (e.g.
4740cbfa66cSDaniel Fojt  * unterminated quotes).
4750cbfa66cSDaniel Fojt  */
4760cbfa66cSDaniel Fojt int
sshkey_advance_past_options(char ** cpp)4770cbfa66cSDaniel Fojt sshkey_advance_past_options(char **cpp)
4780cbfa66cSDaniel Fojt {
4790cbfa66cSDaniel Fojt 	char *cp = *cpp;
4800cbfa66cSDaniel Fojt 	int quoted = 0;
4810cbfa66cSDaniel Fojt 
4820cbfa66cSDaniel Fojt 	for (; *cp && (quoted || (*cp != ' ' && *cp != '\t')); cp++) {
4830cbfa66cSDaniel Fojt 		if (*cp == '\\' && cp[1] == '"')
4840cbfa66cSDaniel Fojt 			cp++;	/* Skip both */
4850cbfa66cSDaniel Fojt 		else if (*cp == '"')
4860cbfa66cSDaniel Fojt 			quoted = !quoted;
4870cbfa66cSDaniel Fojt 	}
4880cbfa66cSDaniel Fojt 	*cpp = cp;
4890cbfa66cSDaniel Fojt 	/* return failure for unterminated quotes */
4900cbfa66cSDaniel Fojt 	return (*cp == '\0' && quoted) ? -1 : 0;
4910cbfa66cSDaniel Fojt }
4920cbfa66cSDaniel Fojt 
4930cbfa66cSDaniel Fojt /* Save a public key */
4940cbfa66cSDaniel Fojt int
sshkey_save_public(const struct sshkey * key,const char * path,const char * comment)4950cbfa66cSDaniel Fojt sshkey_save_public(const struct sshkey *key, const char *path,
4960cbfa66cSDaniel Fojt     const char *comment)
4970cbfa66cSDaniel Fojt {
4980cbfa66cSDaniel Fojt 	int fd, oerrno;
4990cbfa66cSDaniel Fojt 	FILE *f = NULL;
5000cbfa66cSDaniel Fojt 	int r = SSH_ERR_INTERNAL_ERROR;
5010cbfa66cSDaniel Fojt 
5020cbfa66cSDaniel Fojt 	if ((fd = open(path, O_WRONLY|O_CREAT|O_TRUNC, 0644)) == -1)
5030cbfa66cSDaniel Fojt 		return SSH_ERR_SYSTEM_ERROR;
5040cbfa66cSDaniel Fojt 	if ((f = fdopen(fd, "w")) == NULL) {
5050cbfa66cSDaniel Fojt 		r = SSH_ERR_SYSTEM_ERROR;
506ee116499SAntonio Huete Jimenez 		close(fd);
5070cbfa66cSDaniel Fojt 		goto fail;
5080cbfa66cSDaniel Fojt 	}
5090cbfa66cSDaniel Fojt 	if ((r = sshkey_write(key, f)) != 0)
5100cbfa66cSDaniel Fojt 		goto fail;
5110cbfa66cSDaniel Fojt 	fprintf(f, " %s\n", comment);
512ee116499SAntonio Huete Jimenez 	if (ferror(f)) {
5130cbfa66cSDaniel Fojt 		r = SSH_ERR_SYSTEM_ERROR;
514ee116499SAntonio Huete Jimenez 		goto fail;
515ee116499SAntonio Huete Jimenez 	}
516ee116499SAntonio Huete Jimenez 	if (fclose(f) != 0) {
517ee116499SAntonio Huete Jimenez 		r = SSH_ERR_SYSTEM_ERROR;
518ee116499SAntonio Huete Jimenez 		f = NULL;
5190cbfa66cSDaniel Fojt  fail:
520ee116499SAntonio Huete Jimenez 		if (f != NULL) {
5210cbfa66cSDaniel Fojt 			oerrno = errno;
5220cbfa66cSDaniel Fojt 			fclose(f);
5230cbfa66cSDaniel Fojt 			errno = oerrno;
524ee116499SAntonio Huete Jimenez 		}
5250cbfa66cSDaniel Fojt 		return r;
5260cbfa66cSDaniel Fojt 	}
5270cbfa66cSDaniel Fojt 	return 0;
5280cbfa66cSDaniel Fojt }
529