]> granicus.if.org Git - sudo/commitdiff
Add support for SELinux RBAC. Sudoers entries may specify a role and type.
authorTodd C. Miller <Todd.Miller@courtesan.com>
Sat, 9 Feb 2008 14:30:06 +0000 (14:30 +0000)
committerTodd C. Miller <Todd.Miller@courtesan.com>
Sat, 9 Feb 2008 14:30:06 +0000 (14:30 +0000)
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.

14 files changed:
Makefile.in
config.h.in
configure.in
def_data.c
def_data.h
def_data.in
gram.c
gram.h
gram.y
ldap.c
parse.c
parse.h
pathnames.h.in
selinux.c [new file with mode: 0644]

index 161398c4f0f3ac368d3b60ad66a68dcfde000f52..84c70571d224d449f4cbf0e1d5d089fedd5f4b81 100644 (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)
index 7d9d93955580c1156931724106479e2ab6607a86..00d91cbd5a38164f090ddf6898e3cc051211e7e9 100644 (file)
 /* 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
 
index e64526d5e20f75f1cd587bd047a112e40f8dccb9..50428f785a1371cc2d970f9921064b759c04a757 100644 (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.])
index a491266e1a78ed365df1f129c4dd4a5e046dcc52..96a2b20a46a843d4af7df829e7758da58d30ed9f 100644 (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
     }
index 6d636360e7d5969c9395993d65c5458e9102015d..5f7c39ff0d77aeafcbaa362959933b2b6a21cf54 100644 (file)
 #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,
index 83b6433713d5bf8650b1bff0a4590f942e573982..4a3c46a4c60c2869269c171a40e4af68a3525178 100644 (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"
diff --git a/gram.c b/gram.c
index 6fc79ff2ce8ebce4a7b2a1c56bd552c7fb6a805c..ced9a126871e550b3a7c9740608b0451fe17b071 100644 (file)
--- a/gram.c
+++ b/gram.c
@@ -141,11 +141,12 @@ typedef union {
     struct privilege *privilege;
     struct sudo_command command;
     struct cmndtag tag;
+    struct selinux_info seinfo;
     char *string;
     int tok;
 } YYSTYPE;
 #endif /* YYSTYPE_DEFINED */
-#line 149 "y.tab.c"
+#line 150 "y.tab.c"
 #define COMMAND 257
 #define ALIAS 258
 #define DEFVAR 259
@@ -171,6 +172,8 @@ typedef union {
 #define USERALIAS 279
 #define RUNASALIAS 280
 #define ERROR 281
+#define TYPE 282
+#define ROLE 283
 #define YYERRCODE 256
 #if defined(__cplusplus) || defined(__STDC__)
 const short yylhs[] =
@@ -178,15 +181,16 @@ const short yylhs[] =
 short yylhs[] =
 #endif
        {                                        -1,
-    0,    0,   22,   22,   23,   23,   23,   23,   23,   23,
-   23,   23,   23,   23,   23,   23,    4,    4,    3,    3,
+    0,    0,   25,   25,   26,   26,   26,   26,   26,   26,
+   26,   26,   26,   26,   26,   26,    4,    4,    3,    3,
     3,    3,    3,   20,   20,   19,   10,   10,    8,    8,
-    8,    8,    8,    2,    2,    1,    6,    6,   17,   17,
-   18,   18,   18,   21,   21,   21,   21,   21,   21,   21,
-    5,    5,    5,   25,   25,   28,    9,    9,   26,   26,
-   29,    7,    7,   27,   27,   30,   24,   24,   31,   13,
-   13,   11,   11,   12,   12,   12,   12,   12,   16,   16,
-   14,   14,   15,   15,   15,
+    8,    8,    8,    2,    2,    1,    6,    6,   23,   24,
+   22,   22,   22,   22,   22,   17,   17,   18,   18,   18,
+   21,   21,   21,   21,   21,   21,   21,    5,    5,    5,
+   28,   28,   31,    9,    9,   29,   29,   32,    7,    7,
+   30,   30,   33,   27,   27,   34,   13,   13,   11,   11,
+   12,   12,   12,   12,   12,   16,   16,   14,   14,   15,
+   15,   15,
 };
 #if defined(__cplusplus) || defined(__STDC__)
 const short yylen[] =
@@ -197,12 +201,13 @@ short yylen[] =
     0,    1,    1,    2,    1,    2,    2,    2,    2,    2,
     2,    2,    3,    3,    3,    3,    1,    3,    1,    2,
     3,    3,    3,    1,    3,    3,    1,    2,    1,    1,
-    1,    1,    1,    1,    3,    3,    1,    2,    0,    3,
-    1,    3,    2,    0,    2,    2,    2,    2,    2,    2,
-    1,    1,    1,    1,    3,    3,    1,    3,    1,    3,
-    3,    1,    3,    1,    3,    3,    1,    3,    3,    1,
-    3,    1,    2,    1,    1,    1,    1,    1,    1,    3,
-    1,    2,    1,    1,    1,
+    1,    1,    1,    1,    3,    4,    1,    2,    3,    3,
+    0,    1,    1,    2,    2,    0,    3,    1,    3,    2,
+    0,    2,    2,    2,    2,    2,    2,    1,    1,    1,
+    1,    3,    3,    1,    3,    1,    3,    3,    1,    3,
+    1,    3,    3,    1,    3,    3,    1,    3,    1,    2,
+    1,    1,    1,    1,    1,    1,    3,    1,    2,    1,
+    1,    1,
 };
 #if defined(__cplusplus) || defined(__STDC__)
 const short yydefred[] =
@@ -210,20 +215,21 @@ const short yydefred[] =
 short yydefred[] =
 #endif
        {                                      0,
-    0,   74,   76,   77,   78,    0,    0,    0,    0,    0,
-   75,    5,    0,    0,    0,    0,    0,    0,   70,   72,
+    0,   81,   83,   84,   85,    0,    0,    0,    0,    0,
+   82,    5,    0,    0,    0,    0,    0,    0,   77,   79,
     0,    0,    3,    6,    0,    0,   17,    0,   29,   32,
-   31,   33,   30,    0,   27,    0,   57,    0,    0,   53,
-   52,   51,    0,   37,   62,    0,    0,    0,   54,    0,
-    0,   59,    0,    0,   67,    0,    0,   64,   73,    0,
+   31,   33,   30,    0,   27,    0,   64,    0,    0,   60,
+   59,   58,    0,   37,   69,    0,    0,    0,   61,    0,
+    0,   66,    0,    0,   74,    0,    0,   71,   80,    0,
     0,   24,    0,    4,    0,    0,    0,   20,    0,   28,
     0,    0,    0,    0,   38,    0,    0,    0,    0,    0,
-    0,    0,    0,    0,    0,   71,    0,    0,   21,   22,
-   23,   18,   58,   63,    0,   55,    0,   60,    0,   68,
-    0,   65,    0,   34,    0,   44,   25,    0,    0,    0,
-    0,    0,   83,   85,   84,    0,   79,   81,    0,    0,
-   40,   35,   45,   46,   47,   48,   49,   50,   36,   82,
-    0,    0,   80,
+    0,    0,    0,    0,    0,   78,    0,    0,   21,   22,
+   23,   18,   65,   70,    0,   62,    0,   67,    0,   75,
+    0,   72,    0,   34,    0,    0,   25,    0,    0,    0,
+    0,    0,    0,   51,    0,    0,   90,   92,   91,    0,
+   86,   88,    0,    0,   47,   35,    0,    0,    0,   44,
+   45,   89,    0,    0,   40,   39,   52,   53,   54,   55,
+   56,   57,   36,   87,
 };
 #if defined(__cplusplus) || defined(__STDC__)
 const short yydgoto[] =
