sshd: address capsicum issues

* Add a wrapper to proxy login_getpwclass(3) as it is not allowed in
  capability mode.
* Cache timezone data via caph_cache_tzdata() as we cannot access the
  timezone file.
* Reverse resolve hostname before entering capability mode.

PR:		231172
Submitted by:	naito.yuichiro@gmail.com
Reviewed by:	cem, des
Approved by:	re (rgrimes)
MFC after:	3 weeks
Differential Revision:	https://reviews.freebsd.org/D17128
This commit is contained in:
Ed Maste 2018-10-06 21:32:55 +00:00
parent 7e524b0746
commit fc3c19a9fc
9 changed files with 222 additions and 34 deletions

View File

@ -316,7 +316,7 @@ input_userauth_request(int type, u_int32_t seq, struct ssh *ssh)
#ifdef HAVE_LOGIN_CAP #ifdef HAVE_LOGIN_CAP
if (authctxt->pw != NULL && if (authctxt->pw != NULL &&
(lc = login_getpwclass(authctxt->pw)) != NULL) { (lc = PRIVSEP(login_getpwclass(authctxt->pw))) != NULL) {
logit("user %s login class %s", authctxt->pw->pw_name, logit("user %s login class %s", authctxt->pw->pw_name,
authctxt->pw->pw_class); authctxt->pw->pw_class);
from_host = auth_get_canonical_hostname(ssh, options.use_dns); from_host = auth_get_canonical_hostname(ssh, options.use_dns);
@ -331,7 +331,7 @@ input_userauth_request(int type, u_int32_t seq, struct ssh *ssh)
authctxt->pw->pw_name, from_host); authctxt->pw->pw_name, from_host);
packet_disconnect("Logins not available right now."); packet_disconnect("Logins not available right now.");
} }
login_close(lc); PRIVSEP(login_close(lc));
} }
#endif /* HAVE_LOGIN_CAP */ #endif /* HAVE_LOGIN_CAP */

View File

@ -114,6 +114,7 @@ static struct sshbuf *child_state;
int mm_answer_moduli(int, struct sshbuf *); int mm_answer_moduli(int, struct sshbuf *);
int mm_answer_sign(int, struct sshbuf *); int mm_answer_sign(int, struct sshbuf *);
int mm_answer_login_getpwclass(int, struct sshbuf *);
int mm_answer_pwnamallow(int, struct sshbuf *); int mm_answer_pwnamallow(int, struct sshbuf *);
int mm_answer_auth2_read_banner(int, struct sshbuf *); int mm_answer_auth2_read_banner(int, struct sshbuf *);
int mm_answer_authserv(int, struct sshbuf *); int mm_answer_authserv(int, struct sshbuf *);
@ -189,6 +190,7 @@ struct mon_table mon_dispatch_proto20[] = {
{MONITOR_REQ_MODULI, MON_ONCE, mm_answer_moduli}, {MONITOR_REQ_MODULI, MON_ONCE, mm_answer_moduli},
#endif #endif
{MONITOR_REQ_SIGN, MON_ONCE, mm_answer_sign}, {MONITOR_REQ_SIGN, MON_ONCE, mm_answer_sign},
{MONITOR_REQ_GETPWCLASS, MON_AUTH, mm_answer_login_getpwclass},
{MONITOR_REQ_PWNAM, MON_ONCE, mm_answer_pwnamallow}, {MONITOR_REQ_PWNAM, MON_ONCE, mm_answer_pwnamallow},
{MONITOR_REQ_AUTHSERV, MON_ONCE, mm_answer_authserv}, {MONITOR_REQ_AUTHSERV, MON_ONCE, mm_answer_authserv},
{MONITOR_REQ_AUTH2_READ_BANNER, MON_ONCE, mm_answer_auth2_read_banner}, {MONITOR_REQ_AUTH2_READ_BANNER, MON_ONCE, mm_answer_auth2_read_banner},
@ -707,6 +709,46 @@ mm_answer_sign(int sock, struct sshbuf *m)
return (0); return (0);
} }
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);
}
/* Retrieves the password entry and also checks if the user is permitted */ /* Retrieves the password entry and also checks if the user is permitted */
int int
@ -745,19 +787,8 @@ mm_answer_pwnamallow(int sock, struct sshbuf *m)
authctxt->pw = pwent; authctxt->pw = pwent;
authctxt->valid = 1; authctxt->valid = 1;
/* XXX don't sent pwent to unpriv; send fake class/dir/shell too */
if ((r = sshbuf_put_u8(m, 1)) != 0 || if ((r = sshbuf_put_u8(m, 1)) != 0 ||
(r = sshbuf_put_string(m, pwent, sizeof(*pwent))) != 0 || (r = sshbuf_put_passwd(m, 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)); fatal("%s: buffer error: %s", __func__, ssh_err(r));
out: out:

View File

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

View File

@ -247,6 +247,57 @@ mm_sshkey_sign(struct sshkey *key, u_char **sigp, size_t *lenp,
return (0); return (0);
} }
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);
}
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);
}
struct passwd * struct passwd *
mm_getpwnamallow(const char *username) mm_getpwnamallow(const char *username)
{ {
@ -279,25 +330,9 @@ mm_getpwnamallow(const char *username)
goto out; goto out;
} }
/* XXX don't like passing struct passwd like this */ pw = sshbuf_get_passwd(m);
pw = xcalloc(sizeof(*pw), 1); if (pw == NULL)
if ((r = sshbuf_get_string_direct(m, &p, &len)) != 0) fatal("%s: receive get struct passwd failed", __func__);
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: out:
/* copy options block as a Match directive may have changed some */ /* copy options block as a Match directive may have changed some */

