From: Toke Høiland-Jørgensen <toke@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@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: