~alpine/aports

[alpine-aports] [PATCH v3.2] main/ca-certificates: security upgrade to 20161130

Details
Message ID
<1485511494-5523-1-git-send-email-sergej.lukin@gmail.com>
Sender timestamp
1485511494
DKIM signature
missing
Download raw message
Patch: +380 -106
ref #6528
---
 main/ca-certificates/APKBUILD               |  60 +++--
 main/ca-certificates/fix-manpage.patch      |  13 ++
 main/ca-certificates/update-ca-certificates |  86 --------
 main/ca-certificates/update-ca.c            | 327 ++++++++++++++++++++++++++++
 4 files changed, 380 insertions(+), 106 deletions(-)
 create mode 100644 main/ca-certificates/fix-manpage.patch
 delete mode 100755 main/ca-certificates/update-ca-certificates
 create mode 100644 main/ca-certificates/update-ca.c

diff --git a/main/ca-certificates/APKBUILD b/main/ca-certificates/APKBUILD
index 8f5d3fc..84e0ca6 100644
--- a/main/ca-certificates/APKBUILD
+++ b/main/ca-certificates/APKBUILD
@@ -1,30 +1,40 @@
# Contributor: Sergei Lukin <sergej.lukin@gmail.com>
# Maintainer: Natanael Copa <ncopa@alpinelinux.org>
pkgname=ca-certificates
pkgver=20141019

_date=${pkgver%_p*}
_nmu="+nmu${pkgver#*_p}"
[ "$_nmu" = "+nmu${pkgver}" ] && _nmu=""
_ver=${pkgver}

pkgrel=2
pkgver=20161130
pkgrel=0
pkgdesc="Common CA certificates PEM files"
url="http://packages.debian.org/sid/ca-certificates"
arch="noarch"
arch="all"
license="MPL 2.0 GPL2+"
depends="run-parts openssl lua5.2 lua5.2-posix"
makedepends="python"
subpackages="$pkgname-doc"
options="!fhs"
triggers="ca-certificates.trigger=/usr/share/ca-certificates:/usr/local/share/ca-certificates:/etc/ssl/certs"
source="http://ftp.no.debian.org/debian/pool/main/c/$pkgname/${pkgname}_${_ver}.tar.xz
	update-ca-certificates
triggers="ca-certificates.trigger=/usr/share/ca-certificates:/usr/local/share/ca-certificates:/etc/ssl/certs:/etc/ca-certificates/update.d"
source="http://ftp.no.debian.org/debian/pool/main/c/$pkgname/${pkgname}_${pkgver}.tar.xz
	fix-manpage.patch
	update-ca.c
	"

_builddir="$srcdir"/$pkgname-$_ver
_builddir="$srcdir"/$pkgname

prepare() {
	cd "$_builddir"
	for i in $source; do
		case $i in
		*.patch) msg $i; patch -p1 -i "$srcdir"/$i || return 1;; 
		esac
	done
}

build () {
	cd "$_builddir"
	make || return 1


        ${CC:-gcc} ${CFLAGS} -o update-ca-certificates "$srcdir"/update-ca.c \
		${LDFLAGS} || return 1
}

