From 32c1eb2ab37dbe7d3ad5e222e783ab2d2fe4271f Mon Sep 17 00:00:00 2001 From: Renzo Davoli Date: Thu, 4 Aug 2016 12:59:42 +0200 Subject: [PATCH] new feature: conditioned rules in cado.conf --- cado.c | 13 +++++++------ cado.conf.5 | 23 ++++++++++++++++------- configure.ac | 7 +++++++ read_conf.c | 52 ++++++++++++++++++++++++++++++++++++++++++---------- read_conf.h | 2 +- 5 files changed, 73 insertions(+), 24 deletions(-) diff --git a/cado.c b/cado.c index a0f7a47..6494bca 100644 --- a/cado.c +++ b/cado.c @@ -67,7 +67,7 @@ int main(int argc, char*argv[]) { char *progname=basename(argv[0]); char **user_groups=get_user_groups(); - uint64_t okcaps=get_authorized_caps(user_groups); + uint64_t okcaps; uint64_t reqcaps=0; uint64_t grantcap=0; int verbose=0; @@ -95,7 +95,7 @@ int main(int argc, char*argv[]) fprintf(stderr, "setcap requires root access\n"); exit(2); } - okcaps = get_authorized_caps(NULL); + okcaps = get_authorized_caps(NULL, -1LL); okcaps |= 1ULL << CAP_DAC_READ_SEARCH; if (verbose) { printf("Capability needed by %s:\n", progname); @@ -108,13 +108,12 @@ int main(int argc, char*argv[]) exit(0); } - if (verbose) { + if (verbose && (argc == optind)) { + okcaps=get_authorized_caps(user_groups, -1LL); printf("Allowed ambient capabilities:\n"); printcapset(okcaps, " "); - } - - if (verbose && (argc == optind)) exit(0); + } if (argc - optind < 2) usage(progname); @@ -127,6 +126,8 @@ int main(int argc, char*argv[]) printcapset(reqcaps, " "); } + okcaps=get_authorized_caps(user_groups, reqcaps); + if (reqcaps & ~okcaps) { if (verbose) { printf("Unavailable ambient capabilities:\n"); diff --git a/cado.conf.5 b/cado.conf.5 index 69a9381..efbc1c4 100644 --- a/cado.conf.5 +++ b/cado.conf.5 @@ -13,6 +13,10 @@ Non-comment lines have the following syntax .nf \fIlist_of_capabilities\fB:\fI list_of_users_and_groups\fR .fi +or +.nf + \fIlist_of_capabilities\fB:\fI list_of_users_and_groups\fB:\fR \fIlist_of_auth_commands\fR +.fi Both \fIlist_of_capabilities\fR and \fIlist_of_users_and_groups\fR are comma separated lists of identifiers. @@ -22,15 +26,20 @@ have the same meaning). Items of \fIlist_of_users_and_groups\fR are usernames or groupnames (groupnames must be prefexed by '@'). +\fIlist_of_auth_commands\fR is a command or a list of commands separated by semicolon (;). If present, cado runs +all the sequence of commands it grants the capabilities as defined in the current line only if all return zero as +their exit status. + Example of \fBcado.conf\fR file: -.ni - # Capability Ambient DO configuration file - # cado.conf - - net_admin: @netadmin,renzo - net_admin,net_bind_service,net_raw,net_broadcast: @vxvdex - cap_kill: renzo +.nf + # Capability Ambient DO configuration file + # cado.conf + + net_admin: @netadmin,renzo: /usr/bin/logger cado net_admin $USER; /bin/echo OK + net_admin: @privatenet: /usr/local/lib/cado_autorize_privatenet + net_admin,net_bind_service,net_raw,net_broadcast: @vxvdex + cap_kill: renzo .fi In this example the renzo's processes can be granted (by \fBcado\fR) cap_net_admin and cap_kill. diff --git a/configure.ac b/configure.ac index c54692e..c093ce4 100644 --- a/configure.ac +++ b/configure.ac @@ -13,6 +13,10 @@ AC_PROG_CC AC_PROG_INSTALL # Checks for libraries. +AC_CHECK_LIB([s2argv], [s2argv], [], + [ + AC_MSG_ERROR([Could not find S2ARGV library]) + ]) # Checks for header files. AC_CHECK_HEADERS([fcntl.h stdint.h stdlib.h string.h unistd.h]) @@ -22,6 +26,9 @@ AC_CHECK_HEADERS([sys/capability.h], AC_CHECK_HEADERS([security/pam_appl.h security/pam_misc.h], [], [AC_MSG_ERROR([missing PAM headers])]) +AC_CHECK_HEADERS([s2argv.h], + [], + [AC_MSG_ERROR([missing S2ARGV headers])]) # Checks for typedefs, structures, and compiler characteristics. AC_TYPE_UID_T diff --git a/read_conf.c b/read_conf.c index d008961..c6fe7b5 100644 --- a/read_conf.c +++ b/read_conf.c @@ -25,9 +25,11 @@ #include #include #include +#include #include #include #include +#include #ifndef CONFDIR #define CONFDIR "/etc" @@ -35,6 +37,7 @@ #define CADO_CONF CONFDIR "/cado.conf" +/* groupmatch returns 1 if group belongs to grouplist */ static int groupmatch (char *group, char **grouplist) { for (;*grouplist; grouplist++) { //printf("%s %s\n",group, *grouplist); @@ -44,62 +47,91 @@ static int groupmatch (char *group, char **grouplist) { return 0; } -uint64_t get_authorized_caps(char **user_groups) { +/* s2argv security, children must drop their capabilities */ +static int drop_capabilities(void *useless) { + return prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_CLEAR_ALL, 0, 0, 0); +} + +/* get_authorized_caps returns the set of authorized capabilities + for the user user_groups[0] belonging to the groups user_groups[1:] */ +/* if user_groups==NULL, get_authorized_caps computes the maximum set + of capabilities that cado itself must own to be able to assign */ +uint64_t get_authorized_caps(char **user_groups, uint64_t reqset) { uint64_t ok_caps=0; FILE *f; + /* cado.conf is not readble by users. Add the capability to do it */ if (user_groups) raise_cap_dac_read_search(); f=fopen(CADO_CONF, "r"); if (f) { char *line=NULL; ssize_t len,n=0; - while ((len=getline(&line, &n, f)) > 0) { + /* set s2argv security, children must drop their capabilities */ + s2_fork_security=drop_capabilities; + while ((len=getline(&line, &n, f)) > 0 && (reqset & ~ok_caps)) { //printf("%s",line); char *scan=line; + char *tokencap; + char *tokenusergroup; + char *tokencondition; char *tok; uint64_t capset; char *tmptok; + int usermatch=0; + /* skip leading spaces */ while (isspace(*scan)) scan++; if (*scan == 0 || *scan == '#') //comment continue; - tok=strtok_r(scan, ":", &tmptok); - //printf("%s\n",tok); + tokencap=strtok_r(scan, ":", &tmptok); + //printf("CAP %s\n",tokencap); + tokenusergroup=strtok_r(NULL, ":\n", &tmptok); + //printf("UG %s\n",tokenusergroup); + tokencondition=strtok_r(NULL, ":\n", &tmptok); + //printf("COND %s\n",tokencondition); capset=0; - if (capset_from_namelist(tok, &capset) < 0) + if (capset_from_namelist(tokencap, &capset) < 0) continue; if (user_groups == NULL) { ok_caps |= capset; continue; } //printf("CAP %s %d\n",tok,thiscap); - while ((tok=strtok_r(NULL, ",\n ",&tmptok)) != NULL) { + while ((tok=strtok_r(tokenusergroup, ",\n ",&tmptok)) != NULL) { //printf("XX %s\n",tok); if (*tok=='@') { if (groupmatch(tok+1, user_groups+1)) { - ok_caps |= capset; + usermatch = 1; break; } } else if (strcmp(tok, user_groups[0]) == 0) { - ok_caps |= capset; + usermatch = 1; break; } + tokenusergroup=NULL; + } + if (usermatch) { + if (tokencondition) { + if (system_execsa(tokencondition) == 0) + ok_caps |= capset; + } else + ok_caps |= capset; } } fclose(f); if (line) free(line); } + /* the capability to read cado.conf is no longer needed */ if (user_groups) lower_cap_dac_read_search(); return ok_caps; } +/* set_self_capability sets the capability set needed by cado itself */ int set_self_capability(uint64_t capset) { cap_value_t cap; cap_t caps=cap_init(); int f,rv=-1; for (cap = 0; cap <= CAP_LAST_CAP; cap++) { if (capset & (1ULL << cap)) { - /*if (cap_set_flag(caps, CAP_PERMITTED, 1, &cap, CAP_SET) || - cap_set_flag(caps, CAP_EFFECTIVE, 1, &cap, CAP_SET)) {*/ if (cap_set_flag(caps, CAP_PERMITTED, 1, &cap, CAP_SET)) { fprintf(stderr, "Cannot set permitted cap %s\n",cap_to_name(cap)); exit(2); diff --git a/read_conf.h b/read_conf.h index 656e500..ef0c5cb 100644 --- a/read_conf.h +++ b/read_conf.h @@ -2,7 +2,7 @@ #define READ_CONF_H #include -uint64_t get_authorized_caps(char **user_groups); +uint64_t get_authorized_caps(char **user_groups, uint64_t reqset); int set_self_capability(uint64_t capset);