/src/cryptsetup/lib/utils.c
Line | Count | Source (jump to first uncovered line) |
1 | | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | | /* |
3 | | * utils - miscellaneous device utilities for cryptsetup |
4 | | * |
5 | | * Copyright (C) 2004 Jana Saout <jana@saout.de> |
6 | | * Copyright (C) 2004-2007 Clemens Fruhwirth <clemens@endorphin.org> |
7 | | * Copyright (C) 2009-2025 Red Hat, Inc. All rights reserved. |
8 | | * Copyright (C) 2009-2025 Milan Broz |
9 | | */ |
10 | | |
11 | | #include <stdio.h> |
12 | | #include <errno.h> |
13 | | #include <sys/mman.h> |
14 | | #include <sys/resource.h> |
15 | | #include <sys/stat.h> |
16 | | #include <sys/utsname.h> |
17 | | |
18 | | #include "internal.h" |
19 | | |
20 | | size_t crypt_getpagesize(void) |
21 | 0 | { |
22 | 0 | long r = sysconf(_SC_PAGESIZE); |
23 | 0 | return r <= 0 ? DEFAULT_MEM_ALIGNMENT : (size_t)r; |
24 | 0 | } |
25 | | |
26 | | unsigned crypt_cpusonline(void) |
27 | 0 | { |
28 | 0 | long r = sysconf(_SC_NPROCESSORS_ONLN); |
29 | 0 | return r < 0 ? 1 : r; |
30 | 0 | } |
31 | | |
32 | | uint64_t crypt_getphysmemory_kb(void) |
33 | 0 | { |
34 | 0 | long pagesize, phys_pages; |
35 | 0 | uint64_t phys_memory_kb, page_size_kb; |
36 | |
|
37 | 0 | pagesize = sysconf(_SC_PAGESIZE); |
38 | 0 | phys_pages = sysconf(_SC_PHYS_PAGES); |
39 | |
|
40 | 0 | if (pagesize <= 0 || phys_pages <= 0) |
41 | 0 | return 0; |
42 | | |
43 | 0 | page_size_kb = pagesize / 1024; |
44 | 0 | phys_memory_kb = page_size_kb * phys_pages; |
45 | | |
46 | | /* sanity check for overflow */ |
47 | 0 | if (phys_memory_kb / phys_pages != page_size_kb) |
48 | 0 | return 0; |
49 | | |
50 | | /* coverity[return_overflow:FALSE] */ |
51 | 0 | return phys_memory_kb; |
52 | 0 | } |
53 | | |
54 | | uint64_t crypt_getphysmemoryfree_kb(void) |
55 | 0 | { |
56 | 0 | long pagesize, phys_pages; |
57 | 0 | uint64_t phys_memoryfree_kb, page_size_kb; |
58 | |
|
59 | 0 | pagesize = sysconf(_SC_PAGESIZE); |
60 | 0 | phys_pages = sysconf(_SC_AVPHYS_PAGES); |
61 | |
|
62 | 0 | if (pagesize <= 0 || phys_pages <= 0) |
63 | 0 | return 0; |
64 | | |
65 | 0 | page_size_kb = pagesize / 1024; |
66 | 0 | phys_memoryfree_kb = page_size_kb * phys_pages; |
67 | | |
68 | | /* sanity check for overflow */ |
69 | 0 | if (phys_memoryfree_kb / phys_pages != page_size_kb) |
70 | 0 | return 0; |
71 | | |
72 | | /* coverity[return_overflow:FALSE] */ |
73 | 0 | return phys_memoryfree_kb; |
74 | 0 | } |
75 | | |
76 | | bool crypt_swapavailable(void) |
77 | 0 | { |
78 | 0 | int fd; |
79 | 0 | ssize_t size; |
80 | 0 | char buf[4096], *p; |
81 | 0 | uint64_t total; |
82 | |
|
83 | 0 | if ((fd = open("/proc/meminfo", O_RDONLY)) < 0) |
84 | 0 | return true; |
85 | | |
86 | 0 | size = read(fd, buf, sizeof(buf)); |
87 | 0 | close(fd); |
88 | 0 | if (size < 1) |
89 | 0 | return true; |
90 | | |
91 | 0 | if (size < (ssize_t)sizeof(buf)) |
92 | 0 | buf[size] = 0; |
93 | 0 | else |
94 | 0 | buf[sizeof(buf) - 1] = 0; |
95 | |
|
96 | 0 | p = strstr(buf, "SwapTotal:"); |
97 | 0 | if (!p) |
98 | 0 | return true; |
99 | | |
100 | 0 | if (sscanf(p, "SwapTotal: %" PRIu64 " kB", &total) != 1) |
101 | 0 | return true; |
102 | | |
103 | 0 | return total > 0; |
104 | 0 | } |
105 | | |
106 | | void crypt_process_priority(struct crypt_device *cd, int *priority, bool raise) |
107 | 0 | { |
108 | 0 | int _priority, new_priority; |
109 | |
|
110 | 0 | if (raise) { |
111 | 0 | _priority = getpriority(PRIO_PROCESS, 0); |
112 | 0 | if (_priority < 0) |
113 | 0 | _priority = 0; |
114 | 0 | if (priority) |
115 | 0 | *priority = _priority; |
116 | | |
117 | | /* |
118 | | * Do not bother checking CAP_SYS_NICE as device activation |
119 | | * requires CAP_SYSADMIN later anyway. |
120 | | */ |
121 | 0 | if (getuid() || geteuid()) |
122 | 0 | new_priority = 0; |
123 | 0 | else |
124 | 0 | new_priority = -18; |
125 | |
|
126 | 0 | if (setpriority(PRIO_PROCESS, 0, new_priority)) |
127 | 0 | log_dbg(cd, "Cannot raise process priority."); |
128 | 0 | } else { |
129 | 0 | _priority = priority ? *priority : 0; |
130 | 0 | if (setpriority(PRIO_PROCESS, 0, _priority)) |
131 | 0 | log_dbg(cd, "Cannot reset process priority."); |
132 | 0 | } |
133 | 0 | } |
134 | | |
135 | | /* Keyfile processing */ |
136 | | |
137 | | /* |
138 | | * A simple call to lseek(3) might not be possible for some inputs (e.g. |
139 | | * reading from a pipe), so this function instead reads of up to BUFSIZ bytes |
140 | | * at a time until the specified number of bytes. It returns -1 on read error |
141 | | * or when it reaches EOF before the requested number of bytes have been |
142 | | * discarded. |
143 | | */ |
144 | | static int keyfile_seek(int fd, uint64_t bytes) |
145 | 0 | { |
146 | 0 | char tmp[BUFSIZ]; |
147 | 0 | size_t next_read; |
148 | 0 | ssize_t bytes_r; |
149 | 0 | off_t r; |
150 | |
|
151 | 0 | r = lseek(fd, bytes, SEEK_CUR); |
152 | 0 | if (r > 0) |
153 | 0 | return 0; |
154 | 0 | if (r < 0 && errno != ESPIPE) |
155 | 0 | return -1; |
156 | | |
157 | 0 | while (bytes > 0) { |
158 | | /* figure out how much to read */ |
159 | 0 | next_read = bytes > sizeof(tmp) ? sizeof(tmp) : (size_t)bytes; |
160 | |
|
161 | 0 | bytes_r = read(fd, tmp, next_read); |
162 | 0 | if (bytes_r < 0) { |
163 | 0 | if (errno == EINTR) |
164 | 0 | continue; |
165 | | |
166 | 0 | crypt_safe_memzero(tmp, sizeof(tmp)); |
167 | | /* read error */ |
168 | 0 | return -1; |
169 | 0 | } |
170 | | |
171 | 0 | if (bytes_r == 0) |
172 | | /* EOF */ |
173 | 0 | break; |
174 | | |
175 | 0 | bytes -= bytes_r; |
176 | 0 | } |
177 | | |
178 | 0 | crypt_safe_memzero(tmp, sizeof(tmp)); |
179 | 0 | return bytes == 0 ? 0 : -1; |
180 | 0 | } |
181 | | |
182 | | int crypt_keyfile_device_read(struct crypt_device *cd, const char *keyfile, |
183 | | char **key, size_t *key_size_read, |
184 | | uint64_t keyfile_offset, size_t key_size, |
185 | | uint32_t flags) |
186 | 0 | { |
187 | 0 | int fd, regular_file, char_to_read = 0, char_read = 0, unlimited_read = 0; |
188 | 0 | int r = -EINVAL, newline; |
189 | 0 | char *pass = NULL; |
190 | 0 | size_t buflen, i; |
191 | 0 | uint64_t file_read_size; |
192 | 0 | struct stat st; |
193 | 0 | bool close_fd = false; |
194 | |
|
195 | 0 | if (!key || !key_size_read) |
196 | 0 | return -EINVAL; |
197 | | |
198 | 0 | *key = NULL; |
199 | 0 | *key_size_read = 0; |
200 | |
|
201 | 0 | if (keyfile) { |
202 | 0 | fd = open(keyfile, O_RDONLY); |
203 | 0 | if (fd < 0) { |
204 | 0 | log_err(cd, _("Failed to open key file.")); |
205 | 0 | return -EINVAL; |
206 | 0 | } |
207 | 0 | close_fd = true; |
208 | 0 | } else |
209 | 0 | fd = STDIN_FILENO; |
210 | | |
211 | 0 | if (isatty(fd)) { |
212 | 0 | log_err(cd, _("Cannot read keyfile from a terminal.")); |
213 | 0 | goto out; |
214 | 0 | } |
215 | | |
216 | | /* If not requested otherwise, we limit input to prevent memory exhaustion */ |
217 | 0 | if (key_size == 0) { |
218 | 0 | key_size = DEFAULT_KEYFILE_SIZE_MAXKB * 1024 + 1; |
219 | 0 | unlimited_read = 1; |
220 | | /* use 4k for buffer (page divisor but avoid huge pages) */ |
221 | 0 | buflen = 4096 - 16; /* sizeof(struct safe_allocation); */ |
222 | 0 | } else |
223 | 0 | buflen = key_size; |
224 | |
|
225 | 0 | regular_file = 0; |
226 | 0 | if (keyfile) { |
227 | 0 | if (stat(keyfile, &st) < 0) { |
228 | 0 | log_err(cd, _("Failed to stat key file.")); |
229 | 0 | goto out; |
230 | 0 | } |
231 | 0 | if (S_ISREG(st.st_mode)) { |
232 | 0 | regular_file = 1; |
233 | 0 | file_read_size = (uint64_t)st.st_size; |
234 | |
|
235 | 0 | if (keyfile_offset > file_read_size) { |
236 | 0 | log_err(cd, _("Cannot seek to requested keyfile offset.")); |
237 | 0 | goto out; |
238 | 0 | } |
239 | 0 | file_read_size -= keyfile_offset; |
240 | | |
241 | | /* known keyfile size, alloc it in one step */ |
242 | 0 | if (file_read_size >= (uint64_t)key_size) |
243 | 0 | buflen = key_size; |
244 | 0 | else if (file_read_size) |
245 | 0 | buflen = file_read_size; |
246 | 0 | } |
247 | 0 | } |
248 | | |
249 | 0 | pass = crypt_safe_alloc(buflen); |
250 | 0 | if (!pass) { |
251 | 0 | log_err(cd, _("Out of memory while reading passphrase.")); |
252 | 0 | goto out; |
253 | 0 | } |
254 | | |
255 | | /* Discard keyfile_offset bytes on input */ |
256 | 0 | if (keyfile_offset && keyfile_seek(fd, keyfile_offset) < 0) { |
257 | 0 | log_err(cd, _("Cannot seek to requested keyfile offset.")); |
258 | 0 | goto out; |
259 | 0 | } |
260 | | |
261 | 0 | for (i = 0, newline = 0; i < key_size; i += char_read) { |
262 | 0 | if (i == buflen) { |
263 | 0 | buflen += 4096; |
264 | 0 | pass = crypt_safe_realloc(pass, buflen); |
265 | 0 | if (!pass) { |
266 | 0 | log_err(cd, _("Out of memory while reading passphrase.")); |
267 | 0 | r = -ENOMEM; |
268 | 0 | goto out; |
269 | 0 | } |
270 | 0 | } |
271 | | |
272 | 0 | if (flags & CRYPT_KEYFILE_STOP_EOL) { |
273 | | /* If we should stop on newline, we must read the input |
274 | | * one character at the time. Otherwise we might end up |
275 | | * having read some bytes after the newline, which we |
276 | | * promised not to do. |
277 | | */ |
278 | 0 | char_to_read = 1; |
279 | 0 | } else { |
280 | | /* char_to_read = min(key_size - i, buflen - i) */ |
281 | 0 | char_to_read = key_size < buflen ? |
282 | 0 | key_size - i : buflen - i; |
283 | 0 | } |
284 | 0 | char_read = read_buffer(fd, &pass[i], char_to_read); |
285 | 0 | if (char_read < 0) { |
286 | 0 | log_err(cd, _("Error reading passphrase.")); |
287 | 0 | r = -EPIPE; |
288 | 0 | goto out; |
289 | 0 | } |
290 | | |
291 | 0 | if (char_read == 0) |
292 | 0 | break; |
293 | | /* Stop on newline only if not requested read from keyfile */ |
294 | 0 | if ((flags & CRYPT_KEYFILE_STOP_EOL) && pass[i] == '\n') { |
295 | 0 | newline = 1; |
296 | 0 | pass[i] = '\0'; |
297 | 0 | break; |
298 | 0 | } |
299 | 0 | } |
300 | | |
301 | | /* Fail if piped input dies reading nothing */ |
302 | 0 | if (!i && !regular_file && !newline) { |
303 | 0 | log_err(cd, _("Nothing to read on input.")); |
304 | 0 | r = -EPIPE; |
305 | 0 | goto out; |
306 | 0 | } |
307 | | |
308 | | /* Fail if we exceeded internal default (no specified size) */ |
309 | 0 | if (unlimited_read && i == key_size) { |
310 | 0 | log_err(cd, _("Maximum keyfile size exceeded.")); |
311 | 0 | goto out; |
312 | 0 | } |
313 | | |
314 | 0 | if (!unlimited_read && i != key_size) { |
315 | 0 | log_err(cd, _("Cannot read requested amount of data.")); |
316 | 0 | goto out; |
317 | 0 | } |
318 | | |
319 | 0 | *key = pass; |
320 | 0 | *key_size_read = i; |
321 | 0 | r = 0; |
322 | 0 | out: |
323 | 0 | if (close_fd) |
324 | 0 | close(fd); |
325 | |
|
326 | 0 | if (r) |
327 | 0 | crypt_safe_free(pass); |
328 | 0 | return r; |
329 | 0 | } |
330 | | |
331 | | int crypt_keyfile_read(struct crypt_device *cd, const char *keyfile, |
332 | | char **key, size_t *key_size_read, |
333 | | size_t keyfile_offset, size_t keyfile_size_max, |
334 | | uint32_t flags) |
335 | 0 | { |
336 | 0 | return crypt_keyfile_device_read(cd, keyfile, key, key_size_read, |
337 | 0 | keyfile_offset, keyfile_size_max, flags); |
338 | 0 | } |
339 | | |
340 | | int kernel_version(uint64_t *kversion) |
341 | 0 | { |
342 | 0 | struct utsname uts; |
343 | 0 | uint16_t maj, min, patch, rel; |
344 | 0 | int r = -EINVAL; |
345 | |
|
346 | 0 | if (uname(&uts) < 0) |
347 | 0 | return r; |
348 | | |
349 | 0 | if (sscanf(uts.release, "%" SCNu16 ".%" SCNu16 ".%" SCNu16 "-%" SCNu16, |
350 | 0 | &maj, &min, &patch, &rel) == 4) |
351 | 0 | r = 0; |
352 | 0 | else if (sscanf(uts.release, "%" SCNu16 ".%" SCNu16 ".%" SCNu16, |
353 | 0 | &maj, &min, &patch) == 3) { |
354 | 0 | rel = 0; |
355 | 0 | r = 0; |
356 | 0 | } |
357 | |
|
358 | 0 | if (!r) |
359 | 0 | *kversion = compact_version(maj, min, patch, rel); |
360 | |
|
361 | 0 | return r; |
362 | 0 | } |
363 | | |
364 | | bool crypt_string_in(const char *str, char **list, size_t list_size) |
365 | 0 | { |
366 | 0 | size_t i; |
367 | |
|
368 | 0 | for (i = 0; *list && i < list_size; i++, list++) |
369 | 0 | if (!strcmp(str, *list)) |
370 | 0 | return true; |
371 | | |
372 | 0 | return false; |
373 | 0 | } |
374 | | |
375 | | /* compare two strings (allows NULL values) */ |
376 | | int crypt_strcmp(const char *a, const char *b) |
377 | 0 | { |
378 | 0 | if (!a && !b) |
379 | 0 | return 0; |
380 | 0 | else if (!a || !b) |
381 | 0 | return 1; |
382 | 0 | return strcmp(a, b); |
383 | 0 | } |