migrate to new SELinux api

Using hard-coded access vector ids is deprecated and can lead to issues with custom SELinux policies.
Switch to `selinux_check_access()`.

Also use the libselinux log callback and log if available to audit.
This makes it easier for users to catch SELinux denials.

Drop legacy shortcut logic for passwd, which avoided a SELinux check if uid 0 changes a password of a user which username equals the current SELinux user identifier.
Nowadays usernames rarely match SELinux user identifiers and the benefit of skipping a SELinux check is negligible.

Signed-off-by: Christian Göttsche <cgzones@googlemail.com>
This commit is contained in:
Christian Göttsche
2019-10-15 23:33:54 +02:00
parent a0efca4581
commit cbd2472b7c
7 changed files with 125 additions and 107 deletions

View File

@@ -89,33 +89,33 @@ LIBCRYPT_NOPAM = $(LIBCRYPT)
endif
chage_LDADD = $(LDADD) $(LIBPAM_SUID) $(LIBAUDIT) $(LIBSELINUX) $(LIBECONF)
newuidmap_LDADD = $(LDADD) $(LIBSELINUX) $(LIBCAP)
newgidmap_LDADD = $(LDADD) $(LIBSELINUX) $(LIBCAP)
chfn_LDADD = $(LDADD) $(LIBPAM) $(LIBSELINUX) $(LIBCRYPT_NOPAM) $(LIBSKEY) $(LIBMD) $(LIBECONF)
chgpasswd_LDADD = $(LDADD) $(LIBPAM_SUID) $(LIBSELINUX) $(LIBCRYPT) $(LIBECONF)
chsh_LDADD = $(LDADD) $(LIBPAM) $(LIBSELINUX) $(LIBCRYPT_NOPAM) $(LIBSKEY) $(LIBMD) $(LIBECONF)
chpasswd_LDADD = $(LDADD) $(LIBPAM) $(LIBSELINUX) $(LIBCRYPT) $(LIBECONF)
newuidmap_LDADD = $(LDADD) $(LIBAUDIT) $(LIBSELINUX) $(LIBCAP)
newgidmap_LDADD = $(LDADD) $(LIBAUDIT) $(LIBSELINUX) $(LIBCAP)
chfn_LDADD = $(LDADD) $(LIBPAM) $(LIBAUDIT) $(LIBSELINUX) $(LIBCRYPT_NOPAM) $(LIBSKEY) $(LIBMD) $(LIBECONF)
chgpasswd_LDADD = $(LDADD) $(LIBPAM_SUID) $(LIBAUDIT) $(LIBSELINUX) $(LIBCRYPT) $(LIBECONF)
chsh_LDADD = $(LDADD) $(LIBPAM) $(LIBAUDIT) $(LIBSELINUX) $(LIBCRYPT_NOPAM) $(LIBSKEY) $(LIBMD) $(LIBECONF)
chpasswd_LDADD = $(LDADD) $(LIBPAM) $(LIBAUDIT) $(LIBSELINUX) $(LIBCRYPT) $(LIBECONF)
expiry_LDADD = $(LDADD) $(LIBECONF)
gpasswd_LDADD = $(LDADD) $(LIBAUDIT) $(LIBSELINUX) $(LIBCRYPT) $(LIBECONF)
groupadd_LDADD = $(LDADD) $(LIBPAM_SUID) $(LIBAUDIT) $(LIBSELINUX) $(LIBECONF)
groupdel_LDADD = $(LDADD) $(LIBPAM_SUID) $(LIBAUDIT) $(LIBSELINUX) $(LIBECONF)
groupmems_LDADD = $(LDADD) $(LIBPAM) $(LIBSELINUX) $(LIBECONF)
groupmems_LDADD = $(LDADD) $(LIBPAM) $(LIBAUDIT) $(LIBSELINUX) $(LIBECONF)
groupmod_LDADD = $(LDADD) $(LIBPAM_SUID) $(LIBAUDIT) $(LIBSELINUX) $(LIBECONF)
grpck_LDADD = $(LDADD) $(LIBSELINUX) $(LIBECONF)
grpconv_LDADD = $(LDADD) $(LIBSELINUX) $(LIBECONF)
grpunconv_LDADD = $(LDADD) $(LIBSELINUX) $(LIBECONF)
grpck_LDADD = $(LDADD) $(LIBAUDIT) $(LIBSELINUX) $(LIBECONF)
grpconv_LDADD = $(LDADD) $(LIBAUDIT) $(LIBSELINUX) $(LIBECONF)
grpunconv_LDADD = $(LDADD) $(LIBAUDIT) $(LIBSELINUX) $(LIBECONF)
lastlog_LDADD = $(LDADD) $(LIBAUDIT) $(LIBECONF)
login_SOURCES = \
login.c \
login_nopam.c
login_LDADD = $(LDADD) $(LIBPAM) $(LIBAUDIT) $(LIBCRYPT_NOPAM) $(LIBSKEY) $(LIBMD) $(LIBECONF)
newgrp_LDADD = $(LDADD) $(LIBAUDIT) $(LIBCRYPT) $(LIBECONF)
newusers_LDADD = $(LDADD) $(LIBPAM) $(LIBSELINUX) $(LIBCRYPT) $(LIBECONF)
newusers_LDADD = $(LDADD) $(LIBPAM) $(LIBAUDIT) $(LIBSELINUX) $(LIBCRYPT) $(LIBECONF)
nologin_LDADD =
passwd_LDADD = $(LDADD) $(LIBPAM) $(LIBCRACK) $(LIBAUDIT) $(LIBSELINUX) $(LIBCRYPT_NOPAM) $(LIBECONF)
pwck_LDADD = $(LDADD) $(LIBSELINUX) $(LIBECONF)
pwconv_LDADD = $(LDADD) $(LIBSELINUX) $(LIBECONF)
pwunconv_LDADD = $(LDADD) $(LIBSELINUX) $(LIBECONF)
pwck_LDADD = $(LDADD) $(LIBAUDIT) $(LIBSELINUX) $(LIBECONF)
pwconv_LDADD = $(LDADD) $(LIBAUDIT) $(LIBSELINUX) $(LIBECONF)
pwunconv_LDADD = $(LDADD) $(LIBAUDIT) $(LIBSELINUX) $(LIBECONF)
su_SOURCES = \
su.c \
suauth.c
@@ -124,7 +124,7 @@ sulogin_LDADD = $(LDADD) $(LIBCRYPT) $(LIBECONF)
useradd_LDADD = $(LDADD) $(LIBPAM_SUID) $(LIBAUDIT) $(LIBSELINUX) $(LIBSEMANAGE) $(LIBACL) $(LIBATTR) $(LIBECONF)
userdel_LDADD = $(LDADD) $(LIBPAM_SUID) $(LIBAUDIT) $(LIBSELINUX) $(LIBSEMANAGE) $(LIBECONF)
usermod_LDADD = $(LDADD) $(LIBPAM_SUID) $(LIBAUDIT) $(LIBSELINUX) $(LIBSEMANAGE) $(LIBACL) $(LIBATTR) $(LIBECONF)
vipw_LDADD = $(LDADD) $(LIBSELINUX) $(LIBECONF)
vipw_LDADD = $(LDADD) $(LIBAUDIT) $(LIBSELINUX) $(LIBECONF)
install-am: all-am
$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am

