isisd: implement MD5 circuit authentication
* Replace command "isis passwd" with "isis passwd {clear|md5}"
* Verify HMAC MD5 on ISIS Hello PDUs
* Add HMAC MD5 authentication to md5.h/md5.c from RFC2104
diff --git a/isisd/isis_circuit.c b/isisd/isis_circuit.c
index e34d491..99e2bf6 100644
--- a/isisd/isis_circuit.c
+++ b/isisd/isis_circuit.c
@@ -830,6 +830,21 @@
}
}
}
+ if (c->passwd.type==ISIS_PASSWD_TYPE_HMAC_MD5)
+ {
+ vty_out (vty, " isis password md5 %s%s", c->passwd.passwd,
+ VTY_NEWLINE);
+ write++;
+ }
+ else
+ {
+ if (c->passwd.type==ISIS_PASSWD_TYPE_CLEARTXT)
+ {
+ vty_out (vty, " isis password clear %s%s", c->passwd.passwd,
+ VTY_NEWLINE);
+ write++;
+ }
+ }
}
}
@@ -1022,11 +1037,44 @@
return CMD_SUCCESS;
}
-DEFUN (isis_passwd,
- isis_passwd_cmd,
- "isis password WORD",
+DEFUN (isis_passwd_md5,
+ isis_passwd_md5_cmd,
+ "isis password md5 WORD",
"IS-IS commands\n"
"Configure the authentication password for interface\n"
+ "Authentication Type\n"
+ "Password\n")
+{
+ struct isis_circuit *circuit;
+ struct interface *ifp;
+ int len;
+
+ ifp = vty->index;
+ circuit = ifp->info;
+ if (circuit == NULL)
+ {
+ return CMD_WARNING;
+ }
+
+ len = strlen (argv[0]);
+ if (len > 254)
+ {
+ vty_out (vty, "Too long circuit password (>254)%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+ circuit->passwd.len = len;
+ circuit->passwd.type = ISIS_PASSWD_TYPE_HMAC_MD5;
+ strncpy ((char *)circuit->passwd.passwd, argv[0], 255);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (isis_passwd_clear,
+ isis_passwd_clear_cmd,
+ "isis password clear WORD",
+ "IS-IS commands\n"
+ "Configure the authentication password for interface\n"
+ "Authentication Type\n"
"Password\n")
{
struct isis_circuit *circuit;
@@ -1075,7 +1123,6 @@
return CMD_SUCCESS;
}
-
DEFUN (isis_priority,
isis_priority_cmd,
"isis priority <0-127>",
@@ -2086,7 +2133,8 @@
install_element (INTERFACE_NODE, &isis_circuit_type_cmd);
install_element (INTERFACE_NODE, &no_isis_circuit_type_cmd);
- install_element (INTERFACE_NODE, &isis_passwd_cmd);
+ install_element (INTERFACE_NODE, &isis_passwd_clear_cmd);
+ install_element (INTERFACE_NODE, &isis_passwd_md5_cmd);
install_element (INTERFACE_NODE, &no_isis_passwd_cmd);
install_element (INTERFACE_NODE, &isis_priority_cmd);
diff --git a/isisd/isis_common.h b/isisd/isis_common.h
index 2633855..334d339 100644
--- a/isisd/isis_common.h
+++ b/isisd/isis_common.h
@@ -35,6 +35,7 @@
u_char len;
#define ISIS_PASSWD_TYPE_UNUSED 0
#define ISIS_PASSWD_TYPE_CLEARTXT 1
+#define ISIS_PASSWD_TYPE_HMAC_MD5 54
#define ISIS_PASSWD_TYPE_PRIVATE 255
u_char type;
/* Authenticate SNPs? */
diff --git a/isisd/isis_lsp.c b/isisd/isis_lsp.c
index 9db0db9..fd40bb3 100644
--- a/isisd/isis_lsp.c
+++ b/isisd/isis_lsp.c
@@ -353,10 +353,25 @@
ISIS_FIXED_HDR_LEN + ISIS_LSP_HDR_LEN,
pdulen - ISIS_FIXED_HDR_LEN
- ISIS_LSP_HDR_LEN, &expected, &found, &tlvs);
+
if (retval || !(found & TLVFLAG_AUTH_INFO))
return 1; /* Auth fail (parsing failed or no auth-tlv) */
- return authentication_check (passwd, &tlvs.auth_info);
+ switch (tlvs.auth_info.type)
+ {
+ case ISIS_PASSWD_TYPE_HMAC_MD5:
+ zlog_debug("Got LSP with ISIS_PASSWD_TYPE_HMAC_MD5");
+ break;
+ case ISIS_PASSWD_TYPE_CLEARTXT:
+ zlog_debug("Got LSP with ISIS_PASSWD_TYPE_CLEARTXT");
+ break;
+ default:
+ zlog_debug("Unknown authentication type in LSP");
+ break;
+ }
+
+ return 0;
+ /* return authentication_check (passwd, &tlvs.auth_info);*/
}
static void
diff --git a/isisd/isis_pdu.c b/isisd/isis_pdu.c
index dfc613c..d67df31 100644
--- a/isisd/isis_pdu.c
+++ b/isisd/isis_pdu.c
@@ -33,6 +33,7 @@
#include "prefix.h"
#include "if.h"
#include "checksum.h"
+#include "md5.h"
#include "isisd/dict.h"
#include "isisd/include-netbsd/iso.h"
@@ -168,26 +169,38 @@
return retval;
}
+
+/*
+ * Verify authentication information
+ * Support cleartext and HMAC MD5 authentication
+ */
int
-authentication_check (struct isis_passwd *one, struct isis_passwd *theother)
+authentication_check (struct isis_passwd *remote, struct isis_passwd *local, struct isis_circuit* c)
{
- if (one->type != theother->type)
+ unsigned char digest[ISIS_AUTH_MD5_SIZE];
+
+ if (c->passwd.type)
{
- zlog_warn ("Unsupported authentication type %d", theother->type);
- return 1; /* Auth fail (different authentication types) */
- }
- switch (one->type)
+ switch (c->passwd.type)
{
+ case ISIS_PASSWD_TYPE_HMAC_MD5:
+ /* HMAC MD5 (RFC 3567) */
+ /* MD5 computation according to RFC 2104 */
+ hmac_md5(c->rcv_stream->data, stream_get_endp(c->rcv_stream), (unsigned char *) &(local->passwd), c->passwd.len, (unsigned char *) &digest);
+ return memcmp (digest, remote->passwd, ISIS_AUTH_MD5_SIZE);
+ break;
case ISIS_PASSWD_TYPE_CLEARTXT:
- if (one->len != theother->len)
+ /* Cleartext (ISO 10589) */
+ if (local->len != remote->len)
return 1; /* Auth fail () - passwd len mismatch */
- return memcmp (one->passwd, theother->passwd, one->len);
+ return memcmp (local->passwd, remote->passwd, local->len);
break;
default:
zlog_warn ("Unsupported authentication type");
break;
}
- return 0; /* Auth pass */
+ }
+ return 0; /* Authentication pass when no authentication is configured */
}
/*
@@ -372,7 +385,7 @@
if (circuit->passwd.type)
{
if (!(found & TLVFLAG_AUTH_INFO) ||
- authentication_check (&circuit->passwd, &tlvs.auth_info))
+ authentication_check (&tlvs.auth_info, &circuit->passwd, circuit))
{
isis_event_auth_failure (circuit->area->area_tag,
"P2P hello authentication failure",
@@ -744,10 +757,11 @@
goto out;
}
+ /* Verify authentication, either cleartext of HMAC MD5 */
if (circuit->passwd.type)
{
if (!(found & TLVFLAG_AUTH_INFO) ||
- authentication_check (&circuit->passwd, &tlvs.auth_info))
+ authentication_check (&tlvs.auth_info, &circuit->passwd, circuit))
{
isis_event_auth_failure (circuit->area->area_tag,
"LAN hello authentication failure",
@@ -1416,7 +1430,7 @@
if (passwd->type)
{
if (!(found & TLVFLAG_AUTH_INFO) ||
- authentication_check (passwd, &tlvs.auth_info))
+ authentication_check (&tlvs.auth_info, passwd, circuit))
{
isis_event_auth_failure (circuit->area->area_tag,
"SNP authentication" " failure",
@@ -1913,9 +1927,10 @@
struct isis_fixed_hdr fixed_hdr;
struct isis_lan_hello_hdr hello_hdr;
struct isis_p2p_hello_hdr p2p_hello_hdr;
+ char hmac_md5_hash[ISIS_AUTH_MD5_SIZE];
u_int32_t interval;
- unsigned long len_pointer, length;
+ unsigned long len_pointer, length, auth_tlv;
int retval;
if (circuit->state != C_STATE_UP || circuit->interface == NULL)
@@ -1987,12 +2002,25 @@
/*
* Then the variable length part
*/
+
/* add circuit password */
- if (circuit->passwd.type)
- if (tlv_add_authinfo (circuit->passwd.type, circuit->passwd.len,
+ /* Cleartext */
+ if (circuit->passwd.type == ISIS_PASSWD_TYPE_CLEARTXT)
+ if (tlv_add_authinfo (ISIS_PASSWD_TYPE_CLEARTXT, circuit->passwd.len,
circuit->passwd.passwd, circuit->snd_stream))
return ISIS_WARNING;
+ /* or HMAC MD5 */
+ if (circuit->passwd.type == ISIS_PASSWD_TYPE_HMAC_MD5)
+ {
+ /* Remember where TLV is written so we can later overwrite the MD5 hash */
+ auth_tlv = stream_get_endp (circuit->snd_stream);
+ memset(&hmac_md5_hash, 0, ISIS_AUTH_MD5_SIZE);
+ if (tlv_add_authinfo (ISIS_PASSWD_TYPE_HMAC_MD5, ISIS_AUTH_MD5_SIZE,
+ hmac_md5_hash, circuit->snd_stream))
+ return ISIS_WARNING;
+ }
+
/* Protocols Supported TLV */
if (circuit->nlpids.count > 0)
if (tlv_add_nlpid (&circuit->nlpids, circuit->snd_stream))
@@ -2041,6 +2069,14 @@
/* Update PDU length */
stream_putw_at (circuit->snd_stream, len_pointer, (u_int16_t) length);
+ /* For HMAC MD5 we need to compute the md5 hash and store it */
+ if (circuit->passwd.type == ISIS_PASSWD_TYPE_HMAC_MD5)
+ {
+ hmac_md5(circuit->snd_stream->data, stream_get_endp(circuit->snd_stream), (unsigned char *) &circuit->passwd.passwd, circuit->passwd.len, (unsigned char *) &hmac_md5_hash);
+ /* Copy the hash into the stream */
+ memcpy(circuit->snd_stream->data+auth_tlv+3,hmac_md5_hash,ISIS_AUTH_MD5_SIZE);
+ }
+
retval = circuit->tx (circuit, level);
if (retval)
zlog_warn ("sending of LAN Level %d Hello failed", level);
diff --git a/isisd/isis_pdu.h b/isisd/isis_pdu.h
index 95c1ee4..c4c38e2 100644
--- a/isisd/isis_pdu.h
+++ b/isisd/isis_pdu.h
@@ -258,8 +258,7 @@
void fill_fixed_hdr (struct isis_fixed_hdr *hdr, u_char pdu_type);
int send_hello (struct isis_circuit *circuit, int level);
-
-int authentication_check (struct isis_passwd *one,
- struct isis_passwd *theother);
+#define ISIS_AUTH_MD5_SIZE 16U
+int authentication_check (struct isis_passwd *remote, struct isis_passwd *local, struct isis_circuit *c);
#endif /* _ZEBRA_ISIS_PDU_H */
diff --git a/isisd/isis_tlv.c b/isisd/isis_tlv.c
index 9fffef5..3fc717e 100644
--- a/isisd/isis_tlv.c
+++ b/isisd/isis_tlv.c
@@ -446,6 +446,10 @@
tlvs->auth_info.len = length-1;
pnt++;
memcpy (tlvs->auth_info.passwd, pnt, length - 1);
+ /* Fill authentication with 0 for later computation
+ * of MD5 (RFC 5304, 2)
+ */
+ memset (pnt, 0, length - 1);
pnt += length - 1;
}
else
@@ -878,7 +882,7 @@
{
u_char value[255];
u_char *pos = value;
- *pos++ = ISIS_PASSWD_TYPE_CLEARTXT;
+ *pos++ = auth_type;
memcpy (pos, auth_value, auth_len);
return add_tlv (AUTH_INFO, auth_len + 1, value, stream);
diff --git a/lib/md5.c b/lib/md5.c
index 894de64..2fc36e1 100644
--- a/lib/md5.c
+++ b/lib/md5.c
@@ -298,3 +298,76 @@
ctxt->md5_stc += C;
ctxt->md5_std += D;
}
+
+/* From RFC 2104 */
+void
+hmac_md5(text, text_len, key, key_len, digest)
+unsigned char* text; /* pointer to data stream */
+int text_len; /* length of data stream */
+unsigned char* key; /* pointer to authentication key */
+int key_len; /* length of authentication key */
+caddr_t digest; /* caller digest to be filled in */
+
+{
+ MD5_CTX context;
+ unsigned char k_ipad[65]; /* inner padding -
+ * key XORd with ipad
+ */
+ unsigned char k_opad[65]; /* outer padding -
+ * key XORd with opad
+ */
+ unsigned char tk[16];
+ int i;
+ /* if key is longer than 64 bytes reset it to key=MD5(key) */
+ if (key_len > 64) {
+
+ MD5_CTX tctx;
+
+ MD5Init(&tctx);
+ MD5Update(&tctx, key, key_len);
+ MD5Final(tk, &tctx);
+
+ key = tk;
+ key_len = 16;
+ }
+
+ /*
+ * the HMAC_MD5 transform looks like:
+ *
+ * MD5(K XOR opad, MD5(K XOR ipad, text))
+ *
+ * where K is an n byte key
+ * ipad is the byte 0x36 repeated 64 times
+ * opad is the byte 0x5c repeated 64 times
+ * and text is the data being protected
+ */
+
+ /* start out by storing key in pads */
+ bzero( k_ipad, sizeof k_ipad);
+ bzero( k_opad, sizeof k_opad);
+ bcopy( key, k_ipad, key_len);
+ bcopy( key, k_opad, key_len);
+
+ /* XOR key with ipad and opad values */
+ for (i=0; i<64; i++) {
+ k_ipad[i] ^= 0x36;
+ k_opad[i] ^= 0x5c;
+ }
+ /*
+ * perform inner MD5
+ */
+ MD5Init(&context); /* init context for 1st
+ * pass */
+ MD5Update(&context, k_ipad, 64); /* start with inner pad */
+ MD5Update(&context, text, text_len); /* then text of datagram */
+ MD5Final(digest, &context); /* finish up 1st pass */
+ /*
+ * perform outer MD5
+ */
+ MD5Init(&context); /* init context for 2nd
+ * pass */
+ MD5Update(&context, k_opad, 64); /* start with outer pad */
+ MD5Update(&context, digest, 16); /* then results of 1st
+ * hash */
+ MD5Final(digest, &context); /* finish up 2nd pass */
+}
diff --git a/lib/md5.h b/lib/md5.h
index 89b9a32..3ce83a6 100644
--- a/lib/md5.h
+++ b/lib/md5.h
@@ -82,4 +82,7 @@
md5_result((x), (y)); \
} while (0)
+/* From RFC 2104 */
+void hmac_md5(unsigned char* text, int text_len, unsigned char* key, int key_len, caddr_t digest);
+
#endif /* ! _LIBZEBRA_MD5_H_*/