From 4626cbaf78767fc8e9c86dd04785386c59ae0839 Mon Sep 17 00:00:00 2001 From: Damien Miller Date: Fri, 8 Jan 2016 14:24:56 +1100 Subject: Support Illumos/Solaris fine-grained privileges Includes a pre-auth privsep sandbox and several pledge() emulations. bz#2511, patch by Alex Wilson. ok dtucker@ --- openbsd-compat/port-solaris.c | 114 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 114 insertions(+) (limited to 'openbsd-compat/port-solaris.c') diff --git a/openbsd-compat/port-solaris.c b/openbsd-compat/port-solaris.c index 25382f1c9..962cd1685 100644 --- a/openbsd-compat/port-solaris.c +++ b/openbsd-compat/port-solaris.c @@ -227,3 +227,117 @@ solaris_set_default_project(struct passwd *pw) } } #endif /* USE_SOLARIS_PROJECTS */ + +#ifdef USE_SOLARIS_PRIVS +# ifdef HAVE_PRIV_H +# include +# endif + +void +solaris_drop_privs_pinfo_net_fork_exec(void) +{ + priv_set_t *pset = NULL, *npset = NULL; + + /* + * Note: this variant avoids dropping DAC filesystem rights, in case + * the process calling it is running as root and should have the + * ability to read/write/chown any file on the system. + * + * We start with the basic set, then *add* the DAC rights to it while + * taking away other parts of BASIC we don't need. Then we intersect + * this with our existing PERMITTED set. In this way we keep any + * DAC rights we had before, while otherwise reducing ourselves to + * the minimum set of privileges we need to proceed. + * + * This also means we drop any other parts of "root" that we don't + * need (e.g. the ability to kill any process, create new device nodes + * etc etc). + */ + + if ((pset = priv_allocset()) == NULL || + (npset = priv_allocset()) == NULL) + fatal("priv_allocset: %s", strerror(errno)); + + priv_basicset(npset); + + if (priv_addset(npset, PRIV_FILE_CHOWN) != 0 || + priv_addset(npset, PRIV_FILE_DAC_READ) != 0 || + priv_addset(npset, PRIV_FILE_DAC_SEARCH) != 0 || + priv_addset(npset, PRIV_FILE_DAC_WRITE) != 0 || + priv_addset(npset, PRIV_FILE_OWNER) != 0) + fatal("priv_addset: %s", strerror(errno)); + + if (priv_delset(npset, PRIV_FILE_LINK_ANY) != 0 || + priv_delset(npset, PRIV_NET_ACCESS) != 0 || + priv_delset(npset, PRIV_PROC_EXEC) != 0 || + priv_delset(npset, PRIV_PROC_FORK) != 0 || + priv_delset(npset, PRIV_PROC_INFO) != 0 || + priv_delset(npset, PRIV_PROC_SESSION) != 0) + fatal("priv_delset: %s", strerror(errno)); + + if (getppriv(PRIV_PERMITTED, pset) != 0) + fatal("getppriv: %s", strerror(errno)); + + priv_intersect(pset, npset); + + if (setppriv(PRIV_SET, PRIV_PERMITTED, npset) != 0 || + setppriv(PRIV_SET, PRIV_LIMIT, npset) != 0 || + setppriv(PRIV_SET, PRIV_INHERITABLE, npset) != 0) + fatal("setppriv: %s", strerror(errno)); + + priv_freeset(pset); + priv_freeset(npset); +} + +void +solaris_drop_privs_root_pinfo_net(void) +{ + priv_set_t *pset = NULL; + + if ((pset = priv_allocset()) == NULL) + fatal("priv_allocset: %s", strerror(errno)); + + /* Start with "basic" and drop everything we don't need. */ + priv_basicset(pset); + + if (priv_delset(pset, PRIV_FILE_LINK_ANY) != 0 || + priv_delset(pset, PRIV_NET_ACCESS) != 0 || + priv_delset(pset, PRIV_PROC_INFO) != 0 || + priv_delset(pset, PRIV_PROC_SESSION) != 0) + fatal("priv_delset: %s", strerror(errno)); + + if (setppriv(PRIV_SET, PRIV_PERMITTED, pset) != 0 || + setppriv(PRIV_SET, PRIV_LIMIT, pset) != 0 || + setppriv(PRIV_SET, PRIV_INHERITABLE, pset) != 0) + fatal("setppriv: %s", strerror(errno)); + + priv_freeset(pset); +} + +void +solaris_drop_privs_root_pinfo_net_exec(void) +{ + priv_set_t *pset = NULL; + + if ((pset = priv_allocset()) == NULL) + fatal("priv_allocset: %s", strerror(errno)); + + /* Start with "basic" and drop everything we don't need. */ + priv_basicset(pset); + + if (priv_delset(pset, PRIV_FILE_LINK_ANY) != 0 || + priv_delset(pset, PRIV_NET_ACCESS) != 0 || + priv_delset(pset, PRIV_PROC_EXEC) != 0 || + priv_delset(pset, PRIV_PROC_INFO) != 0 || + priv_delset(pset, PRIV_PROC_SESSION) != 0) + fatal("priv_delset: %s", strerror(errno)); + + if (setppriv(PRIV_SET, PRIV_PERMITTED, pset) != 0 || + setppriv(PRIV_SET, PRIV_LIMIT, pset) != 0 || + setppriv(PRIV_SET, PRIV_INHERITABLE, pset) != 0) + fatal("setppriv: %s", strerror(errno)); + + priv_freeset(pset); +} + +#endif -- cgit v1.2.3 From 907091acb188b1057d50c2158f74c3ecf1c2302b Mon Sep 17 00:00:00 2001 From: Darren Tucker Date: Fri, 19 Feb 2016 09:05:39 +1100 Subject: Make Solaris privs code build on older systems. Not all systems with Solaris privs have priv_basicset so factor that out and provide backward compatibility code. Similarly, not all have PRIV_NET_ACCESS so wrap that in #ifdef. Based on code from alex at cooperi.net and djm@ with help from carson at taltos.org and wieland at purdue.edu. --- configure.ac | 12 ++++++------ openbsd-compat/port-solaris.c | 44 ++++++++++++++++++++++++++++++++----------- openbsd-compat/port-solaris.h | 3 +++ sandbox-solaris.c | 9 +++++---- 4 files changed, 47 insertions(+), 21 deletions(-) (limited to 'openbsd-compat/port-solaris.c') diff --git a/configure.ac b/configure.ac index b4c0aaab7..e36b04942 100644 --- a/configure.ac +++ b/configure.ac @@ -896,11 +896,9 @@ mips-sony-bsd|mips-sony-newsos4) else AC_MSG_RESULT([no]) fi - AC_CHECK_FUNC([setppriv], - [ AC_CHECK_HEADERS([priv.h], [ - SOLARIS_PRIVS="yes" - ]) - ]) + AC_CHECK_FUNCS([setppriv]) + AC_CHECK_FUNCS([priv_basicset]) + AC_CHECK_HEADERS([priv.h]) AC_ARG_WITH([solaris-contracts], [ --with-solaris-contracts Enable Solaris process contracts (experimental)], [ @@ -925,7 +923,9 @@ mips-sony-bsd|mips-sony-newsos4) [ --with-solaris-privs Enable Solaris/Illumos privileges (experimental)], [ AC_MSG_CHECKING([for Solaris/Illumos privilege support]) - if test "x$SOLARIS_PRIVS" = "xyes" ; then + if test "x$ac_cv_func_setppriv" = "xyes" -a \ + "x$ac_cv_header_priv_h" = "xyes" ; then + SOLARIS_PRIVS=yes AC_MSG_RESULT([found]) AC_DEFINE([NO_UID_RESTORATION_TEST], [1], [Define to disable UID restoration test]) diff --git a/openbsd-compat/port-solaris.c b/openbsd-compat/port-solaris.c index 962cd1685..e36e412d7 100644 --- a/openbsd-compat/port-solaris.c +++ b/openbsd-compat/port-solaris.c @@ -233,6 +233,26 @@ solaris_set_default_project(struct passwd *pw) # include # endif +priv_set_t * +solaris_basic_privset(void) +{ + priv_set_t *pset; + +#ifdef HAVE_PRIV_BASICSET + if ((pset = priv_allocset()) == NULL) { + error("priv_allocset: %s", strerror(errno)); + return NULL; + } + priv_basicset(pset); +#else + if ((pset = priv_str_to_set("basic", ",", NULL)) == NULL) { + error("priv_str_to_set: %s", strerror(errno)); + return NULL; + } +#endif + return pset; +} + void solaris_drop_privs_pinfo_net_fork_exec(void) { @@ -254,11 +274,10 @@ solaris_drop_privs_pinfo_net_fork_exec(void) * etc etc). */ - if ((pset = priv_allocset()) == NULL || - (npset = priv_allocset()) == NULL) + if ((pset = priv_allocset()) == NULL) fatal("priv_allocset: %s", strerror(errno)); - - priv_basicset(npset); + if ((npset = solaris_basic_privset()) == NULL) + fatal("solaris_basic_privset: %s", strerror(errno)); if (priv_addset(npset, PRIV_FILE_CHOWN) != 0 || priv_addset(npset, PRIV_FILE_DAC_READ) != 0 || @@ -268,7 +287,9 @@ solaris_drop_privs_pinfo_net_fork_exec(void) fatal("priv_addset: %s", strerror(errno)); if (priv_delset(npset, PRIV_FILE_LINK_ANY) != 0 || +#ifdef PRIV_NET_ACCESS priv_delset(npset, PRIV_NET_ACCESS) != 0 || +#endif priv_delset(npset, PRIV_PROC_EXEC) != 0 || priv_delset(npset, PRIV_PROC_FORK) != 0 || priv_delset(npset, PRIV_PROC_INFO) != 0 || @@ -294,14 +315,14 @@ solaris_drop_privs_root_pinfo_net(void) { priv_set_t *pset = NULL; - if ((pset = priv_allocset()) == NULL) - fatal("priv_allocset: %s", strerror(errno)); - /* Start with "basic" and drop everything we don't need. */ - priv_basicset(pset); + if ((pset = solaris_basic_privset()) == NULL) + fatal("solaris_basic_privset: %s", strerror(errno)); if (priv_delset(pset, PRIV_FILE_LINK_ANY) != 0 || +#ifdef PRIV_NET_ACCESS priv_delset(pset, PRIV_NET_ACCESS) != 0 || +#endif priv_delset(pset, PRIV_PROC_INFO) != 0 || priv_delset(pset, PRIV_PROC_SESSION) != 0) fatal("priv_delset: %s", strerror(errno)); @@ -319,14 +340,15 @@ solaris_drop_privs_root_pinfo_net_exec(void) { priv_set_t *pset = NULL; - if ((pset = priv_allocset()) == NULL) - fatal("priv_allocset: %s", strerror(errno)); /* Start with "basic" and drop everything we don't need. */ - priv_basicset(pset); + if ((pset = solaris_basic_privset()) == NULL) + fatal("solaris_basic_privset: %s", strerror(errno)); if (priv_delset(pset, PRIV_FILE_LINK_ANY) != 0 || +#ifdef PRIV_NET_ACCESS priv_delset(pset, PRIV_NET_ACCESS) != 0 || +#endif priv_delset(pset, PRIV_PROC_EXEC) != 0 || priv_delset(pset, PRIV_PROC_INFO) != 0 || priv_delset(pset, PRIV_PROC_SESSION) != 0) diff --git a/openbsd-compat/port-solaris.h b/openbsd-compat/port-solaris.h index b077e186a..3a41ea8cd 100644 --- a/openbsd-compat/port-solaris.h +++ b/openbsd-compat/port-solaris.h @@ -26,8 +26,11 @@ void solaris_contract_pre_fork(void); void solaris_contract_post_fork_child(void); void solaris_contract_post_fork_parent(pid_t pid); void solaris_set_default_project(struct passwd *); +# ifdef USE_SOLARIS_PRIVS +priv_set_t *solaris_basic_privset(void); void solaris_drop_privs_pinfo_net_fork_exec(void); void solaris_drop_privs_root_pinfo_net(void); void solaris_drop_privs_root_pinfo_net_exec(void); +# endif /* USE_SOLARIS_PRIVS */ #endif diff --git a/sandbox-solaris.c b/sandbox-solaris.c index 98714e170..343a01022 100644 --- a/sandbox-solaris.c +++ b/sandbox-solaris.c @@ -48,19 +48,20 @@ ssh_sandbox_init(struct monitor *monitor) struct ssh_sandbox *box = NULL; box = xcalloc(1, sizeof(*box)); - box->pset = priv_allocset(); + + /* Start with "basic" and drop everything we don't need. */ + box->pset = solaris_basic_privset(); if (box->pset == NULL) { free(box); return NULL; } - /* Start with "basic" and drop everything we don't need. */ - priv_basicset(box->pset); - /* Drop everything except the ability to use already-opened files */ if (priv_delset(box->pset, PRIV_FILE_LINK_ANY) != 0 || +#ifdef PRIV_NET_ACCESS priv_delset(box->pset, PRIV_NET_ACCESS) != 0 || +#endif priv_delset(box->pset, PRIV_PROC_EXEC) != 0 || priv_delset(box->pset, PRIV_PROC_FORK) != 0 || priv_delset(box->pset, PRIV_PROC_INFO) != 0 || -- cgit v1.2.3