View File

@ -28,6 +28,8 @@
#ifndef _MM_WRAP_H_ #ifndef _MM_WRAP_H_
#define _MM_WRAP_H_ #define _MM_WRAP_H_
#include <login_cap.h>
extern int use_privsep; extern int use_privsep;
#define PRIVSEP(x) (use_privsep ? mm_##x : x) #define PRIVSEP(x) (use_privsep ? mm_##x : x)
@ -45,6 +47,8 @@ int mm_sshkey_sign(struct sshkey *, u_char **, size_t *, const u_char *, size_t,
const char *, u_int compat); const char *, u_int compat);
void mm_inform_authserv(char *, char *); void mm_inform_authserv(char *, char *);
struct passwd *mm_getpwnamallow(const char *); struct passwd *mm_getpwnamallow(const char *);
login_cap_t *mm_login_getpwclass(const struct passwd *pwd);
void mm_login_close(login_cap_t *lc);
char *mm_auth2_read_banner(void); char *mm_auth2_read_banner(void);
int mm_auth_password(struct ssh *, char *); int mm_auth_password(struct ssh *, char *);
int mm_key_allowed(enum mm_keytype, const char *, const char *, struct sshkey *, int mm_key_allowed(enum mm_keytype, const char *, const char *, struct sshkey *,

View File

@ -31,6 +31,7 @@ __RCSID("$FreeBSD$");
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <unistd.h> #include <unistd.h>
#include <capsicum_helpers.h>
#include "log.h" #include "log.h"
#include "monitor.h" #include "monitor.h"
@ -71,6 +72,8 @@ ssh_sandbox_child(struct ssh_sandbox *box)
struct rlimit rl_zero; struct rlimit rl_zero;
cap_rights_t rights; cap_rights_t rights;
caph_cache_tzdata();
rl_zero.rlim_cur = rl_zero.rlim_max = 0; rl_zero.rlim_cur = rl_zero.rlim_max = 0;
if (setrlimit(RLIMIT_FSIZE, &rl_zero) == -1) if (setrlimit(RLIMIT_FSIZE, &rl_zero) == -1)

View File

@ -25,6 +25,7 @@
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
#include "xmalloc.h"
#include "ssherr.h" #include "ssherr.h"
#include "sshbuf.h" #include "sshbuf.h"
@ -462,3 +463,95 @@ sshbuf_get_bignum2_bytes_direct(struct sshbuf *buf,
} }
return 0; 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 ||
(r = sshbuf_put_u64(buf, pwent->pw_change)) != 0 ||
#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 ||
(r = sshbuf_put_u64(buf, pwent->pw_expire)) != 0 ||
(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;
int r;
size_t len;
/* 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 ||
sshbuf_get_u64(buf, &pw->pw_change) != 0 ||
#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 ||
sshbuf_get_u64(buf, &pw->pw_expire) != 0 ||
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

@ -21,6 +21,7 @@
#include <sys/types.h> #include <sys/types.h>
#include <stdarg.h> #include <stdarg.h>
#include <stdio.h> #include <stdio.h>
#include <pwd.h>
#ifdef WITH_OPENSSL #ifdef WITH_OPENSSL
# include <openssl/bn.h> # include <openssl/bn.h>
# ifdef OPENSSL_HAS_ECC # ifdef OPENSSL_HAS_ECC
@ -246,6 +247,21 @@ int sshbuf_b64tod(struct sshbuf *buf, const char *b64);
*/ */
char *sshbuf_dup_string(struct sshbuf *buf); 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 */ /* Macros for decoding/encoding integers */
#define PEEK_U64(p) \ #define PEEK_U64(p) \
(((u_int64_t)(((const u_char *)(p))[0]) << 56) | \ (((u_int64_t)(((const u_char *)(p))[0]) << 56) | \

View File

@ -2143,6 +2143,11 @@ main(int ac, char **av)
*/ */
remote_ip = ssh_remote_ipaddr(ssh); remote_ip = ssh_remote_ipaddr(ssh);
#ifdef HAVE_LOGIN_CAP
/* Also caches remote hostname for sandboxed child. */
auth_get_canonical_hostname(ssh, options.use_dns);
#endif
#ifdef SSH_AUDIT_EVENTS #ifdef SSH_AUDIT_EVENTS
audit_connection_from(remote_ip, remote_port); audit_connection_from(remote_ip, remote_port);
#endif #endif