Blame SOURCES/0007-Add-support-for-client-certificates-and-dhparams.patch

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