MINOR: ssl: load certificates in alphabetical order

As reported by Raphal Enrici, certificates loaded from a directory are loaded
in a non predictive order. If no certificate was first loaded from a file, it
can result in different behaviours when haproxy is used in cluster.
We can also imagine other cases which weren't met yet.

Instead of using readdir(), we can use scandir() and sort files alphabetically.
This will ensure a predictive behaviour.

This patch should also be backported to 1.5.
This commit is contained in:
Cyril Bont 2015-01-25 00:16:08 +01:00 committed by Willy Tarreau
parent c4dc35047d
commit 3180f7b554
2 changed files with 37 additions and 22 deletions

View File

@ -8553,20 +8553,21 @@ crt <cert>
are loaded.
If a directory name is used instead of a PEM file, then all files found in
that directory will be loaded unless their name ends with '.issuer' or
'.ocsp' (reserved extensions). This directive may be specified multiple times
in order to load certificates from multiple files or directories. The
certificates will be presented to clients who provide a valid TLS Server Name
Indication field matching one of their CN or alt subjects. Wildcards are
supported, where a wildcard character '*' is used instead of the first
hostname component (eg: *.example.org matches www.example.org but not
that directory will be loaded in alphabetic order unless their name ends with
'.issuer' or '.ocsp' (reserved extensions). This directive may be specified
multiple times in order to load certificates from multiple files or
directories. The certificates will be presented to clients who provide a valid
TLS Server Name Indication field matching one of their CN or alt subjects.
Wildcards are supported, where a wildcard character '*' is used instead of the
first hostname component (eg: *.example.org matches www.example.org but not
www.sub.example.org).
If no SNI is provided by the client or if the SSL library does not support
TLS extensions, or if the client provides an SNI hostname which does not
match any certificate, then the first loaded certificate will be presented.
This means that when loading certificates from a directory, it is highly
recommended to load the default one first as a file.
recommended to load the default one first as a file or to ensure that it will
always be the first one in the directory.
Note that the same cert may be loaded multiple times without side effects.

View File

@ -1308,7 +1308,8 @@ static int ssl_sock_load_cert_file(const char *path, struct bind_conf *bind_conf
int ssl_sock_load_cert(char *path, struct bind_conf *bind_conf, struct proxy *curproxy, char **err)
{
struct dirent *de;
struct dirent **de_list;
int i, n;
DIR *dir;
struct stat buf;
char *end;
@ -1322,21 +1323,34 @@ int ssl_sock_load_cert(char *path, struct bind_conf *bind_conf, struct proxy *cu
for (end = path + strlen(path) - 1; end >= path && *end == '/'; end--)
*end = 0;
while ((de = readdir(dir))) {
end = strrchr(de->d_name, '.');
if (end && (!strcmp(end, ".issuer") || !strcmp(end, ".ocsp")))
continue;
n = scandir(path, &de_list, 0, alphasort);
if (n < 0) {
memprintf(err, "%sunable to scan directory '%s' : %s.\n",
err && *err ? *err : "", path, strerror(errno));
cfgerr++;
}
else {
for (i = 0; i < n; i++) {
struct dirent *de = de_list[i];
snprintf(fp, sizeof(fp), "%s/%s", path, de->d_name);
if (stat(fp, &buf) != 0) {
memprintf(err, "%sunable to stat SSL certificate from file '%s' : %s.\n",
err && *err ? *err : "", fp, strerror(errno));
cfgerr++;
continue;
end = strrchr(de->d_name, '.');
if (end && (!strcmp(end, ".issuer") || !strcmp(end, ".ocsp")))
goto ignore_entry;
snprintf(fp, sizeof(fp), "%s/%s", path, de->d_name);
if (stat(fp, &buf) != 0) {
memprintf(err, "%sunable to stat SSL certificate from file '%s' : %s.\n",
err && *err ? *err : "", fp, strerror(errno));
cfgerr++;
goto ignore_entry;
}
if (!S_ISREG(buf.st_mode))
goto ignore_entry;
cfgerr += ssl_sock_load_cert_file(fp, bind_conf, curproxy, NULL, 0, err);
ignore_entry:
free(de);
}
if (!S_ISREG(buf.st_mode))
continue;
cfgerr += ssl_sock_load_cert_file(fp, bind_conf, curproxy, NULL, 0, err);
free(de_list);
}
closedir(dir);
return cfgerr;