Coverage Report

Created: 2026-02-26 06:52

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gnupg/kbx/keybox-init.c
Line
Count
Source
1
/* keybox-init.c - Initialization of the library
2
 *  Copyright (C) 2001 Free Software Foundation, Inc.
3
 *
4
 * This file is part of GnuPG.
5
 *
6
 * GnuPG is free software; you can redistribute it and/or modify
7
 * it under the terms of the GNU General Public License as published by
8
 * the Free Software Foundation; either version 3 of the License, or
9
 * (at your option) any later version.
10
 *
11
 * GnuPG is distributed in the hope that it will be useful,
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
 * GNU General Public License for more details.
15
 *
16
 * You should have received a copy of the GNU General Public License
17
 * along with this program; if not, see <https://www.gnu.org/licenses/>.
18
 */
19
20
#include <config.h>
21
#include <stdlib.h>
22
#include <stdio.h>
23
#include <string.h>
24
#include <unistd.h>
25
#include <assert.h>
26
27
#include "keybox-defs.h"
28
#include "../common/sysutils.h"
29
#include "../common/mischelp.h"
30
31
#ifdef HAVE_W32_SYSTEM
32
# define DEFAULT_LL_BUFFER_SIZE 128
33
#else
34
# define DEFAULT_LL_BUFFER_SIZE 64
35
#endif
36
37
static unsigned int ll_buffer_size = DEFAULT_LL_BUFFER_SIZE;
38
39
static KB_NAME kb_names;
40
41
/* This object is used to mahe setvbuf buffers.  We use a short arary
42
 * to be able to reuse already allocated buffers.  */
43
struct stream_buffer_s
44
{
45
  int inuse;       /* True if used by a stream. */
46
  size_t bufsize;
47
  char *buf;
48
};
49
static struct stream_buffer_s stream_buffers[5];
50
51
52
/* Register a filename for plain keybox files.  Returns 0 on success,
53
 * GPG_ERR_EEXIST if it has already been registered, or another error
54
 * code.  On success or with error code GPG_ERR_EEXIST a token usable
55
 * to access the keybox handle is stored at R_TOKEN, NULL is stored
56
 * for all other errors.  */
57
gpg_error_t
58
keybox_register_file (const char *fname, int secret, void **r_token)
59
1
{
60
1
  KB_NAME kr;
61
62
1
  *r_token = NULL;
63
64
1
  for (kr=kb_names; kr; kr = kr->next)
65
0
    {
66
0
      if (same_file_p (kr->fname, fname) )
67
0
        {
68
0
          *r_token = kr;
69
0
          return gpg_error (GPG_ERR_EEXIST); /* Already registered. */
70
0
        }
71
0
    }
72
73
1
  kr = xtrymalloc (sizeof *kr + strlen (fname));
74
1
  if (!kr)
75
0
    return gpg_error_from_syserror ();
76
1
  strcpy (kr->fname, fname);
77
1
  kr->secret = !!secret;
78
79
1
  kr->handle_table = NULL;
80
1
  kr->handle_table_size = 0;
81
82
1
  kr->lockhd = NULL;
83
1
  kr->is_locked = 0;
84
1
  kr->did_full_scan = 0;
85
  /* keep a list of all issued pointers */
86
1
  kr->next = kb_names;
87
1
  kb_names = kr;
88
89
  /* create the offset table the first time a function here is used */
90
/*      if (!kb_offtbl) */
91
/*        kb_offtbl = new_offset_hash_table (); */
92
93
1
  *r_token = kr;
94
1
  return 0;
95
1
}
96
97
int
98
keybox_is_writable (void *token)
99
2
{
100
2
  KB_NAME r = token;
101
102
2
  return r? !gnupg_access (r->fname, W_OK) : 0;
103
2
}
104
105
106
/* Change the default buffering to KBYTES KiB; using 0 uses the system
107
 * buffers.  This function must be called early.  */
