>From 55ea56eb00b38d500782401248e9f19d6eb36677 Mon Sep 17 00:00:00 2001
From: Simo Sorce <simo@redhat.com>
Date: Fri, 21 Jun 2013 20:36:20 -0400
Subject: [PATCH] Add service match using SeLinux Context

Using getpeercon we can know the elinux context of the process talking to
gssproxy. Use this information as an optional additional filter to match
processes to service definitions.
If a selinux_context option with a full user;role;type context is specified
into a service section, then the connecting process must also be running under
the specified selinux context in order to be allowed to connect.

Signed-off-by: Simo Sorce <simo@redhat.com>
---
 proxy/Makefile.am             |  1 +
 proxy/man/gssproxy.conf.5.xml | 15 +++++++++
 proxy/src/gp_config.c         | 12 ++++++++
 proxy/src/gp_creds.c          |  4 +++
 proxy/src/gp_proxy.h          |  3 ++
 proxy/src/gp_selinux.h        | 71 +++++++++++++++++++++++++++++++++++++++++++
 proxy/src/gp_socket.c         | 66 ++++++++++++++++++++++++++++++++--------
 7 files changed, 159 insertions(+), 13 deletions(-)
 create mode 100644 proxy/src/gp_selinux.h

diff --git a/proxy/Makefile.am b/proxy/Makefile.am
index a7e9a325caba4793c6c224cae5190cec21601ef6..065be6ebcee0b4762f6d3cb603207476240d3b51 100644
--- a/proxy/Makefile.am
+++ b/proxy/Makefile.am
@@ -134,6 +134,7 @@ dist_noinst_HEADERS = \
     src/gp_config_dinglibs.h \
     src/gp_debug.h \
     src/gp_rpc_creds.h \
+    src/gp_selinux.h \
     src/mechglue/gss_plugin.h
 
 
diff --git a/proxy/man/gssproxy.conf.5.xml b/proxy/man/gssproxy.conf.5.xml
index 9238aa6f62916eed19fa9fac8af00b28e5f49ab3..0cbf965dd908ca5f2ef8e3819fa059269c185f8e 100644
--- a/proxy/man/gssproxy.conf.5.xml
+++ b/proxy/man/gssproxy.conf.5.xml
@@ -165,6 +165,21 @@
                     </varlistentry>
 
                 <varlistentry>
+                    <term>selinux_context (string)</term>
+                    <listitem>
+                        <para>This parameter instructs the proxy to allow map a
+                              request to the service only if the context of the
+                              connecting client matches the one defined here.
+                        </para>
+                        <para>When this parameter is not set any client will be
+                              allowed regardless of their selinux context.
+                        </para>
+                        <para>Example: selinux_context = system_u:system_r:gssd_t
+                        </para>
+                    </listitem>
+                    </varlistentry>
+
+                <varlistentry>
                     <term>socket (string)</term>
                     <listitem>
                         <para>This parameter allows to create a per-service socket file over which gssproxy client and server components communicate.
diff --git a/proxy/src/gp_config.c b/proxy/src/gp_config.c
index 1b676d3c75f1a977f29bd94933a7632f1c77964f..2d9a38ecbfa0158780398031ed26c87439338c47 100644
--- a/proxy/src/gp_config.c
+++ b/proxy/src/gp_config.c
@@ -30,6 +30,7 @@
 #include <errno.h>
 #include "gp_proxy.h"
 #include "gp_config.h"
+#include "gp_selinux.h"
 
 static void free_str_array(const char ***a, int *count)
 {
@@ -60,6 +61,7 @@ static void gp_service_free(struct gp_service *svc)
                        &svc->krb5.cred_count);
     }
     gp_free_creds_handle(&svc->creds_handle);
+    SELINUX_context_free(svc->selinux_ctx);
     memset(svc, 0, sizeof(struct gp_service));
 }
 
@@ -250,6 +252,16 @@ static int load_services(struct gp_config *cfg, struct gp_ini_context *ctx)
                 safefree(secname);
                 continue;
             }
+
+            ret = gp_config_get_string(ctx, secname,
+                                       "selinux_context", &value);
+            if (ret == 0) {
+                cfg->svcs[n]->selinux_ctx = SELINUX_context_new(value);
+                if (!cfg->svcs[n]->selinux_ctx) {
+                    ret = EINVAL;
+                    goto done;
+                }
+            }
         }
         safefree(secname);
     }
diff --git a/proxy/src/gp_creds.c b/proxy/src/gp_creds.c
index cd513089f309318b4357fc5b0c3f3a7e49b186f7..4d68afb6fbe812eb1810ab23af07e6288df9968c 100644
--- a/proxy/src/gp_creds.c
+++ b/proxy/src/gp_creds.c
@@ -112,6 +112,10 @@ struct gp_service *gp_creds_match_conn(struct gssproxy_ctx *gpctx,
                     continue;
                 }
             }
