/src/libgpg-error/src/sysutils.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* sysutils.c - Platform specific helper functions |
2 | | * Copyright (C) 2017 g10 Code GmbH |
3 | | * |
4 | | * This file is part of libgpg-error. |
5 | | * |
6 | | * libgpg-error is free software; you can redistribute it and/or |
7 | | * modify it under the terms of the GNU Lesser General Public License |
8 | | * as published by the Free Software Foundation; either version 2.1 of |
9 | | * the License, or (at your option) any later version. |
10 | | * |
11 | | * libgpg-error is distributed in the hope that it will be useful, but |
12 | | * WITHOUT ANY WARRANTY; without even the implied warranty of |
13 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
14 | | * Lesser General Public License for more details. |
15 | | * |
16 | | * You should have received a copy of the GNU Lesser General Public |
17 | | * License along with this program; if not, see <https://www.gnu.org/licenses/>. |
18 | | * SPDX-License-Identifier: LGPL-2.1+ |
19 | | */ |
20 | | |
21 | | #include <config.h> |
22 | | #include <stdlib.h> |
23 | | #include <stdint.h> |
24 | | #include <string.h> |
25 | | #include <unistd.h> |
26 | | #include <errno.h> |
27 | | #ifdef HAVE_W32_SYSTEM |
28 | | # include <windows.h> |
29 | | #endif |
30 | | #ifdef HAVE_STAT |
31 | | # include <sys/stat.h> |
32 | | #endif |
33 | | #include <sys/types.h> |
34 | | #include <fcntl.h> |
35 | | #ifdef HAVE_PWD_H |
36 | | # include <pwd.h> |
37 | | #endif |
38 | | |
39 | | #include "gpgrt-int.h" |
40 | | |
41 | | |
42 | | /* Return true if FD is valid. */ |
43 | | int |
44 | | _gpgrt_fd_valid_p (int fd) |
45 | 0 | { |
46 | 0 | int d = dup (fd); |
47 | 0 | if (d < 0) |
48 | 0 | return 0; |
49 | 0 | close (d); |
50 | 0 | return 1; |
51 | 0 | } |
52 | | |
53 | | |
54 | | /* Our variant of getenv. The returned string must be freed. If the |
55 | | * environment variable does not exists NULL is returned and ERRNO set |
56 | | * to 0. */ |
57 | | char * |
58 | | _gpgrt_getenv (const char *name) |
59 | 0 | { |
60 | 0 | if (!name || !*name || strchr (name, '=')) |
61 | 0 | { |
62 | 0 | _gpg_err_set_errno (EINVAL); |
63 | 0 | return NULL; |
64 | 0 | } |
65 | | |
66 | | #ifdef HAVE_W32_SYSTEM |
67 | | { |
68 | | int len, size; |
69 | | char *result; |
70 | | |
71 | | len = GetEnvironmentVariable (name, NULL, 0); |
72 | | if (!len && GetLastError () == ERROR_ENVVAR_NOT_FOUND) |
73 | | { |
74 | | _gpg_err_set_errno (0); |
75 | | return NULL; |
76 | | } |
77 | | again: |
78 | | size = len; |
79 | | result = _gpgrt_malloc (size); |
80 | | if (!result) |
81 | | return NULL; |
82 | | len = GetEnvironmentVariable (name, result, size); |
83 | | if (len >= size) |
84 | | { |
85 | | /* Changed in the meantime - retry. */ |
86 | | _gpgrt_free (result); |
87 | | goto again; |
88 | | } |
89 | | if (!len && GetLastError () == ERROR_ENVVAR_NOT_FOUND) |
90 | | { |
91 | | /* Deleted in the meantime. */ |
92 | | _gpgrt_free (result); |
93 | | _gpg_err_set_errno (0); |
94 | | return NULL; |
95 | | } |
96 | | if (!len) |
97 | | { |
98 | | /* Other error. FIXME: We need mapping fucntion. */ |
99 | | _gpgrt_free (result); |
100 | | _gpg_err_set_errno (EIO); |
101 | | return NULL; |
102 | | } |
103 | | |
104 | | return result; |
105 | | } |
106 | | #else /*!HAVE_W32_SYSTEM*/ |
107 | 0 | { |
108 | 0 | const char *s = getenv (name); |
109 | 0 | if (!s) |
110 | 0 | { |
111 | 0 | _gpg_err_set_errno (0); |
112 | 0 | return NULL; |
113 | 0 | } |
114 | 0 | return _gpgrt_strdup (s); |
115 | 0 | } |
116 | 0 | #endif /*!HAVE_W32_SYSTEM*/ |
117 | 0 | } |
118 | | |
119 | | |
120 | | /* Wrapper around setenv so that we can have the same function in |
121 | | * Windows and Unix. In contrast to the standard setenv passing a |
122 | | * VALUE as NULL and setting OVERWRITE will remove the envvar. */ |
123 | | gpg_err_code_t |
124 | | _gpgrt_setenv (const char *name, const char *value, int overwrite) |
125 | 0 | { |
126 | 0 | if (!name || !*name || strchr (name, '=')) |
127 | 0 | return GPG_ERR_EINVAL; |
128 | | |
129 | | #ifdef HAVE_W32_SYSTEM |
130 | | /* Windows maintains (at least) two sets of environment variables. |
131 | | * One set can be accessed by GetEnvironmentVariable and |
132 | | * SetEnvironmentVariable. This set is inherited by the children. |
133 | | * The other set is maintained in the C runtime, and is accessed |
134 | | * using getenv and putenv. We try to keep them in sync by |
135 | | * modifying both sets. Note that gpgrt_getenv ignores the libc |
136 | | * values - however, too much existing code still uses getenv. */ |
137 | | { |
138 | | int exists; |
139 | | char tmpbuf[10]; |
140 | | char *buf; |
141 | | |
142 | | if (!value && overwrite) |
143 | | { |
144 | | if (!SetEnvironmentVariable (name, NULL)) |
145 | | return GPG_ERR_EINVAL; |
146 | | if (getenv (name)) |
147 | | { |
148 | | /* Ugly: Leaking memory. */ |
149 | | buf = _gpgrt_strdup (name); |
150 | | if (!buf) |
151 | | return _gpg_err_code_from_syserror (); |
152 | | if (putenv (buf)) |
153 | | return _gpg_err_code_from_syserror (); |
154 | | } |
155 | | return 0; |
156 | | } |
157 | | |
158 | | exists = GetEnvironmentVariable (name, tmpbuf, sizeof tmpbuf); |
159 | | if ((! exists || overwrite) && !SetEnvironmentVariable (name, value)) |
160 | | return GPG_ERR_EINVAL; /* (Might also be ENOMEM.) */ |
161 | | if (overwrite || !getenv (name)) |
162 | | { |
163 | | /* Ugly: Leaking memory. */ |
164 | | buf = _gpgrt_strconcat (name, "=", value, NULL); |
165 | | if (!buf) |
166 | | return _gpg_err_code_from_syserror (); |
167 | | if (putenv (buf)) |
168 | | return _gpg_err_code_from_syserror (); |
169 | | } |
170 | | return 0; |
171 | | } |
172 | | |
173 | | #else /*!HAVE_W32_SYSTEM*/ |
174 | | |
175 | 0 | # ifdef HAVE_SETENV |
176 | | |
177 | 0 | { |
178 | 0 | if (!value && overwrite) |
179 | 0 | { |
180 | 0 | if (unsetenv (name)) |
181 | 0 | return _gpg_err_code_from_syserror (); |
182 | 0 | } |
183 | 0 | else |
184 | 0 | { |
185 | 0 | if (setenv (name, value ? value : "", overwrite)) |
186 | 0 | return _gpg_err_code_from_syserror (); |
187 | 0 | } |
188 | | |
189 | 0 | return 0; |
190 | 0 | } |
191 | |
|
192 | | # else /*!HAVE_SETENV*/ |
193 | | |
194 | | # if __GNUC__ |
195 | | # warning no setenv - using putenv but leaking memory. |
196 | | # endif |
197 | | { |
198 | | char *buf; |
199 | | |
200 | | if (!value && overwrite) |
201 | | { |
202 | | if (getenv (name)) |
203 | | { |
204 | | buf = _gpgrt_strdup (name); |
205 | | if (!buf) |
206 | | return _gpg_err_code_from_syserror (); |
207 | | if (putenv (buf)) |
208 | | return -1; |
209 | | } |
210 | | } |
211 | | else if (overwrite || !getenv (name)) |
212 | | { |
213 | | buf = _gpgrt_strconcat (name, "=", value, NULL); |
214 | | if (!buf) |
215 | | return _gpg_err_code_from_syserror (); |
216 | | if (putenv (buf)) |
217 | | return _gpg_err_code_from_syserror (); |
218 | | } |
219 | | |
220 | | return 0; |
221 | | } |
222 | | # endif /*!HAVE_SETENV*/ |
223 | 0 | #endif /*!HAVE_W32_SYSTEM*/ |
224 | 0 | } |
225 | | |
226 | | |
227 | | #ifdef HAVE_W32_SYSTEM |
228 | | /* Convert an UTF-8 encode file name to wchar. If the file name is |
229 | | * close to the limit of MAXPATH the API functions will fail. The |
230 | | * method to overcome this API limitation is to use a prefix which |
231 | | * bypasses the checking by CreateFile. This also required to first |
232 | | * convert the name to an absolute file name. */ |
233 | | wchar_t * |
234 | | _gpgrt_fname_to_wchar (const char *fname) |
235 | | { |
236 | | wchar_t *wname; |
237 | | wchar_t *wfullpath = NULL; |
238 | | int success = 0; |
239 | | |
240 | | wname = _gpgrt_utf8_to_wchar (fname); |
241 | | if (!wname) |
242 | | return NULL; |
243 | | |
244 | | if (!strncmp (fname, "\\\\?\\", 4)) |
245 | | success = 1; /* Already translated. */ |
246 | | else if (wcslen (wname) > 230) |
247 | | { |
248 | | int wlen = 1024; |
249 | | int extralen; |
250 | | DWORD res; |
251 | | wchar_t *w; |
252 | | |
253 | | try_again: |
254 | | wfullpath = xtrymalloc (wlen * sizeof *wfullpath); |
255 | | if (!wfullpath) |
256 | | goto leave; |
257 | | |
258 | | if (*fname == '\\' && fname[1] == '\\' && fname[2]) |
259 | | { |
260 | | wcscpy (wfullpath, L"\\\\?\\UNC\\"); |
261 | | extralen = 8; |
262 | | } |
263 | | else |
264 | | { |
265 | | wcscpy (wfullpath, L"\\\\?\\"); |
266 | | extralen = 4; |
267 | | } |
268 | | res = GetFullPathNameW (wname, wlen-extralen, wfullpath+extralen, NULL); |
269 | | if (!res) |
270 | | { |
271 | | _gpgrt_w32_set_errno (-1); |
272 | | goto leave; |
273 | | } |
274 | | else if (res >= wlen - extralen) |
275 | | { |
276 | | /* Truncated - increase to the desired length. */ |
277 | | if (wlen > 1024) |
278 | | { |
279 | | /* We should never get to here. */ |
280 | | errno = ENAMETOOLONG; |
281 | | goto leave; |
282 | | } |
283 | | /* GetFullPathNameW indicated the required buffer length. */ |
284 | | _gpgrt_free_wchar (wfullpath); |
285 | | wfullpath = NULL; |
286 | | wlen = res + extralen; |
287 | | goto try_again; |
288 | | } |
289 | | _gpgrt_free_wchar (wname); |
290 | | wname = wfullpath; |
291 | | wfullpath = NULL; |
292 | | /* Need to make sure that all slashes are mapped. */ |
293 | | for (w = wname; *w; w++) |
294 | | if (*w == L'/') |
295 | | *w = L'\\'; |
296 | | success = 1; |
297 | | } |
298 | | else |
299 | | success = 1; |
300 | | |
301 | | leave: |
302 | | _gpgrt_free_wchar (wfullpath); |
303 | | if (!success) |
304 | | { |
305 | | _gpgrt_free_wchar (wname); |
306 | | wname = NULL; |
307 | | } |
308 | | return wname; |
309 | | } |
310 | | |
311 | | #endif /*HAVE_W32_SYSTEM*/ |
312 | | |
313 | | |
314 | | |
315 | | #ifndef HAVE_W32_SYSTEM |
316 | | static mode_t |
317 | | modestr_to_mode (const char *modestr) |
318 | | { |
319 | | mode_t mode = 0; |
320 | | |
321 | | if (modestr && *modestr) |
322 | | { |
323 | | modestr++; |
324 | | if (*modestr && *modestr++ == 'r') |
325 | | mode |= S_IRUSR; |
326 | | if (*modestr && *modestr++ == 'w') |
327 | | mode |= S_IWUSR; |
328 | | if (*modestr && *modestr++ == 'x') |
329 | | mode |= S_IXUSR; |
330 | | if (*modestr && *modestr++ == 'r') |
331 | | mode |= S_IRGRP; |
332 | | if (*modestr && *modestr++ == 'w') |
333 | | mode |= S_IWGRP; |
334 | | if (*modestr && *modestr++ == 'x') |
335 | | mode |= S_IXGRP; |
336 | | if (*modestr && *modestr++ == 'r') |
337 | | mode |= S_IROTH; |
338 | | if (*modestr && *modestr++ == 'w') |
339 | | mode |= S_IWOTH; |
340 | | if (*modestr && *modestr++ == 'x') |
341 | | mode |= S_IXOTH; |
342 | | } |
343 | | |
344 | | return mode; |
345 | | } |
346 | | #endif |
347 | | |
348 | | |
349 | | /* A wrapper around mkdir which takes a string for the mode argument. |
350 | | * This makes it easier to handle the mode argument which is not |
351 | | * defined on all systems. The format of the modestring is |
352 | | * |
353 | | * "-rwxrwxrwx" |
354 | | * |
355 | | * '-' is a don't care or not set. 'r', 'w', 'x' are read allowed, |
356 | | * write allowed, execution allowed with the first group for the user, |
357 | | * the second for the group and the third for all others. If the |
358 | | * string is shorter than above the missing mode characters are meant |
359 | | * to be not set. |
360 | | * |
361 | | * Note that in addition to returning an gpg-error error code ERRNO is |
362 | | * also set by this function. |
363 | | */ |
364 | | gpg_err_code_t |
365 | | _gpgrt_mkdir (const char *name, const char *modestr) |
366 | 0 | { |
367 | | #ifdef HAVE_W32_SYSTEM |
368 | | wchar_t *wname; |
369 | | gpg_err_code_t ec; |
370 | | (void)modestr; |
371 | | |
372 | | /* Note: Fixme: We should set appropriate permissions. */ |
373 | | wname = _gpgrt_fname_to_wchar (name); |
374 | | if (!wname) |
375 | | return _gpg_err_code_from_syserror (); |
376 | | |
377 | | if (!CreateDirectoryW (wname, NULL)) |
378 | | { |
379 | | _gpgrt_w32_set_errno (-1); |
380 | | ec = _gpg_err_code_from_syserror (); |
381 | | } |
382 | | else |
383 | | ec = 0; |
384 | | |
385 | | _gpgrt_free_wchar (wname); |
386 | | return ec; |
387 | | |
388 | | #elif MKDIR_TAKES_ONE_ARG |
389 | | (void)modestr; |
390 | | if (mkdir (name)) |
391 | | return _gpg_err_code_from_syserror (); |
392 | | return 0; |
393 | | #else |
394 | 0 | if (mkdir (name, modestr_to_mode (modestr))) |
395 | 0 | return _gpg_err_code_from_syserror (); |
396 | 0 | return 0; |
397 | 0 | #endif |
398 | 0 | } |
399 | | |
400 | | |
401 | | /* A simple wrapper around chdir. NAME is expected to be utf8 |
402 | | * encoded. Note that in addition to returning an gpg-error error |
403 | | * code ERRNO is also set by this function. */ |
404 | | gpg_err_code_t |
405 | | _gpgrt_chdir (const char *name) |
406 | 0 | { |
407 | | #ifdef HAVE_W32_SYSTEM |
408 | | wchar_t *wname; |
409 | | gpg_err_code_t ec; |
410 | | |
411 | | /* Note that the \\?\ trick does not work with SetCurrentDirectoryW |
412 | | * Thus we use the plain conversion function. */ |
413 | | wname = _gpgrt_utf8_to_wchar (name); |
414 | | if (!wname) |
415 | | return _gpg_err_code_from_syserror (); |
416 | | if (!SetCurrentDirectoryW (wname)) |
417 | | { |
418 | | _gpgrt_w32_set_errno (-1); |
419 | | ec = _gpg_err_code_from_syserror (); |
420 | | } |
421 | | else |
422 | | ec = 0; |
423 | | _gpgrt_free_wchar (wname); |
424 | | return ec; |
425 | | |
426 | | #else /*!HAVE_W32_SYSTEM*/ |
427 | 0 | if (chdir (name)) |
428 | 0 | return _gpg_err_code_from_syserror (); |
429 | 0 | return 0; |
430 | 0 | #endif /*!HAVE_W32_SYSTEM*/ |
431 | 0 | } |
432 | | |
433 | | |
434 | | /* Return the current working directory as a malloced string. Return |
435 | | * NULL and sets ERRNO on error. */ |
436 | | char * |
437 | | _gpgrt_getcwd (void) |
438 | 0 | { |
439 | | #if defined(HAVE_W32_SYSTEM) |
440 | | wchar_t wbuffer[MAX_PATH + sizeof(wchar_t)]; |
441 | | DWORD wlen; |
442 | | char *buf, *p; |
443 | | |
444 | | wlen = GetCurrentDirectoryW (MAX_PATH, wbuffer); |
445 | | if (!wlen) |
446 | | { |
447 | | _gpgrt_w32_set_errno (-1); |
448 | | return NULL; |
449 | | |
450 | | } |
451 | | else if (wlen > MAX_PATH) |
452 | | { |
453 | | /* FWIW: I tried to use GetFullPathNameW (L".") but found no way |
454 | | * to execute a test program at a too long cwd. */ |
455 | | _gpg_err_set_errno (ENAMETOOLONG); |
456 | | return NULL; |
457 | | } |
458 | | buf = _gpgrt_wchar_to_utf8 (wbuffer, wlen); |
459 | | if (buf) |
460 | | { |
461 | | for (p=buf; *p; p++) |
462 | | if (*p == '\\') |
463 | | *p = '/'; |
464 | | } |
465 | | return buf; |
466 | | |
467 | | #else /*Unix*/ |
468 | 0 | char *buffer; |
469 | 0 | size_t size = 100; |
470 | |
|
471 | 0 | for (;;) |
472 | 0 | { |
473 | 0 | buffer = xtrymalloc (size+1); |
474 | 0 | if (!buffer) |
475 | 0 | return NULL; |
476 | 0 | if (getcwd (buffer, size) == buffer) |
477 | 0 | return buffer; |
478 | 0 | xfree (buffer); |
479 | 0 | if (errno != ERANGE) |
480 | 0 | return NULL; |
481 | 0 | size *= 2; |
482 | 0 | } |
483 | 0 | #endif /*Unix*/ |
484 | 0 | } |
485 | | |
486 | | |
487 | | /* Wrapper around access to handle file name encoding under Windows. |
488 | | * Returns 0 if FNAME can be accessed in MODE or an error code. ERRNO |
489 | | * is also set on error. */ |
490 | | gpg_err_code_t |
491 | | _gpgrt_access (const char *fname, int mode) |
492 | 1.02k | { |
493 | 1.02k | gpg_err_code_t ec; |
494 | | |
495 | | #ifdef HAVE_W32_SYSTEM |
496 | | wchar_t *wfname; |
497 | | DWORD attribs; |
498 | | |
499 | | wfname = _gpgrt_fname_to_wchar (fname); |
500 | | if (!wfname) |
501 | | return _gpg_err_code_from_syserror (); |
502 | | |
503 | | attribs = GetFileAttributesW (wfname); |
504 | | if (attribs == (DWORD)(-1)) |
505 | | ec = _gpgrt_w32_get_last_err_code (); |
506 | | else |
507 | | { |
508 | | if ((mode & W_OK) && (attribs & FILE_ATTRIBUTE_READONLY)) |
509 | | { |
510 | | _gpg_err_set_errno (EACCES); |
511 | | ec = _gpg_err_code_from_syserror (); |
512 | | } |
513 | | else |
514 | | ec = 0; |
515 | | } |
516 | | _gpgrt_free_wchar (wfname); |
517 | | #else /* Unix */ |
518 | 1.02k | ec = access (fname, mode)? _gpg_err_code_from_syserror () : 0; |
519 | 1.02k | #endif /* Unix */ |
520 | | |
521 | 1.02k | return ec; |
522 | 1.02k | } |
523 | | |
524 | | |
525 | | /* Get the standard home directory for user NAME. If NAME is NULL the |
526 | | * directory for the current user is returned. Caller must release |
527 | | * the returned string. */ |
528 | | char * |
529 | | _gpgrt_getpwdir (const char *name) |
530 | 0 | { |
531 | 0 | char *result = NULL; |
532 | 0 | #ifdef HAVE_PWD_H |
533 | 0 | struct passwd *pwd = NULL; |
534 | |
|
535 | 0 | if (name) |
536 | 0 | { |
537 | 0 | #ifdef HAVE_GETPWNAM |
538 | | /* Fixme: We should use getpwnam_r if available. */ |
539 | 0 | pwd = getpwnam (name); |
540 | 0 | #endif |
541 | 0 | } |
542 | 0 | else |
543 | 0 | { |
544 | 0 | #ifdef HAVE_GETPWUID |
545 | | /* Fixme: We should use getpwuid_r if available. */ |
546 | 0 | pwd = getpwuid (getuid()); |
547 | 0 | #endif |
548 | 0 | } |
549 | 0 | if (pwd) |
550 | 0 | { |
551 | 0 | result = _gpgrt_strdup (pwd->pw_dir); |
552 | 0 | } |
553 | | #else /*!HAVE_PWD_H*/ |
554 | | /* No support at all. */ |
555 | | (void)name; |
556 | | #endif /*HAVE_PWD_H*/ |
557 | 0 | return result; |
558 | 0 | } |
559 | | |
560 | | |
561 | | /* Return a malloced copy of the current user's account name; this may |
562 | | * return NULL on memory failure. */ |
563 | | char * |
564 | | _gpgrt_getusername (void) |
565 | 0 | { |
566 | 0 | char *result = NULL; |
567 | |
|
568 | | #ifdef HAVE_W32_SYSTEM |
569 | | wchar_t wtmp[1]; |
570 | | wchar_t *wbuf; |
571 | | DWORD wsize = 1; |
572 | | char *buf; |
573 | | |
574 | | GetUserNameW (wtmp, &wsize); |
575 | | wbuf = _gpgrt_malloc (wsize * sizeof *wbuf); |
576 | | if (!wbuf) |
577 | | { |
578 | | _gpgrt_w32_set_errno (-1); |
579 | | return NULL; |
580 | | } |
581 | | if (!GetUserNameW (wbuf, &wsize)) |
582 | | { |
583 | | _gpgrt_w32_set_errno (-1); |
584 | | xfree (wbuf); |
585 | | return NULL; |
586 | | } |
587 | | buf = _gpgrt_wchar_to_utf8 (wbuf, wsize); |
588 | | xfree (wbuf); |
589 | | return buf; |
590 | | |
591 | | #else /* !HAVE_W32_SYSTEM */ |
592 | |
|
593 | 0 | # if defined(HAVE_PWD_H) && defined(HAVE_GETPWUID) |
594 | 0 | struct passwd *pwd; |
595 | |
|
596 | 0 | pwd = getpwuid (getuid()); |
597 | 0 | if (pwd) |
598 | 0 | { |
599 | 0 | result = _gpgrt_strdup (pwd->pw_name); |
600 | 0 | } |
601 | |
|
602 | 0 | # endif /*HAVE_PWD_H*/ |
603 | |
|
604 | 0 | #endif /* !HAVE_W32_SYSTEM */ |
605 | |
|
606 | 0 | return result; |
607 | 0 | } |