cert-gpgsm.c 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750
  1. /* cert-gpgsm.c - Scute certificate searching.
  2. Copyright (C) 2006 g10 Code GmbH
  3. This file is part of Scute[1].
  4. [1] Derived from the RSA Security Inc. PKCS #11 Cryptographic Token
  5. Interface (Cryptoki).
  6. Scute is free software; you can redistribute it and/or modify it
  7. under the terms of the GNU General Public License as published by
  8. the Free Software Foundation; either version 2 of the License, or
  9. (at your option) any later version.
  10. Scute is distributed in the hope that it will be useful, but
  11. WITHOUT ANY WARRANTY; without even the implied warranty of
  12. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  13. General Public License for more details.
  14. You should have received a copy of the GNU General Public License
  15. along with Scute; if not, write to the Free Software Foundation,
  16. Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
  17. In addition, as a special exception, g10 Code GmbH gives permission
  18. to link this library: with the Mozilla Foundation's code for
  19. Mozilla (or with modified versions of it that use the same license
  20. as the "Mozilla" code), and distribute the linked executables. You
  21. must obey the GNU General Public License in all respects for all of
  22. the code used other than "Mozilla". If you modify this file, you
  23. may extend this exception to your version of the file, but you are
  24. not obligated to do so. If you do not wish to do so, delete this
  25. exception statement from your version. */
  26. #if HAVE_CONFIG_H
  27. #include <config.h>
  28. #endif
  29. #include <time.h>
  30. #include <string.h>
  31. #include <stdlib.h>
  32. #include <stdio.h>
  33. #include <stdbool.h>
  34. #include <gpg-error.h>
  35. #include <assuan.h>
  36. #include "cert.h"
  37. #include "support.h"
  38. /* The maximum length of a key listing line. We take the double of
  39. the allowed Assuan line length to avoid a memmove after a part of a
  40. line has been processed. FIXME: There is actually no limit on the
  41. length of the line. */
  42. #define MAX_LINE_LEN (1024*2)
  43. struct search_ctx
  44. {
  45. /* The pending line in an active key listing. */
  46. char pending[MAX_LINE_LEN + 1];
  47. unsigned int pending_len;
  48. /* The caller's search callback, invoked for each certificate. */
  49. cert_search_cb_t search_cb;
  50. void *search_cb_hook;
  51. /* The current certificate. */
  52. struct cert cert;
  53. };
  54. /* Release allocated storage for the certificate CERT and reset the
  55. certificate. */
  56. static void
  57. cert_reset (struct cert *cert)
  58. {
  59. if (cert->issuer_serial)
  60. free (cert->issuer_serial);
  61. if (cert->issuer_name)
  62. free (cert->issuer_name);
  63. if (cert->uid)
  64. free (cert->uid);
  65. if (cert->cert_der)
  66. free (cert->cert_der);
  67. memset (cert, '\0', sizeof (struct cert));
  68. }
  69. /* Support routines for key list processing. */
  70. #define atoi_1(p) (*(p) - '0' )
  71. #define atoi_2(p) ((atoi_1(p) * 10) + atoi_1((p)+1))
  72. #define atoi_4(p) ((atoi_2(p) * 100) + atoi_2((p)+2))
  73. /* Parse the string TIMESTAMP into a time_t. The string may either be
  74. seconds since Epoch or in the ISO 8601 format like
  75. "20390815T143012". Returns 0 for an empty string or seconds since
  76. Epoch. Leading spaces are skipped. If ENDP is not NULL, it will
  77. point to the next non-parsed character in TIMESTRING. */
  78. static time_t
  79. parse_timestamp (const char *timestamp, char **endp)
  80. {
  81. /* Need to skip leading spaces, because that is what strtoul does
  82. but not our ISO 8601 checking code. */
  83. while (*timestamp && *timestamp== ' ')
  84. timestamp++;
  85. if (!*timestamp)
  86. return 0;
  87. if (strlen (timestamp) >= 15 && timestamp[8] == 'T')
  88. {
  89. struct tm buf;
  90. int year;
  91. year = atoi_4 (timestamp);
  92. if (year < 1900)
  93. return (time_t)(-1);
  94. /* Fixme: We would better use a configure test to see whether
  95. mktime can handle dates beyond 2038. */
  96. if (sizeof (time_t) <= 4 && year >= 2038)
  97. return (time_t)2145914603; /* 2037-12-31 23:23:23 */
  98. memset (&buf, 0, sizeof buf);
  99. buf.tm_year = year - 1900;
  100. buf.tm_mon = atoi_2 (timestamp+4) - 1;
  101. buf.tm_mday = atoi_2 (timestamp+6);
  102. buf.tm_hour = atoi_2 (timestamp+9);
  103. buf.tm_min = atoi_2 (timestamp+11);
  104. buf.tm_sec = atoi_2 (timestamp+13);
  105. if (endp)
  106. *endp = (char*)(timestamp + 15);
  107. return timegm (&buf);
  108. }
  109. else
  110. return (time_t)strtoul (timestamp, endp, 10);
  111. }
  112. /* Decode the C formatted string SRC and store the result in the
  113. buffer *DESTP which is LEN bytes long. If LEN is zero, then a
  114. large enough buffer is allocated with malloc and *DESTP is set to
  115. the result. Currently, LEN is only used to specify if allocation
  116. is desired or not, the caller is expected to make sure that *DESTP
  117. is large enough if LEN is not zero. */
  118. static gpg_error_t
  119. decode_c_string (const char *src, char **destp, size_t len)
  120. {
  121. char *dest;
  122. /* Set up the destination buffer. */
  123. if (len)
  124. {
  125. if (len < strlen (src) + 1)
  126. return gpg_error (GPG_ERR_INTERNAL);
  127. dest = *destp;
  128. }
  129. else
  130. {
  131. /* The converted string will never be larger than the original
  132. string. */
  133. dest = malloc (strlen (src) + 1);
  134. if (!dest)
  135. return gpg_error_from_syserror ();
  136. *destp = dest;
  137. }
  138. /* Convert the string. */
  139. while (*src)
  140. {
  141. if (*src != '\\')
  142. {
  143. *(dest++) = *(src++);
  144. continue;
  145. }
  146. switch (src[1])
  147. {
  148. #define DECODE_ONE(match,result) \
  149. case match: \
  150. src += 2; \
  151. *(dest++) = result; \
  152. break;
  153. DECODE_ONE ('\'', '\'');
  154. DECODE_ONE ('\"', '\"');
  155. DECODE_ONE ('\?', '\?');
  156. DECODE_ONE ('\\', '\\');
  157. DECODE_ONE ('a', '\a');
  158. DECODE_ONE ('b', '\b');
  159. DECODE_ONE ('f', '\f');
  160. DECODE_ONE ('n', '\n');
  161. DECODE_ONE ('r', '\r');
  162. DECODE_ONE ('t', '\t');
  163. DECODE_ONE ('v', '\v');
  164. case 'x':
  165. {
  166. int val = xtoi_2 (&src[2]);
  167. if (val == -1)
  168. {
  169. /* Should not happen. */
  170. *(dest++) = *(src++);
  171. *(dest++) = *(src++);
  172. if (*src)
  173. *(dest++) = *(src++);
  174. if (*src)
  175. *(dest++) = *(src++);
  176. }
  177. else
  178. {
  179. if (!val)
  180. {
  181. /* A binary zero is not representable in a C
  182. string. */
  183. *(dest++) = '\\';
  184. *(dest++) = '0';
  185. }
  186. else
  187. *((unsigned char *) dest++) = val;
  188. src += 4;
  189. }
  190. }
  191. break;
  192. default:
  193. {
  194. /* Should not happen. */
  195. *(dest++) = *(src++);
  196. *(dest++) = *(src++);
  197. }
  198. }
  199. }
  200. *(dest++) = 0;
  201. return 0;
  202. }
  203. /* The cert handler for certificate searches. This is invoked for
  204. each complete certificate found by search_certs_line, and the last
  205. pending certificate when EOF is encountered by search_certs. */
  206. static gpg_error_t
  207. search_certs_cert (struct search_ctx *ctx)
  208. {
  209. return (*ctx->search_cb) (ctx->search_cb_hook, &ctx->cert);
  210. }
  211. /* The line handler for certificate searches. This is invoked for
  212. each complete line found by search_certs. */
  213. static gpg_error_t
  214. search_certs_line (struct search_ctx *ctx)
  215. {
  216. char *line;
  217. enum { RT_NONE, RT_CRT, RT_CRS, RT_FPR, RT_GRP, RT_UID } rectype = RT_NONE;
  218. #define NR_FIELDS 16
  219. char *field[NR_FIELDS];
  220. int fields = 0;
  221. struct cert *cert;
  222. /* Strip a trailing carriage return. */
  223. if (ctx->pending_len > 0
  224. && ctx->pending[ctx->pending_len] == '\r')
  225. ctx->pending_len--;
  226. ctx->pending[ctx->pending_len] = '\0';
  227. ctx->pending_len = 0;
  228. cert = &ctx->cert;
  229. line = ctx->pending;
  230. while (line && fields < NR_FIELDS)
  231. {
  232. field[fields++] = line;
  233. line = strchr (line, ':');
  234. if (line)
  235. *(line++) = '\0';
  236. }
  237. if (!strcmp (field[0], "crt"))
  238. rectype = RT_CRT;
  239. else if (!strcmp (field[0], "crs"))
  240. rectype = RT_CRS;
  241. else if (!strcmp (field[0], "fpr"))
  242. rectype = RT_FPR;
  243. else if (!strcmp (field[0], "grp"))
  244. rectype = RT_GRP;
  245. else if (!strcmp (field[0], "uid"))
  246. rectype = RT_UID;
  247. else
  248. rectype = RT_NONE;
  249. switch (rectype)
  250. {
  251. case RT_CRT:
  252. case RT_CRS:
  253. /* Reinitialize CERT. */
  254. if (cert->valid)
  255. {
  256. gpg_error_t err;
  257. err = search_certs_cert (ctx);
  258. if (err)
  259. return err;
  260. cert_reset (cert);
  261. }
  262. cert->valid = true;
  263. #if 0
  264. /* Field 2 has the trust info. */
  265. if (fields >= 2)
  266. set_mainkey_trust_info (key, field[1]);
  267. #endif
  268. /* Field 3 has the key length. */
  269. if (fields >= 3)
  270. {
  271. int i = atoi (field[2]);
  272. /* Ignore invalid values. */
  273. if (i > 1)
  274. cert->length = i;
  275. }
  276. /* Field 4 has the public key algorithm. */
  277. if (fields >= 4)
  278. {
  279. int i = atoi (field[3]);
  280. if (i >= 1 && i < 128)
  281. cert->pubkey_algo = i;
  282. }
  283. /* Field 5 has the long keyid. Allow short key IDs for the
  284. output of an external keyserver listing. */
  285. if (fields >= 5 && strlen (field[4]) <= sizeof (cert->keyid) - 1)
  286. strcpy (cert->keyid, field[4]);
  287. /* Field 6 has the timestamp (seconds). */
  288. if (fields >= 6)
  289. cert->timestamp = parse_timestamp (field[5], NULL);
  290. /* Field 7 has the expiration time (seconds). */
  291. if (fields >= 7)
  292. cert->expires = parse_timestamp (field[6], NULL);
  293. /* Field 8 has the X.509 serial number. */
  294. if (fields >= 8)
  295. {
  296. cert->issuer_serial = strdup (field[7]);
  297. if (!cert->issuer_serial)
  298. return gpg_error_from_syserror ();
  299. }
  300. #if 0
  301. /* Field 9 has the ownertrust. */
  302. if (fields >= 9)
  303. set_ownertrust (key, field[8]);
  304. #endif
  305. /* Field 10 is the issuer name. */
  306. if (fields >= 10)
  307. if (decode_c_string (field[9], &cert->issuer_name, 0))
  308. return gpg_error (GPG_ERR_ENOMEM); /* FIXME */
  309. /* Field 11 has the signature class. */
  310. #if 0
  311. /* Field 12 has the capabilities. */
  312. if (fields >= 12)
  313. set_mainkey_capability (key, field[11]);
  314. #endif
  315. break;
  316. case RT_UID:
  317. if (cert->valid)
  318. {
  319. /* Field 2 has the trust info, and field 10 has the user ID. */
  320. if (fields >= 10)
  321. {
  322. if (decode_c_string (field[9], &cert->uid, 0))
  323. return gpg_error (GPG_ERR_ENOMEM); /* FIXME */
  324. }
  325. }
  326. break;
  327. case RT_FPR:
  328. if (cert->valid)
  329. {
  330. /* Field 10 has the fingerprint (take only the first one). */
  331. if (fields >= 10 && strlen (field[9]) <= sizeof (cert->fpr) - 1)
  332. strcpy (cert->fpr, field[9]);
  333. /* Field 13 has the gpgsm chain ID (take only the first one). */
  334. if (fields >= 13 && strlen (field[12])
  335. <= sizeof (cert->chain_id) - 1)
  336. strcpy (cert->chain_id, field[12]);
  337. }
  338. break;
  339. case RT_GRP:
  340. if (cert->valid)
  341. {
  342. /* Field 10 has the key grip. */
  343. if (fields >= 10 && strlen (field[9]) <= sizeof (cert->grip) - 1)
  344. strcpy (cert->grip, field[9]);
  345. }
  346. break;
  347. case RT_NONE:
  348. /* Unknown record. */
  349. break;
  350. }
  351. return 0;
  352. }
  353. /* This is the data line callback handler provided to assuan_transact
  354. in scute_gpgsm_search_certs. It buffers incomplete lines, and also
  355. handles the EOF signal provided directly by
  356. scute_gpgsm_search_certs. */
  357. static gpg_error_t
  358. search_certs (void *hook, char *line, size_t line_len)
  359. {
  360. struct search_ctx *ctx = hook;
  361. gpg_error_t err;
  362. if (!line)
  363. {
  364. /* This indicates an EOF. */
  365. /* Check for a pending line, in case GPGSM didn't close with a
  366. newline. */
  367. if (ctx->pending_len)
  368. {
  369. err = search_certs_line (ctx);
  370. if (err)
  371. return err;
  372. }
  373. /* Check for a pending certificate. */
  374. if (ctx->cert.valid)
  375. return search_certs_cert (ctx);
  376. return 0;
  377. }
  378. while (line_len)
  379. {
  380. if (*line == '\n')
  381. {
  382. err = search_certs_line (ctx);
  383. if (err)
  384. return err;
  385. }
  386. else
  387. {
  388. if (ctx->pending_len >= MAX_LINE_LEN)
  389. return gpg_error (GPG_ERR_LINE_TOO_LONG);
  390. ctx->pending[ctx->pending_len++] = *line;
  391. }
  392. line++;
  393. line_len--;
  394. }
  395. return 0;
  396. }
  397. /* Invoke SEARCH_CB for each certificate found using assuan connection
  398. CTX to GPGSM. */
  399. static gpg_error_t
  400. scute_gpgsm_search_certs (assuan_context_t ctx, cert_search_cb_t search_cb,
  401. void *search_cb_hook)
  402. {
  403. gpg_error_t err;
  404. struct search_ctx search;
  405. err = assuan_transact (ctx, "OPTION with-key-data", NULL, NULL,
  406. NULL, NULL, NULL, NULL);
  407. if (err)
  408. return err;
  409. search.pending_len = 0;
  410. search.search_cb = search_cb;
  411. search.search_cb_hook = search_cb_hook;
  412. memset (&search.cert, '\0', sizeof (search.cert));
  413. err = assuan_transact (ctx, "DUMPKEYS", &search_certs, &search, NULL,
  414. NULL, NULL, NULL);
  415. if (err)
  416. goto out;
  417. /* Signal the EOF. This is not done by Assuan for us. */
  418. err = search_certs (&search, NULL, 0);
  419. if (err)
  420. goto out;
  421. out:
  422. cert_reset (&search.cert);
  423. return err;
  424. }
  425. struct search_ctx_by_field
  426. {
  427. /* What we are searching for. */
  428. enum { SEARCH_BY_GRIP, SEARCH_BY_FPR } field;
  429. /* The pattern we are looking for. */
  430. const char *pattern;
  431. cert_search_cb_t search_cb;
  432. void *search_cb_hook;
  433. };
  434. /* This is a compatibility function for GPGSM 2.0.0, which does not
  435. support the --data option with the EXPORT command. */
  436. static gpg_error_t
  437. export_cert_compat (char *fpr, struct cert *cert)
  438. {
  439. gpg_error_t err;
  440. assuan_context_t ctx;
  441. const char *argv[] = { "gpgsm", "--server", NULL };
  442. int got;
  443. #define COMMANDLINELEN 80
  444. char cmd[COMMANDLINELEN];
  445. int output_fds[2];
  446. int child_fds[2];
  447. #define MAX_CERT_SIZE 4096
  448. cert->cert_der = malloc (MAX_CERT_SIZE);
  449. if (!cert->cert_der)
  450. return gpg_error_from_syserror ();
  451. if(pipe (output_fds) < 0)
  452. return gpg_error_from_syserror ();
  453. child_fds[0] = output_fds[1];
  454. child_fds[1] = -1;
  455. err = assuan_pipe_connect (&ctx, GPGSM_PATH, argv, child_fds);
  456. close (output_fds[1]);
  457. if (err)
  458. {
  459. close (output_fds[0]);
  460. return err;
  461. }
  462. snprintf (cmd, sizeof (cmd), "OUTPUT FD=%i", output_fds[1]);
  463. err = assuan_transact (ctx, cmd, NULL, NULL, NULL, NULL, NULL, NULL);
  464. if (err)
  465. goto export_out;
  466. /* FIXME: This will only work if the certificate is small and fits
  467. into the pipe buffer completely!!! */
  468. snprintf (cmd, sizeof (cmd), "EXPORT %s\n", cert->fpr);
  469. err = assuan_transact (ctx, cmd, NULL, NULL, NULL, NULL, NULL, NULL);
  470. if (err)
  471. goto export_out;
  472. do
  473. {
  474. got = read (output_fds[0], cert->cert_der + cert->cert_der_len,
  475. MAX_CERT_SIZE - cert->cert_der_len);
  476. if (got > 0)
  477. cert->cert_der_len += got;
  478. }
  479. while (!err && got > 0 && cert->cert_der_len < MAX_CERT_SIZE);
  480. if (got < 0 || cert->cert_der_len == MAX_CERT_SIZE)
  481. err = gpg_error (GPG_ERR_GENERAL);
  482. export_out:
  483. assuan_disconnect (ctx);
  484. close (output_fds[0]);
  485. return err;
  486. }
  487. struct export_hook
  488. {
  489. /* The exported data. */
  490. char *buffer;
  491. /* The length of the exported data buffer. */
  492. unsigned int buffer_len;
  493. /* The size of the allocated exported data buffer. */
  494. unsigned int buffer_size;
  495. };
  496. #define EXP_DATA_START 4096
  497. static gpg_error_t
  498. export_cert_cb (void *hook, char *line, size_t line_len)
  499. {
  500. struct export_hook *exp = hook;
  501. if (exp->buffer_size - exp->buffer_len < line_len)
  502. {
  503. unsigned int new_buffer_size = exp->buffer_size * 2;
  504. char *new_buffer = realloc (exp->buffer, new_buffer_size);
  505. if (!new_buffer)
  506. return gpg_error_from_syserror ();
  507. exp->buffer = new_buffer;
  508. exp->buffer_size = new_buffer_size;
  509. }
  510. memcpy (exp->buffer + exp->buffer_len, line, line_len);
  511. return 0;
  512. }
  513. static gpg_error_t
  514. export_cert (char *fpr, struct cert *cert)
  515. {
  516. gpg_error_t err;
  517. assuan_context_t ctx;
  518. const char *argv[] = { "gpgsm", "--server", NULL };
  519. #define COMMANDLINELEN 80
  520. char cmd[COMMANDLINELEN];
  521. int output_fds[2];
  522. int child_fds[2];
  523. struct export_hook exp;
  524. if(pipe (output_fds) < 0)
  525. return gpg_error_from_syserror ();
  526. child_fds[0] = output_fds[1];
  527. child_fds[1] = -1;
  528. err = assuan_pipe_connect (&ctx, GPGSM_PATH, argv, child_fds);
  529. close (output_fds[1]);
  530. if (err)
  531. {
  532. close (output_fds[0]);
  533. return err;
  534. }
  535. exp.buffer = NULL;
  536. exp.buffer_len = 0;
  537. exp.buffer_size = 0;
  538. snprintf (cmd, sizeof (cmd), "EXPORT --data -- %s\n", cert->fpr);
  539. err = assuan_transact (ctx, cmd, export_cert_cb, &exp,
  540. NULL, NULL, NULL, NULL);
  541. assuan_disconnect (ctx);
  542. close (output_fds[0]);
  543. /* For compatibility with GPGSM 2.0.0, we fall back to a work around
  544. in that case. */
  545. if (gpg_err_code (err) == GPG_ERR_ASS_NO_OUTPUT)
  546. {
  547. if (cert->cert_der)
  548. {
  549. free (cert->cert_der);
  550. cert->cert_der = NULL;
  551. }
  552. err = export_cert_compat (fpr, cert);
  553. }
  554. return err;
  555. }
  556. static gpg_error_t
  557. search_certs_by_field (void *hook, struct cert *cert)
  558. {
  559. struct search_ctx_by_field *ctx = hook;
  560. gpg_error_t err = 0;
  561. if ((ctx->field == SEARCH_BY_GRIP && !strcmp (ctx->pattern, cert->grip))
  562. || (ctx->field == SEARCH_BY_FPR && !strcmp (ctx->pattern, cert->fpr)))
  563. {
  564. if (strlen (cert->fpr) != 40)
  565. return gpg_error (GPG_ERR_GENERAL);
  566. err = export_cert (cert->fpr, cert);
  567. if (err)
  568. return err;
  569. err = (*ctx->search_cb) (ctx->search_cb_hook, cert);
  570. }
  571. return err;
  572. }
  573. /* Invoke SEARCH_CB for each certificate found using assuan connection
  574. CTX to GPGSM. */
  575. gpg_error_t
  576. scute_gpgsm_search_certs_by_grip (const char *grip,
  577. cert_search_cb_t search_cb,
  578. void *search_cb_hook)
  579. {
  580. gpg_error_t err;
  581. assuan_context_t ctx;
  582. const char *argv[] = { "gpgsm", "--server", NULL };
  583. struct search_ctx_by_field search;
  584. err = assuan_pipe_connect (&ctx, GPGSM_PATH, argv, NULL);
  585. if (err)
  586. return err;
  587. search.field = SEARCH_BY_GRIP;
  588. search.pattern = grip;
  589. search.search_cb = search_cb;
  590. search.search_cb_hook = search_cb_hook;
  591. err = scute_gpgsm_search_certs (ctx, &search_certs_by_field, &search);
  592. assuan_disconnect (ctx);
  593. return err;
  594. }
  595. /* Invoke SEARCH_CB for each certificate found using assuan connection
  596. CTX to GPGSM. */
  597. gpg_error_t
  598. scute_gpgsm_search_certs_by_fpr (const char *fpr,
  599. cert_search_cb_t search_cb,
  600. void *search_cb_hook)
  601. {
  602. gpg_error_t err;
  603. assuan_context_t ctx;
  604. const char *argv[] = { "gpgsm", "--server", NULL };
  605. struct search_ctx_by_field search;
  606. err = assuan_pipe_connect (&ctx, GPGSM_PATH, argv, NULL);
  607. if (err)
  608. return err;
  609. search.field = SEARCH_BY_FPR;
  610. search.pattern = fpr;
  611. search.search_cb = search_cb;
  612. search.search_cb_hook = search_cb_hook;
  613. err = scute_gpgsm_search_certs (ctx, &search_certs_by_field, &search);
  614. assuan_disconnect (ctx);
  615. return err;
  616. }