summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorDavid Lamparter <equinox@opensourcerouting.org>2017-08-06 07:35:50 +0200
committerDavid Lamparter <equinox@opensourcerouting.org>2017-08-08 11:14:05 +0200
commit689f5a8c84b95dbd31ecab481f8f2977965fe741 (patch)
tree7c2a2b69908b3c3ac60e0de41b5a3f85caec2ef5 /lib
parent00857b241e6c27efad54a19b4226c37f04d80bf5 (diff)
*: remove --enable-tcp-zebra, rework ZAPI path
This adds "@tcp" as new choice on the -z option present in zebra and the protocol daemons. The --enable-tcp-zebra option on configure is no longer needed, both UNIX and TCP socket support is always available. Note that @tcp should not be used by default (e.g. in an init script), and --enable-tcp-zebra should never have been in any distro package builds, because **** TCP-ZEBRA IS A SECURITY PROBLEM **** It allows arbitrary local users to mess with the routing table and inject bogus data -- and also ZAPI is not designed to be robust against attacks. Signed-off-by: David Lamparter <equinox@opensourcerouting.org>
Diffstat (limited to 'lib')
-rw-r--r--lib/libfrr.c107
-rw-r--r--lib/libfrr.h4
-rw-r--r--lib/zclient.c114
-rw-r--r--lib/zclient.h10
4 files changed, 133 insertions, 102 deletions
diff --git a/lib/libfrr.c b/lib/libfrr.c
index 022296b3fb..e92456cf77 100644
--- a/lib/libfrr.c
+++ b/lib/libfrr.c
@@ -19,6 +19,7 @@
*/
#include <zebra.h>
+#include <sys/un.h>
#include "libfrr.h"
#include "getopt.h"
@@ -40,6 +41,7 @@ char frr_protoname[256] = "NONE";
char frr_protonameinst[256] = "NONE";
char config_default[256];
+char frr_zclientpath[256];
static char pidfile_default[256];
static char vtypath_default[256];
@@ -127,6 +129,100 @@ static const struct optspec os_user = {"u:g:",
lo_user};
+bool frr_zclient_addr(struct sockaddr_storage *sa, socklen_t *sa_len,
+ const char *path)
+{
+ memset(sa, 0, sizeof(*sa));
+
+ if (!path)
+ path = ZEBRA_SERV_PATH;
+
+ if (!strncmp(path, ZAPI_TCP_PATHNAME, strlen(ZAPI_TCP_PATHNAME))) {
+ int af;
+ int port = ZEBRA_PORT;
+ char *err = NULL;
+ struct sockaddr_in *sin = NULL;
+ struct sockaddr_in6 *sin6 = NULL;
+
+ path += strlen(ZAPI_TCP_PATHNAME);
+
+ switch (path[0]) {
+ case '4':
+ path++;
+ af = AF_INET;
+ break;
+ case '6':
+ path++;
+ /* fallthrough */
+ default:
+ af = AF_INET6;
+ break;
+ }
+
+ switch (path[0]) {
+ case '\0':
+ break;
+ case ':':
+ path++;
+ port = strtoul(path, &err, 10);
+ if (*err || !*path)
+ return false;
+ break;
+ default:
+ return false;
+ }
+
+ sa->ss_family = af;
+ switch (af) {
+ case AF_INET:
+ sin = (struct sockaddr_in *)sa;
+ sin->sin_port = htons(port);
+ sin->sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+ *sa_len = sizeof(struct sockaddr_in);
+#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN
+ sin->sin_len = *sa_len;
+#endif
+ break;
+ case AF_INET6:
+ sin6 = (struct sockaddr_in6 *)sa;
+ sin6->sin6_port = htons(port);
+ inet_pton(AF_INET6, "::1", &sin6->sin6_addr);
+ *sa_len = sizeof(struct sockaddr_in6);
+#ifdef SIN6_LEN
+ sin6->sin6_len = *sa_len;
+#endif
+ break;
+ }
+ } else {
+ /* "sun" is a #define on solaris */
+ struct sockaddr_un *suna = (struct sockaddr_un *)sa;
+
+ suna->sun_family = AF_UNIX;
+ strlcpy(suna->sun_path, path, sizeof(suna->sun_path));
+#ifdef HAVE_STRUCT_SOCKADDR_UN_SUN_LEN
+ *sa_len = suna->sun_len = SUN_LEN(suna);
+#else
+ *sa_len = sizeof(suna->sun_family) + strlen(suna->sun_path);
+#endif /* HAVE_STRUCT_SOCKADDR_UN_SUN_LEN */
+#if 0
+ /* this is left here for future reference; Linux abstract
+ * socket namespace support can be enabled by replacing
+ * above #if 0 with #ifdef GNU_LINUX.
+ *
+ * THIS IS A SECURITY ISSUE, the abstract socket namespace
+ * does not have user/group permission control on sockets.
+ * we'd need to implement SCM_CREDENTIALS support first to
+ * check that only proper users can connect to abstract
+ * sockets. (same problem as tcp-zebra, except there is a
+ * fix with SCM_CREDENTIALS. tcp-zebra has no such fix.)
+ */
+ if (suna->sun_path[0] == '@')
+ suna->sun_path[0] = '\0';
+#endif
+ }
+ return true;
+}
+
static struct frr_daemon_info *di = NULL;
void frr_preinit(struct frr_daemon_info *daemon, int argc, char **argv)
@@ -156,6 +252,8 @@ void frr_preinit(struct frr_daemon_info *daemon, int argc, char **argv)
strlcpy(frr_protoname, di->logname, sizeof(frr_protoname));
strlcpy(frr_protonameinst, di->logname, sizeof(frr_protonameinst));
+
+ strlcpy(frr_zclientpath, ZEBRA_SERV_PATH, sizeof(frr_zclientpath));
}
void frr_opt_add(const char *optstr, const struct option *longopts,
@@ -233,7 +331,7 @@ static int frr_opt(int opt)
case 'z':
if (di->flags & FRR_NO_ZCLIENT)
return 1;
- zclient_serv_path_set(optarg);
+ strlcpy(frr_zclientpath, optarg, sizeof(frr_zclientpath));
break;
case 'A':
if (di->flags & FRR_NO_TCPVTY)
@@ -341,6 +439,13 @@ struct thread_master *frr_init(void)
zlog_set_level(ZLOG_DEST_SYSLOG, zlog_default->default_lvl);
#endif
+ if (!frr_zclient_addr(&zclient_addr, &zclient_addr_len,
+ frr_zclientpath)) {
+ fprintf(stderr, "Invalid zserv socket path: %s\n",
+ frr_zclientpath);
+ exit(1);
+ }
+
frrmod_init(di->module);
while (modules) {
modules = (oc = modules)->next;
diff --git a/lib/libfrr.h b/lib/libfrr.h
index 0f6ed0cb00..23516150ee 100644
--- a/lib/libfrr.h
+++ b/lib/libfrr.h
@@ -100,7 +100,11 @@ extern void frr_vty_serv(void);
/* note: contains call to frr_vty_serv() */
extern void frr_run(struct thread_master *master);
+extern bool frr_zclient_addr(struct sockaddr_storage *sa, socklen_t *sa_len,
+ const char *path);
+
extern char config_default[256];
+extern char frr_zclientpath[256];
extern const char frr_sysconfdir[];
extern const char frr_vtydir[];
extern const char frr_moduledir[];
diff --git a/lib/zclient.c b/lib/zclient.c
index a6252af403..233d0359d5 100644
--- a/lib/zclient.c
+++ b/lib/zclient.c
@@ -45,7 +45,8 @@ enum event { ZCLIENT_SCHEDULE, ZCLIENT_READ, ZCLIENT_CONNECT };
/* Prototype for event manager. */
static void zclient_event(enum event, struct zclient *);
-const char *zclient_serv_path = NULL;
+struct sockaddr_storage zclient_addr;
+socklen_t zclient_addr_len;
/* This file local debug flag. */
int zclient_debug = 0;
@@ -183,31 +184,28 @@ void zclient_reset(struct zclient *zclient)
zclient_init(zclient, zclient->redist_default, zclient->instance);
}
-#ifdef HAVE_TCP_ZEBRA
-
-/* Make socket to zebra daemon. Return zebra socket. */
-static int zclient_socket(void)
+/**
+ * Connect to zebra daemon.
+ * @param zclient a pointer to zclient structure
+ * @return socket fd just to make sure that connection established
+ * @see zclient_init
+ * @see zclient_new
+ */
+int zclient_socket_connect(struct zclient *zclient)
{
int sock;
int ret;
- struct sockaddr_in serv;
/* We should think about IPv6 connection. */
- sock = socket(AF_INET, SOCK_STREAM, 0);
+ sock = socket(zclient_addr.ss_family, SOCK_STREAM, 0);
if (sock < 0)
return -1;
- /* Make server socket. */
- memset(&serv, 0, sizeof(struct sockaddr_in));
- serv.sin_family = AF_INET;
- serv.sin_port = htons(ZEBRA_PORT);
-#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN
- serv.sin_len = sizeof(struct sockaddr_in);
-#endif /* HAVE_STRUCT_SOCKADDR_IN_SIN_LEN */
- serv.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+ set_cloexec(sock);
/* Connect to zebra. */
- ret = connect(sock, (struct sockaddr *)&serv, sizeof(serv));
+ ret = connect(sock, (struct sockaddr *)&zclient_addr,
+ zclient_addr_len);
if (ret < 0) {
if (zclient_debug)
zlog_warn("%s connect failure: %d(%s)",
@@ -216,65 +214,11 @@ static int zclient_socket(void)
close(sock);
return -1;
}
- return sock;
-}
-#else
-
-/* For sockaddr_un. */
-#include <sys/un.h>
-
-static int zclient_socket_un(const char *path)
-{
- int ret;
- int sock, len;
- struct sockaddr_un addr;
-
- sock = socket(AF_UNIX, SOCK_STREAM, 0);
- if (sock < 0)
- return -1;
-
- /* Make server socket. */
- memset(&addr, 0, sizeof(struct sockaddr_un));
- addr.sun_family = AF_UNIX;
- strncpy(addr.sun_path, path, strlen(path));
-#ifdef HAVE_STRUCT_SOCKADDR_UN_SUN_LEN
- len = addr.sun_len = SUN_LEN(&addr);
-#else
- len = sizeof(addr.sun_family) + strlen(addr.sun_path);
-#endif /* HAVE_STRUCT_SOCKADDR_UN_SUN_LEN */
-
- ret = connect(sock, (struct sockaddr *)&addr, len);
- if (ret < 0) {
- if (zclient_debug)
- zlog_warn("%s connect failure: %d(%s)",
- __PRETTY_FUNCTION__, errno,
- safe_strerror(errno));
- close(sock);
- return -1;
- }
+ zclient->sock = sock;
return sock;
}
-#endif /* HAVE_TCP_ZEBRA */
-
-/**
- * Connect to zebra daemon.
- * @param zclient a pointer to zclient structure
- * @return socket fd just to make sure that connection established
- * @see zclient_init
- * @see zclient_new
- */
-int zclient_socket_connect(struct zclient *zclient)
-{
-#ifdef HAVE_TCP_ZEBRA
- zclient->sock = zclient_socket();
-#else
- zclient->sock = zclient_socket_un(zclient_serv_path_get());
-#endif
- return zclient->sock;
-}
-
static int zclient_failed(struct zclient *zclient)
{
zclient->fail++;
@@ -2157,34 +2101,6 @@ static void zclient_event(enum event event, struct zclient *zclient)
}
}
-const char *zclient_serv_path_get()
-{
- return zclient_serv_path ? zclient_serv_path : ZEBRA_SERV_PATH;
-}
-
-void zclient_serv_path_set(char *path)
-{
- struct stat sb;
-
- /* reset */
- zclient_serv_path = NULL;
-
- /* test if `path' is socket. don't set it otherwise. */
- if (stat(path, &sb) == -1) {
- zlog_warn("%s: zebra socket `%s' does not exist", __func__,
- path);
- return;
- }
-
- if ((sb.st_mode & S_IFMT) != S_IFSOCK) {
- zlog_warn("%s: `%s' is not unix socket, sir", __func__, path);
- return;
- }
-
- /* it seems that path is unix socket */
- zclient_serv_path = path;
-}
-
void zclient_interface_set_master(struct zclient *client,
struct interface *master,
struct interface *slave)
diff --git a/lib/zclient.h b/lib/zclient.h
index 435d26e2f9..2752acc281 100644
--- a/lib/zclient.h
+++ b/lib/zclient.h
@@ -36,6 +36,14 @@
/* Zebra header size. */
#define ZEBRA_HEADER_SIZE 8
+/* special socket path name to use TCP
+ * @ is used as first character because that's abstract socket names on Linux
+ */
+#define ZAPI_TCP_PATHNAME "@tcp"
+
+extern struct sockaddr_storage zclient_addr;
+extern socklen_t zclient_addr_len;
+
/* Zebra message types. */
typedef enum {
ZEBRA_INTERFACE_ADD,
@@ -275,8 +283,6 @@ extern void zclient_reset(struct zclient *);
extern void zclient_free(struct zclient *);
extern int zclient_socket_connect(struct zclient *);
-extern void zclient_serv_path_set(char *path);
-extern const char *zclient_serv_path_get(void);
extern u_short *redist_check_instance(struct redist_proto *, u_short);
extern void redist_add_instance(struct redist_proto *, u_short);