Coverage Report

Created: 2026-01-10 07:04

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gnupg/common/session-env.c
Line
Count
Source
1
/* session-env.c - Session environment helper functions.
2
 * Copyright (C) 2009 Free Software Foundation, Inc.
3
 *
4
 * This file is part of GnuPG.
5
 *
6
 * This file is free software; you can redistribute it and/or modify
7
 * it under the terms of either
8
 *
9
 *   - the GNU Lesser General Public License as published by the Free
10
 *     Software Foundation; either version 3 of the License, or (at
11
 *     your option) any later version.
12
 *
13
 * or
14
 *
15
 *   - the GNU General Public License as published by the Free
16
 *     Software Foundation; either version 2 of the License, or (at
17
 *     your option) any later version.
18
 *
19
 * or both in parallel, as here.
20
 *
21
 * This file is distributed in the hope that it will be useful,
22
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
23
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
24
 * GNU General Public License for more details.
25
 *
26
 * You should have received a copy of the GNU General Public License
27
 * along with this program; if not, see <https://www.gnu.org/licenses/>.
28
 */
29
30
#include <config.h>
31
#include <stdlib.h>
32
#include <errno.h>
33
#include <ctype.h>
34
#include <assert.h>
35
#include <unistd.h>
36
37
#include "util.h"
38
#include "session-env.h"
39
40
41
struct variable_s
42
{
43
  char *value;    /* Pointer into NAME to the Nul terminated value. */
44
  int is_default; /* The value is a default one.  */
45
  char name[1];   /* Nul terminated Name and space for the value.  */
46
};
47
48
49
50
/* The session environment object.  */
51
struct session_environment_s
52
{
53
  size_t arraysize;          /* Allocated size or ARRAY.  */
54
  size_t arrayused;          /* Used size of ARRAY.  */
55
  struct variable_s **array; /* Array of variables.  NULL slots are unused.  */
56
};
57
58
59
/* A list of environment variables we pass from the actual user
60
  (e.g. gpgme) down to the pinentry.  We do not handle the locale
61
  settings because they do not only depend on envvars.  */
62
static struct
63
{
64
  const char *name;
65
  const char *assname;  /* Name used by Assuan or NULL.  */
66
  unsigned int disabled;/* The entry is not valid        */
67
} stdenvnames[] = {
68
  { "GPG_TTY", "ttyname" },      /* GnuPG specific envvar.  */
69
  { "TERM",    "ttytype" },      /* Used to set ttytype. */
70
  { "DISPLAY", "display" },      /* The X-Display.  */
71
  { "XAUTHORITY","xauthority"},  /* Xlib Authentication.  */
72
  { "XMODIFIERS" },              /* Used by Xlib to select X input
73
                                      modules (eg "@im=SCIM").  */
74
  { "WAYLAND_DISPLAY" },         /* For the Wayland display engine.  */
75
  { "XDG_SESSION_TYPE" },        /* Used by Qt and other non-GTK toolkits
76
                                    to check for x11 or wayland.  */
77
  { "QT_QPA_PLATFORM" },         /* Used by Qt to explicitly request
78
                                    x11 or wayland; in particular, needed
79
                                    to make Qt use Wayland on Gnome.  */
80
  { "GTK_IM_MODULE" },           /* Used by gtk to select gtk input
81
                                    modules (eg "scim-bridge").  */
82
  { "DBUS_SESSION_BUS_ADDRESS" },/* Used by GNOME3 to talk to gcr over
83
                                    dbus */
84
  { "QT_IM_MODULE" },            /* Used by Qt to select qt input
85
                                      modules (eg "xim").  */
86
  { "INSIDE_EMACS" },            /* Set by Emacs before running a
87
                                    process.  */
88
  { "PINENTRY_USER_DATA", "pinentry-user-data"},
89
                                 /* Used for communication with
90
                                    non-standard Pinentries.  */
91
  { "PINENTRY_GEOM_HINT" }       /* Used to pass window information. */
92
};
93
94
95
/* Track last allocated arraysize of all objects ever created.  If
96
   nothing has ever been allocated we use INITIAL_ARRAYSIZE and we
97
   will never use more than MAXDEFAULT_ARRAYSIZE for initial
98
   allocation.  Note that this is not reentrant if used with a
99
   preemptive thread model.  */
