Skip to content

Commit d073bdc

Browse files
committed
Merge branch 'bc/csprng-mktemps'
Pick a better random number generator and use it when we prepare temporary filenames. * bc/csprng-mktemps: wrapper: use a CSPRNG to generate random file names wrapper: add a helper to generate numbers from a CSPRNG
2 parents 8db2f66 + 47efda9 commit d073bdc

File tree

9 files changed

+169
-12
lines changed

9 files changed

+169
-12
lines changed

Makefile

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -234,6 +234,14 @@ all::
234234
# Define NO_TRUSTABLE_FILEMODE if your filesystem may claim to support
235235
# the executable mode bit, but doesn't really do so.
236236
#
237+
# Define CSPRNG_METHOD to "arc4random" if your system has arc4random and
238+
# arc4random_buf, "libbsd" if your system has those functions from libbsd,
239+
# "getrandom" if your system has getrandom, "getentropy" if your system has
240+
# getentropy, "rtlgenrandom" for RtlGenRandom (Windows only), or "openssl" if
241+
# you'd want to use the OpenSSL CSPRNG. You may set multiple options with
242+
# spaces, in which case a suitable option will be chosen. If unset or set to
243+
# anything else, defaults to using "/dev/urandom".
244+
#
237245
# Define NEEDS_MODE_TRANSLATION if your OS strays from the typical file type
238246
# bits in mode values (e.g. z/OS defines I_SFMT to 0xFF000000 as opposed to the
239247
# usual 0xF000).
@@ -693,6 +701,7 @@ TEST_BUILTINS_OBJS += test-bloom.o
693701
TEST_BUILTINS_OBJS += test-chmtime.o
694702
TEST_BUILTINS_OBJS += test-config.o
695703
TEST_BUILTINS_OBJS += test-crontab.o
704+
TEST_BUILTINS_OBJS += test-csprng.o
696705
TEST_BUILTINS_OBJS += test-ctype.o
697706
TEST_BUILTINS_OBJS += test-date.o
698707
TEST_BUILTINS_OBJS += test-delta.o
@@ -1909,6 +1918,31 @@ ifdef HAVE_GETDELIM
19091918
BASIC_CFLAGS += -DHAVE_GETDELIM
19101919
endif
19111920

1921+
ifneq ($(findstring arc4random,$(CSPRNG_METHOD)),)
1922+
BASIC_CFLAGS += -DHAVE_ARC4RANDOM
1923+
endif
1924+
1925+
ifneq ($(findstring libbsd,$(CSPRNG_METHOD)),)
1926+
BASIC_CFLAGS += -DHAVE_ARC4RANDOM_LIBBSD
1927+
EXTLIBS += -lbsd
1928+
endif
1929+
1930+
ifneq ($(findstring getrandom,$(CSPRNG_METHOD)),)
1931+
BASIC_CFLAGS += -DHAVE_GETRANDOM
1932+
endif
1933+
1934+
ifneq ($(findstring getentropy,$(CSPRNG_METHOD)),)
1935+
BASIC_CFLAGS += -DHAVE_GETENTROPY
1936+
endif
1937+
1938+
ifneq ($(findstring rtlgenrandom,$(CSPRNG_METHOD)),)
1939+
BASIC_CFLAGS += -DHAVE_RTLGENRANDOM
1940+
endif
1941+
1942+
ifneq ($(findstring openssl,$(CSPRNG_METHOD)),)
1943+
BASIC_CFLAGS += -DHAVE_OPENSSL_CSPRNG
1944+
endif
1945+
19121946
ifneq ($(PROCFS_EXECUTABLE_PATH),)
19131947
procfs_executable_path_SQ = $(subst ','\'',$(PROCFS_EXECUTABLE_PATH))
19141948
BASIC_CFLAGS += '-DPROCFS_EXECUTABLE_PATH="$(procfs_executable_path_SQ)"'

compat/winansi.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,12 @@
33
*/
44

55
#undef NOGDI
6+
7+
/*
8+
* Including the appropriate header file for RtlGenRandom causes MSVC to see a
9+
* redefinition of types in an incompatible way when including headers below.
10+
*/
11+
#undef HAVE_RTLGENRANDOM
612
#include "../git-compat-util.h"
713
#include <wingdi.h>
814
#include <winreg.h>

