File openssh-6.6p1-restrict_pkcs11-modules.patch of Package openssh.9495
# HG changeset patch
# Parent 177ed550d926f6201af723fbe07f04bdb95b415a
whitelist paths for loading of PKCS#11 modules in ssh-agent
CVE-2016-10009
bsc#1016366
upstream commit 786d5994da79151180cb14a6cf157ebbba61c0cc
diff --git a/openssh-6.6p1/ssh-agent.1 b/openssh-6.6p1/ssh-agent.1
--- a/openssh-6.6p1/ssh-agent.1
+++ b/openssh-6.6p1/ssh-agent.1
@@ -41,16 +41,17 @@
.Nm ssh-agent
.Nd authentication agent
.Sh SYNOPSIS
.Nm ssh-agent
.Op Fl c | s
.Op Fl d
.Op Fl a Ar bind_address
.Op Fl t Ar life
+.Op Fl P Ar pkcs11_whitelist
.Op Ar command Op Ar arg ...
.Nm ssh-agent
.Op Fl c | s
.Fl k
.Sh DESCRIPTION
.Nm
is a program to hold private keys used for public key authentication
(RSA, DSA, ECDSA, ED25519).
@@ -83,16 +84,28 @@ looks like it's a csh style of shell.
Debug mode.
When this option is specified
.Nm
will not fork.
.It Fl k
Kill the current agent (given by the
.Ev SSH_AGENT_PID
environment variable).
+.It Fl P
+Specify a pattern-list of acceptable paths for PKCS#11 shared libraries
+that may be added using the
+.Fl s
+option to
+.Xr ssh-add 1 .
+The default is to allow loading PKCS#11 libraries from
+.Dq /usr/lib/*,/usr/local/lib/* .
+PKCS#11 libraries that do not match the whitelist will be refused.
+See PATTERNS in
+.Xr ssh_config 5
+for a description of pattern-list syntax.
.It Fl s
Generate Bourne shell commands on
.Dv stdout .
This is the default if
.Ev SHELL
does not look like it's a csh style of shell.
.It Fl t Ar life
Set a default value for the maximum lifetime of identities added to the agent.
diff --git a/openssh-6.6p1/ssh-agent.c b/openssh-6.6p1/ssh-agent.c
--- a/openssh-6.6p1/ssh-agent.c
+++ b/openssh-6.6p1/ssh-agent.c
@@ -70,25 +70,30 @@
#include "rsa.h"
#include "buffer.h"
#include "key.h"
#include "authfd.h"
#include "compat.h"
#include "log.h"
#include "misc.h"
#include "digest.h"
+#include "match.h"
#ifdef ENABLE_PKCS11
#include "ssh-pkcs11.h"
#endif
#if defined(HAVE_SYS_PRCTL_H)
#include <sys/prctl.h> /* For prctl() and PR_SET_DUMPABLE */
#endif
+#ifndef DEFAULT_PKCS11_WHITELIST
+# define DEFAULT_PKCS11_WHITELIST "/usr/lib/*,/usr/local/lib/*"
+#endif
+
typedef enum {
AUTH_UNUSED,
AUTH_SOCKET,
AUTH_CONNECTION
} sock_type;
typedef struct {
int fd;
@@ -123,16 +128,19 @@ int max_fd = 0;
/* pid of shell == parent of agent */
pid_t parent_pid = -1;
time_t parent_alive_interval = 0;
/* pathname and directory for AUTH_SOCKET */
char socket_name[MAXPATHLEN];
char socket_dir[MAXPATHLEN];
+/* PKCS#11 path whitelist */
+static char *pkcs11_whitelist;
+
/* locking */
#define LOCK_SIZE 32
#define LOCK_SALT_SIZE 16
#define LOCK_ROUNDS 1
int locked = 0;
char lock_passwd[LOCK_SIZE];
char lock_salt[LOCK_SALT_SIZE];
@@ -611,17 +619,17 @@ no_identities(SocketEntry *e, u_int type
buffer_append(&e->output, buffer_ptr(&msg), buffer_len(&msg));
buffer_free(&msg);
}
#ifdef ENABLE_PKCS11
static void
process_add_smartcard_key(SocketEntry *e)
{
- char *provider = NULL, *pin;
+ char *provider = NULL, *pin, canonical_provider[PATH_MAX];
int i, type, version, count = 0, success = 0, confirm = 0;
time_t death = 0;
Key **keys = NULL, *k;
Identity *id;
Idtab *tab;
provider = buffer_get_string(&e->request, NULL);
pin = buffer_get_string(&e->request, NULL);
@@ -635,29 +643,41 @@ process_add_smartcard_key(SocketEntry *e
confirm = 1;
break;
default:
error("process_add_smartcard_key: "
"Unknown constraint type %d", type);
goto send;
}
}
+ if (realpath(provider, canonical_provider) == NULL) {
+ verbose("failed PKCS#11 add of \"%.100s\": realpath: %s",
+ provider, strerror(errno));
+ goto send;
+ }
+ if (match_pattern_list(canonical_provider, pkcs11_whitelist,
+ strlen(pkcs11_whitelist), 0) != 1) {
+ verbose("refusing PKCS#11 add of \"%.100s\": "
+ "provider not whitelisted", canonical_provider);
+ goto send;
+ }
+ debug("%s: add %.100s", __func__, canonical_provider);
if (lifetime && !death)
death = monotime() + lifetime;
- count = pkcs11_add_provider(provider, pin, &keys);
+ count = pkcs11_add_provider(canonical_provider, pin, &keys);
for (i = 0; i < count; i++) {
k = keys[i];
version = k->type == KEY_RSA1 ? 1 : 2;
tab = idtab_lookup(version);
if (lookup_identity(k, version) == NULL) {
id = xcalloc(1, sizeof(Identity));
id->key = k;
- id->provider = xstrdup(provider);
- id->comment = xstrdup(provider); /* XXX */
+ id->provider = xstrdup(canonical_provider);
+ id->comment = xstrdup(canonical_provider); /* XXX */
id->death = death;
id->confirm = confirm;
TAILQ_INSERT_TAIL(&tab->idlist, id, next);
tab->nentries++;
success = 1;
} else {
key_free(k);
}
@@ -1031,16 +1051,18 @@ usage(void)
__progname);
fprintf(stderr, "Options:\n");
fprintf(stderr, " -c Generate C-shell commands on stdout.\n");
fprintf(stderr, " -s Generate Bourne shell commands on stdout.\n");
fprintf(stderr, " -k Kill the current agent.\n");
fprintf(stderr, " -d Debug mode.\n");
fprintf(stderr, " -a socket Bind agent socket to given name.\n");
fprintf(stderr, " -t life Default identity lifetime (seconds).\n");
+ fprintf(stderr, " -P pkcs11_whitelist\n");
+ fprintf(stderr, " whitelist of directories for loading PKCS#11 modules from.\n");
exit(1);
}
int
main(int ac, char **av)
{
int c_flag = 0, d_flag = 0, k_flag = 0, s_flag = 0;
int sock, fd, ch, result, saved_errno;
@@ -1071,26 +1093,31 @@ main(int ac, char **av)
prctl(PR_SET_DUMPABLE, 0);
#endif
OpenSSL_add_all_algorithms();
__progname = ssh_get_progname(av[0]);
seed_rng();
- while ((ch = getopt(ac, av, "cdksa:t:")) != -1) {
+ while ((ch = getopt(ac, av, "cdksa:P:t:")) != -1) {
switch (ch) {
case 'c':
if (s_flag)
usage();
c_flag++;
break;
case 'k':
k_flag++;
break;
+ case 'P':
+ if (pkcs11_whitelist != NULL)
+ fatal("-P option already specified");
+ pkcs11_whitelist = xstrdup(optarg);
+ break;
case 's':
if (c_flag)
usage();
s_flag++;
break;
case 'd':
if (d_flag)
usage();
@@ -1110,16 +1137,19 @@ main(int ac, char **av)
}
}
ac -= optind;
av += optind;
if (ac > 0 && (c_flag || k_flag || s_flag || d_flag))
usage();
+ if (pkcs11_whitelist == NULL)
+ pkcs11_whitelist = xstrdup(DEFAULT_PKCS11_WHITELIST);
+
if (ac == 0 && !c_flag && !s_flag) {
shell = getenv("SHELL");
if (shell != NULL && (len = strlen(shell)) > 2 &&
strncmp(shell + len - 3, "csh", 3) == 0)
c_flag = 1;
}
if (k_flag) {
const char *errstr = NULL;