openssh: simplify login class restrictions

Login class-based restrictions were introduced in 5b400a39b8.  The
code was adapted for sshd's Capsicum sandbox and received many changes
over time, including at least fc3c19a9fc, bd393de91c, and
e8c56fba29.

During an attempt to upstream the work a much simpler approach was
suggested.  Adopt it now in the in-tree OpenSSH to reduce conflicts with
future updates.

Submitted by:	Yuchiro Naito (against OpenSSH-portable on GitHub)
Obtained from:	https://github.com/openssh/openssh-portable/pull/262
Reviewed by:	allanjude, kevans
MFC after:	2 weeks
Differential Revision:	https://reviews.freebsd.org/D31760
This commit is contained in:
Ed Maste 2021-08-31 15:30:50 -04:00
parent c511383de7
commit 27ceebbc24
11 changed files with 58 additions and 250 deletions

View file

@ -568,6 +568,9 @@ getpwnamallow(const char *user)
struct ssh *ssh = active_state; /* XXX */
#ifdef HAVE_LOGIN_CAP
extern login_cap_t *lc;
#ifdef HAVE_AUTH_HOSTOK
const char *from_host, *from_ip;
#endif
#ifdef BSD_AUTH
auth_session_t *as;
#endif
@ -622,6 +625,21 @@ getpwnamallow(const char *user)
debug("unable to get login class: %s", user);
return (NULL);
}
#ifdef HAVE_AUTH_HOSTOK
from_host = auth_get_canonical_hostname(ssh, options.use_dns);
from_ip = ssh_remote_ipaddr(ssh);
if (!auth_hostok(lc, from_host, from_ip)) {
debug("Denied connection for %.200s from %.200s [%.200s].",
pw->pw_name, from_host, from_ip);
return (NULL);
}
#endif /* HAVE_AUTH_HOSTOK */
#ifdef HAVE_AUTH_TIMEOK
if (!auth_timeok(lc, time(NULL))) {
debug("LOGIN %.200s REFUSED (TIME)", pw->pw_name);
return (NULL);
}
#endif /* HAVE_AUTH_TIMEOK */
#ifdef BSD_AUTH
if ((as = auth_open()) == NULL || auth_setpwd(as, pw) != 0 ||
auth_approval(as, lc, pw->pw_name, "ssh") <= 0) {

View file

@ -314,25 +314,6 @@ input_userauth_request(int type, u_int32_t seq, struct ssh *ssh)
authctxt->user, authctxt->service, user, service);
}
#ifdef HAVE_LOGIN_CAP
if (authctxt->pw != NULL &&
(lc = PRIVSEP(login_getpwclass(authctxt->pw))) != NULL) {
from_host = auth_get_canonical_hostname(ssh, options.use_dns);
from_ip = ssh_remote_ipaddr(ssh);
if (!auth_hostok(lc, from_host, from_ip)) {
logit("Denied connection for %.200s from %.200s [%.200s].",
authctxt->pw->pw_name, from_host, from_ip);
packet_disconnect("Sorry, you are not allowed to connect.");
}
if (!auth_timeok(lc, time(NULL))) {
logit("LOGIN %.200s REFUSED (TIME) FROM %.200s",
authctxt->pw->pw_name, from_host);
packet_disconnect("Logins not available right now.");
}
PRIVSEP(login_close(lc));
}
#endif /* HAVE_LOGIN_CAP */
/* reset state */
auth2_challenge_stop(ssh);

View file

@ -211,6 +211,12 @@
/* Define to 1 if you have the `aug_get_machine' function. */
/* #undef HAVE_AUG_GET_MACHINE */
/* Define to 1 if you have the `auth_hostok' function. */
#define HAVE_AUTH_HOSTOK 1
/* Define to 1 if you have the `auth_timeok' function. */
#define HAVE_AUTH_TIMEOK 1
/* Define to 1 if you have the `b64_ntop' function. */
/* #undef HAVE_B64_NTOP */

