upstream: Allow specification of agent socket directories

Add AgentSocketPath for sshd_config and -A flag for ssh-agent.

Agent socket directories may be shared or user-specific.

Shared directories (specified like "shared:/tmp") will cause the listening
program to create a temporary subdirectory ssh-XXXXXXXXXX under the requeted
path to hold the socket. This supports the old sshd/ssh-agent behaviour before
we switched to the socket directory being under ~/.ssh/agent

User-specific directories just create the socket directly in the requested
directory. This is the default, as user:.ssh/agent

bz3860; ok markus, deraadt

OpenBSD-Commit-ID: 91b95d02f376ad8959ffb23902cc115a0b74d9bf
This commit is contained in:
djm@openbsd.org authored and Damien Miller committed 2026-09-16 10:56:41 +10:00
1 parent 14843e73b9
commit cee6aedb1a
9 files changed
+341 -120

No files matched your search

+187 -63
View File
@@ -1,4 +1,4 @@
/* $OpenBSD: misc-agent.c,v 1.7 2026/02/11 17:05:32 dtucker Exp $ */
/* $OpenBSD: misc-agent.c,v 1.8 2026/09/16 00:25:50 djm Exp $ */
/*
* Copyright (c) 2025 Damien Miller <djm@mindrot.org>
*
@@ -30,6 +30,7 @@
#include <string.h>
#include <time.h>
#include <unistd.h>
#include <libgen.h>
#include "digest.h"
#include "log.h"
@@ -78,7 +79,7 @@ hostname_hash(size_t len)
return xstrdup(p);
}
char *
static char *
agent_hostname_hash(void)
{
return hostname_hash(SOCKET_HOSTNAME_HASHLEN);
@@ -154,71 +155,188 @@ unix_listener_tmp(char *path, int backlog)
}
/*
* Create a subdirectory under the supplied home directory if it
* doesn't already exist
* Shared directory case (e.g. /tmp): create a temporary directory
* for the socket.
*/
static int
ensure_mkdir(const char *homedir, const char *subdir)
agent_listener_shared(const char *parent_dir, pid_t pid, const char *tag,
int *sockp, char **pathp, char **dirp)
{
char *path;
char *dir = NULL, *path = NULL;
int sock, ret = -1;
mode_t prev_mask;
xasprintf(&path, "%s/%s", homedir, subdir);
if (mkdir(path, 0700) == 0)
debug("created directory %s", path);
else if (errno != EEXIST) {
error_f("mkdir %s: %s", path, strerror(errno));
free(path);
return -1;
*pathp = *dirp = NULL;
xasprintf(&dir, "%s/ssh-XXXXXXXXXXXX", parent_dir);
if (mkdtemp(dir) == NULL) {
error_f("failed to create temporary directory "
"in \"%s\": %s", dir, strerror(errno));
goto out;
}
xasprintf(&path, "%s/agent.%s.%ld", dir, tag, (long)pid);
prev_mask = umask(0177);
if ((sock = unix_listener(path, SSH_LISTEN_BACKLOG, 0)) < 0) {
/* Error already logged */
umask(prev_mask);
if (rmdir(dir) != 0)
error_f("rmdir \"%s\": %s", dir, strerror(errno));
goto out;
}
umask(prev_mask);
/* Success */
*dirp = dir;
dir = NULL; /* transferred */
*pathp = path;
path = NULL; /* transferred */
*sockp = sock;
ret = 0;
out:
free(dir);
free(path);
return 0;
return ret;
}
/*
* User-specific directory case (e.g. ~/.ssh/agent): ensure directory
* exists, and use a temp socket name under it.
*/
static int
agent_prepare_sockdir(const char *homedir)
agent_listener_user(const char *dir, pid_t pid, const char *tag,
int *sockp, char **pathp)
{
if (homedir == NULL || *homedir == '\0' ||
ensure_mkdir(homedir, _PATH_SSH_USER_DIR) != 0 ||
ensure_mkdir(homedir, _PATH_SSH_AGENT_SOCKET_DIR) != 0)
return -1;
return 0;
}
/* Get a path template for an agent socket in the user's homedir */
static char *
agent_socket_template(const char *homedir, const char *tag)
{
char *hostnamehash, *ret;
char *hostnamehash = NULL, *path = NULL;
int sock, ret = -1;
if ((hostnamehash = hostname_hash(SOCKET_HOSTNAME_HASHLEN)) == NULL)
return NULL;
xasprintf(&ret, "%s/%s/s.%s.%s.XXXXXXXXXX",
homedir, _PATH_SSH_AGENT_SOCKET_DIR, hostnamehash, tag);
return -1;
xasprintf(&path, "%s/s.%s.%s.%lld.XXXXXXXXXX",
dir, hostnamehash, tag, (long long)pid);
if (mkdir_path(dir, 0700) != 0) {
error_f("failed to create agent socket parent directory");
goto out;
}
if ((sock = unix_listener_tmp(path, SSH_LISTEN_BACKLOG)) == -1) {
/* error already logged */
goto out;
}
/* Success */
*pathp = path;
path = NULL; /* transferred */
*sockp = sock;
ret = 0;
out:
free(hostnamehash);
free(path);
return ret;
}
static char *
expand_pathspec(const char *path, const char *username,
uid_t uid, const char *homedir)
{
char *uidbuf = NULL, *dir = NULL, *tmp = NULL;
xasprintf(&uidbuf, "%lld", (long long)uid);
if ((tmp = percent_expand(path, "u", username, "U", uidbuf,
"h", homedir, NULL)) == NULL) {
error_f("failed to percent-expand agent socket directory");
goto out;
}
if (tilde_expand(tmp, uid, &dir) != 0) {
error_f("failed to user-expand agent socket directory");
goto out;
}
if (dir[0] != '/') {
/* Assume it's relative to the home directory */
free(tmp);
tmp = dir;
xasprintf(&dir, "%s/%s", homedir, tmp);
}
out:
free(uidbuf);
free(tmp);
return dir;
}
int
agent_listener(const char *pathspec, const char *username, uid_t uid,
const char *homedir, pid_t pid, const char *tag, int *sockp,
char **pathp, char **dirp)
{
int sock = -1, ret = -1;
char *path = NULL, *dir = NULL;
*sockp = -1;
*pathp = *dirp = NULL;
if (pathspec == NULL || pathspec[0] == '\0') {
error_f("no agent path specified");
return -1;
}
if (strncmp(pathspec, "shared:", 7) == 0) {
if (pathspec[7] != '/') {
error_f("shared agent socket paths must be absoute");
goto out;
}
if ((dir = expand_pathspec(pathspec + 7,
username, uid, homedir)) == NULL) {
/* Error already logged */
goto out;
}
if (agent_listener_shared(dir, pid, tag,
&sock, &path, dirp) != 0) {
/* Error already logged */
goto out;
}
} else if (strncmp(pathspec, "user:", 5) == 0) {
if ((dir = expand_pathspec(pathspec + 5,
username, uid, homedir)) == NULL) {
/* Error already logged */
goto out;
}
if (agent_listener_user(dir, pid, tag, &sock, &path) != 0) {
/* Error already logged */
goto out;
}
} else {
/* Shouldn't happen */
error_f("unsupported agent path specification %s", pathspec);
goto out;
}
/* success */
ret = 0;
*sockp = sock;
*pathp = path;
path = NULL; /* transferred */
out:
free(path);
free(dir);
return ret;
}
int
agent_listener(const char *homedir, const char *tag, int *sockp, char **pathp)
agent_listener_cleanup(const char *pathspec, const char *sockpath,
const char *sockdir)
{
int sock;
char *path;
*sockp = -1;
*pathp = NULL;
if (agent_prepare_sockdir(homedir) != 0)
return -1; /* error already logged */
if ((path = agent_socket_template(homedir, tag)) == NULL)
return -1; /* error already logged */
if ((sock = unix_listener_tmp(path, SSH_LISTEN_BACKLOG)) == -1) {
free(path);
return -1; /* error already logged */
if (sockpath == NULL || pathspec == NULL)
return 0;
if (unlink(sockpath) != 0) {
error_f("unlink \"%s\": %s", sockpath, strerror(errno));
return -1;
}
/* success */
*sockp = sock;
*pathp = path;
debug3_f("removed socket %s", sockpath);
if (strncmp(pathspec, "shared:", 7) == 0 && sockdir != NULL) {
if (rmdir(sockdir) != 0) {
error_f("rmdir \"%s\": %s", sockdir, strerror(errno));
return -1;
}
debug3_f("removed socket directory %s", sockdir);
}
return 0;
}
@@ -271,14 +389,25 @@ socket_is_stale(const char *path)
#endif
void
agent_cleanup_stale(const char *homedir, int ignore_hosthash)
agent_cleanup_stale(const char *pathspec, const char *username, uid_t uid,
const char *homedir, int ignore_hosthash)
{
DIR *d = NULL;
struct dirent *dp;
struct stat sb;
char *prefix = NULL, *dirpath = NULL, *path = NULL;
char *prefix = NULL, *dir = NULL, *path;
struct timespec now, sub, *mtimp = NULL;
/* Only clean up user socket directories */
if (pathspec == NULL || strncmp(pathspec, "user:", 5) != 0)
return;
if ((dir = expand_pathspec(pathspec + 5,
username, uid, homedir)) == NULL)
return; /* error already logged */
debug_f("cleanup %s", dir);
/* Only consider sockets last modified > 1 hour ago */
if (clock_gettime(CLOCK_REALTIME, &now) != 0) {
error_f("clock_gettime: %s", strerror(errno));
@@ -296,20 +425,14 @@ agent_cleanup_stale(const char *homedir, int ignore_hosthash)
}
xasprintf(&prefix, "s.%s.", path);
free(path);
path = NULL;
}
xasprintf(&dirpath, "%s/%s", homedir, _PATH_SSH_AGENT_SOCKET_DIR);
if ((d = opendir(dirpath)) == NULL) {
if ((d = opendir(dir)) == NULL) {
if (errno != ENOENT)
error_f("opendir \"%s\": %s", dirpath, strerror(errno));
error_f("opendir \"%s\": %s", dir, strerror(errno));
goto out;
}
path = NULL;
while ((dp = readdir(d)) != NULL) {
free(path);
xasprintf(&path, "%s/%s", dirpath, dp->d_name);
#ifdef HAVE_DIRENT_D_TYPE
if (dp->d_type != DT_SOCK && dp->d_type != DT_UNKNOWN)
continue;
@@ -317,7 +440,7 @@ agent_cleanup_stale(const char *homedir, int ignore_hosthash)
if (fstatat(dirfd(d), dp->d_name,
&sb, AT_SYMLINK_NOFOLLOW) != 0 && errno != ENOENT) {
error_f("stat \"%s/%s\": %s",
dirpath, dp->d_name, strerror(errno));
dir, dp->d_name, strerror(errno));
continue;
}
if (!S_ISSOCK(sb.st_mode))
@@ -331,25 +454,26 @@ agent_cleanup_stale(const char *homedir, int ignore_hosthash)
#endif
if (timespeccmp(mtimp, &now, >)) {
debug3_f("Ignoring recent socket \"%s/%s\"",
dirpath, dp->d_name);
dir, dp->d_name);
continue;
}
if (!ignore_hosthash &&
strncmp(dp->d_name, prefix, strlen(prefix)) != 0) {
debug3_f("Ignoring socket \"%s/%s\" "
"from different host", dirpath, dp->d_name);
"from different host", dir, dp->d_name);
continue;
}
xasprintf(&path, "%s/%s", dir, dp->d_name);
if (socket_is_stale(path)) {
debug_f("cleanup stale socket %s", path);
unlinkat(dirfd(d), dp->d_name, 0);
}
free(path);
}
out:
if (d != NULL)
closedir(d);
free(path);
free(dirpath);
free(dir);
free(prefix);
}
+6 -4
View File
@@ -1,4 +1,4 @@
/* $OpenBSD: misc.h,v 1.118 2026/09/16 00:13:58 djm Exp $ */
/* $OpenBSD: misc.h,v 1.119 2026/09/16 00:25:50 djm Exp $ */
/*
* Author: Tatu Ylonen <ylo@cs.hut.fi>
@@ -241,9 +241,11 @@ struct timespec *ptimeout_get_tsp(struct timespec *pt);
int ptimeout_isset(struct timespec *pt);
/* misc-agent.c */
char *agent_hostname_hash(void);
int agent_listener(const char *, const char *, int *, char **);
void agent_cleanup_stale(const char *, int);
int agent_listener(const char *, const char *, uid_t, const char *,
pid_t, const char *, int *, char **, char **);
void agent_cleanup_stale(const char *, const char *, uid_t,
const char *, int);
int agent_listener_cleanup(const char *, const char *, const char *);
/* readpass.c */
+6 -3
View File
@@ -1,4 +1,4 @@
/* $OpenBSD: pathnames.h,v 1.37 2026/06/14 03:59:34 djm Exp $ */
/* $OpenBSD: pathnames.h,v 1.38 2026/09/16 00:25:50 djm Exp $ */
/*
* Author: Tatu Ylonen <ylo@cs.hut.fi>
@@ -68,10 +68,13 @@
/*
* The directory in which ssh-agent sockets and agent sockets forwarded by
* Directory spec for ssh-agent sockets and agent sockets forwarded by
* sshd reside. This directory should not be world-readable.
*/
#define _PATH_SSH_AGENT_SOCKET_DIR _PATH_SSH_USER_DIR "/agent"
#define _PATH_SSH_AGENT_SOCKET_DIR "user:" _PATH_SSH_USER_DIR "/agent"
/* Directory spec for ssh-agent sockets in /tmp */
#define _PATH_SSH_AGENT_SOCKET_TMPDIR "shared:/tmp"
/*
* Per-user file containing host keys of known hosts. This file need not be
+28 -1
View File
@@ -1,4 +1,4 @@
/* $OpenBSD: servconf.c,v 1.454 2026/09/16 00:16:52 djm Exp $ */
/* $OpenBSD: servconf.c,v 1.455 2026/09/16 00:25:50 djm Exp $ */
/*
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
* All rights reserved
@@ -414,6 +414,8 @@ fill_default_server_options(ServerOptions *options)
options->pubkey_auth_options = 0;
options->max_pubkey_ok = DEFAULT_AUTH_FAIL_MAX;
}
if (options->agent_socket_path == NULL)
options->agent_socket_path = xstrdup(_PATH_SSH_AGENT_SOCKET_DIR);
assemble_algorithms(options);
@@ -445,6 +447,7 @@ fill_default_server_options(ServerOptions *options)
CLEAR_ON_NONE(options->routing_domain);
CLEAR_ON_NONE(options->host_key_agent);
CLEAR_ON_NONE(options->per_source_penalty_exempt);
CLEAR_ON_NONE(options->agent_socket_path);
for (i = 0; i < options->num_host_key_files; i++)
CLEAR_ON_NONE(options->host_key_files[i]);
@@ -1673,6 +1676,29 @@ process_server_config_line_depth(ServerOptions *options, char *line,
intptr = &options->allow_agent_forwarding;
goto parse_flag;
case sAgentSocketPath:
charptr = &options->agent_socket_path;
arg = argv_next(&ac, &av);
if (!arg || *arg == '\0')
fatal("%s line %d: missing path.", filename, linenum);
if (strncmp(arg, "shared:", 7) == 0) {
/* Shared paths must be absolute */
if (arg[7] != '/') {
fatal("%s line %d: invalid shared path.",
filename, linenum);
}
} else if (strncmp(arg, "user:", 5) == 0) {
/* User paths must not be empty */
if (arg[5] == '\0') {
fatal("%s line %d: invalid user path.",
filename, linenum);
}
} else if (strcmp(arg, "none") != 0)
fatal("%s line %d: invalid path.", filename, linenum);
if (*activep && *charptr == NULL)
*charptr = xstrdup(arg);
break;
case sDisableForwarding:
intptr = &options->disable_forwarding;
goto parse_flag;
@@ -4341,6 +4367,7 @@ dump_config(ServerOptions *o)
dump_cfg_string(sSshdSessionPath, o->sshd_session_path);
dump_cfg_string(sSshdAuthPath, o->sshd_auth_path);
dump_cfg_string(sPerSourcePenaltyExemptList, o->per_source_penalty_exempt);
dump_cfg_string(sAgentSocketPath, o->agent_socket_path);
/* string arguments requiring a lookup */
dump_cfg_string(sLogLevel, log_level_name(o->log_level));
+3 -3
View File
@@ -1,4 +1,4 @@
/* $OpenBSD: servconf.h,v 1.181 2026/09/16 00:16:52 djm Exp $ */
/* $OpenBSD: servconf.h,v 1.182 2026/09/16 00:25:50 djm Exp $ */
/*
* Author: Tatu Ylonen <ylo@cs.hut.fi>
@@ -240,7 +240,8 @@ SSHCONF_STRARRAY(channel_timeouts, num_channel_timeouts, ChannelTimeout, SSHCFG_
SSHCONF_INT(unused_connection_timeout, UnusedConnectionTimeout, SSHCFG_ALL, NULL, 0, SSHCFG_COPY_MATCH) \
SSHCONF_STRING(sshd_session_path, SshdSessionPath, SSHCFG_GLOBAL, SSHCFG_COPY_NONE) \
SSHCONF_STRING(sshd_auth_path, SshdAuthPath, SSHCFG_GLOBAL, SSHCFG_COPY_NONE) \
SSHCONF_INTFLAG(refuse_connection, RefuseConnection, SSHCFG_ALL, 0, SSHCFG_COPY_MATCH)
SSHCONF_INTFLAG(refuse_connection, RefuseConnection, SSHCFG_ALL, 0, SSHCFG_COPY_MATCH) \
SSHCONF_STRING(agent_socket_path, AgentSocketPath, SSHCFG_ALL, SSHCFG_COPY_MATCH)
#define SSHD_CONFIG_ENTRIES_LEGACY \
SSHCONF_DEPRECATE(ServerKeyBits, SSHCFG_GLOBAL, SSHCONF_DEPRECATED) \
@@ -433,7 +434,6 @@ struct include_item {
};
TAILQ_HEAD(include_list, include_item);
void initialize_server_options(ServerOptions *);
void fill_default_server_options(ServerOptions *);
int process_server_config_line(ServerOptions *, char *, const char *, int,
+8 -3
View File
@@ -1,4 +1,4 @@
/* $OpenBSD: session.c,v 1.350 2026/06/05 08:53:07 djm Exp $ */
/* $OpenBSD: session.c,v 1.351 2026/09/16 00:25:50 djm Exp $ */
/*
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
* All rights reserved
@@ -168,6 +168,7 @@ static char *auth_info_file = NULL;
/* Name and directory of socket for authentication agent forwarding. */
static char *auth_sock_name = NULL;
static char *auth_sock_dir = NULL; /* only set if directory needs cleanup */
/* removes the agent forwarding socket */
@@ -176,7 +177,9 @@ auth_sock_cleanup_proc(struct passwd *pw)
{
if (auth_sock_name != NULL) {
temporarily_use_uid(pw);
unlink(auth_sock_name);
agent_listener_cleanup(options.agent_socket_path,
auth_sock_name, auth_sock_dir);
free(auth_sock_name);
auth_sock_name = NULL;
restore_uid();
}
@@ -196,7 +199,9 @@ auth_input_request_forwarding(struct ssh *ssh, struct passwd *pw, int agent_new)
/* Temporarily drop privileged uid for mkdir/bind. */
temporarily_use_uid(pw);
if (agent_listener(pw->pw_dir, "sshd", &sock, &auth_sock_name) != 0) {
if (agent_listener(options.agent_socket_path, pw->pw_name, pw->pw_uid,
pw->pw_dir, getpid(), "sshd", &sock, &auth_sock_name,
&auth_sock_dir) != 0) {
/* a more detailed error is already logged */
ssh_packet_send_debug(ssh, "Agent forwarding disabled: "
"couldn't create listener socket");
+31 -8
View File
@@ -1,4 +1,4 @@
.\" $OpenBSD: ssh-agent.1,v 1.87 2026/05/27 03:04:30 djm Exp $
.\" $OpenBSD: ssh-agent.1,v 1.88 2026/09/16 00:25:50 djm Exp $
.\"
.\" Author: Tatu Ylonen <ylo@cs.hut.fi>
.\" Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@@ -34,7 +34,7 @@
.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
.\"
.Dd $Mdocdate: May 27 2026 $
.Dd $Mdocdate: September 16 2026 $
.Dt SSH-AGENT 1
.Os
.Sh NAME
@@ -43,15 +43,15 @@
.Sh SYNOPSIS
.Nm ssh-agent
.Op Fl c | s
.Op Fl \&DdTU
.Op Fl a Ar bind_address
.Op Fl \&DdU
.Op Fl T | A Ar directory | Fl a Ar bind_address
.Op Fl E Ar fingerprint_hash
.Op Fl O Ar option
.Op Fl P Ar allowed_providers
.Op Fl t Ar life
.Nm ssh-agent
.Op Fl TU
.Op Fl a Ar bind_address
.Op Fl U
.Op Fl T | A Ar directory | Fl a Ar bind_address
.Op Fl E Ar fingerprint_hash
.Op Fl O Ar option
.Op Fl P Ar allowed_providers
@@ -79,8 +79,31 @@ Bind the agent to the
.Ux Ns -domain
socket
.Ar bind_address .
The default is to create a socket at a random path matching
.Pa $HOME/.ssh/agent/s.* .
The default is to create a socket in the
.Pa $HOME/.ssh/agent
directory using a random path matching
.Pa s.* .
.It Fl A Ar socket_path
Specify a different directory path under which to create the socket.
Sockets may be created in either a shared location or a user-specific
directory.
User-specific directories are specified by prefixing the path name with
.Cm user: .
.Xr ssh-agent 1
will ensure the directory exists and create the listening socket
directly in it.
Relative user-specific directory paths will be created to the user's
.Ev $HOME .
.Pp
Shared directories may be specified by prefixing an absolute path name with
.Cm shared: .
In this case, a temporary subdirectory will be created under the specified
directory and the listening agent socket will be created in that.
.Pp
This option accepts the tokens described in the
.Xr sshd_config 5
.Sx TOKENS
section.
.It Fl c
Generate C-shell commands on standard output.
This is the default if
+43 -34
View File
@@ -1,4 +1,4 @@
/* $OpenBSD: ssh-agent.c,v 1.331 2026/08/07 05:18:05 djm Exp $ */
/* $OpenBSD: ssh-agent.c,v 1.332 2026/09/16 00:25:50 djm Exp $ */
/*
* Author: Tatu Ylonen <ylo@cs.hut.fi>
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@@ -63,6 +63,7 @@
#include <time.h>
#include <unistd.h>
#include <util.h>
#include <pwd.h>
#include "xmalloc.h"
#include "ssh.h"
@@ -166,7 +167,8 @@ pid_t cleanup_pid = 0;
/* pathname and directory for AUTH_SOCKET */
static char *socket_name;
static char socket_dir[PATH_MAX];
static char *socket_dir;
static char *socket_dirspec;
/* Pattern-list of allowed PKCS#11/Security key paths */
static char *allowed_providers;
@@ -2190,12 +2192,12 @@ cleanup_socket(void)
return;
debug_f("cleanup");
if (socket_name != NULL) {
unlink(socket_name);
agent_listener_cleanup(socket_dirspec, socket_name, socket_dir);
free(socket_name);
socket_name = NULL;
free(socket_dir);
socket_dir = NULL;
}
if (socket_dir[0])
rmdir(socket_dir);
}
void
@@ -2238,9 +2240,11 @@ static void
usage(void)
{
fprintf(stderr,
"usage: ssh-agent [-c | -s] [-DdTU] [-a bind_address] [-E fingerprint_hash]\n"
" [-O option] [-P allowed_providers] [-t life]\n"
" ssh-agent [-TU] [-a bind_address] [-E fingerprint_hash] [-O option]\n"
"usage: ssh-agent [-c | -s] [-DdU] [-T | -A directory | -a bind_address]\n"
" [-E fingerprint_hash] [-O option]\n"
" [-P allowed_providers] [-t life]\n"
" ssh-agent [-U] [-T | -A directory | -a bind_address]\n"
" [-E fingerprint_hash] [-O option]\n"
" [-P allowed_providers] [-t life] command [arg ...]\n"
" ssh-agent [-c | -s] -k\n"
" ssh-agent -u\n"
@@ -2273,6 +2277,7 @@ main(int ac, char **av)
u_int maxfds;
sigset_t nsigset, osigset;
int socket_activated = 0;
struct passwd *pw;
/* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */
sanitise_stdfd();
@@ -2283,6 +2288,10 @@ main(int ac, char **av)
platform_disable_tracing(0); /* strict=no */
if ((pw = getpwuid(getuid())) == NULL)
fatal("No user exists for uid %lu", (u_long)getuid());
pw = pwcopy(pw);
#ifdef RLIMIT_NOFILE
if (getrlimit(RLIMIT_NOFILE, &rlim) == -1)
fatal("%s: getrlimit: %s", __progname, strerror(errno));
@@ -2291,7 +2300,7 @@ main(int ac, char **av)
__progname = ssh_get_progname(av[0]);
seed_rng();
while ((ch = getopt(ac, av, "cDdksTuUVE:a:O:P:t:")) != -1) {
while ((ch = getopt(ac, av, "cDdksTuUVA:E:a:O:P:t:")) != -1) {
switch (ch) {
case 'E':
fingerprint_hash = ssh_digest_alg_by_name(optarg);
@@ -2342,6 +2351,9 @@ main(int ac, char **av)
case 'a':
agentsocket = optarg;
break;
case 'A':
socket_dirspec = xstrdup(optarg);
break;
case 't':
if ((lifetime = convtime(optarg)) == -1) {
fprintf(stderr, "Invalid lifetime\n");
@@ -2371,6 +2383,9 @@ main(int ac, char **av)
if (ac > 0 &&
(c_flag || k_flag || s_flag || d_flag || D_flag || u_flag))
usage();
/* only one of -a, -A and -T allowed */
if (((socket_dirspec != NULL) + (agentsocket != NULL) + T_flag) > 1)
usage();
log_init(__progname,
d_flag ? SYSLOG_LEVEL_DEBUG3 : SYSLOG_LEVEL_INFO,
@@ -2381,6 +2396,11 @@ main(int ac, char **av)
if (websafe_allowlist == NULL)
websafe_allowlist = xstrdup(DEFAULT_WEBSAFE_ALLOWLIST);
if (T_flag)
socket_dirspec = xstrdup(_PATH_SSH_AGENT_SOCKET_TMPDIR);
else if (socket_dirspec == NULL && agentsocket == NULL)
socket_dirspec = xstrdup(_PATH_SSH_AGENT_SOCKET_DIR);
if (ac == 0 && !c_flag && !s_flag) {
shell = getenv("SHELL");
if (shell != NULL && (len = strlen(shell)) > 2 &&
@@ -2414,7 +2434,8 @@ main(int ac, char **av)
if (u_flag) {
if ((homedir = get_homedir()) == NULL)
fatal("Couldn't determine home directory");
agent_cleanup_stale(homedir, u_flag > 1);
agent_cleanup_stale(socket_dirspec,
pw->pw_name, pw->pw_uid, homedir, u_flag > 1);
printf("Deleted stale agent sockets in ~/%s\n",
_PATH_SSH_AGENT_SOCKET_DIR);
exit(0);
@@ -2453,35 +2474,23 @@ main(int ac, char **av)
socket_activated = 1;
}
if (sock == -1 && agentsocket == NULL && !T_flag) {
/* Default case: ~/.ssh/agent/[socket] */
if (sock == -1 && agentsocket == NULL) {
/* Listen on a socket in/under a given directory */
if ((homedir = get_homedir()) == NULL)
fatal("Couldn't determine home directory");
if (!U_flag)
agent_cleanup_stale(homedir, 0);
if (agent_listener(homedir, "agent", &sock, &socket_name) != 0)
if (!U_flag) {
agent_cleanup_stale(socket_dirspec,
pw->pw_name, pw->pw_uid, homedir, 0);
}
if (agent_listener(socket_dirspec, pw->pw_name, pw->pw_uid,
homedir, getpid(), "local", &sock, &socket_name,
&socket_dir) != 0)
fatal_f("Couldn't prepare agent socket");
free(homedir);
} else if (sock == -1) {
if (T_flag) {
/*
* Create private directory for agent socket
* in $TMPDIR.
*/
mktemp_proto(socket_dir, sizeof(socket_dir));
if (mkdtemp(socket_dir) == NULL) {
perror("mkdtemp: private socket dir");
exit(1);
}
xasprintf(&socket_name, "%s/agent.%ld",
socket_dir, (long)parent_pid);
} else {
/* Try to use specified agent socket */
socket_dir[0] = '\0';
socket_name = xstrdup(agentsocket);
}
/* Listen on socket */
/* Listen on explicit socket path */
prev_mask = umask(0177);
socket_name = xstrdup(agentsocket);
if ((sock = unix_listener(socket_name,
SSH_LISTEN_BACKLOG, 0)) < 0) {
*socket_name = '\0'; /* Don't unlink existing file */
@@ -2603,7 +2612,7 @@ skip:
fatal("%s: unveil %s %s", __progname, socket_name,
strerror(errno));
}
if (*socket_dir != '\0' && unveil(socket_dir, "c") == -1) {
if (socket_dir != NULL && unveil(socket_dir, "c") == -1) {
fatal("%s: unveil %s %s", __progname, socket_dir,
strerror(errno));
}
+29 -1
View File
@@ -33,7 +33,7 @@
.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
.\"
.\" $OpenBSD: sshd_config.5,v 1.405 2026/09/16 00:16:52 djm Exp $
.\" $OpenBSD: sshd_config.5,v 1.406 2026/09/16 00:25:50 djm Exp $
.Dd $Mdocdate: September 16 2026 $
.Dt SSHD_CONFIG 5
.Os
@@ -97,6 +97,31 @@ Valid arguments are
(use IPv4 only), or
.Cm inet6
(use IPv6 only).
.It Cm AgentSocketPath
Specifies the filesystem path used for forwarded
.Xr ssh-agent 1
sockets.
Sockets may be created in either a shared location or a user-specific
directory.
User-specific directories are specified by prefixing the path name with
.Cm user: .
.Xr sshd 8
will ensure the directory exists and create the listening socket
directly in it.
Relative user-specific directory paths will be created to the user's
.Ev $HOME .
.Pp
Shared directories may be specified by prefixing an absolute path name with
.Cm shared: .
In this case, a temporary subdirectory will be created under the specified
directory and the listening agent socket will be created in that.
.Pp
.Cm AgentSocketPath
accepts the tokens described in the
.Sx TOKENS
section.
The default path speification is
.Pa user:.ssh/agent .
.It Cm AllowAgentForwarding
Specifies whether
.Xr ssh-agent 1
@@ -2279,6 +2304,9 @@ The numeric user ID of the target user.
The username.
.El
.Pp
.Cm AgentSocketPath
accepts the tokens %%, %h, %U, and %u.
.Pp
.Cm AuthorizedKeysCommand
accepts the tokens %%, %C, %D, %f, %h, %k, %t, %U, and %u.
.Pp