summaryrefslogtreecommitdiff
path: root/src/pluto/alg_info.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/pluto/alg_info.c')
-rw-r--r--src/pluto/alg_info.c1205
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 */