108
void
109
keybox_set_buffersize (unsigned int kbytes, int reserved)
110
0
{
111
0
  (void)reserved;
112
  /* Round down to 8k multiples.  */
113
0
  ll_buffer_size = (kbytes + 7)/8 * 8;
114
0
}
115
116
117
static KEYBOX_HANDLE
118
do_keybox_new (KB_NAME resource, int secret, int for_openpgp)
119
4.03k
{
120
4.03k
  KEYBOX_HANDLE hd;
121
4.03k
  int idx;
122
123
4.03k
  assert (resource && !resource->secret == !secret);
124
4.03k
  hd = xtrycalloc (1, sizeof *hd);
125
4.03k
  if (hd)
126
4.03k
    {
127
4.03k
      hd->kb = resource;
128
4.03k
      hd->secret = !!secret;
129
4.03k
      hd->for_openpgp = for_openpgp;
130
4.03k
      if (!resource->handle_table)
131
1
        {
132
1
          resource->handle_table_size = 3;
133
1
          resource->handle_table = xtrycalloc (resource->handle_table_size,
134
1
                                               sizeof *resource->handle_table);
135
1
          if (!resource->handle_table)
136
0
            {
137
0
              resource->handle_table_size = 0;
138
0
              xfree (hd);
139
0
              return NULL;
140
0
            }
141
1
        }
142
6.85k
      for (idx=0; idx < resource->handle_table_size; idx++)
143
6.85k
        if (!resource->handle_table[idx])
144
4.03k
          {
145
4.03k
            resource->handle_table[idx] = hd;
146
4.03k
            break;
147
4.03k
          }
148
4.03k
      if (!(idx < resource->handle_table_size))
149
0
        {
150
0
          KEYBOX_HANDLE *tmptbl;
151
0
          size_t newsize;
152
153
0
          newsize = resource->handle_table_size + 5;
154
0
          tmptbl = xtryrealloc (resource->handle_table,
155
0
                                newsize * sizeof (*tmptbl));
156
0
          if (!tmptbl)
157
0
            {
158
0
              xfree (hd);
159
0
              return NULL;
160
0
            }
161
0
          resource->handle_table = tmptbl;
162
0
          resource->handle_table_size = newsize;
163
0
          resource->handle_table[idx] = hd;
164
0
          for (idx++; idx < resource->handle_table_size; idx++)
165
0
            resource->handle_table[idx] = NULL;
166
0
        }
167
4.03k
    }
168
4.03k
  return hd;
169
4.03k
}
170
171
172
/* Create a new handle for the resource associated with TOKEN.  SECRET
173
   is just a cross-check.  This is the OpenPGP version.  The returned
174
   handle must be released using keybox_release.  */
175
KEYBOX_HANDLE
176
keybox_new_openpgp (void *token, int secret)
177
4.03k
{
178
4.03k
  KB_NAME resource = token;
179
180
4.03k
  return do_keybox_new (resource, secret, 1);
181
4.03k
}
182
183
/* Create a new handle for the resource associated with TOKEN.  SECRET
184
   is just a cross-check.  This is the X.509 version.  The returned
185
   handle must be released using keybox_release.  */
186
KEYBOX_HANDLE
187
keybox_new_x509 (void *token, int secret)
188
0
{
189
0
  KB_NAME resource = token;
190
191
0
  return do_keybox_new (resource, secret, 0);
192
0
}
193
194
195
void
196
keybox_fp_close (KEYBOX_HANDLE hd)
197
4.03k
{
198
4.03k
  if (!hd)
199
0
    return;
200
4.03k
  if (hd->fp)
201
2.98k
    {
202
2.98k
      _keybox_ll_close (hd->fp);
203
2.98k
      hd->fp = NULL;
204
2.98k
    }
205
4.03k
}
206
207
void
208
keybox_release (KEYBOX_HANDLE hd)
209
4.03k
{
210
4.03k
  if (!hd)
211
0
    return;
212
4.03k
  if (hd->kb->handle_table)
213
4.03k
    {
214
4.03k
      int idx;
215
16.1k
      for (idx=0; idx < hd->kb->handle_table_size; idx++)
216
12.1k
        if (hd->kb->handle_table[idx] == hd)
217
4.03k
          hd->kb->handle_table[idx] = NULL;
218
4.03k
    }
219
4.03k
  _keybox_release_blob (hd->found.blob);
220
4.03k
  _keybox_release_blob (hd->saved_found.blob);
221
4.03k
  xfree (hd->word_match.name);
222
4.03k
  xfree (hd->word_match.pattern);
223
4.03k
  xfree (hd);
224
4.03k
}
225
226
227
/* Save the current found state in HD for later retrieval by
228
   keybox_restore_found_state.  Only one state may be saved.  */
