427 lines
8.3 KiB
C
427 lines
8.3 KiB
C
/*
|
|
* 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 <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.
|
|
*/
|
|
|
|
#define _GNU_SOURCE
|
|
#include "command.h"
|
|
#include "pki.h"
|
|
|
|
#include <time.h>
|
|
#include <unistd.h>
|
|
#include <fcntl.h>
|
|
|
|
#include <utils/debug.h>
|
|
#include <credentials/sets/mem_cred.h>
|
|
#include <credentials/sets/callback_cred.h>
|
|
|
|
/**
|
|
* 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);
|
|
}
|