1/* $OpenBSD: auth2-pubkey.c,v 1.34 2013/02/14 21:35:59 djm Exp $ */
2/*
3 * Copyright (c) 2000 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/wait.h>
31
32#include <errno.h>
33#include <fcntl.h>
34#ifdef HAVE_PATHS_H
35# include <paths.h>
36#endif
37#include <pwd.h>
38#include <signal.h>
39#include <stdio.h>
40#include <stdarg.h>
41#include <string.h>
42#include <time.h>
43#include <unistd.h>
44
45#include "xmalloc.h"
46#include "ssh.h"
47#include "ssh2.h"
48#include "packet.h"
49#include "buffer.h"
50#include "log.h"
51#include "servconf.h"
52#include "compat.h"
53#include "key.h"
54#include "hostfile.h"
55#include "auth.h"
56#include "pathnames.h"
57#include "uidswap.h"
58#include "auth-options.h"
59#include "canohost.h"
60#ifdef GSSAPI
61#include "ssh-gss.h"
62#endif
63#include "monitor_wrap.h"
64#include "misc.h"
65#include "authfile.h"
66#include "match.h"
67
68/* import */
69extern ServerOptions options;
70extern u_char *session_id2;
71extern u_int session_id2_len;
72
73static int
74userauth_pubkey(Authctxt *authctxt)
75{
76	Buffer b;
77	Key *key = NULL;
78	char *pkalg;
79	u_char *pkblob, *sig;
80	u_int alen, blen, slen;
81	int have_sig, pktype;
82	int authenticated = 0;
83
84	if (!authctxt->valid) {
85		debug2("userauth_pubkey: disabled because of invalid user");
86		return 0;
87	}
88	have_sig = packet_get_char();
89	if (datafellows & SSH_BUG_PKAUTH) {
90		debug2("userauth_pubkey: SSH_BUG_PKAUTH");
91		/* no explicit pkalg given */
92		pkblob = packet_get_string(&blen);
93		buffer_init(&b);
94		buffer_append(&b, pkblob, blen);
95		/* so we have to extract the pkalg from the pkblob */
96		pkalg = buffer_get_string(&b, &alen);
97		buffer_free(&b);
98	} else {
99		pkalg = packet_get_string(&alen);
100		pkblob = packet_get_string(&blen);
101	}
102	pktype = key_type_from_name(pkalg);
103	if (pktype == KEY_UNSPEC) {
104		/* this is perfectly legal */
105		logit("userauth_pubkey: unsupported public key algorithm: %s",
106		    pkalg);
107		goto done;
108	}
109	key = key_from_blob(pkblob, blen);
110	if (key == NULL) {
111		error("userauth_pubkey: cannot decode key: %s", pkalg);
112		goto done;
113	}
114	if (key->type != pktype) {
115		error("userauth_pubkey: type mismatch for decoded key "
116		    "(received %d, expected %d)", key->type, pktype);
117		goto done;
118	}
119	if (have_sig) {
120		sig = packet_get_string(&slen);
121		packet_check_eom();
122		buffer_init(&b);
123		if (datafellows & SSH_OLD_SESSIONID) {
124			buffer_append(&b, session_id2, session_id2_len);
125		} else {
126			buffer_put_string(&b, session_id2, session_id2_len);
127		}
128		/* reconstruct packet */
129		buffer_put_char(&b, SSH2_MSG_USERAUTH_REQUEST);
130		buffer_put_cstring(&b, authctxt->user);
131		buffer_put_cstring(&b,
132		    datafellows & SSH_BUG_PKSERVICE ?
133		    "ssh-userauth" :
134		    authctxt->service);
135		if (datafellows & SSH_BUG_PKAUTH) {
136			buffer_put_char(&b, have_sig);
137		} else {
138			buffer_put_cstring(&b, "publickey");
139			buffer_put_char(&b, have_sig);
140			buffer_put_cstring(&b, pkalg);
141		}
142		buffer_put_string(&b, pkblob, blen);
143#ifdef DEBUG_PK
144		buffer_dump(&b);
145#endif
146		/* test for correct signature */
147		authenticated = 0;
148		if (PRIVSEP(user_key_allowed(authctxt->pw, key)) &&
149		    PRIVSEP(key_verify(key, sig, slen, buffer_ptr(&b),
150		    buffer_len(&b))) == 1)
151			authenticated = 1;
152		buffer_free(&b);
153		xfree(sig);
154	} else {
155		debug("test whether pkalg/pkblob are acceptable");
156		packet_check_eom();
157
158		/* XXX fake reply and always send PK_OK ? */
159		/*
160		 * XXX this allows testing whether a user is allowed
161		 * to login: if you happen to have a valid pubkey this
162		 * message is sent. the message is NEVER sent at all
163		 * if a user is not allowed to login. is this an
164		 * issue? -markus
165		 */
166		if (PRIVSEP(user_key_allowed(authctxt->pw, key))) {
167			packet_start(SSH2_MSG_USERAUTH_PK_OK);
168			packet_put_string(pkalg, alen);
169			packet_put_string(pkblob, blen);
170			packet_send();
171			packet_write_wait();
172			authctxt->postponed = 1;
173		}
174	}
175	if (authenticated != 1)
176		auth_clear_options();
177done:
178	debug2("userauth_pubkey: authenticated %d pkalg %s", authenticated, pkalg);
179	if (key != NULL)
180		key_free(key);
181	xfree(pkalg);
182	xfree(pkblob);
183	return authenticated;
184}
185
186static int
187match_principals_option(const char *principal_list, struct KeyCert *cert)
188{
189	char *result;
190	u_int i;
191
192	/* XXX percent_expand() sequences for authorized_principals? */
193
194	for (i = 0; i < cert->nprincipals; i++) {
195		if ((result = match_list(cert->principals[i],
196		    principal_list, NULL)) != NULL) {
197			debug3("matched principal from key options \"%.100s\"",
198			    result);
199			xfree(result);
200			return 1;
201		}
202	}
203	return 0;
204}
205
206static int
207match_principals_file(char *file, struct passwd *pw, struct KeyCert *cert)
208{
209	FILE *f;
210	char line[SSH_MAX_PUBKEY_BYTES], *cp, *ep, *line_opts;
211	u_long linenum = 0;
212	u_int i;
213
214	temporarily_use_uid(pw);
215	debug("trying authorized principals file %s", file);
216	if ((f = auth_openprincipals(file, pw, options.strict_modes)) == NULL) {
217		restore_uid();
218		return 0;
219	}
220	while (read_keyfile_line(f, file, line, sizeof(line), &linenum) != -1) {
221		/* Skip leading whitespace. */
222		for (cp = line; *cp == ' ' || *cp == '\t'; cp++)
223			;
224		/* Skip blank and comment lines. */
225		if ((ep = strchr(cp, '#')) != NULL)
226			*ep = '\0';
227		if (!*cp || *cp == '\n')
228			continue;
229		/* Trim trailing whitespace. */
230		ep = cp + strlen(cp) - 1;
231		while (ep > cp && (*ep == '\n' || *ep == ' ' || *ep == '\t'))
232			*ep-- = '\0';
233		/*
234		 * If the line has internal whitespace then assume it has
235		 * key options.
236		 */
237		line_opts = NULL;
238		if ((ep = strrchr(cp, ' ')) != NULL ||
239		    (ep = strrchr(cp, '\t')) != NULL) {
240			for (; *ep == ' ' || *ep == '\t'; ep++)
241				;
242			line_opts = cp;
243			cp = ep;
244		}
245		for (i = 0; i < cert->nprincipals; i++) {
246			if (strcmp(cp, cert->principals[i]) == 0) {
247				debug3("matched principal \"%.100s\" "
248				    "from file \"%s\" on line %lu",
249				    cert->principals[i], file, linenum);
250				if (auth_parse_options(pw, line_opts,
251				    file, linenum) != 1)
252					continue;
253				fclose(f);
254				restore_uid();
255				return 1;
256			}
257		}
258	}
259	fclose(f);
260	restore_uid();
261	return 0;
262}
263
264/*
265 * Checks whether key is allowed in authorized_keys-format file,
266 * returns 1 if the key is allowed or 0 otherwise.
267 */
268static int
269check_authkeys_file(FILE *f, char *file, Key* key, struct passwd *pw)
270{
271	char line[SSH_MAX_PUBKEY_BYTES];
272	const char *reason;
273	int found_key = 0;
274	u_long linenum = 0;
275	Key *found;
276	char *fp;
277
278	found_key = 0;
279	found = key_new(key_is_cert(key) ? KEY_UNSPEC : key->type);
280
281	while (read_keyfile_line(f, file, line, sizeof(line), &linenum) != -1) {
282		char *cp, *key_options = NULL;
283
284		auth_clear_options();
285
286		/* Skip leading whitespace, empty and comment lines. */
287		for (cp = line; *cp == ' ' || *cp == '\t'; cp++)
288			;
289		if (!*cp || *cp == '\n' || *cp == '#')
290			continue;
291
292		if (key_read(found, &cp) != 1) {
293			/* no key?  check if there are options for this key */
294			int quoted = 0;
295			debug2("user_key_allowed: check options: '%s'", cp);
296			key_options = cp;
297			for (; *cp && (quoted || (*cp != ' ' && *cp != '\t')); cp++) {
298				if (*cp == '\\' && cp[1] == '"')
299					cp++;	/* Skip both */
300				else if (*cp == '"')
301					quoted = !quoted;
302			}
303			/* Skip remaining whitespace. */
304			for (; *cp == ' ' || *cp == '\t'; cp++)
305				;
306			if (key_read(found, &cp) != 1) {
307				debug2("user_key_allowed: advance: '%s'", cp);
308				/* still no key?  advance to next line*/
309				continue;
310			}
311		}
312		if (key_is_cert(key)) {
313			if (!key_equal(found, key->cert->signature_key))
314				continue;
315			if (auth_parse_options(pw, key_options, file,
316			    linenum) != 1)
317				continue;
318			if (!key_is_cert_authority)
319				continue;
320			fp = key_fingerprint(found, SSH_FP_MD5,
321			    SSH_FP_HEX);
322			debug("matching CA found: file %s, line %lu, %s %s",
323			    file, linenum, key_type(found), fp);
324			/*
325			 * If the user has specified a list of principals as
326			 * a key option, then prefer that list to matching
327			 * their username in the certificate principals list.
328			 */
329			if (authorized_principals != NULL &&
330			    !match_principals_option(authorized_principals,
331			    key->cert)) {
332				reason = "Certificate does not contain an "
333				    "authorized principal";
334 fail_reason:
335				xfree(fp);
336				error("%s", reason);
337				auth_debug_add("%s", reason);
338				continue;
339			}
340			if (key_cert_check_authority(key, 0, 0,
341			    authorized_principals == NULL ? pw->pw_name : NULL,
342			    &reason) != 0)
343				goto fail_reason;
344			if (auth_cert_options(key, pw) != 0) {
345				xfree(fp);
346				continue;
347			}
348			verbose("Accepted certificate ID \"%s\" "
349			    "signed by %s CA %s via %s", key->cert->key_id,
350			    key_type(found), fp, file);
351			xfree(fp);
352			found_key = 1;
353			break;
354		} else if (key_equal(found, key)) {
355			if (auth_parse_options(pw, key_options, file,
356			    linenum) != 1)
357				continue;
358			if (key_is_cert_authority)
359				continue;
360			found_key = 1;
361			debug("matching key found: file %s, line %lu",
362			    file, linenum);
363			fp = key_fingerprint(found, SSH_FP_MD5, SSH_FP_HEX);
364			verbose("Found matching %s key: %s",
365			    key_type(found), fp);
366			xfree(fp);
367			break;
368		}
369	}
370	key_free(found);
371	if (!found_key)
372		debug2("key not found");
373	return found_key;
374}
375
376/* Authenticate a certificate key against TrustedUserCAKeys */
377static int
378user_cert_trusted_ca(struct passwd *pw, Key *key)
379{
380	char *ca_fp, *principals_file = NULL;
381	const char *reason;
382	int ret = 0;
383
384	if (!key_is_cert(key) || options.trusted_user_ca_keys == NULL)
385		return 0;
386
387	ca_fp = key_fingerprint(key->cert->signature_key,
388	    SSH_FP_MD5, SSH_FP_HEX);
389
390	if (key_in_file(key->cert->signature_key,
391	    options.trusted_user_ca_keys, 1) != 1) {
392		debug2("%s: CA %s %s is not listed in %s", __func__,
393		    key_type(key->cert->signature_key), ca_fp,
394		    options.trusted_user_ca_keys);
395		goto out;
396	}
397	/*
398	 * If AuthorizedPrincipals is in use, then compare the certificate
399	 * principals against the names in that file rather than matching
400	 * against the username.
401	 */
402	if ((principals_file = authorized_principals_file(pw)) != NULL) {
403		if (!match_principals_file(principals_file, pw, key->cert)) {
404			reason = "Certificate does not contain an "
405			    "authorized principal";
406 fail_reason:
407			error("%s", reason);
408			auth_debug_add("%s", reason);
409			goto out;
410		}
411	}
412	if (key_cert_check_authority(key, 0, 1,
413	    principals_file == NULL ? pw->pw_name : NULL, &reason) != 0)
414		goto fail_reason;
415	if (auth_cert_options(key, pw) != 0)
416		goto out;
417
418	verbose("Accepted certificate ID \"%s\" signed by %s CA %s via %s",
419	    key->cert->key_id, key_type(key->cert->signature_key), ca_fp,
420	    options.trusted_user_ca_keys);
421	ret = 1;
422
423 out:
424	if (principals_file != NULL)
425		xfree(principals_file);
426	if (ca_fp != NULL)
427		xfree(ca_fp);
428	return ret;
429}
430
431/*
432 * Checks whether key is allowed in file.
433 * returns 1 if the key is allowed or 0 otherwise.
434 */
435static int
436user_key_allowed2(struct passwd *pw, Key *key, char *file)
437{
438	FILE *f;
439	int found_key = 0;
440
441	/* Temporarily use the user's uid. */
442	temporarily_use_uid(pw);
443
444	debug("trying public key file %s", file);
445	if ((f = auth_openkeyfile(file, pw, options.strict_modes)) != NULL) {
446		found_key = check_authkeys_file(f, file, key, pw);
447		fclose(f);
448	}
449
450	restore_uid();
451	return found_key;
452}
453
454/*
455 * Checks whether key is allowed in output of command.
456 * returns 1 if the key is allowed or 0 otherwise.
457 */
458static int
459user_key_command_allowed2(struct passwd *user_pw, Key *key)
460{
461	FILE *f;
462	int ok, found_key = 0;
463	struct passwd *pw;
464	struct stat st;
465	int status, devnull, p[2], i;
466	pid_t pid;
467	char *username, errmsg[512];
468
469	if (options.authorized_keys_command == NULL ||
470	    options.authorized_keys_command[0] != '/')
471		return 0;
472
473	if (options.authorized_keys_command_user == NULL) {
474		error("No user for AuthorizedKeysCommand specified, skipping");
475		return 0;
476	}
477
478	username = percent_expand(options.authorized_keys_command_user,
479	    "u", user_pw->pw_name, (char *)NULL);
480	pw = getpwnam(username);
481	if (pw == NULL) {
482		error("AuthorizedKeysCommandUser \"%s\" not found: %s",
483		    username, strerror(errno));
484		free(username);
485		return 0;
486	}
487	free(username);
488
489	temporarily_use_uid(pw);
490
491	if (stat(options.authorized_keys_command, &st) < 0) {
492		error("Could not stat AuthorizedKeysCommand \"%s\": %s",
493		    options.authorized_keys_command, strerror(errno));
494		goto out;
495	}
496	if (auth_secure_path(options.authorized_keys_command, &st, NULL, 0,
497	    errmsg, sizeof(errmsg)) != 0) {
498		error("Unsafe AuthorizedKeysCommand: %s", errmsg);
499		goto out;
500	}
501
502	if (pipe(p) != 0) {
503		error("%s: pipe: %s", __func__, strerror(errno));
504		goto out;
505	}
506
507	debug3("Running AuthorizedKeysCommand: \"%s %s\" as \"%s\"",
508	    options.authorized_keys_command, user_pw->pw_name, pw->pw_name);
509
510	/*
511	 * Don't want to call this in the child, where it can fatal() and
512	 * run cleanup_exit() code.
513	 */
514	restore_uid();
515
516	switch ((pid = fork())) {
517	case -1: /* error */
518		error("%s: fork: %s", __func__, strerror(errno));
519		close(p[0]);
520		close(p[1]);
521		return 0;
522	case 0: /* child */
523		for (i = 0; i < NSIG; i++)
524			signal(i, SIG_DFL);
525
526		if ((devnull = open(_PATH_DEVNULL, O_RDWR)) == -1) {
527			error("%s: open %s: %s", __func__, _PATH_DEVNULL,
528			    strerror(errno));
529			_exit(1);
530		}
531		/* Keep stderr around a while longer to catch errors */
532		if (dup2(devnull, STDIN_FILENO) == -1 ||
533		    dup2(p[1], STDOUT_FILENO) == -1) {
534			error("%s: dup2: %s", __func__, strerror(errno));
535			_exit(1);
536		}
537		closefrom(STDERR_FILENO + 1);
538
539		/* Don't use permanently_set_uid() here to avoid fatal() */
540		if (setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) != 0) {
541			error("setresgid %u: %s", (u_int)pw->pw_gid,
542			    strerror(errno));
543			_exit(1);
544		}
545		if (setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid) != 0) {
546			error("setresuid %u: %s", (u_int)pw->pw_uid,
547			    strerror(errno));
548			_exit(1);
549		}
550		/* stdin is pointed to /dev/null at this point */
551		if (dup2(STDIN_FILENO, STDERR_FILENO) == -1) {
552			error("%s: dup2: %s", __func__, strerror(errno));
553			_exit(1);
554		}
555
556		execl(options.authorized_keys_command,
557		    options.authorized_keys_command, user_pw->pw_name, NULL);
558
559		error("AuthorizedKeysCommand %s exec failed: %s",
560		    options.authorized_keys_command, strerror(errno));
561		_exit(127);
562	default: /* parent */
563		break;
564	}
565
566	temporarily_use_uid(pw);
567
568	close(p[1]);
569	if ((f = fdopen(p[0], "r")) == NULL) {
570		error("%s: fdopen: %s", __func__, strerror(errno));
571		close(p[0]);
572		/* Don't leave zombie child */
573		kill(pid, SIGTERM);
574		while (waitpid(pid, NULL, 0) == -1 && errno == EINTR)
575			;
576		goto out;
577	}
578	ok = check_authkeys_file(f, options.authorized_keys_command, key, pw);
579	fclose(f);
580
581	while (waitpid(pid, &status, 0) == -1) {
582		if (errno != EINTR) {
583			error("%s: waitpid: %s", __func__, strerror(errno));
584			goto out;
585		}
586	}
587	if (WIFSIGNALED(status)) {
588		error("AuthorizedKeysCommand %s exited on signal %d",
589		    options.authorized_keys_command, WTERMSIG(status));
590		goto out;
591	} else if (WEXITSTATUS(status) != 0) {
592		error("AuthorizedKeysCommand %s returned status %d",
593		    options.authorized_keys_command, WEXITSTATUS(status));
594		goto out;
595	}
596	found_key = ok;
597 out:
598	restore_uid();
599	return found_key;
600}
601
602/*
603 * Check whether key authenticates and authorises the user.
604 */
605int
606user_key_allowed(struct passwd *pw, Key *key)
607{
608	u_int success, i;
609	char *file;
610
611	if (auth_key_is_revoked(key))
612		return 0;
613	if (key_is_cert(key) && auth_key_is_revoked(key->cert->signature_key))
614		return 0;
615
616	success = user_cert_trusted_ca(pw, key);
617	if (success)
618		return success;
619
620	success = user_key_command_allowed2(pw, key);
621	if (success > 0)
622		return success;
623
624	for (i = 0; !success && i < options.num_authkeys_files; i++) {
625
626		if (strcasecmp(options.authorized_keys_files[i], "none") == 0)
627			continue;
628		file = expand_authorized_keys(
629		    options.authorized_keys_files[i], pw);
630
631		success = user_key_allowed2(pw, key, file);
632		xfree(file);
633	}
634
635	return success;
636}
637
638Authmethod method_pubkey = {
639	"publickey",
640	userauth_pubkey,
641	&options.pubkey_authentication
642};
643