diff options
Diffstat (limited to 'src/pluto/keys.c')
-rw-r--r-- | src/pluto/keys.c | 2249 |
1 files changed, 1080 insertions, 1169 deletions
diff --git a/src/pluto/keys.c b/src/pluto/keys.c index 1aed7a63f..6dfbd6732 100644 --- a/src/pluto/keys.c +++ b/src/pluto/keys.c @@ -10,8 +10,6 @@ * 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 3738 2008-04-02 19:04:45Z andreas $ */ #include <stddef.h> @@ -25,33 +23,34 @@ #include <netinet/in.h> #include <arpa/inet.h> #include <resolv.h> -#include <arpa/nameser.h> /* missing from <resolv.h> on old systems */ +#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 */ +# define GLOB_ABORTED GLOB_ABEND /* fix for old versions */ #endif #include <freeswan.h> -#include <ipsec_policy.h> + +#include <library.h> +#include <asn1/asn1.h> #include "constants.h" #include "defs.h" -#include "mp_defs.h" #include "id.h" #include "x509.h" -#include "pgp.h" +#include "pgpcert.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 "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 "whack.h" /* for RC_LOG_SERIOUS */ #include "timer.h" #include "fetch.h" #include "xauth.h" @@ -61,77 +60,34 @@ 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; + 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; + id_list_t *ids; + enum PrivateKeyKind kind; + union { + chunk_t preshared_secret; + xauth_t xauth_secret; + private_key_t *private_key; + 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 = empty_chunk, n = empty_chunk; - - 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) +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); + DESTROY_IF(pk->public_key); + free_id_content(&pk->id); + free(pk->issuer.ptr); + free(pk->serial.ptr); + free(pk); } secret_t *secrets = NULL; @@ -140,227 +96,215 @@ secret_t *secrets = NULL; * 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) +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) + enum { /* bits */ + match_default = 0x01, + match_him = 0x02, + match_me = 0x04 + }; + + 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_PUBKEY && c->spd.this.cert.type != CERT_NONE) { - 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; - } - 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; - } - - for (s = secrets; s != NULL; s = s->next) - { - if (s->kind == kind) - { - unsigned int match = 0; + public_key_t *pub_key = cert_get_public_key(c->spd.this.cert); - 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) + for (s = secrets; s != NULL; s = s->next) { - if (same_id(my_id, &i->id)) - match |= match_me; - - if (same_id(his_id, &i->id)) - match |= match_him; + 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 */ + } } + return best; + } - /* 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) + 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 = chunk_empty; + happy(anyaddr(addrtypeof(&c->spd.that.host_addr), &rw_id.ip_addr)); + his_id = &rw_id; + } + else if (kind == PPK_PSK + && (c->policy & (POLICY_PSK | POLICY_XAUTH_PSK)) + && ((c->kind == CK_TEMPLATE && c->spd.that.id.kind == ID_ANY) || + (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; + } + + for (s = secrets; s != NULL; s = s->next) + { + if (s->kind == kind) { - /* this is the best match so far */ - best_match = match; - best = s; + 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 + && memeq(s->u.preshared_secret.ptr, best->u.preshared_secret.ptr, s->u.preshared_secret.len); + break; + case PPK_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; + 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 chunk_t* get_preshared_secret(const struct connection *c) { - const secret_t *s = get_secret(c, PPK_PSK, FALSE); + 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; + 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 +/* 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) +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)) + secret_t *s; + bool has_key = FALSE; + public_key_t *pub_key = cert_get_public_key(cert); + + for (s = secrets; s != NULL; s = s->next) { - has_key = TRUE; - break; + if (s->kind == PPK_PUBKEY && + s->u.private_key->belongs_to(s->u.private_key, pub_key)) + { + has_key = TRUE; + break; + } } - } - free_public_key(pubkey); - return has_key; + return has_key; } /* - * get the matching RSA private key belonging to a given X.509 certificate + * get the matching private key belonging to a given X.509 certificate */ -const RSA_private_key_t* -get_x509_private_key(const x509cert_t *cert) +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, {(x509cert_t*)cert}}; - - pubkey_t *pubkey = allocate_RSA_public_key(c); + secret_t *s; - 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)) + for (s = secrets; s != NULL; s = s->next) { - pri = &s->u.RSA_private_key; - break; + if (s->kind == PPK_PUBKEY && + s->u.private_key->belongs_to(s->u.private_key, cert->public_key)) + { + return s->u.private_key; + } } - } - free_public_key(pubkey); - return pri; + return NULL; } -/* find the appropriate RSA private key (see get_secret). +/* find the appropriate 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) +private_key_t* get_private_key(const struct connection *c) { - const secret_t *s = get_secret(c, PPK_RSA, TRUE); + const secret_t *s = get_secret(c, PPK_PUBKEY, TRUE); - return s == NULL? NULL : &s->u.RSA_private_key; + return s == NULL? NULL : s->u.private_key; } /* digest a secrets file @@ -403,1100 +347,1071 @@ get_RSA_private_key(const struct connection *c) */ /* parse PSK from file */ -static err_t -process_psk_secret(chunk_t *psk) +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) + err_t ugh = NULL; + + if (*tok == '"' || *tok == '\'') { - /* ttodata didn't like PSK data */ - ugh = builddiag("PSK data malformed (%s): %s", ugh, tok); + chunk_t secret = { tok + 1, flp->cur - tok -2 }; + + *psk = chunk_clone(secret); + (void) shift(); } else { - clonetochunk(*psk, buf, sz, "PSK"); - (void) shift(); + 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; + 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 +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. + * Conversion into ASN.1 DER encoded PKCS#1 representation. */ -static err_t -process_rsa_secret(RSA_private_key_t *rsak) +static err_t process_rsa_secret(private_key_t **key) { - 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; + chunk_t asn1_chunk[countof(rsa_private_key_part_names)]; + chunk_t pkcs1_chunk; + 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, len = 0; err_t ugh; - if (!shift()) + for (part = RSA_PART_MODULUS; part <= RSA_PART_COEFFICIENT; part++) { - return "premature end of RSA key"; + chunk_t rsa_private_key_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); + part++; + goto end; + } + rsa_private_key_part = chunk_create(buf, sz); + asn1_chunk[part] = asn1_integer("c", rsa_private_key_part); + len += asn1_chunk[part].len; } - else if (!tokeqword(p->name)) + + /* 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 builddiag("%s keyword not found where expected in RSA key" - , p->name); + ugh = "malformed end of RSA private key -- indented '}' required"; + goto end; } - else if (!(shift() - && (!tokeq(":") || shift()))) /* ignore optional ":" */ + if (shift()) { - return "premature end of RSA key"; + ugh = "malformed end of RSA private key -- unexpected token after '}'"; + goto end; } - else if (NULL != (ugh = ttodatav(tok, flp->cur - tok - , 0, buf, sizeof(buf), &sz, diag_space, sizeof(diag_space) - , TTODATAV_SPACECOUNTS))) + + pkcs1_chunk = asn1_wrap(ASN1_SEQUENCE, "ccccccccc", + ASN1_INTEGER_0, + asn1_chunk[RSA_PART_MODULUS], + asn1_chunk[RSA_PART_PUBLIC_EXPONENT], + asn1_chunk[RSA_PART_PRIVATE_EXPONENT], + asn1_chunk[RSA_PART_PRIME1], + asn1_chunk[RSA_PART_PRIME2], + asn1_chunk[RSA_PART_EXPONENT1], + asn1_chunk[RSA_PART_EXPONENT2], + asn1_chunk[RSA_PART_COEFFICIENT]); + + *key = lib->creds->create(lib->creds, CRED_PRIVATE_KEY, KEY_RSA, + BUILD_BLOB_ASN1_DER, pkcs1_chunk, + BUILD_END); + free(pkcs1_chunk.ptr); + if (*key == NULL) { - /* in RSA key, ttodata didn't like */ - return builddiag("RSA data malformed (%s): %s", ugh, tok); + ugh = "parsing of RSA private key failed"; } - else + +end: + /* clean up and return */ + for (p = RSA_PART_MODULUS ; p < part; p++) { - 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 + free(asn1_chunk[p].ptr); } - } - - /* 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); - } + return ugh; } -/* process rsa key file protected with optional passphrase which can either be +/** + * 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_rsa_keyfile(RSA_private_key_t *rsak, int whackfd) +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; + char filename[BUF_LEN]; + prompt_pass_t pass; - /* we expect the filename of a PKCS#1 private key file */ + memset(filename,'\0', BUF_LEN); + memset(pass.secret,'\0', sizeof(pass.secret)); + pass.prompt = FALSE; + pass.fd = whackfd; - if (*tok == '"' || *tok == '\'') /* quoted filename */ - memcpy(filename, tok+1, flp->cur - tok - 2); - else - memcpy(filename, tok, flp->cur - tok); + /* we expect the filename of a PKCS#1 private key file */ - 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; - } + if (*tok == '"' || *tok == '\'') /* quoted filename */ + memcpy(filename, tok+1, flp->cur - tok - 2); else + memcpy(filename, tok, flp->cur - tok); + + if (shift()) { - 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); + /* 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"; + } } - if (shift()) - return "RSA private key file -- unexpected token after passphrase"; - } - return load_rsa_private_key(filename, &pass, rsak); + *key = load_private_key(filename, &pass, type); + + return key ? NULL : "Private key file -- could not be loaded"; } -/* - * process xauth secret read from ipsec.secrets +/** + * Process xauth secret read from ipsec.secrets */ -static err_t -process_xauth(secret_t *s) +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); + 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 +/** + * Get XAUTH secret from chained secrets lists * only one entry is currently supported */ -static bool -xauth_get_secret(xauth_t *xauth_secret) +static bool xauth_get_secret(xauth_t *xauth_secret) { - secret_t *s; - bool found = FALSE; + secret_t *s; + bool found = FALSE; - for (s = secrets; s != NULL; s = s->next) - { - if (s->kind == PPK_XAUTH) + for (s = secrets; s != NULL; s = s->next) { - if (found) - { - plog("found multiple xauth secrets - first selected"); - } - else - { - found = TRUE; - *xauth_secret = s->u.xauth_secret; - } + 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; + return found; } -/* +/** * find a matching secret */ -static bool -xauth_verify_secret(const xauth_peer_t *peer, const xauth_t *xauth_secret) +static bool xauth_verify_secret(const xauth_peer_t *peer, + const xauth_t *xauth_secret) { - bool found = FALSE; - secret_t *s; + bool found = FALSE; + secret_t *s; - for (s = secrets; s != NULL; s = s->next) - { - if (s->kind == PPK_XAUTH) + for (s = secrets; s != NULL; s = s->next) { - 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; + 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; + 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 +/** + * Assign the default xauth functions to any null function pointers */ -void -xauth_defaults(void) +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; - } + 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 +/** + * Process pin read from ipsec.secrets or prompted for it using whack */ -static err_t -process_pin(secret_t *s, int whackfd) +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"; + 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")) + { + 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 - 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"; - } + { + /* 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]; + { + char buf[BUF_LEN]; - if (sc->any_slot) - snprintf(buf, BUF_LEN, "any slot"); - else - snprintf(buf, BUF_LEN, "slot: %lu", sc->slot); + 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); - } + 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!"); + plog(" warning: SMARTCARD support is deactivated in pluto/Makefile!"); #endif - return NULL; + return NULL; } -static void -log_psk(secret_t *s) +static void log_psk(secret_t *s) { - int n = 0; - char buf[BUF_LEN]; - id_list_t *id_list = s->ids; - - if (id_list == NULL) - { - n = snprintf(buf, BUF_LEN, "%%any"); - } - else - { - do + int n = 0; + char buf[BUF_LEN]; + id_list_t *id_list = s->ids; + + if (id_list == NULL) { - n += idtoa(&id_list->id, buf + n, BUF_LEN - n); - if (n >= BUF_LEN) - { - n = BUF_LEN - 1; - break; - } - else if (n < BUF_LEN - 1) - { - n += snprintf(buf + n, BUF_LEN - n, " "); - } - id_list = id_list->next; + n = snprintf(buf, BUF_LEN, "%%any"); } - while (id_list); - } - plog(" loaded shared key for %.*s", n, buf); + else + { + do + { + n += idtoa(&id_list->id, buf + n, BUF_LEN - n); + if (n >= BUF_LEN) + { + n = BUF_LEN - 1; + break; + } + else if (n < BUF_LEN - 1) + { + n += snprintf(buf + n, BUF_LEN - n, " "); + } + id_list = id_list->next; + } + while (id_list); + } + plog(" loaded shared key for %.*s", n, buf); } -static void -process_secret(secret_t *s, int whackfd) +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 */ - log_psk(s); - ugh = process_psk_secret(&s->u.preshared_secret); - } - else if (tokeqword("psk")) - { - /* 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("rsa")) - { - /* RSA key: the fun begins. - * A braced list of keyword and value pairs. - */ - s->kind = PPK_RSA; - if (!shift()) + err_t ugh = NULL; + + s->kind = PPK_PSK; /* default */ + if (*tok == '"' || *tok == '\'') + { + /* old PSK format: just a string */ + log_psk(s); + ugh = process_psk_secret(&s->u.preshared_secret); + } + else if (tokeqword("psk")) + { + /* 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("rsa")) + { + /* RSA key: the fun begins. + * A braced list of keyword and value pairs. + */ + s->kind = PPK_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 = PPK_PUBKEY; + if (!shift()) + { + ugh = "bad ECDSA key syntax"; + } + else + { + ugh = process_keyfile(&s->u.private_key, KEY_ECDSA, whackfd); + } + } + else if (tokeqword("xauth")) { - ugh = "bad RSA key syntax"; + ugh = process_xauth(s); } - else if (tokeq("{")) + else if (tokeqword("pin")) { - ugh = process_rsa_secret(&s->u.RSA_private_key); + ugh = process_pin(s, whackfd); } else { - ugh = process_rsa_keyfile(&s->u.RSA_private_key, whackfd); + ugh = builddiag("unrecognized key format: %s", tok); } - } - 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")) + if (ugh != NULL) { - /* 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 */ - } + loglog(RC_LOG_SERIOUS, "\"%s\" line %d: %s" + , flp->filename, flp->lino, ugh); + free(s); } - else + else if (flushline("expected record boundary in key")) { - /* expecting a list of indices and then the key info */ - secret_t *s = alloc_thing(secret_t, "secret"); + /* gauntlet has been run: install new secret */ + lock_certs_and_keys("process_secret"); + s->next = secrets; + secrets = s; + unlock_certs_and_keys("process_secrets"); + } +} - s->ids = NULL; - s->kind = PPK_PSK; /* default */ - setchunk(s->u.preshared_secret, NULL, 0); - s->next = NULL; +static void process_secrets_file(const char *file_pat, int whackfd); /* forward declaration */ - for (;;) - { - if (tok[0] == '"' || tok[0] == '\'') +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) { - /* found key part */ - process_secret(s, whackfd); - break; + break; } - else if (tokeq(":")) + flp->bdry = B_none; /* eat the Record Boundary */ + (void)shift(); /* get real first token */ + + if (tokeqword("include")) { - /* found key part */ - shift(); /* discard explicit separator */ - process_secret(s, whackfd); - break; + /* 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 { - /* 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; - } + /* expecting a list of indices and then the key info */ + secret_t *s = malloc_thing(secret_t); + + zero(s); + s->ids = NULL; + s->kind = PPK_PSK; /* default */ + s->u.preshared_secret = chunk_empty; + 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 = malloc_thing(id_list_t); + + 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) +static int globugh(const char *epath, int eerrno) { - log_errno_routine(eerrno, "problem with secrets file \"%s\"", epath); - return 1; /* stop glob */ + 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) +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; + struct file_lex_position pos; + char **fnp; + glob_t globbuf; - if (pos.depth > 10) - { - loglog(RC_LOG_SERIOUS, "preshared secrets file \"%s\" nested too deeply", file_pat); - return; - } + pos.depth = flp == NULL? 0 : flp->depth + 1; - /* do globbing */ - { - int r = glob(file_pat, GLOB_ERR, globugh, &globbuf); + if (pos.depth > 10) + { + loglog(RC_LOG_SERIOUS, "preshared secrets file \"%s\" nested too deeply", file_pat); + return; + } - if (r != 0) + /* do globbing */ { - 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; + 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)) + /* for each file... */ + for (fnp = globbuf.gl_pathv; *fnp != NULL; fnp++) { - plog("loading secrets from \"%s\"", *fnp); - (void) flushline("file starts with indentation (continuation notation)"); - process_secret_records(whackfd); - lexclose(); + 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); + globfree(&globbuf); } -void -free_preshared_secrets(void) +void free_preshared_secrets(void) { - lock_certs_and_keys("free_preshared_secrets"); + lock_certs_and_keys("free_preshared_secrets"); - if (secrets != NULL) - { - secret_t *s, *ns; + if (secrets != NULL) + { + secret_t *s, *ns; - plog("forgetting secrets"); + 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); + 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); + free(i); + } + 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); + } + free(s); + } + secrets = NULL; } - secrets = NULL; - } - unlock_certs_and_keys("free_preshard_secrets"); + unlock_certs_and_keys("free_preshard_secrets"); } -void -load_preshared_secrets(int whackfd) +void load_preshared_secrets(int whackfd) { - free_preshared_secrets(); - (void) process_secrets_file(shared_secrets_file, 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* public_key_from_rsa(public_key_t *key) { - 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; + pubkey_t *p = malloc_thing(pubkey_t); + + zero(p); + p->id = empty_id; /* don't know, doesn't matter */ + p->issuer = chunk_empty; + p->serial = chunk_empty; + p->public_key = key; + + /* 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* free_public_keyentry(pubkey_list_t *p) { - pubkey_list_t *nxt = p->next; + pubkey_list_t *nxt = p->next; - if (p->key != NULL) - unreference_key(&p->key); - pfree(p); - return nxt; + if (p->key != NULL) + { + unreference_key(&p->key); + } + free(p); + return nxt; } -void -free_public_keys(pubkey_list_t **keys) +void free_public_keys(pubkey_list_t **keys) { - while (*keys != NULL) - *keys = free_public_keyentry(*keys); + while (*keys != NULL) + { + *keys = free_public_keyentry(*keys); + } } /* root of chained public key list */ -pubkey_list_t *pubkeys = NULL; /* keys from ipsec.conf */ +pubkey_list_t *pubkeys = NULL; /* keys from ipsec.conf */ -void -free_remembered_public_keys(void) +void free_remembered_public_keys(void) { - free_public_keys(&pubkeys); + 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 +/** + * 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"); + struct gw_info *gwp; - pl->key = gwp->key; /* note: this is a transfer */ - gwp->key = NULL; /* really, it is! */ - pl->next = pubkeys; - pubkeys = pl; + 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; - } + { + 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) +static void install_public_key(pubkey_t *pk, pubkey_list_t **head) { - pubkey_list_t *p = alloc_thing(pubkey_list_t, "pubkey entry"); + pubkey_list_t *p = malloc_thing(pubkey_list_t); - unshare_id_content(&pk->id); + 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 issuer dn */ + pk->issuer = chunk_clone(pk->issuer); - /* copy serial number */ - if (pk->serial.ptr != NULL) - pk->serial.ptr = clone_bytes(pk->serial.ptr, pk->serial.len, "serialNumber"); + /* copy serial number */ + pk->serial = chunk_clone(pk->serial); - /* store the time the public key was installed */ - time(&pk->installed_time); + /* 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; + /* 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) +void delete_public_keys(const struct id *id, key_type_t type, + 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_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 (same_id(id, &pk->id) && pk_type == type + && (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) +pubkey_t* reference_key(pubkey_t *pk) { - pk->refcnt++; - return pk; + pk->refcnt++; + return pk; } void unreference_key(pubkey_t **pkp) { - pubkey_t *pk = *pkp; - char b[BUF_LEN]; + pubkey_t *pk = *pkp; + char b[BUF_LEN]; - if (pk == NULL) - return; + 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) - ) + /* 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; + /* cancel out the pointer */ + *pkp = NULL; - passert(pk->refcnt != 0); - pk->refcnt--; - if (pk->refcnt == 0) - free_public_key(pk); + 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) +bool add_public_key(const struct id *id, enum dns_auth_level dns_auth_level, + enum pubkey_alg alg, chunk_t rfc3110_key, + pubkey_list_t **head) { - pubkey_t *pk = alloc_thing(pubkey_t, "pubkey"); + public_key_t *key = NULL; + pubkey_t *pk; - /* first: algorithm-specific decoding of key chunk */ - switch (alg) - { - case PUBKEY_ALG_RSA: + /* first: algorithm-specific decoding of key chunk */ + switch (alg) { - err_t ugh = unpack_RSA_public_key(&pk->u.rsa, key); - - if (ugh != NULL) - { - pfree(pk); - return ugh; - } + case PUBKEY_ALG_RSA: + key = lib->creds->create(lib->creds, CRED_PUBLIC_KEY, KEY_RSA, + BUILD_BLOB_RFC_3110, rfc3110_key, + BUILD_END); + if (key == NULL) + { + return FALSE; + } + break; + default: + bad_case(alg); } - 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; + + pk = malloc_thing(pubkey_t); + zero(pk); + pk->public_key = key; + pk->id = *id; + pk->dns_auth_level = dns_auth_level; + pk->until_time = UNDEFINED_TIME; + pk->issuer = chunk_empty; + pk->serial = chunk_empty; + install_public_key(pk, head); + return TRUE; } /* 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) +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) + generalName_t *gn; + 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 = cert->public_key->get_ref(cert->public_key); + 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; + pk_type = pk->public_key->get_type(pk->public_key); + delete_public_keys(&pk->id, pk_type, pk->issuer, pk->serial); + install_public_key(pk, &pubkeys); + + gn = cert->subjectAltName; + + while (gn != NULL) /* insert all subjectAltNames */ { - 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); + struct id id = empty_id; + + gntoid(&id, gn); + if (id.kind != ID_ANY) + { + pk = malloc_thing(pubkey_t); + zero(pk); + pk->public_key = cert->public_key->get_ref(cert->public_key); + 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_type, pk->issuer, pk->serial); + install_public_key(pk, &pubkeys); + } + gn = gn->next; } - 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) +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); + pubkey_t *pk; + key_type_t pk_type; + + pk = malloc_thing(pubkey_t); + zero(pk); + pk->public_key = cert->public_key->get_ref(cert->public_key); + pk->id.kind = ID_KEY_ID; + pk->id.name = cert->fingerprint->get_encoding(cert->fingerprint); + pk->dns_auth_level = dns_auth_level; + pk->until_time = until; + pk_type = pk->public_key->get_type(pk->public_key); + delete_public_keys(&pk->id, pk_type, chunk_empty, chunk_empty); + 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) +void remove_x509_public_key(const x509cert_t *cert) { - const cert_t c = {CERT_X509_SIGNATURE, {(x509cert_t*)cert}}; - pubkey_list_t *p, **pp; - pubkey_t *revoked_pk; + public_key_t *revoked_key = cert->public_key; + pubkey_list_t *p, **pp; - revoked_pk = allocate_RSA_public_key(c); - p = pubkeys; - pp = &pubkeys; + 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 + while(p != NULL) { - pp = &p->next; + 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; } - p =*pp; - } - free_public_key(revoked_pk); } /* @@ -1504,45 +1419,41 @@ remove_x509_public_key(const x509cert_t *cert) */ 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, " "); - } + pubkey_list_t *p = pubkeys; - while (p != NULL) - { - pubkey_t *key = p->key; + if (p != NULL) + { + whack_log(RC_COMMENT, " "); + whack_log(RC_COMMENT, "List of Public Keys:"); + whack_log(RC_COMMENT, " "); + } - if (key->alg == PUBKEY_ALG_RSA) + while (p != NULL) { - 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); - } + pubkey_t *key = p->key; + public_key_t *public = key->public_key; + identification_t *keyid = public->get_id(public, ID_PUBKEY_INFO_SHA1); + char buf[BUF_LEN]; + + idtoa(&key->id, buf, BUF_LEN); + whack_log(RC_COMMENT,"%T, '%s'", &key->installed_time, utc, buf); + whack_log(RC_COMMENT, " pubkey: %N %4d bits, until %T %s", + key_type_names, public->get_type(public), + public->get_keysize(public) * BITS_PER_BYTE, + &key->until_time, utc, + check_expiry(key->until_time, PUBKEY_WARNING_INTERVAL, TRUE)); + whack_log(RC_COMMENT," keyid: %Y", keyid); + 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; } - p = p->next; - } } |