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.c465
1 files changed, 194 insertions, 271 deletions
diff --git a/src/pluto/keys.c b/src/pluto/keys.c
index 8cf28ace1..6db757ba7 100644
--- a/src/pluto/keys.c
+++ b/src/pluto/keys.c
@@ -53,25 +53,26 @@
#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 {
- identification_t *id;
- id_list_t *next;
+typedef enum secret_kind_t secret_kind_t;
+
+enum secret_kind_t {
+ SECRET_PSK,
+ SECRET_PUBKEY,
+ SECRET_XAUTH,
+ SECRET_PIN
};
-typedef struct secret secret_t;
+typedef struct secret_t secret_t;
-struct secret {
- id_list_t *ids;
- enum PrivateKeyKind kind;
+struct secret_t {
+ linked_list_t *ids;
+ secret_kind_t kind;
union {
chunk_t preshared_secret;
- xauth_t xauth_secret;
private_key_t *private_key;
smartcard_t *smartcard;
} u;
@@ -92,12 +93,11 @@ static void free_public_key(pubkey_t *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.
+/**
+ * Find the secret associated with the combination of me and the peer.
*/
-static const secret_t* get_secret(const connection_t *c,
- enum PrivateKeyKind kind, bool asym)
+const secret_t* match_secret(identification_t *my_id, identification_t *his_id,
+ secret_kind_t kind)
{
enum { /* bits */
match_default = 0x01,
@@ -106,128 +106,92 @@ static const secret_t* get_secret(const connection_t *c,
};
unsigned int best_match = 0;
- secret_t *best = NULL;
- secret_t *s;
- identification_t *my_id, *his_id;
+ secret_t *s, *best = NULL;
- /* is there a certificate assigned to this connection? */
- if (kind == PPK_PUBKEY && c->spd.this.cert)
+ for (s = secrets; s != NULL; s = s->next)
{
- certificate_t *certificate = c->spd.this.cert->cert;
+ unsigned int match = 0;
- public_key_t *pub_key = certificate->get_public_key(certificate);
-
- for (s = secrets; s != NULL; s = s->next)
+ if (s->kind != kind)
{
- if (s->kind == kind &&
- s->u.private_key->belongs_to(s->u.private_key, pub_key))
- {
- best = s;
- break; /* we have found the private key - no sense in searching further */
- }
+ continue;
}
- pub_key->destroy(pub_key);
- return 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 == PPK_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);
- }
- for (s = secrets; s != NULL; s = s->next)
- {
- if (s->kind == kind)
+ 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
{
- unsigned int match = 0;
+ /* check if both ends match ids */
+ enumerator_t *enumerator;
+ identification_t *id;
- if (s->ids == NULL)
- {
- /* a default (signified by lack of ids):
- * accept if no more specific match found
- */
- match = match_default;
- }
- else
+ enumerator = s->ids->create_enumerator(s->ids);
+ while (enumerator->enumerate(enumerator, &id))
{
- /* check if both ends match ids */
- id_list_t *i;
-
- for (i = s->ids; i != NULL; i = i->next)
+ if (my_id->equals(my_id, id))
{
- if (my_id->equals(my_id, i->id))
- {
- match |= match_me;
- }
- if (his_id->equals(his_id, i->id))
- {
- match |= match_him;
- }
+ match |= match_me;
}
-
- /* 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)
+ if (his_id->equals(his_id, id))
{
- match |= match_default;
+ match |= match_him;
}
}
+ enumerator->destroy(enumerator);
- switch (match)
+ /* 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 (!asym)
+ if (kind != SECRET_PUBKEY)
{
break;
}
/* FALLTHROUGH */
- case match_default: /* default all */
- case match_me | match_default: /* default peer */
- case match_me | match_him: /* explicit */
+ 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?
- */
+ /* 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
- && memeq(s->u.preshared_secret.ptr, best->u.preshared_secret.ptr, s->u.preshared_secret.len);
+ case SECRET_PSK:
+ case SECRET_XAUTH:
+ same = chunk_equals(s->u.preshared_secret,
+ best->u.preshared_secret);
break;
- case PPK_PUBKEY:
- same = s->u.private_key->equals(s->u.private_key, best->u.private_key);
+ 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");
+ 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 */
}
}
@@ -237,9 +201,63 @@ static const secret_t* get_secret(const connection_t *c,
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;
}
@@ -250,7 +268,7 @@ static const secret_t* get_secret(const connection_t *c,
*/
const chunk_t* get_preshared_secret(const connection_t *c)
{
- const secret_t *s = get_secret(c, PPK_PSK, FALSE);
+ const secret_t *s = get_secret(c, SECRET_PSK);
DBG(DBG_PRIVATE,
if (s == NULL)
@@ -272,7 +290,7 @@ bool has_private_key(cert_t *cert)
for (s = secrets; s != NULL; s = s->next)
{
- if (s->kind == PPK_PUBKEY &&
+ if (s->kind == SECRET_PUBKEY &&
s->u.private_key->belongs_to(s->u.private_key, pub_key))
{
has_key = TRUE;
@@ -295,7 +313,7 @@ private_key_t* get_x509_private_key(const cert_t *cert)
for (s = secrets; s != NULL; s = s->next)
{
- if (s->kind == PPK_PUBKEY &&
+ if (s->kind == SECRET_PUBKEY &&
s->u.private_key->belongs_to(s->u.private_key, public_key))
{
private_key = s->u.private_key;
@@ -311,9 +329,33 @@ private_key_t* get_x509_private_key(const cert_t *cert)
*/
private_key_t* get_private_key(const connection_t *c)
{
- const secret_t *s = get_secret(c, PPK_PUBKEY, TRUE);
+ 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;
- return s == NULL? NULL : s->u.private_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
@@ -556,120 +598,6 @@ static err_t process_keyfile(private_key_t **key, key_type_t type, int whackfd)
}
/**
- * 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);
- s->u.xauth_secret.user_name = chunk_clone(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_peer_t *peer,
- 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 (!chunk_equals(xauth_secret->user_name, s->u.xauth_secret.user_name))
- {
- continue;
- }
- found = TRUE;
- if (chunk_equals(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)
@@ -677,7 +605,7 @@ static err_t process_pin(secret_t *s, int whackfd)
smartcard_t *sc;
const char *pin_status = "no pin";
- s->kind = PPK_PIN;
+ s->kind = SECRET_PIN;
/* looking for the smartcard keyword */
if (!shift() || strncmp(tok, SCX_TOKEN, strlen(SCX_TOKEN)) != 0)
@@ -748,57 +676,69 @@ static err_t process_pin(secret_t *s, int whackfd)
return NULL;
}
-static void log_psk(secret_t *s)
+static void log_psk(char *label, secret_t *s)
{
int n = 0;
char buf[BUF_LEN];
- id_list_t *id_list = s->ids;
+ enumerator_t *enumerator;
+ identification_t *id;
- if (id_list == NULL)
+ if (s->ids->get_count(s->ids) == 0)
{
n = snprintf(buf, BUF_LEN, "%%any");
}
else
{
- do
+ enumerator = s->ids->create_enumerator(s->ids);
+ while(enumerator->enumerate(enumerator, &id))
{
- n += snprintf(buf + n, BUF_LEN - n, "%Y ", id_list->id);
+ n += snprintf(buf + n, BUF_LEN - n, "%Y ", id);
if (n >= BUF_LEN)
{
n = BUF_LEN - 1;
break;
}
- id_list = id_list->next;
}
- while (id_list);
+ enumerator->destroy(enumerator);
}
- plog(" loaded shared key for %.*s", n, buf);
+ plog(" loaded %s secret for %.*s", label, n, buf);
}
static void process_secret(secret_t *s, int whackfd)
{
err_t ugh = NULL;
- s->kind = PPK_PSK; /* default */
+ s->kind = SECRET_PSK; /* default */
if (*tok == '"' || *tok == '\'')
{
+ log_psk("PSK", s);
+
/* old PSK format: just a string */
- log_psk(s);
ugh = process_psk_secret(&s->u.preshared_secret);
}
else if (tokeqword("psk"))
{
+ log_psk("PSK", s);
+
/* preshared key: quoted string or ttodata format */
- log_psk(s);
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 = PPK_PUBKEY;
+ s->kind = SECRET_PUBKEY;
if (!shift())
{
ugh = "bad RSA key syntax";
@@ -814,7 +754,7 @@ static void process_secret(secret_t *s, int whackfd)
}
else if (tokeqword("ecdsa"))
{
- s->kind = PPK_PUBKEY;
+ s->kind = SECRET_PUBKEY;
if (!shift())
{
ugh = "bad ECDSA key syntax";
@@ -824,10 +764,6 @@ static void process_secret(secret_t *s, int whackfd)
ugh = process_keyfile(&s->u.private_key, KEY_ECDSA, whackfd);
}
}
- else if (tokeqword("xauth"))
- {
- ugh = process_xauth(s);
- }
else if (tokeqword("pin"))
{
ugh = process_pin(s, whackfd);
@@ -919,8 +855,8 @@ static void process_secret_records(int whackfd)
secret_t *s = malloc_thing(secret_t);
zero(s);
- s->ids = NULL;
- s->kind = PPK_PSK; /* default */
+ s->ids = linked_list_create();
+ s->kind = SECRET_PSK; /* default */
s->u.preshared_secret = chunk_empty;
s->next = NULL;
@@ -941,14 +877,10 @@ static void process_secret_records(int whackfd)
}
else
{
- /* an id
- * See RFC2407 IPsec Domain of Interpretation 4.6.2
- */
- id_list_t *i = malloc_thing(id_list_t);
+ identification_t *id;
- i->id = identification_create_from_string(tok);
- i->next = s->ids;
- s->ids = i;
+ id = identification_create_from_string(tok);
+ s->ids->insert_last(s->ids, id);
if (!shift())
{
@@ -1035,32 +967,23 @@ void free_preshared_secrets(void)
for (s = secrets; s != NULL; s = ns)
{
- id_list_t *i, *ni;
-
ns = s->next;
- for (i = s->ids; i != NULL; i = ni)
- {
- ni = i->next;
- i->id->destroy(i->id);
- free(i);
- }
+ s->ids->destroy_offset(s->ids, offsetof(identification_t, destroy));
+
switch (s->kind)
{
- case PPK_PSK:
- free(s->u.preshared_secret.ptr);
- break;
- case PPK_PUBKEY:
- DESTROY_IF(s->u.private_key);
- break;
- case PPK_XAUTH:
- free(s->u.xauth_secret.user_name.ptr);
- free(s->u.xauth_secret.user_password.ptr);
- break;
- case PPK_PIN:
- scx_release(s->u.smartcard);
- break;
- default:
- bad_case(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);
}
@@ -1315,7 +1238,7 @@ void add_public_key_from_cert(cert_t *cert , time_t until,
/* insert all subjectAltNames from X.509 certificates */
enumerator = x509->create_subjectAltName_enumerator(x509);
- while (enumerator->enumerate(enumerator, &id))
+ while (enumerator->enumerate(enumerator, &id))
{
if (id->get_type(id) != ID_ANY)
{
@@ -1404,7 +1327,7 @@ void list_public_keys(bool utc)
public->get_keysize(public) * BITS_PER_BYTE,
&key->until_time, utc,
check_expiry(key->until_time, PUBKEY_WARNING_INTERVAL, TRUE));
- if (public->get_fingerprint(public, KEY_ID_PUBKEY_INFO_SHA1, &keyid))
+ if (public->get_fingerprint(public, KEYID_PUBKEY_INFO_SHA1, &keyid))
{
whack_log(RC_COMMENT," keyid: %#B", &keyid);
}