forked from rpms/openssh
d029bb77ce
This is an automated DistroBaker update from upstream sources. If you do not know what this is about or would like to opt out, contact the OSCI team. Source: https://src.fedoraproject.org/rpms/openssh.git#44aae310bd4e0f19369ea1c91ada03334f29c843
868 lines
26 KiB
Diff
868 lines
26 KiB
Diff
diff -up openssh/auth2.c.role-mls openssh/auth2.c
|
|
--- openssh/auth2.c.role-mls 2018-08-20 07:57:29.000000000 +0200
|
|
+++ openssh/auth2.c 2018-08-22 11:14:56.815430916 +0200
|
|
@@ -256,6 +256,9 @@ input_userauth_request(int type, u_int32
|
|
Authctxt *authctxt = ssh->authctxt;
|
|
Authmethod *m = NULL;
|
|
char *user = NULL, *service = NULL, *method = NULL, *style = NULL;
|
|
+#ifdef WITH_SELINUX
|
|
+ char *role = NULL;
|
|
+#endif
|
|
int r, authenticated = 0;
|
|
double tstart = monotime_double();
|
|
|
|
@@ -268,6 +271,11 @@ input_userauth_request(int type, u_int32
|
|
debug("userauth-request for user %s service %s method %s", user, service, method);
|
|
debug("attempt %d failures %d", authctxt->attempt, authctxt->failures);
|
|
|
|
+#ifdef WITH_SELINUX
|
|
+ if ((role = strchr(user, '/')) != NULL)
|
|
+ *role++ = 0;
|
|
+#endif
|
|
+
|
|
if ((style = strchr(user, ':')) != NULL)
|
|
*style++ = 0;
|
|
|
|
@@ -296,8 +304,15 @@ input_userauth_request(int type, u_int32
|
|
use_privsep ? " [net]" : "");
|
|
authctxt->service = xstrdup(service);
|
|
authctxt->style = style ? xstrdup(style) : NULL;
|
|
- if (use_privsep)
|
|
+#ifdef WITH_SELINUX
|
|
+ authctxt->role = role ? xstrdup(role) : NULL;
|
|
+#endif
|
|
+ if (use_privsep) {
|
|
mm_inform_authserv(service, style);
|
|
+#ifdef WITH_SELINUX
|
|
+ mm_inform_authrole(role);
|
|
+#endif
|
|
+ }
|
|
userauth_banner(ssh);
|
|
if (auth2_setup_methods_lists(authctxt) != 0)
|
|
ssh_packet_disconnect(ssh,
|
|
diff -up openssh/auth2-gss.c.role-mls openssh/auth2-gss.c
|
|
--- openssh/auth2-gss.c.role-mls 2018-08-20 07:57:29.000000000 +0200
|
|
+++ openssh/auth2-gss.c 2018-08-22 11:15:42.459799171 +0200
|
|
@@ -281,6 +281,7 @@ input_gssapi_mic(int type, u_int32_t ple
|
|
Authctxt *authctxt = ssh->authctxt;
|
|
Gssctxt *gssctxt;
|
|
int r, authenticated = 0;
|
|
+ char *micuser;
|
|
struct sshbuf *b;
|
|
gss_buffer_desc mic, gssbuf;
|
|
const char *displayname;
|
|
@@ -298,7 +299,13 @@ input_gssapi_mic(int type, u_int32_t ple
|
|
fatal_f("sshbuf_new failed");
|
|
mic.value = p;
|
|
mic.length = len;
|
|
- ssh_gssapi_buildmic(b, authctxt->user, authctxt->service,
|
|
+#ifdef WITH_SELINUX
|
|
+ if (authctxt->role && authctxt->role[0] != 0)
|
|
+ xasprintf(&micuser, "%s/%s", authctxt->user, authctxt->role);
|
|
+ else
|
|
+#endif
|
|
+ micuser = authctxt->user;
|
|
+ ssh_gssapi_buildmic(b, micuser, authctxt->service,
|
|
"gssapi-with-mic", ssh->kex->session_id);
|
|
|
|
if ((gssbuf.value = sshbuf_mutable_ptr(b)) == NULL)
|
|
@@ -311,6 +318,8 @@ input_gssapi_mic(int type, u_int32_t ple
|
|
logit("GSSAPI MIC check failed");
|
|
|
|
sshbuf_free(b);
|
|
+ if (micuser != authctxt->user)
|
|
+ free(micuser);
|
|
free(mic.value);
|
|
|
|
if ((!use_privsep || mm_is_monitor()) &&
|
|
diff -up openssh/auth2-hostbased.c.role-mls openssh/auth2-hostbased.c
|
|
--- openssh/auth2-hostbased.c.role-mls 2018-08-20 07:57:29.000000000 +0200
|
|
+++ openssh/auth2-hostbased.c 2018-08-22 11:14:56.816430924 +0200
|
|
@@ -123,7 +123,16 @@ userauth_hostbased(struct ssh *ssh)
|
|
/* reconstruct packet */
|
|
if ((r = sshbuf_put_stringb(b, ssh->kex->session_id)) != 0 ||
|
|
(r = sshbuf_put_u8(b, SSH2_MSG_USERAUTH_REQUEST)) != 0 ||
|
|
+#ifdef WITH_SELINUX
|
|
+ (authctxt->role
|
|
+ ? ( (r = sshbuf_put_u32(b, strlen(authctxt->user)+strlen(authctxt->role)+1)) != 0 ||
|
|
+ (r = sshbuf_put(b, authctxt->user, strlen(authctxt->user))) != 0 ||
|
|
+ (r = sshbuf_put_u8(b, '/') != 0) ||
|
|
+ (r = sshbuf_put(b, authctxt->role, strlen(authctxt->role))) != 0)
|
|
+ : (r = sshbuf_put_cstring(b, authctxt->user)) != 0) ||
|
|
+#else
|
|
(r = sshbuf_put_cstring(b, authctxt->user)) != 0 ||
|
|
+#endif
|
|
(r = sshbuf_put_cstring(b, authctxt->service)) != 0 ||
|
|
(r = sshbuf_put_cstring(b, "hostbased")) != 0 ||
|
|
(r = sshbuf_put_string(b, pkalg, alen)) != 0 ||
|
|
diff -up openssh/auth2-pubkey.c.role-mls openssh/auth2-pubkey.c
|
|
--- openssh/auth2-pubkey.c.role-mls 2018-08-22 11:14:56.816430924 +0200
|
|
+++ openssh/auth2-pubkey.c 2018-08-22 11:17:07.331483958 +0200
|
|
@@ -169,9 +169,16 @@ userauth_pubkey(struct ssh *ssh)
|
|
goto done;
|
|
}
|
|
/* reconstruct packet */
|
|
- xasprintf(&userstyle, "%s%s%s", authctxt->user,
|
|
+ xasprintf(&userstyle, "%s%s%s%s%s", authctxt->user,
|
|
authctxt->style ? ":" : "",
|
|
- authctxt->style ? authctxt->style : "");
|
|
+ authctxt->style ? authctxt->style : "",
|
|
+#ifdef WITH_SELINUX
|
|
+ authctxt->role ? "/" : "",
|
|
+ authctxt->role ? authctxt->role : ""
|
|
+#else
|
|
+ "", ""
|
|
+#endif
|
|
+ );
|
|
if ((r = sshbuf_put_u8(b, SSH2_MSG_USERAUTH_REQUEST)) != 0 ||
|
|
(r = sshbuf_put_cstring(b, userstyle)) != 0 ||
|
|
(r = sshbuf_put_cstring(b, authctxt->service)) != 0 ||
|
|
diff -up openssh/auth.h.role-mls openssh/auth.h
|
|
--- openssh/auth.h.role-mls 2018-08-20 07:57:29.000000000 +0200
|
|
+++ openssh/auth.h 2018-08-22 11:14:56.816430924 +0200
|
|
@@ -65,6 +65,9 @@ struct Authctxt {
|
|
char *service;
|
|
struct passwd *pw; /* set if 'valid' */
|
|
char *style;
|
|
+#ifdef WITH_SELINUX
|
|
+ char *role;
|
|
+#endif
|
|
|
|
/* Method lists for multiple authentication */
|
|
char **auth_methods; /* modified from server config */
|
|
diff -up openssh/auth-pam.c.role-mls openssh/auth-pam.c
|
|
--- openssh/auth-pam.c.role-mls 2018-08-20 07:57:29.000000000 +0200
|
|
+++ openssh/auth-pam.c 2018-08-22 11:14:56.816430924 +0200
|
|
@@ -1172,7 +1172,7 @@ is_pam_session_open(void)
|
|
* during the ssh authentication process.
|
|
*/
|
|
int
|
|
-do_pam_putenv(char *name, char *value)
|
|
+do_pam_putenv(char *name, const char *value)
|
|
{
|
|
int ret = 1;
|
|
char *compound;
|
|
diff -up openssh/auth-pam.h.role-mls openssh/auth-pam.h
|
|
--- openssh/auth-pam.h.role-mls 2018-08-20 07:57:29.000000000 +0200
|
|
+++ openssh/auth-pam.h 2018-08-22 11:14:56.817430932 +0200
|
|
@@ -33,7 +33,7 @@ u_int do_pam_account(void);
|
|
void do_pam_session(struct ssh *);
|
|
void do_pam_setcred(int );
|
|
void do_pam_chauthtok(void);
|
|
-int do_pam_putenv(char *, char *);
|
|
+int do_pam_putenv(char *, const char *);
|
|
char ** fetch_pam_environment(void);
|
|
char ** fetch_pam_child_environment(void);
|
|
void free_pam_environment(char **);
|
|
diff -up openssh/misc.c.role-mls openssh/misc.c
|
|
--- openssh/misc.c.role-mls 2018-08-20 07:57:29.000000000 +0200
|
|
+++ openssh/misc.c 2018-08-22 11:14:56.817430932 +0200
|
|
@@ -542,6 +542,7 @@ char *
|
|
colon(char *cp)
|
|
{
|
|
int flag = 0;
|
|
+ int start = 1;
|
|
|
|
if (*cp == ':') /* Leading colon is part of file name. */
|
|
return NULL;
|
|
@@ -557,6 +558,13 @@ colon(char *cp)
|
|
return (cp);
|
|
if (*cp == '/')
|
|
return NULL;
|
|
+ if (start) {
|
|
+ /* Slash on beginning or after dots only denotes file name. */
|
|
+ if (*cp == '/')
|
|
+ return (0);
|
|
+ if (*cp != '.')
|
|
+ start = 0;
|
|
+ }
|
|
}
|
|
return NULL;
|
|
}
|
|
diff -up openssh/monitor.c.role-mls openssh/monitor.c
|
|
--- openssh/monitor.c.role-mls 2018-08-20 07:57:29.000000000 +0200
|
|
+++ openssh/monitor.c 2018-08-22 11:19:56.006844867 +0200
|
|
@@ -115,6 +115,9 @@ int mm_answer_sign(int, struct sshbuf *)
|
|
int mm_answer_pwnamallow(struct ssh *, int, struct sshbuf *);
|
|
int mm_answer_auth2_read_banner(struct ssh *, int, struct sshbuf *);
|
|
int mm_answer_authserv(struct ssh *, int, struct sshbuf *);
|
|
+#ifdef WITH_SELINUX
|
|
+int mm_answer_authrole(struct ssh *, int, struct sshbuf *);
|
|
+#endif
|
|
int mm_answer_authpassword(struct ssh *, int, struct sshbuf *);
|
|
int mm_answer_bsdauthquery(struct ssh *, int, struct sshbuf *);
|
|
int mm_answer_bsdauthrespond(struct ssh *, int, struct sshbuf *);
|
|
@@ -189,6 +192,9 @@ struct mon_table mon_dispatch_proto20[]
|
|
{MONITOR_REQ_SIGN, MON_ONCE, mm_answer_sign},
|
|
{MONITOR_REQ_PWNAM, MON_ONCE, mm_answer_pwnamallow},
|
|
{MONITOR_REQ_AUTHSERV, MON_ONCE, mm_answer_authserv},
|
|
+#ifdef WITH_SELINUX
|
|
+ {MONITOR_REQ_AUTHROLE, MON_ONCE, mm_answer_authrole},
|
|
+#endif
|
|
{MONITOR_REQ_AUTH2_READ_BANNER, MON_ONCE, mm_answer_auth2_read_banner},
|
|
{MONITOR_REQ_AUTHPASSWORD, MON_AUTH, mm_answer_authpassword},
|
|
#ifdef USE_PAM
|
|
@@ -796,6 +802,9 @@ mm_answer_pwnamallow(int sock, struct ss
|
|
|
|
/* Allow service/style information on the auth context */
|
|
monitor_permit(mon_dispatch, MONITOR_REQ_AUTHSERV, 1);
|
|
+#ifdef WITH_SELINUX
|
|
+ monitor_permit(mon_dispatch, MONITOR_REQ_AUTHROLE, 1);
|
|
+#endif
|
|
monitor_permit(mon_dispatch, MONITOR_REQ_AUTH2_READ_BANNER, 1);
|
|
|
|
#ifdef USE_PAM
|
|
@@ -842,6 +851,26 @@ mm_answer_authserv(int sock, struct sshb
|
|
return found;
|
|
}
|
|
|
|
+#ifdef WITH_SELINUX
|
|
+int
|
|
+mm_answer_authrole(struct ssh *ssh, int sock, struct sshbuf *m)
|
|
+{
|
|
+ int r;
|
|
+ monitor_permit_authentications(1);
|
|
+
|
|
+ if ((r = sshbuf_get_cstring(m, &authctxt->role, NULL)) != 0)
|
|
+ fatal_f("buffer error: %s", ssh_err(r));
|
|
+ debug3_f("role=%s", authctxt->role);
|
|
+
|
|
+ if (strlen(authctxt->role) == 0) {
|
|
+ free(authctxt->role);
|
|
+ authctxt->role = NULL;
|
|
+ }
|
|
+
|
|
+ return (0);
|
|
+}
|
|
+#endif
|
|
+
|
|
int
|
|
mm_answer_authpassword(struct ssh *ssh, int sock, struct sshbuf *m)
|
|
{
|
|
@@ -1218,7 +1247,7 @@ monitor_valid_userblob(u_char *data, u_i
|
|
{
|
|
struct sshbuf *b;
|
|
const u_char *p;
|
|
- char *userstyle, *cp;
|
|
+ char *userstyle, *s, *cp;
|
|
size_t len;
|
|
u_char type;
|
|
int r, fail = 0;
|
|
@@ -1251,6 +1280,8 @@ monitor_valid_userblob(u_char *data, u_i
|
|
fail++;
|
|
if ((r = sshbuf_get_cstring(b, &cp, NULL)) != 0)
|
|
fatal_fr(r, "parse userstyle");
|
|
+ if ((s = strchr(cp, '/')) != NULL)
|
|
+ *s = '\0';
|
|
xasprintf(&userstyle, "%s%s%s", authctxt->user,
|
|
authctxt->style ? ":" : "",
|
|
authctxt->style ? authctxt->style : "");
|
|
@@ -1286,7 +1317,7 @@ monitor_valid_hostbasedblob(u_char *data
|
|
{
|
|
struct sshbuf *b;
|
|
const u_char *p;
|
|
- char *cp, *userstyle;
|
|
+ char *cp, *s, *userstyle;
|
|
size_t len;
|
|
int r, fail = 0;
|
|
u_char type;
|
|
@@ -1308,6 +1339,8 @@ monitor_valid_hostbasedblob(u_char *data
|
|
fail++;
|
|
if ((r = sshbuf_get_cstring(b, &cp, NULL)) != 0)
|
|
fatal_fr(r, "parse userstyle");
|
|
+ if ((s = strchr(p, '/')) != NULL)
|
|
+ *s = '\0';
|
|
xasprintf(&userstyle, "%s%s%s", authctxt->user,
|
|
authctxt->style ? ":" : "",
|
|
authctxt->style ? authctxt->style : "");
|
|
diff -up openssh/monitor.h.role-mls openssh/monitor.h
|
|
--- openssh/monitor.h.role-mls 2018-08-20 07:57:29.000000000 +0200
|
|
+++ openssh/monitor.h 2018-08-22 11:14:56.818430941 +0200
|
|
@@ -55,6 +55,10 @@ enum monitor_reqtype {
|
|
MONITOR_REQ_GSSCHECKMIC = 48, MONITOR_ANS_GSSCHECKMIC = 49,
|
|
MONITOR_REQ_TERM = 50,
|
|
|
|
+#ifdef WITH_SELINUX
|
|
+ MONITOR_REQ_AUTHROLE = 80,
|
|
+#endif
|
|
+
|
|
MONITOR_REQ_PAM_START = 100,
|
|
MONITOR_REQ_PAM_ACCOUNT = 102, MONITOR_ANS_PAM_ACCOUNT = 103,
|
|
MONITOR_REQ_PAM_INIT_CTX = 104, MONITOR_ANS_PAM_INIT_CTX = 105,
|
|
diff -up openssh/monitor_wrap.c.role-mls openssh/monitor_wrap.c
|
|
--- openssh/monitor_wrap.c.role-mls 2018-08-22 11:14:56.818430941 +0200
|
|
+++ openssh/monitor_wrap.c 2018-08-22 11:21:47.938747968 +0200
|
|
@@ -390,6 +390,27 @@ mm_inform_authserv(char *service, char *
|
|
sshbuf_free(m);
|
|
}
|
|
|
|
+/* Inform the privileged process about role */
|
|
+
|
|
+#ifdef WITH_SELINUX
|
|
+void
|
|
+mm_inform_authrole(char *role)
|
|
+{
|
|
+ int r;
|
|
+ struct sshbuf *m;
|
|
+
|
|
+ debug3_f("entering");
|
|
+
|
|
+ if ((m = sshbuf_new()) == NULL)
|
|
+ fatal_f("sshbuf_new failed");
|
|
+ if ((r = sshbuf_put_cstring(m, role ? role : "")) != 0)
|
|
+ fatal_f("buffer error: %s", ssh_err(r));
|
|
+ mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_AUTHROLE, m);
|
|
+
|
|
+ sshbuf_free(m);
|
|
+}
|
|
+#endif
|
|
+
|
|
/* Do the password authentication */
|
|
int
|
|
mm_auth_password(struct ssh *ssh, char *password)
|
|
diff -up openssh/monitor_wrap.h.role-mls openssh/monitor_wrap.h
|
|
--- openssh/monitor_wrap.h.role-mls 2018-08-22 11:14:56.818430941 +0200
|
|
+++ openssh/monitor_wrap.h 2018-08-22 11:22:10.439929513 +0200
|
|
@@ -44,6 +44,9 @@ DH *mm_choose_dh(int, int, int);
|
|
const u_char *, size_t, const char *, const char *,
|
|
const char *, u_int compat);
|
|
void mm_inform_authserv(char *, char *);
|
|
+#ifdef WITH_SELINUX
|
|
+void mm_inform_authrole(char *);
|
|
+#endif
|
|
struct passwd *mm_getpwnamallow(struct ssh *, const char *);
|
|
char *mm_auth2_read_banner(void);
|
|
int mm_auth_password(struct ssh *, char *);
|
|
diff -up openssh/openbsd-compat/Makefile.in.role-mls openssh/openbsd-compat/Makefile.in
|
|
--- openssh/openbsd-compat/Makefile.in.role-mls 2018-08-20 07:57:29.000000000 +0200
|
|
+++ openssh/openbsd-compat/Makefile.in 2018-08-22 11:14:56.819430949 +0200
|
|
@@ -92,7 +92,8 @@ PORTS= port-aix.o \
|
|
port-linux.o \
|
|
port-solaris.o \
|
|
port-net.o \
|
|
- port-uw.o
|
|
+ port-uw.o \
|
|
+ port-linux-sshd.o
|
|
|
|
.c.o:
|
|
$(CC) $(CFLAGS_NOPIE) $(PICFLAG) $(CPPFLAGS) -c $<
|
|
diff -up openssh/openbsd-compat/port-linux.c.role-mls openssh/openbsd-compat/port-linux.c
|
|
--- openssh/openbsd-compat/port-linux.c.role-mls 2018-08-20 07:57:29.000000000 +0200
|
|
+++ openssh/openbsd-compat/port-linux.c 2018-08-22 11:14:56.819430949 +0200
|
|
@@ -100,37 +100,6 @@ ssh_selinux_getctxbyname(char *pwname)
|
|
return sc;
|
|
}
|
|
|
|
-/* Set the execution context to the default for the specified user */
|
|
-void
|
|
-ssh_selinux_setup_exec_context(char *pwname)
|
|
-{
|
|
- char *user_ctx = NULL;
|
|
-
|
|
- if (!ssh_selinux_enabled())
|
|
- return;
|
|
-
|
|
- debug3("%s: setting execution context", __func__);
|
|
-
|
|
- user_ctx = ssh_selinux_getctxbyname(pwname);
|
|
- if (setexeccon(user_ctx) != 0) {
|
|
- switch (security_getenforce()) {
|
|
- case -1:
|
|
- fatal("%s: security_getenforce() failed", __func__);
|
|
- case 0:
|
|
- error("%s: Failed to set SELinux execution "
|
|
- "context for %s", __func__, pwname);
|
|
- break;
|
|
- default:
|
|
- fatal("%s: Failed to set SELinux execution context "
|
|
- "for %s (in enforcing mode)", __func__, pwname);
|
|
- }
|
|
- }
|
|
- if (user_ctx != NULL)
|
|
- freecon(user_ctx);
|
|
-
|
|
- debug3("%s: done", __func__);
|
|
-}
|
|
-
|
|
/* Set the TTY context for the specified user */
|
|
void
|
|
ssh_selinux_setup_pty(char *pwname, const char *tty)
|
|
@@ -145,7 +114,11 @@ ssh_selinux_setup_pty(char *pwname, cons
|
|
|
|
debug3("%s: setting TTY context on %s", __func__, tty);
|
|
|
|
- user_ctx = ssh_selinux_getctxbyname(pwname);
|
|
+ if (getexeccon(&user_ctx) != 0) {
|
|
+ error_f("getexeccon: %s", strerror(errno));
|
|
+ goto out;
|
|
+ }
|
|
+
|
|
|
|
/* XXX: should these calls fatal() upon failure in enforcing mode? */
|
|
|
|
diff -up openssh/openbsd-compat/port-linux.h.role-mls openssh/openbsd-compat/port-linux.h
|
|
--- openssh/openbsd-compat/port-linux.h.role-mls 2018-08-20 07:57:29.000000000 +0200
|
|
+++ openssh/openbsd-compat/port-linux.h 2018-08-22 11:14:56.819430949 +0200
|
|
@@ -20,9 +20,10 @@
|
|
#ifdef WITH_SELINUX
|
|
int ssh_selinux_enabled(void);
|
|
void ssh_selinux_setup_pty(char *, const char *);
|
|
-void ssh_selinux_setup_exec_context(char *);
|
|
void ssh_selinux_change_context(const char *);
|
|
void ssh_selinux_setfscreatecon(const char *);
|
|
+
|
|
+void sshd_selinux_setup_exec_context(char *);
|
|
#endif
|
|
|
|
#ifdef LINUX_OOM_ADJUST
|
|
diff -up openssh/openbsd-compat/port-linux-sshd.c.role-mls openssh/openbsd-compat/port-linux-sshd.c
|
|
--- openssh/openbsd-compat/port-linux-sshd.c.role-mls 2018-08-22 11:14:56.819430949 +0200
|
|
+++ openssh/openbsd-compat/port-linux-sshd.c 2018-08-22 11:14:56.819430949 +0200
|
|
@@ -0,0 +1,421 @@
|
|
+/*
|
|
+ * Copyright (c) 2005 Daniel Walsh <dwalsh@redhat.com>
|
|
+ * Copyright (c) 2014 Petr Lautrbach <plautrba@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.
|
|
+ */
|
|
+
|
|
+/*
|
|
+ * Linux-specific portability code - just SELinux support for sshd at present
|
|
+ */
|
|
+
|
|
+#include "includes.h"
|
|
+
|
|
+#if defined(WITH_SELINUX) || defined(LINUX_OOM_ADJUST)
|
|
+#include <errno.h>
|
|
+#include <stdarg.h>
|
|
+#include <string.h>
|
|
+#include <stdio.h>
|
|
+#include <stdlib.h>
|
|
+
|
|
+#include "log.h"
|
|
+#include "xmalloc.h"
|
|
+#include "misc.h" /* servconf.h needs misc.h for struct ForwardOptions */
|
|
+#include "servconf.h"
|
|
+#include "port-linux.h"
|
|
+#include "sshkey.h"
|
|
+#include "hostfile.h"
|
|
+#include "auth.h"
|
|
+
|
|
+#ifdef WITH_SELINUX
|
|
+#include <selinux/selinux.h>
|
|
+#include <selinux/context.h>
|
|
+#include <selinux/get_context_list.h>
|
|
+#include <selinux/get_default_type.h>
|
|
+
|
|
+#ifdef HAVE_LINUX_AUDIT
|
|
+#include <libaudit.h>
|
|
+#include <unistd.h>
|
|
+#endif
|
|
+
|
|
+extern ServerOptions options;
|
|
+extern Authctxt *the_authctxt;
|
|
+extern int inetd_flag;
|
|
+extern int rexeced_flag;
|
|
+
|
|
+/* Send audit message */
|
|
+static int
|
|
+sshd_selinux_send_audit_message(int success, security_context_t default_context,
|
|
+ security_context_t selected_context)
|
|
+{
|
|
+ int rc=0;
|
|
+#ifdef HAVE_LINUX_AUDIT
|
|
+ char *msg = NULL;
|
|
+ int audit_fd = audit_open();
|
|
+ security_context_t default_raw=NULL;
|
|
+ security_context_t selected_raw=NULL;
|
|
+ rc = -1;
|
|
+ if (audit_fd < 0) {
|
|
+ if (errno == EINVAL || errno == EPROTONOSUPPORT ||
|
|
+ errno == EAFNOSUPPORT)
|
|
+ return 0; /* No audit support in kernel */
|
|
+ error("Error connecting to audit system.");
|
|
+ return rc;
|
|
+ }
|
|
+ if (selinux_trans_to_raw_context(default_context, &default_raw) < 0) {
|
|
+ error("Error translating default context.");
|
|
+ default_raw = NULL;
|
|
+ }
|
|
+ if (selinux_trans_to_raw_context(selected_context, &selected_raw) < 0) {
|
|
+ error("Error translating selected context.");
|
|
+ selected_raw = NULL;
|
|
+ }
|
|
+ if (asprintf(&msg, "sshd: default-context=%s selected-context=%s",
|
|
+ default_raw ? default_raw : (default_context ? default_context: "?"),
|
|
+ selected_context ? selected_raw : (selected_context ? selected_context :"?")) < 0) {
|
|
+ error("Error allocating memory.");
|
|
+ goto out;
|
|
+ }
|
|
+ if (audit_log_user_message(audit_fd, AUDIT_USER_ROLE_CHANGE,
|
|
+ msg, NULL, NULL, NULL, success) <= 0) {
|
|
+ error("Error sending audit message.");
|
|
+ goto out;
|
|
+ }
|
|
+ rc = 0;
|
|
+ out:
|
|
+ free(msg);
|
|
+ freecon(default_raw);
|
|
+ freecon(selected_raw);
|
|
+ close(audit_fd);
|
|
+#endif
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+static int
|
|
+mls_range_allowed(security_context_t src, security_context_t dst)
|
|
+{
|
|
+ struct av_decision avd;
|
|
+ int retval;
|
|
+ access_vector_t bit;
|
|
+ security_class_t class;
|
|
+
|
|
+ debug_f("src:%s dst:%s", src, dst);
|
|
+ class = string_to_security_class("context");
|
|
+ if (!class) {
|
|
+ error("string_to_security_class failed to translate security class context");
|
|
+ return 1;
|
|
+ }
|
|
+ bit = string_to_av_perm(class, "contains");
|
|
+ if (!bit) {
|
|
+ error("string_to_av_perm failed to translate av perm contains");
|
|
+ return 1;
|
|
+ }
|
|
+ retval = security_compute_av(src, dst, class, bit, &avd);
|
|
+ if (retval || ((bit & avd.allowed) != bit))
|
|
+ return 0;
|
|
+
|
|
+ return 1;
|
|
+}
|
|
+
|
|
+static int
|
|
+get_user_context(const char *sename, const char *role, const char *lvl,
|
|
+ security_context_t *sc) {
|
|
+#ifdef HAVE_GET_DEFAULT_CONTEXT_WITH_LEVEL
|
|
+ if (lvl == NULL || lvl[0] == '\0' || get_default_context_with_level(sename, lvl, NULL, sc) != 0) {
|
|
+ /* User may have requested a level completely outside of his
|
|
+ allowed range. We get a context just for auditing as the
|
|
+ range check below will certainly fail for default context. */
|
|
+#endif
|
|
+ if (get_default_context(sename, NULL, sc) != 0) {
|
|
+ *sc = NULL;
|
|
+ return -1;
|
|
+ }
|
|
+#ifdef HAVE_GET_DEFAULT_CONTEXT_WITH_LEVEL
|
|
+ }
|
|
+#endif
|
|
+ if (role != NULL && role[0]) {
|
|
+ context_t con;
|
|
+ char *type=NULL;
|
|
+ if (get_default_type(role, &type) != 0) {
|
|
+ error("get_default_type: failed to get default type for '%s'",
|
|
+ role);
|
|
+ goto out;
|
|
+ }
|
|
+ con = context_new(*sc);
|
|
+ if (!con) {
|
|
+ goto out;
|
|
+ }
|
|
+ context_role_set(con, role);
|
|
+ context_type_set(con, type);
|
|
+ freecon(*sc);
|
|
+ *sc = strdup(context_str(con));
|
|
+ context_free(con);
|
|
+ if (!*sc)
|
|
+ return -1;
|
|
+ }
|
|
+#ifdef HAVE_GET_DEFAULT_CONTEXT_WITH_LEVEL
|
|
+ if (lvl != NULL && lvl[0]) {
|
|
+ /* verify that the requested range is obtained */
|
|
+ context_t con;
|
|
+ security_context_t obtained_raw;
|
|
+ security_context_t requested_raw;
|
|
+ con = context_new(*sc);
|
|
+ if (!con) {
|
|
+ goto out;
|
|
+ }
|
|
+ context_range_set(con, lvl);
|
|
+ if (selinux_trans_to_raw_context(*sc, &obtained_raw) < 0) {
|
|
+ context_free(con);
|
|
+ goto out;
|
|
+ }
|
|
+ if (selinux_trans_to_raw_context(context_str(con), &requested_raw) < 0) {
|
|
+ freecon(obtained_raw);
|
|
+ context_free(con);
|
|
+ goto out;
|
|
+ }
|
|
+
|
|
+ debug("get_user_context: obtained context '%s' requested context '%s'",
|
|
+ obtained_raw, requested_raw);
|
|
+ if (strcmp(obtained_raw, requested_raw)) {
|
|
+ /* set the context to the real requested one but fail */
|
|
+ freecon(requested_raw);
|
|
+ freecon(obtained_raw);
|
|
+ freecon(*sc);
|
|
+ *sc = strdup(context_str(con));
|
|
+ context_free(con);
|
|
+ return -1;
|
|
+ }
|
|
+ freecon(requested_raw);
|
|
+ freecon(obtained_raw);
|
|
+ context_free(con);
|
|
+ }
|
|
+#endif
|
|
+ return 0;
|
|
+ out:
|
|
+ freecon(*sc);
|
|
+ *sc = NULL;
|
|
+ return -1;
|
|
+}
|
|
+
|
|
+static void
|
|
+ssh_selinux_get_role_level(char **role, const char **level)
|
|
+{
|
|
+ *role = NULL;
|
|
+ *level = NULL;
|
|
+ if (the_authctxt) {
|
|
+ if (the_authctxt->role != NULL) {
|
|
+ char *slash;
|
|
+ *role = xstrdup(the_authctxt->role);
|
|
+ if ((slash = strchr(*role, '/')) != NULL) {
|
|
+ *slash = '\0';
|
|
+ *level = slash + 1;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+}
|
|
+
|
|
+/* Return the default security context for the given username */
|
|
+static int
|
|
+sshd_selinux_getctxbyname(char *pwname,
|
|
+ security_context_t *default_sc, security_context_t *user_sc)
|
|
+{
|
|
+ char *sename, *lvl;
|
|
+ char *role;
|
|
+ const char *reqlvl;
|
|
+ int r = 0;
|
|
+ context_t con = NULL;
|
|
+
|
|
+ ssh_selinux_get_role_level(&role, &reqlvl);
|
|
+
|
|
+#ifdef HAVE_GETSEUSERBYNAME
|
|
+ if ((r=getseuserbyname(pwname, &sename, &lvl)) != 0) {
|
|
+ sename = NULL;
|
|
+ lvl = NULL;
|
|
+ }
|
|
+#else
|
|
+ sename = pwname;
|
|
+ lvl = "";
|
|
+#endif
|
|
+
|
|
+ if (r == 0) {
|
|
+#ifdef HAVE_GET_DEFAULT_CONTEXT_WITH_LEVEL
|
|
+ r = get_default_context_with_level(sename, lvl, NULL, default_sc);
|
|
+#else
|
|
+ r = get_default_context(sename, NULL, default_sc);
|
|
+#endif
|
|
+ }
|
|
+
|
|
+ if (r == 0) {
|
|
+ /* If launched from xinetd, we must use current level */
|
|
+ if (inetd_flag && !rexeced_flag) {
|
|
+ security_context_t sshdsc=NULL;
|
|
+
|
|
+ if (getcon_raw(&sshdsc) < 0)
|
|
+ fatal("failed to allocate security context");
|
|
+
|
|
+ if ((con=context_new(sshdsc)) == NULL)
|
|
+ fatal("failed to allocate selinux context");
|
|
+ reqlvl = context_range_get(con);
|
|
+ freecon(sshdsc);
|
|
+ if (reqlvl !=NULL && lvl != NULL && strcmp(reqlvl, lvl) == 0)
|
|
+ /* we actually don't change level */
|
|
+ reqlvl = "";
|
|
+
|
|
+ debug_f("current connection level '%s'", reqlvl);
|
|
+
|
|
+ }
|
|
+
|
|
+ if ((reqlvl != NULL && reqlvl[0]) || (role != NULL && role[0])) {
|
|
+ r = get_user_context(sename, role, reqlvl, user_sc);
|
|
+
|
|
+ if (r == 0 && reqlvl != NULL && reqlvl[0]) {
|
|
+ security_context_t default_level_sc = *default_sc;
|
|
+ if (role != NULL && role[0]) {
|
|
+ if (get_user_context(sename, role, lvl, &default_level_sc) < 0)
|
|
+ default_level_sc = *default_sc;
|
|
+ }
|
|
+ /* verify that the requested range is contained in the user range */
|
|
+ if (mls_range_allowed(default_level_sc, *user_sc)) {
|
|
+ logit("permit MLS level %s (user range %s)", reqlvl, lvl);
|
|
+ } else {
|
|
+ r = -1;
|
|
+ error("deny MLS level %s (user range %s)", reqlvl, lvl);
|
|
+ }
|
|
+ if (default_level_sc != *default_sc)
|
|
+ freecon(default_level_sc);
|
|
+ }
|
|
+ } else {
|
|
+ *user_sc = *default_sc;
|
|
+ }
|
|
+ }
|
|
+ if (r != 0) {
|
|
+ error_f("Failed to get default SELinux security "
|
|
+ "context for %s", pwname);
|
|
+ }
|
|
+
|
|
+#ifdef HAVE_GETSEUSERBYNAME
|
|
+ free(sename);
|
|
+ free(lvl);
|
|
+#endif
|
|
+
|
|
+ if (role != NULL)
|
|
+ free(role);
|
|
+ if (con)
|
|
+ context_free(con);
|
|
+
|
|
+ return (r);
|
|
+}
|
|
+
|
|
+/* Setup environment variables for pam_selinux */
|
|
+static int
|
|
+sshd_selinux_setup_pam_variables(void)
|
|
+{
|
|
+ const char *reqlvl;
|
|
+ char *role;
|
|
+ char *use_current;
|
|
+ int rv;
|
|
+
|
|
+ debug3_f("setting execution context");
|
|
+
|
|
+ ssh_selinux_get_role_level(&role, &reqlvl);
|
|
+
|
|
+ rv = do_pam_putenv("SELINUX_ROLE_REQUESTED", role ? role : "");
|
|
+
|
|
+ if (inetd_flag && !rexeced_flag) {
|
|
+ use_current = "1";
|
|
+ } else {
|
|
+ use_current = "";
|
|
+ rv = rv || do_pam_putenv("SELINUX_LEVEL_REQUESTED", reqlvl ? reqlvl: "");
|
|
+ }
|
|
+
|
|
+ rv = rv || do_pam_putenv("SELINUX_USE_CURRENT_RANGE", use_current);
|
|
+
|
|
+ if (role != NULL)
|
|
+ free(role);
|
|
+
|
|
+ return rv;
|
|
+}
|
|
+
|
|
+/* Set the execution context to the default for the specified user */
|
|
+void
|
|
+sshd_selinux_setup_exec_context(char *pwname)
|
|
+{
|
|
+ security_context_t user_ctx = NULL;
|
|
+ int r = 0;
|
|
+ security_context_t default_ctx = NULL;
|
|
+
|
|
+ if (!ssh_selinux_enabled())
|
|
+ return;
|
|
+
|
|
+ if (options.use_pam) {
|
|
+ /* do not compute context, just setup environment for pam_selinux */
|
|
+ if (sshd_selinux_setup_pam_variables()) {
|
|
+ switch (security_getenforce()) {
|
|
+ case -1:
|
|
+ fatal_f("security_getenforce() failed");
|
|
+ case 0:
|
|
+ error_f("SELinux PAM variable setup failure. Continuing in permissive mode.");
|
|
+ break;
|
|
+ default:
|
|
+ fatal_f("SELinux PAM variable setup failure. Aborting connection.");
|
|
+ }
|
|
+ }
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ debug3_f("setting execution context");
|
|
+
|
|
+ r = sshd_selinux_getctxbyname(pwname, &default_ctx, &user_ctx);
|
|
+ if (r >= 0) {
|
|
+ r = setexeccon(user_ctx);
|
|
+ if (r < 0) {
|
|
+ error_f("Failed to set SELinux execution context %s for %s",
|
|
+ user_ctx, pwname);
|
|
+ }
|
|
+#ifdef HAVE_SETKEYCREATECON
|
|
+ else if (setkeycreatecon(user_ctx) < 0) {
|
|
+ error_f("Failed to set SELinux keyring creation context %s for %s",
|
|
+ user_ctx, pwname);
|
|
+ }
|
|
+#endif
|
|
+ }
|
|
+ if (user_ctx == NULL) {
|
|
+ user_ctx = default_ctx;
|
|
+ }
|
|
+ if (r < 0 || user_ctx != default_ctx) {
|
|
+ /* audit just the case when user changed a role or there was
|
|
+ a failure */
|
|
+ sshd_selinux_send_audit_message(r >= 0, default_ctx, user_ctx);
|
|
+ }
|
|
+ if (r < 0) {
|
|
+ switch (security_getenforce()) {
|
|
+ case -1:
|
|
+ fatal_f("security_getenforce() failed");
|
|
+ case 0:
|
|
+ error_f("ELinux failure. Continuing in permissive mode.");
|
|
+ break;
|
|
+ default:
|
|
+ fatal_f("SELinux failure. Aborting connection.");
|
|
+ }
|
|
+ }
|
|
+ if (user_ctx != NULL && user_ctx != default_ctx)
|
|
+ freecon(user_ctx);
|
|
+ if (default_ctx != NULL)
|
|
+ freecon(default_ctx);
|
|
+
|
|
+ debug3_f("done");
|
|
+}
|
|
+
|
|
+#endif
|
|
+#endif
|
|
+
|
|
diff -up openssh/platform.c.role-mls openssh/platform.c
|
|
--- openssh/platform.c.role-mls 2018-08-20 07:57:29.000000000 +0200
|
|
+++ openssh/platform.c 2018-08-22 11:14:56.819430949 +0200
|
|
@@ -183,7 +183,7 @@ platform_setusercontext_post_groups(stru
|
|
}
|
|
#endif /* HAVE_SETPCRED */
|
|
#ifdef WITH_SELINUX
|
|
- ssh_selinux_setup_exec_context(pw->pw_name);
|
|
+ sshd_selinux_setup_exec_context(pw->pw_name);
|
|
#endif
|
|
}
|
|
|
|
diff -up openssh/sshd.c.role-mls openssh/sshd.c
|
|
--- openssh/sshd.c.role-mls 2018-08-20 07:57:29.000000000 +0200
|
|
+++ openssh/sshd.c 2018-08-22 11:14:56.820430957 +0200
|
|
@@ -2186,6 +2186,9 @@ main(int ac, char **av)
|
|
restore_uid();
|
|
}
|
|
#endif
|
|
+#ifdef WITH_SELINUX
|
|
+ sshd_selinux_setup_exec_context(authctxt->pw->pw_name);
|
|
+#endif
|
|
#ifdef USE_PAM
|
|
if (options.use_pam) {
|
|
do_pam_setcred(1);
|