Marcus Meissner
3f6eda5c88
* No changes for askpass, see main package changelog for details. - Update to openssh 9.9p1: = Future deprecation notice * OpenSSH plans to remove support for the DSA signature algorithm in early 2025. This release disables DSA by default at compile time. DSA, as specified in the SSHv2 protocol, is inherently weak - being limited to a 160 bit private key and use of the SHA1 digest. Its estimated security level is only 80 bits symmetric equivalent. OpenSSH has disabled DSA keys by default since 2015 but has retained run-time optional support for them. DSA was the only mandatory-to-implement algorithm in the SSHv2 RFCs, mostly because alternative algorithms were encumbered by patents when the SSHv2 protocol was specified. This has not been the case for decades at this point and better algorithms are well supported by all actively-maintained SSH implementations. We do not consider the costs of maintaining DSA in OpenSSH to be justified and hope that removing it from OpenSSH can accelerate its wider deprecation in supporting cryptography libraries. = Potentially-incompatible changes * ssh(1): remove support for pre-authentication compression. OpenSSH has only supported post-authentication compression in the server for some years. Compression before authentication significantly increases the attack surface of SSH servers and risks creating oracles that reveal information about information sent during authentication. OBS-URL: https://build.opensuse.org/package/show/network/openssh?expand=0&rev=275
864 lines
26 KiB
Diff
864 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;
|
|
|
|
@@ -314,7 +314,13 @@ input_userauth_request(int type, u_int32
|
|
setproctitle("%s [net]", authctxt->valid ? user : "unknown");
|
|
authctxt->service = xstrdup(service);
|
|
authctxt->style = style ? xstrdup(style) : NULL;
|
|
+#ifdef WITH_SELINUX
|
|
+ authctxt->role = role ? xstrdup(role) : NULL;
|
|
+#endif
|
|
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");
|
|
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;
|
|
u_char *p;
|
|
@@ -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);
|
|
|
|
authctxt->postponed = 0;
|
|
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, method)) != 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(void);
|
|
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-8.6p1/monitor.c.role-mls openssh-8.6p1/monitor.c
|
|
--- openssh-8.6p1/monitor.c.role-mls 2021-04-16 05:55:25.000000000 +0200
|
|
+++ openssh-8.6p1/monitor.c 2021-05-21 14:21:56.719414087 +0200
|
|
@@ -117,6 +117,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 *);
|
|
@@ -195,6 +198,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
|
|
@@ -803,6 +809,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
|
|
@@ -877,6 +886,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)
|
|
{
|
|
@@ -1251,7 +1280,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;
|
|
@@ -1282,6 +1311,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 : "");
|
|
@@ -1317,7 +1348,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;
|
|
@@ -1338,6 +1370,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 : "");
|
|
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-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 $<
|
|
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,420 @@
|
|
+/*
|
|
+ * 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;
|
|
+
|
|
+/* 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) {
|
|
+ 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) {
|
|
+ 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-session.c.role-mls 2018-08-20 07:57:29.000000000 +0200
|
|
+++ openssh/sshd-session.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();
|