mirror of
https://github.com/openssh/openssh-portable.git
synced 2026-11-06 05:16:43 +00:00
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:
1 parent
14843e73b9
commit
cee6aedb1a
9 files changed
+341
-120
No files matched your search
+187
-63
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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,
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
|
||||
|
||||
Reference in new issue
Block a user