agent: Have a thread monitoring parent PID and homedir.

* agent/gpg-agent.c (CHECK_PROBLEMS_INTERVAL): New.
(socket_takeover_detected): Remove.
(problem_detected): New.
(handle_tick): Don't check parent PID and homedir in this function.
(handle_connections): Spawn check_others_thread when needed.  Handle
AGENT_PROBLEM_PARENT_HAS_GONE and AGENT_PROBLEM_HOMEDIR_REMOVED.
(check_own_socket_thread): Check SHUTDOWN_PENDING variable in the
loop.  Use PROBLEM_DETECTED variable.
(check_others_thread): New.

--

GnuPG-bug-id: 6693
Signed-off-by: NIIBE Yutaka <gniibe@fsij.org>
This commit is contained in:
NIIBE Yutaka 2023-08-30 14:08:33 +09:00
parent 76896e2339
commit 7025375e8b
No known key found for this signature in database
GPG Key ID: 640114AF89DE6054
1 changed files with 87 additions and 39 deletions

View File

@ -349,7 +349,7 @@ static struct debug_flags_s debug_flags [] =
* don't check at all. All values are in seconds. */
#define TIMERTICK_INTERVAL (4)
#define CHECK_OWN_SOCKET_INTERVAL (60)
#define CHECK_PROBLEMS_INTERVAL (4)
/* Flag indicating that the ssh-agent subsystem has been enabled. */
static int ssh_support;
@ -393,8 +393,11 @@ static int is_supervised;
/* Flag indicating to start the daemon even if one already runs. */
static int steal_socket;
/* Flag to monitor socket takeover. */
static int socket_takeover_detected;
/* Flag to monitor problems. */
static int problem_detected;
#define AGENT_PROBLEM_SOCKET_TAKEOVER (1 << 0)
#define AGENT_PROBLEM_PARENT_HAS_GONE (1 << 1)
#define AGENT_PROBLEM_HOMEDIR_REMOVED (1 << 2)
/* Flag to inhibit socket removal in cleanup. */
static int inhibit_socket_removal;
@ -463,9 +466,14 @@ static const char *debug_level;
the log file after a SIGHUP if it didn't changed. Malloced. */
static char *current_logfile;
/* The handle_tick() function may test whether a parent is still
* running. We record the PID of the parent here or -1 if it should
* be watched. */
#ifdef HAVE_W32_SYSTEM
#define HAVE_PARENT_PID_SUPPORT 0
#else
#define HAVE_PARENT_PID_SUPPORT 1
#endif
/* The check_others_thread() function may test whether a parent is
* still running. We record the PID of the parent here or -1 if it
* should be watched. */
static pid_t parent_pid = (pid_t)(-1);
/* This flag is true if the inotify mechanism for detecting the
@ -532,6 +540,7 @@ static int check_for_running_agent (int silent);
#if CHECK_OWN_SOCKET_INTERVAL > 0
static void *check_own_socket_thread (void *arg);
#endif
static void *check_others_thread (void *arg);
/*
Functions.
@ -2443,35 +2452,8 @@ create_directories (void)
static void
handle_tick (void)
{
struct stat statbuf;
/* If we are running as a child of another process, check whether
the parent is still alive and shutdown if not. */
#ifndef HAVE_W32_SYSTEM
if (parent_pid != (pid_t)(-1))
{
if (kill (parent_pid, 0))
{
shutdown_pending = 2;
log_info ("parent process died - shutting down\n");
log_info ("%s %s stopped\n", gpgrt_strusage(11), gpgrt_strusage(13));
cleanup ();
agent_exit (0);
}
}
#endif /*HAVE_W32_SYSTEM*/
/* Need to check for expired cache entries. */
agent_cache_housekeeping ();
/* Check whether the homedir is still available. */
if (!shutdown_pending
&& (!have_homedir_inotify || !reliable_homedir_inotify)
&& gnupg_stat (gnupg_homedir (), &statbuf) && errno == ENOENT)
{
shutdown_pending = 1;
log_info ("homedir has been removed - shutting down\n");
}
}
@ -3098,6 +3080,16 @@ handle_connections (gnupg_fd_t listen_fd,
}
#endif
if ((HAVE_PARENT_PID_SUPPORT && parent_pid != (pid_t)(-1))
|| (!have_homedir_inotify || !reliable_homedir_inotify))
{
npth_t thread;
err = npth_create (&thread, &tattr, check_others_thread, NULL);
if (err)
log_error ("error spawning check_others_thread: %s\n", strerror (err));
}
/* On Windows we need to fire up a separate thread to listen for
requests from Putty (an SSH client), so we can replace Putty's
Pageant (its ssh-agent implementation). */
@ -3242,7 +3234,18 @@ handle_connections (gnupg_fd_t listen_fd,
continue;
}
if (socket_takeover_detected)
#ifndef HAVE_W32_SYSTEM
if ((problem_detected & AGENT_PROBLEM_PARENT_HAS_GONE))
{
shutdown_pending = 2;
log_info ("parent process died - shutting down\n");
log_info ("%s %s stopped\n", gpgrt_strusage(11), gpgrt_strusage(13));
cleanup ();
agent_exit (0);
}
#endif
if ((problem_detected & AGENT_PROBLEM_SOCKET_TAKEOVER))
{
/* We may not remove the socket as it is now in use by another
server. */
@ -3251,6 +3254,12 @@ handle_connections (gnupg_fd_t listen_fd,
log_info ("this process is useless - shutting down\n");
}
if ((problem_detected & AGENT_PROBLEM_HOMEDIR_REMOVED))
{
shutdown_pending = 1;
log_info ("homedir has been removed - shutting down\n");
}
if (ret <= 0)
/* Interrupt or timeout. Will be handled when calculating the
next timeout. */
@ -3434,22 +3443,61 @@ check_own_socket_thread (void *arg)
if (!sockname)
return NULL; /* Out of memory. */
while (1)
while (!problem_detected)
{
if (do_check_own_socket (sockname))
break;
if (shutdown_pending)
goto leave;
gnupg_sleep (CHECK_OWN_SOCKET_INTERVAL);
if (do_check_own_socket (sockname))
problem_detected |= AGENT_PROBLEM_SOCKET_TAKEOVER;
}
xfree (sockname);
socket_takeover_detected = 1;
agent_kick_the_loop ();
leave:
xfree (sockname);
return NULL;
}
#endif
/* The thread running other checks. */
static void *
check_others_thread (void *arg)
{
const char *homedir = gnupg_homedir ();
(void)arg;
while (!problem_detected)
{
struct stat statbuf;
if (shutdown_pending)
goto leave;
gnupg_sleep (CHECK_PROBLEMS_INTERVAL);
/* If we are running as a child of another process, check whether
the parent is still alive and shutdown if not. */
#ifndef HAVE_W32_SYSTEM
if (parent_pid != (pid_t)(-1) && kill (parent_pid, 0))
problem_detected |= AGENT_PROBLEM_PARENT_HAS_GONE;
#endif /*HAVE_W32_SYSTEM*/
/* Check whether the homedir is still available. */
if ((!have_homedir_inotify || !reliable_homedir_inotify)
&& gnupg_stat (homedir, &statbuf) && errno == ENOENT)
problem_detected |= AGENT_PROBLEM_HOMEDIR_REMOVED;
}
agent_kick_the_loop ();
leave:
return NULL;
}
/* Figure out whether an agent is available and running. Prints an
error if not. If SILENT is true, no messages are printed.
Returns 0 if the agent is running. */