xref: /openbsd-src/usr.bin/ssh/authfile.c (revision ff0e7be1ebbcc809ea8ad2b6dafe215824da9e46)
1 /* $OpenBSD: authfile.c,v 1.144 2023/03/14 07:26:25 dtucker Exp $ */
2 /*
3  * Copyright (c) 2000, 2013 Markus Friedl.  All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
15  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
18  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
19  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
20  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
21  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
23  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24  */
25 
26 
27 #include <sys/types.h>
28 #include <sys/stat.h>
29 #include <sys/uio.h>
30 
31 #include <errno.h>
32 #include <fcntl.h>
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <string.h>
36 #include <unistd.h>
37 #include <limits.h>
38 
39 #include "cipher.h"
40 #include "ssh.h"
41 #include "log.h"
42 #include "authfile.h"
43 #include "misc.h"
44 #include "atomicio.h"
45 #include "sshkey.h"
46 #include "sshbuf.h"
47 #include "ssherr.h"
48 #include "krl.h"
49 
50 #define MAX_KEY_FILE_SIZE	(1024 * 1024)
51 
52 /* Save a key blob to a file */
53 static int
54 sshkey_save_private_blob(struct sshbuf *keybuf, const char *filename)
55 {
56 	int r;
57 	mode_t omask;
58 
59 	omask = umask(077);
60 	r = sshbuf_write_file(filename, keybuf);
61 	umask(omask);
62 	return r;
63 }
64 
65 int
66 sshkey_save_private(struct sshkey *key, const char *filename,
67     const char *passphrase, const char *comment,
68     int format, const char *openssh_format_cipher, int openssh_format_rounds)
69 {
70 	struct sshbuf *keyblob = NULL;
71 	int r;
72 
73 	if ((keyblob = sshbuf_new()) == NULL)
74 		return SSH_ERR_ALLOC_FAIL;
75 	if ((r = sshkey_private_to_fileblob(key, keyblob, passphrase, comment,
76 	    format, openssh_format_cipher, openssh_format_rounds)) != 0)
77 		goto out;
78 	if ((r = sshkey_save_private_blob(keyblob, filename)) != 0)
79 		goto out;
80 	r = 0;
81  out:
82 	sshbuf_free(keyblob);
83 	return r;
84 }
85 
86 /* XXX remove error() calls from here? */
87 int
88 sshkey_perm_ok(int fd, const char *filename)
89 {
90 	struct stat st;
91 
92 	if (fstat(fd, &st) == -1)
93 		return SSH_ERR_SYSTEM_ERROR;
94 	/*
95 	 * if a key owned by the user is accessed, then we check the
96 	 * permissions of the file. if the key owned by a different user,
97 	 * then we don't care.
98 	 */
99 	if ((st.st_uid == getuid()) && (st.st_mode & 077) != 0) {
100 		error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@");
101 		error("@         WARNING: UNPROTECTED PRIVATE KEY FILE!          @");
102 		error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@");
103 		error("Permissions 0%3.3o for '%s' are too open.",
104 		    (u_int)st.st_mode & 0777, filename);
105 		error("It is required that your private key files are NOT accessible by others.");
106 		error("This private key will be ignored.");
107 		return SSH_ERR_KEY_BAD_PERMISSIONS;
108 	}
109 	return 0;
110 }
111 
112 int
113 sshkey_load_private_type(int type, const char *filename, const char *passphrase,
114     struct sshkey **keyp, char **commentp)
115 {
116 	int fd, r;
117 
118 	if (keyp != NULL)
119 		*keyp = NULL;
120 	if (commentp != NULL)
121 		*commentp = NULL;
122 
123 	if ((fd = open(filename, O_RDONLY)) == -1)
124 		return SSH_ERR_SYSTEM_ERROR;
125 
126 	r = sshkey_perm_ok(fd, filename);
127 	if (r != 0)
128 		goto out;
129 
130 	r = sshkey_load_private_type_fd(fd, type, passphrase, keyp, commentp);
131 	if (r == 0 && keyp && *keyp)
132 		r = sshkey_set_filename(*keyp, filename);
133  out:
134 	close(fd);
135 	return r;
136 }
137 
138 int
139 sshkey_load_private(const char *filename, const char *passphrase,
140     struct sshkey **keyp, char **commentp)
141 {
142 	return sshkey_load_private_type(KEY_UNSPEC, filename, passphrase,
143 	    keyp, commentp);
144 }
145 
146 int
147 sshkey_load_private_type_fd(int fd, int type, const char *passphrase,
148     struct sshkey **keyp, char **commentp)
149 {
150 	struct sshbuf *buffer = NULL;
151 	int r;
152 
153 	if (keyp != NULL)
154 		*keyp = NULL;
155 	if ((r = sshbuf_load_fd(fd, &buffer)) != 0 ||
156 	    (r = sshkey_parse_private_fileblob_type(buffer, type,
157 	    passphrase, keyp, commentp)) != 0)
158 		goto out;
159 
160 	/* success */
161 	r = 0;
162  out:
163 	sshbuf_free(buffer);
164 	return r;
165 }
166 
167 /* Load a pubkey from the unencrypted envelope of a new-format private key */
168 static int
169 sshkey_load_pubkey_from_private(const char *filename, struct sshkey **pubkeyp)
170 {
171 	struct sshbuf *buffer = NULL;
172 	struct sshkey *pubkey = NULL;
173 	int r, fd;
174 
175 	if (pubkeyp != NULL)
176 		*pubkeyp = NULL;
177 
178 	if ((fd = open(filename, O_RDONLY)) == -1)
179 		return SSH_ERR_SYSTEM_ERROR;
180 	if ((r = sshbuf_load_fd(fd, &buffer)) != 0 ||
181 	    (r = sshkey_parse_pubkey_from_private_fileblob_type(buffer,
182 	    KEY_UNSPEC, &pubkey)) != 0)
183 		goto out;
184 	if ((r = sshkey_set_filename(pubkey, filename)) != 0)
185 		goto out;
186 	/* success */
187 	if (pubkeyp != NULL) {
188 		*pubkeyp = pubkey;
189 		pubkey = NULL;
190 	}
191 	r = 0;
192  out:
193 	close(fd);
194 	sshbuf_free(buffer);
195 	sshkey_free(pubkey);
196 	return r;
197 }
198 
199 static int
200 sshkey_try_load_public(struct sshkey **kp, const char *filename,
201     char **commentp)
202 {
203 	FILE *f;
204 	char *line = NULL, *cp;
205 	size_t linesize = 0;
206 	int r;
207 	struct sshkey *k = NULL;
208 
209 	if (kp == NULL)
210 		return SSH_ERR_INVALID_ARGUMENT;
211 	*kp = NULL;
212 	if (commentp != NULL)
213 		*commentp = NULL;
214 	if ((f = fopen(filename, "r")) == NULL)
215 		return SSH_ERR_SYSTEM_ERROR;
216 	if ((k = sshkey_new(KEY_UNSPEC)) == NULL) {
217 		fclose(f);
218 		return SSH_ERR_ALLOC_FAIL;
219 	}
220 	while (getline(&line, &linesize, f) != -1) {
221 		cp = line;
222 		switch (*cp) {
223 		case '#':
224 		case '\n':
225 		case '\0':
226 			continue;
227 		}
228 		/* Abort loading if this looks like a private key */
229 		if (strncmp(cp, "-----BEGIN", 10) == 0 ||
230 		    strcmp(cp, "SSH PRIVATE KEY FILE") == 0)
231 			break;
232 		/* Skip leading whitespace. */
233 		for (; *cp && (*cp == ' ' || *cp == '\t'); cp++)
234 			;
235 		if (*cp) {
236 			if ((r = sshkey_read(k, &cp)) == 0) {
237 				cp[strcspn(cp, "\r\n")] = '\0';
238 				if (commentp) {
239 					*commentp = strdup(*cp ?
240 					    cp : filename);
241 					if (*commentp == NULL)
242 						r = SSH_ERR_ALLOC_FAIL;
243 				}
244 				/* success */
245 				*kp = k;
246 				free(line);
247 				fclose(f);
248 				return r;
249 			}
250 		}
251 	}
252 	free(k);
253 	free(line);
254 	fclose(f);
255 	return SSH_ERR_INVALID_FORMAT;
256 }
257 
258 /* load public key from any pubkey file */
259 int
260 sshkey_load_public(const char *filename, struct sshkey **keyp, char **commentp)
261 {
262 	char *pubfile = NULL;
263 	int r, oerrno;
264 
265 	if (keyp != NULL)
266 		*keyp = NULL;
267 	if (commentp != NULL)
268 		*commentp = NULL;
269 
270 	if ((r = sshkey_try_load_public(keyp, filename, commentp)) == 0)
271 		goto out;
272 
273 	/* try .pub suffix */
274 	if (asprintf(&pubfile, "%s.pub", filename) == -1)
275 		return SSH_ERR_ALLOC_FAIL;
276 	if ((r = sshkey_try_load_public(keyp, pubfile, commentp)) == 0)
277 		goto out;
278 
279 	/* finally, try to extract public key from private key file */
280 	if ((r = sshkey_load_pubkey_from_private(filename, keyp)) == 0)
281 		goto out;
282 
283 	/* Pretend we couldn't find the key */
284 	r = SSH_ERR_SYSTEM_ERROR;
285 	errno = ENOENT;
286 
287  out:
288 	oerrno = errno;
289 	free(pubfile);
290 	errno = oerrno;
291 	return r;
292 }
293 
294 /* Load the certificate associated with the named private key */
295 int
296 sshkey_load_cert(const char *filename, struct sshkey **keyp)
297 {
298 	struct sshkey *pub = NULL;
299 	char *file = NULL;
300 	int r = SSH_ERR_INTERNAL_ERROR;
301 
302 	if (keyp != NULL)
303 		*keyp = NULL;
304 
305 	if (asprintf(&file, "%s-cert.pub", filename) == -1)
306 		return SSH_ERR_ALLOC_FAIL;
307 
308 	r = sshkey_try_load_public(keyp, file, NULL);
309 	free(file);
310 	sshkey_free(pub);
311 	return r;
312 }
313 
314 /* Load private key and certificate */
315 int
316 sshkey_load_private_cert(int type, const char *filename, const char *passphrase,
317     struct sshkey **keyp)
318 {
319 	struct sshkey *key = NULL, *cert = NULL;
320 	int r;
321 
322 	if (keyp != NULL)
323 		*keyp = NULL;
324 
325 	switch (type) {
326 #ifdef WITH_OPENSSL
327 	case KEY_RSA:
328 	case KEY_DSA:
329 	case KEY_ECDSA:
330 #endif /* WITH_OPENSSL */
331 	case KEY_ED25519:
332 	case KEY_XMSS:
333 	case KEY_UNSPEC:
334 		break;
335 	default:
336 		return SSH_ERR_KEY_TYPE_UNKNOWN;
337 	}
338 
339 	if ((r = sshkey_load_private_type(type, filename,
340 	    passphrase, &key, NULL)) != 0 ||
341 	    (r = sshkey_load_cert(filename, &cert)) != 0)
342 		goto out;
343 
344 	/* Make sure the private key matches the certificate */
345 	if (sshkey_equal_public(key, cert) == 0) {
346 		r = SSH_ERR_KEY_CERT_MISMATCH;
347 		goto out;
348 	}
349 
350 	if ((r = sshkey_to_certified(key)) != 0 ||
351 	    (r = sshkey_cert_copy(cert, key)) != 0)
352 		goto out;
353 	r = 0;
354 	if (keyp != NULL) {
355 		*keyp = key;
356 		key = NULL;
357 	}
358  out:
359 	sshkey_free(key);
360 	sshkey_free(cert);
361 	return r;
362 }
363 
364 /*
365  * Returns success if the specified "key" is listed in the file "filename",
366  * SSH_ERR_KEY_NOT_FOUND: if the key is not listed or another error.
367  * If "strict_type" is set then the key type must match exactly,
368  * otherwise a comparison that ignores certificate data is performed.
369  * If "check_ca" is set and "key" is a certificate, then its CA key is
370  * also checked and sshkey_in_file() will return success if either is found.
371  */
372 int
373 sshkey_in_file(struct sshkey *key, const char *filename, int strict_type,
374     int check_ca)
375 {
376 	FILE *f;
377 	char *line = NULL, *cp;
378 	size_t linesize = 0;
379 	int r = 0;
380 	struct sshkey *pub = NULL;
381 
382 	int (*sshkey_compare)(const struct sshkey *, const struct sshkey *) =
383 	    strict_type ?  sshkey_equal : sshkey_equal_public;
384 
385 	if ((f = fopen(filename, "r")) == NULL)
386 		return SSH_ERR_SYSTEM_ERROR;
387 
388 	while (getline(&line, &linesize, f) != -1) {
389 		sshkey_free(pub);
390 		pub = NULL;
391 		cp = line;
392 
393 		/* Skip leading whitespace. */
394 		for (; *cp && (*cp == ' ' || *cp == '\t'); cp++)
395 			;
396 
397 		/* Skip comments and empty lines */
398 		switch (*cp) {
399 		case '#':
400 		case '\n':
401 		case '\0':
402 			continue;
403 		}
404 
405 		if ((pub = sshkey_new(KEY_UNSPEC)) == NULL) {
406 			r = SSH_ERR_ALLOC_FAIL;
407 			goto out;
408 		}
409 		switch (r = sshkey_read(pub, &cp)) {
410 		case 0:
411 			break;
412 		case SSH_ERR_KEY_LENGTH:
413 			continue;
414 		default:
415 			goto out;
416 		}
417 		if (sshkey_compare(key, pub) ||
418 		    (check_ca && sshkey_is_cert(key) &&
419 		    sshkey_compare(key->cert->signature_key, pub))) {
420 			r = 0;
421 			goto out;
422 		}
423 	}
424 	r = SSH_ERR_KEY_NOT_FOUND;
425  out:
426 	free(line);
427 	sshkey_free(pub);
428 	fclose(f);
429 	return r;
430 }
431 
432 /*
433  * Checks whether the specified key is revoked, returning 0 if not,
434  * SSH_ERR_KEY_REVOKED if it is or another error code if something
435  * unexpected happened.
436  * This will check both the key and, if it is a certificate, its CA key too.
437  * "revoked_keys_file" may be a KRL or a one-per-line list of public keys.
438  */
439 int
440 sshkey_check_revoked(struct sshkey *key, const char *revoked_keys_file)
441 {
442 	int r;
443 
444 	r = ssh_krl_file_contains_key(revoked_keys_file, key);
445 	/* If this was not a KRL to begin with then continue below */
446 	if (r != SSH_ERR_KRL_BAD_MAGIC)
447 		return r;
448 
449 	/*
450 	 * If the file is not a KRL or we can't handle KRLs then attempt to
451 	 * parse the file as a flat list of keys.
452 	 */
453 	switch ((r = sshkey_in_file(key, revoked_keys_file, 0, 1))) {
454 	case 0:
455 		/* Key found => revoked */
456 		return SSH_ERR_KEY_REVOKED;
457 	case SSH_ERR_KEY_NOT_FOUND:
458 		/* Key not found => not revoked */
459 		return 0;
460 	default:
461 		/* Some other error occurred */
462 		return r;
463 	}
464 }
465 
466 /*
467  * Advanced *cpp past the end of key options, defined as the first unquoted
468  * whitespace character. Returns 0 on success or -1 on failure (e.g.
469  * unterminated quotes).
470  */
471 int
472 sshkey_advance_past_options(char **cpp)
473 {
474 	char *cp = *cpp;
475 	int quoted = 0;
476 
477 	for (; *cp && (quoted || (*cp != ' ' && *cp != '\t')); cp++) {
478 		if (*cp == '\\' && cp[1] == '"')
479 			cp++;	/* Skip both */
480 		else if (*cp == '"')
481 			quoted = !quoted;
482 	}
483 	*cpp = cp;
484 	/* return failure for unterminated quotes */
485 	return (*cp == '\0' && quoted) ? -1 : 0;
486 }
487 
488 /* Save a public key */
489 int
490 sshkey_save_public(const struct sshkey *key, const char *path,
491     const char *comment)
492 {
493 	int fd, oerrno;
494 	FILE *f = NULL;
495 	int r = SSH_ERR_INTERNAL_ERROR;
496 
497 	if ((fd = open(path, O_WRONLY|O_CREAT|O_TRUNC, 0644)) == -1)
498 		return SSH_ERR_SYSTEM_ERROR;
499 	if ((f = fdopen(fd, "w")) == NULL) {
500 		r = SSH_ERR_SYSTEM_ERROR;
501 		close(fd);
502 		goto fail;
503 	}
504 	if ((r = sshkey_write(key, f)) != 0)
505 		goto fail;
506 	fprintf(f, " %s\n", comment);
507 	if (ferror(f)) {
508 		r = SSH_ERR_SYSTEM_ERROR;
509 		goto fail;
510 	}
511 	if (fclose(f) != 0) {
512 		r = SSH_ERR_SYSTEM_ERROR;
513 		f = NULL;
514  fail:
515 		if (f != NULL) {
516 			oerrno = errno;
517 			fclose(f);
518 			errno = oerrno;
519 		}
520 		return r;
521 	}
522 	return 0;
523 }
524