summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBen Lindstrom <mouring@eviladmin.org>2001-06-05 20:25:05 +0000
committerBen Lindstrom <mouring@eviladmin.org>2001-06-05 20:25:05 +0000
commitbfb3a0e973214fabc1be744b8c7e4a89a0c5570c (patch)
tree8227151356ee10ae6762c42442f272b0db418973
parente2595448766a4149bbd2652830d1b086a066af13 (diff)
- markus@cvs.openbsd.org 2001/05/20 17:20:36
[auth-rsa.c auth.c auth.h auth2.c servconf.c servconf.h sshd.8 sshd_config] configurable authorized_keys{,2} location; originally from peter@; ok djm@
-rw-r--r--ChangeLog7
-rw-r--r--auth-rsa.c54
-rw-r--r--auth.c135
-rw-r--r--auth.h12
-rw-r--r--auth2.c58
-rw-r--r--servconf.c25
-rw-r--r--servconf.h4
-rw-r--r--sshd.841
-rw-r--r--sshd_config5
9 files changed, 247 insertions, 94 deletions
diff --git a/ChangeLog b/ChangeLog
index 7894490a..4b1aa113 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -42,6 +42,11 @@
- stevesk@cvs.openbsd.org 2001/05/19 19:57:09
[channels.c]
typo in error message
+ - markus@cvs.openbsd.org 2001/05/20 17:20:36
+ [auth-rsa.c auth.c auth.h auth2.c servconf.c servconf.h sshd.8
+ sshd_config]
+ configurable authorized_keys{,2} location; originally from peter@;
+ ok djm@
20010528
- (tim) [conifgure.in] add setvbuf test needed for sftp-int.c
@@ -5472,4 +5477,4 @@
- Wrote replacements for strlcpy and mkdtemp
- Released 1.0pre1
-$Id: ChangeLog,v 1.1235 2001/06/05 20:01:39 mouring Exp $
+$Id: ChangeLog,v 1.1236 2001/06/05 20:25:05 mouring Exp $
diff --git a/auth-rsa.c b/auth-rsa.c
index 59bee18b..491ed81d 100644
--- a/auth-rsa.c
+++ b/auth-rsa.c
@@ -14,7 +14,7 @@
*/
#include "includes.h"
-RCSID("$OpenBSD: auth-rsa.c,v 1.40 2001/04/06 21:00:07 markus Exp $");
+RCSID("$OpenBSD: auth-rsa.c,v 1.41 2001/05/20 17:20:35 markus Exp $");
#include <openssl/rsa.h>
#include <openssl/md5.h>
@@ -122,7 +122,7 @@ auth_rsa_challenge_dialog(RSA *pk)
int
auth_rsa(struct passwd *pw, BIGNUM *client_n)
{
- char line[8192], file[MAXPATHLEN];
+ char line[8192], *file;
int authenticated;
u_int bits;
FILE *f;
@@ -138,13 +138,14 @@ auth_rsa(struct passwd *pw, BIGNUM *client_n)
temporarily_use_uid(pw);
/* The authorized keys. */
- snprintf(file, sizeof file, "%.500s/%.100s", pw->pw_dir,
- _PATH_SSH_USER_PERMITTED_KEYS);
+ file = authorized_keys_file(pw);
+ debug("trying public RSA key file %s", file);
/* Fail quietly if file does not exist */
if (stat(file, &st) < 0) {
/* Restore the privileged uid. */
restore_uid();
+ xfree(file);
return 0;
}
/* Open the file containing the authorized keys. */
@@ -154,43 +155,17 @@ auth_rsa(struct passwd *pw, BIGNUM *client_n)
restore_uid();
packet_send_debug("Could not open %.900s for reading.", file);
packet_send_debug("If your home is on an NFS volume, it may need to be world-readable.");
+ xfree(file);
return 0;
}
- if (options.strict_modes) {
- int fail = 0;
- char buf[1024];
- /* Check open file in order to avoid open/stat races */
- if (fstat(fileno(f), &st) < 0 ||
- (st.st_uid != 0 && st.st_uid != pw->pw_uid) ||
- (st.st_mode & 022) != 0) {
- snprintf(buf, sizeof buf, "RSA authentication refused for %.100s: "
- "bad ownership or modes for '%s'.", pw->pw_name, file);
- fail = 1;
- } else {
- /* Check path to _PATH_SSH_USER_PERMITTED_KEYS */
- int i;
- static const char *check[] = {
- "", _PATH_SSH_USER_DIR, NULL
- };
- for (i = 0; check[i]; i++) {
- snprintf(line, sizeof line, "%.500s/%.100s", pw->pw_dir, check[i]);
- if (stat(line, &st) < 0 ||
- (st.st_uid != 0 && st.st_uid != pw->pw_uid) ||
- (st.st_mode & 022) != 0) {
- snprintf(buf, sizeof buf, "RSA authentication refused for %.100s: "
- "bad ownership or modes for '%s'.", pw->pw_name, line);
- fail = 1;
- break;
- }
- }
- }
- if (fail) {
- fclose(f);
- log("%s", buf);
- packet_send_debug("%s", buf);
- restore_uid();
- return 0;
- }
+ if (options.strict_modes &&
+ secure_filename(f, file, pw->pw_uid, line, sizeof(line)) != 0) {
+ xfree(file);
+ fclose(f);
+ log("Authentication refused: %s", line);
+ packet_send_debug("Authentication refused: %s", line);
+ restore_uid();
+ return 0;
}
/* Flag indicating whether authentication has succeeded. */
authenticated = 0;
@@ -285,6 +260,7 @@ auth_rsa(struct passwd *pw, BIGNUM *client_n)
restore_uid();
/* Close the file. */
+ xfree(file);
fclose(f);
RSA_free(pk);
diff --git a/auth.c b/auth.c
index 1f976eee..07e5b8f0 100644
--- a/auth.c
+++ b/auth.c
@@ -23,7 +23,7 @@
*/
#include "includes.h"
-RCSID("$OpenBSD: auth.c,v 1.21 2001/03/19 17:07:23 markus Exp $");
+RCSID("$OpenBSD: auth.c,v 1.22 2001/05/20 17:20:35 markus Exp $");
#ifdef HAVE_LOGIN_H
#include <login.h>
@@ -32,6 +32,8 @@ RCSID("$OpenBSD: auth.c,v 1.21 2001/03/19 17:07:23 markus Exp $");
#include <shadow.h>
#endif /* defined(HAVE_SHADOW_H) && !defined(DISABLE_SHADOW) */
+#include <libgen.h>
+
#include "xmalloc.h"
#include "match.h"
#include "groupaccess.h"
@@ -40,6 +42,8 @@ RCSID("$OpenBSD: auth.c,v 1.21 2001/03/19 17:07:23 markus Exp $");
#include "auth.h"
#include "auth-options.h"
#include "canohost.h"
+#include "buffer.h"
+#include "bufaux.h"
/* import */
extern ServerOptions options;
@@ -222,3 +226,132 @@ auth_root_allowed(char *method)
log("ROOT LOGIN REFUSED FROM %.200s", get_remote_ipaddr());
return 0;
}
+
+
+/*
+ * Given a template and a passwd structure, build a filename
+ * by substituting % tokenised options. Currently, %% becomes '%',
+ * %h becomes the home directory and %u the username.
+ *
+ * This returns a buffer allocated by xmalloc.
+ */
+char *
+expand_filename(const char *filename, struct passwd *pw)
+{
+ Buffer buffer;
+ char *file;
+ const char *cp;
+
+ /*
+ * Build the filename string in the buffer by making the appropriate
+ * substitutions to the given file name.
+ */
+ buffer_init(&buffer);
+ for (cp = filename; *cp; cp++) {
+ if (cp[0] == '%' && cp[1] == '%') {
+ buffer_append(&buffer, "%", 1);
+ cp++;
+ continue;
+ }
+ if (cp[0] == '%' && cp[1] == 'h') {
+ buffer_append(&buffer, pw->pw_dir, strlen(pw->pw_dir));
+ cp++;
+ continue;
+ }
+ if (cp[0] == '%' && cp[1] == 'u') {
+ buffer_append(&buffer, pw->pw_name,
+ strlen(pw->pw_name));
+ cp++;
+ continue;
+ }
+ buffer_append(&buffer, cp, 1);
+ }
+ buffer_append(&buffer, "\0", 1);
+
+ /*
+ * Ensure that filename starts anchored. If not, be backward
+ * compatible and prepend the '%h/'
+ */
+ file = xmalloc(MAXPATHLEN);
+ cp = buffer_ptr(&buffer);
+ if (*cp != '/')
+ snprintf(file, MAXPATHLEN, "%s/%s", pw->pw_dir, cp);
+ else
+ strlcpy(file, cp, MAXPATHLEN);
+
+ buffer_free(&buffer);
+ return file;
+}
+
+char *
+authorized_keys_file(struct passwd *pw)
+{
+ return expand_filename(options.authorized_keys_file, pw);
+}
+
+char *
+authorized_keys_file2(struct passwd *pw)
+{
+ return expand_filename(options.authorized_keys_file2, pw);
+}
+
+/*
+ * Check a given file for security. This is defined as all components
+ * of the path to the file must either be owned by either the owner of
+ * of the file or root and no directories must be world writable.
+ *
+ * XXX Should any specific check be done for sym links ?
+ *
+ * Takes an open file descriptor, the file name, a uid and and
+ * error buffer plus max size as arguments.
+ *
+ * Returns 0 on success and -1 on failure
+ */
+int
+secure_filename(FILE *f, const char *file, uid_t uid, char *err, size_t errlen)
+{
+ char buf[MAXPATHLEN];
+ char *cp;
+ struct stat st;
+
+ if (realpath(file, buf) == NULL) {
+ snprintf(err, errlen, "realpath %s failed: %s", file,
+ strerror(errno));
+ return -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) {
+ snprintf(err, errlen, "bad ownership or modes for file %s",
+ buf);
+ return -1;
+ }
+
+ /* for each component of the canonical path, walking upwards */
+ for (;;) {
+ if ((cp = dirname(buf)) == NULL) {
+ snprintf(err, errlen, "dirname() failed");
+ return -1;
+ }
+ strlcpy(buf, cp, sizeof(buf));
+
+ debug3("secure_filename: checking '%s'", buf);
+ if (stat(buf, &st) < 0 ||
+ (st.st_uid != 0 && st.st_uid != uid) ||
+ (st.st_mode & 022) != 0) {
+ snprintf(err, errlen,
+ "bad ownership or modes for directory %s", buf);
+ return -1;
+ }
+
+ /*
+ * dirname should always complete with a "/" path,
+ * but we can be paranoid and check for "." too
+ */
+ if ((strcmp("/", buf) == 0) || (strcmp(".", buf) == 0))
+ break;
+ }
+ return 0;
+}
diff --git a/auth.h b/auth.h
index 20c3ebb7..2d1f1e9b 100644
--- a/auth.h
+++ b/auth.h
@@ -21,7 +21,7 @@
* (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: auth.h,v 1.16 2001/05/18 14:13:28 markus Exp $
+ * $OpenBSD: auth.h,v 1.17 2001/05/20 17:20:35 markus Exp $
*/
#ifndef AUTH_H
#define AUTH_H
@@ -159,6 +159,16 @@ int verify_response(Authctxt *authctxt, const char *response);
struct passwd * auth_get_user(void);
+
+/* expand a filename - return buffer is allocated by xmalloc */
+char *expand_filename(const char *template, struct passwd *pw);
+char *authorized_keys_file(struct passwd *pw);
+char *authorized_keys_file2(struct passwd *pw);
+
+/* check a file and the path to it */
+int
+secure_filename(FILE *f, const char *file, uid_t u, char *err, size_t errlen);
+
#define AUTH_FAIL_MAX 6
#define AUTH_FAIL_LOG (AUTH_FAIL_MAX/2)
#define AUTH_FAIL_MSG "Too many authentication failures for %.100s"
diff --git a/auth2.c b/auth2.c
index e800c058..9988f7ae 100644
--- a/auth2.c
+++ b/auth2.c
@@ -23,7 +23,7 @@
*/
#include "includes.h"
-RCSID("$OpenBSD: auth2.c,v 1.57 2001/05/18 14:13:28 markus Exp $");
+RCSID("$OpenBSD: auth2.c,v 1.58 2001/05/20 17:20:35 markus Exp $");
#include <openssl/evp.h>
@@ -666,7 +666,7 @@ authmethod_lookup(const char *name)
int
user_key_allowed(struct passwd *pw, Key *key)
{
- char line[8192], file[MAXPATHLEN];
+ char line[8192], *file;
int found_key = 0;
FILE *f;
u_long linenum = 0;
@@ -680,13 +680,14 @@ user_key_allowed(struct passwd *pw, Key *key)
temporarily_use_uid(pw);
/* The authorized keys. */
- snprintf(file, sizeof file, "%.500s/%.100s", pw->pw_dir,
- _PATH_SSH_USER_PERMITTED_KEYS2);
+ file = authorized_keys_file2(pw);
+ debug("trying public key file %s", file);
/* Fail quietly if file does not exist */
if (stat(file, &st) < 0) {
/* Restore the privileged uid. */
restore_uid();
+ xfree(file);
return 0;
}
/* Open the file containing the authorized keys. */
@@ -694,48 +695,18 @@ user_key_allowed(struct passwd *pw, Key *key)
if (!f) {
/* Restore the privileged uid. */
restore_uid();
+ xfree(file);
return 0;
}
- if (options.strict_modes) {
- int fail = 0;
- char buf[1024];
- /* Check open file in order to avoid open/stat races */
- if (fstat(fileno(f), &st) < 0 ||
- (st.st_uid != 0 && st.st_uid != pw->pw_uid) ||
- (st.st_mode & 022) != 0) {
- snprintf(buf, sizeof buf,
- "%s authentication refused for %.100s: "
- "bad ownership or modes for '%s'.",
- key_type(key), pw->pw_name, file);
- fail = 1;
- } else {
- /* Check path to _PATH_SSH_USER_PERMITTED_KEYS */
- int i;
- static const char *check[] = {
- "", _PATH_SSH_USER_DIR, NULL
- };
- for (i = 0; check[i]; i++) {
- snprintf(line, sizeof line, "%.500s/%.100s",
- pw->pw_dir, check[i]);
- if (stat(line, &st) < 0 ||
- (st.st_uid != 0 && st.st_uid != pw->pw_uid) ||
- (st.st_mode & 022) != 0) {
- snprintf(buf, sizeof buf,
- "%s authentication refused for %.100s: "
- "bad ownership or modes for '%s'.",
- key_type(key), pw->pw_name, line);
- fail = 1;
- break;
- }
- }
- }
- if (fail) {
- fclose(f);
- log("%s", buf);
- restore_uid();
- return 0;
- }
+ if (options.strict_modes &&
+ secure_filename(f, file, pw->pw_uid, line, sizeof(line)) != 0) {
+ xfree(file);
+ fclose(f);
+ log("Authentication refused: %s", line);
+ restore_uid();
+ return 0;
}
+
found_key = 0;
found = key_new(key->type);
@@ -778,6 +749,7 @@ user_key_allowed(struct passwd *pw, Key *key)
}
restore_uid();
fclose(f);
+ xfree(file);
key_free(found);
if (!found_key)
debug2("key not found");
diff --git a/servconf.c b/servconf.c
index 2d10963c..e357d77a 100644
--- a/servconf.c
+++ b/servconf.c
@@ -10,7 +10,7 @@
*/
#include "includes.h"
-RCSID("$OpenBSD: servconf.c,v 1.81 2001/05/19 19:43:57 stevesk Exp $");
+RCSID("$OpenBSD: servconf.c,v 1.82 2001/05/20 17:20:35 markus Exp $");
#ifdef KRB4
#include <krb.h>
@@ -101,6 +101,8 @@ initialize_server_options(ServerOptions *options)
options->reverse_mapping_check = -1;
options->client_alive_interval = -1;
options->client_alive_count_max = -1;
+ options->authorized_keys_file = NULL;
+ options->authorized_keys_file2 = NULL;
options->pam_authentication_via_kbd_int = -1;
}
@@ -208,6 +210,10 @@ fill_default_server_options(ServerOptions *options)
options->client_alive_interval = 0;
if (options->client_alive_count_max == -1)
options->client_alive_count_max = 3;
+ if (options->authorized_keys_file == NULL)
+ options->authorized_keys_file = _PATH_SSH_USER_PERMITTED_KEYS;
+ if (options->authorized_keys_file2 == NULL)
+ options->authorized_keys_file2 = _PATH_SSH_USER_PERMITTED_KEYS2;
if (options->pam_authentication_via_kbd_int == -1)
options->pam_authentication_via_kbd_int = 0;
}
@@ -235,7 +241,8 @@ typedef enum {
sGatewayPorts, sPubkeyAuthentication, sXAuthLocation, sSubsystem, sMaxStartups,
sBanner, sReverseMappingCheck, sHostbasedAuthentication,
sHostbasedUsesNameFromPacketOnly, sClientAliveInterval,
- sClientAliveCountMax, sPAMAuthenticationViaKbdInt
+ sClientAliveCountMax, sAuthorizedKeysFile, sAuthorizedKeysFile2,
+ sPAMAuthenticationViaKbdInt
} ServerOpCodes;
/* Textual representation of the tokens. */
@@ -301,6 +308,8 @@ static struct {
{ "reversemappingcheck", sReverseMappingCheck },
{ "clientaliveinterval", sClientAliveInterval },
{ "clientalivecountmax", sClientAliveCountMax },
+ { "authorizedkeysfile", sAuthorizedKeysFile },
+ { "authorizedkeysfile2", sAuthorizedKeysFile2 },
{ "PAMAuthenticationViaKbdInt", sPAMAuthenticationViaKbdInt },
{ NULL, 0 }
};
@@ -802,6 +811,18 @@ parse_flag:
case sBanner:
charptr = &options->banner;
goto parse_filename;
+ /*
+ * These options can contain %X options expanded at
+ * connect time, so that you can specify paths like:
+ *
+ * AuthorizedKeysFile /etc/ssh_keys/%u
+ */
+ case sAuthorizedKeysFile:
+ case sAuthorizedKeysFile2:
+ charptr = (opcode == sAuthorizedKeysFile ) ?
+ &options->authorized_keys_file :
+ &options->authorized_keys_file2;
+ goto parse_filename;
case sClientAliveInterval:
intptr = &options->client_alive_interval;
diff --git a/servconf.h b/servconf.h
index a319a5c6..2bf19fb3 100644
--- a/servconf.h
+++ b/servconf.h
@@ -11,7 +11,7 @@
* called by a name other than "ssh" or "Secure Shell".
*/
-/* RCSID("$OpenBSD: servconf.h,v 1.42 2001/05/18 14:13:29 markus Exp $"); */
+/* RCSID("$OpenBSD: servconf.h,v 1.43 2001/05/20 17:20:35 markus Exp $"); */
#ifndef SERVCONF_H
#define SERVCONF_H
@@ -124,6 +124,8 @@ typedef struct {
* for this many intervals, above
* diconnect the session
*/
+ char *authorized_keys_file; /* File containing public RSA keys */
+ char *authorized_keys_file2; /* File containing public SSH2 keys */
int pam_authentication_via_kbd_int;
} ServerOptions;
/*
diff --git a/sshd.8 b/sshd.8
index 02960b70..a66dac54 100644
--- a/sshd.8
+++ b/sshd.8
@@ -34,7 +34,7 @@
.\" (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.8,v 1.124 2001/05/19 19:43:57 stevesk Exp $
+.\" $OpenBSD: sshd.8,v 1.125 2001/05/20 17:20:35 markus Exp $
.Dd September 25, 1999
.Dt SSHD 8
.Os
@@ -331,6 +331,34 @@ wildcards in the patterns.
Only user names are valid; a numerical user ID isn't recognized.
By default login is allowed regardless of the user name.
.Pp
+.It Cm AuthorizedKeysFile
+Specifies the file that contains the public RSA keys that can be used
+for RSA authentication in protocol version 1.
+.Cm AuthorizedKeysFile
+may contain tokens of the form %T which are substituted during connection
+set-up. The following tokens are defined; %% is replaces by a literal '%',
+%h is replaced by the home directory of the user being authenticated and
+%u is replaced by the username of that user.
+After expansion,
+.Cm AuthorizedKeysFile
+is taken to be an absolute path or one realtive to the user's home
+directory.
+The default is
+.Dq .ssh/authorized_keys
+.It Cm AuthorizedKeysFile2
+Specifies the file that contains the public keys that can be used
+for public key authentication in protocol version 2.
+.Cm AuthorizedKeysFile2
+may contain tokens of the form %T which are substituted during connection
+set-up. The following tokens are defined; %% is replaces by a literal '%',
+%h is replaced by the home directory of the user being authenticated and
+%u is replaced by the username of that user.
+After expansion,
+.Cm AuthorizedKeysFile2
+is taken to be an absolute path or one realtive to the user's home
+directory.
+The default is
+.Dq .ssh/authorized_keys2
.It Cm Banner
In some jurisdictions, sending a warning message before authentication
may be relevant for getting legal protection.
@@ -883,15 +911,18 @@ authentication protocol and cookie in standard input.
Runs user's shell or command.
.El
.Sh AUTHORIZED_KEYS FILE FORMAT
-The
.Pa $HOME/.ssh/authorized_keys
-file lists the RSA keys that are
+is the default file that lists the RSA keys that are
permitted for RSA authentication in protocol version 1.
-Similarly, the
+.Cm AuthorizedKeysFile
+may be used to specify an alternative file.
+Similarly,
.Pa $HOME/.ssh/authorized_keys2
-file lists the DSA and RSA keys that are
+is the default file that lists the DSA and RSA keys that are
permitted for public key authentication (PubkeyAuthentication)
in protocol version 2.
+.Cm AuthorizedKeysFile2
+may be used to specify an alternative file.
.Pp
Each line of the file contains one
key (empty lines and lines starting with a
diff --git a/sshd_config b/sshd_config
index 8c411e47..90df340a 100644
--- a/sshd_config
+++ b/sshd_config
@@ -1,4 +1,4 @@
-# $OpenBSD: sshd_config,v 1.38 2001/04/15 21:41:29 deraadt Exp $
+# $OpenBSD: sshd_config,v 1.39 2001/05/20 17:20:36 markus Exp $
# This sshd was compiled with PATH=/usr/bin:/bin:/usr/sbin:/sbin
@@ -41,6 +41,9 @@ RhostsRSAAuthentication no
HostbasedAuthentication no
#
RSAAuthentication yes
+PubkeyAuthentication yes
+#AuthorizedKeysFile %h/.ssh/authorized_keys
+#AuthorizedKeysFile2 %h/.ssh/authorized_keys2
# To disable tunneled clear text passwords, change to no here!
PasswordAuthentication yes