/src/gnupg/g10/call-keyboxd.c
Line | Count | Source |
1 | | /* call-keyboxd.c - Access to the keyboxd storage server |
2 | | * Copyright (C) 2019, 2024 g10 Code GmbH |
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 | | * SPDX-License-Identifier: GPL-3.0-or-later |
19 | | */ |
20 | | |
21 | | #include <config.h> |
22 | | #include <stdio.h> |
23 | | #include <stdlib.h> |
24 | | #include <string.h> |
25 | | #include <errno.h> |
26 | | #include <unistd.h> |
27 | | #include <time.h> |
28 | | #ifdef HAVE_LOCALE_H |
29 | | # include <locale.h> |
30 | | #endif |
31 | | #include <npth.h> |
32 | | |
33 | | #include "gpg.h" |
34 | | #include <assuan.h> |
35 | | #include "../common/util.h" |
36 | | #include "../common/membuf.h" |
37 | | #include "options.h" |
38 | | #include "../common/i18n.h" |
39 | | #include "../common/asshelp.h" |
40 | | #include "../common/host2net.h" |
41 | | #include "../common/status.h" |
42 | | #include "../kbx/kbx-client-util.h" |
43 | | #include "keydb.h" |
44 | | |
45 | | #include "keydb-private.h" /* For struct keydb_handle_s */ |
46 | | |
47 | | |
48 | | /* Data used to keep track of keybox daemon sessions. This allows us |
49 | | * to use several sessions with the keyboxd and also to reuse already |
50 | | * established sessions. Note that gpg.h defines the type |
51 | | * keyboxd_local_t for this structure. */ |
52 | | struct keyboxd_local_s |
53 | | { |
54 | | /* Link to other keyboxd contexts which are used simultaneously. */ |
55 | | struct keyboxd_local_s *next; |
56 | | |
57 | | /* The active Assuan context. */ |
58 | | assuan_context_t ctx; |
59 | | |
60 | | /* The client data helper context. */ |
61 | | kbx_client_data_t kcd; |
62 | | |
63 | | /* I/O buffer with the last search result or NULL. Used if |
64 | | * D-lines are used to convey the keyblocks. */ |
65 | | iobuf_t search_result; |
66 | | |
67 | | /* This flag set while an operation is running on this context. */ |
68 | | unsigned int is_active : 1; |
69 | | |
70 | | /* Flag indicating that a search reset is required. */ |
71 | | unsigned int need_search_reset : 1; |
72 | | |
73 | | }; |
74 | | |
75 | | |
76 | | /* Flag indicating that for example bulk import is enabled. */ |
77 | | static unsigned int in_transaction; |
78 | | |
79 | | |
80 | | |
81 | | |
82 | | /* Deinitialize all session resources pertaining to the keyboxd. */ |
83 | | void |
84 | | gpg_keyboxd_deinit_session_data (ctrl_t ctrl) |
85 | 0 | { |
86 | 0 | keyboxd_local_t kbl; |
87 | 0 | gpg_error_t err; |
88 | |
|
89 | 0 | while ((kbl = ctrl->keyboxd_local)) |
90 | 0 | { |
91 | 0 | ctrl->keyboxd_local = kbl->next; |
92 | 0 | if (kbl->is_active) |
93 | 0 | log_error ("oops: trying to cleanup an active keyboxd context\n"); |
94 | 0 | else |
95 | 0 | { |
96 | 0 | if (kbl->ctx && in_transaction) |
97 | 0 | { |
98 | | /* This is our hack to commit the changes done during a |
99 | | * bulk import. If we won't do that the loss of the |
100 | | * connection would trigger a rollback in keyboxd. Note |
101 | | * that transactions are not associated with a |
102 | | * connection. */ |
103 | 0 | err = assuan_transact (kbl->ctx, "TRANSACTION commit", |
104 | 0 | NULL, NULL, NULL, NULL, NULL, NULL); |
105 | 0 | if (err) |
106 | 0 | log_error ("error committing last transaction: %s\n", |
107 | 0 | gpg_strerror (err)); |
108 | 0 | in_transaction = 0; |
109 | 0 | } |
110 | 0 | assuan_release (kbl->ctx); |
111 | 0 | kbl->ctx = NULL; |
112 | | /* |
113 | | * Since there may be pipe output FD sent to the server (so |
114 | | * that it can receive data through the pipe), we should |
115 | | * release the assuan connection before releasing KBL->KCD. |
116 | | * This way, the data receiving thread can finish cleanly, |
117 | | * and we can join the thread. |
118 | | */ |
119 | 0 | kbx_client_data_release (kbl->kcd); |
120 | 0 | kbl->kcd = NULL; |
121 | 0 | } |
122 | 0 | xfree (kbl); |
123 | 0 | } |
124 | 0 | } |
125 | | |
126 | | |
127 | | /* Print a warning if the server's version number is less than our |
128 | | version number. Returns an error code on a connection problem. */ |
129 | | static gpg_error_t |
130 | | warn_version_mismatch (assuan_context_t ctx, const char *servername) |
131 | 0 | { |
132 | 0 | return warn_server_version_mismatch (ctx, servername, 0, |
133 | 0 | write_status_strings2, NULL, |
134 | 0 | !opt.quiet); |
135 | 0 | } |
136 | | |
137 | | |
138 | | /* Connect to the keybox daemon and launch it if necessary. Handle |
139 | | * the server's initial greeting and set global options. Returns a |
140 | | * new assuan context or an error. */ |
141 | | static gpg_error_t |
142 | | create_new_context (ctrl_t ctrl, assuan_context_t *r_ctx) |
143 | 0 | { |
144 | 0 | gpg_error_t err; |
145 | 0 | assuan_context_t ctx; |
146 | |
|
147 | 0 | *r_ctx = NULL; |
148 | |
|
149 | 0 | err = start_new_keyboxd (&ctx, |
150 | 0 | GPG_ERR_SOURCE_DEFAULT, |
151 | 0 | opt.keyboxd_program, |
152 | 0 | opt.autostart?ASSHELP_FLAG_AUTOSTART:0, |
153 | 0 | opt.verbose, DBG_IPC, |
154 | 0 | NULL, ctrl); |
155 | 0 | if (!opt.autostart && gpg_err_code (err) == GPG_ERR_NO_KEYBOXD) |
156 | 0 | { |
157 | 0 | static int shown; |
158 | |
|
159 | 0 | if (!shown) |
160 | 0 | { |
161 | 0 | shown = 1; |
162 | 0 | log_info (_("no keyboxd running in this session\n")); |
163 | 0 | } |
164 | 0 | } |
165 | 0 | else if (!err && !(err = warn_version_mismatch (ctx, KEYBOXD_NAME))) |
166 | 0 | { |
167 | | /* Place to emit global options. */ |
168 | |
|
169 | 0 | if ((opt.import_options & IMPORT_BULK) && !in_transaction) |
170 | 0 | { |
171 | 0 | err = assuan_transact (ctx, "TRANSACTION begin", |
172 | 0 | NULL, NULL, NULL, NULL, NULL, NULL); |
173 | 0 | if (err) |
174 | 0 | { |
175 | 0 | log_error ("error enabling bulk import option: %s\n", |
176 | 0 | gpg_strerror (err)); |
177 | 0 | } |
178 | 0 | else |
179 | 0 | in_transaction = 1; |
180 | 0 | } |
181 | |
|
182 | 0 | } |
183 | |
|
184 | 0 | if (err) |
185 | 0 | assuan_release (ctx); |
186 | 0 | else |
187 | 0 | *r_ctx = ctx; |
188 | |
|
189 | 0 | return err; |
190 | 0 | } |
191 | | |
192 | | |
193 | | |
194 | | |
195 | | /* Get a context for accessing keyboxd. If no context is available a |
196 | | * new one is created and if necessary keyboxd is started. R_KBL |
197 | | * receives a pointer to the local context object. */ |
198 | | static gpg_error_t |
199 | | open_context (ctrl_t ctrl, keyboxd_local_t *r_kbl) |
200 | 0 | { |
201 | 0 | gpg_error_t err; |
202 | 0 | keyboxd_local_t kbl; |
203 | |
|
204 | 0 | *r_kbl = NULL; |
205 | 0 | for (;;) |
206 | 0 | { |
207 | 0 | for (kbl = ctrl->keyboxd_local; kbl && kbl->is_active; kbl = kbl->next) |
208 | 0 | ; |
209 | 0 | if (kbl) |
210 | 0 | { |
211 | | /* Found an inactive keyboxd session - return that. */ |
212 | 0 | log_assert (!kbl->is_active); |
213 | | |
214 | 0 | kbl->is_active = 1; |
215 | 0 | kbl->need_search_reset = 1; |
216 | |
|
217 | 0 | *r_kbl = kbl; |
218 | 0 | return 0; |
219 | 0 | } |
220 | | |
221 | | /* None found. Create a new session and retry. */ |
222 | 0 | kbl = xtrycalloc (1, sizeof *kbl); |
223 | 0 | if (!kbl) |
224 | 0 | return gpg_error_from_syserror (); |
225 | | |
226 | 0 | err = create_new_context (ctrl, &kbl->ctx); |
227 | 0 | if (err) |
228 | 0 | { |
229 | 0 | xfree (kbl); |
230 | 0 | return err; |
231 | 0 | } |
232 | | |
233 | 0 | err = kbx_client_data_new (&kbl->kcd, kbl->ctx, 0); |
234 | 0 | if (err) |
235 | 0 | { |
236 | 0 | assuan_release (kbl->ctx); |
237 | 0 | xfree (kbl); |
238 | 0 | return err; |
239 | 0 | } |
240 | | |
241 | | /* For thread-saftey we add it to the list and retry; this is |
242 | | * easier than to employ a lock. */ |
243 | 0 | kbl->next = ctrl->keyboxd_local; |
244 | 0 | ctrl->keyboxd_local = kbl; |
245 | 0 | } |
246 | | /*NOTREACHED*/ |
247 | 0 | } |
248 | | |
249 | | |
250 | | |
251 | | /* Create a new database handle. A database handle is similar to a |
252 | | * file handle: it contains a local file position. This is used when |
253 | | * searching: subsequent searches resume where the previous search |
254 | | * left off. To rewind the position, use keydb_search_reset(). This |
255 | | * function returns NULL on error, sets ERRNO, and prints an error |
256 | | * diagnostic. Depending on --use-keyboxd either the old internal |
257 | | * keydb code is used (keydb.c) or, if set, the processing is diverted |
258 | | * to the keyboxd. */ |
259 | | /* FIXME: We should change the interface to return a gpg_error_t. */ |
260 | | KEYDB_HANDLE |
261 | | keydb_new (ctrl_t ctrl) |
262 | 1.63k | { |
263 | 1.63k | gpg_error_t err; |
264 | 1.63k | KEYDB_HANDLE hd; |
265 | | |
266 | 1.63k | if (DBG_CLOCK) |
267 | 1.63k | log_clock ("keydb_new"); |
268 | | |
269 | 1.63k | hd = xtrycalloc (1, sizeof *hd); |
270 | 1.63k | if (!hd) |
271 | 0 | { |
272 | 0 | err = gpg_error_from_syserror (); |
273 | 0 | goto leave; |
274 | 0 | } |
275 | | |
276 | 1.63k | if (!opt.use_keyboxd) |
277 | 1.63k | { |
278 | 1.63k | err = internal_keydb_init (hd); |
279 | 1.63k | goto leave; |
280 | 1.63k | } |
281 | 0 | hd->use_keyboxd = 1; |
282 | 0 | hd->ctrl = ctrl; |
283 | |
|
284 | 0 | err = open_context (ctrl, &hd->kbl); |
285 | |
|
286 | 1.63k | leave: |
287 | 1.63k | if (err) |
288 | 0 | { |
289 | 0 | int rc; |
290 | 0 | log_error (_("error opening key DB: %s\n"), gpg_strerror (err)); |
291 | 0 | xfree (hd); |
292 | 0 | hd = NULL; |
293 | 0 | if (!(rc = gpg_err_code_to_errno (err))) |
294 | 0 | rc = gpg_err_code_to_errno (GPG_ERR_EIO); |
295 | 0 | gpg_err_set_errno (rc); |
296 | 0 | } |
297 | 1.63k | return hd; |
298 | 0 | } |
299 | | |
300 | | |
301 | | /* Release a keydb handle. */ |
302 | | void |
303 | | keydb_release (KEYDB_HANDLE hd) |
304 | 13.1k | { |
305 | 13.1k | keyboxd_local_t kbl; |
306 | | |
307 | 13.1k | if (!hd) |
308 | 11.5k | return; |
309 | | |
310 | 1.63k | if (DBG_CLOCK) |
311 | 1.63k | log_clock ("keydb_release"); |
312 | 1.63k | if (!hd->use_keyboxd) |
313 | 1.63k | internal_keydb_deinit (hd); |
314 | 0 | else |
315 | 0 | { |
316 | 0 | kbl = hd->kbl; |
317 | 0 | if (DBG_CLOCK) |
318 | 0 | log_clock ("close_context (found)"); |
319 | 0 | if (!kbl->is_active) |
320 | 0 | log_fatal ("closing inactive keyboxd context %p\n", kbl); |
321 | 0 | kbl->is_active = 0; |
322 | 0 | hd->kbl = NULL; |
323 | 0 | hd->ctrl = NULL; |
324 | 0 | } |
325 | 1.63k | xfree (hd); |
326 | 1.63k | } |
327 | | |
328 | | |
329 | | /* Return the keyblock last found by keydb_search() in *RET_KB. |
330 | | * |
331 | | * On success, the function returns 0 and the caller must free *RET_KB |
332 | | * using release_kbnode(). Otherwise, the function returns an error |
333 | | * code. |
334 | | * |
335 | | * The returned keyblock has the kbnode flag bit 0 set for the node |
336 | | * with the public key used to locate the keyblock or flag bit 1 set |
337 | | * for the user ID node. */ |
338 | | gpg_error_t |
339 | | keydb_get_keyblock (KEYDB_HANDLE hd, kbnode_t *ret_kb) |
340 | 0 | { |
341 | 0 | gpg_error_t err; |
342 | |
|
343 | 0 | *ret_kb = NULL; |
344 | |
|
345 | 0 | if (!hd) |
346 | 0 | return gpg_error (GPG_ERR_INV_ARG); |
347 | | |
348 | 0 | if (DBG_CLOCK) |
349 | 0 | log_clock ("%s enter", __func__); |
350 | |
|
351 | 0 | if (!hd->use_keyboxd) |
352 | 0 | { |
353 | 0 | err = internal_keydb_get_keyblock (hd, ret_kb); |
354 | 0 | goto leave; |
355 | 0 | } |
356 | | |
357 | 0 | if (hd->kbl->search_result) |
358 | 0 | { |
359 | 0 | err = keydb_parse_keyblock (hd->kbl->search_result, |
360 | 0 | hd->last_ubid_valid? hd->last_pk_no : 0, |
361 | 0 | hd->last_ubid_valid? hd->last_uid_no : 0, |
362 | 0 | ret_kb); |
363 | | /* In contrast to the old code we close the iobuf here and thus |
364 | | * this function may be called only once to get a keyblock. */ |
365 | 0 | iobuf_close (hd->kbl->search_result); |
366 | 0 | hd->kbl->search_result = NULL; |
367 | 0 | } |
368 | 0 | else |
369 | 0 | { |
370 | 0 | err = gpg_error (GPG_ERR_VALUE_NOT_FOUND); |
371 | 0 | goto leave; |
372 | 0 | } |
373 | | |
374 | 0 | leave: |
375 | 0 | if (DBG_CLOCK) |
376 | 0 | log_clock ("%s leave%s", __func__, err? " (failed)":""); |
377 | 0 | return err; |
378 | 0 | } |
379 | | |
380 | | |
381 | | /* Default status callback used to show diagnostics from the keyboxd */ |
382 | | static gpg_error_t |
383 | | keydb_default_status_cb (void *opaque, const char *line) |
384 | 0 | { |
385 | 0 | const char *s; |
386 | |
|
387 | 0 | (void)opaque; |
388 | |
|
389 | 0 | if ((s = has_leading_keyword (line, "NOTE"))) |
390 | 0 | log_info (_("Note: %s\n"), s); |
391 | 0 | else if ((s = has_leading_keyword (line, "WARNING"))) |
392 | 0 | log_info (_("WARNING: %s\n"), s); |
393 | |
|
394 | 0 | return 0; |
395 | 0 | } |
396 | | |
397 | | |
398 | | |
399 | | /* Communication object for STORE commands. */ |
400 | | struct store_parm_s |
401 | | { |
402 | | assuan_context_t ctx; |
403 | | const void *data; /* The key in OpenPGP binary format. */ |
404 | | size_t datalen; /* The length of DATA. */ |
405 | | }; |
406 | | |
407 | | |
408 | | /* Handle the inquiries from the STORE command. */ |
409 | | static gpg_error_t |
410 | | store_inq_cb (void *opaque, const char *line) |
411 | 0 | { |
412 | 0 | struct store_parm_s *parm = opaque; |
413 | 0 | gpg_error_t err = 0; |
414 | |
|
415 | 0 | if (has_leading_keyword (line, "BLOB")) |
416 | 0 | { |
417 | 0 | if (parm->data) |
418 | 0 | err = assuan_send_data (parm->ctx, parm->data, parm->datalen); |
419 | 0 | } |
420 | 0 | else |
421 | 0 | return gpg_error (GPG_ERR_ASS_UNKNOWN_INQUIRE); |
422 | | |
423 | 0 | return err; |
424 | 0 | } |
425 | | |
426 | | |
427 | | /* Update the keyblock KB (i.e., extract the fingerprint and find the |
428 | | * corresponding keyblock in the keyring). |
429 | | * |
430 | | * This doesn't do anything if --dry-run was specified. |
431 | | * |
432 | | * Returns 0 on success. Otherwise, it returns an error code. Note: |
433 | | * if there isn't a keyblock in the keyring corresponding to KB, then |
434 | | * this function returns GPG_ERR_VALUE_NOT_FOUND. |
435 | | * |
436 | | * This function selects the matching record and modifies the current |
437 | | * file position to point to the record just after the selected entry. |
438 | | * Thus, if you do a subsequent search using HD, you should first do a |
439 | | * keydb_search_reset. Further, if the selected record is important, |
440 | | * you should use keydb_push_found_state and keydb_pop_found_state to |
441 | | * save and restore it. */ |
442 | | gpg_error_t |
443 | | keydb_update_keyblock (ctrl_t ctrl, KEYDB_HANDLE hd, kbnode_t kb) |
444 | 0 | { |
445 | 0 | gpg_error_t err; |
446 | 0 | iobuf_t iobuf = NULL; |
447 | 0 | struct store_parm_s parm = {NULL}; |
448 | |
|
449 | 0 | log_assert (kb); |
450 | 0 | log_assert (kb->pkt->pkttype == PKT_PUBLIC_KEY); |
451 | | |
452 | 0 | if (!hd) |
453 | 0 | return gpg_error (GPG_ERR_INV_ARG); |
454 | | |
455 | 0 | if (!hd->use_keyboxd) |
456 | 0 | { |
457 | 0 | err = internal_keydb_update_keyblock (ctrl, hd, kb); |
458 | 0 | goto leave; |
459 | 0 | } |
460 | | |
461 | 0 | if (opt.dry_run) |
462 | 0 | { |
463 | 0 | err = 0; |
464 | 0 | goto leave; |
465 | 0 | } |
466 | | |
467 | 0 | err = build_keyblock_image (kb, &iobuf); |
468 | 0 | if (err) |
469 | 0 | goto leave; |
470 | | |
471 | 0 | parm.ctx = hd->kbl->ctx; |
472 | 0 | parm.data = iobuf_get_temp_buffer (iobuf); |
473 | 0 | parm.datalen = iobuf_get_temp_length (iobuf); |
474 | 0 | err = assuan_transact (hd->kbl->ctx, "STORE --update", |
475 | 0 | NULL, NULL, |
476 | 0 | store_inq_cb, &parm, |
477 | 0 | keydb_default_status_cb, hd); |
478 | | |
479 | |
|
480 | 0 | leave: |
481 | 0 | iobuf_close (iobuf); |
482 | 0 | return err; |
483 | 0 | } |
484 | | |
485 | | |
486 | | /* Insert a keyblock into one of the underlying keyrings or keyboxes. |
487 | | * |
488 | | * By default, the keyring / keybox from which the last search result |
489 | | * came is used. If there was no previous search result (or |
490 | | * keydb_search_reset was called), then the keyring / keybox where the |
491 | | * next search would start is used (i.e., the current file position). |
492 | | * In keyboxd mode the keyboxd decides where to store it. |
493 | | * |
494 | | * Note: this doesn't do anything if --dry-run was specified. |
495 | | * |
496 | | * Returns 0 on success. Otherwise, it returns an error code. */ |
497 | | gpg_error_t |
498 | | keydb_insert_keyblock (KEYDB_HANDLE hd, kbnode_t kb) |
499 | 0 | { |
500 | 0 | gpg_error_t err; |
501 | 0 | iobuf_t iobuf = NULL; |
502 | 0 | struct store_parm_s parm = {NULL}; |
503 | |
|
504 | 0 | if (!hd) |
505 | 0 | return gpg_error (GPG_ERR_INV_ARG); |
506 | | |
507 | 0 | if (!hd->use_keyboxd) |
508 | 0 | { |
509 | 0 | err = internal_keydb_insert_keyblock (hd, kb); |
510 | 0 | goto leave; |
511 | 0 | } |
512 | | |
513 | 0 | if (opt.dry_run) |
514 | 0 | { |
515 | 0 | err = 0; |
516 | 0 | goto leave; |
517 | 0 | } |
518 | | |
519 | 0 | err = build_keyblock_image (kb, &iobuf); |
520 | 0 | if (err) |
521 | 0 | goto leave; |
522 | | |
523 | 0 | parm.ctx = hd->kbl->ctx; |
524 | 0 | parm.data = iobuf_get_temp_buffer (iobuf); |
525 | 0 | parm.datalen = iobuf_get_temp_length (iobuf); |
526 | 0 | err = assuan_transact (hd->kbl->ctx, "STORE --insert", |
527 | 0 | NULL, NULL, |
528 | 0 | store_inq_cb, &parm, |
529 | 0 | keydb_default_status_cb, hd); |
530 | |
|
531 | 0 | leave: |
532 | 0 | iobuf_close (iobuf); |
533 | 0 | return err; |
534 | 0 | } |
535 | | |
536 | | |
537 | | /* Delete the currently selected keyblock. If you haven't done a |
538 | | * search yet on this database handle (or called keydb_search_reset), |
539 | | * then this function returns an error. |
540 | | * |
541 | | * Returns 0 on success or an error code, if an error occurred. */ |
542 | | gpg_error_t |
543 | | keydb_delete_keyblock (KEYDB_HANDLE hd) |
544 | 0 | { |
545 | 0 | gpg_error_t err; |
546 | 0 | unsigned char hexubid[UBID_LEN * 2 + 1]; |
547 | 0 | char line[ASSUAN_LINELENGTH]; |
548 | |
|
549 | 0 | if (!hd) |
550 | 0 | return gpg_error (GPG_ERR_INV_ARG); |
551 | | |
552 | 0 | if (!hd->use_keyboxd) |
553 | 0 | { |
554 | 0 | err = internal_keydb_delete_keyblock (hd); |
555 | 0 | goto leave; |
556 | 0 | } |
557 | | |
558 | 0 | if (opt.dry_run) |
559 | 0 | { |
560 | 0 | err = 0; |
561 | 0 | goto leave; |
562 | 0 | } |
563 | | |
564 | 0 | if (!hd->last_ubid_valid) |
565 | 0 | { |
566 | 0 | err = gpg_error (GPG_ERR_VALUE_NOT_FOUND); |
567 | 0 | goto leave; |
568 | 0 | } |
569 | | |
570 | 0 | bin2hex (hd->last_ubid, UBID_LEN, hexubid); |
571 | 0 | snprintf (line, sizeof line, "DELETE %s", hexubid); |
572 | 0 | err = assuan_transact (hd->kbl->ctx, line, |
573 | 0 | NULL, NULL, |
574 | 0 | NULL, NULL, |
575 | 0 | keydb_default_status_cb, hd); |
576 | |
|
577 | 0 | leave: |
578 | 0 | return err; |
579 | 0 | } |
580 | | |
581 | | |
582 | | /* Clears the current search result and resets the handle's position |
583 | | * so that the next search starts at the beginning of the database. |
584 | | * |
585 | | * Returns 0 on success and an error code if an error occurred. */ |
586 | | gpg_error_t |
587 | | keydb_search_reset (KEYDB_HANDLE hd) |
588 | 6.47k | { |
589 | 6.47k | gpg_error_t err; |
590 | | |
591 | 6.47k | if (!hd) |
592 | 0 | return gpg_error (GPG_ERR_INV_ARG); |
593 | | |
594 | 6.47k | if (DBG_CLOCK) |
595 | 6.47k | log_clock ("%s", __func__); |
596 | 6.47k | if (DBG_CACHE) |
597 | 6.47k | log_debug ("%s (hd=%p)", __func__, hd); |
598 | | |
599 | 6.47k | if (!hd->use_keyboxd) |
600 | 6.47k | { |
601 | 6.47k | err = internal_keydb_search_reset (hd); |
602 | 6.47k | goto leave; |
603 | 6.47k | } |
604 | | |
605 | | /* All we need is to tell search that a reset is pending. Note that |
606 | | * keydb_new sets this flag as well. To comply with the |
607 | | * specification of keydb_delete_keyblock we also need to clear the |
608 | | * ubid flag so that after a reset a delete can't be performed. */ |
609 | 0 | hd->kbl->need_search_reset = 1; |
610 | 0 | hd->last_ubid_valid = 0; |
611 | 0 | err = 0; |
612 | |
|
613 | 6.47k | leave: |
614 | 6.47k | return err; |
615 | 0 | } |
616 | | |
617 | | |
618 | | |
619 | | /* Status callback for SEARCH and NEXT operations. */ |
620 | | static gpg_error_t |
621 | | search_status_cb (void *opaque, const char *line) |
622 | 0 | { |
623 | 0 | KEYDB_HANDLE hd = opaque; |
624 | 0 | gpg_error_t err = 0; |
625 | 0 | const char *s; |
626 | 0 | unsigned int n; |
627 | |
|
628 | 0 | if ((s = has_leading_keyword (line, "PUBKEY_INFO"))) |
629 | 0 | { |
630 | 0 | if (atoi (s) != PUBKEY_TYPE_OPGP) |
631 | 0 | err = gpg_error (GPG_ERR_WRONG_BLOB_TYPE); |
632 | 0 | else |
633 | 0 | { |
634 | 0 | hd->last_ubid_valid = 0; |
635 | 0 | while (*s && !spacep (s)) |
636 | 0 | s++; |
637 | 0 | if (!(n=hex2fixedbuf (s, hd->last_ubid, sizeof hd->last_ubid))) |
638 | 0 | err = gpg_error (GPG_ERR_INV_VALUE); |
639 | 0 | else |
640 | 0 | { |
641 | 0 | hd->last_ubid_valid = 1; |
642 | 0 | hd->last_uid_no = 0; |
643 | 0 | hd->last_pk_no = 0; |
644 | 0 | s += n; |
645 | 0 | while (*s && !spacep (s)) |
646 | 0 | s++; |
647 | 0 | while (spacep (s)) |
648 | 0 | s++; |
649 | 0 | if (*s) |
650 | 0 | { |
651 | 0 | hd->last_uid_no = atoi (s); |
652 | 0 | while (*s && !spacep (s)) |
653 | 0 | s++; |
654 | 0 | while (spacep (s)) |
655 | 0 | s++; |
656 | 0 | if (*s) |
657 | 0 | { |
658 | 0 | hd->last_pk_no = atoi (s); |
659 | | /* Keyboxd returns 0 for invalid but also for |
660 | | * the primary key and 1 for the first subkey. |
661 | | * That does not match our use and thus we |
662 | | * increment it despite that this maps an |
663 | | * invalid index to the primary key. */ |
664 | 0 | if (hd->last_pk_no >= 0) |
665 | 0 | hd->last_pk_no++; |
666 | 0 | } |
667 | |
|
668 | 0 | } |
669 | 0 | } |
670 | 0 | } |
671 | 0 | } |
672 | 0 | else |
673 | 0 | err = keydb_default_status_cb (opaque, line); |
674 | |
|
675 | 0 | return err; |
676 | 0 | } |
677 | | |
678 | | |
679 | | /* Search the database for keys matching the search description. If |
680 | | * the DB contains any legacy keys, these are silently ignored. |
681 | | * |
682 | | * DESC is an array of search terms with NDESC entries. The search |
683 | | * terms are or'd together. That is, the next entry in the DB that |
684 | | * matches any of the descriptions will be returned. |
685 | | * |
686 | | * Note: this function resumes searching where the last search left |
687 | | * off (i.e., at the current file position). If you want to search |
688 | | * from the start of the database, then you need to first call |
689 | | * keydb_search_reset(). |
690 | | * |
691 | | * If no key matches the search description, returns |
692 | | * GPG_ERR_NOT_FOUND. If there was a match, returns 0. If an error |
693 | | * occurred, returns an error code. |
694 | | * |
695 | | * The returned key is considered to be selected and the raw data can, |
696 | | * for instance, be returned by calling keydb_get_keyblock(). */ |
697 | | gpg_error_t |
698 | | keydb_search (KEYDB_HANDLE hd, KEYDB_SEARCH_DESC *desc, |
699 | | size_t ndesc, size_t *descindex) |
700 | 8.11k | { |
701 | 8.11k | gpg_error_t err; |
702 | 8.11k | int i; |
703 | 8.11k | char line[ASSUAN_LINELENGTH]; |
704 | 8.11k | char *buffer; |
705 | 8.11k | size_t len; |
706 | | |
707 | 8.11k | if (!hd) |
708 | 0 | return gpg_error (GPG_ERR_INV_ARG); |
709 | | |
710 | 8.11k | if (descindex) |
711 | 0 | *descindex = 0; /* Make sure it is always set on return. */ |
712 | | |
713 | 8.11k | if (DBG_CLOCK) |
714 | 8.11k | log_clock ("%s enter", __func__); |
715 | | |
716 | 8.11k | if (DBG_KEYDB) |
717 | 0 | { |
718 | 0 | log_debug ("%s: %zu search descriptions:\n", __func__, ndesc); |
719 | 0 | for (i = 0; i < ndesc; i ++) |
720 | 0 | { |
721 | 0 | char *t = keydb_search_desc_dump (&desc[i]); |
722 | 0 | log_debug ("%s %d: %s\n", __func__, i, t); |
723 | 0 | xfree (t); |
724 | 0 | } |
725 | 0 | } |
726 | | |
727 | 8.11k | if (!hd->use_keyboxd) |
728 | 8.11k | { |
729 | 8.11k | err = internal_keydb_search (hd, desc, ndesc, descindex); |
730 | 8.11k | goto leave; |
731 | 8.11k | } |
732 | | |
733 | | /* Clear the result objects. */ |
734 | 0 | if (hd->kbl->search_result) |
735 | 0 | { |
736 | 0 | iobuf_close (hd->kbl->search_result); |
737 | 0 | hd->kbl->search_result = NULL; |
738 | 0 | } |
739 | | |
740 | | /* Check whether this is a NEXT search. */ |
741 | 0 | if (!hd->kbl->need_search_reset) |
742 | 0 | { |
743 | | /* No reset requested thus continue the search. The keyboxd |
744 | | * keeps the context of the search and thus the NEXT operates on |
745 | | * the last search pattern. This is how we always used the |
746 | | * keydb.c functions. In theory we were able to modify the |
747 | | * search pattern between searches but that is not anymore |
748 | | * supported by keyboxd and a cursory check does not show that |
749 | | * we actually made used of that misfeature. */ |
750 | 0 | snprintf (line, sizeof line, "NEXT"); |
751 | 0 | goto do_search; |
752 | 0 | } |
753 | | |
754 | 0 | hd->kbl->need_search_reset = 0; |
755 | |
|
756 | 0 | if (!ndesc) |
757 | 0 | { |
758 | 0 | err = gpg_error (GPG_ERR_INV_ARG); |
759 | 0 | goto leave; |
760 | 0 | } |
761 | 0 | for (i = 0; i < ndesc; i++) |
762 | 0 | if (desc->mode == KEYDB_SEARCH_MODE_FIRST) |
763 | 0 | { |
764 | | /* If any description has mode FIRST, this item trumps all |
765 | | * other descriptions. */ |
766 | 0 | snprintf (line, sizeof line, "SEARCH --openpgp"); |
767 | 0 | goto do_search; |
768 | 0 | } |
769 | | |
770 | 0 | for ( ; ndesc; desc++, ndesc--) |
771 | 0 | { |
772 | 0 | const char *more = ndesc > 1 ? "--openpgp --more" : "--openpgp"; |
773 | |
|
774 | 0 | switch (desc->mode) |
775 | 0 | { |
776 | 0 | case KEYDB_SEARCH_MODE_EXACT: |
777 | 0 | snprintf (line, sizeof line, "SEARCH %s -- =%s", more, desc->u.name); |
778 | 0 | break; |
779 | | |
780 | 0 | case KEYDB_SEARCH_MODE_SUBSTR: |
781 | 0 | snprintf (line, sizeof line, "SEARCH %s -- *%s", more, desc->u.name); |
782 | 0 | break; |
783 | | |
784 | 0 | case KEYDB_SEARCH_MODE_MAIL: |
785 | 0 | snprintf (line, sizeof line, "SEARCH %s -- <%s", |
786 | 0 | more, desc->u.name+(desc->u.name[0] == '<') ); |
787 | 0 | break; |
788 | | |
789 | 0 | case KEYDB_SEARCH_MODE_MAILSUB: |
790 | 0 | snprintf (line, sizeof line, "SEARCH %s -- @%s", more, desc->u.name); |
791 | 0 | break; |
792 | | |
793 | 0 | case KEYDB_SEARCH_MODE_MAILEND: |
794 | 0 | snprintf (line, sizeof line, "SEARCH %s -- .%s", more, desc->u.name); |
795 | 0 | break; |
796 | | |
797 | 0 | case KEYDB_SEARCH_MODE_WORDS: |
798 | 0 | snprintf (line, sizeof line, "SEARCH %s -- +%s", more, desc->u.name); |
799 | 0 | break; |
800 | | |
801 | 0 | case KEYDB_SEARCH_MODE_SHORT_KID: |
802 | 0 | snprintf (line, sizeof line, "SEARCH %s -- 0x%08lX", more, |
803 | 0 | (ulong)desc->u.kid[1]); |
804 | 0 | break; |
805 | | |
806 | 0 | case KEYDB_SEARCH_MODE_LONG_KID: |
807 | 0 | snprintf (line, sizeof line, "SEARCH %s -- 0x%08lX%08lX", more, |
808 | 0 | (ulong)desc->u.kid[0], (ulong)desc->u.kid[1]); |
809 | 0 | break; |
810 | | |
811 | 0 | case KEYDB_SEARCH_MODE_FPR: |
812 | 0 | { |
813 | 0 | unsigned char hexfpr[MAX_FINGERPRINT_LEN * 2 + 1]; |
814 | 0 | log_assert (desc->fprlen <= MAX_FINGERPRINT_LEN); |
815 | 0 | bin2hex (desc->u.fpr, desc->fprlen, hexfpr); |
816 | 0 | snprintf (line, sizeof line, "SEARCH %s -- 0x%s", more, hexfpr); |
817 | 0 | } |
818 | 0 | break; |
819 | | |
820 | 0 | case KEYDB_SEARCH_MODE_ISSUER: |
821 | 0 | snprintf (line, sizeof line, "SEARCH %s -- #/%s", more, desc->u.name); |
822 | 0 | break; |
823 | | |
824 | 0 | case KEYDB_SEARCH_MODE_ISSUER_SN: |
825 | 0 | case KEYDB_SEARCH_MODE_SN: |
826 | 0 | snprintf (line, sizeof line, "SEARCH %s -- #%s", more, desc->u.name); |
827 | 0 | break; |
828 | | |
829 | 0 | case KEYDB_SEARCH_MODE_SUBJECT: |
830 | 0 | snprintf (line, sizeof line, "SEARCH %s -- /%s", more, desc->u.name); |
831 | 0 | break; |
832 | | |
833 | 0 | case KEYDB_SEARCH_MODE_KEYGRIP: |
834 | 0 | { |
835 | 0 | unsigned char hexgrip[KEYGRIP_LEN * 2 + 1]; |
836 | 0 | bin2hex (desc->u.grip, KEYGRIP_LEN, hexgrip); |
837 | 0 | snprintf (line, sizeof line, "SEARCH %s -- &%s", more, hexgrip); |
838 | 0 | } |
839 | 0 | break; |
840 | | |
841 | 0 | case KEYDB_SEARCH_MODE_UBID: |
842 | 0 | { |
843 | 0 | unsigned char hexubid[UBID_LEN * 2 + 1]; |
844 | 0 | bin2hex (desc->u.ubid, UBID_LEN, hexubid); |
845 | 0 | snprintf (line, sizeof line, "SEARCH %s -- ^%s", more, hexubid); |
846 | 0 | } |
847 | 0 | break; |
848 | | |
849 | 0 | case KEYDB_SEARCH_MODE_NEXT: |
850 | 0 | log_debug ("%s: mode next - we should not get to here!\n", __func__); |
851 | 0 | snprintf (line, sizeof line, "NEXT"); |
852 | 0 | break; |
853 | | |
854 | 0 | case KEYDB_SEARCH_MODE_FIRST: |
855 | 0 | log_debug ("%s: mode first - we should not get to here!\n", __func__); |
856 | | /*fallthru*/ |
857 | 0 | default: |
858 | 0 | err = gpg_error (GPG_ERR_INV_ARG); |
859 | 0 | goto leave; |
860 | 0 | } |
861 | | |
862 | 0 | if (ndesc > 1) |
863 | 0 | { |
864 | 0 | err = kbx_client_data_simple (hd->kbl->kcd, line); |
865 | 0 | if (err) |
866 | 0 | goto leave; |
867 | 0 | } |
868 | 0 | } |
869 | | |
870 | | |
871 | 0 | do_search: |
872 | 0 | hd->last_ubid_valid = 0; |
873 | 0 | err = kbx_client_data_cmd (hd->kbl->kcd, line, search_status_cb, hd); |
874 | 0 | if (!err && !(err = kbx_client_data_wait (hd->kbl->kcd, &buffer, &len))) |
875 | 0 | { |
876 | 0 | int any; |
877 | 0 | u32 kid[2]; |
878 | |
|
879 | 0 | for (any = i = 0; i < ndesc && !any; i++) |
880 | 0 | if (desc[i].skipfnc) |
881 | 0 | any = 1; |
882 | |
|
883 | 0 | if (any) |
884 | 0 | { |
885 | 0 | err = kbx_get_first_opgp_keyid (buffer, len, kid); |
886 | 0 | if (err) |
887 | 0 | { |
888 | 0 | log_info ("error getting keyid for skipping callback: %s\n", |
889 | 0 | gpg_strerror (err)); |
890 | 0 | goto leave; |
891 | 0 | } |
892 | 0 | for (i = 0; i < ndesc; i++) |
893 | 0 | if (desc[i].skipfnc (desc[i].skipfncvalue, kid, hd->last_uid_no)) |
894 | 0 | { /* Callback told us to skip this one. */ |
895 | 0 | if (DBG_KEYDB && hd->last_ubid_valid) |
896 | 0 | log_printhex (hd->last_ubid, 20, "skipped UBID (%d,%d):", |
897 | 0 | hd->last_uid_no, hd->last_pk_no); |
898 | 0 | snprintf (line, sizeof line, "NEXT"); |
899 | 0 | goto do_search; /* again */ |
900 | 0 | } |
901 | 0 | } |
902 | | |
903 | 0 | hd->kbl->search_result = iobuf_temp_with_content (buffer, len); |
904 | 0 | xfree (buffer); |
905 | 0 | if (DBG_KEYDB && hd->last_ubid_valid) |
906 | 0 | log_printhex (hd->last_ubid, 20, "found UBID (%d,%d):", |
907 | 0 | hd->last_uid_no, hd->last_pk_no); |
908 | 0 | } |
909 | | |
910 | 8.11k | leave: |
911 | 8.11k | if (DBG_CLOCK) |
912 | 8.11k | log_clock ("%s leave (%sfound)", __func__, err? "not ":""); |
913 | 8.11k | return err; |
914 | 0 | } |