[PATCH v3 4/7] nest: Allow specifying security keys as hex bytes as well as strings

Toke Høiland-Jørgensen toke at toke.dk
Tue Nov 24 16:21:51 CET 2020


From: Toke Høiland-Jørgensen <toke at toke.dk>

This adds support for specifying a password in raw hexadecimal bytes form,
via the 'key' keyword. The result is the same whether a password is
specified as a quoted string or a hex-encoded byte string, this just makes
it more convenient to input high-entropy byte strings as MAC keys.

This means that the config string:

password "test" { algorithm hmac sha256; };

is equivalent to:

key 74:65:73:74 { algorithm hmac sha256; };

or

key 74657374 { algorithm hmac sha256; };

Signed-off-by: Toke Høiland-Jørgensen <toke at toke.dk>
---
 conf/cf-lex.l   |   31 +++++++++++++++++++++++++++++++
 conf/conf.h     |    5 +++++
 conf/confbase.Y |    2 ++
 doc/bird.sgml   |    9 ++++++++-
 lib/string.h    |    1 +
 lib/strtoul.c   |   27 +++++++++++++++++++++++++++
 nest/config.Y   |   40 ++++++++++++++++++++++------------------
 7 files changed, 96 insertions(+), 19 deletions(-)

diff --git a/conf/cf-lex.l b/conf/cf-lex.l
index 05288b1a5..8c88fcaa3 100644
--- a/conf/cf-lex.l
+++ b/conf/cf-lex.l
@@ -255,6 +255,37 @@ WHITE [ \t]
   return IP4;
 }
 
+{XIGIT}{2}(:{XIGIT}{2}|{XIGIT}{2}){15,} {
+  char *s = yytext;
+  size_t len = 0, i;
+  struct bytestring *bytes;
+  byte *b;
+
+  while (*s) {
+    len++;
+    s += 2;
+    if (*s == ':')
+      s++;
+  }
+  bytes = cfg_allocz(sizeof(*bytes) + len);
+
+  bytes->sz = len;
+  b = &bytes->b[0];
+  s = yytext;
+  errno = 0;
+  for (i = 0; i < len; i++) {
+    *b = bstrtobyte16(s);
+    if (errno == ERANGE)
+      cf_error("Invalid hex string");
+    b++;
+    s += 2;
+    if (*s == ':')
+      s++;
+  }
+  cf_lval.b = bytes;
+  return BYTES;
+}
+
 ({XIGIT}*::|({XIGIT}*:){3,})({XIGIT}*|{DIGIT}+\.{DIGIT}+\.{DIGIT}+\.{DIGIT}+) {
   if (!ip6_pton(yytext, &cf_lval.ip6))
     cf_error("Invalid IPv6 address %s", yytext);
diff --git a/conf/conf.h b/conf/conf.h
index 34c6818dc..bdfcda566 100644
--- a/conf/conf.h
+++ b/conf/conf.h
@@ -127,6 +127,11 @@ struct symbol {
   char name[0];
 };
 
+struct bytestring {
+    size_t sz;
+    byte b[];
+};
+
 struct sym_scope {
   struct sym_scope *next;		/* Next on scope stack */
   struct symbol *name;			/* Name of this scope */
diff --git a/conf/confbase.Y b/conf/confbase.Y
index 67dcac6c5..ca273a009 100644
--- a/conf/confbase.Y
+++ b/conf/confbase.Y
@@ -91,6 +91,7 @@ CF_DECLS
   struct channel_limit cl;
   struct timeformat *tf;
   mpls_label_stack *mls;
+  struct bytestring *b;
 }
 
 %token END CLI_MARKER INVALID_TOKEN ELSECOL DDOT
@@ -102,6 +103,7 @@ CF_DECLS
 %token <i64> VPN_RD
 %token <s> CF_SYM_KNOWN CF_SYM_UNDEFINED
 %token <t> TEXT
+%token <b> BYTES
 %type <iface> ipa_scope
 
 %type <i> expr bool pxlen4
diff --git a/doc/bird.sgml b/doc/bird.sgml
index 15d7c3bcf..6f52d41f1 100644
--- a/doc/bird.sgml
+++ b/doc/bird.sgml
@@ -765,7 +765,7 @@ agreement").
 	protocol packets are processed in the local TX queues. This option is
 	Linux specific. Default value is 7 (highest priority, privileged traffic).
 
-	<tag><label id="proto-pass">password "<m/password/" [ { <m>password options</m> } ]</tag>
+	<tag><label id="proto-pass">password "<m/password/" [ { <m>password options</m> } ] | key <m/hex_key/ </tag>
 	Specifies a password that can be used by the protocol as a shared secret
 	key. Password option can be used more times to specify more passwords.
 	If more passwords are specified, it is a protocol-dependent decision
@@ -773,6 +773,13 @@ agreement").
 	authentication is enabled, authentication can be enabled by separate,
 	protocol-dependent <cf/authentication/ option.
 
+        A password can also be specified as a hexadecimal key using the
+        <cf/key/ option, with the hexadecimal key following the option unquoted.
+        The same sub-options can be used in both cases. The bytes in the hex_key
+        can optionally be colon-separated, and a key specified this way must be
+        at least 16 bytes long (although specific algorithms can impose other
+        restrictions).
+
 	This option is allowed in BFD, OSPF and RIP protocols. BGP has also
 	<cf/password/ option, but it is slightly different and described
 	separately.
diff --git a/lib/string.h b/lib/string.h
index 0f650178d..976b1c247 100644
--- a/lib/string.h
+++ b/lib/string.h
@@ -26,6 +26,7 @@ void buffer_puts(buffer *buf, const char *str);
 
 u64 bstrtoul10(const char *str, char **end);
 u64 bstrtoul16(const char *str, char **end);
+byte bstrtobyte16(const char *str);
 
 int patmatch(const byte *pat, const byte *str);
 
diff --git a/lib/strtoul.c b/lib/strtoul.c
index 44a1bb1dd..a5b11f68d 100644
--- a/lib/strtoul.c
+++ b/lib/strtoul.c
@@ -59,3 +59,30 @@ bstrtoul16(const char *str, char **end)
   errno = ERANGE;
   return UINT64_MAX;
 }
