summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDmitry Baryshkov <dbaryshkov@gmail.com>2020-05-16 19:19:22 +0300
committerDmitry Baryshkov <dmitry.baryshkov@linaro.org>2022-09-11 16:28:22 +0300
commit7668399ae5e01a3b5cd516cc69caa12fd13ec0f1 (patch)
treec3ebc41eae159fd585965e53267a34a894ce74b4
parent628831d636b800d97edec6ca66b31105ffeeccc9 (diff)
downloadgnutls-7668399ae5e01a3b5cd516cc69caa12fd13ec0f1.tar.gz
cmstool: add new tool targeting CMS files
Add new cmstool - a command line utility for handling CMS/PKCS#7 files. For now it had inherited pkcs7-related commands from certtol. Signed-off-by: Dmitry Baryshkov <dbaryshkov@gmail.com>
-rw-r--r--.gitignore5
-rw-r--r--doc/cmstool-examples.texi1
-rw-r--r--doc/cmstool-see-also.texi1
-rw-r--r--doc/manpages/Makefile.am11
-rw-r--r--src/Makefile.am21
-rw-r--r--src/certtool.c397
-rw-r--r--src/cmstool-common.c512
-rw-r--r--src/cmstool-common.h35
-rw-r--r--src/cmstool-options.json226
-rw-r--r--src/cmstool.c292
10 files changed, 1107 insertions, 394 deletions
diff --git a/.gitignore b/.gitignore
index 821fc85f1d..518307f798 100644
--- a/.gitignore
+++ b/.gitignore
@@ -121,6 +121,7 @@ doc/latex/gnutls.lot
doc/Makefile
doc/Makefile.in
doc/manpages/certtool.1
+doc/manpages/cmstool.1
doc/manpages/dane_*.3
doc/manpages/danetool.1
doc/manpages/gnutls_*.3
@@ -280,6 +281,9 @@ src/certtool-options.c
src/certtool-options.h
src/cli-cfg.c
src/cli-cfg.h
+src/cmstool
+src/cmstool-options.c
+src/cmstool-options.h
src/dumpcfg
src/gnutls-cli-options.c
src/gnutls-cli-options.h
@@ -300,6 +304,7 @@ src/libcerttool-cfg.la
src/libcmd-certtool.la
src/libcmd-cli-debug.la
src/libcmd-cli.la
+src/libcmd-cmstool.la
src/libcmd-danetool.la
src/libcmd-ocsp.la
src/libcmd-p11tool.la
diff --git a/doc/cmstool-examples.texi b/doc/cmstool-examples.texi
new file mode 100644
index 0000000000..1333ed77b7
--- /dev/null
+++ b/doc/cmstool-examples.texi
@@ -0,0 +1 @@
+TODO
diff --git a/doc/cmstool-see-also.texi b/doc/cmstool-see-also.texi
new file mode 100644
index 0000000000..6647d5ae32
--- /dev/null
+++ b/doc/cmstool-see-also.texi
@@ -0,0 +1 @@
+ certtool (1)
diff --git a/doc/manpages/Makefile.am b/doc/manpages/Makefile.am
index c28abf52e3..cddf789ec6 100644
--- a/doc/manpages/Makefile.am
+++ b/doc/manpages/Makefile.am
@@ -26,7 +26,7 @@ MAINTAINERCLEANFILES = stamp_mans
-include $(top_srcdir)/doc/doc.mk
TOOLS_MANS = gnutls-cli.1 gnutls-cli-debug.1 gnutls-serv.1 \
- certtool.1 psktool.1 p11tool.1 ocsptool.1 tpmtool.1
+ certtool.1 cmstool.1 psktool.1 p11tool.1 ocsptool.1 tpmtool.1
SRP_MANS = srptool.1
DANE_MANS = danetool.1
@@ -64,6 +64,15 @@ certtool.1: $(top_srcdir)/src/certtool-options.json
--include files=$(top_srcdir)/doc/certtool-files.texi \
$< $@
+cmstool.1: $(top_srcdir)/doc/cmstool-see-also.texi $(top_srcdir)/doc/cmstool-examples.texi
+cmstool.1: $(top_srcdir)/src/cmstool-options.json
+ $(AM_V_GEN) PYTHONPATH='$(top_srcdir)/python' \
+ $(PYTHON) $(top_srcdir)/cligen/cli-docgen.py \
+ --format man $(CLIGEN_ARGS) \
+ --include see-also=$(top_srcdir)/doc/cmstool-see-also.texi \
+ --include examples=$(top_srcdir)/doc/cmstool-examples.texi \
+ $< $@
+
ocsptool.1: $(top_srcdir)/doc/ocsptool-see-also.texi $(top_srcdir)/doc/ocsptool-examples.texi $(top_srcdir)/doc/ocsptool-description.texi
ocsptool.1: $(top_srcdir)/src/ocsptool-options.json
$(AM_V_GEN) PYTHONPATH='$(top_srcdir)/cligen' \
diff --git a/src/Makefile.am b/src/Makefile.am
index 3080f90cd6..5dc33c1486 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -25,6 +25,7 @@ OPTIONS_BUILT = \
gnutls-cli-options.c gnutls-cli-options.h \
gnutls-cli-debug-options.c gnutls-cli-debug-options.h \
certtool-options.c certtool-options.h \
+ cmstool-options.c cmstool-options.h \
danetool-options.c danetool-options.h \
p11tool-options.c p11tool-options.h \
tpmtool-options.c tpmtool-options.h \
@@ -33,12 +34,12 @@ OPTIONS_BUILT = \
OPTIONS_JSON = \
certtool-options.json gnutls-cli-debug-options.json gnutls-cli-options.json tpmtool-options.json \
systemkey-tool-options.json srptool-options.json ocsptool-options.json p11tool-options.json \
- danetool-options.json gnutls-serv-options.json psktool-options.json
+ danetool-options.json gnutls-serv-options.json psktool-options.json cmstool-options.json
OPTIONS_STAMP = \
certtool-options.stamp gnutls-cli-debug-options.stamp gnutls-cli-options.stamp tpmtool-options.stamp \
systemkey-tool-options.stamp srptool-options.stamp ocsptool-options.stamp p11tool-options.stamp \
- danetool-options.stamp gnutls-serv-options.stamp psktool-options.stamp
+ danetool-options.stamp gnutls-serv-options.stamp psktool-options.stamp cmstool-options.stamp
BUILT_SOURCES = $(OPTIONS_BUILT)
@@ -55,7 +56,7 @@ AM_CPPFLAGS = \
-I$(srcdir)/../libdane/includes \
-I$(srcdir)/../extra/includes
-bin_PROGRAMS = psktool gnutls-cli-debug certtool gnutls-serv gnutls-cli
+bin_PROGRAMS = psktool gnutls-cli-debug certtool cmstool gnutls-serv gnutls-cli
if ENABLE_SRP
bin_PROGRAMS += srptool
endif
@@ -157,6 +158,7 @@ endif
certtool_SOURCES = certtool.c certtool-common.c certtool-extras.c common.c
+certtool_SOURCES += cmstool-common.h cmstool-common.c
certtool_LDADD = ../lib/libgnutls.la
certtool_LDADD += libcmd-certtool.la ../gl/libgnu.la gl/libgnu_gpl.la
@@ -167,6 +169,17 @@ libcmd_certtool_la_LIBADD += $(COMMON_LIBS)
libcmd_certtool_la_LIBADD += $(LTLIBREADLINE) gl/libgnu_gpl.la
libcmd_certtool_la_LIBADD += $(INET_PTON_LIB) $(LIB_CLOCK_GETTIME)
+cmstool_SOURCES = cmstool.c certtool-common.c common.c
+cmstool_SOURCES += cmstool-common.h cmstool-common.c
+cmstool_LDADD = ../lib/libgnutls.la
+cmstool_LDADD += libcmd-cmstool.la ../gl/libgnu.la gl/libgnu_gpl.la
+
+noinst_LTLIBRARIES += libcmd-cmstool.la
+libcmd_cmstool_la_SOURCES = cmstool-options.c cmstool-options.h
+libcmd_cmstool_la_LIBADD = libcerttool-cfg.la ../lib/libgnutls.la gl/libgnu_gpl.la ../gl/libgnu.la
+libcmd_cmstool_la_LIBADD += $(COMMON_LIBS)
+libcmd_cmstool_la_LIBADD += $(LTLIBREADLINE) gl/libgnu_gpl.la
+libcmd_cmstool_la_LIBADD += $(INET_PTON_LIB) $(LIB_CLOCK_GETTIME)
danetool_SOURCES = danetool.c certtool-common.c certtool-extras.c common.c socket.c
danetool_LDADD = ../lib/libgnutls.la $(LIBIDN_LIBS)
@@ -268,6 +281,7 @@ gnutls-cli-options.stamp: gnutls-cli-options.json
gnutls-serv-options.stamp: gnutls-serv-options.json
srptool-options.stamp: srptool-options.json
certtool-options.stamp: certtool-options.json
+cmstool-options.stamp: cmstool-options.json
systemkey-tool-options.stamp: systemkey-tool-options.json
danetool-options.c danetool-options.h: danetool-options.stamp
@@ -280,6 +294,7 @@ gnutls-cli-options.c gnutls-cli-options.h: gnutls-cli-options.stamp
gnutls-serv-options.c gnutls-serv-options.h: gnutls-serv-options.stamp
srptool-options.c srptool-options.h: srptool-options.stamp
certtool-options.c certtool-options.h: certtool-options.stamp
+cmstool-options.c cmstool-options.h: cmstool-options.stamp
systemkey-tool-options.c systemkey-tool-options.h: systemkey-tool-options.stamp
mech-list.h: gen-mech-list.sh
diff --git a/src/certtool.c b/src/certtool.c
index 71d4aff13e..cb5ce1c200 100644
--- a/src/certtool.c
+++ b/src/certtool.c
@@ -52,20 +52,18 @@
#include <common.h>
#include "certtool-options.h"
#include "certtool-common.h"
+#include "cmstool-common.h"
#define MAX_HASH_SIZE 64
static FILE *stdlog = NULL;
static void print_crl_info(gnutls_x509_crl_t crl, FILE * out, common_info_st *cinfo);
-void pkcs7_info(common_info_st *cinfo, unsigned display_data);
-void pkcs7_sign(common_info_st *, unsigned embed);
-void pkcs7_generate(common_info_st *);
+static void pkcs7_sign(common_info_st *, unsigned embed);
void pkcs8_info(void);
void pkcs8_info_int(gnutls_datum_t *data, unsigned format,
unsigned ignore_err, FILE *out, const char *tab);
void crq_info(common_info_st *cinfo);
-void smime_to_pkcs7(void);
void pkcs12_info(common_info_st *);
void generate_pkcs12(common_info_st *);
void generate_pkcs8(common_info_st *);
@@ -2244,27 +2242,6 @@ static int detailed_verification(gnutls_x509_crt_t cert,
return 0;
}
-static void load_data(common_info_st *cinfo, gnutls_datum_t *data)
-{
- FILE *fp;
- size_t size;
-
- fp = fopen(cinfo->data_file, "r");
- if (fp == NULL) {
- fprintf(stderr, "Could not open %s\n", cinfo->data_file);
- app_exit(1);
- }
-
- data->data = (void *) fread_file(fp, 0, &size);
- if (data->data == NULL) {
- fprintf(stderr, "Error reading data file");
- app_exit(1);
- }
-
- data->size = size;
- fclose(fp);
-}
-
static gnutls_x509_trust_list_t load_tl(common_info_st * cinfo)
{
gnutls_x509_trust_list_t list;
@@ -2638,154 +2615,19 @@ void verify_crl(common_info_st * cinfo)
app_exit(rc);
}
-static void print_pkcs7_sig_info(gnutls_pkcs7_signature_info_st *info, common_info_st *cinfo)
-{
- int ret;
- gnutls_datum_t str;
-
- ret = gnutls_pkcs7_print_signature_info(info, GNUTLS_CRT_PRINT_COMPACT, &str);
- if (ret < 0) {
- fprintf(stderr, "printing error: %s\n",
- gnutls_strerror(ret));
- app_exit(1);
- }
-
- fprintf(outfile, "%s", str.data);
- gnutls_free(str.data);
-}
-
void verify_pkcs7(common_info_st * cinfo, const char *purpose, unsigned display_data)
{
- gnutls_pkcs7_t pkcs7;
- int ret, ecode;
- size_t size;
- gnutls_datum_t data, detached = {NULL,0};
- gnutls_datum_t tmp = {NULL,0};
- int i;
- gnutls_pkcs7_signature_info_st info;
- gnutls_x509_trust_list_t tl = NULL;
- gnutls_typed_vdata_st vdata[2];
- unsigned vdata_size = 0;
- gnutls_x509_crt_t signer = NULL;
unsigned flags = 0;
- ret = gnutls_pkcs7_init(&pkcs7);
- if (ret < 0) {
- fprintf(stderr, "p7_init: %s\n", gnutls_strerror(ret));
- app_exit(1);
- }
-
- data.data = (void *) fread_file(infile, 0, &size);
- data.size = size;
-
- if (!data.data) {
- fprintf(stderr, "%s", infile ? "file" : "standard input");
- app_exit(1);
- }
-
- ret = gnutls_pkcs7_import(pkcs7, &data, cinfo->incert_format);
- free(data.data);
- if (ret < 0) {
- fprintf(stderr, "import error: %s\n",
- gnutls_strerror(ret));
- app_exit(1);
- }
-
- if (cinfo->cert != NULL) {
- signer = load_cert(1, cinfo);
- } else { /* trust list */
- tl = load_tl(cinfo);
- if (tl == NULL) {
- fprintf(stderr, "error loading trust list\n");
- }
- }
-
- if (cinfo->data_file)
- load_data(cinfo, &detached);
-
- if (purpose) {
- vdata[vdata_size].type = GNUTLS_DT_KEY_PURPOSE_OID;
- vdata[vdata_size].data = (void*)purpose;
- vdata[vdata_size].size = strlen(purpose);
- vdata_size++;
- }
-
- ecode = 1;
- for (i=0;;i++) {
- ret = gnutls_pkcs7_get_signature_info(pkcs7, i, &info);
- if (ret < 0)
- break;
-
- if (!display_data) {
- if (i==0) {
- fprintf(outfile, "eContent Type: %s\n", gnutls_pkcs7_get_embedded_data_oid(pkcs7));
- fprintf(outfile, "Signers:\n");
- }
- print_pkcs7_sig_info(&info, cinfo);
- } else if (i == 0) {
- if (!detached.data) {
- ret = gnutls_pkcs7_get_embedded_data(pkcs7, 0, &tmp);
- if (ret < 0) {
- fprintf(stderr, "error getting embedded data: %s\n", gnutls_strerror(ret));
- app_exit(1);
- }
-
- fwrite(tmp.data, 1, tmp.size, outfile);
- gnutls_free(tmp.data);
- tmp.data = NULL;
- } else {
- fwrite(detached.data, 1, detached.size, outfile);
- }
- }
-
- gnutls_pkcs7_signature_info_deinit(&info);
-
- if (HAVE_OPT(VERIFY_ALLOW_BROKEN))
- flags |= GNUTLS_VERIFY_ALLOW_BROKEN;
-
- if (signer) {
- ret = gnutls_pkcs7_verify_direct(pkcs7, signer, i, detached.data!=NULL?&detached:NULL, flags);
-
- if (ret >= 0 && purpose) {
- unsigned res = gnutls_x509_crt_check_key_purpose(signer, purpose, 0);
- if (res == 0)
- ret = GNUTLS_E_CONSTRAINT_ERROR;
- }
-
- } else {
- assert(tl != NULL);
- ret = gnutls_pkcs7_verify(pkcs7, tl, vdata, vdata_size, i, detached.data!=NULL?&detached:NULL, flags);
- }
- if (ret < 0) {
- fprintf(stderr, "\tSignature status: verification failed: %s\n", gnutls_strerror(ret));
- ecode = 1;
- } else {
- fprintf(stderr, "\tSignature status: ok\n");
- ecode = 0;
- }
- }
-
+ if (HAVE_OPT(VERIFY_ALLOW_BROKEN))
+ flags |= GNUTLS_VERIFY_ALLOW_BROKEN;
- gnutls_pkcs7_deinit(pkcs7);
- if (signer)
- gnutls_x509_crt_deinit(signer);
- else
- gnutls_x509_trust_list_deinit(tl, 1);
- free(detached.data);
- app_exit(ecode);
+ return pkcs7_verify_common(cinfo, purpose, display_data, flags);
}
-void pkcs7_sign(common_info_st * cinfo, unsigned embed)
+static void pkcs7_sign(common_info_st * cinfo, unsigned embed)
{
- gnutls_pkcs7_t pkcs7;
- gnutls_privkey_t key;
- int ret;
- size_t size;
- gnutls_datum_t data;
unsigned flags = 0;
- gnutls_x509_crt_t *crts;
- size_t crt_size;
- size_t i;
if (ENABLED_OPT(P7_TIME))
flags |= GNUTLS_PKCS7_INCLUDE_TIME;
@@ -2793,114 +2635,9 @@ void pkcs7_sign(common_info_st * cinfo, unsigned embed)
if (ENABLED_OPT(P7_INCLUDE_CERT))
flags |= GNUTLS_PKCS7_INCLUDE_CERT;
- ret = gnutls_pkcs7_init(&pkcs7);
- if (ret < 0) {
- fprintf(stderr, "p7_init: %s\n", gnutls_strerror(ret));
- app_exit(1);
- }
-
- data.data = (void *) fread_file(infile, 0, &size);
- data.size = size;
-
- if (!data.data) {
- fprintf(stderr, "%s", infile ? "file" : "standard input");
- app_exit(1);
- }
-
- crts = load_cert_list(1, &crt_size, cinfo);
- key = load_private_key(1, cinfo);
-
- if (embed)
- flags |= GNUTLS_PKCS7_EMBED_DATA;
-
- ret = gnutls_pkcs7_sign(pkcs7, *crts, key, &data, NULL, NULL, get_dig(*crts, cinfo), flags);
- if (ret < 0) {
- fprintf(stderr, "Error signing: %s\n", gnutls_strerror(ret));
- app_exit(1);
- }
-
- for (i=1;i<crt_size;i++) {
- ret = gnutls_pkcs7_set_crt(pkcs7, crts[i]);
- if (ret < 0) {
- fprintf(stderr, "Error adding cert: %s\n", gnutls_strerror(ret));
- exit(1);
- }
- }
-
-
- size = lbuffer_size;
- ret =
- gnutls_pkcs7_export(pkcs7, outcert_format, lbuffer, &size);
- if (ret < 0) {
- fprintf(stderr, "pkcs7_export: %s\n", gnutls_strerror(ret));
- app_exit(1);
- }
-
- fwrite(lbuffer, 1, size, outfile);
-
- gnutls_privkey_deinit(key);
- for (i=0;i<crt_size;i++) {
- gnutls_x509_crt_deinit(crts[i]);
- }
- gnutls_free(crts);
- gnutls_pkcs7_deinit(pkcs7);
- app_exit(0);
-}
-
-void pkcs7_generate(common_info_st * cinfo)
-{
- gnutls_pkcs7_t pkcs7;
- int ret;
- size_t crl_size = 0, crt_size = 0;
- gnutls_x509_crt_t *crts;
- gnutls_x509_crl_t *crls;
- gnutls_datum_t tmp;
- unsigned i;
-
- crts = load_cert_list(1, &crt_size, cinfo);
- crls = load_crl_list(0, &crl_size, cinfo);
-
- ret = gnutls_pkcs7_init(&pkcs7);
- if (ret < 0) {
- fprintf(stderr, "p7_init: %s\n", gnutls_strerror(ret));
- app_exit(1);
- }
-
- for (i=0;i<crt_size;i++) {
- ret = gnutls_pkcs7_set_crt(pkcs7, crts[i]);
- if (ret < 0) {
- fprintf(stderr, "Error adding cert: %s\n", gnutls_strerror(ret));
- app_exit(1);
- }
- gnutls_x509_crt_deinit(crts[i]);
- }
- gnutls_free(crts);
-
- for (i=0;i<crl_size;i++) {
- ret = gnutls_pkcs7_set_crl(pkcs7, crls[i]);
- if (ret < 0) {
- fprintf(stderr, "Error adding CRL: %s\n", gnutls_strerror(ret));
- app_exit(1);
- }
- gnutls_x509_crl_deinit(crls[i]);
- }
- gnutls_free(crls);
-
- ret =
- gnutls_pkcs7_export2(pkcs7, outcert_format, &tmp);
- if (ret < 0) {
- fprintf(stderr, "pkcs7_export: %s\n", gnutls_strerror(ret));
- app_exit(1);
- }
-
- fwrite(tmp.data, 1, tmp.size, outfile);
- gnutls_free(tmp.data);
-
- gnutls_pkcs7_deinit(pkcs7);
- app_exit(0);
+ return pkcs7_sign_common(cinfo, embed, flags);
}
-
void generate_pkcs8(common_info_st * cinfo)
{
gnutls_x509_privkey_t key;
@@ -3620,126 +3357,6 @@ void pkcs8_info(void)
free(data.data);
}
-void pkcs7_info(common_info_st *cinfo, unsigned display_data)
-{
- gnutls_pkcs7_t pkcs7;
- int ret;
- size_t size;
- gnutls_datum_t data, str;
-
- ret = gnutls_pkcs7_init(&pkcs7);
- if (ret < 0) {
- fprintf(stderr, "p7_init: %s\n", gnutls_strerror(ret));
- app_exit(1);
- }
-
- data.data = (void *) fread_file(infile, 0, &size);
- data.size = size;
-
- if (!data.data) {
- fprintf(stderr, "%s", infile ? "file" : "standard input");
- app_exit(1);
- }
-
- ret = gnutls_pkcs7_import(pkcs7, &data, incert_format);
- free(data.data);
- if (ret < 0) {
- fprintf(stderr, "import error: %s\n",
- gnutls_strerror(ret));
- app_exit(1);
- }
-
- if (display_data) {
- gnutls_datum_t tmp;
-
- ret = gnutls_pkcs7_get_embedded_data(pkcs7, 0, &tmp);
- if (ret != GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) {
- if (ret < 0) {
- fprintf(stderr, "error getting embedded data: %s\n", gnutls_strerror(ret));
- app_exit(1);
- }
-
- fwrite(tmp.data, 1, tmp.size, outfile);
- gnutls_free(tmp.data);
- } else {
- fprintf(stderr, "no embedded data are available\n");
- app_exit(1);
- }
- } else {
- if (cinfo->outtext) {
- ret = gnutls_pkcs7_print(pkcs7, GNUTLS_CRT_PRINT_FULL, &str);
- if (ret < 0) {
- fprintf(stderr, "printing error: %s\n",
- gnutls_strerror(ret));
- app_exit(1);
- }
-
- fprintf(outfile, "%s", str.data);
- gnutls_free(str.data);
- }
-
- size = lbuffer_size;
- ret =
- gnutls_pkcs7_export(pkcs7, outcert_format,
- lbuffer, &size);
- if (ret < 0) {
- fprintf(stderr, "export error: %s\n",
- gnutls_strerror(ret));
- app_exit(1);
- }
-
- fwrite(lbuffer, 1, size, outfile);
- }
-
- gnutls_pkcs7_deinit(pkcs7);
-}
-
-void smime_to_pkcs7(void)
-{
- size_t linesize = 0;
- char *lineptr = NULL;
- ssize_t len;
-
- /* Find body. We do not handle non-b64 Content-Transfer-Encoding. */
- do {
- len = getline(&lineptr, &linesize, infile);
- if (len == -1) {
- fprintf(stderr,
- "cannot find RFC 2822 header/body separator");
- app_exit(1);
- }
- }
- while (strcmp(lineptr, "\r\n") != 0 && strcmp(lineptr, "\n") != 0);
-
- /* skip newlines */
- do {
- len = getline(&lineptr, &linesize, infile);
- if (len == -1) {
- fprintf(stderr,
- "message has RFC 2822 header but no body");
- app_exit(1);
- }
- }
- while (strcmp(lineptr, "\r\n") == 0 || strcmp(lineptr, "\n") == 0);
-
- fprintf(outfile, "%s", "-----BEGIN PKCS7-----\n");
-
- do {
- while (len > 0
- && (lineptr[len - 1] == '\r'
- || lineptr[len - 1] == '\n'))
- lineptr[--len] = '\0';
- if (strcmp(lineptr, "") != 0)
- fprintf(outfile, "%s\n", lineptr);
- len = getline(&lineptr, &linesize, infile);
- }
- while (len != -1);
-
- fprintf(outfile, "%s", "-----END PKCS7-----\n");
-
- free(lineptr);
-}
-
/* Tries to find a public key in the provided options or stdin
* When @crt is provided, it will be deinitialized.
*/
diff --git a/src/cmstool-common.c b/src/cmstool-common.c
new file mode 100644
index 0000000000..22aaf6235d
--- /dev/null
+++ b/src/cmstool-common.c
@@ -0,0 +1,512 @@
+/*
+ * Copyright (C) 2015-2019 Red Hat, Inc.
+ * Copyright (C) 2020 Dmitry Baryshkov
+ *
+ * This file is part of GnuTLS.
+ *
+ * GnuTLS is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuTLS is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+
+#include <gnutls/gnutls.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+
+/* Gnulib portability files. */
+#include <read-file.h>
+
+#include <cmstool-common.h>
+
+static gnutls_digest_algorithm_t get_dig_for_pub(gnutls_pubkey_t pubkey, common_info_st * cinfo)
+{
+ gnutls_digest_algorithm_t dig;
+ int result;
+ unsigned int mand;
+
+ result =
+ gnutls_pubkey_get_preferred_hash_algorithm(pubkey, &dig,
+ &mand);
+ if (result < 0) {
+ {
+ fprintf(stderr,
+ "crt_get_preferred_hash_algorithm: %s\n",
+ gnutls_strerror(result));
+ app_exit(1);
+ }
+ }
+
+ /* if algorithm allows alternatives */
+ if (mand == 0 && cinfo->hash != GNUTLS_DIG_UNKNOWN)
+ dig = cinfo->hash;
+
+ return dig;
+}
+
+static gnutls_digest_algorithm_t get_dig(gnutls_x509_crt_t crt, common_info_st * cinfo)
+{
+ gnutls_digest_algorithm_t dig;
+ gnutls_pubkey_t pubkey;
+ int result;
+
+ result = gnutls_pubkey_init(&pubkey);
+ if (result < 0) {
+ fprintf(stderr, "memory error\n");
+ app_exit(1);
+ }
+
+ result = gnutls_pubkey_import_x509(pubkey, crt, 0);
+ if (result < 0) {
+ {
+ fprintf(stderr, "gnutls_pubkey_import_x509: %s\n",
+ gnutls_strerror(result));
+ app_exit(1);
+ }
+ }
+
+ dig = get_dig_for_pub(pubkey, cinfo);
+
+ gnutls_pubkey_deinit(pubkey);
+
+ return dig;
+}
+
+static void load_data(common_info_st *cinfo, gnutls_datum_t *data)
+{
+ FILE *fp;
+ size_t size;
+
+ fp = fopen(cinfo->data_file, "r");
+ if (fp == NULL) {
+ fprintf(stderr, "Could not open %s\n", cinfo->data_file);
+ app_exit(1);
+ }
+
+ data->data = (void *) fread_file(fp, 0, &size);
+ if (data->data == NULL) {
+ fprintf(stderr, "Error reading data file");
+ app_exit(1);
+ }
+
+ data->size = size;
+ fclose(fp);
+}
+
+static gnutls_x509_trust_list_t load_tl(common_info_st * cinfo)
+{
+ gnutls_x509_trust_list_t list;
+ int ret;
+
+ ret = gnutls_x509_trust_list_init(&list, 0);
+ if (ret < 0) {
+ fprintf(stderr, "gnutls_x509_trust_list_init: %s\n",
+ gnutls_strerror(ret));
+ app_exit(1);
+ }
+
+ if (cinfo->ca == NULL) { /* system */
+ ret = gnutls_x509_trust_list_add_system_trust(list, 0, 0);
+ if (ret < 0) {
+ fprintf(stderr, "Error loading system trust: %s\n",
+ gnutls_strerror(ret));
+ app_exit(1);
+ }
+ fprintf(stderr, "Loaded system trust (%d CAs available)\n", ret);
+ } else if (cinfo->ca != NULL) {
+ ret = gnutls_x509_trust_list_add_trust_file(list, cinfo->ca, cinfo->crl, cinfo->incert_format, 0, 0);
+ if (ret < 0) {
+ int ret2 = gnutls_x509_trust_list_add_trust_file(list, cinfo->ca, cinfo->crl, GNUTLS_X509_FMT_PEM, 0, 0);
+ if (ret2 >= 0)
+ ret = ret2;
+ }
+
+ if (ret < 0) {
+ fprintf(stderr, "gnutls_x509_trust_add_trust_file: %s\n",
+ gnutls_strerror(ret));
+ app_exit(1);
+ }
+
+ fprintf(stderr, "Loaded CAs (%d available)\n", ret);
+ }
+
+ return list;
+}
+
+void pkcs7_verify_common(common_info_st * cinfo, const char *purpose, unsigned display_data, gnutls_certificate_verify_flags flags)
+{
+ gnutls_pkcs7_t pkcs7;
+ int ret, ecode;
+ size_t size;
+ gnutls_datum_t data, detached = {NULL,0};
+ gnutls_datum_t tmp = {NULL,0};
+ int i;
+ gnutls_pkcs7_signature_info_st info;
+ gnutls_x509_trust_list_t tl = NULL;
+ gnutls_typed_vdata_st vdata[2];
+ unsigned vdata_size = 0;
+ gnutls_x509_crt_t signer = NULL;
+
+ ret = gnutls_pkcs7_init(&pkcs7);
+ if (ret < 0) {
+ fprintf(stderr, "p7_init: %s\n", gnutls_strerror(ret));
+ app_exit(1);
+ }
+
+ data.data = (void *) fread_file(infile, 0, &size);
+ data.size = size;
+
+ if (!data.data) {
+ fprintf(stderr, "%s", infile ? "file" : "standard input");
+ app_exit(1);
+ }
+
+ ret = gnutls_pkcs7_import(pkcs7, &data, cinfo->incert_format);
+ free(data.data);
+ if (ret < 0) {
+ fprintf(stderr, "import error: %s\n",
+ gnutls_strerror(ret));
+ app_exit(1);
+ }
+
+ if (cinfo->cert != NULL) {
+ signer = load_cert(1, cinfo);
+ } else { /* trust list */
+ tl = load_tl(cinfo);
+ if (tl == NULL) {
+ fprintf(stderr, "error loading trust list\n");
+ }
+ }
+
+ if (cinfo->data_file)
+ load_data(cinfo, &detached);
+
+ if (purpose) {
+ vdata[vdata_size].type = GNUTLS_DT_KEY_PURPOSE_OID;
+ vdata[vdata_size].data = (void*)purpose;
+ vdata[vdata_size].size = strlen(purpose);
+ vdata_size++;
+ }
+
+ ecode = 1;
+ for (i=0;;i++) {
+ ret = gnutls_pkcs7_get_signature_info(pkcs7, i, &info);
+ if (ret < 0)
+ break;
+
+ if (!display_data) {
+ if (i==0) {
+ fprintf(outfile, "eContent Type: %s\n", gnutls_pkcs7_get_embedded_data_oid(pkcs7));
+ fprintf(outfile, "Signers:\n");
+ }
+
+ ret = gnutls_pkcs7_print_signature_info(&info, GNUTLS_CRT_PRINT_COMPACT, &tmp);
+ if (ret < 0) {
+ fprintf(stderr, "printing error: %s\n",
+ gnutls_strerror(ret));
+ app_exit(1);
+ }
+
+ fprintf(outfile, "%s", tmp.data);
+ gnutls_free(tmp.data);
+ } else if (i == 0) {
+ if (!detached.data) {
+ ret = gnutls_pkcs7_get_embedded_data(pkcs7, 0, &tmp);
+ if (ret < 0) {
+ fprintf(stderr, "error getting embedded data: %s\n", gnutls_strerror(ret));
+ app_exit(1);
+ }
+
+ fwrite(tmp.data, 1, tmp.size, outfile);
+ gnutls_free(tmp.data);
+ tmp.data = NULL;
+ } else {
+ fwrite(detached.data, 1, detached.size, outfile);
+ }
+ }
+
+ gnutls_pkcs7_signature_info_deinit(&info);
+
+ if (signer) {
+ ret = gnutls_pkcs7_verify_direct(pkcs7, signer, i, detached.data!=NULL?&detached:NULL, flags);
+
+ if (ret >= 0 && purpose) {
+ unsigned res = gnutls_x509_crt_check_key_purpose(signer, purpose, 0);
+ if (res == 0)
+ ret = GNUTLS_E_CONSTRAINT_ERROR;
+ }
+
+ } else {
+ assert(tl != NULL);
+ ret = gnutls_pkcs7_verify(pkcs7, tl, vdata, vdata_size, i, detached.data!=NULL?&detached:NULL, flags);
+ }
+ if (ret < 0) {
+ fprintf(stderr, "\tSignature status: verification failed: %s\n", gnutls_strerror(ret));
+ ecode = 1;
+ } else {
+ fprintf(stderr, "\tSignature status: ok\n");
+ ecode = 0;
+ }
+ }
+
+
+ gnutls_pkcs7_deinit(pkcs7);
+ if (signer)
+ gnutls_x509_crt_deinit(signer);
+ else
+ gnutls_x509_trust_list_deinit(tl, 1);
+ free(detached.data);
+ app_exit(ecode);
+}
+
+void pkcs7_sign_common(common_info_st * cinfo, unsigned embed, gnutls_pkcs7_sign_flags flags)
+{
+ gnutls_pkcs7_t pkcs7;
+ gnutls_privkey_t key;
+ int ret;
+ size_t size;
+ gnutls_datum_t data;
+ gnutls_x509_crt_t *crts;
+ size_t crt_size;
+ size_t i;
+
+ ret = gnutls_pkcs7_init(&pkcs7);
+ if (ret < 0) {
+ fprintf(stderr, "p7_init: %s\n", gnutls_strerror(ret));
+ app_exit(1);
+ }
+
+ data.data = (void *) fread_file(infile, 0, &size);
+ data.size = size;
+
+ if (!data.data) {
+ fprintf(stderr, "%s", infile ? "file" : "standard input");
+ app_exit(1);
+ }
+
+ crts = load_cert_list(1, &crt_size, cinfo);
+ key = load_private_key(1, cinfo);
+
+ if (embed)
+ flags |= GNUTLS_PKCS7_EMBED_DATA;
+
+ ret = gnutls_pkcs7_sign(pkcs7, *crts, key, &data, NULL, NULL, get_dig(*crts, cinfo), flags);
+ if (ret < 0) {
+ fprintf(stderr, "Error signing: %s\n", gnutls_strerror(ret));
+ app_exit(1);
+ }
+
+ for (i=1;i<crt_size;i++) {
+ ret = gnutls_pkcs7_set_crt(pkcs7, crts[i]);
+ if (ret < 0) {
+ fprintf(stderr, "Error adding cert: %s\n", gnutls_strerror(ret));
+ exit(1);
+ }
+ }
+
+
+ size = lbuffer_size;
+ ret =
+ gnutls_pkcs7_export(pkcs7, cinfo->outcert_format, lbuffer, &size);
+ if (ret < 0) {
+ fprintf(stderr, "pkcs7_export: %s\n", gnutls_strerror(ret));
+ app_exit(1);
+ }
+
+ fwrite(lbuffer, 1, size, outfile);
+
+ gnutls_privkey_deinit(key);
+ for (i=0;i<crt_size;i++) {
+ gnutls_x509_crt_deinit(crts[i]);
+ }
+ gnutls_free(crts);
+ gnutls_pkcs7_deinit(pkcs7);
+ app_exit(0);
+}
+
+void pkcs7_generate(common_info_st * cinfo)
+{
+ gnutls_pkcs7_t pkcs7;
+ int ret;
+ size_t crl_size = 0, crt_size = 0;
+ gnutls_x509_crt_t *crts;
+ gnutls_x509_crl_t *crls;
+ gnutls_datum_t tmp;
+ unsigned i;
+
+ crts = load_cert_list(1, &crt_size, cinfo);
+ crls = load_crl_list(0, &crl_size, cinfo);
+
+ ret = gnutls_pkcs7_init(&pkcs7);
+ if (ret < 0) {
+ fprintf(stderr, "p7_init: %s\n", gnutls_strerror(ret));
+ app_exit(1);
+ }
+
+ for (i=0;i<crt_size;i++) {
+ ret = gnutls_pkcs7_set_crt(pkcs7, crts[i]);
+ if (ret < 0) {
+ fprintf(stderr, "Error adding cert: %s\n", gnutls_strerror(ret));
+ app_exit(1);
+ }
+ gnutls_x509_crt_deinit(crts[i]);
+ }
+ gnutls_free(crts);
+
+ for (i=0;i<crl_size;i++) {
+ ret = gnutls_pkcs7_set_crl(pkcs7, crls[i]);
+ if (ret < 0) {
+ fprintf(stderr, "Error adding CRL: %s\n", gnutls_strerror(ret));
+ app_exit(1);
+ }
+ gnutls_x509_crl_deinit(crls[i]);
+ }
+ gnutls_free(crls);
+
+ ret =
+ gnutls_pkcs7_export2(pkcs7, cinfo->outcert_format, &tmp);
+ if (ret < 0) {
+ fprintf(stderr, "pkcs7_export: %s\n", gnutls_strerror(ret));
+ app_exit(1);
+ }
+
+ fwrite(tmp.data, 1, tmp.size, outfile);
+ gnutls_free(tmp.data);
+
+ gnutls_pkcs7_deinit(pkcs7);
+ app_exit(0);
+}
+
+void pkcs7_info(common_info_st *cinfo, unsigned display_data)
+{
+ gnutls_pkcs7_t pkcs7;
+ int ret;
+ size_t size;
+ gnutls_datum_t data, str;
+
+ ret = gnutls_pkcs7_init(&pkcs7);
+ if (ret < 0) {
+ fprintf(stderr, "p7_init: %s\n", gnutls_strerror(ret));
+ app_exit(1);
+ }
+
+ data.data = (void *) fread_file(infile, 0, &size);
+ data.size = size;
+
+ if (!data.data) {
+ fprintf(stderr, "%s", infile ? "file" : "standard input");
+ app_exit(1);
+ }
+
+ ret = gnutls_pkcs7_import(pkcs7, &data, cinfo->incert_format);
+ free(data.data);
+ if (ret < 0) {
+ fprintf(stderr, "import error: %s\n",
+ gnutls_strerror(ret));
+ app_exit(1);
+ }
+
+ if (display_data) {
+ gnutls_datum_t tmp;
+
+ ret = gnutls_pkcs7_get_embedded_data(pkcs7, 0, &tmp);
+ if (ret != GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) {
+ if (ret < 0) {
+ fprintf(stderr, "error getting embedded data: %s\n", gnutls_strerror(ret));
+ app_exit(1);
+ }
+
+ fwrite(tmp.data, 1, tmp.size, outfile);
+ gnutls_free(tmp.data);
+ } else {
+ fprintf(stderr, "no embedded data are available\n");
+ app_exit(1);
+ }
+ } else {
+ if (cinfo->outtext) {
+ ret = gnutls_pkcs7_print(pkcs7, GNUTLS_CRT_PRINT_FULL, &str);
+ if (ret < 0) {
+ fprintf(stderr, "printing error: %s\n",
+ gnutls_strerror(ret));
+ app_exit(1);
+ }
+
+ fprintf(outfile, "%s", str.data);
+ gnutls_free(str.data);
+ }
+
+ size = lbuffer_size;
+ ret =
+ gnutls_pkcs7_export(pkcs7, cinfo->outcert_format,
+ lbuffer, &size);
+ if (ret < 0) {
+ fprintf(stderr, "export error: %s\n",
+ gnutls_strerror(ret));
+ app_exit(1);
+ }
+
+ fwrite(lbuffer, 1, size, outfile);
+ }
+
+ gnutls_pkcs7_deinit(pkcs7);
+}
+
+void smime_to_pkcs7(void)
+{
+ size_t linesize = 0;
+ char *lineptr = NULL;
+ ssize_t len;
+
+ /* Find body. We do not handle non-b64 Content-Transfer-Encoding. */
+ do {
+ len = getline(&lineptr, &linesize, infile);
+ if (len == -1) {
+ fprintf(stderr,
+ "cannot find RFC 2822 header/body separator");
+ app_exit(1);
+ }
+ }
+ while (strcmp(lineptr, "\r\n") != 0 && strcmp(lineptr, "\n") != 0);
+
+ /* skip newlines */
+ do {
+ len = getline(&lineptr, &linesize, infile);
+ if (len == -1) {
+ fprintf(stderr,
+ "message has RFC 2822 header but no body");
+ app_exit(1);
+ }
+ }
+ while (strcmp(lineptr, "\r\n") == 0 || strcmp(lineptr, "\n") == 0);
+
+ fprintf(outfile, "%s", "-----BEGIN PKCS7-----\n");
+
+ do {
+ while (len > 0
+ && (lineptr[len - 1] == '\r'
+ || lineptr[len - 1] == '\n'))
+ lineptr[--len] = '\0';
+ if (strcmp(lineptr, "") != 0)
+ fprintf(outfile, "%s\n", lineptr);
+ len = getline(&lineptr, &linesize, infile);
+ }
+ while (len != -1);
+
+ fprintf(outfile, "%s", "-----END PKCS7-----\n");
+
+ free(lineptr);
+}
diff --git a/src/cmstool-common.h b/src/cmstool-common.h
new file mode 100644
index 0000000000..4ca60fe92b
--- /dev/null
+++ b/src/cmstool-common.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2011-2012 Free Software Foundation, Inc.
+ *
+ * This file is part of GnuTLS.
+ *
+ * GnuTLS is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuTLS is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef GNUTLS_SRC_CMSTOOL_COMMON_H
+#define GNUTLS_SRC_CMSTOOL_COMMON_H
+
+#include <certtool-common.h>
+
+void pkcs7_info(common_info_st *cinfo, unsigned display_data);
+void pkcs7_generate(common_info_st *);
+void pkcs7_sign_common(common_info_st *, unsigned embed, gnutls_pkcs7_sign_flags flags);
+void pkcs7_verify_common(common_info_st * cinfo, const char *purpose, unsigned display_data, gnutls_certificate_verify_flags flags);
+void smime_to_pkcs7(void);
+
+extern FILE *infile;
+extern FILE *outfile;
+
+#endif /* GNUTLS_SRC_CMSTOOL_COMMON_H */
diff --git a/src/cmstool-options.json b/src/cmstool-options.json
new file mode 100644
index 0000000000..4338c543af
--- /dev/null
+++ b/src/cmstool-options.json
@@ -0,0 +1,226 @@
+{
+ "format-version": "0.1.0",
+ "tool": {
+ "name": "cmstool",
+ "title": "GnuTLS CMS tool",
+ "description": "Manipulate Cryptographic Message Syntax files.",
+ "detail": "Tool to parse and generate CMS and PKCS#7 files.\n\nThe tool accepts files or supported URIs via the --infile option. In case PIN\nis required for URI access you can provide it using the environment variables GNUTLS_PIN \nand GNUTLS_SO_PIN.\n",
+ "short-usage": "cmstool [options]\ncmstool --help for usage instructions.\n"
+ },
+ "sections": [
+ {
+ "options": [
+ {
+ "long-option": "debug",
+ "short-option": "d",
+ "description": "Enable debugging",
+ "detail": "Specifies the debug level.",
+ "argument-range": {
+ "min": 0,
+ "max": 9999
+ },
+ "argument-type": "number"
+ },
+ {
+ "long-option": "verbose",
+ "short-option": "V",
+ "description": "More verbose output"
+ },
+ {
+ "long-option": "infile",
+ "description": "Input file",
+ "file-exists": true,
+ "argument-type": "file"
+ },
+ {
+ "long-option": "outfile",
+ "description": "Output file",
+ "argument-type": "string"
+ }
+ ]
+ },
+ {
+ "ref": "pkcs7-options",
+ "description": "PKCS#7 structure options",
+ "options": [
+ {
+ "long-option": "generate",
+ "description": "Generate a PKCS #7 structure",
+ "detail": "This option generates a PKCS #7 certificate container structure. To add certificates in the structure use --load-certificate and --load-crl."
+ },
+ {
+ "long-option": "sign",
+ "description": "Signs using a PKCS #7 structure",
+ "detail": "This option generates a PKCS #7 structure containing a signature for the provided data from infile. The data are stored within the structure. The signer certificate has to be specified using --load-certificate and --load-privkey. The input to --load-certificate can be a list of certificates. In case of a list, the first certificate is used for signing and the other certificates are included in the structure."
+ },
+ {
+ "long-option": "detached-sign",
+ "description": "Signs using a detached PKCS #7 structure",
+ "detail": "This option generates a PKCS #7 structure containing a signature for the provided data from infile. The signer certificate has to be specified using --load-certificate and --load-privkey. The input to --load-certificate can be a list of certificates. In case of a list, the first certificate is used for signing and the other certificates are included in the structure."
+ },
+ {
+ "long-option": "include-cert",
+ "description": "The signer's certificate will be included in the cert list",
+ "detail": "This options works with --sign or --detached-sign and will include or exclude the signer's certificate into the generated signature.",
+ "enabled": true,
+ "disable-prefix": "no-"
+ },
+ {
+ "long-option": "time",
+ "description": "Will include a timestamp in the PKCS #7 structure",
+ "detail": "This option will include a timestamp in the generated signature",
+ "disable-prefix": "no-"
+ },
+ {
+ "long-option": "show-data",
+ "description": "Will show the embedded data in the PKCS #7 structure",
+ "detail": "This option can be combined with --verify or --info and will display the embedded signed data in the PKCS #7 structure.",
+ "disable-prefix": "no-"
+ },
+ {
+ "long-option": "info",
+ "description": "Print information on a PKCS #7 structure"
+ },
+ {
+ "long-option": "verify",
+ "description": "Verify the provided PKCS #7 structure",
+ "detail": "This option verifies the signed PKCS #7 structure. The certificate list to use for verification can be specified with --load-ca-certificate. When no certificate list is provided, then the system's certificate list is used. Alternatively a direct signer can be provided using --load-certificate. A key purpose can be enforced with the --verify-purpose option, and the --load-data option will utilize detached data."
+ },
+ {
+ "long-option": "smime-to-cms",
+ "description": "Convert S/MIME to PKCS #7 structure"
+ }
+ ]
+ },
+ {
+ "ref": "other-options",
+ "description": "Other options",
+ "options": [
+ {
+ "long-option": "load-privkey",
+ "description": "Loads a private key file",
+ "detail": "This can be either a file or a PKCS #11 URL",
+ "argument-type": "string"
+ },
+ {
+ "long-option": "load-pubkey",
+ "description": "Loads a public key file",
+ "detail": "This can be either a file or a PKCS #11 URL",
+ "argument-type": "string"
+ },
+ {
+ "long-option": "load-certificate",
+ "description": "Loads a certificate file",
+ "detail": "This option can be used with a file",
+ "argument-type": "string"
+ },
+ {
+ "long-option": "load-ca-certificate",
+ "description": "Loads the certificate authority's certificate file",
+ "detail": "This can be either a file or a PKCS #11 URL",
+ "argument-type": "string"
+ },
+ {
+ "long-option": "load-crl",
+ "description": "Loads the provided CRL",
+ "detail": "This option can be used with a file",
+ "argument-type": "string"
+ },
+ {
+ "long-option": "load-data",
+ "description": "Loads auxiliary data",
+ "detail": "This option can be used with a file",
+ "argument-type": "string"
+ },
+ {
+ "long-option": "password",
+ "description": "Password to use",
+ "detail": "You can use this option to specify the password in the command line instead of reading it from the tty. Note, that the command line arguments are available for view in others in the system. Specifying password as '' is the same as specifying no password.",
+ "argument-type": "string"
+ },
+ {
+ "long-option": "null-password",
+ "description": "Enforce a NULL password",
+ "detail": "This option enforces a NULL password. This is different than the empty or no password in schemas like PKCS #8."
+ },
+ {
+ "long-option": "empty-password",
+ "description": "Enforce an empty password",
+ "detail": "This option enforces an empty password. This is different than the NULL or no password in schemas like PKCS #8."
+ },
+ {
+ "long-option": "cprint",
+ "description": "In certain operations it prints the information in C-friendly format",
+ "detail": "In certain operations it prints the information in C-friendly format, suitable for including into C programs."
+ },
+ {
+ "long-option": "hash",
+ "description": "Hash algorithm to use for signing",
+ "detail": "Available hash functions are SHA1, RMD160, SHA256, SHA384, SHA512, SHA3-224, SHA3-256, SHA3-384, SHA3-512.",
+ "argument-type": "string"
+ },
+ {
+ "long-option": "salt-size",
+ "description": "Specify the RSA-PSS key default salt size",
+ "detail": "Typical keys shouldn't set or restrict this option.",
+ "argument-type": "number"
+ },
+ {
+ "long-option": "inder",
+ "description": "Use DER format for input certificates, private keys, and DH parameters ",
+ "detail": "The input files will be assumed to be in DER or RAW format. \nUnlike options that in PEM input would allow multiple input data (e.g. multiple \ncertificates), when reading in DER format a single data structure is read.",
+ "disable-prefix": "no-"
+ },
+ {
+ "long-option": "inraw",
+ "aliases": "inder"
+ },
+ {
+ "long-option": "outder",
+ "description": "Use DER format for output certificates, private keys, and DH parameters",
+ "detail": "The output will be in DER or RAW format.",
+ "disable-prefix": "no-"
+ },
+ {
+ "long-option": "outraw",
+ "aliases": "outder"
+ },
+ {
+ "long-option": "stdout-info",
+ "description": "Print information to stdout instead of stderr"
+ },
+ {
+ "long-option": "pkcs-cipher",
+ "description": "Cipher to use for PKCS #8 and #12 operations",
+ "detail": "Cipher may be one of 3des, 3des-pkcs12, aes-128, aes-192, aes-256, rc2-40, arcfour.",
+ "argument-type": "string",
+ "argument-name": "Cipher"
+ },
+ {
+ "long-option": "provider",
+ "description": "Specify the PKCS #11 provider library",
+ "detail": "This will override the default options in /etc/gnutls/pkcs11.conf",
+ "argument-type": "string"
+ },
+ {
+ "long-option": "verify-purpose",
+ "description": "Specify a purpose OID to be used for certificate chain verification",
+ "detail": "This object identifier restricts the purpose of the certificates to be verified. Example purposes are 1.3.6.1.5.5.7.3.1 (TLS WWW), 1.3.6.1.5.5.7.3.4 (EMAIL) etc. Note that a CA certificate without a purpose set (extended key usage) is valid for any purpose.",
+ "argument-type": "string"
+ },
+ {
+ "long-option": "verify-allow-broken",
+ "description": "Allow broken algorithms, such as MD5 for verification",
+ "detail": "This can be combined with --verify."
+ },
+ {
+ "long-option": "text",
+ "description": "Output textual information before PEM-encoded certificates, private keys, etc",
+ "detail": "Output textual information before PEM-encoded data",
+ "enabled": true,
+ "disable-prefix": "no-"
+ }
+ ]
+ }
+ ]
+}
diff --git a/src/cmstool.c b/src/cmstool.c
new file mode 100644
index 0000000000..7bdb465921
--- /dev/null
+++ b/src/cmstool.c
@@ -0,0 +1,292 @@
+/*
+ * Copyright (C) 2015-2019 Red Hat, Inc.
+ * Copyright (C) 2020 Dmitry Baryshkov
+ *
+ * This file is part of GnuTLS.
+ *
+ * GnuTLS is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuTLS is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+
+#include <gnutls/gnutls.h>
+#include <gnutls/x509.h>
+#include <gnutls/openpgp.h>
+#include <gnutls/pkcs12.h>
+#include <gnutls/pkcs11.h>
+#include <gnutls/abstract.h>
+#include <gnutls/crypto.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <time.h>
+#include <unistd.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#ifndef _WIN32
+# include <signal.h>
+#endif
+
+/* Gnulib portability files. */
+#include <read-file.h>
+
+#include <common.h>
+#include "cmstool-options.h"
+#include "certtool-common.h"
+#include "cmstool-common.h"
+
+static FILE *stdlog = NULL;
+
+static void cmd_parser(int argc, char **argv);
+
+FILE *outfile;
+static const char *outfile_name = NULL; /* to delete on exit */
+
+FILE *infile;
+static unsigned int incert_format, outcert_format;
+
+const char *get_pass(void)
+{
+ return getpass("Enter password: ");
+}
+
+const char *get_confirmed_pass(bool empty_ok)
+{
+ return getpass("Enter password: ");
+}
+
+/* ensure we cleanup */
+void app_exit(int val)
+{
+ if (val != 0) {
+ if (outfile_name)
+ (void)remove(outfile_name);
+ }
+ exit(val);
+}
+
+static void tls_log_func(int level, const char *str)
+{
+ fprintf(stderr, "|<%d>| %s", level, str);
+}
+
+int main(int argc, char **argv)
+{
+#ifndef _WIN32
+ signal(SIGPIPE, SIG_IGN);
+#endif
+ cmd_parser(argc, argv);
+
+ return 0;
+}
+
+static void load_infile(const char *file)
+{
+ struct stat st;
+ if (stat(file, &st) == 0) {
+ fix_lbuffer(2*st.st_size);
+ }
+
+ infile = fopen(file, "rb");
+ if (infile == NULL) {
+ fprintf(stderr, "Cannot open %s for reading\n", OPT_ARG(INFILE));
+ app_exit(1);
+ }
+}
+
+static void pkcs7_sign(common_info_st * cinfo, unsigned embed)
+{
+ unsigned flags = 0;
+
+ if (ENABLED_OPT(TIME))
+ flags |= GNUTLS_PKCS7_INCLUDE_TIME;
+
+ if (ENABLED_OPT(INCLUDE_CERT))
+ flags |= GNUTLS_PKCS7_INCLUDE_CERT;
+
+ return pkcs7_sign_common(cinfo, embed, flags);
+}
+
+static void pkcs7_verify(common_info_st * cinfo, const char *purpose, unsigned display_data)
+{
+ unsigned flags = 0;
+
+ if (HAVE_OPT(VERIFY_ALLOW_BROKEN))
+ flags |= GNUTLS_VERIFY_ALLOW_BROKEN;
+
+ return pkcs7_verify_common(cinfo, purpose, display_data, flags);
+}
+
+static void cmd_parser(int argc, char **argv)
+{
+ int ret, privkey_op = 0;
+ common_info_st cinfo;
+
+ optionProcess(&cmstoolOptions, argc, argv);
+
+ if (HAVE_OPT(STDOUT_INFO)) {
+ /* print informational messages on stdout instead of stderr */
+ stdlog = stdout;
+ } else {
+ stdlog = stderr;
+ }
+
+ if (HAVE_OPT(OUTFILE)) {
+ outfile = safe_open_rw(OPT_ARG(OUTFILE), privkey_op);
+ if (outfile == NULL) {
+ fprintf(stderr, "Cannot open %s for writing\n", OPT_ARG(OUTFILE));
+ app_exit(1);
+ }
+ outfile_name = OPT_ARG(OUTFILE);
+ } else {
+ outfile = stdout;
+ }
+
+ if (!HAVE_OPT(INFILE)) {
+ infile = stdin;
+ } else {
+ load_infile(OPT_ARG(INFILE));
+ }
+
+
+ fix_lbuffer(0);
+
+ if (HAVE_OPT(INDER))
+ incert_format = GNUTLS_X509_FMT_DER;
+ else
+ incert_format = GNUTLS_X509_FMT_PEM;
+
+ if (HAVE_OPT(OUTDER))
+ outcert_format = GNUTLS_X509_FMT_DER;
+ else
+ outcert_format = GNUTLS_X509_FMT_PEM;
+
+ gnutls_global_set_log_function(tls_log_func);
+
+ if (HAVE_OPT(DEBUG)) {
+ gnutls_global_set_log_level(OPT_VALUE_DEBUG);
+ printf("Setting log level to %d\n", (int) OPT_VALUE_DEBUG);
+ }
+
+ if ((ret = gnutls_global_init()) < 0) {
+ fprintf(stderr, "global_init: %s\n", gnutls_strerror(ret));
+ app_exit(1);
+ }
+
+ memset(&cinfo, 0, sizeof(cinfo));
+
+ /*ask_pass = cinfo.ask_pass = ENABLED_OPT(ASK_PASS); */
+ cinfo.hash = GNUTLS_DIG_UNKNOWN;
+ if (HAVE_OPT(HASH)) {
+ cinfo.hash = hash_to_id(OPT_ARG(HASH));
+ if (cinfo.hash == GNUTLS_DIG_UNKNOWN) {
+ fprintf(stderr, "invalid hash: %s\n", OPT_ARG(HASH));
+ app_exit(1);
+ }
+ }
+
+#ifdef ENABLE_PKCS11
+ if (HAVE_OPT(PROVIDER)) {
+ ret = gnutls_pkcs11_init(GNUTLS_PKCS11_FLAG_MANUAL, NULL);
+ if (ret < 0)
+ fprintf(stderr, "pkcs11_init: %s",
+ gnutls_strerror(ret));
+ else {
+ ret =
+ gnutls_pkcs11_add_provider(OPT_ARG(PROVIDER),
+ NULL);
+ if (ret < 0) {
+ fprintf(stderr, "pkcs11_add_provider: %s",
+ gnutls_strerror(ret));
+ app_exit(1);
+ }
+ }
+ }
+
+ pkcs11_common(&cinfo);
+#endif
+
+ if (HAVE_OPT(VERBOSE))
+ cinfo.verbose = 1;
+
+ cinfo.cprint = HAVE_OPT(CPRINT);
+
+ if (HAVE_OPT(LOAD_PRIVKEY))
+ cinfo.privkey = OPT_ARG(LOAD_PRIVKEY);
+
+ if (HAVE_OPT(LOAD_CRL))
+ cinfo.crl = OPT_ARG(LOAD_CRL);
+
+ if (HAVE_OPT(LOAD_DATA))
+ cinfo.data_file = OPT_ARG(LOAD_DATA);
+
+ if (HAVE_OPT(LOAD_PUBKEY))
+ cinfo.pubkey = OPT_ARG(LOAD_PUBKEY);
+
+ cinfo.incert_format = incert_format;
+ cinfo.outcert_format = outcert_format;
+ cinfo.outtext = ENABLED_OPT(TEXT) && outcert_format == GNUTLS_X509_FMT_PEM;
+
+ if (HAVE_OPT(LOAD_CERTIFICATE))
+ cinfo.cert = OPT_ARG(LOAD_CERTIFICATE);
+
+ if (HAVE_OPT(LOAD_CA_CERTIFICATE))
+ cinfo.ca = OPT_ARG(LOAD_CA_CERTIFICATE);
+
+ if (HAVE_OPT(PKCS_CIPHER))
+ cinfo.pkcs_cipher = OPT_ARG(PKCS_CIPHER);
+
+ if (HAVE_OPT(PASSWORD))
+ cinfo.password = OPT_ARG(PASSWORD);
+
+ if (HAVE_OPT(NULL_PASSWORD)) {
+ cinfo.null_password = 1;
+ cinfo.password = "";
+ }
+
+ if (HAVE_OPT(EMPTY_PASSWORD)) {
+ cinfo.empty_password = 1;
+ cinfo.password = "";
+ }
+
+ if (HAVE_OPT(INFO))
+ pkcs7_info(&cinfo, ENABLED_OPT(SHOW_DATA));
+ else if (HAVE_OPT(GENERATE))
+ pkcs7_generate(&cinfo);
+ else if (HAVE_OPT(SIGN))
+ pkcs7_sign(&cinfo, 1);
+ else if (HAVE_OPT(DETACHED_SIGN))
+ pkcs7_sign(&cinfo, 0);
+ else if (HAVE_OPT(VERIFY))
+ pkcs7_verify(&cinfo, OPT_ARG(VERIFY_PURPOSE), ENABLED_OPT(SHOW_DATA));
+ else if (HAVE_OPT(SMIME_TO_CMS))
+ smime_to_pkcs7();
+ else
+ USAGE(1);
+
+ if (outfile != stdout)
+ fclose(outfile);
+
+
+ free(cinfo.seed);
+#ifdef ENABLE_PKCS11
+ gnutls_pkcs11_deinit();
+#endif
+ gnutls_global_deinit();
+}