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