/* * Copyright (C) 2012-2017 Tobias Brunner * Copyright (C) 2009 Martin Willi * HSR 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 . * * 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. */ #define _GNU_SOURCE #include "command.h" #include "pki.h" #include #include #include #include #include #include /** * Convert a form string to a encoding type */ bool get_form(char *form, cred_encoding_type_t *enc, credential_type_t type) { if (streq(form, "der")) { switch (type) { case CRED_CERTIFICATE: *enc = CERT_ASN1_DER; return TRUE; case CRED_PRIVATE_KEY: *enc = PRIVKEY_ASN1_DER; return TRUE; case CRED_PUBLIC_KEY: /* der encoded keys usually contain the complete * SubjectPublicKeyInfo */ *enc = PUBKEY_SPKI_ASN1_DER; return TRUE; default: return FALSE; } } else if (streq(form, "pem")) { switch (type) { case CRED_CERTIFICATE: *enc = CERT_PEM; return TRUE; case CRED_PRIVATE_KEY: *enc = PRIVKEY_PEM; return TRUE; case CRED_PUBLIC_KEY: *enc = PUBKEY_PEM; return TRUE; default: return FALSE; } } else if (streq(form, "pgp")) { switch (type) { case CRED_PRIVATE_KEY: *enc = PRIVKEY_PGP; return TRUE; case CRED_PUBLIC_KEY: *enc = PUBKEY_PGP; return TRUE; default: return FALSE; } } else if (streq(form, "dnskey")) { switch (type) { case CRED_PUBLIC_KEY: *enc = PUBKEY_DNSKEY; return TRUE; default: return FALSE; } } else if (streq(form, "sshkey")) { switch (type) { case CRED_PUBLIC_KEY: *enc = PUBKEY_SSHKEY; return TRUE; default: return FALSE; } } return FALSE; } /** * Convert a time string to struct tm using strptime format */ static bool convert_time(char *str, char *format, struct tm *tm) { #ifdef HAVE_STRPTIME char *end; if (!format) { format = "%d.%m.%y %T"; } end = strptime(str, format, tm); if (end == NULL || *end != '\0') { return FALSE; } return TRUE; #else /* !HAVE_STRPTIME */ if (format) { fprintf(stderr, "custom datetime string format not supported\n"); return FALSE; } if (sscanf(str, "%d.%d.%d %d:%d:%d", &tm->tm_mday, &tm->tm_mon, &tm->tm_year, &tm->tm_hour, &tm->tm_min, &tm->tm_sec) != 6) { return FALSE; } /* strptime() interprets two-digit years > 68 as 19xx, do the same here. * mktime() expects years based on 1900 */ if (tm->tm_year <= 68) { tm->tm_year += 100; } else if (tm->tm_year >= 1900) { /* looks like four digits? */ tm->tm_year -= 1900; } /* month is specified from 0-11 */ tm->tm_mon--; /* automatically detect daylight saving time */ tm->tm_isdst = -1; return TRUE; #endif /* !HAVE_STRPTIME */ } /** * 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; now = time(NULL); localtime_r(&now, &tm); if (nbstr) { if (!convert_time(nbstr, format, &tm)) { return FALSE; } } *nb = mktime(&tm); if (*nb == -1) { return FALSE; } localtime_r(&now, &tm); if (nastr) { if (!convert_time(nastr, format, &tm)) { return FALSE; } } *na = mktime(&tm); if (*na == -1) { return FALSE; } if (!nbstr && nastr) { *nb = *na - span; } else if (!nastr) { *na = *nb + span; } return TRUE; } /** * Set output file mode appropriate for credential encoding form on Windows */ void set_file_mode(FILE *stream, cred_encoding_type_t enc) { #ifdef WIN32 int fd; switch (enc) { case CERT_PEM: case PRIVKEY_PEM: case PUBKEY_PEM: /* keep default text mode */ return; default: /* switch to binary mode */ break; } fd = fileno(stream); if (fd != -1) { _setmode(fd, _O_BINARY); } #endif } /** * Determine a default hash algorithm for the given key */ static hash_algorithm_t get_default_digest(private_key_t *private) { enumerator_t *enumerator; signature_params_t *params; hash_algorithm_t alg = HASH_UNKNOWN; enumerator = signature_schemes_for_key(private->get_type(private), private->get_keysize(private)); if (enumerator->enumerate(enumerator, ¶ms)) { alg = hasher_from_signature_scheme(params->scheme, params->params); } enumerator->destroy(enumerator); /* default to SHA-256 */ return alg == HASH_UNKNOWN ? HASH_SHA256 : alg; } /* * Described in header */ signature_params_t *get_signature_scheme(private_key_t *private, hash_algorithm_t digest, bool pss) { signature_params_t *scheme; if (digest == HASH_UNKNOWN) { digest = get_default_digest(private); } if (private->get_type(private) == KEY_RSA && pss) { rsa_pss_params_t pss_params = { .hash = digest, .mgf1_hash = digest, .salt_len = RSA_PSS_SALT_LEN_DEFAULT, }; signature_params_t pss_scheme = { .scheme = SIGN_RSA_EMSA_PSS, .params = &pss_params, }; scheme = signature_params_clone(&pss_scheme); } else { INIT(scheme, .scheme = signature_scheme_from_oid( hasher_signature_algorithm_to_oid(digest, private->get_type(private))), ); } return scheme; } /* * Described in header */ traffic_selector_t* parse_ts(char *str) { ts_type_t type = TS_IPV4_ADDR_RANGE; char *to, from[64]; if (strchr(str, ':')) { type = TS_IPV6_ADDR_RANGE; } to = strchr(str, '-'); if (to) { snprintf(from, sizeof(from), "%.*s", (int)(to - str), str); to++; return traffic_selector_create_from_string(0, type, from, 0, to, 65535); } return traffic_selector_create_from_cidr(str, 0, 0, 65535); } /** * Callback credential set pki uses */ static callback_cred_t *cb_set; /** * Credential set to cache entered secrets */ static mem_cred_t *cb_creds; static shared_key_type_t prompted; /** * Callback function to receive credentials */ static shared_key_t* cb(void *data, shared_key_type_t type, identification_t *me, identification_t *other, id_match_t *match_me, id_match_t *match_other) { char buf[64], *label, *secret = NULL; shared_key_t *shared; if (prompted == type) { return NULL; } switch (type) { case SHARED_PIN: label = "Smartcard PIN"; break; case SHARED_PRIVATE_KEY_PASS: label = "Private key passphrase"; break; default: return NULL; } snprintf(buf, sizeof(buf), "%s: ", label); #ifdef HAVE_GETPASS secret = getpass(buf); #endif if (secret && strlen(secret)) { prompted = type; if (match_me) { *match_me = ID_MATCH_PERFECT; } if (match_other) { *match_other = ID_MATCH_NONE; } shared = shared_key_create(type, chunk_clone(chunk_from_str(secret))); /* cache password in case it is required more than once */ cb_creds->add_shared(cb_creds, shared, NULL); return shared->get_ref(shared); } return NULL; } /** * Register PIN/Passphrase callback function */ static void add_callback() { cb_set = callback_cred_create_shared(cb, NULL); lib->credmgr->add_set(lib->credmgr, &cb_set->set); cb_creds = mem_cred_create(); lib->credmgr->add_set(lib->credmgr, &cb_creds->set); } /** * Unregister PIN/Passphrase callback function */ static void remove_callback() { lib->credmgr->remove_set(lib->credmgr, &cb_creds->set); cb_creds->destroy(cb_creds); lib->credmgr->remove_set(lib->credmgr, &cb_set->set); cb_set->destroy(cb_set); } /** * Library initialization and operation parsing */ int main(int argc, char *argv[]) { atexit(library_deinit); if (!library_init(NULL, "pki")) { exit(SS_RC_LIBSTRONGSWAN_INTEGRITY); } if (lib->integrity && !lib->integrity->check_file(lib->integrity, "pki", argv[0])) { fprintf(stderr, "integrity check of pki failed\n"); exit(SS_RC_DAEMON_INTEGRITY); } if (!lib->plugins->load(lib->plugins, lib->settings->get_str(lib->settings, "pki.load", PLUGINS))) { exit(SS_RC_INITIALIZATION_FAILED); } add_callback(); atexit(remove_callback); return command_dispatch(argc, argv); }