Gitlab has successfully been upgraded to 14.0.10 🎉. Enjoy!

Commit d7a94786 authored by Timo Teräs's avatar Timo Teräs
Browse files

main/ca-certificates: fix and optimize update-ca-certificates

fixes #4772
parent 6190391a
......@@ -7,7 +7,7 @@ _nmu="+nmu${pkgver#*_p}"
[ "$_nmu" = "+nmu${pkgver}" ] && _nmu=""
_ver=${pkgver}
pkgrel=2
pkgrel=3
pkgdesc="Common CA certificates PEM files"
url="http://packages.debian.org/sid/ca-certificates"
arch="all"
......@@ -65,8 +65,8 @@ EOF
}
md5sums="717455f13fb31fd014a11a468ea3895d ca-certificates_20150426.tar.xz
0fb7db0c3c2fb694ad80b71a3093f57d update-ca.c"
a4d6874d07ecee797b4c0f6a76f663bd update-ca.c"
sha256sums="37dbaa93ed64cc4ae93ac295f9248fbc741bd51376438cfb1257f17efab5494f ca-certificates_20150426.tar.xz
2bf5b1455e95026cf0a946526b398fc4512e7a9f1daeb7b949c8c0ce12d54761 update-ca.c"
ee01326ae155ae4f31fc6eb3e53ce667dce1eb4150d74e96f0d4f1eea3faf085 update-ca.c"
sha512sums="920dfc512c018c5338bf07b6a6afcb664d9bfba659d4233ca9e87471d5e0ed05de054c96f3d7e6091549aa6deb46106a79f7f982696081f9b2164e18133eb34d ca-certificates_20150426.tar.xz
8c9cfad84039f089f990862cf1b7624a73ec0f600707834e0c05a6d1910a4779ecd7c685884d25fc8597563539ea8860235784f1d6878be0e2393ba02b2e04ed update-ca.c"
97fc20703cca75418e38721b45020f9aec9e3e32ed2bc6e1c6632f5755a46602502291b5c780d8da371525f4ee2e74f94e8573f159cb8023591f23e6759d8afc update-ca.c"
......@@ -6,189 +6,188 @@
#include <dirent.h>
#include <unistd.h>
#include <fcntl.h>
#include <libgen.h>
#include <sys/stat.h>
#include <sys/sendfile.h>
#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
#define CERTSDIR "/usr/share/ca-certificates/"
#define LOCALCERTSDIR "/usr/local/share/ca-certificates/"
#define ETCCERTSDIR "/etc/ssl/certs/"
#define CERTBUNDLE "ca-certificates.crt"
#define CERTSCONF "/etc/ca-certificates.conf"
static const char *last_component(const char *path)
{
const char *c = strrchr(path, '/');
if (c) return c + 1;
return path;
}
static bool str_begins(const char* str, const char* prefix)
{
return !strncmp(str, prefix, strlen(prefix));
}
/* A string pair */
struct pair
{
char** first;
char** second;
struct hash_item {
struct hash_item *next;
char *key;
char *value;
};
/* Total size */
unsigned size;
/* Fill-level */
unsigned count;
struct hash {
struct hash_item *items[256];
};
static void pair_free(struct pair* data)
static unsigned int hash_string(const char *str)
{
int i = 0;
for (i = 0; i < data->size; i++) {
free(data->first[i]);
free(data->second[i]);
}
free(data->first);
free(data->second);
free(data);
unsigned long h = 5381;
for (; *str; str++)
h = (h << 5) + h + *str;
return h;
}
static struct pair* pair_alloc(int size)
static void hash_init(struct hash *h)
{
struct pair* d = (struct pair*) malloc(sizeof(struct pair));
d->size = size;
d->count = 0;
d->first = calloc(size, sizeof(char** ));
d->second = calloc(size, sizeof(char** ));
return d;
memset(h, 0, sizeof *h);
}
static const char*
get_pair(struct pair* data, const char* key, int* pos)
static struct hash_item *hash_get(struct hash *h, const char *key)
{
int i = 0;
for (i = 0; i < data->size; i++) {
*pos = i;
if (data->second[i] && data->first[i]) {
if (str_begins(key, data->second[i]))
return data->first[i];
else if (str_begins(key, data->first[i]))
return data->second[i];
}
unsigned int bucket = hash_string(key) % ARRAY_SIZE(h->items);
struct hash_item *item;
for (item = h->items[bucket]; item; item = item->next)
if (strcmp(item->key, key) == 0)
return item;
return NULL;
}
static void hash_foreach(struct hash *h, void (*cb)(struct hash_item *))
{
struct hash_item *item;
int i;
for (i = 0; i < ARRAY_SIZE(h->items); i++) {
for (item = h->items[i]; item; item = item->next)
cb(item);
}
return 0;
}
static bool
add_ca_from_pem(struct pair* data, const char* ca, const char* pem)
static bool hash_add(struct hash *h, const char *key, const char *value)
{
int count = data->count++;
if (count >= data->size)
unsigned int bucket = hash_string(key) % ARRAY_SIZE(h->items);
size_t keylen = strlen(key), valuelen = strlen(value);
struct hash_item *i;
i = malloc(sizeof(struct hash_item) + keylen + 1 + valuelen + 1);
if (!i)
return false;
data->first[count] = strdup(ca);
data->second[count] = strdup(pem);
i->key = (char*)(i+1);
strcpy(i->key, key);
i->value = i->key + keylen + 1;
strcpy(i->value, value);
i->next = h->items[bucket];
h->items[bucket] = i;
return true;
}
static bool
copyfile(const char* source, int output)
{
int input;
if ((input = open(source, O_RDONLY)) == -1)
return -1;
off_t bytes = 0;
struct stat fileinfo = {0};
fstat(input, &fileinfo);
int result = sendfile(output, input, &bytes, fileinfo.st_size);
ssize_t result;
int in_fd;
if ((in_fd = open(source, O_RDONLY)) == -1)
return false;
if (fstat(in_fd, &fileinfo) < 0) {
close(in_fd);
return false;
}
close(input);
result = sendfile(output, in_fd, &bytes, fileinfo.st_size);
close(in_fd);
return (fileinfo.st_size == result);
return fileinfo.st_size == result;
}
typedef void (*proc_path)(const char*, struct pair*, int);
typedef void (*proc_path)(const char *fullpath, struct hash *, int);
static void proc_localglobaldir(const char* path, struct pair* d, int tmpfile_fd)
static void proc_localglobaldir(const char *fullpath, struct hash *h, int tmpfile_fd)
{
/* basename() requires we duplicate the string */
char* base = strdup(path);
char* tmp_file = strdup(basename(base));
int base_len = strlen(tmp_file);
char* actual_file = 0;
/* Snip off the .crt suffix*/
if (base_len > 4 && strcmp(&tmp_file[base_len - 4], ".crt") == 0)
tmp_file[base_len - 4] = 0;
bool build_string = asprintf(&actual_file, "%s%s%s", "ca-cert-",
tmp_file, ".pem") != -1;
if (base_len > 0 && build_string) {
char* s;
for (s = actual_file; *s != 0; s++) {
switch(*s) {
case ',':
case ' ':
*s = '_';
break;
case ')':
case '(':
*s = '=';
break;
default:
break;
}
}
const char *fname = last_component(fullpath);
size_t flen = strlen(fname);
char *s, *actual_file = NULL;
/* Snip off the .crt suffix */
if (flen > 4 && strcmp(&fname[flen-4], ".crt") == 0)
flen -= 4;
if (asprintf(&actual_file, "%s%.*s%s",
"ca-cert-",
flen, fname,
".pem") == -1) {
fprintf(stderr, "Cannot open path: %s\n", fullpath);
return;
}
if (add_ca_from_pem(d, path, actual_file)) {
if (copyfile(path, tmpfile_fd) == -1)
printf("Cant copy %s\n", path);
} else {
printf("Warn! Cannot add: %s\n", path);
for (s = actual_file; *s; s++) {
switch(*s) {
case ',':
case ' ':
*s = '_';
break;
case ')':
case '(':
*s = '=';
break;
default:
break;
}
} else {
printf("Can't open path: %s\n", path);
}
free(base);
if (!hash_add(h, actual_file, fullpath))
fprintf(stderr, "Warning! Cannot hash: %s\n", fullpath);
if (!copyfile(fullpath, tmpfile_fd))
fprintf(stderr, "Warning! Cannot copy to bundle: %s\n", fullpath);
free(actual_file);
free(tmp_file);
}
static void proc_etccertsdir(const char* path, struct pair* d, int tmpfile_fd)
static void proc_etccertsdir(const char* fullpath, struct hash* h, int tmpfile_fd)
{
struct stat statbuf;
char linktarget[SYMLINK_MAX];
ssize_t linklen;
if (lstat(path, &statbuf) == -1)
linklen = readlink(fullpath, linktarget, sizeof(linktarget)-1);
if (linklen < 0)
return;
linktarget[linklen] = 0;
char* fullpath = (char*) malloc(sizeof(char*) * statbuf.st_size + 1);
if (readlink(path, fullpath, statbuf.st_size + 1) == -1)
return;
char* base = strdup(path);
const char* actual_file = basename(base);
int pos = -1;
const char* target = get_pair(d, actual_file, &pos);
if (!target) {
struct hash_item *item = hash_get(h, last_component(fullpath));
if (!item) {
/* Symlink exists but is not wanted
* Delete it if it points to 'our' directory
*/
if (str_begins(fullpath, CERTSDIR) || str_begins(fullpath, LOCALCERTSDIR))
remove(fullpath);
} else if (strncmp(fullpath, target, strlen(fullpath)) != 0) {
if (str_begins(linktarget, CERTSDIR) || str_begins(linktarget, LOCALCERTSDIR))
unlink(fullpath);
} else if (strcmp(linktarget, item->value) != 0) {
/* Symlink exists but points wrong */
if (symlink(target, path) == -1)
printf("Warning! Can't link %s -> %s\n", target, path);
unlink(fullpath);
if (symlink(item->value, fullpath) < 0)
fprintf(stderr, "Warning! Cannot update symlink %s -> %s\n", item->value, fullpath);
item->value = 0;
} else {
/* Symlink exists and is ok */
memset(d->first[pos], 0, strlen(d->first[pos]));
item->value = 0;
}
free(base);
free(fullpath);
}
static bool file_readline(const char* file, struct pair* d, int tmpfile_fd)
static bool read_global_ca_list(const char* file, struct hash* d, int tmpfile_fd)
{
FILE * fp = fopen(file, "r");
if (fp == NULL)
......@@ -199,14 +198,13 @@ static bool file_readline(const char* file, struct pair* d, int tmpfile_fd)
ssize_t read;
while ((read = getline(&line, &len, fp)) != -1) {
/* getline returns number of bytes in buffer, and buffer
* contains delimeter if it was found */
if (read > 0 && line[read-1] == '\n')
line[read-1] = 0;
if (str_begins(line, "#") || str_begins(line, "!"))
continue;
char* newline = strstr(line, "\n");
if (newline) {
line[newline - line] = '\0';
}
char* fullpath = 0;
if (asprintf(&fullpath,"%s%s", CERTSDIR, line) != -1) {
proc_localglobaldir(fullpath, d, tmpfile_fd);
......@@ -215,9 +213,7 @@ static bool file_readline(const char* file, struct pair* d, int tmpfile_fd)
}
fclose(fp);
if (line)
free(line);
free(line);
return true;
}
......@@ -241,7 +237,7 @@ static bool is_filetype(const char* path, filetype file_check)
return false;
}
static bool dir_readfiles(struct pair* d, const char* path,
static bool dir_readfiles(struct hash* d, const char* path,
filetype allowed_file_type,
proc_path path_processor,
int tmpfile_fd)
......@@ -255,7 +251,6 @@ static bool dir_readfiles(struct pair* d, const char* path,
if (str_begins(dirp->d_name, "."))
continue;
int size = strlen(path) + strlen(dirp->d_name);
char* fullpath = 0;
if (asprintf(&fullpath, "%s%s", path, dirp->d_name) != -1) {
if (is_filetype(fullpath, allowed_file_type))
......@@ -268,45 +263,46 @@ static bool dir_readfiles(struct pair* d, const char* path,
return closedir(dp) == 0;
}
static void update_ca_symlink(struct hash_item *item)
{
if (!item->value)
return;
char* newpath = 0;
bool build_str = asprintf(&newpath, "%s%s", ETCCERTSDIR, item->key);
if (!build_str || symlink(item->value, newpath) == -1)
fprintf(stderr, "Warning! Cannot symlink %s -> %s\n",
item->value, newpath);
free(newpath);
}
int main(int a, char **v)
{
struct pair* calinks = pair_alloc(256);
struct hash _calinks, *calinks = &_calinks;
const char* bundle = "bundleXXXXXX";
int etccertslen = strlen(ETCCERTSDIR);
char* tmpfile = 0;
if (asprintf(&tmpfile, "%s%s", ETCCERTSDIR, bundle) == -1)
return 0;
return 1;
int fd = mkstemp(tmpfile);
if (fd == -1) {
printf("Failed to open temporary file %s for ca bundle\n", tmpfile);
exit(0);
fprintf(stderr, "Failed to open temporary file %s for ca bundle\n", tmpfile);
return 1;
}
fchmod(fd, 0644);
hash_init(calinks);
/* Handle global CA certs from config file */
file_readline(CERTSCONF, calinks, fd);
read_global_ca_list(CERTSCONF, calinks, fd);
/* Handle local CA certificates */
dir_readfiles(calinks, LOCALCERTSDIR, FILE_REGULAR, &proc_localglobaldir, fd);
/* Update etc cert dir for additions and deletions*/
dir_readfiles(calinks, ETCCERTSDIR, FILE_LINK, &proc_etccertsdir, fd);
int i = 0;
for (i = 0; i < calinks->count; i++) {
if (!strlen(calinks->first[i]))
continue;
int file_len = strlen(calinks->second[i]);
char* newpath = 0;
bool build_str = asprintf(&newpath, "%s%s", ETCCERTSDIR,
calinks->second[i]) != -1;
if (!build_str || symlink(calinks->first[i], newpath) == -1)
printf("Warning! Can't link %s -> %s\n",
calinks->first[i], newpath);
free(newpath);
}
hash_foreach(calinks, update_ca_symlink);
/* Update hashes and the bundle */
if (fd != -1) {
......@@ -318,16 +314,13 @@ int main(int a, char **v)
}
}
pair_free(calinks);
free(tmpfile);
/* Execute c_rehash */
int nullfd = open("/dev/null", O_WRONLY);
if (nullfd == -1)
return 0;
if (dup2(nullfd, STDOUT_FILENO) == -1)
return 0;
return 1;
dup2(nullfd, STDOUT_FILENO);
char* c_rehash_args[] = { "/usr/bin/c_rehash", ETCCERTSDIR, 0 };
execve(c_rehash_args[0], c_rehash_args, NULL);
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment