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