/* Create a key description for the CERT, this may be passed to the pinentry. The caller must free the returned string. NULL may be returned on error. */ char * gpgsm_format_keydesc (ksba_cert_t cert) { char *name, *subject, *buffer; ksba_isotime_t t; char created[20]; char expires[20]; char *sn; ksba_sexp_t sexp; char *orig_codeset; name = ksba_cert_get_subject (cert, 0); subject = name? gpgsm_format_name2 (name, 0) : NULL; ksba_free (name); name = NULL; sexp = ksba_cert_get_serial (cert); sn = sexp? gpgsm_format_serial (sexp) : NULL; ksba_free (sexp); ksba_cert_get_validity (cert, 0, t); if (*t) sprintf (created, "%.4s-%.2s-%.2s", t, t+4, t+6); else *created = 0; ksba_cert_get_validity (cert, 1, t); if (*t) sprintf (expires, "%.4s-%.2s-%.2s", t, t+4, t+6); else *expires = 0; orig_codeset = i18n_switchto_utf8 (); name = xtryasprintf (_("Please enter the passphrase to unlock the" " secret key for the X.509 certificate:\n" "\"%s\"\n" "S/N %s, ID 0x%08lX,\n" "created %s, expires %s.\n" ), subject? subject:"?", sn? sn: "?", gpgsm_get_short_fingerprint (cert, NULL), created, expires); i18n_switchback (orig_codeset); if (!name) { xfree (subject); xfree (sn); return NULL; } xfree (subject); xfree (sn); buffer = percent_plus_escape (name); xfree (name); return buffer; }
/* Ask the agent to pop up a confirmation dialog with the text DESC and an okay and cancel button. */ gpg_error_t gpg_agent_get_confirmation (const char *desc) { int rc; char *tmp; char line[ASSUAN_LINELENGTH]; rc = start_agent (0); if (rc) return rc; tmp = percent_plus_escape (desc); if (!tmp) return gpg_error_from_syserror (); snprintf (line, DIM(line)-1, "GET_CONFIRMATION %s", tmp); line[DIM(line)-1] = 0; xfree (tmp); rc = assuan_transact (agent_ctx, line, NULL, NULL, default_inq_cb, NULL, NULL, NULL); return rc; }
/* 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; }
/* Return an allocated utf-8 string describing the key PK. If ESCAPED is true spaces and control characters are percent or plus escaped. MODE describes the use of the key description; use one of the FORMAT_KEYDESC_ macros. */ char * gpg_format_keydesc (PKT_public_key *pk, int mode, int escaped) { char *uid; size_t uidlen; const char *algo_name; const char *timestr; char *orig_codeset; char *maink; char *desc; const char *prompt; const char *trailer = ""; int is_subkey; is_subkey = (pk->main_keyid[0] && pk->main_keyid[1] && pk->keyid[0] != pk->main_keyid[0] && pk->keyid[1] != pk->main_keyid[1]); algo_name = openpgp_pk_algo_name (pk->pubkey_algo); timestr = strtimestamp (pk->timestamp); uid = get_user_id (is_subkey? pk->main_keyid:pk->keyid, &uidlen); orig_codeset = i18n_switchto_utf8 (); if (is_subkey) maink = xtryasprintf (_(" (main key ID %s)"), keystr (pk->main_keyid)); else maink = NULL; switch (mode) { case FORMAT_KEYDESC_NORMAL: prompt = _("Please enter the passphrase to unlock the" " OpenPGP secret key:"); break; case FORMAT_KEYDESC_IMPORT: prompt = _("Please enter the passphrase to import the" " OpenPGP secret key:"); break; case FORMAT_KEYDESC_EXPORT: if (is_subkey) prompt = _("Please enter the passphrase to export the" " OpenPGP secret subkey:"); else prompt = _("Please enter the passphrase to export the" " OpenPGP secret key:"); break; case FORMAT_KEYDESC_DELKEY: if (is_subkey) prompt = _("Do you really want to permanently delete the" " OpenPGP secret subkey key:"); else prompt = _("Do you really want to permanently delete the" " OpenPGP secret key:"); trailer = "?"; break; default: prompt = "?"; break; } desc = xtryasprintf (_("%s\n" "\"%.*s\"\n" "%u-bit %s key, ID %s,\n" "created %s%s.\n%s"), prompt, (int)uidlen, uid, nbits_from_pk (pk), algo_name, keystr (pk->keyid), timestr, maink?maink:"", trailer); xfree (maink); xfree (uid); i18n_switchback (orig_codeset); if (escaped) { char *tmp = percent_plus_escape (desc); xfree (desc); desc = tmp; } return desc; }
/* Mount the container with name FILENAME at MOUNTPOINT. */ gpg_error_t g13_mount_container (ctrl_t ctrl, const char *filename, const char *mountpoint) { gpg_error_t err; dotlock_t lock; void *enckeyblob = NULL; size_t enckeybloblen; void *keyblob = NULL; size_t keybloblen; tupledesc_t tuples = NULL; size_t n; const unsigned char *value; int conttype; unsigned int rid; char *mountpoint_buffer = NULL; /* A quick check to see whether the container exists. */ if (access (filename, R_OK)) return gpg_error_from_syserror (); if (!mountpoint) { mountpoint_buffer = xtrystrdup ("/tmp/g13-XXXXXX"); if (!mountpoint_buffer) return gpg_error_from_syserror (); if (!gnupg_mkdtemp (mountpoint_buffer)) { err = gpg_error_from_syserror (); log_error (_("can't create directory '%s': %s\n"), "/tmp/g13-XXXXXX", gpg_strerror (err)); xfree (mountpoint_buffer); return err; } mountpoint = mountpoint_buffer; } /* Try to take a lock. */ lock = dotlock_create (filename, 0); if (!lock) { xfree (mountpoint_buffer); return gpg_error_from_syserror (); } if (dotlock_take (lock, 0)) { err = gpg_error_from_syserror (); goto leave; } else err = 0; /* Check again that the file exists. */ { struct stat sb; if (stat (filename, &sb)) { err = gpg_error_from_syserror (); goto leave; } } /* Read the encrypted keyblob. */ err = read_keyblob (filename, &enckeyblob, &enckeybloblen); if (err) goto leave; /* Decrypt that keyblob and store it in a tuple descriptor. */ err = decrypt_keyblob (ctrl, enckeyblob, enckeybloblen, &keyblob, &keybloblen); if (err) goto leave; xfree (enckeyblob); enckeyblob = NULL; err = create_tupledesc (&tuples, keyblob, keybloblen); if (!err) keyblob = NULL; else { if (gpg_err_code (err) == GPG_ERR_NOT_SUPPORTED) log_error ("unknown keyblob version\n"); goto leave; } if (opt.verbose) dump_keyblob (tuples); value = find_tuple (tuples, KEYBLOB_TAG_CONTTYPE, &n); if (!value || n != 2) conttype = 0; else conttype = (value[0] << 8 | value[1]); if (!be_is_supported_conttype (conttype)) { log_error ("content type %d is not supported\n", conttype); err = gpg_error (GPG_ERR_NOT_SUPPORTED); goto leave; } err = be_mount_container (ctrl, conttype, filename, mountpoint, tuples, &rid); if (!err) { err = mountinfo_add_mount (filename, mountpoint, conttype, rid, !!mountpoint_buffer); /* Fixme: What shall we do if this fails? Add a provisional mountinfo entry first and remove it on error? */ if (!err) { char *tmp = percent_plus_escape (mountpoint); if (!tmp) err = gpg_error_from_syserror (); else { g13_status (ctrl, STATUS_MOUNTPOINT, tmp, NULL); xfree (tmp); } } } leave: destroy_tupledesc (tuples); xfree (keyblob); xfree (enckeyblob); dotlock_destroy (lock); xfree (mountpoint_buffer); return err; }
static void test_percent_plus_escape (void) { static struct { const char *string; const char *expect; } tbl[] = { { "", "" }, { "a", "a", }, { " ", "+", }, { " ", "++" }, { "+ +", "%2B+%2B" }, { "\" \"", "%22+%22" }, { "%22", "%2522" }, { "%% ", "%25%25+" }, { "\n ABC\t", "%0A+ABC%09" }, { NULL, NULL } }; char *buf, *buf2; int i; size_t len; for (i=0; tbl[i].string; i++) { buf = percent_plus_escape (tbl[i].string); if (!buf) { fprintf (stderr, "out of core: %s\n", strerror (errno)); exit (2); } if (strcmp (buf, tbl[i].expect)) fail (i); buf2 = percent_plus_unescape (buf, 0); if (!buf2) { fprintf (stderr, "out of core: %s\n", strerror (errno)); exit (2); } if (strcmp (buf2, tbl[i].string)) fail (i); xfree (buf2); /* Now test the inplace conversion. */ len = percent_plus_unescape_inplace (buf, 0); buf[len] = 0; if (strcmp (buf, tbl[i].string)) fail (i); xfree (buf); } }
/* 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; }