diff options
Diffstat (limited to 'lib')
-rw-r--r-- | lib/Makefile.am | 8 | ||||
-rw-r--r-- | lib/memory.h | 2 | ||||
-rw-r--r-- | lib/privs.c | 377 | ||||
-rw-r--r-- | lib/privs.h | 91 | ||||
-rw-r--r-- | lib/vty.c | 58 | ||||
-rw-r--r-- | lib/zebra.h | 6 |
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 */ @@ -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 |