@@ -232,123 +238,125 @@ short yydgoto[] =
 #endif
        {                                      18,
   104,  105,   27,   28,   44,   45,   46,   35,   61,   37,
-   19,   20,   21,  117,  118,  119,  106,  110,   62,   63,
-  112,   22,   23,   54,   48,   51,   57,   49,   52,   58,
-   55,
+   19,   20,   21,  121,  122,  123,  106,  110,   62,   63,
+  129,  114,  115,  116,   22,   23,   54,   48,   51,   57,
+   49,   52,   58,   55,
 };
 #if defined(__cplusplus) || defined(__STDC__)
 const short yysindex[] =
 #else
 short yysindex[] =
 #endif
-       {                                    -33,
- -269,    0,    0,    0,    0,   -8,  454,  458,  458,  -17,
-    0,    0, -249, -247, -241, -231, -243,    0,    0,    0,
-  141,  -33,    0,    0,  -37, -216,    0,  -16,    0,    0,
-    0,    0,    0, -221,    0,  -23,    0,  -21,  -21,    0,
-    0,    0, -244,    0,    0,  -11,  -14,   -1,    0,   -6,
-    2,    0,    3,    4,    0,    5,    7,    0,    0,  458,
-  -15,    0,    9,    0, -219, -207, -202,    0,   -8,    0,
-  454,  -16,  -16,  -16,    0,  -17,  -16,  454, -249,  -17,
- -247,  458, -241,  458, -231,    0,   23,  454,    0,    0,
-    0,    0,    0,    0,   24,    0,   25,    0,   27,    0,
-   27,    0,  217,    0,   28,    0,    0,   -3,   -9,   29,
-   23,  250,    0,    0,    0, -222,    0,    0,   30,   -3,
-    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
-   -3,   30,    0,};
+       {                                    405,
+ -266,    0,    0,    0,    0,   -9,  463,  510,  510,   -2,
+    0,    0, -243, -218, -215, -211, -225,    0,    0,    0,
+  -28,  405,    0,    0,  -36, -210,    0,    4,    0,    0,
+    0,    0,    0, -231,    0,  -33,    0,  -25,  -25,    0,
+    0,    0, -240,    0,    0,  -21,   -6,   -1,    0,    2,
+    6,    0,    7,    8,    0,    9,   11,    0,    0,  510,
+  -22,    0,   13,    0, -203, -201, -198,    0,   -9,    0,
+  463,    4,    4,    4,    0,   -2,    4,  463, -243,   -2,
+ -218,  510, -215,  510, -211,    0,   27,  463,    0,    0,
+    0,    0,    0,    0,   28,    0,   30,    0,   31,    0,
+   31,    0,  141,    0,   32, -262,    0,  -27,  -16,   36,
+   27,   18,   19,    0, -200, -202,    0,    0,    0, -217,
+    0,    0,   39,  -27,    0,    0, -177, -175,  250,    0,
+    0,    0,  -27,   39,    0,    0,    0,    0,    0,    0,
+    0,    0,    0,    0,};
 #if defined(__cplusplus) || defined(__STDC__)
 const short yyrindex[] =
 #else
 short yyrindex[] =
 #endif
-       {                                     73,
+       {                                     90,
     0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
     0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
-    0,   75,    0,    0,    1,    0,    0,  156,    0,    0,
+    0,   91,    0,    0,    1,    0,    0,  156,    0,    0,
     0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
     0,    0,    0,    0,    0,    0,    0,  181,    0,    0,
   206,    0,    0,  237,    0,    0,  274,    0,    0,    0,
     0,    0,  300,    0,    0,    0,    0,    0,    0,    0,
     0,  326,  352,  378,    0,    0,  430,    0,    0,    0,
-    0,    0,    0,    0,    0,    0,  392,    0,    0,    0,
+    0,    0,    0,    0,    0,    0,  -29,    0,    0,    0,
     0,    0,    0,    0,   26,    0,   52,    0,   78,    0,
-  104,    0,    0,    0,  130,    0,    0,    0,   39,    0,
-  392,    0,    0,    0,    0,    0,    0,    0,   40,    0,
-    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
-    0,   41,    0,};
+  104,    0,    0,    0,  130,  442,    0,    0,   51,    0,
+  -29,    0,    0,    0,  461,  485,    0,    0,    0,    0,
+    0,    0,   53,    0,    0,    0,    0,    0,    0,    0,
+    0,    0,    0,   54,    0,    0,    0,    0,    0,    0,
+    0,    0,    0,    0,};
 #if defined(__cplusplus) || defined(__STDC__)
 const short yygindex[] =
 #else
 short yygindex[] =
 #endif
        {                                      0,
-  -28,    0,   17,   12,   44,  -74,    8,   55,   -2,   19,
-   31,   76,   -5,  -39,  -22,  -25,    0,    0,   11,    0,
-    0,    0,   74,    0,    0,    0,    0,   18,   20,   15,
-   22,
+  -18,    0,   29,   15,   56,  -73,   16,   63,   -5,   34,
+   40,   84,    5,  -31,  -17,  -15,    0,    0,   24,    0,
+    0,    0,  -10,   -8,    0,   92,    0,    0,    0,    0,
+   37,   38,   33,   41,
 };
-#define YYTABLESIZE 733
+#define YYTABLESIZE 785
 #if defined(__cplusplus) || defined(__STDC__)
 const short yytable[] =
 #else
 short yytable[] =
 #endif