package() {
@@ -35,6 +45,7 @@ package() {
		"$pkgdir"/usr/local/share/ca-certificates \
		"$pkgdir"/etc/ssl/certs \
		|| return 1

	make DESTDIR="$pkgdir" install || return 1
	install -D -m644 sbin/update-ca-certificates.8 \
		"$pkgdir"/usr/share/man/man8/update-ca-certificates.8 \
@@ -50,7 +61,7 @@ package() {

	# http://bugs.alpinelinux.org/issues/2715
	# http://bugs.alpinelinux.org/issues/2846
	install -m755 "$srcdir"/update-ca-certificates "$pkgdir"/usr/sbin \
	install -m755 update-ca-certificates "$pkgdir"/usr/sbin \
		|| return 1

	mkdir -p "$pkgdir"/etc/apk/protected_paths.d
@@ -59,11 +70,20 @@ package() {
-etc/ssl/certs/ca-cert-*.pem
-etc/ssl/certs/[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f].[r0-9]*
EOF

        cat > "$pkgdir"/etc/ca-certificates/update.d/c_rehash <<EOF
#!/bin/sh
exec /usr/bin/c_rehash /etc/ssl/certs
EOF
        chmod +x "$pkgdir"/etc/ca-certificates/update.d/c_rehash || return 1
}

md5sums="f619282081c8bfc65ea64c37fa5285ed  ca-certificates_20141019.tar.xz
0c2fb9aa695d9d857fecd1c236930016  update-ca-certificates"
sha256sums="684902d3f4e9ad27829f4af0d9d2d588afed03667997579b9c2be86fcd1eb73a  ca-certificates_20141019.tar.xz
b95a80d5881a3ffeea3f36599503a141f9c5a433bc9646d673225658ebc032a1  update-ca-certificates"
sha512sums="5b0e8fb917f5642a5a2b4fde46a706db0c652ff3fb31a5053d9123a5b670b50c6e3cf2496915cc01c613dcbe964d6432f393c12d8a697baedfad58f9d13e568b  ca-certificates_20141019.tar.xz
ce0e6317af25a5433a4fef28db6afd0ef985089f4a6b9eb13ac1ca454de854ae3de18029fed1e385651317cb237581a38d3792c42f5f30ec12667609d689b4e1  update-ca-certificates"
md5sums="1a0a3a1b3390dc83affed4b0c2ae1c05  ca-certificates_20161130.tar.xz
0c3d9f5d795c7475b997e18498b7aec8  fix-manpage.patch
d7773c690432a56911c0fa97a67bf43e  update-ca.c"
sha256sums="04bca9e142a90a834aca0311f7ced237368d71fee7bd5c9f68ef7f4611aee471  ca-certificates_20161130.tar.xz
60b36c4881bb367891df038a0736456c2d170496de8c339026671008b1caa09b  fix-manpage.patch
b2231e7f0304d6360350f1f1aa9afad46fe4eeb0f0306a48eeb8cd74501ab26c  update-ca.c"
sha512sums="8395f27d2369d694b069e1bb250b06df05f732bd9f4a4dc8652091e9c96ad1a84003e28f59cb9e13fdfd22ca5818f495d80149692e74b2d63e34db4f6a95ee9f  ca-certificates_20161130.tar.xz
690d6bb434fb3ccce931d7ee6a167124f9c2d2e7e7a016d85f7b72a5f7f7c34db8c6133f3575e962a91981a32a88f8961776fe5fd907e57f59c03a32f2fcced3  fix-manpage.patch
403c909ea4107d944789b8aae9c911c735ac651cf882bf8468e0f91b3179700a78cabcbec16e3ae0bed730b54d1813d5d0891c1aeae583ad80c7a20b0f425b12  update-ca.c"
diff --git a/main/ca-certificates/fix-manpage.patch b/main/ca-certificates/fix-manpage.patch
new file mode 100644
index 0000000..c4c1290
--- /dev/null
+++ b/main/ca-certificates/fix-manpage.patch
@@ -0,0 +1,13 @@
--- ./sbin/update-ca-certificates.8.orig	2016-01-14 10:56:42.084504796 +0100
+++ ./sbin/update-ca-certificates.8	2016-01-14 10:57:21.685102125 +0100
@@ -40,9 +40,7 @@
 /usr/local/share/ca-certificates are also included as implicitly trusted.
 .PP
 Before terminating, \fBupdate-ca-certificates\fP invokes
-\fBrun-parts\fP on /etc/ca-certificates/update.d and calls each hook with
-a list of certificates: those added are prefixed with a +, those removed are
-prefixed with a -.
+\fBrun-parts\fP on /etc/ca-certificates/update.d.
 .SH OPTIONS
 A summary of options is included below.
 .TP
diff --git a/main/ca-certificates/update-ca-certificates b/main/ca-certificates/update-ca-certificates
deleted file mode 100755
index 1780ce5..0000000
--- a/main/ca-certificates/update-ca-certificates
@@ -1,86 +0,0 @@
#!/usr/bin/lua5.2

local CERTSDIR='/usr/share/ca-certificates/'
local LOCALCERTSDIR='/usr/local/share/ca-certificates/'
local ETCCERTSDIR='/etc/ssl/certs/'
local CERTBUNDLE='ca-certificates.crt'
local CERTSCONF='/etc/ca-certificates.conf'

local posix = require 'posix'
function string.begins(str, prefix) return str:sub(1,#prefix)==prefix end

local function add(fn, out, links)
	-- Map fn to file in etc
	local pem = "ca-cert-"..fn:gsub('.*/', ''):gsub('.crt$',''):gsub('[, ]','_'):gsub('[()]','=')..".pem"
	links[pem] = fn
	-- Read the certificate for the bundle
	local f = io.open(fn, "rb")
	if f ~= nil then
		local content = f:read("*all")
		f:close()
		out:write(content)
		if content:sub(-1) ~= '\n' then out:write('\n') end
	end
end

local calinks = {}
local cacerts = {}

local fd, tmpfile = posix.mkstemp(ETCCERTSDIR..'bundleXXXXXX')
if not fd then
	print("Failed to open temporary file for ca bundle")
	return 1
end
posix.close(fd)
posix.chmod(tmpfile, "rw-r--r--")
local bundle = io.open(tmpfile, "wb")

--- Handle global CA certs from config file
for l in io.lines(CERTSCONF) do
	local firstchar = l:sub(1,1)
	if firstchar ~= "#" and firstchar ~= "!" then
		add(CERTSDIR..l, bundle, calinks)
	end
end

--- Handle local CA certificates
local certlist = posix.glob(LOCALCERTSDIR..'*.crt')
if certlist ~= nil then
	table.sort(certlist)
	for _, fn in ipairs(certlist) do
		if posix.stat(fn, 'type') == 'regular' then
			add(fn, bundle, calinks)
		end
	end
end

--- Update etc cert dir for additions and deletions
local f, target
for f in posix.files(ETCCERTSDIR) do
	local fn = ETCCERTSDIR..f
	if posix.stat(fn, 'type') == 'link' then
		local curtgt = posix.readlink(fn)
		local target = calinks[f]
		if target == nil then
			-- Symlink exists but is not wanted
			-- Delete it if it points to 'our' directory
			if curtgt:begins(CERTSDIR) or curtgt:begins(LOCALCERTSDIR) then
				os.remove(fn)
			end
		elseif curtgt ~= target then
			-- Symlink exists but points wrong
			posix.link(target, ETCCERTSDIR..f, true)
		else
			-- Symlink exists and is ok
			calinks[f] = nil
		end
	end
end
for f, target in pairs(calinks) do
	posix.link(target, ETCCERTSDIR..f, true)
end

--- Update hashes and the bundle
bundle:close()
os.rename(tmpfile, ETCCERTSDIR..CERTBUNDLE)
os.execute("c_rehash "..ETCCERTSDIR.." > /dev/null")
diff --git a/main/ca-certificates/update-ca.c b/main/ca-certificates/update-ca.c
new file mode 100644
index 0000000..20af994
--- /dev/null
+++ b/main/ca-certificates/update-ca.c
@@ -0,0 +1,327 @@
#define _GNU_SOURCE
#include <stdio.h>
#include <stdbool.h>
#include <string.h>
#include <stdlib.h>
#include <dirent.h>
#include <unistd.h>
#include <fcntl.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 RUNPARTSDIR "/etc/ca-certificates/update.d/"
#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));
}

struct hash_item {
	struct hash_item *next;
	char *key;
	char *value;
};

struct hash {
	struct hash_item *items[256];
};

static unsigned int hash_string(const char *str)
{
	unsigned long h = 5381;
	for (; *str; str++)
		h = (h << 5) + h + *str;
	return h;
}

static void hash_init(struct hash *h)
{
	memset(h, 0, sizeof *h);
}

static struct hash_item *hash_get(struct hash *h, const char *key)
{
	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);
	}
}

static bool hash_add(struct hash *h, const char *key, const char *value)
{
	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;

	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)
{
	off_t bytes = 0;
	struct stat fileinfo = {0};
	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;
	}

	result = sendfile(output, in_fd, &bytes, fileinfo.st_size);
	close(in_fd);

	return fileinfo.st_size == result;
}