100
static size_t lastallocatedarraysize;
101
0
#define INITIAL_ARRAYSIZE 14  /* Let's use the number of stdenvnames.  */
102
0
#define CHUNK_ARRAYSIZE 16
103
0
#define MAXDEFAULT_ARRAYSIZE (INITIAL_ARRAYSIZE + CHUNK_ARRAYSIZE * 5)
104
105
106
/* Modify the list of environment names which are known to gpg-agent.
107
 * This function must be called before the session names are used and
108
 * should not be changed later.  The syntax for NAME is:
109
 *
110
 *    -FOO        := Remove the environment variable FOO from the list
111
 *    [+]FOO      := Add the environment variable FOO to the list
112
 *    [+]FOO:bar  := Ditto, but also add "bar" as Assuan alias.
113
 *
114
 * Note that adding environment variables is not yet supported and
115
 * silently ignored.
116
 */
117
void
118
session_env_mod_stdenvnames (const char *name)
119
0
{
120
0
  int idx;
121
122
0
  if (*name != '-')
123
0
    return;
124
0
  name++;
125
0
  if (!*name)
126
0
    return;
127
128
0
  for (idx = 0; idx < DIM (stdenvnames); idx++)
129
0
    {
130
0
      if (!strcmp (stdenvnames[idx].name, name))
131
0
        stdenvnames[idx].disabled = 1;
132
0
    }
133
0
}
134
135
136
/* Return the names of standard environment variables one after the
137
   other.  The caller needs to set the value at the address of
138
   ITERATOR initially to 0 and then call this function until it
139
   returns NULL.  If ITERATOR is NULL, a single comma delimited string
140
   with the names is returned; NULL is never returned in this case and
141
   R_ASSNAME is ignored.  */
142
const char *
143
session_env_list_stdenvnames (int *iterator, const char **r_assname)
144
0
{
145
0
  int idx;
146
0
  static char *commastring;
147
148
0
  if (!iterator)
149
0
    {
150
0
      if (!commastring)
151
0
        {
152
0
          size_t len = 0;
153
0
          char *p;
154
155
0
          for (idx = 0; idx < DIM (stdenvnames); idx++)
156
0
            len += strlen (stdenvnames[idx].name) + 1;
157
0
          commastring = xtrymalloc (len);
158
0
          if (!commastring)
159
0
            {
160
0
              log_error ("%s: error allocating string: %s\n", __func__,
161
0
                         gpg_strerror (gpg_error_from_syserror ()));
162
0
              return "GPG_TTY,TERM,DISPLAY";
163
0
            }
164
0
          p = commastring;
165
0
          for (idx = 0; idx < DIM (stdenvnames); idx++)
166
0
            {
167
0
              if (stdenvnames[idx].disabled)
168
0
                continue;
169
0
              if (idx)
170
0
                *p++ = ',';
171
0
              p = stpcpy (p, stdenvnames[idx].name);
172
0
            }
173
0
          gpgrt_annotate_leaked_object (commastring);
174
0
        }
175
0
      return commastring;
176
0
    }
177
178
0
  do
179
0
    {
180
0
      idx = *iterator;
181
0
      if (idx < 0 || idx >= DIM (stdenvnames))
182
0
        return NULL;
183
0
      *iterator = idx + 1;
184
0
    }
185
0
  while (stdenvnames[idx].disabled);
186
0
  if (r_assname)
187
0
    *r_assname = stdenvnames[idx].assname;
188
0
  return stdenvnames[idx].name;
189
0
}
190
191
192
/* Create a new session environment object.  Return NULL and sets
193
   ERRNO on failure. */
194
session_env_t
195
session_env_new (void)
196
0
{
197
0
  session_env_t se;
198
199
0
  se = xtrycalloc (1, sizeof *se);
200
0
  if (se)
201
0
    {
202
0
      se->arraysize = (lastallocatedarraysize?
203
0
                       lastallocatedarraysize : INITIAL_ARRAYSIZE);
204
0
      se->array = xtrycalloc (se->arraysize, sizeof *se->array);
205
0
      if (!se->array)
206
0
        {
207
0
          xfree (se);
208
0
          se = NULL;
209
0
        }
210
0
    }
211
212
0
  return se;
213
0
}
214
215
216
/* Release a session environment object.  */
217
void
218
session_env_release (session_env_t se)
219
0
{
220
0
  int idx;
221
222
0
  if (!se)
223
0
    return;
224
225
0
  if (se->arraysize > INITIAL_ARRAYSIZE
226
0
      && se->arraysize <= MAXDEFAULT_ARRAYSIZE
227
0
      && se->arraysize > lastallocatedarraysize)
228
0
    lastallocatedarraysize = se->arraysize;
229
230
0
  for (idx=0; idx < se->arrayused; idx++)
231
0
    if (se->array[idx])
232
0
      xfree (se->array[idx]);
233
0
  xfree (se->array);
234
0
  xfree (se);
235
0
}
236
237
238
static gpg_error_t
239
delete_var (session_env_t se, const char *name)
240
0
{
241
0
  int idx;
242
243
0
  for (idx=0; idx < se->arrayused; idx++)
244
0
    if (se->array[idx] && !strcmp (se->array[idx]->name, name))
245
0
      {
246
0
        xfree (se->array[idx]);
247
0
        se->array[idx] = NULL;
248
0
      }
249
0
  return 0;
250
0
}
251
252
253
static gpg_error_t
254
update_var (session_env_t se, const char *string, size_t namelen,
255
            const char *explicit_value, int set_default)
