b3ff99ae3c
- Update to openssh 9.6p1: * No changes for askpass, see main package changelog for details. - Update to openssh 9.6p1: = Security * ssh(1), sshd(8): implement protocol extensions to thwart the so-called "Terrapin attack" discovered by Fabian Bäumer, Marcus Brinkmann and Jörg Schwenk. This attack allows a MITM to effect a limited break of the integrity of the early encrypted SSH transport protocol by sending extra messages prior to the commencement of encryption, and deleting an equal number of consecutive messages immediately after encryption starts. A peer SSH client/server would not be able to detect that messages were deleted. * ssh-agent(1): when adding PKCS#11-hosted private keys while specifying destination constraints, if the PKCS#11 token returned multiple keys then only the first key had the constraints applied. Use of regular private keys, FIDO tokens and unconstrained keys are unaffected. * ssh(1): if an invalid user or hostname that contained shell metacharacters was passed to ssh(1), and a ProxyCommand, LocalCommand directive or "match exec" predicate referenced the user or hostname via %u, %h or similar expansion token, then an attacker who could supply arbitrary user/hostnames to ssh(1) could potentially perform command injection depending on what quoting was present in the user-supplied ssh_config(5) directive. = Potentially incompatible changes * ssh(1), sshd(8): the RFC4254 connection/channels protocol provides a TCP-like window mechanism that limits the amount of data that can be sent without acceptance from the peer. In cases where this OBS-URL: https://build.opensuse.org/request/show/1150500 OBS-URL: https://build.opensuse.org/package/show/network/openssh?expand=0&rev=255
886 lines
26 KiB
Diff
886 lines
26 KiB
Diff
Index: openssh-9.6p1/auth2.c
|
|
===================================================================
|
|
--- openssh-9.6p1.orig/auth2.c
|
|
+++ openssh-9.6p1/auth2.c
|
|
@@ -273,6 +273,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();
|
|
|
|
@@ -286,6 +289,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;
|
|
|
|
@@ -313,8 +321,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 ((r = kex_server_update_ext_info(ssh)) != 0)
|
|
fatal_fr(r, "kex_server_update_ext_info failed");
|
|
Index: openssh-9.6p1/auth2-gss.c
|
|
===================================================================
|
|
--- openssh-9.6p1.orig/auth2-gss.c
|
|
+++ openssh-9.6p1/auth2-gss.c
|
|
@@ -331,6 +331,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;
|
|
@@ -348,7 +349,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)
|
|
@@ -362,6 +369,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()) &&
|
|
Index: openssh-9.6p1/auth2-hostbased.c
|
|
===================================================================
|
|
--- openssh-9.6p1.orig/auth2-hostbased.c
|
|
+++ openssh-9.6p1/auth2-hostbased.c
|
|
@@ -128,7 +128,16 @@ userauth_hostbased(struct ssh *ssh, cons
|
|
/* 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, method)) != 0 ||
|
|
(r = sshbuf_put_string(b, pkalg, alen)) != 0 ||
|
|
Index: openssh-9.6p1/auth2-pubkey.c
|
|
===================================================================
|
|
--- openssh-9.6p1.orig/auth2-pubkey.c
|
|
+++ openssh-9.6p1/auth2-pubkey.c
|
|
@@ -200,9 +200,16 @@ userauth_pubkey(struct ssh *ssh, const c
|
|
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 ||
|
|
Index: openssh-9.6p1/auth.h
|
|
===================================================================
|
|
--- openssh-9.6p1.orig/auth.h
|
|
+++ openssh-9.6p1/auth.h
|
|
@@ -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 */
|
|
Index: openssh-9.6p1/auth-pam.c
|
|
===================================================================
|
|
--- openssh-9.6p1.orig/auth-pam.c
|
|
+++ openssh-9.6p1/auth-pam.c
|
|
@@ -1242,7 +1242,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;
|
|
Index: openssh-9.6p1/auth-pam.h
|
|
===================================================================
|
|
--- openssh-9.6p1.orig/auth-pam.h
|
|
+++ openssh-9.6p1/auth-pam.h
|
|
@@ -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 **);
|
|
Index: openssh-9.6p1/misc.c
|
|
===================================================================
|
|
--- openssh-9.6p1.orig/misc.c
|
|
+++ openssh-9.6p1/misc.c
|
|
@@ -771,6 +771,7 @@ char *
|
|
colon(char *cp)
|
|
{
|
|
int flag = 0;
|
|
+ int start = 1;
|
|
|
|
if (*cp == ':') /* Leading colon is part of file name. */
|
|
return NULL;
|
|
@@ -786,6 +787,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;
|
|
}
|
|
Index: openssh-9.6p1/monitor.c
|
|
===================================================================
|
|
--- openssh-9.6p1.orig/monitor.c
|
|
+++ openssh-9.6p1/monitor.c
|
|
@@ -120,6 +120,9 @@ int mm_answer_sign(struct ssh *, int, st
|
|
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 *);
|
|
@@ -200,6 +203,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
|
|
@@ -834,6 +840,9 @@ mm_answer_pwnamallow(struct ssh *ssh, in
|
|
|
|
/* 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
|
|
@@ -908,6 +917,26 @@ key_base_type_match(const char *method,
|
|
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)
|
|
{
|
|
@@ -1280,7 +1309,7 @@ monitor_valid_userblob(struct ssh *ssh,
|
|
struct sshbuf *b;
|
|
struct sshkey *hostkey = NULL;
|
|
const u_char *p;
|
|
- char *userstyle, *cp;
|
|
+ char *userstyle, *s, *cp;
|
|
size_t len;
|
|
u_char type;
|
|
int hostbound = 0, r, fail = 0;
|
|
@@ -1311,6 +1340,8 @@ monitor_valid_userblob(struct ssh *ssh,
|
|
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 : "");
|
|
@@ -1361,7 +1392,7 @@ monitor_valid_hostbasedblob(const u_char
|
|
{
|
|
struct sshbuf *b;
|
|
const u_char *p;
|
|
- char *cp, *userstyle;
|
|
+ char *cp, *s, *userstyle;
|
|
size_t len;
|
|
int r, fail = 0;
|
|
u_char type;
|
|
@@ -1382,6 +1413,8 @@ monitor_valid_hostbasedblob(const u_char
|
|
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 : "");
|
|
Index: openssh-9.6p1/monitor.h
|
|
===================================================================
|
|
--- openssh-9.6p1.orig/monitor.h
|
|
+++ openssh-9.6p1/monitor.h
|
|
@@ -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,
|
|
Index: openssh-9.6p1/monitor_wrap.c
|
|
===================================================================
|
|
--- openssh-9.6p1.orig/monitor_wrap.c
|
|
+++ openssh-9.6p1/monitor_wrap.c
|
|
@@ -396,6 +396,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)
|
|
Index: openssh-9.6p1/monitor_wrap.h
|
|
===================================================================
|
|
--- openssh-9.6p1.orig/monitor_wrap.h
|
|
+++ openssh-9.6p1/monitor_wrap.h
|
|
@@ -49,6 +49,9 @@ int mm_sshkey_sign(struct ssh *, struct
|
|
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 *);
|
|
Index: openssh-9.6p1/openbsd-compat/Makefile.in
|
|
===================================================================
|
|
--- openssh-9.6p1.orig/openbsd-compat/Makefile.in
|
|
+++ openssh-9.6p1/openbsd-compat/Makefile.in
|
|
@@ -100,7 +100,8 @@ PORTS= port-aix.o \
|
|
port-prngd.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 $<
|
|
Index: openssh-9.6p1/openbsd-compat/port-linux.c
|
|
===================================================================
|
|
--- openssh-9.6p1.orig/openbsd-compat/port-linux.c
|
|
+++ openssh-9.6p1/openbsd-compat/port-linux.c
|
|
@@ -101,37 +101,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)
|
|
@@ -144,7 +113,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? */
|
|
|
|
Index: openssh-9.6p1/openbsd-compat/port-linux.h
|
|
===================================================================
|
|
--- openssh-9.6p1.orig/openbsd-compat/port-linux.h
|
|
+++ openssh-9.6p1/openbsd-compat/port-linux.h
|
|
@@ -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
|
|
Index: openssh-9.6p1/openbsd-compat/port-linux-sshd.c
|
|
===================================================================
|
|
--- /dev/null
|
|
+++ openssh-9.6p1/openbsd-compat/port-linux-sshd.c
|
|
@@ -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
|
|
+
|
|
Index: openssh-9.6p1/platform.c
|
|
===================================================================
|
|
--- openssh-9.6p1.orig/platform.c
|
|
+++ openssh-9.6p1/platform.c
|
|
@@ -185,7 +185,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
|
|
}
|
|
|
|
Index: openssh-9.6p1/sshd.c
|
|
===================================================================
|
|
--- openssh-9.6p1.orig/sshd.c
|
|
+++ openssh-9.6p1/sshd.c
|
|
@@ -2387,6 +2387,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);
|