diff options
Diffstat (limited to 'src/pluto/alg_info.c')
-rw-r--r-- | src/pluto/alg_info.c | 1205 |
1 files changed, 1205 insertions, 0 deletions
diff --git a/src/pluto/alg_info.c b/src/pluto/alg_info.c new file mode 100644 index 000000000..ac5d1672f --- /dev/null +++ b/src/pluto/alg_info.c @@ -0,0 +1,1205 @@ +/* + * Algorithm info parsing and creation functions + * Author: JuanJo Ciarlante <jjo-ipsec@mendoza.gov.ar> + * + * $Id: alg_info.c,v 1.5 2004/09/29 22:42:49 as Exp $ + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ +#include <stddef.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <netinet/in.h> +#include <sys/socket.h> +#include <sys/stat.h> +#include <netinet/in.h> +#include <arpa/inet.h> + +#include <ctype.h> +#include <freeswan.h> +#include <ipsec_policy.h> +#include <pfkeyv2.h> + +#include "alg_info.h" +#include "constants.h" +#ifndef NO_PLUTO +#include "defs.h" +#include "log.h" +#include "whack.h" +#include "sha1.h" +#include "md5.h" +#include "crypto.h" +#include "kernel_alg.h" +#include "ike_alg.h" +#else +/* + * macros/functions for compilation without pluto (eg: spi for manual conns) + */ +#include <assert.h> +#define passert(x) assert(x) +extern int debug; /* eg: spi.c */ +#define DBG(cond, action) { if (debug) { action ; } } +#define DBG_log(x, args...) fprintf(stderr, x "\n" , ##args); +#define RC_LOG_SERIOUS +#define loglog(x, args...) fprintf(stderr, ##args); +#define alloc_thing(thing, name) alloc_bytes(sizeof (thing), name) +void * alloc_bytes(size_t size, const char *name) { + void *p=malloc(size); + if (p == NULL) + fprintf(stderr, "unable to malloc %lu bytes for %s", + (unsigned long) size, name); + memset(p, '\0', size); + return p; +} +#define pfreeany(ptr) free(ptr) +#endif /* NO_PLUTO */ + +/* + * sadb/ESP aa attrib converters + */ +int +alg_info_esp_aa2sadb(int auth) +{ + int sadb_aalg = 0; + + switch(auth) { + case AUTH_ALGORITHM_HMAC_MD5: + case AUTH_ALGORITHM_HMAC_SHA1: + sadb_aalg = auth + 1; + break; + case AUTH_ALGORITHM_HMAC_SHA2_256: + case AUTH_ALGORITHM_HMAC_SHA2_384: + case AUTH_ALGORITHM_HMAC_SHA2_512: + case AUTH_ALGORITHM_HMAC_RIPEMD: + sadb_aalg = auth; + break; + default: + /* loose ... */ + sadb_aalg = auth; + } + return sadb_aalg; +} + +int /* __attribute__ ((unused)) */ +alg_info_esp_sadb2aa(int sadb_aalg) +{ + int auth = 0; + + switch(sadb_aalg) { + case SADB_AALG_MD5_HMAC: + case SADB_AALG_SHA1_HMAC: + auth = sadb_aalg - 1; + break; + /* since they are the same ... :) */ + case AUTH_ALGORITHM_HMAC_SHA2_256: + case AUTH_ALGORITHM_HMAC_SHA2_384: + case AUTH_ALGORITHM_HMAC_SHA2_512: + case AUTH_ALGORITHM_HMAC_RIPEMD: + auth = sadb_aalg; + break; + default: + /* loose ... */ + auth = sadb_aalg; + } + return auth; +} + +/* + * Search enum_name array with in prefixed uppercase + */ +static int +enum_search_prefix (enum_names *ed, const char *prefix, const char *str, int strlen) +{ + char buf[64]; + char *ptr; + int ret; + int len = sizeof(buf) - 1; /* reserve space for final \0 */ + + for (ptr = buf; *prefix; *ptr++ = *prefix++, len--); + while (strlen-- && len-- && *str) *ptr++ = toupper(*str++); + *ptr = 0; + + DBG(DBG_CRYPT, + DBG_log("enum_search_prefix () calling enum_search(%p, \"%s\")" + , ed, buf) + ) + ret = enum_search(ed, buf); + return ret; +} + +/* + * Search enum_name array with in prefixed and postfixed uppercase + */ +static int +enum_search_ppfix (enum_names *ed, const char *prefix, const char *postfix, const char *str, int strlen) +{ + char buf[64]; + char *ptr; + int ret; + int len = sizeof(buf) - 1; /* reserve space for final \0 */ + + for (ptr = buf; *prefix; *ptr++ = *prefix++, len--); + while (strlen-- && len-- && *str) *ptr++ = toupper(*str++); + while (len-- && *postfix) *ptr++ = *postfix++; + *ptr = 0; + + DBG(DBG_CRYPT, + DBG_log("enum_search_ppfixi () calling enum_search(%p, \"%s\")" + , ed, buf) + ) + ret = enum_search(ed, buf); + return ret; +} + +/* + * Search esp_transformid_names for a match, eg: + * "3des" <=> "ESP_3DES" + */ +#define ESP_MAGIC_ID 0x00ffff01 + +static int +ealg_getbyname_esp(const char *const str, int len) +{ + if (!str || !*str) + return -1; + + /* leave special case for eg: "id248" string */ + if (strcmp("id", str) == 0) + return ESP_MAGIC_ID; + + return enum_search_prefix(&esp_transformid_names, "ESP_", str, len); +} + +/* + * Search auth_alg_names for a match, eg: + * "md5" <=> "AUTH_ALGORITHM_HMAC_MD5" + */ +static int +aalg_getbyname_esp(const char *const str, int len) +{ + int ret; + unsigned num; + + if (!str || !*str) + return -1; + + /* interpret 'SHA' as 'SHA1' */ + if (strncasecmp("SHA", str, len) == 0) + return enum_search(&auth_alg_names, "AUTH_ALGORITHM_HMAC_SHA1"); + + ret = enum_search_prefix(&auth_alg_names,"AUTH_ALGORITHM_HMAC_", str ,len); + if (ret >= 0) + return ret; + + ret = enum_search_prefix(&auth_alg_names,"AUTH_ALGORITHM_", str, len); + if (ret >= 0) + return ret; + + sscanf(str, "id%d%n", &ret, &num); + return (ret >= 0 && num != strlen(str))? -1 : ret; +} + +static int +modp_getbyname_esp(const char *const str, int len) +{ + int ret; + + if (!str || !*str) + return -1; + + ret = enum_search_prefix(&oakley_group_names,"OAKLEY_GROUP_", str, len); + if (ret >= 0) + return ret; + + ret = enum_search_ppfix(&oakley_group_names, "OAKLEY_GROUP_", " (extension)", str, len); + return ret; +} + +void +alg_info_free(struct alg_info *alg_info) +{ + pfreeany(alg_info); +} + +/* + * Raw add routine: only checks for no duplicates + */ +static void +__alg_info_esp_add (struct alg_info_esp *alg_info, int ealg_id, unsigned ek_bits, int aalg_id, unsigned ak_bits) +{ + struct esp_info *esp_info=alg_info->esp; + unsigned cnt = alg_info->alg_info_cnt, i; + + /* check for overflows */ + passert(cnt < elemsof(alg_info->esp)); + + /* dont add duplicates */ + for (i = 0; i < cnt; i++) + { + if (esp_info[i].esp_ealg_id == ealg_id + && (!ek_bits || esp_info[i].esp_ealg_keylen == ek_bits) + && esp_info[i].esp_aalg_id == aalg_id + && (!ak_bits || esp_info[i].esp_aalg_keylen == ak_bits)) + return; + } + + esp_info[cnt].esp_ealg_id = ealg_id; + esp_info[cnt].esp_ealg_keylen = ek_bits; + esp_info[cnt].esp_aalg_id = aalg_id; + esp_info[cnt].esp_aalg_keylen = ak_bits; + + /* sadb values */ + esp_info[cnt].encryptalg = ealg_id; + esp_info[cnt].authalg = alg_info_esp_aa2sadb(aalg_id); + alg_info->alg_info_cnt++; + + DBG(DBG_CRYPT, + DBG_log("__alg_info_esp_add() ealg=%d aalg=%d cnt=%d" + , ealg_id, aalg_id, alg_info->alg_info_cnt) + ) +} + +/* + * Add ESP alg info _with_ logic (policy): + */ +static void +alg_info_esp_add (struct alg_info *alg_info, int ealg_id, int ek_bits, int aalg_id, int ak_bits) +{ + /* Policy: default to 3DES */ + if (ealg_id == 0) + ealg_id = ESP_3DES; + + if (ealg_id > 0) + { +#ifndef NO_PLUTO + if (aalg_id > 0) +#else + /* Allow no auth for manual conns (from spi.c) */ + if (aalg_id >= 0) +#endif + __alg_info_esp_add((struct alg_info_esp *)alg_info, + ealg_id, ek_bits, + aalg_id, ak_bits); + else + { + /* Policy: default to MD5 and SHA1 */ + __alg_info_esp_add((struct alg_info_esp *)alg_info, + ealg_id, ek_bits, + AUTH_ALGORITHM_HMAC_MD5, ak_bits); + __alg_info_esp_add((struct alg_info_esp *)alg_info, + ealg_id, ek_bits, + AUTH_ALGORITHM_HMAC_SHA1, ak_bits); + } + } +} + +#ifndef NO_PLUTO +/************************************** + * + * IKE alg + * + *************************************/ +/* + * Search oakley_enc_names for a match, eg: + * "3des_cbc" <=> "OAKLEY_3DES_CBC" + */ +static int +ealg_getbyname_ike(const char *const str, int len) +{ + int ret; + + if (!str || !*str) + return -1; + + ret = enum_search_prefix(&oakley_enc_names,"OAKLEY_", str, len); + if (ret >= 0) + return ret; + + ret = enum_search_ppfix(&oakley_enc_names, "OAKLEY_", "_CBC", str, len); + return ret; +} + +/* + * Search oakley_hash_names for a match, eg: + * "md5" <=> "OAKLEY_MD5" + */ +static int +aalg_getbyname_ike(const char *const str, int len) +{ + int ret; + unsigned num; + + if (!str || !*str) + return -1; + + /* interpret 'SHA1' as 'SHA' */ + if (strncasecmp("SHA1", str, len) == 0) + return enum_search(&oakley_hash_names, "OAKLEY_SHA"); + + ret = enum_search_prefix(&oakley_hash_names,"OAKLEY_", str, len); + if (ret >= 0) + return ret; + + sscanf(str, "id%d%n", &ret, &num); + return (ret >=0 && num != strlen(str))? -1 : ret; +} + +/* + * Search oakley_group_names for a match, eg: + * "modp1024" <=> "OAKLEY_GROUP_MODP1024" + */ +static int +modp_getbyname_ike(const char *const str, int len) +{ + int ret; + + if (!str || !*str) + return -1; + + ret = enum_search_prefix(&oakley_group_names,"OAKLEY_GROUP_", str, len); + if (ret >= 0) + return ret; + + ret = enum_search_ppfix(&oakley_group_names, "OAKLEY_GROUP_", " (extension)", str, len); + return ret; +} + +static void +__alg_info_ike_add (struct alg_info_ike *alg_info, int ealg_id, unsigned ek_bits, int aalg_id, unsigned ak_bits, int modp_id) +{ + struct ike_info *ike_info = alg_info->ike; + unsigned cnt = alg_info->alg_info_cnt; + unsigned i; + + /* check for overflows */ + passert(cnt < elemsof(alg_info->ike)); + + /* dont add duplicates */ + for (i = 0;i < cnt; i++) + { + if (ike_info[i].ike_ealg == ealg_id + && (!ek_bits || ike_info[i].ike_eklen == ek_bits) + && ike_info[i].ike_halg == aalg_id + && (!ak_bits || ike_info[i].ike_hklen == ak_bits) + && ike_info[i].ike_modp==modp_id) + return; + } + + ike_info[cnt].ike_ealg = ealg_id; + ike_info[cnt].ike_eklen = ek_bits; + ike_info[cnt].ike_halg = aalg_id; + ike_info[cnt].ike_hklen = ak_bits; + ike_info[cnt].ike_modp = modp_id; + alg_info->alg_info_cnt++; + + DBG(DBG_CRYPT, + DBG_log("__alg_info_ike_add() ealg=%d aalg=%d modp_id=%d, cnt=%d" + , ealg_id, aalg_id, modp_id + , alg_info->alg_info_cnt) + ) +} + +/* + * Proposals will be built by looping over default_ike_groups array and + * merging alg_info (ike_info) contents + */ + +static int default_ike_groups[] = { + OAKLEY_GROUP_MODP1536, + OAKLEY_GROUP_MODP1024 +}; + +/* + * Add IKE alg info _with_ logic (policy): + */ +static void +alg_info_ike_add (struct alg_info *alg_info, int ealg_id, int ek_bits, int aalg_id, int ak_bits, int modp_id) +{ + int i = 0; + int n_groups = elemsof(default_ike_groups); + + /* if specified modp_id avoid loop over default_ike_groups */ + if (modp_id) + { + n_groups=0; + goto in_loop; + } + + for (; n_groups--; i++) + { + modp_id = default_ike_groups[i]; +in_loop: + /* Policy: default to 3DES */ + if (ealg_id == 0) + ealg_id = OAKLEY_3DES_CBC; + + if (ealg_id > 0) + { + if (aalg_id > 0) + __alg_info_ike_add((struct alg_info_ike *)alg_info, + ealg_id, ek_bits, + aalg_id, ak_bits, + modp_id); + else + { + /* Policy: default to MD5 and SHA */ + __alg_info_ike_add((struct alg_info_ike *)alg_info, + ealg_id, ek_bits, + OAKLEY_MD5, ak_bits, + modp_id); + __alg_info_ike_add((struct alg_info_ike *)alg_info, + ealg_id, ek_bits, + OAKLEY_SHA, ak_bits, + modp_id); + } + } + } +} +#endif /* NO_PLUTO */ + +/* + * Creates a new alg_info by parsing passed string + */ +enum parser_state_esp { + ST_INI, + ST_EA, /* encrypt algo */ + ST_EA_END, + ST_EK, /* enc. key length */ + ST_EK_END, + ST_AA, /* auth algo */ + ST_AA_END, + ST_AK, /* auth. key length */ + ST_AK_END, + ST_MODP, /* modp spec */ + ST_FLAG_STRICT, + ST_END, + ST_EOF, + ST_ERR +}; + +static const char *parser_state_esp_names[] = { + "ST_INI", + "ST_EA", + "ST_EA_END", + "ST_EK", + "ST_EK_END", + "ST_AA", + "ST_AA_END", + "ST_AK", + "ST_AK_END", + "ST_MOPD", + "ST_FLAG_STRICT", + "ST_END", + "ST_EOF", + "ST_ERR" +}; + +static const char* +parser_state_name_esp(enum parser_state_esp state) +{ + return parser_state_esp_names[state]; +} + +/* XXX:jjo to implement different parser for ESP and IKE */ +struct parser_context { + unsigned state, old_state; + unsigned protoid; + char ealg_buf[16]; + char aalg_buf[16]; + char modp_buf[16]; + int (*ealg_getbyname)(const char *const str, int len); + int (*aalg_getbyname)(const char *const str, int len); + int (*modp_getbyname)(const char *const str, int len); + char *ealg_str; + char *aalg_str; + char *modp_str; + int eklen; + int aklen; + int ch; + const char *err; +}; + +static inline void +parser_set_state(struct parser_context *p_ctx, enum parser_state_esp state) +{ + if (state != p_ctx->state) + { + p_ctx->old_state = p_ctx->state; + p_ctx->state = state; + } +} + +static int +parser_machine(struct parser_context *p_ctx) +{ + int ch = p_ctx->ch; + + /* special 'absolute' cases */ + p_ctx->err = "No error."; + + /* chars that end algo strings */ + switch (ch){ + case 0: /* end-of-string */ + case '!': /* flag as strict algo list */ + case ',': /* algo string separator */ + switch (p_ctx->state) { + case ST_EA: + case ST_EK: + case ST_AA: + case ST_AK: + case ST_MODP: + case ST_FLAG_STRICT: + { + enum parser_state_esp next_state = 0; + + switch (ch) { + case 0: + next_state = ST_EOF; + break; + case ',': + next_state = ST_END; + break; + case '!': + next_state = ST_FLAG_STRICT; + break; + } + /* ch? parser_set_state(p_ctx, ST_END) : parser_set_state(p_ctx, ST_EOF) ; */ + parser_set_state(p_ctx, next_state); + goto out; + } + default: + p_ctx->err = "String ended with invalid char"; + goto err; + } + } +re_eval: + switch (p_ctx->state) { + case ST_INI: + if (isspace(ch)) + break; + if (isalnum(ch)) + { + *(p_ctx->ealg_str++) = ch; + parser_set_state(p_ctx, ST_EA); + break; + } + p_ctx->err = "No alphanum. char initially found"; + goto err; + case ST_EA: + if (isalpha(ch) || ch == '_') + { + *(p_ctx->ealg_str++) = ch; + break; + } + if (isdigit(ch)) + { + /* bravely switch to enc keylen */ + *(p_ctx->ealg_str) = 0; + parser_set_state(p_ctx, ST_EK); + goto re_eval; + } + if (ch == '-') + { + *(p_ctx->ealg_str) = 0; + parser_set_state(p_ctx, ST_EA_END); + break; + } + p_ctx->err = "No valid char found after enc alg string"; + goto err; + case ST_EA_END: + if (isdigit(ch)) + { + /* bravely switch to enc keylen */ + parser_set_state(p_ctx, ST_EK); + goto re_eval; + } + if (isalpha(ch)) + { + parser_set_state(p_ctx, ST_AA); + goto re_eval; + } + p_ctx->err = "No alphanum char found after enc alg separator"; + goto err; + case ST_EK: + if (ch == '-') + { + parser_set_state(p_ctx, ST_EK_END); + break; + } + if (isdigit(ch)) + { + p_ctx->eklen = p_ctx->eklen*10 + ch - '0'; + break; + } + p_ctx->err = "Non digit or valid separator found while reading enc keylen"; + goto err; + case ST_EK_END: + if (isalpha(ch)) + { + parser_set_state(p_ctx, ST_AA); + goto re_eval; + } + p_ctx->err = "Non alpha char found after enc keylen end separator"; + goto err; + case ST_AA: + if (ch == '-') + { + *(p_ctx->aalg_str++) = 0; + parser_set_state(p_ctx, ST_AA_END); + break; + } + if (isalnum(ch) || ch == '_') + { + *(p_ctx->aalg_str++) = ch; + break; + } + p_ctx->err = "Non alphanum or valid separator found in auth string"; + goto err; + case ST_AA_END: + if (isdigit(ch)) + { + parser_set_state(p_ctx, ST_AK); + goto re_eval; + } + /* Only allow modpXXXX string if we have a modp_getbyname method */ + if ((p_ctx->modp_getbyname) && isalpha(ch)) + { + parser_set_state(p_ctx, ST_MODP); + goto re_eval; + } + p_ctx->err = "Non initial digit found for auth keylen"; + goto err; + case ST_AK: + if (ch=='-') + { + parser_set_state(p_ctx, ST_AK_END); + break; + } + if (isdigit(ch)) + { + p_ctx->aklen = p_ctx->aklen*10 + ch - '0'; + break; + } + p_ctx->err = "Non digit found for auth keylen"; + goto err; + case ST_AK_END: + /* Only allow modpXXXX string if we have a modp_getbyname method */ + if ((p_ctx->modp_getbyname) && isalpha(ch)) + { + parser_set_state(p_ctx, ST_MODP); + goto re_eval; + } + p_ctx->err = "Non alpha char found after auth keylen"; + goto err; + case ST_MODP: + if (isalnum(ch)) + { + *(p_ctx->modp_str++) = ch; + break; + } + p_ctx->err = "Non alphanum char found after in modp string"; + goto err; + case ST_FLAG_STRICT: + if (ch == 0) + parser_set_state(p_ctx, ST_END); + p_ctx->err = "Flags character(s) must be at end of whole string"; + goto err; + + /* XXX */ + case ST_END: + case ST_EOF: + case ST_ERR: + break; + /* XXX */ + } +out: + return p_ctx->state; +err: + parser_set_state(p_ctx, ST_ERR); + return ST_ERR; +} + +/* + * Must be called for each "new" char, with new + * character in ctx.ch + */ +static void +parser_init(struct parser_context *p_ctx, unsigned protoid) +{ + memset(p_ctx, 0, sizeof (*p_ctx)); + p_ctx->protoid = protoid; /* XXX: jjo */ + p_ctx->protoid = PROTO_IPSEC_ESP; + p_ctx->ealg_str = p_ctx->ealg_buf; + p_ctx->aalg_str = p_ctx->aalg_buf; + p_ctx->modp_str = p_ctx->modp_buf; + p_ctx->state = ST_INI; + + switch (protoid) { +#ifndef NO_PLUTO + case PROTO_ISAKMP: + p_ctx->ealg_getbyname = ealg_getbyname_ike; + p_ctx->aalg_getbyname = aalg_getbyname_ike; + p_ctx->modp_getbyname = modp_getbyname_ike; + break; +#endif + case PROTO_IPSEC_ESP: + p_ctx->ealg_getbyname = ealg_getbyname_esp; + p_ctx->aalg_getbyname = aalg_getbyname_esp; + break; + } +} + +static int +parser_alg_info_add(struct parser_context *p_ctx, struct alg_info *alg_info) +{ + int ealg_id = 0; + int aalg_id = 0; + int modp_id = 0; +#ifndef NO_PLUTO + const struct oakley_group_desc *gd; +#endif + + if (*p_ctx->ealg_buf) + { + ealg_id = p_ctx->ealg_getbyname(p_ctx->ealg_buf, strlen(p_ctx->ealg_buf)); + if (ealg_id == ESP_MAGIC_ID) + { + ealg_id = p_ctx->eklen; + p_ctx->eklen = 0; + } + if (ealg_id < 0) + { + p_ctx->err = "enc_alg not found"; + return -1; + } + DBG(DBG_CRYPT, + DBG_log("parser_alg_info_add() ealg_getbyname(\"%s\")=%d" + , p_ctx->ealg_buf + , ealg_id) + ) + } + if (*p_ctx->aalg_buf) + { + aalg_id = p_ctx->aalg_getbyname(p_ctx->aalg_buf, strlen(p_ctx->aalg_buf)); + if (aalg_id < 0) + { + p_ctx->err = "hash_alg not found"; + return -1; + } + DBG(DBG_CRYPT, + DBG_log("parser_alg_info_add() aalg_getbyname(\"%s\")=%d" + , p_ctx->aalg_buf + , aalg_id) + ) + } + if (p_ctx->modp_getbyname && *p_ctx->modp_buf) + { + modp_id = p_ctx->modp_getbyname(p_ctx->modp_buf, strlen(p_ctx->modp_buf)); + if (modp_id < 0) + { + p_ctx->err = "modp group not found"; + return -1; + } + DBG(DBG_CRYPT, + DBG_log("parser_alg_info_add() modp_getbyname(\"%s\")=%d" + , p_ctx->modp_buf + , modp_id) + ) + } + switch (alg_info->alg_info_protoid) { + case PROTO_IPSEC_ESP: + alg_info_esp_add(alg_info, + ealg_id, p_ctx->eklen, + aalg_id, p_ctx->aklen); + break; +#ifndef NO_PLUTO + case PROTO_ISAKMP: + if (modp_id && !(gd = lookup_group(modp_id))) + { + p_ctx->err = "found modp group id, but not supported"; + return -1; + } + alg_info_ike_add(alg_info, + ealg_id, p_ctx->eklen, + aalg_id, p_ctx->aklen, + modp_id); + break; +#endif + default: + return -1; + } + return 0; +} + +static int +alg_info_parse_str (struct alg_info *alg_info, const char *alg_str, const char **err_p) +{ + struct parser_context ctx; + int ret; + const char *ptr; + static char err_buf[256]; + + *err_buf = 0; + parser_init(&ctx, alg_info->alg_info_protoid); + if (err_p) + *err_p = NULL; + + /* use default if nul esp string */ + if (!*alg_str) + { + switch (alg_info->alg_info_protoid) { +#ifndef NO_PLUTO + case PROTO_ISAKMP: + alg_info_ike_add(alg_info, 0, 0, 0, 0, 0); + return 0; +#endif + case PROTO_IPSEC_ESP: + alg_info_esp_add(alg_info, 0, 0, 0, 0); + return 0; + default: + /* IMPOSSIBLE */ + passert(alg_info->alg_info_protoid); + } + } + + for (ret = 0, ptr = alg_str; ret < ST_EOF;) + { + ctx.ch = *ptr++; + ret = parser_machine(&ctx); + + switch (ret) { + case ST_FLAG_STRICT: + alg_info->alg_info_flags |= ALG_INFO_F_STRICT; + break; + case ST_END: + case ST_EOF: + DBG(DBG_CRYPT, + DBG_log("alg_info_parse_str() ealg_buf=%s aalg_buf=%s" + "eklen=%d aklen=%d", + ctx.ealg_buf, ctx.aalg_buf, + ctx.eklen, ctx.aklen) + ) + if (parser_alg_info_add(&ctx, alg_info) < 0) + { + snprintf(err_buf, sizeof(err_buf), + "%s, enc_alg=\"%s\", auth_alg=\"%s\", modp=\"%s\"", + ctx.err, + ctx.ealg_buf, + ctx.aalg_buf, + ctx.modp_buf); + goto err; + } + /* zero out for next run (ST_END) */ + parser_init(&ctx, alg_info->alg_info_protoid); + break; + case ST_ERR: + snprintf(err_buf, sizeof(err_buf), + "%s, just after \"%.*s\" (old_state=%s)", + ctx.err, + (int)(ptr-alg_str-1), alg_str , + parser_state_name_esp(ctx.old_state)); + goto err; + default: + if (!ctx.ch) + break; + } + } + return 0; +err: + if (err_p) + *err_p=err_buf; + return -1; +} + +struct alg_info_esp * +alg_info_esp_create_from_str (const char *alg_str, const char **err_p) +{ + struct alg_info_esp *alg_info_esp; + char esp_buf[256]; + static char err_buf[256]; + char *pfs_name; + int ret = 0; + /* + * alg_info storage should be sized dynamically + * but this may require 2passes to know + * transform count in advance. + */ + alg_info_esp = alloc_thing (struct alg_info_esp, "alg_info_esp"); + if (!alg_info_esp) + goto out; + + pfs_name=index (alg_str, ';'); + if (pfs_name) + { + memcpy(esp_buf, alg_str, pfs_name-alg_str); + esp_buf[pfs_name-alg_str] = 0; + alg_str = esp_buf; + pfs_name++; + + /* if pfs strings AND first char is not '0' */ + if (*pfs_name && pfs_name[0] != '0') + { + ret = modp_getbyname_esp(pfs_name, strlen(pfs_name)); + if (ret < 0) + { + /* Bomb if pfsgroup not found */ + DBG(DBG_CRYPT, + DBG_log("alg_info_esp_create_from_str(): pfsgroup \"%s\" not found" + , pfs_name) + ) + if (*err_p) + { + snprintf(err_buf, sizeof(err_buf), + "pfsgroup \"%s\" not found", + pfs_name); + + *err_p = err_buf; + } + goto out; + } + alg_info_esp->esp_pfsgroup = ret; + } + } + else + alg_info_esp->esp_pfsgroup = 0; + + alg_info_esp->alg_info_protoid = PROTO_IPSEC_ESP; + ret = alg_info_parse_str((struct alg_info *)alg_info_esp, alg_str, err_p) ; +out: + if (ret < 0) + { + pfreeany(alg_info_esp); + alg_info_esp = NULL; + } + return alg_info_esp; +} + +#ifndef NO_PLUTO +struct alg_info_ike * +alg_info_ike_create_from_str (const char *alg_str, const char **err_p) +{ + struct alg_info_ike *alg_info_ike; + /* + * alg_info storage should be sized dynamically + * but this may require 2passes to know + * transform count in advance. + */ + alg_info_ike = alloc_thing (struct alg_info_ike, "alg_info_ike"); + alg_info_ike->alg_info_protoid = PROTO_ISAKMP; + + if (alg_info_parse_str((struct alg_info *)alg_info_ike, + alg_str, err_p) < 0) + { + pfreeany(alg_info_ike); + return NULL; + } + return alg_info_ike; +} +#endif + +/* + * alg_info struct can be shared by + * several connections instances, + * handle free() with ref_cnts + */ +void +alg_info_addref(struct alg_info *alg_info) +{ + if (alg_info != NULL) + { + alg_info->ref_cnt++; + DBG(DBG_CRYPT, + DBG_log("alg_info_addref() alg_info->ref_cnt=%d" + , alg_info->ref_cnt) + ) + } +} + +void +alg_info_delref(struct alg_info **alg_info_p) +{ + struct alg_info *alg_info = *alg_info_p; + + if (alg_info != NULL) + { + passert(alg_info->ref_cnt != 0); + alg_info->ref_cnt--; + DBG(DBG_CRYPT, + DBG_log("alg_info_delref() alg_info->ref_cnt=%d" + , alg_info->ref_cnt) + ) + if (alg_info->ref_cnt == 0) + { + DBG(DBG_CRYPT, + DBG_log("alg_info_delref() freeing alg_info") + ) + alg_info_free(alg_info); + } + *alg_info_p = NULL; + } +} + +/* snprint already parsed transform list (alg_info) */ +int +alg_info_snprint(char *buf, int buflen, struct alg_info *alg_info) +{ + char *ptr = buf; + int np = 0; + struct esp_info *esp_info; +#ifndef NO_PLUTO + struct ike_info *ike_info; +#endif + int cnt; + + switch (alg_info->alg_info_protoid) { + case PROTO_IPSEC_ESP: + { + struct alg_info_esp *alg_info_esp = (struct alg_info_esp *)alg_info; + + ALG_INFO_ESP_FOREACH(alg_info_esp, esp_info, cnt) + { + np = snprintf(ptr, buflen, "%d_%03d-%d, " + , esp_info->esp_ealg_id + , (int)esp_info->esp_ealg_keylen + , esp_info->esp_aalg_id); + ptr += np; + buflen -= np; + if (buflen < 0) + goto out; + } + if (alg_info_esp->esp_pfsgroup) + { + np = snprintf(ptr, buflen, "; pfsgroup=%d; " + , alg_info_esp->esp_pfsgroup); + ptr += np; + buflen -= np; + if (buflen < 0) + goto out; + } + break; + } +#ifndef NO_PLUTO + case PROTO_ISAKMP: + ALG_INFO_IKE_FOREACH((struct alg_info_ike *)alg_info, ike_info, cnt) + { + np = snprintf(ptr, buflen, "%d_%03d-%d-%d, " + , ike_info->ike_ealg + , (int)ike_info->ike_eklen + , ike_info->ike_halg + , ike_info->ike_modp); + ptr += np; + buflen -= np; + if (buflen < 0) + goto out; + } + break; +#endif + default: + np = snprintf(buf, buflen, "INVALID protoid=%d\n" + , alg_info->alg_info_protoid); + ptr += np; + buflen -= np; + goto out; + } + + np = snprintf(ptr, buflen, "%s" + , alg_info->alg_info_flags & ALG_INFO_F_STRICT? + "strict":""); + ptr += np; + buflen -= np; +out: + if (buflen < 0) + { + loglog(RC_LOG_SERIOUS + , "buffer space exhausted in alg_info_snprint_ike(), buflen=%d" + , buflen); + } + + return ptr - buf; +} + +#ifndef NO_PLUTO +int +alg_info_snprint_esp(char *buf, int buflen, struct alg_info_esp *alg_info) +{ + char *ptr = buf; + + int cnt = alg_info->alg_info_cnt; + struct esp_info *esp_info = alg_info->esp; + + while (cnt--) + { + if (kernel_alg_esp_enc_ok(esp_info->esp_ealg_id, 0, NULL) + && kernel_alg_esp_auth_ok(esp_info->esp_aalg_id, NULL)) + { + u_int eklen = (esp_info->esp_ealg_keylen) + ? esp_info->esp_ealg_keylen + : kernel_alg_esp_enc_keylen(esp_info->esp_ealg_id) + * BITS_PER_BYTE; + + u_int aklen = esp_info->esp_aalg_keylen + ? esp_info->esp_aalg_keylen + : kernel_alg_esp_auth_keylen(esp_info->esp_aalg_id) + * BITS_PER_BYTE; + + int ret = snprintf(ptr, buflen, "%d_%03d-%d_%03d, ", + esp_info->esp_ealg_id, eklen, + esp_info->esp_aalg_id, aklen); + ptr += ret; + buflen -= ret; + if (buflen < 0) + break; + } + esp_info++; + } + return ptr - buf; +} + +int +alg_info_snprint_ike(char *buf, int buflen, struct alg_info_ike *alg_info) +{ + char *ptr = buf; + + int cnt = alg_info->alg_info_cnt; + struct ike_info *ike_info = alg_info->ike; + + while (cnt--) + { + struct encrypt_desc *enc_desc = ike_alg_get_encrypter(ike_info->ike_ealg); + struct hash_desc *hash_desc = ike_alg_get_hasher(ike_info->ike_halg); + + if (enc_desc != NULL && hash_desc != NULL + && lookup_group(ike_info->ike_modp)) + { + + u_int eklen = (ike_info->ike_eklen) + ? ike_info->ike_eklen + : enc_desc->keydeflen; + + u_int aklen = (ike_info->ike_hklen) + ? ike_info->ike_hklen + : hash_desc->hash_digest_size * BITS_PER_BYTE; + + int ret = snprintf(ptr, buflen, "%d_%03d-%d_%03d-%d, ", + ike_info->ike_ealg, eklen, + ike_info->ike_halg, aklen, + ike_info->ike_modp); + ptr += ret; + buflen -= ret; + if (buflen < 0) + break; + } + ike_info++; + } + return ptr - buf; +} +#endif /* NO_PLUTO */ |