+            if (!gp_conn_check_selinux(conn,
+                                       gpctx->config->svcs[i]->selinux_ctx)) {
+                continue;
+            }
             return gpctx->config->svcs[i];
         }
     }
diff --git a/proxy/src/gp_proxy.h b/proxy/src/gp_proxy.h
index dc7aada2700e427593c7db95f435df075b9da314..835fcf5669ab34c18e8e74ae81a306a5600ec495 100644
--- a/proxy/src/gp_proxy.h
+++ b/proxy/src/gp_proxy.h
@@ -31,6 +31,7 @@
 #include <stdint.h>
 #include "verto.h"
 #include "gp_common.h"
+#include "gp_selinux.h"
 
 #define _(STRING) gettext(STRING)
 
@@ -52,6 +53,7 @@ struct gp_service {
     bool trusted;
     bool kernel_nfsd;
     char *socket;
+    SELINUX_CTX selinux_ctx;
 
     uint32_t mechs;
     struct gp_cred_krb5 krb5;
@@ -106,6 +108,7 @@ void gp_socket_send_data(verto_ctx *vctx, struct gp_conn *conn,
                          uint8_t *buffer, size_t buflen);
 struct gp_creds *gp_conn_get_creds(struct gp_conn *conn);
 const char *gp_conn_get_socket(struct gp_conn *conn);
+bool gp_conn_check_selinux(struct gp_conn *conn, SELINUX_CTX ctx);
 
 /* from gp_workers.c */
 int gp_workers_init(struct gssproxy_ctx *gpctx);
diff --git a/proxy/src/gp_selinux.h b/proxy/src/gp_selinux.h
new file mode 100644
index 0000000000000000000000000000000000000000..693a12421823e0e6808cf19b8835b68411c106c7
--- /dev/null
+++ b/proxy/src/gp_selinux.h
@@ -0,0 +1,71 @@
+/*
+   GSS-PROXY
+
+   Copyright (C) 2013 Red Hat, Inc.
+   Copyright (C) 2013 Simo Sorce <simo.sorce@redhat.com>
+
+   Permission is hereby granted, free of charge, to any person obtaining a
+   copy of this software and associated documentation files (the "Software"),
+   to deal in the Software without restriction, including without limitation
+   the rights to use, copy, modify, merge, publish, distribute, sublicense,
+   and/or sell copies of the Software, and to permit persons to whom the
+   Software is furnished to do so, subject to the following conditions:
+
+   The above copyright notice and this permission notice shall be included in
+   all copies or substantial portions of the Software.
+
+   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+   IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+   THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+   LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+   FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+   DEALINGS IN THE SOFTWARE.
+*/
+
+#ifndef _GP_SELINUX_H_
+#define _GP_SELINUX_H_
+
+#ifdef HAVE_SELINUX
+
+#include <selinux/context.h>
+#define SELINUX_CTX context_t
+#include <selinux/selinux.h>
+#define SEC_CTX security_context_t
+
+#define SELINUX_context_new context_new
+#define SELINUX_context_free context_free
+#define SELINUX_context_str context_str
+#define SELINUX_context_type_get context_type_get
+#define SELINUX_context_user_get context_user_get
+#define SELINUX_context_role_get context_role_get
+#define SELINUX_context_range_get context_range_get
+#define SELINUX_getpeercon getpeercon
+#define SELINUX_freecon freecon
+
+#else /* not HAVE_SELINUX */
+
+#define SELINUX_CTX void *
+#define SEC_CTX void *
+
+void *SELINUX_context_new(const char *str) { return NULL; }
+#define SELINUX_context_free(x) (x) = NULL;
+const char *SELINUX_context_dummy_get(void *) { return NULL; }
+#define SELINUX_context_str SELINUX_context_dummy_get
+#define SELINUX_context_type_get SELINUX_context_dummy_get
+#define SELINUX_context_user_get SELINUX_context_dummy_get
+#define SELINUX_context_role_get SELINUX_context_dummy_get
+#define SELINUX_context_range_get SELINUX_context_dummy_get
+
+#include <errno.h>
+int SELINUX_getpeercon(int fd, SEC_CTX *con)
+{
+    *con = NULL;
+    errno = ENOTSUP;
+    return -1;
+}
+#define SELINUX_freecon(x) (x) = NULL;
+
+#endif /* done HAVE_SELINUX */
+
+#endif  /*_GP_SELINUX_H_ */
diff --git a/proxy/src/gp_socket.c b/proxy/src/gp_socket.c
index 02bc88264182548f43bab0c68965605c360a4fc6..4196a76056cfd6e28f6d55f9c12257f776d9a872 100644
--- a/proxy/src/gp_socket.c
+++ b/proxy/src/gp_socket.c
@@ -35,13 +35,7 @@
 #include <netinet/in.h>
 #include "gp_proxy.h"
 #include "gp_creds.h"
-
-#ifdef HAVE_SELINUX
-#include <selinux/selinux.h>
-#define SEC_CTX security_context_t
-#else
-#define SEC_CTX void *
-#endif /* HAVE_SELINUX */
+#include "gp_selinux.h"
 
 #define FRAGMENT_BIT (1 << 31)
 
@@ -58,7 +52,7 @@ struct gp_conn {
     struct gp_sock_ctx *sock_ctx;
     struct unix_sock_conn us;
     struct gp_creds creds;
-    SEC_CTX secctx;
+    SELINUX_CTX selinux_ctx;
 };
 
 struct gp_buffer {
@@ -68,6 +62,40 @@ struct gp_buffer {
     size_t pos;
 };
 
+bool gp_conn_check_selinux(struct gp_conn *conn, SELINUX_CTX ctx)
+{
+    const char *ra, *rb;
+
+    if (ctx == NULL) {
+        return true;
+    }
+
+    if (!(conn->creds.type | CRED_TYPE_SELINUX) ||
+         (conn->selinux_ctx == NULL)) {
+        return false;
+    }
+
+    if (strcmp(SELINUX_context_user_get(ctx),
+               SELINUX_context_user_get(conn->selinux_ctx)) != 0) {
+        return false;
+    }
+    if (strcmp(SELINUX_context_role_get(ctx),
+               SELINUX_context_role_get(conn->selinux_ctx)) != 0) {
+        return false;
+    }
+    if (strcmp(SELINUX_context_type_get(ctx),
+               SELINUX_context_type_get(conn->selinux_ctx)) != 0) {
+        return false;
+    }
+    ra = SELINUX_context_range_get(ctx);
+    rb = SELINUX_context_range_get(conn->selinux_ctx);
+    if (ra && rb && (strcmp(ra, rb) != 0)) {
+        return false;
+    }
+
+    return true;
+}
+
 struct gp_creds *gp_conn_get_creds(struct gp_conn *conn)
 {
     return &conn->creds;
@@ -85,6 +113,7 @@ void gp_conn_free(struct gp_conn *conn)
     if (conn->us.sd != -1) {
         close(conn->us.sd);
     }
+    SELINUX_context_free(conn->selinux_ctx);
     free(conn);
 }
 
@@ -202,6 +231,7 @@ done:
 
 static int get_peercred(int fd, struct gp_conn *conn)
 {
+    SEC_CTX secctx;
     socklen_t len;
     int ret;
 
@@ -219,17 +249,17 @@ static int get_peercred(int fd, struct gp_conn *conn)
 
     conn->creds.type |= CRED_TYPE_UNIX;
 
-#ifdef HAVE_SELINUX
-    ret = getpeercon(fd, &conn->secctx);
+    ret = SELINUX_getpeercon(fd, &secctx);
     if (ret == 0) {
         conn->creds.type |= CRED_TYPE_SELINUX;
+        conn->selinux_ctx = SELINUX_context_new(secctx);
+        SELINUX_freecon(secctx);
     } else {
         ret = errno;
         GPDEBUG("Failed to get peer's SELinux context (%d:%s)\n",
                 ret, strerror(ret));
         /* consider thisnot fatal, selinux may be disabled */
     }
-#endif /* HAVE_SELINUX */
 
     return 0;
 }
@@ -507,8 +537,6 @@ void accept_sock_conn(verto_ctx *vctx, verto_ev *ev)
     }
     conn->us.sd = fd;
 
-    GPDEBUG("Client connected(fd = %d)\n", fd);
-
     ret = set_status_flags(fd, O_NONBLOCK);
     if (ret) {
         GPDEBUG("Failed to set O_NONBLOCK on %d!\n", fd);
@@ -526,6 +554,18 @@ void accept_sock_conn(verto_ctx *vctx, verto_ev *ev)
         goto done;
     }
 
+    GPDEBUG("Client connected (fd = %d)\n", fd);
+    if (conn->creds.type & CRED_TYPE_UNIX) {
+        GPDEBUG("(pid = %d) (uid = %d) (gid = %d)\n",
+                conn->creds.ucred.pid,
+                conn->creds.ucred.uid,
+                conn->creds.ucred.gid);
+    }
+    if (conn->creds.type & CRED_TYPE_SELINUX) {
+        GPDEBUG("(context = %s)\n",
+                SELINUX_context_str(conn->selinux_ctx));
+    }
+
     gp_setup_reader(vctx, conn);
 
     ret = 0;
-- 
1.8.2.1

