Page MenuHomePhabricator

sshd with "ForceUser" does not compile on High Sierra
Open, LowPublic

Description

I'm not immediately able to get sshd from rSSH (with the ForceUser modification) to compile and run on macOS High Sierra.

I technically got it to build like this:

-       strnvis(ptitle, buf, sizeof(ptitle),
-           VIS_CSTYLE|VIS_NL|VIS_TAB|VIS_OCTAL);
+       strnvis(ptitle, buf, sizeof(ptitle), 0);

...but then it segfaults immediately.

Using the system sshd works fine, but without ForceUser I had to make this change to get things working with the Phacility extensions loaded, since otherwise we try to forward the SSH connection with -l local, and there's no such user account:

diff --git a/src/applications/almanac/util/AlmanacKeys.php b/src/applications/almanac/util/AlmanacKeys.php
index 7ed9098e9..7ebb972ac 100644
--- a/src/applications/almanac/util/AlmanacKeys.php
+++ b/src/applications/almanac/util/AlmanacKeys.php
@@ -62,7 +62,7 @@ final class AlmanacKeys extends Phobject {
     // we have no way to tell which host you think you're reaching: the SSH
     // protocol does not have a mechanism like a "Host" header.
     $username = PhabricatorEnv::getEnvConfig('cluster.instance');
-    if (strlen($username)) {
+    if (strlen($username) && false) {
       return $username;
     }

A possible fix would be to add magic for "local" here, but that's Phacility-specific logic which would be going into the vanilla upstream.

Another possible "fix" would be to create a user account named "local" so this kind of works by accident, but that seems particularly dumb.

Revisions and Commits

Restricted Differential Revision

Event Timeline

epriestley created this task.

Yikes on rPe57bfbf421f4. Let me see if I can make this build.

OpenSSH 7.9p1 builds cleanly and does not segfault immediately if configured like this:

./configure --with-ssl-dir=/usr/local/opt/openssl

Let me see if I can forward port the ForceUser patch to it, since it would be good to upgrade sshd anyway.

Good news: recent versions of sshd can pass the key fingerprint to the AuthorizedKeysCommand with %f: https://bugzilla.mindrot.org/show_bug.cgi?id=2081

I also have the ForceUser patch working against sshd-7.9p1. I'm going to let it soak for a bit on my local machine, but we can upgrade at some point.

I was also able to get OpenSSH 7.9p1 to build cleanly on OS X Mojave 10.14.2. For posterity:

$ brew install openssl
$ autoreconf
$ ./configure --with-ssl-dir=/usr/local/opt/openssl
$ make

I'll get this actually landed, but here's the patch I'm using for ForceUser until I get there:

diff --git a/openssh-7.9p1/.gitignore b/openssh-7.9p1/.gitignore
index 650eb3c..fdb20c5 100644
--- a/openssh-7.9p1/.gitignore
+++ b/openssh-7.9p1/.gitignore
@@ -3,6 +3,7 @@ buildpkg.sh
 config.h
 config.h.in
 config.status
+config.log
 configure
 openbsd-compat/Makefile
 openbsd-compat/regress/Makefile
diff --git a/openssh-7.9p1/auth.h b/openssh-7.9p1/auth.h
index 977562f..6b1adab 100644
--- a/openssh-7.9p1/auth.h
+++ b/openssh-7.9p1/auth.h
@@ -62,6 +62,7 @@ struct Authctxt {
 	int		 server_caused_failure;
 	int		 force_pwchange;
 	char		*user;		/* username sent by the client */
+  char    *original_user;
 	char		*service;
 	struct passwd	*pw;		/* set if 'valid' */
 	char		*style;
@@ -134,7 +135,7 @@ int      auth_password(struct ssh *, const char *);
 
 int	 hostbased_key_allowed(struct passwd *, const char *, char *,
 	    struct sshkey *);
-int	 user_key_allowed(struct ssh *, struct passwd *, struct sshkey *, int,
+int	 user_key_allowed(struct ssh *, struct Authctxt *, struct sshkey *, int,
     struct sshauthopt **);
 int	 auth2_key_already_used(Authctxt *, const struct sshkey *);
 
diff --git a/openssh-7.9p1/auth2-pubkey.c b/openssh-7.9p1/auth2-pubkey.c
index 2fb5950..729ee47 100644
--- a/openssh-7.9p1/auth2-pubkey.c
+++ b/openssh-7.9p1/auth2-pubkey.c
@@ -88,7 +88,6 @@ static int
 userauth_pubkey(struct ssh *ssh)
 {
 	Authctxt *authctxt = ssh->authctxt;
-	struct passwd *pw = authctxt->pw;
 	struct sshbuf *b = NULL;
 	struct sshkey *key = NULL;
 	char *pkalg = NULL, *userstyle = NULL, *key_s = NULL, *ca_s = NULL;
@@ -175,7 +174,7 @@ userauth_pubkey(struct ssh *ssh)
 			goto done;
 		}
 		/* reconstruct packet */
-		xasprintf(&userstyle, "%s%s%s", authctxt->user,
+		xasprintf(&userstyle, "%s%s%s", authctxt->original_user,
 		    authctxt->style ? ":" : "",
 		    authctxt->style ? authctxt->style : "");
 		if ((r = sshbuf_put_u8(b, SSH2_MSG_USERAUTH_REQUEST)) != 0 ||
@@ -192,7 +191,7 @@ userauth_pubkey(struct ssh *ssh)
 #endif
 		/* test for correct signature */
 		authenticated = 0;
-		if (PRIVSEP(user_key_allowed(ssh, pw, key, 1, &authopts)) &&
+		if (PRIVSEP(user_key_allowed(ssh, authctxt, key, 1, &authopts)) &&
 		    PRIVSEP(sshkey_verify(key, sig, slen,
 		    sshbuf_ptr(b), sshbuf_len(b),
 		    (ssh->compat & SSH_BUG_SIGTYPE) == 0 ? pkalg : NULL,
@@ -222,7 +221,7 @@ userauth_pubkey(struct ssh *ssh)
 		 * if a user is not allowed to login. is this an
 		 * issue? -markus
 		 */
-		if (PRIVSEP(user_key_allowed(ssh, pw, key, 0, NULL))) {
+		if (PRIVSEP(user_key_allowed(ssh, authctxt, key, 0, NULL))) {
 			if ((r = sshpkt_start(ssh, SSH2_MSG_USERAUTH_PK_OK))
 			    != 0 ||
 			    (r = sshpkt_put_cstring(ssh, pkalg)) != 0 ||
@@ -836,7 +835,7 @@ user_cert_trusted_ca(struct ssh *ssh, struct passwd *pw, struct sshkey *key,
  * returns 1 if the key is allowed or 0 otherwise.
  */
 static int
-user_key_allowed2(struct ssh *ssh, struct passwd *pw, struct sshkey *key,
+user_key_allowed2(struct ssh *ssh, struct Authctxt *authctxt, struct sshkey *key,
     char *file, struct sshauthopt **authoptsp)
 {
 	FILE *f;
@@ -846,11 +845,11 @@ user_key_allowed2(struct ssh *ssh, struct passwd *pw, struct sshkey *key,
 		*authoptsp = NULL;
 
 	/* Temporarily use the user's uid. */
-	temporarily_use_uid(pw);
+	temporarily_use_uid(authctxt->pw);
 
 	debug("trying public key file %s", file);
-	if ((f = auth_openkeyfile(file, pw, options.strict_modes)) != NULL) {
-		found_key = check_authkeys_file(ssh, pw, f, file,
+	if ((f = auth_openkeyfile(file, authctxt->pw, options.strict_modes)) != NULL) {
+		found_key = check_authkeys_file(ssh, authctxt->pw, f, file,
 		    key, authoptsp);
 		fclose(f);
 	}
@@ -864,10 +863,11 @@ user_key_allowed2(struct ssh *ssh, struct passwd *pw, struct sshkey *key,
  * returns 1 if the key is allowed or 0 otherwise.
  */
 static int
-user_key_command_allowed2(struct ssh *ssh, struct passwd *user_pw,
+user_key_command_allowed2(struct ssh *ssh, struct Authctxt *authctxt,
     struct sshkey *key, struct sshauthopt **authoptsp)
 {
 	struct passwd *runas_pw = NULL;
+  struct passwd *user_pw = authctxt->pw;
 	FILE *f = NULL;
 	int r, ok, found_key = 0;
 	int i, uid_swapped = 0, ac = 0;
@@ -928,7 +928,7 @@ user_key_command_allowed2(struct ssh *ssh, struct passwd *user_pw,
 	for (i = 1; i < ac; i++) {
 		tmp = percent_expand(av[i],
 		    "U", uidstr,
-		    "u", user_pw->pw_name,
+		    "u", authctxt->original_user,
 		    "h", user_pw->pw_dir,
 		    "t", sshkey_ssh_name(key),
 		    "f", key_fp,
@@ -949,7 +949,7 @@ user_key_command_allowed2(struct ssh *ssh, struct passwd *user_pw,
 	 */
 	if (ac == 1) {
 		av = xreallocarray(av, ac + 2, sizeof(*av));
-		av[1] = xstrdup(user_pw->pw_name);
+		av[1] = xstrdup(authctxt->original_user);
 		av[2] = NULL;
 		/* Fix up command too, since it is used in log messages */
 		free(command);
@@ -995,7 +995,7 @@ user_key_command_allowed2(struct ssh *ssh, struct passwd *user_pw,
  * Check whether key authenticates and authorises the user.
  */
 int
-user_key_allowed(struct ssh *ssh, struct passwd *pw, struct sshkey *key,
+user_key_allowed(struct ssh *ssh, struct Authctxt *authctxt, struct sshkey *key,
     int auth_attempt, struct sshauthopt **authoptsp)
 {
 	u_int success, i;
@@ -1010,12 +1010,12 @@ user_key_allowed(struct ssh *ssh, struct passwd *pw, struct sshkey *key,
 	    auth_key_is_revoked(key->cert->signature_key))
 		return 0;
 
-	if ((success = user_cert_trusted_ca(ssh, pw, key, &opts)) != 0)
+	if ((success = user_cert_trusted_ca(ssh, authctxt->pw, key, &opts)) != 0)
 		goto out;
 	sshauthopt_free(opts);
 	opts = NULL;
 
-	if ((success = user_key_command_allowed2(ssh, pw, key, &opts)) != 0)
+	if ((success = user_key_command_allowed2(ssh, authctxt, key, &opts)) != 0)
 		goto out;
 	sshauthopt_free(opts);
 	opts = NULL;
@@ -1024,8 +1024,8 @@ user_key_allowed(struct ssh *ssh, struct passwd *pw, struct sshkey *key,
 		if (strcasecmp(options.authorized_keys_files[i], "none") == 0)
 			continue;
 		file = expand_authorized_keys(
-		    options.authorized_keys_files[i], pw);
-		success = user_key_allowed2(ssh, pw, key, file, &opts);
+		    options.authorized_keys_files[i], authctxt->pw);
+		success = user_key_allowed2(ssh, authctxt, key, file, &opts);
 		free(file);
 	}
 
diff --git a/openssh-7.9p1/auth2.c b/openssh-7.9p1/auth2.c
index 4d19957..3848dbb 100644
--- a/openssh-7.9p1/auth2.c
+++ b/openssh-7.9p1/auth2.c
@@ -255,7 +255,7 @@ input_userauth_request(int type, u_int32_t seq, struct ssh *ssh)
 {
 	Authctxt *authctxt = ssh->authctxt;
 	Authmethod *m = NULL;
-	char *user, *service, *method, *style = NULL;
+	char *user, *original_user, *service, *method, *style = NULL;
 	int authenticated = 0;
 	double tstart = monotime_double();
 
@@ -263,6 +263,7 @@ input_userauth_request(int type, u_int32_t seq, struct ssh *ssh)
 		fatal("input_userauth_request: no authctxt");
 
 	user = packet_get_cstring(NULL);
+  original_user = xstrdup(user);
 	service = packet_get_cstring(NULL);
 	method = packet_get_cstring(NULL);
 	debug("userauth-request for user %s service %s method %s", user, service, method);
@@ -271,7 +272,15 @@ input_userauth_request(int type, u_int32_t seq, struct ssh *ssh)
 	if ((style = strchr(user, ':')) != NULL)
 		*style++ = 0;
 
+  if (options.force_user) {
+    debug("forcing user to %s", options.force_user);
+    free(user);
+    user = xstrdup(options.force_user);
+  }
+
 	if (authctxt->attempt++ == 0) {
+    authctxt->original_user = xstrdup(original_user);
+
 		/* setup auth context */
 		authctxt->pw = PRIVSEP(getpwnamallow(user));
 		authctxt->user = xstrdup(user);
@@ -301,11 +310,11 @@ input_userauth_request(int type, u_int32_t seq, struct ssh *ssh)
 		userauth_banner();
 		if (auth2_setup_methods_lists(authctxt) != 0)
 			packet_disconnect("no authentication methods enabled");
-	} else if (strcmp(user, authctxt->user) != 0 ||
+	} else if (strcmp(original_user, authctxt->original_user) != 0 ||
 	    strcmp(service, authctxt->service) != 0) {
 		packet_disconnect("Change of username or service not allowed: "
 		    "(%s,%s) -> (%s,%s)",
-		    authctxt->user, authctxt->service, user, service);
+		    authctxt->original_user, authctxt->service, original_user, service);
 	}
 	/* reset state */
 	auth2_challenge_stop(ssh);
@@ -333,6 +342,7 @@ input_userauth_request(int type, u_int32_t seq, struct ssh *ssh)
 
 	free(service);
 	free(user);
+  free(original_user);
 	free(method);
 	return 0;
 }
diff --git a/openssh-7.9p1/monitor.c b/openssh-7.9p1/monitor.c
index 531b299..db45279 100644
--- a/openssh-7.9p1/monitor.c
+++ b/openssh-7.9p1/monitor.c
@@ -1122,7 +1122,7 @@ mm_answer_keyallowed(int sock, struct sshbuf *m)
 {
 	struct ssh *ssh = active_state;	/* XXX */
 	struct sshkey *key = NULL;
-	char *cuser, *chost;
+	char *cuser, *chost, *corig;
 	u_int pubkey_auth_attempt;
 	enum mm_keytype type = 0;
 	int r, allowed = 0;
@@ -1132,6 +1132,7 @@ mm_answer_keyallowed(int sock, struct sshbuf *m)
 	if ((r = sshbuf_get_u32(m, &type)) != 0 ||
 	    (r = sshbuf_get_cstring(m, &cuser, NULL)) != 0 ||
 	    (r = sshbuf_get_cstring(m, &chost, NULL)) != 0 ||
+	    (r = sshbuf_get_cstring(m, &corig, NULL)) != 0 ||
 	    (r = sshkey_froms(m, &key)) != 0 ||
 	    (r = sshbuf_get_u32(m, &pubkey_auth_attempt)) != 0)
 		fatal("%s: buffer error: %s", __func__, ssh_err(r));
@@ -1146,6 +1147,11 @@ mm_answer_keyallowed(int sock, struct sshbuf *m)
 
 		switch (type) {
 		case MM_USERKEY:
+      if (authctxt->original_user) {
+        free(authctxt->original_user);
+      }
+      authctxt->original_user = xstrdup(corig);
+
 			auth_method = "publickey";
 			if (!options.pubkey_authentication)
 				break;
@@ -1154,7 +1160,7 @@ mm_answer_keyallowed(int sock, struct sshbuf *m)
 			if (match_pattern_list(sshkey_ssh_name(key),
 			    options.pubkey_key_types, 0) != 1)
 				break;
-			allowed = user_key_allowed(ssh, authctxt->pw, key,
+			allowed = user_key_allowed(ssh, authctxt, key,
 			    pubkey_auth_attempt, &opts);
 			break;
 		case MM_HOSTKEY:
@@ -1202,6 +1208,7 @@ mm_answer_keyallowed(int sock, struct sshbuf *m)
 		free(cuser);
 		free(chost);
 	}
+  free(corig);
 	sshkey_free(key);
 
 	sshbuf_reset(m);
@@ -1255,7 +1262,7 @@ monitor_valid_userblob(u_char *data, u_int datalen)
 		fail++;
 	if ((r = sshbuf_get_cstring(b, &cp, NULL)) != 0)
 		fatal("%s: buffer error: %s", __func__, ssh_err(r));
-	xasprintf(&userstyle, "%s%s%s", authctxt->user,
+	xasprintf(&userstyle, "%s%s%s", authctxt->original_user,
 	    authctxt->style ? ":" : "",
 	    authctxt->style ? authctxt->style : "");
 	if (strcmp(userstyle, cp) != 0) {
diff --git a/openssh-7.9p1/monitor_wrap.c b/openssh-7.9p1/monitor_wrap.c
index 732fb34..87338f3 100644
--- a/openssh-7.9p1/monitor_wrap.c
+++ b/openssh-7.9p1/monitor_wrap.c
@@ -430,23 +430,24 @@ mm_auth_password(struct ssh *ssh, char *password)
 }
 
 int
-mm_user_key_allowed(struct ssh *ssh, struct passwd *pw, struct sshkey *key,
+mm_user_key_allowed(struct ssh *ssh, struct Authctxt *authctxt, struct sshkey *key,
     int pubkey_auth_attempt, struct sshauthopt **authoptp)
 {
 	return (mm_key_allowed(MM_USERKEY, NULL, NULL, key,
-	    pubkey_auth_attempt, authoptp));
+	    pubkey_auth_attempt, authoptp, authctxt->original_user));
 }
 
 int
 mm_hostbased_key_allowed(struct passwd *pw, const char *user, const char *host,
     struct sshkey *key)
 {
-	return (mm_key_allowed(MM_HOSTKEY, user, host, key, 0, NULL));
+	return (mm_key_allowed(MM_HOSTKEY, user, host, key, 0, NULL, NULL));
 }
 
 int
 mm_key_allowed(enum mm_keytype type, const char *user, const char *host,
-    struct sshkey *key, int pubkey_auth_attempt, struct sshauthopt **authoptp)
+    struct sshkey *key, int pubkey_auth_attempt, struct sshauthopt **authoptp,
+    char *original_user)
 {
 	struct sshbuf *m;
 	int r, allowed = 0;
@@ -462,6 +463,7 @@ mm_key_allowed(enum mm_keytype type, const char *user, const char *host,
 	if ((r = sshbuf_put_u32(m, type)) != 0 ||
 	    (r = sshbuf_put_cstring(m, user ? user : "")) != 0 ||
 	    (r = sshbuf_put_cstring(m, host ? host : "")) != 0 ||
+      (r = sshbuf_put_cstring(m, original_user ? original_user : "")) != 0 ||
 	    (r = sshkey_puts(key, m)) != 0 ||
 	    (r = sshbuf_put_u32(m, pubkey_auth_attempt)) != 0)
 		fatal("%s: buffer error: %s", __func__, ssh_err(r));
diff --git a/openssh-7.9p1/monitor_wrap.h b/openssh-7.9p1/monitor_wrap.h
index 644da08..f9785da 100644
--- a/openssh-7.9p1/monitor_wrap.h
+++ b/openssh-7.9p1/monitor_wrap.h
@@ -48,8 +48,8 @@ struct passwd *mm_getpwnamallow(const char *);
 char *mm_auth2_read_banner(void);
 int mm_auth_password(struct ssh *, char *);
 int mm_key_allowed(enum mm_keytype, const char *, const char *, struct sshkey *,
-    int, struct sshauthopt **);
-int mm_user_key_allowed(struct ssh *, struct passwd *, struct sshkey *, int,
+    int, struct sshauthopt **, char *);
+int mm_user_key_allowed(struct ssh *, struct Authctxt *, struct sshkey *, int,
     struct sshauthopt **);
 int mm_hostbased_key_allowed(struct passwd *, const char *,
     const char *, struct sshkey *);
diff --git a/openssh-7.9p1/servconf.c b/openssh-7.9p1/servconf.c
index 932d363..f222a14 100644
--- a/openssh-7.9p1/servconf.c
+++ b/openssh-7.9p1/servconf.c
@@ -169,6 +169,7 @@ initialize_server_options(ServerOptions *options)
 	options->chroot_directory = NULL;
 	options->authorized_keys_command = NULL;
 	options->authorized_keys_command_user = NULL;
+	options->force_user = NULL;
 	options->revoked_keys_file = NULL;
 	options->trusted_user_ca_keys = NULL;
 	options->authorized_principals_file = NULL;
@@ -493,6 +494,7 @@ typedef enum {
 	sAuthorizedPrincipalsCommand, sAuthorizedPrincipalsCommandUser,
 	sKexAlgorithms, sCASignatureAlgorithms, sIPQoS, sVersionAddendum,
 	sAuthorizedKeysCommand, sAuthorizedKeysCommandUser,
+  sForceUser,
 	sAuthenticationMethods, sHostKeyAgent, sPermitUserRC,
 	sStreamLocalBindMask, sStreamLocalBindUnlink,
 	sAllowStreamLocalForwarding, sFingerprintHash, sDisableForwarding,
@@ -633,6 +635,7 @@ static struct {
 	{ "ipqos", sIPQoS, SSHCFG_ALL },
 	{ "authorizedkeyscommand", sAuthorizedKeysCommand, SSHCFG_ALL },
 	{ "authorizedkeyscommanduser", sAuthorizedKeysCommandUser, SSHCFG_ALL },
+	{ "forceuser", sForceUser, SSHCFG_ALL },
 	{ "authorizedprincipalscommand", sAuthorizedPrincipalsCommand, SSHCFG_ALL },
 	{ "authorizedprincipalscommanduser", sAuthorizedPrincipalsCommandUser, SSHCFG_ALL },
 	{ "versionaddendum", sVersionAddendum, SSHCFG_GLOBAL },
@@ -2037,6 +2040,16 @@ process_server_config_line(ServerOptions *options, char *line,
 			*charptr = xstrdup(arg);
 		break;
 
+	case sForceUser:
+		charptr = &options->force_user;
+
+		arg = strdelim(&cp);
+		if (!arg || *arg == '\0')
+			fatal("%s line %d: missing ForceUser argument.", filename, linenum);
+		if (*activep && *charptr == NULL)
+			*charptr = xstrdup(arg);
+		break;
+
 	case sAuthorizedPrincipalsCommand:
 		if (cp == NULL)
 			fatal("%.200s line %d: Missing argument.", filename,
@@ -2605,6 +2618,7 @@ dump_config(ServerOptions *o)
 	    ? "none" : o->version_addendum);
 	dump_cfg_string(sAuthorizedKeysCommand, o->authorized_keys_command);
 	dump_cfg_string(sAuthorizedKeysCommandUser, o->authorized_keys_command_user);
+  dump_cfg_string(sForceUser, o->force_user);
 	dump_cfg_string(sAuthorizedPrincipalsCommand, o->authorized_principals_command);
 	dump_cfg_string(sAuthorizedPrincipalsCommandUser, o->authorized_principals_command_user);
 	dump_cfg_string(sHostKeyAgent, o->host_key_agent);
diff --git a/openssh-7.9p1/servconf.h b/openssh-7.9p1/servconf.h
index 0175e00..a2c5240 100644
--- a/openssh-7.9p1/servconf.h
+++ b/openssh-7.9p1/servconf.h
@@ -195,6 +195,7 @@ typedef struct {
 	char   *trusted_user_ca_keys;
 	char   *authorized_keys_command;
 	char   *authorized_keys_command_user;
+  char   *force_user;
 	char   *authorized_principals_file;
 	char   *authorized_principals_command;
 	char   *authorized_principals_command_user;
epriestley added a revision: Restricted Differential Revision.Dec 19 2018, 8:48 PM
epriestley added a commit: Restricted Diffusion Commit.Dec 19 2018, 8:59 PM