config.mak.uname

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,7 @@ ifeq ($(uname_S),Darwin)
146146
HAVE_BSD_SYSCTL = YesPlease
147147
FREAD_READS_DIRECTORIES = UnfortunatelyYes
148148
HAVE_NS_GET_EXECUTABLE_PATH = YesPlease
149+
CSPRNG_METHOD = arc4random
149150

150151
# Workaround for `gettext` being keg-only and not even being linked via
151152
# `brew link --force gettext`, should be obsolete as of
@@ -261,6 +262,7 @@ ifeq ($(uname_S),FreeBSD)
261262
HAVE_PATHS_H = YesPlease
262263
HAVE_BSD_SYSCTL = YesPlease
263264
HAVE_BSD_KERN_PROC_SYSCTL = YesPlease
265+
CSPRNG_METHOD = arc4random
264266
PAGER_ENV = LESS=FRX LV=-c MORE=FRX
265267
FREAD_READS_DIRECTORIES = UnfortunatelyYes
266268
FILENO_IS_A_MACRO = UnfortunatelyYes
@@ -279,6 +281,7 @@ ifeq ($(uname_S),OpenBSD)
279281
HAVE_PATHS_H = YesPlease
280282
HAVE_BSD_SYSCTL = YesPlease
281283
HAVE_BSD_KERN_PROC_SYSCTL = YesPlease
284+
CSPRNG_METHOD = arc4random
282285
PROCFS_EXECUTABLE_PATH = /proc/curproc/file
283286
FREAD_READS_DIRECTORIES = UnfortunatelyYes
284287
FILENO_IS_A_MACRO = UnfortunatelyYes
@@ -290,6 +293,7 @@ ifeq ($(uname_S),MirBSD)
290293
NEEDS_LIBICONV = YesPlease
291294
HAVE_PATHS_H = YesPlease
292295
HAVE_BSD_SYSCTL = YesPlease
296+
CSPRNG_METHOD = arc4random
293297
endif
294298
ifeq ($(uname_S),NetBSD)
295299
ifeq ($(shell expr "$(uname_R)" : '[01]\.'),2)
@@ -301,6 +305,7 @@ ifeq ($(uname_S),NetBSD)
301305
HAVE_PATHS_H = YesPlease
302306
HAVE_BSD_SYSCTL = YesPlease
303307
HAVE_BSD_KERN_PROC_SYSCTL = YesPlease
308+
CSPRNG_METHOD = arc4random
304309
PROCFS_EXECUTABLE_PATH = /proc/curproc/exe
305310
endif
306311
ifeq ($(uname_S),AIX)
@@ -430,6 +435,7 @@ ifeq ($(uname_S),Windows)
430435
NO_STRTOUMAX = YesPlease
431436
NO_MKDTEMP = YesPlease
432437
NO_INTTYPES_H = YesPlease
438+
CSPRNG_METHOD = rtlgenrandom
433439
# VS2015 with UCRT claims that snprintf and friends are C99 compliant,
434440
# so we don't need this:
435441
#
@@ -599,6 +605,7 @@ ifeq ($(uname_S),NONSTOP_KERNEL)
599605
NO_MMAP = YesPlease
600606
NO_POLL = YesPlease
601607
NO_INTPTR_T = UnfortunatelyYes
608+
CSPRNG_METHOD = openssl
602609
SANE_TOOL_PATH = /usr/coreutils/bin:/usr/local/bin
603610
SHELL_PATH = /usr/coreutils/bin/bash
604611
endif
@@ -634,6 +641,7 @@ ifeq ($(uname_S),MINGW)
634641
NO_POSIX_GOODIES = UnfortunatelyYes
635642
DEFAULT_HELP_FORMAT = html
636643
HAVE_PLATFORM_PROCINFO = YesPlease
644+
CSPRNG_METHOD = rtlgenrandom
637645
BASIC_LDFLAGS += -municode
638646
COMPAT_CFLAGS += -DNOGDI -Icompat -Icompat/win32
639647
COMPAT_CFLAGS += -DSTRIP_EXTENSION=\".exe\"

