upstream: Start the process of splitting sshd into separate

binaries. This step splits sshd into a listener and a session binary. More
splits are planned.

After this changes, the listener binary will validate the configuration,
load the hostkeys, listen on port 22 and manage MaxStartups only. All
session handling will be performed by a new sshd-session binary that the
listener fork+execs.

This reduces the listener process to the minimum necessary and sets us
up for future work on the sshd-session binary.

feedback/ok markus@ deraadt@

NB. if you're updating via source, please restart sshd after installing,
otherwise you run the risk of locking yourself out.

OpenBSD-Commit-ID: 43c04a1ab96cdbdeb53d2df0125a6d42c5f19934
This commit is contained in:
djm@openbsd.org 2024-05-17 00:30:23 +00:00 committed by Damien Miller
parent 1c0d813579
commit 03e3de416e
No known key found for this signature in database
33 changed files with 2096 additions and 1931 deletions

View File

@ -668,7 +668,7 @@ static struct pam_conv store_conv = { sshpam_store_conv, NULL };
void
sshpam_cleanup(void)
{
if (sshpam_handle == NULL || (use_privsep && !mm_is_monitor()))
if (sshpam_handle == NULL || !mm_is_monitor())
return;
debug("PAM: cleanup");
pam_set_item(sshpam_handle, PAM_CONV, (const void *)&null_conv);
@ -705,7 +705,8 @@ sshpam_init(struct ssh *ssh, Authctxt *authctxt)
fatal("%s: called initially with no "
"packet context", __func__);
}
} if (sshpam_handle != NULL) {
}
if (sshpam_handle != NULL) {
/* We already have a PAM context; check if the user matches */
sshpam_err = pam_get_item(sshpam_handle,
PAM_USER, (sshpam_const void **)ptr_pam_user);
@ -1101,20 +1102,15 @@ do_pam_account(void)
}
void
do_pam_setcred(int init)
do_pam_setcred(void)
{
sshpam_err = pam_set_item(sshpam_handle, PAM_CONV,
(const void *)&store_conv);
if (sshpam_err != PAM_SUCCESS)
fatal("PAM: failed to set PAM_CONV: %s",
pam_strerror(sshpam_handle, sshpam_err));
if (init) {
debug("PAM: establishing credentials");
sshpam_err = pam_setcred(sshpam_handle, PAM_ESTABLISH_CRED);
} else {
debug("PAM: reinitializing credentials");
sshpam_err = pam_setcred(sshpam_handle, PAM_REINITIALIZE_CRED);
}
debug("PAM: establishing credentials");
sshpam_err = pam_setcred(sshpam_handle, PAM_ESTABLISH_CRED);
if (sshpam_err == PAM_SUCCESS) {
sshpam_cred_established = 1;
return;
@ -1127,6 +1123,7 @@ do_pam_setcred(int init)
pam_strerror(sshpam_handle, sshpam_err));
}
#if 0
static int
sshpam_tty_conv(int n, sshpam_const struct pam_message **msg,
struct pam_response **resp, void *data)
@ -1182,6 +1179,7 @@ sshpam_tty_conv(int n, sshpam_const struct pam_message **msg,
}
static struct pam_conv tty_conv = { sshpam_tty_conv, NULL };
#endif
/*
* XXX this should be done in the authentication phase, but ssh1 doesn't
@ -1190,8 +1188,8 @@ static struct pam_conv tty_conv = { sshpam_tty_conv, NULL };
void
do_pam_chauthtok(void)
{
if (use_privsep)
fatal("Password expired (unable to change with privsep)");
fatal("Password expired");
#if 0
sshpam_err = pam_set_item(sshpam_handle, PAM_CONV,
(const void *)&tty_conv);
if (sshpam_err != PAM_SUCCESS)
@ -1202,6 +1200,7 @@ do_pam_chauthtok(void)
if (sshpam_err != PAM_SUCCESS)
fatal("PAM: pam_chauthtok(): %s",
pam_strerror(sshpam_handle, sshpam_err));
#endif
}
void

View File

@ -31,7 +31,7 @@ void start_pam(struct ssh *);
void finish_pam(void);
u_int do_pam_account(void);
void do_pam_session(struct ssh *);
void do_pam_setcred(int );
void do_pam_setcred(void);
void do_pam_chauthtok(void);
int do_pam_putenv(char *, char *);
char ** fetch_pam_environment(void);

View File

@ -1,4 +1,4 @@
/* $OpenBSD: auth-rhosts.c,v 1.57 2022/12/09 00:17:40 dtucker Exp $ */
/* $OpenBSD: auth-rhosts.c,v 1.58 2024/05/17 00:30:23 djm Exp $ */
/*
* Author: Tatu Ylonen <ylo@cs.hut.fi>
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@ -46,7 +46,6 @@
/* import */
extern ServerOptions options;
extern int use_privsep;
/*
* This function processes an rhosts-style file (.rhosts, .shosts, or

108
auth.c
View File

@ -1,4 +1,4 @@
/* $OpenBSD: auth.c,v 1.160 2023/03/05 05:34:09 dtucker Exp $ */
/* $OpenBSD: auth.c,v 1.161 2024/05/17 00:30:23 djm Exp $ */
/*
* Copyright (c) 2000 Markus Friedl. All rights reserved.
*
@ -79,7 +79,6 @@
/* import */
extern ServerOptions options;
extern struct include_list includes;
extern int use_privsep;
extern struct sshbuf *loginmsg;
extern struct passwd *privsep_pw;
extern struct sshauthopt *auth_opts;
@ -272,7 +271,7 @@ auth_log(struct ssh *ssh, int authenticated, int partial,
const char *authmsg;
char *extra = NULL;
if (use_privsep && !mm_is_monitor() && !authctxt->postponed)
if (!mm_is_monitor() && !authctxt->postponed)
return;
/* Raise logging level */
@ -472,14 +471,14 @@ getpwnamallow(struct ssh *ssh, const char *user)
struct connection_info *ci;
u_int i;
ci = get_connection_info(ssh, 1, options.use_dns);
ci = server_get_connection_info(ssh, 1, options.use_dns);
ci->user = user;
parse_server_match_config(&options, &includes, ci);
log_change_level(options.log_level);
log_verbose_reset();
for (i = 0; i < options.num_log_verbose; i++)
log_verbose_add(options.log_verbose[i]);
process_permitopen(ssh, &options);
server_process_permitopen(ssh);
#if defined(_AIX) && defined(HAVE_SETAUTHDB)
aix_setauthdb(user);
@ -637,97 +636,6 @@ fakepw(void)
return (&fake);
}
/*
* Returns the remote DNS hostname as a string. The returned string must not
* be freed. NB. this will usually trigger a DNS query the first time it is
* called.
* This function does additional checks on the hostname to mitigate some
* attacks on based on conflation of hostnames and IP addresses.
*/
static char *
remote_hostname(struct ssh *ssh)
{
struct sockaddr_storage from;
socklen_t fromlen;
struct addrinfo hints, *ai, *aitop;
char name[NI_MAXHOST], ntop2[NI_MAXHOST];
const char *ntop = ssh_remote_ipaddr(ssh);
/* Get IP address of client. */
fromlen = sizeof(from);
memset(&from, 0, sizeof(from));
if (getpeername(ssh_packet_get_connection_in(ssh),
(struct sockaddr *)&from, &fromlen) == -1) {
debug("getpeername failed: %.100s", strerror(errno));
return xstrdup(ntop);
}
ipv64_normalise_mapped(&from, &fromlen);
if (from.ss_family == AF_INET6)
fromlen = sizeof(struct sockaddr_in6);
debug3("Trying to reverse map address %.100s.", ntop);
/* Map the IP address to a host name. */
if (getnameinfo((struct sockaddr *)&from, fromlen, name, sizeof(name),
NULL, 0, NI_NAMEREQD) != 0) {
/* Host name not found. Use ip address. */
return xstrdup(ntop);
}
/*
* if reverse lookup result looks like a numeric hostname,
* someone is trying to trick us by PTR record like following:
* 1.1.1.10.in-addr.arpa. IN PTR 2.3.4.5
*/
memset(&hints, 0, sizeof(hints));
hints.ai_socktype = SOCK_DGRAM; /*dummy*/
hints.ai_flags = AI_NUMERICHOST;
if (getaddrinfo(name, NULL, &hints, &ai) == 0) {
logit("Nasty PTR record \"%s\" is set up for %s, ignoring",
name, ntop);
freeaddrinfo(ai);
return xstrdup(ntop);
}
/* Names are stored in lowercase. */
lowercase(name);
/*
* Map it back to an IP address and check that the given
* address actually is an address of this host. This is
* necessary because anyone with access to a name server can
* define arbitrary names for an IP address. Mapping from
* name to IP address can be trusted better (but can still be
* fooled if the intruder has access to the name server of
* the domain).
*/
memset(&hints, 0, sizeof(hints));
hints.ai_family = from.ss_family;
hints.ai_socktype = SOCK_STREAM;
if (getaddrinfo(name, NULL, &hints, &aitop) != 0) {
logit("reverse mapping checking getaddrinfo for %.700s "
"[%s] failed.", name, ntop);
return xstrdup(ntop);
}
/* Look for the address from the list of addresses. */
for (ai = aitop; ai; ai = ai->ai_next) {
if (getnameinfo(ai->ai_addr, ai->ai_addrlen, ntop2,
sizeof(ntop2), NULL, 0, NI_NUMERICHOST) == 0 &&
(strcmp(ntop, ntop2) == 0))
break;
}
freeaddrinfo(aitop);
/* If we reached the end of the list, the address was not there. */
if (ai == NULL) {
/* Address not found for the host name. */
logit("Address %.100s maps to %.600s, but this does not "
"map back to the address.", ntop, name);
return xstrdup(ntop);
}
return xstrdup(name);
}
/*
* Return the canonical name of the host in the other side of the current
* connection. The host name is cached, so it is efficient to call this
@ -741,12 +649,10 @@ auth_get_canonical_hostname(struct ssh *ssh, int use_dns)
if (!use_dns)
return ssh_remote_ipaddr(ssh);
else if (dnsname != NULL)
if (dnsname != NULL)
return dnsname;
else {
dnsname = remote_hostname(ssh);
return dnsname;
}
dnsname = ssh_remote_hostname(ssh);
return dnsname;
}
/* These functions link key/cert options to the auth framework */

12
auth.h
View File