256
0
{
257
0
  int idx;
258
0
  int freeidx = -1;
259
0
  const char *value;
260
0
  size_t valuelen;
261
0
  struct variable_s *var;
262
263
0
  if (explicit_value)
264
0
    value = explicit_value;
265
0
  else
266
0
    value = string + namelen + 1;
267
0
  valuelen = strlen (value);
268
269
0
  for (idx=0; idx < se->arrayused; idx++)
270
0
    {
271
0
      if (!se->array[idx])
272
0
        freeidx = idx;
273
0
      else if (!strncmp (se->array[idx]->name, string, namelen)
274
0
               && strlen (se->array[idx]->name) == namelen)
275
0
        {
276
0
          if (strlen (se->array[idx]->value) == valuelen)
277
0
            {
278
              /* The new value has the same length.  We can update it
279
                 in-place.  */
280
0
              memcpy (se->array[idx]->value, value, valuelen);
281
0
              se->array[idx]->is_default = !!set_default;
282
0
              return 0;
283
0
            }
284
          /* Prepare for update.  */
285
0
          freeidx = idx;
286
0
        }
287
0
    }
288
289
0
  if (freeidx == -1)
290
0
    {
291
0
      if (se->arrayused == se->arraysize)
292
0
        {
293
          /* Reallocate the array. */
294
0
          size_t newsize;
295
0
          struct variable_s **newarray;
296
297
0
          newsize = se->arraysize + CHUNK_ARRAYSIZE;
298
0
          newarray = xtrycalloc (newsize, sizeof *newarray);
299
0
          if (!newarray)
300
0
            return gpg_error_from_syserror ();
301
0
          for (idx=0; idx < se->arrayused; idx++)
302
0
            newarray[idx] = se->array[idx];
303
0
          se->arraysize = newsize;
304
0
          xfree (se->array);
305
0
          se->array = newarray;
306
0
        }
307
0
      freeidx = se->arrayused++;
308
0
    }
309
310
  /* Allocate new memory and return an error if that didn't worked.
311
     Allocating it first allows us to keep the old value; it doesn't
312
     matter that arrayused has already been incremented in case of a
313
     new entry - it will then pint to a NULL slot.  */
314
0
  var = xtrymalloc (sizeof *var + namelen + 1 + valuelen);
315
0
  if (!var)
316
0
    return gpg_error_from_syserror ();
317
0
  var->is_default = !!set_default;
318
0
  memcpy (var->name, string, namelen);
319
0
  var->name[namelen] = '\0';
320
0
  var->value = var->name + namelen + 1;
321
0
  strcpy (var->value, value);
322
323
0
  xfree (se->array[freeidx]);
324
0
  se->array[freeidx] = var;
325
0
  return 0;
326
0
}
327
328
329
/* Set or update an environment variable of the session environment.
330
   String is similar to the putval(3) function but it is reentrant and
331
   takes a copy.  In particular it exhibits this behaviour:
332
333
          <NAME>            Delete envvar NAME
334
          <KEY>=            Set envvar NAME to the empty string
335
          <KEY>=<VALUE>     Set envvar NAME to VALUE
336
337
   On success 0 is returned; on error an gpg-error code.  */
338
gpg_error_t
339
session_env_putenv (session_env_t se, const char *string)
340
0
{
341
0
  const char *s;
342
343
0
  if (!string || !*string)
344
0
    return gpg_error (GPG_ERR_INV_VALUE);
345
0
  s = strchr (string, '=');
346
0
  if (s == string)
347
0
    return gpg_error (GPG_ERR_INV_VALUE);
348
0
  if (!s)
349
0
    return delete_var (se, string);
350
0
  else
351
0
    return update_var (se, string, s - string, NULL, 0);
352
0
}
353
354
355
/* Same as session_env_putenv but with name and value given as distinct
356
   values.  */
