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