From 88d1ce29a9d37f0a9770414475a6398d1faa2f68 Mon Sep 17 00:00:00 2001 From: Markus Friedl Date: Sat, 10 Nov 2012 16:24:59 +0100 Subject: [PATCH] sync cvs as of 2012-11-10 --- regress/CVS/Entries | 8 +- regress/cert-userkey.sh | 7 +- ssh/CVS/Entries | 138 +++++++++++++------------- ssh/auth-rsa.c | 4 +- ssh/auth.c | 53 +++++++--- ssh/auth.h | 11 ++- ssh/auth1.c | 7 +- ssh/auth2-pubkey.c | 208 ++++++++++++++++++++++++++++++++++++---- ssh/auth2.c | 203 ++++++++++++++++++++++++++++++++++++--- ssh/monitor.c | 35 ++++++- ssh/servconf.c | 54 ++++++++++- ssh/servconf.h | 9 +- ssh/sshd.c | 39 +++++++- ssh/sshd_config | 5 +- ssh/sshd_config.5 | 46 ++++++++- 15 files changed, 692 insertions(+), 135 deletions(-) diff --git a/regress/CVS/Entries b/regress/CVS/Entries index cf7f928..19816b9 100644 --- a/regress/CVS/Entries +++ b/regress/CVS/Entries @@ -9,7 +9,6 @@ /broken-pipe.sh/1.4/Mon Mar 26 21:06:58 2012// /brokenkeys.sh/1.1/Mon Mar 26 21:06:58 2012// /cert-hostkey.sh/1.6/Mon Mar 26 21:06:58 2012// -/cert-userkey.sh/1.8/Mon Mar 26 21:06:58 2012// /cfgmatch.sh/1.6/Mon Mar 26 21:06:58 2012// /conch-ciphers.sh/1.2/Mon Mar 26 21:06:58 2012// /connect.sh/1.4/Mon Mar 26 21:06:58 2012// @@ -62,8 +61,9 @@ /connect-privsep.sh/1.4/Thu Aug 9 18:41:57 2012// /forwarding.sh/1.8/Thu Aug 9 18:41:57 2012// /sftp-cmds.sh/1.12/Thu Aug 9 18:41:57 2012// -/cipher-speed.sh/1.6/Fri Oct 5 12:38:36 2012// -/multiplex.sh/1.17/Fri Oct 5 12:38:36 2012// /sshd-log-wrapper.sh/1.2/Fri Sep 21 10:04:07 2012// -/try-ciphers.sh/1.15/Fri Oct 5 12:38:36 2012// +/cert-userkey.sh/1.9/Sat Nov 10 15:25:43 2012// +/cipher-speed.sh/1.6/Tue Oct 9 21:24:24 2012// +/multiplex.sh/1.17/Tue Oct 9 21:24:24 2012// +/try-ciphers.sh/1.15/Tue Oct 9 21:24:24 2012// D diff --git a/regress/cert-userkey.sh b/regress/cert-userkey.sh index 1384fde..85e13b1 100644 --- a/regress/cert-userkey.sh +++ b/regress/cert-userkey.sh @@ -1,4 +1,4 @@ -# $OpenBSD: cert-userkey.sh,v 1.8 2011/05/17 07:13:31 djm Exp $ +# $OpenBSD: cert-userkey.sh,v 1.9 2012/10/19 05:10:42 djm Exp $ # Placed in the Public Domain. tid="certified user keys" @@ -16,9 +16,8 @@ for ktype in rsa dsa ecdsa ; do ${SSHKEYGEN} -q -N '' -t ${ktype} \ -f $OBJ/cert_user_key_${ktype} || \ fail "ssh-keygen of cert_user_key_${ktype} failed" - ${SSHKEYGEN} -q -s $OBJ/user_ca_key -I \ - "regress user key for $USER" \ - -n ${USER},mekmitasdigoat $OBJ/cert_user_key_${ktype} || + ${SSHKEYGEN} -q -s $OBJ/user_ca_key -I "regress user key for $USER" \ + -z $$ -n ${USER},mekmitasdigoat $OBJ/cert_user_key_${ktype} || fail "couldn't sign cert_user_key_${ktype}" # v00 ecdsa certs do not exist test "${ktype}" = "ecdsa" && continue diff --git a/ssh/CVS/Entries b/ssh/CVS/Entries index 931faa8..4c5721a 100644 --- a/ssh/CVS/Entries +++ b/ssh/CVS/Entries @@ -1,7 +1,6 @@ /Makefile/1.15/Tue Feb 9 08:55:31 2010// /Makefile.inc/1.39/Fri Oct 1 23:10:48 2010// /OVERVIEW/1.11/Thu Aug 3 03:34:41 2006// -/auth.h/1.69/Mon May 23 03:30:07 2011// /compat.h/1.43/Fri Sep 23 07:45:05 2011// /dispatch.c/1.22/Fri Oct 31 15:05:34 2008// /dispatch.h/1.11/Thu Apr 20 09:27:09 2006// @@ -27,8 +26,6 @@ D/sshd//// /auth-options.c/1.56/Fri Jan 6 10:00:44 2012// /auth-options.h/1.20/Fri Jan 6 10:00:44 2012// /auth-rh-rsa.c/1.43/Fri Jan 6 10:00:44 2012// -/auth-rsa.c/1.80/Fri Jan 6 10:00:44 2012// -/auth1.c/1.75/Fri Jan 6 10:00:44 2012// /auth2-chall.c/1.34/Fri Jan 6 10:00:45 2012// /auth2-gss.c/1.17/Fri Jan 6 10:00:45 2012// /auth2-hostbased.c/1.14/Fri Jan 6 10:00:45 2012// @@ -36,8 +33,6 @@ D/sshd//// /auth2-kbdint.c/1.5/Fri Jan 6 10:00:45 2012// /auth2-none.c/1.16/Fri Jan 6 10:00:45 2012// /auth2-passwd.c/1.9/Fri Jan 6 10:00:45 2012// -/auth2-pubkey.c/1.30/Fri Jan 6 10:00:45 2012// -/auth2.c/1.124/Fri Jan 6 10:00:45 2012// /authfd.c/1.86/Fri Jan 6 10:00:45 2012// /authfd.h/1.37/Fri Jan 6 10:00:45 2012// /authfile.h/1.16/Fri Jan 6 10:00:45 2012// @@ -85,65 +80,36 @@ D/sshd//// /PROTOCOL.agent/1.6/Mon Mar 19 16:04:54 2012// /README/1.7/Mon Mar 19 16:04:54 2012// /addrmatch.c/1.6/Result of merge// -/atomicio.c/1.26/Mon Mar 19 16:04:54 2012// -/atomicio.h/1.11/Mon Mar 19 16:04:54 2012// /auth-bsdauth.c/1.11/Mon Mar 19 16:04:54 2012// -/auth-chall.c/1.12/Mon Mar 19 16:04:54 2012// /auth-krb5.c/1.19/Mon Mar 19 16:04:54 2012// /auth-passwd.c/1.43/Mon Mar 19 16:04:54 2012// /auth-rhosts.c/1.44/Mon Mar 19 16:04:54 2012// -/auth.c/1.96/Result of merge// /channels.c/1.318/Result of merge// /channels.h/1.111/Result of merge// -/cleanup.c/1.5/Mon Mar 19 16:04:54 2012// -/compress.c/1.26/Mon Jul 30 18:22:06 2012// -/compress.h/1.12/Mon Jul 30 18:22:06 2012// -/crc32.c/1.11/Mon Mar 19 16:04:54 2012// -/crc32.h/1.15/Mon Mar 19 16:04:54 2012// /dns.h/1.12/Result of merge// -/fatal.c/1.7/Mon Mar 19 16:04:54 2012// -/groupaccess.c/1.13/Mon Mar 19 16:04:54 2012// -/groupaccess.h/1.8/Mon Mar 19 16:04:54 2012// /gss-genr.c/1.20/Wed Mar 21 09:25:01 2012// /gss-serv-krb5.c/1.7/Mon Mar 19 16:04:54 2012// /gss-serv.c/1.23/Mon Mar 19 16:04:54 2012// -/jpake.h/1.2/Mon Mar 19 16:04:54 2012// /key.h/1.34/Result of merge// -/match.h/1.15/Mon Mar 19 16:04:54 2012// /monitor.h/1.16/Mon Mar 19 16:04:54 2012// -/monitor_fdpass.c/1.19/Mon Mar 19 16:04:54 2012// -/monitor_fdpass.h/1.4/Mon Mar 19 16:04:54 2012// -/monitor_mm.c/1.16/Mon Mar 19 16:04:54 2012// -/monitor_mm.h/1.5/Mon Mar 19 16:04:54 2012// /msg.c/1.15/Mon Mar 19 16:04:54 2012// /msg.h/1.4/Mon Mar 19 16:04:54 2012// /nchan.ms/1.8/Mon Mar 19 16:04:54 2012// /nchan2.ms/1.4/Mon Mar 19 16:04:54 2012// -/pathnames.h/1.22/Wed Apr 11 09:29:54 2012// -/pkcs11.h/1.2/Mon Mar 19 16:04:54 2012// -/progressmeter.c/1.37/Mon Mar 19 16:04:54 2012// -/progressmeter.h/1.2/Mon Mar 19 16:04:54 2012// -/readpass.c/1.48/Mon Mar 19 16:04:54 2012// /roaming.h/1.6/Mon Mar 19 16:04:54 2012// /roaming_common.c/1.9/Mon Mar 19 16:04:54 2012// -/sandbox-rlimit.c/1.3/Mon Mar 19 16:04:54 2012// /schnorr.c/1.5/Wed Mar 21 09:25:01 2012// -/schnorr.h/1.1/Mon Mar 19 16:04:54 2012// /scp.1/1.58/Mon Mar 19 16:04:54 2012// -/scp.c/1.171/Mon Mar 19 16:04:54 2012// /serverloop.c/1.162/Result of merge// /serverloop.h/1.6/Mon Mar 19 16:04:54 2012// /session.c/1.260/Result of merge// /session.h/1.30/Mon Mar 19 16:04:54 2012// -/sftp-client.h/1.20/Wed Mar 21 09:25:01 2012// /sftp-common.c/1.23/Mon Mar 19 16:04:54 2012// /sftp-common.h/1.11/Mon Mar 19 16:04:54 2012// /sftp-glob.c/1.23/Mon Mar 19 16:04:54 2012// -/sftp-server-main.c/1.4/Mon Mar 19 16:04:54 2012// /sftp-server.8/1.19/Mon Mar 19 16:04:54 2012// /sftp-server.c/1.94/Wed Mar 21 09:25:01 2012// /sftp.1/1.91/Mon Mar 19 16:04:54 2012// -/sftp.h/1.9/Mon Mar 19 16:04:54 2012// /ssh-add.1/1.56/Mon Mar 19 16:04:54 2012// /ssh-agent.1/1.53/Mon Mar 19 16:04:54 2012// /ssh-gss.h/1.10/Mon Mar 19 16:04:54 2012// @@ -151,25 +117,10 @@ D/sshd//// /ssh-keysign.8/1.12/Mon Mar 19 16:04:54 2012// /ssh-pkcs11-client.c/1.3/Result of merge// /ssh-pkcs11-helper.8/1.3/Mon Mar 19 16:04:54 2012// -/ssh-sandbox.h/1.1/Wed Apr 11 09:29:17 2012// -/ssh.h/1.79/Mon Mar 19 16:04:54 2012// -/ssh1.h/1.6/Mon Mar 19 16:04:54 2012// -/ssh2.h/1.14/Mon Mar 19 16:04:54 2012// /ssh_config/1.26/Mon Mar 19 16:04:54 2012// /sshconnect2.c/1.189/Result of merge// /sshlogin.c/1.27/Mon Mar 19 16:04:54 2012// -/sshlogin.h/1.8/Mon Mar 19 16:04:54 2012// -/sshpty.c/1.28/Mon Mar 19 16:04:54 2012// -/sshpty.h/1.12/Mon Mar 19 16:04:54 2012// -/sshtty.c/1.14/Mon Mar 19 16:04:54 2012// /ttymodes.c/1.29/Mon Mar 19 16:04:54 2012// -/ttymodes.h/1.14/Mon Mar 19 16:04:54 2012// -/uidswap.c/1.35/Mon Mar 19 16:04:54 2012// -/uidswap.h/1.13/Mon Mar 19 16:04:54 2012// -/uuencode.c/1.26/Mon Mar 19 16:04:54 2012// -/uuencode.h/1.14/Mon Mar 19 16:04:54 2012// -/xmalloc.c/1.27/Mon Mar 19 16:04:54 2012// -/xmalloc.h/1.13/Wed Mar 21 09:25:01 2012// /PROTOCOL.certkeys/1.9/Thu Aug 9 18:41:57 2012// /PROTOCOL.mux/1.9/Thu Aug 9 18:41:57 2012// /authfile.c/1.93/Result of merge// @@ -180,38 +131,87 @@ D/sshd//// /jpake.c/1.7/Thu Aug 9 18:41:57 2012// /key.c/1.99/Result of merge// /log.c/1.43/Result of merge// -/moduli.c/1.26/Thu Aug 9 18:41:57 2012// -/monitor.c/1.117/Result of merge// /mux.c/1.37/Result of merge// /packet.h/1.57/Result of merge// -/sandbox-systrace.c/1.6/Thu Aug 9 18:41:57 2012// -/servconf.c/1.230/Fri Sep 14 16:41:14 2012// -/servconf.h/1.103/Thu Aug 9 18:41:57 2012// /sftp-client.c/1.97/Thu Aug 9 18:41:57 2012// /ssh-keyscan.1/1.30/Thu Aug 9 18:41:57 2012// /ssh-pkcs11-helper.c/1.4/Result of merge// /ssh.c/1.370/Result of merge// -/sshd.c/1.393/Result of merge// -/sshd_config/1.87/Thu Aug 9 18:41:57 2012// -/version.h/1.65/Thu Aug 9 18:41:57 2012// /kex.c/1.87/Result of merge// /sshconnect.c/1.236/Result of merge// -/bufaux.c/1.50/Tue Sep 25 15:24:07 2012// -/bufbn.c/1.6/Tue Sep 25 15:24:07 2012// -/bufec.c/1.1/Tue Sep 25 15:24:07 2012// -/buffer.c/1.32/Tue Sep 25 15:24:07 2012// -/buffer.h/1.21/Tue Sep 25 15:24:07 2012// -/log.h/1.19/Mon Sep 17 19:41:04 2012// -/roaming_serv.c/1.1/Thu Sep 20 21:50:47 2012// /ssh-keygen.1/1.110/Mon Sep 17 19:41:04 2012// /packet.c/1.177/Result of merge// /mac.c/1.19/Result of merge// /ssh-keygen.c/1.218/Result of merge// /monitor_wrap.c/1.74/Result of merge// -/myproposal.h/1.30/Thu Oct 4 13:30:40 2012// /sftp.c/1.141/Result of merge// -/ssh.1/1.330/Thu Oct 4 13:30:40 2012// -/ssh_config.5/1.158/Thu Oct 4 13:30:40 2012// -/sshd.8/1.267/Thu Oct 4 13:30:40 2012// -/sshd_config.5/1.145/Thu Oct 4 13:30:40 2012// /umac.h/1.2/Thu Oct 4 13:30:40 2012// +/atomicio.c/1.26/Fri Oct 12 15:53:54 2012// +/atomicio.h/1.11/Fri Oct 12 15:53:54 2012// +/auth-chall.c/1.12/Fri Oct 12 15:53:54 2012// +/auth-rsa.c/1.81/Result of merge// +/auth.c/1.97/Result of merge// +/auth.h/1.71/Result of merge// +/auth1.c/1.76/Result of merge// +/auth2-pubkey.c/1.32/Result of merge+Sat Nov 10 15:06:24 2012// +/auth2.c/1.125/Result of merge+Sat Nov 10 15:06:24 2012// +/bufaux.c/1.50/Fri Oct 12 15:53:55 2012// +/bufbn.c/1.6/Fri Oct 12 15:53:55 2012// +/bufec.c/1.1/Fri Oct 12 15:53:55 2012// +/buffer.c/1.32/Fri Oct 12 15:53:55 2012// +/buffer.h/1.21/Fri Oct 12 15:53:55 2012// +/cleanup.c/1.5/Fri Oct 12 15:53:55 2012// +/compress.c/1.26/Fri Oct 12 15:53:55 2012// +/compress.h/1.12/Fri Oct 12 15:53:55 2012// +/crc32.c/1.11/Fri Oct 12 15:53:55 2012// +/crc32.h/1.15/Fri Oct 12 15:53:55 2012// +/fatal.c/1.7/Fri Oct 12 15:53:55 2012// +/groupaccess.c/1.13/Fri Oct 12 15:53:55 2012// +/groupaccess.h/1.8/Fri Oct 12 15:53:55 2012// +/jpake.h/1.2/Fri Oct 12 15:53:55 2012// +/log.h/1.19/Fri Oct 12 15:53:55 2012// +/match.h/1.15/Fri Oct 12 15:53:55 2012// +/moduli.c/1.26/Fri Oct 12 15:53:55 2012// +/monitor.c/1.118/Result of merge// +/monitor_fdpass.c/1.19/Fri Oct 12 15:53:55 2012// +/monitor_fdpass.h/1.4/Fri Oct 12 15:53:55 2012// +/monitor_mm.c/1.16/Fri Oct 12 15:53:55 2012// +/monitor_mm.h/1.5/Fri Oct 12 15:53:55 2012// +/myproposal.h/1.30/Fri Oct 12 15:53:55 2012// +/pathnames.h/1.22/Fri Oct 12 15:53:55 2012// +/pkcs11.h/1.2/Fri Oct 12 15:53:55 2012// +/progressmeter.c/1.37/Fri Oct 12 15:53:55 2012// +/progressmeter.h/1.2/Fri Oct 12 15:53:55 2012// +/readpass.c/1.48/Fri Oct 12 15:53:55 2012// +/roaming_serv.c/1.1/Fri Oct 12 15:53:55 2012// +/sandbox-rlimit.c/1.3/Fri Oct 12 15:53:55 2012// +/sandbox-systrace.c/1.6/Fri Oct 12 15:53:55 2012// +/schnorr.h/1.1/Fri Oct 12 15:53:55 2012// +/scp.c/1.171/Fri Oct 12 15:53:55 2012// +/servconf.c/1.232/Result of merge+Sat Nov 10 15:06:25 2012// +/servconf.h/1.105/Result of merge// +/sftp-client.h/1.20/Fri Oct 12 15:53:55 2012// +/sftp-server-main.c/1.4/Fri Oct 12 15:53:55 2012// +/sftp.h/1.9/Fri Oct 12 15:53:55 2012// +/ssh-sandbox.h/1.1/Fri Oct 12 15:53:56 2012// +/ssh.1/1.330/Tue Oct 9 21:24:24 2012// +/ssh.h/1.79/Fri Oct 12 15:54:04 2012// +/ssh1.h/1.6/Fri Oct 12 15:53:56 2012// +/ssh2.h/1.14/Fri Oct 12 15:53:56 2012// +/ssh_config.5/1.158/Tue Oct 9 21:24:24 2012// +/sshd.8/1.267/Tue Oct 9 21:24:24 2012// +/sshd.c/1.396/Result of merge// +/sshd_config/1.88/Sat Nov 10 15:06:26 2012// +/sshd_config.5/1.149/Sat Nov 10 15:06:26 2012// +/sshlogin.h/1.8/Fri Oct 12 15:53:56 2012// +/sshpty.c/1.28/Fri Oct 12 15:53:56 2012// +/sshpty.h/1.12/Fri Oct 12 15:53:56 2012// +/sshtty.c/1.14/Fri Oct 12 15:53:56 2012// +/ttymodes.h/1.14/Fri Oct 12 15:53:56 2012// +/uidswap.c/1.35/Fri Oct 12 15:53:56 2012// +/uidswap.h/1.13/Fri Oct 12 15:53:56 2012// +/uuencode.c/1.26/Fri Oct 12 15:53:56 2012// +/uuencode.h/1.14/Fri Oct 12 15:53:56 2012// +/version.h/1.65/Fri Oct 12 15:53:56 2012// +/xmalloc.c/1.27/Fri Oct 12 15:53:56 2012// +/xmalloc.h/1.13/Fri Oct 12 15:53:56 2012// diff --git a/ssh/auth-rsa.c b/ssh/auth-rsa.c index 41c6a15..51142ad 100644 --- a/ssh/auth-rsa.c +++ b/ssh/auth-rsa.c @@ -1,4 +1,4 @@ -/* $OpenBSD: auth-rsa.c,v 1.80 2011/05/23 03:30:07 djm Exp $ */ +/* $OpenBSD: auth-rsa.c,v 1.81 2012/10/30 21:29:54 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -280,6 +280,8 @@ auth_rsa_key_allowed(struct passwd *pw, BIGNUM *client_n, temporarily_use_uid(pw); for (i = 0; !allowed && i < options.num_authkeys_files; i++) { + if (strcasecmp(options.authorized_keys_files[i], "none") == 0) + continue; file = expand_authorized_keys( options.authorized_keys_files[i], pw); allowed = rsa_key_allowed_in_file(pw, file, client_n, rkey); diff --git a/ssh/auth.c b/ssh/auth.c index 745b2e2..7edb3ba 100644 --- a/ssh/auth.c +++ b/ssh/auth.c @@ -1,4 +1,4 @@ -/* $OpenBSD: auth.c,v 1.96 2012/05/13 01:42:32 dtucker Exp $ */ +/* $OpenBSD: auth.c,v 1.97 2012/10/30 21:29:54 djm Exp $ */ /* * Copyright (c) 2000 Markus Friedl. All rights reserved. * @@ -322,41 +322,42 @@ check_key_in_hostfiles(struct passwd *pw, struct sshkey *key, const char *host, return host_status; } - /* - * Check a given file for security. This is defined as all components + * Check a given path for security. This is defined as all components * of the path to the file must be owned by either the owner of * of the file or root and no directories must be group or world writable. * * XXX Should any specific check be done for sym links ? * - * Takes an open file descriptor, the file name, a uid and and + * Takes an the file name, its stat information (preferably from fstat() to + * avoid races), the uid of the expected owner, their home directory and an * error buffer plus max size as arguments. * * Returns 0 on success and -1 on failure */ -static int -secure_filename(FILE *f, const char *file, struct passwd *pw, - char *err, size_t errlen) +int +auth_secure_path(const char *name, struct stat *stp, const char *pw_dir, + uid_t uid, char *err, size_t errlen) { - uid_t uid = pw->pw_uid; char buf[MAXPATHLEN], homedir[MAXPATHLEN]; char *cp; int comparehome = 0; struct stat st; - if (realpath(file, buf) == NULL) { - snprintf(err, errlen, "realpath %s failed: %s", file, + if (realpath(name, buf) == NULL) { + snprintf(err, errlen, "realpath %s failed: %s", name, strerror(errno)); return -1; } - if (realpath(pw->pw_dir, homedir) != NULL) + if (pw_dir != NULL && realpath(pw_dir, homedir) != NULL) comparehome = 1; - /* check the open file to avoid races */ - if (fstat(fileno(f), &st) < 0 || - (st.st_uid != 0 && st.st_uid != uid) || - (st.st_mode & 022) != 0) { + if (!S_ISREG(stp->st_mode)) { + snprintf(err, errlen, "%s is not a regular file", buf); + return -1; + } + if ((stp->st_uid != 0 && stp->st_uid != uid) || + (stp->st_mode & 022) != 0) { snprintf(err, errlen, "bad ownership or modes for file %s", buf); return -1; @@ -392,6 +393,28 @@ secure_filename(FILE *f, const char *file, struct passwd *pw, return 0; } +/* + * Version of secure_path() that accepts an open file descriptor to + * avoid races. + * + * Returns 0 on success and -1 on failure + */ +static int +secure_filename(FILE *f, const char *file, struct passwd *pw, + char *err, size_t errlen) +{ + char buf[MAXPATHLEN]; + struct stat st; + + /* check the open file to avoid races */ + if (fstat(fileno(f), &st) < 0) { + snprintf(err, errlen, "cannot stat file %s: %s", + buf, strerror(errno)); + return -1; + } + return auth_secure_path(file, &st, pw->pw_dir, pw->pw_uid, err, errlen); +} + static FILE * auth_openfile(const char *file, struct passwd *pw, int strict_modes, int log_missing, char *file_type) diff --git a/ssh/auth.h b/ssh/auth.h index faf080d..164f6d3 100644 --- a/ssh/auth.h +++ b/ssh/auth.h @@ -1,4 +1,4 @@ -/* $OpenBSD: auth.h,v 1.69 2011/05/23 03:30:07 djm Exp $ */ +/* $OpenBSD: auth.h,v 1.71 2012/11/04 11:09:15 djm Exp $ */ /* * Copyright (c) 2000 Markus Friedl. All rights reserved. @@ -58,6 +58,8 @@ struct Authctxt { void *kbdintctxt; void *jpake_ctx; auth_session_t *as; + char **auth_methods; /* modified from server config */ + u_int num_auth_methods; #ifdef KRB5 krb5_context krb5_ctx; krb5_ccache krb5_fwd_ccache; @@ -114,6 +116,10 @@ int hostbased_key_allowed(struct passwd *, const char *, char *, struct sshkey *); int user_key_allowed(struct passwd *, struct sshkey *); +struct stat; +int auth_secure_path(const char *, struct stat *, const char *, uid_t, + char *, size_t); + #ifdef KRB5 int auth_krb5(Authctxt *authctxt, krb5_data *auth, char **client, krb5_data *); int auth_krb5_tgt(Authctxt *authctxt, krb5_data *tgt); @@ -129,6 +135,9 @@ void userauth_finish(struct ssh *, int, char *); int auth_root_allowed(char *); char *auth2_read_banner(void); +int auth2_methods_valid(const char *, int); +int auth2_update_methods_lists(Authctxt *, const char *); +int auth2_setup_methods_lists(Authctxt *); void privsep_challenge_enable(void); diff --git a/ssh/auth1.c b/ssh/auth1.c index 5e5621c..95b1cac 100644 --- a/ssh/auth1.c +++ b/ssh/auth1.c @@ -1,4 +1,4 @@ -/* $OpenBSD: auth1.c,v 1.75 2010/08/31 09:58:37 djm Exp $ */ +/* $OpenBSD: auth1.c,v 1.76 2012/11/04 11:09:15 djm Exp $ */ /* * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland * All rights reserved @@ -357,6 +357,11 @@ do_authentication(struct ssh *ssh) authctxt->pw = fakepw(); } + /* Configuration may have changed as a result of Match */ + if (options.num_auth_methods != 0) + fatal("AuthenticationMethods is not supported with SSH " + "protocol 1"); + setproctitle("%s%s", authctxt->valid ? user : "unknown", use_privsep ? " [net]" : ""); diff --git a/ssh/auth2-pubkey.c b/ssh/auth2-pubkey.c index f2c03f7..1515e1e 100644 --- a/ssh/auth2-pubkey.c +++ b/ssh/auth2-pubkey.c @@ -1,4 +1,4 @@ -/* $OpenBSD: auth2-pubkey.c,v 1.30 2011/09/25 05:44:47 djm Exp $ */ +/* $OpenBSD: auth2-pubkey.c,v 1.32 2012/11/04 10:38:43 djm Exp $ */ /* * Copyright (c) 2000 Markus Friedl. All rights reserved. * @@ -26,9 +26,13 @@ #include #include +#include +#include #include +#include #include +#include #include #include #include @@ -279,31 +283,22 @@ match_principals_file(char *file, struct passwd *pw, struct sshkey_cert *cert) fclose(f); restore_uid(); return 0; -} +} -/* return 1 if user allows given key */ +/* + * Checks whether key is allowed in authorized_keys-format file, + * returns 1 if the key is allowed or 0 otherwise. + */ static int -user_key_allowed2(struct passwd *pw, struct sshkey *key, char *file) +check_authkeys_file(FILE *f, char *file, struct sshkey *key, struct passwd *pw) { char line[SSH_MAX_PUBKEY_BYTES]; const char *reason; int found_key = 0; - FILE *f; u_long linenum = 0; struct sshkey *found = NULL; char *fp; - /* Temporarily use the user's uid. */ - temporarily_use_uid(pw); - - debug("trying public key file %s", file); - f = auth_openkeyfile(file, pw, options.strict_modes); - - if (!f) { - restore_uid(); - return 0; - } - found_key = 0; found = sshkey_new(sshkey_is_cert(key) ? KEY_UNSPEC : key->type); if (found == NULL) @@ -399,8 +394,6 @@ user_key_allowed2(struct passwd *pw, struct sshkey *key, char *file) } } done: - restore_uid(); - fclose(f); if (found != NULL) sshkey_free(found); if (!found_key) @@ -470,7 +463,176 @@ user_cert_trusted_ca(struct passwd *pw, struct sshkey *key) return ret; } -/* check whether given key is in .ssh/authorized_keys* */ +/* + * Checks whether key is allowed in file. + * returns 1 if the key is allowed or 0 otherwise. + */ +static int +user_key_allowed2(struct passwd *pw, struct sshkey *key, char *file) +{ + FILE *f; + int found_key = 0; + + /* Temporarily use the user's uid. */ + temporarily_use_uid(pw); + + debug("trying public key file %s", file); + if ((f = auth_openkeyfile(file, pw, options.strict_modes)) != NULL) { + found_key = check_authkeys_file(f, file, key, pw); + fclose(f); + } + + restore_uid(); + return found_key; +} + +/* + * Checks whether key is allowed in output of command. + * returns 1 if the key is allowed or 0 otherwise. + */ +static int +user_key_command_allowed2(struct passwd *user_pw, struct sshkey *key) +{ + FILE *f; + int ok, found_key = 0; + struct passwd *pw; + struct stat st; + int status, devnull, p[2], i; + pid_t pid; + char *username, errmsg[512]; + + if (options.authorized_keys_command == NULL || + options.authorized_keys_command[0] != '/') + return 0; + + if (options.authorized_keys_command_user == NULL) { + error("No user for AuthorizedKeysCommand specified, skipping"); + return 0; + } + + username = percent_expand(options.authorized_keys_command_user, + "u", user_pw->pw_name, (char *)NULL); + pw = getpwnam(username); + if (pw == NULL) { + error("AuthorizedKeyCommandUser \"%s\" not found: %s", + options.authorized_keys_command, strerror(errno)); + free(username); + return 0; + } + free(username); + + temporarily_use_uid(pw); + + if (stat(options.authorized_keys_command, &st) < 0) { + error("Could not stat AuthorizedKeysCommand \"%s\": %s", + options.authorized_keys_command, strerror(errno)); + goto out; + } + if (auth_secure_path(options.authorized_keys_command, &st, NULL, 0, + errmsg, sizeof(errmsg)) != 0) { + error("Unsafe AuthorizedKeysCommand: %s", errmsg); + goto out; + } + + if (pipe(p) != 0) { + error("%s: pipe: %s", __func__, strerror(errno)); + goto out; + } + + debug3("Running AuthorizedKeysCommand: \"%s\" as \"%s\"", + options.authorized_keys_command, pw->pw_name); + + /* + * Don't want to call this in the child, where it can fatal() and + * run cleanup_exit() code. + */ + restore_uid(); + + switch ((pid = fork())) { + case -1: /* error */ + error("%s: fork: %s", __func__, strerror(errno)); + close(p[0]); + close(p[1]); + return 0; + case 0: /* child */ + for (i = 0; i < NSIG; i++) + signal(i, SIG_DFL); + + closefrom(STDERR_FILENO + 1); + /* Don't use permanently_set_uid() here to avoid fatal() */ + if (setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) != 0) { + error("setresgid %u: %s", (u_int)pw->pw_gid, + strerror(errno)); + _exit(1); + } + if (setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid) != 0) { + error("setresuid %u: %s", (u_int)pw->pw_uid, + strerror(errno)); + _exit(1); + } + + close(p[0]); + if ((devnull = open(_PATH_DEVNULL, O_RDWR)) == -1) { + error("%s: open %s: %s", __func__, _PATH_DEVNULL, + strerror(errno)); + _exit(1); + } + if (dup2(devnull, STDIN_FILENO) == -1 || + dup2(p[1], STDOUT_FILENO) == -1 || + dup2(devnull, STDERR_FILENO) == -1) { + error("%s: dup2: %s", __func__, strerror(errno)); + _exit(1); + } + + execl(options.authorized_keys_command, + options.authorized_keys_command, pw->pw_name, NULL); + + error("AuthorizedKeysCommand %s exec failed: %s", + options.authorized_keys_command, strerror(errno)); + _exit(127); + default: /* parent */ + break; + } + + temporarily_use_uid(pw); + + close(p[1]); + if ((f = fdopen(p[0], "r")) == NULL) { + error("%s: fdopen: %s", __func__, strerror(errno)); + close(p[0]); + /* Don't leave zombie child */ + kill(pid, SIGTERM); + while (waitpid(pid, NULL, 0) == -1 && errno == EINTR) + ; + goto out; + } + ok = check_authkeys_file(f, options.authorized_keys_command, key, pw); + fclose(f); + + while (waitpid(pid, &status, 0) == -1) { + if (errno != EINTR) { + error("%s: waitpid: %s", __func__, strerror(errno)); + goto out; + } + } + if (WIFSIGNALED(status)) { + error("AuthorizedKeysCommand %s exited on signal %d", + options.authorized_keys_command, WTERMSIG(status)); + goto out; + } else if (WEXITSTATUS(status) != 0) { + error("AuthorizedKeysCommand %s returned status %d", + options.authorized_keys_command, WEXITSTATUS(status)); + goto out; + } + found_key = ok; + out: + restore_uid(); + return found_key; +} + +/* + * Check whether key authenticates and authorises the user. + */ int user_key_allowed(struct passwd *pw, struct sshkey *key) { @@ -487,9 +649,17 @@ user_key_allowed(struct passwd *pw, struct sshkey *key) if (success) return success; + success = user_key_command_allowed2(pw, key); + if (success > 0) + return success; + for (i = 0; !success && i < options.num_authkeys_files; i++) { + + if (strcasecmp(options.authorized_keys_files[i], "none") == 0) + continue; file = expand_authorized_keys( options.authorized_keys_files[i], pw); + success = user_key_allowed2(pw, key, file); xfree(file); } diff --git a/ssh/auth2.c b/ssh/auth2.c index 7ef334d..51da1fb 100644 --- a/ssh/auth2.c +++ b/ssh/auth2.c @@ -1,4 +1,4 @@ -/* $OpenBSD: auth2.c,v 1.124 2011/12/07 05:44:38 djm Exp $ */ +/* $OpenBSD: auth2.c,v 1.125 2012/11/04 11:09:15 djm Exp $ */ /* * Copyright (c) 2000 Markus Friedl. All rights reserved. * @@ -93,8 +93,10 @@ static int input_service_request(int, u_int32_t, struct ssh *); static int input_userauth_request(int, u_int32_t, struct ssh *); /* helper */ -static Authmethod *authmethod_lookup(const char *); -static char *authmethods_get(void); +static Authmethod *authmethod_lookup(Authctxt *, const char *); +static char *authmethods_get(Authctxt *authctxt); +static int method_allowed(Authctxt *, const char *); +static int list_starts_with(const char *, const char *); char * auth2_read_banner(void) @@ -250,6 +252,9 @@ input_userauth_request(int type, u_int32_t seq, struct ssh *ssh) if (use_privsep) mm_inform_authserv(service, style); userauth_banner(ssh); + if (auth2_setup_methods_lists(authctxt) != 0) + ssh_packet_disconnect(ssh, + "no authentication methods enabled"); } else if (strcmp(user, authctxt->user) != 0 || strcmp(service, authctxt->service) != 0) { ssh_packet_disconnect(ssh, "Change of username or service not allowed: " @@ -272,7 +277,7 @@ input_userauth_request(int type, u_int32_t seq, struct ssh *ssh) authctxt->server_caused_failure = 0; /* try to authenticate user */ - m = authmethod_lookup(method); + m = authmethod_lookup(authctxt, method); if (m != NULL && authctxt->failures < options.max_authtries) { debug2("input_userauth_request: try method %s", method); authenticated = m->userauth(ssh); @@ -292,7 +297,7 @@ userauth_finish(struct ssh *ssh, int authenticated, char *method) { Authctxt *authctxt = ssh->authctxt; char *methods; - int r; + int r, partial = 0; if (!authctxt->valid && authenticated) fatal("INTERNAL ERROR: authenticated invalid user %s", @@ -309,7 +314,13 @@ userauth_finish(struct ssh *ssh, int authenticated, char *method) if (authctxt->postponed) return; - /* XXX todo: check if multiple auth methods are needed */ + if (authenticated && options.num_auth_methods != 0) { + if (!auth2_update_methods_lists(authctxt, method)) { + authenticated = 0; + partial = 1; + } + } + if (authenticated == 1) { /* turn off userauth */ ssh_dispatch_set(ssh, SSH2_MSG_USERAUTH_REQUEST, @@ -328,10 +339,12 @@ userauth_finish(struct ssh *ssh, int authenticated, char *method) if (authctxt->failures >= options.max_authtries) ssh_packet_disconnect(ssh, AUTH_FAIL_MSG, authctxt->user); - methods = authmethods_get(); + methods = authmethods_get(authctxt); + debug3("%s: failure partial=%d next methods=\"%s\"", __func__, + partial, methods); if ((r = sshpkt_start(ssh, SSH2_MSG_USERAUTH_FAILURE)) != 0 || (r = sshpkt_put_cstring(ssh, methods)) != 0 || - (r = sshpkt_put_u8(ssh, 0)) != 0 || /* partial success XXX */ + (r = sshpkt_put_u8(ssh, partial)) != 0 || (r = sshpkt_send(ssh)) != 0) fatal("%s: %s", __func__, ssh_err(r)); ssh_packet_write_wait(ssh); @@ -339,8 +352,31 @@ userauth_finish(struct ssh *ssh, int authenticated, char *method) } } +/* + * Checks whether method is allowed by at least one AuthenticationMethods + * methods list. Returns 1 if allowed, or no methods lists configured. + * 0 otherwise. + */ +static int +method_allowed(Authctxt *authctxt, const char *method) +{ + u_int i; + + /* + * NB. authctxt->num_auth_methods might be zero as a result of + * auth2_setup_methods_lists(), so check the configuration. + */ + if (options.num_auth_methods == 0) + return 1; + for (i = 0; i < authctxt->num_auth_methods; i++) { + if (list_starts_with(authctxt->auth_methods[i], method)) + return 1; + } + return 0; +} + static char * -authmethods_get(void) +authmethods_get(Authctxt *authctxt) { struct sshbuf *b; char *list; @@ -354,6 +390,8 @@ authmethods_get(void) if (authmethods[i]->enabled == NULL || *(authmethods[i]->enabled) == 0) continue; + if (!method_allowed(authctxt, authmethods[i]->name)) + continue; if ((r = sshbuf_putf(b, "%s%s", sshbuf_len(b) ? "," : "", authmethods[i]->name)) != 0) fatal("%s: buffer error: %s", __func__, ssh_err(r)); @@ -366,7 +404,7 @@ authmethods_get(void) } static Authmethod * -authmethod_lookup(const char *name) +authmethod_lookup(Authctxt *authctxt, const char *name) { int i; @@ -374,9 +412,152 @@ authmethod_lookup(const char *name) for (i = 0; authmethods[i] != NULL; i++) if (authmethods[i]->enabled != NULL && *(authmethods[i]->enabled) != 0 && - strcmp(name, authmethods[i]->name) == 0) + strcmp(name, authmethods[i]->name) == 0 && + method_allowed(authctxt, authmethods[i]->name)) return authmethods[i]; debug2("Unrecognized authentication method name: %s", name ? name : "NULL"); return NULL; } + +/* + * Check a comma-separated list of methods for validity. Is need_enable is + * non-zero, then also require that the methods are enabled. + * Returns 0 on success or -1 if the methods list is invalid. + */ +int +auth2_methods_valid(const char *_methods, int need_enable) +{ + char *methods, *omethods, *method; + u_int i, found; + int ret = -1; + + if (*_methods == '\0') { + error("empty authentication method list"); + return -1; + } + omethods = methods = xstrdup(_methods); + while ((method = strsep(&methods, ",")) != NULL) { + for (found = i = 0; !found && authmethods[i] != NULL; i++) { + if (strcmp(method, authmethods[i]->name) != 0) + continue; + if (need_enable) { + if (authmethods[i]->enabled == NULL || + *(authmethods[i]->enabled) == 0) { + error("Disabled method \"%s\" in " + "AuthenticationMethods list \"%s\"", + method, _methods); + goto out; + } + } + found = 1; + break; + } + if (!found) { + error("Unknown authentication method \"%s\" in list", + method); + goto out; + } + } + ret = 0; + out: + free(omethods); + return ret; +} + +/* + * Prune the AuthenticationMethods supplied in the configuration, removing + * any methods lists that include disabled methods. Note that this might + * leave authctxt->num_auth_methods == 0, even when multiple required auth + * has been requested. For this reason, all tests for whether multiple is + * enabled should consult options.num_auth_methods directly. + */ +int +auth2_setup_methods_lists(Authctxt *authctxt) +{ + u_int i; + + if (options.num_auth_methods == 0) + return 0; + debug3("%s: checking methods", __func__); + authctxt->auth_methods = xcalloc(options.num_auth_methods, + sizeof(*authctxt->auth_methods)); + authctxt->num_auth_methods = 0; + for (i = 0; i < options.num_auth_methods; i++) { + if (auth2_methods_valid(options.auth_methods[i], 1) != 0) { + logit("Authentication methods list \"%s\" contains " + "disabled method, skipping", + options.auth_methods[i]); + continue; + } + debug("authentication methods list %d: %s", + authctxt->num_auth_methods, options.auth_methods[i]); + authctxt->auth_methods[authctxt->num_auth_methods++] = + xstrdup(options.auth_methods[i]); + } + if (authctxt->num_auth_methods == 0) { + error("No AuthenticationMethods left after eliminating " + "disabled methods"); + return -1; + } + return 0; +} + +static int +list_starts_with(const char *methods, const char *method) +{ + size_t l = strlen(method); + + if (strncmp(methods, method, l) != 0) + return 0; + if (methods[l] != ',' && methods[l] != '\0') + return 0; + return 1; +} + +/* + * Remove method from the start of a comma-separated list of methods. + * Returns 0 if the list of methods did not start with that method or 1 + * if it did. + */ +static int +remove_method(char **methods, const char *method) +{ + char *omethods = *methods; + size_t l = strlen(method); + + if (!list_starts_with(omethods, method)) + return 0; + *methods = xstrdup(omethods + l + (omethods[l] == ',' ? 1 : 0)); + free(omethods); + return 1; +} + +/* + * Called after successful authentication. Will remove the successful method + * from the start of each list in which it occurs. If it was the last method + * in any list, then authentication is deemed successful. + * Returns 1 if the method completed any authentication list or 0 otherwise. + */ +int +auth2_update_methods_lists(Authctxt *authctxt, const char *method) +{ + u_int i, found = 0; + + debug3("%s: updating methods list after \"%s\"", __func__, method); + for (i = 0; i < authctxt->num_auth_methods; i++) { + if (!remove_method(&(authctxt->auth_methods[i]), method)) + continue; + found = 1; + if (*authctxt->auth_methods[i] == '\0') { + debug2("authentication methods list %d complete", i); + return 1; + } + debug3("authentication methods list %d remaining: \"%s\"", + i, authctxt->auth_methods[i]); + } + /* This should not happen, but would be bad if it did */ + if (!found) + fatal("%s: method not in AuthenticationMethods", __func__); + return 0; +} diff --git a/ssh/monitor.c b/ssh/monitor.c index 2e69dad..b1c7fa6 100644 --- a/ssh/monitor.c +++ b/ssh/monitor.c @@ -1,4 +1,4 @@ -/* $OpenBSD: monitor.c,v 1.117 2012/06/22 12:30:26 dtucker Exp $ */ +/* $OpenBSD: monitor.c,v 1.118 2012/11/04 11:09:15 djm Exp $ */ /* * Copyright 2002 Niels Provos * Copyright 2002 Markus Friedl @@ -275,6 +275,21 @@ monitor_child_preauth(Authctxt *_authctxt, struct monitor *pmonitor) while (!authenticated) { auth_method = "unknown"; authenticated = (monitor_read(pmonitor, mon_dispatch, &ent) == 1); + + /* Special handling for multiple required authentications */ + if (options.num_auth_methods != 0) { + if (!compat20) + fatal("AuthenticationMethods is not supported" + "with SSH protocol 1"); + if (authenticated && + !auth2_update_methods_lists(authctxt, + auth_method)) { + debug3("%s: method %s: partial", __func__, + auth_method); + authenticated = 0; + } + } + if (authenticated) { if (!(ent->flags & MON_AUTHDECIDE)) fatal("%s: unexpected authentication from %d", @@ -283,7 +298,6 @@ monitor_child_preauth(Authctxt *_authctxt, struct monitor *pmonitor) !auth_root_allowed(auth_method)) authenticated = 0; } - if (ent->flags & (MON_AUTHDECIDE|MON_ALOG)) { auth_log(authctxt, authenticated, auth_method, compat20 ? " ssh2" : ""); @@ -704,7 +718,17 @@ mm_answer_pwnamallow(int sock, struct sshbuf *m) COPY_MATCH_STRING_OPTS(); #undef M_CP_STROPT #undef M_CP_STRARRAYOPT - + + /* Create valid auth method lists */ + if (compat20 && auth2_setup_methods_lists(authctxt) != 0) { + /* + * The monitor will continue long enough to let the child + * run to it's packet_disconnect(), but it must not allow any + * authentication to succeed. + */ + debug("%s: no valid authentication method lists", __func__); + } + debug3("%s: sending MONITOR_ANS_PWNAM: %d", __func__, allowed); mm_request_send(sock, MONITOR_ANS_PWNAM, m); @@ -843,7 +867,10 @@ mm_answer_bsdauthrespond(int sock, struct sshbuf *m) debug3("%s: sending authenticated: %d", __func__, authok); mm_request_send(sock, MONITOR_ANS_BSDAUTHRESPOND, m); - auth_method = "bsdauth"; + if (compat20) + auth_method = "keyboard-interactive"; + else + auth_method = "bsdauth"; return (authok != 0); } diff --git a/ssh/servconf.c b/ssh/servconf.c index fa4afb7..4676179 100644 --- a/ssh/servconf.c +++ b/ssh/servconf.c @@ -1,5 +1,5 @@ -/* $OpenBSD: servconf.c,v 1.230 2012/09/13 23:37:36 dtucker Exp $ */ +/* $OpenBSD: servconf.c,v 1.232 2012/11/04 11:09:15 djm Exp $ */ /* * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland * All rights reserved @@ -47,6 +47,8 @@ #include "canohost.h" #include "packet.h" #include "err.h" +#include "hostfile.h" +#include "auth.h" static void add_listen_addr(ServerOptions *, char *, int); static void add_one_listen_addr(ServerOptions *, char *, int); @@ -129,6 +131,8 @@ initialize_server_options(ServerOptions *options) options->num_permitted_opens = -1; options->adm_forced_command = NULL; options->chroot_directory = NULL; + options->authorized_keys_command = NULL; + options->authorized_keys_command_user = NULL; options->zero_knowledge_password_authentication = -1; options->revoked_keys_file = NULL; options->trusted_user_ca_keys = NULL; @@ -303,6 +307,8 @@ typedef enum { sZeroKnowledgePasswordAuthentication, sHostCertificate, sRevokedKeys, sTrustedUserCAKeys, sAuthorizedPrincipalsFile, sKexAlgorithms, sIPQoS, sVersionAddendum, + sAuthorizedKeysCommand, sAuthorizedKeysCommandUser, + sAuthenticationMethods, sDeprecated, sUnsupported } ServerOpCodes; @@ -415,7 +421,10 @@ static struct { { "authorizedprincipalsfile", sAuthorizedPrincipalsFile, SSHCFG_ALL }, { "kexalgorithms", sKexAlgorithms, SSHCFG_GLOBAL }, { "ipqos", sIPQoS, SSHCFG_ALL }, + { "authorizedkeyscommand", sAuthorizedKeysCommand, SSHCFG_ALL }, + { "authorizedkeyscommanduser", sAuthorizedKeysCommandUser, SSHCFG_ALL }, { "versionaddendum", sVersionAddendum, SSHCFG_GLOBAL }, + { "authenticationmethods", sAuthenticationMethods, SSHCFG_ALL }, { NULL, sBadOption, 0 } }; @@ -1455,6 +1464,43 @@ process_server_config_line(ServerOptions *options, char *line, } return 0; + case sAuthorizedKeysCommand: + len = strspn(cp, WHITESPACE); + if (*activep && options->authorized_keys_command == NULL) { + if (cp[len] != '/' && strcasecmp(cp + len, "none") != 0) + fatal("%.200s line %d: AuthorizedKeysCommand " + "must be an absolute path", + filename, linenum); + options->authorized_keys_command = xstrdup(cp + len); + } + return 0; + + case sAuthorizedKeysCommandUser: + charptr = &options->authorized_keys_command_user; + + arg = strdelim(&cp); + if (*activep && *charptr == NULL) + *charptr = xstrdup(arg); + break; + + case sAuthenticationMethods: + if (*activep && options->num_auth_methods == 0) { + while ((arg = strdelim(&cp)) && *arg != '\0') { + if (options->num_auth_methods >= + MAX_AUTH_METHODS) + fatal("%s line %d: " + "too many authentication methods.", + filename, linenum); + if (auth2_methods_valid(arg, 0) != 0) + fatal("%s line %d: invalid " + "authentication method list.", + filename, linenum); + options->auth_methods[ + options->num_auth_methods++] = xstrdup(arg); + } + } + return 0; + case sDeprecated: logit("%s line %d: Deprecated option %s", filename, linenum, arg); @@ -1606,6 +1652,8 @@ copy_set_server_options(ServerOptions *dst, ServerOptions *src, int preauth) M_CP_INTOPT(hostbased_uses_name_from_packet_only); M_CP_INTOPT(kbd_interactive_authentication); M_CP_INTOPT(zero_knowledge_password_authentication); + M_CP_STROPT(authorized_keys_command); + M_CP_STROPT(authorized_keys_command_user); M_CP_INTOPT(permit_root_login); M_CP_INTOPT(permit_empty_passwd); @@ -1861,6 +1909,8 @@ dump_config(ServerOptions *o) dump_cfg_string(sAuthorizedPrincipalsFile, o->authorized_principals_file); dump_cfg_string(sVersionAddendum, o->version_addendum); + dump_cfg_string(sAuthorizedKeysCommand, o->authorized_keys_command); + dump_cfg_string(sAuthorizedKeysCommandUser, o->authorized_keys_command_user); /* string arguments requiring a lookup */ dump_cfg_string(sLogLevel, log_level_name(o->log_level)); @@ -1878,6 +1928,8 @@ dump_config(ServerOptions *o) dump_cfg_strarray(sAllowGroups, o->num_allow_groups, o->allow_groups); dump_cfg_strarray(sDenyGroups, o->num_deny_groups, o->deny_groups); dump_cfg_strarray(sAcceptEnv, o->num_accept_env, o->accept_env); + dump_cfg_strarray_oneline(sAuthenticationMethods, + o->num_auth_methods, o->auth_methods); /* other arguments */ for (i = 0; i < o->num_subsystems; i++) diff --git a/ssh/servconf.h b/ssh/servconf.h index fc44d93..fce742e 100644 --- a/ssh/servconf.h +++ b/ssh/servconf.h @@ -1,4 +1,4 @@ -/* $OpenBSD: servconf.h,v 1.103 2012/07/10 02:19:15 djm Exp $ */ +/* $OpenBSD: servconf.h,v 1.105 2012/11/04 11:09:15 djm Exp $ */ /* * Author: Tatu Ylonen @@ -28,6 +28,7 @@ #define MAX_ACCEPT_ENV 256 /* Max # of env vars. */ #define MAX_MATCH_GROUPS 256 /* Max # of groups for Match. */ #define MAX_AUTHKEYS_FILES 256 /* Max # of authorized_keys files. */ +#define MAX_AUTH_METHODS 256 /* Max # of AuthenticationMethods. */ /* permit_root_login */ #define PERMIT_NOT_SET -1 @@ -164,8 +165,13 @@ typedef struct { char *revoked_keys_file; char *trusted_user_ca_keys; char *authorized_principals_file; + char *authorized_keys_command; + char *authorized_keys_command_user; char *version_addendum; /* Appended to SSH banner */ + + u_int num_auth_methods; + char *auth_methods[MAX_AUTH_METHODS]; } ServerOptions; /* Information about the incoming connection as used by Match */ @@ -195,6 +201,7 @@ struct connection_info { M_CP_STRARRAYOPT(allow_groups, num_allow_groups); \ M_CP_STRARRAYOPT(deny_groups, num_deny_groups); \ M_CP_STRARRAYOPT(accept_env, num_accept_env); \ + M_CP_STRARRAYOPT(auth_methods, num_auth_methods); \ } while (0) struct sshbuf; diff --git a/ssh/sshd.c b/ssh/sshd.c index a827d87..20168ba 100644 --- a/ssh/sshd.c +++ b/ssh/sshd.c @@ -1,4 +1,4 @@ -/* $OpenBSD: sshd.c,v 1.393 2012/07/10 02:19:15 djm Exp $ */ +/* $OpenBSD: sshd.c,v 1.396 2012/11/04 11:09:15 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -337,6 +337,15 @@ grace_alarm_handler(int sig) if (use_privsep && pmonitor != NULL && pmonitor->m_pid > 0) kill(pmonitor->m_pid, SIGALRM); + /* + * Try to kill any processes that we have spawned, E.g. authorized + * keys command helpers. + */ + if (getpgid(0) == getpid()) { + signal(SIGTERM, SIG_IGN); + killpg(0, SIGTERM); + } + /* Log error and exit. */ sigdie("Timeout before authentication for %s", ssh_remote_ipaddr(active_state)); @@ -1333,6 +1342,7 @@ main(int ac, char **av) int r, remote_port; char *line; int config_s[2] = { -1 , -1 }; + u_int n; u_int64_t ibytes, obytes; mode_t new_umask; struct sshkey *key; @@ -1522,6 +1532,33 @@ main(int ac, char **av) if (options.challenge_response_authentication) options.kbd_interactive_authentication = 1; + /* Check that options are sensible */ + if (options.authorized_keys_command_user == NULL && + (options.authorized_keys_command != NULL && + strcasecmp(options.authorized_keys_command, "none") != 0)) + fatal("AuthorizedKeysCommand set without " + "AuthorizedKeysCommandUser"); + + /* + * Check whether there is any path through configured auth methods. + * Unfortunately it is not possible to verify this generally before + * daemonisation in the presence of Match block, but this catches + * and warns for trivial misconfigurations that could break login. + */ + if (options.num_auth_methods != 0) { + if ((options.protocol & SSH_PROTO_1)) + fatal("AuthenticationMethods is not supported with " + "SSH protocol 1"); + for (n = 0; n < options.num_auth_methods; n++) { + if (auth2_methods_valid(options.auth_methods[n], + 1) == 0) + break; + } + if (n >= options.num_auth_methods) + fatal("AuthenticationMethods cannot be satisfied by " + "enabled authentication methods"); + } + /* set default channel AF */ channel_set_af(options.address_family); diff --git a/ssh/sshd_config b/ssh/sshd_config index 1c600a8..80e1f6f 100644 --- a/ssh/sshd_config +++ b/ssh/sshd_config @@ -1,4 +1,4 @@ -# $OpenBSD: sshd_config,v 1.87 2012/07/10 02:19:15 djm Exp $ +# $OpenBSD: sshd_config,v 1.88 2012/10/30 21:29:55 djm Exp $ # This is the sshd server system-wide configuration file. See # sshd_config(5) for more information. @@ -49,6 +49,9 @@ AuthorizedKeysFile .ssh/authorized_keys #AuthorizedPrincipalsFile none +#AuthorizedKeysCommand none +#AuthorizedKeysCommandUser nobody + # For this to work you will also need host keys in /etc/ssh/ssh_known_hosts #RhostsRSAAuthentication no # similar for protocol version 2 diff --git a/ssh/sshd_config.5 b/ssh/sshd_config.5 index 18f6a2f..91935d0 100644 --- a/ssh/sshd_config.5 +++ b/ssh/sshd_config.5 @@ -33,8 +33,8 @@ .\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF .\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. .\" -.\" $OpenBSD: sshd_config.5,v 1.145 2012/10/04 13:21:50 markus Exp $ -.Dd $Mdocdate: October 4 2012 $ +.\" $OpenBSD: sshd_config.5,v 1.149 2012/11/04 11:09:15 djm Exp $ +.Dd $Mdocdate: November 4 2012 $ .Dt SSHD_CONFIG 5 .Os .Sh NAME @@ -151,6 +151,45 @@ See in .Xr ssh_config 5 for more information on patterns. +.It Cm AuthenticationMethods +Specifies the authentication methods that must be successfully completed +for a user to be granted access. +This option must be followed by one or more comma-separated lists of +authentication method names. +Successful authentication requires completion of every method in at least +one of these lists. +.Pp +For example, an argument of +.Dq publickey,password publickey,keyboard-interactive +would require the user to complete public key authentication, followed by +either password or keyboard interactive authentication. +Only methods that are next in one or more lists are offered at each stage, +so for this example, it would not be possible to attempt password or +keyboard-interactive authentication before public key. +.Pp +This option is only available for SSH protocol 2 and will yield a fatal +error if enabled if protocol 1 is also enabled. +Note that each authentication method listed should also be explicitly enabled +in the configuration. +The default is not to require multiple authentication; successful completion +of a single authentication method is sufficient. +.It Cm AuthorizedKeysCommand +Specifies a program to be used to look up the user's public keys. +The program will be invoked with a single argument of the username +being authenticated, and should produce on standard output zero or +more lines of authorized_keys output (see +.Sx AUTHORIZED_KEYS +in +.Xr sshd 8 ) . +If a key supplied by AuthorizedKeysCommand does not successfully authenticate +and authorize the user then public key authentication continues using the usual +.Cm AuthorizedKeysFile +files. +By default, no AuthorizedKeysCommand is run. +.It Cm AuthorizedKeysCommandUser +Specifies the user under whose account the AuthorizedKeysCommand is run. +It is recommended to use a dedicated user that has no other role on the host +than running authorized keys commands. .It Cm AuthorizedKeysFile Specifies the file that contains the public keys that can be used for user authentication. @@ -712,6 +751,9 @@ Available keywords are .Cm AllowGroups , .Cm AllowTcpForwarding , .Cm AllowUsers , +.Cm AuthenticationMethods , +.Cm AuthorizedKeysCommand , +.Cm AuthorizedKeysCommandUser , .Cm AuthorizedKeysFile , .Cm AuthorizedPrincipalsFile , .Cm Banner ,