357
gpg_error_t
358
session_env_setenv (session_env_t se, const char *name, const char *value)
359
0
{
360
0
  if (!name || !*name)
361
0
    return gpg_error (GPG_ERR_INV_VALUE);
362
0
  if (!value)
363
0
    return delete_var (se, name);
364
0
  else
365
0
    return update_var (se, name, strlen (name), value, 0);
366
0
}
367
368
369
370
371
/* Return the value of the environment variable NAME from the SE
372
   object.  If the variable does not exist, NULL is returned.  The
373
   returned value is valid as long as SE is valid and as long it has
374
   not been removed or updated by a call to session_env_putenv.  The
375
   caller MUST not change the returned value. */
376
char *
377
session_env_getenv (session_env_t se, const char *name)
378
0
{
379
0
  int idx;
380
381
0
  if (!se || !name || !*name)
382
0
    return NULL;
383
384
0
  for (idx=0; idx < se->arrayused; idx++)
385
0
    if (se->array[idx] && !strcmp (se->array[idx]->name, name))
386
0
      return se->array[idx]->is_default? NULL : se->array[idx]->value;
387
0
  return NULL;
388
0
}
389
390
391
/* Return the value of the environment variable NAME from the SE
392
   object.  The returned value is valid as long as SE is valid and as
393
   long it has not been removed or updated by a call to
394
   session_env_putenv.  If the variable does not exist, the function
395
   tries to return the value through a call to getenv; if that returns
396
   a value, this value is recorded and used.  If no value could be
397
   found, returns NULL.  The caller must not change the returned
398
   value. */
399
char *
400
session_env_getenv_or_default (session_env_t se, const char *name,
401
                               int *r_default)
402
0
{
403
0
  int idx;
404
0
  char *defvalue;
405
406
0
  if (r_default)
407
0
    *r_default = 0;
408
0
  if (!se || !name || !*name)
409
0
    return NULL;
410
411
0
  for (idx=0; idx < se->arrayused; idx++)
412
0
    if (se->array[idx] && !strcmp (se->array[idx]->name, name))
413
0
      {
414
0
        if (r_default && se->array[idx]->is_default)
415
0
          *r_default = 1;
416
0
        return se->array[idx]->value;
417
0
      }
418
419
  /* Get the default value with an additional fallback for GPG_TTY.  */
420
0
  defvalue = getenv (name);
421
0
  if ((!defvalue || !*defvalue) && !strcmp (name, "GPG_TTY")
422
0
      && gnupg_ttyname (0))
423
0
    {
424
0
      defvalue = gnupg_ttyname (0);
425
0
    }
426
0
  if (defvalue)
427
0
    {
428
      /* Record the default value for later use so that we are safe
429
         from later modifications of the environment.  We need to take
430
         a copy to better cope with the rules of putenv(3).  We ignore
431
         the error of the update function because we can't return an
432
         explicit error anyway and the following scan would then fail
433
         anyway. */
434
0
      update_var (se, name, strlen (name), defvalue, 1);
435
436
0
      for (idx=0; idx < se->arrayused; idx++)
437
0
        if (se->array[idx] && !strcmp (se->array[idx]->name, name))
438
0
          {
439
0
            if (r_default && se->array[idx]->is_default)
440
0
              *r_default = 1;
441
0
            return se->array[idx]->value;
442
0
          }
443
0
    }
444
445
0
  return NULL;
446
0
}
447
448
449
/* List the entire environment stored in SE.  The caller initially
450
   needs to set the value of ITERATOR to 0 and then call this function
451
   until it returns NULL.  The value is returned at R_VALUE.  If
452
   R_DEFAULT is not NULL, the default flag is stored on return.  The
453
   default flag indicates that the value has been taken from the
454
   process's environment.  The caller must not change the returned
455
   name or value.  */
456
char *
457
session_env_listenv (session_env_t se, int *iterator,
458
                     const char **r_value, int *r_default)
459
0
{
460
0
  int idx = *iterator;
461
462
0
  if (!se || idx < 0)
463
0
    return NULL;
464
465
0
  for (; idx < se->arrayused; idx++)
466
0
    if (se->array[idx])
467
0
      {
468
0
        *iterator = idx+1;
469
0
        if (r_default)
470
0
          *r_default = se->array[idx]->is_default;
471
0
        if (r_value)
472
0
          *r_value = se->array[idx]->value;
473
0
        return se->array[idx]->name;
474
0
      }
475
0
  return NULL;
476
0
}