Fix: sessiond: instance uuid is not sufficiently unique
[lttng-tools.git] / src / common / random.c
1 /*
2 * Copyright (C) 2023 Jérémie Galarneau <jeremie.galarneau@efficios.com>
3 *
4 * SPDX-License-Identifier: LGPL-2.1-only
5 *
6 */
7
8 #include <lttng/constant.h>
9
10 #include <common/error.h>
11 #include <common/hashtable/utils.h>
12 #include <common/random.h>
13 #include <common/readwrite.h>
14 #include <common/time.h>
15
16 #include <errno.h>
17 #include <fcntl.h>
18 #include <unistd.h>
19
20 #ifdef HAVE_SYS_SYSCALL_H
21 #include <sys/syscall.h>
22 #endif
23
24 /* getrandom is available in Linux >= 3.17. */
25 #if defined(__linux__) && defined(SYS_getrandom) && defined(HAVE_SYS_RANDOM_H)
26
27 #include <sys/random.h>
28
29 /* A glibc wrapper is provided only for glibc >= 2.25. */
30 #if defined(HAVE_GETRANDOM)
31 /* Simply use the existing wrapper, passing the non-block flag. */
32 static ssize_t _call_getrandom_nonblock(char *out_data, size_t size)
33 {
34 return getrandom(out_data, size, GRND_NONBLOCK);
35 }
36 #else
37 static ssize_t _call_getrandom_nonblock(char *out_data, size_t size)
38 {
39 const int grnd_nonblock_flag = 0x1;
40 long ret = syscall(SYS_getrandom, out_data, size, grnd_nonblock_flag);
41
42 if (ret < 0) {
43 errno = -ret;
44 ret = -1;
45 }
46
47 return ret;
48 }
49 #endif /* defined(HAVE_GETRANDOM) */
50
51 /* Returns either with a full read or throws. */
52 static int getrandom_nonblock(char *out_data, size_t size)
53 {
54 /*
55 * Since GRND_RANDOM is _not_ used, a partial read can only be caused
56 * by a signal interruption. In this case, retry.
57 */
58 int ret = 0;
59 ssize_t random_ret;
60
61 do {
62 random_ret = _call_getrandom_nonblock(out_data, size);
63 } while ((random_ret > 0 && random_ret != size) || (random_ret == -1 && errno == EINTR));
64
65 if (random_ret < 0) {
66 PERROR("Failed to get true random data using getrandom(): size=%zu", size);
67 ret = -1;
68 }
69
70 return ret;
71 }
72 #else /* defined(__linux__) && defined(SYS_getrandom) && defined(HAVE_SYS_RANDOM_H) */
73 static int getrandom_nonblock(char *out_data, size_t size)
74 {
75 WARN("getrandom() is not supported by this platform");
76 return -1;
77 }
78 #endif /* defined(__linux__) && defined(SYS_getrandom) && defined(HAVE_SYS_RANDOM_H) */
79
80 static int produce_pseudo_random_seed(seed_t *out_seed)
81 {
82 int ret;
83 struct timespec real_time = {};
84 struct timespec monotonic_time = {};
85 unsigned long hash_seed;
86 char hostname[LTTNG_HOST_NAME_MAX] = {};
87 unsigned long seed;
88 unsigned long pid;
89
90 ret = clock_gettime(CLOCK_REALTIME, &real_time);
91 if (ret) {
92 PERROR("Failed to read real time while generating pseudo-random seed");
93 goto error;
94 }
95
96 ret = clock_gettime(CLOCK_MONOTONIC, &monotonic_time);
97 if (ret) {
98 PERROR("Failed to read monotonic time while generating pseudo-random seed");
99 goto error;
100 }
101
102 ret = gethostname(hostname, sizeof(hostname));
103 if (ret) {
104 PERROR("Failed to get host name while generating pseudo-random seed");
105 goto error;
106 }
107
108 hash_seed = (unsigned long) real_time.tv_nsec ^ (unsigned long) real_time.tv_sec ^
109 (unsigned long) monotonic_time.tv_nsec ^
110 (unsigned long) monotonic_time.tv_sec;
111 seed = hash_key_ulong((void *) real_time.tv_sec, hash_seed);
112 seed ^= hash_key_ulong((void *) real_time.tv_nsec, hash_seed);
113 seed ^= hash_key_ulong((void *) monotonic_time.tv_sec, hash_seed);
114 seed ^= hash_key_ulong((void *) monotonic_time.tv_nsec, hash_seed);
115
116 pid = getpid();
117 seed ^= hash_key_ulong((void *) pid, hash_seed);
118 seed ^= hash_key_str(hostname, hash_seed);
119 ret = 0;
120
121 *out_seed = (seed_t) seed;
122 error:
123 return ret;
124 }
125
126 static int produce_random_seed_from_urandom(seed_t *out_seed)
127 {
128 int ret = 0, read_ret;
129 const int urandom_raw_fd = open("/dev/urandom", O_RDONLY | O_CLOEXEC);
130
131 if (urandom_raw_fd < 0) {
132 PERROR("Failed to open `/dev/urandom`");
133 ret = -1;
134 goto end;
135 }
136
137 read_ret = lttng_read(urandom_raw_fd, out_seed, sizeof(*out_seed));
138 if (read_ret != sizeof(*out_seed)) {
139 PERROR("Failed to read from `/dev/urandom`: size=%zu",
140 sizeof(*out_seed));
141 ret = -1;
142 goto end;
143 }
144
145 end:
146 if (urandom_raw_fd >= 0) {
147 if (close(urandom_raw_fd)) {
148 PERROR("Failed to close `/dev/urandom` file descriptor");
149 }
150 }
151 return ret;
152 }
153
154 int lttng_produce_true_random_seed(seed_t *out_seed)
155 {
156 return getrandom_nonblock((char *) out_seed, sizeof(*out_seed));
157 }
158
159 int lttng_produce_best_effort_random_seed(seed_t *out_seed)
160 {
161 int ret;
162
163 ret = lttng_produce_true_random_seed(out_seed);
164 if (!ret) {
165 goto end;
166 } else {
167 WARN("Failed to produce a random seed using getrandom(), falling back to pseudo-random device seed generation which will block until its pool is initialized");
168 }
169
170 ret = produce_random_seed_from_urandom(out_seed);
171 if (!ret) {
172 goto end;
173 } else {
174 WARN("Failed to produce a random seed from the urandom device");
175 }
176
177 /* Fallback to seed generation based on time and system configuration. */
178 ret = produce_pseudo_random_seed(out_seed);
179 end:
180 return ret;
181 }
This page took 0.0328 seconds and 4 git commands to generate.