Commit c17d7322 authored by Ariadne Conill's avatar Ariadne Conill 🐰
Browse files

Add additional data from Alpineized ca-certificates package.

parent db98f6b8
/* c_rehash.c - Create hash symlinks for certificates
* C implementation based on the original Perl and shell versions
*
* Copyright (c) 2013-2014 Timo Teräs <timo.teras@iki.fi>
* All rights reserved.
*
* This software is licensed under the MIT License.
* Full license available at: http://opensource.org/licenses/MIT
*/
#include <stdio.h>
#include <limits.h>
#include <string.h>
#include <unistd.h>
#include <dirent.h>
#include <sys/stat.h>
#include <openssl/evp.h>
#include <openssl/pem.h>
#include <openssl/x509.h>
#define MAX_COLLISIONS 256
#define countof(x) (sizeof(x) / sizeof(x[0]))
#if 0
#define DEBUG(args...) fprintf(stderr, args)
#else
#define DEBUG(args...)
#endif
struct entry_info {
struct entry_info *next;
char *filename;
unsigned short old_id;
unsigned char need_symlink;
unsigned char digest[EVP_MAX_MD_SIZE];
};
struct bucket_info {
struct bucket_info *next;
struct entry_info *first_entry, *last_entry;
unsigned int hash;
unsigned short type;
unsigned short num_needed;
};
enum Type {
TYPE_CERT = 0,
TYPE_CRL
};
static const char *symlink_extensions[] = { "", "r" };
static const char *file_extensions[] = { "pem", "crt", "cer", "crl" };
static int evpmdsize;
static const EVP_MD *evpmd;
static int do_hash_new = 1;
static int do_hash_old = 0;
static int do_remove_links = 1;
static int do_verbose = 0;
static struct bucket_info *hash_table[257];
static void bit_set(unsigned char *set, unsigned bit)
{
set[bit / 8] |= 1 << (bit % 8);
}
static int bit_isset(unsigned char *set, unsigned bit)
{
return set[bit / 8] & (1 << (bit % 8));
}
static void add_entry(
int type, unsigned int hash,
const char *filename, const unsigned char *digest,
int need_symlink, unsigned short old_id)
{
struct bucket_info *bi;
struct entry_info *ei, *found = NULL;
unsigned int ndx = (type + hash) % countof(hash_table);
for (bi = hash_table[ndx]; bi; bi = bi->next)
if (bi->type == type && bi->hash == hash)
break;
if (!bi) {
bi = calloc(1, sizeof(*bi));
if (!bi) return;
bi->next = hash_table[ndx];
bi->type = type;
bi->hash = hash;
hash_table[ndx] = bi;
}
for (ei = bi->first_entry; ei; ei = ei->next) {
if (digest && memcmp(digest, ei->digest, evpmdsize) == 0) {
fprintf(stderr,
"WARNING: Skipping duplicate certificate in file %s\n",
filename);
return;
}
if (!strcmp(filename, ei->filename)) {
found = ei;
if (!digest) break;
}
}
ei = found;
if (!ei) {
if (bi->num_needed >= MAX_COLLISIONS) return;
ei = calloc(1, sizeof(*ei));
if (!ei) return;
ei->old_id = ~0;
ei->filename = strdup(filename);
if (bi->last_entry) bi->last_entry->next = ei;
if (!bi->first_entry) bi->first_entry = ei;
bi->last_entry = ei;
}
if (old_id < ei->old_id) ei->old_id = old_id;
if (need_symlink && !ei->need_symlink) {
ei->need_symlink = 1;
bi->num_needed++;
memcpy(ei->digest, digest, evpmdsize);
}
}
static int handle_symlink(const char *filename, const char *fullpath)
{
static char xdigit[] = {
0, 1, 2, 3, 4, 5, 6, 7, 8, 9,-1,-1,-1,-1,-1,-1,
-1,10,11,12,13,14,15,-1,-1,-1,-1,-1,-1,-1,-1,-1,
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
-1,10,11,12,13,14,15
};
char linktarget[NAME_MAX], *endptr;
unsigned int hash = 0;
unsigned char ch;
int i, type, id;
ssize_t n;
for (i = 0; i < 8; i++) {
ch = filename[i] - '0';
if (ch >= countof(xdigit) || xdigit[ch] < 0)
return -1;
hash <<= 4;
hash += xdigit[ch];
}
if (filename[i++] != '.') return -1;
for (type = countof(symlink_extensions) - 1; type > 0; type--)
if (strcasecmp(symlink_extensions[type], &filename[i]) == 0)
break;
i += strlen(symlink_extensions[type]);
id = strtoul(&filename[i], &endptr, 10);
if (*endptr != 0) return -1;
n = readlink(fullpath, linktarget, sizeof(linktarget));
if (n >= sizeof(linktarget) || n < 0) return -1;
linktarget[n] = 0;
DEBUG("Found existing symlink %s for %08x (%d), certname %s\n",
filename, hash, type, linktarget);
add_entry(type, hash, linktarget, NULL, 0, id);
return 0;
}
static int handle_certificate(const char *filename, const char *fullpath)
{
STACK_OF(X509_INFO) *inf;
X509_INFO *x;
BIO *b;
const char *ext;
unsigned char digest[EVP_MAX_MD_SIZE];
X509_NAME *name = NULL;
int i, type, ret = -1;
ext = strrchr(filename, '.');
if (ext == NULL) return 0;
for (i = 0; i < countof(file_extensions); i++) {
if (strcasecmp(file_extensions[i], ext+1) == 0)
break;
}
if (i >= countof(file_extensions)) return -1;
b = BIO_new_file(fullpath, "r");
if (!b) return -1;
inf = PEM_X509_INFO_read_bio(b, NULL, NULL, NULL);
BIO_free(b);
if (!inf) return -1;
if (sk_X509_INFO_num(inf) == 1) {
x = sk_X509_INFO_value(inf, 0);
if (x->x509) {
type = TYPE_CERT;
name = X509_get_subject_name(x->x509);
X509_digest(x->x509, evpmd, digest, NULL);
} else if (x->crl) {
type = TYPE_CRL;
name = X509_CRL_get_issuer(x->crl);
X509_CRL_digest(x->crl, evpmd, digest, NULL);
}
if (name && do_hash_new)
add_entry(type, X509_NAME_hash(name), filename, digest, 1, ~0);
if (name && do_hash_old)
add_entry(type, X509_NAME_hash_old(name), filename, digest, 1, ~0);
} else {
fprintf(stderr,
"WARNING: %s does not contain exactly one certificate or CRL: skipping\n",
filename);
}
sk_X509_INFO_pop_free(inf, X509_INFO_free);
return ret;
}
static int hash_dir(const char *dirname)
{
struct bucket_info *bi, *nextbi;
struct entry_info *ei, *nextei;
struct dirent *de;
struct stat st;
unsigned char idmask[MAX_COLLISIONS / 8];
int i, n, nextid, buflen, ret = -1;
const char *pathsep;
char *buf;
DIR *d;
if (access(dirname, R_OK|W_OK|X_OK) != 0) {
fprintf(stderr,
"ERROR: Access denied '%s'\n",
dirname);
return -1;
}
buflen = strlen(dirname);
pathsep = (buflen && dirname[buflen-1] == '/') ? "" : "/";
buflen += NAME_MAX + 2;
buf = malloc(buflen);
if (buf == NULL)
goto err;
if (do_verbose) printf("Doing %s\n", dirname);
d = opendir(dirname);
if (!d) goto err;
while ((de = readdir(d)) != NULL) {
if (snprintf(buf, buflen, "%s%s%s", dirname, pathsep, de->d_name) >= buflen)
continue;
if (lstat(buf, &st) < 0)
continue;
if (S_ISLNK(st.st_mode) && handle_symlink(de->d_name, buf) == 0)
continue;
handle_certificate(de->d_name, buf);
}
closedir(d);
for (i = 0; i < countof(hash_table); i++) {
for (bi = hash_table[i]; bi; bi = nextbi) {
nextbi = bi->next;
DEBUG("Type %d, hash %08x, num entries %d:\n", bi->type, bi->hash, bi->num_needed);
nextid = 0;
memset(idmask, 0, (bi->num_needed+7)/8);
for (ei = bi->first_entry; ei; ei = ei->next)
if (ei->old_id < bi->num_needed)
bit_set(idmask, ei->old_id);
for (ei = bi->first_entry; ei; ei = nextei) {
nextei = ei->next;
DEBUG("\t(old_id %d, need_symlink %d) Cert %s\n",
ei->old_id, ei->need_symlink,
ei->filename);
if (ei->old_id < bi->num_needed) {
/* Link exists, and is used as-is */
snprintf(buf, buflen, "%08x.%s%d", bi->hash, symlink_extensions[bi->type], ei->old_id);
if (do_verbose) printf("link %s -> %s\n", ei->filename, buf);
} else if (ei->need_symlink) {
/* New link needed (it may replace something) */
while (bit_isset(idmask, nextid))
nextid++;
snprintf(buf, buflen, "%s%s%n%08x.%s%d",
dirname, pathsep, &n, bi->hash,
symlink_extensions[bi->type],
nextid);
if (do_verbose) printf("link %s -> %s\n", ei->filename, &buf[n]);
unlink(buf);
symlink(ei->filename, buf);
} else if (do_remove_links) {
/* Link to be deleted */
snprintf(buf, buflen, "%s%s%n%08x.%s%d",
dirname, pathsep, &n, bi->hash,
symlink_extensions[bi->type],
ei->old_id);
if (do_verbose) printf("unlink %s\n", &buf[n]);
unlink(buf);
}
free(ei->filename);
free(ei);
}
free(bi);
}
hash_table[i] = NULL;
}
ret = 0;
err:
free(buf);
return ret;
}
static void c_rehash_usage(void)
{
printf("\
usage: c_rehash <args> <dirs>\n\
\n\
-compat - create new- and old-style hashed links\n\
-old - use old-style hashing for generating links\n\
-h - display this help\n\
-n - do not remove existing links\n\
-v - be more verbose\n\
\n");
}
int main(int argc, char **argv)
{
const char *env, *opt;
int i, numargs, r = 0;
evpmd = EVP_sha1();
evpmdsize = EVP_MD_size(evpmd);
numargs = argc;
for (i = 1; i < argc; i++) {
if (argv[i][0] != '-') continue;
if (strcmp(argv[i], "--") == 0) { argv[i] = 0; numargs--; break; }
opt = &argv[i][1];
if (strcmp(opt, "compat") == 0) {
do_hash_new = do_hash_old = 1;
} else if (strcmp(opt, "old") == 0) {
do_hash_new = 0;
do_hash_old = 1;
} else if (strcmp(opt, "n") == 0) {
do_remove_links = 0;
} else if (strcmp(opt, "v") == 0) {
do_verbose++;
} else {
if (strcmp(opt, "h") != 0)
fprintf(stderr, "unknown option %s\n", argv[i]);
c_rehash_usage();
return 1;
}
argv[i] = 0;
numargs--;
}
if (numargs > 1) {
for (i = 1; i < argc; i++)
if (argv[i]) r |= hash_dir(argv[i]);
} else if ((env = getenv("SSL_CERT_DIR")) != NULL) {
char *e, *m;
m = strdup(env);
for (e = strtok(m, ":"); e != NULL; e = strtok(NULL, ":"))
r |= hash_dir(e);
free(m);
} else {
r |= hash_dir("/etc/ssl/certs");
}
return r ? 2 : 0;
}
#!/usr/bin/python
# vim:set et sw=4:
#
# certdata2pem.py - splits certdata.txt into multiple files
#
# Copyright (C) 2009 Philipp Kern <pkern@debian.org>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301,
# USA.
import base64
import os.path
import re
import sys
import textwrap
import io
objects = []
# Dirty file parser.
in_data, in_multiline, in_obj = False, False, False
field, type, value, obj = None, None, None, dict()
# Python 3 will not let us decode non-ascii characters if we
# have not specified an encoding, but Python 2's open does not
# have an option to set the encoding. Python 3's open is io.open
# and io.open has been backported to Python 2.6 and 2.7, so use io.open.
for line in io.open('certdata.txt', 'rt', encoding='utf8'):
# Ignore the file header.
if not in_data:
if line.startswith('BEGINDATA'):
in_data = True
continue
# Ignore comment lines.
if line.startswith('#'):
continue
# Empty lines are significant if we are inside an object.
if in_obj and len(line.strip()) == 0:
objects.append(obj)
obj = dict()
in_obj = False
continue
if len(line.strip()) == 0:
continue
if in_multiline:
if not line.startswith('END'):
if type == 'MULTILINE_OCTAL':
line = line.strip()
for i in re.finditer(r'\\([0-3][0-7][0-7])', line):
value.append(int(i.group(1), 8))
else:
value += line
continue
obj[field] = value
in_multiline = False
continue
if line.startswith('CKA_CLASS'):
in_obj = True
line_parts = line.strip().split(' ', 2)
if len(line_parts) > 2:
field, type = line_parts[0:2]
value = ' '.join(line_parts[2:])
elif len(line_parts) == 2:
field, type = line_parts
value = None
else:
raise NotImplementedError('line_parts < 2 not supported.')
if type == 'MULTILINE_OCTAL':
in_multiline = True
value = bytearray()
continue
obj[field] = value
if len(obj) > 0:
objects.append(obj)
# Read blacklist.
blacklist = []
if os.path.exists('blacklist.txt'):
for line in open('blacklist.txt', 'r'):
line = line.strip()
if line.startswith('#') or len(line) == 0:
continue
item = line.split('#', 1)[0].strip()
blacklist.append(item)
# Build up trust database.
trust = dict()
for obj in objects:
if obj['CKA_CLASS'] != 'CKO_NSS_TRUST':
continue
if obj['CKA_LABEL'] in blacklist:
print("Certificate %s blacklisted, ignoring." % obj['CKA_LABEL'])
elif obj['CKA_TRUST_SERVER_AUTH'] == 'CKT_NSS_TRUSTED_DELEGATOR':
trust[obj['CKA_LABEL']] = True
elif obj['CKA_TRUST_EMAIL_PROTECTION'] == 'CKT_NSS_TRUSTED_DELEGATOR':
trust[obj['CKA_LABEL']] = True
elif obj['CKA_TRUST_SERVER_AUTH'] == 'CKT_NSS_NOT_TRUSTED':
print('!'*74)
print("UNTRUSTED BUT NOT BLACKLISTED CERTIFICATE FOUND: %s" % obj['CKA_LABEL'])
print('!'*74)
else:
print("Ignoring certificate %s. SAUTH=%s, EPROT=%s" % \
(obj['CKA_LABEL'], obj['CKA_TRUST_SERVER_AUTH'],
obj['CKA_TRUST_EMAIL_PROTECTION']))
for obj in objects:
if obj['CKA_CLASS'] == 'CKO_CERTIFICATE':
if not obj['CKA_LABEL'] in trust or not trust[obj['CKA_LABEL']]:
continue
bname = obj['CKA_LABEL'][1:-1].replace('/', '_')\
.replace(' ', '_')\
.replace('(', '=')\
.replace(')', '=')\
.replace(',', '_')
# this is the only way to decode the way NSS stores multi-byte UTF-8
# and we need an escaped string for checking existence of things
# otherwise we're dependant on the user's current locale.
if bytes != str:
# We're in python 3, convert the utf-8 string to a
# sequence of bytes that represents this utf-8 string
# then encode the byte-sequence as an escaped string that
# can be passed to open() and os.path.exists()
bname = bname.encode('utf-8').decode('unicode_escape').encode('latin-1')
else:
# Python 2
# Convert the unicode string back to its original byte form
# (contents of files returned by io.open are returned as
# unicode strings)
# then to an escaped string that can be passed to open()
# and os.path.exists()
bname = bname.encode('utf-8').decode('string_escape')
fname = bname + b'.crt'
if os.path.exists(fname):
print("Found duplicate certificate name %s, renaming." % bname)
fname = bname + b'_2.crt'
f = open(fname, 'w')
f.write("-----BEGIN CERTIFICATE-----\n")
encoded = base64.b64encode(obj['CKA_VALUE']).decode('utf-8')
f.write("\n".join(textwrap.wrap(encoded, 64)))
f.write("\n-----END CERTIFICATE-----\n")
.\" Hey, EMACS: -*- nroff -*-
.\" First parameter, NAME, should be all caps
.\" Second parameter, SECTION, should be 1-8, maybe w/ subsection
.\" other parameters are allowed: see man(7), man(1)
.TH UPDATE-CA-CERTIFICATES 8 "20 April 2003"
.\" Please adjust this date whenever revising the manpage.
.\"
.\" Some roff macros, for reference:
.\" .nh disable hyphenation
.\" .hy enable hyphenation
.\" .ad l left justify
.\" .ad b justify to both left and right margins
.\" .nf disable filling
.\" .fi enable filling
.\" .br insert line break
.\" .sp <n> insert n+1 empty lines
.\" for manpage-specific macros, see man(7)
.SH NAME
update-ca-certificates \- update /etc/ssl/certs and ca-certificates.crt
.SH SYNOPSIS
.B update-ca-certificates
.RI [ options ]
.SH DESCRIPTION
This manual page documents briefly the
.B update-ca-certificates
command.
.PP
\fBupdate-ca-certificates\fP is a program that updates the directory
/etc/ssl/certs to hold SSL certificates and generates ca-certificates.crt,
a concatenated single-file list of certificates.
.PP
It reads the file /etc/ca-certificates.conf. Each line gives a pathname of
a CA certificate under /usr/share/ca-certificates that should be trusted.
Lines that begin with "#" are comment lines and thus ignored.
Lines that begin with "!" are deselected, causing the deactivation of the CA
certificate in question. Certificates must have a .crt extension in order to
be included by update-ca-certificates.
.PP
Furthermore all certificates with a .crt extension found below
/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.
.SH OPTIONS
A summary of options is included below.
.TP
.B \-h, \-\-help
Show summary of options.
.TP
.B \-v, \-\-verbose
Be verbose. Output \fBc_rehash\fP.
.TP
.B \-f, \-\-fresh
Fresh updates. Remove symlinks in /etc/ssl/certs directory.
.SH FILES
.TP
.I /etc/ca-certificates.conf
A configuration file.
.TP
.I /etc/ssl/certs/ca-certificates.crt
A single-file version of CA certificates. This holds
all CA certificates that you activated in /etc/ca-certificates.conf.
.TP
.I /usr/share/ca-certificates
Directory of CA certificates.
.TP
.I /usr/local/share/ca-certificates
Directory of local CA certificates (with .crt extension).
.SH SEE ALSO
.BR c_rehash (1)
.SH AUTHOR
This manual page was written by Fumitoshi UKAI <ukai@debian.or.jp>,
for the Debian project (but may be used by others).
#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 <errno.h>
#include <limits.h>
#include <sys/stat.h>
#include <sys/sendfile.h>