diff options
Diffstat (limited to 'sbin/iked/parse.y')
-rw-r--r-- | sbin/iked/parse.y | 223 |
1 files changed, 191 insertions, 32 deletions
diff --git a/sbin/iked/parse.y b/sbin/iked/parse.y index 548686779fb..58b0b825abc 100644 --- a/sbin/iked/parse.y +++ b/sbin/iked/parse.y @@ -1,4 +1,4 @@ -/* $OpenBSD: parse.y,v 1.61 2017/01/20 13:56:51 mikeb Exp $ */ +/* $OpenBSD: parse.y,v 1.62 2017/03/27 10:06:41 reyk Exp $ */ /* * Copyright (c) 2010-2013 Reyk Floeter <reyk@openbsd.org> @@ -31,6 +31,9 @@ #include <netinet/in.h> #include <arpa/inet.h> +#include <openssl/pem.h> +#include <openssl/evp.h> + #include <ctype.h> #include <err.h> #include <errno.h> @@ -59,10 +62,14 @@ static struct file { int lineno; int errors; } *file; +EVP_PKEY *wrap_pubkey(FILE *); +EVP_PKEY *find_pubkey(const char *); +int set_policy(char *, int, struct iked_policy *); +int set_policy_auth_method(const char *, EVP_PKEY *, + struct iked_policy *); struct file *pushfile(const char *, int); int popfile(void); int check_file_secrecy(int, const char *); -int check_pubkey(char *, int ); int yyparse(void); int yylex(void); int yyerror(const char *, ...) @@ -242,11 +249,13 @@ const struct ipsec_xf groupxfs[] = { }; const struct ipsec_xf methodxfs[] = { + { "none", IKEV2_AUTH_NONE }, { "rsa", IKEV2_AUTH_RSA_SIG }, - { "dss", IKEV2_AUTH_DSS_SIG }, - { "ecdsa-256", IKEV2_AUTH_ECDSA_256 }, - { "ecdsa-384", IKEV2_AUTH_ECDSA_384 }, - { "ecdsa-521", IKEV2_AUTH_ECDSA_521 }, + { "ecdsa256", IKEV2_AUTH_ECDSA_256 }, + { "ecdsa384", IKEV2_AUTH_ECDSA_384 }, + { "ecdsa521", IKEV2_AUTH_ECDSA_521 }, + { "rfc7427", IKEV2_AUTH_SIG }, + { "signature", IKEV2_AUTH_SIG_ANY }, { NULL } }; @@ -805,12 +814,7 @@ ipcomp : /* empty */ { $$ = 0; } ; ikeauth : /* empty */ { - $$.auth_method = IKEV2_AUTH_RSA_SIG; - $$.auth_eap = 0; - $$.auth_length = 0; - } - | RSA { - $$.auth_method = IKEV2_AUTH_RSA_SIG; + $$.auth_method = IKEV2_AUTH_SIG_ANY; /* default */ $$.auth_eap = 0; $$.auth_length = 0; } @@ -837,6 +841,21 @@ ikeauth : /* empty */ { $$.auth_eap = EAP_TYPE_MSCHAP_V2; $$.auth_length = 0; } + | STRING { + const struct ipsec_xf *xf; + + if ((xf = parse_xf($1, 0, methodxfs)) == NULL || + xf->id == IKEV2_AUTH_NONE) { + yyerror("not a valid authentication mode"); + free($1); + YYERROR; + } + free($1); + + $$.auth_method = xf->id; + $$.auth_eap = 0; + $$.auth_length = 0; + } ; byte_spec : NUMBER { @@ -1662,44 +1681,178 @@ get_id_type(char *string) return (IKEV2_ID_FQDN); } +EVP_PKEY * +wrap_pubkey(FILE *fp) +{ + EVP_PKEY *key = NULL; + struct rsa_st *rsa = NULL; + + key = PEM_read_PUBKEY(fp, NULL, NULL, NULL); + if (key == NULL) { + /* reading PKCS #8 failed, try PEM */ + rewind(fp); + rsa = PEM_read_RSAPublicKey(fp, NULL, NULL, NULL); + fclose(fp); + if (rsa == NULL) + return (NULL); + if ((key = EVP_PKEY_new()) == NULL) { + RSA_free(rsa); + return (NULL); + } + if (!EVP_PKEY_set1_RSA(key, rsa)) { + RSA_free(rsa); + EVP_PKEY_free(key); + return (NULL); + } + /* Always free RSA *rsa */ + RSA_free(rsa); + } else { + fclose(fp); + } + + return (key); +} + +EVP_PKEY * +find_pubkey(const char *keyfile) +{ + FILE *fp = NULL; + if ((fp = fopen(keyfile, "r")) == NULL) + return (NULL); + + return (wrap_pubkey(fp)); +} + int -check_pubkey(char *idstr, int type) +set_policy_auth_method(const char *peerid, EVP_PKEY *key, + struct iked_policy *pol) +{ + struct rsa_st *rsa; + EC_KEY *ec_key; + u_int8_t method; + u_int8_t cert_type; + struct iked_auth *ikeauth; + + method = IKEV2_AUTH_NONE; + cert_type = IKEV2_CERT_NONE; + + if (key != NULL) { + /* infer policy from key type */ + if ((rsa = EVP_PKEY_get1_RSA(key)) != NULL) { + method = IKEV2_AUTH_RSA_SIG; + cert_type = IKEV2_CERT_RSA_KEY; + RSA_free(rsa); + } else if ((ec_key = EVP_PKEY_get1_EC_KEY(key)) != NULL) { + const EC_GROUP *group = EC_KEY_get0_group(ec_key); + if (group == NULL) { + EC_KEY_free(ec_key); + return (-1); + } + switch (EC_GROUP_get_degree(group)) { + case 256: + method = IKEV2_AUTH_ECDSA_256; + break; + case 384: + method = IKEV2_AUTH_ECDSA_384; + break; + case 521: + method = IKEV2_AUTH_ECDSA_521; + break; + default: + EC_KEY_free(ec_key); + return (-1); + } + cert_type = IKEV2_CERT_ECDSA; + EC_KEY_free(ec_key); + } + + if (method == IKEV2_AUTH_NONE || cert_type == IKEV2_CERT_NONE) + return (-1); + } else { + /* default to IKEV2_CERT_X509_CERT otherwise */ + method = IKEV2_AUTH_SIG; + cert_type = IKEV2_CERT_X509_CERT; + } + + ikeauth = &pol->pol_auth; + + if (ikeauth->auth_method == IKEV2_AUTH_SHARED_KEY_MIC) { + if (key != NULL && + method != IKEV2_AUTH_RSA_SIG) + goto mismatch; + return (0); + } + + if (ikeauth->auth_method != IKEV2_AUTH_NONE && + ikeauth->auth_method != IKEV2_AUTH_SIG_ANY && + ikeauth->auth_method != method) + goto mismatch; + + ikeauth->auth_method = method; + pol->pol_certreqtype = cert_type; + + log_debug("%s: using %s for peer %s", __func__, + print_xf(method, 0, methodxfs), peerid); + + return (0); + + mismatch: + log_warnx("%s: ikeauth policy mismatch, %s specified, but only %s " + "possible", __func__, print_xf(ikeauth->auth_method, 0, methodxfs), + print_xf(method, 0, methodxfs)); + return (-1); +} + +int +set_policy(char *idstr, int type, struct iked_policy *pol) { char keyfile[PATH_MAX]; - FILE *fp = NULL; - const char *suffix = NULL; + const char *prefix = NULL; + EVP_PKEY *key; switch (type) { case IKEV2_ID_IPV4: - suffix = "ipv4"; + prefix = "ipv4"; break; case IKEV2_ID_IPV6: - suffix = "ipv6"; + prefix = "ipv6"; break; case IKEV2_ID_FQDN: - suffix = "fqdn"; + prefix = "fqdn"; break; case IKEV2_ID_UFQDN: - suffix = "ufqdn"; + prefix = "ufqdn"; break; default: /* Unspecified ID or public key not supported for this type */ + log_debug("%s: unknown type = %d", __func__, type); return (-1); } lc_string(idstr); if ((size_t)snprintf(keyfile, sizeof(keyfile), - IKED_CA IKED_PUBKEY_DIR "%s/%s", suffix, + IKED_CA IKED_PUBKEY_DIR "%s/%s", prefix, idstr) >= sizeof(keyfile)) { log_warnx("%s: public key path is too long", __func__); return (-1); } - if ((fp = fopen(keyfile, "r")) == NULL) + if ((key = find_pubkey(keyfile)) == NULL) { + log_warnx("%s: could not find pubkey for %s", __func__, + keyfile); + } + + if (set_policy_auth_method(keyfile, key, pol) < 0) { + EVP_PKEY_free(key); + log_warnx("%s: failed to set policy auth method for %s", + __func__, keyfile); return (-1); - fclose(fp); + } - log_debug("%s: found public key file %s", __func__, keyfile); + if (key != NULL) { + EVP_PKEY_free(key); + log_debug("%s: found pubkey for %s", __func__, keyfile); + } return (0); } @@ -2341,12 +2494,16 @@ print_policy(struct iked_policy *pol) print_verbose(" lifetime %llu bytes %llu", pol->pol_lifetime.lt_seconds, pol->pol_lifetime.lt_bytes); - if (pol->pol_auth.auth_method == IKEV2_AUTH_SHARED_KEY_MIC) { - print_verbose(" psk 0x"); - for (i = 0; i < pol->pol_auth.auth_length; i++) - print_verbose("%02x", - pol->pol_auth.auth_data[i]); - } else { + switch (pol->pol_auth.auth_method) { + case IKEV2_AUTH_NONE: + print_verbose (" none"); + break; + case IKEV2_AUTH_SHARED_KEY_MIC: + print_verbose(" psk 0x"); + for (i = 0; i < pol->pol_auth.auth_length; i++) + print_verbose("%02x", pol->pol_auth.auth_data[i]); + break; + default: if (pol->pol_auth.auth_eap) print_verbose(" eap \"%s\"", print_map(pol->pol_auth.auth_eap, eap_type_map)); @@ -2695,9 +2852,11 @@ create_ike(char *name, int af, uint8_t ipproto, struct ipsec_hosts *hosts, } } - /* Check if we have a raw public key for this peer */ - if (check_pubkey(idstr, idtype) != -1) - pol.pol_certreqtype = IKEV2_CERT_RSA_KEY; + /* Make sure that we know how to authenticate this peer */ + if (idtype && set_policy(idstr, idtype, &pol) < 0) { + log_debug("%s: set_policy failed", __func__); + return (-1); + } config_setpolicy(env, &pol, PROC_IKEV2); |