/* If STRING includes known @FOO@ macros, replace these macros and return a new static string. Warning: STRING must have been allocated statically. Note that this function allocates memory which will not be released (similar to gettext). */ const char * map_static_macro_string (const char *string) { const char *s, *s2, *s3, *value; membuf_t mb; char *p; if ((s = already_mapped (string))) return s; s = string; value = find_macro (s, &s2, &s3); if (!value) return string; /* No macros at all. */ init_membuf (&mb, strlen (string) + 100); do { put_membuf (&mb, s, s2 - s); put_membuf_str (&mb, value); s = s3 + 1; } while ((value = find_macro (s, &s2, &s3))); put_membuf_str (&mb, s); put_membuf (&mb, "", 1); p = get_membuf_shrink (&mb, NULL); if (!p) log_fatal ("map_static_macro_string failed: %s\n", strerror (errno)); return store_mapping (string, p); }
/* The thread started by start_reader. */ static void * reader_thread_main (void *arg) { struct reader_thread_parms *parm = arg; char buffer[4096]; int nread; while ( (nread = npth_read (parm->fd, buffer, sizeof buffer)) ) { if (nread < 0) { if (errno == EINTR) continue; *parm->err_addr = gpg_error_from_syserror (); break; /* Read error. */ } put_membuf (parm->mb, buffer, nread); } if (close (parm->fd)) log_error ("closing reader fd %d failed: %s\n", parm->fd, strerror (errno)); xfree (parm); return NULL; }
/* Return the S2K iteration count as computed by gpg-agent. */ gpg_error_t agent_get_s2k_count (unsigned long *r_count) { gpg_error_t err; membuf_t data; char *buf; *r_count = 0; err = start_agent (0); if (err) return err; init_membuf (&data, 32); err = assuan_transact (agent_ctx, "GETINFO s2k_count", membuf_data_cb, &data, NULL, NULL, NULL, NULL); if (err) xfree (get_membuf (&data, NULL)); else { put_membuf (&data, "", 1); buf = get_membuf (&data, NULL); if (!buf) err = gpg_error_from_syserror (); else { *r_count = strtoul (buf, NULL, 10); xfree (buf); } } return err; }
static gpg_error_t scd_atr_data_cb (void *opaque, const void *data, size_t datalen) { membuf_t *mb = opaque; put_membuf (mb, data, datalen); return 0; }
static gpg_error_t get_cached_cert_data_cb (void *opaque, const void *buffer, size_t length) { struct membuf *mb = opaque; if (buffer) put_membuf (mb, buffer, length); return 0; }
static gpg_error_t membuf_data_cb (void *opaque, const void *buffer, size_t length) { membuf_t *data = opaque; if (buffer) put_membuf (data, buffer, length); return 0; }
/* Use the assuan machinery to read the ATR. */ static void reload_data (GpaCMUnknown *card) { gpg_error_t err, operr; char command[100]; gpgme_ctx_t gpgagent; membuf_t mb; char *buf; gpgagent = GPA_CM_OBJECT (card)->agent_ctx; g_return_if_fail (gpgagent); card->reloading++; init_membuf (&mb, 512); err = gpgme_op_assuan_transact_ext (gpgagent, "SCD APDU --dump-atr", scd_atr_data_cb, &mb, NULL, NULL, NULL, NULL, &operr); if (!err) err = operr; if (!err) { put_membuf (&mb, "", 1); buf = get_membuf (&mb, NULL); if (buf) { buf = g_strdup_printf ("\n%s\n%s", _("The ATR of the card is:"), buf); gtk_label_set_text (GTK_LABEL (card->label), buf); g_free (buf); } else gtk_label_set_text (GTK_LABEL (card->label), ""); } else { g_free (get_membuf (&mb, NULL)); if (gpg_err_code (err) == GPG_ERR_CARD_NOT_PRESENT) ; /* Lost the card. */ else g_debug ("assuan command `%s' failed: %s <%s>\n", command, gpg_strerror (err), gpg_strsource (err)); gtk_label_set_text (GTK_LABEL (card->label), ""); } card->reloading--; }
/* The thread started by start_reader. */ static void * reader_thread_main (void *arg) { gpg_error_t err = 0; struct reader_thread_parms *parm = arg; char buffer[4096]; int nread; while ( (nread = npth_read (parm->fd, buffer, sizeof buffer)) ) { if (nread < 0) { if (errno == EINTR) continue; err = my_error_from_syserror (); break; /* Read error. */ } if (parm->stream) { const char *p = buffer; size_t nwritten; while (nread) { err = es_write (parm->stream, p, nread, &nwritten); if (err) { log_error ("writing stream failed: %s\n", gpg_strerror (err)); goto leave; } nread -= nwritten; p += nwritten; } } else put_membuf (parm->mb, buffer, nread); } leave: *parm->err_addr = err; if (close (parm->fd)) log_error ("closing reader fd %d failed: %s\n", parm->fd, strerror (errno)); xfree (parm); return NULL; }
/* Lookup helpers*/ static gpg_error_t lookup_cb (void *opaque, const void *buffer, size_t length) { struct lookup_parm_s *parm = opaque; size_t len; char *buf; ksba_cert_t cert; int rc; if (parm->error) return 0; if (buffer) { put_membuf (&parm->data, buffer, length); return 0; } /* END encountered - process what we have */ buf = get_membuf (&parm->data, &len); if (!buf) { parm->error = gpg_error (GPG_ERR_ENOMEM); return 0; } rc = ksba_cert_new (&cert); if (rc) { parm->error = rc; return 0; } rc = ksba_cert_init_from_mem (cert, buf, len); if (rc) { log_error ("failed to parse a certificate: %s\n", gpg_strerror (rc)); } else { parm->cb (parm->cb_value, cert); } ksba_cert_release (cert); init_membuf (&parm->data, 4096); return 0; }
/* Fill the app_selection box with the available applications. */ static void setup_app_selector (GpaCardManager *cardman) { gpg_error_t err, operr; membuf_t mb; char *string; char *p, *p0, *p1; if (!cardman->gpgagent || !cardman->app_selector) return; init_membuf (&mb, 256); err = gpgme_op_assuan_transact_ext (cardman->gpgagent, "SCD GETINFO app_list", setup_app_selector_data_cb, &mb, NULL, NULL, NULL, NULL, &operr); if (err || operr) { g_free (get_membuf (&mb, NULL)); return; } /* Make sure the data is a string and get it. */ put_membuf (&mb, "", 1); string = get_membuf (&mb, NULL); if (!string) return; /* Out of core. */ for (p=p0=string; *p; p++) { if (*p == '\n') { *p = 0; p1 = strchr (p0, ':'); if (p1) *p1 = 0; gtk_combo_box_append_text (GTK_COMBO_BOX (cardman->app_selector), p0); if (p[1]) p0 = p+1; } } g_free (string); }
/* Expands %i and %o in the args to the full temp files within the temp directory. */ static int expand_args(struct exec_info *info,const char *args_in) { const char *ch = args_in; membuf_t command; info->flags.use_temp_files=0; info->flags.keep_temp_files=0; if(DBG_EXTPROG) log_debug("expanding string \"%s\"\n",args_in); init_membuf (&command, 100); while(*ch!='\0') { if(*ch=='%') { char *append=NULL; ch++; switch(*ch) { case 'O': info->flags.keep_temp_files=1; /* fall through */ case 'o': /* out */ if(!info->flags.madedir) { if(make_tempdir(info)) goto fail; } append=info->tempfile_out; info->flags.use_temp_files=1; break; case 'I': info->flags.keep_temp_files=1; /* fall through */ case 'i': /* in */ if(!info->flags.madedir) { if(make_tempdir(info)) goto fail; } append=info->tempfile_in; info->flags.use_temp_files=1; break; case '%': append="%"; break; } if(append) put_membuf_str (&command, append); } else put_membuf (&command, ch, 1); ch++; } put_membuf (&command, "", 1); /* Terminate string. */ info->command = get_membuf (&command, NULL); if (!info->command) return gpg_error_from_syserror (); if(DBG_EXTPROG) log_debug("args expanded to \"%s\", use %u, keep %u\n",info->command, info->flags.use_temp_files,info->flags.keep_temp_files); return 0; fail: xfree (get_membuf (&command, NULL)); return G10ERR_GENERAL; }
/* Note: All strings shall be UTF-8. On success the caller needs to free the string stored at R_PASSPHRASE. On error NULL will be stored at R_PASSPHRASE and an appropriate fpf error code returned. */ gpg_error_t agent_get_passphrase (const char *cache_id, const char *err_msg, const char *prompt, const char *desc_msg, int repeat, int check, char **r_passphrase) { int rc; char line[ASSUAN_LINELENGTH]; char *arg1 = NULL; char *arg2 = NULL; char *arg3 = NULL; char *arg4 = NULL; membuf_t data; *r_passphrase = NULL; rc = start_agent (0); if (rc) return rc; /* Check that the gpg-agent understands the repeat option. */ if (assuan_transact (agent_ctx, "GETINFO cmd_has_option GET_PASSPHRASE repeat", NULL, NULL, NULL, NULL, NULL, NULL)) return gpg_error (GPG_ERR_NOT_SUPPORTED); if (cache_id && *cache_id) if (!(arg1 = percent_plus_escape (cache_id))) goto no_mem; if (err_msg && *err_msg) if (!(arg2 = percent_plus_escape (err_msg))) goto no_mem; if (prompt && *prompt) if (!(arg3 = percent_plus_escape (prompt))) goto no_mem; if (desc_msg && *desc_msg) if (!(arg4 = percent_plus_escape (desc_msg))) goto no_mem; snprintf (line, DIM(line)-1, "GET_PASSPHRASE --data --repeat=%d%s -- %s %s %s %s", repeat, check? " --check --qualitybar":"", arg1? arg1:"X", arg2? arg2:"X", arg3? arg3:"X", arg4? arg4:"X"); line[DIM(line)-1] = 0; xfree (arg1); xfree (arg2); xfree (arg3); xfree (arg4); init_membuf_secure (&data, 64); rc = assuan_transact (agent_ctx, line, membuf_data_cb, &data, default_inq_cb, NULL, NULL, NULL); if (rc) xfree (get_membuf (&data, NULL)); else { put_membuf (&data, "", 1); *r_passphrase = get_membuf (&data, NULL); if (!*r_passphrase) rc = gpg_error_from_syserror (); } return rc; no_mem: rc = gpg_error_from_syserror (); xfree (arg1); xfree (arg2); xfree (arg3); xfree (arg4); return rc; }
/* Assume that the reader is at a pkcs#12 message and try to import certificates from that stupid format. We will transfer secret keys to the agent. */ static gpg_error_t parse_p12 (ctrl_t ctrl, ksba_reader_t reader, struct stats_s *stats) { gpg_error_t err = 0; char buffer[1024]; size_t ntotal, nread; membuf_t p12mbuf; char *p12buffer = NULL; size_t p12buflen; size_t p12bufoff; gcry_mpi_t *kparms = NULL; struct rsa_secret_key_s sk; char *passphrase = NULL; unsigned char *key = NULL; size_t keylen; void *kek = NULL; size_t keklen; unsigned char *wrappedkey = NULL; size_t wrappedkeylen; gcry_cipher_hd_t cipherhd = NULL; gcry_sexp_t s_key = NULL; unsigned char grip[20]; int bad_pass = 0; int i; struct store_cert_parm_s store_cert_parm; memset (&store_cert_parm, 0, sizeof store_cert_parm); store_cert_parm.ctrl = ctrl; store_cert_parm.stats = stats; init_membuf (&p12mbuf, 4096); ntotal = 0; while (!(err = ksba_reader_read (reader, buffer, sizeof buffer, &nread))) { if (ntotal >= MAX_P12OBJ_SIZE*1024) { /* Arbitrary limit to avoid DoS attacks. */ err = gpg_error (GPG_ERR_TOO_LARGE); log_error ("pkcs#12 object is larger than %dk\n", MAX_P12OBJ_SIZE); break; } put_membuf (&p12mbuf, buffer, nread); ntotal += nread; } if (gpg_err_code (err) == GPG_ERR_EOF) err = 0; if (!err) { p12buffer = get_membuf (&p12mbuf, &p12buflen); if (!p12buffer) err = gpg_error_from_syserror (); } if (err) { log_error (_("error reading input: %s\n"), gpg_strerror (err)); goto leave; } /* GnuPG 2.0.4 accidently created binary P12 files with the string "The passphrase is %s encoded.\n\n" prepended to the ASN.1 data. We fix that here. */ if (p12buflen > 29 && !memcmp (p12buffer, "The passphrase is ", 18)) { for (p12bufoff=18; p12bufoff < p12buflen && p12buffer[p12bufoff] != '\n'; p12bufoff++) ; p12bufoff++; if (p12bufoff < p12buflen && p12buffer[p12bufoff] == '\n') p12bufoff++; } else p12bufoff = 0; err = gpgsm_agent_ask_passphrase (ctrl, i18n_utf8 ("Please enter the passphrase to unprotect the PKCS#12 object."), 0, &passphrase); if (err) goto leave; kparms = p12_parse (p12buffer + p12bufoff, p12buflen - p12bufoff, passphrase, store_cert_cb, &store_cert_parm, &bad_pass); xfree (passphrase); passphrase = NULL; if (!kparms) { log_error ("error parsing or decrypting the PKCS#12 file\n"); err = gpg_error (GPG_ERR_INV_OBJ); goto leave; } /* print_mpi (" n", kparms[0]); */ /* print_mpi (" e", kparms[1]); */ /* print_mpi (" d", kparms[2]); */ /* print_mpi (" p", kparms[3]); */ /* print_mpi (" q", kparms[4]); */ /* print_mpi ("dmp1", kparms[5]); */ /* print_mpi ("dmq1", kparms[6]); */ /* print_mpi (" u", kparms[7]); */ sk.n = kparms[0]; sk.e = kparms[1]; sk.d = kparms[2]; sk.q = kparms[3]; sk.p = kparms[4]; sk.u = kparms[7]; err = rsa_key_check (&sk); if (err) goto leave; /* print_mpi (" n", sk.n); */ /* print_mpi (" e", sk.e); */ /* print_mpi (" d", sk.d); */ /* print_mpi (" p", sk.p); */ /* print_mpi (" q", sk.q); */ /* print_mpi (" u", sk.u); */ /* Create an S-expresion from the parameters. */ err = gcry_sexp_build (&s_key, NULL, "(private-key(rsa(n%m)(e%m)(d%m)(p%m)(q%m)(u%m)))", sk.n, sk.e, sk.d, sk.p, sk.q, sk.u, NULL); for (i=0; i < 8; i++) gcry_mpi_release (kparms[i]); gcry_free (kparms); kparms = NULL; if (err) { log_error ("failed to create S-expression from key: %s\n", gpg_strerror (err)); goto leave; } /* Compute the keygrip. */ if (!gcry_pk_get_keygrip (s_key, grip)) { err = gpg_error (GPG_ERR_GENERAL); log_error ("can't calculate keygrip\n"); goto leave; } log_printhex ("keygrip=", grip, 20); /* Convert to canonical encoding using a function which pads it to a multiple of 64 bits. We need this padding for AESWRAP. */ err = make_canon_sexp_pad (s_key, 1, &key, &keylen); if (err) { log_error ("error creating canonical S-expression\n"); goto leave; } gcry_sexp_release (s_key); s_key = NULL; /* Get the current KEK. */ err = gpgsm_agent_keywrap_key (ctrl, 0, &kek, &keklen); if (err) { log_error ("error getting the KEK: %s\n", gpg_strerror (err)); goto leave; } /* Wrap the key. */ err = gcry_cipher_open (&cipherhd, GCRY_CIPHER_AES128, GCRY_CIPHER_MODE_AESWRAP, 0); if (err) goto leave; err = gcry_cipher_setkey (cipherhd, kek, keklen); if (err) goto leave; xfree (kek); kek = NULL; wrappedkeylen = keylen + 8; wrappedkey = xtrymalloc (wrappedkeylen); if (!wrappedkey) { err = gpg_error_from_syserror (); goto leave; } err = gcry_cipher_encrypt (cipherhd, wrappedkey, wrappedkeylen, key, keylen); if (err) goto leave; xfree (key); key = NULL; gcry_cipher_close (cipherhd); cipherhd = NULL; /* Send the wrapped key to the agent. */ err = gpgsm_agent_import_key (ctrl, wrappedkey, wrappedkeylen); if (!err) { stats->count++; stats->secret_read++; stats->secret_imported++; } else if ( gpg_err_code (err) == GPG_ERR_EEXIST ) { err = 0; stats->count++; stats->secret_read++; stats->secret_dups++; } /* If we did not get an error from storing the secret key we return a possible error from parsing the certificates. We do this after storing the secret keys so that a bad certificate does not inhibit our chance to store the secret key. */ if (!err && store_cert_parm.err) err = store_cert_parm.err; leave: if (kparms) { for (i=0; i < 8; i++) gcry_mpi_release (kparms[i]); gcry_free (kparms); kparms = NULL; } xfree (key); gcry_sexp_release (s_key); xfree (passphrase); gcry_cipher_close (cipherhd); xfree (wrappedkey); xfree (kek); xfree (get_membuf (&p12mbuf, NULL)); xfree (p12buffer); if (bad_pass) { /* We only write a plain error code and not direct BAD_PASSPHRASE because the pkcs12 parser might issue this message multiple times, BAD_PASSPHRASE in general requires a keyID and parts of the import might actually succeed so that IMPORT_PROBLEM is also not appropriate. */ gpgsm_status_with_err_code (ctrl, STATUS_ERROR, "import.parsep12", GPG_ERR_BAD_PASSPHRASE); } return err; }
/** * assuan_inquire: * @ctx: An assuan context * @keyword: The keyword used for the inquire * @r_buffer: Returns an allocated buffer * @r_length: Returns the length of this buffer * @maxlen: If not 0, the size limit of the inquired data. * * A Server may use this to Send an inquire. r_buffer, r_length and * maxlen may all be NULL/0 to indicate that no real data is expected. * * Return value: 0 on success or an ASSUAN error code **/ assuan_error_t assuan_inquire (assuan_context_t ctx, const char *keyword, unsigned char **r_buffer, size_t *r_length, size_t maxlen) { assuan_error_t rc; struct membuf mb; char cmdbuf[LINELENGTH-10]; /* (10 = strlen ("INQUIRE ")+CR,LF) */ unsigned char *line, *p; int linelen; int nodataexpected; if (!ctx || !keyword || (10 + strlen (keyword) >= sizeof (cmdbuf))) return _assuan_error (ASSUAN_Invalid_Value); nodataexpected = !r_buffer && !r_length && !maxlen; if (!nodataexpected && (!r_buffer || !r_length)) return _assuan_error (ASSUAN_Invalid_Value); if (!ctx->is_server) return _assuan_error (ASSUAN_Not_A_Server); if (ctx->in_inquire) return _assuan_error (ASSUAN_Nested_Commands); ctx->in_inquire = 1; if (nodataexpected) memset (&mb, 0, sizeof mb); /* avoid compiler warnings */ else init_membuf (&mb, maxlen? maxlen:1024, maxlen); strcpy (stpcpy (cmdbuf, "INQUIRE "), keyword); rc = assuan_write_line (ctx, cmdbuf); if (rc) goto leave; for (;;) { do { do rc = _assuan_read_line (ctx); while (_assuan_error_is_eagain (rc)); if (rc) goto leave; line = (unsigned char *) ctx->inbound.line; linelen = ctx->inbound.linelen; } while (*line == '#' || !linelen); if (line[0] == 'E' && line[1] == 'N' && line[2] == 'D' && (!line[3] || line[3] == ' ')) break; /* END command received*/ if (line[0] == 'C' && line[1] == 'A' && line[2] == 'N') { rc = _assuan_error (ASSUAN_Canceled); goto leave; } if (line[0] != 'D' || line[1] != ' ' || nodataexpected) { rc = _assuan_error (ASSUAN_Unexpected_Command); goto leave; } if (linelen < 3) continue; line += 2; linelen -= 2; p = line; while (linelen) { for (;linelen && *p != '%'; linelen--, p++) ; put_membuf (&mb, line, p-line); if (linelen > 2) { /* handle escaping */ unsigned char tmp[1]; p++; *tmp = xtoi_2 (p); p += 2; linelen -= 3; put_membuf (&mb, tmp, 1); } line = p; } if (mb.too_large) { rc = _assuan_error (ASSUAN_Too_Much_Data); goto leave; } } if (!nodataexpected) { *r_buffer = get_membuf (&mb, r_length); if (!*r_buffer) rc = _assuan_error (ASSUAN_Out_Of_Core); } leave: if (!nodataexpected) free_membuf (&mb); ctx->in_inquire = 0; return rc; }
int _assuan_inquire_ext_cb (assuan_context_t ctx) { int rc; unsigned char *line; int linelen; struct membuf *mb; unsigned char *p; line = (unsigned char *) ctx->inbound.line; linelen = ctx->inbound.linelen; mb = ctx->inquire_membuf; if (line[0] == 'C' && line[1] == 'A' && line[2] == 'N') { rc = _assuan_error (ASSUAN_Canceled); goto leave; } if (line[0] == 'E' && line[1] == 'N' && line[2] == 'D' && (!line[3] || line[3] == ' ')) { rc = 0; goto leave; } if (line[0] != 'D' || line[1] != ' ' || mb == NULL) { rc = _assuan_error (ASSUAN_Unexpected_Command); goto leave; } if (linelen < 3) return 0; line += 2; linelen -= 2; p = line; while (linelen) { for (;linelen && *p != '%'; linelen--, p++) ; put_membuf (mb, line, p-line); if (linelen > 2) { /* handle escaping */ unsigned char tmp[1]; p++; *tmp = xtoi_2 (p); p += 2; linelen -= 3; put_membuf (mb, tmp, 1); } line = p; } if (mb->too_large) { rc = _assuan_error (ASSUAN_Too_Much_Data); goto leave; } return 0; leave: { size_t buf_len = 0; unsigned char *buf = NULL; if (mb) { buf = get_membuf (mb, &buf_len); if (!buf) rc = _assuan_error (ASSUAN_Out_Of_Core); free_membuf (mb); free (mb); ctx->inquire_membuf = NULL; } ctx->in_inquire = 0; rc = (ctx->inquire_cb) (ctx->inquire_cb_data, rc, buf, buf_len); } return rc; }
/** * assuan_inquire: * @ctx: An assuan context * @keyword: The keyword used for the inquire * @r_buffer: Returns an allocated buffer * @r_length: Returns the length of this buffer * @maxlen: If not 0, the size limit of the inquired data. * * A Server may use this to Send an inquire. r_buffer, r_length and * maxlen may all be NULL/0 to indicate that no real data is expected. * * Return value: 0 on success or an ASSUAN error code **/ gpg_error_t assuan_inquire (assuan_context_t ctx, const char *keyword, unsigned char **r_buffer, size_t *r_length, size_t maxlen) { gpg_error_t rc; struct membuf mb; char cmdbuf[LINELENGTH-10]; /* (10 = strlen ("INQUIRE ")+CR,LF) */ unsigned char *line, *p; int linelen; int nodataexpected; if (!ctx || !keyword || (10 + strlen (keyword) >= sizeof (cmdbuf))) return _assuan_error (ctx, GPG_ERR_ASS_INV_VALUE); nodataexpected = !r_buffer && !r_length && !maxlen; if (!nodataexpected && (!r_buffer || !r_length)) return _assuan_error (ctx, GPG_ERR_ASS_INV_VALUE); if (!ctx->is_server) return _assuan_error (ctx, GPG_ERR_ASS_NOT_A_SERVER); if (ctx->in_inquire) return _assuan_error (ctx, GPG_ERR_ASS_NESTED_COMMANDS); ctx->in_inquire = 1; if (nodataexpected) memset (&mb, 0, sizeof mb); /* avoid compiler warnings */ else init_membuf (ctx, &mb, maxlen? maxlen:1024, maxlen); strcpy (stpcpy (cmdbuf, "INQUIRE "), keyword); rc = assuan_write_line (ctx, cmdbuf); if (rc) goto leave; for (;;) { do { do rc = _assuan_read_line (ctx); while (_assuan_error_is_eagain (ctx, rc)); if (rc) goto leave; line = (unsigned char *) ctx->inbound.line; linelen = ctx->inbound.linelen; } while (*line == '#' || !linelen); if (line[0] == 'E' && line[1] == 'N' && line[2] == 'D' && (!line[3] || line[3] == ' ')) break; /* END command received*/ if (line[0] == 'C' && line[1] == 'A' && line[2] == 'N') { rc = _assuan_error (ctx, GPG_ERR_ASS_CANCELED); goto leave; } if (line[0] != 'D' || line[1] != ' ' || nodataexpected) { rc = _assuan_error (ctx, GPG_ERR_ASS_UNEXPECTED_CMD); goto leave; } if (linelen < 3) continue; line += 2; linelen -= 2; p = line; while (linelen) { for (;linelen && *p != '%'; linelen--, p++) ; put_membuf (ctx, &mb, line, p-line); if (linelen > 2) { /* handle escaping */ unsigned char tmp[1]; p++; *tmp = xtoi_2 (p); p += 2; linelen -= 3; put_membuf (ctx, &mb, tmp, 1); } line = p; } if (mb.too_large) { rc = _assuan_error (ctx, GPG_ERR_ASS_TOO_MUCH_DATA); goto leave; } } if (!nodataexpected) { *r_buffer = get_membuf (ctx, &mb, r_length); if (!*r_buffer) rc = _assuan_error (ctx, gpg_err_code_from_syserror ()); } leave: if (!nodataexpected) free_membuf (ctx, &mb); ctx->in_inquire = 0; return rc; }
gpg_error_t _assuan_inquire_ext_cb (assuan_context_t ctx) { gpg_error_t rc; unsigned char *line; int linelen; struct membuf *mb; unsigned char *p; line = (unsigned char *) ctx->inbound.line; linelen = ctx->inbound.linelen; mb = ctx->inquire_membuf; if (line[0] == 'C' && line[1] == 'A' && line[2] == 'N') { rc = _assuan_error (ctx, GPG_ERR_ASS_CANCELED); goto leave; } if (line[0] == 'E' && line[1] == 'N' && line[2] == 'D' && (!line[3] || line[3] == ' ')) { rc = 0; goto leave; } if (line[0] != 'D' || line[1] != ' ' || mb == NULL) { rc = _assuan_error (ctx, GPG_ERR_ASS_UNEXPECTED_CMD); goto leave; } if (linelen < 3) return 0; line += 2; linelen -= 2; p = line; while (linelen) { for (;linelen && *p != '%'; linelen--, p++) ; put_membuf (ctx, mb, line, p-line); if (linelen > 2) { /* handle escaping */ unsigned char tmp[1]; p++; *tmp = xtoi_2 (p); p += 2; linelen -= 3; put_membuf (ctx, mb, tmp, 1); } line = p; } if (mb->too_large) { rc = _assuan_error (ctx, GPG_ERR_ASS_TOO_MUCH_DATA); goto leave; } return 0; leave: { size_t buf_len = 0; unsigned char *buf = NULL; if (mb) { buf = get_membuf (ctx, mb, &buf_len); if (!buf) rc = _assuan_error (ctx, gpg_err_code_from_syserror ()); free_membuf (ctx, mb); free (mb); ctx->inquire_membuf = NULL; } ctx->in_inquire = 0; rc = (ctx->inquire_cb) (ctx->inquire_cb_data, rc, buf, buf_len); } return rc; }
/* Ask for a passphrase via gpg-agent. On success the caller needs to free the string stored at R_PASSPHRASE. On error NULL will be stored at R_PASSPHRASE and an appropriate gpg error code is returned. With REPEAT set to 1, gpg-agent will ask the user to repeat the just entered passphrase. CACHE_ID is a gpg-agent style passphrase cache id or NULL. ERR_MSG is a error message to be presented to the user (e.g. "bad passphrase - try again") or NULL. PROMPT is the prompt string to label the entry box, it may be NULL for a default one. DESC_MSG is a longer description to be displayed above the entry box, if may be NULL for a default one. If USE_SECMEM is true, the returned passphrase is retruned in secure memory. The length of all these strings is limited; they need to fit in their encoded form into a standard Assuan line (i.e less then about 950 characters). All strings shall be UTF-8. */ gpg_error_t gnupg_get_passphrase (const char *cache_id, const char *err_msg, const char *prompt, const char *desc_msg, int repeat, int check_quality, int use_secmem, char **r_passphrase) { gpg_error_t err; char line[ASSUAN_LINELENGTH]; const char *arg1 = NULL; char *arg2 = NULL; char *arg3 = NULL; char *arg4 = NULL; membuf_t data; *r_passphrase = NULL; err = start_agent (); if (err) return err; /* Check that the gpg-agent understands the repeat option. */ if (assuan_transact (agent_ctx, "GETINFO cmd_has_option GET_PASSPHRASE repeat", NULL, NULL, NULL, NULL, NULL, NULL)) return gpg_error (GPG_ERR_NOT_SUPPORTED); arg1 = cache_id && *cache_id? cache_id:NULL; if (err_msg && *err_msg) if (!(arg2 = percent_plus_escape (err_msg))) goto no_mem; if (prompt && *prompt) if (!(arg3 = percent_plus_escape (prompt))) goto no_mem; if (desc_msg && *desc_msg) if (!(arg4 = percent_plus_escape (desc_msg))) goto no_mem; snprintf (line, DIM(line)-1, "GET_PASSPHRASE --data %s--repeat=%d -- %s %s %s %s", check_quality? "--check ":"", repeat, arg1? arg1:"X", arg2? arg2:"X", arg3? arg3:"X", arg4? arg4:"X"); line[DIM(line)-1] = 0; xfree (arg2); xfree (arg3); xfree (arg4); if (use_secmem) init_membuf_secure (&data, 64); else init_membuf (&data, 64); err = assuan_transact (agent_ctx, line, membuf_data_cb, &data, default_inq_cb, NULL, NULL, NULL); /* Older Pinentries return the old assuan error code for canceled which gets translated bt libassuan to GPG_ERR_ASS_CANCELED and not to the code for a user cancel. Fix this here. */ if (err && gpg_err_source (err) && gpg_err_code (err) == GPG_ERR_ASS_CANCELED) err = gpg_err_make (gpg_err_source (err), GPG_ERR_CANCELED); if (err) { void *p; size_t n; p = get_membuf (&data, &n); if (p) wipememory (p, n); xfree (p); } else { put_membuf (&data, "", 1); *r_passphrase = get_membuf (&data, NULL); if (!*r_passphrase) err = gpg_error_from_syserror (); } return err; no_mem: err = gpg_error_from_syserror (); xfree (arg2); xfree (arg3); xfree (arg4); return err; }
/* Try to find KEY in the file FNAME. */ static char * findkey_fname (const char *key, const char *fname) { gpg_error_t err = 0; FILE *fp; int lnr = 0; int c; char *p, line[256]; int in_item = 0; membuf_t mb = MEMBUF_ZERO; fp = fopen (fname, "r"); if (!fp) { if (errno != ENOENT) { err = gpg_error_from_syserror (); log_error (_("can't open `%s': %s\n"), fname, gpg_strerror (err)); } return NULL; } while (fgets (line, DIM(line)-1, fp)) { lnr++; if (!*line || line[strlen(line)-1] != '\n') { /* Eat until end of line. */ while ( (c=getc (fp)) != EOF && c != '\n') ; err = gpg_error (*line? GPG_ERR_LINE_TOO_LONG : GPG_ERR_INCOMPLETE_LINE); log_error (_("file `%s', line %d: %s\n"), fname, lnr, gpg_strerror (err)); } else line[strlen(line)-1] = 0; /* Chop the LF. */ again: if (!in_item) { /* Allow for empty lines and spaces while not in an item. */ for (p=line; spacep (p); p++) ; if (!*p || *p == '#') continue; if (*line != '.' || spacep(line+1)) { log_info (_("file `%s', line %d: %s\n"), fname, lnr, _("ignoring garbage line")); continue; } trim_trailing_spaces (line); in_item = 1; if (!strcmp (line+1, key)) { /* Found. Start collecting. */ init_membuf (&mb, 1024); } continue; } /* If in an item only allow for comments in the first column and provide ". " as an escape sequence to allow for leading dots and hash marks in the actual text. */ if (*line == '#') continue; if (*line == '.') { if (spacep(line+1)) p = line + 2; else { trim_trailing_spaces (line); in_item = 0; if (is_membuf_ready (&mb)) break; /* Yep, found and collected the item. */ if (!line[1]) continue; /* Just an end of text dot. */ goto again; /* A new key line. */ } } else p = line; if (is_membuf_ready (&mb)) { put_membuf_str (&mb, p); put_membuf (&mb, "\n", 1); } } if ( !err && ferror (fp) ) { err = gpg_error_from_syserror (); log_error (_("error reading `%s', line %d: %s\n"), fname, lnr, gpg_strerror (err)); } fclose (fp); if (is_membuf_ready (&mb)) { /* We have collected something. */ if (err) { xfree (get_membuf (&mb, NULL)); return NULL; } else { put_membuf (&mb, "", 1); /* Terminate string. */ return get_membuf (&mb, NULL); } } else return NULL; }
/* Debug function to print the entire hosttable. */ gpg_error_t ks_hkp_print_hosttable (ctrl_t ctrl) { gpg_error_t err; int idx, idx2; hostinfo_t hi; membuf_t mb; time_t curtime; char *p, *died; const char *diedstr; err = ks_print_help (ctrl, "hosttable (idx, ipv6, ipv4, dead, name, time):"); if (err) return err; /* FIXME: We need a lock for the hosttable. */ curtime = gnupg_get_time (); for (idx=0; idx < hosttable_size; idx++) if ((hi=hosttable[idx])) { if (hi->dead && hi->died_at) { died = elapsed_time_string (hi->died_at, curtime); diedstr = died? died : "error"; } else diedstr = died = NULL; if (!hi->iporname_valid) { char *canon = NULL; xfree (hi->iporname); hi->iporname = NULL; /* Do a lookup just for the display purpose. */ if (hi->onion || hi->pool) ; else if (is_ip_address (hi->name)) { dns_addrinfo_t aibuf, ai; /* Turn the numerical IP address string into an AI and * then do a DNS PTR lookup. */ if (!resolve_dns_name (hi->name, 0, 0, SOCK_STREAM, &aibuf, &canon)) { if (canon && is_ip_address (canon)) { xfree (canon); canon = NULL; } for (ai = aibuf; !canon && ai; ai = ai->next) { resolve_dns_addr (ai->addr, ai->addrlen, DNS_WITHBRACKET, &canon); if (canon && is_ip_address (canon)) { /* We already have the numeric IP - no need to * display it a second time. */ xfree (canon); canon = NULL; } } } free_dns_addrinfo (aibuf); } else { dns_addrinfo_t aibuf, ai; /* Get the IP address as a string from a name. Note * that resolve_dns_addr allocates CANON on success * and thus terminates the loop. */ if (!resolve_dns_name (hi->name, 0, hi->v6? AF_INET6 : AF_INET, SOCK_STREAM, &aibuf, NULL)) { for (ai = aibuf; !canon && ai; ai = ai->next) { resolve_dns_addr (ai->addr, ai->addrlen, DNS_NUMERICHOST|DNS_WITHBRACKET, &canon); } } free_dns_addrinfo (aibuf); } hi->iporname = canon; hi->iporname_valid = 1; } err = ks_printf_help (ctrl, "%3d %s %s %s %s%s%s%s%s%s%s\n", idx, hi->onion? "O" : hi->v6? "6":" ", hi->v4? "4":" ", hi->dead? "d":" ", hi->name, hi->iporname? " (":"", hi->iporname? hi->iporname : "", hi->iporname? ")":"", diedstr? " (":"", diedstr? diedstr:"", diedstr? ")":"" ); xfree (died); if (err) return err; if (hi->cname) err = ks_printf_help (ctrl, " . %s", hi->cname); if (err) return err; if (hi->pool) { init_membuf (&mb, 256); put_membuf_printf (&mb, " . -->"); for (idx2 = 0; idx2 < hi->pool_len && hi->pool[idx2] != -1; idx2++) { put_membuf_printf (&mb, " %d", hi->pool[idx2]); if (hi->poolidx == hi->pool[idx2]) put_membuf_printf (&mb, "*"); } put_membuf( &mb, "", 1); p = get_membuf (&mb, NULL); if (!p) return gpg_error_from_syserror (); err = ks_print_help (ctrl, p); xfree (p); if (err) return err; } } return 0; }
/* Search for keys on the keyservers. The patterns are given in the string list TOKENS. */ gpg_error_t keyserver_search (ctrl_t ctrl, strlist_t tokens) { gpg_error_t err; char *searchstr; struct search_line_handler_parm_s parm; memset (&parm, 0, sizeof parm); if (!tokens) return 0; /* Return success if no patterns are given. */ if (!opt.keyserver) { log_error (_("no keyserver known (use option --keyserver)\n")); return gpg_error (GPG_ERR_NO_KEYSERVER); } /* Write global options */ /* for(temp=opt.keyserver_options.other;temp;temp=temp->next) */ /* fprintf(spawn->tochild,"OPTION %s\n",temp->d); */ /* Write per-keyserver options */ /* for(temp=keyserver->options;temp;temp=temp->next) */ /* fprintf(spawn->tochild,"OPTION %s\n",temp->d); */ { membuf_t mb; strlist_t item; init_membuf (&mb, 1024); for (item = tokens; item; item = item->next) { if (item != tokens) put_membuf (&mb, " ", 1); put_membuf_str (&mb, item->d); } put_membuf (&mb, "", 1); /* Append Nul. */ searchstr = get_membuf (&mb, NULL); if (!searchstr) { err = gpg_error_from_syserror (); goto leave; } } /* FIXME: Enable the next line */ /* log_info (_("searching for \"%s\" from %s\n"), searchstr, keyserver->uri); */ parm.ctrl = ctrl; if (searchstr) parm.searchstr_disp = utf8_to_native (searchstr, strlen (searchstr), 0); err = gpg_dirmngr_ks_search (ctrl, searchstr, search_line_handler, &parm); if (parm.not_found) { if (parm.searchstr_disp) log_info (_("key \"%s\" not found on keyserver\n"), parm.searchstr_disp); else log_info (_("key not found on keyserver\n")); } if (gpg_err_code (err) == GPG_ERR_NO_KEYSERVER) log_error (_("no keyserver known (use option --keyserver)\n")); else if (err) log_error ("error searching keyserver: %s\n", gpg_strerror (err)); /* switch(ret) */ /* { */ /* case KEYSERVER_SCHEME_NOT_FOUND: */ /* log_error(_("no handler for keyserver scheme '%s'\n"), */ /* opt.keyserver->scheme); */ /* break; */ /* case KEYSERVER_NOT_SUPPORTED: */ /* log_error(_("action '%s' not supported with keyserver " */ /* "scheme '%s'\n"), "search", opt.keyserver->scheme); */ /* break; */ /* case KEYSERVER_TIMEOUT: */ /* log_error(_("keyserver timed out\n")); */ /* break; */ /* case KEYSERVER_INTERNAL_ERROR: */ /* default: */ /* log_error(_("keyserver internal error\n")); */ /* break; */ /* } */ /* return gpg_error (GPG_ERR_KEYSERVER); */ leave: xfree (parm.desc); xfree (parm.searchstr_disp); xfree(searchstr); return err; }
/* Debug function to print the entire hosttable. */ gpg_error_t ks_hkp_print_hosttable (ctrl_t ctrl) { gpg_error_t err; int idx, idx2; hostinfo_t hi; membuf_t mb; time_t curtime; char *p, *died; const char *diedstr; err = ks_print_help (ctrl, "hosttable (idx, ipv6, ipv4, dead, name, time):"); if (err) return err; curtime = gnupg_get_time (); for (idx=0; idx < hosttable_size; idx++) if ((hi=hosttable[idx])) { if (hi->dead && hi->died_at) { died = elapsed_time_string (hi->died_at, curtime); diedstr = died? died : "error"; } else diedstr = died = NULL; err = ks_printf_help (ctrl, "%3d %s %s %s %s%s%s%s%s%s%s%s\n", idx, hi->v6? "6":" ", hi->v4? "4":" ", hi->dead? "d":" ", hi->name, hi->v6addr? " v6=":"", hi->v6addr? hi->v6addr:"", hi->v4addr? " v4=":"", hi->v4addr? hi->v4addr:"", diedstr? " (":"", diedstr? diedstr:"", diedstr? ")":"" ); xfree (died); if (err) return err; if (hi->cname) err = ks_printf_help (ctrl, " . %s", hi->cname); if (err) return err; if (hi->pool) { init_membuf (&mb, 256); put_membuf_printf (&mb, " . -->"); for (idx2=0; hi->pool[idx2] != -1; idx2++) { put_membuf_printf (&mb, " %d", hi->pool[idx2]); if (hi->poolidx == hi->pool[idx2]) put_membuf_printf (&mb, "*"); } put_membuf( &mb, "", 1); p = get_membuf (&mb, NULL); if (!p) return gpg_error_from_syserror (); err = ks_print_help (ctrl, p); xfree (p); if (err) return err; } } return 0; }