static gpg_error_t armor_data (char **r_string, const void *data, size_t datalen) { gpg_error_t err; struct b64state b64state; estream_t fp; long length; char *buffer; size_t nread; *r_string = NULL; fp = es_fopenmem (0, "rw"); if (!fp) return gpg_error_from_syserror (); if ((err=b64enc_start_es (&b64state, fp, "PGP PUBLIC KEY BLOCK")) || (err=b64enc_write (&b64state, data, datalen)) || (err = b64enc_finish (&b64state))) { es_fclose (fp); return err; } /* FIXME: To avoid the extra buffer allocation estream should provide a function to snatch the internal allocated memory from such a memory stream. */ length = es_ftell (fp); if (length < 0) { err = gpg_error_from_syserror (); es_fclose (fp); return err; } buffer = xtrymalloc (length+1); if (!buffer) { err = gpg_error_from_syserror (); es_fclose (fp); return err; } es_rewind (fp); if (es_read (fp, buffer, length, &nread)) { err = gpg_error_from_syserror (); es_fclose (fp); return err; } buffer[nread] = 0; es_fclose (fp); *r_string = buffer; return 0; }
/* Ask the dirmngr for the policy flags and return them as an estream * memory stream. If no policy flags are set, NULL is stored at * R_BUFFER. */ gpg_error_t wkd_get_policy_flags (const char *addrspec, estream_t *r_buffer) { gpg_error_t err; assuan_context_t ctx; struct wkd_get_parm_s parm; char *line = NULL; char *buffer = NULL; memset (&parm, 0, sizeof parm); *r_buffer = NULL; err = connect_dirmngr (&ctx); if (err) return err; line = es_bsprintf ("WKD_GET --policy-flags -- %s", addrspec); if (!line) { err = gpg_error_from_syserror (); goto leave; } if (strlen (line) + 2 >= ASSUAN_LINELENGTH) { err = gpg_error (GPG_ERR_TOO_LARGE); goto leave; } parm.memfp = es_fopenmem (0, "rwb"); if (!parm.memfp) { err = gpg_error_from_syserror (); goto leave; } err = assuan_transact (ctx, line, wkd_get_data_cb, &parm, NULL, NULL, wkd_get_status_cb, &parm); if (err) goto leave; es_rewind (parm.memfp); *r_buffer = parm.memfp; parm.memfp = 0; leave: es_free (buffer); es_fclose (parm.memfp); xfree (line); assuan_release (ctx); return err; }
static gpg_error_t collect_signeddata (void *cookie, const char *data) { receive_ctx_t ctx = cookie; if (!ctx->signeddata) if (!(ctx->signeddata = es_fopenmem (MAX_SIGNEDDATA, "w+b,samethread"))) return gpg_error_from_syserror (); if (data) es_fputs (data, ctx->signeddata); if (es_ferror (ctx->signeddata)) return gpg_error_from_syserror (); return 0; }
static gpg_error_t collect_signature (void *cookie, const char *data) { receive_ctx_t ctx = cookie; if (!ctx->signature) if (!(ctx->signature = es_fopenmem (MAX_SIGNATURE, "w+b,samethread"))) return gpg_error_from_syserror (); if (data) es_fputs (data, ctx->signature); if (es_ferror (ctx->signature)) return gpg_error_from_syserror (); if (!data) { verify_signature (ctx); } return 0; }
static gpg_error_t collect_encrypted (void *cookie, const char *data) { receive_ctx_t ctx = cookie; if (!ctx->encrypted) if (!(ctx->encrypted = es_fopenmem (MAX_ENCRYPTED, "w+b,samethread"))) return gpg_error_from_syserror (); if (data) es_fputs (data, ctx->encrypted); if (es_ferror (ctx->encrypted)) return gpg_error_from_syserror (); if (!data) { decrypt_data (ctx); } return 0; }
/* Helper to write mail to the output(s). */ gpg_error_t wks_send_mime (mime_maker_t mime) { gpg_error_t err; estream_t mail; /* Without any option we take a short path. */ if (!opt.use_sendmail && !opt.output) { es_set_binary (es_stdout); return mime_maker_make (mime, es_stdout); } mail = es_fopenmem (0, "w+b"); if (!mail) { err = gpg_error_from_syserror (); return err; } err = mime_maker_make (mime, mail); if (!err && opt.output) { es_rewind (mail); err = send_mail_to_file (mail, opt.output); } if (!err && opt.use_sendmail) { es_rewind (mail); err = send_mail (mail); } es_fclose (mail); return err; }
/* Decrypt the collected data. */ static void decrypt_data (receive_ctx_t ctx) { gpg_error_t err; ccparray_t ccp; const char **argv; int c; es_rewind (ctx->encrypted); if (!ctx->plaintext) ctx->plaintext = es_fopenmem (0, "w+b"); if (!ctx->plaintext) { err = gpg_error_from_syserror (); log_error ("error allocating space for plaintext: %s\n", gpg_strerror (err)); return; } ccparray_init (&ccp, 0); /* We limit the output to 64 KiB to avoid DoS using compression * tricks. A regular client will anyway only send a minimal key; * that is one w/o key signatures and attribute packets. */ ccparray_put (&ccp, "--max-output=0xf0000"); /*FIXME: Change s/F/1/ */ ccparray_put (&ccp, "--batch"); if (opt.verbose) ccparray_put (&ccp, "--verbose"); ccparray_put (&ccp, "--always-trust"); ccparray_put (&ccp, "--decrypt"); ccparray_put (&ccp, "--"); ccparray_put (&ccp, NULL); argv = ccparray_get (&ccp, NULL); if (!argv) { err = gpg_error_from_syserror (); goto leave; } err = gnupg_exec_tool_stream (opt.gpg_program, argv, ctx->encrypted, NULL, ctx->plaintext, decrypt_data_status_cb, ctx); if (err) { log_error ("decryption failed: %s\n", gpg_strerror (err)); goto leave; } if (opt.debug) { es_rewind (ctx->plaintext); log_debug ("plaintext: '"); while ((c = es_getc (ctx->plaintext)) != EOF) log_printf ("%c", c); log_printf ("'\n"); } es_rewind (ctx->plaintext); leave: xfree (argv); }
static gpg_error_t new_part (void *cookie, const char *mediatype, const char *mediasubtype) { receive_ctx_t ctx = cookie; gpg_error_t err = 0; ctx->collect_key_data = 0; ctx->collect_wkd_data = 0; if (!strcmp (mediatype, "application") && !strcmp (mediasubtype, "pgp-keys")) { log_info ("new '%s/%s' message part\n", mediatype, mediasubtype); if (ctx->key_data) { log_error ("we already got a key - ignoring this part\n"); err = gpg_error (GPG_ERR_FALSE); } else { ctx->key_data = es_fopenmem (0, "w+b"); if (!ctx->key_data) { err = gpg_error_from_syserror (); log_error ("error allocating space for key: %s\n", gpg_strerror (err)); } else { ctx->collect_key_data = 1; err = gpg_error (GPG_ERR_TRUE); /* We want the part decoded. */ } } } else if (!strcmp (mediatype, "application") && !strcmp (mediasubtype, "vnd.gnupg.wks")) { log_info ("new '%s/%s' message part\n", mediatype, mediasubtype); if (ctx->wkd_data) { log_error ("we already got a wkd part - ignoring this part\n"); err = gpg_error (GPG_ERR_FALSE); } else { ctx->wkd_data = es_fopenmem (0, "w+b"); if (!ctx->wkd_data) { err = gpg_error_from_syserror (); log_error ("error allocating space for key: %s\n", gpg_strerror (err)); } else { ctx->collect_wkd_data = 1; err = gpg_error (GPG_ERR_TRUE); /* We want the part decoded. */ } } } else { log_error ("unexpected '%s/%s' message part\n", mediatype, mediasubtype); err = gpg_error (GPG_ERR_FALSE); /* We do not want the part. */ } return err; }
/* Run gpg as a filter on KEY and write the output to a new stream * stored at R_NEWKEY. The new key will contain only the user id UID. * Returns 0 on success. Only one key is expected in KEY. If BINARY * is set the resulting key is returned as a binary (non-armored) * keyblock. */ gpg_error_t wks_filter_uid (estream_t *r_newkey, estream_t key, const char *uid, int binary) { gpg_error_t err; ccparray_t ccp; const char **argv = NULL; estream_t newkey; char *filterexp = NULL; *r_newkey = NULL; /* Open a memory stream. */ newkey = es_fopenmem (0, "w+b"); if (!newkey) { err = gpg_error_from_syserror (); log_error ("error allocating memory buffer: %s\n", gpg_strerror (err)); return err; } /* Prefix the key with the MIME content type. */ if (!binary) es_fputs ("Content-Type: application/pgp-keys\n" "\n", newkey); filterexp = es_bsprintf ("keep-uid=uid=%s", uid); if (!filterexp) { err = gpg_error_from_syserror (); log_error ("error allocating memory buffer: %s\n", gpg_strerror (err)); goto leave; } ccparray_init (&ccp, 0); ccparray_put (&ccp, "--no-options"); if (!opt.verbose) ccparray_put (&ccp, "--quiet"); else if (opt.verbose > 1) ccparray_put (&ccp, "--verbose"); ccparray_put (&ccp, "--batch"); ccparray_put (&ccp, "--status-fd=2"); ccparray_put (&ccp, "--always-trust"); if (!binary) ccparray_put (&ccp, "--armor"); ccparray_put (&ccp, "--import-options=import-export"); ccparray_put (&ccp, "--import-filter"); ccparray_put (&ccp, filterexp); ccparray_put (&ccp, "--import"); ccparray_put (&ccp, NULL); argv = ccparray_get (&ccp, NULL); if (!argv) { err = gpg_error_from_syserror (); goto leave; } err = gnupg_exec_tool_stream (opt.gpg_program, argv, key, NULL, newkey, key_status_cb, NULL); if (err) { log_error ("import/export failed: %s\n", gpg_strerror (err)); goto leave; } es_rewind (newkey); *r_newkey = newkey; newkey = NULL; leave: xfree (filterexp); xfree (argv); es_fclose (newkey); return err; }
/* Run gpg on KEY and store the primary fingerprint at R_FPR and the * list of mailboxes at R_MBOXES. Returns 0 on success; on error NULL * is stored at R_FPR and R_MBOXES and an error code is returned. * R_FPR may be NULL if the fingerprint is not needed. */ gpg_error_t wks_list_key (estream_t key, char **r_fpr, uidinfo_list_t *r_mboxes) { gpg_error_t err; ccparray_t ccp; const char **argv; estream_t listing; char *line = NULL; size_t length_of_line = 0; size_t maxlen; ssize_t len; char **fields = NULL; int nfields; int lnr; char *fpr = NULL; uidinfo_list_t mboxes = NULL; if (r_fpr) *r_fpr = NULL; *r_mboxes = NULL; /* Open a memory stream. */ listing = es_fopenmem (0, "w+b"); if (!listing) { err = gpg_error_from_syserror (); log_error ("error allocating memory buffer: %s\n", gpg_strerror (err)); return err; } ccparray_init (&ccp, 0); ccparray_put (&ccp, "--no-options"); if (!opt.verbose) ccparray_put (&ccp, "--quiet"); else if (opt.verbose > 1) ccparray_put (&ccp, "--verbose"); ccparray_put (&ccp, "--batch"); ccparray_put (&ccp, "--status-fd=2"); ccparray_put (&ccp, "--always-trust"); ccparray_put (&ccp, "--with-colons"); ccparray_put (&ccp, "--dry-run"); ccparray_put (&ccp, "--import-options=import-minimal,import-show"); ccparray_put (&ccp, "--import"); ccparray_put (&ccp, NULL); argv = ccparray_get (&ccp, NULL); if (!argv) { err = gpg_error_from_syserror (); goto leave; } err = gnupg_exec_tool_stream (opt.gpg_program, argv, key, NULL, listing, key_status_cb, NULL); if (err) { log_error ("import failed: %s\n", gpg_strerror (err)); goto leave; } es_rewind (listing); lnr = 0; maxlen = 2048; /* Set limit. */ while ((len = es_read_line (listing, &line, &length_of_line, &maxlen)) > 0) { lnr++; if (!maxlen) { log_error ("received line too long\n"); err = gpg_error (GPG_ERR_LINE_TOO_LONG); goto leave; } /* Strip newline and carriage return, if present. */ while (len > 0 && (line[len - 1] == '\n' || line[len - 1] == '\r')) line[--len] = '\0'; /* log_debug ("line '%s'\n", line); */ xfree (fields); fields = strtokenize (line, ":"); if (!fields) { err = gpg_error_from_syserror (); log_error ("strtokenize failed: %s\n", gpg_strerror (err)); goto leave; } for (nfields = 0; fields[nfields]; nfields++) ; if (!nfields) { err = gpg_error (GPG_ERR_INV_ENGINE); goto leave; } if (!strcmp (fields[0], "sec")) { /* gpg may return "sec" as the first record - but we do not * accept secret keys. */ err = gpg_error (GPG_ERR_NO_PUBKEY); goto leave; } if (lnr == 1 && strcmp (fields[0], "pub")) { /* First record is not a public key. */ err = gpg_error (GPG_ERR_INV_ENGINE); goto leave; } if (lnr > 1 && !strcmp (fields[0], "pub")) { /* More than one public key. */ err = gpg_error (GPG_ERR_TOO_MANY); goto leave; } if (!strcmp (fields[0], "sub") || !strcmp (fields[0], "ssb")) break; /* We can stop parsing here. */ if (!strcmp (fields[0], "fpr") && nfields > 9 && !fpr) { fpr = xtrystrdup (fields[9]); if (!fpr) { err = gpg_error_from_syserror (); goto leave; } } else if (!strcmp (fields[0], "uid") && nfields > 9) { /* Fixme: Unescape fields[9] */ if (!append_to_uidinfo_list (&mboxes, fields[9], parse_timestamp (fields[5], NULL))) { err = gpg_error_from_syserror (); goto leave; } } } if (len < 0 || es_ferror (listing)) { err = gpg_error_from_syserror (); log_error ("error reading memory stream\n"); goto leave; } if (!fpr) { err = gpg_error (GPG_ERR_NO_PUBKEY); goto leave; } if (r_fpr) { *r_fpr = fpr; fpr = NULL; } *r_mboxes = mboxes; mboxes = NULL; leave: xfree (fpr); free_uidinfo_list (mboxes); xfree (fields); es_free (line); xfree (argv); es_fclose (listing); return err; }
/* Get a key by fingerprint from gpg's keyring and make sure that the * mail address ADDRSPEC is included in the key. If EXACT is set the * returned user id must match Addrspec exactly and not just in the * addr-spec (mailbox) part. The key is returned as a new memory * stream at R_KEY. */ gpg_error_t wks_get_key (estream_t *r_key, const char *fingerprint, const char *addrspec, int exact) { gpg_error_t err; ccparray_t ccp; const char **argv = NULL; estream_t key = NULL; struct get_key_status_parm_s parm; char *filterexp = NULL; memset (&parm, 0, sizeof parm); *r_key = NULL; key = es_fopenmem (0, "w+b"); if (!key) { err = gpg_error_from_syserror (); log_error ("error allocating memory buffer: %s\n", gpg_strerror (err)); goto leave; } /* Prefix the key with the MIME content type. */ es_fputs ("Content-Type: application/pgp-keys\n" "\n", key); filterexp = es_bsprintf ("keep-uid=%s=%s", exact? "uid":"mbox", addrspec); if (!filterexp) { err = gpg_error_from_syserror (); log_error ("error allocating memory buffer: %s\n", gpg_strerror (err)); goto leave; } ccparray_init (&ccp, 0); ccparray_put (&ccp, "--no-options"); if (!opt.verbose) ccparray_put (&ccp, "--quiet"); else if (opt.verbose > 1) ccparray_put (&ccp, "--verbose"); ccparray_put (&ccp, "--batch"); ccparray_put (&ccp, "--status-fd=2"); ccparray_put (&ccp, "--always-trust"); ccparray_put (&ccp, "--armor"); ccparray_put (&ccp, "--export-options=export-minimal"); ccparray_put (&ccp, "--export-filter"); ccparray_put (&ccp, filterexp); ccparray_put (&ccp, "--export"); ccparray_put (&ccp, "--"); ccparray_put (&ccp, fingerprint); ccparray_put (&ccp, NULL); argv = ccparray_get (&ccp, NULL); if (!argv) { err = gpg_error_from_syserror (); goto leave; } parm.fpr = fingerprint; err = gnupg_exec_tool_stream (opt.gpg_program, argv, NULL, NULL, key, get_key_status_cb, &parm); if (!err && parm.count > 1) err = gpg_error (GPG_ERR_TOO_MANY); else if (!err && !parm.found) err = gpg_error (GPG_ERR_NOT_FOUND); if (err) { log_error ("export failed: %s\n", gpg_strerror (err)); goto leave; } es_rewind (key); *r_key = key; key = NULL; leave: es_fclose (key); xfree (argv); xfree (filterexp); return err; }
/* Ask the dirmngr for the submission address of a WKD server for the * mail address ADDRSPEC. On success the submission address is stored * at R_ADDRSPEC. */ gpg_error_t wkd_get_submission_address (const char *addrspec, char **r_addrspec) { gpg_error_t err; assuan_context_t ctx; struct wkd_get_parm_s parm; char *line = NULL; void *vp; char *buffer = NULL; char *p; memset (&parm, 0, sizeof parm); *r_addrspec = NULL; err = connect_dirmngr (&ctx); if (err) return err; line = es_bsprintf ("WKD_GET --submission-address -- %s", addrspec); if (!line) { err = gpg_error_from_syserror (); goto leave; } if (strlen (line) + 2 >= ASSUAN_LINELENGTH) { err = gpg_error (GPG_ERR_TOO_LARGE); goto leave; } parm.memfp = es_fopenmem (0, "rwb"); if (!parm.memfp) { err = gpg_error_from_syserror (); goto leave; } err = assuan_transact (ctx, line, wkd_get_data_cb, &parm, NULL, NULL, wkd_get_status_cb, &parm); if (err) goto leave; es_fputc (0, parm.memfp); if (es_fclose_snatch (parm.memfp, &vp, NULL)) { err = gpg_error_from_syserror (); goto leave; } buffer = vp; parm.memfp = NULL; p = strchr (buffer, '\n'); if (p) *p = 0; trim_spaces (buffer); if (!is_valid_mailbox (buffer)) { err = gpg_error (GPG_ERR_INV_USER_ID); goto leave; } *r_addrspec = xtrystrdup (buffer); if (!*r_addrspec) err = gpg_error_from_syserror (); leave: es_free (buffer); es_fclose (parm.memfp); xfree (line); assuan_release (ctx); return err; }