summaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/Makefile.am8
-rw-r--r--lib/memory.h2
-rw-r--r--lib/privs.c377
-rw-r--r--lib/privs.h91
-rw-r--r--lib/vty.c58
-rw-r--r--lib/zebra.h6
6 files changed, 528 insertions, 14 deletions
diff --git a/lib/Makefile.am b/lib/Makefile.am
index 81f1e41e..dbd105aa 100644
--- a/lib/Makefile.am
+++ b/lib/Makefile.am
@@ -3,16 +3,16 @@
INCLUDES = @INCLUDES@ -I.. -I$(top_srcdir) -I$(top_srcdir)/lib
DEFS = @DEFS@ -DSYSCONFDIR=\"$(sysconfdir)/\"
-noinst_LIBRARIES = libzebra.a
+lib_LIBRARIES = libzebra.a
libzebra_a_SOURCES = \
version.c network.c pid_output.c getopt.c getopt1.c daemon.c \
print_version.c checksum.c vector.c linklist.c vty.c command.c \
sockunion.c prefix.c thread.c if.c memory.c buffer.c table.c hash.c \
filter.c routemap.c distribute.c stream.c str.c log.c plist.c \
- zclient.c sockopt.c smux.c md5.c if_rmap.c keychain.c
+ zclient.c sockopt.c smux.c md5.c keychain.c privs.c
-libzebra_a_DEPENDENCIES = @LIB_REGEX@
+libzebra_a_DEPENDENCIES = @LIB_REGEX@ @LIBCAP@
libzebra_a_LIBADD = @LIB_REGEX@
@@ -20,7 +20,7 @@ noinst_HEADERS = \
buffer.h command.h filter.h getopt.h hash.h if.h linklist.h log.h \
memory.h network.h prefix.h routemap.h distribute.h sockunion.h \
str.h stream.h table.h thread.h vector.h version.h vty.h zebra.h \
- plist.h zclient.h sockopt.h smux.h md5-gnu.h if_rmap.h keychain.h
+ plist.h zclient.h sockopt.h smux.h md5-gnu.h keychain.h privs.h
EXTRA_DIST = regex.c regex-gnu.h
diff --git a/lib/memory.h b/lib/memory.h
index 52e3bc11..06681bd1 100644
--- a/lib/memory.h
+++ b/lib/memory.h
@@ -184,6 +184,8 @@ enum
MTYPE_VRF,
MTYPE_VRF_NAME,
+
+ MTYPE_PRIVS,
MTYPE_MAX
};
diff --git a/lib/privs.c b/lib/privs.c
new file mode 100644
index 00000000..8932eacc
--- /dev/null
+++ b/lib/privs.c
@@ -0,0 +1,377 @@
+/*
+ * Zebra privileges.
+ *
+ * Copyright (C) 2003 Paul Jakma.
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * GNU Zebra is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GNU Zebra; see the file COPYING. If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#include <zebra.h>
+#include "log.h"
+#include "privs.h"
+#include "memory.h"
+
+
+/* internal privileges state */
+static struct _zprivs_t
+{
+#ifdef HAVE_LCAPS
+ cap_t caps; /* caps storage */
+ cap_value_t *syscaps_p; /* system permitted caps */
+ cap_value_t *syscaps_i; /* system inheritable caps */
+ int sys_num_p; /* number of syscaps_p */
+ int sys_num_i; /* number of syscaps_i */
+#endif /* HAVE_LCAPS */
+ uid_t zuid, /* uid to run as */
+ zsuid; /* saved uid */
+ gid_t zgid; /* gid to run as */
+ gid_t vtygrp; /* gid for vty sockets */
+} zprivs_state;
+
+/* externally exported but not directly accessed functions */
+#ifdef HAVE_LCAPS
+int zprivs_change_caps (zebra_privs_ops_t);
+zebra_privs_current_t zprivs_state_caps (void);
+#endif /* HAVE_LCAPS */
+int zprivs_change_uid (zebra_privs_ops_t);
+zebra_privs_current_t zprivs_state_uid (void);
+int zprivs_change_null (zebra_privs_ops_t);
+zebra_privs_current_t zprivs_state_null (void);
+void zprivs_terminate (void);
+
+#ifdef HAVE_LCAPS
+static int
+cap_map [ZCAP_MAX] =
+{
+ [ZCAP_SETGID] = CAP_SETGID,
+ [ZCAP_SETUID] = CAP_SETUID,
+ [ZCAP_BIND] = CAP_NET_BIND_SERVICE,
+ [ZCAP_BROADCAST] = CAP_NET_BROADCAST,
+ [ZCAP_ADMIN] = CAP_NET_ADMIN,
+ [ZCAP_RAW] = CAP_NET_RAW,
+ [ZCAP_CHROOT] = CAP_SYS_CHROOT,
+ [ZCAP_NICE] = CAP_SYS_NICE,
+ [ZCAP_PTRACE] = CAP_SYS_PTRACE,
+ [ZCAP_DAC_OVERRIDE] = CAP_DAC_OVERRIDE,
+ [ZCAP_READ_SEARCH] = CAP_DAC_READ_SEARCH,
+ [ZCAP_SYS_ADMIN] = CAP_SYS_ADMIN,
+ [ZCAP_FOWNER] = ZCAP_FOWNER
+};
+
+/* convert zebras privileges to system capabilities */
+static cap_value_t *
+zcaps2sys (zebra_capabilities_t *zcaps, int num)
+{
+ cap_value_t *syscaps;
+ int i;
+
+ if (!num)
+ return NULL;
+
+ syscaps = (cap_value_t *) XCALLOC ( MTYPE_PRIVS,
+ (sizeof(cap_value_t) * num) );
+ if (!syscaps)
+ {
+ zlog_err ("zcap2sys: could not XCALLOC!");
+ return NULL;
+ }
+
+ for (i=0; i < num; i++)
+ {
+ syscaps[i] = cap_map[zcaps[i]];
+ }
+
+ return syscaps;
+}
+
+/* set or clear the effective capabilities to/from permitted */
+int
+zprivs_change_caps (zebra_privs_ops_t op)
+{
+ cap_flag_value_t cflag;
+
+ if (op == ZPRIVS_RAISE)
+ cflag = CAP_SET;
+ else if (op == ZPRIVS_LOWER)
+ cflag = CAP_CLEAR;
+ else
+ return -1;
+
+ if ( !cap_set_flag (zprivs_state.caps, CAP_EFFECTIVE,
+ zprivs_state.sys_num_p, zprivs_state.syscaps_p, cflag))
+ return cap_set_proc (zprivs_state.caps);
+ return -1;
+}
+
+zebra_privs_current_t
+zprivs_state_caps (void)
+{
+ int i;
+ cap_flag_value_t val;
+
+ for (i=0; i < zprivs_state.sys_num_p; i++)
+ {
+ if ( cap_get_flag (zprivs_state.caps, zprivs_state.syscaps_p[i],
+ CAP_EFFECTIVE, &val) )
+ zlog_warn ("zprivs_state_caps: could not cap_get_flag, %s",
+ strerror (errno) );
+ if (val == CAP_SET)
+ return ZPRIVS_RAISED;
+ }
+ return ZPRIVS_LOWERED;
+}
+
+#endif /* HAVE_LCAPS */
+
+int
+zprivs_change_uid (zebra_privs_ops_t op)
+{
+
+ if (op == ZPRIVS_RAISE)
+ return seteuid (zprivs_state.zsuid);
+ else if (op == ZPRIVS_LOWER)
+ return seteuid (zprivs_state.zuid);
+ else
+ return -1;
+}
+
+zebra_privs_current_t
+zprivs_state_uid (void)
+{
+ return ( (zprivs_state.zuid == geteuid()) ? ZPRIVS_LOWERED : ZPRIVS_RAISED);
+}
+
+int
+zprivs_change_null (zebra_privs_ops_t op)
+{
+ return 0;
+}
+
+zebra_privs_current_t
+zprivs_state_null (void)
+{
+ return ZPRIVS_RAISED;
+}
+
+
+void
+zprivs_init(struct zebra_privs_t *zprivs)
+{
+ struct passwd *pwentry = NULL;
+ struct group *grentry = NULL;
+
+ /* NULL privs */
+ if (! (zprivs->user || zprivs->group
+ || zprivs->cap_num_p || zprivs->cap_num_i) )
+ {
+ zprivs->change = zprivs_change_null;
+ zprivs->current_state = zprivs_state_null;
+ return;
+ }
+
+ if (zprivs->user)
+ {
+ if ( (pwentry = getpwnam (zprivs->user)) )
+ {
+ zprivs_state.zuid = pwentry->pw_uid;
+ }
+ else
+ {
+ zlog_err ("privs_init: could not lookup supplied user");
+ exit (1);
+ }
+ }
+
+
+#ifdef VTY_GROUP
+ /* Add the VTY_GROUP to the supplementary groups so it can be chowned to */
+ if ( (grentry = getgrnam (zprivs->vty_group)) )
+ {
+ if ( setgroups (1, &grentry->gr_gid) )
+ {
+ zlog_err ("privs_init: could not setgroups, %s",
+ strerror (errno) );
+ exit (1);
+ }
+ zprivs_state.vtygrp = grentry->gr_gid;
+ }
+ else
+ {
+ zlog_err ("privs_init: could not lookup supplied user");
+ exit (1);
+ }
+#endif /* VTY_GROUP */
+
+ if (zprivs->group)
+ {
+ if ( (grentry = getgrnam (zprivs->user)) )
+ {
+ zprivs_state.zgid = grentry->gr_gid;
+ }
+ else
+ {
+ zlog_err ("privs_init: could not lookup supplied user");
+ exit (1);
+ }
+
+ /* change group now, forever. uid we do later */
+ if ( setregid (zprivs_state.zgid, zprivs_state.zgid) )
+ {
+ zlog_err ("privs_init: could not setregid");
+ exit (1);
+ }
+ }
+
+#ifdef HAVE_LCAPS
+ zprivs_state.syscaps_p = zcaps2sys (zprivs->caps_p, zprivs->cap_num_p);
+ zprivs_state.sys_num_p = zprivs->cap_num_p;
+ zprivs_state.syscaps_i = zcaps2sys (zprivs->caps_i, zprivs->cap_num_i);
+ zprivs_state.sys_num_i = zprivs->cap_num_i;
+
+ /* Tell kernel we want caps maintained across uid changes */
+ if ( prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0) == -1 )
+ {
+ zlog_err("privs_init: could not set PR_SET_KEEPCAPS, %s",
+ strerror (errno) );
+ exit(1);
+ }
+
+ if ( !zprivs_state.syscaps_p )
+ {
+ zlog_warn ("privs_init: capabilities enabled, but no capabilities supplied");
+ }
+
+ if ( !(zprivs_state.caps = cap_init()) )
+ {
+ zlog_err ("privs_init: failed to cap_init, %s", strerror (errno) );
+ exit (1);
+ }
+
+ /* we have caps, we have no need to ever change back the original user */
+ if (zprivs_state.zuid)
+ {
+ if ( setreuid (zprivs_state.zuid, zprivs_state.zuid) )
+ {
+ zlog_err ("privs_init (cap): could not setreuid, %s", strerror (errno) );
+ exit (1);
+ }
+ }
+
+ if ( cap_clear (zprivs_state.caps) )
+ {
+ zlog_err ("privs_init: failed to cap_clear, %s", strerror (errno));
+ exit (1);
+ }
+
+ /* set permitted caps */
+ cap_set_flag(zprivs_state.caps, CAP_PERMITTED,
+ zprivs_state.sys_num_p, zprivs_state.syscaps_p, CAP_SET);
+ cap_set_flag(zprivs_state.caps, CAP_EFFECTIVE,
+ zprivs_state.sys_num_p, zprivs_state.syscaps_p, CAP_SET);
+
+ /* set inheritable caps, if any */
+ if (zprivs_state.sys_num_i)
+ {
+ cap_set_flag(zprivs_state.caps, CAP_INHERITABLE,
+ zprivs_state.sys_num_i, zprivs_state.syscaps_i, CAP_SET);
+ }
+
+ /* apply caps. CAP_EFFECTIVE is cleared. we'll raise the caps as
+ * and when, and only when, they are needed.
+ */
+ if ( cap_set_proc (zprivs_state.caps) )
+ {
+ zlog_err ("privs_init: initial cap_set_proc failed");
+ exit (1);
+ }
+
+ /* set methods for the caller to use */
+ zprivs->change = zprivs_change_caps;
+ zprivs->current_state = zprivs_state_caps;
+
+#elif !defined(HAVE_LCAPS)
+ /* we dont have caps. we'll need to maintain rid and saved uid
+ * and change euid back to saved uid (who we presume has all neccessary
+ * privileges) whenever we are asked to raise our privileges.
+ */
+ zprivs_state.zsuid = geteuid();
+ if ( zprivs_state.zuid )
+ {
+ if ( setreuid (-1, zprivs_state.zuid) )
+ {
+ zlog_err ("privs_init (uid): could not setreuid, %s", strerror (errno));
+ exit (1);
+ }
+ }
+
+ zprivs->change = zprivs_change_uid;
+ zprivs->current_state = zprivs_state_uid;
+#endif /* HAVE_LCAPS */
+}
+
+void
+zprivs_terminate (void)
+{
+
+#ifdef HAVE_LCAPS
+
+ if (zprivs_state.caps)
+ cap_clear (zprivs_state.caps);
+
+ if ( cap_set_proc (zprivs_state.caps) )
+ {
+ zlog_err ("privs_terminate: cap_set_proc failed, %s",
+ strerror (errno) );
+ exit (1);
+ }
+
+ if (zprivs_state.sys_num_p)
+ XFREE (MTYPE_PRIVS, zprivs_state.syscaps_p);
+
+ if (zprivs_state.sys_num_i)
+ XFREE (MTYPE_PRIVS, zprivs_state.syscaps_i);
+
+ cap_free (zprivs_state.caps);
+#else
+ if (zprivs_state.zuid)
+ {
+ if ( setreuid (zprivs_state.zuid, zprivs_state.zuid) )
+ {
+ zlog_err ("privs_terminate: could not setreuid, %s",
+ strerror (errno) );
+ exit (1);
+ }
+ }
+#endif /* HAVE_LCAPS */
+ return;
+}
+
+void
+zprivs_get_ids(struct zprivs_ids_t *ids)
+{
+
+ ids->uid_priv = getuid();
+ (zprivs_state.zuid) ? (ids->uid_normal = zprivs_state.zuid)
+ : (ids->uid_normal = -1);
+ (zprivs_state.zgid) ? (ids->gid_normal = zprivs_state.zgid)
+ : (ids->gid_normal = -1);
+ (zprivs_state.vtygrp) ? (ids->gid_vty = zprivs_state.vtygrp)
+ : (ids->gid_vty = -1);
+
+ return;
+}
diff --git a/lib/privs.h b/lib/privs.h
new file mode 100644
index 00000000..65c9f358
--- /dev/null
+++ b/lib/privs.h
@@ -0,0 +1,91 @@
+/*
+ * Zebra privileges header.
+ *
+ * Copyright (C) 2003 Paul Jakma.
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * GNU Zebra is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GNU Zebra; see the file COPYING. If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#ifndef _ZEBRA_PRIVS_H
+#define _ZEBRA_PRIVS_H
+
+/* list of zebra capabilities */
+typedef enum
+{
+ ZCAP_SETGID,
+ ZCAP_SETUID,
+ ZCAP_BIND,
+ ZCAP_BROADCAST,
+ ZCAP_ADMIN,
+ ZCAP_SYS_ADMIN,
+ ZCAP_RAW,
+ ZCAP_CHROOT,
+ ZCAP_NICE,
+ ZCAP_PTRACE,
+ ZCAP_DAC_OVERRIDE,
+ ZCAP_READ_SEARCH,
+ ZCAP_FOWNER,
+ ZCAP_MAX
+} zebra_capabilities_t;
+
+typedef enum
+{
+ ZPRIVS_LOWERED,
+ ZPRIVS_RAISED
+} zebra_privs_current_t;
+
+typedef enum
+{
+ ZPRIVS_RAISE,
+ ZPRIVS_LOWER,
+} zebra_privs_ops_t;
+
+struct zebra_privs_t
+{
+ zebra_capabilities_t *caps_p; /* caps required for operation */
+ zebra_capabilities_t *caps_i; /* caps to allow inheritance of */
+ int cap_num_p; /* number of caps in arrays */
+ int cap_num_i;
+ char *user; /* user and group to run as */
+ char *group;
+ char *vty_group; /* group to chown vty socket to */
+ /* methods */
+ int
+ (*change) (zebra_privs_ops_t); /* change privileges, 0 on success */
+ zebra_privs_current_t
+ (*current_state) (void); /* current privilege state */
+};
+
+struct zprivs_ids_t
+{
+ /* -1 is undefined */
+ uid_t uid_priv; /* privileged uid */
+ uid_t uid_normal; /* normal uid */
+ gid_t gid_priv; /* privileged uid */
+ gid_t gid_normal; /* normal uid */
+ gid_t gid_vty; /* vty gid */
+};
+
+ /* initialise zebra privileges */
+void zprivs_init (struct zebra_privs_t *zprivs);
+ /* drop all and terminate privileges */
+void zprivs_terminate (void);
+ /* query for runtime uid's and gid's, eg vty needs this */
+void zprivs_get_ids(struct zprivs_ids_t *);
+
+#endif /* _ZEBRA_PRIVS_H */
diff --git a/lib/vty.c b/lib/vty.c
index d31521cc..b583e443 100644
--- a/lib/vty.c
+++ b/lib/vty.c
@@ -33,6 +33,7 @@
#include "log.h"
#include "prefix.h"
#include "filter.h"
+#include "privs.h"
/* Vty events */
enum event
@@ -1776,14 +1777,37 @@ vty_serv_sock_addrinfo (const char *hostname, unsigned short port)
/* Make vty server socket. */
void
-vty_serv_sock_family (unsigned short port, int family)
+vty_serv_sock_family (const char* addr, unsigned short port, int family)
{
int ret;
union sockunion su;
int accept_sock;
+ void* naddr=NULL;
memset (&su, 0, sizeof (union sockunion));
su.sa.sa_family = family;
+ if(addr)
+ switch(family)
+ {
+ case AF_INET:
+ naddr=&su.sin.sin_addr;
+#ifdef HAVE_IPV6
+ case AF_INET6:
+ naddr=&su.sin6.sin6_addr;
+#endif
+ }
+
+ if(naddr)
+ switch(inet_pton(family,addr,naddr))
+ {
+ case -1:
+ zlog_err("bad address %s",addr);
+ naddr=NULL;
+ break;
+ case 0:
+ zlog_err("error translating address %s: %s",addr,strerror(errno));
+ naddr=NULL;
+ }
/* Make new socket. */
accept_sock = sockunion_stream_socket (&su);
@@ -1795,9 +1819,10 @@ vty_serv_sock_family (unsigned short port, int family)
sockopt_reuseport (accept_sock);
/* Bind socket to universal address and given port. */
- ret = sockunion_bind (accept_sock, &su, port, NULL);
+ ret = sockunion_bind (accept_sock, &su, port, naddr);
if (ret < 0)
{
+ zlog_warn("can't bind socket");
close (accept_sock); /* Avoid sd leak. */
return;
}
@@ -1827,12 +1852,13 @@ vty_serv_un (char *path)
int sock, len;
struct sockaddr_un serv;
mode_t old_mask;
-
+ struct zprivs_ids_t ids;
+
/* First of all, unlink existing socket */
unlink (path);
/* Set umask */
- old_mask = umask (0077);
+ old_mask = umask (0007);
/* Make UNIX domain socket. */
sock = socket (AF_UNIX, SOCK_STREAM, 0);
@@ -1870,6 +1896,18 @@ vty_serv_un (char *path)
umask (old_mask);
+ zprivs_get_ids(&ids);
+
+ if (ids.gid_vty > 0)
+ {
+ /* set group of socket */
+ if ( chown (path, -1, ids.gid_vty) )
+ {
+ zlog_err ("vty_serv_un: could chown socket, %s",
+ strerror (errno) );
+ }
+ }
+
vty_event (VTYSH_SERV, sock, NULL);
}
@@ -1966,7 +2004,7 @@ vtysh_read (struct thread *thread)
/* Determine address family to bind. */
void
-vty_serv_sock (const char *hostname, unsigned short port, char *path)
+vty_serv_sock (const char *addr, unsigned short port, char *path)
{
/* If port is set to 0, do not listen on TCP/IP at all! */
if (port)
@@ -1974,19 +2012,19 @@ vty_serv_sock (const char *hostname, unsigned short port, char *path)
#ifdef HAVE_IPV6
#ifdef NRL
- vty_serv_sock_family (port, AF_INET);
- vty_serv_sock_family (port, AF_INET6);
+ vty_serv_sock_family (addr, port, AF_INET);
+ vty_serv_sock_family (addr, port, AF_INET6);
#else /* ! NRL */
- vty_serv_sock_addrinfo (hostname, port);
+ vty_serv_sock_addrinfo (addr, port);
#endif /* NRL*/
#else /* ! HAVE_IPV6 */
- vty_serv_sock_family (port, AF_INET);
+ vty_serv_sock_family (addr,port, AF_INET);
#endif /* HAVE_IPV6 */
}
#ifdef VTYSH
vty_serv_un (path);
-#endif /* VTYSH */
+#endif /* VTYSH */v
}
/* Close vty interface. */
diff --git a/lib/zebra.h b/lib/zebra.h
index a34f5d4a..c9aaf70d 100644
--- a/lib/zebra.h
+++ b/lib/zebra.h
@@ -45,6 +45,8 @@ typedef int socklen_t;
#include <fcntl.h>
#include <signal.h>
#include <string.h>
+#include <pwd.h>
+#include <grp.h>
#ifdef HAVE_STROPTS_H
#include <stropts.h>
#endif /* HAVE_STROPTS_H */
@@ -73,6 +75,10 @@ typedef int socklen_t;
#ifdef HAVE_RUSAGE
#include <sys/resource.h>
#endif /* HAVE_RUSAGE */
+#ifdef HAVE_LCAPS
+#include <sys/capability.h>
+#include <sys/prctl.h>
+#endif /* HAVE_LCAPS */
/* machine dependent includes */
#ifdef SUNOS_5