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>
#ifdef WITH_SELINUX
extern int set_selinux_file_context (const char *dst_name);
extern int reset_selinux_file_context (void);
+extern int check_selinux_permit (const char *perm_name);
#endif
/* semanage.c */
#include "defines.h"
#include <selinux/selinux.h>
+#include <selinux/context.h>
#include "prototypes.h"
return 0;
}
+/*
+ * Log callback for libselinux internal error reporting.
+ */
+__attribute__((__format__ (printf, 2, 3)))
+static int selinux_log_cb (int type, const char *fmt, ...) {
+ va_list ap;
+ char *buf;
+ int r;
+#ifdef WITH_AUDIT
+ static int selinux_audit_fd = -2;
+#endif
+
+ va_start (ap, fmt);
+ r = vasprintf (&buf, fmt, ap);
+ va_end (ap);
+
+ if (r < 0) {
+ return 0;
+ }
+
+#ifdef WITH_AUDIT
+ if (-2 == selinux_audit_fd) {
+ selinux_audit_fd = audit_open ();
+
+ if (-1 == selinux_audit_fd) {
+ /* You get these only when the kernel doesn't have
+ * audit compiled in. */
+ if ( (errno != EINVAL)
+ && (errno != EPROTONOSUPPORT)
+ && (errno != EAFNOSUPPORT)) {
+
+ (void) fputs (_("Cannot open audit interface.\n"),
+ stderr);
+ SYSLOG ((LOG_WARN, "Cannot open audit interface."));
+ }
+ }
+ }
+
+ if (-1 != selinux_audit_fd) {
+ if (SELINUX_AVC == type) {
+ if (audit_log_user_avc_message (selinux_audit_fd,
+ AUDIT_USER_AVC, buf, NULL, NULL,
+ NULL, 0) > 0) {
+ goto skip_syslog;
+ }
+ } else if (SELINUX_ERROR == type) {
+ if (audit_log_user_avc_message (selinux_audit_fd,
+ AUDIT_USER_SELINUX_ERR, buf, NULL, NULL,
+ NULL, 0) > 0) {
+ goto skip_syslog;
+ }
+ }
+ }
+#endif
+
+ SYSLOG ((LOG_WARN, "libselinux: %s", buf));
+
+skip_syslog:
+ free (buf);
+
+ return 0;
+}
+
+/*
+ * check_selinux_permit - Check whether SELinux grants the given
+ * operation
+ *
+ * Parameter is the SELinux permission name, e.g. rootok
+ *
+ * Returns 0 when permission is granted
+ * or something failed but running in
+ * permissive mode
+ */
+int check_selinux_permit (const char *perm_name)
+{
+ char *user_context_str;
+ int r;
+
+ if (0 == is_selinux_enabled ()) {
+ return 0;
+ }
+
+ selinux_set_callback (SELINUX_CB_LOG, (union selinux_callback) selinux_log_cb);
+
+ if (getprevcon (&user_context_str) != 0) {
+ fprintf (stderr,
+ _("%s: can not get previous SELinux process context: %s\n"),
+ Prog, strerror (errno));
+ SYSLOG ((LOG_WARN,
+ "can not get previous SELinux process context: %s",
+ strerror (errno)));
+ return (security_getenforce () != 0);
+ }
+
+ r = selinux_check_access (user_context_str, user_context_str, "passwd", perm_name, NULL);
+ freecon (user_context_str);
+ return r;
+}
+
#else /* !WITH_SELINUX */
extern int errno; /* warning: ANSI C forbids an empty source file */
#endif /* !WITH_SELINUX */
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
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
#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"
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
#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"
* 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);
#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"
* 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"),
#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"
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
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
*
#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 */