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