-/* $OpenBSD: auth2-pubkey.c,v 1.50 2015/05/21 06:38:35 djm Exp $ */
+/* $OpenBSD: auth2-pubkey.c,v 1.51 2015/05/21 06:43:30 djm Exp $ */
/*
* Copyright (c) 2000 Markus Friedl. All rights reserved.
*
}
static int
-match_principals_file(char *file, struct passwd *pw, struct sshkey_cert *cert)
+process_principals(FILE *f, char *file, struct passwd *pw,
+ struct sshkey_cert *cert)
{
- FILE *f;
char line[SSH_MAX_PUBKEY_BYTES], *cp, *ep, *line_opts;
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. */
for (cp = line; *cp == ' ' || *cp == '\t'; cp++)
}
for (i = 0; i < cert->nprincipals; i++) {
if (strcmp(cp, cert->principals[i]) == 0) {
- debug3("matched principal \"%.100s\" "
- "from file \"%s\" on line %lu",
- cert->principals[i], file, linenum);
+ debug3("%s:%lu: matched principal \"%.100s\"",
+ file == NULL ? "(command)" : file,
+ linenum, cert->principals[i]);
if (auth_parse_options(pw, line_opts,
file, linenum) != 1)
continue;
- fclose(f);
- restore_uid();
return 1;
}
}
}
+ return 0;
+}
+
+static int
+match_principals_file(char *file, struct passwd *pw, struct sshkey_cert *cert)
+{
+ FILE *f;
+ int success;
+
+ 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;
+ }
+ success = process_principals(f, file, pw, cert);
fclose(f);
restore_uid();
- return 0;
+ return success;
}
+/*
+ * Checks whether principal is allowed in output of command.
+ * returns 1 if the principal is allowed or 0 otherwise.
+ */
+static int
+match_principals_command(struct passwd *user_pw, struct sshkey *key)
+{
+ FILE *f = NULL;
+ int ok, found_principal = 0;
+ struct passwd *pw;
+ int i, ac = 0, uid_swapped = 0;
+ pid_t pid;
+ char *tmp, *username = NULL, *command = NULL, **av = NULL;
+ void (*osigchld)(int);
+
+ if (options.authorized_principals_command == NULL)
+ return 0;
+ if (options.authorized_principals_command_user == NULL) {
+ error("No user for AuthorizedPrincipalsCommand specified, "
+ "skipping");
+ return 0;
+ }
+
+ /*
+ * NB. all returns later this function should go via "out" to
+ * ensure the original SIGCHLD handler is restored properly.
+ */
+ osigchld = signal(SIGCHLD, SIG_DFL);
+
+ /* Prepare and verify the user for the command */
+ username = percent_expand(options.authorized_principals_command_user,
+ "u", user_pw->pw_name, (char *)NULL);
+ pw = getpwnam(username);
+ if (pw == NULL) {
+ error("AuthorizedPrincipalsCommandUser \"%s\" not found: %s",
+ username, strerror(errno));
+ goto out;
+ }
+
+ /* Turn the command into an argument vector */
+ if (split_argv(options.authorized_principals_command, &ac, &av) != 0) {
+ error("AuthorizedPrincipalsCommand \"%s\" contains "
+ "invalid quotes", command);
+ goto out;
+ }
+ if (ac == 0) {
+ error("AuthorizedPrincipalsCommand \"%s\" yielded no arguments",
+ command);
+ goto out;
+ }
+ for (i = 1; i < ac; i++) {
+ tmp = percent_expand(av[i],
+ "u", user_pw->pw_name,
+ "h", user_pw->pw_dir,
+ (char *)NULL);
+ if (tmp == NULL)
+ fatal("%s: percent_expand failed", __func__);
+ free(av[i]);
+ av[i] = tmp;
+ }
+ /* Prepare a printable command for logs, etc. */
+ command = assemble_argv(ac, av);
+
+ if ((pid = subprocess("AuthorizedPrincipalsCommand", pw, command,
+ ac, av, &f)) == 0)
+ goto out;
+
+ uid_swapped = 1;
+ temporarily_use_uid(pw);
+
+ ok = process_principals(f, NULL, pw, key->cert);
+
+ if (exited_cleanly(pid, "AuthorizedPrincipalsCommand", command) != 0)
+ goto out;
+
+ /* Read completed successfully */
+ found_principal = ok;
+ out:
+ if (f != NULL)
+ fclose(f);
+ signal(SIGCHLD, osigchld);
+ for (i = 0; i < ac; i++)
+ free(av[i]);
+ free(av);
+ if (uid_swapped)
+ restore_uid();
+ free(command);
+ free(username);
+ return found_principal;
+}
/*
* Checks whether key is allowed in authorized_keys-format file,
* returns 1 if the key is allowed or 0 otherwise.
{
char *ca_fp, *principals_file = NULL;
const char *reason;
- int ret = 0;
+ int ret = 0, found_principal = 0;
if (!key_is_cert(key) || options.trusted_user_ca_keys == NULL)
return 0;
* 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";
+ if (match_principals_file(principals_file, pw, key->cert))
+ found_principal = 1;
+ }
+ /* Try querying command if specified */
+ if (!found_principal && match_principals_command(pw, key))
+ found_principal = 1;
+ /* If principals file or command specify, then require a match here */
+ if (!found_principal && (principals_file != NULL ||
+ options.authorized_principals_command != NULL)) {
+ reason = "Certificate does not contain an authorized principal";
fail_reason:
- error("%s", reason);
- auth_debug_add("%s", reason);
- goto out;
- }
+ 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)
-/* $OpenBSD: servconf.c,v 1.269 2015/05/04 06:10:48 djm Exp $ */
+/* $OpenBSD: servconf.c,v 1.270 2015/05/21 06:43:30 djm Exp $ */
/*
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
* All rights reserved
options->revoked_keys_file = NULL;
options->trusted_user_ca_keys = NULL;
options->authorized_principals_file = NULL;
+ options->authorized_principals_command = NULL;
+ options->authorized_principals_command_user = NULL;
options->ip_qos_interactive = -1;
options->ip_qos_bulk = -1;
options->version_addendum = NULL;
sUsePrivilegeSeparation, sAllowAgentForwarding,
sHostCertificate,
sRevokedKeys, sTrustedUserCAKeys, sAuthorizedPrincipalsFile,
+ sAuthorizedPrincipalsCommand, sAuthorizedPrincipalsCommandUser,
sKexAlgorithms, sIPQoS, sVersionAddendum,
sAuthorizedKeysCommand, sAuthorizedKeysCommandUser,
sAuthenticationMethods, sHostKeyAgent, sPermitUserRC,
{ "ipqos", sIPQoS, SSHCFG_ALL },
{ "authorizedkeyscommand", sAuthorizedKeysCommand, SSHCFG_ALL },
{ "authorizedkeyscommanduser", sAuthorizedKeysCommandUser, SSHCFG_ALL },
+ { "authorizedprincipalscommand", sAuthorizedPrincipalsCommand, SSHCFG_ALL },
+ { "authorizedprincipalscommanduser", sAuthorizedPrincipalsCommandUser, SSHCFG_ALL },
{ "versionaddendum", sVersionAddendum, SSHCFG_GLOBAL },
{ "authenticationmethods", sAuthenticationMethods, SSHCFG_ALL },
{ "streamlocalbindmask", sStreamLocalBindMask, SSHCFG_ALL },
*charptr = xstrdup(arg);
break;
+ case sAuthorizedPrincipalsCommand:
+ if (cp == NULL)
+ fatal("%.200s line %d: Missing argument.", filename,
+ linenum);
+ len = strspn(cp, WHITESPACE);
+ if (*activep &&
+ options->authorized_principals_command == NULL) {
+ if (cp[len] != '/' && strcasecmp(cp + len, "none") != 0)
+ fatal("%.200s line %d: "
+ "AuthorizedPrincipalsCommand must be "
+ "an absolute path", filename, linenum);
+ options->authorized_principals_command =
+ xstrdup(cp + len);
+ }
+ return 0;
+
+ case sAuthorizedPrincipalsCommandUser:
+ charptr = &options->authorized_principals_command_user;
+
+ arg = strdelim(&cp);
+ if (!arg || *arg == '\0')
+ fatal("%s line %d: missing "
+ "AuthorizedPrincipalsCommandUser argument.",
+ filename, linenum);
+ if (*activep && *charptr == NULL)
+ *charptr = xstrdup(arg);
+ break;
+
case sAuthenticationMethods:
if (options->num_auth_methods == 0) {
while ((arg = strdelim(&cp)) && *arg != '\0') {
? "none" : o->version_addendum);
dump_cfg_string(sAuthorizedKeysCommand, o->authorized_keys_command);
dump_cfg_string(sAuthorizedKeysCommandUser, o->authorized_keys_command_user);
+ dump_cfg_string(sAuthorizedPrincipalsCommand, o->authorized_principals_command);
+ dump_cfg_string(sAuthorizedPrincipalsCommandUser, o->authorized_principals_command_user);
dump_cfg_string(sHostKeyAgent, o->host_key_agent);
dump_cfg_string(sKexAlgorithms,
o->kex_algorithms ? o->kex_algorithms : KEX_SERVER_KEX);
-/* $OpenBSD: servconf.h,v 1.117 2015/04/29 03:48:56 dtucker Exp $ */
+/* $OpenBSD: servconf.h,v 1.118 2015/05/21 06:43:31 djm Exp $ */
/*
* Author: Tatu Ylonen <ylo@cs.hut.fi>
char *chroot_directory;
char *revoked_keys_file;
char *trusted_user_ca_keys;
- char *authorized_principals_file;
char *authorized_keys_command;
char *authorized_keys_command_user;
+ char *authorized_principals_file;
+ char *authorized_principals_command;
+ char *authorized_principals_command_user;
int64_t rekey_limit;
int rekey_interval;
M_CP_STROPT(banner); \
M_CP_STROPT(trusted_user_ca_keys); \
M_CP_STROPT(revoked_keys_file); \
- M_CP_STROPT(authorized_principals_file); \
M_CP_STROPT(authorized_keys_command); \
M_CP_STROPT(authorized_keys_command_user); \
+ M_CP_STROPT(authorized_principals_file); \
+ M_CP_STROPT(authorized_principals_command); \
+ M_CP_STROPT(authorized_principals_command_user); \
M_CP_STROPT(hostbased_key_types); \
M_CP_STROPT(pubkey_key_types); \
M_CP_STRARRAYOPT(authorized_keys_files, num_authkeys_files); \
-/* $OpenBSD: sshd.c,v 1.448 2015/04/27 00:21:21 djm Exp $ */
+/* $OpenBSD: sshd.c,v 1.449 2015/05/21 06:43:31 djm Exp $ */
/*
* Author: Tatu Ylonen <ylo@cs.hut.fi>
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
strcasecmp(options.authorized_keys_command, "none") != 0))
fatal("AuthorizedKeysCommand set without "
"AuthorizedKeysCommandUser");
+ if (options.authorized_principals_command_user == NULL &&
+ (options.authorized_principals_command != NULL &&
+ strcasecmp(options.authorized_principals_command, "none") != 0))
+ fatal("AuthorizedPrincipalsCommand set without "
+ "AuthorizedPrincipalsCommandUser");
/*
* Check whether there is any path through configured auth methods.
.\" (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.201 2015/05/21 06:38:35 djm Exp $
+.\" $OpenBSD: sshd_config.5,v 1.202 2015/05/21 06:43:31 djm Exp $
.Dd $Mdocdate: May 21 2015 $
.Dt SSHD_CONFIG 5
.Os
Multiple files may be listed, separated by whitespace.
The default is
.Dq .ssh/authorized_keys .ssh/authorized_keys2 .
+.It Cm AuthorizedPrincipalsCommand
+Specifies a program to be used to generate the list of allowed
+certificate principals as per
+.Cm AuthorizedPrincipalsFile .
+The program must be owned by root, not writable by group or others and
+specified by an absolute path.
+.Pp
+Arguments to
+.Cm AuthorizedPrincipalsCommand
+may be provided using the following tokens, which will be expanded
+at runtime: %% is replaced by a literal '%', %u is replaced by the
+username being authenticated and %h is replaced by the home directory
+of the user being authenticated.
+.Pp
+The program should produce on standard output zero or
+more lines of
+.Cm AuthorizedPrincipalsFile
+output.
+If either
+.Cm AuthorizedPrincipalsCommand
+or
+.Cm AuthorizedPrincipalsFile
+is specified, then certificates offered by the client for authentication
+must contain a principal that is listed.
+By default, no AuthorizedPrincipalsCommand is run.
+.It Cm AuthorizedPrincipalsCommandUser
+Specifies the user under whose account the AuthorizedPrincipalsCommand is run.
+It is recommended to use a dedicated user that has no other role on the host
+than running authorized principals commands.
+If
+.Cm AuthorizedPrincipalsCommand
+is specified but
+.Cm AuthorizedPrincipalsCommandUser
+is not, then
+.Xr sshd 8
+will refuse to start.
.It Cm AuthorizedPrincipalsFile
Specifies a file that lists principal names that are accepted for
certificate authentication.