/* Handle inquiries from the dirmngr COMMAND. */ static gpg_error_t run_command_inq_cb (void *opaque, const char *line) { struct run_command_parm_s *parm = opaque; int rc = 0; if ( !strncmp (line, "SENDCERT", 8) && (line[8] == ' ' || !line[8]) ) { /* send the given certificate */ int err; ksba_cert_t cert; const unsigned char *der; size_t derlen; line += 8; if (!*line) return gpg_error (GPG_ERR_ASS_PARAMETER); err = gpgsm_find_cert (line, NULL, &cert); if (err) { log_error ("certificate not found: %s\n", gpg_strerror (err)); rc = gpg_error (GPG_ERR_NOT_FOUND); } else { der = ksba_cert_get_image (cert, &derlen); if (!der) rc = gpg_error (GPG_ERR_INV_CERT_OBJ); else rc = assuan_send_data (parm->ctx, der, derlen); ksba_cert_release (cert); } } else if ( !strncmp (line, "PRINTINFO", 9) && (line[9] == ' ' || !line[9]) ) { /* Simply show the message given in the argument. */ line += 9; log_info ("dirmngr: %s\n", line); } else { log_error ("unsupported inquiry `%s'\n", line); rc = gpg_error (GPG_ERR_ASS_UNKNOWN_INQUIRE); } return rc; }
/* Export a certificate and its private key. */ void gpgsm_p12_export (ctrl_t ctrl, const char *name, estream_t stream) { gpg_error_t err = 0; KEYDB_HANDLE hd; KEYDB_SEARCH_DESC *desc = NULL; Base64Context b64writer = NULL; ksba_writer_t writer; ksba_cert_t cert = NULL; const unsigned char *image; size_t imagelen; char *keygrip = NULL; char *prompt; void *data; size_t datalen; hd = keydb_new (0); if (!hd) { log_error ("keydb_new failed\n"); goto leave; } desc = xtrycalloc (1, sizeof *desc); if (!desc) { log_error ("allocating memory for export failed: %s\n", gpg_strerror (out_of_core ())); goto leave; } err = classify_user_id (name, desc, 0); if (err) { log_error ("key '%s' not found: %s\n", name, gpg_strerror (err)); goto leave; } /* Lookup the certificate and make sure that it is unique. */ err = keydb_search (hd, desc, 1); if (!err) { err = keydb_get_cert (hd, &cert); if (err) { log_error ("keydb_get_cert failed: %s\n", gpg_strerror (err)); goto leave; } next_ambiguous: err = keydb_search (hd, desc, 1); if (!err) { ksba_cert_t cert2 = NULL; if (!keydb_get_cert (hd, &cert2)) { if (gpgsm_certs_identical_p (cert, cert2)) { ksba_cert_release (cert2); goto next_ambiguous; } ksba_cert_release (cert2); } err = gpg_error (GPG_ERR_AMBIGUOUS_NAME); } else if (err == -1 || gpg_err_code (err) == GPG_ERR_EOF) err = 0; if (err) { log_error ("key '%s' not found: %s\n", name, gpg_strerror (err)); goto leave; } } keygrip = gpgsm_get_keygrip_hexstring (cert); if (!keygrip || gpgsm_agent_havekey (ctrl, keygrip)) { /* Note, that the !keygrip case indicates a bad certificate. */ err = gpg_error (GPG_ERR_NO_SECKEY); log_error ("can't export key '%s': %s\n", name, gpg_strerror (err)); goto leave; } image = ksba_cert_get_image (cert, &imagelen); if (!image) { log_error ("ksba_cert_get_image failed\n"); goto leave; } if (ctrl->create_pem) { print_short_info (cert, stream); es_putc ('\n', stream); } if (opt.p12_charset && ctrl->create_pem) { es_fprintf (stream, "The passphrase is %s encoded.\n\n", opt.p12_charset); } ctrl->pem_name = "PKCS12"; err = gpgsm_create_writer (&b64writer, ctrl, stream, &writer); if (err) { log_error ("can't create writer: %s\n", gpg_strerror (err)); goto leave; } prompt = gpgsm_format_keydesc (cert); err = export_p12 (ctrl, image, imagelen, prompt, keygrip, &data, &datalen); xfree (prompt); if (err) goto leave; err = ksba_writer_write (writer, data, datalen); xfree (data); if (err) { log_error ("write failed: %s\n", gpg_strerror (err)); goto leave; } if (ctrl->create_pem) { /* We want one certificate per PEM block */ err = gpgsm_finish_writer (b64writer); if (err) { log_error ("write failed: %s\n", gpg_strerror (err)); goto leave; } gpgsm_destroy_writer (b64writer); b64writer = NULL; } ksba_cert_release (cert); cert = NULL; leave: gpgsm_destroy_writer (b64writer); ksba_cert_release (cert); xfree (desc); keydb_release (hd); }
/* Handle a SENDCERT inquiry. */ static gpg_error_t inq_certificate (void *opaque, const char *line) { struct inq_certificate_parm_s *parm = opaque; int rc; const unsigned char *der; size_t derlen; int issuer_mode = 0; ksba_sexp_t ski = NULL; if (!strncmp (line, "SENDCERT", 8) && (line[8] == ' ' || !line[8])) { line += 8; } else if (!strncmp (line, "SENDCERT_SKI", 12) && (line[12]==' ' || !line[12])) { size_t n; /* Send a certificate where a sourceKeyIdentifier is included. */ line += 12; while (*line == ' ') line++; ski = make_simple_sexp_from_hexstr (line, &n); line += n; while (*line == ' ') line++; } else if (!strncmp (line, "SENDISSUERCERT", 14) && (line[14] == ' ' || !line[14])) { line += 14; issuer_mode = 1; } else if (!strncmp (line, "ISTRUSTED", 9) && (line[9]==' ' || !line[9])) { /* The server is asking us whether the certificate is a trusted root certificate. */ const char *s; size_t n; char fpr[41]; struct rootca_flags_s rootca_flags; line += 9; while (*line == ' ') line++; for (s=line,n=0; hexdigitp (s); s++, n++) ; if (*s || n != 40) return gpg_error (GPG_ERR_ASS_PARAMETER); for (s=line, n=0; n < 40; s++, n++) fpr[n] = (*s >= 'a')? (*s & 0xdf): *s; fpr[n] = 0; if (!gpgsm_agent_istrusted (parm->ctrl, NULL, fpr, &rootca_flags)) rc = assuan_send_data (parm->ctx, "1", 1); else rc = 0; return rc; } else { log_error ("unsupported inquiry `%s'\n", line); return gpg_error (GPG_ERR_ASS_UNKNOWN_INQUIRE); } if (!*line) { /* Send the current certificate. */ der = ksba_cert_get_image (issuer_mode? parm->issuer_cert : parm->cert, &derlen); if (!der) rc = gpg_error (GPG_ERR_INV_CERT_OBJ); else rc = assuan_send_data (parm->ctx, der, derlen); } else if (issuer_mode) { log_error ("sending specific issuer certificate back " "is not yet implemented\n"); rc = gpg_error (GPG_ERR_ASS_UNKNOWN_INQUIRE); } else { /* Send the given certificate. */ int err; ksba_cert_t cert; err = gpgsm_find_cert (line, ski, &cert); if (err) { log_error ("certificate not found: %s\n", gpg_strerror (err)); rc = gpg_error (GPG_ERR_NOT_FOUND); } else { der = ksba_cert_get_image (cert, &derlen); if (!der) rc = gpg_error (GPG_ERR_INV_CERT_OBJ); else rc = assuan_send_data (parm->ctx, der, derlen); ksba_cert_release (cert); } } xfree (ski); return rc; }
/* Export all certificates or just those given in NAMES. The output is written to STREAM. */ void gpgsm_export (ctrl_t ctrl, strlist_t names, estream_t stream) { KEYDB_HANDLE hd = NULL; KEYDB_SEARCH_DESC *desc = NULL; int ndesc; Base64Context b64writer = NULL; ksba_writer_t writer; strlist_t sl; ksba_cert_t cert = NULL; int rc=0; int count = 0; int i; duptable_t *dtable; dtable = create_duptable (); if (!dtable) { log_error ("creating duplicates table failed: %s\n", strerror (errno)); goto leave; } hd = keydb_new (0); if (!hd) { log_error ("keydb_new failed\n"); goto leave; } if (!names) ndesc = 1; else { for (sl=names, ndesc=0; sl; sl = sl->next, ndesc++) ; } desc = xtrycalloc (ndesc, sizeof *desc); if (!ndesc) { log_error ("allocating memory for export failed: %s\n", gpg_strerror (out_of_core ())); goto leave; } if (!names) desc[0].mode = KEYDB_SEARCH_MODE_FIRST; else { for (ndesc=0, sl=names; sl; sl = sl->next) { rc = classify_user_id (sl->d, desc+ndesc, 0); if (rc) { log_error ("key '%s' not found: %s\n", sl->d, gpg_strerror (rc)); rc = 0; } else ndesc++; } } /* If all specifications are done by fingerprint or keygrip, we switch to ephemeral mode so that _all_ currently available and matching certificates are exported. */ if (names && ndesc) { for (i=0; (i < ndesc && (desc[i].mode == KEYDB_SEARCH_MODE_FPR || desc[i].mode == KEYDB_SEARCH_MODE_FPR20 || desc[i].mode == KEYDB_SEARCH_MODE_FPR16 || desc[i].mode == KEYDB_SEARCH_MODE_KEYGRIP)); i++) ; if (i == ndesc) keydb_set_ephemeral (hd, 1); } while (!(rc = keydb_search (hd, desc, ndesc))) { unsigned char fpr[20]; int exists; if (!names) desc[0].mode = KEYDB_SEARCH_MODE_NEXT; rc = keydb_get_cert (hd, &cert); if (rc) { log_error ("keydb_get_cert failed: %s\n", gpg_strerror (rc)); goto leave; } gpgsm_get_fingerprint (cert, 0, fpr, NULL); rc = insert_duptable (dtable, fpr, &exists); if (rc) { log_error ("inserting into duplicates table failed: %s\n", gpg_strerror (rc)); goto leave; } if (!exists && count && !ctrl->create_pem) { log_info ("exporting more than one certificate " "is not possible in binary mode\n"); log_info ("ignoring other certificates\n"); break; } if (!exists) { const unsigned char *image; size_t imagelen; image = ksba_cert_get_image (cert, &imagelen); if (!image) { log_error ("ksba_cert_get_image failed\n"); goto leave; } if (ctrl->create_pem) { if (count) es_putc ('\n', stream); print_short_info (cert, stream); es_putc ('\n', stream); } count++; if (!b64writer) { ctrl->pem_name = "CERTIFICATE"; rc = gpgsm_create_writer (&b64writer, ctrl, stream, &writer); if (rc) { log_error ("can't create writer: %s\n", gpg_strerror (rc)); goto leave; } } rc = ksba_writer_write (writer, image, imagelen); if (rc) { log_error ("write error: %s\n", gpg_strerror (rc)); goto leave; } if (ctrl->create_pem) { /* We want one certificate per PEM block */ rc = gpgsm_finish_writer (b64writer); if (rc) { log_error ("write failed: %s\n", gpg_strerror (rc)); goto leave; } gpgsm_destroy_writer (b64writer); b64writer = NULL; } } ksba_cert_release (cert); cert = NULL; } if (rc && rc != -1) log_error ("keydb_search failed: %s\n", gpg_strerror (rc)); else if (b64writer) { rc = gpgsm_finish_writer (b64writer); if (rc) { log_error ("write failed: %s\n", gpg_strerror (rc)); goto leave; } } leave: gpgsm_destroy_writer (b64writer); ksba_cert_release (cert); xfree (desc); keydb_release (hd); destroy_duptable (dtable); }