xref: /openssh-portable/authfile.c (revision dd8002fb)
1 /* $OpenBSD: authfile.c,v 1.135 2019/09/03 08:30:47 djm 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 #include "includes.h"
27 
28 #include <sys/types.h>
29 #include <sys/stat.h>
30 #include <sys/uio.h>
31 
32 #include <errno.h>
33 #include <fcntl.h>
34 #include <stdio.h>
35 #include <stdarg.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
sshkey_save_private_blob(struct sshbuf * keybuf,const char * filename)56 sshkey_save_private_blob(struct sshbuf *keybuf, const char *filename)
57 {
58 	int fd, oerrno;
59 
60 	if ((fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0600)) == -1)
61 		return SSH_ERR_SYSTEM_ERROR;
62 	if (atomicio(vwrite, fd, sshbuf_mutable_ptr(keybuf),
63 	    sshbuf_len(keybuf)) != sshbuf_len(keybuf)) {
64 		oerrno = errno;
65 		close(fd);
66 		unlink(filename);
67 		errno = oerrno;
68 		return SSH_ERR_SYSTEM_ERROR;
69 	}
70 	close(fd);
71 	return 0;
72 }
73 
74 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)75 sshkey_save_private(struct sshkey *key, const char *filename,
76     const char *passphrase, const char *comment,
77     int format, const char *openssh_format_cipher, int openssh_format_rounds)
78 {
79 	struct sshbuf *keyblob = NULL;
80 	int r;
81 
82 	if ((keyblob = sshbuf_new()) == NULL)
83 		return SSH_ERR_ALLOC_FAIL;
84 	if ((r = sshkey_private_to_fileblob(key, keyblob, passphrase, comment,
85 	    format, openssh_format_cipher, openssh_format_rounds)) != 0)
86 		goto out;
87 	if ((r = sshkey_save_private_blob(keyblob, filename)) != 0)
88 		goto out;
89 	r = 0;
90  out:
91 	sshbuf_free(keyblob);
92 	return r;
93 }
94 
95 /* Load a key from a fd into a buffer */
96 int
sshkey_load_file(int fd,struct sshbuf * blob)97 sshkey_load_file(int fd, struct sshbuf *blob)
98 {
99 	u_char buf[1024];
100 	size_t len;
101 	struct stat st;
102 	int r;
103 
104 	if (fstat(fd, &st) == -1)
105 		return SSH_ERR_SYSTEM_ERROR;
106 	if ((st.st_mode & (S_IFSOCK|S_IFCHR|S_IFIFO)) == 0 &&
107 	    st.st_size > MAX_KEY_FILE_SIZE)
108 		return SSH_ERR_INVALID_FORMAT;
109 	for (;;) {
110 		if ((len = atomicio(read, fd, buf, sizeof(buf))) == 0) {
111 			if (errno == EPIPE)
112 				break;
113 			r = SSH_ERR_SYSTEM_ERROR;
114 			goto out;
115 		}
116 		if ((r = sshbuf_put(blob, buf, len)) != 0)
117 			goto out;
118 		if (sshbuf_len(blob) > MAX_KEY_FILE_SIZE) {
119 			r = SSH_ERR_INVALID_FORMAT;
120 			goto out;
121 		}
122 	}
123 	if ((st.st_mode & (S_IFSOCK|S_IFCHR|S_IFIFO)) == 0 &&
124 	    st.st_size != (off_t)sshbuf_len(blob)) {
125 		r = SSH_ERR_FILE_CHANGED;
126 		goto out;
127 	}
128 	r = 0;
129 
130  out:
131 	explicit_bzero(buf, sizeof(buf));
132 	if (r != 0)
133 		sshbuf_reset(blob);
134 	return r;
135 }
136 
137 
138 /* XXX remove error() calls from here? */
139 int
sshkey_perm_ok(int fd,const char * filename)140 sshkey_perm_ok(int fd, const char *filename)
141 {
142 	struct stat st;
143 
144 	if (fstat(fd, &st) == -1)
145 		return SSH_ERR_SYSTEM_ERROR;
146 	/*
147 	 * if a key owned by the user is accessed, then we check the
148 	 * permissions of the file. if the key owned by a different user,
149 	 * then we don't care.
150 	 */
151 #ifdef HAVE_CYGWIN
152 	if (check_ntsec(filename))
153 #endif
154 	if ((st.st_uid == getuid()) && (st.st_mode & 077) != 0) {
155 		error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@");
156 		error("@         WARNING: UNPROTECTED PRIVATE KEY FILE!          @");
157 		error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@");
158 		error("Permissions 0%3.3o for '%s' are too open.",
159 		    (u_int)st.st_mode & 0777, filename);
160 		error("It is required that your private key files are NOT accessible by others.");
161 		error("This private key will be ignored.");
162 		return SSH_ERR_KEY_BAD_PERMISSIONS;
163 	}
164 	return 0;
165 }
166 
167 int
sshkey_load_private_type(int type,const char * filename,const char * passphrase,struct sshkey ** keyp,char ** commentp)168 sshkey_load_private_type(int type, const char *filename, const char *passphrase,
169     struct sshkey **keyp, char **commentp)
170 {
171 	int fd, r;
172 
173 	if (keyp != NULL)
174 		*keyp = NULL;
175 	if (commentp != NULL)
176 		*commentp = NULL;
177 
178 	if ((fd = open(filename, O_RDONLY)) == -1)
179 		return SSH_ERR_SYSTEM_ERROR;
180 
181 	r = sshkey_perm_ok(fd, filename);
182 	if (r != 0)
183 		goto out;
184 
185 	r = sshkey_load_private_type_fd(fd, type, passphrase, keyp, commentp);
186 	if (r == 0 && keyp && *keyp)
187 		r = sshkey_set_filename(*keyp, filename);
188  out:
189 	close(fd);
190 	return r;
191 }
192 
193 int
sshkey_load_private_type_fd(int fd,int type,const char * passphrase,struct sshkey ** keyp,char ** commentp)194 sshkey_load_private_type_fd(int fd, int type, const char *passphrase,
195     struct sshkey **keyp, char **commentp)
196 {
197 	struct sshbuf *buffer = NULL;
198 	int r;
199 
200 	if (keyp != NULL)
201 		*keyp = NULL;
202 	if ((buffer = sshbuf_new()) == NULL) {
203 		r = SSH_ERR_ALLOC_FAIL;
204 		goto out;
205 	}
206 	if ((r = sshkey_load_file(fd, buffer)) != 0 ||
207 	    (r = sshkey_parse_private_fileblob_type(buffer, type,
208 	    passphrase, keyp, commentp)) != 0)
209 		goto out;
210 
211 	/* success */
212 	r = 0;
213  out:
214 	sshbuf_free(buffer);
215 	return r;
216 }
217 
218 /* XXX this is almost identical to sshkey_load_private_type() */
219 int
sshkey_load_private(const char * filename,const char * passphrase,struct sshkey ** keyp,char ** commentp)220 sshkey_load_private(const char *filename, const char *passphrase,
221     struct sshkey **keyp, char **commentp)
222 {
223 	struct sshbuf *buffer = NULL;
224 	int r, fd;
225 
226 	if (keyp != NULL)
227 		*keyp = NULL;
228 	if (commentp != NULL)
229 		*commentp = NULL;
230 
231 	if ((fd = open(filename, O_RDONLY)) == -1)
232 		return SSH_ERR_SYSTEM_ERROR;
233 	if (sshkey_perm_ok(fd, filename) != 0) {
234 		r = SSH_ERR_KEY_BAD_PERMISSIONS;
235 		goto out;
236 	}
237 
238 	if ((buffer = sshbuf_new()) == NULL) {
239 		r = SSH_ERR_ALLOC_FAIL;
240 		goto out;
241 	}
242 	if ((r = sshkey_load_file(fd, buffer)) != 0 ||
243 	    (r = sshkey_parse_private_fileblob(buffer, passphrase, keyp,
244 	    commentp)) != 0)
245 		goto out;
246 	if (keyp && *keyp &&
247 	    (r = sshkey_set_filename(*keyp, filename)) != 0)
248 		goto out;
249 	r = 0;
250  out:
251 	close(fd);
252 	sshbuf_free(buffer);
253 	return r;
254 }
255 
256 static int
sshkey_try_load_public(struct sshkey * k,const char * filename,char ** commentp)257 sshkey_try_load_public(struct sshkey *k, const char *filename, char **commentp)
258 {
259 	FILE *f;
260 	char *line = NULL, *cp;
261 	size_t linesize = 0;
262 	int r;
263 
264 	if (commentp != NULL)
265 		*commentp = NULL;
266 	if ((f = fopen(filename, "r")) == NULL)
267 		return SSH_ERR_SYSTEM_ERROR;
268 	while (getline(&line, &linesize, f) != -1) {
269 		cp = line;
270 		switch (*cp) {
271 		case '#':
272 		case '\n':
273 		case '\0':
274 			continue;
275 		}
276 		/* Abort loading if this looks like a private key */
277 		if (strncmp(cp, "-----BEGIN", 10) == 0 ||
278 		    strcmp(cp, "SSH PRIVATE KEY FILE") == 0)
279 			break;
280 		/* Skip leading whitespace. */
281 		for (; *cp && (*cp == ' ' || *cp == '\t'); cp++)
282 			;
283 		if (*cp) {
284 			if ((r = sshkey_read(k, &cp)) == 0) {
285 				cp[strcspn(cp, "\r\n")] = '\0';
286 				if (commentp) {
287 					*commentp = strdup(*cp ?
288 					    cp : filename);
289 					if (*commentp == NULL)
290 						r = SSH_ERR_ALLOC_FAIL;
291 				}
292 				free(line);
293 				fclose(f);
294 				return r;
295 			}
296 		}
297 	}
298 	free(line);
299 	fclose(f);
300 	return SSH_ERR_INVALID_FORMAT;
301 }
302 
303 /* load public key from any pubkey file */
304 int
sshkey_load_public(const char * filename,struct sshkey ** keyp,char ** commentp)305 sshkey_load_public(const char *filename, struct sshkey **keyp, char **commentp)
306 {
307 	struct sshkey *pub = NULL;
308 	char *file = NULL;
309 	int r;
310 
311 	if (keyp != NULL)
312 		*keyp = NULL;
313 	if (commentp != NULL)
314 		*commentp = NULL;
315 
316 	if ((pub = sshkey_new(KEY_UNSPEC)) == NULL)
317 		return SSH_ERR_ALLOC_FAIL;
318 	if ((r = sshkey_try_load_public(pub, filename, commentp)) == 0) {
319 		if (keyp != NULL) {
320 			*keyp = pub;
321 			pub = NULL;
322 		}
323 		r = 0;
324 		goto out;
325 	}
326 	sshkey_free(pub);
327 
328 	/* try .pub suffix */
329 	if (asprintf(&file, "%s.pub", filename) == -1)
330 		return SSH_ERR_ALLOC_FAIL;
331 	if ((pub = sshkey_new(KEY_UNSPEC)) == NULL) {
332 		r = SSH_ERR_ALLOC_FAIL;
333 		goto out;
334 	}
335 	if ((r = sshkey_try_load_public(pub, file, commentp)) == 0) {
336 		if (keyp != NULL) {
337 			*keyp = pub;
338 			pub = NULL;
339 		}
340 		r = 0;
341 	}
342  out:
343 	free(file);
344 	sshkey_free(pub);
345 	return r;
346 }
347 
348 /* Load the certificate associated with the named private key */
349 int
sshkey_load_cert(const char * filename,struct sshkey ** keyp)350 sshkey_load_cert(const char *filename, struct sshkey **keyp)
351 {
352 	struct sshkey *pub = NULL;
353 	char *file = NULL;
354 	int r = SSH_ERR_INTERNAL_ERROR;
355 
356 	if (keyp != NULL)
357 		*keyp = NULL;
358 
359 	if (asprintf(&file, "%s-cert.pub", filename) == -1)
360 		return SSH_ERR_ALLOC_FAIL;
361 
362 	if ((pub = sshkey_new(KEY_UNSPEC)) == NULL) {
363 		goto out;
364 	}
365 	if ((r = sshkey_try_load_public(pub, file, NULL)) != 0)
366 		goto out;
367 	/* success */
368 	if (keyp != NULL) {
369 		*keyp = pub;
370 		pub = NULL;
371 	}
372 	r = 0;
373  out:
374 	free(file);
375 	sshkey_free(pub);
376 	return r;
377 }
378 
379 /* Load private key and certificate */
380 int
sshkey_load_private_cert(int type,const char * filename,const char * passphrase,struct sshkey ** keyp)381 sshkey_load_private_cert(int type, const char *filename, const char *passphrase,
382     struct sshkey **keyp)
383 {
384 	struct sshkey *key = NULL, *cert = NULL;
385 	int r;
386 
387 	if (keyp != NULL)
388 		*keyp = NULL;
389 
390 	switch (type) {
391 #ifdef WITH_OPENSSL
392 	case KEY_RSA:
393 	case KEY_DSA:
394 	case KEY_ECDSA:
395 #endif /* WITH_OPENSSL */
396 	case KEY_ED25519:
397 	case KEY_XMSS:
398 	case KEY_UNSPEC:
399 		break;
400 	default:
401 		return SSH_ERR_KEY_TYPE_UNKNOWN;
402 	}
403 
404 	if ((r = sshkey_load_private_type(type, filename,
405 	    passphrase, &key, NULL)) != 0 ||
406 	    (r = sshkey_load_cert(filename, &cert)) != 0)
407 		goto out;
408 
409 	/* Make sure the private key matches the certificate */
410 	if (sshkey_equal_public(key, cert) == 0) {
411 		r = SSH_ERR_KEY_CERT_MISMATCH;
412 		goto out;
413 	}
414 
415 	if ((r = sshkey_to_certified(key)) != 0 ||
416 	    (r = sshkey_cert_copy(cert, key)) != 0)
417 		goto out;
418 	r = 0;
419 	if (keyp != NULL) {
420 		*keyp = key;
421 		key = NULL;
422 	}
423  out:
424 	sshkey_free(key);
425 	sshkey_free(cert);
426 	return r;
427 }
428 
429 /*
430  * Returns success if the specified "key" is listed in the file "filename",
431  * SSH_ERR_KEY_NOT_FOUND: if the key is not listed or another error.
432  * If "strict_type" is set then the key type must match exactly,
433  * otherwise a comparison that ignores certficiate data is performed.
434  * If "check_ca" is set and "key" is a certificate, then its CA key is
435  * also checked and sshkey_in_file() will return success if either is found.
436  */
437 int
sshkey_in_file(struct sshkey * key,const char * filename,int strict_type,int check_ca)438 sshkey_in_file(struct sshkey *key, const char *filename, int strict_type,
439     int check_ca)
440 {
441 	FILE *f;
442 	char *line = NULL, *cp;
443 	size_t linesize = 0;
444 	int r = 0;
445 	struct sshkey *pub = NULL;
446 
447 	int (*sshkey_compare)(const struct sshkey *, const struct sshkey *) =
448 	    strict_type ?  sshkey_equal : sshkey_equal_public;
449 
450 	if ((f = fopen(filename, "r")) == NULL)
451 		return SSH_ERR_SYSTEM_ERROR;
452 
453 	while (getline(&line, &linesize, f) != -1) {
454 		sshkey_free(pub);
455 		pub = NULL;
456 		cp = line;
457 
458 		/* Skip leading whitespace. */
459 		for (; *cp && (*cp == ' ' || *cp == '\t'); cp++)
460 			;
461 
462 		/* Skip comments and empty lines */
463 		switch (*cp) {
464 		case '#':
465 		case '\n':
466 		case '\0':
467 			continue;
468 		}
469 
470 		if ((pub = sshkey_new(KEY_UNSPEC)) == NULL) {
471 			r = SSH_ERR_ALLOC_FAIL;
472 			goto out;
473 		}
474 		switch (r = sshkey_read(pub, &cp)) {
475 		case 0:
476 			break;
477 		case SSH_ERR_KEY_LENGTH:
478 			continue;
479 		default:
480 			goto out;
481 		}
482 		if (sshkey_compare(key, pub) ||
483 		    (check_ca && sshkey_is_cert(key) &&
484 		    sshkey_compare(key->cert->signature_key, pub))) {
485 			r = 0;
486 			goto out;
487 		}
488 	}
489 	r = SSH_ERR_KEY_NOT_FOUND;
490  out:
491 	free(line);
492 	sshkey_free(pub);
493 	fclose(f);
494 	return r;
495 }
496 
497 /*
498  * Checks whether the specified key is revoked, returning 0 if not,
499  * SSH_ERR_KEY_REVOKED if it is or another error code if something
500  * unexpected happened.
501  * This will check both the key and, if it is a certificate, its CA key too.
502  * "revoked_keys_file" may be a KRL or a one-per-line list of public keys.
503  */
504 int
sshkey_check_revoked(struct sshkey * key,const char * revoked_keys_file)505 sshkey_check_revoked(struct sshkey *key, const char *revoked_keys_file)
506 {
507 	int r;
508 
509 	r = ssh_krl_file_contains_key(revoked_keys_file, key);
510 	/* If this was not a KRL to begin with then continue below */
511 	if (r != SSH_ERR_KRL_BAD_MAGIC)
512 		return r;
513 
514 	/*
515 	 * If the file is not a KRL or we can't handle KRLs then attempt to
516 	 * parse the file as a flat list of keys.
517 	 */
518 	switch ((r = sshkey_in_file(key, revoked_keys_file, 0, 1))) {
519 	case 0:
520 		/* Key found => revoked */
521 		return SSH_ERR_KEY_REVOKED;
522 	case SSH_ERR_KEY_NOT_FOUND:
523 		/* Key not found => not revoked */
524 		return 0;
525 	default:
526 		/* Some other error occurred */
527 		return r;
528 	}
529 }
530 
531 /*
532  * Advanced *cpp past the end of key options, defined as the first unquoted
533  * whitespace character. Returns 0 on success or -1 on failure (e.g.
534  * unterminated quotes).
535  */
536 int
sshkey_advance_past_options(char ** cpp)537 sshkey_advance_past_options(char **cpp)
538 {
539 	char *cp = *cpp;
540 	int quoted = 0;
541 
542 	for (; *cp && (quoted || (*cp != ' ' && *cp != '\t')); cp++) {
543 		if (*cp == '\\' && cp[1] == '"')
544 			cp++;	/* Skip both */
545 		else if (*cp == '"')
546 			quoted = !quoted;
547 	}
548 	*cpp = cp;
549 	/* return failure for unterminated quotes */
550 	return (*cp == '\0' && quoted) ? -1 : 0;
551 }
552 
553