Page MenuHome GnuPG

No OneTemporary

diff --git a/src/ChangeLog b/src/ChangeLog
index 295bb65..da82c48 100644
--- a/src/ChangeLog
+++ b/src/ChangeLog
@@ -1,727 +1,732 @@
+2006-03-20 Werner Koch <wk@g10code.com>
+
+ * ber-decoder.c: Dump debug info always to stderr and not to stdout.
+ (decoder_next): Take zero length SETs into account.
+
2005-11-13 Werner Koch <wk@g10code.com>
* cert.c (ksba_cert_set_user_data): Fixed.
* ksba.h (ksba_cert_set_user_data, ksba_cert_get_user_data): Add
prototypes.
2005-11-11 Werner Koch <wk@g10code.com>
* cert.h (struct cert_user_data): New.
(struct ksba_cert_s): New field UDATA.
* cert.c (ksba_cert_release): Release the UDATA list.
(ksba_cert_set_user_data, ksba_cert_get_user_data): New.
* libksba.vers: Add the new functions.
2005-09-28 Marcus Brinkmann <marcus@g10code.de>
* Makefile.am (AM_CCPFLAGS): Renamed to AM_CPPFLAGS.
2005-07-21 Werner Koch <wk@g10code.com>
* certreq.h (struct general_names_s): New.
(struct ksba_certreq_s): New member SUBJECT_ALT_NAMES.
* certreq.c (ksba_certreq_release): Free it.
(add_general_names_to_extn): New.
(build_cri): And build them here.
* cert.c (get_name): Support dNSName and uRI.
* certreq.c (ksba_certreq_add_subject): Ditto.
2005-06-15 Werner Koch <wk@g10code.com>
* ksba.h, certreq.c (ksba_certreq_add_extension): Changed arg DER from
unsigned char* to void*.
* ksba.h, cms.c (ksba_cms_set_content_enc_algo): Changed arg IV
from unsigned char* to void*.
* ksba.h, cms.c (ksba_cms_get_content_enc_iv): Ditto.
* ksba.h, cms.c (ksba_cms_set_message_digest): Changed arg DIGEST
from char* to unsigned char*.
2005-06-02 Werner Koch <wk@g10code.com>
* Makefile.am: Add flags for gnulib.
* asn1-func.c: Include alloca.h.
2005-04-20 Werner Koch <wk@g10code.com>
* time.c (_ksba_asntime_to_iso): Add arg IS_UTCTIME to allow for
UTCTimes w/o seconds.
* ocsp.c (parse_asntime_into_isotime): Set this arg.
* crl.c (parse_to_next_update, parse_crl_entry): Ditto.
* cert.c (ksba_cert_get_validity): Ditto.
2005-04-19 Werner Koch <wk@g10code.com>
* Makefile.am: Don't have asn1-tables depend on built tools.
* ocsp.c (ksba_ocsp_set_nonce): Set the length of the nonce.
(write_request_extensions): New.
(ksba_ocsp_prepare_request): Call new function.
2005-04-18 Werner Koch <wk@g10code.com>
* crl.c (parse_crl_entry): Handle entry extensions.
(store_one_entry_extension): New.
(parse_enumerated): New.
2005-04-15 Werner Koch <wk@g10code.com>
* cert.c (get_info_access): Fixed case of more than one
AccessDescription.
* ksba.h (ksba_cert_get_subj_key_id): New.
* cert.c (get_simple_octet_string_ext): New.
(ksba_cert_get_subj_key_id): New.
* libksba.vers: Added ksba_cert_get_subj_key_id.
* cert.c (ksba_cert_get_auth_key_id): Add support for
keyIdentifier.
* crl.c (ksba_crl_get_auth_key_id): Ditto.
2005-01-31 Werner Koch <wk@g10code.com>
* cms-parser.c (_ksba_cms_parse_signed_data_part_2): Allow NDEF
encoding for the set of certificates.
2005-01-21 Werner Koch <wk@g10code.com>
* Makefile.am (ber_dump_SOURCES): Use per target flags to force
object renaming.
2004-12-18 Werner Koch <wk@g10code.com>
* Makefile.am (noinst_PROGRAMS): Don't generate them when cross
compiling.
(EXTRA_DIST): Include asn1-parse.c asn1-tables.c.
(DISTCLEAN): Removed.
2004-12-15 Werner Koch <wk@g10code.com>
* ksba.m4: Use proper quoting for use with automake 1.9.
2004-11-29 Werner Koch <wk@g10code.com>
* cms.c (read_and_hash_cont): Added more code for the unusual
non-NDEF things and factored common code out to ..
(read_hash_block): .. new function.
2004-11-26 Werner Koch <wk@g10code.com>
* util.c (_ksba_stpcpy): Renamed from stpcpy to avoid duble
defines sympols in case libska gets statically linked.
* util.h (stpcpy): Redefine it.
2004-08-17 Werner Koch <wk@g10code.de>
* cert.c (ksba_cert_get_image): Replaced assert by proper error
message.
* ber-decoder.c (match_der): Fix for primitive implicit context
tags. Argh, this whole decoder mess needs a full rewrite.
2004-08-16 Werner Koch <wk@g10code.de>
* keyinfo.c: Fixed second DSA entry; a comma was missing.
2004-07-20 Werner Koch <wk@gnupg.org>
* cert.c (_ksba_cert_get_issuer_dn_ptr): Renamed to ..
(_ksba_cert_get_subject_dn_ptr): .. this. And return the
subject's DN.
* ocsp.c (issuer_name_hash): Use it here. Thanks to Bernhard
Herzog for pointing this out. It revealed itself only in a
multi-level certificate chain.
2004-06-08 Werner Koch <wk@gnupg.org>
* certreq.c (ksba_certreq_add_extension): Removed unused variable.
2004-06-06 Werner Koch <wk@gnupg.org>
* ksba.h: Comment cleanups.
* certreq.c (ksba_certreq_add_extension): New.
2004-04-28 Werner Koch <wk@gnupg.org>
* libksba.vers: Add new functions.
* crl.h (crl_extn_s, crl_extn_t): New.
(ksba_crl_s): Add member EXTENSION_LIST;
* crl.c (ksba_crl_release): Free an extension list.
(parse_one_extension, store_one_extension): New.
(parse_crl_extensions): Store extensions.
(ksba_crl_get_extension, ksba_crl_get_auth_key_id)
(ksba_crl_get_crl_number): New.
2004-04-06 Werner Koch <wk@gnupg.org>
* cms.c (ksba_cms_add_smime_capability): Oops, we forgot to store
the parameters.
* writer.c (do_writer_write): Fixed memory allocation and added an
extra check.
2004-03-24 Werner Koch <wk@gnupg.org>
* cms.c (ksba_cms_add_smime_capability): New.
(ksba_cms_release): Free the capability list.
(store_smime_capability_sequence): New.
(build_signed_data_attributes): Include the smime capabilities.
* cms.h (oidparmlist_s): New.
(ksba_cms_s): Add element CAPABILITY_LIST.
* der-encoder.c (_ksba_der_store_sequence): New.
* asn1-func.c (_ksba_asn_is_primitive): New dummy type PRE_SEQUENCE.
* der-encoder.c (set_nhdr_and_len, copy_nhdr_and_len): Support it.
2004-03-17 Werner Koch <wk@gnupg.org>
* ber-help.c (_ksba_ber_parse_tl): Check for length overflow.
(_ksba_ber_read_tl): Ditto.
2004-03-16 Werner Koch <wk@gnupg.org>
* ocsp.c (ksba_ocsp_get_responder_id, ksba_ocsp_get_cert): New.
2004-03-15 Werner Koch <wk@gnupg.org>
* Makefile.am: Support for version scripts.
* libksba.vers: New.
* ocsp.c (parse_response): Fixed parsing of optional certificates.
2004-03-09 Werner Koch <wk@gnupg.org>
* ksba.h (ksba_set_hash_buffer_function): Add missing prototype.
2004-02-20 Werner Koch <wk@gnupg.org>
* cert.c (ksba_cert_get_ext_key_usages): New.
(ksba_cert_get_auth_key_id): Fix for the case that there is only
the KeyIdentifier.
2004-02-13 Werner Koch <wk@gnupg.org>
* reader.c, ksba.h (ksba_reader_clear): New.
2004-02-12 Werner Koch <wk@gnupg.org>
* cms.c (ksba_cms_identify): Detect pkcs#12.
* ksba.h: Add KSBA_CT_PCKS12.
2004-01-29 Werner Koch <wk@gnupg.org>
* cms.c (build_signed_data_attributes): Arggg; noticed that severe
bug shortly after the last release. Fixed wrong array size passed
to qsort.
2004-01-28 Werner Koch <wk@gnupg.org>
* dn.c: Updated the table and changed a few names only used for
String->DER. Added a source specifier as a replacement for the
allowed_by_rfc2253 field.
2004-01-19 Werner Koch <wk@gnupg.org>
* cms.c (build_signed_data_attributes): Argh. We need to sort the
attributes.
(compare_attrarray): New.
2003-12-19 Werner Koch <wk@gnupg.org>
* cms.c (build_signed_data_header): Fixed silly ==/= bug.
2003-12-09 Werner Koch <wk@gnupg.org>
* cms.c (ksba_cms_set_sig_val): Fixed a code cleanup I did on Nov
25 and led to bad signature creation. Unfortunatley gpgsm has
currently no real regression tests. BTW, why the hell didn't I
wrote an entry for that cleanup.
2003-11-28 Werner Koch <wk@gnupg.org>
* ocsp.h, ocsp.c: New.
* ksba.h (ksba_status_t, ksba_ocsp_response_status_t): New.
(ksba_ocsp_t): New.
2003-11-25 Werner Koch <wk@gnupg.org>
* ksba.h: Added a few more CRL reason codes.
2003-11-21 Werner Koch <wk@gnupg.org>
* oid.c (ksba_oid_from_str): Changed RBUF to unsigned char* and
changed all callers to avoid incompatible pointer warnings.
2003-11-20 Werner Koch <wk@gnupg.org>
* cert.c (_ksba_cert_get_issuer_dn_ptr): New.
(_ksba_cert_get_public_key_ptr): New.
(_ksba_cert_get_serial_ptr): New.
* util.c (_ksba_hash_buffer): New.
(ksba_set_hash_buffer_function): New.
* cms.c: Include sexp-parse.h.
(ksba_cms_set_sig_val): Use snext and smatch instead of open
coding it.
* sexp-parse.h: New. Taken from GnuPG 1.9.
2003-11-18 Werner Koch <wk@gnupg.org>
* reader.c (ksba_reader_read): Return GPG_ERR_EOF and not -1.
2003-11-17 Werner Koch <wk@gnupg.org>
* cert.c (get_name, ksba_cert_get_extension): Return GPG_ERR_EOF
and not just -1.
* ber-decoder.c (_ksba_ber_decoder_decode): Ditto.
2003-11-14 Werner Koch <wk@gnupg.org>
* cms.c (write_encrypted_cont): Fixed test on EOF.
* cert.c (ksba_cert_is_ca, ksba_cert_get_cert_policies)
(ksba_cert_get_auth_key_id): Ditto.
* ber-decoder.c (_ksba_ber_decoder_dump)
(_ksba_ber_decoder_decode): Ditto.
2003-11-13 Werner Koch <wk@gnupg.org>
* ber-decoder.c (eof_or_error): Replaced READ_ERROR with a system
provided error.
* crl.c (parse_to_next_update): Ditto.
* cms-parser.c (parse_cms_version): Ditto.
(_ksba_cms_parse_signed_data_part_1):
* ber-help.c (eof_or_error): Ditto.
* reader.c (ksba_reader_error): Changed to return gpg_error_t.
* writer.c (ksba_writer_error): Ditto.
2003-11-12 Werner Koch <wk@gnupg.org>
* writer.c (do_writer_write): Replaced WRITE_ERROR by one
generated from errno.
* asn1-parse.y (ksba_asn_parse_file): Replaced FILE_ERROR by one
generated from errno.
* cms.c (ksba_cms_new): Changed Interface to return an error code.
* name.c (ksba_name_new): Ditto.
* writer.c (ksba_writer_new): Ditto.
* reader.c (ksba_reader_new): Ditto.
* certreq.c (ksba_certreq_new): Ditto.
* crl.c (ksba_crl_new): Ditto.
* ksba.h: Removed all KSBA_ error codes and replaced them all over
the package by the gpg-error.h codes. Include gpg-error.h
* mkerrors: Removed.
* Makefile.am: Removed generation of errors.c
* asn1-gentables.c (one_file): Simplified error printing
* ksba-config.in (libs,cflags): Add values for gpg-error. Remove
duplicates.
2003-11-11 Werner Koch <wk@gnupg.org>
* cert.c (ksba_cert_new): Changed interface.
2003-10-31 Werner Koch <wk@gnupg.org>
Changed the representation of time.
* ksba.h (ksba_isotime_t): New.
* convert.h, cms.h, crl.h: Changed all data structure to use that
new time object.
* time.c (_ksba_asntime_to_epoch): Renamed to ..
(_ksba_asntime_to_iso): .. this and rewrote for the new time format.
(_ksba_asntime_from_epoch): Removed. Was never used.
(_ksba_assert_time_format, _ksba_copy_time, _ksba_cmp_time)
(_ksab_current_time): New.
* cert.c (ksba_cert_get_validity): Changed for new time data type.
* der-encoder.c (_ksba_der_store_time): Ditto.
* crl.c (ksba_crl_get_update_times, ksba_crl_get_item)
(parse_to_next_update, parse_crl_entry): Ditto.
* cms.c (ksba_cms_get_signing_time, ksba_cms_set_signing_time):
Ditto.
2003-10-21 Werner Koch <wk@gnupg.org>
* dn.c (oid_name_tbl): Changed OIDLEN to size_t.
(parse_rdn): Ditto.
* ber-decoder.c (dump_tlv): Fixed format specifier for NHDR.
Reported by Stephane Corthesy.
2003-08-20 Marcus Brinkmann <marcus@g10code.de>
* ber-decoder.c (new_decoder_state): Initialize DS->cur.in_any.
2003-03-17 Werner Koch <wk@gnupg.org>
* dn.c (count_quoted_string): Fixed detection of nonprintable
chars for hex pairs. Use IA5STRING instead of UTF8 if the only
reason is an at-sign.
(parse_rdn): Fixed parsing of hash prefixed hex pairs. Use
IA5STRING instead of UTF8 if the only reason is an at-sign.
2002-12-12 Werner Koch <wk@gnupg.org>
* keyinfo.c (sig_algo_table): Added sha-1WithRSAEncryption.
2002-12-03 Werner Koch <wk@gnupg.org>
* ber-decoder.c (cmp_tag): Handle ANY tag even when the classes
don't match. Test case arecertificates with a id-aa-encrypKeyPref.
2002-11-25 Werner Koch <wk@gnupg.org>
* cms.c (ksba_cms_get_sigattr_oids): New.
(build_signed_data_attributes): Store the content-type.
* der-encoder.c (_ksba_der_store_oid): Handle TYPE_ANY.
2002-10-22 Werner Koch <wk@gnupg.org>
* asn1-parse.y (yyparse): Prefix with _ksba_asn1_.
* asn1-func.c (_asn1_find_left): Made static.
* dn.c (parse_rdn): Add a parse-only mode to determine the end os
a part.
(_ksba_dn_from_str): Parse the string in reversed order as
specified by rfc2253.
2002-08-23 Werner Koch <wk@gnupg.org>
* ksba.m4: Removed unnecessary libs
* ksba-config.in: Made --prefix work for --libs.
* Makefile.am (EXTRA_DIST): Add ksba.m4
2002-08-21 Werner Koch <wk@gnupg.org>
* der-encoder.c (_ksba_der_copy_tree)
* crl.c (ksba_crl_get_issuer)
* cms.c (ksba_cms_get_issuer_serial)
(ksba_cms_get_sig_val)
(ksba_cms_get_enc_val)
* cert.c (ksba_cert_get_image)
(ksba_cert_hash)
(ksba_cert_get_serial)
(ksba_cert_get_sig_val): Removed all debugging output for encoding
errors.
2002-08-13 Werner Koch <wk@gnupg.org>
* ksba.m4: New.
2002-08-06 Werner Koch <wk@gnupg.org>
* cms.h (signer_info_s, sig_val_s): New. Actually moved out of
ksba_cms_s and made it pointers there.
* cms.c (ksba_cms_release): Changed for new types of signer_info
and sig_val.
(ksba_cms_get_issuer_serial): Implemented index for signer_info.
(ksba_cms_get_digest_algo): Ditto.
(ksba_cms_get_message_digest): Ditto.
(ksba_cms_get_sig_val): Ditto.
(ksba_cms_hash_signed_attrs): Ditto.
(build_signed_data_attributes): Build the list of signer_infos.
(build_signed_data_rest): And process the list.
(ksba_cms_set_sig_val): Append values.
(build_signed_data_rest): Use the sig_val list.
(ct_build_signed_data): Changed check for set sig_val.
(build_signed_data_header): Fixed writing of more than one algo.
* cms-parser.c (_ksba_cms_parse_signed_data_part_2): Create a list
of signer_infos and not just one.
2002-08-05 Werner Koch <wk@gnupg.org>
* cms.c (ksba_cms_add_cert): Don't add duplicates.
* cert.c (_ksba_cert_cmp): New.
2002-07-04 Werner Koch <wk@gnupg.org>
* cms.c (ksba_cms_identify): Make sure to read the full first 20
bytes.
2002-07-02 Werner Koch <wk@gnupg.org>
* certreq.c (ksba_certreq_set_sig_val): Don't store a leanding zero.
2002-06-27 Werner Koch <wk@gnupg.org>
* cert.c (ksba_cert_get_auth_key_id): Skip keyIdentifier tag.
2002-06-17 Werner Koch <wk@gnupg.org>
* cms.c (write_encrypted_cont): Don't use write_octet_stream here
because this is used with an implicit tag and the outer context
already provides the constructed containter.
2002-06-13 Werner Koch <wk@gnupg.org>
* writer.c (ksba_writer_write_octet_string): Correctly write
undefined length octet strings.
* cms.c (ct_build_signed_data): Write an end tag for non-detached
sigs.
(build_enveloped_data_header): Fix doc; it is an explicit octect
string.
(read_encrypted_cont): Read constructed octet strings.
2002-06-12 Werner Koch <wk@gnupg.org>
* writer.c (ksba_writer_write_octet_string): New.
* cms.c (write_encrypted_cont): Use it here.
2002-05-17 Werner Koch <wk@gnupg.org>
* Makefile.am: Tweaked to avoid double compilations.
* cms.c (ksba_cms_identify): New.
2002-05-16 Werner Koch <wk@gnupg.org>
* cms-parser.c (_ksba_cms_parse_signed_data_part_1): Never
allocate 0 bytes - this is not defined.
2002-05-04 Werner Koch <wk@gnupg.org>
* dn.c (parse_rdn): Better detection of empty elements.
2002-04-27 Werner Koch <wk@gnupg.org>
* cms.c (read_and_hash_cont): Handle constructed octet strings.
* cms-parser.c (_ksba_cms_parse_signed_data_part_2): Eat one
pending end tag.
2002-04-15 Werner Koch <wk@gnupg.org>
* version.c: New.
* dn.c (append_ucs4_value,append_ucs2_value): Implemented.
* cert.c (ksba_cert_get_auth_key_id): New.
* name.c (_ksba_name_new_from_der): Add support for "Name" type.
* dn.c (_ksba_derdn_to_str): New.
2002-03-22 Werner Koch <wk@gnupg.org>
* cert.c (get_name): Fixed enumerating of alternate names.
* name.c: New.
* ksba.h (KsbaName): New. Added defintions for the name functions.
(KsbaCRLReason): Changed values to allow use as bit flags.
* cert.c (parse_distribution_point): New.
(ksba_cert_get_crl_dist_point): New.
2002-03-15 Werner Koch <wk@gnupg.org>
* asn1-func.c (_ksba_asn_check_identifier): Better check against
overflow, also the data used is static.
2002-03-13 Werner Koch <wk@gnupg.org>
* cms.h (value_tree_s): New and use it for recp_info.
(enc_val_s): New, use it in certlist and remove it from the
ksba_cms_s and change all users.
* cms.c (ksba_cms_set_enc_val): Allow multiple recipients.
(ksba_cms_add_signer): Keep ordering of signers so that they can
be implictly counted by an index.
(build_enveloped_data_header): Fixed the creation of the RID.
* cms.c (release_value_tree): New.
(ksba_cms_release): And use it here.
(ksba_cms_get_issuer_serial): Use the new recp_list structure and
take the IDX into account.
(ksba_cms_get_enc_val): Ditto.
(ksba_cms_set_sig_val,ksba_cms_set_enc_val): Don't store a leading
zero.
* cms-parser.c (_ksba_cms_parse_enveloped_data_part_1): Handle
multiple recipients.
* cms.c (build_signed_data_rest): Write 3 end tags.
(ct_build_enveloped_data): Write 4 end tags.
2002-03-12 Werner Koch <wk@gnupg.org>
* cms-parser.c (_ksba_cms_parse_signed_data_part_2): Allow an
empty set of signer infos.
2002-03-11 Werner Koch <wk@gnupg.org>
* keyinfo.c: Fixed last change; forgot to set the length.
2002-03-09 Werner Koch <wk@gnupg.org>
* keyinfo.c: Add algo for Telesec NetKey cards.
2002-02-19 Werner Koch <wk@gnupg.org>
* cert.c (ksba_cert_get_cert_policies): New.
2002-02-07 Werner Koch <wk@gnupg.org>
* cert.c (ksba_cert_release): Release the nodes.
2002-02-01 Marcus Brinkmann <marcus@g10code.de>
* asn1-parse.y: Add missing colon at end of block.
2002-02-01 Werner Koch <wk@gnupg.org>
* cms.c (ksba_cms_set_sig_val): Add kludge to allow "rsa" instead
of an OID.
2002-01-28 Werner Koch <wk@gnupg.org>
* oid.c (ksba_oid_from_str): Fixed docs and return type.
* certreq.c (ksba_certreq_set_subject): Renamed to..
(ksba_certreq_add_subject): this and added logic to store
subjectAltNames.
(build_extensions): New.
(build_cri): Write the extensions.
* ber-help.c (_ksba_ber_encode_tl): New.
2002-01-25 Werner Koch <wk@gnupg.org>
* cms.c (build_signed_data_attributes): Write the optional certs.
* cert.c (ksba_cert_get_image): Don't return the image length but
the parsed length.
2002-01-24 Werner Koch <wk@gnupg.org>
* cms.h: Add new member cert_info_list.
* cms.c (ksba_cms_add_cert): New.
(ksba_cms_release): Release the cert info list.
2002-01-23 Werner Koch <wk@gnupg.org>
* crl.c (parse_to_next_update): Use measured length to fixup the
tbs_len after reading the name. Check tbs_len before checking
for the entry list.
* ksba.h (KsbaKeyUsage): New.
* cert.c (ksba_cert_get_key_usage): New.
* cert.c (get_name): New, implemented rfc822 AltNames.
(ksba_cert_get_issuer,ksba_cert_get_subject): Use it.
2002-01-22 Werner Koch <wk@gnupg.org>
* cert.c (ksba_cert_is_ca): New.
* ber-help.c (premature_eof): New.
(eof_or_error): Use it here.
(_ksba_ber_parse_tl): New.
2002-01-21 Werner Koch <wk@gnupg.org>
* cert.c (ksba_cert_get_extension): New.
(read_extensions): New.
(ksba_cert_release): Release the cached extension info.
2002-01-14 Werner Koch <wk@gnupg.org>
* dn.c: Fixed oid table according to rfc2253.
(append_atv): Do only use the allowed names.
2002-01-11 Werner Koch <wk@gnupg.org>
* dn.c (append_atv): Don't write a trailing hash sign.
* time.c (_ksba_asntime_to_epoch): Kludge to cope with the Y2038
problem.
* crl.c (do_hash): New. Apply the hashing where needed.
2002-01-10 Werner Koch <wk@gnupg.org>
* certreq.c, certreq.h: New.
* keyinfo.c (_ksba_keyinfo_from_sexp): New.
* der-encoder.c: Removed commented code.
2002-01-09 Werner Koch <wk@gnupg.org>
* dn.c (count_quoted_string, parse_rdn, write_escaped): New.
(_ksba_dn_from_str): Implemented.
* ber-help.c (_ksba_ber_count_tl): New.
* util.h (spacep): New.
(hexdigitp): Now takes a pointer as arg.
* writer.c (do_writer_write): Set the error flag when we are out
of core.
(ksba_writer_snatch_mem): New.
2002-01-08 Werner Koch <wk@gnupg.org>
* writer.c (ksba_writer_set_mem): New.
(do_writer_write): Implemented it here.
(ksba_writer_get_mem): New.
* tmttv2.asn (CertificationRequestInfo): Added.
2002-01-07 Werner Koch <wk@gnupg.org>
* ksba.h (KsbaCRLReason): New.
* crl.c: Basically works.
2002-01-05 Werner Koch <wk@gnupg.org>
* asn1-func.c (_ksba_asn_find_node): Moved code to ..
(find_node): new function which allows resolving of identifiers.
(_ksba_asn_expand_tree): Use the resolving find_node here.
* keyinfo.c (_ksba_parse_algorithm_identifier2): Reset len2 so
that a given r_parm returns correct values.
* ksba.h (KsbaCRL): New typedef and prototypes for the CRL functions.
* crl.c, crl.h: New but not yet complete.
2001-12-20 Werner Koch <wk@gnupg.org>
* cms.c (read_and_hash_cont): New.
(ct_parse_signed_data): Use it here for non-detached signatures.
* cms-parser.c (_ksba_cms_parse_signed_data_part_1): Store the
inner content length.
(_ksba_cms_parse_signed_data_part_2): Add dummy read code for CRLs.
* cert.c (ksba_cert_set_serial): Changed to return an entire S-Exp
and not just an octet string.
* cms.c (ksba_cms_get_issuer_serial): Ditto.
2001-12-18 Werner Koch <wk@gnupg.org>
* Makefile.am (DISTCLEANFILES): Add asn1-tables.c
2001-12-17 Werner Koch <wk@gnupg.org>
* cert.c (ksba_cert_get_serial): Does now return a S-expression.
* cms.c (ksba_cms_get_issuer_serial): Ditto.
* keyinfo.c: Changed all functions to return canonical encoded S-Exp.
* Makefile.am (asn1-tables.c): Fixed rule to work with VPATH builds.
2001-12-14 Werner Koch <wk@gnupg.org>
* oid.c, time.c, cms.c : Moved atoi macros and digitp macro to ..
* util.h: .. here. Fixed digitp macro. Added hexdigitp.
2001-12-13 Werner Koch <wk@gnupg.org>
* cert.c (ksba_cert_get_issuer): Added an idx argument so that the
function can be used to enumerate alternate names.
(ksba_cert_get_subject): Ditto.
Copyright 2001, 2002, 2003, 2004, 2005 g10 Code GmbH
This file is free software; as a special exception the author gives
unlimited permission to copy and/or distribute it, with or without
modifications, as long as this notice is preserved.
This file is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY, to the extent permitted by law; without even the
implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
diff --git a/src/ber-decoder.c b/src/ber-decoder.c
index 414fda9..c5dc7d6 100644
--- a/src/ber-decoder.c
+++ b/src/ber-decoder.c
@@ -1,1173 +1,1179 @@
/* ber-decoder.c - Basic Encoding Rules Decoder
* Copyright (C) 2001, 2004 g10 Code GmbH
*
* This file is part of KSBA.
*
* KSBA 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 2 of the License, or
* (at your option) any later version.
*
* KSBA 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, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
*/
#include <config.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include "util.h"
#include "ksba.h"
#include "asn1-func.h"
#include "ber-decoder.h"
#include "ber-help.h"
struct decoder_state_item_s {
AsnNode node;
int went_up;
int in_seq_of;
int in_any; /* actually in a constructed any */
int again;
int next_tag;
int length; /* length of the value */
int ndef_length; /* the length is of indefinite length */
int nread; /* number of value bytes processed */
};
typedef struct decoder_state_item_s DECODER_STATE_ITEM;
struct decoder_state_s {
DECODER_STATE_ITEM cur; /* current state */
int stacksize;
int idx;
DECODER_STATE_ITEM stack[1];
};
typedef struct decoder_state_s *DECODER_STATE;
struct ber_decoder_s {
AsnNode module; /* the ASN.1 structure */
ksba_reader_t reader;
const char *last_errdesc; /* string with the error description */
int non_der; /* set if the encoding is not DER conform */
AsnNode root; /* of the expanded parse tree */
DECODER_STATE ds;
int bypass;
int honor_module_end;
int debug;
int use_image;
struct {
unsigned char *buf;
size_t used;
size_t length;
} image;
struct {
int primitive; /* current value is a primitive one */
int length; /* length of the primitive one */
int nhdr; /* length of the header */
int tag;
int is_endtag;
AsnNode node; /* NULL or matching node */
} val;
};
static DECODER_STATE
new_decoder_state (void)
{
DECODER_STATE ds;
ds = xmalloc (sizeof (*ds) + 99*sizeof(DECODER_STATE_ITEM));
ds->stacksize = 100;
ds->idx = 0;
ds->cur.node = NULL;
ds->cur.went_up = 0;
ds->cur.in_seq_of = 0;
ds->cur.in_any = 0;
ds->cur.again = 0;
ds->cur.next_tag = 0;
ds->cur.length = 0;
ds->cur.ndef_length = 1;
ds->cur.nread = 0;
return ds;
}
static void
release_decoder_state (DECODER_STATE ds)
{
xfree (ds);
}
static void
dump_decoder_state (DECODER_STATE ds)
{
int i;
for (i=0; i < ds->idx; i++)
{
- fprintf (stdout," ds stack[%d] (", i);
+ fprintf (stderr," ds stack[%d] (", i);
if (ds->stack[i].node)
- _ksba_asn_node_dump (ds->stack[i].node, stdout);
+ _ksba_asn_node_dump (ds->stack[i].node, stderr);
else
- printf ("Null");
- fprintf (stdout,") %s%d (%d)%s\n",
+ fprintf (stderr, "Null");
+ fprintf (stderr,") %s%d (%d)%s\n",
ds->stack[i].ndef_length? "ndef ":"",
ds->stack[i].length,
ds->stack[i].nread,
ds->stack[i].in_seq_of? " in_seq_of":"");
}
}
/* Push ITEM onto the stack */
static void
push_decoder_state (DECODER_STATE ds)
{
if (ds->idx >= ds->stacksize)
{
fprintf (stderr, "ERROR: decoder stack overflow!\n");
abort ();
}
ds->stack[ds->idx++] = ds->cur;
}
static void
pop_decoder_state (DECODER_STATE ds)
{
if (!ds->idx)
{
fprintf (stderr, "ERROR: decoder stack underflow!\n");
abort ();
}
ds->cur = ds->stack[--ds->idx];
}
static int
set_error (BerDecoder d, AsnNode node, const char *text)
{
fprintf (stderr,"ber-decoder: node `%s': %s\n",
node? node->name:"?", text);
d->last_errdesc = text;
return gpg_error (GPG_ERR_BAD_BER);
}
static int
eof_or_error (BerDecoder d, int premature)
{
gpg_error_t err;
err = ksba_reader_error (d->reader);
if (err)
{
set_error (d, NULL, "read error");
return err;
}
if (premature)
return set_error (d, NULL, "premature EOF");
return gpg_error (GPG_ERR_EOF);
}
static const char *
universal_tag_name (unsigned long no)
{
static const char *names[31] = {
"[End Tag]",
"BOOLEAN",
"INTEGER",
"BIT STRING",
"OCTECT STRING",
"NULL",
"OBJECT IDENTIFIER",
"ObjectDescriptor",
"EXTERNAL",
"REAL",
"ENUMERATED",
"EMBEDDED PDV",
"UTF8String",
"RELATIVE-OID",
"[UNIVERSAL 14]",
"[UNIVERSAL 15]",
"SEQUENCE",
"SET",
"NumericString",
"PrintableString",
"TeletexString",
"VideotexString",
"IA5String",
"UTCTime",
"GeneralizedTime",
"GraphicString",
"VisibleString",
"GeneralString",
"UniversalString",
"CHARACTER STRING",
"BMPString"
};
return no < DIM(names)? names[no]:NULL;
}
static void
dump_tlv (const struct tag_info *ti, FILE *fp)
{
const char *tagname = NULL;
if (ti->class == CLASS_UNIVERSAL)
tagname = universal_tag_name (ti->tag);
if (tagname)
fputs (tagname, fp);
else
fprintf (fp, "[%s %lu]",
ti->class == CLASS_UNIVERSAL? "UNIVERSAL" :
ti->class == CLASS_APPLICATION? "APPLICATION" :
ti->class == CLASS_CONTEXT? "CONTEXT-SPECIFIC" : "PRIVATE",
ti->tag);
fprintf (fp, " %c hdr=%lu len=",
ti->is_constructed? 'c':'p',
(unsigned long)ti->nhdr);
if (ti->ndef)
fputs ("ndef", fp);
else
fprintf (fp, "%lu", ti->length);
}
static void
clear_help_flags (AsnNode node)
{
AsnNode p;
for (p=node; p; p = _ksba_asn_walk_tree (node, p))
{
if (p->type == TYPE_TAG)
{
p->flags.tag_seen = 0;
}
p->flags.skip_this = 0;
}
}
static void
prepare_copied_tree (AsnNode node)
{
AsnNode p;
clear_help_flags (node);
for (p=node; p; p = _ksba_asn_walk_tree (node, p))
p->off = -1;
}
static void
fixup_type_any (AsnNode node)
{
AsnNode p;
for (p=node; p; p = _ksba_asn_walk_tree (node, p))
{
if (p->type == TYPE_ANY && p->off != -1)
p->type = p->actual_type;
}
}
BerDecoder
_ksba_ber_decoder_new (void)
{
BerDecoder d;
d = xtrycalloc (1, sizeof *d);
if (!d)
return NULL;
return d;
}
void
_ksba_ber_decoder_release (BerDecoder d)
{
xfree (d);
}
/**
* _ksba_ber_decoder_set_module:
* @d: Decoder object
* @module: ASN.1 Parse tree
*
* Initialize the decoder with the ASN.1 module. Note, that this is a
* shallow copy of the module. Hmmm: What about ref-counting of
* AsnNodes?
*
* Return value: 0 on success or an error code
**/
gpg_error_t
_ksba_ber_decoder_set_module (BerDecoder d, ksba_asn_tree_t module)
{
if (!d || !module)
return gpg_error (GPG_ERR_INV_VALUE);
if (d->module)
return gpg_error (GPG_ERR_CONFLICT); /* module already set */
d->module = module->parse_tree;
return 0;
}
gpg_error_t
_ksba_ber_decoder_set_reader (BerDecoder d, ksba_reader_t r)
{
if (!d || !r)
return gpg_error (GPG_ERR_INV_VALUE);
if (d->reader)
return gpg_error (GPG_ERR_CONFLICT); /* reader already set */
d->reader = r;
return 0;
}
/**********************************************
*********** decoding machinery *************
**********************************************/
/* Read one byte, return that byte or -1 on error or EOF. */
static int
read_byte (ksba_reader_t reader)
{
unsigned char buf;
size_t nread;
int rc;
do
rc = ksba_reader_read (reader, &buf, 1, &nread);
while (!rc && !nread);
return rc? -1: buf;
}
/* Read COUNT bytes into buffer. BUFFER may be NULL to skip over
COUNT bytes. Return 0 on success or -1 on error. */
static int
read_buffer (ksba_reader_t reader, char *buffer, size_t count)
{
size_t nread;
if (buffer)
{
while (count)
{
if (ksba_reader_read (reader, buffer, count, &nread))
return -1;
buffer += nread;
count -= nread;
}
}
else
{
char dummy[256];
size_t n;
while (count)
{
n = count > DIM(dummy) ? DIM(dummy): count;
if (ksba_reader_read (reader, dummy, n, &nread))
return -1;
count -= nread;
}
}
return 0;
}
/* Return 0 for no match, 1 for a match and 2 for an ANY match of an
constructed type */
static int
cmp_tag (AsnNode node, const struct tag_info *ti)
{
if (node->flags.class != ti->class)
{
if (node->flags.class == CLASS_UNIVERSAL && node->type == TYPE_ANY)
return ti->is_constructed? 2:1;
return 0;
}
if (node->type == TYPE_TAG)
{
return_val_if_fail (node->valuetype == VALTYPE_ULONG, 0);
return node->value.v_ulong == ti->tag;
}
if (node->type == ti->tag)
return 1;
if (ti->class == CLASS_UNIVERSAL)
{
if (node->type == TYPE_SEQUENCE_OF && ti->tag == TYPE_SEQUENCE)
return 1;
if (node->type == TYPE_SET_OF && ti->tag == TYPE_SET)
return 1;
if (node->type == TYPE_ANY)
return _ksba_asn_is_primitive (ti->tag)? 1:2;
}
return 0;
}
/* Find the node in the tree ROOT corresponding to TI and return that
node. Returns NULL if the node was not found */
static AsnNode
find_anchor_node (AsnNode root, const struct tag_info *ti)
{
AsnNode node = root;
while (node)
{
if (cmp_tag (node, ti))
{
return node; /* found */
}
if (node->down)
node = node->down;
else if (node == root)
return NULL; /* not found */
else if (node->right)
node = node->right;
else
{ /* go up and right */
do
{
while (node->left && node->left->right == node)
node = node->left;
node = node->left;
if (!node || node == root)
return NULL; /* back at the root -> not found */
}
while (!node->right);
node = node->right;
}
}
return NULL;
}
static int
match_der (AsnNode root, const struct tag_info *ti,
DECODER_STATE ds, AsnNode *retnode, int debug)
{
int rc;
AsnNode node;
*retnode = NULL;
node = ds->cur.node;
if (!node)
{
if (debug)
- puts (" looking for anchor");
+ fputs (" looking for anchor\n", stderr);
node = find_anchor_node (root, ti);
if (!node)
- fputs (" anchor node not found\n", stdout);
+ fputs (" anchor node not found\n", stderr);
}
else if (ds->cur.again)
{
if (debug)
- puts (" doing last again");
+ fputs (" doing last again\n", stderr);
ds->cur.again = 0;
}
else if (_ksba_asn_is_primitive (node->type) || node->type == TYPE_ANY
|| node->type == TYPE_SIZE || node->type == TYPE_DEFAULT )
{
if (debug)
- puts (" primitive type - get next");
+ fputs (" primitive type - get next\n", stderr);
if (node->right)
node = node->right;
else if (!node->flags.in_choice)
{
if (node->flags.is_implicit)
{
if (debug)
- puts (" node was implicit - advancing");
+ fputs (" node was implicit - advancing\n", stderr);
while (node->left && node->left->right == node)
node = node->left;
node = node->left; /* this is the up pointer */
if (node)
node = node->right;
}
else
node = NULL;
}
else /* in choice */
{
if (debug)
- puts (" going up after choice - get next");
+ fputs (" going up after choice - get next\n", stderr);
while (node->left && node->left->right == node)
node = node->left;
node = node->left; /* this is the up pointer */
if (node)
node = node->right;
}
}
else if (node->type == TYPE_SEQUENCE_OF || node->type == TYPE_SET_OF)
{
if (debug)
{
- printf (" prepare for seq/set_of (%d %d) ",
+ fprintf (stderr, " prepare for seq/set_of (%d %d) ",
ds->cur.length, ds->cur.nread);
- printf (" cur: ("); _ksba_asn_node_dump (node, stdout);
- printf (")\n");
+ fprintf (stderr, " cur: ("); _ksba_asn_node_dump (node, stderr);
+ fprintf (stderr, ")\n");
if (ds->cur.node->flags.in_array)
- puts (" This is in an array!");
+ fputs (" This is in an array!\n", stderr);
if (ds->cur.went_up)
- puts (" And we are going up!");
+ fputs (" And we are going up!\n", stderr);
}
if ((ds->cur.went_up && !ds->cur.node->flags.in_array) ||
(ds->idx && ds->cur.nread >= ds->stack[ds->idx-1].length))
{
if (debug)
- printf (" advancing\n");
+ fprintf (stderr, " advancing\n");
if (node->right)
node = node->right;
else
{
for (;;)
{
while (node->left && node->left->right == node)
node = node->left;
node = node->left; /* this is the up pointer */
if (!node)
break;
if (node->right)
{
node = node->right;
break;
}
}
}
}
else if (ds->cur.node->flags.in_array
&& ds->cur.went_up)
{
if (debug)
- puts (" Reiterating");
+ fputs (" Reiterating\n", stderr);
node = _ksba_asn_insert_copy (node);
if (node)
prepare_copied_tree (node);
}
else
node = node->down;
}
else /* constructed */
{
if (debug)
{
- printf (" prepare for constructed (%d %d) ",
+ fprintf (stderr, " prepare for constructed (%d %d) ",
ds->cur.length, ds->cur.nread);
- printf (" cur: ("); _ksba_asn_node_dump (node, stdout);
- printf (")\n");
+ fprintf (stderr, " cur: ("); _ksba_asn_node_dump (node, stderr);
+ fprintf (stderr, ")\n");
if (ds->cur.node->flags.in_array)
- puts (" This is in an array!");
+ fputs (" This is in an array!\n", stderr);
if (ds->cur.went_up)
- puts (" And we going up!");
+ fputs (" And we going up!\n", stderr);
}
ds->cur.in_seq_of = 0;
if (ds->cur.node->flags.in_array
&& ds->cur.went_up)
{
if (debug)
- puts (" Reiterating this");
+ fputs (" Reiterating this\n", stderr);
node = _ksba_asn_insert_copy (node);
if (node)
prepare_copied_tree (node);
}
else if (ds->cur.went_up || ds->cur.next_tag || ds->cur.node->flags.skip_this)
{
if (node->right)
node = node->right;
else
{
for (;;)
{
while (node->left && node->left->right == node)
node = node->left;
node = node->left; /* this is the up pointer */
if (!node)
break;
if (node->right)
{
node = node->right;
break;
}
}
}
}
else
node = node->down;
}
if (!node)
return -1;
ds->cur.node = node;
ds->cur.went_up = 0;
ds->cur.next_tag = 0;
if (debug)
{
- printf (" Expect ("); _ksba_asn_node_dump (node,stdout); printf (")\n");
+ fprintf (stderr, " Expect ("); _ksba_asn_node_dump (node,stderr); fprintf (stderr, ")\n");
}
if (node->flags.skip_this)
{
if (debug)
- printf (" skipping this\n");
+ fprintf (stderr, " skipping this\n");
return 1;
}
if (node->type == TYPE_SIZE)
{
if (debug)
- printf (" skipping size tag\n");
+ fprintf (stderr, " skipping size tag\n");
return 1;
}
if (node->type == TYPE_DEFAULT)
{
if (debug)
- printf (" skipping default tag\n");
+ fprintf (stderr, " skipping default tag\n");
return 1;
}
if (node->flags.is_implicit)
{
if (debug)
- printf (" dummy accept for implicit tag\n");
+ fprintf (stderr, " dummy accept for implicit tag\n");
return 1; /* again */
}
if ( (rc=cmp_tag (node, ti)))
{
*retnode = node;
return rc==2? 4:3;
}
if (node->type == TYPE_CHOICE)
{
if (debug)
- printf (" testing choice...\n");
+ fprintf (stderr, " testing choice...\n");
for (node = node->down; node; node = node->right)
{
if (debug)
{
- printf (" %s (", node->flags.skip_this? "skip":" cmp");
- _ksba_asn_node_dump (node, stdout);
- printf (")\n");
+ fprintf (stderr, " %s (", node->flags.skip_this? "skip":" cmp");
+ _ksba_asn_node_dump (node, stderr);
+ fprintf (stderr, ")\n");
}
if (!node->flags.skip_this && cmp_tag (node, ti) == 1)
{
if (debug)
{
- printf (" choice match <"); dump_tlv (ti, stdout);
- printf (">\n");
+ fprintf (stderr, " choice match <"); dump_tlv (ti, stderr);
+ fprintf (stderr, ">\n");
}
/* mark the remaining as done */
for (node=node->right; node; node = node->right)
node->flags.skip_this = 1;
return 1;
}
node->flags.skip_this = 1;
}
node = ds->cur.node; /* reset */
}
if (node->flags.in_choice)
{
if (debug)
- printf (" skipping non matching choice\n");
+ fprintf (stderr, " skipping non matching choice\n");
return 1;
}
if (node->flags.is_optional)
{
if (debug)
- printf (" skipping optional element\n");
+ fprintf (stderr, " skipping optional element\n");
if (node->type == TYPE_TAG)
ds->cur.next_tag = 1;
return 1;
}
if (node->flags.has_default)
{
if (debug)
- printf (" use default value\n");
+ fprintf (stderr, " use default value\n");
if (node->type == TYPE_TAG)
ds->cur.next_tag = 1;
*retnode = node;
return 2;
}
return -1;
}
static gpg_error_t
decoder_init (BerDecoder d, const char *start_name)
{
d->ds = new_decoder_state ();
d->root = _ksba_asn_expand_tree (d->module, start_name);
clear_help_flags (d->root);
d->bypass = 0;
if (d->debug)
- printf ("DECODER_INIT for `%s'\n", start_name? start_name: "[root]");
+ fprintf (stderr, "DECODER_INIT for `%s'\n", start_name? start_name: "[root]");
return 0;
}
static void
decoder_deinit (BerDecoder d)
{
release_decoder_state (d->ds);
d->ds = NULL;
d->val.node = NULL;
if (d->debug)
- printf ("DECODER_DEINIT\n");
+ fprintf (stderr, "DECODER_DEINIT\n");
}
static gpg_error_t
decoder_next (BerDecoder d)
{
struct tag_info ti;
AsnNode node;
gpg_error_t err;
DECODER_STATE ds = d->ds;
int debug = d->debug;
err = _ksba_ber_read_tl (d->reader, &ti);
if (err)
{
return err;
}
if (debug)
{
- printf ("ReadTLV <"); dump_tlv (&ti, stdout); printf (">\n");
+ fprintf (stderr, "ReadTLV <");
+ dump_tlv (&ti, stderr);
+ fprintf (stderr, ">\n");
}
if (d->use_image)
{
if (!d->image.buf)
{
/* we need some extra bytes to store the stuff we read ahead
at the end of the module which is later pushed back */
d->image.length = ti.length + 100;
d->image.used = 0;
d->image.buf = xtrymalloc (d->image.length);
if (!d->image.buf)
return gpg_error (GPG_ERR_ENOMEM);
}
if (ti.nhdr + d->image.used >= d->image.length)
return set_error (d, NULL, "image buffer too short to store the tag");
memcpy (d->image.buf + d->image.used, ti.buf, ti.nhdr);
d->image.used += ti.nhdr;
}
if (!d->bypass)
{
int again, endtag;
do
{
again = endtag = 0;
switch ( ds->cur.in_any? 4
: (ti.class == CLASS_UNIVERSAL && !ti.tag)? (endtag=1,5)
: match_der (d->root, &ti, ds, &node, debug))
{
case -1:
if (debug)
{
- printf (" FAIL <"); dump_tlv (&ti, stdout); printf (">\n");
+ fprintf (stderr, " FAIL <");
+ dump_tlv (&ti, stderr);
+ fprintf (stderr, ">\n");
}
if (d->honor_module_end)
{
/* We must push back the stuff we already read */
ksba_reader_unread (d->reader, ti.buf, ti.nhdr);
return gpg_error (GPG_ERR_EOF);
}
else
d->bypass = 1;
break;
case 0:
if (debug)
- puts (" End of description");
+ fputs (" End of description\n", stderr);
d->bypass = 1;
break;
case 1: /* again */
if (debug)
- printf (" Again\n");
+ fprintf (stderr, " Again\n");
again = 1;
break;
case 2: /* Use default value + again */
if (debug)
- printf (" Using default\n");
+ fprintf (stderr, " Using default\n");
again = 1;
break;
case 4: /* Match of ANY on a constructed type */
if (debug)
- printf (" ANY");
+ fprintf (stderr, " ANY");
ds->cur.in_any = 1;
case 3: /* match */
case 5: /* end tag */
if (debug)
{
- printf (" Match <"); dump_tlv (&ti, stdout); printf (">\n");
+ fprintf (stderr, " Match <");
+ dump_tlv (&ti, stderr);
+ fprintf (stderr, ">\n");
}
/* Increment by the header length */
ds->cur.nread += ti.nhdr;
if (!ti.is_constructed)
ds->cur.nread += ti.length;
ds->cur.went_up = 0;
do
{
if (debug)
- printf (" (length %d nread %d) %s\n",
+ fprintf (stderr, " (length %d nread %d) %s\n",
ds->idx? ds->stack[ds->idx-1].length:-1,
ds->cur.nread,
ti.is_constructed? "con":"pri");
if ( ds->idx
&& !ds->stack[ds->idx-1].ndef_length
&& (ds->cur.nread
> ds->stack[ds->idx-1].length))
{
- fprintf (stderr, " ERROR: object length field %d octects"
- " too large\n",
+ fprintf (stderr, " ERROR: object length field "
+ "%d octects too large\n",
ds->cur.nread > ds->cur.length);
ds->cur.nread = ds->cur.length;
}
if ( ds->idx
&& (endtag
|| (!ds->stack[ds->idx-1].ndef_length
&& (ds->cur.nread
>= ds->stack[ds->idx-1].length))))
{
int n = ds->cur.nread;
pop_decoder_state (ds);
ds->cur.nread += n;
ds->cur.went_up++;
}
endtag = 0;
}
while ( ds->idx
&& !ds->stack[ds->idx-1].ndef_length
&& (ds->cur.nread
>= ds->stack[ds->idx-1].length));
- if (ti.is_constructed)
+ if (ti.is_constructed && (ti.length || ti.ndef))
{
/* prepare for the next level */
ds->cur.length = ti.length;
ds->cur.ndef_length = ti.ndef;
push_decoder_state (ds);
ds->cur.length = 0;
ds->cur.ndef_length = 0;
ds->cur.nread = 0;
}
if (debug)
- printf (" (length %d nread %d) end\n",
+ fprintf (stderr, " (length %d nread %d) end\n",
ds->idx? ds->stack[ds->idx-1].length:-1,
ds->cur.nread);
break;
default:
never_reached ();
abort ();
break;
}
}
while (again);
}
d->val.primitive = !ti.is_constructed;
d->val.length = ti.length;
d->val.nhdr = ti.nhdr;
d->val.tag = ti.tag; /* kludge to fix TYPE_ANY probs */
d->val.is_endtag = (ti.class == CLASS_UNIVERSAL && !ti.tag);
d->val.node = d->bypass? NULL : node;
if (debug)
dump_decoder_state (ds);
return 0;
}
static gpg_error_t
decoder_skip (BerDecoder d)
{
if (d->val.primitive)
{
if (read_buffer (d->reader, NULL, d->val.length))
return eof_or_error (d, 1);
}
return 0;
}
/* Calculate the distance between the 2 nodes */
static int
distance (AsnNode root, AsnNode node)
{
int n=0;
while (node && node != root)
{
while (node->left && node->left->right == node)
node = node->left;
node = node->left;
n++;
}
return n;
}
/**
* _ksba_ber_decoder_dump:
* @d: Decoder object
*
* Dump a textual representation of the encoding to the given stream.
*
* Return value:
**/
gpg_error_t
_ksba_ber_decoder_dump (BerDecoder d, FILE *fp)
{
gpg_error_t err;
int depth = 0;
AsnNode node;
unsigned char *buf = NULL;
size_t buflen = 0;;
if (!d)
return gpg_error (GPG_ERR_INV_VALUE);
d->debug = !!getenv("DEBUG_BER_DECODER");
d->use_image = 0;
d->image.buf = NULL;
err = decoder_init (d, NULL);
if (err)
return err;
while (!(err = decoder_next (d)))
{
node = d->val.node;
if (node)
depth = distance (d->root, node);
fprintf (fp, "%4lu %4u:%*s",
ksba_reader_tell (d->reader) - d->val.nhdr,
d->val.length,
depth*2, "");
if (node)
_ksba_asn_node_dump (node, fp);
else
fputs ("[No matching node]", fp);
if (node && d->val.primitive)
{
int i, n, c;
char *p;
if (!buf || buflen < d->val.length)
{
xfree (buf);
buflen = d->val.length + 100;
buf = xtrymalloc (buflen);
if (!buf)
err = gpg_error (GPG_ERR_ENOMEM);
}
for (n=0; !err && n < d->val.length; n++)
{
if ( (c=read_byte (d->reader)) == -1)
err = eof_or_error (d, 1);
buf[n] = c;
}
if (err)
break;
fputs (" (", fp);
p = NULL;
switch (node->type)
{
case TYPE_OBJECT_ID:
p = ksba_oid_to_str (buf, n);
break;
default:
for (i=0; i < n && i < 20; i++)
fprintf (fp,"%02x", buf[i]);
if (i < n)
fputs ("..more..", fp);
break;
}
if (p)
{
fputs (p, fp);
xfree (p);
}
fputs (")\n", fp);
}
else
{
err = decoder_skip (d);
putc ('\n', fp);
}
if (err)
break;
}
if (gpg_err_code (err) == GPG_ERR_EOF)
err = 0;
decoder_deinit (d);
xfree (buf);
return err;
}
gpg_error_t
_ksba_ber_decoder_decode (BerDecoder d, const char *start_name,
AsnNode *r_root,
unsigned char **r_image, size_t *r_imagelen)
{
gpg_error_t err;
AsnNode node;
unsigned char *buf = NULL;
size_t buflen = 0;
unsigned long startoff;
if (!d)
return gpg_error (GPG_ERR_INV_VALUE);
if (r_root)
*r_root = NULL;
d->debug = !!getenv("DEBUG_BER_DECODER");
d->honor_module_end = 1;
d->use_image = 1;
d->image.buf = NULL;
startoff = ksba_reader_tell (d->reader);
err = decoder_init (d, start_name);
if (err)
return err;
while (!(err = decoder_next (d)))
{
int n, c;
node = d->val.node;
if (node && d->use_image)
{
if (!d->val.is_endtag)
{ /* We don't have nodes for the end tag - so don't store it */
node->off = (ksba_reader_tell (d->reader)
- d->val.nhdr - startoff);
node->nhdr = d->val.nhdr;
node->len = d->val.length;
if (node->type == TYPE_ANY)
node->actual_type = d->val.tag;
}
if (d->image.used + d->val.length > d->image.length)
err = set_error(d, NULL, "TLV length too large");
else if (d->val.primitive)
{
if( read_buffer (d->reader,
d->image.buf + d->image.used, d->val.length))
err = eof_or_error (d, 1);
else
d->image.used += d->val.length;
}
}
else if (node && d->val.primitive)
{
if (!buf || buflen < d->val.length)
{
xfree (buf);
buflen = d->val.length + 100;
buf = xtrymalloc (buflen);
if (!buf)
err = gpg_error (GPG_ERR_ENOMEM);
}
for (n=0; !err && n < d->val.length; n++)
{
if ( (c=read_byte (d->reader)) == -1)
err = eof_or_error (d, 1);
buf[n] = c;
}
if (err)
break;
switch (node->type)
{
default:
_ksba_asn_set_value (node, VALTYPE_MEM, buf, n);
break;
}
}
else
{
err = decoder_skip (d);
}
if (err)
break;
}
if (gpg_err_code (err) == GPG_ERR_EOF)
err = 0;
if (r_root && !err)
{
if (!d->image.buf)
{ /* Not even the first node available - return eof */
/* Fixme: release d->root */
d->root = NULL;
err = gpg_error (GPG_ERR_EOF);
}
fixup_type_any (d->root);
*r_root = d->root;
d->root = NULL;
*r_image = d->image.buf;
d->image.buf = NULL;
*r_imagelen = d->image.used;
if (d->debug)
{
- fputs ("Value Tree:\n", stdout);
- _ksba_asn_node_dump_all (*r_root, stdout);
+ fputs ("Value Tree:\n", stderr);
+ _ksba_asn_node_dump_all (*r_root, stderr);
}
}
decoder_deinit (d);
xfree (buf);
return err;
}
diff --git a/src/cert.c b/src/cert.c
index 691a2e5..5d67585 100644
--- a/src/cert.c
+++ b/src/cert.c
@@ -1,2110 +1,2110 @@
/* cert.c - main function for the certificate handling
* Copyright (C) 2001, 2002, 2003, 2004, 2005 g10 Code GmbH
*
* This file is part of KSBA.
*
* KSBA 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 2 of the License, or
* (at your option) any later version.
*
* KSBA 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, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
*/
#include <config.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <errno.h>
#include "util.h"
#include "ber-decoder.h"
#include "ber-help.h"
#include "convert.h"
#include "keyinfo.h"
#include "cert.h"
static const char oidstr_subjectKeyIdentifier[] = "2.5.29.14";
static const char oidstr_keyUsage[] = "2.5.29.15";
static const char oidstr_subjectAltName[] = "2.5.29.17";
static const char oidstr_issuerAltName[] = "2.5.29.18";
static const char oidstr_basicConstraints[] = "2.5.29.19";
static const char oidstr_crlDistributionPoints[] = "2.5.29.31";
static const char oidstr_certificatePolicies[] = "2.5.29.32";
static const char oidstr_authorityKeyIdentifier[] = "2.5.29.35";
static const char oidstr_extKeyUsage[] = "2.5.29.37";
static const char oidstr_authorityInfoAccess[] = "1.3.6.1.5.5.7.1.1";
static const char oidstr_subjectInfoAccess[] = "1.3.6.1.5.5.7.1.11";
/**
* ksba_cert_new:
*
* Create a new and empty certificate object
*
* Return value: 0 on success or error code. For a successful
* operation, ACERT is set to the new certifixate obbject, otherwise
* it is set to NULL.
**/
gpg_error_t
ksba_cert_new (ksba_cert_t *acert)
{
*acert = xtrycalloc (1, sizeof **acert);
if (!*acert)
return gpg_error_from_errno (errno);
(*acert)->ref_count++;
return 0;
}
void
ksba_cert_ref (ksba_cert_t cert)
{
if (!cert)
fprintf (stderr, "BUG: ksba_cert_ref for NULL\n");
else
++cert->ref_count;
}
/**
* ksba_cert_release:
* @cert: A certificate object
*
* Release a certificate object.
**/
void
ksba_cert_release (ksba_cert_t cert)
{
int i;
if (!cert)
return;
if (cert->ref_count < 1)
{
fprintf (stderr, "BUG: trying to release an already released cert\n");
return;
}
if (--cert->ref_count)
return;
if (cert->udata)
{
struct cert_user_data *ud = cert->udata;
cert->udata = NULL;
do
{
struct cert_user_data *ud2 = ud->next;
if (ud->data && ud->data != ud->databuf)
xfree (ud->data);
xfree (ud);
ud = ud2;
}
while (ud);
}
xfree (cert->cache.digest_algo);
if (cert->cache.extns_valid)
{
for (i=0; i < cert->cache.n_extns; i++)
xfree (cert->cache.extns[i].oid);
xfree (cert->cache.extns);
}
/* FIXME: release cert->root, ->asn_tree */
xfree (cert);
}
/* Store arbitrary data along with a certificate. The DATA of length
DATALEN will be stored under the string KEY. If some data is
already stored under this key it will be replaced by the new data.
Using NULL for DATA will effectivly delete the data.
On error (i.e. out or memory) an already existing data object
stored under KEY may get deleted.
- This function is not thread safe because we don't have employ any
+ This function is not thread safe because we don't employ any
locking. */
gpg_error_t
ksba_cert_set_user_data (ksba_cert_t cert,
const char *key, const void *data, size_t datalen)
{
struct cert_user_data *ud;
if (!cert || !key || !*key)
return gpg_error (GPG_ERR_INV_VALUE);
for (ud=cert->udata; ud; ud = ud->next)
if (!strcmp (ud->key, key))
break;
if (ud) /* Update the data stored under this key or reuse this item. */
{
if (ud->data && ud->data != ud->databuf)
xfree (ud->data);
ud->data = NULL;
if (data && datalen <= sizeof ud->databuf)
{
memcpy (ud->databuf, data, datalen);
ud->data = ud->databuf;
ud->datalen = datalen;
}
else if (data)
{
ud->data = xtrymalloc (datalen);
if (!ud->data)
return gpg_error_from_errno (errno);
memcpy (ud->data, data, datalen);
ud->datalen = datalen;
}
}
else if (data) /* Insert as a new item. */
{
ud = xtrycalloc (1, sizeof *ud + strlen (key));
if (!ud)
return gpg_error_from_errno (errno);
strcpy (ud->key, key);
if (datalen <= sizeof ud->databuf)
{
memcpy (ud->databuf, data, datalen);
ud->data = ud->databuf;
ud->datalen = datalen;
}
else
{
ud->data = xtrymalloc (datalen);
if (!ud->data)
{
xfree (ud);
return gpg_error_from_errno (errno);
}
memcpy (ud->data, data, datalen);
ud->datalen = datalen;
}
ud->next = cert->udata;
cert->udata = ud;
}
return 0;
}
/* Return user data for certificate CERT stored under the string
KEY. The caller needs to provide a suitable large BUFFER and pass
the usable length of the buffer in BUFFERLEN. If DATALEN is not
NULL, the length of the data stored at BUFFER will be stored there.
If BUFFER is NULL, BUFFERLEN will be ignored and the required
length of the buffer will be returned at DATALEN.
On success 0 is returned. If no data is stored under KEY
GPG_ERR_NOT_FOUND is returned. If the provided buffer is too
short, GPG_ERR_BUFFER_TOO_SHORT will be returned (note, that this
is not the case if BUFFER is NULL).
*/
gpg_error_t
ksba_cert_get_user_data (ksba_cert_t cert, const char *key,
void *buffer, size_t bufferlen, size_t *datalen)
{
struct cert_user_data *ud;
if (!cert || !key || !*key)
return gpg_error (GPG_ERR_INV_VALUE);
for (ud=cert->udata; ud; ud = ud->next)
if (!strcmp (ud->key, key))
break;
if (!ud || !ud->data)
return gpg_error (GPG_ERR_NOT_FOUND);
if (datalen)
*datalen = ud->datalen;
if (buffer)
{
if (ud->datalen > bufferlen)
return gpg_error (GPG_ERR_BUFFER_TOO_SHORT);
memcpy (buffer, ud->data, ud->datalen);
}
return 0;
}
/**
* ksba_cert_read_der:
* @cert: An unitialized certificate object
* @reader: A KSBA Reader object
*
* Read the next certificate from the reader and store it in the
* certificate object for future access. The certificate is parsed
* and rejected if it has any syntactical or semantical error
* (i.e. does not match the ASN.1 description).
*
* Return value: 0 on success or an error value
**/
gpg_error_t
ksba_cert_read_der (ksba_cert_t cert, ksba_reader_t reader)
{
gpg_error_t err = 0;
BerDecoder decoder = NULL;
if (!cert || !reader)
return gpg_error (GPG_ERR_INV_VALUE);
if (cert->initialized)
return gpg_error (GPG_ERR_CONFLICT); /* Fixme: should remove the old one */
/* fixme: clear old cert->root */
err = ksba_asn_create_tree ("tmttv2", &cert->asn_tree);
if (err)
goto leave;
decoder = _ksba_ber_decoder_new ();
if (!decoder)
{
err = gpg_error (GPG_ERR_ENOMEM);
goto leave;
}
err = _ksba_ber_decoder_set_reader (decoder, reader);
if (err)
goto leave;
err = _ksba_ber_decoder_set_module (decoder, cert->asn_tree);
if (err)
goto leave;
err = _ksba_ber_decoder_decode (decoder, "TMTTv2.Certificate",
&cert->root, &cert->image, &cert->imagelen);
if (!err)
cert->initialized = 1;
leave:
_ksba_ber_decoder_release (decoder);
return err;
}
gpg_error_t
ksba_cert_init_from_mem (ksba_cert_t cert, const void *buffer, size_t length)
{
gpg_error_t err;
ksba_reader_t reader;
err = ksba_reader_new (&reader);
if (err)
return err;
err = ksba_reader_set_mem (reader, buffer, length);
if (err)
{
ksba_reader_release (reader);
return err;
}
err = ksba_cert_read_der (cert, reader);
ksba_reader_release (reader);
return err;
}
const unsigned char *
ksba_cert_get_image (ksba_cert_t cert, size_t *r_length )
{
AsnNode n;
if (!cert)
return NULL;
if (!cert->initialized)
return NULL;
n = _ksba_asn_find_node (cert->root, "Certificate");
if (!n)
return NULL;
if (n->off == -1)
{
/* fputs ("ksba_cert_get_image problem at node:\n", stderr); */
/* _ksba_asn_node_dump_all (n, stderr); */
return NULL;
}
/* Due to minor problems in our parser we might hit the assertion
below. Thus we better return a error, proper. */
if ( !(n->nhdr + n->len + n->off <= cert->imagelen) )
{
fprintf (stderr,"\nOops, ksba_cert_get_image failed: "
"imagelen=%d hdr=%d len=%d off=%d\n",
cert->imagelen, n->nhdr, n->len, n->off);
return NULL;
}
/*assert (n->nhdr + n->len + n->off <= cert->imagelen);*/
if (r_length)
*r_length = n->nhdr + n->len;
return cert->image + n->off;
}
/* Check whether certificates A and B are identical and return o in
this case. */
int
_ksba_cert_cmp (ksba_cert_t a, ksba_cert_t b)
{
const unsigned char *img_a, *img_b;
size_t len_a, len_b;
img_a = ksba_cert_get_image (a, &len_a);
if (!img_a)
return 1;
img_b = ksba_cert_get_image (b, &len_b);
if (!img_b)
return 1;
return !(len_a == len_b && !memcmp (img_a, img_b, len_a));
}
gpg_error_t
ksba_cert_hash (ksba_cert_t cert, int what,
void (*hasher)(void *, const void *, size_t length),
void *hasher_arg)
{
AsnNode n;
if (!cert /*|| !hasher*/)
return gpg_error (GPG_ERR_INV_VALUE);
if (!cert->initialized)
return gpg_error (GPG_ERR_NO_DATA);
n = _ksba_asn_find_node (cert->root,
what == 1? "Certificate.tbsCertificate"
: "Certificate");
if (!n)
return gpg_error (GPG_ERR_NO_VALUE); /* oops - should be there */
if (n->off == -1)
{
/* fputs ("ksba_cert_hash problem at node:\n", stderr); */
/* _ksba_asn_node_dump_all (n, stderr); */
return gpg_error (GPG_ERR_NO_VALUE);
}
hasher (hasher_arg, cert->image + n->off, n->nhdr + n->len);
return 0;
}
/**
* ksba_cert_get_digest_algo:
* @cert: Initialized certificate object
*
* Figure out the the digest algorithm used for the signature and
* return its OID
*
* This function is intended as a helper for the ksba_cert_hash().
*
* Return value: NULL for error otherwise a constant string with the OID.
* This string is valid as long the certificate object is valid.
**/
const char *
ksba_cert_get_digest_algo (ksba_cert_t cert)
{
AsnNode n;
char *algo;
if (!cert)
{
cert->last_error = gpg_error (GPG_ERR_INV_VALUE);
return NULL;
}
if (!cert->initialized)
{
cert->last_error = gpg_error (GPG_ERR_NO_DATA);
return NULL;
}
if (cert->cache.digest_algo)
return cert->cache.digest_algo;
n = _ksba_asn_find_node (cert->root,
"Certificate.signatureAlgorithm.algorithm");
algo = _ksba_oid_node_to_str (cert->image, n);
if (!algo)
cert->last_error = gpg_error (GPG_ERR_UNKNOWN_ALGORITHM);
else
cert->cache.digest_algo = algo;
return algo;
}
/**
* ksba_cert_get_serial:
* @cert: certificate object
*
* This function returnes the serial number of the certificate. The
* serial number is an integer returned as an cancnical encoded
* S-expression with just one element.
*
* Return value: An allocated S-Exp or NULL for no value.
**/
ksba_sexp_t
ksba_cert_get_serial (ksba_cert_t cert)
{
AsnNode n;
char *p;
char numbuf[22];
int numbuflen;
if (!cert || !cert->initialized)
return NULL;
n = _ksba_asn_find_node (cert->root,
"Certificate.tbsCertificate.serialNumber");
if (!n)
return NULL; /* oops - should be there */
if (n->off == -1)
{
/* fputs ("get_serial problem at node:\n", stderr); */
/* _ksba_asn_node_dump_all (n, stderr); */
return NULL;
}
sprintf (numbuf,"(%u:", (unsigned int)n->len);
numbuflen = strlen (numbuf);
p = xtrymalloc (numbuflen + n->len + 2);
if (!p)
return NULL;
strcpy (p, numbuf);
memcpy (p+numbuflen, cert->image + n->off + n->nhdr, n->len);
p[numbuflen + n->len] = ')';
p[numbuflen + n->len + 1] = 0;
return p;
}
/* Return a pointer to the DER encoding of the serial number in CERT in
PTR and the length of that field in LENGTH. */
gpg_error_t
_ksba_cert_get_serial_ptr (ksba_cert_t cert,
unsigned char const **ptr, size_t *length)
{
asn_node_t n;
if (!cert || !cert->initialized || !ptr || !length)
return gpg_error (GPG_ERR_INV_VALUE);
n = _ksba_asn_find_node (cert->root,
"Certificate.tbsCertificate.serialNumber");
if (!n || n->off == -1)
return gpg_error (GPG_ERR_NO_VALUE);
*ptr = cert->image + n->off + n->nhdr;
*length = n->len;
return 0;
}
/* Return a pointer to the DER encoding of the subject's DN in CERT in
PTR and the length of that object in LENGTH. */
gpg_error_t
_ksba_cert_get_subject_dn_ptr (ksba_cert_t cert,
unsigned char const **ptr, size_t *length)
{
asn_node_t n;
if (!cert || !cert->initialized || !ptr || !length)
return gpg_error (GPG_ERR_INV_VALUE);
n = _ksba_asn_find_node (cert->root, "Certificate.tbsCertificate.subject");
if (!n || !n->down)
return gpg_error (GPG_ERR_NO_VALUE); /* oops - should be there */
n = n->down; /* dereference the choice node */
if (n->off == -1)
return gpg_error (GPG_ERR_NO_VALUE);
*ptr = cert->image + n->off;
*length = n->nhdr + n->len;
return 0;
}
/* Worker function for get_isssuer and get_subject. */
static gpg_error_t
get_name (ksba_cert_t cert, int idx, int use_subject, char **result)
{
gpg_error_t err;
char *p;
int i;
const char *oid;
struct tag_info ti;
const unsigned char *der;
size_t off, derlen, seqlen;
if (!cert || !cert->initialized || !result)
return gpg_error (GPG_ERR_INV_VALUE);
if (idx < 0)
return gpg_error (GPG_ERR_INV_INDEX);
*result = NULL;
if (!idx)
{ /* Get the required DN */
AsnNode n;
n = _ksba_asn_find_node (cert->root,
(use_subject?
"Certificate.tbsCertificate.subject":
"Certificate.tbsCertificate.issuer") );
if (!n || !n->down)
return gpg_error (GPG_ERR_NO_VALUE); /* oops - should be there */
n = n->down; /* dereference the choice node */
if (n->off == -1)
return gpg_error (GPG_ERR_NO_VALUE);
err = _ksba_dn_to_str (cert->image, n, &p);
if (err)
return err;
*result = p;
return 0;
}
/* get {issuer,subject}AltName */
for (i=0; !(err=ksba_cert_get_extension (cert, i, &oid, NULL,
&off, &derlen)); i++)
{
if (!strcmp (oid, (use_subject?
oidstr_subjectAltName:oidstr_issuerAltName)))
break;
}
if (err)
return err; /* no alt name or error*/
der = cert->image + off;
/* FIXME: We should use _ksba_name_new_from_der and ksba_name_enum here */
err = _ksba_ber_parse_tl (&der, &derlen, &ti);
if (err)
return err;
if ( !(ti.class == CLASS_UNIVERSAL && ti.tag == TYPE_SEQUENCE
&& ti.is_constructed) )
return gpg_error (GPG_ERR_INV_CERT_OBJ);
if (ti.ndef)
return gpg_error (GPG_ERR_NOT_DER_ENCODED);
seqlen = ti.length;
if (seqlen > derlen)
return gpg_error (GPG_ERR_BAD_BER);
if (!seqlen)
return gpg_error (GPG_ERR_INV_CERT_OBJ); /* empty sequence is not allowed */
while (seqlen)
{
err = _ksba_ber_parse_tl (&der, &derlen, &ti);
if (err)
return err;
if (ti.class != CLASS_CONTEXT)
return gpg_error (GPG_ERR_INV_CERT_OBJ); /* we expected a tag */
if (ti.ndef)
return gpg_error (GPG_ERR_NOT_DER_ENCODED);
if (seqlen < ti.nhdr)
return gpg_error (GPG_ERR_BAD_BER);
seqlen -= ti.nhdr;
if (seqlen < ti.length)
return gpg_error (GPG_ERR_BAD_BER);
seqlen -= ti.length;
if (derlen < ti.length)
return gpg_error (GPG_ERR_BAD_BER);
if (--idx)
; /* not yet at the desired index */
else if (ti.tag == 1)
{ /* rfc822Name - this is an implicit IA5_STRING */
p = xtrymalloc (ti.length+3);
if (!p)
return gpg_error (GPG_ERR_ENOMEM);
*p = '<';
memcpy (p+1, der, ti.length);
p[ti.length+1] = '>';
p[ti.length+2] = 0;
*result = p;
return 0;
}
else if (ti.tag == 2 || ti.tag == 6)
{ /* dNSName or URI - this are implicit IA5_STRINGs */
char numbuf[30];
snprintf (numbuf, DIM(numbuf), "%lu:", ti.length);
p = xtrymalloc (11 + strlen (numbuf) + ti.length + 3);
if (!p)
return gpg_error (GPG_ERR_ENOMEM);
*result = p;
p = stpcpy (p, ti.tag == 2? "(8:dns-name" : "(3:uri");
p = stpcpy (p, numbuf);
memcpy (p, der, ti.length);
p += ti.length;
*p++ = ')';
*p = 0;
return 0;
}
/* advance pointer */
der += ti.length;
derlen -= ti.length;
}
return gpg_error (GPG_ERR_EOF);
}
/**
* ksba_cert_get_issuer:
* @cert: certificate object
*
* With @idx == 0 this function returns the Distinguished Name (DN) of
* the certificate issuer which in most cases is a CA. The format of
* the returned string is in accordance with RFC-2253. NULL is
* returned if the DN is not available which is an error and should
* have been catched by the certificate reading function.
*
* With @idx > 0 the function may be used to enumerate alternate
* issuer names. The function returns NULL if there are no more
* alternate names. The function does only return alternate names
* which are recognized by libksba and ignores others. The format of
* the returned name is either a RFC-2253 formated one which can be
* detected by checking whether the first character is letter or
* digit. rfc-2822 conform email addresses are returned enclosed in
* angle brackets, the opening angle bracket should be used to
* indicate this. Other formats are returned as an S-Expression in
* canonical format, so a opening parenthesis may be used to detect
* this encoding, the name may include binary null characters, so
* strlen may return a length shorther than actually used, the real
* length is implictly given by the structure of the S-Exp, an extra
* null is appended for safety reasons.
*
* The caller must free the returned string using ksba_free() or the
* function he has registered as a replacement.
*
* Return value: An allocated string or NULL for error.
**/
char *
ksba_cert_get_issuer (ksba_cert_t cert, int idx)
{
gpg_error_t err;
char *name;
err = get_name (cert, idx, 0, &name);
if (err)
{
cert->last_error = err;
return NULL;
}
return name;
}
/* See ..get_issuer */
char *
ksba_cert_get_subject (ksba_cert_t cert, int idx)
{
gpg_error_t err;
char *name;
err = get_name (cert, idx, 1, &name);
if (err)
{
cert->last_error = err;
return NULL;
}
return name;
}
/**
* ksba_cert_get_valididy:
* @cert: certificate object
* @what: 0 for notBefore, 1 for notAfter
* @timebuf: Returns the time.
*
* Return the validity object from the certificate. If no value is
* available 0 is returned because we can safely assume that this is
* not a valid date.
*
* Return value: The time value an 0 or an error code.
**/
gpg_error_t
ksba_cert_get_validity (ksba_cert_t cert, int what, ksba_isotime_t timebuf)
{
AsnNode n, n2;
if (!cert || what < 0 || what > 1)
return gpg_error (GPG_ERR_INV_VALUE);
*timebuf = 0;
if (!cert->initialized)
return gpg_error (GPG_ERR_NO_DATA);
n = _ksba_asn_find_node (cert->root,
what == 0? "Certificate.tbsCertificate.validity.notBefore"
: "Certificate.tbsCertificate.validity.notAfter");
if (!n)
return 0; /* no value available */
/* Fixme: We should remove the choice node and don't use this ugly hack */
for (n2=n->down; n2; n2 = n2->right)
{
if ((n2->type == TYPE_UTC_TIME || n2->type == TYPE_GENERALIZED_TIME)
&& n2->off != -1)
break;
}
n = n2;
if (!n)
return 0; /* no value available */
return_val_if_fail (n->off != -1, gpg_error (GPG_ERR_BUG));
return _ksba_asntime_to_iso (cert->image + n->off + n->nhdr, n->len,
n->type == TYPE_UTC_TIME, timebuf);
}
ksba_sexp_t
ksba_cert_get_public_key (ksba_cert_t cert)
{
AsnNode n;
gpg_error_t err;
ksba_sexp_t string;
if (!cert)
return NULL;
if (!cert->initialized)
return NULL;
n = _ksba_asn_find_node (cert->root,
"Certificate"
".tbsCertificate.subjectPublicKeyInfo");
if (!n)
{
cert->last_error = gpg_error (GPG_ERR_NO_VALUE);
return NULL;
}
err = _ksba_keyinfo_to_sexp (cert->image + n->off, n->nhdr + n->len,
&string);
if (err)
{
cert->last_error = err;
return NULL;
}
return string;
}
/* Return a pointer to the DER encoding of the actual public key
(i.e. the bit string) in PTR and the length of that object in
LENGTH. */
gpg_error_t
_ksba_cert_get_public_key_ptr (ksba_cert_t cert,
unsigned char const **ptr, size_t *length)
{
asn_node_t n;
if (!cert || !cert->initialized || !ptr || !length)
return gpg_error (GPG_ERR_INV_VALUE);
n = _ksba_asn_find_node (cert->root,
"Certificate.tbsCertificate.subjectPublicKeyInfo");
if (!n || !n->down || !n->down->right)
return gpg_error (GPG_ERR_NO_VALUE); /* oops - should be there */
n = n->down->right;
if (n->off == -1)
return gpg_error (GPG_ERR_NO_VALUE);
*ptr = cert->image + n->off + n->nhdr;
*length = n->len;
/* Somehow we end up at the preceding NULL value, and not at a
sequence, we hack it way here. */
if (*length && !**ptr)
{
(*length)--;
(*ptr)++;
}
return 0;
}
ksba_sexp_t
ksba_cert_get_sig_val (ksba_cert_t cert)
{
AsnNode n, n2;
gpg_error_t err;
ksba_sexp_t string;
if (!cert)
return NULL;
if (!cert->initialized)
return NULL;
n = _ksba_asn_find_node (cert->root,
"Certificate.signatureAlgorithm");
if (!n)
{
cert->last_error = gpg_error (GPG_ERR_NO_VALUE);
return NULL;
}
if (n->off == -1)
{
/* fputs ("ksba_cert_get_sig_val problem at node:\n", stderr); */
/* _ksba_asn_node_dump_all (n, stderr); */
cert->last_error = gpg_error (GPG_ERR_NO_VALUE);
return NULL;
}
n2 = n->right;
err = _ksba_sigval_to_sexp (cert->image + n->off,
n->nhdr + n->len
+ ((!n2||n2->off == -1)? 0:(n2->nhdr+n2->len)),
&string);
if (err)
{
cert->last_error = err;
return NULL;
}
return string;
}
/* Read all extensions into the cache */
static gpg_error_t
read_extensions (ksba_cert_t cert)
{
AsnNode start, n;
int count;
assert (!cert->cache.extns_valid);
assert (!cert->cache.extns);
start = _ksba_asn_find_node (cert->root,
"Certificate.tbsCertificate.extensions..");
for (count=0, n=start; n; n = n->right)
count++;
if (!count)
{
cert->cache.n_extns = 0;
cert->cache.extns_valid = 1;
return 0; /* no extensions at all */
}
cert->cache.extns = xtrycalloc (count, sizeof *cert->cache.extns);
if (!cert->cache.extns)
return gpg_error (GPG_ERR_ENOMEM);
cert->cache.n_extns = count;
{
for (count=0; start; start = start->right, count++)
{
n = start->down;
if (!n || n->type != TYPE_OBJECT_ID)
goto no_value;
cert->cache.extns[count].oid = _ksba_oid_node_to_str (cert->image, n);
if (!cert->cache.extns[count].oid)
goto no_value;
n = n->right;
if (n && n->type == TYPE_BOOLEAN)
{
if (n->off != -1 && n->len && cert->image[n->off + n->nhdr])
cert->cache.extns[count].crit = 1;
n = n->right;
}
if (!n || n->type != TYPE_OCTET_STRING || n->off == -1)
goto no_value;
cert->cache.extns[count].off = n->off + n->nhdr;
cert->cache.extns[count].len = n->len;
}
assert (count == cert->cache.n_extns);
cert->cache.extns_valid = 1;
return 0;
no_value:
for (count=0; count < cert->cache.n_extns; count++)
xfree (cert->cache.extns[count].oid);
xfree (cert->cache.extns);
cert->cache.extns = NULL;
return gpg_error (GPG_ERR_NO_VALUE);
}
}
/* Return information about the IDX nth extension */
gpg_error_t
ksba_cert_get_extension (ksba_cert_t cert, int idx,
char const **r_oid, int *r_crit,
size_t *r_deroff, size_t *r_derlen)
{
gpg_error_t err;
if (!cert)
return gpg_error (GPG_ERR_INV_VALUE);
if (!cert->initialized)
return gpg_error (GPG_ERR_NO_DATA);
if (!cert->cache.extns_valid)
{
err = read_extensions (cert);
if (err)
return err;
assert (cert->cache.extns_valid);
}
if (idx == cert->cache.n_extns)
return gpg_error (GPG_ERR_EOF); /* No more extensions. */
if (idx < 0 || idx >= cert->cache.n_extns)
return gpg_error (GPG_ERR_INV_INDEX);
if (r_oid)
*r_oid = cert->cache.extns[idx].oid;
if (r_crit)
*r_crit = cert->cache.extns[idx].crit;
if (r_deroff)
*r_deroff = cert->cache.extns[idx].off;
if (r_derlen)
*r_derlen = cert->cache.extns[idx].len;
return 0;
}
/* Return information on the basicConstraint (2.5.19.19) of CERT.
R_CA receives true if this is a CA and only in that case R_PATHLEN
is set to the maximim certification path length or -1 if there is
nosuch limitation */
gpg_error_t
ksba_cert_is_ca (ksba_cert_t cert, int *r_ca, int *r_pathlen)
{
gpg_error_t err;
const char *oid;
int idx, crit;
size_t off, derlen, seqlen;
const unsigned char *der;
struct tag_info ti;
unsigned long value;
/* set default values */
if (r_ca)
*r_ca = 0;
if (r_pathlen)
*r_pathlen = -1;
for (idx=0; !(err=ksba_cert_get_extension (cert, idx, &oid, &crit,
&off, &derlen)); idx++)
{
if (!strcmp (oid, oidstr_basicConstraints))
break;
}
if (gpg_err_code (err) == GPG_ERR_EOF)
return 0; /* no such constraint */
if (err)
return err;
/* check that there is only one */
for (idx++; !(err=ksba_cert_get_extension (cert, idx, &oid, NULL,
NULL, NULL)); idx++)
{
if (!strcmp (oid, oidstr_basicConstraints))
return gpg_error (GPG_ERR_DUP_VALUE);
}
der = cert->image + off;
err = _ksba_ber_parse_tl (&der, &derlen, &ti);
if (err)
return err;
if ( !(ti.class == CLASS_UNIVERSAL && ti.tag == TYPE_SEQUENCE
&& ti.is_constructed) )
return gpg_error (GPG_ERR_INV_CERT_OBJ);
if (ti.ndef)
return gpg_error (GPG_ERR_NOT_DER_ENCODED);
seqlen = ti.length;
if (seqlen > derlen)
return gpg_error (GPG_ERR_BAD_BER);
if (!seqlen)
return 0; /* an empty sequence is allowed because both elements
are optional */
err = _ksba_ber_parse_tl (&der, &derlen, &ti);
if (err)
return err;
if (seqlen < ti.nhdr)
return gpg_error (GPG_ERR_BAD_BER);
seqlen -= ti.nhdr;
if (seqlen < ti.length)
return gpg_error (GPG_ERR_BAD_BER);
seqlen -= ti.length;
if (ti.class == CLASS_UNIVERSAL && ti.tag == TYPE_BOOLEAN)
{
if (ti.length != 1)
return gpg_error (GPG_ERR_ENCODING_PROBLEM);
if (r_ca)
*r_ca = !!*der;
der++; derlen--;
if (!seqlen)
return 0; /* ready (no pathlength) */
err = _ksba_ber_parse_tl (&der, &derlen, &ti);
if (err)
return err;
if (seqlen < ti.nhdr)
return gpg_error (GPG_ERR_BAD_BER);
seqlen -= ti.nhdr;
if (seqlen < ti.length)
return gpg_error (GPG_ERR_BAD_BER);
seqlen -= ti.length;
}
if (!(ti.class == CLASS_UNIVERSAL && ti.tag == TYPE_INTEGER))
return gpg_error (GPG_ERR_INV_CERT_OBJ);
for (value=0; ti.length; ti.length--)
{
value <<= 8;
value |= (*der++) & 0xff;
derlen--;
}
if (r_pathlen)
*r_pathlen = value;
/* if the extension is marked as critical and any stuff is still
left we better return an error */
if (crit && seqlen)
return gpg_error (GPG_ERR_INV_CERT_OBJ);
return 0;
}
/* Get the key usage flags. The function returns Ksba_No_Data if no
key usage is specified. */
gpg_error_t
ksba_cert_get_key_usage (ksba_cert_t cert, unsigned int *r_flags)
{
gpg_error_t err;
const char *oid;
int idx, crit;
size_t off, derlen;
const unsigned char *der;
struct tag_info ti;
unsigned int bits, mask;
int i, unused, full;
if (!r_flags)
return gpg_error (GPG_ERR_INV_VALUE);
*r_flags = 0;
for (idx=0; !(err=ksba_cert_get_extension (cert, idx, &oid, &crit,
&off, &derlen)); idx++)
{
if (!strcmp (oid, oidstr_keyUsage))
break;
}
if (gpg_err_code (err) == GPG_ERR_EOF)
return gpg_error (GPG_ERR_NO_DATA); /* no key usage */
if (err)
return err;
/* check that there is only one */
for (idx++; !(err=ksba_cert_get_extension (cert, idx, &oid, NULL,
NULL, NULL)); idx++)
{
if (!strcmp (oid, oidstr_keyUsage))
return gpg_error (GPG_ERR_DUP_VALUE);
}
der = cert->image + off;
err = _ksba_ber_parse_tl (&der, &derlen, &ti);
if (err)
return err;
if ( !(ti.class == CLASS_UNIVERSAL && ti.tag == TYPE_BIT_STRING
&& !ti.is_constructed) )
return gpg_error (GPG_ERR_INV_CERT_OBJ);
if (ti.ndef)
return gpg_error (GPG_ERR_NOT_DER_ENCODED);
if (!ti.length || ti.length > derlen)
return gpg_error (GPG_ERR_ENCODING_PROBLEM); /* number of unused bits missing */
unused = *der++; derlen--;
ti.length--;
if ((!ti.length && unused) || unused/8 > ti.length)
return gpg_error (GPG_ERR_ENCODING_PROBLEM);
full = ti.length - (unused+7)/8;
unused %= 8;
mask = 0;
for (i=1; unused; i <<= 1, unused--)
mask |= i;
/* the first octet */
if (!ti.length)
return 0; /* no bits set */
bits = *der++; derlen--; ti.length--;
if (full)
full--;
else {
bits &= ~mask;
mask = 0;
}
if (bits & 0x80)
*r_flags |= KSBA_KEYUSAGE_DIGITAL_SIGNATURE;
if (bits & 0x40)
*r_flags |= KSBA_KEYUSAGE_NON_REPUDIATION;
if (bits & 0x20)
*r_flags |= KSBA_KEYUSAGE_KEY_ENCIPHERMENT;
if (bits & 0x10)
*r_flags |= KSBA_KEYUSAGE_DATA_ENCIPHERMENT;
if (bits & 0x08)
*r_flags |= KSBA_KEYUSAGE_KEY_AGREEMENT;
if (bits & 0x04)
*r_flags |= KSBA_KEYUSAGE_KEY_CERT_SIGN;
if (bits & 0x02)
*r_flags |= KSBA_KEYUSAGE_CRL_SIGN;
if (bits & 0x01)
*r_flags |= KSBA_KEYUSAGE_ENCIPHER_ONLY;
/* the second octet */
if (!ti.length)
return 0; /* no bits set */
bits = *der++; derlen--; ti.length--;
if (full)
full--;
else {
bits &= mask;
mask = ~0;
}
if (bits & 0x80)
*r_flags |= KSBA_KEYUSAGE_DECIPHER_ONLY;
return 0;
}
/* Note, that this helper is also used for ext_key_usage. */
static gpg_error_t
append_cert_policy (char **policies, const char *oid, int crit)
{
char *p;
if (!*policies)
{
*policies = xtrymalloc (strlen (oid) + 4);
if (!*policies)
return gpg_error (GPG_ERR_ENOMEM);
p = *policies;
}
else
{
char *tmp = xtryrealloc (*policies,
strlen(*policies) + 1 + strlen (oid) + 4);
if (!tmp)
return gpg_error (GPG_ERR_ENOMEM);
*policies = tmp;
p = stpcpy (tmp+strlen (tmp), "\n");;
}
strcpy (stpcpy (p, oid), crit? ":C:": ":N:");
return 0;
}
/* Return a string with the certificatePolicies delimited by
linefeeds. The return values may be extended to carry more
information er line, so the caller should only use the first
white-space delimited token per line. The function returns
GPG_ERR_NO_DATA when this extension is not used. Caller must free
the returned value. */
gpg_error_t
ksba_cert_get_cert_policies (ksba_cert_t cert, char **r_policies)
{
gpg_error_t err;
const char *oid;
int idx, crit;
size_t off, derlen, seqlen;
const unsigned char *der;
struct tag_info ti;
if (!cert || !r_policies)
return gpg_error (GPG_ERR_INV_VALUE);
*r_policies = NULL;
for (idx=0; !(err=ksba_cert_get_extension (cert, idx, &oid, &crit,
&off, &derlen)); idx++)
{
if (!strcmp (oid, oidstr_certificatePolicies))
{
char *suboid;
der = cert->image + off;
err = _ksba_ber_parse_tl (&der, &derlen, &ti);
if (err)
goto leave;
if ( !(ti.class == CLASS_UNIVERSAL && ti.tag == TYPE_SEQUENCE
&& ti.is_constructed) )
{
err = gpg_error (GPG_ERR_INV_CERT_OBJ);
goto leave;
}
if (ti.ndef)
{
err = gpg_error (GPG_ERR_NOT_DER_ENCODED);
goto leave;
}
seqlen = ti.length;
if (seqlen > derlen)
{
err = gpg_error (GPG_ERR_BAD_BER);
goto leave;
}
while (seqlen)
{
size_t seqseqlen;
err = _ksba_ber_parse_tl (&der, &derlen, &ti);
if (err)
goto leave;
if ( !(ti.class == CLASS_UNIVERSAL && ti.tag == TYPE_SEQUENCE
&& ti.is_constructed) )
{
err = gpg_error (GPG_ERR_INV_CERT_OBJ);
goto leave;
}
if (ti.ndef)
{
err = gpg_error (GPG_ERR_NOT_DER_ENCODED);
goto leave;
}
if (!ti.length)
{
err = gpg_error (GPG_ERR_INV_CERT_OBJ); /* no empty inner SEQ */
goto leave;
}
if (ti.nhdr+ti.length > seqlen)
{
err = gpg_error (GPG_ERR_BAD_BER);
goto leave;
}
seqlen -= ti.nhdr + ti.length;
seqseqlen = ti.length;
err = _ksba_ber_parse_tl (&der, &derlen, &ti);
if (err)
goto leave;
if ( !(ti.class == CLASS_UNIVERSAL && ti.tag == TYPE_OBJECT_ID))
{
err = gpg_error (GPG_ERR_INV_CERT_OBJ);
goto leave;
}
if (ti.nhdr+ti.length > seqseqlen)
{
err = gpg_error (GPG_ERR_BAD_BER);
goto leave;
}
seqseqlen -= ti.nhdr;
suboid = ksba_oid_to_str (der, ti.length);
if (!suboid)
{
err = gpg_error (GPG_ERR_ENOMEM);
goto leave;
}
der += ti.length;
derlen -= ti.length;
seqseqlen -= ti.length;
err = append_cert_policy (r_policies, suboid, crit);
xfree (suboid);
if (err)
goto leave;
/* skip the rest of the seq which is more or less optional */
der += seqseqlen;
derlen -= seqseqlen;
}
}
}
if (gpg_err_code (err) == GPG_ERR_EOF)
err = 0;
if (!*r_policies)
err = gpg_error (GPG_ERR_NO_DATA);
leave:
if (err)
{
xfree (*r_policies);
*r_policies = NULL;
}
return err;
}
/* Return a string with the extendedKeyUsageOIDs delimited by
linefeeds. The return values may be extended to carry more
information per line, so the caller should only use the first
white-space delimited token per line. The function returns
GPG_ERR_NO_DATA when this extension is not used. Caller must free
the returned value. */
gpg_error_t
ksba_cert_get_ext_key_usages (ksba_cert_t cert, char **result)
{
gpg_error_t err;
const char *oid;
int idx, crit;
size_t off, derlen;
const unsigned char *der;
struct tag_info ti;
if (!cert || !result)
return gpg_error (GPG_ERR_INV_VALUE);
*result = NULL;
for (idx=0; !(err=ksba_cert_get_extension (cert, idx, &oid, &crit,
&off, &derlen)); idx++)
{
if (!strcmp (oid, oidstr_extKeyUsage))
{
char *suboid;
der = cert->image + off;
err = _ksba_ber_parse_tl (&der, &derlen, &ti);
if (err)
goto leave;
if ( !(ti.class == CLASS_UNIVERSAL && ti.tag == TYPE_SEQUENCE
&& ti.is_constructed) )
{
err = gpg_error (GPG_ERR_INV_CERT_OBJ);
goto leave;
}
if (ti.ndef)
{
err = gpg_error (GPG_ERR_NOT_DER_ENCODED);
goto leave;
}
if (ti.length > derlen)
{
err = gpg_error (GPG_ERR_BAD_BER);
goto leave;
}
while (derlen)
{
err = _ksba_ber_parse_tl (&der, &derlen, &ti);
if (err)
goto leave;
if ( !(ti.class == CLASS_UNIVERSAL && ti.tag == TYPE_OBJECT_ID))
{
err = gpg_error (GPG_ERR_INV_CERT_OBJ);
goto leave;
}
suboid = ksba_oid_to_str (der, ti.length);
if (!suboid)
{
err = gpg_error (GPG_ERR_ENOMEM);
goto leave;
}
der += ti.length;
derlen -= ti.length;
err = append_cert_policy (result, suboid, crit);
xfree (suboid);
if (err)
goto leave;
}
}
}
if (gpg_err_code (err) == GPG_ERR_EOF)
err = 0;
if (!*result)
err = gpg_error (GPG_ERR_NO_DATA);
leave:
if (err)
{
xfree (*result);
*result = NULL;
}
return err;
}
/* Helper function for ksba_cert_get_crl_dist_point */
static gpg_error_t
parse_distribution_point (const unsigned char *der, size_t derlen,
ksba_name_t *distpoint, ksba_name_t *issuer,
unsigned int *reason)
{
gpg_error_t err;
struct tag_info ti;
err = _ksba_ber_parse_tl (&der, &derlen, &ti);
if (err)
return err;
if (ti.class != CLASS_CONTEXT)
return gpg_error (GPG_ERR_INV_CERT_OBJ); /* we expected a tag */
if (ti.ndef)
return gpg_error (GPG_ERR_NOT_DER_ENCODED);
if (derlen < ti.length)
return gpg_error (GPG_ERR_BAD_BER);
if (ti.tag == 0 && derlen)
{ /* distributionPointName */
err = _ksba_ber_parse_tl (&der, &derlen, &ti);
if (err)
return err;
if (ti.class != CLASS_CONTEXT)
return gpg_error (GPG_ERR_INV_CERT_OBJ); /* we expected a tag */
if (ti.ndef)
return gpg_error (GPG_ERR_NOT_DER_ENCODED);
if (derlen < ti.nhdr)
return gpg_error (GPG_ERR_BAD_BER);
if (derlen < ti.length)
return gpg_error (GPG_ERR_BAD_BER);
if (ti.tag == 0)
{
if (distpoint)
{
err = _ksba_name_new_from_der (distpoint, der, ti.length);
if (err)
return err;
}
}
else
{
/* We don't support nameRelativeToCRLIssuer yet*/
}
der += ti.length;
derlen -= ti.length;
if (!derlen)
return 0;
/* read the next optional element */
err = _ksba_ber_parse_tl (&der, &derlen, &ti);
if (err)
return err;
if (ti.class != CLASS_CONTEXT)
return gpg_error (GPG_ERR_INV_CERT_OBJ); /* we expected a tag */
if (ti.ndef)
return gpg_error (GPG_ERR_NOT_DER_ENCODED);
if (derlen < ti.length)
return gpg_error (GPG_ERR_BAD_BER);
}
if (ti.tag == 1 && derlen)
{ /* reasonFlags */
unsigned int bits, mask;
int i, unused, full;
unused = *der++; derlen--;
ti.length--;
if ((!ti.length && unused) || unused/8 > ti.length)
return gpg_error (GPG_ERR_ENCODING_PROBLEM);
full = ti.length - (unused+7)/8;
unused %= 8;
mask = 0;
for (i=1; unused; i <<= 1, unused--)
mask |= i;
/* we are only required to look at the first octect */
if (ti.length && reason)
{
bits = *der;
if (full)
full--;
else {
bits &= ~mask;
mask = 0;
}
if (bits & 0x80)
*reason |= KSBA_CRLREASON_UNSPECIFIED;
if (bits & 0x40)
*reason |= KSBA_CRLREASON_KEY_COMPROMISE;
if (bits & 0x20)
*reason |= KSBA_CRLREASON_CA_COMPROMISE;
if (bits & 0x10)
*reason |= KSBA_CRLREASON_AFFILIATION_CHANGED;
if (bits & 0x08)
*reason |= KSBA_CRLREASON_SUPERSEDED;
if (bits & 0x04)
*reason |= KSBA_CRLREASON_CESSATION_OF_OPERATION;
if (bits & 0x02)
*reason |= KSBA_CRLREASON_CERTIFICATE_HOLD;
}
der += ti.length;
derlen -= ti.length;
if (!derlen)
return 0;
/* read the next optional element */
err = _ksba_ber_parse_tl (&der, &derlen, &ti);
if (err)
return err;
if (ti.class != CLASS_CONTEXT)
return gpg_error (GPG_ERR_INV_CERT_OBJ); /* we expected a tag */
if (ti.ndef)
return gpg_error (GPG_ERR_NOT_DER_ENCODED);
if (derlen < ti.length)
return gpg_error (GPG_ERR_BAD_BER);
}
if (ti.tag == 2 && derlen)
{ /* crlIssuer */
if (issuer)
{
err = _ksba_name_new_from_der (issuer, der, ti.length);
if (err)
return err;
}
der += ti.length;
derlen -= ti.length;
}
if (derlen)
return gpg_error (GPG_ERR_INV_CERT_OBJ);
return 0;
}
/* Return the CRLDistPoints given in the cert extension. IDX should
be iterated started from 0 until the function returns -1.
R_DISTPOINT returns a ksba_name_t object with the distribution point
name(s) the return value may be NULL to indicate that this name is
not available. R_ISSUER returns the CRL issuer; if the returned
value is NULL the caller should assume that the CRL issuer is the
same as the certificate issuer. R_REASON returns the reason for
the CRL. This is a bit encoded value with no bit set if this has
not been specified in the cert.
The caller may pass NULL to any of the pointer arguments if he is
not interested in this value. The return values for R_DISTPOINT
and R_ISSUER must be released by the caller using
ksba_name_release(). */
gpg_error_t
ksba_cert_get_crl_dist_point (ksba_cert_t cert, int idx,
ksba_name_t *r_distpoint,
ksba_name_t *r_issuer,
unsigned int *r_reason)
{
gpg_error_t err;
const char *oid;
size_t off, derlen;
int myidx, crit;
if (r_distpoint)
*r_distpoint = NULL;
if (r_issuer)
*r_issuer = NULL;
if (r_reason)
*r_reason = 0;
for (myidx=0; !(err=ksba_cert_get_extension (cert, myidx, &oid, &crit,
&off, &derlen)); myidx++)
{
if (!strcmp (oid, oidstr_crlDistributionPoints))
{
const unsigned char *der;
struct tag_info ti;
size_t seqlen;
der = cert->image + off;
err = _ksba_ber_parse_tl (&der, &derlen, &ti);
if (err)
return err;
if ( !(ti.class == CLASS_UNIVERSAL && ti.tag == TYPE_SEQUENCE
&& ti.is_constructed) )
return gpg_error (GPG_ERR_INV_CERT_OBJ);
if (ti.ndef)
return gpg_error (GPG_ERR_NOT_DER_ENCODED);
seqlen = ti.length;
if (seqlen > derlen)
return gpg_error (GPG_ERR_BAD_BER);
/* Note: an empty sequence is actually not allowed but we
better don't care */
while (seqlen)
{
err = _ksba_ber_parse_tl (&der, &derlen, &ti);
if (err)
return err;
if ( !(ti.class == CLASS_UNIVERSAL && ti.tag == TYPE_SEQUENCE
&& ti.is_constructed) )
return gpg_error (GPG_ERR_INV_CERT_OBJ);
if (derlen < ti.length)
return gpg_error (GPG_ERR_BAD_BER);
if (seqlen < ti.nhdr)
return gpg_error (GPG_ERR_BAD_BER);
seqlen -= ti.nhdr;
if (seqlen < ti.length)
return gpg_error (GPG_ERR_BAD_BER);
if (idx)
{ /* skip because we are not yet at the desired index */
der += ti.length;
derlen -= ti.length;
seqlen -= ti.length;
idx--;
continue;
}
if (!ti.length)
return 0;
err = parse_distribution_point (der, ti.length,
r_distpoint, r_issuer, r_reason);
if (err && r_distpoint)
{
ksba_name_release (*r_distpoint);
*r_distpoint = NULL;
}
if (err && r_issuer)
{
ksba_name_release (*r_issuer);
*r_issuer = NULL;
}
if (err && r_reason)
*r_reason = 0;
return err;
}
}
}
return err;
}
/* Return the authorityKeyIdentifier in R_NAME and R_SERIAL or/and in
R_KEYID. GPG_ERR_NO_DATA is returned if no authorityKeyIdentifier
or only one using the keyIdentifier method is available and R_KEYID
is NULL. */
gpg_error_t
ksba_cert_get_auth_key_id (ksba_cert_t cert,
ksba_sexp_t *r_keyid,
ksba_name_t *r_name,
ksba_sexp_t *r_serial)
{
gpg_error_t err;
const char *oid;
size_t off, derlen;
const unsigned char *der;
const unsigned char *keyid_der = NULL;
size_t keyid_derlen = 0;
int idx, crit;
struct tag_info ti;
char numbuf[30];
size_t numbuflen;
if (r_keyid)
*r_keyid = NULL;
if (!r_name || !r_serial)
return gpg_error (GPG_ERR_INV_VALUE);
*r_name = NULL;
*r_serial = NULL;
for (idx=0; !(err=ksba_cert_get_extension (cert, idx, &oid, &crit,
&off, &derlen)); idx++)
{
if (!strcmp (oid, oidstr_authorityKeyIdentifier))
break;
}
if (gpg_err_code (err) == GPG_ERR_EOF)
return gpg_error (GPG_ERR_NO_DATA); /* not available */
if (err)
return err;
/* check that there is only one */
for (idx++; !(err=ksba_cert_get_extension (cert, idx, &oid, NULL,
NULL, NULL)); idx++)
{
if (!strcmp (oid, oidstr_authorityKeyIdentifier))
return gpg_error (GPG_ERR_DUP_VALUE);
}
der = cert->image + off;
err = _ksba_ber_parse_tl (&der, &derlen, &ti);
if (err)
return err;
if ( !(ti.class == CLASS_UNIVERSAL && ti.tag == TYPE_SEQUENCE
&& ti.is_constructed) )
return gpg_error (GPG_ERR_INV_CERT_OBJ);
if (ti.ndef)
return gpg_error (GPG_ERR_NOT_DER_ENCODED);
if (ti.length > derlen)
return gpg_error (GPG_ERR_BAD_BER);
err = _ksba_ber_parse_tl (&der, &derlen, &ti);
if (err)
return err;
if (ti.class != CLASS_CONTEXT)
return gpg_error (GPG_ERR_INV_CERT_OBJ); /* we expected a tag */
if (ti.ndef)
return gpg_error (GPG_ERR_NOT_DER_ENCODED);
if (derlen < ti.length)
return gpg_error (GPG_ERR_BAD_BER);
if (ti.tag == 0)
{ /* keyIdentifier: Save it away and skip over it. */
keyid_der = der;
keyid_derlen = ti.length;
der += ti.length;
derlen -= ti.length;
/* If the keyid has been requested but no other data follows, we
directly jump to the end. */
if (r_keyid && !derlen)
goto build_keyid;
if (!derlen)
return gpg_error (GPG_ERR_NO_DATA); /* not available */
err = _ksba_ber_parse_tl (&der, &derlen, &ti);
if (err)
return err;
if (ti.class != CLASS_CONTEXT)
return gpg_error (GPG_ERR_INV_CERT_OBJ); /* we expected a tag */
if (ti.ndef)
return gpg_error (GPG_ERR_NOT_DER_ENCODED);
if (derlen < ti.length)
return gpg_error (GPG_ERR_BAD_BER);
}
if (ti.tag != 1 || !derlen)
return gpg_error (GPG_ERR_INV_CERT_OBJ);
err = _ksba_name_new_from_der (r_name, der, ti.length);
if (err)
return err;
der += ti.length;
derlen -= ti.length;
/* fixme: we should release r_name before returning on error */
err = _ksba_ber_parse_tl (&der, &derlen, &ti);
if (err)
return err;
if (ti.class != CLASS_CONTEXT)
return gpg_error (GPG_ERR_INV_CERT_OBJ); /* we expected a tag */
if (ti.ndef)
return gpg_error (GPG_ERR_NOT_DER_ENCODED);
if (derlen < ti.length)
return gpg_error (GPG_ERR_BAD_BER);
if (ti.tag != 2 || !derlen)
return gpg_error (GPG_ERR_INV_CERT_OBJ);
sprintf (numbuf,"(%u:", (unsigned int)ti.length);
numbuflen = strlen (numbuf);
*r_serial = xtrymalloc (numbuflen + ti.length + 2);
if (!*r_serial)
return gpg_error (GPG_ERR_ENOMEM);
strcpy (*r_serial, numbuf);
memcpy (*r_serial+numbuflen, der, ti.length);
(*r_serial)[numbuflen + ti.length] = ')';
(*r_serial)[numbuflen + ti.length + 1] = 0;
build_keyid:
if (r_keyid && keyid_der && keyid_derlen)
{
sprintf (numbuf,"(%u:", (unsigned int)keyid_derlen);
numbuflen = strlen (numbuf);
*r_keyid = xtrymalloc (numbuflen + keyid_derlen + 2);
if (!*r_keyid)
return gpg_error (GPG_ERR_ENOMEM);
strcpy (*r_keyid, numbuf);
memcpy (*r_keyid+numbuflen, keyid_der, keyid_derlen);
(*r_keyid)[numbuflen + keyid_derlen] = ')';
(*r_keyid)[numbuflen + keyid_derlen + 1] = 0;
}
return 0;
}
/* Return a simple octet string extension at the object identifier OID
from certificate CERT. The data is return as a simple S-expression
and stored at R_DATA. Returns 0 on success or an error code.
common error codes are: GPG_ERR_NO_DATA if no such extension is
available, GPG_ERR_DUP_VALUE if more than one is available. If
R_CRIT is not NULL, the critical extension flag will be stored at
that address. */
static gpg_error_t
get_simple_octet_string_ext (ksba_cert_t cert, const char *oid,
int *r_crit, ksba_sexp_t *r_data)
{
gpg_error_t err;
const char *tmpoid;
size_t off, derlen;
const unsigned char *der;
int idx, crit;
struct tag_info ti;
char numbuf[30];
size_t numbuflen;
if (!r_data)
return gpg_error (GPG_ERR_INV_VALUE);
*r_data = NULL;
for (idx=0; !(err=ksba_cert_get_extension (cert, idx, &tmpoid, &crit,
&off, &derlen)); idx++)
{
if (!strcmp (tmpoid, oid))
break;
}
if (err)
{
if (gpg_err_code (err) == GPG_ERR_EOF)
return gpg_error (GPG_ERR_NO_DATA);
return err;
}
/* Check that there is only one */
for (idx++; !(err=ksba_cert_get_extension (cert, idx, &tmpoid, NULL,
NULL, NULL)); idx++)
{
if (!strcmp (tmpoid, oid))
return gpg_error (GPG_ERR_DUP_VALUE);
}
der = cert->image + off;
err = _ksba_ber_parse_tl (&der, &derlen, &ti);
if (err)
return err;
if ( !(ti.class == CLASS_UNIVERSAL && ti.tag == TYPE_OCTET_STRING
&& !ti.is_constructed) )
return gpg_error (GPG_ERR_INV_CERT_OBJ);
if (ti.ndef)
return gpg_error (GPG_ERR_NOT_DER_ENCODED);
if (ti.length > derlen)
return gpg_error (GPG_ERR_BAD_BER);
if (ti.length != derlen)
return gpg_error (GPG_ERR_INV_CERT_OBJ); /* Garbage follows. */
sprintf (numbuf,"(%u:", (unsigned int)ti.length);
numbuflen = strlen (numbuf);
*r_data = xtrymalloc (numbuflen + ti.length + 2);
if (!*r_data)
return gpg_error (GPG_ERR_ENOMEM);
strcpy (*r_data, numbuf);
memcpy (*r_data+numbuflen, der, ti.length);
(*r_data)[numbuflen + ti.length] = ')';
(*r_data)[numbuflen + ti.length + 1] = 0;
if (r_crit)
*r_crit = crit;
return 0;
}
/* Return the subjectKeyIdentifier extension as a simple allocated
S-expression at the address of R_KEYID. 0 is returned on success,
GPG_ERR_NO_DATA if no such extension is available or any other
error code. If R_CRIT is not passed as NULL, the criticla flag of
this is extension is stored there. */
gpg_error_t
ksba_cert_get_subj_key_id (ksba_cert_t cert, int *r_crit, ksba_sexp_t *r_keyid)
{
return get_simple_octet_string_ext (cert, oidstr_subjectKeyIdentifier,
r_crit, r_keyid);
}
/* MODE 0 := authorityInfoAccess
1 := subjectInfoAccess
Caller must release METHOD and LOCATION if the function returned
with success; on error both variables will point to NULL.
*/
static gpg_error_t
get_info_access (ksba_cert_t cert, int idx, int mode,
char **method, ksba_name_t *location)
{
gpg_error_t err;
const char *oid;
size_t off, derlen;
int myidx, crit;
*method = NULL;
*location = NULL;
if (!cert || !cert->initialized)
return gpg_error (GPG_ERR_INV_VALUE);
if (idx < 0)
return gpg_error (GPG_ERR_INV_INDEX);
for (myidx=0; !(err=ksba_cert_get_extension (cert, myidx, &oid, &crit,
&off, &derlen)); myidx++)
{
if (!strcmp (oid,(mode == 0)? oidstr_authorityInfoAccess
: oidstr_subjectInfoAccess) )
{
const unsigned char *der;
struct tag_info ti;
size_t seqlen;
der = cert->image + off;
/* What we are going to parse is:
*
* AuthorityInfoAccessSyntax ::=
* SEQUENCE SIZE (1..MAX) OF AccessDescription
*
* AccessDescription ::= SEQUENCE {
* accessMethod OBJECT IDENTIFIER,
* accessLocation GeneralName }
*/
err = _ksba_ber_parse_tl (&der, &derlen, &ti);
if (err)
return err;
if ( !(ti.class == CLASS_UNIVERSAL && ti.tag == TYPE_SEQUENCE
&& ti.is_constructed) )
return gpg_error (GPG_ERR_INV_CERT_OBJ);
if (ti.ndef)
return gpg_error (GPG_ERR_NOT_DER_ENCODED);
seqlen = ti.length;
if (seqlen > derlen)
return gpg_error (GPG_ERR_BAD_BER);
/* Note: an empty sequence is actually not allowed but we
better don't care. */
while (seqlen)
{
err = _ksba_ber_parse_tl (&der, &derlen, &ti);
if (err)
return err;
if ( !(ti.class == CLASS_UNIVERSAL && ti.tag == TYPE_SEQUENCE
&& ti.is_constructed) )
return gpg_error (GPG_ERR_INV_CERT_OBJ);
if (derlen < ti.length)
return gpg_error (GPG_ERR_BAD_BER);
if (seqlen < ti.nhdr)
return gpg_error (GPG_ERR_BAD_BER);
seqlen -= ti.nhdr;
if (seqlen < ti.length)
return gpg_error (GPG_ERR_BAD_BER);
if (idx)
{ /* Skip because we are not yet at the desired index. */
der += ti.length;
derlen -= ti.length;
seqlen -= ti.length;
idx--;
continue;
}
/* We only need the next object, thus we can (and
actually need to) limit the DERLEN to the length of
the current sequence. */
derlen = ti.length;
if (!derlen)
return gpg_error (GPG_ERR_INV_CERT_OBJ);
err = _ksba_ber_parse_tl (&der, &derlen, &ti);
if (err)
return err;
if ( !(ti.class == CLASS_UNIVERSAL && ti.tag == TYPE_OBJECT_ID
&& !ti.is_constructed))
return gpg_error (GPG_ERR_INV_CERT_OBJ);
if (ti.ndef)
return gpg_error (GPG_ERR_NOT_DER_ENCODED);
if (derlen < ti.length)
return gpg_error (GPG_ERR_BAD_BER);
*method = ksba_oid_to_str (der, ti.length);
if (!*method)
return gpg_error (GPG_ERR_ENOMEM);
der += ti.length;
derlen -= ti.length;
err = _ksba_name_new_from_der (location, der, derlen);
if (err)
{
ksba_free (*method);
*method = NULL;
return err;
}
return 0;
}
}
}
return err;
}
/* Return the authorityInfoAccess attributes. IDX should be iterated
starting from 0 until the function returns GPG_ERR_EOF. R_METHOD
returns an allocated string with the OID of one item and R_LOCATION
return the GeneralName for that OID. The return values for
R_METHOD and R_LOCATION must be released by the caller unless the
function returned an error; the function will however make sure
that R_METHOD and R_LOCATION will point to NULL if the function
returns an error. See RFC 2459, section 4.2.2.1 */
gpg_error_t
ksba_cert_get_authority_info_access (ksba_cert_t cert, int idx,
char **r_method, ksba_name_t *r_location)
{
if (!r_method || !r_location)
return gpg_error (GPG_ERR_INV_VALUE);
return get_info_access (cert, idx, 0, r_method, r_location);
}
/* Return the subjectInfoAccess attributes. IDX should be iterated
starting from 0 until the function returns GPG_ERR_EOF. R_METHOD
returns an allocated string with the OID of one item and R_LOCATION
return the GeneralName for that OID. The return values for
R_METHOD and R_LOCATION must be released by the caller unless the
function returned an error; the function will however make sure
that R_METHOD and R_LOCATION will point to NULL if the function
returns an error. See RFC 2459, section 4.2.2.2 */
gpg_error_t
ksba_cert_get_subject_info_access (ksba_cert_t cert, int idx,
char **r_method, ksba_name_t *r_location)
{
if (!r_method || !r_location)
return gpg_error (GPG_ERR_INV_VALUE);
return get_info_access (cert, idx, 1, r_method, r_location);
}
diff --git a/src/cms-parser.c b/src/cms-parser.c
index 31025fa..11b44e7 100644
--- a/src/cms-parser.c
+++ b/src/cms-parser.c
@@ -1,905 +1,906 @@
/* cms-parse.c - parse cryptographic message syntax
* Copyright (C) 2001 g10 Code GmbH
*
* This file is part of KSBA.
*
* KSBA 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 2 of the License, or
* (at your option) any later version.
*
* KSBA 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, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
*/
/*
We handle CMS by using a handcrafted parser for the outer
structures and the generic parser of the parts we can handle in
memory. Extending the generic parser to allow hooks for indefinite
length objects and to auto select the object depending on the
content type OID is too complicated.
*/
#include <config.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include "util.h"
#include "cms.h"
#include "asn1-func.h" /* need some constants */
#include "ber-decoder.h"
#include "ber-help.h"
#include "keyinfo.h"
static int
read_byte (ksba_reader_t reader)
{
unsigned char buf;
size_t nread;
int rc;
do
rc = ksba_reader_read (reader, &buf, 1, &nread);
while (!rc && !nread);
return rc? -1: buf;
}
/* read COUNT bytes into buffer. Return 0 on success */
static int
read_buffer (ksba_reader_t reader, char *buffer, size_t count)
{
size_t nread;
while (count)
{
if (ksba_reader_read (reader, buffer, count, &nread))
return -1;
buffer += nread;
count -= nread;
}
return 0;
}
/* Create a new decoder and run it for the given element */
static gpg_error_t
create_and_run_decoder (ksba_reader_t reader, const char *elem_name,
AsnNode *r_root,
unsigned char **r_image, size_t *r_imagelen)
{
gpg_error_t err;
ksba_asn_tree_t cms_tree;
BerDecoder decoder;
err = ksba_asn_create_tree ("cms", &cms_tree);
if (err)
return err;
decoder = _ksba_ber_decoder_new ();
if (!decoder)
{
ksba_asn_tree_release (cms_tree);
return gpg_error (GPG_ERR_ENOMEM);
}
err = _ksba_ber_decoder_set_reader (decoder, reader);
if (err)
{
ksba_asn_tree_release (cms_tree);
_ksba_ber_decoder_release (decoder);
return err;
}
err = _ksba_ber_decoder_set_module (decoder, cms_tree);
if (err)
{
ksba_asn_tree_release (cms_tree);
_ksba_ber_decoder_release (decoder);
return err;
}
err = _ksba_ber_decoder_decode (decoder, elem_name,
r_root, r_image, r_imagelen);
_ksba_ber_decoder_release (decoder);
ksba_asn_tree_release (cms_tree);
return err;
}
/* Parse this structure and return the oid of the content. The read
position is then located at the value of content. This fucntion is
the core for parsing ContentInfo and EncapsulatedContentInfo.
ContentInfo ::= SEQUENCE {
contentType ContentType,
content [0] EXPLICIT ANY DEFINED BY contentType
}
ContentType ::= OBJECT IDENTIFIER
Returns: 0 on success or an error code. Other values are returned
by the parameters.
*/
static gpg_error_t
parse_content_info (ksba_reader_t reader,
unsigned long *r_len, int *r_ndef,
char **r_oid, int *has_content)
{
struct tag_info ti;
gpg_error_t err;
int content_ndef;
unsigned long content_len;
unsigned char oidbuf[100]; /* pretty large for an OID */
char *oid = NULL;
/* read the sequence triplet */
err = _ksba_ber_read_tl (reader, &ti);
if (err)
return err;
if ( !(ti.class == CLASS_UNIVERSAL && ti.tag == TYPE_SEQUENCE
&& ti.is_constructed) )
return gpg_error (GPG_ERR_INV_CMS_OBJ);
content_len = ti.length;
content_ndef = ti.ndef;
if (!content_ndef && content_len < 3)
return gpg_error (GPG_ERR_TOO_SHORT); /* to encode an OID */
/* read the OID */
err = _ksba_ber_read_tl (reader, &ti);
if (err)
return err;
if ( !(ti.class == CLASS_UNIVERSAL && ti.tag == TYPE_OBJECT_ID
&& !ti.is_constructed && ti.length) )
return gpg_error (GPG_ERR_INV_CMS_OBJ);
if (!content_ndef)
{
if (content_len < ti.nhdr)
return gpg_error (GPG_ERR_BAD_BER); /* triplet header larger that sequence */
content_len -= ti.nhdr;
if (content_len < ti.length)
return gpg_error (GPG_ERR_BAD_BER); /* triplet larger that sequence */
content_len -= ti.length;
}
if (ti.length >= DIM(oidbuf))
return gpg_error (GPG_ERR_TOO_LARGE);
err = read_buffer (reader, oidbuf, ti.length);
if (err)
return err;
oid = ksba_oid_to_str (oidbuf, ti.length);
if (!oid)
return gpg_error (GPG_ERR_ENOMEM);
if (!content_ndef && !content_len)
{ /* no data */
*has_content = 0;
}
else
{ /* now read the explicit tag 0 which is optional */
err = _ksba_ber_read_tl (reader, &ti);
if (err)
{
xfree (oid);
return err;
}
if ( ti.class == CLASS_CONTEXT && ti.tag == 0 && ti.is_constructed )
{
*has_content = 1;
}
else if ( ti.class == CLASS_UNIVERSAL && ti.tag == 0 && !ti.is_constructed )
{
*has_content = 0; /* this is optional - allow NUL tag */
}
else /* neither [0] nor NULL */
{
xfree (oid);
return gpg_error (GPG_ERR_INV_CMS_OBJ);
}
if (!content_ndef)
{
if (content_len < ti.nhdr)
return gpg_error (GPG_ERR_BAD_BER); /* triplet header larger that sequence */
content_len -= ti.nhdr;
if (!ti.ndef && content_len < ti.length)
return gpg_error (GPG_ERR_BAD_BER); /* triplet larger that sequence */
}
}
*r_len = content_len;
*r_ndef = content_ndef;
*r_oid = oid;
return 0;
}
/* Parse this structure and return the oid of the content as well as
the algorithm identifier. The read position is then located at the
value of the octect string.
EncryptedContentInfo ::= SEQUENCE {
contentType OBJECT IDENTIFIER,
contentEncryptionAlgorithm ContentEncryptionAlgorithmIdentifier,
encryptedContent [0] IMPLICIT OCTET STRING OPTIONAL }
Returns: 0 on success or an error code. Other values are returned
by the parameters.
*/
static gpg_error_t
parse_encrypted_content_info (ksba_reader_t reader,
unsigned long *r_len, int *r_ndef,
char **r_cont_oid, char **r_algo_oid,
char **r_algo_parm, size_t *r_algo_parmlen,
int *has_content)
{
struct tag_info ti;
gpg_error_t err;
int content_ndef;
unsigned long content_len;
unsigned char tmpbuf[500]; /* for OID or algorithmIdentifier */
char *cont_oid = NULL;
char *algo_oid = NULL;
char *algo_parm = NULL;
size_t algo_parmlen;
size_t nread;
/* Fixme: release oids in case of errors */
/* read the sequence triplet */
err = _ksba_ber_read_tl (reader, &ti);
if (err)
return err;
if ( !(ti.class == CLASS_UNIVERSAL && ti.tag == TYPE_SEQUENCE
&& ti.is_constructed) )
return gpg_error (GPG_ERR_INV_CMS_OBJ);
content_len = ti.length;
content_ndef = ti.ndef;
if (!content_ndef && content_len < 3)
return gpg_error (GPG_ERR_TOO_SHORT); /* to encode an OID */
/* read the OID */
err = _ksba_ber_read_tl (reader, &ti);
if (err)
return err;
if ( !(ti.class == CLASS_UNIVERSAL && ti.tag == TYPE_OBJECT_ID
&& !ti.is_constructed && ti.length) )
return gpg_error (GPG_ERR_INV_CMS_OBJ);
if (!content_ndef)
{
if (content_len < ti.nhdr)
return gpg_error (GPG_ERR_BAD_BER); /* triplet header larger that sequence */
content_len -= ti.nhdr;
if (content_len < ti.length)
return gpg_error (GPG_ERR_BAD_BER); /* triplet larger that sequence */
content_len -= ti.length;
}
if (ti.length >= DIM(tmpbuf))
return gpg_error (GPG_ERR_TOO_LARGE);
err = read_buffer (reader, tmpbuf, ti.length);
if (err)
return err;
cont_oid = ksba_oid_to_str (tmpbuf, ti.length);
if (!cont_oid)
return gpg_error (GPG_ERR_ENOMEM);
/* read the algorithmIdentifier */
err = _ksba_ber_read_tl (reader, &ti);
if (err)
return err;
if ( !(ti.class == CLASS_UNIVERSAL && ti.tag == TYPE_SEQUENCE
&& ti.is_constructed) )
return gpg_error (GPG_ERR_INV_CMS_OBJ);
if (!content_ndef)
{
if (content_len < ti.nhdr)
return gpg_error (GPG_ERR_BAD_BER); /* triplet header larger that sequence */
content_len -= ti.nhdr;
if (content_len < ti.length)
return gpg_error (GPG_ERR_BAD_BER); /* triplet larger that sequence */
content_len -= ti.length;
}
if (ti.nhdr + ti.length >= DIM(tmpbuf))
return gpg_error (GPG_ERR_TOO_LARGE);
memcpy (tmpbuf, ti.buf, ti.nhdr);
err = read_buffer (reader, tmpbuf+ti.nhdr, ti.length);
if (err)
return err;
err = _ksba_parse_algorithm_identifier2 (tmpbuf, ti.nhdr+ti.length,
&nread,&algo_oid,
&algo_parm, &algo_parmlen);
if (err)
return err;
assert (nread <= ti.nhdr + ti.length);
if (nread < ti.nhdr + ti.length)
return gpg_error (GPG_ERR_TOO_SHORT);
/* the optional encryptedDataInfo */
*has_content = 0;
if (content_ndef || content_len)
{ /* now read the implicit tag 0. Actually this is optional but
in that case we don't expect to have a content_len - well, it
may be the end tag */
err = _ksba_ber_read_tl (reader, &ti);
if (err)
{
xfree (cont_oid);
xfree (algo_oid);
return err;
}
/* Note: the tag may eithe denote a constructed or a primitve
object. Actually this should match the use of NDEF header
but we don't ceck that */
if ( ti.class == CLASS_CONTEXT && ti.tag == 0 )
{
*has_content = 1;
if (!content_ndef)
{
if (content_len < ti.nhdr)
return gpg_error (GPG_ERR_BAD_BER);
content_len -= ti.nhdr;
if (!ti.ndef && content_len < ti.length)
return gpg_error (GPG_ERR_BAD_BER);
}
}
else /* not what we want - push it back */
{
*has_content = 0;
err = ksba_reader_unread (reader, ti.buf, ti.nhdr);
if (err)
return err;
}
}
*r_len = content_len;
*r_ndef = content_ndef;
*r_cont_oid = cont_oid;
*r_algo_oid = algo_oid;
*r_algo_parm = algo_parm;
*r_algo_parmlen = algo_parmlen;
return 0;
}
/* Parse this structure and return the oid of the content. The read
position is then located at the value of content.
ContentInfo ::= SEQUENCE {
contentType ContentType,
content [0] EXPLICIT ANY DEFINED BY contentType
}
ContentType ::= OBJECT IDENTIFIER
Returns: 0 on success or an error code. On success the OID and the
length values are stored in the cms structure.
*/
gpg_error_t
_ksba_cms_parse_content_info (ksba_cms_t cms)
{
gpg_error_t err;
int has_content;
int content_ndef;
unsigned long content_len;
char *oid;
err = parse_content_info (cms->reader, &content_len, &content_ndef,
&oid, &has_content);
if (err)
{ /* return a more meaningful error message. This way the caller
can pass arbitrary data to the function and get back an error
that this is not CMS instead of the the not very detailed BER
Error. */
if (gpg_err_code (err) == GPG_ERR_BAD_BER
|| gpg_err_code (err) == GPG_ERR_INV_CMS_OBJ
|| gpg_err_code (err) == GPG_ERR_TOO_SHORT)
err = gpg_error (GPG_ERR_NO_CMS_OBJ);
return err;
}
if (!has_content)
return gpg_error (GPG_ERR_NO_CMS_OBJ); /* It is not optional here */
cms->content.length = content_len;
cms->content.ndef = content_ndef;
xfree (cms->content.oid);
cms->content.oid = oid;
return 0;
}
/* parse a SEQUENCE and the first element which is expected to be the
CMS version. Return the version and the length info */
static gpg_error_t
parse_cms_version (ksba_reader_t reader, int *r_version,
unsigned long *r_len, int *r_ndef)
{
struct tag_info ti;
gpg_error_t err;
unsigned long data_len;
int data_ndef;
int c;
/* read the sequence triplet */
err = _ksba_ber_read_tl (reader, &ti);
if (err)
return err;
if ( !(ti.class == CLASS_UNIVERSAL && ti.tag == TYPE_SEQUENCE
&& ti.is_constructed) )
return gpg_error (GPG_ERR_INV_CMS_OBJ);
data_len = ti.length;
data_ndef = ti.ndef;
if (!data_ndef && data_len < 3)
return gpg_error (GPG_ERR_TOO_SHORT); /*to encode the version*/
/* read the version integer */
err = _ksba_ber_read_tl (reader, &ti);
if (err)
return err;
if ( !(ti.class == CLASS_UNIVERSAL && ti.tag == TYPE_INTEGER
&& !ti.is_constructed && ti.length) )
return gpg_error (GPG_ERR_INV_CMS_OBJ);
if (!data_ndef)
{
if (data_len < ti.nhdr)
return gpg_error (GPG_ERR_BAD_BER); /* triplet header larger that sequence */
data_len -= ti.nhdr;
if (data_len < ti.length)
return gpg_error (GPG_ERR_BAD_BER); /* triplet larger that sequence */
data_len -= ti.length;
}
if (ti.length != 1)
return gpg_error (GPG_ERR_UNSUPPORTED_CMS_VERSION);
if ( (c=read_byte (reader)) == -1)
{
err = ksba_reader_error (reader);
return err? err : gpg_error (GPG_ERR_GENERAL);
}
if ( !(c == 0 || c == 1 || c == 2 || c == 3 || c == 4) )
return gpg_error (GPG_ERR_UNSUPPORTED_CMS_VERSION);
*r_version = c;
*r_len = data_len;
*r_ndef = data_ndef;
return 0;
}
/* Parse a structure:
SignedData ::= SEQUENCE {
version INTEGER { v0(0), v1(1), v2(2), v3(3), v4(4) }),
digestAlgorithms SET OF AlgorithmIdentifier,
encapContentInfo EncapsulatedContentInfo,
certificates [0] IMPLICIT CertificateSet OPTIONAL,
crls [1] IMPLICIT CertificateRevocationLists OPTIONAL,
signerInfos SignerInfos }
AlgorithmIdentifier ::= SEQUENCE {
algorithm OBJECT IDENTIFIER,
parameters ANY DEFINED BY algorithm OPTIONAL
}
*/
gpg_error_t
_ksba_cms_parse_signed_data_part_1 (ksba_cms_t cms)
{
struct tag_info ti;
gpg_error_t err;
int signed_data_ndef;
unsigned long signed_data_len;
int algo_set_ndef;
unsigned long algo_set_len;
int encap_cont_ndef;
unsigned long encap_cont_len;
int has_content;
char *oid;
char *p, *buffer;
unsigned long off, len;
err = parse_cms_version (cms->reader, &cms->cms_version,
&signed_data_len, &signed_data_ndef);
if (err)
return err;
/* read the SET OF algorithmIdentifiers */
err = _ksba_ber_read_tl (cms->reader, &ti);
if (err)
return err;
if ( !(ti.class == CLASS_UNIVERSAL && ti.tag == TYPE_SET
&& ti.is_constructed) )
return gpg_error (GPG_ERR_INV_CMS_OBJ); /* not the expected SET tag */
if (!signed_data_ndef)
{
if (signed_data_len < ti.nhdr)
return gpg_error (GPG_ERR_BAD_BER); /* triplet header larger that sequence */
signed_data_len -= ti.nhdr;
if (!ti.ndef && signed_data_len < ti.length)
return gpg_error (GPG_ERR_BAD_BER); /* triplet larger that sequence */
signed_data_len -= ti.length;
}
algo_set_len = ti.length;
algo_set_ndef = ti.ndef;
/* fixme: we are not able to read ndef length algorithm indentifiers. */
if (algo_set_ndef)
return gpg_error (GPG_ERR_UNSUPPORTED_ENCODING);
/* read the entire sequence into a buffer (add one to avoid malloc(0)) */
buffer = xtrymalloc (algo_set_len + 1);
if (!buffer)
return gpg_error (GPG_ERR_ENOMEM);
if (read_buffer (cms->reader, buffer, algo_set_len))
{
xfree (buffer);
err = ksba_reader_error (cms->reader);
return err? err: gpg_error (GPG_ERR_GENERAL);
}
p = buffer;
while (algo_set_len)
{
size_t nread;
struct oidlist_s *ol;
err = _ksba_parse_algorithm_identifier (p, algo_set_len, &nread, &oid);
if (err)
{
xfree (buffer);
return err;
}
assert (nread <= algo_set_len);
algo_set_len -= nread;
p += nread;
/* store the oid */
ol = xtrymalloc (sizeof *ol);
if (!ol)
{
xfree (oid);
return gpg_error (GPG_ERR_ENOMEM);
}
ol->oid = oid;
ol->next = cms->digest_algos;
cms->digest_algos = ol;
}
xfree (buffer); buffer = NULL;
/* Now for the encapsulatedContentInfo */
off = ksba_reader_tell (cms->reader);
err = parse_content_info (cms->reader,
&encap_cont_len, &encap_cont_ndef,
&oid, &has_content);
if (err)
return err;
cms->inner_cont_len = encap_cont_len;
cms->inner_cont_ndef = encap_cont_ndef;
cms->inner_cont_oid = oid;
cms->detached_data = !has_content;
if (!signed_data_ndef)
{
len = ksba_reader_tell (cms->reader) - off;
if (signed_data_len < len)
return gpg_error (GPG_ERR_BAD_BER); /* parsed content info larger that sequence */
signed_data_len -= len;
if (!encap_cont_ndef && signed_data_len < encap_cont_len)
return gpg_error (GPG_ERR_BAD_BER); /* triplet larger that sequence */
}
/* We have to stop here so that the caller can set up the hashing etc. */
return 0;
}
/* Continue parsing of the structure we started to parse with the
part_1 function. We expect to be right at the certificates tag. */
gpg_error_t
_ksba_cms_parse_signed_data_part_2 (ksba_cms_t cms)
{
struct tag_info ti;
gpg_error_t err;
struct signer_info_s *si, **si_tail;
/* read the next triplet which is either a [0], a [1] or a SET OF
(signerInfo) */
err = _ksba_ber_read_tl (cms->reader, &ti);
if (err)
return err;
if (ti.class == CLASS_UNIVERSAL && !ti.tag && !ti.is_constructed)
{
/* well, there might be still an end tag pending; eat it -
fixme: we should keep track of this to catch invalid
encodings */
err = _ksba_ber_read_tl (cms->reader, &ti);
if (err)
return err;
}
if (ti.class == CLASS_CONTEXT && ti.tag == 0 && ti.is_constructed)
{ /* Implicit SET OF certificateSet with elements of CHOICE, but
we assume the first choice which is a Certificate; all other
choices are obsolete. We are now parsing a set of
certificates which we do by utilizing the ksba_cert code. */
ksba_cert_t cert;
int expect_endtag;
expect_endtag = !!ti.ndef;
for (;;)
{
struct certlist_s *cl;
/* First see whether this is really a sequence */
err = _ksba_ber_read_tl (cms->reader, &ti);
if (err)
return err;
if (expect_endtag && !ti.class && ti.tag == TYPE_NULL )
{
/* This is an end tag. Read the next tag but don't fail
if this is just an EOF. */
err = _ksba_ber_read_tl (cms->reader, &ti);
if (err)
{
if (gpg_err_code (err) == GPG_ERR_EOF)
err = 0;
return err;
}
break;
}
if (!(ti.class == CLASS_UNIVERSAL && ti.tag == TYPE_SEQUENCE
&& ti.is_constructed))
break; /* not a sequence, so we are ready with the set */
/* We must unread so that the standard parser sees the sequence */
err = ksba_reader_unread (cms->reader, ti.buf, ti.nhdr);
if (err)
return err;
/* Use the standard certificate parser */
err = ksba_cert_new (&cert);
if (err)
return err;
err = ksba_cert_read_der (cert, cms->reader);
if (err)
{
ksba_cert_release (cert);
return err;
}
cl = xtrycalloc (1, sizeof *cl);
if (!cl)
{
ksba_cert_release (cert);
return gpg_error (GPG_ERR_ENOMEM);
}
cl->cert = cert;
cl->next = cms->cert_list;
cms->cert_list = cl;
}
}
if (ti.class == CLASS_CONTEXT && ti.tag == 1 && ti.is_constructed)
{ /* implicit SET OF certificateList. We should delegate the
parsing to a - not yet existing - ksba_crl module. CRLs are
quite important for other applications too so we should
provide a nice interface */
int expect_endtag;
expect_endtag = !!ti.ndef;
/* FIXME this is just dummy read code */
/* fprintf (stderr,"WARNING: Can't handle CRLs yet\n"); */
for (;;)
{
/* first see whether this is really a sequence */
err = _ksba_ber_read_tl (cms->reader, &ti);
if (err)
return err;
if (expect_endtag && !ti.class && ti.tag == TYPE_NULL )
{
/* This is an end tag. Read the next tag but don't fail
if this is just an EOF. */
err = _ksba_ber_read_tl (cms->reader, &ti);
if (err)
{
if (gpg_err_code (err) == GPG_ERR_EOF)
err = 0;
return err;
}
break;
}
if ( !(ti.class == CLASS_UNIVERSAL && ti.tag == TYPE_SEQUENCE
&& ti.is_constructed))
break; /* not a sequence, so we are ready with the set */
while (ti.length)
{
size_t n, nread;
char dummy[256];
n = ti.length > DIM(dummy) ? DIM(dummy): ti.length;
err = ksba_reader_read (cms->reader, dummy, n, &nread);
if (err)
return err;
ti.length -= nread;
}
}
}
/* expect a SET OF signerInfo */
if ( !(ti.class == CLASS_UNIVERSAL
&& ti.tag == TYPE_SET && ti.is_constructed))
return gpg_error (GPG_ERR_INV_CMS_OBJ);
si_tail = &cms->signer_info;
+
while (ti.length)
{
size_t off1, off2;
off1 = ksba_reader_tell (cms->reader);
si = xtrycalloc (1, sizeof *si);
if (!si)
return gpg_error (GPG_ERR_ENOMEM);
err = create_and_run_decoder (cms->reader,
"CryptographicMessageSyntax.SignerInfo",
&si->root, &si->image, &si->imagelen);
/* The signerInfo might be an empty set in the case of a certs-only
signature. Thus we have to allow for EOF here */
if (gpg_err_code (err) == GPG_ERR_EOF)
{
err = 0;
break;
}
if (err)
return err;
*si_tail = si;
si_tail = &si->next;
off2 = ksba_reader_tell (cms->reader);
if ( (off2 - off1) > ti.length )
ti.length = 0;
else
ti.length -= off2 - off1;
}
return 0;
}
/* Parse the structure:
EnvelopedData ::= SEQUENCE {
version INTEGER { v0(0), v1(1), v2(2), v3(3), v4(4) }),
originatorInfo [0] IMPLICIT OriginatorInfo OPTIONAL,
recipientInfos RecipientInfos,
encryptedContentInfo EncryptedContentInfo,
unprotectedAttrs [1] IMPLICIT UnprotectedAttributes OPTIONAL }
OriginatorInfo ::= SEQUENCE {
certs [0] IMPLICIT CertificateSet OPTIONAL,
crls [1] IMPLICIT CertificateRevocationLists OPTIONAL }
RecipientInfos ::= SET OF RecipientInfo
EncryptedContentInfo ::= SEQUENCE {
contentType ContentType,
contentEncryptionAlgorithm ContentEncryptionAlgorithmIdentifier,
encryptedContent [0] IMPLICIT EncryptedContent OPTIONAL }
EncryptedContent ::= OCTET STRING
We stop parsing so that the next read will be the first byte of the
encryptedContent or (if there is no content) the unprotectedAttrs.
*/
gpg_error_t
_ksba_cms_parse_enveloped_data_part_1 (ksba_cms_t cms)
{
struct tag_info ti;
gpg_error_t err;
int env_data_ndef;
unsigned long env_data_len;
int encr_cont_ndef;
unsigned long encr_cont_len;
int has_content;
unsigned long off, len;
char *cont_oid = NULL;
char *algo_oid = NULL;
char *algo_parm = NULL;
size_t algo_parmlen;
struct value_tree_s *vt, **vtend;
/* get the version */
err = parse_cms_version (cms->reader, &cms->cms_version,
&env_data_len, &env_data_ndef);
if (err)
return err;
/* read the next triplet which is either a [0] for originatorInfos
or a SET_OF (recipientInfo) */
err = _ksba_ber_read_tl (cms->reader, &ti);
if (err)
return err;
if (ti.class == CLASS_CONTEXT && ti.tag == 0 && ti.is_constructed)
{ /* originatorInfo - but we skip it for now */
/* well, raise an error */
return gpg_error (GPG_ERR_UNSUPPORTED_CMS_OBJ);
}
/* Next one is the SET OF recipientInfos */
if ( !(ti.class == CLASS_UNIVERSAL
&& ti.tag == TYPE_SET && ti.is_constructed))
return gpg_error (GPG_ERR_INV_CMS_OBJ);
vtend = &cms->recp_info;
while (ti.length)
{
size_t off1, off2;
off1 = ksba_reader_tell (cms->reader);
vt = xtrycalloc (1, sizeof *vt);
if (!vt)
return gpg_error (GPG_ERR_ENOMEM);
err = create_and_run_decoder (cms->reader,
"CryptographicMessageSyntax.KeyTransRecipientInfo",
&vt->root, &vt->image, &vt->imagelen);
if (err)
return err;
*vtend = vt;
vtend = &vt->next;
off2 = ksba_reader_tell (cms->reader);
if ( (off2 - off1) > ti.length )
ti.length = 0;
else
ti.length -= off2 - off1;
}
/* Now for the encryptedContentInfo */
off = ksba_reader_tell (cms->reader);
err = parse_encrypted_content_info (cms->reader,
&encr_cont_len, &encr_cont_ndef,
&cont_oid,
&algo_oid,
&algo_parm, &algo_parmlen,
&has_content);
if (err)
return err;
cms->inner_cont_len = encr_cont_len;
cms->inner_cont_ndef = encr_cont_ndef;
cms->inner_cont_oid = cont_oid;
cms->detached_data = !has_content;
cms->encr_algo_oid = algo_oid;
cms->encr_iv = algo_parm; algo_parm = NULL;
cms->encr_ivlen = algo_parmlen;
if (!env_data_ndef)
{
len = ksba_reader_tell (cms->reader) - off;
if (env_data_len < len)
return gpg_error (GPG_ERR_BAD_BER); /* parsed content info larger that sequence */
env_data_len -= len;
if (!encr_cont_ndef && env_data_len < encr_cont_len)
return gpg_error (GPG_ERR_BAD_BER); /* triplet larger that sequence */
}
return 0;
}
/* handle the unprotected attributes */
gpg_error_t
_ksba_cms_parse_enveloped_data_part_2 (ksba_cms_t cms)
{
/* FIXME */
return 0;
}

File Metadata

Mime Type
text/x-diff
Expires
Mon, May 12, 6:39 PM (1 d, 21 h)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
b3/89/ee4818a7959e9e1a2641675724f6

Event Timeline