diff options
Diffstat (limited to 'agent')
-rw-r--r-- | agent/query.c | 473 | ||||
-rw-r--r-- | agent/sexp-parse.h | 98 | ||||
-rw-r--r-- | agent/trustlist.c | 306 |
3 files changed, 877 insertions, 0 deletions
diff --git a/agent/query.c b/agent/query.c new file mode 100644 index 000000000..4a051965d --- /dev/null +++ b/agent/query.c @@ -0,0 +1,473 @@ +/* query.c - fork of the pinentry to query stuff from the user + * Copyright (C) 2001, 2002 Free Software Foundation, Inc. + * + * This file is part of GnuPG. + * + * GnuPG is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * GnuPG is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +#include <config.h> +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <ctype.h> +#include <assert.h> +#include <unistd.h> +#include <sys/stat.h> +#ifdef USE_GNU_PTH +# include <pth.h> +#endif + +#include "agent.h" +#include "i18n.h" +#include <assuan.h> + +#ifdef _POSIX_OPEN_MAX +#define MAX_OPEN_FDS _POSIX_OPEN_MAX +#else +#define MAX_OPEN_FDS 20 +#endif + +static ASSUAN_CONTEXT entry_ctx = NULL; +#ifdef USE_GNU_PTH +static pth_mutex_t entry_lock = PTH_MUTEX_INIT; +#endif + +/* data to be passed to our callbacks */ +struct entry_parm_s { + int lines; + size_t size; + char *buffer; +}; + + + + +/* Unlock the pinentry so that another thread can start one and + disconnect that pinentry - we do this after the unlock so that a + stalled pinentry does not block other threads. Fixme: We should + have a timeout in Assuan for the disconnetc operation. */ +static int +unlock_pinentry (int rc) +{ + ASSUAN_CONTEXT ctx = entry_ctx; + +#ifdef USE_GNU_PTH + if (!pth_mutex_release (&entry_lock)) + { + log_error ("failed to release the entry lock\n"); + if (!rc) + rc = gpg_error (GPG_ERR_INTERNAL); + } +#endif + entry_ctx = NULL; + assuan_disconnect (ctx); + return rc; +} + +/* Fork off the pin entry if this has not already been done. Note, + that this function must always be used to aquire the lock for the + pinentry - we will serialize _all_ pinentry calls. + */ +static int +start_pinentry (CTRL ctrl) +{ + int rc; + const char *pgmname; + ASSUAN_CONTEXT ctx; + const char *argv[5]; + int no_close_list[3]; + int i; + +#ifdef USE_GNU_PTH + if (!pth_mutex_acquire (&entry_lock, 0, NULL)) + { + log_error ("failed to acquire the entry lock\n"); + return gpg_error (GPG_ERR_INTERNAL); + } +#endif + + if (entry_ctx) + return 0; + + if (opt.verbose) + log_info ("starting a new PIN Entry\n"); + + if (fflush (NULL)) + { + gpg_error_t tmperr = gpg_error (gpg_err_code_from_errno (errno)); + log_error ("error flushing pending output: %s\n", strerror (errno)); + return unlock_pinentry (tmperr); + } + + if (!opt.pinentry_program || !*opt.pinentry_program) + opt.pinentry_program = GNUPG_DEFAULT_PINENTRY; + if ( !(pgmname = strrchr (opt.pinentry_program, '/'))) + pgmname = opt.pinentry_program; + else + pgmname++; + + argv[0] = pgmname; + if (ctrl->display && !opt.keep_display) + { + argv[1] = "--display"; + argv[2] = ctrl->display; + argv[3] = NULL; + } + else + argv[1] = NULL; + + i=0; + if (!opt.running_detached) + { + if (log_get_fd () != -1) + no_close_list[i++] = log_get_fd (); + no_close_list[i++] = fileno (stderr); + } + no_close_list[i] = -1; + + /* connect to the pinentry and perform initial handshaking */ + rc = assuan_pipe_connect (&ctx, opt.pinentry_program, (char**)argv, + no_close_list); + if (rc) + { + log_error ("can't connect to the PIN entry module: %s\n", + assuan_strerror (rc)); + return unlock_pinentry (gpg_error (GPG_ERR_NO_PIN_ENTRY)); + } + entry_ctx = ctx; + + if (DBG_ASSUAN) + log_debug ("connection to PIN entry established\n"); + + rc = assuan_transact (entry_ctx, + opt.no_grab? "OPTION no-grab":"OPTION grab", + NULL, NULL, NULL, NULL, NULL, NULL); + if (rc) + return unlock_pinentry (map_assuan_err (rc)); + if (ctrl->ttyname) + { + char *optstr; + if (asprintf (&optstr, "OPTION ttyname=%s", ctrl->ttyname) < 0 ) + return unlock_pinentry (out_of_core ()); + rc = assuan_transact (entry_ctx, optstr, NULL, NULL, NULL, NULL, NULL, + NULL); + free (optstr); + if (rc) + return unlock_pinentry (map_assuan_err (rc)); + } + if (ctrl->ttytype) + { + char *optstr; + if (asprintf (&optstr, "OPTION ttytype=%s", ctrl->ttytype) < 0 ) + return unlock_pinentry (out_of_core ()); + rc = assuan_transact (entry_ctx, optstr, NULL, NULL, NULL, NULL, NULL, + NULL); + if (rc) + return unlock_pinentry (map_assuan_err (rc)); + } + if (ctrl->lc_ctype) + { + char *optstr; + if (asprintf (&optstr, "OPTION lc-ctype=%s", ctrl->lc_ctype) < 0 ) + return unlock_pinentry (out_of_core ()); + rc = assuan_transact (entry_ctx, optstr, NULL, NULL, NULL, NULL, NULL, + NULL); + if (rc) + return unlock_pinentry (map_assuan_err (rc)); + } + if (ctrl->lc_messages) + { + char *optstr; + if (asprintf (&optstr, "OPTION lc-messages=%s", ctrl->lc_messages) < 0 ) + return unlock_pinentry (out_of_core ()); + rc = assuan_transact (entry_ctx, optstr, NULL, NULL, NULL, NULL, NULL, + NULL); + if (rc) + return unlock_pinentry (map_assuan_err (rc)); + } + return 0; +} + + +static AssuanError +getpin_cb (void *opaque, const void *buffer, size_t length) +{ + struct entry_parm_s *parm = opaque; + + if (!buffer) + return 0; + + /* we expect the pin to fit on one line */ + if (parm->lines || length >= parm->size) + return ASSUAN_Too_Much_Data; + + /* fixme: we should make sure that the assuan buffer is allocated in + secure memory or read the response byte by byte */ + memcpy (parm->buffer, buffer, length); + parm->buffer[length] = 0; + parm->lines++; + return 0; +} + + +static int +all_digitsp( const char *s) +{ + for (; *s && *s >= '0' && *s <= '9'; s++) + ; + return !*s; +} + + + +/* Call the Entry and ask for the PIN. We do check for a valid PIN + number here and repeat it as long as we have invalid formed + numbers. */ +int +agent_askpin (CTRL ctrl, + const char *desc_text, struct pin_entry_info_s *pininfo) +{ + int rc; + char line[ASSUAN_LINELENGTH]; + struct entry_parm_s parm; + const char *errtext = NULL; + int is_pin = 0; + + if (opt.batch) + return 0; /* fixme: we should return BAD PIN */ + + if (!pininfo || pininfo->max_length < 1) + return gpg_error (GPG_ERR_INV_VALUE); + if (!desc_text && pininfo->min_digits) + desc_text = _("Please enter your PIN, so that the secret key " + "can be unlocked for this session"); + else if (!desc_text) + desc_text = _("Please enter your passphrase, so that the secret key " + "can be unlocked for this session"); + + is_pin = desc_text && strstr (desc_text, "PIN"); + + rc = start_pinentry (ctrl); + if (rc) + return rc; + + snprintf (line, DIM(line)-1, "SETDESC %s", desc_text); + line[DIM(line)-1] = 0; + rc = assuan_transact (entry_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL); + if (rc) + return unlock_pinentry (map_assuan_err (rc)); + + rc = assuan_transact (entry_ctx, + is_pin? "SETPROMPT PIN:" + : "SETPROMPT Passphrase:", + NULL, NULL, NULL, NULL, NULL, NULL); + if (rc) + return unlock_pinentry (map_assuan_err (rc)); + + for (;pininfo->failed_tries < pininfo->max_tries; pininfo->failed_tries++) + { + memset (&parm, 0, sizeof parm); + parm.size = pininfo->max_length; + parm.buffer = pininfo->pin; + + if (errtext) + { + /* fixme: should we show the try count? It must be translated */ + snprintf (line, DIM(line)-1, "SETERROR %s (try %d of %d)", + errtext, pininfo->failed_tries+1, pininfo->max_tries); + line[DIM(line)-1] = 0; + rc = assuan_transact (entry_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL); + if (rc) + return unlock_pinentry (map_assuan_err (rc)); + errtext = NULL; + } + + rc = assuan_transact (entry_ctx, "GETPIN", getpin_cb, &parm, + NULL, NULL, NULL, NULL); + if (rc == ASSUAN_Too_Much_Data) + errtext = is_pin? _("PIN too long") + : _("Passphrase too long"); + else if (rc) + return unlock_pinentry (map_assuan_err (rc)); + + if (!errtext && pininfo->min_digits) + { + /* do some basic checks on the entered PIN. */ + if (!all_digitsp (pininfo->pin)) + errtext = _("Invalid characters in PIN"); + else if (pininfo->max_digits + && strlen (pininfo->pin) > pininfo->max_digits) + errtext = _("PIN too long"); + else if (strlen (pininfo->pin) < pininfo->min_digits) + errtext = _("PIN too short"); + } + + if (!errtext && pininfo->check_cb) + { + /* More checks by utilizing the optional callback. */ + pininfo->cb_errtext = NULL; + rc = pininfo->check_cb (pininfo); + if (rc == -1 && pininfo->cb_errtext) + errtext = pininfo->cb_errtext; + else if (gpg_err_code (rc) == GPG_ERR_BAD_PASSPHRASE + || gpg_err_code (rc) == GPG_ERR_BAD_PIN) + errtext = (is_pin? _("Bad PIN") + : _("Bad Passphrase")); + else if (rc) + return unlock_pinentry (map_assuan_err (rc)); + } + + if (!errtext) + return unlock_pinentry (0); /* okay, got a PIN or passphrase */ + } + + return unlock_pinentry (gpg_error (pininfo->min_digits? GPG_ERR_BAD_PIN + : GPG_ERR_BAD_PASSPHRASE)); +} + + + +/* Ask for the passphrase using the supplied arguments. The + passphrase is returned in RETPASS as an hex encoded string to be + freed by the caller */ +int +agent_get_passphrase (CTRL ctrl, + char **retpass, const char *desc, const char *prompt, + const char *errtext) +{ + + int rc; + char line[ASSUAN_LINELENGTH]; + struct entry_parm_s parm; + unsigned char *p, *hexstring; + int i; + + *retpass = NULL; + if (opt.batch) + return gpg_error (GPG_ERR_BAD_PASSPHRASE); + + rc = start_pinentry (ctrl); + if (rc) + return rc; + + if (!prompt) + prompt = desc && strstr (desc, "PIN")? "PIN": _("Passphrase"); + + + if (desc) + snprintf (line, DIM(line)-1, "SETDESC %s", desc); + else + snprintf (line, DIM(line)-1, "RESET"); + line[DIM(line)-1] = 0; + rc = assuan_transact (entry_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL); + if (rc) + return unlock_pinentry (map_assuan_err (rc)); + + snprintf (line, DIM(line)-1, "SETPROMPT %s", prompt); + line[DIM(line)-1] = 0; + rc = assuan_transact (entry_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL); + if (rc) + return unlock_pinentry (map_assuan_err (rc)); + + if (errtext) + { + snprintf (line, DIM(line)-1, "SETERROR %s", errtext); + line[DIM(line)-1] = 0; + rc = assuan_transact (entry_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL); + if (rc) + return unlock_pinentry (map_assuan_err (rc)); + } + + memset (&parm, 0, sizeof parm); + parm.size = ASSUAN_LINELENGTH/2 - 5; + parm.buffer = gcry_malloc_secure (parm.size+10); + if (!parm.buffer) + return unlock_pinentry (out_of_core ()); + + assuan_begin_confidential (entry_ctx); + rc = assuan_transact (entry_ctx, "GETPIN", getpin_cb, &parm, NULL, NULL, NULL, NULL); + if (rc) + { + xfree (parm.buffer); + return unlock_pinentry (map_assuan_err (rc)); + } + + hexstring = gcry_malloc_secure (strlen (parm.buffer)*2+1); + if (!hexstring) + { + gpg_error_t tmperr = out_of_core (); + xfree (parm.buffer); + return unlock_pinentry (tmperr); + } + + for (i=0, p=parm.buffer; *p; p++, i += 2) + sprintf (hexstring+i, "%02X", *p); + + xfree (parm.buffer); + *retpass = hexstring; + return unlock_pinentry (0); +} + + + +/* Pop up the PIN-entry, display the text and the prompt and ask the + user to confirm this. We return 0 for success, ie. the used + confirmed it, GPG_ERR_NOT_CONFIRMED for what the text says or an + other error. */ +int +agent_get_confirmation (CTRL ctrl, + const char *desc, const char *ok, const char *cancel) +{ + int rc; + char line[ASSUAN_LINELENGTH]; + + rc = start_pinentry (ctrl); + if (rc) + return rc; + + if (desc) + snprintf (line, DIM(line)-1, "SETDESC %s", desc); + else + snprintf (line, DIM(line)-1, "RESET"); + line[DIM(line)-1] = 0; + rc = assuan_transact (entry_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL); + if (rc) + return unlock_pinentry (map_assuan_err (rc)); + + if (ok) + { + snprintf (line, DIM(line)-1, "SETOK %s", ok); + line[DIM(line)-1] = 0; + rc = assuan_transact (entry_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL); + if (rc) + return unlock_pinentry (map_assuan_err (rc)); + } + if (cancel) + { + snprintf (line, DIM(line)-1, "SETCANCEL %s", cancel); + line[DIM(line)-1] = 0; + rc = assuan_transact (entry_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL); + if (rc) + return unlock_pinentry (map_assuan_err (rc)); + } + + rc = assuan_transact (entry_ctx, "CONFIRM", NULL, NULL, NULL, NULL, NULL, NULL); + return unlock_pinentry (map_assuan_err (rc)); +} + + + diff --git a/agent/sexp-parse.h b/agent/sexp-parse.h new file mode 100644 index 000000000..338321f48 --- /dev/null +++ b/agent/sexp-parse.h @@ -0,0 +1,98 @@ +/* sexp-parse.h - S-Exp helper functions + * Copyright (C) 2002 Free Software Foundation, Inc. + * + * This file is part of GnuPG. + * + * GnuPG is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * GnuPG is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +#ifndef SEXP_PARSE_H +#define SEXP_PARSE_H + +#include "../common/util.h" + +/* Return the length of the next S-Exp part and update the pointer to + the first data byte. 0 is return on error */ +static inline size_t +snext (unsigned char const **buf) +{ + const unsigned char *s; + int n; + + s = *buf; + for (n=0; *s && *s != ':' && digitp (s); s++) + n = n*10 + atoi_1 (s); + if (!n || *s != ':') + return 0; /* we don't allow empty lengths */ + *buf = s+1; + return n; +} + +/* Skip over the S-Expression BUF points to and update BUF to point to + the chacter right behind. DEPTH gives the initial number of open + lists and may be passed as a positive number to skip over the + remainder of an S-Expression if the current position is somewhere + in an S-Expression. The function may return an error code if it + encounters an impossible conditions */ +static inline int +sskip (unsigned char const **buf, int *depth) +{ + const unsigned char *s = *buf; + size_t n; + int d = *depth; + + while (d > 0) + { + if (*s == '(') + { + d++; + s++; + } + else if (*s == ')') + { + d--; + s++; + } + else + { + if (!d) + return gpg_error (GPG_ERR_INV_SEXP); + n = snext (&s); + if (!n) + return gpg_error (GPG_ERR_INV_SEXP); + s += n; + } + } + *buf = s; + *depth = d; + return 0; +} + + +/* Check whether the the string at the address BUF points to matches + the token. Return true on match and update BUF to point behind the + token. */ +static inline int +smatch (unsigned char const **buf, size_t buflen, const char *token) +{ + size_t toklen = strlen (token); + + if (buflen != toklen || memcmp (*buf, token, toklen)) + return 0; + *buf += toklen; + return 1; +} + +#endif /*SEXP_PARSE_H*/ diff --git a/agent/trustlist.c b/agent/trustlist.c new file mode 100644 index 000000000..8575aedb0 --- /dev/null +++ b/agent/trustlist.c @@ -0,0 +1,306 @@ +/* trustlist.c - Maintain the list of trusted keys + * Copyright (C) 2002 Free Software Foundation, Inc. + * + * This file is part of GnuPG. + * + * GnuPG is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * GnuPG is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +#include <config.h> +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <ctype.h> +#include <assert.h> +#include <unistd.h> +#include <sys/stat.h> + +#include "agent.h" +#include <assuan.h> /* fixme: need a way to avoid assuan calls here */ + +static const char headerblurb[] = +"# This is the list of trusted keys. Comments like this one and empty\n" +"# lines are allowed but keep in mind that the entire file is integrity\n" +"# protected by the use of a MAC, so changing the file does not make\n" +"# much sense without the knowledge of the MAC key. Lines do have a\n" +"# length limit but this is not serious limitation as the format of the\n" +"# entries is fixed and checked by gpg-agent: A non-comment line starts\n" +"# with optional white spaces, followed by exactly 40 hex character,\n" +"# optioanlly followed by a flag character which my either be 'P', 'S'\n" +"# or '*'. Additional data delimited with by a white space is ignored.\n" +"\n"; + + +static FILE *trustfp; + + +static int +open_list (int append) +{ + char *fname; + + fname = make_filename (opt.homedir, "trustlist.txt", NULL); + trustfp = fopen (fname, append? "a+":"r"); + if (!trustfp && errno == ENOENT) + { + trustfp = fopen (fname, "wx"); + if (!trustfp) + { + gpg_error_t tmperr = gpg_error (gpg_err_code_from_errno (errno)); + log_error ("can't create `%s': %s\n", fname, strerror (errno)); + xfree (fname); + return tmperr; + } + fputs (headerblurb, trustfp); + fclose (trustfp); + trustfp = fopen (fname, append? "a+":"r"); + } + + if (!trustfp) + { + gpg_error_t tmperr = gpg_error (gpg_err_code_from_errno (errno)); + log_error ("can't open `%s': %s\n", fname, strerror (errno)); + xfree (fname); + return tmperr; + } + + /*FIXME: check the MAC */ + + return 0; +} + + + +/* Read the trustlist and return entry by entry. KEY must point to a + buffer of at least 41 characters. KEYFLAG does return either 'P', + 'S' or '*'. + + Reading a valid entry return 0, EOF returns -1 any other error + returns the appropriate error code. */ +static int +read_list (char *key, int *keyflag) +{ + int rc; + int c, i; + char *p, line[256]; + + if (!trustfp) + { + rc = open_list (0); + if (rc) + return rc; + } + + do + { + if (!fgets (line, DIM(line)-1, trustfp) ) + { + if (feof (trustfp)) + return -1; + return gpg_error (gpg_err_code_from_errno (errno)); + } + + if (!*line || line[strlen(line)-1] != '\n') + { + /* eat until end of line */ + while ( (c=getc (trustfp)) != EOF && c != '\n') + ; + return gpg_error (*line? GPG_ERR_LINE_TOO_LONG + : GPG_ERR_INCOMPLETE_LINE); + } + + /* Allow for emty lines and spaces */ + for (p=line; spacep (p); p++) + ; + } + while (!*p || *p == '\n' || *p == '#'); + + for (i=0; hexdigitp (p+i) && i < 40; i++) + key[i] = p[i] >= 'a'? (p[i] & 0xdf): p[i]; + key[i] = 0; + if (i!=40 || !(spacep (p+i) || p[i] == '\n')) + { + log_error ("invalid formatted fingerprint in trustlist\n"); + return gpg_error (GPG_ERR_BAD_DATA); + } + assert (p[i]); + if (p[i] == '\n') + *keyflag = '*'; + else + { + i++; + if ( p[i] == 'P' || p[i] == 'p') + *keyflag = 'P'; + else if ( p[i] == 'S' || p[i] == 's') + *keyflag = 'S'; + else if ( p[i] == '*') + *keyflag = '*'; + else + { + log_error ("invalid keyflag in trustlist\n"); + return gpg_error (GPG_ERR_BAD_DATA); + } + i++; + if ( !(spacep (p+i) || p[i] == '\n')) + { + log_error ("invalid keyflag in trustlist\n"); + return gpg_error (GPG_ERR_BAD_DATA); + } + } + + return 0; +} + +/* check whether the given fpr is in our trustdb. We expect FPR to be + an all uppercase hexstring of 40 characters. */ +int +agent_istrusted (const char *fpr) +{ + int rc; + static char key[41]; + int keyflag; + + if (trustfp) + rewind (trustfp); + while (!(rc=read_list (key, &keyflag))) + { + if (!strcmp (key, fpr)) + return 0; + } + if (rc != -1) + { + /* error in the trustdb - close it to give the user a chance for + correction */ + fclose (trustfp); + trustfp = NULL; + } + return rc; +} + + +/* write all trust entries to FP */ +int +agent_listtrusted (void *assuan_context) +{ + int rc; + static char key[51]; + int keyflag; + + if (trustfp) + rewind (trustfp); + while (!(rc=read_list (key, &keyflag))) + { + key[40] = ' '; + key[41] = keyflag; + key[42] = '\n'; + assuan_send_data (assuan_context, key, 43); + assuan_send_data (assuan_context, NULL, 0); /* flush */ + } + if (rc == -1) + rc = 0; + if (rc) + { + /* error in the trustdb - close it to give the user a chance for + correction */ + fclose (trustfp); + trustfp = NULL; + } + return rc; +} + + +/* Insert the given fpr into our trustdb. We expect FPR to be an all + uppercase hexstring of 40 characters. FLAG is either 'P' or 'C'. + This function does first check whether that key has alreay ben put + into the trustdb and returns success in this case. Before a FPR + actually gets inserted, the user is asked by means of the pin-entry + whether this is actual wants he want to do. +*/ +int +agent_marktrusted (CTRL ctrl, const char *name, const char *fpr, int flag) +{ + int rc; + static char key[41]; + int keyflag; + char *desc; + + if (trustfp) + rewind (trustfp); + while (!(rc=read_list (key, &keyflag))) + { + if (!strcmp (key, fpr)) + return 0; + } + fclose (trustfp); + trustfp = NULL; + if (rc != -1) + return rc; /* error in the trustdb */ + + /* insert a new one */ + if (asprintf (&desc, + "Please verify that the certificate identified as:%%0A" + " \"%s\"%%0A" + "has the fingerprint:%%0A" + " %s", name, fpr) < 0 ) + return out_of_core (); + rc = agent_get_confirmation (ctrl, desc, "Correct", "No"); + free (desc); + if (rc) + return rc; + + if (asprintf (&desc, + "Do you ultimately trust%%0A" + " \"%s\"%%0A" + "to correctly certify user certificates?", + name) < 0 ) + return out_of_core (); + rc = agent_get_confirmation (ctrl, desc, "Yes", "No"); + free (desc); + if (rc) + return rc; + + /* now check again to avoid duplicates. Also open in append mode now */ + rc = open_list (1); + if (rc) + return rc; + rewind (trustfp); + while (!(rc=read_list (key, &keyflag))) + { + if (!strcmp (key, fpr)) + return 0; + } + if (rc != -1) + { + fclose (trustfp); + trustfp = NULL; + return rc; /* error in the trustdb */ + } + rc = 0; + + /* append the key */ + fflush (trustfp); + fputs ("\n# ", trustfp); + print_sanitized_string (trustfp, name, 0); + fprintf (trustfp, "\n%s %c\n", fpr, flag); + if (ferror (trustfp)) + rc = gpg_error (gpg_err_code_from_errno (errno)); + + /* close because we are in append mode */ + if (fclose (trustfp)) + rc = gpg_error (gpg_err_code_from_errno (errno)); + trustfp = NULL; + return rc; +} |