typedef void (*proc_path)(const char *fullpath, struct hash *, int);

static void proc_localglobaldir(const char *fullpath, struct hash *h, int tmpfile_fd)
{
	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;
	}

	for (s = actual_file; *s; s++) {
		switch(*s) {
		case ',':
		case ' ':
			*s = '_';
			break;
		case ')':
		case '(':
			*s = '=';
			break;
		default:
			break;
		}
	}

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

static void proc_etccertsdir(const char* fullpath, struct hash* h, int tmpfile_fd)
{
	char linktarget[SYMLINK_MAX];
	ssize_t linklen;

	linklen = readlink(fullpath, linktarget, sizeof(linktarget)-1);
	if (linklen < 0)
		return;
	linktarget[linklen] = 0;

	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(linktarget, CERTSDIR) || str_begins(linktarget, LOCALCERTSDIR))
			unlink(fullpath);
	} else if (strcmp(linktarget, item->value) != 0) {
		/* Symlink exists but points wrong */
		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 */
		item->value = 0;
	}
}

static bool read_global_ca_list(const char* file, struct hash* d, int tmpfile_fd)
{
	FILE * fp = fopen(file, "r");
	if (fp == NULL)
		return false;

	char * line = NULL;
	size_t len = 0;
	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* fullpath = 0;
		if (asprintf(&fullpath,"%s%s", CERTSDIR, line) != -1) {
			proc_localglobaldir(fullpath, d, tmpfile_fd);
			free(fullpath);
		}
	}

	fclose(fp);
	free(line);
	return true;
}