@ -1,4 +1,4 @@
/* $OpenBSD: auth.h,v 1.106 2022/06/15 16:08:25 djm Exp $ */
/* $OpenBSD: auth.h,v 1.107 2024/05/17 00:30:23 djm Exp $ */
/*
* Copyright (c) 2000 Markus Friedl. All rights reserved.
@ -104,11 +104,15 @@ struct Authctxt {
* the client.
*/
struct authmethod_cfg {
const char *name;
const char *synonym;
int *enabled;
};
struct Authmethod {
char *name;
char *synonym;
struct authmethod_cfg *cfg;
int (*userauth)(struct ssh *, const char *);
int *enabled;
};
/*

View File

@ -1,4 +1,4 @@
/* $OpenBSD: auth2-gss.c,v 1.34 2023/03/31 04:22:27 djm Exp $ */
/* $OpenBSD: auth2-gss.c,v 1.35 2024/05/17 00:30:23 djm Exp $ */
/*
* Copyright (c) 2001-2003 Simon Wilkinson. All rights reserved.
@ -51,6 +51,7 @@
#define SSH_GSSAPI_MAX_MECHS 2048
extern ServerOptions options;
extern struct authmethod_cfg methodcfg_gssapi;
static int input_gssapi_token(int type, u_int32_t plen, struct ssh *ssh);
static int input_gssapi_mic(int type, u_int32_t plen, struct ssh *ssh);
@ -116,7 +117,7 @@ userauth_gssapi(struct ssh *ssh, const char *method)
return (0);
}
if (GSS_ERROR(PRIVSEP(ssh_gssapi_server_ctx(&ctxt, &goid)))) {
if (GSS_ERROR(mm_ssh_gssapi_server_ctx(&ctxt, &goid))) {
if (ctxt != NULL)
ssh_gssapi_delete_ctx(&ctxt);
free(doid);
@ -153,7 +154,7 @@ input_gssapi_token(int type, u_int32_t plen, struct ssh *ssh)
size_t len;
int r;
if (authctxt == NULL || (authctxt->methoddata == NULL && !use_privsep))
if (authctxt == NULL)
fatal("No authentication or GSSAPI context");
gssctxt = authctxt->methoddata;
@ -163,8 +164,8 @@ input_gssapi_token(int type, u_int32_t plen, struct ssh *ssh)
recv_tok.value = p;
recv_tok.length = len;
maj_status = PRIVSEP(ssh_gssapi_accept_ctx(gssctxt, &recv_tok,
&send_tok, &flags));
maj_status = mm_ssh_gssapi_accept_ctx(gssctxt, &recv_tok,
&send_tok, &flags);
free(p);
@ -217,7 +218,7 @@ input_gssapi_errtok(int type, u_int32_t plen, struct ssh *ssh)
u_char *p;
size_t len;
if (authctxt == NULL || (authctxt->methoddata == NULL && !use_privsep))
if (authctxt == NULL)
fatal("No authentication or GSSAPI context");
gssctxt = authctxt->methoddata;
@ -228,8 +229,8 @@ input_gssapi_errtok(int type, u_int32_t plen, struct ssh *ssh)
recv_tok.length = len;
/* Push the error token into GSSAPI to see what it says */
maj_status = PRIVSEP(ssh_gssapi_accept_ctx(gssctxt, &recv_tok,
&send_tok, NULL));
maj_status = mm_ssh_gssapi_accept_ctx(gssctxt, &recv_tok,
&send_tok, NULL);
free(recv_tok.value);
@ -256,7 +257,7 @@ input_gssapi_exchange_complete(int type, u_int32_t plen, struct ssh *ssh)
int r, authenticated;
const char *displayname;
if (authctxt == NULL || (authctxt->methoddata == NULL && !use_privsep))
if (authctxt == NULL)
fatal("No authentication or GSSAPI context");
/*
@ -267,11 +268,7 @@ input_gssapi_exchange_complete(int type, u_int32_t plen, struct ssh *ssh)
if ((r = sshpkt_get_end(ssh)) != 0)
fatal_fr(r, "parse packet");
authenticated = PRIVSEP(ssh_gssapi_userok(authctxt->user));
if ((!use_privsep || mm_is_monitor()) &&
(displayname = ssh_gssapi_displayname()) != NULL)
auth2_record_info(authctxt, "%s", displayname);
authenticated = mm_ssh_gssapi_userok(authctxt->user);
authctxt->postponed = 0;
ssh_dispatch_set(ssh, SSH2_MSG_USERAUTH_GSSAPI_TOKEN, NULL);
@ -294,7 +291,7 @@ input_gssapi_mic(int type, u_int32_t plen, struct ssh *ssh)
u_char *p;
size_t len;
if (authctxt == NULL || (authctxt->methoddata == NULL && !use_privsep))
if (authctxt == NULL)
fatal("No authentication or GSSAPI context");
gssctxt = authctxt->methoddata;
@ -312,18 +309,14 @@ input_gssapi_mic(int type, u_int32_t plen, struct ssh *ssh)
fatal_f("sshbuf_mutable_ptr failed");
gssbuf.length = sshbuf_len(b);
if (!GSS_ERROR(PRIVSEP(ssh_gssapi_checkmic(gssctxt, &gssbuf, &mic))))
authenticated = PRIVSEP(ssh_gssapi_userok(authctxt->user));
if (!GSS_ERROR(mm_ssh_gssapi_checkmic(gssctxt, &gssbuf, &mic)))
authenticated = mm_ssh_gssapi_userok(authctxt->user);
else
logit("GSSAPI MIC check failed");
sshbuf_free(b);
free(mic.value);
if ((!use_privsep || mm_is_monitor()) &&
(displayname = ssh_gssapi_displayname()) != NULL)
auth2_record_info(authctxt, "%s", displayname);
authctxt->postponed = 0;
ssh_dispatch_set(ssh, SSH2_MSG_USERAUTH_GSSAPI_TOKEN, NULL);
ssh_dispatch_set(ssh, SSH2_MSG_USERAUTH_GSSAPI_ERRTOK, NULL);
@ -334,10 +327,8 @@ input_gssapi_mic(int type, u_int32_t plen, struct ssh *ssh)
}
Authmethod method_gssapi = {
"gssapi-with-mic",
NULL,
&methodcfg_gssapi,
userauth_gssapi,
&options.gss_authentication
};
#endif /* GSSAPI */

View File

@ -1,4 +1,4 @@
/* $OpenBSD: auth2-hostbased.c,v 1.52 2023/03/05 05:34:09 dtucker Exp $ */
/* $OpenBSD: auth2-hostbased.c,v 1.53 2024/05/17 00:30:23 djm Exp $ */
/*
* Copyright (c) 2000 Markus Friedl. All rights reserved.
*
@ -54,6 +54,7 @@
/* import */
extern ServerOptions options;
extern struct authmethod_cfg methodcfg_hostbased;
static int
userauth_hostbased(struct ssh *ssh, const char *method)
@ -145,10 +146,10 @@ userauth_hostbased(struct ssh *ssh, const char *method)
/* test for allowed key and correct signature */
authenticated = 0;
if (PRIVSEP(hostbased_key_allowed(ssh, authctxt->pw, cuser,
chost, key)) &&
PRIVSEP(sshkey_verify(key, sig, slen,
sshbuf_ptr(b), sshbuf_len(b), pkalg, ssh->compat, NULL)) == 0)
if (mm_hostbased_key_allowed(ssh, authctxt->pw, cuser,
chost, key) &&
mm_sshkey_verify(key, sig, slen,
sshbuf_ptr(b), sshbuf_len(b), pkalg, ssh->compat, NULL) == 0)
authenticated = 1;
auth2_record_key(authctxt, authenticated, key);
@ -252,8 +253,6 @@ hostbased_key_allowed(struct ssh *ssh, struct passwd *pw,
}
Authmethod method_hostbased = {
"hostbased",
NULL,
&methodcfg_hostbased,
userauth_hostbased,
&options.hostbased_authentication
};

View File

@ -1,4 +1,4 @@
/* $OpenBSD: auth2-kbdint.c,v 1.14 2021/12/19 22:12:07 djm Exp $ */
/* $OpenBSD: auth2-kbdint.c,v 1.15 2024/05/17 00:30:23 djm Exp $ */
/*
* Copyright (c) 2000 Markus Friedl. All rights reserved.
*
@ -42,6 +42,7 @@
/* import */
extern ServerOptions options;
extern struct authmethod_cfg methodcfg_kbdint;
static int
userauth_kbdint(struct ssh *ssh, const char *method)
@ -65,8 +66,6 @@ userauth_kbdint(struct ssh *ssh, const char *method)
}
Authmethod method_kbdint = {
"keyboard-interactive",
NULL,
&methodcfg_kbdint,
userauth_kbdint,
&options.kbd_interactive_authentication
};

View File

@ -1,4 +1,4 @@
/* $OpenBSD: auth2-none.c,v 1.25 2023/03/05 05:34:09 dtucker Exp $ */
/* $OpenBSD: auth2-none.c,v 1.26 2024/05/17 00:30:23 djm Exp $ */
/*
* Copyright (c) 2000 Markus Friedl. All rights reserved.
*
@ -53,9 +53,9 @@
/* import */
extern ServerOptions options;
extern struct authmethod_cfg methodcfg_none;
/* "none" is allowed only one time */
static int none_enabled = 1;
extern int none_enabled;
static int
userauth_none(struct ssh *ssh, const char *method)
@ -66,13 +66,11 @@ userauth_none(struct ssh *ssh, const char *method)
if ((r = sshpkt_get_end(ssh)) != 0)
fatal_fr(r, "parse packet");
if (options.permit_empty_passwd && options.password_authentication)
return (PRIVSEP(auth_password(ssh, "")));
return mm_auth_password(ssh, "");
return (0);
}
Authmethod method_none = {
"none",
NULL,
&methodcfg_none,
userauth_none,
&none_enabled
};

View File

@ -1,4 +1,4 @@
/* $OpenBSD: auth2-passwd.c,v 1.21 2022/05/27 04:29:40 dtucker Exp $ */
/* $OpenBSD: auth2-passwd.c,v 1.22 2024/05/17 00:30:23 djm Exp $ */
/*
* Copyright (c) 2000 Markus Friedl. All rights reserved.
*
@ -47,6 +47,7 @@
/* import */
extern ServerOptions options;
extern struct authmethod_cfg methodcfg_passwd;
static int
userauth_passwd(struct ssh *ssh, const char *method)
@ -66,15 +67,13 @@ userauth_passwd(struct ssh *ssh, const char *method)
if (change)
logit("password change not supported");
else if (PRIVSEP(auth_password(ssh, password)) == 1)
else if (mm_auth_password(ssh, password) == 1)
authenticated = 1;
freezero(password, len);
return authenticated;
}
Authmethod method_passwd = {
"password",
NULL,
&methodcfg_passwd,
userauth_passwd,
&options.password_authentication
};

View File

