mirror of
https://github.com/sudo-project/sudo.git
synced 2025-08-22 01:49:11 +00:00
Add support for SELinux RBAC. Sudoers entries may specify a role and type.
There are also role and type defaults that may be used. To make sure a transition occurs, when using RBAC commands are executed via the new sesh binary. Based on initial changes from Dan Walsh.
This commit is contained in:
parent
5d20923c2f
commit
f2b70188b6
@ -61,6 +61,7 @@ exec_prefix = @exec_prefix@
|
||||
bindir = @bindir@
|
||||
sbindir = @sbindir@
|
||||
sysconfdir = @sysconfdir@
|
||||
libexecdir = @libexecdir@
|
||||
datarootdir = @datarootdir@
|
||||
mandir = @mandir@
|
||||
noexecdir = @NOEXECDIR@
|
||||
@ -106,7 +107,7 @@ SRCS = alias.c alloc.c check.c closefrom.c def_data.c defaults.c env.c \
|
||||
pwutil.c set_perms.c sigaction.c snprintf.c strcasecmp.c strerror.c \
|
||||
strlcat.c strlcpy.c sudo.c sudo_noexec.c sudo_edit.c sudo_nss.c \
|
||||
testsudoers.c tgetpass.c toke.c toke.l tsgetgrpw.c utimes.c visudo.c \
|
||||
zero_bytes.c redblack.c $(AUTH_SRCS)
|
||||
zero_bytes.c redblack.c selinux.c sesh.c $(AUTH_SRCS)
|
||||
|
||||
AUTH_SRCS = auth/afs.c auth/aix_auth.c auth/bsdauth.c auth/dce.c auth/fwtk.c \
|
||||
auth/kerb4.c auth/kerb5.c auth/pam.c auth/passwd.c auth/rfc1938.c \
|
||||
@ -286,6 +287,8 @@ strlcat.o: $(srcdir)/strlcat.c $(srcdir)/compat.h config.h
|
||||
$(CC) -c $(CPPFLAGS) $(CFLAGS) $(DEFS) $(OPTIONS) $(srcdir)/strlcat.c
|
||||
strlcpy.o: $(srcdir)/strlcpy.c $(srcdir)/compat.h config.h
|
||||
$(CC) -c $(CPPFLAGS) $(CFLAGS) $(DEFS) $(OPTIONS) $(srcdir)/strlcpy.c
|
||||
selinux.o: $(srcdir)/selinux.c $(SUDODEP)
|
||||
$(CC) -c $(CPPFLAGS) $(CFLAGS) $(DEFS) $(OPTIONS) $(srcdir)/selinux.c
|
||||
sudo.o: $(srcdir)/sudo.c $(SUDODEP) sudo_usage.h $(srcdir)/interfaces.h $(srcdir)/version.h
|
||||
$(CC) -c $(CPPFLAGS) $(CFLAGS) $(DEFS) $(OPTIONS) $(srcdir)/sudo.c
|
||||
sudo_edit.o: $(srcdir)/sudo_edit.c $(SUDODEP)
|
||||
@ -401,8 +404,9 @@ install-binaries: $(PROGS)
|
||||
$(INSTALL) -O $(install_uid) -G $(install_gid) -M 4111 -s sudo $(DESTDIR)$(sudodir)/sudo
|
||||
rm -f $(DESTDIR)$(sudodir)/sudoedit
|
||||
ln $(DESTDIR)$(sudodir)/sudo $(DESTDIR)$(sudodir)/sudoedit
|
||||
|
||||
$(INSTALL) -O $(install_uid) -G $(install_gid) -M 0111 -s visudo $(DESTDIR)$(visudodir)/visudo
|
||||
@SELINUX@ $(INSTALL) -O $(install_uid) -G $(install_gid) -M 0111 -s sesh $(DESTDIR)$(libexecdir)/sesh
|
||||
|
||||
|
||||
install-noexec: sudo_noexec.la
|
||||
$(LIBTOOL) --mode=install $(INSTALL) sudo_noexec.la $(DESTDIR)$(noexecdir)
|
||||
|
@ -317,6 +317,9 @@
|
||||
/* Define to 1 if you use SecurID for authentication. */
|
||||
#undef HAVE_SECURID
|
||||
|
||||
/* Define to 1 to enable SELinux RBAC support. */
|
||||
#undef HAVE_SELINUX
|
||||
|
||||
/* Define to 1 if you have the `seteuid' function. */
|
||||
#undef HAVE_SETEUID
|
||||
|
||||
|
36
configure.in
36
configure.in
@ -33,6 +33,7 @@ AC_SUBST(SUDOERS_MODE)
|
||||
AC_SUBST(SUDOERS_UID)
|
||||
AC_SUBST(SUDOERS_GID)
|
||||
AC_SUBST(DEV)
|
||||
AC_SUBST(SELINUX)
|
||||
AC_SUBST(devdir)
|
||||
AC_SUBST(mansectsu)
|
||||
AC_SUBST(mansectform)
|
||||
@ -42,6 +43,7 @@ AC_SUBST(noexec_file)
|
||||
AC_SUBST(INSTALL_NOEXEC)
|
||||
AC_SUBST(DONT_LEAK_PATH_INFO)
|
||||
AC_SUBST(BSDAUTH_USAGE)
|
||||
AC_SUBST(SELINUX_USAGE)
|
||||
AC_SUBST(LDAP)
|
||||
AC_SUBST(LOGINCAP_USAGE)
|
||||
dnl
|
||||
@ -118,6 +120,7 @@ PROGS="sudo visudo"
|
||||
: ${SUDOERS_GID='0'}
|
||||
DEV="#"
|
||||
LDAP="#"
|
||||
SELINUX="#"
|
||||
AUTH_OBJS=
|
||||
AUTH_REG=
|
||||
AUTH_EXCL=
|
||||
@ -1146,6 +1149,20 @@ AC_ARG_ENABLE(path_info,
|
||||
esac
|
||||
], AC_MSG_RESULT(no))
|
||||
|
||||
AC_ARG_WITH(selinux, [ --with-selinux enable SELinux support],
|
||||
[case $with_selinux in
|
||||
yes) SELINUX_USAGE="[[-r role]] [[-t type]] "
|
||||
AC_DEFINE(HAVE_SELINUX)
|
||||
SUDO_LIBS="${SUDO_LIBS} -lselinux"
|
||||
SUDO_OBJS="${SUDO_OBJS} selinux.o"
|
||||
PROGS="${PROGS} sesh"
|
||||
SELINUX=""
|
||||
;;
|
||||
no) ;;
|
||||
*) AC_MSG_ERROR(["--with-selinux does not take an argument."])
|
||||
;;
|
||||
esac])
|
||||
|
||||
dnl
|
||||
dnl If we don't have egrep we can't do anything...
|
||||
dnl
|
||||
@ -2414,10 +2431,7 @@ dnl
|
||||
dnl Defer setting _PATH_SUDO_NOEXEC until after exec_prefix is set
|
||||
dnl XXX - this is gross!
|
||||
dnl
|
||||
if test "$with_noexec" != "no"; then
|
||||
PROGS="${PROGS} sudo_noexec.la"
|
||||
INSTALL_NOEXEC="install-noexec"
|
||||
|
||||
if test X"$with_noexec" != X"no" -o X"$with_selinux" != X"no"; then
|
||||
oexec_prefix="$exec_prefix"
|
||||
if test "$exec_prefix" = '$(prefix)'; then
|
||||
if test "$prefix" = "NONE"; then
|
||||
@ -2426,8 +2440,17 @@ if test "$with_noexec" != "no"; then
|
||||
exec_prefix="$prefix"
|
||||
fi
|
||||
fi
|
||||
eval noexec_file="$with_noexec"
|
||||
AC_DEFINE_UNQUOTED(_PATH_SUDO_NOEXEC, "$noexec_file", [The fully qualified pathname of sudo_noexec.so])
|
||||
if test X"$with_noexec" != X"no"; then
|
||||
PROGS="${PROGS} sudo_noexec.la"
|
||||
INSTALL_NOEXEC="install-noexec"
|
||||
|
||||
eval noexec_file="$with_noexec"
|
||||
AC_DEFINE_UNQUOTED(_PATH_SUDO_NOEXEC, "$noexec_file", [The fully qualified pathname of sudo_noexec.so])
|
||||
fi
|
||||
if test X"$with_selinux" != X"no"; then
|
||||
eval sesh_file="$libexecdir/sesh"
|
||||
AC_DEFINE_UNQUOTED(_PATH_SUDO_SESH, "$sesh_file", [The fully qualified pathname of sesh])
|
||||
fi
|
||||
exec_prefix="$oexec_prefix"
|
||||
fi
|
||||
|
||||
@ -2489,6 +2512,7 @@ AH_TEMPLATE(HAVE_OPIE, [Define to 1 if you use NRL OPIE.])
|
||||
AH_TEMPLATE(HAVE_PAM, [Define to 1 if you use PAM authentication.])
|
||||
AH_TEMPLATE(HAVE_PROJECT_H, [Define to 1 if you have the <project.h> header file.])
|
||||
AH_TEMPLATE(HAVE_SECURID, [Define to 1 if you use SecurID for authentication.])
|
||||
AH_TEMPLATE(HAVE_SELINUX, [Define to 1 to enable SELinux RBAC support.])
|
||||
AH_TEMPLATE(HAVE_SIA, [Define to 1 if you use SIA authentication.])
|
||||
AH_TEMPLATE(HAVE_SIGACTION_T, [Define to 1 if <signal.h> has the sigaction_t typedef.])
|
||||
AH_TEMPLATE(HAVE_SKEY, [Define to 1 if you use S/Key.])
|
||||
|
@ -274,6 +274,14 @@ struct sudo_defs_types sudo_defs_table[] = {
|
||||
"env_keep", T_LIST|T_BOOL,
|
||||
"Environment variables to preserve:",
|
||||
NULL,
|
||||
}, {
|
||||
"role", T_STR,
|
||||
"SELinux role to use in the new security context",
|
||||
NULL,
|
||||
}, {
|
||||
"type", T_STR,
|
||||
"SELinux type to use in the new security context",
|
||||
NULL,
|
||||
}, {
|
||||
NULL, 0, NULL
|
||||
}
|
||||
|
@ -124,6 +124,10 @@
|
||||
#define I_ENV_DELETE 61
|
||||
#define def_env_keep (sudo_defs_table[62].sd_un.list)
|
||||
#define I_ENV_KEEP 62
|
||||
#define def_role (sudo_defs_table[63].sd_un.str)
|
||||
#define I_ROLE 63
|
||||
#define def_type (sudo_defs_table[64].sd_un.str)
|
||||
#define I_TYPE 64
|
||||
|
||||
enum def_tupple {
|
||||
never,
|
||||
|
@ -202,3 +202,9 @@ env_delete
|
||||
env_keep
|
||||
T_LIST|T_BOOL
|
||||
"Environment variables to preserve:"
|
||||
role
|
||||
T_STR
|
||||
"SELinux role to use in the new security context"
|
||||
type
|
||||
T_STR
|
||||
"SELinux type to use in the new security context"
|
||||
|
3
gram.h
3
gram.h
@ -23,6 +23,8 @@
|
||||
#define USERALIAS 279
|
||||
#define RUNASALIAS 280
|
||||
#define ERROR 281
|
||||
#define TYPE 282
|
||||
#define ROLE 283
|
||||
#ifndef YYSTYPE_DEFINED
|
||||
#define YYSTYPE_DEFINED
|
||||
typedef union {
|
||||
@ -33,6 +35,7 @@ typedef union {
|
||||
struct privilege *privilege;
|
||||
struct sudo_command command;
|
||||
struct cmndtag tag;
|
||||
struct selinux_info seinfo;
|
||||
char *string;
|
||||
int tok;
|
||||
} YYSTYPE;
|
||||
|
69
gram.y
69
gram.y
@ -122,6 +122,7 @@ yyerror(s)
|
||||
struct privilege *privilege;
|
||||
struct sudo_command command;
|
||||
struct cmndtag tag;
|
||||
struct selinux_info seinfo;
|
||||
char *string;
|
||||
int tok;
|
||||
}
|
||||
@ -154,6 +155,8 @@ yyerror(s)
|
||||
%token <tok> ':' '=' ',' '!' '+' '-' /* union member tokens */
|
||||
%token <tok> '(' ')' /* runas tokens */
|
||||
%token <tok> ERROR
|
||||
%token <tok> TYPE /* SELinux type */
|
||||
%token <tok> ROLE /* SELinux role */
|
||||
|
||||
%type <cmndspec> cmndspec
|
||||
%type <cmndspec> cmndspeclist
|
||||
@ -176,6 +179,9 @@ yyerror(s)
|
||||
%type <privilege> privilege
|
||||
%type <privilege> privileges
|
||||
%type <tag> cmndtag
|
||||
%type <seinfo> selinux
|
||||
%type <string> rolespec
|
||||
%type <string> typespec
|
||||
|
||||
%%
|
||||
|
||||
@ -296,6 +302,13 @@ host : ALIAS {
|
||||
cmndspeclist : cmndspec
|
||||
| cmndspeclist ',' cmndspec {
|
||||
list_append($1, $3);
|
||||
#ifdef HAVE_SELINUX
|
||||
/* propagate role and type */
|
||||
if ($3->role == NULL)
|
||||
$3->role = $3->prev->role;
|
||||
if ($3->type == NULL)
|
||||
$3->type = $3->prev->type;
|
||||
#endif /* HAVE_SELINUX */
|
||||
/* propagate tags and runas list */
|
||||
if ($3->tags.nopasswd == UNSPEC)
|
||||
$3->tags.nopasswd = $3->prev->tags.nopasswd;
|
||||
@ -315,7 +328,7 @@ cmndspeclist : cmndspec
|
||||
}
|
||||
;
|
||||
|
||||
cmndspec : runasspec cmndtag opcmnd {
|
||||
cmndspec : runasspec selinux cmndtag opcmnd {
|
||||
struct cmndspec *cs = emalloc(sizeof(*cs));
|
||||
if ($1 != NULL) {
|
||||
list2tq(&cs->runasuserlist, $1->runasusers);
|
||||
@ -325,8 +338,12 @@ cmndspec : runasspec cmndtag opcmnd {
|
||||
tq_init(&cs->runasuserlist);
|
||||
tq_init(&cs->runasgrouplist);
|
||||
}
|
||||
cs->tags = $2;
|
||||
cs->cmnd = $3;
|
||||
#ifdef HAVE_SELINUX
|
||||
cs->role = $2.role;
|
||||
cs->type = $2.type;
|
||||
#endif
|
||||
cs->tags = $3;
|
||||
cs->cmnd = $4;
|
||||
cs->prev = cs;
|
||||
cs->next = NULL;
|
||||
/* sudo "ALL" implies the SETENV tag */
|
||||
@ -347,6 +364,38 @@ opcmnd : cmnd {
|
||||
}
|
||||
;
|
||||
|
||||
rolespec : ROLE '=' WORD {
|
||||
$$ = $3;
|
||||
}
|
||||
;
|
||||
|
||||
typespec : TYPE '=' WORD {
|
||||
$$ = $3;
|
||||
}
|
||||
;
|
||||
|
||||
selinux : /* empty */ {
|
||||
$$.role = NULL;
|
||||
$$.type = NULL;
|
||||
}
|
||||
| rolespec {
|
||||
$$.role = $1;
|
||||
$$.type = NULL;
|
||||
}
|
||||
| typespec {
|
||||
$$.type = $1;
|
||||
$$.role = NULL;
|
||||
}
|
||||
| rolespec typespec {
|
||||
$$.role = $1;
|
||||
$$.type = $2;
|
||||
}
|
||||
| typespec rolespec {
|
||||
$$.type = $1;
|
||||
$$.role = $2;
|
||||
}
|
||||
;
|
||||
|
||||
runasspec : /* empty */ {
|
||||
$$ = NULL;
|
||||
}
|
||||
@ -638,12 +687,26 @@ init_parser(path, quiet)
|
||||
}
|
||||
while ((priv = tq_pop(&us->privileges)) != NULL) {
|
||||
struct member *runasuser = NULL, *runasgroup = NULL;
|
||||
#ifdef HAVE_SELINUX
|
||||
char *role = NULL, *type = NULL;
|
||||
#endif /* HAVE_SELINUX */
|
||||
|
||||
while ((m = tq_pop(&priv->hostlist)) != NULL) {
|
||||
efree(m->name);
|
||||
efree(m);
|
||||
}
|
||||
while ((cs = tq_pop(&priv->cmndlist)) != NULL) {
|
||||
#ifdef HAVE_SELINUX
|
||||
/* Only free the first instance of a role/type. */
|
||||
if (cs->role != role) {
|
||||
role = cs->role;
|
||||
efree(cs->role);
|
||||
}
|
||||
if (cs->type != type) {
|
||||
type = cs->type;
|
||||
efree(cs->type);
|
||||
}
|
||||
#endif /* HAVE_SELINUX */
|
||||
if (tq_last(&cs->runasuserlist) != runasuser) {
|
||||
runasuser = tq_last(&cs->runasuserlist);
|
||||
while ((m = tq_pop(&cs->runasuserlist)) != NULL) {
|
||||
|
7
ldap.c
7
ldap.c
@ -1844,6 +1844,13 @@ sudo_ldap_lookup(nss, ret, pwflag)
|
||||
if (setenv_implied)
|
||||
def_setenv = TRUE;
|
||||
sudo_ldap_parse_options(ld, entry);
|
||||
#ifdef HAVE_SELINUX
|
||||
/* Set role and type if not specified on command line. */
|
||||
if (user_role == NULL)
|
||||
user_role = def_role;
|
||||
if (user_type == NULL)
|
||||
user_type = def_type;
|
||||
#endif /* HAVE_SELINUX */
|
||||
/* make sure we don't reenter loop */
|
||||
SET(ret, VALIDATE_OK);
|
||||
CLR(ret, VALIDATE_NOT_OK);
|
||||
|
13
parse.c
13
parse.c
@ -274,6 +274,13 @@ sudo_file_lookup(nss, validated, pwflag)
|
||||
if (cmnd_match != UNSPEC) {
|
||||
match = cmnd_match;
|
||||
tags = &cs->tags;
|
||||
#ifdef HAVE_SELINUX
|
||||
/* Set role and type if not specified on command line. */
|
||||
if (user_role == NULL)
|
||||
user_role = cs->role ? estrdup(cs->role) : def_role;
|
||||
if (user_type == NULL)
|
||||
user_type = cs->type ? estrdup(cs->type) : def_type;
|
||||
#endif /* HAVE_SELINUX */
|
||||
goto matched2;
|
||||
}
|
||||
}
|
||||
@ -311,6 +318,12 @@ sudo_file_append_cmnd(cs, tags, lbuf)
|
||||
{
|
||||
struct member *m;
|
||||
|
||||
#ifdef HAVE_SELINUX
|
||||
if (cs->role)
|
||||
lbuf_append(lbuf, "ROLE=", cs->role, " ", NULL);
|
||||
if (cs->type)
|
||||
lbuf_append(lbuf, "TYPE=", cs->type, " ", NULL);
|
||||
#endif /* HAVE_SELINUX */
|
||||
if (TAG_CHANGED(setenv)) {
|
||||
lbuf_append(lbuf, cs->tags.setenv ? "SETENV: " :
|
||||
"NOSETENV: ", NULL);
|
||||
|
12
parse.h
12
parse.h
@ -48,6 +48,15 @@ struct cmndtag {
|
||||
char extra;
|
||||
};
|
||||
|
||||
/*
|
||||
* SELinux-specific container struct.
|
||||
* Currently just contains a role and type.
|
||||
*/
|
||||
struct selinux_info {
|
||||
char *role;
|
||||
char *type;
|
||||
};
|
||||
|
||||
/*
|
||||
* The parses sudoers file is stored as a collection of linked lists,
|
||||
* modelled after the yacc grammar.
|
||||
@ -100,6 +109,9 @@ struct cmndspec {
|
||||
struct member_list runasgrouplist; /* list of runas groups */
|
||||
struct member *cmnd; /* command to allow/deny */
|
||||
struct cmndtag tags; /* tag specificaion */
|
||||
#ifdef HAVE_SELINUX
|
||||
char *role, *type; /* SELinux role and type */
|
||||
#endif
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -108,6 +108,10 @@
|
||||
#define _PATH_USRTMP "/usr/tmp/"
|
||||
#endif /* _PATH_USRTMP */
|
||||
|
||||
#ifndef _PATH_SUDO_SESH
|
||||
#undef _PATH_SUDO_SESH
|
||||
#endif /* _PATH_SUDO_SESH */
|
||||
|
||||
#ifndef _PATH_LDAP_CONF
|
||||
#undef _PATH_LDAP_CONF
|
||||
#endif /* _PATH_LDAP_CONF */
|
||||
|
332
selinux.c
Normal file
332
selinux.c
Normal file
@ -0,0 +1,332 @@
|
||||
/*
|
||||
* Copyright (c) 2008 Dan Walsh <dwalsh@redhat.com>
|
||||
*
|
||||
* Borrowed heavily from newrole source code
|
||||
* Authors:
|
||||
* Anthony Colatrella
|
||||
* Tim Fraser
|
||||
* Steve Grubb <sgrubb@redhat.com>
|
||||
* Darrel Goeddel <DGoeddel@trustedcs.com>
|
||||
* Michael Thompson <mcthomps@us.ibm.com>
|
||||
* Dan Walsh <dwalsh@redhat.com>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/wait.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <signal.h>
|
||||
#ifdef WITH_AUDIT
|
||||
#include <libaudit.h>
|
||||
#endif
|
||||
|
||||
#include <selinux/flask.h> /* for SECCLASS_CHR_FILE */
|
||||
#include <selinux/selinux.h> /* for is_selinux_enabled() */
|
||||
#include <selinux/context.h> /* for context-mangling functions */
|
||||
#include <selinux/get_default_type.h>
|
||||
#include <selinux/get_context_list.h>
|
||||
|
||||
#include "sudo.h"
|
||||
#include "pathnames.h"
|
||||
|
||||
/*
|
||||
* This function attempts to revert the relabeling done to the tty.
|
||||
* fd - referencing the opened ttyn
|
||||
* ttyn - name of tty to restore
|
||||
* tty_context - original context of the tty
|
||||
* new_tty_context - context tty was relabeled to
|
||||
*
|
||||
* Returns zero on success, non-zero otherwise
|
||||
*/
|
||||
static int
|
||||
restore_tty_label(int fd, const char *ttyn, security_context_t tty_context,
|
||||
security_context_t new_tty_context)
|
||||
{
|
||||
int rc = 0;
|
||||
security_context_t chk_tty_context = NULL;
|
||||
|
||||
if (!ttyn)
|
||||
goto skip_relabel;
|
||||
|
||||
if (!new_tty_context)
|
||||
goto skip_relabel;
|
||||
|
||||
/* Verify that the tty still has the context set by newrole. */
|
||||
if ((rc = fgetfilecon(fd, &chk_tty_context)) < 0) {
|
||||
warning("unable to fgetfilecon %s", ttyn);
|
||||
goto skip_relabel;
|
||||
}
|
||||
|
||||
if ((rc = strcmp(chk_tty_context, new_tty_context))) {
|
||||
warningx("%s changed labels.", ttyn);
|
||||
goto skip_relabel;
|
||||
}
|
||||
|
||||
if ((rc = fsetfilecon(fd, tty_context)) < 0)
|
||||
warning("unable to restore context for %s", ttyn);
|
||||
|
||||
skip_relabel:
|
||||
freecon(chk_tty_context);
|
||||
return(rc);
|
||||
}
|
||||
|
||||
/*
|
||||
* This function attempts to relabel the tty. If this function fails, then
|
||||
* the fd is closed, the contexts are free'd and -1 is returned. On success,
|
||||
* a valid fd is returned and tty_context and new_tty_context are set.
|
||||
*
|
||||
* This function will not fail if it can not relabel the tty when selinux is
|
||||
* in permissive mode.
|
||||
*/
|
||||
static int
|
||||
relabel_tty(const char *ttyn, security_context_t new_context,
|
||||
security_context_t * tty_context, security_context_t * new_tty_context)
|
||||
{
|
||||
int fd;
|
||||
int enforcing = security_getenforce();
|
||||
security_context_t tty_con = NULL;
|
||||
security_context_t new_tty_con = NULL;
|
||||
|
||||
if (!ttyn)
|
||||
return(0);
|
||||
|
||||
if (enforcing < 0) {
|
||||
warningx("unable to determine enforcing mode.");
|
||||
return(-1);
|
||||
}
|
||||
|
||||
/* Re-open TTY descriptor */
|
||||
fd = open(ttyn, O_RDWR | O_NONBLOCK);
|
||||
if (fd == -1) {
|
||||
warning("unable to open %s", ttyn);
|
||||
return(-1);
|
||||
}
|
||||
(void)fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) & ~O_NONBLOCK);
|
||||
|
||||
if (fgetfilecon(fd, &tty_con) < 0) {
|
||||
warning("unable to get current context for %s, not relabeling tty",
|
||||
ttyn);
|
||||
if (enforcing)
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (tty_con && (security_compute_relabel(new_context, tty_con,
|
||||
SECCLASS_CHR_FILE, &new_tty_con) < 0)) {
|
||||
warning("unable to get new context for %s, not relabeling tty", ttyn);
|
||||
if (enforcing)
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (new_tty_con != NULL) {
|
||||
if (fsetfilecon(fd, new_tty_con) < 0) {
|
||||
warning("unable to set new context for %s", ttyn);
|
||||
if (enforcing)
|
||||
goto error;
|
||||
}
|
||||
freecon(new_tty_con);
|
||||
new_tty_con = NULL;
|
||||
}
|
||||
|
||||
*tty_context = tty_con;
|
||||
*new_tty_context = new_tty_con;
|
||||
return(fd);
|
||||
|
||||
error:
|
||||
freecon(tty_con);
|
||||
close(fd);
|
||||
return(-1);
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns a new security context based on the old context and the
|
||||
* specified role and type.
|
||||
*/
|
||||
security_context_t
|
||||
get_exec_context(security_context_t old_context, char *role, char *type)
|
||||
{
|
||||
security_context_t new_context = NULL;
|
||||
context_t context = NULL;
|
||||
char *typebuf = NULL;
|
||||
|
||||
/* We must have a role, the type is optional (we can use the default). */
|
||||
if (!role) {
|
||||
warningx("you must specify a role.");
|
||||
return(NULL);
|
||||
}
|
||||
if (!type) {
|
||||
if (get_default_type(role, &typebuf)) {
|
||||
warningx("unable to get default type");
|
||||
return(NULL);
|
||||
}
|
||||
type = typebuf;
|
||||
}
|
||||
|
||||
/*
|
||||
* Expand old_context into a context_t so that we extract and modify
|
||||
* its components easily.
|
||||
*/
|
||||
context = context_new(old_context);
|
||||
|
||||
/*
|
||||
* Replace the role and type in "context" with the role and
|
||||
* type we will be running the command as.
|
||||
*/
|
||||
if (context_role_set(context, role)) {
|
||||
warningx("failed to set new role %s", role);
|
||||
goto error;
|
||||
}
|
||||
if (context_type_set(context, type)) {
|
||||
warningx("failed to set new type %s", type);
|
||||
goto error;
|
||||
}
|
||||
|
||||
/*
|
||||
* Convert "context" back into a string and verify it.
|
||||
*/
|
||||
new_context = estrdup(context_str(context));
|
||||
if (security_check_context(new_context) < 0) {
|
||||
warningx("%s is not a valid context", new_context);
|
||||
goto error;
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
warningx("Your new context is %s", new_context);
|
||||
#endif
|
||||
|
||||
context_free(context);
|
||||
return(new_context);
|
||||
|
||||
error:
|
||||
free(typebuf);
|
||||
context_free(context);
|
||||
freecon(new_context);
|
||||
return(NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
* If the program is being run with a different security context we
|
||||
* need to go through an intermediary process for the transition to
|
||||
* be allowed by the policy. We use the "sesh" shell for this, which
|
||||
* will simply execute the command pass to it on the command line.
|
||||
*/
|
||||
void
|
||||
selinux_exec(char *role, char *type, char **argv, int login_shell)
|
||||
{
|
||||
security_context_t old_context = NULL;
|
||||
security_context_t new_context = NULL;
|
||||
security_context_t tty_context = NULL;
|
||||
security_context_t new_tty_context = NULL;
|
||||
pid_t childPid;
|
||||
int ttyfd;
|
||||
|
||||
/* Must have a tty. */
|
||||
if (user_ttypath == NULL || *user_ttypath == '\0')
|
||||
error(EXIT_FAILURE, "unable to determine tty");
|
||||
|
||||
/* Store the caller's SID in old_context. */
|
||||
if (getprevcon(&old_context))
|
||||
error(EXIT_FAILURE, "failed to get old_context");
|
||||
|
||||
#ifdef DEBUG
|
||||
warningx("your old context was %s", old_context);
|
||||
#endif
|
||||
new_context = get_exec_context(old_context, role, type);
|
||||
if (!new_context)
|
||||
exit(EXIT_FAILURE);
|
||||
|
||||
ttyfd = relabel_tty(user_ttypath, new_context, &tty_context,
|
||||
&new_tty_context);
|
||||
if (ttyfd < 0)
|
||||
error(EXIT_FAILURE, "unable to setup tty context for %s", new_context);
|
||||
|
||||
#ifdef DEBUG
|
||||
warningx("your old tty context is %s", tty_context);
|
||||
warningx("your new tty context is %s", new_tty_context);
|
||||
#endif
|
||||
|
||||
childPid = fork();
|
||||
if (childPid < 0) {
|
||||
/* fork failed, no child to worry about */
|
||||
warning("unable to fork");
|
||||
if (restore_tty_label(ttyfd, user_ttypath, tty_context, new_tty_context))
|
||||
warningx("unable to restore tty label");
|
||||
exit(EXIT_FAILURE);
|
||||
} else if (childPid) {
|
||||
pid_t pid;
|
||||
int status;
|
||||
|
||||
/* Parent, wait for child to finish. */
|
||||
do {
|
||||
pid = waitpid(childPid, &status, 0);
|
||||
} while (pid == -1 && errno == EINTR);
|
||||
|
||||
if (pid == -1)
|
||||
error(EXIT_FAILURE, "waitpid");
|
||||
|
||||
if (restore_tty_label(ttyfd, user_ttypath, tty_context, new_tty_context))
|
||||
errorx(EXIT_FAILURE, "unable to restore tty label");
|
||||
|
||||
/* Preserve child exit status. */
|
||||
if (WIFEXITED(status))
|
||||
exit(WEXITSTATUS(status));
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
/* Child */
|
||||
/* Close the tty and reopen descriptors 0 through 2 */
|
||||
if (close(ttyfd) || close(STDIN_FILENO) || close(STDOUT_FILENO) ||
|
||||
close(STDERR_FILENO)) {
|
||||
warning("could not close descriptors");
|
||||
goto error;
|
||||
}
|
||||
ttyfd = open(user_ttypath, O_RDONLY | O_NONBLOCK);
|
||||
if (ttyfd != STDIN_FILENO)
|
||||
goto error;
|
||||
fcntl(ttyfd, F_SETFL, fcntl(ttyfd, F_GETFL, 0) & ~O_NONBLOCK);
|
||||
ttyfd = open(user_ttypath, O_RDWR | O_NONBLOCK);
|
||||
if (ttyfd != STDOUT_FILENO)
|
||||
goto error;
|
||||
fcntl(ttyfd, F_SETFL, fcntl(ttyfd, F_GETFL, 0) & ~O_NONBLOCK);
|
||||
ttyfd = dup(STDOUT_FILENO);
|
||||
if (ttyfd != STDERR_FILENO)
|
||||
goto error;
|
||||
|
||||
if (setexeccon(new_context)) {
|
||||
warning("unable to set exec context to %s", new_context);
|
||||
goto error;
|
||||
}
|
||||
|
||||
#ifdef WITH_AUDIT
|
||||
if (send_audit_message(1, old_context, new_context, user_ttypath))
|
||||
goto error;
|
||||
#endif
|
||||
|
||||
/* We use the "spare" slot in argv to store sesh. */
|
||||
--argv;
|
||||
argv[0] = login_shell ? "-sesh" : "sesh";
|
||||
argv[1] = safe_cmnd;
|
||||
|
||||
execv(_PATH_SUDO_SESH, argv);
|
||||
warning("%s", safe_cmnd);
|
||||
|
||||
error:
|
||||
_exit(EXIT_FAILURE);
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user