-       {                                      17,
-   19,   94,   38,   39,   36,   66,   24,   67,   47,   26,
-   50,   26,   40,   41,    2,   43,   53,    3,    4,    5,
-   71,   26,   60,   65,   26,   56,   56,   69,   71,  116,
-   42,   11,   76,   19,   60,  113,   29,  129,   30,   31,
-  114,   32,   68,   89,   19,   87,   78,   72,  120,   73,
-   74,   61,  115,   33,   80,   90,   79,   77,   56,   81,
-   91,   83,  103,   82,   85,   84,   88,   71,   76,  121,
-   60,  111,    1,  131,    2,   95,   99,   69,  101,   41,
-   43,   42,  122,   56,   61,   92,   75,   97,   70,   93,
-   86,  133,   59,  130,  132,   64,   96,  109,  107,  102,
-   98,    0,    0,   66,  100,    0,    0,    0,    0,   61,
-   69,    0,    0,    0,    0,    0,    0,    0,    0,    0,
-    0,    0,    0,    0,    0,    0,    0,    0,    0,   26,
-    0,    0,    0,    0,    0,   69,   66,    0,    0,    0,
+       {                                      26,
+   19,   36,   94,   46,   34,  120,   66,   26,   67,   24,
+   71,   26,   38,   39,   47,   60,   40,   41,   60,  112,
+  113,   71,   76,   26,   65,   63,   29,   60,   30,   31,
+   43,   32,    2,   19,   42,    3,    4,    5,   87,   50,
+  117,  124,   53,   33,   19,  118,   56,   69,   68,   11,
+   72,   68,   73,   74,   78,  143,   79,  119,   63,   89,
+   77,   90,   80,   81,   91,   83,  103,   82,   85,   84,
+   88,   71,   95,   76,   60,  111,  125,   76,  127,  128,
+  113,  112,  133,   63,   68,  135,   99,  136,  101,    1,
+    2,   48,  126,   50,   49,   97,   70,   92,   75,   86,
+   59,  144,  132,   73,   93,  131,  130,  109,  134,   68,
+   76,  107,    0,   64,    0,   96,    0,  102,   98,    0,
+    0,    0,    0,  100,    0,    0,    0,    0,    0,   26,
+    0,    0,    0,    0,    0,   76,   73,    0,    0,    0,
     0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
     0,    0,    0,    0,    0,   12,    0,    0,    0,    0,
-    0,   66,   26,    0,    0,    0,    0,    0,    0,    0,
-    0,    0,    0,   34,    0,    0,    0,    0,    0,    0,
-    9,    0,    0,    0,   60,    0,    0,   26,   12,    0,
-    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
+    0,   73,   26,    0,    0,    0,    0,    0,    0,    0,
+    0,    0,    0,   17,    0,    0,    0,    0,    0,    0,
+    9,    0,    0,    0,    0,    0,    0,   26,   12,    0,
+    0,    0,    0,    0,    0,    0,    0,    0,  108,    0,
     0,    0,    0,    0,    0,   10,    0,    0,    0,    0,
     0,    0,    0,    9,    0,    0,    0,    0,    0,    0,
-    0,    0,    1,    0,    2,    0,    0,    3,    4,    5,
-    6,    7,    8,    9,   10,   25,    8,   25,   10,   40,
-   41,   11,   12,   13,   14,   15,   16,   25,    0,   17,
-   25,    0,    0,    0,  113,    0,   19,   42,   19,  114,
+    0,    0,    0,    0,    0,   25,    0,   46,   46,   29,
+  117,   30,   31,   25,   32,  118,    8,   25,   10,   46,
+   46,   46,   46,   46,   46,   46,   33,  119,    0,   25,
+    0,    0,   46,   46,   40,   41,   19,    0,   19,    0,
     0,   19,   19,   19,   19,   19,   19,   19,   19,    8,
-    0,  115,    0,   11,  108,   19,   19,   19,   19,   19,
-   19,   56,   43,   56,    0,    0,   56,   56,   56,   56,
-   56,   56,   56,   56,    0,    0,    0,    0,    0,    7,
-   56,   56,   56,   56,   56,   56,   11,   61,    0,   61,
-    0,    0,   61,   61,   61,   61,   61,   61,   61,   61,
-    0,    0,    0,    0,    0,   15,   61,   61,   61,   61,
-   61,   61,    7,   69,    0,   69,    0,    0,   69,   69,
-   69,   69,   69,   69,   69,   69,    0,    0,    0,    0,
-    0,   13,   69,   69,   69,   69,   69,   69,   15,   66,
-    0,   66,    0,    0,   66,   66,   66,   66,   66,   66,
-   66,   66,    0,    0,    0,    0,    0,   14,   66,   66,
-   66,   66,   66,   66,   13,   26,    0,   26,    0,    0,
-   26,   26,   26,   26,   26,   26,   26,   26,   29,    0,
-   30,   31,    0,   32,   26,   26,   26,   26,   26,   26,
-   14,   12,    0,   12,    0,   33,   12,   12,   12,   12,
-   12,   12,   12,   12,   39,    0,    0,    0,    0,   16,
-   12,   12,   12,   12,   12,   12,    9,    0,    9,    0,
+    0,    0,   42,   11,    0,   19,   19,   19,   19,   19,
+   19,   63,   43,   63,    0,    0,   63,   63,   63,   63,
+   63,   63,   63,   63,    0,    0,    0,    0,    0,    7,
+   63,   63,   63,   63,   63,   63,   11,   68,    0,   68,
+    0,    0,   68,   68,   68,   68,   68,   68,   68,   68,
+    0,    0,    0,    0,    0,   15,   68,   68,   68,   68,
+   68,   68,    7,   76,    0,   76,    0,    0,   76,   76,
+   76,   76,   76,   76,   76,   76,    0,    0,    0,    0,
+    0,   13,   76,   76,   76,   76,   76,   76,   15,   73,
+    0,   73,    0,    0,   73,   73,   73,   73,   73,   73,
+   73,   73,    0,    0,    0,    0,    0,   14,   73,   73,
+   73,   73,   73,   73,   13,   26,    0,   26,    0,    0,
+   26,   26,   26,   26,   26,   26,   26,   26,    2,    0,
+    0,    3,    4,    5,   26,   26,   26,   26,   26,   26,
+   14,   12,    0,   12,    0,   11,   12,   12,   12,   12,
+   12,   12,   12,   12,    0,    0,    0,    0,    0,   16,
+   12,   12,   12,   12,   12,   12,    9,   17,    9,    0,
     0,    9,    9,    9,    9,    9,    9,    9,    9,    0,
     0,    0,    0,    0,    0,    9,    9,    9,    9,    9,
     9,   10,   16,   10,    0,    0,   10,   10,   10,   10,
-   10,   10,   10,   10,    2,    0,    0,    3,    4,    5,
-   10,   10,   10,   10,   10,   10,   34,    0,    0,    0,
-   17,   11,    8,    0,    8,    0,    0,    8,    8,    8,
+   10,   10,   10,   10,   41,    0,    0,    0,    0,    0,
+   10,   10,   10,   10,   10,   10,    0,    0,    0,    0,
+    0,    0,    8,   42,    8,   34,    0,    8,    8,    8,
     8,    8,    8,    8,    8,    0,   40,   41,    0,    0,
-    0,    8,    8,    8,    8,    8,    8,    0,  123,  124,
-  125,  126,  127,  128,   42,    0,    0,    0,    0,   11,
+    0,    8,    8,    8,    8,    8,    8,   43,  137,  138,
+  139,  140,  141,  142,   42,    0,    0,    0,    0,   11,
     0,   11,    0,    0,   11,   11,   11,   11,   11,   11,
-   11,   11,    0,    0,    0,    0,    0,    0,   11,   11,
+   11,   11,   17,    0,    0,    0,    0,    0,   11,   11,
    11,   11,   11,   11,    0,    7,    0,    7,    0,    0,
     7,    7,    7,    7,    7,    7,    7,    7,    0,    0,
     0,    0,    0,    0,    7,    7,    7,    7,    7,    7,
@@ -358,16 +366,21 @@ short yytable[] =
     0,    0,   13,   13,   13,   13,   13,   13,   13,   13,
     0,    0,    0,    0,    0,    0,   13,   13,   13,   13,
    13,   13,    0,   14,    0,   14,    0,    0,   14,   14,
-   14,   14,   14,   14,   14,   14,    0,    0,   39,   39,
+   14,   14,   14,   14,   14,   14,    0,    0,    0,    0,
     0,    0,   14,   14,   14,   14,   14,   14,    0,    0,
-   39,   39,   39,   39,   39,   39,   39,    0,    0,    0,
-    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
-    0,    0,    0,    0,    0,   16,    0,   16,    0,    0,
-   16,   16,   16,   16,   16,   16,   16,   16,    0,    0,
+    1,    0,    2,    0,    0,    3,    4,    5,    6,    7,
+    8,    9,   10,    0,    0,    0,    0,    0,    0,   11,
+   12,   13,   14,   15,   16,   16,    0,   16,    0,    0,
+   16,   16,   16,   16,   16,   16,   16,   16,   41,   41,
     0,    0,    0,    0,   16,   16,   16,   16,   16,   16,
-    0,   29,    0,   30,   31,    2,   32,    0,    3,    4,
-    5,    0,    0,    0,    0,    0,    0,    0,   33,    0,
-    0,    0,   11,
+   41,   41,   41,   41,   41,   41,   41,   42,   42,    0,
+   29,    0,   30,   31,    0,   32,    0,    0,    0,   42,
+   42,   42,   42,   42,   42,   42,    0,   33,    0,    0,
+    0,   43,   43,    0,    0,    0,    0,    0,    0,    0,
+    0,    0,    0,   43,   43,   43,   43,   43,   43,   43,
+    0,    0,    0,    0,    0,    0,    0,    2,    0,    0,
+    3,    4,    5,    0,    0,    0,    0,    0,    0,    0,
+    0,    0,    0,    0,   11,
 };
 #if defined(__cplusplus) || defined(__STDC__)
 const short yycheck[] =
@@ -375,34 +388,34 @@ const short yycheck[] =
 short yycheck[] =
 #endif
        {                                      33,
-    0,   76,    8,    9,    7,   43,  276,   45,  258,   33,
-  258,   33,  257,  258,  258,   33,  258,  261,  262,  263,
-   44,   33,   44,   61,   33,    0,  258,   44,   44,   33,
-  275,  275,   44,   33,   44,  258,  258,  112,  260,  261,
-  263,  263,  259,  263,   44,   61,   61,   36,   58,   38,
-   39,    0,  275,  275,   61,  263,   58,   46,   33,   58,
-  263,   58,   40,   61,   58,   61,   58,   44,   44,   41,
-   44,   44,    0,   44,    0,   78,   82,    0,   84,   41,
-   41,   41,  111,   58,   33,   69,   43,   80,   34,   71,
-   60,  131,   17,  116,  120,   22,   79,  103,   88,   85,
-   81,   -1,   -1,    0,   83,   -1,   -1,   -1,   -1,   58,
-   33,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,    0,
+    0,    7,   76,   33,   33,   33,   43,   33,   45,  276,
+   44,   33,    8,    9,  258,   44,  257,  258,   44,  282,
+  283,   44,   44,   33,   61,    0,  258,   44,  260,  261,
+   33,  263,  258,   33,  275,  261,  262,  263,   61,  258,
+  258,   58,  258,  275,   44,  263,  258,   44,  259,  275,
+   36,    0,   38,   39,   61,  129,   58,  275,   33,  263,
+   46,  263,   61,   58,  263,   58,   40,   61,   58,   61,
+   58,   44,   78,   44,   44,   44,   41,    0,   61,   61,
+  283,  282,   44,   58,   33,  263,   82,  263,   84,    0,
+    0,   41,  111,   41,   41,   80,   34,   69,   43,   60,
+   17,  133,  120,    0,   71,  116,  115,  103,  124,   58,
+   33,   88,   -1,   22,   -1,   79,   -1,   85,   81,   -1,
+   -1,   -1,   -1,   83,   -1,   -1,   -1,   -1,   -1,    0,
    -1,   -1,   -1,   -1,   -1,   58,   33,   -1,   -1,   -1,
    -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
    -1,   -1,   -1,   -1,   -1,    0,   -1,   -1,   -1,   -1,
    -1,   58,   33,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
    -1,   -1,   -1,   33,   -1,   -1,   -1,   -1,   -1,   -1,
-    0,   -1,   -1,   -1,   44,   -1,   -1,   58,   33,   -1,
-   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+    0,   -1,   -1,   -1,   -1,   -1,   -1,   58,   33,   -1,
+   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   58,   -1,
    -1,   -1,   -1,   -1,   -1,    0,   -1,   -1,   -1,   -1,
    -1,   -1,   -1,   33,   -1,   -1,   -1,   -1,   -1,   -1,
-   -1,   -1,  256,   -1,  258,   -1,   -1,  261,  262,  263,
-  264,  265,  266,  267,  268,  259,    0,  259,   33,  257,
-  258,  275,  276,  277,  278,  279,  280,  259,   -1,   33,
-  259,   -1,   -1,   -1,  258,   -1,  256,  275,  258,  263,
+   -1,   -1,   -1,   -1,   -1,  259,   -1,  257,  258,  258,
+  258,  260,  261,  259,  263,  263,    0,  259,   33,  269,
+  270,  271,  272,  273,  274,  275,  275,  275,   -1,  259,
+   -1,   -1,  282,  283,  257,  258,  256,   -1,  258,   -1,
    -1,  261,  262,  263,  264,  265,  266,  267,  268,   33,
-   -1,  275,   -1,    0,   58,  275,  276,  277,  278,  279,
+   -1,   -1,  275,    0,   -1,  275,  276,  277,  278,  279,
   280,  256,   33,  258,   -1,   -1,  261,  262,  263,  264,
   265,  266,  267,  268,   -1,   -1,   -1,   -1,   -1,    0,
   275,  276,  277,  278,  279,  280,   33,  256,   -1,  258,
@@ -415,21 +428,21 @@ short yycheck[] =
   267,  268,   -1,   -1,   -1,   -1,   -1,    0,  275,  276,
   277,  278,  279,  280,   33,  256,   -1,  258,   -1,   -1,
   261,  262,  263,  264,  265,  266,  267,  268,  258,   -1,
-  260,  261,   -1,  263,  275,  276,  277,  278,  279,  280,
+   -1,  261,  262,  263,  275,  276,  277,  278,  279,  280,
    33,  256,   -1,  258,   -1,  275,  261,  262,  263,  264,
-  265,  266,  267,  268,   33,   -1,   -1,   -1,   -1,    0,
-  275,  276,  277,  278,  279,  280,  256,   -1,  258,   -1,
+  265,  266,  267,  268,   -1,   -1,   -1,   -1,   -1,    0,
+  275,  276,  277,  278,  279,  280,  256,   33,  258,   -1,
    -1,  261,  262,  263,  264,  265,  266,  267,  268,   -1,
    -1,   -1,   -1,   -1,   -1,  275,  276,  277,  278,  279,
   280,  256,   33,  258,   -1,   -1,  261,  262,  263,  264,
-  265,  266,  267,  268,  258,   -1,   -1,  261,  262,  263,
-  275,  276,  277,  278,  279,  280,   33,   -1,   -1,   -1,
-   33,  275,  256,   -1,  258,   -1,   -1,  261,  262,  263,
+  265,  266,  267,  268,   33,   -1,   -1,   -1,   -1,   -1,
+  275,  276,  277,  278,  279,  280,   -1,   -1,   -1,   -1,
+   -1,   -1,  256,   33,  258,   33,   -1,  261,  262,  263,
   264,  265,  266,  267,  268,   -1,  257,  258,   -1,   -1,
-   -1,  275,  276,  277,  278,  279,  280,   -1,  269,  270,
+   -1,  275,  276,  277,  278,  279,  280,   33,  269,  270,
   271,  272,  273,  274,  275,   -1,   -1,   -1,   -1,  256,
    -1,  258,   -1,   -1,  261,  262,  263,  264,  265,  266,
-  267,  268,   -1,   -1,   -1,   -1,   -1,   -1,  275,  276,
+  267,  268,   33,   -1,   -1,   -1,   -1,   -1,  275,  276,
   277,  278,  279,  280,   -1,  256,   -1,  258,   -1,   -1,
   261,  262,  263,  264,  265,  266,  267,  268,   -1,   -1,
    -1,   -1,   -1,   -1,  275,  276,  277,  278,  279,  280,
@@ -439,22 +452,27 @@ short yycheck[] =
    -1,   -1,  261,  262,  263,  264,  265,  266,  267,  268,
    -1,   -1,   -1,   -1,   -1,   -1,  275,  276,  277,  278,
   279,  280,   -1,  256,   -1,  258,   -1,   -1,  261,  262,
-  263,  264,  265,  266,  267,  268,   -1,   -1,  257,  258,
+  263,  264,  265,  266,  267,  268,   -1,   -1,   -1,   -1,
    -1,   -1,  275,  276,  277,  278,  279,  280,   -1,   -1,
-  269,  270,  271,  272,  273,  274,  275,   -1,   -1,   -1,
-   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-   -1,   -1,   -1,   -1,   -1,  256,   -1,  258,   -1,   -1,
-  261,  262,  263,  264,  265,  266,  267,  268,   -1,   -1,
+  256,   -1,  258,   -1,   -1,  261,  262,  263,  264,  265,
+  266,  267,  268,   -1,   -1,   -1,   -1,   -1,   -1,  275,
+  276,  277,  278,  279,  280,  256,   -1,  258,   -1,   -1,
+  261,  262,  263,  264,  265,  266,  267,  268,  257,  258,
    -1,   -1,   -1,   -1,  275,  276,  277,  278,  279,  280,
-   -1,  258,   -1,  260,  261,  258,  263,   -1,  261,  262,
-  263,   -1,   -1,   -1,   -1,   -1,   -1,   -1,  275,   -1,
-   -1,   -1,  275,
+  269,  270,  271,  272,  273,  274,  275,  257,  258,   -1,
+  258,   -1,  260,  261,   -1,  263,   -1,   -1,   -1,  269,
+  270,  271,  272,  273,  274,  275,   -1,  275,   -1,   -1,
+   -1,  257,  258,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+   -1,   -1,   -1,  269,  270,  271,  272,  273,  274,  275,
+   -1,   -1,   -1,   -1,   -1,   -1,   -1,  258,   -1,   -1,
+  261,  262,  263,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+   -1,   -1,   -1,   -1,  275,
 };
 #define YYFINAL 18
 #ifndef YYDEBUG
 #define YYDEBUG 0
 #endif
-#define YYMAXTOKEN 281
+#define YYMAXTOKEN 283
 #if YYDEBUG
 #if defined(__cplusplus) || defined(__STDC__)
 const char * const yyname[] =
@@ -472,7 +490,7 @@ char *yyname[] =
 "COMMAND","ALIAS","DEFVAR","NTWKADDR","NETGROUP","USERGROUP","WORD","DEFAULTS",
 "DEFAULTS_HOST","DEFAULTS_USER","DEFAULTS_RUNAS","DEFAULTS_CMND","NOPASSWD",
 "PASSWD","NOEXEC","EXEC","SETENV","NOSETENV","ALL","COMMENT","HOSTALIAS",
-"CMNDALIAS","USERALIAS","RUNASALIAS","ERROR",
+"CMNDALIAS","USERALIAS","RUNASALIAS","ERROR","TYPE","ROLE",
 };
 #if defined(__cplusplus) || defined(__STDC__)
 const char * const yyrule[] =