View file

@ -1751,6 +1751,8 @@ fi
dnl Checks for library functions. Please keep in alphabetical order
AC_CHECK_FUNCS([ \
auth_hostok \
auth_timeok \
Blowfish_initstate \
Blowfish_expandstate \
Blowfish_expand0state \

View file

@ -114,9 +114,6 @@ static struct sshbuf *child_state;
int mm_answer_moduli(int, struct sshbuf *);
int mm_answer_sign(int, struct sshbuf *);
#ifdef HAVE_LOGIN_CAP
int mm_answer_login_getpwclass(int, struct sshbuf *);
#endif
int mm_answer_pwnamallow(int, struct sshbuf *);
int mm_answer_auth2_read_banner(int, struct sshbuf *);
int mm_answer_authserv(int, struct sshbuf *);
@ -192,9 +189,6 @@ struct mon_table mon_dispatch_proto20[] = {
{MONITOR_REQ_MODULI, MON_ONCE, mm_answer_moduli},
#endif
{MONITOR_REQ_SIGN, MON_ONCE, mm_answer_sign},
#ifdef HAVE_LOGIN_CAP
{MONITOR_REQ_GETPWCLASS, MON_ISAUTH, mm_answer_login_getpwclass},
#endif
{MONITOR_REQ_PWNAM, MON_ONCE, mm_answer_pwnamallow},
{MONITOR_REQ_AUTHSERV, MON_ONCE, mm_answer_authserv},
{MONITOR_REQ_AUTH2_READ_BANNER, MON_ONCE, mm_answer_auth2_read_banner},
@ -713,48 +707,6 @@ mm_answer_sign(int sock, struct sshbuf *m)
return (0);
}
#ifdef HAVE_LOGIN_CAP
int
mm_answer_login_getpwclass(int sock, struct sshbuf *m)
{
login_cap_t *lc;
struct passwd *pw;
int r;
u_int len;
debug3("%s", __func__);
pw = sshbuf_get_passwd(m);
if (pw == NULL)
fatal("%s: receive get struct passwd failed", __func__);
lc = login_getpwclass(pw);
sshbuf_reset(m);
if (lc == NULL) {
if (r = sshbuf_put_u8(m, 0) != 0)
fatal("%s: buffer error: %s", __func__, ssh_err(r));
goto out;
}
if ((r = sshbuf_put_u8(m, 1)) != 0 ||
(r = sshbuf_put_cstring(m, lc->lc_class)) != 0 ||
(r = sshbuf_put_cstring(m, lc->lc_cap)) != 0 ||
(r = sshbuf_put_cstring(m, lc->lc_style)) != 0)
fatal("%s: buffer error: %s", __func__, ssh_err(r));
login_close(lc);
out:
debug3("%s: sending MONITOR_ANS_GETPWCLASS", __func__);
mm_request_send(sock, MONITOR_ANS_GETPWCLASS, m);
sshbuf_free_passwd(pw);
return (0);
}
#endif
/* Retrieves the password entry and also checks if the user is permitted */
int
@ -793,8 +745,19 @@ mm_answer_pwnamallow(int sock, struct sshbuf *m)
authctxt->pw = pwent;
authctxt->valid = 1;
/* XXX don't sent pwent to unpriv; send fake class/dir/shell too */
if ((r = sshbuf_put_u8(m, 1)) != 0 ||
(r = sshbuf_put_passwd(m, pwent)) != 0)
(r = sshbuf_put_string(m, pwent, sizeof(*pwent))) != 0 ||
(r = sshbuf_put_cstring(m, pwent->pw_name)) != 0 ||
(r = sshbuf_put_cstring(m, "*")) != 0 ||
#ifdef HAVE_STRUCT_PASSWD_PW_GECOS
(r = sshbuf_put_cstring(m, pwent->pw_gecos)) != 0 ||
#endif
#ifdef HAVE_STRUCT_PASSWD_PW_CLASS
(r = sshbuf_put_cstring(m, pwent->pw_class)) != 0 ||
#endif
(r = sshbuf_put_cstring(m, pwent->pw_dir)) != 0 ||
(r = sshbuf_put_cstring(m, pwent->pw_shell)) != 0)
fatal("%s: buffer error: %s", __func__, ssh_err(r));
out:

View file

@ -53,8 +53,7 @@ enum monitor_reqtype {
MONITOR_REQ_GSSSTEP = 44, MONITOR_ANS_GSSSTEP = 45,
MONITOR_REQ_GSSUSEROK = 46, MONITOR_ANS_GSSUSEROK = 47,
MONITOR_REQ_GSSCHECKMIC = 48, MONITOR_ANS_GSSCHECKMIC = 49,
MONITOR_REQ_GETPWCLASS = 50, MONITOR_ANS_GETPWCLASS = 51,
MONITOR_REQ_TERM = 52,
MONITOR_REQ_TERM = 50,
MONITOR_REQ_PAM_START = 100,
MONITOR_REQ_PAM_ACCOUNT = 102, MONITOR_ANS_PAM_ACCOUNT = 103,

View file

@ -247,61 +247,6 @@ mm_sshkey_sign(struct sshkey *key, u_char **sigp, size_t *lenp,
return (0);
}
#ifdef HAVE_LOGIN_CAP
login_cap_t *
mm_login_getpwclass(const struct passwd *pwent)
{
int r;
struct sshbuf *m;
char rc;
login_cap_t *lc;
debug3("%s entering", __func__);
if ((m = sshbuf_new()) == NULL)
fatal("%s: sshbuf_new failed", __func__);
if ((r = sshbuf_put_passwd(m, pwent)) != 0)
fatal("%s: buffer error: %s", __func__, ssh_err(r));
mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_GETPWCLASS, m);
debug3("%s: waiting for MONITOR_ANS_GETPWCLASS", __func__);
mm_request_receive_expect(pmonitor->m_recvfd, MONITOR_ANS_GETPWCLASS, m);
if ((r = sshbuf_get_u8(m, &rc)) != 0)
fatal("%s: buffer error: %s", __func__, ssh_err(r));
if (rc == 0) {
lc = NULL;
goto out;
}
lc = xmalloc(sizeof(*lc));
if ((r = sshbuf_get_cstring(m, &lc->lc_class, NULL)) != 0 ||
(r = sshbuf_get_cstring(m, &lc->lc_cap, NULL)) != 0 ||
(r = sshbuf_get_cstring(m, &lc->lc_style, NULL)) != 0)
fatal("%s: buffer error: %s", __func__, ssh_err(r));
out:
sshbuf_free(m);
return (lc);
}
#endif
#ifdef HAVE_LOGIN_CAP
void
mm_login_close(login_cap_t *lc)
{
if (lc == NULL)
return;
free(lc->lc_style);
free(lc->lc_class);
free(lc->lc_cap);
free(lc);
}
#endif
struct passwd *
mm_getpwnamallow(const char *username)
{
@ -334,9 +279,25 @@ mm_getpwnamallow(const char *username)
goto out;
}
pw = sshbuf_get_passwd(m);
if (pw == NULL)
fatal("%s: receive get struct passwd failed", __func__);
/* XXX don't like passing struct passwd like this */
pw = xcalloc(sizeof(*pw), 1);
if ((r = sshbuf_get_string_direct(m, &p, &len)) != 0)
fatal("%s: buffer error: %s", __func__, ssh_err(r));
if (len != sizeof(*pw))
fatal("%s: struct passwd size mismatch", __func__);
memcpy(pw, p, sizeof(*pw));
if ((r = sshbuf_get_cstring(m, &pw->pw_name, NULL)) != 0 ||
(r = sshbuf_get_cstring(m, &pw->pw_passwd, NULL)) != 0 ||
#ifdef HAVE_STRUCT_PASSWD_PW_GECOS
(r = sshbuf_get_cstring(m, &pw->pw_gecos, NULL)) != 0 ||
#endif
#ifdef HAVE_STRUCT_PASSWD_PW_CLASS
(r = sshbuf_get_cstring(m, &pw->pw_class, NULL)) != 0 ||
#endif
(r = sshbuf_get_cstring(m, &pw->pw_dir, NULL)) != 0 ||
(r = sshbuf_get_cstring(m, &pw->pw_shell, NULL)) != 0)
fatal("%s: buffer error: %s", __func__, ssh_err(r));
out:
/* copy options block as a Match directive may have changed some */

View file

@ -44,10 +44,6 @@ DH *mm_choose_dh(int, int, int);
int mm_sshkey_sign(struct sshkey *, u_char **, size_t *, const u_char *, size_t,
const char *, u_int compat);
void mm_inform_authserv(char *, char *);
#ifdef HAVE_LOGIN_CAP
struct login_cap *mm_login_getpwclass(const struct passwd *pwd);
void mm_login_close(struct login_cap *lc);
#endif
struct passwd *mm_getpwnamallow(const char *);
char *mm_auth2_read_banner(void);
int mm_auth_password(struct ssh *, char *);

View file

@ -657,7 +657,6 @@
#define sshbuf_dump_data Fssh_sshbuf_dump_data
#define sshbuf_dup_string Fssh_sshbuf_dup_string
#define sshbuf_free Fssh_sshbuf_free
#define sshbuf_free_passwd Fssh_sshbuf_free_passwd
#define sshbuf_from Fssh_sshbuf_from
#define sshbuf_fromb Fssh_sshbuf_fromb
#define sshbuf_froms Fssh_sshbuf_froms
@ -668,7 +667,6 @@
#define sshbuf_get_cstring Fssh_sshbuf_get_cstring
#define sshbuf_get_ec Fssh_sshbuf_get_ec
#define sshbuf_get_eckey Fssh_sshbuf_get_eckey
#define sshbuf_get_passwd Fssh_sshbuf_get_passwd
#define sshbuf_get_string Fssh_sshbuf_get_string
#define sshbuf_get_string_direct Fssh_sshbuf_get_string_direct
#define sshbuf_get_stringb Fssh_sshbuf_get_stringb
@ -690,7 +688,6 @@
#define sshbuf_put_cstring Fssh_sshbuf_put_cstring
#define sshbuf_put_ec Fssh_sshbuf_put_ec
#define sshbuf_put_eckey Fssh_sshbuf_put_eckey
#define sshbuf_put_passwd Fssh_sshbuf_put_passwd
#define sshbuf_put_string Fssh_sshbuf_put_string
#define sshbuf_put_stringb Fssh_sshbuf_put_stringb
#define sshbuf_put_u16 Fssh_sshbuf_put_u16

View file

@ -463,103 +463,3 @@ sshbuf_get_bignum2_bytes_direct(struct sshbuf *buf,
}
return 0;
}
/*
* store struct pwd
*/
int
sshbuf_put_passwd(struct sshbuf *buf, const struct passwd *pwent)
{
int r;
/*
* We never send pointer values of struct passwd.
* It is safe from wild pointer even if a new pointer member is added.
*/
if ((r = sshbuf_put_u64(buf, sizeof(*pwent)) != 0) ||
(r = sshbuf_put_cstring(buf, pwent->pw_name)) != 0 ||
(r = sshbuf_put_cstring(buf, "*")) != 0 ||
(r = sshbuf_put_u32(buf, pwent->pw_uid)) != 0 ||
(r = sshbuf_put_u32(buf, pwent->pw_gid)) != 0 ||
#ifdef HAVE_STRUCT_PASSWD_PW_CHANGE
(r = sshbuf_put_time(buf, pwent->pw_change)) != 0 ||
#endif
#ifdef HAVE_STRUCT_PASSWD_PW_GECOS
(r = sshbuf_put_cstring(buf, pwent->pw_gecos)) != 0 ||
#endif
#ifdef HAVE_STRUCT_PASSWD_PW_CLASS
(r = sshbuf_put_cstring(buf, pwent->pw_class)) != 0 ||
#endif
(r = sshbuf_put_cstring(buf, pwent->pw_dir)) != 0 ||
(r = sshbuf_put_cstring(buf, pwent->pw_shell)) != 0 ||
#ifdef HAVE_STRUCT_PASSWD_PW_EXPIRE
(r = sshbuf_put_time(buf, pwent->pw_expire)) != 0 ||
#endif
(r = sshbuf_put_u32(buf, pwent->pw_fields)) != 0) {
return r;
}
return 0;
}
/*
* extract struct pwd
*/
struct passwd *
sshbuf_get_passwd(struct sshbuf *buf)
{
struct passwd *pw;
u_int64_t len;
int r;
/* check if size of struct passwd is as same as sender's size */
r = sshbuf_get_u64(buf, &len);
if (r != 0 || len != sizeof(*pw))
return NULL;
pw = xcalloc(1, sizeof(*pw));
if (sshbuf_get_cstring(buf, &pw->pw_name, NULL) != 0 ||
sshbuf_get_cstring(buf, &pw->pw_passwd, NULL) != 0 ||
sshbuf_get_u32(buf, &pw->pw_uid) != 0 ||
sshbuf_get_u32(buf, &pw->pw_gid) != 0 ||
#ifdef HAVE_STRUCT_PASSWD_PW_CHANGE
sshbuf_get_time(buf, &pw->pw_change) != 0 ||
#endif
#ifdef HAVE_STRUCT_PASSWD_PW_GECOS
sshbuf_get_cstring(buf, &pw->pw_gecos, NULL) != 0 ||
#endif
#ifdef HAVE_STRUCT_PASSWD_PW_CLASS
sshbuf_get_cstring(buf, &pw->pw_class, NULL) != 0 ||
#endif
sshbuf_get_cstring(buf, &pw->pw_dir, NULL) != 0 ||
sshbuf_get_cstring(buf, &pw->pw_shell, NULL) != 0 ||
#ifdef HAVE_STRUCT_PASSWD_PW_EXPIRE
sshbuf_get_time(buf, &pw->pw_expire) != 0 ||
#endif
sshbuf_get_u32(buf, &pw->pw_fields) != 0) {
sshbuf_free_passwd(pw);
return NULL;
}
return pw;
}
/*
* free struct passwd obtained from sshbuf_get_passwd.
*/
void
sshbuf_free_passwd(struct passwd *pwent)
{
if (pwent == NULL)
return;
free(pwent->pw_shell);
free(pwent->pw_dir);
#ifdef HAVE_STRUCT_PASSWD_PW_CLASS
free(pwent->pw_class);
#endif
#ifdef HAVE_STRUCT_PASSWD_PW_GECOS
free(pwent->pw_gecos);
#endif
free(pwent->pw_passwd);
free(pwent->pw_name);
free(pwent);
}

View file

@ -254,21 +254,6 @@ int sshbuf_b64tod(struct sshbuf *buf, const char *b64);
*/
char *sshbuf_dup_string(struct sshbuf *buf);
/*
* store struct pwd
*/
int sshbuf_put_passwd(struct sshbuf *buf, const struct passwd *pwent);
/*
* extract struct pwd
*/
struct passwd *sshbuf_get_passwd(struct sshbuf *buf);
/*
* free struct passwd obtained from sshbuf_get_passwd.
*/
void sshbuf_free_passwd(struct passwd *pwent);
/* Macros for decoding/encoding integers */
#define PEEK_U64(p) \
(((u_int64_t)(((const u_char *)(p))[0]) << 56) | \