229
void
230
keybox_push_found_state (KEYBOX_HANDLE hd)
231
0
{
232
0
  if (hd->saved_found.blob)
233
0
    {
234
0
      _keybox_release_blob (hd->saved_found.blob);
235
0
      hd->saved_found.blob = NULL;
236
0
    }
237
0
  hd->saved_found = hd->found;
238
0
  hd->found.blob = NULL;
239
0
}
240
241
242
/* Restore the saved found state in HD.  */
243
void
244
keybox_pop_found_state (KEYBOX_HANDLE hd)
245
0
{
246
0
  if (hd->found.blob)
247
0
    {
248
0
      _keybox_release_blob (hd->found.blob);
249
0
      hd->found.blob = NULL;
250
0
    }
251
0
  hd->found = hd->saved_found;
252
0
  hd->saved_found.blob = NULL;
253
0
}
254
255
256
const char *
257
keybox_get_resource_name (KEYBOX_HANDLE hd)
258
0
{
259
0
  if (!hd || !hd->kb)
260
0
    return NULL;
261
0
  return hd->kb->fname;
262
0
}
263
264
int
265
keybox_set_ephemeral (KEYBOX_HANDLE hd, int yes)
266
0
{
267
0
  if (!hd)
268
0
    return gpg_error (GPG_ERR_INV_HANDLE);
269
0
  hd->ephemeral = yes;
270
0
  return 0;
271
0
}
272
273
274
/* Low-level open function to be used for keybox files.  This function
275
 * also manages custom buffering.  On success 0 is returned and a new
276
 * file pointer stored at RFP; on error an error code is returned and
277
 * NULL is stored at RFP.  MODE is one of
278
 *   KEYBOX_LL_OPEN_READ(0) := fopen mode is "rb"
279
 *   KEYBOX_LL_OPEN_UPDATE  := fopen mode is "r+b"
280
 *   KEYBOX_LL_OPEN_CREATE  := fopen mode is "wb"
281
 */
282
gpg_error_t
283
_keybox_ll_open (estream_t *rfp, const char *fname, unsigned int mode)
284
2.98k
{
285
2.98k
  estream_t fp;
286
2.98k
  int i;
287
2.98k
  size_t bufsize;
288
289
2.98k
  *rfp = NULL;
290
291
2.98k
  fp = es_fopen (fname,
292
2.98k
                 mode == KEYBOX_LL_OPEN_CREATE
293
2.98k
                 ? "wb,sysopen,sequential,share=rw" :
294
2.98k
                 mode == KEYBOX_LL_OPEN_UPDATE
295
2.98k
                 ? "r+b,sysopen,sequential,share=rw" :
296
2.98k
                 "rb,sysopen,sequential,share=rw");
297
2.98k
  if (!fp)
298
0
    return gpg_error_from_syserror ();
299
300
2.98k
  if (ll_buffer_size)
301
2.98k
    {
302
3.43k
      for (i=0; i < DIM (stream_buffers); i++)
303
3.43k
        if (!stream_buffers[i].inuse)
304
2.98k
          {
305
            /* There is a free slot - we can use a larger buffer.  */
306
2.98k
            stream_buffers[i].inuse = 1;
307
2.98k
            if (!stream_buffers[i].buf)
308
2
              {
309
2
                bufsize = ll_buffer_size * 1024;
310
2
                stream_buffers[i].buf = xtrymalloc (bufsize);
311
2
                if (stream_buffers[i].buf)
312
2
                  stream_buffers[i].bufsize = bufsize;
313
0
                else
314
0
                  {
315
0
                    log_info ("can't allocate a large buffer for a kbx file;"
316
0
                              " using default\n");
317
0
                    stream_buffers[i].inuse = 0;
318
0
                  }
319
2
              }
320
321
2.98k
            if (stream_buffers[i].buf)
322
2.98k
              {
323
2.98k
                es_setvbuf (fp, stream_buffers[i].buf, _IOFBF,
324
2.98k
                            stream_buffers[i].bufsize);
325
2.98k
                es_opaque_set (fp, stream_buffers + i);
326
2.98k
              }
327
2.98k
            break;
328
2.98k
          }
329
2.98k
    }
330
331
2.98k
  *rfp = fp;
332
2.98k
  return 0;
333
2.98k
}
334
335
336
/* Wrapper around es_fclose to be used for file opened with
337
 * _keybox_ll_open.  */
