diff --git a/sm/certlist.c b/sm/certlist.c index c9e275e9d..c3e4e821b 100644 --- a/sm/certlist.c +++ b/sm/certlist.c @@ -1,608 +1,613 @@ /* 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 #include "gpgsm.h" #include #include #include "keydb.h" #include "../common/i18n.h" 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) +cert_usage_p (ksba_cert_t cert, int mode, int silent) { gpg_error_t err; unsigned int use; 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 < 2) + if (opt.verbose && mode < 2 && !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 == 4) { if ((use & (KSBA_KEYUSAGE_KEY_CERT_SIGN))) return 0; - log_info (_("certificate should not have " - "been used for certification\n")); + if (!silent) + log_info (_("certificate should not have " + "been used for certification\n")); return gpg_error (GPG_ERR_WRONG_KEY_USAGE); } if (mode == 5) { if (use != ~0 && (have_ocsp_signing || (use & (KSBA_KEYUSAGE_KEY_CERT_SIGN |KSBA_KEYUSAGE_CRL_SIGN)))) return 0; - log_info (_("certificate should not have " - "been used for OCSP response signing\n")); + if (!silent) + log_info (_("certificate should not have " + "been used for OCSP response signing\n")); return gpg_error (GPG_ERR_WRONG_KEY_USAGE); } if ((use & ((mode&1)? (KSBA_KEYUSAGE_KEY_ENCIPHERMENT|KSBA_KEYUSAGE_DATA_ENCIPHERMENT): (KSBA_KEYUSAGE_DIGITAL_SIGNATURE|KSBA_KEYUSAGE_NON_REPUDIATION))) ) return 0; - log_info (mode==3? _("certificate should not have been used for encryption\n"): - mode==2? _("certificate should not have been used for signing\n"): - mode==1? _("certificate is not usable for encryption\n"): - _("certificate is not usable for signing\n")); + if (!silent) + log_info + (mode==3? _("certificate should not have been used for encryption\n"): + mode==2? _("certificate should not have been used for signing\n"): + mode==1? _("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) +gpgsm_cert_use_sign_p (ksba_cert_t cert, int silent) { - return cert_usage_p (cert, 0); + return cert_usage_p (cert, 0, 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, 1); + return cert_usage_p (cert, 1, 0); } int gpgsm_cert_use_verify_p (ksba_cert_t cert) { - return cert_usage_p (cert, 2); + return cert_usage_p (cert, 2, 0); } int gpgsm_cert_use_decrypt_p (ksba_cert_t cert) { - return cert_usage_p (cert, 3); + return cert_usage_p (cert, 3, 0); } int gpgsm_cert_use_cert_p (ksba_cert_t cert) { - return cert_usage_p (cert, 4); + return cert_usage_p (cert, 4, 0); } int gpgsm_cert_use_ocsp_p (ksba_cert_t cert) { - return cert_usage_p (cert, 5); + return cert_usage_p (cert, 5, 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 (); 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) + 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) { /* save the first match */ wrong_usage = rc; ksba_cert_release (cert); cert = NULL; goto get_next; } else if (same_subject_issuer (first_subject, first_issuer, cert)) { wrong_usage = rc; ksba_cert_release (cert); cert = NULL; 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 (rc == -1) 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 accidential 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) - : gpgsm_cert_use_encrypt_p (cert2) + 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, "", NULL, 0, NULL, 0, NULL); if (!rc) { certlist_t cl = xtrycalloc (1, sizeof *cl); if (!cl) rc = out_of_core (); 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 rc == -1? 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) { int rc; KEYDB_SEARCH_DESC desc; KEYDB_HANDLE kh = NULL; *r_cert = NULL; rc = classify_user_id (name, &desc, 0); if (!rc) { kh = keydb_new (); if (!kh) rc = gpg_error (GPG_ERR_ENOMEM); else { 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 somehwat 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 (rc == -1) 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 rc == -1? gpg_error (GPG_ERR_NO_PUBKEY): rc; } diff --git a/sm/gpgsm.h b/sm/gpgsm.h index 325948aff..0ce96ff55 100644 --- a/sm/gpgsm.h +++ b/sm/gpgsm.h @@ -1,437 +1,437 @@ /* 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" #define MAX_DIGEST_LEN 64 struct keyserver_spec { struct keyserver_spec *next; char *host; int port; char *user; char *pass; char *base; }; /* A large struct named "opt" to keep global flags. */ 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; const char *config_filename; /* Name of the used config file. */ const char *agent_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 delimted 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 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. */ 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_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. */ struct keyserver_spec *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; enum gnupg_compliance_mode compliance; } 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_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) /* Forward declaration for an object defined in server.c */ struct server_local_s; /* 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; 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. */ }; /* 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 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. */ }; /*-- gpgsm.c --*/ void gpgsm_exit (int rc); void gpgsm_init_default_ctrl (struct server_control_s *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); char *gpgsm_get_certid (ksba_cert_t cert); /*-- certdump.c --*/ void gpgsm_print_serial (estream_t fp, ksba_const_sexp_t p); 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, ksba_const_sexp_t sigval, gcry_md_hd_t md, int hash_algo, 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 int 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 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); int gpgsm_find_cert (ctrl_t ctrl, const char *name, ksba_sexp_t keyid, ksba_cert_t *r_cert, int allow_ambiguous); /*-- keylist.c --*/ gpg_error_t gpgsm_list_keys (ctrl_t ctrl, strlist_t names, estream_t fp, unsigned int mode); /*-- 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 --*/ 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 --*/ int gpgsm_dirmngr_isvalid (ctrl_t ctrl, ksba_cert_t cert, ksba_cert_t issuer_cert, int use_ocsp); int gpgsm_dirmngr_lookup (ctrl_t ctrl, strlist_t names, 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 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); #endif /*GPGSM_H*/ diff --git a/sm/sign.c b/sm/sign.c index 24ecad3d7..fd6ebe00c 100644 --- a/sm/sign.c +++ b/sm/sign.c @@ -1,828 +1,828 @@ /* sign.c - Sign a message * Copyright (C) 2001, 2002, 2003, 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 "keydb.h" #include "../common/i18n.h" /* Hash the data and return if something was hashed. Return -1 on error. */ static int hash_data (int fd, gcry_md_hd_t md) { estream_t fp; char buffer[4096]; int nread; int rc = 0; fp = es_fdopen_nc (fd, "rb"); if (!fp) { log_error ("fdopen(%d) failed: %s\n", fd, strerror (errno)); return -1; } do { nread = es_fread (buffer, 1, DIM(buffer), fp); gcry_md_write (md, buffer, nread); } while (nread); if (es_ferror (fp)) { log_error ("read error on fd %d: %s\n", fd, strerror (errno)); rc = -1; } es_fclose (fp); return rc; } static int hash_and_copy_data (int fd, gcry_md_hd_t md, ksba_writer_t writer) { gpg_error_t err; estream_t fp; char buffer[4096]; int nread; int rc = 0; int any = 0; fp = es_fdopen_nc (fd, "rb"); if (!fp) { gpg_error_t tmperr = gpg_error_from_syserror (); log_error ("fdopen(%d) failed: %s\n", fd, strerror (errno)); return tmperr; } do { nread = es_fread (buffer, 1, DIM(buffer), fp); if (nread) { any = 1; gcry_md_write (md, buffer, nread); err = ksba_writer_write_octet_string (writer, buffer, nread, 0); if (err) { log_error ("write failed: %s\n", gpg_strerror (err)); rc = err; } } } while (nread && !rc); if (es_ferror (fp)) { rc = gpg_error_from_syserror (); log_error ("read error on fd %d: %s\n", fd, strerror (errno)); } es_fclose (fp); if (!any) { /* We can't allow signing an empty message because it does not make much sense and more seriously, ksba_cms_build has already written the tag for data and now expects an octet string and an octet string of size 0 is illegal. */ log_error ("cannot sign an empty message\n"); rc = gpg_error (GPG_ERR_NO_DATA); } if (!rc) { err = ksba_writer_write_octet_string (writer, NULL, 0, 1); if (err) { log_error ("write failed: %s\n", gpg_strerror (err)); rc = err; } } return rc; } /* Get the default certificate which is defined as the first certificate capable of signing returned by the keyDB and has a secret key available. */ int gpgsm_get_default_cert (ctrl_t ctrl, ksba_cert_t *r_cert) { KEYDB_HANDLE hd; ksba_cert_t cert = NULL; int rc; char *p; hd = keydb_new (); if (!hd) return gpg_error (GPG_ERR_GENERAL); rc = keydb_search_first (ctrl, hd); if (rc) { keydb_release (hd); return rc; } do { rc = keydb_get_cert (hd, &cert); if (rc) { log_error ("keydb_get_cert failed: %s\n", gpg_strerror (rc)); keydb_release (hd); return rc; } - if (!gpgsm_cert_use_sign_p (cert)) + if (!gpgsm_cert_use_sign_p (cert, 1)) { p = gpgsm_get_keygrip_hexstring (cert); if (p) { if (!gpgsm_agent_havekey (ctrl, p)) { xfree (p); keydb_release (hd); *r_cert = cert; return 0; /* got it */ } xfree (p); } } ksba_cert_release (cert); cert = NULL; } while (!(rc = keydb_search_next (ctrl, hd))); if (rc && rc != -1) log_error ("keydb_search_next failed: %s\n", gpg_strerror (rc)); ksba_cert_release (cert); keydb_release (hd); return rc; } static ksba_cert_t get_default_signer (ctrl_t ctrl) { KEYDB_SEARCH_DESC desc; ksba_cert_t cert = NULL; KEYDB_HANDLE kh = NULL; int rc; if (!opt.local_user) { rc = gpgsm_get_default_cert (ctrl, &cert); if (rc) { if (rc != -1) log_debug ("failed to find default certificate: %s\n", gpg_strerror (rc)); return NULL; } return cert; } rc = classify_user_id (opt.local_user, &desc, 0); if (rc) { log_error ("failed to find default signer: %s\n", gpg_strerror (rc)); return NULL; } kh = keydb_new (); if (!kh) return NULL; rc = keydb_search (ctrl, kh, &desc, 1); if (rc) { log_debug ("failed to find default certificate: rc=%d\n", rc); } else { rc = keydb_get_cert (kh, &cert); if (rc) { log_debug ("failed to get cert: rc=%d\n", rc); } } keydb_release (kh); return cert; } /* Depending on the options in CTRL add the certificate CERT as well as other certificate up in the chain to the Root-CA to the CMS object. */ static int add_certificate_list (ctrl_t ctrl, ksba_cms_t cms, ksba_cert_t cert) { gpg_error_t err; int rc = 0; ksba_cert_t next = NULL; int n; int not_root = 0; ksba_cert_ref (cert); n = ctrl->include_certs; log_debug ("adding certificates at level %d\n", n); if (n == -2) { not_root = 1; n = -1; } if (n < 0 || n > 50) n = 50; /* We better apply an upper bound */ /* First add my own certificate unless we don't want any certificate included at all. */ if (n) { if (not_root && gpgsm_is_root_cert (cert)) err = 0; else err = ksba_cms_add_cert (cms, cert); if (err) goto ksba_failure; if (n>0) n--; } /* Walk the chain to include all other certificates. Note that a -1 used for N makes sure that there is no limit and all certs get included. */ while ( n-- && !(rc = gpgsm_walk_cert_chain (ctrl, cert, &next)) ) { if (not_root && gpgsm_is_root_cert (next)) err = 0; else err = ksba_cms_add_cert (cms, next); ksba_cert_release (cert); cert = next; next = NULL; if (err) goto ksba_failure; } ksba_cert_release (cert); return rc == -1? 0: rc; ksba_failure: ksba_cert_release (cert); log_error ("ksba_cms_add_cert failed: %s\n", gpg_strerror (err)); return err; } /* Perform a sign operation. Sign the data received on DATA-FD in embedded mode or in detached mode when DETACHED is true. Write the signature to OUT_FP. The keys used to sign are taken from SIGNERLIST or the default one will be used if the value of this argument is NULL. */ int gpgsm_sign (ctrl_t ctrl, certlist_t signerlist, int data_fd, int detached, estream_t out_fp) { int i, rc; gpg_error_t err; gnupg_ksba_io_t b64writer = NULL; ksba_writer_t writer; ksba_cms_t cms = NULL; ksba_stop_reason_t stopreason; KEYDB_HANDLE kh = NULL; gcry_md_hd_t data_md = NULL; int signer; const char *algoid; int algo; ksba_isotime_t signed_at; certlist_t cl; int release_signerlist = 0; audit_set_type (ctrl->audit, AUDIT_TYPE_SIGN); kh = keydb_new (); if (!kh) { log_error (_("failed to allocate keyDB handle\n")); rc = gpg_error (GPG_ERR_GENERAL); goto leave; } if (!gnupg_rng_is_compliant (opt.compliance)) { rc = gpg_error (GPG_ERR_FORBIDDEN); log_error (_("%s is not compliant with %s mode\n"), "RNG", gnupg_compliance_option_string (opt.compliance)); gpgsm_status_with_error (ctrl, STATUS_ERROR, "random-compliance", rc); goto leave; } ctrl->pem_name = "SIGNED MESSAGE"; rc = gnupg_ksba_create_writer (&b64writer, ((ctrl->create_pem? GNUPG_KSBA_IO_PEM : 0) | (ctrl->create_base64? GNUPG_KSBA_IO_BASE64 : 0)), ctrl->pem_name, out_fp, &writer); if (rc) { log_error ("can't create writer: %s\n", gpg_strerror (rc)); goto leave; } err = ksba_cms_new (&cms); if (err) { rc = err; goto leave; } err = ksba_cms_set_reader_writer (cms, NULL, writer); if (err) { log_debug ("ksba_cms_set_reader_writer failed: %s\n", gpg_strerror (err)); rc = err; goto leave; } /* We are going to create signed data with data as encap. content */ err = ksba_cms_set_content_type (cms, 0, KSBA_CT_SIGNED_DATA); if (!err) err = ksba_cms_set_content_type (cms, 1, KSBA_CT_DATA); if (err) { log_debug ("ksba_cms_set_content_type failed: %s\n", gpg_strerror (err)); rc = err; goto leave; } /* If no list of signers is given, use the default certificate. */ if (!signerlist) { ksba_cert_t cert = get_default_signer (ctrl); if (!cert) { log_error ("no default signer found\n"); gpgsm_status2 (ctrl, STATUS_INV_SGNR, get_inv_recpsgnr_code (GPG_ERR_NO_SECKEY), NULL); rc = gpg_error (GPG_ERR_GENERAL); goto leave; } /* Although we don't check for ambiguous specification we will check that the signer's certificate is usable and valid. */ - rc = gpgsm_cert_use_sign_p (cert); + rc = gpgsm_cert_use_sign_p (cert, 0); if (!rc) rc = gpgsm_validate_chain (ctrl, cert, "", NULL, 0, NULL, 0, NULL); if (rc) { char *tmpfpr; tmpfpr = gpgsm_get_fingerprint_hexstring (cert, 0); gpgsm_status2 (ctrl, STATUS_INV_SGNR, get_inv_recpsgnr_code (rc), tmpfpr, NULL); xfree (tmpfpr); goto leave; } /* That one is fine - create signerlist. */ signerlist = xtrycalloc (1, sizeof *signerlist); if (!signerlist) { rc = out_of_core (); ksba_cert_release (cert); goto leave; } signerlist->cert = cert; release_signerlist = 1; } /* Figure out the hash algorithm to use. We do not want to use the one for the certificate but if possible an OID for the plain algorithm. */ if (opt.forced_digest_algo && opt.verbose) log_info ("user requested hash algorithm %d\n", opt.forced_digest_algo); for (i=0, cl=signerlist; cl; cl = cl->next, i++) { const char *oid; if (opt.forced_digest_algo) { oid = NULL; cl->hash_algo = opt.forced_digest_algo; } else { oid = ksba_cert_get_digest_algo (cl->cert); cl->hash_algo = oid ? gcry_md_map_name (oid) : 0; } switch (cl->hash_algo) { case GCRY_MD_SHA1: oid = "1.3.14.3.2.26"; break; case GCRY_MD_RMD160: oid = "1.3.36.3.2.1"; break; case GCRY_MD_SHA224: oid = "2.16.840.1.101.3.4.2.4"; break; case GCRY_MD_SHA256: oid = "2.16.840.1.101.3.4.2.1"; break; case GCRY_MD_SHA384: oid = "2.16.840.1.101.3.4.2.2"; break; case GCRY_MD_SHA512: oid = "2.16.840.1.101.3.4.2.3"; break; /* case GCRY_MD_WHIRLPOOL: oid = "No OID yet"; break; */ case GCRY_MD_MD5: /* We don't want to use MD5. */ case 0: /* No algorithm found in cert. */ default: /* Other algorithms. */ log_info (_("hash algorithm %d (%s) for signer %d not supported;" " using %s\n"), cl->hash_algo, oid? oid: "?", i, gcry_md_algo_name (GCRY_MD_SHA1)); cl->hash_algo = GCRY_MD_SHA1; oid = "1.3.14.3.2.26"; break; } cl->hash_algo_oid = oid; /* Check compliance. */ if (! gnupg_digest_is_allowed (opt.compliance, 1, cl->hash_algo)) { log_error (_("digest algorithm '%s' may not be used in %s mode\n"), gcry_md_algo_name (cl->hash_algo), gnupg_compliance_option_string (opt.compliance)); err = gpg_error (GPG_ERR_DIGEST_ALGO); goto leave; } { unsigned int nbits; int pk_algo = gpgsm_get_key_algo_info (cl->cert, &nbits); if (! gnupg_pk_is_allowed (opt.compliance, PK_USE_SIGNING, pk_algo, NULL, nbits, NULL)) { char kidstr[10+1]; snprintf (kidstr, sizeof kidstr, "0x%08lX", gpgsm_get_short_fingerprint (cl->cert, NULL)); log_error (_("key %s may not be used for signing in %s mode\n"), kidstr, gnupg_compliance_option_string (opt.compliance)); err = gpg_error (GPG_ERR_PUBKEY_ALGO); goto leave; } } } if (opt.verbose) { for (i=0, cl=signerlist; cl; cl = cl->next, i++) log_info (_("hash algorithm used for signer %d: %s (%s)\n"), i, gcry_md_algo_name (cl->hash_algo), cl->hash_algo_oid); } /* Gather certificates of signers and store them in the CMS object. */ for (cl=signerlist; cl; cl = cl->next) { - rc = gpgsm_cert_use_sign_p (cl->cert); + rc = gpgsm_cert_use_sign_p (cl->cert, 0); if (rc) goto leave; err = ksba_cms_add_signer (cms, cl->cert); if (err) { log_error ("ksba_cms_add_signer failed: %s\n", gpg_strerror (err)); rc = err; goto leave; } rc = add_certificate_list (ctrl, cms, cl->cert); if (rc) { log_error ("failed to store list of certificates: %s\n", gpg_strerror(rc)); goto leave; } /* Set the hash algorithm we are going to use */ err = ksba_cms_add_digest_algo (cms, cl->hash_algo_oid); if (err) { log_debug ("ksba_cms_add_digest_algo failed: %s\n", gpg_strerror (err)); rc = err; goto leave; } } /* Check whether one of the certificates is qualified. Note that we already validated the certificate and thus the user data stored flag must be available. */ if (!opt.no_chain_validation) { for (cl=signerlist; cl; cl = cl->next) { size_t buflen; char buffer[1]; err = ksba_cert_get_user_data (cl->cert, "is_qualified", &buffer, sizeof (buffer), &buflen); if (err || !buflen) { log_error (_("checking for qualified certificate failed: %s\n"), gpg_strerror (err)); rc = err; goto leave; } if (*buffer) err = gpgsm_qualified_consent (ctrl, cl->cert); else err = gpgsm_not_qualified_warning (ctrl, cl->cert); if (err) { rc = err; goto leave; } } } /* Prepare hashing (actually we are figuring out what we have set above). */ rc = gcry_md_open (&data_md, 0, 0); if (rc) { log_error ("md_open failed: %s\n", gpg_strerror (rc)); goto leave; } if (DBG_HASHING) gcry_md_debug (data_md, "sign.data"); for (i=0; (algoid=ksba_cms_get_digest_algo_list (cms, i)); i++) { algo = gcry_md_map_name (algoid); if (!algo) { log_error ("unknown hash algorithm '%s'\n", algoid? algoid:"?"); rc = gpg_error (GPG_ERR_BUG); goto leave; } gcry_md_enable (data_md, algo); audit_log_i (ctrl->audit, AUDIT_DATA_HASH_ALGO, algo); } audit_log (ctrl->audit, AUDIT_SETUP_READY); if (detached) { /* We hash the data right now so that we can store the message digest. ksba_cms_build() takes this as an flag that detached data is expected. */ unsigned char *digest; size_t digest_len; if (!hash_data (data_fd, data_md)) audit_log (ctrl->audit, AUDIT_GOT_DATA); for (cl=signerlist,signer=0; cl; cl = cl->next, signer++) { digest = gcry_md_read (data_md, cl->hash_algo); digest_len = gcry_md_get_algo_dlen (cl->hash_algo); if ( !digest || !digest_len ) { log_error ("problem getting the hash of the data\n"); rc = gpg_error (GPG_ERR_BUG); goto leave; } err = ksba_cms_set_message_digest (cms, signer, digest, digest_len); if (err) { log_error ("ksba_cms_set_message_digest failed: %s\n", gpg_strerror (err)); rc = err; goto leave; } } } gnupg_get_isotime (signed_at); for (cl=signerlist,signer=0; cl; cl = cl->next, signer++) { err = ksba_cms_set_signing_time (cms, signer, signed_at); if (err) { log_error ("ksba_cms_set_signing_time failed: %s\n", gpg_strerror (err)); rc = err; goto leave; } } /* We need to write at least a minimal list of our capabilities to try to convince some MUAs to use 3DES and not the crippled RC2. Our list is: aes128-CBC des-EDE3-CBC */ err = ksba_cms_add_smime_capability (cms, "2.16.840.1.101.3.4.1.2", NULL, 0); if (!err) err = ksba_cms_add_smime_capability (cms, "1.2.840.113549.3.7", NULL, 0); if (err) { log_error ("ksba_cms_add_smime_capability failed: %s\n", gpg_strerror (err)); goto leave; } /* Main building loop. */ do { err = ksba_cms_build (cms, &stopreason); if (err) { log_debug ("ksba_cms_build failed: %s\n", gpg_strerror (err)); rc = err; goto leave; } if (stopreason == KSBA_SR_BEGIN_DATA) { /* Hash the data and store the message digest. */ unsigned char *digest; size_t digest_len; assert (!detached); rc = hash_and_copy_data (data_fd, data_md, writer); if (rc) goto leave; audit_log (ctrl->audit, AUDIT_GOT_DATA); for (cl=signerlist,signer=0; cl; cl = cl->next, signer++) { digest = gcry_md_read (data_md, cl->hash_algo); digest_len = gcry_md_get_algo_dlen (cl->hash_algo); if ( !digest || !digest_len ) { log_error ("problem getting the hash of the data\n"); rc = gpg_error (GPG_ERR_BUG); goto leave; } err = ksba_cms_set_message_digest (cms, signer, digest, digest_len); if (err) { log_error ("ksba_cms_set_message_digest failed: %s\n", gpg_strerror (err)); rc = err; goto leave; } } } else if (stopreason == KSBA_SR_NEED_SIG) { /* Compute the signature for all signers. */ gcry_md_hd_t md; rc = gcry_md_open (&md, 0, 0); if (rc) { log_error ("md_open failed: %s\n", gpg_strerror (rc)); goto leave; } if (DBG_HASHING) gcry_md_debug (md, "sign.attr"); ksba_cms_set_hash_function (cms, HASH_FNC, md); for (cl=signerlist,signer=0; cl; cl = cl->next, signer++) { unsigned char *sigval = NULL; char *buf, *fpr; audit_log_i (ctrl->audit, AUDIT_NEW_SIG, signer); if (signer) gcry_md_reset (md); { certlist_t cl_tmp; for (cl_tmp=signerlist; cl_tmp; cl_tmp = cl_tmp->next) { gcry_md_enable (md, cl_tmp->hash_algo); audit_log_i (ctrl->audit, AUDIT_ATTR_HASH_ALGO, cl_tmp->hash_algo); } } rc = ksba_cms_hash_signed_attrs (cms, signer); if (rc) { log_debug ("hashing signed attrs failed: %s\n", gpg_strerror (rc)); gcry_md_close (md); goto leave; } rc = gpgsm_create_cms_signature (ctrl, cl->cert, md, cl->hash_algo, &sigval); if (rc) { audit_log_cert (ctrl->audit, AUDIT_SIGNED_BY, cl->cert, rc); gcry_md_close (md); goto leave; } err = ksba_cms_set_sig_val (cms, signer, sigval); xfree (sigval); if (err) { audit_log_cert (ctrl->audit, AUDIT_SIGNED_BY, cl->cert, err); log_error ("failed to store the signature: %s\n", gpg_strerror (err)); rc = err; gcry_md_close (md); goto leave; } /* write a status message */ fpr = gpgsm_get_fingerprint_hexstring (cl->cert, GCRY_MD_SHA1); if (!fpr) { rc = gpg_error (GPG_ERR_ENOMEM); gcry_md_close (md); goto leave; } rc = 0; { int pkalgo = gpgsm_get_key_algo_info (cl->cert, NULL); buf = xtryasprintf ("%c %d %d 00 %s %s", detached? 'D':'S', pkalgo, cl->hash_algo, signed_at, fpr); if (!buf) rc = gpg_error_from_syserror (); } xfree (fpr); if (rc) { gcry_md_close (md); goto leave; } gpgsm_status (ctrl, STATUS_SIG_CREATED, buf); xfree (buf); audit_log_cert (ctrl->audit, AUDIT_SIGNED_BY, cl->cert, 0); } gcry_md_close (md); } } while (stopreason != KSBA_SR_READY); rc = gnupg_ksba_finish_writer (b64writer); if (rc) { log_error ("write failed: %s\n", gpg_strerror (rc)); goto leave; } audit_log (ctrl->audit, AUDIT_SIGNING_DONE); log_info ("signature created\n"); leave: if (rc) log_error ("error creating signature: %s <%s>\n", gpg_strerror (rc), gpg_strsource (rc) ); if (release_signerlist) gpgsm_release_certlist (signerlist); ksba_cms_release (cms); gnupg_ksba_destroy_writer (b64writer); keydb_release (kh); gcry_md_close (data_md); return rc; }