@@ -515,9 +533,16 @@ char *yyrule[] =
 "host : WORD",
 "cmndspeclist : cmndspec",
 "cmndspeclist : cmndspeclist ',' cmndspec",
-"cmndspec : runasspec cmndtag opcmnd",
+"cmndspec : runasspec selinux cmndtag opcmnd",
 "opcmnd : cmnd",
 "opcmnd : '!' cmnd",
+"rolespec : ROLE '=' WORD",
+"typespec : TYPE '=' WORD",
+"selinux :",
+"selinux : rolespec",
+"selinux : typespec",
+"selinux : rolespec typespec",
+"selinux : typespec rolespec",
 "runasspec :",
 "runasspec : '(' runaslist ')'",
 "runaslist : userlist",
@@ -592,7 +617,7 @@ short *yyss;
 short *yysslim;
 YYSTYPE *yyvs;
 int yystacksize;
-#line 541 "gram.y"
+#line 591 "gram.y"
 static struct defaults *
 new_default(var, val, op)
     char *var;
@@ -693,12 +718,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) {
@@ -747,7 +786,7 @@ init_parser(path, quiet)
     sudolineno = 1;
     verbose = !quiet;
 }
-#line 699 "y.tab.c"
+#line 738 "y.tab.c"
 /* allocate initial stack or double stack size, up to YYMAXDEPTH */
 #if defined(__cplusplus) || defined(__STDC__)
 static int yygrowstack(void)