typedef enum {
	FILE_LINK,
	FILE_REGULAR
} filetype;

static bool is_filetype(const char* path, filetype file_check)
{
	struct stat statbuf;

	if (lstat(path, &statbuf) < 0)
		return false;
	switch(file_check) {
	case FILE_LINK: return S_ISLNK(statbuf.st_mode);
	case FILE_REGULAR: return S_ISREG(statbuf.st_mode);
	default: break;
	}

	return false;
}

static bool dir_readfiles(struct hash* d, const char* path,
			  filetype allowed_file_type,
			  proc_path path_processor,
			  int tmpfile_fd)
{
	DIR *dp = opendir(path);
	if (!dp)
		return false;
 
	struct dirent *dirp;
	while ((dirp = readdir(dp)) != NULL) {
		if (str_begins(dirp->d_name, "."))
			continue;

		char* fullpath = 0;
		if (asprintf(&fullpath, "%s%s", path, dirp->d_name) != -1) {
			if (is_filetype(fullpath, allowed_file_type))
				path_processor(fullpath, d, tmpfile_fd);

			free(fullpath);
		}
	}

	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 hash _calinks, *calinks = &_calinks;

	const char* bundle = "bundleXXXXXX";
	char* tmpfile = 0;
	if (asprintf(&tmpfile, "%s%s", ETCCERTSDIR, bundle) == -1)
		return 1;

	int fd = mkstemp(tmpfile);
	if (fd == -1) {
		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 */
	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);
	hash_foreach(calinks, update_ca_symlink);

	/* Update hashes and the bundle */
	if (fd != -1) {
		close(fd);
		char* newcertname = 0;
		if (asprintf(&newcertname, "%s%s", ETCCERTSDIR, CERTBUNDLE) != -1) {
			rename(tmpfile, newcertname);
			free(newcertname);
		}
	}

	free(tmpfile);

	/* Execute run-parts */
	static const char *run_parts_args[] = { "run-parts", RUNPARTSDIR, 0 };
	execve("/usr/bin/run-parts", run_parts_args, NULL);
	execve("/bin/run-parts", run_parts_args, NULL);
	perror("run-parts");

	return 1;
}
-- 
2.4.11



---
Unsubscribe:  alpine-aports+unsubscribe@lists.alpinelinux.org
Help:         alpine-aports+help@lists.alpinelinux.org
---
Reply to thread Export thread (mbox)