[Dnsmasq-discuss] [PATCH 4/6] Rekey PRNG on fork() to avoid sharing PRNG state and/or leaking it
Leonid Evdokimov
leon at darkk.net.ru
Fri Oct 4 21:14:51 UTC 2024
---
src/dnsmasq.c | 6 +++---
src/dnsmasq.h | 1 +
src/helper.c | 4 ++--
src/util.c | 21 +++++++++++++++++++++
4 files changed, 27 insertions(+), 5 deletions(-)
diff --git src/dnsmasq.c src/dnsmasq.c
index a9f26ae3..d15177f8 100644
--- src/dnsmasq.c
+++ src/dnsmasq.c
@@ -607,7 +607,7 @@ int main (int argc, char **argv)
When startup is complete we close this and the process terminates. */
safe_pipe(err_pipe, 0);
- if ((pid = fork()) == -1)
+ if ((pid = my_fork()) == -1)
/* fd == -1 since we've not forked, never returns. */
send_event(-1, EVENT_FORK_ERR, errno, NULL);
@@ -632,7 +632,7 @@ int main (int argc, char **argv)
setsid();
- if ((pid = fork()) == -1)
+ if ((pid = my_fork()) == -1)
send_event(err_pipe[1], EVENT_FORK_ERR, errno, NULL);
if (pid != 0)
@@ -1955,7 +1955,7 @@ static void check_dns_listeners(time_t now)
shutdown(confd, SHUT_RDWR);
close(confd);
}
- else if (!option_bool(OPT_DEBUG) && pipe(pipefd) == 0 && (p = fork()) != 0)
+ else if (!option_bool(OPT_DEBUG) && pipe(pipefd) == 0 && (p = my_fork()) != 0)
{
close(pipefd[1]); /* parent needs read pipe end. */
if (p == -1)
diff --git src/dnsmasq.h src/dnsmasq.h
index e455c3f7..ff926bf8 100644
--- src/dnsmasq.h
+++ src/dnsmasq.h
@@ -1462,6 +1462,7 @@ unsigned char *do_rfc1035_name(unsigned char *p, char *sval, char *limit);
void *safe_malloc(size_t size);
void safe_strncpy(char *dest, const char *src, size_t size);
void safe_pipe(int *fd, int read_noblock);
+pid_t my_fork(void);
void *whine_malloc(size_t size);
void *whine_realloc(void *ptr, size_t size);
int sa_len(union mysockaddr *addr);
diff --git src/helper.c src/helper.c
index b9da2259..a5714303 100644
--- src/helper.c
+++ src/helper.c
@@ -85,7 +85,7 @@ int create_helper(int event_fd, int err_fd, uid_t uid, gid_t gid, long max_fd)
/* create the pipe through which the main program sends us commands,
then fork our process. */
- if (pipe(pipefd) == -1 || !fix_fd(pipefd[1]) || (pid = fork()) == -1)
+ if (pipe(pipefd) == -1 || !fix_fd(pipefd[1]) || (pid = my_fork()) == -1)
{
send_event(err_fd, EVENT_PIPE_ERR, errno, NULL);
_exit(0);
@@ -506,7 +506,7 @@ int create_helper(int event_fd, int err_fd, uid_t uid, gid_t gid, long max_fd)
continue;
/* possible fork errors are all temporary resource problems */
- while ((pid = fork()) == -1 && (errno == EAGAIN || errno == ENOMEM))
+ while ((pid = my_fork()) == -1 && (errno == EAGAIN || errno == ENOMEM))
sleep(2);
if (pid == -1)
diff --git src/util.c src/util.c
index 71b1dd57..28b9c6d2 100644
--- src/util.c
+++ src/util.c
@@ -64,6 +64,18 @@ void rand_init()
die(_("failed to seed the random number generator: %s"), NULL, EC_MISC);
}
+static void rand_atfork(pid_t fpid)
+{
+ u32 next[countof(seed)];
+ for (unsigned int i = 0; i < countof(seed); i++)
+ next[i] = rand32();
+ // Child reseeds the state with generated values. Parent skips those values explicitly as they
+ // might be exposed to an external observer and used to guess something about PRNG of the child.
+ if (fpid == 0)
+ for (unsigned int i = 0; i < countof(seed); i++)
+ seed[i] ^= next[i];
+}
+
#define ROTATE(x,b) (((x) << (b)) | ((x) >> (32 - (b))))
#define MUSH(i,b) x = t[i] += (((x ^ seed[i]) + sum) ^ ROTATE(x,b));
@@ -334,6 +346,15 @@ void safe_pipe(int *fd, int read_noblock)
die(_("cannot create pipe: %s"), NULL, EC_MISC);
}
+// pthread_atfork() might be a better way to wrap fork(), but it requires -pthread besides libc.
+pid_t my_fork()
+{
+ const pid_t fpid = fork();
+ if (fpid != -1)
+ rand_atfork(fpid);
+ return fpid;
+}
+
void *whine_malloc(size_t size)
{
void *ret = calloc(1, size);
--
2.34.1
More information about the Dnsmasq-discuss
mailing list