@@ -950,127 +989,127 @@ yyreduce:
     switch (yyn)
     {
 case 1:
-#line 182 "gram.y"
+#line 188 "gram.y"
 { ; }
 break;
 case 5:
-#line 190 "gram.y"
+#line 196 "gram.y"
 {
                            ;
                        }
 break;
 case 6:
-#line 193 "gram.y"
+#line 199 "gram.y"
 {
                            yyerrok;
                        }
 break;
 case 7:
-#line 196 "gram.y"
+#line 202 "gram.y"
 {
                            add_userspec(yyvsp[-1].member, yyvsp[0].privilege);
                        }
 break;
 case 8:
-#line 199 "gram.y"
+#line 205 "gram.y"
 {
                            ;
                        }
 break;
 case 9:
-#line 202 "gram.y"
+#line 208 "gram.y"
 {
                            ;
                        }
 break;
 case 10:
-#line 205 "gram.y"
+#line 211 "gram.y"
 {
                            ;
                        }
 break;
 case 11:
-#line 208 "gram.y"
+#line 214 "gram.y"
 {
                            ;
                        }
 break;
 case 12:
-#line 211 "gram.y"
+#line 217 "gram.y"
 {
                            add_defaults(DEFAULTS, NULL, yyvsp[0].defaults);
                        }
 break;
 case 13:
-#line 214 "gram.y"
+#line 220 "gram.y"
 {
                            add_defaults(DEFAULTS_USER, yyvsp[-1].member, yyvsp[0].defaults);
                        }
 break;
 case 14:
-#line 217 "gram.y"
+#line 223 "gram.y"
 {
                            add_defaults(DEFAULTS_RUNAS, yyvsp[-1].member, yyvsp[0].defaults);
                        }
 break;
 case 15:
-#line 220 "gram.y"
+#line 226 "gram.y"
 {
                            add_defaults(DEFAULTS_HOST, yyvsp[-1].member, yyvsp[0].defaults);
                        }
 break;
 case 16:
-#line 223 "gram.y"
+#line 229 "gram.y"
 {
                            add_defaults(DEFAULTS_CMND, yyvsp[-1].member, yyvsp[0].defaults);
                        }
 break;
 case 18:
-#line 229 "gram.y"
+#line 235 "gram.y"
 {
                            list_append(yyvsp[-2].defaults, yyvsp[0].defaults);
                            yyval.defaults = yyvsp[-2].defaults;
                        }
 break;
 case 19:
-#line 235 "gram.y"
+#line 241 "gram.y"
 {
                            yyval.defaults = new_default(yyvsp[0].string, NULL, TRUE);
                        }
 break;
 case 20:
-#line 238 "gram.y"
+#line 244 "gram.y"
 {
                            yyval.defaults = new_default(yyvsp[0].string, NULL, FALSE);
                        }
 break;
 case 21:
-#line 241 "gram.y"
+#line 247 "gram.y"
 {
                            yyval.defaults = new_default(yyvsp[-2].string, yyvsp[0].string, TRUE);
                        }
 break;
 case 22:
-#line 244 "gram.y"
+#line 250 "gram.y"
 {
                            yyval.defaults = new_default(yyvsp[-2].string, yyvsp[0].string, '+');
                        }
 break;
 case 23:
-#line 247 "gram.y"
+#line 253 "gram.y"
 {
                            yyval.defaults = new_default(yyvsp[-2].string, yyvsp[0].string, '-');
                        }
 break;
 case 25:
-#line 253 "gram.y"
+#line 259 "gram.y"
 {
                            list_append(yyvsp[-2].privilege, yyvsp[0].privilege);
                            yyval.privilege = yyvsp[-2].privilege;
                        }
 break;
 case 26:
-#line 259 "gram.y"
+#line 265 "gram.y"
 {
                            struct privilege *p = emalloc(sizeof(*p));
                            list2tq(&p->hostlist, yyvsp[-2].member);
@@ -1081,53 +1120,61 @@ case 26:
                        }
 break;
 case 27:
-#line 269 "gram.y"
+#line 275 "gram.y"
 {
                            yyval.member = yyvsp[0].member;
                            yyval.member->negated = FALSE;
                        }
 break;
 case 28:
-#line 273 "gram.y"
+#line 279 "gram.y"
 {
                            yyval.member = yyvsp[0].member;
                            yyval.member->negated = TRUE;
                        }
 break;
 case 29:
-#line 279 "gram.y"
+#line 285 "gram.y"
 {
                            yyval.member = new_member(yyvsp[0].string, ALIAS);
                        }
 break;
 case 30:
-#line 282 "gram.y"
+#line 288 "gram.y"
 {
                            yyval.member = new_member(NULL, ALL);
                        }
 break;
 case 31:
-#line 285 "gram.y"
+#line 291 "gram.y"
 {
                            yyval.member = new_member(yyvsp[0].string, NETGROUP);
                        }
 break;
 case 32:
-#line 288 "gram.y"
+#line 294 "gram.y"
 {
                            yyval.member = new_member(yyvsp[0].string, NTWKADDR);
                        }
 break;
 case 33:
-#line 291 "gram.y"
+#line 297 "gram.y"
 {
                            yyval.member = new_member(yyvsp[0].string, WORD);
                        }
 break;
 case 35:
-#line 297 "gram.y"
+#line 303 "gram.y"
 {
                            list_append(yyvsp[-2].cmndspec, yyvsp[0].cmndspec);
+#ifdef HAVE_SELINUX
+                           /* propagate role and type */
+                           /* XXX - must be careful when freeing */
+                           if (yyvsp[0].cmndspec->role == NULL)
+                               yyvsp[0].cmndspec->role = yyvsp[0].cmndspec->prev->role;
+                           if (yyvsp[0].cmndspec->type == NULL)
+                               yyvsp[0].cmndspec->type = yyvsp[0].cmndspec->prev->type;
+#endif /* HAVE_SELINUX */
                            /* propagate tags and runas list */
                            if (yyvsp[0].cmndspec->tags.nopasswd == UNSPEC)
                                yyvsp[0].cmndspec->tags.nopasswd = yyvsp[0].cmndspec->prev->tags.nopasswd;
@@ -1147,17 +1194,21 @@ case 35:
                        }
 break;
 case 36:
-#line 318 "gram.y"
+#line 332 "gram.y"
 {
                            struct cmndspec *cs = emalloc(sizeof(*cs));
-                           if (yyvsp[-2].runas != NULL) {
-                               list2tq(&cs->runasuserlist, yyvsp[-2].runas->runasusers);
-                               list2tq(&cs->runasgrouplist, yyvsp[-2].runas->runasgroups);
-                               efree(yyvsp[-2].runas);
+                           if (yyvsp[-3].runas != NULL) {
+                               list2tq(&cs->runasuserlist, yyvsp[-3].runas->runasusers);
+                               list2tq(&cs->runasgrouplist, yyvsp[-3].runas->runasgroups);
+                               efree(yyvsp[-3].runas);
                            } else {
                                tq_init(&cs->runasuserlist);
                                tq_init(&cs->runasgrouplist);
                            }
+#ifdef HAVE_SELINUX
+                           cs->role = yyvsp[-2].seinfo.role;
+                           cs->type = yyvsp[-2].seinfo.type;
+#endif
                            cs->tags = yyvsp[-1].tag;
                            cs->cmnd = yyvsp[0].member;
                            cs->prev = cs;
@@ -1170,111 +1221,158 @@ case 36:
                        }
 break;
 case 37:
-#line 340 "gram.y"
+#line 358 "gram.y"
 {
                            yyval.member = yyvsp[0].member;
                            yyval.member->negated = FALSE;
                        }
 break;
 case 38:
-#line 344 "gram.y"
+#line 362 "gram.y"
 {
                            yyval.member = yyvsp[0].member;
                            yyval.member->negated = TRUE;
                        }
 break;
 case 39:
-#line 350 "gram.y"
+#line 368 "gram.y"
 {
-                           yyval.runas = NULL;
+                           yyval.string = yyvsp[0].string;
                        }
 break;
 case 40:
-#line 353 "gram.y"
+#line 373 "gram.y"
 {
-                           yyval.runas = yyvsp[-1].runas;
+                           yyval.string = yyvsp[0].string;
                        }
 break;
 case 41:
-#line 358 "gram.y"
+#line 378 "gram.y"
+{
+                           yyval.seinfo.role = NULL;
+                           yyval.seinfo.type = NULL;
+                       }
+break;
+case 42:
+#line 382 "gram.y"
+{
+                           yyval.seinfo.role = yyvsp[0].string;
+                           yyval.seinfo.type = NULL;
+                       }
+break;
+case 43:
+#line 386 "gram.y"
+{
+                           yyval.seinfo.type = yyvsp[0].string;
+                           yyval.seinfo.role = NULL;
+                       }
+break;
+case 44:
+#line 390 "gram.y"
+{
+                           yyval.seinfo.role = yyvsp[-1].string;
+                           yyval.seinfo.type = yyvsp[0].string;
+                       }
+break;
+case 45:
+#line 394 "gram.y"
+{
+                           yyval.seinfo.type = yyvsp[-1].string;
+                           yyval.seinfo.role = yyvsp[0].string;
+                       }
+break;
+case 46:
+#line 400 "gram.y"
+{
+                           yyval.runas = NULL;
+                       }
+break;
+case 47:
+#line 403 "gram.y"
+{
+                           yyval.runas = yyvsp[-1].runas;
+                       }
+break;
+case 48:
+#line 408 "gram.y"
 {
                            yyval.runas = emalloc(sizeof(struct runascontainer));
                            yyval.runas->runasusers = yyvsp[0].member;
                            yyval.runas->runasgroups = NULL;
                        }
 break;
-case 42:
-#line 363 "gram.y"
+case 49:
+#line 413 "gram.y"
 {
                            yyval.runas = emalloc(sizeof(struct runascontainer));
                            yyval.runas->runasusers = yyvsp[-2].member;
                            yyval.runas->runasgroups = yyvsp[0].member;
                        }
 break;
-case 43:
-#line 368 "gram.y"
+case 50:
+#line 418 "gram.y"
 {
                            yyval.runas = emalloc(sizeof(struct runascontainer));
                            yyval.runas->runasusers = NULL;
                            yyval.runas->runasgroups = yyvsp[0].member;
                        }
 break;
-case 44:
-#line 375 "gram.y"
+case 51:
+#line 425 "gram.y"
 {
                            yyval.tag.nopasswd = yyval.tag.noexec = yyval.tag.setenv = UNSPEC;
                        }
 break;
-case 45:
-#line 378 "gram.y"
+case 52:
+#line 428 "gram.y"
 {
                            yyval.tag.nopasswd = TRUE;
                        }
 break;
-case 46:
-#line 381 "gram.y"
+case 53:
+#line 431 "gram.y"
 {
                            yyval.tag.nopasswd = FALSE;
                        }
 break;
-case 47:
-#line 384 "gram.y"
+case 54:
+#line 434 "gram.y"
 {
                            yyval.tag.noexec = TRUE;
                        }
 break;
-case 48:
-#line 387 "gram.y"
+case 55:
+#line 437 "gram.y"
 {
                            yyval.tag.noexec = FALSE;
                        }
 break;
-case 49:
-#line 390 "gram.y"
+case 56:
+#line 440 "gram.y"
 {
                            yyval.tag.setenv = TRUE;
                        }
 break;
-case 50:
-#line 393 "gram.y"
+case 57:
+#line 443 "gram.y"
 {
                            yyval.tag.setenv = FALSE;
                        }
 break;
-case 51:
-#line 398 "gram.y"
+case 58:
+#line 448 "gram.y"
 {
                            yyval.member = new_member(NULL, ALL);
                        }
 break;
-case 52:
-#line 401 "gram.y"
+case 59:
+#line 451 "gram.y"
 {
                            yyval.member = new_member(yyvsp[0].string, ALIAS);
                        }
 break;
-case 53:
-#line 404 "gram.y"
+case 60:
+#line 454 "gram.y"
 {
                            struct sudo_command *c = emalloc(sizeof(*c));
                            c->cmnd = yyvsp[0].command.cmnd;
@@ -1282,8 +1380,8 @@ case 53:
                            yyval.member = new_member((char *)c, COMMAND);
                        }
 break;
-case 56:
-#line 416 "gram.y"
+case 63:
+#line 466 "gram.y"
 {
                            char *s;
                            if ((s = alias_add(yyvsp[-2].string, HOSTALIAS, yyvsp[0].member)) != NULL) {
@@ -1292,15 +1390,15 @@ case 56:
                            }
                        }
 break;
-case 58:
-#line 426 "gram.y"
+case 65:
+#line 476 "gram.y"
 {
                            list_append(yyvsp[-2].member, yyvsp[0].member);
                            yyval.member = yyvsp[-2].member;
                        }
 break;
-case 61:
-#line 436 "gram.y"
+case 68:
+#line 486 "gram.y"
 {
                            char *s;
                            if ((s = alias_add(yyvsp[-2].string, CMNDALIAS, yyvsp[0].member)) != NULL) {
@@ -1309,15 +1407,15 @@ case 61:
                            }
                        }
 break;
-case 63:
-#line 446 "gram.y"
+case 70:
+#line 496 "gram.y"
 {
                            list_append(yyvsp[-2].member, yyvsp[0].member);
                            yyval.member = yyvsp[-2].member;
                        }
 break;
-case 66:
-#line 456 "gram.y"
+case 73:
+#line 506 "gram.y"
 {
                            char *s;
                            if ((s = alias_add(yyvsp[-2].string, RUNASALIAS, yyvsp[0].member)) != NULL) {
@@ -1326,8 +1424,8 @@ case 66:
                            }
                        }
 break;
-case 69:
-#line 469 "gram.y"
+case 76:
+#line 519 "gram.y"
 {
                            char *s;
                            if ((s = alias_add(yyvsp[-2].string, USERALIAS, yyvsp[0].member)) != NULL) {
@@ -1336,97 +1434,97 @@ case 69:
                            }
                        }
 break;
-case 71:
-#line 479 "gram.y"
+case 78:
+#line 529 "gram.y"
 {
                            list_append(yyvsp[-2].member, yyvsp[0].member);
                            yyval.member = yyvsp[-2].member;
                        }
 break;
-case 72:
-#line 485 "gram.y"
+case 79:
+#line 535 "gram.y"
 {
                            yyval.member = yyvsp[0].member;
                            yyval.member->negated = FALSE;
                        }
 break;
-case 73:
-#line 489 "gram.y"
+case 80:
+#line 539 "gram.y"
 {
                            yyval.member = yyvsp[0].member;
                            yyval.member->negated = TRUE;
                        }
 break;
-case 74:
-#line 495 "gram.y"
+case 81:
+#line 545 "gram.y"
 {
                            yyval.member = new_member(yyvsp[0].string, ALIAS);
                        }
 break;
-case 75:
-#line 498 "gram.y"
+case 82:
+#line 548 "gram.y"
 {
                            yyval.member = new_member(NULL, ALL);
                        }
 break;
-case 76:
-#line 501 "gram.y"
+case 83:
+#line 551 "gram.y"
 {
                            yyval.member = new_member(yyvsp[0].string, NETGROUP);
                        }
 break;
-case 77:
-#line 504 "gram.y"
+case 84:
+#line 554 "gram.y"
 {
                            yyval.member = new_member(yyvsp[0].string, USERGROUP);
                        }
 break;
-case 78:
-#line 507 "gram.y"
+case 85:
+#line 557 "gram.y"
 {
                            yyval.member = new_member(yyvsp[0].string, WORD);
                        }
 break;
-case 80:
-#line 513 "gram.y"
+case 87:
+#line 563 "gram.y"
 {
                            list_append(yyvsp[-2].member, yyvsp[0].member);
                            yyval.member = yyvsp[-2].member;
                        }
 break;
-case 81:
-#line 519 "gram.y"
+case 88:
+#line 569 "gram.y"
 {
                            yyval.member = yyvsp[0].member;
                            yyval.member->negated = FALSE;
                        }
 break;
-case 82:
-#line 523 "gram.y"
+case 89:
+#line 573 "gram.y"
 {
                            yyval.member = yyvsp[0].member;
                            yyval.member->negated = TRUE;
                        }
 break;
-case 83:
-#line 529 "gram.y"
+case 90:
+#line 579 "gram.y"
 {
                            yyval.member = new_member(yyvsp[0].string, ALIAS);
                        }
 break;
-case 84:
-#line 532 "gram.y"
+case 91:
+#line 582 "gram.y"
 {
                            yyval.member = new_member(NULL, ALL);
                        }
 break;
-case 85:
-#line 535 "gram.y"
+case 92:
+#line 585 "gram.y"
 {
                            yyval.member = new_member(yyvsp[0].string, WORD);
                        }
 break;
-#line 1378 "y.tab.c"
+#line 1476 "y.tab.c"
     }
     yyssp -= yym;
     yystate = *yyssp;
diff --git a/gram.h b/gram.h
index d4aaf6d5ca0b12316fbc4b6faacff8e7721a3c98..8f054916ae7263fb5e9e0697c568ae7691121cd2 100644 (file)
--- a/gram.h
+++ b/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;
diff --git a/gram.y b/gram.y
index dd1c769e32632c73cf07ced98f73147e4457ee7f..c2c68a77871fb197d18c5f3d997f96e9ee62a9a8 100644 (file)
--- a/gram.y
+++ b/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) {
diff --git a/ldap.c b/ldap.c
index 49018717221318cc4bf2cc202c39e239fc5f8f6f..418c02c143624fa3527283e8d8f43b629b997ac1 100644 (file)
--- a/ldap.c
+++ b/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);
diff --git a/parse.c b/parse.c
index 2a0aaa46873f75b1c08659265b87241c7e3e8bf6..4c3c126342ea87da14ace833c5882a3c367cf997 100644 (file)
--- a/parse.c
+++ b/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);
diff --git a/parse.h b/parse.h
index 76395e8fe6d12f5815c7ae47de669ecfce2b71ca..5f66300532e30fd686b5301a4076f619fbbc93eb 100644 (file)
--- a/parse.h
+++ b/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
 };
 
 /*
index c8aebb9ebda6aba3318e4409c3186a2d486951cd..b54877e22105573a1210eb23cbf6188728e25dbb 100644 (file)
 #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 */
diff --git a/selinux.c b/selinux.c
new file mode 100644 (file)
index 0000000..43df79b
--- /dev/null
+++ b/selinux.c
@@ -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);
+}