+
+byte
+bstrtobyte16(const char *str)
+{
+  byte out = 0;
+  for (int i=0; i<2; i++) {
+    switch (str[i]) {
+      case '0' ... '9':
+	out *= 16;
+	out += str[i] - '0';
+	break;
+      case 'a' ... 'f':
+	out *= 16;
+	out += str[i] + 10 - 'a';
+	break;
+      case 'A' ... 'F':
+	out *= 16;
+	out += str[i] + 10 - 'A';
+	break;
+      default:
+	errno = ERANGE;
+	return -1;
+    }
+  }
+
+  return out;
+}
diff --git a/nest/config.Y b/nest/config.Y
index ca920ca33..82c2194fa 100644
--- a/nest/config.Y
+++ b/nest/config.Y
@@ -37,6 +37,25 @@ iface_patt_check(void)
       cf_error("Interface name/mask expected, not IP prefix");
 }
 
+static inline void
+init_password(void *data, size_t sz)
+{
+   if (!this_p_list) {
+      this_p_list = cfg_allocz(sizeof(list));
+      init_list(this_p_list);
+      password_id = 1;
+   }
+   this_p_item = cfg_allocz(sizeof (struct password_item));
+   this_p_item->password = data;
+   this_p_item->length = sz;
+   this_p_item->genfrom = 0;
+   this_p_item->gento = TIME_INFINITY;
+   this_p_item->accfrom = 0;
+   this_p_item->accto = TIME_INFINITY;
+   this_p_item->id = password_id++;
+   this_p_item->alg = ALG_UNDEFINED;
+   add_tail(this_p_list, &this_p_item->n);
+}
 
 static inline void
 reset_passwords(void)
@@ -99,7 +118,7 @@ CF_KEYWORDS(RELOAD, IN, OUT, MRTDUMP, MESSAGES, RESTRICT, MEMORY, IGP_METRIC, CL
 CF_KEYWORDS(TIMEFORMAT, ISO, SHORT, LONG, ROUTE, PROTOCOL, BASE, LOG, S, MS, US)
 CF_KEYWORDS(GRACEFUL, RESTART, WAIT, MAX, FLUSH, AS)
 CF_KEYWORDS(MIN, IDLE, RX, TX, INTERVAL, MULTIPLIER, PASSIVE)
-CF_KEYWORDS(CHECK, LINK)
+CF_KEYWORDS(CHECK, LINK, KEY)
 
 /* For r_args_channel */
 CF_KEYWORDS(IPV4, IPV4_MC, IPV4_MPLS, IPV6, IPV6_MC, IPV6_MPLS, IPV6_SADR, VPN4, VPN4_MC, VPN4_MPLS, VPN6, VPN6_MC, VPN6_MPLS, ROA4, ROA6, FLOW4, FLOW6, MPLS, PRI, SEC)
@@ -477,23 +496,8 @@ password_item:
 ;
 
 password_item_begin:
-   PASSWORD text {
-     if (!this_p_list) {
-	this_p_list = cfg_allocz(sizeof(list));
-	init_list(this_p_list);
-	password_id = 1;
-     }
-     this_p_item = cfg_allocz(sizeof(struct password_item));
-     this_p_item->password = $2;
-     this_p_item->length = strlen($2);
-     this_p_item->genfrom = 0;
-     this_p_item->gento = TIME_INFINITY;
-     this_p_item->accfrom = 0;
-     this_p_item->accto = TIME_INFINITY;
-     this_p_item->id = password_id++;
-     this_p_item->alg = ALG_UNDEFINED;
-     add_tail(this_p_list, &this_p_item->n);
-   }
+    PASSWORD text { init_password((void *)$2, strlen($2)); }
+  | KEY BYTES { init_password($2->b, $2->sz); }
 ;
 
 password_item_params:



More information about the Bird-users mailing list