summaryrefslogtreecommitdiffstats
path: root/sysdep
diff options
context:
space:
mode:
authorOndrej Zajicek <santiago@crfreenet.org>2008-10-26 22:42:39 +0100
committerOndrej Zajicek <santiago@crfreenet.org>2008-10-26 22:42:39 +0100
commitd51aa2819005a03e4cfb6f62333be6ccadfb3c06 (patch)
tree2a76b826cfdac4cf1a94a305f9fe416a2df45840 /sysdep
parent11cb620266035ffbe17b21c4a174380cb8b6a521 (diff)
downloadbird-d51aa2819005a03e4cfb6f62333be6ccadfb3c06.tar
bird-d51aa2819005a03e4cfb6f62333be6ccadfb3c06.zip
Implementation of MD5 authentication of BGP sessions.
Diffstat (limited to 'sysdep')
-rw-r--r--sysdep/linux/sysio.h21
-rw-r--r--sysdep/unix/io.c75
2 files changed, 96 insertions, 0 deletions
diff --git a/sysdep/linux/sysio.h b/sysdep/linux/sysio.h
index 3a29cdc..b0aff71 100644
--- a/sysdep/linux/sysio.h
+++ b/sysdep/linux/sysio.h
@@ -139,3 +139,24 @@ static inline char *sysio_mcast_join(sock *s)
#endif
#endif
+
+#include <linux/socket.h>
+#include <linux/tcp.h>
+
+/* For the case that we have older kernel headers */
+/* Copied from Linux kernel file include/linux/tcp.h */
+
+#ifndef TCP_MD5SIG
+
+#define TCP_MD5SIG 14
+#define TCP_MD5SIG_MAXKEYLEN 80
+
+struct tcp_md5sig {
+ struct __kernel_sockaddr_storage tcpm_addr; /* address associated */
+ __u16 __tcpm_pad1; /* zero */
+ __u16 tcpm_keylen; /* key length */
+ __u32 __tcpm_pad2; /* zero */
+ __u8 tcpm_key[TCP_MD5SIG_MAXKEYLEN]; /* key (binary) */
+};
+
+#endif
diff --git a/sysdep/unix/io.c b/sysdep/unix/io.c
index 7dcca21..6faa176 100644
--- a/sysdep/unix/io.c
+++ b/sysdep/unix/io.c
@@ -546,6 +546,7 @@ sk_new(pool *p)
s->err_hook = NULL;
s->fd = -1;
s->rbuf_alloc = s->tbuf_alloc = NULL;
+ s->password = NULL;
return s;
}
@@ -642,6 +643,71 @@ bad:
return err;
}
+
+/* FIXME: check portability */
+
+static int
+sk_set_md5_auth_int(sock *s, sockaddr *sa, char *passwd)
+{
+ struct tcp_md5sig md5;
+
+ memset(&md5, 0, sizeof(md5));
+ memcpy(&md5.tcpm_addr, (struct sockaddr *) sa, sizeof(*sa));
+
+ if (passwd)
+ {
+ int len = strlen(passwd);
+
+ if (len > TCP_MD5SIG_MAXKEYLEN)
+ {
+ log(L_ERR "MD5 password too long");
+ return -1;
+ }
+
+ md5.tcpm_keylen = len;
+ memcpy(&md5.tcpm_key, passwd, len);
+ }
+
+ int rv = setsockopt(s->fd, IPPROTO_TCP, TCP_MD5SIG, &md5, sizeof(md5));
+
+ if (rv < 0)
+ {
+ if (errno == ENOPROTOOPT)
+ log(L_ERR "Kernel does not support TCP MD5 signatures");
+ else
+ log(L_ERR "sk_set_md5_auth_int: setsockopt: %m");
+ }
+
+ return rv;
+}
+
+/**
+ * sk_set_md5_auth - add / remove MD5 security association for given socket.
+ * @s: socket
+ * @a: IP address of the other side
+ * @passwd: password used for MD5 authentication
+ *
+ * In TCP MD5 handling code in kernel, there is a set of pairs
+ * (address, password) used to choose password according to
+ * address of the other side. This function is useful for
+ * listening socket, for active sockets it is enough to set
+ * s->password field.
+ *
+ * When called with passwd != NULL, the new pair is added,
+ * When called with passwd == NULL, the existing pair is removed.
+ *
+ * Result: 0 for success, -1 for an error.
+ */
+
+int
+sk_set_md5_auth(sock *s, ip_addr a, char *passwd)
+{
+ sockaddr sa;
+ fill_in_sockaddr(&sa, a, 0);
+ return sk_set_md5_auth_int(s, &sa, passwd);
+}
+
+
static void
sk_tcp_connected(sock *s)
{
@@ -805,6 +871,14 @@ sk_open(sock *s)
ERR("bind");
}
fill_in_sockaddr(&sa, s->daddr, s->dport);
+
+ if (s->password)
+ {
+ int rv = sk_set_md5_auth_int(s, &sa, s->password);
+ if (rv < 0)
+ goto bad_no_log;
+ }
+
switch (type)
{
case SK_TCP_ACTIVE:
@@ -846,6 +920,7 @@ sk_open(sock *s)
bad:
log(L_ERR "sk_open: %s: %m", err);
+bad_no_log:
close(fd);
s->fd = -1;
return -1;