contrib/buildsystems/CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -260,7 +260,7 @@ if(CMAKE_SYSTEM_NAME STREQUAL "Windows")
260260
_CONSOLE DETECT_MSYS_TTY STRIP_EXTENSION=".exe" NO_SYMLINK_HEAD UNRELIABLE_FSTAT
261261
NOGDI OBJECT_CREATION_MODE=1 __USE_MINGW_ANSI_STDIO=0
262262
USE_NED_ALLOCATOR OVERRIDE_STRDUP MMAP_PREVENTS_DELETE USE_WIN32_MMAP
263-
UNICODE _UNICODE HAVE_WPGMPTR ENSURE_MSYSTEM_IS_SET)
263+
UNICODE _UNICODE HAVE_WPGMPTR ENSURE_MSYSTEM_IS_SET HAVE_RTLGENRANDOM)
264264
list(APPEND compat_SOURCES compat/mingw.c compat/winansi.c compat/win32/path-utils.c
265265
compat/win32/pthread.c compat/win32mmap.c compat/win32/syslog.c
266266
compat/win32/trace2_win32_process_info.c compat/win32/dirent.c

git-compat-util.h

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,12 @@
197197
#endif
198198
#include <windows.h>
199199
#define GIT_WINDOWS_NATIVE
200+
#ifdef HAVE_RTLGENRANDOM
201+
/* This is required to get access to RtlGenRandom. */
202+
#define SystemFunction036 NTAPI SystemFunction036
203+
#include <NTSecAPI.h>
204+
#undef SystemFunction036
205+
#endif
200206
#endif
201207

202208
#include <unistd.h>
@@ -267,6 +273,12 @@
267273
#else
268274
#include <stdint.h>
269275
#endif
276+
#ifdef HAVE_ARC4RANDOM_LIBBSD
277+
#include <bsd/stdlib.h>
278+
#endif
279+
#ifdef HAVE_GETRANDOM
280+
#include <sys/random.h>
281+
#endif
270282
#ifdef NO_INTPTR_T
271283
/*
272284
* On I16LP32, ILP32 and LP64 "long" is the safe bet, however
@@ -1432,4 +1444,11 @@ static inline void *container_of_or_null_offset(void *ptr, size_t offset)
14321444

14331445
void sleep_millisec(int millisec);
14341446

1447+
/*
1448+
* Generate len bytes from the system cryptographically secure PRNG.
1449+
* Returns 0 on success and -1 on error, setting errno. The inability to
1450+
* satisfy the full request is an error.
1451+
*/
1452+
int csprng_bytes(void *buf, size_t len);
1453+
14351454
#endif

t/helper/test-csprng.c

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
#include "test-tool.h"
2+
#include "git-compat-util.h"
3+
4+
5+
int cmd__csprng(int argc, const char **argv)
6+
{
7+
unsigned long count;
8+
unsigned char buf[1024];
9+
10+
if (argc > 2) {
11+
fprintf(stderr, "usage: %s [<size>]\n", argv[0]);
12+
return 2;
13+
}
14+
15+
count = (argc == 2) ? strtoul(argv[1], NULL, 0) : -1L;
16+
17+
while (count) {
18+
unsigned long chunk = count < sizeof(buf) ? count : sizeof(buf);
19+
if (csprng_bytes(buf, chunk) < 0) {
20+
perror("failed to read");
21+
return 5;
22+
}
23+
if (fwrite(buf, chunk, 1, stdout) != chunk)
24+
return 1;
25+
count -= chunk;
26+
}
27+
28+
return 0;
29+
}

t/helper/test-tool.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ static struct test_cmd cmds[] = {
2020
{ "chmtime", cmd__chmtime },
2121
{ "config", cmd__config },
2222
{ "crontab", cmd__crontab },
23+
{ "csprng", cmd__csprng },
2324
{ "ctype", cmd__ctype },
2425
{ "date", cmd__date },
2526
{ "delta", cmd__delta },

t/helper/test-tool.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ int cmd__bloom(int argc, const char **argv);
1010
int cmd__chmtime(int argc, const char **argv);
1111
int cmd__config(int argc, const char **argv);
1212
int cmd__crontab(int argc, const char **argv);
13+
int cmd__csprng(int argc, const char **argv);
1314
int cmd__ctype(int argc, const char **argv);
1415
int cmd__date(int argc, const char **argv);
1516
int cmd__delta(int argc, const char **argv);

wrapper.c

Lines changed: 70 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -463,8 +463,6 @@ int git_mkstemps_mode(char *pattern, int suffix_len, int mode)
463463
static const int num_letters = ARRAY_SIZE(letters) - 1;
464464
static const char x_pattern[] = "XXXXXX";
465465
static const int num_x = ARRAY_SIZE(x_pattern) - 1;
466-
uint64_t value;
467-
struct timeval tv;
468466
char *filename_template;
469467
size_t len;
470468
int fd, count;
@@ -485,12 +483,13 @@ int git_mkstemps_mode(char *pattern, int suffix_len, int mode)
485483
* Replace pattern's XXXXXX characters with randomness.
486484
* Try TMP_MAX different filenames.
487485
*/
488-
gettimeofday(&tv, NULL);
489-
value = ((uint64_t)tv.tv_usec << 16) ^ tv.tv_sec ^ getpid();
490486
filename_template = &pattern[len - num_x - suffix_len];
491487
for (count = 0; count < TMP_MAX; ++count) {
492-
uint64_t v = value;
493488
int i;
489+
uint64_t v;
490+
if (csprng_bytes(&v, sizeof(v)) < 0)
491+
return error_errno("unable to get random bytes for temporary file");
492+
494493
/* Fill in the random bits. */
495494
for (i = 0; i < num_x; i++) {
496495
filename_template[i] = letters[v % num_letters];
@@ -506,12 +505,6 @@ int git_mkstemps_mode(char *pattern, int suffix_len, int mode)
506505
*/
507506
if (errno != EEXIST)
508507
break;
509-
/*
510-
* This is a random value. It is only necessary that
511-
* the next TMP_MAX values generated by adding 7777 to
512-
* VALUE are different with (module 2^32).
513-
*/
514-
value += 7777;
515508
}
516509
/* We return the null string if we can't find a unique file name. */
517510
pattern[0] = '\0';
@@ -702,3 +695,69 @@ int open_nofollow(const char *path, int flags)
702695
return open(path, flags);
703696
#endif
704697
}
698+
699+
int csprng_bytes(void *buf, size_t len)
700+
{
701+
#if defined(HAVE_ARC4RANDOM) || defined(HAVE_ARC4RANDOM_LIBBSD)
702+
/* This function never returns an error. */
703+
arc4random_buf(buf, len);
704+
return 0;
705+
#elif defined(HAVE_GETRANDOM)
706+
ssize_t res;
707+
char *p = buf;
708+
while (len) {
709+
res = getrandom(p, len, 0);
710+
if (res < 0)
711+
return -1;
712+
len -= res;
713+
p += res;
714+
}
715+
return 0;
716+
#elif defined(HAVE_GETENTROPY)
717+
int res;
718+
char *p = buf;
719+
while (len) {
720+
/* getentropy has a maximum size of 256 bytes. */
721+
size_t chunk = len < 256 ? len : 256;
722+
res = getentropy(p, chunk);
723+
if (res < 0)
724+
return -1;
725+
len -= chunk;
726+
p += chunk;
727+
}
728+
return 0;
729+
#elif defined(HAVE_RTLGENRANDOM)
730+
if (!RtlGenRandom(buf, len))
731+
return -1;
732+
return 0;
733+
#elif defined(HAVE_OPENSSL_CSPRNG)
734+
int res = RAND_bytes(buf, len);
735+
if (res == 1)
736+
return 0;
737+
if (res == -1)
738+
errno = ENOTSUP;
739+
else
740+
errno = EIO;
741+
return -1;
742+
#else
743+
ssize_t res;
744+
char *p = buf;
745+
int fd, err;
746+
fd = open("/dev/urandom", O_RDONLY);
747+
if (fd < 0)
748+
return -1;
749+
while (len) {
750+
res = xread(fd, p, len);
751+
if (res < 0) {
752+
err = errno;
753+
close(fd);
754+
errno = err;
755+
return -1;
756+
}
757+
len -= res;
758+
p += res;
759+
}
760+
close(fd);
761+
return 0;
762+
#endif
763+
}

0 commit comments

Comments
 (0)