From: Craig Small Date: Mon, 11 Jan 2021 10:30:31 +0000 (+1100) Subject: killall: User security attributes and dlsym selinux X-Git-Tag: v23.4rc1~2 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=627167335878fb1cfc7712e26a6a045667ee7473;p=psmisc killall: User security attributes and dlsym selinux Like what was done for pstree, use the kernel security attributes when availavble and runttime link to selinux library when required. --- diff --git a/ChangeLog b/ChangeLog index 70ba912..e519515 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,6 @@ Changes in NEXT =============== + * killall: Dynamically link to selinux and use security attributes * pstree: Do not crash on missing processes !21 * pstree: fix layout when using -C !24 * pstree: add time namespace !25 diff --git a/Makefile.am b/Makefile.am index 84b6b87..762a05d 100644 --- a/Makefile.am +++ b/Makefile.am @@ -70,7 +70,7 @@ src_fuser_SOURCES += src/timeout.c src/timeout.h endif src_fuser_LDADD = @LIBINTL@ src_killall_SOURCES = src/killall.c src/comm.h src/signals.c src/signals.h src/i18n.h -src_killall_LDADD = @LIBINTL@ @SELINUX_LIB@ +src_killall_LDADD = @LIBINTL@ @DL_LIB@ src_peekfd_SOURCES = src/peekfd.c src_peekfd_LDADD = @LIBINTL@ src_pslog_SOURCES = src/pslog.c diff --git a/configure.ac b/configure.ac index 3ab7645..72e8ea2 100644 --- a/configure.ac +++ b/configure.ac @@ -30,6 +30,7 @@ PSMISC_PROG_PO4A dnl checks for options # SELinux support - off by default +DL_LIB= AC_SUBST([WITH_SELINUX]) AC_ARG_ENABLE([selinux], [AS_HELP_STRING([--enable-selinux], [Enable Security-Enhanced Linux features])], @@ -37,10 +38,13 @@ AC_ARG_ENABLE([selinux], [enable_selinux="no"]) if test "$enable_selinux" = "yes"; then AC_DEFINE([WITH_SELINUX], [1], [Use Security-Enhanced Linux features]) - AC_CHECK_LIB([selinux], [getfilecon], [SELINUX_LIB=-lselinux], [ - AC_MSG_ERROR([Cannot find selinux static library]) ]) + AC_SEARCH_LIBS([dlopen], [dl], [], + [AC_MSG_ERROR([dynamic linking unavailable, circumvent with --disable-selinux])]) + if test "x$ac_cv_search_dlopen" != "xnone required"; then + DL_LIB="$ac_cv_search_dlopen" + fi fi -AC_SUBST([SELINUX_LIB]) +AC_SUBST([DL_LIB]) # Call fork before all stat calls to stop hanging on NFS mounts AC_SUBST([WITH_TIMEOUT_STAT]) diff --git a/doc/killall.1 b/doc/killall.1 index d63097c..f0d28a7 100644 --- a/doc/killall.1 +++ b/doc/killall.1 @@ -1,12 +1,12 @@ .\" .\" Copyright 1993-2002 Werner Almesberger -.\" 2002-2020 Craig Small +.\" 2002-2021 Craig Small .\" This program 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 of the License, or .\" (at your option) any later version. .\" -.TH KILLALL 1 "2020-09-09" "psmisc" "User Commands" +.TH KILLALL 1 "2021-01-11" "psmisc" "User Commands" .SH NAME killall \- kill processes by name .SH SYNOPSIS @@ -136,7 +136,7 @@ specified. The time is specified as a float then a unit. The units are s,m,h,d,w,M,y for seconds, minutes, hours, days, weeks, Months and years respectively. .IP "\fB\-Z\fP, \fB\-\-context\fP" -(SELinux Only) Specify security context: kill only processes having +Specify security context: kill only processes having security context that match with given extended regular expression pattern. Must precede other arguments on the command line. Command names are optional. diff --git a/src/killall.c b/src/killall.c index 524760b..9fab8f5 100644 --- a/src/killall.c +++ b/src/killall.c @@ -2,7 +2,7 @@ * killall.c - kill processes by name or list PIDs * * Copyright (C) 1993-2002 Werner Almesberger - * Copyright (C) 2002-2020 Craig Small + * Copyright (C) 2002-2021 Craig Small * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -45,6 +45,7 @@ #include #ifdef WITH_SELINUX +#include #include #endif /*WITH_SELINUX*/ @@ -257,6 +258,60 @@ match_process_uid(pid_t pid, uid_t uid) return re; } +/* Match on the given scontext to the process context + * Return 0 on a match + */ +static int +match_process_context(const pid_t pid, const regex_t *scontext) +{ + static void (*my_freecon)(char*) = NULL; + static int (*my_getpidcon)(pid_t pid, char **context) = NULL; + static int selinux_enabled = 0; + char *lcontext; + int retval = 1; + +#ifdef WITH_SELINUX + static int tried_load = 0; + static int (*my_is_selinux_enabled)(void) = NULL; + + if(!my_getpidcon && !tried_load){ + void *handle = dlopen("libselinux.so.1", RTLD_NOW); + if(handle) { + my_freecon = dlsym(handle, "freecon"); + if(dlerror()) + my_freecon = NULL; + my_getpidcon = dlsym(handle, "getpidcon"); + if(dlerror()) + my_getpidcon = NULL; + my_is_selinux_enabled = dlsym(handle, "is_selinux_enabled"); + if(dlerror()) + my_is_selinux_enabled = 0; + else + selinux_enabled = my_is_selinux_enabled(); + } + tried_load++; + } +#endif /* WITH_SELINUX */ + + if (my_getpidcon && selinux_enabled && !my_getpidcon(pid, &lcontext)) { + retval = (regexec(scontext, lcontext, 0, NULL, 0) ==0); + my_freecon(lcontext); + } else { + FILE *file; + char path[50]; + char readbuf[BUFSIZ+1]; + snprintf(path, sizeof path, "/proc/%d/attr/current", pid); + if ( (file = fopen(path, "r")) != NULL) { + if (fgets(readbuf, BUFSIZ, file) != NULL) { + retval = (regexec(scontext, readbuf, 0, NULL, 0)==0); + } + fclose(file); + } + } + return retval; +} + + static void free_regexp_list(regex_t *reglist, int names) { @@ -536,14 +591,9 @@ static int match_process_name( return (0 == strcmp2 (match_name, proc_comm, ignore_case)); } -#ifdef WITH_SELINUX static int kill_all(int signal, int name_count, char **namelist, struct passwd *pwent, regex_t *scontext ) -#else /*WITH_SELINUX*/ -static int -kill_all (int signal, int name_count, char **namelist, struct passwd *pwent) -#endif /*WITH_SELINUX*/ { struct stat st; NAMEINFO *name_info = NULL; @@ -556,9 +606,6 @@ kill_all (int signal, int name_count, char **namelist, struct passwd *pwent) unsigned long found; regex_t *reglist = NULL; long ns_ino = 0; -#ifdef WITH_SELINUX - security_context_t lcontext=NULL; -#endif /*WITH_SELINUX*/ if (opt_ns_pid) ns_ino = get_ns(opt_ns_pid, PIDNS); @@ -599,19 +646,9 @@ kill_all (int signal, int name_count, char **namelist, struct passwd *pwent) if (opt_ns_pid && ns_ino && ns_ino != get_ns(pid_table[i], PIDNS)) continue; -#ifdef WITH_SELINUX - /* match by SELinux context */ - if (scontext) - { - if (getpidcon(pid_table[i], &lcontext) < 0) - continue; - if (regexec(scontext, lcontext, 0, NULL, 0) != 0) { - freecon(lcontext); - continue; - } - freecon(lcontext); - } -#endif /*WITH_SELINUX*/ + if (scontext && match_process_context(pid_table[i], scontext) == 0) + continue; + length = load_process_name_and_age(comm, &process_age_sec, pid_table[i], (younger_than||older_than)); if (length < 0) continue; @@ -763,14 +800,8 @@ usage (const char *msg) { if (msg != NULL) fprintf(stderr, "%s\n", msg); -#ifdef WITH_SELINUX - fprintf(stderr, _( - "Usage: killall [ -Z CONTEXT ] [ -u USER ] [ -y TIME ] [ -o TIME ] [ -eIgiqrvw ]\n" - " [ -s SIGNAL | -SIGNAL ] NAME...\n")); -#else /*WITH_SELINUX*/ fprintf(stderr, _( "Usage: killall [OPTION]... [--] NAME...\n")); -#endif /*WITH_SELINUX*/ fprintf(stderr, _( " killall -l, --list\n" " killall -V, --version\n\n" @@ -791,11 +822,9 @@ usage (const char *msg) " -n,--ns PID match processes that belong to the same namespaces\n" " as PID\n")); -#ifdef WITH_SELINUX fprintf(stderr, _( " -Z,--context REGEXP kill only process(es) having context\n" " (must precede other arguments)\n")); -#endif /*WITH_SELINUX*/ fputc('\n', stderr); exit(1); } @@ -805,7 +834,7 @@ void print_version() { fprintf(stderr, "killall (PSmisc) %s\n", VERSION); fprintf(stderr, _( - "Copyright (C) 1993-2020 Werner Almesberger and Craig Small\n\n")); + "Copyright (C) 1993-2021 Werner Almesberger and Craig Small\n\n")); fprintf(stderr, _( "PSmisc comes with ABSOLUTELY NO WARRANTY.\n" "This is free software, and you are welcome to redistribute it under\n" @@ -853,9 +882,7 @@ main (int argc, char **argv) {"verbose", 0, NULL, 'v'}, {"wait", 0, NULL, 'w'}, {"ns", 1, NULL, 'n' }, -#ifdef WITH_SELINUX {"context", 1, NULL, 'Z'}, -#endif /*WITH_SELINUX*/ {"version", 0, NULL, 'V'}, {0,0,0,0 }}; @@ -866,12 +893,10 @@ main (int argc, char **argv) bindtextdomain(PACKAGE, LOCALEDIR); textdomain(PACKAGE); #endif -#ifdef WITH_SELINUX - security_context_t scontext = NULL; + char *scontext = NULL; regex_t scontext_reg; if ( argc < 2 ) usage(NULL); /* do the obvious thing... */ -#endif /*WITH_SELINUX*/ name = strrchr (*argv, '/'); if (name) @@ -882,11 +907,7 @@ main (int argc, char **argv) opterr = 0; -#ifdef WITH_SELINUX while ( (optc = getopt_long_only(argc,argv,"egy:o:ilqrs:u:vwZ:VIn:",options,NULL)) != -1) { -#else - while ( (optc = getopt_long_only(argc,argv,"egy:o:ilqrs:u:vwVIn:",options,NULL)) != -1) { -#endif switch (optc) { case 'e': exact = 1; @@ -963,18 +984,13 @@ main (int argc, char **argv) opt_ns_pid = (pid_t) num; } break; -#ifdef WITH_SELINUX case 'Z': - if (is_selinux_enabled()>0) { - scontext=optarg; - if (regcomp(&scontext_reg, scontext, REG_EXTENDED|REG_NOSUB) != 0) { - fprintf(stderr, _("Bad regular expression: %s\n"), scontext); - exit (1); - } - } else - fprintf(stderr, "Warning: -Z (--context) ignored. Requires an SELinux enabled kernel\n"); + scontext=optarg; + if (regcomp(&scontext_reg, scontext, REG_EXTENDED|REG_NOSUB) != 0) { + fprintf(stderr, _("Bad regular expression: %s\n"), scontext); + exit (1); + } break; -#endif /*WITH_SELINUX*/ case '?': if (skip_error == optind) break; @@ -1001,11 +1017,7 @@ main (int argc, char **argv) } } myoptind = optind; -#ifdef WITH_SELINUX if ((argc - myoptind < 1) && pwent==NULL && scontext==NULL) -#else - if ((argc - myoptind < 1) && pwent==NULL) -#endif usage(NULL); if (argc - myoptind > MAX_NAMES) { @@ -1019,10 +1031,6 @@ main (int argc, char **argv) exit (1); } argv = argv + myoptind; -#ifdef WITH_SELINUX return kill_all(sig_num,argc - myoptind, argv, pwent, scontext ? &scontext_reg : NULL); -#else /*WITH_SELINUX*/ - return kill_all(sig_num,argc - myoptind, argv, pwent); -#endif /*WITH_SELINUX*/ } diff --git a/src/pstree.c b/src/pstree.c index 9b9b50b..1021b6c 100644 --- a/src/pstree.c +++ b/src/pstree.c @@ -48,6 +48,7 @@ #include "comm.h" #ifdef WITH_SELINUX +#include #include #else typedef void* security_context_t; /* DUMMY to remove most ifdefs */ @@ -470,30 +471,30 @@ static int out_int(int x) */ static void out_scontext(const PROC *current) { - static void (*ps_freecon)(char*) = 0; - static int (*ps_getpidcon)(pid_t pid, char **context) = 0; + static void (*my_freecon)(char*) = 0; + static int (*my_getpidcon)(pid_t pid, char **context) = 0; static int selinux_enabled = 0; char *context; #ifdef WITH_SELINUX - static int (*ps_is_selinux_enabled)(void) = 0; + static int (*my_is_selinux_enabled)(void) = 0; static int tried_load = 0; - if(!ps_getpidcon && !tried_load){ + if(!my_getpidcon && !tried_load){ void *handle = dlopen("libselinux.so.1", RTLD_NOW); if(handle) { - ps_freecon = dlsym(handle, "freecon"); + my_freecon = dlsym(handle, "freecon"); if(dlerror()) - ps_freecon = 0; + my_freecon = 0; dlerror(); - ps_getpidcon = dlsym(handle, "getpidcon"); + my_getpidcon = dlsym(handle, "getpidcon"); if(dlerror()) - ps_getpidcon = 0; - ps_is_selinux_enabled = dlsym(handle, "is_selinux_enabled"); + my_getpidcon = 0; + my_is_selinux_enabled = dlsym(handle, "is_selinux_enabled"); if(dlerror()) - ps_is_selinux_enabled = 0; + my_is_selinux_enabled = 0; else - selinux_enabled = ps_is_selinux_enabled(); + selinux_enabled = my_is_selinux_enabled(); } tried_load++; } @@ -501,9 +502,9 @@ static void out_scontext(const PROC *current) out_string("`"); - if (ps_getpidcon && selinux_enabled && !ps_getpidcon(current->pid, &context)) { + if (my_getpidcon && selinux_enabled && !my_getpidcon(current->pid, &context)) { out_string(context); - ps_freecon(context); + my_freecon(context); } else { FILE *file; char path[50];