summaryrefslogtreecommitdiff
path: root/src/pluto/keys.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/pluto/keys.c')
-rw-r--r--src/pluto/keys.c1474
1 files changed, 0 insertions, 1474 deletions
diff --git a/src/pluto/keys.c b/src/pluto/keys.c
deleted file mode 100644
index c5adbfd11..000000000
--- a/src/pluto/keys.c
+++ /dev/null
@@ -1,1474 +0,0 @@
-/* mechanisms for preshared keys (public, private, and preshared secrets)
- * Copyright (C) 1998-2001 D. Hugh Redelmeier.
- * Copyright (C) 2009 Andreas Steffen - 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 <stddef.h>
-#include <stdlib.h>
-#include <string.h>
-#include <ctype.h>
-#include <unistd.h>
-#include <errno.h>
-#include <time.h>
-#include <sys/socket.h>
-#include <netinet/in.h>
-#include <arpa/inet.h>
-#include <resolv.h>
-#include <arpa/nameser.h> /* missing from <resolv.h> on old systems */
-#include <sys/queue.h>
-
-#ifdef HAVE_GLOB_H
-#include <glob.h>
-#ifndef GLOB_ABORTED
-# define GLOB_ABORTED GLOB_ABEND /* fix for old versions */
-#endif
-#endif
-
-#include <freeswan.h>
-
-#include <library.h>
-#include <asn1/asn1.h>
-#include <credentials/certificates/pgp_certificate.h>
-#include <credentials/sets/mem_cred.h>
-#include <credentials/sets/callback_cred.h>
-
-#include "constants.h"
-#include "defs.h"
-#include "x509.h"
-#include "certs.h"
-#include "smartcard.h"
-#include "connections.h"
-#include "state.h"
-#include "lex.h"
-#include "keys.h"
-#include "adns.h" /* needs <resolv.h> */
-#include "dnskey.h" /* needs keys.h and adns.h */
-#include "log.h"
-#include "whack.h" /* for RC_LOG_SERIOUS */
-#include "timer.h"
-#include "fetch.h"
-
-const char *shared_secrets_file = SHARED_SECRETS_FILE;
-
-
-typedef enum secret_kind_t secret_kind_t;
-
-enum secret_kind_t {
- SECRET_PSK,
- SECRET_PUBKEY,
- SECRET_XAUTH,
- SECRET_PIN
-};
-
-typedef struct secret_t secret_t;
-
-struct secret_t {
- linked_list_t *ids;
- secret_kind_t kind;
- union {
- chunk_t preshared_secret;
- private_key_t *private_key;
- smartcard_t *smartcard;
- } u;
- secret_t *next;
-};
-
-/*
- * free a public key struct
- */
-static void free_public_key(pubkey_t *pk)
-{
- DESTROY_IF(pk->id);
- DESTROY_IF(pk->public_key);
- DESTROY_IF(pk->issuer);
- free(pk->serial.ptr);
- free(pk);
-}
-
-secret_t *secrets = NULL;
-
-/**
- * Find the secret associated with the combination of me and the peer.
- */
-const secret_t* match_secret(identification_t *my_id, identification_t *his_id,
- secret_kind_t kind)
-{
- enum { /* bits */
- match_default = 0x01,
- match_him = 0x02,
- match_me = 0x04
- };
-
- unsigned int best_match = 0;
- secret_t *s, *best = NULL;
-
- for (s = secrets; s != NULL; s = s->next)
- {
- unsigned int match = 0;
-
- if (s->kind != kind)
- {
- continue;
- }
-
- if (s->ids->get_count(s->ids) == 0)
- {
- /* a default (signified by lack of ids):
- * accept if no more specific match found
- */
- match = match_default;
- }
- else
- {
- /* check if both ends match ids */
- enumerator_t *enumerator;
- identification_t *id;
-
- enumerator = s->ids->create_enumerator(s->ids);
- while (enumerator->enumerate(enumerator, &id))
- {
- if (my_id->equals(my_id, id))
- {
- match |= match_me;
- }
- if (his_id->equals(his_id, id))
- {
- match |= match_him;
- }
- }
- enumerator->destroy(enumerator);
-
- /* If our end matched the only id in the list,
- * default to matching any peer.
- * A more specific match will trump this.
- */
- if (match == match_me && s->ids->get_count(s->ids) == 1)
- {
- match |= match_default;
- }
- }
-
- switch (match)
- {
- case match_me:
- /* if this is an asymmetric (eg. public key) system,
- * allow this-side-only match to count, even if
- * there are other ids in the list.
- */
- if (kind != SECRET_PUBKEY)
- {
- break;
- }
- /* FALLTHROUGH */
- case match_default: /* default all */
- case match_me | match_default: /* default peer */
- case match_me | match_him: /* explicit */
- if (match == best_match)
- {
- /* two good matches are equally good: do they agree? */
- bool same = FALSE;
-
- switch (kind)
- {
- case SECRET_PSK:
- case SECRET_XAUTH:
- same = chunk_equals(s->u.preshared_secret,
- best->u.preshared_secret);
- break;
- case SECRET_PUBKEY:
- same = s->u.private_key->equals(s->u.private_key,
- best->u.private_key);
- break;
- default:
- bad_case(kind);
- }
- if (!same)
- {
- loglog(RC_LOG_SERIOUS, "multiple ipsec.secrets entries with "
- "distinct secrets match endpoints: first secret used");
- best = s; /* list is backwards: take latest in list */
- }
- }
- else if (match > best_match)
- {
- /* this is the best match so far */
- best_match = match;
- best = s;
- }
- }
- }
- return best;
-}
-
-/**
- * Retrieves an XAUTH secret primarily based on the user ID and
- * secondarily based on the server ID
- */
-bool get_xauth_secret(identification_t *user, identification_t *server,
- chunk_t *secret)
-{
- const secret_t *s;
-
- s = match_secret(user, server, SECRET_XAUTH);
- if (s)
- {
- *secret = chunk_clone(s->u.preshared_secret);
- return TRUE;
- }
- else
- {
- *secret = chunk_empty;
- return FALSE;
- }
-}
-
-/**
- * We match the ID (if none, the IP address). Failure is indicated by a NULL.
- */
-static const secret_t* get_secret(const connection_t *c, secret_kind_t kind)
-{
- identification_t *my_id, *his_id;
- const secret_t *best;
-
- my_id = c->spd.this.id;
-
- if (his_id_was_instantiated(c))
- {
- /* roadwarrior: replace him with 0.0.0.0 */
- his_id = identification_create_from_string("%any");
- }
- else if (kind == SECRET_PSK && (c->policy & (POLICY_PSK | POLICY_XAUTH_PSK)) &&
- ((c->kind == CK_TEMPLATE &&
- c->spd.that.id->get_type(c->spd.that.id) == ID_ANY) ||
- (c->kind == CK_INSTANCE && id_is_ipaddr(c->spd.that.id))))
- {
- /* roadwarrior: replace him with 0.0.0.0 */
- his_id = identification_create_from_string("%any");
- }
- else
- {
- his_id = c->spd.that.id->clone(c->spd.that.id);
- }
-
- best = match_secret(my_id, his_id, kind);
-
- his_id->destroy(his_id);
- return best;
-}
-
-/* find the appropriate preshared key (see get_secret).
- * Failure is indicated by a NULL pointer.
- * Note: the result is not to be freed by the caller.
- */
-const chunk_t* get_preshared_secret(const connection_t *c)
-{
- const secret_t *s = get_secret(c, SECRET_PSK);
-
- DBG(DBG_PRIVATE,
- if (s == NULL)
- DBG_log("no Preshared Key Found");
- else
- DBG_dump_chunk("Preshared Key", s->u.preshared_secret);
- )
- return s == NULL? NULL : &s->u.preshared_secret;
-}
-
-/* check the existence of a private key matching a public key contained
- * in an X.509 or OpenPGP certificate
- */
-bool has_private_key(cert_t *cert)
-{
- secret_t *s;
- bool has_key = FALSE;
- public_key_t *pub_key = cert->cert->get_public_key(cert->cert);
-
- for (s = secrets; s != NULL; s = s->next)
- {
- if (s->kind == SECRET_PUBKEY &&
- s->u.private_key->belongs_to(s->u.private_key, pub_key))
- {
- has_key = TRUE;
- break;
- }
- }
- pub_key->destroy(pub_key);
- return has_key;
-}
-
-/*
- * get the matching private key belonging to a given X.509 certificate
- */
-private_key_t* get_x509_private_key(const cert_t *cert)
-{
- public_key_t *public_key = cert->cert->get_public_key(cert->cert);
- private_key_t *private_key = NULL;
- secret_t *s;
-
- for (s = secrets; s != NULL; s = s->next)
- {
-
- if (s->kind == SECRET_PUBKEY &&
- s->u.private_key->belongs_to(s->u.private_key, public_key))
- {
- private_key = s->u.private_key;
- break;
- }
- }
- public_key->destroy(public_key);
- return private_key;
-}
-
-/* find the appropriate private key (see get_secret).
- * Failure is indicated by a NULL pointer.
- */
-private_key_t* get_private_key(const connection_t *c)
-{
- const secret_t *s, *best = NULL;
-
- /* is a certificate assigned to this connection? */
- if (c->spd.this.cert)
- {
- certificate_t *certificate;
- public_key_t *pub_key;
-
- certificate = c->spd.this.cert->cert;
- pub_key = certificate->get_public_key(certificate);
-
- for (s = secrets; s != NULL; s = s->next)
- {
- if (s->kind == SECRET_PUBKEY &&
- s->u.private_key->belongs_to(s->u.private_key, pub_key))
- {
- best = s;
- break; /* found the private key - no sense in searching further */
- }
- }
- pub_key->destroy(pub_key);
- }
- else
- {
- best = get_secret(c, SECRET_PUBKEY);
- }
- return best ? best->u.private_key : NULL;
-}
-
-/* digest a secrets file
- *
- * The file is a sequence of records. A record is a maximal sequence of
- * tokens such that the first, and only the first, is in the first column
- * of a line.
- *
- * Tokens are generally separated by whitespace and are key words, ids,
- * strings, or data suitable for ttodata(3). As a nod to convention,
- * a trailing ":" on what would otherwise be a token is taken as a
- * separate token. If preceded by whitespace, a "#" is taken as starting
- * a comment: it and the rest of the line are ignored.
- *
- * One kind of record is an include directive. It starts with "include".
- * The filename is the only other token in the record.
- * If the filename does not start with /, it is taken to
- * be relative to the directory containing the current file.
- *
- * The other kind of record describes a key. It starts with a
- * sequence of ids and ends with key information. Each id
- * is an IP address, a Fully Qualified Domain Name (which will immediately
- * be resolved), or @FQDN which will be left as a name.
- *
- * The key part can be in several forms.
- *
- * The old form of the key is still supported: a simple
- * quoted strings (with no escapes) is taken as a preshred key.
- *
- * The new form starts the key part with a ":".
- *
- * For Preshared Key, use the "PSK" keyword, and follow it by a string
- * or a data token suitable for ttodata(3).
- *
- * For RSA Private Key, use the "RSA" keyword, followed by a
- * brace-enclosed list of key field keywords and data values.
- * The data values are large integers to be decoded by ttodata(3).
- * The fields are a subset of those used by BIND 8.2 and have the
- * same names.
- */
-
-/* parse PSK from file */
-static err_t process_psk_secret(chunk_t *psk)
-{
- err_t ugh = NULL;
-
- if (*tok == '"' || *tok == '\'')
- {
- chunk_t secret = { tok + 1, flp->cur - tok -2 };
-
- *psk = chunk_clone(secret);
- (void) shift();
- }
- else
- {
- char buf[BUF_LEN]; /* limit on size of binary representation of key */
- size_t sz;
-
- ugh = ttodatav(tok, flp->cur - tok, 0, buf, sizeof(buf), &sz
- , diag_space, sizeof(diag_space), TTODATAV_SPACECOUNTS);
- if (ugh != NULL)
- {
- /* ttodata didn't like PSK data */
- ugh = builddiag("PSK data malformed (%s): %s", ugh, tok);
- }
- else
- {
- chunk_t secret = { buf, sz };
- *psk = chunk_clone(secret);
- (void) shift();
- }
- }
- return ugh;
-}
-
-typedef enum rsa_private_key_part_t rsa_private_key_part_t;
-
-enum rsa_private_key_part_t {
- RSA_PART_MODULUS = 0,
- RSA_PART_PUBLIC_EXPONENT = 1,
- RSA_PART_PRIVATE_EXPONENT = 2,
- RSA_PART_PRIME1 = 3,
- RSA_PART_PRIME2 = 4,
- RSA_PART_EXPONENT1 = 5,
- RSA_PART_EXPONENT2 = 6,
- RSA_PART_COEFFICIENT = 7
-};
-
-const char *rsa_private_key_part_names[] = {
- "Modulus",
- "PublicExponent",
- "PrivateExponent",
- "Prime1",
- "Prime2",
- "Exponent1",
- "Exponent2",
- "Coefficient"
-};
-
-/**
- * Parse fields of an RSA private key in BIND 8.2's representation
- * consistiong of a braced list of keyword and value pairs in required order.
- */
-static err_t process_rsa_secret(private_key_t **key)
-{
- chunk_t rsa_chunk[countof(rsa_private_key_part_names)];
- u_char buf[RSA_MAX_ENCODING_BYTES]; /* limit on size of binary representation of key */
- rsa_private_key_part_t part, p;
- size_t sz;
- err_t ugh;
-
- for (part = RSA_PART_MODULUS; part <= RSA_PART_COEFFICIENT; part++)
- {
- const char *keyword = rsa_private_key_part_names[part];
-
- if (!shift())
- {
- ugh = "premature end of RSA key";
- goto end;
- }
- if (!tokeqword(keyword))
- {
- ugh = builddiag("%s keyword not found where expected in RSA key"
- , keyword);
- goto end;
- }
- if (!(shift() && (!tokeq(":") || shift()))) /* ignore optional ":" */
- {
- ugh = "premature end of RSA key";
- goto end;
- }
- ugh = ttodatav(tok, flp->cur - tok, 0, buf, sizeof(buf), &sz,
- diag_space, sizeof(diag_space), TTODATAV_SPACECOUNTS);
- if (ugh)
- {
- ugh = builddiag("RSA data malformed (%s): %s", ugh, tok);
- goto end;
- }
- rsa_chunk[part] = chunk_create(buf, sz);
- rsa_chunk[part] = chunk_clone(rsa_chunk[part]);
- }
-
- /* We require an (indented) '}' and the end of the record.
- * We break down the test so that the diagnostic will be more helpful.
- * Some people don't seem to wish to indent the brace!
- */
- if (!shift() || !tokeq("}"))
- {
- ugh = "malformed end of RSA private key -- indented '}' required";
- goto end;
- }
- if (shift())
- {
- ugh = "malformed end of RSA private key -- unexpected token after '}'";
- goto end;
- }
-
- *key = lib->creds->create(lib->creds, CRED_PRIVATE_KEY, KEY_RSA,
- BUILD_RSA_MODULUS, rsa_chunk[RSA_PART_MODULUS],
- BUILD_RSA_PUB_EXP, rsa_chunk[RSA_PART_PUBLIC_EXPONENT],
- BUILD_RSA_PRIV_EXP, rsa_chunk[RSA_PART_PRIVATE_EXPONENT],
- BUILD_RSA_PRIME1, rsa_chunk[RSA_PART_PRIME1],
- BUILD_RSA_PRIME2, rsa_chunk[RSA_PART_PRIME2],
- BUILD_RSA_EXP1, rsa_chunk[RSA_PART_EXPONENT1],
- BUILD_RSA_EXP2, rsa_chunk[RSA_PART_EXPONENT2],
- BUILD_RSA_COEFF, rsa_chunk[RSA_PART_COEFFICIENT],
- BUILD_END);
-
- if (*key == NULL)
- {
- ugh = "parsing of RSA private key failed";
- }
-
-end:
- /* clean up and return */
- for (p = RSA_PART_MODULUS ; p < part; p++)
- {
- chunk_clear(&rsa_chunk[p]);
- }
- return ugh;
-}
-
-/* struct used to prompt for a secret passphrase
- * from a console with file descriptor fd
- */
-typedef struct {
- char secret[PROMPT_PASS_LEN+1];
- bool prompt;
- int fd;
- int try;
-} prompt_pass_t;
-
-/**
- * Passphrase callback to read from whack fd
- */
-static shared_key_t* whack_pass_cb(prompt_pass_t *pass, shared_key_type_t type,
- identification_t *me, identification_t *other,
- id_match_t *match_me, id_match_t *match_other)
-{
- int n;
-
- if (type != SHARED_ANY && type != SHARED_PRIVATE_KEY_PASS)
- {
- return NULL;
- }
-
- if (pass->try > MAX_PROMPT_PASS_TRIALS)
- {
- whack_log(RC_LOG_SERIOUS, "invalid passphrase, too many trials");
- return NULL;
- }
- if (pass->try == 1)
- {
- whack_log(RC_ENTERSECRET, "need passphrase for 'private key'");
- }
- else
- {
- whack_log(RC_ENTERSECRET, "invalid passphrase, please try again");
- }
- pass->try++;
-
- n = read(pass->fd, pass->secret, PROMPT_PASS_LEN);
- if (n == -1)
- {
- whack_log(RC_LOG_SERIOUS, "read(whackfd) failed");
- return NULL;
- }
- pass->secret[n-1] = '\0';
-
- if (strlen(pass->secret) == 0)
- {
- whack_log(RC_LOG_SERIOUS, "no passphrase entered, aborted");
- return NULL;
- }
- if (match_me)
- {
- *match_me = ID_MATCH_PERFECT;
- }
- if (match_other)
- {
- *match_other = ID_MATCH_NONE;
- }
- return shared_key_create(SHARED_PRIVATE_KEY_PASS,
- chunk_clone(chunk_create(pass->secret, strlen(pass->secret))));
-}
-
-/**
- * Loads a PKCS#1 or PGP private key file
- */
-static private_key_t* load_private_key(char* filename, prompt_pass_t *pass,
- key_type_t type)
-{
- private_key_t *key = NULL;
- char *path;
-
- path = concatenate_paths(PRIVATE_KEY_PATH, filename);
- if (pass && pass->prompt && pass->fd != NULL_FD)
- { /* use passphrase callback */
- callback_cred_t *cb;
-
- cb = callback_cred_create_shared((void*)whack_pass_cb, pass);
- lib->credmgr->add_local_set(lib->credmgr, &cb->set);
-
- key = lib->creds->create(lib->creds, CRED_PRIVATE_KEY, type,
- BUILD_FROM_FILE, path, BUILD_END);
- lib->credmgr->remove_local_set(lib->credmgr, &cb->set);
- cb->destroy(cb);
- if (key)
- {
- whack_log(RC_SUCCESS, "valid passphrase");
- }
- }
- else if (pass)
- { /* use a given passphrase */
- mem_cred_t *mem;
- shared_key_t *shared;
-
- mem = mem_cred_create();
- lib->credmgr->add_local_set(lib->credmgr, &mem->set);
- shared = shared_key_create(SHARED_PRIVATE_KEY_PASS,
- chunk_clone(chunk_create(pass->secret, strlen(pass->secret))));
- mem->add_shared(mem, shared, NULL);
- key = lib->creds->create(lib->creds, CRED_PRIVATE_KEY, type,
- BUILD_FROM_FILE, path, BUILD_END);
- lib->credmgr->remove_local_set(lib->credmgr, &mem->set);
- mem->destroy(mem);
- }
- else
- { /* no passphrase */
- key = lib->creds->create(lib->creds, CRED_PRIVATE_KEY, type,
- BUILD_FROM_FILE, path, BUILD_END);
-
- }
- if (key)
- {
- plog(" loaded private key from '%s'", filename);
- }
- else
- {
- plog(" syntax error in private key file");
- }
- return key;
-}
-
-/**
- * process a key file protected with optional passphrase which can either be
- * read from ipsec.secrets or prompted for by using whack
- */
-static err_t process_keyfile(private_key_t **key, key_type_t type, int whackfd)
-{
- char filename[BUF_LEN];
- prompt_pass_t pass;
-
- memset(filename,'\0', BUF_LEN);
- memset(pass.secret,'\0', sizeof(pass.secret));
- pass.prompt = FALSE;
- pass.fd = whackfd;
- pass.try = 1;
-
- /* we expect the filename of a PKCS#1 private key file */
-
- if (*tok == '"' || *tok == '\'') /* quoted filename */
- memcpy(filename, tok+1, flp->cur - tok - 2);
- else
- memcpy(filename, tok, flp->cur - tok);
-
- if (shift())
- {
- /* we expect an appended passphrase or passphrase prompt*/
- if (tokeqword("%prompt"))
- {
- if (pass.fd == NULL_FD)
- {
- return "Private key file -- enter passphrase using 'ipsec secrets'";
- }
- pass.prompt = TRUE;
- }
- else
- {
- char *passphrase = tok;
- size_t len = flp->cur - passphrase;
-
- if (*tok == '"' || *tok == '\'') /* quoted passphrase */
- {
- passphrase++;
- len -= 2;
- }
- if (len > PROMPT_PASS_LEN)
- {
- return "Private key file -- passphrase exceeds 64 characters";
- }
- memcpy(pass.secret, passphrase, len);
- }
- if (shift())
- {
- return "Private key file -- unexpected token after passphrase";
- }
- }
- *key = load_private_key(filename, &pass, type);
-
- return *key ? NULL : "Private key file -- could not be loaded";
-}
-
-/**
- * Process pin read from ipsec.secrets or prompted for it using whack
- */
-static err_t process_pin(secret_t *s, int whackfd)
-{
- smartcard_t *sc;
- const char *pin_status = "no pin";
-
- s->kind = SECRET_PIN;
-
- /* looking for the smartcard keyword */
- if (!shift() || strncmp(tok, SCX_TOKEN, strlen(SCX_TOKEN)) != 0)
- return "PIN keyword must be followed by %smartcard<reader>:<id>";
-
- sc = scx_add(scx_parse_number_slot_id(tok + strlen(SCX_TOKEN)));
- s->u.smartcard = sc;
- scx_share(sc);
- if (sc->pin.ptr != NULL)
- {
- scx_release_context(sc);
- scx_free_pin(&sc->pin);
- }
- sc->valid = FALSE;
-
- if (!shift())
- return "PIN statement must be terminated either by <pin code>, %pinpad or %prompt";
-
- if (tokeqword("%prompt"))
- {
- shift();
- /* if whackfd exists, whack will be used to prompt for a pin */
- if (whackfd != NULL_FD)
- pin_status = scx_get_pin(sc, whackfd) ? "valid pin" : "invalid pin";
- else
- pin_status = "pin entry via prompt";
- }
- else if (tokeqword("%pinpad"))
- {
- chunk_t empty_pin = { "", 0 };
-
- shift();
-
- /* pin will be entered via pin pad during verification */
- sc->pin = chunk_clone(empty_pin);
- sc->pinpad = TRUE;
- sc->valid = TRUE;
- pin_status = "pin entry via pad";
- if (pkcs11_keep_state)
- {
- scx_verify_pin(sc);
- }
- }
- else
- {
- /* we read the pin directly from ipsec.secrets */
- err_t ugh = process_psk_secret(&sc->pin);
- if (ugh != NULL)
- return ugh;
- /* verify the pin */
- pin_status = scx_verify_pin(sc) ? "valid PIN" : "invalid PIN";
- }
-#ifdef SMARTCARD
- {
- char buf[BUF_LEN];
-
- if (sc->any_slot)
- snprintf(buf, BUF_LEN, "any slot");
- else
- snprintf(buf, BUF_LEN, "slot: %lu", sc->slot);
-
- plog(" %s for #%d (%s, id: %s)"
- , pin_status, sc->number, scx_print_slot(sc, ""), sc->id);
- }
-#else
- plog(" warning: SMARTCARD support is deactivated in pluto/Makefile!");
-#endif
- return NULL;
-}
-
-static void log_psk(char *label, secret_t *s)
-{
- int n = 0;
- char buf[BUF_LEN];
- enumerator_t *enumerator;
- identification_t *id;
-
- if (s->ids->get_count(s->ids) == 0)
- {
- n = snprintf(buf, BUF_LEN, "%%any");
- }
- else
- {
- enumerator = s->ids->create_enumerator(s->ids);
- while(enumerator->enumerate(enumerator, &id))
- {
- n += snprintf(buf + n, BUF_LEN - n, "%Y ", id);
- if (n >= BUF_LEN)
- {
- n = BUF_LEN - 1;
- break;
- }
- }
- enumerator->destroy(enumerator);
- }
- plog(" loaded %s secret for %.*s", label, n, buf);
-}
-
-static void process_secret(secret_t *s, int whackfd)
-{
- err_t ugh = NULL;
-
- s->kind = SECRET_PSK; /* default */
- if (tokeqword("psk"))
- {
- log_psk("PSK", s);
-
- /* preshared key: quoted string or ttodata format */
- ugh = !shift()? "unexpected end of record in PSK"
- : process_psk_secret(&s->u.preshared_secret);
- }
- else if (tokeqword("xauth"))
- {
- s->kind = SECRET_XAUTH;
- log_psk("XAUTH", s);
-
- /* xauth secret: quoted string or ttodata format */
- ugh = !shift()? "unexpected end of record in XAUTH"
- : process_psk_secret(&s->u.preshared_secret);
- }
- else if (tokeqword("rsa"))
- {
- /* RSA key: the fun begins.
- * A braced list of keyword and value pairs.
- */
- s->kind = SECRET_PUBKEY;
- if (!shift())
- {
- ugh = "bad RSA key syntax";
- }
- else if (tokeq("{"))
- {
- ugh = process_rsa_secret(&s->u.private_key);
- }
- else
- {
- ugh = process_keyfile(&s->u.private_key, KEY_RSA, whackfd);
- }
- }
- else if (tokeqword("ecdsa"))
- {
- s->kind = SECRET_PUBKEY;
- if (!shift())
- {
- ugh = "bad ECDSA key syntax";
- }
- else
- {
- ugh = process_keyfile(&s->u.private_key, KEY_ECDSA, whackfd);
- }
- }
- else if (tokeqword("pin"))
- {
- ugh = process_pin(s, whackfd);
- }
- else
- {
- ugh = builddiag("unrecognized key format: %s", tok);
- }
-
- if (ugh != NULL)
- {
- loglog(RC_LOG_SERIOUS, "\"%s\" line %d: %s"
- , flp->filename, flp->lino, ugh);
- s->ids->destroy_offset(s->ids, offsetof(identification_t, destroy));
- free(s);
- }
- else if (flushline("expected record boundary in key"))
- {
- /* gauntlet has been run: install new secret */
- lock_certs_and_keys("process_secret");
- s->next = secrets;
- secrets = s;
- unlock_certs_and_keys("process_secrets");
- }
-}
-
-static void process_secrets_file(const char *file_pat, int whackfd); /* forward declaration */
-
-static void process_secret_records(int whackfd)
-{
- /* read records from ipsec.secrets and load them into our table */
- for (;;)
- {
- (void)flushline(NULL); /* silently ditch leftovers, if any */
- if (flp->bdry == B_file)
- {
- break;
- }
- flp->bdry = B_none; /* eat the Record Boundary */
- (void)shift(); /* get real first token */
-
- if (tokeqword("include"))
- {
- /* an include directive */
- char fn[MAX_TOK_LEN]; /* space for filename (I hope) */
- char *p = fn;
- char *end_prefix = strrchr(flp->filename, '/');
-
- if (!shift())
- {
- loglog(RC_LOG_SERIOUS, "\"%s\" line %d: unexpected end of include directive"
- , flp->filename, flp->lino);
- continue; /* abandon this record */
- }
-
- /* if path is relative and including file's pathname has
- * a non-empty dirname, prefix this path with that dirname.
- */
- if (tok[0] != '/' && end_prefix != NULL)
- {
- size_t pl = end_prefix - flp->filename + 1;
-
- /* "clamp" length to prevent problems now;
- * will be rediscovered and reported later.
- */
- if (pl > sizeof(fn))
- {
- pl = sizeof(fn);
- }
- memcpy(fn, flp->filename, pl);
- p += pl;
- }
- if (flp->cur - tok >= &fn[sizeof(fn)] - p)
- {
- loglog(RC_LOG_SERIOUS, "\"%s\" line %d: include pathname too long"
- , flp->filename, flp->lino);
- continue; /* abandon this record */
- }
- strcpy(p, tok);
- (void) shift(); /* move to Record Boundary, we hope */
- if (flushline("ignoring malformed INCLUDE -- expected Record Boundary after filename"))
- {
- process_secrets_file(fn, whackfd);
- tok = NULL; /* correct, but probably redundant */
- }
- }
- else
- {
- /* expecting a list of indices and then the key info */
- secret_t *s = malloc_thing(secret_t);
-
- zero(s);
- s->ids = linked_list_create();
- s->kind = SECRET_PSK; /* default */
- s->u.preshared_secret = chunk_empty;
- s->next = NULL;
-
- for (;;)
- {
- if (tokeq(":"))
- {
- /* found key part */
- shift(); /* discard explicit separator */
- process_secret(s, whackfd);
- break;
- }
- else
- {
- identification_t *id;
-
- id = identification_create_from_string(tok);
- s->ids->insert_last(s->ids, id);
-
- if (!shift())
- {
- /* unexpected Record Boundary or EOF */
- loglog(RC_LOG_SERIOUS, "\"%s\" line %d: unexpected end"
- " of id list", flp->filename, flp->lino);
- s->ids->destroy_offset(s->ids,
- offsetof(identification_t, destroy));
- free(s);
- break;
- }
- }
- }
- }
- }
-}
-
-static int globugh(const char *epath, int eerrno)
-{
- log_errno_routine(eerrno, "problem with secrets file \"%s\"", epath);
- return 1; /* stop glob */
-}
-
-static void process_secrets_file(const char *file_pat, int whackfd)
-{
- struct file_lex_position pos;
- char **fnp;
-
- pos.depth = flp == NULL? 0 : flp->depth + 1;
-
- if (pos.depth > 10)
- {
- loglog(RC_LOG_SERIOUS, "preshared secrets file \"%s\" nested too deeply", file_pat);
- return;
- }
-
-#ifdef HAVE_GLOB_H
- /* do globbing */
- {
- glob_t globbuf;
- int r = glob(file_pat, GLOB_ERR, globugh, &globbuf);
-
- if (r != 0)
- {
- switch (r)
- {
- case GLOB_NOSPACE:
- loglog(RC_LOG_SERIOUS, "out of space processing secrets filename \"%s\"", file_pat);
- break;
- case GLOB_ABORTED:
- break; /* already logged */
- case GLOB_NOMATCH:
- loglog(RC_LOG_SERIOUS, "no secrets filename matched \"%s\"", file_pat);
- break;
- default:
- loglog(RC_LOG_SERIOUS, "unknown glob error %d", r);
- break;
- }
- globfree(&globbuf);
- return;
- }
-
- /* for each file... */
- for (fnp = globbuf.gl_pathv; *fnp != NULL; fnp++)
- {
- if (lexopen(&pos, *fnp, FALSE))
- {
- plog("loading secrets from \"%s\"", *fnp);
- flushline("file starts with indentation (continuation notation)");
- process_secret_records(whackfd);
- lexclose();
- }
- }
-
- globfree(&globbuf);
- }
-#else /* HAVE_GLOB_H */
- /* if glob(3) is not available, try to load pattern directly */
- if (lexopen(&pos, file_pat, FALSE))
- {
- plog("loading secrets from \"%s\"", file_pat);
- flushline("file starts with indentation (continuation notation)");
- process_secret_records(whackfd);
- lexclose();
- }
-#endif /* HAVE_GLOB_H */
-}
-
-void free_preshared_secrets(void)
-{
- lock_certs_and_keys("free_preshared_secrets");
-
- if (secrets != NULL)
- {
- secret_t *s, *ns;
-
- plog("forgetting secrets");
-
- for (s = secrets; s != NULL; s = ns)
- {
- ns = s->next;
- s->ids->destroy_offset(s->ids, offsetof(identification_t, destroy));
-
- switch (s->kind)
- {
- case SECRET_PSK:
- case SECRET_XAUTH:
- free(s->u.preshared_secret.ptr);
- break;
- case SECRET_PUBKEY:
- DESTROY_IF(s->u.private_key);
- break;
- case SECRET_PIN:
- scx_release(s->u.smartcard);
- break;
- default:
- bad_case(s->kind);
- }
- free(s);
- }
- secrets = NULL;
- }
-
- unlock_certs_and_keys("free_preshard_secrets");
-}
-
-void load_preshared_secrets(int whackfd)
-{
- free_preshared_secrets();
- (void) process_secrets_file(shared_secrets_file, whackfd);
-}
-
-/* public key machinery
- * Note: caller must set dns_auth_level.
- */
-
-pubkey_t* public_key_from_rsa(public_key_t *key)
-{
- pubkey_t *p = malloc_thing(pubkey_t);
-
- zero(p);
- p->id = identification_create_from_string("%any"); /* don't know, doesn't matter */
- p->issuer = NULL;
- p->serial = chunk_empty;
- p->public_key = key;
-
- /* note that we return a 1 reference count upon creation:
- * invariant: recount > 0.
- */
- p->refcnt = 1;
- return p;
-}
-
-/* Free a public key record.
- * As a convenience, this returns a pointer to next.
- */
-pubkey_list_t* free_public_keyentry(pubkey_list_t *p)
-{
- pubkey_list_t *nxt = p->next;
-
- if (p->key != NULL)
- {
- unreference_key(&p->key);
- }
- free(p);
- return nxt;
-}
-
-void free_public_keys(pubkey_list_t **keys)
-{
- while (*keys != NULL)
- {
- *keys = free_public_keyentry(*keys);
- }
-}
-
-/* root of chained public key list */
-
-pubkey_list_t *pubkeys = NULL; /* keys from ipsec.conf */
-
-void free_remembered_public_keys(void)
-{
- free_public_keys(&pubkeys);
-}
-
-/**
- * Transfer public keys from *keys list to front of pubkeys list
- */
-void transfer_to_public_keys(struct gw_info *gateways_from_dns
-#ifdef USE_KEYRR
-, pubkey_list_t **keys
-#endif /* USE_KEYRR */
-)
-{
- {
- struct gw_info *gwp;
-
- for (gwp = gateways_from_dns; gwp != NULL; gwp = gwp->next)
- {
- pubkey_list_t *pl = malloc_thing(pubkey_list_t);
-
- pl->key = gwp->key; /* note: this is a transfer */
- gwp->key = NULL; /* really, it is! */
- pl->next = pubkeys;
- pubkeys = pl;
- }
- }
-
-#ifdef USE_KEYRR
- {
- pubkey_list_t **pp = keys;
-
- while (*pp != NULL)
- {
- pp = &(*pp)->next;
- }
- *pp = pubkeys;
- pubkeys = *keys;
- *keys = NULL;
- }
-#endif /* USE_KEYRR */
-}
-
-
-static void install_public_key(pubkey_t *pk, pubkey_list_t **head)
-{
- pubkey_list_t *p = malloc_thing(pubkey_list_t);
-
- /* install new key at front */
- p->key = reference_key(pk);
- p->next = *head;
- *head = p;
-}
-
-void delete_public_keys(identification_t *id, key_type_t type,
- identification_t *issuer, chunk_t serial)
-{
- pubkey_list_t **pp, *p;
- pubkey_t *pk;
- key_type_t pk_type;
-
- for (pp = &pubkeys; (p = *pp) != NULL; )
- {
- pk = p->key;
- pk_type = pk->public_key->get_type(pk->public_key);
-
- if (id->equals(id, pk->id) && pk_type == type
- && (issuer == NULL || pk->issuer == NULL
- || issuer->equals(issuer, pk->issuer))
- && (serial.ptr == NULL || chunk_equals(serial, pk->serial)))
- {
- *pp = free_public_keyentry(p);
- }
- else
- {
- pp = &p->next;
- }
- }
-}
-
-pubkey_t* reference_key(pubkey_t *pk)
-{
- DBG(DBG_CONTROLMORE,
- DBG_log(" ref key: %p %p cnt %d '%Y'",
- pk, pk->public_key, pk->refcnt, pk->id)
- )
- pk->refcnt++;
- return pk;
-}
-
-void unreference_key(pubkey_t **pkp)
-{
- pubkey_t *pk = *pkp;
-
- if (pk == NULL)
- {
- return;
- }
-
- DBG(DBG_CONTROLMORE,
- DBG_log("unref key: %p %p cnt %d '%Y'",
- pk, pk->public_key, pk->refcnt, pk->id)
- )
-
- /* cancel out the pointer */
- *pkp = NULL;
-
- passert(pk->refcnt != 0);
- pk->refcnt--;
- if (pk->refcnt == 0)
- {
- free_public_key(pk);
- }
-}
-
-bool add_public_key(identification_t *id, enum dns_auth_level dns_auth_level,
- enum pubkey_alg alg, chunk_t rfc3110_key,
- pubkey_list_t **head)
-{
- public_key_t *key = NULL;
- pubkey_t *pk;
-
- /* first: algorithm-specific decoding of key chunk */
- switch (alg)
- {
- case PUBKEY_ALG_RSA:
- key = lib->creds->create(lib->creds, CRED_PUBLIC_KEY, KEY_RSA,
- BUILD_BLOB_DNSKEY, rfc3110_key,
- BUILD_END);
- if (key == NULL)
- {
- return FALSE;
- }
- break;
- default:
- bad_case(alg);
- }
-
- pk = malloc_thing(pubkey_t);
- zero(pk);
- pk->public_key = key;
- pk->id = id->clone(id);
- pk->dns_auth_level = dns_auth_level;
- pk->until_time = UNDEFINED_TIME;
- pk->issuer = NULL;
- pk->serial = chunk_empty;
- install_public_key(pk, head);
- return TRUE;
-}
-
-/**
- * Extract id and public key a certificate and insert it into a pubkeyrec
- */
-void add_public_key_from_cert(cert_t *cert , time_t until,
- enum dns_auth_level dns_auth_level)
-{
- certificate_t *certificate = cert->cert;
- identification_t *subject = certificate->get_subject(certificate);
- identification_t *issuer = NULL;
- identification_t *id;
- chunk_t serialNumber = chunk_empty;
- pubkey_t *pk;
- key_type_t pk_type;
-
- /* ID type: ID_DER_ASN1_DN (X.509 subject field) */
- pk = malloc_thing(pubkey_t);
- zero(pk);
- pk->public_key = certificate->get_public_key(certificate);
- pk_type = pk->public_key->get_type(pk->public_key);
- pk->id = subject->clone(subject);
- pk->dns_auth_level = dns_auth_level;
- pk->until_time = until;
- if (certificate->get_type(certificate) == CERT_X509)
- {
- x509_t *x509 = (x509_t*)certificate;
-
- issuer = certificate->get_issuer(certificate);
- serialNumber = x509->get_serial(x509);
- pk->issuer = issuer->clone(issuer);
- pk->serial = chunk_clone(serialNumber);
- }
- delete_public_keys(pk->id, pk_type, pk->issuer, pk->serial);
- install_public_key(pk, &pubkeys);
-
- if (certificate->get_type(certificate) == CERT_X509)
- {
- x509_t *x509 = (x509_t*)certificate;
- enumerator_t *enumerator;
-
- /* insert all subjectAltNames from X.509 certificates */
- enumerator = x509->create_subjectAltName_enumerator(x509);
- while (enumerator->enumerate(enumerator, &id))
- {
- if (id->get_type(id) != ID_ANY)
- {
- pk = malloc_thing(pubkey_t);
- zero(pk);
- pk->id = id->clone(id);
- pk->public_key = certificate->get_public_key(certificate);
- pk->dns_auth_level = dns_auth_level;
- pk->until_time = until;
- pk->issuer = issuer->clone(issuer);
- pk->serial = chunk_clone(serialNumber);
- delete_public_keys(pk->id, pk_type, pk->issuer, pk->serial);
- install_public_key(pk, &pubkeys);
- }
- }
- enumerator->destroy(enumerator);
- }
- else
- {
- pgp_certificate_t *pgp_cert = (pgp_certificate_t*)certificate;
- chunk_t fingerprint = pgp_cert->get_fingerprint(pgp_cert);
-
- /* add v3 or v4 PGP fingerprint */
- pk = malloc_thing(pubkey_t);
- zero(pk);
- pk->id = identification_create_from_encoding(ID_KEY_ID, fingerprint);
- pk->public_key = certificate->get_public_key(certificate);
- pk->dns_auth_level = dns_auth_level;
- pk->until_time = until;
- delete_public_keys(pk->id, pk_type, pk->issuer, pk->serial);
- install_public_key(pk, &pubkeys);
- }
-}
-
-/* when a X.509 certificate gets revoked, all instances of
- * the corresponding public key must be removed
- */
-void remove_x509_public_key(const cert_t *cert)
-{
- public_key_t *revoked_key = cert->cert->get_public_key(cert->cert);
- pubkey_list_t *p, **pp;
-
- p = pubkeys;
- pp = &pubkeys;
-
- while(p != NULL)
- {
- if (revoked_key->equals(revoked_key, p->key->public_key))
- {
- /* remove p from list and free memory */
- *pp = free_public_keyentry(p);
- loglog(RC_LOG_SERIOUS, "invalid public key deleted");
- }
- else
- {
- pp = &p->next;
- }
- p =*pp;
- }
- revoked_key->destroy(revoked_key);
-}
-
-/*
- * list all public keys in the chained list
- */
-void list_public_keys(bool utc)
-{
- pubkey_list_t *p = pubkeys;
- chunk_t serial;
-
- if (p != NULL)
- {
- whack_log(RC_COMMENT, " ");
- whack_log(RC_COMMENT, "List of Public Keys:");
- }
-
- while (p != NULL)
- {
- pubkey_t *key = p->key;
- public_key_t *public = key->public_key;
- chunk_t keyid;
-
- whack_log(RC_COMMENT, " ");
- whack_log(RC_COMMENT, " identity: '%Y'", key->id);
- whack_log(RC_COMMENT, " pubkey: %N %4d bits, until %T %s",
- key_type_names, public->get_type(public),
- public->get_keysize(public),
- &key->until_time, utc,
- check_expiry(key->until_time, PUBKEY_WARNING_INTERVAL, TRUE));
- if (public->get_fingerprint(public, KEYID_PUBKEY_INFO_SHA1, &keyid))
- {
- whack_log(RC_COMMENT," keyid: %#B", &keyid);
- }
- if (key->issuer)
- {
- whack_log(RC_COMMENT," issuer: \"%Y\"", key->issuer);
- }
- if (key->serial.len)
- {
- serial = chunk_skip_zero(key->serial);
- whack_log(RC_COMMENT," serial: %#B", &serial);
- }
- p = p->next;
- }
-}