diff --git a/sm/call-dirmngr.c b/sm/call-dirmngr.c index da3839349..8e2761b1e 100644 --- a/sm/call-dirmngr.c +++ b/sm/call-dirmngr.c @@ -1,1161 +1,1162 @@ /* call-dirmngr.c - Communication with the dirmngr * Copyright (C) 2002, 2003, 2005, 2007, 2008, * 2010 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 3 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, see . */ #include #include #include #include #include #include #include #include #include "gpgsm.h" #include #include #include "../common/i18n.h" #include "keydb.h" #include "../common/asshelp.h" struct membuf { size_t len; size_t size; char *buf; int out_of_core; }; /* fixme: We need a context for each thread or serialize the access to the dirmngr. */ static assuan_context_t dirmngr_ctx = NULL; static assuan_context_t dirmngr2_ctx = NULL; static int dirmngr_ctx_locked; static int dirmngr2_ctx_locked; struct inq_certificate_parm_s { ctrl_t ctrl; assuan_context_t ctx; ksba_cert_t cert; ksba_cert_t issuer_cert; }; struct isvalid_status_parm_s { ctrl_t ctrl; int seen; unsigned char fpr[20]; gnupg_isotime_t revoked_at; char *revocation_reason; /* malloced or NULL */ }; struct lookup_parm_s { ctrl_t ctrl; assuan_context_t ctx; void (*cb)(void *, ksba_cert_t); void *cb_value; struct membuf data; int error; }; struct run_command_parm_s { ctrl_t ctrl; assuan_context_t ctx; }; static gpg_error_t get_cached_cert (assuan_context_t ctx, const unsigned char *fpr, ksba_cert_t *r_cert); /* A simple implementation of a dynamic buffer. Use init_membuf() to create a buffer, put_membuf to append bytes and get_membuf to release and return the buffer. Allocation errors are detected but only returned at the final get_membuf(), this helps not to clutter the code with out of core checks. */ static void init_membuf (struct membuf *mb, int initiallen) { mb->len = 0; mb->size = initiallen; mb->out_of_core = 0; mb->buf = xtrymalloc (initiallen); if (!mb->buf) mb->out_of_core = 1; } static void put_membuf (struct membuf *mb, const void *buf, size_t len) { if (mb->out_of_core) return; if (mb->len + len >= mb->size) { char *p; mb->size += len + 1024; p = xtryrealloc (mb->buf, mb->size); if (!p) { mb->out_of_core = 1; return; } mb->buf = p; } memcpy (mb->buf + mb->len, buf, len); mb->len += len; } static void * get_membuf (struct membuf *mb, size_t *len) { char *p; if (mb->out_of_core) { xfree (mb->buf); mb->buf = NULL; return NULL; } p = mb->buf; *len = mb->len; mb->buf = NULL; mb->out_of_core = 1; /* don't allow a reuse */ return p; } /* Print a warning if the server's version number is less than our version number. Returns an error code on a connection problem. */ static gpg_error_t warn_version_mismatch (ctrl_t ctrl, assuan_context_t ctx, const char *servername, int mode) { return warn_server_version_mismatch (ctx, servername, mode, gpgsm_status2, ctrl, !opt.quiet); } /* This function prepares the dirmngr for a new session. The audit-events option is used so that other dirmngr clients won't get disturbed by such events. */ static void prepare_dirmngr (ctrl_t ctrl, assuan_context_t ctx, gpg_error_t err) { strlist_t server; if (!err) err = warn_version_mismatch (ctrl, ctx, DIRMNGR_NAME, 0); if (!err) { err = assuan_transact (ctx, "OPTION audit-events=1", NULL, NULL, NULL, NULL, NULL, NULL); if (gpg_err_code (err) == GPG_ERR_UNKNOWN_OPTION) err = 0; /* Allow the use of old dirmngr versions. */ } audit_log_ok (ctrl->audit, AUDIT_DIRMNGR_READY, err); if (!ctx || err) return; server = opt.keyserver; while (server) { char line[ASSUAN_LINELENGTH]; /* If the host is "ldap" we prefix the entire line with "ldap:" * to avoid an ambiguity on the server due to the introduction * of this optional prefix. */ snprintf (line, DIM (line), "LDAPSERVER %s%s", !strncmp (server->d, "ldap:", 5)? "ldap:":"", server->d); assuan_transact (ctx, line, NULL, NULL, NULL, NULL, NULL, NULL); /* The code below is not required because we don't return an error. */ /* err = [above call] */ /* if (gpg_err_code (err) == GPG_ERR_ASS_UNKNOWN_CMD) */ /* err = 0; /\* Allow the use of old dirmngr versions. *\/ */ server = server->next; } } /* Return a new assuan context for a Dirmngr connection. */ static gpg_error_t start_dirmngr_ext (ctrl_t ctrl, assuan_context_t *ctx_r) { gpg_error_t err; assuan_context_t ctx; if (opt.disable_dirmngr || ctrl->offline) return gpg_error (GPG_ERR_NO_DIRMNGR); if (*ctx_r) return 0; /* Note: if you change this to multiple connections, you also need to take care of the implicit option sending caching. */ err = start_new_dirmngr (&ctx, GPG_ERR_SOURCE_DEFAULT, opt.dirmngr_program, opt.autostart, opt.verbose, DBG_IPC, gpgsm_status2, ctrl); if (!opt.autostart && gpg_err_code (err) == GPG_ERR_NO_DIRMNGR) { static int shown; if (!shown) { shown = 1; log_info (_("no dirmngr running in this session\n")); } } prepare_dirmngr (ctrl, ctx, err); if (err) return err; *ctx_r = ctx; return 0; } static int start_dirmngr (ctrl_t ctrl) { gpg_error_t err; log_assert (! dirmngr_ctx_locked); dirmngr_ctx_locked = 1; err = start_dirmngr_ext (ctrl, &dirmngr_ctx); /* We do not check ERR but the existence of a context because the error might come from a failed command send to the dirmngr. Fixme: Why don't we close the drimngr context if we encountered an error in prepare_dirmngr? */ if (!dirmngr_ctx) dirmngr_ctx_locked = 0; return err; } static void release_dirmngr (ctrl_t ctrl) { (void)ctrl; if (!dirmngr_ctx_locked) log_error ("WARNING: trying to release a non-locked dirmngr ctx\n"); dirmngr_ctx_locked = 0; } static int start_dirmngr2 (ctrl_t ctrl) { gpg_error_t err; log_assert (! dirmngr2_ctx_locked); dirmngr2_ctx_locked = 1; err = start_dirmngr_ext (ctrl, &dirmngr2_ctx); if (!dirmngr2_ctx) dirmngr2_ctx_locked = 0; return err; } static void release_dirmngr2 (ctrl_t ctrl) { (void)ctrl; if (!dirmngr2_ctx_locked) log_error ("WARNING: trying to release a non-locked dirmngr2 ctx\n"); dirmngr2_ctx_locked = 0; } /* Handle a SENDCERT inquiry. */ static gpg_error_t inq_certificate (void *opaque, const char *line) { struct inq_certificate_parm_s *parm = opaque; const char *s; int rc; size_t n; const unsigned char *der; size_t derlen; int issuer_mode = 0; ksba_sexp_t ski = NULL; if ((s = has_leading_keyword (line, "SENDCERT"))) { line = s; } else if ((s = has_leading_keyword (line, "SENDCERT_SKI"))) { /* Send a certificate where a sourceKeyIdentifier is included. */ line = s; ski = make_simple_sexp_from_hexstr (line, &n); line += n; while (*line == ' ') line++; } else if ((s = has_leading_keyword (line, "SENDISSUERCERT"))) { line = s; issuer_mode = 1; } else if ((s = has_leading_keyword (line, "ISTRUSTED"))) { /* The server is asking us whether the certificate is a trusted root certificate. */ char fpr[41]; struct rootca_flags_s rootca_flags; line = s; 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 certificate 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 (parm->ctrl, line, ski, &cert, 1); + err = gpgsm_find_cert (parm->ctrl, line, ski, &cert, + FIND_CERT_ALLOW_AMBIG|FIND_CERT_WITH_EPHEM); 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; } /* Take a 20 byte hexencoded string and put it into the provided 20 byte buffer FPR in binary format. */ static int unhexify_fpr (const char *hexstr, unsigned char *fpr) { const char *s; int n; for (s=hexstr, n=0; hexdigitp (s); s++, n++) ; if (*s || (n != 40)) return 0; /* no fingerprint (invalid or wrong length). */ for (s=hexstr, n=0; *s; s += 2, n++) fpr[n] = xtoi_2 (s); return 1; /* okay */ } /* This is a helper to print diagnostics from dirmngr indicated by * WARNING or NOTE status lines. Returns true if the status LINE was * processed. */ static int warning_and_note_printer (const char *line) { const char *s, *s2; const char *warn = NULL; int is_note = 0; if ((s = has_leading_keyword (line, "WARNING"))) ; else if ((is_note = !!(s = has_leading_keyword (line, "NOTE")))) ; else return 0; /* Nothing to process. */ if ((s2 = has_leading_keyword (s, "no_crl_due_to_tor")) || (s2 = has_leading_keyword (s, "no_ldap_due_to_tor")) || (s2 = has_leading_keyword (s, "no_ocsp_due_to_tor"))) warn = _("Tor might be in use - network access is limited"); else warn = NULL; if (warn) { if (is_note) log_info (_("Note: %s\n"), warn); else log_info (_("WARNING: %s\n"), warn); if (s2) { while (*s2 && !spacep (s2)) s2++; while (*s2 && spacep (s2)) s2++; if (*s2) gpgsm_print_further_info ("%s", s2); } } return 1; /* Status line processed. */ } static gpg_error_t isvalid_status_cb (void *opaque, const char *line) { struct isvalid_status_parm_s *parm = opaque; const char *s; if ((s = has_leading_keyword (line, "PROGRESS"))) { if (parm->ctrl) { line = s; if (gpgsm_status (parm->ctrl, STATUS_PROGRESS, line)) return gpg_error (GPG_ERR_ASS_CANCELED); } } else if ((s = has_leading_keyword (line, "ONLY_VALID_IF_CERT_VALID"))) { parm->seen++; if (!*s || !unhexify_fpr (s, parm->fpr)) parm->seen++; /* Bump it to indicate an error. */ } else if ((s = has_leading_keyword (line, "REVOCATIONINFO"))) { if (*s && strlen (s) >= 15) { memcpy (parm->revoked_at, s, 15); parm->revoked_at[15] = 0; } s += 15; while (*s && spacep (s)) s++; xfree (parm->revocation_reason); parm->revocation_reason = *s? xtrystrdup (s) : NULL; } else if (warning_and_note_printer (line)) { } return 0; } /* Call the directory manager to check whether the certificate is valid Returns 0 for valid or usually one of the errors: GPG_ERR_CERTIFICATE_REVOKED GPG_ERR_NO_CRL_KNOWN GPG_ERR_CRL_TOO_OLD Values for USE_OCSP: 0 = Do CRL check. 1 = Do an OCSP check but fallback to CRL unless CRLs are disabled. 2 = Do only an OCSP check (used for the chain model). If R_REVOKED_AT pr R_REASON are not NULL and the certificate has been revoked the revocation time and the reason are copied to there. The caller needs to free R_REASON. */ gpg_error_t gpgsm_dirmngr_isvalid (ctrl_t ctrl, ksba_cert_t cert, ksba_cert_t issuer_cert, int use_ocsp, gnupg_isotime_t r_revoked_at, char **r_reason) { static int did_options; int rc; char *certid, *certfpr; char line[ASSUAN_LINELENGTH]; struct inq_certificate_parm_s parm; struct isvalid_status_parm_s stparm; if (r_revoked_at) *r_revoked_at = 0; if (r_reason) *r_reason = NULL; rc = start_dirmngr (ctrl); if (rc) return rc; certfpr = gpgsm_get_fingerprint_hexstring (cert, GCRY_MD_SHA1); certid = gpgsm_get_certid (cert); if (!certid) { log_error ("error getting the certificate ID\n"); release_dirmngr (ctrl); return gpg_error (GPG_ERR_GENERAL); } if (opt.verbose > 1) { char *fpr = gpgsm_get_fingerprint_string (cert, GCRY_MD_SHA1); log_info ("asking dirmngr about %s%s\n", fpr, use_ocsp? " (using OCSP)":""); xfree (fpr); } parm.ctx = dirmngr_ctx; parm.ctrl = ctrl; parm.cert = cert; parm.issuer_cert = issuer_cert; stparm.ctrl = ctrl; stparm.seen = 0; memset (stparm.fpr, 0, 20); stparm.revoked_at[0] = 0; stparm.revocation_reason = NULL; /* It is sufficient to send the options only once because we have * one connection per process only. */ if (!did_options) { if (opt.force_crl_refresh) assuan_transact (dirmngr_ctx, "OPTION force-crl-refresh=1", NULL, NULL, NULL, NULL, NULL, NULL); did_options = 1; } snprintf (line, DIM(line), "ISVALID%s %s%s%s", (use_ocsp == 2 || opt.no_crl_check) ? " --only-ocsp":"", certid, use_ocsp? " ":"", use_ocsp? certfpr:""); xfree (certid); xfree (certfpr); rc = assuan_transact (dirmngr_ctx, line, NULL, NULL, inq_certificate, &parm, isvalid_status_cb, &stparm); if (opt.verbose > 1) log_info ("response of dirmngr: %s\n", rc? gpg_strerror (rc): "okay"); if (gpg_err_code (rc) == GPG_ERR_CERT_REVOKED && !check_isotime (stparm.revoked_at)) { if (r_revoked_at) gnupg_copy_time (r_revoked_at, stparm.revoked_at); if (r_reason) { *r_reason = stparm.revocation_reason; stparm.revocation_reason = NULL; } } if (!rc && stparm.seen) { /* Need to also check the certificate validity. */ if (stparm.seen != 1) { log_error ("communication problem with dirmngr detected\n"); rc = gpg_error (GPG_ERR_INV_CRL); } else { ksba_cert_t rspcert = NULL; if (get_cached_cert (dirmngr_ctx, stparm.fpr, &rspcert)) { /* Ooops: Something went wrong getting the certificate from the dirmngr. Try our own cert store now. */ KEYDB_HANDLE kh; kh = keydb_new (ctrl); if (!kh) rc = gpg_error (GPG_ERR_ENOMEM); if (!rc) rc = keydb_search_fpr (ctrl, kh, stparm.fpr); if (!rc) rc = keydb_get_cert (kh, &rspcert); if (rc) { log_error ("unable to find the certificate used " "by the dirmngr: %s\n", gpg_strerror (rc)); rc = gpg_error (GPG_ERR_INV_CRL); } keydb_release (kh); } if (!rc) { rc = gpgsm_cert_use_ocsp_p (rspcert); if (rc) rc = gpg_error (GPG_ERR_INV_CRL); else { /* Note the no_dirmngr flag: This avoids checking this certificate over and over again. */ rc = gpgsm_validate_chain (ctrl, rspcert, GNUPG_ISOTIME_NONE, NULL, 0, NULL, VALIDATE_FLAG_NO_DIRMNGR, NULL); if (rc) { log_error ("invalid certificate used for CRL/OCSP: %s\n", gpg_strerror (rc)); rc = gpg_error (GPG_ERR_INV_CRL); } } } ksba_cert_release (rspcert); } } release_dirmngr (ctrl); xfree (stparm.revocation_reason); return rc; } /* 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; } /* Return a properly escaped pattern from NAMES. The only error return is NULL to indicate a malloc failure. */ static char * pattern_from_strlist (strlist_t names) { strlist_t sl; int n; const char *s; char *pattern, *p; for (n=0, sl=names; sl; sl = sl->next) { for (s=sl->d; *s; s++, n++) { if (*s == '%' || *s == ' ' || *s == '+') n += 2; } n++; } p = pattern = xtrymalloc (n+1); if (!pattern) return NULL; for (sl=names; sl; sl = sl->next) { for (s=sl->d; *s; s++) { switch (*s) { case '%': *p++ = '%'; *p++ = '2'; *p++ = '5'; break; case ' ': *p++ = '%'; *p++ = '2'; *p++ = '0'; break; case '+': *p++ = '%'; *p++ = '2'; *p++ = 'B'; break; default: *p++ = *s; break; } } *p++ = ' '; } if (p == pattern) *pattern = 0; /* is empty */ else p[-1] = '\0'; /* remove trailing blank */ return pattern; } static gpg_error_t lookup_status_cb (void *opaque, const char *line) { struct lookup_parm_s *parm = opaque; const char *s; if ((s = has_leading_keyword (line, "PROGRESS"))) { if (parm->ctrl) { line = s; if (gpgsm_status (parm->ctrl, STATUS_PROGRESS, line)) return gpg_error (GPG_ERR_ASS_CANCELED); } } else if ((s = has_leading_keyword (line, "TRUNCATED"))) { if (parm->ctrl) { line = s; gpgsm_status (parm->ctrl, STATUS_TRUNCATED, line); } } else if (warning_and_note_printer (line)) { } return 0; } /* Run the Directory Manager's lookup command using the pattern compiled from the strings given in NAMES or from URI. The caller must provide the callback CB which will be passed cert by cert. Note that CTRL is optional. With CACHE_ONLY the dirmngr will search only its own key cache. */ int gpgsm_dirmngr_lookup (ctrl_t ctrl, strlist_t names, const char *uri, int cache_only, void (*cb)(void*, ksba_cert_t), void *cb_value) { int rc; char line[ASSUAN_LINELENGTH]; struct lookup_parm_s parm; size_t len; assuan_context_t ctx; const char *s; if ((names && uri) || (!names && !uri)) return gpg_error (GPG_ERR_INV_ARG); /* The lookup function can be invoked from the callback of a lookup function, for example to walk the chain. */ if (!dirmngr_ctx_locked) { rc = start_dirmngr (ctrl); if (rc) return rc; ctx = dirmngr_ctx; } else if (!dirmngr2_ctx_locked) { rc = start_dirmngr2 (ctrl); if (rc) return rc; ctx = dirmngr2_ctx; } else { log_fatal ("both dirmngr contexts are in use\n"); } if (names) { char *pattern = pattern_from_strlist (names); if (!pattern) { if (ctx == dirmngr_ctx) release_dirmngr (ctrl); else release_dirmngr2 (ctrl); return out_of_core (); } snprintf (line, DIM(line), "LOOKUP%s %s", cache_only? " --cache-only":"", pattern); xfree (pattern); } else { for (s=uri; *s; s++) if (*s <= ' ') { if (ctx == dirmngr_ctx) release_dirmngr (ctrl); else release_dirmngr2 (ctrl); return gpg_error (GPG_ERR_INV_URI); } snprintf (line, DIM(line), "LOOKUP --url %s", uri); } parm.ctrl = ctrl; parm.ctx = ctx; parm.cb = cb; parm.cb_value = cb_value; parm.error = 0; init_membuf (&parm.data, 4096); rc = assuan_transact (ctx, line, lookup_cb, &parm, NULL, NULL, lookup_status_cb, &parm); xfree (get_membuf (&parm.data, &len)); if (ctx == dirmngr_ctx) release_dirmngr (ctrl); else release_dirmngr2 (ctrl); if (rc) return rc; return parm.error; } 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; } /* Return a certificate from the Directory Manager's cache. This function only returns one certificate which must be specified using the fingerprint FPR and will be stored at R_CERT. On error NULL is stored at R_CERT and an error code returned. Note that the caller must provide the locked dirmngr context CTX. */ static gpg_error_t get_cached_cert (assuan_context_t ctx, const unsigned char *fpr, ksba_cert_t *r_cert) { gpg_error_t err; char line[ASSUAN_LINELENGTH]; char hexfpr[2*20+1]; struct membuf mb; char *buf; size_t buflen = 0; ksba_cert_t cert; *r_cert = NULL; bin2hex (fpr, 20, hexfpr); snprintf (line, DIM(line), "LOOKUP --single --cache-only 0x%s", hexfpr); init_membuf (&mb, 4096); err = assuan_transact (ctx, line, get_cached_cert_data_cb, &mb, NULL, NULL, NULL, NULL); buf = get_membuf (&mb, &buflen); if (err) { xfree (buf); return err; } if (!buf) return gpg_error (GPG_ERR_ENOMEM); err = ksba_cert_new (&cert); if (err) { xfree (buf); return err; } err = ksba_cert_init_from_mem (cert, buf, buflen); xfree (buf); if (err) { log_error ("failed to parse a certificate: %s\n", gpg_strerror (err)); ksba_cert_release (cert); return err; } *r_cert = cert; return 0; } /* Run Command helpers*/ /* Fairly simple callback to write all output of dirmngr to stdout. */ static gpg_error_t run_command_cb (void *opaque, const void *buffer, size_t length) { (void)opaque; if (buffer) { if ( fwrite (buffer, length, 1, stdout) != 1 ) log_error ("error writing to stdout: %s\n", strerror (errno)); } return 0; } /* 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; const char *s; int rc = 0; if ((s = has_leading_keyword (line, "SENDCERT"))) { /* send the given certificate */ int err; ksba_cert_t cert; const unsigned char *der; size_t derlen; line = s; if (!*line) return gpg_error (GPG_ERR_ASS_PARAMETER); - err = gpgsm_find_cert (parm->ctrl, line, NULL, &cert, 1); + err = gpgsm_find_cert (parm->ctrl, line, NULL, &cert, + FIND_CERT_ALLOW_AMBIG); 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 ((s = has_leading_keyword (line, "PRINTINFO"))) { /* Simply show the message given in the argument. */ line = s; log_info ("dirmngr: %s\n", line); } else if ((s = has_leading_keyword (line, "ISTRUSTED"))) { /* The server is asking us whether the certificate is a trusted root certificate. */ char fpr[41]; struct rootca_flags_s rootca_flags; int n; line = s; 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 command inquiry '%s'\n", line); rc = gpg_error (GPG_ERR_ASS_UNKNOWN_INQUIRE); } return rc; } static gpg_error_t run_command_status_cb (void *opaque, const char *line) { ctrl_t ctrl = opaque; const char *s; if (opt.verbose) { log_info ("dirmngr status: %s\n", line); } if ((s = has_leading_keyword (line, "PROGRESS"))) { if (ctrl) { line = s; if (gpgsm_status (ctrl, STATUS_PROGRESS, line)) return gpg_error (GPG_ERR_ASS_CANCELED); } } else if (warning_and_note_printer (line)) { } return 0; } /* Pass COMMAND to dirmngr and print all output generated by Dirmngr to stdout. A couple of inquiries are defined (see above). ARGC arguments in ARGV are given to the Dirmngr. Spaces, plus and percent characters within the argument strings are percent escaped so that blanks can act as delimiters. */ int gpgsm_dirmngr_run_command (ctrl_t ctrl, const char *command, int argc, char **argv) { int rc; int i; const char *s; char *line, *p; size_t len; struct run_command_parm_s parm; rc = start_dirmngr (ctrl); if (rc) return rc; parm.ctrl = ctrl; parm.ctx = dirmngr_ctx; len = strlen (command) + 1; for (i=0; i < argc; i++) len += 1 + 3*strlen (argv[i]); /* enough space for percent escaping */ line = xtrymalloc (len); if (!line) { release_dirmngr (ctrl); return out_of_core (); } p = stpcpy (line, command); for (i=0; i < argc; i++) { *p++ = ' '; for (s=argv[i]; *s; s++) { if (!isascii (*s)) *p++ = *s; else if (*s == ' ') *p++ = '+'; else if (!isprint (*s) || *s == '+') { sprintf (p, "%%%02X", *(const unsigned char *)s); p += 3; } else *p++ = *s; } } *p = 0; rc = assuan_transact (dirmngr_ctx, line, run_command_cb, NULL, run_command_inq_cb, &parm, run_command_status_cb, ctrl); xfree (line); log_info ("response of dirmngr: %s\n", rc? gpg_strerror (rc): "okay"); release_dirmngr (ctrl); return rc; } diff --git a/sm/certlist.c b/sm/certlist.c index b5f9f7874..fdf31a198 100644 --- a/sm/certlist.c +++ b/sm/certlist.c @@ -1,627 +1,631 @@ /* certlist.c - build list of certificates * Copyright (C) 2001, 2003, 2004, 2005, 2007, * 2008, 2011 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 3 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, see . */ #include #include #include #include #include #include #include #include "gpgsm.h" #include #include #include "keydb.h" #include "../common/i18n.h" /* Mode values for cert_usage_p. * Take care: the values have a semantic. */ #define USE_MODE_SIGN 0 #define USE_MODE_ENCR 1 #define USE_MODE_VRFY 2 #define USE_MODE_DECR 3 #define USE_MODE_CERT 4 #define USE_MODE_OCSP 5 /* OIDs we use here. */ static const char oid_kp_serverAuth[] = "1.3.6.1.5.5.7.3.1"; static const char oid_kp_clientAuth[] = "1.3.6.1.5.5.7.3.2"; static const char oid_kp_codeSigning[] = "1.3.6.1.5.5.7.3.3"; static const char oid_kp_emailProtection[]= "1.3.6.1.5.5.7.3.4"; static const char oid_kp_timeStamping[] = "1.3.6.1.5.5.7.3.8"; static const char oid_kp_ocspSigning[] = "1.3.6.1.5.5.7.3.9"; /* Return 0 if the cert is usable for encryption. A MODE of 0 checks for signing a MODE of 1 checks for encryption, a MODE of 2 checks for verification and a MODE of 3 for decryption (just for debugging). MODE 4 is for certificate signing, MODE for COSP response signing. */ static int cert_usage_p (ksba_cert_t cert, int mode, int silent) { gpg_error_t err; unsigned int use; unsigned int encr_bits, sign_bits; char *extkeyusages; int have_ocsp_signing = 0; err = ksba_cert_get_ext_key_usages (cert, &extkeyusages); if (gpg_err_code (err) == GPG_ERR_NO_DATA) err = 0; /* no policy given */ if (!err) { unsigned int extusemask = ~0; /* Allow all. */ if (extkeyusages) { char *p, *pend; int any_critical = 0; extusemask = 0; p = extkeyusages; while (p && (pend=strchr (p, ':'))) { *pend++ = 0; /* Only care about critical flagged usages. */ if ( *pend == 'C' ) { any_critical = 1; if ( !strcmp (p, oid_kp_serverAuth)) extusemask |= (KSBA_KEYUSAGE_DIGITAL_SIGNATURE | KSBA_KEYUSAGE_KEY_ENCIPHERMENT | KSBA_KEYUSAGE_KEY_AGREEMENT); else if ( !strcmp (p, oid_kp_clientAuth)) extusemask |= (KSBA_KEYUSAGE_DIGITAL_SIGNATURE | KSBA_KEYUSAGE_KEY_AGREEMENT); else if ( !strcmp (p, oid_kp_codeSigning)) extusemask |= (KSBA_KEYUSAGE_DIGITAL_SIGNATURE); else if ( !strcmp (p, oid_kp_emailProtection)) extusemask |= (KSBA_KEYUSAGE_DIGITAL_SIGNATURE | KSBA_KEYUSAGE_NON_REPUDIATION | KSBA_KEYUSAGE_KEY_ENCIPHERMENT | KSBA_KEYUSAGE_KEY_AGREEMENT); else if ( !strcmp (p, oid_kp_timeStamping)) extusemask |= (KSBA_KEYUSAGE_DIGITAL_SIGNATURE | KSBA_KEYUSAGE_NON_REPUDIATION); } /* This is a hack to cope with OCSP. Note that we do not yet fully comply with the requirements and that the entire CRL/OCSP checking thing should undergo a thorough review and probably redesign. */ if ( !strcmp (p, oid_kp_ocspSigning)) have_ocsp_signing = 1; if ((p = strchr (pend, '\n'))) p++; } xfree (extkeyusages); extkeyusages = NULL; if (!any_critical) extusemask = ~0; /* Reset to the don't care mask. */ } err = ksba_cert_get_key_usage (cert, &use); if (gpg_err_code (err) == GPG_ERR_NO_DATA) { err = 0; if (opt.verbose && mode < USE_MODE_VRFY && !silent) log_info (_("no key usage specified - assuming all usages\n")); use = ~0; } /* Apply extKeyUsage. */ use &= extusemask; } if (err) { log_error (_("error getting key usage information: %s\n"), gpg_strerror (err)); xfree (extkeyusages); return err; } if (mode == USE_MODE_CERT) { if ((use & (KSBA_KEYUSAGE_KEY_CERT_SIGN))) return 0; if (!silent) log_info (_("certificate should not have " "been used for certification\n")); return gpg_error (GPG_ERR_WRONG_KEY_USAGE); } if (mode == USE_MODE_OCSP) { if (use != ~0 && (have_ocsp_signing || (use & (KSBA_KEYUSAGE_KEY_CERT_SIGN |KSBA_KEYUSAGE_CRL_SIGN)))) return 0; if (!silent) log_info (_("certificate should not have " "been used for OCSP response signing\n")); return gpg_error (GPG_ERR_WRONG_KEY_USAGE); } encr_bits = (KSBA_KEYUSAGE_KEY_ENCIPHERMENT|KSBA_KEYUSAGE_DATA_ENCIPHERMENT); if ((opt.compat_flags & COMPAT_ALLOW_KA_TO_ENCR) || gpgsm_is_ecc_key (cert)) encr_bits |= KSBA_KEYUSAGE_KEY_AGREEMENT; sign_bits = (KSBA_KEYUSAGE_DIGITAL_SIGNATURE|KSBA_KEYUSAGE_NON_REPUDIATION); if ((use & ((mode&1)? encr_bits : sign_bits))) return 0; if (!silent) log_info (mode == USE_MODE_DECR? _("certificate should not have been used for encryption\n") : mode == USE_MODE_VRFY? _("certificate should not have been used for signing\n") : mode == USE_MODE_ENCR? _("certificate is not usable for encryption\n") : _("certificate is not usable for signing\n")); return gpg_error (GPG_ERR_WRONG_KEY_USAGE); } /* Return 0 if the cert is usable for signing */ int gpgsm_cert_use_sign_p (ksba_cert_t cert, int silent) { return cert_usage_p (cert, USE_MODE_SIGN, silent); } /* Return 0 if the cert is usable for encryption */ int gpgsm_cert_use_encrypt_p (ksba_cert_t cert) { return cert_usage_p (cert, USE_MODE_ENCR, 0); } int gpgsm_cert_use_verify_p (ksba_cert_t cert) { return cert_usage_p (cert, USE_MODE_VRFY, 0); } int gpgsm_cert_use_decrypt_p (ksba_cert_t cert) { return cert_usage_p (cert, USE_MODE_DECR, 0); } int gpgsm_cert_use_cert_p (ksba_cert_t cert) { return cert_usage_p (cert, USE_MODE_CERT, 0); } int gpgsm_cert_use_ocsp_p (ksba_cert_t cert) { return cert_usage_p (cert, USE_MODE_OCSP, 0); } /* Return true if CERT has the well known private key extension. */ int gpgsm_cert_has_well_known_private_key (ksba_cert_t cert) { int idx; const char *oid; for (idx=0; !ksba_cert_get_extension (cert, idx, &oid, NULL, NULL, NULL);idx++) if (!strcmp (oid, "1.3.6.1.4.1.11591.2.2.2") ) return 1; /* Yes. */ return 0; /* No. */ } static int same_subject_issuer (const char *subject, const char *issuer, ksba_cert_t cert) { char *subject2 = ksba_cert_get_subject (cert, 0); char *issuer2 = ksba_cert_get_issuer (cert, 0); int tmp; tmp = (subject && subject2 && !strcmp (subject, subject2) && issuer && issuer2 && !strcmp (issuer, issuer2)); xfree (subject2); xfree (issuer2); return tmp; } /* Return true if CERT_A is the same as CERT_B. */ int gpgsm_certs_identical_p (ksba_cert_t cert_a, ksba_cert_t cert_b) { const unsigned char *img_a, *img_b; size_t len_a, len_b; img_a = ksba_cert_get_image (cert_a, &len_a); if (img_a) { img_b = ksba_cert_get_image (cert_b, &len_b); if (img_b && len_a == len_b && !memcmp (img_a, img_b, len_a)) return 1; /* Identical. */ } return 0; } /* Return true if CERT is already contained in CERTLIST. */ static int is_cert_in_certlist (ksba_cert_t cert, certlist_t certlist) { const unsigned char *img_a, *img_b; size_t len_a, len_b; img_a = ksba_cert_get_image (cert, &len_a); if (img_a) { for ( ; certlist; certlist = certlist->next) { img_b = ksba_cert_get_image (certlist->cert, &len_b); if (img_b && len_a == len_b && !memcmp (img_a, img_b, len_a)) return 1; /* Already contained. */ } } return 0; } /* Add CERT to the list of certificates at CERTADDR but avoid duplicates. */ int gpgsm_add_cert_to_certlist (ctrl_t ctrl, ksba_cert_t cert, certlist_t *listaddr, int is_encrypt_to) { (void)ctrl; if (!is_cert_in_certlist (cert, *listaddr)) { certlist_t cl = xtrycalloc (1, sizeof *cl); if (!cl) return out_of_core (); cl->cert = cert; ksba_cert_ref (cert); cl->next = *listaddr; cl->is_encrypt_to = is_encrypt_to; *listaddr = cl; } return 0; } /* Add a certificate to a list of certificate and make sure that it is a valid certificate. With SECRET set to true a secret key must be available for the certificate. IS_ENCRYPT_TO sets the corresponding flag in the new create LISTADDR item. */ int gpgsm_add_to_certlist (ctrl_t ctrl, const char *name, int secret, certlist_t *listaddr, int is_encrypt_to) { int rc; KEYDB_SEARCH_DESC desc; KEYDB_HANDLE kh = NULL; ksba_cert_t cert = NULL; rc = classify_user_id (name, &desc, 0); if (!rc) { kh = keydb_new (ctrl); if (!kh) rc = gpg_error (GPG_ERR_ENOMEM); else { int wrong_usage = 0; char *first_subject = NULL; char *first_issuer = NULL; get_next: rc = keydb_search (ctrl, kh, &desc, 1); if (!rc) rc = keydb_get_cert (kh, &cert); if (!rc) { if (!first_subject) { /* Save the subject and the issuer for key usage and ambiguous name tests. */ first_subject = ksba_cert_get_subject (cert, 0); first_issuer = ksba_cert_get_issuer (cert, 0); } rc = secret? gpgsm_cert_use_sign_p (cert, 0) : gpgsm_cert_use_encrypt_p (cert); if (gpg_err_code (rc) == GPG_ERR_WRONG_KEY_USAGE) { /* There might be another certificate with the correct usage, so we try again */ if (!wrong_usage || same_subject_issuer (first_subject, first_issuer,cert)) { if (!wrong_usage) wrong_usage = rc; /* save error of the first match */ ksba_cert_release (cert); cert = NULL; log_info (_("looking for another certificate\n")); goto get_next; } else wrong_usage = rc; } } /* We want the error code from the first match in this case. */ if (rc && wrong_usage) rc = wrong_usage; if (!rc) { certlist_t dup_certs = NULL; next_ambigious: rc = keydb_search (ctrl, kh, &desc, 1); if (gpg_err_code (rc) == GPG_ERR_NOT_FOUND) rc = 0; else if (!rc) { ksba_cert_t cert2 = NULL; /* If this is the first possible duplicate, add the original certificate to our list of duplicates. */ if (!dup_certs) gpgsm_add_cert_to_certlist (ctrl, cert, &dup_certs, 0); /* We have to ignore ambiguous names as long as there only fault is a bad key usage. This is required to support encryption and signing certificates of the same subject. Further we ignore them if they are due to an identical certificate (which may happen if a certificate is accidentally duplicated in the keybox). */ if (!keydb_get_cert (kh, &cert2)) { int tmp = (same_subject_issuer (first_subject, first_issuer, cert2) && ((gpg_err_code ( secret? gpgsm_cert_use_sign_p (cert2,0) : gpgsm_cert_use_encrypt_p (cert2) ) ) == GPG_ERR_WRONG_KEY_USAGE)); if (tmp) gpgsm_add_cert_to_certlist (ctrl, cert2, &dup_certs, 0); else { if (is_cert_in_certlist (cert2, dup_certs)) tmp = 1; } ksba_cert_release (cert2); if (tmp) goto next_ambigious; } rc = gpg_error (GPG_ERR_AMBIGUOUS_NAME); } gpgsm_release_certlist (dup_certs); } xfree (first_subject); xfree (first_issuer); first_subject = NULL; first_issuer = NULL; if (!rc && !is_cert_in_certlist (cert, *listaddr)) { if (!rc && secret) { char *p; rc = gpg_error (GPG_ERR_NO_SECKEY); p = gpgsm_get_keygrip_hexstring (cert); if (p) { if (!gpgsm_agent_havekey (ctrl, p)) rc = 0; xfree (p); } } if (!rc) rc = gpgsm_validate_chain (ctrl, cert, GNUPG_ISOTIME_NONE, NULL, 0, NULL, 0, NULL); if (!rc) { certlist_t cl = xtrycalloc (1, sizeof *cl); if (!cl) rc = gpg_error_from_syserror (); else { cl->cert = cert; cert = NULL; cl->next = *listaddr; cl->is_encrypt_to = is_encrypt_to; *listaddr = cl; } } } } } keydb_release (kh); ksba_cert_release (cert); return (gpg_err_code (rc) == GPG_ERR_NOT_FOUND ? gpg_error (GPG_ERR_NO_PUBKEY): rc); } void gpgsm_release_certlist (certlist_t list) { while (list) { certlist_t cl = list->next; ksba_cert_release (list->cert); xfree (list); list = cl; } } /* Like gpgsm_add_to_certlist, but look only for one certificate. No chain validation is done. If KEYID is not NULL it is taken as an additional filter value which must match the subjectKeyIdentifier. */ int gpgsm_find_cert (ctrl_t ctrl, const char *name, ksba_sexp_t keyid, ksba_cert_t *r_cert, - int allow_ambiguous) + unsigned int flags) { int rc; KEYDB_SEARCH_DESC desc; KEYDB_HANDLE kh = NULL; + int allow_ambiguous = (flags & FIND_CERT_ALLOW_AMBIG); *r_cert = NULL; rc = classify_user_id (name, &desc, 0); if (!rc) { kh = keydb_new (ctrl); if (!kh) rc = gpg_error (GPG_ERR_ENOMEM); else { + if ((flags & FIND_CERT_WITH_EPHEM)) + keydb_set_ephemeral (kh, 1); + nextone: rc = keydb_search (ctrl, kh, &desc, 1); if (!rc) { rc = keydb_get_cert (kh, r_cert); if (!rc && keyid) { ksba_sexp_t subj; rc = ksba_cert_get_subj_key_id (*r_cert, NULL, &subj); if (!rc) { if (cmp_simple_canon_sexp (keyid, subj)) { xfree (subj); goto nextone; } xfree (subj); /* Okay: Here we know that the certificate's subjectKeyIdentifier matches the requested one. */ } else if (gpg_err_code (rc) == GPG_ERR_NO_DATA) goto nextone; } } /* If we don't have the KEYID filter we need to check for ambiguous search results. Note, that it is somewhat reasonable to assume that a specification of a KEYID won't lead to ambiguous names. */ if (!rc && !keyid) { ksba_isotime_t notbefore = ""; const unsigned char *image = NULL; size_t length = 0; if (allow_ambiguous) { /* We want to return the newest certificate */ if (ksba_cert_get_validity (*r_cert, 0, notbefore)) *notbefore = '\0'; image = ksba_cert_get_image (*r_cert, &length); } next_ambiguous: rc = keydb_search (ctrl, kh, &desc, 1); if (gpg_err_code (rc) == GPG_ERR_NOT_FOUND) rc = 0; else { if (!rc) { ksba_cert_t cert2 = NULL; ksba_isotime_t notbefore2 = ""; const unsigned char *image2 = NULL; size_t length2 = 0; int cmp = 0; if (!keydb_get_cert (kh, &cert2)) { if (gpgsm_certs_identical_p (*r_cert, cert2)) { ksba_cert_release (cert2); goto next_ambiguous; } if (allow_ambiguous) { if (ksba_cert_get_validity (cert2, 0, notbefore2)) *notbefore2 = '\0'; image2 = ksba_cert_get_image (cert2, &length2); cmp = strcmp (notbefore, notbefore2); /* use certificate image bits as last resort for stable ordering */ if (!cmp) cmp = memcmp (image, image2, length < length2 ? length : length2); if (!cmp) cmp = length < length2 ? -1 : length > length2 ? 1 : 0; if (cmp < 0) { ksba_cert_release (*r_cert); *r_cert = cert2; strcpy (notbefore, notbefore2); image = image2; length = length2; } else ksba_cert_release (cert2); goto next_ambiguous; } ksba_cert_release (cert2); } rc = gpg_error (GPG_ERR_AMBIGUOUS_NAME); } ksba_cert_release (*r_cert); *r_cert = NULL; } } } } keydb_release (kh); return (gpg_err_code (rc) == GPG_ERR_NOT_FOUND? gpg_error (GPG_ERR_NO_PUBKEY): rc); } diff --git a/sm/gpgsm.h b/sm/gpgsm.h index ced2d679f..e09dd75e9 100644 --- a/sm/gpgsm.h +++ b/sm/gpgsm.h @@ -1,526 +1,529 @@ /* gpgsm.h - Global definitions for GpgSM * Copyright (C) 2001, 2003, 2004, 2007, 2009, * 2010 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 3 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, see . */ #ifndef GPGSM_H #define GPGSM_H #ifdef GPG_ERR_SOURCE_DEFAULT #error GPG_ERR_SOURCE_DEFAULT already defined #endif #define GPG_ERR_SOURCE_DEFAULT GPG_ERR_SOURCE_GPGSM #include #include #include "../common/util.h" #include "../common/status.h" #include "../common/audit.h" #include "../common/session-env.h" #include "../common/ksba-io-support.h" #include "../common/compliance.h" /* The maximum length of a binary fingerprints. This is used to * provide a static buffer and will be increased if we need to support * longer fingerprints. */ #define MAX_FINGERPRINT_LEN 32 /* The maximum length of a binary digest. */ #define MAX_DIGEST_LEN 64 /* Fits for SHA-512 */ /* A large struct named "opt" to keep global flags. */ EXTERN_UNLESS_MAIN_MODULE struct { unsigned int debug; /* debug flags (DBG_foo_VALUE) */ int verbose; /* verbosity level */ int quiet; /* be as quiet as possible */ int batch; /* run in batch mode, i.e w/o any user interaction */ int answer_yes; /* assume yes on most questions */ int answer_no; /* assume no on most questions */ int dry_run; /* don't change any persistent data */ int no_homedir_creation; int use_keyboxd; /* Use the external keyboxd as storage backend. */ const char *config_filename; /* Name of the used config file. */ const char *agent_program; const char *keyboxd_program; session_env_t session_env; char *lc_ctype; char *lc_messages; int autostart; const char *dirmngr_program; int disable_dirmngr; /* Do not do any dirmngr calls. */ const char *protect_tool_program; char *outfile; /* name of output file */ int with_key_data;/* include raw key in the column delimited output */ int fingerprint; /* list fingerprints in all key listings */ int with_md5_fingerprint; /* Also print an MD5 fingerprint for standard key listings. */ int with_keygrip; /* Option --with-keygrip active. */ int with_key_screening; /* Option --with-key-screening active. */ int pinentry_mode; int request_origin; int armor; /* force base64 armoring (see also ctrl.with_base64) */ int no_armor; /* don't try to figure out whether data is base64 armored*/ const char *p12_charset; /* Use this charset for encoding the pkcs#12 passphrase. */ const char *def_cipher_algoid; /* cipher algorithm to use if nothing else is specified */ int def_compress_algo; /* Ditto for compress algorithm */ int forced_digest_algo; /* User forced hash algorithm. */ int force_ecdh_sha1kdf; /* Only for debugging and testing. */ char *def_recipient; /* userID of the default recipient */ int def_recipient_self; /* The default recipient is the default key */ int no_encrypt_to; /* Ignore all as encrypt to marked recipients. */ char *local_user; /* NULL or argument to -u */ int extra_digest_algo; /* A digest algorithm also used for verification of signatures. */ int always_trust; /* Trust the given keys even if there is no valid certification chain */ int skip_verify; /* do not check signatures on data */ int lock_once; /* Keep lock once they are set */ int ignore_time_conflict; /* Ignore certain time conflicts */ int no_crl_check; /* Don't do a CRL check */ int no_trusted_cert_crl_check; /* Don't run a CRL check for trusted certs. */ int force_crl_refresh; /* Force refreshing the CRL. */ int enable_issuer_based_crl_check; /* Backward compatibility hack. */ int enable_ocsp; /* Default to use OCSP checks. */ char *policy_file; /* full pathname of policy file */ int no_policy_check; /* ignore certificate policies */ int no_chain_validation; /* Bypass all cert chain validity tests */ int ignore_expiration; /* Ignore the notAfter validity checks. */ int auto_issuer_key_retrieve; /* try to retrieve a missing issuer key. */ int qualsig_approval; /* Set to true if this software has officially been approved to create an verify qualified signatures. This is a runtime option in case we want to check the integrity of the software at runtime. */ unsigned int min_rsa_length; /* Used for compliance checks. */ strlist_t keyserver; /* A list of certificate extension OIDs which are ignored so that one can claim that a critical extension has been handled. One OID per string. */ strlist_t ignored_cert_extensions; /* A list of OIDs which will be used to ignore certificates with * sunch an OID during --learn-card. */ strlist_t ignore_cert_with_oid; /* The current compliance mode. */ enum gnupg_compliance_mode compliance; /* Fail if an operation can't be done in the requested compliance * mode. */ int require_compliance; /* Enable creation of authenticode signatures. */ int authenticode; /* A list of extra attributes put into a signed data object. For a * signed each attribute each string has the format: * :s: * and for an unsigned attribute * :u: * The OID is in the usual dotted decimal for. The HEX_OR_FILENAME * is either a list of hex digits or a filename with the DER encoded * value. A filename is detected by the presence of a slash in the * HEX_OR_FILENAME. The actual value needs to be encoded as a SET OF * attribute values. */ strlist_t attributes; /* Compatibility flags (COMPAT_FLAG_xxxx). */ unsigned int compat_flags; } opt; /* Debug values and macros. */ #define DBG_X509_VALUE 1 /* debug x.509 data reading/writing */ #define DBG_MPI_VALUE 2 /* debug mpi details */ #define DBG_CRYPTO_VALUE 4 /* debug low level crypto */ #define DBG_MEMORY_VALUE 32 /* debug memory allocation stuff */ #define DBG_CACHE_VALUE 64 /* debug the caching */ #define DBG_MEMSTAT_VALUE 128 /* show memory statistics */ #define DBG_HASHING_VALUE 512 /* debug hashing operations */ #define DBG_IPC_VALUE 1024 /* debug assuan communication */ #define DBG_CLOCK_VALUE 4096 #define DBG_LOOKUP_VALUE 8192 /* debug the key lookup */ #define DBG_X509 (opt.debug & DBG_X509_VALUE) #define DBG_CRYPTO (opt.debug & DBG_CRYPTO_VALUE) #define DBG_MEMORY (opt.debug & DBG_MEMORY_VALUE) #define DBG_CACHE (opt.debug & DBG_CACHE_VALUE) #define DBG_HASHING (opt.debug & DBG_HASHING_VALUE) #define DBG_IPC (opt.debug & DBG_IPC_VALUE) #define DBG_CLOCK (opt.debug & DBG_CLOCK_VALUE) #define DBG_LOOKUP (opt.debug & DBG_LOOKUP_VALUE) /* Compatibility flags */ /* Telesec RSA cards produced for NRW in 2022 came with only the * keyAgreement bit set. This flag allows there use for encryption * anyway. Example cert: * Issuer: /CN=DOI CA 10a/OU=DOI/O=PKI-1-Verwaltung/C=DE * key usage: digitalSignature nonRepudiation keyAgreement * policies: 1.3.6.1.4.1.7924.1.1:N: */ #define COMPAT_ALLOW_KA_TO_ENCR 1 /* Forward declaration for an object defined in server.c */ struct server_local_s; /* Object used to keep state locally in keydb.c */ struct keydb_local_s; typedef struct keydb_local_s *keydb_local_t; /* Session control object. This object is passed down to most functions. Note that the default values for it are set by gpgsm_init_default_ctrl(). */ struct server_control_s { int no_server; /* We are not running under server control */ int status_fd; /* Only for non-server mode */ struct server_local_s *server_local; keydb_local_t keydb_local; /* Local data for call-keyboxd.c */ audit_ctx_t audit; /* NULL or a context for the audit subsystem. */ int agent_seen; /* Flag indicating that the gpg-agent has been accessed. */ int with_colons; /* Use column delimited output format */ int with_secret; /* Mark secret keys in a public key listing. */ int with_chain; /* Include the certifying certs in a listing */ int with_validation;/* Validate each key while listing. */ int with_ephemeral_keys; /* Include ephemeral flagged keys in the keylisting. */ int autodetect_encoding; /* Try to detect the input encoding */ int is_pem; /* Is in PEM format */ int is_base64; /* is in plain base-64 format */ int create_base64; /* Create base64 encoded output */ int create_pem; /* create PEM output */ const char *pem_name; /* PEM name to use */ int include_certs; /* -1 to send all certificates in the chain along with a signature or the number of certificates up the chain (0 = none, 1 = only signer) */ int use_ocsp; /* Set to true if OCSP should be used. */ int validation_model; /* 0 := standard model (shell), 1 := chain model, 2 := STEED model. */ int offline; /* If true gpgsm won't do any network access. */ /* The current time. Used as a helper in certchain.c. */ ksba_isotime_t current_time; /* The revocation info. Used as a helper inc ertchain.c */ gnupg_isotime_t revoked_at; char *revocation_reason; }; /* An object to keep a list of certificates. */ struct certlist_s { struct certlist_s *next; ksba_cert_t cert; int is_encrypt_to; /* True if the certificate has been set through the --encrypto-to option. */ int pk_algo; /* The PK_ALGO from CERT or 0 if not yet known. */ int hash_algo; /* Used to track the hash algorithm to use. */ const char *hash_algo_oid; /* And the corresponding OID. */ }; typedef struct certlist_s *certlist_t; /* A structure carrying information about trusted root certificates. */ struct rootca_flags_s { unsigned int valid:1; /* The rest of the structure has valid information. */ unsigned int relax:1; /* Relax checking of root certificates. */ unsigned int chain_model:1; /* Root requires the use of the chain model. */ unsigned int qualified:1; /* Root CA used for qualfied signatures. */ }; /*-- gpgsm.c --*/ extern int gpgsm_errors_seen; void gpgsm_exit (int rc); void gpgsm_init_default_ctrl (struct server_control_s *ctrl); void gpgsm_deinit_default_ctrl (ctrl_t ctrl); int gpgsm_parse_validation_model (const char *model); /*-- server.c --*/ void gpgsm_server (certlist_t default_recplist); gpg_error_t gpgsm_status (ctrl_t ctrl, int no, const char *text); gpg_error_t gpgsm_status2 (ctrl_t ctrl, int no, ...) GPGRT_ATTR_SENTINEL(0); gpg_error_t gpgsm_status_with_err_code (ctrl_t ctrl, int no, const char *text, gpg_err_code_t ec); gpg_error_t gpgsm_status_with_error (ctrl_t ctrl, int no, const char *text, gpg_error_t err); gpg_error_t gpgsm_proxy_pinentry_notify (ctrl_t ctrl, const unsigned char *line); /*-- fingerprint --*/ unsigned char *gpgsm_get_fingerprint (ksba_cert_t cert, int algo, unsigned char *array, int *r_len); char *gpgsm_get_fingerprint_string (ksba_cert_t cert, int algo); char *gpgsm_get_fingerprint_hexstring (ksba_cert_t cert, int algo); unsigned long gpgsm_get_short_fingerprint (ksba_cert_t cert, unsigned long *r_high); unsigned char *gpgsm_get_keygrip (ksba_cert_t cert, unsigned char *array); char *gpgsm_get_keygrip_hexstring (ksba_cert_t cert); int gpgsm_get_key_algo_info (ksba_cert_t cert, unsigned int *nbits); int gpgsm_get_key_algo_info2 (ksba_cert_t cert, unsigned int *nbits, char **r_curve); int gpgsm_is_ecc_key (ksba_cert_t cert); char *gpgsm_pubkey_algo_string (ksba_cert_t cert, int *r_algoid); gcry_mpi_t gpgsm_get_rsa_modulus (ksba_cert_t cert); char *gpgsm_get_certid (ksba_cert_t cert); /*-- certdump.c --*/ const void *gpgsm_get_serial (ksba_const_sexp_t sn, size_t *r_length); void gpgsm_print_serial (estream_t fp, ksba_const_sexp_t p); void gpgsm_print_serial_decimal (estream_t fp, ksba_const_sexp_t sn); void gpgsm_print_time (estream_t fp, ksba_isotime_t t); void gpgsm_print_name2 (FILE *fp, const char *string, int translate); void gpgsm_print_name (FILE *fp, const char *string); void gpgsm_es_print_name (estream_t fp, const char *string); void gpgsm_es_print_name2 (estream_t fp, const char *string, int translate); void gpgsm_cert_log_name (const char *text, ksba_cert_t cert); void gpgsm_dump_cert (const char *text, ksba_cert_t cert); void gpgsm_dump_serial (ksba_const_sexp_t p); void gpgsm_dump_time (ksba_isotime_t t); void gpgsm_dump_string (const char *string); char *gpgsm_format_serial (ksba_const_sexp_t p); char *gpgsm_format_name2 (const char *name, int translate); char *gpgsm_format_name (const char *name); char *gpgsm_format_sn_issuer (ksba_sexp_t sn, const char *issuer); char *gpgsm_fpr_and_name_for_status (ksba_cert_t cert); char *gpgsm_format_keydesc (ksba_cert_t cert); /*-- certcheck.c --*/ int gpgsm_check_cert_sig (ksba_cert_t issuer_cert, ksba_cert_t cert); int gpgsm_check_cms_signature (ksba_cert_t cert, gcry_sexp_t sigval, gcry_md_hd_t md, int hash_algo, unsigned int pkalgoflags, int *r_pkalgo); /* fixme: move create functions to another file */ int gpgsm_create_cms_signature (ctrl_t ctrl, ksba_cert_t cert, gcry_md_hd_t md, int mdalgo, unsigned char **r_sigval); /*-- certchain.c --*/ /* Flags used with gpgsm_validate_chain. */ #define VALIDATE_FLAG_NO_DIRMNGR 1 #define VALIDATE_FLAG_CHAIN_MODEL 2 #define VALIDATE_FLAG_STEED 4 gpg_error_t gpgsm_walk_cert_chain (ctrl_t ctrl, ksba_cert_t start, ksba_cert_t *r_next); int gpgsm_is_root_cert (ksba_cert_t cert); int gpgsm_validate_chain (ctrl_t ctrl, ksba_cert_t cert, ksba_isotime_t checktime, ksba_isotime_t r_exptime, int listmode, estream_t listfp, unsigned int flags, unsigned int *retflags); int gpgsm_basic_cert_check (ctrl_t ctrl, ksba_cert_t cert); /*-- certlist.c --*/ int gpgsm_cert_use_sign_p (ksba_cert_t cert, int silent); int gpgsm_cert_use_encrypt_p (ksba_cert_t cert); int gpgsm_cert_use_verify_p (ksba_cert_t cert); int gpgsm_cert_use_decrypt_p (ksba_cert_t cert); int gpgsm_cert_use_cert_p (ksba_cert_t cert); int gpgsm_cert_use_ocsp_p (ksba_cert_t cert); int gpgsm_cert_has_well_known_private_key (ksba_cert_t cert); int gpgsm_certs_identical_p (ksba_cert_t cert_a, ksba_cert_t cert_b); int gpgsm_add_cert_to_certlist (ctrl_t ctrl, ksba_cert_t cert, certlist_t *listaddr, int is_encrypt_to); int gpgsm_add_to_certlist (ctrl_t ctrl, const char *name, int secret, certlist_t *listaddr, int is_encrypt_to); void gpgsm_release_certlist (certlist_t list); + +#define FIND_CERT_ALLOW_AMBIG 1 +#define FIND_CERT_WITH_EPHEM 2 int gpgsm_find_cert (ctrl_t ctrl, const char *name, ksba_sexp_t keyid, - ksba_cert_t *r_cert, int allow_ambiguous); + ksba_cert_t *r_cert, unsigned int flags); /*-- keylist.c --*/ gpg_error_t gpgsm_list_keys (ctrl_t ctrl, strlist_t names, estream_t fp, unsigned int mode); gpg_error_t gpgsm_show_certs (ctrl_t ctrl, int nfiles, char **files, estream_t fp); /*-- import.c --*/ int gpgsm_import (ctrl_t ctrl, int in_fd, int reimport_mode); int gpgsm_import_files (ctrl_t ctrl, int nfiles, char **files, int (*of)(const char *fname)); /*-- export.c --*/ void gpgsm_export (ctrl_t ctrl, strlist_t names, estream_t stream); void gpgsm_p12_export (ctrl_t ctrl, const char *name, estream_t stream, int rawmode); /*-- delete.c --*/ int gpgsm_delete (ctrl_t ctrl, strlist_t names); /*-- verify.c --*/ int gpgsm_verify (ctrl_t ctrl, int in_fd, int data_fd, estream_t out_fp); /*-- sign.c --*/ int gpgsm_get_default_cert (ctrl_t ctrl, ksba_cert_t *r_cert); int gpgsm_sign (ctrl_t ctrl, certlist_t signerlist, int data_fd, int detached, estream_t out_fp); /*-- encrypt.c --*/ int gpgsm_encrypt (ctrl_t ctrl, certlist_t recplist, int in_fd, estream_t out_fp); /*-- decrypt.c --*/ gpg_error_t ecdh_derive_kek (unsigned char *key, unsigned int keylen, int hash_algo, const char *wrap_algo_str, const void *secret, unsigned int secretlen, const void *ukm, unsigned int ukmlen); int gpgsm_decrypt (ctrl_t ctrl, int in_fd, estream_t out_fp); /*-- certreqgen.c --*/ int gpgsm_genkey (ctrl_t ctrl, estream_t in_stream, estream_t out_stream); /*-- certreqgen-ui.c --*/ void gpgsm_gencertreq_tty (ctrl_t ctrl, estream_t out_stream); /*-- qualified.c --*/ gpg_error_t gpgsm_is_in_qualified_list (ctrl_t ctrl, ksba_cert_t cert, char *country); gpg_error_t gpgsm_qualified_consent (ctrl_t ctrl, ksba_cert_t cert); gpg_error_t gpgsm_not_qualified_warning (ctrl_t ctrl, ksba_cert_t cert); /*-- call-agent.c --*/ int gpgsm_agent_pksign (ctrl_t ctrl, const char *keygrip, const char *desc, unsigned char *digest, size_t digestlen, int digestalgo, unsigned char **r_buf, size_t *r_buflen); int gpgsm_scd_pksign (ctrl_t ctrl, const char *keyid, const char *desc, unsigned char *digest, size_t digestlen, int digestalgo, unsigned char **r_buf, size_t *r_buflen); int gpgsm_agent_pkdecrypt (ctrl_t ctrl, const char *keygrip, const char *desc, ksba_const_sexp_t ciphertext, char **r_buf, size_t *r_buflen); int gpgsm_agent_genkey (ctrl_t ctrl, ksba_const_sexp_t keyparms, ksba_sexp_t *r_pubkey); int gpgsm_agent_readkey (ctrl_t ctrl, int fromcard, const char *hexkeygrip, ksba_sexp_t *r_pubkey); int gpgsm_agent_scd_serialno (ctrl_t ctrl, char **r_serialno); int gpgsm_agent_scd_keypairinfo (ctrl_t ctrl, strlist_t *r_list); int gpgsm_agent_istrusted (ctrl_t ctrl, ksba_cert_t cert, const char *hexfpr, struct rootca_flags_s *rootca_flags); int gpgsm_agent_havekey (ctrl_t ctrl, const char *hexkeygrip); int gpgsm_agent_marktrusted (ctrl_t ctrl, ksba_cert_t cert); int gpgsm_agent_learn (ctrl_t ctrl); int gpgsm_agent_passwd (ctrl_t ctrl, const char *hexkeygrip, const char *desc); gpg_error_t gpgsm_agent_get_confirmation (ctrl_t ctrl, const char *desc); gpg_error_t gpgsm_agent_send_nop (ctrl_t ctrl); gpg_error_t gpgsm_agent_keyinfo (ctrl_t ctrl, const char *hexkeygrip, char **r_serialno); gpg_error_t gpgsm_agent_ask_passphrase (ctrl_t ctrl, const char *desc_msg, int repeat, char **r_passphrase); gpg_error_t gpgsm_agent_keywrap_key (ctrl_t ctrl, int forexport, void **r_kek, size_t *r_keklen); gpg_error_t gpgsm_agent_import_key (ctrl_t ctrl, const void *key, size_t keylen); gpg_error_t gpgsm_agent_export_key (ctrl_t ctrl, const char *keygrip, const char *desc, unsigned char **r_result, size_t *r_resultlen); /*-- call-dirmngr.c --*/ gpg_error_t gpgsm_dirmngr_isvalid (ctrl_t ctrl, ksba_cert_t cert, ksba_cert_t issuer_cert, int use_ocsp, gnupg_isotime_t r_revoked_at, char **r_reason); int gpgsm_dirmngr_lookup (ctrl_t ctrl, strlist_t names, const char *uri, int cache_only, void (*cb)(void*, ksba_cert_t), void *cb_value); int gpgsm_dirmngr_run_command (ctrl_t ctrl, const char *command, int argc, char **argv); /*-- misc.c --*/ void gpgsm_print_further_info (const char *format, ...) GPGRT_ATTR_PRINTF(1,2); void setup_pinentry_env (void); gpg_error_t transform_sigval (const unsigned char *sigval, size_t sigvallen, int mdalgo, unsigned char **r_newsigval, size_t *r_newsigvallen); gcry_sexp_t gpgsm_ksba_cms_get_sig_val (ksba_cms_t cms, int idx); int gpgsm_get_hash_algo_from_sigval (gcry_sexp_t sigval, unsigned int *r_pkalgo_flags); #endif /*GPGSM_H*/