2
0
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:
Todd C. Miller 2008-02-09 14:30:06 +00:00
parent 5d20923c2f
commit f2b70188b6
14 changed files with 858 additions and 277 deletions

View File

@ -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)

View File

@ -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

View File

@ -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.])

View File

@ -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
}

View File

@ -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,

View File

@ -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"

630
gram.c

File diff suppressed because it is too large Load Diff

3
gram.h
View File

@ -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
View File

@ -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
View File

@ -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
View File

@ -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
View File

@ -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
};
/*

View File

@ -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
View 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);
}