diff options
Diffstat (limited to 'src/scepclient/scepclient.c')
-rw-r--r-- | src/scepclient/scepclient.c | 865 |
1 files changed, 554 insertions, 311 deletions
diff --git a/src/scepclient/scepclient.c b/src/scepclient/scepclient.c index 0b54eeee3..78b0d7e7a 100644 --- a/src/scepclient/scepclient.c +++ b/src/scepclient/scepclient.c @@ -1,4 +1,5 @@ /* + * Copyright (C) 2012 Tobias Brunner * Copyright (C) 2005 Jan Hutter, Martin Willi * Hochschule fuer Technik Rapperswil * @@ -13,17 +14,6 @@ * for more details. */ -/** - * @file main.c - * @brief scepclient main program - */ - -/** - * @mainpage SCEP for Linux strongSwan - * - * Documentation of SCEP for Linux StrongSwan - */ - #include <stdarg.h> #include <stdio.h> #include <stdlib.h> @@ -32,8 +22,8 @@ #include <ctype.h> #include <unistd.h> #include <time.h> - -#include <freeswan.h> +#include <limits.h> +#include <syslog.h> #include <library.h> #include <debug.h> @@ -52,18 +42,18 @@ #include <credentials/certificates/pkcs10.h> #include <plugins/plugin.h> -#include "../pluto/constants.h" -#include "../pluto/defs.h" -#include "../pluto/log.h" -#include "../pluto/certs.h" -#include "../pluto/pkcs7.h" - #include "scep.h" /* * definition of some defaults */ +/* some paths */ +#define REQ_PATH IPSEC_CONFDIR "/ipsec.d/reqs" +#define HOST_CERT_PATH IPSEC_CONFDIR "/ipsec.d/certs" +#define CA_CERT_PATH IPSEC_CONFDIR "/ipsec.d/cacerts" +#define PRIVATE_KEY_PATH IPSEC_CONFDIR "/ipsec.d/private" + /* default name of DER-encoded PKCS#1 private key file */ #define DEFAULT_FILENAME_PKCS1 "myKey.der" @@ -100,6 +90,9 @@ /* default distinguished name */ #define DEFAULT_DN "C=CH, O=Linux strongSwan, CN=" +/* minimum RSA key size */ +#define RSA_MIN_OCTETS (512 / BITS_PER_BYTE) + /* challenge password buffer size */ #define MAX_PASSWORD_LENGTH 256 @@ -125,7 +118,6 @@ options_t *options; /* * Global variables */ - chunk_t pkcs1; chunk_t pkcs7; chunk_t challengePassword; @@ -148,13 +140,118 @@ certificate_t *x509_ca_enc = NULL; certificate_t *x509_ca_sig = NULL; certificate_t *pkcs10_req = NULL; +/* logging */ +static bool log_to_stderr = TRUE; +static bool log_to_syslog = TRUE; +static level_t default_loglevel = 1; + +/** + * logging function for scepclient + */ +static void scepclient_dbg(debug_t group, level_t level, char *fmt, ...) +{ + char buffer[8192]; + char *current = buffer, *next; + va_list args; + + if (level <= default_loglevel) + { + if (log_to_stderr) + { + va_start(args, fmt); + vfprintf(stderr, fmt, args); + va_end(args); + fprintf(stderr, "\n"); + } + if (log_to_syslog) + { + /* write in memory buffer first */ + va_start(args, fmt); + vsnprintf(buffer, sizeof(buffer), fmt, args); + va_end(args); + + /* do a syslog with every line */ + while (current) + { + next = strchr(current, '\n'); + if (next) + { + *(next++) = '\0'; + } + syslog(LOG_INFO, "%s\n", current); + current = next; + } + } + } +} + +/** + * Initialize logging to stderr/syslog + */ +static void init_log(const char *program) +{ + dbg = scepclient_dbg; + + if (log_to_stderr) + { + setbuf(stderr, NULL); + } + if (log_to_syslog) + { + openlog(program, LOG_CONS | LOG_NDELAY | LOG_PID, LOG_AUTHPRIV); + } +} + +/** + * join two paths if filename is not absolute + */ +static void join_paths(char *target, size_t target_size, char *parent, + char *filename) +{ + if (*filename == '/' || *filename == '.') + { + snprintf(target, target_size, "%s", filename); + } + else + { + snprintf(target, target_size, "%s/%s", parent, filename); + } +} + +/** + * add a suffix to a given filename, properly handling extensions like '.der' + */ +static void add_path_suffix(char *target, size_t target_size, char *filename, + char *suffix_fmt, ...) +{ + char suffix[PATH_MAX], *start, *dot; + va_list args; + + va_start(args, suffix_fmt); + vsnprintf(suffix, sizeof(suffix), suffix_fmt, args); + va_end(args); + + start = strrchr(filename, '/'); + start = start ?: filename; + dot = strrchr(start, '.'); + + if (!dot || dot == start || dot[1] == '\0') + { /* no extension add suffix at the end */ + snprintf(target, target_size, "%s%s", filename, suffix); + } + else + { /* add the suffix between the filename and the extension */ + snprintf(target, target_size, "%.*s%s%s", (int)(dot - filename), + filename, suffix, dot); + } +} + /** * @brief exit scepclient * * @param status 0 = OK, 1 = general discomfort */ -static void -exit_scepclient(err_t message, ...) +static void exit_scepclient(err_t message, ...) { int status = 0; @@ -183,7 +280,7 @@ exit_scepclient(err_t message, ...) if (message != NULL && *message != '\0') { va_list args; - char m[LOG_WIDTH]; /* longer messages will be truncated */ + char m[8192]; va_start(args, message); vsnprintf(m, sizeof(m), message, args); @@ -193,7 +290,6 @@ exit_scepclient(err_t message, ...) status = -1; } library_deinit(); - close_log(); exit(status); } @@ -201,8 +297,7 @@ exit_scepclient(err_t message, ...) * @brief prints the program version and exits * */ -static void -version(void) +static void version(void) { printf("scepclient %s\n", scepclient_version); exit_scepclient(NULL); @@ -214,22 +309,27 @@ version(void) * If message is set, program is exitet with 1 (error) * @param message message in case of an error */ -static void -usage(const char *message) +static void usage(const char *message) { fprintf(stderr, "Usage: scepclient\n" " --help (-h) show usage and exit\n" " --version (-v) show version and exit\n" " --quiet (-q) do not write log output to stderr\n" - " --in (-i) <type>[=<filename>] use <filename> of <type> for input \n" - " <type> = pkcs1 | cacert-enc | cacert-sig\n" - " - if no pkcs1 input is defined, a \n" - " RSA key will be generated\n" + " --in (-i) <type>[=<filename>] use <filename> of <type> for input\n" + " <type> = pkcs1 | pkcs10 | cert-self\n" + " cacert-enc | cacert-sig\n" + " - if no pkcs1 input is defined, an RSA\n" + " key will be generated\n" + " - if no pkcs10 input is defined, a\n" + " PKCS#10 request will be generated\n" + " - if no cert-self input is defined, a\n" + " self-signed certificate will be generated\n" " - if no filename is given, default is used\n" " --out (-o) <type>[=<filename>] write output of <type> to <filename>\n" " multiple outputs are allowed\n" - " <type> = pkcs1 | pkcs10 | pkcs7 | cert-self | cert | cacert\n" + " <type> = pkcs1 | pkcs10 | pkcs7 | cert-self |\n" + " cert | cacert\n" " - type cacert defines filename prefix of\n" " received CA certificate(s)\n" " - if no filename is given, default is used\n" @@ -238,7 +338,7 @@ usage(const char *message) "\n" "Options for key generation (pkcs1):\n" " --keylength (-k) <bits> key length for RSA key generation\n" - "(default: 2048 bits)\n" + " (default: 2048 bits)\n" "\n" "Options for validity:\n" " --days (-D) <days> validity in days\n" @@ -250,27 +350,26 @@ usage(const char *message) " --subjectAltName (-s) <t>=<v> include subjectAltName in certificate request\n" " <t> = email | dns | ip \n" " --password (-p) <pw> challenge password\n" - " - if pw is '%%prompt', password gets prompted for\n" - " --algorithm (-a) <algo> use specified algorithm for PKCS#7 encryption\n" - " <algo> = des | 3des (default) | aes128| aes192 | \n" - " aes256 | camellia128 | camellia192 | camellia256\n" + " - use '%%prompt' as pw for a password prompt\n" + " --algorithm (-a) [<type>=]<algo> algorithm to be used for PKCS#7 encryption,\n" + " PKCS#7 digest or PKCS#10 signature\n" + " <type> = enc | dgst | sig\n" + " - if no type is given enc is assumed\n" + " <algo> = des (default) | 3des | aes128 |\n" + " aes192 | aes256 | camellia128 |\n" + " camellia192 | camellia256\n" + " <algo> = md5 (default) | sha1 | sha256 |\n" + " sha384 | sha512\n" "\n" "Options for enrollment (cert):\n" " --url (-u) <url> url of the SCEP server\n" " --method (-m) post | get http request type\n" - " --interval (-t) <seconds> manual mode poll interval in seconds (default 20s)\n" + " --interval (-t) <seconds> poll interval in seconds (default 20s)\n" " --maxpolltime (-x) <seconds> max poll time in seconds when in manual mode\n" " (default: unlimited)\n" -#ifdef DEBUG "\n" "Debugging output:\n" - " --debug-all (-A) show everything except private\n" - " --debug-parsing (-P) show parsing relevant stuff\n" - " --debug-raw (-R) show raw hex dumps\n" - " --debug-control (-C) show control flow output\n" - " --debug-controlmore (-M) show more control flow\n" - " --debug-private (-X) show sensitive data (private keys, etc.)\n" -#endif + " --debug (-l) <level> changes the log level (-1..4, default: 1)\n" ); exit_scepclient(message); } @@ -295,7 +394,7 @@ int main(int argc, char **argv) CERT_SELF = 0x08, CERT = 0x10, CACERT_ENC = 0x20, - CACERT_SIG = 0x40 + CACERT_SIG = 0x40, } scep_filetype_t; /* filetype to read from, defaults to "generate a key" */ @@ -306,6 +405,8 @@ int main(int argc, char **argv) /* input files */ char *file_in_pkcs1 = DEFAULT_FILENAME_PKCS1; + char *file_in_pkcs10 = DEFAULT_FILENAME_PKCS10; + char *file_in_cert_self = DEFAULT_FILENAME_CERT_SELF; char *file_in_cacert_enc = DEFAULT_FILENAME_CACERT_ENC; char *file_in_cacert_sig = DEFAULT_FILENAME_CACERT_SIG; @@ -337,14 +438,15 @@ int main(int argc, char **argv) /* challenge password */ char challenge_password_buffer[MAX_PASSWORD_LENGTH]; - /* symmetric encryption algorithm used by pkcs7, default is 3DES */ - int pkcs7_symmetric_cipher = OID_3DES_EDE_CBC; + /* symmetric encryption algorithm used by pkcs7, default is DES */ + encryption_algorithm_t pkcs7_symmetric_cipher = ENCR_DES; + size_t pkcs7_key_size = 0; - /* digest algorithm used by pkcs7, default is SHA-1 */ - int pkcs7_digest_alg = OID_SHA1; + /* digest algorithm used by pkcs7, default is MD5 */ + hash_algorithm_t pkcs7_digest_alg = HASH_MD5; - /* signature algorithm used by pkcs10, default is SHA-1 */ - hash_algorithm_t pkcs10_signature_alg = HASH_SHA1; + /* signature algorithm used by pkcs10, default is MD5 */ + hash_algorithm_t pkcs10_signature_alg = HASH_MD5; /* URL of the SCEP-Server */ char *scep_url = NULL; @@ -388,7 +490,6 @@ int main(int argc, char **argv) scep_response = chunk_empty; subjectAltNames = linked_list_create(); options = options_create(); - log_to_stderr = TRUE; for (;;) { @@ -398,6 +499,7 @@ int main(int argc, char **argv) { "version", no_argument, NULL, 'v' }, { "optionsfrom", required_argument, NULL, '+' }, { "quiet", no_argument, NULL, 'q' }, + { "debug", required_argument, NULL, 'l' }, { "in", required_argument, NULL, 'i' }, { "out", required_argument, NULL, 'o' }, { "force", no_argument, NULL, 'f' }, @@ -413,14 +515,6 @@ int main(int argc, char **argv) { "method", required_argument, NULL, 'm' }, { "interval", required_argument, NULL, 't' }, { "maxpolltime", required_argument, NULL, 'x' }, -#ifdef DEBUG - { "debug-all", no_argument, NULL, 'A' }, - { "debug-parsing", no_argument, NULL, 'P'}, - { "debug-raw", no_argument, NULL, 'R'}, - { "debug-control", no_argument, NULL, 'C'}, - { "debug-controlmore", no_argument, NULL, 'M'}, - { "debug-private", no_argument, NULL, 'X'}, -#endif { 0,0,0,0 } }; @@ -429,20 +523,24 @@ int main(int argc, char **argv) switch (c) { - case EOF: /* end of flags */ - break; + case EOF: /* end of flags */ + break; + + case 'h': /* --help */ + usage(NULL); - case 'h': /* --help */ - usage(NULL); + case 'v': /* --version */ + version(); - case 'v': /* --version */ - version(); + case 'q': /* --quiet */ + log_to_stderr = FALSE; + continue; - case 'q': /* --quiet */ - log_to_stderr = FALSE; - continue; + case 'l': /* --debug <level> */ + default_loglevel = atoi(optarg); + continue; - case 'i': /* --in <type> [= <filename>] */ + case 'i': /* --in <type> [= <filename>] */ { char *filename = strstr(optarg, "="); @@ -459,6 +557,12 @@ int main(int argc, char **argv) if (filename) file_in_pkcs1 = filename; } + else if (strcaseeq("pkcs10", optarg)) + { + filetype_in |= PKCS10; + if (filename) + file_in_pkcs10 = filename; + } else if (strcaseeq("cacert-enc", optarg)) { filetype_in |= CACERT_ENC; @@ -469,7 +573,13 @@ int main(int argc, char **argv) { filetype_in |= CACERT_SIG; if (filename) - file_in_cacert_sig = filename; + file_in_cacert_sig = filename; + } + else if (strcaseeq("cert-self", optarg)) + { + filetype_in |= CERT_SELF; + if (filename) + file_in_cert_self = filename; } else { @@ -478,7 +588,7 @@ int main(int argc, char **argv) continue; } - case 'o': /* --out <type> [= <filename>] */ + case 'o': /* --out <type> [= <filename>] */ { char *filename = strstr(optarg, "="); @@ -532,18 +642,18 @@ int main(int argc, char **argv) continue; } - case 'f': /* --force */ - force = TRUE; - continue; + case 'f': /* --force */ + force = TRUE; + continue; - case '+': /* --optionsfrom <filename> */ - if (!options->from(options, optarg, &argc, &argv, optind)) - { - exit_scepclient("optionsfrom failed"); - } - continue; + case '+': /* --optionsfrom <filename> */ + if (!options->from(options, optarg, &argc, &argv, optind)) + { + exit_scepclient("optionsfrom failed"); + } + continue; - case 'k': /* --keylength <length> */ + case 'k': /* --keylength <length> */ { div_t q; @@ -561,45 +671,56 @@ int main(int argc, char **argv) continue; } - case 'D': /* --days */ - if (optarg == NULL || !isdigit(optarg[0])) - usage("missing number of days"); - { - char *endptr; - long days = strtol(optarg, &endptr, 0); + case 'D': /* --days */ + if (optarg == NULL || !isdigit(optarg[0])) + { + usage("missing number of days"); + } + else + { + char *endptr; + long days = strtol(optarg, &endptr, 0); - if (*endptr != '\0' || endptr == optarg - || days <= 0) - usage("<days> must be a positive number"); - validity = 24*3600*days; - } - continue; + if (*endptr != '\0' || endptr == optarg + || days <= 0) + usage("<days> must be a positive number"); + validity = 24*3600*days; + } + continue; - case 'S': /* --startdate */ - if (optarg == NULL || strlen(optarg) != 13 || optarg[12] != 'Z') - usage("date format must be YYMMDDHHMMSSZ"); - { - chunk_t date = { optarg, 13 }; - notBefore = asn1_to_time(&date, ASN1_UTCTIME); - } - continue; + case 'S': /* --startdate */ + if (optarg == NULL || strlen(optarg) != 13 || optarg[12] != 'Z') + { + usage("date format must be YYMMDDHHMMSSZ"); + } + else + { + chunk_t date = { optarg, 13 }; + notBefore = asn1_to_time(&date, ASN1_UTCTIME); + } + continue; - case 'E': /* --enddate */ - if (optarg == NULL || strlen(optarg) != 13 || optarg[12] != 'Z') - usage("date format must be YYMMDDHHMMSSZ"); - { - chunk_t date = { optarg, 13 }; - notAfter = asn1_to_time(&date, ASN1_UTCTIME); - } - continue; + case 'E': /* --enddate */ + if (optarg == NULL || strlen(optarg) != 13 || optarg[12] != 'Z') + { + usage("date format must be YYMMDDHHMMSSZ"); + } + else + { + chunk_t date = { optarg, 13 }; + notAfter = asn1_to_time(&date, ASN1_UTCTIME); + } + continue; - case 'd': /* --dn */ - if (distinguishedName) - usage("only one distinguished name allowed"); - distinguishedName = optarg; - continue; + case 'd': /* --dn */ + if (distinguishedName) + { + usage("only one distinguished name allowed"); + } + distinguishedName = optarg; + continue; - case 's': /* --subjectAltName */ + case 's': /* --subjectAltName */ { char *value = strstr(optarg, "="); @@ -612,7 +733,7 @@ int main(int argc, char **argv) } if (strcaseeq("email", optarg) || - strcaseeq("dns", optarg) || + strcaseeq("dns", optarg) || strcaseeq("ip", optarg)) { subjectAltNames->insert_last(subjectAltNames, @@ -626,111 +747,137 @@ int main(int argc, char **argv) } } - case 'p': /* --password */ - if (challengePassword.len > 0) - { - usage("only one challenge password allowed"); - } - if (strcaseeq("%prompt", optarg)) - { - printf("Challenge password: "); - if (fgets(challenge_password_buffer, sizeof(challenge_password_buffer)-1, stdin)) + case 'p': /* --password */ + if (challengePassword.len > 0) + { + usage("only one challenge password allowed"); + } + if (strcaseeq("%prompt", optarg)) { - challengePassword.ptr = challenge_password_buffer; - /* discard the terminating '\n' from the input */ - challengePassword.len = strlen(challenge_password_buffer) - 1; + printf("Challenge password: "); + if (fgets(challenge_password_buffer, + sizeof(challenge_password_buffer) - 1, stdin)) + { + challengePassword.ptr = challenge_password_buffer; + /* discard the terminating '\n' from the input */ + challengePassword.len = strlen(challenge_password_buffer) - 1; + } + else + { + usage("challenge password could not be read"); + } } else { - usage("challenge password could not be read"); + challengePassword.ptr = optarg; + challengePassword.len = strlen(optarg); } - } - else - { - challengePassword.ptr = optarg; - challengePassword.len = strlen(optarg); - } - continue; - - case 'u': /* -- url */ - if (scep_url) - { - usage("only one URL argument allowed"); - } - scep_url = optarg; - continue; + continue; - case 'm': /* --method */ - if (strcaseeq("get", optarg)) - { - http_get_request = TRUE; - } - else if (strcaseeq("post", optarg)) - { - http_get_request = FALSE; - } - else - { - usage("invalid http request method specified"); - } - continue; + case 'u': /* -- url */ + if (scep_url) + { + usage("only one URL argument allowed"); + } + scep_url = optarg; + continue; - case 't': /* --interval */ - poll_interval = atoi(optarg); - if (poll_interval <= 0) - { - usage("invalid interval specified"); - } - continue; + case 'm': /* --method */ + if (strcaseeq("get", optarg)) + { + http_get_request = TRUE; + } + else if (strcaseeq("post", optarg)) + { + http_get_request = FALSE; + } + else + { + usage("invalid http request method specified"); + } + continue; - case 'x': /* --maxpolltime */ - max_poll_time = atoi(optarg); - continue; + case 't': /* --interval */ + poll_interval = atoi(optarg); + if (poll_interval <= 0) + { + usage("invalid interval specified"); + } + continue; - case 'a': /*--algorithm */ - { - const proposal_token_t *token; + case 'x': /* --maxpolltime */ + max_poll_time = atoi(optarg); + continue; - token = proposal_get_token(optarg, strlen(optarg)); - if (token == NULL || token->type != ENCRYPTION_ALGORITHM) + case 'a': /*--algorithm [<type>=]algo */ { - usage("invalid algorithm specified"); - } - pkcs7_symmetric_cipher = encryption_algorithm_to_oid( - token->algorithm, token->keysize); - if (pkcs7_symmetric_cipher == OID_UNKNOWN) - { - usage("unsupported encryption algorithm specified"); + const proposal_token_t *token; + char *type = optarg; + char *algo = strstr(optarg, "="); + + if (algo) + { + *algo = '\0'; + algo++; + } + else + { + type = "enc"; + algo = optarg; + } + + if (strcaseeq("enc", type)) + { + token = lib->proposal->get_token(lib->proposal, algo); + if (token == NULL || token->type != ENCRYPTION_ALGORITHM) + { + usage("invalid algorithm specified"); + } + pkcs7_symmetric_cipher = token->algorithm; + pkcs7_key_size = token->keysize; + if (encryption_algorithm_to_oid(token->algorithm, + token->keysize) == OID_UNKNOWN) + { + usage("unsupported encryption algorithm specified"); + } + } + else if (strcaseeq("dgst", type) || + strcaseeq("sig", type)) + { + hash_algorithm_t hash; + + token = lib->proposal->get_token(lib->proposal, algo); + if (token == NULL || token->type != INTEGRITY_ALGORITHM) + { + usage("invalid algorithm specified"); + } + hash = hasher_algorithm_from_integrity(token->algorithm, + NULL); + if (hash == OID_UNKNOWN) + { + usage("invalid algorithm specified"); + } + if (strcaseeq("dgst", type)) + { + pkcs7_digest_alg = hash; + } + else + { + pkcs10_signature_alg = hash; + } + } + else + { + usage("invalid --algorithm type"); + } + continue; } - continue; - } -#ifdef DEBUG - case 'A': /* --debug-all */ - base_debugging |= DBG_ALL; - continue; - case 'P': /* debug parsing */ - base_debugging |= DBG_PARSING; - continue; - case 'R': /* debug raw */ - base_debugging |= DBG_RAW; - continue; - case 'C': /* debug control */ - base_debugging |= DBG_CONTROL; - continue; - case 'M': /* debug control more */ - base_debugging |= DBG_CONTROLMORE; - continue; - case 'X': /* debug private */ - base_debugging |= DBG_PRIVATE; - continue; -#endif - default: - usage("unknown option"); + default: + usage("unknown option"); } /* break from loop */ break; } - cur_debugging = base_debugging; init_log("scepclient"); @@ -740,12 +887,12 @@ int main(int argc, char **argv) { exit_scepclient("plugin loading failed"); } - DBG1(DBG_LIB, " loaded plugins: %s", + DBG1(DBG_APP, " loaded plugins: %s", lib->plugins->loaded_plugins(lib->plugins)); if ((filetype_out == 0) && (!request_ca_certificate)) { - usage ("--out filetype required"); + usage("--out filetype required"); } if (request_ca_certificate && (filetype_out > 0 || filetype_in > 0)) { @@ -767,7 +914,8 @@ int main(int argc, char **argv) /* get CA cert */ if (request_ca_certificate) { - char *path = concatenate_paths(CA_CERT_PATH, file_out_ca_cert); + char ca_path[PATH_MAX]; + pkcs7_t *pkcs7; if (!scep_http_request(scep_url, chunk_empty, SCEP_GET_CA_CERT, http_get_request, &scep_response)) @@ -775,9 +923,77 @@ int main(int argc, char **argv) exit_scepclient("did not receive a valid scep response"); } - if (!chunk_write(scep_response, path, "ca cert", 0022, force)) + join_paths(ca_path, sizeof(ca_path), CA_CERT_PATH, file_out_ca_cert); + + pkcs7 = pkcs7_create_from_chunk(scep_response, 0); + if (!pkcs7 || !pkcs7->parse_signedData(pkcs7, NULL)) + { /* no PKCS#7 encoded CA+RA certificates, assume simple CA cert */ + DESTROY_IF(pkcs7); + if (!chunk_write(scep_response, ca_path, "ca cert", 0022, force)) + { + exit_scepclient("could not write ca cert file '%s'", ca_path); + } + } + else { - exit_scepclient("could not write ca cert file '%s'", path); + enumerator_t *enumerator; + certificate_t *cert; + int ra_certs = 0, ca_certs = 0; + int ra_index = 1, ca_index = 1; + + enumerator = pkcs7->create_certificate_enumerator(pkcs7); + while (enumerator->enumerate(enumerator, &cert)) + { + x509_t *x509 = (x509_t*)cert; + if (x509->get_flags(x509) & X509_CA) + { + ca_certs++; + } + else + { + ra_certs++; + } + } + enumerator->destroy(enumerator); + + enumerator = pkcs7->create_certificate_enumerator(pkcs7); + while (enumerator->enumerate(enumerator, &cert)) + { + x509_t *x509 = (x509_t*)cert; + bool ca_cert = x509->get_flags(x509) & X509_CA; + char cert_path[PATH_MAX], *path = ca_path; + + if (ca_cert && ca_certs > 1) + { + add_path_suffix(cert_path, sizeof(cert_path), ca_path, + "-%.1d", ca_index++); + path = cert_path; + } + else if (!ca_cert) + { /* use CA name as base for RA certs */ + if (ra_certs > 1) + { + add_path_suffix(cert_path, sizeof(cert_path), ca_path, + "-ra-%.1d", ra_index++); + } + else + { + add_path_suffix(cert_path, sizeof(cert_path), ca_path, + "-ra"); + } + path = cert_path; + } + + if (!cert->get_encoding(cert, CERT_ASN1_DER, &encoding) || + !chunk_write(encoding, path, + ca_cert ? "ca cert" : "ra cert", 0022, force)) + { + exit_scepclient("could not write cert file '%s'", path); + } + chunk_free(&encoding); + } + enumerator->destroy(enumerator); + pkcs7->destroy(pkcs7); } exit_scepclient(NULL); /* no further output required */ } @@ -787,7 +1003,9 @@ int main(int argc, char **argv) */ if (filetype_in & PKCS1) /* load an RSA key pair from file */ { - char *path = concatenate_paths(PRIVATE_KEY_PATH, file_in_pkcs1); + char path[PATH_MAX]; + + join_paths(path, sizeof(path), PRIVATE_KEY_PATH, file_in_pkcs1); private_key = lib->creds->create(lib->creds, CRED_PRIVATE_KEY, KEY_RSA, BUILD_FROM_FILE, path, BUILD_END); @@ -807,8 +1025,8 @@ int main(int argc, char **argv) /* check for minimum key length */ if (private_key->get_keysize(private_key) < RSA_MIN_OCTETS / BITS_PER_BYTE) { - exit_scepclient("length of RSA key has to be at least %d bits" - ,RSA_MIN_OCTETS * BITS_PER_BYTE); + exit_scepclient("length of RSA key has to be at least %d bits", + RSA_MIN_OCTETS * BITS_PER_BYTE); } /* @@ -816,13 +1034,19 @@ int main(int argc, char **argv) */ if (filetype_in & PKCS10) { - /* user wants to load a pkcs10 request - * operation is not yet supported - * would require a PKCS#10 parsing function + char path[PATH_MAX]; - pkcs10 = pkcs10_read_from_file(file_in_pkcs10); + join_paths(path, sizeof(path), REQ_PATH, file_in_pkcs10); - */ + pkcs10_req = lib->creds->create(lib->creds, CRED_CERTIFICATE, + CERT_PKCS10_REQUEST, BUILD_FROM_FILE, + path, BUILD_END); + if (!pkcs10_req) + { + exit_scepclient("could not read certificate request '%s'", path); + } + subject = pkcs10_req->get_subject(pkcs10_req); + subject = subject->clone(subject); } else { @@ -840,41 +1064,39 @@ int main(int argc, char **argv) distinguishedName = buf; } - DBG(DBG_CONTROL, - DBG_log("dn: '%s'", distinguishedName); - ) + DBG2(DBG_APP, "dn: '%s'", distinguishedName); subject = identification_create_from_string(distinguishedName); if (subject->get_type(subject) != ID_DER_ASN1_DN) { exit_scepclient("parsing of distinguished name failed"); } - DBG(DBG_CONTROL, - DBG_log("building pkcs10 object:") - ) + DBG2(DBG_APP, "building pkcs10 object:"); pkcs10_req = lib->creds->create(lib->creds, CRED_CERTIFICATE, - CERT_PKCS10_REQUEST, - BUILD_SIGNING_KEY, private_key, - BUILD_SUBJECT, subject, - BUILD_SUBJECT_ALTNAMES, subjectAltNames, - BUILD_CHALLENGE_PWD, challengePassword, - BUILD_DIGEST_ALG, pkcs10_signature_alg, - BUILD_END); + CERT_PKCS10_REQUEST, + BUILD_SIGNING_KEY, private_key, + BUILD_SUBJECT, subject, + BUILD_SUBJECT_ALTNAMES, subjectAltNames, + BUILD_CHALLENGE_PWD, challengePassword, + BUILD_DIGEST_ALG, pkcs10_signature_alg, + BUILD_END); if (!pkcs10_req) { exit_scepclient("generating pkcs10 request failed"); } - pkcs10_req->get_encoding(pkcs10_req, CERT_ASN1_DER, &pkcs10_encoding); - fingerprint = scep_generate_pkcs10_fingerprint(pkcs10_encoding); - plog(" fingerprint: %s", fingerprint.ptr); } + pkcs10_req->get_encoding(pkcs10_req, CERT_ASN1_DER, &pkcs10_encoding); + fingerprint = scep_generate_pkcs10_fingerprint(pkcs10_encoding); + DBG1(DBG_APP, " fingerprint: %s", fingerprint.ptr); /* * output of PKCS#10 file */ if (filetype_out & PKCS10) { - char *path = concatenate_paths(REQ_PATH, file_out_pkcs10); + char path[PATH_MAX]; + + join_paths(path, sizeof(path), REQ_PATH, file_out_pkcs10); if (!chunk_write(pkcs10_encoding, path, "pkcs10", 0022, force)) { @@ -893,11 +1115,11 @@ int main(int argc, char **argv) */ if (filetype_out & PKCS1) { - char *path = concatenate_paths(PRIVATE_KEY_PATH, file_out_pkcs1); + char path[PATH_MAX]; + + join_paths(path, sizeof(path), PRIVATE_KEY_PATH, file_out_pkcs1); - DBG(DBG_CONTROL, - DBG_log("building pkcs1 object:") - ) + DBG2(DBG_APP, "building pkcs1 object:"); if (!private_key->get_encoding(private_key, PRIVKEY_ASN1_DER, &pkcs1) || !chunk_write(pkcs1, path, "pkcs1", 0066, force)) { @@ -912,24 +1134,41 @@ int main(int argc, char **argv) } scep_generate_transaction_id(public_key, &transID, &serialNumber); - plog(" transaction ID: %.*s", (int)transID.len, transID.ptr); - - notBefore = notBefore ? notBefore : time(NULL); - notAfter = notAfter ? notAfter : (notBefore + validity); - - /* generate a self-signed X.509 certificate */ - x509_signer = lib->creds->create(lib->creds, CRED_CERTIFICATE, CERT_X509, - BUILD_SIGNING_KEY, private_key, - BUILD_PUBLIC_KEY, public_key, - BUILD_SUBJECT, subject, - BUILD_NOT_BEFORE_TIME, notBefore, - BUILD_NOT_AFTER_TIME, notAfter, - BUILD_SERIAL, serialNumber, - BUILD_SUBJECT_ALTNAMES, subjectAltNames, - BUILD_END); - if (!x509_signer) + DBG1(DBG_APP, " transaction ID: %.*s", (int)transID.len, transID.ptr); + + /* + * read or generate self-signed X.509 certificate + */ + if (filetype_in & CERT_SELF) + { + char path[PATH_MAX]; + + join_paths(path, sizeof(path), HOST_CERT_PATH, file_in_cert_self); + + x509_signer = lib->creds->create(lib->creds, CRED_CERTIFICATE, CERT_X509, + BUILD_FROM_FILE, path, BUILD_END); + if (!x509_signer) + { + exit_scepclient("could not read certificate file '%s'", path); + } + } + else { - exit_scepclient("generating certificate failed"); + notBefore = notBefore ? notBefore : time(NULL); + notAfter = notAfter ? notAfter : (notBefore + validity); + x509_signer = lib->creds->create(lib->creds, CRED_CERTIFICATE, CERT_X509, + BUILD_SIGNING_KEY, private_key, + BUILD_PUBLIC_KEY, public_key, + BUILD_SUBJECT, subject, + BUILD_NOT_BEFORE_TIME, notBefore, + BUILD_NOT_AFTER_TIME, notAfter, + BUILD_SERIAL, serialNumber, + BUILD_SUBJECT_ALTNAMES, subjectAltNames, + BUILD_END); + if (!x509_signer) + { + exit_scepclient("generating certificate failed"); + } } /* @@ -937,7 +1176,9 @@ int main(int argc, char **argv) */ if (filetype_out & CERT_SELF) { - char *path = concatenate_paths(HOST_CERT_PATH, file_out_cert_self); + char path[PATH_MAX]; + + join_paths(path, sizeof(path), HOST_CERT_PATH, file_out_cert_self); if (!x509_signer->get_encoding(x509_signer, CERT_ASN1_DER, &encoding)) { @@ -960,7 +1201,9 @@ int main(int argc, char **argv) * load ca encryption certificate */ { - char *path = concatenate_paths(CA_CERT_PATH, file_in_cacert_enc); + char path[PATH_MAX]; + + join_paths(path, sizeof(path), CA_CERT_PATH, file_in_cacert_enc); x509_ca_enc = lib->creds->create(lib->creds, CRED_CERTIFICATE, CERT_X509, BUILD_FROM_FILE, path, BUILD_END); @@ -985,13 +1228,15 @@ int main(int argc, char **argv) } else { - DBG(DBG_CONTROL, - DBG_log("building pkcs7 request") - ) + DBG2(DBG_APP, "building pkcs7 request"); pkcs7 = scep_build_request(pkcs10_encoding, - transID, SCEP_PKCSReq_MSG, - x509_ca_enc, pkcs7_symmetric_cipher, - x509_signer, pkcs7_digest_alg, private_key); + transID, SCEP_PKCSReq_MSG, x509_ca_enc, + pkcs7_symmetric_cipher, pkcs7_key_size, + x509_signer, pkcs7_digest_alg, private_key); + if (!pkcs7.ptr) + { + exit_scepclient("failed to build pkcs7 request"); + } } /* @@ -999,11 +1244,14 @@ int main(int argc, char **argv) */ if (filetype_out & PKCS7) { - char *path = concatenate_paths(REQ_PATH, file_out_pkcs7); + char path[PATH_MAX]; + + join_paths(path, sizeof(path), REQ_PATH, file_out_pkcs7); if (!chunk_write(pkcs7, path, "pkcs7 encrypted request", 0022, force)) + { exit_scepclient("could not write pkcs7 file '%s'", path); -; + } filetype_out &= ~PKCS7; /* delete PKCS7 flag */ } @@ -1020,14 +1268,12 @@ int main(int argc, char **argv) bool stored = FALSE; certificate_t *cert; enumerator_t *enumerator; - char *path = concatenate_paths(CA_CERT_PATH, file_in_cacert_sig); + char path[PATH_MAX]; time_t poll_start = 0; + pkcs7_t *data = NULL; + scep_attributes_t attrs = empty_scep_attributes; - linked_list_t *certs = linked_list_create(); - chunk_t envelopedData = chunk_empty; - chunk_t certData = chunk_empty; - contentInfo_t data = empty_contentInfo; - scep_attributes_t attrs = empty_scep_attributes; + join_paths(path, sizeof(path), CA_CERT_PATH, file_in_cacert_sig); x509_ca_sig = lib->creds->create(lib->creds, CRED_CERTIFICATE, CERT_X509, BUILD_FROM_FILE, path, BUILD_END); @@ -1037,12 +1283,12 @@ int main(int argc, char **argv) } if (!scep_http_request(scep_url, pkcs7, SCEP_PKI_OPERATION, - http_get_request, &scep_response)) + http_get_request, &scep_response)) { exit_scepclient("did not receive a valid scep response"); } - ugh = scep_parse_response(scep_response, transID, &data, &attrs - , x509_ca_sig); + ugh = scep_parse_response(scep_response, transID, &data, &attrs, + x509_ca_sig); if (ugh != NULL) { exit_scepclient(ugh); @@ -1053,8 +1299,8 @@ int main(int argc, char **argv) { identification_t *issuer = x509_ca_sig->get_subject(x509_ca_sig); - plog(" scep request pending, polling every %d seconds" - , poll_interval); + DBG1(DBG_APP, " scep request pending, polling every %d seconds", + poll_interval); poll_start = time_monotonic(NULL); issuerAndSubject = asn1_wrap(ASN1_SEQUENCE, "cc", issuer->get_encoding(issuer), @@ -1062,36 +1308,38 @@ int main(int argc, char **argv) } while (attrs.pkiStatus == SCEP_PENDING) { - if (max_poll_time > 0 - && (time_monotonic(NULL) - poll_start >= max_poll_time)) + if (max_poll_time > 0 && + (time_monotonic(NULL) - poll_start >= max_poll_time)) { exit_scepclient("maximum poll time reached: %d seconds" , max_poll_time); } - DBG(DBG_CONTROL, - DBG_log("going to sleep for %d seconds", poll_interval) - ) + DBG2(DBG_APP, "going to sleep for %d seconds", poll_interval); sleep(poll_interval); free(scep_response.ptr); + data->destroy(data); - DBG(DBG_CONTROL, - DBG_log("fingerprint: %.*s", (int)fingerprint.len, fingerprint.ptr); - DBG_log("transaction ID: %.*s", (int)transID.len, transID.ptr) - ) + DBG2(DBG_APP, "fingerprint: %.*s", + (int)fingerprint.len, fingerprint.ptr); + DBG2(DBG_APP, "transaction ID: %.*s", + (int)transID.len, transID.ptr); chunk_free(&getCertInitial); - getCertInitial = scep_build_request(issuerAndSubject - , transID, SCEP_GetCertInitial_MSG - , x509_ca_enc, pkcs7_symmetric_cipher - , x509_signer, pkcs7_digest_alg, private_key); - + getCertInitial = scep_build_request(issuerAndSubject, + transID, SCEP_GetCertInitial_MSG, x509_ca_enc, + pkcs7_symmetric_cipher, pkcs7_key_size, + x509_signer, pkcs7_digest_alg, private_key); + if (!getCertInitial.ptr) + { + exit_scepclient("failed to build scep request"); + } if (!scep_http_request(scep_url, getCertInitial, SCEP_PKI_OPERATION, http_get_request, &scep_response)) { exit_scepclient("did not receive a valid scep response"); } - ugh = scep_parse_response(scep_response, transID, &data, &attrs - , x509_ca_sig); + ugh = scep_parse_response(scep_response, transID, &data, &attrs, + x509_ca_sig); if (ugh != NULL) { exit_scepclient(ugh); @@ -1100,31 +1348,25 @@ int main(int argc, char **argv) if (attrs.pkiStatus != SCEP_SUCCESS) { + data->destroy(data); exit_scepclient("reply status is not 'SUCCESS'"); } - envelopedData = data.content; - - if (data.type != OID_PKCS7_DATA - || !asn1_parse_simple_object(&envelopedData, ASN1_OCTET_STRING, 0, "data")) - { - exit_scepclient("contentInfo is not of type 'data'"); - } - if (!pkcs7_parse_envelopedData(envelopedData, &certData - , serialNumber, private_key)) + if (!data->parse_envelopedData(data, serialNumber, private_key)) { + data->destroy(data); exit_scepclient("could not decrypt envelopedData"); } - if (!pkcs7_parse_signedData(certData, NULL, certs, NULL, NULL)) + if (!data->parse_signedData(data, NULL)) { + data->destroy(data); exit_scepclient("error parsing the scep response"); } - chunk_free(&certData); /* store the end entity certificate */ - path = concatenate_paths(HOST_CERT_PATH, file_out_cert); + join_paths(path, sizeof(path), HOST_CERT_PATH, file_out_cert); - enumerator = certs->create_enumerator(certs); + enumerator = data->create_certificate_enumerator(data); while (enumerator->enumerate(enumerator, &cert)) { x509_t *x509 = (x509_t*)cert; @@ -1144,7 +1386,8 @@ int main(int argc, char **argv) stored = TRUE; } } - certs->destroy_offset(certs, offsetof(certificate_t, destroy)); + enumerator->destroy(enumerator); + data->destroy(data); filetype_out &= ~CERT; /* delete CERT flag */ } |