2652 lines
74 KiB
Diff
2652 lines
74 KiB
Diff
From ceed1c19b6002164482eb358570a91a9563ce694 Mon Sep 17 00:00:00 2001
|
|
From: Tim Burke <tim.burke@gmail.com>
|
|
Date: Wed, 2 Oct 2019 13:10:23 -0700
|
|
Subject: [PATCH 7/7] Add support for client certificates and dhparams
|
|
|
|
Resolves: rhbz#1720667
|
|
|
|
Add --crl-file option
|
|
|
|
... to (optionally) create an empty Certificate Revocation List file.
|
|
|
|
Default the mode for the file to 0644, similar to the default for the CA
|
|
certificate. User can override this with a new --crl-mode option.
|
|
|
|
Closes #10.
|
|
|
|
Add function for DH parameter generation
|
|
|
|
Signed-off-by: Stephen Gallagher <sgallagh@redhat.com>
|
|
|
|
Generate DH parameters file
|
|
|
|
Adds CLI options to enable and control dhparam file generation.
|
|
|
|
Signed-off-by: Stephen Gallagher <sgallagh@redhat.com>
|
|
|
|
Add serverAuth extendedKeyUsage for server certificates
|
|
|
|
Related to https://github.com/sgallagher/sscg/issues/13
|
|
|
|
Signed-off-by: Stephen Gallagher <sgallagh@redhat.com>
|
|
|
|
Rename create_service_cert() to create_cert()
|
|
|
|
Signed-off-by: Stephen Gallagher <sgallagh@redhat.com>
|
|
|
|
Use a common macro for default file modes
|
|
|
|
Signed-off-by: Stephen Gallagher <sgallagh@redhat.com>
|
|
|
|
Add I/O utility routines
|
|
|
|
Signed-off-by: Stephen Gallagher <sgallagh@redhat.com>
|
|
|
|
Rework output file handling
|
|
|
|
SSCG will now verify that it can open all of the files before it
|
|
starts processing through them. In addition, it now has better
|
|
validation that it isn't storing two keys into the same file, or
|
|
dhparams/CRL in a file with other content.
|
|
|
|
Signed-off-by: Stephen Gallagher <sgallagh@redhat.com>
|
|
|
|
Check for invalid file combinations
|
|
|
|
Signed-off-by: Stephen Gallagher <sgallagh@redhat.com>
|
|
|
|
Add support for client certificates
|
|
|
|
Fixes: https://github.com/sgallagher/sscg/issues/13
|
|
|
|
Signed-off-by: Stephen Gallagher <sgallagh@redhat.com>
|
|
---
|
|
include/{service.h => cert.h} | 19 +-
|
|
include/dhparams.h | 42 ++
|
|
include/io_utils.h | 93 +++++
|
|
include/sscg.h | 99 ++++-
|
|
meson.build | 51 ++-
|
|
meson_options.txt | 24 ++
|
|
src/cert.c | 191 +++++++++
|
|
src/dhparams.c | 146 +++++++
|
|
src/io_utils.c | 424 +++++++++++++++++++
|
|
src/service.c | 164 --------
|
|
src/sscg.c | 753 +++++++++++++++++++++-------------
|
|
test/dhparams_test.c | 106 +++++
|
|
12 files changed, 1651 insertions(+), 461 deletions(-)
|
|
rename include/{service.h => cert.h} (67%)
|
|
create mode 100644 include/dhparams.h
|
|
create mode 100644 include/io_utils.h
|
|
create mode 100644 meson_options.txt
|
|
create mode 100644 src/cert.c
|
|
create mode 100644 src/dhparams.c
|
|
create mode 100644 src/io_utils.c
|
|
delete mode 100644 src/service.c
|
|
create mode 100644 test/dhparams_test.c
|
|
|
|
diff --git a/include/service.h b/include/cert.h
|
|
similarity index 67%
|
|
rename from include/service.h
|
|
rename to include/cert.h
|
|
index 20083caf905f8e70360539053134c0e9702c304a..11d04e50ebb2e08af781d0aa14b9aec930ff2f50 100644
|
|
--- a/include/service.h
|
|
+++ b/include/cert.h
|
|
@@ -21,15 +21,16 @@
|
|
#include "x509.h"
|
|
#include "key.h"
|
|
|
|
-#ifndef _SERVICE_H
|
|
-#define _SERVICE_H
|
|
+#ifndef _SSCG_CERT_H
|
|
+#define _SSCG_CERT_H
|
|
|
|
int
|
|
-create_service_cert (TALLOC_CTX *mem_ctx,
|
|
- const struct sscg_options *options,
|
|
- struct sscg_x509_cert *ca_cert,
|
|
- struct sscg_evp_pkey *ca_key,
|
|
- struct sscg_x509_cert **_svc_cert,
|
|
- struct sscg_evp_pkey **_svc_key);
|
|
+create_cert (TALLOC_CTX *mem_ctx,
|
|
+ const struct sscg_options *options,
|
|
+ struct sscg_x509_cert *ca_cert,
|
|
+ struct sscg_evp_pkey *ca_key,
|
|
+ enum sscg_cert_type type,
|
|
+ struct sscg_x509_cert **_svc_cert,
|
|
+ struct sscg_evp_pkey **_svc_key);
|
|
|
|
-#endif /* _SERVICE_H */
|
|
+#endif /* _SSCG_CERT_H */
|
|
diff --git a/include/dhparams.h b/include/dhparams.h
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..baa4fb3b297c5747fcce20f6950239779f71f19a
|
|
--- /dev/null
|
|
+++ b/include/dhparams.h
|
|
@@ -0,0 +1,42 @@
|
|
+/*
|
|
+ This file is part of sscg.
|
|
+
|
|
+ sscg 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 3 of the License, or
|
|
+ (at your option) any later version.
|
|
+
|
|
+ sscg 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 sscg. If not, see <http://www.gnu.org/licenses/>.
|
|
+
|
|
+ Copyright 2019 by Stephen Gallagher <sgallagh@redhat.com>
|
|
+*/
|
|
+
|
|
+#ifndef _SSCG_DHPARAMS_H
|
|
+#define _SSCG_DHPARAMS_H
|
|
+
|
|
+#include <talloc.h>
|
|
+
|
|
+#include "include/sscg.h"
|
|
+
|
|
+struct sscg_dhparams
|
|
+{
|
|
+ int prime_len;
|
|
+ int generator;
|
|
+ DH *dh;
|
|
+ BN_GENCB *cb;
|
|
+};
|
|
+
|
|
+int
|
|
+create_dhparams (TALLOC_CTX *mem_ctx,
|
|
+ enum sscg_verbosity options,
|
|
+ int prime_len,
|
|
+ int generator,
|
|
+ struct sscg_dhparams **_dhparams);
|
|
+
|
|
+#endif /* _SSCG_DHPARAMS_H */
|
|
diff --git a/include/io_utils.h b/include/io_utils.h
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..6a89a476b3d982447b6603153c6765835cd67464
|
|
--- /dev/null
|
|
+++ b/include/io_utils.h
|
|
@@ -0,0 +1,93 @@
|
|
+/*
|
|
+ This file is part of sscg.
|
|
+
|
|
+ sscg 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 3 of the License, or
|
|
+ (at your option) any later version.
|
|
+
|
|
+ sscg 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 sscg. If not, see <http://www.gnu.org/licenses/>.
|
|
+
|
|
+ Copyright 2019 by Stephen Gallagher <sgallagh@redhat.com>
|
|
+*/
|
|
+
|
|
+#ifndef _SSCG_IO_UTILS_H
|
|
+#define _SSCG_IO_UTILS_H
|
|
+
|
|
+#include <openssl/ssl.h>
|
|
+#include <stdbool.h>
|
|
+#include <talloc.h>
|
|
+
|
|
+#include "include/sscg.h"
|
|
+
|
|
+
|
|
+struct sscg_stream
|
|
+{
|
|
+ BIO *bio;
|
|
+ char *path;
|
|
+ int mode;
|
|
+ int filetypes;
|
|
+};
|
|
+
|
|
+
|
|
+int
|
|
+sscg_normalize_path (TALLOC_CTX *mem_ctx,
|
|
+ const char *path,
|
|
+ char **_normalized_path);
|
|
+
|
|
+
|
|
+struct sscg_stream *
|
|
+sscg_io_utils_get_stream_by_path (struct sscg_stream **streams,
|
|
+ const char *normalized_path);
|
|
+
|
|
+
|
|
+struct sscg_stream *
|
|
+sscg_io_utils_get_stream_by_type (struct sscg_stream **streams,
|
|
+ enum sscg_file_type filetype);
|
|
+
|
|
+
|
|
+BIO *
|
|
+sscg_io_utils_get_bio_by_type (struct sscg_stream **streams,
|
|
+ enum sscg_file_type filetype);
|
|
+
|
|
+
|
|
+const char *
|
|
+sscg_io_utils_get_path_by_type (struct sscg_stream **streams,
|
|
+ enum sscg_file_type filetype);
|
|
+
|
|
+
|
|
+/**
|
|
+ * sscg_io_utils_add_output_file:
|
|
+ * @streams: The array of streams from the sscg_options
|
|
+ * @filetype:
|
|
+ * @path: The path to the file on disk.
|
|
+ * @mode: The filesystem mode this file should have when written to disk.
|
|
+ * See chmod(1) for the possible values.
|
|
+ * @overwrite: If true, replace any existing file at @normalized_path. If
|
|
+ * false, opening will fail if it already exists and return an error.
|
|
+ *
|
|
+ * Prepares all output filenames to be opened. Files are not created until
|
|
+ * sscg_io_utils_open_output_files() is called.
|
|
+ */
|
|
+int
|
|
+sscg_io_utils_add_output_file (struct sscg_stream **streams,
|
|
+ enum sscg_file_type filetype,
|
|
+ const char *path,
|
|
+ int mode);
|
|
+
|
|
+
|
|
+int
|
|
+sscg_io_utils_open_output_files (struct sscg_stream **streams, bool overwrite);
|
|
+
|
|
+/* If this function fails, some of the output files may be left as 0400 */
|
|
+int
|
|
+sscg_io_utils_finalize_output_files (struct sscg_stream **streams);
|
|
+
|
|
+
|
|
+#endif /* _SSCG_IO_UTILS_H */
|
|
diff --git a/include/sscg.h b/include/sscg.h
|
|
index ce9a7916e9432d0843d82af61d56ea7238ded682..2744404c25c68ed905ca621bb955e0c04b33ca81 100644
|
|
--- a/include/sscg.h
|
|
+++ b/include/sscg.h
|
|
@@ -27,6 +27,8 @@
|
|
#include <talloc.h>
|
|
#include <stdint.h>
|
|
|
|
+#include "include/io_utils.h"
|
|
+
|
|
#ifndef _SSCG_H
|
|
#define _SSCG_H
|
|
|
|
@@ -108,6 +110,13 @@
|
|
} \
|
|
while (0)
|
|
|
|
+
|
|
+#define SSCG_CERT_DEFAULT_MODE 0644
|
|
+#define SSCG_CERT_DEFAULT_MODE_HELP _ ("0644")
|
|
+#define SSCG_KEY_DEFAULT_MODE 0600
|
|
+#define SSCG_KEY_DEFAULT_MODE_HELP _ ("0600")
|
|
+
|
|
+
|
|
enum sscg_verbosity
|
|
{
|
|
SSCG_QUIET = -1,
|
|
@@ -116,6 +125,75 @@ enum sscg_verbosity
|
|
SSCG_DEBUG
|
|
};
|
|
|
|
+extern int verbosity;
|
|
+
|
|
+const char *sscg_get_verbosity_name (enum sscg_verbosity);
|
|
+
|
|
+#define SSCG_LOG(_level, _format, ...) \
|
|
+ do \
|
|
+ { \
|
|
+ if (verbosity >= _level) \
|
|
+ { \
|
|
+ printf ("%s", sscg_get_verbosity_name (_level)); \
|
|
+ printf (_format, ##__VA_ARGS__); \
|
|
+ } \
|
|
+ } \
|
|
+ while (0)
|
|
+
|
|
+#define SSCG_ERROR(_format, ...) \
|
|
+ do \
|
|
+ { \
|
|
+ if (verbosity > SSCG_QUIET) \
|
|
+ { \
|
|
+ fprintf (stderr, "ERROR: "); \
|
|
+ fprintf (stderr, _format, ##__VA_ARGS__); \
|
|
+ } \
|
|
+ } \
|
|
+ while (0)
|
|
+
|
|
+
|
|
+enum sscg_file_type
|
|
+{
|
|
+ SSCG_FILE_TYPE_UNKNOWN = -1,
|
|
+ SSCG_FILE_TYPE_CA,
|
|
+ SSCG_FILE_TYPE_CA_KEY,
|
|
+ SSCG_FILE_TYPE_SVC,
|
|
+ SSCG_FILE_TYPE_SVC_KEY,
|
|
+ SSCG_FILE_TYPE_CLIENT,
|
|
+ SSCG_FILE_TYPE_CLIENT_KEY,
|
|
+ SSCG_FILE_TYPE_CRL,
|
|
+ SSCG_FILE_TYPE_DHPARAMS,
|
|
+
|
|
+ SSCG_NUM_FILE_TYPES
|
|
+};
|
|
+
|
|
+#define SSCG_FILE_TYPE_KEYS \
|
|
+ ((1 << SSCG_FILE_TYPE_CA_KEY) | (1 << SSCG_FILE_TYPE_SVC_KEY) | \
|
|
+ (1 << SSCG_FILE_TYPE_CLIENT_KEY))
|
|
+
|
|
+#define SSCG_FILE_TYPE_SVC_TYPES \
|
|
+ ((1 << SSCG_FILE_TYPE_SVC) | (1 << SSCG_FILE_TYPE_SVC_KEY))
|
|
+
|
|
+#define SSCG_FILE_TYPE_CLIENT_TYPES \
|
|
+ ((1 << SSCG_FILE_TYPE_CLIENT) | (1 << SSCG_FILE_TYPE_CLIENT_KEY))
|
|
+
|
|
+#define SSCG_FILE_TYPE_CA_TYPES \
|
|
+ ((1 << SSCG_FILE_TYPE_CA) | (1 << SSCG_FILE_TYPE_CA_KEY))
|
|
+
|
|
+const char *
|
|
+sscg_get_file_type_name (enum sscg_file_type _type);
|
|
+
|
|
+#define GET_BIO(_type) sscg_io_utils_get_bio_by_type (options->streams, _type)
|
|
+
|
|
+#define GET_PATH(_type) \
|
|
+ sscg_io_utils_get_path_by_type (options->streams, _type)
|
|
+
|
|
+#define ANNOUNCE_WRITE(_type) \
|
|
+ SSCG_LOG (SSCG_DEFAULT, \
|
|
+ "Wrote %s to %s\n", \
|
|
+ sscg_get_file_type_name (_type), \
|
|
+ GET_PATH (_type));
|
|
+
|
|
struct sscg_options
|
|
{
|
|
/* How noisy to be when printing information */
|
|
@@ -149,15 +227,28 @@ struct sscg_options
|
|
char *ca_key_pass;
|
|
bool cert_key_pass_prompt;
|
|
char *cert_key_pass;
|
|
+ bool client_key_pass_prompt;
|
|
+ char *client_key_pass;
|
|
|
|
/* Output Files */
|
|
- char *ca_file;
|
|
- char *ca_key_file;
|
|
- char *cert_file;
|
|
- char *cert_key_file;
|
|
+ struct sscg_stream **streams;
|
|
+
|
|
+ /* Diffie-Hellman Parameters */
|
|
+ int dhparams_prime_len;
|
|
+ int dhparams_generator;
|
|
|
|
/* Overwrite the output files */
|
|
bool overwrite;
|
|
};
|
|
|
|
+
|
|
+enum sscg_cert_type
|
|
+{
|
|
+ SSCG_CERT_TYPE_UNKNOWN = -1,
|
|
+ SSCG_CERT_TYPE_SERVER,
|
|
+ SSCG_CERT_TYPE_CLIENT,
|
|
+
|
|
+ SSCG_NUM_CERT_TYPES
|
|
+};
|
|
+
|
|
#endif /* _SSCG_H */
|
|
diff --git a/meson.build b/meson.build
|
|
index c7b08ed3d6dff686f08a90ca869ba5881a9e8aaa..eb339ea8c768adab6d576736fbe476b83529e78d 100644
|
|
--- a/meson.build
|
|
+++ b/meson.build
|
|
@@ -52,21 +52,30 @@ endif
|
|
|
|
has_get_sec_level = cc.has_function(
|
|
'SSL_CTX_get_security_level',
|
|
- dependencies: [ ssl])
|
|
+ dependencies: [ ssl ])
|
|
+
|
|
+has_generator_3 = cc.has_header_symbol(
|
|
+ 'openssl/dh.h',
|
|
+ 'DH_GENERATOR_3',
|
|
+ dependencies: [ ssl ])
|
|
|
|
sscg_lib_srcs = [
|
|
'src/authority.c',
|
|
'src/bignum.c',
|
|
+ 'src/cert.c',
|
|
+ 'src/dhparams.c',
|
|
+ 'src/io_utils.c',
|
|
'src/key.c',
|
|
- 'src/service.c',
|
|
'src/x509.c',
|
|
]
|
|
|
|
sscg_lib_hdrs = [
|
|
'include/authority.h',
|
|
'include/bignum.h',
|
|
+ 'include/cert.h',
|
|
+ 'include/dhparams.h',
|
|
+ 'include/io_utils.h',
|
|
'include/key.h',
|
|
- 'include/service.h',
|
|
'include/x509.h',
|
|
]
|
|
|
|
@@ -140,6 +149,42 @@ init_bignum_test = executable(
|
|
)
|
|
test('init_bignum_test', init_bignum_test)
|
|
|
|
+dhparams_test = executable(
|
|
+ 'dhparams_test',
|
|
+ 'test/dhparams_test.c',
|
|
+ link_with : sscg_lib,
|
|
+ dependencies: [],
|
|
+ install : false
|
|
+)
|
|
+
|
|
+# Test generating 512-bit, 1024-bit and 2048-bit DH params with multiple
|
|
+# generators. 4096-bit and larger takes over ten minutes, so it's excluded from
|
|
+# the test suite by default.
|
|
+prime_lengths = [ 512, 1024 ]
|
|
+dhparam_timeout = 120
|
|
+
|
|
+if get_option('run_slow_tests')
|
|
+ prime_lengths = prime_lengths + [ 2048, 4096 ]
|
|
+ dhparam_timeout = 900
|
|
+endif
|
|
+
|
|
+generators = [ 2, 5 ]
|
|
+
|
|
+if (has_generator_3)
|
|
+ generators += [ 3 ]
|
|
+endif
|
|
+
|
|
+foreach prime_len : prime_lengths
|
|
+ foreach g : generators
|
|
+ test('dhparams_test_' + prime_len.to_string() + '_' + g.to_string(),
|
|
+ dhparams_test,
|
|
+ args: [ prime_len.to_string(), g.to_string() ],
|
|
+ timeout: dhparam_timeout)
|
|
+ endforeach
|
|
+endforeach
|
|
+
|
|
+
|
|
+
|
|
cdata = configuration_data()
|
|
cdata.set_quoted('PACKAGE_VERSION', meson.project_version())
|
|
cdata.set('HAVE_SSL_CTX_GET_SECURITY_LEVEL', has_get_sec_level)
|
|
diff --git a/meson_options.txt b/meson_options.txt
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..1d8ff959b3c3d3f2aa6dc928ba38efeedfbe5c96
|
|
--- /dev/null
|
|
+++ b/meson_options.txt
|
|
@@ -0,0 +1,24 @@
|
|
+# This file is part of sscg.
|
|
+#
|
|
+# sscg 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 3 of the License, or
|
|
+# (at your option) any later version.
|
|
+#
|
|
+# sscg 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 sscg. If not, see <http://www.gnu.org/licenses/>.
|
|
+#
|
|
+# Copyright 2019 by Stephen Gallagher <sgallagh@redhat.com>
|
|
+
|
|
+# Generating 4096-bit Diffie-Hellman parameters can take over ten minutes on a
|
|
+# fast system. We skip testing it by default.
|
|
+
|
|
+# Some tests take a long time (dozens of seconds or even minutes)
|
|
+# For general development, we will skip them and run them only in the CI
|
|
+# environment.
|
|
+option('run_slow_tests', type : 'boolean', value : false)
|
|
diff --git a/src/cert.c b/src/cert.c
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..0377d1357a3881a9705fcb09fdfe2a9c78cc5ed4
|
|
--- /dev/null
|
|
+++ b/src/cert.c
|
|
@@ -0,0 +1,191 @@
|
|
+/*
|
|
+ This file is part of sscg.
|
|
+
|
|
+ sscg 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 3 of the License, or
|
|
+ (at your option) any later version.
|
|
+
|
|
+ sscg 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 sscg. If not, see <http://www.gnu.org/licenses/>.
|
|
+
|
|
+ Copyright 2017 by Stephen Gallagher <sgallagh@redhat.com>
|
|
+*/
|
|
+
|
|
+
|
|
+#include "include/sscg.h"
|
|
+#include "include/cert.h"
|
|
+#include "include/x509.h"
|
|
+#include "include/key.h"
|
|
+
|
|
+int
|
|
+create_cert (TALLOC_CTX *mem_ctx,
|
|
+ const struct sscg_options *options,
|
|
+ struct sscg_x509_cert *ca_cert,
|
|
+ struct sscg_evp_pkey *ca_key,
|
|
+ enum sscg_cert_type type,
|
|
+ struct sscg_x509_cert **_cert,
|
|
+ struct sscg_evp_pkey **_key)
|
|
+{
|
|
+ int ret;
|
|
+ size_t i;
|
|
+ struct sscg_bignum *e;
|
|
+ struct sscg_bignum *serial;
|
|
+ struct sscg_cert_info *certinfo;
|
|
+ struct sscg_x509_req *csr;
|
|
+ struct sscg_evp_pkey *pkey;
|
|
+ struct sscg_x509_cert *cert;
|
|
+ X509_EXTENSION *ex = NULL;
|
|
+ EXTENDED_KEY_USAGE *extended;
|
|
+ TALLOC_CTX *tmp_ctx = NULL;
|
|
+
|
|
+ tmp_ctx = talloc_new (NULL);
|
|
+ CHECK_MEM (tmp_ctx);
|
|
+
|
|
+ /* create a serial number for this certificate */
|
|
+ ret = sscg_generate_serial (tmp_ctx, &serial);
|
|
+ CHECK_OK (ret);
|
|
+
|
|
+ certinfo = sscg_cert_info_new (tmp_ctx, options->hash_fn);
|
|
+ CHECK_MEM (certinfo);
|
|
+
|
|
+ /* Populate cert_info from options */
|
|
+ certinfo->country = talloc_strdup (certinfo, options->country);
|
|
+ CHECK_MEM (certinfo->country);
|
|
+
|
|
+ certinfo->state = talloc_strdup (certinfo, options->state);
|
|
+ CHECK_MEM (certinfo->state);
|
|
+
|
|
+ certinfo->locality = talloc_strdup (certinfo, options->locality);
|
|
+ CHECK_MEM (certinfo->locality);
|
|
+
|
|
+ certinfo->org = talloc_strdup (certinfo, options->org);
|
|
+ CHECK_MEM (certinfo->org);
|
|
+
|
|
+ certinfo->org_unit = talloc_strdup (certinfo, options->org_unit);
|
|
+ CHECK_MEM (certinfo->org_unit);
|
|
+
|
|
+ certinfo->email = talloc_strdup (certinfo, options->email);
|
|
+ CHECK_MEM (certinfo->email);
|
|
+
|
|
+ certinfo->cn = talloc_strdup (certinfo, options->hostname);
|
|
+ CHECK_MEM (certinfo->cn);
|
|
+
|
|
+ if (options->subject_alt_names)
|
|
+ {
|
|
+ for (i = 0; options->subject_alt_names[i]; i++)
|
|
+ {
|
|
+ certinfo->subject_alt_names = talloc_realloc (
|
|
+ certinfo, certinfo->subject_alt_names, char *, i + 2);
|
|
+ CHECK_MEM (certinfo->subject_alt_names);
|
|
+
|
|
+ certinfo->subject_alt_names[i] = talloc_strdup (
|
|
+ certinfo->subject_alt_names, options->subject_alt_names[i]);
|
|
+ CHECK_MEM (certinfo->subject_alt_names[i]);
|
|
+
|
|
+ /* Add a NULL terminator to the end */
|
|
+ certinfo->subject_alt_names[i + 1] = NULL;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ /* Ensure that this certificate may not sign other certificates */
|
|
+ /* Add key extensions */
|
|
+ ex = X509V3_EXT_conf_nid (
|
|
+ NULL, NULL, NID_key_usage, "critical,digitalSignature,keyEncipherment");
|
|
+ CHECK_MEM (ex);
|
|
+ sk_X509_EXTENSION_push (certinfo->extensions, ex);
|
|
+
|
|
+ extended = sk_ASN1_OBJECT_new_null ();
|
|
+
|
|
+ switch (type)
|
|
+ {
|
|
+ case SSCG_CERT_TYPE_SERVER:
|
|
+ sk_ASN1_OBJECT_push (extended, OBJ_nid2obj (NID_server_auth));
|
|
+ break;
|
|
+
|
|
+ case SSCG_CERT_TYPE_CLIENT:
|
|
+ sk_ASN1_OBJECT_push (extended, OBJ_nid2obj (NID_client_auth));
|
|
+ break;
|
|
+
|
|
+ default:
|
|
+ fprintf (stdout, "Unknown certificate type!");
|
|
+ ret = EINVAL;
|
|
+ goto done;
|
|
+ }
|
|
+
|
|
+
|
|
+ ex = X509V3_EXT_i2d (NID_ext_key_usage, 0, extended);
|
|
+ sk_ASN1_OBJECT_pop_free (extended, ASN1_OBJECT_free);
|
|
+ sk_X509_EXTENSION_push (certinfo->extensions, ex);
|
|
+
|
|
+ /* Mark it as not a CA */
|
|
+ ex = X509V3_EXT_conf_nid (NULL, NULL, NID_basic_constraints, "CA:FALSE");
|
|
+ CHECK_MEM (ex);
|
|
+ sk_X509_EXTENSION_push (certinfo->extensions, ex);
|
|
+
|
|
+ /* Use an exponent value of RSA F4 aka 0x10001 (65537) */
|
|
+ ret = sscg_init_bignum (tmp_ctx, RSA_F4, &e);
|
|
+ CHECK_OK (ret);
|
|
+
|
|
+ /* Generate an RSA keypair for this CA */
|
|
+ if (options->verbosity >= SSCG_VERBOSE)
|
|
+ {
|
|
+ fprintf (stdout, "Generating RSA key for certificate.\n");
|
|
+ }
|
|
+ /* TODO: support DSA keys as well */
|
|
+ ret = sscg_generate_rsa_key (tmp_ctx, options->key_strength, e, &pkey);
|
|
+ CHECK_OK (ret);
|
|
+
|
|
+ /* Create a certificate signing request for the private CA */
|
|
+ if (options->verbosity >= SSCG_VERBOSE)
|
|
+ {
|
|
+ fprintf (stdout, "Generating CSR for certificate.\n");
|
|
+ }
|
|
+ ret = sscg_x509v3_csr_new (tmp_ctx, certinfo, pkey, &csr);
|
|
+ CHECK_OK (ret);
|
|
+
|
|
+ /* Finalize the CSR */
|
|
+ ret = sscg_x509v3_csr_finalize (certinfo, pkey, csr);
|
|
+ CHECK_OK (ret);
|
|
+
|
|
+ if (options->verbosity >= SSCG_DEBUG)
|
|
+ {
|
|
+ const char *tempcert =
|
|
+ SSCG_CERT_TYPE_SERVER ? "./debug-service.csr" : "debug-client.csr";
|
|
+
|
|
+ fprintf (stderr, "DEBUG: Writing certificate CSR to %s\n", tempcert);
|
|
+ BIO *csr_out = BIO_new_file (tempcert, "w");
|
|
+ int sslret = PEM_write_bio_X509_REQ (csr_out, csr->x509_req);
|
|
+ CHECK_SSL (sslret, PEM_write_bio_X509_REQ);
|
|
+ }
|
|
+
|
|
+ /* Sign the certificate */
|
|
+
|
|
+ if (options->verbosity >= SSCG_VERBOSE)
|
|
+ {
|
|
+ fprintf (stdout, "Signing CSR for certificate. \n");
|
|
+ }
|
|
+
|
|
+ ret = sscg_sign_x509_csr (tmp_ctx,
|
|
+ csr,
|
|
+ serial,
|
|
+ options->lifetime,
|
|
+ ca_cert,
|
|
+ ca_key,
|
|
+ options->hash_fn,
|
|
+ &cert);
|
|
+ CHECK_OK (ret);
|
|
+
|
|
+ *_cert = talloc_steal (mem_ctx, cert);
|
|
+ *_key = talloc_steal (mem_ctx, pkey);
|
|
+
|
|
+ ret = EOK;
|
|
+done:
|
|
+ talloc_free (tmp_ctx);
|
|
+ return ret;
|
|
+}
|
|
diff --git a/src/dhparams.c b/src/dhparams.c
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..f9b64629709beb857a948a7f2f42eb805d76c557
|
|
--- /dev/null
|
|
+++ b/src/dhparams.c
|
|
@@ -0,0 +1,146 @@
|
|
+/*
|
|
+ This file is part of sscg.
|
|
+
|
|
+ sscg 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 3 of the License, or
|
|
+ (at your option) any later version.
|
|
+
|
|
+ sscg 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 sscg. If not, see <http://www.gnu.org/licenses/>.
|
|
+
|
|
+ Copyright 2019 by Stephen Gallagher <sgallagh@redhat.com>
|
|
+*/
|
|
+
|
|
+#include <assert.h>
|
|
+
|
|
+#include "include/sscg.h"
|
|
+#include "include/dhparams.h"
|
|
+
|
|
+
|
|
+static int
|
|
+_sscg_dhparams_destructor (TALLOC_CTX *ctx);
|
|
+
|
|
+static int
|
|
+dh_cb (int p, int n, BN_GENCB *cb);
|
|
+
|
|
+int
|
|
+create_dhparams (TALLOC_CTX *mem_ctx,
|
|
+ enum sscg_verbosity verbosity,
|
|
+ int prime_len,
|
|
+ int generator,
|
|
+ struct sscg_dhparams **_dhparams)
|
|
+{
|
|
+ int ret;
|
|
+ struct sscg_dhparams *dhparams = NULL;
|
|
+ TALLOC_CTX *tmp_ctx = NULL;
|
|
+
|
|
+ /* First validate the input */
|
|
+ assert (_dhparams && !*_dhparams);
|
|
+
|
|
+ if (prime_len <= 0)
|
|
+ {
|
|
+ fprintf (stderr, "Prime length must be a positive integer");
|
|
+ ret = ERANGE;
|
|
+ goto done;
|
|
+ }
|
|
+
|
|
+ if (generator <= 0)
|
|
+ {
|
|
+ fprintf (stderr, "Generator must be a positive integer");
|
|
+ ret = ERANGE;
|
|
+ goto done;
|
|
+ }
|
|
+
|
|
+ tmp_ctx = talloc_new (NULL);
|
|
+ CHECK_MEM (tmp_ctx);
|
|
+
|
|
+ dhparams = talloc_zero (tmp_ctx, struct sscg_dhparams);
|
|
+ CHECK_MEM (dhparams);
|
|
+
|
|
+ dhparams->prime_len = prime_len;
|
|
+ dhparams->generator = generator;
|
|
+ talloc_set_destructor ((TALLOC_CTX *)dhparams, _sscg_dhparams_destructor);
|
|
+
|
|
+ if (verbosity >= SSCG_DEFAULT)
|
|
+ {
|
|
+ fprintf (stdout,
|
|
+ "Generating DH parameters of length %d and generator %d. "
|
|
+ "This will take a long time.\n",
|
|
+ dhparams->prime_len,
|
|
+ dhparams->generator);
|
|
+ }
|
|
+
|
|
+ dhparams->dh = DH_new ();
|
|
+
|
|
+ if (verbosity >= SSCG_VERBOSE)
|
|
+ {
|
|
+#if OPENSSL_VERSION_NUMBER < 0x10100000L
|
|
+ dhparams->cb = talloc_zero (dhparams, BN_GENCB);
|
|
+#else
|
|
+ dhparams->cb = BN_GENCB_new ();
|
|
+#endif
|
|
+ if (dhparams->cb == NULL)
|
|
+ {
|
|
+ ERR_print_errors_fp (stderr);
|
|
+ ret = ENOMEM;
|
|
+ goto done;
|
|
+ }
|
|
+
|
|
+ BN_GENCB_set (dhparams->cb, dh_cb, NULL);
|
|
+ }
|
|
+
|
|
+ if (!DH_generate_parameters_ex (
|
|
+ dhparams->dh, dhparams->prime_len, dhparams->generator, dhparams->cb))
|
|
+ {
|
|
+ ERR_print_errors_fp (stderr);
|
|
+ ret = EIO;
|
|
+ goto done;
|
|
+ }
|
|
+
|
|
+ ret = EOK;
|
|
+ *_dhparams = talloc_steal (mem_ctx, dhparams);
|
|
+
|
|
+done:
|
|
+ talloc_free (tmp_ctx);
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static int
|
|
+_sscg_dhparams_destructor (TALLOC_CTX *ctx)
|
|
+{
|
|
+ struct sscg_dhparams *params =
|
|
+ talloc_get_type_abort (ctx, struct sscg_dhparams);
|
|
+
|
|
+ if (params->dh != NULL)
|
|
+ {
|
|
+ DH_free (params->dh);
|
|
+ params->dh = NULL;
|
|
+ }
|
|
+
|
|
+#if OPENSSL_VERSION_NUMBER >= 0x10100000L
|
|
+ if (params->cb != NULL)
|
|
+ {
|
|
+ BN_GENCB_free (params->cb);
|
|
+ params->cb = NULL;
|
|
+ }
|
|
+#endif
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int
|
|
+dh_cb (int p, int n, BN_GENCB *cb)
|
|
+{
|
|
+ static const char symbols[] = ".+*\n";
|
|
+ char c = (p >= 0 && (size_t)p < sizeof (symbols) - 1) ? symbols[p] : '?';
|
|
+
|
|
+ fprintf (stdout, "%c", c);
|
|
+
|
|
+ return 1;
|
|
+}
|
|
diff --git a/src/io_utils.c b/src/io_utils.c
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..809a1da0e455afa0dba0796a5f7ac406742328a1
|
|
--- /dev/null
|
|
+++ b/src/io_utils.c
|
|
@@ -0,0 +1,424 @@
|
|
+/*
|
|
+ This file is part of sscg.
|
|
+
|
|
+ sscg 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 3 of the License, or
|
|
+ (at your option) any later version.
|
|
+
|
|
+ sscg 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 sscg. If not, see <http://www.gnu.org/licenses/>.
|
|
+
|
|
+ Copyright 2019 by Stephen Gallagher <sgallagh@redhat.com>
|
|
+*/
|
|
+
|
|
+
|
|
+#include <assert.h>
|
|
+#include <path_utils.h>
|
|
+#include <string.h>
|
|
+#include <talloc.h>
|
|
+
|
|
+#include "include/io_utils.h"
|
|
+#include "include/sscg.h"
|
|
+
|
|
+int
|
|
+sscg_normalize_path (TALLOC_CTX *mem_ctx,
|
|
+ const char *path,
|
|
+ char **_normalized_path)
|
|
+{
|
|
+ int ret;
|
|
+ char *normalized_path = NULL;
|
|
+
|
|
+ TALLOC_CTX *tmp_ctx = talloc_new (NULL);
|
|
+ CHECK_MEM (tmp_ctx);
|
|
+
|
|
+ normalized_path = talloc_zero_array (tmp_ctx, char, PATH_MAX);
|
|
+ CHECK_MEM (normalized_path);
|
|
+
|
|
+ ret = make_normalized_absolute_path (normalized_path, PATH_MAX, path);
|
|
+ CHECK_OK (ret);
|
|
+
|
|
+ *_normalized_path = talloc_strdup (mem_ctx, normalized_path);
|
|
+ CHECK_MEM (*_normalized_path);
|
|
+
|
|
+ ret = EOK;
|
|
+
|
|
+done:
|
|
+ talloc_free (normalized_path);
|
|
+ talloc_free (tmp_ctx);
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+
|
|
+static int
|
|
+sscg_stream_destructor (TALLOC_CTX *ptr)
|
|
+{
|
|
+ struct sscg_stream *stream = talloc_get_type_abort (ptr, struct sscg_stream);
|
|
+
|
|
+ BIO_free (stream->bio);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+
|
|
+struct sscg_stream *
|
|
+sscg_io_utils_get_stream_by_path (struct sscg_stream **streams,
|
|
+ const char *normalized_path)
|
|
+{
|
|
+ struct sscg_stream *stream = NULL;
|
|
+
|
|
+ /* First see if this path already exists in the list */
|
|
+ for (int i = 0; (stream = streams[i]) && i < SSCG_NUM_FILE_TYPES; i++)
|
|
+ {
|
|
+ if (strcmp (normalized_path, stream->path) == 0)
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ return stream;
|
|
+}
|
|
+
|
|
+
|
|
+struct sscg_stream *
|
|
+sscg_io_utils_get_stream_by_type (struct sscg_stream **streams,
|
|
+ enum sscg_file_type filetype)
|
|
+{
|
|
+ struct sscg_stream *stream = NULL;
|
|
+
|
|
+ /* First see if this path already exists in the list */
|
|
+ for (int i = 0; (stream = streams[i]) && i < SSCG_NUM_FILE_TYPES; i++)
|
|
+ {
|
|
+ SSCG_LOG (SSCG_DEBUG,
|
|
+ "Checking for 0x%.4x in 0x%.4x\n",
|
|
+ (1 << filetype),
|
|
+ stream->filetypes);
|
|
+ if (stream->filetypes & (1 << filetype))
|
|
+ {
|
|
+ SSCG_LOG (SSCG_DEBUG,
|
|
+ "Found file type %s in %s\n",
|
|
+ sscg_get_file_type_name (filetype),
|
|
+ stream->path);
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (!stream)
|
|
+ SSCG_LOG (SSCG_DEBUG,
|
|
+ "Could not locate file type: %s. Skipping.\n",
|
|
+ sscg_get_file_type_name (filetype));
|
|
+
|
|
+ return stream;
|
|
+}
|
|
+
|
|
+
|
|
+BIO *
|
|
+sscg_io_utils_get_bio_by_type (struct sscg_stream **streams,
|
|
+ enum sscg_file_type filetype)
|
|
+{
|
|
+ struct sscg_stream *_tmp_stream =
|
|
+ sscg_io_utils_get_stream_by_type (streams, filetype);
|
|
+
|
|
+ if (_tmp_stream)
|
|
+ {
|
|
+ return _tmp_stream->bio;
|
|
+ }
|
|
+
|
|
+ return NULL;
|
|
+}
|
|
+
|
|
+
|
|
+const char *
|
|
+sscg_io_utils_get_path_by_type (struct sscg_stream **streams,
|
|
+ enum sscg_file_type filetype)
|
|
+{
|
|
+ struct sscg_stream *_tmp_stream =
|
|
+ sscg_io_utils_get_stream_by_type (streams, filetype);
|
|
+
|
|
+ if (_tmp_stream)
|
|
+ {
|
|
+ return _tmp_stream->path;
|
|
+ }
|
|
+
|
|
+ return NULL;
|
|
+}
|
|
+
|
|
+
|
|
+int
|
|
+sscg_io_utils_add_output_file (struct sscg_stream **streams,
|
|
+ enum sscg_file_type filetype,
|
|
+ const char *path,
|
|
+ int mode)
|
|
+{
|
|
+ int ret, i;
|
|
+ TALLOC_CTX *tmp_ctx = NULL;
|
|
+ struct sscg_stream *stream = NULL;
|
|
+ char *normalized_path = NULL;
|
|
+
|
|
+ /* If we haven't been passed a path, just return; it's probably an optional
|
|
+ * output file
|
|
+ */
|
|
+ if (path == NULL)
|
|
+ {
|
|
+ SSCG_LOG (SSCG_DEBUG,
|
|
+ "Got a NULL path with filetype: %s\n",
|
|
+ sscg_get_file_type_name (filetype));
|
|
+ return EOK;
|
|
+ }
|
|
+
|
|
+ tmp_ctx = talloc_new (NULL);
|
|
+ CHECK_MEM (tmp_ctx);
|
|
+
|
|
+ /* Get the normalized version of the path */
|
|
+ ret = sscg_normalize_path (tmp_ctx, path, &normalized_path);
|
|
+ CHECK_OK (ret);
|
|
+
|
|
+ SSCG_LOG (SSCG_DEBUG,
|
|
+ "%s file path: %s\n",
|
|
+ sscg_get_file_type_name (filetype),
|
|
+ normalized_path);
|
|
+
|
|
+ /* First see if this path already exists in the list */
|
|
+ stream = sscg_io_utils_get_stream_by_path (streams, normalized_path);
|
|
+
|
|
+ if (stream == NULL)
|
|
+ {
|
|
+ /* The path wasn't found, so open it and create it */
|
|
+
|
|
+ /* First advance the index to the end */
|
|
+ for (i = 0; streams[i]; i++)
|
|
+ ;
|
|
+
|
|
+ /* This should never happen. The streams array should always be
|
|
+ * sized to the maximum number of known types. If we are asked to add
|
|
+ * more entries to the array than we have known file types, it must be
|
|
+ * due to a bug.
|
|
+ */
|
|
+ assert (i < SSCG_NUM_FILE_TYPES);
|
|
+
|
|
+ stream = talloc_zero (tmp_ctx, struct sscg_stream);
|
|
+ CHECK_MEM (stream);
|
|
+ talloc_set_destructor ((TALLOC_CTX *)stream, sscg_stream_destructor);
|
|
+
|
|
+ stream->path = talloc_steal (stream, normalized_path);
|
|
+ CHECK_MEM (stream->path);
|
|
+
|
|
+ streams[i] = talloc_steal (streams, stream);
|
|
+ }
|
|
+
|
|
+ /* Always set the mode to the most-restrictive one requested */
|
|
+ SSCG_LOG (SSCG_DEBUG, "Requested mode: %o\n", mode);
|
|
+ if (stream->mode)
|
|
+ stream->mode &= mode;
|
|
+ else
|
|
+ stream->mode = mode;
|
|
+ SSCG_LOG (SSCG_DEBUG, "Actual mode: %o\n", stream->mode);
|
|
+
|
|
+ /* Add the file type */
|
|
+ stream->filetypes |= (1 << filetype);
|
|
+
|
|
+ ret = EOK;
|
|
+
|
|
+done:
|
|
+ talloc_free (tmp_ctx);
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+
|
|
+enum io_utils_errors
|
|
+{
|
|
+ IO_UTILS_OK = 0,
|
|
+ IO_UTILS_TOOMANYKEYS,
|
|
+ IO_UTILS_DHPARAMS_NON_EXCLUSIVE,
|
|
+ IO_UTILS_CRL_NON_EXCLUSIVE,
|
|
+ IO_UTILS_SVC_UNMATCHED,
|
|
+ IO_UTILS_CLIENT_UNMATCHED,
|
|
+ IO_UTILS_CA_UNMATCHED
|
|
+};
|
|
+
|
|
+static enum io_utils_errors
|
|
+io_utils_validate (struct sscg_stream **streams)
|
|
+{
|
|
+ enum io_utils_errors ret;
|
|
+ struct sscg_stream *stream = NULL;
|
|
+ int keybits;
|
|
+ int allbits = 0;
|
|
+
|
|
+ for (int i = 0; (stream = streams[i]) && i < SSCG_NUM_FILE_TYPES; i++)
|
|
+ {
|
|
+ SSCG_LOG (SSCG_DEBUG, "filetypes: 0x%.4x\n", stream->filetypes);
|
|
+
|
|
+ allbits |= stream->filetypes;
|
|
+
|
|
+ /* No file may contain two different private keys */
|
|
+ /* First check if any private keys are in this file */
|
|
+ if ((keybits = stream->filetypes & SSCG_FILE_TYPE_KEYS))
|
|
+ {
|
|
+ /* Next check if there is exactly one private key in the remainder.
|
|
+ * The following bitwise magic checks whether the value is exactly a
|
|
+ * power of two (meaning only one bit is set). If the result is
|
|
+ * nonzero, more than one bit was set and we have been asked to
|
|
+ * include multiple keys into the same file.
|
|
+ */
|
|
+ if (keybits & (keybits - 1))
|
|
+ {
|
|
+ ret = IO_UTILS_TOOMANYKEYS;
|
|
+ goto done;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ /* The dhparams file may only contain dhparams */
|
|
+ if ((stream->filetypes & (1 << SSCG_FILE_TYPE_DHPARAMS)) &&
|
|
+ (stream->filetypes ^ (1 << SSCG_FILE_TYPE_DHPARAMS)))
|
|
+ {
|
|
+ ret = IO_UTILS_DHPARAMS_NON_EXCLUSIVE;
|
|
+ goto done;
|
|
+ }
|
|
+
|
|
+ /* The CRL file may only contain certificate revocations */
|
|
+ if ((stream->filetypes & (1 << SSCG_FILE_TYPE_CRL)) &&
|
|
+ (stream->filetypes ^ (1 << SSCG_FILE_TYPE_CRL)))
|
|
+ {
|
|
+ ret = IO_UTILS_CRL_NON_EXCLUSIVE;
|
|
+ goto done;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ SSCG_LOG (SSCG_DEBUG, "allbits: 0x%.4x\n", allbits);
|
|
+
|
|
+ /* If the public or private key is present for the service cert, the other
|
|
+ * must be present also
|
|
+ */
|
|
+ if ((allbits & SSCG_FILE_TYPE_SVC_TYPES) &&
|
|
+ ((allbits & SSCG_FILE_TYPE_SVC_TYPES) != SSCG_FILE_TYPE_SVC_TYPES))
|
|
+ {
|
|
+ ret = IO_UTILS_SVC_UNMATCHED;
|
|
+ goto done;
|
|
+ }
|
|
+
|
|
+ /* If the public or private key is present for the client cert, the other
|
|
+ * must be present also
|
|
+ */
|
|
+ if ((allbits & SSCG_FILE_TYPE_CLIENT_TYPES) &&
|
|
+ ((allbits & SSCG_FILE_TYPE_CLIENT_TYPES) != SSCG_FILE_TYPE_CLIENT_TYPES))
|
|
+ {
|
|
+ ret = IO_UTILS_CLIENT_UNMATCHED;
|
|
+ goto done;
|
|
+ }
|
|
+
|
|
+ /* If the private key is present for the CA cert, the public key must be
|
|
+ * present also
|
|
+ */
|
|
+ if ((allbits & (1 << SSCG_FILE_TYPE_CA_KEY)) &&
|
|
+ !(allbits & (1 << SSCG_FILE_TYPE_CA)))
|
|
+ {
|
|
+ ret = IO_UTILS_CA_UNMATCHED;
|
|
+ goto done;
|
|
+ }
|
|
+
|
|
+
|
|
+ ret = IO_UTILS_OK;
|
|
+
|
|
+done:
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+
|
|
+int
|
|
+sscg_io_utils_open_output_files (struct sscg_stream **streams, bool overwrite)
|
|
+{
|
|
+ int ret;
|
|
+ TALLOC_CTX *tmp_ctx = NULL;
|
|
+ enum io_utils_errors validation_result;
|
|
+ char *create_mode = NULL;
|
|
+ struct sscg_stream *stream = NULL;
|
|
+
|
|
+ validation_result = io_utils_validate (streams);
|
|
+ switch (validation_result)
|
|
+ {
|
|
+ case IO_UTILS_TOOMANYKEYS:
|
|
+ SSCG_ERROR ("Attempted to output multiple keys to the same file.\n");
|
|
+ ret = EINVAL;
|
|
+ goto done;
|
|
+
|
|
+ case IO_UTILS_CRL_NON_EXCLUSIVE:
|
|
+ SSCG_ERROR ("The CRL file may not include other content.\n");
|
|
+ ret = EINVAL;
|
|
+ goto done;
|
|
+
|
|
+ case IO_UTILS_DHPARAMS_NON_EXCLUSIVE:
|
|
+ SSCG_ERROR ("The dhparams file may not include other content.\n");
|
|
+ ret = EINVAL;
|
|
+ goto done;
|
|
+
|
|
+ case IO_UTILS_SVC_UNMATCHED:
|
|
+ SSCG_ERROR (
|
|
+ "The service certificate must have both public and private key "
|
|
+ "locations specified.\n");
|
|
+ ret = EINVAL;
|
|
+ goto done;
|
|
+
|
|
+ case IO_UTILS_CLIENT_UNMATCHED:
|
|
+ SSCG_ERROR (
|
|
+ "The client certificate must have both public and private key "
|
|
+ "locations specified.\n");
|
|
+ ret = EINVAL;
|
|
+ goto done;
|
|
+
|
|
+ case IO_UTILS_CA_UNMATCHED:
|
|
+ SSCG_ERROR (
|
|
+ "The CA certificate must have a public key location specified.\n");
|
|
+ ret = EINVAL;
|
|
+ goto done;
|
|
+
|
|
+ case IO_UTILS_OK: break;
|
|
+ }
|
|
+
|
|
+ tmp_ctx = talloc_new (NULL);
|
|
+ CHECK_MEM (tmp_ctx);
|
|
+
|
|
+ if (overwrite)
|
|
+ create_mode = talloc_strdup (tmp_ctx, "w");
|
|
+ else
|
|
+ create_mode = talloc_strdup (tmp_ctx, "wx");
|
|
+ CHECK_MEM (create_mode);
|
|
+
|
|
+ for (int i = 0; (stream = streams[i]) && i < SSCG_NUM_FILE_TYPES; i++)
|
|
+ {
|
|
+ SSCG_LOG (SSCG_DEBUG, "Opening %s\n", stream->path);
|
|
+ stream->bio = BIO_new_file (stream->path, create_mode);
|
|
+ CHECK_BIO (stream->bio, stream->path);
|
|
+ }
|
|
+
|
|
+ ret = EOK;
|
|
+done:
|
|
+ talloc_free (tmp_ctx);
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+
|
|
+int
|
|
+sscg_io_utils_finalize_output_files (struct sscg_stream **streams)
|
|
+{
|
|
+ struct sscg_stream *stream = NULL;
|
|
+ FILE *fp;
|
|
+
|
|
+ for (int i = 0; (stream = streams[i]) && i < SSCG_NUM_FILE_TYPES; i++)
|
|
+ {
|
|
+ /* Set the final permissions mode */
|
|
+ SSCG_LOG (SSCG_DEBUG,
|
|
+ "Setting %s file permissions to %o\n",
|
|
+ stream->path,
|
|
+ stream->mode);
|
|
+ BIO_get_fp (stream->bio, &fp);
|
|
+
|
|
+ errno = 0;
|
|
+ if (fchmod (fileno (fp), stream->mode) != 0)
|
|
+ return errno;
|
|
+ }
|
|
+
|
|
+ return EOK;
|
|
+}
|
|
diff --git a/src/service.c b/src/service.c
|
|
deleted file mode 100644
|
|
index 34c976dbe905528000b181c24d1fa95da3cd1377..0000000000000000000000000000000000000000
|
|
--- a/src/service.c
|
|
+++ /dev/null
|
|
@@ -1,164 +0,0 @@
|
|
-/*
|
|
- This file is part of sscg.
|
|
-
|
|
- sscg 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 3 of the License, or
|
|
- (at your option) any later version.
|
|
-
|
|
- sscg 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 sscg. If not, see <http://www.gnu.org/licenses/>.
|
|
-
|
|
- Copyright 2017 by Stephen Gallagher <sgallagh@redhat.com>
|
|
-*/
|
|
-
|
|
-
|
|
-#include "include/sscg.h"
|
|
-#include "include/service.h"
|
|
-#include "include/x509.h"
|
|
-#include "include/key.h"
|
|
-
|
|
-int
|
|
-create_service_cert (TALLOC_CTX *mem_ctx,
|
|
- const struct sscg_options *options,
|
|
- struct sscg_x509_cert *ca_cert,
|
|
- struct sscg_evp_pkey *ca_key,
|
|
- struct sscg_x509_cert **_svc_cert,
|
|
- struct sscg_evp_pkey **_svc_key)
|
|
-{
|
|
- int ret;
|
|
- size_t i;
|
|
- struct sscg_bignum *e;
|
|
- struct sscg_bignum *serial;
|
|
- struct sscg_cert_info *svc_certinfo;
|
|
- struct sscg_x509_req *csr;
|
|
- struct sscg_evp_pkey *pkey;
|
|
- struct sscg_x509_cert *cert;
|
|
- X509_EXTENSION *ex = NULL;
|
|
- TALLOC_CTX *tmp_ctx = NULL;
|
|
-
|
|
- tmp_ctx = talloc_new (NULL);
|
|
- CHECK_MEM (tmp_ctx);
|
|
-
|
|
- /* create a serial number for this certificate */
|
|
- ret = sscg_generate_serial (tmp_ctx, &serial);
|
|
- CHECK_OK (ret);
|
|
-
|
|
- svc_certinfo = sscg_cert_info_new (tmp_ctx, options->hash_fn);
|
|
- CHECK_MEM (svc_certinfo);
|
|
-
|
|
- /* Populate cert_info from options */
|
|
- svc_certinfo->country = talloc_strdup (svc_certinfo, options->country);
|
|
- CHECK_MEM (svc_certinfo->country);
|
|
-
|
|
- svc_certinfo->state = talloc_strdup (svc_certinfo, options->state);
|
|
- CHECK_MEM (svc_certinfo->state);
|
|
-
|
|
- svc_certinfo->locality = talloc_strdup (svc_certinfo, options->locality);
|
|
- CHECK_MEM (svc_certinfo->locality);
|
|
-
|
|
- svc_certinfo->org = talloc_strdup (svc_certinfo, options->org);
|
|
- CHECK_MEM (svc_certinfo->org);
|
|
-
|
|
- svc_certinfo->org_unit = talloc_strdup (svc_certinfo, options->org_unit);
|
|
- CHECK_MEM (svc_certinfo->org_unit);
|
|
-
|
|
- svc_certinfo->email = talloc_strdup (svc_certinfo, options->email);
|
|
- CHECK_MEM (svc_certinfo->email);
|
|
-
|
|
- svc_certinfo->cn = talloc_strdup (svc_certinfo, options->hostname);
|
|
- CHECK_MEM (svc_certinfo->cn);
|
|
-
|
|
- if (options->subject_alt_names)
|
|
- {
|
|
- for (i = 0; options->subject_alt_names[i]; i++)
|
|
- {
|
|
- svc_certinfo->subject_alt_names = talloc_realloc (
|
|
- svc_certinfo, svc_certinfo->subject_alt_names, char *, i + 2);
|
|
- CHECK_MEM (svc_certinfo->subject_alt_names);
|
|
-
|
|
- svc_certinfo->subject_alt_names[i] = talloc_strdup (
|
|
- svc_certinfo->subject_alt_names, options->subject_alt_names[i]);
|
|
- CHECK_MEM (svc_certinfo->subject_alt_names[i]);
|
|
-
|
|
- /* Add a NULL terminator to the end */
|
|
- svc_certinfo->subject_alt_names[i + 1] = NULL;
|
|
- }
|
|
- }
|
|
-
|
|
- /* Ensure that this certificate may not sign other certificates */
|
|
- /* Add key extensions */
|
|
- ex = X509V3_EXT_conf_nid (
|
|
- NULL, NULL, NID_key_usage, "critical,digitalSignature,keyEncipherment");
|
|
- CHECK_MEM (ex);
|
|
- sk_X509_EXTENSION_push (svc_certinfo->extensions, ex);
|
|
-
|
|
- /* Mark it as not a CA */
|
|
- ex = X509V3_EXT_conf_nid (NULL, NULL, NID_basic_constraints, "CA:FALSE");
|
|
- CHECK_MEM (ex);
|
|
- sk_X509_EXTENSION_push (svc_certinfo->extensions, ex);
|
|
-
|
|
- /* Use an exponent value of RSA F4 aka 0x10001 (65537) */
|
|
- ret = sscg_init_bignum (tmp_ctx, RSA_F4, &e);
|
|
- CHECK_OK (ret);
|
|
-
|
|
- /* Generate an RSA keypair for this CA */
|
|
- if (options->verbosity >= SSCG_VERBOSE)
|
|
- {
|
|
- fprintf (stdout, "Generating RSA key for service certificate.\n");
|
|
- }
|
|
- /* TODO: support DSA keys as well */
|
|
- ret = sscg_generate_rsa_key (tmp_ctx, options->key_strength, e, &pkey);
|
|
- CHECK_OK (ret);
|
|
-
|
|
- /* Create a certificate signing request for the private CA */
|
|
- if (options->verbosity >= SSCG_VERBOSE)
|
|
- {
|
|
- fprintf (stdout, "Generating CSR for service certificate.\n");
|
|
- }
|
|
- ret = sscg_x509v3_csr_new (tmp_ctx, svc_certinfo, pkey, &csr);
|
|
- CHECK_OK (ret);
|
|
-
|
|
- /* Finalize the CSR */
|
|
- ret = sscg_x509v3_csr_finalize (svc_certinfo, pkey, csr);
|
|
- CHECK_OK (ret);
|
|
-
|
|
- if (options->verbosity >= SSCG_DEBUG)
|
|
- {
|
|
- fprintf (stderr,
|
|
- "DEBUG: Writing service certificate CSR to ./debug-svc.csr\n");
|
|
- BIO *svc_csr_out = BIO_new_file ("./debug-svc.csr", "w");
|
|
- int sslret = PEM_write_bio_X509_REQ (svc_csr_out, csr->x509_req);
|
|
- CHECK_SSL (sslret, PEM_write_bio_X509_REQ);
|
|
- }
|
|
-
|
|
- /* Sign the certificate */
|
|
-
|
|
- if (options->verbosity >= SSCG_VERBOSE)
|
|
- {
|
|
- fprintf (stdout, "Signing CSR for service certificate. \n");
|
|
- }
|
|
-
|
|
- ret = sscg_sign_x509_csr (tmp_ctx,
|
|
- csr,
|
|
- serial,
|
|
- options->lifetime,
|
|
- ca_cert,
|
|
- ca_key,
|
|
- options->hash_fn,
|
|
- &cert);
|
|
- CHECK_OK (ret);
|
|
-
|
|
- *_svc_cert = talloc_steal (mem_ctx, cert);
|
|
- *_svc_key = talloc_steal (mem_ctx, pkey);
|
|
-
|
|
- ret = EOK;
|
|
-done:
|
|
- talloc_free (tmp_ctx);
|
|
- return ret;
|
|
-}
|
|
diff --git a/src/sscg.c b/src/sscg.c
|
|
index a02e4df66c6cf9ec1865f425b4a15da82fbfdc72..470af815d91f5170a1e8fe00006dbaee4d07b209 100644
|
|
--- a/src/sscg.c
|
|
+++ b/src/sscg.c
|
|
@@ -32,7 +32,12 @@
|
|
#include "config.h"
|
|
#include "include/sscg.h"
|
|
#include "include/authority.h"
|
|
-#include "include/service.h"
|
|
+#include "include/cert.h"
|
|
+#include "include/dhparams.h"
|
|
+#include "include/io_utils.h"
|
|
+
|
|
+
|
|
+int verbosity;
|
|
|
|
|
|
/* Same as OpenSSL CLI */
|
|
@@ -58,6 +63,8 @@ set_default_options (struct sscg_options *opts)
|
|
int security_level = get_security_level ();
|
|
|
|
opts->lifetime = 3650;
|
|
+ opts->dhparams_prime_len = 2048;
|
|
+ opts->dhparams_generator = 2;
|
|
|
|
/* Select the default key strength based on the system security level
|
|
* See:
|
|
@@ -132,51 +139,6 @@ print_options (struct sscg_options *opts)
|
|
fprintf (stdout, "=================\n");
|
|
}
|
|
|
|
-static int
|
|
-_sscg_normalize_path (TALLOC_CTX *mem_ctx,
|
|
- const char *path,
|
|
- const char *path_default,
|
|
- char **_normalized_path)
|
|
-{
|
|
- int ret;
|
|
- char *orig_path = NULL;
|
|
- char *normalized_path = NULL;
|
|
-
|
|
- TALLOC_CTX *tmp_ctx = talloc_new (NULL);
|
|
- CHECK_MEM (tmp_ctx);
|
|
-
|
|
- if (path)
|
|
- {
|
|
- orig_path = talloc_strdup (tmp_ctx, path);
|
|
- }
|
|
- else
|
|
- {
|
|
- if (!path_default)
|
|
- {
|
|
- /* If no default is set and no path was provided,
|
|
- * return NULL */
|
|
- *_normalized_path = NULL;
|
|
- ret = EOK;
|
|
- goto done;
|
|
- }
|
|
- orig_path = talloc_strdup (tmp_ctx, path_default);
|
|
- CHECK_MEM (orig_path);
|
|
- }
|
|
-
|
|
- normalized_path = talloc_zero_array (tmp_ctx, char, PATH_MAX);
|
|
- CHECK_MEM (normalized_path);
|
|
-
|
|
- ret = make_normalized_absolute_path (normalized_path, PATH_MAX, orig_path);
|
|
- CHECK_OK (ret);
|
|
-
|
|
- *_normalized_path = talloc_steal (mem_ctx, normalized_path);
|
|
- ret = EOK;
|
|
-
|
|
-done:
|
|
- talloc_free (tmp_ctx);
|
|
- return ret;
|
|
-}
|
|
-
|
|
|
|
/* This function takes a copy of a string into a talloc hierarchy and memsets
|
|
* the original string to zeroes to avoid leaking it when that memory is freed.
|
|
@@ -251,6 +213,53 @@ sscg_read_pw_file (TALLOC_CTX *mem_ctx, char *path)
|
|
}
|
|
|
|
|
|
+const char *
|
|
+sscg_get_verbosity_name (enum sscg_verbosity type)
|
|
+{
|
|
+ switch (type)
|
|
+ {
|
|
+ case SSCG_DEFAULT:
|
|
+ case SSCG_VERBOSE: return "";
|
|
+
|
|
+ case SSCG_DEBUG: return "DEBUG: ";
|
|
+
|
|
+ default: break;
|
|
+ }
|
|
+
|
|
+ /* If it wasn't one of these, we have a bug */
|
|
+ return "Unknown Verbosity (bug):";
|
|
+}
|
|
+
|
|
+
|
|
+const char *
|
|
+sscg_get_file_type_name (enum sscg_file_type type)
|
|
+{
|
|
+ switch (type)
|
|
+ {
|
|
+ case SSCG_FILE_TYPE_CA: return "CA certificate";
|
|
+
|
|
+ case SSCG_FILE_TYPE_CA_KEY: return "CA certificate key";
|
|
+
|
|
+ case SSCG_FILE_TYPE_SVC: return "service certificate";
|
|
+
|
|
+ case SSCG_FILE_TYPE_SVC_KEY: return "service certificate key";
|
|
+
|
|
+ case SSCG_FILE_TYPE_CLIENT: return "client auth certificate";
|
|
+
|
|
+ case SSCG_FILE_TYPE_CLIENT_KEY: return "client auth certificate key";
|
|
+
|
|
+ case SSCG_FILE_TYPE_CRL: return "certificate revocation list";
|
|
+
|
|
+ case SSCG_FILE_TYPE_DHPARAMS: return "Diffie-Hellman parameters";
|
|
+
|
|
+ default: break;
|
|
+ }
|
|
+
|
|
+ /* If it wasn't one of these, we have a bug */
|
|
+ return "Unknown (bug)";
|
|
+}
|
|
+
|
|
+
|
|
int
|
|
main (int argc, const char **argv)
|
|
{
|
|
@@ -274,36 +283,48 @@ main (int argc, const char **argv)
|
|
char *ca_key_file = NULL;
|
|
char *cert_file = NULL;
|
|
char *cert_key_file = NULL;
|
|
+ char *client_file = NULL;
|
|
+ char *client_key_file = NULL;
|
|
+ char *dhparams_file = NULL;
|
|
|
|
- int ca_mode = 0644;
|
|
- int ca_key_mode = 0600;
|
|
+ int ca_mode = SSCG_CERT_DEFAULT_MODE;
|
|
+ int ca_key_mode = SSCG_KEY_DEFAULT_MODE;
|
|
char *ca_key_password = NULL;
|
|
char *ca_key_passfile = NULL;
|
|
|
|
- int cert_mode = 0644;
|
|
- int cert_key_mode = 0600;
|
|
+ int crl_mode = SSCG_CERT_DEFAULT_MODE;
|
|
+ char *crl_file = NULL;
|
|
+
|
|
+ int cert_mode = SSCG_CERT_DEFAULT_MODE;
|
|
+ int cert_key_mode = SSCG_KEY_DEFAULT_MODE;
|
|
char *cert_key_password = NULL;
|
|
char *cert_key_passfile = NULL;
|
|
|
|
- char *create_mode = NULL;
|
|
+ int client_mode = SSCG_CERT_DEFAULT_MODE;
|
|
+ int client_key_mode = SSCG_KEY_DEFAULT_MODE;
|
|
+ char *client_key_password = NULL;
|
|
+ char *client_key_passfile = NULL;
|
|
|
|
struct sscg_x509_cert *cacert;
|
|
struct sscg_evp_pkey *cakey;
|
|
struct sscg_x509_cert *svc_cert;
|
|
struct sscg_evp_pkey *svc_key;
|
|
+ struct sscg_x509_cert *client_cert;
|
|
+ struct sscg_evp_pkey *client_key;
|
|
|
|
- BIO *ca_out = NULL;
|
|
- BIO *ca_key_out = NULL;
|
|
- BIO *cert_out = NULL;
|
|
- BIO *cert_key_out = NULL;
|
|
-
|
|
- FILE *fp;
|
|
+ int dhparams_mode = SSCG_CERT_DEFAULT_MODE;
|
|
+ struct sscg_dhparams *dhparams = NULL;
|
|
|
|
/* Always use umask 0577 for generating certificates and keys
|
|
This means that it's opened as write-only by the effective
|
|
user. */
|
|
umask (0577);
|
|
|
|
+#if OPENSSL_VERSION_NUMBER < 0x10100000L
|
|
+ /* In OpenSSL <1.1.0, we need to initialize the library. */
|
|
+ OpenSSL_add_all_algorithms ();
|
|
+#endif
|
|
+
|
|
TALLOC_CTX *main_ctx = talloc_new (NULL);
|
|
if (!main_ctx)
|
|
{
|
|
@@ -315,6 +336,9 @@ main (int argc, const char **argv)
|
|
CHECK_MEM (options);
|
|
talloc_set_destructor ((TALLOC_CTX *)options, sscg_options_destructor);
|
|
|
|
+ options->streams =
|
|
+ talloc_zero_array (options, struct sscg_stream *, SSCG_NUM_FILE_TYPES);
|
|
+
|
|
ret = set_default_options (options);
|
|
if (ret != EOK)
|
|
goto done;
|
|
@@ -323,101 +347,144 @@ main (int argc, const char **argv)
|
|
talloc_asprintf (main_ctx, "%d or larger", options->minimum_key_strength);
|
|
|
|
options->verbosity = SSCG_DEFAULT;
|
|
+ // clang-format off
|
|
struct poptOption long_options[] = {
|
|
- POPT_AUTOHELP{ "quiet",
|
|
- 'q',
|
|
- POPT_ARG_VAL,
|
|
- &options->verbosity,
|
|
- SSCG_QUIET,
|
|
- _ ("Display no output unless there is an error."),
|
|
- NULL },
|
|
- { "verbose",
|
|
+ POPT_AUTOHELP
|
|
+
|
|
+ {
|
|
+ "quiet",
|
|
+ 'q',
|
|
+ POPT_ARG_VAL,
|
|
+ &options->verbosity,
|
|
+ SSCG_QUIET,
|
|
+ ("Display no output unless there is an error."),
|
|
+ NULL
|
|
+ },
|
|
+
|
|
+ {
|
|
+ "verbose",
|
|
'v',
|
|
POPT_ARG_VAL,
|
|
&options->verbosity,
|
|
SSCG_VERBOSE,
|
|
_ ("Display progress messages."),
|
|
- NULL },
|
|
- { "debug",
|
|
+ NULL
|
|
+ },
|
|
+
|
|
+ {
|
|
+ "debug",
|
|
'd',
|
|
POPT_ARG_VAL,
|
|
&options->verbosity,
|
|
SSCG_DEBUG,
|
|
_ ("Enable logging of debug messages. Implies verbose. Warning! "
|
|
"This will print private key information to the screen!"),
|
|
- NULL },
|
|
- { "version",
|
|
+ NULL
|
|
+ },
|
|
+
|
|
+ {
|
|
+ "version",
|
|
'V',
|
|
POPT_ARG_NONE,
|
|
&options->print_version,
|
|
0,
|
|
_ ("Display the version number and exit."),
|
|
- NULL },
|
|
- { "force",
|
|
+ NULL
|
|
+ },
|
|
+
|
|
+ {
|
|
+ "force",
|
|
'f',
|
|
POPT_ARG_NONE,
|
|
&options->overwrite,
|
|
0,
|
|
_ ("Overwrite any pre-existing files in the requested locations"),
|
|
- NULL },
|
|
- { "lifetime",
|
|
+ NULL
|
|
+ },
|
|
+
|
|
+ {
|
|
+ "lifetime",
|
|
'\0',
|
|
POPT_ARG_INT | POPT_ARGFLAG_SHOW_DEFAULT,
|
|
&options->lifetime,
|
|
0,
|
|
_ ("Certificate lifetime (days)."),
|
|
- _ ("1-3650") },
|
|
- { "country",
|
|
+ _ ("1-3650")
|
|
+ },
|
|
+
|
|
+ {
|
|
+ "country",
|
|
'\0',
|
|
POPT_ARG_STRING,
|
|
&country,
|
|
0,
|
|
_ ("Certificate DN: Country (C). (default: \"US\")"),
|
|
- _ ("US, CZ, etc.") },
|
|
- { "state",
|
|
+ _ ("US, CZ, etc.")
|
|
+ },
|
|
+
|
|
+ {
|
|
+ "state",
|
|
'\0',
|
|
POPT_ARG_STRING,
|
|
&state,
|
|
0,
|
|
_ ("Certificate DN: State or Province (ST)."),
|
|
- _ ("Massachusetts, British Columbia, etc.") },
|
|
- { "locality",
|
|
+ _ ("Massachusetts, British Columbia, etc.")
|
|
+ },
|
|
+
|
|
+ {
|
|
+ "locality",
|
|
'\0',
|
|
POPT_ARG_STRING,
|
|
&locality,
|
|
0,
|
|
_ ("Certificate DN: Locality (L)."),
|
|
- _ ("Westford, Paris, etc.") },
|
|
- { "organization",
|
|
+ _ ("Westford, Paris, etc.")
|
|
+ },
|
|
+
|
|
+ {
|
|
+ "organization",
|
|
'\0',
|
|
POPT_ARG_STRING,
|
|
&organization,
|
|
0,
|
|
_ ("Certificate DN: Organization (O). (default: \"Unspecified\")"),
|
|
- _ ("My Company") },
|
|
- { "organizational-unit",
|
|
+ _ ("My Company")
|
|
+ },
|
|
+
|
|
+ {
|
|
+ "organizational-unit",
|
|
'\0',
|
|
POPT_ARG_STRING,
|
|
&organizational_unit,
|
|
0,
|
|
_ ("Certificate DN: Organizational Unit (OU)."),
|
|
- _ ("Engineering, etc.") },
|
|
- { "email",
|
|
+ _ ("Engineering, etc.")
|
|
+ },
|
|
+
|
|
+ {
|
|
+ "email",
|
|
'\0',
|
|
POPT_ARG_STRING,
|
|
&email,
|
|
0,
|
|
_ ("Certificate DN: Email Address (Email)."),
|
|
- _ ("myname@example.com") },
|
|
- { "hostname",
|
|
+ _ ("myname@example.com")
|
|
+ },
|
|
+
|
|
+ {
|
|
+ "hostname",
|
|
'\0',
|
|
POPT_ARG_STRING,
|
|
&hostname,
|
|
0,
|
|
_ ("The valid hostname of the certificate. Must be an FQDN. (default: "
|
|
"current system FQDN)"),
|
|
- _ ("server.example.com") },
|
|
- { "subject-alt-name",
|
|
+ _ ("server.example.com")
|
|
+ },
|
|
+
|
|
+ {
|
|
+ "subject-alt-name",
|
|
'\0',
|
|
POPT_ARG_ARGV,
|
|
&alternative_names,
|
|
@@ -427,7 +494,9 @@ main (int argc, const char **argv)
|
|
"supported by RFC 5280 such as "
|
|
"IP:xxx.xxx.xxx.xxx/yyy.yyy.yyy.yyy "
|
|
"May be specified multiple times."),
|
|
- _ ("alt.example.com") },
|
|
+ _ ("alt.example.com")
|
|
+ },
|
|
+
|
|
{
|
|
"package",
|
|
'\0',
|
|
@@ -437,7 +506,9 @@ main (int argc, const char **argv)
|
|
_ ("Unused. Retained for compatibility with earlier versions of sscg."),
|
|
NULL,
|
|
},
|
|
- { "key-strength",
|
|
+
|
|
+ {
|
|
+ "key-strength",
|
|
'\0',
|
|
POPT_ARG_INT | POPT_ARGFLAG_SHOW_DEFAULT,
|
|
&options->key_strength,
|
|
@@ -453,6 +524,7 @@ main (int argc, const char **argv)
|
|
_ ("Hashing algorithm to use for signing."),
|
|
_ ("{sha256,sha384,sha512}"),
|
|
},
|
|
+
|
|
{
|
|
"cipher-alg",
|
|
'\0',
|
|
@@ -473,15 +545,17 @@ main (int argc, const char **argv)
|
|
"\"./ca.crt\")"),
|
|
NULL,
|
|
},
|
|
+
|
|
{
|
|
"ca-mode",
|
|
'\0',
|
|
POPT_ARG_INT,
|
|
&ca_mode,
|
|
0,
|
|
- _ ("File mode of the created CA certificate. (default: 0644)"),
|
|
- _ ("0644"),
|
|
+ _ ("File mode of the created CA certificate."),
|
|
+ SSCG_CERT_DEFAULT_MODE_HELP,
|
|
},
|
|
+
|
|
{
|
|
"ca-key-file",
|
|
'\0',
|
|
@@ -492,15 +566,17 @@ main (int argc, const char **argv)
|
|
"the key will be destroyed rather than written to the disk."),
|
|
NULL,
|
|
},
|
|
+
|
|
{
|
|
"ca-key-mode",
|
|
'\0',
|
|
POPT_ARG_INT,
|
|
&ca_key_mode,
|
|
0,
|
|
- _ ("File mode of the created CA key. (default: 0600)"),
|
|
- _ ("0600"),
|
|
+ _ ("File mode of the created CA key."),
|
|
+ SSCG_KEY_DEFAULT_MODE_HELP,
|
|
},
|
|
+
|
|
{
|
|
"ca-key-password",
|
|
'\0',
|
|
@@ -534,6 +610,28 @@ main (int argc, const char **argv)
|
|
NULL
|
|
},
|
|
|
|
+ {
|
|
+ "crl-file",
|
|
+ '\0',
|
|
+ POPT_ARG_STRING,
|
|
+ &crl_file,
|
|
+ 0,
|
|
+ _ ("Path where an (empty) Certificate Revocation List file will be "
|
|
+ "created, for applications that expect such a file to exist. If "
|
|
+ "unspecified, no such file will be created."),
|
|
+ NULL
|
|
+ },
|
|
+
|
|
+ {
|
|
+ "crl-mode",
|
|
+ '\0',
|
|
+ POPT_ARG_INT,
|
|
+ &crl_mode,
|
|
+ 0,
|
|
+ _ ("File mode of the created Certificate Revocation List."),
|
|
+ SSCG_CERT_DEFAULT_MODE_HELP,
|
|
+ },
|
|
+
|
|
{
|
|
"cert-file",
|
|
'\0',
|
|
@@ -544,15 +642,17 @@ main (int argc, const char **argv)
|
|
"(default \"./service.pem\")"),
|
|
NULL,
|
|
},
|
|
+
|
|
{
|
|
"cert-mode",
|
|
'\0',
|
|
POPT_ARG_INT,
|
|
&cert_mode,
|
|
0,
|
|
- _ ("File mode of the created certificate. (default: 0644)"),
|
|
- _ ("0644"),
|
|
+ _ ("File mode of the created certificate."),
|
|
+ SSCG_CERT_DEFAULT_MODE_HELP,
|
|
},
|
|
+
|
|
{
|
|
"cert-key-file",
|
|
'\0',
|
|
@@ -563,15 +663,17 @@ main (int argc, const char **argv)
|
|
"(default \"service-key.pem\")"),
|
|
NULL,
|
|
},
|
|
+
|
|
{
|
|
"cert-key-mode",
|
|
'\0',
|
|
POPT_ARG_INT,
|
|
&cert_key_mode,
|
|
0,
|
|
- _ ("File mode of the created certificate key. (default: 0600)"),
|
|
- _ ("0600"),
|
|
+ _ ("File mode of the created certificate key."),
|
|
+ SSCG_KEY_DEFAULT_MODE_HELP,
|
|
},
|
|
+
|
|
{
|
|
"cert-key-password",
|
|
'p',
|
|
@@ -605,8 +707,115 @@ main (int argc, const char **argv)
|
|
NULL
|
|
},
|
|
|
|
+ {
|
|
+ "client-file",
|
|
+ '\0',
|
|
+ POPT_ARG_STRING,
|
|
+ &client_file,
|
|
+ 0,
|
|
+ _ ("Path where a client authentication certificate will be stored."),
|
|
+ NULL
|
|
+ },
|
|
+ {
|
|
+ "client-mode",
|
|
+ '\0',
|
|
+ POPT_ARG_INT,
|
|
+ &client_mode,
|
|
+ 0,
|
|
+ _ ("File mode of the created certificate."),
|
|
+ SSCG_CERT_DEFAULT_MODE_HELP,
|
|
+ },
|
|
+
|
|
+ {
|
|
+ "client-key-file",
|
|
+ '\0',
|
|
+ POPT_ARG_STRING,
|
|
+ &client_key_file,
|
|
+ 0,
|
|
+ _ ("Path where the client's private key will be stored. "
|
|
+ "(default is client-file with a .key suffix, if "
|
|
+ "--client-file was passed, otherwise this file will not "
|
|
+ "be generated.)"),
|
|
+ NULL,
|
|
+ },
|
|
+
|
|
+ {
|
|
+ "client-key-mode",
|
|
+ '\0',
|
|
+ POPT_ARG_INT,
|
|
+ &client_key_mode,
|
|
+ 0,
|
|
+ _ ("File mode of the created certificate key."),
|
|
+ SSCG_KEY_DEFAULT_MODE_HELP,
|
|
+ },
|
|
+
|
|
+ {
|
|
+ "client-key-password",
|
|
+ '\0',
|
|
+ POPT_ARG_STRING,
|
|
+ &client_key_password,
|
|
+ 0,
|
|
+ _ ("Provide a password for the client key file. Note that this will be "
|
|
+ "visible in the process table for all users, so this flag should be "
|
|
+ "used for testing purposes only. Use --client-keypassfile or "
|
|
+ "--client-key-password-prompt for secure password entry."),
|
|
+ NULL
|
|
+ },
|
|
+
|
|
+ {
|
|
+ "client-key-passfile",
|
|
+ '\0',
|
|
+ POPT_ARG_STRING,
|
|
+ &client_key_passfile,
|
|
+ 0,
|
|
+ _ ("A file containing the password to encrypt the client key file."),
|
|
+ NULL
|
|
+ },
|
|
+
|
|
+ {
|
|
+ "client-key-password-prompt",
|
|
+ '\0',
|
|
+ POPT_ARG_NONE,
|
|
+ &options->client_key_pass_prompt,
|
|
+ 0,
|
|
+ _ ("Prompt to enter a password for the client key file."),
|
|
+ NULL
|
|
+ },
|
|
+
|
|
+ {
|
|
+ "dhparams-file",
|
|
+ '\0',
|
|
+ POPT_ARG_STRING,
|
|
+ &dhparams_file,
|
|
+ 0,
|
|
+ _("A file to contain a set of generated Diffie-Hellman parameters. "
|
|
+ "If unspecified, no such file will be created."),
|
|
+ NULL
|
|
+ },
|
|
+
|
|
+ {
|
|
+ "dhparams-prime-len",
|
|
+ '\0',
|
|
+ POPT_ARG_INT | POPT_ARGFLAG_SHOW_DEFAULT,
|
|
+ &options->dhparams_prime_len,
|
|
+ 0,
|
|
+ _ ("The length of the prime number to generate for dhparams, in bits."),
|
|
+ NULL
|
|
+ },
|
|
+
|
|
+ {
|
|
+ "dhparams-generator",
|
|
+ '\0',
|
|
+ POPT_ARG_INT | POPT_ARGFLAG_SHOW_DEFAULT,
|
|
+ &options->dhparams_generator,
|
|
+ 0,
|
|
+ _ ("The generator value for dhparams."),
|
|
+ _("{2,3,5}")
|
|
+ },
|
|
+
|
|
POPT_TABLEEND
|
|
};
|
|
+ // clang-format on
|
|
|
|
pc = poptGetContext (argv[0], argc, argv, long_options, 0);
|
|
while ((opt = poptGetNextOpt (pc)) != -1)
|
|
@@ -630,6 +839,8 @@ main (int argc, const char **argv)
|
|
return 0;
|
|
}
|
|
|
|
+ verbosity = options->verbosity;
|
|
+
|
|
/* Process the Subject information */
|
|
|
|
if (country)
|
|
@@ -788,6 +999,22 @@ main (int argc, const char **argv)
|
|
}
|
|
}
|
|
|
|
+ if (client_key_password)
|
|
+ {
|
|
+ options->client_key_pass =
|
|
+ sscg_secure_string_steal (options, client_key_password);
|
|
+ }
|
|
+ else if (client_key_passfile)
|
|
+ {
|
|
+ options->client_key_pass =
|
|
+ sscg_read_pw_file (options, client_key_passfile);
|
|
+ if (!options->client_key_pass)
|
|
+ {
|
|
+ fprintf (
|
|
+ stderr, "Failed to read passphrase from %s", client_key_passfile);
|
|
+ goto done;
|
|
+ }
|
|
+ }
|
|
|
|
if (options->key_strength < options->minimum_key_strength)
|
|
{
|
|
@@ -822,86 +1049,115 @@ main (int argc, const char **argv)
|
|
if (options->verbosity >= SSCG_VERBOSE)
|
|
print_options (options);
|
|
|
|
- /* Get the paths of the output files */
|
|
- ret = _sscg_normalize_path (options, ca_file, "./ca.crt", &options->ca_file);
|
|
+ /* Prepare the output files */
|
|
+ ret = sscg_io_utils_add_output_file (options->streams,
|
|
+ SSCG_FILE_TYPE_CA,
|
|
+ ca_file ? ca_file : "./ca.crt",
|
|
+ ca_mode);
|
|
CHECK_OK (ret);
|
|
|
|
- ret =
|
|
- _sscg_normalize_path (options, ca_key_file, NULL, &options->ca_key_file);
|
|
+ ret = sscg_io_utils_add_output_file (
|
|
+ options->streams, SSCG_FILE_TYPE_CA_KEY, ca_key_file, ca_key_mode);
|
|
CHECK_OK (ret);
|
|
- if (options->verbosity >= SSCG_DEBUG)
|
|
- {
|
|
- fprintf (stdout,
|
|
- "DEBUG: CA Key file path: %s\n",
|
|
- options->ca_key_file ? options->ca_key_file : "(N/A)");
|
|
- }
|
|
-
|
|
- ret = _sscg_normalize_path (
|
|
- options, cert_file, "./service.pem", &options->cert_file);
|
|
+
|
|
+ ret = sscg_io_utils_add_output_file (options->streams,
|
|
+ SSCG_FILE_TYPE_SVC,
|
|
+ cert_file ? cert_file : "./service.pem",
|
|
+ cert_mode);
|
|
+ CHECK_OK (ret);
|
|
+
|
|
+ ret = sscg_io_utils_add_output_file (options->streams,
|
|
+ SSCG_FILE_TYPE_SVC_KEY,
|
|
+ cert_key_file ? cert_key_file :
|
|
+ "./service-key.pem",
|
|
+ cert_key_mode);
|
|
+ CHECK_OK (ret);
|
|
+
|
|
+
|
|
+ ret = sscg_io_utils_add_output_file (
|
|
+ options->streams, SSCG_FILE_TYPE_CLIENT, client_file, client_mode);
|
|
+ CHECK_OK (ret);
|
|
+
|
|
+
|
|
+ ret = sscg_io_utils_add_output_file (options->streams,
|
|
+ SSCG_FILE_TYPE_CLIENT_KEY,
|
|
+ client_key_file ? client_key_file :
|
|
+ client_file,
|
|
+ client_key_mode);
|
|
+ CHECK_OK (ret);
|
|
+
|
|
+ ret = sscg_io_utils_add_output_file (
|
|
+ options->streams, SSCG_FILE_TYPE_CRL, crl_file, crl_mode);
|
|
CHECK_OK (ret);
|
|
|
|
- ret = _sscg_normalize_path (
|
|
- options, cert_key_file, "./service-key.pem", &options->cert_key_file);
|
|
+ ret = sscg_io_utils_add_output_file (
|
|
+ options->streams, SSCG_FILE_TYPE_DHPARAMS, dhparams_file, dhparams_mode);
|
|
CHECK_OK (ret);
|
|
|
|
poptFreeContext (pc);
|
|
|
|
- /* Validate the file paths */
|
|
+ /* Validate and open the file paths */
|
|
+ ret = sscg_io_utils_open_output_files (options->streams, options->overwrite);
|
|
+ CHECK_OK (ret);
|
|
|
|
- /* Only one key can exist in a single file */
|
|
- if (options->ca_key_file &&
|
|
- strcmp (options->ca_key_file, options->cert_key_file) == 0)
|
|
- {
|
|
- fprintf (stderr,
|
|
- "Certificate key and CA key may not be in the same file.\n");
|
|
- ret = EINVAL;
|
|
- goto done;
|
|
- }
|
|
-
|
|
- /* The CA key must not be in the same file as the service cert */
|
|
- if (options->ca_key_file &&
|
|
- strcmp (options->ca_key_file, options->cert_file) == 0)
|
|
- {
|
|
- fprintf (
|
|
- stderr,
|
|
- "CA key and service certificate may not be in the same file.\n");
|
|
- ret = EINVAL;
|
|
- goto done;
|
|
- }
|
|
|
|
/* Generate the private CA for the certificate */
|
|
ret = create_private_CA (main_ctx, options, &cacert, &cakey);
|
|
CHECK_OK (ret);
|
|
|
|
/* Generate the service certificate and sign it with the private CA */
|
|
- ret = create_service_cert (
|
|
- main_ctx, options, cacert, cakey, &svc_cert, &svc_key);
|
|
+ ret = create_cert (main_ctx,
|
|
+ options,
|
|
+ cacert,
|
|
+ cakey,
|
|
+ SSCG_CERT_TYPE_SERVER,
|
|
+ &svc_cert,
|
|
+ &svc_key);
|
|
CHECK_OK (ret);
|
|
|
|
+ /* If requested, generate the client auth certificate and sign it with the
|
|
+ * private CA.
|
|
+ */
|
|
+ if (GET_BIO (SSCG_FILE_TYPE_CLIENT))
|
|
+ {
|
|
+ ret = create_cert (main_ctx,
|
|
+ options,
|
|
+ cacert,
|
|
+ cakey,
|
|
+ SSCG_CERT_TYPE_CLIENT,
|
|
+ &client_cert,
|
|
+ &client_key);
|
|
+ CHECK_OK (ret);
|
|
+ }
|
|
+
|
|
|
|
/* ==== Output the final files ==== */
|
|
|
|
- /* Set the file-creation mode */
|
|
- if (options->overwrite)
|
|
- {
|
|
- create_mode = talloc_strdup (main_ctx, "w");
|
|
- }
|
|
- else
|
|
- {
|
|
- create_mode = talloc_strdup (main_ctx, "wx");
|
|
- }
|
|
- CHECK_MEM (create_mode);
|
|
|
|
- /* Create certificate private key file */
|
|
- if (options->verbosity >= SSCG_DEFAULT)
|
|
+ /* Write private keys first */
|
|
+
|
|
+ if (GET_BIO (SSCG_FILE_TYPE_CLIENT_KEY))
|
|
{
|
|
- fprintf (
|
|
- stdout, "Writing svc private key to %s \n", options->cert_key_file);
|
|
+ /* This function has a default mechanism for prompting for the
|
|
+ * password if it is passed a cipher and gets a NULL password.
|
|
+ *
|
|
+ * Only pass the cipher if we have a password or were instructed
|
|
+ * to prompt for one.
|
|
+ */
|
|
+ sret = PEM_write_bio_PrivateKey (
|
|
+ GET_BIO (SSCG_FILE_TYPE_CLIENT_KEY),
|
|
+ client_key->evp_pkey,
|
|
+ options->client_key_pass_prompt || options->client_key_pass ?
|
|
+ options->cipher :
|
|
+ NULL,
|
|
+ (unsigned char *)options->client_key_pass,
|
|
+ options->client_key_pass ? strlen (options->client_key_pass) : 0,
|
|
+ NULL,
|
|
+ NULL);
|
|
+ CHECK_SSL (sret, PEM_write_bio_PrivateKey (svc));
|
|
+ ANNOUNCE_WRITE (SSCG_FILE_TYPE_SVC_KEY);
|
|
}
|
|
|
|
- cert_key_out = BIO_new_file (options->cert_key_file, create_mode);
|
|
- CHECK_BIO (cert_key_out, options->cert_key_file);
|
|
-
|
|
/* This function has a default mechanism for prompting for the
|
|
* password if it is passed a cipher and gets a NULL password.
|
|
*
|
|
@@ -909,7 +1165,7 @@ main (int argc, const char **argv)
|
|
* to prompt for one.
|
|
*/
|
|
sret = PEM_write_bio_PrivateKey (
|
|
- cert_key_out,
|
|
+ GET_BIO (SSCG_FILE_TYPE_SVC_KEY),
|
|
svc_key->evp_pkey,
|
|
options->cert_key_pass_prompt || options->cert_key_pass ? options->cipher :
|
|
NULL,
|
|
@@ -918,83 +1174,11 @@ main (int argc, const char **argv)
|
|
NULL,
|
|
NULL);
|
|
CHECK_SSL (sret, PEM_write_bio_PrivateKey (svc));
|
|
- BIO_get_fp (cert_key_out, &fp);
|
|
-
|
|
- if (options->verbosity >= SSCG_DEBUG)
|
|
- {
|
|
- fprintf (stdout,
|
|
- "DEBUG: Setting svc key file permissions to %o\n",
|
|
- cert_key_mode);
|
|
- }
|
|
- fchmod (fileno (fp), cert_key_mode);
|
|
-
|
|
- BIO_free (cert_key_out);
|
|
- cert_key_out = NULL;
|
|
-
|
|
-
|
|
- /* Create service public certificate */
|
|
- if (options->verbosity >= SSCG_DEFAULT)
|
|
- {
|
|
- fprintf (stdout,
|
|
- "Writing service public certificate to %s\n",
|
|
- options->cert_file);
|
|
- }
|
|
- if (strcmp (options->cert_key_file, options->cert_file) == 0)
|
|
- {
|
|
- cert_out = BIO_new_file (options->cert_file, "a");
|
|
- }
|
|
- else
|
|
- {
|
|
- cert_out = BIO_new_file (options->cert_file, create_mode);
|
|
- }
|
|
- CHECK_BIO (cert_out, options->cert_file);
|
|
-
|
|
- sret = PEM_write_bio_X509 (cert_out, svc_cert->certificate);
|
|
- CHECK_SSL (sret, PEM_write_bio_X509 (svc));
|
|
- BIO_get_fp (cert_out, &fp);
|
|
-
|
|
- /* If this file matches the keyfile, do not set its permissions */
|
|
- if (strcmp (options->cert_file, options->cert_key_file) == 0)
|
|
- {
|
|
- if (options->verbosity >= SSCG_DEBUG)
|
|
- {
|
|
- fprintf (stdout,
|
|
- "DEBUG: Not setting service cert file permissions: "
|
|
- "superseded by the key\n");
|
|
- }
|
|
- }
|
|
- else
|
|
- {
|
|
- if (options->verbosity >= SSCG_DEBUG)
|
|
- {
|
|
- fprintf (stdout,
|
|
- "DEBUG: Setting service cert file permissions to %o\n",
|
|
- cert_mode);
|
|
- }
|
|
- fchmod (fileno (fp), cert_mode);
|
|
- }
|
|
- BIO_free (cert_out);
|
|
- cert_out = NULL;
|
|
-
|
|
+ ANNOUNCE_WRITE (SSCG_FILE_TYPE_SVC_KEY);
|
|
|
|
/* Create CA private key, if requested */
|
|
- if (options->ca_key_file)
|
|
+ if (GET_BIO (SSCG_FILE_TYPE_CA_KEY))
|
|
{
|
|
- if (options->verbosity >= SSCG_DEFAULT)
|
|
- {
|
|
- fprintf (
|
|
- stdout, "Writing CA private key to %s\n", options->ca_key_file);
|
|
- }
|
|
- if (strcmp (options->ca_file, options->ca_key_file) == 0)
|
|
- {
|
|
- ca_key_out = BIO_new_file (options->ca_key_file, "a");
|
|
- }
|
|
- else
|
|
- {
|
|
- ca_key_out = BIO_new_file (options->ca_key_file, create_mode);
|
|
- }
|
|
- CHECK_BIO (ca_key_out, options->ca_key_file);
|
|
-
|
|
/* This function has a default mechanism for prompting for the
|
|
* password if it is passed a cipher and gets a NULL password.
|
|
*
|
|
@@ -1002,7 +1186,7 @@ main (int argc, const char **argv)
|
|
* to prompt for one.
|
|
*/
|
|
sret = PEM_write_bio_PrivateKey (
|
|
- ca_key_out,
|
|
+ GET_BIO (SSCG_FILE_TYPE_CA_KEY),
|
|
cakey->evp_pkey,
|
|
options->ca_key_pass_prompt || options->ca_key_pass ? options->cipher :
|
|
NULL,
|
|
@@ -1011,73 +1195,80 @@ main (int argc, const char **argv)
|
|
NULL,
|
|
NULL);
|
|
CHECK_SSL (sret, PEM_write_bio_PrivateKey (CA));
|
|
- BIO_get_fp (ca_key_out, &fp);
|
|
- if (options->verbosity >= SSCG_DEBUG)
|
|
- {
|
|
- fprintf (stdout,
|
|
- "DEBUG: Setting CA key file permissions to %o\n",
|
|
- ca_key_mode);
|
|
- }
|
|
- fchmod (fileno (fp), ca_key_mode);
|
|
- BIO_free (ca_key_out);
|
|
- ca_key_out = NULL;
|
|
+ ANNOUNCE_WRITE (SSCG_FILE_TYPE_CA_KEY);
|
|
}
|
|
|
|
+ /* Public keys come next, in chain order */
|
|
+
|
|
+ /* Start with the client certificate */
|
|
+ if (GET_BIO (SSCG_FILE_TYPE_CLIENT))
|
|
+ {
|
|
+ sret = PEM_write_bio_X509 (GET_BIO (SSCG_FILE_TYPE_CLIENT),
|
|
+ client_cert->certificate);
|
|
+ CHECK_SSL (sret, PEM_write_bio_X509 (client));
|
|
+ ANNOUNCE_WRITE (SSCG_FILE_TYPE_CLIENT);
|
|
+ }
|
|
+
|
|
+ /* Create service public certificate */
|
|
+ sret =
|
|
+ PEM_write_bio_X509 (GET_BIO (SSCG_FILE_TYPE_SVC), svc_cert->certificate);
|
|
+ CHECK_SSL (sret, PEM_write_bio_X509 (svc));
|
|
+ ANNOUNCE_WRITE (SSCG_FILE_TYPE_SVC);
|
|
+
|
|
|
|
/* Create CA public certificate */
|
|
- if (options->verbosity >= SSCG_DEFAULT)
|
|
- {
|
|
- fprintf (
|
|
- stdout, "Writing CA public certificate to %s\n", options->ca_file);
|
|
- }
|
|
- if (strcmp (options->ca_file, options->cert_file) == 0)
|
|
- {
|
|
- ca_out = BIO_new_file (options->ca_file, "a");
|
|
- }
|
|
- else
|
|
- {
|
|
- ca_out = BIO_new_file (options->ca_file, create_mode);
|
|
- }
|
|
- CHECK_BIO (ca_out, options->ca_file);
|
|
-
|
|
- sret = PEM_write_bio_X509 (ca_out, cacert->certificate);
|
|
+ struct sscg_stream *stream =
|
|
+ sscg_io_utils_get_stream_by_type (options->streams, SSCG_FILE_TYPE_CA);
|
|
+ sret = PEM_write_bio_X509 (stream->bio, cacert->certificate);
|
|
CHECK_SSL (sret, PEM_write_bio_X509 (CA));
|
|
- BIO_get_fp (ca_out, &fp);
|
|
- /* If this file matches the keyfile, do not set its permissions */
|
|
- if (options->ca_key_file &&
|
|
- strcmp (options->ca_file, options->ca_key_file) == 0)
|
|
+ ANNOUNCE_WRITE (SSCG_FILE_TYPE_CA);
|
|
+
|
|
+
|
|
+ /* Then write any non-certificate files */
|
|
+
|
|
+ /* Create CRL file */
|
|
+ if (GET_BIO (SSCG_FILE_TYPE_CRL))
|
|
{
|
|
- if (options->verbosity >= SSCG_DEBUG)
|
|
- {
|
|
- fprintf (
|
|
- stdout,
|
|
- "DEBUG: Not setting CA file permissions: superseded by a key\n");
|
|
- }
|
|
+ /* The CRL file is left intentionally blank, so do nothing here. The
|
|
+ * file was created as empty, so it will just be closed and have its
|
|
+ * permissions set later.
|
|
+ */
|
|
+ ANNOUNCE_WRITE (SSCG_FILE_TYPE_CRL);
|
|
}
|
|
- else
|
|
+
|
|
+
|
|
+ /* Create DH parameters file */
|
|
+ if (GET_BIO (SSCG_FILE_TYPE_DHPARAMS))
|
|
{
|
|
- if (options->verbosity >= SSCG_DEBUG)
|
|
- {
|
|
- fprintf (
|
|
- stdout, "DEBUG: Setting CA file permissions to %o\n", ca_mode);
|
|
- }
|
|
- fchmod (fileno (fp), ca_mode);
|
|
+ /* Open the file before generating the parameters. This avoids wasting
|
|
+ * the time to generate them if the destination is not writable.
|
|
+ */
|
|
+
|
|
+ ret = create_dhparams (main_ctx,
|
|
+ options->verbosity,
|
|
+ options->dhparams_prime_len,
|
|
+ options->dhparams_generator,
|
|
+ &dhparams);
|
|
+ CHECK_OK (ret);
|
|
+
|
|
+ /* Export the DH parameters to the file */
|
|
+ sret = PEM_write_bio_DHparams (GET_BIO (SSCG_FILE_TYPE_DHPARAMS),
|
|
+ dhparams->dh);
|
|
+ CHECK_SSL (sret, PEM_write_bio_DHparams ());
|
|
+ ANNOUNCE_WRITE (SSCG_FILE_TYPE_DHPARAMS);
|
|
}
|
|
- BIO_free (cert_out);
|
|
- cert_out = NULL;
|
|
|
|
|
|
+ /* Set the final file permissions */
|
|
+ sscg_io_utils_finalize_output_files (options->streams);
|
|
+
|
|
ret = EOK;
|
|
+
|
|
done:
|
|
- BIO_free (cert_key_out);
|
|
- BIO_free (cert_out);
|
|
- BIO_free (ca_key_out);
|
|
- BIO_free (ca_out);
|
|
-
|
|
talloc_zfree (main_ctx);
|
|
if (ret != EOK)
|
|
{
|
|
- fprintf (stderr, "%s\n", strerror (ret));
|
|
+ SSCG_ERROR ("%s\n", strerror (ret));
|
|
}
|
|
return ret;
|
|
}
|
|
diff --git a/test/dhparams_test.c b/test/dhparams_test.c
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..b054b40ea73ca98870836bd89ea10677be8fb075
|
|
--- /dev/null
|
|
+++ b/test/dhparams_test.c
|
|
@@ -0,0 +1,106 @@
|
|
+/*
|
|
+ This file is part of sscg.
|
|
+
|
|
+ sscg 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 3 of the License, or
|
|
+ (at your option) any later version.
|
|
+
|
|
+ sscg 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 sscg. If not, see <http://www.gnu.org/licenses/>.
|
|
+
|
|
+ Copyright 2019 by Stephen Gallagher <sgallagh@redhat.com>
|
|
+*/
|
|
+
|
|
+#include <errno.h>
|
|
+#include <stdio.h>
|
|
+#include <string.h>
|
|
+#include <talloc.h>
|
|
+#include <openssl/err.h>
|
|
+
|
|
+#include "include/dhparams.h"
|
|
+
|
|
+int
|
|
+main (int argc, char **argv)
|
|
+{
|
|
+ int ret, sret, prime_len, generator;
|
|
+ struct sscg_dhparams *params = NULL;
|
|
+ TALLOC_CTX *main_ctx = NULL;
|
|
+
|
|
+ if (getenv ("SSCG_SKIP_DHPARAMS"))
|
|
+ {
|
|
+ /* Skip this test */
|
|
+ return 77;
|
|
+ }
|
|
+
|
|
+ errno = 0;
|
|
+ prime_len = strtol (argv[1], NULL, 0);
|
|
+ if (errno)
|
|
+ {
|
|
+ fprintf (stderr, "Prime length was not a valid integer.");
|
|
+ ret = errno;
|
|
+ goto done;
|
|
+ }
|
|
+
|
|
+ errno = 0;
|
|
+ generator = strtol (argv[2], NULL, 0);
|
|
+ if (errno)
|
|
+ {
|
|
+ fprintf (stderr, "Generator was not a valid integer.");
|
|
+ ret = errno;
|
|
+ goto done;
|
|
+ }
|
|
+
|
|
+ main_ctx = talloc_new (NULL);
|
|
+
|
|
+ ret = create_dhparams (main_ctx, SSCG_DEBUG, prime_len, generator, ¶ms);
|
|
+ if (ret != EOK)
|
|
+ {
|
|
+ fprintf (stderr,
|
|
+ "Could not generate DH parameters: [%s]",
|
|
+ ERR_error_string (ERR_get_error (), NULL));
|
|
+ goto done;
|
|
+ }
|
|
+
|
|
+ if (!DH_check (params->dh, &sret))
|
|
+ {
|
|
+ ERR_print_errors_fp (stderr);
|
|
+ goto done;
|
|
+ }
|
|
+ if (sret & DH_CHECK_P_NOT_PRIME)
|
|
+ fprintf (stderr, "p value is not prime\n");
|
|
+ if (sret & DH_CHECK_P_NOT_SAFE_PRIME)
|
|
+ fprintf (stderr, "p value is not a safe prime\n");
|
|
+ if (sret & DH_CHECK_Q_NOT_PRIME)
|
|
+ fprintf (stderr, "q value is not a prime\n");
|
|
+ if (sret & DH_CHECK_INVALID_Q_VALUE)
|
|
+ fprintf (stderr, "q value is invalid\n");
|
|
+ if (sret & DH_CHECK_INVALID_J_VALUE)
|
|
+ fprintf (stderr, "j value is invalid\n");
|
|
+ if (sret & DH_UNABLE_TO_CHECK_GENERATOR)
|
|
+ fprintf (stderr, "unable to check the generator value\n");
|
|
+ if (sret & DH_NOT_SUITABLE_GENERATOR)
|
|
+ fprintf (stderr, "the g value is not a generator\n");
|
|
+
|
|
+ if (sret != 0)
|
|
+ {
|
|
+ /*
|
|
+ * We have generated parameters but DH_check() indicates they are
|
|
+ * invalid! This should never happen!
|
|
+ */
|
|
+ fprintf (stderr, "ERROR: Invalid parameters generated\n");
|
|
+ ret = EIO;
|
|
+ goto done;
|
|
+ }
|
|
+
|
|
+ ret = EOK;
|
|
+
|
|
+done:
|
|
+ talloc_free (main_ctx);
|
|
+ return ret;
|
|
+}
|
|
--
|
|
2.23.0
|
|
|