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