summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--ChangeLog22
-rw-r--r--auth-options.c43
-rw-r--r--auth-options.h3
-rw-r--r--auth.c41
-rw-r--r--auth.h4
-rw-r--r--auth2-pubkey.c102
-rw-r--r--key.c4
-rw-r--r--servconf.c18
-rw-r--r--servconf.h3
-rw-r--r--sshd.815
-rw-r--r--sshd_config.541
11 files changed, 262 insertions, 34 deletions
diff --git a/ChangeLog b/ChangeLog
index 68460945..090e2352 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -31,6 +31,28 @@
[sftp.c]
restore mput and mget which got lost in the tab-completion changes.
found by Kenneth Whitaker, ok djm@
+ - djm@cvs.openbsd.org 2010/05/07 11:30:30
+ [auth-options.c auth-options.h auth.c auth.h auth2-pubkey.c]
+ [key.c servconf.c servconf.h sshd.8 sshd_config.5]
+ add some optional indirection to matching of principal names listed
+ in certificates. Currently, a certificate must include the a user's name
+ to be accepted for authentication. This change adds the ability to
+ specify a list of certificate principal names that are acceptable.
+
+ When authenticating using a CA trusted through ~/.ssh/authorized_keys,
+ this adds a new principals="name1[,name2,...]" key option.
+
+ For CAs listed through sshd_config's TrustedCAKeys option, a new config
+ option "AuthorizedPrincipalsFile" specifies a per-user file containing
+ the list of acceptable names.
+
+ If either option is absent, the current behaviour of requiring the
+ username to appear in principals continues to apply.
+
+ These options are useful for role accounts, disjoint account namespaces
+ and "user@realm"-style naming policies in certificates.
+
+ feedback and ok markus@
20100423
- (dtucker) [configure.ac] Bug #1756: Check for the existence of a lib64 dir
diff --git a/auth-options.c b/auth-options.c
index 60d5f749..57a67ec7 100644
--- a/auth-options.c
+++ b/auth-options.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: auth-options.c,v 1.50 2010/04/16 01:47:26 djm Exp $ */
+/* $OpenBSD: auth-options.c,v 1.51 2010/05/07 11:30:29 djm Exp $ */
/*
* Author: Tatu Ylonen <ylo@cs.hut.fi>
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@@ -55,6 +55,9 @@ struct envstring *custom_environment = NULL;
/* "tunnel=" option. */
int forced_tun_device = -1;
+/* "principals=" option. */
+char *authorized_principals = NULL;
+
extern ServerOptions options;
void
@@ -76,6 +79,10 @@ auth_clear_options(void)
xfree(forced_command);
forced_command = NULL;
}
+ if (authorized_principals) {
+ xfree(authorized_principals);
+ authorized_principals = NULL;
+ }
forced_tun_device = -1;
channel_clear_permitted_opens();
}
@@ -141,6 +148,8 @@ auth_parse_options(struct passwd *pw, char *opts, char *file, u_long linenum)
cp = "command=\"";
if (strncasecmp(opts, cp, strlen(cp)) == 0) {
opts += strlen(cp);
+ if (forced_command != NULL)
+ xfree(forced_command);
forced_command = xmalloc(strlen(opts) + 1);
i = 0;
while (*opts) {
@@ -167,6 +176,38 @@ auth_parse_options(struct passwd *pw, char *opts, char *file, u_long linenum)
opts++;
goto next_option;
}
+ cp = "principals=\"";
+ if (strncasecmp(opts, cp, strlen(cp)) == 0) {
+ opts += strlen(cp);
+ if (authorized_principals != NULL)
+ xfree(authorized_principals);
+ authorized_principals = xmalloc(strlen(opts) + 1);
+ i = 0;
+ while (*opts) {
+ if (*opts == '"')
+ break;
+ if (*opts == '\\' && opts[1] == '"') {
+ opts += 2;
+ authorized_principals[i++] = '"';
+ continue;
+ }
+ authorized_principals[i++] = *opts++;
+ }
+ if (!*opts) {
+ debug("%.100s, line %lu: missing end quote",
+ file, linenum);
+ auth_debug_add("%.100s, line %lu: missing end quote",
+ file, linenum);
+ xfree(authorized_principals);
+ authorized_principals = NULL;
+ goto bad_option;
+ }
+ authorized_principals[i] = '\0';
+ auth_debug_add("principals: %.900s",
+ authorized_principals);
+ opts++;
+ goto next_option;
+ }
cp = "environment=\"";
if (options.permit_user_env &&
strncasecmp(opts, cp, strlen(cp)) == 0) {
diff --git a/auth-options.h b/auth-options.h
index 20f0dbe3..7455c945 100644
--- a/auth-options.h
+++ b/auth-options.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: auth-options.h,v 1.19 2010/04/16 01:47:26 djm Exp $ */
+/* $OpenBSD: auth-options.h,v 1.20 2010/05/07 11:30:29 djm Exp $ */
/*
* Author: Tatu Ylonen <ylo@cs.hut.fi>
@@ -31,6 +31,7 @@ extern char *forced_command;
extern struct envstring *custom_environment;
extern int forced_tun_device;
extern int key_is_cert_authority;
+extern char *authorized_principals;
int auth_parse_options(struct passwd *, char *, char *, u_long);
void auth_clear_options(void);
diff --git a/auth.c b/auth.c
index 89a93606..bec191a5 100644
--- a/auth.c
+++ b/auth.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: auth.c,v 1.86 2010/03/05 02:58:11 djm Exp $ */
+/* $OpenBSD: auth.c,v 1.87 2010/05/07 11:30:29 djm Exp $ */
/*
* Copyright (c) 2000 Markus Friedl. All rights reserved.
*
@@ -366,6 +366,14 @@ authorized_keys_file2(struct passwd *pw)
return expand_authorized_keys(options.authorized_keys_file2, pw);
}
+char *
+authorized_principals_file(struct passwd *pw)
+{
+ if (options.authorized_principals_file == NULL)
+ return NULL;
+ return expand_authorized_keys(options.authorized_principals_file, pw);
+}
+
/* return ok if key exists in sysfile or userfile */
HostStatus
check_key_in_hostfiles(struct passwd *pw, Key *key, const char *host,
@@ -477,21 +485,18 @@ secure_filename(FILE *f, const char *file, struct passwd *pw,
return 0;
}
-FILE *
-auth_openkeyfile(const char *file, struct passwd *pw, int strict_modes)
+static FILE *
+auth_openfile(const char *file, struct passwd *pw, int strict_modes,
+ int log_missing, char *file_type)
{
char line[1024];
struct stat st;
int fd;
FILE *f;
- /*
- * Open the file containing the authorized keys
- * Fail quietly if file does not exist
- */
if ((fd = open(file, O_RDONLY|O_NONBLOCK)) == -1) {
- if (errno != ENOENT)
- debug("Could not open keyfile '%s': %s", file,
+ if (log_missing || errno != ENOENT)
+ debug("Could not open %s '%s': %s", file_type, file,
strerror(errno));
return NULL;
}
@@ -501,8 +506,8 @@ auth_openkeyfile(const char *file, struct passwd *pw, int strict_modes)
return NULL;
}
if (!S_ISREG(st.st_mode)) {
- logit("User %s authorized keys %s is not a regular file",
- pw->pw_name, file);
+ logit("User %s %s %s is not a regular file",
+ pw->pw_name, file_type, file);
close(fd);
return NULL;
}
@@ -521,6 +526,20 @@ auth_openkeyfile(const char *file, struct passwd *pw, int strict_modes)
return f;
}
+
+FILE *
+auth_openkeyfile(const char *file, struct passwd *pw, int strict_modes)
+{
+ return auth_openfile(file, pw, strict_modes, 1, "authorized keys");
+}
+
+FILE *
+auth_openprincipals(const char *file, struct passwd *pw, int strict_modes)
+{
+ return auth_openfile(file, pw, strict_modes, 0,
+ "authorized principals");
+}
+
struct passwd *
getpwnamallow(const char *user)
{
diff --git a/auth.h b/auth.h
index a65b87dd..77317aee 100644
--- a/auth.h
+++ b/auth.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: auth.h,v 1.65 2010/03/04 10:36:03 djm Exp $ */
+/* $OpenBSD: auth.h,v 1.66 2010/05/07 11:30:29 djm Exp $ */
/*
* Copyright (c) 2000 Markus Friedl. All rights reserved.
@@ -169,8 +169,10 @@ void abandon_challenge_response(Authctxt *);
char *authorized_keys_file(struct passwd *);
char *authorized_keys_file2(struct passwd *);
+char *authorized_principals_file(struct passwd *);
FILE *auth_openkeyfile(const char *, struct passwd *, int);
+FILE *auth_openprincipals(const char *, struct passwd *, int);
int auth_key_is_revoked(Key *);
HostStatus
diff --git a/auth2-pubkey.c b/auth2-pubkey.c
index 83ecd659..6b4a9972 100644
--- a/auth2-pubkey.c
+++ b/auth2-pubkey.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: auth2-pubkey.c,v 1.23 2010/04/16 01:47:26 djm Exp $ */
+/* $OpenBSD: auth2-pubkey.c,v 1.24 2010/05/07 11:30:29 djm Exp $ */
/*
* Copyright (c) 2000 Markus Friedl. All rights reserved.
*
@@ -57,6 +57,7 @@
#include "monitor_wrap.h"
#include "misc.h"
#include "authfile.h"
+#include "match.h"
/* import */
extern ServerOptions options;
@@ -176,6 +177,63 @@ done:
return authenticated;
}
+static int
+match_principals_option(const char *principal_list, struct KeyCert *cert)
+{
+ char *result;
+ u_int i;
+
+ /* XXX percent_expand() sequences for authorized_principals? */
+
+ for (i = 0; i < cert->nprincipals; i++) {
+ if ((result = match_list(cert->principals[i],
+ principal_list, NULL)) != NULL) {
+ debug3("matched principal from key options \"%.100s\"",
+ result);
+ xfree(result);
+ return 1;
+ }
+ }
+ return 0;
+}
+
+static int
+match_principals_file(const char *file, struct passwd *pw, struct KeyCert *cert)
+{
+ FILE *f;
+ char line[SSH_MAX_PUBKEY_BYTES], *cp;
+ u_long linenum = 0;
+ u_int i;
+
+ temporarily_use_uid(pw);
+ debug("trying authorized principals file %s", file);
+ if ((f = auth_openprincipals(file, pw, options.strict_modes)) == NULL) {
+ restore_uid();
+ return 0;
+ }
+ while (read_keyfile_line(f, file, line, sizeof(line), &linenum) != -1) {
+ /* Skip leading whitespace, empty and comment lines. */
+ for (cp = line; *cp == ' ' || *cp == '\t'; cp++)
+ ;
+ if (!*cp || *cp == '\n' || *cp == '#')
+ continue;
+ line[strcspn(line, "\n")] = '\0';
+
+ for (i = 0; i < cert->nprincipals; i++) {
+ if (strcmp(cp, cert->principals[i]) == 0) {
+ debug3("matched principal from file \"%.100s\"",
+ cert->principals[i]);
+ fclose(f);
+ restore_uid();
+ return 1;
+ }
+ }
+ }
+ fclose(f);
+ restore_uid();
+ return 0;
+}
+
/* return 1 if user allows given key */
static int
user_key_allowed2(struct passwd *pw, Key *key, char *file)
@@ -244,13 +302,26 @@ user_key_allowed2(struct passwd *pw, Key *key, char *file)
SSH_FP_HEX);
debug("matching CA found: file %s, line %lu, %s %s",
file, linenum, key_type(found), fp);
- if (key_cert_check_authority(key, 0, 0, pw->pw_name,
- &reason) != 0) {
+ /*
+ * If the user has specified a list of principals as
+ * a key option, then prefer that list to matching
+ * their username in the certificate principals list.
+ */
+ if (authorized_principals != NULL &&
+ !match_principals_option(authorized_principals,
+ key->cert)) {
+ reason = "Certificate does not contain an "
+ "authorized principal";
+ fail_reason:
xfree(fp);
error("%s", reason);
auth_debug_add("%s", reason);
continue;
}
+ if (key_cert_check_authority(key, 0, 0,
+ authorized_principals == NULL ? pw->pw_name : NULL,
+ &reason) != 0)
+ goto fail_reason;
if (auth_cert_options(key, pw) != 0) {
xfree(fp);
continue;
@@ -284,7 +355,7 @@ user_key_allowed2(struct passwd *pw, Key *key, char *file)
static int
user_cert_trusted_ca(struct passwd *pw, Key *key)
{
- char *ca_fp;
+ char *ca_fp, *principals_file = NULL;
const char *reason;
int ret = 0;
@@ -301,11 +372,24 @@ user_cert_trusted_ca(struct passwd *pw, Key *key)
options.trusted_user_ca_keys);
goto out;
}
- if (key_cert_check_authority(key, 0, 1, pw->pw_name, &reason) != 0) {
- error("%s", reason);
- auth_debug_add("%s", reason);
- goto out;
+ /*
+ * If AuthorizedPrincipals is in use, then compare the certificate
+ * principals against the names in that file rather than matching
+ * against the username.
+ */
+ if ((principals_file = authorized_principals_file(pw)) != NULL) {
+ if (!match_principals_file(principals_file, pw, key->cert)) {
+ reason = "Certificate does not contain an "
+ "authorized principal";
+ fail_reason:
+ error("%s", reason);
+ auth_debug_add("%s", reason);
+ goto out;
+ }
}
+ if (key_cert_check_authority(key, 0, 1,
+ principals_file == NULL ? pw->pw_name : NULL, &reason) != 0)
+ goto fail_reason;
if (auth_cert_options(key, pw) != 0)
goto out;
@@ -315,6 +399,8 @@ user_cert_trusted_ca(struct passwd *pw, Key *key)
ret = 1;
out:
+ if (principals_file != NULL)
+ xfree(principals_file);
if (ca_fp != NULL)
xfree(ca_fp);
return ret;
diff --git a/key.c b/key.c
index 34f678b3..c4d9d577 100644
--- a/key.c
+++ b/key.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: key.c,v 1.87 2010/04/16 01:47:26 djm Exp $ */
+/* $OpenBSD: key.c,v 1.88 2010/05/07 11:30:29 djm Exp $ */
/*
* read_bignum():
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@@ -1623,7 +1623,7 @@ key_cert_check_authority(const Key *k, int want_host, int require_principal,
*reason = "Certificate lacks principal list";
return -1;
}
- } else {
+ } else if (name != NULL) {
principal_matches = 0;
for (i = 0; i < k->cert->nprincipals; i++) {
if (strcmp(name, k->cert->principals[i]) == 0) {
diff --git a/servconf.c b/servconf.c
index 7d027ddb..c556986e 100644
--- a/servconf.c
+++ b/servconf.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: servconf.c,v 1.207 2010/03/25 23:38:28 djm Exp $ */
+/* $OpenBSD: servconf.c,v 1.208 2010/05/07 11:30:29 djm Exp $ */
/*
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
* All rights reserved
@@ -131,6 +131,7 @@ initialize_server_options(ServerOptions *options)
options->zero_knowledge_password_authentication = -1;
options->revoked_keys_file = NULL;
options->trusted_user_ca_keys = NULL;
+ options->authorized_principals_file = NULL;
}
void
@@ -310,7 +311,7 @@ typedef enum {
sMatch, sPermitOpen, sForceCommand, sChrootDirectory,
sUsePrivilegeSeparation, sAllowAgentForwarding,
sZeroKnowledgePasswordAuthentication, sHostCertificate,
- sRevokedKeys, sTrustedUserCAKeys,
+ sRevokedKeys, sTrustedUserCAKeys, sAuthorizedPrincipalsFile,
sDeprecated, sUnsupported
} ServerOpCodes;
@@ -432,6 +433,7 @@ static struct {
{ "hostcertificate", sHostCertificate, SSHCFG_GLOBAL },
{ "revokedkeys", sRevokedKeys, SSHCFG_ALL },
{ "trustedusercakeys", sTrustedUserCAKeys, SSHCFG_ALL },
+ { "authorizedprincipalsfile", sAuthorizedPrincipalsFile, SSHCFG_GLOBAL },
{ NULL, sBadOption, 0 }
};
@@ -1218,10 +1220,14 @@ process_server_config_line(ServerOptions *options, char *line,
* AuthorizedKeysFile /etc/ssh_keys/%u
*/
case sAuthorizedKeysFile:
+ charptr = &options->authorized_keys_file;
+ goto parse_tilde_filename;
case sAuthorizedKeysFile2:
- charptr = (opcode == sAuthorizedKeysFile) ?
- &options->authorized_keys_file :
- &options->authorized_keys_file2;
+ charptr = &options->authorized_keys_file2;
+ goto parse_tilde_filename;
+ case sAuthorizedPrincipalsFile:
+ charptr = &options->authorized_principals_file;
+ parse_tilde_filename:
arg = strdelim(&cp);
if (!arg || *arg == '\0')
fatal("%s line %d: missing file name.",
@@ -1682,6 +1688,8 @@ dump_config(ServerOptions *o)
dump_cfg_string(sChrootDirectory, o->chroot_directory);
dump_cfg_string(sTrustedUserCAKeys, o->trusted_user_ca_keys);
dump_cfg_string(sRevokedKeys, o->revoked_keys_file);
+ dump_cfg_string(sAuthorizedPrincipalsFile,
+ o->authorized_principals_file);
/* string arguments requiring a lookup */
dump_cfg_string(sLogLevel, log_level_name(o->log_level));
diff --git a/servconf.h b/servconf.h
index 860009f9..45d2a2ae 100644
--- a/servconf.h
+++ b/servconf.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: servconf.h,v 1.92 2010/03/04 10:36:03 djm Exp $ */
+/* $OpenBSD: servconf.h,v 1.93 2010/05/07 11:30:30 djm Exp $ */
/*
* Author: Tatu Ylonen <ylo@cs.hut.fi>
@@ -156,6 +156,7 @@ typedef struct {
char *chroot_directory;
char *revoked_keys_file;
char *trusted_user_ca_keys;
+ char *authorized_principals_file;
} ServerOptions;
void initialize_server_options(ServerOptions *);
diff --git a/sshd.8 b/sshd.8
index 5f196600..6eb49238 100644
--- a/sshd.8
+++ b/sshd.8
@@ -34,8 +34,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.8,v 1.255 2010/03/05 06:50:35 jmc Exp $
-.Dd $Mdocdate: March 5 2010 $
+.\" $OpenBSD: sshd.8,v 1.256 2010/05/07 11:30:30 djm Exp $
+.Dd $Mdocdate: May 7 2010 $
.Dt SSHD 8
.Os
.Sh NAME
@@ -602,6 +602,17 @@ Multiple
options may be applied separated by commas.
No pattern matching is performed on the specified hostnames,
they must be literal domains or addresses.
+.It Cm principals="principals"
+On a
+.Cm cert-authority
+line, specifies allowed principals for certificate authentication as a
+comma-separated list.
+At least one name from the list must appear in the certificate's
+list of principals for the certificate to be accepted.
+This option is ignored for keys that are not marked as trusted certificate
+signers using the
+.Cm cert-authority
+option.
.It Cm tunnel="n"
Force a
.Xr tun 4
diff --git a/sshd_config.5 b/sshd_config.5
index 2f541028..a5260d35 100644
--- a/sshd_config.5
+++ b/sshd_config.5
@@ -34,8 +34,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.120 2010/03/04 23:17:25 djm Exp $
-.Dd $Mdocdate: March 4 2010 $
+.\" $OpenBSD: sshd_config.5,v 1.121 2010/05/07 11:30:30 djm Exp $
+.Dd $Mdocdate: May 7 2010 $
.Dt SSHD_CONFIG 5
.Os
.Sh NAME
@@ -167,6 +167,43 @@ is taken to be an absolute path or one relative to the user's home
directory.
The default is
.Dq .ssh/authorized_keys .
+.It Cm AuthorizedPrincipalsFile
+Specifies a file that lists principal names that are accepted for
+certificate authentication.
+When using certificates signed by a key listed in
+.Cm TrustedUserCAKeys ,
+this file lists names, one of which must appear in the certificate for it
+to be accepted for authentication.
+Names are listed one per line; empty lines and comments starting with
+.Ql #
+are ignored.
+.Pp
+.Cm AuthorizedPrincipalsFile
+may contain tokens of the form %T which are substituted during connection
+setup.
+The following tokens are defined: %% is replaced 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 AuthorizedPrincipalsFile
+is taken to be an absolute path or one relative to the user's home
+directory.
+.Pp
+The default is not to use a principals file - in this case, the username
+of the user must appear in a certificate's principals list for it to be
+accepted.
+Note that
+.Cm AuthorizedPrincipalsFile
+is only used when authentication proceeds using a CA listed in
+.Cm TrustedUserCAKeys
+and is not consulted for certification authorities trusted via
+.Pa ~/.ssh/authorized_keys ,
+though the
+.Cm principals=
+key option offers a similar facility (see
+.Xr sshd 8
+for details).
+.Pp
.It Cm Banner
The contents of the specified file are sent to the remote user before
authentication is allowed.