summaryrefslogtreecommitdiff
path: root/programs/pluto/keys.c
diff options
context:
space:
mode:
Diffstat (limited to 'programs/pluto/keys.c')
-rw-r--r--programs/pluto/keys.c1516
1 files changed, 0 insertions, 1516 deletions
diff --git a/programs/pluto/keys.c b/programs/pluto/keys.c
deleted file mode 100644
index 39726f424..000000000
--- a/programs/pluto/keys.c
+++ /dev/null
@@ -1,1516 +0,0 @@
-/* mechanisms for preshared keys (public, private, and preshared secrets)
- * Copyright (C) 1998-2001 D. Hugh Redelmeier.
- *
- * 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.
- *
- * RCSID $Id: keys.c,v 1.26 2007/01/10 00:36:19 as Exp $
- */
-
-#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>
-
-#include <glob.h>
-#ifndef GLOB_ABORTED
-# define GLOB_ABORTED GLOB_ABEND /* fix for old versions */
-#endif
-
-#include <freeswan.h>
-#include <freeswan/ipsec_policy.h>
-
-#include "constants.h"
-#include "defs.h"
-#include "mp_defs.h"
-#include "id.h"
-#include "x509.h"
-#include "pgp.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"
-#include "xauth.h"
-
-const char *shared_secrets_file = SHARED_SECRETS_FILE;
-
-typedef struct id_list id_list_t;
-
-struct id_list {
- struct id id;
- id_list_t *next;
-};
-
-typedef struct secret secret_t;
-
-struct secret {
- id_list_t *ids;
- enum PrivateKeyKind kind;
- union {
- chunk_t preshared_secret;
- RSA_private_key_t RSA_private_key;
- xauth_t xauth_secret;
- smartcard_t *smartcard;
- } u;
- secret_t *next;
-};
-
-static pubkey_t*
-allocate_RSA_public_key(const cert_t cert)
-{
- pubkey_t *pk = alloc_thing(pubkey_t, "pubkey");
- chunk_t e, n;
-
- switch (cert.type)
- {
- case CERT_PGP:
- e = cert.u.pgp->publicExponent;
- n = cert.u.pgp->modulus;
- break;
- case CERT_X509_SIGNATURE:
- e = cert.u.x509->publicExponent;
- n = cert.u.x509->modulus;
- break;
- default:
- plog("RSA public key allocation error");
- }
-
- init_RSA_public_key(&pk->u.rsa, e, n);
- DBG(DBG_RAW,
- RSA_show_public_key(&pk->u.rsa)
- )
-
- pk->alg = PUBKEY_ALG_RSA;
- pk->id = empty_id;
- pk->issuer = empty_chunk;
- pk->serial = empty_chunk;
-
- return pk;
-}
-
-/*
- * free a public key struct
- */
-static void
-free_public_key(pubkey_t *pk)
-{
- free_id_content(&pk->id);
- freeanychunk(pk->issuer);
- freeanychunk(pk->serial);
-
- /* algorithm-specific freeing */
- switch (pk->alg)
- {
- case PUBKEY_ALG_RSA:
- free_RSA_public_content(&pk->u.rsa);
- break;
- default:
- bad_case(pk->alg);
- }
- pfree(pk);
-}
-
-secret_t *secrets = NULL;
-
-/* find the struct secret associated with the combination of
- * me and the peer. We match the Id (if none, the IP address).
- * Failure is indicated by a NULL.
- */
-static const secret_t *
-get_secret(const struct connection *c, enum PrivateKeyKind kind, bool asym)
-{
- enum { /* bits */
- match_default = 01,
- match_him = 02,
- match_me = 04
- };
-
- unsigned int best_match = 0;
- secret_t *best = NULL;
- secret_t *s;
- const struct id *my_id = &c->spd.this.id
- , *his_id = &c->spd.that.id;
- struct id rw_id;
-
- /* is there a certificate assigned to this connection? */
- if (kind == PPK_RSA && c->spd.this.cert.type != CERT_NONE)
- {
- pubkey_t *my_public_key = allocate_RSA_public_key(c->spd.this.cert);
-
- for (s = secrets; s != NULL; s = s->next)
- {
- if (s->kind == kind &&
- same_RSA_public_key(&s->u.RSA_private_key.pub, &my_public_key->u.rsa))
- {
- best = s;
- break; /* we have found the private key - no sense in searching further */
- }
- }
- free_public_key(my_public_key);
- return best;
- }
-
- if (his_id_was_instantiated(c))
- {
- /* roadwarrior: replace him with 0.0.0.0 */
- rw_id.kind = c->spd.that.id.kind;
- rw_id.name = empty_chunk;
- happy(anyaddr(addrtypeof(&c->spd.that.host_addr), &rw_id.ip_addr));
- his_id = &rw_id;
- }
-#ifdef NAT_TRAVERSAL
- else if (kind == PPK_PSK
- && (c->policy & (POLICY_PSK | POLICY_XAUTH_PSK))
- && ((c->kind == CK_TEMPLATE && c->spd.that.id.kind == ID_NONE) ||
- (c->kind == CK_INSTANCE && id_is_ipaddr(&c->spd.that.id))))
- {
- /* roadwarrior: replace him with 0.0.0.0 */
- rw_id.kind = ID_IPV4_ADDR;
- happy(anyaddr(addrtypeof(&c->spd.that.host_addr), &rw_id.ip_addr));
- his_id = &rw_id;
- }
-#endif
-
- for (s = secrets; s != NULL; s = s->next)
- {
- if (s->kind == kind)
- {
- unsigned int match = 0;
-
- if (s->ids == NULL)
- {
- /* a default (signified by lack of ids):
- * accept if no more specific match found
- */
- match = match_default;
- }
- else
- {
- /* check if both ends match ids */
- id_list_t *i;
-
- for (i = s->ids; i != NULL; i = i->next)
- {
- if (same_id(my_id, &i->id))
- match |= match_me;
-
- if (same_id(his_id, &i->id))
- match |= match_him;
- }
-
- /* 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->next == NULL)
- 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 (!asym)
- 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 PPK_PSK:
- same = s->u.preshared_secret.len == best->u.preshared_secret.len
- && memcmp(s->u.preshared_secret.ptr, best->u.preshared_secret.ptr, s->u.preshared_secret.len) == 0;
- break;
- case PPK_RSA:
- /* Dirty trick: since we have code to compare
- * RSA public keys, but not private keys, we
- * make the assumption that equal public keys
- * mean equal private keys. This ought to work.
- */
- same = same_RSA_public_key(&s->u.RSA_private_key.pub
- , &best->u.RSA_private_key.pub);
- 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;
-}
-
-/* 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 struct connection *c)
-{
- const secret_t *s = get_secret(c, PPK_PSK, FALSE);
-
- 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 an RSA private key matching an RSA public
- * key contained in an X.509 or OpenPGP certificate
- */
-bool
-has_private_key(cert_t cert)
-{
- secret_t *s;
- bool has_key = FALSE;
- pubkey_t *pubkey = allocate_RSA_public_key(cert);
-
- for (s = secrets; s != NULL; s = s->next)
- {
- if (s->kind == PPK_RSA &&
- same_RSA_public_key(&s->u.RSA_private_key.pub, &pubkey->u.rsa))
- {
- has_key = TRUE;
- break;
- }
- }
- free_public_key(pubkey);
- return has_key;
-}
-
-/*
- * get the matching RSA private key belonging to a given X.509 certificate
- */
-const RSA_private_key_t*
-get_x509_private_key(const x509cert_t *cert)
-{
- secret_t *s;
- const RSA_private_key_t *pri = NULL;
- const cert_t c = {CERT_X509_SIGNATURE, {cert}};
-
- pubkey_t *pubkey = allocate_RSA_public_key(c);
-
- for (s = secrets; s != NULL; s = s->next)
- {
- if (s->kind == PPK_RSA &&
- same_RSA_public_key(&s->u.RSA_private_key.pub, &pubkey->u.rsa))
- {
- pri = &s->u.RSA_private_key;
- break;
- }
- }
- free_public_key(pubkey);
- return pri;
-}
-
-/* find the appropriate RSA private key (see get_secret).
- * Failure is indicated by a NULL pointer.
- */
-const RSA_private_key_t *
-get_RSA_private_key(const struct connection *c)
-{
- const secret_t *s = get_secret(c, PPK_RSA, TRUE);
-
- return s == NULL? NULL : &s->u.RSA_private_key;
-}
-
-/* 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 == '\'')
- {
- clonetochunk(*psk, tok+1, flp->cur - tok - 2, "PSK");
- (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
- {
- clonetochunk(*psk, buf, sz, "PSK");
- (void) shift();
- }
- }
- return ugh;
-}
-
-/* Parse fields of RSA private key.
- * A braced list of keyword and value pairs.
- * At the moment, each field is required, in order.
- * The fields come from BIND 8.2's representation
- */
-static err_t
-process_rsa_secret(RSA_private_key_t *rsak)
-{
- char buf[RSA_MAX_ENCODING_BYTES]; /* limit on size of binary representation of key */
- const struct fld *p;
-
- /* save bytes of Modulus and PublicExponent for keyid calculation */
- unsigned char ebytes[sizeof(buf)];
- unsigned char *eb_next = ebytes;
- chunk_t pub_bytes[2];
- chunk_t *pb_next = &pub_bytes[0];
-
- for (p = RSA_private_field; p < &RSA_private_field[RSA_PRIVATE_FIELD_ELEMENTS]; p++)
- {
- size_t sz;
- err_t ugh;
-
- if (!shift())
- {
- return "premature end of RSA key";
- }
- else if (!tokeqword(p->name))
- {
- return builddiag("%s keyword not found where expected in RSA key"
- , p->name);
- }
- else if (!(shift()
- && (!tokeq(":") || shift()))) /* ignore optional ":" */
- {
- return "premature end of RSA key";
- }
- else if (NULL != (ugh = ttodatav(tok, flp->cur - tok
- , 0, buf, sizeof(buf), &sz, diag_space, sizeof(diag_space)
- , TTODATAV_SPACECOUNTS)))
- {
- /* in RSA key, ttodata didn't like */
- return builddiag("RSA data malformed (%s): %s", ugh, tok);
- }
- else
- {
- MP_INT *n = (MP_INT *) ((char *)rsak + p->offset);
-
- n_to_mpz(n, buf, sz);
- if (pb_next < &pub_bytes[elemsof(pub_bytes)])
- {
- if (eb_next - ebytes + sz > sizeof(ebytes))
- return "public key takes too many bytes";
-
- setchunk(*pb_next, eb_next, sz);
- memcpy(eb_next, buf, sz);
- eb_next += sz;
- pb_next++;
- }
-#if 0 /* debugging info that compromises security */
- {
- size_t sz = mpz_sizeinbase(n, 16);
- char buf[RSA_MAX_OCTETS * 2 + 2]; /* ought to be big enough */
-
- passert(sz <= sizeof(buf));
- mpz_get_str(buf, 16, n);
-
- loglog(RC_LOG_SERIOUS, "%s: %s", p->name, buf);
- }
-#endif
- }
- }
-
- /* 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("}"))
- {
- return "malformed end of RSA private key -- indented '}' required";
- }
- else if (shift())
- {
- return "malformed end of RSA private key -- unexpected token after '}'";
- }
- else
- {
- unsigned bits = mpz_sizeinbase(&rsak->pub.n, 2);
-
- rsak->pub.k = (bits + BITS_PER_BYTE - 1) / BITS_PER_BYTE;
- rsak->pub.keyid[0] = '\0'; /* in case of splitkeytoid failure */
- splitkeytoid(pub_bytes[1].ptr, pub_bytes[1].len
- , pub_bytes[0].ptr, pub_bytes[0].len
- , rsak->pub.keyid, sizeof(rsak->pub.keyid));
- return RSA_private_key_sanity(rsak);
- }
-}
-
-/* process rsa key file protected with optional passphrase which can either be
- * read from ipsec.secrets or prompted for by using whack
- */
-static err_t
-process_rsa_keyfile(RSA_private_key_t *rsak, 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;
-
- /* 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 "RSA 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 "RSA private key file -- passphrase exceeds 64 characters";
-
- memcpy(pass.secret, passphrase, len);
- }
- if (shift())
- return "RSA private key file -- unexpected token after passphrase";
- }
- return load_rsa_private_key(filename, &pass, rsak);
-}
-
-/*
- * process xauth secret read from ipsec.secrets
- */
-static err_t
-process_xauth(secret_t *s)
-{
- chunk_t user_name;
-
- s->kind = PPK_XAUTH;
-
- if (!shift())
- return "missing xauth user name";
- if (*tok == '"' || *tok == '\'') /* quoted user name */
- {
- user_name.ptr = tok + 1;
- user_name.len = flp->cur - tok - 2;
- }
- else
- {
- user_name.ptr = tok;
- user_name.len = flp->cur - tok;
- }
- plog(" loaded xauth credentials of user '%.*s'"
- , user_name.len
- , user_name.ptr);
- clonetochunk(s->u.xauth_secret.user_name
- , user_name.ptr, user_name.len, "xauth user name");
-
- if (!shift())
- return "missing xauth user password";
- return process_psk_secret(&s->u.xauth_secret.user_password);
-}
-
-/* get XAUTH secret from chained secrets lists
- * only one entry is currently supported
- */
-static bool
-xauth_get_secret(xauth_t *xauth_secret)
-{
- secret_t *s;
- bool found = FALSE;
-
- for (s = secrets; s != NULL; s = s->next)
- {
- if (s->kind == PPK_XAUTH)
- {
- if (found)
- {
- plog("found multiple xauth secrets - first selected");
- }
- else
- {
- found = TRUE;
- *xauth_secret = s->u.xauth_secret;
- }
- }
- }
- return found;
-}
-
-/*
- * find a matching secret
- */
-static bool
-xauth_verify_secret(const xauth_t *xauth_secret)
-{
- bool found = FALSE;
- secret_t *s;
-
- for (s = secrets; s != NULL; s = s->next)
- {
- if (s->kind == PPK_XAUTH)
- {
- if (!same_chunk(xauth_secret->user_name, s->u.xauth_secret.user_name))
- continue;
- found = TRUE;
- if (same_chunk(xauth_secret->user_password, s->u.xauth_secret.user_password))
- return TRUE;
- }
- }
- plog("xauth user '%.*s' %s"
- , xauth_secret->user_name.len, xauth_secret->user_name.ptr
- , found? "sent wrong password":"not found");
- return FALSE;
-}
-
-/*
- * the global xauth_module struct is defined here
- */
-xauth_module_t xauth_module;
-
-/*
- * assign the default xauth functions to any null function pointers
- */
-void
-xauth_defaults(void)
-{
- if (xauth_module.get_secret == NULL)
- {
- DBG(DBG_CONTROL,
- DBG_log("xauth module: using default get_secret() function")
- )
- xauth_module.get_secret = xauth_get_secret;
- }
- if (xauth_module.verify_secret == NULL)
- {
- DBG(DBG_CONTROL,
- DBG_log("xauth module: using default verify_secret() function")
- )
- xauth_module.verify_secret = xauth_verify_secret;
- }
-};
-
-/*
- * 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 = PPK_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"))
- {
- shift();
- /* pin will be entered via pin pad during verification */
- clonetochunk(sc->pin, "", 0, "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
-process_secret(secret_t *s, int whackfd)
-{
- err_t ugh = NULL;
-
- s->kind = PPK_PSK; /* default */
- if (*tok == '"' || *tok == '\'')
- {
- /* old PSK format: just a string */
- ugh = process_psk_secret(&s->u.preshared_secret);
- }
- else if (tokeqword("psk"))
- {
- /* 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("rsa"))
- {
- /* RSA key: the fun begins.
- * A braced list of keyword and value pairs.
- */
- s->kind = PPK_RSA;
- if (!shift())
- {
- ugh = "bad RSA key syntax";
- }
- else if (tokeq("{"))
- {
- ugh = process_rsa_secret(&s->u.RSA_private_key);
- }
- else
- {
- ugh = process_rsa_keyfile(&s->u.RSA_private_key, whackfd);
- }
- }
- else if (tokeqword("xauth"))
- {
- ugh = process_xauth(s);
- }
- 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);
- pfree(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 = alloc_thing(secret_t, "secret");
-
- s->ids = NULL;
- s->kind = PPK_PSK; /* default */
- setchunk(s->u.preshared_secret, NULL, 0);
- s->next = NULL;
-
- for (;;)
- {
- if (tok[0] == '"' || tok[0] == '\'')
- {
- /* found key part */
- process_secret(s, whackfd);
- break;
- }
- else if (tokeq(":"))
- {
- /* found key part */
- shift(); /* discard explicit separator */
- process_secret(s, whackfd);
- break;
- }
- else
- {
- /* an id
- * See RFC2407 IPsec Domain of Interpretation 4.6.2
- */
- struct id id;
- err_t ugh;
-
- if (tokeq("%any"))
- {
- id = empty_id;
- id.kind = ID_IPV4_ADDR;
- ugh = anyaddr(AF_INET, &id.ip_addr);
- }
- else if (tokeq("%any6"))
- {
- id = empty_id;
- id.kind = ID_IPV6_ADDR;
- ugh = anyaddr(AF_INET6, &id.ip_addr);
- }
- else
- {
- ugh = atoid(tok, &id, FALSE);
- }
-
- if (ugh != NULL)
- {
- loglog(RC_LOG_SERIOUS
- , "ERROR \"%s\" line %d: index \"%s\" %s"
- , flp->filename, flp->lino, tok, ugh);
- }
- else
- {
- id_list_t *i = alloc_thing(id_list_t
- , "id_list");
-
- i->id = id;
- unshare_id_content(&i->id);
- i->next = s->ids;
- s->ids = i;
- /* DBG_log("id type %d: %s %.*s", i->kind, ip_str(&i->ip_addr), (int)i->name.len, i->name.ptr); */
- }
- if (!shift())
- {
- /* unexpected Record Boundary or EOF */
- loglog(RC_LOG_SERIOUS, "\"%s\" line %d: unexpected end of id list"
- , flp->filename, flp->lino);
- 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;
- glob_t globbuf;
-
- 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;
- }
-
- /* do globbing */
- {
- 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);
- (void) flushline("file starts with indentation (continuation notation)");
- process_secret_records(whackfd);
- lexclose();
- }
- }
-
- globfree(&globbuf);
-}
-
-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)
- {
- id_list_t *i, *ni;
-
- ns = s->next; /* grab before freeing s */
- for (i = s->ids; i != NULL; i = ni)
- {
- ni = i->next; /* grab before freeing i */
- free_id_content(&i->id);
- pfree(i);
- }
- switch (s->kind)
- {
- case PPK_PSK:
- pfree(s->u.preshared_secret.ptr);
- break;
- case PPK_RSA:
- free_RSA_private_content(&s->u.RSA_private_key);
- break;
- case PPK_XAUTH:
- pfree(s->u.xauth_secret.user_name.ptr);
- pfree(s->u.xauth_secret.user_password.ptr);
- break;
- case PPK_PIN:
- scx_release(s->u.smartcard);
- break;
- default:
- bad_case(s->kind);
- }
- pfree(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(const RSA_public_key_t *k)
-{
- pubkey_t *p = alloc_thing(pubkey_t, "pubkey");
-
- p->id = empty_id; /* don't know, doesn't matter */
- p->issuer = empty_chunk;
- p->serial = empty_chunk;
- p->alg = PUBKEY_ALG_RSA;
-
- memcpy(p->u.rsa.keyid, k->keyid, sizeof(p->u.rsa.keyid));
- p->u.rsa.k = k->k;
- mpz_init_set(&p->u.rsa.e, &k->e);
- mpz_init_set(&p->u.rsa.n, &k->n);
-
- /* note that we return a 1 reference count upon creation:
- * invariant: recount > 0.
- */
- p->refcnt = 1;
- time(&p->installed_time);
- 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);
- pfree(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 = alloc_thing(pubkey_list_t, "from TXT");
-
- 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 */
-}
-
-/* decode of RSA pubkey chunk
- * - format specified in RFC 2537 RSA/MD5 Keys and SIGs in the DNS
- * - exponent length in bytes (1 or 3 octets)
- * + 1 byte if in [1, 255]
- * + otherwise 0x00 followed by 2 bytes of length
- * - exponent
- * - modulus
- */
-err_t
-unpack_RSA_public_key(RSA_public_key_t *rsa, const chunk_t *pubkey)
-{
- chunk_t exp;
- chunk_t mod;
-
- if (pubkey->len < 3)
- return "RSA public key blob way to short"; /* not even room for length! */
-
- if (pubkey->ptr[0] != 0x00)
- {
- setchunk(exp, pubkey->ptr + 1, pubkey->ptr[0]);
- }
- else
- {
- setchunk(exp, pubkey->ptr + 3
- , (pubkey->ptr[1] << BITS_PER_BYTE) + pubkey->ptr[2]);
- }
-
- if (pubkey->len - (exp.ptr - pubkey->ptr) < exp.len + RSA_MIN_OCTETS_RFC)
- return "RSA public key blob too short";
-
- mod.ptr = exp.ptr + exp.len;
- mod.len = &pubkey->ptr[pubkey->len] - mod.ptr;
-
- if (mod.len < RSA_MIN_OCTETS)
- return RSA_MIN_OCTETS_UGH;
-
- if (mod.len > RSA_MAX_OCTETS)
- return RSA_MAX_OCTETS_UGH;
-
- init_RSA_public_key(rsa, exp, mod);
- rsa->k = mpz_sizeinbase(&rsa->n, 2); /* size in bits, for a start */
- rsa->k = (rsa->k + BITS_PER_BYTE - 1) / BITS_PER_BYTE; /* now octets */
- DBG(DBG_RAW,
- RSA_show_public_key(rsa)
- )
-
- if (rsa->k != mod.len)
- {
- mpz_clear(&rsa->e);
- mpz_clear(&rsa->n);
- return "RSA modulus shorter than specified";
- }
-
- return NULL;
-}
-
-static void
-install_public_key(pubkey_t *pk, pubkey_list_t **head)
-{
- pubkey_list_t *p = alloc_thing(pubkey_list_t, "pubkey entry");
-
- unshare_id_content(&pk->id);
-
- /* copy issuer dn */
- if (pk->issuer.ptr != NULL)
- pk->issuer.ptr = clone_bytes(pk->issuer.ptr, pk->issuer.len, "issuer dn");
-
- /* copy serial number */
- if (pk->serial.ptr != NULL)
- pk->serial.ptr = clone_bytes(pk->serial.ptr, pk->serial.len, "serialNumber");
-
- /* store the time the public key was installed */
- time(&pk->installed_time);
-
- /* install new key at front */
- p->key = reference_key(pk);
- p->next = *head;
- *head = p;
-}
-
-
-void
-delete_public_keys(const struct id *id, enum pubkey_alg alg
-, chunk_t issuer, chunk_t serial)
-{
- pubkey_list_t **pp, *p;
- pubkey_t *pk;
-
- for (pp = &pubkeys; (p = *pp) != NULL; )
- {
- pk = p->key;
-
- if (same_id(id, &pk->id) && pk->alg == alg
- && (issuer.ptr == NULL || pk->issuer.ptr == NULL
- || same_dn(issuer, pk->issuer))
- && same_serial(serial, pk->serial))
- *pp = free_public_keyentry(p);
- else
- pp = &p->next;
- }
-}
-
-pubkey_t *
-reference_key(pubkey_t *pk)
-{
- pk->refcnt++;
- return pk;
-}
-
-void
-unreference_key(pubkey_t **pkp)
-{
- pubkey_t *pk = *pkp;
- char b[BUF_LEN];
-
- if (pk == NULL)
- return;
-
- /* print stuff */
- DBG(DBG_CONTROLMORE,
- idtoa(&pk->id, b, sizeof(b));
- DBG_log("unreference key: %p %s cnt %d--", pk, b, pk->refcnt)
- )
-
- /* cancel out the pointer */
- *pkp = NULL;
-
- passert(pk->refcnt != 0);
- pk->refcnt--;
- if (pk->refcnt == 0)
- free_public_key(pk);
-}
-
-err_t
-add_public_key(const struct id *id
-, enum dns_auth_level dns_auth_level
-, enum pubkey_alg alg
-, const chunk_t *key
-, pubkey_list_t **head)
-{
- pubkey_t *pk = alloc_thing(pubkey_t, "pubkey");
-
- /* first: algorithm-specific decoding of key chunk */
- switch (alg)
- {
- case PUBKEY_ALG_RSA:
- {
- err_t ugh = unpack_RSA_public_key(&pk->u.rsa, key);
-
- if (ugh != NULL)
- {
- pfree(pk);
- return ugh;
- }
- }
- break;
- default:
- bad_case(alg);
- }
-
- pk->id = *id;
- pk->dns_auth_level = dns_auth_level;
- pk->alg = alg;
- pk->until_time = UNDEFINED_TIME;
- pk->issuer = empty_chunk;
- pk->serial = empty_chunk;
-
- install_public_key(pk, head);
- return NULL;
-}
-
-/* extract id and public key from x.509 certificate and
- * insert it into a pubkeyrec
- */
-void
-add_x509_public_key(x509cert_t *cert , time_t until
- , enum dns_auth_level dns_auth_level)
-{
- generalName_t *gn;
- pubkey_t *pk;
- cert_t c = { CERT_X509_SIGNATURE, {cert} };
-
- /* we support RSA only */
- if (cert->subjectPublicKeyAlgorithm != PUBKEY_ALG_RSA)
- return;
-
- /* ID type: ID_DER_ASN1_DN (X.509 subject field) */
- pk = allocate_RSA_public_key(c);
- pk->id.kind = ID_DER_ASN1_DN;
- pk->id.name = cert->subject;
- pk->dns_auth_level = dns_auth_level;
- pk->until_time = until;
- pk->issuer = cert->issuer;
- pk->serial = cert->serialNumber;
- delete_public_keys(&pk->id, pk->alg, pk->issuer, pk->serial);
- install_public_key(pk, &pubkeys);
-
- gn = cert->subjectAltName;
-
- while (gn != NULL) /* insert all subjectAltNames */
- {
- struct id id = empty_id;
-
- gntoid(&id, gn);
- if (id.kind != ID_NONE)
- {
- pk = allocate_RSA_public_key(c);
- pk->id = id;
- pk->dns_auth_level = dns_auth_level;
- pk->until_time = until;
- pk->issuer = cert->issuer;
- pk->serial = cert->serialNumber;
- delete_public_keys(&pk->id, pk->alg, pk->issuer, pk->serial);
- install_public_key(pk, &pubkeys);
- }
- gn = gn->next;
- }
-}
-
-/* extract id and public key from OpenPGP certificate and
- * insert it into a pubkeyrec
- */
-void
-add_pgp_public_key(pgpcert_t *cert , time_t until
- , enum dns_auth_level dns_auth_level)
-{
- pubkey_t *pk;
- cert_t c;
-
- c.type = CERT_PGP;
- c.u.pgp = cert;
-
- /* we support RSA only */
- if (cert->pubkeyAlg != PUBKEY_ALG_RSA)
- {
- plog(" RSA public keys supported only");
- return;
- }
-
- pk = allocate_RSA_public_key(c);
- pk->id.kind = ID_KEY_ID;
- pk->id.name.ptr = cert->fingerprint;
- pk->id.name.len = PGP_FINGERPRINT_SIZE;
- pk->dns_auth_level = dns_auth_level;
- pk->until_time = until;
- delete_public_keys(&pk->id, pk->alg, empty_chunk, empty_chunk);
- 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 x509cert_t *cert)
-{
- const cert_t c = {CERT_X509_SIGNATURE, {cert}};
- pubkey_list_t *p, **pp;
- pubkey_t *revoked_pk;
-
- revoked_pk = allocate_RSA_public_key(c);
- p = pubkeys;
- pp = &pubkeys;
-
- while(p != NULL)
- {
- if (same_RSA_public_key(&p->key->u.rsa, &revoked_pk->u.rsa))
- {
- /* remove p from list and free memory */
- *pp = free_public_keyentry(p);
- loglog(RC_LOG_SERIOUS,
- "invalid RSA public key deleted");
- }
- else
- {
- pp = &p->next;
- }
- p =*pp;
- }
- free_public_key(revoked_pk);
-}
-
-/*
- * list all public keys in the chained list
- */
-void list_public_keys(bool utc)
-{
- pubkey_list_t *p = pubkeys;
-
- if (p != NULL)
- {
- whack_log(RC_COMMENT, " ");
- whack_log(RC_COMMENT, "List of Public Keys:");
- whack_log(RC_COMMENT, " ");
- }
-
- while (p != NULL)
- {
- pubkey_t *key = p->key;
-
- if (key->alg == PUBKEY_ALG_RSA)
- {
- char buf[BUF_LEN];
- char expires_buf[TIMETOA_BUF];
-
- idtoa(&key->id, buf, BUF_LEN);
- strcpy(expires_buf, timetoa(&key->until_time, utc));
- whack_log(RC_COMMENT, "%s, %4d RSA Key %s, until %s %s",
-
- timetoa(&key->installed_time, utc), 8*key->u.rsa.k, key->u.rsa.keyid,
- expires_buf,
- check_expiry(key->until_time, PUBKEY_WARNING_INTERVAL, TRUE));
- whack_log(RC_COMMENT," %s '%s'",
- enum_show(&ident_names, key->id.kind), buf);
- if (key->issuer.len > 0)
- {
- dntoa(buf, BUF_LEN, key->issuer);
- whack_log(RC_COMMENT," issuer: '%s'", buf);
- }
- if (key->serial.len > 0)
- {
- datatot(key->serial.ptr, key->serial.len, ':'
- , buf, BUF_LEN);
- whack_log(RC_COMMENT," serial: %s", buf);
- }
- }
- p = p->next;
- }
-}