@ -1,4 +1,4 @@
/* $OpenBSD: auth2-pubkey.c,v 1.119 2023/07/27 22:25:17 djm Exp $ */
/* $OpenBSD: auth2-pubkey.c,v 1.120 2024/05/17 00:30:23 djm Exp $ */
/*
* Copyright (c) 2000 Markus Friedl. All rights reserved.
* Copyright (c) 2010 Damien Miller. All rights reserved.
@ -72,6 +72,7 @@
/* import */
extern ServerOptions options;
extern struct authmethod_cfg methodcfg_pubkey;
static char *
format_key(const struct sshkey *key)
@ -219,11 +220,11 @@ userauth_pubkey(struct ssh *ssh, const char *method)
#endif
/* test for correct signature */
authenticated = 0;
if (PRIVSEP(user_key_allowed(ssh, pw, key, 1, &authopts)) &&
PRIVSEP(sshkey_verify(key, sig, slen,
if (mm_user_key_allowed(ssh, pw, key, 1, &authopts) &&
mm_sshkey_verify(key, sig, slen,
sshbuf_ptr(b), sshbuf_len(b),
(ssh->compat & SSH_BUG_SIGTYPE) == 0 ? pkalg : NULL,
ssh->compat, &sig_details)) == 0) {
ssh->compat, &sig_details) == 0) {
authenticated = 1;
}
if (authenticated == 1 && sig_details != NULL) {
@ -281,7 +282,7 @@ userauth_pubkey(struct ssh *ssh, const char *method)
* if a user is not allowed to login. is this an
* issue? -markus
*/
if (PRIVSEP(user_key_allowed(ssh, pw, key, 0, NULL))) {
if (mm_user_key_allowed(ssh, pw, key, 0, NULL)) {
if ((r = sshpkt_start(ssh, SSH2_MSG_USERAUTH_PK_OK))
!= 0 ||
(r = sshpkt_put_cstring(ssh, pkalg)) != 0 ||
@ -813,8 +814,6 @@ user_key_allowed(struct ssh *ssh, struct passwd *pw, struct sshkey *key,
}
Authmethod method_pubkey = {
"publickey",
"publickey-hostbound-v00@openssh.com",
&methodcfg_pubkey,
userauth_pubkey,
&options.pubkey_authentication
};

91
auth2.c
View File

@ -1,4 +1,4 @@
/* $OpenBSD: auth2.c,v 1.168 2023/12/18 14:45:49 djm Exp $ */
/* $OpenBSD: auth2.c,v 1.169 2024/05/17 00:30:23 djm Exp $ */
/*
* Copyright (c) 2000 Markus Friedl. All rights reserved.
*
@ -156,7 +156,7 @@ userauth_banner(struct ssh *ssh)
if (options.banner == NULL)
return;
if ((banner = PRIVSEP(auth2_read_banner())) == NULL)
if ((banner = mm_auth2_read_banner()) == NULL)
goto done;
userauth_send_banner(ssh, banner);
@ -291,7 +291,7 @@ input_userauth_request(int type, u_int32_t seq, struct ssh *ssh)
auth_maxtries_exceeded(ssh);
if (authctxt->attempt++ == 0) {
/* setup auth context */
authctxt->pw = PRIVSEP(getpwnamallow(ssh, user));
authctxt->pw = mm_getpwnamallow(ssh, user);
authctxt->user = xstrdup(user);
if (authctxt->pw && strcmp(service, "ssh-connection")==0) {
authctxt->valid = 1;
@ -301,21 +301,19 @@ input_userauth_request(int type, u_int32_t seq, struct ssh *ssh)
/* Invalid user, fake password information */
authctxt->pw = fakepw();
#ifdef SSH_AUDIT_EVENTS
PRIVSEP(audit_event(ssh, SSH_INVALID_USER));
mm_audit_event(ssh, SSH_INVALID_USER);
#endif
}
#ifdef USE_PAM
if (options.use_pam)
PRIVSEP(start_pam(ssh));
mm_start_pam(ssh);
#endif
ssh_packet_set_log_preamble(ssh, "%suser %s",
authctxt->valid ? "authenticating " : "invalid ", user);
setproctitle("%s%s", authctxt->valid ? user : "unknown",
use_privsep ? " [net]" : "");
setproctitle("%s [net]", authctxt->valid ? user : "unknown");
authctxt->service = xstrdup(service);
authctxt->style = style ? xstrdup(style) : NULL;
if (use_privsep)
mm_inform_authserv(service, style);
mm_inform_authserv(service, style);
userauth_banner(ssh);
if ((r = kex_server_update_ext_info(ssh)) != 0)
fatal_fr(r, "kex_server_update_ext_info failed");
@ -379,7 +377,7 @@ userauth_finish(struct ssh *ssh, int authenticated, const char *packet_method,
/* prefer primary authmethod name to possible synonym */
if ((m = authmethod_byname(method)) == NULL)
fatal("INTERNAL ERROR: bad method %s", method);
method = m->name;
method = m->cfg->name;
}
/* Special handling for root */
@ -387,7 +385,7 @@ userauth_finish(struct ssh *ssh, int authenticated, const char *packet_method,
!auth_root_allowed(ssh, method)) {
authenticated = 0;
#ifdef SSH_AUDIT_EVENTS
PRIVSEP(audit_event(ssh, SSH_LOGIN_ROOT_DENIED));
mm_audit_event(ssh, SSH_LOGIN_ROOT_DENIED);
#endif
}
@ -410,7 +408,7 @@ userauth_finish(struct ssh *ssh, int authenticated, const char *packet_method,
#ifdef USE_PAM
if (options.use_pam && authenticated) {
int r, success = PRIVSEP(do_pam_account());
int r, success = mm_do_pam_account();
/* If PAM returned a message, send it to the user. */
if (sshbuf_len(loginmsg) > 0) {
@ -448,7 +446,7 @@ userauth_finish(struct ssh *ssh, int authenticated, const char *packet_method,
authctxt->failures++;
if (authctxt->failures >= options.max_authtries) {
#ifdef SSH_AUDIT_EVENTS
PRIVSEP(audit_event(ssh, SSH_LOGIN_EXCEED_MAXTRIES));
mm_audit_event(ssh, SSH_LOGIN_EXCEED_MAXTRIES);
#endif
auth_maxtries_exceeded(ssh);
}
@ -500,16 +498,16 @@ authmethods_get(Authctxt *authctxt)
if ((b = sshbuf_new()) == NULL)
fatal_f("sshbuf_new failed");
for (i = 0; authmethods[i] != NULL; i++) {
if (strcmp(authmethods[i]->name, "none") == 0)
if (strcmp(authmethods[i]->cfg->name, "none") == 0)
continue;
if (authmethods[i]->enabled == NULL ||
*(authmethods[i]->enabled) == 0)
if (authmethods[i]->cfg->enabled == NULL ||
*(authmethods[i]->cfg->enabled) == 0)
continue;
if (!auth2_method_allowed(authctxt, authmethods[i]->name,
if (!auth2_method_allowed(authctxt, authmethods[i]->cfg->name,
NULL))
continue;
if ((r = sshbuf_putf(b, "%s%s", sshbuf_len(b) ? "," : "",
authmethods[i]->name)) != 0)
authmethods[i]->cfg->name)) != 0)
fatal_fr(r, "buffer error");
}
if ((list = sshbuf_dup_string(b)) == NULL)
@ -526,9 +524,9 @@ authmethod_byname(const char *name)
if (name == NULL)
fatal_f("NULL authentication method name");
for (i = 0; authmethods[i] != NULL; i++) {
if (strcmp(name, authmethods[i]->name) == 0 ||
(authmethods[i]->synonym != NULL &&
strcmp(name, authmethods[i]->synonym) == 0))
if (strcmp(name, authmethods[i]->cfg->name) == 0 ||
(authmethods[i]->cfg->synonym != NULL &&
strcmp(name, authmethods[i]->cfg->synonym) == 0))
return authmethods[i];
}
debug_f("unrecognized authentication method name: %s", name);
@ -543,11 +541,11 @@ authmethod_lookup(Authctxt *authctxt, const char *name)
if ((method = authmethod_byname(name)) == NULL)
return NULL;
if (method->enabled == NULL || *(method->enabled) == 0) {
if (method->cfg->enabled == NULL || *(method->cfg->enabled) == 0) {
debug3_f("method %s not enabled", name);
return NULL;
}
if (!auth2_method_allowed(authctxt, method->name, NULL)) {
if (!auth2_method_allowed(authctxt, method->cfg->name, NULL)) {
debug3_f("method %s not allowed "
"by AuthenticationMethods", name);
return NULL;
@ -555,53 +553,6 @@ authmethod_lookup(Authctxt *authctxt, const char *name)
return method;
}
/*
* Check a comma-separated list of methods for validity. Is need_enable is
* non-zero, then also require that the methods are enabled.
* Returns 0 on success or -1 if the methods list is invalid.
*/
int
auth2_methods_valid(const char *_methods, int need_enable)
{
char *methods, *omethods, *method, *p;
u_int i, found;
int ret = -1;
if (*_methods == '\0') {
error("empty authentication method list");
return -1;
}
omethods = methods = xstrdup(_methods);
while ((method = strsep(&methods, ",")) != NULL) {
for (found = i = 0; !found && authmethods[i] != NULL; i++) {
if ((p = strchr(method, ':')) != NULL)
*p = '\0';
if (strcmp(method, authmethods[i]->name) != 0)
continue;
if (need_enable) {
if (authmethods[i]->enabled == NULL ||
*(authmethods[i]->enabled) == 0) {
error("Disabled method \"%s\" in "
"AuthenticationMethods list \"%s\"",
method, _methods);
goto out;
}
}
found = 1;
break;
}
if (!found) {
error("Unknown authentication method \"%s\" in list",
method);
goto out;
}
}
ret = 0;
out:
free(omethods);
return ret;
}
/*
* Prune the AuthenticationMethods supplied in the configuration, removing
* any methods lists that include disabled methods. Note that this might

View File

@ -1,4 +1,4 @@
/* $OpenBSD: channels.c,v 1.437 2024/03/06 02:59:59 djm Exp $ */
/* $OpenBSD: channels.c,v 1.438 2024/05/17 00:30:23 djm Exp $ */
/*
* Author: Tatu Ylonen <ylo@cs.hut.fi>
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@ -93,13 +93,6 @@
/* -- agent forwarding */
#define NUM_SOCKS 10
/* -- tcp forwarding */
/* special-case port number meaning allow any port */
#define FWD_PERMIT_ANY_PORT 0
/* special-case wildcard meaning allow any host */
#define FWD_PERMIT_ANY_HOST "*"
/* -- X11 forwarding */
/* Maximum number of fake X11 displays to try. */
#define MAX_DISPLAYS 1000
@ -4579,19 +4572,6 @@ channel_update_permission(struct ssh *ssh, int idx, int newport)
}
}
/* returns port number, FWD_PERMIT_ANY_PORT or -1 on error */
int
permitopen_port(const char *p)
{
int port;
if (strcmp(p, "*") == 0)
return FWD_PERMIT_ANY_PORT;
if ((port = a2port(p)) > 0)
return port;
return -1;
}
/* Try to start non-blocking connect to next host in cctx list */
static int
connect_next(struct channel_connect *cctx)

270
kex.c
View File

@ -1,4 +1,4 @@
/* $OpenBSD: kex.c,v 1.185 2024/01/08 00:34:33 djm Exp $ */
/* $OpenBSD: kex.c,v 1.186 2024/05/17 00:30:23 djm Exp $ */
/*
* Copyright (c) 2000, 2001 Markus Friedl. All rights reserved.
*
@ -81,254 +81,6 @@ static const char * const proposal_names[PROPOSAL_MAX] = {
"languages stoc",
};
struct kexalg {
char *name;
u_int type;
int ec_nid;
int hash_alg;
};
static const struct kexalg kexalgs[] = {
#ifdef WITH_OPENSSL
{ KEX_DH1, KEX_DH_GRP1_SHA1, 0, SSH_DIGEST_SHA1 },
{ KEX_DH14_SHA1, KEX_DH_GRP14_SHA1, 0, SSH_DIGEST_SHA1 },
{ KEX_DH14_SHA256, KEX_DH_GRP14_SHA256, 0, SSH_DIGEST_SHA256 },
{ KEX_DH16_SHA512, KEX_DH_GRP16_SHA512, 0, SSH_DIGEST_SHA512 },
{ KEX_DH18_SHA512, KEX_DH_GRP18_SHA512, 0, SSH_DIGEST_SHA512 },
{ KEX_DHGEX_SHA1, KEX_DH_GEX_SHA1, 0, SSH_DIGEST_SHA1 },
#ifdef HAVE_EVP_SHA256
{ KEX_DHGEX_SHA256, KEX_DH_GEX_SHA256, 0, SSH_DIGEST_SHA256 },
#endif /* HAVE_EVP_SHA256 */
#ifdef OPENSSL_HAS_ECC
{ KEX_ECDH_SHA2_NISTP256, KEX_ECDH_SHA2,
NID_X9_62_prime256v1, SSH_DIGEST_SHA256 },
{ KEX_ECDH_SHA2_NISTP384, KEX_ECDH_SHA2, NID_secp384r1,
SSH_DIGEST_SHA384 },
# ifdef OPENSSL_HAS_NISTP521
{ KEX_ECDH_SHA2_NISTP521, KEX_ECDH_SHA2, NID_secp521r1,
SSH_DIGEST_SHA512 },
# endif /* OPENSSL_HAS_NISTP521 */
#endif /* OPENSSL_HAS_ECC */
#endif /* WITH_OPENSSL */
#if defined(HAVE_EVP_SHA256) || !defined(WITH_OPENSSL)
{ KEX_CURVE25519_SHA256, KEX_C25519_SHA256, 0, SSH_DIGEST_SHA256 },
{ KEX_CURVE25519_SHA256_OLD, KEX_C25519_SHA256, 0, SSH_DIGEST_SHA256 },
#ifdef USE_SNTRUP761X25519
{ KEX_SNTRUP761X25519_SHA512, KEX_KEM_SNTRUP761X25519_SHA512, 0,
SSH_DIGEST_SHA512 },
#endif
#endif /* HAVE_EVP_SHA256 || !WITH_OPENSSL */
{ NULL, 0, -1, -1},
};
char *
kex_alg_list(char sep)
{
char *ret = NULL, *tmp;
size_t nlen, rlen = 0;
const struct kexalg *k;
for (k = kexalgs; k->name != NULL; k++) {
if (ret != NULL)
ret[rlen++] = sep;
nlen = strlen(k->name);
if ((tmp = realloc(ret, rlen + nlen + 2)) == NULL) {
free(ret);
return NULL;
}
ret = tmp;
memcpy(ret + rlen, k->name, nlen + 1);
rlen += nlen;
}
return ret;
}
static const struct kexalg *
kex_alg_by_name(const char *name)
{
const struct kexalg *k;
for (k = kexalgs; k->name != NULL; k++) {
if (strcmp(k->name, name) == 0)
return k;
}
return NULL;
}
/* Validate KEX method name list */
int
kex_names_valid(const char *names)
{
char *s, *cp, *p;
if (names == NULL || strcmp(names, "") == 0)
return 0;
if ((s = cp = strdup(names)) == NULL)
return 0;
for ((p = strsep(&cp, ",")); p && *p != '\0';
(p = strsep(&cp, ","))) {
if (kex_alg_by_name(p) == NULL) {
error("Unsupported KEX algorithm \"%.100s\"", p);
free(s);
return 0;
}
}
debug3("kex names ok: [%s]", names);
free(s);
return 1;
}
/* returns non-zero if proposal contains any algorithm from algs */
static int
has_any_alg(const char *proposal, const char *algs)
{
char *cp;
if ((cp = match_list(proposal, algs, NULL)) == NULL)
return 0;
free(cp);
return 1;
}
/*
* Concatenate algorithm names, avoiding duplicates in the process.
* Caller must free returned string.
*/
char *
kex_names_cat(const char *a, const char *b)
{
char *ret = NULL, *tmp = NULL, *cp, *p;
size_t len;
if (a == NULL || *a == '\0')
return strdup(b);
if (b == NULL || *b == '\0')
return strdup(a);
if (strlen(b) > 1024*1024)
return NULL;
len = strlen(a) + strlen(b) + 2;
if ((tmp = cp = strdup(b)) == NULL ||
(ret = calloc(1, len)) == NULL) {
free(tmp);
return NULL;
}
strlcpy(ret, a, len);
for ((p = strsep(&cp, ",")); p && *p != '\0'; (p = strsep(&cp, ","))) {
if (has_any_alg(ret, p))
continue; /* Algorithm already present */
if (strlcat(ret, ",", len) >= len ||
strlcat(ret, p, len) >= len) {
free(tmp);
free(ret);
return NULL; /* Shouldn't happen */
}
}
free(tmp);
return ret;
}
/*
* Assemble a list of algorithms from a default list and a string from a
* configuration file. The user-provided string may begin with '+' to
* indicate that it should be appended to the default, '-' that the
* specified names should be removed, or '^' that they should be placed
* at the head.
*/
int
kex_assemble_names(char **listp, const char *def, const char *all)
{
char *cp, *tmp, *patterns;
char *list = NULL, *ret = NULL, *matching = NULL, *opatterns = NULL;
int r = SSH_ERR_INTERNAL_ERROR;
if (listp == NULL || def == NULL || all == NULL)
return SSH_ERR_INVALID_ARGUMENT;
if (*listp == NULL || **listp == '\0') {
if ((*listp = strdup(def)) == NULL)
return SSH_ERR_ALLOC_FAIL;
return 0;
}
list = *listp;
*listp = NULL;
if (*list == '+') {
/* Append names to default list */
if ((tmp = kex_names_cat(def, list + 1)) == NULL) {
r = SSH_ERR_ALLOC_FAIL;
goto fail;
}
free(list);
list = tmp;
} else if (*list == '-') {
/* Remove names from default list */
if ((*listp = match_filter_denylist(def, list + 1)) == NULL) {
r = SSH_ERR_ALLOC_FAIL;
goto fail;
}
free(list);
/* filtering has already been done */
return 0;
} else if (*list == '^') {
/* Place names at head of default list */
if ((tmp = kex_names_cat(list + 1, def)) == NULL) {
r = SSH_ERR_ALLOC_FAIL;
goto fail;
}
free(list);
list = tmp;
} else {
/* Explicit list, overrides default - just use "list" as is */
}
/*
* The supplied names may be a pattern-list. For the -list case,
* the patterns are applied above. For the +list and explicit list
* cases we need to do it now.
*/
ret = NULL;
if ((patterns = opatterns = strdup(list)) == NULL) {
r = SSH_ERR_ALLOC_FAIL;
goto fail;
}
/* Apply positive (i.e. non-negated) patterns from the list */
while ((cp = strsep(&patterns, ",")) != NULL) {
if (*cp == '!') {
/* negated matches are not supported here */
r = SSH_ERR_INVALID_ARGUMENT;
goto fail;
}
free(matching);
if ((matching = match_filter_allowlist(all, cp)) == NULL) {
r = SSH_ERR_ALLOC_FAIL;
goto fail;
}
if ((tmp = kex_names_cat(ret, matching)) == NULL) {
r = SSH_ERR_ALLOC_FAIL;
goto fail;
}
free(ret);
ret = tmp;
}
if (ret == NULL || *ret == '\0') {
/* An empty name-list is an error */
/* XXX better error code? */
r = SSH_ERR_INVALID_ARGUMENT;
goto fail;
}
/* success */
*listp = ret;
ret = NULL;
r = 0;
fail:
free(matching);
free(opatterns);
free(list);
free(ret);
return r;
}
/*
* Fill out a proposal array with dynamically allocated values, which may
* be modified as required for compatibility reasons.
@ -527,11 +279,11 @@ kex_set_server_sig_algs(struct ssh *ssh, const char *allowed_algs)
(alg = strsep(&algs, ","))) {
if ((sigalg = sshkey_sigalg_by_name(alg)) == NULL)
continue;
if (!has_any_alg(sigalg, sigalgs))
if (!kex_has_any_alg(sigalg, sigalgs))
continue;
/* Don't add an algorithm twice. */
if (ssh->kex->server_sig_algs != NULL &&
has_any_alg(sigalg, ssh->kex->server_sig_algs))
kex_has_any_alg(sigalg, ssh->kex->server_sig_algs))
continue;
xextendf(&ssh->kex->server_sig_algs, ",", "%s", sigalg);
}
@ -1108,20 +860,18 @@ choose_comp(struct sshcomp *comp, char *client, char *server)
static int
choose_kex(struct kex *k, char *client, char *server)
{
const struct kexalg *kexalg;
k->name = match_list(client, server, NULL);
debug("kex: algorithm: %s", k->name ? k->name : "(no match)");
if (k->name == NULL)
return SSH_ERR_NO_KEX_ALG_MATCH;
if ((kexalg = kex_alg_by_name(k->name)) == NULL) {
if (!kex_name_valid(k->name)) {
error_f("unsupported KEX method %s", k->name);
return SSH_ERR_INTERNAL_ERROR;
}
k->kex_type = kexalg->type;
k->hash_alg = kexalg->hash_alg;
k->ec_nid = kexalg->ec_nid;
k->kex_type = kex_type_from_name(k->name);
k->hash_alg = kex_hash_from_name(k->name);
k->ec_nid = kex_nid_from_name(k->name);
return 0;
}
@ -1171,7 +921,7 @@ proposals_match(char *my[PROPOSAL_MAX], char *peer[PROPOSAL_MAX])
static int
kexalgs_contains(char **peer, const char *ext)
{
return has_any_alg(peer[PROPOSAL_KEX_ALGS], ext);
return kex_has_any_alg(peer[PROPOSAL_KEX_ALGS], ext);
}
static int
@ -1222,10 +972,10 @@ kex_choose_conf(struct ssh *ssh, uint32_t seq)
/* Check whether client supports rsa-sha2 algorithms */
if (kex->server && (kex->flags & KEX_INITIAL)) {
if (has_any_alg(peer[PROPOSAL_SERVER_HOST_KEY_ALGS],
if (kex_has_any_alg(peer[PROPOSAL_SERVER_HOST_KEY_ALGS],
"rsa-sha2-256,rsa-sha2-256-cert-v01@openssh.com"))
kex->flags |= KEX_RSA_SHA2_256_SUPPORTED;
if (has_any_alg(peer[PROPOSAL_SERVER_HOST_KEY_ALGS],
if (kex_has_any_alg(peer[PROPOSAL_SERVER_HOST_KEY_ALGS],
"rsa-sha2-512,rsa-sha2-512-cert-v01@openssh.com"))
kex->flags |= KEX_RSA_SHA2_512_SUPPORTED;
}

9
kex.h
View File

@ -1,4 +1,4 @@
/* $OpenBSD: kex.h,v 1.122 2024/02/02 00:13:34 djm Exp $ */
/* $OpenBSD: kex.h,v 1.123 2024/05/17 00:30:23 djm Exp $ */
/*
* Copyright (c) 2000, 2001 Markus Friedl. All rights reserved.
@ -92,7 +92,7 @@ enum kex_modes {
};
enum kex_exchange {
KEX_DH_GRP1_SHA1,
KEX_DH_GRP1_SHA1 = 1,
KEX_DH_GRP14_SHA1,
KEX_DH_GRP14_SHA256,
KEX_DH_GRP16_SHA512,
@ -183,9 +183,14 @@ struct kex {
struct sshbuf *client_pub;
};
int kex_name_valid(const char *);
u_int kex_type_from_name(const char *);
int kex_hash_from_name(const char *);
int kex_nid_from_name(const char *);
int kex_names_valid(const char *);
char *kex_alg_list(char);
char *kex_names_cat(const char *, const char *);
int kex_has_any_alg(const char *, const char *);
int kex_assemble_names(char **, const char *, const char *);
void kex_proposal_populate_entries(struct ssh *, char *prop[PROPOSAL_MAX],
const char *, const char *, const char *, const char *, const char *);

View File

@ -1,4 +1,4 @@
/* $OpenBSD: kexgexs.c,v 1.46 2023/03/29 01:07:48 dtucker Exp $ */
/* $OpenBSD: kexgexs.c,v 1.47 2024/05/17 00:30:23 djm Exp $ */
/*
* Copyright (c) 2000 Niels Provos. All rights reserved.
* Copyright (c) 2001 Markus Friedl. All rights reserved.
@ -98,7 +98,7 @@ input_kex_dh_gex_request(int type, u_int32_t seq, struct ssh *ssh)
}
/* Contact privileged parent */
kex->dh = PRIVSEP(choose_dh(min, nbits, max));
kex->dh = mm_choose_dh(min, nbits, max);
if (kex->dh == NULL) {
(void)sshpkt_disconnect(ssh, "no matching DH grp found");
r = SSH_ERR_ALLOC_FAIL;

15
misc.c
View File

@ -1,4 +1,4 @@
/* $OpenBSD: misc.c,v 1.193 2024/04/02 10:02:08 deraadt Exp $ */
/* $OpenBSD: misc.c,v 1.194 2024/05/17 00:30:23 djm Exp $ */
/*
* Copyright (c) 2000 Markus Friedl. All rights reserved.
* Copyright (c) 2005-2020 Damien Miller. All rights reserved.
@ -2004,6 +2004,19 @@ forward_equals(const struct Forward *a, const struct Forward *b)
return 1;
}
/* returns port number, FWD_PERMIT_ANY_PORT or -1 on error */
int
permitopen_port(const char *p)
{
int port;
if (strcmp(p, "*") == 0)
return FWD_PERMIT_ANY_PORT;
if ((port = a2port(p)) > 0)
return port;
return -1;
}
/* returns 1 if process is already daemonized, 0 otherwise */
int
daemonized(void)

10
misc.h
View File

@ -1,4 +1,4 @@
/* $OpenBSD: misc.h,v 1.107 2024/03/04 02:16:11 djm Exp $ */
/* $OpenBSD: misc.h,v 1.108 2024/05/17 00:30:24 djm Exp $ */
/*
* Author: Tatu Ylonen <ylo@cs.hut.fi>
@ -21,6 +21,12 @@
#include <stdio.h>
#include <signal.h>
/* special-case port number meaning allow any port */
#define FWD_PERMIT_ANY_PORT 0
/* special-case wildcard meaning allow any host */
#define FWD_PERMIT_ANY_HOST "*"
/* Data structure for representing a forwarding request. */
struct Forward {
char *listen_host; /* Host (address) to listen on. */
@ -34,6 +40,8 @@ struct Forward {
};
int forward_equals(const struct Forward *, const struct Forward *);
int permitopen_port(const char *p);
int daemonized(void);
/* Common server and client forwarding options. */

View File

@ -1,4 +1,4 @@
/* $OpenBSD: monitor.c,v 1.237 2023/08/16 16:14:11 djm Exp $ */
/* $OpenBSD: monitor.c,v 1.238 2024/05/17 00:30:24 djm Exp $ */
/*
* Copyright 2002 Niels Provos <provos@citi.umich.edu>
* Copyright 2002 Markus Friedl <markus@openbsd.org>
@ -707,13 +707,39 @@ mm_answer_sign(struct ssh *ssh, int sock, struct sshbuf *m)
fatal_fr(r, "assemble %s", #id); \
} while (0)
void
mm_encode_server_options(struct sshbuf *m)
{
int r;
u_int i;
/* XXX this leaks raw pointers to the unpriv child processes */
if ((r = sshbuf_put_string(m, &options, sizeof(options))) != 0)
fatal_fr(r, "assemble options");
#define M_CP_STROPT(x) do { \
if (options.x != NULL && \
(r = sshbuf_put_cstring(m, options.x)) != 0) \
fatal_fr(r, "assemble %s", #x); \
} while (0)
#define M_CP_STRARRAYOPT(x, nx) do { \
for (i = 0; i < options.nx; i++) { \
if ((r = sshbuf_put_cstring(m, options.x[i])) != 0) \
fatal_fr(r, "assemble %s", #x); \
} \
} while (0)
/* See comment in servconf.h */
COPY_MATCH_STRING_OPTS();
#undef M_CP_STROPT
#undef M_CP_STRARRAYOPT
}
/* Retrieves the password entry and also checks if the user is permitted */
int
mm_answer_pwnamallow(struct ssh *ssh, int sock, struct sshbuf *m)
{
struct passwd *pwent;
int r, allowed = 0;
u_int i;
debug3_f("entering");
@ -766,24 +792,9 @@ mm_answer_pwnamallow(struct ssh *ssh, int sock, struct sshbuf *m)
out:
ssh_packet_set_log_preamble(ssh, "%suser %s",
authctxt->valid ? "authenticating" : "invalid ", authctxt->user);
if ((r = sshbuf_put_string(m, &options, sizeof(options))) != 0)
fatal_fr(r, "assemble options");
#define M_CP_STROPT(x) do { \
if (options.x != NULL && \
(r = sshbuf_put_cstring(m, options.x)) != 0) \
fatal_fr(r, "assemble %s", #x); \
} while (0)
#define M_CP_STRARRAYOPT(x, nx) do { \
for (i = 0; i < options.nx; i++) { \
if ((r = sshbuf_put_cstring(m, options.x[i])) != 0) \
fatal_fr(r, "assemble %s", #x); \
} \
} while (0)
/* See comment in servconf.h */
COPY_MATCH_STRING_OPTS();
#undef M_CP_STROPT
#undef M_CP_STRARRAYOPT
/* Send active options to unpriv */
mm_encode_server_options(m);
/* Create valid auth method lists */
if (auth2_setup_methods_lists(authctxt) != 0) {

View File

@ -1,4 +1,4 @@
/* $OpenBSD: monitor.h,v 1.23 2019/01/19 21:43:56 djm Exp $ */
/* $OpenBSD: monitor.h,v 1.24 2024/05/17 00:30:24 djm Exp $ */
/*
* Copyright 2002 Niels Provos <provos@citi.umich.edu>
@ -66,6 +66,7 @@ enum monitor_reqtype {
};
struct ssh;
struct sshbuf;
struct monitor {
int m_recvfd;
@ -92,4 +93,7 @@ void mm_request_receive(int, struct sshbuf *);
void mm_request_receive_expect(int, enum monitor_reqtype, struct sshbuf *);
void mm_get_keystate(struct ssh *, struct monitor *);
/* XXX: should be returned via a monitor call rather than config_fd */
void mm_encode_server_options(struct sshbuf *);
#endif /* _MONITOR_H_ */

View File

@ -1,4 +1,4 @@
/* $OpenBSD: monitor_wrap.c,v 1.129 2023/12/18 14:45:49 djm Exp $ */
/* $OpenBSD: monitor_wrap.c,v 1.130 2024/05/17 00:30:24 djm Exp $ */
/*
* Copyright 2002 Niels Provos <provos@citi.umich.edu>
* Copyright 2002 Markus Friedl <markus@openbsd.org>
@ -65,7 +65,6 @@
#ifdef GSSAPI
#include "ssh-gss.h"
#endif
#include "monitor_wrap.h"
#include "atomicio.h"
#include "monitor_fdpass.h"
#include "misc.h"
@ -73,6 +72,7 @@
#include "channels.h"
#include "session.h"
#include "servconf.h"
#include "monitor_wrap.h"
#include "ssherr.h"
@ -147,8 +147,10 @@ mm_request_receive(int sock, struct sshbuf *m)
debug3_f("entering");
if (atomicio(read, sock, buf, sizeof(buf)) != sizeof(buf)) {
if (errno == EPIPE)
if (errno == EPIPE) {
debug3_f("monitor fd closed");
cleanup_exit(255);
}
fatal_f("read: %s", strerror(errno));
}
msg_len = PEEK_U32(buf);
@ -243,6 +245,49 @@ mm_sshkey_sign(struct ssh *ssh, struct sshkey *key, u_char **sigp, size_t *lenp,
return (0);
}
void
mm_decode_activate_server_options(struct ssh *ssh, struct sshbuf *m)
{
const u_char *p;
size_t len;
u_int i;
ServerOptions *newopts;
int r;
if ((r = sshbuf_get_string_direct(m, &p, &len)) != 0)
fatal_fr(r, "parse opts");
if (len != sizeof(*newopts))
fatal_f("option block size mismatch");
newopts = xcalloc(sizeof(*newopts), 1);
memcpy(newopts, p, sizeof(*newopts));
#define M_CP_STROPT(x) do { \
if (newopts->x != NULL && \
(r = sshbuf_get_cstring(m, &newopts->x, NULL)) != 0) \
fatal_fr(r, "parse %s", #x); \
} while (0)
#define M_CP_STRARRAYOPT(x, nx) do { \
newopts->x = newopts->nx == 0 ? \
NULL : xcalloc(newopts->nx, sizeof(*newopts->x)); \
for (i = 0; i < newopts->nx; i++) { \
if ((r = sshbuf_get_cstring(m, \
&newopts->x[i], NULL)) != 0) \
fatal_fr(r, "parse %s", #x); \
} \
} while (0)
/* See comment in servconf.h */
COPY_MATCH_STRING_OPTS();
#undef M_CP_STROPT
#undef M_CP_STRARRAYOPT
copy_set_server_options(&options, newopts, 1);
log_change_level(options.log_level);
log_verbose_reset();
for (i = 0; i < options.num_log_verbose; i++)
log_verbose_add(options.log_verbose[i]);
free(newopts);
}
#define GETPW(b, id) \
do { \
if ((r = sshbuf_get_string_direct(b, &p, &len)) != 0) \
@ -258,8 +303,6 @@ mm_getpwnamallow(struct ssh *ssh, const char *username)
struct sshbuf *m;
struct passwd *pw;
size_t len;
u_int i;
ServerOptions *newopts;
int r;
u_char ok;
const u_char *p;
@ -307,41 +350,10 @@ mm_getpwnamallow(struct ssh *ssh, const char *username)
out:
/* copy options block as a Match directive may have changed some */
if ((r = sshbuf_get_string_direct(m, &p, &len)) != 0)
fatal_fr(r, "parse opts");
if (len != sizeof(*newopts))
fatal_f("option block size mismatch");
newopts = xcalloc(sizeof(*newopts), 1);
memcpy(newopts, p, sizeof(*newopts));
#define M_CP_STROPT(x) do { \
if (newopts->x != NULL && \
(r = sshbuf_get_cstring(m, &newopts->x, NULL)) != 0) \
fatal_fr(r, "parse %s", #x); \
} while (0)
#define M_CP_STRARRAYOPT(x, nx) do { \
newopts->x = newopts->nx == 0 ? \
NULL : xcalloc(newopts->nx, sizeof(*newopts->x)); \
for (i = 0; i < newopts->nx; i++) { \
if ((r = sshbuf_get_cstring(m, \
&newopts->x[i], NULL)) != 0) \
fatal_fr(r, "parse %s", #x); \
} \
} while (0)
/* See comment in servconf.h */
COPY_MATCH_STRING_OPTS();
#undef M_CP_STROPT
#undef M_CP_STRARRAYOPT
copy_set_server_options(&options, newopts, 1);
log_change_level(options.log_level);
log_verbose_reset();
for (i = 0; i < options.num_log_verbose; i++)
log_verbose_add(options.log_verbose[i]);
process_permitopen(ssh, &options);
process_channel_timeouts(ssh, &options);
mm_decode_activate_server_options(ssh, m);
server_process_permitopen(ssh);
server_process_channel_timeouts(ssh);
kex_set_server_sig_algs(ssh, options.pubkey_accepted_algos);
free(newopts);
sshbuf_free(m);
return (pw);
@ -1018,3 +1030,91 @@ mm_ssh_gssapi_userok(char *user)
return (authenticated);
}
#endif /* GSSAPI */
/*
* Inform channels layer of permitopen options for a single forwarding
* direction (local/remote).
*/
static void
server_process_permitopen_list(struct ssh *ssh, int listen,
char **opens, u_int num_opens)
{
u_int i;
int port;
char *host, *arg, *oarg;
int where = listen ? FORWARD_REMOTE : FORWARD_LOCAL;
const char *what = listen ? "permitlisten" : "permitopen";
channel_clear_permission(ssh, FORWARD_ADM, where);
if (num_opens == 0)
return; /* permit any */
/* handle keywords: "any" / "none" */
if (num_opens == 1 && strcmp(opens[0], "any") == 0)
return;
if (num_opens == 1 && strcmp(opens[0], "none") == 0) {
channel_disable_admin(ssh, where);
return;
}
/* Otherwise treat it as a list of permitted host:port */
for (i = 0; i < num_opens; i++) {
oarg = arg = xstrdup(opens[i]);
host = hpdelim(&arg);
if (host == NULL)
fatal_f("missing host in %s", what);
host = cleanhostname(host);
if (arg == NULL || ((port = permitopen_port(arg)) < 0))
fatal_f("bad port number in %s", what);
/* Send it to channels layer */
channel_add_permission(ssh, FORWARD_ADM,
where, host, port);
free(oarg);
}
}
/*
* Inform channels layer of permitopen options from configuration.
*/
void
server_process_permitopen(struct ssh *ssh)
{
server_process_permitopen_list(ssh, 0,
options.permitted_opens, options.num_permitted_opens);
server_process_permitopen_list(ssh, 1,
options.permitted_listens, options.num_permitted_listens);
}
void
server_process_channel_timeouts(struct ssh *ssh)
{
u_int i, secs;
char *type;
debug3_f("setting %u timeouts", options.num_channel_timeouts);
channel_clear_timeouts(ssh);
for (i = 0; i < options.num_channel_timeouts; i++) {
if (parse_pattern_interval(options.channel_timeouts[i],
&type, &secs) != 0) {
fatal_f("internal error: bad timeout %s",
options.channel_timeouts[i]);
}
channel_add_timeout(ssh, type, secs);
free(type);
}
}
struct connection_info *
server_get_connection_info(struct ssh *ssh, int populate, int use_dns)
{
static struct connection_info ci;
if (ssh == NULL || !populate)
return &ci;
ci.host = use_dns ? ssh_remote_hostname(ssh) : ssh_remote_ipaddr(ssh);
ci.address = ssh_remote_ipaddr(ssh);
ci.laddress = ssh_local_ipaddr(ssh);
ci.lport = ssh_local_port(ssh);
ci.rdomain = ssh_packet_rdomain_in(ssh);
return &ci;
}

View File

@ -1,4 +1,4 @@
/* $OpenBSD: monitor_wrap.h,v 1.49 2022/06/15 16:08:25 djm Exp $ */
/* $OpenBSD: monitor_wrap.h,v 1.50 2024/05/17 00:30:24 djm Exp $ */
/*
* Copyright 2002 Niels Provos <provos@citi.umich.edu>
@ -28,9 +28,6 @@
#ifndef _MM_WRAP_H_
#define _MM_WRAP_H_
extern int use_privsep;
#define PRIVSEP(x) (use_privsep ? mm_##x : x)
enum mm_keytype { MM_NOKEY, MM_HOSTKEY, MM_USERKEY };
struct ssh;
@ -61,6 +58,8 @@ int mm_hostbased_key_allowed(struct ssh *, struct passwd *, const char *,
int mm_sshkey_verify(const struct sshkey *, const u_char *, size_t,
const u_char *, size_t, const char *, u_int, struct sshkey_sig_details **);
void mm_decode_activate_server_options(struct ssh *ssh, struct sshbuf *m);
#ifdef GSSAPI
OM_uint32 mm_ssh_gssapi_server_ctx(Gssctxt **, gss_OID);
OM_uint32 mm_ssh_gssapi_accept_ctx(Gssctxt *,
@ -99,4 +98,10 @@ void mm_send_keystate(struct ssh *, struct monitor*);
int mm_bsdauth_query(void *, char **, char **, u_int *, char ***, u_int **);
int mm_bsdauth_respond(void *, u_int, char **);
/* config / channels glue */
void server_process_permitopen(struct ssh *);
void server_process_channel_timeouts(struct ssh *ssh);
struct connection_info *
server_get_connection_info(struct ssh *, int, int);
#endif /* _MM_WRAP_H_ */

5
msg.c
View File

@ -1,4 +1,4 @@
/* $OpenBSD: msg.c,v 1.20 2020/10/18 11:32:01 djm Exp $ */
/* $OpenBSD: msg.c,v 1.21 2024/05/17 00:30:24 djm Exp $ */
/*
* Copyright (c) 2002 Markus Friedl. All rights reserved.
*
@ -47,7 +47,7 @@ ssh_msg_send(int fd, u_char type, struct sshbuf *m)
u_char buf[5];
u_int mlen = sshbuf_len(m);
debug3_f("type %u", (unsigned int)type & 0xff);
debug3_f("type %u len %zu", (unsigned int)type & 0xff, sshbuf_len(m));
put_u32(buf, mlen + 1);
buf[4] = type; /* 1st byte of payload is mesg-type */
@ -59,6 +59,7 @@ ssh_msg_send(int fd, u_char type, struct sshbuf *m)
error_f("write: %s", strerror(errno));
return (-1);
}
debug3_f("done");
return (0);
}

View File

@ -1,4 +1,4 @@
/* $OpenBSD: packet.c,v 1.313 2023/12/18 14:45:17 djm Exp $ */
/* $OpenBSD: packet.c,v 1.314 2024/05/17 00:30:24 djm Exp $ */
/*
* Author: Tatu Ylonen <ylo@cs.hut.fi>
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@ -534,6 +534,98 @@ ssh_remote_ipaddr(struct ssh *ssh)
return ssh->remote_ipaddr;
}
/*
* Returns the remote DNS hostname as a string. The returned string must not
* be freed. NB. this will usually trigger a DNS query. Return value is on
* heap and no caching is performed.
* This function does additional checks on the hostname to mitigate some
* attacks on based on conflation of hostnames and addresses and will
* fall back to returning an address on error.
*/
char *
ssh_remote_hostname(struct ssh *ssh)
{
struct sockaddr_storage from;
socklen_t fromlen;
struct addrinfo hints, *ai, *aitop;
char name[NI_MAXHOST], ntop2[NI_MAXHOST];
const char *ntop = ssh_remote_ipaddr(ssh);
/* Get IP address of client. */
fromlen = sizeof(from);
memset(&from, 0, sizeof(from));
if (getpeername(ssh_packet_get_connection_in(ssh),
(struct sockaddr *)&from, &fromlen) == -1) {
debug_f("getpeername failed: %.100s", strerror(errno));
return xstrdup(ntop);
}
ipv64_normalise_mapped(&from, &fromlen);
if (from.ss_family == AF_INET6)
fromlen = sizeof(struct sockaddr_in6);
debug3("trying to reverse map address %.100s.", ntop);
/* Map the IP address to a host name. */
if (getnameinfo((struct sockaddr *)&from, fromlen, name, sizeof(name),
NULL, 0, NI_NAMEREQD) != 0) {
/* Host name not found. Use ip address. */
return xstrdup(ntop);
}
/*
* if reverse lookup result looks like a numeric hostname,
* someone is trying to trick us by PTR record like following:
* 1.1.1.10.in-addr.arpa. IN PTR 2.3.4.5
*/
memset(&hints, 0, sizeof(hints));
hints.ai_socktype = SOCK_DGRAM; /*dummy*/
hints.ai_flags = AI_NUMERICHOST;
if (getaddrinfo(name, NULL, &hints, &ai) == 0) {
logit("Nasty PTR record \"%s\" is set up for %s, ignoring",
name, ntop);
freeaddrinfo(ai);
return xstrdup(ntop);
}
/* Names are stored in lowercase. */
lowercase(name);
/*
* Map it back to an IP address and check that the given
* address actually is an address of this host. This is
* necessary because anyone with access to a name server can
* define arbitrary names for an IP address. Mapping from
* name to IP address can be trusted better (but can still be
* fooled if the intruder has access to the name server of
* the domain).
*/
memset(&hints, 0, sizeof(hints));
hints.ai_family = from.ss_family;
hints.ai_socktype = SOCK_STREAM;
if (getaddrinfo(name, NULL, &hints, &aitop) != 0) {
logit("reverse mapping checking getaddrinfo for %.700s "
"[%s] failed.", name, ntop);
return xstrdup(ntop);
}
/* Look for the address from the list of addresses. */
for (ai = aitop; ai; ai = ai->ai_next) {
if (getnameinfo(ai->ai_addr, ai->ai_addrlen, ntop2,
sizeof(ntop2), NULL, 0, NI_NUMERICHOST) == 0 &&
(strcmp(ntop, ntop2) == 0))
break;
}
freeaddrinfo(aitop);
/* If we reached the end of the list, the address was not there. */
if (ai == NULL) {
/* Address not found for the host name. */
logit("Address %.100s maps to %.600s, but this does not "
"map back to the address.", ntop, name);
return xstrdup(ntop);
}
return xstrdup(name);
}
/* Returns the port number of the remote host. */
int

View File

@ -1,4 +1,4 @@
/* $OpenBSD: packet.h,v 1.96 2023/12/18 14:45:17 djm Exp $ */
/* $OpenBSD: packet.h,v 1.97 2024/05/17 00:30:24 djm Exp $ */
/*
* Author: Tatu Ylonen <ylo@cs.hut.fi>
@ -165,6 +165,7 @@ int ssh_remote_port(struct ssh *);
const char *ssh_local_ipaddr(struct ssh *);
int ssh_local_port(struct ssh *);
const char *ssh_packet_rdomain_in(struct ssh *);
char *ssh_remote_hostname(struct ssh *);
void ssh_packet_set_rekey_limits(struct ssh *, u_int64_t, u_int32_t);
time_t ssh_packet_get_rekey_timeout(struct ssh *);

View File

@ -1,4 +1,4 @@
/* $OpenBSD: pathnames.h,v 1.31 2019/11/12 19:33:08 markus Exp $ */
/* $OpenBSD: pathnames.h,v 1.32 2024/05/17 00:30:24 djm Exp $ */
/*
* Author: Tatu Ylonen <ylo@cs.hut.fi>
@ -47,6 +47,11 @@
#define _PATH_SSH_PROGRAM "/usr/bin/ssh"
#endif
/* Binary paths for the sshd components */
#ifndef _PATH_SSHD_SESSION
#define _PATH_SSHD_SESSION "/usr/libexec/sshd-session"
#endif
/*
* The process id of the daemon listening for connections is saved here to
* make it easier to kill the correct daemon when necessary.

View File

@ -1,4 +1,4 @@
/* $OpenBSD: servconf.c,v 1.405 2024/03/04 02:16:11 djm Exp $ */
/* $OpenBSD: servconf.c,v 1.406 2024/05/17 00:30:24 djm Exp $ */
/*
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
* All rights reserved
@ -77,8 +77,6 @@ static void parse_server_config_depth(ServerOptions *options,
const char *filename, struct sshbuf *conf, struct include_list *includes,
struct connection_info *connectinfo, int flags, int *activep, int depth);
/* Use of privilege separation or not */
extern int use_privsep;
extern struct sshbuf *cfg;
/* Initializes the server options to their default values. */
@ -197,6 +195,7 @@ initialize_server_options(ServerOptions *options)
options->channel_timeouts = NULL;
options->num_channel_timeouts = 0;
options->unused_connection_timeout = -1;
options->sshd_session_path = NULL;
}
/* Returns 1 if a string option is unset or set to "none" or 0 otherwise. */
@ -447,13 +446,11 @@ fill_default_server_options(ServerOptions *options)
options->required_rsa_size = SSH_RSA_MINIMUM_MODULUS_SIZE;
if (options->unused_connection_timeout == -1)
options->unused_connection_timeout = 0;
if (options->sshd_session_path == NULL)
options->sshd_session_path = xstrdup(_PATH_SSHD_SESSION);
assemble_algorithms(options);
/* Turn privilege separation and sandboxing on by default */
if (use_privsep == -1)
use_privsep = PRIVSEP_ON;
#define CLEAR_ON_NONE(v) \
do { \
if (option_clear_or_none(v)) { \
@ -531,6 +528,7 @@ typedef enum {
sAllowStreamLocalForwarding, sFingerprintHash, sDisableForwarding,
sExposeAuthInfo, sRDomain, sPubkeyAuthOptions, sSecurityKeyProvider,
sRequiredRSASize, sChannelTimeout, sUnusedConnectionTimeout,
sSshdMonitorPath,
sDeprecated, sIgnore, sUnsupported
} ServerOpCodes;
@ -693,6 +691,7 @@ static struct {
{ "requiredrsasize", sRequiredRSASize, SSHCFG_ALL },
{ "channeltimeout", sChannelTimeout, SSHCFG_ALL },
{ "unusedconnectiontimeout", sUnusedConnectionTimeout, SSHCFG_ALL },
{ "sshdmonitorpath", sSshdMonitorPath, SSHCFG_GLOBAL },
{ NULL, sBadOption, 0 }
};
@ -902,95 +901,6 @@ process_queued_listen_addrs(ServerOptions *options)
options->num_queued_listens = 0;
}
/*
* Inform channels layer of permitopen options for a single forwarding
* direction (local/remote).
*/
static void
process_permitopen_list(struct ssh *ssh, ServerOpCodes opcode,
char **opens, u_int num_opens)
{
u_int i;
int port;
char *host, *arg, *oarg;
int where = opcode == sPermitOpen ? FORWARD_LOCAL : FORWARD_REMOTE;
const char *what = lookup_opcode_name(opcode);
channel_clear_permission(ssh, FORWARD_ADM, where);
if (num_opens == 0)
return; /* permit any */
/* handle keywords: "any" / "none" */
if (num_opens == 1 && strcmp(opens[0], "any") == 0)
return;
if (num_opens == 1 && strcmp(opens[0], "none") == 0) {
channel_disable_admin(ssh, where);
return;
}
/* Otherwise treat it as a list of permitted host:port */
for (i = 0; i < num_opens; i++) {
oarg = arg = xstrdup(opens[i]);
host = hpdelim(&arg);
if (host == NULL)
fatal_f("missing host in %s", what);
host = cleanhostname(host);
if (arg == NULL || ((port = permitopen_port(arg)) < 0))
fatal_f("bad port number in %s", what);
/* Send it to channels layer */
channel_add_permission(ssh, FORWARD_ADM,
where, host, port);
free(oarg);
}
}
/*
* Inform channels layer of permitopen options from configuration.
*/
void
process_permitopen(struct ssh *ssh, ServerOptions *options)
{
process_permitopen_list(ssh, sPermitOpen,
options->permitted_opens, options->num_permitted_opens);
process_permitopen_list(ssh, sPermitListen,
options->permitted_listens,
options->num_permitted_listens);
}
void
process_channel_timeouts(struct ssh *ssh, ServerOptions *options)
{
int secs;
u_int i;
char *type;
debug3_f("setting %u timeouts", options->num_channel_timeouts);
channel_clear_timeouts(ssh);
for (i = 0; i < options->num_channel_timeouts; i++) {
if (parse_pattern_interval(options->channel_timeouts[i],
&type, &secs) != 0) {
fatal_f("internal error: bad timeout %s",
options->channel_timeouts[i]);
}
channel_add_timeout(ssh, type, secs);
free(type);
}
}
struct connection_info *
get_connection_info(struct ssh *ssh, int populate, int use_dns)
{
static struct connection_info ci;
if (ssh == NULL || !populate)
return &ci;
ci.host = auth_get_canonical_hostname(ssh, use_dns);
ci.address = ssh_remote_ipaddr(ssh);
ci.laddress = ssh_local_ipaddr(ssh);
ci.lport = ssh_local_port(ssh);
ci.rdomain = ssh_packet_rdomain_in(ssh);
return &ci;
}
/*
* The strategy for the Match blocks is that the config file is parsed twice.
*
@ -2593,6 +2503,10 @@ process_server_config_line_depth(ServerOptions *options, char *line,
}
goto parse_time;
case sSshdMonitorPath:
charptr = &options->sshd_session_path;
goto parse_filename;
case sDeprecated:
case sIgnore:
case sUnsupported:
@ -3167,6 +3081,7 @@ dump_config(ServerOptions *o)
#if defined(__OpenBSD__) || defined(HAVE_SYS_SET_PROCESS_RDOMAIN)
dump_cfg_string(sRDomain, o->routing_domain);
#endif
dump_cfg_string(sSshdMonitorPath, o->sshd_session_path);
/* string arguments requiring a lookup */
dump_cfg_string(sLogLevel, log_level_name(o->log_level));

View File

@ -1,4 +1,4 @@
/* $OpenBSD: servconf.h,v 1.160 2023/09/06 23:35:35 djm Exp $ */
/* $OpenBSD: servconf.h,v 1.161 2024/05/17 00:30:24 djm Exp $ */
/*
* Author: Tatu Ylonen <ylo@cs.hut.fi>
@ -27,11 +27,6 @@
#define PERMIT_NO_PASSWD 2
#define PERMIT_YES 3
/* use_privsep */
#define PRIVSEP_OFF 0
#define PRIVSEP_ON 1
#define PRIVSEP_NOSANDBOX 2
/* PermitOpen */
#define PERMITOPEN_ANY 0
#define PERMITOPEN_NONE -2
@ -233,6 +228,8 @@ typedef struct {
u_int num_channel_timeouts;
int unused_connection_timeout;
char *sshd_session_path;
} ServerOptions;
/* Information about the incoming connection as used by Match */
@ -297,18 +294,16 @@ TAILQ_HEAD(include_list, include_item);
M_CP_STRARRAYOPT(subsystem_args, num_subsystems); \
} while (0)
struct connection_info *get_connection_info(struct ssh *, int, int);
void initialize_server_options(ServerOptions *);
void fill_default_server_options(ServerOptions *);
int process_server_config_line(ServerOptions *, char *, const char *, int,
int *, struct connection_info *, struct include_list *includes);
void process_permitopen(struct ssh *ssh, ServerOptions *options);
void process_channel_timeouts(struct ssh *ssh, ServerOptions *);
void load_server_config(const char *, struct sshbuf *);
void parse_server_config(ServerOptions *, const char *, struct sshbuf *,
struct include_list *includes, struct connection_info *, int);
void parse_server_match_config(ServerOptions *,
struct include_list *includes, struct connection_info *);
int parse_channel_timeout(const char *, char **, u_int *);
int parse_server_match_testspec(struct connection_info *, char *);
int server_match_spec_complete(struct connection_info *);
void servconf_merge_subsystems(ServerOptions *, ServerOptions *);

View File

@ -1,4 +1,4 @@
/* $OpenBSD: serverloop.c,v 1.238 2024/04/30 02:14:10 djm Exp $ */
/* $OpenBSD: serverloop.c,v 1.239 2024/05/17 00:30:24 djm Exp $ */
/*
* Author: Tatu Ylonen <ylo@cs.hut.fi>
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@ -86,44 +86,23 @@ extern ServerOptions options;
/* XXX */
extern Authctxt *the_authctxt;
extern struct sshauthopt *auth_opts;
extern int use_privsep;
static int no_more_sessions = 0; /* Disallow further sessions. */
static volatile sig_atomic_t child_terminated = 0; /* The child has terminated. */
/* Cleanup on signals (!use_privsep case only) */
static volatile sig_atomic_t received_sigterm = 0;
/* prototypes */
static void server_init_dispatch(struct ssh *);
/* requested tunnel forwarding interface(s), shared with session.c */
char *tun_fwd_ifnames = NULL;
/* returns 1 if bind to specified port by specified user is permitted */
static int
bind_permitted(int port, uid_t uid)
{
if (use_privsep)
return 1; /* allow system to decide */
if (port < IPPORT_RESERVED && uid != 0)
return 0;
return 1;
}
static void
sigchld_handler(int sig)
{
child_terminated = 1;
}
static void
sigterm_handler(int sig)
{
received_sigterm = sig;
}
static void
client_alive_check(struct ssh *ssh)
{
@ -354,12 +333,6 @@ server_loop2(struct ssh *ssh, Authctxt *authctxt)
connection_in = ssh_packet_get_connection_in(ssh);
connection_out = ssh_packet_get_connection_out(ssh);
if (!use_privsep) {
ssh_signal(SIGTERM, sigterm_handler);
ssh_signal(SIGINT, sigterm_handler);
ssh_signal(SIGQUIT, sigterm_handler);
}
server_init_dispatch(ssh);
for (;;) {
@ -383,12 +356,6 @@ server_loop2(struct ssh *ssh, Authctxt *authctxt)
if (sigprocmask(SIG_SETMASK, &osigset, NULL) == -1)
error_f("osigset sigprocmask: %s", strerror(errno));
if (received_sigterm) {
logit("Exiting on signal %d", (int)received_sigterm);
/* Clean up sessions, utmp, etc. */
cleanup_exit(255);
}
channel_after_poll(ssh, pfd, npfd_active);
if (conn_in_ready &&
process_input(ssh, connection_in) < 0)
@ -498,7 +465,7 @@ server_request_direct_streamlocal(struct ssh *ssh)
/* XXX fine grained permissions */
if ((options.allow_streamlocal_forwarding & FORWARD_LOCAL) != 0 &&
auth_opts->permit_port_forwarding_flag &&
!options.disable_forwarding && (pw->pw_uid == 0 || use_privsep)) {
!options.disable_forwarding) {
c = channel_connect_to_path(ssh, target,
"direct-streamlocal@openssh.com", "direct-streamlocal");
} else {
@ -792,9 +759,7 @@ server_input_global_request(int type, u_int32_t seq, struct ssh *ssh)
(options.allow_tcp_forwarding & FORWARD_REMOTE) == 0 ||
!auth_opts->permit_port_forwarding_flag ||
options.disable_forwarding ||
(!want_reply && fwd.listen_port == 0) ||
(fwd.listen_port != 0 &&
!bind_permitted(fwd.listen_port, pw->pw_uid))) {
(!want_reply && fwd.listen_port == 0)) {
success = 0;
ssh_packet_send_debug(ssh, "Server has disabled port forwarding.");
} else {
@ -827,8 +792,7 @@ server_input_global_request(int type, u_int32_t seq, struct ssh *ssh)
/* check permissions */
if ((options.allow_streamlocal_forwarding & FORWARD_REMOTE) == 0
|| !auth_opts->permit_port_forwarding_flag ||
options.disable_forwarding ||
(pw->pw_uid != 0 && !use_privsep)) {
options.disable_forwarding) {
success = 0;
ssh_packet_send_debug(ssh, "Server has disabled "
"streamlocal forwarding.");

View File

@ -1,4 +1,4 @@
/* $OpenBSD: session.c,v 1.337 2024/02/01 02:37:33 djm Exp $ */
/* $OpenBSD: session.c,v 1.338 2024/05/17 00:30:24 djm Exp $ */
/*
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
* All rights reserved
@ -738,8 +738,6 @@ do_login(struct ssh *ssh, Session *s, const char *command)
{
socklen_t fromlen;
struct sockaddr_storage from;
struct passwd * pw = s->pw;
pid_t pid = getpid();
/*
* Get IP address of client. If the connection is not a socket, let
@ -755,26 +753,6 @@ do_login(struct ssh *ssh, Session *s, const char *command)
}
}
/* Record that there was a login on that tty from the remote host. */
if (!use_privsep)
record_login(pid, s->tty, pw->pw_name, pw->pw_uid,
session_get_remote_name_or_ip(ssh, utmp_len,
options.use_dns),
(struct sockaddr *)&from, fromlen);
#ifdef USE_PAM
/*
* If password change is needed, do it now.
* This needs to occur before the ~/.hushlogin check.
*/
if (options.use_pam && !use_privsep && s->authctxt->force_pwchange) {
display_loginmsg();
do_pam_chauthtok();
s->authctxt->force_pwchange = 0;
/* XXX - signal [net] parent to enable forwardings */
}
#endif
if (check_quietlogin(s, command))
return;
@ -1924,8 +1902,7 @@ session_pty_req(struct ssh *ssh, Session *s)
/* Allocate a pty and open it. */
debug("Allocating pty.");
if (!PRIVSEP(pty_allocate(&s->ptyfd, &s->ttyfd, s->tty,
sizeof(s->tty)))) {
if (!mm_pty_allocate(&s->ptyfd, &s->ttyfd, s->tty, sizeof(s->tty))) {
free(s->term);
s->term = NULL;
s->ptyfd = -1;
@ -1940,9 +1917,6 @@ session_pty_req(struct ssh *ssh, Session *s)
if ((r = sshpkt_get_end(ssh)) != 0)
sshpkt_fatal(ssh, r, "%s: parse packet", __func__);
if (!use_privsep)
pty_setowner(s->pw, s->tty);
/* Set window size from the packet. */
pty_change_window_size(s->ptyfd, s->row, s->col, s->xpixel, s->ypixel);
@ -2160,7 +2134,7 @@ session_signal_req(struct ssh *ssh, Session *s)
signame, s->forced ? "forced-command" : "subsystem");
goto out;
}
if (!use_privsep || mm_is_monitor()) {
if (mm_is_monitor()) {
error_f("session signalling requires privilege separation");
goto out;
}
@ -2303,7 +2277,7 @@ session_pty_cleanup2(Session *s)
void
session_pty_cleanup(Session *s)
{
PRIVSEP(session_pty_cleanup2(s));
mm_session_pty_cleanup2(s);
}
static char *
@ -2712,7 +2686,7 @@ do_cleanup(struct ssh *ssh, Authctxt *authctxt)
* Cleanup ptys/utmp only if privsep is disabled,
* or if running in monitor.
*/
if (!use_privsep || mm_is_monitor())
if (mm_is_monitor())
session_destroy_all(ssh, session_pty_cleanup2);
}

View File

@ -1,4 +1,4 @@
/* $OpenBSD: ssh_api.c,v 1.28 2024/01/09 21:39:14 djm Exp $ */
/* $OpenBSD: ssh_api.c,v 1.29 2024/05/17 00:30:24 djm Exp $ */
/*
* Copyright (c) 2012 Markus Friedl. All rights reserved.
*
@ -27,6 +27,7 @@
#include "log.h"
#include "authfile.h"
#include "sshkey.h"
#include "dh.h"
#include "misc.h"
#include "ssh2.h"
#include "version.h"
@ -49,10 +50,8 @@ int _ssh_host_key_sign(struct ssh *, struct sshkey *, struct sshkey *,
u_char **, size_t *, const u_char *, size_t, const char *);
/*
* stubs for the server side implementation of kex.
* disable privsep so our stubs will never be called.
* stubs for privsep calls in the server side implementation of kex.
*/
int use_privsep = 0;
int mm_sshkey_sign(struct sshkey *, u_char **, u_int *,
const u_char *, u_int, const char *, const char *, const char *, u_int);
@ -65,14 +64,20 @@ mm_sshkey_sign(struct sshkey *key, u_char **sigp, u_int *lenp,
const u_char *data, u_int datalen, const char *alg,
const char *sk_provider, const char *sk_pin, u_int compat)
{
return (-1);
size_t slen = 0;
int ret;
ret = sshkey_sign(key, sigp, &slen, data, datalen, alg,
sk_provider, sk_pin, compat);
*lenp = slen;
return ret;
}
#ifdef WITH_OPENSSL
DH *
mm_choose_dh(int min, int nbits, int max)
{
return (NULL);
return choose_dh(min, nbits, max);
}
#endif

1472
sshd-session.c Normal file

File diff suppressed because it is too large Load Diff

1317
sshd.c

File diff suppressed because it is too large Load Diff