diff options
Diffstat (limited to 'src/pki')
-rw-r--r-- | src/pki/Makefile.am | 1 | ||||
-rw-r--r-- | src/pki/Makefile.in | 8 | ||||
-rw-r--r-- | src/pki/command.c | 4 | ||||
-rw-r--r-- | src/pki/command.h | 4 | ||||
-rw-r--r-- | src/pki/commands/acert.c | 292 | ||||
-rw-r--r-- | src/pki/commands/issue.c | 34 | ||||
-rw-r--r-- | src/pki/commands/print.c | 95 | ||||
-rw-r--r-- | src/pki/commands/self.c | 33 | ||||
-rw-r--r-- | src/pki/commands/signcrl.c | 28 | ||||
-rw-r--r-- | src/pki/man/Makefile.am | 1 | ||||
-rw-r--r-- | src/pki/man/Makefile.in | 8 | ||||
-rw-r--r-- | src/pki/man/pki---acert.1.in | 130 | ||||
-rw-r--r-- | src/pki/man/pki---issue.1.in | 27 | ||||
-rw-r--r-- | src/pki/man/pki---print.1.in | 5 | ||||
-rw-r--r-- | src/pki/man/pki---self.1.in | 27 | ||||
-rw-r--r-- | src/pki/man/pki---signcrl.1.in | 27 | ||||
-rw-r--r-- | src/pki/man/pki.1.in | 4 | ||||
-rw-r--r-- | src/pki/pki.c | 53 | ||||
-rw-r--r-- | src/pki/pki.h | 17 |
19 files changed, 760 insertions, 38 deletions
diff --git a/src/pki/Makefile.am b/src/pki/Makefile.am index efbed9b2b..266802cf7 100644 --- a/src/pki/Makefile.am +++ b/src/pki/Makefile.am @@ -11,6 +11,7 @@ pki_SOURCES = pki.c pki.h command.c command.h \ commands/self.c \ commands/print.c \ commands/signcrl.c \ + commands/acert.c \ commands/pkcs7.c \ commands/verify.c diff --git a/src/pki/Makefile.in b/src/pki/Makefile.in index 461d958da..2dd91e801 100644 --- a/src/pki/Makefile.in +++ b/src/pki/Makefile.in @@ -107,7 +107,8 @@ am_pki_OBJECTS = pki.$(OBJEXT) command.$(OBJEXT) \ commands/keyid.$(OBJEXT) commands/pub.$(OBJEXT) \ commands/req.$(OBJEXT) commands/self.$(OBJEXT) \ commands/print.$(OBJEXT) commands/signcrl.$(OBJEXT) \ - commands/pkcs7.$(OBJEXT) commands/verify.$(OBJEXT) + commands/acert.$(OBJEXT) commands/pkcs7.$(OBJEXT) \ + commands/verify.$(OBJEXT) pki_OBJECTS = $(am_pki_OBJECTS) pki_DEPENDENCIES = $(top_builddir)/src/libstrongswan/libstrongswan.la AM_V_lt = $(am__v_lt_@AM_V@) @@ -386,7 +387,6 @@ nm_LIBS = @nm_LIBS@ nm_ca_dir = @nm_ca_dir@ nm_plugins = @nm_plugins@ oldincludedir = @oldincludedir@ -openac_plugins = @openac_plugins@ pcsclite_CFLAGS = @pcsclite_CFLAGS@ pcsclite_LIBS = @pcsclite_LIBS@ pdfdir = @pdfdir@ @@ -436,6 +436,7 @@ pki_SOURCES = pki.c pki.h command.c command.h \ commands/self.c \ commands/print.c \ commands/signcrl.c \ + commands/acert.c \ commands/pkcs7.c \ commands/verify.c @@ -549,6 +550,8 @@ commands/print.$(OBJEXT): commands/$(am__dirstamp) \ commands/$(DEPDIR)/$(am__dirstamp) commands/signcrl.$(OBJEXT): commands/$(am__dirstamp) \ commands/$(DEPDIR)/$(am__dirstamp) +commands/acert.$(OBJEXT): commands/$(am__dirstamp) \ + commands/$(DEPDIR)/$(am__dirstamp) commands/pkcs7.$(OBJEXT): commands/$(am__dirstamp) \ commands/$(DEPDIR)/$(am__dirstamp) commands/verify.$(OBJEXT): commands/$(am__dirstamp) \ @@ -567,6 +570,7 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/command.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pki.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@commands/$(DEPDIR)/acert.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@commands/$(DEPDIR)/gen.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@commands/$(DEPDIR)/issue.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@commands/$(DEPDIR)/keyid.Po@am__quote@ diff --git a/src/pki/command.c b/src/pki/command.c index b6966ee0b..075a2279a 100644 --- a/src/pki/command.c +++ b/src/pki/command.c @@ -200,7 +200,7 @@ int command_usage(char *error) fprintf(out, "usage:\n"); if (active == help_idx) { - for (i = 0; cmds[i].cmd; i++) + for (i = 0; i < MAX_COMMANDS && cmds[i].cmd; i++) { fprintf(out, " pki --%-7s (-%c) %s\n", cmds[i].cmd, cmds[i].op, cmds[i].description); @@ -263,7 +263,7 @@ int command_dispatch(int c, char *v[]) build_opts(); op = getopt_long(c, v, command_optstring, command_opts, NULL); - for (i = 0; cmds[i].cmd; i++) + for (i = 0; i < MAX_COMMANDS && cmds[i].cmd; i++) { if (cmds[i].op == op) { diff --git a/src/pki/command.h b/src/pki/command.h index 737f4658d..9cf036bf2 100644 --- a/src/pki/command.h +++ b/src/pki/command.h @@ -24,12 +24,12 @@ /** * Maximum number of commands (+1). */ -#define MAX_COMMANDS 11 +#define MAX_COMMANDS 12 /** * Maximum number of options in a command (+3) */ -#define MAX_OPTIONS 32 +#define MAX_OPTIONS 36 /** * Maximum number of usage summary lines (+1) diff --git a/src/pki/commands/acert.c b/src/pki/commands/acert.c new file mode 100644 index 000000000..d49365db5 --- /dev/null +++ b/src/pki/commands/acert.c @@ -0,0 +1,292 @@ +/* + * Copyright (C) 2009 Martin Willi + * Hochschule fuer Technik Rapperswil + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include <time.h> +#include <errno.h> + +#include "pki.h" + +#include <utils/debug.h> +#include <asn1/asn1.h> +#include <collections/linked_list.h> +#include <credentials/certificates/certificate.h> +#include <credentials/certificates/x509.h> +#include <credentials/certificates/ac.h> + +/** + * Issue an attribute certificate + */ +static int acert() +{ + cred_encoding_type_t form = CERT_ASN1_DER; + hash_algorithm_t digest = HASH_SHA1; + certificate_t *ac = NULL, *cert = NULL, *issuer =NULL; + private_key_t *private = NULL; + public_key_t *public = NULL; + char *file = NULL, *hex = NULL, *issuercert = NULL, *issuerkey = NULL; + char *error = NULL, *keyid = NULL; + linked_list_t *groups; + chunk_t serial = chunk_empty, encoding = chunk_empty; + time_t not_before, not_after, lifetime = 24 * 60 * 60; + char *datenb = NULL, *datena = NULL, *dateform = NULL; + rng_t *rng; + char *arg; + + groups = linked_list_create(); + + while (TRUE) + { + switch (command_getopt(&arg)) + { + case 'h': + goto usage; + case 'g': + digest = enum_from_name(hash_algorithm_short_names, arg); + if (digest == -1) + { + error = "invalid --digest type"; + goto usage; + } + continue; + case 'i': + file = arg; + continue; + case 'm': + groups->insert_last(groups, arg); + continue; + case 'c': + issuercert = arg; + continue; + case 'k': + issuerkey = arg; + continue; + case 'x': + keyid = arg; + continue; + case 'l': + lifetime = atoi(arg) * 60 * 60; + if (!lifetime) + { + error = "invalid --lifetime value"; + goto usage; + } + continue; + case 'D': + dateform = arg; + continue; + case 'F': + datenb = arg; + continue; + case 'T': + datena = arg; + continue; + case 's': + hex = arg; + continue; + case 'f': + if (!get_form(arg, &form, CRED_CERTIFICATE)) + { + error = "invalid output format"; + goto usage; + } + continue; + case EOF: + break; + default: + error = "invalid --acert option"; + goto usage; + } + break; + } + + if (!calculate_lifetime(dateform, datenb, datena, lifetime, + ¬_before, ¬_after)) + { + error = "invalid --not-before/after datetime"; + goto usage; + } + + if (!issuercert) + { + error = "--issuercert is required"; + goto usage; + } + if (!issuerkey && !keyid) + { + error = "--issuerkey or --issuerkeyid is required"; + goto usage; + } + + issuer = lib->creds->create(lib->creds, CRED_CERTIFICATE, CERT_X509, + BUILD_FROM_FILE, issuercert, BUILD_END); + if (!issuer) + { + error = "parsing issuer certificate failed"; + goto end; + } + public = issuer->get_public_key(issuer); + if (!public) + { + error = "extracting issuer certificate public key failed"; + goto end; + } + if (issuerkey) + { + private = lib->creds->create(lib->creds, CRED_PRIVATE_KEY, + public->get_type(public), + BUILD_FROM_FILE, issuerkey, BUILD_END); + } + else + { + chunk_t chunk; + + chunk = chunk_from_hex(chunk_create(keyid, strlen(keyid)), NULL); + private = lib->creds->create(lib->creds, CRED_PRIVATE_KEY, KEY_ANY, + BUILD_PKCS11_KEYID, chunk, BUILD_END); + free(chunk.ptr); + } + if (!private) + { + error = "loading issuer private key failed"; + goto end; + } + if (!private->belongs_to(private, public)) + { + error = "issuer private key does not match issuer certificate"; + goto end; + } + + if (hex) + { + serial = chunk_from_hex(chunk_create(hex, strlen(hex)), NULL); + } + else + { + rng = lib->crypto->create_rng(lib->crypto, RNG_WEAK); + if (!rng) + { + error = "no random number generator found"; + goto end; + } + if (!rng_allocate_bytes_not_zero(rng, 8, &serial, FALSE)) + { + error = "failed to generate serial number"; + rng->destroy(rng); + goto end; + } + serial.ptr[0] &= 0x7F; + rng->destroy(rng); + } + + if (file) + { + cert = lib->creds->create(lib->creds, CRED_CERTIFICATE, CERT_X509, + BUILD_FROM_FILE, file, BUILD_END); + } + else + { + if (!chunk_from_fd(0, &encoding)) + { + fprintf(stderr, "%s: ", strerror(errno)); + error = "reading public key failed"; + goto end; + } + cert = lib->creds->create(lib->creds, CRED_CERTIFICATE, CERT_X509, + BUILD_BLOB, encoding, BUILD_END); + chunk_free(&encoding); + } + if (!cert) + { + error = "parsing user certificate failed"; + goto end; + } + + ac = lib->creds->create(lib->creds, + CRED_CERTIFICATE, CERT_X509_AC, + BUILD_CERT, cert, + BUILD_NOT_BEFORE_TIME, not_before, + BUILD_NOT_AFTER_TIME, not_after, + BUILD_SERIAL, serial, + BUILD_AC_GROUP_STRINGS, groups, + BUILD_SIGNING_CERT, issuer, + BUILD_SIGNING_KEY, private, + BUILD_END); + if (!ac) + { + error = "generating attribute certificate failed"; + goto end; + } + if (!ac->get_encoding(ac, form, &encoding)) + { + error = "encoding attribute certificate failed"; + goto end; + } + if (fwrite(encoding.ptr, encoding.len, 1, stdout) != 1) + { + error = "writing attribute certificate key failed"; + goto end; + } + +end: + DESTROY_IF(ac); + DESTROY_IF(cert); + DESTROY_IF(issuer); + DESTROY_IF(public); + DESTROY_IF(private); + groups->destroy(groups); + free(encoding.ptr); + free(serial.ptr); + + if (error) + { + fprintf(stderr, "%s\n", error); + return 1; + } + return 0; + +usage: + groups->destroy(groups); + return command_usage(error); +} + +/** + * Register the command. + */ +static void __attribute__ ((constructor))reg() +{ + command_register((command_t) { + acert, 'z', "acert", + "issue an attribute certificate", + {"[--in file] [--group name]* --issuerkey file|--issuerkeyid hex", + " --issuercert file [--serial hex] [--lifetime hours]", + " [--not-before datetime] [--not-after datetime] [--dateform form]", + "[--digest md5|sha1|sha224|sha256|sha384|sha512] [--outform der|pem]"}, + { + {"help", 'h', 0, "show usage information"}, + {"in", 'i', 1, "holder certificate, default: stdin"}, + {"group", 'm', 1, "group membership string to include"}, + {"issuercert", 'c', 1, "issuer certificate file"}, + {"issuerkey", 'k', 1, "issuer private key file"}, + {"issuerkeyid", 'x', 1, "keyid on smartcard of issuer private key"}, + {"serial", 's', 1, "serial number in hex, default: random"}, + {"lifetime", 'l', 1, "hours the acert is valid, default: 24"}, + {"not-before", 'F', 1, "date/time the validity of the AC starts"}, + {"not-after", 'T', 1, "date/time the validity of the AC ends"}, + {"dateform", 'D', 1, "strptime(3) input format, default: %d.%m.%y %T"}, + {"digest", 'g', 1, "digest for signature creation, default: sha1"}, + {"outform", 'f', 1, "encoding of generated cert, default: der"}, + } + }); +} diff --git a/src/pki/commands/issue.c b/src/pki/commands/issue.c index d5c33b89f..d03326e3d 100644 --- a/src/pki/commands/issue.c +++ b/src/pki/commands/issue.c @@ -72,8 +72,8 @@ static int issue() int inhibit_mapping = X509_NO_CONSTRAINT, require_explicit = X509_NO_CONSTRAINT; chunk_t serial = chunk_empty; chunk_t encoding = chunk_empty; - time_t lifetime = 1095; - time_t not_before, not_after; + time_t not_before, not_after, lifetime = 1095 * 24 * 60 * 60; + char *datenb = NULL, *datena = NULL, *dateform = NULL; x509_flag_t flags = 0; x509_t *x509; x509_cdp_t *cdp = NULL; @@ -132,13 +132,22 @@ static int issue() san->insert_last(san, identification_create_from_string(arg)); continue; case 'l': - lifetime = atoi(arg); + lifetime = atoi(arg) * 24 * 60 * 60; if (!lifetime) { error = "invalid --lifetime value"; goto usage; } continue; + case 'D': + dateform = arg; + continue; + case 'F': + datenb = arg; + continue; + case 'T': + datena = arg; + continue; case 's': hex = arg; continue; @@ -242,6 +251,10 @@ static int issue() { flags |= X509_OCSP_SIGNER; } + else if (streq(arg, "msSmartcardLogon")) + { + flags |= X509_MS_SMARTCARD_LOGON; + } continue; case 'f': if (!get_form(arg, &form, CRED_CERTIFICATE)) @@ -285,6 +298,12 @@ static int issue() error = "--cakey or --keyid is required"; goto usage; } + if (!calculate_lifetime(dateform, datenb, datena, lifetime, + ¬_before, ¬_after)) + { + error = "invalid --not-before/after datetime"; + goto usage; + } if (dn && *dn) { id = identification_create_from_string(dn); @@ -363,6 +382,7 @@ static int issue() rng->destroy(rng); goto end; } + serial.ptr[0] &= 0x7F; rng->destroy(rng); } @@ -454,9 +474,6 @@ static int issue() chunk_from_chars(ASN1_SEQUENCE, 0)); } - not_before = time(NULL); - not_after = not_before + lifetime * 24 * 60 * 60; - cert = lib->creds->create(lib->creds, CRED_CERTIFICATE, CERT_X509, BUILD_SIGNING_KEY, private, BUILD_SIGNING_CERT, ca, BUILD_PUBLIC_KEY, public, BUILD_SUBJECT, id, @@ -536,7 +553,7 @@ static void __attribute__ ((constructor))reg() {"[--in file] [--type pub|pkcs10] --cakey file|--cakeyid hex", " --cacert file [--dn subject-dn] [--san subjectAltName]+", "[--lifetime days] [--serial hex] [--ca] [--pathlen len]", - "[--flag serverAuth|clientAuth|crlSign|ocspSigning]+", + "[--flag serverAuth|clientAuth|crlSign|ocspSigning|msSmartcardLogon]+", "[--crl uri [--crlissuer i]]+ [--ocsp uri]+ [--nc-permitted name]", "[--nc-excluded name] [--policy-mapping issuer-oid:subject-oid]", "[--policy-explicit len] [--policy-inhibit len] [--policy-any len]", @@ -552,6 +569,9 @@ static void __attribute__ ((constructor))reg() {"dn", 'd', 1, "distinguished name to include as subject"}, {"san", 'a', 1, "subjectAltName to include in certificate"}, {"lifetime", 'l', 1, "days the certificate is valid, default: 1095"}, + {"not-before", 'F', 1, "date/time the validity of the cert starts"}, + {"not-after", 'T', 1, "date/time the validity of the cert ends"}, + {"dateform", 'D', 1, "strptime(3) input format, default: %d.%m.%y %T"}, {"serial", 's', 1, "serial number in hex, default: random"}, {"ca", 'b', 0, "include CA basicConstraint, default: no"}, {"pathlen", 'p', 1, "set path length constraint"}, diff --git a/src/pki/commands/print.c b/src/pki/commands/print.c index 077c1ef3e..15ace035d 100644 --- a/src/pki/commands/print.c +++ b/src/pki/commands/print.c @@ -16,9 +16,11 @@ #include "pki.h" #include <asn1/asn1.h> +#include <asn1/oid.h> #include <credentials/certificates/certificate.h> #include <credentials/certificates/x509.h> #include <credentials/certificates/crl.h> +#include <credentials/certificates/ac.h> #include <selectors/traffic_selector.h> #include <time.h> @@ -138,6 +140,10 @@ static void print_x509(x509_t *x509) { printf("iKEIntermediate "); } + if (flags & X509_MS_SMARTCARD_LOGON) + { + printf("msSmartcardLogon "); + } if (flags & X509_SELF_SIGNED) { printf("self-signed "); @@ -388,6 +394,85 @@ static void print_crl(crl_t *crl) } /** + * Print AC specific information + */ +static void print_ac(ac_t *ac) +{ + ac_group_type_t type; + identification_t *id; + enumerator_t *groups; + chunk_t chunk; + bool first = TRUE; + + chunk = chunk_skip_zero(ac->get_serial(ac)); + printf("serial: %#B\n", &chunk); + + id = ac->get_holderIssuer(ac); + if (id) + { + printf("hissuer: \"%Y\"\n", id); + } + chunk = chunk_skip_zero(ac->get_holderSerial(ac)); + if (chunk.ptr) + { + printf("hserial: %#B\n", &chunk); + } + groups = ac->create_group_enumerator(ac); + while (groups->enumerate(groups, &type, &chunk)) + { + int oid; + char *str; + + if (first) + { + printf("groups: "); + first = FALSE; + } + else + { + printf(" "); + } + switch (type) + { + case AC_GROUP_TYPE_STRING: + printf("%.*s", (int)chunk.len, chunk.ptr); + break; + case AC_GROUP_TYPE_OID: + oid = asn1_known_oid(chunk); + if (oid == OID_UNKNOWN) + { + str = asn1_oid_to_string(chunk); + if (str) + { + printf("%s", str); + free(str); + } + else + { + printf("OID:%#B", &chunk); + } + } + else + { + printf("%s", oid_names[oid].name); + } + break; + case AC_GROUP_TYPE_OCTETS: + printf("%#B", &chunk); + break; + } + printf("\n"); + } + groups->destroy(groups); + + chunk = ac->get_authKeyIdentifier(ac); + if (chunk.ptr) + { + printf("authkey: %#B\n", &chunk); + } +} + +/** * Print certificate information */ static void print_cert(certificate_t *cert) @@ -432,6 +517,9 @@ static void print_cert(certificate_t *cert) case CERT_X509_CRL: print_crl((crl_t*)cert); break; + case CERT_X509_AC: + print_ac((ac_t*)cert); + break; default: printf("parsing certificate subtype %N not implemented\n", certificate_type_names, cert->get_type(cert)); @@ -472,6 +560,11 @@ static int print() type = CRED_CERTIFICATE; subtype = CERT_X509_CRL; } + else if (streq(arg, "ac")) + { + type = CRED_CERTIFICATE; + subtype = CERT_X509_AC; + } else if (streq(arg, "pub")) { type = CRED_PUBLIC_KEY; @@ -558,7 +651,7 @@ static void __attribute__ ((constructor))reg() command_register((command_t) { print, 'a', "print", "print a credential in a human readable form", - {"[--in file] [--type rsa-priv|ecdsa-priv|pub|x509|crl]"}, + {"[--in file] [--type rsa-priv|ecdsa-priv|pub|x509|crl|ac]"}, { {"help", 'h', 0, "show usage information"}, {"in", 'i', 1, "input file, default: stdin"}, diff --git a/src/pki/commands/self.c b/src/pki/commands/self.c index c28c9c291..a35a42b89 100644 --- a/src/pki/commands/self.c +++ b/src/pki/commands/self.c @@ -60,8 +60,8 @@ static int self() int inhibit_mapping = X509_NO_CONSTRAINT, require_explicit = X509_NO_CONSTRAINT; chunk_t serial = chunk_empty; chunk_t encoding = chunk_empty; - time_t lifetime = 1095; - time_t not_before, not_after; + time_t not_before, not_after, lifetime = 1095 * 24 * 60 * 60; + char *datenb = NULL, *datena = NULL, *dateform = NULL; x509_flag_t flags = 0; x509_cert_policy_t *policy = NULL; char *arg; @@ -115,13 +115,22 @@ static int self() san->insert_last(san, identification_create_from_string(arg)); continue; case 'l': - lifetime = atoi(arg); + lifetime = atoi(arg) * 24 * 60 * 60; if (!lifetime) { error = "invalid --lifetime value"; goto usage; } continue; + case 'D': + dateform = arg; + continue; + case 'F': + datenb = arg; + continue; + case 'T': + datena = arg; + continue; case 's': hex = arg; continue; @@ -225,6 +234,10 @@ static int self() { flags |= X509_OCSP_SIGNER; } + else if (streq(arg, "msSmartcardLogon")) + { + flags |= X509_MS_SMARTCARD_LOGON; + } continue; case 'f': if (!get_form(arg, &form, CRED_CERTIFICATE)) @@ -250,6 +263,12 @@ static int self() error = "--dn is required"; goto usage; } + if (!calculate_lifetime(dateform, datenb, datena, lifetime, + ¬_before, ¬_after)) + { + error = "invalid --not-before/after datetime"; + goto usage; + } id = identification_create_from_string(dn); if (id->get_type(id) != ID_DER_ASN1_DN) { @@ -314,10 +333,9 @@ static int self() rng->destroy(rng); goto end; } + serial.ptr[0] &= 0x7F; rng->destroy(rng); } - not_before = time(NULL); - not_after = not_before + lifetime * 24 * 60 * 60; cert = lib->creds->create(lib->creds, CRED_CERTIFICATE, CERT_X509, BUILD_SIGNING_KEY, private, BUILD_PUBLIC_KEY, public, BUILD_SUBJECT, id, BUILD_NOT_BEFORE_TIME, not_before, @@ -391,7 +409,7 @@ static void __attribute__ ((constructor))reg() {" [--in file|--keyid hex] [--type rsa|ecdsa]", " --dn distinguished-name [--san subjectAltName]+", "[--lifetime days] [--serial hex] [--ca] [--ocsp uri]+", - "[--flag serverAuth|clientAuth|crlSign|ocspSigning]+", + "[--flag serverAuth|clientAuth|crlSign|ocspSigning|msSmartcardLogon]+", "[--nc-permitted name] [--nc-excluded name]", "[--policy-map issuer-oid:subject-oid]", "[--policy-explicit len] [--policy-inhibit len] [--policy-any len]", @@ -405,6 +423,9 @@ static void __attribute__ ((constructor))reg() {"dn", 'd', 1, "subject and issuer distinguished name"}, {"san", 'a', 1, "subjectAltName to include in certificate"}, {"lifetime", 'l', 1, "days the certificate is valid, default: 1095"}, + {"not-before", 'F', 1, "date/time the validity of the cert starts"}, + {"not-after", 'T', 1, "date/time the validity of the cert ends"}, + {"dateform", 'D', 1, "strptime(3) input format, default: %d.%m.%y %T"}, {"serial", 's', 1, "serial number in hex, default: random"}, {"ca", 'b', 0, "include CA basicConstraint, default: no"}, {"pathlen", 'p', 1, "set path length constraint"}, diff --git a/src/pki/commands/signcrl.c b/src/pki/commands/signcrl.c index 4f9dd291d..c9eebbf59 100644 --- a/src/pki/commands/signcrl.c +++ b/src/pki/commands/signcrl.c @@ -124,7 +124,8 @@ static int sign_crl() int serial_len = 0; crl_reason_t reason = CRL_REASON_UNSPECIFIED; time_t thisUpdate, nextUpdate, date = time(NULL); - time_t lifetime = 15; + time_t lifetime = 15 * 24 * 60 * 60; + char *datetu = NULL, *datenu = NULL, *dateform = NULL; linked_list_t *list, *cdps; enumerator_t *enumerator, *lastenum = NULL; x509_cdp_t *cdp; @@ -161,13 +162,22 @@ static int sign_crl() lastupdate = arg; continue; case 'l': - lifetime = atoi(arg); + lifetime = atoi(arg) * 24 * 60 * 60; if (!lifetime) { - error = "invalid lifetime"; + error = "invalid --lifetime value"; goto usage; } continue; + case 'D': + dateform = arg; + continue; + case 'F': + datetu = arg; + continue; + case 'T': + datenu = arg; + continue; case 'z': serial_len = read_serial(arg, serial, sizeof(serial)); if (serial_len < 0) @@ -275,6 +285,12 @@ static int sign_crl() error = "--cakey or --keyid is required"; goto usage; } + if (!calculate_lifetime(dateform, datetu, datenu, lifetime, + &thisUpdate, &nextUpdate)) + { + error = "invalid --this/next-update datetime"; + goto usage; + } ca = lib->creds->create(lib->creds, CRED_CERTIFICATE, CERT_X509, BUILD_FROM_FILE, cacert, BUILD_END); @@ -321,9 +337,6 @@ static int sign_crl() goto error; } - thisUpdate = time(NULL); - nextUpdate = thisUpdate + lifetime * 24 * 60 * 60; - if (basecrl) { lastcrl = lib->creds->create(lib->creds, CRED_CERTIFICATE, CERT_X509_CRL, @@ -442,6 +455,9 @@ static void __attribute__ ((constructor))reg() {"cakey", 'k', 1, "CA private key file"}, {"cakeyid", 'x', 1, "keyid on smartcard of CA private key"}, {"lifetime", 'l', 1, "days the CRL gets a nextUpdate, default: 15"}, + {"this-update", 'F', 1, "date/time the validity of the CRL starts"}, + {"next-update", 'T', 1, "date/time the validity of the CRL ends"}, + {"dateform", 'D', 1, "strptime(3) input format, default: %d.%m.%y %T"}, {"lastcrl", 'a', 1, "CRL of lastUpdate to copy revocations from"}, {"basecrl", 'b', 1, "base CRL to create a delta CRL for"}, {"crluri", 'u', 1, "freshest delta CRL URI to include"}, diff --git a/src/pki/man/Makefile.am b/src/pki/man/Makefile.am index 618bd4093..4c901ae3c 100644 --- a/src/pki/man/Makefile.am +++ b/src/pki/man/Makefile.am @@ -4,6 +4,7 @@ man1_MANS = \ pki---self.1 \ pki---issue.1 \ pki---signcrl.1 \ + pki---acert.1 \ pki---req.1 \ pki---pkcs7.1 \ pki---keyid.1 \ diff --git a/src/pki/man/Makefile.in b/src/pki/man/Makefile.in index edbde85b5..5d901a87e 100644 --- a/src/pki/man/Makefile.in +++ b/src/pki/man/Makefile.in @@ -84,7 +84,7 @@ DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am \ $(srcdir)/pki---pkcs7.1.in $(srcdir)/pki---print.1.in \ $(srcdir)/pki---pub.1.in $(srcdir)/pki---req.1.in \ $(srcdir)/pki---self.1.in $(srcdir)/pki---signcrl.1.in \ - $(srcdir)/pki---verify.1.in + $(srcdir)/pki---acert.1.in $(srcdir)/pki---verify.1.in ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/config/libtool.m4 \ $(top_srcdir)/m4/config/ltoptions.m4 \ @@ -102,7 +102,7 @@ mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = pki.1 pki---gen.1 pki---issue.1 pki---keyid.1 \ pki---pkcs7.1 pki---print.1 pki---pub.1 pki---req.1 \ - pki---self.1 pki---signcrl.1 pki---verify.1 + pki---self.1 pki---signcrl.1 pki---acert.1 pki---verify.1 CONFIG_CLEAN_VPATH_FILES = AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) @@ -325,7 +325,6 @@ nm_LIBS = @nm_LIBS@ nm_ca_dir = @nm_ca_dir@ nm_plugins = @nm_plugins@ oldincludedir = @oldincludedir@ -openac_plugins = @openac_plugins@ pcsclite_CFLAGS = @pcsclite_CFLAGS@ pcsclite_LIBS = @pcsclite_LIBS@ pdfdir = @pdfdir@ @@ -371,6 +370,7 @@ man1_MANS = \ pki---self.1 \ pki---issue.1 \ pki---signcrl.1 \ + pki---acert.1 \ pki---req.1 \ pki---pkcs7.1 \ pki---keyid.1 \ @@ -432,6 +432,8 @@ pki---self.1: $(top_builddir)/config.status $(srcdir)/pki---self.1.in cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ pki---signcrl.1: $(top_builddir)/config.status $(srcdir)/pki---signcrl.1.in cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ +pki---acert.1: $(top_builddir)/config.status $(srcdir)/pki---acert.1.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ pki---verify.1: $(top_builddir)/config.status $(srcdir)/pki---verify.1.in cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ diff --git a/src/pki/man/pki---acert.1.in b/src/pki/man/pki---acert.1.in new file mode 100644 index 000000000..ec1d8be6e --- /dev/null +++ b/src/pki/man/pki---acert.1.in @@ -0,0 +1,130 @@ +.TH "PKI \-\-ACERT" 1 "2014-02-05" "@PACKAGE_VERSION@" "strongSwan" +. +.SH "NAME" +. +pki \-\-acert \- Issue an attribute certificate +. +.SH "SYNOPSIS" +. +.SY pki\ \-\-acert +.OP \-\-in file +.OP \-\-group membership +.BI \-\-issuerkey\~ file |\-\-issuerkeyid\~ hex +.BI \-\-issuercert\~ file +.OP \-\-lifetime hours +.OP \-\-not-before datetime +.OP \-\-not-after datetime +.OP \-\-serial hex +.OP \-\-digest digest +.OP \-\-outform encoding +.OP \-\-debug level +.YS +. +.SY pki\ \-\-acert +.BI \-\-options\~ file +.YS +. +.SY "pki \-\-acert" +.B \-h +| +.B \-\-help +.YS +. +.SH "DESCRIPTION" +. +This sub-command of +.BR pki (1) +is used to issue an attribute certificate using an issuer certificate with its +private key and the holder certificate. +. +.SH "OPTIONS" +. +.TP +.B "\-h, \-\-help" +Print usage information with a summary of the available options. +.TP +.BI "\-v, \-\-debug " level +Set debug level, default: 1. +.TP +.BI "\-+, \-\-options " file +Read command line options from \fIfile\fR. +.TP +.BI "\-i, \-\-in " file +Holder certificate to issue an attribute certificate for. If not given the +certificate is read from \fISTDIN\fR. +.TP +.BI "\-m, \-\-group " membership +Group membership the attribute certificate shall certify. The specified group +is included as a string. To include multiple groups, the option can be repeated. +.TP +.BI "\-k, \-\-issuerkey " file +Issuer private key file. Either this or +.B \-\-issuerkeyid +is required. +.TP +.BI "\-x, \-\-issuerkeyid " hex +Key ID of a issuer private key on a smartcard. Either this or +.B \-\-issuerkey +is required. +.TP +.BI "\-c, \-\-issuercert " file +Issuer certificate file. Required. +.TP +.BI "\-l, \-\-lifetime " hours +Hours the attribute certificate is valid, default: 24. Ignored if both +an absolute start and end time are given. +.TP +.BI "\-F, \-\-not-before " datetime +Absolute time when the validity of the AC begins. The datetime format is +defined by the +.B \-\-dateform +option. +.TP +.BI "\-T, \-\-not-after " datetime +Absolute time when the validity of the AC ends. The datetime format is +defined by the +.B \-\-dateform +option. +.TP +.BI "\-D, \-\-dateform " form +strptime(3) format for the +.B \-\-not\-before +and +.B \-\-not\-after +options, default: +.B %d.%m.%y %T +.TP +.BI "\-s, \-\-serial " hex +Serial number in hex. It is randomly allocated by default. +.TP +.BI "\-g, \-\-digest " digest +Digest to use for signature creation. One of \fImd5\fR, \fIsha1\fR, +\fIsha224\fR, \fIsha256\fR, \fIsha384\fR, or \fIsha512\fR. Defaults to +\fIsha1\fR. +.TP +.BI "\-f, \-\-outform " encoding +Encoding of the created certificate file. Either \fIder\fR (ASN.1 DER) or +\fIpem\fR (Base64 PEM), defaults to \fIder\fR. +. +.SH "EXAMPLES" +. +To save repetitive typing, command line options can be stored in files. +Lets assume +.I acert.opt +contains the following contents: +.PP +.EX + --issuercert aacert.der --issuerkey aakey.der --digest sha256 --lifetime 4 +.EE +.PP +Then the following command can be used to issue an attribute certificate based +on a holder certificate and the options above: +.PP +.EX + pki --acert --options acert.opt --in holder.der --group sales --group finance -f pem +.EE +.PP +. +.SH "SEE ALSO" +. +.BR pki (1) diff --git a/src/pki/man/pki---issue.1.in b/src/pki/man/pki---issue.1.in index 3fad1ae8a..375cb2fe4 100644 --- a/src/pki/man/pki---issue.1.in +++ b/src/pki/man/pki---issue.1.in @@ -14,6 +14,8 @@ pki \-\-issue \- Issue a certificate using a CA certificate and key .OP \-\-dn subject-dn .OP \-\-san subjectAltName .OP \-\-lifetime days +.OP \-\-not-before datetime +.OP \-\-not-after datetime .OP \-\-serial hex .OP \-\-flag flag .OP \-\-digest digest @@ -88,7 +90,28 @@ Subject distinguished name (DN) of the issued certificate. subjectAltName extension to include in certificate. Can be used multiple times. .TP .BI "\-l, \-\-lifetime " days -Days the certificate is valid, default: 1095. +Days the certificate is valid, default: 1095. Ignored if both +an absolute start and end time are given. +.TP +.BI "\-F, \-\-not-before " datetime +Absolute time when the validity of the certificate begins. The datetime format +is defined by the +.B \-\-dateform +option. +.TP +.BI "\-T, \-\-not-after " datetime +Absolute time when the validity of the certificate ends. The datetime format is +defined by the +.B \-\-dateform +option. +.TP +.BI "\-D, \-\-dateform " form +strptime(3) format for the +.B \-\-not\-before +and +.B \-\-not\-after +options, default: +.B %d.%m.%y %T .TP .BI "\-s, \-\-serial " hex Serial number in hex. It is randomly allocated by default. @@ -176,4 +199,4 @@ given PKCS#10 certificate request and the options above: . .SH "SEE ALSO" . -.BR pki (1)
\ No newline at end of file +.BR pki (1) diff --git a/src/pki/man/pki---print.1.in b/src/pki/man/pki---print.1.in index 8d3345edc..434d4ea16 100644 --- a/src/pki/man/pki---print.1.in +++ b/src/pki/man/pki---print.1.in @@ -46,8 +46,9 @@ Input file. If not given the input is read from \fISTDIN\fR. .BI "\-t, \-\-type " type Type of input. One of \fIrsa-priv\fR (RSA private key), \fIecdsa-priv\fR (ECDSA private key), \fIpub\fR (public key), \fIx509\fR (X.509 certificate), \fIcrl\fR -(Certificate Revocation List, CRL), defaults to \fIx509\fR. +(Certificate Revocation List, CRL), \fIac\fR (Attribute Certificate), +defaults to \fIx509\fR. . .SH "SEE ALSO" . -.BR pki (1)
\ No newline at end of file +.BR pki (1) diff --git a/src/pki/man/pki---self.1.in b/src/pki/man/pki---self.1.in index ee42cf9a0..5e6e78bd0 100644 --- a/src/pki/man/pki---self.1.in +++ b/src/pki/man/pki---self.1.in @@ -14,6 +14,8 @@ pki \-\-self \- Create a self-signed certificate .BI \-\-dn\~ distinguished-name .OP \-\-san subjectAltName .OP \-\-lifetime days +.OP \-\-not-before datetime +.OP \-\-not-after datetime .OP \-\-serial hex .OP \-\-flag flag .OP \-\-digest digest @@ -75,7 +77,28 @@ Subject and issuer distinguished name (DN). Required. subjectAltName extension to include in certificate. Can be used multiple times. .TP .BI "\-l, \-\-lifetime " days -Days the certificate is valid, default: 1095. +Days the certificate is valid, default: 1095. Ignored if both +an absolute start and end time are given. +.TP +.BI "\-F, \-\-not-before " datetime +Absolute time when the validity of the certificate begins. The datetime format +is defined by the +.B \-\-dateform +option. +.TP +.BI "\-T, \-\-not-after " datetime +Absolute time when the validity of the certificate ends. The datetime format is +defined by the +.B \-\-dateform +option. +.TP +.BI "\-D, \-\-dateform " form +strptime(3) format for the +.B \-\-not\-before +and +.B \-\-not\-after +options, default: +.B %d.%m.%y %T .TP .BI "\-s, \-\-serial " hex Serial number in hex. It is randomly allocated by default. @@ -145,4 +168,4 @@ Generate a self-signed certificate using the given RSA key: . .SH "SEE ALSO" . -.BR pki (1)
\ No newline at end of file +.BR pki (1) diff --git a/src/pki/man/pki---signcrl.1.in b/src/pki/man/pki---signcrl.1.in index 6ba96f6bc..bd6cba547 100644 --- a/src/pki/man/pki---signcrl.1.in +++ b/src/pki/man/pki---signcrl.1.in @@ -10,6 +10,8 @@ pki \-\-signcrl \- Issue a Certificate Revocation List (CRL) using a CA certific .BI \-\-cakey\~ file |\-\-cakeyid\~ hex .BI \-\-cacert\~ file .OP \-\-lifetime days +.OP \-\-this-update datetime +.OP \-\-next-update datetime .OP \-\-lastcrl crl .OP \-\-basecrl crl .OP \-\-crluri uri @@ -62,7 +64,28 @@ is required. CA certificate file. Required. .TP .BI "\-l, \-\-lifetime " days -Days until the CRL gets a nextUpdate, default: 15. +Days until the CRL gets a nextUpdate, default: 15. Ignored if both +an absolute start and end time are given. +.TP +.BI "\-F, \-\-this-update " datetime +Absolute time when the validity of the CRL begins. The datetime format is +defined by the +.B \-\-dateform +option. +.TP +.BI "\-T, \-\-next-update " datetime +Absolute time when the validity of the CRL end. The datetime format is +defined by the +.B \-\-dateform +option. +.TP +.BI "\-D, \-\-dateform " form +strptime(3) format for the +.B \-\-this\-update +and +.B \-\-next\-update +options, default: +.B %d.%m.%y %T .TP .BI "\-a, \-\-lastcrl " crl CRL of lastUpdate to copy revocations from. @@ -121,4 +144,4 @@ number, but no reason: .PP .SH "SEE ALSO" . -.BR pki (1)
\ No newline at end of file +.BR pki (1) diff --git a/src/pki/man/pki.1.in b/src/pki/man/pki.1.in index 8dfc53af3..f347031b4 100644 --- a/src/pki/man/pki.1.in +++ b/src/pki/man/pki.1.in @@ -49,6 +49,9 @@ Issue a certificate using a CA certificate and key. .B "\-c, \-\-signcrl" Issue a CRL using a CA certificate and key. .TP +.B "\-z, \-\-acert" +Issue an attribute certificate. +.TP .B "\-r, \-\-req" Create a PKCS#10 certificate request. .TP @@ -148,6 +151,7 @@ certificates with the \-\-crl option. .BR pki\ \-\-self (1), .BR pki\ \-\-issue (1), .BR pki\ \-\-signcrl (1), +.BR pki\ \-\-acert (1), .BR pki\ \-\-req (1), .BR pki\ \-\-pkcs7 (1), .BR pki\ \-\-keyid (1), diff --git a/src/pki/pki.c b/src/pki/pki.c index eb614dd7f..ae4ef1cb0 100644 --- a/src/pki/pki.c +++ b/src/pki/pki.c @@ -13,9 +13,11 @@ * for more details. */ +#define _GNU_SOURCE #include "command.h" #include "pki.h" +#include <time.h> #include <unistd.h> #include <utils/debug.h> @@ -102,6 +104,56 @@ bool get_form(char *form, cred_encoding_type_t *enc, credential_type_t type) } /** + * See header + */ +bool calculate_lifetime(char *format, char *nbstr, char *nastr, time_t span, + time_t *nb, time_t *na) +{ + struct tm tm; + time_t now; + char *end; + + if (!format) + { + format = "%d.%m.%y %T"; + } + + now = time(NULL); + + localtime_r(&now, &tm); + if (nbstr) + { + end = strptime(nbstr, format, &tm); + if (end == NULL || *end != '\0') + { + return FALSE; + } + } + *nb = mktime(&tm); + + localtime_r(&now, &tm); + if (nastr) + { + end = strptime(nastr, format, &tm); + if (end == NULL || *end != '\0') + { + return FALSE; + } + } + *na = mktime(&tm); + + if (!nbstr && nastr) + { + *nb = *na - span; + } + else if (!nastr) + { + *na = *nb + span; + } + return TRUE; +} + +/** * Callback credential set pki uses */ static callback_cred_t *cb_set; @@ -188,4 +240,3 @@ int main(int argc, char *argv[]) atexit(remove_callback); return command_dispatch(argc, argv); } - diff --git a/src/pki/pki.h b/src/pki/pki.h index 09c50c6c2..616fac44a 100644 --- a/src/pki/pki.h +++ b/src/pki/pki.h @@ -33,4 +33,21 @@ */ bool get_form(char *form, cred_encoding_type_t *enc, credential_type_t type); +/** + * Calculate start/end lifetime for certificates. + * + * If both nbstr and nastr are given, span is ignored. Otherwise missing + * arguments are calculated, or assumed to be now. + * + * @param format strptime() format, NULL for default: %d.%m.%y %T + * @param nbstr string describing notBefore datetime, or NULL + * @param nastr string describing notAfter datetime, or NULL + * @param span lifetime span, from notBefore to notAfter + * @param nb calculated notBefore time + * @param na calculated notAfter time + * @return TRUE of nb/na calculated successfully + */ +bool calculate_lifetime(char *format, char *nbstr, char *nastr, time_t span, + time_t *nb, time_t *na); + #endif /** PKI_H_ @}*/ |