View File

@@ -48,10 +48,6 @@
#endif /* USE_PAM */
#endif /* ACCT_TOOLS_SETUID */
#include <pwd.h>
#ifdef WITH_SELINUX
#include <selinux/selinux.h>
#include <selinux/av_permissions.h>
#endif
#include "prototypes.h"
#include "defines.h"
#include "pwio.h"
@@ -832,8 +828,8 @@ int main (int argc, char **argv)
rgid = getgid ();
amroot = (ruid == 0);
#ifdef WITH_SELINUX
if (amroot && (is_selinux_enabled () > 0)) {
amroot = (selinux_check_passwd_access (PASSWD__ROOTOK) == 0);
if (amroot) {
amroot = (check_selinux_permit ("rootok") == 0);
}
#endif

View File

@@ -40,10 +40,6 @@
#include <stdio.h>
#include <sys/types.h>
#include <getopt.h>
#ifdef WITH_SELINUX
#include <selinux/selinux.h>
#include <selinux/av_permissions.h>
#endif
#include "defines.h"
#include "getdef.h"
#include "nscd.h"
@@ -379,8 +375,7 @@ static void check_perms (const struct passwd *pw)
* check if the change is allowed by SELinux policy.
*/
if ((pw->pw_uid != getuid ())
&& (is_selinux_enabled () > 0)
&& (selinux_check_passwd_access (PASSWD__CHFN) != 0)) {
&& (check_selinux_permit ("chfn") != 0)) {
fprintf (stderr, _("%s: Permission denied.\n"), Prog);
closelog ();
exit (E_NOPERM);

View File

@@ -39,10 +39,6 @@
#include <pwd.h>
#include <stdio.h>
#include <sys/types.h>
#ifdef WITH_SELINUX
#include <selinux/selinux.h>
#include <selinux/av_permissions.h>
#endif
#include "defines.h"
#include "getdef.h"
#include "nscd.h"
@@ -286,8 +282,7 @@ static void check_perms (const struct passwd *pw)
* check if the change is allowed by SELinux policy.
*/
if ((pw->pw_uid != getuid ())
&& (is_selinux_enabled () > 0)
&& (selinux_check_passwd_access (PASSWD__CHSH) != 0)) {
&& (check_selinux_permit("chsh") != 0)) {
SYSLOG ((LOG_WARN, "can't change shell for '%s'", pw->pw_name));
fprintf (stderr,
_("You may not change the shell for '%s'.\n"),

View File

@@ -41,12 +41,6 @@
#include <signal.h>
#include <stdio.h>
#include <sys/types.h>
#ifdef WITH_SELINUX
#include <selinux/selinux.h>
#include <selinux/flask.h>
#include <selinux/av_permissions.h>
#include <selinux/context.h>
#endif /* WITH_SELINUX */
#include <time.h>
#include "defines.h"
#include "getdef.h"
@@ -149,11 +143,6 @@ static char *update_crypt_pw (char *);
static void update_noshadow (void);
static void update_shadow (void);
#ifdef WITH_SELINUX
static int check_selinux_access (const char *changed_user,
uid_t changed_uid,
access_vector_t requested_access);
#endif /* WITH_SELINUX */
/*
* usage - print command usage and exit
@@ -710,55 +699,6 @@ static void update_shadow (void)
spw_locked = false;
}
#ifdef WITH_SELINUX
static int check_selinux_access (const char *changed_user,
uid_t changed_uid,
access_vector_t requested_access)
{
int status = -1;
security_context_t user_context;
context_t c;
const char *user;
/* if in permissive mode then allow the operation */
if (security_getenforce() == 0) {
return 0;
}
/* get the context of the process which executed passwd */
if (getprevcon(&user_context) != 0) {
return -1;
}
/* get the "user" portion of the context (the part before the first
colon) */
c = context_new(user_context);
user = context_user_get(c);
/* if changing a password for an account with UID==0 or for an account
where the identity matches then return success */
if (changed_uid != 0 && strcmp(changed_user, user) == 0) {
status = 0;
} else {
struct av_decision avd;
int retval;
retval = security_compute_av(user_context,
user_context,
SECCLASS_PASSWD,
requested_access,
&avd);
if ((retval == 0) &&
((requested_access & avd.allowed) == requested_access)) {
status = 0;
}
}
context_free(c);
freecon(user_context);
return status;
}
#endif /* WITH_SELINUX */
/*
* passwd - change a user's password file information
*
@@ -1034,22 +974,13 @@ int main (int argc, char **argv)
#ifdef WITH_SELINUX
/* only do this check when getuid()==0 because it's a pre-condition for
changing a password without entering the old one */
if ((is_selinux_enabled() > 0) && (getuid() == 0) &&
(check_selinux_access (name, pw->pw_uid, PASSWD__PASSWD) != 0)) {
security_context_t user_context = NULL;
const char *user = "Unknown user context";
if (getprevcon (&user_context) == 0) {
user = user_context; /* FIXME: use context_user_get? */
}
if (amroot && (check_selinux_permit ("passwd") != 0)) {
SYSLOG ((LOG_ALERT,
"%s is not authorized to change the password of %s",
user, name));
"root is not authorized by SELinux to change the password of %s",
name));
(void) fprintf(stderr,
_("%s: %s is not authorized to change the password of %s\n"),
Prog, user, name);
if (NULL != user_context) {
freecon (user_context);
}
_("%s: root is not authorized by SELinux to change the password of %s\n"),
Prog, name);
exit (E_NOPERM);
}
#endif /* WITH_SELINUX */