Coverage Report

Created: 2022-12-08 06:09

/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
}