- 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@
This commit is contained in:
Damien Miller 2010-05-10 11:58:03 +10:00
parent 099fc1634e
commit 30da3447d2
11 changed files with 262 additions and 34 deletions

View File

@ -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

View File

@ -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) {

View File

@ -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);

41
auth.c
View File

@ -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)
{

4
auth.h
View File

@ -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

View File

@ -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;

4
key.c
View File

@ -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) {

View File

@ -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));

View File

@ -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 *);

15
sshd.8
View File

@ -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

View File

@ -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.