338
gpg_error_t
339
_keybox_ll_close (estream_t fp)
340
2.98k
{
341
2.98k
  gpg_error_t err;
342
2.98k
  struct stream_buffer_s *sbuf;
343
2.98k
  int i;
344
345
2.98k
  if (!fp)
346
0
    return 0;
347
348
2.98k
  sbuf = ll_buffer_size? es_opaque_get (fp) : NULL;
349
2.98k
  if (es_fclose (fp))
350
0
    err = gpg_error_from_syserror ();
351
2.98k
  else
352
2.98k
    err = 0;
353
2.98k
  if (sbuf)
354
2.98k
    {
355
3.43k
      for (i=0; i < DIM (stream_buffers); i++)
356
3.43k
        if (stream_buffers + i == sbuf)
357
2.98k
          break;
358
2.98k
      log_assert (i < DIM (stream_buffers));
359
2.98k
      stream_buffers[i].inuse = 0;
360
2.98k
    }
361
362
363
2.98k
  return err;
364
2.98k
}
365
366
367
368
/* Close the file of the resource identified by HD.  For consistent
369
   results this function closes the files of all handles pointing to
370
   the resource identified by HD.  */
371
void
372
_keybox_close_file (KEYBOX_HANDLE hd)
373
1
{
374
1
  int idx;
375
1
  KEYBOX_HANDLE roverhd;
376
377
1
  if (!hd || !hd->kb || !hd->kb->handle_table)
378
0
    return;
379
380
4
  for (idx=0; idx < hd->kb->handle_table_size; idx++)
381
3
    if ((roverhd = hd->kb->handle_table[idx]))
382
1
      {
383
1
        if (roverhd->fp)
384
0
          {
385
0
            _keybox_ll_close (roverhd->fp);
386
0
            roverhd->fp = NULL;
387
0
          }
388
1
      }
389
1
  log_assert (!hd->fp);
390
1
}
391
392
393
/*
394
 * Lock the keybox at handle HD, or unlock if YES is false.  TIMEOUT
395
 * is the value used for dotlock_take.  In general -1 should be used
396
 * when taking a lock; use 0 when releasing a lock.
397
 */
398
gpg_error_t
399
keybox_lock (KEYBOX_HANDLE hd, int yes, long timeout)
400
2
{
401
2
  gpg_error_t err = 0;
402
2
  KB_NAME kb = hd->kb;
403
404
2
  if (!keybox_is_writable (kb))
405
0
    return 0;
406
407
  /* Make sure the lock handle has been created.  */
408
2
  if (!kb->lockhd)
409
1
    {
410
1
      kb->lockhd = dotlock_create (kb->fname, 0);
411
1
      if (!kb->lockhd)
412
0
        {
413
0
          err = gpg_error_from_syserror ();
414
0
          log_info ("can't allocate lock for '%s'\n", kb->fname );
415
0
          return err;
416
0
        }
417
1
    }
418
419
2
  if (yes) /* Take the lock.  */
420
1
    {
421
1
      if (!kb->is_locked)
422
1
        {
423
#ifdef HAVE_W32_SYSTEM
424
          /* Under Windows we need to close the file before we try
425
           * to lock it.  This is because another process might have
426
           * taken the lock and is using keybox_file_rename to
427
           * rename the base file.  Now if our dotlock_take below is
428
           * waiting for the lock but we have the base file still
429
           * open, keybox_file_rename will never succeed as we are
430
           * in a deadlock.  */
431
          _keybox_close_file (hd);
432
#endif /*HAVE_W32_SYSTEM*/
433
1
          if (dotlock_take (kb->lockhd, timeout))
434
0
            {
435
0
              err = gpg_error_from_syserror ();
436
0
              if (!timeout && gpg_err_code (err) == GPG_ERR_EACCES)
437
0
                ; /* No diagnostic if we only tried to lock.  */
438
0
              else
439
0
                log_info ("can't lock '%s'\n", kb->fname );
440
0
            }
441
1
          else
442
1
            kb->is_locked = 1;
443
1
        }
444
1
    }
445
1
  else /* Release the lock.  */
446
1
    {
447
1
      if (kb->is_locked)
448
1
        {
449
1
          if (dotlock_release (kb->lockhd))
450
0
            {
451
0
              err = gpg_error_from_syserror ();
452
0
              log_info ("can't unlock '%s'\n", kb->fname );
453
0
            }
454
1
          else
455
1
            kb->is_locked = 0;
456
1
        }
457
1
   }
458
459
2
  return err;
460
2
}